Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
11 KiB
Screenshot Automation for Landing Page
Overview
A Playwright-based script that seeds the Crumbs app with bretzel-themed demo data, captures 10 screenshots (7 desktop + 3 mobile) for the landing page, and commits them. Triggered manually via a GitHub Actions workflow.
Architecture
scripts/
screenshots.ts # Main orchestrator: seed + capture
fixtures/
bretzel-pixel.ts # Generates a pixel-art bretzel PNG programmatically
Single script (scripts/screenshots.ts) that:
- Spawns the preview server as a child process (
pnpm preview --port 4173), same approach asplaywright.config.tsuses for e2e tests. The app uses@sveltejs/adapter-node, sovite previewimport won't work — must spawn via CLI. - Deletes any existing screenshot DB to start fresh
- Seeds data via API calls (setup admin, login, create notes/users/keys)
- Navigates to each view and captures screenshots at specific viewports
- Saves PNGs directly to
website/assets/ - Kills the preview server in
finallyblock
Seed Data — Bretzel Universe
Users
| User | Role | Purpose | |
|---|---|---|---|
| Bretzel Admin | admin@bretzel.app | admin | Main user, owns all notes |
| Alice Chen | alice@bretzel.app | user | Collaborator for sharing screenshot |
Tags
Passed explicitly in the tags array of each POST /api/notes request body: baking, bretzel, dev, devops, homelab, personal, reading, weekend. Tags are NOT auto-parsed from hashtags in content — they must be provided as a separate field.
Notes
| # | Title | Color | Mode | Tags | Pinned | Special |
|---|---|---|---|---|---|---|
| 1 | Bretzel Ingredients | sand | checklist | #baking, #bretzel | yes | Checklist items (see below) |
| 2 | Self-Hosting Stack | fog | rich text | #homelab, #devops | no | Homelab list (see below) |
| 3 | Back to Retro Gaming | peach | rich text | #reading, #personal | no | Retro gaming note |
| 4 | The Best Software Is the Software You Own | coral | rich text | #personal, #bretzel | no | Self-hosting manifesto |
| 5 | Weekend Plans | mint | rich text | #personal, #weekend | no | Casual weekend plans |
| 6 | Ancient Flute Harmonica Discovered | chalk | rich text | #reading | no | Quirky news note |
| 7 | Baking code for the... | clay | checklist | #dev, #bretzel | no | Dev/baking crossover |
Note Content Details
Bretzel Ingredients (checklist):
- 500g bread flour
- 300ml warm water
- 10g salt
- 7g dry yeast
- 30g butter (softened)
- Baking soda for the bath
- Coarse salt for topping
Self-Hosting Stack (rich text — used for editor screenshot):
Current homelab setup:
**Crumbs** — notes (obviously)
**42** — holiday budget tracking
**Dokploy** — deployment platform
**Outline** — team wiki
**Navidrome** — music streaming
**Vaultwarden** — passwords
**Nextcloud** — files & calendar
**Plausible** — analytics
Everything below daily reverse proxy.
The Best Software Is the Software You Own (rich text):
Self-host everything. Trust no cloud.
The best software is the software you own.
Your data. Your server. Your rules.
If it can't run on your hardware, it's not really yours.
Back to Retro Gaming (rich text):
Games to replay this summer:
Chrono Trigger (SNES) — best RPG ever made
The Legend of Zelda: Link's Awakening (GB)
Castlevania: Symphony of the Night (PS1)
Advance Wars (GBA)
Final Fantasy Tactics (PS1)
The pixel art era was peak game design.
Weekend Plans (rich text):
Saturday:
- Farmer's market (get sourdough starter)
- Fix the Dokploy SSL cert renewal
- Bake a batch of bretzels
Sunday:
- Retro gaming marathon
- Update the self-hosting stack
- Plan next trip on 42
Ancient Flute Harmonica Discovered (rich text):
Archaeologists found a 40,000-year-old flute carved from vulture bone.
The oldest known musical instrument.
Imagine the first song ever played.
Baking code for the... (checklist):
- [x] Set up CI/CD pipeline
- [x] Write the CRDT sync logic
- [ ] Bake the docker image
- [ ] Deploy to the homelab
- [ ] Celebrate with a bretzel
Image Attachments
Pixel-art bretzel image (scripts/fixtures/bretzel-pixel.ts):
- Generated programmatically as a PNG using raw pixel data (no external dependencies)
- ~64x64 pixel grid, bretzel shape in warm browns (#C8860A gold, #8B6914 dark, #F0E6D3 parchment bg)
- Attached as featured image to "Bretzel Ingredients" note
- Also create a second smaller image attached to the same note (e.g. a pixel-art salt shaker or oven) for visual variety
Version History
Send 4-5 PATCH /api/notes/:id calls to "Self-Hosting Stack" with varying content. Version history entries are created as a side-effect of each PATCH — there is no explicit "create version" endpoint. Each version adds or changes a tool in the stack, so the diff is meaningful when viewed in the history panel.
API Keys
Create via API after seeding:
| Name | Purpose |
|---|---|
| Claude Code | For MCP integration screenshot |
| N8N | For automation screenshot |
Screenshot Capture Plan
Desktop (1280x800)
| # | Filename | View | Capture Steps |
|---|---|---|---|
| 1 | screenshot-grid.png | Home grid | Navigate to /, wait for notes to render, ensure sidebar is expanded with tags visible |
| 2 | screenshot-editor.png | Note editor | Click "Self-Hosting Stack" note, wait for editor to open with content and toolbar visible |
| 3 | screenshot-checklist.png | Checklist mode | Click "Bretzel Ingredients" note, wait for checklist items with checkboxes visible |
| 4 | screenshot-sharing.png | Share dialog | Open a note, click share button, wait for ShareDialog with "Alice Chen" listed as collaborator |
| 5 | screenshot-history.png | Version history | Open "Self-Hosting Stack", click history button, select a middle version in the list, wait for preview panel |
| 6 | screenshot-attachments.png | Attachments | Open "Bretzel Ingredients" note (has featured bretzel image), ensure image thumbnails and featured strip visible |
| 7 | screenshot-api.png | Settings > API/MCP | Navigate to /settings/mcp, wait for API keys list ("Claude Code", "N8N") and MCP config JSON block |
Mobile (390x844)
| # | Filename | View | Capture Steps |
|---|---|---|---|
| 1 | screenshot-mobile-grid.png | Home grid | Navigate to /, wait for notes in mobile layout (no sidebar, tags as horizontal pills) |
| 2 | screenshot-mobile-editor.png | Note editor | Open "Self-Hosting Stack" note, wait for mobile editor |
| 3 | screenshot-mobile-checklist.png | Checklist | Open "Bretzel Ingredients" note, wait for mobile checklist view |
Capture Settings
- Format: PNG
- Full page: false (viewport only)
- Animations disabled via
page.emulateMedia({ reducedMotion: 'reduce' })to avoid capturing mid-animation - Wait for
networkidleafter each navigation before capturing
GitHub Actions Workflow
File: .github/workflows/screenshots.yml
name: Screenshots
on:
workflow_dispatch:
permissions:
contents: write
jobs:
screenshots:
name: Capture Landing Page Screenshots
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: latest
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Install Playwright browsers and system deps
run: pnpm exec playwright install --with-deps chromium
- name: Build app
run: pnpm build
env:
DATABASE_URL: ./data/screenshots.db
- name: Capture screenshots
run: pnpm exec tsx scripts/screenshots.ts
env:
DATABASE_URL: ./data/screenshots.db
- name: Commit screenshots
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add website/assets/screenshot-*.png
git diff --cached --quiet && echo "No screenshot changes" && exit 0
git commit -m "chore: update landing page screenshots [skip ci]"
git push origin main
Script Structure
// scripts/screenshots.ts
import { chromium, type Page } from 'playwright';
import { spawn, type ChildProcess } from 'child_process';
import { generateBretzelPixelArt } from './fixtures/bretzel-pixel';
const BASE = 'http://localhost:4173';
const DESKTOP = { width: 1280, height: 800 };
const MOBILE = { width: 390, height: 844 };
const OUTPUT = 'website/assets';
async function main() {
// 1. Start preview server (spawn child process, wait for port 4173)
const server = await startPreviewServer();
// 2. Launch browser
const browser = await chromium.launch();
try {
// 3. Seed data (desktop context — API calls work at any viewport)
const seedContext = await browser.newContext({ viewport: DESKTOP });
const seedPage = await seedContext.newPage();
await seed(seedPage);
// 4. Desktop screenshots
await captureDesktop(seedPage);
await seedContext.close();
// 5. Mobile screenshots
const mobileContext = await browser.newContext({ viewport: MOBILE });
const mobilePage = await mobileContext.newPage();
await login(mobilePage);
await captureMobile(mobilePage);
await mobileContext.close();
} finally {
await browser.close();
server.close();
}
}
async function seed(page: Page) {
// Setup admin via POST /api/auth/setup
// Login via POST /api/auth/login (sets session cookie on page context)
// Create notes via POST /api/notes (with content, color, tags:string[], pinned, checklistMode)
// → capture note IDs from responses for later use
// Create collaborator user via POST /api/admin/users
// → capture alice.id (numeric) from response
// Share a note via POST /api/notes/:id/collaborators { userId: alice.id }
// Create version history: PATCH /api/notes/:id × 4-5 with varying content
// Upload pixel-art bretzel image via POST /api/notes/:id/attachments (multipart form)
// Create API keys via POST /api/settings/api-keys { name: "Claude Code" } and { name: "N8N" }
}
async function captureDesktop(page: Page) {
// D1: Grid view
// D2: Editor (Self-Hosting Stack)
// D3: Checklist (Bretzel Ingredients)
// D4: Sharing dialog
// D5: Version history
// D6: Attachments
// D7: API/MCP settings
}
async function captureMobile(page: Page) {
// M1: Grid view
// M2: Editor
// M3: Checklist
}
Landing Page Updates
After the screenshots are generated, website/index.html needs updating:
- Add new screenshot entries for: checklist, sharing, history, attachments, API
- Update alt text and labels
- Hero image stays as
screenshot-grid.png - Mobile section adds checklist screenshot, replaces sidebar screenshot
Dependencies
No new dependencies. Uses:
playwright(already in devDependencies for e2e tests)tsx(already in devDependencies for other scripts)- Node.js built-in
fs,path,Bufferfor pixel-art PNG generation - SvelteKit's preview server (spawned as child process via
pnpm preview)
Open Questions
None — all decisions resolved during brainstorming.