Files
command-center/agent/managed-skills/screenshot-walkthrough-in-static-docs/SKILL.md
T

4.2 KiB
Raw Blame History

name, description
name description
screenshot-walkthrough-in-static-docs Embed a real-browser user-flow walkthrough (screenshots as data URIs) into a single-file zero-JS static docs page; includes the selector laws, cookie isolation, and secret-gate proof earned driving franksign's full demo.

name: screenshot-walkthrough-in-static-docs description: "Embed a real-browser user-flow walkthrough (screenshots as data URIs) into a single-file zero-JS static docs page."

Screenshot walkthroughs in a single-file static guide

Friction-earned 2026-10-21 on dank/franksign guide/index.html §13: 19 un-retouched captures of every user flow (signup → studio → template → send → sign → freeze → signed PDF → claim shelf → void → 410 door), embedded as data URIs, guide still single file, zero JS.

Recipe (end to end)

  1. Boot the prod-shaped stack on a non-collision port (PORT=3210; e2e seams squat :3100, demos :3000). Kill it before running any test gate.
  2. Walk flows as distinct identities sequentially — the managed browser shares ONE cookie jar across tabs. Between roles: tab.clearCookies() then tab.reload() on the app's origin, never on about:blank (domain-scoped clear on about:blank clears nothing and the previous identity's session silently survives — you'll screenshot the admin where you meant the signer).
  3. Fresh per-checkout demo state is fine; re-running flows with new identities leaves harmless history that actually helps later shots (roster with completed + pending + voided rows).
  4. Selectors: prefer role/textbox[name="Label *"] / role/button[name="Sign"] / testid/…. Forms render required markers INTO the label text (Email *) — label="Email" misses; text/… clicks may silently no-op where role/button[...] works. Snapshot once per screen (tab.ariaSnapshot(undefined,{compact:true,interactive:true})) instead of guessing.
  5. Screenshot to disk (path option silently ignored — see browser-screenshot-disk-and-cookies): tab.run(async ({page}, p) => { const r = await page.screenshot({encoding:"base64", type:"png"}); await Bun.write(p, Buffer.from(typeof r === "string" ? r : r.data, "base64")); }, { args: [path] }) — this variant returns a bare string, not {data}; handle both.
  6. Shrink before embedding: for f in *.png; do magick "$f" -resize 1000x "$f"; done → 17–83 kB each; 19 shots ≈ 800 kB raw, ~1.1 MB final guide.
  7. Non-browser payoff shots: rasterize artifacts the flow produces (e.g. signed PDF) with pdftoppm -png -r 90 + resize — the deterministic-output image is the best single screenshot of a PDF pipeline.
  8. Assembly: write the section HTML with __IMG:name__ placeholders via the write tool; a one-shot node script replaces tokens with data:image/png;base64,… and splices before <footer>. Never paste base64 through the edit tool.
  9. Update nav + add a pointer paragraph from the prose-journeys section to the walkthrough section.

Verify

Serve the dir (python3 -m http.server), open, assert via one evaluate: img count == captured, failed == 0 (complete && naturalWidth>0), and every href^="#" resolves to an id. Screenshot the section top + a mid scroll. Then kill the stack BEFORE the gate.

Secret gate vs data URIs

A base64 wall trips shape patterns (AKIA/ASIA[A-Z0-9]{16} etc.) via case-insensitive grep at law-of-large-numbers rates. Prove noise, don't just override: git diff --cached | grep -oEi '<patterns>' | sort -u — hits that are substrings of PNG streams (mixed-case AkIa…, random tails) are image bytes. Then use the hook's documented deliberate override (ALLOW_SECRETS=1 git commit) and record the proof in the commit body. Confirm no real credential is visible in any shot first (password fields render masked; note demo tokens in captions are dev-DB throwaways).

Voice/law notes

  • Captions must state what the demo fakes (no SMTP → "link travels by copy-paste") and that captures are un-retouched with real demo values.
  • The walkthrough routinely surfaces UI laws the prose hid (e.g. Void disabled={r.state !== "pending"} while the API accepts completed voids) — write those discoveries into a closing law box, and check the prose section against them.