Démos UI Synthétiques
Rien n'est enregistré depuis l'écran. Claude Code lit le dépôt du produit, reconstruit uniquement les écrans qu'une scène montre en React statique, et un curseur ainsi qu'une caméra scriptés exécutent le parcours dans Remotion. Chaque clic est un numéro d'image, donc une scène se re-rend à l'identique, n'a jamais besoin d'un compte pré-rempli ou d'un environnement réel, et est mise à jour en éditant le code lorsque le produit change.
Constructions de référence (à lire en parallèle de cette compétence ; les deux se construisent sans les dépôts produits) :
examples/ds-free-schedule-demo: un flux de 26s (planifier un appel) avec une vignette narrateur. Le cas complet le plus simple. Sonsrc/Demo.tsxest une timeline unique avec chaque image clé du curseur, clic et mouvement de caméra.examples/ds-release-649: une vidéo de notes de version de 80s, huit scènes dans trois applications.templates/ds-release-notesen est la forme réutilisable (livrée en release 650).src/Release.tsxséquence les scènes,src/scenes/contient un fichier par élément de version, etsrc/room/etsrc/ac/sont les kits d'écran.
Quand utiliser ceci vs. Playwright
| Synthétique (cette compétence) | Enregistrement Playwright (/record-demo) |
|---|---|
| Vous pouvez lire le code source de l'application | Vous avez seulement l'application en cours d'exécution |
| Notes de version et visites guidées de fonctionnalités refaites à chaque version | Capture unique d'un comportement ou de données réelles |
| Le flux nécessite des données que vous n'avez pas (un appel complet, d'autres utilisateurs, une vue admin) | Le réel est facile d'accès et correct tel quel |
| Mouvements de caméra, timing de clics exact, re-rendus après un changement de voix | Interactions longues et complexes qui seraient coûteuses à reconstruire |
Blocs de construction (lib/components)
import { Stage, typed, cursorAt, near } from '../../../../lib/components';
<Stage
url="app.example.com/settings" // barre d'adresse ; hôte plus sombre que le chemin
camera={CAMERA} // [{ f, z, x, y }] en pixels du canevas, lissé, limité à la fenêtre
path={PATH} // [{ f, x, y }] images clés du curseur en pixels du canevas
clicks={CLICKS} // images qui écrasent le curseur et déclenchent une ondulation
aiNote // "Les personnes montrées sont générées par IA" (ou passez votre propre texte)
canvasWidth={1440} canvasHeight={810} // les valeurs par défaut
>
<SettingsScreen field={typed(frame, 'Q4 Product Review', 128)} />
</Stage>
Stagedessine le chrome du navigateur autour d'un canevas d'application de taille fixe, applique la caméra, et place le curseur par-dessus à unzIndexde 100. Au zoom 1, la fenêtre estfit(0,8625) de la largeur du cadre. Les images clés répètent une valeur pour la maintenir.CursoretcursorAtfonctionnent seuls pour une scène sans navigateur ;near(cursor, target)pilote les états de survol.- Les kits d'écran spécifiques au produit (thème, icônes, mises en page) restent dans le projet. Ils ne font pas partie de la bibliothèque.
Méthode
1. Extraire une spécification visuelle du dépôt de l'application, avec des sous-agents
Envoyez un sous-agent par application ou domaine, en parallèle, et demandez à chacun de retourner une spécification compacte plutôt que des dumps de fichiers :
- Thème : palette, famille de polices, rayons, espacements, ombres (depuis le fichier de thème ou les variables CSS).
- Chaînes : les chaînes de langue anglaises exactes pour chaque étiquette, élément de menu, dialogue et toast que la scène montre (
public/locales/en/*.json,src/i18n/locales/en/*). Ne paraphrasez jamais le texte de l'interface utilisateur. - Icônes : si l'application utilise une police d'icônes, son fichier
selection.jsonicomoon/fontello contient les chemins SVG de chaque glyphe. Extrayez ceux dont vous avez besoin dans un petit JSON et rendez-les sous forme de<svg><path d=...>. Sinon, copiez les composants SVG. - Mise en page et comportement : arbre des composants pour les écrans affichés, ordre de la barre d'outils, valeurs par défaut, ce que fait chaque contrôle.
Lisez la branche par défaut depuis le dépôt distant (git show origin/main:chemin, ou git fetch puis lisez origin/<default>). Les checkouts locaux sont souvent obsolètes et vous donneront l'interface du trimestre dernier.
Pour les notes de version, tracez chaque élément jusqu'au commit et au composant qui le livrent en premier. Cela vous donne les chaînes exactes et les valeurs par défaut à afficher.
2. Reconstruire uniquement ce qu'une scène montre, sur un canevas fixe
- Disposez chaque élément avec des positions absolues sur un canevas fixe (1440x810 est une bonne valeur par défaut). Exportez les rectangles importants sous forme de constantes (
SAVE_BTN = { x, y, w, h }), afin que les cibles de clic soient arithmétiques, pas des suppositions. - Construisez seulement ce que la caméra voit. Un panneau qui n'est jamais dans le champ peut être une couleur unie.
- Restez en React statique : pas d'état, pas d'effets. Chaque changement visuel est une fonction de
frame. - Placez le kit dans son propre dossier (
src/ui/,src/room/) avec untheme.tspour les constantes.
3. La voix d'abord, puis placez la timeline en images
- Écrivez une ligne par scène, coupez aux phrases (un
.txtpar phrase), et générez l'audio en premier (voiceover.py --scene-dir ...). Trois prises ettools/pick_takes.pyvalent mieux qu'une seule prise plus--max-wpm: un clone copie le rythme et la platitude de son clip de référence, donc si les lignes sortent précipitées ou monotones, corrigez le clip avectools/voice_prosody.py expandplutôt que d'étirer la sortie (voir "Clone Prosody" dans CLAUDE.md). Lisez les durées mesurées, ettools/captions.pypour les timings des mots. - Dimensionnez chaque scène à sa ligne plus un temps mort, et gardez un commentaire d'en-tête listant le début et la durée de chaque ligne. Ce commentaire est la source de vérité (voir "Audio-Anchored Timelines" dans CLAUDE.md).
- Tout est une constante d'image en haut du fichier de scène : images clés du curseur,
CLICKS, images clés de la caméra, changements d'état de l'interface utilisateur (frame >= C.savemontre le dialogue). Exportez les images de clic pour que le parent puisse placer les effets sonores de clic. - Cherchez les images à partir de la narration, ne les définissez pas manuellement.
voice()(lib/components/cues.ts; le template ds-release-notes en a une copie) prend les phrases de la scène avec leurs.captions.jsonet images de début, etcue('save')retourne l'image où le mot commence. Ancrez chaque action au mot qu'elle illustre, avec un décalage, et chaque image clé du curseur et de la caméra à ces ancres :Régénérez ou re-timing une phrase et les clics, la frappe, le curseur et la caméra la suivent. Un repère dont les mots ne sont plus prononcés échoue le bundle avec les mots qui le sont. Options :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('avant', { offset: -3 }), save: cue('enregistrer') }; const PATH = [{ f: C.field - 6, ...field }, { f: C.save - 24, ...saveBtn }];offset,edge: 'end',after(une image, pour un mot répété),nth,in(une phrase). Le parent place l'audio depuisLINK_VOICE.lines, donc l'image de début d'une phrase est écrite une fois.templates/ds-release-notes/src/scenes/FreeCustomLink.tsxest l'exemple travaillé.
4. Diviser les lignes de liste denses à leurs pauses
Une ligne qui liste plusieurs choses ("planification, contacts, enregistrements et transcriptions") est trop rapide pour une seule prise. Divisez l'audio à ses pauses en fichiers de phrase (07a, 07b, ...), donnez à chaque phrase sa propre prise, et placez chaque phrase sur son propre temps mort. ds-release-649's moreVo fait cela.
5. Mesurer les cibles de clic à partir d'une planche contact avant le rendu complet
Une coordonnée devinée est généralement décalée de 20 à 50 px une fois que la caméra zoome. Effectuez un rendu d'images fixes à chaque image de clic et quelques images avant, juxtaposez-les et vérifiez que la pointe du curseur se trouve sur la cible :
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
Corrigez les constantes, re-rendez les images fixes, et seulement ensuite effectuez le rendu de la vidéo.
6. Étiqueter les personnes générées par IA à l'écran
Toute personne qui n'est pas réelle (portraits FLUX.2 dans les vignettes, avatars, narrateur) reçoit une étiquette visible : aiNote sur le Stage, ou un badge "IA" sur une vignette de narrateur. Les noms, contacts et données d'appel sont également inventés, donc gardez-les clairement fictifs.
7. Rendre, puis normaliser
tools/qc.py --project ... avant le rendu, puis qc.py --render out/video.mp4 --fix pour -14 LUFS. qc.py --project ... --layout vérifie le propre texte de la vidéo (sous-titres, cartes, note IA) pour la taille et le contraste, et liste séparément celui de l'interface utilisateur du produit. Stage marque sa fenêtre data-qc="product" ; mettez le même attribut sur toute maquette de téléphone ou tablette que vous construisez en dehors, ou son texte d'interface utilisateur sera jugé comme celui de la vidéo.
Pièges
- Les logos SVG monochromes rendent en noir. Un
logo_mono.svga généralement des remplissages noirs ; sur un fond sombre, utilisezfilter: brightness(0) invert(1). - Branches locales obsolètes. Lisez
origin/<default>, pas la branche sur laquelle le checkout local se trouve. rsync --exclude srcsupprime aussinode_modules/*/src. Lors de la copie d'un projet ou kit, ancrez les exclusions à la racine de transfert avec une barre oblique :--exclude /src.- La caméra se limite. Un centre de caméra près du bord du canevas est tiré vers l'intérieur pour que le bord de la fenêtre ne dépasse jamais du cadre. Si un zoom atterrit quelque part d'inattendu, c'en est la raison ; visez un point plus à l'intérieur.
- Les menus et dialogues doivent se trouver à l'intérieur du canevas. Le canevas coupe ses enfants. Positionnez un menu pour qu'il s'ouvre vers l'intérieur, comme le fait l'application réelle.
- Les états de survol viennent du chemin du curseur. Utilisez
near(cursorAt(frame, PATH), target)plutôt qu'un second ensemble de constantes d'image, de sorte que déplacer une image clé déplace le survol avec elle. - Une ligne vocale modifiée déplace tout ce qui la suit. Dans une scène avec repères, les clics, le curseur et la caméra suivent d'eux-mêmes, mais la longueur de la scène et les
atdes phrases ultérieures doivent encore être vérifiés. Dans une scène réglée manuellement, re-mesurez chaque image. Dans tous les cas, mettez à jour le commentaire d'en-tête et les longueurs de scène, puis revérifiez les images fixes de clic. Si un narrateur lip-sync est piloté par la piste vocale assemblée, régénérez-le aussi. - Un kit survit à sa démo. Un kit d'écran reconstruit peut héberger d'autres vidéos : le
ds-release-notesroom est devenu la scène d'une édition de marque d'un court métrage parodique, avec des visages animés dans ses vignettes et aucun nouveau travail d'écran. Copiez le kit dans le nouveau projet et mettez à l'échelle son canevas logique au cadre (.claude/skills/parody-short/formats.md).