@@ -2233,6 +2233,40 @@ function RemoteFunctions(config = {}) {
22332233 .phoenix-ribbon-thumb {
22342234 cursor: pointer !important;
22352235 }
2236+
2237+ .phoenix-ribbon-thumb.downloading {
2238+ opacity: 0.6 !important;
2239+ pointer-events: none !important;
2240+ }
2241+
2242+ .phoenix-download-indicator {
2243+ position: absolute !important;
2244+ top: 50% !important;
2245+ left: 50% !important;
2246+ transform: translate(-50%, -50%) !important;
2247+ background: rgba(0, 0, 0, 0.8) !important;
2248+ border-radius: 50% !important;
2249+ width: 40px !important;
2250+ height: 40px !important;
2251+ display: flex !important;
2252+ align-items: center !important;
2253+ justify-content: center !important;
2254+ z-index: 10 !important;
2255+ }
2256+
2257+ .phoenix-download-spinner {
2258+ width: 20px !important;
2259+ height: 20px !important;
2260+ border: 2px solid rgba(255, 255, 255, 0.3) !important;
2261+ border-top: 2px solid #fff !important;
2262+ border-radius: 50% !important;
2263+ animation: phoenix-spin 1s linear infinite !important;
2264+ }
2265+
2266+ @keyframes phoenix-spin {
2267+ 0% { transform: rotate(0deg); }
2268+ 100% { transform: rotate(360deg); }
2269+ }
22362270 </style>
22372271 <div class="phoenix-image-ribbon">
22382272 <div class="phoenix-ribbon-container">
@@ -2691,6 +2725,13 @@ function RemoteFunctions(config = {}) {
26912725 thumbDiv . addEventListener ( 'click' , ( e ) => {
26922726 e . stopPropagation ( ) ;
26932727 e . preventDefault ( ) ;
2728+
2729+ // prevent multiple downloads of the same image
2730+ if ( thumbDiv . classList . contains ( 'downloading' ) ) { return ; }
2731+
2732+ // show download indicator
2733+ this . _showDownloadIndicator ( thumbDiv ) ;
2734+
26942735 const filename = this . _generateFilename ( image ) ;
26952736 const extnName = ".jpg" ;
26962737
@@ -2700,7 +2741,7 @@ function RemoteFunctions(config = {}) {
27002741 const heightNum = parseInt ( targetHeight ) ;
27012742
27022743 const downloadUrl = image . url ? `${ image . url } ?w=${ widthNum } &h=${ heightNum } &fit=crop` : image . thumb_url ;
2703- this . _useImage ( downloadUrl , filename , extnName , false ) ;
2744+ this . _useImage ( downloadUrl , filename , extnName , false , thumbDiv ) ;
27042745 } ) ;
27052746
27062747 thumbDiv . appendChild ( img ) ;
@@ -2736,7 +2777,7 @@ function RemoteFunctions(config = {}) {
27362777 return `${ cleanSearchTerm } -by-${ cleanPhotographerName } ` ;
27372778 } ,
27382779
2739- _useImage : function ( imageUrl , filename , extnName , isLocalFile ) {
2780+ _useImage : function ( imageUrl , filename , extnName , isLocalFile , thumbDiv ) {
27402781 // send the message to the editor instance to save the image and update the source code
27412782 const tagId = this . element . getAttribute ( "data-brackets-id" ) ;
27422783
@@ -2763,6 +2804,14 @@ function RemoteFunctions(config = {}) {
27632804 }
27642805
27652806 window . _Brackets_MessageBroker . send ( messageData ) ;
2807+
2808+ // if thumbDiv is provided, hide the download indicator after a reasonable timeout
2809+ // this is to make sure that the indicator is always removed even if there's no explicit success callback
2810+ if ( thumbDiv ) {
2811+ setTimeout ( ( ) => {
2812+ this . _hideDownloadIndicator ( thumbDiv ) ;
2813+ } , 3000 ) ;
2814+ }
27662815 } ,
27672816
27682817 _handleLocalImageSelection : function ( file ) {
@@ -2783,7 +2832,7 @@ function RemoteFunctions(config = {}) {
27832832 const filename = cleanName || 'selected-image' ;
27842833
27852834 // Use the unified _useImage method with isLocalFile flag
2786- this . _useImage ( imageDataUrl , filename , extension , true ) ;
2835+ this . _useImage ( imageDataUrl , filename , extension , true , null ) ;
27872836
27882837 // Close the ribbon after successful selection
27892838 this . remove ( ) ;
@@ -2824,6 +2873,32 @@ function RemoteFunctions(config = {}) {
28242873 window . document . body . removeChild ( this . body ) ;
28252874 this . body = null ;
28262875 }
2876+ } ,
2877+
2878+ _showDownloadIndicator : function ( thumbDiv ) {
2879+ // add downloading class
2880+ thumbDiv . classList . add ( 'downloading' ) ;
2881+
2882+ // create download indicator
2883+ const indicator = window . document . createElement ( 'div' ) ;
2884+ indicator . className = 'phoenix-download-indicator' ;
2885+
2886+ const spinner = window . document . createElement ( 'div' ) ;
2887+ spinner . className = 'phoenix-download-spinner' ;
2888+
2889+ indicator . appendChild ( spinner ) ;
2890+ thumbDiv . appendChild ( indicator ) ;
2891+ } ,
2892+
2893+ _hideDownloadIndicator : function ( thumbDiv ) {
2894+ // remove downloading class
2895+ thumbDiv . classList . remove ( 'downloading' ) ;
2896+
2897+ // remove download indicator
2898+ const indicator = thumbDiv . querySelector ( '.phoenix-download-indicator' ) ;
2899+ if ( indicator ) {
2900+ indicator . remove ( ) ;
2901+ }
28272902 }
28282903 } ;
28292904
0 commit comments