primocode 9.10.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +30 -6
  2. package/bin/primocode.js +100 -341
  3. package/lib/act.js +245 -23
  4. package/lib/browser.js +17 -0
  5. package/lib/casar.js +108 -0
  6. package/lib/catalogo.js +6 -2
  7. package/lib/config.js +10 -3
  8. package/lib/cursor.js +10 -1
  9. package/lib/entrada.js +4 -2
  10. package/lib/estudio/diretor.js +9 -1
  11. package/lib/estudio/estilo.js +51 -0
  12. package/lib/estudio/glifos.js +215 -0
  13. package/lib/estudio/index.js +41 -18
  14. package/lib/estudio/kit/base.jsx +285 -0
  15. package/lib/estudio/kit/index.jsx +11 -0
  16. package/lib/estudio/kit/marca.jsx +741 -0
  17. package/lib/estudio/kit/vox.jsx +357 -0
  18. package/lib/estudio/kit.js +72 -0
  19. package/lib/estudio/lancamento.js +413 -0
  20. package/lib/estudio/motion.js +2 -5
  21. package/lib/estudio/plano.js +28 -3
  22. package/lib/estudio/remotion.js +87 -43
  23. package/lib/estudio/ritmo.js +77 -0
  24. package/lib/estudio/vox.js +385 -0
  25. package/lib/fala.js +1 -94
  26. package/lib/pipeline.js +55 -56
  27. package/lib/repeticao.js +3 -1
  28. package/lib/skills.js +8 -2
  29. package/lib/snapshot.js +111 -0
  30. package/lib/tools.js +79 -20
  31. package/lib/ui.js +13 -6
  32. package/lib/video/analise.js +89 -0
  33. package/lib/video/cli.js +74 -0
  34. package/lib/video/delta.js +90 -0
  35. package/lib/video/edits.js +379 -0
  36. package/lib/video/filters.js +247 -0
  37. package/lib/video/ops.js +322 -0
  38. package/lib/video/projeto.js +133 -0
  39. package/lib/video/render.js +221 -0
  40. package/lib/video/texto.js +142 -0
  41. package/lib/video/timeline.js +310 -0
  42. package/lib/video/transcript.js +124 -0
  43. package/lib/voz.js +75 -223
  44. package/package.json +2 -3
  45. package/skills/motion-de-primeira/SKILL.md +3 -4
  46. package/studio/texto.py +208 -0
  47. package/voz/primo_voz/__main__.py +96 -236
  48. package/voz/primo_voz/transcrever.py +31 -1
  49. package/app/ansi.js +0 -55
  50. package/app/app.css +0 -500
  51. package/app/app.js +0 -1017
  52. package/app/icone.svg +0 -6
  53. package/app/index.html +0 -220
  54. package/app/primo-marca.js +0 -192
  55. package/lib/app.js +0 -615
  56. package/lib/icone.js +0 -263
  57. package/lib/instalar-app.js +0 -231
  58. package/lib/painel.js +0 -291
  59. package/voz/primo_voz/desperta.py +0 -195
  60. package/voz/primo_voz/orbe.py +0 -406
