Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ data/
test-results/
playwright-report/

# Demo recorder (dev-only, not part of the workspace)
scripts/record-demo/node_modules/
scripts/record-demo/out/
scripts/record-demo/.tmp/

# macOS
.DS_Store

Expand Down
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ CI (`.github/workflows/ci.yml`) and CHANGELOG.md's compare links must target `ma
`main`/`master` mismatch left CI structurally unable to fire on any PR. Check this whenever
adding or touching a workflow trigger or a branch-referencing doc link.

## Regenerating the README demo (`docs/assets/demo.mp4`/`demo.gif`)

`make demo` (or `scripts/record-demo/`'s own README) boots a disposable Postgres + the
dashboard + `examples/demo-site`, drives the real product loop with Playwright, and converts
the recording with ffmpeg. It runs the dashboard via `next start` against a production
build, not `next dev` - the dev-mode build indicator overlay (`nextjs-portal`) intercepts
Playwright's clicks and made the recorder flaky. `scripts/record-demo` is a standalone
package outside `pnpm-workspace.yaml`'s `packages/*` glob (dev-only, never a product
dependency).

## Maintaining this file

Keep this file for knowledge useful to almost every future agent session in this project.
Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build dev test lint tidy clean
.PHONY: build dev test lint tidy clean demo

build:
pnpm --filter @diffboard/core run build
Expand All @@ -23,3 +23,10 @@ tidy:

clean:
rm -rf packages/*/dist packages/server/.next node_modules packages/*/node_modules

# Boots the real stack, records a genuine walkthrough, and converts it into
# docs/assets/demo.mp4 + demo.gif for the README. See scripts/record-demo/README.md.
demo:
cd scripts/record-demo && npm install
cd scripts/record-demo && npm run record
cd scripts/record-demo && npm run convert
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ each change before the build goes green.

</div>

## Demo

![diffboard demo](docs/assets/demo.gif)

A real run against the demo fixture in this repo: sign in, capture a baseline, ship a real CSS
change to the CTA button, then triage the flagged diff in the review dashboard - approving the
desktop screenshot (the intended rebrand) and rejecting the mobile one, where the same change
shrinks the button below a usable tap target. Full quality: [docs/assets/demo.mp4](docs/assets/demo.mp4).

## What it does

- Captures screenshots of any URL or Storybook story across a viewport matrix, driven by a
Expand All @@ -36,12 +45,6 @@ each change before the build goes green.
- Runs entirely on infrastructure you already have: `docker compose up` gets you Postgres +
the dashboard, with no per-snapshot billing and no third party watching your screenshots

<img src="docs/assets/demo.gif" alt="Reviewing a visual diff on the diffboard dashboard: a CTA button's padding and color changed, shown with a slider comparison, then approved" width="900">

*A real recording against the running app in this repo: a CSS change shrinks and washes out
a button, `diffboard test` flags it as changed in CI, and the reviewer compares it on the
dashboard before approving.*

## Why not Percy or Chromatic?

Percy and Chromatic are good products, billed per screenshot. Chromatic's free tier caps out
Expand Down
Binary file modified docs/assets/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/demo.mp4
Binary file not shown.
61 changes: 61 additions & 0 deletions scripts/record-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Demo recorder

Boots the real diffboard stack from source and records a genuine walkthrough of the product
loop: sign in, create a project, capture a baseline against `examples/demo-site`, ship a real
visual change, and triage the resulting diff in the review dashboard (approve one screenshot,
reject another). Produces `docs/assets/demo.mp4` and `docs/assets/demo.gif` for the README.

This package is dev-only - it lives outside `pnpm-workspace.yaml`'s `packages/*` glob, so it
is never pulled into the product build or `pnpm install` at the repo root.

## Run it

From the repo root:

```bash
make demo
```

Or step by step from this directory:

```bash
npm install
npm run record # boots Postgres (docker), the dashboard, and demo-site; drives the browser; saves out/demo.raw.webm
npm run convert # ffmpeg: out/demo.raw.webm -> docs/assets/demo.mp4 + demo.gif
```

## What `record.mjs` does

