piyush140104/screen-record-skills

Compétence Claude Code pour créer des vidéos de démonstration de navigateur scénarisées, avec voix off ElevenLabs, musique, effets sonores de clic et sous-titres incrustés.

Qu'est-ce que screen-record-skills ?

Cette compétence (skill) pour Claude Code permet de générer automatiquement des vidéos soignées de démonstration de sites web, idéales pour LinkedIn, Twitter, des présentations commerciales ou des documents d’intégration. Elle orchestre l’ensemble du processus : navigation headless dans un navigateur Chromium (1920×1080), animation du curseur, synthèse vocale via ElevenLabs, ajout de musique d’ambiance et d’effets sonores de clic, ainsi que l’incrustation de sous-titres aux formats SRT et ASS. Le développeur décrit le scénario (URL, actions du curseur, narration) dans un fichier `scenes` JavaScript, et le moteur exécute la vidéo de bout en bout. La compétence gère également la validation de la clé API ElevenLabs, la sélection de voix, et l’inspection préalable du DOM avec Playwright pour éviter les sélecteurs cassés. En sortie, elle produit un fichier MP4 prêt pour LinkedIn, un WebM, et les fichiers de sous-titres.

Compatible avec✓Claude Code~Codex CLI✓Cursor
npx skills add piyush140104/screen-record-skills

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

/screen-record — Vidéos de démonstration de navigateur scénarisées

Quand l'utiliser

L'utilisateur souhaite une vidéo soignée d'un site web en action — typiquement pour LinkedIn, Twitter, une présentation commerciale, une mise à jour pour investisseurs, ou une documentation d'intégration. Il décrit ce qui doit apparaître à l'écran, ce que fait le curseur, et le contenu approximatif de la narration. Vous écrivez le script ; le moteur se charge de l'animation du curseur, de la synthèse vocale via ElevenLabs, de la musique, des effets sonores de clic et de l'incrustation des sous-titres.

N'utilisez PAS cette compétence pour : des enregistrements réels d'une session en cours (utilisez macOS Screenshot ou QuickTime), des GIFs (utilisez un outil GIF), ou des vidéos nécessitant une véritable voix humaine (vous pouvez toujours rédiger le script, puis lui faire ré-enregistrer).

Comment ça fonctionne

Le moteur se trouve dans engine.mjs. Il exporte runDemo(config) qui :

  1. Génère un clip TTS par réplique avec ElevenLabs.
  2. Génère un fond musical doux et un effet sonore de clic UI avec ffmpeg.
  3. Lance Chromium en mode headless en 1920×1080, enregistre la vidéo.
  4. Exécute votre fonction scenes(helpers) qui pilote le navigateur.
  5. Mixe l'audio (narration forte + musique douce + clics subtils) et grave les sous-titres stylisés ASS.
  6. Retourne les chemins vers les fichiers finaux .mp4, .webm, .srt, .ass.

Flux de travail

Étape 1 — lire la demande de l'utilisateur

Identifiez : URL à enregistrer, parcours (ce que le curseur doit faire), ton (formel/décontracté), durée cible. Si quelque chose manque, posez une question ciblée plutôt que de deviner.

Étape 2 — vérifier la clé API

Cherchez ELEVENLABS_API_KEY dans cet ordre :

  1. $CLAUDE_PLUGIN_ROOT/.env.local (ou le dossier .env.local de la compétence)
  2. Le .env.local du projet actuel
  3. process.env

Si absente, dites à l'utilisateur :

J'ai besoin d'une clé API ElevenLabs. Obtenez-en une gratuite sur https://elevenlabs.io/app/settings/api-keys (10 000 caractères/mois, assez pour ~15 démos). Soit vous la collez dans le chat et je l'enregistre dans le .env.local de la compétence, soit vous l'ajoutez vous-même.

Si une clé est présente mais que le moteur lève ApiKeyDeadError (HTTP 401/402/403), dites à l'utilisateur :

