Prompt file imported from blit386/blit386 (
.cursor/commands/demos-shots.md). Copyright stays with the author.
Demo shots
The /shots page in packages/demos is both the capture engine (dev server only) and the gallery (every build, and
live at next.demos.blit386.dev/shots). Captures are committed under packages/demos/public/shots/. The page source is
packages/demos/_shots/: shots.js is the entry, capture.js the engine, capture-mode.js the dev-only wiring, and
review-mode.js the review aids (diff, findings).
Usage
/demos-shots # capture every demo with the committed variants, then inspect
/demos-shots changed # only demos whose source differs from the merge base with origin/main, plus untracked files (every demo if src/shared or _shots/poses.js changed)
/demos-shots basics,sprites # a slug list
Steps
1. Capture
Start the dev server through the preview tool (pnpm run dev in packages/demos, port 5173). Then, from
packages/demos:
pnpm run capture:shots -- --all
--changed and --demos a,b select subsets; --variants display,uidebug,output,software,touch picks variants
(default: the committed pair). output is the drawing buffer from configure() (default 640x480), not device pixel
ratio, and stays out of COMMITTED_VARIANTS. The manifest keeps one display width/height; an output tile and its
lightbox size from naturalWidth/naturalHeight, so at 1x img.width equals img.naturalWidth. --changed selects
every demo whenever any file under src/shared/ (tests included) or _shots/poses.js differs from the merge base of
origin/main and HEAD; untracked files count as changed, and commits that only landed on main do not. The driver
opens /shots?auto... in Chrome, waits for the page to finish, and writes public/shots/<slug>/<pose>.<variant>.png
plus a merged manifest.json. After a clean run it deletes .png files in the captured demos' folders that the
manifest no longer lists (a renamed or removed pose leaves none behind); only those demos' folders are touched, so check
the deletions in git status. It refuses to write outside public/shots/ unless --out points somewhere else. Exit
code 1 means at least one tile failed; the failure list names the step (load, seek, step, capture).
2. Inspect
Order of operations for an agent:
- Read
public/shots/manifest.jsonand list entries with a non-emptylintarray or anerror. Lint rules:overlap,out-of-display,margin,row-gap(src/shared/ui-lint.js). - Read the flagged PNGs with the Read tool. The
uidebugvariant draws the group box (accent), the pad inset (dim), and every widget hit rect (warm), which is where spacing problems show. - When a single pixel matters, make a 4x nearest-neighbor crop before reading:
ffmpeg -i public/shots/<slug>/<pose>.display.png -vf "crop=120:60:0:140,scale=iw*4:ih*4:flags=neighbor" /tmp/crop.png - Open
/shotsin the built-in browser for the human view:gtoggles the focused stack,Shift+Gall stacks,Enteropens the lightbox,124+-zoom it,#shows the pixel grid,dandbshow the diff overlay and the baseline in the lightbox too,?lists every key (and while that list is open only?andEscdo anything).
3. Compare against main
Serve main's public/shots on another port (a second worktree's dev server, or
pnpm dlx serve --cors public/shots -l 5174) and open /shots?baseline=http://localhost:5174. The baseline must be an
http(s) URL or a same-origin path; anything else is refused. In production the CSP allows only same-origin baselines.
d toggles the diff overlay, b cycles current, baseline, diff.
4. Add a pose
Edit POSES_BY_SLUG in packages/demos/_shots/poses.js. A pose is { id, at } plus at most one of keys: ['Digit1'],
pointer: { x, y } (display pixels, hover), or touch: true. Ids are kebab-case and become file names. Run
pnpm run test (the pose table is validated against the registry) and re-capture that demo.
5. Add a lint rule
Add a pure function in src/shared/ui-lint.js, append its name to LINT_RULES, add cases to
src/shared/__tests__/ui-lint.test.mjs, re-capture, and confirm the badge appears.
6. Before a PR
Any change under src/shared/ is followed by pnpm run capture:shots -- --changed, and the resulting PNG diff is part
of the PR. check:demo-registry refuses a demo named shots.
