yumiamd 0.1.20 → 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,7 +4387,7 @@ ${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,
2892
4391
  bold: true,
2893
4392
  color: this.cleanHexColor(theme.colors.primary),
2894
4393
  fontFace: cleanFontFace(theme.typography.headingFont)
@@ -2901,7 +4400,7 @@ ${item.description}` : "";
2901
4400
  w: colW,
2902
4401
  h: rect.h,
2903
4402
  fill: { color: this.cleanHexColor(theme.colors.surface) },
2904
- line: { color: this.cleanHexColor(theme.colors.border), width: 1 },
4403
+ line: { color: this.cleanHexColor(theme.colors.border), width: 1.5 },
2905
4404
  rectRadius: 0.1
2906
4405
  });
2907
4406
  if (compare.rightTitle) {
@@ -2910,12 +4409,222 @@ ${item.description}` : "";
2910
4409
  y: rect.y + 0.15,
2911
4410
  w: colW - 0.3,
2912
4411
  h: 0.4,
2913
- fontSize: 14,
4412
+ fontSize: 15,
2914
4413
  bold: true,
2915
4414
  color: this.cleanHexColor(theme.colors.primary),
2916
4415
  fontFace: cleanFontFace(theme.typography.headingFont)
2917
4416
  });
2918
4417
  }
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,
4548
+ bold: true,
4549
+ color: primaryHex,
4550
+ fontFace: cleanFontFace(theme.typography.headingFont)
4551
+ });
4552
+ currentY += 0.65;
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);
@@ -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 */
@@ -3968,6 +5861,7 @@ var HtmlRenderer = class {
3968
5861
  <button class="yumia-btn" id="btn-next" title="Next Slide (\u2192)">\u25B6</button>
3969
5862
  <button class="yumia-btn" id="btn-overview" title="Slide Overview (ESC / O)">\u25A6</button>
3970
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>
3971
5865
  <button class="yumia-btn" id="btn-notes" title="Toggle Notes (N)">\u{1F4DD}</button>
3972
5866
  <button class="yumia-btn" id="btn-fs" title="Fullscreen (F)">\u26F6</button>
3973
5867
  </div>
@@ -4080,9 +5974,40 @@ var HtmlRenderer = class {
4080
5974
  if (window.renderMermaidInSlide && slides[currentIdx]) {
4081
5975
  window.renderMermaidInSlide(slides[currentIdx]);
4082
5976
  }
5977
+ if (slides[currentIdx]) {
5978
+ renderMathInSlide(slides[currentIdx]);
5979
+ }
4083
5980
  }, 50);
4084
5981
  }
