primocode 10.9.1 → 10.10.1

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.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # PrimoCode v10.9.1
1
+ # PrimoCode v10.10.1
2
2
 
3
3
  Agente de engenharia brasileiro para o terminal, no estilo Claude Code.
4
4
  Cria arquivos de verdade, roda comandos, **controla o navegador e o desktop** —
@@ -9,7 +9,7 @@ OpenRouter, e cada apelido (`top`/`main`/`fast`) é uma **corrente**: se um mode
9
9
  está no limite de pedidos, o próximo atende. Você não vê o erro, e não paga nada.
10
10
 
11
11
  ```
12
- ▐▛███▜▌ Primo Code v10.9.1
12
+ ▐▛███▜▌ Primo Code v10.10.1
13
13
  ▝▜█████▛▘ Bem-vindo de volta, Joel
14
14
  ▘▘ ▝▝ ~/primocode · /dir <pasta> muda
15
15
  ```
package/lib/act.js CHANGED
@@ -939,6 +939,8 @@ async function runAct(userPrompt, opts) {
939
939
  + 'Senão: '
940
940
  + 'rode `primocode video help` UMA vez (lista as operações), depois import → remove_silence/remove_words → add_captions → inspect (olhe o png) → export. '
941
941
  + 'Cada edição devolve um delta curto e tem undo: edite sem pedir licença a cada passo.\n'
942
+ /* "deixar os vídeos cinematográficos… fazer um color grading sozinha" (13/10). */
943
+ + 'CINEMATOGRÁFICO: `primocode video grade --json \'{}\'` mede a imagem e faz o color grading de cinema sozinho (look:cinema|quente|noite|natural|pb, intensidade); use antes do export quando a pessoa quer o vídeo bonito, e OLHE o inspect.\n'
942
944
  + 'EFEITOS que seguem a pessoa: separate_layers (recorte + momento 3D), replace_background (fundo novo no estalo), '
943
945
  + 'attach_3d (logo 3D na mão, segue a mão), merge_on_join (mãos juntas → fusão → cérebro), cut_silence_at (silêncio depois do estalo), '
944
946
  + 'style_captions (cor, legenda em cima da cabeça) — detalhe em `primocode video help efeitos`. Atalho: `primocode video do --json \'{"pedido":"<o pedido em português>"}\'`.' });
