sagadeck 1.7.0 → 1.7.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 +1 -1
- package/SKILL.md +3 -3
- package/bin/sagadeck.js +2 -2
- package/docs/ANDAMENTO.md +40 -4
- package/docs/EXPERIENCIAS.md +1 -1
- package/docs/REFERENCIA.md +1 -1
- package/package.json +1 -1
- package/src/ai/ia-config.js +17 -2
- package/src/ai/llm.js +6 -4
- package/src/ai/variety.js +1 -1
- package/src/experiences.js +1 -1
- package/src/library.js +2 -2
- package/src/mcp/server.js +4 -4
- package/src/runtime/base.css +1 -1
- package/src/runtime/code-lab.js +10 -10
- package/src/runtime/skins/aluminio.css +1 -1
- package/src/runtime/skins/prata.css +1 -1
- package/src/studio/banner.js +1 -1
- package/src/studio/ia-routes.js +4 -1
- package/src/studio/public/ai-settings.js +11 -1
- package/src/studio/public/app.js +6 -4
- package/src/studio/public/chat.js +1 -1
- package/src/studio/public/library.css +2 -1
- package/src/studio/public/library.js +5 -1
- package/src/studio/server.js +3 -1
- package/src/templates/scaffold.js +5 -5
- package/src/themes.js +5 -5
- package/templates/{exemplo-keynote.yaml → exemplo-prata.yaml} +6 -6
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Você (ou uma IA) escreve um arquivo `.yaml` com o conteúdo. O sagadeck gera:
|
|
|
9
9
|
| **`.pdf`** | um slide por página, para mandar depois da palestra |
|
|
10
10
|
| **`- roteiro.pdf`** | roteiro do apresentador: miniatura de cada slide + fala + interações + relógio planejado |
|
|
11
11
|
|
|
12
|
-
Ele vem com 11 temas (incluindo
|
|
12
|
+
Ele vem com 11 temas (incluindo prata limpo e respirável, artesanal desenhado à mão, azul elétrico vivo e pop alegre), 23 layouts (incluindo KPIs e fluxos de processo), 2.100+ ícones, pictogramas e cenas geradas na hora, diagramas, 7 tipos de gráfico, e um **fiscal automático** que detecta texto estourado, sobreposição, contraste baixo e slides com texto demais ("anti-sono").
|
|
13
13
|
|
|
14
14
|
## Instalação
|
|
15
15
|
|
package/SKILL.md
CHANGED
|
@@ -120,7 +120,7 @@ O agente no terminal deve seguir o fluxo:
|
|
|
120
120
|
## Decisão de Arquitetura: Mesmo Repositório vs. Repositório Separado
|
|
121
121
|
|
|
122
122
|
### Por que o Estúdio deve ficar no **mesmo repositório** (`sagadeck`):
|
|
123
|
-
1. **Fonte Única da Verdade**: Os layouts (incluindo `stats` para KPIs e `steps` para processos), os 11 temas visuais (incluindo `prata`
|
|
123
|
+
1. **Fonte Única da Verdade**: Os layouts (incluindo `stats` para KPIs e `steps` para processos), os 11 temas visuais (incluindo `prata` limpo e respirável, `rabisco` artesanal pintado à mão, `oceano` azul elétrico vivo, `pop` alegre/chiclete e `aurora` neon), as regras de tipografia e as rotinas de verificação geométrica residem no mesmo código (`src/layouts.js`, `src/themes.js`, `src/fiscal/autofix.js`). Qualquer alteração ou novo layout adicionado ao compilador fica disponível **instantaneamente** no estúdio visual.
|
|
124
124
|
2. **Zero Descompasso de Versões**: Em repositórios separados, quando o formato YAML ou os temas do SagaDeck evoluem, a interface visual quebra ou fica desatualizada até que alguém publique e atualize dependências externas.
|
|
125
125
|
3. **Experiência de Uso (DX) Imediata**: O desenvolvedor ou agente clona o repositório ou roda `npm install -g sagadeck` e tem **tudo num só lugar**: linha de comando (`sagadeck build`), estúdio visual (`sagadeck studio`), servidor de agentes (`sagadeck mcp`) e fiscal (`sagadeck check`).
|
|
126
126
|
4. **Leve e Modular**: A interface web foi construída com tecnologias nativas leves (Vanilla JS, CSS moderno e servidor HTTP embutido do Node.js sem frameworks pesados). Isso mantém o pacote pequeno (~1.1 MB empacotado) sem impactar o desempenho do compilador.
|
|
@@ -130,7 +130,7 @@ O agente no terminal deve seguir o fluxo:
|
|
|
130
130
|
## Novos Temas Visuais & Recursos Anti-Textão
|
|
131
131
|
|
|
132
132
|
### 1. Temas Modernos, Vivos, Jovens e Clean
|
|
133
|
-
- **`prata`**: **
|
|
133
|
+
- **`prata`**: **Limpo e espaçoso**. Jovem, clean, respirável e sofisticado (levado a sério). Fundo cinza prata acetinado (`#F5F5F7`), texto em ardósia/preto profundo (`#1D1D1F`), cinza titânio intermediário (`#86868B`), divisores finos (`#D2D2D7`) e azul vivo (`#0071E3`). Cartelas com efeito vidro fosco / acabamento acetinado com sombra ultra-suave e tipografia do sistema / Inter.
|
|
134
134
|
- **`rabisco`**: Estilo artesanal e lousa com fontes desenhadas à mão (`'Caveat'`, `'Patrick Hand'`, `'Ink Free'`), bordas orgânicas de caderno, sombras desenhadas, fitas adesivas/post-its e ícones em estilo doodle. Ideal quando se quer uma apresentação calorosa, amigável e ilustrada.
|
|
135
135
|
- **`oceano`**: Azul elétrico ultra vibrante (`#0062FF`), ciano neon (`#00C6FF`), tipografia moderna punchy e fundo luminoso de alta energia. Perfeito para tech, startups e produtos dinâmicos.
|
|
136
136
|
- **`pop`**: Alegre, colorido e chiclete com paleta super animada (roxo, rosa chiclete, menta, sol) e cantos ultra-arredondados (26px).
|
|
@@ -146,7 +146,7 @@ Para apresentações de alto impacto sem "muralhas de texto":
|
|
|
146
146
|
- **Sem bloqueios de robô**: Modelos de IA não devem tentar raspar o Google via navegadores headless (o Google bloqueia com CAPTCHA/bot detection). O SagaDeck inclui pesquisa nativa via DuckDuckGo com `sagadeck search "<termo>"` e a ferramenta MCP `sagadeck_web_search`.
|
|
147
147
|
- **Controle Flexível de Fontes**:
|
|
148
148
|
- **Quando o usuário pedir fontes citadas**: Inclua `source: "Fonte: Nome do Relatório, Ano"` nos slides com métricas (`stats`, `number`, `chart`, `split`) e a URL completa no roteiro do apresentador (`notes:`).
|
|
149
|
-
- **Quando o usuário pedir sem fontes (ou estilo clean
|
|
149
|
+
- **Quando o usuário pedir sem fontes (ou estilo clean)**: Omita o campo `source:`, mantendo o slide limpo e desobstruído.
|
|
150
150
|
- **Garantia para Modelos Econômicos**: Qualquer modelo (inclusive os menores como Haiku, Flash ou GPT-4o-mini) deve sempre finalizar com o comando `sagadeck autofix <deck.yaml>`. Isso corrige problemas cobertos pelas regras; depois confira a renderização, porque não garante perfeição geométrica ou visual.
|
|
151
151
|
|
|
152
152
|
## Recursos recentes para autoria por IA
|
package/bin/sagadeck.js
CHANGED
|
@@ -52,7 +52,7 @@ const HELP = `sagadeck — YAML -> apresentação (HTML animado + PowerPoint edi
|
|
|
52
52
|
(imagens: peça no briefing; --images = "você decide onde ilustrar", --no-images = nenhuma)
|
|
53
53
|
sagadeck napkin <texto|arquivo> [-o deck.yaml] [--rules] texto bruto -> slide visual (LLM se houver; --rules força as regras)
|
|
54
54
|
sagadeck imagens <deck.yaml> gera as imagens pedidas com image_prompt: no YAML (modelo de imagem)
|
|
55
|
-
sagadeck scaffold <nome> [--theme=prata] [--type=pitch|
|
|
55
|
+
sagadeck scaffold <nome> [--theme=prata] [--type=lancamento|pitch|palestra] gera esqueleto narrativo pronto (economiza 80% de tokens)
|
|
56
56
|
sagadeck new <nome> [--topic=T] [--theme=x] cria um deck de exemplo NA BIBLIOTECA (<biblioteca>/<tópico>/<nome>/)
|
|
57
57
|
sagadeck build <deck.yaml> gera <deck>.html (abre no navegador; P = modo apresentador)
|
|
58
58
|
sagadeck check <deck.yaml> procura texto estourado, sobreposição, contraste, excesso de texto
|
|
@@ -202,7 +202,7 @@ async function main() {
|
|
|
202
202
|
const { generateScaffold } = await import("../src/templates/scaffold.js");
|
|
203
203
|
const YAML = (await import("yaml")).default;
|
|
204
204
|
const theme = flags.theme || "prata";
|
|
205
|
-
const type = flags.type || "
|
|
205
|
+
const type = flags.type || "lancamento";
|
|
206
206
|
const title = flags.title || (args[0] ? path.basename(args[0], path.extname(args[0])) : "Nova Apresentação");
|
|
207
207
|
const author = flags.author || "Seu Nome";
|
|
208
208
|
const spec = generateScaffold({ title, theme, type, author });
|
package/docs/ANDAMENTO.md
CHANGED
|
@@ -12,10 +12,10 @@ começar. Trabalho em curso vai para o GitHub (commit + push) a cada etapa, nunc
|
|
|
12
12
|
|
|
13
13
|
**Para quem pega isto numa sessão nova, sem a conversa de origem.** O projeto é o **sagadeck**:
|
|
14
14
|
- Repositório GitHub `naruminho/sagadeck`, clonado em `C:\Users\narum\src\sagadeck`, branch `main`.
|
|
15
|
-
- Publicado no npm como `sagadeck` (1.7.
|
|
15
|
+
- Publicado no npm como `sagadeck` (1.7.1: a IA diz por que o `ia.json` não serve e, no servidor, não mostra a configuração; o PyPI parou na 1.5.0).
|
|
16
16
|
- Ferramenta Node (ESM) que transforma apresentações escritas em YAML em HTML/PPTX.
|
|
17
17
|
- Tem um editor web, o Studio (`sagadeck studio`, porta 3517).
|
|
18
|
-
- Tem uma IA (
|
|
18
|
+
- Tem uma IA embutida (provedor direto, configurado em `~/.sagadeck/ia.json` pela tela Configurar IA) que cria e edita decks, inclusive a partir de PDFs de
|
|
19
19
|
artigos científicos para apresentar em congresso.
|
|
20
20
|
|
|
21
21
|
Os itens abaixo vieram de duas rodadas de 08/10/2026, ambas descritas nas seções logo abaixo deste diário:
|
|
@@ -42,6 +42,42 @@ datadas abaixo). O resto fica para depois e só entra se for pedido. Antes de co
|
|
|
42
42
|
ler o conteúdo força o download. Conferir se listar a biblioteca (`openLibrary`, `/api/library`) lê o YAML de
|
|
43
43
|
cada deck; se lê, listar só pelo nome/metadado e abrir o conteúdo sob demanda.
|
|
44
44
|
|
|
45
|
+
## Sem citar o programa de apresentações da maçã (1.7.1) — 10/10/2026
|
|
46
|
+
|
|
47
|
+
Pedido de Narumi: o código e a documentação não citam o programa de apresentações da maçã, a empresa nem os
|
|
48
|
+
aparelhos dela. O tema `prata` passa a se descrever como "limpo e espaçoso"; o `aluminio`, como "lançamento de
|
|
49
|
+
aparelho premium". O exemplo virou `templates/exemplo-prata.yaml`, e o tipo padrão do `scaffold` (CLI e MCP) virou
|
|
50
|
+
`lancamento` (tipo desconhecido continua caindo nele). Saiu também o slogan da empresa do esqueleto. Ficam só os nomes
|
|
51
|
+
técnicos (a fonte do sistema no CSS, a identificação de navegador da pesquisa). `guards.test.js` falha se voltar
|
|
52
|
+
(olha os arquivos do repositório, menos bibliotecas de terceiros); regra no `CLAUDE.md`.
|
|
53
|
+
|
|
54
|
+
## No servidor, a configuração da IA não sai para quem usa (1.7.1) — 10/10/2026
|
|
55
|
+
|
|
56
|
+
No multiusuário (portal do Oracle), `/api/ia` e `/api/ai/status` mostravam a qualquer usuário o provedor, o endereço,
|
|
57
|
+
os modelos, o caminho do `ia.json` e os 4 últimos caracteres da chave, embora ele não pudesse editar nada.
|
|
58
|
+
|
|
59
|
+
- `/api/ia` no multiusuário devolve só `{ editable: false, configured }`; `/api/ai/status`, só `{ available,
|
|
60
|
+
configured, server: true }`.
|
|
61
|
+
- O Configurar IA, no servidor, vira um aviso: quem configura é quem administra, e se a IA está configurada. O
|
|
62
|
+
indicador do editor diz "A IA deste servidor está ligada" (ou "não está respondendo / não está configurada. Avise
|
|
63
|
+
quem administra"), sem endereço nem modelo; o progresso do chat diz "Enviando para a IA".
|
|
64
|
+
- Teste (`test/library-ui.test.js`): com a IA configurada no servidor, nada de chave, modelo, endereço ou arquivo nas
|
|
65
|
+
respostas nem na página do editor; o diálogo não tem campos. Falha sem a mudança.
|
|
66
|
+
|
|
67
|
+
## ia.json ilegível diz o motivo — 10/10/2026
|
|
68
|
+
|
|
69
|
+
Na migração do Oracle (o modelrelay saiu; os serviços `sagadeck` e `sagadeck-dev` falam direto com o OpenRouter, cada
|
|
70
|
+
um com seu `ia.json`), o arquivo foi criado como root e o serviço, que roda com usuário próprio, não conseguia lê-lo:
|
|
71
|
+
o sagadeck dizia só "IA não configurada", como se o arquivo nem existisse.
|
|
72
|
+
|
|
73
|
+
- `readIA` (`src/ai/ia-config.js`) separa "não existe" (só não configurada) de "existe e não serve": sem permissão,
|
|
74
|
+
ilegível, JSON quebrado, sem `url`. O motivo vai em `llmConfig().problem`.
|
|
75
|
+
- Aparece ao subir (`studioBanner`, a linha "IA: não configurada. …", que é o que quem administra vê no log), no erro
|
|
76
|
+
da chamada à IA, em `/api/ia` e `/api/ai/status`, na faixa da biblioteca, no Configurar IA e no indicador do editor.
|
|
77
|
+
- A faixa da biblioteca quebra o texto longo (o caminho do arquivo empurrava o botão para baixo de outro elemento).
|
|
78
|
+
- Testes: `test/ia-config.test.js` (pasta no lugar do arquivo, sem permissão fora do Windows, JSON quebrado, sem
|
|
79
|
+
endereço) e `test/library-ui.test.js` (faixa e Configurar IA dizem "Não deu para ler"). Falham sem a correção.
|
|
80
|
+
|
|
45
81
|
## CI verde de novo (e a regra: merge só com a CI verde) — 09/10/2026
|
|
46
82
|
|
|
47
83
|
A CI do GitHub estava vermelha havia dias (desde antes da 1.4.0) e 13 PRs foram mergeados e a 1.5.0 publicada assim.
|
|
@@ -788,8 +824,8 @@ raciocínio, a mesma conferência sai em 1–2 s e acha o que importa (título r
|
|
|
788
824
|
resposta parava no meio perdia os itens → os que faltaram vão de novo, um por vez. Respostas que não serviram
|
|
789
825
|
ficam em `.sagadeck/transform/falhas/` (as 40 últimas).
|
|
790
826
|
- Casos de 02/10 (decks em `Pedidos de teste/`):
|
|
791
|
-
- **
|
|
792
|
-
|
|
827
|
+
- **Prateado**: o `prata` achado sem graça. Tema novo **`aluminio`**: a cara
|
|
828
|
+
de lançamento de aparelho premium (prata metálico com brilho de estúdio, alumínio escovado, título
|
|
793
829
|
cromado com a cor sólida em `color` para o fiscal medir, disco de metal polido na capa e na seção, cartões de
|
|
794
830
|
alumínio com chanfro, seção em azul-pacífico, escuro em grafite).
|
|
795
831
|
- **história dos videogames em 5 slides, temática, o último como mapa de Mario Kart visto de cima**: saiu no tema
|
package/docs/EXPERIENCIAS.md
CHANGED
|
@@ -6,7 +6,7 @@ Na biblioteca, escolha uma experiência para abrir um roteiro pronto e editável
|
|
|
6
6
|
|---|---|---|
|
|
7
7
|
| Sala de decisão | Estratégia e diretoria | Grafite e ouro, gráficos limpos, recomendação → evidências → riscos → decisão |
|
|
8
8
|
| Matéria de capa | História e investigação | Serifas, papel claro, enquadramentos amplos, pistas e virada narrativa |
|
|
9
|
-
| Modo espetáculo | Palco e
|
|
9
|
+
| Modo espetáculo | Palco e palestras | Contraste luminoso, escala monumental, pausas, escolhas da plateia e revelação por clique |
|
|
10
10
|
| Clube criativo | Workshop | Formas primárias, composição geométrica, desafios e timer |
|
|
11
11
|
| Laboratório vivo | Programação | Código por etapas, saída ilustrativa, screenshot com focos e previsão antes da resposta |
|
|
12
12
|
| Essencial | Produto e comunicação objetiva | Espaço vazio, uma ideia por vez, tipografia discreta e comparação |
|
package/docs/REFERENCIA.md
CHANGED
|
@@ -563,7 +563,7 @@ Linhas que começam com PALAVRAS EM MAIÚSCULAS seguidas de `:` viram etiquetas
|
|
|
563
563
|
|
|
564
564
|
Como no PowerPoint, são duas escolhas independentes:
|
|
565
565
|
|
|
566
|
-
- **Tema** (`theme`): fontes, raio das bordas, textura e uma **pele** própria que rearruma capa, seção e encerramento, muda o jeito dos slides de conteúdo (título, cartões, marcadores da lista, citação, número grande, frase) e põe ornamentos (`sinal`: faixa zebrada e selo no chapéu; `editorial`: fios e capa centralizada; `noite`: moldura fina; `bauhaus`: círculo, quadrado e triângulo; `terminal`: janela com barra e prompt; `jornal`: fios de jornal e manchete sublinhada; `rabisco`: fitas adesivas e títulos inclinados; `oceano`: ondas e chapéu em pílula; `pop`: adesivos e sombra chapada; `aurora`: brilhos e traço em gradiente; `prata`: limpo e centralizado
|
|
566
|
+
- **Tema** (`theme`): fontes, raio das bordas, textura e uma **pele** própria que rearruma capa, seção e encerramento, muda o jeito dos slides de conteúdo (título, cartões, marcadores da lista, citação, número grande, frase) e põe ornamentos (`sinal`: faixa zebrada e selo no chapéu; `editorial`: fios e capa centralizada; `noite`: moldura fina; `bauhaus`: círculo, quadrado e triângulo; `terminal`: janela com barra e prompt; `jornal`: fios de jornal e manchete sublinhada; `rabisco`: fitas adesivas e títulos inclinados; `oceano`: ondas e chapéu em pílula; `pop`: adesivos e sombra chapada; `aurora`: brilhos e traço em gradiente; `prata`: limpo e centralizado; `relevo`: ficção científica sóbria (sala de controle de filme, sem néon): grafite profundo, curvas de nível ao fundo, cantoneiras de mira, título leve embaixo à esquerda, rótulos técnicos em monoespaçada; combina com ilustração 3D realista (terreno, cidade, água) e gráfico 3D (`science` com superfície); `arcade`: videogame e fliperama, tela de tubo com linhas de varredura, neon, título em pixel no meio e chão de tijolos de jogo de plataforma; `aluminio`: prata metálico de lançamento de aparelho premium, título cromado embaixo à esquerda e um disco de metal polido; `manual` e `manual-noite`: documentação técnica, sem ornamento, código e texto corrido legíveis — o par claro/escuro do mesmo arranjo, para material de consulta, tutorial e apostila: `manual` imprime bem, `manual-noite` descansa a vista). Nos slides de conteúdo: `sinal` barra de aviso e placas; `editorial` fios finos e citação centralizada; `noite` tudo centralizado e cartões vazados; `bauhaus` faixas de cor primária e círculos; `terminal` título como comando e cartões-janela; `jornal` fios duplos e colunas; `oceano` cartões flutuando e balões; `pop` contorno grosso e sombra dura; `prata` centralizado e sem caixa; `aluminio` cartões de alumínio com chanfro de luz; `relevo` painéis de vidro fosco com traço gelo; `arcade` cartões de 8 bits com borda de pixel e sombra dura; `rabisco` tracejados e números circulados; `aurora` vidro com brilho. Trocar o tema já muda o arranjo: não reescreva slides só para "combinar" com o tema.
|
|
567
567
|
- **Paleta** (`palette`): só as cores, em qualquer tema. `tinta`, `floresta`, `mar`, `entardecer`, `lavanda`, `grafite`, `neon`, `areia`, `cereja`, `corporativo`, `luzquente`, ou as suas: `palette: { paper: "F4F1EA", ink: "161616", accent: "D7263D", alert: "1B998B" }`. Os quatro tons saem dessas quatro cores, com contraste garantido. A paleta troca também o fundo: em tema escuro por natureza (`relevo`, `arcade`, `aurora`, `noite`, `terminal`), uma paleta de papel claro (`safira`, `corporativo`, `tinta`…) deixa o tema claro; para manter o visual escuro que o pedido quer, não ponha paleta (ou use uma de papel escuro, como `neon`).
|
|
568
568
|
- **Paletas de família** (para identidade de marca sem cansar a vista): a cor forte da marca fica só no detalhe (`alert`, a ênfase pontual) e a página trabalha com os parentes mais agradáveis dela (`accent` e `family`). `rubi` (rosas, magenta e vinho; vermelho só no detalhe), `ametista` (roxos e lilases), `tangerina` (laranja com azul-marinho), `safira` (azuis com um toque âmbar), `esmeralda` (verdes). Na sua: `palette: { paper: "FFFFFF", ink: "3B2B33", accent: "B83A6E", alert: "CC092F", family: ["F9DCE5", "EFA3BC", "D9668F", "7E2349"] }` (de 2 a 8 parentes). `family` pinta as séries extras dos gráficos e os grupos e ramos dos diagramas. Pedido de "cores da empresa X": monte uma paleta de família assim, com a cor forte em `alert`. Para clima futurista de bom gosto (luz quente de prédios e shows de drones, nunca neon barato): `luzquente` — brancos quentes e âmbar que funcionam no claro e, no `tone: dark`, viram HUD âmbar sobre carvão quente.
|
|
569
569
|
|
package/package.json
CHANGED
package/src/ai/ia-config.js
CHANGED
|
@@ -40,10 +40,25 @@ export function cleanIA(raw) {
|
|
|
40
40
|
return out.url || out.adaptador ? out : null;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
// lê o arquivo e, quando ele existe mas não serve, diz por quê (sem isso, um ia.json de outro dono, que o serviço não
|
|
44
|
+
// consegue ler, aparecia só como "IA não configurada"). Sem arquivo não é problema: é só não configurada.
|
|
45
|
+
export function readIA(file = iaFile()) {
|
|
46
|
+
let text;
|
|
47
|
+
try { text = fs.readFileSync(file, "utf8"); }
|
|
48
|
+
catch (e) {
|
|
49
|
+
if (e.code === "ENOENT") return { config: null, problem: "" };
|
|
50
|
+
if (e.code === "EACCES" || e.code === "EPERM") return { config: null, problem: `O arquivo ${file} existe, mas este processo não tem permissão para lê-lo. Confira o dono e as permissões dele.` };
|
|
51
|
+
return { config: null, problem: `Não deu para ler ${file} (${e.code || e.message}).` };
|
|
52
|
+
}
|
|
53
|
+
let raw;
|
|
54
|
+
try { raw = JSON.parse(text); } catch (e) { return { config: null, problem: `O arquivo ${file} não é um JSON válido (${e.message}).` }; }
|
|
55
|
+
const config = cleanIA(raw);
|
|
56
|
+
return config ? { config, problem: "" } : { config: null, problem: `O arquivo ${file} não tem o endereço do provedor ("url").` };
|
|
45
57
|
}
|
|
46
58
|
|
|
59
|
+
export const loadIA = (file = iaFile()) => readIA(file).config;
|
|
60
|
+
export const iaProblem = (file = iaFile()) => readIA(file).problem;
|
|
61
|
+
|
|
47
62
|
// grava de forma atômica, só para quem é dono da conta (a chave mora aqui)
|
|
48
63
|
export function saveIA(cfg, file = iaFile()) {
|
|
49
64
|
const clean = cleanIA(cfg);
|
package/src/ai/llm.js
CHANGED
|
@@ -17,7 +17,7 @@ import { mediaPrompt } from './art-direction.js';
|
|
|
17
17
|
import { currentUsage, recordUsage } from './usage.js';
|
|
18
18
|
import path from 'node:path';
|
|
19
19
|
import { pathToFileURL } from 'node:url';
|
|
20
|
-
import { iaFile,
|
|
20
|
+
import { iaFile, readIA, keyOf, migrateModelrelay } from './ia-config.js';
|
|
21
21
|
import {setTimeout as delay} from 'node:timers/promises';
|
|
22
22
|
|
|
23
23
|
export class LLMError extends Error {
|
|
@@ -38,7 +38,7 @@ let migrated = false;
|
|
|
38
38
|
export function llmConfig(env = process.env) {
|
|
39
39
|
const fromEnv = !!env.SAGADECK_LLM_URL;
|
|
40
40
|
if (!fromEnv && !migrated) { migrated = true; try { migrateModelrelay({ env, file: iaFile(env) }); } catch { /* segue sem */ } }
|
|
41
|
-
const ia = fromEnv ? null :
|
|
41
|
+
const { config: ia, problem } = fromEnv ? { config: null, problem: "" } : readIA(iaFile(env));
|
|
42
42
|
const m = ia?.models || {};
|
|
43
43
|
return {
|
|
44
44
|
url: (env.SAGADECK_LLM_URL || ia?.url || "").replace(/\/+$/, ""),
|
|
@@ -51,6 +51,8 @@ export function llmConfig(env = process.env) {
|
|
|
51
51
|
headers: ia?.headers || {},
|
|
52
52
|
adaptador: ia?.adaptador || "",
|
|
53
53
|
source: fromEnv ? "variável" : ia ? "arquivo" : "nenhuma",
|
|
54
|
+
// o ia.json existe mas não serve (sem permissão, JSON quebrado): o motivo, para não dizer só "não configurada"
|
|
55
|
+
problem,
|
|
54
56
|
timeoutMs: Number(env.SAGADECK_LLM_TIMEOUT || 180) * 1000,
|
|
55
57
|
firstTimeoutMs: Number(env.SAGADECK_LLM_FIRST_TIMEOUT || 600) * 1000,
|
|
56
58
|
};
|
|
@@ -79,7 +81,7 @@ async function send(cfg, url, init) {
|
|
|
79
81
|
if (typeof fn !== "function") throw new LLMError(`O adaptador ${cfg.adaptador} não exporta uma função padrão (url, init) => Response.`);
|
|
80
82
|
return fn(url, init);
|
|
81
83
|
}
|
|
82
|
-
const notConfigured = () => new LLMError("A IA não está configurada: no Studio, abra Configurar IA (ou crie ~/.sagadeck/ia.json).", { code: "AI_NOT_CONFIGURED" });
|
|
84
|
+
const notConfigured = (cfg) => new LLMError(cfg?.problem ? `A IA não está configurada. ${cfg.problem}` : "A IA não está configurada: no Studio, abra Configurar IA (ou crie ~/.sagadeck/ia.json).", { code: "AI_NOT_CONFIGURED" });
|
|
83
85
|
const howToFix = "Confira o provedor e a chave em Configurar IA, no Studio.";
|
|
84
86
|
|
|
85
87
|
// Disponibilidade com cache curto, para o Studio decidir entre LLM e as regras determinísticas.
|
|
@@ -182,7 +184,7 @@ async function chatOnce(messages, opts = {}) {
|
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
async function chatOnceRaw(messages, { model, temperature, maxTokens, onDelta, signal, reasoningOff, cfg = llmConfig() } = {}) {
|
|
185
|
-
if (!llmConfigured(cfg)) throw notConfigured();
|
|
187
|
+
if (!llmConfigured(cfg)) throw notConfigured(cfg);
|
|
186
188
|
const body = { model: resolveModel(cfg, model || cfg.textModel), messages };
|
|
187
189
|
if (reasoningOff) body.reasoning = { enabled: false };
|
|
188
190
|
if (temperature !== undefined) body.temperature = temperature;
|
package/src/ai/variety.js
CHANGED
|
@@ -38,7 +38,7 @@ export function varietyReport(spec) {
|
|
|
38
38
|
// Direções criativas: cada deck gerado sorteia um jeito de contar (e de se parecer), para dois decks sobre
|
|
39
39
|
// assuntos parecidos não saírem iguais.
|
|
40
40
|
export const CREATIVE_DIRECTIONS = [
|
|
41
|
-
"
|
|
41
|
+
"Palco minimalista: uma frase de abertura sem ilustração, grandes espaços vazios, composição central, um número por ideia. Considere prata; destaque em cor apenas na virada.",
|
|
42
42
|
"Investigação editorial: abra com uma cena concreta, revele pistas por enquadramentos e comparações, entregue a conclusão no final. Considere editorial; serifas, linhas finas, papel claro e um único contraste escuro.",
|
|
43
43
|
"Workshop geométrico: abra com uma escolha da plateia, alterne desafios curtos, diagramas e timer. Considere bauhaus ou pop; formas primárias, assimetria e revelação por clique.",
|
|
44
44
|
"Estudo de caso documental: contexto → problema → tentativa → resultado → lição. Considere jornal; manchetes, evidências visuais, cronologia e legendas curtas. Só use dados do briefing ou dados verificados.",
|
package/src/experiences.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
export const EXPERIENCES = [
|
|
5
5
|
{ id: "executivo", name: "Sala de decisão", subtitle: "Clareza que convence.", description: "Uma recomendação, evidências e a decisão. Elegância sóbria para reuniões importantes.", theme: "noite", accent: "#E4B660", background: "#0F1115", color: "#EDEBE6", font: "Segoe UI", category: "executive", tags: ["Estratégia", "Dados", "Sóbrio"], slideCount: 8 },
|
|
6
6
|
{ id: "editorial", name: "Matéria de capa", subtitle: "Uma história bem contada.", description: "Tipografia editorial, enquadramentos amplos e pistas que se revelam até a virada.", theme: "editorial", accent: "#E0432B", background: "#FAF9F6", color: "#141414", font: "Georgia", category: "creative", tags: ["Storytelling", "Editorial", "Autoral"], slideCount: 8 },
|
|
7
|
-
{ id: "palco", name: "Modo espetáculo", subtitle: "A próxima ideia já vem.", description: "Contraste, pausas dramáticas e participação da plateia. Feito para ocupar o palco.", theme: "aurora", accent: "#00F2FE", background: "#0A0D17", color: "#F8FAFC", font: "Segoe UI", category: "stage", tags: ["
|
|
7
|
+
{ id: "palco", name: "Modo espetáculo", subtitle: "A próxima ideia já vem.", description: "Contraste, pausas dramáticas e participação da plateia. Feito para ocupar o palco.", theme: "aurora", accent: "#00F2FE", background: "#0A0D17", color: "#F8FAFC", font: "Segoe UI", category: "stage", tags: ["Palco", "Impacto", "Interativo"], slideCount: 8 },
|
|
8
8
|
{ id: "pop", name: "Clube criativo", subtitle: "Ideias que saem da linha.", description: "Formas geométricas, cores primárias e um workshop com espaço para experimentar.", theme: "bauhaus", accent: "#D93A2B", background: "#F3EEE3", color: "#1A1A1A", font: "Century Gothic", category: "creative", tags: ["Workshop", "Geométrico", "Divertido"], slideCount: 8 },
|
|
9
9
|
{ id: "aula", name: "Laboratório vivo", subtitle: "Entender. Prever. Experimentar.", description: "Código por etapas, terminal, screenshot com foco e desafios para ensinar programação.", theme: "terminal", accent: "#FFB224", background: "#0D0E0D", color: "#DAD6CC", font: "Cascadia Mono", category: "code", tags: ["Programação", "Código", "Aulas"], slideCount: 9 },
|
|
10
10
|
{ id: "minimal", name: "Uma ideia por vez", subtitle: "Só o que importa.", description: "Uma ideia por vez. Muito espaço, tipografia precisa e detalhes que deixam o conteúdo respirar.", theme: "prata", accent: "#0071E3", background: "#F5F5F7", color: "#1D1D1F", font: "Segoe UI", category: "executive", tags: ["Clean", "Produto", "Respiro"], slideCount: 8 },
|
package/src/library.js
CHANGED
|
@@ -429,10 +429,10 @@ export function openLibrary(root, { trashDays } = {}) {
|
|
|
429
429
|
return idOf(file);
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
-
// .pptx (PowerPoint, Google Slides
|
|
432
|
+
// .pptx (PowerPoint, Google Slides e outros programas que exportam .pptx) vira uma apresentação da biblioteca, fiel ao original
|
|
433
433
|
// (src/import): cada slide na mesma posição, mídias em imagens/original/, cópia e fotos do original em original/
|
|
434
434
|
async function importOffice(buffer, topic, name = "Importada.pptx", opts = {}) {
|
|
435
|
-
if (!/\.pptx$/i.test(name)) throw new Error("Por enquanto a importação lê .pptx (o PowerPoint salva em .pptx; o Google Slides e
|
|
435
|
+
if (!/\.pptx$/i.test(name)) throw new Error("Por enquanto a importação lê .pptx (o PowerPoint salva em .pptx; o Google Slides e outros programas exportam).");
|
|
436
436
|
const { importToDir } = await import("./import/index.js");
|
|
437
437
|
const base = topicDir(topic || ensureTopic("Importados"));
|
|
438
438
|
const dir = uniquePath(base, safeName(opts.title || name.replace(/\.pptx$/i, "")));
|
package/src/mcp/server.js
CHANGED
|
@@ -91,8 +91,8 @@ const TOOLS = [
|
|
|
91
91
|
},
|
|
92
92
|
type: {
|
|
93
93
|
type: "string",
|
|
94
|
-
enum: ["
|
|
95
|
-
description: "Tipo narrativo do esqueleto (padrão:
|
|
94
|
+
enum: ["lancamento", "pitch", "palestra"],
|
|
95
|
+
description: "Tipo narrativo do esqueleto (padrão: lancamento)",
|
|
96
96
|
},
|
|
97
97
|
author: { type: "string", description: "Nome do autor ou palestrante" },
|
|
98
98
|
},
|
|
@@ -110,7 +110,7 @@ const TOOLS = [
|
|
|
110
110
|
theme: {
|
|
111
111
|
type: "string",
|
|
112
112
|
enum: ["sinal", "prata", "rabisco", "oceano", "pop", "aurora", "editorial", "noite", "bauhaus", "terminal", "jornal"],
|
|
113
|
-
description: "Tema visual do Sagadeck (ex.: prata para
|
|
113
|
+
description: "Tema visual do Sagadeck (ex.: prata para limpo e espaçoso, rabisco para artesanal, oceano para azul elétrico)",
|
|
114
114
|
},
|
|
115
115
|
author: { type: "string", description: "Nome do autor ou palestrante" },
|
|
116
116
|
},
|
|
@@ -307,7 +307,7 @@ async function handleToolCall(name, args) {
|
|
|
307
307
|
const spec = generateScaffold({
|
|
308
308
|
title: args.title || "Nova Apresentação",
|
|
309
309
|
theme: args.theme || "prata",
|
|
310
|
-
type: args.type || "
|
|
310
|
+
type: args.type || "lancamento",
|
|
311
311
|
author: args.author || "Seu Nome",
|
|
312
312
|
});
|
|
313
313
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
package/src/runtime/base.css
CHANGED
|
@@ -69,7 +69,7 @@ body{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;font-k
|
|
|
69
69
|
.deco-silver::before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(circle at 50% -15%,rgba(255,255,255,.9) 0%,transparent 60%),radial-gradient(circle at 80% 115%,rgba(210,210,215,.4) 0%,transparent 55%)}
|
|
70
70
|
.tone-dark.deco-silver::before,.tone-alert.deco-silver::before{background:radial-gradient(circle at 50% -20%,rgba(255,255,255,.09) 0%,transparent 70%),radial-gradient(circle at 50% 120%,rgba(255,255,255,.04) 0%,transparent 60%)}
|
|
71
71
|
|
|
72
|
-
/* estilo prata
|
|
72
|
+
/* estilo prata: limpo e espacial */
|
|
73
73
|
.deco-silver .card,.deco-silver .stat-card,.deco-silver .step-card{
|
|
74
74
|
box-shadow:0 4px 24px -2px rgba(0,0,0,.05),0 2px 6px -1px rgba(0,0,0,.02)!important;
|
|
75
75
|
border:1px solid rgba(0,0,0,.07)!important;
|