Skip to content

Regenerate the site figures from a headless rig - #477

Merged
jonassaa merged 7 commits into
mainfrom
feat/shot-rig
Sep 17, 2026
Merged

jonassaa merged 7 commits into
mainfrom
feat/shot-rig

Conversation

@jonassaa

Copy link
Copy Markdown
Owner

The three site figures were 1x masters from 2026-08-18, so both halves of "blurry and old" were real — and each turned out to have a separate, invisible cause.

Blurry

Two independent breaks, either of which was enough on its own:

  1. The masters were 1x (1600x1112). screenshots.mjs only emits a @2x variant when the master is at least 2080px wide, so it correctly refused — every Retina visitor got the 1040px file painted into 2080 device pixels.
  2. Screenshot.astro's 2x probe could never have fired. It resolved ../../public/screenshots/ through import.meta.url, which at build time points at the built chunk, so it looked in dist/public/screenshots/ and has2x was always false. No figure has ever shipped a srcset. The bug was invisible because it landed beside a capture path that never produced a 2x master either — there was nothing for the probe to find, so nothing looked wrong.

Old

112 src/ commits had landed since those captures. Most consequentially a73f5ef (#425) replaced the hand-drawn icon set with lucide, so every icon in all three figures belonged to a set the app no longer ships. Also visible: babc0f7 (#426) runs the activity bar to the window's bottom edge, 0530187 (#443) gives a tag pill a tag icon, a666295 (#444) stops a narrow pane eating the subject column.

Why a rig rather than a re-capture

pnpm capture needed a human on a Retina display to size a window and click it, which is why the figures went thirty days without a refresh. It also could not have worked: it sized the window to 1600x1112pt and then checked the resulting PNG — window plus drop shadow — against that same 1.4388 ratio, which its own aspect gate would reject.

So pnpm shoot renders the real src/ components in headless Chrome at device scale factor 2 and composites the two things macOS actually contributes. The app sets titleBarStyle: "Overlay", so it draws its entire titlebar in HTML — only the drop shadow and three traffic lights are native.

  • Mock surface is one function. All 167 wrappers in lib/tauri.ts funnel through one private invoke, so a scene is a cmd -> fixture map. The fake throws on an unanswered command, so pnpm shoot <scene> --report renders the worklist instead of leaving you to guess; a scene is done at MISSING (0).
  • Fixtures are typed against src/lib/types.ts, so a backend shape change breaks tsc rather than quietly rendering a product that no longer exists — which is how the hand-built AppShowcase replica died. It already caught a missing required embedded field while I was writing them.
  • Geometry is 2x the shipped master exactly (2924x1950 window in a 3200x2224 canvas, margins 138/138/94/180), verified by alpha bounding box before any content was rendered. So screenshots.mjs and Screenshot.astro needed no change.
  • Deterministic: frozen clock, pinned Text size / Spacing presets, seeded oids. Two consecutive runs are byte-identical (measured).
  • Composition preserved: same refs, same four fictional authors, same selected commit, at the same 120% app zoom the originals were shot at — measured from the old master (the Welcome card is 634 CSS px there, 526 at 100%), and the new figures match it to within 0.5%. That zoom is a legibility decision worth keeping: the site renders a 1462px window into a 1040px column.

Not run in CI. Masters and encoded WebP stay committed, so astro build needs no image pipeline and the deploy installs no Chrome — the arrangement pnpm og already uses.

Also fixed along the way

screenshots.mjs and capture.mjs had each copied a sharp loader that looked only for lib/index.js. sharp 0.35 moved its entry to dist/index.mjs, so pnpm screenshots failed with "No sharp found" on any machine resolving 0.35 — which reads like a missing install rather than a moved file. Extracted to one scripts/sharp.mjs that tries both.

scripts/capture.mjs is removed: leaving a broken manual path beside a working automated one invites someone to use it.

Verification

  • pnpm test — 405 files, 4157 tests, green (re-run after the last edit)
  • pnpm tsc --noEmit and tsc -p site/scripts/shoot/tsconfig.json --noEmit — clean
  • pnpm build in site/ — green, and the built HTML now carries
    srcset="/screenshots/history-dark.webp 1040w, /screenshots/history-dark@2x.webp 2080w" on all three figures, which is the end-to-end proof a Retina visitor is served the 2x asset
  • pnpm screenshots emits six files with no "is 1x" warning
  • Two pnpm shoot runs produce byte-identical masters

Spec: docs/superpowers/specs/2026-09-17-screenshot-rig-design.md
Plan: docs/superpowers/plans/2026-09-17-screenshot-rig.md

🤖 Generated with Claude Code

jonassaa and others added 7 commits September 17, 2026 15:03
Why: the three site figures are 1x masters from 2026-08-18, so every Retina
visitor gets an upscale of 1x text, and 112 src/ commits have landed since --
including the lucide icon swap, so every icon in them is retired. The manual
capture path cannot fix it: it needs a human clicking the window, and its
resize target disagrees with its own aspect gate by construction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Renders the real src/ components in headless Chrome at device scale factor 2
with the Tauri surface aliased to one shim, then composites the drop shadow
and traffic lights -- the only pixels macOS contributes, since the app sets
titleBarStyle Overlay and draws its own titlebar.

Why: the shipped figures are 1x masters from 2026-08-18, so Retina visitors
get an upscale of 1x text, and the manual path needed a human clicking a
window. Geometry is 2x the shipped master exactly (2924x1950 window in a
3200x2224 canvas), so screenshots.mjs and Screenshot.astro need no change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Transcribes the approved 2026-08-18 composition as typed fixtures: the same
seven refs, four fictional authors, selected commit and +2 -7 diff. Parents
are resolved into a real DAG so the graph draws its lanes and the diff header
reads e3677fd -> 335d8fe rather than (root), and short oids expand through a
seeded PRNG so the detail pane shows a plausible sha instead of zero padding.

Why typed against src/lib/types.ts: a backend shape change then breaks tsc
instead of quietly rendering a figure of a product that no longer exists --
which is how the hand-built AppShowcase replica died.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Screenshot.astro probed for a <name>@2x.webp through import.meta.url, which at
build time points at the BUILT chunk -- so it looked in dist/public/screenshots
and has2x was always false. No figure has ever shipped a srcset.

The bug was invisible because it landed beside a capture path that never
produced a 2x master either: there was nothing for the probe to find, so
nothing looked wrong. Resolve from process.cwd() instead, which is site/ for
both astro dev and astro build.

Also extracts the sharp loader both site scripts had copied: each looked only
for lib/index.js, and sharp 0.35 moved its entry to dist/index.mjs, so
`pnpm screenshots` failed with "No sharp found" on any machine resolving 0.35.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the commit scene and the afterMount hook it needs: the app always launches
on History and the Unified/Split toggle is local React state, so this figure
clicks through the way a user does rather than being seeded into place. Driving
the real UI also keeps it honest -- a scene cannot show a state the app cannot
reach.

get_diff answers per path: a constant meant the figure captioned NOTES.md over
the engine table.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The shipped figures were 1x masters from 2026-08-18. 112 src/ commits had
landed since, so every icon in them belonged to the hand-drawn set that #425
replaced with lucide, the activity bar stopped short of the window edge (#426)
and a tag pill wore a branch icon (#443).

Now 3200x2224 masters with @2x variants, so a Retina visitor paints them 1:1
instead of upscaling 1x text. Composition, refs and authors are the approved
ones, at the same 120% app zoom the originals were shot at -- measured, the
Welcome card matches the old master to within 0.5%.

Retires scripts/capture.mjs: it needed a human clicking a window, and it could
not have worked anyway -- it sized the WINDOW to 1600x1112pt then checked the
resulting PNG, which is window plus shadow, against that same ratio.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jonassaa
jonassaa merged commit 343fb16 into main Sep 17, 2026
14 checks passed
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.

1 participant