Skip to content

feat(capture): pipeline improvements — contact sheets, design styles, snapshot#1003

Merged
ukimsanov merged 2 commits into
mainfrom
feat/capture-pipeline-improvements-v2
May 21, 2026
Merged

feat(capture): pipeline improvements — contact sheets, design styles, snapshot#1003
ukimsanov merged 2 commits into
mainfrom
feat/capture-pipeline-improvements-v2

Conversation

@ukimsanov
Copy link
Copy Markdown
Collaborator

Re-stacked version of #988 (which was silently lost during yesterday's Graphite stack-merge — that merge landed in graphite-base/988 instead of main).

Content is identical to what was approved on #988, rebased onto current main.

What's in this PR

  • Paginated contact sheets (contactSheet.ts) with full XML entity escaping
  • Live DOM computed-style extractor (designStyleExtractor.ts)
  • TreeWalker-based overlay scanner with cookie-consent scoping (screenshotCapture.ts)
  • agentPromptGenerator.ts regex metacharacter escape + numeric sort for 10+ page filenames
  • snapshot.ts --describe null handling (false → null, undefined → "true")
  • sharp ^0.34.0^0.34.5

Original review history

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.

ukimsanov added 2 commits May 21, 2026 10:57
… 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.
@github-actions
Copy link
Copy Markdown

Fallow audit report

Found 37 findings.

