← Commit history

Publish 0.28.2

John Lauer ·0546276e16 ·1mo ago ·parent ce2802a
7 files changed +86−54
Cargo.lock+1−1
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-shotlog"-version = "0.28.1"+version = "0.28.2" dependencies = [  "axum",  "base64",
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "adom-shotlog"-version = "0.28.1"+version = "0.28.2" edition = "2021" description = "Screenshot log viewer and CLI for Adom" license = "MIT"
VERSION+1−1
@@ -1 +1 @@-0.28.1+0.28.2
bin/adom-shotlog
⋯ 1 unchanged line ⋯
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "adom-shotlog",-  "version": "0.28.1",+  "version": "0.28.2",   "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.1",+  "version": "0.28.2",   "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+81−49
@@ -93,24 +93,24 @@ header {    one row crushed the channel name to 2-3 chars once the action cluster grew.    Row 1: logo + full channel name + count. Row 2: all actions, right-aligned    (with room back for the canvas button's label). Never more than two rows. */-/* Breakpoint raised 520 -> 760 (John, 2026-09-02, "ugly overlapping"): a-   ~570px ah webview pane still ran the channel name + count under the action-   cluster on one row. Anything narrower than 760px gets the two-row header. */-@media (max-width: 760px) {-  header { gap: 4px 8px; padding: 5px 10px 6px; height: auto; min-height: 44px; flex-wrap: wrap; }+/* ONE ROW, always (John, 2026-09-02: "why are you wasting those top 2 lines").+   As the panel narrows we shed TEXT, never rows: the brand words go first,+   then the Pup button's label (icon only), and the channel name ellipsizes.+   Only under ~260px (narrower than any real column) may the actions wrap. */+@media (max-width: 560px) {+  header { gap: 8px; padding: 0 10px; }   .brand .app-name, .brand .sep, .brand .app-subtitle { display: none; } /* logo carries identity */   .vsep { display: none; }-  .subject { flex: 1; min-width: 0; }   /* the name gets the whole rest of row 1 */-  .actions { flex-basis: 100%; justify-content: flex-end; gap: 5px; }+  .subject-meta { display: none; }        /* the count lives in the tab strip's tooltip */ }-/* A 10% own column on a 1707px-wide workspace is ~170 CSS px: the action-   cluster cannot fit one row there, so below 300px it WRAPS (never overflows-   past the edge; John, 2026-09-02, "ugly overlapping"). */-@media (max-width: 300px) {-  header { padding: 4px 8px 5px; }-  .actions { flex-wrap: wrap; row-gap: 4px; gap: 4px; min-width: 0; }-  .hdr-btn { height: 24px; padding: 0 7px; }-  .hdr-btn.icon-only { width: 24px; }+@media (max-width: 420px) {+  #canvasBtn span { display: none; }      /* Pup / Webview button: icon only */+  #canvasBtn { padding: 0; width: 26px; justify-content: center; }+  .actions { gap: 4px; }+}+@media (max-width: 260px) {+  header { height: auto; min-height: 44px; padding: 4px 8px; flex-wrap: wrap; }+  .actions { flex-wrap: wrap; row-gap: 4px; } } /* Header action buttons + LED (adom-app-header). These were the raw-<button>    ugliness: clean pills, subtle until hover, icon+label capable. */@@ -499,7 +499,7 @@ body.compact .card-popups .ph { padding-top: 4px; } /* ===== Settings (adom-ui-design > settings-layout) ===== */ /* Backdrop law: dim 25%, NEVER blur (settings often change the app behind). */ .helper.settings-overlay { background: rgba(0,0,0,.25); backdrop-filter: none; align-items: flex-start; padding: 24px 12px; overflow: hidden; }-.settings-dialog { padding: 0; overflow: hidden; border-radius: 12px; width: min(760px, 100%); max-height: calc(100vh - 48px); display: flex; flex-direction: column; outline: none; }+.settings-dialog { padding: 0; overflow: hidden; border-radius: 12px; width: min(820px, 100%); max-width: none; max-height: calc(100vh - 48px); display: flex; flex-direction: column; outline: none; } .settings-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px 12px; border-bottom: 1px solid var(--border); flex-shrink: 0; } .settings-head .h-title { margin: 0; } .settings-head .h-close { position: static; }@@ -529,6 +529,29 @@ body.compact .card-popups .ph { padding-top: 4px; } .set-child-why { font: 11.5px 'Satoshi', sans-serif; color: var(--text3); padding-top: 8px; } .set-children[data-enabled="false"] { opacity: .55; } .set-children[data-enabled="false"] .toggle, .set-children[data-enabled="false"] .num-wrap { pointer-events: none; }+/* Webview layout: an explicit TWO-OPTION picker (John: "hard to tell you're+   giving me 2 choices"). Each option is a box with a radio header, its real+   screenshot, and ONLY the controls that apply to it. The unselected box+   stays visible but dimmed, its controls disabled (never hidden). */+.layout-pick { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; margin: 8px 0 4px; }+.layout-opt { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px 8px; background: var(--surface); transition: border-color .15s, box-shadow .15s; }+.layout-opt.selected { border-color: var(--accent); box-shadow: 0 0 0 1px rgba(0,184,176,.35) inset; }+.layout-opt:not(.selected) .setting-shot, .layout-opt:not(.selected) .opt-desc { opacity: .6; }+.opt-head { -webkit-appearance: none; appearance: none; width: 100%; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; background: none; border: none; padding: 0 0 8px; cursor: pointer; color: var(--text); text-align: left; }+.opt-head:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }+.opt-radio { width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--text3); flex-shrink: 0; align-self: center; position: relative; transition: border-color .15s; }+.layout-opt.selected .opt-radio { border-color: var(--accent); }+.layout-opt.selected .opt-radio::after { content: ''; position: absolute; inset: 3px; border-radius: 50%; background: var(--accent); }+.opt-head:hover .opt-radio { border-color: var(--accent); }+.opt-title { font: 700 14px 'Familjen Grotesk', sans-serif; color: var(--text); }+.layout-opt.selected .opt-title { color: #8fd3d4; }+.opt-sub { font: 12px 'Satoshi', sans-serif; color: var(--text2); }+.opt-desc { margin: 6px 0 2px; font: 12.5px/1.5 'Satoshi', sans-serif; color: var(--text2); }+.opt-controls { margin-top: 6px; border-top: 1px solid var(--border); }+.opt-controls .set-row.set-child { border-top: none; padding-top: 8px; }+.opt-controls[data-enabled="false"] { opacity: .5; }+.opt-controls[data-enabled="false"] .toggle, .opt-controls[data-enabled="false"] .num-wrap { pointer-events: none; }+.layout-opt .setting-shot { max-width: none; } /* screenshots of the thing the setting changes: real captures, both states */ .set-shots { display: flex; gap: 12px; flex-wrap: wrap; margin: 4px 0 6px; } .setting-shot { margin: 0; flex: 1 1 260px; max-width: 520px; }@@ -660,7 +683,6 @@ body.compact .card-popups .ph { padding-top: 4px; }   </div>   <div class="actions">     <button class="hdr-btn" id="canvasBtn" style="display:none"></button>-    <button class="hdr-btn icon-only" id="toTopBtn" data-tooltip="Scroll to newest (top)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg></button>     <button class="hdr-btn icon-only" id="filterBtn" data-tooltip="Filter shots: child-window arrays, capture endpoint, safe/full pairs, direct vs auto-captured."><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/></svg></button>     <button class="hdr-btn danger" id="clearBtn" data-tooltip="Delete ALL screenshots in EVERY channel and remove all the thread tabs (cannot be undone).&#10;Safe for running AIs: a channel auto-recreates the moment any thread injects to it again.">clear</button>     <span class="ws-led" id="wsLed" data-tooltip="WebSocket: grey = disconnected, green = live.&#10;While green the server knows you're watching and&#10;won't force-open a duplicate tab on new shots."></span>@@ -750,31 +772,42 @@ body.compact .card-popups .ph { padding-top: 4px; }     </section>     <section class="settings-card" id="cardLayout">       <h3 class="card-title">Webview layout <span class="card-status" id="stLayout"></span></h3>-      <p class="card-desc">Where the shotlog webview lives inside your Hydrogen workspace, and how wide it is. Two alternatives: share your existing webview pane as a tab (the default), or take an own narrow column at the right edge. The width settings below belong to one mode or the other and are dimmed when they do not apply.</p>-            <div class="set-row" id="rowOwnCol">-        <h4 class="set-name" title="webview_own_column"><span class="prefix">Webview layout: </span><span class="title">Own column (third column at the right edge)</span></h4>-        <p class="set-desc">Off: shotlog is a tab inside your existing webview pane, sharing it with your other webviews. On: when you already have a webview pane, shotlog opens as its own narrow third column at the right edge, the width below, so it is always visible while your webviews keep their pane and their width. The editor gives up the space; shotlog never resizes your webview pane in this mode.</p>-        <div class="set-shots">-          <figure class="setting-shot"><img id="shotOwnOn" src="own-column.png?v=3" alt="Own column on: your webview pane keeps its tabs and width, and shotlog is a separate narrow column at the far right" loading="lazy"><figcaption>On: shotlog is the narrow third column at the far right.</figcaption></figure>-          <figure class="setting-shot"><img id="shotOwnOff" src="own-column-off.png?v=3" alt="Own column off: shotlog is one more tab inside the webview pane, beside your other webviews" loading="lazy"><figcaption>Off: shotlog is a tab in your webview pane.</figcaption></figure>+      <p class="card-desc">Where the shotlog webview lives inside your Hydrogen workspace. Pick ONE of the two layouts below; each shows a real screenshot of what you get, and the settings that only apply to that layout sit inside its box.</p>+      <div class="layout-pick" role="radiogroup" aria-label="Webview layout">+        <div class="layout-opt" data-own="false" id="optShared">+          <button class="opt-head" role="radio" aria-checked="true" data-own="false"><span class="opt-radio"></span><span class="opt-title">Shared pane</span><span class="opt-sub">a tab inside your webview pane</span></button>+          <figure class="setting-shot"><img id="shotOwnOff" src="own-column-off.png?v=3" alt="Shared pane: shotlog is one more tab inside the webview pane, beside your other webviews" loading="lazy"><figcaption>Shotlog sits as a tab beside your other webviews.</figcaption></figure>+          <p class="opt-desc">Default. Shotlog joins your existing webview pane as one more tab, so it shares that pane's width with your other webviews.</p>+          <div class="opt-controls" id="sharedChildren" data-enabled="true">+            <div class="set-row set-child" id="rowWidth">+              <h4 class="set-name" title="webview_width"><span class="prefix">Shared pane: </span><span class="title">Pane width</span></h4>+              <p class="set-desc">The width shotlog uses when it first CREATES the shared pane, as a percent of your workspace (10-50). It only seeds a brand-new pane; it never resizes one you already have unless auto-size is on.</p>+              <div class="set-ctl"><span class="num-wrap" data-tooltip="Percent of the workspace width for the shared webview pane (10-50)."><button class="num-step" data-target="prefWidth" data-dir="-1" aria-label="decrease" tabindex="-1">−</button><input class="num-input" id="prefWidth" type="number" min="10" max="50" step="5"><span class="unit">%</span><button class="num-step" data-target="prefWidth" data-dir="1" aria-label="increase" tabindex="-1">+</button></span></div>+              <div class="set-foot"><span class="default-hint">Default: 20%</span><span class="ai-chip"><span class="lbl">or say to your AI</span>make the shotlog webview panel 25% wide</span></div>+            </div>+            <div class="set-row set-child" id="rowAutosize">+              <h4 class="set-name" title="webview_autosize"><span class="prefix">Shared pane: </span><span class="title">Auto-size the pane</span></h4>+              <p class="set-desc">On: every time shotlog opens, it snaps the shared pane back to the width above (rude if you use webviews a lot, hence off). Off: shotlog leaves the pane at whatever width you last dragged it to.</p>+              <div class="set-ctl"><button class="toggle" id="prefAutosize" role="switch" aria-checked="false" aria-label="Auto-size the pane" data-tooltip="On: snap the shared pane to your width on every open. Off (default): respect the width you set manually."><span class="toggle-track"><span class="toggle-thumb"></span></span></button></div>+              <div class="set-foot"><span class="default-hint">Default: Off</span><span class="ai-chip"><span class="lbl">or say to your AI</span>turn on shotlog webview auto-sizing</span></div>+            </div>+          </div>+        </div>+        <div class="layout-opt" data-own="true" id="optOwn">+          <button class="opt-head" role="radio" aria-checked="false" data-own="true"><span class="opt-radio"></span><span class="opt-title">Own column</span><span class="opt-sub">a third column at the right edge</span></button>+          <figure class="setting-shot"><img id="shotOwnOn" src="own-column.png?v=3" alt="Own column: your webview pane keeps its tabs and width, and shotlog is a separate narrow column at the far right" loading="lazy"><figcaption>Shotlog is its own narrow column at the far right.</figcaption></figure>+          <p class="opt-desc">Shotlog opens as its own narrow third column at the right edge, the width below, so it is always in view while your webviews keep their pane and their width. The editor gives up the space; shotlog never resizes your webview pane.</p>+          <div class="opt-controls" id="ownChildren" data-enabled="false">+            <div class="set-row set-child" id="rowOwnCol">+              <h4 class="set-name" title="own_column_width"><span class="prefix">Own column: </span><span class="title">Column width</span></h4>+              <p class="set-desc">Width of shotlog's own column as a percent of the screen (5-30).</p>+              <div class="set-ctl"><span class="num-wrap" data-tooltip="Width of shotlog's own column, percent of the screen (5-30)."><button class="num-step" data-target="prefOwnWidth" data-dir="-1" aria-label="decrease" tabindex="-1">−</button><input class="num-input" id="prefOwnWidth" type="number" min="5" max="30" step="5"><span class="unit">%</span><button class="num-step" data-target="prefOwnWidth" data-dir="1" aria-label="increase" tabindex="-1">+</button></span></div>+              <div class="set-foot"><span class="default-hint">Default: 10%</span><span class="ai-chip"><span class="lbl">or say to your AI</span>open shotlog as its own 10% column on the right</span></div>+            </div>+          </div>         </div>-        <div class="set-ctl"><button class="toggle" id="prefOwnCol" role="switch" aria-checked="false" aria-label="Own column" data-tooltip="On: shotlog gets its own narrow column at the right edge (your webview pane is untouched). Off: shotlog is a tab in your webview pane."><span class="toggle-track"><span class="toggle-thumb"></span></span></button><span class="num-wrap" data-tooltip="Width of shotlog's own column, percent of the screen (5-30)."><button class="num-step" data-target="prefOwnWidth" data-dir="-1" aria-label="decrease" tabindex="-1">−</button><input class="num-input" id="prefOwnWidth" type="number" min="5" max="30" step="5"><span class="unit">%</span><button class="num-step" data-target="prefOwnWidth" data-dir="1" aria-label="increase" tabindex="-1">+</button></span></div>-        <div class="set-foot"><span class="default-hint">Default: Off, 10% wide</span><span class="ai-chip"><span class="lbl">or say to your AI</span>open shotlog as its own 10% column on the right</span></div>-      </div>-      <div class="set-children" id="sharedChildren" title="Used when Own column is off: these size the SHARED webview pane.">-        <div class="set-child-why">Used when Own column is off (the shared pane).</div>      <div class="set-row set-child" id="rowAutosize">-        <h4 class="set-name" title="webview_autosize"><span class="prefix">Shared pane: </span><span class="title">Auto-size the webview pane</span></h4>-        <p class="set-desc">On: shotlog re-forces the shared pane to the width below every time it opens the webview (rude if you use webviews a lot, hence off). Off: shotlog leaves the pane at whatever width you last dragged it to; the width below only seeds a brand-new pane.</p>-        <div class="set-ctl"><button class="toggle" id="prefAutosize" role="switch" aria-checked="false" aria-label="Auto-size the webview pane" data-tooltip="On: snap the webview pane to your preferred width on every open. Off (default): respect the width you set manually."><span class="toggle-track"><span class="toggle-thumb"></span></span></button></div>-        <div class="set-foot"><span class="default-hint">Default: Off</span><span class="ai-chip"><span class="lbl">or say to your AI</span>turn on shotlog webview auto-sizing</span></div>-      </div>-      <div class="set-row set-child" id="rowWidth">-        <h4 class="set-name" title="webview_width"><span class="prefix">Shared pane: </span><span class="title">Webview pane width</span></h4>-        <p class="set-desc">The width shotlog uses when it first CREATES the shared webview pane, as a percent of your workspace (10-50). With auto-size off this only seeds a new pane and never resizes an existing one; with it on, every open snaps back to this.</p>-        <div class="set-ctl"><span class="num-wrap" data-tooltip="Percent of the workspace width for the shared webview pane (10-50). Seeds a new pane; only re-applied on every open when auto-size is on."><button class="num-step" data-target="prefWidth" data-dir="-1" aria-label="decrease" tabindex="-1">−</button><input class="num-input" id="prefWidth" type="number" min="10" max="50" step="5"><span class="unit">%</span><button class="num-step" data-target="prefWidth" data-dir="1" aria-label="increase" tabindex="-1">+</button></span></div>-        <div class="set-foot"><span class="default-hint">Default: 20%</span><span class="ai-chip"><span class="lbl">or say to your AI</span>make the shotlog webview panel 25% wide</span></div>-      </div>       </div>+      <div class="set-foot" style="padding-top:8px"><span class="default-hint">Default: Shared pane</span></div>     </section>     <section class="settings-card" id="cardStorage">       <h3 class="card-title">Storage <span class="card-status" id="stStorage"></span></h3>@@ -1717,11 +1750,11 @@ body.compact .card-popups .ph { padding-top: 4px; }       // shared-pane rows are CHILDREN of this switch: dimmed + inert when it is       // on, never hidden (settings-layout: disable, never hide).       const own = p.webview_own_column===true;-      $('prefOwnCol').setAttribute('aria-checked', String(own));       $('prefOwnWidth').value = p.own_column_width ?? 10;-      $('prefOwnWidth').closest('.num-wrap').classList.toggle('off', !own);-      $('sharedChildren').dataset.enabled = String(!own);-      $('sharedChildren').querySelectorAll('.toggle,.num-input,.num-step').forEach(el=>{ el.disabled = own; });+      document.querySelectorAll('.layout-opt').forEach(o=>{ const sel=(o.dataset.own==='true')===own; o.classList.toggle('selected', sel);+        o.querySelector('.opt-head').setAttribute('aria-checked', String(sel));+        const ctl=o.querySelector('.opt-controls'); ctl.dataset.enabled=String(sel);+        ctl.querySelectorAll('.toggle,.num-input,.num-step').forEach(el=>{ el.disabled=!sel; }); });       // Live status in every card title (recomputed truth, never a cached claim).       const st=(id,txt,ok)=>{ const el=$(id); if(!el) return; el.textContent=txt; el.classList.toggle('ok', !!ok); };       st('stHook', p.read_hook!==false ? 'active' : 'off', p.read_hook!==false);@@ -1764,10 +1797,10 @@ body.compact .card-popups .ph { padding-top: 4px; }       const now=$('prefAutosize').getAttribute('aria-checked')==='true';       patch({webview_autosize: !now}, !now?'auto-size ON: the webview column snaps to your width on every open.':'auto-size OFF: shotlog leaves the webview column at the width you set.');     });-    $('prefOwnCol').addEventListener('click',()=>{-      const now=$('prefOwnCol').getAttribute('aria-checked')==='true';-      patch({webview_own_column: !now}, !now?'own column ON: the next time shotlog opens with no Shotlog tab, it takes its own column at the right edge (your webview pane is untouched).':'own column OFF: shotlog joins your webview pane as a tab.');-    });+    document.querySelectorAll('.layout-opt .opt-head').forEach(h=>h.addEventListener('click',()=>{+      const own=h.dataset.own==='true';+      patch({webview_own_column: own}, own?'own column: the next time shotlog opens with no Shotlog tab, it takes its own column at the right edge (your webview pane is untouched).':'shared pane: shotlog joins your webview pane as a tab.');+    }));     $('prefCompact').addEventListener('click',()=>{       const now=$('prefCompact').getAttribute('aria-checked')==='true';       patch({compact: !now}, !now?'compact ON: image-first cards - hover the ⓘ on a thumbnail for its info.':'compact OFF: classic cards with the info text always visible.');@@ -1947,7 +1980,6 @@ body.compact .card-popups .ph { padding-top: 4px; }   connectWs();    // ── Wiring ───────────────────────────────────────────────────────────────-  $('toTopBtn').addEventListener('click',()=>{ feed.scrollTop=0; });   // Two-step arm instead of a browser-modal confirm(): first click arms the   // button ("sure? clears all") for 3s; a second click within that window does it.   (function initClear(){