Skip to main content

Reproducible screenshot capture for docs — drive a web app or CLI from a declarative shot list and capture polished feature screenshots.

Project description

capture

CI Python 3.11+ License: MIT

Screenshots for your docs — as code. One committed shot list captures your web pages, your real terminal windows, and stateful CLI sessions — and regenerates them all with a single command.

The old way: dragging Screen Shot 2026-... files into ever-more-cursed filenames, then shipping a UI tweak that makes them all stale. The capture way: one `capture run`.

The problem

Documenting a feature means launching the app, clicking to the right state, screenshotting, naming the file, and embedding it — every time the UI changes. The screenshots drift out of date the moment you ship, and nobody notices until they're embarrassingly wrong.

capture makes them reproducible: describe how to start your app and what to shoot once, in a committed .capture.yaml, then regenerate the whole set on demand — locally or in CI. Same config + same app state → same screenshots.

Quickstart

pip install shotlist             # installs the `capture` command
playwright install chromium      # one-time browser download

capture init        # writes a starter .capture.yaml
capture run         # boots your app, captures every shot, tears it all down

One shot list, four kinds of shot

output:
  dir: docs/screenshots
  readme: README.md            # optional: splice <img> snippets straight into the README

app:                           # optional — omit for static sites or pure-CLI shots
  command: "npm run dev"
  ready: { url: http://localhost:5173, timeout: 30 }   # never shoot a half-booted app

shots:
  - { name: dashboard, kind: web, url: http://localhost:5173/dashboard, full_page: true, alt: "Dashboard" }
  - { name: cli-help,  kind: cli, command: "mytool --help", alt: "Top-level help" }
Kind Captures How
web a browser page — with optional click/fill/wait steps first Playwright / Chromium
cli · native (macOS default) a real screenshot of your Terminal.app window — your font, your theme AppleScript + screencapture
cli · rendered (any OS, CI-safe) the command's output drawn as a styled terminal card PTY → ANSI→HTML → Chromium
session a stateful, multi-command flow in one persistent terminal — one shot per step one Terminal window, captured after each step

A session is how you screenshot a flow whose later steps depend on earlier ones — the shell state (cwd, env, background processes) carries across. Background a long-running process with & and a small wait_ms, keep capturing, and the session tears it down on close.

Recipes

Copy-paste .capture.yaml configs for the common jobs — test-evidence proofs, CI regeneration, long-running servers, web flows with interactions, versioned visual history — live in docs/recipes.md.

Proof reports & pipelines

Every capture run also writes, next to the PNGs:

  • index.html — a self-contained gallery you can open and share as a proof report;
  • manifest.json — a machine-readable record of the run (a pipeline artifact).
The generated index.html gallery: a header with the shot count and timestamp, then a card per shot showing the screenshot, its name, a kind badge, and its alt text.

Attach manifest.json to a CI job, or open index.html as test-evidence. Gate CI with capture check — it re-captures and fails when a screenshot drifts from the committed baseline (capture check --update to accept intended changes; add --diff DIR to render baseline·current·diff images) — or drop in the bundled GitHub Action. Turn the report off with --no-report (or output.report: false). Details in docs/pipeline.md.

Why capture, and not the others

The pieces exist in isolation; capture is the one tool that does all of it under a single committed config.

web pages real terminal CLI sessions README auto-embed reproducible / CI
capture
shot-scraper
freeze / carbon synthetic
Percy / Chromatic ✅ (cloud, paid)
doing it by hand 😖 😖 😖

No cloud, no paid services, no special OS permissions for web/rendered shots. (Native Terminal capture needs macOS Screen-Recording permission; everything else needs nothing.)

How it works

.capture.yaml ─► load + validate ─► [ boot app, wait until ready ] ─► one engine
                                                                        routes each
                                                                        shot by kind:
        web ───────► Playwright / Chromium
        cli·native ► a real Terminal.app window
        cli·render ► PTY → ANSI→HTML → Chromium
        session ───► one persistent Terminal, a shot per step
                                                                      ─► NN-name.png
                                                                         + README splice

The clever part is what isn't here: no AI runs at capture time. Claude's only job is to author the .capture.yaml once by reading your repo; after that the engine is a plain, deterministic program — fast, free, and re-runnable in CI with no model in the loop. See the full design in docs/design.md.

Robust by design. The readiness probe (HTTP / TCP port / log line) means you never screenshot a half-booted app, and the app is launched in its own process group and torn down — even on a crash or Ctrl-C — so a capture run never leaves an orphaned dev server behind.

Capture, captured by capture

This repo dogfoods itself: the shots below are produced by running capture run on its own .capture.yaml and spliced in automatically.

The capture CLI

capture --help showing the init, validate, and run commands

Run options

capture run options: --config, --only, and --version

Use with Claude

capture ships an optional Claude integration in integrations/claude/:

  • a /capture skill that inspects your repo (routes, --help, README), writes the .capture.yaml for you, and runs it;
  • an optional auto-snapshot hook that drops a raw snapshot when a dev server starts (the honest "dumb snapshot"; the curated set always comes from capture run).

Commands

Command What it does
capture init Scaffold a starter .capture.yaml
capture validate Check the shot list is well-formed
capture run Capture every shot and write outputs
capture run --only dashboard Capture a single shot by name
capture run --version v2 Write into a versioned subfolder
capture check Fail if a screenshot drifted from the committed baseline
capture check --update Re-shoot and accept the current screenshots as the baseline
capture check --diff DIR Also render baseline·current·diff images for changed shots

Develop

git clone https://github.com/varmabudharaju/capture && cd capture
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
playwright install chromium
pytest                       # the suite is fully offline

The hero GIF is itself reproducible — demo.tape + vhs demo.tape.

License

MIT © Varma Budharaju

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

shotlist-0.1.0.tar.gz (47.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

shotlist-0.1.0-py3-none-any.whl (30.5 kB view details)

Uploaded Python 3

File details

Details for the file shotlist-0.1.0.tar.gz.

File metadata

  • Download URL: shotlist-0.1.0.tar.gz
  • Upload date:
  • Size: 47.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for shotlist-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8d63d017fde96462dcda929b51a910750b8f8141569f9f0cb74f5dd558cba2af
MD5 82b43bc096c9d221567ca75cfbe87033
BLAKE2b-256 cb3e2c5d40bd4f18c564fa40e66a18913f825d9eabb02abb0271f6bec6714dd8

See more details on using hashes here.

Provenance

The following attestation bundles were made for shotlist-0.1.0.tar.gz:

Publisher: publish.yml on varmabudharaju/capture

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shotlist-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: shotlist-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 30.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for shotlist-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2a0c81e2ac1cd226d5f0773785873e35797a25b427bd67001f14d8cff7c76112
MD5 1e9db274cb68a17f9b6b7da6186ed14f
BLAKE2b-256 df9ea83ba013bfeacf7e52f3e40d65c6bc1b55941dcd556850b0c27f77bfe334

See more details on using hashes here.

Provenance

The following attestation bundles were made for shotlist-0.1.0-py3-none-any.whl:

Publisher: publish.yml on varmabudharaju/capture

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page