11/* global acquireVsCodeApi */ 
22"use strict" ; 
33
4+ const  OVERLAY_MIN_MS  =  3000 ;  // keep overlay visible at least this long 
5+ let  overlayShownAt  =  0 ; 
6+ let  overlayTimerId  =  null 
7+ 
48const  vscode  =  acquireVsCodeApi ( ) ; 
59
610/** @typedef  {{ id:string, depId:string, label:string, version:string, installedAt: string, lastUsed:string, path?:string } } Item */ 
@@ -12,6 +16,8 @@ let state = {
1216  filter : "" , 
1317  selected : new  Set ( ) ,      // of item.id 
1418  uninstalling : false , 
19+   overlayVisible : false , 
20+   pendingRender : false , 
1521} ; 
1622
1723const  $  =  ( sel ,  root  =  document )  =>  /** @type  {HTMLElement|null } */ ( root . querySelector ( sel ) ) ; 
@@ -225,11 +231,15 @@ function renderList() {
225231  ` ; 
226232} 
227233
228- function  renderOverlay ( )  { 
234+ function  renderOverlay ( visible   =   false )  { 
229235  return  ` 
230-   <div id="overlay" class="fixed inset-0 hidden items-center justify-center z-50 bg-black/60 backdrop-blur-sm"> 
231-     <div class="flex flex-col items-center gap-4 p-8 rounded-2xl bg-white/90 dark:bg-zinc-900/90 border border-gray-200 dark:border-zinc-800"> 
232-       <div class="h-10 w-10 rounded-full border-4 border-gray-300 dark:border-zinc-700 border-t-transparent animate-spin"></div> 
236+   <div id="overlay" 
237+        class="fixed inset-0 ${ visible  ? ''  : 'hidden' }  
238+     <div class="pointer-events-auto flex flex-col items-center gap-4 p-8 rounded-2xl 
239+                 bg-white/90 dark:bg-zinc-900/90 border border-gray-200 dark:border-zinc-800 shadow-lg" 
240+          role="status" aria-live="polite"> 
241+       <div class="h-10 w-10 rounded-full border-4 border-gray-300 dark:border-zinc-700 border-t-transparent animate-spin" 
242+            aria-hidden="true"></div> 
233243      <div id="overlay-text" class="text-sm text-gray-700 dark:text-gray-200">Uninstalling…</div> 
234244    </div> 
235245  </div>` ; 
@@ -252,7 +262,7 @@ function renderFrame() {
252262      </div> 
253263    </main> 
254264  </div> 
255-   ${ renderOverlay ( ) }  
265+   ${ renderOverlay ( state . overlayVisible ) }  
256266  ` ; 
257267  bindEvents ( ) ; 
258268  updateSelectAllCheckbox ( ) ; 
@@ -400,19 +410,51 @@ function beginUninstall(ids) {
400410} 
401411
402412function  showOverlay ( text )  { 
413+   // reset any previous timer 
414+   if  ( overlayTimerId )  { 
415+     clearTimeout ( overlayTimerId ) ; 
416+     overlayTimerId  =  null ; 
417+   } 
418+ 
419+   overlayShownAt  =  Date . now ( ) ; 
420+   state . overlayVisible  =  true ; 
403421  const  ov  =  $ ( "#overlay" ) ; 
404422  if  ( ! ov )  return ; 
405-   $ ( "#overlay-text" ) . textContent  =  text  ||  "Working…" ; 
423+   const  ovT  =  $ ( "#overlay-text" ) ; 
424+   if  ( ! ovT )  return ; 
425+   ovT . textContent  =  text  ||  "Working…" ; 
426+ 
406427  ov . classList . remove ( "hidden" ) ; 
407428  // prevent interaction underneath 
408429  document . body . style . pointerEvents  =  "none" ; 
409430  ov . style . pointerEvents  =  "auto" ; 
410431} 
411432
433+ function  scheduleHideOverlay ( )  { 
434+   const  now  =  Date . now ( ) ; 
435+   const  remain  =  Math . max ( OVERLAY_MIN_MS  -  ( now  -  overlayShownAt ) ,  0 ) ; 
436+   console . debug ( `scheduling overlay hide in ${ remain }  ) ; 
437+   if  ( overlayTimerId )  clearTimeout ( overlayTimerId ) ; 
438+ 
439+   const  finish  =  ( )  =>  { 
440+     hideOverlay ( ) ; 
441+     overlayTimerId  =  null ; 
442+     if  ( state . pendingRender )  { 
443+       state . pendingRender  =  false ; 
444+       update ( ) ; 
445+     } 
446+   } ; 
447+ 
448+   overlayTimerId  =  remain  <=  0 
449+     ? setTimeout ( ( )  =>  requestAnimationFrame ( finish ) ,  0 )  // next frame 
450+     : setTimeout ( finish ,  remain ) ; 
451+ } 
452+ 
412453function  hideOverlay ( )  { 
413454  const  ov  =  $ ( "#overlay" ) ; 
414455  if  ( ! ov )  return ; 
415456  ov . classList . add ( "hidden" ) ; 
457+   state . overlayVisible  =  false ; 
416458  document . body . style . pointerEvents  =  "" ; 
417459} 
418460
@@ -453,13 +495,17 @@ window.addEventListener("message", (event) => {
453495      state . items  =  state . items . filter ( i  =>  ! removedIds . has ( i . id ) ) ; 
454496      for  ( const  id  of  removedIds )  state . selected . delete ( id ) ; 
455497      state . uninstalling  =  false ; 
456-       hideOverlay ( ) ; 
457-       update ( ) ; 
498+ 
499+       //hideOverlay(); 
500+       // don't flicker: wait until min visible time has passed 
501+       state . pendingRender  =  true ; 
502+       scheduleHideOverlay ( ) ; 
458503      break ; 
459504    } 
460505    case  "uninstall:error" : { 
461506      state . uninstalling  =  false ; 
462-       hideOverlay ( ) ; 
507+       //hideOverlay(); 
508+       scheduleHideOverlay ( ) ; 
463509      alert ( msg . error  ||  "Uninstall failed." ) ;  // simple; VS Code shows alerts fine 
464510      break ; 
465511    } 
@@ -469,7 +515,11 @@ window.addEventListener("message", (event) => {
469515        state . items  =  msg . items ; 
470516        // prune selection 
471517        state . selected  =  new  Set ( [ ...state . selected ] . filter ( id  =>  state . items . some ( i  =>  i . id  ===  id ) ) ) ; 
472-         update ( ) ; 
518+         if  ( state . overlayVisible )  { 
519+           state . pendingRender  =  true ;    // defer while overlay is up 
520+         }  else  { 
521+           update ( ) ; 
522+         } 
473523      } 
474524      break ; 
475525    } 
0 commit comments