O que ndemo faz?
Crie vídeos de demonstração narrados com gravação de tela para aplicações web.
Configuração
No primeiro uso, você DEVE construir o kit de ferramentas antes de executar qualquer comando.
Verifique se ${CLAUDE_SKILL_DIR}/dist/cli.js existe. Caso contrário, execute:
cd ${CLAUDE_SKILL_DIR} && npm install && npm run build
Em seguida, instale o navegador Playwright, se necessário:
cd ${CLAUDE_SKILL_DIR} && npx playwright install chromium
Verifique a configuração:
${CLAUDE_SKILL_DIR}/ndemo doctor
Comandos
Todos os comandos são executados via ${CLAUDE_SKILL_DIR}/ndemo:
| Comando | O que faz |
|---|---|
${CLAUDE_SKILL_DIR}/ndemo open <playbook> | Inicia o daemon do navegador e navega até a aplicação |
${CLAUDE_SKILL_DIR}/ndemo close | Desliga o daemon do navegador |
${CLAUDE_SKILL_DIR}/ndemo reset | Navega de volta para a URL da aplicação (estado limpo) |
${CLAUDE_SKILL_DIR}/ndemo page-state | Exibe a árvore de acessibilidade da página atual |
${CLAUDE_SKILL_DIR}/ndemo page-state --screenshot | Mesmo que acima + salva captura de tela em .ndemo/screenshot.png |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> | Reproduz todos os segmentos |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --segment <id> | Reproduz um segmento (rebobina primeiro) |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --from <id> | Reproduz do segmento até o final |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --to <id> | Para após este segmento |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --audio | Reproduz com narração TTS |
${CLAUDE_SKILL_DIR}/ndemo render <playbook> | Pipeline completo: TTS → replay → merge → mp4 |
${CLAUDE_SKILL_DIR}/ndemo render <playbook> --output <caminho> | Renderiza para um arquivo de saída específico |
${CLAUDE_SKILL_DIR}/ndemo doctor | Verifica dependências |
Fluxo de Trabalho
Passo 1 — Criar o playbook
Cada playbook reside em seu próprio diretório dentro de demo/ no
projeto do usuário. O nome do diretório corresponde ao nome do playbook.
demo/
minha-funcionalidade/
minha-funcionalidade.yaml ← playbook
fixtures/ ← arquivos para restaurar durante a configuração
audio/ ← arquivos TTS gerados (automático)
video-raw/ ← gravação bruta (automático)
demo.mp4 ← saída final (automático)
Antes de escrever o playbook, pense em qual estado a aplicação precisa estar para que a demonstração funcione de forma confiável e repetível:
- O usuário precisa estar logado? → adicione etapas de login condicionais
- A demo modifica arquivos/dados que precisam ser restaurados? → copie
os originais para o diretório
fixtures/do playbook para que a configuração possa restaurá-los - A demo depende de conteúdo específico existir? → crie-o em fixtures ou via comandos shell de configuração
Copie quaisquer arquivos que serão modificados durante a demo para o
subdiretório fixtures/ do diretório do playbook. As etapas de configuração
os copiarão de volta antes de cada execução para que a demo sempre comece
de um estado limpo.
Crie o diretório e o arquivo YAML. O esquema completo do playbook suporta estas seções de nível superior:
# demo/edit-page/edit-page.yaml
app:
url: http://localhost:8080/wiki
viewport: # opcional, padrão 1920x1080
width: 1920
height: 1080
scale: 2 # fator de escala do dispositivo (padrão 2)
zoom: 1.25 # nível de zoom CSS (padrão 1.25)
colorScheme: light # "light" ou "dark" (padrão "light")
setup:
# Restaurar arquivos modificados durante a demo
- run: cp demo/edit-page/fixtures/page.txt data/pages/page.txt
# Limpar artefatos de execuções anteriores
- run: rm -f data/cache/*.tmp
# Fazer login se necessário (condicional — ignorado se já estiver logado)
- type: click
target: { role: link, name: "Login" }
if:
hidden: ".user-info"
- type: type
target: { role: textbox, name: "Username" }
text: admin
if:
visible: ".login-form"
- type: type
target: { role: textbox, name: "Password" }
text: password
if:
visible: ".login-form"
- type: click
target: { role: button, name: "Sign in" }
if:
visible: ".login-form"
done:
visible: ".user-info"
titleCard: # cartão de título opcional (exibido como primeiro quadro)
title: "Editando uma Página Wiki" # exibido em destaque
subtitle: "Um tour rápido pelo editor" # subtítulo opcional
duration: 3000 # milissegundos para segurar (padrão 3000)
tts: # configuração opcional de TTS
provider: openai # "openai" (padrão) ou "elevenlabs"
voice: alloy # nome da voz TTS (padrão "alloy")
speed: 1.0 # multiplicador de velocidade de fala (padrão 1.0)
recording: # configurações opcionais de gravação
outputDir: "." # diretório de saída relativo ao playbook (padrão ".")
fps: 30 # taxa de quadros do vídeo (padrão 30)
segments:
- id: intro
narration: "Bem-vindo ao nosso wiki. Vamos editar uma página."
intent: "mostrar a página inicial do wiki"
timing: after # "after" (padrão) ou "parallel"
actions:
- type: wait
duration: 2000
- id: open-editor
narration: "Clique no botão de editar para abrir o editor."
intent: "clicar no botão de editar"
actions: []
Timing do segmento controla quando as ações são executadas em relação à narração:
after(padrão) — a narração é reproduzida primeiro, depois as ações executamparallel— as ações executam enquanto a narração é reproduzida
Escreva todos os segmentos com narração e intenção primeiro. Deixe as ações como arrays vazios. Use caminhos absolutos ao passar caminhos de playbook para comandos ndemo.
Passo 2 — Abrir o navegador
${CLAUDE_SKILL_DIR}/ndemo open /caminho/absoluto/para/demo/minha-demo/minha-demo.yaml
Passo 3 — Criar cada segmento
Para cada segmento com ações vazias:
a) Leia o estado atual da página:
${CLAUDE_SKILL_DIR}/ndemo page-state
b) Observe a saída da árvore de acessibilidade. Encontre os elementos referenciados na intenção do segmento. Escreva ações no playbook YAML usando elementos da árvore.
Como escrever alvos — use informações da saída page-state:
Se page-state mostrar [button "Settings"]:
target: { role: button, name: "Settings" }
Se page-state mostrar [searchbox "Search reports" value=""]:
target: { role: searchbox, name: "Search reports" }
Outros campos de alvo — use o que melhor identificar o elemento:
target: { selector: "#meu-elemento" } # seletor CSS
target: { testId: "submit-btn" } # atributo data-testid
target: { label: "Email" } # aria-label
target: { placeholder: "Pesquisar..." } # texto placeholder
target: { text: "Clique aqui" } # conteúdo de texto visível
Pelo menos um campo de alvo é obrigatório. Múltiplos campos podem ser combinados para restringir a correspondência.
Dica: O código-fonte da aplicação web está disponível no repositório do projeto.
Veja o código-fonte do componente em busca de atributos data-testid,
nomes de classe ou IDs quando a árvore de acessibilidade não for suficiente.
Como escrever condições de conclusão:
done:
visible: ".painel-configuracoes" # elemento aparece
hidden: ".spinner-carregando" # elemento desaparece
networkIdle: true # sem requisições pendentes
stable: 500 # DOM inalterado por 500ms
url: "**/configuracoes" # URL alterada
text: # elemento contém texto
selector: ".resultados"
has: "Receita Q3"
attribute: # elemento possui atributo
selector: html
name: data-theme
value: dark
timeout: 10000 # substituir tempo limite padrão (ms)
Toda ação que altera a página DEVE ter uma condição de conclusão. Sem uma, a próxima ação pode executar antes da página estar pronta.
Adicione ações wait após alterações visíveis para que o espectador possa ver
o que aconteceu:
- type: wait
duration: 2000 # 2 segundos
c) Teste o segmento:
${CLAUDE_SKILL_DIR}/ndemo play /caminho/absoluto/para/demo/minha-demo/minha-demo.yaml --segment <id>
d) Se falhar, execute ${CLAUDE_SKILL_DIR}/ndemo page-state para ver
o que está na tela agora, ajuste as ações e tente novamente.
e) Após o segmento funcionar, leia page-state novamente antes de criar o próximo segmento — a página mudou.
Passo 4 — Revisão
${CLAUDE_SKILL_DIR}/ndemo play /caminho/absoluto/para/demo/minha-demo/minha-demo.yaml
Assista à sequência completa no navegador. Pergunte ao usuário se está correto.
Para revisar com narração TTS (requer OPENAI_API_KEY e ffplay):
${CLAUDE_SKILL_DIR}/ndemo play /caminho/absoluto/para/demo/minha-demo/minha-demo.yaml --audio
Isso gera áudio TTS para cada segmento (armazenado em cache por hash de conteúdo), reproduz o áudio junto com as ações do navegador e ajusta o tempo para que as ações correspondam à duração da narração. Se o texto da narração for alterado, o áudio é automaticamente regenerado e o arquivo antigo é excluído.
Passo 5 — Iteração
O usuário pode solicitar alterações. Padrões comuns:
| Usuário diz | O que fazer |
|---|---|
| "Botão errado" | Execute page-state, encontre o elemento correto, atualize o alvo |
| "Muito rápido" / "Muito lento" | Ajuste as durações de wait, reproduza novamente com --audio para verificar o tempo |
| "Mude a narração para ..." | Edite o campo narration |
| "Adicione uma etapa mostrando X" | Insira um novo segmento, crie suas ações |
| "Remova essa etapa" | Exclua o segmento do YAML |
| "Reproduza a partir do segmento X" | ${CLAUDE_SKILL_DIR}/ndemo play --from <id> |
| "Recomeçar" | ${CLAUDE_SKILL_DIR}/ndemo reset e depois reproduza novamente |
Após cada alteração, reproduza novamente o(s) segmento(s) afetado(s) para verificar.
Passo 6 — Renderização
Quando o usuário aprovar:
${CLAUDE_SKILL_DIR}/ndemo render /caminho/absoluto/para/demo/minha-demo/minha-demo.yaml
Isso produz o MP4 final com narração TTS.
Referência de Etapas de Configuração
As etapas de configuração são executadas antes do início da demo (em open, reset, play
e render). Podem ser comandos shell ou ações do navegador.
Comandos shell são executados em relação ao diretório de trabalho do projeto
(o diretório de onde o ndemo foi chamado). Todos os caminhos em comandos run
devem ser relativos à raiz do projeto.
Comandos shell — use run para operações de arquivo:
setup:
- run: cp demo/minha-funcionalidade/fixtures/original.txt data/pagina.txt
- run: rm -f data/cache/*.tmp
- run: ./scripts/reset-db.sh
Ações do navegador — mesma sintaxe das ações de segmento:
setup:
- type: click
target: { role: button, name: "Login" }
- type: type
target: { role: textbox, name: "Username" }
text: admin
Etapas condicionais — adicione if para pular quando a condição não for atendida:
setup:
# Fazer login apenas se ainda não estiver logado
- type: click
target: { role: link, name: "Sign in" }
if:
hidden: ".user-menu" # pular se o menu do usuário estiver visível
# Executar apenas em uma página específica
- type: click
target: { role: button, name: "Reset" }
if:
url: "**/admin/configuracoes" # pular se não estiver nesta página
Campos de condição:
visible: "<seletor>"— a etapa executa apenas se o seletor corresponder a elementos visíveishidden: "<seletor>"— a etapa executa apenas se o seletor não corresponder a elementos visíveisurl: "<padrão>"— a etapa executa apenas se a URL atual corresponder (**= qualquer caminho)
Referência de Tipos de Ação
| Tipo | Campos obrigatórios | Notas |
|---|---|---|
| click | target | |
| type | target, text | delay opcional em ms entre teclas (padrão 80) |
| hover | target | |
| scroll | target | Rola o elemento para a visualização |
| wait | duration (ms) | Pausa para o espectador (padrão 1000ms) |
| press | key | Tecla do teclado, ex.: "Enter", "Escape" |
| select | target, option | Seleção de dropdown |
Regras de ID de Segmento
- Alfanumérico minúsculo com hífens:
abrir-configuracoes,alternar-tema - Deve ser único dentro do playbook
- Usado como nomes de arquivos para áudio/vídeo, portanto mantenha-os curtos
Solução de Problemas
- Navegador não responde:
${CLAUDE_SKILL_DIR}/ndemo closee depoisopennovamente. Verifique.ndemo/daemon.logpara saída do daemon do navegador. - Elemento não encontrado: Execute
${CLAUDE_SKILL_DIR}/ndemo page-statee verifique a árvore de acessibilidade. O elemento pode estar em um iframe ou ter um estado diferente do esperado. - Áudio não reproduz: Verifique se
ffplayestá instalado ou se a chave de API TTS está configurada. - Renderização falha: Verifique se há espaço em disco suficiente e se todos os segmentos têm áudio gerado.