yumiamd 0.1.25 → 0.1.27

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-PRZ5CFRH.js";
4
+ } from "./chunk-FTPWLNJL.js";
5
5
 
6
6
  // src/bin.ts
7
7
  var result = await runCli(process.argv);
@@ -1560,9 +1560,21 @@ var NativeYumiaParser = class {
1560
1560
  case "h1":
1561
1561
  case "h2":
1562
1562
  case "h3": {
1563
- const level = tok.command === "h1" ? 1 : tok.command === "h2" ? 2 : tok.command === "h3" ? 3 : 1;
1564
- const text = this.stripQuotes(tok.args);
1565
- const el = createHeading(text, level);
1563
+ const lvlMatch = tok.args.match(/^(?:level=)?([1-4])\b/);
1564
+ let level = 2;
1565
+ let text = tok.args;
1566
+ if (tok.command === "h1") {
1567
+ level = 1;
1568
+ } else if (tok.command === "h2") {
1569
+ level = 2;
1570
+ } else if (tok.command === "h3") {
1571
+ level = 3;
1572
+ } else if (lvlMatch && lvlMatch[1]) {
1573
+ level = parseInt(lvlMatch[1], 10);
1574
+ text = tok.args.replace(/^(?:level=)?[1-4]\s*/, "");
1575
+ }
1576
+ text = this.stripQuotes(text);
1577
+ const el = createHeading(text, Math.min(6, Math.max(1, level)));
1566
1578
  el.loc = {
1567
1579
  start: { line: tok.lineNum, column: 1 },
1568
1580
  end: { line: tok.lineNum, column: tok.text.length }
@@ -1612,6 +1624,20 @@ var NativeYumiaParser = class {
1612
1624
  }
1613
1625
  return { element: createCard(children, title, variant), nextIdx };
1614
1626
  }
1627
+ case "column": {
1628
+ const children = [];
1629
+ let nextIdx = idx + 1;
1630
+ while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1631
+ const childRes = this.parseElement(tokens, nextIdx, components);
1632
+ if (childRes) {
1633
+ children.push(childRes.element);
1634
+ nextIdx = childRes.nextIdx;
1635
+ } else {
1636
+ nextIdx++;
1637
+ }
1638
+ }
1639
+ return { element: createColumn(children), nextIdx };
1640
+ }
1615
1641
  case "columns": {
1616
1642
  const ratios = tok.args || "50:50";
1617
1643
  const cols = [];
@@ -1791,35 +1817,64 @@ var NativeYumiaParser = class {
1791
1817
  const nodes = [];
1792
1818
  const edges = [];
1793
1819
  const nodeMap = /* @__PURE__ */ new Map();
1794
- const ensureNode = (rawLabel, shape, variant) => {
1795
- const clean = rawLabel.replace(/^[[({]+|[\])}]+$/g, "").trim();
1796
- const id = clean.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
1797
- if (!nodeMap.has(id)) {
1798
- let detectedShape = "round";
1799
- if (rawLabel.startsWith("[(") || shape === "database")
1820
+ const ensureNode = (rawInput, shape, variant, explicitLabel) => {
1821
+ let id = "";
1822
+ let label = "";
1823
+ let detectedShape = shape || "round";
1824
+ const inlineMatch = rawInput.match(/^([a-zA-Z0-9_-]+)(\[\([^)]+\)\]|\(\([^)]+\)\)|\{[^}]+\}|\[[^\]]+\])$/);
1825
+ if (inlineMatch) {
1826
+ id = inlineMatch[1].toLowerCase();
1827
+ const body = inlineMatch[2];
1828
+ if (body.startsWith("[("))
1829
+ detectedShape = "database";
1830
+ else if (body.startsWith("(("))
1831
+ detectedShape = "circle";
1832
+ else if (body.startsWith("{"))
1833
+ detectedShape = "diamond";
1834
+ label = body.replace(/^[[({]+|[\])}]+$/g, "").trim();
1835
+ } else {
1836
+ const clean = rawInput.replace(/^[[({]+|[\])}]+$/g, "").trim();
1837
+ if (rawInput.startsWith("[(") || shape === "database")
1800
1838
  detectedShape = "database";
1801
- else if (rawLabel.startsWith("((") || shape === "circle")
1839
+ else if (rawInput.startsWith("((") || shape === "circle")
1802
1840
  detectedShape = "circle";
1803
- else if (rawLabel.startsWith("{") || shape === "diamond")
1841
+ else if (rawInput.startsWith("{") || shape === "diamond")
1804
1842
  detectedShape = "diamond";
1805
- else if (shape)
1806
- detectedShape = shape;
1807
- const node = {
1843
+ label = explicitLabel || clean;
1844
+ id = clean.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
1845
+ }
1846
+ let existing = nodeMap.get(id) || nodeMap.get(label.toLowerCase()) || nodeMap.get(label.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase());
1847
+ if (!existing && explicitLabel) {
1848
+ const labelSlug = explicitLabel.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase();
1849
+ existing = nodeMap.get(labelSlug) || nodeMap.get(explicitLabel.toLowerCase());
1850
+ }
1851
+ if (!existing) {
1852
+ existing = {
1808
1853
  id,
1809
- label: clean,
1854
+ label,
1810
1855
  shape: detectedShape,
1811
1856
  variant: variant || (nodes.length === 0 ? "primary" : "accent")
1812
1857
  };
1813
- nodeMap.set(id, node);
1814
- nodes.push(node);
1858
+ nodes.push(existing);
1859
+ } else {
1860
+ if (shape)
1861
+ existing.shape = detectedShape;
1862
+ if (variant)
1863
+ existing.variant = variant;
1864
+ if (explicitLabel)
1865
+ existing.label = explicitLabel;
1815
1866
  }
1816
- return nodeMap.get(id);
1867
+ nodeMap.set(id, existing);
1868
+ nodeMap.set(existing.id, existing);
1869
+ nodeMap.set(existing.label.toLowerCase(), existing);
1870
+ nodeMap.set(existing.label.replace(/[^a-zA-Z0-9_-]/g, "_").toLowerCase(), existing);
1871
+ return existing;
1817
1872
  };
1818
1873
  let nextIdx = idx + 1;
1819
1874
  while (nextIdx < tokens.length && tokens[nextIdx].indent > baseIndent) {
1820
1875
  const dLine = tokens[nextIdx].text;
1821
1876
  if (dLine.includes("->") || dLine.includes("-->") || dLine.includes("-[")) {
1822
- const segRegex = /(\[[^\]]+\]|\(\([^)]+\)\)|\[\([^)]+\)\]|\S+)(?:\s*(?:-\[([^\]]+)\]->|-->|->)\s*)?/g;
1877
+ const segRegex = /([a-zA-Z0-9_-]+\[\([^)]+\)\]|[a-zA-Z0-9_-]+\(\([^)]+\)\)|[a-zA-Z0-9_-]+\{[^}]+\}|[a-zA-Z0-9_-]+\[[^\]]+\]|\[\([^)]+\)\]|\(\([^)]+\)\)|\{[^}]+\}|\[[^\]]+\]|\S+)(?:\s*(?:-\[([^\]]+)\]->|-->|->)\s*)?/g;
1823
1878
  let m;
1824
1879
  let prevNode = null;
1825
1880
  let pendingEdgeLabel = void 0;
@@ -1849,10 +1904,9 @@ var NativeYumiaParser = class {
1849
1904
  const nVarMatch = nParts.match(/\bvariant=["']?([^"'\s]+)["']?/);
1850
1905
  if (nIdMatch) {
1851
1906
  const nId = nIdMatch[1];
1852
- const node = ensureNode(nLabelMatch ? nLabelMatch[1] : nId, nShapeMatch ? nShapeMatch[1] : void 0, nVarMatch ? nVarMatch[1] : void 0);
1853
- node.id = nId.toLowerCase();
1854
- if (nLabelMatch)
1855
- node.label = nLabelMatch[1];
1907
+ const explicitLabel = nLabelMatch ? nLabelMatch[1] : void 0;
1908
+ const node = ensureNode(nId, nShapeMatch ? nShapeMatch[1] : void 0, nVarMatch ? nVarMatch[1] : void 0, explicitLabel);
1909
+ nodeMap.set(nId.toLowerCase(), node);
1856
1910
  }
1857
1911
  }
1858
1912
  nextIdx++;
