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
@@ -0,0 +1,309 @@
1
+ /**
2
+ * marca-kit.js — o KIT DE MARCA: a pessoa cria uma vez, todo vídeo e site usa.
3
+ *
4
+ * ~/.primocode/marcas/<nome>/marca.json nome, cores {fundo,destaque,tinta,apoio},
5
+ * fontes {titulo,texto} (Google Fonts), tom, slogan,
6
+ * site, redes, assinatura (motion preferida, opcional)
7
+ * ~/.primocode/marcas/<nome>/logo.png|svg
8
+ * ~/.primocode/marcas/.ativa nome do kit ativo (global)
9
+ * <projeto>/.primocode/marca fixa o kit naquela pasta (ganha do global)
10
+ *
11
+ * Comandos: `primocode marca criar|listar|usar <nome> [aqui]|mostrar|apagar <nome>` e `/marca`.
12
+ * O Estúdio chama `preencher(marca, {pastas})`: o kit entra por baixo, o que o
13
+ * roteiro já trouxe ganha, campo a campo.
14
+ */
15
+ const fs = require('fs');
16
+ const os = require('os');
17
+ const path = require('path');
18
+
19
+ const PAPEIS = ['fundo', 'destaque', 'tinta', 'apoio'];
20
+
21
+ function raiz() { return process.env.PRIMOCODE_MARCAS || path.join(os.homedir(), '.primocode', 'marcas'); }
22
+
23
+ function slug(nome) {
24
+ return String(nome || '').normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase()
25
+ .replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40);
26
+ }
27
+ const hexOk = (h) => /^#[0-9a-f]{6}$/i.test(String(h || ''));
28
+ function normHex(h) {
29
+ let s = String(h || '').trim();
30
+ if (/^[0-9a-f]{3}$/i.test(s)) s = '#' + s;
31
+ if (/^#[0-9a-f]{3}$/i.test(s)) s = '#' + s.slice(1).split('').map((x) => x + x).join('');
32
+ if (/^[0-9a-f]{6}$/i.test(s)) s = '#' + s;
33
+ return hexOk(s) ? s.toLowerCase() : null;
34
+ }
35
+
36
+ // ── contraste (WCAG) ─────────────────────────────────────────────────────
37
+ function rgb(h) { const n = parseInt(h.slice(1), 16); return [n >> 16, (n >> 8) & 255, n & 255]; }
38
+ function luz(h) {
39
+ const [r, g, b] = rgb(h).map((v) => { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); });
40
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
41
+ }
42
+ function contraste(a, b) {
43
+ const [x, y] = [luz(a), luz(b)].sort((p, q) => q - p);
44
+ return (x + 0.05) / (y + 0.05);
45
+ }
46
+ function misturar(a, b, t) {
47
+ const A = rgb(a), B = rgb(b);
48
+ return '#' + A.map((v, i) => Math.round(v + (B[i] - v) * t).toString(16).padStart(2, '0')).join('');
49
+ }
50
+ /** A cor mais perto de `cor` que passa de `min` sobre `fundo` (clareia ou escurece em direção ao extremo que contrasta). */
51
+ function ajustar(cor, fundo, min = 4.5) {
52
+ const alvo = luz(fundo) > 0.18 ? '#000000' : '#ffffff';
53
+ for (let t = 0.05; t <= 1.001; t += 0.05) {
54
+ const c = misturar(cor, alvo, t);
55
+ if (contraste(c, fundo) >= min) return c;
56
+ }
57
+ return alvo;
58
+ }
59
+ /** Confere tinta×fundo e destaque×fundo. Devolve [{par, razao, ok, sugestao}]. */
60
+ function validarContraste(cores = {}, min = 4.5) {
61
+ const out = [];
62
+ for (const [par, k] of [['tinta×fundo', 'tinta'], ['destaque×fundo', 'destaque']]) {
63
+ if (!hexOk(cores[k]) || !hexOk(cores.fundo)) continue;
64
+ const razao = Math.round(contraste(cores[k], cores.fundo) * 100) / 100;
65
+ const ok = razao >= min;
66
+ out.push({ par, razao, ok, ...(ok ? {} : { sugestao: { [k]: ajustar(cores[k], cores.fundo, min) } }) });
67
+ }
68
+ return out;
69
+ }
70
+
71
+ // ── disco ────────────────────────────────────────────────────────────────
72
+ function pastaDe(nome) { const s = slug(nome); return s ? path.join(raiz(), s) : null; }
73
+ function ler(nome) {
74
+ const p = pastaDe(nome);
75
+ if (!p) return null;
76
+ try {
77
+ const m = JSON.parse(fs.readFileSync(path.join(p, 'marca.json'), 'utf8'));
78
+ const logo = ['logo.svg', 'logo.png'].map((f) => path.join(p, f)).find((f) => fs.existsSync(f));
79
+ return { ...m, ...(logo ? { logo } : {}), _pasta: p };
80
+ } catch { return null; }
81
+ }
82
+ function listar() {
83
+ try {
84
+ return fs.readdirSync(raiz(), { withFileTypes: true }).filter((d) => d.isDirectory())
85
+ .map((d) => ler(d.name)).filter(Boolean);
86
+ } catch { return []; }
87
+ }
88
+ function validar(m) {
89
+ const erros = [];
90
+ if (!m || !String(m.nome || '').trim()) erros.push('falta o nome.');
91
+ for (const k of PAPEIS) if (m.cores && m.cores[k] && !hexOk(m.cores[k])) erros.push(`cor ${k} inválida: ${m.cores[k]}`);
92
+ return erros;
93
+ }
94
+ /** Grava o kit. `logoArquivo` (opcional) é copiado para logo.png|svg. */
95
+ function salvar(m, { logoArquivo } = {}) {
96
+ const erros = validar(m);
97
+ if (erros.length) return { ok: false, error: erros.join(' ') };
98
+ const p = pastaDe(m.nome);
99
+ if (!p) return { ok: false, error: 'o nome não serve de pasta.' };
100
+ fs.mkdirSync(p, { recursive: true });
101
+ const { logo, _pasta, ...dados } = m;
102
+ fs.writeFileSync(path.join(p, 'marca.json'), JSON.stringify({ ...dados, criada: dados.criada || new Date().toISOString() }, null, 2) + '\n');
103
+ if (logoArquivo && fs.existsSync(logoArquivo)) {
104
+ const ext = /\.svg$/i.test(logoArquivo) ? '.svg' : '.png';
105
+ for (const e of ['.svg', '.png']) try { fs.unlinkSync(path.join(p, 'logo' + e)); } catch { /* não havia */ }
106
+ fs.copyFileSync(logoArquivo, path.join(p, 'logo' + ext));
107
+ }
108
+ return { ok: true, pasta: p, kit: ler(m.nome) };
109
+ }
110
+ function apagar(nome) {
111
+ const p = pastaDe(nome);
112
+ if (!p || !fs.existsSync(path.join(p, 'marca.json'))) return { ok: false, error: `não há kit "${nome}".` };
113
+ fs.rmSync(p, { recursive: true, force: true });
114
+ if (ativaGlobal() === slug(nome)) try { fs.unlinkSync(path.join(raiz(), '.ativa')); } catch { /* ok */ }
115
+ return { ok: true };
116
+ }
117
+
118
+ // ── ativa: pino do projeto ganha da global ────────────────────────────────
119
+ function ativaGlobal() { try { return fs.readFileSync(path.join(raiz(), '.ativa'), 'utf8').trim() || null; } catch { return null; } }
120
+ function usar(nome, { aqui = null } = {}) {
121
+ const k = ler(nome);
122
+ if (!k) return { ok: false, error: `não há kit "${nome}". Veja: primocode marca listar` };
123
+ if (aqui) {
124
+ fs.mkdirSync(path.join(aqui, '.primocode'), { recursive: true });
125
+ fs.writeFileSync(path.join(aqui, '.primocode', 'marca'), slug(nome) + '\n');
126
+ return { ok: true, kit: k, onde: 'projeto' };
127
+ }
128
+ fs.mkdirSync(raiz(), { recursive: true });
129
+ fs.writeFileSync(path.join(raiz(), '.ativa'), slug(nome) + '\n');
130
+ return { ok: true, kit: k, onde: 'global' };
131
+ }
132
+ function fixadaEm(pasta) {
133
+ let d = pasta ? path.resolve(pasta) : null;
134
+ for (let i = 0; d && i < 6; i++) {
135
+ try { const n = fs.readFileSync(path.join(d, '.primocode', 'marca'), 'utf8').trim(); if (n) return n; } catch { /* sobe */ }
136
+ const pai = path.dirname(d);
137
+ if (pai === d) break;
138
+ d = pai;
139
+ }
140
+ return null;
141
+ }
142
+ /** O kit em vigor: pino do primeiro `pastas` que tiver, senão a global. */
143
+ function ativa(pastas = []) {
144
+ for (const p of [].concat(pastas).filter(Boolean)) {
145
+ const n = fixadaEm(p);
146
+ if (n) { const k = ler(n); if (k) return k; }
147
+ }
148
+ const g = ativaGlobal();
149
+ return g ? ler(g) : null;
150
+ }
151
+
152
+ /**
153
+ * O gancho do Estúdio: preenche a `marca` do JSON com o kit, campo a campo
154
+ * (o que a pessoa/roteiro já pôs ganha). Sem kit ativo, devolve a marca intacta.
155
+ */
156
+ function preencher(marca, { pastas = [] } = {}) {
157
+ const k = ativa(pastas);
158
+ if (!k) return marca;
159
+ const m = marca && typeof marca === 'object' ? marca : {};
160
+ const temCores = m.cores && (Array.isArray(m.cores) ? m.cores.length : Object.keys(m.cores).length);
161
+ const temFontes = m.fontes && (Array.isArray(m.fontes) ? m.fontes.length : Object.keys(m.fontes).length);
162
+ return {
163
+ ...m,
164
+ nome: m.nome || k.nome,
165
+ cores: temCores ? (Array.isArray(m.cores) ? m.cores : { ...(k.cores || {}), ...m.cores }) : { ...(k.cores || {}) },
166
+ fontes: temFontes ? (Array.isArray(m.fontes) ? m.fontes : { ...(k.fontes || {}), ...m.fontes }) : { ...(k.fontes || {}) },
167
+ ...(m.logo || k.logo ? { logo: m.logo || k.logo } : {}),
168
+ ...(k.slogan && !m.slogan ? { slogan: k.slogan } : {}),
169
+ ...(k.tom && !m.tom ? { tom: k.tom } : {}),
170
+ ...(k.assinatura && !m.assinatura ? { assinatura: k.assinatura } : {}),
171
+ kit: k.nome,
172
+ };
173
+ }
174
+
175
+ // ── do site: logo e cores com o que já existe ────────────────────────────
176
+ async function doSite(entrada, { identidade = require('./estudio/identidade.js') } = {}) {
177
+ let url = String(entrada || '').trim();
178
+ if (!url) return { ok: false, error: 'sem site.' };
179
+ if (!/^https?:\/\//i.test(url)) {
180
+ if (!/\./.test(url)) { const a = await identidade.acharSite(url); if (!a || !a.ok) return { ok: false, error: (a && a.error) || 'não achei o site.' }; url = a.url || a.site; }
181
+ else url = 'https://' + url;
182
+ }
183
+ const r = await identidade.analisar(url);
184
+ if (!r.ok) return r;
185
+ const lista = ((r.cores && r.cores.marca) || []).map((x) => x.cor || x).filter(hexOk);
186
+ const todas = lista.length ? lista : ((r.cores && r.cores.todas) || []).map((x) => x.cor || x).filter(hexOk);
187
+ const cores = require('./estudio/marca.js').marcaEmObjeto({ cores: todas }).cores || {};
188
+ const f = r.fontes || {};
189
+ const nomes = [...(f.doGoogleFonts || []), ...(f.declaradas || []).map((x) => x.fonte)];
190
+ const unicas = [...new Set(nomes)];
191
+ const fontes = { ...(unicas[0] ? { titulo: unicas[0] } : {}), ...(unicas[0] ? { texto: unicas[1] || unicas[0] } : {}) };
192
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'marca-'));
193
+ let logoArquivo = null;
194
+ try {
195
+ const l = await identidade.pegarLogo(r.html || '', r.url, { pasta: tmp, nome: 'kit' });
196
+ if (l && l.ok && l.src) logoArquivo = path.join(tmp, l.src);
197
+ } catch { /* sem logo: a pessoa manda depois */ }
198
+ return { ok: true, url: r.url, nome: r.marca || '', slogan: r.descricao || '', cores, fontes, logoArquivo };
199
+ }
200
+
201
+ // ── criar (interativo curto) ─────────────────────────────────────────────
202
+ /**
203
+ * `perguntar(texto, padrao) → Promise<string>` e `dizer(texto)` são injetáveis (teste).
204
+ * Fluxo: nome → site (opcional) → confirma cores/fonte/logo → contraste → grava.
205
+ */
206
+ async function criar({ perguntar, dizer = console.log, deps = {} } = {}) {
207
+ const nome = String(await perguntar('Nome da marca', '')).trim();
208
+ if (!nome) return { ok: false, error: 'sem nome, sem kit.' };
209
+ const site = String(await perguntar('Site (opcional, Enter pula)', '')).trim();
210
+ let achado = { cores: {}, fontes: {}, logoArquivo: null, slogan: '' };
211
+ if (site) {
212
+ dizer('Lendo o site...');
213
+ const r = await doSite(site, deps);
214
+ if (r.ok) { achado = r; dizer(`Achei: ${Object.entries(r.cores).map(([k, v]) => `${k} ${v}`).join(' · ') || 'sem cores'}${r.fontes.titulo ? ` · fonte ${r.fontes.titulo}` : ''}${r.logoArquivo ? ' · logo' : ' · sem logo'}`); }
215
+ else dizer(`Não deu pra ler o site (${r.error}). Sigo com o que você disser.`);
216
+ }
217
+ const cores = {};
218
+ for (const k of PAPEIS) {
219
+ const padrao = achado.cores[k] || (k === 'fundo' ? '#0b0b0f' : k === 'tinta' ? '#f5f5f5' : k === 'destaque' ? '#ff5a1f' : '');
220
+ const v = normHex(await perguntar(`Cor ${k}${k === 'apoio' ? ' (opcional)' : ''}`, padrao));
221
+ if (v) cores[k] = v;
222
+ }
223
+ let sugeriu = [];
224
+ for (let volta = 0; volta < 2; volta++) {
225
+ const ruins = validarContraste(cores).filter((x) => !x.ok);
226
+ if (!ruins.length) break;
227
+ for (const x of ruins) {
228
+ const [k, v] = Object.entries(x.sugestao)[0];
229
+ dizer(`Contraste baixo em ${x.par}: ${x.razao} (o mínimo é 4,5). Sugiro ${k} ${v}.`);
230
+ const ok = /^(s|sim|y|yes|)$/i.test(String(await perguntar(`Trocar ${k} por ${v}? (s/n)`, 's')).trim());
231
+ if (ok) { cores[k] = v; sugeriu.push(k); }
232
+ }
233
+ }
234
+ const titulo = String(await perguntar('Fonte do título (Google Fonts)', achado.fontes.titulo || '')).trim();
235
+ const texto = String(await perguntar('Fonte do texto (Google Fonts)', achado.fontes.texto || titulo)).trim();
236
+ const slogan = String(await perguntar('Slogan (opcional)', '')).trim();
237
+ const tom = String(await perguntar('Tom de voz (opcional, ex.: direto e simpático)', '')).trim();
238
+ const redes = String(await perguntar('@redes (opcional)', '')).trim();
239
+ let logoArquivo = achado.logoArquivo;
240
+ if (!logoArquivo) {
241
+ const l = String(await perguntar('Arquivo do logo (png/svg, opcional)', '')).trim().replace(/^~(?=\/)/, os.homedir());
242
+ if (l && fs.existsSync(l)) logoArquivo = l; else if (l) dizer('Não achei esse arquivo; fica sem logo.');
243
+ }
244
+ const r = salvar({
245
+ nome, cores,
246
+ ...((titulo || texto) ? { fontes: { ...(titulo ? { titulo } : {}), ...(texto ? { texto } : {}) } } : {}),
247
+ ...(slogan ? { slogan } : {}), ...(tom ? { tom } : {}), ...(site ? { site } : {}), ...(redes ? { redes: redes.split(/[\s,]+/).filter(Boolean) } : {}),
248
+ }, { logoArquivo });
249
+ if (r.ok) r.contraste = validarContraste(cores);
250
+ return r;
251
+ }
252
+
253
+ // ── texto e CLI ──────────────────────────────────────────────────────────
254
+ function mostrar(k) {
255
+ if (!k) return 'Nenhum kit ativo. Crie um: primocode marca criar';
256
+ const c = k.cores || {}, f = k.fontes || {};
257
+ const lin = [`Marca: ${k.nome}`,
258
+ ` cores ${PAPEIS.filter((p) => c[p]).map((p) => `${p} ${c[p]}`).join(' · ') || '-'}`,
259
+ ` fontes ${f.titulo || '-'} / ${f.texto || '-'}`,
260
+ ` logo ${k.logo || '-'}`];
261
+ for (const [r, v] of [['slogan', k.slogan], ['tom', k.tom], ['site', k.site], ['redes', (k.redes || []).join(' ')], ['motion', k.assinatura]]) if (v) lin.push(` ${r.padEnd(7)} ${v}`);
262
+ const ruins = validarContraste(c).filter((x) => !x.ok);
263
+ for (const x of ruins) lin.push(` ! contraste ${x.par} ${x.razao} (< 4,5)`);
264
+ return lin.join('\n');
265
+ }
266
+ function tela(lista, ativaNome) {
267
+ return lista.length ? lista.map((k) => `${slug(k.nome) === ativaNome ? '●' : ' '} ${k.nome}${k.site ? ' ' + k.site : ''}`).join('\n') : 'Nenhum kit ainda. Crie um: primocode marca criar';
268
+ }
269
+
270
+ function perguntarNoTerminal() {
271
+ const rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
272
+ const ask = (t, p) => new Promise((res) => rl.question(`${t}${p ? ` [${p}]` : ''}: `, (a) => res(a.trim() || p || '')));
273
+ return { ask, fechar: () => rl.close() };
274
+ }
275
+
276
+ /** `primocode marca <op>`; o /marca da sessão chama o mesmo, com `perguntar` próprio. */
277
+ async function main(argv, { perguntar, dizer = console.log, pasta = process.cwd(), deps } = {}) {
278
+ const [op = 'mostrar', ...resto] = argv;
279
+ const nome = resto.filter((x) => x !== 'aqui').join(' ').trim();
280
+ const aqui = resto.includes('aqui');
281
+ switch (op) {
282
+ case 'criar': {
283
+ const t = perguntar ? null : perguntarNoTerminal();
284
+ try {
285
+ const r = await criar({ perguntar: perguntar || t.ask, dizer, deps });
286
+ if (!r.ok) { dizer(r.error); return 1; }
287
+ dizer(`Kit "${r.kit.nome}" salvo em ${r.pasta}. Ativar: primocode marca usar ${r.kit.nome}`);
288
+ return 0;
289
+ } finally { if (t) t.fechar(); }
290
+ }
291
+ case 'listar': case 'ls': dizer(tela(listar(), ativaGlobal())); return 0;
292
+ case 'usar': {
293
+ if (!nome) { dizer('Qual? primocode marca usar <nome> [aqui]'); return 1; }
294
+ const r = usar(nome, { aqui: aqui ? pasta : null });
295
+ dizer(r.ok ? `Marca "${r.kit.nome}" ${r.onde === 'projeto' ? 'fixada nesta pasta' : 'ativa'}.` : r.error);
296
+ return r.ok ? 0 : 1;
297
+ }
298
+ case 'mostrar': case 'ver': dizer(mostrar(nome ? ler(nome) : ativa([pasta]))); return 0;
299
+ case 'apagar': case 'rm': {
300
+ if (!nome) { dizer('Qual? primocode marca apagar <nome>'); return 1; }
301
+ const r = apagar(nome);
302
+ dizer(r.ok ? `Kit "${nome}" apagado.` : r.error);
303
+ return r.ok ? 0 : 1;
304
+ }
305
+ default: dizer('Uso: primocode marca criar | listar | usar <nome> [aqui] | mostrar [nome] | apagar <nome>'); return 1;
306
+ }
307
+ }
308
+
309
+ module.exports = { raiz, slug, normHex, contraste, ajustar, validarContraste, ler, listar, salvar, apagar, usar, ativa, fixadaEm, preencher, doSite, criar, mostrar, main };