4085
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
+
4086
6011
  function openSpeakerWindow() {
4087
6012
  const url = new URL(window.location.href);
4088
6013
  url.searchParams.set('speaker', 'true');
@@ -4231,8 +6156,15 @@ var HtmlRenderer = class {
4231
6156
  const hash = window.location.hash.replace('#', '');
4232
6157
  const initialIdx = parseInt(hash, 10) - 1;
4233
6158
  goToSlide(isNaN(initialIdx) ? 0 : initialIdx, false);
6159
+ setTimeout(() => {
6160
+ slides.forEach(renderMathInSlide);
6161
+ }, 100);
4234
6162
  }
4235
6163
 
6164
+ window.addEventListener('load', () => {
6165
+ slides.forEach(renderMathInSlide);
6166
+ });
6167
+
4236
6168
  window.deckController = {
4237
6169
  init: init,
4238
6170
  next: function() { goToSlide(currentIdx + 1); },
@@ -4240,6 +6172,7 @@ var HtmlRenderer = class {
4240
6172
  toggleNotes: function() { notesDrawer?.classList.toggle('open'); },
4241
6173
  toggleOverview: function() { toggleOverview(); },
4242
6174
  openSpeaker: openSpeakerWindow,
6175
+ print: function() { window.print(); },
4243
6176
  toggleFs: function() {
4244
6177
  if (!document.fullscreenElement) {
4245
6178
  document.documentElement.requestFullscreen().catch(() => {});
@@ -4258,6 +6191,7 @@ var HtmlRenderer = class {
4258
6191
  document.getElementById('btn-overview')?.addEventListener('click', window.deckController.toggleOverview);
4259
6192
  document.getElementById('btn-close-overview')?.addEventListener('click', () => toggleOverview(false));
4260
6193
  document.getElementById('btn-speaker')?.addEventListener('click', window.deckController.openSpeaker);
6194
+ document.getElementById('btn-print')?.addEventListener('click', window.deckController.print);
4261
6195
  document.getElementById('btn-fs')?.addEventListener('click', window.deckController.toggleFs);
4262
6196
 
4263
6197
  window.addEventListener('keydown', function(e) {
@@ -4275,6 +6209,11 @@ var HtmlRenderer = class {
4275
6209
  }
4276
6210
  }
4277
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
+
4278
6217
  if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown' || e.key.toLowerCase() === 'l') {
4279
6218
  e.preventDefault();
4280
6219
  window.deckController.next();
@@ -4285,6 +6224,8 @@ var HtmlRenderer = class {
4285
6224
  window.deckController.toggleFs();
4286
6225
  } else if (e.key.toLowerCase() === 's') {
4287
6226
  window.deckController.openSpeaker();
6227
+ } else if (e.key.toLowerCase() === 'p') {
6228
+ window.deckController.print();
4288
6229
  } else if (e.key.toLowerCase() === 'n') {
4289
6230
  window.deckController.toggleNotes();
4290
6231
  } else if (e.key === 'Escape' || e.key.toLowerCase() === 'o') {
@@ -4315,21 +6256,25 @@ var HtmlRenderer = class {
4315
6256
  slideCount: presentation.slides.length
4316
6257
  };
4317
6258
  }
4318
- renderSlide(slide, slideNum, totalSlides, theme) {
6259
+ renderSlide(slide, slideNum, totalSlides, theme, watermarkOpt, presentation) {
4319
6260
  const activeClass = slideNum === 1 ? "active" : "";
4320
6261
  const notesAttr = slide.notes ? this.escapeHtml(slide.notes.replace(/\n/g, "<br>")) : "";
4321
6262
  const progressPercent = Math.round(slideNum / totalSlides * 100);
4322
6263
  const transition = slide.transition || "fade";
4323
6264
  const transType = typeof transition === "string" ? transition : transition.type;
4324
6265
  const transClass = `transition-${transType || "fade"}`;
4325
- 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");
4326
6270
  return `
4327
6271
  <div class="yumia-slide-wrapper ${activeClass} ${transClass}" id="slide-${slideNum}" data-notes="${notesAttr}">
4328
6272
  ${elementsHtml}
6273
+ ${watermarkHtml}
4329
6274
  <div class="yumia-progress-bar" style="width: ${progressPercent}%;"></div>
4330
6275
  </div>`;
4331
6276
  }
4332
- renderElement(element, theme) {
6277
+ renderElement(element, theme, presentation) {
4333
6278
  switch (element.type) {
4334
6279
  case "heading": {
4335
6280
  const h = element;
@@ -4349,8 +6294,100 @@ var HtmlRenderer = class {
4349
6294
  case "code": {
4350
6295
  const c = element;
4351
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
+ }
4352
6327
  return `<pre><code ${langClass}>${this.escapeHtml(c.code)}</code></pre>`;
4353
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
+ }
4354
6391
  case "quote": {
4355
6392
  const q = element;
4356
6393
  const author = q.author ? `<br><small>\u2014 ${this.escapeHtml(q.author)}</small>` : "";
@@ -4426,9 +6463,10 @@ var HtmlRenderer = class {
4426
6463
  }
4427
6464
  case "math": {
4428
6465
  const mathEl = element;
6466
+ const expr = mathEl.expression;
4429
6467
  return `
4430
6468
  <div class="yumia-math-container">
4431
- <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>
4432
6470
  </div>`;
4433
6471
  }
4434
6472
  case "chart": {
@@ -4437,6 +6475,27 @@ var HtmlRenderer = class {
4437
6475
  case "timeline": {
4438
6476
  return this.renderTimeline(element);
4439
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
+ }
4440
6499
  case "compare": {
4441
6500
  return this.renderCompare(element, theme);
4442
6501
  }
@@ -4590,8 +6649,7 @@ var HtmlRenderer = class {
4590
6649
  </div>`;
4591
6650
  }
4592
6651
  formatInline(text) {
4593
- const escaped = this.escapeHtml(text);
4594
- 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>");
4595
6653
  }
4596
6654
  escapeHtml(str) {
4597
6655
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
@@ -4636,7 +6694,7 @@ var PdfRenderer = class {
4636
6694
  const totalSlides = presentation.slides.length;
4637
6695
  for (let i = 0; i < totalSlides; i++) {
4638
6696
  const slide = presentation.slides[i];
4639
- this.renderSlide(doc, slide, i + 1, totalSlides, pageWidth, pageHeight, theme);
6697
+ this.renderSlide(doc, slide, i + 1, totalSlides, pageWidth, pageHeight, theme, presentation);
4640
6698
  }
4641
6699
  doc.end();
4642
6700
  } catch (err) {
@@ -4644,7 +6702,7 @@ var PdfRenderer = class {
4644
6702
  }
4645
6703
  });
4646
6704
  }
4647
- renderSlide(doc, slide, slideNum, totalSlides, pageWidth, pageHeight, theme) {
6705
+ renderSlide(doc, slide, slideNum, totalSlides, pageWidth, pageHeight, theme, presentation) {
4648
6706
  doc.addPage({
4649
6707
  size: [pageWidth, pageHeight],
4650
6708
  margins: { top: 0, bottom: 0, left: 0, right: 0 }
@@ -4655,7 +6713,7 @@ var PdfRenderer = class {
4655
6713
  const contentWidth = pageWidth - padX * 2;
4656
6714
  let cursorY = padY;
4657
6715
  for (const element of slide.elements) {
4658
- cursorY = this.renderElement(doc, element, padX, cursorY, contentWidth, theme);
6716
+ cursorY = this.renderElement(doc, element, padX, cursorY, contentWidth, theme, presentation);
4659
6717
  cursorY += 12;
4660
6718
  }
4661
6719
  const progressWidth = slideNum / totalSlides * pageWidth;
@@ -4665,7 +6723,7 @@ var PdfRenderer = class {
4665
6723
  align: "right"
4666
6724
  });
4667
6725
  }
4668
- renderElement(doc, element, x, y, width, theme) {
6726
+ renderElement(doc, element, x, y, width, theme, presentation) {
4669
6727
  switch (element.type) {
4670
6728
  case "heading": {
4671
6729
  const h = element;
@@ -4717,12 +6775,42 @@ var PdfRenderer = class {
4717
6775
  }
4718
6776
  case "code": {
4719
6777
  const c = element;
4720
- const codeText = c.code;
4721
- doc.font("Courier").fontSize(11);
4722
- const textHeight = doc.heightOfString(codeText, { width: width - 24 });
4723
- 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;
4724
6782
  doc.roundedRect(x, y, width, boxHeight, 6).fill("#0a0a10").strokeColor(theme.colors.border || "rgba(255,255,255,0.1)").stroke();
4725
- 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
+ });
4726
6814
  return y + boxHeight;
4727
6815
  }
4728
6816
  case "card": {
@@ -4930,7 +7018,7 @@ var PdfRenderer = class {
4930
7018
  const barY = topY + plotH - h;
4931
7019
  doc.rect(barX, barY, barW, h).fill(theme.colors.primary);
4932
7020
  doc.font("Helvetica").fontSize(9).fillColor(theme.colors.text).text(String(val), barX, barY - 12, { width: barW, align: "center" });
4933
- if (ch.labels[idx]) {
7021
+ if (ch.labels?.[idx]) {
4934
7022
  doc.font("Helvetica").fontSize(9).fillColor(theme.colors.muted || "#888888").text(this.stripFormatting(ch.labels[idx]), barX - 10, topY + plotH + 4, {
4935
7023
  width: barW + 20,
4936
7024
  align: "center"
@@ -4959,6 +7047,116 @@ var PdfRenderer = class {
4959
7047
  });
4960
7048
  return y + boxH + 8;
4961
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
+ }
4962
7160
  default:
4963
7161
  return y;
4964
7162
  }
@@ -5097,7 +7295,7 @@ function startDevServer(filePath, options = {}) {
5097
7295
  // src/cli.ts
5098
7296
  import { mkdirSync, readFileSync as readFileSync2, watch as fsWatch, writeFileSync } from "fs";
5099
7297
  import { basename, dirname, extname, join, resolve as resolve2 } from "path";
5100
- var VERSION = "0.1.20";
7298
+ var VERSION = "0.1.21";
5101
7299
  function printHelp() {
5102
7300
  return `
5103
7301
  YumiaMD \u2014 Markdown-based presentation compiler (v${VERSION})
@@ -5636,10 +7834,21 @@ export {
5636
7834
  createGroup,
5637
7835
  createColumn,
5638
7836
  createColumns,
7837
+ createSection,
7838
+ createToc,
5639
7839
  createMath,
5640
7840
  createLayoutDirective,
7841
+ createIcon,
7842
+ createGrid,
7843
+ createStack,
7844
+ createComponent,
7845
+ createSlot,
7846
+ NativeYumiaParser,
7847
+ parseNativeYumia,
7848
+ parseHighlightLines,
5641
7849
  DefaultYumiaParser,
5642
7850
  parseYumia,
7851
+ migrateMarkdownToNative,
5643
7852
  DEFAULT_VIEWPORT,
5644
7853
  VIEWPORT_PRESETS,
5645
7854
  DefaultLayoutEngine,
@@ -5649,6 +7858,11 @@ export {
5649
7858
  corporateTheme,
5650
7859
  terminalTheme,
5651
7860
  academicTheme,
7861
+ nordTheme,
7862
+ draculaTheme,
7863
+ tokyoNightTheme,
7864
+ emeraldTheme,
7865
+ synthwaveTheme,
5652
7866
  THEME_REGISTRY,
5653
7867
  resolveTheme,
5654
7868
  createTheme,
@@ -5657,6 +7871,8 @@ export {
5657
7871
  parse,
5658
7872
  validate,
5659
7873
  lint,
7874
+ IconResolver,
7875
+ defaultIconResolver,
5660
7876
  cleanFontFace,
5661
7877
  parseInlineMarkdown,
5662
7878
  PptxRenderer,