@@ -2165,7 +2219,7 @@ var VIEWPORT_PRESETS = {
2165
2219
  var DefaultLayoutEngine = class {
2166
2220
  computeSlide(slide, viewport = DEFAULT_VIEWPORT, options = {}) {
2167
2221
  const padding = options.padding ?? 64;
2168
- const gap = options.gap ?? 32;
2222
+ const gap = options.gap ?? 28;
2169
2223
  const availableWidth = Math.max(100, viewport.width - padding * 2);
2170
2224
  const availableHeight = Math.max(100, viewport.height - padding * 2);
2171
2225
  if (slide.elements.length === 0) {
@@ -2208,6 +2262,9 @@ var DefaultLayoutEngine = class {
2208
2262
  }
2209
2263
  layoutSingleElement(element, x, y, width, gap) {
2210
2264
  switch (element.type) {
2265
+ case "hero": {
2266
+ return this.layoutHero(element, x, y, width, gap);
2267
+ }
2211
2268
  case "heading": {
2212
2269
  const height = this.estimateHeadingHeight(element, width);
2213
2270
  return { element, bounds: { x, y, width, height } };
@@ -2240,16 +2297,37 @@ var DefaultLayoutEngine = class {
2240
2297
  const height = this.estimateMetricHeight(element);
2241
2298
  return { element, bounds: { x, y, width, height } };
2242
2299
  }
2300
+ case "callout": {
2301
+ const c = element;
2302
+ const lines = Math.ceil(c.text.length / Math.max(15, Math.floor(width / 14))) || 1;
2303
+ const height = Math.max(80, lines * 28 + (c.title ? 45 : 0) + 30);
2304
+ return { element, bounds: { x, y, width, height } };
2305
+ }
2306
+ case "badge": {
2307
+ return { element, bounds: { x, y, width, height: 45 } };
2308
+ }
2243
2309
  case "math": {
2244
2310
  const height = 90;
2245
2311
  return { element, bounds: { x, y, width, height } };
2246
2312
  }
2313
+ case "chart": {
2314
+ const height = 280;
2315
+ return { element, bounds: { x, y, width, height } };
2316
+ }
2317
+ case "diagram": {
2318
+ const height = 320;
2319
+ return { element, bounds: { x, y, width, height } };
2320
+ }
2321
+ case "timeline": {
2322
+ const height = 160;
2323
+ return { element, bounds: { x, y, width, height } };
2324
+ }
2247
2325
  case "section": {
2248
2326
  const height = 280;
2249
2327
  return { element, bounds: { x, y, width, height } };
2250
2328
  }
2251
2329
  case "toc": {
2252
- const height = 320;
2330
+ const height = 340;
2253
2331
  return { element, bounds: { x, y, width, height } };
2254
2332
  }
2255
2333
  case "card": {
@@ -2258,12 +2336,94 @@ var DefaultLayoutEngine = class {
2258
2336
  case "columns": {
2259
2337
  return this.layoutColumns(element, x, y, width, gap);
2260
2338
  }
2339
+ case "grid": {
2340
+ return this.layoutGrid(element, x, y, width, gap);
2341
+ }
2342
+ case "compare": {
2343
+ return this.layoutCompare(element, x, y, width, gap);
2344
+ }
2345
+ case "stack": {
2346
+ return this.layoutStack(element, x, y, width, gap);
2347
+ }
2261
2348
  default: {
2262
2349
  const height = 60;
2263
2350
  return { element, bounds: { x, y, width, height } };
2264
2351
  }
2265
2352
  }
2266
2353
  }
2354
+ layoutHero(element, x, y, width, gap) {
2355
+ let curY = y;
2356
+ if (element.tagline)
2357
+ curY += 35;
2358
+ curY += 85;
2359
+ if (element.subtitle)
2360
+ curY += 65;
2361
+ const children = [];
2362
+ if (element.elements) {
2363
+ for (const child of element.elements) {
2364
+ const node = this.layoutSingleElement(child, x, curY, width, gap);
2365
+ children.push(node);
2366
+ curY += node.bounds.height + gap;
2367
+ }
2368
+ }
2369
+ return { element, bounds: { x, y, width, height: curY - y }, children };
2370
+ }
2371
+ layoutGrid(element, x, y, width, gap) {
2372
+ const colCount = typeof element.columns === "number" ? element.columns : parseInt(String(element.columns), 10) || 2;
2373
+ const totalGap = gap * (colCount - 1);
2374
+ const colWidth = Math.max(10, (width - totalGap) / colCount);
2375
+ const children = [];
2376
+ const colYs = Array(colCount).fill(y);
2377
+ for (let i = 0; i < element.elements.length; i++) {
2378
+ const colIdx = i % colCount;
2379
+ const colX = x + colIdx * (colWidth + gap);
2380
+ const childEl = element.elements[i];
2381
+ const childNode = this.layoutSingleElement(childEl, colX, colYs[colIdx], colWidth, gap);
2382
+ children.push(childNode);
2383
+ colYs[colIdx] += childNode.bounds.height + gap;
2384
+ }
2385
+ const maxGridHeight = Math.max(...colYs.map((cy) => cy - y), 100);
2386
+ return {
2387
+ element,
2388
+ bounds: { x, y, width, height: maxGridHeight },
2389
+ children
2390
+ };
2391
+ }
2392
+ layoutCompare(element, x, y, width, gap) {
2393
+ const colW = (width - gap) / 2;
2394
+ const titleH = 45;
2395
+ const pad = 24;
2396
+ const leftStartY = y + pad + (element.leftTitle ? titleH : 0);
2397
+ const { nodes: leftChildren, totalHeight: leftInnerH } = this.layoutElementList(element.left, x + pad, leftStartY, colW - pad * 2, gap / 2);
2398
+ const rightX = x + colW + gap;
2399
+ const rightStartY = y + pad + (element.rightTitle ? titleH : 0);
2400
+ const { nodes: rightChildren, totalHeight: rightInnerH } = this.layoutElementList(element.right, rightX + pad, rightStartY, colW - pad * 2, gap / 2);
2401
+ const totalHeight = Math.max(leftInnerH, rightInnerH, 120) + (element.leftTitle || element.rightTitle ? titleH : 0) + pad * 2;
2402
+ return {
2403
+ element,
2404
+ bounds: { x, y, width, height: totalHeight },
2405
+ children: [...leftChildren, ...rightChildren]
2406
+ };
2407
+ }
2408
+ layoutStack(element, x, y, width, gap) {
2409
+ if (element.direction === "horizontal") {
2410
+ const count = element.elements.length;
2411
+ const itemW = (width - gap * (count - 1)) / Math.max(1, count);
2412
+ let curX = x;
2413
+ const children = [];
2414
+ let maxH = 0;
2415
+ for (const child of element.elements) {
2416
+ const node = this.layoutSingleElement(child, curX, y, itemW, gap);
2417
+ children.push(node);
2418
+ maxH = Math.max(maxH, node.bounds.height);
2419
+ curX += itemW + gap;
2420
+ }
2421
+ return { element, bounds: { x, y, width, height: maxH }, children };
2422
+ } else {
2423
+ const { nodes: children, totalHeight } = this.layoutElementList(element.elements, x, y, width, gap);
2424
+ return { element, bounds: { x, y, width, height: totalHeight }, children };
2425
+ }
2426
+ }
2267
2427
  layoutCard(element, x, y, width, gap) {
2268
2428
  const cardPadding = 32;
2269
2429
  const innerWidth = Math.max(10, width - cardPadding * 2);
@@ -4542,6 +4702,19 @@ var PptxRenderer = class {
4542
4702
  for (const node of slideLayout.nodes) {
4543
4703
  this.renderNode(pptxSlide, pptx, node, scaleX, scaleY, theme, presentation);
4544
4704
  }
4705
+ if (presentation.metadata.watermark) {
4706
+ const watermarkText = typeof presentation.metadata.watermark === "string" ? presentation.metadata.watermark : "CONFIDENTIAL";
4707
+ pptxSlide.addText(watermarkText.toUpperCase(), {
4708
+ x: 0.5,
4709
+ y: slideHeightInches - 0.4,
4710
+ w: slideWidthInches - 1,
4711
+ h: 0.25,
4712
+ fontSize: 9,
4713
+ bold: true,
4714
+ color: this.cleanHexColor(theme.colors.muted || "#888888"),
4715
+ fontFace: cleanFontFace(theme.typography.headingFont)
4716
+ });
4717
+ }
4545
4718
  if (slide.notes) {
4546
4719
  pptxSlide.addNotes(slide.notes);
4547
4720
  }
@@ -4919,7 +5092,7 @@ var PptxRenderer = class {
4919
5092
  }
4920
5093
  renderCode(pptxSlide, pptx, code, rect, theme) {
4921
5094
  const codeTheme = theme.components?.code;
4922
- const bgColor = this.cleanHexColor(codeTheme?.background || "#0a0f1d");
5095
+ const bgColor = this.cleanHexColor(codeTheme?.background || "#0f172a");
4923
5096
  const textColor = this.cleanHexColor(codeTheme?.textColor || "#f8fafc");
4924
5097
  const primaryColor = this.cleanHexColor(theme.colors.primary || "#00F0FF");
4925
5098
  const mutedColor = this.cleanHexColor(theme.colors.muted || "#64748b");
@@ -4929,8 +5102,32 @@ var PptxRenderer = class {
4929
5102
  w: rect.w,
4930
5103
  h: rect.h,
4931
5104
  fill: { color: bgColor },
4932
- line: { color: this.cleanHexColor(theme.colors.border || "#1e293b"), width: 1 },
4933
- rectRadius: 0.05
5105
+ line: { color: this.cleanHexColor(theme.colors.border || "#334155"), width: 1.5 },
5106
+ rectRadius: 0.08
5107
+ });
5108
+ pptxSlide.addShape(pptx.ShapeType.ellipse || "ellipse", {
5109
+ x: rect.x + 0.15,
5110
+ y: rect.y + 0.12,
5111
+ w: 0.1,
5112
+ h: 0.1,
5113
+ fill: { color: "ef4444" },
5114
+ line: { color: "ef4444", width: 0 }
5115
+ });
5116
+ pptxSlide.addShape(pptx.ShapeType.ellipse || "ellipse", {
5117
+ x: rect.x + 0.3,
5118
+ y: rect.y + 0.12,
5119
+ w: 0.1,
5120
+ h: 0.1,
5121
+ fill: { color: "f59e0b" },
5122
+ line: { color: "f59e0b", width: 0 }
5123
+ });
5124
+ pptxSlide.addShape(pptx.ShapeType.ellipse || "ellipse", {
5125
+ x: rect.x + 0.45,
5126
+ y: rect.y + 0.12,
5127
+ w: 0.1,
5128
+ h: 0.1,
5129
+ fill: { color: "10b981" },
5130
+ line: { color: "10b981", width: 0 }
4934
5131
  });
4935
5132
  const lines = code.code.split("\n");
4936
5133
  if (code.highlight) {
@@ -4960,11 +5157,11 @@ var PptxRenderer = class {
4960
5157
  const isHl = highlightSet.has(lineNum);
4961
5158
  const lineNumPad = String(lineNum).padStart(2, "0");
4962
5159
  const numColor = isHl ? primaryColor : mutedColor;
4963
- const contentColor = isHl ? primaryColor : mutedColor;
5160
+ const contentColor = isHl ? primaryColor : textColor;
4964
5161
  chunks.push({
4965
5162
  text: `${lineNumPad} `,
4966
5163
  options: {
4967
- fontSize: 12,
5164
+ fontSize: 11,
4968
5165
  fontFace: "Consolas",
4969
5166
  color: numColor,
4970
5167
  bold: isHl
@@ -4973,7 +5170,7 @@ var PptxRenderer = class {
4973
5170
  chunks.push({
4974
5171
  text: line || " ",
4975
5172
  options: {
4976
- fontSize: 12,
5173
+ fontSize: 11,
4977
5174
  fontFace: "Consolas",
4978
5175
  color: contentColor,
4979
5176
  bold: isHl,
@@ -4983,19 +5180,19 @@ var PptxRenderer = class {
4983
5180
  });
4984
5181
  pptxSlide.addText(chunks, {
4985
5182
  x: rect.x + 0.2,
4986
- y: rect.y + 0.15,
5183
+ y: rect.y + 0.32,
4987
5184
  w: rect.w - 0.4,
4988
- h: rect.h - 0.3,
5185
+ h: rect.h - 0.4,
4989
5186
  valign: "top",
4990
5187
  margin: 0
4991
5188
  });
4992
5189
  } else {
4993
5190
  pptxSlide.addText(code.code, {
4994
5191
  x: rect.x + 0.2,
4995
- y: rect.y + 0.15,
5192
+ y: rect.y + 0.32,
4996
5193
  w: rect.w - 0.4,
4997
- h: rect.h - 0.3,
4998
- fontSize: 13,
5194
+ h: rect.h - 0.4,
5195
+ fontSize: 11.5,
4999
5196
  fontFace: "Consolas",
5000
5197
  color: textColor,
5001
5198
  valign: "top"
@@ -5321,6 +5518,11 @@ ${item.description}` : "";
5321
5518
  align: "center",
5322
5519
  valign: "middle"
5323
5520
  });
5521
+ if (node.children) {
5522
+ for (const childNode of node.children) {
5523
+ this.renderNode(pptxSlide, pptx, childNode, scaleX, scaleY, theme);
5524
+ }
5525
+ }
5324
5526
  }
5325
5527
  renderSection(pptxSlide, pptx, section, rect, theme) {
5326
5528
  const cardBg = this.cleanHexColor(theme.colors.surface || "#0f172a");
@@ -5662,22 +5864,30 @@ ${mermaid.code}`, {
5662
5864
  const nodeH = isLR ? Math.min(0.65, (rect.h - 0.5) / maxLane) : Math.min(0.6, (rect.h - 0.5) / (numRanks * 1.4));
5663
5865
  const gapX = isLR ? numRanks > 1 ? (rect.w - numRanks * nodeW) / (numRanks - 1) : 0 : maxLane > 1 ? (rect.w - maxLane * nodeW) / (maxLane - 1) : 0;
5664
5866
  const gapY = isLR ? maxLane > 1 ? (rect.h - maxLane * nodeH) / (maxLane - 1) : 0 : numRanks > 1 ? (rect.h - numRanks * nodeH) / (numRanks - 1) : 0;
5867
+ const maxLaneHeight = isLR ? maxLane * (nodeH + gapY) - gapY : numRanks * (nodeH + gapY) - gapY;
5868
+ const maxRankWidth = isLR ? numRanks * (nodeW + gapX) - gapX : maxLane * (nodeW + gapX) - gapX;
5665
5869
  const positions = {};
5666
5870
  sortedRanks.forEach((r, rIdx) => {
5667
5871
  const ids = rankGroups[r];
5668
- ids.forEach((id, lIdx) => {
5669
- if (isLR) {
5872
+ if (isLR) {
5873
+ const colHeight = ids.length * (nodeH + gapY) - gapY;
5874
+ const offsetY = (maxLaneHeight - colHeight) / 2;
5875
+ ids.forEach((id, lIdx) => {
5670
5876
  positions[id] = {
5671
5877
  x: rect.x + rIdx * (nodeW + gapX),
5672
- y: rect.y + lIdx * (nodeH + gapY)
5878
+ y: rect.y + offsetY + lIdx * (nodeH + gapY)
5673
5879
  };
5674
- } else {
5880
+ });
5881
+ } else {
5882
+ const rowWidth = ids.length * (nodeW + gapX) - gapX;
5883
+ const offsetX = (maxRankWidth - rowWidth) / 2;
5884
+ ids.forEach((id, lIdx) => {
5675
5885
  positions[id] = {
5676
- x: rect.x + lIdx * (nodeW + gapX),
5886
+ x: rect.x + offsetX + lIdx * (nodeW + gapX),
5677
5887
  y: rect.y + rIdx * (nodeH + gapY)
5678
5888
  };
5679
- }
5680
- });
5889
+ });
5890
+ }
5681
5891
  });
5682
5892
  const surfaceColor = this.cleanHexColor(theme.colors.surface || "1e293b");
5683
5893
  const arrowColor = this.cleanHexColor(theme.colors.accent || theme.colors.primary);
@@ -5690,11 +5900,19 @@ ${mermaid.code}`, {
5690
5900
  const y1 = isLR ? p1.y + nodeH / 2 : p1.y + nodeH;
5691
5901
  const x2 = isLR ? p2.x : p2.x + nodeW / 2;
5692
5902
  const y2 = isLR ? p2.y + nodeH / 2 : p2.y;
5903
+ const minX = Math.min(x1, x2);
5904
+ const minY = Math.min(y1, y2);
5905
+ const lineW = Math.max(0.01, Math.abs(x2 - x1));
5906
+ const lineH = Math.max(0.01, Math.abs(y2 - y1));
5907
+ const flipH = x2 < x1;
5908
+ const flipV = y2 < y1;
5693
5909
  pptxSlide.addShape(pptx.ShapeType.line, {
5694
- x: x1,
5695
- y: y1,
5696
- w: x2 - x1,
5697
- h: y2 - y1,
5910
+ x: minX,
5911
+ y: minY,
5912
+ w: lineW,
5913
+ h: lineH,
5914
+ flipH,
5915
+ flipV,
5698
5916
  line: {
5699
5917
  color: arrowColor,
5700
5918
  width: 2,
@@ -5705,13 +5923,25 @@ ${mermaid.code}`, {
5705
5923
  if (e.label) {
5706
5924
  const midX = (x1 + x2) / 2;
5707
5925
  const midY = (y1 + y2) / 2;
5926
+ const pillW = Math.min(1.4, Math.max(0.7, e.label.length * 0.08 + 0.25));
5927
+ pptxSlide.addShape(pptx.ShapeType.roundRect, {
5928
+ x: midX - pillW / 2,
5929
+ y: midY - 0.13,
5930
+ w: pillW,
5931
+ h: 0.26,
5932
+ fill: { color: surfaceColor },
5933
+ line: { color: this.cleanHexColor(theme.colors.border || "cbd5e1"), width: 1 },
5934
+ rectRadius: 0.06
5935
+ });
5708
5936
  pptxSlide.addText(e.label, {
5709
- x: midX - 0.4,
5710
- y: midY - 0.15,
5711
- w: 0.8,
5712
- h: 0.3,
5937
+ x: midX - pillW / 2,
5938
+ y: midY - 0.13,
5939
+ w: pillW,
5940
+ h: 0.26,
5713
5941
  fontSize: 9,
5714
- color: this.cleanHexColor(theme.colors.muted || "94a3b8"),
5942
+ bold: true,
5943
+ color: this.cleanHexColor(theme.colors.muted || "64748b"),
5944
+ fontFace: cleanFontFace(theme.typography.headingFont),
5715
5945
  align: "center",
5716
5946
  valign: "middle"
5717
5947
  });
@@ -5737,7 +5967,7 @@ ${mermaid.code}`, {
5737
5967
  else if (n.shape === "database")
5738
5968
  shapeType = pptx.ShapeType.can;
5739
5969
  else if (n.shape === "circle")
5740
- shapeType = pptx.ShapeType.oval;
5970
+ shapeType = pptx.ShapeType.ellipse || "ellipse";
5741
5971
  pptxSlide.addShape(shapeType, {
5742
5972
  x: p.x,
5743
5973
  y: p.y,
@@ -5747,14 +5977,24 @@ ${mermaid.code}`, {
5747
5977
  line: { color: nodeBorderColor, width: 2 },
5748
5978
  rectRadius: 0.08
5749
5979
  });
5980
+ if (n.shape === "database") {
5981
+ pptxSlide.addShape(pptx.ShapeType.rect, {
5982
+ x: p.x + 0.15,
5983
+ y: p.y + 0.1,
5984
+ w: Math.max(0.1, nodeW - 0.3),
5985
+ h: 0.02,
5986
+ fill: { color: nodeBorderColor },
5987
+ line: { color: nodeBorderColor, width: 1 }
5988
+ });
5989
+ }
5750
5990
  pptxSlide.addText(n.label, {
5751
- x: p.x + 0.05,
5752
- y: p.y + 0.05,
5753
- w: nodeW - 0.1,
5754
- h: nodeH - 0.1,
5991
+ x: p.x + 0.06,
5992
+ y: p.y + 0.06,
5993
+ w: Math.max(0.1, nodeW - 0.12),
5994
+ h: Math.max(0.1, nodeH - 0.12),
5755
5995
  align: "center",
5756
5996
  valign: "middle",
5757
- fontSize: 11,
5997
+ fontSize: n.label.length > 18 ? 10 : 11,
5758
5998
  bold: true,
5759
5999
  color: this.cleanHexColor(theme.colors.text || "ffffff"),
5760
6000
  fontFace: cleanFontFace(theme.typography.headingFont)
@@ -5776,6 +6016,13 @@ ${mermaid.code}`, {
5776
6016
  if (CSS_NAMED_COLORS[trimmed]) {
5777
6017
  return CSS_NAMED_COLORS[trimmed];
5778
6018
  }
6019
+ const rgbMatch = trimmed.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
6020
+ if (rgbMatch && rgbMatch[1] && rgbMatch[2] && rgbMatch[3]) {
6021
+ const r = Math.min(255, Math.max(0, parseInt(rgbMatch[1], 10))).toString(16).padStart(2, "0");
6022
+ const g = Math.min(255, Math.max(0, parseInt(rgbMatch[2], 10))).toString(16).padStart(2, "0");
6023
+ const b = Math.min(255, Math.max(0, parseInt(rgbMatch[3], 10))).toString(16).padStart(2, "0");
6024
+ return `${r}${g}${b}`;
6025
+ }
5779
6026
  const hexMatch = trimmed.match(/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i);
5780
6027
  if (hexMatch && hexMatch[1]) {
5781
6028
  const hex = hexMatch[1];
@@ -5978,7 +6225,7 @@ var HtmlRenderer = class {
5978
6225
  flex-direction: column;
5979
6226
  justify-content: flex-start;
5980
6227
  align-items: stretch;
5981
- padding: clamp(2rem, 4vw, 3.5rem) clamp(2.5rem, 5vw, 4.5rem);
6228
+ padding: clamp(1.4rem, 2.8vw, 2.2rem) clamp(2rem, 4vw, 3.8rem) clamp(2.2rem, 3.5vw, 3.2rem);
5982
6229
  box-sizing: border-box;
5983
6230
  animation: fadeIn 0.25s cubic-bezier(0.16, 1, 0.3, 1);
5984
6231
  }
@@ -5997,31 +6244,40 @@ var HtmlRenderer = class {
5997
6244
  font-family: var(--yumia-font-heading), system-ui, -apple-system, sans-serif;
5998
6245
  font-weight: 700;
5999
6246
  line-height: var(--yumia-line-height-tight);
6000
- margin-bottom: 0.4em;
6247
+ margin-bottom: 0.35em;
6001
6248
  letter-spacing: var(--yumia-letter-spacing-tight);
6002
6249
  overflow-wrap: break-word;
6003
6250
  word-break: break-word;
6004
6251
  }
6005
6252
 
6006
6253
  h1 {
6007
- font-size: clamp(1.8rem, 3.2vw, 2.9rem);
6254
+ font-size: clamp(1.6rem, 2.8vw, 2.5rem);
6008
6255
  color: var(--yumia-primary);
6009
6256
  line-height: 1.15;
6010
6257
  }
6011
6258
 
6259
+ h1 + h2 {
6260
+ margin-top: -0.15em;
6261
+ margin-bottom: 0.75em;
6262
+ font-size: clamp(1rem, 1.6vw, 1.35rem);
6263
+ color: var(--yumia-muted);
6264
+ font-weight: 600;
6265
+ letter-spacing: 0.02em;
6266
+ }
6267
+
6012
6268
  h2 {
6013
- font-size: clamp(1.5rem, 2.6vw, 2.3rem);
6269
+ font-size: clamp(1.3rem, 2.2vw, 1.9rem);
6014
6270
  color: var(--yumia-text);
6015
6271
  line-height: 1.2;
6016
6272
  }
6017
6273
 
6018
6274
  h3 {
6019
- font-size: clamp(1.25rem, 2vw, 1.8rem);
6275
+ font-size: clamp(1.15rem, 1.8vw, 1.5rem);
6020
6276
  color: var(--yumia-text);
6021
6277
  }
6022
6278
 
6023
6279
  h4 {
6024
- font-size: clamp(1.05rem, 1.5vw, 1.35rem);
6280
+ font-size: clamp(1rem, 1.4vw, 1.25rem);
6025
6281
  color: var(--yumia-muted);
6026
6282
  }
6027
6283
 
@@ -6113,6 +6369,7 @@ var HtmlRenderer = class {
6113
6369
  flex-direction: column;
6114
6370
  gap: 0.8rem;
6115
6371
  height: 100%;
6372
+ min-width: 0;
6116
6373
  }
6117
6374
 
6118
6375
  /* Cards */
@@ -6458,24 +6715,25 @@ var HtmlRenderer = class {
6458
6715
  .yumia-compare {
6459
6716
  display: grid;
6460
6717
  grid-template-columns: 1fr auto 1fr;
6461
- gap: 20px;
6718
+ gap: clamp(12px, 1.8vw, 20px);
6462
6719
  align-items: stretch;
6463
- margin: 1.2rem 0;
6720
+ margin: 0.5rem 0;
6464
6721
  }
6465
6722
  .yumia-compare-col {
6466
6723
  background: var(--yumia-surface);
6467
- border: 1px solid var(--yumia-border);
6724
+ border: 1.5px solid var(--yumia-border);
6468
6725
  border-radius: var(--yumia-radius-card);
6469
- padding: 1.5rem;
6726
+ padding: clamp(1rem, 1.6vw, 1.3rem);
6470
6727
  display: flex;
6471
6728
  flex-direction: column;
6729
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
6472
6730
  }
6473
6731
  .yumia-compare-title {
6474
- font-size: 1.2rem;
6732
+ font-size: clamp(1rem, 1.4vw, 1.15rem);
6475
6733
  font-weight: 700;
6476
6734
  color: var(--yumia-primary);
6477
- margin-bottom: 1rem;
6478
- padding-bottom: 0.5rem;
6735
+ margin-bottom: 0.6rem;
6736
+ padding-bottom: 0.4rem;
6479
6737
  border-bottom: 1px solid var(--yumia-divider);
6480
6738
  }
6481
6739
  .yumia-compare-divider {
@@ -6483,49 +6741,80 @@ var HtmlRenderer = class {
6483
6741
  align-items: center;
6484
6742
  justify-content: center;
6485
6743
  font-weight: 800;
6486
- font-size: 0.9rem;
6744
+ font-size: 0.85rem;
6487
6745
  color: var(--yumia-muted);
6488
- background: rgba(255, 255, 255, 0.05);
6746
+ background: var(--yumia-surface);
6489
6747
  border-radius: 50%;
6490
- width: 36px;
6491
- height: 36px;
6748
+ width: 32px;
6749
+ height: 32px;
6492
6750
  align-self: center;
6493
- border: 1px solid var(--yumia-border);
6751
+ border: 1.5px solid var(--yumia-border);
6752
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
6494
6753
  }
6495
6754
 
6496
6755
  /* Native SVG Chart Container */
6497
6756
  .yumia-chart-container {
6498
6757
  background: var(--yumia-surface);
6499
- border: 1px solid var(--yumia-border);
6758
+ border: 1.5px solid var(--yumia-border);
6500
6759
  border-radius: var(--yumia-radius-card);
6501
- padding: 1.25rem;
6502
- margin: 1rem 0;
6760
+ padding: clamp(0.8rem, 1.4vw, 1.25rem);
6761
+ margin: 0.6rem 0;
6503
6762
  display: flex;
6504
6763
  flex-direction: column;
6505
6764
  align-items: center;
6506
6765
  width: 100%;
6766
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
6507
6767
  }
6508
6768
  .yumia-chart-title {
6509
- font-size: 1.1rem;
6769
+ font-size: 1.05rem;
6510
6770
  font-weight: 700;
6511
6771
  color: var(--yumia-text);
6512
- margin-bottom: 0.75rem;
6772
+ margin-bottom: 0.6rem;
6513
6773
  align-self: flex-start;
6514
6774
  }
6515
6775
 
6776
+ /* Diagram Container */
6777
+ .yumia-diagram-container {
6778
+ width: 100%;
6779
+ display: flex;
6780
+ flex-direction: column;
6781
+ align-items: center;
6782
+ justify-content: center;
6783
+ margin: 0.5rem 0;
6784
+ max-height: 52vh;
6785
+ }
6786
+ .yumia-diagram {
6787
+ max-width: 100%;
6788
+ max-height: 48vh;
6789
+ height: auto;
6790
+ background: var(--yumia-surface);
6791
+ border: 1.5px solid var(--yumia-border);
6792
+ border-radius: var(--yumia-radius-card);
6793
+ padding: 12px;
6794
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
6795
+ box-sizing: border-box;
6796
+ }
6797
+ .yumia-diag-node {
6798
+ transition: transform 0.2s ease, filter 0.2s ease;
6799
+ cursor: pointer;
6800
+ }
6801
+ .yumia-diag-node:hover {
6802
+ filter: brightness(1.1);
6803
+ }
6804
+
6516
6805
  /* Mermaid Container */
6517
6806
  .mermaid-container {
6518
- background: rgba(10, 10, 18, 0.6);
6807
+ background: var(--yumia-surface);
6519
6808
  border: 1.5px solid var(--yumia-border);
6520
6809
  border-radius: var(--yumia-radius-card);
6521
- padding: 1.5rem;
6522
- margin: 1rem 0;
6810
+ padding: 1.2rem;
6811
+ margin: 0.6rem 0;
6523
6812
  display: flex;
6524
6813
  justify-content: center;
6525
6814
  align-items: center;
6526
6815
  overflow: auto;
6527
6816
  width: 100%;
6528
- min-height: 240px;
6817
+ min-height: 200px;
6529
6818
  }
6530
6819
 
6531
6820
  .mermaid-container pre.mermaid,
@@ -6535,7 +6824,7 @@ var HtmlRenderer = class {
6535
6824
  padding: 0 !important;
6536
6825
  margin: 0 !important;
6537
6826
  font-size: 1rem;
6538
- color: #fff;
6827
+ color: var(--yumia-text);
6539
6828
  display: flex;
6540
6829
  justify-content: center;
6541
6830
  align-items: center;
@@ -6554,18 +6843,18 @@ var HtmlRenderer = class {
6554
6843
  border: 1.5px solid var(--yumia-border);
6555
6844
  border-left: 4px solid var(--yumia-primary);
6556
6845
  border-radius: var(--yumia-radius-card);
6557
- padding: 0.75rem 1.4rem;
6558
- margin: 0.7rem 0;
6846
+ padding: 0.55rem 1.2rem;
6847
+ margin: 0.45rem 0;
6559
6848
  display: flex;
6560
6849
  align-items: center;
6561
6850
  justify-content: center;
6562
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.25);
6851
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
6563
6852
  overflow-x: auto;
6564
6853
  max-width: 100%;
6565
6854
  }
6566
6855
 
6567
6856
  .yumia-math-equation {
6568
- font-size: clamp(1.1rem, 1.8vw, 1.45rem);
6857
+ font-size: clamp(1rem, 1.6vw, 1.35rem);
6569
6858
  color: var(--yumia-text);
6570
6859
  letter-spacing: 0.03em;
6571
6860
  }
@@ -7510,7 +7799,7 @@ var HtmlRenderer = class {
7510
7799
  const colorVar = sev === "warning" ? "var(--yumia-warning)" : sev === "danger" ? "var(--yumia-danger)" : sev === "success" ? "var(--yumia-success)" : "var(--yumia-info)";
7511
7800
  const titleHtml = c.title ? `<div style="font-weight:700; font-size:1.05rem; color:${colorVar}; margin-bottom:4px;">${this.formatInline(c.title)}</div>` : "";
7512
7801
  return `
7513
- <div class="yumia-callout" data-severity="${sev}" data-yumia-role="callout" style="background:var(--yumia-surface); border-left:4px solid ${colorVar}; border-radius:var(--yumia-radius-card); padding:1rem 1.4rem; margin:0.8rem 0; width:100%;">
7802
+ <div class="yumia-callout" data-severity="${sev}" data-yumia-role="callout" style="background:var(--yumia-surface); border-left:4px solid ${colorVar}; border-radius:var(--yumia-radius-card); padding:1rem 1.4rem; margin:0.8rem 0; width:100%; display:flex; flex-direction:column; gap:6px; box-sizing:border-box;">
7514
7803
  ${titleHtml}
7515
7804
  <div style="font-size:0.95rem; color:var(--yumia-text); line-height:1.5;">${this.formatInline(c.text)}</div>
7516
7805
  </div>`;
@@ -7759,10 +8048,10 @@ var HtmlRenderer = class {
7759
8048
  }
7760
8049
  renderDiagram(d, theme) {
7761
8050
  const isLR = (d.direction || "LR").toUpperCase() === "LR";
7762
- const nodeW = 140;
7763
- const nodeH = 50;
7764
- const gapX = isLR ? 70 : 40;
7765
- const gapY = isLR ? 40 : 60;
8051
+ const nodeW = 155;
8052
+ const nodeH = 48;
8053
+ const gapX = isLR ? 60 : 35;
8054
+ const gapY = isLR ? 30 : 50;
7766
8055
  const nodeIds = d.nodes.map((n) => n.id);
7767
8056
  const inDegree = {};
7768
8057
  const adj = {};
@@ -7813,28 +8102,42 @@ var HtmlRenderer = class {
7813
8102
  });
7814
8103
  const sortedRanks = Object.keys(rankGroups).map(Number).sort((a, b) => a - b);
7815
8104
  const positions = {};
7816
- let maxLaneIndex = 0;
8105
+ let maxLaneIndex = 1;
8106
+ sortedRanks.forEach((r) => {
8107
+ maxLaneIndex = Math.max(maxLaneIndex, rankGroups[r].length);
8108
+ });
8109
+ const maxLaneHeight = isLR ? maxLaneIndex * (nodeH + gapY) - gapY : sortedRanks.length * (nodeH + gapY) - gapY;
8110
+ const maxRankWidth = isLR ? sortedRanks.length * (nodeW + gapX) - gapX : maxLaneIndex * (nodeW + gapX) - gapX;
7817
8111
  sortedRanks.forEach((r, rIdx) => {
7818
8112
  const ids = rankGroups[r];
7819
- maxLaneIndex = Math.max(maxLaneIndex, ids.length);
7820
- ids.forEach((id, lIdx) => {
7821
- if (isLR) {
8113
+ if (isLR) {
8114
+ const colHeight = ids.length * (nodeH + gapY) - gapY;
8115
+ const offsetY = (maxLaneHeight - colHeight) / 2;
8116
+ ids.forEach((id, lIdx) => {
7822
8117
  positions[id] = {
7823
8118
  x: 40 + rIdx * (nodeW + gapX),
7824
- y: 40 + lIdx * (nodeH + gapY)
8119
+ y: 40 + offsetY + lIdx * (nodeH + gapY)
7825
8120
  };
7826
- } else {
8121
+ });
8122
+ } else {
8123
+ const rowWidth = ids.length * (nodeW + gapX) - gapX;
8124
+ const offsetX = (maxRankWidth - rowWidth) / 2;
8125
+ ids.forEach((id, lIdx) => {
7827
8126
  positions[id] = {
7828
- x: 40 + lIdx * (nodeW + gapX),
8127
+ x: 40 + offsetX + lIdx * (nodeW + gapX),
7829
8128
  y: 40 + rIdx * (nodeH + gapY)
7830
8129
  };
7831
- }
7832
- });
8130
+ });
8131
+ }
7833
8132
  });
7834
8133
  const totalWidth = isLR ? 80 + Math.max(1, sortedRanks.length) * (nodeW + gapX) - gapX : 80 + Math.max(1, maxLaneIndex) * (nodeW + gapX) - gapX;
7835
- const totalHeight = isLR ? 80 + Math.max(1, maxLaneIndex) * (nodeH + gapY) - gapY : 80 + Math.max(1, sortedRanks.length) * (nodeH + gapY) - gapY;
8134
+ const totalHeight = isLR ? 80 + maxLaneHeight : 80 + Math.max(1, sortedRanks.length) * (nodeH + gapY) - gapY;
7836
8135
  const arrowColor = theme.colors.accent || theme.colors.primary;
7837
8136
  const markerId = `arrow-${Math.random().toString(36).slice(2, 8)}`;
8137
+ const surfaceFill = theme.colors.surface || "rgba(15, 23, 42, 0.85)";
8138
+ const textFill = theme.colors.text || "#ffffff";
8139
+ const mutedColor = theme.colors.muted || "#64748b";
8140
+ const borderColor = theme.colors.border || "rgba(255, 255, 255, 0.15)";
7838
8141
  let edgesSvg = "";
7839
8142
  d.edges.forEach((e) => {
7840
8143
  const p1 = positions[e.from];
@@ -7862,8 +8165,8 @@ var HtmlRenderer = class {
7862
8165
  edgesSvg += `<path d="M ${x1} ${y1} C ${midX} ${y1}, ${midX} ${y2}, ${x2} ${y2}" fill="none" stroke="${arrowColor}" stroke-width="2" ${dash} marker-end="url(#${markerId})" />`;
7863
8166
  if (e.label) {
7864
8167
  edgesSvg += `<g transform="translate(${midX}, ${midY})">
7865
- <rect x="-35" y="-10" width="70" height="20" rx="4" fill="rgba(15, 23, 42, 0.85)" stroke="rgba(255,255,255,0.2)" stroke-width="1"/>
7866
- <text x="0" y="3" text-anchor="middle" fill="#e2e8f0" font-size="10" font-family="sans-serif">${this.escapeHtml(e.label)}</text>
8168
+ <rect x="-42" y="-11" width="84" height="22" rx="6" fill="${surfaceFill}" stroke="${borderColor}" stroke-width="1.2"/>
8169
+ <text x="0" y="4" text-anchor="middle" fill="${mutedColor}" font-size="10.5" font-weight="600" font-family="sans-serif">${this.escapeHtml(e.label)}</text>
7867
8170
  </g>`;
7868
8171
  }
7869
8172
  });
@@ -7885,28 +8188,28 @@ var HtmlRenderer = class {
7885
8188
  let shapeSvg = "";
7886
8189
  if (n.shape === "database") {
7887
8190
  shapeSvg = `
7888
- <rect x="${p.x}" y="${p.y}" width="${nodeW}" height="${nodeH}" rx="6" fill="rgba(15, 23, 42, 0.75)" stroke="${nodeColor}" stroke-width="2" />
7889
- <path d="M ${p.x} ${p.y + 12} C ${p.x + nodeW / 2} ${p.y + 20}, ${p.x + nodeW / 2} ${p.y + 20}, ${p.x + nodeW} ${p.y + 12}" fill="none" stroke="${nodeColor}" stroke-width="1.5"/>
8191
+ <rect x="${p.x}" y="${p.y}" width="${nodeW}" height="${nodeH}" rx="8" fill="${surfaceFill}" stroke="${nodeColor}" stroke-width="2" />
8192
+ <path d="M ${p.x} ${p.y + 11} C ${p.x + nodeW / 2} ${p.y + 18}, ${p.x + nodeW / 2} ${p.y + 18}, ${p.x + nodeW} ${p.y + 11}" fill="none" stroke="${nodeColor}" stroke-width="1.5"/>
7890
8193
  `;
7891
8194
  } else if (n.shape === "diamond") {
7892
8195
  const cx = p.x + nodeW / 2;
7893
8196
  const cy = p.y + nodeH / 2;
7894
- shapeSvg = `<polygon points="${cx},${p.y} ${p.x + nodeW},${cy} ${cx},${p.y + nodeH} ${p.x},${cy}" fill="rgba(15, 23, 42, 0.75)" stroke="${nodeColor}" stroke-width="2"/>`;
8197
+ shapeSvg = `<polygon points="${cx},${p.y} ${p.x + nodeW},${cy} ${cx},${p.y + nodeH} ${p.x},${cy}" fill="${surfaceFill}" stroke="${nodeColor}" stroke-width="2"/>`;
7895
8198
  } else {
7896
- shapeSvg = `<rect x="${p.x}" y="${p.y}" width="${nodeW}" height="${nodeH}" rx="8" fill="rgba(15, 23, 42, 0.75)" stroke="${nodeColor}" stroke-width="2" />`;
8199
+ shapeSvg = `<rect x="${p.x}" y="${p.y}" width="${nodeW}" height="${nodeH}" rx="10" fill="${surfaceFill}" stroke="${nodeColor}" stroke-width="2" />`;
7897
8200
  }
7898
8201
  nodesSvg += `
7899
8202
  <g class="yumia-diag-node" data-id="${n.id}">
7900
8203
  ${shapeSvg}
7901
- <text x="${p.x + nodeW / 2}" y="${p.y + nodeH / 2 + 4}" text-anchor="middle" fill="#ffffff" font-size="12" font-weight="600" font-family="sans-serif">${this.escapeHtml(n.label)}</text>
8204
+ <text x="${p.x + nodeW / 2}" y="${p.y + nodeH / 2 + 4}" text-anchor="middle" fill="${textFill}" font-size="11.5" font-weight="700" font-family="sans-serif">${this.escapeHtml(n.label)}</text>
7902
8205
  </g>
7903
8206
  `;
7904
8207
  });
7905
- const titleHtml = d.title ? `<div style="font-weight:700; font-size:1.1rem; color:var(--yumia-primary); margin-bottom:0.6rem; text-align:center;">${this.escapeHtml(d.title)}</div>` : "";
8208
+ const titleHtml = d.title ? `<div style="font-weight:700; font-size:1.05rem; color:var(--yumia-primary); margin-bottom:0.4rem; text-align:center;">${this.escapeHtml(d.title)}</div>` : "";
7906
8209
  return `
7907
- <div class="yumia-diagram-container" style="width:100%; display:flex; flex-direction:column; align-items:center; margin:1rem 0;">
8210
+ <div class="yumia-diagram-container">
7908
8211
  ${titleHtml}
7909
- <svg class="yumia-diagram" viewBox="0 0 ${totalWidth} ${totalHeight}" style="max-width:100%; height:auto; background:rgba(0,0,0,0.2); border:1px solid rgba(255,255,255,0.08); border-radius:12px; padding:10px;">
8212
+ <svg class="yumia-diagram" viewBox="0 0 ${totalWidth} ${totalHeight}" preserveAspectRatio="xMidYMid meet">
7910
8213
  <defs>
7911
8214
  <marker id="${markerId}" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
7912
8215
  <path d="M 0 1.5 L 8 5 L 0 8.5 z" fill="${arrowColor}" />
@@ -8149,7 +8452,14 @@ var PdfRenderer = class {
8149
8452
  }
8150
8453
  const progressWidth = slideNum / totalSlides * pageWidth;
8151
8454
  doc.rect(0, pageHeight - 4, progressWidth, 4).fill(theme.colors.primary);
8152
- doc.font("Helvetica").fontSize(10).fillColor(theme.colors.muted || "#888888").text(`${slideNum} / ${totalSlides}`, pageWidth - padX - 60, pageHeight - 24, {
8455
+ if (presentation?.metadata.watermark) {
8456
+ const watermarkText = typeof presentation.metadata.watermark === "string" ? presentation.metadata.watermark : "CONFIDENTIAL";
8457
+ doc.font("Helvetica-Bold").fontSize(9).fillColor(theme.colors.muted || "#888888").text(watermarkText.toUpperCase(), padX, pageHeight - 22, {
8458
+ width: contentWidth - 80,
8459
+ align: "left"
8460
+ });
8461
+ }
8462
+ doc.font("Helvetica").fontSize(10).fillColor(theme.colors.muted || "#888888").text(`${slideNum} / ${totalSlides}`, pageWidth - padX - 60, pageHeight - 22, {
8153
8463
  width: 60,
8154
8464
  align: "right"
8155
8465
  });
@@ -8158,18 +8468,19 @@ var PdfRenderer = class {
8158
8468
  switch (element.type) {
8159
8469
  case "hero": {
8160
8470
  const hero = element;
8471
+ const align = hero.align || "center";
8161
8472
  let curY = y;
8162
8473
  if (hero.tagline) {
8163
8474
  doc.font("Helvetica-Bold").fontSize(11).fillColor(theme.colors.primary);
8164
- doc.text(this.stripFormatting(hero.tagline).toUpperCase(), x, curY, { width });
8475
+ doc.text(this.stripFormatting(hero.tagline).toUpperCase(), x, curY, { width, align });
8165
8476
  curY += 20;
8166
8477
  }
8167
8478
  doc.font("Helvetica-Bold").fontSize(34).fillColor(theme.colors.text);
8168
- doc.text(this.stripFormatting(hero.title), x, curY, { width, lineGap: 6 });
8479
+ doc.text(this.stripFormatting(hero.title), x, curY, { width, lineGap: 6, align });
8169
8480
  curY += doc.heightOfString(this.stripFormatting(hero.title), { width }) + 8;
8170
8481
  if (hero.subtitle) {
8171
8482
  doc.font("Helvetica").fontSize(16).fillColor(theme.colors.muted || "#888888");
8172
- doc.text(this.stripFormatting(hero.subtitle), x, curY, { width, lineGap: 4 });
8483
+ doc.text(this.stripFormatting(hero.subtitle), x, curY, { width, lineGap: 4, align });
8173
8484
  curY += doc.heightOfString(this.stripFormatting(hero.subtitle), { width }) + 12;
8174
8485
  }
8175
8486
  if (hero.elements) {
@@ -8290,20 +8601,24 @@ var PdfRenderer = class {
8290
8601
  const card = element;
8291
8602
  const variantColor = this.getVariantColor(card.variant, theme);
8292
8603
  const cardPad = 14;
8293
- let cardCursorY = y + cardPad;
8604
+ let estHeight = cardPad * 2;
8294
8605
  if (card.title) {
8295
- doc.font("Helvetica-Bold").fontSize(15).fillColor(variantColor).text(this.stripFormatting(card.title), x + cardPad, cardCursorY, {
8296
- width: width - cardPad * 2
8297
- });
8298
- cardCursorY += 22;
8606
+ doc.font("Helvetica-Bold").fontSize(15);
8607
+ estHeight += doc.heightOfString(this.stripFormatting(card.title), { width: width - cardPad * 2 }) + 8;
8299
8608
  }
8300
8609
  if (card.elements) {
8301
8610
  for (const child of card.elements) {
8302
- cardCursorY = this.renderElement(doc, child, x + cardPad, cardCursorY, width - cardPad * 2, theme);
8303
- cardCursorY += 8;
8611
+ if (child.type === "metric")
8612
+ estHeight += 95;
8613
+ else if (child.type === "paragraph") {
8614
+ doc.font("Helvetica").fontSize(12);
8615
+ estHeight += doc.heightOfString(this.stripFormatting(child.text), { width: width - cardPad * 2 }) + 8;
8616
+ } else {
8617
+ estHeight += 45;
8618
+ }
8304
8619
  }
8305
8620
  }
8306
- const totalCardHeight = Math.max(70, cardCursorY - y + cardPad);
8621
+ const totalCardHeight = Math.max(80, estHeight);
8307
8622
  doc.save();
8308
8623
  doc.roundedRect(x, y, width, totalCardHeight, 8).fill(theme.colors.surface || "rgba(255,255,255,0.06)");
8309
8624
  doc.roundedRect(x, y, width, totalCardHeight, 8).lineWidth(1.5).strokeColor(variantColor).stroke();
@@ -8313,12 +8628,11 @@ var PdfRenderer = class {
8313
8628
  doc.font("Helvetica-Bold").fontSize(15).fillColor(variantColor).text(this.stripFormatting(card.title), x + cardPad, renderTop, {
8314
8629
  width: width - cardPad * 2
8315
8630
  });
8316
- renderTop += 22;
8631
+ renderTop += doc.heightOfString(this.stripFormatting(card.title), { width: width - cardPad * 2 }) + 8;
8317
8632
  }
8318
8633
  if (card.elements) {
8319
8634
  for (const child of card.elements) {
8320
- renderTop = this.renderElement(doc, child, x + cardPad, renderTop, width - cardPad * 2, theme);
8321
- renderTop += 8;
8635
+ renderTop = this.renderElement(doc, child, x + cardPad, renderTop, width - cardPad * 2, theme, presentation) + 6;
8322
8636
  }
8323
8637
  }
8324
8638
  return y + totalCardHeight;
@@ -8326,15 +8640,15 @@ var PdfRenderer = class {
8326
8640
  case "metric": {
8327
8641
  const m = element;
8328
8642
  const variantColor = this.getVariantColor(m.variant, theme);
8329
- const boxHeight = 90;
8643
+ const boxHeight = 85;
8330
8644
  doc.roundedRect(x, y, width, boxHeight, 8).fill(theme.colors.surface || "rgba(255,255,255,0.06)");
8331
8645
  doc.roundedRect(x, y, width, boxHeight, 8).lineWidth(1.5).strokeColor(variantColor).stroke();
8332
- doc.font("Helvetica-Bold").fontSize(11).fillColor(theme.colors.muted || "#888888").text(m.label.toUpperCase(), x + 16, y + 14, { width: width - 32 });
8646
+ doc.font("Helvetica-Bold").fontSize(10).fillColor(theme.colors.muted || "#888888").text(m.label.toUpperCase(), x + 14, y + 12, { width: width - 28 });
8333
8647
  const displayVal = m.unit ? `${m.value} ${m.unit}` : m.value;
8334
- doc.font("Helvetica-Bold").fontSize(28).fillColor(theme.colors.primary).text(displayVal, x + 16, y + 32, { width: width - 32 });
8648
+ doc.font("Helvetica-Bold").fontSize(26).fillColor(variantColor).text(displayVal, x + 14, y + 28, { width: width - 28 });
8335
8649
  if (m.change) {
8336
8650
  const changeColor = m.change.startsWith("+") ? "#10b981" : "#ef4444";
8337
- doc.font("Helvetica-Bold").fontSize(12).fillColor(changeColor).text(m.change, x + width - 90, y + 38, { width: 74, align: "right" });
8651
+ doc.font("Helvetica-Bold").fontSize(11).fillColor(changeColor).text(m.change, x + width - 85, y + 34, { width: 70, align: "right" });
8338
8652
  }
8339
8653
  return y + boxHeight;
8340
8654
  }
@@ -8357,7 +8671,7 @@ var PdfRenderer = class {
8357
8671
  const colWidth = ratios[i] / totalRatio * availableWidth;
8358
8672
  let colCursorY = y;
8359
8673
  for (const child of col.elements) {
8360
- colCursorY = this.renderElement(doc, child, curX, colCursorY, colWidth, theme);
8674
+ colCursorY = this.renderElement(doc, child, curX, colCursorY, colWidth, theme, presentation);
8361
8675
  colCursorY += 8;
8362
8676
  }
8363
8677
  if (colCursorY > maxY)
@@ -8448,27 +8762,65 @@ var PdfRenderer = class {
8448
8762
  }
8449
8763
  case "compare": {
8450
8764
  const c = element;
8451
- const colW = (width - 24) / 2;
8452
- let leftY = y + 12;
8453
- let rightY = y + 12;
8765
+ const colW = (width - 32) / 2;
8766
+ const pad = 14;
8767
+ let leftContentH = 0;
8768
+ if (c.leftTitle)
8769
+ leftContentH += 24;
8770
+ for (const el of c.left) {
8771
+ if (el.type === "metric")
8772
+ leftContentH += 95;
8773
+ else if (el.type === "paragraph") {
8774
+ doc.font("Helvetica").fontSize(12);
8775
+ leftContentH += doc.heightOfString(this.stripFormatting(el.text), { width: colW - pad * 2 }) + 8;
8776
+ } else if (el.type === "badge")
8777
+ leftContentH += 32;
8778
+ else
8779
+ leftContentH += 45;
8780
+ }
8781
+ let rightContentH = 0;
8782
+ if (c.rightTitle)
8783
+ rightContentH += 24;
8784
+ for (const el of c.right) {
8785
+ if (el.type === "metric")
8786
+ rightContentH += 95;
8787
+ else if (el.type === "paragraph") {
8788
+ doc.font("Helvetica").fontSize(12);
8789
+ rightContentH += doc.heightOfString(this.stripFormatting(el.text), { width: colW - pad * 2 }) + 8;
8790
+ } else if (el.type === "badge")
8791
+ rightContentH += 32;
8792
+ else
8793
+ rightContentH += 45;
8794
+ }
8795
+ const totalH = Math.max(leftContentH, rightContentH, 90) + pad * 2;
8796
+ const rightX = x + colW + 32;
8797
+ doc.save();
8798
+ doc.roundedRect(x, y, colW, totalH, 8).fill(theme.colors.surface || "rgba(255,255,255,0.06)");
8799
+ doc.roundedRect(x, y, colW, totalH, 8).lineWidth(1.5).strokeColor(theme.colors.border || "rgba(255,255,255,0.15)").stroke();
8800
+ doc.roundedRect(rightX, y, colW, totalH, 8).fill(theme.colors.surface || "rgba(255,255,255,0.06)");
8801
+ doc.roundedRect(rightX, y, colW, totalH, 8).lineWidth(1.5).strokeColor(theme.colors.border || "rgba(255,255,255,0.15)").stroke();
8802
+ const vsX = x + colW + 4;
8803
+ const vsY = y + totalH / 2 - 12;
8804
+ doc.roundedRect(vsX, vsY, 24, 24, 12).fill(theme.colors.surface || "#151522");
8805
+ doc.roundedRect(vsX, vsY, 24, 24, 12).lineWidth(1).strokeColor(theme.colors.border || "rgba(255,255,255,0.2)").stroke();
8806
+ doc.font("Helvetica-Bold").fontSize(9).fillColor(theme.colors.muted || "#888888").text("VS", vsX, vsY + 7, { width: 24, align: "center" });
8807
+ doc.restore();
8808
+ let leftY = y + pad;
8454
8809
  if (c.leftTitle) {
8455
- doc.font("Helvetica-Bold").fontSize(14).fillColor(theme.colors.primary).text(this.stripFormatting(c.leftTitle), x + 12, leftY, { width: colW - 24 });
8456
- leftY += 22;
8810
+ doc.font("Helvetica-Bold").fontSize(14).fillColor(theme.colors.primary).text(this.stripFormatting(c.leftTitle), x + pad, leftY, { width: colW - pad * 2 });
8811
+ leftY += 24;
8457
8812
  }
8458
8813
  for (const el of c.left) {
8459
- leftY = this.renderElement(doc, el, x + 12, leftY, colW - 24, theme) + 6;
8814
+ leftY = this.renderElement(doc, el, x + pad, leftY, colW - pad * 2, theme, presentation) + 6;
8460
8815
  }
8461
- const rightX = x + colW + 24;
8816
+ let rightY = y + pad;
8462
8817
  if (c.rightTitle) {
8463
- doc.font("Helvetica-Bold").fontSize(14).fillColor(theme.colors.primary).text(this.stripFormatting(c.rightTitle), rightX + 12, rightY, { width: colW - 24 });
8464
- rightY += 22;
8818
+ doc.font("Helvetica-Bold").fontSize(14).fillColor(theme.colors.primary).text(this.stripFormatting(c.rightTitle), rightX + pad, rightY, { width: colW - pad * 2 });
8819
+ rightY += 24;
8465
8820
  }
8466
8821
  for (const el of c.right) {
8467
- rightY = this.renderElement(doc, el, rightX + 12, rightY, colW - 24, theme) + 6;
8822
+ rightY = this.renderElement(doc, el, rightX + pad, rightY, colW - pad * 2, theme, presentation) + 6;
8468
8823
  }
8469
- const totalH = Math.max(leftY - y, rightY - y, 60) + 12;
8470
- doc.roundedRect(x, y, colW, totalH, 8).strokeColor(theme.colors.border || "rgba(255,255,255,0.15)").stroke();
8471
- doc.roundedRect(rightX, y, colW, totalH, 8).strokeColor(theme.colors.border || "rgba(255,255,255,0.15)").stroke();
8472
8824
  return y + totalH + 8;
8473
8825
  }
8474
8826
  case "chart": {
@@ -8705,22 +9057,30 @@ var PdfRenderer = class {
8705
9057
  titleOffset = 24;
8706
9058
  }
8707
9059
  const startY = y + titleOffset;
9060
+ const maxLaneHeight = isLR ? maxLane * (nodeH + gapY) - gapY : numRanks * (nodeH + gapY) - gapY;
9061
+ const maxRankWidth = isLR ? numRanks * (nodeW + gapX) - gapX : maxLane * (nodeW + gapX) - gapX;
8708
9062
  const positions = {};
8709
9063
  sortedRanks.forEach((r, rIdx) => {
8710
9064
  const ids = rankGroups[r];
8711
- ids.forEach((id, lIdx) => {
8712
- if (isLR) {
9065
+ if (isLR) {
9066
+ const colHeight = ids.length * (nodeH + gapY) - gapY;
9067
+ const offsetY = (maxLaneHeight - colHeight) / 2;
9068
+ ids.forEach((id, lIdx) => {
8713
9069
  positions[id] = {
8714
9070
  x: x + 20 + rIdx * (nodeW + gapX),
8715
- y: startY + 10 + lIdx * (nodeH + gapY)
9071
+ y: startY + 10 + offsetY + lIdx * (nodeH + gapY)
8716
9072
  };
8717
- } else {
9073
+ });
9074
+ } else {
9075
+ const rowWidth = ids.length * (nodeW + gapX) - gapX;
9076
+ const offsetX = (maxRankWidth - rowWidth) / 2;
9077
+ ids.forEach((id, lIdx) => {
8718
9078
  positions[id] = {
8719
- x: x + 20 + lIdx * (nodeW + gapX),
9079
+ x: x + 20 + offsetX + lIdx * (nodeW + gapX),
8720
9080
  y: startY + 10 + rIdx * (nodeH + gapY)
8721
9081
  };
8722
- }
8723
- });
9082
+ });
9083
+ }
8724
9084
  });
8725
9085
  const arrowColor = theme.colors.accent || theme.colors.primary;
8726
9086
  diagram.edges.forEach((e) => {
@@ -8768,7 +9128,7 @@ var PdfRenderer = class {
8768
9128
  doc.roundedRect(p.x, p.y, nodeW, nodeH, 6).fill(theme.colors.surface || "#151522");
8769
9129
  doc.roundedRect(p.x, p.y, nodeW, nodeH, 6).lineWidth(1.5).strokeColor(nodeColor).stroke();
8770
9130
  doc.restore();
8771
- doc.font("Helvetica-Bold").fontSize(10).fillColor("#ffffff").text(this.stripFormatting(n.label), p.x + 4, p.y + nodeH / 2 - 5, {
9131
+ doc.font("Helvetica-Bold").fontSize(10).fillColor(theme.colors.text).text(this.stripFormatting(n.label), p.x + 4, p.y + nodeH / 2 - 5, {
8772
9132
  width: nodeW - 8,
8773
9133
  align: "center"
8774
9134
  });
@@ -8910,7 +9270,7 @@ function startDevServer(filePath, options = {}) {
8910
9270
  // src/cli.ts
8911
9271
  import { mkdirSync, readFileSync as readFileSync3, watch as fsWatch, writeFileSync } from "fs";
8912
9272
  import { basename, dirname, extname, join, resolve as resolve2 } from "path";
8913
- var VERSION = "0.1.25";
9273
+ var VERSION = "0.1.27";
8914
9274
  function printHelp() {
8915
9275
  return `
8916
9276
  YumiaMD \u2014 Markdown-based presentation compiler (v${VERSION})
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.25";
12
+ declare const VERSION = "0.1.27";
13
13
  declare function printHelp(): string;
14
14
  declare function runCli(argv: string[]): Promise<{
15
15
  exitCode: number;
package/dist/index.js CHANGED
@@ -73,7 +73,7 @@ import {
73
73
  terminalTheme,
74
74
  tokyoNightTheme,
75
75
  validate
76
- } from "./chunk-PRZ5CFRH.js";
76
+ } from "./chunk-FTPWLNJL.js";
77
77
  export {
78
78
  DEFAULT_VIEWPORT,
79
79
  DefaultLayoutEngine,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yumiamd",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
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/ast": "0.1.25",
25
- "@yumiamd/parser": "0.1.25",
26
- "@yumiamd/layout": "0.1.25",
27
- "@yumiamd/renderer": "0.1.25",
28
- "@yumiamd/core": "0.1.25",
29
- "@yumiamd/renderer-html": "0.1.25",
30
- "@yumiamd/renderer-pdf": "0.1.25",
31
- "@yumiamd/renderer-pptx": "0.1.25",
32
- "@yumiamd/theme": "0.1.25"
24
+ "@yumiamd/ast": "0.1.27",
25
+ "@yumiamd/layout": "0.1.27",
26
+ "@yumiamd/core": "0.1.27",
27
+ "@yumiamd/parser": "0.1.27",
28
+ "@yumiamd/renderer": "0.1.27",
29
+ "@yumiamd/renderer-html": "0.1.27",
30
+ "@yumiamd/renderer-pptx": "0.1.27",
31
+ "@yumiamd/theme": "0.1.27",
32
+ "@yumiamd/renderer-pdf": "0.1.27"
33
33
  },
34
34
  "keywords": [
35
35
  "yumia",