primocode 10.2.0 → 10.3.0-beta.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.
- package/README.md +2 -2
- package/lib/act.js +5 -0
- package/lib/browser.js +1 -1
- package/lib/catalogo.js +16 -7
- package/lib/estudio/assinatura.js +108 -0
- package/lib/estudio/assinaturas/fluido.json +15 -0
- package/lib/estudio/assinaturas/gerativo.json +15 -0
- package/lib/estudio/diretor.js +70 -4
- package/lib/estudio/efeitos.js +95 -0
- package/lib/estudio/falantes.js +97 -0
- package/lib/estudio/fluido.js +28 -3
- package/lib/estudio/gerativo.js +309 -85
- package/lib/estudio/hibrido.js +1 -1
- package/lib/estudio/index.js +130 -15
- package/lib/estudio/kit/gerativo.jsx +90 -21
- package/lib/estudio/kit/marca.jsx +58 -1
- package/lib/estudio/kit/tres.jsx +4 -1
- package/lib/estudio/kit.js +2 -0
- package/lib/estudio/plano.js +5 -1
- package/lib/estudio/print-site.js +152 -0
- package/lib/estudio/referencias.js +12 -1
- package/lib/estudio/remotion.js +72 -3
- package/lib/estudio/variedade.js +13 -5
- package/package.json +2 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* print-site.js — o print DE VERDADE de um site, para virar cena 3D.
|
|
3
|
+
*
|
|
4
|
+
* "Se eu mandar um site e pedir, dependendo do modelo, ela pode tirar print
|
|
5
|
+
* do site, transformar em 3D e pôr no Motion." (Joel, 09/10/2026)
|
|
6
|
+
*
|
|
7
|
+
* Até aqui, quando o vídeo era SOBRE um site, o estúdio desenhava uma janela
|
|
8
|
+
* de mentira (a peça `site` do kit: manchete, botão e um bloco de cor). Quem
|
|
9
|
+
* conhece o site olhava e não reconhecia — era um site genérico com o nome
|
|
10
|
+
* da marca. O navegador escondido já existia (browser.js, o revisor), mas
|
|
11
|
+
* só servia para conferir página; o `browser_screenshot` tirava print da
|
|
12
|
+
* aba visível da pessoa, do tamanho da janela dela, e só se o agente
|
|
13
|
+
* lembrasse de chamar.
|
|
14
|
+
*
|
|
15
|
+
* Aqui o print sai do navegador ESCONDIDO, com a tela de um computador
|
|
16
|
+
* (1440 de largura) ou de um celular (390, densidade 2), da página inteira
|
|
17
|
+
* (até um teto, para a imagem não virar 40 MB) ou só da primeira dobra. A
|
|
18
|
+
* imagem vai para midia/site/ do projeto e volta com a proporção medida,
|
|
19
|
+
* que é o que a peça `printsite` do kit precisa para rolar a página.
|
|
20
|
+
*
|
|
21
|
+
* Não decide se o vídeo usa o print. Isso é o modelo, pelo cardápio do
|
|
22
|
+
* estudio_midia: o pedido traz um site e o vídeo é sobre ele → vale; o vídeo
|
|
23
|
+
* só cita a marca → não precisa.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
'use strict';
|
|
27
|
+
|
|
28
|
+
const fs = require('fs');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
|
|
31
|
+
const TELAS = {
|
|
32
|
+
computador: { width: 1440, height: 900, deviceScaleFactor: 1, mobile: false },
|
|
33
|
+
celular: { width: 390, height: 844, deviceScaleFactor: 2, mobile: true },
|
|
34
|
+
};
|
|
35
|
+
// Página mais longa que isto é cortada: um print de 1440×20000 passa de 10 MB
|
|
36
|
+
// e o vídeo nunca mostra mais que umas três telas rolando.
|
|
37
|
+
const ALTURA_MAX = 6000;
|
|
38
|
+
|
|
39
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
40
|
+
|
|
41
|
+
/** A URL que o navegador pode abrir — só http(s). null = não é site. */
|
|
42
|
+
function normalizarUrl(url) {
|
|
43
|
+
let u = String(url || '').trim();
|
|
44
|
+
if (!u) return null;
|
|
45
|
+
if (!/^[a-z][a-z0-9+.-]*:/i.test(u)) u = 'https://' + u;
|
|
46
|
+
try {
|
|
47
|
+
const p = new URL(u);
|
|
48
|
+
return /^https?:$/.test(p.protocol) && p.hostname ? p.toString() : null;
|
|
49
|
+
} catch { return null; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Antes do print, a página desce até o fim e volta: foto com loading="lazy"
|
|
53
|
+
só carrega quando chega perto da tela, e sem isto o print saía com metade
|
|
54
|
+
das imagens em branco. */
|
|
55
|
+
const ROLAR_TUDO = `(async () => {
|
|
56
|
+
const passo = innerHeight * 0.8, fim = Math.min(document.documentElement.scrollHeight, ${ALTURA_MAX});
|
|
57
|
+
for (let y = 0; y < fim; y += passo) { scrollTo(0, y); await new Promise((r) => setTimeout(r, 120)); }
|
|
58
|
+
scrollTo(0, 0); await new Promise((r) => setTimeout(r, 300));
|
|
59
|
+
return true;
|
|
60
|
+
})()`;
|
|
61
|
+
const MEDIR = `JSON.stringify({ altura: Math.max(document.documentElement.scrollHeight, document.body ? document.body.scrollHeight : 0), titulo: document.title || '' })`;
|
|
62
|
+
|
|
63
|
+
async function valor(sessao, expression, awaitPromise = false) {
|
|
64
|
+
const r = await sessao.send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise }, 30000).catch(() => null);
|
|
65
|
+
return r && r.result ? r.result.value : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Tira o print do site e grava na pasta do projeto.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} url
|
|
72
|
+
* @param {object} op { pasta, celular, inteira (padrão true), alturaMax, navegador, assentar }
|
|
73
|
+
* @returns {Promise<{ok, src, url, titulo, tela, largura, altura, proporcao, cortada, elemento} | {ok:false, error}>}
|
|
74
|
+
*/
|
|
75
|
+
async function capturarSite(url, op = {}) {
|
|
76
|
+
const alvo = normalizarUrl(url);
|
|
77
|
+
if (!alvo) return { ok: false, error: `"${url || ''}" não é o endereço de um site (http ou https).` };
|
|
78
|
+
if (!op.pasta) return { ok: false, error: 'o print vai para a pasta do projeto — diga o projeto (id).' };
|
|
79
|
+
const navegador = op.navegador || require('../browser.js');
|
|
80
|
+
const tela = op.celular ? 'celular' : 'computador';
|
|
81
|
+
const metrica = TELAS[tela];
|
|
82
|
+
// No celular a página é três vezes mais comprida: o teto é menor.
|
|
83
|
+
const teto = Math.max(metrica.height, Number(op.alturaMax) || (metrica.mobile ? 4000 : ALTURA_MAX));
|
|
84
|
+
|
|
85
|
+
let aba;
|
|
86
|
+
try {
|
|
87
|
+
/* A aba nasce em branco e só depois navega: a tela (largura, celular)
|
|
88
|
+
tem de valer ANTES do site carregar, senão o site responsivo já
|
|
89
|
+
escolheu o layout de computador e o "print do celular" sai largo. */
|
|
90
|
+
aba = await navegador.abaEscondida('about:blank', { assentar: 0 });
|
|
91
|
+
const s = aba.sessao;
|
|
92
|
+
await s.send('Emulation.setDeviceMetricsOverride', metrica);
|
|
93
|
+
if (metrica.mobile) await s.send('Emulation.setTouchEmulationEnabled', { enabled: true }).catch(() => {});
|
|
94
|
+
await s.send('Page.navigate', { url: alvo });
|
|
95
|
+
const prazo = Date.now() + (op.prazo || 20000);
|
|
96
|
+
while (Date.now() < prazo) {
|
|
97
|
+
if (await valor(s, 'document.readyState') === 'complete') break;
|
|
98
|
+
await sleep(200);
|
|
99
|
+
}
|
|
100
|
+
await sleep(op.assentar == null ? 1500 : op.assentar);
|
|
101
|
+
if (op.inteira !== false) await valor(s, ROLAR_TUDO, true);
|
|
102
|
+
|
|
103
|
+
let medida = {};
|
|
104
|
+
try { medida = JSON.parse(await valor(s, MEDIR) || '{}'); } catch { /* segue com a tela */ }
|
|
105
|
+
const alturaPagina = Math.max(metrica.height, Number(medida.altura) || metrica.height);
|
|
106
|
+
const altura = op.inteira === false ? metrica.height : Math.min(alturaPagina, teto);
|
|
107
|
+
|
|
108
|
+
const r = await s.send('Page.captureScreenshot', {
|
|
109
|
+
format: 'jpeg', quality: 88, captureBeyondViewport: true,
|
|
110
|
+
clip: { x: 0, y: 0, width: metrica.width, height: altura, scale: 1 },
|
|
111
|
+
}, 60000);
|
|
112
|
+
if (!r || !r.data) return { ok: false, error: 'o navegador devolveu um print vazio.' };
|
|
113
|
+
|
|
114
|
+
const host = new URL(alvo).hostname.replace(/^www\./, '').replace(/[^a-z0-9.-]/gi, '-');
|
|
115
|
+
const src = `midia/site/${host}-${tela}${op.inteira === false ? '-dobra' : ''}.jpg`;
|
|
116
|
+
const cheio = path.join(op.pasta, src);
|
|
117
|
+
fs.mkdirSync(path.dirname(cheio), { recursive: true });
|
|
118
|
+
fs.writeFileSync(cheio, Buffer.from(r.data, 'base64'));
|
|
119
|
+
|
|
120
|
+
const proporcao = Math.round((altura / metrica.width) * 1000) / 1000;
|
|
121
|
+
const feito = {
|
|
122
|
+
ok: true, src, url: alvo, titulo: medida.titulo || '', tela,
|
|
123
|
+
largura: metrica.width, altura, proporcao,
|
|
124
|
+
cortada: op.inteira !== false && alturaPagina > teto,
|
|
125
|
+
};
|
|
126
|
+
feito.elemento = elementoDoPrint(feito);
|
|
127
|
+
feito.nota = 'O elemento é uma sugestão: ajuste x, y, largura e o tempo ao seu plano. '
|
|
128
|
+
+ 'rolar: true desce a página devagar; inclinacao muda o ângulo 3D.';
|
|
129
|
+
return feito;
|
|
130
|
+
} catch (e) {
|
|
131
|
+
return { ok: false, error: `não consegui tirar o print de ${alvo}: ${e.message}` };
|
|
132
|
+
} finally {
|
|
133
|
+
if (aba) { try { await aba.fechar(); } catch { /* a aba já fechou */ } }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* O elemento de plano que mostra o print: a janela (ou o celular) em 3D.
|
|
139
|
+
* Num quadro 1920×1080 por padrão — o modelo ajusta ao formato dele.
|
|
140
|
+
*/
|
|
141
|
+
function elementoDoPrint(print, { quadroW = 1920, quadroH = 1080, t = 0 } = {}) {
|
|
142
|
+
const celular = print.tela === 'celular';
|
|
143
|
+
const largura = celular ? Math.round(quadroH * 0.42) : Math.round(quadroW * 0.66);
|
|
144
|
+
const altura = celular ? Math.round(largura * 2.05) : Math.round(largura * 0.64);
|
|
145
|
+
return {
|
|
146
|
+
tipo: 'kit', peca: 'printsite', src: print.src, proporcao: print.proporcao,
|
|
147
|
+
celular, url: print.url, t, rolar: print.proporcao > (altura / largura) * 1.2,
|
|
148
|
+
x: Math.round((quadroW - largura) / 2), y: Math.round((quadroH - altura) / 2), largura, altura,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = { capturarSite, elementoDoPrint, normalizarUrl, TELAS, ALTURA_MAX };
|
|
@@ -66,8 +66,19 @@ function inferir(texto) {
|
|
|
66
66
|
return { modo: 'sem', escopo: '', explicito: false };
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/* O QUE A PESSOA DISSE dos vídeos (assinatura.js): as últimas notas da
|
|
70
|
+
assinatura do fluido vão junto da instrução, para o agente ler ANTES de
|
|
71
|
+
escrever o roteiro — o feedback de um vídeo vale para o próximo. */
|
|
72
|
+
function notasDaAssinatura() {
|
|
73
|
+
try {
|
|
74
|
+
const notas = require('./assinatura.js').notasRecentes('fluido', 6);
|
|
75
|
+
return notas.length ? '\n[O QUE A PESSOA DISSE DOS ÚLTIMOS VÍDEOS — assinatura fluido] ' + notas.map((n) => `(${n.quando}) ${n.texto}`).join(' · ') : '';
|
|
76
|
+
} catch { return ''; }
|
|
77
|
+
}
|
|
78
|
+
|
|
69
79
|
/** A instrução para o agente, conforme o modo. */
|
|
70
|
-
function instrucao(
|
|
80
|
+
function instrucao(atual = ATUAL) { const i = instrucaoDoModo(atual); return i ? i + notasDaAssinatura() : ''; }
|
|
81
|
+
function instrucaoDoModo({ modo, escopo, explicito } = ATUAL) {
|
|
71
82
|
// O vídeo sem imagem é motion: o agente escolhe o estilo de motion que serve ao roteiro, não um fixo.
|
|
72
83
|
const motion = 'O vídeo é MOTION: tipografia, formas, ícones, animação e som. Escolha em roteiro.estilo o motion que serve a este '
|
|
73
84
|
+ 'roteiro (gerativo, fluido, impacto, vox, marca, lancamento) ou deixe vazio para o rodízio — não repita sempre o mesmo.';
|
package/lib/estudio/remotion.js
CHANGED
|
@@ -103,6 +103,7 @@ import {
|
|
|
103
103
|
} from 'remotion';
|
|
104
104
|
import { morfar, ehCor, misturarCor } from './morfar';
|
|
105
105
|
import { KIT, CURVAS_KIT, Filtros } from './kit';
|
|
106
|
+
import { COMPONENTES } from './componentes';
|
|
106
107
|
|
|
107
108
|
/* ── SUAVIZAÇÃO ──────────────────────────────────────────────────────────
|
|
108
109
|
* Cada curva é uma função de 0..1 em 0..1. "suave" é o padrão porque
|
|
@@ -227,7 +228,7 @@ function Elemento({ el, t, dados }) {
|
|
|
227
228
|
/* A PEÇA DO KIT desenha nas coordenadas da cena (x, y dela são o lugar do
|
|
228
229
|
assunto, não o canto de uma caixa): a caixa dela é o quadro inteiro, e o
|
|
229
230
|
"anima" do elemento move o conjunto. */
|
|
230
|
-
const base = el.tipo === 'kit'
|
|
231
|
+
const base = el.tipo === 'kit' || el.tipo === 'componente'
|
|
231
232
|
? { position: 'absolute', left: 0, top: 0, width: '100%', height: '100%' }
|
|
232
233
|
: posicao(el);
|
|
233
234
|
const move = transformDe(a, {});
|
|
@@ -383,6 +384,20 @@ function Elemento({ el, t, dados }) {
|
|
|
383
384
|
);
|
|
384
385
|
}
|
|
385
386
|
|
|
387
|
+
/* O COMPONENTE que a IA escreveu (ou reusou do catálogo, efeitos.js).
|
|
388
|
+
Antes o plano o aceitava e o Cena não sabia desenhar: o efeito sumia
|
|
389
|
+
do vídeo sem erro. Agora montar() copia o arquivo para src/componentes/
|
|
390
|
+
e ele recebe {el, t, fps}, no quadro inteiro, como a peça do kit. */
|
|
391
|
+
case 'componente': {
|
|
392
|
+
const Comp = COMPONENTES[el.src];
|
|
393
|
+
if (!Comp) return null;
|
|
394
|
+
return (
|
|
395
|
+
<div style={{ ...estilo, transformOrigin: el.origem || ((el.x || 0) + 'px ' + (el.y || 0) + 'px') }}>
|
|
396
|
+
<Comp el={el} t={tLocal} fps={dados.fps} />
|
|
397
|
+
</div>
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
386
401
|
case 'grupo':
|
|
387
402
|
return (
|
|
388
403
|
<div style={{
|
|
@@ -611,9 +626,41 @@ import icones from './icones.json';
|
|
|
611
626
|
/* A transição vive em quem SAI e em quem ENTRA ao mesmo tempo: as duas cenas
|
|
612
627
|
* ficam no ar durante o cruzamento. Fazer só a que entra aparecer por cima dá
|
|
613
628
|
* o "piscar" de fundo que denuncia corte mal feito. */
|
|
614
|
-
|
|
629
|
+
/* AS TRANSIÇÕES EM 3D (09/10: "quero mais animações, mais efeitos, e efeitos
|
|
630
|
+
* mais 3D dependendo do vídeo"). As de antes giravam só a cena que entra, sem
|
|
631
|
+
* perspectiva: o "giro" era um achatamento, não um giro. Estas quatro mexem
|
|
632
|
+
* nas DUAS cenas (a que sai também recebe o progresso, em "saida") e ficam
|
|
633
|
+
* num palco com perspectiva:
|
|
634
|
+
* virar a cena vira como um cartão, e o verso é a próxima
|
|
635
|
+
* cubo as duas são faces de um cubo que gira
|
|
636
|
+
* profundidade a que sai passa pela câmera; a nova vem do fundo, desfocada
|
|
637
|
+
* inclinar a nova levanta deitada, como uma folha que se ergue */
|
|
638
|
+
const TRANS_3D = { virar: 1, cubo: 1, profundidade: 1, inclinar: 1 };
|
|
639
|
+
function estilo3d(tipo, p, saindo) {
|
|
640
|
+
const k = p < 0.5 ? 4 * p * p * p : 1 - Math.pow(-2 * p + 2, 3) / 2;
|
|
641
|
+
if (tipo === 'virar') return { transform: 'rotateY(' + (saindo ? 180 * k : -180 + 180 * k) + 'deg)', backfaceVisibility: 'hidden' };
|
|
642
|
+
if (tipo === 'cubo') return saindo
|
|
643
|
+
? { transformOrigin: '100% 50%', transform: 'translateX(' + -100 * k + '%) rotateY(' + -90 * k + 'deg)' }
|
|
644
|
+
: { transformOrigin: '0% 50%', transform: 'translateX(' + 100 * (1 - k) + '%) rotateY(' + 90 * (1 - k) + 'deg)' };
|
|
645
|
+
if (tipo === 'profundidade') return saindo
|
|
646
|
+
? { transform: 'translateZ(' + 900 * k + 'px)', opacity: 1 - k }
|
|
647
|
+
: { transform: 'translateZ(' + -1400 * (1 - k) + 'px)', filter: 'blur(' + (1 - k) * 16 + 'px)', opacity: Math.min(1, k * 2.5) };
|
|
648
|
+
if (tipo === 'inclinar') return saindo
|
|
649
|
+
? { transform: 'translateY(' + -12 * k + '%) scale(' + (1 - 0.08 * k) + ')', filter: 'brightness(' + (1 - 0.5 * k) + ')' }
|
|
650
|
+
: { transformOrigin: '50% 100%', transform: 'rotateX(' + 72 * (1 - k) + 'deg)', opacity: Math.min(1, k * 3) };
|
|
651
|
+
return {};
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function Transicao({ tipo, progresso, saida, children }) {
|
|
615
655
|
const p = progresso;
|
|
616
656
|
const { width: W, height: H } = useVideoConfig();
|
|
657
|
+
// A cena que sai, quando a próxima entra por uma transição 3D, mexe também.
|
|
658
|
+
if (saida && TRANS_3D[saida.tipo] && saida.p > 0 && !(TRANS_3D[tipo] && p < 1)) {
|
|
659
|
+
return <AbsoluteFill style={{ perspective: 1800 }}><AbsoluteFill style={estilo3d(saida.tipo, saida.p, true)}>{children}</AbsoluteFill></AbsoluteFill>;
|
|
660
|
+
}
|
|
661
|
+
if (TRANS_3D[tipo] && p < 1) {
|
|
662
|
+
return <AbsoluteFill style={{ perspective: 1800 }}><AbsoluteFill style={estilo3d(tipo, p, false)}>{children}</AbsoluteFill></AbsoluteFill>;
|
|
663
|
+
}
|
|
617
664
|
let estilo = {};
|
|
618
665
|
if (tipo === 'esmaecer') estilo = { opacity: p };
|
|
619
666
|
else if (tipo === 'deslizar') estilo = { transform: 'translateX(' + (1 - p) * 100 + '%)' };
|
|
@@ -683,9 +730,12 @@ export const Video = () => {
|
|
|
683
730
|
const progresso = cruz > 0 && local < cruz && i > 0
|
|
684
731
|
? interpolate(local, [0, cruz], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' })
|
|
685
732
|
: 1;
|
|
733
|
+
// O progresso de quem ENTRA depois desta: as transições 3D mexem nas duas.
|
|
734
|
+
const prox = pedacos[i + 1];
|
|
735
|
+
const saida = prox && prox.cruz > 0 ? { tipo: prox.tipo, p: interpolate(frame - prox.de, [0, prox.cruz], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }) } : null;
|
|
686
736
|
return (
|
|
687
737
|
<Sequence key={i} from={de} durationInFrames={dur} name={'cena ' + (i + 1)}>
|
|
688
|
-
<Transicao tipo={i === 0 ? 'corte' : tipo} progresso={progresso}>
|
|
738
|
+
<Transicao tipo={i === 0 ? 'corte' : tipo} progresso={progresso} saida={saida}>
|
|
689
739
|
<Cena cena={cena} dados={dados} />
|
|
690
740
|
</Transicao>
|
|
691
741
|
</Sequence>
|
|
@@ -814,6 +864,24 @@ para este projeto, não para o PrimoCode.
|
|
|
814
864
|
`;
|
|
815
865
|
}
|
|
816
866
|
|
|
867
|
+
/* Os componentes do plano viram import de verdade: o webpack do Remotion não
|
|
868
|
+
carrega arquivo por nome em tempo de render, então cada `src` é copiado
|
|
869
|
+
para src/componentes/ e entra no mapa COMPONENTES pelo mesmo nome. */
|
|
870
|
+
function escreverComponentes(pasta, src, plano) {
|
|
871
|
+
const srcs = new Set();
|
|
872
|
+
const ver = (els) => { for (const el of els || []) { if (el && el.tipo === 'componente' && el.src) srcs.add(el.src); if (el) ver(el.elementos); } };
|
|
873
|
+
for (const c of plano.cenas || []) ver(c.elementos);
|
|
874
|
+
const linhas = [], mapa = [];
|
|
875
|
+
[...srcs].forEach((s, i) => {
|
|
876
|
+
const de = path.resolve(pasta, s);
|
|
877
|
+
if (!de.startsWith(path.resolve(pasta) + path.sep) || !fs.existsSync(de)) return;
|
|
878
|
+
escrever(path.join(src, 'componentes', 'c' + i + path.extname(de)), fs.readFileSync(de, 'utf8'));
|
|
879
|
+
linhas.push(`import * as M${i} from './componentes/c${i}';`);
|
|
880
|
+
mapa.push(` ${JSON.stringify(s)}: M${i}.default || Object.values(M${i}).find((v) => typeof v === 'function'),`);
|
|
881
|
+
});
|
|
882
|
+
escrever(path.join(src, 'componentes.js'), `${linhas.join('\n')}\nexport const COMPONENTES = {\n${mapa.join('\n')}\n};\n`);
|
|
883
|
+
}
|
|
884
|
+
|
|
817
885
|
/**
|
|
818
886
|
* Escreve o projeto Remotion dentro da pasta do projeto do Studio.
|
|
819
887
|
*
|
|
@@ -851,6 +919,7 @@ function montar({ pasta, titulo, plano, icones = {} }) {
|
|
|
851
919
|
// O kit de motion (kit.js): as peças que o elemento {"tipo":"kit"} monta.
|
|
852
920
|
for (const f of require('./kit.js').arquivos()) escrever(path.join(src, f.nome), f.conteudo);
|
|
853
921
|
escrever(path.join(src, 'plano.json'), JSON.stringify(plano, null, 2));
|
|
922
|
+
escreverComponentes(pasta, src, plano);
|
|
854
923
|
escrever(path.join(src, 'icones.json'), JSON.stringify(icones, null, 2));
|
|
855
924
|
escrever(path.join(src, 'fontes.css'), fontesCSS(plano));
|
|
856
925
|
|
package/lib/estudio/variedade.js
CHANGED
|
@@ -20,8 +20,16 @@ const fs = require('fs');
|
|
|
20
20
|
const os = require('os');
|
|
21
21
|
const path = require('path');
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
/* A pasta é lida a cada uso (não no require): o teste troca o HOME depois de
|
|
24
|
+
carregar o módulo e a memória de verdade fica intocada. */
|
|
25
|
+
const PASTA = () => path.join(os.homedir(), '.primocode', 'studio');
|
|
26
|
+
/* Os tipos do gerativo e do fluido (09/10): "vício em cores, músicas, fontes"
|
|
27
|
+
— a paleta, as fontes e o gênero saíam só da semente, sem lembrar do vídeo
|
|
28
|
+
anterior. Agora passam por aqui como o resto. */
|
|
29
|
+
const MEMORIA = { trilha: 16, sfx: 40, broll: 80, icones: 4, estilo: 8, '3d': 24, 'icone-nome': 30, 'paleta-fluido': 6,
|
|
30
|
+
'fonte-fluido': 4, 'humor-gerativo': 4, 'paleta-gerativo': 10, 'fonte-gerativo': 18, 'genero-gerativo': 8,
|
|
31
|
+
'arranjo-icone': 10, 'material-icone': 4, 'heroi-gerativo': 8, 'composicao-gerativo': 12,
|
|
32
|
+
'moldura-gerativo': 4, 'ambiente-gerativo': 4, 'acento-gerativo': 3 };
|
|
25
33
|
|
|
26
34
|
/* A DIVERSIDADE POR PONTOS (07/10): "o sistema não deve simplesmente proibir
|
|
27
35
|
repetição; escolhas usadas recentemente recebem penalidade, as muito usadas
|
|
@@ -40,8 +48,8 @@ const PESO_FREQUENCIA = 0.25;
|
|
|
40
48
|
const MEIA_VIDA_DIAS = 21;
|
|
41
49
|
const EMPATE = 0.05; // dentro disso, é sorteio
|
|
42
50
|
|
|
43
|
-
function arquivo(tipo) { return path.join(PASTA, `${tipo}-usadas.json`); }
|
|
44
|
-
function arquivoHistorico(tipo) { return path.join(PASTA, `${tipo}-historico.json`); }
|
|
51
|
+
function arquivo(tipo) { return path.join(PASTA(), `${tipo}-usadas.json`); }
|
|
52
|
+
function arquivoHistorico(tipo) { return path.join(PASTA(), `${tipo}-historico.json`); }
|
|
45
53
|
|
|
46
54
|
function usadas(tipo) {
|
|
47
55
|
try { const l = JSON.parse(fs.readFileSync(arquivo(tipo), 'utf8')); return Array.isArray(l) ? l : []; } catch { return []; }
|
|
@@ -57,7 +65,7 @@ const usosHoje = (reg, agora) => (reg ? reg.n * Math.pow(0.5, Math.max(0, agora
|
|
|
57
65
|
function marcar(tipo, id, agora = Date.now()) {
|
|
58
66
|
if (!id) return;
|
|
59
67
|
try {
|
|
60
|
-
fs.mkdirSync(PASTA, { recursive: true });
|
|
68
|
+
fs.mkdirSync(PASTA(), { recursive: true });
|
|
61
69
|
// A lista de recentes é maior que a janela de antes: a recência decai, não corta.
|
|
62
70
|
const l = [id, ...usadas(tipo).filter((x) => x !== id)].slice(0, (MEMORIA[tipo] || 20) * 2);
|
|
63
71
|
fs.writeFileSync(arquivo(tipo), JSON.stringify(l));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.3.0-beta.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/hibrido.test.js && node test/batida.test.js && node test/revisor.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"
|
|
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/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"
|
|
12
12
|
},
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=18.17.0"
|