package/lib/catalogo.js CHANGED
@@ -93,7 +93,8 @@ const GRUPOS = [
93
93
  + 'contraste{nao,sim} citacao{texto,autor} imagem{busca,titulo,sub} grafico{titulo,barras:[{rotulo,valor}]} cta{titulo,link} '
94
94
  + 'logo{titulo} cores{cores:[{nome,hex}]} produto{titulo,texto,destaque,selo,barras}. Em qualquer parte: destaque (a palavra-tese), fonte (a fonte citada). O texto da TELA sai da FALA: o título usa palavras que a fala diz, cedo na frase (a tela entra quando a voz diz; palavra que a voz não diz deixa a tela esperando). '
95
95
  + 'visual:{titulo,texto,paleta} (hibrido, cinema, impacto): fonte e cor do cardápio (estudio_midia acao:fontes); sem visual, o PrimoCode sorteia sem repetir os últimos vídeos. '
96
- + 'estilo (SEM IMAGEM é o padrão: escolha o motion que serve a ESTE roteiro, ou deixe vazio e ele gira entre gerativo, fluido e impacto): hibrido (SÓ quando a pessoa pediu imagem: foto e vídeo reais + o motion por cima; dê busca:"termo em inglês" só nas partes em que a imagem agrega — a COISA que a frase nomeia, 2 a 4 palavras, não a frase inteira; rota:"tipografia" em número/citação, "produto" quando a frase fala do produto; ancora:"palavra" — o clipe troca 0,2 s depois dela) | gerativo (= motion: sem template — a tipografia anima nos eixos da fonte numa linha do tempo contínua, composição, fonte, cor e som sorteados do roteiro; modelo3d fone|celular|notebook|relogio|garrafa|lata|caixa: o objeto gira em 3D numa batida — ponha quando o vídeo é de um produto ou objeto físico; ideia abstrata, história ou explicação: deixe vazio; o vídeo tem UMA identidade (2 chãos, 1 acento, 2 fontes) e o fundo só troca na virada de seção — se o roteiro tem atos, dê secao:"nome curto" nas partes, senão o PrimoCode divide; moldura cantos|completa: o quadro fixo do reel (cantos, o assunto no alto, "01 · seção", o tempo) — SÓ quando a pessoa pedir; vazio = sem moldura (ela entrega que o vídeo foi gerado); heroi em cada parte: o desenho da batida além do texto, tirado do CONTEÚDO — um ícone 3D RENDERIZADO da biblioteca ("fluent3d:hot beverage", "fluent3d:money bag"; procure com estudio_midia acao:icones3d — é o que mais parece 3D de verdade), uma animação pronta ("noto:1f525"; acao:animacoes), um ícone do Iconify do objeto que a fala cita ("mdi:bread-slice", "mdi:qrcode"; acao:icones), um 3D do kit (raio|relogio|check|balao|escudo|coracao|estrela|sol|play|engrenagem|cadeado|barras|fone|onda|celular|seta|bateria) ou "nenhum" quando a tipografia basta; forma abstrata (explosao|poligono|esfera|toro|truchet|aneis|grade|rastro) só em assunto abstrato ou de tecnologia — numa conversa de padaria, nunca; ambiente limpo|luz|bokeh e acento traco|viagem (o ponto que viaja entre as batidas): vazio, o PrimoCode escolhe com memória e o bokeh quase não sai fora de tecnologia) | classico (o motion 3D antigo) | vox (explainer editorial: tinta sobre papel, câmera que conta a frase) | '
96
+ + 'componente:"componentes/x.jsx" numa parte = a cena que VOCÊ escreveu em React — é o caminho do motion (skill Motion de primeira): uma ideia desenhada por fala; a voz, a trilha, os sons e o fundo seguem do Estúdio; cena que não compila volta em cenasQuebradas. '
97
+ + 'estilo (deixe VAZIO: o gerativo, com as suas cenas em componente e o estiloVisual que você montou; outro só se a pessoa pedir pelo nome): hibrido (SÓ quando a pessoa pediu imagem: foto e vídeo reais + o motion por cima; dê busca:"termo em inglês" só nas partes em que a imagem agrega — a COISA que a frase nomeia, 2 a 4 palavras, não a frase inteira; rota:"tipografia" em número/citação, "produto" quando a frase fala do produto; ancora:"palavra" — o clipe troca 0,2 s depois dela) | gerativo (= motion: sem template — a tipografia anima nos eixos da fonte numa linha do tempo contínua, composição, fonte, cor e som — estiloVisual {humor, paleta, display, texto, mono, genero, ambiente, acento, material} é o estilo que VOCÊ monta pelo assunto (cardápio: estudio_midia acao:estilos; vazio, o PrimoCode sorteia); modelo3d fone|celular|notebook|relogio|garrafa|lata|caixa: o objeto gira em 3D numa batida — ponha quando o vídeo é de um produto ou objeto físico; ideia abstrata, história ou explicação: deixe vazio; o vídeo tem UMA identidade (2 chãos, 1 acento, 2 fontes) e o fundo só troca na virada de seção — se o roteiro tem atos, dê secao:"nome curto" nas partes, senão o PrimoCode divide; moldura cantos|completa: o quadro fixo do reel (cantos, o assunto no alto, "01 · seção", o tempo) — SÓ quando a pessoa pedir; vazio = sem moldura (ela entrega que o vídeo foi gerado); heroi em cada parte: o desenho da batida além do texto, tirado do CONTEÚDO — um ícone 3D RENDERIZADO da biblioteca ("fluent3d:hot beverage", "fluent3d:money bag"; procure com estudio_midia acao:icones3d — é o que mais parece 3D de verdade), uma animação pronta ("noto:1f525"; acao:animacoes), um ícone do Iconify do objeto que a fala cita ("mdi:bread-slice", "mdi:qrcode"; acao:icones), um 3D do kit (raio|relogio|check|balao|escudo|coracao|estrela|sol|play|engrenagem|cadeado|barras|fone|onda|celular|seta|bateria) ou "nenhum" quando a tipografia basta; forma abstrata (explosao|poligono|esfera|toro|truchet|aneis|grade|rastro) só em assunto abstrato ou de tecnologia — numa conversa de padaria, nunca; ambiente limpo|luz|bokeh e acento traco|viagem (o ponto que viaja entre as batidas): vazio, o PrimoCode escolhe com memória e o bokeh quase não sai fora de tecnologia) | classico (o motion 3D antigo) | vox (explainer editorial: tinta sobre papel, câmera que conta a frase) | '
97
98
  + 'marca (brand reveal: brilho, letras em vetor, marca cromada, padrão) | lancamento (produto: cartões, painel, calendário) | fluido (minimalista: uma ideia por batida, a forma de uma vira a próxima, sem corte; paleta noite|breu|preto|cobalto (escuras) ou lavanda|creme|laranja|branco (claras) — escolha pelo assunto e NÃO repita a do vídeo anterior; vazio, o PrimoCode escolhe; fontesFluido aprovado|editorial|revista|carater|tecnico|classico, vazio gira) | impacto | cinema | personagem (a ANIMAÇÃO 3D de verdade, o vídeo GRAVADO em 3D no visual de blocos/voxel de render de cinema — é a escolha para animação 3D, personagem, bicho, boneco, avatar, mascote, podcast, entrevista ou alguém FALANDO em 3D; cada parte é UMA fala {falante, texto, emocao?, destaque?, acao?, cenario?}, sem tipo). NADA É FIXO, monte o que a pessoa pediu: cena:"texto livre do lugar" (praia, cidade, floresta, parque, fazenda, neve, deserto, espaço, fundo do mar, escritório, sala, cozinha, escola, palco… + hora: "à noite", "ao pôr do sol") ou cena:{lugar, hora, elementos:["palmeira","barraca","violão",…]} — elemento fora do catálogo vira ícone 3D em blocos; parte.cenario muda o lugar a partir daquela fala. Até 4 falantes; personagens:{"nome":{tipo:pessoa|robo|animal|coisa, especie (animal: cachorro|gato|cavalo|vaca|porco|ovelha|raposa|lobo|urso|panda|coelho|leao|tigre|elefante|girafa|tartaruga|galinha|pato|passaro|papagaio|coruja|pinguim|peixe|tubarao|golfinho|dinossauro|sapo|macaco), icone (coisa: "pizza", "café", "polvo" — qualquer objeto/bicho que fala), pessoa: pele, cabelo:curto|cacheado|longo|topete|coque|careca, corCabelo, roupa, oculos, barba; robo: cor}} — sem tipo, o nome decide (nome de bicho vira bicho, de IA vira o mascote laranja, de objeto vira coisa); figurantes:["cachorro","pessoa"] (quem está na cena sem falar); BICHO NÃO FALA: a fala de um bicho sai da narração e ele fica na cena — só fala se a pessoa pedir ("cachorro falante") ou personagens.x.fala:true; a voz de cada falante sai do GÊNERO do nome (personagens.x.genero:f|m manda), uma voz por personagem do começo ao fim; bigode:true e cabeca:"cubo" (o rosto de pixel antigo) em personagens; sem lugar no pedido, o set é a cobertura à noite (janela da cidade, cartões de interface na frente). acao em cada parte: acenar|apontar|pular|dancar|comemorar|pensar|andar|correr. emocao feliz|serio|surpreso|pensativo|animado|curioso; destaque:"texto curto"; paineis:["título",…] só no set de telas (o padrão quando o pedido não diz lugar); visual:"liso" só se pedirem o boneco liso antigo). '
98
99
  + 'marca e lancamento aceitam visual:orbe|vetor|esfera|grade|site|padrao|cromo|cartoes|painel|cores numa parte. marca.nome dá o nome. '
99
100
  + 'voz (thalita|antonio|francisca). DUAS VOZES (o pedido fala em dois narradores, diálogo, conversa, entrevista): roteiro.vozes {"nome":"antonio","outro":"thalita"} e falante:"nome" em cada parte — cada um sai na sua voz. Ajuste fino: plano: {formato:{largura,altura,fps}, cenas:[…], audio:{…}} — ou cenas direto. '
@@ -109,8 +110,11 @@ const GRUPOS = [
109
110
  + 'o render empurra para dentro, encolhe o texto largo e afasta o colado; enfeite de fundo marque decorativo: true.' },
110
111
 
111
112
  { tool: 'estudio_midia', escreve: true, faz: 'procura e baixa ícone, imagem, música e efeito sonoro da internet, e lista as fontes',
112
- args: 'acao: biblioteca | movimentos | cenarios | salvar_cenario | imagem | icones | icones3d | animacoes | som | trilha | efeito | fontes | print | elementos | elemento | recortar. termo. id (para baixar). '
113
+ args: 'acao: biblioteca | estilos | salvar_estilo | movimentos | cenarios | salvar_cenario | imagem | icones | icones3d | animacoes | som | trilha | efeito | fontes | print | elementos | elemento | recortar. termo. id (para baixar). '
113
114
  + 'movimentos termo:"quique" categoria:entrada|saida|enfase|loop: os 100+ movimentos prontos com nome; no elemento, movimento:"<nome>" (ou {nome, em, duracao}, ou uma lista) vira quadros-chave sozinho. Varie: não use o mesmo em todo elemento. Pode inventar o seu em anima. '
115
+ + 'estilos: as peças do gerativo (humor, paleta, fontes, trilha, ambiente, acento, material) — dezenas de milhares de combinações, e os estilos que a pessoa salvou. '
116
+ + 'Monte o do vídeo pelo ASSUNTO em roteiro.estiloVisual; misture um salvo com {base:"<nome>", ...}; crie paleta nova com 5 cores. '
117
+ + 'No fim, pergunte "posso salvar este estilo pra usar nos próximos vídeos?" e, SÓ com o sim, salvar_estilo com nome, pecas {…} e autorizado:true. '
114
118
  + 'cenarios: os lugares prontos do vídeo 3D e os que a pessoa já salvou. Pode CRIAR um cenário novo na cena (lugar base + ceu, chao, parede, luz, acento, hora, elementos); '
115
119
  + 'no fim do vídeo pergunte "posso salvar este cenário pra usar na próxima vez?" e, SÓ com o sim, salvar_cenario com nome, cenario {…} e autorizado:true. Depois, lugar:"<nome>" traz ele de volta. '
116
120
  + 'biblioteca: o cardápio INTEIRO do que existe (ícone 2D, ícone 3D, animação pronta, efeito sonoro, música, fonte, personagem 3D) — olhe antes de escolher. '
@@ -466,7 +466,8 @@ const ATALHOS = [
466
466
  ['Ctrl+C', 'limpa o que foi digitado; com a caixa vazia, sai'],
467
467
  ['Ctrl+D', 'sai (com a caixa vazia)'],
468
468
  ['Ctrl+L', 'limpa a tela'],
469
- ['PgUp · PgDn', 'rola a conversa'],
469
+ ['↑ ↓ · roda · PgUp PgDn', 'rolam a conversa (na tela cheia)'],
470
+ ['Option+↑ · Option+↓', 'o pedido anterior · o próximo (na tela cheia)'],
470
471
  ['arrastar o mouse', 'seleciona texto; Cmd+C (Mac) ou Ctrl+Shift+C copia'],
471
472
  ['Ctrl+A · Ctrl+E', 'vai para o começo · o fim da linha'],
472
473
  ['Ctrl+U · Ctrl+K', 'apaga antes · depois do cursor'],
package/lib/entrada.js CHANGED
@@ -587,6 +587,20 @@ class Entrada extends EventEmitter {
587
587
  this.desenhado = { linhas: q.linhas, cursorLinha: q.cursor.linha };
588
588
  }
589
589
 
590
+ /* A RODA QUE VIRAVA HISTÓRICO (13 e 14/10: "quando eu tento subir o
591
+ histórico ele fica mostrando meus prompts"). Sem capturar o mouse (por
592
+ causa da seleção de texto), o terminal manda a roda como setas ↑↓. O
593
+ ritmo não separa as duas coisas: medido no Orca (14/10), a roda devagar
594
+ chega uma seta a cada 50–250 ms, e o ↑ segurado no teclado a cada 83 ms.
595
+ Então, na tela cheia, a seta ROLA a conversa (2 linhas), sempre; o
596
+ pedido anterior fica no Option+↑/↓ (ou Ctrl/Shift, o que o terminal mandar). */
597
+ _setaNaCheia(dir, key) {
598
+ // ESC ESC [A é o Option dos terminais com "Option como Meta": o Node entrega como seta pura.
599
+ const comOption = key && (key.meta || key.ctrl || key.shift || String(key.sequence || '').startsWith('\x1b\x1b'));
600
+ if (comOption) return this._historico(dir > 0 ? -1 : 1);
601
+ return this._rolar(dir * 2);
602
+ }
603
+
590
604
  _rolar(passo) {
591
605
  this.rolagem = Math.max(0, this.rolagem + passo);
592
606
  this._pintar();
@@ -878,12 +892,14 @@ class Entrada extends EventEmitter {
878
892
 
879
893
  case 'up':
880
894
  if (this.menu) { this.menu.sel = Math.max(0, this.menu.sel - 1); return this._redesenhar(); }
895
+ if (this.cheia) return this._setaNaCheia(1, key);
881
896
  return this._historico(-1);
882
897
  case 'down':
883
898
  if (this.menu) {
884
899
  this.menu.sel = Math.min(this.menu.itens.length - 1, this.menu.sel + 1);
885
900
  return this._redesenhar();
886
901
  }
902
+ if (this.cheia) return this._setaNaCheia(-1, key);
887
903
  return this._historico(1);
888
904
 
889
905
  default:
@@ -893,6 +909,8 @@ class Entrada extends EventEmitter {
893
909
 
894
910
  _ctrl(key) {
895
911
  switch (key.name) {
912
+ case 'up': return this.cheia ? this._setaNaCheia(1, key) : this._historico(-1);
913
+ case 'down': return this.cheia ? this._setaNaCheia(-1, key) : this._historico(1);
896
914
  case 'c':
897
915
  // Com texto no campo, Ctrl+C limpa o que foi digitado — sair
898
916
  // por acidente no meio de um pedido longo é perder o pedido.
@@ -0,0 +1,47 @@
1
+ /**
2
+ * cena.js — confere a cena que a IA escreveu antes do render.
3
+ *
4
+ * "só quero motions nesse nível" (Joel, 14/10, com as referências: cada
5
+ * cena é uma ideia desenhada para a fala, escrita pelo modelo).
6
+ *
7
+ * O caminho do motion é a IA escrever cada cena em React (parte.componente).
8
+ * Um erro de sintaxe num .jsx derrubava o bundle do Remotion e o vídeo
9
+ * inteiro. Aqui a cena é compilada (o esbuild que já mora no remotion-base)
10
+ * e os imports são conferidos: a cena quebrada sai, a parte cai no gerativo,
11
+ * e o erro volta para a IA consertar e mandar de novo.
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const fs = require('fs');
17
+ const os = require('os');
18
+ const path = require('path');
19
+
20
+ const PERMITIDOS = /^(react|remotion|three|@remotion\/[\w-]+|\.{1,2}\/.+)$/;
21
+
22
+ let esbuild;
23
+ function compilador() {
24
+ if (esbuild !== undefined) return esbuild;
25
+ try { esbuild = require(path.join(os.homedir(), '.primocode', 'studio', 'remotion-base', 'node_modules', 'esbuild')); } catch { esbuild = null; }
26
+ return esbuild;
27
+ }
28
+
29
+ /** {ok} ou {ok:false, erro} — o erro em uma linha, com a linha do arquivo. */
30
+ function conferirCena(arquivo, { compilar = compilador() } = {}) {
31
+ let codigo;
32
+ try { codigo = fs.readFileSync(arquivo, 'utf8'); } catch (e) { return { ok: false, erro: `não li ${path.basename(arquivo)}: ${e.message}` }; }
33
+ if (!/export\s+default\b/.test(codigo)) return { ok: false, erro: 'falta o export default da função da cena ({el, t, fps}).' };
34
+ const fora = [...codigo.matchAll(/\bfrom\s+['"]([^'"]+)['"]|\brequire\(\s*['"]([^'"]+)['"]\s*\)/g)].map((m) => m[1] || m[2]).filter((x) => !PERMITIDOS.test(x));
35
+ if (fora.length) return { ok: false, erro: `import fora do permitido: ${[...new Set(fora)].join(', ')} (só react, remotion, @remotion/*, three).` };
36
+ if (!compilar) return { ok: true, semCompilador: true };
37
+ try {
38
+ compilar.transformSync(codigo, { loader: /\.tsx$/i.test(arquivo) ? 'tsx' : 'jsx', jsx: 'automatic', logLevel: 'silent' });
39
+ return { ok: true };
40
+ } catch (e) {
41
+ const x = (e.errors && e.errors[0]) || {};
42
+ const onde = x.location ? `linha ${x.location.line}: ` : '';
43
+ return { ok: false, erro: `${onde}${x.text || String(e.message).split('\n')[0]}` };
44
+ }
45
+ }
46
+
47
+ module.exports = { conferirCena, PERMITIDOS };
@@ -0,0 +1,147 @@
1
+ /**
2
+ * estilos.js — os milhares de estilos do gerativo, montados por peça.
3
+ *
4
+ * "volta pro remotion, terão 10 mil estilos e a IA pode misturar eles e
5
+ * criar novos." (Joel, 14/10)
6
+ *
7
+ * Um estilo é uma combinação de peças: humor, paleta (o sistema de 5 campos),
8
+ * fonte de título, de texto e mono, gênero da trilha, ambiente, acento e o
9
+ * material dos ícones. As peças são as do gerativo.js, então a conta sai do
10
+ * código (total()), e não de uma lista escrita à mão.
11
+ *
12
+ * Quem escolhe é a IA: ela pede o cardápio (estudio_midia acao:"estilos"),
13
+ * monta o estilo pelo assunto e passa roteiro.estiloVisual. O que ela deixar
14
+ * vazio, o sorteio com memória completa (escolherVisual). Ela também pode
15
+ * misturar um estilo salvo (base:"<nome>") e criar uma paleta nova (5 cores).
16
+ * O estilo que ficou bom só é guardado com o sim da pessoa, como os cenários.
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ const fs = require('fs');
22
+ const os = require('os');
23
+ const path = require('path');
24
+ const gerativo = require('./gerativo.js');
25
+ const { chave } = require('./cenarios.js');
26
+
27
+ const arquivo = () => path.join(os.homedir(), '.primocode', 'estilos.json');
28
+ const ehCor = (c) => typeof c === 'string' && /^#[0-9a-f]{6}$/i.test(c);
29
+ const unicos = (campo) => [...new Set(Object.values(gerativo.HUMORES).flatMap((h) => h[campo]))];
30
+
31
+ /** As paletas com nome: "comida-1" é o primeiro sistema de 5 campos do humor comida. */
32
+ function paletas() {
33
+ const out = {};
34
+ for (const [h, lista] of Object.entries(gerativo.CAMPOS)) lista.forEach((k, i) => { out[`${h}-${i + 1}`] = k; });
35
+ return out;
36
+ }
37
+
38
+ function pecas() {
39
+ return {
40
+ humor: Object.keys(gerativo.HUMORES),
41
+ paleta: Object.keys(paletas()),
42
+ display: unicos('displays').filter((f) => !gerativo.BANIDAS.includes(f) && gerativo.VARIAVEIS[f]),
43
+ texto: unicos('textos'),
44
+ mono: unicos('monos'),
45
+ genero: unicos('generos'),
46
+ ambiente: gerativo.AMBIENTES,
47
+ acento: gerativo.ACENTOS_JEITO,
48
+ material: gerativo.MATERIAIS_ICONE,
49
+ };
50
+ }
51
+
52
+ /** Quantos estilos dá pra montar só com as peças prontas (sem contar as paletas que a IA cria). */
53
+ function total() {
54
+ return Object.values(pecas()).reduce((n, lista) => n * lista.length, 1);
55
+ }
56
+
57
+ /**
58
+ * Confere as peças que a IA escolheu. Peça que não existe vira aviso, com as
59
+ * opções, e não entra (o sorteio cobre). A paleta pode ser um nome ou 5 cores.
60
+ */
61
+ function validar(pedido) {
62
+ const P = pecas(), out = {}, avisos = [];
63
+ for (const [campo, v] of Object.entries(pedido || {})) {
64
+ if (campo === 'base' || v == null || v === '') continue;
65
+ if (campo === 'paleta') {
66
+ if (Array.isArray(v)) {
67
+ if (v.length !== 5 || !v.every(ehCor)) avisos.push('paleta nova precisa de 5 cores #rrggbb: escura, assinatura, papel e duas ousadas.');
68
+ else if (v.some(gerativo.corGenerica)) avisos.push(`paleta ${v.join(' ')}: tem cor de cara de IA (violeta de SaaS ou neon); troque.`);
69
+ else out.paleta = v.map((c) => c.toUpperCase());
70
+ } else if (paletas()[v]) out.paleta = paletas()[v];
71
+ else avisos.push(`paleta "${v}" não existe: ${P.paleta.join(', ')} ou 5 cores suas.`);
72
+ continue;
73
+ }
74
+ if (!P[campo]) { avisos.push(`"${campo}" não é peça de estilo: ${Object.keys(P).join(', ')}.`); continue; }
75
+ if (P[campo].includes(v)) out[campo] = v;
76
+ else avisos.push(`${campo} "${v}" não existe: ${P[campo].join(', ')}.`);
77
+ }
78
+ return { pecas: out, avisos };
79
+ }
80
+
81
+ /**
82
+ * O estilo do vídeo a partir do que a IA pediu (objeto, ou o nome de um salvo).
83
+ * Devolve as escolhasGerativo prontas: o que ela não disse, o sorteio completa.
84
+ */
85
+ function montar(pedido, roteiro = {}, { aleatorio } = {}) {
86
+ let p = typeof pedido === 'string' ? { base: pedido } : { ...(pedido || {}) };
87
+ const avisos = [];
88
+ if (p.base) {
89
+ const salvo = ler(p.base);
90
+ if (salvo) p = { ...salvo.pecas, ...p };
91
+ else avisos.push(`não existe estilo salvo "${p.base}".`);
92
+ }
93
+ const v = validar(p);
94
+ avisos.push(...v.avisos);
95
+ const humor = v.pecas.humor;
96
+ const sorteio = gerativo.escolherVisual(humor ? { ...roteiro, pedido: `${roteiro.pedido || ''} ${gerativo.HUMORES[humor].palavras}` } : roteiro, { aleatorio });
97
+ const escolhas = { ...sorteio };
98
+ for (const campo of ['humor', 'paleta', 'display', 'texto', 'mono', 'genero', 'ambiente', 'acento']) if (v.pecas[campo]) escolhas[campo] = v.pecas[campo];
99
+ if (v.pecas.material) escolhas.icones = { ...(sorteio.icones || {}), material: v.pecas.material };
100
+ return { escolhas, escolhidas: Object.keys(v.pecas), avisos };
101
+ }
102
+
103
+ function lerTudo() {
104
+ try { const j = JSON.parse(fs.readFileSync(arquivo(), 'utf8')); return j && typeof j === 'object' ? j : {}; } catch { return {}; }
105
+ }
106
+
107
+ function ler(nome) {
108
+ const k = chave(nome);
109
+ return k ? lerTudo()[k] || null : null;
110
+ }
111
+
112
+ function listar() {
113
+ return Object.entries(lerTudo()).map(([k, e]) => ({ chave: k, nome: e.nome, descricao: e.descricao || '', pecas: e.pecas }));
114
+ }
115
+
116
+ /** Guarda o estilo. `autorizado` é a resposta da pessoa: sem ela, nada é escrito. */
117
+ function salvar({ nome, pecas: pedido, descricao, autorizado } = {}) {
118
+ if (autorizado !== true) {
119
+ return { ok: false, error: 'falta o sim da pessoa.', comoResolver: 'pergunte "posso salvar este estilo pra usar nos próximos vídeos?" e só chame de novo com autorizado:true se ela disser que sim.' };
120
+ }
121
+ const k = chave(nome);
122
+ if (!k) return { ok: false, error: 'dê um nome ao estilo (ex: "padaria de manhã").' };
123
+ const v = validar(pedido);
124
+ if (!Object.keys(v.pecas).length) return { ok: false, error: 'nenhuma peça válida para guardar.', avisos: v.avisos };
125
+ const tudo = lerTudo();
126
+ const novo = { nome: String(nome).slice(0, 60), pecas: v.pecas, salvo: new Date().toISOString() };
127
+ if (typeof descricao === 'string' && descricao.trim()) novo.descricao = descricao.slice(0, 200);
128
+ tudo[k] = novo;
129
+ fs.mkdirSync(path.dirname(arquivo()), { recursive: true });
130
+ fs.writeFileSync(arquivo(), JSON.stringify(tudo, null, 2));
131
+ return { ok: true, chave: k, estilo: novo, avisos: v.avisos, comoUsar: `no próximo vídeo, roteiro.estiloVisual:"${k}" traz este estilo (ou {base:"${k}", ...} para misturar).` };
132
+ }
133
+
134
+ /** O cardápio que a IA lê antes de montar o estilo. */
135
+ function cardapio() {
136
+ return {
137
+ ok: true,
138
+ total: total(),
139
+ pecas: { ...pecas(), paleta: paletas() },
140
+ salvos: listar(),
141
+ comoUsar: 'monte o estilo pelo ASSUNTO e passe roteiro.estiloVisual {humor, paleta, display, texto, mono, genero, ambiente, acento, material} no estilo gerativo. '
142
+ + 'Paleta: um nome da lista ou 5 cores suas [escura, assinatura, papel, ousada, ousada]. Misture: {base:"<salvo>", display:"..."} troca só uma peça. '
143
+ + 'Peça vazia, o PrimoCode completa sem repetir os últimos vídeos. Ficou bom? Pergunte se pode salvar e use acao:"salvar_estilo".',
144
+ };
145
+ }
146
+
147
+ module.exports = { pecas, paletas, total, validar, montar, salvar, ler, listar, cardapio, arquivo };
@@ -192,6 +192,15 @@ const HUMORES = {
192
192
  paletas: [['#2A0E12', '#F0E6D6', '#C9A24A'], ['#F1EBE1', '#1B1A17', '#8C1C13']],
193
193
  generos: ['jazz', 'soul-rnb', 'classical'], som: 'papel',
194
194
  },
195
+ /* (14/10) A padaria caía no editorial — música clássica e mono de jornal —
196
+ porque nenhum humor era de comida. */
197
+ comida: {
198
+ palavras: 'pao paes padaria padeiro cafe cafeteria bolo bolos doce doces confeitaria forno fornada fermentacao massa farinha receita cozinha comida restaurante pizza lanche almoco jantar sabor chef croissant',
199
+ displays: ['Fraunces', 'Playfair Display', 'Bricolage Grotesque'], textos: ['Hanken Grotesk', 'Familjen Grotesk'], monos: ['Space Mono', 'Martian Mono'],
200
+ serifas: ['Instrument Serif', 'Fraunces'], cineticas: ['Anybody', 'Archivo'],
201
+ paletas: [['#F3E6CF', '#3B2416', '#B5651D'], ['#EFE3D0', '#2A1A12', '#C8553D']],
202
+ generos: ['acoustic', 'jazz', 'soul-rnb'], som: 'organico',
203
+ },
195
204
  };
196
205
  /* A cor que o próprio assunto diz ("por que o céu é AZUL") vira o acento. */
197
206
  const CORES_DITAS = { azul: '#2546D9', vermelho: '#C8291E', verde: '#2F7D4F', laranja: '#FF5F1F', amarelo: '#E8B92F', dourado: '#C9A24A', rosa: '#E86A8A', coral: '#FF6F59' };
@@ -208,11 +217,15 @@ const ICONE_DE = {
208
217
  notebook: 'celular', pronto: 'check', feito: 'check', certo: 'check', proximo: 'seta', passo: 'seta', adiante: 'seta', ricocheteia: 'seta',
209
218
  };
210
219
 
220
+ /* O humor novo (comida, 14/10) só entra quando o roteiro diz as palavras dele:
221
+ não gasta a semente (a mesma semente segue dando o mesmo vídeo nos projetos
222
+ que já existem) nem tira a vez dos outros num roteiro que não é de comida. */
223
+ const SO_COM_PALAVRA = new Set(['comida']);
211
224
  /** O humor do roteiro: as palavras dele contra as de cada humor (empate: a semente). */
212
225
  function humorDe(roteiro, s) {
213
226
  const n = acertosDeHumor(roteiro);
214
227
  const nota = {};
215
- for (const h of Object.keys(HUMORES)) nota[h] = n[h] + s.prox() * 0.5;
228
+ for (const h of Object.keys(HUMORES)) nota[h] = SO_COM_PALAVRA.has(h) ? (n[h] ? n[h] + 0.25 : -1) : n[h] + s.prox() * 0.5;
216
229
  return Object.entries(nota).sort((a, b) => b[1] - a[1])[0][0];
217
230
  }
218
231
  /** Quantas palavras de cada humor o roteiro diz. */
@@ -241,7 +254,7 @@ function escolherVisual(roteiro = {}, { aleatorio = Math.random } = {}) {
241
254
  const marca = require('./marca.js').marcaEmObjeto(roteiro.marca);
242
255
  const n = acertosDeHumor(roteiro);
243
256
  const max = Math.max(...Object.values(n));
244
- const humor = variedade.sortear(Object.keys(HUMORES), 'humor-gerativo', { aleatorio, adequacao: (h) => (n[h] + 1) / (max + 1) });
257
+ const humor = variedade.sortear(Object.keys(HUMORES).filter((h) => !SO_COM_PALAVRA.has(h) || n[h]), 'humor-gerativo', { aleatorio, adequacao: (h) => (n[h] + 1) / (max + 1) });
245
258
  const de = (campo, outros) => { const m = {}; for (const [h, d] of Object.entries(HUMORES)) for (const x of d[campo]) if (!BANIDAS.includes(x) && (m[x] == null || h === humor)) m[x] = h === humor ? 1 : outros; return m; };
246
259
  const escolher = (m, tipo, prefixo) => variedade.sortear(Object.keys(m), tipo, { aleatorio, prefixo, adequacao: m });
247
260
  const out = { humor };
@@ -323,7 +336,7 @@ const MOLDURAS = ['sem', 'cantos', 'completa'];
323
336
  'bokeh' (as manchas de luz desfocadas na frente). O acento: 'traco' (cada
324
337
  traço se desenha e some) ou 'viagem' (o ponto que nasce no meio, viaja
325
338
  entre as batidas e se recolhe — o de antes). */
326
- const AMBIENTES = ['limpo', 'luz', 'bokeh'];
339
+ const AMBIENTES = ['limpo', 'luz', 'bokeh', 'brilho'];
327
340
  const ACENTOS_JEITO = ['traco', 'viagem'];
328
341
  const RITMOS_ICONE = ['seguidos', 'alternados', 'esparsos'];
329
342
  const MATERIAIS_ICONE = ['argila', 'verniz', 'metal', 'perola', 'acetinado'];
@@ -524,7 +537,8 @@ function pesosDe(c, { curto, palavras, primeira, ultima, tem3d, pergunta, exclam
524
537
  case 'contraste': return { divisao: 5, duelo: 2.6 };
525
538
  case 'citacao': return { citacao: 5, pilha: 1 };
526
539
  case 'marca': return { marca: primeira || ultima ? 4.5 : 3, gigante: 2.4, parede: curto ? 2 : 0 };
527
- case 'cta': return { pilha: 3, gigante: curto ? 2.6 : 0.6, parede: c.titulo.length <= 16 ? 1.6 : 0, marca: 1, produto: tem3d ? 3.5 : 0 };
540
+ // A marca é UMA linha: "Todo dia às 7h – Padaria Pão Quente" saía em 40 px (14/10). Chamada longa empilha.
541
+ case 'cta': return { pilha: 3, gigante: curto ? 2.6 : 0.6, parede: c.titulo.length <= 16 ? 1.6 : 0, marca: c.titulo.length <= 18 ? 1 : 0, produto: tem3d ? 3.5 : 0 };
528
542
  case 'produto': return tem3d ? { produto: 9, pilha: 0.5 } : { pilha: 3, gigante: curto ? 2.5 : 0.5, parede: c.titulo.length <= 12 ? 1.2 : 0 };
529
543
  case 'falado': return { palavras: 4, gigante: palavras <= 3 ? 2.6 : 0, pilha: palavras <= 9 ? 1.2 : 0 };
530
544
  default: return {
@@ -628,6 +642,7 @@ const CAMPOS = {
628
642
  energia: [['#0B0B0B', '#FF4F00', '#F2EEE6', '#1D3BD1', '#C9F23A'], ['#101010', '#E8C547', '#F4EBDD', '#E5322D', '#2A57FF']],
629
643
  natureza: [['#0F2A2A', '#F2A541', '#F2EDE2', '#2546D9', '#E4573D'], ['#13233F', '#2546D9', '#F2EDE2', '#F2A541', '#C8553D']],
630
644
  luxo: [['#120D0B', '#C9A24A', '#F0E6D6', '#5B1A1F', '#2F4F4F']],
645
+ comida: [['#2A1A12', '#C47A2C', '#F3E6CF', '#7A2E1C', '#E8C27A'], ['#1F1611', '#B5651D', '#F5ECDD', '#5E7D3A', '#E9B44C']],
631
646
  };
632
647
  function camposDe(marca, humor, s, dita = null, fixa = null) {
633
648
  const c = marca.cores || {};
@@ -815,7 +830,25 @@ function desenharSom(P, { trilha = null, sons = {} } = {}) {
815
830
  const eco = () => `aecho=0.8:${f(sh.entre(0.4, 0.75))}:${Math.round(sh.entre(30, 90))}|${Math.round(sh.entre(100, 220))}:${f(sh.entre(0.15, 0.35))}|${f(sh.entre(0.08, 0.2))}`;
816
831
  const ss = () => f(sh.entre(4, Math.max(6, DUR + 20))); // de onde da trilha sai o pedaço
817
832
  const sint = sonsDoVideo({ partes: P.batidas.map((b) => ({ titulo: b.comp, fala: String(b.de) })) }).receitas;
833
+ /* OS SONS PEQUENOS (13/10: "90% do motion é efeito sonoro: barulhinho
834
+ bonitinho, gostoso, satisfatório"). Sintetizados, limpos e cada um com
835
+ a sua altura: o tique da palavra que entra, o pop do ícone e do item, o
836
+ brilho da marca. Não vêm da trilha: são o "clique" do motion. */
837
+ const MICRO = new Set(['tique', 'pop', 'brilho']);
838
+ const receitaMicro = (gesto) => {
839
+ if (gesto === 'tique') {
840
+ const fq = Math.round(sh.entre(1800, 3200)), d = f(sh.entre(0.05, 0.08));
841
+ return ['-f', 'lavfi', '-i', `aevalsrc='0.55*sin(2*PI*${fq}*t)*exp(-${Math.round(sh.entre(70, 110))}*t)+0.12*(random(0)*2-1)*exp(-200*t)':s=44100:d=${d}`, '-af', `highpass=f=900,${panF()}`];
842
+ }
843
+ if (gesto === 'pop') {
844
+ const a0 = Math.round(sh.entre(700, 1200)), a1 = Math.round(sh.entre(380, 640));
845
+ return ['-f', 'lavfi', '-i', `aevalsrc='0.8*sin(2*PI*(${a0}-${a0 - a1}*t*5)*t)*exp(-${Math.round(sh.entre(22, 34))}*t)*(1-exp(-800*t))':s=44100:d=0.2`, '-af', panF()];
846
+ }
847
+ const b0 = Math.round(sh.entre(780, 1050));
848
+ return ['-f', 'lavfi', '-i', `aevalsrc='0.3*sin(2*PI*${b0}*t)*exp(-3*t)+0.2*sin(2*PI*${Math.round(b0 * 1.5)}*t)*exp(-3.8*t)+0.12*sin(2*PI*${b0 * 2}*t)*exp(-4.6*t)':s=44100:d=1.3`, '-af', `${eco()},${panF()}`];
849
+ };
818
850
  const receita = (gesto) => {
851
+ if (MICRO.has(gesto)) return receitaMicro(gesto);
819
852
  if (!musica) { // sem trilha: a síntese, um arquivo por evento (a semente muda a cada um)
820
853
  const base = { revela: 'revela0', golpe: sh.um(['golpe0', 'golpe1']), fatia: sh.um(['nasce0', 'nasce1', 'nasce2']), gaguejo: sh.um(['passagem0', 'passagem1']), ar: sh.um(['passagem1', 'passagem2']) }[gesto];
821
854
  const args = [...sint[base]];
@@ -854,7 +887,9 @@ function desenharSom(P, { trilha = null, sons = {} } = {}) {
854
887
  };
855
888
  const evento = (t, gesto, volume) => {
856
889
  if (t < 0 || t > DUR - 0.1) return;
857
- if (efeitos.some((e) => Math.abs(e.t - t) < 0.25)) return;
890
+ // Som grande não encosta em som grande (0,25 s); o pequeno só não encosta em nada a menos de 0,09 s.
891
+ const micro = MICRO.has(gesto);
892
+ if (efeitos.some((e) => Math.abs(e.t - t) < (micro || MICRO.has(e.gesto) ? 0.09 : 0.25))) return;
858
893
  const nome = 'e' + String(efeitos.length).padStart(2, '0') + '-' + gesto;
859
894
  receitas[nome] = receita(gesto);
860
895
  efeitos.push({ t: f(Math.max(0, t + (sh.prox() - 0.5) * 0.02)), nome, gesto, volume: f(volume * (0.85 + sh.prox() * 0.3)) });
@@ -882,6 +917,18 @@ function desenharSom(P, { trilha = null, sons = {} } = {}) {
882
917
  if (tr === 'atravessa' || tr === 'inunda') return evento(b.de - 0.4, 'ar', 0.75);
883
918
  return evento(b.de - 0.08, 'fatia', 0.75);
884
919
  });
920
+ // O estilo da casa ganha os sons pequenos: cada palavra da tese, cada ícone e item, e o brilho da marca.
921
+ if (P.somDenso) {
922
+ bs.forEach((b) => {
923
+ for (const l of b.linhas || []) {
924
+ if (l.papel === 'tese' || l.papel === 'numero') for (const p of l.palavras || []) if (p.t != null) evento(p.t, 'tique', 0.32);
925
+ if (l.papel === 'item' && l.t != null) evento(l.t, 'pop', 0.42);
926
+ }
927
+ if (b.icone3d && b.icone3d.t != null) evento(b.icone3d.t, 'pop', 0.5);
928
+ if (b.comp === 'marca') evento(b.de + 0.35, 'brilho', 0.55);
929
+ if (b.livre) evento(b.de + 0.08, 'pop', 0.45); // a cena da IA entra com um pop
930
+ });
931
+ }
885
932
  efeitos.sort((a, b) => a.t - b.t);
886
933
  // O src é o nome que sons-leves.prepararReceitas dá (semente + nome): um arquivo por evento.
887
934
  const sufixo = (P.semente >>> 0).toString(36);
@@ -905,6 +952,27 @@ function montarGerativo({ roteiro, largura = 1920, altura = 1080, tempos = [], n
905
952
  const partitura = partituraDe({ roteiro: { ...roteiro, partes }, largura, altura, tempos, icones });
906
953
  const DUR = partitura.duracao;
907
954
 
955
+ /* A PARTE QUE A IA DESENHA (13/10: "a IA vai criar os estilos, os ícones,
956
+ as coisas"). A parte com `componente` aponta um React escrito pela IA na
957
+ pasta do projeto; ele ocupa o quadro no tempo da batida e recebe em
958
+ el.dados o texto, cada palavra com o seu tempo (t relativo ao início da
959
+ batida), as cores e as fontes. O Estúdio segue cuidando da voz, da
960
+ trilha, dos sons e do fundo; a batida deixa de desenhar o texto dela. */
961
+ const livres = [];
962
+ partitura.batidas.forEach((b) => {
963
+ const pt = partes[b.parte];
964
+ const src = pt && typeof pt.componente === 'string' ? pt.componente.trim() : '';
965
+ if (!src) return;
966
+ const palavras = (b.linhas || []).flatMap((l) => (l.palavras || []).map((p) => ({ texto: p.texto, t: p.t != null ? r3(p.t - b.de) : null })));
967
+ livres.push({ tipo: 'componente', src, de: b.de, ate: b.ate, dados: {
968
+ fala: pt.fala || '', titulo: pt.titulo || '', palavras, duracao: r3(b.ate - b.de),
969
+ cores: b.cores, fontes: partitura.fontes, largura, altura, ...(pt.dados && typeof pt.dados === 'object' ? pt.dados : {}) } });
970
+ // As linhas ficam (o leiaute do kit conta com elas), mas ocultas: quem desenha é a cena da IA.
971
+ for (const l of b.linhas || []) l.oculta = true;
972
+ b.acento = []; b.livre = true;
973
+ delete b.heroi; delete b.icone3d; delete b.textura; delete b.contador;
974
+ });
975
+
908
976
  // ── o som: efeitos nas transformações, a música abaixa sob a voz ─────
909
977
  /* O SOM (desenharSom): um evento por batida, no máximo — e cada um é um
910
978
  arquivo PRÓPRIO, feito da trilha do vídeo (ou sintetizado, sem trilha). */
@@ -928,7 +996,7 @@ function montarGerativo({ roteiro, largura = 1920, altura = 1080, tempos = [], n
928
996
  // As chaves "fonte*" são o que o fontes.css procura (remotion.js).
929
997
  fonte: partitura.fontes.display, fonteTexto: partitura.fontes.texto, fonteMono: partitura.fontes.mono,
930
998
  fonteCinetica: partitura.fontes.cinetica, fonteSerifa: partitura.fontes.serifa,
931
- }],
999
+ }, ...livres],
932
1000
  }],
933
1001
  audio: {
934
1002
  ...(narracao ? { narracao } : {}),
@@ -1498,6 +1566,34 @@ function partituraDe({ roteiro, largura = 1920, altura = 1080, tempos = [], icon
1498
1566
  }
1499
1567
  });
1500
1568
 
1569
+ /* O ESTILO DA CASA (13/10, as referências do Joel — o "Vantage"): o vídeo
1570
+ inteiro no chão escuro da marca, com o brilho do acento derivando no
1571
+ fundo; a frase média (título 7–11% da altura, motionmaxxing SKILL:134),
1572
+ não a palavra gigante; cada palavra entra inteira, desfocando (type.md
1573
+ R2.6), na hora em que a voz a diz; sem a parede de texto repetido. */
1574
+ if (roteiro.casa) {
1575
+ fundo.ambiente = 'brilho';
1576
+ const escura = batidas.reduce((m, b) => (b.cores && (!m || luz(b.cores.fundo) < luz(m.fundo)) ? b.cores : m), null);
1577
+ const teto = (deitado ? 0.11 : 0.075) * H;
1578
+ for (const b of batidas) {
1579
+ if (escura) b.cores = { ...escura };
1580
+ b.troca = 'corte';
1581
+ // A saída é da LINHA inteira (desliza) ou corte seco: o "evapora" e o "comprime" espalhavam as letras ("a ad e").
1582
+ if (b.saida && b.saida !== 'fim') b.saida = batidas.indexOf(b) % 2 ? 'corte' : 'desliza';
1583
+ delete b.textura;
1584
+ // O enfeite abstrato (colunas 3D, truchet, a textura de curvas) sai; o ícone do site (b.icone3d) fica.
1585
+ delete b.heroi;
1586
+ for (const l of b.linhas || []) {
1587
+ // O teto vale também para o "encher" do kit (cresce até l.max): sem isso a tese gigante passava de 30% da altura.
1588
+ if ((l.papel === 'tese' || l.papel === 'numero' || l.papel === 'fraco') && !l.marca) {
1589
+ if (l.tamanho > teto) l.tamanho = Math.round(teto);
1590
+ if (l.encher) l.max = Math.round(Math.min(l.max || teto, teto));
1591
+ }
1592
+ if (l.papel === 'tese' && !(b.contador && b.contador.linha === l.id)) l.comportamento = 'desfoque';
1593
+ }
1594
+ }
1595
+ }
1596
+
1501
1597
  return {
1502
1598
  versao: 1, semente, fps: FPS, humor,
1503
1599
  musica: { genero: ou(E.genero, s.um(H0.generos)), generos: H0.generos }, largura: W, altura: H, duracao, regras: REGRAS,
@@ -1505,6 +1601,7 @@ function partituraDe({ roteiro, largura = 1920, altura = 1080, tempos = [], icon
1505
1601
  margem: M, fundo, batidas, camera, produtos,
1506
1602
  identidade: { chaos: ident.chaos, acento: ident.acento, comeca: campos[comeca], fontes: [display, texto] }, secoes: secoes.nomes,
1507
1603
  hud: molduraDe(roteiro, E, { W, H, deitado, marca, secoes: secoes.nomes }),
1604
+ somDenso: !!roteiro.casa,
1508
1605
  };
1509
1606
  }
1510
1607
 
@@ -1942,4 +2039,5 @@ function contarAcentos(b) {
1942
2039
  module.exports = { tituloDaFala,
1943
2040
  montarGerativo, partituraDe, sonsDoVideo, musicaDe, desenharSom, humorDe, escolherVisual, MOLDURAS, AMBIENTES, ACENTOS_JEITO, ARRANJOS_ICONE, RITMOS_ICONE, MATERIAIS_ICONE, corGenerica, BANIDAS, HUMORES, REGRAS, VARIAVEIS, COMPOSICOES, COMPORTAMENTOS, SAIDAS, ACENTOS, FPS,
1944
2041
  especEixos, sorteador, hash, larguraEm, conteudoDe, casar, HEROIS_OBJETO, HEROIS_CAMPO, HEROIS_3D, FAMILIA_HEROI,
2042
+ CAMPOS, escolhasValidas,
1945
2043
  };