From 7f9b566f729acc4d14b682f751cfe77b4ec6f300 Mon Sep 17 00:00:00 2001 From: kipavy Date: Fri, 24 Jul 2026 11:24:11 +0000 Subject: [PATCH] feat(headless): capture screenshots/video of the app + brand framing Enable faithful media capture from the headless dev stack and document it as a skill: - compose.headless.yml: set WEBKIT_DISABLE_COMPOSITING_MODE=1. WebKitGTK renders CSS opacity/transform animations on GPU compositor layers that Xvfb's software path never flushes to the X framebuffer, so screen-grabbed captures silently drop every transition. Forcing compositing off paints into the readable window buffer. - Dockerfile.tauri-headless: add ffmpeg (x11grab recording) and python3-pil (brand framing). - .claude/skills/capturing-voltius-media: new skill covering stills (existing docs pipeline), video (record via ffmpeg x11grab, drive via tauri-driver, edit via ffmpeg zoompan), and brand-gradient framing that reuses docs/tools/screenshots/frame.py. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01HKUYb6g8PZNPSususpTGPg --- .../skills/capturing-voltius-media/SKILL.md | 112 ++++++++++++++++++ .../capturing-voltius-media/edit-video.sh | 32 +++++ .../capturing-voltius-media/frame-video.sh | 24 ++++ .../capturing-voltius-media/frame_video.py | 29 +++++ .../capturing-voltius-media/record-video.sh | 42 +++++++ Dockerfile.tauri-headless | 5 +- compose.headless.yml | 7 ++ 7 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 .claude/skills/capturing-voltius-media/SKILL.md create mode 100644 .claude/skills/capturing-voltius-media/edit-video.sh create mode 100644 .claude/skills/capturing-voltius-media/frame-video.sh create mode 100644 .claude/skills/capturing-voltius-media/frame_video.py create mode 100644 .claude/skills/capturing-voltius-media/record-video.sh diff --git a/.claude/skills/capturing-voltius-media/SKILL.md b/.claude/skills/capturing-voltius-media/SKILL.md new file mode 100644 index 000000000..defdcd570 --- /dev/null +++ b/.claude/skills/capturing-voltius-media/SKILL.md @@ -0,0 +1,112 @@ +--- +name: capturing-voltius-media +description: Use when producing screenshots or videos of the Voltius app UI for docs, blog posts, changelog, or demos — capturing or recording the live headless dev build and editing the result (crop, zoom, loop) with ffmpeg. +--- + +# Capturing Voltius Media (screenshots & video) + +Produce real screenshot and video assets of the Voltius desktop app by driving the +**headless dev build** and screen-grabbing its Xvfb framebuffer. Same stack as +[[iterating-on-voltius-ui]] (`compose.headless.yml`: `tauri-headless` = app + Xvfb + +tauri-driver on 4444; `ssh-host-1` = throwaway SSH host `voltius`/`voltius`). + +**Path note:** the screenshot pipeline and brand framing live in the **docs sibling repo**, +`../docs/tools/screenshots/` — one level *up* from the voltius repo (there is no `docs/` +inside voltius). All references below are to that sibling path. + +## ⚠️ The one thing that will silently ruin captures + +**WebKitGTK renders opacity/transform animations (CSS transitions, `animate-fadeIn`, +dropdown/modal open) on GPU compositor layers. Under Xvfb's software path those layers +are NOT flushed to the X framebuffer that a screen grab reads** — so recordings show +**instant jumps with no transitions**, and a screenshot taken mid-animation shows the +pre-animation state. There is no error; it just looks wrong. + +**Fix: run the app with `WEBKIT_DISABLE_COMPOSITING_MODE=1`.** It's baked into +`compose.headless.yml` (`tauri-headless` `environment:`). Verify before trusting a capture: + +```bash +docker exec tauri-headless sh -c 'pid=$(pgrep -f target/debug/voltius|head -1); \ + tr "\0" "\n" ` — put the scene .mjs directly + in `/tmp` and pass a **bare filename**, not a path. +2. **Recording.** ffmpeg `x11grab` on the app's display. Derive `DISPLAY`/`XAUTHORITY` + from the running app process (the Xvfb auth path regenerates per boot). Grab the exact + window region (set the window to a known size first, e.g. `1200x800@0,0`). See + `record-video.sh` — it also logs an `event → epoch` **timeline** so edits can key zooms + to real moments. +3. **Editing = ffmpeg only.** `edit-video.sh` has the recipes: crop, **smooth keyed zoom + via `zoompan`** (NOT `crop` — `crop` evaluates `w`/`h` once at config time, so it can't + animate; `zoompan` can), supersample-then-downscale to kill zoom shimmer, clean loop, + and web encode (`-pix_fmt yuv420p -movflags +faststart`, silent). + +Blog `