sagadeck 1.9.0 → 1.11.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 (56) hide show
  1. package/README.md +9 -2
  2. package/SKILL.md +1 -1
  3. package/bin/sagadeck.js +1 -1
  4. package/docs/ANDAMENTO.md +302 -0
  5. package/docs/REFERENCIA.md +72 -4
  6. package/package.json +4 -4
  7. package/src/ai/context.js +7 -2
  8. package/src/ai/critique.js +8 -1
  9. package/src/ai/deck-ai.js +36 -12
  10. package/src/ai/document-visuals.js +6 -2
  11. package/src/ai/ia-check.js +13 -4
  12. package/src/ai/ia-config.js +4 -2
  13. package/src/ai/llm.js +12 -7
  14. package/src/ai/math-check.js +318 -0
  15. package/src/ai/quality.js +3 -3
  16. package/src/ai/units.js +85 -0
  17. package/src/api-client.js +42 -10
  18. package/src/build.js +1 -0
  19. package/src/code-highlight.js +19 -2
  20. package/src/code-language.js +13 -0
  21. package/src/fs-retry.js +2 -2
  22. package/src/lessons.js +72 -11
  23. package/src/mcp/server.js +1 -1
  24. package/src/package.js +4 -0
  25. package/src/runtime/api-core.js +122 -11
  26. package/src/runtime/api-ui.js +2 -1
  27. package/src/runtime/base.css +3 -0
  28. package/src/runtime/skins/aurora-dev.css +39 -0
  29. package/src/runtime/skins/terminal-pro.css +33 -0
  30. package/src/runtime/solution.js +131 -0
  31. package/src/studio/ai-chat-route.js +69 -7
  32. package/src/studio/conference-routes.js +44 -0
  33. package/src/studio/conference.js +144 -0
  34. package/src/studio/developer-starters.js +46 -0
  35. package/src/studio/generation-report.js +61 -1
  36. package/src/studio/generation-request.js +80 -0
  37. package/src/studio/ia-routes.js +1 -1
  38. package/src/studio/layout-samples.js +1 -1
  39. package/src/studio/library-routes.js +7 -0
  40. package/src/studio/public/about.js +68 -0
  41. package/src/studio/public/ai-settings.js +4 -4
  42. package/src/studio/public/app.js +14 -2
  43. package/src/studio/public/chat.js +11 -3
  44. package/src/studio/public/conference-ui.js +91 -0
  45. package/src/studio/public/developer-authoring.css +12 -0
  46. package/src/studio/public/developer-authoring.js +306 -0
  47. package/src/studio/public/generation-guide.js +42 -0
  48. package/src/studio/public/index.html +21 -0
  49. package/src/studio/public/library.css +36 -0
  50. package/src/studio/public/library.html +4 -1
  51. package/src/studio/public/library.js +118 -28
  52. package/src/studio/public/slide-form.js +17 -17
  53. package/src/studio/public/style.css +25 -0
  54. package/src/studio/public/ui-icons.js +1 -1
  55. package/src/studio/server.js +20 -8
  56. package/src/themes.js +48 -0
@@ -37,8 +37,9 @@ import { openLibrary, defaultLibraryRoot, safeName } from "../library.js";
37
37
  import { ApiEnvironments, defaultEnvFile, readRecordings, writeRecording, mimeOf } from "../api-client.js";
38
38
  import { startMockApi, demoEnv, DEMO_FILES } from "../api-demo.js";
39
39
  import { slideSnapshots, diagramCheck } from "./snapshot.js";
40
- import { saveGenReport } from './generation-report.js';
40
+ import { saveGenReport, generationNotice } from './generation-report.js';
41
41
  import { generateForStudio } from "./generate.js";
42
+ import { saveGenerationRequest } from "./generation-request.js";
42
43
  import { runCommand, logCommand } from "../ai/commands.js";
43
44
  import { needsApproval, approvalLabel, awaitApproval, commandEnv, estimateSubmitCost } from "./command-approval.js";
44
45
  import { demoDeck, demoAssets, demoProjectFiles } from "./demo-decks.js";
@@ -51,6 +52,7 @@ import { styleRoutes, styleAction } from "./style-routes.js";
51
52
  import { shareRoutes } from "./share-routes.js";
52
53
  import { apiRoutes } from "./api-routes.js";
