primocode 10.4.7 → 10.5.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.
@@ -50,6 +50,26 @@ const CENARIOS = {
50
50
  noite: { parede: '#120f1f', parede2: '#1a1530', chao: '#141218', acento: '#7b5cff', luz: '#c8b8ff', mesa: true, microfones: true, neon: 'ON' },
51
51
  };
52
52
 
53
+ /* Os sets do VOXEL (kit/voxel.jsx, o padrão desde 11/10): o chão de
54
+ ladrilhos (duas cores alternadas), a parede escura, a cor das telas, o
55
+ acento (as luzinhas e o cartão de destaque), a luz principal e o recorte.
56
+ "telas" é o da referência do Joel: roxo, telas azuis e o laranja do Claude. */
57
+ const CENARIOS_VOXEL = {
58
+ telas: { parede: '#07061a', chao: '#4338ca', chao2: '#3b32b8', tela: '#101734', acento: '#ff7a45', luz: '#ffd2b0', recorte: '#8b7bff' },
59
+ neon: { parede: '#06030c', chao: '#6d28d9', chao2: '#5b21b6', tela: '#140b24', acento: '#22d3ee', luz: '#ffe1f5', recorte: '#ff4fd8' },
60
+ oceano: { parede: '#020c12', chao: '#0e7490', chao2: '#0b6079', tela: '#06202b', acento: '#f97316', luz: '#fff1dc', recorte: '#38bdf8' },
61
+ terra: { parede: '#120c08', chao: '#b45309', chao2: '#9a4708', tela: '#1c140e', acento: '#facc15', luz: '#fff4dc', recorte: '#fb923c' },
62
+ };
63
+ // O nome antigo do set (podcast, estúdio…) tem um par no voxel.
64
+ const VOXEL_DO_LISO = { podcast: 'telas', estudio: 'oceano', escritorio: 'terra', noite: 'neon' };
65
+ // O mascote é laranja (o do Claude) a menos que o roteiro diga a cor.
66
+ const CORES_MASCOTE = { claude: '#d97757', gpt: '#10a37f', chatgpt: '#10a37f', gemini: '#4f7cff', primo: '#22b8d8', primocode: '#22b8d8' };
67
+ const ROUPAS_VOXEL = ['#c2452f', '#2f4cdd', '#1f8a5b', '#d08a1e', '#7b5cff', '#e8e2d6'];
68
+ // A pele de bloco é um tom mais fundo: sob a luz quente, o tom claro estourava.
69
+ const PELES_VOXEL = ['#d9a07a', '#c08660', '#a8704c', '#8a5a3c', '#6b4229'];
70
+ // O cabelo de bloco é escuro: o loiro sumia na pele de bloco (11/10).
71
+ const CABELOS_VOXEL = ['#1d1612', '#3b2618', '#2b2b30', '#4a2e1c', '#141016'];
72
+
53
73
  /** Um número de 0 a 1 a partir de um texto: a mesma pessoa sai sempre igual. */
