Construye un video demo del flujo que el usuario describió. El resultado se guarda en una carpeta nueva demo/vN/.
La única regla que importa: cada pantalla es una transcripción de la IU real, no una impresión de ella. Un demo que invente diseño, texto o componentes es peor que ningún demo: enseña a los espectadores un producto que no existe. Lee el código fuente de cada pantalla antes de escribir cualquier marcado.
Entradas
/create-product-demo-video <lo que debería pasar> — una descripción del flujo. Si es vaga ("mostrar el
producto"), elige el flujo completo más específico que termine en un valor visible, di cuál elegiste
en una línea, y construyelo. No te detengas a hacer preguntas.
Pasos
-
Mapea el flujo a rutas. Convierte la solicitud en una lista ordenada de rutas reales por las que pasaría el usuario. Encuentra primero el router — un directorio
routes/, un árbolapp/, un bloque<Routes>, un manifiesto de rutas — y lista solo las rutas que existan. Si el flujo necesita una entidad que debe crearse antes de que pueda existir una pantalla posterior (un proyecto antes de un informe por proyecto), incluye esa pantalla o elige un alcance que no la necesite — no omitas un paso requerido silenciosamente. -
Lee el código fuente de cada pantalla. Para cada ruta: el archivo de ruta, los componentes que renderiza, los componentes de diseño compartidos de la app (shell, encabezado de página, estado vacío), y sus primitivas del sistema de diseño (
components/ui/o donde sea que estén). Lee también todo lo que genera cadenas que el demo muestra — ayudantes de vista previa, nombres derivados, servicios de renderizado/plantillas del backend. Copia las cadenas textualmente; no parafrasees el texto del producto. -
Andamiaje.
mkdir -p demo/vN(siguiente N no usado — nunca sobrescribas un vN existente), luego copiaassets/skeleton.html→demo/vN/index.htmlyassets/record.mjs→demo/vN/record.mjs.assets/está junto a esteSKILL.md, donde sea que el skill esté instalado — un directorio de caché de plugins,~/.claude/skills/, o.claude/skills/del proyecto. Resuélvelo desde el propio directorio del skill, nunca desde el repositorio que se está demostrando. El esqueleto trae un shell placeholder, una escala de tokens y el motor de reproducción. Para un demo con música, copia también la pista elegida deassets/music/→demo/vN/music.mp3— ver Música. Copiarla dentro (en lugar de referenciar la carpeta del skill) es lo que permite quevN/sea regrabable más tarde, la misma razón por la querecord.mjsse copia por versión. -
Adapta el esqueleto a este producto. Todo lo marcado con
REPLACE:es un placeholder que debe intercambiarse antes de escribir cualquier escena, porque cada pantalla lo hereda:En index.htmlReemplázalo con PRODUCTEl nombre de la app, el host que muestra su barra de direcciones y su logo svg real NAVLa barra lateral transcrita de la navegación real — mismas secciones, mismo orden, mismas etiquetas TOPBARLa barra superior real; elimina controles que la app no tenga tokens :rootEl bloque :rootde la hoja de estilo de la app, para que color/radio/tipo coincidanICONSDatos de rutas del conjunto de iconos que la app realmente usa Elimina los bloques CSS opcionales (pantalla de terceros, paneles de entrega) que el flujo no use. -
Escribe las escenas. Una
<section class="scene" data-scene="N">por pantalla, en orden. Reutiliza las clases CSS del esqueleto (.pagehead,.card,.btn,.seg,.opt,.chip,.scopeid,.statusbadge,.empty,.input,.pillbar,.toast) — están moldeadas como la salida de un sistema tipo Tailwind/shadcn, así que verifica cada una contra las clases reales del componente y ajusta en lugar de asumir. Añade CSS nuevo solo para un componente que falte en el esqueleto, y derívalo de las clases reales de ese componente. -
Escribe
play(). Portada → pantallas → cierre. Usa subtítulos para nombrar cada paso, y estableceCAPTIONSal principio del script — el estilo, ysteps= cuántas llamadassay()hace la ejecución. Las interacciones pasan por los ayudantes del puntero para que el espectador pueda seguir lo que se hace clic. -
Graba.
node demo/vN/record.mjs→demo/vN/out/product-demo.mp4. Añade--gifpara un gif de 900 px. Un archivodemo/vN/music.*se detecta automáticamente;--music <ruta>apunta a uno en otro lugar,--music-db <n>ajusta el nivel,--no-musicfuerza silencio. -
Verifica mirando. Extrae fotogramas y lee las imágenes — esto no es opcional, así es como se detectan errores de diseño:
for t in 5 12 20 28 36 44; do ffmpeg -y -loglevel error -ss $t \ -i demo/vN/out/product-demo.mp4 -frames:v 1 /tmp/f$t.png; doneVerifica: sin iconos sobredimensionados, nada recortado en el pliegue, el puntero aterriza en el elemento que dice hacer clic, el texto no está colapsado en una línea. Corrige y regraba hasta que esté limpio.
Los fotogramas no pueden mostrarte el audio. En un demo con música, verifica también el flujo y las rampas —
volumedetectregistra a nivel info, así que-v errortragaría su salida:ffprobe -v error -select_streams a:0 -show_entries stream=codec_name,duration \ -of default=nw=1 demo/vN/out/product-demo.mp4 for w in "-ss 0 -t 0.25" "-ss 12 -t 0.5" "-sseof -0.25"; do \ ffmpeg -hide_banner -nostats $w -i demo/vN/out/product-demo.mp4 \ -af volumedetect -f null - 2>&1 | grep mean_volume; doneEspera un flujo
aacdentro de ~0.05 s de la duración del video,max_volumepor debajo de 0 dB, y las medias de cabeza y cola muy por debajo de la del medio — eso es el fundido que prueba que funcionó. -
Escribe
demo/vN/README.md— tabla de escenas con rutas, el mapeo de archivos fuente, duración, el estiloCAPTIONSy la pista de música con la que se grabó la versión, y lo que el flujo omite deliberadamente. Actualizademo/README.mdpara listar la nueva versión.
Reporta la ruta, la duración y el tamaño, luego abre el mp4 para que el usuario pueda verlo — open en
macOS, xdg-open en Linux, start en Windows.
API del motor
Disponible dentro de play():
| Llamada | Efecto |
|---|---|
scene(n) | Transición cruzada a la escena n, actualiza la barra de direcciones desde URLS |
tap(sel, ms) | Mueve el puntero al elemento y haz clic (ondulación + presión) |
moveTo(sel, ms) / click(sel) | Las dos mitades, cuando necesitas cambios de estado entre ellas |
hideCursor() / place(x, y, 0) | Reinicia el puntero al entrar a una escena |
say(text, opts?) / hush() | Subtítulo dentro/fuera. { at: sel } ancla un subtítulo coach; { step: n } fuerza el índice |
type(el, text, ms) | Escritura carácter por carácter en un <span> |
wait(ms) | Espera |
reveal(el) | Llamado automáticamente por moveTo — desplazamiento suave de .main para que los objetivos fuera del pliegue sean visibles |
Establece window.__demoDone = true al final (el esqueleto ya lo hace) — el grabador espera en ello.
Diseño de subtítulos
const CAPTIONS = { style: 'pill', steps: 6 } se sitúa con URLS al principio del motor. Los cuatro
estilos vienen incluidos en el esqueleto; say() es la misma llamada independientemente de cuál se elija. Elige por video:
style | Qué es | Elígelo cuando |
|---|---|---|
pill (predeterminado) | Cápsula de tinta, centro inferior | Cualquier cosa corta y orientada a marketing. Menos oclusión, ~10 palabras por subtítulo |
lower-third | Bloque anclado a la izquierda con índice de paso + barra de progreso | Flujos más largos donde el espectador debe sentir la secuencia; más legible en un gif |
rail | Banda de 68 px debajo de la ventana, puntos de capítulo | Documentación y clips de soporte donde nada puede ser cubierto. Cuesta altura de la app — ver abajo |
coach | Llamada anclada al elemento en juego, resto atenuado | Tutoriales guiados. Cada subtítulo necesita { at: sel }; los que no tienen vuelven a pill |
steps impulsa el índice, la barra de progreso y los puntos del rail — cuenta las llamadas say() y establécela,
o la ejecución termina leyendo 05 / 06. ¿No estás seguro del estilo? Abre index.html en un navegador y cambia
CAPTIONS.style — la página se reproduce con R, por lo que los cuatro pueden compararse sin grabar.
Música
Opcional, y desactivada a menos que haya una pista. La base se multiplexa en el momento de codificación — Playwright no captura ningún audio, por lo que nada de ello toca la página de demostración o el motor.
node demo/vN/record.mjs # usa demo/vN/music.* si existe, silencio si no
node demo/vN/record.mjs --music ~/beds/x.mp3 # la ruta explícita gana sobre el archivo hermano
node demo/vN/record.mjs --music-db -4 # base más silenciosa
node demo/vN/record.mjs --no-music # silencio incluso con una pista hermana
- Las pistas incluidas viven en
assets/music/, una fila por archivo en suLICENSES.md. Ninguna pista se incluye sin una línea CC0/CC-BY verificable — un mp3 sin licencia es un problema para todos los que instalen esto, no solo para ti. ¿No estás seguro? Mantenlo fuera del repositorio y pasa--music <ruta>. - El nivel está normalizado, no ajustado. Cada pista se lleva a −18 LUFS con un techo de pico real de −1.5 dB,
por lo que las bases intercambiadas entre versiones coinciden entre sí sin ajuste manual.
--music-dbdesplaza ese objetivo; no añade ganancia encima. - Los fundidos son automáticos — 1.5 s de entrada, 2.5 s de salida, la salida sincronizada con la duración real del video.
- Elige una pista más larga que el video. Cualquier cosa más corta se repite y la costura es audible; el grabador advierte cuando tiene que hacerlo. 60 s+ cubre un demo normal de 40–60 s.
- Sin voces, sin caídas bruscas. El espectador está leyendo subtítulos y texto de la IU al mismo tiempo.
- El audio solo va en el mp4. El webm es la captura silenciosa sin procesar, y un gif no tiene pista de audio
en absoluto — un cambio solo de música es invisible en la salida
--gif.
Convenciones
- El escenario es un navegador simulado fijo de 1280×800; 44 px de eso son el cromo del navegador, dejando 1280×756 para la app. El esqueleto incluye una base de 48