53
54
  import { aiChatRoutes } from "./ai-chat-route.js";
55
+ import { conferenceRoutes } from "./conference-routes.js";
54
56
  import { libraryRoutes } from "./library-routes.js";
55
57
  export { lightVariant };
56
58
  import { VERSION, buildInfo } from "./instance.js";
@@ -320,16 +322,19 @@ export function createStudioServer(deckPath = null, opts = {}) {
320
322
  // Gerar com IA (editor ou biblioteca): a IA trabalha numa pasta oculta do tópico (invisível na biblioteca)
321
323
  // e a apresentação só aparece quando pronta, publicada com o título final. Falhou ou a IA perguntou algo:
322
324
  // a pasta vai para a lixeira (recuperável). Minutos, estilo, anexos.
323
- async function generateIntoLibrary(W, topic, b, emit) {
325
+ async function generateIntoLibrary(W, topic, b, emit, origin = "biblioteca") {
324
326
  const L = W.library;
325
327
  const { dir, file } = L.createStagingDeck(topic);
326
328
  try {
327
- const gen = await generateForStudio(b, { dir, materials: takeMaterials(W, b.materials), emit });
329
+ const materials = takeMaterials(W, b.materials);
330
+ const gen = await generateForStudio(b, { dir, materials, emit });
328
331
  if (gen.question) { L.discardStaging(topic, dir); return { question: gen.question }; } // a IA quer saber para que serve o material
329
332
  // estilo padrão da biblioteca (Brand Kit), se a pessoa não escolheu um tema para esta
330
333
  if (!b.theme) gen.spec = L.withDefaultStyle(gen.spec, dir);
331
334
  fs.writeFileSync(file, toYaml(gen.spec), "utf8");
332
335
  saveGenReport(dir, gen);
336
+ // o pedido que originou a apresentação, para auditar depois (.sagadeck/geracao.json)
337
+ try { saveGenerationRequest(dir, { origem: origin, pedido: b, materials, spec: gen.spec, research: gen.research }); } catch { /* o registro não derruba a criação */ }
333
338
  const finalId = L.publishStagedDeck(topic, dir, gen.spec.title || "Nova apresentação");
334
339
  return { id: finalId, file: L.resolveId(finalId), images: gen.images, research: gen.research, quality: gen.quality, variety: gen.variety };
335
340
  } catch (e) {
@@ -464,7 +469,7 @@ export function createStudioServer(deckPath = null, opts = {}) {
464
469
  res.end(html);
465
470
  return;
466
471
  }
467
- if (["/style.css", "/library.css", "/studio-next.css"].includes(pathname)) {
472
+ if (["/style.css", "/library.css", "/studio-next.css", "/developer-authoring.css"].includes(pathname)) {
468
473
  const css = fs.readFileSync(path.join(PUBLIC_DIR, pathname.slice(1)), "utf8");
469
474
  res.writeHead(200, { "Content-Type": "text/css; charset=utf-8" });
470
475
  res.end(css);
@@ -496,7 +501,7 @@ export function createStudioServer(deckPath = null, opts = {}) {
496
501
  res.end(fs.readFileSync(path.join(RUNTIME_DIR, ["/map.js", "/map-background.js"].includes(pathname) ? pathname.slice(1) : `vendor${pathname}`)));
497
502
  return;
498
503
  }
499
- if (["/science.js", "/formula.js", "/calc.js", "/plotly.min.js"].includes(pathname)) {
504
+ if (["/science.js", "/formula.js", "/calc.js", "/solution.js", "/plotly.min.js"].includes(pathname)) {
500
505
  res.writeHead(200, {"Content-Type":"application/javascript; charset=utf-8"});
501
506
  res.end(fs.readFileSync(path.join(RUNTIME_DIR, pathname === "/plotly.min.js" ? "vendor/plotly.min.js" : pathname.slice(1))));
502
507
  return;
@@ -590,7 +595,7 @@ export function createStudioServer(deckPath = null, opts = {}) {
590
595
  // quem é esta instância (o CLI confere antes de abrir outra na mesma porta)
591
596
  if (pathname === "/api/instance" && req.method === "GET") {
592
597
  res.writeHead(200, { "Content-Type": "application/json" });
593
- res.end(JSON.stringify({ app: "sagadeck-studio", ...BUILD, library: libraryRoot, started: STARTED, multiuser: !!opts.multiuser }));
598
+ res.end(JSON.stringify({ app: "sagadeck-studio", ...BUILD, node: process.version, platform: process.platform, library: libraryRoot, started: STARTED, multiuser: !!opts.multiuser }));
594
599
  return;
595
600
  }
596
601
  if (pathname === "/api/deck" && req.method === "GET") {
@@ -707,6 +712,7 @@ export function createStudioServer(deckPath = null, opts = {}) {
707
712
  if (await versionRoutes({req,res,pathname,W,readJSON})) return;
708
713
  if (await meetingRoutes({req,res,pathname,W,readJSON,persist})) return;
709
714
  if (await styleRoutes({ req, res, pathname, W, persist, readJSON })) return;
715
+ if (await conferenceRoutes({ req, res, pathname, W, readJSON, isBundledTemplate })) return;
710
716
  if (await shares.api({ req, res, pathname, W, readJSON })) return;
711
717
  if (pathname === "/api/aspect" && req.method === "POST") {
712
718
  const b = await readJSON(req);
@@ -954,7 +960,7 @@ export function createStudioServer(deckPath = null, opts = {}) {
954
960
  }
955
961
  // O deck novo vai para uma pasta própria na biblioteca (regra do CLAUDE.md), no tópico do deck aberto
956
962
  await respond(res, body.stream, async (emit) => {
957
- const r = await generateIntoLibrary(W, topicOfOpen(W), body, emit);
963
+ const r = await generateIntoLibrary(W, topicOfOpen(W), body, emit, "editor");
958
964
  if (r.question) return { ok: true, question: r.question }; // a IA perguntou para que serve o material
959
965
  W.file = r.file;
960
966
  W.spec = loadSpec(r.file);
@@ -1064,8 +1070,14 @@ Responda só com JSON: {"colunas": [{"nome": "…", "tipo": "tempo|categoria|num
1064
1070
  if (req.method === "GET") {
1065
1071
  let history = W.chatHistory || [];
1066
1072
  if (file) try { history = JSON.parse(fs.readFileSync(file, "utf8")).history || []; } catch { history = []; }
1073
+ // avisos que a criação com IA deixou sem resolver: viram mensagem do chat na primeira abertura
1074
+ const notice = file ? generationNotice(path.dirname(W.file)) : null;
1075
+ if (notice) {
1076
+ history = [...history, { role: "assistant", text: notice, talk: true }];
1077
+ try { const tmp = `${file}.tmp-${process.pid}`; fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(tmp, JSON.stringify({ history }, null, 1)); renameRetry(tmp, file); } catch (e) { console.error("[Studio] conversa:", e.message); }
1078
+ }
1067
1079
  res.writeHead(200, { "Content-Type": "application/json" });
1068
- res.end(JSON.stringify({ history }));
1080
+ res.end(JSON.stringify({ history, notice: !!notice }));
1069
1081
  return;
1070
1082
  }
1071
1083
  if (req.method === "POST") {
package/src/themes.js CHANGED
@@ -25,6 +25,16 @@ const MUSGO_FACES = {
25
25
  quote: { css: "font-family: 'Fraunces', Georgia, serif; font-style: italic; font-weight: 400; letter-spacing: -0.01em; line-height: 1.14;", pptx: { face: "Georgia", italic: true } },
26
26
  };
27
27
 
28
+ // Desenvolvimento: fontes já embutidas, com corpo de leitura e código sem ligaduras que mudem a pontuação.
29
+ const DEV_FACES = {
30
+ display: { css: "font-family: 'Space Grotesk', 'Segoe UI', sans-serif; font-weight: 700; letter-spacing: -0.045em; line-height: 1.02;", pptx: { face: "Segoe UI", bold: true } },
31
+ heading: { css: "font-family: 'Space Grotesk', 'Segoe UI', sans-serif; font-weight: 600; letter-spacing: -0.025em; line-height: 1.1;", pptx: { face: "Segoe UI Semibold" } },
32
+ body: { css: "font-family: 'Space Grotesk', 'Segoe UI', sans-serif; font-weight: 400; line-height: 1.45;", pptx: { face: "Segoe UI" }, pptxBold: { face: "Segoe UI Semibold" } },
33
+ label: { css: `font-family: 'IBM Plex Mono', ${SYS_MONO}; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; line-height: 1.25;`, pptx: { face: "Cascadia Mono" } },
34
+ mono: { css: `font-family: 'IBM Plex Mono', ${SYS_MONO}; font-weight: 400; font-variant-ligatures: none; line-height: 1.5;`, pptx: { face: "Cascadia Mono" }, pptxBold: { face: "Cascadia Mono SemiBold" } },
35
+ quote: { css: "font-family: 'Space Grotesk', 'Segoe UI', sans-serif; font-weight: 400; letter-spacing: -0.02em; line-height: 1.2;", pptx: { face: "Segoe UI" } },
36
+ };
37
+
28
38
  export const THEMES = {
29
39
  sinal: {
30
40
  label: "Sinal — sinalização/segurança: DIN condensada, amarelo de aviso, pictogramas",
@@ -153,6 +163,44 @@ export const THEMES = {
153
163
  radius: 6, deco: "grid",
154
164
  },
155
165
 
166
+ "terminal-pro": {
167
+ label: "Terminal Pro — programação e APIs: grafite, verde menta, janelas de código e rede viva discreta",
168
+ dark: true,
169
+ colors: {
170
+ paper: "0B1118", ink: "EAF5F1", accent: "74E8B4", alert: "FF9C97",
171
+ muted: "A4B8B4", line: "2B4145", surface: "131E28", surfaceDark: "0D1720",
172
+ c1: "74E8B4", c2: "83CCF8", c3: "FFCB8A", c4: "CEB4FF", c5: "FF9C97",
173
+ },
174
+ tones: {
175
+ light: { bg: "paper", fg: "ink", muted: "muted", line: "line", surface: "surface", hi: "accent", em: "accent", onHi: "0B1118" },
176
+ dark: { bg: "070D13", fg: "ink", muted: "muted", line: "line", surface: "surfaceDark", hi: "accent", em: "accent", onHi: "0B1118" },
177
+ accent: { bg: "0D211D", fg: "ink", muted: "B2C8BF", line: "31534A", surface: "152F28", hi: "accent", em: "accent", onHi: "0B1118" },
178
+ alert: { bg: "27151B", fg: "FFF0EC", muted: "DDB9BD", line: "65404A", surface: "382029", hi: "alert", em: "alert", onHi: "0B1118" },
179
+ },
180
+ fontFaces: [],
181
+ faces: DEV_FACES,
182
+ radius: 12, deco: "none", coverAmbient: "rede",
183
+ },
184
+
185
+ "aurora-dev": {
186
+ label: "Aurora Dev — desenvolvimento criativo: azul noite, lilás e ciano, painéis luminosos e rede viva",
187
+ dark: true,
188
+ colors: {
189
+ paper: "101329", ink: "F1F2FF", accent: "C4ADFF", alert: "FFADCA",
190
+ muted: "B5BBD9", line: "394266", surface: "1B2240", surfaceDark: "151B35",
191
+ c1: "C4ADFF", c2: "83E7EF", c3: "FFADCA", c4: "A9C8FF", c5: "FFDCA2",
192
+ },
193
+ tones: {
194
+ light: { bg: "paper", fg: "ink", muted: "muted", line: "line", surface: "surface", hi: "accent", em: "83E7EF", onHi: "101329" },
195
+ dark: { bg: "090D20", fg: "ink", muted: "muted", line: "line", surface: "surfaceDark", hi: "accent", em: "83E7EF", onHi: "101329" },
196
+ accent: { bg: "211737", fg: "ink", muted: "CCC0E5", line: "594572", surface: "302349", hi: "accent", em: "83E7EF", onHi: "101329" },
197
+ alert: { bg: "301A32", fg: "FFF1F8", muted: "DEC0D4", line: "684C65", surface: "43273F", hi: "alert", em: "alert", onHi: "101329" },
198
+ },
199
+ fontFaces: [],
200
+ faces: DEV_FACES,
201
+ radius: 24, deco: "none", coverAmbient: "rede",
202
+ },
203
+
156
204
  jornal: {
157
205
  label: "Jornal — manchete: Franklin Gothic pesada, papel-jornal, azul-tinta",
158
206
  colors: {