← Commit history

v0.1.6: badge tracks ACTIVE driving (lazy-correlate on drive verbs + idle-expiry), not ownership - fixes stale badge on an idle owned window

John Lauer ·dba7c51924 ·4mo ago ·parent 68dd557
5 files changed +31−13
bridge/BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.1.5+0.1.6
bridge/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "native-browser",   "displayName": "Adom Native Browser (Chrome + Edge)",-  "version": "0.1.5",+  "version": "0.1.6",   "expectedExtensionVersion": "0.1.2",   "description": "Drives the user's REAL signed-in Chrome/Edge via an installed extension (native-messaging transport). Mirrors the puppeteer bridge's verb surface as nbrowser_* but runs inside the actual logged-in profile, so cookies/SSO/saved-logins/captcha human-trust all work. The bridge binds an ephemeral loopback port (PORTS.md compliant) and writes a discovery file; the native-messaging host pumps stdio <-> this bridge; the extension never touches a TCP port.",   "homepage": "https://wiki.adom.inc/adom/adom-browser-extension",
bridge/package.json+1−1
@@ -1,6 +1,6 @@ {   "name": "adom-native-browser-bridge",-  "version": "0.1.5",+  "version": "0.1.6",   "private": true,   "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)",   "main": "server.js",
bridge/server.js+24−6
@@ -436,8 +436,26 @@ function armBadgeIdle(sid) {   badgeIdle.set(sid, setTimeout(() => { const h = sessionHwnd.get(sid); if (h) taskbar(h, { overlay: { badge: 'none' } }).catch(() => {}); badgeOn.delete(sid); badgeIdle.delete(sid); }, BADGE_IDLE_MS)); } function applyBadge(sid, hwnd) { if (!sid || !hwnd) return; taskbar(hwnd, { overlay: { badge: 'adom', tooltip: 'Adom is driving this browser' } }); badgeOn.add(sid); armBadgeIdle(sid); }-function touchBadge(sid) { if (!sid) return; const h = sessionHwnd.get(sid); if (!h) return; if (!badgeOn.has(sid)) applyBadge(sid, h); else armBadgeIdle(sid); }-function clearBadge(sid) { const t = badgeIdle.get(sid); if (t) clearTimeout(t); badgeIdle.delete(sid); badgeOn.delete(sid); }+// Called on every DRIVE verb: the badge tracks ACTIVE driving, not mere ownership. On the first activity+// (or after a bridge restart) it lazily correlates the driven profile's taskbar window; then it just+// re-arms the idle timer. An IDLE owned window (a thread opened it then went quiet) is NEVER badged here,+// so it fades and stays faded — fixing the "personal profile badged when nothing's driving it" bug.+const badgeTry = new Map(); // sid -> last correlate-attempt ts (throttles the desktop_list_windows call)+async function touchBadge(sid, profileKey) {+  if (!sid) return;+  let h = sessionHwnd.get(sid);+  if (!h) {+    const last = badgeTry.get(sid) || 0;+    if (Date.now() - last < 8000) return; // throttle correlation attempts+    badgeTry.set(sid, Date.now());+    const email = emailOf(profileKey);+    if (!email.includes('@')) return;+    try { const list = await chromeWindowsDetailed(); const m = list.find((w) => (w.title || '').includes(email)); if (m) { h = m.hwnd; sessionHwnd.set(sid, h); } } catch {}+    if (!h) return;+  }+  if (!badgeOn.has(sid)) applyBadge(sid, h); else armBadgeIdle(sid);+}+function clearBadge(sid) { const t = badgeIdle.get(sid); if (t) clearTimeout(t); badgeIdle.delete(sid); badgeOn.delete(sid); badgeTry.delete(sid); } // Capture the driven window's hwnd by DIFFING the real-Chrome window list across the open (the new // hwnd is the one we opened). Robust vs Windows foreground-lock - no GetForegroundWindow guessing. let preOpenHwnds = new Set();@@ -1328,9 +1346,9 @@ async function onHostFrame(frame) {         payload = { ...payload, ...(neu ? { hwnd: neu } : {}), _taskbarHint: flashHint(),           _screenshotHint: 'To SEE this window without foregrounding it: nbrowser_screenshot {sessionId} = the PAGE bitmap (fast, default). To see the WINDOW FRAME or a native popup/dialog (Chrome password dropdown, print dialog, alert/confirm), use AD desktop_screenshot_window {hwnd:' + (neu || '<from desktop_list_windows by title>') + '} - it captures the window + its OWNED CHILD windows (parent/child hwnds). NEVER a fullscreen/monitor screenshot to inspect a browser window.' };       }-      // Badge any adopted SPAWNED windows surfaced by owned/list_windows (they have their own hwnd, no-      // sessionId, so we correlate by bounds on-demand — there is no push channel).-      if (p.verb === 'nbrowser_owned' || p.verb === 'nbrowser_list_windows') { badgeSpawnsIn(payload, p.profileKey).catch(() => {}); }+      // NOTE: list_windows/owned deliberately do NOT badge here anymore — badging an owned-but-IDLE window+      // is what left a "driving" badge on a profile no thread was actually driving. The badge is applied+      // ONLY by active driving (touchBadge, below) + open_window, and idle-expires on its own.       if (p.verb === 'nbrowser_close_window' || p.verb === 'nbrowser_cleanup') { // clean up the taskbar when we stop driving         const sid = payload.sessionId;         const h = (sid && sessionHwnd.get(sid)) || lastHwnd;@@ -1342,7 +1360,7 @@ async function onHostFrame(frame) {         }       }       maybeAutoFlash(p.verb, payload);-      if (payload && payload.sessionId && ACTIVITY.has(p.verb)) touchBadge(payload.sessionId); // keep the badge alive while actively driving; it idle-expires when commands stop+      if (payload && payload.sessionId && ACTIVITY.has(p.verb)) touchBadge(payload.sessionId, p.profileKey).catch(() => {}); // badge = ACTIVE driving; lazily correlates, idle-expires when commands stop       resolve_ok(p.resolve, payload);     } else p.resolve({ success: false, error: frame.error || 'unknown error', _hint: frame._hint });     return;
native-browser-bridge-manifest.json+4−4
@@ -1,8 +1,8 @@ {   "manifest_version": 1,-  "version": "0.1.5",-  "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.5/native-browser-bridge-v0.1.5.bin",-  "sha256": "37900c24ec1d583f201f74577c41523c0864cf4fa7bc4025e907eee80767edad",-  "size": 54953,+  "version": "0.1.6",+  "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.6/native-browser-bridge-v0.1.6.bin",+  "sha256": "0d8f72596f87f42c9d8b42797d6c0fc07838c3722bafe17d0b227c24f8a3f105",+  "size": 55297,   "released_at": "2026-07-14T00:00:00Z" }\ No newline at end of file