E1 · Captura
5 minEjecuta el bloque "capturar el app".
Listo cuando: captura: 0, las cinco fotos de 00-home.png a 04-resultado.png y passos no steps.json: 5.
Clase 3 · 20 min
Para mostrar una app, la pantalla real convence más que cualquier animación. El video-demonstrativo dirige un navegador por tu app, toma una foto por paso con la posición del elemento clicado, narra con voz local y arma el video con cursor y zoom. En esta clase haces todo eso, con una app de ejemplo, sin crédito.
Comenzar la clase
Objetivo: capturar una app paso a paso con el agente y renderizar la demostración narrada que pasa el verificador.
Haz clic en cada etapa.
Etapas de la skill video-demonstrativo en el commit a87b382 (05/10/2026). Captura antes de animar: el video muestra la pantalla de verdad.
Una app de una página en el kit (cuántas palabras caben en una narración) y el actions.json con cinco pasos.
{
"url": "http://127.0.0.1:18791/",
"viewport": [1280, 800],
"window": { "top": 96, "titleH": 52, "urlLabel": "127.0.0.1:18791" },
"eyebrow": "DEMONSTRAÇÃO · MINUTOS DE VÍDEO",
"ctaNarration": "Pronto: agora você sabe quantas palavras cabem no seu vídeo.",
"ctaCaption": "Roteiro do tamanho certo",
"steps": [
{ "id": "home", "intro": true, "caption": "Minutos de vídeo · do zero", "narration": "Este app diz quantas palavras cabem numa narração. Vamos usar em três cliques." },
{ "id": "minutos", "do": { "type": "setValue", "selector": "#min", "value": "3" }, "target": "#min", "caption": "1 · Duração: 3 minutos", "narration": "Primeiro, a duração do vídeo: três minutos." },
{ "id": "ritmo", "do": { "type": "setValue", "selector": "#ppm", "value": "140" }, "target": "#ppm", "caption": "2 · Ritmo: 140 palavras por minuto", "narration": "Depois, o ritmo da fala. Uma narração calma fica perto de cento e quarenta palavras por minuto." },
{ "id": "calcular", "do": { "type": "clickText", "tag": "button", "text": "Calcular" }, "target": { "tag": "button", "text": "Calcular" }, "click": true, "caption": "3 · Calcular", "narration": "Agora é só clicar em calcular." },
{ "id": "resultado", "target": "#res", "zoom": true, "caption": "Cabem 420 palavras", "narration": "Cabem quatrocentas e vinte palavras. Esse é o tamanho do seu roteiro." }
]
}El bloque levanta la app en un puerto local, ejecuta la captura en una sesión propia de agent-browser y cierra todo al final.
K=~/projetos/formacao-cinema-ia-fabrica-v72/kit
command -v agent-browser > /dev/null || { echo "FALTA agent-browser: npm i -g agent-browser@0.6.0 && agent-browser install"; exit 1; }
E=${TMPDIR:-/tmp}/ensaio-fabrica/a3; rm -rf "$E"; mkdir -p "$E"; cd "$E"
git clone -q https://github.com/inematds/video-demonstrativo.git && git -C video-demonstrativo checkout -q a87b382
cp video-demonstrativo/skills/video-demonstrativo/scripts/capture.mjs "$K/exemplos/a3-demo/actions.json" .
python3 -m http.server 18791 --bind 127.0.0.1 --directory "$K/exemplos/a3-demo" > servidor.log 2>&1 & S=$!
until curl -s -o /dev/null http://127.0.0.1:18791/; do sleep 0.3; done
export AGENT_BROWSER_SESSION=ensaio-fabrica
node capture.mjs actions.json > captura.log 2>&1; echo "captura: $?"
agent-browser close > /dev/null 2>&1; kill $S
ls assets/shots
python3 -c "import json; s=json.load(open('steps.json')); print('passos no steps.json:', len(s['steps']))"captura: 0 00-home.png 01-minutos.png 02-ritmo.png 03-calcular.png 04-resultado.png passos no steps.json: 5
Una foto por paso y el steps.json con las cajas medidas. El until curl espera a que el puerto responda antes de abrir el navegador; un sleep fijo falla en una máquina cargada.
El bloque se detiene en la primera línea con FALTA agent-browser: npm i -g agent-browser@0.6.0 && agent-browser install. El paquete es Apache-2.0 (npm, 19/10/2026); el install descarga el navegador. La sesión AGENT_BROWSER_SESSION=ensaio-fabrica evita tocar otra sesión que tengas abierta.
Crea el proyecto HyperFrames, copia fotos, fuentes y steps.json, genera una frase por paso y escribe la composición.
export HYPERFRAMES_NO_TELEMETRY=1 NO_COLOR=1
E=${TMPDIR:-/tmp}/ensaio-fabrica/a3; S=$E/video-demonstrativo/skills/video-demonstrativo
cd "$E" && rm -rf demo
npx -y hyperframes@0.8.176 init demo --non-interactive > init.log 2>&1; echo "init: $?"
cd demo && mkdir -p assets/audio assets/txt
cp -r ../assets/shots assets/ && cp -r "$S/assets/fonts" assets/ && cp ../steps.json .
cp "$S/scripts/composition-template.mjs" build-demo.mjs
node -e 'const d=require("./steps.json"); const l=d.steps.map(s=>s.narration); l.push(d.ctaNarration);
l.forEach((t,i)=>require("fs").writeFileSync(`assets/txt/s${i+1}.txt`, t.trim()+"\n")); console.log("falas:", l.length)'
for f in assets/txt/s*.txt; do
n=$(basename "$f" .txt)
npx -y hyperframes@0.8.176 tts "$f" --voice pf_dora --lang pt-br --speed 0.98 --output "assets/audio/$n.wav" > /dev/null 2>&1 || echo "falhou $n"
done
ls assets/audio | wc -l
node build-demo.mjs | head -1init: 0 falas: 6 6 index.html gerado · 1920x1080 · TOTAL=30.389s · 6 cenas (5 passos + CTA)
Seis frases de narración (cinco pasos y el cierre), seis WAVs. El TOTAL sale de la suma de las narraciones con un margen antes y después de cada una: si cambias la narración, cambia la duración.
La skill en el commit a87b382 llama a npx hyperframes sin versión, y el curso anterior fijaba 0.8.66. Los bloques de este curso usan hyperframes@0.8.176 (npm, 19/10/2026) y HYPERFRAMES_NO_TELEMETRY=1. El porqué, y el resto de HyperFrames, está en el curso 3.
Check, render en borrador y el verificador con la duración que la propia composición declara.
export HYPERFRAMES_NO_TELEMETRY=1 NO_COLOR=1
K=~/projetos/formacao-cinema-ia-fabrica-v72/kit
cd ${TMPDIR:-/tmp}/ensaio-fabrica/a3/demo
npx -y hyperframes@0.8.176 check 2>&1 | grep -E "◇|✗" | head -3
npx -y hyperframes@0.8.176 render --quality draft --workers 2 --quiet --output renders/demo.mp4 > render.log 2>&1
echo "render: $?"
D=$(grep -o 'id="root"[^>]*data-duration="[0-9.]*"' index.html | grep -o "[0-9.]*\"$" | tr -d \")
echo "duração pedida: $D s"
python3 "$K/conferir.py" renders/demo.mp4 "$D" 1920x1080 --audio; echo "saída: $?"◇ 0 errors, 0 warnings ◇ 0 issues across 9 sample(s) ◇ 0 errors, 0 warnings render: 0 duração pedida: 30.389 s OK: 30,4 s · 1920×1080 · 30 fps · com áudio saída: 0
En esta máquina el render de 30 s tardó menos de un minuto (medido fuera de la captura; render por software, --workers 2). La duración pedida sale del data-duration de la raíz, así que el verificador sirve para cualquier guion tuyo.
De la app de ejemplo a tu app.
Ejecuta el bloque "capturar el app".
Listo cuando: captura: 0, las cinco fotos de 00-home.png a 04-resultado.png y passos no steps.json: 5.
Ejecuta "narrar y montar" y después "render y verificar".
Listo cuando: index.html gerado · 1920x1080, render: 0 y OK: … · 1920×1080 · 30 fps · com áudio con saída: 0.
Pídele a tu agente un actions.json con 4 a 6 pasos para una app tuya que corre en localhost. Cambia la URL, ejecuta la captura con tu archivo y después los dos bloques siguientes.
Listo cuando: En la carpeta del ensayo, python3 -c "import json; print(len(json.load(open('steps.json'))['steps']))" → el número de pasos de tu actions.json, y el render termina en OK: … · com áudio y saída: 0.
La explicación aparece después de tu respuesta.
Pregunta 1 de 3
¿Por qué el viewport del actions.json es fijo?
Pregunta 2 de 3
¿Qué define la duración final de la demostración?
Pregunta 3 de 3
El bloque espera el puerto del app con un bucle until curl. ¿Por qué no sleep 1?
Elige una alternativa. La explicación aparece después de tu intento.