AppLoidx/record-demo

Skill de agente / projeto relacionado à criação de vídeos de demonstração de lançamento ou de produto: AppLoidx/record-demo

O que é record-demo?

Este skill capacita assistentes de IA, como o Claude Code, a criarem vídeos de demonstração de produto de alta qualidade a partir de storyboards Playwright autônomos. Ele fornece um DSL (Linguagem Específica de Domínio) para definir ações de demonstração, como zoom, spotlight e narração, e um mecanismo de gravação que renderiza vídeos MP4 e GIFs otimizados para postagem em redes sociais. O skill suporta fluxos autenticados, múltiplos formatos de proporção (16:9, 1:1, 9:16) e inclui ferramentas de verificação e edição para garantir clipes polidos. O objetivo principal é permitir que os agentes criem vídeos de demonstração reproduzíveis e prontos para compartilhamento sem exigir um ambiente de estúdio de vídeo completo.

Funciona com✓Claude Code~Codex CLI✓Cursor
npx skills add AppLoidx/record-demo

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

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

Documentação

Gravar vídeos de demonstração

Escreva um storyboard Playwright ad-hoc e execute o mecanismo autocontido de qualquer diretório de trabalho. NÃO adicione o storyboard a um repositório de aplicativo, a menos que o usuário solicite. Mantenha o storyboard autocontido; o executor prepara esse arquivo dentro do mecanismo antes de o Playwright carregá-lo, portanto, não use imports auxiliares relativos ao storyboard. <skill-dir> significa o diretório onde este repositório foi clonado, por exemplo, ~/.claude/skills/record-demo.

Escreva o storyboard

Importe o DSL com este import portátil:

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

O executor reescreve este especificador para o caminho real do mecanismo durante a preparação, para que o arquivo de storyboard possa estar em qualquer lugar. A resolução de tipo do editor para record-demo/storyboard vem de engine/tsconfig.json paths - fora do repositório, mapeie record-demo/storyboard para o caminho absoluto <skill-dir>/engine/lib/storyboard.ts em seu paths do tsconfig, ou mantenha o arquivo em engine/scripts/.

Use engine/scripts/example-site.demo.ts como referência. Prefira seletores getByRole, use d.click(locator) em vez de locator.click(), pause as ações com d.hold(...) e afirme ou aguarde o estado final.

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

  await d.step("Leia a 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" });
  });
});

O mecanismo substitui o nome da linha do tempo por --name ou pelo nome base do script, portanto, a string passada para demo(...) não precisa corresponder ao slug de saída.

Pré-voo (antes de qualquer captura)

Ao gravar contra um checkout servido localmente: git fetch origin no checkout servidor e afirme que HEAD == origin/main (ou o branch nomeado pelo usuário) — um checkout desatualizado grava um design desatualizado. Se estiver desatualizado, reconstrua/reinicie o servidor primeiro. Imprima o SHA do servidor nas notas da captura para que cada gravação seja atribuível.

Gravar

Requer Node >= 22.18 (nvm ou sistema), ffmpeg, Xvfb e xdpyinfo. Instale as dependências uma vez dentro de engine/ com pnpm install.

node <skill-dir>/engine/record.mjs \
  --script /caminho/absoluto/ou/relativo-ao-cwd/demo.demo.ts \
  --url https://example.org \
  [--name slug-de-saída] [--formats 16x9,1x1,9x16] \
  [--chrome none|macos|browser] [--storage-state <caminho>] [--raw]

Use --formats para selecionar uma ou mais predefinições de composição (padrão 16x9) e --chrome para escolher o quadro da janela renderizado (padrão none). Os formatos são renderizados na ordem solicitada, e o primeiro é o primário usado para codificar o GIF. O formato 9x16 é um corte central agressivo (cerca de 44% da largura da página está visível no plano geral), portanto, mantenha a ação centralizada ao criar para retrato.

Use --raw para parar após a captura limpa. Sem ele, espere estes entregáveis:

  • engine/out/<nome>-raw.mp4
  • engine/out/<nome>-<formato>.mp4 para cada formato solicitado
  • engine/out/<nome>.gif codificado a partir do formato primário

O executor imprime os caminhos absolutos dos entregáveis quando termina. Trate a reprodutibilidade em sites ativos como responsabilidade do site alvo e nunca incorpore credenciais reais em um storyboard.

Sites autenticados

Grave fluxos autenticados apenas com contas de teste ou de staging; nunca use credenciais de produção. O agente deve primeiro fazer login uma vez com suas próprias ferramentas, usando um script Playwright headless. Se o login exigir um OTP enviado por e-mail ou um link mágico, a rota preferida é o próprio acesso de e-mail do agente - um conector/MCP do Gmail conectado no Claude Code, ou um CLI de e-mail em outros runtimes - para que ele leia o código sozinho; caso contrário, peça ao usuário para colá-lo. Após o login, salve a sessão com await context.storageState({ path: storageStatePath }), onde storageStatePath é um caminho temporário único criado como storageStatePath="$(mktemp -d)/<conta>.storage-state.json". Em seguida, execute o comando de gravação com --storage-state "$storageStatePath".

