yumiamd 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +1 -1
- package/dist/{chunk-FHM6V4JH.js → chunk-WUJKHJJU.js} +781 -96
- package/dist/index.d.ts +1 -1
- package/dist/index.js +23 -3
- package/package.json +8 -8
package/dist/bin.js
CHANGED
|
@@ -47,6 +47,17 @@ function createCard(elements, title, variant) {
|
|
|
47
47
|
...variant ? { variant } : {}
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
function createMetric(value, label, variant, description, unit, change) {
|
|
51
|
+
return {
|
|
52
|
+
type: "metric",
|
|
53
|
+
value,
|
|
54
|
+
label,
|
|
55
|
+
...variant ? { variant } : {},
|
|
56
|
+
...description ? { description } : {},
|
|
57
|
+
...unit ? { unit } : {},
|
|
58
|
+
...change ? { change } : {}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
50
61
|
function createCode(code, language, highlightLines) {
|
|
51
62
|
return {
|
|
52
63
|
type: "code",
|
|
@@ -111,13 +122,14 @@ var DefaultYumiaParser = class {
|
|
|
111
122
|
}
|
|
112
123
|
const normalized = source.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
113
124
|
const { metadata, content, lineOffset } = this.extractFrontmatter(normalized);
|
|
125
|
+
if (!content.trim()) {
|
|
126
|
+
return createPresentation(metadata, []);
|
|
127
|
+
}
|
|
114
128
|
const slideChunks = this.splitSlides(content, lineOffset);
|
|
115
129
|
const slides = [];
|
|
116
130
|
for (const chunk of slideChunks) {
|
|
117
131
|
const slide = this.parseSlide(chunk.content, chunk.startLine);
|
|
118
|
-
|
|
119
|
-
slides.push(slide);
|
|
120
|
-
}
|
|
132
|
+
slides.push(slide);
|
|
121
133
|
}
|
|
122
134
|
const presentation = createPresentation(metadata, slides);
|
|
123
135
|
if (this.diagnostics.length > 0) {
|
|
@@ -346,6 +358,28 @@ var DefaultYumiaParser = class {
|
|
|
346
358
|
i++;
|
|
347
359
|
continue;
|
|
348
360
|
}
|
|
361
|
+
if (directiveHeader.startsWith("metric")) {
|
|
362
|
+
const valueMatch = directiveHeader.match(/value=['"](.*?)['"]/);
|
|
363
|
+
const labelMatch = directiveHeader.match(/label=['"](.*?)['"]/);
|
|
364
|
+
const variantMatch = directiveHeader.match(/variant=['"](.*?)['"]/);
|
|
365
|
+
const descMatch = directiveHeader.match(/description=['"](.*?)['"]/);
|
|
366
|
+
const unitMatch = directiveHeader.match(/unit=['"](.*?)['"]/);
|
|
367
|
+
const changeMatch = directiveHeader.match(/change=['"](.*?)['"]/);
|
|
368
|
+
const value = valueMatch ? valueMatch[1] : "0";
|
|
369
|
+
const label = labelMatch ? labelMatch[1] : "";
|
|
370
|
+
const variant = variantMatch ? variantMatch[1] : void 0;
|
|
371
|
+
const desc = descMatch ? descMatch[1] : void 0;
|
|
372
|
+
const unit = unitMatch ? unitMatch[1] : void 0;
|
|
373
|
+
const change = changeMatch ? changeMatch[1] : void 0;
|
|
374
|
+
const el = createMetric(value, label, variant, desc, unit, change);
|
|
375
|
+
el.loc = {
|
|
376
|
+
start: { line: currentLineNum, column: 1 },
|
|
377
|
+
end: { line: currentLineNum, column: rawLine.length + 1 }
|
|
378
|
+
};
|
|
379
|
+
elements.push(el);
|
|
380
|
+
i++;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
349
383
|
const [directiveName, ...args] = directiveHeader.split(" ");
|
|
350
384
|
const directiveArg = args.join(" ").trim();
|
|
351
385
|
const blockLines = [];
|
|
@@ -354,7 +388,8 @@ var DefaultYumiaParser = class {
|
|
|
354
388
|
let closed = false;
|
|
355
389
|
while (i < lines.length) {
|
|
356
390
|
const innerLine = lines[i]?.trim() ?? "";
|
|
357
|
-
|
|
391
|
+
const isSingleLine = innerLine.startsWith(":::metric") || innerLine.startsWith(":::layout");
|
|
392
|
+
if (innerLine.startsWith(":::") && innerLine.length > 3 && !isSingleLine) {
|
|
358
393
|
nestedCount++;
|
|
359
394
|
} else if (innerLine === ":::") {
|
|
360
395
|
nestedCount--;
|
|
@@ -382,9 +417,17 @@ var DefaultYumiaParser = class {
|
|
|
382
417
|
if (directiveName === "notes") {
|
|
383
418
|
notes = blockLines.map((l) => l.trim()).filter(Boolean).join("\n");
|
|
384
419
|
} else if (directiveName === "card") {
|
|
385
|
-
|
|
420
|
+
let cardTitle;
|
|
421
|
+
let cardVariant;
|
|
422
|
+
const variantMatch = directiveArg.match(/variant=['"](.*?)['"]/);
|
|
423
|
+
if (variantMatch) {
|
|
424
|
+
cardVariant = variantMatch[1];
|
|
425
|
+
cardTitle = directiveArg.replace(/variant=['"].*?['"]/, "").trim().replace(/^['"](.*)['"]$/, "$1") || void 0;
|
|
426
|
+
} else {
|
|
427
|
+
cardTitle = directiveArg.replace(/^['"](.*)['"]$/, "$1") || void 0;
|
|
428
|
+
}
|
|
386
429
|
const { elements: cardElements } = this.parseLines(blockLines, blockBaseLine);
|
|
387
|
-
const el = createCard(cardElements,
|
|
430
|
+
const el = createCard(cardElements, cardTitle, cardVariant);
|
|
388
431
|
el.loc = {
|
|
389
432
|
start: { line: directiveStartLine, column: 1 },
|
|
390
433
|
end: { line: baseLine + i - 1, column: 1 }
|
|
@@ -530,7 +573,8 @@ var DefaultYumiaParser = class {
|
|
|
530
573
|
let nestedCount = 1;
|
|
531
574
|
while (i < lines.length) {
|
|
532
575
|
const innerLine = lines[i]?.trim() ?? "";
|
|
533
|
-
|
|
576
|
+
const isSingleLine = innerLine.startsWith(":::metric") || innerLine.startsWith(":::layout");
|
|
577
|
+
if (innerLine.startsWith(":::") && innerLine.length > 3 && !isSingleLine) {
|
|
534
578
|
nestedCount++;
|
|
535
579
|
} else if (innerLine === ":::") {
|
|
536
580
|
nestedCount--;
|
|
@@ -574,7 +618,7 @@ var VIEWPORT_PRESETS = {
|
|
|
574
618
|
var DefaultLayoutEngine = class {
|
|
575
619
|
computeSlide(slide, viewport = DEFAULT_VIEWPORT, options = {}) {
|
|
576
620
|
const padding = options.padding ?? 64;
|
|
577
|
-
const gap = options.gap ??
|
|
621
|
+
const gap = options.gap ?? 32;
|
|
578
622
|
const availableWidth = Math.max(100, viewport.width - padding * 2);
|
|
579
623
|
const availableHeight = Math.max(100, viewport.height - padding * 2);
|
|
580
624
|
if (slide.elements.length === 0) {
|
|
@@ -626,7 +670,7 @@ var DefaultLayoutEngine = class {
|
|
|
626
670
|
return { element, bounds: { x, y, width, height } };
|
|
627
671
|
}
|
|
628
672
|
case "list": {
|
|
629
|
-
const height = this.estimateListHeight(element);
|
|
673
|
+
const height = this.estimateListHeight(element, width);
|
|
630
674
|
return { element, bounds: { x, y, width, height } };
|
|
631
675
|
}
|
|
632
676
|
case "code": {
|
|
@@ -645,6 +689,10 @@ var DefaultLayoutEngine = class {
|
|
|
645
689
|
const height = this.estimateImageHeight(element);
|
|
646
690
|
return { element, bounds: { x, y, width, height } };
|
|
647
691
|
}
|
|
692
|
+
case "metric": {
|
|
693
|
+
const height = this.estimateMetricHeight(element);
|
|
694
|
+
return { element, bounds: { x, y, width, height } };
|
|
695
|
+
}
|
|
648
696
|
case "card": {
|
|
649
697
|
return this.layoutCard(element, x, y, width, gap);
|
|
650
698
|
}
|
|
@@ -658,13 +706,13 @@ var DefaultLayoutEngine = class {
|
|
|
658
706
|
}
|
|
659
707
|
}
|
|
660
708
|
layoutCard(element, x, y, width, gap) {
|
|
661
|
-
const cardPadding =
|
|
709
|
+
const cardPadding = 32;
|
|
662
710
|
const innerWidth = Math.max(10, width - cardPadding * 2);
|
|
663
|
-
const titleHeight = element.title ?
|
|
711
|
+
const titleHeight = element.title ? 60 : 0;
|
|
664
712
|
const innerStartY = y + cardPadding + titleHeight;
|
|
665
|
-
const { nodes: children, totalHeight: innerHeight } = this.layoutElementList(element.elements, x + cardPadding, innerStartY, innerWidth, gap
|
|
666
|
-
const cardHeight = titleHeight + innerHeight + cardPadding * 2;
|
|
667
|
-
const bounds = { x, y, width, height: Math.max(
|
|
713
|
+
const { nodes: children, totalHeight: innerHeight } = this.layoutElementList(element.elements, x + cardPadding, innerStartY, innerWidth, gap);
|
|
714
|
+
const cardHeight = titleHeight + innerHeight + cardPadding * 2 + 12;
|
|
715
|
+
const bounds = { x, y, width, height: Math.max(120, cardHeight) };
|
|
668
716
|
return {
|
|
669
717
|
element,
|
|
670
718
|
bounds,
|
|
@@ -727,24 +775,31 @@ var DefaultLayoutEngine = class {
|
|
|
727
775
|
estimateParagraphHeight(paragraph, width) {
|
|
728
776
|
const charsPerLine = Math.max(20, Math.floor(width / 14));
|
|
729
777
|
const lines = Math.ceil(paragraph.text.length / charsPerLine) || 1;
|
|
730
|
-
return Math.max(40, lines *
|
|
778
|
+
return Math.max(40, lines * 36);
|
|
731
779
|
}
|
|
732
|
-
estimateListHeight(list) {
|
|
733
|
-
|
|
780
|
+
estimateListHeight(list, width = 800) {
|
|
781
|
+
const charsPerLine = Math.max(15, Math.floor(width / 13));
|
|
782
|
+
let totalHeight = 0;
|
|
783
|
+
for (const item of list.items) {
|
|
784
|
+
const cleanLen = item.text.replace(/\*\*/g, "").replace(/\*/g, "").length;
|
|
785
|
+
const lines = Math.ceil(cleanLen / charsPerLine) || 1;
|
|
786
|
+
totalHeight += lines * 34 + 18;
|
|
787
|
+
}
|
|
788
|
+
return Math.max(40, totalHeight);
|
|
734
789
|
}
|
|
735
790
|
estimateCodeHeight(code) {
|
|
736
791
|
const lines = code.code.split("\n").length || 1;
|
|
737
|
-
return lines *
|
|
792
|
+
return lines * 28 + 48;
|
|
738
793
|
}
|
|
739
794
|
estimateQuoteHeight(quote, width) {
|
|
740
795
|
const charsPerLine = Math.max(20, Math.floor(width / 14));
|
|
741
796
|
const lines = Math.ceil(quote.text.length / charsPerLine) || 1;
|
|
742
|
-
return lines *
|
|
797
|
+
return lines * 34 + 32;
|
|
743
798
|
}
|
|
744
799
|
estimateTableHeight(table) {
|
|
745
|
-
const headerHeight = table.headers && table.headers.length > 0 ?
|
|
746
|
-
const rowsHeight = (table.rows ? table.rows.length : 0) *
|
|
747
|
-
return Math.max(60, headerHeight + rowsHeight +
|
|
800
|
+
const headerHeight = table.headers && table.headers.length > 0 ? 56 : 0;
|
|
801
|
+
const rowsHeight = (table.rows ? table.rows.length : 0) * 44;
|
|
802
|
+
return Math.max(60, headerHeight + rowsHeight + 20);
|
|
748
803
|
}
|
|
749
804
|
estimateImageHeight(image) {
|
|
750
805
|
if (typeof image.height === "number") {
|
|
@@ -752,6 +807,9 @@ var DefaultLayoutEngine = class {
|
|
|
752
807
|
}
|
|
753
808
|
return 320;
|
|
754
809
|
}
|
|
810
|
+
estimateMetricHeight(metric) {
|
|
811
|
+
return metric.change ? 140 : 120;
|
|
812
|
+
}
|
|
755
813
|
};
|
|
756
814
|
|
|
757
815
|
// ../theme/dist/default-theme.js
|
|
@@ -766,7 +824,11 @@ var defaultTheme = {
|
|
|
766
824
|
text: "#0f172a",
|
|
767
825
|
muted: "#64748b",
|
|
768
826
|
accent: "#06b6d4",
|
|
769
|
-
border: "#e2e8f0"
|
|
827
|
+
border: "#e2e8f0",
|
|
828
|
+
success: "#10b981",
|
|
829
|
+
warning: "#f59e0b",
|
|
830
|
+
danger: "#ef4444",
|
|
831
|
+
info: "#3b82f6"
|
|
770
832
|
},
|
|
771
833
|
typography: {
|
|
772
834
|
headingFont: "Inter, system-ui, -apple-system, sans-serif",
|
|
@@ -823,29 +885,523 @@ var defaultTheme = {
|
|
|
823
885
|
}
|
|
824
886
|
}
|
|
825
887
|
};
|
|
826
|
-
|
|
888
|
+
var cyberpunkTheme = {
|
|
889
|
+
name: "cyberpunk",
|
|
890
|
+
description: "High-energy dark cyberpunk theme with neon pink and cyan accents",
|
|
891
|
+
colors: {
|
|
892
|
+
primary: "#FF2E88",
|
|
893
|
+
secondary: "#00F0FF",
|
|
894
|
+
background: "#0B0B12",
|
|
895
|
+
surface: "#151522",
|
|
896
|
+
text: "#FFFFFF",
|
|
897
|
+
muted: "#A7A7B5",
|
|
898
|
+
accent: "#7B61FF",
|
|
899
|
+
border: "#2E2E48",
|
|
900
|
+
success: "#00FF9F",
|
|
901
|
+
warning: "#FFE600",
|
|
902
|
+
danger: "#FF0055",
|
|
903
|
+
info: "#00F0FF"
|
|
904
|
+
},
|
|
905
|
+
typography: {
|
|
906
|
+
headingFont: "Outfit, Inter, sans-serif",
|
|
907
|
+
bodyFont: "Inter, sans-serif",
|
|
908
|
+
codeFont: "JetBrains Mono, monospace",
|
|
909
|
+
sizes: {
|
|
910
|
+
h1: 46,
|
|
911
|
+
h2: 38,
|
|
912
|
+
h3: 28,
|
|
913
|
+
h4: 22,
|
|
914
|
+
body: 18,
|
|
915
|
+
small: 14,
|
|
916
|
+
code: 16
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
spacing: {
|
|
920
|
+
unit: 8,
|
|
921
|
+
slidePadding: 48,
|
|
922
|
+
elementGap: 28
|
|
923
|
+
},
|
|
924
|
+
radius: {
|
|
925
|
+
default: 12,
|
|
926
|
+
sm: 6,
|
|
927
|
+
md: 12,
|
|
928
|
+
lg: 20,
|
|
929
|
+
full: 9999
|
|
930
|
+
},
|
|
931
|
+
components: {
|
|
932
|
+
card: {
|
|
933
|
+
background: "#151522",
|
|
934
|
+
borderColor: "#FF2E88",
|
|
935
|
+
borderRadius: 16,
|
|
936
|
+
padding: 28
|
|
937
|
+
},
|
|
938
|
+
code: {
|
|
939
|
+
background: "#07070D",
|
|
940
|
+
textColor: "#00F0FF",
|
|
941
|
+
borderRadius: 8
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
var minimalTheme = {
|
|
946
|
+
name: "minimal",
|
|
947
|
+
description: "Monochrome Swiss-style design with maximum clarity and contrast",
|
|
948
|
+
colors: {
|
|
949
|
+
primary: "#111111",
|
|
950
|
+
secondary: "#333333",
|
|
951
|
+
background: "#FFFFFF",
|
|
952
|
+
surface: "#F5F5F7",
|
|
953
|
+
text: "#111111",
|
|
954
|
+
muted: "#777777",
|
|
955
|
+
accent: "#000000",
|
|
956
|
+
border: "#E5E5E5",
|
|
957
|
+
success: "#10B981",
|
|
958
|
+
warning: "#F59E0B",
|
|
959
|
+
danger: "#EF4444",
|
|
960
|
+
info: "#3B82F6"
|
|
961
|
+
},
|
|
962
|
+
typography: {
|
|
963
|
+
headingFont: "Helvetica Neue, Arial, sans-serif",
|
|
964
|
+
bodyFont: "Helvetica Neue, Arial, sans-serif",
|
|
965
|
+
codeFont: "Menlo, Monaco, monospace",
|
|
966
|
+
sizes: {
|
|
967
|
+
h1: 44,
|
|
968
|
+
h2: 34,
|
|
969
|
+
h3: 26,
|
|
970
|
+
h4: 20,
|
|
971
|
+
body: 18,
|
|
972
|
+
small: 14,
|
|
973
|
+
code: 15
|
|
974
|
+
}
|
|
975
|
+
},
|
|
976
|
+
spacing: {
|
|
977
|
+
unit: 8,
|
|
978
|
+
slidePadding: 56,
|
|
979
|
+
elementGap: 24
|
|
980
|
+
},
|
|
981
|
+
radius: {
|
|
982
|
+
default: 4,
|
|
983
|
+
sm: 2,
|
|
984
|
+
md: 4,
|
|
985
|
+
lg: 8,
|
|
986
|
+
full: 9999
|
|
987
|
+
},
|
|
988
|
+
components: {
|
|
989
|
+
card: {
|
|
990
|
+
background: "#F5F5F7",
|
|
991
|
+
borderColor: "#E5E5E5",
|
|
992
|
+
borderRadius: 6,
|
|
993
|
+
padding: 24
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
var corporateTheme = {
|
|
998
|
+
name: "corporate",
|
|
999
|
+
description: "Executive navy blue palette tailored for business and enterprise decks",
|
|
1000
|
+
colors: {
|
|
1001
|
+
primary: "#0A2540",
|
|
1002
|
+
secondary: "#635BFF",
|
|
1003
|
+
background: "#FFFFFF",
|
|
1004
|
+
surface: "#F8FAFC",
|
|
1005
|
+
text: "#1A1F36",
|
|
1006
|
+
muted: "#4F566B",
|
|
1007
|
+
accent: "#00D4B2",
|
|
1008
|
+
border: "#E3E8EE",
|
|
1009
|
+
success: "#00D4B2",
|
|
1010
|
+
warning: "#FFC043",
|
|
1011
|
+
danger: "#DF1B41",
|
|
1012
|
+
info: "#635BFF"
|
|
1013
|
+
},
|
|
1014
|
+
typography: {
|
|
1015
|
+
headingFont: "Inter, Segoe UI, sans-serif",
|
|
1016
|
+
bodyFont: "Inter, Segoe UI, sans-serif",
|
|
1017
|
+
codeFont: "Consolas, monospace",
|
|
1018
|
+
sizes: {
|
|
1019
|
+
h1: 42,
|
|
1020
|
+
h2: 34,
|
|
1021
|
+
h3: 26,
|
|
1022
|
+
h4: 20,
|
|
1023
|
+
body: 18,
|
|
1024
|
+
small: 14,
|
|
1025
|
+
code: 15
|
|
1026
|
+
}
|
|
1027
|
+
},
|
|
1028
|
+
spacing: {
|
|
1029
|
+
unit: 8,
|
|
1030
|
+
slidePadding: 48,
|
|
1031
|
+
elementGap: 24
|
|
1032
|
+
},
|
|
1033
|
+
radius: {
|
|
1034
|
+
default: 8,
|
|
1035
|
+
sm: 4,
|
|
1036
|
+
md: 8,
|
|
1037
|
+
lg: 12,
|
|
1038
|
+
full: 9999
|
|
1039
|
+
},
|
|
1040
|
+
components: {
|
|
1041
|
+
card: {
|
|
1042
|
+
background: "#F8FAFC",
|
|
1043
|
+
borderColor: "#0A2540",
|
|
1044
|
+
borderRadius: 8,
|
|
1045
|
+
padding: 24
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
var terminalTheme = {
|
|
1050
|
+
name: "terminal",
|
|
1051
|
+
description: "Retro hacker green-on-dark theme with pure monospace aesthetic",
|
|
1052
|
+
colors: {
|
|
1053
|
+
primary: "#00FF66",
|
|
1054
|
+
secondary: "#33FF88",
|
|
1055
|
+
background: "#0C100C",
|
|
1056
|
+
surface: "#141A14",
|
|
1057
|
+
text: "#E0FFE0",
|
|
1058
|
+
muted: "#4E7A4E",
|
|
1059
|
+
accent: "#FFB000",
|
|
1060
|
+
border: "#1F331F",
|
|
1061
|
+
success: "#00FF66",
|
|
1062
|
+
warning: "#FFB000",
|
|
1063
|
+
danger: "#FF3333",
|
|
1064
|
+
info: "#00E5FF"
|
|
1065
|
+
},
|
|
1066
|
+
typography: {
|
|
1067
|
+
headingFont: "JetBrains Mono, Courier New, monospace",
|
|
1068
|
+
bodyFont: "JetBrains Mono, Courier New, monospace",
|
|
1069
|
+
codeFont: "JetBrains Mono, Courier New, monospace",
|
|
1070
|
+
sizes: {
|
|
1071
|
+
h1: 40,
|
|
1072
|
+
h2: 32,
|
|
1073
|
+
h3: 26,
|
|
1074
|
+
h4: 20,
|
|
1075
|
+
body: 17,
|
|
1076
|
+
small: 13,
|
|
1077
|
+
code: 15
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
spacing: {
|
|
1081
|
+
unit: 8,
|
|
1082
|
+
slidePadding: 48,
|
|
1083
|
+
elementGap: 24
|
|
1084
|
+
},
|
|
1085
|
+
radius: {
|
|
1086
|
+
default: 0,
|
|
1087
|
+
sm: 0,
|
|
1088
|
+
md: 0,
|
|
1089
|
+
lg: 0,
|
|
1090
|
+
full: 0
|
|
1091
|
+
},
|
|
1092
|
+
components: {
|
|
1093
|
+
card: {
|
|
1094
|
+
background: "#141A14",
|
|
1095
|
+
borderColor: "#00FF66",
|
|
1096
|
+
borderRadius: 0,
|
|
1097
|
+
padding: 24
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
var academicTheme = {
|
|
1102
|
+
name: "academic",
|
|
1103
|
+
description: "Scholarly paper theme with serif typography and crimson accents",
|
|
1104
|
+
colors: {
|
|
1105
|
+
primary: "#8B0000",
|
|
1106
|
+
secondary: "#1C3F60",
|
|
1107
|
+
background: "#FCFBF7",
|
|
1108
|
+
surface: "#F5F2EB",
|
|
1109
|
+
text: "#2C2B29",
|
|
1110
|
+
muted: "#6B6862",
|
|
1111
|
+
accent: "#C29B38",
|
|
1112
|
+
border: "#E0DACD",
|
|
1113
|
+
success: "#2E7D32",
|
|
1114
|
+
warning: "#E65100",
|
|
1115
|
+
danger: "#C62828",
|
|
1116
|
+
info: "#1565C0"
|
|
1117
|
+
},
|
|
1118
|
+
typography: {
|
|
1119
|
+
headingFont: "Georgia, Times New Roman, serif",
|
|
1120
|
+
bodyFont: "Georgia, Times New Roman, serif",
|
|
1121
|
+
codeFont: "Courier New, monospace",
|
|
1122
|
+
sizes: {
|
|
1123
|
+
h1: 42,
|
|
1124
|
+
h2: 34,
|
|
1125
|
+
h3: 26,
|
|
1126
|
+
h4: 20,
|
|
1127
|
+
body: 18,
|
|
1128
|
+
small: 14,
|
|
1129
|
+
code: 15
|
|
1130
|
+
}
|
|
1131
|
+
},
|
|
1132
|
+
spacing: {
|
|
1133
|
+
unit: 8,
|
|
1134
|
+
slidePadding: 52,
|
|
1135
|
+
elementGap: 24
|
|
1136
|
+
},
|
|
1137
|
+
radius: {
|
|
1138
|
+
default: 4,
|
|
1139
|
+
sm: 2,
|
|
1140
|
+
md: 4,
|
|
1141
|
+
lg: 6,
|
|
1142
|
+
full: 9999
|
|
1143
|
+
},
|
|
1144
|
+
components: {
|
|
1145
|
+
card: {
|
|
1146
|
+
background: "#F5F2EB",
|
|
1147
|
+
borderColor: "#8B0000",
|
|
1148
|
+
borderRadius: 4,
|
|
1149
|
+
padding: 24
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
var THEME_REGISTRY = {
|
|
1154
|
+
default: defaultTheme,
|
|
1155
|
+
cyberpunk: cyberpunkTheme,
|
|
1156
|
+
minimal: minimalTheme,
|
|
1157
|
+
corporate: corporateTheme,
|
|
1158
|
+
terminal: terminalTheme,
|
|
1159
|
+
academic: academicTheme
|
|
1160
|
+
};
|
|
1161
|
+
function resolveTheme(themeNameOrRef, explicitOverrides) {
|
|
1162
|
+
const name = typeof themeNameOrRef === "string" ? themeNameOrRef : themeNameOrRef?.name || "default";
|
|
1163
|
+
const baseTheme = THEME_REGISTRY[name.toLowerCase()] || defaultTheme;
|
|
1164
|
+
const refOverrides = typeof themeNameOrRef === "object" ? themeNameOrRef.overrides : void 0;
|
|
1165
|
+
const mergedOverrides = {
|
|
1166
|
+
...refOverrides,
|
|
1167
|
+
...explicitOverrides,
|
|
1168
|
+
colors: { ...baseTheme.colors, ...refOverrides?.colors, ...explicitOverrides?.colors },
|
|
1169
|
+
typography: {
|
|
1170
|
+
...baseTheme.typography,
|
|
1171
|
+
...refOverrides?.typography,
|
|
1172
|
+
...explicitOverrides?.typography
|
|
1173
|
+
},
|
|
1174
|
+
components: {
|
|
1175
|
+
...baseTheme.components,
|
|
1176
|
+
...refOverrides?.components,
|
|
1177
|
+
...explicitOverrides?.components
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
827
1180
|
return {
|
|
828
|
-
...
|
|
829
|
-
...
|
|
830
|
-
colors: { ...defaultTheme.colors, ...overrides.colors },
|
|
831
|
-
typography: { ...defaultTheme.typography, ...overrides.typography },
|
|
832
|
-
spacing: { ...defaultTheme.spacing, ...overrides.spacing },
|
|
833
|
-
radius: { ...defaultTheme.radius, ...overrides.radius },
|
|
834
|
-
shadows: { ...defaultTheme.shadows, ...overrides.shadows },
|
|
835
|
-
components: { ...defaultTheme.components, ...overrides.components }
|
|
1181
|
+
...baseTheme,
|
|
1182
|
+
...mergedOverrides
|
|
836
1183
|
};
|
|
837
1184
|
}
|
|
1185
|
+
function createTheme(overrides) {
|
|
1186
|
+
return resolveTheme(overrides.name, overrides);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// ../core/dist/linter.js
|
|
1190
|
+
var YumiaLinter = class {
|
|
1191
|
+
layoutEngine;
|
|
1192
|
+
constructor(layoutEngine) {
|
|
1193
|
+
this.layoutEngine = layoutEngine ?? new DefaultLayoutEngine();
|
|
1194
|
+
}
|
|
1195
|
+
lint(presentation, options = {}) {
|
|
1196
|
+
const issues = [];
|
|
1197
|
+
const layout = this.layoutEngine.computePresentation(presentation, options.viewport);
|
|
1198
|
+
if (presentation.diagnostics) {
|
|
1199
|
+
for (const diag of presentation.diagnostics) {
|
|
1200
|
+
issues.push({
|
|
1201
|
+
code: diag.code || "YUM002",
|
|
1202
|
+
slide: 1,
|
|
1203
|
+
message: diag.message,
|
|
1204
|
+
severity: diag.severity === "error" ? "error" : "warning",
|
|
1205
|
+
loc: diag.loc
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
presentation.slides.forEach((slide, index) => {
|
|
1210
|
+
const slideNum = index + 1;
|
|
1211
|
+
const slideLayout = layout.slides[index];
|
|
1212
|
+
if (slide.elements.length === 0) {
|
|
1213
|
+
issues.push({
|
|
1214
|
+
code: "YUM006",
|
|
1215
|
+
slide: slideNum,
|
|
1216
|
+
message: "Slide contains no content elements.",
|
|
1217
|
+
severity: "warning",
|
|
1218
|
+
loc: slide.loc
|
|
1219
|
+
});
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
if (slideLayout && slideLayout.overflow) {
|
|
1223
|
+
const overflowPx = Math.round(slideLayout.overflowAmount || 0);
|
|
1224
|
+
issues.push({
|
|
1225
|
+
code: "YUM001",
|
|
1226
|
+
slide: slideNum,
|
|
1227
|
+
message: `Content exceeds slide viewport bounds by ~${overflowPx}px. Consider reducing font size or splitting into multiple slides.`,
|
|
1228
|
+
severity: options.strict ? "error" : "warning",
|
|
1229
|
+
loc: slide.loc
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
const hasHeading = this.slideHasHeading(slide.elements);
|
|
1233
|
+
if (!hasHeading) {
|
|
1234
|
+
issues.push({
|
|
1235
|
+
code: "YUM003",
|
|
1236
|
+
slide: slideNum,
|
|
1237
|
+
message: "Slide has no heading (h1-h4). Consider adding a descriptive title for structure.",
|
|
1238
|
+
severity: "warning",
|
|
1239
|
+
loc: slide.loc
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
const { listCount, wordCount } = this.calculateSlideDensity(slide.elements);
|
|
1243
|
+
if (listCount > 7) {
|
|
1244
|
+
issues.push({
|
|
1245
|
+
code: "YUM004",
|
|
1246
|
+
slide: slideNum,
|
|
1247
|
+
message: `High information density: slide contains ${listCount} list items (recommended max: 7).`,
|
|
1248
|
+
severity: "warning",
|
|
1249
|
+
loc: slide.loc
|
|
1250
|
+
});
|
|
1251
|
+
} else if (wordCount > 120) {
|
|
1252
|
+
issues.push({
|
|
1253
|
+
code: "YUM004",
|
|
1254
|
+
slide: slideNum,
|
|
1255
|
+
message: `High information density: slide contains ~${wordCount} words (recommended max: 120 words).`,
|
|
1256
|
+
severity: "warning",
|
|
1257
|
+
loc: slide.loc
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
this.checkImages(slide.elements, slideNum, issues);
|
|
1261
|
+
const maxDepth = this.getMaxNestingDepth(slide.elements, 0);
|
|
1262
|
+
if (maxDepth > 2) {
|
|
1263
|
+
issues.push({
|
|
1264
|
+
code: "YUM008",
|
|
1265
|
+
slide: slideNum,
|
|
1266
|
+
message: `Excessive directive nesting depth (${maxDepth} levels). Maximum recommended depth is 2.`,
|
|
1267
|
+
severity: "warning",
|
|
1268
|
+
loc: slide.loc
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
if (options.includeOptionalNotesRule && (!slide.notes || slide.notes.trim() === "")) {
|
|
1272
|
+
issues.push({
|
|
1273
|
+
code: "YUM009",
|
|
1274
|
+
slide: slideNum,
|
|
1275
|
+
message: "Slide has no speaker notes (:::notes directive).",
|
|
1276
|
+
severity: "info",
|
|
1277
|
+
loc: slide.loc
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
const errors = issues.filter((i) => i.severity === "error");
|
|
1282
|
+
const warnings = issues.filter((i) => i.severity === "warning");
|
|
1283
|
+
const infos = issues.filter((i) => i.severity === "info");
|
|
1284
|
+
return {
|
|
1285
|
+
passed: options.strict ? errors.length === 0 && warnings.length === 0 : errors.length === 0,
|
|
1286
|
+
totalSlides: presentation.slides.length,
|
|
1287
|
+
issueCount: issues.length,
|
|
1288
|
+
errors,
|
|
1289
|
+
warnings,
|
|
1290
|
+
infos
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
slideHasHeading(elements) {
|
|
1294
|
+
for (const el of elements) {
|
|
1295
|
+
if (el.type === "heading")
|
|
1296
|
+
return true;
|
|
1297
|
+
if (el.type === "card" && el.title)
|
|
1298
|
+
return true;
|
|
1299
|
+
if (el.type === "columns") {
|
|
1300
|
+
const cols = el.columns;
|
|
1301
|
+
for (const col of cols) {
|
|
1302
|
+
if (this.slideHasHeading(col.elements))
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
return false;
|
|
1308
|
+
}
|
|
1309
|
+
calculateSlideDensity(elements) {
|
|
1310
|
+
let listCount = 0;
|
|
1311
|
+
let wordCount = 0;
|
|
1312
|
+
const traverse = (els) => {
|
|
1313
|
+
for (const el of els) {
|
|
1314
|
+
if (el.type === "list") {
|
|
1315
|
+
const list = el;
|
|
1316
|
+
listCount += list.items.length;
|
|
1317
|
+
for (const item of list.items) {
|
|
1318
|
+
wordCount += item.text.split(/\s+/).filter(Boolean).length;
|
|
1319
|
+
}
|
|
1320
|
+
} else if (el.type === "paragraph") {
|
|
1321
|
+
const p = el;
|
|
1322
|
+
wordCount += p.text.split(/\s+/).filter(Boolean).length;
|
|
1323
|
+
} else if (el.type === "card") {
|
|
1324
|
+
const card = el;
|
|
1325
|
+
if (card.title)
|
|
1326
|
+
wordCount += card.title.split(/\s+/).filter(Boolean).length;
|
|
1327
|
+
if (card.elements)
|
|
1328
|
+
traverse(card.elements);
|
|
1329
|
+
} else if (el.type === "columns") {
|
|
1330
|
+
const cols = el;
|
|
1331
|
+
for (const col of cols.columns) {
|
|
1332
|
+
traverse(col.elements);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
traverse(elements);
|
|
1338
|
+
return { listCount, wordCount };
|
|
1339
|
+
}
|
|
1340
|
+
checkImages(elements, slideNum, issues) {
|
|
1341
|
+
const traverse = (els) => {
|
|
1342
|
+
for (const el of els) {
|
|
1343
|
+
if (el.type === "image") {
|
|
1344
|
+
const img = el;
|
|
1345
|
+
if (!img.alt || img.alt.trim() === "") {
|
|
1346
|
+
issues.push({
|
|
1347
|
+
code: "YUM007",
|
|
1348
|
+
slide: slideNum,
|
|
1349
|
+
message: `Image '${img.src}' is missing descriptive alt text for accessibility.`,
|
|
1350
|
+
severity: "warning",
|
|
1351
|
+
loc: img.loc
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
if (!img.src || img.src.trim() === "") {
|
|
1355
|
+
issues.push({
|
|
1356
|
+
code: "YUM005",
|
|
1357
|
+
slide: slideNum,
|
|
1358
|
+
message: "Image element has empty src attribute.",
|
|
1359
|
+
severity: "error",
|
|
1360
|
+
loc: img.loc
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
} else if (el.type === "card" && el.elements) {
|
|
1364
|
+
traverse(el.elements);
|
|
1365
|
+
} else if (el.type === "columns") {
|
|
1366
|
+
for (const col of el.columns) {
|
|
1367
|
+
traverse(col.elements);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
traverse(elements);
|
|
1373
|
+
}
|
|
1374
|
+
getMaxNestingDepth(elements, currentDepth) {
|
|
1375
|
+
let max = currentDepth;
|
|
1376
|
+
for (const el of elements) {
|
|
1377
|
+
if (el.type === "card" && el.elements) {
|
|
1378
|
+
const d = this.getMaxNestingDepth(el.elements, currentDepth + 1);
|
|
1379
|
+
if (d > max)
|
|
1380
|
+
max = d;
|
|
1381
|
+
} else if (el.type === "columns") {
|
|
1382
|
+
for (const col of el.columns) {
|
|
1383
|
+
const d = this.getMaxNestingDepth(col.elements, currentDepth + 1);
|
|
1384
|
+
if (d > max)
|
|
1385
|
+
max = d;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
return max;
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
838
1392
|
|
|
839
1393
|
// ../core/dist/compiler.js
|
|
840
1394
|
var YumiaCompiler = class {
|
|
841
1395
|
parser;
|
|
842
1396
|
layoutEngine;
|
|
843
1397
|
defaultTheme;
|
|
1398
|
+
linter;
|
|
844
1399
|
viewport;
|
|
845
1400
|
constructor(config = {}) {
|
|
846
1401
|
this.parser = config.parser ?? new DefaultYumiaParser();
|
|
847
1402
|
this.layoutEngine = config.layoutEngine ?? new DefaultLayoutEngine();
|
|
848
1403
|
this.defaultTheme = config.defaultTheme ?? defaultTheme;
|
|
1404
|
+
this.linter = new YumiaLinter(this.layoutEngine);
|
|
849
1405
|
if (config.viewport) {
|
|
850
1406
|
this.viewport = config.viewport;
|
|
851
1407
|
}
|
|
@@ -865,12 +1421,20 @@ var YumiaCompiler = class {
|
|
|
865
1421
|
warnings
|
|
866
1422
|
};
|
|
867
1423
|
}
|
|
1424
|
+
lint(sourceOrPresentation, options) {
|
|
1425
|
+
const presentation = typeof sourceOrPresentation === "string" ? this.parse(sourceOrPresentation) : sourceOrPresentation;
|
|
1426
|
+
return this.linter.lint(presentation, {
|
|
1427
|
+
...this.viewport ? { viewport: this.viewport } : {},
|
|
1428
|
+
...options
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
868
1431
|
layout(presentation, viewport) {
|
|
869
1432
|
return this.layoutEngine.computePresentation(presentation, viewport ?? this.viewport);
|
|
870
1433
|
}
|
|
871
1434
|
async render(presentation, renderer, contextOverrides = {}) {
|
|
872
1435
|
const layout = contextOverrides.layout ?? this.layout(presentation);
|
|
873
|
-
const
|
|
1436
|
+
const presentationTheme = presentation.metadata.theme ? resolveTheme(presentation.metadata.theme) : this.defaultTheme;
|
|
1437
|
+
const theme = contextOverrides.theme ?? presentationTheme;
|
|
874
1438
|
const context = {
|
|
875
1439
|
theme,
|
|
876
1440
|
layout,
|
|
@@ -896,7 +1460,10 @@ var YumiaCompiler = class {
|
|
|
896
1460
|
subtitle: { type: "string" },
|
|
897
1461
|
author: { type: "string" },
|
|
898
1462
|
date: { type: "string" },
|
|
899
|
-
theme: {
|
|
1463
|
+
theme: {
|
|
1464
|
+
type: "string",
|
|
1465
|
+
enum: ["default", "cyberpunk", "minimal", "corporate", "terminal", "academic"]
|
|
1466
|
+
},
|
|
900
1467
|
aspectRatio: { enum: ["16:9", "4:3", "16:10"] }
|
|
901
1468
|
}
|
|
902
1469
|
},
|
|
@@ -908,9 +1475,13 @@ var YumiaCompiler = class {
|
|
|
908
1475
|
description: "Multi-column grid layout"
|
|
909
1476
|
},
|
|
910
1477
|
card: {
|
|
911
|
-
syntax:
|
|
1478
|
+
syntax: ':::card [Title] [variant="primary|success|warning|danger|info"]\\n...\\n:::',
|
|
912
1479
|
description: "Visual container card with theme border and background"
|
|
913
1480
|
},
|
|
1481
|
+
metric: {
|
|
1482
|
+
syntax: ':::metric value="99.9%" label="Uptime" change="+0.4%" variant="success"',
|
|
1483
|
+
description: "Stat callout box with prominent value, label, and trend"
|
|
1484
|
+
},
|
|
914
1485
|
notes: {
|
|
915
1486
|
syntax: ":::notes\\nSpeaker notes text\\n:::",
|
|
916
1487
|
description: "Speaker notes attached to slide metadata"
|
|
@@ -944,6 +1515,37 @@ var CSS_NAMED_COLORS = {
|
|
|
944
1515
|
slate: "0f172a",
|
|
945
1516
|
transparent: "ffffff"
|
|
946
1517
|
};
|
|
1518
|
+
function parseInlineMarkdown(rawText, baseOptions) {
|
|
1519
|
+
const chunks = [];
|
|
1520
|
+
const regex = /(\*\*.*?\*\*|\*.*?\*|`.*?`)/g;
|
|
1521
|
+
const parts = rawText.split(regex);
|
|
1522
|
+
for (const part of parts) {
|
|
1523
|
+
if (!part)
|
|
1524
|
+
continue;
|
|
1525
|
+
if (part.startsWith("**") && part.endsWith("**") && part.length >= 4) {
|
|
1526
|
+
chunks.push({
|
|
1527
|
+
text: part.slice(2, -2),
|
|
1528
|
+
options: { ...baseOptions, bold: true }
|
|
1529
|
+
});
|
|
1530
|
+
} else if (part.startsWith("*") && part.endsWith("*") && part.length >= 2) {
|
|
1531
|
+
chunks.push({
|
|
1532
|
+
text: part.slice(1, -1),
|
|
1533
|
+
options: { ...baseOptions, italic: true }
|
|
1534
|
+
});
|
|
1535
|
+
} else if (part.startsWith("`") && part.endsWith("`") && part.length >= 2) {
|
|
1536
|
+
chunks.push({
|
|
1537
|
+
text: part.slice(1, -1),
|
|
1538
|
+
options: { ...baseOptions, fontFace: "Courier New" }
|
|
1539
|
+
});
|
|
1540
|
+
} else {
|
|
1541
|
+
chunks.push({
|
|
1542
|
+
text: part,
|
|
1543
|
+
options: { ...baseOptions }
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
return chunks.length > 0 ? chunks : [{ text: rawText, options: baseOptions }];
|
|
1548
|
+
}
|
|
947
1549
|
var PptxRenderer = class {
|
|
948
1550
|
name = "PptxRenderer";
|
|
949
1551
|
targetFormat = "pptx";
|
|
@@ -954,7 +1556,8 @@ var PptxRenderer = class {
|
|
|
954
1556
|
async render(presentation, context = {}) {
|
|
955
1557
|
const PptxConstructor = pptxgen.default || pptxgen;
|
|
956
1558
|
const pptx = new PptxConstructor();
|
|
957
|
-
const
|
|
1559
|
+
const resolvedTheme = presentation.metadata.theme ? resolveTheme(presentation.metadata.theme) : defaultTheme;
|
|
1560
|
+
const theme = context.theme || resolvedTheme;
|
|
958
1561
|
const title = presentation.metadata.title || "Yumia Presentation";
|
|
959
1562
|
const author = presentation.metadata.author || "YumiaMD";
|
|
960
1563
|
pptx.title = title;
|
|
@@ -1014,6 +1617,9 @@ var PptxRenderer = class {
|
|
|
1014
1617
|
case "image":
|
|
1015
1618
|
this.renderImage(pptxSlide, element, rect);
|
|
1016
1619
|
break;
|
|
1620
|
+
case "metric":
|
|
1621
|
+
this.renderMetric(pptxSlide, pptx, node, scaleX, scaleY, theme);
|
|
1622
|
+
break;
|
|
1017
1623
|
case "card":
|
|
1018
1624
|
this.renderCard(pptxSlide, pptx, node, scaleX, scaleY, theme);
|
|
1019
1625
|
break;
|
|
@@ -1027,15 +1633,17 @@ var PptxRenderer = class {
|
|
|
1027
1633
|
renderHeading(pptxSlide, heading, rect, theme) {
|
|
1028
1634
|
const fontSize = this.getHeadingFontSize(heading.level, theme);
|
|
1029
1635
|
const color = this.cleanHexColor(heading.level === 1 ? theme.colors.primary : theme.colors.text);
|
|
1030
|
-
|
|
1636
|
+
const chunks = parseInlineMarkdown(heading.text, {
|
|
1637
|
+
fontSize,
|
|
1638
|
+
bold: true,
|
|
1639
|
+
fontFace: theme.typography.headingFont.split(",")[0]?.trim() || "Arial",
|
|
1640
|
+
color
|
|
1641
|
+
});
|
|
1642
|
+
pptxSlide.addText(chunks, {
|
|
1031
1643
|
x: rect.x,
|
|
1032
1644
|
y: rect.y,
|
|
1033
1645
|
w: rect.w,
|
|
1034
1646
|
h: rect.h,
|
|
1035
|
-
fontSize,
|
|
1036
|
-
bold: true,
|
|
1037
|
-
fontFace: theme.typography.headingFont.split(",")[0]?.trim() || "Arial",
|
|
1038
|
-
color,
|
|
1039
1647
|
align: heading.align || "left",
|
|
1040
1648
|
valign: "middle",
|
|
1041
1649
|
margin: 0
|
|
@@ -1044,14 +1652,16 @@ var PptxRenderer = class {
|
|
|
1044
1652
|
renderParagraph(pptxSlide, paragraph, rect, theme) {
|
|
1045
1653
|
const fontSize = theme.typography.sizes?.body || 18;
|
|
1046
1654
|
const color = this.cleanHexColor(theme.colors.text);
|
|
1047
|
-
|
|
1655
|
+
const chunks = parseInlineMarkdown(paragraph.text, {
|
|
1656
|
+
fontSize,
|
|
1657
|
+
fontFace: theme.typography.bodyFont.split(",")[0]?.trim() || "Arial",
|
|
1658
|
+
color
|
|
1659
|
+
});
|
|
1660
|
+
pptxSlide.addText(chunks, {
|
|
1048
1661
|
x: rect.x,
|
|
1049
1662
|
y: rect.y,
|
|
1050
1663
|
w: rect.w,
|
|
1051
1664
|
h: rect.h,
|
|
1052
|
-
fontSize,
|
|
1053
|
-
fontFace: theme.typography.bodyFont.split(",")[0]?.trim() || "Arial",
|
|
1054
|
-
color,
|
|
1055
1665
|
align: paragraph.align || "left",
|
|
1056
1666
|
valign: "top",
|
|
1057
1667
|
margin: 0
|
|
@@ -1060,18 +1670,24 @@ var PptxRenderer = class {
|
|
|
1060
1670
|
renderList(pptxSlide, list, rect, theme) {
|
|
1061
1671
|
const fontSize = theme.typography.sizes?.body || 18;
|
|
1062
1672
|
const color = this.cleanHexColor(theme.colors.text);
|
|
1063
|
-
const
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
bullet: list.ordered ? { type: "number" } : true,
|
|
1673
|
+
const allChunks = [];
|
|
1674
|
+
list.items.forEach((item) => {
|
|
1675
|
+
const itemChunks = parseInlineMarkdown(item.text, {
|
|
1067
1676
|
fontSize,
|
|
1068
1677
|
color,
|
|
1069
1678
|
fontFace: theme.typography.bodyFont.split(",")[0]?.trim() || "Arial",
|
|
1070
1679
|
indentLevel: item.depth || 0,
|
|
1071
1680
|
paraSpaceAfter: 8
|
|
1681
|
+
});
|
|
1682
|
+
if (itemChunks.length > 0) {
|
|
1683
|
+
itemChunks[0].options = {
|
|
1684
|
+
...itemChunks[0].options,
|
|
1685
|
+
bullet: list.ordered ? { type: "number" } : true
|
|
1686
|
+
};
|
|
1072
1687
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1688
|
+
allChunks.push(...itemChunks);
|
|
1689
|
+
});
|
|
1690
|
+
pptxSlide.addText(allChunks, {
|
|
1075
1691
|
x: rect.x,
|
|
1076
1692
|
y: rect.y,
|
|
1077
1693
|
w: rect.w,
|
|
@@ -1086,7 +1702,7 @@ var PptxRenderer = class {
|
|
|
1086
1702
|
const borderColor = this.cleanHexColor(theme.colors.border || "#cbd5e1");
|
|
1087
1703
|
if (table.headers && table.headers.length > 0) {
|
|
1088
1704
|
tableRows.push(table.headers.map((h) => ({
|
|
1089
|
-
text: h,
|
|
1705
|
+
text: h.replace(/\*\*/g, ""),
|
|
1090
1706
|
options: {
|
|
1091
1707
|
bold: true,
|
|
1092
1708
|
color: "ffffff",
|
|
@@ -1100,7 +1716,7 @@ var PptxRenderer = class {
|
|
|
1100
1716
|
table.rows.forEach((row, rowIndex) => {
|
|
1101
1717
|
const rowBg = rowIndex % 2 === 1 ? "f8fafc" : "ffffff";
|
|
1102
1718
|
tableRows.push(row.map((cell) => ({
|
|
1103
|
-
text: cell,
|
|
1719
|
+
text: cell.replace(/\*\*/g, ""),
|
|
1104
1720
|
options: {
|
|
1105
1721
|
color: this.cleanHexColor(theme.colors.text),
|
|
1106
1722
|
fill: { color: rowBg },
|
|
@@ -1149,12 +1765,79 @@ var PptxRenderer = class {
|
|
|
1149
1765
|
});
|
|
1150
1766
|
}
|
|
1151
1767
|
}
|
|
1768
|
+
renderMetric(pptxSlide, pptx, node, scaleX, scaleY, theme) {
|
|
1769
|
+
const metric = node.element;
|
|
1770
|
+
const rect = this.toInches(node.bounds, scaleX, scaleY);
|
|
1771
|
+
const cardTheme = theme.components?.card;
|
|
1772
|
+
let accentColor = theme.colors.primary;
|
|
1773
|
+
if (metric.variant && theme.colors[metric.variant]) {
|
|
1774
|
+
accentColor = theme.colors[metric.variant];
|
|
1775
|
+
}
|
|
1776
|
+
const fillColor = this.cleanHexColor(cardTheme?.background || theme.colors.surface);
|
|
1777
|
+
const borderColor = this.cleanHexColor(metric.variant && theme.colors[metric.variant] ? theme.colors[metric.variant] : cardTheme?.borderColor || theme.colors.border || "#cbd5e1");
|
|
1778
|
+
pptxSlide.addShape(pptx.ShapeType.roundRect, {
|
|
1779
|
+
x: rect.x,
|
|
1780
|
+
y: rect.y,
|
|
1781
|
+
w: rect.w,
|
|
1782
|
+
h: rect.h,
|
|
1783
|
+
fill: { color: fillColor },
|
|
1784
|
+
line: { color: borderColor, width: 1.5 },
|
|
1785
|
+
rectRadius: 0.08
|
|
1786
|
+
});
|
|
1787
|
+
pptxSlide.addText(metric.label.toUpperCase(), {
|
|
1788
|
+
x: rect.x + 0.15,
|
|
1789
|
+
y: rect.y + 0.15,
|
|
1790
|
+
w: rect.w - 0.3,
|
|
1791
|
+
h: 0.25,
|
|
1792
|
+
fontSize: 11,
|
|
1793
|
+
bold: true,
|
|
1794
|
+
color: this.cleanHexColor(theme.colors.muted || "#64748b"),
|
|
1795
|
+
fontFace: theme.typography.headingFont.split(",")[0]?.trim() || "Arial",
|
|
1796
|
+
align: "center",
|
|
1797
|
+
valign: "middle"
|
|
1798
|
+
});
|
|
1799
|
+
const displayValue = metric.unit ? `${metric.value} ${metric.unit}` : metric.value;
|
|
1800
|
+
pptxSlide.addText(displayValue, {
|
|
1801
|
+
x: rect.x + 0.1,
|
|
1802
|
+
y: rect.y + 0.4,
|
|
1803
|
+
w: rect.w - 0.2,
|
|
1804
|
+
h: 0.55,
|
|
1805
|
+
fontSize: 30,
|
|
1806
|
+
bold: true,
|
|
1807
|
+
color: this.cleanHexColor(accentColor),
|
|
1808
|
+
fontFace: theme.typography.headingFont.split(",")[0]?.trim() || "Arial",
|
|
1809
|
+
align: "center",
|
|
1810
|
+
valign: "middle"
|
|
1811
|
+
});
|
|
1812
|
+
if (metric.change) {
|
|
1813
|
+
const isPositive = metric.change.startsWith("+");
|
|
1814
|
+
const changeColor = isPositive ? this.cleanHexColor(theme.colors.success || "#10b981") : this.cleanHexColor(theme.colors.danger || "#ef4444");
|
|
1815
|
+
pptxSlide.addText(metric.change, {
|
|
1816
|
+
x: rect.x + 0.1,
|
|
1817
|
+
y: rect.y + rect.h - 0.32,
|
|
1818
|
+
w: rect.w - 0.2,
|
|
1819
|
+
h: 0.22,
|
|
1820
|
+
fontSize: 12,
|
|
1821
|
+
bold: true,
|
|
1822
|
+
color: changeColor,
|
|
1823
|
+
fontFace: theme.typography.bodyFont.split(",")[0]?.trim() || "Arial",
|
|
1824
|
+
align: "center",
|
|
1825
|
+
valign: "middle"
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1152
1829
|
renderCard(pptxSlide, pptx, node, scaleX, scaleY, theme) {
|
|
1153
1830
|
const card = node.element;
|
|
1154
1831
|
const rect = this.toInches(node.bounds, scaleX, scaleY);
|
|
1155
1832
|
const cardTheme = theme.components?.card;
|
|
1156
1833
|
const fillColor = this.cleanHexColor(cardTheme?.background || theme.colors.surface);
|
|
1157
|
-
|
|
1834
|
+
let borderColor = this.cleanHexColor(cardTheme?.borderColor || theme.colors.border || "#cbd5e1");
|
|
1835
|
+
let titleColor = this.cleanHexColor(theme.colors.primary);
|
|
1836
|
+
if (card.variant && theme.colors[card.variant]) {
|
|
1837
|
+
const variantHex = this.cleanHexColor(theme.colors[card.variant]);
|
|
1838
|
+
borderColor = variantHex;
|
|
1839
|
+
titleColor = variantHex;
|
|
1840
|
+
}
|
|
1158
1841
|
pptxSlide.addShape(pptx.ShapeType.roundRect, {
|
|
1159
1842
|
x: rect.x,
|
|
1160
1843
|
y: rect.y,
|
|
@@ -1162,17 +1845,17 @@ var PptxRenderer = class {
|
|
|
1162
1845
|
h: rect.h,
|
|
1163
1846
|
fill: { color: fillColor },
|
|
1164
1847
|
line: { color: borderColor, width: 1.5 },
|
|
1165
|
-
rectRadius: 0.
|
|
1848
|
+
rectRadius: 0.08
|
|
1166
1849
|
});
|
|
1167
1850
|
if (card.title) {
|
|
1168
1851
|
pptxSlide.addText(card.title, {
|
|
1169
|
-
x: rect.x +
|
|
1170
|
-
y: rect.y +
|
|
1171
|
-
w: rect.w -
|
|
1172
|
-
h:
|
|
1173
|
-
fontSize:
|
|
1852
|
+
x: rect.x + 0.25,
|
|
1853
|
+
y: rect.y + 0.18,
|
|
1854
|
+
w: rect.w - 0.5,
|
|
1855
|
+
h: 0.35,
|
|
1856
|
+
fontSize: 20,
|
|
1174
1857
|
bold: true,
|
|
1175
|
-
color:
|
|
1858
|
+
color: titleColor,
|
|
1176
1859
|
fontFace: theme.typography.headingFont.split(",")[0]?.trim() || "Arial"
|
|
1177
1860
|
});
|
|
1178
1861
|
}
|
|
@@ -1225,12 +1908,13 @@ var PptxRenderer = class {
|
|
|
1225
1908
|
h: rect.h,
|
|
1226
1909
|
fill: { color: accentColor }
|
|
1227
1910
|
});
|
|
1228
|
-
|
|
1911
|
+
const cleanText = quote.text.replace(/^["'“”«»]+|["'“”«»]+$/g, "").trim();
|
|
1912
|
+
pptxSlide.addText(`\u201C${cleanText}\u201D`, {
|
|
1229
1913
|
x: rect.x + 0.2,
|
|
1230
1914
|
y: rect.y,
|
|
1231
1915
|
w: rect.w - 0.2,
|
|
1232
1916
|
h: rect.h,
|
|
1233
|
-
fontSize:
|
|
1917
|
+
fontSize: 18,
|
|
1234
1918
|
italic: true,
|
|
1235
1919
|
color: this.cleanHexColor(theme.colors.muted || theme.colors.text),
|
|
1236
1920
|
fontFace: theme.typography.bodyFont.split(",")[0]?.trim() || "Arial",
|
|
@@ -1282,7 +1966,7 @@ var PptxRenderer = class {
|
|
|
1282
1966
|
// src/cli.ts
|
|
1283
1967
|
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
1284
1968
|
import { basename, dirname, extname, join, resolve } from "path";
|
|
1285
|
-
var VERSION = "0.1.
|
|
1969
|
+
var VERSION = "0.1.8";
|
|
1286
1970
|
function printHelp() {
|
|
1287
1971
|
return `
|
|
1288
1972
|
YumiaMD \u2014 Markdown-based presentation compiler (v${VERSION})
|
|
@@ -1301,6 +1985,7 @@ Commands:
|
|
|
1301
1985
|
Options:
|
|
1302
1986
|
--out, -o <file> Specify output file path (default: dist/<name>.pptx)
|
|
1303
1987
|
--format, -f <fmt> Target output format: pptx (default)
|
|
1988
|
+
--strict Enforce zero warnings in 'lint' (exits with code 1 on warning)
|
|
1304
1989
|
--json Output results formatted as JSON for CI/CD and AI tools
|
|
1305
1990
|
--layout Show computed geometric bounding boxes in 'inspect'
|
|
1306
1991
|
-h, --help Show this help message
|
|
@@ -1316,6 +2001,7 @@ async function runCli(argv) {
|
|
|
1316
2001
|
return { exitCode: 0, output: `yumia v${VERSION}` };
|
|
1317
2002
|
}
|
|
1318
2003
|
const isJson = args.includes("--json");
|
|
2004
|
+
const isStrict = args.includes("--strict");
|
|
1319
2005
|
const nonFlagArgs = args.filter((a) => !a.startsWith("-"));
|
|
1320
2006
|
const command = nonFlagArgs[0];
|
|
1321
2007
|
const target = nonFlagArgs[1];
|
|
@@ -1452,57 +2138,46 @@ ${errorLines.join("\n")}`
|
|
|
1452
2138
|
}
|
|
1453
2139
|
try {
|
|
1454
2140
|
const source = readFileSync(target, "utf-8");
|
|
1455
|
-
const
|
|
1456
|
-
const
|
|
1457
|
-
const layout = engine.computePresentation(presentation);
|
|
1458
|
-
const issues = [];
|
|
1459
|
-
layout.slides.forEach((slideResult, index) => {
|
|
1460
|
-
const slideNum = index + 1;
|
|
1461
|
-
if (slideResult.overflow) {
|
|
1462
|
-
issues.push({
|
|
1463
|
-
slide: slideNum,
|
|
1464
|
-
type: "overflow",
|
|
1465
|
-
message: `Content exceeds slide height by ~${Math.round(slideResult.overflowAmount || 0)}px`
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
const slide = presentation.slides[index];
|
|
1469
|
-
if (slide.elements.length === 0) {
|
|
1470
|
-
issues.push({
|
|
1471
|
-
slide: slideNum,
|
|
1472
|
-
type: "empty",
|
|
1473
|
-
message: "Slide has no content elements."
|
|
1474
|
-
});
|
|
1475
|
-
}
|
|
1476
|
-
});
|
|
2141
|
+
const compiler = new YumiaCompiler();
|
|
2142
|
+
const report = compiler.lint(source, { strict: isStrict });
|
|
1477
2143
|
if (isJson) {
|
|
1478
2144
|
return {
|
|
1479
|
-
exitCode: 0,
|
|
2145
|
+
exitCode: report.passed ? 0 : 1,
|
|
1480
2146
|
output: JSON.stringify(
|
|
1481
2147
|
{
|
|
1482
2148
|
target,
|
|
1483
|
-
|
|
1484
|
-
slideCount: presentation.slides.length,
|
|
1485
|
-
issues
|
|
2149
|
+
...report
|
|
1486
2150
|
},
|
|
1487
2151
|
null,
|
|
1488
2152
|
2
|
|
1489
2153
|
)
|
|
1490
2154
|
};
|
|
1491
2155
|
}
|
|
1492
|
-
if (
|
|
2156
|
+
if (report.issueCount === 0) {
|
|
1493
2157
|
return {
|
|
1494
2158
|
exitCode: 0,
|
|
1495
|
-
output: `\u2713 Yumia Lint: All ${
|
|
2159
|
+
output: `\u2713 Yumia Lint: All ${report.totalSlides} slide(s) passed with 0 issues.`
|
|
1496
2160
|
};
|
|
1497
2161
|
} else {
|
|
1498
|
-
const
|
|
2162
|
+
const issueLines = [];
|
|
2163
|
+
for (const e of report.errors) {
|
|
2164
|
+
issueLines.push(` \u2717 [${e.code}] Slide ${e.slide}: ${e.message}`);
|
|
2165
|
+
}
|
|
2166
|
+
for (const w of report.warnings) {
|
|
2167
|
+
issueLines.push(` \u26A0 [${w.code}] Slide ${w.slide}: ${w.message}`);
|
|
2168
|
+
}
|
|
2169
|
+
for (const info of report.infos) {
|
|
2170
|
+
issueLines.push(` \u2139 [${info.code}] Slide ${info.slide}: ${info.message}`);
|
|
2171
|
+
}
|
|
2172
|
+
const summary = `Found ${report.errors.length} error(s), ${report.warnings.length} warning(s).`;
|
|
2173
|
+
const exitCode = report.passed ? 0 : 1;
|
|
1499
2174
|
return {
|
|
1500
|
-
exitCode
|
|
2175
|
+
exitCode,
|
|
1501
2176
|
output: `Yumia Lint Report for '${target}':
|
|
1502
2177
|
|
|
1503
|
-
${
|
|
2178
|
+
${issueLines.join("\n")}
|
|
1504
2179
|
|
|
1505
|
-
|
|
2180
|
+
${summary}${isStrict && report.warnings.length > 0 ? " (failed due to --strict)" : ""}`
|
|
1506
2181
|
};
|
|
1507
2182
|
}
|
|
1508
2183
|
} catch (err) {
|
|
@@ -1614,6 +2289,7 @@ export {
|
|
|
1614
2289
|
createList,
|
|
1615
2290
|
createImage,
|
|
1616
2291
|
createCard,
|
|
2292
|
+
createMetric,
|
|
1617
2293
|
createCode,
|
|
1618
2294
|
createQuote,
|
|
1619
2295
|
createTable,
|
|
@@ -1627,8 +2303,17 @@ export {
|
|
|
1627
2303
|
VIEWPORT_PRESETS,
|
|
1628
2304
|
DefaultLayoutEngine,
|
|
1629
2305
|
defaultTheme,
|
|
2306
|
+
cyberpunkTheme,
|
|
2307
|
+
minimalTheme,
|
|
2308
|
+
corporateTheme,
|
|
2309
|
+
terminalTheme,
|
|
2310
|
+
academicTheme,
|
|
2311
|
+
THEME_REGISTRY,
|
|
2312
|
+
resolveTheme,
|
|
1630
2313
|
createTheme,
|
|
2314
|
+
YumiaLinter,
|
|
1631
2315
|
YumiaCompiler,
|
|
2316
|
+
parseInlineMarkdown,
|
|
1632
2317
|
PptxRenderer,
|
|
1633
2318
|
VERSION,
|
|
1634
2319
|
printHelp,
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export * from '@yumiamd/layout';
|
|
|
6
6
|
export * from '@yumiamd/renderer';
|
|
7
7
|
export * from '@yumiamd/renderer-pptx';
|
|
8
8
|
|
|
9
|
-
declare const VERSION = "0.1.
|
|
9
|
+
declare const VERSION = "0.1.8";
|
|
10
10
|
declare function printHelp(): string;
|
|
11
11
|
declare function runCli(argv: string[]): Promise<{
|
|
12
12
|
exitCode: number;
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,13 @@ import {
|
|
|
3
3
|
DefaultLayoutEngine,
|
|
4
4
|
DefaultYumiaParser,
|
|
5
5
|
PptxRenderer,
|
|
6
|
+
THEME_REGISTRY,
|
|
6
7
|
VERSION,
|
|
7
8
|
VIEWPORT_PRESETS,
|
|
8
9
|
YumiaCompiler,
|
|
10
|
+
YumiaLinter,
|
|
11
|
+
academicTheme,
|
|
12
|
+
corporateTheme,
|
|
9
13
|
createCard,
|
|
10
14
|
createCode,
|
|
11
15
|
createColumn,
|
|
@@ -15,25 +19,35 @@ import {
|
|
|
15
19
|
createImage,
|
|
16
20
|
createLayoutDirective,
|
|
17
21
|
createList,
|
|
22
|
+
createMetric,
|
|
18
23
|
createParagraph,
|
|
19
24
|
createPresentation,
|
|
20
25
|
createQuote,
|
|
21
26
|
createSlide,
|
|
22
27
|
createTable,
|
|
23
28
|
createTheme,
|
|
29
|
+
cyberpunkTheme,
|
|
24
30
|
defaultTheme,
|
|
31
|
+
minimalTheme,
|
|
32
|
+
parseInlineMarkdown,
|
|
25
33
|
parseYumia,
|
|
26
34
|
printHelp,
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
resolveTheme,
|
|
36
|
+
runCli,
|
|
37
|
+
terminalTheme
|
|
38
|
+
} from "./chunk-WUJKHJJU.js";
|
|
29
39
|
export {
|
|
30
40
|
DEFAULT_VIEWPORT,
|
|
31
41
|
DefaultLayoutEngine,
|
|
32
42
|
DefaultYumiaParser,
|
|
33
43
|
PptxRenderer,
|
|
44
|
+
THEME_REGISTRY,
|
|
34
45
|
VERSION,
|
|
35
46
|
VIEWPORT_PRESETS,
|
|
36
47
|
YumiaCompiler,
|
|
48
|
+
YumiaLinter,
|
|
49
|
+
academicTheme,
|
|
50
|
+
corporateTheme,
|
|
37
51
|
createCard,
|
|
38
52
|
createCode,
|
|
39
53
|
createColumn,
|
|
@@ -43,14 +57,20 @@ export {
|
|
|
43
57
|
createImage,
|
|
44
58
|
createLayoutDirective,
|
|
45
59
|
createList,
|
|
60
|
+
createMetric,
|
|
46
61
|
createParagraph,
|
|
47
62
|
createPresentation,
|
|
48
63
|
createQuote,
|
|
49
64
|
createSlide,
|
|
50
65
|
createTable,
|
|
51
66
|
createTheme,
|
|
67
|
+
cyberpunkTheme,
|
|
52
68
|
defaultTheme,
|
|
69
|
+
minimalTheme,
|
|
70
|
+
parseInlineMarkdown,
|
|
53
71
|
parseYumia,
|
|
54
72
|
printHelp,
|
|
55
|
-
|
|
73
|
+
resolveTheme,
|
|
74
|
+
runCli,
|
|
75
|
+
terminalTheme
|
|
56
76
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yumiamd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Command line interface and compiler for YumiaMD presentations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"tsup": "^8.5.1",
|
|
23
|
-
"@yumiamd/ast": "0.1.
|
|
24
|
-
"@yumiamd/parser": "0.1.
|
|
25
|
-
"@yumiamd/core": "0.1.
|
|
26
|
-
"@yumiamd/
|
|
27
|
-
"@yumiamd/
|
|
28
|
-
"@yumiamd/renderer-pptx": "0.1.
|
|
29
|
-
"@yumiamd/theme": "0.1.
|
|
23
|
+
"@yumiamd/ast": "0.1.8",
|
|
24
|
+
"@yumiamd/parser": "0.1.8",
|
|
25
|
+
"@yumiamd/core": "0.1.8",
|
|
26
|
+
"@yumiamd/renderer": "0.1.8",
|
|
27
|
+
"@yumiamd/layout": "0.1.8",
|
|
28
|
+
"@yumiamd/renderer-pptx": "0.1.8",
|
|
29
|
+
"@yumiamd/theme": "0.1.8"
|
|
30
30
|
},
|
|
31
31
|
"keywords": [
|
|
32
32
|
"yumia",
|