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.13.2
7 files changed
+104−13
Cargo.lock+1−1@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-shotlog"-version = "0.13.1"+version = "0.13.2" dependencies = [ "axum", "base64",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-shotlog"-version = "0.13.1"+version = "0.13.2" edition = "2021" description = "Screenshot log viewer and CLI for Adom" license = "MIT"
VERSION+1−1@@ -1 +1 @@-0.13.1+0.13.2
bin/adom-shotlog⋯ 1 unchanged line ⋯
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-shotlog",- "version": "0.13.1",+ "version": "0.13.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.13.1",+ "version": "0.13.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+99−8@@ -65,22 +65,22 @@ img { max-width: 100%; } /* Header (adom-app-header, 44px) */ header {- display: flex; align-items: center; gap: 10px; padding: 0 12px; height: 44px;+ display: flex; align-items: center; gap: 12px; padding: 0 16px; height: 44px; flex-shrink: 0; background: var(--surface); border-bottom: 1px solid var(--border-muted); position: relative; }-.brand { display: flex; align-items: center; gap: 7px; color: var(--accent);+.brand { display: flex; align-items: center; gap: 8px; color: var(--accent); font-family: 'Familjen Grotesk', sans-serif; font-size: 13px; font-weight: 600; cursor: default; flex-shrink: 0; outline: none; } .brand:hover .app-name { text-decoration: underline; text-underline-offset: 3px; }-.brand svg.logo { width: 15px; height: 15px; flex-shrink: 0; color: var(--text); } /* monochrome per brand */+.brand svg.logo { width: 16px; height: 16px; flex-shrink: 0; color: var(--text); } /* monochrome per brand */ .brand .sep { color: var(--text3); font-weight: 400; margin: 0 1px; } .brand .app-subtitle { color: var(--text2); font-weight: 500; font-size: 12px; } .vsep { width: 1px; height: 16px; background: var(--border); flex-shrink: 0; }-.subject { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; }-.subject-name { font-family: 'Familjen Grotesk', sans-serif; font-size: 13px; font-weight: 600;+.subject { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1; }+.subject-name { font-family: 'Familjen Grotesk', sans-serif; font-size: 14px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }-.subject-meta { color: var(--text3); font-family: 'JetBrains Mono', monospace; font-size: 10px;+.subject-meta { color: var(--text2); font-family: 'JetBrains Mono', monospace; font-size: 11px; white-space: nowrap; flex-shrink: 0; } /* Canonical adom-app-header: the header is ALWAYS ONE 44px row (multi-row is anti-pattern #1 in the adom-app-header spec). To fit a skinny ~360px panel we@@ -245,6 +245,38 @@ header { box-shadow: 0 8px 28px rgba(0,0,0,.5); } .toast.show { opacity: 1; } +/* In-app helper dialog (e.g. "pup needs Adom Desktop") - NEVER a native+ alert/confirm. Backdrop + card, dismiss via X / backdrop / Esc. */+.helper { position: fixed; inset: 0; background: rgba(0,0,0,.72); display: none;+ align-items: center; justify-content: center; z-index: 400; padding: 14px; }+.helper.open { display: flex; }+.helper-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px;+ padding: 16px 18px 15px; width: 100%; max-width: 440px; max-height: calc(100dvh - 28px);+ overflow-y: auto; box-shadow: 0 12px 40px rgba(0,0,0,.6); position: relative; }+.helper-card .h-close { position: absolute; top: 10px; right: 10px; width: 26px; height: 26px;+ display: flex; align-items: center; justify-content: center; border-radius: 6px;+ color: var(--text3); cursor: pointer; border: none; background: transparent; font-size: 15px; }+.helper-card .h-close:hover { background: var(--overlay); color: var(--text); }+.helper-card .h-title { font: 700 15px 'Familjen Grotesk', sans-serif; color: var(--text);+ margin-bottom: 8px; padding-right: 28px; }+.helper-card .h-body { font: 13px/1.55 'Satoshi', sans-serif; color: var(--text2); margin-bottom: 12px; }+.helper-card .h-lbl { font: 600 9.5px 'Satoshi', sans-serif; letter-spacing: .05em;+ text-transform: uppercase; color: var(--text3); margin: 0 0 5px; }+.helper-card .h-prompt { background: rgba(0,184,176,.08); border: 1px solid rgba(0,184,176,.3);+ border-radius: 8px; padding: 10px 12px; font-family: 'JetBrains Mono', monospace;+ font-size: 11.5px; line-height: 1.5; color: var(--accent-bright); user-select: all;+ word-break: break-word; }+.helper-card .h-actions { display: flex; gap: 8px; margin-top: 12px; align-items: center; flex-wrap: wrap; }+.helper-card .h-btn { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 13px;+ border-radius: 7px; font: 600 12px 'Familjen Grotesk', sans-serif; cursor: pointer;+ border: 1px solid rgba(0,184,176,.4); background: rgba(0,184,176,.12); color: var(--accent-bright); }+.helper-card .h-btn:hover { background: rgba(0,184,176,.2); }+.helper-card .h-btn.secondary { border-color: var(--border); background: var(--overlay); color: var(--text2); }+.helper-card .h-btn.secondary:hover { color: var(--text); }+.helper-card .h-link { color: var(--accent); font: 600 12px 'Familjen Grotesk', sans-serif;+ text-decoration: none; margin-left: auto; }+.helper-card .h-link:hover { text-decoration: underline; }+ /* Paste progress overlay */ .paste-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.7); display: none; align-items: center; justify-content: center; z-index: 150; }@@ -296,6 +328,20 @@ header { <div class="feed" id="feed"><div class="empty">loading…</div></div> +<div class="helper" id="pupHelp" role="dialog" aria-modal="true" aria-label="Pup setup help">+ <div class="helper-card">+ <button class="h-close" id="pupHelpClose" data-tooltip="Close">✕</button>+ <div class="h-title" id="pupHelpTitle">Pup needs Adom Desktop</div>+ <div class="h-body" id="pupHelpBody"></div>+ <div class="h-lbl">copy this prompt into any AI thread and it will fix it for you</div>+ <div class="h-prompt" id="pupHelpPrompt"></div>+ <div class="h-actions">+ <button class="h-btn" id="pupHelpCopy">⧉ Copy prompt</button>+ <button class="h-btn secondary" id="pupHelpDismiss">Close</button>+ <a class="h-link" href="https://wiki.adom.inc/adom/adom-desktop" target="_blank" rel="noopener">Adom Desktop wiki ↗</a>+ </div>+ </div>+</div> <div class="toast" id="toast"></div> <div class="modal" id="modal"><div class="modal-card" id="modalCard"> <div class="m-desc" id="mDesc"></div><div class="m-meta" id="mMeta"></div><img id="mImg" alt="">@@ -644,9 +690,9 @@ header { busy('checking pup…'); const sim=new URLSearchParams(location.search).get('simulate'); const chk = sim==='no-pup'- ? {available:false, hint:'(simulated) pup needs Adom Desktop. Install it on your computer from wiki.adom.inc/adom/adom-desktop - the pup bridge is a default bridge that auto-installs right after it.'}+ ? {available:false, reason:'no-adom-desktop', simulated:true} : await fetch('api/canvas-check').then(r=>r.json());- if(!chk.available){ showToast(chk.hint||'pup is not available on this machine yet.', 12000); restore(); return; }+ if(!chk.available){ showPupHelp(chk); restore(); return; } } const baseline=await surfaceCount(); if(baseline<0){ showToast('cannot reach the shotlog broker - switch aborted, nothing changed.', 7000); restore(); return; }@@ -696,6 +742,51 @@ header { }); })(); + // ── Pup helper dialog: reason-specific explanation + a COPYABLE AI prompt ──+ function showPupHelp(chk){+ const reason=chk.reason||'no-adom-desktop';+ const C={+ 'no-adom-desktop':{+ title:'Pup needs Adom Desktop',+ body:'Pup opens shotlog in a full browser window on YOUR computer, driven by Adom Desktop. Adom Desktop is not installed (or not reachable) yet - pup is a default bridge that installs automatically right after it.',+ prompt:'Install Adom Desktop on my computer so shotlog can open pup windows. Start from wiki.adom.inc/adom/adom-desktop, walk me through the install, make sure it connects to my container, verify the pup bridge with `adom-desktop browser_readiness`, then show my shotlog channel "'+channel+'" in pup.'},+ 'desktop-not-connected':{+ title:'Adom Desktop is not connected',+ body:'Adom Desktop is installed but not linked to this container right now. Launch it on your computer and it reconnects automatically.',+ prompt:'My Adom Desktop is installed but not connected to this container. Help me get it running and linked, verify with `adom-desktop browser_readiness`, then show my shotlog channel "'+channel+'" in pup.'},+ 'pup-not-ready':{+ title:'Pup bridge is still warming up',+ body:'Adom Desktop is connected, but the pup browser bridge is not ready yet. It auto-installs on first use - this usually resolves in under a minute.',+ prompt:'The pup bridge on my Adom Desktop is not ready. Run `adom-desktop browser_prewarm`, verify with `adom-desktop browser_readiness`, then show my shotlog channel "'+channel+'" in pup.'},+ 'multiple-desktops':{+ title:'More than one Adom Desktop is connected',+ body:'The broker cannot tell which computer should host the pup window. It needs a target.',+ prompt:'Multiple Adom Desktops are connected to my container. Figure out which one I am actually using, set SHOTLOG_AD_TARGET=<that name> on my shotlog broker, then show my shotlog channel "'+channel+'" in pup.'}+ };+ const c=C[reason]||C['no-adom-desktop'];+ $('pupHelpTitle').textContent=(chk.simulated?'(simulated) ':'')+c.title;+ $('pupHelpBody').textContent=c.body+(chk.hint&&!chk.simulated?' ('+chk.hint+')':'');+ $('pupHelpPrompt').textContent=c.prompt;+ $('pupHelp').classList.add('open');+ }+ (function wirePupHelp(){+ const dlg=$('pupHelp'); if(!dlg) return;+ const close=()=>dlg.classList.remove('open');+ $('pupHelpClose').addEventListener('click',close);+ $('pupHelpDismiss').addEventListener('click',close);+ dlg.addEventListener('click',e=>{ if(e.target===dlg) close(); });+ document.addEventListener('keydown',e=>{ if(e.key==='Escape'&&dlg.classList.contains('open')) close(); });+ $('pupHelpCopy').addEventListener('click',()=>{+ const t=$('pupHelpPrompt').textContent;+ const done=()=>{ $('pupHelpCopy').textContent='copied ✓'; setTimeout(()=>$('pupHelpCopy').innerHTML='⧉ Copy prompt',1400); };+ if(navigator.clipboard&&navigator.clipboard.writeText){ navigator.clipboard.writeText(t).then(done).catch(()=>{fallback();}); }+ else fallback();+ function fallback(){ const r=document.createRange(); r.selectNodeContents($('pupHelpPrompt'));+ const sel=getSelection(); sel.removeAllRanges(); sel.addRange(r);+ try{ document.execCommand('copy'); done(); }catch{ showToast('select the prompt text and copy manually',5000); } }+ });+ })();+ // ── WebSocket live updates ({type:'entry', entry}) ─────────────────────── let activeWs = null; // Gentle "you have unseen shots" cue: while this tab is backgrounded, count new