← Commit history

Fix jump-list first-right-click blip: stop stamping a transient .plain AUMID while a window is still loading (it created a jump-list-less button that Explorer resolved first), and clear an AUMIDs jump list when unregistering so stale CustomDestinations files stop piling up

John Lauer ·23a60c2961 · ·parent eec0dfa
4 files changed +43−8
adom-bridge-puppeteer-manifest.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "1.9.51",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.51/adom-bridge-puppeteer-v1.9.51.zip",-  "sha256": "216796fc4447779b85db47f69c6d315c692544754cb28304ca7a2a276c717465",-  "size": 299556,+  "version": "1.9.52",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.52/adom-bridge-puppeteer-v1.9.52.zip",+  "sha256": "54a45ac9b5412f30ad915ed9849e4e14b738b7c073291f91a6e5764eedc6dd37",+  "size": 300513,   "verbPrefixes": [     "browser_",     "desktop_recorder_",
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.51+1.9.52
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "1.9.51",+  "version": "1.9.52",   "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.",   "homepage": "https://wiki.adom.inc/adom/adom-desktop",   "author": "Adom Inc.",
src/server.js+37−2
@@ -1900,9 +1900,32 @@ async function stampPupIdentity(sessionId) {     // windows are transient and their pins launch via relaunchCommand anyway, so tile-tracks-state     // wins here; GROUPED mode keeps the stable pinnable Adom.Pup.     const grouped = pupSettings().taskbarGrouping === 'grouped';-    const cat = pupCategory(sessionId) || 'plain';-    const appId = grouped ? PUP_APP_ID : `${PUP_APP_ID}.${sessionId}.${cat}.${PUP_ICON_GEN}`;     const sess = sessions.get(sessionId);+    // v1.9.52 — DO NOT stamp a transient ".plain" identity for a window that is merely still LOADING.+    // A freshly-opened window sits on about:blank for a beat (category null -> "plain"), then lands on+    // its real URL (category web/wiki/app). The old code stamped ".plain" during that beat, which+    // REGISTERED an Adom.Pup.<sid>.plain.<gen> AUMID — a full taskbar identity with NO jump list — and+    // created a button under it, then created ANOTHER button under ".category" a moment later. Windows+    // caches the button->AUMID->jumplist association per button; that plain->category churn is what+    // made the jump list "blip out and need a second right-click" (Explorer first resolved the stale+    // ".plain" association, which had no list). Skipping the plain stamp while a real URL is loading+    // means the button is created ONCE, under its final category AUMID, with its jump list already+    // attached — so the menu resolves on the first click. A genuinely category-less window (a page+    // that never leaves about:blank) still gets "plain" after a few attempts so it is never unbranded.+    const rawCat = pupCategory(sessionId);+    if (!grouped && !rawCat) {+      const hasRealUrlLoading = (sess && (sess.tabs || []).some(t => {+        try { const u = t.page && t.page.url(); return u && !/^(about:blank|chrome|edge|devtools):/i.test(u); } catch (e) { return false; }+      }));+      const attempts = (sess && sess._plainStampAttempts) || 0;+      if (hasRealUrlLoading || attempts < 6) {+        if (sess) sess._plainStampAttempts = attempts + 1;+        return false; // pending — the retry loop / framenavigated recheck re-stamps once the category is known+      }+    }+    if (sess) sess._plainStampAttempts = 0;+    const cat = rawCat || 'plain';+    const appId = grouped ? PUP_APP_ID : `${PUP_APP_ID}.${sessionId}.${cat}.${PUP_ICON_GEN}`;     const prevAppId = sess && sess._curAppId;     // Register the NEW appId BEFORE stamping (AD's verified order) so the fresh button resolves     // its icon/name from the registry the moment it is created.@@ -2026,6 +2049,13 @@ function pruneStaleAumids() { async function unregisterPupAumid(appId) {   if (process.platform !== 'win32' || !appId || appId === PUP_APP_ID) return;   _registeredPupAumids.delete(appId);+  // v1.9.52: CLEAR this AUMID's jump list before unregistering it. Otherwise its CustomDestinations+  // file lingers in the shell (measured: 6 stale jump-list files for 3 live windows, because every+  // category flip mints a new AUMID and orphans the old one's list). Explorer resolving a button+  // against a pile of stale-but-present jump lists is part of the first-right-click "blip".+  if (_jumplistSupported !== false) {+    try { await chrome.adCommand('desktop_set_window_jumplist', { appId, tasks: [] }, { timeoutMs: 5000 }); } catch (e) {}+  }   try { await chrome.adCommand('desktop_unregister_app_identity', { appId }, { timeoutMs: 5000 }); } catch (e) {} } @@ -2108,6 +2138,7 @@ async function updateWikiJumplist(session, sessionId) {     if (_isUnknownVerb(r, raw)) { _jumplistSupported = false; return; }     _jumplistSupported = true;     session._wikiJumplistView = view;+    console.log(`[jumplist] "${sessionId}" set ${tasks.length} task(s) on ${appId} (view=${view || 'none'}${view ? '+' : ''}close-all)`);   } catch (e) {} } // v1.8.80: TAB-COUNT overlay badge (John: "if the ai opens 8 tabs that makes that pup window@@ -2888,6 +2919,10 @@ async function refreshWindowChrome(session, sessionId, tries = 5) { async function brandPupWindow(sessionId) {   const stamped = await stampPupIdentity(sessionId);   if (stamped) return true;+  // v1.9.52: if identity IS supported, a false here is a transient "not yet" (window not findable,+  // or category still pending) — NOT a reason to paint the legacy badge, which would fight the+  // identity path. Only fall back to the badge on AD builds without identity support.+  if (_identitySupported === true) return false;   return badgePupTaskbar(sessionId); } async function badgePupTaskbar(sessionId) {