Votre clé ElevenLabs a été rejetée (révoquée, expirée, ou quota épuisé). Remplacez-la sur https://elevenlabs.io/app/settings/api-keys, puis mettez à jour .env.local. Je ne réessayerai pas tant que vous ne l'aurez pas fait.

Ne réessayez PAS silencieusement, ne retombez PAS sur la commande macOS say sauf si l'utilisateur le demande explicitement.

Étape 3 — choisir une voix

Par défaut : Liam (TX3LPaxmHKxFdv7VOQHJ) — américain énergique masculin, moderne. Plus léger que George (britannique) et Sarah (américaine féminine). Autres voix gratuites dans engine.mjs sous FREE_TIER_VOICES. Si l'utilisateur dit « utilise la voix X », trouvez-la dans le catalogue. S'il dit « grave/profonde/légère/chaude/féminine », choisissez en conséquence.

Voix de bibliothèque (Charlotte, Rachel, Adam, etc.) nécessitent un abonnement payant ElevenLabs. Si l'utilisateur en demande une et qu'il est en offre gratuite, vous obtiendrez une erreur HTTP 402 — retombez sur la voix gratuite la plus proche et informez l'utilisateur.

Étape 4 — sonder la page cible (uniquement si nécessaire)

Si vous ne connaissez pas la structure de la page, utilisez Playwright ou curl pour inspecter le DOM et trouver les sélecteurs. Ne devinez pas — les sélecteurs sont importants, les correspondances ambiguës feront expirer l'enregistrement.

Pour une SPA avec contenu dynamique, préférez aria-label, role, ou les sélecteurs de texte plutôt que les noms de classe. Utilisez la sémantique page.locator(s).first() (les helpers du moteur le font déjà).

Étape 5 — écrire un fichier de scène

