yumiamd 0.1.19 → 0.1.21

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.
@@ -143,6 +143,21 @@ function createColumns(columns, ratios, gap) {
143
143
  ...gap !== void 0 ? { gap } : {}
144
144
  };
145
145
  }
146
+ function createSection(title, subtitle, number) {
147
+ return {
148
+ type: "section",
149
+ title,
150
+ ...subtitle ? { subtitle } : {},
151
+ ...number !== void 0 ? { number } : {}
152
+ };
153
+ }
154
+ function createToc(title, items) {
155
+ return {
156
+ type: "toc",
157
+ ...title ? { title } : {},
158
+ ...items ? { items } : {}
159
+ };
160
+ }
146
161
  function createMath(expression, displayMode = true) {
147
162
  return {
148
163
  type: "math",
@@ -157,8 +172,75 @@ function createLayoutDirective(mode, attributes) {
157
172
  ...attributes ? { attributes } : {}
158
173
  };
159
174
  }
175
+ function createIcon(name, provider, size, color) {
176
+ return {
177
+ type: "icon",
178
+ name,
179
+ ...provider ? { provider } : {},
180
+ ...size !== void 0 ? { size } : {},
181
+ ...color ? { color } : {}
182
+ };
183
+ }
184
+ function createGrid(elements, columns = 2, gap) {
185
+ return {
186
+ type: "grid",
187
+ columns,
188
+ elements,
189
+ ...gap !== void 0 ? { gap } : {}
190
+ };
191
+ }
192
+ function createStack(elements, direction = "vertical", gap, align, justify) {
193
+ return {
194
+ type: "stack",
195
+ direction,
196
+ elements,
197
+ ...gap !== void 0 ? { gap } : {},
198
+ ...align ? { align } : {},
199
+ ...justify ? { justify } : {}
200
+ };
201
+ }
202
+ function createComponent(name, props, elements) {
203
+ return {
204
+ type: "component",
205
+ name,
206
+ ...props ? { props } : {},
207
+ ...elements ? { elements } : {}
208
+ };
209
+ }
210
+ function createSlot(name, elements) {
211
+ return {
212
+ type: "slot",
213
+ ...name ? { name } : {},
214
+ ...elements ? { elements } : {}
215
+ };
216
+ }
160
217
 
161
218
  // ../parser/dist/parser.js
219
+ function parseHighlightLines(highlightStr) {
220
+ if (!highlightStr)
221
+ return [];
222
+ const lines = /* @__PURE__ */ new Set();
223
+ const parts = highlightStr.split(",");
224
+ for (const part of parts) {
225
+ const trimmed = part.trim();
226
+ if (!trimmed)
227
+ continue;
228
+ if (trimmed.includes("-")) {
229
+ const [startStr, endStr] = trimmed.split("-");
230
+ const start = parseInt(startStr || "0", 10);
231
+ const end = parseInt(endStr || "0", 10);
232
+ if (!isNaN(start) && !isNaN(end) && start <= end) {
233
+ for (let n = start; n <= end; n++)
234
+ lines.add(n);
235
+ }
236
+ } else {
237
+ const num = parseInt(trimmed, 10);
238
+ if (!isNaN(num))
239
+ lines.add(num);
240
+ }
241
+ }
242
+ return Array.from(lines).sort((a, b) => a - b);
243
+ }
162
244
  var DefaultYumiaParser = class {
163
245
  diagnostics = [];
164
246
  parse(source, options) {
@@ -166,6 +248,11 @@ var DefaultYumiaParser = class {
166
248
  if (!source || typeof source !== "string") {
167
249
  return createPresentation({}, []);
168
250
  }
251
+ const trimmed = source.trimStart();
252
+ if (trimmed.startsWith("document ") || trimmed.startsWith("document\n") || trimmed.startsWith("slide ") || trimmed.startsWith("slide\n")) {
253
+ const nativeParser = new NativeYumiaParser();
254
+ return nativeParser.parse(source);
255
+ }
169
256
  const normalized = source.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
170
257
  const { metadata, content, lineOffset } = this.extractFrontmatter(normalized);
171
258
  if (!content.trim()) {
@@ -234,16 +321,34 @@ var DefaultYumiaParser = class {
234
321
  parseYamlMetadata(block) {
235
322
  const metadata = {};
236
323
  const lines = block.split("\n");
324
+ let currentKey = null;
237
325
  for (const line of lines) {
238
326
  const trimmed = line.trim();
239
327
  if (!trimmed || trimmed.startsWith("#"))
240
328
  continue;
329
+ if (trimmed.startsWith("- ") && currentKey) {
330
+ const itemVal = trimmed.slice(2).trim().replace(/^['"](.*)['"]$/, "$1");
331
+ if (Array.isArray(metadata[currentKey])) {
332
+ metadata[currentKey].push(itemVal);
333
+ } else {
334
+ metadata[currentKey] = [itemVal];
335
+ }
336
+ continue;
337
+ }
241
338
  const colonIndex = trimmed.indexOf(":");
242
339
  if (colonIndex > 0) {
243
340
  const key = trimmed.slice(0, colonIndex).trim();
244
341
  const rawVal = trimmed.slice(colonIndex + 1).trim();
245
- const unquoted = rawVal.replace(/^['"](.*)['"]$/, "$1");
246
- metadata[key] = unquoted;
342
+ currentKey = key;
343
+ if (rawVal.startsWith("[") && rawVal.endsWith("]")) {
344
+ const listItems = rawVal.slice(1, -1).split(",").map((item) => item.trim().replace(/^['"](.*)['"]$/, "$1")).filter(Boolean);
345
+ metadata[key] = listItems;
346
+ } else if (rawVal) {
347
+ const unquoted = rawVal.replace(/^['"](.*)['"]$/, "$1");
348
+ metadata[key] = unquoted;
349
+ } else {
350
+ metadata[key] = [];
351
+ }
247
352
  }
248
353
  }
249
354
  const result = {};
@@ -268,6 +373,23 @@ var DefaultYumiaParser = class {
268
373
  } else if (typeof metadata["embedFonts"] === "boolean") {
269
374
  result.embedFonts = metadata["embedFonts"];
270
375
  }
376
+ if (typeof metadata["watermark"] === "string") {
377
+ if (metadata["watermark"] === "false" || metadata["watermark"] === "none" || metadata["watermark"] === "off") {
378
+ result.watermark = false;
379
+ } else if (metadata["watermark"] === "true" || metadata["watermark"] === "yes" || metadata["watermark"] === "on") {
380
+ result.watermark = true;
381
+ } else {
382
+ result.watermark = metadata["watermark"];
383
+ }
384
+ } else if (typeof metadata["watermark"] === "boolean") {
385
+ result.watermark = metadata["watermark"];
386
+ }
387
+ if (metadata["styles"] || metadata["stylesheets"] || metadata["css"]) {
388
+ result.styles = metadata["styles"] || metadata["stylesheets"] || metadata["css"];
389
+ }
390
+ if (metadata["scripts"] || metadata["js"]) {
391
+ result.scripts = metadata["scripts"] || metadata["js"];
392
+ }
271
393
  const colors = {};
272
394
  if (typeof metadata["background"] === "string")
273
395
  colors.background = metadata["background"];
@@ -386,7 +508,16 @@ var DefaultYumiaParser = class {
386
508
  }
387
509
  if (line.startsWith("```")) {
388
510
  flushAll();
389
- const language = line.slice(3).trim() || void 0;
511
+ let rawLang = line.slice(3).trim() || void 0;
512
+ let highlight;
513
+ if (rawLang) {
514
+ const hlMatch = rawLang.match(/\{([\d,\s-]+)\}/) || rawLang.match(/highlight=['"]([\d,\s-]+)['"]/);
515
+ if (hlMatch) {
516
+ highlight = hlMatch[1]?.trim();
517
+ rawLang = rawLang.replace(hlMatch[0], "").trim() || void 0;
518
+ }
519
+ }
520
+ const language = rawLang;
390
521
  const codeLines = [];
391
522
  const codeStartLine = currentLineNum;
392
523
  i++;
@@ -413,6 +544,10 @@ var DefaultYumiaParser = class {
413
544
  });
414
545
  }
415
546
  const el = createCode(codeLines.join("\n"), language);
547
+ if (highlight) {
548
+ el.highlight = highlight;
549
+ el.highlightLines = parseHighlightLines(highlight);
550
+ }
416
551
  el.loc = {
417
552
  start: { line: codeStartLine, column: 1 },
418
553
  end: { line: baseLine + i - 1, column: 1 }
@@ -462,7 +597,7 @@ var DefaultYumiaParser = class {
462
597
  const variantMatch = directiveHeader.match(/variant=['"](.*?)['"]/);
463
598
  const descMatch = directiveHeader.match(/description=['"](.*?)['"]/);
464
599
  const unitMatch = directiveHeader.match(/unit=['"](.*?)['"]/);
465
- const changeMatch = directiveHeader.match(/change=['"](.*?)['"]/);
600
+ const changeMatch = directiveHeader.match(/(?:change|diff)=['"](.*?)['"]/);
466
601
  const value = valueMatch ? valueMatch[1] : "0";
467
602
  const label = labelMatch ? labelMatch[1] : "";
468
603
  const variant = variantMatch ? variantMatch[1] : void 0;
@@ -606,6 +741,82 @@ var DefaultYumiaParser = class {
606
741
  end: { line: baseLine + i - 1, column: 1 }
607
742
  };
608
743
  elements.push(el);
744
+ } else if (directiveName === "code") {
745
+ const langMatch = directiveArg.match(/lang(?:uage)?=['"](.*?)['"]/);
746
+ const hlMatch = directiveArg.match(/highlight=['"](.*?)['"]/);
747
+ let lang = langMatch ? langMatch[1] : directiveArg.split(" ")[0] || void 0;
748
+ if (lang && (lang.includes("=") || lang.startsWith("{")))
749
+ lang = void 0;
750
+ const hlBraceMatch = directiveArg.match(/\{([\d,\s-]+)\}/);
751
+ const highlight = hlMatch ? hlMatch[1] : hlBraceMatch ? hlBraceMatch[1] : void 0;
752
+ const codeContent = blockLines.join("\n");
753
+ const el = createCode(codeContent, lang);
754
+ if (highlight) {
755
+ el.highlight = highlight;
756
+ el.highlightLines = parseHighlightLines(highlight);
757
+ }
758
+ el.loc = {
759
+ start: { line: directiveStartLine, column: 1 },
760
+ end: { line: baseLine + i - 1, column: 1 }
761
+ };
762
+ elements.push(el);
763
+ } else if (directiveName === "section") {
764
+ const allSectionText = [directiveArg, ...blockLines].join(" ");
765
+ const titleMatch = allSectionText.match(/\btitle=(?:"([^"]*)"|'([^']*)'|(\S+))/);
766
+ const subtitleMatch = allSectionText.match(/\bsubtitle=(?:"([^"]*)"|'([^']*)'|(\S+))/);
767
+ const numberMatch = allSectionText.match(/\bnumber=(?:"([^"]*)"|'([^']*)'|(\S+))/);
768
+ let subtitle = subtitleMatch ? subtitleMatch[1] ?? subtitleMatch[2] ?? subtitleMatch[3] : void 0;
769
+ const number = numberMatch ? numberMatch[1] ?? numberMatch[2] ?? numberMatch[3] : void 0;
770
+ let title = titleMatch ? titleMatch[1] ?? titleMatch[2] ?? titleMatch[3] : void 0;
771
+ if (!title) {
772
+ const stripped = directiveArg.replace(/\bsubtitle=(?:"[^"]*"|'[^']*'|\S+)/g, "").replace(/\bnumber=(?:"[^"]*"|'[^']*'|\S+)/g, "").trim();
773
+ title = stripped.replace(/^['"](.*)['"]$/, "$1").trim() || void 0;
774
+ }
775
+ if (!title && blockLines.length > 0) {
776
+ const plainLines = blockLines.filter((l) => !l.includes("subtitle=") && !l.includes("number="));
777
+ if (plainLines.length > 0)
778
+ title = plainLines[0]?.trim();
779
+ if (!subtitle && plainLines.length > 1)
780
+ subtitle = plainLines.slice(1).join("\n").trim();
781
+ }
782
+ const el = createSection(title || "Section", subtitle, number);
783
+ el.loc = {
784
+ start: { line: directiveStartLine, column: 1 },
785
+ end: { line: baseLine + i - 1, column: 1 }
786
+ };
787
+ elements.push(el);
788
+ } else if (directiveName === "toc") {
789
+ const titleMatch = directiveArg.match(/\btitle=(?:"([^"]*)"|'([^']*)'|(\S+))/);
790
+ let title = titleMatch ? titleMatch[1] ?? titleMatch[2] ?? titleMatch[3] : void 0;
791
+ if (!title) {
792
+ title = directiveArg.replace(/^['"](.*)['"]$/, "$1").trim() || void 0;
793
+ }
794
+ const items = [];
795
+ for (const bl of blockLines) {
796
+ const trimmed = bl.trim();
797
+ if (!trimmed)
798
+ continue;
799
+ const matchOrdered = trimmed.match(/^(\d+)\.\s+(.*?)(?:\s+[-–—:]\s+(.*))?$/);
800
+ const matchUnordered = trimmed.match(/^[-*]\s+(.*?)(?:\s+[-–—:]\s+(.*))?$/);
801
+ if (matchOrdered && matchOrdered[1] && matchOrdered[2]) {
802
+ items.push({
803
+ number: matchOrdered[1],
804
+ title: matchOrdered[2].trim(),
805
+ description: matchOrdered[3]?.trim() || void 0
806
+ });
807
+ } else if (matchUnordered && matchUnordered[1]) {
808
+ items.push({
809
+ title: matchUnordered[1].trim(),
810
+ description: matchUnordered[2]?.trim() || void 0
811
+ });
812
+ }
813
+ }
814
+ const el = createToc(title, items.length > 0 ? items : void 0);
815
+ el.loc = {
816
+ start: { line: directiveStartLine, column: 1 },
817
+ end: { line: baseLine + i - 1, column: 1 }
818
+ };
819
+ elements.push(el);
609
820
  } else if (directiveName === "step") {
610
821
  const { elements: stepElements } = this.parseLines(blockLines, blockBaseLine);
611
822
  for (const sEl of stepElements) {
@@ -620,6 +831,39 @@ var DefaultYumiaParser = class {
620
831
  end: { line: baseLine + i - 1, column: 1 }
621
832
  };
622
833
  elements.push(el);
834
+ } else if (directiveName === "icon") {
835
+ const nameMatch = directiveArg.match(/^(?:name=)?["']?([^"'\s]+)["']?/);
836
+ const name = nameMatch ? nameMatch[1] : directiveArg.trim();
837
+ const sizeMatch = directiveArg.match(/\bsize=["']?([^"'\s]+)["']?/);
838
+ const colorMatch = directiveArg.match(/\bcolor=["']?([^"'\s]+)["']?/);
839
+ const el = createIcon(name, void 0, sizeMatch ? sizeMatch[1] : void 0, colorMatch ? colorMatch[1] : void 0);
840
+ el.loc = {
841
+ start: { line: directiveStartLine, column: 1 },
842
+ end: { line: baseLine + i - 1, column: 1 }
843
+ };
844
+ elements.push(el);
845
+ } else if (directiveName === "grid") {
846
+ const colsMatch = directiveArg.match(/\bcolumns=["']?([^"'\s]+)["']?/);
847
+ const gapMatch = directiveArg.match(/\bgap=["']?([^"'\s]+)["']?/);
848
+ const columns = colsMatch ? parseInt(colsMatch[1], 10) || colsMatch[1] : 2;
849
+ const { elements: gridChildren } = this.parseLines(blockLines, blockBaseLine);
850
+ const el = createGrid(gridChildren, columns, gapMatch ? gapMatch[1] : void 0);
851
+ el.loc = {
852
+ start: { line: directiveStartLine, column: 1 },
853
+ end: { line: baseLine + i - 1, column: 1 }
854
+ };
855
+ elements.push(el);
856
+ } else if (directiveName === "stack") {
857
+ const dirMatch = directiveArg.match(/\bdirection=["']?(horizontal|vertical)["']?/);
858
+ const direction = dirMatch ? dirMatch[1] : "vertical";
859
+ const gapMatch = directiveArg.match(/\bgap=["']?([^"'\s]+)["']?/);
860
+ const { elements: stackChildren } = this.parseLines(blockLines, blockBaseLine);
861
+ const el = createStack(stackChildren, direction, gapMatch ? gapMatch[1] : void 0);
862
+ el.loc = {
863
+ start: { line: directiveStartLine, column: 1 },
864
+ end: { line: baseLine + i - 1, column: 1 }
865
+ };
866
+ elements.push(el);
623
867
  }
624
868
  continue;
625
869
  }
@@ -837,6 +1081,7 @@ var DefaultYumiaParser = class {
837
1081
  series.push({ name: title || "Data", values });
838
1082
  }
839
1083
  }
1084
+ const singleSeriesValues = [];
840
1085
  for (const line of blockLines) {
841
1086
  const trimmed = line.trim();
842
1087
  if (!trimmed || trimmed.startsWith("#"))
@@ -858,13 +1103,28 @@ var DefaultYumiaParser = class {
858
1103
  if (l && valStr) {
859
1104
  labels.push(l);
860
1105
  const val = parseFloat(valStr) || 0;
861
- if (series.length === 0)
862
- series.push({ name: "Data", values: [] });
863
- series[0]?.values.push(val);
1106
+ singleSeriesValues.push(val);
864
1107
  }
865
1108
  }
1109
+ } else {
1110
+ const kvMatch = trimmed.match(/^[-*]?\s*(.*?)\s*[:=]\s*([+-]?\d+(?:\.\d+)?%?)$/);
1111
+ if (kvMatch && kvMatch[1] && kvMatch[2]) {
1112
+ labels.push(kvMatch[1].trim());
1113
+ const val = parseFloat(kvMatch[2].replace("%", "")) || 0;
1114
+ singleSeriesValues.push(val);
1115
+ }
866
1116
  }
867
1117
  }
1118
+ if (singleSeriesValues.length > 0) {
1119
+ if (series.length === 0) {
1120
+ series.push({ name: title || "Data", values: singleSeriesValues });
1121
+ } else if (series[0]) {
1122
+ series[0].values.push(...singleSeriesValues);
1123
+ }
1124
+ }
1125
+ if (series.length === 0) {
1126
+ series.push({ name: title || "Data", values: [] });
1127
+ }
868
1128
  if (labels.length === 0) {
869
1129
  const maxLen = Math.max(0, ...series.map((s) => s.values.length));
870
1130
  labels = Array.from({ length: maxLen }, (_, i) => `Item ${i + 1}`);
@@ -910,6 +1170,13 @@ var DefaultYumiaParser = class {
910
1170
  const rightMatch = headerArg.match(/right=['"](.*?)['"]/);
911
1171
  const leftTitle = leftMatch ? leftMatch[1] : void 0;
912
1172
  const rightTitle = rightMatch ? rightMatch[1] : void 0;
1173
+ const hasColumns = blockLines.some((l) => l.trim().startsWith(":::column"));
1174
+ if (hasColumns) {
1175
+ const cols = this.parseColumnsBlock(blockLines, baseLine);
1176
+ const leftElements2 = cols[0]?.elements || [];
1177
+ const rightElements2 = cols[1]?.elements || [];
1178
+ return createCompare(leftElements2, rightElements2, leftTitle, rightTitle);
1179
+ }
913
1180
  let leftLines = [];
914
1181
  let rightLines = [];
915
1182
  let currentSide = "left";
@@ -944,6 +1211,449 @@ function parseYumia(source, options) {
944
1211
  return parser.parse(source, options);
945
1212
  }
946
1213
 
1214
+ // ../parser/dist/native-parser.js
1215
+ var NativeYumiaParser = class {
1216
+ parse(source) {
1217
+ const lines = source.split(/\r?\n/);
1218
+ const tokens = [];
1219
+ for (let i = 0; i < lines.length; i++) {
1220
+ const raw = lines[i];
1221
+ const matchIndent = raw.match(/^(\s*)/);
1222
+ const indent = matchIndent ? matchIndent[1].length : 0;
1223
+ const text = raw.trim();
1224
+ if (!text || text.startsWith("//") || text.startsWith("#"))
1225
+ continue;
1226
+ const spaceIdx = text.indexOf(" ");
1227
+ const command = spaceIdx === -1 ? text : text.slice(0, spaceIdx);
1228
+ const args = spaceIdx === -1 ? "" : text.slice(spaceIdx + 1).trim();
1229
+ tokens.push({
1230
+ indent,
1231
+ lineNum: i + 1,
1232
+ text,
1233
+ command: command.toLowerCase(),
1234
+ args
1235
+ });
1236
+ }
1237
+ const metadata = {};
1238
+ const slides = [];
1239
+ let currentSlideElements = [];
1240
+ let currentSlide = null;
1241
+ const flushSlide = () => {
1242
+ if (currentSlide) {
1243
+ currentSlide.elements = [...currentSlideElements];
1244
+ slides.push(currentSlide);
1245
+ currentSlideElements = [];
1246
+ currentSlide = null;
1247
+ }
1248
+ };
1249
+ let idx = 0;
1250
+ while (idx < tokens.length) {
1251
+ const tok = tokens[idx];
1252
+ const cmdLower = tok.command.toLowerCase();
1253
+ if (cmdLower === "document" || cmdLower === "title") {
1254
+ metadata.title = this.stripQuotes(tok.args);
1255
+ idx++;
1256
+ continue;
1257
+ }
1258
+ if (cmdLower === "theme") {
1259
+ metadata.theme = this.stripQuotes(tok.args);
1260
+ idx++;
1261
+ continue;
1262
+ }
1263
+ if (cmdLower === "aspectratio" || cmdLower === "ratio") {
1264
+ metadata.aspectRatio = this.stripQuotes(tok.args);
1265
+ idx++;
1266
+ continue;
1267
+ }
1268
+ if (cmdLower === "transition") {
1269
+ metadata.transition = this.stripQuotes(tok.args);
1270
+ idx++;
1271
+ continue;
1272
+ }
1273
+ if (cmdLower === "watermark") {
1274
+ metadata.watermark = this.stripQuotes(tok.args);
1275
+ idx++;
1276
+ continue;
1277
+ }
1278
+ if (cmdLower === "author") {
1279
+ metadata.author = this.stripQuotes(tok.args);
1280
+ idx++;
1281
+ continue;
1282
+ }
1283
+ if (tok.command === "slide" || tok.command === "---") {
1284
+ flushSlide();
1285
+ const slideTitle = tok.command === "slide" ? this.stripQuotes(tok.args) : void 0;
1286
+ currentSlide = createSlide([], {
1287
+ loc: {
1288
+ start: { line: tok.lineNum, column: 1 },
1289
+ end: { line: tok.lineNum, column: tok.text.length }
1290
+ }
1291
+ });
1292
+ if (slideTitle) {
1293
+ currentSlideElements.push(createHeading(slideTitle, 1));
1294
+ }
1295
+ idx++;
1296
+ continue;
1297
+ }
1298
+ if (!currentSlide) {
1299
+ currentSlide = createSlide([], {
1300
+ loc: {
1301
+ start: { line: tok.lineNum, column: 1 },
1302
+ end: { line: tok.lineNum, column: tok.text.length }
1303
+ }
1304
+ });
1305
+ }
1306
+ if (tok.command === "notes") {
1307
+ const noteLines = [];
1308
+ idx++;
1309
+ while (idx < tokens.length && tokens[idx].indent > tok.indent) {
1310
+ noteLines.push(tokens[idx].text);
1311
+ idx++;
1312
+ }
1313
+ currentSlide.notes = (currentSlide.notes ? currentSlide.notes + "\n" : "") + noteLines.join("\n");
1314
+ continue;
1315
+ }
1316
+ const parsedEl = this.parseElement(tokens, idx);
1317
+ if (parsedEl) {
1318
+ currentSlideElements.push(parsedEl.element);
1319
+ idx = parsedEl.nextIdx;
1320
+ } else {
1321
+ idx++;
1322
+ }
1323
+ }
1324
+ flushSlide();
1325
+ if (slides.length === 0) {
1326
+ slides.push(createSlide([createHeading(metadata.title || "Untitled Presentation", 1)]));
1327
+ }
1328
+ return createPresentation(metadata, slides);
1329
+ }
1330
+ parseElement(tokens, idx) {
1331
+ const tok = tokens[idx];
1332
+ const baseIndent = tok.indent;
1333
+ switch (tok.command) {
1334
+ case "heading":
1335
+ case "h1":
1336
+ case "h2":
1337
+ case "h3": {
1338
+ const level = tok.command === "h1" ? 1 : tok.command === "h2" ? 2 : tok.command === "h3" ? 3 : 1;
1339
+ const text = this.stripQuotes(tok.args);
1340
+ return { element: createHeading(text, level), nextIdx: idx + 1 };
1341
+ }
1342
+ case "paragraph":
1343
+ case "text":
1344
+ case "p": {
1345
+ let text = this.stripQuotes(tok.args);
1346
+ let nextIdx = idx + 1;
1347
+ if (!text) {
1348
+ const pLines = [];
1349
+ while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1350
+ pLines.push(tokens[nextIdx].text);
1351
+ nextIdx++;
1352
+ }
1353
+ text = pLines.join(" ");
1354
+ }
1355
+ return { element: createParagraph(text), nextIdx };
1356
+ }
1357
+ case "icon": {
1358
+ const nameMatch = tok.args.match(/^(?:name=)?["']?([^"'\s]+)["']?/);
1359
+ const name = nameMatch ? nameMatch[1] : tok.args.trim();
1360
+ const sizeMatch = tok.args.match(/\bsize=["']?([^"'\s]+)["']?/);
1361
+ const colorMatch = tok.args.match(/\bcolor=["']?([^"'\s]+)["']?/);
1362
+ return {
1363
+ element: createIcon(name, void 0, sizeMatch ? sizeMatch[1] : void 0, colorMatch ? colorMatch[1] : void 0),
1364
+ nextIdx: idx + 1
1365
+ };
1366
+ }
1367
+ case "card": {
1368
+ const titleMatch = tok.args.match(/title=["']([^"']+)["']/);
1369
+ const variantMatch = tok.args.match(/variant=["']?([^"'\s]+)["']?/);
1370
+ const title = titleMatch ? titleMatch[1] : this.stripQuotes(tok.args) || void 0;
1371
+ const variant = variantMatch ? variantMatch[1] : "default";
1372
+ const children = [];
1373
+ let nextIdx = idx + 1;
1374
+ while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1375
+ const childRes = this.parseElement(tokens, nextIdx);
1376
+ if (childRes) {
1377
+ children.push(childRes.element);
1378
+ nextIdx = childRes.nextIdx;
1379
+ } else {
1380
+ nextIdx++;
1381
+ }
1382
+ }
1383
+ return { element: createCard(children, title, variant), nextIdx };
1384
+ }
1385
+ case "columns": {
1386
+ const ratios = tok.args || "50:50";
1387
+ const cols = [];
1388
+ let nextIdx = idx + 1;
1389
+ while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1390
+ const childRes = this.parseElement(tokens, nextIdx);
1391
+ if (childRes) {
1392
+ cols.push(childRes.element);
1393
+ nextIdx = childRes.nextIdx;
1394
+ } else {
1395
+ nextIdx++;
1396
+ }
1397
+ }
1398
+ const colElements = cols.map((c) => c.type === "column" ? c : createColumn([c]));
1399
+ return { element: createColumns(colElements, ratios), nextIdx };
1400
+ }
1401
+ case "grid": {
1402
+ const colsMatch = tok.args.match(/columns=["']?([^"'\s]+)["']?/);
1403
+ const gapMatch = tok.args.match(/gap=["']?([^"'\s]+)["']?/);
1404
+ const columns = colsMatch ? parseInt(colsMatch[1], 10) || colsMatch[1] : 2;
1405
+ const gap = gapMatch ? gapMatch[1] : void 0;
1406
+ const children = [];
1407
+ let nextIdx = idx + 1;
1408
+ while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1409
+ const childRes = this.parseElement(tokens, nextIdx);
1410
+ if (childRes) {
1411
+ children.push(childRes.element);
1412
+ nextIdx = childRes.nextIdx;
1413
+ } else {
1414
+ nextIdx++;
1415
+ }
1416
+ }
1417
+ return { element: createGrid(children, columns, gap), nextIdx };
1418
+ }
1419
+ case "stack": {
1420
+ const dirMatch = tok.args.match(/direction=["']?(horizontal|vertical)["']?/);
1421
+ const direction = dirMatch ? dirMatch[1] : "vertical";
1422
+ const gapMatch = tok.args.match(/gap=["']?([^"'\s]+)["']?/);
1423
+ const children = [];
1424
+ let nextIdx = idx + 1;
1425
+ while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1426
+ const childRes = this.parseElement(tokens, nextIdx);
1427
+ if (childRes) {
1428
+ children.push(childRes.element);
1429
+ nextIdx = childRes.nextIdx;
1430
+ } else {
1431
+ nextIdx++;
1432
+ }
1433
+ }
1434
+ return {
1435
+ element: createStack(children, direction, gapMatch ? gapMatch[1] : void 0),
1436
+ nextIdx
1437
+ };
1438
+ }
1439
+ case "metric": {
1440
+ const valMatch = tok.args.match(/^(?:value=)?["']([^"']+)["']/);
1441
+ const labelMatch = tok.args.match(/\blabel=["']([^"']+)["']/);
1442
+ const diffMatch = tok.args.match(/\bdiff=["']([^"']+)["']/);
1443
+ const variantMatch = tok.args.match(/\bvariant=["']?([^"'\s]+)["']?/);
1444
+ const value = valMatch ? valMatch[1] : "0";
1445
+ const label = labelMatch ? labelMatch[1] : "Metric";
1446
+ return {
1447
+ element: createMetric(value, label, variantMatch ? variantMatch[1] : "primary", void 0, void 0, diffMatch ? diffMatch[1] : void 0),
1448
+ nextIdx: idx + 1
1449
+ };
1450
+ }
1451
+ case "code": {
1452
+ const langMatch = tok.args.match(/\blang(?:uage)?=["']?([^"'\s]+)["']?/);
1453
+ const hlMatch = tok.args.match(/\bhighlight=["']([^"']+)["']/);
1454
+ const codeLines = [];
1455
+ let nextIdx = idx + 1;
1456
+ while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1457
+ codeLines.push(tokens[nextIdx].text);
1458
+ nextIdx++;
1459
+ }
1460
+ const el = createCode(codeLines.join("\n"), langMatch ? langMatch[1] : void 0);
1461
+ if (hlMatch && hlMatch[1]) {
1462
+ el.highlight = hlMatch[1];
1463
+ el.highlightLines = parseHighlightLines(hlMatch[1]);
1464
+ }
1465
+ return { element: el, nextIdx };
1466
+ }
1467
+ case "section": {
1468
+ const titleMatch = tok.args.match(/^(?:title=)?["']([^"']+)["']/);
1469
+ const subtitleMatch = tok.args.match(/\bsubtitle=["']([^"']+)["']/);
1470
+ const numMatch = tok.args.match(/\bnumber=["']?([^"'\s]+)["']?/);
1471
+ const title = titleMatch ? titleMatch[1] : this.stripQuotes(tok.args) || "Section";
1472
+ return {
1473
+ element: createSection(title, subtitleMatch ? subtitleMatch[1] : void 0, numMatch ? numMatch[1] : void 0),
1474
+ nextIdx: idx + 1
1475
+ };
1476
+ }
1477
+ case "toc": {
1478
+ const title = this.stripQuotes(tok.args) || "Table of Contents";
1479
+ return { element: createToc(title), nextIdx: idx + 1 };
1480
+ }
1481
+ case "badge": {
1482
+ const textMatch = tok.args.match(/^(?:text=)?["']([^"']+)["']/);
1483
+ const variantMatch = tok.args.match(/\bvariant=["']?([^"'\s]+)["']?/);
1484
+ const text = textMatch ? textMatch[1] : this.stripQuotes(tok.args);
1485
+ return {
1486
+ element: createBadge(text, variantMatch ? variantMatch[1] : "default"),
1487
+ nextIdx: idx + 1
1488
+ };
1489
+ }
1490
+ case "list": {
1491
+ const items = [];
1492
+ let nextIdx = idx + 1;
1493
+ while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1494
+ const sub = tokens[nextIdx];
1495
+ if (sub.command === "item" || sub.command === "-") {
1496
+ items.push({ text: this.stripQuotes(sub.args) });
1497
+ } else {
1498
+ items.push({ text: sub.text.replace(/^[-*]\s*/, "") });
1499
+ }
1500
+ nextIdx++;
1501
+ }
1502
+ return { element: createList(items, false), nextIdx };
1503
+ }
1504
+ case "quote": {
1505
+ const authorMatch = tok.args.match(/\bauthor=["']([^"']+)["']/);
1506
+ const textLines = [];
1507
+ let nextIdx = idx + 1;
1508
+ while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1509
+ textLines.push(tokens[nextIdx].text);
1510
+ nextIdx++;
1511
+ }
1512
+ return {
1513
+ element: createQuote(textLines.join(" "), authorMatch ? authorMatch[1] : void 0),
1514
+ nextIdx
1515
+ };
1516
+ }
1517
+ default:
1518
+ return { element: createParagraph(tok.text), nextIdx: idx + 1 };
1519
+ }
1520
+ }
1521
+ stripQuotes(str) {
1522
+ if (!str)
1523
+ return "";
1524
+ const trimmed = str.trim();
1525
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
1526
+ return trimmed.slice(1, -1);
1527
+ }
1528
+ return trimmed;
1529
+ }
1530
+ };
1531
+ function parseNativeYumia(source) {
1532
+ const parser = new NativeYumiaParser();
1533
+ return parser.parse(source);
1534
+ }
1535
+
1536
+ // ../parser/dist/migrator.js
1537
+ function migrateMarkdownToNative(source) {
1538
+ const presentation = parseYumia(source);
1539
+ const out = [];
1540
+ if (presentation.metadata.title) {
1541
+ out.push(`document "${presentation.metadata.title}"`);
1542
+ }
1543
+ if (presentation.metadata.theme) {
1544
+ const themeName = typeof presentation.metadata.theme === "string" ? presentation.metadata.theme : presentation.metadata.theme.name;
1545
+ out.push(` theme "${themeName}"`);
1546
+ }
1547
+ if (presentation.metadata.author) {
1548
+ out.push(` author "${presentation.metadata.author}"`);
1549
+ }
1550
+ if (presentation.metadata.aspectRatio) {
1551
+ out.push(` aspectRatio "${presentation.metadata.aspectRatio}"`);
1552
+ }
1553
+ if (presentation.metadata.transition) {
1554
+ const trans = typeof presentation.metadata.transition === "string" ? presentation.metadata.transition : presentation.metadata.transition.type;
1555
+ out.push(` transition "${trans}"`);
1556
+ }
1557
+ if (presentation.metadata.watermark) {
1558
+ out.push(` watermark "${presentation.metadata.watermark}"`);
1559
+ }
1560
+ out.push("");
1561
+ for (const slide of presentation.slides) {
1562
+ const firstHeading = slide.elements.find((el) => el.type === "heading");
1563
+ const slideTitle = firstHeading && firstHeading.level === 1 ? firstHeading.text : "";
1564
+ if (slideTitle) {
1565
+ out.push(`slide "${slideTitle}"`);
1566
+ } else {
1567
+ out.push("slide");
1568
+ }
1569
+ if (slide.transition) {
1570
+ const trans = typeof slide.transition === "string" ? slide.transition : slide.transition.type;
1571
+ out.push(` transition "${trans}"`);
1572
+ }
1573
+ for (const el of slide.elements) {
1574
+ if (el === firstHeading && firstHeading.level === 1)
1575
+ continue;
1576
+ out.push(serializeElement(el, 2));
1577
+ }
1578
+ if (slide.notes) {
1579
+ out.push(" notes");
1580
+ for (const nl of slide.notes.split("\n")) {
1581
+ out.push(` ${nl}`);
1582
+ }
1583
+ }
1584
+ out.push("");
1585
+ }
1586
+ return out.join("\n").trim() + "\n";
1587
+ }
1588
+ function serializeElement(el, indent) {
1589
+ const pad = " ".repeat(indent);
1590
+ switch (el.type) {
1591
+ case "heading":
1592
+ return `${pad}heading "${el.text}"`;
1593
+ case "paragraph":
1594
+ return `${pad}text "${el.text}"`;
1595
+ case "icon":
1596
+ return `${pad}icon "${el.name}"`;
1597
+ case "badge":
1598
+ return `${pad}badge "${el.text}" variant="${el.variant || "default"}"`;
1599
+ case "metric":
1600
+ return `${pad}metric "${el.value}" label="${el.label}"${el.change ? ` diff="${el.change}"` : ""}`;
1601
+ case "code": {
1602
+ const hl = el.highlight ? ` highlight="${el.highlight}"` : "";
1603
+ const lang = el.language ? ` lang="${el.language}"` : "";
1604
+ const codeLines = el.code.split("\n").map((l) => `${pad} ${l}`).join("\n");
1605
+ return `${pad}code${lang}${hl}
1606
+ ${codeLines}`;
1607
+ }
1608
+ case "section": {
1609
+ const sub = el.subtitle ? ` subtitle="${el.subtitle}"` : "";
1610
+ const num = el.number !== void 0 ? ` number="${el.number}"` : "";
1611
+ return `${pad}section "${el.title}"${sub}${num}`;
1612
+ }
1613
+ case "toc":
1614
+ return `${pad}toc "${el.title || "Table of Contents"}"`;
1615
+ case "card": {
1616
+ const cleanTitle = (el.title || "").replace(/^[Tt]itle=['"]?/, "").replace(/['"]$/, "");
1617
+ const title = cleanTitle ? ` title="${cleanTitle}"` : "";
1618
+ const v = el.variant ? ` variant="${el.variant}"` : "";
1619
+ const cardChildren = (el.elements || []).map((c) => serializeElement(c, indent + 2)).join("\n");
1620
+ return `${pad}card${title}${v}
1621
+ ${cardChildren}`;
1622
+ }
1623
+ case "grid": {
1624
+ const gridChildren = el.elements.map((c) => serializeElement(c, indent + 2)).join("\n");
1625
+ return `${pad}grid columns=${el.columns}
1626
+ ${gridChildren}`;
1627
+ }
1628
+ case "stack": {
1629
+ const stackChildren = el.elements.map((c) => serializeElement(c, indent + 2)).join("\n");
1630
+ return `${pad}stack direction="${el.direction}"
1631
+ ${stackChildren}`;
1632
+ }
1633
+ case "columns": {
1634
+ const colChildren = el.columns.map((col) => {
1635
+ const inner = col.elements.map((c) => serializeElement(c, indent + 4)).join("\n");
1636
+ return `${pad} column
1637
+ ${inner}`;
1638
+ }).join("\n");
1639
+ return `${pad}columns ${el.ratios || "50:50"}
1640
+ ${colChildren}`;
1641
+ }
1642
+ case "list": {
1643
+ const listItems = el.items.map((it) => `${pad} item "${it.text}"`).join("\n");
1644
+ return `${pad}list
1645
+ ${listItems}`;
1646
+ }
1647
+ case "quote": {
1648
+ const qAuthor = el.author ? ` author="${el.author}"` : "";
1649
+ return `${pad}quote${qAuthor}
1650
+ ${pad} ${el.text}`;
1651
+ }
1652
+ default:
1653
+ return `${pad}// [${el.type}]`;
1654
+ }
1655
+ }
1656
+
947
1657
  // ../layout/dist/engine.js
948
1658
  var DEFAULT_VIEWPORT = {
949
1659
  width: 1920,
@@ -1036,6 +1746,14 @@ var DefaultLayoutEngine = class {
1036
1746
  const height = 90;
1037
1747
  return { element, bounds: { x, y, width, height } };
1038
1748
  }
1749
+ case "section": {
1750
+ const height = 280;
1751
+ return { element, bounds: { x, y, width, height } };
1752
+ }
1753
+ case "toc": {
1754
+ const height = 320;
1755
+ return { element, bounds: { x, y, width, height } };
1756
+ }
1039
1757
  case "card": {
1040
1758
  return this.layoutCard(element, x, y, width, gap);
1041
1759
  }
@@ -1753,50 +2471,559 @@ var academicTheme = {
1753
2471
  }
1754
2472
  }
1755
2473
  };
1756
- var THEME_REGISTRY = {
1757
- default: defaultTheme,
1758
- cyberpunk: cyberpunkTheme,
1759
- minimal: minimalTheme,
1760
- corporate: corporateTheme,
1761
- terminal: terminalTheme,
1762
- academic: academicTheme
1763
- };
1764
- function resolveTheme(themeNameOrRef, explicitOverrides) {
1765
- const name = typeof themeNameOrRef === "string" ? themeNameOrRef : themeNameOrRef?.name || "default";
1766
- const baseTheme = THEME_REGISTRY[name.toLowerCase()] || defaultTheme;
1767
- const refOverrides = typeof themeNameOrRef === "object" ? themeNameOrRef.overrides : void 0;
1768
- const mergedOverrides = {
1769
- ...refOverrides,
1770
- ...explicitOverrides,
1771
- colors: { ...baseTheme.colors, ...refOverrides?.colors, ...explicitOverrides?.colors },
1772
- typography: {
1773
- ...baseTheme.typography,
1774
- ...refOverrides?.typography,
1775
- ...explicitOverrides?.typography,
1776
- sizes: {
1777
- ...baseTheme.typography.sizes,
1778
- ...refOverrides?.typography?.sizes,
1779
- ...explicitOverrides?.typography?.sizes
1780
- },
1781
- weights: {
1782
- ...baseTheme.typography.weights,
1783
- ...refOverrides?.typography?.weights,
1784
- ...explicitOverrides?.typography?.weights
1785
- },
1786
- lineHeights: {
1787
- ...baseTheme.typography.lineHeights,
1788
- ...refOverrides?.typography?.lineHeights,
1789
- ...explicitOverrides?.typography?.lineHeights
1790
- }
1791
- },
1792
- spacing: {
1793
- ...baseTheme.spacing,
1794
- ...refOverrides?.spacing,
1795
- ...explicitOverrides?.spacing,
1796
- safeArea: {
1797
- ...baseTheme.spacing.safeArea,
1798
- ...refOverrides?.spacing?.safeArea,
1799
- ...explicitOverrides?.spacing?.safeArea
2474
+ var nordTheme = {
2475
+ name: "nord",
2476
+ description: "Arctic, north-bluish clean dark palette for elegant developer decks",
2477
+ colors: {
2478
+ primary: "#88C0D0",
2479
+ secondary: "#81A1C1",
2480
+ background: "#2E3440",
2481
+ surface: "#3B4252",
2482
+ elevatedSurface: "#434C5E",
2483
+ text: "#ECEFF4",
2484
+ muted: "#D8DEE9",
2485
+ accent: "#5E81AC",
2486
+ border: "#4C566A",
2487
+ divider: "#434C5E",
2488
+ success: "#A3BE8C",
2489
+ warning: "#EBCB8B",
2490
+ danger: "#BF616A",
2491
+ info: "#88C0D0"
2492
+ },
2493
+ typography: {
2494
+ headingFont: "Inter, system-ui, sans-serif",
2495
+ bodyFont: "Inter, system-ui, sans-serif",
2496
+ codeFont: "JetBrains Mono, Fira Code, monospace",
2497
+ sizes: {
2498
+ display: 56,
2499
+ h1: 44,
2500
+ h2: 36,
2501
+ h3: 28,
2502
+ h4: 22,
2503
+ body: 18,
2504
+ small: 14,
2505
+ caption: 12,
2506
+ code: 16,
2507
+ quote: 20,
2508
+ metadata: 12
2509
+ },
2510
+ weights: {
2511
+ normal: 400,
2512
+ medium: 500,
2513
+ bold: 700
2514
+ },
2515
+ lineHeights: {
2516
+ tight: 1.15,
2517
+ snug: 1.3,
2518
+ normal: 1.55,
2519
+ relaxed: 1.7
2520
+ },
2521
+ letterSpacing: {
2522
+ tight: "-0.02em",
2523
+ normal: "0em",
2524
+ wide: "0.03em"
2525
+ }
2526
+ },
2527
+ spacing: {
2528
+ unit: 8,
2529
+ scale: [0, 4, 8, 12, 16, 24, 32, 48, 64, 96],
2530
+ slidePadding: 48,
2531
+ elementGap: 24,
2532
+ safeArea: { top: 48, bottom: 48, left: 48, right: 48 }
2533
+ },
2534
+ radius: {
2535
+ none: 0,
2536
+ sm: 4,
2537
+ md: 8,
2538
+ lg: 14,
2539
+ xl: 20,
2540
+ full: 9999,
2541
+ default: 8
2542
+ },
2543
+ shadows: {
2544
+ none: "none",
2545
+ sm: "0 2px 4px rgba(0, 0, 0, 0.25)",
2546
+ md: "0 6px 12px rgba(0, 0, 0, 0.35)",
2547
+ lg: "0 12px 24px rgba(0, 0, 0, 0.45)",
2548
+ glow: "0 0 16px rgba(136, 192, 208, 0.35)"
2549
+ },
2550
+ components: {
2551
+ card: {
2552
+ background: "#3B4252",
2553
+ borderColor: "#88C0D0",
2554
+ borderRadius: 10,
2555
+ padding: 24
2556
+ },
2557
+ metric: {
2558
+ valueColor: "#88C0D0",
2559
+ labelColor: "#D8DEE9",
2560
+ borderRadius: 10
2561
+ },
2562
+ code: {
2563
+ background: "#242933",
2564
+ textColor: "#ECEFF4",
2565
+ borderRadius: 8
2566
+ },
2567
+ table: {
2568
+ headerBackground: "#434C5E",
2569
+ rowAlternateBackground: "#3B4252",
2570
+ borderColor: "#4C566A"
2571
+ }
2572
+ }
2573
+ };
2574
+ var draculaTheme = {
2575
+ name: "dracula",
2576
+ description: "Famous dark theme with rich purples, vibrant pinks and pastel highlights",
2577
+ colors: {
2578
+ primary: "#BD93F9",
2579
+ secondary: "#FF79C6",
2580
+ background: "#282A36",
2581
+ surface: "#343746",
2582
+ elevatedSurface: "#44475A",
2583
+ text: "#F8F8F2",
2584
+ muted: "#6272A4",
2585
+ accent: "#8BE9FD",
2586
+ border: "#44475A",
2587
+ divider: "#6272A4",
2588
+ success: "#50FA7B",
2589
+ warning: "#F1FA8C",
2590
+ danger: "#FF5555",
2591
+ info: "#8BE9FD"
2592
+ },
2593
+ typography: {
2594
+ headingFont: "Outfit, Inter, sans-serif",
2595
+ bodyFont: "Inter, sans-serif",
2596
+ codeFont: "Fira Code, JetBrains Mono, monospace",
2597
+ sizes: {
2598
+ display: 56,
2599
+ h1: 44,
2600
+ h2: 36,
2601
+ h3: 28,
2602
+ h4: 22,
2603
+ body: 18,
2604
+ small: 14,
2605
+ caption: 12,
2606
+ code: 16,
2607
+ quote: 20,
2608
+ metadata: 12
2609
+ },
2610
+ weights: {
2611
+ normal: 400,
2612
+ medium: 500,
2613
+ bold: 700
2614
+ },
2615
+ lineHeights: {
2616
+ tight: 1.15,
2617
+ snug: 1.3,
2618
+ normal: 1.55,
2619
+ relaxed: 1.7
2620
+ },
2621
+ letterSpacing: {
2622
+ tight: "-0.02em",
2623
+ normal: "0em",
2624
+ wide: "0.03em"
2625
+ }
2626
+ },
2627
+ spacing: {
2628
+ unit: 8,
2629
+ scale: [0, 4, 8, 12, 16, 24, 32, 48, 64, 96],
2630
+ slidePadding: 48,
2631
+ elementGap: 24,
2632
+ safeArea: { top: 48, bottom: 48, left: 48, right: 48 }
2633
+ },
2634
+ radius: {
2635
+ none: 0,
2636
+ sm: 4,
2637
+ md: 10,
2638
+ lg: 16,
2639
+ xl: 24,
2640
+ full: 9999,
2641
+ default: 10
2642
+ },
2643
+ shadows: {
2644
+ none: "none",
2645
+ sm: "0 2px 5px rgba(0, 0, 0, 0.3)",
2646
+ md: "0 8px 16px rgba(0, 0, 0, 0.45)",
2647
+ lg: "0 16px 32px rgba(0, 0, 0, 0.6)",
2648
+ glow: "0 0 20px rgba(189, 147, 249, 0.4)"
2649
+ },
2650
+ components: {
2651
+ card: {
2652
+ background: "#343746",
2653
+ borderColor: "#BD93F9",
2654
+ borderRadius: 12,
2655
+ padding: 24
2656
+ },
2657
+ metric: {
2658
+ valueColor: "#FF79C6",
2659
+ labelColor: "#6272A4",
2660
+ borderRadius: 12
2661
+ },
2662
+ code: {
2663
+ background: "#1E1F29",
2664
+ textColor: "#F8F8F2",
2665
+ borderRadius: 8
2666
+ },
2667
+ table: {
2668
+ headerBackground: "#44475A",
2669
+ rowAlternateBackground: "#343746",
2670
+ borderColor: "#6272A4"
2671
+ }
2672
+ }
2673
+ };
2674
+ var tokyoNightTheme = {
2675
+ name: "tokyo-night",
2676
+ description: "Sleek Tokyo Night neon indigo theme with cyan, violet and magenta tones",
2677
+ colors: {
2678
+ primary: "#7AA2F7",
2679
+ secondary: "#BB9AF7",
2680
+ background: "#1A1B26",
2681
+ surface: "#24283B",
2682
+ elevatedSurface: "#2F354A",
2683
+ text: "#C0CAF5",
2684
+ muted: "#565F89",
2685
+ accent: "#7DCFFF",
2686
+ border: "#2F354A",
2687
+ divider: "#414868",
2688
+ success: "#9ECE6A",
2689
+ warning: "#E0AF68",
2690
+ danger: "#F7768E",
2691
+ info: "#7DCFFF"
2692
+ },
2693
+ typography: {
2694
+ headingFont: "Outfit, Inter, sans-serif",
2695
+ bodyFont: "Inter, sans-serif",
2696
+ codeFont: "JetBrains Mono, monospace",
2697
+ sizes: {
2698
+ display: 58,
2699
+ h1: 46,
2700
+ h2: 36,
2701
+ h3: 28,
2702
+ h4: 22,
2703
+ body: 18,
2704
+ small: 14,
2705
+ caption: 12,
2706
+ code: 16,
2707
+ quote: 20,
2708
+ metadata: 12
2709
+ },
2710
+ weights: {
2711
+ normal: 400,
2712
+ medium: 500,
2713
+ bold: 700
2714
+ },
2715
+ lineHeights: {
2716
+ tight: 1.15,
2717
+ snug: 1.3,
2718
+ normal: 1.55,
2719
+ relaxed: 1.7
2720
+ },
2721
+ letterSpacing: {
2722
+ tight: "-0.02em",
2723
+ normal: "0em",
2724
+ wide: "0.03em"
2725
+ }
2726
+ },
2727
+ spacing: {
2728
+ unit: 8,
2729
+ scale: [0, 4, 8, 12, 16, 24, 32, 48, 64, 96],
2730
+ slidePadding: 48,
2731
+ elementGap: 24,
2732
+ safeArea: { top: 48, bottom: 48, left: 48, right: 48 }
2733
+ },
2734
+ radius: {
2735
+ none: 0,
2736
+ sm: 6,
2737
+ md: 10,
2738
+ lg: 16,
2739
+ xl: 24,
2740
+ full: 9999,
2741
+ default: 10
2742
+ },
2743
+ shadows: {
2744
+ none: "none",
2745
+ sm: "0 2px 4px rgba(0, 0, 0, 0.3)",
2746
+ md: "0 6px 14px rgba(0, 0, 0, 0.45)",
2747
+ lg: "0 14px 28px rgba(0, 0, 0, 0.6)",
2748
+ glow: "0 0 18px rgba(122, 162, 247, 0.4)"
2749
+ },
2750
+ components: {
2751
+ card: {
2752
+ background: "#24283B",
2753
+ borderColor: "#7AA2F7",
2754
+ borderRadius: 12,
2755
+ padding: 24
2756
+ },
2757
+ metric: {
2758
+ valueColor: "#7AA2F7",
2759
+ labelColor: "#565F89",
2760
+ borderRadius: 12
2761
+ },
2762
+ code: {
2763
+ background: "#16161E",
2764
+ textColor: "#C0CAF5",
2765
+ borderRadius: 8
2766
+ },
2767
+ table: {
2768
+ headerBackground: "#2F354A",
2769
+ rowAlternateBackground: "#24283B",
2770
+ borderColor: "#414868"
2771
+ }
2772
+ }
2773
+ };
2774
+ var emeraldTheme = {
2775
+ name: "emerald",
2776
+ description: "Deep forest luxury green with mint and emerald highlights",
2777
+ colors: {
2778
+ primary: "#10B981",
2779
+ secondary: "#34D399",
2780
+ background: "#022C22",
2781
+ surface: "#064E3B",
2782
+ elevatedSurface: "#065F46",
2783
+ text: "#ECFDF5",
2784
+ muted: "#6EE7B7",
2785
+ accent: "#A7F3D0",
2786
+ border: "#047857",
2787
+ divider: "#065F46",
2788
+ success: "#34D399",
2789
+ warning: "#FBBF24",
2790
+ danger: "#F87171",
2791
+ info: "#38BDF8"
2792
+ },
2793
+ typography: {
2794
+ headingFont: "Outfit, Inter, sans-serif",
2795
+ bodyFont: "Inter, sans-serif",
2796
+ codeFont: "JetBrains Mono, monospace",
2797
+ sizes: {
2798
+ display: 56,
2799
+ h1: 44,
2800
+ h2: 36,
2801
+ h3: 28,
2802
+ h4: 22,
2803
+ body: 18,
2804
+ small: 14,
2805
+ caption: 12,
2806
+ code: 16,
2807
+ quote: 20,
2808
+ metadata: 12
2809
+ },
2810
+ weights: {
2811
+ normal: 400,
2812
+ medium: 500,
2813
+ bold: 700
2814
+ },
2815
+ lineHeights: {
2816
+ tight: 1.15,
2817
+ snug: 1.3,
2818
+ normal: 1.55,
2819
+ relaxed: 1.7
2820
+ },
2821
+ letterSpacing: {
2822
+ tight: "-0.02em",
2823
+ normal: "0em",
2824
+ wide: "0.03em"
2825
+ }
2826
+ },
2827
+ spacing: {
2828
+ unit: 8,
2829
+ scale: [0, 4, 8, 12, 16, 24, 32, 48, 64, 96],
2830
+ slidePadding: 48,
2831
+ elementGap: 24,
2832
+ safeArea: { top: 48, bottom: 48, left: 48, right: 48 }
2833
+ },
2834
+ radius: {
2835
+ none: 0,
2836
+ sm: 4,
2837
+ md: 8,
2838
+ lg: 16,
2839
+ xl: 24,
2840
+ full: 9999,
2841
+ default: 8
2842
+ },
2843
+ shadows: {
2844
+ none: "none",
2845
+ sm: "0 2px 4px rgba(0, 0, 0, 0.3)",
2846
+ md: "0 6px 14px rgba(0, 0, 0, 0.45)",
2847
+ lg: "0 14px 28px rgba(0, 0, 0, 0.6)",
2848
+ glow: "0 0 18px rgba(16, 185, 129, 0.4)"
2849
+ },
2850
+ components: {
2851
+ card: {
2852
+ background: "#064E3B",
2853
+ borderColor: "#10B981",
2854
+ borderRadius: 12,
2855
+ padding: 24
2856
+ },
2857
+ metric: {
2858
+ valueColor: "#10B981",
2859
+ labelColor: "#6EE7B7",
2860
+ borderRadius: 12
2861
+ },
2862
+ code: {
2863
+ background: "#011A14",
2864
+ textColor: "#ECFDF5",
2865
+ borderRadius: 8
2866
+ },
2867
+ table: {
2868
+ headerBackground: "#065F46",
2869
+ rowAlternateBackground: "#064E3B",
2870
+ borderColor: "#047857"
2871
+ }
2872
+ }
2873
+ };
2874
+ var synthwaveTheme = {
2875
+ name: "synthwave",
2876
+ description: "80s retro neon sunset with hot pink, glowing cyan and electric violet",
2877
+ colors: {
2878
+ primary: "#FF71CE",
2879
+ secondary: "#01CDFE",
2880
+ background: "#1A0E2E",
2881
+ surface: "#261447",
2882
+ elevatedSurface: "#351B63",
2883
+ text: "#FFFFFF",
2884
+ muted: "#B967FF",
2885
+ accent: "#05FFA1",
2886
+ border: "#4D288C",
2887
+ divider: "#6432B8",
2888
+ success: "#05FFA1",
2889
+ warning: "#FFFB96",
2890
+ danger: "#FF2A6D",
2891
+ info: "#01CDFE"
2892
+ },
2893
+ typography: {
2894
+ headingFont: "Outfit, Inter, sans-serif",
2895
+ bodyFont: "Inter, sans-serif",
2896
+ codeFont: "Fira Code, monospace",
2897
+ sizes: {
2898
+ display: 58,
2899
+ h1: 46,
2900
+ h2: 38,
2901
+ h3: 28,
2902
+ h4: 22,
2903
+ body: 18,
2904
+ small: 14,
2905
+ caption: 12,
2906
+ code: 16,
2907
+ quote: 20,
2908
+ metadata: 12
2909
+ },
2910
+ weights: {
2911
+ normal: 400,
2912
+ medium: 500,
2913
+ bold: 700
2914
+ },
2915
+ lineHeights: {
2916
+ tight: 1.15,
2917
+ snug: 1.3,
2918
+ normal: 1.55,
2919
+ relaxed: 1.7
2920
+ },
2921
+ letterSpacing: {
2922
+ tight: "-0.02em",
2923
+ normal: "0em",
2924
+ wide: "0.04em"
2925
+ }
2926
+ },
2927
+ spacing: {
2928
+ unit: 8,
2929
+ scale: [0, 4, 8, 12, 16, 24, 32, 48, 64, 96],
2930
+ slidePadding: 48,
2931
+ elementGap: 24,
2932
+ safeArea: { top: 48, bottom: 48, left: 48, right: 48 }
2933
+ },
2934
+ radius: {
2935
+ none: 0,
2936
+ sm: 6,
2937
+ md: 12,
2938
+ lg: 18,
2939
+ xl: 26,
2940
+ full: 9999,
2941
+ default: 12
2942
+ },
2943
+ shadows: {
2944
+ none: "none",
2945
+ sm: "0 2px 6px rgba(0, 0, 0, 0.4)",
2946
+ md: "0 8px 18px rgba(0, 0, 0, 0.6)",
2947
+ lg: "0 16px 36px rgba(0, 0, 0, 0.75)",
2948
+ glow: "0 0 22px rgba(255, 113, 206, 0.5)"
2949
+ },
2950
+ components: {
2951
+ card: {
2952
+ background: "#261447",
2953
+ borderColor: "#FF71CE",
2954
+ borderRadius: 14,
2955
+ padding: 24
2956
+ },
2957
+ metric: {
2958
+ valueColor: "#FF71CE",
2959
+ labelColor: "#B967FF",
2960
+ borderRadius: 14
2961
+ },
2962
+ code: {
2963
+ background: "#11091F",
2964
+ textColor: "#05FFA1",
2965
+ borderRadius: 8
2966
+ },
2967
+ table: {
2968
+ headerBackground: "#351B63",
2969
+ rowAlternateBackground: "#261447",
2970
+ borderColor: "#4D288C"
2971
+ }
2972
+ }
2973
+ };
2974
+ var THEME_REGISTRY = {
2975
+ default: defaultTheme,
2976
+ cyberpunk: cyberpunkTheme,
2977
+ minimal: minimalTheme,
2978
+ corporate: corporateTheme,
2979
+ terminal: terminalTheme,
2980
+ academic: academicTheme,
2981
+ nord: nordTheme,
2982
+ dracula: draculaTheme,
2983
+ "tokyo-night": tokyoNightTheme,
2984
+ tokyonight: tokyoNightTheme,
2985
+ midnight: tokyoNightTheme,
2986
+ emerald: emeraldTheme,
2987
+ forest: emeraldTheme,
2988
+ synthwave: synthwaveTheme,
2989
+ sunset: synthwaveTheme
2990
+ };
2991
+ function resolveTheme(themeNameOrRef, explicitOverrides) {
2992
+ const name = typeof themeNameOrRef === "string" ? themeNameOrRef : themeNameOrRef?.name || "default";
2993
+ const baseTheme = THEME_REGISTRY[name.toLowerCase()] || defaultTheme;
2994
+ const refOverrides = typeof themeNameOrRef === "object" ? themeNameOrRef.overrides : void 0;
2995
+ const mergedOverrides = {
2996
+ ...refOverrides,
2997
+ ...explicitOverrides,
2998
+ colors: { ...baseTheme.colors, ...refOverrides?.colors, ...explicitOverrides?.colors },
2999
+ typography: {
3000
+ ...baseTheme.typography,
3001
+ ...refOverrides?.typography,
3002
+ ...explicitOverrides?.typography,
3003
+ sizes: {
3004
+ ...baseTheme.typography.sizes,
3005
+ ...refOverrides?.typography?.sizes,
3006
+ ...explicitOverrides?.typography?.sizes
3007
+ },
3008
+ weights: {
3009
+ ...baseTheme.typography.weights,
3010
+ ...refOverrides?.typography?.weights,
3011
+ ...explicitOverrides?.typography?.weights
3012
+ },
3013
+ lineHeights: {
3014
+ ...baseTheme.typography.lineHeights,
3015
+ ...refOverrides?.typography?.lineHeights,
3016
+ ...explicitOverrides?.typography?.lineHeights
3017
+ }
3018
+ },
3019
+ spacing: {
3020
+ ...baseTheme.spacing,
3021
+ ...refOverrides?.spacing,
3022
+ ...explicitOverrides?.spacing,
3023
+ safeArea: {
3024
+ ...baseTheme.spacing.safeArea,
3025
+ ...refOverrides?.spacing?.safeArea,
3026
+ ...explicitOverrides?.spacing?.safeArea
1800
3027
  }
1801
3028
  },
1802
3029
  radius: {
@@ -2248,6 +3475,174 @@ function lint(sourceOrPresentation, options) {
2248
3475
  return defaultCompiler.lint(sourceOrPresentation, options);
2249
3476
  }
2250
3477
 
3478
+ // ../renderer/dist/icons.js
3479
+ var BUILTIN_ICONS = {
3480
+ // Lucide & General
3481
+ rocket: {
3482
+ name: "rocket",
3483
+ provider: "lucide",
3484
+ viewBox: "0 0 24 24",
3485
+ path: '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>'
3486
+ },
3487
+ code: {
3488
+ name: "code",
3489
+ provider: "lucide",
3490
+ viewBox: "0 0 24 24",
3491
+ path: '<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>'
3492
+ },
3493
+ terminal: {
3494
+ name: "terminal",
3495
+ provider: "lucide",
3496
+ viewBox: "0 0 24 24",
3497
+ path: '<polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/>'
3498
+ },
3499
+ cpu: {
3500
+ name: "cpu",
3501
+ provider: "lucide",
3502
+ viewBox: "0 0 24 24",
3503
+ path: '<rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><line x1="9" y1="1" x2="9" y2="4"/><line x1="15" y1="1" x2="15" y2="4"/><line x1="9" y1="20" x2="9" y2="23"/><line x1="15" y1="20" x2="15" y2="23"/><line x1="20" y1="9" x2="23" y2="9"/><line x1="20" y1="14" x2="23" y2="14"/><line x1="1" y1="9" x2="4" y2="9"/><line x1="1" y1="14" x2="4" y2="14"/>'
3504
+ },
3505
+ database: {
3506
+ name: "database",
3507
+ provider: "lucide",
3508
+ viewBox: "0 0 24 24",
3509
+ path: '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>'
3510
+ },
3511
+ zap: {
3512
+ name: "zap",
3513
+ provider: "lucide",
3514
+ viewBox: "0 0 24 24",
3515
+ path: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>'
3516
+ },
3517
+ shield: {
3518
+ name: "shield",
3519
+ provider: "lucide",
3520
+ viewBox: "0 0 24 24",
3521
+ path: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>'
3522
+ },
3523
+ activity: {
3524
+ name: "activity",
3525
+ provider: "lucide",
3526
+ viewBox: "0 0 24 24",
3527
+ path: '<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>'
3528
+ },
3529
+ palette: {
3530
+ name: "palette",
3531
+ provider: "lucide",
3532
+ viewBox: "0 0 24 24",
3533
+ path: '<circle cx="13.5" cy="6.5" r=".5"/><circle cx="17.5" cy="10.5" r=".5"/><circle cx="8.5" cy="7.5" r=".5"/><circle cx="6.5" cy="12.5" r=".5"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z"/>'
3534
+ },
3535
+ chart: {
3536
+ name: "chart",
3537
+ provider: "lucide",
3538
+ viewBox: "0 0 24 24",
3539
+ path: '<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/>'
3540
+ },
3541
+ layers: {
3542
+ name: "layers",
3543
+ provider: "lucide",
3544
+ viewBox: "0 0 24 24",
3545
+ path: '<polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/>'
3546
+ },
3547
+ users: {
3548
+ name: "users",
3549
+ provider: "lucide",
3550
+ viewBox: "0 0 24 24",
3551
+ path: '<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>'
3552
+ },
3553
+ check: {
3554
+ name: "check",
3555
+ provider: "lucide",
3556
+ viewBox: "0 0 24 24",
3557
+ path: '<polyline points="20 6 9 17 4 12"/>'
3558
+ },
3559
+ "git-branch": {
3560
+ name: "git-branch",
3561
+ provider: "lucide",
3562
+ viewBox: "0 0 24 24",
3563
+ path: '<line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/>'
3564
+ },
3565
+ globe: {
3566
+ name: "globe",
3567
+ provider: "lucide",
3568
+ viewBox: "0 0 24 24",
3569
+ path: '<circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>'
3570
+ },
3571
+ sparkles: {
3572
+ name: "sparkles",
3573
+ provider: "lucide",
3574
+ viewBox: "0 0 24 24",
3575
+ path: '<path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/>'
3576
+ }
3577
+ };
3578
+ var IconResolver = class {
3579
+ cache = /* @__PURE__ */ new Map();
3580
+ customProviders = /* @__PURE__ */ new Map();
3581
+ constructor() {
3582
+ for (const [key, def] of Object.entries(BUILTIN_ICONS)) {
3583
+ this.cache.set(key, def);
3584
+ this.cache.set(`lucide:${key}`, def);
3585
+ this.cache.set(`material:${key}`, { ...def, provider: "material" });
3586
+ this.cache.set(`fa:${key}`, { ...def, provider: "fa" });
3587
+ this.cache.set(`tabler:${key}`, { ...def, provider: "tabler" });
3588
+ this.cache.set(`heroicons:${key}`, { ...def, provider: "heroicons" });
3589
+ }
3590
+ }
3591
+ registerProvider(name, resolver) {
3592
+ this.customProviders.set(name.toLowerCase(), resolver);
3593
+ }
3594
+ resolve(rawName, options = {}) {
3595
+ const trimmed = rawName.trim();
3596
+ if (!trimmed) {
3597
+ return this.getFallback("unknown");
3598
+ }
3599
+ if (this.cache.has(trimmed.toLowerCase())) {
3600
+ return this.cache.get(trimmed.toLowerCase());
3601
+ }
3602
+ let provider = options.defaultProvider || "lucide";
3603
+ let iconName = trimmed;
3604
+ if (trimmed.includes(":")) {
3605
+ const parts = trimmed.split(":");
3606
+ provider = parts[0].toLowerCase();
3607
+ iconName = parts.slice(1).join(":").toLowerCase();
3608
+ }
3609
+ if (this.customProviders.has(provider)) {
3610
+ const customDef = this.customProviders.get(provider)(iconName);
3611
+ if (customDef) {
3612
+ this.cache.set(trimmed.toLowerCase(), customDef);
3613
+ return customDef;
3614
+ }
3615
+ }
3616
+ if (this.cache.has(iconName)) {
3617
+ const def = { ...this.cache.get(iconName), provider };
3618
+ this.cache.set(trimmed.toLowerCase(), def);
3619
+ return def;
3620
+ }
3621
+ if (options.strict) {
3622
+ throw new Error(`Icon '${trimmed}' not found in registry (provider: ${provider})`);
3623
+ }
3624
+ const fallback = this.getFallback(iconName, provider);
3625
+ this.cache.set(trimmed.toLowerCase(), fallback);
3626
+ return fallback;
3627
+ }
3628
+ getFallback(iconName, provider = "lucide") {
3629
+ const initial = iconName.charAt(0).toUpperCase() || "\u2605";
3630
+ return {
3631
+ name: iconName,
3632
+ provider,
3633
+ viewBox: "0 0 24 24",
3634
+ path: `<circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"/><text x="12" y="16" text-anchor="middle" font-size="11" font-weight="bold" fill="currentColor" font-family="sans-serif">${initial}</text>`
3635
+ };
3636
+ }
3637
+ toSvg(rawName, size = 24, color, className = "yumia-icon") {
3638
+ const icon = this.resolve(rawName);
3639
+ const fill = color ? `color="${color}"` : "";
3640
+ const style = color ? `style="color:${color}; fill:none; stroke:currentColor;"` : 'style="fill:none; stroke:currentColor;"';
3641
+ return `<svg class="${className}" width="${size}" height="${size}" viewBox="${icon.viewBox}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" ${fill} ${style}>${icon.path}</svg>`;
3642
+ }
3643
+ };
3644
+ var defaultIconResolver = new IconResolver();
3645
+
2251
3646
  // ../renderer-pptx/dist/renderer.js
2252
3647
  import pptxgen from "pptxgenjs";
2253
3648
  var CSS_NAMED_COLORS = {
@@ -2294,6 +3689,8 @@ function cleanFontFace(fontString) {
2294
3689
  return "Segoe UI";
2295
3690
  }
2296
3691
  function parseInlineMarkdown(rawText, baseOptions) {
3692
+ if (!rawText)
3693
+ return [];
2297
3694
  const chunks = [];
2298
3695
  const regex = /(\*\*.*?\*\*|\*.*?\*|`.*?`)/g;
2299
3696
  const parts = rawText.split(regex);
@@ -2356,7 +3753,7 @@ var PptxRenderer = class {
2356
3753
  pptxSlide.background = { color: bgColor };
2357
3754
  const slideLayout = this.layoutEngine.computeSlide(slide, pixelViewport);
2358
3755
  for (const node of slideLayout.nodes) {
2359
- this.renderNode(pptxSlide, pptx, node, scaleX, scaleY, theme);
3756
+ this.renderNode(pptxSlide, pptx, node, scaleX, scaleY, theme, presentation);
2360
3757
  }
2361
3758
  if (slide.notes) {
2362
3759
  pptxSlide.addNotes(slide.notes);
@@ -2371,7 +3768,7 @@ var PptxRenderer = class {
2371
3768
  fileName: `${title.toLowerCase().replace(/[^a-z0-9]+/g, "-")}.pptx`
2372
3769
  };
2373
3770
  }
2374
- renderNode(pptxSlide, pptx, node, scaleX, scaleY, theme) {
3771
+ renderNode(pptxSlide, pptx, node, scaleX, scaleY, theme, presentation) {
2375
3772
  const { element, bounds } = node;
2376
3773
  const rect = this.toInches(bounds, scaleX, scaleY);
2377
3774
  switch (element.type) {
@@ -2400,10 +3797,10 @@ var PptxRenderer = class {
2400
3797
  this.renderMetric(pptxSlide, pptx, node, scaleX, scaleY, theme);
2401
3798
  break;
2402
3799
  case "card":
2403
- this.renderCard(pptxSlide, pptx, node, scaleX, scaleY, theme);
3800
+ this.renderCard(pptxSlide, pptx, node, scaleX, scaleY, theme, presentation);
2404
3801
  break;
2405
3802
  case "columns":
2406
- this.renderColumns(pptxSlide, pptx, node, scaleX, scaleY, theme);
3803
+ this.renderColumns(pptxSlide, pptx, node, scaleX, scaleY, theme, presentation);
2407
3804
  break;
2408
3805
  case "badge":
2409
3806
  this.renderBadge(pptxSlide, pptx, element, rect, theme);
@@ -2417,12 +3814,42 @@ var PptxRenderer = class {
2417
3814
  case "compare":
2418
3815
  this.renderCompare(pptxSlide, pptx, node, scaleX, scaleY, theme);
2419
3816
  break;
3817
+ case "section":
3818
+ this.renderSection(pptxSlide, pptx, element, rect, theme);
3819
+ break;
3820
+ case "toc":
3821
+ this.renderToc(pptxSlide, pptx, element, rect, theme, presentation);
3822
+ break;
2420
3823
  case "mermaid":
2421
3824
  this.renderMermaid(pptxSlide, pptx, element, rect, theme);
2422
3825
  break;
2423
3826
  case "math":
2424
3827
  this.renderMath(pptxSlide, pptx, element, rect, theme);
2425
3828
  break;
3829
+ case "icon": {
3830
+ const ic = element;
3831
+ const iconName = ic.name.replace(/^[^:]+:/, "").toUpperCase();
3832
+ pptxSlide.addText(`\u2605 ${iconName}`, {
3833
+ x: rect.x,
3834
+ y: rect.y,
3835
+ w: Math.max(rect.w, 1.5),
3836
+ h: Math.max(rect.h, 0.4),
3837
+ fontSize: 14,
3838
+ bold: true,
3839
+ color: this.cleanHexColor(theme.colors.primary),
3840
+ valign: "middle"
3841
+ });
3842
+ break;
3843
+ }
3844
+ case "grid":
3845
+ case "stack": {
3846
+ if (node.children) {
3847
+ for (const childNode of node.children) {
3848
+ this.renderNode(pptxSlide, pptx, childNode, scaleX, scaleY, theme, presentation);
3849
+ }
3850
+ }
3851
+ break;
3852
+ }
2426
3853
  default:
2427
3854
  break;
2428
3855
  }
@@ -2636,7 +4063,7 @@ var PptxRenderer = class {
2636
4063
  });
2637
4064
  }
2638
4065
  }
2639
- renderCard(pptxSlide, pptx, node, scaleX, scaleY, theme) {
4066
+ renderCard(pptxSlide, pptx, node, scaleX, scaleY, theme, presentation) {
2640
4067
  const card = node.element;
2641
4068
  const rect = this.toInches(node.bounds, scaleX, scaleY);
2642
4069
  const cardTheme = theme.components?.card;
@@ -2657,6 +4084,14 @@ var PptxRenderer = class {
2657
4084
  line: { color: borderColor, width: 1.5 },
2658
4085
  rectRadius: 0.08
2659
4086
  });
4087
+ pptxSlide.addShape(pptx.ShapeType.roundRect, {
4088
+ x: rect.x + 0.02,
4089
+ y: rect.y + 0.04,
4090
+ w: 0.06,
4091
+ h: rect.h - 0.08,
4092
+ fill: { color: borderColor },
4093
+ rectRadius: 0.03
4094
+ });
2660
4095
  if (card.title) {
2661
4096
  pptxSlide.addText(card.title, {
2662
4097
  x: rect.x + 0.25,
@@ -2671,16 +4106,16 @@ var PptxRenderer = class {
2671
4106
  }
2672
4107
  if (node.children) {
2673
4108
  for (const childNode of node.children) {
2674
- this.renderNode(pptxSlide, pptx, childNode, scaleX, scaleY, theme);
4109
+ this.renderNode(pptxSlide, pptx, childNode, scaleX, scaleY, theme, presentation);
2675
4110
  }
2676
4111
  }
2677
4112
  }
2678
- renderColumns(pptxSlide, pptx, node, scaleX, scaleY, theme) {
4113
+ renderColumns(pptxSlide, pptx, node, scaleX, scaleY, theme, presentation) {
2679
4114
  if (node.children) {
2680
4115
  for (const colNode of node.children) {
2681
4116
  if (colNode.children) {
2682
4117
  for (const itemNode of colNode.children) {
2683
- this.renderNode(pptxSlide, pptx, itemNode, scaleX, scaleY, theme);
4118
+ this.renderNode(pptxSlide, pptx, itemNode, scaleX, scaleY, theme, presentation);
2684
4119
  }
2685
4120
  }
2686
4121
  }
@@ -2688,26 +4123,88 @@ var PptxRenderer = class {
2688
4123
  }
2689
4124
  renderCode(pptxSlide, pptx, code, rect, theme) {
2690
4125
  const codeTheme = theme.components?.code;
2691
- const bgColor = this.cleanHexColor(codeTheme?.background || "#0f172a");
4126
+ const bgColor = this.cleanHexColor(codeTheme?.background || "#0a0f1d");
2692
4127
  const textColor = this.cleanHexColor(codeTheme?.textColor || "#f8fafc");
4128
+ const primaryColor = this.cleanHexColor(theme.colors.primary || "#00F0FF");
4129
+ const mutedColor = this.cleanHexColor(theme.colors.muted || "#64748b");
2693
4130
  pptxSlide.addShape(pptx.ShapeType.roundRect, {
2694
4131
  x: rect.x,
2695
4132
  y: rect.y,
2696
4133
  w: rect.w,
2697
4134
  h: rect.h,
2698
4135
  fill: { color: bgColor },
4136
+ line: { color: this.cleanHexColor(theme.colors.border || "#1e293b"), width: 1 },
2699
4137
  rectRadius: 0.05
2700
4138
  });
2701
- pptxSlide.addText(code.code, {
2702
- x: rect.x + 0.2,
2703
- y: rect.y + 0.15,
2704
- w: rect.w - 0.4,
2705
- h: rect.h - 0.3,
2706
- fontSize: 14,
2707
- fontFace: "Courier New",
2708
- color: textColor,
2709
- valign: "top"
2710
- });
4139
+ const lines = code.code.split("\n");
4140
+ if (code.highlight) {
4141
+ const highlightSet = /* @__PURE__ */ new Set();
4142
+ const parts = code.highlight.split(",");
4143
+ for (const part of parts) {
4144
+ const trimmed = part.trim();
4145
+ if (!trimmed)
4146
+ continue;
4147
+ if (trimmed.includes("-")) {
4148
+ const [startStr, endStr] = trimmed.split("-");
4149
+ const start = parseInt(startStr || "0", 10);
4150
+ const end = parseInt(endStr || "0", 10);
4151
+ if (!isNaN(start) && !isNaN(end) && start <= end) {
4152
+ for (let n = start; n <= end; n++)
4153
+ highlightSet.add(n);
4154
+ }
4155
+ } else {
4156
+ const num = parseInt(trimmed, 10);
4157
+ if (!isNaN(num))
4158
+ highlightSet.add(num);
4159
+ }
4160
+ }
4161
+ const chunks = [];
4162
+ lines.forEach((line, idx) => {
4163
+ const lineNum = idx + 1;
4164
+ const isHl = highlightSet.has(lineNum);
4165
+ const lineNumPad = String(lineNum).padStart(2, "0");
4166
+ const numColor = isHl ? primaryColor : mutedColor;
4167
+ const contentColor = isHl ? primaryColor : mutedColor;
4168
+ chunks.push({
4169
+ text: `${lineNumPad} `,
4170
+ options: {
4171
+ fontSize: 12,
4172
+ fontFace: "Consolas",
4173
+ color: numColor,
4174
+ bold: isHl
4175
+ }
4176
+ });
4177
+ chunks.push({
4178
+ text: line || " ",
4179
+ options: {
4180
+ fontSize: 12,
4181
+ fontFace: "Consolas",
4182
+ color: contentColor,
4183
+ bold: isHl,
4184
+ breakLine: idx < lines.length - 1
4185
+ }
4186
+ });
4187
+ });
4188
+ pptxSlide.addText(chunks, {
4189
+ x: rect.x + 0.2,
4190
+ y: rect.y + 0.15,
4191
+ w: rect.w - 0.4,
4192
+ h: rect.h - 0.3,
4193
+ valign: "top",
4194
+ margin: 0
4195
+ });
4196
+ } else {
4197
+ pptxSlide.addText(code.code, {
4198
+ x: rect.x + 0.2,
4199
+ y: rect.y + 0.15,
4200
+ w: rect.w - 0.4,
4201
+ h: rect.h - 0.3,
4202
+ fontSize: 13,
4203
+ fontFace: "Consolas",
4204
+ color: textColor,
4205
+ valign: "top"
4206
+ });
4207
+ }
2711
4208
  }
2712
4209
  renderQuote(pptxSlide, pptx, quote, rect, theme) {
2713
4210
  const accentColor = this.cleanHexColor(theme.colors.accent || theme.colors.primary);
@@ -2734,7 +4231,8 @@ var PptxRenderer = class {
2734
4231
  renderBadge(pptxSlide, pptx, badge, rect, theme) {
2735
4232
  const colorKey = badge.variant || "primary";
2736
4233
  const badgeColor = this.cleanHexColor(theme.colors[colorKey] || theme.colors.primary);
2737
- const badgeW = Math.min(2.5, Math.max(1, badge.text.length * 0.12 + 0.4));
4234
+ const badgeText = badge.text;
4235
+ const badgeW = Math.min(2.5, Math.max(1, badgeText.length * 0.12 + 0.4));
2738
4236
  const badgeH = Math.min(0.38, rect.h);
2739
4237
  pptxSlide.addShape(pptx.ShapeType.roundRect, {
2740
4238
  x: rect.x,
@@ -2745,7 +4243,7 @@ var PptxRenderer = class {
2745
4243
  line: { color: badgeColor, width: 1.5 },
2746
4244
  rectRadius: 0.15
2747
4245
  });
2748
- pptxSlide.addText(badge.text.toUpperCase(), {
4246
+ pptxSlide.addText(badgeText.toUpperCase(), {
2749
4247
  x: rect.x,
2750
4248
  y: rect.y,
2751
4249
  w: badgeW,
@@ -2853,9 +4351,10 @@ var PptxRenderer = class {
2853
4351
  align: "center"
2854
4352
  });
2855
4353
  }
4354
+ const cleanTitle = item.title;
2856
4355
  const descText = item.description ? `
2857
4356
  ${item.description}` : "";
2858
- pptxSlide.addText(`${item.title}${descText}`, {
4357
+ pptxSlide.addText(`${cleanTitle}${descText}`, {
2859
4358
  x: itemX + 0.05,
2860
4359
  y: lineY + 0.45,
2861
4360
  w: itemW - 0.1,
@@ -2879,7 +4378,7 @@ ${item.description}` : "";
2879
4378
  w: colW,
2880
4379
  h: rect.h,
2881
4380
  fill: { color: this.cleanHexColor(theme.colors.surface) },
2882
- line: { color: this.cleanHexColor(theme.colors.border), width: 1 },
4381
+ line: { color: this.cleanHexColor(theme.colors.border), width: 1.5 },
2883
4382
  rectRadius: 0.1
2884
4383
  });
2885
4384
  if (compare.leftTitle) {
@@ -2888,34 +4387,244 @@ ${item.description}` : "";
2888
4387
  y: rect.y + 0.15,
2889
4388
  w: colW - 0.3,
2890
4389
  h: 0.4,
2891
- fontSize: 14,
4390
+ fontSize: 15,
4391
+ bold: true,
4392
+ color: this.cleanHexColor(theme.colors.primary),
4393
+ fontFace: cleanFontFace(theme.typography.headingFont)
4394
+ });
4395
+ }
4396
+ const rightX = rect.x + colW + 0.4;
4397
+ pptxSlide.addShape(pptx.ShapeType.roundRect, {
4398
+ x: rightX,
4399
+ y: rect.y,
4400
+ w: colW,
4401
+ h: rect.h,
4402
+ fill: { color: this.cleanHexColor(theme.colors.surface) },
4403
+ line: { color: this.cleanHexColor(theme.colors.border), width: 1.5 },
4404
+ rectRadius: 0.1
4405
+ });
4406
+ if (compare.rightTitle) {
4407
+ pptxSlide.addText(compare.rightTitle, {
4408
+ x: rightX + 0.15,
4409
+ y: rect.y + 0.15,
4410
+ w: colW - 0.3,
4411
+ h: 0.4,
4412
+ fontSize: 15,
2892
4413
  bold: true,
2893
4414
  color: this.cleanHexColor(theme.colors.primary),
2894
4415
  fontFace: cleanFontFace(theme.typography.headingFont)
2895
4416
  });
2896
4417
  }
2897
- const rightX = rect.x + colW + 0.4;
2898
- pptxSlide.addShape(pptx.ShapeType.roundRect, {
2899
- x: rightX,
2900
- y: rect.y,
2901
- w: colW,
2902
- h: rect.h,
2903
- fill: { color: this.cleanHexColor(theme.colors.surface) },
2904
- line: { color: this.cleanHexColor(theme.colors.border), width: 1 },
2905
- rectRadius: 0.1
2906
- });
2907
- if (compare.rightTitle) {
2908
- pptxSlide.addText(compare.rightTitle, {
2909
- x: rightX + 0.15,
2910
- y: rect.y + 0.15,
2911
- w: colW - 0.3,
2912
- h: 0.4,
2913
- fontSize: 14,
4418
+ const vsX = rect.x + colW + 0.05;
4419
+ pptxSlide.addShape(pptx.ShapeType.roundRect, {
4420
+ x: vsX,
4421
+ y: rect.y + rect.h / 2 - 0.18,
4422
+ w: 0.3,
4423
+ h: 0.3,
4424
+ fill: { color: this.cleanHexColor(theme.colors.border || "#334155") },
4425
+ rectRadius: 0.15
4426
+ });
4427
+ pptxSlide.addText("VS", {
4428
+ x: vsX,
4429
+ y: rect.y + rect.h / 2 - 0.18,
4430
+ w: 0.3,
4431
+ h: 0.3,
4432
+ fontSize: 10,
4433
+ bold: true,
4434
+ color: this.cleanHexColor(theme.colors.muted || "#94a3b8"),
4435
+ fontFace: cleanFontFace(theme.typography.headingFont),
4436
+ align: "center",
4437
+ valign: "middle"
4438
+ });
4439
+ }
4440
+ renderSection(pptxSlide, pptx, section, rect, theme) {
4441
+ const cardBg = this.cleanHexColor(theme.colors.surface || "#0f172a");
4442
+ const primaryHex = this.cleanHexColor(theme.colors.primary || "#00F0FF");
4443
+ pptxSlide.addShape(pptx.ShapeType.roundRect, {
4444
+ x: rect.x,
4445
+ y: rect.y,
4446
+ w: rect.w,
4447
+ h: rect.h,
4448
+ fill: { color: cardBg },
4449
+ line: { color: primaryHex, width: 2 },
4450
+ rectRadius: 0.1
4451
+ });
4452
+ if (section.number !== void 0) {
4453
+ const numStr = String(section.number);
4454
+ const pillW = Math.min(2.4, Math.max(1.4, numStr.length * 0.15 + 0.8));
4455
+ const pillX = rect.x + (rect.w - pillW) / 2;
4456
+ const pillY = rect.y + 0.35;
4457
+ pptxSlide.addShape(pptx.ShapeType.roundRect, {
4458
+ x: pillX,
4459
+ y: pillY,
4460
+ w: pillW,
4461
+ h: 0.36,
4462
+ fill: { color: primaryHex },
4463
+ rectRadius: 0.18
4464
+ });
4465
+ pptxSlide.addText(`SECTION ${numStr}`.toUpperCase(), {
4466
+ x: pillX,
4467
+ y: pillY,
4468
+ w: pillW,
4469
+ h: 0.36,
4470
+ fontSize: 11,
4471
+ bold: true,
4472
+ color: "000000",
4473
+ fontFace: cleanFontFace(theme.typography.headingFont),
4474
+ align: "center",
4475
+ valign: "middle"
4476
+ });
4477
+ }
4478
+ const titleY = section.number !== void 0 ? rect.y + 0.85 : rect.y + 0.45;
4479
+ pptxSlide.addText(section.title, {
4480
+ x: rect.x + 0.3,
4481
+ y: titleY,
4482
+ w: rect.w - 0.6,
4483
+ h: 0.85,
4484
+ fontSize: 28,
4485
+ bold: true,
4486
+ color: this.cleanHexColor(theme.colors.text || "#ffffff"),
4487
+ fontFace: cleanFontFace(theme.typography.headingFont),
4488
+ align: "center",
4489
+ valign: "middle"
4490
+ });
4491
+ if (section.subtitle) {
4492
+ pptxSlide.addText(section.subtitle, {
4493
+ x: rect.x + 0.4,
4494
+ y: titleY + 0.85,
4495
+ w: rect.w - 0.8,
4496
+ h: 0.55,
4497
+ fontSize: 15,
4498
+ color: this.cleanHexColor(theme.colors.muted || "#94a3b8"),
4499
+ fontFace: cleanFontFace(theme.typography.bodyFont),
4500
+ align: "center",
4501
+ valign: "top"
4502
+ });
4503
+ }
4504
+ }
4505
+ renderToc(pptxSlide, pptx, toc, rect, theme, presentation) {
4506
+ const items = toc.items ? [...toc.items] : [];
4507
+ if (items.length === 0 && presentation) {
4508
+ let autoIdx = 1;
4509
+ for (const s of presentation.slides) {
4510
+ for (const el of s.elements) {
4511
+ if (el.type === "section") {
4512
+ const sec = el;
4513
+ items.push({
4514
+ number: sec.number !== void 0 ? String(sec.number) : String(autoIdx++),
4515
+ title: sec.title,
4516
+ description: sec.subtitle
4517
+ });
4518
+ }
4519
+ }
4520
+ }
4521
+ if (items.length === 0) {
4522
+ presentation.slides.forEach((s, idx) => {
4523
+ const heading = s.elements.find((el) => el.type === "heading");
4524
+ if (heading) {
4525
+ items.push({
4526
+ number: String(idx + 1),
4527
+ title: heading.text
4528
+ });
4529
+ }
4530
+ });
4531
+ }
4532
+ }
4533
+ if (items.length === 0)
4534
+ return;
4535
+ const primaryHex = this.cleanHexColor(theme.colors.primary || "#00F0FF");
4536
+ const surfaceHex = this.cleanHexColor(theme.colors.surface || "#0f172a");
4537
+ const borderHex = this.cleanHexColor(theme.colors.border || "#334155");
4538
+ const textHex = this.cleanHexColor(theme.colors.text || "#ffffff");
4539
+ const mutedHex = this.cleanHexColor(theme.colors.muted || "#94a3b8");
4540
+ let currentY = rect.y;
4541
+ if (toc.title) {
4542
+ pptxSlide.addText(toc.title, {
4543
+ x: rect.x,
4544
+ y: currentY,
4545
+ w: rect.w,
4546
+ h: 0.5,
4547
+ fontSize: 22,
2914
4548
  bold: true,
2915
- color: this.cleanHexColor(theme.colors.primary),
4549
+ color: primaryHex,
2916
4550
  fontFace: cleanFontFace(theme.typography.headingFont)
2917
4551
  });
4552
+ currentY += 0.65;
2918
4553
  }
4554
+ const availableH = rect.h - (currentY - rect.y);
4555
+ const cols = items.length > 4 ? 2 : 1;
4556
+ const rows = Math.ceil(items.length / cols);
4557
+ const colW = (rect.w - (cols - 1) * 0.3) / cols;
4558
+ const itemH = Math.min(0.85, (availableH - (rows - 1) * 0.12) / rows);
4559
+ items.forEach((item, idx) => {
4560
+ const c = idx % cols;
4561
+ const r = Math.floor(idx / cols);
4562
+ const itemX = rect.x + c * (colW + 0.3);
4563
+ const itemY = currentY + r * (itemH + 0.12);
4564
+ const num = item.number !== void 0 ? String(item.number) : String(idx + 1);
4565
+ const descText = item.description || item.subtitle;
4566
+ pptxSlide.addShape(pptx.ShapeType.roundRect, {
4567
+ x: itemX,
4568
+ y: itemY,
4569
+ w: colW,
4570
+ h: itemH,
4571
+ fill: { color: surfaceHex },
4572
+ line: { color: borderHex, width: 1 },
4573
+ rectRadius: 0.08
4574
+ });
4575
+ pptxSlide.addShape(pptx.ShapeType.roundRect, {
4576
+ x: itemX + 0.12,
4577
+ y: itemY + (itemH - 0.36) / 2,
4578
+ w: 0.36,
4579
+ h: 0.36,
4580
+ fill: { color: primaryHex },
4581
+ rectRadius: 0.08
4582
+ });
4583
+ pptxSlide.addText(num, {
4584
+ x: itemX + 0.12,
4585
+ y: itemY + (itemH - 0.36) / 2,
4586
+ w: 0.36,
4587
+ h: 0.36,
4588
+ fontSize: 11,
4589
+ bold: true,
4590
+ color: "000000",
4591
+ fontFace: cleanFontFace(theme.typography.headingFont),
4592
+ align: "center",
4593
+ valign: "middle"
4594
+ });
4595
+ const textX = itemX + 0.55;
4596
+ const textW = colW - 0.65;
4597
+ const titleChunks = [
4598
+ {
4599
+ text: item.title,
4600
+ options: {
4601
+ fontSize: 13,
4602
+ bold: true,
4603
+ color: textHex,
4604
+ fontFace: cleanFontFace(theme.typography.headingFont),
4605
+ breakLine: Boolean(descText)
4606
+ }
4607
+ }
4608
+ ];
4609
+ if (descText) {
4610
+ titleChunks.push({
4611
+ text: descText,
4612
+ options: {
4613
+ fontSize: 10,
4614
+ color: mutedHex,
4615
+ fontFace: cleanFontFace(theme.typography.bodyFont)
4616
+ }
4617
+ });
4618
+ }
4619
+ pptxSlide.addText(titleChunks, {
4620
+ x: textX,
4621
+ y: itemY + 0.06,
4622
+ w: textW,
4623
+ h: itemH - 0.12,
4624
+ valign: "middle",
4625
+ margin: 0
4626
+ });
4627
+ });
2919
4628
  }
2920
4629
  renderMermaid(pptxSlide, pptx, mermaid, rect, theme) {
2921
4630
  pptxSlide.addShape(pptx.ShapeType.roundRect, {
@@ -3076,7 +4785,9 @@ var HtmlRenderer = class {
3076
4785
  })();
3077
4786
  </script>
3078
4787
  ` : "";
3079
- const slidesHtml = presentation.slides.map((slide, idx) => this.renderSlide(slide, idx + 1, presentation.slides.length, theme)).join("\n");
4788
+ const slidesHtml = presentation.slides.map((slide, idx) => this.renderSlide(slide, idx + 1, presentation.slides.length, theme, presentation.metadata.watermark, presentation)).join("\n");
4789
+ const customStyles = presentation.metadata.styles ? (Array.isArray(presentation.metadata.styles) ? presentation.metadata.styles : [presentation.metadata.styles]).map((url) => `<link rel="stylesheet" href="${url}">`).join("\n ") : "";
4790
+ const customScripts = presentation.metadata.scripts ? (Array.isArray(presentation.metadata.scripts) ? presentation.metadata.scripts : [presentation.metadata.scripts]).map((url) => `<script src="${url}"></script>`).join("\n ") : "";
3080
4791
  const html = `<!DOCTYPE html>
3081
4792
  <html lang="en">
3082
4793
  <head>
@@ -3085,6 +4796,11 @@ var HtmlRenderer = class {
3085
4796
  <title>${this.escapeHtml(title)}</title>
3086
4797
  <link rel="preconnect" href="https://fonts.googleapis.com">
3087
4798
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
4799
+ ${customStyles}
4800
+ ${customScripts}
4801
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" crossorigin="anonymous">
4802
+ <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" crossorigin="anonymous"></script>
4803
+ <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" crossorigin="anonymous"></script>
3088
4804
  <script type="module">
3089
4805
  import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
3090
4806
  try {
@@ -3246,9 +4962,6 @@ var HtmlRenderer = class {
3246
4962
  margin-bottom: 0.8em;
3247
4963
  overflow-wrap: break-word;
3248
4964
  }
3249
- color: var(--yumia-text);
3250
- margin-bottom: 0.8em;
3251
- }
3252
4965
 
3253
4966
  p strong, li strong {
3254
4967
  color: var(--yumia-primary);
@@ -3470,7 +5183,7 @@ var HtmlRenderer = class {
3470
5183
  padding: 6px 14px;
3471
5184
  border-radius: 30px;
3472
5185
  border: 1px solid rgba(255, 255, 255, 0.15);
3473
- z-index: 1000;
5186
+ z-index: 2500;
3474
5187
  font-size: 13px;
3475
5188
  color: #e2e8f0;
3476
5189
  box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
@@ -3685,20 +5398,40 @@ var HtmlRenderer = class {
3685
5398
  border: 1.5px solid var(--yumia-border);
3686
5399
  border-left: 4px solid var(--yumia-primary);
3687
5400
  border-radius: var(--yumia-radius-card);
3688
- padding: 1.25rem 2rem;
3689
- margin: 1.2rem 0;
5401
+ padding: 0.75rem 1.4rem;
5402
+ margin: 0.7rem 0;
3690
5403
  display: flex;
3691
5404
  align-items: center;
3692
5405
  justify-content: center;
3693
5406
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.25);
5407
+ overflow-x: auto;
5408
+ max-width: 100%;
3694
5409
  }
3695
5410
 
3696
5411
  .yumia-math-equation {
3697
- font-family: 'Cambria Math', 'JetBrains Mono', monospace;
3698
- font-size: clamp(1.2rem, 2.2vw, 1.8rem);
3699
- font-style: italic;
5412
+ font-size: clamp(1.1rem, 1.8vw, 1.45rem);
3700
5413
  color: var(--yumia-text);
3701
- letter-spacing: 0.05em;
5414
+ letter-spacing: 0.03em;
5415
+ }
5416
+
5417
+ /* Watermark Branding */
5418
+ .yumia-watermark {
5419
+ position: absolute;
5420
+ bottom: 12px;
5421
+ left: 20px;
5422
+ font-size: 11px;
5423
+ font-weight: 600;
5424
+ letter-spacing: 0.08em;
5425
+ text-transform: uppercase;
5426
+ color: var(--yumia-muted);
5427
+ opacity: 0.35;
5428
+ pointer-events: none;
5429
+ user-select: none;
5430
+ z-index: 10;
5431
+ transition: opacity 0.2s ease;
5432
+ }
5433
+ .yumia-slide-wrapper:hover .yumia-watermark {
5434
+ opacity: 0.65;
3702
5435
  }
3703
5436
 
3704
5437
  /* Slide Transitions */
@@ -3728,17 +5461,177 @@ var HtmlRenderer = class {
3728
5461
  to { opacity: 1; transform: scale(1); }
3729
5462
  }
3730
5463
 
3731
- /* Progressive Reveal (Step Builds) */
3732
- .yumia-slide-wrapper [data-step] {
3733
- transition: opacity 0.25s ease, transform 0.25s ease;
5464
+ /* Code Block Line Highlighting & Numbers */
5465
+ .yumia-code-block {
5466
+ display: block;
5467
+ padding: 0.75rem 0;
5468
+ font-family: var(--yumia-font-code);
5469
+ font-size: 0.95rem;
5470
+ line-height: 1.6;
5471
+ overflow-x: auto;
5472
+ border-radius: var(--yumia-radius-card);
5473
+ background: rgba(10, 15, 30, 0.88);
5474
+ border: 1.5px solid var(--yumia-border);
5475
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.35);
5476
+ margin: 0.8rem 0;
5477
+ }
5478
+ .yumia-code-block code {
5479
+ display: block;
5480
+ padding: 0;
5481
+ background: transparent;
5482
+ border: none;
5483
+ }
5484
+ .yumia-code-line {
5485
+ display: flex;
5486
+ align-items: center;
5487
+ min-height: 1.6em;
5488
+ padding: 0 1rem;
5489
+ transition: background 0.15s ease, opacity 0.15s ease;
3734
5490
  }
3735
- .yumia-slide-wrapper:not(.step-active) [data-step] {
3736
- opacity: 0.2;
3737
- transform: translateY(4px);
5491
+ .yumia-code-line.dimmed {
5492
+ opacity: 0.35;
3738
5493
  }
3739
- .yumia-slide-wrapper.step-active [data-step] {
5494
+ .yumia-code-line.highlighted {
5495
+ background: rgba(0, 240, 255, 0.16);
5496
+ border-left: 3.5px solid var(--yumia-primary);
3740
5497
  opacity: 1;
3741
- transform: translateY(0);
5498
+ font-weight: 600;
5499
+ }
5500
+ .yumia-code-line .line-num {
5501
+ user-select: none;
5502
+ width: 28px;
5503
+ margin-right: 14px;
5504
+ color: rgba(255, 255, 255, 0.3);
5505
+ font-size: 0.8rem;
5506
+ text-align: right;
5507
+ }
5508
+ .yumia-code-line .line-text {
5509
+ flex: 1;
5510
+ }
5511
+
5512
+ /* Section Divider Card */
5513
+ .yumia-section-card {
5514
+ display: flex;
5515
+ flex-direction: column;
5516
+ align-items: center;
5517
+ justify-content: center;
5518
+ text-align: center;
5519
+ padding: 3rem 2rem;
5520
+ margin: auto 0;
5521
+ background: radial-gradient(circle at center, rgba(0, 240, 255, 0.12) 0%, rgba(15, 23, 42, 0.6) 100%);
5522
+ border: 1.5px solid var(--yumia-primary);
5523
+ border-radius: var(--yumia-radius-card);
5524
+ box-shadow: 0 12px 35px rgba(0, 0, 0, 0.5), 0 0 20px rgba(0, 240, 255, 0.2);
5525
+ width: 100%;
5526
+ }
5527
+ .yumia-section-pill {
5528
+ display: inline-flex;
5529
+ align-items: center;
5530
+ padding: 4px 14px;
5531
+ border-radius: 999px;
5532
+ background: var(--yumia-primary);
5533
+ color: #000;
5534
+ font-weight: 800;
5535
+ font-size: 0.85rem;
5536
+ letter-spacing: 0.08em;
5537
+ text-transform: uppercase;
5538
+ margin-bottom: 1.2rem;
5539
+ }
5540
+ .yumia-section-title {
5541
+ font-size: clamp(2rem, 3.5vw, 3rem);
5542
+ font-weight: 800;
5543
+ color: var(--yumia-text);
5544
+ margin-bottom: 0.8rem;
5545
+ line-height: 1.2;
5546
+ }
5547
+ .yumia-section-subtitle {
5548
+ font-size: 1.15rem;
5549
+ color: var(--yumia-muted);
5550
+ max-width: 650px;
5551
+ }
5552
+
5553
+ /* Table of Contents Grid */
5554
+ .yumia-toc-grid {
5555
+ display: grid;
5556
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
5557
+ gap: 16px;
5558
+ width: 100%;
5559
+ margin: 1.2rem 0;
5560
+ }
5561
+ .yumia-toc-item {
5562
+ display: flex;
5563
+ align-items: center;
5564
+ gap: 14px;
5565
+ background: var(--yumia-surface);
5566
+ border: 1.5px solid var(--yumia-border);
5567
+ border-radius: 10px;
5568
+ padding: 14px 18px;
5569
+ transition: transform 0.2s ease, border-color 0.2s ease;
5570
+ }
5571
+ .yumia-toc-item:hover {
5572
+ transform: translateX(4px);
5573
+ border-color: var(--yumia-primary);
5574
+ }
5575
+ .yumia-toc-num {
5576
+ display: flex;
5577
+ align-items: center;
5578
+ justify-content: center;
5579
+ width: 34px;
5580
+ height: 34px;
5581
+ border-radius: 8px;
5582
+ background: rgba(0, 240, 255, 0.15);
5583
+ color: var(--yumia-primary);
5584
+ font-weight: 800;
5585
+ font-size: 0.95rem;
5586
+ }
5587
+ .yumia-toc-title {
5588
+ font-weight: 600;
5589
+ font-size: 1.05rem;
5590
+ color: var(--yumia-text);
5591
+ }
5592
+
5593
+ /* Print & Export Media Styles */
5594
+ @media print {
5595
+ @page {
5596
+ size: landscape;
5597
+ margin: 0;
5598
+ }
5599
+ body {
5600
+ background: #0f172a !important;
5601
+ color: #f8fafc !important;
5602
+ overflow: visible !important;
5603
+ -webkit-print-color-adjust: exact !important;
5604
+ print-color-adjust: exact !important;
5605
+ }
5606
+ #deck-container {
5607
+ display: block !important;
5608
+ width: 100% !important;
5609
+ height: auto !important;
5610
+ }
5611
+ #yumia-deck {
5612
+ display: block !important;
5613
+ position: static !important;
5614
+ width: 100% !important;
5615
+ height: auto !important;
5616
+ }
5617
+ .yumia-slide-wrapper {
5618
+ display: flex !important;
5619
+ page-break-after: always !important;
5620
+ break-after: page !important;
5621
+ page-break-inside: avoid !important;
5622
+ position: relative !important;
5623
+ width: 100vw !important;
5624
+ height: 100vh !important;
5625
+ min-height: 100vh !important;
5626
+ margin: 0 !important;
5627
+ box-shadow: none !important;
5628
+ opacity: 1 !important;
5629
+ visibility: visible !important;
5630
+ transform: none !important;
5631
+ }
5632
+ .yumia-controls, .yumia-notes-drawer, .yumia-overview-modal, .yumia-progress-bar {
5633
+ display: none !important;
5634
+ }
3742
5635
  }
3743
5636
 
3744
5637
  /* Notes Drawer */
@@ -3747,11 +5640,11 @@ var HtmlRenderer = class {
3747
5640
  bottom: 0;
3748
5641
  left: 0;
3749
5642
  right: 0;
3750
- max-height: 240px;
5643
+ max-height: 260px;
3751
5644
  background: rgba(10, 10, 18, 0.96);
3752
5645
  backdrop-filter: blur(16px);
3753
5646
  border-top: 2px solid var(--yumia-primary);
3754
- padding: 18px 28px;
5647
+ padding: 14px 24px 18px 24px;
3755
5648
  overflow-y: auto;
3756
5649
  display: none;
3757
5650
  z-index: 2000;
@@ -3765,6 +5658,22 @@ var HtmlRenderer = class {
3765
5658
  display: block;
3766
5659
  }
3767
5660
 
5661
+ .yumia-notes-header {
5662
+ display: flex;
5663
+ justify-content: space-between;
5664
+ align-items: center;
5665
+ margin-bottom: 8px;
5666
+ padding-bottom: 6px;
5667
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
5668
+ font-weight: 700;
5669
+ font-size: 14px;
5670
+ color: var(--yumia-primary);
5671
+ }
5672
+
5673
+ .yumia-notes-content {
5674
+ padding-right: 200px;
5675
+ }
5676
+
3768
5677
  /* Slide Overview Modal */
3769
5678
  .yumia-overview-modal {
3770
5679
  position: fixed;
@@ -3952,11 +5861,18 @@ var HtmlRenderer = class {
3952
5861
  <button class="yumia-btn" id="btn-next" title="Next Slide (\u2192)">\u25B6</button>
3953
5862
  <button class="yumia-btn" id="btn-overview" title="Slide Overview (ESC / O)">\u25A6</button>
3954
5863
  <button class="yumia-btn" id="btn-speaker" title="Speaker View (S)">\u{1F3A4}</button>
5864
+ <button class="yumia-btn" id="btn-print" title="Stampa / Salva PDF (Ctrl+P)">\u{1F5A8}\uFE0F</button>
3955
5865
  <button class="yumia-btn" id="btn-notes" title="Toggle Notes (N)">\u{1F4DD}</button>
3956
5866
  <button class="yumia-btn" id="btn-fs" title="Fullscreen (F)">\u26F6</button>
3957
5867
  </div>
3958
5868
 
3959
- <div id="notes-drawer" class="yumia-notes-drawer"></div>
5869
+ <div id="notes-drawer" class="yumia-notes-drawer">
5870
+ <div class="yumia-notes-header">
5871
+ <span>\u{1F4DD} Speaker Notes</span>
5872
+ <button class="yumia-btn" id="btn-close-notes" title="Close Notes (N / ESC)" style="font-size: 13px; padding: 2px 8px; border: 1px solid rgba(255,255,255,0.2);">\u2715 Close</button>
5873
+ </div>
5874
+ <div id="notes-drawer-content" class="yumia-notes-content"></div>
5875
+ </div>
3960
5876
 
3961
5877
  <div id="overview-modal" class="yumia-overview-modal">
3962
5878
  <div class="yumia-overview-header">
@@ -4043,8 +5959,9 @@ var HtmlRenderer = class {
4043
5959
 
4044
5960
  const currentSlide = slides[currentIdx];
4045
5961
  const notes = currentSlide ? currentSlide.getAttribute('data-notes') : '';
4046
- if (notesDrawer) {
4047
- notesDrawer.innerHTML = notes ? '<strong>Speaker Notes:</strong><br>' + notes : '<em>No speaker notes for this slide.</em>';
5962
+ const notesContent = document.getElementById('notes-drawer-content');
5963
+ if (notesContent) {
5964
+ notesContent.innerHTML = notes ? notes : '<em>No speaker notes for this slide.</em>';
4048
5965
  }
4049
5966
 
4050
5967
  window.location.hash = '#' + (currentIdx + 1);
@@ -4057,9 +5974,40 @@ var HtmlRenderer = class {
4057
5974
  if (window.renderMermaidInSlide && slides[currentIdx]) {
4058
5975
  window.renderMermaidInSlide(slides[currentIdx]);
4059
5976
  }
5977
+ if (slides[currentIdx]) {
5978
+ renderMathInSlide(slides[currentIdx]);
5979
+ }
4060
5980
  }, 50);
4061
5981
  }
4062
5982
 
5983
+ function renderMathInSlide(slideEl) {
5984
+ if (!slideEl) return;
5985
+ if (window.renderMathInElement) {
5986
+ try {
5987
+ window.renderMathInElement(slideEl, {
5988
+ delimiters: [
5989
+ { left: '$$', right: '$$', display: true }
5990
+ ],
5991
+ throwOnError: false
5992
+ });
5993
+ } catch (e) {
5994
+ console.warn('KaTeX auto-render notice:', e);
5995
+ }
5996
+ }
5997
+ if (window.katex) {
5998
+ slideEl.querySelectorAll('.yumia-math-equation:not([data-katex="true"])').forEach(function(el) {
5999
+ try {
6000
+ const rawExpr =
6001
+ el.getAttribute('data-expr') || el.textContent.replace(/^[$]{2}|[$]{2}$/g, '').trim();
6002
+ window.katex.render(rawExpr, el, { displayMode: true, throwOnError: false });
6003
+ el.setAttribute('data-katex', 'true');
6004
+ } catch (err) {
6005
+ console.warn('KaTeX equation render notice:', err);
6006
+ }
6007
+ });
6008
+ }
6009
+ }
6010
+
4063
6011
  function openSpeakerWindow() {
4064
6012
  const url = new URL(window.location.href);
4065
6013
  url.searchParams.set('speaker', 'true');
@@ -4208,8 +6156,15 @@ var HtmlRenderer = class {
4208
6156
  const hash = window.location.hash.replace('#', '');
4209
6157
  const initialIdx = parseInt(hash, 10) - 1;
4210
6158
  goToSlide(isNaN(initialIdx) ? 0 : initialIdx, false);
6159
+ setTimeout(() => {
6160
+ slides.forEach(renderMathInSlide);
6161
+ }, 100);
4211
6162
  }
4212
6163
 
6164
+ window.addEventListener('load', () => {
6165
+ slides.forEach(renderMathInSlide);
6166
+ });
6167
+
4213
6168
  window.deckController = {
4214
6169
  init: init,
4215
6170
  next: function() { goToSlide(currentIdx + 1); },
@@ -4217,6 +6172,7 @@ var HtmlRenderer = class {
4217
6172
  toggleNotes: function() { notesDrawer?.classList.toggle('open'); },
4218
6173
  toggleOverview: function() { toggleOverview(); },
4219
6174
  openSpeaker: openSpeakerWindow,
6175
+ print: function() { window.print(); },
4220
6176
  toggleFs: function() {
4221
6177
  if (!document.fullscreenElement) {
4222
6178
  document.documentElement.requestFullscreen().catch(() => {});
@@ -4229,9 +6185,13 @@ var HtmlRenderer = class {
4229
6185
  document.getElementById('btn-next')?.addEventListener('click', window.deckController.next);
4230
6186
  document.getElementById('btn-prev')?.addEventListener('click', window.deckController.prev);
4231
6187
  document.getElementById('btn-notes')?.addEventListener('click', window.deckController.toggleNotes);
6188
+ document.getElementById('btn-close-notes')?.addEventListener('click', () => {
6189
+ notesDrawer?.classList.remove('open');
6190
+ });
4232
6191
  document.getElementById('btn-overview')?.addEventListener('click', window.deckController.toggleOverview);
4233
6192
  document.getElementById('btn-close-overview')?.addEventListener('click', () => toggleOverview(false));
4234
6193
  document.getElementById('btn-speaker')?.addEventListener('click', window.deckController.openSpeaker);
6194
+ document.getElementById('btn-print')?.addEventListener('click', window.deckController.print);
4235
6195
  document.getElementById('btn-fs')?.addEventListener('click', window.deckController.toggleFs);
4236
6196
 
4237
6197
  window.addEventListener('keydown', function(e) {
@@ -4242,6 +6202,18 @@ var HtmlRenderer = class {
4242
6202
  return;
4243
6203
  }
4244
6204
 
6205
+ if (e.key === 'Escape') {
6206
+ if (notesDrawer && notesDrawer.classList.contains('open')) {
6207
+ notesDrawer.classList.remove('open');
6208
+ return;
6209
+ }
6210
+ }
6211
+
6212
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'p') {
6213
+ // let default browser print work or call print explicitly
6214
+ return;
6215
+ }
6216
+
4245
6217
  if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown' || e.key.toLowerCase() === 'l') {
4246
6218
  e.preventDefault();
4247
6219
  window.deckController.next();
@@ -4252,6 +6224,8 @@ var HtmlRenderer = class {
4252
6224
  window.deckController.toggleFs();
4253
6225
  } else if (e.key.toLowerCase() === 's') {
4254
6226
  window.deckController.openSpeaker();
6227
+ } else if (e.key.toLowerCase() === 'p') {
6228
+ window.deckController.print();
4255
6229
  } else if (e.key.toLowerCase() === 'n') {
4256
6230
  window.deckController.toggleNotes();
4257
6231
  } else if (e.key === 'Escape' || e.key.toLowerCase() === 'o') {
@@ -4282,21 +6256,25 @@ var HtmlRenderer = class {
4282
6256
  slideCount: presentation.slides.length
4283
6257
  };
4284
6258
  }
4285
- renderSlide(slide, slideNum, totalSlides, theme) {
6259
+ renderSlide(slide, slideNum, totalSlides, theme, watermarkOpt, presentation) {
4286
6260
  const activeClass = slideNum === 1 ? "active" : "";
4287
6261
  const notesAttr = slide.notes ? this.escapeHtml(slide.notes.replace(/\n/g, "<br>")) : "";
4288
6262
  const progressPercent = Math.round(slideNum / totalSlides * 100);
4289
6263
  const transition = slide.transition || "fade";
4290
6264
  const transType = typeof transition === "string" ? transition : transition.type;
4291
6265
  const transClass = `transition-${transType || "fade"}`;
4292
- const elementsHtml = slide.elements.map((el) => this.renderElement(el, theme)).join("\n");
6266
+ const isWatermarkEnabled = watermarkOpt !== false;
6267
+ const watermarkText = typeof watermarkOpt === "string" && watermarkOpt.trim().length > 0 ? watermarkOpt : "YumiaMD";
6268
+ const watermarkHtml = isWatermarkEnabled ? `<div class="yumia-watermark">${this.escapeHtml(watermarkText)}</div>` : "";
6269
+ const elementsHtml = slide.elements.map((el) => this.renderElement(el, theme, presentation)).join("\n");
4293
6270
  return `
4294
6271
  <div class="yumia-slide-wrapper ${activeClass} ${transClass}" id="slide-${slideNum}" data-notes="${notesAttr}">
4295
6272
  ${elementsHtml}
6273
+ ${watermarkHtml}
4296
6274
  <div class="yumia-progress-bar" style="width: ${progressPercent}%;"></div>
4297
6275
  </div>`;
4298
6276
  }
4299
- renderElement(element, theme) {
6277
+ renderElement(element, theme, presentation) {
4300
6278
  switch (element.type) {
4301
6279
  case "heading": {
4302
6280
  const h = element;
@@ -4316,8 +6294,100 @@ var HtmlRenderer = class {
4316
6294
  case "code": {
4317
6295
  const c = element;
4318
6296
  const langClass = c.language ? `class="language-${c.language}"` : "";
6297
+ const lines = c.code.split("\n");
6298
+ if (c.highlight) {
6299
+ const highlightSet = /* @__PURE__ */ new Set();
6300
+ const parts = c.highlight.split(",");
6301
+ for (const part of parts) {
6302
+ const trimmed = part.trim();
6303
+ if (!trimmed)
6304
+ continue;
6305
+ if (trimmed.includes("-")) {
6306
+ const [startStr, endStr] = trimmed.split("-");
6307
+ const start = parseInt(startStr || "0", 10);
6308
+ const end = parseInt(endStr || "0", 10);
6309
+ if (!isNaN(start) && !isNaN(end) && start <= end) {
6310
+ for (let n = start; n <= end; n++)
6311
+ highlightSet.add(n);
6312
+ }
6313
+ } else {
6314
+ const num = parseInt(trimmed, 10);
6315
+ if (!isNaN(num))
6316
+ highlightSet.add(num);
6317
+ }
6318
+ }
6319
+ const linesHtml = lines.map((line, idx) => {
6320
+ const lineNum = idx + 1;
6321
+ const isHl = highlightSet.has(lineNum);
6322
+ const hlClass = isHl ? "highlighted" : "dimmed";
6323
+ return `<div class="yumia-code-line ${hlClass}"><span class="line-num">${lineNum}</span><span class="line-text">${this.escapeHtml(line) || "&nbsp;"}</span></div>`;
6324
+ }).join("");
6325
+ return `<pre class="yumia-code-block"><code ${langClass}>${linesHtml}</code></pre>`;
6326
+ }
4319
6327
  return `<pre><code ${langClass}>${this.escapeHtml(c.code)}</code></pre>`;
4320
6328
  }
6329
+ case "section": {
6330
+ const s = element;
6331
+ const numStr = s.number !== void 0 ? `<div class="yumia-section-pill">SECTION ${this.escapeHtml(String(s.number))}</div>` : "";
6332
+ const subStr = s.subtitle ? `<div class="yumia-section-subtitle">${this.formatInline(s.subtitle)}</div>` : "";
6333
+ return `
6334
+ <div class="yumia-section-card">
6335
+ ${numStr}
6336
+ <div class="yumia-section-title">${this.formatInline(s.title)}</div>
6337
+ ${subStr}
6338
+ </div>`;
6339
+ }
6340
+ case "toc": {
6341
+ const t = element;
6342
+ const items = t.items ? [...t.items] : [];
6343
+ if (items.length === 0 && presentation) {
6344
+ let autoSecIdx = 1;
6345
+ for (const s of presentation.slides) {
6346
+ for (const el of s.elements) {
6347
+ if (el.type === "section") {
6348
+ const sec = el;
6349
+ items.push({
6350
+ number: sec.number !== void 0 ? String(sec.number) : String(autoSecIdx++),
6351
+ title: sec.title,
6352
+ description: sec.subtitle
6353
+ });
6354
+ }
6355
+ }
6356
+ }
6357
+ if (items.length === 0) {
6358
+ presentation.slides.forEach((s, idx) => {
6359
+ const heading = s.elements.find((el) => el.type === "heading");
6360
+ if (heading) {
6361
+ items.push({
6362
+ number: String(idx + 1),
6363
+ title: heading.text
6364
+ });
6365
+ }
6366
+ });
6367
+ }
6368
+ }
6369
+ const titleHtml = t.title ? `<h2 style="margin-bottom:1.5rem;">${this.escapeHtml(t.title)}</h2>` : "";
6370
+ const itemsHtml = items.map((it, idx) => {
6371
+ const num = it.number !== void 0 ? String(it.number) : String(idx + 1);
6372
+ const descText = it.description || it.subtitle;
6373
+ const desc = descText ? `<div style="font-size:0.85rem; color:var(--yumia-muted); margin-top:2px;">${this.formatInline(descText)}</div>` : "";
6374
+ return `
6375
+ <div class="yumia-toc-item">
6376
+ <div class="yumia-toc-num">${this.escapeHtml(num)}</div>
6377
+ <div>
6378
+ <div class="yumia-toc-title">${this.formatInline(it.title)}</div>
6379
+ ${desc}
6380
+ </div>
6381
+ </div>`;
6382
+ }).join("\n");
6383
+ return `
6384
+ <div class="yumia-toc-container">
6385
+ ${titleHtml}
6386
+ <div class="yumia-toc-grid">
6387
+ ${itemsHtml}
6388
+ </div>
6389
+ </div>`;
6390
+ }
4321
6391
  case "quote": {
4322
6392
  const q = element;
4323
6393
  const author = q.author ? `<br><small>\u2014 ${this.escapeHtml(q.author)}</small>` : "";
@@ -4393,9 +6463,10 @@ var HtmlRenderer = class {
4393
6463
  }
4394
6464
  case "math": {
4395
6465
  const mathEl = element;
6466
+ const expr = mathEl.expression;
4396
6467
  return `
4397
6468
  <div class="yumia-math-container">
4398
- <div class="yumia-math-equation">${this.escapeHtml(mathEl.expression)}</div>
6469
+ <div class="yumia-math-equation" data-expr="${this.escapeHtml(expr)}">$$${this.escapeHtml(expr)}$$</div>
4399
6470
  </div>`;
4400
6471
  }
4401
6472
  case "chart": {
@@ -4404,6 +6475,27 @@ var HtmlRenderer = class {
4404
6475
  case "timeline": {
4405
6476
  return this.renderTimeline(element);
4406
6477
  }
6478
+ case "icon": {
6479
+ const ic = element;
6480
+ const iconSvg = defaultIconResolver.toSvg(ic.name, ic.size || 24, ic.color || "currentColor", "yumia-icon");
6481
+ return `<span class="yumia-icon-wrapper" style="display:inline-flex; align-items:center; vertical-align:middle;">${iconSvg}</span>`;
6482
+ }
6483
+ case "grid": {
6484
+ const g = element;
6485
+ const cols = typeof g.columns === "number" ? `repeat(${g.columns}, 1fr)` : g.columns;
6486
+ const gap = g.gap !== void 0 ? typeof g.gap === "number" ? `${g.gap}px` : g.gap : "1.5rem";
6487
+ const inner = g.elements.map((child) => this.renderElement(child, theme, presentation)).join("\n");
6488
+ return `<div class="yumia-grid" style="display:grid; grid-template-columns:${cols}; gap:${gap}; width:100%;">${inner}</div>`;
6489
+ }
6490
+ case "stack": {
6491
+ const st = element;
6492
+ const dir = st.direction === "horizontal" ? "row" : "column";
6493
+ const gap = st.gap !== void 0 ? typeof st.gap === "number" ? `${st.gap}px` : st.gap : "1rem";
6494
+ const align = st.align ? `align-items:${st.align};` : "";
6495
+ const justify = st.justify ? `justify-content:${st.justify};` : "";
6496
+ const inner = st.elements.map((child) => this.renderElement(child, theme, presentation)).join("\n");
6497
+ return `<div class="yumia-stack" style="display:flex; flex-direction:${dir}; gap:${gap}; ${align} ${justify} width:100%;">${inner}</div>`;
6498
+ }
4407
6499
  case "compare": {
4408
6500
  return this.renderCompare(element, theme);
4409
6501
  }
@@ -4557,8 +6649,7 @@ var HtmlRenderer = class {
4557
6649
  </div>`;
4558
6650
  }
4559
6651
  formatInline(text) {
4560
- const escaped = this.escapeHtml(text);
4561
- return escaped.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\*(.*?)\*/g, "<em>$1</em>").replace(/`(.*?)`/g, "<code>$1</code>");
6652
+ return this.escapeHtml(text).replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\*(.*?)\*/g, "<em>$1</em>").replace(/`(.*?)`/g, "<code>$1</code>");
4562
6653
  }
4563
6654
  escapeHtml(str) {
4564
6655
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
@@ -4603,7 +6694,7 @@ var PdfRenderer = class {
4603
6694
  const totalSlides = presentation.slides.length;
4604
6695
  for (let i = 0; i < totalSlides; i++) {
4605
6696
  const slide = presentation.slides[i];
4606
- this.renderSlide(doc, slide, i + 1, totalSlides, pageWidth, pageHeight, theme);
6697
+ this.renderSlide(doc, slide, i + 1, totalSlides, pageWidth, pageHeight, theme, presentation);
4607
6698
  }
4608
6699
  doc.end();
4609
6700
  } catch (err) {
@@ -4611,7 +6702,7 @@ var PdfRenderer = class {
4611
6702
  }
4612
6703
  });
4613
6704
  }
4614
- renderSlide(doc, slide, slideNum, totalSlides, pageWidth, pageHeight, theme) {
6705
+ renderSlide(doc, slide, slideNum, totalSlides, pageWidth, pageHeight, theme, presentation) {
4615
6706
  doc.addPage({
4616
6707
  size: [pageWidth, pageHeight],
4617
6708
  margins: { top: 0, bottom: 0, left: 0, right: 0 }
@@ -4622,7 +6713,7 @@ var PdfRenderer = class {
4622
6713
  const contentWidth = pageWidth - padX * 2;
4623
6714
  let cursorY = padY;
4624
6715
  for (const element of slide.elements) {
4625
- cursorY = this.renderElement(doc, element, padX, cursorY, contentWidth, theme);
6716
+ cursorY = this.renderElement(doc, element, padX, cursorY, contentWidth, theme, presentation);
4626
6717
  cursorY += 12;
4627
6718
  }
4628
6719
  const progressWidth = slideNum / totalSlides * pageWidth;
@@ -4632,7 +6723,7 @@ var PdfRenderer = class {
4632
6723
  align: "right"
4633
6724
  });
4634
6725
  }
4635
- renderElement(doc, element, x, y, width, theme) {
6726
+ renderElement(doc, element, x, y, width, theme, presentation) {
4636
6727
  switch (element.type) {
4637
6728
  case "heading": {
4638
6729
  const h = element;
@@ -4684,12 +6775,42 @@ var PdfRenderer = class {
4684
6775
  }
4685
6776
  case "code": {
4686
6777
  const c = element;
4687
- const codeText = c.code;
4688
- doc.font("Courier").fontSize(11);
4689
- const textHeight = doc.heightOfString(codeText, { width: width - 24 });
4690
- const boxHeight = textHeight + 20;
6778
+ const lines = c.code.split("\n");
6779
+ doc.font("Courier").fontSize(10);
6780
+ const lineHeight = 14;
6781
+ const boxHeight = lines.length * lineHeight + 20;
4691
6782
  doc.roundedRect(x, y, width, boxHeight, 6).fill("#0a0a10").strokeColor(theme.colors.border || "rgba(255,255,255,0.1)").stroke();
4692
- doc.font("Courier").fontSize(11).fillColor(theme.colors.accent || "#38bdf8").text(codeText, x + 12, y + 10, { width: width - 24 });
6783
+ const highlightSet = /* @__PURE__ */ new Set();
6784
+ if (c.highlight) {
6785
+ const parts = c.highlight.split(",");
6786
+ for (const part of parts) {
6787
+ const trimmed = part.trim();
6788
+ if (!trimmed)
6789
+ continue;
6790
+ if (trimmed.includes("-")) {
6791
+ const [startStr, endStr] = trimmed.split("-");
6792
+ const start = parseInt(startStr || "0", 10);
6793
+ const end = parseInt(endStr || "0", 10);
6794
+ if (!isNaN(start) && !isNaN(end) && start <= end) {
6795
+ for (let n = start; n <= end; n++)
6796
+ highlightSet.add(n);
6797
+ }
6798
+ } else {
6799
+ const num = parseInt(trimmed, 10);
6800
+ if (!isNaN(num))
6801
+ highlightSet.add(num);
6802
+ }
6803
+ }
6804
+ }
6805
+ lines.forEach((line, idx) => {
6806
+ const lineNum = idx + 1;
6807
+ const lineY = y + 10 + idx * lineHeight;
6808
+ const isHl = highlightSet.has(lineNum);
6809
+ const numColor = isHl ? theme.colors.primary : theme.colors.muted || "#555566";
6810
+ const textColor = c.highlight ? isHl ? theme.colors.primary || "#00f0ff" : theme.colors.muted || "#666677" : theme.colors.accent || "#38bdf8";
6811
+ doc.font("Courier").fontSize(9).fillColor(numColor).text(String(lineNum).padStart(2, " "), x + 10, lineY, { width: 22 });
6812
+ doc.font("Courier").fontSize(10).fillColor(textColor).text(line || " ", x + 36, lineY, { width: width - 48 });
6813
+ });
4693
6814
  return y + boxHeight;
4694
6815
  }
4695
6816
  case "card": {
@@ -4897,7 +7018,7 @@ var PdfRenderer = class {
4897
7018
  const barY = topY + plotH - h;
4898
7019
  doc.rect(barX, barY, barW, h).fill(theme.colors.primary);
4899
7020
  doc.font("Helvetica").fontSize(9).fillColor(theme.colors.text).text(String(val), barX, barY - 12, { width: barW, align: "center" });
4900
- if (ch.labels[idx]) {
7021
+ if (ch.labels?.[idx]) {
4901
7022
  doc.font("Helvetica").fontSize(9).fillColor(theme.colors.muted || "#888888").text(this.stripFormatting(ch.labels[idx]), barX - 10, topY + plotH + 4, {
4902
7023
  width: barW + 20,
4903
7024
  align: "center"
@@ -4926,6 +7047,116 @@ var PdfRenderer = class {
4926
7047
  });
4927
7048
  return y + boxH + 8;
4928
7049
  }
7050
+ case "section": {
7051
+ const s = element;
7052
+ const boxH = 130;
7053
+ doc.roundedRect(x, y, width, boxH, 8).fill(theme.colors.surface || "rgba(255,255,255,0.06)");
7054
+ doc.roundedRect(x, y, width, boxH, 8).lineWidth(2).strokeColor(theme.colors.primary).stroke();
7055
+ let curY = y + 18;
7056
+ if (s.number !== void 0) {
7057
+ const numStr = String(s.number);
7058
+ doc.roundedRect(x + width / 2 - 40, curY, 80, 18, 9).fill(theme.colors.primary);
7059
+ doc.font("Helvetica-Bold").fontSize(9).fillColor("#000000").text(`SECTION ${numStr}`.toUpperCase(), x, curY + 4, { width, align: "center" });
7060
+ curY += 28;
7061
+ }
7062
+ doc.font("Helvetica-Bold").fontSize(22).fillColor(theme.colors.text).text(this.stripFormatting(s.title), x + 20, curY, {
7063
+ width: width - 40,
7064
+ align: "center"
7065
+ });
7066
+ curY += 28;
7067
+ if (s.subtitle) {
7068
+ doc.font("Helvetica").fontSize(12).fillColor(theme.colors.muted || "#999999").text(this.stripFormatting(s.subtitle), x + 30, curY, {
7069
+ width: width - 60,
7070
+ align: "center"
7071
+ });
7072
+ }
7073
+ return y + boxH + 8;
7074
+ }
7075
+ case "toc": {
7076
+ const t = element;
7077
+ const items = t.items ? [...t.items] : [];
7078
+ if (items.length === 0 && presentation) {
7079
+ let autoIdx = 1;
7080
+ for (const s of presentation.slides) {
7081
+ for (const el of s.elements) {
7082
+ if (el.type === "section") {
7083
+ const sec = el;
7084
+ items.push({
7085
+ number: sec.number !== void 0 ? String(sec.number) : String(autoIdx++),
7086
+ title: sec.title,
7087
+ description: sec.subtitle
7088
+ });
7089
+ }
7090
+ }
7091
+ }
7092
+ if (items.length === 0) {
7093
+ presentation.slides.forEach((s, idx) => {
7094
+ const heading = s.elements.find((el) => el.type === "heading");
7095
+ if (heading) {
7096
+ items.push({
7097
+ number: String(idx + 1),
7098
+ title: heading.text
7099
+ });
7100
+ }
7101
+ });
7102
+ }
7103
+ }
7104
+ let curY = y;
7105
+ if (t.title) {
7106
+ doc.font("Helvetica-Bold").fontSize(18).fillColor(theme.colors.primary).text(this.stripFormatting(t.title), x, curY, { width });
7107
+ curY += 26;
7108
+ }
7109
+ const itemH = 34;
7110
+ items.forEach((item, idx) => {
7111
+ const num = item.number !== void 0 ? String(item.number) : String(idx + 1);
7112
+ const descText = item.description || item.subtitle;
7113
+ const itemY = curY + idx * (itemH + 8);
7114
+ doc.roundedRect(x, itemY, width, itemH, 6).fill(theme.colors.surface || "rgba(255,255,255,0.04)");
7115
+ doc.roundedRect(x, itemY, width, itemH, 6).strokeColor(theme.colors.border || "rgba(255,255,255,0.1)").stroke();
7116
+ doc.roundedRect(x + 10, itemY + 6, 22, 22, 4).fill(theme.colors.primary);
7117
+ doc.font("Helvetica-Bold").fontSize(10).fillColor("#000000").text(num, x + 10, itemY + 11, { width: 22, align: "center" });
7118
+ doc.font("Helvetica-Bold").fontSize(12).fillColor(theme.colors.text).text(this.stripFormatting(item.title), x + 40, itemY + (descText ? 5 : 10), {
7119
+ width: width - 50
7120
+ });
7121
+ if (descText) {
7122
+ doc.font("Helvetica").fontSize(9).fillColor(theme.colors.muted || "#888888").text(this.stripFormatting(descText), x + 40, itemY + 19, {
7123
+ width: width - 50
7124
+ });
7125
+ }
7126
+ });
7127
+ return curY + items.length * (itemH + 8) + 8;
7128
+ }
7129
+ case "icon": {
7130
+ const ic = element;
7131
+ const iconText = `\u2605 ${ic.name.replace(/^[^:]+:/, "").toUpperCase()}`;
7132
+ doc.font("Helvetica-Bold").fontSize(11).fillColor(theme.colors.primary);
7133
+ doc.text(iconText, x, y, { width: 120 });
7134
+ return y + 20;
7135
+ }
7136
+ case "grid": {
7137
+ const g = element;
7138
+ const colCount = typeof g.columns === "number" ? g.columns : parseInt(String(g.columns), 10) || 2;
7139
+ const gap = 16;
7140
+ const availableWidth = width - gap * (colCount - 1);
7141
+ const colWidth = availableWidth / colCount;
7142
+ let maxY = y;
7143
+ for (let i = 0; i < g.elements.length; i++) {
7144
+ const colIdx = i % colCount;
7145
+ const colX = x + colIdx * (colWidth + gap);
7146
+ const childY = this.renderElement(doc, g.elements[i], colX, y, colWidth, theme, presentation);
7147
+ if (childY > maxY)
7148
+ maxY = childY;
7149
+ }
7150
+ return maxY + 8;
7151
+ }
7152
+ case "stack": {
7153
+ const st = element;
7154
+ let curY = y;
7155
+ for (const child of st.elements) {
7156
+ curY = this.renderElement(doc, child, x, curY, width, theme, presentation) + 8;
7157
+ }
7158
+ return curY;
7159
+ }
4929
7160
  default:
4930
7161
  return y;
4931
7162
  }
@@ -4981,8 +7212,15 @@ function startDevServer(filePath, options = {}) {
4981
7212
  }
4982
7213
  }
4983
7214
  const server = createServer(async (req, res) => {
4984
- const url = req.url || "/";
4985
- if (url === "/__yumia_live_reload") {
7215
+ const rawUrl = req.url || "/";
7216
+ let pathname = "/";
7217
+ try {
7218
+ const parsed = new URL(rawUrl, `http://localhost:${port}`);
7219
+ pathname = parsed.pathname;
7220
+ } catch {
7221
+ pathname = rawUrl.split("?")[0] || "/";
7222
+ }
7223
+ if (pathname === "/__yumia_live_reload") {
4986
7224
  res.writeHead(200, {
4987
7225
  "Content-Type": "text/event-stream",
4988
7226
  "Cache-Control": "no-cache",
@@ -4996,7 +7234,7 @@ function startDevServer(filePath, options = {}) {
4996
7234
  });
4997
7235
  return;
4998
7236
  }
4999
- if (url === "/" || url.startsWith("/#")) {
7237
+ if (pathname === "/" || pathname === "/index.html" || pathname.startsWith("/#")) {
5000
7238
  const html = await getCompiledHtml();
5001
7239
  res.writeHead(200, {
5002
7240
  "Content-Type": "text/html; charset=utf-8",
@@ -5057,7 +7295,7 @@ function startDevServer(filePath, options = {}) {
5057
7295
  // src/cli.ts
5058
7296
  import { mkdirSync, readFileSync as readFileSync2, watch as fsWatch, writeFileSync } from "fs";
5059
7297
  import { basename, dirname, extname, join, resolve as resolve2 } from "path";
5060
- var VERSION = "0.1.19";
7298
+ var VERSION = "0.1.21";
5061
7299
  function printHelp() {
5062
7300
  return `
5063
7301
  YumiaMD \u2014 Markdown-based presentation compiler (v${VERSION})
@@ -5596,10 +7834,21 @@ export {
5596
7834
  createGroup,
5597
7835
  createColumn,
5598
7836
  createColumns,
7837
+ createSection,
7838
+ createToc,
5599
7839
  createMath,
5600
7840
  createLayoutDirective,
7841
+ createIcon,
7842
+ createGrid,
7843
+ createStack,
7844
+ createComponent,
7845
+ createSlot,
7846
+ NativeYumiaParser,
7847
+ parseNativeYumia,
7848
+ parseHighlightLines,
5601
7849
  DefaultYumiaParser,
5602
7850
  parseYumia,
7851
+ migrateMarkdownToNative,
5603
7852
  DEFAULT_VIEWPORT,
5604
7853
  VIEWPORT_PRESETS,
5605
7854
  DefaultLayoutEngine,
@@ -5609,6 +7858,11 @@ export {
5609
7858
  corporateTheme,
5610
7859
  terminalTheme,
5611
7860
  academicTheme,
7861
+ nordTheme,
7862
+ draculaTheme,
7863
+ tokyoNightTheme,
7864
+ emeraldTheme,
7865
+ synthwaveTheme,
5612
7866
  THEME_REGISTRY,
5613
7867
  resolveTheme,
5614
7868
  createTheme,
@@ -5617,6 +7871,8 @@ export {
5617
7871
  parse,
5618
7872
  validate,
5619
7873
  lint,
7874
+ IconResolver,
7875
+ defaultIconResolver,
5620
7876
  cleanFontFace,
5621
7877
  parseInlineMarkdown,
5622
7878
  PptxRenderer,