sagadeck 1.8.0 → 1.9.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.
- package/README.md +19 -8
- package/SKILL.md +10 -3
- package/bin/sagadeck.js +23 -0
- package/docs/ANDAMENTO.md +367 -7
- package/docs/PLANO-MELHORIAS.md +180 -0
- package/docs/REFERENCIA.md +131 -110
- package/package.json +33 -2
- package/src/ai/commands.js +2 -2
- package/src/ai/deck-ai.js +1 -1
- package/src/ai/ia-check.js +127 -0
- package/src/ai/ia-config.js +2 -2
- package/src/ai/llm.js +5 -1
- package/src/ai/map-analysis-run.js +1 -1
- package/src/ai/quality.js +1 -1
- package/src/ai/video-generation.js +70 -25
- package/src/build.js +46 -14
- package/src/elements.js +39 -1
- package/src/export/pptx.js +7 -2
- package/src/export/shots.js +7 -2
- package/src/fiscal/normalize.js +19 -0
- package/src/fs-retry.js +37 -2
- package/src/import/office.js +5 -4
- package/src/layouts.js +104 -99
- package/src/lessons.js +1 -1
- package/src/library.js +7 -7
- package/src/map-analysis.js +4 -1
- package/src/map-config.js +10 -4
- package/src/map.js +21 -8
- package/src/mcp/server.js +17 -0
- package/src/motion.js +20 -6
- package/src/purpose.js +1 -1
- package/src/runtime/base.css +88 -332
- package/src/runtime/fit.js +3 -2
- package/src/runtime/fonts/amatic-sc.css +3 -0
- package/src/runtime/fonts/bebas-neue.css +2 -0
- package/src/runtime/fonts/cabin-sketch.css +2 -0
- package/src/runtime/fonts/fraunces.css +3 -0
- package/src/runtime/fonts/gelasio.css +3 -0
- package/src/runtime/fonts/ibm-plex-mono.css +4 -0
- package/src/runtime/fonts/index.json +10 -1
- package/src/runtime/fonts/itim.css +2 -0
- package/src/runtime/fonts/montserrat.css +2 -0
- package/src/runtime/fonts/space-grotesk.css +2 -0
- package/src/runtime/map-background.js +91 -0
- package/src/runtime/map.js +41 -9
- package/src/runtime/runtime.js +81 -100
- package/src/runtime/skins/caderno.css +33 -0
- package/src/runtime/skins/cartaz.css +27 -0
- package/src/runtime/skins/infografico.css +29 -0
- package/src/runtime/skins/lousa.css +22 -0
- package/src/runtime/skins/musgo.css +42 -0
- package/src/runtime/vendor/MAPLIBRE-LEAFLET-LICENSE.txt +15 -0
- package/src/runtime/vendor/MAPLIBRE-LICENSE.txt +116 -0
- package/src/runtime/vendor/geo-contornos.json +1 -1
- package/src/runtime/vendor/maplibre-gl.css +1 -0
- package/src/runtime/vendor/maplibre-gl.js +825 -0
- package/src/runtime/vendor/maplibre-leaflet.js +234 -0
- package/src/runtime/vendor/maplibre-worker.js +10 -0
- package/src/studio/assets/imagens/tour-mapa-botao.jpg +0 -0
- package/src/studio/assets/imagens/tour-mapa-editor.jpg +0 -0
- package/src/studio/command-approval.js +2 -3
- package/src/studio/demo-decks.js +4 -2
- package/src/studio/export-jobs.js +86 -0
- package/src/studio/exporting.js +36 -18
- package/src/studio/feature-demos.js +144 -0
- package/src/studio/ia-routes.js +9 -0
- package/src/studio/layout-samples.js +9 -18
- package/src/studio/library-routes.js +0 -9
- package/src/studio/project.js +1 -0
- package/src/studio/public/ai-settings.js +2 -2
- package/src/studio/public/app.js +41 -51
- package/src/studio/public/export-status.js +154 -0
- package/src/studio/public/index.html +13 -10
- package/src/studio/public/library.css +12 -3
- package/src/studio/public/library.html +1 -0
- package/src/studio/public/library.js +36 -14
- package/src/studio/public/map-data.js +7 -3
- package/src/studio/public/map-editor.js +625 -236
- package/src/studio/public/map-settings.js +3 -1
- package/src/studio/public/slide-form.js +18 -15
- package/src/studio/public/style.css +54 -9
- package/src/studio/public/ui-icons.js +1 -1
- package/src/studio/server.js +27 -12
- package/src/studio/snapshot.js +8 -4
- package/src/studio/template-collections.js +1 -1
- package/src/studio/tour.js +89 -7
- package/src/themes.js +154 -0
- package/templates/cenario-e-ciencia.yaml +1 -21
- package/templates/ensaio-api.yaml +3 -3
- package/templates/cenario/Texto no cen/303/241rio.yaml" +0 -171
- package/templates/cenario/imagens/cidade.svg +0 -30
- package/templates/cenario/imagens/pessoa.svg +0 -15
|
@@ -71,13 +71,31 @@
|
|
|
71
71
|
$("#ai-banner-btn").onclick = () => window.SagaAISettings.open(aiSaved);
|
|
72
72
|
const openEditor = (id, present) => { location.href = `editor?deck=${encodeURIComponent(id)}${present ? "&present=1" : ""}`; };
|
|
73
73
|
|
|
74
|
+
// Demos oficiais (só leitura): as demos de um assunto só (src/studio/feature-demos.js: mapas, programação, APIs com
|
|
75
|
+
// curl) em primeiro, o Conheça o SagaDeck (src/studio/tour.js) e as demos de recursos da vitrine. Moram numa
|
|
76
|
+
// pasta própria da lateral e o Conheça aparece também entre as apresentações. Abrem em prévia (editor?model=…): navegar
|
|
77
|
+
// e apresentar não gravam nada; editar cria a cópia da pessoa em Modelos, como nos modelos. Sem mover, renomear nem
|
|
78
|
+
// excluir: a demo de fábrica nunca muda.
|
|
79
|
+
const officialDemos = () => GALLERY.filter(([kind]) => kind === "recurso");
|
|
80
|
+
const officialId = (key) => (key.startsWith("model-") ? key.slice(6) : key);
|
|
81
|
+
const openOfficial = (key, present) => { location.href = `editor?model=${encodeURIComponent(officialId(key))}&topic=Modelos${present ? "&present=1" : ""}`; };
|
|
82
|
+
function officialCard([, key, title, desc, sw]) {
|
|
83
|
+
return `<div class="card card-oficial" data-oficial="${esc(officialId(key))}" data-key="${esc(key)}" tabindex="0" title="Demo oficial do SagaDeck: só leitura (editar cria uma cópia sua em Modelos)">
|
|
84
|
+
<div class="thumb" style="background:linear-gradient(90deg,${sw.map(esc).join(",")})"><img loading="lazy" alt="" src="api/library/gallery-cover?key=${encodeURIComponent(key)}" onerror="this.remove()">
|
|
85
|
+
<div class="over"><button class="pill" data-present>${ic("play")}Apresentar</button><button class="pill" data-open>${ic("eye")}Abrir</button></div></div>
|
|
86
|
+
<div class="meta"><div class="t"><div class="name">${esc(title)}</div>
|
|
87
|
+
<div class="info"><span class="chip chip-oficial">${ic("lock")}demo oficial</span>·<span class="desc">${esc(desc)}</span></div></div></div>
|
|
88
|
+
</div>`;
|
|
89
|
+
}
|
|
90
|
+
const tourCard = () => officialCard(officialDemos().find(([, key]) => key === "model-conheca"));
|
|
91
|
+
|
|
74
92
|
// ---------------------------------------------------------------- barra lateral
|
|
75
93
|
function renderSide() {
|
|
76
94
|
const loose = data.decks.filter((d) => !d.topic).length;
|
|
77
95
|
$("#side").innerHTML = `
|
|
78
96
|
<button class="nav ${view === "recentes" ? "active" : ""}" data-view="recentes">${ic("clock")}<span class="name">Recentes</span></button>
|
|
79
97
|
<button class="nav ${view === "todas" ? "active" : ""}" data-view="todas">${ic("layout-grid")}<span class="name">Todas</span><span class="count">${data.decks.length || ""}</span></button>
|
|
80
|
-
<button class="nav nav-
|
|
98
|
+
<button class="nav nav-oficiais ${view === "oficiais" ? "active" : ""}" data-view="oficiais" title="Apresentações do próprio SagaDeck, só leitura: o Conheça o SagaDeck e as demos (abrem em prévia)">${ic("lock")}<span class="name">Demos oficiais</span></button>
|
|
81
99
|
<div class="side-title">TÓPICOS<button title="Novo tópico" id="new-topic" aria-label="Novo tópico">${ic("plus")}</button></div>
|
|
82
100
|
${data.topics.map((t) => `<button class="nav ${view === t.id ? "active" : ""}" data-view="${esc(t.id)}" data-topic="${esc(t.id)}"><span class="dot" style="background:${esc(t.color)}"></span><span class="name">${esc(t.name)}</span><span class="count">${t.count || ""}</span></button>`).join("")}
|
|
83
101
|
${loose ? `<button class="nav ${view === "" ? "active" : ""}" data-view="" title="Arquivos .yaml deixados direto na pasta da biblioteca"><span class="dot" style="background:${NO_TOPIC.color}"></span><span class="name">${NO_TOPIC.name}</span><span class="count">${loose}</span></button>` : ""}
|
|
@@ -88,8 +106,6 @@
|
|
|
88
106
|
hydrate($("#side"));
|
|
89
107
|
$("#side").querySelectorAll("[data-view]").forEach((b) => b.onclick = () => { view = b.dataset.view; store.set("libView", view); render(); });
|
|
90
108
|
$("#new-topic").onclick = () => topicDialog();
|
|
91
|
-
// a apresentação de recursos (src/studio/tour.js) abre como os modelos da vitrine: em prévia, sem criar arquivo
|
|
92
|
-
$("#side [data-tour]").onclick = () => { location.href = "editor?model=conheca&topic=Modelos"; };
|
|
93
109
|
$("#side").querySelectorAll("[data-topic]").forEach((b) => {
|
|
94
110
|
b.ondragover = (e) => { if (e.dataTransfer.types.includes("application/x-sagadeck-deck")) { e.preventDefault(); b.classList.add("drop"); } };
|
|
95
111
|
b.ondragleave = () => b.classList.remove("drop");
|
|
@@ -127,6 +143,10 @@
|
|
|
127
143
|
(data.trash.length ? data.trash.map((t) => `<div class="trash-row"><div class="t"><b>${esc(t.title)}</b><div class="sub">Era de ${esc(t.topic || NO_TOPIC.name)} · ${t.slides || 0} slides · excluída ${ago(t.deleted)}</div></div>
|
|
128
144
|
<button class="lib-btn ghost" data-restore="${esc(t.id)}">${ic("rotate-ccw")}Restaurar</button><button class="lib-btn ghost danger" data-purge="${esc(t.id)}">Excluir de vez</button></div>`).join("")
|
|
129
145
|
: `<div class="empty"><div class="big">${ic("trash-2")}</div><h2>Lixeira vazia</h2></div>`);
|
|
146
|
+
} else if (view === "oficiais") {
|
|
147
|
+
const demos = officialDemos().filter(([, , title, desc]) => !q || `${title} ${desc}`.toLowerCase().includes(q));
|
|
148
|
+
main.innerHTML = `<div class="head"><div><h1>${ic("lock")} SagaDeck · demos oficiais</h1><div class="sub">Só leitura: abrem em prévia, sem criar arquivo. Gostou? Edite e nasce uma cópia sua em Modelos.</div></div></div>
|
|
149
|
+
${demos.length ? `<div class="grid">${demos.map(officialCard).join("")}</div>` : `<div class="empty"><div class="big">${ic("search")}</div><h2>Nada encontrado</h2></div>`}`;
|
|
130
150
|
} else {
|
|
131
151
|
const t = view === "" ? NO_TOPIC : topicOf(view);
|
|
132
152
|
const all = t ? data.decks.filter((d) => d.topic === t.id) : data.decks;
|
|
@@ -144,11 +164,13 @@
|
|
|
144
164
|
} else if (!data.decks.length && !q) {
|
|
145
165
|
main.innerHTML += `<div class="empty"><div class="big">${ic("library")}</div><h2>Sua biblioteca está vazia</h2>
|
|
146
166
|
<p>Crie um tópico (ex.: Palestras, Trabalho) e comece uma apresentação — em branco, com IA ou importando um .sagadeck ou um PowerPoint (.pptx).</p>
|
|
147
|
-
<div class="actions"><button class="lib-btn ghost" data-empty-topic>${ic("folder-plus")}Novo tópico</button><button class="lib-btn primary" data-empty-new>${ic("plus")}Nova apresentação</button></div></div
|
|
167
|
+
<div class="actions"><button class="lib-btn ghost" data-empty-topic>${ic("folder-plus")}Novo tópico</button><button class="lib-btn primary" data-empty-new>${ic("plus")}Nova apresentação</button></div></div>
|
|
168
|
+
<div class="grid grid-oficial">${tourCard()}</div>`;
|
|
148
169
|
} else if (!list.length) {
|
|
149
170
|
main.innerHTML += `<div class="empty"><div class="big">${ic("search")}</div><h2>Nada encontrado</h2><p>Nenhuma apresentação com "${esc(q)}".</p></div>`;
|
|
150
171
|
} else {
|
|
151
|
-
|
|
172
|
+
// em Recentes e Todas, o Conheça o SagaDeck (demo oficial) vem primeiro, para quem ainda não abriu a pasta
|
|
173
|
+
main.innerHTML += `<div class="grid">${t && !q ? `<div class="card new-card" id="card-new" tabindex="0"><div class="thumb"><div class="plus">${ic("plus")}Nova em ${esc(t.name)}</div></div></div>` : ""}${!t && !q ? tourCard() : ""}${items.map((d) => card(d, !t)).join("")}</div>`;
|
|
152
174
|
}
|
|
153
175
|
const sortSel = $("#sort");
|
|
154
176
|
if (sortSel) { sortSel.value = sortBy; sortSel.onchange = () => { sortBy = sortSel.value; store.set("libSort", sortBy); render(); }; }
|
|
@@ -159,6 +181,10 @@
|
|
|
159
181
|
}
|
|
160
182
|
|
|
161
183
|
function wire(main) {
|
|
184
|
+
main.querySelectorAll(".card-oficial").forEach((c) => {
|
|
185
|
+
c.onclick = (e) => openOfficial(c.dataset.key, !!e.target.closest("[data-present]"));
|
|
186
|
+
c.onkeydown = (e) => { if (e.key === "Enter") openOfficial(c.dataset.key); };
|
|
187
|
+
});
|
|
162
188
|
main.querySelectorAll(".card[data-id]").forEach((c) => {
|
|
163
189
|
const id = c.dataset.id;
|
|
164
190
|
c.ondragstart = (e) => { e.dataTransfer.setData("application/x-sagadeck-deck", id); c.classList.add("dragging"); };
|
|
@@ -228,8 +254,7 @@
|
|
|
228
254
|
m.querySelectorAll("[data-move]").forEach((b) => b.onclick = () => { closeMenus(); act(() => api("api/library/decks/move", { id, topic: b.dataset.move }), `Movida para ${topicOf(b.dataset.move).name}`); });
|
|
229
255
|
m.querySelectorAll("[data-dl]").forEach((b) => b.onclick = () => {
|
|
230
256
|
closeMenus();
|
|
231
|
-
|
|
232
|
-
location.href = `api/library/download?id=${encodeURIComponent(id)}&kind=${b.dataset.dl}${b.dataset.notas === "0" ? "¬as=0" : ""}`;
|
|
257
|
+
window.SagadeckExports.start({ id, kind: b.dataset.dl, notes: b.dataset.notas !== '0', title: d.title }).catch(err => toast('Não deu para gerar: ' + err.message, 8000));
|
|
233
258
|
});
|
|
234
259
|
}
|
|
235
260
|
|
|
@@ -281,16 +306,13 @@
|
|
|
281
306
|
["visual", "model-lavanda", "Estúdio lavanda", "Mosaicos, cápsulas e composições editoriais", ["#EFE9FB", "#5B3FA8", "#D8C8F5"]],
|
|
282
307
|
["visual", "model-executivo", "Relatório executivo", "Resumo, indicadores e plano de trabalho", ["#0F1115", "#E4B660", "#EDEBE6"]],
|
|
283
308
|
["visual", "model-workshop", "Workshop visual", "Perguntas, código guiado e consulta", ["#FFF7E6", "#1A1A1A", "#E8590C"]],
|
|
309
|
+
["recurso", "model-mapas", "Mapas", "Pontos, bolhas, regiões, linhas e áreas; rota, alcance, raio e faixa calculados; a IA e o Editar mapa", ["#EAF4F8", "#0B2A3C", "#0E7CB8"]],
|
|
310
|
+
["recurso", "model-programacao", "Programação", "Código destacado e guiado, código que a plateia roda, algoritmo animado, Git a dois e diagramas", ["#0C0F0B", "#F0B24A", "#A9C79B"]],
|
|
311
|
+
["recurso", "model-apis", "APIs com curl", "A anatomia de um curl, chamadas ao vivo com o curl para copiar, polling, streaming e as opções do dia a dia", ["#170A10", "#F2506A", "#F3A6B5"]],
|
|
284
312
|
["recurso", "model-conheca", "Conheça o SagaDeck", "Um slide por recurso, com a frase para pedir cada um à IA", ["#F2F0EA", "#1A1A1A", "#F2B33D"]],
|
|
285
|
-
["recurso", "model-explorar", "Explore uma ideia", "Prever, comparar cenários, curvas conectadas e acompanhar o mesmo objeto entre cenas", ["#F4F1EA", "#1B1F3B", "#E8590C"]],
|
|
286
|
-
["recurso", "model-novidades", "Novidades", "Carrossel, gráfico do Excel e da planilha do projeto, fórmulas vivas, duelo de commits, terminais e zoom", ["#F4F1EA", "#1B1F3B", "#E8590C"]],
|
|
287
313
|
["recurso", "model-hidraulica", "Aula de hidráulica", "Navier–Stokes, curva que se mexe, calculadora de Reynolds ao vivo e exercício resolvido passo a passo", ["#EAF4F8", "#0B5C7A", "#1B998B"]],
|
|
288
314
|
["recurso", "model-algoritmos", "Aula de algoritmos", "Ordenação e busca rodando de verdade: barras, código, contadores e o botão Tocar", ["#0F1426", "#7C8CFF", "#F2B33D"]],
|
|
289
|
-
["recurso", "model-compacto", "Material de consulta", "Código completo, JSON e páginas compactas", ["#F7F6F2", "#20242B", "#0F6CBD"]],
|
|
290
|
-
["recurso", "model-avancado", "Recursos avançados", "Conteúdo denso, avisos, diagramas e tipografia cinética", ["#F7F6F2", "#20242B", "#E8590C"]],
|
|
291
315
|
["recurso", "model-diagramas", "Diagramas vivos", "Fluxo, sequência, estados e mapa mental", ["#F4F7FB", "#1F6FB2", "#5DBB86"]],
|
|
292
|
-
["recurso", "example-api", "Aula de APIs ao vivo", "Slides que executam requisições de verdade. Roda sem configurar nada", ["#111B2B", "#5B8DEF", "#ECF1FF"]],
|
|
293
|
-
["recurso", "example-cenario", "Texto no cenário", "As 13 composições: fundo, transparência, recorte na frente das letras", ["#0A0D17", "#00F2FE", "#FF007A"]],
|
|
294
316
|
];
|
|
295
317
|
const GALLERY_KINDS = [["", "Todos"], ["visual", "Visuais para começar"], ["recurso", "Recursos do SagaDeck"]];
|
|
296
318
|
async function galleryDialog(topic, kind = "") {
|
|
@@ -370,7 +392,7 @@
|
|
|
370
392
|
let reading = 0;
|
|
371
393
|
dialog(`<h3 id="dialog-heading">Nova apresentação com IA</h3>
|
|
372
394
|
<label for="dlg-brief">Sobre o que é, para quem e com que objetivo? <span class="muted">Ou só anexe o material abaixo.</span></label>
|
|
373
|
-
<textarea id="dlg-brief" placeholder="Ex.: palestra para gestores sobre
|
|
395
|
+
<textarea id="dlg-brief" placeholder="Ex.: palestra para gestores sobre trabalho híbrido, tom leve, começando com um caso real. Ou: resumo deste relatório para a diretoria, focando nos números do ano."></textarea>
|
|
374
396
|
<div class="dlg-two">
|
|
375
397
|
<div><label for="dlg-min">Quanto tempo você tem?</label><div class="dlg-min"><input type="number" id="dlg-min" min="1" max="120" value="10"><span>min</span><span class="hint" id="dlg-slides"></span></div></div>
|
|
376
398
|
<div><label for="dlg-style">Estilo</label><select id="dlg-style"><option value="">Automático (a IA escolhe pelo assunto)</option>${STYLES.map(([v, l]) => `<option value="${v}">${esc(l)}</option>`).join("")}</select></div>
|
|
@@ -121,7 +121,7 @@ window.SagaMapData = (() => {
|
|
|
121
121
|
// linha selecionada na planilha -> cartão do ponto no mapa da prévia
|
|
122
122
|
const selectRow = (path, linha) => document.dispatchEvent(new CustomEvent("sagamap:linha", { detail: { path, linha } }));
|
|
123
123
|
|
|
124
|
-
// botão "Editar
|
|
124
|
+
// botão "Editar mapa" na prévia grande; a tela do editor (map-editor.js) só carrega no primeiro clique
|
|
125
125
|
let editorLoad;
|
|
126
126
|
const loadEditor = () => (window.SagaMapEditor ? Promise.resolve() : (editorLoad ||= new Promise((res, rej) => {
|
|
127
127
|
const s = document.createElement("script"); s.src = "map-editor.js"; s.onload = res;
|
|
@@ -137,12 +137,16 @@ window.SagaMapData = (() => {
|
|
|
137
137
|
const host = document.getElementById("canvas-viewport") || box;
|
|
138
138
|
host.querySelectorAll(":scope > .me-open").forEach((b) => b.remove());
|
|
139
139
|
const btn = document.createElement("button");
|
|
140
|
-
btn.type = "button"; btn.className = "me-open"; btn.innerHTML = '<i class="ic" data-ic="pencil"></i> Editar
|
|
140
|
+
btn.type = "button"; btn.className = "me-open"; btn.innerHTML = '<i class="ic" data-ic="pencil"></i> Editar mapa';
|
|
141
|
+
btn.title = "Abre a tela do mapa: camadas, pontos, linhas, áreas e as propriedades de cada um (ou dê dois cliques no mapa)";
|
|
141
142
|
for (const ev of ["pointerdown", "mousedown", "click", "dblclick"]) btn.addEventListener(ev, (e) => e.stopPropagation());
|
|
142
|
-
|
|
143
|
+
const openEditor = async () => {
|
|
143
144
|
try { await loadEditor(); btn.hidden = true; window.SagaMapEditor.start(box, slide, commit, ctx); }
|
|
144
145
|
catch (e) { ctx.toast(e.message, 5000); }
|
|
145
146
|
};
|
|
147
|
+
btn.onclick = openEditor;
|
|
148
|
+
// dois cliques no mapa da prévia também abrem a tela (o mapa pequeno, escalado, não serve para editar)
|
|
149
|
+
box.addEventListener("dblclick", (e) => { e.stopPropagation(); e.preventDefault(); openEditor(); });
|
|
146
150
|
host.append(btn);
|
|
147
151
|
ctx.hydrate?.(btn);
|
|
148
152
|
// o slide redesenhou com o editor aberto (gravou algo): volta no mesmo modo
|