sagadeck 1.2.0 → 1.3.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 +11 -2
- package/bin/sagadeck.js +36 -3
- package/docs/ANDAMENTO.md +37 -0
- package/docs/REFERENCIA.md +4 -3
- package/package.json +1 -1
- package/src/ai/deck-ai.js +54 -12
- package/src/ai/document-visuals.js +230 -24
- package/src/ai/quality.js +1 -1
- package/src/ai/transform.js +51 -2
- package/src/diff.js +44 -0
- package/src/elements.js +37 -2
- package/src/export/pptx.js +8 -6
- package/src/figures/charts.js +11 -3
- package/src/figures/diagrams.js +37 -9
- package/src/fiscal/autofix.js +1 -1
- package/src/fiscal/enxugar.js +22 -0
- package/src/fiscal/normalize.js +19 -2
- package/src/import/crop.js +10 -3
- package/src/layouts.js +5 -3
- package/src/library.js +74 -2
- package/src/motion.js +1 -1
- package/src/package.js +16 -1
- package/src/research/brave.js +12 -0
- package/src/research/chat-research.js +29 -0
- package/src/research/links.js +37 -0
- package/src/research/research.js +42 -2
- package/src/research/wikipedia.js +15 -0
- package/src/runtime/base.css +6 -6
- package/src/runtime/motion.js +1 -1
- package/src/runtime/runtime.js +15 -2
- package/src/studio/ai-chat-route.js +158 -0
- package/src/studio/exporting.js +2 -2
- package/src/studio/library-routes.js +158 -0
- package/src/studio/public/app.js +44 -508
- package/src/studio/public/chat.js +461 -0
- package/src/studio/public/index.html +17 -12
- package/src/studio/public/library.js +13 -2
- package/src/studio/public/slide-clipboard.js +20 -7
- package/src/studio/public/slide-select.js +73 -0
- package/src/studio/public/studio-next.css +28 -1
- package/src/studio/public/style.css +7 -1
- package/src/studio/public-files.js +1 -1
- package/src/studio/server.js +14 -285
- package/src/studio/slide-copy-routes.js +7 -4
- package/src/studio/snapshot.js +3 -0
- package/src/themes.js +32 -2
package/README.md
CHANGED
|
@@ -18,6 +18,12 @@ pip install sagadeck
|
|
|
18
18
|
```
|
|
19
19
|
Requer **Node.js 18+** e **Chrome ou Edge** instalados (o motor é JavaScript e vem empacotado no pacote Python).
|
|
20
20
|
|
|
21
|
+
Quem já vive no Node pode instalar direto, sem Python e sem bundle (vai a fonte + dependências):
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g sagadeck
|
|
24
|
+
```
|
|
25
|
+
A IA funciona apontando para um modelrelay ou gateway compatível (`SAGADECK_LLM_URL`, veja abaixo); o `pip install "sagadeck[ia]"` continua sendo o caminho com o modelrelay junto.
|
|
26
|
+
|
|
21
27
|
## Como usar
|
|
22
28
|
|
|
23
29
|
Toda apresentação mora na **biblioteca**: `SAGADECK_HOME`, ou `~/sagadeck` (no Windows, `C:Users<você>sagadeck`). Uma pasta por tópico, uma por apresentação.
|
|
@@ -36,17 +42,20 @@ sagadeck new <nome> [--topic=T] [--theme=x] cria um deck de exemplo na bibliote
|
|
|
36
42
|
sagadeck studio [deck.yaml] [--port=3517] sem arquivo: a biblioteca; com arquivo: o editor com chat IA (já aberto: usa o aberto)
|
|
37
43
|
sagadeck ensaio-api [--port=3000] exemplo de slides de API rodando contra uma API de mentira
|
|
38
44
|
sagadeck autofix <deck.yaml> [--out=pasta] auto-corrige sobreposições, margens e excesso de texto no YAML
|
|
45
|
+
sagadeck diff <a.yaml> <b.yaml> [--json] o que entrou, saiu, mudou e andou entre dois decks (por uid)
|
|
46
|
+
sagadeck enxugar <deck.yaml> [--fix] palavras por slide contra o limite do material (--fix: excedente para as notas)
|
|
39
47
|
sagadeck build <deck.yaml> gera o .html
|
|
40
48
|
sagadeck watch <deck.yaml> recompila o .html a cada vez que você salva o YAML
|
|
41
49
|
sagadeck check <deck.yaml> fiscal: estouro de texto, sobreposição, contraste, excesso de texto
|
|
42
50
|
sagadeck shots <deck.yaml> [--steps] PNG de cada slide + folhas de contato (para revisar)
|
|
43
|
-
sagadeck pptx <deck.yaml> [--native-charts] [--sem-notas] PowerPoint editável (--sem-notas: para mandar sem a sua cola)
|
|
51
|
+
sagadeck pptx <deck.yaml> [--native-charts] [--sem-notas] [--mac-fonts] PowerPoint editável (--sem-notas: para mandar sem a sua cola; --mac-fonts: fontes dos dois sistemas, para abrir igual no Mac)
|
|
44
52
|
sagadeck pdf <deck.yaml> PDF
|
|
45
53
|
sagadeck roteiro <deck.yaml> roteiro do apresentador em PDF
|
|
46
54
|
sagadeck all <deck.yaml> tudo acima
|
|
47
55
|
sagadeck mcp servidor MCP para IDEs agênticos (Cursor, Claude Code, Cline)
|
|
48
56
|
sagadeck themes vitrine com os 6 temas
|
|
49
57
|
sagadeck icons [filtro] lista os ícones (ex.: sagadeck icons car)
|
|
58
|
+
sagadeck links <deck.yaml> confere se as fontes da pesquisa (contexto/pesquisa) continuam de pe
|
|
50
59
|
sagadeck ref referência completa do YAML
|
|
51
60
|
sagadeck skill instruções para agentes de IA
|
|
52
61
|
```
|
|
@@ -300,6 +309,6 @@ tools/test-live.mjs teste automático do modo apresentação e dos widgets
|
|
|
300
309
|
|
|
301
310
|
## Limitações conhecidas
|
|
302
311
|
|
|
303
|
-
- O PPTX usa as fontes do Windows/Office dos temas.
|
|
312
|
+
- O PPTX usa as fontes do Windows/Office dos temas. Para abrir igual num Mac sem Office, exporte com `sagadeck pptx deck.yaml --mac-fonts` (só o PPTX troca as fontes por equivalentes dos dois sistemas; o HTML e o PDF continuam iguais).
|
|
304
313
|
- Widgets interativos (jogos, simuladores) viram imagem do estado final no PowerPoint e no PDF — a interação existe só no HTML.
|
|
305
314
|
- Gráficos entram como imagem nítida no PPTX; com `--native-charts`, barras/colunas/linhas/rosca viram gráficos nativos editáveis (com visual mais simples).
|
package/bin/sagadeck.js
CHANGED
|
@@ -51,7 +51,7 @@ const HELP = `sagadeck — YAML -> apresentação (HTML animado + PowerPoint edi
|
|
|
51
51
|
sagadeck build <deck.yaml> gera <deck>.html (abre no navegador; P = modo apresentador)
|
|
52
52
|
sagadeck check <deck.yaml> procura texto estourado, sobreposição, contraste, excesso de texto
|
|
53
53
|
sagadeck shots <deck.yaml> [--steps] [--only=3,5] PNG de cada slide + folhas de contato (para revisar)
|
|
54
|
-
sagadeck pptx <deck.yaml> [--native-charts] [--sem-notas] gera <deck>.pptx editável (com animações dos cliques e notas; --sem-notas tira as notas, para mandar a alguém)
|
|
54
|
+
sagadeck pptx <deck.yaml> [--native-charts] [--sem-notas] [--mac-fonts] gera <deck>.pptx editável (com animações dos cliques e notas; --sem-notas tira as notas, para mandar a alguém; --mac-fonts usa só fontes do Windows e do Mac, para abrir igual sem Office)
|
|
55
55
|
sagadeck pdf <deck.yaml> gera <deck>.pdf (um slide por página)
|
|
56
56
|
sagadeck roteiro <deck.yaml> gera <deck> - roteiro.pdf (miniaturas + notas + tempos)
|
|
57
57
|
sagadeck estudo <deck.yaml> [--html] gera <deck> - material de estudo.pdf (cada slide + texto de consulta)
|
|
@@ -68,11 +68,14 @@ const HELP = `sagadeck — YAML -> apresentação (HTML animado + PowerPoint edi
|
|
|
68
68
|
sagadeck importar <arquivo.pptx> [--topico=X] [--titulo=Y] [--sem-fotos]
|
|
69
69
|
traz um PowerPoint para a biblioteca, fiel ao original
|
|
70
70
|
sagadeck autofix <deck.yaml> [--out=pasta] auto-corrige sobreposições, margens e excesso de texto no YAML
|
|
71
|
+
sagadeck diff <a.yaml> <b.yaml> [--json] o que entrou, saiu, mudou e andou entre dois decks (por uid)
|
|
72
|
+
sagadeck enxugar <deck.yaml> [--fix] palavras por slide contra o limite do material (--fix: excedente para as notas)
|
|
71
73
|
sagadeck mcp inicia o servidor MCP para IDEs agênticos (Cursor, Claude Code, Cline)
|
|
72
74
|
sagadeck watch <deck.yaml> recompila o HTML sempre que o YAML mudar
|
|
73
75
|
sagadeck themes [--out=pasta] gera uma vitrine com todos os temas
|
|
74
76
|
sagadeck icons [filtro] lista ícones disponíveis (2.100+)
|
|
75
77
|
sagadeck search <termo> [--limit=5] pesquisa na web via DuckDuckGo (sem bloqueio) para enriquecer dados
|
|
78
|
+
sagadeck links <deck.yaml> confere se as fontes da pesquisa (contexto/pesquisa) continuam de pe
|
|
76
79
|
sagadeck ref imprime a referência completa do YAML (ótimo para dar a um LLM)
|
|
77
80
|
sagadeck skill imprime as instruções para agentes de IA
|
|
78
81
|
sagadeck --version
|
|
@@ -260,7 +263,7 @@ async function main() {
|
|
|
260
263
|
const p = paths(args[0]); const r = doBuild(p, true);
|
|
261
264
|
const { exportPptx } = await import("../src/export/pptx.js");
|
|
262
265
|
console.log("… exportando PowerPoint");
|
|
263
|
-
const { errors } = await exportPptx(p.html, p.pptx, { theme: r.theme, meta: { ...r.meta, slides: r.slidesMeta }, nativeCharts: !!flags["native-charts"], notes: !flags["sem-notas"], log: flags.verbose ? console.log : () => {} });
|
|
266
|
+
const { errors } = await exportPptx(p.html, p.pptx, { theme: r.theme, meta: { ...r.meta, slides: r.slidesMeta }, nativeCharts: !!flags["native-charts"], notes: !flags["sem-notas"], fontFallback: flags["mac-fonts"] ? "mac" : undefined, log: flags.verbose ? console.log : () => {} });
|
|
264
267
|
errors.forEach((e) => console.log(" ✗ " + e));
|
|
265
268
|
console.log(`✓ PPTX: ${p.pptx}`);
|
|
266
269
|
break;
|
|
@@ -294,7 +297,7 @@ async function main() {
|
|
|
294
297
|
await doCheck(p);
|
|
295
298
|
const files = await doShots(p, flags.revisao ? p.shots : p.tmpShots);
|
|
296
299
|
const { exportPptx } = await import("../src/export/pptx.js");
|
|
297
|
-
await exportPptx(p.html, p.pptx, { theme: r.theme, meta: { ...r.meta, slides: r.slidesMeta }, nativeCharts: !!flags["native-charts"], notes: !flags["sem-notas"] });
|
|
300
|
+
await exportPptx(p.html, p.pptx, { theme: r.theme, meta: { ...r.meta, slides: r.slidesMeta }, nativeCharts: !!flags["native-charts"], notes: !flags["sem-notas"], fontFallback: flags["mac-fonts"] ? "mac" : undefined });
|
|
298
301
|
console.log(`✓ PPTX: ${p.pptx}`);
|
|
299
302
|
const { pdf } = await import("../src/export/shots.js");
|
|
300
303
|
await pdf(p.html, p.pdf); console.log(`✓ PDF: ${p.pdf}`);
|
|
@@ -454,6 +457,28 @@ async function main() {
|
|
|
454
457
|
}
|
|
455
458
|
break;
|
|
456
459
|
}
|
|
460
|
+
case "diff": {
|
|
461
|
+
const [fa, fb] = args;
|
|
462
|
+
if (!fa || !fb) { console.error("Uso: sagadeck diff <a.yaml> <b.yaml> [--json]"); process.exit(1); }
|
|
463
|
+
const { diffDecks, formatDiff } = await import("../src/diff.js");
|
|
464
|
+
const d = diffDecks(loadSpec(path.resolve(fa)), loadSpec(path.resolve(fb)));
|
|
465
|
+
if (flags.json) console.log(JSON.stringify(d, null, 2));
|
|
466
|
+
else console.log(formatDiff(d, { aName: fa, bName: fb }));
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
case "enxugar": {
|
|
470
|
+
const p = paths(args[0]);
|
|
471
|
+
const spec = loadSpec(p.abs);
|
|
472
|
+
const { enxugarReport, formatEnxugar } = await import("../src/fiscal/enxugar.js");
|
|
473
|
+
console.log(formatEnxugar(enxugarReport(spec)));
|
|
474
|
+
if (flags.fix) {
|
|
475
|
+
const { autofixDeck } = await import("../src/fiscal/autofix.js");
|
|
476
|
+
const res = autofixDeck(spec);
|
|
477
|
+
(await import("../src/deck-file.js")).writeDeckFile(p.abs, res.spec);
|
|
478
|
+
console.log(`enxugado: ${res.modifiedSlidesCount} slide(s) corrigido(s), excedente nas notas.`);
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
457
482
|
case "mcp": {
|
|
458
483
|
const { runMCPServer } = await import("../src/mcp/server.js");
|
|
459
484
|
runMCPServer();
|
|
@@ -501,6 +526,14 @@ async function main() {
|
|
|
501
526
|
}
|
|
502
527
|
break;
|
|
503
528
|
}
|
|
529
|
+
case "links": {
|
|
530
|
+
const p = paths(args[0]);
|
|
531
|
+
const { linksOfDeck, checkLinks, formatLinks } = await import("../src/research/links.js");
|
|
532
|
+
const urls = linksOfDeck(path.dirname(p.abs));
|
|
533
|
+
if (!urls.length) { console.log("sem pesquisa salva ao lado do deck (contexto/pesquisa/fontes.json)."); break; }
|
|
534
|
+
console.log(formatLinks(await checkLinks(urls)));
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
504
537
|
case "ref": case "referencia": process.stdout.write(fs.readFileSync(path.join(DOCS, "REFERENCIA.md"), "utf8")); break;
|
|
505
538
|
case "skill": process.stdout.write(fs.readFileSync(SKILL, "utf8")); break;
|
|
506
539
|
case "--version": case "-v": case "version": console.log(`sagadeck ${VERSION}`); break;
|
package/docs/ANDAMENTO.md
CHANGED
|
@@ -1253,3 +1253,40 @@ Atualização 04/10: screenshots revelaram hierarquia invertida e evidências tr
|
|
|
1253
1253
|
### Autonomia total (Rose): o agente vigia, a pessoa não
|
|
1254
1254
|
- Carimbo do motor na barra de status (versão + commit + início) via /api/instance; aprovação de submit mostra preço do catálogo; direções aprovadas salvas e injetadas no prompt; vigia baixa clipe pronto sozinho e avisa com toast; versões fotografadas a cada save com modal lado a lado e restaurar com rede.
|
|
1255
1255
|
- Caça própria: YAML inexistente no cliente quebrava o init em silêncio (botões mortos); show do servidor agora manda slides prontos. Módulos extraídos para respeitar a trava de 100KB do server.js.
|
|
1256
|
+
|
|
1257
|
+
### PPTX legível no Mac sem Office (Rose) — 05/10/2026
|
|
1258
|
+
- Pedido do Narumi: melhorar export PPTX/PDF, começando pela fidelidade no Mac (limitação conhecida do README: PPTX usava fontes Windows/Office e o PowerPoint trocava a fonte).
|
|
1259
|
+
- Novo `sagadeck pptx deck.yaml --mac-fonts` (vale no `all`): o PPTX troca as 18 faces só-Windows por equivalentes dos dois sistemas (Bahnschrift→Arial Narrow, Segoe UI→Arial, Cascadia Mono→Courier New, Century Gothic→Verdana, Franklin Gothic Heavy→Arial Black, resto Franklin→Arial, mão→Comic Sans MS, pixel→Courier New); HTML e PDF não mudam. No Studio, `/api/export/pptx?macFonts=1`.
|
|
1260
|
+
- Código: `PPTX_MAC_SAFE` + `macFallbackFace` em `src/themes.js`, `fontFallback` em `exportPptx` (`src/export/pptx.js`, cobre texto, tabela e o `VARIABLE` do Bahnschrift), `macFonts` em `sendExport` + rota do Studio. Docs em `README.md` e `docs/REFERENCIA.md`.
|
|
1261
|
+
- Testes: `test/pptx-mac-fonts.test.js` (mapa só-safe em todos os temas sem navegador + export real com/sem flag). Validação: suíte foca verde (export, guards, engine, 74 testes); suíte completa com 780 testes tem as mesmas 5 falhas do baseline sem a mudança (bundle/pip sem esbuild, diagram, retorno-recriada, ribbon-fit, share-link — pré-existentes, ambiente).
|
|
1262
|
+
- Fronteira do teste: verificado XML do PPTX (sem Bahnschrift/Segoe, com Arial Narrow; tabela segue nativa) e CLI de ponta a ponta; NÃO verificado num Mac de verdade (sem acesso a um) — quem tiver Mac confirma visualmente.
|
|
1263
|
+
|
|
1264
|
+
### Suíte verde no Oracle: 5 falhas viram 0 (Rose) — 05/10/2026
|
|
1265
|
+
- Pedido do Narumi: zerar as 5 falhas pré-existentes da suíte. Diagnóstico com baseline (clone sem a mudança, mesmos 5 fails) + CI (vermelho na main há vários merges) para separar bug de ambiente.
|
|
1266
|
+
- 4 eram ambiente do Oracle: `bundle` (node_modules sem devDeps → `npm install`), `diagram` e `share` (só fontes DejaVu → instaladas Liberation, métricas compatíveis com Arial) e `retorno-recriada` (Chrome 153 hifeniza pt-BR, CI não: teste aceita encolher OU hifenizar, `test/retorno-recriada.test.js`, mantendo irmãos iguais como invariante).
|
|
1267
|
+
- 1 era bug de verdade, em todo lugar (CI ubuntu+windows também): a faixa Design estourava em máquina nova. Causas: (a) `flex-shrink` esmagava a nota de identidade a 0px em silêncio (nem o aviso aparecia); travado com `flex-shrink: 0` no grupo Identidade, e o aviso voltou a aparecer; (b) com o aviso à vista, o grupo não cabia: controles compactos só ali (`#identity-select`, `#direction-select`, botões, px fixos para não depender de fonte); (c) em 900px e notebook (1366), blocos compactos em `studio-next.css` (`@media` 1440/1024) para 3 temas + 3 paletas à vista sem rolagem.
|
|
1268
|
+
- Validação: ribbon, retorno, diagram, engine, guards, pptx-mac (85), share+export (14), studio (87 passed, 1 skip), bundle (5, roda com sudo aqui porque a árvore é de root). `src/runtime/code-lab.js` regenerado pelo teste foi revertido (ruído de versão do esbuild, fora do escopo).
|
|
1269
|
+
- Fronteira: verde NESTE Oracle (DejaVu+Liberation+Chromium 153). CI tem relatório próprio (motion-scenes, motion-trace, copy/paste, transform — outros responsáveis); o Design do CI deve melhorar com este merge, conferir no próximo run.
|
|
1270
|
+
|
|
1271
|
+
### Multisseleção de slides + copiar/colar no botão direito (Rose) — 06/10/2026
|
|
1272
|
+
- Pedido do Narumi: botão direito no slide copia/cola (estava só na faixa, e o Ctrl+C/V da lista nem funcionava: branch depois do return), Ctrl+click / Shift+click / Ctrl+setas selecionam vários.
|
|
1273
|
+
- `selectedSlides` em módulo próprio (`src/studio/public/slide-select.js`, pela trava de tamanho do app.js: 273.379 < 274.000): click alterna, Shift faz intervalo, Ctrl+setas estendem, Ctrl+A tudo; Delete exclui o bloco com Desfazer; mover acompanha a seleção; duplicar/novo/colado focam o resultado. Miniaturas marcadas com tracejado (`.selected`).
|
|
1274
|
+
- Copiar/colar vale para N slides (cliente `slide-clipboard.js` + `slide-copy-routes.js` aceitam lista, com compat para o formato de 1; colados ganham uids novos). Menu do botão direito com Copiar/Colar/Excluir com contagem; Mover/Duplicar só no singular.
|
|
1275
|
+
- Achado no caminho: `PUBLIC_SCRIPTS` (`src/studio/public-files.js`) barrou o módulo novo com 404 — registrado.
|
|
1276
|
+
- Testes: `test/slide-select.test.js` (menu copiar/colar, Ctrl+click+Delete+Desfazer, Ctrl+seta). Validação: slide-select (3), guards/engine/delete/undo (74), studio (87+1skip), share+export (14); suíte completa com 820 testes: 796 passed, 3 falhas pré-existentes/ambiente (bundle precisa escrita em árvore root; motion e transform passam isolados, instáveis sob carga — o transform já caía no CI).
|
|
1277
|
+
|
|
1278
|
+
### Nome amigável na biblioteca atrás do portal (Rose) — 06/10/2026
|
|
1279
|
+
- Narumi viu o id cru (`305cc...`, avatar "3") no topo da biblioteca: `api/library` devolve o id e a página mostrava direto.
|
|
1280
|
+
- A página agora lê o username do `/whoami` do portal (mesma origem, convenção `username || email`, avatar com a inicial maiúscula; sem portal, segue como antes). Sem tocar no filmes-api nem no nginx.
|
|
1281
|
+
- Detalhe: o fetch só acontece com usuário (no Studio local o 404 sujava o console e quebrava os testes de zero-erro); o proxy fake do teste de prefixo ganhou `/whoami` com `logged_in:false` (portal sem nome → mostra o id, sem pedido quebrado).
|
|
1282
|
+
- Teste: `biblioteca mostra o username do portal em vez do id cru` (whoami mockado); `library-ui` 27/27.
|
|
1283
|
+
- Fronteira: verificado com whoami mockado; a confirmação visual no portal de verdade (nome "narumi" no topo) fica com o Narumi.
|
|
1284
|
+
|
|
1285
|
+
### Publicação no npm (Rose) — 06/10/2026
|
|
1286
|
+
- Pedido do Narumi: o app é Node, merece casa no npm (e aposenta a gambiarra do zip/fonte).
|
|
1287
|
+
- Pacote pronto: nome `sagadeck` livre, `bin` executável, `files` completo (fonte + deps, 11 MB, sem bundle). Instalação via tarball testada de ponta a ponta (`npm i <tgz>` + `sagadeck build` gera o HTML).
|
|
1288
|
+
- `publish.yml` ganha job `publish-npm` (trusted publishing OIDC + provenance, mesma release). Falta o passo manual único: publicar a 1.2.0 à mão (`npm login` + `npm publish`) e ligar o Trusted Publisher em npmjs.com.
|
|
1289
|
+
- PyPI continua (público Python + banco); docs no README.
|
|
1290
|
+
|
|
1291
|
+
### Legenda e marca-texto — reconciliação Windows + Oracle (Rose)
|
|
1292
|
+
- Branch dela (`fix/legenda-e-marca`) revisada por diff: mesma causa-raiz na legenda, mas o condicional dela (só maiúsculas) submedia nomes mistos — o CSS prova `text-transform: uppercase` + `letter-spacing` em todo `f-label`, então vale caps sempre. Mantida a estrutura dela (right-aligned + `ch-leg`) com medida sempre-caps; mark 84%/58% dela; teste de navegador dela (getBBox + pixel) + meu teste de cordas. Verdes: chart-legend-mark 2/2, engine 63/63, blocks 1/1.
|
package/docs/REFERENCIA.md
CHANGED
|
@@ -512,7 +512,7 @@ Todo elemento aceita: `step` (clique em que aparece), `exit` (clique em que some
|
|
|
512
512
|
- `console` (`screen: "TEXTO"`, `alarm: true`) · `desk` (`papers: true`) · `pair` · `judge` · `elevator` (`button: false`)
|
|
513
513
|
- `car-top` — carro visto de cima: `driver` / `passenger`: `human | human-watch | human-phone | machine`, `back: sleep | human`, `button: true`
|
|
514
514
|
|
|
515
|
-
**Diagramas**: `{ diagram: loop, nodes: [a, b, c, d], actor: in|on|out, at: 2 }` · `{ diagram: spectrum, stops: [...], at: 1, left, right }` · `{ diagram: flow, steps: [...], highlight: 1 }` · `{ diagram: venn, a, b, both }`
|
|
515
|
+
**Diagramas**: `{ diagram: loop, nodes: [a, b, c, d], actor: in|on|out, at: 2 }` · `{ diagram: spectrum, stops: [...], at: 1, left, right }` · `{ diagram: flow, steps: [...], highlight: 1, cols: 5 }` (mais de 5 passos desce em serpentina, ida e volta, sem encolher a letra) · `{ diagram: venn, a, b, both }`
|
|
516
516
|
|
|
517
517
|
**Gráficos** (animados no HTML; com `--native-charts` viram gráficos nativos editáveis no PowerPoint):
|
|
518
518
|
```yaml
|
|
@@ -529,6 +529,7 @@ Todo elemento aceita: `step` (clique em que aparece), `exit` (clique em que some
|
|
|
529
529
|
`null` numa série quebra a linha (ex.: sessões diferentes). Cores aceitam papéis do tema (`fg`, `hi`, `em`, `muted`, `line`) ou hex.
|
|
530
530
|
`xLabel` / `yLabel` dão nome aos eixos (linhas e colunas). `from: { file, sheet, columns: [rótulo, série…] }` num slide de gráfico diz de que planilha do projeto os dados vieram (o Studio põe ao inserir uma sugestão; "Atualizar da planilha" relê o arquivo). Não invente `from`.
|
|
531
531
|
`csv:` (qualquer tipo com dados) troca `data`/`labels`/`series` pelo conteúdo do arquivo (`;`, `,` ou tab; vírgula decimal; cabeçalho vira o nome das séries): atualizou o arquivo, o slide atualiza. Arquivo sumido: ficam os dados do slide e o fiscal avisa. No Studio, os dados ficam numa **planilha** no Formatar: cola do Excel (Ctrl+V em qualquer célula preenche a partir dela), importa CSV e ganha colunas de série.
|
|
532
|
+
Tabela colada no chat pedindo gráfico: a IA sugere com prévia (`variants` com 2 a 3 tipos — barras, linhas, pizza — nos mesmos dados) em vez de montar um `chart` direto; só faz direto com o tipo já dito.
|
|
532
533
|
|
|
533
534
|
**Mapa do Brasil por UF** (em grade: cada estado é um quadrado na posição aproximada; a cor mais forte é o maior
|
|
534
535
|
valor): `{ ufmap: { SP: 320, RJ: 140, MG: 150 }, suffix: " mil", prefix: "R$ ", highlight: [SP], legend: "cadastros", showValues: true }`.
|
|
@@ -585,7 +586,7 @@ theme:
|
|
|
585
586
|
faces:
|
|
586
587
|
display: { css: "font-family: 'Rockwell Extra Bold', serif; line-height: .95;", pptx: { face: "Rockwell Extra Bold" } }
|
|
587
588
|
```
|
|
588
|
-
Cada face tem `css` (navegador) e `pptx` (nome exato da fonte no PowerPoint). Use fontes instaladas no Windows/Office para o PPTX sair idêntico.
|
|
589
|
+
Cada face tem `css` (navegador) e `pptx` (nome exato da fonte no PowerPoint). Use fontes instaladas no Windows/Office para o PPTX sair idêntico. Para mandar a alguém que abre num Mac sem Office: `sagadeck pptx deck.yaml --mac-fonts` (no Studio, `/api/export/pptx?macFonts=1`): o PPTX troca as fontes só-Windows (Bahnschrift, Segoe UI, Cascadia Mono, Century Gothic, Franklin Gothic…) por equivalentes dos dois sistemas (Arial Narrow, Arial, Courier New, Verdana…); o HTML e o PDF não mudam.
|
|
589
590
|
|
|
590
591
|
## Widgets próprios
|
|
591
592
|
|
|
@@ -1095,7 +1096,7 @@ Use os elementos nativos para textos, logos/imagens, figuras e motion. Não entr
|
|
|
1095
1096
|
|
|
1096
1097
|
### Trabalho no Studio
|
|
1097
1098
|
|
|
1098
|
-
Cada aba do editor mantém sua apresentação independente. Enquanto o chat processa, a entrada permanece disponível e o botão de enviar vira Parar; cancelar impede aplicar resposta pendente. Slides podem ser copiados para outra apresentação com Copiar/Colar na faixa Início
|
|
1099
|
+
Cada aba do editor mantém sua apresentação independente. Enquanto o chat processa, a entrada permanece disponível e o botão de enviar vira Parar; cancelar impede aplicar resposta pendente. Slides podem ser copiados para outra apresentação com Copiar/Colar na faixa Início, no botão direito da miniatura ou Ctrl+C/Ctrl+V com foco na lista (vale para vários de uma vez); arquivos locais referenciados acompanham a cópia. Na lista, Ctrl+click (ou Shift+click, Ctrl+setas) seleciona vários; Delete exclui o bloco com Desfazer. O slide passa a usar o tema da apresentação de destino.
|
|
1099
1100
|
|
|
1100
1101
|
## Documentos e direção contextual
|
|
1101
1102
|
|
package/package.json
CHANGED
package/src/ai/deck-ai.js
CHANGED
|
@@ -47,6 +47,7 @@ Siga ESTRITAMENTE a referência abaixo: use só layouts, elementos, campos e fig
|
|
|
47
47
|
- Experiências usam entradas compartilhadas e fórmulas reais (calc), cenários, previsão, comparação e explicação. Dados fictícios levam illustrative: true. Consulte os estados extremos; não esconda erro matemático em gráfico bonito.
|
|
48
48
|
- Ao reformular composição ou criar uma experiência pelo chat, inclua review: true para a conferência visual. Preserve exemplos, ordem e conteúdo; uma crítica de desenho não autoriza apagar informação.
|
|
49
49
|
- Adapte tom, estilo, tema, densidade e recursos ao conteúdo, público e objetivo. Estas orientações são guias contextuais: o pedido explícito prevalece. Antes de entregar, confira fidelidade, legibilidade, variedade e pertinência; corrija problemas observados sem esperar que a pessoa peça. Papers fiéis preservam seus elementos visuais; resumos executivos podem selecionar evidências. Não transforme toda metodologia em desenho se o original já comunica bem.
|
|
50
|
+
- Paper científico anexado (congresso, defesa, aula): metodologia merece slides de verdade, respeitando a natureza do paper — SE tiver método quantitativo, detalhe modelos com parâmetros e calibração, métricas com valores E fontes (tabela comparativa quando houver); se for teórico, revisão ou qualitativo, apresente a estrutura do argumento sem inventar números. Números sempre do próprio texto com a origem ("n=1.403", "NSE 0,90–0,96"). Prefira as figuras extraídas do documento às páginas inteiras como imagem; página inteira só em último caso. Todo o conteúdo (títulos, legendas que você escrever, notes) num ÚNICO idioma: o do paper, salvo pedido explícito em contrário.
|
|
50
51
|
- Capacidades reais: quando pesquisa/comandos estiverem disponíveis, use-os antes de alegar falta de acesso à web. Geradores de imagem podem produzir texto legível: não declare impossibilidade geral; avalie o resultado e corrija grafia. Prefira texto editável quando útil, mas uma imagem inteira com texto é válida se solicitada.
|
|
51
52
|
- Direção de arte: se pedirem propostas visuais, use variants com três composições do MESMO conteúdo real; varie hierarquia, enquadramento e tipografia, não só cor. Cada opção pode levar direction: {theme, rationale}. Escolher aplica o tema ao deck, mantendo outros slides intactos.
|
|
52
53
|
- Para integrar texto ao cenário, componha camadas editáveis em canvas (imagem, texto, primeiro plano). continuity no mesmo objeto em slides consecutivos mantém continuidade espacial. Prefira uma imagem estática forte quando movimento não explicar nada.
|
|
@@ -57,12 +58,14 @@ Siga ESTRITAMENTE a referência abaixo: use só layouts, elementos, campos e fig
|
|
|
57
58
|
- Nunca invente fatos: nada de número, estatística, pesquisa, data, nome ou citação que não esteja no pedido ou no material. Se um número ajudaria, use um exemplo claramente hipotético ("por exemplo, num time de 5 pessoas…") ou fique sem número. Não invente \`author\` nem \`date\` (nem "Seu Nome"): omita se o pedido não disser.
|
|
58
59
|
- Prefira figuras geradas (icon, picto, diagram, chart) a listas de bullets. Ícones são do Lucide, nomes em inglês kebab-case (ex.: rocket, shield-check, trending-up).
|
|
59
60
|
- Ensinar como algo FUNCIONA (algoritmo, método, conta, receita técnica): o mecanismo inteiro, passo a passo, e não só "o que é". Um exemplo concreto com números atravessa a aula (as mesmas entradas do começo ao fim), cada passo mostra a conta feita na tela (\`solution\`, \`steps\`, \`table\`, \`algo\`), uma simulação deixa mexer (\`calc\` ou \`science\` com controles: muda a entrada, vê o resultado) e, se ele aprende ou se ajusta, mostre uma rodada desse ajuste com os números. Público leigo ou criança: simplifique as PALAVRAS (analogias do dia a dia, "pontos por pista" em vez de "pesos"), nunca os PASSOS; uma ideia por slide, frases curtas, um exercício para fazer junto e ilustrações nos momentos-chave.
|
|
60
|
-
-
|
|
61
|
+
- ANTI-FEIJÃO-COM-ARROZ: varie os layouts de verdade; capa (cover) no início e encerramento (end) no fim quando fizer sentido. Regras duras: nunca 2 slides seguidos com o mesmo layout (salvo série intencional); em 6+ slides use 5+ layouts diferentes com 1 slide de impacto (headline, number, statement, full, image, question); no máximo 45% do deck em listas/cartões (cards, list, steps, stats, bento, agenda). Conteúdo manda no layout, não o contrário (ver o mapa em "Layouts válidos").
|
|
61
62
|
- Slide denso (documentação, referência, números): feche com 1 takeaway em ==destaque== e use o elemento \`aviso\` (tipos: \`importante\`, \`atencao\`, \`dica\`, \`perigo\`) para o que não pode passar batido; grife ==palavras-chave== no texto corrido em vez de encher de negrito.
|
|
62
63
|
- Escreva no idioma do pedido do usuário.
|
|
63
|
-
- Layouts válidos (\`layout:\`): ${Object.keys(LAYOUTS).join(", ")}.
|
|
64
|
+
- Layouts válidos (\`layout:\`): ${Object.keys(LAYOUTS).join(", ")}. Conteúdo → layout (use o que comunica, não o mais fácil): processo/fluxo/arquitetura/UML: layout \`diagram\` (Mermaid); itens em volta de uma ideia: layout \`infographic\`; gráfico: \`chart\` (fórmula/curva/3D: \`science\`); datas: \`calendar\` (grade) ou \`timeline\` (marcos); página densa estilo revista: \`poster\`; jornada+problema+solução+painel: \`onepage\`; status semanal: \`status\`; definição: \`definition\`; citação: \`quote\`; número gigante: \`number\`; KPIs: \`stats\`; A×B: \`compare\`; 2×2: \`matrix\`; pergunta à plateia: \`question\`/\`poll\`; código: \`code\`, guiado: \`codewalk\`; screenshot com foco: \`spotlight\`; API ao vivo: \`api\`; conta/calculadora: \`solution\`/\`calc\`; algoritmo animado: \`algo\`; redes/rotas: \`graphlab\`; código executável pelo aluno: \`codelab\`; experimento: \`playground\`; decisão: \`decisionlab\`; duelo/terminais/turnos: \`duel\`/\`terminals\`/\`turns\`; carrossel: \`carousel\`; funil/pirâmide/agenda: \`funnel\`/\`pyramid\`/\`agenda\`; grade/consulta: \`bento\`/\`mosaic\`/\`ribbon\`/\`dossier\`; tipografia em cena: \`kinetic\`; texto no cenário: \`scenography\`; caminhos com volta ao mapa: \`hub\`+\`goto\`/\`back\`/\`next\`; manchete: \`headline\`; imagem total: \`full\`/\`image\`. Diagramas simples (\`diagram: loop/flow/venn…\`) e gráficos também existem como ELEMENTOS, dentro de figure/content/side.
|
|
64
65
|
- Temas disponíveis: ${themes}.
|
|
65
66
|
- YAML: coloque entre aspas duplas todo texto que comece com marcação (\`**\`, \`*\`, \`==\`, \`^^\`, \`~~\`, \`[\`) ou que contenha ": ".
|
|
67
|
+
- Em sequências inline ([{...}]): coloque entre aspas todo valor de texto com vírgula ("3,5 peixes por m³"). Sem aspas a vírgula parte o objeto e o dado sai truncado.
|
|
68
|
+
- Números com milhar em \`stats\`/\`number\`: escreva sem separador ("7400") ou por extenso — nunca "7,400" solto (vira dado quebrado no slide).
|
|
66
69
|
${images
|
|
67
70
|
? `- Você PODE pedir ilustrações geradas por IA com \`image_prompt: "descrição visual detalhada, em inglês"\` no lugar de \`image\` — por exemplo \`figure: { image_prompt: "...", fit: cover }\` num split/cover, ou um slide \`layout: image\` ou \`full\` com \`image_prompt\`. No máximo ${maxImages} imagens novas por resposta.
|
|
68
71
|
- Gerar imagem custa dinheiro e leva segundos. Leia o que a pessoa pediu:
|
|
@@ -71,11 +74,18 @@ ${images
|
|
|
71
74
|
- não falou de imagem → VOCÊ decide, do mesmo jeito: ilustre o que fica mais claro com uma imagem (um objeto, um lugar, uma cena, um processo físico, a capa) e deixe o resto com ícones, gráficos e diagramas do sagadeck;
|
|
72
75
|
- pediu sem imagens (ou só ícones) → não gere.`
|
|
73
76
|
: "- NÃO use `image_prompt` nem imagens externas; use as figuras geradas do sagadeck."}
|
|
77
|
+
- PENSAMENTO CRÍTICO NA ILUSTRAÇÃO: cada imagem gerada tem que retratar o CONTEÚDO ESPECÍFICO do pedido e do material
|
|
78
|
+
(o lugar, o objeto, o processo nomeado — ex.: um paper sobre a bacia do Aricanduva pede o Aricanduva, rio urbano da
|
|
79
|
+
zona leste de São Paulo, nunca "uma cidade genérica"). Genérico bonito é ERRO, pior que ausência: se não der para
|
|
80
|
+
retratar o específico com fidelidade, prefira a figura do material, um diagrama, um mapa ou nada. A capa ilustrada
|
|
81
|
+
ancora no caso concreto do briefing, não num símbolo vago do tema. O alt descreve o que a imagem mostra de verdade.
|
|
82
|
+
- LEGENDA SEM NÚMERO: a legenda de figura, diagrama ou esquema é só a descrição curta do que se vê (sem "Figure N:", "Figura N:", "Table N:"). Nada de numerar visual — nem os do paper, nem os criados por você.
|
|
83
|
+
- statement é frase curta de impacto (até ~12 palavras); texto mais longo vai em split, quote ou lead — nunca jogue parágrafo em statement gigante.
|
|
74
84
|
- Nunca invente campos começando com "_" e não use caminhos de imagem que não existam no deck.
|
|
75
85
|
- Referências visuais REAIS são diferentes de ilustrações: para aplicações, tutoriais, personagens e produtos, use fotos/gráficos observados nas fontes com web_image: {url: URL_EXATA, source: PAGINA, alt: DESCRICAO}, ou uma captura pública com web_capture: {url: PAGINA, selector: SELETOR_OPCIONAL}. Não adivinhe URLs ou coordenadas de cliques. O motor importa a referência para a biblioteca e a apresentação funciona offline. Falhas aparecem; não substitua screenshot por desenho inventado. Não peça captura de páginas privadas ou autenticadas.
|
|
76
86
|
- Para ensinar relações e redes (logística, dependências, pessoas, conhecimento), considere graphlab com nós semânticos e um experimento de rota/bloqueio. Para algoritmos, considere codelab com um desafio que o aluno possa alterar e executar. A escolha e o exemplo vêm do conteúdo, não do nome do assunto. HTTPX/NetworkX reais ficam em api/codewalk ou comandos Python; codelab interpreta Python simples, sem essas bibliotecas.
|
|
77
87
|
|
|
78
|
-
DIREÇÃO E VARIEDADE: escolha a composição pela função narrativa de cada slide, não pelo layout mais fácil. Antes de gerar, planeje a cadência: uma cena concreta, exploração/demonstração, explicação, evidência e convite conforme o conteúdo. Não transforme tudo em cards, bento ou mosaicos. Uma jornada pede sequência/ribbon/poster; relações pedem diagrama/graphlab; screenshot pede spotlight; experimento pede calc/playground; uma tese pede section/statement; conteúdo técnico pede codewalk/codelab/api. Varie escala e composição sem inventar antagonismo, dados, quizzes ou interatividade decorativa. Preserve exemplos, ordem e cadência em revisões pontuais. Ao revisar, procure repetição visual mesmo quando nomes de layouts diferem. Respeite fundo claro, paleta e restrições pedidos; não force escuro/neon só porque a pessoa disse futurista. Uma animação 2D ou crossfade não equivale a vídeo 3D ou morph volumétrico. Nunca anuncie vídeo gerado quando só existe prompt, poster ou script.
|
|
88
|
+
DIREÇÃO E VARIEDADE: escolha a composição pela função narrativa de cada slide, não pelo layout mais fácil. Antes de gerar, planeje a cadência: uma cena concreta, exploração/demonstração, explicação, evidência e convite conforme o conteúdo. Não transforme tudo em cards, bento ou mosaicos. Se o resultado sair com cara de feijão com arroz (só cards/list/statement), refaça a cadência com os layouts do mapa antes de entregar. Uma jornada pede sequência/ribbon/poster; relações pedem diagrama/graphlab; screenshot pede spotlight; experimento pede calc/playground; uma tese pede section/statement; conteúdo técnico pede codewalk/codelab/api. Varie escala e composição sem inventar antagonismo, dados, quizzes ou interatividade decorativa. Preserve exemplos, ordem e cadência em revisões pontuais. Ao revisar, procure repetição visual mesmo quando nomes de layouts diferem. Respeite fundo claro, paleta e restrições pedidos; não force escuro/neon só porque a pessoa disse futurista. Uma animação 2D ou crossfade não equivale a vídeo 3D ou morph volumétrico. Nunca anuncie vídeo gerado quando só existe prompt, poster ou script.
|
|
79
89
|
|
|
80
90
|
=== REFERÊNCIA DO YAML ===
|
|
81
91
|
${reference()}`;
|
|
@@ -85,22 +95,31 @@ ${reference()}`;
|
|
|
85
95
|
// Parsing e validação da resposta
|
|
86
96
|
// ---------------------------------------------------------------------------------------------
|
|
87
97
|
|
|
88
|
-
// Para que serve o material: o modelo
|
|
98
|
+
// Para que serve o material: o modelo EXTRAI do pedido primeiro e só pergunta o que faltar de verdade.
|
|
99
|
+
// Regra: nunca pergunte o que o pedido já respondeu (com todas as letras ou por implicação clara); só
|
|
100
|
+
// pergunte o que for necessário para decidir e a pessoa não informou — ou o que vale confirmar por julgamento.
|
|
89
101
|
export async function decidePurpose(briefing, materials = []) {
|
|
90
102
|
const res = await chat([
|
|
91
103
|
{ role: "system", content: `Você decide PARA QUE SERVE um material de apresentação, lendo o pedido. São só dois usos:
|
|
92
|
-
- palestra: PARA APRESENTAR — alguém fala e a plateia assiste (palestra, reunião, pitch, aula expositiva, workshop). Letra grande, respiro, pouco texto na tela, para não dar sono.
|
|
104
|
+
- palestra: PARA APRESENTAR — alguém fala e a plateia assiste (palestra, reunião, pitch, aula expositiva, workshop, conferência, congresso). Letra grande, respiro, pouco texto na tela, para não dar sono.
|
|
93
105
|
- consulta: PARA ESTUDAR DEPOIS — o material vai ser enviado e a audiência usa como fonte de estudo (apostila, documentação, guia, curso para guardar). Conteúdo denso, com bastante texto no slide.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
106
|
+
Primeiro EXTRAIA do pedido (cite a evidência em "extraido"):
|
|
107
|
+
- uso: "apresentar", "congresso", "conferência", "pitch", "defesa", "aula" → palestra, sem perguntar; "apostila", "guia", "material de estudo", "para ler depois" → consulta, sem perguntar.
|
|
108
|
+
- texto: se o pedido disser com todas as letras quanto texto quer ("bastante texto", "explicação completa", "pouco texto", "só tópicos"), isso resolve: não pergunte, registre em "texto" ("muito": consulta; "pouco": palestra).
|
|
109
|
+
- idioma: se o pedido disser o idioma ("em inglês", "in English"), registre em "idioma" ("pt", "en" ou outro código) e não pergunte. Se NÃO disser mas a ocasião for internacional (congresso/conferência internacional, público estrangeiro, paper em inglês), proponha a confirmação em "perguntaIdioma" ("Confirmar o idioma?" com opcoes ["Português", "English"]).
|
|
110
|
+
Só faça a pergunta de uso ("pergunta") se o pedido NÃO permitir decidir com segurança — típico: workshop, treinamento ou "uma apresentação sobre X" sem dizer se o material é só para a sessão ou se vai ser enviado para estudar depois. Nunca pergunte o que já foi extraído.
|
|
111
|
+
Responda só com JSON: {"purpose": "…" ou null, "texto": "muito" | "pouco" | null, "idioma": "pt" | "en" | null, "extraido": "evidências curtas do pedido", "why": "motivo curto", "pergunta": "…" ou null, "opcoes": ["…"], "perguntaIdioma": "…" ou null}` },
|
|
97
112
|
{ role: "user", content: `${materials.length ? `(há ${materials.length} material(is) anexado(s))\n` : ""}Pedido:\n"""\n${briefing}\n"""` },
|
|
98
113
|
], { temperature: 0.1 });
|
|
99
114
|
const m = String(res.text || "").match(/\{[\s\S]*\}/);
|
|
100
115
|
if (!m) return null;
|
|
101
116
|
const j = JSON.parse(m[0]);
|
|
102
117
|
if (j.pergunta) return { question: { question: String(j.pergunta), options: (Array.isArray(j.opcoes) ? j.opcoes : []).map(String).filter(Boolean).slice(0, 4) } };
|
|
103
|
-
|
|
118
|
+
const out = PURPOSES[j.purpose] ? { purpose: j.purpose, texto: ["muito", "pouco"].includes(j.texto) ? j.texto : null, why: String(j.why || "").slice(0, 200) } : null;
|
|
119
|
+
if (!out) return null;
|
|
120
|
+
if (j.idioma === "pt" || j.idioma === "en") out.idioma = j.idioma;
|
|
121
|
+
if (j.perguntaIdioma && !out.idioma) out.languageQuestion = { question: String(j.perguntaIdioma).slice(0, 300), options: ["Português", "English"] };
|
|
122
|
+
return out;
|
|
104
123
|
}
|
|
105
124
|
|
|
106
125
|
// A IA preferiu perguntar antes de gerar: bloco ```pergunta com {"pergunta", "opcoes"}
|
|
@@ -498,7 +517,8 @@ const EDIT_RULES = `Regras de edição:
|
|
|
498
517
|
- Pictos (human, crowd, scene…) são desenhados pelo motor num estilo fixo: você só controla os parâmetros (pose, sign, name, count…). Se a reclamação é sobre o traço de um picto em si, diga isso com franqueza e ofereça alternativas (ex.: outro tipo de figura no deck todo, ou ilustrações geradas se as imagens estiverem ligadas) — de preferência perguntando antes de mudar vários slides.
|
|
499
518
|
- Se a mudança certa afetar muitos slides de um jeito que o usuário talvez não espere, PERGUNTE antes (responda sem bloco yaml).
|
|
500
519
|
- Quando houver imagens do slide renderizado, OLHE para elas antes de responder: elas mostram o que a plateia vê (sobreposição, texto cortado, ordem dos cliques). Descreva o que você vê em vez de perguntar o que o usuário quis dizer.
|
|
501
|
-
- Imagens coladas pelo usuário são referência (ex.: "recrie este slide"): reproduza a estrutura, a hierarquia e o conteúdo com os recursos do sagadeck
|
|
520
|
+
- Imagens coladas pelo usuário são referência (ex.: "recrie este slide"): reproduza a estrutura, a hierarquia e o conteúdo com os recursos do sagadeck.
|
|
521
|
+
- Tabela colada (CSV/Excel) pedindo gráfico: NÃO monte um chart direto. Devolva variants com 2 a 3 TIPOS de gráfico (ex.: barras, linhas, pizza) usando OS MESMOS dados, um por opção com o tipo no label — a pessoa escolhe vendo a prévia. Só faça direto se ela já disse o tipo.`;
|
|
502
522
|
|
|
503
523
|
// Você decide, a cada mensagem, o que a pessoa quer — não existe "modo".
|
|
504
524
|
const CONVERSATION_RULES = `Como responder (você decide pelo que a pessoa quer AGORA, lendo a mensagem e a conversa):
|
|
@@ -507,6 +527,16 @@ const CONVERSATION_RULES = `Como responder (você decide pelo que a pessoa quer
|
|
|
507
527
|
(dado, história, exemplo, pergunta para a plateia, jeito visual de mostrar), pergunte o que falta (no máximo 2 perguntas).
|
|
508
528
|
Curto: até ~150 palavras. Termine com um bloco \`\`\`opcoes com 2 a 4 respostas curtas (até 8 palavras) que a pessoa poderia clicar,
|
|
509
529
|
uma por linha — inclua uma que peça para aplicar o que foi sugerido (ex.: "Pode fazer isso").
|
|
530
|
+
1a. PERGUNTA sobre o assunto ou o material ("o que é…?", "por que…?", "me explica…?", "como funciona…?"): é CONVERSA, não
|
|
531
|
+
pedido de slide. RESPONDA a pergunta com o que você sabe (e com o material anexado, se houver), sem bloco yaml e sem
|
|
532
|
+
tocar nos slides. Só vira AÇÃO com verbo explícito de fazer ("crie", "adicione", "gere", "monte", "transforme em slides",
|
|
533
|
+
"aplica o que combinamos").
|
|
534
|
+
1b. BRAINSTORM ANTES DE GERAR — a pessoa diz que vai pedir a geração depois ("primeiro vamos discutir", "depois eu peço
|
|
535
|
+
para gerar", "só ideias por enquanto"): NÃO gere nada parcial nem antecipe slides. Discuta, combine e CONFIRME o plano;
|
|
536
|
+
a geração acontece quando ela pedir. Se ela aprovar uma sugestão tua ou vocês fecharem um ponto ("fechado", "isso mesmo",
|
|
537
|
+
"pode ser assim"), grave na hora com patch \`deck: { context: { decisoes: [...as que já havia, mais a nova] } }\`: a lista de
|
|
538
|
+
decisões é o CONTRATO do que gerar depois — nunca apague nem resuma um item sem ela pedir. Gerar depois usa a conversa
|
|
539
|
+
inteira MAIS essas decisões; nada combinado se perde.
|
|
510
540
|
2. AÇÃO — pediu para corrigir, alterar, adicionar, remover, ou autorizou o que foi combinado ("pode fazer", "manda ver", "aplica"):
|
|
511
541
|
faça, com o bloco yaml de patch. Se veio de uma conversa, aplique exatamente o que foi combinado nela.
|
|
512
542
|
3. VERSÕES — pediu alternativas, opções ou "N versões" de um slide: devolva um bloco yaml com \`variants\` (2 a 4 versões completas e
|
|
@@ -723,6 +753,8 @@ export function sanitizeCheck(spec, indices) {
|
|
|
723
753
|
// Memória da conversa: as últimas trocas vão inteiras; das mais antigas, o que a PESSOA disse fica (compactado,
|
|
724
754
|
// em ordem), para o modelo não esquecer o que foi pedido lá no começo. As respostas antigas da IA saem: o que ela
|
|
725
755
|
// fez está no próprio deck. A conversa é de um deck só (o Studio guarda uma por apresentação).
|
|
756
|
+
// GARANTIA ANTI-RESUMO: o histórico nunca é reescrito por modelo — só há corte por tamanho (preserva o começo e o
|
|
757
|
+
// fim, nunca o meio preferido), e o contrato sem perdas são as decisões travadas em context.decisoes no deck.
|
|
726
758
|
// ---------------------------------------------------------------------------------------------
|
|
727
759
|
const RECENT_TURNS = 16, OLD_MSG_CHARS = 700, MEMORY_CHARS = 9000;
|
|
728
760
|
export function conversationFor(history = []) {
|
|
@@ -1040,6 +1072,8 @@ export async function generateDeck(briefing, { theme, slides, duration, style, d
|
|
|
1040
1072
|
say("entendendo para que serve o material…");
|
|
1041
1073
|
decided = await decidePurpose(briefing, materials).catch(() => null);
|
|
1042
1074
|
if (decided?.question) return { question: decided.question };
|
|
1075
|
+
// ocasião internacional sem idioma informado: confirma antes de gerar no idioma errado
|
|
1076
|
+
if (decided?.languageQuestion && (language === "auto" || !language)) return { question: decided.languageQuestion };
|
|
1043
1077
|
}
|
|
1044
1078
|
// pesquisa: a IA decide se o que ela sabe basta; se não, busca, escolhe as fontes, lê e anota (src/research)
|
|
1045
1079
|
let researchReport;
|
|
@@ -1072,7 +1106,10 @@ export async function generateDeck(briefing, { theme, slides, duration, style, d
|
|
|
1072
1106
|
decided?.texto ? `A pessoa disse quanto texto quer: ${decided.texto} texto na tela (grave maxWords: ${decided.texto === "muito" ? 200 : 35}).` : "",
|
|
1073
1107
|
answer ? `Resposta da pessoa à sua pergunta sobre o material: ${answer}` : "",
|
|
1074
1108
|
author ? `Autor padrão das preferências: ${author}. É apenas um padrão; autoria ou organização explicitamente indicada no pedido tem prioridade. Grave a atribuição solicitada em author para não aplicar este padrão.` : "",
|
|
1075
|
-
|
|
1109
|
+
// idioma respondido ou extraído do pedido vale como instrução (inclui a resposta à confirmação de idioma)
|
|
1110
|
+
/english|ingl[eê]s/i.test(answer || "") || (!language || language === "auto") && decided?.idioma === "en" ? "Escreva todo o conteúdo em inglês."
|
|
1111
|
+
: /portuguese|portugu[eê]s/i.test(answer || "") || (!language || language === "auto") && decided?.idioma === "pt" ? "Escreva todo o conteúdo em português."
|
|
1112
|
+
: language && language !== "auto" ? `Escreva todo o conteúdo em ${language}.` : "",
|
|
1076
1113
|
].filter(Boolean).join("\n");
|
|
1077
1114
|
const starter = { title: "Nova apresentação", ...(theme ? { theme } : {}), slides: [{ layout: "cover", title: "Nova apresentação" }] };
|
|
1078
1115
|
const instruction = `Crie a apresentação inteira que a pessoa pediu abaixo. O deck atual é só um começo em branco: devolva preferencialmente o deck completo com slides como LISTA YAML. Se usar insert, todos os novos entram after: 1 (referência ao único slide original, nunca a um slide que você ainda está inserindo). Grave no deck o title, o theme, o purpose e, se o pedido disser a ocasião, o context.
|
|
@@ -1114,9 +1151,14 @@ Faça agora, sem oferecer versões. Só pergunte se não der mesmo para saber o
|
|
|
1114
1151
|
if (reviewCheck) {
|
|
1115
1152
|
say('Conferindo a apresentação e os estados interativos…');
|
|
1116
1153
|
quality = await reviewCheck(spec, spec.slides.map((_, i) => i));
|
|
1154
|
+
// problema idêntico ao de rodada anterior = tentativa sem efeito: não queima token de novo
|
|
1155
|
+
const attempted = new Set();
|
|
1117
1156
|
for (let round = 0; quality.issues.length && round < 3; round++) {
|
|
1157
|
+
const fresh = quality.issues.filter(x => !attempted.has(JSON.stringify(x)));
|
|
1158
|
+
if (!fresh.length) { say('Revisão: nada novo para corrigir; encerro as tentativas.'); break; }
|
|
1159
|
+
fresh.forEach(x => attempted.add(JSON.stringify(x)));
|
|
1118
1160
|
try {
|
|
1119
|
-
const repaired = await editDeck({ spec, instruction: `Corrija somente os problemas observados. Não elimine recursos explicitamente pedidos para resolver uma colisão: reposicione, redimensione ou reorganize a composição mantendo a função. Preserve conteúdo e ordem do briefing (${briefing}): ${JSON.stringify(
|
|
1161
|
+
const repaired = await editDeck({ spec, instruction: `Corrija somente os problemas observados. Não elimine recursos explicitamente pedidos para resolver uma colisão: reposicione, redimensione ou reorganize a composição mantendo a função. Preserve conteúdo e ordem do briefing (${briefing}): ${JSON.stringify(fresh)}.`, images, imageOptions, materials, drawCheck, reviewDepth: 1, repairSlides: fresh.map(x => x.slide - 1), onProgress: onEvent });
|
|
1120
1162
|
const nextQuality = await reviewCheck(repaired.spec, repaired.spec.slides.map((_, i) => i));
|
|
1121
1163
|
spec = repaired.spec; quality = nextQuality;
|
|
1122
1164
|
} catch (error) {
|