← Commit history

Switching taskbar identity states in Settings now shows a live step list driven by the real apply promises, stamping or stripping identities then rebuilding buttons then painting overlay badges, ending in a green check when the taskbar actually matches, and the plain rows finally tell the truth visually: one stock Chrome button with a drawn hover-preview fan showing that the windows stay separate windows even though they can no longer have separate buttons.

John Lauer ·088cadc536 ·2mo ago ·parent 68b82fb
4 files changed +115−19
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "2.0.213",-  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.213/adom-bridge-puppeteer-v2.0.213.zip",-  "sha256": "6e133f727afd5f82307aed89b96515c49321c28e4460f71eba3feaa774958a22",-  "size": 937263,+  "version": "2.0.214",+  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.214/adom-bridge-puppeteer-v2.0.214.zip",+  "sha256": "675f55344649b4b7635c31850adea9bbe11cfffca6c7eeadc68fa0624534f731",+  "size": 939361,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-2.0.213+2.0.214
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "2.0.213",+  "version": "2.0.214",   "description": "Drives a browser ALREADY on the machine \u2014 installed Chrome, else Microsoft Edge (every Windows PC ships with Edge) \u2014 launched with a FRESH isolated profile, so the common case has NO ~150 MB Chrome-for-Testing download and it never touches the user's real tabs/logins. Binds its local server to LOOPBACK (127.0.0.1) only, so no Windows Firewall 'allow Node through the firewall' prompt ever pops. Chrome for Testing is fetched only as a LAST RESORT if the machine has no Chromium browser at all. SPAWN-VERIFIED fallback: pup tries each detected browser (Chrome \u2192 Edge \u2192 CfT) and actually launches it, falling through to the next if one won't start (and handling browsers like Edge that relaunch onto another PID), so a corrupt/partial install never hard-fails an open. The browser that launches is cached as the default (persisted across restarts) so it isn't re-probed each time. On spawn the bridge SELF-WARMS: detects browsers + disk, and only if the box has no Chrome/Edge and no cached CfT does it background-fetch CfT so the first open is instant. pup_use pins a specific browser (chrome | edge | cft | auto); {browser:'chrome',install:true} downloads & installs REAL Google Chrome (user-scoped, no admin) on an Edge-only box. pup_readiness (READ-ONLY) reports which browser will be used + the full candidate list + the default + free disk. Every failure returns a specific errorCode + _hint (chrome_install_no_disk, chrome_download_network, chrome_install_permission, chrome_stable_not_detected, \u2026). Launches with FULL browser permissions by default for full debug (strictPermissions:true opts out). Headful automation: open/close windows+tabs, navigate, screenshot, eval JS, record. pup is a GENERIC, non-profile browser only \u2014 every session is a fresh isolated Chrome/Edge process, never the user's real signed-in browser; profile defaults to sessionId. To drive the user's REAL Chrome/Edge (their profile, cookies, SSO), use the Adom browser EXTENSION's nbrowser_* verbs \u2014 a separate bridge, not pup's job. macOS (v1.9.74+): full parity with mac-native mechanics \u2014 background-by-default is the app-level hide (windows stay drivable/screenshottable while hidden), raise/lower map to unhide+activate/hide, pup_alert_window posts a native toast whose click focuses the window, pup_use {browser:'chrome',install:true} installs Chrome from the official dmg, and taskbar identity/badging remains Windows-only.",   "homepage": "https://wiki.adom.inc/adom/adom-bridge",   "author": "Adom Inc.",
src/server.js+109−13
@@ -4721,6 +4721,48 @@ async function unregisterPupAumid(appId) { // toggle wasn't wired to anything. This runs on every jumpLists/jlViewTasks/jlCloseAll change: OFF → // commit an EMPTY list to each window's AUMID (removes the menu tasks); ON/sub-change → force a // re-commit so the new task set shows immediately.+// v2.0.214 (John: "when i switch taskbar identity states in the settings panel, can you show a+// progress bar with indicator of what step you're doing... then a green check mark you're done.+// cuz i sit there waiting and am not sure if you're done or not"): a mode switch takes several+// seconds (strip/stamp, button-rebuild nudges, overlay repaints) and the dialog gave zero feedback.+// This tracker is driven by the REAL apply promises, not timers alone, so the check mark is honest.+let _modeApply = null;+let _modeApplyTok = 0;+function _startModeApply(val) {+  const steps = [+    { label: val === 'plain' ? 'Stripping pup identities (back to the stock Chrome button)' : 'Stamping taskbar identities', state: 'run' },+    { label: 'Rebuilding taskbar buttons', state: 'pend' },+    { label: 'Painting overlay badges', state: 'pend' },+  ];+  _modeApply = { at: Date.now(), token: ++_modeApplyTok, mode: val, steps, done: false };+  const my = _modeApply;+  (async () => {+    try {+      if (val === 'plain') {+        await stripAllPupIdentities('settings-taskbarmode-plain');+      } else {+        for (const [, s7] of [...sessions]) { try { s7._curAppId = null; s7._frozenAppId = null; s7._wikiJumplistKey = undefined; s7._jlFrozen = false; } catch (e) {} }+        try { _registeredPupAumids.clear(); } catch (e) {}+        await brandSweep('settings-taskbarmode');+        reconcileModel('setting taskbarMode').catch(() => {});+      }+    } catch (e) {}+    if (_modeApply !== my) return;+    my.steps[0].state = 'done'; my.steps[1].state = 'run';+    // shell settle: the hide/show nudges plus (in tiles mode) the 5s thread-stamp overlay gate+    await new Promise(r => setTimeout(r, val === 'tiles' ? 6000 : 2500));+    if (_modeApply !== my) return;+    my.steps[1].state = 'done';+    if (pupSettings().overlayBadges === 'on') {+      my.steps[2].state = 'run';+      for (const [sid9, s9] of [...sessions]) { try { s9._lastPaintSig = null; await applyAppOverlay(s9, sid9); } catch (e) {} }+    } else {+      my.steps[2].label = 'Overlay badges are off (nothing to paint)';+    }+    if (_modeApply !== my) return;+    my.steps[2].state = 'done'; my.done = true;+  })();+} async function refreshAllJumplists() {   if (process.platform !== 'win32' || _jumplistSupported === false) return;   const off = pupSettings().jumpLists !== 'on';@@ -11957,6 +11999,50 @@ function paintThreadExamples(){   // samples too — each as the stock Chrome icon (that IS what plain looks like per window), with   // its OWN site badge in the overlay row. The stacking note stays as a caption, not a collapse.   function plainIc(sm,ov){ return '<span class="mic"><img class="mbase" src="/dashboard/baseicon?profile=adom-you&'+V+'">'+(ov?fb(sm):'')+'</span>'; }+  function plainGroup(ov){+    var cards=SAMPLES.map(function(sm){+      return '<span style="display:inline-flex;flex-direction:column;width:44px;height:34px;border:1px solid #3a4a52;border-radius:4px;overflow:hidden;background:#e8eaed;flex:none">'+        +'<span style="display:flex;align-items:center;gap:3px;height:12px;background:#202124;padding:0 3px">'+        +'<img src="'+sm.fav+'" style="width:8px;height:8px;object-fit:contain">'+        +'<span style="flex:1"></span><span style="color:#9aa0a6;font-size:7px;line-height:1">\u2715</span></span>'+        +'<span style="flex:1;background:linear-gradient(#ffffff,#e8eaed)"></span></span>';+    }).join('');+    return '<div style="display:flex;flex-direction:column;align-items:center;gap:3px">'+      +'<div style="display:flex;gap:4px;padding:5px 6px;border:1px solid #2f3f46;border-radius:6px;background:#1b2429">'+cards+'</div>'+      +'<span style="color:#4a5a58;font-size:10px;line-height:1.2">\u25B2 what hovering the ONE button shows</span>'+      +'<span class="mic"><img class="mbase" src="/dashboard/baseicon?profile=adom-you&'+V+'">'+(ov?fb(SAMPLES[3]):'')+'</span>'+      +'</div>';+  }+  function showApplyProgress(){+    var host=strip.parentElement; if(!host) return;+    var el=host.querySelector('#applyprog');+    if(!el){ el=document.createElement('div'); el.id='applyprog';+      el.style.cssText='margin:10px 4px 2px;padding:10px 12px;border:1px solid #2a3f3e;border-radius:8px;background:#0f1817;font:12px/1.8 "JetBrains Mono",Consolas,monospace;color:#9fb3b1';+      strip.insertAdjacentElement('afterend',el); }+    el.style.display='block';+    var t0=Date.now();+    function paint(a){+      if(!a || (Date.now()-a.at>60000 && !a.done)){ el.innerHTML='<span style="color:#00b8b1">Applying\u2026</span>'; return false; }+      var h='<style>@keyframes apr{to{transform:rotate(360deg)}}.apspin{display:inline-block;animation:apr 1s linear infinite}</style>';+      a.steps.forEach(function(st){+        var mark = st.state==='done' ? '<span style="color:#3fb950">\u2713</span>'+                 : st.state==='run' ? '<span class="apspin" style="color:#00b8b1">\u25CC</span>'+                 : '<span style="color:#4a5a58">\u25CB</span>';+        h+='<div>'+mark+' '+st.label+'</div>';+      });+      if(a.done) h+='<div style="margin-top:6px;color:#3fb950;font-weight:700">\u2713 Done: your taskbar now matches this state</div>';+      el.innerHTML=h;+      return !!a.done;+    }+    (function tick(){+      fetch('/dashboard/apply-progress').then(function(r){return r.json()}).then(function(j){+        var fin=paint(j&&j.apply);+        if(fin){ setTimeout(function(){ el.style.display='none'; },7000); return; }+        if(Date.now()-t0<45000) setTimeout(tick,600);+        else el.innerHTML='<span style="color:#d29922">Still settling in the background\u2026 (check the taskbar in a few seconds)</span>';+      }).catch(function(){ if(Date.now()-t0<45000) setTimeout(tick,900); });+    })();+  }   const states=[     ['tiles',true, '1. tiles + overlay'],     ['tiles',false,'2. tiles'],@@ -11972,13 +12058,18 @@ function paintThreadExamples(){     let icons='';     if(m==='tiles'){ icons=SAMPLES.map(function(sm){return tileIc(sm,ov);}).join(''); }     else if(m==='aumid'){ icons=SAMPLES.map(function(sm){return aumidIc(sm,ov);}).join(''); }-    else { icons=SAMPLES.map(function(sm){return plainIc(sm,ov);}).join('')+'<span class="texn" style="align-self:center">(Windows stacks all of these under ONE button)</span>'; }+    else {+      var ptxt = ov+        ? 'ALL pup windows share this ONE stock Chrome button. They stay separate windows: hover the button and Windows fans out a preview of each (drawn above). You just cannot ungroup them on the bar, because the per-app identity that separates buttons is exactly what plain mode removes. The single badge belongs to the most recently updated window.'+        : 'ALL pup windows share this ONE stock Chrome button. They stay separate windows: hover the button and Windows fans out a preview of each (drawn above). You just cannot ungroup them on the bar, because the per-app identity that separates buttons is exactly what plain mode removes.';+      icons=plainGroup(ov)+'<span class="texn" style="align-self:center;max-width:330px;text-align:left">'+ptxt+'</span>';+    }     return '<div class="strow6'+(cur?' mcur':'')+'" data-si="'+i+'" title="click to switch to this state">'       +'<span class="stlbl">'+lbl+(cur?' (current)':'')+(isDefault?' <span class="mdef">default</span>':'')+'</span>'       +'<div class="sticons">'+icons+'</div></div>';   }).join('')+'</div>';   const cap=strip.parentElement?strip.parentElement.querySelector('.texcap'):null;-  if(cap)cap.textContent='Five canonical sample windows — a wiki page, the pup dashboard app, ti.com, mouser and digikey — rendered under every state so you can compare like for like. Tiles are drawn by the real tile renderer; category icons and the Chrome icon are the real registered art; badges are each site\u2019s own favicon. Click a row to switch to it.';+  if(cap)cap.textContent='Six canonical sample windows (signed-in wiki, public wiki, the pup dashboard app, ti.com, mouser, digikey) rendered under every state so you can compare like for like. Tiles are drawn by the real tile renderer; category icons and the Chrome icon are the real registered art; badges are each site\u2019s own favicon. Click a row to switch to it; a step list shows the apply progress and a green check when your taskbar matches.';   strip.querySelectorAll('canvas[data-tname]').forEach(function(cv){ drawNameTile(cv.getContext('2d'),128,cv.getAttribute('data-tname')); });   strip.querySelectorAll('img[data-catfb]').forEach(function(im){     im.onerror=function(){@@ -11996,6 +12087,7 @@ function paintThreadExamples(){       const c=states[i];       setSetting('taskbarMode',c[0]);       setSetting('overlayBadges',c[1]?'on':'off');+      showApplyProgress();     };   }); }@@ -12284,13 +12376,7 @@ const server = http.createServer(async (req, res) => {               : { aumidIcons: 'off', threadTiles: 'off' };             savePupSettings({ ...pair, _chosen: { ...(pupSettings()._chosen || {}), aumidIcons: true, threadTiles: true } });             console.log(`[settings] taskbarMode -> ${val} (dashboard dialog)`);-            if (val === 'plain') { stripAllPupIdentities('settings-taskbarmode-plain').catch(() => {}); }-            else {-              for (const [, s7] of [...sessions]) { try { s7._curAppId = null; s7._frozenAppId = null; s7._wikiJumplistKey = undefined; s7._jlFrozen = false; } catch (e) {} }-              try { _registeredPupAumids.clear(); } catch (e) {}-              brandSweep('settings-taskbarmode').catch(() => {});-              reconcileModel('setting taskbarMode').catch(() => {});-            }+            _startModeApply(val);   // v2.0.214: the apply-progress tracker owns the strip/sweep now             return done({ ok: true, settings: pupSettings() });           }           if (key === 'floatyHitPx') {@@ -12311,14 +12397,22 @@ const server = http.createServer(async (req, res) => {           // badges already on glass stayed until some unrelated churn wiped them, so the toggle           // looked broken. OFF now actively clears every painted badge; ON repaints the fleet.           if (key === 'overlayBadges') {+            // v2.0.214: a standalone overlay toggle gets its own one-step progress entry; when a+            // mode switch is mid-flight its tracker already covers the badge pass, so leave it be.+            let _obApply = null;+            if (!_modeApply || _modeApply.done || Date.now() - _modeApply.at > 20000) {+              _obApply = { at: Date.now(), token: ++_modeApplyTok, mode: 'overlay-' + val, steps: [{ label: val === 'on' ? 'Painting overlay badges' : 'Clearing overlay badges', state: 'run' }], done: false };+              _modeApply = _obApply;+            }             (async () => { try {               for (const [sid8, s8] of [...sessions]) {                 if (val === 'off') {                   try { await chrome.abCommand('desktop_taskbar', { ...(await windowTarget(s8, sid8)), overlay: { badge: 'none' } }, { timeoutMs: 5000 }); } catch (e) {}                   s8._appOverlay = false; s8._lastPaintSig = null;-                } else { s8._appOverlay = false; s8._lastPaintSig = null; s8._overlayEnforceAt = 0; }+                } else { s8._appOverlay = false; s8._lastPaintSig = null; s8._overlayEnforceAt = 0; try { await applyAppOverlay(s8, sid8); } catch (e) {} }               }-            } catch (e) {} })();+            } catch (e) {}+            if (_obApply && _modeApply === _obApply) { _obApply.steps[0].state = 'done'; _obApply.done = true; } })();           }           // v2.0.90: the pencil hit-area slider live-applies — re-inject the floaty on every open           // page (the versioned guard includes the px value, so a change tears down and rebuilds).@@ -12575,6 +12669,7 @@ const server = http.createServer(async (req, res) => {       } catch (e) {}       res.writeHead(404); res.end(); return;     }+    if (pathname === '/dashboard/apply-progress') { sendJSON(res, { ok: true, apply: _modeApply }); return; }     if (pathname === '/dashboard/sample-cat') {       // The real registered category tiles (wiki signed-in / app / web) as PNG, for the aumid rows.       try {@@ -13774,9 +13869,10 @@ const server = http.createServer(async (req, res) => {                 if (ob === 'off') {                   try { await chrome.abCommand('desktop_taskbar', { ...(await windowTarget(s8, sid8)), overlay: { badge: 'none' } }, { timeoutMs: 5000 }); } catch (e) {}                   s8._appOverlay = false; s8._lastPaintSig = null;-                } else { s8._appOverlay = false; s8._lastPaintSig = null; s8._overlayEnforceAt = 0; }+                } else { s8._appOverlay = false; s8._lastPaintSig = null; s8._overlayEnforceAt = 0; try { await applyAppOverlay(s8, sid8); } catch (e) {} }               }-            } catch (e) {} })();+            } catch (e) {}+            if (_obApply && _modeApply === _obApply) { _obApply.steps[0].state = 'done'; _obApply.done = true; } })();           }           // v2.0.213 (selftest T6 caught it): pup_configure NEVER handled jumpLists — the arg was           // silently ignored (only the dashboard dialog was wired to refreshAllJumplists), so a