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.28.4
7 files changed
+47−5
Cargo.lock+1−1@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-shotlog"-version = "0.28.3"+version = "0.28.4" dependencies = [ "axum", "base64",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-shotlog"-version = "0.28.3"+version = "0.28.4" edition = "2021" description = "Screenshot log viewer and CLI for Adom" license = "MIT"
VERSION+1−1@@ -1 +1 @@-0.28.3+0.28.4
bin/adom-shotlog⋯ 1 unchanged line ⋯
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-shotlog",- "version": "0.28.3",+ "version": "0.28.4", "type": "app", "title": "Shotlog - Screenshot Log", "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 Bridge child-window arrays via --meta.",
page.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-shotlog",- "version": "0.28.3",+ "version": "0.28.4", "type": "app", "title": "Shotlog - Screenshot Log", "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 Bridge child-window arrays via --meta.",
src/html/index.html+42@@ -257,6 +257,14 @@ body.compact .card-popups .ph { padding-top: 4px; } .card-refbar { display: flex; align-items: center; gap: 8px; padding: 3px 10px 2px; min-height: 16px; font: 600 10.5px/1.3 'JetBrains Mono', monospace; color: var(--text3); } .card-refbar .rb-ref { letter-spacing: .2px; }+/* copy button (Ray, PR #5): hover-revealed like ⛶, never on the thumbnail */+.card-refbar .rb-copy { margin-left: 4px; padding: 0 5px; border-radius: 5px; cursor: pointer;+ font: inherit; line-height: 1.5; color: var(--text3); background: transparent;+ border: 1px solid var(--border-muted); opacity: 0; transition: opacity .12s, color .12s, border-color .12s; }+.shot-card:hover .card-refbar .rb-copy, .card-refbar .rb-copy:focus-visible { opacity: 1; }+.card-refbar .rb-copy:hover { color: var(--accent-bright); border-color: rgba(0,184,176,.5); }+.card-refbar .rb-copy.done { opacity: 1; color: var(--accent-bright); border-color: rgba(0,184,176,.5); }+@media (hover: none) { .card-refbar .rb-copy { opacity: .8; } } .card-refbar .rb-tag { color: var(--accent-bright); background: rgba(0,184,176,.12); border: 1px solid rgba(0,184,176,.45); border-radius: 5px; padding: 0 6px; max-width: 70%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .shot-card:not(.has-refbar) .card-refbar { }@@ -966,6 +974,21 @@ body.compact .card-popups .ph { padding-top: 4px; } })(); // ── Helpers ──────────────────────────────────────────────────────────────+ // execCommand('copy') is deprecated but is the only thing that works in a+ // sandboxed frame with no clipboard permission, which is where this viewer+ // sometimes lives. Used only when navigator.clipboard is missing or rejects.+ // (Ray, PR #5)+ function legacyCopy(text, done) {+ try {+ const ta = document.createElement('textarea');+ ta.value = text; ta.setAttribute('readonly', '');+ ta.style.cssText = 'position:fixed;top:-1000px;opacity:0';+ document.body.appendChild(ta); ta.select();+ const ok = document.execCommand('copy');+ document.body.removeChild(ta);+ if (ok && done) done();+ } catch (e) { /* nothing else to try; leave the label alone */ }+ } 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@@ -1212,6 +1235,25 @@ body.compact .card-popups .ph { padding-top: 4px; } if (e.tag){ const tg=document.createElement('span'); tg.className='rb-tag'; tg.textContent=e.tag; tg.setAttribute('data-tooltip','Handle "'+e.tag+'" given by the AI that injected this shot (--tag). Say "'+e.tag+'" to mean exactly this shot.'); rb.appendChild(tg); }+ // Copy the WHOLE reference, "#158 hero-final", not just one half (Ray,+ // PR #5): the ref is globally unambiguous and the handle is what a human+ // actually says, so pasting both lets the AI resolve it either way and+ // survives a handle being reused later. Untagged shots copy just "#158".+ { const txt = '#' + e.id + (e.tag ? ' ' + e.tag : '');+ const cp = document.createElement('button'); cp.className = 'rb-copy';+ cp.textContent = 'copy'; cp.setAttribute('aria-label', 'Copy ' + txt);+ cp.setAttribute('data-tooltip', 'Copy "' + txt + '": paste it to your AI to mean exactly this shot.');+ cp.addEventListener('click', function (ev) {+ ev.stopPropagation(); // never let it open the shot+ const done = function () {+ cp.textContent = 'copied'; cp.classList.add('done');+ setTimeout(function () { cp.textContent = 'copy'; cp.classList.remove('done'); }, 1200);+ };+ if (navigator.clipboard && navigator.clipboard.writeText) {+ navigator.clipboard.writeText(txt).then(done, function () { legacyCopy(txt, done); });+ } else { legacyCopy(txt, done); }+ });+ rb.appendChild(cp); } c.appendChild(rb); } // info (hidden in compact) -> refbar -> imgWrap (appended below) // COMPACT mode's reveal chip: hover (or tap, on touch) the ⓘ on the image // to overlay the full info block; it hides again when the pointer leaves