Que fait ndemo ?
Créez des vidéos de démonstration commentées d'applications web via capture d'écran.
Configuration
Lors de la première utilisation, vous DEVEZ construire la boîte à outils avant d'exécuter une commande.
Vérifiez si ${CLAUDE_SKILL_DIR}/dist/cli.js existe. Sinon, exécutez :
cd ${CLAUDE_SKILL_DIR} && npm install && npm run build
Installez ensuite le navigateur Playwright si nécessaire :
cd ${CLAUDE_SKILL_DIR} && npx playwright install chromium
Vérifiez la configuration :
${CLAUDE_SKILL_DIR}/ndemo doctor
Commandes
Toutes les commandes s'exécutent via ${CLAUDE_SKILL_DIR}/ndemo :
| Commande | Action |
|---|---|
${CLAUDE_SKILL_DIR}/ndemo open <playbook> | Lance le démon navigateur, accède à l'application |
${CLAUDE_SKILL_DIR}/ndemo close | Arrête le démon navigateur |
${CLAUDE_SKILL_DIR}/ndemo reset | Revient à l'URL de l'application (état frais) |
${CLAUDE_SKILL_DIR}/ndemo page-state | Affiche l'arbre d'accessibilité de la page actuelle |
${CLAUDE_SKILL_DIR}/ndemo page-state --screenshot | Idem + enregistre une capture d'écran dans .ndemo/screenshot.png |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> | Joue tous les segments |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --segment <id> | Joue un segment (rembobine d'abord) |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --from <id> | Joue du segment jusqu'à la fin |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --to <id> | S'arrête après ce segment |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --audio | Joue avec la narration audio TTS |
${CLAUDE_SKILL_DIR}/ndemo render <playbook> | Pipeline complet : TTS → rejeu → fusion → mp4 |
${CLAUDE_SKILL_DIR}/ndemo render <playbook> --output <chemin> | Rendu vers un fichier de sortie spécifique |
${CLAUDE_SKILL_DIR}/ndemo doctor | Vérifie les dépendances |
Flux de travail
Étape 1 — Créer le playbook
Chaque playbook vit dans son propre répertoire sous demo/ dans le
projet de l'utilisateur. Le nom du répertoire correspond au nom du playbook.
demo/
ma-fonctionnalite/
ma-fonctionnalite.yaml ← playbook
fixtures/ ← fichiers à restaurer pendant la configuration
audio/ ← fichiers TTS générés (automatique)
video-raw/ ← enregistrement brut (automatique)
demo.mp4 ← sortie finale (automatique)
Avant d'écrire le playbook, réfléchissez à l'état dans lequel l'application doit être pour que la démo fonctionne de manière fiable et reproductible :
- L'utilisateur doit-il être connecté ? → ajoutez des étapes de connexion conditionnelles
- La démo modifie-t-elle des fichiers/données qui doivent être restaurés ? → copiez
les originaux dans le répertoire
fixtures/du playbook pour que la configuration puisse les restaurer - La démo dépend-elle de contenu spécifique existant ? → créez-le dans les fixtures ou via des commandes shell de configuration
Copiez tous les fichiers qui seront modifiés pendant la démo dans le
sous-répertoire fixtures/ du répertoire du playbook. Les étapes de configuration
les recopieront avant chaque exécution pour que la démo démarre toujours
dans un état propre.
Créez le répertoire et le fichier YAML. Le schéma complet du playbook prend en charge ces sections de premier niveau :
# demo/edit-page/edit-page.yaml
app:
url: http://localhost:8080/wiki
viewport: # optionnel, défaut 1920x1080
width: 1920
height: 1080
scale: 2 # facteur d'échelle de l'appareil (défaut 2)
zoom: 1.25 # niveau de zoom CSS (défaut 1.25)
colorScheme: light # "light" ou "dark" (défaut "light")
setup:
# Restaure les fichiers modifiés pendant la démo
- run: cp demo/edit-page/fixtures/page.txt data/pages/page.txt
# Nettoie les artefacts des exécutions précédentes
- run: rm -f data/cache/*.tmp
# Connexion si nécessaire (conditionnelle — ignorée si déjà connecté)
- type: click
target: { role: link, name: "Connexion" }
if:
hidden: ".user-info"
- type: type
target: { role: textbox, name: "Nom d'utilisateur" }
text: admin
if:
visible: ".login-form"
- type: type
target: { role: textbox, name: "Mot de passe" }
text: password
if:
visible: ".login-form"
- type: click
target: { role: button, name: "Se connecter" }
if:
visible: ".login-form"
done:
visible: ".user-info"
titleCard: # carte de titre optionnelle (affichée comme première image)
title: "Modifier une page Wiki" # affichée en évidence
subtitle: "Un tour rapide de l'éditeur" # sous-titre optionnel
duration: 3000 # millisecondes d'affichage (défaut 3000)
tts: # configuration TTS optionnelle
provider: openai # "openai" (défaut) ou "elevenlabs"
voice: alloy # nom de la voix TTS (défaut "alloy")
speed: 1.0 # multiplicateur de vitesse de parole (défaut 1.0)
recording: # paramètres d'enregistrement optionnels
outputDir: "." # répertoire de sortie relatif au playbook (défaut ".")
fps: 30 # fréquence d'images vidéo (défaut 30)
segments:
- id: intro
narration: "Bienvenue sur notre wiki. Modifions une page."
intent: "afficher la page d'accueil du wiki"
timing: after # "after" (défaut) ou "parallel"
actions:
- type: wait
duration: 2000
- id: open-editor
narration: "Cliquez sur le bouton Modifier pour ouvrir l'éditeur."
intent: "cliquer sur le bouton Modifier"
actions: []
Le timing des segments contrôle le moment où les actions s'exécutent par rapport à la narration :
after(défaut) — la narration est jouée d'abord, puis les actions s'exécutentparallel— les actions s'exécutent pendant que la narration est jouée
Écrivez d'abord tous les segments avec la narration et l'intention. Laissez les actions sous forme de tableaux vides. Utilisez des chemins absolus lorsque vous passez les chemins des playbooks aux commandes ndemo.
Étape 2 — Ouvrir le navigateur
${CLAUDE_SKILL_DIR}/ndemo open /chemin/absolu/vers/demo/ma-demo/ma-demo.yaml
Étape 3 — Autoriser chaque segment
Pour chaque segment avec des actions vides :
a) Lisez l'état actuel de la page :
${CLAUDE_SKILL_DIR}/ndemo page-state
b) Examinez la sortie de l'arbre d'accessibilité. Trouvez les éléments référencés dans l'intention du segment. Écrivez les actions dans le YAML du playbook en utilisant les éléments de l'arbre.
Comment écrire des cibles — utilisez les informations de la sortie page-state :
Si page-state affiche [button "Paramètres"] :
target: { role: button, name: "Paramètres" }
Si page-state affiche [searchbox "Rechercher des rapports" value=""] :
target: { role: searchbox, name: "Rechercher des rapports" }
Autres champs de cible — utilisez celui qui identifie le mieux l'élément :
target: { selector: "#mon-element" } # sélecteur CSS
target: { testId: "submit-btn" } # attribut data-testid
target: { label: "Email" } # aria-label
target: { placeholder: "Rechercher..." } # texte d'espace réservé
target: { text: "Cliquez ici" } # contenu textuel visible
Au moins un champ de cible est requis. Plusieurs champs peuvent être combinés pour affiner la correspondance.
Astuce : Le code source de l'application web est disponible dans le dépôt du projet.
Regardez le source du composant pour les attributs data-testid,
les noms de classe ou les ID lorsque l'arbre d'accessibilité ne suffit pas.
Comment écrire des conditions de réalisation :
done:
visible: ".settings-panel" # l'élément apparaît
hidden: ".loading-spinner" # l'élément disparaît
networkIdle: true # aucune requête en attente
stable: 500 # DOM inchangé pendant 500ms
url: "**/settings" # URL modifiée
text: # l'élément contient du texte
selector: ".results"
has: "Q3 Revenue"
attribute: # l'élément a un attribut
selector: html
name: data-theme
value: dark
timeout: 10000 # remplacer le délai d'attente par défaut (ms)
Chaque action qui modifie la page DOIT avoir une condition de réalisation. Sans cela, l'action suivante peut s'exécuter avant que la page ne soit prête.
Ajoutez des actions wait après les modifications visibles pour que le spectateur puisse voir
ce qui s'est passé :
- type: wait
duration: 2000 # 2 secondes
c) Testez le segment :
${CLAUDE_SKILL_DIR}/ndemo play /chemin/absolu/vers/demo/ma-demo/ma-demo.yaml --segment <id>
d) Si cela échoue, exécutez ${CLAUDE_SKILL_DIR}/ndemo page-state pour voir
ce qui est à l'écran maintenant, ajustez les actions et réessayez.
e) Une fois le segment opérationnel, lisez à nouveau l'état de la page avant d'écrire le segment suivant — la page a changé.
Étape 4 — Révision
${CLAUDE_SKILL_DIR}/ndemo play /chemin/absolu/vers/demo/ma-demo/ma-demo.yaml
Regardez la séquence complète dans le navigateur. Demandez à l'utilisateur si tout semble correct.
Pour réviser avec la narration TTS (nécessite OPENAI_API_KEY et ffplay) :
${CLAUDE_SKILL_DIR}/ndemo play /chemin/absolu/vers/demo/ma-demo/ma-demo.yaml --audio
Cela génère l'audio TTS pour chaque segment (mis en cache par hash de contenu), joue l'audio en parallèle des actions du navigateur et ajuste le timing pour que les actions correspondent à la durée de la narration. Si le texte de narration change, l'audio est automatiquement regénéré et l'ancien fichier est supprimé.
Étape 5 — Itérer
L'utilisateur peut demander des modifications. Cas courants :
| L'utilisateur dit | Ce qu'il faut faire |
|---|---|
| "Mauvais bouton" | Exécutez page-state, trouvez le bon élément, mettez à jour la cible |
| "Trop rapide" / "Trop lent" | Ajustez les durées d'attente, rejouez avec --audio pour vérifier le timing |
| "Changez la narration en ..." | Modifiez le champ narration |
| "Ajoutez une étape montrant X" | Insérez un nouveau segment, écrivez ses actions |
| "Supprimez cette étape" | Supprimez le segment du YAML |
| "Rejouez à partir du segment X" | ${CLAUDE_SKILL_DIR}/ndemo play --from <id> |
| "Recommencez" | ${CLAUDE_SKILL_DIR}/ndemo reset puis rejouez |
Après chaque modification, rejouez le(s) segment(s) concerné(s) pour vérifier.
Étape 6 — Rendu
Lorsque l'utilisateur approuve :
${CLAUDE_SKILL_DIR}/ndemo render /chemin/absolu/vers/demo/ma-demo/ma-demo.yaml
Cela produit le mp4 final avec la narration TTS.
Référence des étapes de configuration
Les étapes de configuration s'exécutent avant le début de la démo (sur open, reset, play
et render). Il peut s'agir de commandes shell ou d'actions de navigateur.
Les commandes shell s'exécutent relativement au répertoire de travail du projet
(le répertoire à partir duquel ndemo a été appelé). Tous les chemins dans les commandes run
doivent être relatifs à la racine du projet.
Commandes shell — utilisez run pour les opérations sur fichiers :
setup:
- run: cp demo/ma-fonctionnalite/fixtures/original.txt data/page.txt
- run: rm -f data/cache/*.tmp
- run: ./scripts/reset-db.sh
Actions de navigateur — même syntaxe que les actions de segment :
setup:
- type: click
target: { role: button, name: "Connexion" }
- type: type
target: { role: textbox, name: "Nom d'utilisateur" }
text: admin
Étapes conditionnelles — ajoutez if pour ignorer si la condition n'est pas remplie :
setup:
# Se connecter uniquement si pas déjà connecté
- type: click
target: { role: link, name: "Se connecter" }
if:
hidden: ".user-menu" # ignorer si le menu utilisateur est visible
# Exécuter uniquement sur une page spécifique
- type: click
target: { role: button, name: "Réinitialiser" }
if:
url: "**/admin/settings" # ignorer si pas sur cette page
Champs de condition :
visible: "<sélecteur>"— l'étape s'exécute uniquement si le sélecteur correspond à des éléments visibleshidden: "<sélecteur>"— l'étape s'exécute uniquement si le sélecteur ne correspond à aucun élément visibleurl: "<motif>"— l'étape s'exécute uniquement si l'URL actuelle correspond (**= chemin quelconque)
Référence des types d'actions
| Type | Champs requis | Remarques |
|---|---|---|
| click | target | |
| type | target, text | delay optionnel en ms entre les frappes (défaut 80) |
| hover | target | |
| scroll | target | Fait défiler l'élément dans la vue |
| wait | duration (ms) | Pause pour le spectateur (défaut 1000ms) |
| press | key | Touche du clavier, ex. "Enter", "Escape" |
| select | target, option | Sélection de liste déroulante |
Règles pour les identifiants de segment
- Alphanumériques en minuscules avec des traits d'union :
open-settings,toggle-dark - Doivent être uniques dans le playbook
- Utilisés comme noms de fichiers pour l'audio/vidéo, donc gardez-les courts
Dépannage
- Le navigateur ne répond pas :
${CLAUDE_SKILL_DIR}/ndemo closepuisopenà nouveau. Consultez.ndemo/daemon.logpour la sortie du démon navigateur.