CommunityProgramación y desarrollogithub.com

piyush140104/screen-record-skills

Habilidad de Claude Code: videos de recorrido guiado por el navegador con locución de ElevenLabs, música, efectos de sonido de clic y subtítulos integrados.

¿Qué es screen-record-skills?

Esta habilidad para agentes de IA (como Claude Code) te permite crear videos pulidos de recorridos por sitios web directamente desde el navegador. Es ideal para publicaciones en LinkedIn, Twitter, presentaciones comerciales, actualizaciones para inversores o documentación de incorporación. El usuario describe qué debe aparecer en pantalla, qué hace el cursor y el tono de la narración; tú generas un script, y el motor se encarga de la animación del cursor, la síntesis de voz con ElevenLabs, la música de fondo ambiental, los efectos de sonido de clics y la inclusión de subtítulos con estilo. Soporta videos en formato MP4 (1920×1080, H.264) y WebM, así como archivos SRT y ASS para subtítulos. Para usarla, necesitas una clave API de ElevenLabs (gratuita, suficiente para ~15 demos). La habilidad incluye un conjunto completo de funciones auxiliares para controlar la reproducción de voz, los clics y la escritura en el navegador mediante Playwright, y gestiona automáticamente la sincronización de audio y subtítulos.

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

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

/screen-record — Videos de recorrido guiado por el navegador

Cuándo usarlo

El usuario quiere un video pulido de un sitio web haciendo algo, normalmente para LinkedIn, Twitter, una presentación comercial, una actualización para inversores o documentación de incorporación. El usuario describe lo que debe aparecer en pantalla, qué hace el cursor y aproximadamente lo que debe decir la narración. Tú creas un script; el motor se encarga de la animación del cursor, la síntesis de voz (TTS) con ElevenLabs, la música, los efectos de sonido de clic y la inclusión de subtítulos.

No uses esta habilidad para: grabaciones de pantalla reales de una sesión ya en ejecución (usa Captura de pantalla de macOS o QuickTime), GIFs (usa una herramienta de GIFs) o videos que necesiten una locución humana real (puedes redactar el guion y luego pedir al usuario que lo regrabe).

Cómo funciona

El motor está en engine.mjs. Exporta runDemo(config) que:

  1. Genera un clip de TTS por cada indicación con ElevenLabs.
  2. Genera una pista de música ambiental suave y un efecto de sonido de clic con ffmpeg.
  3. Inicia Chromium sin interfaz gráfica a 1920×1080 y graba el video.
  4. Ejecuta tu función scenes(helpers) que controla el navegador.
  5. Mezcla el audio (narración alta + música baja + clics sutiles) e incluye subtítulos con estilo ASS.
  6. Devuelve las rutas a los archivos finales .mp4, .webm, .srt, .ass.

Flujo de trabajo

Paso 1 — lee la solicitud del usuario

Determina: URL a grabar, flujo (lo que debe hacer el cursor), tono (formal/informal) y duración objetivo. Si falta algo, haz una pregunta concreta en lugar de adivinar.

Paso 2 — verifica la clave API

Busca ELEVENLABS_API_KEY en este orden:

  1. $CLAUDE_PLUGIN_ROOT/.env.local (o el .env.local de la propia carpeta de la habilidad)
  2. El .env.local del proyecto actual
  3. process.env

Si no está, dile al usuario:

Necesito una clave API de ElevenLabs. Obtén una gratuita en https://elevenlabs.io/app/settings/api-keys (10.000 caracteres/mes, suficiente para ~15 demos). Puedes pegarla en el chat y la guardaré en el .env.local de la habilidad, o añadirla tú mismo.

Si la clave está presente pero el motor lanza ApiKeyDeadError (HTTP 401/402/403), dile al usuario:

Tu clave de ElevenLabs fue rechazada (revocada, caducada o sin saldo). Renuévala en https://elevenlabs.io/app/settings/api-keys y luego actualiza .env.local. No lo reintentaré hasta que lo hagas.

No reintentes en silencio, no recurras a say de macOS a menos que el usuario lo solicite explícitamente.

Paso 3 — elige una voz

Por defecto es Liam (TX3LPaxmHKxFdv7VOQHJ) — voz masculina estadounidense enérgica y moderna. Más ligero que George (británica) y Sarah (femenina estadounidense). Otras voces gratuitas en engine.mjs bajo FREE_TIER_VOICES. Si el usuario dice "usa la voz X", búscala en el catálogo. Si dice "grave/fuerte/suave/cálida/femenina", elige en consecuencia.

Voces de la biblioteca (Charlotte, Rachel, Adam, etc.) requieren una suscripción paga de ElevenLabs. Si el usuario pide una y está en el plan gratuito, recibirás HTTP 402 — recurre a la voz gratuita más cercana e informa al usuario.

Paso 4 — analiza la página de destino (solo si es necesario)

Si no conoces la estructura de la página, usa Playwright o curl para inspeccionar el DOM y encontrar selectores. No adivines — los selectores importan, las coincidencias ambiguas agotarán el tiempo de grabación.

Para una SPA con contenido dinámico, prefiere aria-label, role o selectores de texto sobre nombres de clase. Usa la semántica de page.locator(s).first() (los ayudantes del motor ya lo hacen).

Paso 5 — escribe un archivo de escena

Crea un archivo en /tmp/screen-record-<nombre>.mjs (no contaminar el repositorio del usuario) que importe el motor y llame a runDemo. Plantilla:

import { runDemo } from '/Users/<tu>/Desktop/Projects/skill/screen-record-skills/engine.mjs';
// o, si está instalado mediante enlace simbólico:
// import { runDemo } from '/Users/<tu>/.claude/skills/screen-record/engine.mjs';

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

  splash: {
    label: 'Visítanos en',
    url:   'www.tuaplicacion.com',
    footer: 'TuApp — flujo de incorporación',
  },

  cues: [
    { id: 'intro',   text: 'Bienvenido a TuApp. Déjame mostrarte cómo funciona.' },
    { id: 'signup',  text: 'Toca Registrarse para crear una cuenta.' },
    { id: 'form',    text: 'Introduce tu correo electrónico y una contraseña.' },
    { id: 'done',    text: 'Y eso es todo — ya estás dentro.' },
  ],

  // Opcional: simula el backend para que la demo sea determinista.
  routes: [
    { match: /\/api\/signup$/, fulfill: { status: 200, body: { ok: true } } },
  ],

  // Opcional: presiembra un usuario autenticado para que la demo omita el inicio de sesión.
  // initialAuth: { id: 'u1', email: '[email protected]', name: 'Usuario Demo' },

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

    // Escena 0: pantalla de inicio (el motor ya navegó a baseUrl fuera de cámara).
    speak('intro');
    await typeSplashUrl('www.tuaplicacion.com');
    await awaitCue('intro');
    await hideSplash();
    await sleep(750); // coincide con la transición de la pantalla de inicio

    // Escena 1: haz clic en registrarse
    speak('signup');
    await clickAt('button:has-text("Registrarse")');
    await awaitCue('signup');

    // Escena 2: completa el formulario
    speak('form');
    await typeInto('input[type="email"]', '[email protected]');
    await typeInto('input[type="password"]', 'CorrectHorseBattery');
    await awaitCue('form');

    // Escena 3: envía
    speak('done');
    await clickAt('button[type="submit"]');
    await sleep(1000);
    await awaitCue('done');
  },
});

Paso 6 — ejecútalo

cd /Users/<tu>/Desktop/Projects/skill/screen-record-skills
# Solo la primera vez:
npm install --silent
# Cada vez:
node /tmp/screen-record-<nombre>.mjs

Cada ejecución dura aproximadamente el largo del video final más ~10s de overhead.

Paso 7 — informa

Dile al usuario:

  • Ruta al .mp4 final (listo para LinkedIn, 1920×1080, H.264 + AAC)
  • Ruta al .srt (LinkedIn lo importará automáticamente como subtítulos)
  • Ofrece reproducirlo con afplay <ruta> en macOS

Referencia de ayudantes (lo que tus escenas pueden llamar)

AyudanteQué hace
pagePágina de Playwright sin procesar si la necesitas
baseUrlLa URL base configurada
sleep(ms)await sleep(800)
moveCursorTo(x, y, duraciónMs?)Movimiento suave a coordenadas
moveToSelector(selector, duraciónMs?, offset?)Movimiento suave al centro del elemento
clickAt(selector, duraciónMs?)Mover + pulsar + hacer clic (registra el tiempo del clic para el SFX)
typeInto(selector, texto, porCarácter?)Hacer clic + borrar + escribir carácter por carácter
recordClick()Registrar manualmente una marca de tiempo de clic (para SFX)
speak(idDeCue)Marcar el inicio de una indicación. El final es inicio + duraciónAudio + 0.35s
awaitCue(idDeCue)Dormir hasta que termine la narración de la indicación
typeSplashUrl(texto, porCarácter?)Escritura animada en la barra de URL de la pantalla de inicio
hideSplash()Desvanecer la pantalla de inicio (siempre dormir ~750ms después)
configSwitch({ fromInitial, fromLabel, toInitial, toLabel, title, sub })Establecer el contenido de la tarjeta de cambio de cuenta
showSwitch() / hideSwitch()Alternar la superposición de cambio de cuenta
setAuth(usuario)Establecer localStorage[authStorageKey] a un nuevo usuario (emparejado con showSwitch para demos de cambio de cuenta)

Reglas generales de ritmo

  • Una frase por indicación. Mantenla por debajo de ~12 palabras.
  • Para acciones de interfaz, speak(cue) ANTES de la acción para que la narración suene mientras el cursor se mueve.
  • Para escenas con mucha escritura, la escritura toma más tiempo que la narración — usa sleep en lugar de awaitCue para marcar el ritmo.
  • La duración total del video suele ser suma(duracionesDeAudio) + suma(sleeps). Apunta a 30–75 segundos para LinkedIn.

Selectores comunes que fallan

  • text=Foo por sí solo puede coincidir con muchos elementos. Usa page.locator(...).first() que es lo que el motor ya hace, O usa button:has-text("Foo") para limitarlo a una etiqueta.
  • Los nombres de clase de Tailwind son frágiles. Prefiere aria-label, role, etiqueta semántica + texto.
  • Modales/diálogos: espera a que aparezca [role="dialog"] o el texto del encabezado del modal antes de hacer clic dentro.

Lo que NO debes hacer

  • No escribas el texto real de la narración en el archivo de escena — mantén la narración en cues[].text para que los subtítulos y el audio se mantengan sincronizados.
  • No incrustes el cursor en el CSS de la página — el motor lo inyecta.
  • No uses await page.screenshot() en medio de una escena — capturarás el cursor en posiciones extrañas; usa extractFrames después si es necesario.
  • No subas .env.local (está en .gitignore).

Fin de las instrucciones de la habilidad.

Skills relacionados