Adds an end-to-end pipeline for generating the marketing reel via Playwright, including tight/full variants, drag ghost, integrations layout, animated stats, brand-color logos, fade-to-black scene cuts, and a handover CLAUDE.md for the reel pipeline. |
||
|---|---|---|
| .. | ||
| scenarios | ||
| seed | ||
| build-store-assets.ts | ||
| fixture.ts | ||
| helpers.ts | ||
| matrix.ts | ||
| README.md | ||
Automated screenshot pipeline
Reproducible app-store screenshots driven by Playwright + a single seed dataset.
Quick start
# Capture everything (all viewports × all scenarios that apply to that platform)
npm run screenshots
# Or split:
npm run screenshots:capture # full web matrix → .tmp/screenshots/_master/
npm run screenshots:capture:desktop # desktopMaster only
npm run screenshots:capture:mobile # iPhone/iPad/Android viewports only
npm run screenshots:capture:electron # Electron build → .tmp/screenshots/_master_electron/
npm run screenshots:electron # capture:electron + build (lands in dist/)
npm run screenshots:build # rebuild dist/ layout from existing masters
# One group while iterating (pattern matches `desktop dark|light|catppuccin` etc.)
npx playwright test --config e2e/playwright.store-screenshots.config.ts \
--project=desktopMaster --grep "desktop dark \(en\)"
Environment overrides
| Var | Effect |
|---|---|
SCREENSHOT_MODE=electron |
Switches the fixture to the Electron pipeline (set by screenshots:capture:electron). |
SCREENSHOT_BASE_DATE=2026-05-06T09:30:00 |
Pin the "today" anchor used by the seed builder. Default is a Wednesday well clear of midnight in CI timezones. |
SP_SCREENSHOT_BG_DARK_URL / SP_SCREENSHOT_BG_LIGHT_URL |
Override the default Unsplash backgrounds (e.g. point to a vendored asset for offline / privacy-sensitive runs). |
SP_SCREENSHOT_BG_DISABLE=1 |
Drop background images entirely. |
SP_SCREENSHOT_BG_OVERLAY_OPACITY=80 |
Drives the per-context "Darken/lighten background image for better contrast" slider (0–99). Default 80 for screenshots vs. 20 in the app. |
Master captures land in .tmp/screenshots/_master/<viewport>/<locale>/<theme>/<scenario>/<name>.png.
Per-store assets land in dist/screenshots/<store>/<locale>/NN-name.png (and the F-Droid fastlane/... layout).
Scenario lineup
| Slot | Platform | Theme | What it shows |
|---|---|---|---|
| mobile-01 | mobile | dark | Planner |
| mobile-02 | mobile | dark | Planner with calendar nav expanded |
| mobile-03 | mobile | dark | Eisenhower matrix board |
| mobile-04 | mobile | light | Planner expanded (light variant) |
| mobile-05 | mobile | dark | Schedule view |
| mobile-06 | mobile | dark | Today task list |
| desktop-01 | desktop | dark | Today + schedule day-panel open |
| desktop-02 | desktop | dark | Eisenhower matrix board |
| desktop-03 | desktop | dark | Schedule view |
| desktop-04 | desktop | light | Project (Work) + notes panel populated |
| desktop-05 | desktop | dark | Focus mode |
| desktop-06 | desktop | light | Schedule (light variant) |
| desktop-07 | desktop | catppuccin-mocha | Today list with custom theme |
Specs are platform-grouped: scenarios/desktop/all.spec.ts and scenarios/mobile/all.spec.ts each capture every dark+light slot in a single session, flipping DARK_MODE between groups via applyTheme() (Playwright addInitScript is append-only, so a later script wins on each reload). The catppuccin slot stays in its own spec because changing customTheme requires a different seed file. Each spec runs once per locale (en + de).
Files
| Path | Purpose |
|---|---|
matrix.ts |
Locales, themes, viewports, mobile/desktop classification, store rules |
seed/seed.template.json |
Curated dataset with date offsets and @@PLANNER_OFFSET_+N placeholders |
seed/build-seed.ts |
Materializes offsets to absolute dates, injects locale + customTheme |
fixture.ts |
Pins clock, applies dark-mode, imports seed via UI flow, exposes screenshotMaster (which reads live DARK_MODE so light/dark scenes land in the right directory) |
helpers.ts |
gotoAndSettle, openNotesPanel, openSchedulePanel, resetView, applyTheme |
scenarios/desktop/all.spec.ts |
6 desktop slots (dark + light, single session) |
scenarios/desktop/catppuccin.spec.ts |
desktop-07 (different seed → standalone) |
scenarios/mobile/all.spec.ts |
6 mobile slots (dark + light, single session) |
build-store-assets.ts |
Renames + copies masters into per-store directory layouts; JPEG re-encode for maxBytes-capped stores (Snap) |
../playwright.store-screenshots.config.ts |
Separate Playwright config; one project per viewport |
How it works
- Per-test seed file is materialized to
.tmp/screenshot-seeds/seed-<date>-<locale>[-<customTheme>].json. - Fixture boots the app with
page.clock.install({ time: SCREENSHOT_BASE_DATE }), setslocalStorage.DARK_MODE, pins browser context toen-US(soImportPage's English text matchers always work), then imports the seed viaBackupService.importCompleteBackup. Locale flows throughglobalConfig.localization.lng→applyLanguageFromState$effect. Custom themes flow throughglobalConfig.misc.customTheme. - Each scenario spec drives the app to a state and calls
screenshotMaster(scenario, name). The Playwright project name (e.g.desktopMaster) determines the viewport. - Post-processor copies masters into per-store layouts. No resizing — captures are already at native size for each store.
Adding a scenario
// scenarios/desktop/08-new-thing.spec.ts
import { test } from '../../fixture';
import { LOCALES } from '../../matrix';
import { gotoAndSettle, onlyOn } from '../../helpers';
for (const locale of LOCALES) {
test.describe(`@screenshot desktop-08-new-thing (${locale})`, () => {
test.use({ locale, theme: 'dark' });
test('new thing', async ({ seededPage, screenshotMaster }, testInfo) => {
onlyOn(testInfo, 'desktop');
await gotoAndSettle(seededPage, '/#/whatever');
await seededPage.locator('whatever-component').waitFor();
await screenshotMaster('desktop-08-new-thing', 'new-thing');
});
});
}
Per-store gotchas
- MS Store reserves bottom 1/4 for system-rendered captions → keep critical UI in top 3/4.
- Mac App Store rejects letterboxed 16:9 in 16:10 frames — capture native 2880×1800.
- Snap caps at 5 items, ≤2 MB each, single global gallery (no per-locale). Pipeline emits all desktops re-encoded as JPEG (mozjpeg, q90→q60 step-down) so each fits the cap; trim manually to 5 before Snap upload.
- Play / Apple explicitly forbid / discourage device frames.
- Apple requires only iPhone 6.9" (1290×2796) and iPad 13" (2064×2752); smaller sizes auto-derive.
- Flathub requires native window chrome and forbids overlays — sourced from the Electron capture pipeline (single global gallery; run on a Linux X11/Wayland host via
npm run screenshots:capture:electron).
Electron pipeline (Mac App Store, Flathub)
Web Chromium captures don't look "native" on macOS — wrong fonts, wrong scrollbars, no traffic-lights. Flathub explicitly requires native window chrome. So there's a parallel pipeline that runs the actual SP Electron build via Playwright's _electron API and captures via OS-level region tools (screencapture on macOS, grim/import on Linux).
# Capture only — masters land in .tmp/screenshots/_master_electron/.
npm run screenshots:capture:electron
# Capture + build — masters under .tmp/, deliverables under dist/screenshots/
# (macappstore/, flathub/). Mirrors `npm run screenshots` for the web pipeline.
npm run screenshots:electron
Same scenarios, same fixture file — store-screenshots/fixture.ts branches on the SCREENSHOT_MODE env var (the npm script sets it). Each desktop spec runs unchanged in either mode.
The OS-level capture grabs the full window rect including titlebar, shadow, and traffic-lights / GTK decoration. Bounds come from BrowserWindow.getBounds(); on macOS Retina, 1440×900 points capture as 2880×1800 px. On Linux X11/Wayland bounds == pixels.
Per-OS tooling (must be on PATH):
- macOS —
screencapture(built-in) - Linux X11 — ImageMagick (
apt install imagemagick, shipsimport) - Linux Wayland —
grim(apt install grim, wlroots-based compositors only)
The Mac App Store store rule has masterDir: 'electron' in STORE_RULES, so the post-processor pulls those captures into dist/screenshots/macappstore/. All other stores still come from the web pipeline.
Status
- ✅ Foundation: matrix, seed builder, fixture (web + electron modes), helpers, post-processor
- ✅ 13 scenarios (6 mobile + 7 desktop) covering planner, boards, schedule, focus, notes, custom theme
- ✅ Electron-mode pipeline with OS-level chrome capture (
screencapture/grim/import) - ✅ Mac App Store wired to source from
_master_electron/ - ✅ Flathub STORE_RULE (single-gallery, sourced from
_master_electron/) - ✅ Snap JPEG re-encode under 2 MB cap (mozjpeg, automatic per-file)
- ✅ Tooltip suppression + cursor parking so leftover Material tooltips don't bleed into captures
- ⏳ Smoke-test the Electron pipeline on a real Mac (or Linux X11 for plumbing)