Demo-Videos mit bueno-demo
Mit bueno-demo zeichnest du einen skriptgesteuerten Durchlauf durch eine Webseite auf und renderst ihn in einen Screen-Studio-ähnlichen Clip. Es gibt zwei Schritte: Capture steuert ein echtes Chromium-Fenster und zeichnet auf; Render fügt Kamera, Cursor und Titel hinzu.
Einrichtung (einmalig pro Rechner)
npm i -D bueno-demo # oder alles mit npx bueno-demo ausführen
npx playwright install chromium
brew install ffmpeg # erforderlich; füge imagemagick für WebP-Poster hinzu
Der Ablauf
- Schreibe ein Skript, eine JSON-Datei (siehe unten). Halte eine Idee pro Clip, 10 bis 15 Sekunden.
- Capture:
npx bueno-demo capture demos/<name>.json out/<name> - Optionaler Schnitt eines langsamen Ladevorgangs:
npx bueno-demo cut out/<name> <von-s> <bis-s> - Render:
npx bueno-demo render out/<name> out/<name>/<name> --gif - Überprüfe das Ergebnis vor der Verwendung. Erstelle ein Kontaktblatt und prüfe jedes Bild:
ffmpeg -i out/<name>/<name>.mp4 -vf "fps=1,scale=640:-1,tile=3x4" -frames:v 1 sheet.pngWenn ein Fehler oder private Daten im Bild erscheinen, korrigiere das Skript oder schneide den Clip, und rendere dann erneut.
Skripte
{
"url": "http://localhost:3000/settings",
"viewport": [1440, 900],
"storage": "auth.json",
"keep": "document.querySelectorAll('[data-user-email]').forEach(e => e.textContent = '[email protected]')",
"steps": [
{ "scroll": "[data-testid=billing]" },
{ "click": "[data-testid=plan-toggle]", "after": 1200 },
{ "type": "[data-testid=team-name]", "text": "Northwind", "clear": true },
{ "hover": "[data-testid=save]", "zoom": true },
{ "click": "[data-testid=save]", "zoom": false },
{ "wait": 800 }
]
}
Schritte, jeweils optional mit after (ms Pause danach):
click: zum Element gleiten und klicken.zoom: falseüberspringt den Kamera-Zoom; verwende es bei Klicks, die zu einer neuen Seite navigieren.type: klicken, danntexteingeben (delayms pro Taste, Standard 55;clear: trueersetzt den Wert).fill: klicken, danntextsofort setzen, für Eingabefelder, die Tippen nicht steuern kann (z. B. Datumswähler).hover: dorthin gleiten.zoom: truerichtet auch die Kamera aus.upload: zu einer Drop-Zone gleiten undfile(relativ zum Skript) aninputanhängen.press: eine Taste, z. B."Escape".scroll: das Element sanft in den sichtbaren Bereich scrollen.wait: Pause in ms.eval: JavaScript auf der Seite ausführen (sichtbar für die Kamera).
Optionen:
- Anmelden:
storageverweist auf eine Playwright-Speicherstatusdatei (erstellen mitnpx playwright codegen --save-storage=auth.json <url>). Oder"cookiesFrom": "agent-browser"leiht eine angemeldete agent-browser-Sitzung aus. Cookies werden nie ausgegeben. - Datenschutz:
keepist JavaScript, das vor der Aufzeichnung und währenddessen alle 250 ms ausgeführt wird, um echte Namen und E-Mails durch Platzhalter zu ersetzen, sodass ein erneutes Rendern sie nie zurückbringt.hidefügt CSS-Selektoren zum Ausblenden hinzu (Dev-Overlays von Next.js und Vercel sind bereits ausgeblendet;"hide": falsezeigt sie an). - Nicht im Bild:
setupist JavaScript, das einmal vor der Aufzeichnung ausgeführt wird (z. B. um ein Banner zu schließen). - Gefühl:
travelMs(Cursor-Bewegungsdauer, Standard 700),settle(Wartezeit nach dem Laden, Standard 1500),tail(Haltezeit am Ende, Standard 1200),cursorStart([x, y]),reducedMotion: true. - Laden:
waitUntil(Standardnetworkidle). Eine Seite, die Videos streamt oder pollt, wird nie inaktiv; die Aufnahme würde dann abbrechen. Setze"load"und lasssettleden Rest übernehmen. - Anderer Server:
--base http://localhost:3001beim Capture zeichnet denselben Pfad auf einem anderen Server auf.
Selektoren: bevorzuge data-testid oder Struktur (#title, input[name=amount]) gegenüber sichtbarem Text, damit Textänderungen das Skript nicht zerstören.
Render-Flags
--zoom 1.6wie weit ein Klick zoomt (1.3, wenn zwei Dinge gleichzeitig wichtig sind).--speed 0.9spielt 10 % langsamer;--speed 2.5für eine schnelle Collage.--end 9.8schneidet bei dieser Sekunde ab;--hold 1.2friert das letzte Bild ein.--matte '#fdfaf1'rahmt die Seite als abgerundetes Fenster auf dieser Farbe ein (Standard ist randlos).--title 'Termin verschieben' --kicker 'Termine'fügt einen Eröffnungstitel hinzu;--end-capfügt eine Schlusskarte hinzu.--brand brand.jsonlegt das Aussehen von Titel und Karte fest:ink,paper,accent,displayFont/monoFont({ "family", "file", "weight" }),logo,footer,kicker. Pfade sind relativ zur Brand-Datei.--fitrendert im eigenen Seitenformat (Hochformat für ein Smartphone-Viewport).--gifschreibt zusätzlich ein 960 px, 15 fps GIF.
Regeln
- Nie echte Kundendaten aufzeichnen. Verwende Testkonten und einen
keep-Hook für persönliche Daten; überprüfe das Kontaktblatt vor der Weitergabe. - Kurz und zweckgebunden. Eine Funktion pro Clip; schneide Wartezeiten mit
cutab. - Video außerhalb des Repos hosten. Committe das Skript (und ein Poster, falls deine Dokumentation es benötigt); lege MP4s und GIFs in deinem Storage oder CDN ab.