app
Shotlog - Screenshot Log
Public Made by Adomby adom
See the shots your AI never showed you, and know it actually SAW them. adom-shotlog streams every screenshot the AI gathers into a live gallery (webview/pup/browser/phone), reports whether a human is really watching and on which surface, and ingests Adom Bridge child-window arrays via --meta.
← Commit history
Publish 0.18.2
9 files changed
+27−13
Cargo.lock+1−1@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-shotlog"-version = "0.18.1"+version = "0.18.2" dependencies = [ "axum", "base64",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-shotlog"-version = "0.18.1"+version = "0.18.2" edition = "2021" description = "Screenshot log viewer and CLI for Adom" license = "MIT"
VERSION+1−1@@ -1 +1 @@-0.18.1+0.18.2
bin/adom-shotlog⋯ 1 unchanged line ⋯
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-shotlog",- "version": "0.18.1",+ "version": "0.18.2", "type": "app", "title": "Adom Shotlog", "description": "See the shots your AI never showed you, and know it actually SAW them. adom-shotlog streams every screenshot the AI gathers into a live gallery (webview/pup/browser/phone), reports whether a human is really watching and on which surface, and ingests Adom Desktop child-window arrays via --meta.",
page.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-shotlog",- "version": "0.18.1",+ "version": "0.18.2", "type": "app", "title": "Adom Shotlog", "description": "See the shots your AI never showed you, and know it actually SAW them. adom-shotlog streams every screenshot the AI gathers into a live gallery (webview/pup/browser/phone), reports whether a human is really watching and on which surface, and ingests Adom Desktop child-window arrays via --meta.",
src/html/index.html+6@@ -778,9 +778,12 @@ header { // destination viewer's WebSocket actually connects (the full signal) -> // only then close this (source) surface. Any failure leaves this surface // open and reports exactly what happened. Nothing is ever closed blind.+ const uiLog=(line)=>{ fetch('api/log',{method:'POST',headers:{'Content-Type':'application/json'},+ body:JSON.stringify({line:'UI '+line})}).catch(()=>{}); }; btn.addEventListener('click', async ()=>{ if(btn.disabled) return; btn.disabled=true;+ uiLog('switch click: '+channel+' -> '+dest); let opened=false; try{ showToast(toPup?'Opening the pup window…':'Opening the webview panel…', 3500);@@ -802,6 +805,7 @@ header { const op=await fetch('api/canvas-open',{method:'POST',headers:{'Content-Type':'application/json'}, body:JSON.stringify({channel, to:dest})}).then(r=>r.json()); if(!op.ok){+ uiLog('switch FAILED opening '+dest+' for '+channel+': '+String(op.error||op.detail||'').slice(0,160)); showToast('could not open '+dest+': '+String(op.error||op.detail||'unknown').slice(0,300)+' - this tab stays open.', 10000); restore(); return; }@@ -823,11 +827,13 @@ header { busy('rolling back…'); try{ await fetch('api/canvas-close',{method:'POST',headers:{'Content-Type':'application/json'}, body:JSON.stringify({channel, surface:dest})}); }catch{}+ uiLog('switch ROLLBACK: '+dest+' never connected for '+channel+' (30s)'); showToast(dest+' opened but never connected (waited 30s) - rolled back, this tab stays open. '+(toPup?'Is the pup window blocked or the laptop asleep?':'Is Hydrogen open?'), 12000); restore(); return; } // Stage 3: destination is LIVE. Now (and only now) close this surface.+ uiLog('switch OK: '+channel+' now on '+dest); busy('switched ✓'); showToast(dest+' is live - closing this '+src+' tab.', 4000); await fetch('api/canvas-close',{method:'POST',headers:{'Content-Type':'application/json'},
src/open.rs+11−8@@ -211,11 +211,18 @@ pub fn open_pup(channel: &str, port: u16, swap: bool) { // tab exists -> switch_tab (2 trips) // session, no tab -> open_tab (2 trips) let needle = format!("/log/{channel}/");+ // The bridge's tab REGISTRY can outlive the actual Chrome window (a closed+ // window leaves GHOST tab records; verbs then fail "browser is not+ // connected"). So: try the cheap path, but ANY failure of switch/open_tab+ // falls through to browser_open_window, which revives a dead session.+ let open_window = |ad: &dyn Fn(&[&str]) -> (bool, String)| -> bool {+ let win_json = format!("{{\"sessionId\":\"shotlog\",\"profile\":\"shotlog\",\"url\":\"{url}\"}}");+ ad(&["browser_open_window", &win_json]).0+ }; let (listed, list_out) = ad(&["browser_list_tabs", "{\"sessionId\":\"shotlog\"}"]); let mut opened = false; if !listed || list_out.contains("session_not_found") {- let win_json = format!("{{\"sessionId\":\"shotlog\",\"profile\":\"shotlog\",\"url\":\"{url}\"}}");- opened = ad(&["browser_open_window", &win_json]).0;+ opened = open_window(&ad); } else { let existing = serde_json::from_str::<serde_json::Value>(&list_out).ok() .and_then(|v| v.get("tabs").and_then(|t| t.as_array()).and_then(|tabs| {@@ -224,14 +231,10 @@ pub fn open_pup(channel: &str, port: u16, swap: bool) { })); if let Some(tid) = existing { let sw = format!("{{\"sessionId\":\"shotlog\",\"tabId\":\"{tid}\"}}");- opened = ad(&["browser_switch_tab", &sw]).0;+ opened = ad(&["browser_switch_tab", &sw]).0 || open_window(&ad); } else { let tab_json = format!("{{\"sessionId\":\"shotlog\",\"url\":\"{url}\"}}");- let (ok, out) = ad(&["browser_open_tab", &tab_json]);- opened = ok || (out.contains("session_not_found") && {- let win_json = format!("{{\"sessionId\":\"shotlog\",\"profile\":\"shotlog\",\"url\":\"{url}\"}}");- ad(&["browser_open_window", &win_json]).0- });+ opened = ad(&["browser_open_tab", &tab_json]).0 || open_window(&ad); } }
src/server.rs+5@@ -385,6 +385,11 @@ async fn canvas_open( }) .await .unwrap_or_else(|_| serde_json::json!({"ok": false, "error": "spawn failed"}));+ let okf = v.get("ok").and_then(|b| b.as_bool()).unwrap_or(false);+ let mut detail = v.get("detail").or_else(|| v.get("error")).and_then(|d| d.as_str()).unwrap_or("").replace('\n', " | ");+ detail.truncate(220);+ println!("{} DECISION canvas-open result ok={okf}{}", ts(),+ if okf { String::new() } else { format!(" detail: {detail}") }); Json(v) }