@@ -0,0 +1,285 @@
1
+ /* kit/base.jsx — a gramática de movimento que as peças do kit dividem.
2
+ *
3
+ * Copiado para o projeto Remotion (src/kit/) por remotion.js. Tudo aqui é
4
+ * função do QUADRO: nada de Math.random, Date.now nem transição CSS — o mesmo
5
+ * plano dá o mesmo vídeo em qualquer máquina.
6
+ *
7
+ * As regras vêm do guia MOTION VOX STYLE: ataque rápido, desaceleração
8
+ * forte, repouso seco, sem bounce. Elemento tem energia (mola SNAPPY);
9
+ * câmera não (bezier longa). */
10
+ import React from 'react';
11
+ import { spring, Img, staticFile } from 'remotion';
12
+
13
+ // ── o tempo ──────────────────────────────────────────────────────────────
14
+ export const SNAPPY = { damping: 26, stiffness: 240, mass: 0.65 };
15
+
16
+ /* A mola do elemento: 0 antes de "de", ~1 em uns 10 quadros, sem passar de 1
17
+ (o amortecimento 26 com massa 0,65 fica acima do crítico: não quica). */
18
+ export function mola(t, de, fps, config = SNAPPY) {
19
+ const f = Math.round((t - de) * fps);
20
+ if (f < 0) return 0;
21
+ return spring({ frame: f, fps, config });
22
+ }
23
+
24
+ /* Bezier cúbica como no CSS: resolve x → t por Newton e devolve y. */
25
+ export function bezier(x1, y1, x2, y2) {
26
+ const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx;
27
+ const cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
28
+ const sx = (u) => ((ax * u + bx) * u + cx) * u;
29
+ const sy = (u) => ((ay * u + by) * u + cy) * u;
30
+ const dx = (u) => (3 * ax * u + 2 * bx) * u + cx;
31
+ return (x) => {
32
+ if (x <= 0) return 0;
33
+ if (x >= 1) return 1;
34
+ let u = x;
35
+ for (let i = 0; i < 8; i++) {
36
+ const e = sx(u) - x;
37
+ const d = dx(u);
38
+ if (Math.abs(e) < 1e-5 || Math.abs(d) < 1e-6) break;
39
+ u -= e / d;
40
+ }
41
+ return sy(Math.min(1, Math.max(0, u)));
42
+ };
43
+ }
44
+
45
+ /* As curvas novas, que o Cena.jsx também aceita nos quadros-chave:
46
+ editorial ataque rápido, desaceleração forte (a entrada de elemento)
47
+ camera longa e sem tranco nas pontas (câmera nunca tem energia)
48
+ arrasto a mão que desenha: rápida no começo, arrastando no fim
49
+ acelera a câmera que ganha velocidade antes do corte */
50
+ export const CURVAS_KIT = {
51
+ editorial: bezier(0.16, 1, 0.3, 1),
52
+ camera: bezier(0.55, 0.05, 0.2, 1),
53
+ arrasto: bezier(0.12, 0.8, 0.25, 1),
54
+ acelera: bezier(0.6, 0, 0.9, 0.4),
55
+ };
56
+
57
+ export const entre = (t, a, b) => (b <= a ? (t >= a ? 1 : 0) : Math.min(1, Math.max(0, (t - a) / (b - a))));
58
+ export const lerp = (a, b, k) => a + (b - a) * k;
59
+
60
+ /* Pseudoaleatório pela SEMENTE: o mesmo número dá sempre o mesmo sorteio. */
61
+ export function acaso(n) {
62
+ const x = Math.sin(n * 12.9898 + 78.233) * 43758.5453;
63
+ return x - Math.floor(x);
64
+ }
65
+
66
+ // ── o carimbo ────────────────────────────────────────────────────────────
67
+ /* A palavra carimba: chega com leve rotação e escala maior, assenta em
68
+ poucos quadros e a tinta absorve em duas fases. Nunca desliza, nunca faz
69
+ fade — antes do quadro dela, não existe; no quadro dela, já está lá. */
70
+ export function carimbo(t, de, fps, semente = 1, forca = 1) {
71
+ if (t < de) return { opacity: 0 };
72
+ const m = mola(t, de, fps);
73
+ const f = (t - de) * fps;
74
+ const rot0 = (acaso(semente) - 0.5) * 7 * forca;
75
+ const tinta = f < 2 ? 1.1 : Math.max(0, 0.5 * (1 - (f - 2) / 7));
76
+ return {
77
+ opacity: 1,
78
+ transform: 'rotate(' + (rot0 * (1 - m)).toFixed(3) + 'deg) scale(' + (1 + 0.2 * forca * (1 - m)).toFixed(4) + ')',
79
+ filter: tinta > 0.02 ? 'blur(' + tinta.toFixed(2) + 'px)' : undefined,
80
+ };
81
+ }
82
+
83
+ // ── o traço que se desenha ──────────────────────────────────────────────
84
+ /* Um caminho SVG que se desenha de 0 a 1. pathLength=1 deixa o traço
85
+ independente do tamanho: o dasharray é sempre 1. */
86
+ export function Traco({ d, p, cor, espessura = 4, opacidade = 1, tampa = 'round', filtro }) {
87
+ if (p <= 0) return null;
88
+ return (
89
+ <path d={d} pathLength={1} fill="none" stroke={cor} strokeWidth={espessura} strokeLinecap={tampa}
90
+ strokeLinejoin="round" strokeDasharray="1 1" strokeDashoffset={1 - Math.min(1, p)} opacity={opacidade}
91
+ filter={filtro} />
92
+ );
93
+ }
94
+
95
+ /* A elipse de caneta: não fecha certinho, passa um pouco do começo e treme
96
+ de leve — é a mão do professor apontando, não um <ellipse>. */
97
+ export function elipseMao(cx, cy, rx, ry, semente = 3) {
98
+ const pts = [];
99
+ const n = 26;
100
+ const a0 = -2.2 + acaso(semente) * 0.4;
101
+ for (let i = 0; i <= n; i++) {
102
+ const a = a0 + (i / n) * Math.PI * 2 * 1.12;
103
+ const tremor = 1 + (acaso(semente * 7 + i) - 0.5) * 0.05;
104
+ const espiral = 1 + (i / n) * 0.06;
105
+ pts.push([cx + Math.cos(a) * rx * tremor * espiral, cy + Math.sin(a) * ry * tremor * espiral]);
106
+ }
107
+ return suave(pts);
108
+ }
109
+
110
+ /* O sublinhado de caneta: uma linha que sobe um pouquinho no fim. */
111
+ export function sublinhadoMao(x, y, w, semente = 5) {
112
+ const pts = [];
113
+ for (let i = 0; i <= 8; i++) {
114
+ const k = i / 8;
115
+ pts.push([x + k * w, y + Math.sin(k * 3.1 + semente) * w * 0.006 - k * k * w * 0.012]);
116
+ }
117
+ return suave(pts);
118
+ }
119
+
120
+ /* Catmull-Rom → cúbicas: pontos soltos viram uma curva sem quina. */
121
+ export function suave(pts, fechado = false) {
122
+ if (pts.length < 2) return '';
123
+ const P = (i) => pts[fechado ? (i + pts.length) % pts.length : Math.max(0, Math.min(pts.length - 1, i))];
124
+ let d = 'M ' + pts[0][0].toFixed(1) + ' ' + pts[0][1].toFixed(1);
125
+ const fim = fechado ? pts.length : pts.length - 1;
126
+ for (let i = 0; i < fim; i++) {
127
+ const p0 = P(i - 1), p1 = P(i), p2 = P(i + 1), p3 = P(i + 2);
128
+ const c1 = [p1[0] + (p2[0] - p0[0]) / 6, p1[1] + (p2[1] - p0[1]) / 6];
129
+ const c2 = [p2[0] - (p3[0] - p1[0]) / 6, p2[1] - (p3[1] - p1[1]) / 6];
130
+ d += ' C ' + c1[0].toFixed(1) + ' ' + c1[1].toFixed(1) + ' ' + c2[0].toFixed(1) + ' ' + c2[1].toFixed(1) + ' ' + p2[0].toFixed(1) + ' ' + p2[1].toFixed(1);
131
+ }
132
+ return fechado ? d + ' Z' : d;
133
+ }
134
+
135
+ /* A mancha líquida: um círculo de raio R com lóbulos que respiram. É o
136
+ que a transição "liquido" recorta, e o que a peça "bolha" desenha. */
137
+ export function bolhaCaminho(cx, cy, R, fase = 0, semente = 2, lobos = 9) {
138
+ const pts = [];
139
+ for (let i = 0; i < lobos; i++) {
140
+ const a = (i / lobos) * Math.PI * 2;
141
+ const r = R * (1 + 0.16 * Math.sin(a * 3 + fase * 2.1 + semente) + 0.08 * Math.sin(a * 5 - fase * 3.3));
142
+ pts.push([cx + Math.cos(a) * r, cy + Math.sin(a) * r]);
143
+ }
144
+ return suave(pts, true);
145
+ }
146
+
147
+ // ── fonte e cor ──────────────────────────────────────────────────────────
148
+ export const familia = (f, reserva = 'system-ui, sans-serif') => (f ? '"' + f + '", ' + reserva : reserva);
149
+ export const MONO = (f) => familia(f || 'JetBrains Mono', 'ui-monospace, monospace');
150
+
151
+ export function rgb(hex) {
152
+ const h = String(hex || '').replace('#', '');
153
+ const n = h.length === 3 ? h.split('').map((c) => c + c).join('') : h.padEnd(6, '0').slice(0, 6);
154
+ return [0, 2, 4].map((i) => parseInt(n.slice(i, i + 2), 16) || 0);
155
+ }
156
+ export const luz = (c) => { const [r, g, b] = rgb(c); return (0.299 * r + 0.587 * g + 0.114 * b) / 255; };
157
+ export const sobre = (c) => (luz(c) > 0.6 ? '#111114' : '#ffffff');
158
+ export function mistura(a, b, k) {
159
+ const A = rgb(a), B = rgb(b);
160
+ return '#' + A.map((v, i) => Math.round(v + (B[i] - v) * k).toString(16).padStart(2, '0')).join('');
161
+ }
162
+ export const alfa = (c, a) => { const [r, g, b] = rgb(c); return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; };
163
+
164
+ // A sombra de contato: o objeto encosta no papel (curta e escura) e flutua um pouco (longa e clara).
165
+ export const SOMBRA_CONTATO = '0 1px 1px rgba(20,16,10,0.22), 0 3px 6px rgba(20,16,10,0.14), 0 18px 34px -12px rgba(20,16,10,0.32)';
166
+
167
+ // ── a marca (logo, glifo ou monograma) ──────────────────────────────────
168
+ /* O monograma de quem não tem logo: um quadrado de canto redondo vazado e
169
+ um brilho de quatro pontas dentro. Só caminho, sem texto: como imagem de
170
+ máscara (data URI) o SVG não carrega fonte, e a letra sairia em Times. */
171
+ const MONOGRAMA = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path fill-rule="evenodd" fill="#fff" d="'
172
+ + 'M30 0H70A30 30 0 0 1 100 30V70A30 30 0 0 1 70 100H30A30 30 0 0 1 0 70V30A30 30 0 0 1 30 0Z'
173
+ + 'M32 11H68A21 21 0 0 1 89 32V68A21 21 0 0 1 68 89H32A21 21 0 0 1 11 68V32A21 21 0 0 1 32 11Z'
174
+ + 'M50 27C52.5 43 57 47.5 73 50C57 52.5 52.5 57 50 73C47.5 57 43 52.5 27 50C43 47.5 47.5 43 50 27Z"/></svg>';
175
+ export const MONOGRAMA_URI = 'url("data:image/svg+xml;utf8,' + encodeURIComponent(MONOGRAMA) + '")';
176
+
177
+ /* A marca desenhada no tamanho pedido. Com logo (PNG/SVG da pasta), é a
178
+ imagem; sem logo, o monograma. "tinta" pinta a forma inteira (a logo vira
179
+ máscara) — é o que deixa a mesma marca sair cromada, bordada ou chapada
180
+ na cor da marca. */
181
+ export function Marca({ logo, tamanho = 200, cor = '#fff', tinta }) {
182
+ const s = tamanho;
183
+ if (logo && !tinta) return <Img src={staticFile(logo)} style={{ width: s, height: s, objectFit: 'contain', display: 'block' }} />;
184
+ const url = logo ? 'url(' + staticFile(logo) + ')' : MONOGRAMA_URI;
185
+ return <div style={{ width: s, height: s, background: tinta || cor, WebkitMaskImage: url, maskImage: url, WebkitMaskSize: 'contain', maskSize: 'contain',
186
+ WebkitMaskRepeat: 'no-repeat', maskRepeat: 'no-repeat', WebkitMaskPosition: 'center', maskPosition: 'center' }} />;
187
+ }
188
+
189
+ // ── o acabamento do quadro inteiro ──────────────────────────────────────
190
+ /* Os filtros SVG que o quadro usa: a textura de tinta (a letra impressa no
191
+ papel, com a borda comida) e o grão. Definidos uma vez por cena. */
192
+ export function Filtros() {
193
+ return (
194
+ <svg width="0" height="0" style={{ position: 'absolute' }} aria-hidden>
195
+ <defs>
196
+ <filter id="pc-tinta" x="-2%" y="-10%" width="104%" height="120%">
197
+ <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" seed="7" result="ruido" />
198
+ <feDisplacementMap in="SourceGraphic" in2="ruido" scale="1.6" xChannelSelector="R" yChannelSelector="G" result="borda" />
199
+ <feColorMatrix in="ruido" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 1.28" result="poros" />
200
+ <feComposite in="borda" in2="poros" operator="in" />
201
+ </filter>
202
+ </defs>
203
+ </svg>
204
+ );
205
+ }
206
+
207
+ /* Papel: a cor do papel, a fibra e as manchas de luz. Vai na camada do fundo
208
+ (profundidade 0,12) com sobra de 116%: a câmera anda e a borda nunca aparece. */
209
+ export function Papel({ el }) {
210
+ const cor = el.cor || '#efece6';
211
+ const sobra = el.sobra || 1.16;
212
+ return (
213
+ <div style={{ position: 'absolute', left: ((1 - sobra) / 2) * 100 + '%', top: ((1 - sobra) / 2) * 100 + '%', width: sobra * 100 + '%', height: sobra * 100 + '%',
214
+ background: 'radial-gradient(ellipse at 30% 25%, ' + mistura(cor, '#ffffff', 0.35) + ' 0%, ' + cor + ' 55%, ' + mistura(cor, '#8a7a60', 0.12) + ' 100%)' }}>
215
+ <svg width="100%" height="100%" style={{ position: 'absolute', inset: 0, opacity: el.fibra ?? 0.5, mixBlendMode: 'multiply' }}>
216
+ <filter id="pc-fibra">
217
+ <feTurbulence type="fractalNoise" baseFrequency="0.012 0.05" numOctaves="3" seed="11" />
218
+ <feColorMatrix type="matrix" values="0 0 0 0 0.45 0 0 0 0 0.40 0 0 0 0 0.32 0 0 0 0.35 0" />
219
+ </filter>
220
+ <rect width="100%" height="100%" filter="url(#pc-fibra)" />
221
+ </svg>
222
+ </div>
223
+ );
224
+ }
225
+
226
+ /* O grão, a vinheta, a luz passando e a grade. Era um bloco dentro do
227
+ Video.jsx; agora é peça do kit e vale para qualquer estilo. */
228
+ export function Acabamento({ ac, frame }) {
229
+ if (!ac) return null;
230
+ return (
231
+ <>
232
+ {ac.luz && (
233
+ <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', mixBlendMode: 'screen', opacity: ac.luzForca ?? 0.22,
234
+ background: 'radial-gradient(circle at ' + (50 + 40 * Math.sin(frame / 38)) + '% ' + (30 + 25 * Math.cos(frame / 51)) + '%, '
235
+ + ac.luz[0] + ' 0%, transparent 45%), radial-gradient(circle at ' + (50 - 40 * Math.sin(frame / 44)) + '% '
236
+ + (75 - 20 * Math.sin(frame / 33)) + '%, ' + (ac.luz[1] || ac.luz[0]) + ' 0%, transparent 40%)' }} />
237
+ )}
238
+ {ac.grade && (
239
+ <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: ac.grade,
240
+ backgroundImage: 'linear-gradient(rgba(255,255,255,0.5) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.5) 1px, transparent 1px)',
241
+ backgroundSize: (ac.passoGrade || 120) + 'px ' + (ac.passoGrade || 120) + 'px',
242
+ backgroundPosition: (frame * 0.2) + 'px ' + (frame * 0.1) + 'px' }} />
243
+ )}
244
+ {ac.vinheta && (
245
+ <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none',
246
+ background: 'radial-gradient(ellipse at center, transparent 55%, rgba(' + (ac.corVinheta || '0,0,0') + ',' + ac.vinheta + ') 100%)' }} />
247
+ )}
248
+ {ac.grao && (
249
+ <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: ac.grao, mixBlendMode: 'overlay' }}>
250
+ <svg width="100%" height="100%">
251
+ <filter id="grao"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" seed={frame % 60} /></filter>
252
+ <rect width="100%" height="100%" filter="url(#grao)" />
253
+ </svg>
254
+ </div>
255
+ )}
256
+ </>
257
+ );
258
+ }
259
+
260
+ /* O HUD do motion designer: cantos de enquadramento, o nome e a seção no
261
+ topo, o timecode e os quadros por segundo embaixo, e a régua do vídeo. */
262
+ export function Hud({ hud, frame, fps, total, W, H, secao, escuro }) {
263
+ const cor = hud.cor || (escuro ? 'rgba(255,255,255,0.78)' : 'rgba(17,17,20,0.72)');
264
+ const m = Math.round(Math.min(W, H) * 0.028);
265
+ const t = frame / fps;
266
+ const tc = [Math.floor(t / 3600), Math.floor(t / 60) % 60, Math.floor(t) % 60, frame % fps].map((n) => String(n).padStart(2, '0')).join(':');
267
+ const tam = Math.max(11, Math.round(Math.min(W, H) * 0.0125));
268
+ const canto = (x, y, sx, sy) => (
269
+ <div key={x + '-' + y} style={{ position: 'absolute', left: x, top: y, width: m * 0.8, height: m * 0.8,
270
+ borderLeft: sx > 0 ? '1.5px solid ' + cor : undefined, borderRight: sx < 0 ? '1.5px solid ' + cor : undefined,
271
+ borderTop: sy > 0 ? '1.5px solid ' + cor : undefined, borderBottom: sy < 0 ? '1.5px solid ' + cor : undefined,
272
+ transform: 'translate(' + (sx < 0 ? '-100%' : '0') + ',' + (sy < 0 ? '-100%' : '0') + ')' }} />
273
+ );
274
+ const rotulo = { position: 'absolute', fontFamily: MONO(hud.fonte), fontSize: tam, letterSpacing: '0.14em', color: cor, textTransform: 'uppercase', whiteSpace: 'nowrap' };
275
+ return (
276
+ <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
277
+ {canto(m, m, 1, 1)}{canto(W - m, m, -1, 1)}{canto(m, H - m, 1, -1)}{canto(W - m, H - m, -1, -1)}
278
+ <div style={{ ...rotulo, left: m * 1.6, top: m * 1.25 }}><b style={{ fontWeight: 700 }}>{hud.marca || ''}</b>{hud.rotulo ? <span style={{ opacity: 0.55 }}>{' · ' + hud.rotulo}</span> : null}</div>
279
+ {secao ? <div style={{ ...rotulo, right: m * 1.6, top: m * 1.25 }}>{secao}</div> : null}
280
+ <div style={{ ...rotulo, left: m * 1.6, bottom: m * 1.25 }}>{tc}<span style={{ opacity: 0.55 }}>{' ' + fps + ' FPS'}</span></div>
281
+ <div style={{ ...rotulo, right: m * 1.6, bottom: m * 1.25 }}>{hud.direita || ''}</div>
282
+ <div style={{ position: 'absolute', left: m * 1.6, bottom: m * 0.7, height: 1.5, width: (W - m * 3.2) * Math.min(1, frame / Math.max(1, total - 1)), background: cor, opacity: 0.6 }} />
283
+ </div>
284
+ );
285
+ }
@@ -0,0 +1,11 @@
1
+ /* kit/index.jsx — o kit de motion que o Cena.jsx monta pelo nome.
2
+ *
3
+ * Um elemento {"tipo": "kit", "peca": "<nome>", …} no plano vira a peça de
4
+ * mesmo nome daqui. A lista de nomes (e o que cada um precisa) mora do lado do
5
+ * PrimoCode, em lib/estudio/kit.js — é ela que o validador do plano confere. */
6
+ import { VOX } from './vox';
7
+ import { MARCA } from './marca';
8
+ import { Papel } from './base';
9
+
10
+ export const KIT = { ...VOX, ...MARCA, papel: Papel };
11
+ export { Filtros, Acabamento, Hud, CURVAS_KIT, bolhaCaminho } from './base';