CommunityProgramación y desarrollogithub.com

AppLoidx/record-demo

Habilidad para agente sobre lanzamiento o video demo de producto: AppLoidx/record-demo

¿Qué es record-demo?

Esta habilidad permite a un agente de IA (como Claude Code, Cursor, Codex, etc.) grabar videos de demostración de productos web de forma automatizada. Utiliza Playwright para definir un storyboard (guion gráfico) de forma declarativa, y un motor independiente para renderizar videos MP4 y GIFs de alta calidad. Soporta múltiples formatos (16:9, 1:1, 9:16), edición posterior en Remotion Studio, y puede manejar flujos autenticados con cuentas de prueba, incluyendo la gestión segura de tokens de sesión y OTPs. El agente guía al usuario en la creación del storyboard, la grabación, la verificación de calidad y la edición final, asegurando que el resultado sea un clip publicable, sin URLs locales ni credenciales reales en pantalla.

Compatible con✓Claude Code~Codex CLI✓Cursor
npx skills add AppLoidx/record-demo

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

Grabar videos demo

Escribe un storyboard ad-hoc de Playwright, luego ejecuta el motor autocontenido desde cualquier directorio de trabajo. No agregues el storyboard a un repositorio de aplicación a menos que el usuario lo solicite. Mantén el storyboard autocontenido; el ejecutor coloca ese archivo dentro del motor antes de que Playwright lo cargue, por lo que no uses importaciones auxiliares relativas al storyboard. <skill-dir> es el directorio donde está clonado este repositorio, por ejemplo ~/.claude/skills/record-demo.

Escribir el storyboard

Importa el DSL con esta importación portable:

import { demo } from "record-demo/storyboard";

El ejecutor reescribe este especificador a la ruta real del motor al preparar el archivo, por lo que el storyboard puede estar en cualquier lugar. La resolución de tipos del editor para record-demo/storyboard proviene de engine/tsconfig.json paths: fuera del repositorio, asigna record-demo/storyboard a la ruta absoluta <skill-dir>/engine/lib/storyboard.ts en tu tsconfig paths, o mantén el archivo en engine/scripts/.

Usa engine/scripts/example-site.demo.ts como referencia. Prefiere selectores getByRole, usa d.click(locator) en lugar de locator.click(), espacia las acciones con d.hold(...), y afirma o espera el estado final.

demo("titulo-del-storyboard", async (d) => {
  await d.seed(async (page) => {
    await page.getByRole("heading", { name: "Example Domain" }).waitFor();
  });

  await d.step("Leer la página", async (page) => {
    const heading = page.getByRole("heading", { name: "Example Domain" });
    await d.zoomTo(heading, { padding: 48 });
    await d.hold(2_000);
    d.zoomOut();
    await heading.waitFor({ state: "visible" });
  });
});

El motor sobrescribe el nombre de la línea de tiempo con --name o el nombre base del script, por lo que la cadena pasada a demo(...) no necesita coincidir con el slug de salida.

Prevuelo (antes de cualquier captura)

Al grabar contra un checkout servido localmente: git fetch origin en el checkout servidor y afirma que HEAD == origin/main (o la rama nombrada por el usuario) — un checkout desactualizado graba un diseño desactualizado. Si está desactualizado, reconstruye/reinicia el servidor primero. Imprime el SHA servido en las notas de la toma para que cada grabación sea atribuible.

Grabar

Requiere Node >= 22.18 (nvm o sistema), ffmpeg, Xvfb y xdpyinfo. Instala las dependencias una vez dentro de engine/ con pnpm install.

node <skill-dir>/engine/record.mjs \
  --script /ruta/absoluta/o/relativa/al-cwd/demo.demo.ts \
  --url https://example.org \
  [--name output-slug] [--formats 16x9,1x1,9x16] \
  [--chrome none|macos|browser] [--storage-state <ruta>] [--raw]

Usa --formats para seleccionar uno o más preajustes de composición (por defecto 16x9) y --chrome para elegir el marco de ventana renderizado (por defecto none). Los formatos se renderizan en el orden solicitado, y el primero es el principal utilizado para codificar el GIF. El formato 9x16 es un recorte centrado agresivo (aproximadamente el 44% del ancho de la página es visible en el plano general), por lo que debes mantener la acción centrada al crear para retrato.

Usa --raw para detenerte después de la captura limpia. Sin él, espera estos entregables:

  • engine/out/<nombre>-raw.mp4
  • engine/out/<nombre>-<formato>.mp4 para cada formato solicitado
  • engine/out/<nombre>.gif codificado desde el formato principal

El ejecutor imprime las rutas absolutas de los entregables al finalizar. Trata la reproducibilidad del sitio en vivo como responsabilidad del sitio de destino y nunca incrustes credenciales reales en un storyboard.

Sitios autenticados

Graba flujos autenticados solo con cuentas de prueba o staging; nunca uses credenciales de producción. El agente primero debe iniciar sesión una vez con sus propias herramientas, usando un script headless de Playwright. Si el inicio de sesión requiere un OTP enviado por correo electrónico o un enlace mágico, la ruta preferida es el acceso al correo del propio agente: un conector/MCP de Gmail conectado en Claude Code, o una CLI de correo en otros entornos de ejecución, para que lea el código por sí mismo; de lo contrario, pide al usuario que lo pegue. Después del inicio de sesión, guarda la sesión con await context.storageState({ path: storageStatePath }), donde storageStatePath es una ruta temporal de un solo uso creada como storageStatePath="$(mktemp -d)/<cuenta>.storage-state.json". Luego ejecuta el comando de grabación con --storage-state "$storageStatePath".

