AGENTS.md · diff

git:20260822.ad99a55 to git:20260907.985fba0

62 added, 179 removed. Audit A to A.

# AGENTS.md
- Este arquivo é o guia para **copiar para o seu projeto Remotion** (agentes leem
- antes de escrever composição). Se você está *neste* repositório
- (`remotion-agent-catalog`), o mapa do produto está no `README.md` e em
- `docs/ARQUITETURA.md`. O `npm run catalog` citado abaixo é o **gerador da origem**
- — não existe aqui.
-
- O índice do que existe está em `catalog.json` (gerado — nunca edite à mão).
-
- ---
-
- ## As seis armadilhas
-
- Todas foram descobertas quebrando alguma coisa aqui. Nenhuma dá erro claro — a maioria falha em silêncio.
+ Copy this guide into a Remotion project before an agent writes a composition. In this repository, start with [README.md](./README.md) and [docs/ARQUITETURA.md](./docs/ARQUITETURA.md). The `npm run catalog` command mentioned below belongs to the source generator and is intentionally absent here.
- ### 1. `useVideoConfig()` reporta a COMPOSIÇÃO, não o container
+ `catalog.json` is generated. Never edit it by hand in this repository.
- Componentes que calculam tamanho de fonte, safe area ou posição percentual leem `useVideoConfig()`. Isso devolve as dimensões da `<Composition>`, **não** do elemento onde você os colocou.
+ ## Six failure modes
- Consequência: reduzir por `transform: scale()` encolhe o quadro mas **não o texto**, e tudo vaza.
+ These were found by breaking real compositions. Most fail silently.
- - Para exibir uma cena 1920×1080 dentro de uma célula, use a flag `cena: true` do `Item` — ela renderiza no tamanho real e reduz por transform.
- - Para formato vertical, **crie uma `<Composition>` 1080×1920 de verdade**. Não existe atalho.
- - `cena: true` também é obrigatório para qualquer componente posicionado por porcentagem do canvas (ex.: `SimulatedCursor`, cujo `x`/`y` são 0–100 do vídeo inteiro).
+ ### 1. `useVideoConfig()` reports the composition, not its container
- ### 2. `Sequence` renderiza um `AbsoluteFill`
+ Components that calculate font size, safe area, or percentage positions read the dimensions of their `<Composition>`, not the element that contains them. `transform: scale()` shrinks the frame but not its layout calculations.
- Ele sai do fluxo. Dentro de um flex com `space-between`, todos os blocos colapsam no canto superior esquerdo.
+ - To show a 1920×1080 scene in a cell, use the `cena: true` `Item` flag so it renders at full size before it is scaled.
+ - For vertical work, create a real 1080×1920 `<Composition>`.
+ - `cena: true` is also required for canvas-percentage components such as `SimulatedCursor`.
- Em peça com várias `Sequence` empilhadas, use **posições absolutas explícitas** (`top`, `left`), como em `src/playground/pecas.tsx`.
+ ### 2. `Sequence` renders an `AbsoluteFill`
- ### 3. `defaultProps` passa por JSON
+ It leaves normal flow. Several `Sequence` elements inside a `space-between` flex layout collapse into the upper-left corner. Stack them with explicit absolute `top` and `left` positions, as in `src/playground/pecas.tsx`.
- Elementos React não sobrevivem: chegam do outro lado como `{key, ref, props}` e o React lança o erro #31.
+ ### 3. `defaultProps` crosses a JSON boundary
- Nunca passe JSX por `defaultProps`. Crie um componente estável por página no módulo:
+ React elements arrive as `{key, ref, props}` and can trigger React error #31. Do not pass JSX through `defaultProps`; build stable page components in the module instead:
```tsx
const COMPONENTES = Object.fromEntries(
PAGINAS.map((p) => [p.id, () => <Pagina {...p} />]),
);
```
- ### 4. Em `effects`, a ordem importa — e o tipo do efeito também
-
- - **Gerador** (`checkerboard`, `rings`, `lightLeak`, `zigzag`) desenha imagem nova e ignora o que havia embaixo.
- - **Filtro** (`thermalVision`, `halftone`, `pixelate`, `duotone`, `zoomBlur`) lê os pixels existentes e os transforma.
-
- Um filtro sem nada embaixo não produz nada. Um gerador no fim do array apaga tudo que veio antes.
-
- Muitos efeitos com parâmetros default entregam pouco (`duotone({})` sai branco chapado). Sempre configure.
-
- ### 5. `TransitionSeries` dura menos que a soma das partes
+ ### 4. Effect order and type matter
- A duração é **soma das sequências − soma das sobreposições**. Duas de 40 frames com transição de 20 dão 60, não 80.
+ - Generators (`checkerboard`, `rings`, `lightLeak`, `zigzag`) draw new pixels and discard what was below.
+ - Filters (`thermalVision`, `halftone`, `pixelate`, `duotone`, `zoomBlur`) transform existing pixels.
- Se o `<Loop>` for maior que isso, sobra tela preta. Use o helper `cicloTransicao()` em `pages-extra.tsx`.
+ A filter with no input produces nothing; a generator at the end erases previous work. Configure effects explicitly—many defaults, including `duotone({})`, are not useful.
- Prefira blocos curtos e transição longa: senão o espectador quase sempre pega a cena parada em vez do movimento.
+ ### 5. `TransitionSeries` is shorter than the sum of its sequences
- ### 6. Cenas de mídia nem sempre aceitam vídeo
+ Its duration is the sum of sequences minus the overlaps. Two 40-frame sequences with a 20-frame transition last 60 frames, not 80. A longer `<Loop>` leaves black frames. Use `cicloTransicao()` from `pages-extra.tsx`; favor short blocks and longer transitions.
- `MediaFrame` e `SplitScreen` chamam `isVideoSource(src)` e trocam `<Img>` por `<Video>` sozinhas — basta passar um `.mp4`.
+ ### 6. Some media scenes accept only images
- `ZoomPanFrame`, `DeviceMockupZoom` e `CalloutSpotlight` renderizam **só `<Img>`**: passar vídeo resulta em quadro vazio, **sem erro**. Extraia um frame:
+ `MediaFrame` and `SplitScreen` choose `<Img>` or `<Video>` through `isVideoSource(src)`. `ZoomPanFrame`, `DeviceMockupZoom`, and `CalloutSpotlight` render only `<Img>`; passing video gives an empty frame without an error. Extract a still instead:
```bash
ffmpeg -ss 3.2 -i public/video.mp4 -frames:v 1 -q:v 2 public/video.jpg
```
- Mídia quadrada em cena 16:9 deixa tarja preta — use `fit="cover"`.
-
- ---
-
- ## As três armadilhas de 3D
-
- Descobertas portando uma cena three.js autoral para uma composição (`LampadaBrowserFlow`). Numeração separada de propósito: as seis acima são referenciadas por número no código, e renumerar quebraria os comentários.
-
- O padrão das três é o mesmo das seis — **falham em silêncio**. Pior: falham só no render. O Studio mostra a cena certa, o MP4 sai preto. Todas foram isoladas por bissecção, com `npx remotion still` a cada passo.
-
- ### 3D-1. `UnrealBloomPass` fora da última posição zera o buffer em headless
-
- Ele tem `needsSwap = false` e compõe o brilho de volta no próprio `readBuffer` — o mesmo alvo cuja textura acabou de amostrar no high-pass. É um feedback loop framebuffer↔textura.
-
- Chrome com GPU tolera. Chromium headless descarta o draw, e **todo pass depois dele lê preto**. Sem erro de shader, sem erro de GL, sem contexto perdido, sem exceção. Nada no console.
-
- Reproduz idêntico em `--gl=angle` e `--gl=swangle`: não é backend, é o headless.
-
- **Resolução**: bloom por último na cadeia. Se precisar de passes depois dele, não use `UnrealBloomPass`.
-
- ### 3D-2. Bloom por último reaplica tone mapping e sRGB
-
- Consequência de resolver a anterior. Ao desenhar na tela (`setRenderTarget(null)`) o three aplica `toneMapping` e `outputColorSpace` — é assim que ele trata qualquer material desenhado no canvas. Com um `OutputPass` antes na cadeia, a imagem leva a conversão **duas vezes**.
-
- O sintoma é uma imagem lavada, que passa por "escolha estética" até você medir. Neste projeto, o piso de madeira:
-
- ```
- (123,101,82) → (198,187,176)
- ```
-
- **Resolução**: remover o `OutputPass` e deixar o renderer converter uma vez, no draw final. Bônus: o bloom volta a operar em HDR linear, que é onde ele foi calibrado — os limiares originais da cena seguem válidos sem retoque.
-
- ### 3D-3. `SMAAPass` r168+ ignora o construtor e carrega textura assíncrona
-
- Duas coisas na mesma classe. A assinatura mudou: `new SMAAPass(largura, altura)` compila, roda e **ignora os dois argumentos**. E as lookup textures são montadas atribuindo um data URL a `new Image()` — assíncrono, como o próprio fonte do three anota.
-
- Num browser o loop de animação redesenha e em dois quadros ninguém percebe. Aqui é um tiro por frame.
-
- **Resolução**: MSAA no alvo do composer (`new WebGLRenderTarget(w, h, { samples: 4 })`) resolve sem pass nenhum. Note também que a doc do three pede que passes desse tipo venham **depois** do `OutputPass` — se o seu vem antes, ele está operando em HDR linear, não em sRGB.
-
- ### O que vale além do three.js
-
- Cena 3D só entra no vídeo se cada frame for função pura de `useCurrentFrame()`. `useFrame()` do React Three Fiber é loop de relógio e quebra o determinismo — e o defeito não aparece no Studio, só no MP4. Carregamento assíncrono (GLB, textura, shader) precisa de `useDelayRender()`, e o teto padrão de 30 s não cobre cena pesada.
-
- O teste que fecha a conta: renderize o mesmo frame duas vezes, em processos separados, e compare o hash. Se divergir, sobrou relógio em algum lugar.
-
- ---
-
- ## Regras do ambiente
-
- - **`typescript` fica fixo em `5.x`.** O TS 7 removeu `ts.sys` da API JS e o bundler do Remotion depende dela. Sintoma: `Cannot read properties of undefined (reading 'readFile')`.
- - **O alias `@/` precisa estar em dois lugares** — `tsconfig.json` (`paths`) e `remotion.config.ts` (`overrideWebpackConfig`). Só o tsconfig faz o typecheck passar e o render quebrar.
- - **Mexeu em `remotion.config.ts` ou `tsconfig.json`? Reinicie o Studio.** Ele lê esses arquivos apenas no boot; hot reload não pega, e o sintoma é tela em branco.
- - **Mapas não renderizam headless.** `maplibre-gl` exige WebGL2; o `delayRender` nunca resolve. A página existe em `pages-mapas-ia.tsx` como `PAGINA_MAPAS`, desregistrada de propósito.
-
- ## Ao usar o CLI do RemotionUI
-
- - **`remotion-ui add` de um *composition* edita o seu `Root.tsx`**, injetando `<Composition>` e imports fora das suas pastas, sem avisar. Sempre confira o Root depois.
- - **`remotion-ui init` cria um projeto novo aninhado** se não achar config. Escreva o `remotion-ui.json` à mão.
- - **Atualizar a lib sobrescreve arquivos já copiados.** A 0.7.0 reescreveu `transition-timing.ts` (derrubando quatro wipes) e trocou a API do `TerminalSimulator`. Reinstale os componentes afetados.
-
- ---
-
- ## Como escrever uma peça aqui
+ Square media in a 16:9 scene produces letterboxing; use `fit="cover"`.
- 1. **Consulte `catalog.json`** para achar o componente pela intenção (campo `quando`) e o caminho (`importa`).
- 2. **Cores e fonte vêm do tema.** Importe de `src/shared/theme.ts` (`PALETTE`, `MONO`, `RADIUS`). Nunca escreva hex no meio do componente — quebra a troca de marca em `brand.ts`.
- 3. **Passe `fontSize` explícito** sempre que o componente aceitar. Sem isso ele calcula a partir de `useVideoConfig()` e não escala.
- 4. **Uma coisa se move por vez.** Duas animações simultâneas viram ruído; veja as anotações em `src/recipes/recipes.tsx`.
- 5. **Verifique renderizando**, não lendo. `npx remotion still <Id> out/x.png --frame=N` e olhe a imagem. Metade dos defeitos aqui só apareceu assim.
- 6. **Rode `npm run catalog`** se acrescentou componente a uma página.
+ ## Three 3D failure modes
- ## Convenções
+ These were isolated while porting the authored three.js `LampadaBrowserFlow` scene. They can look correct in Studio and render black in the MP4.
- - UI e comentários em **português**; identificadores em **inglês** quando for API, em português quando for domínio do catálogo.
- - O selo `AUTORAL` é para o que foi escrito aqui, sem biblioteca. Não marque como `RemotionUI`/`Bits` algo que você escreveu — induz a procurar um componente que não existe.
- - **E o inverso é pior:** não marque como `AUTORAL` algo que veio de uma lib. Isso é reivindicar autoria de terceiro. Em caso de dúvida, o campo `lib` do `catalog.json` é a fonte da verdade — não a memória de quem escreve. Este projeto já errou nessa direção uma vez, num vídeo que falava justamente sobre procedência; o conserto foi cruzar cada selo contra o catálogo antes de publicar.
- - Grade do catálogo é fixa em **4 itens por página**. Cinco estouram a altura; crie outra página.
- - Peças da **vertical Brasil** (`src/remotion/brasil/`) recebem `escala`, nunca `fontSize` calculado de `useVideoConfig()` — é o que permite usá-las dentro de célula reduzida por transform sem cair na armadilha 1.
+ ### 3D-1. `UnrealBloomPass` must be last
- ---
+ Outside the final position it creates a framebuffer-to-texture feedback loop. Headless Chromium can discard the draw and every later pass reads black, with no shader, GL, or console error. The behavior reproduces with `--gl=angle` and `--gl=swangle`. Put bloom last; do not use it if later passes are required.
- ## Relação com as Agent Skills oficiais do Remotion
+ ### 3D-2. Final bloom can apply tone mapping and sRGB twice
- Em 14/08/2026 o Remotion publicou [Agent Skills](https://www.remotion.dev/docs/ai/skills) — 12 skills
- que ensinam um agente a usar o framework (`npx skills add remotion-dev/skills`). Elas resolvem um
- problema diferente do deste arquivo, e as duas coisas se somam:
+ Drawing with `setRenderTarget(null)` makes three apply `toneMapping` and `outputColorSpace`. With a preceding `OutputPass`, conversion happens twice and produces a washed-out image. In this project the wood floor changed from `(123,101,82)` to `(198,187,176)`. Remove `OutputPass` and let the renderer convert once during the final draw.
- | camada | pergunta que responde | onde vive |
- |---|---|---|
- | Agent Skills | **como** escrever Remotion corretamente | repositório oficial |
- | `catalog.json` | **o quê** já existe pronto neste projeto | aqui |
- | `AGENTS.md` | **onde quebra**, e como o defeito se manifesta | aqui |
+ ### 3D-3. `SMAAPass` r168+ is asynchronous
- ### Cobertura verificada das seis armadilhas
+ Its constructor dimensions are ignored and lookup textures load through `new Image()`. A browser animation loop hides this; a one-frame render does not. Use MSAA on the composer target instead: `new WebGLRenderTarget(w, h, { samples: 4 })`.
- (As três de 3D estão fora do escopo das skills oficiais: são comportamento do three.js e do Chromium headless, não do Remotion.)
+ For all 3D scenes, each frame must be a pure function of `useCurrentFrame()`. React Three Fiber `useFrame()` is clock-driven and non-deterministic in rendered output. Use `useDelayRender()` for async GLB, textures, and shaders. Render the same frame twice in separate processes and compare hashes.
- Leitura do conteúdo real das 12 skills (commit `9f0faa5`, 14/08/2026):
+ ## Environment rules
- | # | armadilha | nas skills oficiais |
- |---|---|---|
- | 5 | `TransitionSeries` dura menos que a soma | **coberta** — seção dedicada, com a conta |
- | 1 | `useVideoConfig()` reporta a composição | parcial — o mecanismo aparece como escape hatch; a consequência e o caso vertical, não |
- | 2 | `Sequence` renderiza `AbsoluteFill` | parcial — o fato está documentado; o sintoma no flex, não |
- | 3 | `defaultProps` passa por JSON | parcial — "valores precisam ser JSON-serializáveis"; JSX e o erro React #31, não |
- | 4 | ordem e tipo dos `effects` | **não coberta** — os ~50 efeitos aparecem numa lista plana, gerador e filtro misturados, e não há um único exemplo com mais de um efeito no array |
- | 6 | cenas que só renderizam `<Img>` | fora de escopo — `MediaFrame`, `ZoomPanFrame` e `CalloutSpotlight` são do RemotionUI, não do Remotion |
+ - Keep `typescript` on `5.x`; the Remotion bundler depends on `ts.sys`.
+ - Configure the `@/` alias in both `tsconfig.json` and `remotion.config.ts`.
+ - Restart Studio after changing either configuration file.
+ - `maplibre-gl` requires WebGL2 and does not render headlessly; `PAGINA_MAPAS` remains deliberately unregistered.
- O padrão das parciais: a documentação enuncia a **regra**, raramente o **modo de falha**. Saber que
- props precisam ser serializáveis não é o mesmo que reconhecer o erro #31 quando ele aparece. É essa
- distância que este arquivo cobre — e é por isso que ele continua útil mesmo com as skills instaladas.
+ ## RemotionUI CLI
- **Recomendação:** instale as skills oficiais *e* mantenha um `AGENTS.md` do seu projeto. Elas não competem.
+ - `remotion-ui add` for a composition edits `Root.tsx`; inspect it afterwards.
+ - `remotion-ui init` creates a nested project when it cannot find config; write `remotion-ui.json` yourself.
+ - Updating the library can overwrite copied files. Version 0.7.0 rewrote `transition-timing.ts` and changed `TerminalSimulator`; reinstall affected components deliberately.
- ---
+ ## Writing a component here
- ## Ambiente de agente (cloud)
+ 1. Consult `catalog.json` for intent (`quando`) and the import path (`importa`).
+ 2. Import colors and typography from `src/shared/theme.ts` (`PALETTE`, `MONO`, `RADIUS`); do not scatter hex values.
+ 3. Pass explicit `fontSize` when supported.
+ 4. Animate one thing at a time; see `src/recipes/recipes.tsx`.
+ 5. Verify by rendering: `npx remotion still <Id> out/x.png --frame=N`.
+ 6. Run the source generator's `npm run catalog` after adding a page component.
- Comentários explicam o código, nunca a conversa. Sem path de casa, sem e-mail pessoal — ver `.cursor/rules/no-private-leak.mdc`.
+ ## Attribution and catalog conventions
- Mapa do produto: `docs/ARQUITETURA.md`. Site: `https://victorsodre.github.io/remotion-agent-catalog/`.
+ - Keep identifiers stable. The public interface is English-first with a pt-BR option; generated catalog values remain source data.
+ - `AUTORAL` applies only to code written here. Never mark library code as authored, and do not infer provenance from memory: `catalog.json.lib` is authoritative.
+ - The catalog grid is four items per page. Add a page rather than a fifth item.
+ - Brazil vertical components (`src/remotion/brasil/`) receive `escala`, never `useVideoConfig()`-derived `fontSize`.
- **O `npm run catalog` citado no resto deste arquivo é o GERADOR da origem** (deriva o `catalog.json`
- de páginas `.tsx`). Esse gerador **não existe neste repo**. Não há script `catalog` no `package.json`
- daqui, de propósito. **Trate `catalog.json` como somente-leitura** (exceto o campo `preview`, que o
- `scripts/link-previews.mjs` preenche).
+ ## Relationship to official Remotion Agent Skills
- **Tooling** (`node`/`jq` já vêm no ambiente):
+ The official skills published on 2026-08-14 (commit `9f0faa5`) explain framework use. They complement this repository: Agent Skills explain **how** to write Remotion; `catalog.json` explains **what** is available; this guide explains **where it fails**. The official skills cover `TransitionSeries`; they partially cover composition dimensions, `Sequence`, and JSON-serializable props. Effect ordering and media-scene limitations remain project-specific.
- - `npm install` — Remotion 4.0.x + MCP + ajv. Update script: `if [ -f package.json ]; then npm install; fi`
- - `npm run validate` / `npm test` — verificação do índice (CI).
- - `npm run studio` — Remotion Studio em `http://localhost:3000`. **102 peças** do `catalog.json` em
- 1080×1080 (1:1), pastas por página. RemotionUI + Bits (`src/demos/bits.tsx`) + remocn
- (SoftBlurIn/ShimmerSweep/Confetti) + Marketing BR + vertical Brasil (`src/remotion/brasil/`,
- prop `escala`) + 4 autorais 3D/movimento **rodam o React**. Pasta `TextoEntrada` dura 180f.
- `SlotRoll` precisa de `color` explícita (default claro some no `THEME.ink`). Typewriter remocn
- ainda é card. `StaggeredMotion` abre com `delay` negativo senão o frame 0 fica vazio.
- Peças Brasil usam `ENTER = -16` no `useSpring` pelo mesmo motivo. Loops Marketing BR
- começam em `Sequence from={-36}`. Se o canvas do Studio parecer branco em `00:00.00`,
- aperta Space — o tema é papel claro, não tela vazia. `localhost:3000` é o Studio
- (`npm run studio`); o site Pages e `npm run web` (:8080) são o viewer estático.
- `remotion-bits@0.2.0` importa `culori` no bundle — o `package.json` daqui declara. Mexeu em
- `remotion.config.ts` ou `tsconfig.json`? Reinicie o Studio. `npx remotion-ui add` **edita
- `Root.tsx`** — depois de `npm run libs`, confira o Root.
- - `typescript` fica em **5.x** (o bundler do Remotion depende de `ts.sys`).
- - `npm run web` — visualizador em `http://localhost:8080/web/` (precisa de HTTP, não `file://`).
- Filtro de lib é um por vez (Todas / RemotionUI / Autoral / Bits / Remocn). Sem chip “vídeo real”.
- Prévias: `npm run previews:render` (VP8 540×540 em `web/previews/`). Uma peça:
- `npx remotion render PixQr-Autoral web/previews/PixQr-Autoral.webm --codec=vp8 --scale=0.5`
- e `node scripts/link-previews.mjs`. Pula mapas, Typewriter remocn e verticais.
- - `npx remotion-catalog find "<intenção>"` / `npm run mcp`
+ ## Repository environment
- **Não há lint.** `npm run validate` + `npm test` (+ `npm run typecheck` se mexer em `src/`) bastam.
+ Comments explain code, never conversations. Do not put home paths or personal email addresses in source. The source generator is not present here. Treat `catalog.json` as read-only except for the optional `preview` field maintained by `scripts/link-previews.mjs`.
- **Nuances de dados intencionais** (o validador trata como exceções — não "conserte"):
+ - `npm install` installs Remotion 4.0.x, MCP, and AJV.
+ - `npm run validate` and `npm test` verify the catalog and tooling.
+ - `npm run studio` serves 102 square compositions at `http://localhost:3000`.
+ - `npm run web` serves the static viewer at `http://localhost:8080/web/`.
+ - `npx remotion-catalog find "<intent>"` and `npm run mcp` expose the catalog to tools.
- - `Typewriter` existe duas vezes (RemotionUI e Remocn) — peças distintas. README conta como Remocn.
- - `AnimatedBarChart` multi-listada em `GraficosDados` e `AudioReativo`.
- - Trilhas compostas (`"UI + SimulatedCursor"`); token genérico `UI` é ignorado.
- - Quatro receitas de reel são stubs (sem `trilhas`).
+ There is no lint command. Run `npm run validate`, `npm test`, and `npm run typecheck` after TypeScript changes. Intentional data nuances include the two distinct Typewriter entries, the multi-listed `AnimatedBarChart`, composed tracks such as `UI + SimulatedCursor`, and four reel recipe stubs without `trilhas`.