Demos de UI Sintéticas
Nada se graba de pantalla. Claude Code lee el repositorio del producto, reconstruye solo las pantallas que muestra una escena como React estático, y un cursor y cámara programados realizan el flujo en Remotion. Cada clic es un número de fotograma, por lo que una escena se re-renderiza de manera idéntica, nunca necesita una cuenta con datos o un entorno en vivo, y se actualiza editando código cuando el producto cambia.
Builds de referencia (léelos junto con esta skill; ambos se construyen sin los repositorios del producto):
examples/ds-free-schedule-demo: un flujo de 26s (programar una llamada) con un recuadro de narrador. El caso completo más simple. Susrc/Demo.tsxes una sola línea de tiempo con cada fotograma clave del cursor, clic y movimiento de cámara.examples/ds-release-649: un video de notas de versión de 80s, ocho escenas en tres aplicaciones.templates/ds-release-noteses la forma reutilizable del mismo (se envió como release 650).src/Release.tsxsecuencia las escenas,src/scenes/tiene un archivo por elemento de la versión, ysrc/room/ysrc/ac/son los kits de pantalla.
Cuándo usar esto en lugar de Playwright
| Sintético (esta skill) | Grabación con Playwright (/record-demo) |
|---|---|
| Puedes leer el código fuente de la app | Solo tienes la app en ejecución |
| Notas de versión y tours de funciones que se rehacen cada release | Captura única de comportamiento real o datos reales |
| El flujo necesita datos que no tienes (una llamada completa, otros usuarios, una vista de administrador) | Lo real es fácil de alcanzar y se ve bien tal cual |
| Movimientos de cámara, sincronización exacta de clics, re-renderizados después de un cambio de voz | Interacciones largas y complejas que serían costosas de reconstruir |
Bloques de construcción (lib/components)
import { Stage, typed, cursorAt, near } from '../../../../lib/components';
<Stage
url="app.example.com/settings" // barra de direcciones; host más oscuro que la ruta
camera={CAMERA} // [{ f, z, x, y }] en píxeles del lienzo, suavizados, ajustados a la ventana
path={PATH} // [{ f, x, y }] fotogramas clave del cursor en píxeles del lienzo
clicks={CLICKS} // fotogramas que aplastan el cursor y generan una onda
aiNote // "Las personas mostradas son generadas por IA" (o pasa tu propio texto)
canvasWidth={1440} canvasHeight={810} // los valores por defecto
>
<SettingsScreen field={typed(frame, 'Q4 Product Review', 128)} />
</Stage>
Stagedibuja el marco del navegador alrededor de un lienzo de app de tamaño fijo, aplica la cámara y coloca el cursor encima con zIndex 100. Con zoom 1, la ventana ocupafit(0.8625) del ancho del fotograma. Los fotogramas clave repiten un valor para mantenerlo.CursorycursorAtfuncionan por sí solos para una escena sin navegador;near(cursor, target)genera estados de hover.- Los kits de pantalla específicos del producto (tema, iconos, diseños) se quedan en el proyecto. No son material de la librería.
Método
1. Obtén una especificación visual del repositorio de la app, con subagentes
Envía un subagente por app o área, en paralelo, y haz que cada uno devuelva una especificación compacta en lugar de volcados de archivos:
- Tema: paleta, familia de fuentes, radios, espaciado, sombras (del archivo de tema o variables CSS).
- Strings: las cadenas exactas de la locale en inglés para cada etiqueta, elemento de menú, diálogo y toast que muestra la escena (
public/locales/en/*.json,src/i18n/locales/en/*). Nunca parafrasees el texto de la UI. - Iconos: si la app usa una fuente de iconos, su
selection.jsonde icomoon/fontello contiene las rutas SVG de cada glifo. Extrae los que necesites en un pequeño JSON y renderízalos como<svg><path d=...>. De lo contrario, copia los componentes SVG. - Diseño y comportamiento: árbol de componentes para las pantallas mostradas, orden de la barra de herramientas, valores predeterminados, qué hace cada control.
Lee la rama por defecto desde el remoto (git show origin/main:path, o git fetch luego lee origin/<default>). Los checkouts locales suelen estar desactualizados y te darán la UI del trimestre pasado.
Para las notas de versión, rastrea cada elemento hasta el commit y el componente que lo envía por primera vez. Eso te da las cadenas exactas y los valores predeterminados a mostrar.
2. Reconstruye solo lo que muestra una escena, en un lienzo fijo
- Coloca cada elemento con posiciones absolutas en un lienzo fijo (1440x810 es un buen valor por defecto). Exporta los rectángulos importantes como constantes (
SAVE_BTN = { x, y, w, h }), para que los objetivos de clic sean aritméticos, no adivinanzas. - Construye solo lo que la cámara ve. Un panel que nunca aparece puede ser un color plano.
- Mantenlo como React estático: sin estado, sin efectos. Cada cambio visual es función de
frame. - Pon el kit en su propia carpeta (
src/ui/,src/room/) con untheme.tspara constantes.
3. Voz primero, luego pon la línea de tiempo en fotogramas
- Escribe una línea por escena, dividiendo en oraciones (un
.txtpor frase), y genera el audio primero (voiceover.py --scene-dir ...). Tres tomas ytools/pick_takes.pysuperan a una sola toma más--max-wpm: un clon copia el ritmo y la planitud de su clip de referencia, así que si las líneas salen apresuradas o monótonas, arregla el clip contools/voice_prosody.py expanden lugar de estirar la salida (ver "Clone Prosody" en CLAUDE.md). Lee las duraciones medidas, ytools/captions.pypara las temporizaciones de palabras. - Ajusta cada escena a su línea más un beat, y mantén un comentario de cabecera que enumere el inicio y la duración de cada línea. Ese comentario es la fuente de verdad (ver "Audio-Anchored Timelines" en CLAUDE.md).
- Todo es una constante de fotograma al principio del archivo de escena: fotogramas clave del cursor,
CLICKS, fotogramas clave de la cámara, cambios de estado de la UI (frame >= C.savemuestra el diálogo). Exporta los fotogramas de clic para que el padre pueda colocar los efectos de sonido de clic. - Busca los fotogramas a partir de la narración, no los establezcas manualmente.
voice()(lib/components/cues.ts; la plantilla ds-release-notes tiene una copia) toma las frases de la escena con sus.captions.jsony fotogramas de inicio, ycue('save')devuelve el fotograma en el que comienza la palabra. Ancla cada acción a la palabra que ilustra, con un desplazamiento, y cada fotograma clave del cursor y la cámara a esos anclajes:Regenera o re-temporiza una frase y los clics, escritura, cursor y cámara la siguen. Una palabra clave cuyas palabras ya no se dicen falla el bundle con las palabras que lo están. Opciones:import line06a from '../../public/audio/scenes/06a_link.captions.json'; export const LINK_VOICE = voice([{ file: '06a_link', at: 10, captions: line06a }, ...]); const { cue } = LINK_VOICE; const C = { field: cue('before', { offset: -3 }), save: cue('save') }; const PATH = [{ f: C.field - 6, ...field }, { f: C.save - 24, ...saveBtn }];offset,edge: 'end',after(un fotograma, para una palabra repetida),nth,in(una frase). El padre coloca el audio desdeLINK_VOICE.lines, así que el fotograma de inicio de una frase se escribe una vez.templates/ds-release-notes/src/scenes/FreeCustomLink.tsxes el ejemplo trabajado.
4. Divide las líneas de lista densas en sus pausas
Una línea que enumera varias cosas ("programación, contactos, grabaciones y transcripciones") es demasiado rápida para una sola toma. Divide el audio en sus pausas en archivos de frase (07a, 07b, ...), asigna a cada frase su propia toma, y coloca cada frase en su propio beat. ds-release-649's moreVo hace esto.
5. Mide los objetivos de clic desde una hoja de contactos antes del render completo
Una coordenada adivinada suele estar 20-50px desviada una vez que la cámara hace zoom. Renderiza fotogramas fijos en cada fotograma de clic y unos pocos fotogramas antes, combínalos en mosaico, y verifica que la punta del cursor se sitúa sobre el objetivo:
i=1; for f in 46 78 122 212; do npx remotion still MyComp out/stills/s$i.png --frame=$f; i=$((i+1)); done
ffmpeg -start_number 1 -i out/stills/s%d.png -vf "scale=640:-1,tile=2x2" -frames:v 1 out/contact.png
Arregla las constantes, re-renderiza los fotogramas fijos, y solo entonces renderiza el video.
6. Etiqueta a las personas generadas por IA en pantalla
Cualquier persona que no sea real (retratos FLUX.2 en mosaicos, avatares, un narrador) recibe una etiqueta visible: aiNote en el Stage, o una insignia "IA" en un recuadro de narrador. Los nombres, contactos y datos de llamadas también son inventados, así que mantenlos claramente ficticios.
7. Renderiza, luego normaliza
tools/qc.py --project ... antes de renderizar, luego qc.py --render out/video.mp4 --fix para -14 LUFS. qc.py --project ... --layout verifica el texto propio del video (subtítulos, tarjetas, la nota de IA) en cuanto a tamaño y contraste, y lista el texto de la UI del producto por separado. Stage marca su ventana con data-qc="product"; pon el mismo atributo en cualquier maqueta de teléfono o tableta que construyas fuera de él, o su texto de UI se juzgará como parte del video.
Trucos y trampas
- Los logotipos SVG monocromáticos se renderizan en negro. Un
logo_mono.svgsuele tener rellenos negros; sobre un fondo oscuro usafilter: brightness(0) invert(1). - Ramas locales desactualizadas. Lee
origin/<default>, no la rama en la que esté el checkout local. rsync --exclude srctambién eliminanode_modules/*/src. Al copiar un proyecto o kit, ancla las exclusiones a la raíz de transferencia con una barra inicial:--exclude /src.- La cámara se ajusta. Un centro de cámara cerca del borde del lienzo se tira hacia adentro para que el borde de la ventana nunca se muestre más allá del fotograma. Si un zoom aterriza en algún lugar inesperado, esa es la razón; apunta a un punto más adentro.
- Los menús y diálogos deben estar dentro del lienzo. El lienzo recorta a sus hijos. Posiciona un menú para que se abra hacia adentro, como lo hace la app real.
- Los estados de hover provienen de la ruta del cursor. Usa
near(cursorAt(frame, PATH), target)en lugar de un segundo conjunto de constantes de fotograma, para que mover un fotograma clave mueva el hover con él. - Una línea de voz cambiada mueve todo lo que viene después. En una escena con señales, los clics, cursor y cámara siguen solos, pero la longitud de la escena y el
atde las frases posteriores aún necesitan revisión. En una escena manual, vuelve a medir cada fotograma. De cualquier manera, actualiza el comentario de cabecera y las longitudes de las escenas, luego vuelve a verificar los fotogramas fijos de clic. Si un narrador sincronizado con los labios se maneja mediante la pista de voz ensamblada, regenéralo también. - Un kit sobrevive a su demo. Un kit de pantalla reconstruido puede alojar otros videos: la sala
ds-release-notesse convirtió en el escenario de una edición de marca de un corto paródico, con caras animadas en sus mosaicos y sin nuevo trabajo de pantalla. Copia el kit en el nuevo proyecto y escala su lienzo lógico al fotograma (.claude/skills/parody-short/formats.md).