Communitygithub.com

demo-reel

Grave um vídeo de demonstração suave e com precisão de quadros a 60 fps de qualquer aplicativo web a partir de um roteiro curto — cursor roteirizado com um brilho, ondulações de clique, legendas e um selo no canto, renderizado em um relógio virtual para nunca tremer, codificado em MP4 que funciona em PowerPoint, Keynote ou na web. Use quando o usuário quiser um vídeo de demonstração, tour pelo produto, gravação de tela, demonstração de recursos, vídeo de comparação antes/depois, um clipe para slides ou página de destino.

O que é demo-reel?

O `demo-reel` é uma habilidade especializada que transforma um roteiro de navegação em um aplicativo web em um vídeo de demonstração polido, semelhante a uma gravação de tela profissional. Ele oferece movimento suave a 60 fps com um cursor visível e um halo, ondulações de clique e legendas, tudo isso operando o aplicativo real em um navegador Chromium headless, garantindo que o conteúdo seja genuíno e não uma simulação. A chave para a suavidade do vídeo é um 'relógio virtual' injetado que controla funções como `requestAnimationFrame` e `performance.now`, avançando-as apenas no ritmo da taxa de quadros definida, enquanto os `setTimeout`/`setInterval` reais são mantidos para evitar problemas de travamento em páginas complexas. Isso garante que, independentemente do desempenho da máquina de renderização, o vídeo resultante seja perfeitamente suave. A habilidade é compatível com qualquer aplicativo web servido localmente, de preferência uma build de produção, e é controlada por um arquivo de roteiro (`.scenario.mjs`) onde se define a sequência de ações como navegação, cliques, digitação e rolagem, com legendas e outras sobreposições. O fluxo de trabalho inclui servir o app, escrever o roteiro, gravar com `node scripts/record.mjs`, fazer QA com um script de contato e, finalmente, entregar o vídeo no formato MP4 (H.264) para PowerPoint, Keynote ou web. Para usuários de ferramentas como Claude Code, Cursor ou Codex, o `demo-reel` é útil para criar rapidamente vídeos de demonstração de funcionalidades, tours de produtos, gravações de tela para documentação ou landing pages, e comparações antes/depois, sem a necessidade de edição manual de vídeo ou capturas de tela imprecisas.

Funciona com~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/hasuwini77/demo-reel/tree/main/skills/demo-reel

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Prévia

Do README da skill

Documentação

O que demo-reel faz?

Transforme um roteiro de navegação por um aplicativo web em um vídeo que parece uma gravação de tela polida: movimento suave a 60 fps, cursor visível com um halo, ondulações de clique e legendas. Ele opera o aplicativo real em Chromium headless — a filmagem é o produto, não uma simulação.

Por que o vídeo é suave

Uma gravação de tela normal captura quadros quando o navegador decide renderizar; em uma máquina lenta (ou WebGL via software) isso fica em ~10–15 fps e treme. O demo-reel injeta um relógio virtual (scripts/inject.js): requestAnimationFrame, performance.now e Date só avançam quando o gravador permite, e animações CSS/Web são controladas via document.getAnimations(). Cada quadro é avançado exatamente 1/fps, capturado em screenshot e enviado para o ffmpeg. Não importa quanto tempo um quadro leva para renderizar, o resultado final é perfeitamente suave.

Os temporizadores (setTimeout/setInterval) são mantidos reais deliberadamente — falsificá-los faz com que cadeias de temporizadores sem atraso em carregadores e agendadores rodem para sempre (é por isso que o page.clock do Playwright trava em muitas páginas WebGL).

Requisitos

  • Node 18+, ffmpeg no PATH.
  • Uma vez, neste diretório da habilidade: npm install && npx playwright install chromium.

Fluxo de trabalho

  1. Sirva o aplicativo da forma que os usuários o veem — idealmente uma build de produção (vite build && vite preview, next build && next start) com dados locais ou simulados. Servidores de desenvolvimento funcionam, mas são mais lentos e podem mostrar overlays de desenvolvimento. Nunca grave dados reais ou sensíveis — use dados de demonstração.
  2. Escreva um roteiro (*.scenario.mjs, veja a API abaixo). Comece a partir de examples/tasks.scenario.mjs. Planeje como uma história: 20–60 s, uma ideia por legenda, pausas de 1–2 s após cada resultado para que os espectadores possam ler.
  3. Grave: node scripts/record.mjs meu.roteiro.mjs --out demo.mp4 Um clipe de 60 s em 1080p leva aproximadamente 3–10 min (páginas WebGL pesadas são as mais lentas). Execute em segundo plano.
  4. QA: scripts/sheet.sh demo.mp4 cria uma folha de contato (um quadro a cada 2,5 s). Observe: as legendas correspondem ao que está na tela, sem spinners de carregamento, nada coberto pela legenda, selo/cursor visíveis. Verifique a suavidade extraindo alguns quadros consecutivos — cada um deve diferir ligeiramente durante o movimento.
  5. Entrega (veja "Usando o vídeo").

API do Roteiro

