Vídeos de demonstração
Walkthroughs roteirizados gravados a partir do aplicativo real em execução. O espectador vê o produto realizando trabalho real; partes lentas (respostas do modelo, tarefas) são aceleradas, e cada batida tem uma pequena legenda dizendo o que observar. O mesmo formato é usado sempre, para que segmentos possam ser unidos em um único vídeo.
O formato padrão
Mantenha estes exatos, para que novos segmentos correspondam aos anteriores:
- Vídeo: 1440×900, 30fps, H.264 (
libx264 -preset slow -crf 18),yuv420p,+faststart, sem áudio. - Cartões de título e fim: tela branca total, centralizado: uma linha opcional de marca pequena em maiúsculas cinza
(defina
brandemkit.start; nenhum por padrão), título em negrito 46px, subtítulo cinza 20px. Mantenha cerca de 2,8s, depois fade out. Segmentos abrem com um cartão de título; o último termina com um cartão de takeaway de uma linha (ex.: "Ideia → avaliação → teste ao vivo → envio" / "Nenhum deploy de app no meio"). - Legendas: pill escuro (
rgba(17,17,17,.88)), branco 17px seminegrito Lato, centro inferior. Uma ideia por legenda, cerca de 60 caracteres ou menos, sentença normal, sem ponto final, tempo presente, escrita do lado do espectador ("Resultados abrem como um artefato ao lado do chat"). Mude a legenda quando a batida mudar; mantenha cada uma por cerca de 2,5–4s. - Cliques: um ripple roxo (
rgba(122,71,255,…)) aparece no alvo imediatamente antes de cada clique. - Digitação: visível, tecla por tecla (cerca de 25ms por tecla; 14ms para texto longo).
- Esperas: qualquer coisa lenta executa dentro de
ff(), que reproduz em cerca de 2s. Todo o resto é em tempo real. - Cenas de arquitetura: uma página branca fixa 1440×900 de cenas (título → antes → problema
destacado → depois → contagem de métricas → lista de funcionalidades), Lato, tinta
#111, acento roxo#6f43d6; tags "antes" em cinza, tags "depois" em roxo.assets/architecture-example.htmlé a referência: reutilize seu CSS e estrutura e altere o conteúdo.
Onde os vídeos ficam: o repositório de demos
Cada vídeo tem uma pasta no repositório de demos da equipe (padrão ~/src/demos, ou
$DEMOS_REPO), contendo seus scripts de gravação e um manifesto demo.json. Os repositórios de produto
permanecem livres de código de demonstração. As gravações nunca são commitadas; o output do manifesto diz
onde o MP4 vai.
~/src/demos/
package.json playwright-core + ffmpeg-static (npm install uma vez)
<video>/
demo.json manifesto
architecture.html segmento "scenes" (opcional)
record-<part>.js segmentos "walkthrough", escritos no kit de demonstração
.build/ MP4s e frames por segmento (gitignorado)
Manifesto (demo.json):
{
"name": "<nome-do-video>",
"title": "Uma linha: o que o vídeo mostra",
"description": "Para quem é e o ponto que faz",
"owner": "email",
"repos": ["repositórios onde as funcionalidades gravadas estão, com branches"],
"needs": [{ "what": "serviço", "check": "URL que responde 2xx/3xx quando estiver no ar", "start": "como iniciá-lo" }],
"reset": "como obter uma gravação limpa, e o que a gravação deixa para trás",
"segments": [
{ "name": "architecture", "kind": "scenes", "page": "architecture.html", "timeline": "0:3000,1:4500" },
{ "name": "walkthrough", "kind": "walkthrough", "script": "record-walkthrough.js" },
{ "name": "intro", "kind": "video", "file": "~/path/to/existing.mp4" }
],
"output": "~/Downloads/<nome>.mp4"
}
A pasta examples/sift/ desta skill é uma pasta de referência: um vídeo de dois segmentos (uma página de cena e um
walkthrough real) de um pequeno aplicativo de exemplo que está na mesma pasta.
Se o repositório de demos ainda não foi clonado, crie-o (git init, o package.json acima, e um
.gitignore cobrindo node_modules/, .build/, *.mp4, *.jpg, *.png, *-frames/ e
*-timeline.json) e informe ao usuário que ele ainda não tem um remote.
Ferramentas (em scripts/, relativo a esta skill)
| Script | O que faz |
|---|---|
find-demos.js | node find-demos.js [root…] [--json]: lista cada vídeo no repositório de demos, com segmentos, repositórios, saída, e quando foi gravado e commitado pela última vez. |
record.js | node record.js <video-dir> [--only <segment>]: verifica needs, grava e codifica cada segmento em .build/, junta-os em output (o vídeo antigo é mantido como *.previous.mp4). Execute-o a partir da raiz do repositório de demos. |
demo-kit.js | Biblioteca para scripts de walkthrough: kit.start() fornece page, title/untitle, caption, tap, type, ff, scroll/scrollTop, ripple, navigate, startCast, finish. Frames são transmitidos para o disco. |
encode.js | node encode.js <frames-dir> <timeline.json> <out.mp4>: constrói o MP4, comprimindo janelas ff(). |
record-scenes.js | node record-scenes.js <page.html> <out.mp4> "0:3000,1:4500,…": grava uma página de cena que define window.step(n). |
concat.js | node concat.js <out.mp4> <a.mp4> <b.mp4> …: junta segmentos, re-codificando para o formato padrão. |
review.js | node review.js <video.mp4> <out-dir> [stills] [at=s,s,…]: contact sheets 2×2 de stills para ler de volta. |
assets/example-walkthrough.js é um exemplo completo trabalhado no kit (e
examples/sift/record-walkthrough.js é um real). Leia-o antes de
escrever um novo walkthrough; ele mostra a estrutura, incluindo troca de aplicativos no meio do vídeo,
tarefas aceleradas e um cartão final.
Fluxo de trabalho
0. Encontre o que já existe
Execute find-demos.js primeiro. Se o usuário quiser regravar ou estender um vídeo que já
tem uma pasta, trabalhe nessa pasta: regrave com record.js <folder> (ou
--only <segment> após uma mudança de UI), ou adicione um segmento ao seu manifesto. Não comece uma
segunda pasta para o mesmo vídeo. Crie uma nova pasta apenas para um vídeo genuinamente novo, nomeada
após ele (kebab-case).
1. Storyboard primeiro
Antes de qualquer código, escreva o storyboard como uma lista numerada de seções, cada uma com suas batidas: o que acontece na tela e a legenda. Decida antecipadamente:
- O que deve ser real, e o que pode ser simulado. Prefira real (chamadas de modelo reais, buscas reais). Se os dados precisarem ser simulados (ex.: semanas de tráfego ao vivo), diga isso na tela, tanto em uma legenda quanto no próprio aplicativo, se puder rotular. Os espectadores aceitam uma demonstração pelo valor de face, portanto um resultado simulado apresentado como real é enganoso.
- O que não deve ser clicado. Qualquer coisa que escreva em ambientes compartilhados (salvar quadros, enviar e-mail, compras). Mostre o botão; não pressione.
- Duração: cerca de 60–90s por segmento. Um vídeo montado de 3 minutos é aceitável; cada segmento deve fazer um ponto.
Se o objetivo do usuário estiver claro pela conversa, vá em frente e compartilhe o storyboard em sua atualização enquanto trabalha. Pergunte apenas quando o objetivo do vídeo não estiver claro.
2. Configure a pasta do vídeo
Crie <demos repo>/<video>/ com um demo.json (preencha needs com uma URL check para
cada serviço que o vídeo tocar, e anote reset). Execute npm install na raiz do repositório
de demos se node_modules estiver faltando. O kit e os scripts resolvem playwright-core e
ffmpeg-static a partir do diretório de trabalho, então execute tudo a partir da raiz do repositório. O Chrome
dever estar instalado (channel: 'chrome'; nenhum download de navegador Playwright é necessário).
3. Pré-voo do aplicativo
- Confirme que cada serviço que o vídeo toca está no ar (curl cada URL), e que o HTTPS local funciona (o kit ignora certificados autoassinados).
- Redefina o estado para que a gravação comece limpa (novo banco de dados, localStorage limpo), e faça o script lançar um erro imediatamente se o estado estiver sujo. Uma gravação que falha 3 minutos depois custa chamadas de modelo reais e tempo.
- Cronometre os endpoints lentos que o vídeo consulta. Uma consulta de resultados de 4 segundos mostra um esqueleto de carregamento na câmera; corrija a consulta ou espere por ela.
- Capture a tela de cada estado-chave com um pequeno script Playwright e observe: layout, quebra de linha, imagens quebradas, o que está acima da dobra em 1440×900. Isso é mais barato que gravar.
4. Escreva o script de gravação
Copie a estrutura de assets/example-walkthrough.js:
const kit = require(process.env.DEMO_KIT || `${process.env.HOME}/.claude/skills/demo-video/scripts/demo-kit`);
const demo = await kit.start(); // record.js fornece os caminhos de frame e timeline
await demo.page.goto(URL, { waitUntil: 'networkidle' });
await demo.title('Nome da funcionalidade', 'O que ela permite fazer');
await demo.startCast(); // inicia após o título cobrir a página
await demo.sleep(2800); await demo.untitle(); await demo.sleep(900);
await demo.caption('…'); await demo.tap(locator); await demo.type(locator, 'text');
await demo.ff(() => page.waitForFunction(() => /* resposta terminada */ true, null, { timeout: 180000 }));
await demo.finish();
- Use seletores estáveis (
aria-label, ids,data-*) e verifique-os contra o DOM real. - Aguarde condições reais de conclusão dentro de
ff(), não sleeps fixos, com timeouts generosos. - Quando uma legenda falar sobre algo, role-o para a tela primeiro (
demo.scroll(px, 'main')para aplicativos cujo conteúdo rola dentro de um elemento). demo.navigate(url)troca de páginas ou aplicativos; ele reinicia o screencast na nova página.- Para um segmento de arquitetura, construa a página de cena a partir do exemplo e grave-a com
record-scenes.js. Também dê a ela navegação por teclas de seta e espaço (o exemplo tem), para que a mesma página funcione ao apresentar ao vivo.
5. Grave
A partir da raiz do repositório de demos, execute o vídeo inteiro (ou um segmento) em primeiro plano com um timeout longo, registrando a saída:
set -o pipefail; node ~/.claude/skills/demo-video/scripts/record.js <video> [--only <segment>] 2>&1 | tee <video>/.build/record.log
Não redirecione para tail: ele esconde o código de saída do gravador, e uma gravação com falha parece
sucesso. Se precisar colocá-lo em segundo plano, espere pelo PID real (while kill -0 $PID; do sleep 5; done)
em vez de confiar em uma notificação "concluído", que pode chegar antes do processo terminar.
6. Revise
record.js já codificou e juntou tudo; ele imprime o comando de revisão:
node ~/.claude/skills/demo-video/scripts/review.js <output.mp4> <video>/.build/review 16
Para olhar um segmento, revise <video>/.build/<segment>.mp4.
Leia cada contact sheet antes de considerar o vídeo pronto. Verifique que:
- cada legenda corresponde ao que está na tela naquele momento;
- a coisa a que a legenda se refere está visível, não abaixo da dobra;
- não há imagens quebradas ou texto alternativo, esqueletos travados, toasts de erro, cabeçalhos quebrados ou cortados, ou interface de depuração;
- esperas aceleradas parecem um rápido borrão de progresso, não um quadro congelado;
- cartões de título e fim estão lá.
Corrija o script ou o aplicativo (ex.: um placeholder para imagens que falham ao carregar, uma consulta mais rápida), redefina o estado, e regrave. Espere duas ou três tentativas.
7. Entregue
- Revise os pontos de junção:
review.js <output> <dir> 0 at=<t-1>,<t+1>em cada limite de segmento. record.jsmantém a versão anterior como*.previous.mp4. Nunca sobrescreva vídeos que não são o própriooutputdesta pasta: para adicionar ao vídeo de outra pessoa, adicione-o como um segmentovideode uma nova pasta.- Informe ao usuário que a pasta do vídeo está pronta para commit no repositório de demos. Faça commit apenas se ele pedir.
- Relate ao usuário: os caminhos de arquivo e durações, o que cada segmento mostra, o que era real e o que foi simulado, e qualquer estado que a gravação deixou para trás (serviços em execução, registros que criou, qualquer coisa ainda ligada) com como desfazer.
Armadilhas encontradas antes
- Memória: um screencast CDP envia até 60 quadros por segundo enquanto algo anima.
Armazenar frames em buffer na memória para uma gravação de vários minutos faz o gravador ser morto sem saída.
O kit transmite frames para o disco e os reduz durante
ff(); mantenha isso assim. - Relógio: janelas
ff()e timestamps de quadro usam segundos de relógio de parede, então cronometre a espera inteira (incluindo polling) dentro de uma única chamadaff(). - Trabalho bloqueante (ex.:
execFileSyncpara um script de dados) dentro deff()é aceitável: frames retomam depois e a janela é comprimida de qualquer forma. - Portas: verifique se uma porta está livre antes de iniciar um serviço para a demonstração; não mate processos que você não iniciou.
- Estado residual: uma demonstração que termina enviando ou habilitando algo deixa isso ligado. Desligue depois ou informe o usuário.