Créez un fichier dans /tmp/screen-record-<nom>.mjs (ne polluez pas le dépôt de l'utilisateur) qui importe le moteur et appelle runDemo. Modèle :

import { runDemo } from '/Users/<vous>/Desktop/Projects/skill/screen-record-skills/engine.mjs';
// ou, si installé via un lien symbolique :
// import { runDemo } from '/Users/<vous>/.claude/skills/screen-record/engine.mjs';

await runDemo({
  baseUrl: 'http://localhost:3000',
  outputDir: './output/onboarding-demo',
  outputName: 'onboarding',

  splash: {
    label: 'Rendez-vous sur',
    url:   'www.votreapp.com',
    footer: 'VotreApp — parcours d\'intégration',
  },

  cues: [
    { id: 'intro',   text: 'Bienvenue sur VotreApp. Laissez-moi vous guider.' },
    { id: 'signup',  text: 'Appuyez sur S\'inscrire pour créer un compte.' },
    { id: 'form',    text: 'Entrez votre email et un mot de passe.' },
    { id: 'done',    text: 'Et voilà — vous êtes connecté.' },
  ],

  // Optionnel : simulez un backend pour que la démo soit déterministe.
  routes: [
    { match: /\/api\/signup$/, fulfill: { status: 200, body: { ok: true } } },
  ],

  // Optionnel : pré-remplissez un utilisateur authentifié pour sauter la connexion.
  // initialAuth: { id: 'u1', email: '[email protected]', name: 'Utilisateur Démo' },

  scenes: async ({ page, baseUrl, sleep, speak, awaitCue,
                   clickAt, typeInto, moveCursorTo, hideSplash, typeSplashUrl,
                   configSwitch, showSwitch, hideSwitch, setAuth }) => {

    // Scène 0 : splash URL (le moteur a déjà navigué vers baseUrl hors caméra).
    speak('intro');
    await typeSplashUrl('www.votreapp.com');
    await awaitCue('intro');
    await hideSplash();
    await sleep(750); // correspond à la disparition du splash

    // Scène 1 : cliquer sur S'inscrire
    speak('signup');
    await clickAt('button:has-text("S\'inscrire")');
    await awaitCue('signup');

    // Scène 2 : remplir le formulaire
    speak('form');
    await typeInto('input[type="email"]', '[email protected]');
    await typeInto('input[type="password"]', 'ChevalBatterieCorrect');
    await awaitCue('form');

    // Scène 3 : soumettre
    speak('done');
    await clickAt('button[type="submit"]');
    await sleep(1000);
    await awaitCue('done');
  },
});

Étape 6 — exécuter

cd /Users/<vous>/Desktop/Projects/skill/screen-record-skills
# Première fois uniquement :
npm install --silent
# Chaque fois :
node /tmp/screen-record-<nom>.mjs

Chaque exécution dure environ la durée de la vidéo finale plus ~10s de frais généraux.

Étape 7 — rapporter

Informez l'utilisateur :

  • Chemin vers le fichier .mp4 final (prêt pour LinkedIn, 1920×1080, H.264 + AAC)
  • Chemin vers le fichier .srt (LinkedIn importera automatiquement les sous-titres fermés)
  • Proposez de lire avec afplay <chemin> sur macOS

Référence des helpers (ce que vos scènes peuvent appeler)

HelperDescription
pagePage Playwright brute si nécessaire
baseUrlL'URL de base configurée
sleep(ms)await sleep(800)
moveCursorTo(x, y, duréeMs?)Déplacement fluide vers les coordonnées
moveToSelector(selecteur, duréeMs?, décalage?)Déplacement fluide vers le centre de l'élément
clickAt(selecteur, duréeMs?)Déplacement + impulsion + clic (enregistre l'heure du clic pour l'effet sonore)
typeInto(selecteur, texte, parCaractère?)Clic + effacement + saisie caractère par caractère
recordClick()Enregistre manuellement un horodatage de clic (pour l'effet sonore)
speak(idRéplique)Marque le début de la réplique. La fin est début + duréeAudio + 0.35s
awaitCue(idRéplique)Attend la fin de la narration de la réplique
typeSplashUrl(texte, parCaractère?)Saisie animée dans la barre d'URL du splash
hideSplash()Fait disparaître le splash (attendez toujours ~750ms après)
configSwitch({ fromInitial, fromLabel, toInitial, toLabel, title, sub })Définit le contenu de la carte de changement de compte
showSwitch() / hideSwitch()Affiche/masque la superposition de changement de compte
setAuth(utilisateur)Définit localStorage[authStorageKey] avec un nouvel utilisateur (associé à showSwitch pour les démos de changement de compte)

Règles de rythme générales

  • Une phrase par réplique. Limitez-vous à ~12 mots maximum.
  • Pour les actions UI, appelez speak(réplique) AVANT l'action pour que la narration soit jouée pendant que le curseur se déplace.
  • Pour les scènes avec beaucoup de saisie, la saisie prend plus de temps que la narration — utilisez sleep au lieu de awaitCue pour le rythme.
  • La durée totale de la vidéo est généralement somme(duréesAudio) + somme(sleeps). Visez 30–75s pour LinkedIn.

Sélecteurs courants qui posent problème

  • text=Foo seul peut correspondre à plusieurs éléments. Utilisez page.locator(...).first() (ce que le moteur fait déjà), OU utilisez button:has-text("Foo") pour limiter à une balise.
  • Les noms de classe Tailwind sont fragiles. Préférez aria-label, role, balise sémantique + texte.
  • Fenêtres modales/dialogues : attendez [role="dialog"] ou le texte d'en-tête de la modale avant de cliquer à l'intérieur.

Ce qu'il ne faut PAS faire

  • N'écrivez pas le texte de la narration directement dans le fichier de scène — conservez la narration dans cues[].text pour que les sous-titres et l'audio restent synchronisés.
  • N'intégrez pas le curseur dans le CSS de la page — le moteur l'injecte.
  • N'utilisez pas await page.screenshot() au milieu d'une scène — vous captureriez le curseur dans des positions étranges ; utilisez extractFrames a posteriori si nécessaire.
  • Ne commitez pas .env.local (il est dans .gitignore).

Fin des instructions de la compétence.

Skills associés