Gera um deck VERTICAL 9:16 (1080x1920) pronto para gravar no OBS Studio, onde cada slide declara um de três layouts: camera (webcam ao vivo em tela cheia), split (animação quadrada estilo mira-squared em cima + câmera embaixo) e full (animação vertical estilo mira-vertical). A câmera do apresentador é embutida ao vivo no slide via getUserMedia (módulo mira/mira-camera.js), sem chroma key: no OBS basta capturar a janela. Sem câmera, a área vira verde chroma #00FF00 como plano B. Use SEMPRE que o usuário disser /mira-studio, deck para OBS, deck com câmera, slide com câmera, meio a meio, câmera embutida, deck para gravar vídeo comigo falando, ou pedir slides que misturam câmera e animação num vídeo vertical.
설치
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
Gera um deck VERTICAL 9:16 (1080x1920) pronto para gravar no OBS Studio, onde cada slide declara um de três layouts: camera (webcam ao vivo em tela cheia), split (animação quadrada estilo mira-squared em cima + câmera embaixo) e full (animação vertical estilo mira-vertical). A câmera do apresentador é embutida ao vivo no slide via getUserMedia (módulo mira/mira-camera.js), sem chroma key: no OBS basta capturar a janela. Sem câmera, a área vira verde chroma #00FF00 como plano B. Use SEMPRE que o usuário disser /mira-studio, deck para OBS, deck com câmera, slide com câmera, meio a meio, câmera embutida, deck para gravar vídeo comigo falando, ou pedir slides que misturam câmera e animação num vídeo vertical.
Skill: Mira Studio (9:16 com câmera embutida, pronto para OBS)
Cria decks verticais 9:16 para gravação de vídeo (Reels, Shorts, TikTok, videoaula) em que o apresentador aparece AO VIVO dentro do próprio slide. Cada slide declara um layout, e o usuário escolhe slide a slide, na conversa:
camera — a webcam preenche a coluna inteira (você falando).
split — quadrado 1:1 no topo com título + animação (padrão mira-squared) e a câmera preenchendo o resto embaixo (você + a metáfora).
full — animação vertical em tela cheia (padrão mira-vertical, sem câmera).
Fonte da verdade: o padrão desta skill está congelado no deck de referência (validado em 2026-07-11). Resolva o arquivo nesta ordem:
mira-templates/decks/mira-studio-demo/index.html (projeto com Mira instalado)
templates/decks/mira-studio-demo/index.html (repositório fonte do Mira)
Se nenhum existir, peça para rodar npx mira-animator update. Em dúvida sobre um valor exato, o resultado deve bater com o deck de referência.
O resultado, em uma frase
Uma coluna 9:16 central (laterais #333333) onde cada slide de CONTEÚDO (body > section) declara data-layout="camera|split|full" (capa e encerramento, sem data-layout, mantêm layout próprio): nas áreas .cam-area o módulo mira/mira-camera.js injeta o feed da webcam ao vivo (object-fit: cover, espelhado estilo selfie), nas áreas de animação valem as regras congeladas das skills irmãs, e o deck inteiro está pronto para o OBS capturar a janela sem chroma key.
Fluxo conversacional (como o usuário monta o deck)
O usuário descreve o roteiro e diz o layout de cada slide, por exemplo: "slide 1 só câmera, slide 2 meio a meio sobre X, slide 3 só animação sobre Y". Para cada slide:
camera → gere só <section data-layout="camera"><div class="cam-area"></div></section>. Nada de texto por cima (a fala é do apresentador).
split → título curto (máx. 6 palavras) + metáfora animada PENSADA PARA O QUADRADO (radial/orbital/hub rende mais) + .cam-area embaixo.
full → título curto + metáfora animada PENSADA PARA O RETRATO (eixo dominante vertical: fluxo desce, comparação empilha).
Se o usuário não declarar o layout de um slide, pergunte. A capa é opcional (deck de gravação pode começar direto no primeiro slide do roteiro); quando houver capa, ela segue a diretiva do título com text-wrap: balance.
Dimensão
O quadro é 9:16 cravado e generalista para a tela: --fmt-w: calc(100vh * 9 / 16), --fmt-h: 100vh (numa tela 1080p, ~607x1080; o OBS recorta a coluna e grava em 1080x1920). Não fixe pixels. Diferente do mira-vertical clássico (100vw/3), aqui a proporção exata importa porque a saída é vídeo 9:16.
As regras herdadas (não reinvente)
Área de animação do split: é um quadrado (aspect-ratio: 1/1, lado = largura da coluna) com área segura proporcional de 4.63% (50/1080), título dentro no topo e animação preenchendo o resto com casarPalco + fitToArea (código canônico em agents/mira-squared/SKILL.md). Vale o CRITÉRIO Nº 1: a animação preenche a maior parte do quadrado.
Slide full: título no topo (máx. 2 linhas, IIFE fitTitles), palco ocupando todo o resto, metáfora com eixo vertical, casarPalco + fitToArea (playbook de composição em agents/mira-vertical/SKILL.md).
Regra Zero: toda animação tem loop interno infinito com generation counter (window.__slugGen).
Fonte mínima: nenhum texto renderiza abaixo de 13px (SVG: font-size >= 24 para W = 960).
Cor: laranja da marca #FF904D; sem arco-íris.
Todo deck: os 5 módulos em mira/ referenciados antes de </body>, nesta ordem: mira-edit.js, mira-edit-free.js, mira-draw.js, mira-camera.js, mira-record.js. Libs vendoradas em assets/vendor/.
O módulo mira/mira-camera.js (contrato)
Fonte canônica em templates/authoring/mira-camera.js; copie para mira/ do deck. O que ele garante:
Stream único por sessão: um só getUserMedia({video, audio: false}) memoizado; todas as .cam-area compartilham o mesmo MediaStream (uma permissão, sem flicker). Cada .cam-area é um SINK <video> separado desse stream único — o stream nunca é duplicado, mas os elementos de vídeo sim.
Escalabilidade (muitos slides de câmera): com mais de 2 .cam-area, o módulo anexa o stream só nas câmeras do slide visível (e vizinhos, via IntersectionObserver) e solta ao sair, mantendo ~O(1) sinks ativos mesmo em decks de 10/30 slides de câmera (evita 30 texturas de vídeo ociosas). O stream/permissão continua único; só o srcObject dos <video> é ligado/desligado. Deck com ≤2 câmeras anexa tudo (custo desprezível). A gravação é indiferente ao total de slides: captura só a coluna do slide visível e encoda no Worker.
Vídeo sempre mudo: o áudio da gravação é do OBS/microfone.
Fallback verde chroma: sem câmera (contexto file://, permissão negada, sem dispositivo), cada .cam-area ganha .cam-fallback (fundo #00FF00 PURO, nada por cima) e um aviso discreto aparece FORA da área, sumindo em 5s. Plano B: filtro Chroma Key do OBS.
Tecla C: alterna espelhamento (body.cam-mirror, padrão LIGADO, estilo selfie). Fica quieto durante os modos E/P e digitação.
Encerramento: tracks paradas no pagehide.
A skill NÃO reimplementa nada disso: só marca as áreas com .cam-area e inclui o script.
O módulo mira/mira-record.js (gravação nativa, sem OBS)
Fonte canônica em templates/authoring/mira-record.js; copie para mira/ do deck. Painel de gravação no lado DIREITO da tela (fora da coluna, portanto fora do vídeo):
Grava SOMENTE a área dos slides: captura a própria aba (getDisplayMedia com preferCurrentTab) e tenta recortar a track para a coluna 9:16 via Region Capture (CropTarget.fromElement + track.cropTo). O Worker nunca confia só na Promise: valida displayWidth/displayHeight de cada frame. Entrada 9:16 segue direta; full-tab é recortada pelas coordenadas normalizadas no OffscreenCanvas; se a proporção do frame já não corresponder ao viewport congelado, a fração incompatível é descartada e entra um crop central 9:16 seguro. Nunca estique o frame. Saída H.264 avc1 com resolução constante por sessão: 1080x1920 em Alta ou a resolução 9:16 nativa em Desempenho.
Pipeline em Worker (desempenho — a razão de não travar): todo o caminho captura→escala→encode→mux roda fora do main thread, num Worker dedicado. MediaStreamTrackProcessor puxa VideoFrames direto da track recortada (sem <video>, sem requestVideoFrameCallback, sem canvas no main thread) e o readable é transferido ao Worker; lá dentro o VideoEncoder (fixo em 1080x1920, escala interna; fallback OffscreenCanvas no próprio Worker) codifica com backpressure (encodeQueueSize>=2 descarta), timestamps VFR preservados, keyframe a cada 2 s, e o mp4-muxer faz o mux (cross-track-offset para o A/V). O Worker é criado por Blob URL de dentro do próprio módulo — não há arquivo .js novo para copiar no deck. O main thread só renderiza a página (câmera ao vivo + animações) e recebe o MP4 pronto no fim. É isso que mantém câmera e slides fluidos durante a gravação, com CPU ou GPU — o encoder de hardware só acelera a compressão, não a preparação do frame.
Três informações separadas, sem promessa de NVENC: (1) GPUs instaladas, inventário Win32 vindo de /__mira/gpus; (2) Renderer ativo, detectado pelo WebGL e escolhido pelo Chrome/Windows; (3) encoderAuto / Hardware preferido / Software (CPU), que mapeia para hardwareAcceleration (no-preference/prefer-hardware/prefer-software). GPUs instaladas NUNCA viram opções do encoder: uma página não escolhe a placa física nem confirma NVENC. O teste real de encode confirma apenas que a preferência foi aceita. Em file://, o painel explica que o inventário requer o launcher/localhost. Se o renderer continuar na integrada, oriente Configurações do Windows > Sistema > Tela > Gráficos para o chrome.exe; não automatize configuração do sistema. O mux MP4 usa assets/vendor/mp4-muxer.js; áudio do microfone usa AAC quando suportado.
Fallback de compatibilidade: navegador sem WebCodecs/MediaStreamTrackProcessor/OffscreenCanvas cai no caminho antigo — MediaRecorder sobre um canvas fixo 1080x1920 alimentado por requestVideoFrameCallback (MP4/avc1 12 Mbps, ou WebM com aviso). Só roda quando não há o pipeline em Worker.
Métricas reais ao vivo: durante a gravação o painel mostra fps efetivo · % descartado · fila do encoder · Mbps real · MB (reportado pelo Worker). É o diagnóstico honesto — se o % descartado sobe ou o fps cai de 20, é o sinal para trocar para o modo Desempenho ou checar chrome://gpu. Abaixo de ~20 fps por 3s o painel também avisa uma vez (trecho de tela estática não conta como lentidão). Em notebook, grave na tomada.
Diagnóstico de recorte e navegação: o painel registra input, crop, output, caminho direct/canvas, maior long task, gap de rAF e gap PTS na janela de cada mira-navigation. Ao finalizar, o botão salvar diagnóstico JSON permite anexar as métricas à evidência. Os limites Windows são long task ≤50 ms e gap PTS ≤100 ms na troca.
Qualidade × Desempenho: seletor no painel. Alta = 1080×1920 (padrão de reels). Desempenho = grava na resolução NATIVA da coluna (~608×1080 num display comum), ~3× menos pixels a codificar — a alavanca real para máquina fraca, independente de CPU/GPU. Resolução constante mantém o MP4 avc1 válido; o bitrate é escalado proporcionalmente.
Gravação longa — direto no disco, sem teto (padrão): a chave salvar direto no disco (ligada por padrão) faz o Worker escrever o MP4 no arquivo enquanto grava (File System Access + FileSystemWritableFileStreamTarget do mp4-muxer, com fastStart: false). Nada acumula em RAM: a gravação dura o que o disco aguentar, e o finalize() deixa de copiar centenas de MB. O usuário escolhe o arquivo uma vez, antes de começar (o showSaveFilePicker exige ativação do usuário, por isso vem antes do seletor de captura); ao parar, o arquivo já está lá — não há download. Preço honesto: sem o buffer inteiro em mãos, o índice do MP4 (moov) vai para o fim do arquivo. Parar normalmente escreve o índice e o vídeo abre em qualquer player; um travamento do navegador no meio deixa um MP4 com os dados e sem índice (ilegível sem reparo). Se o seletor for cancelado, a gravação nem começa (e o arquivo de 0 byte é removido).
Gravação em memória (fallback): sem File System Access (file://, outros navegadores) a chave fica desligada e cinza, e o mux volta a ser in-memory — com o teto real do ArrayBuffer (~2 GB): avisa por volta de ~384 MB e PARA sozinha perto de ~512 MB, que a 12 Mbps são uns 6 minutos. Nesse modo, para clipes longos, grave em partes.
Microfone opcional: toggle no painel, mixado na gravação (a câmera já está composta no slide).
Controles: botão Gravar/Parar no painel ou tecla R (quieta nos modos E/P e digitação). Ao iniciar, o usuário escolhe "Esta guia" no seletor do navegador. O arquivo baixa sozinho ao parar (mira-reels-<timestamp>.mp4, ou -PARCIAL quando houve perda no caminho de encode/leitura/flush).
Recorte da coluna: tenta Element Capture (restrictTo na seção visível) quando o deck declara window.__miraElemCapture; a track já sai 9:16 e o teleprompter fica fora do vídeo. O restrictTo é reaplicado a cada troca de slide (evento mira-navigation e scroll), senão o vídeo congela no slide anterior. Sem a flag ou sem suporte, cai no Region Capture de sempre.
O painel some em telas estreitas (a coluna ocupa tudo) e nunca aparece na gravação.
O OBS continua como alternativa (captura de janela + recorte); a gravação nativa é o caminho sem instalação.
Teleprompter que não entra no vídeo (padrão de todo deck mira-studio)
Todo deck nasce com o teleprompter em duas peças, mais o editor de overlays e a persistência no arquivo. Os blocos canônicos estão no deck de referência; copie-os como estão.
Painel lateral (#mira-prompter, tecla T): fica na margem cinza, FORA da coluna. É o editor: as quatro chaves, o texto do slide (#mp-body, contenteditable) e o botão "Salvar no arquivo". Some durante a gravação (html[data-mira-recording] #mira-prompter { display: none }) — quem se lê no ar é o overlay.
Overlay central (#tp-ov, tecla O): o retângulo que o apresentador lê, por cima da coluna (fundo preto 60%, texto branco 60%). É irmão das <section>, nunca filho — é exatamente isso que permite excluí-lo do vídeo.
Texto por slide: vem do roteiro.md na raiz do deck (seção abaixo), não do HTML. Editar no painel reflete no overlay na hora, grava no .md e persiste em localStorage['mira-tp-text'] (cópia de trabalho). O texto troca ao navegar (scroll + mira-navigation).
Element Capture (tecla G): a gravação nativa restringe a captura à subárvore da seção visível (RestrictionTarget.fromElement + track.restrictTo), então tudo que não é descendente dela — o overlay e o painel — não é pintado no vídeo, mesmo sobreposto. O deck liga o recurso declarando window.__miraElemCapture = true; sem essa flag o mira-record.js usa o Region Capture de sempre.
Editor genérico de overlays (.me-ov, tecla E): qualquer camada sobre a câmera marcada com class="me-ov" data-me-chrome data-me-key="<id>" (mais um filho <div class="me-ov-grip" data-me-chrome></div>) vira movível e redimensionável no modo E. O data-me-chrome é obrigatório: sem ele o mira-edit-free disputa o mesmo clique e seleciona o <img> interno em vez do grupo.
Salvar no arquivo (botão ou Ctrl+S): grava o estado editável de LAYOUT (posições e tamanhos dos overlays, sem o texto do roteiro) no bloco <script id="mira-studio-state" type="application/json"> do próprio index.html — em localhost por POST /__mira_save, em file:// pela File System Access API. No load, um IIFE semeia o localStorage a partir desse bloco: o arquivo é a fonte da verdade, não o navegador. No template o bloco começa {}.
Onde cada overlay mora (a regra de ouro): o que deve entrar no vídeo (logo, selo, animação sobre a câmera) fica DENTRO da <section>; o que não deve (teleprompter, painéis) fica FORA dela, como filho direto de body.
Restrições honestas (diga isto ao usuário, não prometa mais)
Excluir o teleprompter do vídeo só funciona no gravador nativo (tecla R). No OBS não: ele grava os pixels da janela, e nada exclui um overlay. Para gravar no OBS, use só o painel lateral (que já fica fora da coluna recortada).
Element Capture exige Chrome/Edge desktop recente e contexto seguro (localhost ou https). Sem suporte, o deck cai no Region Capture e o overlay some durante a gravação (fallback no CSS, via html[data-mira-recording]:not([data-mira-elemcapture])), para não vazar.
body > section { isolation: isolate } é pré-requisito: sem stacking context o Chrome aceita o restrictTo e não emite frame — o MP4 sai vazio.
Salvar por localhost exige o servidor com os endpoints (/__mira_save, /__mira_meta): num deck antigo, reinicie o launcher depois de atualizar o mira-studio-server.cjs.
Roteiro externo roteiro.md (todo deck de gravação nasce com um)
O roteiro NÃO mora mais dentro do HTML. Todo deck gerado leva um roteiro.md na raiz, ao lado do index.html: é dele que saem os slides (layout e título) e a fala de cada slide. O apresentador escreve o roteiro no editor de texto que quiser, versiona em git, e vê o resultado no deck aberto sem recarregar.
Gramática (uma linha por cabeçalho):
<intro: texto livre, documenta a gramática para o usuário>
## Slide 1 | capa | Um roteiro, *três formatos*
Fala do apresentador neste slide.
## Slide 2 | camera
Fala do apresentador neste slide.
layout (obrigatório): capa, camera, split ou full, comparado em minúsculas. Valor desconhecido cai em camera, o layout mais simples.
Título: vale em capa, split e full; ignorado em camera. *entre asteriscos* vira <span class="accent">, montado por fragmento (nunca innerHTML).
Sem campo de animação. As animações do mira-studio são AUTORAIS (metáfora escrita à mão por slide, não montada a partir de uma lista de itens). O builder cria só o palco vazio svg#sv-slide-N, com N = posição do slide no arquivo, e cada animação escrita à mão se prende ao seu palco. Palco sem animação fica vazio em vez de quebrar; avise o usuário quando o roteiro tiver mais slides do que animações autoradas.
O número do cabeçalho é rótulo, não índice: o mapeamento é sempre pela ordem de aparição. Numeração duplicada ou fora de ordem não desloca texto nenhum.
O que sincroniza e o que não:
Dado
Fonte da verdade
Quando vale
Layout, título
roteiro.md
lido uma vez, no load (mudou, recarregue)
Texto da fala
roteiro.md
ao vivo, nos dois sentidos
Posição/tamanho dos overlays
bloco #mira-studio-state no index.html
Ctrl+S
Cópia de trabalho do texto
localStorage
entre polls
Precedência no load: roteiro.md > localStorage > array SCRIPT[] embutido. O texto não entra no #mira-studio-state: duas fontes da verdade fariam o seed do load restaurar texto velho por cima do arquivo.
Como o deck consome (blocos canônicos no deck de referência):
Um IIFE antes das animações e dos <script defer> faz a leitura síncrona do .md (os módulos de câmera, gravação e edição precisam das <section> já no DOM), guarda texto bruto, intro e cabeçalhos, monta as seções e as insere antes do painel do teleprompter, removendo as estáticas.
As <section> estáticas do HTML continuam lá como fallback: em file://, sem servidor ou com parse vazio, elas SÃO o deck. A ausência do roteiro nunca produz deck em branco.
Sincronização: poll de leitura a cada 1,5 s, escrita com debounce de 800 ms, com quatro guards que não são opcionais: (1) escrita em voo ou agendada trava o poll, senão ele desfaz o que você acabou de digitar; (2) gravação em andamento trava o poll, para não piscar texto dentro do vídeo; (3) campo com foco recebe estado e overlay mas não é sobrescrito, senão o cursor salta; (4) conteúdo idêntico aborta poll e escrita.
A escrita de volta remonta o arquivo com a intro e os cabeçalhos capturados no load mais os textos atuais. Cabeçalho nunca é reescrito a partir do estado do deck.
Arquivo ausente (404) é recriado uma vez a partir dos slides e textos embutidos.
Restrição honesta: a escrita depende do POST /__mira_save. Ele existe no mira/mira-studio-server.cjs (launcher, que também cria o arquivo quando falta) e no lib/mira-serve.js (grava, mas só em arquivo que já existe). Em file:// não há leitura nem escrita: o deck usa os slides embutidos, sem erro no console e sem sincronizar.
Chrome autoral dentro da <section> (logo, selo, .me-ov que deve entrar no vídeo) precisa ser emitido pelo builder, senão a reconstrução do load o descarta.
Fontes canônicas em templates/studio/: o .bat vai para a RAIZ do deck (diretiva: raiz = index.html + launchers) e o mira-studio-server.cjs para a pasta mira/. O ciclo de vida segue o padrão comprovado do Mira Remote/mesa tática: Node em primeiro plano, navegador aberto pelo servidor somente depois de listen() confirmar readiness.
Sobe mira/mira-studio-server.cjs (Node puro): serve o deck em http://127.0.0.1:8123 (ou próxima porta livre), expõe /__mira/health e /__mira/gpus. A consulta Win32 é uma Promise cacheada com estado loading/ready/error; o painel usa retry limitado. Falha de GPU não torna o HTTP do deck indisponível. O servidor também expõe GET /__mira_meta (caminho absoluto do alvo, usado pelo rótulo do mira-edit) e POST /__mira_save (grava o HTML no disco: é o "Salvar no arquivo" do deck e o Salvar da barra do mira-edit). O /__mira_save só aceita .html/.htm, só dentro do root servido (trava de path traversal) e até ~25 MB. O MIME inclui .pdf (slide full com PDF em <iframe> fica em branco sem ele).
Só depois do listen() bem-sucedido, o próprio servidor abre um Chrome dedicado (--user-data-dir em %LOCALAPPDATA%\mira-studio\) com --force-high-performance-gpu. A flag é uma preferência, não garantia: Windows/driver escolhem o adaptador final e o painel separa GPUs instaladas, renderer ativo e encoder hardware preferido. Nenhuma escrita em registro ou configuração do Windows.
O Node permanece em primeiro plano até Ctrl+C. Não use start /wait chrome.exe nem mate o servidor quando o comando Chrome retornar: se o mesmo perfil já estiver aberto, o Chrome encaminha a URL ao processo existente e retorna imediatamente.
stdout/stderr ficam visíveis e os eventos do servidor também são anexados a mira/mira-studio.log. Node ausente, portas esgotadas ou servidor ausente deixam mensagem acionável; não abra uma aba destinada a loading.
Sem GPU dedicada o launcher é inócuo (a flag aponta para a única GPU); o deck continua funcionando por index.html/serve normal.
<styleid="mira-formato-multi">/* Coluna 9:16 cravada, generalista para a tela (saída OBS 1080x1920). */:root { --fmt-w: calc(100vh * 9 / 16); --fmt-h: 100vh; }
html { background: #333333; }
body { background: #333333; display: flex; flex-direction: column; align-items: center; }
body > section {
position: relative;
width: var(--fmt-w); height: var(--fmt-h); min-height: var(--fmt-h);
overflow: hidden; background: var(--mira-bg, #0d0d0f);
display: flex; flex-direction: column;
/* stacking context: EXIGIDO pelo Element Capture. Sem isto o Chrome aceita
o restrictTo e NÃO emite frame nenhum (o MP4 sai vazio). Não remova. */isolation: isolate;
}
/* camera: webcam na coluna inteira */section[data-layout="camera"].cam-area { flex: 11 auto; min-height: 0; }
/* split: quadrado 1:1 no topo (área segura proporcional 50/1080) + câmera no resto */section[data-layout="split"].split-top {
width: 100%; aspect-ratio: 1 / 1; flex: 00 auto;
display: flex; flex-direction: column; padding: 4.63%;
}
section[data-layout="split"].split-toph2 { flex: 00 auto; }
section[data-layout="split"].cam-area { flex: 11 auto; min-height: 0; }
/* full: animação vertical na coluna inteira */section[data-layout="full"].full-wrap {
flex: 11 auto; min-height: 0;
display: flex; flex-direction: column; padding: 4.63%4.63%3%;
}
section[data-layout="full"]h2 { flex: 00 auto; }
/* palco: preenche todo o resto (viewBox casado em runtime pelo casarPalco) */.anim-stage { flex: 11 auto; min-height: 0; width: 100%; }
.anim-stagesvg { width: 100%; height: 100%; display: block; }
</style>
Injete também o IIFE fitTitles (auto-ajuste de título para máx. 2 linhas) e a navegação por teclado quieta durante E/P (ambos no deck de referência).
Transição padrão: dissolve fora da gravação
Todo deck mira-studio nasce com a transição dissolve (View Transitions same-document, o mesmo padrão do mira-transition-dissolve) aplicada DIRETO no index.html, sem arquivo -dissolve separado:
Bloco CSS marcado === DISSOLVE: ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.55s; } e um view-transition-name único para CADA elemento de UI fixa (#mrc-panel, .cam-notice e qualquer outro position: fixed), senão a UI pisca no crossfade.
Na navegação, helper dissolve(jump) com fallback (if (document.startViewTransition) ... else jump()) e scrollIntoView({ behavior: 'instant' }), nunca 'smooth' ou 'auto' dentro da transição.
Durante a gravação nativa, mira-record.js marca <html data-mira-recording="true">; nesse estado a navegação chama jump() diretamente, sem startViewTransition. Antes do salto, emita window.dispatchEvent(new CustomEvent('mira-navigation', {detail:{from,to,at:performance.now()}})) para abrir a janela de métricas. Os snapshots old/new disputam a captura da própria guia e podem criar hitch/gap no MP4. Ao terminar, o atributo é removido e o dissolve volta automaticamente.
Os dois blocos estão no deck de referência; detalhes e regras completas em agents/mira-transition-dissolve/SKILL.md.
Teclas do deck (documente na entrega)
T painel lateral · O overlay central · G Element Capture · E editar (mover/redimensionar overlays) · Ctrl+S salvar no arquivo · R gravar · C espelhar câmera · P pintar · setas navegam. T/O/G ficam quietas nos modos E/P e durante a digitação.
Passos
Colher o roteiro. Liste com o usuário os slides e o layout de cada um. Sem layout declarado, pergunte. O que ele vai FALAR em cada slide vai para o roteiro.md, não para dentro do HTML. Diga a ele, em uma linha, que dá para editar esse arquivo com o deck aberto: o texto aparece no teleprompter em cerca de 1,5 s.
Criar a estrutura.decks/<nome>/ com index.html, roteiro.md (um bloco ## Slide por slide combinado, já com a fala do usuário no corpo, e a intro documentando a gramática e os layouts DESTE deck), mira/ (edit, edit-free, draw, camera, record copiados de templates/authoring/ + mira-studio-server.cjs de templates/studio/), assets/vendor/mp4-muxer.js (de templates/vendor/, obrigatório para os encoders do painel), assets/vendor/d3.v7.min.js quando houver animação, e mira-studio-windows.bat na raiz (de templates/studio/, launcher com preferência de alto desempenho).
Gerar os slides. Um bloco ## Slide no roteiro.md por slide e, no HTML, o body > section equivalente (fallback de file://) com data-layout correto; .cam-area nas áreas de câmera; animações nativas da geometria (quadrado no split, retrato no full) com casarPalco + enquadramento uma vez sobre a parte estática (reenquadrar a cada frame faz o palco reescalar junto com o que se move) e loop interno. Todo callback de d3.timer vai dentro de try/catch: uma exceção num deles congela a FILA inteira de timers do d3.
Injetar os blocos canônicos.<style id="mira-formato-multi"> (com o isolation: isolate), o builder do roteiro.md (antes das animações e dos <script defer>), fitTitles, navegação com dissolve (transição padrão), o teleprompter completo (painel + overlay + sincronização com o .md + SCRIPT[] só como fallback + chaves + editor .me-ov + bloco #mira-studio-state vazio + seed + salvar/Ctrl+S) e os cinco <script defer src="mira/..."> antes de </body> (mira-edit.js → mira-edit-free.js → mira-draw.js → mira-camera.js → mira-record.js).
Verificar. Servido em localhost: câmera nas áreas certas, permissão pedida uma vez, animações preenchendo, títulos em máx. 2 linhas. Painel lateral com as chaves T/O/G/E sincronizadas; editar o texto reflete no overlay; editar o roteiro.md no editor externo aparece no deck em ~1,5 s e digitar no painel grava no .md sem tocar na intro nem nos cabeçalhos; Ctrl+S grava posições e tamanhos, que sobrevivem ao reload. Gravando com Element Capture: ON, o overlay continua visível para você e some do MP4 — e navegar entre slides durante a gravação não pode congelar o vídeo. Em file://: áreas verdes #00FF00 puras.
Reportar. Caminho do deck, o roteiro.md como lugar de escrever a fala (editável com o deck aberto), layout de cada slide (uma linha por slide), a gravação nativa (tecla R; encoder Auto/Hardware preferido/Software no painel; launcher Windows para inventário e preferência de alto desempenho) e a receita OBS como alternativa: servir com node lib/mira-serve.js decks/<nome> (ou npx mira-animator serve), Chrome em tela cheia, Captura de Janela no OBS, recorte na coluna, gravação 1080x1920.
Edge cases (do mais comum ao menos)
Aberto em file://:getUserMedia não existe; todas as áreas de câmera ficam verde chroma e o aviso ensina a servir em localhost. O deck continua navegável e gravável (keying manual).
Permissão negada / sem webcam: mesmo fallback verde; aviso específico.
Webcam 16:9 numa área 9:16 ou quadrada:object-fit: cover corta as sobras, nunca distorce.
Título longo no split/full:fitTitles reduz até 2 linhas; o casarPalco re-casa o palco à altura restante.
Vários slides com câmera: todos compartilham o MESMO stream; trocar de slide não repete o prompt.
Tecla C durante edição (modo E) ou digitação: o módulo ignora, sem conflito.
Deck sem nenhuma .cam-area: o módulo fica inerte (não pede permissão à toa).
Checklist
Os que mais falham (cheque primeiro):
Cada slide com o data-layout que o usuário pediu, na ordem do roteiro.
split: quadrado 1:1 exato no topo, animação PREENCHENDO o quadrado (Critério nº 1), câmera no resto.
full: animação vertical preenchendo o palco, sem faixa fina.
Câmera: stream único, mudo, espelhado por padrão, tecla C alternando.
Fallback: em file:// as áreas ficam #00FF00 PURO, sem texto por cima.
Teleprompter (o que mais quebra aqui):
body > section { isolation: isolate } presente (sem ele o Element Capture grava vídeo VAZIO).
Painel #mira-prompter e overlay #tp-ov-wrapfora das <section>; overlays que devem entrar no vídeo, dentro.
roteiro.md na raiz, com intro documentando a gramática e um ## Slide por <section>; os layouts citados na intro batem com os que o deck aceita.
Slides nascem do .md; sem o arquivo (ou em file://), o deck sobe com os slides padrão embutidos, sem erro no console.
Numeração duplicada ou fora de ordem nos cabeçalhos não desloca os textos (mapeamento por ordem de aparição).
Editar o .md aparece no deck em ~1,5 s; digitar no painel grava no .md com intro e cabeçalhos intactos; digitação contínua não tem o cursor roubado pelo poll.
Durante a gravação o poll fica parado; .md apagado com o deck aberto é recriado.
Builder do roteiro ANTES das animações e dos <script defer>; animação autoral presa ao palco sv-slide-N.
SCRIPT[] continua no HTML só como fallback, e mira-tp-text NÃO entra no #mira-studio-state.
window.__miraElemCapture = true declarado (é o opt-in que liga o Element Capture no mira-record).
Bloco #mira-studio-state começa {}, com o IIFE de seed antes de qualquer leitor de localStorage.
.me-ov com data-me-chrome (senão o mira-edit-free briga pelo clique) e .me-ov-grip.
Restrições honestas ditas ao usuário: só no gravador nativo (R), não no OBS; Chrome desktop recente; localhost/https.
Transição dissolve aplicada no index.html (bloco === DISSOLVE + dissolve() na navegação; UI fixa com view-transition-name, inclusive #mira-prompter e #tp-ov-wrap).
Durante recording, navegação instantânea sem startViewTransition e evento mira-navigation emitido antes do salto.
Coluna calc(100vh * 9/16) x 100vh, laterais #333333, centralizada via flex.
mira/ com os 5 módulos e tags na ordem certa antes de </body>.
assets/vendor/mp4-muxer.js presente (encoders do painel), mira-studio-windows.bat na raiz e mira/mira-studio-server.cjs (launcher + inventário real de GPUs).
Painel separa inventário de GPUs, renderer ativo e preferência do encoder; diagnóstico JSON disponível ao final.
Loop interno em toda animação; generation counter presente.
Nenhum texto abaixo de 13px renderizados; título máx. 2 linhas.
Capa (se houver) com text-wrap: balance; sem travessão em texto visível.