Communitygithub.com

lyonsv/demo-video-skill

Enregistre des vidéos de démonstration scriptées et soignées d'une application web (présentations de produits, animations d'architecture avant/après, segments de fonctionnalités) au format MP4 1440×900, avec un format maison cohérent : cartes-titres blanches, légendes en pilule, ondulations de clic, attentes accélérées, en pilotant Chrome headless avec Playwright et en encodant avec ffmpeg.

Qu'est-ce que demo-video-skill ?

Cette compétence génère des vidéos de démonstration professionnelles et reproductibles pour des applications web. Elle pilote un navigateur Chrome headless via Playwright pour enregistrer des parcours scriptés, puis les assemble et les encode avec ffmpeg. Le format est standardisé (1440×900, 30 fps, H.264) avec des cartes-titres, des légendes en bas de l'écran, des effets de clic et des accélérations des temps d'attente. Elle est idéale pour créer des présentations de fonctionnalités, des démonstrations de workflows ou des animations d'architecture, et peut être utilisée avec des outils comme Claude Code ou Cursor.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/lyonsv/demo-video-skill/tree/HEAD

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Vidéos de démonstration

Parcours scriptés enregistrés à partir de l'application réelle en cours d'exécution. Le spectateur voit le produit effectuer un travail réel ; les parties lentes (réponses du modèle, tâches) sont accélérées, et chaque temps fort comporte une courte légende indiquant ce qu'il faut remarquer. Le même format est utilisé à chaque fois, de sorte que les segments puissent être assemblés en une seule vidéo.

Le format maison

Respectez ces spécifications exactes, pour que les nouveaux segments correspondent aux précédents :

  • Vidéo : 1440×900, 30ips, H.264 (libx264 -preset slow -crf 18), yuv420p, +faststart, pas d'audio.
  • Cartes de titre et de fin : écran blanc plein, centré : une petite ligne de marque grise en majuscules facultative (définir brand dans kit.start ; aucune par défaut), titre en gras 46px, sous-titre gris 20px. Tenir environ 2,8s, puis fondu. Les segments s'ouvrent avec une carte de titre ; le dernier se termine par une carte de conclusion d'une ligne (par exemple "Idée → évaluation → test en direct → déploiement" / "Aucun déploiement d'application entre-temps").
  • Légendes : pilule sombre (rgba(17,17,17,.88)), Lato semi-gras 17px blanc, centrée en bas. Une idée par légende, environ 60 caractères ou moins, casse de phrase, pas de point final, temps présent, rédigée du point de vue du spectateur ("Les résultats s'ouvrent sous forme d'artefact à côté du chat"). Changer la légende lorsque le temps fort change ; maintenir chaque légende environ 2,5 à 4 s.
  • Clics : une ondulation violette (rgba(122,71,255,…)) apparaît sur la cible juste avant chaque clic.
  • Saisie : visible, touche par touche (environ 25 ms par touche ; 14 ms pour les textes longs).
  • Attentes : tout ce qui est lent s'exécute dans ff(), ce qui le lit en environ 2 s. Tout le reste est en temps réel.
  • Scènes d'architecture : une page blanche fixe de 1440×900 avec des scènes (titre → avant → problème mis en évidence → après → comptage des métriques → liste des fonctionnalités), Lato, encre #111, accent violet #6f43d6 ; les étiquettes "avant" sont grises, les étiquettes "après" sont violettes. assets/architecture-example.html est la référence : réutiliser son CSS et sa structure et modifier le contenu.

Où les vidéos sont stockées : le dépôt demos

Chaque vidéo a un dossier dans le dépôt demos de l'équipe (par défaut ~/src/demos, ou $DEMOS_REPO), contenant ses scripts d'enregistrement et un manifeste demo.json. Les dépôts de produits restent exempts de code de démonstration. Les enregistrements ne sont jamais commités ; le output du manifeste indique où va le MP4.

