← Commit history

Taskbar mimic made FAITHFUL (John's on-glass audit: 'i want to see the full icon you're showing in the taskbar, i.e. the chrome icon and then exactly where the overlay is placed. and right now your icons in the dashboard are inconsistent with what's in the taskbar'). The audit against a real taskbar screenshot found three lies: the strip blew the overlay badge up as the whole button instead of showing the base icon with the badge in its corner; the grouping collapsed AUMID-stamped windows (which keep their OWN real buttons with the teal category tile until closed, even under minimal-touch) into the process bucket, so the dashboard showed fewer buttons than the taskbar; and the badge shown was wins[0]'s dump rather than what the button actually wears. Fixes: (1) each strip button now renders the REAL base icon - extracted from the browser exe that owns the profile process via PowerShell ExtractAssociatedIcon and cached, so Edge shows Edge - with the painted overlay at TOP-RIGHT at ~55%, the placement measured from his actual taskbar, and NO overlay when none is painted, because a bare Chrome button must render bare; (2) grouping honours a still-stamped _curAppId as its own button with its category tile (served from the shipped pup-cat icos via the existing ico extractor), only unstamped windows share the browser-process button; (3) the badge shown is the NEWEST actually-painted overlay in the group, mirroring last-painter-wins on the real button. Tree group headers use the same base+overlay stack.

John Lauer ·b628674e2a ·2mo ago ·parent f967f2c
4 files changed +82−14
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "1.9.234",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.234/adom-bridge-puppeteer-v1.9.234.zip",-  "sha256": "22c8ecafbb0c5d89eae0d7f84e23e4e20c84c3f64ba57574c9d63f483543e2e2",-  "size": 456496,+  "version": "1.9.235",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.235/adom-bridge-puppeteer-v1.9.235.zip",+  "sha256": "f02619d369adcee7f45746a2938f90793e4de73ad9b78a18c01571548e6b15ef",+  "size": 457962,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.234+1.9.235
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "1.9.234",+  "version": "1.9.235",   "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. browser_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. browser_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, browser_alert_window posts a native toast whose click focuses the window, browser_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-desktop",   "author": "Adom Inc.",
src/server.js+76−8
@@ -7828,11 +7828,30 @@ async function dashboardSnapshot() {     // hwnd/session invented a button per window and the dashboard showed more rows than the real     // taskbar had icons. Key by PROFILE (= one Chrome process = one button), which is why the header     // count "chromes N" equals the number of real taskbar icons.-    if (minimalTouch()) { key = 'chrome-process:' + w.profile; label = 'Chrome (' + w.profile + ', unbranded)'; }-    else if (grouping === 'grouped') { key = 'Adom.Pup'; label = 'Adom Pup (grouped)'; }-    else { key = (w._curAppId || ('Adom.Pup.' + w.sessionId)); label = w.sessionId; }-    if (!buttons.has(key)) buttons.set(key, { key, label, grouping: minimalTouch() ? 'minimal-touch' : grouping, windows: [] });-    buttons.get(key).windows.push(w.sessionId);+    // v1.9.235 (John's on-glass audit): a window that STILL CARRIES a per-session AUMID (stamped+    // before minimal-touch, kept until close) has its OWN real taskbar button with the category tile+    // as its base icon - grouping those into the process bucket made the dashboard show fewer+    // buttons than the real taskbar. Honour the stamp when it exists; only unstamped windows share+    // the browser-process button.+    if (w._curAppId && w._curAppId !== 'Adom.Pup') {+      key = w._curAppId; label = w.sessionId;+      const catM = /\.([a-z-]+)\.i\d+$/.exec(w._curAppId);+      var baseIcon = '/dashboard/caticon?cat=' + encodeURIComponent((catM && catM[1]) || 'default');+      var kind = 'aumid';+    } else if (!minimalTouch() && grouping === 'grouped') {+      key = 'Adom.Pup'; label = 'Adom Pup (grouped)';+      baseIcon = '/dashboard/caticon?cat=default'; kind = 'aumid';+    } else {+      key = 'chrome-process:' + w.profile; label = 'Chrome (' + w.profile + ')';+      baseIcon = '/dashboard/baseicon?profile=' + encodeURIComponent(w.profile); kind = 'process';+    }+    if (!buttons.has(key)) buttons.set(key, { key, label, kind, baseIcon, grouping: minimalTouch() ? 'minimal-touch' : grouping, windows: [], badgeSession: null, badgeAt: 0 });+    const btn = buttons.get(key);+    btn.windows.push(w.sessionId);+    // The real button wears whatever pup painted LAST - mirror that, and show NO overlay when+    // nothing is currently painted (a bare Chrome button must render bare here too).+    const paintedAt = (w.overlay && !String(w.overlay).startsWith('none') && w.overlayWhy && w.overlayWhy.at) || 0;+    if (paintedAt > btn.badgeAt) { btn.badgeAt = paintedAt; btn.badgeSession = w.sessionId; }   }   const threads = [...new Set(wins.map(w => w.owner || w.ownerThread).filter(Boolean))].sort();   const profiles = {};@@ -7879,7 +7898,8 @@ select,input{background:#111b23;color:#e6edf3;border:1px solid #23303c;border-ra .tbi{position:relative;width:48px;height:42px;border-radius:8px;background:#0f1720;border:1px solid #1d2833;display:flex;align-items:center;justify-content:center;cursor:pointer;flex:0 0 auto} .tbi:hover{border-color:#12e0c4;background:#12202a} .tbi.fg{border-color:#12e0c4}-.tbi img{width:28px;height:28px;object-fit:contain;border-radius:6px}+.tbi img.base{width:30px;height:30px;object-fit:contain}+.tbi img.ov{position:absolute;top:1px;right:1px;width:17px;height:17px;object-fit:contain;border-radius:4px} .tbi .n{position:absolute;top:-6px;right:-6px;background:#12e0c4;color:#06282a;font-size:10px;font-weight:700;border-radius:8px;min-width:16px;height:16px;line-height:16px;text-align:center;padding:0 3px} .tbi .u{position:absolute;bottom:2px;left:50%;transform:translateX(-50%);width:16px;height:3px;border-radius:2px;background:#12e0c4} @@ -7893,6 +7913,9 @@ main{padding:14px 18px} .tog.leaf{visibility:hidden} .ico{width:22px;height:22px;flex:0 0 22px;object-fit:contain;border-radius:5px;background:#0f1720} .ico.sm{width:16px;height:16px;flex:0 0 16px}+.icostack{position:relative;flex:0 0 22px;width:22px;height:22px}+.icostack .ico{position:absolute;inset:0}+.icostack .icoov{position:absolute;top:-3px;right:-3px;width:13px;height:13px;object-fit:contain;border-radius:3px} .nm{font-weight:600} .nm.dim{font-weight:400;color:#c3d0da} .meta{color:#7d8f9f;font-size:12px}@@ -7952,10 +7975,15 @@ function render(){   const groups=(d.taskbarButtons||[]).map(b=>({...b,wins:b.windows.map(s=>byId[s]).filter(w=>w&&match(w))})).filter(g=>g.wins.length);    // taskbar strip (real order, real badge)+  // v1.9.235 (John's audit): each strip button mimics the REAL taskbar button - the browser/category+  // BASE icon full-size, with the painted overlay badge in the TOP-RIGHT corner at ~55% (measured+  // from his actual taskbar), and NO overlay at all when pup has not painted one (a bare Chrome+  // button must render bare here too, otherwise the mimic lies).   _q('#taskbar').innerHTML='<span class="lbl">TASKBAR</span>'+groups.map((g,i)=>{     const fg=g.wins.some(w=>w.foreground);     return '<div class="tbi'+(fg?' fg':'')+'" data-jump="g'+i+'" title="'+esc(g.label)+'">'-      +'<img src="'+badge(g.wins[0].sessionId)+'" data-fallback="hide">'+      +'<img class="base" src="'+esc(g.baseIcon||'')+'" data-fallback="hide">'+      +(g.badgeSession?'<img class="ov" src="'+badge(g.badgeSession)+'" data-fallback="hide">':'')       +(g.wins.length>1?'<span class="n">'+g.wins.length+'</span>':'')       +(fg?'<span class="u"></span>':'')+'</div>';   }).join('');@@ -7965,7 +7993,8 @@ function render(){     const gid='g'+gi, gcoll=COLL.has(gid);     return '<div class="tbnode" id="'+gid+'">'       +'<div class="row"><span class="tog" data-toggle="'+gid+'">'+(gcoll?'▸':'▾')+'</span>'-        +'<img class="ico" src="'+badge(g.wins[0].sessionId)+'" data-fallback="hide">'+        +'<span class="icostack"><img class="ico" src="'+esc(g.baseIcon||'')+'" data-fallback="hide">'+        +(g.badgeSession?'<img class="icoov" src="'+badge(g.badgeSession)+'" data-fallback="hide">':'')+'</span>'         +'<span class="nm">'+esc(g.label)+'</span>'         +'<span class="meta">'+g.wins.length+' window'+(g.wins.length>1?'s':'')+' &middot; '+esc(g.grouping)+'</span>'         +'<span class="acts"><button class="btn sm" data-act="flash-group" data-ids="'+esc(g.windows.join(','))+'">flash</button>'@@ -8163,6 +8192,45 @@ const server = http.createServer(async (req, res) => {         res.end(buf); return;       } catch (e) { res.writeHead(404); res.end(); return; }     }+    if (pathname === '/dashboard/baseicon') {+      // v1.9.235 (John's audit): the taskbar strip must show the FULL button - the real browser icon+      // with the overlay in its corner - not the overlay blown up as the whole button. Extract the+      // actual icon from the browser exe that owns the profile's process (PowerShell, once, cached),+      // so if the machine runs Edge we show Edge, not a hardcoded Chrome.+      try {+        const prof = url.searchParams.get('profile') || '';+        const cache = path.join(require('os').tmpdir(), 'pup-badge-debug', `base-${prof.replace(/[^a-z0-9-]/gi,'_')}.png`);+        if (!fs.existsSync(cache)) {+          const be = browsers.get(prof);+          const pid = be && be.browser && (be.browser._detachedPid || (() => { try { return be.browser.process()?.pid; } catch (e) { return null; } })());+          if (!pid) { res.writeHead(404); res.end(); return; }+          const ps1 = path.join(require('os').tmpdir(), `_adom_baseicon_${process.pid}.ps1`);+          fs.writeFileSync(ps1, [+            'Add-Type -AssemblyName System.Drawing',+            `$p = (Get-Process -Id ${Number(pid)} -ErrorAction Stop).Path`,+            '$i = [System.Drawing.Icon]::ExtractAssociatedIcon($p)',+            '$b = $i.ToBitmap()',+            `$b.Save('${cache.split(String.fromCharCode(92)).join(String.fromCharCode(92,92))}', [System.Drawing.Imaging.ImageFormat]::Png)`,+          ].join(String.fromCharCode(13,10)));+          await runPsHiddenPromise(ps1, { timeout: 8000 });+        }+        const buf = fs.readFileSync(cache);+        res.writeHead(200, { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=600' });+        res.end(buf); return;+      } catch (e) { res.writeHead(404); res.end(); return; }+    }+    if (pathname === '/dashboard/caticon') {+      // Base icon for an AUMID-stamped button: the pup category .ico that AUMID was registered with.+      try {+        const cat = (url.searchParams.get('cat') || 'default').replace(/[^a-z0-9-]/gi, '');+        let f = path.join(__dirname, 'icons', `pup-cat-${cat}9.ico`);+        if (!fs.existsSync(f)) f = path.join(__dirname, 'icons', 'pup-cat-default9.ico');+        const inner = icoLargestPng(fs.readFileSync(f));+        if (!inner) { res.writeHead(404); res.end(); return; }+        res.writeHead(200, { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=3600' });+        res.end(inner); return;+      } catch (e) { res.writeHead(404); res.end(); return; }+    }     if (pathname === '/dashboard/badge') {       // Serve the exact overlay PNG pup last composed for a session, so the dashboard shows the REAL       // badge rather than a re-render that might differ from what Windows was handed.