sagadeck 1.4.0 → 1.6.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 (58) hide show
  1. package/README.md +59 -67
  2. package/SKILL.md +18 -11
  3. package/bin/sagadeck.js +12 -11
  4. package/docs/ANDAMENTO.md +355 -0
  5. package/docs/REFERENCIA.md +4 -4
  6. package/package.json +2 -3
  7. package/src/ai/bench.js +132 -0
  8. package/src/ai/context.js +4 -2
  9. package/src/ai/critique.js +114 -0
  10. package/src/ai/deck-ai.js +142 -32
  11. package/src/ai/document-visuals.js +7 -3
  12. package/src/ai/ia-config.js +101 -0
  13. package/src/ai/llm.js +62 -26
  14. package/src/ai/progress.js +23 -0
  15. package/src/ai/quality.js +30 -2
  16. package/src/api-client.js +2 -1
  17. package/src/build.js +3 -0
  18. package/src/decision-lab.js +2 -1
  19. package/src/deck-file.js +2 -1
  20. package/src/elements.js +10 -3
  21. package/src/figures/charts.js +7 -4
  22. package/src/figures/diagrams.js +15 -1
  23. package/src/figures/ufmap.js +2 -1
  24. package/src/fs-retry.js +19 -0
  25. package/src/layouts.js +1 -1
  26. package/src/library.js +75 -19
  27. package/src/locale.js +18 -0
  28. package/src/mcp/server.js +2 -2
  29. package/src/motion.js +5 -3
  30. package/src/preferences.js +5 -1
  31. package/src/research/research.js +6 -5
  32. package/src/runtime/calc.js +3 -3
  33. package/src/runtime/decision-lab.js +1 -1
  34. package/src/runtime/motion.js +5 -1
  35. package/src/runtime/runtime.js +2 -1
  36. package/src/studio/ai-chat-route.js +100 -4
  37. package/src/studio/banner.js +18 -0
  38. package/src/studio/code-version.js +35 -0
  39. package/src/studio/generate.js +53 -0
  40. package/src/studio/ia-routes.js +53 -0
  41. package/src/studio/project.js +8 -7
  42. package/src/studio/public/ai-settings.js +76 -0
  43. package/src/studio/public/app.js +11 -239
  44. package/src/studio/public/chat.js +71 -1
  45. package/src/studio/public/header-footer.js +140 -0
  46. package/src/studio/public/index.html +6 -0
  47. package/src/studio/public/library.html +3 -1
  48. package/src/studio/public/library.js +7 -5
  49. package/src/studio/public/preferences-ui.js +86 -0
  50. package/src/studio/public/provenance.js +13 -0
  51. package/src/studio/public/style.css +17 -0
  52. package/src/studio/public/ui-icons.js +1 -1
  53. package/src/studio/public/update-notice.js +25 -0
  54. package/src/studio/public-files.js +11 -1
  55. package/src/studio/server.js +26 -47
  56. package/src/studio/share-routes.js +2 -1
  57. package/src/studio/slide-copy.js +3 -1
  58. package/src/ai/relay.js +0 -45
