sagadeck 1.2.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 (233) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +305 -0
  3. package/SKILL.md +148 -0
  4. package/bin/sagadeck.js +511 -0
  5. package/docs/ANDAMENTO.md +1255 -0
  6. package/docs/EXPERIENCIAS.md +39 -0
  7. package/docs/PLANO-EXPERIENCIAS.md +39 -0
  8. package/docs/PLANO-TESTE-AULAS-TECNICAS.md +44 -0
  9. package/docs/PLANO-TESTE-DESINFORMACAO.md +38 -0
  10. package/docs/REFERENCIA.md +1131 -0
  11. package/package.json +59 -0
  12. package/src/adaptive-layouts.js +18 -0
  13. package/src/ai/art-direction.js +8 -0
  14. package/src/ai/commands.js +130 -0
  15. package/src/ai/context.js +245 -0
  16. package/src/ai/deck-ai.js +1135 -0
  17. package/src/ai/directions.js +74 -0
  18. package/src/ai/document-materials.js +49 -0
  19. package/src/ai/document-visuals.js +215 -0
  20. package/src/ai/evaluation.js +31 -0
  21. package/src/ai/ground.js +55 -0
  22. package/src/ai/llm.js +292 -0
  23. package/src/ai/quality.js +51 -0
  24. package/src/ai/relay.js +45 -0
  25. package/src/ai/staged-generation.js +24 -0
  26. package/src/ai/transform.js +858 -0
  27. package/src/ai/usage.js +17 -0
  28. package/src/ai/variety.js +80 -0
  29. package/src/ai/video-frames.js +24 -0
  30. package/src/ai/video-generation.js +126 -0
  31. package/src/ai/web-operation.js +31 -0
  32. package/src/algo-catalog.js +153 -0
  33. package/src/algo-trace.js +139 -0
  34. package/src/api-client.js +514 -0
  35. package/src/api-collections.js +14 -0
  36. package/src/api-demo.js +222 -0
  37. package/src/aspect.js +40 -0
  38. package/src/build.js +323 -0
  39. package/src/carousel.js +56 -0
  40. package/src/chrome.js +65 -0
  41. package/src/code-highlight.js +68 -0
  42. package/src/code-lab-browser.js +22 -0
  43. package/src/code-lab.js +3 -0
  44. package/src/code-language.js +52 -0
  45. package/src/csv.js +74 -0
  46. package/src/decision-lab.js +20 -0
  47. package/src/deck-file.js +119 -0
  48. package/src/diagram/napkin-examples.js +13 -0
  49. package/src/diagram/napkin.js +539 -0
  50. package/src/docx.js +128 -0
  51. package/src/dynamics/git-sim.js +200 -0
  52. package/src/dynamics/layouts.js +112 -0
  53. package/src/elements.js +554 -0
  54. package/src/experiences.js +124 -0
  55. package/src/exploration.js +31 -0
  56. package/src/export/browser.js +53 -0
  57. package/src/export/estudo.js +67 -0
  58. package/src/export/pptx.js +475 -0
  59. package/src/export/roteiro.js +40 -0
  60. package/src/export/shots.js +182 -0
  61. package/src/figures/charts.js +383 -0
  62. package/src/figures/diagrams.js +117 -0
  63. package/src/figures/icons.js +166 -0
  64. package/src/figures/material-pictos.js +20 -0
  65. package/src/figures/pictos.js +255 -0
  66. package/src/figures/points.js +106 -0
  67. package/src/figures/qr.js +29 -0
  68. package/src/figures/ufmap.js +48 -0
  69. package/src/fiscal/autofix.js +377 -0
  70. package/src/fiscal/normalize.js +115 -0
  71. package/src/graph-lab.js +24 -0
  72. package/src/hologram.js +30 -0
  73. package/src/identity.js +86 -0
  74. package/src/import/crop.js +59 -0
  75. package/src/import/index.js +132 -0
  76. package/src/import/legacy-word.js +42 -0
  77. package/src/import/merge.js +45 -0
  78. package/src/import/office.js +123 -0
  79. package/src/import/omml.js +69 -0
  80. package/src/import/pdf-render.js +56 -0
  81. package/src/import/pptx.js +701 -0
  82. package/src/import/xml.js +42 -0
  83. package/src/infographic.js +401 -0
  84. package/src/layouts.js +723 -0
  85. package/src/lessons.js +171 -0
  86. package/src/library.js +399 -0
  87. package/src/markup.js +116 -0
  88. package/src/master.js +214 -0
  89. package/src/mcp/server.js +371 -0
  90. package/src/meeting.js +42 -0
  91. package/src/motion.js +56 -0
  92. package/src/package.js +116 -0
  93. package/src/playground.js +19 -0
  94. package/src/portal-scene.js +20 -0
  95. package/src/preferences.js +61 -0
  96. package/src/protect.js +51 -0
  97. package/src/purpose.js +24 -0
  98. package/src/pytrace.js +1092 -0
  99. package/src/research/duckduckgo.js +73 -0
  100. package/src/research/research.js +202 -0
  101. package/src/research/svg-image.js +17 -0
  102. package/src/research/visuals.js +52 -0
  103. package/src/runtime/api-collection-ui.js +5 -0
  104. package/src/runtime/api-collections.js +28 -0
  105. package/src/runtime/api-core.js +530 -0
  106. package/src/runtime/api-ui.js +966 -0
  107. package/src/runtime/base.css +1774 -0
  108. package/src/runtime/calc.js +139 -0
  109. package/src/runtime/code-lab.js +371 -0
  110. package/src/runtime/decision-lab.js +49 -0
  111. package/src/runtime/diagram.js +341 -0
  112. package/src/runtime/fit.js +268 -0
  113. package/src/runtime/fonts/caveat.css +2 -0
  114. package/src/runtime/fonts/comic-neue.css +3 -0
  115. package/src/runtime/fonts/fredoka.css +2 -0
  116. package/src/runtime/fonts/index.json +10 -0
  117. package/src/runtime/fonts/inter.css +2 -0
  118. package/src/runtime/fonts/patrick-hand.css +2 -0
  119. package/src/runtime/fonts/plus-jakarta-sans.css +2 -0
  120. package/src/runtime/fonts/press-start-2p.css +2 -0
  121. package/src/runtime/fonts/vt323.css +2 -0
  122. package/src/runtime/formula.js +148 -0
  123. package/src/runtime/graph-lab.js +72 -0
  124. package/src/runtime/meeting.css +7 -0
  125. package/src/runtime/meeting.js +10 -0
  126. package/src/runtime/motion.js +15 -0
  127. package/src/runtime/playground.js +11 -0
  128. package/src/runtime/points.js +19 -0
  129. package/src/runtime/portal-scene.css +6 -0
  130. package/src/runtime/portal-scene.js +22 -0
  131. package/src/runtime/runtime.js +827 -0
  132. package/src/runtime/science.js +89 -0
  133. package/src/runtime/skins/aluminio.css +31 -0
  134. package/src/runtime/skins/arcade.css +25 -0
  135. package/src/runtime/skins/aurora.css +17 -0
  136. package/src/runtime/skins/bauhaus.css +19 -0
  137. package/src/runtime/skins/editorial.css +19 -0
  138. package/src/runtime/skins/jornal.css +17 -0
  139. package/src/runtime/skins/manual-noite.css +14 -0
  140. package/src/runtime/skins/manual.css +14 -0
  141. package/src/runtime/skins/noite.css +21 -0
  142. package/src/runtime/skins/oceano.css +17 -0
  143. package/src/runtime/skins/pop.css +23 -0
  144. package/src/runtime/skins/prata.css +18 -0
  145. package/src/runtime/skins/prisma.css +28 -0
  146. package/src/runtime/skins/rabisco.css +18 -0
  147. package/src/runtime/skins/relevo.css +33 -0
  148. package/src/runtime/skins/sinal.css +16 -0
  149. package/src/runtime/skins/terminal.css +23 -0
  150. package/src/runtime/vendor/KATEX-LICENSE.txt +21 -0
  151. package/src/runtime/vendor/MERMAID-LICENSE.txt +21 -0
  152. package/src/runtime/vendor/PLOTLY-LICENSE.txt +21 -0
  153. package/src/runtime/vendor/katex.css +1 -0
  154. package/src/runtime/vendor/mermaid.min.js +3636 -0
  155. package/src/runtime/vendor/plotly.min.js +3620 -0
  156. package/src/runtime/video-player.js +35 -0
  157. package/src/science.js +162 -0
  158. package/src/studio/ai-response.js +36 -0
  159. package/src/studio/api-routes.js +152 -0
  160. package/src/studio/assets/contexto/leia-me.md +12 -0
  161. package/src/studio/assets/contexto/vendas.csv +7 -0
  162. package/src/studio/assets/imagens/colecao-arquitetura.png +0 -0
  163. package/src/studio/assets/imagens/colecao-cor.png +0 -0
  164. package/src/studio/assets/imagens/colecao-equipe.png +0 -0
  165. package/src/studio/assets/imagens/lavanda-cover.jpg +0 -0
  166. package/src/studio/code-routes.js +32 -0
  167. package/src/studio/command-approval.js +48 -0
  168. package/src/studio/demo-decks.js +98 -0
  169. package/src/studio/direction-routes.js +29 -0
  170. package/src/studio/errors.js +65 -0
  171. package/src/studio/exploration-demo.js +17 -0
  172. package/src/studio/exporting.js +105 -0
  173. package/src/studio/generation-report.js +10 -0
  174. package/src/studio/instance.js +48 -0
  175. package/src/studio/layout-samples.js +152 -0
  176. package/src/studio/project.js +371 -0
  177. package/src/studio/public/api-collections.js +28 -0
  178. package/src/studio/public/app.js +5265 -0
  179. package/src/studio/public/art-preview.js +10 -0
  180. package/src/studio/public/calc-fields.js +11 -0
  181. package/src/studio/public/chat-progress.js +47 -0
  182. package/src/studio/public/developer-fields.js +22 -0
  183. package/src/studio/public/explorer.js +443 -0
  184. package/src/studio/public/history.js +58 -0
  185. package/src/studio/public/index.html +867 -0
  186. package/src/studio/public/inspector.js +270 -0
  187. package/src/studio/public/lab-fields.js +5 -0
  188. package/src/studio/public/library.css +275 -0
  189. package/src/studio/public/library.html +55 -0
  190. package/src/studio/public/library.js +471 -0
  191. package/src/studio/public/merge-decks.js +155 -0
  192. package/src/studio/public/review-ui.js +100 -0
  193. package/src/studio/public/screenshot-editor.js +140 -0
  194. package/src/studio/public/share-ui.js +72 -0
  195. package/src/studio/public/slide-clipboard.js +23 -0
  196. package/src/studio/public/slide-form.js +1263 -0
  197. package/src/studio/public/studio-awareness.js +154 -0
  198. package/src/studio/public/studio-next.css +303 -0
  199. package/src/studio/public/style.css +1262 -0
  200. package/src/studio/public/ui-icons.js +2 -0
  201. package/src/studio/public/viewers.js +372 -0
  202. package/src/studio/public/visual-editor.js +326 -0
  203. package/src/studio/public/workspace.js +18 -0
  204. package/src/studio/public-files.js +1 -0
  205. package/src/studio/request-helpers.js +41 -0
  206. package/src/studio/server.js +1583 -0
  207. package/src/studio/share-routes.js +146 -0
  208. package/src/studio/slide-copy-routes.js +26 -0
  209. package/src/studio/slide-copy.js +30 -0
  210. package/src/studio/snapshot.js +160 -0
  211. package/src/studio/style-routes.js +103 -0
  212. package/src/studio/template-collections.js +97 -0
  213. package/src/studio/versions.js +95 -0
  214. package/src/studio/video-download.js +29 -0
  215. package/src/studio/visual-keys.js +52 -0
  216. package/src/studio/workspaces.js +39 -0
  217. package/src/table.js +93 -0
  218. package/src/templates/scaffold.js +372 -0
  219. package/src/themes.js +604 -0
  220. package/src/trace-view.js +450 -0
  221. package/src/uid.js +19 -0
  222. package/src/video-player.js +40 -0
  223. package/src/visual-edits.js +99 -0
  224. package/src/ws.js +134 -0
  225. package/templates/cenario/Texto no cen/303/241rio.yaml" +171 -0
  226. package/templates/cenario/imagens/cidade.svg +30 -0
  227. package/templates/cenario/imagens/pessoa.svg +15 -0
  228. package/templates/cenario-e-ciencia.yaml +47 -0
  229. package/templates/ensaio-api.yaml +160 -0
  230. package/templates/exemplo-alegre.yaml +75 -0
  231. package/templates/exemplo-keynote.yaml +90 -0
  232. package/templates/exemplo-napkin.yaml +139 -0
  233. package/templates/exemplo.yaml +228 -0
