primocode 10.3.0 → 10.4.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 +29 -2
- package/bin/primocode.js +111 -41
- package/lib/act.js +19 -5
- package/lib/catalogo.js +10 -4
- package/lib/entrada.js +261 -67
- package/lib/estudio/acervo.js +21 -1
- package/lib/estudio/estilo.js +12 -5
- package/lib/estudio/gerativo.js +18 -0
- package/lib/estudio/index.js +35 -3
- package/lib/estudio/kit/gerativo.jsx +62 -14
- package/lib/estudio/kit/index.jsx +2 -1
- package/lib/estudio/kit/personagem.jsx +582 -0
- package/lib/estudio/kit.js +3 -1
- package/lib/estudio/lancamento.js +5 -2
- package/lib/estudio/margem.js +235 -0
- package/lib/estudio/personagem.js +232 -0
- package/lib/estudio/print-site.js +190 -1
- package/lib/tela.js +223 -0
- package/lib/ui.js +70 -88
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# PrimoCode v10.
|
|
1
|
+
# PrimoCode v10.4.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.
|
|
12
|
+
▐▛███▜▌ Primo Code v10.4.1
|
|
13
13
|
▝▜█████▛▘ Bem-vindo de volta, Joel
|
|
14
14
|
▘▘ ▝▝ ~/primocode · /dir <pasta> muda
|
|
15
15
|
```
|
|
@@ -17,6 +17,33 @@ está no limite de pedidos, o próximo atende. Você não vê o erro, e não pag
|
|
|
17
17
|
Personalize o mascote (`primo`, `fox`, `cat`) e o nome com que ele te chama —
|
|
18
18
|
veja `/mascot` e `/name`.
|
|
19
19
|
|
|
20
|
+
## O que mudou na 10.4
|
|
21
|
+
|
|
22
|
+
- **A tela do OpenCode.** O PrimoCode abre em tela cheia: a conversa rola em
|
|
23
|
+
cima, a caixa de digitar fica presa no pé, e a barra da direita mostra a
|
|
24
|
+
sessão, o contexto (tokens e % usado), o modo, a pasta e a versão. Sem
|
|
25
|
+
conversa, a home põe o logo e a caixa no meio. `Tab` troca o modo, `Ctrl+P`
|
|
26
|
+
abre os comandos (o menu abre em cima da caixa), a roda do mouse e o
|
|
27
|
+
`PgUp`/`PgDn` rolam a conversa. `PRIMOCODE_TELA=linha` volta ao jeito antigo.
|
|
28
|
+
- **Motion sem encostar na borda.** Margem de 6% nas laterais e 7% em cima e
|
|
29
|
+
embaixo, e 4% entre elementos — garantido em código antes do render.
|
|
30
|
+
- **Vídeo gravado em 3D (`estilo: personagem`).** Personagens modelados — gente
|
|
31
|
+
ou robô — conversando num set (podcast, estúdio, escritório). A boca segue a
|
|
32
|
+
voz letra por letra, eles piscam, gesticulam e viram a cabeça para quem fala.
|
|
33
|
+
A câmera corta como num programa gravado: aberto, ombro, close e detalhe,
|
|
34
|
+
com o balanço de câmera na mão. Por cima: legenda grossa palavra por
|
|
35
|
+
palavra, o nome de quem fala, o cartão de destaque, o REC e o grão de filme.
|
|
36
|
+
Peça "um podcast", "uma entrevista" ou "o Claude falando em 3D".
|
|
37
|
+
- **A peça do site, tirada pelo código.** `estudio_midia acao:elementos` lê o
|
|
38
|
+
site e lista logo, menu, título, botões, cartões, preço; `acao:elemento` tira
|
|
39
|
+
uma delas sozinha, em PNG transparente no dobro da resolução, com o código
|
|
40
|
+
(HTML, fonte, cor, raio) para redesenhar se quiser.
|
|
41
|
+
- **Recorte de fundo com IA sem instalar nada.** Com o `uv` na máquina, o
|
|
42
|
+
`acao:recortar` usa o rembg num ambiente à parte, em vez do corte por cor.
|
|
43
|
+
- **O logo do Claude saiu do cardápio dos vídeos.**
|
|
44
|
+
- **Com Claude, Codex ou chave própria, o agente não esquece o arquivo que
|
|
45
|
+
acabou de ler** (antes relia o mesmo arquivo várias vezes).
|
|
46
|
+
|
|
20
47
|
## O que mudou na 10.0
|
|
21
48
|
|
|
22
49
|
- **A potência vem do sistema, não do modelo.** O mesmo pedido de landing
|
package/bin/primocode.js
CHANGED
|
@@ -427,30 +427,21 @@ function ramoDoGit(dir) {
|
|
|
427
427
|
return ramo;
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
+
/* O que a caixa mostra além do texto, no desenho do OpenCode: DENTRO do
|
|
431
|
+
painel, o modo e o esforço ("Auto · alto" — o "Build · Nemotron" de lá);
|
|
432
|
+
FORA, uma linha só, com a pasta e o ramo à esquerda e o contexto e os
|
|
433
|
+
atalhos à direita. A cor da barra do painel é a do modo. */
|
|
434
|
+
const COR_MODO = { auto: '\x1b[38;5;75m', primocode: '\x1b[38;5;216m', chat: '\x1b[38;5;45m' };
|
|
435
|
+
const NOME_MODO = { auto: 'Auto', primocode: 'Primo Code', chat: 'Chat' };
|
|
436
|
+
|
|
430
437
|
function barraDeStatus() {
|
|
431
438
|
const partes = [];
|
|
432
439
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
vermelho a partir de 100%, que é quando `/compact` deixa de ser
|
|
437
|
-
conselho e vira o que fazer. */
|
|
438
|
-
const uso = memoria.usoDoContexto(state.memoria, state.history);
|
|
439
|
-
if (uso.pct >= 5) {
|
|
440
|
-
partes.push(uso.pct >= 100 ? c.err(`${uso.pct}% ctx`)
|
|
441
|
-
: uso.pct >= 70 ? c.warn(`${uso.pct}% ctx`)
|
|
442
|
-
: c.dim(`${uso.pct}% ctx`));
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// O esforço, que é o que escolhe o modelo. Não há provedor para mostrar:
|
|
446
|
-
// a API vem com o produto.
|
|
447
|
-
partes.push(c.muted(effort.normalizar(state.config.effort)));
|
|
448
|
-
|
|
449
|
-
// A PASTA. O nome dela, e o caminho curto quando não é um projeto — quem
|
|
450
|
-
// apontou com /dir para fora do ~/primocode precisa ver onde está.
|
|
440
|
+
// A PASTA, e o ramo do git colado nela ("~/primocode/loja:main"), como
|
|
441
|
+
// o rodapé do OpenCode. Quem apontou com /dir para fora do ~/primocode
|
|
442
|
+
// precisa ver onde está.
|
|
451
443
|
const dir = state.workDir || process.cwd();
|
|
452
444
|
partes.push(c.brandDim(shortPath(dir)));
|
|
453
|
-
|
|
454
445
|
const ramo = ramoDoGit(dir);
|
|
455
446
|
if (ramo) partes.push(c.dim('⎇ ' + ramo));
|
|
456
447
|
|
|
@@ -463,25 +454,73 @@ function barraDeStatus() {
|
|
|
463
454
|
esquece que deu — e ela vale até o PrimoCode fechar. */
|
|
464
455
|
if (state.controle && state.controle.liberado) partes.push(c.warn('controle do pc'));
|
|
465
456
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
457
|
+
/* O CONTEXTO. Só aparece depois que há conversa: um "ctx 0%" na abertura
|
|
458
|
+
é ruído, e ruído que aparece sempre é o que a pessoa aprende a não
|
|
459
|
+
enxergar — inclusive quando marcar 90%. Âmbar a partir de 70% e
|
|
460
|
+
vermelho a partir de 100%, que é quando `/compact` deixa de ser
|
|
461
|
+
conselho e vira o que fazer. */
|
|
462
|
+
const direita = [];
|
|
463
|
+
const uso = memoria.usoDoContexto(state.memoria, state.history);
|
|
464
|
+
if (uso.pct >= 5) {
|
|
465
|
+
direita.push(uso.pct >= 100 ? c.err(`${uso.pct}% ctx`)
|
|
466
|
+
: uso.pct >= 70 ? c.warn(`${uso.pct}% ctx`)
|
|
467
|
+
: c.dim(`${uso.pct}% ctx`));
|
|
468
|
+
}
|
|
469
|
+
direita.push(state.trabalhando
|
|
470
|
+
? c.warn('trabalhando') + c.dim(' · ') + c.white('esc esc') + c.dim(' para')
|
|
471
|
+
: c.white('tab') + c.dim(' modos ') + c.white('ctrl+p') + c.dim(' comandos'));
|
|
472
|
+
|
|
473
|
+
/* O modo DENTRO do painel. No auto, depois do primeiro pedido, ele diz
|
|
474
|
+
qual modo acabou respondendo — senão você nunca sabe quem respondeu. */
|
|
475
|
+
const cor = COR_MODO[state.mode] || COR_MODO.auto;
|
|
476
|
+
let modo = `${cor}${NOME_MODO[state.mode] || 'Auto'}${c.reset}`;
|
|
477
|
+
if (state.mode === 'auto' && state.modoEfetivo) {
|
|
478
|
+
modo += c.dim(' → ') + c.muted(NOME_MODO[state.modoEfetivo] || state.modoEfetivo);
|
|
475
479
|
}
|
|
476
|
-
|
|
480
|
+
modo += c.dim(' · ') + c.white(effort.normalizar(state.config.effort)) + ' ' + c.dim('PrimoCode');
|
|
477
481
|
|
|
478
|
-
|
|
482
|
+
return { modo, cor, esquerda: partes.join(c.dim(' · ')), direita: direita.join(c.dim(' ')) };
|
|
483
|
+
}
|
|
479
484
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
485
|
+
/* A barra da direita da tela cheia, como a do OpenCode: o contexto (quantos
|
|
486
|
+
tokens a conversa já ocupa e quanto isso é do teto), o modo e o esforço, e
|
|
487
|
+
no pé a pasta com o ramo e a versão. */
|
|
488
|
+
function barraLateral() {
|
|
489
|
+
const uso = memoria.usoDoContexto(state.memoria, state.history);
|
|
490
|
+
const dir = state.workDir || process.cwd();
|
|
491
|
+
const ramo = ramoDoGit(dir);
|
|
492
|
+
const m = state.mode === 'auto' && state.modoEfetivo ? state.modoEfetivo : state.mode;
|
|
493
|
+
const secoes = [
|
|
494
|
+
{ titulo: 'Contexto', linhas: [
|
|
495
|
+
c.muted(`${uso.tokens.toLocaleString('pt-BR')} tokens`),
|
|
496
|
+
(uso.pct >= 100 ? c.err : uso.pct >= 70 ? c.warn : c.muted)(`${uso.pct}% usado`),
|
|
497
|
+
] },
|
|
498
|
+
{ titulo: 'Modo', linhas: [
|
|
499
|
+
`${COR_MODO[m] || COR_MODO.auto}${NOME_MODO[m] || m}${c.reset}` + c.muted(` · esforço ${effort.normalizar(state.config.effort)}`),
|
|
500
|
+
] },
|
|
501
|
+
];
|
|
502
|
+
if (state.attachedFiles.length) {
|
|
503
|
+
secoes.push({ titulo: 'Anexos', linhas: state.attachedFiles.map((f) => c.muted(path.basename(f.path))) });
|
|
504
|
+
}
|
|
505
|
+
if (state.controle && state.controle.liberado) secoes.push({ titulo: 'Controle do PC', linhas: [c.warn('liberado até fechar')] });
|
|
506
|
+
return {
|
|
507
|
+
secoes,
|
|
508
|
+
rodape: [
|
|
509
|
+
c.muted(shortPath(dir)) + (ramo ? c.white(':' + ramo) : ''),
|
|
510
|
+
c.brand('●') + ' ' + c.bold(c.white('Primo')) + c.white('Code') + ' ' + c.dim(VERSION),
|
|
511
|
+
],
|
|
512
|
+
};
|
|
513
|
+
}
|
|
483
514
|
|
|
484
|
-
|
|
515
|
+
/* O Tab com a caixa vazia gira o modo, como o "tab agents" do OpenCode:
|
|
516
|
+
auto → primo code → chat → auto. A escolha fica salva, como a do /mode. */
|
|
517
|
+
function trocarModo() {
|
|
518
|
+
const ordem = ['auto', 'primocode', 'chat'];
|
|
519
|
+
const m = ordem[(ordem.indexOf(state.mode) + 1) % ordem.length];
|
|
520
|
+
state.mode = m;
|
|
521
|
+
state.modoEfetivo = null;
|
|
522
|
+
cfg.saveConfig({ mode: m });
|
|
523
|
+
refreshPrompt();
|
|
485
524
|
}
|
|
486
525
|
|
|
487
526
|
// A barra se redesenha sozinha a cada quadro; isto só pede um quadro novo.
|
|
@@ -1043,10 +1082,18 @@ async function sendPrompt(userText) {
|
|
|
1043
1082
|
|
|
1044
1083
|
// PRIMOCODE_TEMPOS=1: uma linha no fim com o custo de cada fase.
|
|
1045
1084
|
tempos.comecar();
|
|
1085
|
+
const inicio = Date.now();
|
|
1046
1086
|
try { await enviarPedido(userText); }
|
|
1047
1087
|
finally {
|
|
1048
1088
|
const linha = tempos.relatorio();
|
|
1049
1089
|
if (linha) console.log(c.dim(` ⏱ ${linha}`));
|
|
1090
|
+
/* O fecho de cada resposta, como no OpenCode ("▣ Build · Nemotron ·
|
|
1091
|
+
4.0s"): quem respondeu, com que esforço, e quanto levou. */
|
|
1092
|
+
const m = state.mode === 'auto' ? (state.modoEfetivo || 'auto') : state.mode;
|
|
1093
|
+
if (!state.semTerminal) {
|
|
1094
|
+
console.log('\n ' + `${COR_MODO[m] || COR_MODO.auto}▣${c.reset} ` + c.white(NOME_MODO[m] || m)
|
|
1095
|
+
+ c.dim(` · ${effort.normalizar(state.config.effort)} · ${((Date.now() - inicio) / 1000).toFixed(1)}s`) + '\n');
|
|
1096
|
+
}
|
|
1050
1097
|
}
|
|
1051
1098
|
}
|
|
1052
1099
|
|
|
@@ -2690,17 +2737,39 @@ async function main() {
|
|
|
2690
2737
|
process.exit(0);
|
|
2691
2738
|
}
|
|
2692
2739
|
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2740
|
+
/* A TELA CHEIA do OpenCode (lib/tela.js) é o padrão: a conversa rola em
|
|
2741
|
+
cima, a caixa fica presa no pé, a barra da direita mostra a sessão e o
|
|
2742
|
+
contexto, e sem conversa a home põe o logo e a caixa no meio da tela.
|
|
2743
|
+
PRIMOCODE_TELA=linha (ou "tela": "linha" no config) volta ao jeito
|
|
2744
|
+
antigo, que escreve em linha pelo terminal. */
|
|
2745
|
+
const telaCheia = process.stdout.isTTY && process.stdout.rows >= 16
|
|
2746
|
+
&& (process.env.PRIMOCODE_TELA || state.config.tela || 'cheia') !== 'linha';
|
|
2747
|
+
if (!telaCheia) {
|
|
2748
|
+
if (process.stdout.isTTY && process.stdout.rows >= 20) {
|
|
2749
|
+
const rows = process.stdout.rows;
|
|
2750
|
+
const alto = 8 + 6; // o logo com a versão + a caixa
|
|
2751
|
+
process.stdout.write('\n'.repeat(rows) + '\x1b[H' + '\n'.repeat(Math.max(0, Math.floor((rows - alto) / 2) - 1)));
|
|
2752
|
+
}
|
|
2753
|
+
console.log(welcomeBox(VERSION, state.workDir ? shortPath(state.workDir) : null, {
|
|
2754
|
+
pastaPadrao: state.workDirOrigem === 'padrao',
|
|
2755
|
+
}));
|
|
2756
|
+
}
|
|
2699
2757
|
rl = entrada.criar({
|
|
2700
2758
|
status: barraDeStatus,
|
|
2701
2759
|
comandos: comandosDoMenu,
|
|
2702
|
-
vazio:
|
|
2760
|
+
vazio: ui.convite(),
|
|
2761
|
+
telaCheia,
|
|
2762
|
+
lateral: barraLateral,
|
|
2763
|
+
versao: VERSION,
|
|
2764
|
+
pasta: () => {
|
|
2765
|
+
const dir = state.workDir || process.cwd();
|
|
2766
|
+
const ramo = ramoDoGit(dir);
|
|
2767
|
+
return shortPath(dir) + (ramo ? ':' + ramo : '');
|
|
2768
|
+
},
|
|
2703
2769
|
});
|
|
2770
|
+
if (telaCheia && state.workDirOrigem === 'padrao') {
|
|
2771
|
+
console.log(c.warn(` pasta padrão: ${shortPath(state.workDir)}`) + c.dim(' · /dir <pasta> para trabalhar numa sua'));
|
|
2772
|
+
}
|
|
2704
2773
|
// O spinner desenha dentro da caixa (lib/entrada.js mostrarSpinner).
|
|
2705
2774
|
if (rl.tty) Spinner.caixa = rl;
|
|
2706
2775
|
ui.titulo('PrimoCode');
|
|
@@ -2739,6 +2808,7 @@ async function main() {
|
|
|
2739
2808
|
// Ctrl+C com a caixa vazia: sair é sair, mas sem deixar o último turno
|
|
2740
2809
|
// pela metade — o sayBye espera a gravação subir.
|
|
2741
2810
|
rl.on('sigint', () => rl.close());
|
|
2811
|
+
rl.on('trocar-modo', trocarModo);
|
|
2742
2812
|
|
|
2743
2813
|
function sayBye() {
|
|
2744
2814
|
if (saidBye) return;
|
package/lib/act.js
CHANGED
|
@@ -333,6 +333,16 @@ function summarizeForModel(result) {
|
|
|
333
333
|
// que o modelo precisa para decidir o próximo passo; os velhos viram peso.
|
|
334
334
|
const RESULTADOS_INTEIROS = 3;
|
|
335
335
|
const RESTO_DO_RESULTADO = 300;
|
|
336
|
+
/* ── COM MOTOR PRÓPRIO, A MEMÓRIA NÃO É AMPUTADA ────────────────────────
|
|
337
|
+
* Os 3 e os 300 de cima são para o teto de ~6 mil tokens por minuto do
|
|
338
|
+
* servidor grátis. Com Claude Code, Codex ou chave própria (`provedor.atual()`)
|
|
339
|
+
* eles valiam igual, e o modelo perdia o arquivo que tinha acabado de ler:
|
|
340
|
+
* no jogo de carro de 09/10 o Claude leu o world.js (458 linhas) sete vezes,
|
|
341
|
+
* em fatias, porque a cada três ferramentas a leitura anterior virava 300
|
|
342
|
+
* caracteres. A pre-leitura já folgava com motor próprio; o compactar agora
|
|
343
|
+
* também. */
|
|
344
|
+
const RESULTADOS_INTEIROS_FOLGADO = 20;
|
|
345
|
+
const RESTO_DO_RESULTADO_FOLGADO = 1500;
|
|
336
346
|
// A pesquisa é MATÉRIA-PRIMA, não passo intermediário: é dela que saem os
|
|
337
347
|
// fatos do vídeo, do post, do slide. Ela sempre fica velha (pesquisa-se no
|
|
338
348
|
// começo, escreve-se no fim), e a 300 caracteres não sobrava fato nenhum — o
|
|
@@ -404,15 +414,17 @@ function encolherEscritas(m) {
|
|
|
404
414
|
* Nenhuma mensagem é REMOVIDA: a API exige que cada role:'tool' continue
|
|
405
415
|
* pareada com o tool_call que a pediu. Só o texto encolhe.
|
|
406
416
|
*/
|
|
407
|
-
function compactar(messages) {
|
|
417
|
+
function compactar(messages, { folgado = false } = {}) {
|
|
408
418
|
const { inteiras, duplicadas } = repeticao.referenciasNaConversa(messages);
|
|
419
|
+
const quantosInteiros = folgado ? RESULTADOS_INTEIROS_FOLGADO : RESULTADOS_INTEIROS;
|
|
420
|
+
const resto = folgado ? RESTO_DO_RESULTADO_FOLGADO : RESTO_DO_RESULTADO;
|
|
409
421
|
|
|
410
422
|
const indices = [];
|
|
411
423
|
for (let i = 0; i < messages.length; i++) if (messages[i].role === 'tool') indices.push(i);
|
|
412
|
-
if (indices.length <=
|
|
424
|
+
if (indices.length <= quantosInteiros && !duplicadas.size && !assistentesAntigas(messages).size) return messages;
|
|
413
425
|
|
|
414
426
|
const chamadas = repeticao.chamadasPorId(messages);
|
|
415
|
-
const encolher = new Set(indices.slice(0, -
|
|
427
|
+
const encolher = new Set(indices.slice(0, -quantosInteiros));
|
|
416
428
|
const antigas = assistentesAntigas(messages);
|
|
417
429
|
return messages.map((m, i) => {
|
|
418
430
|
if (antigas.has(i)) return encolherEscritas(m);
|
|
@@ -421,7 +433,7 @@ function compactar(messages) {
|
|
|
421
433
|
}
|
|
422
434
|
if (inteiras.has(i) || !encolher.has(i)) return m;
|
|
423
435
|
const chamada = chamadas.get(m.tool_call_id);
|
|
424
|
-
const limite = chamada && MATERIA_PRIMA.has(chamada.nome) ? RESTO_DA_PESQUISA :
|
|
436
|
+
const limite = chamada && MATERIA_PRIMA.has(chamada.nome) ? Math.max(RESTO_DA_PESQUISA, resto) : resto;
|
|
425
437
|
const texto = String(m.content || '');
|
|
426
438
|
if (texto.length <= limite) return m;
|
|
427
439
|
return { ...m, content: texto.slice(0, limite) + `…(resultado antigo encurtado; eram ${texto.length} caracteres)` };
|
|
@@ -984,7 +996,9 @@ async function runAct(userPrompt, opts) {
|
|
|
984
996
|
emVoo = new AbortController();
|
|
985
997
|
const tRodada = Date.now();
|
|
986
998
|
try {
|
|
987
|
-
|
|
999
|
+
let folgado = false;
|
|
1000
|
+
try { folgado = !!require('./local/provedor.js').atual(); } catch { /* sem provedor próprio */ }
|
|
1001
|
+
const enviadas = compactar(messages, { folgado });
|
|
988
1002
|
traco(`volta ${voltas}${subagent ? ' (agente)' : ''} · ${enviadas.length} mensagens · ${JSON.stringify(enviadas).length} caracteres`);
|
|
989
1003
|
await motor.requestAct(server, { messages: enviadas, model, effort, token, projectDir: pastaAtual, projectName, subagent, sinal: emVoo.signal }, (evt) => {
|
|
990
1004
|
if (evt.erro) { streamError = evt.erro; return; }
|
package/lib/catalogo.js
CHANGED
|
@@ -94,8 +94,8 @@ const GRUPOS = [
|
|
|
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
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) | 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|false: o quadro fixo do reel (cantos, o assunto no alto, "01 · seção", o tempo) — ponha só se o vídeo pede cara de peça de design; vazio, o PrimoCode alterna com memória e às vezes não põe; heroi em cada parte: o desenho da batida além do texto, tirado do CONTEÚDO — um ícone do Iconify do objeto que a fala cita ("mdi:bread-slice", "mdi:qrcode"; procure com estudio_midia 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
|
-
+ '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. '
|
|
98
|
-
+ 'marca e lancamento aceitam visual:orbe|vetor|
|
|
97
|
+
+ '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 (o vídeo GRAVADO em 3D — cada parte é UMA fala {falante, texto, emocao?, destaque?}, sem tipo: personagens modelados — gente ou robô — conversando num set de podcast/estúdio/escritório, a boca segue a voz, gestos, e a câmera corta entre aberto, ombro e close; use quando o pedido fala em personagem, boneco, avatar, podcast, entrevista, ou alguém FALANDO em 3D. Com dois falantes (falante em cada parte) eles conversam; personagens:{"nome":{tipo:robo|pessoa, pele, cabelo:curto|cacheado|longo|topete|coque|careca, corCabelo, roupa, oculos, barba, rotulo}} — nome de IA vira robô sozinho; emocao feliz|serio|surpreso|pensativo|animado|curioso e destaque:"texto curto" (o cartão na tela) em cada parte; cenario podcast|estudio|escritorio|noite). '
|
|
98
|
+
+ 'marca e lancamento aceitam visual:orbe|vetor|esfera|grade|site|padrao|cromo|cartoes|painel|cores numa parte. marca.nome dá o nome. '
|
|
99
99
|
+ '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. '
|
|
100
100
|
+ 'cena (opcional): o número de UMA cena para trocar só ela, sem reescrever o resto. '
|
|
101
101
|
+ 'motivo (opcional): fica no caderno. voltarPara (opcional): nome de uma versão. '
|
|
@@ -104,13 +104,19 @@ const GRUPOS = [
|
|
|
104
104
|
+ '"anima": {propriedade: [{t,v,e}]} — quadros-chave em segundos. A cena tem "camera": {zoom,x,y,rotacao} e '
|
|
105
105
|
+ '"perspectiva" para 3D. Cena.transicao {tipo,duracao}: corte|esmaecer|deslizar|zoom|giro|mascara|morph|chicote|soco|flash|liquido, '
|
|
106
106
|
+ 'e as 3D virar|cubo|profundidade|inclinar (mexem nas duas cenas) — use 3D se o vídeo pede volume (produto, site, tecnologia); em editorial plano, prefira as planas. '
|
|
107
|
-
+ 'NÃO existe layout pronto nem tema — cada propriedade do vídeo é escolha sua, a cada vídeo.'
|
|
107
|
+
+ 'NÃO existe layout pronto nem tema — cada propriedade do vídeo é escolha sua, a cada vídeo. '
|
|
108
|
+
+ 'ÁREA SEGURA: nada a menos de 6% da largura e 7% da altura da borda, e 4% do lado curto entre dois elementos no ar juntos — '
|
|
109
|
+
+ 'o render empurra para dentro, encolhe o texto largo e afasta o colado; enfeite de fundo marque decorativo: true.' },
|
|
108
110
|
|
|
109
111
|
{ tool: 'estudio_midia', escreve: true, faz: 'procura e baixa ícone, imagem, música e efeito sonoro da internet, e lista as fontes',
|
|
110
|
-
args: 'acao: imagem | icones | som | trilha | efeito | fontes | print. termo. id (para baixar). '
|
|
112
|
+
args: 'acao: imagem | icones | som | trilha | efeito | fontes | print | elementos | elemento | recortar. termo. id (para baixar). '
|
|
111
113
|
+ 'print: url — tira o print REAL do site e devolve o src e um elemento {tipo:"kit", peca:"printsite"} pronto '
|
|
112
114
|
+ '(o site de verdade numa janela 3D que rola a página; celular: true para a tela do celular; inteira: false só a primeira dobra). '
|
|
113
115
|
+ 'Use SE o pedido trouxer um site e o vídeo for SOBRE ele (lançamento, tour, case); vídeo que só cita a marca não precisa. '
|
|
116
|
+
+ 'elementos: url — lê o CÓDIGO do site e lista as peças que dá para tirar (logo, menu, titulo, hero, botao, cartao, imagem, formulario, preco, rodape), cada uma com o seletor. '
|
|
117
|
+
+ 'elemento: url + seletor (ou texto: o que está escrito nele) — tira SÓ aquela peça, em PNG com fundo transparente e no dobro da resolução, e devolve o código dela (html e estilo: fonte, cor, raio, sombra) e um elemento {tipo:"imagem"} pronto; '
|
|
118
|
+
+ 'use para pôr o botão, o cartão de preço ou a logo do site animando no vídeo (ou redesenhe a peça como componente a partir do código). '
|
|
119
|
+
+ 'recortar: arquivo — tira o fundo de qualquer foto com IA (rembg), PNG transparente. '
|
|
114
120
|
+ 'efeitos: lista os componentes guardados (os menos usados primeiro). usar-efeito {nome}: copia para o projeto e devolve o elemento — use se um deles servir a ESTE roteiro. '
|
|
115
121
|
+ 'guardar-efeito {arquivo, nome, descricao}: guarda um componente que você escreveu e ficou bom, para os próximos vídeos '
|
|
116
122
|
+ '(o componente: export default de uma função React que recebe {el, t, fps}, num arquivo só, importando só react, remotion, @remotion/*, three). '
|