Demonstrações de UI Sintéticas
Nada é gravado da tela. Claude Code lê o repositório do produto, reconstrói apenas as telas que uma cena mostra como React estático, e um cursor e câmera roteirizados executam o fluxo no Remotion. Cada clique é um número de frame, então uma cena é renderizada de forma idêntica, nunca precisa de uma conta com dados pré-cadastrados ou de um ambiente ativo, e é atualizada editando o código quando o produto muda.
Builds de referência (leia-os junto com esta skill; ambos compilam sem os repositórios do produto):
examples/ds-free-schedule-demo: um fluxo de 26s (agendar uma chamada) com um tile de narrador. O caso completo mais simples. Seusrc/Demo.tsxé uma única linha do tempo com todos os keyframes do cursor, cliques e movimentos de câmera.examples/ds-release-649: um vídeo de notas de versão de 80s, oito cenas em três aplicativos.templates/ds-release-notesé a forma reutilizável dele (lançado como release 650).src/Release.tsxsequencia as cenas,src/scenes/tem um arquivo por item de release, esrc/room/esrc/ac/são os kits de tela.
Quando usar isto vs. Playwright
| Sintético (esta skill) | Gravação Playwright (/record-demo) |
|---|---|
| Você pode ler o código-fonte do aplicativo | Você só tem o aplicativo em execução |
| Notas de versão e tours de funcionalidades que são refeitos a cada release | Captura única de comportamento real ou dados reais |
| O fluxo precisa de dados que você não tem (uma chamada completa, outros usuários, uma visão de admin) | A coisa real é fácil de acessar e parece boa como está |
| Movimentos de câmera, tempo exato de clique, re-renderização após uma mudança de voz | Interações longas e complexas que seriam caras para reconstruir |
Blocos de construção (lib/components)
import { Stage, typed, cursorAt, near } from '../../../../lib/components';
<Stage
url="app.example.com/settings" // barra de endereço; host mais escuro que o caminho
camera={CAMERA} // [{ f, z, x, y }] em px do canvas, suavizados, limitados à janela
path={PATH} // [{ f, x, y }] keyframes do cursor em px do canvas
clicks={CLICKS} // frames que achatam o cursor e lançam uma ondulação
aiNote // "As pessoas mostradas são geradas por IA" (ou passe seu próprio texto)
canvasWidth={1440} canvasHeight={810} // os padrões
>
<SettingsScreen field={typed(frame, 'Revisão Q4', 128)} />
</Stage>
Stagedesenha a borda do navegador ao redor de um canvas de tamanho fixo, aplica a câmera e coloca o cursor no topo com zIndex 100. No zoom 1 a janela éfit(0.8625) da largura do frame. Keyframes repetem um valor para mantê-lo.CursorecursorAtfuncionam sozinhos para uma cena sem navegador;near(cursor, target)aciona estados de hover.- Kits de tela específicos do produto (tema, ícones, layouts) ficam no projeto. Eles não são material da biblioteca.
Método
1. Extraia uma especificação visual do repositório do aplicativo, com subagentes
Envie um subagente por aplicativo ou área, em paralelo, e faça cada um retornar uma especificação compacta em vez de despejos de arquivos:
- Tema: paleta, família de fontes, raios, espaçamento, sombras (do arquivo de tema ou variáveis CSS).
- Strings: as strings exatas de localização em inglês para cada rótulo, item de menu, diálogo e toast que a cena mostra
(
public/locales/en/*.json,src/i18n/locales/en/*). Nunca parafraseie texto da interface. - Ícones: se o aplicativo usa uma fonte de ícones, seu arquivo
selection.json(icomoon/fontello) contém os paths SVG de cada glifo. Extraia os que você precisa em um pequeno JSON e renderize-os como<svg><path d=...>. Caso contrário, copie os componentes SVG. - Layout e comportamento: árvore de componentes para as telas mostradas, ordem da barra de ferramentas, padrões, o que cada controle faz.
Leia o branch padrão do remoto (git show origin/main:path, ou git fetch e depois leia origin/<default>).
Checkouts locais estão frequentemente desatualizados e lhe darão a interface do trimestre passado.
Para notas de versão, rastreie cada item até o commit e o componente que o entrega primeiro. Isso lhe dá as strings exatas e os valores padrão a serem mostrados.
2. Reconstrua apenas o que uma cena mostra, em um canvas fixo
- Disponha cada elemento com posições absolutas em um canvas fixo (1440x810 é um bom padrão). Exporte os retângulos
importantes como constantes (
SAVE_BTN = { x, y, w, h }), para que os alvos de clique sejam aritméticos, não suposições. - Construa apenas o que a câmera vê. Um painel que nunca está no quadro pode ser uma cor sólida.
- Mantenha-o React estático: sem estado, sem efeitos. Toda mudança visual é uma função de
frame. - Coloque o kit em sua própria pasta (
src/ui/,src/room/) com umtheme.tspara constantes.
3. Voz primeiro, depois coloque a linha do tempo em frames
- Escreva uma linha por cena, dividida em sentenças (um
.txtpor frase), e gere o áudio primeiro (voiceover.py --scene-dir ...). Três takes etools/pick_takes.pyvencem um take único mais--max-wpm: um clone copia o ritmo e a monotonia do clipe de referência, então se as linhas saírem apressadas ou monótonas, corrija o clipe comtools/voice_prosody.py expandem vez de esticar a saída (veja "Clone Prosody" no CLAUDE.md). Leia as durações medidas etools/captions.pypara temporizações de palavras. - Dimensione cada cena para sua linha mais uma pausa, e mantenha um comentário de cabeçalho listando o início e a duração de cada linha. Esse comentário é a fonte da verdade (veja "Audio-Anchored Timelines" no CLAUDE.md).
- Tudo é uma constante de frame no topo do arquivo da cena: keyframes do cursor,
CLICKS, keyframes da câmera, mudanças de estado da UI (frame >= C.savemostra o diálogo). Exporte os frames de clique para que o pai possa colocar efeitos sonoros de clique. - Consulte os frames a partir da narração, não os defina manualmente.
voice()(lib/components/cues.ts; o modelo ds-release-notes tem uma cópia) pega as frases da cena com seus.captions.jsone frames de início, ecue('save')retorna o frame em que a palavra começa. Ancore cada ação à palavra que ela ilustra, com um deslocamento, e cada keyframe de cursor e câmera a essas âncoras:Regere ou re-temporize uma frase e os cliques, digitação, cursor e câmera a seguem. Uma deixa cujas palavras não são mais ditas falha no bundle com as palavras que são. Opções: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('antes', { offset: -3 }), save: cue('salvar') }; const PATH = [{ f: C.field - 6, ...field }, { f: C.save - 24, ...saveBtn }];offset,edge: 'end',after(um frame, para uma palavra repetida),nth,in(uma frase). O pai posiciona o áudio deLINK_VOICE.lines, então o frame de início de uma frase é escrito uma vez.templates/ds-release-notes/src/scenes/FreeCustomLink.tsxé o exemplo prático.
4. Divida linhas de lista densas em suas pausas
Uma linha que lista várias coisas ("agendamento, contatos, gravações e transcrições") é rápida demais para um único take.
Divida o áudio em suas pausas em arquivos de frase (07a, 07b, ...), dê a cada frase seu próprio take, e coloque cada
frase em seu próprio tempo. ds-release-649's campo moreVo faz isso.
5. Meça os alvos de clique a partir de uma folha de contato antes da renderização completa
Uma coordenada adivinhada geralmente está 20-50px fora depois que a câmera dá zoom. Renderize stills em cada frame de clique e alguns frames antes, disponha-os em grade e verifique se a ponta do cursor está sobre o alvo:
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
Corrija as constantes, re-renderize os stills e só então renderize o vídeo.
6. Rotule pessoas geradas por IA na tela
Qualquer pessoa que não seja real (retratos FLUX.2 em tiles, avatares, um narrador) recebe um rótulo visível: aiNote no
Stage, ou um selo "IA" em um tile de narrador. Nomes, contatos e dados de chamada também são inventados, então mantenha-os
claramente ficcionais.
7. Renderize, depois normalize
tools/qc.py --project ... antes de renderizar, depois qc.py --render out/video.mp4 --fix para -14 LUFS.
qc.py --project ... --layout verifica o texto do próprio vídeo (legendas, cards, a nota de IA) quanto a tamanho e contraste, e
lista o texto da UI do produto separadamente. Stage marca sua janela data-qc="product"; coloque o mesmo atributo em qualquer
mockup de telefone ou tablet que você construir fora dele, ou seu texto de UI será julgado como o do vídeo.
Armadilhas
- Logos SVG monocromáticos renderizam pretos. Um
logo_mono.svggeralmente tem preenchimentos pretos; em um fundo escuro usefilter: brightness(0) invert(1). - Branches locais desatualizados. Leia
origin/<default>, não o branch em que o checkout local está. rsync --exclude srctambém removenode_modules/*/src. Ao copiar um projeto ou kit, ancore exclusões à raiz de transferência com uma barra no início:--exclude /src.- A câmera limita. Um centro de câmera próximo à borda do canvas é puxado para dentro para que a borda da janela nunca apareça além do frame. Se um zoom pousar em algum lugar inesperado, é por isso; aponte para um ponto mais para dentro.
- Menus e diálogos devem ficar dentro do canvas. O canvas recorta seus filhos. Posicione um menu para que ele abra para dentro, como o aplicativo real faz.
- Estados de hover vêm do caminho do cursor. Use
near(cursorAt(frame, PATH), target)em vez de um segundo conjunto de constantes de frame, para que mover um keyframe mova o hover junto. - Uma linha de voz alterada move tudo depois dela. Em uma cenário com deixas, os cliques, cursor e câmera seguem por conta
própria, mas a duração da cena e os
atdas frases posteriores ainda precisam ser verificados. Em uma cena com frames manuais, re-meça cada frame. De qualquer forma, atualize o comentário de cabeçalho e as durações das cenas, depois verifique novamente os stills de clique. Se um narrador com sincronia labial for orientado pela trilha de áudio montada, regere-a também. - Um kit sobrevive à sua demonstração. Um kit de tela reconstruído pode hospedar outros vídeos: a sala
ds-release-notesse tornou o palco de uma edição de marca de um curta de paródia, com rostos animados em seus tiles e nenhum novo trabalho de tela. Copie o kit para o novo projeto e dimensione seu canvas lógico para o frame (.claude/skills/parody-short/formats.md).