feat(capture): pipeline improvements — contact sheets, design styles, snapshot#1003
Conversation
… snapshot
Capture pipeline work that came out of the 11-round website-to-video
eval branch. The wins that actually moved quality were the artifacts
agents read (contact sheets, design-styles) and the snapshot tool
visual-verification fixes; the rest are smaller follow-ons.
**Contact sheets (`contactSheet.ts`, new)**
- Replaces the embedded one-image-per-asset listing with paginated
labeled grids (3-col screenshots / 4-col raster / 5-col SVG). Each
page contains 9–15 cells with filename labels baked in via SVG
text overlay (`escapeXml` covers `&<>"'`).
- `fit: "contain"` keeps every asset visible at its real aspect
ratio; the old `fit: "cover"` cropped to the first image's box.
- Returns `string[]` (page paths) — single-page captures get one
file, multi-page produce `contact-sheet-1.jpg`, `contact-sheet-2.jpg`,
etc.
- `createSvgContactSheet` scans both `assets/svgs/` (inline-extracted
SVGs) and `assets/` root (external SVGs from `<img src="*.svg">`)
and de-dupes by filename. Sites with all-external SVGs (huly.io)
now get coverage they previously didn't.
**Design styles extractor (`designStyleExtractor.ts`, new)**
- Walks the live DOM and reads computed styles to produce
`extracted/design-styles.json`: typography hierarchy (every text
role with exact font-size / weight / line-height / letter-spacing),
button variants (background / padding / radius / shadow), card /
container / nav styles, spacing scale with base unit, border-radius
scale, box-shadow values with usage counts.
- Primary data source for DESIGN.md authoring at Step 1. Replaces
the prior "guess from screenshots" workflow.
**Snapshot tool (`snapshot.ts`)**
- HyperShader pre-rendering used to swallow the entire snapshot
capture window (every frame after the first showed the loading
overlay or final-opacity-zero exit fades). Wait signal is now
`window.__hf.shaderTransitions[].ready` (set after both warm and
cold cache paths complete); local-time seek for sub-comps means
exit fades read at their own t=0..duration, not global time.
- Gemini vision per-frame analysis runs by default (`descriptions.md`
next to the contact sheet). `--describe "custom Q"` overrides the
prompt; `--describe false` opts out.
- 3-column contact sheet generation for snapshot frames so reviewers
see all beats at a glance.
**Screenshot capture (`screenshotCapture.ts`)**
- Replaces `querySelectorAll('*') + getComputedStyle` overlay scan
with a TreeWalker that early-exits on cheap rect checks before
reaching the expensive style read. Caps at 5000 elements per page.
- Cookie/consent dismissal selectors are scoped under cookie /
consent / gdpr ancestors so we don't click "Accept invitation" or
similar unrelated buttons.
**Agent prompt (`agentPromptGenerator.ts`)**
- Auto-discovers contact-sheet page count (matches base name plus
paginated `-NNN` variants only, with regex escaping on the base
name and numeric sort for 10+ pages).
- `inferColorRole`: classifies extracted hex colors as bg-dark /
bg-light / accent / surface / neutral via luminance + saturation,
so the agent prompt shows `#533AFD (accent)` instead of bare hex.
- `design-styles.json` row is gated on `existsSync` — the upstream
write is wrapped in try/catch and may skip on failure, so the
prompt only points to files actually on disk.
**Other CLI ergonomics**
- `cli.ts`: auto-load `.env` from CWD on startup so subcommands like
`snapshot` don't need explicit `export GEMINI_API_KEY=…`. Handles
`export FOO=bar`, quoted values, inline `# comments`.
- `commands/transcribe.ts`: default output dir is the input file's
directory, not CWD. Stops the "wrote transcript.json somewhere
unexpected" footgun.
- `assetDownloader.ts`: improved asset naming uses catalog context;
de-duplicates inline SVG filenames.
- `contentExtractor.ts`: captions SVGs via Gemini (code-as-text) and
integrates them into asset descriptions.
- `tokenExtractor.ts` + `types.ts`: SVG bounding box dimensions and
new DesignStyles schema added.
Required by the contact-sheet pagination code added on this PR (uses Sharp APIs that landed in 0.34.5). Originally bumped on #987 by mistake — moved here per Copilot review.
Fallow audit reportFound 37 findings. Dead code (1)
Duplication (5)
Health (31)
Generated by fallow. |
miguel-heygen
left a comment
There was a problem hiding this comment.
Content matches previously approved #988 (exact same diff stats +1388/-85). Re-approved.
jrusso1020
left a comment
There was a problem hiding this comment.
Re-approving the v2 re-cut.
Content equivalence verified at the patch-id level — both commits in #1003 produce byte-equivalent patches to #988:
#988: 711b9021..., 4596c410...
#1003: 711b9021..., 4596c410...
git patch-id is content-addressed and stable across rebases, so identical IDs prove the v2 re-cut introduces no functional drift from what was already approved on #988. The merge-base diff stat looks larger only because it includes main's drift between the two cuts (timeline UI overhaul, capture-pipeline-improvements landing, etc.) — none of which are in this PR.
Non-blocking — Fallow audit failure
Fallow audit reports 37 findings (1 unused-export, 5 minor duplication, 31 high-CRAP-score). This check wasn't in the required set when #988 was approved yesterday; the findings are the inherent complexity of the new capture/snapshot code introduced by this PR. Same findings would fire on #988 if Fallow ran against that head.
The one finding worth a follow-up check (not blocking): fallow/unused-export on packages/cli/src/capture/contactSheet.ts:27 flags createContactSheet as never imported by other modules. Verified — it's only used internally by createContactSheetPages (line 151 same file). Either the export is intentional for future external consumers, or it can be dropped. Cosmetic.
Per Ular's note in the original Slack message: merge via GitHub UI individually, not Graphite stack-merge — confirmed.
— Rames Jusso
Re-stacked version of #988 (which was silently lost during yesterday's Graphite stack-merge — that merge landed in
graphite-base/988instead ofmain).Content is identical to what was approved on #988, rebased onto current main.
What's in this PR
contactSheet.ts) with full XML entity escapingdesignStyleExtractor.ts)screenshotCapture.ts)agentPromptGenerator.tsregex metacharacter escape + numeric sort for 10+ page filenamessnapshot.ts--describenull handling (false → null, undefined → "true")^0.34.0→^0.34.5Original review history
graphite-base/988)This is layer 1 of a 4-PR stack. Subsequent PRs build on this one. Merge via GitHub UI individually — do not use Graphite stack-merge.