O storageState do Playwright restaura apenas cookies e localStorage. Aplicativos que mantêm autenticação em IndexedDB ou sessionStorage gravarão silenciosamente uma tela de não logado, portanto, verifique a página autenticada antes de capturar. O arquivo storage-state é um segredo: nunca o imprima ou envie para o repositório. O gravador o exclui imediatamente após carregá-lo; é uma transferência única, portanto, regravar requer um novo login e nada relacionado à autenticação permanece no disco. O script auxiliar de login único também contém credenciais: mantenha-o temporário e nunca o envie para o repositório.

Editar uma demonstração gravada no Remotion Studio

Exporte um projeto autocontido após a gravação, depois instale e abra o Studio:

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

O painel da linha do tempo do Studio mostra faixas no estilo de editor (Filmstrip da tela, um clipe nomeado por legenda, spotlight, movimento de zoom e encerramento) - arraste o playhead para navegar, clique na linha de um clipe para encontrar seu evento. Use o painel de propriedades ou fixtures/timeline.json para editar legendas, temporização do zoom (mantenha os movimentos de zoom em ou acima do piso de 1650ms), temporização do spotlight e título de encerramento. Edite remotion/theme.ts para cores, tipografia, espaçamento e movimento. Renderize o vídeo 16:9 finalizado com pnpm render.

Regras de movimento e storyboard (o que torna um clipe postável)

  • Um movimento de câmera por cena, não por campo. Aplique zoom uma vez na seção, aja dentro dela. Zooms por entrada parecem cortes abruptos.
  • Plano geral em transições de página, permaneça em plano geral em páginas esparsas. d.zoomOut() ANTES de um clique que navega; nunca aplique zoom em um status pill em uma página quase vazia.
  • Seções próximas ao final da página: d.zoomTo([...locators], { scroll: "center", padding: 48 }) - a pinça da câmera não consegue centralizar um retângulo no canto da página; a rolagem centralizadora corrige isso na raiz. Locator[] enquadra uma seção inteira via bbox de união.
  • d.spotlight([...locators]) = foco estilo Apple (escurecimento com recorte + anel). holdMs generoso, termine explicitamente com d.spotlightOff() + hold(600) antes do final da etapa. Os retângulos do spotlight devem caber no viewport (aplicado); os retângulos de zoom podem transbordar por design.
  • Seed espera por dados reais, não apenas pelo elemento de gatilho - caso contrário, o vídeo abre em um esqueleto. Aguarde um nó de conteúdo conhecido + ~600ms.
  • d.settle(locator) antes das legendas de resultado. As visualizações que buscam novamente após um envio ficam em branco brevemente; um waitFor simples resolve na primeira pintura e a legenda de resultado dispara sobre uma página branca.
  • Legendas são imperativos vinculados à ação do cursor; coloque uma legenda de resultado (d.caption(...) no final da etapa) nas cenas de encerramento; corte as legendas das etapas para que os imperativos nunca fiquem pendentes sobre ações concluídas.
  • d.outro(titulo) fecha clipes postáveis com uma placa final calma após a cena final.
  • Sempre afirme o estado final - sem ele, o pipeline renderiza um vídeo polido de um fluxo quebrado.
  • Nunca envie um clipe com uma URL localhost, hostname interno, nome de provedor ou credenciais reais na tela. Mascare strings de apresentação apenas na seed (patch de window.open e uma reescrita de entrada do MutationObserver).

Verificar antes de relatar como concluído

  1. pnpm typecheck && pnpm test em engine/.
  2. node scripts/score-motion.mjs out/timeline.json - FAIL bloqueia; WARN taxa de zoom ~0,78/s é o estado estável aceito; qualquer veredito de descontinuidade é um erro de câmera; WARNs de silêncio morto significam um trecho estático >= 3,5s com menos de 40px de deslocamento do cursor - re-pace ou coloque legenda.
  3. node scripts/prove-composition.mjs após alterações no mecanismo.
  4. Assista à renderização real: ffmpeg -i out/<nome>-16x9.mp4 -vf fps=1 out/frames/%03d.png e leia os quadros. Zoom cortado, atraso de legenda, dessincronização do recorte e URLs de desenvolvimento na tela são invisíveis em uma execução de teste verde.

Problemas comuns

SintomaCausa / correção
Invalid demo viewport ... abortoA geometria derivou - corrija as flags de inicialização, nunca relaxe a asserção.
Deslocamento do cursor ~174px verticalmenteO Chrome foi executado com abas, não --app.
Não foi possível encontrar quadros da claqueteA tolerância do detector é 64 (capturas externas médias impuras); se ainda assim falhar, verifique se a captura não está preta (Chrome no DISPLAY errado).
Legendas/deslocamento do cursor sobre o clipeSincronização da claquete - não subtraia pelo relógio de parede.
Recorte do spotlight fora do alvoO retângulo correu em uma rolagem suave - o loop de estabilidade do measureTarget deve segurar; se recorrer, aumente seu timeout.
Palavras cortadas no meio da letra na borda do cortezoomTo(locator, { padding: 120 }) para modais.
Zoom corta conteúdo fora do quadroProblema de gosto do maximumScale (1,45 funciona), não um bug de pinça.

Verificar alterações no mecanismo

Execute a partir de engine/:

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

Habilidades Relacionadas