← Commit history

Update 3 file(s)

John Lauer ·adccfa8e85 ·2mo ago ·parent 48315cc
2 files changed +26−6
CHANGELOG.md+9
@@ -1,5 +1,14 @@ # Changelog — Puppeteer Bridge +## 2.0.443 — 2026-09-06 — no window left behind: closes verified against the OS, orphan windows swept on connect++Recreating John's windows across reinstalls left three Chrome windows nobody tracked, wearing old tiles:+a close that raced a bridge respawn had no live CDP handle, dropped the session and left the window.+pup_close_window now waits for the OS window to go and, if it survives, closes it through ab (WM_CLOSE,+forced after 3 s). And the connect-time sweep now closes any WINDOW no session owns (judged by window,+so a user's extra tab in a live pup window is never touched), not only about:blank leftovers.+Also the tile text gets the width the rounded plate allows (0.90 of the tile from 7.5% in).+ ## 2.0.442 — 2026-09-06 — thread tiles through the brand raster pipeline, in Rust, on the Adom rounded plate  John: rounded corners like the Adom favicon, two lines at most, no ellipsis, and the downscaling that the
rust/verbs/src/lib.rs+17−6
@@ -433,17 +433,24 @@ pub fn touch(ctx: &Ctx, sid: &str, note: Option<&str>) { ctx.sessions.with_mut(s /// published, so no verb can be creating a target in the gap. Popups (openerId) and every target a /// session owns are never touched. fn close_orphan_blank_targets(ctx: &Ctx, cdp: &Cdp, profile: &str) {+    // Orphans: (1) the about:blank launch window an older build left behind, and (2) whole WINDOWS no+    // session owns any more, left by a close that raced a bridge respawn (John's laptop, 2026-09-06:+    // three leftover windows wearing old tiles). A window is orphaned only when NONE of its tabs belongs+    // to a session, so a user's extra tab in a live pup window is never touched; popups never are.     let Ok(targets) = cdp.targets() else { return };     let owned: std::collections::HashSet<String> = ctx.sessions.all().iter().flat_map(|s| s.tabs.iter().map(|t| t.target_id.clone())).collect();+    let pages: Vec<&Value> = targets.iter().filter(|t| t["type"] == json!("page") && !t.get("openerId").and_then(|o| o.as_str()).map(|o| !o.is_empty()).unwrap_or(false)).collect();+    let mut win_of: HashMap<String, i64> = HashMap::new();+    for t in &pages { if let Some(tid) = t["targetId"].as_str() { if let Ok((wid, _)) = cdp.window_for_target(tid) { win_of.insert(tid.to_string(), wid); } } }+    let owned_windows: std::collections::HashSet<i64> = win_of.iter().filter(|(tid, _)| owned.contains(*tid)).map(|(_, w)| *w).collect();     let mut closed = 0;-    for t in targets {-        let tid = t["targetId"].as_str().unwrap_or("");-        let is_page = t["type"] == json!("page");+    for t in &pages {+        let tid = t["targetId"].as_str().unwrap_or(""); if tid.is_empty() || owned.contains(tid) { continue; }         let blank = t["url"].as_str().map(|u| u == "about:blank" || u.is_empty()).unwrap_or(false);-        let popup = t.get("openerId").and_then(|o| o.as_str()).map(|o| !o.is_empty()).unwrap_or(false);-        if is_page && blank && !popup && !tid.is_empty() && !owned.contains(tid) { if cdp.close_target(tid).is_ok() { closed += 1; } }+        let orphan_window = win_of.get(tid).map(|w| !owned_windows.contains(w)).unwrap_or(false);+        if blank || orphan_window { if cdp.close_target(tid).is_ok() { closed += 1; pup_core::log(format!("[launch] closed orphan {} in profile \"{profile}\": {}", if blank { "about:blank window" } else { "window no session owns" }, t["url"].as_str().unwrap_or("").chars().take(60).collect::<String>())); } }     }-    if closed > 0 { pup_core::log(format!("[launch] closed {closed} orphan about:blank window(s) in profile \"{profile}\" (untracked launch window, stock taskbar icon)")); }+    if closed > 0 { pup_core::log(format!("[launch] {closed} orphan window(s) closed in profile \"{profile}\"")); } }  pub fn browser_for(ctx: &Ctx, profile: &str) -> Option<Arc<BrowserHandle>> { ctx.browsers.lock().unwrap().get(profile).cloned().filter(|b| b.cdp.is_alive()) }@@ -884,6 +891,10 @@ pub fn close_window(ctx: &Ctx, caller: &Caller, args: &Value) -> Value {     let mut closed = Vec::new(); let mut chrome_killed = 0;     for s in targets {         if let Some(b) = browser_for(ctx, &s.profile) { for t in &s.tabs { let _ = b.cdp.close_target(&t.target_id); } }+        // Verify the OS window is gone; a close that races a bridge respawn (no live CDP handle yet) used to+        // drop the session and leave the window behind (John's laptop, 2026-09-06: three leftovers wearing+        // old tiles). The window is closed through ab (WM_CLOSE, forced after 3 s) when it survives.+        if let Some(h) = s.hwnd { std::thread::sleep(Duration::from_millis(300)); if ctx.host.is_alive(h) { let _ = ctx.ab.call("desktop_close_window", json!({ "hwnd": h, "forceAfterMs": 3000 }), caller, Duration::from_secs(6)); pup_core::log(format!("[close] \"{}\" window {h} survived the CDP close; closed through the OS", s.id)); } }         ctx.sessions.remove(&s.id); ctx.sessions.delete_file(&s.id); closed.push(s.id.clone());         // last window on that profile: let the browser go (Node: killProfileBrowserIfUnused)         if ctx.sessions.for_profile(&s.profile).is_empty() {