primocode 8.42.1 → 9.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 (102) hide show
  1. package/README.md +41 -37
  2. package/bin/primocode.js +599 -327
  3. package/lib/acervo.js +8 -1
  4. package/lib/act.js +205 -114
  5. package/lib/api.js +35 -4
  6. package/lib/boas-vindas.js +31 -192
  7. package/lib/catalogo.js +60 -60
  8. package/lib/config.js +1 -1
  9. package/lib/conta.js +3 -24
  10. package/lib/diff.js +161 -0
  11. package/lib/diretor.js +37 -47
  12. package/lib/entrada.js +545 -0
  13. package/lib/especialistas.js +2 -2
  14. package/lib/estudio/assets.js +343 -0
  15. package/lib/estudio/identidade.js +560 -0
  16. package/lib/estudio/index.js +565 -0
  17. package/lib/estudio/plano.js +327 -0
  18. package/lib/estudio/projeto.js +246 -0
  19. package/lib/estudio/remotion.js +606 -0
  20. package/lib/estudio/slides.js +190 -0
  21. package/lib/estudio/video.js +296 -0
  22. package/lib/fala.js +13 -1
  23. package/lib/marca.js +4 -1
  24. package/lib/marketplace.js +189 -0
  25. package/lib/permissao.js +137 -0
  26. package/lib/registro.js +229 -0
  27. package/lib/skills.js +329 -0
  28. package/lib/tools.js +51 -181
  29. package/lib/ui.js +7 -3
  30. package/lib/voz.js +239 -0
  31. package/package.json +12 -3
  32. package/skills/acessibilidade/SKILL.md +28 -0
  33. package/skills/api-que-nao-quebra/SKILL.md +27 -0
  34. package/skills/depurar-de-verdade/SKILL.md +29 -0
  35. package/skills/editar-meu-video/SKILL.md +59 -0
  36. package/skills/escrever-em-portugues/SKILL.md +28 -0
  37. package/skills/git-limpo/SKILL.md +28 -0
  38. package/skills/motion-de-primeira/SKILL.md +62 -0
  39. package/skills/pagina-que-converte/SKILL.md +28 -0
  40. package/skills/performance-web/SKILL.md +28 -0
  41. package/skills/revisao-antes-de-entregar/SKILL.md +27 -0
  42. package/skills/slides-que-prendem/SKILL.md +40 -0
  43. package/studio/edge.py +16 -66
  44. package/studio/narrar.py +274 -0
  45. package/studio/pessoa.py +167 -0
  46. package/voz/primo_voz/__init__.py +0 -0
  47. package/voz/primo_voz/__main__.py +325 -0
  48. package/voz/primo_voz/escuta.py +235 -0
  49. package/voz/primo_voz/orbe.py +216 -0
  50. package/lib/audio-livre.js +0 -177
  51. package/lib/chaves.js +0 -149
  52. package/lib/claude-engine.js +0 -631
  53. package/lib/codex-engine.js +0 -208
  54. package/lib/remotion.js +0 -663
  55. package/lib/studio.js +0 -960
  56. package/studio/PRIMOCODE.md +0 -58
  57. package/studio/README.md +0 -154
  58. package/studio/__pycache__/edge.cpython-311.pyc +0 -0
  59. package/studio/__pycache__/elevenlabs.cpython-311.pyc +0 -0
  60. package/studio/__pycache__/voz.cpython-311.pyc +0 -0
  61. package/studio/arquivos/corte-teste-de-camera-fb823.json +0 -57
  62. package/studio/ferramentas/00-protocolo.md +0 -125
  63. package/studio/ferramentas/01-modelos.md +0 -270
  64. package/studio/ferramentas/README.md +0 -32
  65. package/studio/ferramentas/corte.md +0 -919
  66. package/studio/ferramentas/grade.md +0 -92
  67. package/studio/ferramentas/prisma.md +0 -107
  68. package/studio/ferramentas/prosa.md +0 -86
  69. package/studio/ferramentas/tela.md +0 -93
  70. package/studio/ferramentas/traco.md +0 -79
  71. package/studio/server.py +0 -2254
  72. package/studio/studio.log +0 -57
  73. package/studio/voz.py +0 -388
  74. package/studio/web/analise.html +0 -52
  75. package/studio/web/core.css +0 -1167
  76. package/studio/web/fontes/bebas.woff2 +0 -0
  77. package/studio/web/fontes/grotesk-700.woff2 +0 -0
  78. package/studio/web/fontes/inter-400.woff2 +0 -0
  79. package/studio/web/fontes/inter-700.woff2 +0 -0
  80. package/studio/web/fontes/jetbrains-400.woff2 +0 -0
  81. package/studio/web/fontes/playfair-700.woff2 +0 -0
  82. package/studio/web/fontes.css +0 -23
  83. package/studio/web/home.html +0 -217
  84. package/studio/web/js/analise.js +0 -510
  85. package/studio/web/js/audio.js +0 -607
  86. package/studio/web/js/editor.js +0 -1802
  87. package/studio/web/js/exportar.js +0 -222
  88. package/studio/web/js/ferramentas.js +0 -108
  89. package/studio/web/js/icones.js +0 -120
  90. package/studio/web/js/midia.js +0 -157
  91. package/studio/web/js/narracao.js +0 -325
  92. package/studio/web/js/player.js +0 -903
  93. package/studio/web/js/render-grade.js +0 -243
  94. package/studio/web/js/render-prosa.js +0 -104
  95. package/studio/web/js/render.js +0 -1852
  96. package/studio/web/js/sfx.js +0 -233
  97. package/studio/web/js/video.js +0 -464
  98. package/studio/web/js/voz-neural.js +0 -152
  99. package/studio/web/render.css +0 -1352
  100. package/studio/web/tf.html +0 -15
  101. package/studio/web/tool.html +0 -78
  102. package/studio/web/view.html +0 -27
