← Commit history

v2.0.702 (insiders): The toolbar pen works on pages that change their address in place (Slack, Gmail, YouTube): it finds its window by the browser's tab id, not by the page address pup last recorded, and falls back to the pup window in front. A pen click that still cannot start shows a ! on the button with the reason, and pup logs it (John's Slack window, 2026-10-07: 'it didn't ever show me the toolbar'). Gates on winvm: smoke all pass, parity 55/56, jump list 12/15; foreground proof passed 3 of 4 runs, one run had 1 of 20 samples with a pup window on top, not reproduced.

John Lauer ·ef739f319c ·1mo ago ·parent 1c01166
7 files changed +40−20
rust/Cargo.lock+12−12
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-pup-bridge"-version = "2.0.701"+version = "2.0.702" dependencies = [  "pup-browser",  "pup-cdp",@@ -655,7 +655,7 @@ dependencies = [  [[package]] name = "pup-browser"-version = "2.0.701"+version = "2.0.702" dependencies = [  "pup-core",  "pup-host",@@ -667,7 +667,7 @@ dependencies = [  [[package]] name = "pup-cdp"-version = "2.0.701"+version = "2.0.702" dependencies = [  "serde_json",  "thiserror",@@ -677,7 +677,7 @@ dependencies = [  [[package]] name = "pup-core"-version = "2.0.701"+version = "2.0.702" dependencies = [  "serde",  "serde_json",@@ -687,7 +687,7 @@ dependencies = [  [[package]] name = "pup-dashboard"-version = "2.0.701"+version = "2.0.702" dependencies = [  "pup-cdp",  "pup-core",@@ -700,7 +700,7 @@ dependencies = [  [[package]] name = "pup-host"-version = "2.0.701"+version = "2.0.702" dependencies = [  "serde",  "serde_json",@@ -709,14 +709,14 @@ dependencies = [  [[package]] name = "pup-host-mac"-version = "2.0.701"+version = "2.0.702" dependencies = [  "pup-host", ]  [[package]] name = "pup-host-win"-version = "2.0.701"+version = "2.0.702" dependencies = [  "pup-core",  "pup-host",@@ -726,7 +726,7 @@ dependencies = [  [[package]] name = "pup-identity"-version = "2.0.701"+version = "2.0.702" dependencies = [  "fontdue",  "image",@@ -736,7 +736,7 @@ dependencies = [  [[package]] name = "pup-session"-version = "2.0.701"+version = "2.0.702" dependencies = [  "pup-core",  "serde",@@ -746,7 +746,7 @@ dependencies = [  [[package]] name = "pup-vault"-version = "2.0.701"+version = "2.0.702" dependencies = [  "pup-core",  "pup-host",@@ -757,7 +757,7 @@ dependencies = [  [[package]] name = "pup-verbs"-version = "2.0.701"+version = "2.0.702" dependencies = [  "base64",  "png 0.17.16",
rust/Cargo.toml+1−1
@@ -4,7 +4,7 @@ members = ["core", "host", "host-win", "host-mac", "cdp", "browser", "session",  # One version for the whole bridge, in lockstep with src/BRIDGE_VERSION (tools/ship.sh bumps both). [workspace.package]-version = "2.0.701"+version = "2.0.702" edition = "2021" license = "MIT" 
rust/verbs/src/annotate.rs+2−1
@@ -20,7 +20,8 @@ pub fn floaty_script(ctx: &Ctx) -> String {  /// pup_annotate {tool: pen|snip, mode: auto|multi}: arm the overlay and watch for the capture. pub fn start(ctx: &Ctx, caller: &Caller, args: &Value) -> Value {-    let s = match crate::resolve(ctx, caller, args) { Ok(s) => s, Err(e) => return e };+    // 2.0.702: say why in the log. A toolbar click that resolved no window used to leave no trace at all.+    let s = match crate::resolve(ctx, caller, args) { Ok(s) => s, Err(e) => { pup_core::log(format!("[annotate] could not start (by {}): {} (tab {:?}, url {:?})", caller.thread, e["error"].as_str().unwrap_or("?"), args.get("_tabId"), args.get("_tabUrl").and_then(|u| u.as_str()).map(|u| u.chars().take(80).collect::<String>()))); return e } };     let Some(t) = s.active().cloned() else { return err("no_tab", "the window has no active tab", None) };     let lane = match crate::transport::lane_for(ctx, &s) { Ok(l) => l, Err(e) => return e };     let tool = args["tool"].as_str().unwrap_or("pen").to_string();
rust/verbs/src/lib.rs+11
@@ -851,7 +851,18 @@ pub fn resolve(ctx: &Ctx, caller: &Caller, args: &Value) -> Result<Session, Valu             // 2.0.453 (John, woke from sleep, toolbar pen did nothing): the Adom toolbar identifies its window by             // the TAB URL (`_tabUrl`, Node's contract); the Rust port never resolved it, so a pen click landed             // here with thread "user-toolbar" and no window. Any tab in any pup window matches, exact first.+            // 2.0.702: the browser's own tab id first. A page that changes its address in place (Slack, Gmail, YouTube)+            // no longer matched pup's recorded URL, so the toolbar pen resolved no window and did nothing (John's Slack+            // window, 2026-10-07). The id never changes.+            if let Some(tid) = args.get("_tabId").and_then(|v| v.as_i64()).map(|v| v.to_string()) {+                if let Some(s) = ctx.sessions.all().into_iter().find(|s| s.tabs.iter().any(|t| t.target_id == tid)) { return Ok(s); }+            }             if let Some((s, _)) = args.get("_tabUrl").and_then(|u| u.as_str()).and_then(|u| session_for_tab_url(ctx, u)) { return Ok(s); }+            // last resort for a human click: the pup window in front is the one they clicked in+            if args.get("_tabUrl").is_some() || args.get("_tabId").is_some() {+                let fg = ctx.host.foreground();+                if fg != 0 { if let Some(s) = ctx.sessions.all().into_iter().find(|s| s.hwnd == Some(fg)) { return Ok(s); } }+            }             let d = thread_default_session_id(&caller.thread);             if ctx.sessions.exists(&d) { d } else {                 let mine = ctx.sessions.owned_by(&caller.thread);
src/extension-annotate/go.js+10−2
@@ -8,11 +8,19 @@       const r = await fetch(`http://127.0.0.1:${port}/health`, { signal: AbortSignal.timeout(1200) });       const j = await r.json();       if (!j || j.bridge !== 'puppeteer') continue;-      await fetch(`http://127.0.0.1:${port}/command`, {+      const res = await fetch(`http://127.0.0.1:${port}/command`, {         method: 'POST',         headers: { 'content-type': 'application/json', 'x-adom-caller-thread': 'user-toolbar', 'x-adom-caller-reason': 'Adom Annotate button' },-        body: JSON.stringify({ command: 'pup_annotate', args: { _tabUrl: tab && tab.url, tool: 'pen', caller: { aiThread: 'user-toolbar' } } }),+        body: JSON.stringify({ command: 'pup_annotate', args: { _tabUrl: tab && tab.url, _tabId: tab && tab.id, tool: 'pen', caller: { aiThread: 'user-toolbar' } } }),       });+      // 2.0.702: a refusal used to vanish with the popup, so a click that did nothing said nothing. Put the reason on the+      // button: a "!" badge and the error in its tooltip.+      try {+        const j = await res.json();+        const okd = j && j.ok !== false && !(j.error && String(j.error).length);+        chrome.action.setBadgeText({ text: okd ? '' : '!' });+        chrome.action.setTitle({ title: okd ? 'Annotate this page (Alt+Shift+A)' : 'Annotate could not start: ' + String(j.error || j.errorCode || 'unknown').slice(0, 180) });+      } catch (e) {}       break;     } catch (e) {}   }
src/extension/popup.js+1−1
@@ -72,7 +72,7 @@ async function cmd(port, command, args) {     if (!btn) return;     const act = btn.dataset.act;     try {-      const base = sid ? { sessionId: sid } : { _tabUrl: tab && tab.url };+      const base = sid ? { sessionId: sid } : { _tabUrl: tab && tab.url, _tabId: tab && tab.id };       let r = null;       if (act === 'annotate') r = await cmd(port, 'pup_annotate', { ...base });       else if (act === 'snip') r = await cmd(port, 'pup_annotate', { ...base, tool: 'snip' });
src/extension/worker.js+3−3
@@ -131,7 +131,7 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {       await fetch(`http://127.0.0.1:${found.port}/command`, {         method: 'POST',         headers: { 'content-type': 'application/json', 'x-adom-caller-thread': 'user-toolbar', 'x-adom-caller-reason': 'floaty pencil click' },-        body: JSON.stringify({ command: 'pup_annotate', args: { _tabUrl: sender.tab.url, tool: msg.tool || 'pen', caller: { aiThread: 'user-toolbar' } } }),+        body: JSON.stringify({ command: 'pup_annotate', args: { _tabUrl: sender.tab.url, _tabId: sender.tab.id, tool: msg.tool || 'pen', caller: { aiThread: 'user-toolbar' } } }),       }).catch(() => {});       sendResponse({ ok: true });     }@@ -163,8 +163,8 @@ chrome.commands.onCommand.addListener(async (command) => {   // v0.2.7: Alt+Shift+V toggles the wiki view — pup resolves the current view server-side   // (view:"toggle"), so the hotkey needs no state.   const payload = command === 'pup-view'-    ? { command: 'pup_wiki_set_view', args: { _tabUrl: tab.url, view: 'toggle', caller: { aiThread: 'user-toolbar' } } }-    : { command: 'pup_annotate', args: { _tabUrl: tab.url, tool: 'pen', caller: { aiThread: 'user-toolbar' } } };+    ? { command: 'pup_wiki_set_view', args: { _tabUrl: tab.url, _tabId: tab.id, view: 'toggle', caller: { aiThread: 'user-toolbar' } } }+    : { command: 'pup_annotate', args: { _tabUrl: tab.url, _tabId: tab.id, tool: 'pen', caller: { aiThread: 'user-toolbar' } } };   await fetch(`http://127.0.0.1:${found.port}/command`, {     method: 'POST',     headers: { 'content-type': 'application/json', 'x-adom-caller-thread': 'user-toolbar', 'x-adom-caller-reason': command === 'pup-view' ? 'Alt+Shift+V view-toggle hotkey' : 'Alt+Shift+A annotate hotkey' },