4.2 KiB
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)
- 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. - Walk flows as distinct identities sequentially — the managed browser shares ONE cookie jar across tabs. Between roles:
tab.clearCookies()thentab.reload()on the app's origin, never onabout: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). - 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).
- 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 whererole/button[...]works. Snapshot once per screen (tab.ariaSnapshot(undefined,{compact:true,interactive:true})) instead of guessing. - 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. - 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. - 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. - Assembly: write the section HTML with
__IMG:name__placeholders via the write tool; a one-shot node script replaces tokens withdata:image/png;base64,…and splices before<footer>. Never paste base64 through the edit tool. - 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.