1. Builds `@diffboard/core`, the CLI, and the server (`next build` - a **production** build;
`next dev`'s dev-mode overlay intercepts clicks and was flaky under this recorder).
2. Starts a fresh, disposable Postgres container (`diffboard-demo-pg`, host port `5434` so it
doesn't collide with any dev Postgres on `5432`/`5433`), runs migrations, and seeds the
admin account.
3. Starts the dashboard (`next start`, port `4300`) and `examples/demo-site` (port `3100`,
`DEMO_VARIANT=v1`).
4. Drives a headless Chromium via Playwright (`recordVideo`, 1280x800,
`deviceScaleFactor: 2`) through the real UI: sign in, create a project, capture its token
from the page.
5. Shells out to the real `diffboard` CLI twice - once against `v1` (baseline) and once
against `v2` (a real CSS change already in the fixture: a shrunk, washed-out CTA button) -
and navigates the browser to each resulting build.
6. Reviews the second build's real diff in the dashboard: drags the slider, switches to
side-by-side and diff-overlay, approves the desktop screenshot (the rebrand is the
intended change) and rejects the mobile one (the same CSS change shrinks the button below a
usable tap target on a small screen - a genuine regression, not a fabricated one).
7. Stops every child process and removes the Postgres container so the next run starts clean.

Re-running against a freshly seeded stack produces the same walkthrough - there's no random
data and no reliance on state left over from a previous run.

## Ports

Only `:4300` is a fixed requirement (the app). If `:4300` is already taken by something else,
this fails fast rather than rewriting the committed port - free it and re-run. `:3100` (demo
site) and `:5434` (recorder's own throwaway Postgres) can be changed at the top of
`record.mjs` if they collide with something on your machine.

## Debugging

Child process output (the dashboard and demo-site) is captured to `.tmp/<name>.log` instead
of being discarded, since Next's production server is otherwise silent on success.
42 changes: 42 additions & 0 deletions scripts/record-demo/convert.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Converts the raw Playwright recording (out/demo.raw.webm) into the two
# assets the README embeds: docs/assets/demo.mp4 (full quality, linked) and
# docs/assets/demo.gif (embedded inline - must stay under 10 MB).
set -euo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$HERE/../.." && pwd)"
RAW="$HERE/out/demo.raw.webm"
MP4="$REPO_ROOT/docs/assets/demo.mp4"
GIF="$REPO_ROOT/docs/assets/demo.gif"
PALETTE="$HERE/out/palette.png"

if [ ! -f "$RAW" ]; then
echo "No raw recording at $RAW - run 'npm run record' first." >&2
exit 1
fi

mkdir -p "$REPO_ROOT/docs/assets"

echo "Encoding $MP4 ..."
ffmpeg -y -i "$RAW" -vf "scale=1280:-2" -c:v libx264 -pix_fmt yuv420p -movflags +faststart "$MP4"

echo "Building palette for the GIF ..."
ffmpeg -y -i "$RAW" -vf "fps=12,scale=960:-2:flags=lanczos,palettegen" "$PALETTE"

echo "Encoding $GIF ..."
ffmpeg -y -i "$RAW" -i "$PALETTE" \
-lavfi "fps=12,scale=960:-2:flags=lanczos[x];[x][1:v]paletteuse" \
"$GIF"

rm -f "$PALETTE"

MP4_SIZE=$(du -h "$MP4" | cut -f1)
GIF_SIZE=$(du -h "$GIF" | cut -f1)
echo "Done: $MP4 ($MP4_SIZE), $GIF ($GIF_SIZE)"

GIF_BYTES=$(wc -c < "$GIF")
if [ "$GIF_BYTES" -gt 10485760 ]; then
echo "WARNING: $GIF is over 10 MB ($GIF_BYTES bytes) - drop fps or shorten the walkthrough." >&2
exit 1
fi
78 changes: 78 additions & 0 deletions scripts/record-demo/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions scripts/record-demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "diffboard-record-demo",
"private": true,
"version": "0.0.0",
"description": "Dev-only recorder that boots the real diffboard stack and captures docs/assets/demo.mp4 + demo.gif. Not part of the pnpm workspace or product build.",
"type": "module",
"scripts": {
"record": "node record.mjs",
"convert": "bash convert.sh"
},
"devDependencies": {
"@playwright/test": "^1.62.1"
}
}
Loading
Loading