Dead code (1)
Severity Rule Location Description
major fallow/unused-export packages/cli/src/capture/contactSheet.ts:27 Export 'createContactSheet' is never imported by other modules
Duplication (5)
Severity Rule Location Description
minor fallow/code-duplication packages/cli/src/capture/contentExtractor.ts:216 Code clone group 1 (10 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/capture/contentExtractor.ts:279 Code clone group 1 (10 lines, 2 instances)
minor fallow/code-duplication packages/cli/src/commands/remove-background.ts:178 Code clone group 2 (7 lines, 3 instances)
minor fallow/code-duplication packages/cli/src/commands/transcribe.ts:173 Code clone group 2 (7 lines, 3 instances)
minor fallow/code-duplication packages/cli/src/commands/tts.ts:181 Code clone group 2 (7 lines, 3 instances)
Health (31)
Severity Rule Location Description
critical fallow/high-crap-score packages/cli/src/capture/agentPromptGenerator.ts:23 'inferColorRole' has CRAP score 156.0 (threshold: 30.0, cyclomatic 12)
critical fallow/high-crap-score packages/cli/src/capture/agentPromptGenerator.ts:59 'buildPrompt' has CRAP score 272.0 (threshold: 30.0, cyclomatic 16)
critical fallow/high-crap-score packages/cli/src/capture/assetDownloader.ts:13 'downloadAssets' has CRAP score 1560.0 (threshold: 30.0, cyclomatic 39)
minor fallow/high-crap-score packages/cli/src/capture/assetDownloader.ts:85 'hasGoodContext' has CRAP score 42.0 (threshold: 30.0, cyclomatic 6)
major fallow/high-crap-score packages/cli/src/capture/assetDownloader.ts:122 'results' has CRAP score 56.0 (threshold: 30.0, cyclomatic 7)
critical fallow/high-crap-score packages/cli/src/capture/assetDownloader.ts:190 'downloadAndRewriteFonts' has CRAP score 132.0 (threshold: 30.0, cyclomatic 11)
minor fallow/high-crap-score packages/cli/src/capture/assetDownloader.ts:210 'getFamilyForUrl' has CRAP score 30.0 (threshold: 30.0, cyclomatic 5)
critical fallow/high-crap-score packages/cli/src/capture/assetDownloader.ts:258 'isPrivateUrl' has CRAP score 420.0 (threshold: 30.0, cyclomatic 20)
major fallow/high-crap-score packages/cli/src/capture/assetDownloader.ts:282 'fetchBuffer' has CRAP score 72.0 (threshold: 30.0, cyclomatic 8)
critical fallow/high-crap-score packages/cli/src/capture/assetDownloader.ts:315 'deriveAssetName' has CRAP score 870.0 (threshold: 30.0, cyclomatic 29)
critical fallow/high-crap-score packages/cli/src/capture/contactSheet.ts:27 'createContactSheet' has CRAP score 110.0 (threshold: 30.0, cyclomatic 10)
major fallow/high-crap-score packages/cli/src/capture/contactSheet.ts:125 'createContactSheetPages' has CRAP score 90.0 (threshold: 30.0, cyclomatic 9)
critical fallow/high-crap-score packages/cli/src/capture/contactSheet.ts:266 'createSvgContactSheet' has CRAP score 132.0 (threshold: 30.0, cyclomatic 11)
critical fallow/high-crap-score packages/cli/src/capture/contentExtractor.ts:23 'detectLibraries' has CRAP score 156.0 (threshold: 30.0, cyclomatic 12)
critical fallow/high-crap-score packages/cli/src/capture/contentExtractor.ts:163 'captionImagesWithGemini' has CRAP score 462.0 (threshold: 30.0, cyclomatic 21)
minor fallow/high-crap-score packages/cli/src/capture/contentExtractor.ts:193 'results' has CRAP score 30.0 (threshold: 30.0, cyclomatic 5)
critical fallow/high-crap-score packages/cli/src/capture/contentExtractor.ts:309 'generateAssetDescriptions' has CRAP score 1332.0 (threshold: 30.0, cyclomatic 36)
critical fallow/high-crap-score packages/cli/src/capture/index.ts:44 'captureWebsite' has CRAP score 1892.0 (threshold: 30.0, cyclomatic 43)
critical fallow/high-crap-score packages/cli/src/capture/index.ts:144 '<arrow>' has CRAP score 210.0 (threshold: 30.0, cyclomatic 14)
minor fallow/high-crap-score packages/cli/src/capture/index.ts:393 '<arrow>' has CRAP score 42.0 (threshold: 30.0, cyclomatic 6)
major fallow/high-crap-score packages/cli/src/capture/screenshotCapture.ts:24 'captureScrollScreenshots' has CRAP score 56.0 (threshold: 30.0, cyclomatic 7)
critical fallow/high-crap-score packages/cli/src/capture/screenshotCapture.ts:36 '<arrow>' has CRAP score 272.0 (threshold: 30.0, cyclomatic 16)
minor fallow/high-crap-score packages/cli/src/commands/snapshot.ts:23 'extractVideoFrameToBuffer' has CRAP score 30.0 (threshold: 30.0, cyclomatic 5)
critical fallow/high-crap-score packages/cli/src/commands/snapshot.ts:91 'captureSnapshots' has CRAP score 870.0 (threshold: 30.0, cyclomatic 29)
critical fallow/high-crap-score packages/cli/src/commands/snapshot.ts:225 'duration' has CRAP score 132.0 (threshold: 30.0, cyclomatic 11)
critical fallow/high-crap-score packages/cli/src/commands/snapshot.ts:313 '<arrow>' has CRAP score 110.0 (threshold: 30.0, cyclomatic 10)
critical fallow/high-crap-score packages/cli/src/commands/snapshot.ts:362 '<arrow>' has CRAP score 462.0 (threshold: 30.0, cyclomatic 21)
critical fallow/high-crap-score packages/cli/src/commands/snapshot.ts:493 'run' has CRAP score 756.0 (threshold: 30.0, cyclomatic 27)
minor fallow/high-crap-score packages/cli/src/commands/snapshot.ts:595 'results' has CRAP score 30.0 (threshold: 30.0, cyclomatic 5)
minor fallow/high-crap-score packages/cli/src/commands/transcribe.ts:52 'run' has CRAP score 42.0 (threshold: 30.0, cyclomatic 6)
critical fallow/high-crap-score packages/cli/src/commands/transcribe.ts:113 'transcribeAudio' has CRAP score 156.0 (threshold: 30.0, cyclomatic 12)

Generated by fallow.

Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Content matches previously approved #988 (exact same diff stats +1388/-85). Re-approved.

Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@ukimsanov ukimsanov merged commit eb3c9d7 into main May 21, 2026
33 of 34 checks passed
@ukimsanov ukimsanov deleted the feat/capture-pipeline-improvements-v2 branch May 21, 2026 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants