primocode 10.12.0 → 10.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +74 -12
  2. package/bin/primocode.js +148 -8
  3. package/lib/act.js +48 -1
  4. package/lib/catalogo.js +12 -1
  5. package/lib/config.js +1 -0
  6. package/lib/estudio/assinaturas/gerativo.json +1 -1
  7. package/lib/estudio/camadas.js +4 -0
  8. package/lib/estudio/cota.js +150 -0
  9. package/lib/estudio/dados/projetos/clube-ad5.json +105 -0
  10. package/lib/estudio/dados/projetos/clube-ae-sem-memoria.json +107 -0
  11. package/lib/estudio/dados/projetos/clube-agent.json +120 -0
  12. package/lib/estudio/dados/projetos/clube-antes-e-agora.json +86 -0
  13. package/lib/estudio/dados/projetos/clube-apresentando-clube.json +110 -0
  14. package/lib/estudio/dados/projetos/clube-bluevisa.json +106 -0
  15. package/lib/estudio/dados/projetos/clube-caught-in-the-fire.json +127 -0
  16. package/lib/estudio/dados/projetos/clube-chat-cliente-satisfeito.json +98 -0
  17. package/lib/estudio/dados/projetos/clube-chatgpt-anuncios.json +120 -0
  18. package/lib/estudio/dados/projetos/clube-claude-dj.json +140 -0
  19. package/lib/estudio/dados/projetos/clube-claude-mcp.json +142 -0
  20. package/lib/estudio/dados/projetos/clube-cleanui.json +106 -0
  21. package/lib/estudio/dados/projetos/clube-clinica-take-unico.json +128 -0
  22. package/lib/estudio/dados/projetos/clube-clinica.json +104 -0
  23. package/lib/estudio/dados/projetos/clube-clube-opener.json +125 -0
  24. package/lib/estudio/dados/projetos/clube-clube.json +108 -0
  25. package/lib/estudio/dados/projetos/clube-codigo.json +101 -0
  26. package/lib/estudio/dados/projetos/clube-distilbook.json +104 -0
  27. package/lib/estudio/dados/projetos/clube-drive-upload.json +115 -0
  28. package/lib/estudio/dados/projetos/clube-dub.json +119 -0
  29. package/lib/estudio/dados/projetos/clube-enxovaly.json +105 -0
  30. package/lib/estudio/dados/projetos/clube-fecha7.json +118 -0
  31. package/lib/estudio/dados/projetos/clube-filatov.json +128 -0
  32. package/lib/estudio/dados/projetos/clube-frame.json +108 -0
  33. package/lib/estudio/dados/projetos/clube-glass.json +118 -0
  34. package/lib/estudio/dados/projetos/clube-letra-em-apps.json +84 -0
  35. package/lib/estudio/dados/projetos/clube-letra-globo-3d.json +111 -0
  36. package/lib/estudio/dados/projetos/clube-letra-nos-apps.json +146 -0
  37. package/lib/estudio/dados/projetos/clube-letra-seis-mundos.json +100 -0
  38. package/lib/estudio/dados/projetos/clube-lula-3.json +146 -0
  39. package/lib/estudio/dados/projetos/clube-morph.json +119 -0
  40. package/lib/estudio/dados/projetos/clube-motion-designer.json +115 -0
  41. package/lib/estudio/dados/projetos/clube-motion-pacote.json +112 -0
  42. package/lib/estudio/dados/projetos/clube-movimento.json +103 -0
  43. package/lib/estudio/dados/projetos/clube-not-like-us.json +117 -0
  44. package/lib/estudio/dados/projetos/clube-pinterest-inspiracao.json +86 -0
  45. package/lib/estudio/dados/projetos/clube-pixel-e-tinta.json +108 -0
  46. package/lib/estudio/dados/projetos/clube-ponte.json +118 -0
  47. package/lib/estudio/dados/projetos/clube-processo-edit-tonal.json +126 -0
  48. package/lib/estudio/dados/projetos/clube-processo-motion-100-ia.json +109 -0
  49. package/lib/estudio/dados/projetos/clube-promo.json +114 -0
  50. package/lib/estudio/dados/projetos/clube-proposito.json +115 -0
  51. package/lib/estudio/dados/projetos/clube-rankhog.json +128 -0
  52. package/lib/estudio/dados/projetos/clube-recursao.json +116 -0
  53. package/lib/estudio/dados/projetos/clube-revelar.json +118 -0
  54. package/lib/estudio/dados/projetos/clube-robo.json +125 -0
  55. package/lib/estudio/dados/projetos/clube-shelby-ep2.json +144 -0
  56. package/lib/estudio/dados/projetos/clube-shelby.json +97 -0
  57. package/lib/estudio/dados/projetos/clube-solar-eclipse.json +96 -0
  58. package/lib/estudio/dados/projetos/clube-soldier.json +128 -0
  59. package/lib/estudio/dados/projetos/clube-som-de-cena.json +108 -0
  60. package/lib/estudio/dados/projetos/clube-spotify-candidatura.json +112 -0
  61. package/lib/estudio/dados/projetos/clube-tda-app.json +118 -0
  62. package/lib/estudio/dados/projetos/clube-tda-ia.json +75 -0
  63. package/lib/estudio/dados/projetos/clube-telegram-chat.json +118 -0
  64. package/lib/estudio/dados/projetos/clube-telegram-ilha.json +106 -0
  65. package/lib/estudio/dados/projetos/clube-tennessee-nike.json +132 -0
  66. package/lib/estudio/dados/projetos/clube-tudo-se-move.json +112 -0
  67. package/lib/estudio/dados/projetos/clube-uiad.json +107 -0
  68. package/lib/estudio/dados/projetos/clube-viralgrow.json +108 -0
  69. package/lib/estudio/dados/projetos/clube-whose-side.json +140 -0
  70. package/lib/estudio/dados/projetos/clube-youtube-inscrever.json +108 -0
  71. package/lib/estudio/dados/projetos/elementos-do-site-em-movimento.json +49 -0
  72. package/lib/estudio/dados/projetos/explainer-narrado.json +51 -0
  73. package/lib/estudio/dados/projetos/lancamento-de-produto.json +51 -0
  74. package/lib/estudio/dados/projetos/motion-so-com-sons.json +50 -0
  75. package/lib/estudio/dados/projetos/titulo-em-after-effects.json +51 -0
  76. package/lib/estudio/dados/projetos/tour-de-saas.json +50 -0
  77. package/lib/estudio/dados/projetos-ae/antes-e-agora/ae/cenas.jsx +733 -0
  78. package/lib/estudio/dados/projetos-ae/antes-e-agora/assets/fazer_assets.py +88 -0
  79. package/lib/estudio/dados/projetos-ae/letra-em-apps/ae/cenas.jsx +402 -0
  80. package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/preparar_assets.py +17 -0
  81. package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/svg_para_ae.py +25 -0
  82. package/lib/estudio/dados/projetos-ae/letra-nos-apps/BRIEF-AGENTES.md +31 -0
  83. package/lib/estudio/dados/projetos-ae/letra-nos-apps/README.md +53 -0
  84. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ROTEIRO-CENAS.md +69 -0
  85. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/A-agenda.jsx +148 -0
  86. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/B-docs.jsx +174 -0
  87. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/C-alttab.jsx +113 -0
  88. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/D-spotify.jsx +118 -0
  89. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/E-notas.jsx +146 -0
  90. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/F-jornal1.jsx +144 -0
  91. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/G-google.jsx +153 -0
  92. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/H-mapa.jsx +176 -0
  93. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/I-imagens.jsx +132 -0
  94. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/J-jornal2.jsx +191 -0
  95. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/K-maps.jsx +184 -0
  96. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera-movimentos.jsx +48 -0
  97. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera.jsx +57 -0
  98. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/card-spotify.jsx +98 -0
  99. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/digitar.jsx +38 -0
  100. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/jornal-comum.jsx +196 -0
  101. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra.jsx +23 -0
  102. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra_automatica.py +83 -0
  103. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/montagem.jsx +74 -0
  104. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/render_html.py +13 -0
  105. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/util.jsx +162 -0
  106. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-midia-comum.jsx +114 -0
  107. package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/wg-util.jsx +360 -0
  108. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/F-jornal1/render_html.py +13 -0
  109. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/render_w-midia.py +26 -0
  110. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/inclin.py +30 -0
  111. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_d.py +36 -0
  112. package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_e.py +27 -0
  113. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ajustar_movimentos.py +61 -0
  114. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ancorar.py +74 -0
  115. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/assar.py +47 -0
  116. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/camera3d_jornal.py +172 -0
  117. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/comparar.py +32 -0
  118. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/dados_ae_jornal.py +46 -0
  119. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/folha_contato.py +28 -0
  120. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/homografia_jornal.py +74 -0
  121. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/integrar.py +44 -0
  122. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/medir_quadro_a_quadro.py +32 -0
  123. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/mesa_2d.py +64 -0
  124. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/plot_cenas.py +11 -0
  125. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/retificar_quadros.py +38 -0
  126. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/assar_ab.py +52 -0
  127. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/medir_ab.py +59 -0
  128. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ajustar_h.py +111 -0
  129. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_corrige_cam.py +17 -0
  130. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ecc_cadeia.py +80 -0
  131. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_finalizar_cam.py +65 -0
  132. package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_mosaico.py +61 -0
  133. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/01-gramatica-e-roteiro.md +55 -0
  134. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/02-camera.md +66 -0
  135. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/03-montagem-no-ae.md +50 -0
  136. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/04-render-e-conferencia.md +43 -0
  137. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/05-abertura-celular-mergulho.md +43 -0
  138. package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces.md +42 -0
  139. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/atraso_imagem.py +31 -0
  140. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/fundo.py +56 -0
  141. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/juntar.py +45 -0
  142. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/montar.py +177 -0
  143. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/rastreio.py +74 -0
  144. package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/sincronia.py +18 -0
  145. package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/ae/cenas.jsx +530 -0
  146. package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/assets/fazer_assets.py +107 -0
  147. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/assets/pessoas/figurinha.py +46 -0
  148. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/BRIEF-A.md +83 -0
  149. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/audio.py +179 -0
  150. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/confere.py +20 -0
  151. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte1.js +1 -0
  152. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte2.js +1 -0
  153. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/gerar_emojis.py +11 -0
  154. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/img/filmes/_indice.js +122 -0
  155. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/base.js +380 -0
  156. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/main.js +86 -0
  157. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1a.js +169 -0
  158. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1b.js +455 -0
  159. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1c.js +389 -0
  160. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p2.js +379 -0
  161. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/video.py +62 -0
  162. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/README.md +65 -0
  163. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/oraculo_take.py +137 -0
  164. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/render_take.py +388 -0
  165. package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/take.js +235 -0
  166. package/lib/estudio/diretor.js +2 -1
  167. package/lib/estudio/editor.js +285 -0
  168. package/lib/estudio/efeitos-fabrica/terminal-digitando.jsx +2 -1
  169. package/lib/estudio/effectcraft.js +152 -0
  170. package/lib/estudio/exportar.js +181 -0
  171. package/lib/estudio/gerativo.js +44 -15
  172. package/lib/estudio/index.js +126 -5
  173. package/lib/estudio/kit/gerativo.jsx +15 -0
  174. package/lib/estudio/kit-ae/base.jsx +1146 -0
  175. package/lib/estudio/kit-ae/cenas.jsx +395 -0
  176. package/lib/estudio/kit-ae/primitivas.jsx +911 -0
  177. package/lib/estudio/kokoro.js +3 -1
  178. package/lib/estudio/maquina-diretor.js +393 -0
  179. package/lib/estudio/maquina-elementos.js +134 -0
  180. package/lib/estudio/maquina-layout.js +198 -0
  181. package/lib/estudio/maquina-som.js +478 -0
  182. package/lib/estudio/maquina-trilha.js +160 -0
  183. package/lib/estudio/maquina-voz.js +429 -0
  184. package/lib/estudio/maquina.js +516 -0
  185. package/lib/estudio/projetos.js +60 -0
  186. package/lib/estudio/remotion.js +6 -4
  187. package/lib/estudio/revisor.js +61 -0
  188. package/lib/estudio/voicestudio.js +81 -0
  189. package/lib/headless.js +71 -0
  190. package/lib/hooks.js +72 -0
  191. package/lib/local/cerebro.json +398 -12
  192. package/lib/local/entrada.js +46 -2
  193. package/lib/marca-kit.js +309 -0
  194. package/lib/mcp.js +286 -6
  195. package/lib/perigo.js +31 -3
  196. package/lib/planos.js +47 -19
  197. package/lib/preparar.js +111 -0
  198. package/lib/retomar.js +55 -0
  199. package/lib/sandbox.js +197 -0
  200. package/lib/skills.js +3 -1
  201. package/lib/tools.js +13 -4
  202. package/lib/ui.js +1 -1
  203. package/lib/video/auto.js +18 -3
  204. package/package.json +4 -2
  205. package/skills/after-effects/SKILL.md +52 -0
  206. package/skills/maquina-de-motion/SKILL.md +84 -0
  207. package/skills/motion-de-primeira/SKILL.md +33 -51
@@ -48,6 +48,7 @@ const video = require('./video.js');
48
48
  const batida = require('./batida.js');
49
49
  const revisor = require('./revisor.js');
50
50
  const marcaLib = require('./marca.js');
51
+ const marcaKit = require('../marca-kit.js');
51
52
  const conferencia = require('./conferencia.js');
52
53
  const margem = require('./margem.js');
53
54
  const assinatura = require('./assinatura.js');
@@ -65,6 +66,7 @@ const gerativo = require('./gerativo.js');
65
66
  const fluido = require('./fluido.js');
66
67
  const vox = require('./vox.js');
67
68
  const kokoro = require('./kokoro.js');
69
+ const voicestudio = require('./voicestudio.js');
68
70
  const lancamento = require('./lancamento.js');
69
71
  const glifos = require('./glifos.js');
70
72
  const personagem = require('./personagem.js');
@@ -361,7 +363,7 @@ async function roteiroDoServidor(p, args) {
361
363
  estilo: args.estilo || (escolherEstilo({ pedido }) === 'impacto' ? 'impacto' : undefined),
362
364
  }, cfg.token);
363
365
  if (!r || !r.ok) return { ok: false, error: (r && r.erro) || 'o roteirista não respondeu.' };
364
- return { ok: true, roteiro: { ...r.roteiro, marca: (args.roteiro && args.roteiro.marca) || p.marca || undefined } };
366
+ return { ok: true, roteiro: { ...r.roteiro, marca: marcaKit.preencher((args.roteiro && args.roteiro.marca) || p.marca || undefined, { pastas: [p.pasta, process.cwd()] }) } };
365
367
  }
366
368
 
367
369
  async function ferramentaPlano(args) {
@@ -505,6 +507,9 @@ async function ferramentaMidia(args) {
505
507
  const p = args.id ? projeto.ler(args.id) : null;
506
508
  if (args.id && !p) return semProjeto(args.id);
507
509
 
510
+ if (acao === 'projetos') return require('./projetos.js').procurar({ termo: args.termo, categoria: args.categoria, limite: args.limite });
511
+ if (acao === 'projeto') return require('./projetos.js').ver(args.nome || args.id_item || args.termo);
512
+
508
513
  if (acao === 'fontes') {
509
514
  if (args.conferir) return assets.conferirFonte(args.conferir);
510
515
  return { ok: true, fontes: assets.FONTES, biblioteca: biblioteca.cardapio(),
@@ -662,6 +667,86 @@ async function baixarEscolhido(lista, args, p) {
662
667
  comoUsar: r.ok ? `no plano: "src": "${r.relativo}"` : undefined };
663
668
  }
664
669
 
670
+ // ── estudio_ae ───────────────────────────────────────────────────────────
671
+
672
+ /** O motor After Effects: roda o JSX do agente na pasta do projeto e devolve o mp4 e os quadros. */
673
+ async function ferramentaAE(args) {
674
+ const p = args.id ? projeto.ler(args.id) : null;
675
+ if (!args.id || !p) return semProjeto(args.id);
676
+ if (!args.script || !args.comp) return { ok: false, error: 'estudio_ae pede script (o JSX) e comp (o nome da composição).' };
677
+ return require('./cota.js').comCota(() => renderAE(args, p));
678
+ }
679
+
680
+ async function renderAE(args, p) {
681
+ const kitM = marcaKit.preencher(null, { pastas: [p.pasta, process.cwd()] });
682
+ const fontesKit = kitM && kitM.kit ? [kitM.fontes && kitM.fontes.titulo, kitM.fontes && kitM.fontes.texto].filter(Boolean) : [];
683
+ const r = await require('./effectcraft.js').renderizar({
684
+ pasta: p.pasta, script: String(args.script), comp: String(args.comp),
685
+ fontes: [...new Set([...(Array.isArray(args.fontes) ? args.fontes : []), ...fontesKit])], audio: args.audio || undefined,
686
+ conferir: Array.isArray(args.conferir) ? args.conferir : [],
687
+ });
688
+ if (!r.ok) return r;
689
+ const daMarca = kitM && kitM.kit ? { marca: { nome: kitM.nome, cores: kitM.cores, fontes: kitM.fontes, logo: kitM.logo } } : {};
690
+ return { ...r, ...daMarca, nota: 'Olhe os quadros (arquivos em ae/) antes de dar por pronto; ajuste o JSX e rode de novo se algo estiver torto.' };
691
+ }
692
+
693
+ // ── estudio_motion ───────────────────────────────────────────────────────
694
+
695
+ /** O pacote pra postar (exportar.js) + a galeria; nunca derruba o vídeo. `entrega:false` desliga. */
696
+ function entregar(args, p, r) {
697
+ const x = r._exportar || {}; delete r._exportar;
698
+ if (args.entrega === false) return;
699
+ try {
700
+ const E = require('./exportar.js');
701
+ const e = E.exportar({ video: r.video, pasta: p.pasta, assunto: args.assunto, formatos: args.formatos, cenas: x.cenas, palavras: x.palavras, trilha: r.trilha && { ...r.trilha, arquivo: x.trilhaArquivo }, sfx: x.sfx });
702
+ if (e.ok) {
703
+ r.entrega = { pasta: e.dir, arquivos: e.arquivos.map((f) => path.basename(f)), creditos: e.creditos, ...(e.avisos ? { avisos: e.avisos } : {}) };
704
+ E.registrar({ id: p.id, assunto: args.assunto, caminho: r.video, formato: r.formato, duracao: r.duracao, assinatura: r.assinatura, entrega: e.dir });
705
+ } else r.entrega = { erro: e.error };
706
+ } catch (err) { r.entrega = { erro: err.message }; }
707
+ }
708
+
709
+ /** A máquina de motion: assunto + cenas em JSON viram o mp4 (maquina.js), com sons, trilha e voz opcionais. */
710
+ async function ferramentaMotion(args) {
711
+ const p = args.id ? projeto.ler(args.id) : null;
712
+ if (!args.id || !p) return semProjeto(args.id);
713
+ let receita = null;
714
+ if (args.receita) {
715
+ const r = require('./projetos.js').ver(args.receita);
716
+ if (!r.ok) return r;
717
+ receita = r.projeto;
718
+ }
719
+ return require('./cota.js').comCota(() => renderMotion(args, p, receita));
720
+ }
721
+
722
+ async function renderMotion(args, p, receita) {
723
+ const narrar = async (texto, mp3, voz) => rodarPython('narrar.py', ['falar', '--texto', texto, '--saida', mp3, '--voz', String(voz || 'francisca')], { timeout: 5 * 60000 });
724
+ // Kit de marca ativo: cores, fontes e logo entram onde o JSON não disse; o logo vai no CTA (última cena).
725
+ const kitM = marcaKit.preencher(args.marca, { pastas: [p.pasta, process.cwd()] });
726
+ if (kitM && kitM.kit) {
727
+ args = { ...args, marca: kitM };
728
+ if (kitM.logo && Array.isArray(args.cenas) && args.cenas.length) {
729
+ args.cenas = args.cenas.map((c, i) => (i === args.cenas.length - 1 && c && !c.logo ? { ...c, logo: kitM.logo } : c));
730
+ }
731
+ }
732
+ const r = await require('./maquina.js').motion(args, { pasta: p.pasta, receita, deps: { narrar } });
733
+ if (r.ok) projeto.anotar(p.id, 'motion', `${r.duracao}s, ${r.cenas.length} cenas, ${r.lufs} LUFS`);
734
+ if (r.ok) entregar(args, p, r);
735
+ return r.ok ? { ...r, nota: 'Abra os quadros (png em ae/) para conferir. Se algo ficou torto, mude o JSON e rode de novo.' } : r;
736
+ }
737
+
738
+ // ── estudio_editar ───────────────────────────────────────────────────────
739
+
740
+ /** O editor por comando: reabre o plano da máquina de motion, aplica as mudanças e guarda a versão (editor.js). */
741
+ async function ferramentaEditar(args) {
742
+ const id = args.id_video || args.id;
743
+ const p = id ? projeto.ler(id) : null;
744
+ if (!p) return semProjeto(id);
745
+ const r = await require('./editor.js').editar(args, { pasta: p.pasta });
746
+ if (r.ok && r.versao && !args.listar) projeto.anotar(p.id, 'editar', `v${r.versao}: ${(r.feitas || [r.nota || '']).join('; ').slice(0, 120)}`);
747
+ return r.ok && r.video ? { ...r, nota: r.nota || 'Mostre o vídeo. Se não era isso, mude de novo ou use voltar_para.' } : r;
748
+ }
749
+
665
750
  // ── estudio_narrar ───────────────────────────────────────────────────────
666
751
 
667
752
  async function ferramentaNarrar(args) {
@@ -887,7 +972,7 @@ async function ferramentaVideo(args, ctx = {}) {
887
972
  ok: true, proposta: camadas.propor(palavras, m.segundos), duracao: m.segundos,
888
973
  comoUsar: 'é uma SUGESTÃO pela transcrição. Decida você: mude as formas, junte ou separe batidas, '
889
974
  + 'escreva a cena de motion de cada ideia (componentes/x.jsx, recebe {el, t, fps}; el.dados traz largura/altura do espaço livre, '
890
- + 'palavras com t, destaque, cores e fontes) ou use um efeito de fábrica pelo nome (estudio_midia acao:"efeitos"), '
975
+ + 'palavras com t, destaque, cores e fontes; a LEGENDA da fala já está no quadro, então a cena NÃO escreve a fala: desenha a ideia) ou use um efeito de fábrica pelo nome (estudio_midia acao:"efeitos"), '
891
976
  + 'e chame de novo com batidas:[{de, ate, rosto, cena?, destaque?, canto?, dados?}].',
892
977
  };
893
978
  }
@@ -1177,6 +1262,35 @@ async function narrarComGemini(lista, mp3, { voz, direcao } = {}) {
1177
1262
  }
1178
1263
  }
1179
1264
 
1265
+ /* A VOZ DO VOICESTUDIO (15/10): o app local fala o roteiro inteiro, e o resto
1266
+ é o caminho do Gemini — volume de Reels, a transcrição para achar as partes.
1267
+ Fechado ou falhando, devolve ok:false e a cadeia segue para o Gemini. */
1268
+ async function narrarComVoiceStudio(lista, mp3, { voz } = {}) {
1269
+ if (process.env.PRIMOCODE_SEM_VOICESTUDIO) return { ok: false, indisponivel: true, error: 'voicestudio desligado' };
1270
+ try {
1271
+ const texto = lista.map((x) => x.texto).join('\n\n');
1272
+ // A voz do Gemini ("Kore", "Charon") não existe lá: só um perfil do próprio app.
1273
+ const perfil = /^vs:/i.test(String(voz || '')) ? String(voz).slice(3) : undefined;
1274
+ const r = await voicestudio.falar(texto, { voz: perfil });
1275
+ if (!r.ok) return r;
1276
+ const bruto = mp3.replace(/\.mp3$/, '-vs.mp3');
1277
+ fs.writeFileSync(bruto, r.audio);
1278
+ const c = await video.rodar(video.ffmpeg(), ['-y', '-i', bruto, '-af', 'loudnorm=I=-14:TP=-1.5:LRA=9', '-c:a', 'libmp3lame', '-q:a', '3', mp3], { timeout: 120000 });
1279
+ try { fs.unlinkSync(bruto); } catch { /* fica */ }
1280
+ if (!c.ok) return { ok: false, error: 'voicestudio: não consegui converter a voz' };
1281
+ const med = await video.medir(mp3);
1282
+ const chars = lista.reduce((n, x) => n + x.texto.length, 0);
1283
+ if (med.segundos < chars / 22) return { ok: false, error: `voicestudio: a voz veio cortada (${med.segundos.toFixed(1)} s para ${chars} caracteres)` };
1284
+ const cfg = require('../config').loadConfig();
1285
+ const tr = await require('../api').requestTranscricao(cfg.server, { token: cfg.token, audio: fs.readFileSync(mp3).toString('base64'), nome: 'voz.mp3', idioma: 'pt' });
1286
+ const palavras = (tr && tr.palavras) || [];
1287
+ if (!palavras.length) return { ok: false, error: 'voicestudio: sem a transcrição da voz para achar as partes' };
1288
+ return { ok: true, voz: `voicestudio ${perfil || 'default'}`, segundos: med.segundos, partes: alinharPartes(lista, palavras, med.segundos) };
1289
+ } catch (e) {
1290
+ return { ok: false, error: 'voicestudio: ' + e.message };
1291
+ }
1292
+ }
1293
+
1180
1294
  /* Onde estão os rostos em cada foto (studio/rostos.py, pelo uv). Guardado no
1181
1295
  projeto: a mesma foto não é analisada duas vezes. */
1182
1296
  async function acharRostos(p, srcs) {
@@ -1270,7 +1384,9 @@ async function narrarComCache(lista, mp3, { voz, direcao } = {}) {
1270
1384
  if (fs.existsSync(cm) && fs.existsSync(cj)) { fs.copyFileSync(cm, mp3); return { ok: true, ...JSON.parse(fs.readFileSync(cj, 'utf8')) }; }
1271
1385
  // A voz local pedida (voz: 'kokoro'): direto nela, sem gastar a cota do Gemini.
1272
1386
  if (kokoro.querKokoro(voz)) return kokoro.narrar(lista, mp3, { voz, uv: acharUv() });
1273
- const v = await narrarComGemini(lista, mp3, { voz, direcao });
1387
+ // O VoiceStudio local primeiro (15/10); fechado ou falhando, o Gemini.
1388
+ let v = await narrarComVoiceStudio(lista, mp3, { voz });
1389
+ if (!v.ok) v = await narrarComGemini(lista, mp3, { voz, direcao });
1274
1390
  if (v.ok) { try { fs.mkdirSync(dir, { recursive: true }); fs.copyFileSync(mp3, cm); fs.writeFileSync(cj, JSON.stringify({ voz: v.voz, segundos: v.segundos, partes: v.partes })); } catch { /* sem cache */ } }
1275
1391
  return v;
1276
1392
  }
@@ -1289,6 +1405,7 @@ async function produzirNecessidade(p, roteiro, pedido, provedores) {
1289
1405
  direção dele), o Kokoro local e o Edge. A falha de uma passa para a próxima. */
1290
1406
  function provedoresDeVoz(lista, mp3, arq, voz, vozEdge, { soEdge = false, gemini, escolha } = {}) {
1291
1407
  const todos = {
1408
+ voicestudio: () => narrarComVoiceStudio(lista, mp3, { voz }),
1292
1409
  gemini: () => gemini(),
1293
1410
  kokoro: () => kokoro.narrar(lista, mp3, { voz, uv: acharUv() }),
1294
1411
  edge: () => {
@@ -1307,7 +1424,7 @@ function provedoresDeVoz(lista, mp3, arq, voz, vozEdge, { soEdge = false, gemini
1307
1424
  /* A VOZ DE RESERVA, quando o Gemini falha: o Kokoro (local, sem cota) e
1308
1425
  depois o Edge. A falha de uma passa para a próxima, nunca derruba. */
1309
1426
  function narrarReserva(lista, mp3, arq, voz, vozEdge, { soEdge = false, semKokoro = false } = {}) {
1310
- const passos = kokoro.ordem(voz, process.env, { soEdge }).filter((n) => n !== 'gemini' && !(semKokoro && n === 'kokoro'));
1427
+ const passos = kokoro.ordem(voz, process.env, { soEdge }).filter((n) => n !== 'gemini' && n !== 'voicestudio' && !(semKokoro && n === 'kokoro'));
1311
1428
  return kokoro.corrente(passos, {
1312
1429
  kokoro: () => kokoro.narrar(lista, mp3, { voz, uv: acharUv() }),
1313
1430
  edge: () => rodarPython('narrar.py', ['roteiro', '--arquivo', arq, '--saida', mp3, '--voz', kokoro.vozNoEdge(voz, vozEdge)], { timeout: 10 * 60000 }),
@@ -1457,6 +1574,10 @@ async function prepararRoteiro(p, args) {
1457
1574
  const novas = referencias.imagens().filter((f) => !jaTem.has(f));
1458
1575
  for (const f of novas) { try { importarReferencia(p, f); } catch (e) { projeto.anotar(p.id, 'referencia', `não importou ${path.basename(f)}: ${e.message}`); } }
1459
1576
  if (novas.length) p = projeto.ler(p.id) || p;
1577
+ /* O KIT DE MARCA ativo (lib/marca-kit.js) vale mais que a marca lida do
1578
+ site: a pessoa criou uma vez e quer em todo vídeo. O roteiro ainda ganha. */
1579
+ const kitM = marcaKit.preencher({}, { pastas: [p.pasta, process.cwd()] });
1580
+ if (kitM.kit) { p = { ...p, marca: kitM }; }
1460
1581
  if (roteiro.marca) {
1461
1582
  /* A do roteiro por cima da do site, CAMPO A CAMPO: antes o "cores" em
1462
1583
  lista do agente apagava o {fundo, destaque} lido do site (13/10). */
@@ -2567,7 +2688,7 @@ async function ferramentaSite(args) {
2567
2688
 
2568
2689
  module.exports = { proporcaoDoPedido,
2569
2690
  vazamentoNoComeco, variarIcones, objetosDoRoteiro,
2570
- ferramentaProjeto, ferramentaPlano, ferramentaMidia, ferramentaNarrar,
2691
+ ferramentaProjeto, ferramentaPlano, ferramentaMidia, ferramentaAE, ferramentaMotion, ferramentaEditar, ferramentaNarrar,
2571
2692
  ferramentaVideo, ferramentaRender, ferramentaSite,
2572
2693
  projeto, plano, remotion, assets, video, identidade, slides, python, rodarPython, alinharPartes, prepararRoteiro, roteiroValido, juntarVozes,
2573
2694
  };
@@ -295,6 +295,21 @@ function geometria(a, b, L, P) {
295
295
  for (let i = 0; i < N; i++) { const q = -2.3 + (i / (N - 1)) * Math.PI * 2 * 1.1; const e = 1 + (i / N) * 0.07; pts.push([ccx + Math.cos(q) * rx * e, ccy + Math.sin(q) * ry * e]); }
296
296
  return { pts, larg: Math.max(4 * base, cx.s * 0.03), alfa: 1, frente: true, cx: ccx, cy: ccy };
297
297
  }
298
+ if (f === 'visto') {
299
+ /* O ✓ de caneta (15/10, as referências): depois do círculo, ao lado da
300
+ palavra, em dois traços; se não cabe à direita, encosta na margem. */
301
+ const pw = alvoPalavra();
302
+ const s = cx.s, cy0 = cx.y + cx.h * 0.5;
303
+ const A = [pw.x + pw.w + s * 0.16, cy0], B = [pw.x + pw.w + s * 0.27, cy0 + s * 0.2], C = [pw.x + pw.w + s * 0.52, cy0 - s * 0.3];
304
+ const dx = Math.min(0, P.largura - 24 * base - C[0]);
305
+ const q = 0.3;
306
+ for (let i = 0; i < N; i++) {
307
+ const k = i / (N - 1);
308
+ const [p0, p1, u] = k < q ? [A, B, k / q] : [B, C, (k - q) / (1 - q)];
309
+ pts.push([lerp(p0[0], p1[0], u) + dx, lerp(p0[1], p1[1], u)]);
310
+ }
311
+ return { pts, larg: Math.max(5 * base, s * 0.045), alfa: 1, frente: true, tampa: 'round', cx: B[0] + dx, cy: B[1] };
312
+ }
298
313
  if (f === 'barra') {
299
314
  // O vão da barra é o respiro do vídeo (margem.js): colada, ela riscava a primeira letra.
300
315
  // A deriva da vida abre as letras até 3% da linha para fora: a barra recua junto.