export default {
  size: "1920x1080",          // viewport = tamanho do vídeo
  fps: 60,
  theme: { captionPosition: "bottom", haloFill: "rgba(250,204,21,.30)" }, // opcional
  async run(d) {
    await d.open("http://localhost:4173/");     // navegar + aguardar estabilização (não gravado)
    d.badge("NOVO", "#15803d");                  // selo no canto (null oculta)
    d.caption("O novo painel");                  // pílula de legenda (null oculta)
    await d.hold(1500);                          // gravar 1,5 s como está
    await d.click(d.page.getByRole("button", { name: "Filtros" }));
    d.caption("Filtros em um só painel");       // definir legendas APÓS a ação
    await d.hold(1200);
    await d.type("Estocolmo");                  // digitação em velocidade humana
    await d.press("Enter");
    await d.scroll(600);                        // rolagem suave com roda
    await d.step("parte opcional", async () => { /* falhas são registradas, gravação continua */ });
  },
};
ChamadaFaz
d.open(url, { settle })Navegar, deixar a página estabilizar (não gravado, padrão 2500 ms).
d.hold(ms)Gravar a página como está.
d.settle(ms)Avançar o tempo sem gravar — pular estados de carregamento, chunks lentos, animações de layout que você não quer mostrar.
d.move(target, { ms }) / d.hoverDeslizar o cursor (com easing) até um alvo.
d.click(target, { ms, button })Mover, clicar com ondulação. button: "right" para menus de contexto.
d.doubleClick(target)Clique duplo.
d.type(text, { delay })Digitar uma tecla por vez (padrão 90 ms/tecla).
d.press(key)Atalho de teclado, ex. "Escape", "Control+K".
d.scroll(dy, { ms })Rolagem suave com roda.
d.caption(text) / d.badge(text, color) / d.cursor(bool)Estado da sobreposição; sobrevive a navegações completas da página.
d.step(name, fn)Etapa nomeada; erros são registrados, não fatais.
d.page, d.point(target), d.width, d.heightEscapes.

Alvos podem ser um Locator do Playwright, uma string de seletor CSS, { x, y }, ou uma função assíncrona (page) => ({ x, y }).

UIs renderizadas em canvas (gráficos, mapas, charts) não têm elemento DOM por item — passe uma função que peça as coordenadas de tela à biblioteca. Exemplo para Cytoscape.js:

const node = (label) => (page) => page.evaluate((label) => {
  const el = [...document.querySelectorAll("div")].find((e) => e._cyreg?.cy);
  const n = el._cyreg.cy.nodes().filter((n) => n.data("label") === label)[0];
  const r = el.getBoundingClientRect(), p = n.renderedPosition();
  return { x: r.left + p.x, y: r.top + p.y };
}, label);
await d.click(node("Servidor A"));

Escrevendo boas demonstrações

  • Legendas dizem a verdade. Afirme apenas o que a filmagem mostra. Se um bug que você quer mostrar não se reproduz na gravação, mude a legenda — não a mantenha.
  • Legende após a ação, não antes — caso contrário, a legenda da próxima tela aparece sobre a anterior.
  • Mostre resultados, não cliques. Pause 1–2 s em cada resultado; corte carregamentos com d.settle.
  • Antes/depois: grave dois roteiros com a mesma história e passos, selo ANTES/DEPOIS (vermelho/verde), e mantenha arquivos separados além de uma concatenação opcional.
  • Momentos destaque (uma página inicial, uma animação): dê 5–10 s — os espectadores precisam de um momento para absorver.
  • Mantenha o cursor calmo: 600–900 ms por movimento, estacione-o longe do conteúdo durante as pausas.
  • Os temporizadores reais continuam rodando enquanto os quadros são renderizados (~50–300 ms de tempo real por quadro), então UIs baseadas em temporizadores — toasts, pesquisa com debounce, tooltips que desaparecem automaticamente — podem desaparecer mais cedo no vídeo do que na vida real. Dispare-os imediatamente antes de querê-los na tela, ou pause menos.

Usando o vídeo

  • PowerPoint: Insert → Video → Este Dispositivo. Guia Reprodução: Iniciar: Automaticamente e Repetir até Parar. MP4 H.264 yuv420p (o que o demo-reel gera) funciona no Windows e macOS.
  • Keynote: arraste para dentro; Movie → Repeat: Loop.
  • Web: <video src="demo.mp4" autoplay loop muted playsinline></video>.
  • GIF (README, chat): ffmpeg -i demo.mp4 -vf "fps=20,scale=960:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" demo.gif
  • Concatenar clipes do mesmo tamanho/fps: printf "file 'a.mp4'\nfile 'b.mp4'\n" > l.txt && ffmpeg -f concat -safe 0 -i l.txt -c copy ambos.mp4

Solução de problemas

SintomaSolução
Quadros em branco / "Carregando…"Aumentar settle em d.open, ou await d.settle(1500) após um clique de navegação.
Alvo não visívelEstá fora da tela ou dentro de um menu fechado — abra/role primeiro; para itens em canvas use uma função de ponto.
WebGL/3D renderiza pretoMantenha as flags de inicialização padrão (--use-angle=swiftshader); é lento (~0,25 s/quadro) mas correto.
Vídeo muito curto/longo vs planejadoAs durações são exatas: soma de hold + movimentos. O tempo settle nunca é gravado.
Cores parecem desbotadas no PowerPointNão recodifique sem -pix_fmt yuv420p; a saída do demo-reel já está correta.
Texto ligeiramente suave--png captura quadros sem perda (mais lento, maior).

Habilidades Relacionadas