splitbrain/ndemo

Créez des vidéos de démonstration commentées d'applications web via capture d'écran.

Qu'est-ce que ndemo ?

`splitbrain/ndemo` est un outil de création de vidéos démo commentées pour applications web. Il automatise l'enregistrement d'écran en combinant des interactions utilisateur simulées via Playwright avec une narration audio générée par TTS (OpenAI ou ElevenLabs). L'outil fonctionne à partir de « playbooks » (fichiers YAML) qui décrivent chaque étape de la démo : URL, état de départ, segments avec narration et actions. Il supporte un cycle de travail itératif : ouverture du navigateur, rédaction des segments via l'arbre d'accessibilité, test avec rejeu, et rendu final en MP4. Il est particulièrement utile pour les développeurs qui doivent documenter rapidement une fonctionnalité sans montage vidéo manuel. Compatible avec Claude Code, Cursor, Codex et tout agent capable d'exécuter des commandes shell. Le paramétrage initial est simple : build du CLI, installation de Chromium, et vérification via `ndemo doctor`. L'outil gère automatiquement la synchronisation entre narration et actions, ainsi que les conditions de réalisation pour des interactions fiables.

Compatible avec✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/splitbrain/ndemo/tree/main

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

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

Documentation

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 :

CommandeAction
${CLAUDE_SKILL_DIR}/ndemo open <playbook>Lance le démon navigateur, accède à l'application
${CLAUDE_SKILL_DIR}/ndemo closeArrête le démon navigateur
${CLAUDE_SKILL_DIR}/ndemo resetRevient à l'URL de l'application (état frais)
${CLAUDE_SKILL_DIR}/ndemo page-stateAffiche l'arbre d'accessibilité de la page actuelle
${CLAUDE_SKILL_DIR}/ndemo page-state --screenshotIdem + 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> --audioJoue 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 doctorVé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écutent
  • parallel — 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 ditCe 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 visibles
  • hidden: "<sélecteur>" — l'étape s'exécute uniquement si le sélecteur ne correspond à aucun élément visible
  • url: "<motif>" — l'étape s'exécute uniquement si l'URL actuelle correspond (** = chemin quelconque)

Référence des types d'actions

TypeChamps requisRemarques
clicktarget
typetarget, textdelay optionnel en ms entre les frappes (défaut 80)
hovertarget
scrolltargetFait défiler l'élément dans la vue
waitduration (ms)Pause pour le spectateur (défaut 1000ms)
presskeyTouche du clavier, ex. "Enter", "Escape"
selecttarget, optionSé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 close puis open à nouveau. Consultez .ndemo/daemon.log pour la sortie du démon navigateur.

Skills associés