@@ -0,0 +1,20 @@
1
+ import {esc} from './markup.js';
2
+ import {iconSVG} from './figures/icons.js';
3
+ import {el} from './elements.js';
4
+
5
+ export const portalExample={layout:'portal',title:'Entre. Descubra. Transforme.',caption:'Catálogo ilustrativo · funciona offline, sem login ou iframe de um portal real.',items:[
6
+ {title:'Documentos em movimento',icon:'files',text:'Uma pilha de documentos vira informação organizada.',beforeWord:'RUÍDO',afterWord:'CLAREZA',before:'Arquivos dispersos, leitura repetida, informação difícil de encontrar.',after:'As mesmas informações agora têm uma ordem e um caminho.',objects:[{label:'Documentos',icon:'files',shape:'sheet'},{label:'Leitura',icon:'scan-text'},{label:'Informação',icon:'database',shape:'column'},{label:'Busca',icon:'search',shape:'orb'},{label:'Resposta',icon:'message-square'}]},
7
+ {title:'Uma entrega, vários caminhos',icon:'truck',text:'Da fábrica até quem espera pelo pedido.',beforeWord:'DISTÂNCIA',afterWord:'CONEXÃO',before:'O pedido atravessa etapas que parecem desconectadas.',after:'Cada etapa tem seu lugar. A jornada fica visível.',objects:[{label:'Fábrica',icon:'factory'},{label:'Depósito',icon:'warehouse'},{label:'Rota',icon:'route'},{label:'Entrega',icon:'truck'}]},
8
+ {title:'Dentro de uma aplicação',icon:'app-window',text:'Atravesse a interface e revele o fluxo por trás dela.',beforeWord:'INTERFACE',afterWord:'SISTEMA',before:'Você vê a interface. O que acontece depois do clique?',after:'Pedido, processamento e resposta formam uma sequência.',objects:[{label:'Interface',icon:'app-window'},{label:'Requisição',icon:'send'},{label:'Serviço',icon:'server'},{label:'Resposta',icon:'check'}]}
9
+ ]};
10
+
11
+ export function portalHTML(s,ctx,head){
12
+ const items=s.items||[];if(!items.length)throw new Error('portal: adicione pelo menos um elemento para explorar.');
13
+ return `<div class="L-portal" data-portal data-view="catalog" data-skin="${esc(['neon','editorial','industrial','aurora'].includes(s.skin)?s.skin:'neon')}">${head(s)}<div class="portal-stage"><div class="portal-atmosphere" aria-hidden="true"><div class="portal-orbit"></div><div class="portal-grid"></div></div>
14
+ <div class="portal-catalog"><div class="portal-sign">${esc(s.label||'ESCOLHA UM MUNDO PARA EXPLORAR')}</div><div class="portal-cards">${items.map((item,i)=>`<button class="portal-card" data-portal-enter="${i}" aria-label="Entrar em ${esc(item.title||'elemento')}"><span class="portal-card-art">${item.image?el({image:item.image,fit:'cover'},ctx):iconSVG(item.icon||'app-window',64)}</span><strong>${esc(item.title||`Elemento ${i+1}`)}</strong><span>${esc(item.text||'Explore esta cena.')}</span><small>Entrar ${iconSVG('arrow-up-right',20)}</small></button>`).join('')}</div></div>
15
+ <div class="portal-inside" hidden><div class="portal-scene-title"></div><div class="portal-floor-word" aria-hidden="true"></div><svg class="portal-paths" viewBox="0 0 1000 600" preserveAspectRatio="none" aria-hidden="true"></svg><div class="portal-objects"></div></div></div>
16
+ <div class="portal-controls"><button data-portal-back hidden>${iconSVG('arrow-left',20)} Voltar ao catálogo</button><button data-portal-transform hidden>${iconSVG('sparkles',20)} Transformar cenário</button><button data-portal-reset>Restaurar</button><output data-portal-description aria-live="polite">${esc(s.caption||'Escolha um elemento para entrar na cena.')}</output></div>
17
+ <script class="portal-model" type="application/json">${JSON.stringify(items.map(item=>({...item,objects:item.objects?.map(node=>({...node,glyph:iconSVG(node.icon||'box',30)}))}))).replace(/</g,'\\u003c')}</script></div>`;
18
+ }
19
+
20
+ export function portalDemo(){return{title:'Demo — mundos que se transformam',theme:'manual-noite',slides:[portalExample,{layout:'portal',title:'A ideia muda. A cena acompanha.',skin:'editorial',label:'UM NOVO CONTEXTO',caption:'O mesmo recurso aplicado a uma aula de processos.',items:[{...portalExample.items[2],title:'O caminho de uma solicitação',beforeWord:'POR QUÊ?',afterWord:'AGORA ENTENDI'}]}]};}
@@ -0,0 +1,61 @@
1
+ // Preferências do sagadeck nesta máquina: ~/.sagadeck/preferencias.json (ou SAGADECK_PREFERENCIAS), ao lado do
2
+ // ambientes.yaml e do identidades.yaml. Valem para o Studio inteiro; o deck (fit:, author…) vence quando define.
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ // Cada preferência: padrão e, para número, a faixa aceita (fora dela volta para o limite)
8
+ export const PREF_SCHEMA = {
9
+ texto: {
10
+ minCodePt: { def: 10, min: 6, max: 24 }, // código encolhe até aqui (pt, como no PowerPoint; 1 pt = 2 px no slide)
11
+ minTextPt: { def: 6, min: 6, max: 30 }, // texto que "encolhe para caber" não passa daqui (6 pt: quase sem limite)
12
+ wrapCode: { def: true }, // linha longa de código quebra em vez de encolher tudo
13
+ },
14
+ ia: {
15
+ perguntar: { def: true }, // pedido sem dizer para que serve o material: a IA pergunta antes de gerar
16
+ imagens: { def: true }, // gerar imagens quando o pedido pedir (desligado: só ícones, gráficos e diagramas)
17
+ pesquisa: { def: true }, // pesquisar na web quando o pedido precisar de informação recente ou de um artigo (a IA decide)
18
+ autor: { def: "" }, // nome na capa e no rodapé das apresentações novas (no lugar de "Seu Nome")
19
+ idioma: { def: "auto" }, // auto = o idioma do pedido
20
+ },
21
+ exportacao: {
22
+ pdfClaro: { def: false }, // PDF e impressão na versão clara do tema (quando o tema tem par claro/escuro)
23
+ },
24
+ };
25
+
26
+ export function preferencesFile(env = process.env, home = os.homedir()) {
27
+ return env.SAGADECK_PREFERENCIAS || path.join(home, ".sagadeck", "preferencias.json");
28
+ }
29
+
30
+ const defaults = () => Object.fromEntries(Object.entries(PREF_SCHEMA).map(([sec, items]) => [sec, Object.fromEntries(Object.entries(items).map(([k, v]) => [k, v.def]))]));
31
+
32
+ // só as chaves conhecidas, cada uma no tipo e na faixa certos (arquivo editado à mão não quebra o Studio)
33
+ function clean(raw) {
34
+ const out = defaults();
35
+ for (const [sec, items] of Object.entries(PREF_SCHEMA)) {
36
+ for (const [k, rule] of Object.entries(items)) {
37
+ const v = raw?.[sec]?.[k];
38
+ if (v === undefined || v === null || v === "") continue;
39
+ if (typeof rule.def === "boolean") out[sec][k] = v === true || v === "true";
40
+ else if (typeof rule.def === "number") { const n = Number(v); if (Number.isFinite(n)) out[sec][k] = Math.min(rule.max, Math.max(rule.min, n)); }
41
+ else out[sec][k] = String(v).slice(0, 200);
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+
47
+ export function loadPreferences(file = preferencesFile()) {
48
+ try { return clean(JSON.parse(fs.readFileSync(file, "utf8"))); } catch { return defaults(); }
49
+ }
50
+
51
+ // patch: { texto: { minCodePt: 12 } } — junta com o que já está salvo e grava de forma atômica
52
+ export function savePreferences(patch, file = preferencesFile()) {
53
+ const cur = loadPreferences(file);
54
+ const merged = Object.fromEntries(Object.keys(PREF_SCHEMA).map((sec) => [sec, { ...cur[sec], ...(patch?.[sec] || {}) }]));
55
+ const out = clean(merged);
56
+ fs.mkdirSync(path.dirname(file), { recursive: true });
57
+ const tmp = `${file}.tmp-${process.pid}`;
58
+ fs.writeFileSync(tmp, JSON.stringify(out, null, 2) + "\n", "utf8");
59
+ fs.renameSync(tmp, file);
60
+ return out;
61
+ }
package/src/protect.js ADDED
@@ -0,0 +1,51 @@
1
+ // Guarda de valores protegidos (segredos dos slides de API) no arquivo de ambientes.
2
+ //
3
+ // No Windows: DPAPI do usuário (ProtectedData, escopo CurrentUser). O texto vira "dpapi:BASE64" e só este usuário,
4
+ // nesta máquina, consegue ler de volta — se o arquivo for copiado ou mandado para alguém, o segredo não vai junto.
5
+ // O valor passa pelo stdin do PowerShell (nunca pela linha de comando, que outros processos conseguem ver).
6
+ // Fora do Windows (ex.: o servidor Linux): fica como está, e o arquivo é gravado só para o dono (0600).
7
+ import { execFileSync } from "node:child_process";
8
+
9
+ export const PROTECTION = process.platform === "win32" ? "dpapi" : "arquivo";
10
+ const PREFIX = "dpapi:";
11
+ const cache = new Map(); // "dpapi:..." -> texto (decifrar custa um PowerShell; o mesmo valor, uma vez só)
12
+
13
+ function powershell(script, input) {
14
+ return execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script],
15
+ { input, encoding: "utf8", windowsHide: true, maxBuffer: 1 << 24 }).trim();
16
+ }
17
+
18
+ // uma chamada para vários valores: entra e sai JSON (lista de strings)
19
+ function dpapi(values, direction) {
20
+ const op = direction === "protect"
21
+ ? "[Convert]::ToBase64String([Security.Cryptography.ProtectedData]::Protect([Text.Encoding]::UTF8.GetBytes($v), $null, 'CurrentUser'))"
22
+ : "[Text.Encoding]::UTF8.GetString([Security.Cryptography.ProtectedData]::Unprotect([Convert]::FromBase64String($v), $null, 'CurrentUser'))";
23
+ const script = `Add-Type -AssemblyName System.Security; $in = [Console]::In.ReadToEnd() | ConvertFrom-Json; $out = @(); foreach ($v in @($in)) { $out += ${op} }; ConvertTo-Json -Compress -InputObject @($out)`;
24
+ const out = JSON.parse(powershell(script, JSON.stringify(values)));
25
+ return Array.isArray(out) ? out : [out];
26
+ }
27
+
28
+ export const isProtected = (v) => typeof v === "string" && v.startsWith(PREFIX);
29
+
30
+ export function protect(plain) {
31
+ if (PROTECTION !== "dpapi") return String(plain);
32
+ const [b64] = dpapi([String(plain)], "protect");
33
+ const v = PREFIX + b64;
34
+ cache.set(v, String(plain));
35
+ return v;
36
+ }
37
+
38
+ export function unprotect(value) {
39
+ if (!isProtected(value)) return value;
40
+ if (!cache.has(value)) unprotectAll([value]);
41
+ return cache.get(value);
42
+ }
43
+
44
+ // decifra de uma vez todos os ainda não vistos (um PowerShell só ao abrir o arquivo)
45
+ export function unprotectAll(values) {
46
+ const todo = [...new Set(values.filter((v) => isProtected(v) && !cache.has(v)))];
47
+ if (!todo.length) return;
48
+ if (PROTECTION !== "dpapi") throw new Error("Este valor foi protegido no Windows (DPAPI) e só pode ser lido lá, pelo mesmo usuário.");
49
+ const out = dpapi(todo.map((v) => v.slice(PREFIX.length)), "unprotect");
50
+ todo.forEach((v, i) => cache.set(v, out[i]));
51
+ }
package/src/purpose.js ADDED
@@ -0,0 +1,24 @@
1
+ // Para que serve o material (deck.purpose): quem decide é a IA, lendo o pedido (ou a pessoa, no Studio). Muda quanto
2
+ // texto cabe num slide antes do fiscal "anti-sono" reclamar e o que a IA e a auto-correção fazem com o texto:
3
+ // material de consulta/aula guarda a explicação NO slide; palestra e executiva deixam pouco texto e o resto em notes.
4
+ // São dois usos: apresentar (letra grande, respiro, pouco texto, para não dar sono) ou estudar depois (o material é
5
+ // enviado e vira fonte de estudo: conteúdo denso). aula, workshop e executiva são de decks antigos: continuam valendo,
6
+ // mas nem o Studio nem a IA oferecem mais.
7
+ export const PURPOSES = {
8
+ palestra: { label: "Para apresentar", hint: "letra grande e pouco texto: a apresentação fica dinâmica e não dá sono", maxWords: 40, dense: false },
9
+ consulta: { label: "Para estudar depois", hint: "o material vai ser enviado e usado como fonte de estudo: conteúdo denso", maxWords: 220, dense: true },
10
+ aula: { label: "Aula (antigo)", hint: "", maxWords: 160, dense: true, legacy: true },
11
+ workshop: { label: "Workshop (antigo)", hint: "", maxWords: 110, dense: false, legacy: true },
12
+ executiva: { label: "Executiva (antigo)", hint: "", maxWords: 40, dense: false, legacy: true },
13
+ };
14
+
15
+ const LAYOUT_WORDS = { onepage: 120, status: 90, solution: 140, calc: 90, algo: 60 }; // densos por natureza (uma página com tudo; exercício resolvido se revela aos poucos)
16
+
17
+ // Limite de palavras na tela de um slide: o do slide > o do deck > o do propósito (ou do layout, se maior) > 40
18
+ export function wordLimit(s = {}, spec = {}, layout = s.layout) {
19
+ if (s.maxWords) return s.maxWords;
20
+ if (spec.maxWords) return spec.maxWords;
21
+ return Math.max(PURPOSES[spec.purpose]?.maxWords || 0, LAYOUT_WORDS[layout] || 0) || 40;
22
+ }
23
+
24
+ export const isDense = (spec = {}) => !!PURPOSES[spec.purpose]?.dense;