~/src/demos/
  package.json            playwright-core + ffmpeg-static (npm install une fois)
  <video>/
    demo.json             manifeste
    architecture.html     segment "scènes" (facultatif)
    record-<part>.js      segments "walkthrough", écrits sur demo-kit
    .build/               MP4 et images par segment (gitignored)

Manifeste (demo.json) :

{
  "name": "<video-name>",
  "title": "Une ligne : ce que montre la vidéo",
  "description": "À qui elle s'adresse et le point qu'elle fait",
  "owner": "email",
  "repos": ["dépôts où vivent les fonctionnalités enregistrées, avec branches"],
  "needs": [{ "what": "service", "check": "URL qui répond 2xx/3xx quand il est opérationnel", "start": "comment le démarrer" }],
  "reset": "comment obtenir une prise propre, et ce que l'enregistrement laisse derrière lui",
  "segments": [
    { "name": "architecture", "kind": "scenes", "page": "architecture.html", "timeline": "0:3000,1:4500" },
    { "name": "walkthrough", "kind": "walkthrough", "script": "record-walkthrough.js" },
    { "name": "intro", "kind": "video", "file": "~/path/to/existing.mp4" }
  ],
  "output": "~/Downloads/<name>.mp4"
}

Le dossier examples/sift/ de cette compétence est un dossier de référence : une vidéo à deux segments (une page de scènes et une vraie walkthrough) d'une petite application exemple qui se trouve dans le même dossier.

Si le dépôt demos n'est pas encore cloné, le créer (git init, le package.json ci-dessus, et un .gitignore couvrant node_modules/, .build/, *.mp4, *.jpg, *.png, *-frames/ et *-timeline.json) et indiquer à l'utilisateur qu'il n'a pas encore de remote.

Outils (dans scripts/, relatif à cette compétence)

