Vidéo de démonstration
Enregistre l'application réelle (jamais une maquette ou une ancienne vidéo) dans un navigateur headless frais, la commente avec Edge TTS, grave les sous-titres et génère un MP4. Une démo se compose de deux fichiers écrits pour son processus : narration.json et flow.cjs ; tout le reste est géré par la commande demo-video-kit dans ce dossier. L'exemple concret se trouve dans examples/todomvc/.
<kit> ci-dessous signifie node <ce dossier>/bin/demo-video-kit.js (ce dossier est, par exemple, ~/.claude/skills/demo-video) ; lorsque le projet de l'utilisateur a le kit installé, npx demo-video-kit fonctionne de la même manière. <kit> help liste toutes les commandes.
Ce que la requête détermine
| Requête | Action |
|---|---|
| « Crée une démo pour » | L'ensemble du flux ci-dessous, dans demos/<processus>/ du projet de l'utilisateur |
| « Les écrans de ont changé, refais la démo » | Simulation à blanc des nouveaux écrans (étape 2), correction de flow.cjs et, si les mots ne tiennent plus, narration.json ; puis étapes 4 à 8 |
| « Ajoute des boîtes à la vidéo existante de » (sans nouvel enregistrement) | Mesure des boîtes dans l'application en cours, écriture de annotations.json, exécution de <kit> annotate <dossier-demo> (voir Surbrillances) |
| « … sans surbrillance » | Omet toute option highlight ; rien d'autre ne change |
| « Change la voix / la vitesse / le texte » | Modifie narration.json (voice, rate, text des segments), puis étapes 4 à 8 : les durées des clips déterminent le timing à l'écran, donc la vidéo est toujours réenregistrée |
Règles (toujours)
- Réel et frais. Une nouvelle session navigateur ; l'application réelle ; des données réelles. Jamais de résultat, de nombre ou de statut factice ; annoncer le succès uniquement après que l'application l'affiche.
- Sûr. Un environnement local ou de test uniquement : chaque exécution crée réellement ce que le flux crée. Ne jamais imprimer ni stocker les identifiants : ils proviennent de l'environnement (par exemple
DEMO_EMAIL,DEMO_PASSWORD) ; un mot de passe s'affiche uniquement sous forme de points. Demander un compte de test si aucun n'est connu. - Image. 1920x1080, 16:9, 30 ips, H.264 + AAC MP4, enregistré en headless pour que rien ne soit coupé. Un curseur visible qui glisse vers chaque cible et un anneau de clic léger ; pas de zoom, pas de mouvement de caméra, pas de transitions fantaisistes. Avant d'agir, faire défiler l'élément pour qu'il soit entièrement visible ; lire les pages longues de haut en bas, assez lentement pour être lisible.
- Son. Edge TTS, voix par défaut
en-IN-NeerjaNeural(liste des autres avec<kit> voices en-ou un autre filtre). Un clip par segment de narration ; les clips ne se chevauchent jamais ; la narration décrit uniquement ce qui est à l'écran ou sur le point de se produire dans cette scène. - Pas d'intervalle de plus de 7 secondes. Aucun silence sans narration ne peut atteindre 7 secondes. Lorsque l'enregistreur émet un avertissement, ou que
verify.pyéchoue, allonger ou diviser la narration de ce segment, ou accélérer ses actions, et réenregistrer. - Rythme. Pas de précipitation : chaque valeur saisie doit être visible avant de passer à la suite.
- Suivre l'application. Déduire les routes, libellés et boutons de l'application en cours (simulation à blanc), jamais de mémoire. Nommer les boutons comme le fait l'application.
- Les sorties restent hors de git. Les fichiers audio générés, les builds et les MP4 ne sont pas commités sauf demande explicite.
- Surbrillances. Chaque démo marque ce que la narration désigne avec une boîte rouge, sauf si la requête mentionne « sans surbrillance ».
Surbrillances
Une boîte rouge (4 px, #E11D48, 6 px dégagés de l'élément) est dessinée en direct dans la page pendant l'enregistrement, de sorte qu'elle suit le défilement et fait partie de l'image. Boîte par défaut : chaque élément de menu cliqué pour accéder à l'écran, les boutons qui démarrent le processus, chaque bloc de données saisies sur une page de révision, et le bouton de soumission final. Ne pas encadrer chaque champ saisi : le curseur et la frappe le montrent déjà.
API dans flow.cjs :
goto / click / type / pick (loc, ..., { highlight }):highlight: trueencadre l'élément vers lequel le curseur se dirige ;highlight: <locator>encadre un autre élément à la place (par exemple toute sa ligne ou son bloc).- Une boîte reste jusqu'à ce que le curseur se déplace vers la cible suivante.
{ keep: true }conserve également les boîtes actuelles ;k.clearHighlights()les supprime ;k.highlight(loc)en ajoute une sans déplacer le curseur. - Chaque boîte ajoute une entrée
highlightàmarksdansbuild/timeline-raw.json.
Pour une vidéo déjà enregistrée sans boîtes, <kit> annotate <dossier-demo> les dessine après coup à partir de annotations.json. C'est une solution de repli ; réenregistrer est plus simple et plus précis.
Flux de travail
- Configuration. Exécuter
<kit> doctor(voir Configuration) et vérifier que l'application répond à son URL. - Simulation à blanc. Avec les outils navigateur de Playwright, parcourir le flux réel sans enregistrement et sans la soumission finale : noter la route de chaque écran, les libellés et les noms
getByRole, les options de listes déroulantes, les boîtes de dialogue, le bouton final et ce qui se passe après. - Écrire la démo :
<kit> init <dossier-demo> --url <url app>crée un starter qui fonctionne déjà ; ou copierexamples/todomvc/. Puis éditer :narration.json: titre, voix, débit,pronounce(écrit → parlé), et segments{id, scène, texte}. Diviser une scène en plusieurs segments si ses actions prennent du temps, de sorte que la narration de chaque segment dure environ aussi longtemps que ses actions.flow.cjs:runDemo(__dirname, async (k) => { ... })avec unk.segment(id, async (at, clip) => { ... })par segment de narration. Utiliserk.click / k.type / k.pick / k.goto / k.scrollBy;at(secondes)attend un point dans le clip (par exempleclip.cues[0].end). Attendre les éléments, jamais « network idle ». Retourner ce qui a été vérifié. Conserverrequire(process.env.DEMO_KIT || 'demo-video-kit'): la commande définitDEMO_KIT.
- Réaliser.
<kit> run <dossier-demo>(avecDEMO_BASE_URLet les identifiants éventuels dans l'environnement) exécute l'audio, l'enregistrement, le rendu et la vérification. Chaque ligneWARNINGde l'enregistreur, et chaque ligneFAILde la vérification, enfreint la règle 5 : corriger et relancer. Après avoir modifié uniquementflow.cjs,--from recordsaute l'audio ; après avoir modifié uniquement les sous-titres ou l'encodage,--from rendersaute l'enregistrement. - Regarder.
build/check/contact-sheet.png: chaque image montre l'écran complet, le curseur, la bonne étape, des sous-titres lisibles, et des boîtes sur les éléments que la narration nomme. La vidéo est<dossier-demo>/<nom du dossier>.mp4. - Transmettre. Signaler le chemin du MP4, sa durée, son format, l'intervalle maximal, ce que l'exécution a créé, et tout ce qui diffère de la requête parce que l'application fonctionne différemment.
Configuration
Node 18+ et Google Chrome (ou Microsoft Edge : l'enregistreur l'utilise en repli, ou définir DEMO_BROWSER=msedge). Exécuter npm install une fois dans ce dossier ; les voix (msedge-tts), ffmpeg (ffmpeg-static) et playwright-core sont inclus, et l'enregistreur récupère le petit ffmpeg propre à Playwright lors de sa première exécution. <kit> doctor vérifie tout cela, y compris l'accès Internet, nécessaire aux voix.
Pièges connus
| Piège | Éviter |
|---|---|
| Bande grise, page coupée en bas | Ne jamais enregistrer dans une fenêtre de navigateur visible ; demo-kit.cjs enregistre en headless à 1920x1080 |
waitForLoadState('networkidle') bloque sur les applications qui interrogent régulièrement | Attendre un élément à la place |
| Une boîte de dialogue interface headless signalée comme cachée | Attendre un champ à l'intérieur, pas la boîte de dialogue |
| Échapper dans une liste déroulante ferme toute la boîte de dialogue | Choisir une option au lieu d'appuyer sur Échap |
| Un silence prolongé pendant la saisie de nombreux champs | Diviser la narration de ce segment ; type() prend { delay } (par défaut 32 ms) |
| Libellés affichés en majuscules par CSS | Correspondre au texte réel, pas à la casse affichée |
| Une boîte autour d'un libellé uniquement, pas de son bloc | Encadrer le parent du libellé (xpath=..) ou le grand-parent (xpath=../..) |