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.23.7
9 files changed
+89−8
Cargo.lock+1−1@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-shotlog"-version = "0.23.6"+version = "0.23.7" dependencies = [ "axum", "base64",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-shotlog"-version = "0.23.6"+version = "0.23.7" edition = "2021" description = "Screenshot log viewer and CLI for Adom" license = "MIT"
VERSION+1−1@@ -1 +1 @@-0.23.6+0.23.7
bin/adom-shotlog⋯ 1 unchanged line ⋯
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-shotlog",- "version": "0.23.6",+ "version": "0.23.7", "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.23.6",+ "version": "0.23.7", "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+9@@ -303,6 +303,14 @@ header { opacity: 0; transition: opacity .18s; pointer-events: none; z-index: 200; max-width: min(360px, calc(100vw - 24px)); width: max-content; text-align: center; box-shadow: 0 8px 28px rgba(0,0,0,.5); }+/* When the countdown card holds bottom-center, the toast rides ABOVE it+ instead of stacking into the same pixels (JS adds .lifted). */+.toast.lifted { bottom: 96px; }+/* Skinny panel: tighter toast, never edge-to-edge slab */+@media (max-width: 520px) {+ .toast { font-size: 11px; padding: 7px 11px; border-radius: 10px;+ max-width: calc(100vw - 32px); }+} .toast.show { opacity: 1; } /* In-app helper dialog (e.g. "pup needs Adom Desktop") - NEVER a native@@ -628,6 +636,7 @@ header { // ── Helpers ────────────────────────────────────────────────────────────── let toastTimer=null; function showToast(msg, ms){ toast.textContent=msg; toast.classList.add('show');+ toast.classList.toggle('lifted', !!document.querySelector('.cdown')); // never under the countdown card clearTimeout(toastTimer); toastTimer=setTimeout(()=>toast.classList.remove('show'), ms||1800); } function esc(s){ return String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); } function fmtTime(ts){ const d=new Date(ts), h=d.getHours(), mm=String(d.getMinutes()).padStart(2,'0');
src/server.rs+20−3@@ -14,6 +14,23 @@ use crate::store::Store; const INDEX_HTML: &str = include_str!("html/index.html"); const ICON_SVG: &str = include_str!("html/icon.svg");++/// Version stamp for the page<->broker reload handshake: version PLUS the+/// broker process start time. Same-version redeploys used to slip past a+/// version-only stamp, leaving humans staring at stale UIs ("dude if you make+/// changes you have to refresh your surfaces"). Any restart now makes every+/// connected page reload exactly once on reconnect (guard is keyed by stamp).+fn boot_stamp() -> &'static str {+ use std::sync::OnceLock;+ static STAMP: OnceLock<String> = OnceLock::new();+ STAMP.get_or_init(|| {+ let epoch = std::time::SystemTime::now()+ .duration_since(std::time::UNIX_EPOCH)+ .map(|d| d.as_secs())+ .unwrap_or(0);+ format!("{}+{}", env!("CARGO_PKG_VERSION"), epoch)+ })+} // Raster favicons: Chrome ignores SVG favicons for app-mode windows, pinned // tabs, and the OS taskbar (what pup windows show) — those need PNG/ICO. const ICON_PNG_32: &[u8] = include_bytes!("icons/favicon-32.png");@@ -164,7 +181,7 @@ async fn serve_viewer() -> Response { // no-cache: the viewer UI must NEVER go stale in a long-lived webview/pup // tab - without this, browsers heuristically cached old HTML across // deploys and reloads showed a months-old UI.- let html = html.replacen("__SHOTLOG_VERSION__", env!("CARGO_PKG_VERSION"), 1);+ let html = html.replacen("__SHOTLOG_VERSION__", boot_stamp(), 1); let mut res = Html(html).into_response(); res.headers_mut().insert( header::CACHE_CONTROL,@@ -1221,7 +1238,7 @@ async fn ws_all_connection(mut socket: WebSocket, state: Arc<AppState>) { let mut conn_surface: String = "browser".into(); state.store.register_viewer("_all", vid, "browser".into(), true, None).await; let _ = socket.send(Message::Text(- serde_json::json!({"type": "welcome", "version": env!("CARGO_PKG_VERSION")}).to_string().into()+ serde_json::json!({"type": "welcome", "version": boot_stamp()}).to_string().into() )).await; loop { tokio::select! {@@ -1316,7 +1333,7 @@ async fn ws_connection(mut socket: WebSocket, channel: String, state: Arc<AppSta state.store.register_viewer(&channel, vid, "browser".into(), true, None).await; // Version handshake: stale pages reload themselves on mismatch. let _ = socket.send(Message::Text(- serde_json::json!({"type": "welcome", "version": env!("CARGO_PKG_VERSION")}).to_string().into()+ serde_json::json!({"type": "welcome", "version": boot_stamp()}).to_string().into() )).await; loop {
tools/deploy.shadded+55@@ -0,0 +1,55 @@+#!/bin/bash+# The ONLY sanctioned way to deploy shotlog locally. Build -> install (both+# PATH copies) -> restart broker -> verify served stamp -> REFRESH EVERY LIVE+# SURFACE. The human must SEE updates without doing anything (John, 2026-07-18:+# "if you make changes you have to refresh your surfaces so i see your+# updates" ... "always do that").+#+# Two refresh layers, on purpose:+# 1. boot-stamp handshake: every restart changes the welcome stamp, so any+# connected page reloads itself once on WS reconnect.+# 2. this script ALSO hard-refreshes the named webview tab and nudges the+# pup session, because a page mid-crash can miss a reconnect.+set -e+cd "$(dirname "$0")/.."+V=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')++touch src/main.rs # cargo fingerprints skip re-embedding include_str! edits otherwise+cargo build --release++for D in "$HOME/.local/bin" /usr/local/bin; do+ [ -d "$D" ] || continue+ if [ -w "$D" ]; then cp target/release/adom-shotlog "$D/.n" && mv "$D/.n" "$D/adom-shotlog"+ else sudo -n cp target/release/adom-shotlog "$D/.n" && sudo -n mv "$D/.n" "$D/adom-shotlog"; fi+ echo "installed $D/adom-shotlog"+done++# Kill by EXACT process name only. The hook self-heal starts the broker as+# `shotlog serve` (argv[0]=shotlog); -f patterns match your own shell. Both+# lessons cost us dearly - see dev-skills.+for P in $(pgrep -x adom-shotlog; pgrep -x shotlog); do kill "$P" 2>/dev/null || true; done+sleep 1++cd /home/adom/project+echo "=== deploy $V $(date '+%m-%d %H:%M:%S') (tools/deploy.sh) ===" >> prod-srv.log+SHOTLOG_AD_TARGET="${SHOTLOG_AD_TARGET:-AdomLapper}" nohup adom-shotlog serve >> prod-srv.log 2>&1 &+sleep 2++SERVED=$(curl -s http://127.0.0.1:8820/log/ | grep -o "APP_VERSION='[^']*'" || true)+case "$SERVED" in *"$V"*) echo "serving: $SERVED" ;; *)+ echo "ERROR: served stamp '$SERVED' does not contain $V - deploy NOT live"; exit 1 ;; esac++# Refresh the webview tab ONLY if it exists (open-or-refresh would create one,+# which must never happen as a deploy side effect on a pup-canvas human).+if adom-cli hydrogen workspace find-tab Shotlog 2>/dev/null | grep -q 'Shotlog'; then+ PROXY="${VSCODE_PROXY_URI/\{\{port\}\}/8820}"+ adom-cli hydrogen webview open-or-refresh --name Shotlog \+ --url "${PROXY}log/?surface=webview" >/dev/null 2>&1 \+ && echo "webview tab refreshed" || echo "WARN: webview refresh failed"+else+ echo "no webview tab open - nothing to refresh there"+fi+# Pup pages reload themselves via the boot-stamp on reconnect; nudge anyway.+adom-desktop --target "${SHOTLOG_AD_TARGET:-AdomLapper}" browser_reload '{"sessionId":"shotlog"}' >/dev/null 2>&1 \+ && echo "pup session nudged" || true+echo "OK: deployed $V and refreshed surfaces."