← Commit history
Cargo.lock+1−1
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-shotlog"-version = "0.8.0"+version = "0.8.1" dependencies = [  "axum",  "base64",
Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "adom-shotlog"-version = "0.8.0"+version = "0.8.1" edition = "2021" description = "Screenshot log viewer and CLI for Adom" license = "MIT"
SKILL.md+6−6
@@ -14,17 +14,17 @@ Binary: `/usr/local/bin/adom-shotlog` (the `shotlog` command is a back-compat al  ## This skill pack (main + sub-skills) -This is the **main** skill. Deeper topics are **sub-skills** shipped in the same-`adom/adom-shotlog` package (flat under `skills/<name>/`, installed to-`~/.claude/skills/`). Each declares its **tier** (User / Dev / Publish) for the-wiki Skills tab:+This is the **main** (user) skill. Deeper topics ship in the same+`adom/adom-shotlog` repo using the wiki's three-tier layout: `skills/` = USER+(ships + installs for everyone), `dev-skills/` = DEV (source-only), and+`publish-skills/` = PUBLISH (source-only).  | Skill | Tier | What it's for | |-------|------|---------------| | [adom-shotlog-surfacing](skills/adom-shotlog-surfacing/SKILL.md) | user | surfacing a shot *gently* per surface (webview activate / pup taskbar flash / phone badge), never stealing foreground; plus AD window-discovery + cue verbs | | [adom-shotlog-child-windows](skills/adom-shotlog-child-windows/SKILL.md) | user | capturing a parent window **plus its child windows** (owned dropdowns/dialogs) via `--meta`, and how they render inline |-| [adom-shotlog-dev](skills/adom-shotlog-dev/SKILL.md) | dev | build/deploy, the central-broker architecture, and the layout rules (ralph-test the skinny view, no outer-html scrollbars, pretty scrollbars) |-| [adom-shotlog-publishing](skills/adom-shotlog-publishing/SKILL.md) | publish | publishing to wiki.adom.inc (org ownership, OSS source, hero, the wiki traps) |+| [adom-shotlog-dev](dev-skills/adom-shotlog-dev/SKILL.md) | dev | build/deploy, the central-broker architecture, and the layout rules (ralph-test the skinny view, no outer-html scrollbars, pretty scrollbars) |+| [adom-shotlog-publishing](publish-skills/adom-shotlog-publishing/SKILL.md) | publish | publishing to wiki.adom.inc (org ownership, OSS source, hero, the wiki traps) |  ## ⚠ WHY THIS EXISTS: you can't show the human images in a text thread 
VERSION+1−1
@@ -1 +1 @@-0.8.0+0.8.1
bin/adom-shotlog
⋯ 1 unchanged line ⋯
dev-skills/adom-shotlog-dev/SKILL.mdadded+117
@@ -0,0 +1,117 @@+---+name: adom-shotlog-dev+description: "DEV skill for building, debugging, and laying out adom-shotlog (the Rust CLI + server + embedded HTML viewer). Read before editing the viewer, the server, or the CLI. Covers the ralph-test-yourself loop, the hard fluid-layout rules (no outer-html scrollbars, ever; pretty scrollbars always), the build/deploy cycle, and the central-broker architecture. Trigger words: shotlog dev, build shotlog, shotlog layout, ralph test shotlog, viewer css, skinny webview layout, horizontal scrollbar, fluid layout, shotlog architecture, shotlog server."+---++# adom-shotlog - DEV (build, layout, architecture)++The app is one Rust binary: `src/main.rs` (CLI), `src/server.rs` (HTTP + WS ++webview brokering), `src/store.rs` (channels + presence), `src/open.rs` (Hydrogen+tabs), and `src/html/index.html` (the viewer, embedded via `include_str!`).++## ALWAYS ralph-test yourself after a layout change++Never trust a viewer change by eye at desktop width. Load it at the **skinny ~20%+webview width (≈360px)** - that's where the layout actually has to hold - screenshot+it, measure it, fix, repeat. The skinny view is the one that matters.++Headless ralph check (a real 360px VIEWPORT, so the CSS media queries fire - unlike+CSS-narrowing an element, which does not):++```js+// ralph-check.cjs - node ralph-check.cjs <live-viewer-url>+const puppeteer = require('/home/adom/project/node_modules/puppeteer-core');+(async()=>{+  const b = await puppeteer.launch({executablePath: process.env.CHROME, headless:'new',+    args:['--no-sandbox','--disable-gpu','--disable-dev-shm-usage']});+  const p = await b.newPage();+  await p.setViewport({width:360, height:820, deviceScaleFactor:2});+  await p.goto(process.argv[2], {waitUntil:'networkidle2', timeout:25000}).catch(()=>{});+  await new Promise(r=>setTimeout(r,1800));+  console.log(JSON.stringify(await p.evaluate(()=>{+    const de=document.documentElement, vw=de.clientWidth;+    const off=[...document.querySelectorAll('*')].map(el=>{const r=el.getBoundingClientRect();+      return {t:el.tagName+'.'+((''+el.className).split(' ')[0]||''), right:Math.round(r.right)};})+      .filter(x=>x.right>vw+1).sort((a,b)=>b.right-a.right).slice(0,10);+    return {vw, htmlScrollW:de.scrollWidth, outerHScroll: de.scrollWidth>vw, off};+  })));+  await b.close();+})();+```+`CHROME=$(ls -d ~/.cache/puppeteer/chrome/linux-*/chrome-linux64/chrome|head -1) node ralph-check.cjs <url>`+**Pass = `outerHScroll:false` AND `off:[]`.** If anything is in `off`, that element+is wider than the panel - make it fluid; don't paper over it with a scrollbar.++## HARD RULE: ONE designated scroller - the outer html NEVER shows a scrollbar++The outermost html/body never scroll, either axis: `html, body { overflow: hidden }`.+The `.feed` is the one designated scroller; overlays (modal) scroll themselves.+Nested outer+inner scrollbars are an instant audit failure.++Hard-won: `overflow-x: hidden` alone is NOT enough. At fractional devicePixelRatio+(a pup window at DPR 1.5), `100dvh` computes fractionally, `scrollHeight` rounds+1px past `clientHeight`, and a phantom OUTER vertical scrollbar appears beside the+feed's real one - even though a DPR-1 headless audit says everything fits. Root+`overflow: hidden` makes the phantom impossible. Audit BOTH axes in the ralph+check: `outerVScroll` and `outerHScroll` must both be false.++A skinny webview must never show a horizontal scrollbar on the outermost html. If+it does, the layout isn't fluid. Rules that keep it fluid:++- `html, body { overflow: hidden; max-width: 100%; }` (the guarantee), plus+  `img { max-width: 100%; }`.+- **No fixed-width children.** The classic offender was the child-window row: it+  used fixed `168px` cells + `overflow-x:auto`, which overflowed the ~360px panel+  and made an ugly scrollbar. Fix: `flex-wrap: wrap` with cells `flex: 1 1 130px;+  min-width: 0; max-width: 220px` so they share the row and wrap, never scroll.+- **Wrap the header at narrow widths** (`@media (max-width: 520px)`): stack it into+  rows (brand / channel + count / actions) so nothing truncates or overflows.+- Long values wrap: `word-break: break-word` / `pre-wrap`, never a fixed min-width+  that forces overflow.++## HARD RULE: scrollbars are ALWAYS pretty++Every scrollable area gets styled - thin, rounded, themed, transparent track,+teal on hover. Never ship a default OS scrollbar.++```css+* { scrollbar-width: thin; scrollbar-color: rgba(139,148,158,.38) transparent; }+::-webkit-scrollbar { width: 10px; height: 10px; }+::-webkit-scrollbar-track { background: transparent; }+::-webkit-scrollbar-thumb { background: rgba(139,148,158,.32); border-radius: 999px;+  border: 2px solid transparent; background-clip: padding-box; }+::-webkit-scrollbar-thumb:hover { background: rgba(0,184,176,.55); background-clip: padding-box; }+```++## Build + deploy cycle++```bash+cargo build --release+# targeted restart of the ONE server (never a broad pkill):+kill "$(pgrep -f '[a]dom-shotlog serve' | head -1)"; sleep 1+sudo cp target/release/adom-shotlog /usr/local/bin/adom-shotlog+adom-shotlog serve &            # data dir: ./screenshots/shotlog+```+`SKILL.md` (and the viewer HTML) are embedded via `include_str!` - **you must+rebuild** after editing them. Bump `Cargo.toml` + `VERSION` together.++## Architecture: one server = the central broker (per container)++- The **same binary** is both the server and the CLI. `adom-shotlog serve` spawns+  ONE broker on **port 8820**; `inject`/`open`/`viewers`/`channels` are CLI verbs+  that HTTP-POST/GET to it.+- **Every AI thread in the container** runs `adom-shotlog inject`, which posts to+  that one local server. So the server is the central broker: it owns all channels,+  tracks every viewer's surface + on-screen state (presence over the WebSocket),+  and manages all the Hydrogen webviews (auto-open a slim 20% panel, bring the+  right channel's tab forward - by its unique `Shots: <channel>` name so parallel+  shotlogs are never cross-touched).+- **Webview activate = the BOUNCE.** Hydrogen has no "activate tab" verb;+  `webview open-or-refresh` does NOT change `activeTabIndex`, and `move-tab`+  rejects same-pane moves. The working recipe (in `bring_webview_forward`): find+  the tab by its `panelState` URL (`/log/<ch>/`), `move-tab` it to any OTHER pane,+  then `move-tab` back to `--target-index <orig>` with `--alert` - move-tab+  auto-activates in its target pane, so the tab surfaces to top with the orange+  update highlight, order preserved.+- It's **per-container**: threads in a different container get their own server.+- `shotlog channels` prints the live overview - how every thread is using it.
install.sh+4−5
@@ -14,11 +14,10 @@ echo "OK: adom-shotlog binary at ~/.local/bin/adom-shotlog (alias: shotlog)" # main SKILL.md + bash completions + alias "$HOME/.local/bin/adom-shotlog" install || true -# Bundled sub-skills — listed EXPLICITLY (the publish linter rejects a glob).+# Bundled USER sub-skills - listed EXPLICITLY (the publish linter rejects a glob).+# (dev-skills/ and publish-skills/ are source-only per the wiki three-tier layout.) install_skill() { mkdir -p "$DEST/$1"; cp "$HERE/$2" "$DEST/$1/SKILL.md"; echo "OK: skill $1"; }-install_skill adom-shotlog-surfacing     skills/adom-shotlog-surfacing/SKILL.md      # user-install_skill adom-shotlog-child-windows skills/adom-shotlog-child-windows/SKILL.md  # user-install_skill adom-shotlog-dev           skills/adom-shotlog-dev/SKILL.md            # dev-install_skill adom-shotlog-publishing    skills/adom-shotlog-publishing/SKILL.md     # publish+install_skill adom-shotlog-surfacing     skills/adom-shotlog-surfacing/SKILL.md+install_skill adom-shotlog-child-windows skills/adom-shotlog-child-windows/SKILL.md  echo "OK: adom-shotlog installed. Run 'adom-shotlog serve &' then 'adom-shotlog open -c my-project'."
package.json+4−2
@@ -1,6 +1,6 @@ {   "slug": "adom-shotlog",-  "version": "0.8.0",+  "version": "0.8.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.",@@ -25,7 +25,9 @@     "build.sh",     "VERSION",     "LICENSE",-    "tools/**"+    "tools/**",+    "dev-skills/**",+    "publish-skills/**"   ],   "install": {     "binary_name": "adom-shotlog"
page.json+4−2
@@ -1,6 +1,6 @@ {   "slug": "adom-shotlog",-  "version": "0.8.0",+  "version": "0.8.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.",@@ -25,7 +25,9 @@     "build.sh",     "VERSION",     "LICENSE",-    "tools/**"+    "tools/**",+    "dev-skills/**",+    "publish-skills/**"   ],   "install": {     "binary_name": "adom-shotlog"
publish-skills/adom-shotlog-publishing/SKILL.mdadded+71
@@ -0,0 +1,71 @@+---+name: adom-shotlog-publishing+description: "How to publish adom-shotlog (and apps like it) to wiki.adom.inc with adompkg - org ownership, OSS source, MIT license, hero, and the wiki traps that waste hours (visibility re-privatization, hero caching, binary overwrite, title vs slug). Use when: publishing/republishing adom-shotlog, cutting a release, fixing an unlisted/privatized page, a stale hero, or bundling sub-skills. Trigger words: publish shotlog, adompkg publish, republish, wiki release, hero not updating, page went private, visibility trap, bundle sub-skills, org owned, publish rules."+---++# Publishing adom-shotlog to wiki.adom.inc++The whole app - CLI binary, source, README, main skill, AND sub-skills - ships as+**one package**: `adom/adom-shotlog`. Do NOT create separate wiki pages/repos for+the skills; they live in THIS repo and ride along.++## The publish command++```bash+cd <staging-dir>          # holds package.json, bin/, src/, skills/, SKILL.md, README.md, LICENSE, screenshots/+adompkg publish --org adom --public --version <next> --yes+```++- **`--org adom`** → the page is **org-owned** (`/adom/adom-shotlog`), not+  user-owned. (The `author_name` sidebar shows the publishing employee and is+  **immutable via the API** - that's attribution, not ownership.)+- **`--public`** → anyone can install/view.+- **Version must bump every publish** - you cannot republish the same version.+  Keep `Cargo.toml`, `VERSION`, and `package.json` version in lockstep, and rebuild+  so the binary's `--version` matches what the page advertises.++## package.json essentials++- `slug: "adom-shotlog"` (install name) vs `title: "Adom Shotlog"` (display heading).+  They're different on purpose - slug stays kebab, title is human.+- `license: "MIT"` + ship a real **`LICENSE`** file (© Adom Industries) + set+  `license`/`authors` in `Cargo.toml`. Metadata alone isn't enough for OSS.+- `hero: { path: "screenshots/hero.png" }` - REQUIRED; publish refuses without it.+- `bundled: true`, a `tags` array, and `files` **must whitelist everything that+  should be browseable/installed**, including source + sub-skills:+  `["SKILL.md","skills/**","README.md","package.json","install.sh","uninstall.sh","bin/**","screenshots/**","src/**","Cargo.toml","Cargo.lock","build.sh","VERSION","LICENSE"]`+  (Only what's in `files` reaches the git repo / Files tab - omit `src/**` and the+  source is NOT browseable.)+- `install.sh` deploys the binary AND loops `skills/*/` to install every sub-skill.++## The traps (learned the hard way)++1. **Visibility re-privatization.** A `POST /files` or a metadata `PUT` on a public+   org page silently flips `visibility` back to private. **Fix: don't poke files+   piecemeal - do a bumped `adompkg publish --org adom --public`,** which+   re-publicizes. After publishing, **verify anonymously**:+   ```bash+   curl -s -o /dev/null -w "%{http_code}\n" https://wiki.adom.inc/adom/adom-shotlog+   curl -s -o /dev/null -w "%{http_code}\n" https://wiki.adom.inc/api/packages/adom/adom-shotlog/<ver>/manifest+   ```+   Both must be `200`.+2. **Hero caching by URL.** The hero blob URL (`/blob/app/adom-shotlog/screenshots/hero.png`)+   is cached by browser + CDN; overwriting the SAME path serves the stale image.+   You should NOT have to rename the file - the platform should cache-bust by+   content hash. Until it does, if a hero looks stale, confirm the served bytes+   (`curl … | md5sum`) match your new file; it's a cache, not a regression.+   (Logged for Colby - don't normalize the rename-the-file workaround.)+3. **Binary overwrite.** Overwriting a binary at the same repo path can no-op+   (`commit:null`). A version bump + republish sidesteps it.+4. **Overview vs Files lag.** The Overview tab renders a `readme` DB field set from+   the **tarball** README at publish time - a bare `git push` of README updates the+   Files tab but not the Overview. Republish to refresh Overview.++## After EVERY publish++- Verify anon `200` (above).+- **Show it in pup** (public page → pup): open/refresh the page, screenshot, look.+  A `200` proves nothing about what the human sees.+- Retiring an old slug (e.g. the pre-rename `adom/shotlog`)? `adompkg delete+  <owner>/<slug> --confirm` is a **soft, reversible** delete (`adompkg undelete`).+  It breaks `adompkg install <old>`, so mention the migration.
skills/adom-shotlog-child-windows/SKILL.md−1
@@ -1,6 +1,5 @@ --- name: adom-shotlog-child-windows-tier: user description: "Capture and display a parent window PLUS its CHILD WINDOWS in shotlog - the owned dropdowns, confirm bubbles, and native dialogs that Adom Desktop returns alongside a window shot, ingested via `inject --meta`. Use when: injecting a desktop_screenshot_window response, a captured window had a dropdown/dialog open, showing multi-window captures, or asking how child windows render. Trigger words: child windows, parent window, owned windows, owned popups, ownedPopupCount, window array, desktop_screenshot_window array, --meta, coordMap, dropdown capture, dialog capture, multi-window screenshot." --- 
skills/adom-shotlog-surfacing/SKILL.md−1
@@ -1,6 +1,5 @@ --- name: adom-shotlog-surfacing-tier: user description: "How shotlog should SURFACE a new shot to the human - gently, per surface, without stealing focus. Use when: an AI is injecting shots and deciding how to notify the human, a shot lands unseen, choosing between pup and webview, setting up the preferred slim right-side shotlog panel, or a viewer is backgrounded. Trigger words: shotlog surfacing, don't foreground, taskbar flash, blink the tab, skinny panel, shotlog notification, shotlog update, backgrounded viewer, auto-switch shotlog, shove in my face." ---