storageState de Playwright restaura solo cookies y localStorage. Las aplicaciones que mantienen la autenticación en IndexedDB o sessionStorage grabarán silenciosamente una pantalla sin inicio de sesión, por lo que debes verificar la página autenticada antes de capturar. El archivo storage-state es un secreto: nunca lo imprimas ni lo confirmes. El grabador lo elimina inmediatamente después de cargarlo; es una transferencia de un solo uso, por lo que volver a grabar requiere un nuevo inicio de sesión y no queda nada relacionado con la autenticación en el disco. El script auxiliar de inicio de sesión único también contiene credenciales: mantenlo temporal y nunca lo confirmes.

Editar una demo grabada en Remotion Studio

Exporta un proyecto autocontenido después de grabar, luego instala y abre Studio:

node <skill-dir>/engine/scripts/export-project.mjs <nombre>
cd <skill-dir>/engine/out/<nombre>-project
pnpm install
pnpm studio

El panel de la línea de tiempo de Studio muestra pistas de estilo editor (filmina de pantalla, un clip nombrado por título, foco, movimiento de zoom y outro) — arrastra el cabezal de reproducción para desplazarte, haz clic en la fila de un clip para encontrar su evento. Usa el panel de propiedades o fixtures/timeline.json para editar títulos, temporización del zoom (mantén los movimientos de zoom en o por encima del mínimo de 1650 ms), temporización del foco y el título del outro. Edita remotion/theme.ts para colores, tipografía, espaciado y movimiento. Renderiza el video 16:9 finalizado con pnpm render.

Reglas de movimiento y storyboard (qué hace que un clip sea publicable)

  • Un movimiento de cámara por escena, no por campo. Haz zoom una vez a la sección, actúa dentro de ella. Los zooms por entrada se leen como cortes nerviosos.
  • Plano general en transiciones de página, mantén el plano general en páginas dispersas. d.zoomOut() ANTES de un clic que navegue; nunca hagas zoom a una píldora de estado en una página casi vacía.
  • Secciones cerca de la parte inferior de la página: d.zoomTo([...localizadores], { scroll: "center", padding: 48 }) - la pinza de la cámara no puede centrar un rectángulo en el borde de la página; el scroll-centrado lo arregla en la raíz. Locator[] enmarca una sección completa a través del bbox de unión.
  • d.spotlight([...localizadores]) = enfoque de estilo Apple (atenuación + anillo). holdMs generoso, finaliza explícitamente con d.spotlightOff() + hold(600) antes de que termine el paso. Los rectángulos de foco deben caber en el viewport (obligatorio); los rectángulos de zoom pueden desbordarse por diseño.
  • Seed espera datos reales, no solo el elemento desencadenante; de lo contrario, el video se abre sobre un esqueleto. Espera un nodo de contenido conocido + ~600ms.
  • d.settle(localizador) antes de los subtítulos de resultado. Las vistas que vuelven a buscar después de un envío se vacían brevemente; un waitFor simple se resuelve en el primer pintado y el subtítulo de resultado se dispara sobre una página en blanco.
  • Los subtítulos son imperativos vinculados a la acción del cursor; coloca un subtítulo de resultado (d.caption(...) al final del paso) en escenas de cierre; recorta los subtítulos de los pasos para que los imperativos nunca se ciernen sobre acciones completadas.
  • d.outro(titulo) cierra los clips publicables con una placa final tranquila después de la última escena.
  • Siempre afirma el estado final — sin ello, el pipeline renderiza un video pulido de un flujo roto.
  • Nunca envíes un clip con una URL de localhost, nombre de host interno, nombre de proveedor o credenciales reales en pantalla. Enmascara las cadenas solo de presentación en el seed (parche de window.open y una reescritura de entrada de MutationObserver).

Verificar antes de informar como terminado

  1. pnpm typecheck && pnpm test en engine/.
  2. node scripts/score-motion.mjs out/timeline.json - FAIL bloquea; WARN tasa de zoom ~0.78/s es el estado estable aceptado; cualquier veredicto de discontinuidad es un error de cámara; WARN de silencio significa un tramo estático >= 3.5s con menos de 40px de recorrido del cursor - re-espacia o subtitúlalo.
  3. node scripts/prove-composition.mjs después de cambios en el motor.
  4. Mira el render real: ffmpeg -i out/<nombre>-16x9.mp4 -vf fps=1 out/frames/%03d.png y lee los fotogramas. El recorte del zoom, el retraso del subtítulo, la desincronización del recorte y las URLs de desarrollo en pantalla son invisibles en una ejecución de prueba verde.

Problemas comunes

SíntomaCausa / solución
Invalid demo viewport ... abortoLa geometría se desvió - corrige las banderas de lanzamiento, nunca relajes la aserción.
Desplazamiento del cursor ~174px verticalChrome se ejecutó con pestañas, no con --app.
No se pudieron encontrar fotogramas de la claquetaLa tolerancia del detector es 64 (las capturas externas son promedio impuras); si aún falla, verifica que la captura no sea negra (Chrome en DISPLAY incorrecto).
Los subtítulos/el cursor se desplazan sobre el clipSincronización de la claqueta - no restes el tiempo de reloj de pared.
El recorte del foco está fuera de su objetivoEl rectángulo compitió con un desplazamiento suave - el bucle de estabilidad de measureTarget debería mantenerlo; si se repite, aumenta su tiempo de espera.
Las palabras se cortan a mitad de letra en el borde del recortezoomTo(locator, { padding: 120 }) para modales.
El zoom recorta contenido fuera del cuadromaximumScale es una cuestión de gusto (1.45 funciona), no es un error de pinza.

Verificar cambios en el motor

Ejecuta desde engine/:

pnpm typecheck && pnpm test
node scripts/prove-composition.mjs

Skills relacionados