HyperFrames
O HTML é a fonte da verdade para vídeos. Uma composição é um arquivo HTML com atributos data-* para temporização, uma timeline GSAP para animações e CSS para aparência. O framework gerencia a visibilidade dos clipes, a reprodução de mídia e a sincronização com a timeline.
Integração com Open Design (estrutural para esta superfície)
Quando esta skill é executada dentro do Open Design (ou seja, $OD_PROJECT_DIR está definido), o fluxo de saída é fixo: apenas o .mp4 renderizado deve ser colocado na raiz do projeto. Os arquivos fonte da composição (hyperframes.json, meta.json, index.html, assets) pertencem a um diretório de cache oculto para não poluir o FileViewer do usuário ou os "arquivos produzidos" do chat.
Fluxo de renderização dentro do OD — caminho rápido:
Para a maioria das solicitações do OD ("testar vídeo", "revelação de produto de 5s", "clipe de demonstração"), NÃO escreva a composição HTML do zero. Use o scaffold embutido do HyperFrames e edite apenas o que o prompt realmente altera. O caminho de "criação do zero" custa minutos de saída do modelo e tempo de ferramenta de chat silenciosa; o caminho do scaffold custa segundos.
# 1. Escolha um slot de cache oculto. O prefixo com ponto (.) faz com que a
# listagem de arquivos do projeto OD o ignore, portanto os arquivos fonte
# nunca poluem o chat.
COMP_REL=".hyperframes-cache/$(date +%s)-$(openssl rand -hex 2)"
COMP="$OD_PROJECT_DIR/$COMP_REL"
# 2. Obtenha um scaffold imediatamente renderizável (hyperframes.json,
# meta.json, index.html com CDN do GSAP + window.__timelines.main
# já registrado). Isso é executado no seu shell — cópia de arquivo pura,
# sem Chrome, sem rede além do cache do npx.
npx hyperframes init "$COMP" --example blank --skip-skills --non-interactive
# 3. Edite APENAS $COMP/index.html — altere `data-duration` na raiz
# se precisar de uma duração não padrão, troque a paleta de placeholder
# em <style>, adicione 1-3 <div>s de clipe para texto/imagens e anexe
# os tweens GSAP correspondentes dentro do bloco
# `window.__timelines["main"] = gsap.timeline({paused:true})` já existente.
# Mantenha as edições mínimas; o scaffold já é HF válido.
# 4. Despache a renderização através do daemon do OD. NÃO execute `npx hyperframes
# render` a partir deste shell — o daemon o executa para você em um processo
# não-sandboxado. (Muitos CLIs de agente, Claude Code em particular, envolvem
# o Bash em sandbox-exec do macOS, sob o qual o subprocesso Chrome do puppeteer
# trava parcialmente durante a captura de quadros. O processo do daemon é
# não-sandboxado, portanto as renderizações são concluídas de forma confiável.)
#
# O dispatcher retorna em ~1s com um {taskId}; conduza a renderização até a
# conclusão fazendo um loop de chamadas `"$OD_NODE_BIN" "$OD_BIN" media wait <taskId>`.
# Cada chamada faz long-poll por até 25s (muito abaixo do limite padrão de 30s
# da sua ferramenta de shell) e sai com 0/2/5 para sinalizar concluído/em execução/falhou.
out=$("$OD_NODE_BIN" "$OD_BIN" media generate \
--project "$OD_PROJECT_ID" \
--surface video \
--model hyperframes-html \
--output "<nome-descritivo>.mp4" \
--composition-dir "$COMP_REL")
ec=$?
task_id=$(printf '%s\n' "$out" | tail -1 | jq -r '.taskId // empty')
since=$(printf '%s\n' "$out" | tail -1 | jq -r '.nextSince // 0')
while [ "$ec" -eq 2 ] && [ -n "$task_id" ]; do
out=$("$OD_NODE_BIN" "$OD_BIN" media wait "$task_id" --since "$since")
ec=$?
since=$(printf '%s\n' "$out" | tail -1 | jq -r '.nextSince // '"$since")
done
[ "$ec" -ne 0 ] && { echo "$out" >&2; exit "$ec"; }
Cada chamada generate e cada wait duram no máximo ~25s, portanto o limite padrão de ~30s da ferramenta de shell do agente nunca é acionado. As linhas de progresso do HF (Capturando quadro N/M) são transmitidas para stderr ao vivo durante todo o loop. Quando a renderização termina, a última linha do stdout é {"file": { "name": "<saída>", "size": …, "kind": "video", … }} — cite file.name na sua resposta para que o usuário saiba o que foi produzido.
Pule a Portaria de Identidade Visual dentro do OD. A seção HARD-GATE abaixo (em "Abordagem") diz para você ler DESIGN.md / visual-style.md ou parar e fazer 3 perguntas de humor antes de escrever qualquer composição. Essa portaria é para projetos HF independentes. Os projetos OD já têm sua própria camada de sistema de design — o usuário escolheu sua direção visual no momento da criação do projeto. Para uma renderização de teste OD, use o padrão: canvas escuro (#0b0b0f), um acento quente (#ffb76b), um acento frio (#7da4ff), movimento contido. Só peça informações estilísticas se o prompt do usuário for muito vago para sequer escolher um assunto (muito raro).
Quando pular o scaffold e escrever do zero: apenas quando o usuário solicitar explicitamente algo que o template em branco claramente não pode hospedar (por exemplo, timelines de múltiplas composições, sobreposições reativas a áudio, legendas sincronizadas com uma faixa TTS que já geraram). Para todo o resto, init + editar é o caminho padrão.
Os subcomandos HF mais leves que você AINDA pode executar do seu próprio shell (eles não precisam iniciar o Chrome):
npx hyperframes lint "$COMP"— validar composição antes do despachonpx hyperframes transcribe <audio>— gerar legendasnpx hyperframes tts <text>— gerar narração
Reserve o despacho do daemon para render/inspect/preview (qualquer coisa que dependa do Chrome).
NÃO chame "$OD_NODE_BIN" "$OD_BIN" media generate --model hyperframes-html — esse caminho de dispatcher retorna um 400 (AGENT_RENDERED) de propósito. HyperFrames é renderizado por você diretamente via npx.
NÃO coloque hyperframes.json / meta.json / index.html na raiz do projeto; a listagem de arquivos do OD verifica recursivamente e o usuário veria três arquivos não relacionados aparecerem no chat.
Para opções de CLI além de render (lint, preview, transcribe, tts, inspect, benchmark) chame-as diretamente da sua ferramenta de shell quando a tarefa justificar (por exemplo, gerar áudio TTS no cache antes de referenciá-lo na composição).
Abordagem
Antes de escrever HTML, pense em alto nível:
- O quê — o que o espectador deve experimentar? Identifique o arco narrativo, os momentos-chave e os batimentos emocionais.
- Estrutura — quantas composições, quais são subcomposições vs inline, que trilhas carregam o quê (vídeo, áudio, sobreposições, legendas).
- Temporização — quais clipes determinam a duração, onde as transições ocorrem, qual é o ritmo.
- Layout — construa o estado final primeiro. Veja "Layout Antes da Animação" abaixo.
- Animar — depois, adicione movimento usando as regras abaixo.
Para pequenas edições (corrigir uma cor, ajustar temporização, adicionar um elemento), vá direto para as regras.
Portaria de Identidade Visual
Verifique nesta ordem:
- DESIGN.md existe no projeto? → Leia-o. Use suas cores, fontes, regras de movimento e restrições "O que NÃO Fazer" exatas.
- visual-style.md existe? → Leia-o. Aplique seu
style_prompt_fulle campos estruturados. (Nota:visual-style.mdé um arquivo específico do projeto.visual-styles.mdé a biblioteca de estilos com 8 predefinições nomeadas — arquivos diferentes.) - Usuário nomeou um estilo (ex.: "Swiss Pulse", "escuro e tecnológico", "marca de luxo")? → Leia visual-styles.md para as 8 predefinições nomeadas. Gere um DESIGN.md mínimo com:
## Style Prompt(um parágrafo),## Colors(3-5 valores hex com funções),## Typography(1-2 famílias de fontes),## What NOT to Do(3-5 antipadrões). - Nenhum dos acima? → Faça 3 perguntas antes de escrever qualquer HTML:
- Qual é o clima? (explosivo / cinematográfico / fluido / técnico / caótico / quente)
- Canvas claro ou escuro?
- Alguma cor de marca, fonte ou referência visual específica? Em seguida, gere um DESIGN.md mínimo a partir das respostas.
Toda composição deve ter sua paleta e tipografia rastreadas até um DESIGN.md, visual-style.md ou direção explícita do usuário. Se você estiver recorrendo a #333, #3b82f6 ou Roboto — você pulou esta etapa.
Para padrões de movimento, dimensionamento, padrões de entrada e easing — siga house-style.md. O house style trata de COMO as coisas se movem. O DESIGN.md trata da APARÊNCIA das coisas.
Layout Antes da Animação
Posicione cada elemento onde ele deve estar em seu momento de maior visibilidade — o quadro onde ele entrou completamente, está corretamente posicionado e ainda não está saindo. Escreva isso como HTML+CSS estático primeiro. Ainda sem GSAP.
Por que isso é importante: Se você posicionar elementos em seu estado inicial animado (fora da tela, escalado para 0, opacidade 0) e animá-los até onde você acha que eles devem pousar, você está adivinhando o layout final. Sobreposições são invisíveis até o vídeo ser renderizado. Ao construir o estado final primeiro, você pode ver e corrigir problemas de layout antes de adicionar qualquer movimento.
O processo
- Identifique o quadro herói para cada cena — o momento em que o maior número de elementos está simultaneamente visível. Este é o layout que você constrói.
- Escreva CSS estático para esse quadro. O contêiner
.scene-contentDEVE preencher a cena inteira usandowidth: 100%; height: 100%; padding: Npx;comdisplay: flex; flex-direction: column; gap: Npx; box-sizing: border-box. Use padding para empurrar o conteúdo para dentro — NUNCA useposition: absolute; top: Npxem um contêiner de conteúdo. Contêineres de conteúdo posicionados absolutamente transbordam quando o conteúdo é mais alto que o espaço restante. Reserveposition: absoluteapenas para elementos decorativos. - Adicione entradas com
gsap.from()— anime DE fora da tela/invisível PARA a posição CSS. A posição CSS é a verdade absoluta; o tween descreve a jornada para chegar lá. - Adicione saídas com
gsap.to()— anime PARA fora da tela/invisível A PARTIR da posição CSS.
Exemplo
/* scene-content preenche a cena, padding posiciona o conteúdo */
.scene-content {
display: flex;
flex-direction: column;
justify-content: center;
width: 100%;
height: 100%;
padding: 120px 160px;
gap: 24px;
box-sizing: border-box;
}
.title {
font-size: 120px;
}
.subtitle {
font-size: 42px;
}
/* O contêiner preenche qualquer tamanho de cena (1920x1080, 1080x1920, etc).
O padding posiciona o conteúdo. Flex + gap lida com o espaçamento. */
ERRADO — dimensões fixas e posicionamento absoluto:
.scene-content {
position: absolute;
top: 200px;
left: 160px;
width: 1920px;
height: 1080px;
display: flex; /* ... */
}
// Etapa 3: Anime PARA essas posições
tl.from(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
tl.from(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
tl.from(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);
// Etapa 4: Anime PARA FORA dessas posições
tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
tl.to(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
tl.to(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);
Quando elementos compartilham espaço ao longo do tempo
Se o elemento A sai antes do elemento B entrar na mesma área, ambos devem ter posições CSS corretas para seus respectivos quadros heróis. A ordenação da timeline garante que eles nunca coexistam visualmente — mas se você pular a etapa de layout, não perceberá o caso em que eles acidentalmente se sobrepõem devido a um erro de temporização.
O que conta como sobreposição intencional
Efeitos em camadas (brilho atrás do texto, elementos de sombra, padrões de fundo) e designs empilhados em z (pilhas de cartões, camadas de profundidade) são intencionais. A etapa de layout é para detectar sobreposições não intencionais — dois títulos caindo um em cima do outro, uma estatística cobrindo um rótulo, conteúdo vazando para fora do quadro.
Atributos de Dados
Todos os Clipes
| Atributo | Obrigatório | Valores |
|---|---|---|
id | Sim | Identificador único |
data-start | Sim | Segundos ou referência de ID de clipe ("el-1", "intro + 2") |
data-duration | Obrigatório para img/div/composições | Segundos. Vídeo/áudio assume a duração da mídia. |
data-track-index | Sim | Inteiro. Clipes na mesma trilha não podem se sobrepor. |
data-media-start | Não | Deslocamento de corte na fonte (segundos) |
data-volume | Não | 0-1 (padrão 1) |
data-track-index não afeta a camada visual — use CSS z-index.
Clipes de Composição
| Atributo | Obrigatório | Valores |
|---|---|---|
data-composition-id | Sim | ID único da composição |
data-start | Sim | Tempo de início (composição raiz: use "0") |
data-duration | Sim | Prevalece sobre a duração da timeline GSAP |
data-width / data-height | Sim | Dimensões em pixels (1920x1080 ou 1080x1920) |
data-composition-src | Não | Caminho para arquivo HTML externo |
Estrutura da Composição
Subcomposições carregadas via data-composition-src usam um invólucro <template>. Composições autônomas (o index.html principal) NÃO usam <template> — elas colocam a div data-composition-id diretamente em <body>. Usar <template> em um arquivo autônomo esconde todo o conteúdo do navegador e quebra a renderização.
Estrutura da subcomposição:
<template id="my-comp-template">
<div data-composition-id="my-comp" data-width="1920" data-height="1080">
<!-- conteúdo -->
<style>
[data-composition-id="my-comp"] {
/* estilos com escopo */
}
</style>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
// tweens...
window.__timelines["my-comp"] = tl;
</script>
</div>
</template>
Carregamento na raiz: <div id="el-1" data-composition-id="my-comp" data-composition-src="compositions/my-comp.html" data-star