← Commit history

Publish 0.12.1

John Lauer ·b6bd8446a0 ·3mo ago ·parent bada43f
7 files changed +46−22
Cargo.lock+1−1
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-shotlog"-version = "0.12.0"+version = "0.12.1" dependencies = [  "axum",  "base64",
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "adom-shotlog"-version = "0.12.0"+version = "0.12.1" edition = "2021" description = "Screenshot log viewer and CLI for Adom" license = "MIT"
VERSION+1−1
@@ -1 +1 @@-0.12.0+0.12.1
bin/adom-shotlog
⋯ 1 unchanged line ⋯
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-shotlog",-  "version": "0.12.0",+  "version": "0.12.1",   "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.12.0",+  "version": "0.12.1",   "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+41−17
@@ -52,6 +52,16 @@ img { max-width: 100%; }   z-index: 99999; pointer-events: none; opacity: 0; display: none; transition: opacity .18s; } #global-tooltip.visible { display: block; } #global-tooltip.shown { opacity: 1; }+/* Structured (rich) tooltip content — title / body / requirement / prompt chip. */+#global-tooltip .tt-title { font: 600 12.5px 'Familjen Grotesk', sans-serif; color: var(--text); }+#global-tooltip .tt-p { margin-top: 6px; color: var(--text2); }+#global-tooltip .tt-req { margin-top: 7px; color: var(--text3); font-size: 11px; }+#global-tooltip .tt-count { margin-top: 8px; color: var(--text3); font-family: 'JetBrains Mono', monospace; font-size: 10px; }+#global-tooltip .tt-prompt { margin-top: 9px; padding: 7px 9px; border-radius: 7px;+  background: rgba(0,184,176,.10); border: 1px solid rgba(0,184,176,.28);+  color: var(--accent-bright); font-family: 'JetBrains Mono', monospace; font-size: 11px; line-height: 1.45; }+#global-tooltip .tt-prompt .lbl { display: block; color: var(--text3); font-family: 'Satoshi', sans-serif;+  font-size: 9.5px; letter-spacing: .04em; text-transform: uppercase; margin-bottom: 3px; }  /* Header (adom-app-header, 44px) */ header {@@ -310,8 +320,11 @@ header {     const tip = $('global-tooltip'); let cx=0, cy=0, timer=null;     document.addEventListener('mousemove', e => { cx=e.clientX; cy=e.clientY; }, true);     function show(el) {-      const t = el.getAttribute('data-tooltip'); if (!t) return;-      tip.textContent = t; tip.classList.add('visible');+      // Rich (authored, trusted) HTML via el._ttHtml; else plain text. Dynamic+      // values interpolated into _ttHtml are escaped by the builders below.+      if (el._ttHtml) { tip.innerHTML = el._ttHtml; }+      else { const t = el.getAttribute('data-tooltip'); if (!t) return; tip.textContent = t; }+      tip.classList.add('visible');       const tw=tip.offsetWidth, th=tip.offsetHeight, vw=innerWidth, vh=innerHeight, O=14, M=6;       const c=[{x:cx+O,y:cy+O},{x:cx-tw-O,y:cy+O},{x:cx+O,y:cy-th-O},{x:cx-tw-O,y:cy-th-O}]         .find(p=>p.x>=M&&p.y>=M&&p.x+tw<=vw-M&&p.y+th<=vh-M)||{x:cx+O,y:cy+O};@@ -543,24 +556,27 @@ header {     for (let i=entries.length-1;i>=0;i--) feed.appendChild(card(entries[i])); // newest first     updateCount();   }-  function channelTooltip(countText){+  function channelTooltipHtml(countText){+    const cnt = countText ? '<div class="tt-count">'+esc(countText)+' captured</div>' : '';     // read-captures is the SPECIAL auto-capture channel — explain it in full,-    // because even the shots' origin is non-obvious. Multi-paragraph is fine;-    // the renderer clamps it on-screen.+    // because even the shots' origin is non-obvious.     if (channel === 'read-captures') {-      return 'read-captures — the automatic capture channel.\n\n'+-        'Whenever ANY AI thread in this container READS an image file — a screenshot, a render, a photo — to look at it, a hook instantly copies that exact image here. So this channel shows you everything your AIs have looked at, without them having to remember to send it.\n\n'+-        'That is why shots appear here on their own: they are a side effect of an AI opening an image to analyze it, not something it deliberately posts.\n\n'+-        'Powered by the shotlog read-hook (a Claude Code PostToolUse(Read) hook that runs `shotlog inject -c read-captures`).'+-        (countText ? '\n\n'+countText+' captured so far.' : '');+      return '<div class="tt-title">read-captures — automatic capture</div>'++        '<div class="tt-p">Whenever any AI thread here READS an image file (a screenshot, a render, a photo) to look at it, a hook instantly copies that exact image into this channel. You see everything your AIs looked at — they never have to remember to send it.</div>'++        '<div class="tt-p">So shots appear on their own: a side effect of an AI opening an image to analyze it, not a deliberate post. Powered by the shotlog read-hook.</div>'++        '<div class="tt-prompt"><span class="lbl">want a deliberate log? say to your AI</span>ralph test your app and show me all the screenshots in shotlog</div>'++        cnt;     }-    return 'Channel: '+channel+(countText?'  ·  '+countText:'')+'\nAI agents inject shots here; you watch them arrive live.';+    return '<div class="tt-title">Channel: '+esc(channel)+'</div>'++      '<div class="tt-p">AI agents inject shots into this channel; you watch them arrive live, newest first.</div>'++      '<div class="tt-prompt"><span class="lbl">say to your AI</span>show me your progress in shotlog as you go</div>'++      cnt;   }   function updateCount(){ const t = shotCount? shotCount+(shotCount===1?' shot':' shots'):'';     $('shotCount').textContent = t;     // Surface the count in the channel-name tooltip too, since .subject-meta is     // hidden in the skinny panel (canonical adom-app-header: meta -> subject tooltip).-    $('channelName').setAttribute('data-tooltip', channelTooltip(t)); }+    $('channelName')._ttHtml = channelTooltipHtml(t); }    // ── Load ───────────────────────────────────────────────────────────────   fetch('api/shots?channel='+encodeURIComponent(channel)).then(r=>r.json()).then(data=>{@@ -582,11 +598,19 @@ header {     btn.style.display='';     const render=()=>{ btn.innerHTML=ICON+'<span>'+label+'</span>'; };     render();-    // Tooltip: the surface term (pup/webview), what it does, and the plain-English-    // prompt the human can just say to their AI to do the same thing.-    btn.setAttribute('data-tooltip', toPup-      ? 'Show in pup — a full browser window. Real zoom, whole-screen images. Closes this slim webview tab.\nRequires Adom Desktop installed on your computer (pup is a default bridge that auto-installs with it).\nOr just tell your AI: "show me shotlog in pup"'-      : 'Show in webview — the slim panel docked in Hydrogen. Closes this pup tab.\nOr just tell your AI: "show me shotlog in webview"');+    // Structured tooltip: title, what it does, requirement (pup only), and the+    // plain-English prompt the human can say to an AI to do the same thing.+    // NOTE: also set a plain data-tooltip so the hover handler (which matches+    // `[data-tooltip]`) fires for this button; the renderer prefers _ttHtml.+    btn.setAttribute('data-tooltip', toPup ? 'Show in pup' : 'Show in webview');+    btn._ttHtml = toPup+      ? '<div class="tt-title">Show in pup</div>'++        '<div class="tt-p">A full browser window: real zoom, whole-screen images. Closes this slim webview tab.</div>'++        '<div class="tt-req">Requires Adom Desktop installed (pup is a default bridge that auto-installs with it).</div>'++        '<div class="tt-prompt"><span class="lbl">say to your AI</span>show me shotlog in pup</div>'+      : '<div class="tt-title">Show in webview</div>'++        '<div class="tt-p">The slim panel docked in Hydrogen. Closes this pup tab.</div>'++        '<div class="tt-prompt"><span class="lbl">say to your AI</span>show me shotlog in webview</div>';     const busy=(t)=>{ btn.disabled=true; btn.innerHTML='<span>'+t+'</span>'; };     btn.addEventListener('click', async ()=>{       busy('checking…');