primocode 8.18.1 → 8.19.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/bin/primocode.js CHANGED
@@ -522,8 +522,11 @@ const RE_DESCOBRIR = /\b(pesquis|busca|procur|descubr|investig|analis|leia|estud
522
522
  const RE_CRIAR_DEPOIS = /\b(cri[ae]|faz|faça|mont[ae]|ger[ae]|escrev[ae]|produz|desenvolv)\w*/i;
523
523
 
524
524
  /** "baixa/exporta o vídeo" resolvido na hora, sem gastar modelo. */
525
- const RE_BAIXAR = /\b(baixa\w*|exporta\w*|download|salva\w*)\b/i;
526
- const RE_PECA_STUDIO = /\b(v[ií]deo|mp4|mp3|apresenta[çc][ãa]o|slides?|reels?)\b/i;
525
+ // "me manda o vídeo", "quero pra mandar no zap", "gera o arquivo" — a lista
526
+ // era curta e o pedido sai de muitos jeitos. Fora dela, o pedido virava
527
+ // tarefa de agente: gasto de API para abrir uma URL que o CLI já sabe.
528
+ const RE_BAIXAR = /\b(baixa\w*|exporta\w*|download|salva\w*|grava\w*|me manda|manda(r)? (o|a|esse|essa)|gera(r)? o arquivo|em mp4|pra mandar|arquivo do)\b/i;
529
+ const RE_PECA_STUDIO = /\b(v[ií]deo|mp4|mp3|apresenta[çc][ãa]o|slides?|reels?|anima[çc][ãa]o|filme)\b/i;
527
530
 
528
531
  async function baixarCriacaoDireto(texto) {
529
532
  if (!(RE_BAIXAR.test(texto) && RE_PECA_STUDIO.test(texto))) return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "primocode",
3
- "version": "8.18.1",
3
+ "version": "8.19.0",
4
4
  "description": "PrimoCode — agente de engenharia com IA e cursor próprio. Modelos grátis. 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": {
@@ -1,7 +1,7 @@
1
1
  # CORTE — vídeo
2
2
 
3
3
  No lugar de CapCut, Premiere e After Effects. Modelo `cena`, com `duracao` em cada
4
- cena, trilha sonora, transições e gravação em `.webm`.
4
+ cena, trilha sonora, transições e download em `.mp4`.
5
5
 
6
6
  <!-- O EXEMPLO FICA NO COMEÇO DE PROPÓSITO: quando o plano de tokens é
7
7
  apertado, o agente recebe só os primeiros ~3500 caracteres desta spec. Um
@@ -269,11 +269,25 @@ play/pausa, linha do tempo clicável com marca por cena, tempo, volume e **Grava
269
269
 
270
270
  `K` toca e pausa · `M` corta o som · `→` `←` pulam cena · `F` tela cheia · `Escape` para.
271
271
 
272
- ## Virar arquivo de vídeo
272
+ ## Baixar o vídeo como arquivo
273
273
 
274
- O botão **Gravar** usa a captura de tela do navegador: escolha a aba, o vídeo toca do
275
- começo, para sozinho no fim e baixa um `.webm` com a trilha junto. Para o melhor
276
- resultado, entre em tela cheia (`F`) antes de gravar.
274
+ O botão **Baixar MP4** no player faz tudo sozinho: pede a permissão de captura, toca
275
+ do começo, para no fim e baixa. Sai `.mp4` (H.264/AAC, que toca em qualquer lugar) nos
276
+ navegadores que sabem gravar MP4; onde não sabem, sai `.webm`.
277
+
278
+ **Se o usuário pedir o arquivo do vídeo** — "baixa", "exporta", "me manda em mp4",
279
+ "quero mandar no WhatsApp" — abra o player já em modo de gravação:
280
+
281
+ ```
282
+ open_in_browser → http://localhost:7777/v/<id>#gravar
283
+ ```
284
+
285
+ A âncora `#gravar` dispara o download assim que a página abre; o usuário só confirma a
286
+ permissão de captura. **Não** use `write_file` para criar `.mp4`, e **nunca** chame
287
+ ffmpeg: o vídeo já nasce pronto aqui, e escrever um `.mp4` com write_file produz um
288
+ arquivo de texto com nome de vídeo — já aconteceu.
289
+
290
+ Para o melhor resultado, entre em tela cheia (`F`) antes de gravar.
277
291
 
278
292
  O export normal (`/api/arquivos/<id>/export`) devolve um `.html` autônomo com a
279
293
  mídia embutida — abre offline em qualquer navegador e toca igual.
@@ -337,7 +337,7 @@
337
337
  '<div class="tp-tempo" id="tp-total">00:00</div>' +
338
338
  '<button class="tp-btn pequeno" id="tp-som" title="Mudo (M)"></button>' +
339
339
  '<input class="tp-vol" id="tp-vol" type="range" min="0" max="100" value="70">' +
340
- '<button class="tp-btn texto" id="tp-gravar" title="Gravar em .webm">Gravar</button>';
340
+ '<button class="tp-btn texto primo" id="tp-gravar" title="Baixar este vídeo como arquivo">Baixar MP4</button>';
341
341
  document.body.appendChild(transporte);
342
342
 
343
343
  document.getElementById("tp-tocar").onclick = function () { tocando ? pausar() : tocar(); };
@@ -393,14 +393,47 @@
393
393
  iniciarGravacao();
394
394
  }
395
395
 
396
+ /* O melhor formato que este navegador sabe gravar.
397
+ *
398
+ * A ordem importa: H.264 com AAC é o MP4 que toca em qualquer lugar —
399
+ * WhatsApp, Instagram, PowerPoint, TV. AV1 em MP4 é MP4 de verdade mas ainda
400
+ * não toca em tudo, então vem depois. O webm é o último recurso: abre em
401
+ * navegador e pouco mais.
402
+ */
403
+ var FORMATOS = [
404
+ "video/mp4;codecs=avc1.42E01E,mp4a.40.2",
405
+ "video/mp4;codecs=avc1,mp4a.40.2",
406
+ "video/mp4;codecs=avc1",
407
+ "video/mp4",
408
+ "video/webm;codecs=vp9,opus",
409
+ "video/webm",
410
+ ];
411
+
412
+ function escolherFormato() {
413
+ for (var i = 0; i < FORMATOS.length; i++) {
414
+ if (MediaRecorder.isTypeSupported(FORMATOS[i])) return FORMATOS[i];
415
+ }
416
+ return "video/webm";
417
+ }
418
+
419
+ function gravaMp4() { return /mp4/.test(escolherFormato()); }
420
+
396
421
  function iniciarGravacao() {
397
422
  if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
398
423
  return aviso("este navegador não grava a tela", 3000);
399
424
  }
400
425
  // audio:true deixa o usuário marcar "compartilhar áudio da aba" — é assim
401
426
  // que a narração falada entra na gravação
427
+ // preferCurrentTab tira o passo de escolher a janela: o Chrome já vem
428
+ // com ESTA aba selecionada, então baixar vira um clique e um "permitir"
429
+ // em vez de um clique, uma escolha e um "permitir".
402
430
  navigator.mediaDevices.getDisplayMedia({
403
431
  video: { frameRate: 30 }, audio: true,
432
+ preferCurrentTab: true, selfBrowserSurface: "include",
433
+ }).catch(function () {
434
+ // Navegador que não conhece preferCurrentTab recusa o pedido inteiro:
435
+ // repete sem as opções novas em vez de falhar.
436
+ return navigator.mediaDevices.getDisplayMedia({ video: { frameRate: 30 }, audio: true });
404
437
  }).then(function (fluxo) {
405
438
  fluxoTela = fluxo;
406
439
  var faixas = fluxo.getVideoTracks();
@@ -410,11 +443,14 @@
410
443
  }
411
444
  var misto = new MediaStream(faixas);
412
445
  pedacos = [];
413
- // MP4 primeiro, quando o navegador sabe gravar mp4 (Safari sabe;
414
- // Chrome grava webm). O usuário pediu "baixar em mp4": onde dá, dá.
415
- var tipo = MediaRecorder.isTypeSupported("video/mp4") ? "video/mp4"
416
- : MediaRecorder.isTypeSupported("video/webm;codecs=vp9,opus")
417
- ? "video/webm;codecs=vp9,opus" : "video/webm";
446
+ // MP4 primeiro, e testando os codecs NOMEADOS antes do genérico.
447
+ //
448
+ // Testar só "video/mp4" perdia MP4 em navegador que existe: o Chrome
449
+ // responde false para o nome solto e true para
450
+ // "video/mp4;codecs=avc1.42E01E,mp4a.40.2". Quem pediu MP4 recebia
451
+ // webm à toa. A lista vai do mais compatível (H.264 + AAC, que toca em
452
+ // tudo) para o menos, e só cai no webm quando não há MP4 nenhum.
453
+ var tipo = escolherFormato();
418
454
  gravador = new MediaRecorder(misto, { mimeType: tipo, videoBitsPerSecond: 8000000 });
419
455
  gravador.ondataavailable = function (ev) { if (ev.data.size) pedacos.push(ev.data); };
420
456
  gravador.onstop = baixarGravacao;