ScriptCe qu'il fait
find-demos.jsnode find-demos.js [root…] [--json] : liste chaque vidéo du dépôt demos, avec segments, dépôts, sortie, et date du dernier enregistrement et commit.
record.jsnode record.js <video-dir> [--only <segment>] : vérifie needs, enregistre et encode chaque segment dans .build/, les assemble dans output (l'ancienne vidéo est conservée sous *.previous.mp4). L'exécuter depuis la racine du dépôt demos.
demo-kit.jsBibliothèque pour les scripts de walkthrough : kit.start() donne page, title/untitle, caption, tap, type, ff, scroll/scrollTop, ripple, navigate, startCast, finish. Les images sont diffusées vers le disque.
encode.jsnode encode.js <frames-dir> <timeline.json> <out.mp4> : construit le MP4, en compressant les fenêtres ff().
record-scenes.jsnode record-scenes.js <page.html> <out.mp4> "0:3000,1:4500,…" : enregistre une page de scènes qui définit window.step(n).
concat.jsnode concat.js <out.mp4> <a.mp4> <b.mp4> … : assemble les segments, en ré-encodant au format maison.
review.jsnode review.js <video.mp4> <out-dir> [stills] [at=s,s,…] : planches contact 2×2 d'images fixes pour relecture.

assets/example-walkthrough.js est un exemple complet de travail sur le kit (et examples/sift/record-walkthrough.js en est un vrai). Lisez-le avant d'écrire une nouvelle walkthrough ; il montre la structure, y compris le changement d'application en cours de vidéo, les tâches accélérées et une carte de fin.

Flux de travail

0. Trouver ce qui existe déjà

Exécutez find-demos.js d'abord. Si l'utilisateur souhaite ré-enregistrer ou étendre une vidéo qui existe déjà, travaillez dans ce dossier : ré-enregistrez avec record.js <folder> (ou --only <segment> après un changement d'interface), ou ajoutez un segment à son manifeste. Ne créez pas un deuxième dossier pour la même vidéo. Créez un nouveau dossier uniquement pour une vidéo vraiment nouvelle, nommée d'après elle (kebab-case).

1. Storyboard d'abord

Avant tout code, écrivez le storyboard sous forme de liste numérotée de sections, chacune avec ses temps forts : ce qui se passe à l'écran et la légende. Décidez à l'avance :

  • Ce qui doit être réel, et ce qui peut être simulé. Privilégiez le réel (vrais appels de modèle, vraies recherches). Si les données doivent être simulées (par exemple, des semaines de trafic en direct), dites-le à l'écran, à la fois dans une légende et dans l'application elle-même si elle peut l'étiqueter. Les spectateurs prennent une démo au pied de la lettre, donc un résultat simulé présenté comme réel est trompeur.
  • Ce qui ne doit pas être cliqué. Tout ce qui écrit dans des environnements partagés (enregistrement de tableaux, envoi d'e-mails, achats). Montrez le bouton ; ne le pressez pas.
  • Durée : environ 60 à 90 s par segment. Une vidéo assemblée de 3 minutes convient ; chaque segment doit faire un point.

Si l'objectif de l'utilisateur est clair d'après la conversation, allez-y et partagez le storyboard dans votre mise à jour au fur et à mesure. Ne demandez que lorsque le but de la vidéo n'est pas clair.

2. Configurer le dossier de la vidéo

Créez <demos repo>/<video>/ avec un demo.json (remplissez needs avec une URL check pour chaque service que la vidéo touche, et notez reset). Exécutez npm install à la racine du dépôt demos si node_modules est manquant. Le kit et les scripts résolvent playwright-core et ffmpeg-static depuis le répertoire de travail, donc exécutez tout depuis la racine du dépôt. Chrome doit être installé (channel: 'chrome' ; aucun téléchargement de navigateur Playwright n'est nécessaire).

3. Pré-vol de l'application

  • Confirmez que chaque service que la vidéo touche est opérationnel (curl chaque URL), et que le HTTPS local fonctionne (le kit ignore les certificats auto-signés).
  • Réinitialisez l'état pour que la prise commence propre (nouvelle base de données, localStorage effacé), et faites en sorte que le script lève immédiatement une exception si l'état est sale. Une prise qui échoue 3 minutes plus tard coûte de vrais appels de modèle et du temps.
  • Chronométrez les endpoints lents que la vidéo interroge. Une requête de résultats de 4 secondes affiche un squelette de chargement à l'écran ; corrigez la requête ou attendez-la.
  • Prenez une capture d'écran de chaque état clé avec un petit script Playwright et regardez-la : mise en page, césure, images cassées, ce qui est au-dessus de la ligne de flottaison à 1440×900. C'est moins coûteux que d'enregistrer.

4. Écrire le script d'enregistrement

Copiez la structure de assets/example-walkthrough.js :

const kit = require(process.env.DEMO_KIT || `${process.env.HOME}/.claude/skills/demo-video/scripts/demo-kit`);
const demo = await kit.start();                             // record.js fournit les chemins des images et de la timeline
await demo.page.goto(URL, { waitUntil: 'networkidle' });
await demo.title('Nom de la fonctionnalité', 'Ce qu'elle permet de faire');
await demo.startCast();                                     // démarrer après que le titre couvre la page
await demo.sleep(2800); await demo.untitle(); await demo.sleep(900);
await demo.caption('…'); await demo.tap(locator); await demo.type(locator, 'texte');
await demo.ff(() => page.waitForFunction(() => /* réponse terminée */ true, null, { timeout: 180000 }));
await demo.finish();
  • Utilisez des sélecteurs stables (aria-label, ids, data-*) et vérifiez-les par rapport au DOM réel.
  • Attendez les conditions réelles de fin à l'intérieur de ff(), pas des temps de pause fixes, avec des délais d'attente généreux.
  • Lorsqu'une légende parle de quelque chose, faites-la d'abord défiler à l'écran (demo.scroll(px, 'main') pour les applications dont le contenu défile à l'intérieur d'un élément).
  • demo.navigate(url) change de page ou d'application ; il redémarre la screencast sur la nouvelle page.
  • Pour un segment d'architecture, construisez la page de scènes à partir de l'exemple et enregistrez-la avec record-scenes.js. Ajoutez également une navigation par touches fléchées et espace (l'exemple l'a), pour que la même page fonctionne lors d'une présentation en direct.

5. Enregistrer

Depuis la racine du dépôt demos, exécutez la vidéo entière (ou un segment) au premier plan avec un long délai d'attente, en enregistrant la sortie :

set -o pipefail; node ~/.claude/skills/demo-video/scripts/record.js <video> [--only <segment>] 2>&1 | tee <video>/.build/record.log

Ne redirigez pas vers tail : cela cache le code de sortie de l'enregistreur, et une prise échouée ressemble à un succès. Si vous devez le mettre en arrière-plan, attendez le PID réel (while kill -0 $PID; do sleep 5; done) plutôt que de vous fier à une notification « terminé », qui peut arriver avant la fin du processus.

6. Réviser

record.js a déjà encodé et assemblé le tout ; il imprime la commande de révision :

node ~/.claude/skills/demo-video/scripts/review.js <output.mp4> <video>/.build/review 16

Pour examiner un segment, révisez <video>/.build/<segment>.mp4.

Lisez chaque planche contact avant de considérer la vidéo comme terminée. Vérifiez que :

  • chaque légende correspond à ce qui est à l'écran à ce moment-là ;
  • la chose à laquelle une légende fait référence est visible, pas en dessous de la ligne de flottaison ;
  • il n'y a pas d'images cassées ou de texte alternatif, de squelettes bloqués, de toasts d'erreur, d'en-têtes coupés ou tronqués, ou d'interface de débogage ;
  • les attentes accélérées ressemblent à un flou rapide de progression, pas à une image figée ;
  • les cartes de titre et de fin sont présentes.

Corrigez le script ou l'application (par exemple, un espace réservé pour les images qui ne se chargent pas, une requête plus rapide), réinitialisez l'état et ré-enregistrez. Prévoyez deux ou trois prises.

7. Livrer

  • Vérifiez les points d'assemblage : review.js <output> <dir> 0 at=<t-1>,<t+1> à chaque limite de segment.
  • record.js conserve la version précédente sous *.previous.mp4. Ne jamais écraser des vidéos qui ne sont pas le propre output de ce dossier : pour ajouter à la vidéo de quelqu'un d'autre, ajoutez-la comme un segment video d'un nouveau dossier.
  • Dites à l'utilisateur que le dossier vidéo est prêt à être commité dans le dépôt demos. Ne commitez que s'il le demande.
  • Rapportez à l'utilisateur : les chemins de fichiers et les durées, ce que chaque segment montre, ce qui était réel et ce qui était simulé, et tout état laissé par l'enregistrement (services en cours d'exécution, enregistrements créés, tout ce qui est encore activé) avec la marche à suivre pour l'annuler.

Pièges déjà rencontrés

  • Mémoire : une screencast CDP envoie jusqu'à 60 images par seconde pendant une animation. Mettre en mémoire tampon les images pour une prise de plusieurs minutes fait tuer l'enregistreur sans sortie. Le kit diffuse les images vers le disque et les réduit pendant ff() ; gardez-le ainsi.
  • Horloge : les fenêtres ff() et les horodatages des images utilisent tous deux les secondes du mur, donc chronométrez toute l'attente (y compris le polling) à l'intérieur d'un seul appel ff().
  • Travail bloquant (par exemple, execFileSync pour un script de données) à l'intérieur de ff() est acceptable : les images reprennent après et la fenêtre est de toute façon compressée.
  • Ports : vérifiez qu'un port est libre avant de démarrer un service pour la démo ; ne tuez pas des processus que vous n'avez pas démarrés.
  • État résiduel : une démo qui se termine en déployant ou en activant quelque chose le laisse activé. Désactivez-le ensuite ou prévenez l'utilisateur.

Skills associés