← Commit history
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=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[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."