@@ -1,325 +0,0 @@
1
- /* ==========================================================================
2
- narracao.js — locução.
3
-
4
- Três caminhos, do melhor para o pior:
5
- 1. VOZ GRAVADA — o usuário grava com o microfone. Sempre ganha.
6
- 2. VOZ PREMIUM — voz aprimorada do sistema, se estiver instalada.
7
- 3. VOZ PADRÃO — síntese compacta do sistema. Serve para rascunho.
8
-
9
- As vozes de brincadeira da Apple (Eddy, Grandma, Rocko…) ficam escondidas:
10
- existem no sistema, mas não servem para narrar nada.
11
- ========================================================================== */
12
-
13
- var Narracao = (function () {
14
- "use strict";
15
-
16
- var sintese = window.speechSynthesis || null;
17
- var cacheVozes = [];
18
- var cancelado = false;
19
-
20
- /* Vozes de novidade do macOS — som proposital de desenho animado. */
21
- var NOVIDADE = /^(albert|bad news|bahh|bells|boing|bubbles|cellos|deranged|good news|jester|junior|kathy|organ|pipe organ|princess|ralph|superstar|trinoids|whisper|wobble|zarvox|fred|hysterical|eddy|flo|grandma|grandpa|reed|rocko|sandy|shelley|bruce|agnes|vicki|victoria|alice|alva|damayanti|fiona|moira|tessa|veena|rishi|karen|melina|xander)\b/i;
22
-
23
- var PREMIUM = /premium|enhanced|aprimorad|neural|siri/i;
24
-
25
- /* "Joana (português (Portugal))" → "Joana" — os parênteses aninham, então
26
- corta a partir do primeiro em vez de tentar casar os pares. */
27
- function limpo(nome) {
28
- return String(nome || "").split(/\s*\(/)[0].trim() || String(nome || "");
29
- }
30
-
31
- function disponivel() { return !!sintese; }
32
-
33
- /* nota de qualidade: quanto maior, melhor a voz para narrar */
34
- function nota(v) {
35
- var n = 0;
36
- if (PREMIUM.test(v.name)) n += 100;
37
- if (/pt[-_]?BR/i.test(v.lang)) n += 40;
38
- else if (/^pt/i.test(v.lang)) n += 25;
39
- if (v.localService) n += 5;
40
- if (NOVIDADE.test(limpo(v.name))) n -= 200;
41
- return n;
42
- }
43
-
44
- function vozes(op) {
45
- op = op || {};
46
- if (!sintese) return [];
47
- var v = sintese.getVoices() || [];
48
- if (v.length) cacheVozes = v;
49
- var lista = cacheVozes.slice();
50
- if (!op.todas) {
51
- var uteis = lista.filter(function (x) { return !NOVIDADE.test(limpo(x.name)); });
52
- if (uteis.length) lista = uteis;
53
- }
54
- if (op.idioma !== false) {
55
- var pt = lista.filter(function (x) { return /^pt/i.test(x.lang); });
56
- if (pt.length && !op.todosIdiomas) lista = pt;
57
- }
58
- // o sistema às vezes repete a mesma voz; fica só uma de cada
59
- var vistas = {};
60
- lista = lista.filter(function (v) {
61
- var chave = limpo(v.name) + "|" + v.lang;
62
- if (vistas[chave]) return false;
63
- vistas[chave] = true;
64
- return true;
65
- });
66
-
67
- return lista.sort(function (a, b) { return nota(b) - nota(a) || a.name.localeCompare(b.name); });
68
- }
69
-
70
- function melhorVoz() {
71
- var v = vozes()[0];
72
- return v ? v.name : null;
73
- }
74
-
75
- /* Quantas vozes boas existem: se der zero, vale avisar o usuário. */
76
- function diagnostico() {
77
- var todas = (sintese && sintese.getVoices()) || cacheVozes;
78
- var pt = todas.filter(function (v) { return /^pt/i.test(v.lang); });
79
- return {
80
- total: todas.length,
81
- portugues: pt.length,
82
- premium: todas.filter(function (v) { return PREMIUM.test(v.name); }).length,
83
- boasEmPortugues: pt.filter(function (v) { return !NOVIDADE.test(limpo(v.name)); }).length,
84
- melhor: melhorVoz(),
85
- };
86
- }
87
-
88
- function prontas() {
89
- return new Promise(function (ok) {
90
- if (!sintese) return ok([]);
91
- if (vozes().length) return ok(vozes());
92
- var t = setTimeout(function () { ok(vozes()); }, 1200);
93
- sintese.addEventListener("voiceschanged", function so() {
94
- clearTimeout(t);
95
- sintese.removeEventListener("voiceschanged", so);
96
- ok(vozes());
97
- });
98
- });
99
- }
100
-
101
- /* ------------------------------------------------------------ prosódia -- */
102
-
103
- /* Fala corrida soa robótica. Quebrar em frases e respirar entre elas é o
104
- que mais aproxima de uma locução de verdade. */
105
- function emFrases(texto) {
106
- return String(texto || "")
107
- .split(/(?<=[.!?…:])\s+|\n+/)
108
- .map(function (f) { return f.trim(); })
109
- .filter(Boolean);
110
- }
111
-
112
- function pausaDe(frase) {
113
- if (/[?!]$/.test(frase)) return 420;
114
- if (/[.…]$/.test(frase)) return 340;
115
- if (/:$/.test(frase)) return 260;
116
- return 180;
117
- }
118
-
119
- function montar(texto, cfg) {
120
- cfg = cfg || {};
121
- var f = new SpeechSynthesisUtterance(String(texto || ""));
122
- f.lang = cfg.idioma || "pt-BR";
123
- // um pouco mais devagar que o padrão soa menos apressado
124
- f.rate = cfg.velocidade != null ? cfg.velocidade : 0.96;
125
- f.pitch = cfg.tom != null ? cfg.tom : 1;
126
- f.volume = cfg.volume != null ? cfg.volume : 1;
127
-
128
- var alvo = cfg.voz || melhorVoz();
129
- if (alvo) {
130
- var achou = (sintese.getVoices() || cacheVozes).filter(function (v) {
131
- return v.name === alvo;
132
- })[0];
133
- if (achou) { f.voice = achou; f.lang = achou.lang; }
134
- }
135
- return f;
136
- }
137
-
138
- function falarUma(texto, cfg) {
139
- return new Promise(function (ok) {
140
- var f = montar(texto, cfg);
141
- f.onend = f.onerror = function () { ok(); };
142
- sintese.speak(f);
143
- });
144
- }
145
-
146
- /* Fala o texto inteiro, frase a frase, com respiro entre elas.
147
-
148
- A VOZ NEURAL VEM PRIMEIRO. O speechSynthesis daqui para baixo é o TTS da
149
- própria máquina: existe em todo computador, e é exatamente por isso que
150
- soa a computador. Para vídeo não serve. A voz neural (voz-neural.js) tem
151
- timbre de locução e dá para escolher — inclusive uma grave, de abertura.
152
- Se ela não subir, o caminho antigo continua aqui embaixo, intacto. */
153
- function falar(texto, cfg) {
154
- cfg = cfg || {};
155
- var t = String(texto || "").trim();
156
- if (!t) return Promise.resolve(0);
157
-
158
- if (cfg.motor !== "sistema" && typeof VozNeural !== "undefined") {
159
- parar();
160
- cancelado = false;
161
- var t0n = performance.now();
162
- return VozNeural.falar(t, cfg)
163
- .then(function () { return (performance.now() - t0n) / 1000; })
164
- .catch(function (e) {
165
- if (window.console) console.warn("[narração] voz neural falhou, indo de sistema:", e && e.message);
166
- return pelaSintese(t, cfg);
167
- });
168
- }
169
- return pelaSintese(t, cfg);
170
- }
171
-
172
- /* O caminho antigo: o TTS do sistema, frase a frase. */
173
- function pelaSintese(texto, cfg) {
174
- if (!sintese || !String(texto || "").trim()) return Promise.resolve(0);
175
- parar();
176
- cancelado = false;
177
-
178
- var frases = emFrases(texto);
179
- var t0 = performance.now();
180
-
181
- return frases.reduce(function (corrente, frase) {
182
- return corrente.then(function () {
183
- if (cancelado) return;
184
- return falarUma(frase, cfg).then(function () {
185
- if (cancelado) return;
186
- return new Promise(function (r) { setTimeout(r, pausaDe(frase)); });
187
- });
188
- });
189
- }, Promise.resolve()).then(function () {
190
- return (performance.now() - t0) / 1000;
191
- });
192
- }
193
-
194
- /* Mede quanto tempo a fala leva, sem ninguém ouvir.
195
-
196
- Com a voz neural o áudio vem pronto, em arquivo, e a duração está nos
197
- metadados dele: perguntar é exato e é silencioso. Só o caminho do
198
- speechSynthesis ainda precisa falar para cronometrar — e lá o volume
199
- zero é obedecido de verdade. */
200
- function medir(texto, cfg) {
201
- cfg = cfg || {};
202
- if (cfg.motor !== "sistema" && typeof VozNeural !== "undefined") {
203
- return VozNeural.duracaoDe(texto, cfg).then(function (s) {
204
- return s || medirPelaSintese(texto, cfg);
205
- }).catch(function () { return medirPelaSintese(texto, cfg); });
206
- }
207
- return medirPelaSintese(texto, cfg);
208
- }
209
-
210
- function medirPelaSintese(texto, cfg) {
211
- var c = {};
212
- for (var k in (cfg || {})) c[k] = cfg[k];
213
- c.volume = 0;
214
- c.motor = "sistema";
215
- return pelaSintese(texto, c);
216
- }
217
-
218
- function parar() {
219
- cancelado = true;
220
- if (typeof VozNeural !== "undefined") { try { VozNeural.parar(); } catch (_) {} }
221
- if (!sintese) return;
222
- try { sintese.cancel(); } catch (_) {}
223
- }
224
-
225
- /* As vozes que o usuário pode escolher: as neurais primeiro (as boas), as
226
- do sistema depois, marcadas como o que são. */
227
- function vozesParaEscolher() {
228
- var lista = [];
229
- if (typeof VozNeural !== "undefined") {
230
- lista = VozNeural.vozes().map(function (v) {
231
- return { id: v.id, nome: v.nome, tom: v.tom, grave: !!v.grave, motor: "neural" };
232
- });
233
- }
234
- vozes().forEach(function (v) {
235
- lista.push({ id: v.name, nome: limpo(v.name), tom: "voz do seu computador", motor: "sistema" });
236
- });
237
- return lista;
238
- }
239
-
240
- /* O áudio como arquivo — é o que permite a locução ir junto no vídeo
241
- exportado. O speechSynthesis nunca deu isso: ele fala e some. */
242
- function audioDe(texto, cfg) {
243
- if (typeof VozNeural === "undefined") return Promise.reject(new Error("voz neural indisponível"));
244
- return VozNeural.audioDe(texto, cfg);
245
- }
246
-
247
- function falando() {
248
- /* A voz pode estar saindo por três lugares; o player precisa da verdade
249
- de todos, senão corta a cena no meio da fala neural. */
250
- if (typeof VozNeural !== "undefined" && VozNeural.falando && VozNeural.falando()) return true;
251
- return !!sintese && sintese.speaking;
252
- }
253
-
254
- function estimar(texto, velocidade) {
255
- var palavras = String(texto || "").trim().split(/\s+/).filter(Boolean).length;
256
- if (!palavras) return 0;
257
- var frases = emFrases(texto).length;
258
- return Math.max(1.2, palavras / (2.6 * (velocidade || 0.96)) + frases * 0.32);
259
- }
260
-
261
- /* -------------------------------------------------- gravar a própria voz */
262
-
263
- var gravador = null, fluxoMic = null, pedacos = [];
264
-
265
- function podeGravar() {
266
- return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia &&
267
- window.MediaRecorder);
268
- }
269
-
270
- /* Grava do microfone e devolve {url, nome} já salvo na biblioteca. */
271
- function gravar() {
272
- if (!podeGravar()) return Promise.reject(new Error("este navegador não grava áudio"));
273
- return navigator.mediaDevices.getUserMedia({
274
- audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true },
275
- }).then(function (fluxo) {
276
- fluxoMic = fluxo;
277
- pedacos = [];
278
- var tipo = MediaRecorder.isTypeSupported("audio/webm;codecs=opus")
279
- ? "audio/webm;codecs=opus"
280
- : (MediaRecorder.isTypeSupported("audio/mp4") ? "audio/mp4" : "");
281
- gravador = new MediaRecorder(fluxo, tipo ? { mimeType: tipo } : undefined);
282
- gravador.ondataavailable = function (e) { if (e.data.size) pedacos.push(e.data); };
283
- gravador.start();
284
- return true;
285
- });
286
- }
287
-
288
- function pararGravacao(nomeBase) {
289
- return new Promise(function (ok, falha) {
290
- if (!gravador || gravador.state !== "recording") return falha(new Error("nada gravando"));
291
- gravador.onstop = function () {
292
- if (fluxoMic) fluxoMic.getTracks().forEach(function (f) { f.stop(); });
293
- var mime = gravador.mimeType || "audio/webm";
294
- var blob = new Blob(pedacos, { type: mime });
295
- var ext = /mp4/.test(mime) ? ".m4a" : ".weba";
296
- var leitor = new FileReader();
297
- leitor.onload = function () {
298
- fetch("/api/midia", {
299
- method: "POST",
300
- headers: { "Content-Type": "application/json" },
301
- body: JSON.stringify({
302
- nome: (nomeBase || "narracao") + ext,
303
- dados: leitor.result,
304
- }),
305
- }).then(function (r) { return r.json(); })
306
- .then(function (m) { m.erro ? falha(new Error(m.erro)) : ok(m); })
307
- .catch(falha);
308
- };
309
- leitor.onerror = function () { falha(new Error("não consegui ler a gravação")); };
310
- leitor.readAsDataURL(blob);
311
- };
312
- gravador.stop();
313
- });
314
- }
315
-
316
- function gravando() { return !!gravador && gravador.state === "recording"; }
317
-
318
- return {
319
- disponivel: disponivel, vozes: vozes, prontas: prontas, melhorVoz: melhorVoz,
320
- diagnostico: diagnostico, limpo: limpo,
321
- falar: falar, medir: medir, parar: parar, falando: falando, estimar: estimar,
322
- podeGravar: podeGravar, gravar: gravar, pararGravacao: pararGravacao, gravando: gravando,
323
- vozesParaEscolher: vozesParaEscolher, audioDe: audioDe,
324
- };
325
- })();