yumiamd 0.1.15 → 0.1.16
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-7A5BHABM.js → chunk-UPUEFJLX.js} +974 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -1
- package/package.json +10 -10
package/dist/bin.js
CHANGED
|
@@ -81,6 +81,45 @@ function createTable(rows, headers) {
|
|
|
81
81
|
...headers ? { headers } : {}
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
+
function createChart(chartType, labels, series, title) {
|
|
85
|
+
return {
|
|
86
|
+
type: "chart",
|
|
87
|
+
chartType,
|
|
88
|
+
labels,
|
|
89
|
+
series,
|
|
90
|
+
...title ? { title } : {}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function createMermaid(code, chartType) {
|
|
94
|
+
return {
|
|
95
|
+
type: "mermaid",
|
|
96
|
+
code,
|
|
97
|
+
...chartType ? { chartType } : {}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function createTimeline(items, layout = "horizontal") {
|
|
101
|
+
return {
|
|
102
|
+
type: "timeline",
|
|
103
|
+
items,
|
|
104
|
+
layout
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function createCompare(left, right, leftTitle, rightTitle) {
|
|
108
|
+
return {
|
|
109
|
+
type: "compare",
|
|
110
|
+
left,
|
|
111
|
+
right,
|
|
112
|
+
...leftTitle ? { leftTitle } : {},
|
|
113
|
+
...rightTitle ? { rightTitle } : {}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function createBadge(text, variant) {
|
|
117
|
+
return {
|
|
118
|
+
type: "badge",
|
|
119
|
+
text,
|
|
120
|
+
...variant ? { variant } : {}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
84
123
|
function createGroup(elements, direction = "row", gap) {
|
|
85
124
|
return {
|
|
86
125
|
type: "group",
|
|
@@ -402,6 +441,32 @@ var DefaultYumiaParser = class {
|
|
|
402
441
|
i++;
|
|
403
442
|
continue;
|
|
404
443
|
}
|
|
444
|
+
if (directiveHeader.startsWith("badge")) {
|
|
445
|
+
const badgeArg = directiveHeader.slice(5).trim();
|
|
446
|
+
const variantMatch = badgeArg.match(/variant=['"](.*?)['"]/);
|
|
447
|
+
const textMatch = badgeArg.match(/text=['"](.*?)['"]/);
|
|
448
|
+
const variant = variantMatch ? variantMatch[1] : void 0;
|
|
449
|
+
let text = textMatch ? textMatch[1] : badgeArg.replace(/variant=['"].*?['"]/, "").trim();
|
|
450
|
+
text = text.replace(/^['"](.*)['"]$/, "$1");
|
|
451
|
+
const el = createBadge(text, variant);
|
|
452
|
+
el.loc = {
|
|
453
|
+
start: { line: currentLineNum, column: 1 },
|
|
454
|
+
end: { line: currentLineNum, column: rawLine.length + 1 }
|
|
455
|
+
};
|
|
456
|
+
elements.push(el);
|
|
457
|
+
i++;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (directiveHeader.startsWith("chart") && (directiveHeader.includes("data=") || directiveHeader.includes("labels="))) {
|
|
461
|
+
const el = this.parseChartDirective(directiveHeader, []);
|
|
462
|
+
el.loc = {
|
|
463
|
+
start: { line: currentLineNum, column: 1 },
|
|
464
|
+
end: { line: currentLineNum, column: rawLine.length + 1 }
|
|
465
|
+
};
|
|
466
|
+
elements.push(el);
|
|
467
|
+
i++;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
405
470
|
const [directiveName, ...args] = directiveHeader.split(" ");
|
|
406
471
|
const directiveArg = args.join(" ").trim();
|
|
407
472
|
const blockLines = [];
|
|
@@ -410,7 +475,7 @@ var DefaultYumiaParser = class {
|
|
|
410
475
|
let closed = false;
|
|
411
476
|
while (i < lines.length) {
|
|
412
477
|
const innerLine = lines[i]?.trim() ?? "";
|
|
413
|
-
const isSingleLine = innerLine.startsWith(":::metric") || innerLine.startsWith(":::layout");
|
|
478
|
+
const isSingleLine = innerLine.startsWith(":::metric") || innerLine.startsWith(":::layout") || innerLine.startsWith(":::badge") && (innerLine.includes("text=") || innerLine.includes("variant=")) || innerLine.startsWith(":::chart") && innerLine.includes("data=");
|
|
414
479
|
if (innerLine.startsWith(":::") && innerLine.length > 3 && !isSingleLine) {
|
|
415
480
|
nestedCount++;
|
|
416
481
|
} else if (innerLine === ":::") {
|
|
@@ -473,6 +538,41 @@ var DefaultYumiaParser = class {
|
|
|
473
538
|
end: { line: baseLine + i - 1, column: 1 }
|
|
474
539
|
};
|
|
475
540
|
elements.push(el);
|
|
541
|
+
} else if (directiveName === "mermaid") {
|
|
542
|
+
const code = blockLines.join("\n").trim();
|
|
543
|
+
const el = createMermaid(code);
|
|
544
|
+
el.loc = {
|
|
545
|
+
start: { line: directiveStartLine, column: 1 },
|
|
546
|
+
end: { line: baseLine + i - 1, column: 1 }
|
|
547
|
+
};
|
|
548
|
+
elements.push(el);
|
|
549
|
+
} else if (directiveName === "chart") {
|
|
550
|
+
const el = this.parseChartDirective(directiveArg, blockLines);
|
|
551
|
+
el.loc = {
|
|
552
|
+
start: { line: directiveStartLine, column: 1 },
|
|
553
|
+
end: { line: baseLine + i - 1, column: 1 }
|
|
554
|
+
};
|
|
555
|
+
elements.push(el);
|
|
556
|
+
} else if (directiveName === "timeline") {
|
|
557
|
+
const el = this.parseTimelineBlock(directiveArg, blockLines);
|
|
558
|
+
el.loc = {
|
|
559
|
+
start: { line: directiveStartLine, column: 1 },
|
|
560
|
+
end: { line: baseLine + i - 1, column: 1 }
|
|
561
|
+
};
|
|
562
|
+
elements.push(el);
|
|
563
|
+
} else if (directiveName === "compare") {
|
|
564
|
+
const el = this.parseCompareBlock(directiveArg, blockLines, blockBaseLine);
|
|
565
|
+
el.loc = {
|
|
566
|
+
start: { line: directiveStartLine, column: 1 },
|
|
567
|
+
end: { line: baseLine + i - 1, column: 1 }
|
|
568
|
+
};
|
|
569
|
+
elements.push(el);
|
|
570
|
+
} else if (directiveName === "step") {
|
|
571
|
+
const { elements: stepElements } = this.parseLines(blockLines, blockBaseLine);
|
|
572
|
+
for (const sEl of stepElements) {
|
|
573
|
+
sEl.step = 1;
|
|
574
|
+
elements.push(sEl);
|
|
575
|
+
}
|
|
476
576
|
}
|
|
477
577
|
continue;
|
|
478
578
|
}
|
|
@@ -621,6 +721,140 @@ var DefaultYumiaParser = class {
|
|
|
621
721
|
}
|
|
622
722
|
return columns;
|
|
623
723
|
}
|
|
724
|
+
parseChartDirective(headerArg, blockLines) {
|
|
725
|
+
const typeMatch = headerArg.match(/type=['"](.*?)['"]/);
|
|
726
|
+
const titleMatch = headerArg.match(/title=['"](.*?)['"]/);
|
|
727
|
+
const labelsMatch = headerArg.match(/labels=['"](.*?)['"]/);
|
|
728
|
+
const dataMatch = headerArg.match(/data=['"](.*?)['"]/);
|
|
729
|
+
const chartType = typeMatch ? typeMatch[1] : "bar";
|
|
730
|
+
const title = titleMatch ? titleMatch[1] : void 0;
|
|
731
|
+
let labels = [];
|
|
732
|
+
const series = [];
|
|
733
|
+
if (labelsMatch && labelsMatch[1]) {
|
|
734
|
+
labels = labelsMatch[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
735
|
+
}
|
|
736
|
+
if (dataMatch && dataMatch[1]) {
|
|
737
|
+
const rawData = dataMatch[1].trim();
|
|
738
|
+
if (rawData.includes(":")) {
|
|
739
|
+
const parts = rawData.split(",").map((p) => p.trim());
|
|
740
|
+
const values = [];
|
|
741
|
+
const extractedLabels = [];
|
|
742
|
+
for (const part of parts) {
|
|
743
|
+
const [k, v] = part.split(":").map((s) => s.trim());
|
|
744
|
+
if (k && v !== void 0) {
|
|
745
|
+
extractedLabels.push(k);
|
|
746
|
+
values.push(parseFloat(v) || 0);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (labels.length === 0)
|
|
750
|
+
labels = extractedLabels;
|
|
751
|
+
series.push({ name: title || "Data", values });
|
|
752
|
+
} else {
|
|
753
|
+
const values = rawData.split(",").map((v) => parseFloat(v.trim()) || 0);
|
|
754
|
+
series.push({ name: title || "Data", values });
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
for (const line of blockLines) {
|
|
758
|
+
const trimmed = line.trim();
|
|
759
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
760
|
+
continue;
|
|
761
|
+
if (trimmed.toLowerCase().startsWith("labels:")) {
|
|
762
|
+
labels = trimmed.slice(7).split(",").map((s) => s.trim()).filter(Boolean);
|
|
763
|
+
} else if (trimmed.toLowerCase().startsWith("series:")) {
|
|
764
|
+
const seriesContent = trimmed.slice(7).trim();
|
|
765
|
+
const sMatch = seriesContent.match(/^(.*?)\s*\[(.*?)\]$/);
|
|
766
|
+
if (sMatch && sMatch[1] && sMatch[2]) {
|
|
767
|
+
const sName = sMatch[1].trim();
|
|
768
|
+
const sValues = sMatch[2].split(",").map((v) => parseFloat(v.trim()) || 0);
|
|
769
|
+
series.push({ name: sName, values: sValues });
|
|
770
|
+
}
|
|
771
|
+
} else if (trimmed.includes("|")) {
|
|
772
|
+
const parts = trimmed.split("|").map((s) => s.trim());
|
|
773
|
+
if (parts.length >= 2) {
|
|
774
|
+
const [l, valStr] = parts;
|
|
775
|
+
if (l && valStr) {
|
|
776
|
+
labels.push(l);
|
|
777
|
+
const val = parseFloat(valStr) || 0;
|
|
778
|
+
if (series.length === 0)
|
|
779
|
+
series.push({ name: "Data", values: [] });
|
|
780
|
+
series[0]?.values.push(val);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (labels.length === 0) {
|
|
786
|
+
const maxLen = Math.max(0, ...series.map((s) => s.values.length));
|
|
787
|
+
labels = Array.from({ length: maxLen }, (_, i) => `Item ${i + 1}`);
|
|
788
|
+
}
|
|
789
|
+
return createChart(chartType, labels, series, title);
|
|
790
|
+
}
|
|
791
|
+
parseTimelineBlock(headerArg, blockLines) {
|
|
792
|
+
const layoutMatch = headerArg.match(/layout=['"](.*?)['"]/);
|
|
793
|
+
const layout = layoutMatch && layoutMatch[1] === "vertical" ? "vertical" : "horizontal";
|
|
794
|
+
const items = [];
|
|
795
|
+
for (const line of blockLines) {
|
|
796
|
+
const trimmed = line.trim();
|
|
797
|
+
if (!trimmed)
|
|
798
|
+
continue;
|
|
799
|
+
const bracketMatch = trimmed.match(/^[-*]?\s*\[(.*?)\]\s*(.*?)(?::\s*(.*))?$/);
|
|
800
|
+
if (bracketMatch && bracketMatch[1] && bracketMatch[2]) {
|
|
801
|
+
items.push({
|
|
802
|
+
date: bracketMatch[1].trim(),
|
|
803
|
+
title: bracketMatch[2].trim(),
|
|
804
|
+
description: bracketMatch[3] ? bracketMatch[3].trim() : void 0
|
|
805
|
+
});
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if (trimmed.includes("|")) {
|
|
809
|
+
const parts = trimmed.split("|").map((s) => s.trim());
|
|
810
|
+
if (parts.length >= 2) {
|
|
811
|
+
items.push({
|
|
812
|
+
date: parts[0],
|
|
813
|
+
title: parts[1] || "",
|
|
814
|
+
description: parts[2] || void 0
|
|
815
|
+
});
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
items.push({
|
|
820
|
+
title: trimmed.replace(/^[-*]\s*/, "")
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
return createTimeline(items, layout);
|
|
824
|
+
}
|
|
825
|
+
parseCompareBlock(headerArg, blockLines, baseLine) {
|
|
826
|
+
const leftMatch = headerArg.match(/left=['"](.*?)['"]/);
|
|
827
|
+
const rightMatch = headerArg.match(/right=['"](.*?)['"]/);
|
|
828
|
+
const leftTitle = leftMatch ? leftMatch[1] : void 0;
|
|
829
|
+
const rightTitle = rightMatch ? rightMatch[1] : void 0;
|
|
830
|
+
let leftLines = [];
|
|
831
|
+
let rightLines = [];
|
|
832
|
+
let currentSide = "left";
|
|
833
|
+
for (const line of blockLines) {
|
|
834
|
+
const trimmed = line.trim();
|
|
835
|
+
if (trimmed === ":::vs" || trimmed === ":::right" || trimmed === "---") {
|
|
836
|
+
currentSide = "right";
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
if (trimmed === ":::left") {
|
|
840
|
+
currentSide = "left";
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
if (currentSide === "left") {
|
|
844
|
+
leftLines.push(line);
|
|
845
|
+
} else {
|
|
846
|
+
rightLines.push(line);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
if (rightLines.length === 0 && leftLines.length > 1) {
|
|
850
|
+
const half = Math.ceil(leftLines.length / 2);
|
|
851
|
+
rightLines = leftLines.slice(half);
|
|
852
|
+
leftLines = leftLines.slice(0, half);
|
|
853
|
+
}
|
|
854
|
+
const { elements: leftElements } = this.parseLines(leftLines, baseLine);
|
|
855
|
+
const { elements: rightElements } = this.parseLines(rightLines, baseLine + leftLines.length);
|
|
856
|
+
return createCompare(leftElements, rightElements, leftTitle, rightTitle);
|
|
857
|
+
}
|
|
624
858
|
};
|
|
625
859
|
function parseYumia(source, options) {
|
|
626
860
|
const parser = new DefaultYumiaParser();
|
|
@@ -2050,6 +2284,21 @@ var PptxRenderer = class {
|
|
|
2050
2284
|
case "columns":
|
|
2051
2285
|
this.renderColumns(pptxSlide, pptx, node, scaleX, scaleY, theme);
|
|
2052
2286
|
break;
|
|
2287
|
+
case "badge":
|
|
2288
|
+
this.renderBadge(pptxSlide, pptx, element, rect, theme);
|
|
2289
|
+
break;
|
|
2290
|
+
case "chart":
|
|
2291
|
+
this.renderChart(pptxSlide, pptx, element, rect, theme);
|
|
2292
|
+
break;
|
|
2293
|
+
case "timeline":
|
|
2294
|
+
this.renderTimeline(pptxSlide, pptx, element, rect, theme);
|
|
2295
|
+
break;
|
|
2296
|
+
case "compare":
|
|
2297
|
+
this.renderCompare(pptxSlide, pptx, node, scaleX, scaleY, theme);
|
|
2298
|
+
break;
|
|
2299
|
+
case "mermaid":
|
|
2300
|
+
this.renderMermaid(pptxSlide, pptx, element, rect, theme);
|
|
2301
|
+
break;
|
|
2053
2302
|
default:
|
|
2054
2303
|
break;
|
|
2055
2304
|
}
|
|
@@ -2358,6 +2607,216 @@ var PptxRenderer = class {
|
|
|
2358
2607
|
valign: "middle"
|
|
2359
2608
|
});
|
|
2360
2609
|
}
|
|
2610
|
+
renderBadge(pptxSlide, pptx, badge, rect, theme) {
|
|
2611
|
+
const colorKey = badge.variant || "primary";
|
|
2612
|
+
const badgeColor = this.cleanHexColor(theme.colors[colorKey] || theme.colors.primary);
|
|
2613
|
+
const badgeW = Math.min(2.5, Math.max(1, badge.text.length * 0.12 + 0.4));
|
|
2614
|
+
const badgeH = Math.min(0.38, rect.h);
|
|
2615
|
+
pptxSlide.addShape(pptx.ShapeType.roundRect, {
|
|
2616
|
+
x: rect.x,
|
|
2617
|
+
y: rect.y,
|
|
2618
|
+
w: badgeW,
|
|
2619
|
+
h: badgeH,
|
|
2620
|
+
fill: { color: this.cleanHexColor(theme.colors.surface) },
|
|
2621
|
+
line: { color: badgeColor, width: 1.5 },
|
|
2622
|
+
rectRadius: 0.15
|
|
2623
|
+
});
|
|
2624
|
+
pptxSlide.addText(badge.text.toUpperCase(), {
|
|
2625
|
+
x: rect.x,
|
|
2626
|
+
y: rect.y,
|
|
2627
|
+
w: badgeW,
|
|
2628
|
+
h: badgeH,
|
|
2629
|
+
fontSize: 10,
|
|
2630
|
+
bold: true,
|
|
2631
|
+
color: badgeColor,
|
|
2632
|
+
fontFace: cleanFontFace(theme.typography.headingFont),
|
|
2633
|
+
align: "center",
|
|
2634
|
+
valign: "middle"
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2637
|
+
renderChart(pptxSlide, pptx, chart, rect, theme) {
|
|
2638
|
+
const series = chart.series || [];
|
|
2639
|
+
const labels = chart.labels || [];
|
|
2640
|
+
const chartData = series.map((s) => ({
|
|
2641
|
+
name: s.name || "Data",
|
|
2642
|
+
labels,
|
|
2643
|
+
values: s.values
|
|
2644
|
+
}));
|
|
2645
|
+
let pptxChartType = pptx.ChartType.bar;
|
|
2646
|
+
if (chart.chartType === "line")
|
|
2647
|
+
pptxChartType = pptx.ChartType.line;
|
|
2648
|
+
if (chart.chartType === "pie")
|
|
2649
|
+
pptxChartType = pptx.ChartType.pie;
|
|
2650
|
+
if (chart.chartType === "doughnut")
|
|
2651
|
+
pptxChartType = pptx.ChartType.doughnut;
|
|
2652
|
+
const chartColors = [
|
|
2653
|
+
this.cleanHexColor(theme.colors.primary),
|
|
2654
|
+
this.cleanHexColor(theme.colors.accent),
|
|
2655
|
+
this.cleanHexColor(theme.colors.secondary),
|
|
2656
|
+
this.cleanHexColor(theme.colors.success),
|
|
2657
|
+
this.cleanHexColor(theme.colors.warning)
|
|
2658
|
+
];
|
|
2659
|
+
try {
|
|
2660
|
+
pptxSlide.addChart(pptxChartType, chartData, {
|
|
2661
|
+
x: rect.x,
|
|
2662
|
+
y: rect.y,
|
|
2663
|
+
w: rect.w,
|
|
2664
|
+
h: rect.h,
|
|
2665
|
+
showTitle: Boolean(chart.title),
|
|
2666
|
+
title: chart.title || "",
|
|
2667
|
+
titleColor: this.cleanHexColor(theme.colors.text),
|
|
2668
|
+
titleFontFace: cleanFontFace(theme.typography.headingFont),
|
|
2669
|
+
showLegend: true,
|
|
2670
|
+
legendPos: "b",
|
|
2671
|
+
legendColor: this.cleanHexColor(theme.colors.muted || theme.colors.text),
|
|
2672
|
+
chartColors
|
|
2673
|
+
});
|
|
2674
|
+
} catch {
|
|
2675
|
+
pptxSlide.addShape(pptx.ShapeType.roundRect, {
|
|
2676
|
+
x: rect.x,
|
|
2677
|
+
y: rect.y,
|
|
2678
|
+
w: rect.w,
|
|
2679
|
+
h: rect.h,
|
|
2680
|
+
fill: { color: this.cleanHexColor(theme.colors.surface) },
|
|
2681
|
+
line: { color: this.cleanHexColor(theme.colors.border), width: 1 }
|
|
2682
|
+
});
|
|
2683
|
+
pptxSlide.addText(`[Chart: ${chart.title || chart.chartType}]`, {
|
|
2684
|
+
x: rect.x,
|
|
2685
|
+
y: rect.y,
|
|
2686
|
+
w: rect.w,
|
|
2687
|
+
h: rect.h,
|
|
2688
|
+
color: this.cleanHexColor(theme.colors.primary),
|
|
2689
|
+
align: "center",
|
|
2690
|
+
valign: "middle"
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
renderTimeline(pptxSlide, pptx, timeline, rect, theme) {
|
|
2695
|
+
const items = timeline.items || [];
|
|
2696
|
+
if (items.length === 0)
|
|
2697
|
+
return;
|
|
2698
|
+
const itemW = rect.w / items.length;
|
|
2699
|
+
const lineY = rect.y + 0.25;
|
|
2700
|
+
pptxSlide.addShape(pptx.ShapeType.rect, {
|
|
2701
|
+
x: rect.x + 0.2,
|
|
2702
|
+
y: lineY,
|
|
2703
|
+
w: rect.w - 0.4,
|
|
2704
|
+
h: 0.03,
|
|
2705
|
+
fill: { color: this.cleanHexColor(theme.colors.border) }
|
|
2706
|
+
});
|
|
2707
|
+
items.forEach((item, idx) => {
|
|
2708
|
+
const itemX = rect.x + idx * itemW;
|
|
2709
|
+
const dotX = itemX + itemW / 2 - 0.1;
|
|
2710
|
+
pptxSlide.addShape(pptx.ShapeType.roundRect, {
|
|
2711
|
+
x: dotX,
|
|
2712
|
+
y: lineY - 0.08,
|
|
2713
|
+
w: 0.2,
|
|
2714
|
+
h: 0.2,
|
|
2715
|
+
fill: { color: this.cleanHexColor(theme.colors.primary) },
|
|
2716
|
+
line: { color: this.cleanHexColor(theme.colors.background), width: 2 },
|
|
2717
|
+
rectRadius: 0.1
|
|
2718
|
+
});
|
|
2719
|
+
if (item.date) {
|
|
2720
|
+
pptxSlide.addText(item.date, {
|
|
2721
|
+
x: itemX,
|
|
2722
|
+
y: lineY + 0.18,
|
|
2723
|
+
w: itemW,
|
|
2724
|
+
h: 0.25,
|
|
2725
|
+
fontSize: 11,
|
|
2726
|
+
bold: true,
|
|
2727
|
+
color: this.cleanHexColor(theme.colors.accent || theme.colors.primary),
|
|
2728
|
+
fontFace: cleanFontFace(theme.typography.codeFont),
|
|
2729
|
+
align: "center"
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
const descText = item.description ? `
|
|
2733
|
+
${item.description}` : "";
|
|
2734
|
+
pptxSlide.addText(`${item.title}${descText}`, {
|
|
2735
|
+
x: itemX + 0.05,
|
|
2736
|
+
y: lineY + 0.45,
|
|
2737
|
+
w: itemW - 0.1,
|
|
2738
|
+
h: rect.h - 0.55,
|
|
2739
|
+
fontSize: 12,
|
|
2740
|
+
color: this.cleanHexColor(theme.colors.text),
|
|
2741
|
+
fontFace: cleanFontFace(theme.typography.bodyFont),
|
|
2742
|
+
align: "center",
|
|
2743
|
+
valign: "top"
|
|
2744
|
+
});
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
renderCompare(pptxSlide, pptx, node, scaleX, scaleY, theme) {
|
|
2748
|
+
const { element, bounds } = node;
|
|
2749
|
+
const compare = element;
|
|
2750
|
+
const rect = this.toInches(bounds, scaleX, scaleY);
|
|
2751
|
+
const colW = (rect.w - 0.4) / 2;
|
|
2752
|
+
pptxSlide.addShape(pptx.ShapeType.roundRect, {
|
|
2753
|
+
x: rect.x,
|
|
2754
|
+
y: rect.y,
|
|
2755
|
+
w: colW,
|
|
2756
|
+
h: rect.h,
|
|
2757
|
+
fill: { color: this.cleanHexColor(theme.colors.surface) },
|
|
2758
|
+
line: { color: this.cleanHexColor(theme.colors.border), width: 1 },
|
|
2759
|
+
rectRadius: 0.1
|
|
2760
|
+
});
|
|
2761
|
+
if (compare.leftTitle) {
|
|
2762
|
+
pptxSlide.addText(compare.leftTitle, {
|
|
2763
|
+
x: rect.x + 0.15,
|
|
2764
|
+
y: rect.y + 0.15,
|
|
2765
|
+
w: colW - 0.3,
|
|
2766
|
+
h: 0.4,
|
|
2767
|
+
fontSize: 14,
|
|
2768
|
+
bold: true,
|
|
2769
|
+
color: this.cleanHexColor(theme.colors.primary),
|
|
2770
|
+
fontFace: cleanFontFace(theme.typography.headingFont)
|
|
2771
|
+
});
|
|
2772
|
+
}
|
|
2773
|
+
const rightX = rect.x + colW + 0.4;
|
|
2774
|
+
pptxSlide.addShape(pptx.ShapeType.roundRect, {
|
|
2775
|
+
x: rightX,
|
|
2776
|
+
y: rect.y,
|
|
2777
|
+
w: colW,
|
|
2778
|
+
h: rect.h,
|
|
2779
|
+
fill: { color: this.cleanHexColor(theme.colors.surface) },
|
|
2780
|
+
line: { color: this.cleanHexColor(theme.colors.border), width: 1 },
|
|
2781
|
+
rectRadius: 0.1
|
|
2782
|
+
});
|
|
2783
|
+
if (compare.rightTitle) {
|
|
2784
|
+
pptxSlide.addText(compare.rightTitle, {
|
|
2785
|
+
x: rightX + 0.15,
|
|
2786
|
+
y: rect.y + 0.15,
|
|
2787
|
+
w: colW - 0.3,
|
|
2788
|
+
h: 0.4,
|
|
2789
|
+
fontSize: 14,
|
|
2790
|
+
bold: true,
|
|
2791
|
+
color: this.cleanHexColor(theme.colors.primary),
|
|
2792
|
+
fontFace: cleanFontFace(theme.typography.headingFont)
|
|
2793
|
+
});
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
renderMermaid(pptxSlide, pptx, mermaid, rect, theme) {
|
|
2797
|
+
pptxSlide.addShape(pptx.ShapeType.roundRect, {
|
|
2798
|
+
x: rect.x,
|
|
2799
|
+
y: rect.y,
|
|
2800
|
+
w: rect.w,
|
|
2801
|
+
h: rect.h,
|
|
2802
|
+
fill: { color: this.cleanHexColor(theme.colors.surface) },
|
|
2803
|
+
line: { color: this.cleanHexColor(theme.colors.primary), width: 1 },
|
|
2804
|
+
rectRadius: 0.1
|
|
2805
|
+
});
|
|
2806
|
+
pptxSlide.addText(`[Diagram: Mermaid]
|
|
2807
|
+
|
|
2808
|
+
${mermaid.code}`, {
|
|
2809
|
+
x: rect.x + 0.2,
|
|
2810
|
+
y: rect.y + 0.2,
|
|
2811
|
+
w: rect.w - 0.4,
|
|
2812
|
+
h: rect.h - 0.4,
|
|
2813
|
+
fontSize: 12,
|
|
2814
|
+
color: this.cleanHexColor(theme.colors.text),
|
|
2815
|
+
fontFace: cleanFontFace(theme.typography.codeFont),
|
|
2816
|
+
align: "center",
|
|
2817
|
+
valign: "middle"
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2361
2820
|
toInches(bounds, scaleX, scaleY) {
|
|
2362
2821
|
return {
|
|
2363
2822
|
x: Math.max(0, bounds.x * scaleX),
|
|
@@ -2461,6 +2920,10 @@ var HtmlRenderer = class {
|
|
|
2461
2920
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
2462
2921
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
2463
2922
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&family=Outfit:wght@600;700;800&display=swap" rel="stylesheet">
|
|
2923
|
+
<script type="module">
|
|
2924
|
+
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
|
2925
|
+
mermaid.initialize({ startOnLoad: true, theme: 'dark', securityLevel: 'loose' });
|
|
2926
|
+
</script>
|
|
2464
2927
|
<style>
|
|
2465
2928
|
:root {
|
|
2466
2929
|
--yumia-bg: ${theme.colors.background};
|
|
@@ -2843,6 +3306,172 @@ var HtmlRenderer = class {
|
|
|
2843
3306
|
transition: width 0.25s ease;
|
|
2844
3307
|
}
|
|
2845
3308
|
|
|
3309
|
+
/* Badge Directive */
|
|
3310
|
+
.yumia-badge {
|
|
3311
|
+
display: inline-flex;
|
|
3312
|
+
align-items: center;
|
|
3313
|
+
padding: 3px 10px;
|
|
3314
|
+
border-radius: 999px;
|
|
3315
|
+
font-size: 0.8rem;
|
|
3316
|
+
font-weight: 700;
|
|
3317
|
+
letter-spacing: 0.04em;
|
|
3318
|
+
text-transform: uppercase;
|
|
3319
|
+
background: rgba(255, 255, 255, 0.1);
|
|
3320
|
+
color: var(--yumia-text);
|
|
3321
|
+
border: 1px solid var(--yumia-border);
|
|
3322
|
+
margin-bottom: 0.5rem;
|
|
3323
|
+
}
|
|
3324
|
+
.yumia-badge.variant-primary { background: rgba(0, 240, 255, 0.15); color: var(--yumia-primary); border-color: var(--yumia-primary); }
|
|
3325
|
+
.yumia-badge.variant-success { background: rgba(16, 185, 129, 0.15); color: var(--yumia-success); border-color: var(--yumia-success); }
|
|
3326
|
+
.yumia-badge.variant-warning { background: rgba(245, 158, 11, 0.15); color: var(--yumia-warning); border-color: var(--yumia-warning); }
|
|
3327
|
+
.yumia-badge.variant-danger { background: rgba(239, 68, 68, 0.15); color: var(--yumia-danger); border-color: var(--yumia-danger); }
|
|
3328
|
+
.yumia-badge.variant-info { background: rgba(59, 130, 246, 0.15); color: var(--yumia-info); border-color: var(--yumia-info); }
|
|
3329
|
+
.yumia-badge.variant-accent { background: rgba(255, 46, 136, 0.15); color: var(--yumia-accent); border-color: var(--yumia-accent); }
|
|
3330
|
+
|
|
3331
|
+
/* Timeline Directive */
|
|
3332
|
+
.yumia-timeline {
|
|
3333
|
+
display: flex;
|
|
3334
|
+
gap: 16px;
|
|
3335
|
+
margin: 1.2rem 0;
|
|
3336
|
+
width: 100%;
|
|
3337
|
+
}
|
|
3338
|
+
.yumia-timeline.layout-horizontal {
|
|
3339
|
+
flex-direction: row;
|
|
3340
|
+
justify-content: space-between;
|
|
3341
|
+
position: relative;
|
|
3342
|
+
}
|
|
3343
|
+
.yumia-timeline.layout-horizontal::before {
|
|
3344
|
+
content: '';
|
|
3345
|
+
position: absolute;
|
|
3346
|
+
top: 18px;
|
|
3347
|
+
left: 20px;
|
|
3348
|
+
right: 20px;
|
|
3349
|
+
height: 2px;
|
|
3350
|
+
background: var(--yumia-border);
|
|
3351
|
+
z-index: 0;
|
|
3352
|
+
}
|
|
3353
|
+
.yumia-timeline-item {
|
|
3354
|
+
position: relative;
|
|
3355
|
+
z-index: 1;
|
|
3356
|
+
display: flex;
|
|
3357
|
+
flex-direction: column;
|
|
3358
|
+
align-items: center;
|
|
3359
|
+
text-align: center;
|
|
3360
|
+
flex: 1;
|
|
3361
|
+
}
|
|
3362
|
+
.yumia-timeline-dot {
|
|
3363
|
+
width: 14px;
|
|
3364
|
+
height: 14px;
|
|
3365
|
+
border-radius: 50%;
|
|
3366
|
+
background: var(--yumia-primary);
|
|
3367
|
+
border: 3px solid var(--yumia-bg);
|
|
3368
|
+
box-shadow: 0 0 10px var(--yumia-primary);
|
|
3369
|
+
margin-bottom: 10px;
|
|
3370
|
+
}
|
|
3371
|
+
.yumia-timeline-date {
|
|
3372
|
+
font-size: 0.85rem;
|
|
3373
|
+
font-weight: 700;
|
|
3374
|
+
color: var(--yumia-accent);
|
|
3375
|
+
margin-bottom: 4px;
|
|
3376
|
+
font-family: var(--yumia-font-code);
|
|
3377
|
+
}
|
|
3378
|
+
.yumia-timeline-title {
|
|
3379
|
+
font-size: 1.05rem;
|
|
3380
|
+
font-weight: 600;
|
|
3381
|
+
color: var(--yumia-text);
|
|
3382
|
+
margin-bottom: 4px;
|
|
3383
|
+
}
|
|
3384
|
+
.yumia-timeline-desc {
|
|
3385
|
+
font-size: 0.85rem;
|
|
3386
|
+
color: var(--yumia-muted);
|
|
3387
|
+
line-height: 1.4;
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
/* Compare Directive */
|
|
3391
|
+
.yumia-compare {
|
|
3392
|
+
display: grid;
|
|
3393
|
+
grid-template-columns: 1fr auto 1fr;
|
|
3394
|
+
gap: 20px;
|
|
3395
|
+
align-items: stretch;
|
|
3396
|
+
margin: 1.2rem 0;
|
|
3397
|
+
}
|
|
3398
|
+
.yumia-compare-col {
|
|
3399
|
+
background: var(--yumia-surface);
|
|
3400
|
+
border: 1px solid var(--yumia-border);
|
|
3401
|
+
border-radius: var(--yumia-radius-card);
|
|
3402
|
+
padding: 1.5rem;
|
|
3403
|
+
display: flex;
|
|
3404
|
+
flex-direction: column;
|
|
3405
|
+
}
|
|
3406
|
+
.yumia-compare-title {
|
|
3407
|
+
font-size: 1.2rem;
|
|
3408
|
+
font-weight: 700;
|
|
3409
|
+
color: var(--yumia-primary);
|
|
3410
|
+
margin-bottom: 1rem;
|
|
3411
|
+
padding-bottom: 0.5rem;
|
|
3412
|
+
border-bottom: 1px solid var(--yumia-divider);
|
|
3413
|
+
}
|
|
3414
|
+
.yumia-compare-divider {
|
|
3415
|
+
display: flex;
|
|
3416
|
+
align-items: center;
|
|
3417
|
+
justify-content: center;
|
|
3418
|
+
font-weight: 800;
|
|
3419
|
+
font-size: 0.9rem;
|
|
3420
|
+
color: var(--yumia-muted);
|
|
3421
|
+
background: rgba(255, 255, 255, 0.05);
|
|
3422
|
+
border-radius: 50%;
|
|
3423
|
+
width: 36px;
|
|
3424
|
+
height: 36px;
|
|
3425
|
+
align-self: center;
|
|
3426
|
+
border: 1px solid var(--yumia-border);
|
|
3427
|
+
}
|
|
3428
|
+
|
|
3429
|
+
/* Native SVG Chart Container */
|
|
3430
|
+
.yumia-chart-container {
|
|
3431
|
+
background: var(--yumia-surface);
|
|
3432
|
+
border: 1px solid var(--yumia-border);
|
|
3433
|
+
border-radius: var(--yumia-radius-card);
|
|
3434
|
+
padding: 1.25rem;
|
|
3435
|
+
margin: 1rem 0;
|
|
3436
|
+
display: flex;
|
|
3437
|
+
flex-direction: column;
|
|
3438
|
+
align-items: center;
|
|
3439
|
+
width: 100%;
|
|
3440
|
+
}
|
|
3441
|
+
.yumia-chart-title {
|
|
3442
|
+
font-size: 1.1rem;
|
|
3443
|
+
font-weight: 700;
|
|
3444
|
+
color: var(--yumia-text);
|
|
3445
|
+
margin-bottom: 0.75rem;
|
|
3446
|
+
align-self: flex-start;
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
/* Mermaid Container */
|
|
3450
|
+
.mermaid-container {
|
|
3451
|
+
background: rgba(10, 10, 18, 0.6);
|
|
3452
|
+
border: 1px solid var(--yumia-border);
|
|
3453
|
+
border-radius: var(--yumia-radius-card);
|
|
3454
|
+
padding: 1rem;
|
|
3455
|
+
margin: 1rem 0;
|
|
3456
|
+
display: flex;
|
|
3457
|
+
justify-content: center;
|
|
3458
|
+
overflow: auto;
|
|
3459
|
+
width: 100%;
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
/* Progressive Reveal (Step Builds) */
|
|
3463
|
+
.yumia-slide-wrapper [data-step] {
|
|
3464
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
3465
|
+
}
|
|
3466
|
+
.yumia-slide-wrapper:not(.step-active) [data-step] {
|
|
3467
|
+
opacity: 0.2;
|
|
3468
|
+
transform: translateY(4px);
|
|
3469
|
+
}
|
|
3470
|
+
.yumia-slide-wrapper.step-active [data-step] {
|
|
3471
|
+
opacity: 1;
|
|
3472
|
+
transform: translateY(0);
|
|
3473
|
+
}
|
|
3474
|
+
|
|
2846
3475
|
/* Notes Drawer */
|
|
2847
3476
|
.yumia-notes-drawer {
|
|
2848
3477
|
position: fixed;
|
|
@@ -3467,10 +4096,174 @@ var HtmlRenderer = class {
|
|
|
3467
4096
|
}).join("\n");
|
|
3468
4097
|
return `<div class="yumia-columns" style="grid-template-columns: ${ratioTemplate};">${colsHtml}</div>`;
|
|
3469
4098
|
}
|
|
4099
|
+
case "badge": {
|
|
4100
|
+
return this.renderBadge(element);
|
|
4101
|
+
}
|
|
4102
|
+
case "mermaid": {
|
|
4103
|
+
const m = element;
|
|
4104
|
+
return `
|
|
4105
|
+
<div class="mermaid-container">
|
|
4106
|
+
<pre class="mermaid">${this.escapeHtml(m.code)}</pre>
|
|
4107
|
+
</div>`;
|
|
4108
|
+
}
|
|
4109
|
+
case "chart": {
|
|
4110
|
+
return this.renderChart(element, theme);
|
|
4111
|
+
}
|
|
4112
|
+
case "timeline": {
|
|
4113
|
+
return this.renderTimeline(element);
|
|
4114
|
+
}
|
|
4115
|
+
case "compare": {
|
|
4116
|
+
return this.renderCompare(element, theme);
|
|
4117
|
+
}
|
|
3470
4118
|
default:
|
|
3471
4119
|
return "";
|
|
3472
4120
|
}
|
|
3473
4121
|
}
|
|
4122
|
+
renderBadge(b) {
|
|
4123
|
+
const variant = b.variant || "default";
|
|
4124
|
+
return `<span class="yumia-badge variant-${variant}">${this.escapeHtml(b.text)}</span>`;
|
|
4125
|
+
}
|
|
4126
|
+
renderTimeline(t) {
|
|
4127
|
+
const layout = t.layout || "horizontal";
|
|
4128
|
+
const itemsHtml = t.items.map((item) => {
|
|
4129
|
+
const dateHtml = item.date ? `<div class="yumia-timeline-date">${this.escapeHtml(item.date)}</div>` : "";
|
|
4130
|
+
const descHtml = item.description ? `<div class="yumia-timeline-desc">${this.formatInline(item.description)}</div>` : "";
|
|
4131
|
+
return `
|
|
4132
|
+
<div class="yumia-timeline-item">
|
|
4133
|
+
<div class="yumia-timeline-dot"></div>
|
|
4134
|
+
${dateHtml}
|
|
4135
|
+
<div class="yumia-timeline-title">${this.formatInline(item.title)}</div>
|
|
4136
|
+
${descHtml}
|
|
4137
|
+
</div>`;
|
|
4138
|
+
}).join("\n");
|
|
4139
|
+
return `<div class="yumia-timeline layout-${layout}">${itemsHtml}</div>`;
|
|
4140
|
+
}
|
|
4141
|
+
renderCompare(c, theme) {
|
|
4142
|
+
const leftTitle = c.leftTitle ? `<div class="yumia-compare-title">${this.escapeHtml(c.leftTitle)}</div>` : "";
|
|
4143
|
+
const rightTitle = c.rightTitle ? `<div class="yumia-compare-title">${this.escapeHtml(c.rightTitle)}</div>` : "";
|
|
4144
|
+
const leftInner = c.left.map((el) => this.renderElement(el, theme)).join("\n");
|
|
4145
|
+
const rightInner = c.right.map((el) => this.renderElement(el, theme)).join("\n");
|
|
4146
|
+
return `
|
|
4147
|
+
<div class="yumia-compare">
|
|
4148
|
+
<div class="yumia-compare-col left">
|
|
4149
|
+
${leftTitle}
|
|
4150
|
+
${leftInner}
|
|
4151
|
+
</div>
|
|
4152
|
+
<div class="yumia-compare-divider">VS</div>
|
|
4153
|
+
<div class="yumia-compare-col right">
|
|
4154
|
+
${rightTitle}
|
|
4155
|
+
${rightInner}
|
|
4156
|
+
</div>
|
|
4157
|
+
</div>`;
|
|
4158
|
+
}
|
|
4159
|
+
renderChart(c, theme) {
|
|
4160
|
+
const titleHtml = c.title ? `<div class="yumia-chart-title">${this.escapeHtml(c.title)}</div>` : "";
|
|
4161
|
+
const labels = c.labels || [];
|
|
4162
|
+
const series = c.series || [];
|
|
4163
|
+
const colors = [
|
|
4164
|
+
theme.colors.primary || "#00F0FF",
|
|
4165
|
+
theme.colors.accent || "#FF2E88",
|
|
4166
|
+
theme.colors.secondary || "#7B2CBF",
|
|
4167
|
+
theme.colors.success || "#10B981",
|
|
4168
|
+
theme.colors.warning || "#F59E0B"
|
|
4169
|
+
];
|
|
4170
|
+
if (c.chartType === "line") {
|
|
4171
|
+
const allValues = series.flatMap((s) => s.values);
|
|
4172
|
+
const maxVal2 = Math.max(...allValues, 1);
|
|
4173
|
+
const width2 = 500;
|
|
4174
|
+
const height2 = 180;
|
|
4175
|
+
const padding2 = 35;
|
|
4176
|
+
const plotW2 = width2 - padding2 * 2;
|
|
4177
|
+
const plotH2 = height2 - padding2 * 2;
|
|
4178
|
+
let pathsHtml = "";
|
|
4179
|
+
series.forEach((s, sIdx) => {
|
|
4180
|
+
const sColor = s.color || colors[sIdx % colors.length];
|
|
4181
|
+
const pts = s.values.map((v, i) => {
|
|
4182
|
+
const x = padding2 + i / Math.max(s.values.length - 1, 1) * plotW2;
|
|
4183
|
+
const y = height2 - padding2 - v / maxVal2 * plotH2;
|
|
4184
|
+
return `${x},${y}`;
|
|
4185
|
+
});
|
|
4186
|
+
const pointsStr = pts.join(" ");
|
|
4187
|
+
const circles = pts.map((pt) => `<circle cx="${pt.split(",")[0]}" cy="${pt.split(",")[1]}" r="4" fill="${sColor}" />`).join("");
|
|
4188
|
+
pathsHtml += `
|
|
4189
|
+
<polyline fill="none" stroke="${sColor}" stroke-width="3" stroke-linecap="round" points="${pointsStr}" />
|
|
4190
|
+
${circles}`;
|
|
4191
|
+
});
|
|
4192
|
+
const labelTexts = labels.map((l, i) => {
|
|
4193
|
+
const x = padding2 + i / Math.max(labels.length - 1, 1) * plotW2;
|
|
4194
|
+
return `<text x="${x}" y="${height2 - 10}" text-anchor="middle" fill="${theme.colors.muted || "#94a3b8"}" font-size="11" font-family="sans-serif">${this.escapeHtml(l)}</text>`;
|
|
4195
|
+
}).join("");
|
|
4196
|
+
return `
|
|
4197
|
+
<div class="yumia-chart-container">
|
|
4198
|
+
${titleHtml}
|
|
4199
|
+
<svg viewBox="0 0 ${width2} ${height2}" style="width:100%; max-height:220px;">
|
|
4200
|
+
<line x1="${padding2}" y1="${height2 - padding2}" x2="${width2 - padding2}" y2="${height2 - padding2}" stroke="rgba(255,255,255,0.15)" stroke-width="1" />
|
|
4201
|
+
${pathsHtml}
|
|
4202
|
+
${labelTexts}
|
|
4203
|
+
</svg>
|
|
4204
|
+
</div>`;
|
|
4205
|
+
}
|
|
4206
|
+
if (c.chartType === "pie" || c.chartType === "doughnut") {
|
|
4207
|
+
const values2 = series[0]?.values || [];
|
|
4208
|
+
const total = values2.reduce((a, b) => a + b, 0) || 1;
|
|
4209
|
+
let cumulativePercent = 0;
|
|
4210
|
+
const radius = 60;
|
|
4211
|
+
const cx = 100;
|
|
4212
|
+
const cy = 100;
|
|
4213
|
+
const strokeWidth = c.chartType === "doughnut" ? 24 : 60;
|
|
4214
|
+
const circ = 2 * Math.PI * radius;
|
|
4215
|
+
const slices = values2.map((val, i) => {
|
|
4216
|
+
const percent = val / total;
|
|
4217
|
+
const strokeDasharray = `${percent * circ} ${circ}`;
|
|
4218
|
+
const strokeDashoffset = -cumulativePercent * circ;
|
|
4219
|
+
cumulativePercent += percent;
|
|
4220
|
+
const sColor = colors[i % colors.length];
|
|
4221
|
+
return `<circle cx="${cx}" cy="${cy}" r="${radius}" fill="none" stroke="${sColor}" stroke-width="${strokeWidth}" stroke-dasharray="${strokeDasharray}" stroke-dashoffset="${strokeDashoffset}" />`;
|
|
4222
|
+
}).join("");
|
|
4223
|
+
const legend = labels.map((l, i) => {
|
|
4224
|
+
const sColor = colors[i % colors.length];
|
|
4225
|
+
const pct = Math.round((values2[i] || 0) / total * 100);
|
|
4226
|
+
return `<div style="display:flex; align-items:center; gap:8px; font-size:12px; margin:4px 0;"><span style="width:10px; height:10px; border-radius:50%; background:${sColor};"></span><span style="color:var(--yumia-text);">${this.escapeHtml(l)} (${pct}%)</span></div>`;
|
|
4227
|
+
}).join("");
|
|
4228
|
+
return `
|
|
4229
|
+
<div class="yumia-chart-container">
|
|
4230
|
+
${titleHtml}
|
|
4231
|
+
<div style="display:flex; align-items:center; justify-content:center; gap:28px; width:100%;">
|
|
4232
|
+
<svg viewBox="0 0 200 200" style="width:160px; height:160px; transform: rotate(-90deg);">
|
|
4233
|
+
${slices}
|
|
4234
|
+
</svg>
|
|
4235
|
+
<div style="display:flex; flex-direction:column;">${legend}</div>
|
|
4236
|
+
</div>
|
|
4237
|
+
</div>`;
|
|
4238
|
+
}
|
|
4239
|
+
const values = series[0]?.values || [];
|
|
4240
|
+
const maxVal = Math.max(...values, 1);
|
|
4241
|
+
const width = 500;
|
|
4242
|
+
const height = 180;
|
|
4243
|
+
const padding = 35;
|
|
4244
|
+
const plotW = width - padding * 2;
|
|
4245
|
+
const plotH = height - padding * 2;
|
|
4246
|
+
const barWidth = Math.min(48, Math.max(16, plotW / Math.max(values.length, 1) * 0.6));
|
|
4247
|
+
const bars = values.map((val, i) => {
|
|
4248
|
+
const x = padding + (i + 0.5) * (plotW / Math.max(values.length, 1)) - barWidth / 2;
|
|
4249
|
+
const barH = val / maxVal * plotH;
|
|
4250
|
+
const y = height - padding - barH;
|
|
4251
|
+
const color = colors[i % colors.length];
|
|
4252
|
+
const label = labels[i] || "";
|
|
4253
|
+
return `
|
|
4254
|
+
<rect x="${x}" y="${y}" width="${barWidth}" height="${barH}" rx="4" fill="${color}" opacity="0.9" />
|
|
4255
|
+
<text x="${x + barWidth / 2}" y="${y - 6}" text-anchor="middle" fill="${color}" font-size="11" font-weight="600" font-family="sans-serif">${val}</text>
|
|
4256
|
+
<text x="${x + barWidth / 2}" y="${height - 12}" text-anchor="middle" fill="${theme.colors.muted || "#94a3b8"}" font-size="11" font-family="sans-serif">${this.escapeHtml(label)}</text>`;
|
|
4257
|
+
}).join("");
|
|
4258
|
+
return `
|
|
4259
|
+
<div class="yumia-chart-container">
|
|
4260
|
+
${titleHtml}
|
|
4261
|
+
<svg viewBox="0 0 ${width} ${height}" style="width:100%; max-height:220px;">
|
|
4262
|
+
<line x1="${padding}" y1="${height - padding}" x2="${width - padding}" y2="${height - padding}" stroke="rgba(255,255,255,0.15)" stroke-width="1" />
|
|
4263
|
+
${bars}
|
|
4264
|
+
</svg>
|
|
4265
|
+
</div>`;
|
|
4266
|
+
}
|
|
3474
4267
|
formatInline(text) {
|
|
3475
4268
|
const escaped = this.escapeHtml(text);
|
|
3476
4269
|
return escaped.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\*(.*?)\*/g, "<em>$1</em>").replace(/`(.*?)`/g, "<code>$1</code>");
|
|
@@ -3718,6 +4511,117 @@ var PdfRenderer = class {
|
|
|
3718
4511
|
doc.rect(x, y, width, curY - y).strokeColor(theme.colors.border || "rgba(255,255,255,0.1)").stroke();
|
|
3719
4512
|
return curY;
|
|
3720
4513
|
}
|
|
4514
|
+
case "badge": {
|
|
4515
|
+
const b = element;
|
|
4516
|
+
const variantColor = this.getVariantColor(b.variant, theme);
|
|
4517
|
+
const text = this.stripFormatting(b.text).toUpperCase();
|
|
4518
|
+
const badgeW = Math.min(180, Math.max(60, text.length * 7 + 20));
|
|
4519
|
+
const badgeH = 22;
|
|
4520
|
+
doc.roundedRect(x, y, badgeW, badgeH, 11).fill(theme.colors.surface || "rgba(255,255,255,0.08)");
|
|
4521
|
+
doc.roundedRect(x, y, badgeW, badgeH, 11).lineWidth(1).strokeColor(variantColor).stroke();
|
|
4522
|
+
doc.font("Helvetica-Bold").fontSize(10).fillColor(variantColor).text(text, x, y + 6, { width: badgeW, align: "center" });
|
|
4523
|
+
return y + badgeH + 6;
|
|
4524
|
+
}
|
|
4525
|
+
case "timeline": {
|
|
4526
|
+
const t = element;
|
|
4527
|
+
const items = t.items || [];
|
|
4528
|
+
if (items.length === 0)
|
|
4529
|
+
return y;
|
|
4530
|
+
const itemW = (width - (items.length - 1) * 12) / items.length;
|
|
4531
|
+
const lineY = y + 14;
|
|
4532
|
+
doc.moveTo(x + 10, lineY).lineTo(x + width - 10, lineY).lineWidth(2).strokeColor(theme.colors.border || "rgba(255,255,255,0.2)").stroke();
|
|
4533
|
+
let maxItemH = 60;
|
|
4534
|
+
items.forEach((item, idx) => {
|
|
4535
|
+
const itemX = x + idx * (itemW + 12);
|
|
4536
|
+
const dotX = itemX + itemW / 2;
|
|
4537
|
+
doc.circle(dotX, lineY, 6).fill(theme.colors.primary);
|
|
4538
|
+
let curItemY = lineY + 12;
|
|
4539
|
+
if (item.date) {
|
|
4540
|
+
doc.font("Helvetica-Bold").fontSize(10).fillColor(theme.colors.accent || theme.colors.primary).text(this.stripFormatting(item.date), itemX, curItemY, {
|
|
4541
|
+
width: itemW,
|
|
4542
|
+
align: "center"
|
|
4543
|
+
});
|
|
4544
|
+
curItemY += 14;
|
|
4545
|
+
}
|
|
4546
|
+
doc.font("Helvetica-Bold").fontSize(12).fillColor(theme.colors.text).text(this.stripFormatting(item.title), itemX, curItemY, {
|
|
4547
|
+
width: itemW,
|
|
4548
|
+
align: "center"
|
|
4549
|
+
});
|
|
4550
|
+
curItemY += 16;
|
|
4551
|
+
if (item.description) {
|
|
4552
|
+
doc.font("Helvetica").fontSize(9).fillColor(theme.colors.muted || "#888888").text(this.stripFormatting(item.description), itemX, curItemY, {
|
|
4553
|
+
width: itemW,
|
|
4554
|
+
align: "center"
|
|
4555
|
+
});
|
|
4556
|
+
curItemY += 24;
|
|
4557
|
+
}
|
|
4558
|
+
if (curItemY - y > maxItemH)
|
|
4559
|
+
maxItemH = curItemY - y;
|
|
4560
|
+
});
|
|
4561
|
+
return y + maxItemH + 8;
|
|
4562
|
+
}
|
|
4563
|
+
case "compare": {
|
|
4564
|
+
const c = element;
|
|
4565
|
+
const colW = (width - 24) / 2;
|
|
4566
|
+
let leftY = y + 12;
|
|
4567
|
+
let rightY = y + 12;
|
|
4568
|
+
if (c.leftTitle) {
|
|
4569
|
+
doc.font("Helvetica-Bold").fontSize(14).fillColor(theme.colors.primary).text(this.stripFormatting(c.leftTitle), x + 12, leftY, { width: colW - 24 });
|
|
4570
|
+
leftY += 22;
|
|
4571
|
+
}
|
|
4572
|
+
for (const el of c.left) {
|
|
4573
|
+
leftY = this.renderElement(doc, el, x + 12, leftY, colW - 24, theme) + 6;
|
|
4574
|
+
}
|
|
4575
|
+
const rightX = x + colW + 24;
|
|
4576
|
+
if (c.rightTitle) {
|
|
4577
|
+
doc.font("Helvetica-Bold").fontSize(14).fillColor(theme.colors.primary).text(this.stripFormatting(c.rightTitle), rightX + 12, rightY, { width: colW - 24 });
|
|
4578
|
+
rightY += 22;
|
|
4579
|
+
}
|
|
4580
|
+
for (const el of c.right) {
|
|
4581
|
+
rightY = this.renderElement(doc, el, rightX + 12, rightY, colW - 24, theme) + 6;
|
|
4582
|
+
}
|
|
4583
|
+
const totalH = Math.max(leftY - y, rightY - y, 60) + 12;
|
|
4584
|
+
doc.roundedRect(x, y, colW, totalH, 8).strokeColor(theme.colors.border || "rgba(255,255,255,0.15)").stroke();
|
|
4585
|
+
doc.roundedRect(rightX, y, colW, totalH, 8).strokeColor(theme.colors.border || "rgba(255,255,255,0.15)").stroke();
|
|
4586
|
+
return y + totalH + 8;
|
|
4587
|
+
}
|
|
4588
|
+
case "chart": {
|
|
4589
|
+
const ch = element;
|
|
4590
|
+
const boxH = 130;
|
|
4591
|
+
doc.roundedRect(x, y, width, boxH, 8).fill(theme.colors.surface || "rgba(255,255,255,0.04)");
|
|
4592
|
+
doc.roundedRect(x, y, width, boxH, 8).strokeColor(theme.colors.border || "rgba(255,255,255,0.1)").stroke();
|
|
4593
|
+
let topY = y + 10;
|
|
4594
|
+
if (ch.title) {
|
|
4595
|
+
doc.font("Helvetica-Bold").fontSize(13).fillColor(theme.colors.text).text(this.stripFormatting(ch.title), x + 14, topY, { width: width - 28 });
|
|
4596
|
+
topY += 20;
|
|
4597
|
+
}
|
|
4598
|
+
const values = ch.series[0]?.values || [];
|
|
4599
|
+
const maxVal = Math.max(...values, 1);
|
|
4600
|
+
const plotH = boxH - (topY - y) - 28;
|
|
4601
|
+
const barW = Math.min(40, (width - 40) / Math.max(values.length, 1) * 0.6);
|
|
4602
|
+
values.forEach((val, idx) => {
|
|
4603
|
+
const barX = x + 24 + idx * ((width - 48) / Math.max(values.length, 1));
|
|
4604
|
+
const h = val / maxVal * plotH;
|
|
4605
|
+
const barY = topY + plotH - h;
|
|
4606
|
+
doc.rect(barX, barY, barW, h).fill(theme.colors.primary);
|
|
4607
|
+
doc.font("Helvetica").fontSize(9).fillColor(theme.colors.text).text(String(val), barX, barY - 12, { width: barW, align: "center" });
|
|
4608
|
+
if (ch.labels[idx]) {
|
|
4609
|
+
doc.font("Helvetica").fontSize(9).fillColor(theme.colors.muted || "#888888").text(this.stripFormatting(ch.labels[idx]), barX - 10, topY + plotH + 4, {
|
|
4610
|
+
width: barW + 20,
|
|
4611
|
+
align: "center"
|
|
4612
|
+
});
|
|
4613
|
+
}
|
|
4614
|
+
});
|
|
4615
|
+
return y + boxH + 8;
|
|
4616
|
+
}
|
|
4617
|
+
case "mermaid": {
|
|
4618
|
+
const m = element;
|
|
4619
|
+
const boxH = 90;
|
|
4620
|
+
doc.roundedRect(x, y, width, boxH, 8).fill(theme.colors.surface || "rgba(255,255,255,0.04)");
|
|
4621
|
+
doc.roundedRect(x, y, width, boxH, 8).strokeColor(theme.colors.primary).stroke();
|
|
4622
|
+
doc.font("Courier").fontSize(11).fillColor(theme.colors.text).text(this.stripFormatting(m.code), x + 12, y + 12, { width: width - 24 });
|
|
4623
|
+
return y + boxH + 8;
|
|
4624
|
+
}
|
|
3721
4625
|
default:
|
|
3722
4626
|
return y;
|
|
3723
4627
|
}
|
|
@@ -3849,7 +4753,7 @@ function startDevServer(filePath, options = {}) {
|
|
|
3849
4753
|
// src/cli.ts
|
|
3850
4754
|
import { mkdirSync, readFileSync as readFileSync2, watch as fsWatch, writeFileSync } from "fs";
|
|
3851
4755
|
import { basename, dirname, extname, join, resolve as resolve2 } from "path";
|
|
3852
|
-
var VERSION = "0.1.
|
|
4756
|
+
var VERSION = "0.1.16";
|
|
3853
4757
|
function printHelp() {
|
|
3854
4758
|
return `
|
|
3855
4759
|
YumiaMD \u2014 Markdown-based presentation compiler (v${VERSION})
|
|
@@ -3866,6 +4770,7 @@ Commands:
|
|
|
3866
4770
|
inspect <file> Inspect the AST and geometric layout tree
|
|
3867
4771
|
schema Output machine-readable JSON schema for AI agents
|
|
3868
4772
|
build <file> Compile a presentation to PowerPoint (.pptx), PDF (.pdf), or HTML (.html)
|
|
4773
|
+
deploy <file> Export and deploy presentation to static site, GitHub Pages, or Vercel
|
|
3869
4774
|
|
|
3870
4775
|
Theming & Color Options:
|
|
3871
4776
|
--theme, -t <name> Base theme: default | cyberpunk | minimal | corporate | terminal | academic
|
|
@@ -4196,6 +5101,68 @@ ${summary}${isStrict && report.warnings.length > 0 ? " (failed due to --strict)"
|
|
|
4196
5101
|
};
|
|
4197
5102
|
}
|
|
4198
5103
|
}
|
|
5104
|
+
if (command === "deploy") {
|
|
5105
|
+
if (!target) {
|
|
5106
|
+
const msg = "Error: Please specify a presentation file to deploy (e.g. 'yumia deploy presentation.yumia.md')";
|
|
5107
|
+
return { exitCode: 1, output: isJson ? JSON.stringify({ error: msg }) : msg };
|
|
5108
|
+
}
|
|
5109
|
+
try {
|
|
5110
|
+
const resolvedInput = resolve2(process.cwd(), target);
|
|
5111
|
+
const source = readFileSync2(resolvedInput, "utf-8");
|
|
5112
|
+
const provider = (getFlagValue(args, ["--provider"]) || "static").toLowerCase();
|
|
5113
|
+
const outDir = resolve2(process.cwd(), getFlagValue(args, ["--out", "-o"]) || "dist-site");
|
|
5114
|
+
mkdirSync(outDir, { recursive: true });
|
|
5115
|
+
const compiler = new YumiaCompiler();
|
|
5116
|
+
const htmlRenderer = new HtmlRenderer();
|
|
5117
|
+
const result = await compiler.compile(source, htmlRenderer);
|
|
5118
|
+
const indexPath = join(outDir, "index.html");
|
|
5119
|
+
writeFileSync(indexPath, result.html, "utf-8");
|
|
5120
|
+
if (provider === "gh-pages" || provider === "github") {
|
|
5121
|
+
writeFileSync(join(outDir, ".nojekyll"), "", "utf-8");
|
|
5122
|
+
} else if (provider === "vercel") {
|
|
5123
|
+
const vercelConfig = {
|
|
5124
|
+
version: 2,
|
|
5125
|
+
cleanUrls: true,
|
|
5126
|
+
routes: [{ src: "/(.*)", dest: "/index.html" }]
|
|
5127
|
+
};
|
|
5128
|
+
writeFileSync(join(outDir, "vercel.json"), JSON.stringify(vercelConfig, null, 2), "utf-8");
|
|
5129
|
+
}
|
|
5130
|
+
if (isJson) {
|
|
5131
|
+
return {
|
|
5132
|
+
exitCode: 0,
|
|
5133
|
+
output: JSON.stringify(
|
|
5134
|
+
{
|
|
5135
|
+
success: true,
|
|
5136
|
+
provider,
|
|
5137
|
+
outputDirectory: outDir,
|
|
5138
|
+
indexPath,
|
|
5139
|
+
slideCount: result.slideCount
|
|
5140
|
+
},
|
|
5141
|
+
null,
|
|
5142
|
+
2
|
|
5143
|
+
)
|
|
5144
|
+
};
|
|
5145
|
+
}
|
|
5146
|
+
return {
|
|
5147
|
+
exitCode: 0,
|
|
5148
|
+
output: [
|
|
5149
|
+
`\u{1F680} YumiaMD Presentation Deployment Ready!`,
|
|
5150
|
+
`\u2713 Standalone interactive deck compiled (${result.slideCount} slides)`,
|
|
5151
|
+
`\u2713 Target Directory: ${outDir}`,
|
|
5152
|
+
`\u2713 Provider Profile: ${provider}`,
|
|
5153
|
+
`\u2713 Entrypoint: ${indexPath}`,
|
|
5154
|
+
``,
|
|
5155
|
+
provider === "gh-pages" ? `To publish to GitHub Pages: push '${outDir}' contents to 'gh-pages' branch.` : `To deploy to Vercel/Netlify/S3: run 'vercel ${outDir}' or host files from '${outDir}'.`
|
|
5156
|
+
].join("\n")
|
|
5157
|
+
};
|
|
5158
|
+
} catch (err) {
|
|
5159
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5160
|
+
if (isJson) {
|
|
5161
|
+
return { exitCode: 1, output: JSON.stringify({ success: false, error: msg }) };
|
|
5162
|
+
}
|
|
5163
|
+
return { exitCode: 1, output: `\u2717 Deployment build failed: ${msg}` };
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
4199
5166
|
if (command === "build" || command === "watch") {
|
|
4200
5167
|
if (!target) {
|
|
4201
5168
|
const msg = `Error: Please specify a presentation file to ${command}.`;
|
|
@@ -4314,6 +5281,11 @@ export {
|
|
|
4314
5281
|
createCode,
|
|
4315
5282
|
createQuote,
|
|
4316
5283
|
createTable,
|
|
5284
|
+
createChart,
|
|
5285
|
+
createMermaid,
|
|
5286
|
+
createTimeline,
|
|
5287
|
+
createCompare,
|
|
5288
|
+
createBadge,
|
|
4317
5289
|
createGroup,
|
|
4318
5290
|
createColumn,
|
|
4319
5291
|
createColumns,
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export * from '@yumiamd/renderer-pptx';
|
|
|
9
9
|
export * from '@yumiamd/renderer-html';
|
|
10
10
|
export * from '@yumiamd/renderer-pdf';
|
|
11
11
|
|
|
12
|
-
declare const VERSION = "0.1.
|
|
12
|
+
declare const VERSION = "0.1.16";
|
|
13
13
|
declare function printHelp(): string;
|
|
14
14
|
declare function runCli(argv: string[]): Promise<{
|
|
15
15
|
exitCode: number;
|
package/dist/index.js
CHANGED
|
@@ -13,15 +13,19 @@ import {
|
|
|
13
13
|
academicTheme,
|
|
14
14
|
cleanFontFace,
|
|
15
15
|
corporateTheme,
|
|
16
|
+
createBadge,
|
|
16
17
|
createCard,
|
|
18
|
+
createChart,
|
|
17
19
|
createCode,
|
|
18
20
|
createColumn,
|
|
19
21
|
createColumns,
|
|
22
|
+
createCompare,
|
|
20
23
|
createGroup,
|
|
21
24
|
createHeading,
|
|
22
25
|
createImage,
|
|
23
26
|
createLayoutDirective,
|
|
24
27
|
createList,
|
|
28
|
+
createMermaid,
|
|
25
29
|
createMetric,
|
|
26
30
|
createParagraph,
|
|
27
31
|
createPresentation,
|
|
@@ -29,6 +33,7 @@ import {
|
|
|
29
33
|
createSlide,
|
|
30
34
|
createTable,
|
|
31
35
|
createTheme,
|
|
36
|
+
createTimeline,
|
|
32
37
|
cyberpunkTheme,
|
|
33
38
|
defaultTheme,
|
|
34
39
|
minimalTheme,
|
|
@@ -39,7 +44,7 @@ import {
|
|
|
39
44
|
runCli,
|
|
40
45
|
startDevServer,
|
|
41
46
|
terminalTheme
|
|
42
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-UPUEFJLX.js";
|
|
43
48
|
export {
|
|
44
49
|
DEFAULT_VIEWPORT,
|
|
45
50
|
DefaultLayoutEngine,
|
|
@@ -55,15 +60,19 @@ export {
|
|
|
55
60
|
academicTheme,
|
|
56
61
|
cleanFontFace,
|
|
57
62
|
corporateTheme,
|
|
63
|
+
createBadge,
|
|
58
64
|
createCard,
|
|
65
|
+
createChart,
|
|
59
66
|
createCode,
|
|
60
67
|
createColumn,
|
|
61
68
|
createColumns,
|
|
69
|
+
createCompare,
|
|
62
70
|
createGroup,
|
|
63
71
|
createHeading,
|
|
64
72
|
createImage,
|
|
65
73
|
createLayoutDirective,
|
|
66
74
|
createList,
|
|
75
|
+
createMermaid,
|
|
67
76
|
createMetric,
|
|
68
77
|
createParagraph,
|
|
69
78
|
createPresentation,
|
|
@@ -71,6 +80,7 @@ export {
|
|
|
71
80
|
createSlide,
|
|
72
81
|
createTable,
|
|
73
82
|
createTheme,
|
|
83
|
+
createTimeline,
|
|
74
84
|
cyberpunkTheme,
|
|
75
85
|
defaultTheme,
|
|
76
86
|
minimalTheme,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yumiamd",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Command line interface and compiler for YumiaMD presentations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"tsup": "^8.5.1",
|
|
24
|
-
"@yumiamd/
|
|
25
|
-
"@yumiamd/
|
|
26
|
-
"@yumiamd/
|
|
27
|
-
"@yumiamd/
|
|
28
|
-
"@yumiamd/
|
|
29
|
-
"@yumiamd/
|
|
30
|
-
"@yumiamd/renderer-pptx": "0.1.
|
|
31
|
-
"@yumiamd/theme": "0.1.
|
|
32
|
-
"@yumiamd/renderer-pdf": "0.1.
|
|
24
|
+
"@yumiamd/core": "0.1.16",
|
|
25
|
+
"@yumiamd/renderer": "0.1.16",
|
|
26
|
+
"@yumiamd/ast": "0.1.16",
|
|
27
|
+
"@yumiamd/layout": "0.1.16",
|
|
28
|
+
"@yumiamd/parser": "0.1.16",
|
|
29
|
+
"@yumiamd/renderer-html": "0.1.16",
|
|
30
|
+
"@yumiamd/renderer-pptx": "0.1.16",
|
|
31
|
+
"@yumiamd/theme": "0.1.16",
|
|
32
|
+
"@yumiamd/renderer-pdf": "0.1.16"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"yumia",
|