splitbrain/ndemo

Crie vídeos de demonstração narrados com gravação de tela para aplicações web.

O que é ndemo?

O ndemo é uma ferramenta poderosa que permite a criação automatizada de vídeos de demonstração com narração em áudio, voltada para desenvolvedores que desejam documentar visualmente o fluxo de uso de aplicações web. A habilidade automatiza todo o pipeline: desde a navegação no navegador, gravação de tela, narração via TTS (OpenAI ou ElevenLabs), até a renderização final em MP4. Baseada em arquivos de configuração YAML chamados 'playbooks', a ferramenta suporta ações como cliques, digitação, espera e rolagem, com condições de conclusão para garantir a sincronia. Ideal para criar tutoriais, demonstrações de funcionalidades e vídeos de onboarding, o ndemo integra-se diretamente ao ambiente de desenvolvimento, permitindo iteração rápida e ajustes precisos. Para usar, é necessário configurar o Playwright e, opcionalmente, uma chave de API para serviços de TTS, tornando-a uma solução completa para demos profissionais.

Funciona com✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/splitbrain/ndemo/tree/main

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

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:

ComandoO que faz
${CLAUDE_SKILL_DIR}/ndemo open <playbook>Inicia o daemon do navegador e navega até a aplicação
${CLAUDE_SKILL_DIR}/ndemo closeDesliga o daemon do navegador
${CLAUDE_SKILL_DIR}/ndemo resetNavega de volta para a URL da aplicação (estado limpo)
${CLAUDE_SKILL_DIR}/ndemo page-stateExibe a árvore de acessibilidade da página atual
${CLAUDE_SKILL_DIR}/ndemo page-state --screenshotMesmo 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> --audioReproduz 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 doctorVerifica 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 executam
  • parallel — 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 dizO 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íveis
  • hidden: "<seletor>" — a etapa executa apenas se o seletor não corresponder a elementos visíveis
  • url: "<padrão>" — a etapa executa apenas se a URL atual corresponder (** = qualquer caminho)

Referência de Tipos de Ação

TipoCampos obrigatóriosNotas
clicktarget
typetarget, textdelay opcional em ms entre teclas (padrão 80)
hovertarget
scrolltargetRola o elemento para a visualização
waitduration (ms)Pausa para o espectador (padrão 1000ms)
presskeyTecla do teclado, ex.: "Enter", "Escape"
selecttarget, optionSeleçã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 close e depois open novamente. Verifique .ndemo/daemon.log para saída do daemon do navegador.
  • Elemento não encontrado: Execute ${CLAUDE_SKILL_DIR}/ndemo page-state e 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 ffplay está 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.

Habilidades Relacionadas