package/README.md CHANGED
@@ -14,19 +14,26 @@ Ele vem com 11 temas (incluindo estilo Keynote Apple em prata clean e respiráve
14
14
  ## Instalação
15
15
 
16
16
  ```bash
17
- pip install sagadeck
17
+ npm install -g sagadeck
18
18
  ```
19
- Requer **Node.js 18+** e **Chrome ou Edge** instalados (o motor é JavaScript e vem empacotado no pacote Python).
19
+ Requer **Node.js 18+** e **Chrome ou Edge** instalados (o PDF, o PNG e as conferências visuais usam o navegador).
20
+ É tudo Node: não precisa de Python. (O pacote do PyPI parou na 1.5.0.)
21
+
22
+ ## Comece aqui
20
23
 
21
- Quem já vive no Node pode instalar direto, sem Python e sem bundle (vai a fonte + dependências):
22
24
  ```bash
23
- npm install -g sagadeck
25
+ sagadeck studio
24
26
  ```
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.
27
+ e abra **http://127.0.0.1:3517** no navegador. **Essa é a página do sagadeck**: a biblioteca das suas
28
+ apresentações, o editor (estilo PowerPoint) e o chat com a IA. Não há outra interface para abrir nem para criar.
29
+
30
+ Para usar a IA (gerar apresentações, conversar com o chat), clique em **Configurar IA** no alto da biblioteca (ou
31
+ em **IA desligada**, no editor), escolha o provedor, cole a chave, clique em **Testar** e **Salvar**. Detalhes em
32
+ [IA de verdade](#ia-de-verdade-llm).
26
33
 
27
34
  ## Como usar
28
35
 
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
+ Toda apresentação mora na **biblioteca**: `SAGADECK_HOME`, senão a pasta `sagadeck` dentro de **Documentos** no Windows (a pasta Documentos que o Explorer mostra, mesmo redirecionada para o OneDrive ou outro disco) e `~/sagadeck` no Mac e no Linux. Quem usava a biblioteca antiga em `C:\Users\<você>\sagadeck` tem ela movida para Documentos na primeira execução; o caminho antigo continua levando até lá. Uma pasta por tópico, uma por apresentação.
30
37
 
31
38
  ```bash
32
39
  sagadeck new "Minha palestra" --topic=Palestras --theme=editorial
@@ -69,64 +76,52 @@ O sagadeck foi projetado para ser usado por humanos e por agentes de IA:
69
76
  2. **Pelo Estúdio Visual**: Execute `sagadeck studio palestra.yaml` para abrir a interface web estilo PowerPoint, onde você pode editar visualmente no canvas 16:9 e conversar com a IA no chat lateral. A IA se auto-corrige e nunca deixa elementos sobrepostos ou fora das margens.
70
77
  3. **Pelo Protocolo MCP**: Execute `sagadeck mcp` para que IDEs agênticos (Cursor, Windsurf, Cline, Roo Code) descubram e invoquem diretamente as ferramentas de criação, leitura, fiscalização e auto-cura de apresentações.
71
78
 
72
- Se o código de uma ferramenta em Python quiser chamar o sagadeck diretamente (sem montar comandos de terminal), há uma API mínima:
73
-
74
- ```python
75
- import sagadeck
76
- sagadeck.autofix("palestra.yaml") # corrige sobreposições e margens automaticamente
77
- sagadeck.studio() # abre a biblioteca (~/sagadeck)
78
- sagadeck.studio("palestra.yaml", port=3517) # abre direto o editor de um deck
79
- sagadeck.export("palestra.yaml", out="saida") # {'html': …, 'pptx': …, 'pdf': …, 'roteiro': …}
80
- print(sagadeck.check("palestra.yaml")) # relatório do fiscal em texto
81
- contexto = sagadeck.reference() # referência do YAML para colocar no prompt
82
- ```
83
-
84
79
  ## IA de verdade (LLM)
85
80
 
86
- O chat lateral do Studio, o Napkin (texto → slide), o **"✨ Deck com IA"** e a geração de imagens usam um LLM quando há um disponível; sem LLM, o chat e o Napkin continuam funcionando com as regras locais.
87
-
88
- O sagadeck fala com qualquer endpoint compatível com OpenAI (`/v1/chat/completions`). O caminho recomendado é o [modelrelay](https://github.com/naruminho/modelrelay), que decide pela configuração dele para onde as chamadas vão (OpenRouter, OpenAI, gateway corporativo), sem nada disso no sagadeck:
89
-
90
- ```bash
91
- pip install "sagadeck[ia]" # o sagadeck com o modelrelay na versão que ele exige
92
- # ou só o modelrelay: pip install -U modelrelay
93
- ```
94
-
95
- Com um modelrelay mais velho do que o exigido, o sagadeck avisa ao abrir e mostra o comando para atualizar.
96
- Quem desenvolve o modelrelay e o instalou direto do clone (`pip install -e .` na pasta dele, a "instalação
97
- editável": o Python lê o código da pasta, sem cópia) atualiza só com `git pull` ali, e o aviso diz isso, com a
98
- pasta. `pip install -e .` de novo só quando o modelrelay ganhar uma dependência nova.
81
+ O chat lateral do Studio, o Napkin (texto → slide), o **"Deck com IA"** e a geração de imagens usam um LLM quando há um configurado; sem ele, o chat e o Napkin continuam funcionando com as regras locais.
99
82
 
100
- O jeito mais fácil de configurar é pela tela: abra `sagadeck studio` e clique em **modelrelay** no alto da
101
- biblioteca (ou abra http://127.0.0.1:8765/ com um `modelrelay serve` rodando). Lá você escolhe o provedor
102
- (OpenRouter, OpenAI, Google, DeepSeek, o gateway da empresa…), cola a chave, testa e diz qual modelo faz
103
- **texto** e qual faz **imagem**. A tela grava o `~/.modelrelay/config.toml`, que também dá para editar à mão
104
- (`modelrelay init` cria um modelo comentado). O botão só aparece quando o modelrelay roda nesta máquina; no
105
- modo multiusuário, quem configura é o admin.
83
+ O sagadeck fala direto com qualquer provedor compatível com a API da OpenAI (`/v1/chat/completions`): OpenRouter,
84
+ OpenAI, o proxy de IA da empresa. Não há serviço à parte para instalar nem para manter rodando.
106
85
 
107
- À mão, no `~/.modelrelay/config.toml`, os apelidos que o sagadeck usa são:
86
+ **Pela tela** (o jeito mais fácil): **Configurar IA**, no alto da biblioteca, ou **IA desligada / IA ligada**, no
87
+ editor. Escolha o provedor (OpenRouter, OpenAI ou "outro compatível", com o endereço), cole a chave e diga o modelo
88
+ de cada papel:
108
89
 
109
- ```toml
110
- [models]
111
- "text" = "google/gemini-2.5-flash" # chat, Napkin e geração de deck
112
- "image" = "google/gemini-2.5-flash-image" # imagens
90
+ | papel | para quê | |
91
+ |---|---|---|
92
+ | texto | chat, Napkin, geração e revisão de decks | obrigatório |
93
+ | visão | ver o slide renderizado e as imagens coladas | opcional; vazio = o de texto |
94
+ | imagem | ilustrações (`image_prompt`) | opcional |
95
+ | busca | pesquisa na web da geração (no OpenRouter, um modelo `:online`) | opcional; sem ele, buscadores diretos |
96
+
97
+ **Testar** faz uma pergunta curta com o que está na tela antes de salvar. A configuração fica só nesta máquina, em
98
+ `~/.sagadeck/ia.json` (fora do código e fora da biblioteca, que pode sincronizar com a nuvem); a chave nunca volta
99
+ inteira para a página. No modo multiusuário, quem configura é quem administra o servidor.
100
+
101
+ **À mão**, o mesmo arquivo:
102
+
103
+ ```json
104
+ {
105
+ "provider": "openrouter",
106
+ "url": "https://openrouter.ai/api/v1",
107
+ "keyEnv": "OPENROUTER_API_KEY",
108
+ "models": { "text": "deepseek/deepseek-v4.1-flash", "image": "google/gemini-3.1-flash-image" }
109
+ }
113
110
  ```
111
+ A chave vai em `key` ou numa variável de ambiente indicada em `keyEnv`. `headers` acrescenta cabeçalhos fixos.
114
112
 
115
- O sagadeck se identifica para o modelrelay (cabeçalho `X-Modelrelay-App: sagadeck`), então dá para usar
116
- modelos diferentes só nele, sem afetar outros apps que usam o mesmo modelrelay:
113
+ **Provedor que não segue a API da OpenAI** (outro jeito de autenticar, outro endereço): em vez de mexer no sagadeck,
114
+ aponte `"adaptador"` para um arquivo `.mjs` seu cujo `export default` recebe `(url, init)` e devolve a resposta (como
115
+ o `fetch`). O resto do pedido segue no formato da OpenAI.
117
116
 
118
- ```toml
119
- [apps.sagadeck.models] # só o que muda para o sagadeck; o resto vem de [models]
120
- "text" = "deepseek/deepseek-v4-flash"
121
- ```
122
-
123
- Confira com `modelrelay show --app sagadeck`. Detalhes na seção *Per-app models* do README do modelrelay.
117
+ **Quem usava o modelrelay**: na primeira vez, o sagadeck traz a configuração dele (`~/.modelrelay/config.toml`:
118
+ provedor, chave e os modelos do sagadeck) para o `~/.sagadeck/ia.json`. Depois disso o modelrelay não é mais usado.
124
119
 
125
120
  **Modelo sem visão** (ex.: DeepSeek V4 Flash): o assistente manda uma foto do slide e as imagens que você cola.
126
121
  Se o modelo recusar imagem, o sagadeck refaz o pedido sem as imagens, avisa na resposta ("não enxerga imagens")
127
122
  e não insiste nesse modelo até reiniciar. Tudo funciona, só que a IA não vê o slide renderizado.
128
123
 
129
- Pronto: com o modelrelay instalado no mesmo Python, `sagadeck studio`, `new`, `napkin` e `imagens` sobem um `modelrelay serve` sozinhos enquanto rodam. Rodando o motor Node direto (`node bin/sagadeck.js`), deixe um `modelrelay serve` aberto em outro terminal.
124
+ Na linha de comando, `new`, `napkin` e `imagens` usam a mesma configuração:
130
125
 
131
126
  ```bash
132
127
  sagadeck new palestra --prompt "Palestra de 15 min para gerentes sobre IA com segurança. Ilustre onde fizer sentido." --slides=10
@@ -134,14 +129,15 @@ sagadeck napkin "1) cliente abre chamado 2) triagem por IA 3) analista revisa"
134
129
  sagadeck imagens palestra.yaml # gera as imagens pedidas com image_prompt: no YAML
135
130
  ```
136
131
 
137
- | variável | padrão | |
138
- |---|---|---|
139
- | `SAGADECK_LLM_URL` | `http://127.0.0.1:8765/v1` | qualquer API compatível com OpenAI (ex.: `https://openrouter.ai/api/v1`) |
140
- | `SAGADECK_LLM_KEY` | — | bearer token, se apontar direto para um provedor |
141
- | `SAGADECK_TEXT_MODEL` / `SAGADECK_IMAGE_MODEL` | `text` / `image` | nomes dos modelos |
142
- | `SAGADECK_LLM_TIMEOUT` | `180` | segundos por chamada |
143
- | `SAGADECK_NO_RELAY` | — | `1` impede o pacote Python de subir o modelrelay |
144
- | `SAGADECK_APP` | `sagadeck` | nome com que o sagadeck se identifica ao modelrelay (`[apps.<nome>.models]`) |
132
+ As variáveis abaixo valem por cima da configuração (útil em servidor e em testes):
133
+
134
+ | variável | |
135
+ |---|---|
136
+ | `SAGADECK_LLM_URL` | qualquer API compatível com OpenAI (ex.: `https://openrouter.ai/api/v1`) |
137
+ | `SAGADECK_LLM_KEY` | a chave (bearer token) |
138
+ | `SAGADECK_TEXT_MODEL` / `SAGADECK_VISION_MODEL` / `SAGADECK_IMAGE_MODEL` / `SAGADECK_SEARCH_MODEL` | os modelos de cada papel |
139
+ | `SAGADECK_LLM_TIMEOUT` | segundos por chamada (padrão `180`) |
140
+ | `SAGADECK_IA` | outro arquivo de configuração (padrão `~/.sagadeck/ia.json`) |
145
141
 
146
142
  **Imagens geradas: você pede no texto**, no chat, no briefing do "Deck com IA" ou no `--prompt`; não há caixa para marcar.
147
143
  "Com fotos em todos os slides" ilustra todos; "você decide onde ilustrar" deixa a IA escolher só os slides em que uma imagem
@@ -156,15 +152,13 @@ Todo YAML vindo do LLM é validado (renderiza cada slide); se falhar, o erro vol
156
152
  ```bash
157
153
  npm install
158
154
  node bin/sagadeck.js build templates/exemplo.yaml
159
- npm run bundle # empacota o motor em python/sagadeck/engine
160
- npm run build:py # gera dist/*.whl e dist/*.tar.gz
161
155
  npm test # suíte inteira: motor + runtime + Studio (clicando num Chrome headless)
162
156
  npm run test:unit # só o motor (rápido, sem navegador)
163
- SAGADECK_LIVE=1 npm test # inclui os testes que chamam o LLM de verdade (modelrelay)
157
+ SAGADECK_LIVE=1 npm test # inclui os testes que chamam o LLM de verdade (o configurado em Configurar IA)
164
158
  ```
165
159
  **Toda funcionalidade nova entra com teste** em `test/` — é o que garante que um refactor não apague o que já funciona. Veja [CLAUDE.md](CLAUDE.md).
166
160
 
167
- Publicação: crie uma release `vX.Y.Z` no GitHub (o workflow `.github/workflows/publish.yml` publica no PyPI via *trusted publishing*). A versão fica em `package.json` e `python/sagadeck/__init__.py` (o bundle confere se são iguais).
161
+ Publicação: com a CI verde, crie uma release `vX.Y.Z` no GitHub; o workflow `.github/workflows/publish.yml` publica no npm (*trusted publishing*). A versão fica em `package.json`.
168
162
 
169
163
  ## Arquivo `.sagadeck` (a apresentação inteira)
170
164
 
@@ -193,7 +187,7 @@ formato avisa para atualizar o sagadeck. Se um filtro de e-mail barrar a extens
193
187
  ## Biblioteca
194
188
 
195
189
  `sagadeck studio` sem arquivo abre a biblioteca: suas apresentações organizadas em tópicos, com capa, busca,
196
- lixeira (30 dias) e o menu **⋯** de cada cartão (apresentar, renomear, duplicar, mover, baixar tudo/.sagadeck/PPTX/PDF).
190
+ lixeira (30 dias; o prazo se muda em Preferências) e o menu **⋯** de cada cartão (apresentar, renomear, duplicar, mover, baixar tudo/.sagadeck/PPTX/PDF).
197
191
 
198
192
  **Nova apresentação** tem três caminhos: *Descrever com IA* (o assunto, quanto tempo você tem, que vira o número de
199
193
  slides, o estilo e, se quiser, material de apoio: PDF, Word, PowerPoint, Excel ou um link), *A partir de um arquivo
@@ -205,7 +199,7 @@ Arraste um cartão para um tópico para movê-lo. O ícone da biblioteca, no can
205
199
  Não tem banco de dados: a biblioteca é uma pasta comum, que dá para abrir no Explorer e fazer backup.
206
200
 
207
201
  ```text
208
- ~/sagadeck/ (ou SAGADECK_HOME, ou --library=PASTA)
202
+ Documentos/sagadeck/ (Windows; ~/sagadeck no Mac/Linux; ou SAGADECK_HOME, ou --library=PASTA)
209
203
  Palestras/ tópico = pasta (a cor fica em .topico.json)
210
204
  Minha palestra/ apresentação = pasta com o YAML e os arquivos dela
211
205
  Minha palestra.yaml
@@ -301,8 +295,6 @@ src/export/ PowerPoint, PDF, imagens, roteiro e o fiscal
301
295
  templates/exemplo.yaml deck de exemplo com todos os layouts
302
296
  docs/REFERENCIA.md referência completa do YAML
303
297
  SKILL.md instruções para IAs gerarem decks com o sagadeck
304
- python/sagadeck/ pacote Python (CLI + API) que carrega o motor empacotado
305
- scripts/bundle.mjs empacota o motor para o pip
306
298
  tools/ppt-render.ps1 renderiza um .pptx pelo PowerPoint (para conferir fidelidade)
307
299
  tools/test-live.mjs teste automático do modo apresentação e dos widgets
308
300
  ```
package/SKILL.md CHANGED
@@ -7,15 +7,30 @@ description: Gera apresentações bonitas (HTML animado + PowerPoint editável +
7
7
 
8
8
  O sagadeck gera apresentações profissionais a partir de um arquivo YAML, oferecendo tanto linha de comando (CLI), estúdio web visual no estilo PowerPoint, quanto protocolo MCP para integração com IDEs agênticos (Claude Code, Cursor, Windsurf, Cline, Roo Code).
9
9
 
10
+ ## Para agentes: "deixar pronto para usar"
11
+
12
+ Quando a pessoa pede para instalar o sagadeck e deixar pronto:
13
+
14
+ 1. `npm install -g sagadeck` (Node 18+; não precisa de Python).
15
+ 2. `sagadeck studio` e diga para ela abrir **http://127.0.0.1:3517** (a primeira linha que o comando escreve é
16
+ esse endereço). **O Studio é a interface do sagadeck**: biblioteca, editor e chat com IA.
17
+ 3. **Nunca crie uma página, um servidor ou uma interface própria** para o sagadeck, nem "suba as APIs" à mão: tudo
18
+ que a pessoa precisa está no Studio.
19
+ 4. Para a IA, mostre onde configurar: **Configurar IA** no alto da biblioteca (ou **IA desligada** no editor):
20
+ provedor, chave, Testar e Salvar. A configuração fica em `~/.sagadeck/ia.json`; nunca grave a chave no código nem
21
+ na pasta das apresentações.
22
+ 5. As apresentações moram na biblioteca (`SAGADECK_HOME`, senão Documentos\sagadeck no Windows e `~/sagadeck` fora
23
+ dele). Não crie decks em outra pasta.
24
+
10
25
  ## Comandos Disponíveis
11
26
 
12
27
  | comando | o que faz |
13
28
  |---|---|
14
- | `sagadeck studio [deck.yaml] [--port=3000]` | **Abre o estúdio web estilo PowerPoint** com canvas visual 16:9, edição WYSIWYG direta, detector de sobreposição em tempo real e **chat lateral com IA**. |
29
+ | `sagadeck studio [deck.yaml] [--port=3517]` | **Abre o estúdio web estilo PowerPoint** com canvas visual 16:9, edição WYSIWYG direta, detector de sobreposição em tempo real e **chat lateral com IA**. |
15
30
  | `sagadeck autofix <deck.yaml>` | **Auto-cura do layout**: detecta e repara automaticamente sobreposição entre elementos, elementos fora das margens seguras (120px) e excesso de texto. |
16
31
  | `sagadeck search "<termo>"` | **Pesquisa web sem bloqueio (DuckDuckGo)**: busca dados reais, números e referências confiáveis para enriquecer os slides. |
17
32
  | `sagadeck mcp` | **Servidor MCP**: expõe ferramentas padronizadas (JSON-RPC) para IDEs agênticos criarem, lerem, pesquisarem, fiscalizarem e editarem decks. |
18
- | `sagadeck new deck.yaml [--theme=prata]` | cria um deck de exemplo estruturado |
33
+ | `sagadeck new "<nome>" [--topic=T] [--theme=prata]` | cria um deck de exemplo na biblioteca |
19
34
  | `sagadeck build deck.yaml` | compila `deck.html` standalone (abre no navegador; tecla P = modo apresentador) |
20
35
  | `sagadeck check deck.yaml` | fiscal: texto estourado, sobreposição, contraste, fonte pequena, excesso de texto |
21
36
  | `sagadeck pptx deck.yaml` | gera `deck.pptx` **totalmente editável** no PowerPoint com formas e caixas nativas |
@@ -99,14 +114,6 @@ O agente no terminal deve seguir o fluxo:
99
114
  3. Rodar `sagadeck check deck.yaml` para confirmar ausência de avisos.
100
115
  4. Rodar `sagadeck all deck.yaml` para gerar todas as entregas.
101
116
 
102
- ### 3. Via API Python
103
- ```python
104
- import sagadeck
105
- sagadeck.autofix("palestra.yaml")
106
- sagadeck.build("palestra.yaml")
107
- sagadeck.studio("palestra.yaml", port=3000)
108
- ```
109
-
110
117
  ---
111
118
 
112
119
  ## Decisão de Arquitetura: Mesmo Repositório vs. Repositório Separado
@@ -114,7 +121,7 @@ sagadeck.studio("palestra.yaml", port=3000)
114
121
  ### Por que o Estúdio deve ficar no **mesmo repositório** (`sagadeck`):
115
122
  1. **Fonte Única da Verdade**: Os layouts (incluindo `stats` para KPIs e `steps` para processos), os 11 temas visuais (incluindo `prata` estilo Keynote Apple, `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.
116
123
  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.
117
- 3. **Experiência de Uso (DX) Imediata**: O desenvolvedor ou agente clona o repositório ou roda `pip install sagadeck` / `npm install` 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`).
124
+ 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`).
118
125
  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.
119
126
 
120
127
  ---
package/bin/sagadeck.js CHANGED
@@ -57,7 +57,7 @@ const HELP = `sagadeck — YAML -> apresentação (HTML animado + PowerPoint edi
57
57
  sagadeck estudo <deck.yaml> [--html] gera <deck> - material de estudo.pdf (cada slide + texto de consulta)
58
58
  sagadeck all <deck.yaml> build + check + pptx + pdf + roteiro
59
59
  sagadeck studio [deck.yaml|x.sagadeck] [--port=3517] [--library=PASTA] sem arquivo: abre a biblioteca; já aberto: usa o aberto
60
- (PASTA padrão: SAGADECK_HOME ou ~/sagadeck); com arquivo: abre o editor dele
60
+ (PASTA padrão: SAGADECK_HOME, senão Documentossagadeck no Windows, ~/sagadeck fora dele); com arquivo: abre o editor dele
61
61
  --host=0.0.0.0 abre para a rede (padrão: só esta máquina)
62
62
  --multiuser: uma biblioteca por usuário, atrás de um proxy que envia X-Sagadeck-User
63
63
  --agentes=naru,ana: no multiusuário, quem pode deixar a IA rodar comandos (padrão: ninguém)
@@ -124,6 +124,14 @@ async function doShots(p, dir = p.shots) {
124
124
  }
125
125
 
126
126
  async function main() {
127
+ // biblioteca antiga (~/sagadeck) vai para Documentos\sagadeck no Windows, uma vez (src/library.js)
128
+ if (!flags.library) {
129
+ const { migrateLegacyLibrary } = await import("../src/library.js");
130
+ const m = migrateLegacyLibrary();
131
+ if (m.moved) console.log(`✓ biblioteca movida para ${m.target} (o caminho antigo ${m.legacy} continua levando até ela)`);
132
+ else if (m.error) console.log(`! não deu para mover a biblioteca para ${m.target} (${m.error}); sigo usando ${m.legacy}`);
133
+ else if (m.skipped === "as duas têm conteúdo") console.log(`! há apresentações em ${m.legacy} e em ${m.target}; uso ${m.target}. Junte as duas pelo Explorer se quiser.`);
134
+ }
127
135
  switch (cmd) {
128
136
  case "napkin":
129
137
  case "visual": {
@@ -386,22 +394,15 @@ async function main() {
386
394
  process.exit(1);
387
395
  }
388
396
  const agentUsers = typeof flags.agentes === "string" ? flags.agentes.split(",").map((x) => x.trim()).filter(Boolean) : undefined;
389
- // IA: sobe o `modelrelay serve` junto, se ele estiver instalado e ninguém escutar na porta dele (como o sagadeck do pip)
390
- const { startRelay } = await import("../src/ai/relay.js");
391
- const relay = multiuser ? null : await startRelay();
392
- if (relay) {
393
- console.log(`✓ IA: modelrelay em ${relay.url} (configuração: http://127.0.0.1:8765/)`);
394
- for (const sig of ["exit", "SIGINT", "SIGTERM"]) process.on(sig, () => { relay.stop(); if (sig !== "exit") process.exit(0); });
395
- }
396
397
  const server = createStudioServer(deckFile, { port, host, library, multiuser, userHeader: flags["user-header"], agentUsers });
397
398
  server.on("error", (e) => {
398
399
  if (e.code !== "EADDRINUSE") throw e;
399
400
  console.error(`A porta ${port} está ocupada por outro programa. Use --port=OUTRA.`);
400
401
  process.exit(1);
401
402
  });
402
- server.listen(port, host, () => {
403
- console.log(`✓ SagaDeck Studio ${VERSION} em http://${shownHost}:${port}${deckFile ? "" : " (biblioteca)"} (processo ${process.pid})`);
404
- console.log(multiuser ? ` multiusuário: bibliotecas em ${path.join(library, "usuarios")}` : ` biblioteca: ${library}`);
403
+ server.listen(port, host, async () => {
404
+ const { studioBanner } = await import("../src/studio/banner.js");
405
+ for (const line of studioBanner({ version: VERSION, url: `http://${shownHost}:${port}/`, library, ai: (await import("../src/ai/llm.js")).llmConfig(), multiuser, usersDir: path.join(library, "usuarios"), isLibrary: !deckFile })) console.log(line);
405
406
  });
406
407
  break;
407
408
  }