54
74
  function hashDe(txt) {
55
75
  let h = 2166136261;
@@ -154,6 +174,38 @@ function planosDe(falas, { dois, duracao, aleatorio }) {
154
174
  return planos;
155
175
  }
156
176
 
177
+ /* OS CARTÕES DA PAREDE DE TELAS. Na referência, cada tela é um chat
178
+ trabalhando ("Leitura do app", "Testes", "Banco de dados"). Aqui os títulos
179
+ saem do ASSUNTO: o que o roteiro mandou (roteiro.paineis), o destaque de cada
180
+ fala e as palavras fortes — nunca texto inventado sem relação com o vídeo. */
181
+ const PALAVRA_FRACA = /^(que|com|para|pra|por|uma|um|uns|umas|não|nao|mas|mais|isso|esse|essa|este|esta|você|voce|eu|ele|ela|nós|nos|meu|minha|seu|sua|tudo|todo|toda|quando|como|onde|muito|muita|então|entao|aqui|agora|ainda|também|tambem|sobre|depois|antes|porque|qual|quem|cada|está|esta|estou|são|sao|foi|ser|ter|tem|vai|vou|faz|fazer|dele|dela|disso|nisso|desse|dessa|pelo|pela|seus|suas|isso)$/i;
182
+ // Corta no fim de uma palavra: "robô que não dá pa" é pior que "robô que não dá".
183
+ function cortarNaPalavra(txt, n) {
184
+ const t = String(txt).trim();
185
+ if (t.length <= n) return t;
186
+ const c = t.slice(0, n + 1).replace(/\s+\S*$/, '');
187
+ return (c || t.slice(0, n)).replace(/[,;:·-]+$/, '');
188
+ }
189
+ function paineisDe(falas, roteiro = {}) {
190
+ if (Array.isArray(roteiro.paineis) && roteiro.paineis.length) {
191
+ return roteiro.paineis.slice(0, 12).map((x) => (typeof x === 'string' ? { titulo: x.slice(0, 18), linha: '' } : { titulo: String(x.titulo || '').slice(0, 18), linha: String(x.linha || '').slice(0, 30) }));
192
+ }
193
+ const out = [];
194
+ const ja = new Set();
195
+ const por = (titulo, linha) => {
196
+ const tt = String(titulo || '').trim();
197
+ if (!tt || ja.has(tt.toLowerCase()) || out.length >= 8) return;
198
+ ja.add(tt.toLowerCase());
199
+ out.push({ titulo: cortarNaPalavra(tt, 18), linha: cortarNaPalavra(String(linha || ''), 30) });
200
+ };
201
+ for (const f of falas) if (f.destaque) por(f.destaque, f.texto);
202
+ for (const f of falas) {
203
+ const fortes = f.texto.split(/\s+/).map((w) => w.replace(/[^\p{L}\d%$-]/gu, '')).filter((w) => w.length >= 5 && !PALAVRA_FRACA.test(w));
204
+ if (fortes[0]) por(fortes[0].replace(/^./, (x) => x.toUpperCase()), f.texto);
205
+ }
206
+ return out.length ? out : [{ titulo: String(roteiro.titulo || 'Agora').slice(0, 18), linha: '' }];
207
+ }
208
+
157
209
  /** A partitura inteira: o que o kit/personagem.jsx desenha, quadro a quadro. */
158
210
  function partituraDe({ roteiro = {}, largura = 1920, altura = 1080, tempos = [] }) {
159
211
  const partes = (roteiro.partes || []).filter((p) => p && typeof p === 'object' && falaDe(p));
@@ -194,8 +246,27 @@ function partituraDe({ roteiro = {}, largura = 1920, altura = 1080, tempos = []
194
246
  if (roteiro.acento && /^#[0-9a-f]{6}$/i.test(roteiro.acento)) cenario.acento = roteiro.acento;
195
247
 
196
248
  const planos = planosDe(falas, { dois: elenco.length > 1, duracao, aleatorio });
249
+
250
+ /* O VISUAL. "voxel" (blocos, kit/voxel.jsx) é o padrão: é o 3D da
251
+ referência do Joel (11/10). "liso" é o boneco de esferas de antes, só
252
+ quando o roteiro pede pelo nome. */
253
+ const visual = String(roteiro.visual || '').toLowerCase() === 'liso' ? 'liso' : 'voxel';
254
+ let cenarioFinal = cenario;
255
+ if (visual === 'voxel') {
256
+ const pedido = String(roteiro.cenario || '').toLowerCase();
257
+ const nomeV = CENARIOS_VOXEL[pedido] ? pedido : VOXEL_DO_LISO[pedido] || 'telas';
258
+ cenarioFinal = { nome: nomeV, ...CENARIOS_VOXEL[nomeV] };
259
+ if (roteiro.acento && /^#[0-9a-f]{6}$/i.test(roteiro.acento)) cenarioFinal.acento = roteiro.acento;
260
+ for (const e of elenco) {
261
+ if (e.tipo === 'robo' && !(pedidoDe(e.nome) || {}).cor) e.cor = CORES_MASCOTE[e.nome] || '#d97757';
262
+ if (e.tipo === 'pessoa' && !(pedidoDe(e.nome) || {}).roupa) e.roupa = pegar(ROUPAS_VOXEL, hashDe(e.nome + ':roupa'));
263
+ if (e.tipo === 'pessoa' && !(pedidoDe(e.nome) || {}).pele) e.pele = pegar(PELES_VOXEL, hashDe(e.nome + ':pele'));
264
+ if (e.tipo === 'pessoa' && !(pedidoDe(e.nome) || {}).corCabelo) e.corCabelo = pegar(CABELOS_VOXEL, hashDe(e.nome + ':corCabelo'));
265
+ }
266
+ }
197
267
  return {
198
- versao: 1, fps: FPS, largura, altura, duracao, semente, cenario, elenco, falas, planos,
268
+ versao: 1, fps: FPS, largura, altura, duracao, semente, cenario: cenarioFinal, elenco, falas, planos, visual,
269
+ paineis: paineisDe(falas, roteiro),
199
270
  legenda: roteiro.legenda !== false,
200
271
  titulo: typeof roteiro.titulo === 'string' ? roteiro.titulo.slice(0, 60) : '',
201
272
  filme: { grao: roteiro.grao !== false, vinheta: true },
@@ -216,7 +287,7 @@ function montarPersonagem({ roteiro, largura = 1920, altura = 1080, tempos = [],
216
287
  const plano = {
217
288
  formato: { largura, altura, fps: FPS },
218
289
  fundoGeral: partitura.cenario.parede,
219
- cenas: [{ duracao: DUR, fundo: partitura.cenario.parede, elementos: [{ tipo: 'kit', peca: 'personagem', partitura, x: 0, y: 0, largura, altura }] }],
290
+ cenas: [{ duracao: DUR, fundo: partitura.cenario.parede, elementos: [{ tipo: 'kit', peca: partitura.visual === 'liso' ? 'personagem' : 'voxel', partitura, x: 0, y: 0, largura, altura }] }],
220
291
  audio: {
221
292
  ...(narracao ? { narracao } : {}),
222
293
  ...(trilha ? { trilha: { src: trilha, volumes, loop: true } } : {}),
@@ -225,8 +296,8 @@ function montarPersonagem({ roteiro, largura = 1920, altura = 1080, tempos = [],
225
296
  };
226
297
  return {
227
298
  ok: true, plano, partitura,
228
- resumo: { cenas: 1, segundos: DUR, fps: FPS, cenario: partitura.cenario.nome, elenco: partitura.elenco.map((e) => `${e.rotulo} (${e.tipo})`), planos: partitura.planos.length, falas: partitura.falas.length },
299
+ resumo: { visual: partitura.visual, cenas: 1, segundos: DUR, fps: FPS, cenario: partitura.cenario.nome, elenco: partitura.elenco.map((e) => `${e.rotulo} (${e.tipo})`), planos: partitura.planos.length, falas: partitura.falas.length },
229
300
  };
230
301
  }
231
302
 
232
- module.exports = { montarPersonagem, partituraDe, planosDe, aparencia, destaqueDe, emocaoDe, CENARIOS, EH_ROBO, FPS };
303
+ module.exports = { montarPersonagem, partituraDe, planosDe, paineisDe, aparencia, destaqueDe, emocaoDe, CENARIOS, CENARIOS_VOXEL, EH_ROBO, FPS };
@@ -78,6 +78,8 @@ function pkgJson(nome) {
78
78
  '@remotion/three': VERSAO_REMOTION,
79
79
  three: '0.186.1',
80
80
  '@react-three/fiber': '9.8.1',
81
+ // A animação pronta da biblioteca (kit/animacao.jsx): Lottie, tocado quadro a quadro.
82
+ 'lottie-web': '5.13.0',
81
83
  },
82
84
  }, null, 2) + '\n';
83
85
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primocode",
3
- "version": "10.4.7",
3
+ "version": "10.5.0",
4
4
  "description": "PrimoCode — agente de engenharia com IA e cursor próprio. Requer conta Conecta Primo AI (Premium ou Super). Cria arquivos, roda comandos, controla navegador e desktop: abre apps, clica em botões e ícones pelo nome, digita e usa atalhos.",
5
5
  "main": "bin/primocode.js",
6
6
  "bin": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "start": "node bin/primocode.js",
11
- "test": "node test/tools.test.js && node test/ui.test.js && node test/entrada.test.js && node test/transparencia.test.js && node test/permissao.test.js && node test/compatibilidade.test.js && node test/skills.test.js && node test/skills-de-fora.test.js && node test/skills-novas.test.js && node test/comandos.test.js && node test/estudio.test.js && node test/voz.test.js && node test/modo.test.js && node test/pasta.test.js && node test/projeto.test.js && node test/nome-projeto.test.js && node test/api-propria.test.js && node test/claude-engine.test.js && node test/codex.test.js && node test/conferir.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/seta.test.js && node test/effort.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/casar.test.js && node test/recado.test.js && node test/releitura.test.js && node test/snapshot.test.js && node test/video.test.js && node test/limpeza.test.js && node test/autonomia.test.js && node test/desfazer.test.js && node test/desfazer-git.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/pipeline.test.js && node test/conta.test.mjs && node test/nuvem.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/empurrao.test.js && node test/terminal-novo.test.js && node test/markdown.test.js && node test/coerencia.test.js && node test/contexto.test.js && node test/perigo.test.js && node test/rapidez.test.js && node test/mascotes.test.js && node test/comandos-extra.test.js && node test/offline.test.js && node test/edicao.test.js && node test/composicao.test.js && node test/acervo-site.test.js && node test/impacto.test.js && node test/cinema.test.js && node test/producao.test.js && node test/ende.test.js && node test/capa.test.js && node test/broll.test.js && node test/morfar.test.js && node test/motion.test.js && node test/motion-roteiro.test.js && node test/kit-motion.test.js && node test/gerativo.test.js && node test/gerativo-identidade.test.js && node test/gerativo-moldura.test.js && node test/gerativo-heroi.test.js && node test/gerativo-ambiente.test.js && node test/gerativo-variedade.test.js && node test/hibrido.test.js && node test/batida.test.js && node test/estudio-acervo.test.js && node test/estudio-diretor.test.js && node test/estudio-marca.test.js && node test/estudio-novo-projeto.test.js && node test/estudio-plano.test.js && node test/estudio-slides.test.js && node test/estudio-video.test.js && node test/revisor.test.js && node test/assinatura.test.js && node test/biblioteca.test.js && node test/mcp.test.js && node test/personas.test.js && node test/efeitos.test.js && node test/auto.test.js && node test/colar.test.js && node test/orquestra.test.js && node test/shell-limpo.test.js && node test/editar-remotion.test.js && node test/variacao3d.test.js && node test/portao-skill.test.js && node test/clones.test.js && node test/kokoro.test.js && node test/fluido.test.js && node test/zoom.test.js && node test/personagem.test.js && node test/tela.test.js && node test/margem.test.js && node test/vazamento.test.js"
11
+ "test": "node test/tools.test.js && node test/ui.test.js && node test/entrada.test.js && node test/transparencia.test.js && node test/permissao.test.js && node test/compatibilidade.test.js && node test/skills.test.js && node test/skills-de-fora.test.js && node test/skills-novas.test.js && node test/comandos.test.js && node test/estudio.test.js && node test/voz.test.js && node test/modo.test.js && node test/pasta.test.js && node test/projeto.test.js && node test/nome-projeto.test.js && node test/api-propria.test.js && node test/claude-engine.test.js && node test/codex.test.js && node test/conferir.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/seta.test.js && node test/effort.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/casar.test.js && node test/recado.test.js && node test/releitura.test.js && node test/snapshot.test.js && node test/video.test.js && node test/limpeza.test.js && node test/autonomia.test.js && node test/desfazer.test.js && node test/desfazer-git.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/pipeline.test.js && node test/conta.test.mjs && node test/nuvem.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/empurrao.test.js && node test/terminal-novo.test.js && node test/markdown.test.js && node test/coerencia.test.js && node test/contexto.test.js && node test/perigo.test.js && node test/rapidez.test.js && node test/mascotes.test.js && node test/comandos-extra.test.js && node test/offline.test.js && node test/edicao.test.js && node test/composicao.test.js && node test/acervo-site.test.js && node test/impacto.test.js && node test/cinema.test.js && node test/producao.test.js && node test/ende.test.js && node test/capa.test.js && node test/broll.test.js && node test/morfar.test.js && node test/motion.test.js && node test/motion-roteiro.test.js && node test/kit-motion.test.js && node test/gerativo.test.js && node test/gerativo-identidade.test.js && node test/gerativo-moldura.test.js && node test/gerativo-heroi.test.js && node test/gerativo-ambiente.test.js && node test/gerativo-variedade.test.js && node test/hibrido.test.js && node test/batida.test.js && node test/estudio-acervo.test.js && node test/estudio-diretor.test.js && node test/estudio-marca.test.js && node test/estudio-novo-projeto.test.js && node test/estudio-plano.test.js && node test/estudio-slides.test.js && node test/estudio-video.test.js && node test/revisor.test.js && node test/assinatura.test.js && node test/biblioteca.test.js && node test/mcp.test.js && node test/personas.test.js && node test/efeitos.test.js && node test/auto.test.js && node test/colar.test.js && node test/orquestra.test.js && node test/shell-limpo.test.js && node test/editar-remotion.test.js && node test/variacao3d.test.js && node test/portao-skill.test.js && node test/clones.test.js && node test/kokoro.test.js && node test/fluido.test.js && node test/zoom.test.js && node test/personagem.test.js && node test/tela.test.js && node test/margem.test.js && node test/vazamento.test.js && node test/colecao.test.js"
12
12
  },
13
13
  "engines": {
14
14
  "node": ">=18.17.0"