Gravação de Vídeos de Demonstração (grátis, com script)
Crie vídeos silenciosos de demonstração do app real com script de navegador e renderização para mp4. Grátis e local: Playwright controla o app, ffmpeg gera o vídeo. Sem SaaS pago, sem narração, sem cliques humanos. O mesmo script serve como teste de fumaça UX e é re-renderizado quando a UI muda — o que o torna o caminho sustentável para produzir muitas demos de funcionalidades.
Quando usar
- O usuário quer um ou vários vídeos de demonstração de funcionalidades / walkthrough e não quer pagar por uma ferramenta.
- O problema recorrente "alguém precisa gravar a tela para cada nova funcionalidade".
- Prova visual rápida de que uma funcionalidade funciona, de ponta a ponta, na UI real.
Não serve para: vídeos de marketing com muita animação e narração (use um editor de verdade), ou qualquer coisa que precise de uma pessoa reagindo na câmera.
A estrutura
criar sessão de login → controlar o app com Playwright (grava .webm) → ffmpeg → .mp4
Três arquivos vão para o repositório de destino em demo/ (copiados de resources/):
record.mjs— o motor. Um bloco CONFIG (específico do repositório) + um mapa FLOWS (uma entrada por vídeo).polish.sh—.webm→.mp4, corta a introdução do login, legenda opcional queimada.run.sh— um comando: aponta dev para staging, grava, finaliza, limpa.README.md— uso por repositório.
Para criar um novo vídeo: copie uma entrada do FLOWS, altere a url + steps. Esse é o ciclo completo.
Checklist de configuração (faça uma vez por repositório)
- Instale as ferramentas (uma vez):
npm install -D @playwright/test && npx playwright install chromium. Precisa doffmpeg(Homebrew). Para legendas queimadas você precisa de um ffmpeg compilado comlibfreetype; caso contrário, as legendas são ignoradas automaticamente e todo o resto funciona. - Descubra os detalhes do repositório e preencha o bloco CONFIG em
record.mjs:- Comando de dev — como o app é executado localmente (
npm run dev, geralmente em um subdiretório comocongrats/). - Caminho do módulo cliente Supabase — geralmente
src/integrations/supabase/client.ts. O gravador o importa na página para fazer login (veja Armadilha 1). - Qual banco de dados usar para gravação — use staging, nunca produção (sem PII de usuários reais). Encontre sua URL + chave anônima/pública + service key + uma URL direta do banco. Nesta organização, elas estão no
.env.localdo repositório de audição (VETTED_SUPABASE_*para staging do VettedAI,STAGING_CONGRATS_SUPABASE_*para staging do Congrats). - Um e-mail de login — uma conta sintética/de teste que possui dados demonstráveis. Sempre pergunte ao usuário qual conta antes de criar uma sessão (regra do projeto: nenhum teste com usuário real sem permissão).
- Rotas — os caminhos de link profundo para os fluxos que você gravará.
- Comando de dev — como o app é executado localmente (
- Aponte o servidor dev local para staging (veja Armadilha 2).
- Crie um fluxo, execute
bash demo/run.sh <fluxo>, verifique extraindo quadros (Armadilha 8).
Armadilhas (as conquistadas com esforço — leia antes de depurar)
1. Faça login via o CLIENTE PRÓPRIO do app, não falsificando localStorage
O Supabase-js grava sua sessão sob uma chave/envoltório não padrão no navegador; injetar manualmente
localStorage['sb-<ref>-auth-token'] = JSON.stringify(session) parece correto, mas o cliente não o lê →
o app redireciona para /login. Em vez disso: crie uma sessão no servidor (generateLink admin → verifyOtp fornece
access_token+refresh_token, sem precisar de senha), então na página importe o cliente Supabase do app
e chame supabase.auth.setSession({access_token, refresh_token}). Ele grava o armazenamento correto e a
sessão sobrevive à navegação. Este é o passo mais importante.
2. --mode staging pode ser ignorado silenciosamente → use .env.development.local
vite-react-ssg dev (e algumas configurações) ignoram --mode para carregamento de arquivos env, então .env.staging nunca é carregado e
o app permanece apontado para produção (você verá requisições ao host Supabase de produção). O Vite sempre carrega automaticamente
.env.development.local em dev com prioridade maior que .env/.env.local. Então run.sh copia sua configuração
de staging para .env.development.local durante a gravação, e então o deleta ao sair (caso contrário, todo futuro
npm run dev estará silenciosamente em staging — uma verdadeira armadilha). Repositórios vite simples podem honrar --mode;
teste observando qual host Supabase o app chama.
3. O schema do staging pode estar atrasado em relação ao frontend → reconciliação aditiva direcionada
Uma branch de staging pode estar sem colunas recentes que o frontend seleciona → a consulta de dados retorna 400
(coluna X não existe) e a tela fica vazia. Não replique migrações. Compare as colunas das
poucas tabelas que a tela lê (staging vs produção via information_schema.columns) e aplique apenas as colunas ausentes
como aditivas, anuláveis com ALTER TABLE ... ADD COLUMN IF NOT EXISTS. Obtenha a aprovação do usuário — é uma alteração no banco de staging.
4. Os dados de seed podem estar na escala errada
Valores/cores de seed sintéticos podem não corresponder ao que o formatador da UI espera (por exemplo, scores armazenados como 0–100 enquanto a UI
faz valor/5*100 → "1392%"). Verifique a faixa esperada do formatador em relação aos valores reais de produção; redimensione os dados
de seed se necessário (pergunte primeiro — é uma mutação de dados).
5. Os vídeos do Playwright não mostram o cursor do mouse
Injete um cursor falso com addInitScript (um ponto que segue o mousemove + cresce no mousedown).
O modelo já faz isso. Mova o mouse para coordenadas reais antes de clicar para que ele seja visível.
6. O ffmpeg pode não ter drawtext
Compilações sem libfreetype geram erro em legendas queimadas. O polish.sh detecta drawtext e ignora a legenda se
faltar — não deixe uma falha de legenda bloquear o mp4.
7. Corte automático da introdução de login/inicialização
A gravação captura o login + inicialização do app (segundos mortos). O record.mjs mede a introdução e escreve
um arquivo .trim auxiliar; o polish.sh o corta (ffmpeg -ss).
8. Verifique por quadros, nunca por "gravou"
ffmpeg -ss <t> -i out.mp4 -frames:v 1 frame.png em alguns timestamps e realmente olhe. As primeiras execuções
cairão em /login, um esqueleto ou na aba errada — quadros são como você percebe.
9. Segurança de dados e autenticação
Grave em staging com dados sintéticos (ex.: seed_…@test.vetted.com), nunca um projeto de cliente real
(PII). Sempre pergunte ao usuário em qual conta fazer login. Criar uma sessão de autenticação é uma ação real — confirme primeiro.
Criando um fluxo
Em record.mjs, FLOWS é um mapa de { label, url, steps(page, ui) }. Os helpers ui cobrem a maioria das necessidades:
ui.settle(oque, [palavras])— espera pelo conteúdo (networkidle + uma palavra que prova que a tela carregou)ui.scrollThrough(fração, passos)— rolagem suave para baixo e para cimaui.clickFirstCandidate()/ generalize paraui.clickFirst(seletores)— clique de melhor esforço, nunca falha completamenteui.beat(ms)— uma pausa deliberada para que o vídeo seja bem legível
Mantenha os fluxos com poucos seletores e de melhor esforço (cliques com try/catch) para que um seletor perdido resulte em "ainda um bom demo de rolagem" em vez de uma falha. Pausas generosas (800–1800ms); um bom demo silencioso tem 15–35s.
Saída + entrega
O mp4 vai para demo/output/ (gitignore). open demo/output/<fluxo>.mp4 para reproduzi-lo (um link markdown relativo
não funcionará em um IDE — ele abre o arquivo no editor). Compartilhe o caminho ou use open para o usuário.
Propagando este skill (ele vive no skill-master)
Este skill é canônico em submodules/skill-master/.agent/skills/recording-demo-videos/ e linkado simbolicamente em cada
repositório em .agent/skills/. Após editá-lo, faça commit+push do submódulo primeiro, depois do ponteiro do repositório pai, e
execute /distribute-skill para propagá-lo para os repositórios irmãos (pergunte ao usuário antes de fazer push). Veja
a regra skill-master-submodule-sync.