Desktop MP4 export renders frames from a hidden BrowserWindow via
capturePage, pipes PNGs to a bundled ffmpeg (libx264/yuv420p/+faststart),
and writes the file through an atomic temp-file swap. It shares scene
validation (shared/renderValidation.cjs) and the render.html renderer with
the standalone Puppeteer server, which is hardened in parallel.
Render worker & main (electron/):
- abortable render loop with an inactivity watchdog and cancel plumbing
- atomic temp-file + rename so a cancel/failure never truncates a
pre-existing target (#7); stdin EPIPE / early-exit / backpressure-hang
handled (#8)
- HiDPI frames normalized to canvas size (#10); odd dimensions padded to
even (#13, also server); backgroundThrottling disabled (#12)
- reject canvases larger than the display (#11); single-render guard;
isolated render session that blocks network requests (#5)
Tests:
- Electron harnesses (scripts/verify-*.cjs) for capture, export, cancel,
timeout, assets, background, capture-fidelity, resolution, odd-dims,
aggregated by verify-desktop-render.cjs
- server smoke + e2e and shared validation unit tests
- headless hook (src/devtools/headless.ts) to run the in-app suite under
browser/electron hosts; pixel-diff util + capture fixtures (test/)
- dev_docs plans: integration, fixes (#7-#13), and the e2e/capture strategy
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New frame-by-frame export pipeline in src/lib/frameExport.ts: loads
scene images, walks rangeIn..rangeOut at scene.fps, drawSceneFrame
to an offscreen canvas, awaits a per-frame callback, yields every
4 frames to keep progress responsive, honours AbortSignal.
- Animated GIF via gifenc (per-frame quantize/palette, ~5 KB lib).
- Animated PNG via UPNG.js + pako (lossless RGBA, alpha-aware, ~30 KB).
- drawSceneFrame now clearRects first and skips the background fill
when scene.canvas.background === 'transparent' — prerequisite for
the alpha to reach the APNG output.
- DocumentDrawer: new checkerboard toggle next to the colour picker
flips the stage between its last-picked colour and transparent;
disabled swatch + 'Transparent' hex label make the state obvious.
- ExportDialog: three new option rows — Animated GIF, Animated PNG,
and an APNG preview page that bundles the rendered APNG into a
standalone HTML with a checkerboard backdrop + dark/light toggles
for verifying transparency at a glance.
- Tests: renderFrames frame count + abort, GIF header magic, APNG
acTL presence, transparent round-trip (decoded alpha == 0 on
un-painted pixels), preview HTML structure, and the two UI tests
covering ExportDialog's new rows and the progress-view transition.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>