mirror of
https://github.com/paperclipai/paperclip-docs.git
synced 2026-11-07 14:21:18 -10:00
Recapture every screenshot flagged in SCREENSHOTS_PENDING.md (98 entries; 233 PNGs updated across light/dark) against parent `master`, and stamp registry.json with the new captured_sha. Mark SCREENSHOTS_PENDING.md as caught up. Also fix two isolation gaps that made `screenshots:refresh` unusable while a real local Paperclip is running: - Free embedded-Postgres port: onboard hard-codes port 54329 (no env/flag override; the port comes only from config.json), which collides with a real local instance. run.mjs now generates the config in external-DB mode (keeping the scratch data dir pristine) and rewrites database.embeddedPostgresPort to a scanned free port before boot. - Parent .env neutralized: the server loads PARENT_REPO/.env from its cwd via dotenv (override: false), which leaked DATABASE_URL (real external DB on 5432) and SERVE_UI=false into the "isolated" instance. instanceEnv() now pins DATABASE_URL="", DATABASE_MIGRATION_URL="" and SERVE_UI="true" so these win. seed-execution-workspace.mjs now throws instead of falling back to 54329, so the direct-DB seed can never connect to a real database. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
6.6 KiB
6.6 KiB
Screenshot pipeline
Automated capture of light + dark UI screenshots at 1440×900 @2x for use in the docs. Output PNGs land under docs/user-guides/screenshots/{light,dark}/ and are reviewed in a PR — they are never auto-pushed.
Prerequisites
- Parent repo present. The pipeline spins up a real Paperclip instance from source. Set
PAPERCLIP_REPOto its absolute path, or ensure it lives at the default location~/Documents/PaperclipAI/paperclip(the default resolved byconfig.mjs). See paperclip-localdev-runbook.md conceptually — the runner follows the samelocal_trusted/private/loopbackisolation pattern. - Playwright Chromium installed. Run once after
npm install:npx playwright install chromium pnpmavailable. The runner starts the parent's onboarding entrypoint viapnpm paperclipai onboard --yes --run.
One-command flow
npm run screenshots:refresh
What happens under the hood:
- Isolated instance.
run.mjsspawnspnpm paperclipai onboard --yes --runinside the parent repo with a throw-awayHOME/PAPERCLIP_HOMEunderos.tmpdir(),PORT=3197,PAPERCLIP_BIND=loopback,PAPERCLIP_DEPLOYMENT_MODE=local_trusted, andPAPERCLIP_DEPLOYMENT_EXPOSURE=private. The child process receives only a small allowlist of runtime environment variables, so real database URLs, provider keys, and GitHub tokens are not forwarded.- Parent
.envneutralized. The server also loads a.envfrom its working directory (the parent repo) via dotenv withoverride: false, which would otherwise re-inject the developer's real settings and defeat the allowlist.instanceEnv()therefore explicitly pins the keys that matter:DATABASE_URL=""(andDATABASE_MIGRATION_URL="") so the instance uses its own embedded Postgres instead of the parent's external database, andSERVE_UI="true"so the UI is served even if the parent's.envsetSERVE_UI=false. Because dotenv runs withoverride: false, these explicit values win over the parent.env. - Free embedded-Postgres port. Before that,
run.mjsruns a short config-generation pass (onboard --yespointed at an unreachable externalDATABASE_URL, so it writes a config without initializing the embedded cluster) and rewritesdatabase.embeddedPostgresPortto a guaranteed-free port discovered by scanning upward from 54329. This matters because onboard hard-codes embedded Postgres to 54329 with no env/flag override, and that's the same default a developer's real local Paperclip uses. Pinning a free port means a screenshot run started while your real instance is up can never collide with — or, worse, connect into and seed — your real database. (The generation pass must stay in external-DB mode: if it ever raninitdbon the scratch data dir, a half-finished cluster would make the real run skip init and fail withrole "paperclip" does not exist.) The direct-DB workspace seed (seed-execution-workspace.mjs) reads the live port back from the instance'spostmaster.pid/config.jsonand refuses to guess 54329.
- Parent
- Health check. Polls
http://127.0.0.1:3197/api/healthuntil 200 (up to 120 s). - Seed.
seed.mjscreates a minimal demo company ("Acme Robotics") plus a manager agent, a regular agent, a project, a goal, a routine, an issue, and a workspace. The company'sissuePrefixis auto-derived server-side (first three uppercase letters of the name →ACM) and cannot be set via REST, so the real prefix is captured and written to.seed-ids.jsonalongside the entity IDs (gitignored).routes.mjsreadscompanyPrefixfrom there, so routes always resolve to whatever the server assigned. Idempotent — re-running looks up the existing company first. - Sync registry.
sync-registry.mjsmerges route anddepends_onmetadata fromCAPTURE_TARGETSintoregistry.jsonwithout overwriting other fields. - Capture.
capture.mjsopens Chromium via@playwright/test, navigates each route in both light and dark themes (1440×900, deviceScaleFactor 2), and saves PNGs todocs/user-guides/screenshots/{light,dark}/<name>.png. The board theme is seeded via bothcolorSchemeemulation and alocalStorageinit script so the UI picks it up consistently. - Stamp. After capture,
registry.jsonentries are updated withcaptured_sha(parent git HEAD) andcaptured_against(branch/tag name). - Teardown. The server child process is killed and the scratch home is removed (
--keepskips the cleanup for debugging).
Overhaul mode
After a large UI/UX redesign — when every screen has changed — recapture everything in one pass:
npm run screenshots:refresh:all
This passes --all to capture.mjs, which ignores staleness and re-shoots every entry in CAPTURE_TARGETS.
Individual scripts
| Script | Command | What it does |
|---|---|---|
seed.mjs |
npm run screenshots:seed |
Seeds demo data against a running instance at BASE_URL. Writes .seed-ids.json. |
capture.mjs |
npm run screenshots:capture |
Captures screenshots against a running instance. Flags: --all, --only <substr>, --theme <light|dark|both>, --stale <file,...>, --base-url <url>, --keep. |
sync-registry.mjs |
npm run screenshots:sync-registry |
Syncs route + depends_on from routes.mjs CAPTURE_TARGETS into registry.json. Safe to run standalone. |
run.mjs |
npm run screenshots:refresh |
Full orchestrator: starts isolated instance → seeds → syncs registry → captures → tears down. Forwards --all, --only, --theme to capture.mjs. |
Route map
The full list of screenshots to capture, their routes, and their depends_on mappings is maintained in docs/user-guides/screenshots/SCREENSHOTS_TODO.md. routes.mjs is the machine-readable version of that same list — keep them in sync when adding new pages.
Rules
- Never run against live data. The pipeline always talks to the seeded demo instance on loopback port 3197.
PAPERCLIP_BIND=loopbackensures no external traffic. - Safe to run alongside your real local Paperclip. The screenshot instance pins its embedded Postgres to a free port (see "Isolated instance" above), so you do not need to stop a real instance first. It will not touch the real database on 54329.
- Output goes to a PR, never auto-pushed. After
screenshots:refresh, open a PR with the changed PNGs for human review. The pipeline does not push. - Always capture both themes. The renderer swaps variants at runtime — providing only one breaks dark- or light-mode users.