/pitch-deck-video
Genera un vídeo de demostración narrado de un producto a partir de una aplicación web local en ejecución. La edición se deriva, nunca se ajusta manualmente, por lo que cuando cambia el frontend, vuelves a grabar y el corte sigue — sin números de fotograma que reparar.
Diseñado para el caso que realmente ocurre: la interfaz aún se está moviendo mientras necesitas el vídeo.
Uso
/pitch-deck-video # filma la aplicación en el directorio actual
/pitch-deck-video <ruta-a-la-app> # filma una aplicación específica
/pitch-deck-video --rebuild # regraba y recorta un proyecto de vídeo existente
/pitch-deck-video --script # solo cambió la narración; reutiliza la última toma
/pitch-deck-video --render # solo cambiaron tarjetas/leyendas; vuelve a renderizar
La estructura
Cuatro archivos generados lo controlan todo. Solo el primero es escrito por un humano.
| archivo | contiene | escrito por |
|---|---|---|
shots.json | qué se dice, la voz, la música, el tema y qué hace el navegador | tú |
src/vo-durations.json | cuánto tarda realmente cada línea | scripts/tts.mjs |
capture/marks.json | cuándo comenzó cada toma en la grabación y dónde buscar | scripts/record.mjs |
src/frames.json | cuántos fotogramas salieron de la toma | scripts/frames.mjs |
src/timeline.ts deriva toda la edición a partir de esos datos. La duración de las escenas proviene del audio de la narración medido. El metraje se estira para que encaje. Los movimientos de cámara provienen de los cuadros delimitadores de los elementos medidos en vivo durante la grabación, por lo que un botón movido mueve la toma en lugar de romperla.
Flujo de trabajo
1 · Andamiaje
Copia template/ de esta skill en <nombre-app>-video/ junto a la aplicación, luego ejecuta npm install. La plantilla incluye JSON semilla para que funcione con tipos y se renderice antes de la primera grabación.
Nunca edites archivos bajo ~/.claude/skills/pitch-deck-video/template/ para un solo proyecto — esa es la plantilla compartida. Edita la copia.
2 · Primero, verifica si hay una sesión guardada
Si la aplicación tiene autenticación, busca un estado de almacenamiento de Playwright que el repositorio ya mantenga para sus propias pruebas e2e (.auth/*.json, storageState, playwright/.auth/). Apunta app.storageState a él y la grabación comienza con la sesión iniciada sin mostrar el flujo de inicio de sesión en cámara. Solo crea una toma de inicio de sesión si no existe tal archivo — y entonces usa {"type": {"sel": "…", "env": "DEMO_PASSWORD"}} para que la contraseña nunca se escriba en shots.json.
3 · Conoce la aplicación antes de escribir una palabra
Inicia el servidor de desarrollo de la aplicación y captura pantallazos de cada ruta a 1920×1080, luego lee las imágenes. No escribas narración a partir del código fuente. Estás escribiendo sobre lo que un juez verá, y solo los pantallazos te lo dicen.
// script de una sola vez; Playwright viene con la plantilla
for (const r of routes) {
await p.goto(base + r, { waitUntil: 'networkidle' });
await p.waitForTimeout(700);
await p.screenshot({ path: `/tmp/shots/${r}.png` });
console.log(r, await p.evaluate(() => document.documentElement.scrollHeight));
}
Anota las alturas de desplazamiento: cualquier cosa que supere 1080 necesita una acción scroll o nunca aparecerá en cámara.
4 · Escribe shots.json
Comienza desde template/shots.example.json. Consulta references/shots-reference.md para cada campo y acción.
Extrae los theme.colors de los tokens de diseño de la propia aplicación para que las tarjetas y el metraje se lean como una sola pieza. Completa cards.arch.nodes con la arquitectura real — qué cajas son llamadas a modelos y cuáles son código determinista. Ese detalle es lo que distingue una demostración de un envoltorio, y los jueces lo buscan.
Reglas de narración que importan:
- 12–16 momentos clave, 1:30–1:50 en total. Bajo un límite de 2:00, deja 10 segundos de margen.
- Una idea por momento clave. El momento clave termina cuando la idea termina.
- Di el número. "Seis personas puntuaron, Priya gana" es mejor que "elige a alguien."
- Nunca narres la interfaz. No digas "aquí hacemos clic en Aprobar" — di lo que significa.
- Empieza con el problema en palabras del usuario, antes de que exista el nombre del producto.
- Termina con una frase que quieras dejar en pantalla.
5 · Construye
APP_DIR=/ruta/a/la/app npm run all
Narración → grabación → fotogramas → renderizado. Inicia el servidor de desarrollo si no hay nada sirviendo app.url, leyendo el propio script dev de la aplicación para averiguar cómo ese framework toma un puerto. Configura app.devCommand si necesita algo específico. Una reconstrucción típica completa: ~2 minutos.
shots.json se valida primero, por lo que un id duplicado o una acción desconocida falla en dos segundos en lugar de producir un vídeo confuso dos minutos después.
6 · Control de calidad mirándolo — esto no es opcional
npm run qa # un fotograma por toma, apilados en hojas de contactos
npm run qa 3 # tres por toma, cuando quieras verificar una transición
Luego lee las hojas como imágenes. Rastrear el vídeo es lento y te perderás cosas; una hoja de contactos toma segundos y captura exactamente los fallos que importan.
Verifica: capture/marks.json → warnings está vacío (un selector faltante es una advertencia, no un bloqueo — aún obtienes un vídeo y una lista de lo que falló). Luego: ¿cada leyenda aparece en un fotograma que la respalde? ¿Hay fotogramas en blanco o blancos? ¿El texto pequeño es legible, o necesita un focus?
references/troubleshooting.md contiene cada fallo que este pipeline ha encontrado realmente y la solución para cada uno. Léelo antes de depurar desde cero.
Re-corte después de cambios en la interfaz
De esto se trata todo.
APP_DIR=/ruta/a/la/app npm run all
La mayoría de los cambios no necesitan nada más — elementos movidos, páginas rediseñadas, nuevo texto. Lo que aún necesita criterio:
- Una página se eliminó o renombró → re-apunta o reemplaza esa toma.
- Una característica se reescribió → la narración de ese momento clave ahora es una afirmación sobre algo que ya no existe. Reescribe la línea.
- Un selector desapareció realmente → revisa la lista de advertencias y arréglalo allí.
La narración se almacena en caché por línea, por lo que solo las líneas editadas se resintetizan.
Voz
La voz es la diferencia de calidad más grande en el vídeo terminado, y la alternativa por defecto es realmente pobre. Dilo claramente antes de la primera construcción, no después.
tts.provider: "minimax"— bueno. Pregunta al usuario dónde está su clave de MiniMax y apuntatts.envFilea ese archivo. Los ids de voz provienen de la lista de voces del sistema de MiniMax; la etiqueta de idioma en una voz es una sugerencia, no una restricción (una voz etiquetada como ucraniano lee bien en inglés conlanguageBoost: "English").tts.provider: "say"— el sintetizador integrado de macOS, y solo las voces antiguas suelen estar instaladas. Es robótico, pronuncia mal los nombres de productos y es obvio para cualquiera que lo vea. Utilizable para verificar tiempos, débil para cualquier cosa que un juez o cliente verá.- Una grabación humana supera a ambas. Coloca archivos
<id>.wavenpublic/vo/y ejecutanpm run script; las duraciones se vuelven a medir y todo el vídeo se reajusta sin ninguna otra edición.
Si no hay clave, construye el vídeo de todos modos — pero dile al usuario de antemano que la voz es la parte débil y que una clave o diez minutos con un micrófono lo solucionan. No entregues silenciosamente una locución say como si estuviera terminada.
MiniMax recurre a say por línea si la API no está accesible, por lo que una ejecución nunca falla por completo.
Nunca copies una clave API en el proyecto. scripts/tts.mjs lee MINIMAX_API_KEY / MINIMAX_GROUP_ID del entorno y, de lo contrario, los extrae del archivo que nombre tts.envFile. Pregunta al usuario dónde está su clave y apunta envFile a ella.
Una grabación humana supera a cualquier sintetizador: coloca archivos <id>.wav en public/vo/ y ejecuta npm run script — los vuelve a medir y reajusta todo el vídeo.
Música
scripts/music.mjs descarga la pista nombrada en music.url con yt-dlp y corta un fondo musical a la duración actual del vídeo. Dile al usuario que verifique la licencia de la pista antes de publicar — las pistas de biblioteca suelen ser gratuitas con atribución en la descripción y de pago sin ella. El título y la URL se guardan en public/music/SOURCE.txt para que el crédito sea fácil de copiar.
Lo que no debes hacer
- No ajustes los tiempos manualmente. Si un momento clave parece apresurado, la narración es demasiado larga para la toma — arregla
shots.json, no la línea de tiempo. - No pongas
<Screen />dentro de un<Sequence>. Lee el tiempo absoluto por diseño;useCurrentFrame()dentro de una Sequence devuelve fotogramas locales a la secuencia. Esto ha roto el vídeo antes y el síntoma (metraje sutilmente desincronizado con la voz) es difícil de detectar. - No dejes que el tiempo de preparación aparezca en cámara. La navegación, la carga de datos y el cierre de un banner ocurren dentro de la ventana de captura de la toma y se estiran bajo la narración. Pon
{"cut": true}después de los pasos de preparación; el metraje comienza entonces desde allí. - No filmes nada que sea un stub. Si un botón devuelve "conecta el backend", déjalo fuera y dile al usuario qué toma agregar una vez que esté conectado.
- No entregues sin verificar la lista de advertencias.
- No narres un número que veas que varía entre ejecuciones, y nunca describas una pantalla como llena cuando la versión de esta cuenta está vacía. Un juez que detecte una afirmación falsa descartará el resto.