Was macht Synthetische UI-Demos?
Nichts wird per Bildschirmaufnahme erstellt. Claude Code liest das Repository des Produkts, baut nur die Bildschirme einer Szene als statisches React nach, und ein skriptgesteuerter Cursor und eine Kamera führen den Ablauf in Remotion aus. Jeder Klick ist eine Framenummer, sodass eine Szene identisch neu gerendert wird, nie ein eingeloggtes Konto oder eine Live-Umgebung benötigt und durch Bearbeiten des Codes aktualisiert wird, wenn sich das Produkt ändert.
Referenz-Builds (parallel zu diesem Skill lesen; beide bauen ohne die Produkt-Repos):
examples/ds-free-schedule-demo: ein 26-Sekunden-Ablauf (Termin planen) mit einer Sprecher-Kachel. Der einfachste vollständige Fall. Seinesrc/Demo.tsxist eine einzelne Timeline mit jedem Cursor-Keyframe, Klick und jeder Kamerabewegung.examples/ds-release-649: ein 80-Sekunden-Release-Notes-Video, acht Szenen über drei Apps.templates/ds-release-notesist die wiederverwendbare Form davon (ausgeliefert als Release 650).src/Release.tsxsequenziert die Szenen,src/scenes/enthält eine Datei pro Release-Element, undsrc/room/undsrc/ac/sind die Bildschirm-Kits.
Wann dies statt Playwright verwenden
| Synthetisch (dieses Skill) | Playwright-Aufzeichnung (/record-demo) |
|---|---|
| Du kannst den Quellcode der App lesen | Du hast nur die laufende App |
| Release Notes und Feature-Touren, die pro Release neu gemacht werden | Einmalige Aufnahme von echtem Verhalten oder echten Daten |
| Der Ablauf benötigt Daten, die du nicht hast (ein kompletter Anruf, andere Benutzer, eine Admin-Ansicht) | Das echte Ding ist leicht erreichbar und sieht so aus wie es ist |
| Kamerabewegungen, exaktes Klick-Timing, Neurendering nach einer Sprachänderung | Lange, komplexe Interaktionen, deren Nachbau aufwändig wäre |
Bausteine (lib/components)
import { Stage, typed, cursorAt, near } from '../../../../lib/components';
<Stage
url="app.example.com/settings" // Adressleiste; Host dunkler als Pfad
camera={CAMERA} // [{ f, z, x, y }] in Canvas-Px, eased, auf das Fenster begrenzt
path={PATH} // [{ f, x, y }] Cursor-Keyframes in Canvas-Px
clicks={CLICKS} // Frames, die den Cursor zusammendrücken und eine Welle auslösen
aiNote // "Die gezeigten Personen sind KI-generiert" (oder eigener Text)
canvasWidth={1440} canvasHeight={810} // die Standardwerte
>
<SettingsScreen field={typed(frame, 'Q4 Produktbewertung', 128)} />
</Stage>
Stagezeichnet einen Browser-Chrome um eine feste App-Leinwand, wendet die Kamera an und platziert den Cursor darüber bei zIndex 100. Bei Zoom 1 ist das Fensterfit(0,8625) der Framebreite. Keyframes wiederholen einen Wert, um ihn zu halten.CursorundcursorAtfunktionieren auch eigenständig für eine Nicht-Browser-Szene;near(cursor, target)steuert Hover-Zustände.- Produktspezifische Bildschirm-Kits (Theme, Icons, Layouts) bleiben im Projekt. Sie sind kein Lib-Material.
Methode
1. Ein visuelles Spezifikation aus dem App-Repo ziehen, mit Unteragenten
Sende einen Unteragenten pro App oder Bereich parallel und lasse jeden eine kompakte Spezifikation statt Datei-Dumps zurückgeben:
- Theme: Palette, Schriftstapel, Radien, Abstände, Schatten (aus der Theme-Datei oder CSS-Variablen).
- Strings: die exakten deutschen Locale-Strings für jedes Label, Menüelement, Dialog und Toast, das die Szene zeigt (
public/locales/en/*.json,src/i18n/locales/en/*). UI-Text niemals paraphrasieren. - Icons: Wenn die App eine Icon-Schriftart verwendet, enthält ihre Icomoon/Fontello
selection.jsondie SVG-Pfade jedes Glyphen. Extrahiere die benötigten in ein kleines JSON und rendere sie als<svg><path d="...">. Kopiere andernfalls die SVG-Komponenten. - Layout und Verhalten: Komponentenbaum für die gezeigten Bildschirme, Toolbar-Reihenfolge, Standardwerte, was jedes Steuerelement tut.
Lese den Standard-Branch vom Remote (git show origin/main:path, oder git fetch dann origin/<default> lesen). Lokale Checkouts sind oft veraltet und zeigen die UI des letzten Quartals.
Bei Release Notes verfolge jedes Element bis zum Commit und der Komponente zurück, die es zuerst ausliefert. Das liefert die exakten Strings und die Standardwerte, die gezeigt werden sollen.
2. Nur das nachbauen, was eine Szene zeigt, auf einer festen Leinwand
- Platziere jedes Element mit absoluten Positionen auf einer festen Leinwand (1440x810 ist ein guter Standard). Exportiere die relevanten Rechtecke als Konstanten (
SAVE_BTN = { x, y, w, h }), damit Klickziele arithmetisch sind, nicht geraten. - Baue nur das, was die Kamera sieht. Ein Panel, das nie im Bild ist, kann eine einfarbige Fläche sein.
- Halte es statisch React: kein State, keine Effekte. Jede visuelle Änderung ist eine Funktion von
frame. - Lege das Kit in einem eigenen Ordner (
src/ui/,src/room/) mit einertheme.tsfür Konstanten.
3. Sprache zuerst, dann die Timeline in Frames setzen
- Schreibe eine Zeile pro Szene, an Satzgrenzen geteilt (eine
.txtpro Phrase), und generiere zuerst das Audio (voiceover.py --scene-dir ...). Drei Aufnahmen undtools/pick_takes.pyschlagen eine einzelne Aufnahme plus--max-wpm: ein Klon kopiert das Tempo und die Flachheit des Referenzclips. Wenn die Zeilen also gehetzt oder monoton klingen, korrigiere den Clip mittools/voice_prosody.py expand, anstatt das Ausgabe zu dehnen (siehe "Clone Prosody" in CLAUDE.md). Lies die gemessenen Dauern, undtools/captions.pyfür Wort-Zeitangaben. - Messe jede Szene an ihrer Zeile plus einem Beat und behalte einen Header-Kommentar, der den Start und die Länge jeder Zeile auflistet. Dieser Kommentar ist die Quelle der Wahrheit (siehe "Audio-Anchored Timelines" in CLAUDE.md).
- Alles ist eine Frame-Konstante am Anfang der Szenendatei: Cursor-Keyframes,
CLICKS, Kamera-Keyframes, UI-State-Wechsel (frame >= C.savezeigt den Dialog). Exportiere die Klick-Frames, damit das Elternteil Klick-SFX platzieren kann. - Suche die Frames aus der Erzählung, setze sie nicht von Hand.
voice()(lib/components/cues.ts; die ds-release-notes-Vorlage hat eine Kopie) nimmt die Phrasen der Szene mit ihren.captions.jsonund Start-Frames entgegen, undcue('save')gibt den Frame zurück, an dem das Wort beginnt. Verankere jede Aktion an dem Wort, das sie veranschaulicht, mit einem Offset, und jeden Cursor- und Kamera-Keyframe an diesen Ankern:Regeneriere oder verschiebe eine Phrase, und die Klicks, das Tippen, der Cursor und die Kamera folgen ihr. Ein Cue, dessen Wörter nicht mehr gesprochen werden, lässt den Build mit den betreffenden Wörtern fehlschlagen. Optionen:import line06a from '../../public/audio/scenes/06a_link.captions.json'; export const LINK_VOICE = voice([{ file: '06a_link', at: 10, captions: line06a }, ...]); const { cue } = LINK_VOICE; const C = { field: cue('before', { offset: -3 }), save: cue('save') }; const PATH = [{ f: C.field - 6, ...field }, { f: C.save - 24, ...saveBtn }];offset,edge: 'end',after(ein Frame, für ein wiederholtes Wort),nth,in(eine Phrase). Das Elternteil platziert das Audio ausLINK_VOICE.lines, sodass der Start-Frame einer Phrase einmal geschrieben wird.templates/ds-release-notes/src/scenes/FreeCustomLink.tsxist das ausgearbeitete Beispiel.
4. Dichte Listenzeilen an ihren Pausen teilen
Eine Zeile, die mehrere Dinge auflistet ("Terminplanung, Kontakte, Aufzeichnungen und Transkripte"), ist für eine einzige Einstellung zu schnell. Teile das Audio an seinen Pausen in Phrasen-Dateien (07a, 07b, ...), gib jeder Phrase ihre eigene Einstellung und platziere jede Phrase auf ihrem eigenen Beat. ds-release-649's moreVo-Feld tut dies.
5. Klickziele aus einem Kontaktbogen vor dem vollständigen Render messen
Eine geratene Koordinate liegt nach dem Zoomen der Kamera meist 20-50 Pixel daneben. Rendere Standbilder bei jedem Klick-Frame und ein paar Frames davor, kachel sie zusammen und überprüfe, ob die Cursor-Spitze auf dem Ziel sitzt:
i=1; for f in 46 78 122 212; do npx remotion still MyComp out/stills/s$i.png --frame=$f; i=$((i+1)); done
ffmpeg -start_number 1 -i out/stills/s%d.png -vf "scale=640:-1,tile=2x2" -frames:v 1 out/contact.png
Korrigiere die Konstanten, rendere die Standbilder neu und erst dann rendere das Video.
6. KI-generierte Personen auf dem Bildschirm kennzeichnen
Jede Person, die nicht real ist (FLUX.2-Porträts in Kacheln, Avatare, eine Sprecherin) erhält ein sichtbares Label: aiNote auf dem Stage oder ein "KI"-Abzeichen auf einer Sprecher-Kachel. Namen, Kontakte und Anrufdaten sind ebenfalls erfunden, also halte sie deutlich als fiktiv erkennbar.
7. Rendern, dann normalisieren
tools/qc.py --project ... vor dem Rendern, dann qc.py --render out/video.mp4 --fix für -14 LUFS. qc.py --project ... --layout prüft den eigenen Text des Videos (Untertitel, Karten, die KI-Notiz) auf Größe und Kontrast und listet die UI des Produkts separat auf. Stage markiert sein Fenster mit data-qc="product"; setze dasselbe Attribut auf jeden Telefon- oder Tablet-Mockup, den du außerhalb baust, sonst wird sein UI-Text als Teil des Videos beurteilt.
Fallstricke
- Monochrom-SVG-Logos werden schwarz gerendert. Ein
logo_mono.svghat normalerweise schwarze Füllungen; auf dunklem Hintergrund verwendefilter: brightness(0) invert(1). - Veraltete lokale Branches. Lese
origin/<default>, nicht den Branch, auf dem der lokale Checkout gerade ist. rsync --exclude srcverwirft auchnode_modules/*/src. Beim Kopieren eines Projekts oder Kits verankere Ausschlüsse am Übertragungsstamm mit einem führenden Schrägstrich:--exclude /src.- Die Kamera wird begrenzt. Ein Kamerazentrum nahe der Canvas-Kante wird nach innen gezogen, damit der Fensterrand niemals über den Frame hinausragt. Wenn ein Zoom an einer unerwarteten Stelle landet, liegt es daran; ziele auf einen weiter innen liegenden Punkt.
- Menüs und Dialoge müssen innerhalb der Leinwand sitzen. Die Leinwand schneidet ihre Kinder zu. Positioniere ein Menü so, dass es nach innen öffnet, wie die echte App es tut.
- Hover-Zustände kommen vom Cursor-Pfad. Verwende
near(cursorAt(frame, PATH), target)anstelle eines zweiten Satzes von Frame-Konstanten, damit das Verschieben eines Keyframes den Hover mit verschiebt. - Eine geänderte Sprachzeile verschiebt alles danach. In einer gecue-ten Szene folgen Klicks, Cursor und Kamera von selbst, aber die Szenenlänge und die
atder späteren Phrasen müssen noch überprüft werden. In einer von Hand gesetzten Szene musst du jeden Frame neu messen. In jedem Fall den Header-Kommentar und die Szenenlängen aktualisieren, dann Klick-Standbilder erneut prüfen. Wenn das Lippen-synchrone eines Sprechers von der zusammengestellten Sprachspur gesteuert wird, generiere es ebenfalls neu. - Ein Kit überlebt seine Demo. Ein nachgebautes Bildschirm-Kit kann andere Videos hosten: Der Raum von
ds-release-649wurde zur Bühne einer Parodie-Kurzversion einer Markenedition, mit animierten Gesichtern in seinen Kacheln und keiner neuen Bildschirmarbeit. Kopiere das Kit in das neue Projekt und skaliere seine logische Leinwand auf den Frame (.claude/skills/parody-short/formats.md).