yumiamd 0.1.30 → 0.1.32

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-MXMPDHHS.js";
4
+ } from "./chunk-Y2OWK5OP.js";
5
5
 
6
6
  // src/bin.ts
7
7
  var result = await runCli(process.argv);
@@ -2193,7 +2193,7 @@ var NativeYumiaParser = class {
2193
2193
  }
2194
2194
  } else if (dLine.startsWith("node ")) {
2195
2195
  const nParts = dLine.slice(5).trim();
2196
- const nIdMatch = nParts.match(/^(\S+)/);
2196
+ const nIdMatch = nParts.match(/^(\[[^\]]+\]|\([^)]+\)|\{[^}]+\}|[a-zA-Z0-9_-]+(?:\[\([^)]+\)\]|\(\([^)]+\)\)|\[[^\]]+\]|\{[^}]+\})?)/);
2197
2197
  const nLabelMatch = nParts.match(/\blabel=["']([^"']+)["']/);
2198
2198
  const nShapeMatch = nParts.match(/\bshape=["']?([^"'\s]+)["']?/);
2199
2199
  const nVarMatch = nParts.match(/\bvariant=["']?([^"'\s]+)["']?/);
@@ -2202,6 +2202,7 @@ var NativeYumiaParser = class {
2202
2202
  const explicitLabel = nLabelMatch ? nLabelMatch[1] : void 0;
2203
2203
  const node = ensureNode(nId, nShapeMatch ? nShapeMatch[1] : void 0, nVarMatch ? nVarMatch[1] : void 0, explicitLabel);
2204
2204
  nodeMap.set(nId.toLowerCase(), node);
2205
+ nodeMap.set(node.id, node);
2205
2206
  }
2206
2207
  }
2207
2208
  nextIdx++;
@@ -2753,9 +2754,17 @@ function computeDiagramLayout(diagram, availableWidth, isLR = (diagram.direction
2753
2754
  };
2754
2755
  if (nodeIds.length === 0)
2755
2756
  return empty;
2757
+ const connected = /* @__PURE__ */ new Set();
2758
+ diagram.edges.forEach((e) => {
2759
+ connected.add(e.from);
2760
+ connected.add(e.to);
2761
+ });
2762
+ const activeIds = connected.size > 0 ? nodeIds.filter((id) => connected.has(id)) : nodeIds;
2763
+ if (activeIds.length === 0)
2764
+ return empty;
2756
2765
  const inDegree = {};
2757
2766
  const adj = {};
2758
- nodeIds.forEach((id) => {
2767
+ activeIds.forEach((id) => {
2759
2768
  inDegree[id] = 0;
2760
2769
  adj[id] = [];
2761
2770
  });
@@ -2769,20 +2778,20 @@ function computeDiagramLayout(diagram, availableWidth, isLR = (diagram.direction
2769
2778
  });
2770
2779
  const ranks = {};
2771
2780
  const queue = [];
2772
- const visitBudget = Math.max(4, nodeIds.length * 2);
2781
+ const visitBudget = Math.max(4, activeIds.length * 2);
2773
2782
  const visits = {};
2774
- nodeIds.forEach((id) => {
2783
+ activeIds.forEach((id) => {
2775
2784
  if (inDegree[id] === 0) {
2776
2785
  ranks[id] = 0;
2777
2786
  queue.push(id);
2778
2787
  }
2779
2788
  });
2780
2789
  if (queue.length === 0) {
2781
- ranks[nodeIds[0]] = 0;
2782
- queue.push(nodeIds[0]);
2790
+ ranks[activeIds[0]] = 0;
2791
+ queue.push(activeIds[0]);
2783
2792
  }
2784
2793
  let steps = 0;
2785
- const maxSteps = nodeIds.length * nodeIds.length + 8;
2794
+ const maxSteps = activeIds.length * activeIds.length + 8;
2786
2795
  while (queue.length > 0 && steps < maxSteps) {
2787
2796
  steps++;
2788
2797
  const u = queue.shift();
@@ -2791,7 +2800,7 @@ function computeDiagramLayout(diagram, availableWidth, isLR = (diagram.direction
2791
2800
  continue;
2792
2801
  const r = ranks[u] ?? 0;
2793
2802
  for (const v of adj[u] || []) {
2794
- const nextR = Math.min(nodeIds.length - 1, r + 1);
2803
+ const nextR = Math.min(activeIds.length - 1, r + 1);
2795
2804
  if (ranks[v] === void 0 || ranks[v] < nextR) {
2796
2805
  ranks[v] = nextR;
2797
2806
  if ((visits[v] || 0) <= visitBudget) {
@@ -2800,13 +2809,13 @@ function computeDiagramLayout(diagram, availableWidth, isLR = (diagram.direction
2800
2809
  }
2801
2810
  }
2802
2811
  }
2803
- nodeIds.forEach((id, idx) => {
2812
+ activeIds.forEach((id, idx) => {
2804
2813
  if (ranks[id] === void 0) {
2805
- ranks[id] = Math.min(idx, nodeIds.length - 1);
2814
+ ranks[id] = Math.min(idx, activeIds.length - 1);
2806
2815
  }
2807
2816
  });
2808
2817
  const rankGroups = {};
2809
- nodeIds.forEach((id) => {
2818
+ activeIds.forEach((id) => {
2810
2819
  const r = ranks[id] ?? 0;
2811
2820
  if (!rankGroups[r])
2812
2821
  rankGroups[r] = [];
@@ -2819,13 +2828,14 @@ function computeDiagramLayout(diagram, availableWidth, isLR = (diagram.direction
2819
2828
  maxLane = Math.max(maxLane, rankGroups[r].length);
2820
2829
  });
2821
2830
  const titleOffset = diagram.title ? options.titleHeight ?? 28 : 0;
2822
- const gapX = isLR ? 28 : 20;
2823
- const gapY = isLR ? 18 : 28;
2824
- const nodeWidth = isLR ? Math.max(72, Math.min(140, (availableWidth - 40 - gapX * (numRanks - 1)) / numRanks)) : Math.max(80, Math.min(150, (availableWidth - 40 - gapX * (maxLane - 1)) / Math.max(1, maxLane)));
2825
- const longestLabel = Math.max(1, ...diagram.nodes.map((n) => (n.label || n.id).length));
2826
- const charsPerLine = Math.max(8, Math.floor((nodeWidth - 12) / 6.2));
2831
+ const gapX = isLR ? 36 : 24;
2832
+ const gapY = isLR ? 22 : 32;
2833
+ const nodeWidth = isLR ? Math.max(100, Math.min(260, (availableWidth - 48 - gapX * (numRanks - 1)) / numRanks)) : Math.max(100, Math.min(220, (availableWidth - 48 - gapX * (maxLane - 1)) / Math.max(1, maxLane)));
2834
+ const activeNodes = diagram.nodes.filter((n) => activeIds.includes(n.id));
2835
+ const longestLabel = Math.max(1, ...activeNodes.map((n) => (n.label || n.id).length));
2836
+ const charsPerLine = Math.max(8, Math.floor((nodeWidth - 16) / 6.5));
2827
2837
  const labelLines = Math.max(1, Math.ceil(longestLabel / charsPerLine));
2828
- const nodeHeight = Math.max(36, Math.min(72, 18 + labelLines * 14));
2838
+ const nodeHeight = Math.max(40, Math.min(84, 20 + labelLines * 16));
2829
2839
  const contentH = isLR ? maxLane * (nodeHeight + gapY) - gapY : numRanks * (nodeHeight + gapY) - gapY;
2830
2840
  const height = titleOffset + 24 + contentH + 16;
2831
2841
  const originX = options.originX ?? 0;
@@ -2872,6 +2882,28 @@ function computeDiagramLayout(diagram, availableWidth, isLR = (diagram.direction
2872
2882
  positions
2873
2883
  };
2874
2884
  }
2885
+ function orthogonalEdgePoints(isLR, p1, p2, nodeW, nodeH) {
2886
+ const x1 = isLR ? p1.x + nodeW : p1.x + nodeW / 2;
2887
+ const y1 = isLR ? p1.y + nodeH / 2 : p1.y + nodeH;
2888
+ const x2 = isLR ? p2.x : p2.x + nodeW / 2;
2889
+ const y2 = isLR ? p2.y + nodeH / 2 : p2.y;
2890
+ if (isLR) {
2891
+ const midX = (x1 + x2) / 2;
2892
+ return [
2893
+ { x: x1, y: y1 },
2894
+ { x: midX, y: y1 },
2895
+ { x: midX, y: y2 },
2896
+ { x: x2, y: y2 }
2897
+ ];
2898
+ }
2899
+ const midY = (y1 + y2) / 2;
2900
+ return [
2901
+ { x: x1, y: y1 },
2902
+ { x: x1, y: midY },
2903
+ { x: x2, y: midY },
2904
+ { x: x2, y: y2 }
2905
+ ];
2906
+ }
2875
2907
  function fitDiagramLabel(label, maxChars) {
2876
2908
  const clean = label.replace(/\s+/g, " ").trim();
2877
2909
  if (clean.length <= maxChars)
@@ -2934,18 +2966,19 @@ var DefaultLayoutEngine = class {
2934
2966
  layoutElementList(elements, startX, startY, availableWidth, gap) {
2935
2967
  let currentY = startY;
2936
2968
  const nodes = [];
2969
+ const compactHero = elements.length > 1 && elements.some((el) => el.type === "hero");
2937
2970
  for (const element of elements) {
2938
- const node = this.layoutSingleElement(element, startX, currentY, availableWidth, gap);
2971
+ const node = this.layoutSingleElement(element, startX, currentY, availableWidth, gap, compactHero);
2939
2972
  nodes.push(node);
2940
2973
  currentY += node.bounds.height + gap;
2941
2974
  }
2942
2975
  const totalHeight = elements.length > 0 ? currentY - startY - gap : 0;
2943
2976
  return { nodes, totalHeight };
2944
2977
  }
2945
- layoutSingleElement(element, x, y, width, gap) {
2978
+ layoutSingleElement(element, x, y, width, gap, compactHero = false) {
2946
2979
  switch (element.type) {
2947
2980
  case "hero": {
2948
- return this.layoutHero(element, x, y, width, gap);
2981
+ return this.layoutHero(element, x, y, width, gap, compactHero);
2949
2982
  }
2950
2983
  case "heading": {
2951
2984
  const height = this.estimateHeadingHeight(element, width);
@@ -2960,7 +2993,7 @@ var DefaultLayoutEngine = class {
2960
2993
  return { element, bounds: { x, y, width, height } };
2961
2994
  }
2962
2995
  case "code": {
2963
- const height = this.estimateCodeHeight(element);
2996
+ const height = this.estimateCodeHeight(element, width);
2964
2997
  return { element, bounds: { x, y, width, height } };
2965
2998
  }
2966
2999
  case "quote": {
@@ -2982,11 +3015,11 @@ var DefaultLayoutEngine = class {
2982
3015
  case "callout": {
2983
3016
  const c = element;
2984
3017
  const lines = Math.ceil(c.text.length / Math.max(15, Math.floor(width / 14))) || 1;
2985
- const height = Math.max(80, lines * 28 + (c.title ? 45 : 0) + 30);
3018
+ const height = Math.max(100, lines * 30 + (c.title ? 52 : 0) + 40);
2986
3019
  return { element, bounds: { x, y, width, height } };
2987
3020
  }
2988
3021
  case "badge": {
2989
- return { element, bounds: { x, y, width, height: 45 } };
3022
+ return { element, bounds: { x, y, width, height: 52 } };
2990
3023
  }
2991
3024
  case "math": {
2992
3025
  const height = 90;
@@ -3061,47 +3094,61 @@ var DefaultLayoutEngine = class {
3061
3094
  }
3062
3095
  }
3063
3096
  }
3064
- layoutHero(element, x, y, width, gap) {
3097
+ layoutHero(element, x, y, width, gap, compact = false) {
3065
3098
  let curY = y;
3066
3099
  if (element.badge || element.tagline) {
3067
- curY += 52;
3100
+ curY += compact ? 44 : 56;
3068
3101
  }
3069
- const charsPerLineTitle = Math.max(12, Math.floor(width / 28));
3102
+ const charsPerLineTitle = Math.max(10, Math.floor(width / (compact ? 36 : 30)));
3070
3103
  const titleLines = Math.max(1, Math.ceil(element.title.length / charsPerLineTitle));
3071
- const titleHeight = Math.max(100, titleLines * 72 + 24);
3104
+ const titleHeight = compact ? Math.max(72, titleLines * 52 + 16) : Math.max(110, titleLines * 76 + 28);
3072
3105
  curY += titleHeight;
3073
3106
  if (element.subtitle) {
3074
- const charsPerLineSub = Math.max(20, Math.floor(width / 18));
3107
+ const charsPerLineSub = Math.max(18, Math.floor(width / (compact ? 22 : 18)));
3075
3108
  const subLines = Math.max(1, Math.ceil(element.subtitle.length / charsPerLineSub));
3076
- const subHeight = Math.max(56, subLines * 36 + 16);
3109
+ const subHeight = compact ? Math.max(44, subLines * 34 + 12) : Math.max(64, subLines * 40 + 18);
3077
3110
  curY += subHeight;
3078
3111
  }
3079
- curY += 16;
3112
+ curY += compact ? 14 : 20;
3080
3113
  const children = [];
3081
3114
  if (element.elements) {
3082
3115
  for (const child of element.elements) {
3083
- const node = this.layoutSingleElement(child, x, curY, width, gap);
3116
+ const node = this.layoutSingleElement(child, x, curY, width, gap, false);
3084
3117
  children.push(node);
3085
3118
  curY += node.bounds.height + gap;
3086
3119
  }
3087
3120
  }
3088
- return { element, bounds: { x, y, width, height: Math.max(curY - y, 160) }, children };
3121
+ return {
3122
+ element,
3123
+ bounds: { x, y, width, height: Math.max(curY - y, compact ? 120 : 160) },
3124
+ children
3125
+ };
3089
3126
  }
3090
3127
  layoutGrid(element, x, y, width, gap) {
3091
3128
  const colCount = typeof element.columns === "number" ? element.columns : parseInt(String(element.columns), 10) || 2;
3092
- const totalGap = gap * (colCount - 1);
3129
+ const rawGap = element.gap !== void 0 ? Number(element.gap) : gap;
3130
+ const gridGap = Number.isFinite(rawGap) && rawGap >= 0 ? rawGap : gap;
3131
+ const totalGap = gridGap * (colCount - 1);
3093
3132
  const colWidth = Math.max(10, (width - totalGap) / colCount);
3094
3133
  const children = [];
3095
3134
  const colYs = Array(colCount).fill(y);
3096
3135
  for (let i = 0; i < element.elements.length; i++) {
3097
3136
  const colIdx = i % colCount;
3098
- const colX = x + colIdx * (colWidth + gap);
3137
+ const colX = x + colIdx * (colWidth + gridGap);
3099
3138
  const childEl = element.elements[i];
3100
3139
  const childNode = this.layoutSingleElement(childEl, colX, colYs[colIdx], colWidth, gap);
3101
3140
  children.push(childNode);
3102
- colYs[colIdx] += childNode.bounds.height + gap;
3141
+ colYs[colIdx] += childNode.bounds.height + gridGap;
3142
+ }
3143
+ const rowCount = Math.ceil(children.length / colCount);
3144
+ for (let row = 0; row < rowCount; row++) {
3145
+ const rowNodes = children.slice(row * colCount, row * colCount + colCount);
3146
+ const rowMax = Math.max(...rowNodes.map((n) => n.bounds.height), 0);
3147
+ for (const node of rowNodes) {
3148
+ node.bounds.height = rowMax;
3149
+ }
3103
3150
  }
3104
- const maxGridHeight = Math.max(...colYs.map((cy) => cy - y), 100);
3151
+ const maxGridHeight = Math.max(...children.map((n) => n.bounds.y + n.bounds.height - y), 100);
3105
3152
  return {
3106
3153
  element,
3107
3154
  bounds: { x, y, width, height: maxGridHeight },
@@ -3109,12 +3156,13 @@ var DefaultLayoutEngine = class {
3109
3156
  };
3110
3157
  }
3111
3158
  layoutCompare(element, x, y, width, gap) {
3112
- const colW = (width - gap) / 2;
3113
- const titleH = 45;
3114
- const pad = 24;
3159
+ const vsGap = Math.max(gap, 56);
3160
+ const colW = (width - vsGap) / 2;
3161
+ const titleH = 56;
3162
+ const pad = 28;
3115
3163
  const leftStartY = y + pad + (element.leftTitle ? titleH : 0);
3116
3164
  const { nodes: leftChildren, totalHeight: leftInnerH } = this.layoutElementList(element.left, x + pad, leftStartY, colW - pad * 2, gap / 2);
3117
- const rightX = x + colW + gap;
3165
+ const rightX = x + colW + vsGap;
3118
3166
  const rightStartY = y + pad + (element.rightTitle ? titleH : 0);
3119
3167
  const { nodes: rightChildren, totalHeight: rightInnerH } = this.layoutElementList(element.right, rightX + pad, rightStartY, colW - pad * 2, gap / 2);
3120
3168
  const totalHeight = Math.max(leftInnerH, rightInnerH, 120) + (element.leftTitle || element.rightTitle ? titleH : 0) + pad * 2;
@@ -3144,11 +3192,12 @@ var DefaultLayoutEngine = class {
3144
3192
  }
3145
3193
  }
3146
3194
  layoutCard(element, x, y, width, gap) {
3147
- const cardPadding = 32;
3195
+ const cardPadding = 28;
3148
3196
  const innerWidth = Math.max(10, width - cardPadding * 2);
3149
- const titleHeight = element.title ? 60 : 0;
3197
+ const titleHeight = element.title ? 56 : 0;
3150
3198
  const innerStartY = y + cardPadding + titleHeight;
3151
- const { nodes: children, totalHeight: innerHeight } = this.layoutElementList(element.elements, x + cardPadding, innerStartY, innerWidth, gap);
3199
+ const innerGap = Math.min(12, Math.max(6, Math.round(gap * 0.4)));
3200
+ const { nodes: children, totalHeight: innerHeight } = this.layoutElementList(element.elements, x + cardPadding, innerStartY, innerWidth, innerGap);
3152
3201
  const cardHeight = titleHeight + innerHeight + cardPadding * 2 + 12;
3153
3202
  const bounds = { x, y, width, height: Math.max(120, cardHeight) };
3154
3203
  return {
@@ -3200,30 +3249,36 @@ var DefaultLayoutEngine = class {
3200
3249
  }
3201
3250
  estimateHeadingHeight(heading, width = 1600) {
3202
3251
  const fontSize = heading.level === 1 ? 46 : heading.level === 2 ? 36 : 28;
3203
- const charWidth = fontSize * 0.55;
3204
- const charsPerLine = Math.max(15, Math.floor(width / charWidth));
3252
+ const charWidth = fontSize * 0.62;
3253
+ const charsPerLine = Math.max(12, Math.floor(width / charWidth));
3205
3254
  const lines = Math.ceil(heading.text.length / charsPerLine) || 1;
3206
- const lineHeight = fontSize * 1.25;
3207
- return Math.max(heading.level === 1 ? 90 : 70, Math.round(lines * lineHeight + 20));
3255
+ const lineHeight = fontSize * 1.4;
3256
+ const minH = heading.level === 1 ? 110 : heading.level === 2 ? 88 : 72;
3257
+ return Math.max(minH, Math.round(lines * lineHeight + 28));
3208
3258
  }
3209
3259
  estimateParagraphHeight(paragraph, width) {
3210
- const charsPerLine = Math.max(20, Math.floor(width / 14));
3260
+ const charsPerLine = Math.max(18, Math.floor(width / 9.5));
3211
3261
  const lines = Math.ceil(paragraph.text.length / charsPerLine) || 1;
3212
- return Math.max(40, lines * 36);
3262
+ return Math.max(36, lines * 34 + 8);
3213
3263
  }
3214
3264
  estimateListHeight(list, width = 800) {
3215
- const charsPerLine = Math.max(15, Math.floor(width / 13));
3265
+ const charsPerLine = Math.max(18, Math.floor(width / 9.5));
3216
3266
  let totalHeight = 0;
3217
3267
  for (const item of list.items) {
3218
3268
  const cleanLen = item.text.replace(/\*\*/g, "").replace(/\*/g, "").length;
3219
3269
  const lines = Math.ceil(cleanLen / charsPerLine) || 1;
3220
- totalHeight += lines * 34 + 18;
3270
+ totalHeight += lines * 32 + 10;
3221
3271
  }
3222
3272
  return Math.max(40, totalHeight);
3223
3273
  }
3224
- estimateCodeHeight(code) {
3225
- const lines = code.code.split("\n").length || 1;
3226
- return lines * 28 + 48;
3274
+ estimateCodeHeight(code, width = 800) {
3275
+ const lines = code.code.split("\n");
3276
+ const charsPerLine = Math.max(18, Math.floor((width - 48) / 9.5));
3277
+ let visualLines = 0;
3278
+ for (const line of lines) {
3279
+ visualLines += Math.max(1, Math.ceil((line.length || 1) / charsPerLine));
3280
+ }
3281
+ return Math.max(120, visualLines * 34 + 84);
3227
3282
  }
3228
3283
  estimateQuoteHeight(quote, width) {
3229
3284
  const charsPerLine = Math.max(20, Math.floor(width / 14));
@@ -6234,7 +6289,9 @@ var PptxRenderer = class {
6234
6289
  h: rect.h,
6235
6290
  align: paragraph.align || "left",
6236
6291
  valign: "top",
6237
- margin: 0
6292
+ margin: 0,
6293
+ // Keep glyphs inside the layout box; wrapping is fine, overflow into neighbors is not.
6294
+ shrinkText: rect.h < 0.32
6238
6295
  });
6239
6296
  }
6240
6297
  renderList(pptxSlide, list, rect, theme) {
@@ -6349,7 +6406,8 @@ var PptxRenderer = class {
6349
6406
  x: rect.x,
6350
6407
  y: rect.y,
6351
6408
  w: rect.w,
6352
- h: rect.h
6409
+ h: rect.h,
6410
+ sizing: { type: "contain", w: rect.w, h: rect.h }
6353
6411
  });
6354
6412
  } catch {
6355
6413
  drawPlaceholder();
@@ -6573,14 +6631,15 @@ var PptxRenderer = class {
6573
6631
  });
6574
6632
  } else {
6575
6633
  pptxSlide.addText(code.code, {
6576
- x: rect.x + 0.2,
6577
- y: rect.y + 0.32,
6578
- w: rect.w - 0.4,
6579
- h: rect.h - 0.4,
6580
- fontSize: 11.5,
6634
+ x: rect.x + 0.22,
6635
+ y: rect.y + 0.36,
6636
+ w: Math.max(0.2, rect.w - 0.44),
6637
+ h: Math.max(0.2, rect.h - 0.48),
6638
+ fontSize: rect.h < 2.2 ? 10 : 11.5,
6581
6639
  fontFace: "Consolas",
6582
6640
  color: textColor,
6583
- valign: "top"
6641
+ valign: "top",
6642
+ margin: 0
6584
6643
  });
6585
6644
  }
6586
6645
  }
@@ -6646,12 +6705,12 @@ var PptxRenderer = class {
6646
6705
  line: { color: sevColor, width: 2 },
6647
6706
  rectRadius: 0.08
6648
6707
  });
6649
- const titleH = callout.title ? 0.35 : 0;
6708
+ const titleH = callout.title ? 0.38 : 0;
6650
6709
  if (callout.title) {
6651
6710
  pptxSlide.addText(callout.title, {
6652
- x: rect.x + 0.15,
6653
- y: rect.y + 0.08,
6654
- w: rect.w - 0.3,
6711
+ x: rect.x + 0.2,
6712
+ y: rect.y + 0.16,
6713
+ w: rect.w - 0.4,
6655
6714
  h: 0.3,
6656
6715
  fontSize: 13,
6657
6716
  bold: true,
@@ -6660,10 +6719,10 @@ var PptxRenderer = class {
6660
6719
  });
6661
6720
  }
6662
6721
  pptxSlide.addText(callout.text, {
6663
- x: rect.x + 0.15,
6664
- y: rect.y + titleH + 0.08,
6665
- w: rect.w - 0.3,
6666
- h: rect.h - titleH - 0.16,
6722
+ x: rect.x + 0.2,
6723
+ y: rect.y + titleH + 0.14,
6724
+ w: rect.w - 0.4,
6725
+ h: Math.max(0.2, rect.h - titleH - 0.28),
6667
6726
  fontSize: 12,
6668
6727
  color: this.cleanHexColor(theme.colors.text),
6669
6728
  fontFace: cleanFontFace(theme.typography.bodyFont),
@@ -6679,15 +6738,18 @@ var PptxRenderer = class {
6679
6738
  h: node.bounds.height * scaleY
6680
6739
  };
6681
6740
  const align = hero.align || "center";
6682
- let curY = rect.y + 0.3;
6683
- if (hero.badge) {
6741
+ const compact = rect.h < 2.4;
6742
+ const maxY = rect.y + rect.h - 0.04;
6743
+ let curY = rect.y + (compact ? 0.08 : 0.24);
6744
+ if (hero.badge && curY < maxY) {
6684
6745
  const badgeW = Math.min(3.2, Math.max(1.4, hero.badge.length * 0.11 + 0.6));
6685
6746
  const badgeX = align === "left" ? rect.x : align === "right" ? rect.x + rect.w - badgeW : rect.x + (rect.w - badgeW) / 2;
6747
+ const badgeH = Math.min(compact ? 0.26 : 0.32, maxY - curY);
6686
6748
  pptxSlide.addShape(pptx.ShapeType.roundRect, {
6687
6749
  x: badgeX,
6688
6750
  y: curY,
6689
6751
  w: badgeW,
6690
- h: 0.32,
6752
+ h: badgeH,
6691
6753
  fill: { color: this.cleanHexColor(theme.colors.surface) },
6692
6754
  line: { color: this.cleanHexColor(theme.colors.primary), width: 1.5 },
6693
6755
  rectRadius: 0.16
@@ -6696,50 +6758,55 @@ var PptxRenderer = class {
6696
6758
  x: badgeX,
6697
6759
  y: curY,
6698
6760
  w: badgeW,
6699
- h: 0.32,
6700
- fontSize: 10,
6761
+ h: badgeH,
6762
+ fontSize: compact ? 9 : 10,
6701
6763
  bold: true,
6702
6764
  color: this.cleanHexColor(theme.colors.primary),
6703
6765
  fontFace: cleanFontFace(theme.typography.headingFont),
6704
6766
  align: "center",
6705
6767
  valign: "middle"
6706
6768
  });
6707
- curY += 0.48;
6708
- } else if (hero.tagline) {
6769
+ curY += compact ? 0.36 : 0.48;
6770
+ } else if (hero.tagline && curY < maxY) {
6771
+ const tagH = Math.min(0.32, maxY - curY);
6709
6772
  pptxSlide.addText(hero.tagline.toUpperCase(), {
6710
6773
  x: rect.x,
6711
6774
  y: curY,
6712
6775
  w: rect.w,
6713
- h: 0.32,
6714
- fontSize: 11,
6776
+ h: tagH,
6777
+ fontSize: compact ? 10 : 11,
6715
6778
  bold: true,
6716
6779
  color: this.cleanHexColor(theme.colors.primary),
6717
6780
  fontFace: cleanFontFace(theme.typography.headingFont),
6718
6781
  align
6719
6782
  });
6720
- curY += 0.42;
6783
+ curY += compact ? 0.34 : 0.42;
6721
6784
  }
6722
- const titleLines = Math.max(1, Math.ceil(hero.title.length / 38));
6723
- const titleH = Math.max(0.7, titleLines * 0.55 + 0.1);
6785
+ const titleLines = Math.max(1, Math.ceil(hero.title.length / (compact ? 48 : 38)));
6786
+ const titleBudget = Math.max(0.24, maxY - curY - (hero.subtitle ? 0.3 : 0.02));
6787
+ const titleH = Math.min(Math.max(compact ? 0.42 : 0.7, titleLines * (compact ? 0.36 : 0.55) + 0.08), titleBudget);
6724
6788
  const displaySize = themeSizeToPptxPoints(theme.typography.sizes?.display, 56);
6725
- const titleFontSize = titleLines > 2 ? displaySize - 10 : titleLines > 1 ? displaySize - 6 : displaySize;
6726
- pptxSlide.addText(hero.title, {
6727
- x: rect.x,
6728
- y: curY,
6729
- w: rect.w,
6730
- h: titleH,
6731
- fontSize: titleFontSize,
6732
- bold: true,
6733
- color: this.cleanHexColor(theme.colors.text),
6734
- fontFace: cleanFontFace(theme.typography.headingFont),
6735
- align
6736
- });
6737
- curY += titleH + 0.08;
6738
- if (hero.subtitle) {
6739
- const subLines = Math.max(1, Math.ceil(hero.subtitle.length / 56));
6740
- const subH = Math.max(0.38, subLines * 0.3 + 0.08);
6789
+ const titleFontSize = compact ? titleLines > 1 ? Math.min(20, displaySize - 16) : Math.min(24, displaySize - 12) : titleLines > 2 ? displaySize - 10 : titleLines > 1 ? displaySize - 6 : displaySize;
6790
+ if (titleH >= 0.24) {
6791
+ pptxSlide.addText(hero.title, {
6792
+ x: rect.x,
6793
+ y: curY,
6794
+ w: rect.w,
6795
+ h: titleH,
6796
+ fontSize: titleFontSize,
6797
+ bold: true,
6798
+ color: this.cleanHexColor(theme.colors.text),
6799
+ fontFace: cleanFontFace(theme.typography.headingFont),
6800
+ align,
6801
+ valign: "top"
6802
+ });
6803
+ }
6804
+ curY += titleH + (compact ? 0.04 : 0.08);
6805
+ if (hero.subtitle && curY < maxY - 0.14) {
6806
+ const subLines = Math.max(1, Math.ceil(hero.subtitle.length / (compact ? 64 : 56)));
6807
+ const subH = Math.min(Math.max(compact ? 0.28 : 0.38, subLines * 0.28 + 0.06), maxY - curY);
6741
6808
  const bodySize = themeSizeToPptxPoints(theme.typography.sizes?.body, 18);
6742
- const subFontSize = subLines > 2 ? bodySize - 1 : bodySize + 1;
6809
+ const subFontSize = compact ? bodySize - 1 : subLines > 2 ? bodySize - 1 : bodySize + 1;
6743
6810
  pptxSlide.addText(hero.subtitle, {
6744
6811
  x: rect.x,
6745
6812
  y: curY,
@@ -6748,22 +6815,22 @@ var PptxRenderer = class {
6748
6815
  fontSize: subFontSize,
6749
6816
  color: this.cleanHexColor(theme.colors.muted || theme.colors.text),
6750
6817
  fontFace: cleanFontFace(theme.typography.bodyFont),
6751
- align
6818
+ align,
6819
+ valign: "top"
6752
6820
  });
6753
- curY += subH + 0.12;
6821
+ curY += subH + (compact ? 0.06 : 0.12);
6754
6822
  }
6755
- if (hero.tagline && hero.badge) {
6823
+ if (hero.tagline && hero.badge && curY + 0.28 <= maxY) {
6756
6824
  pptxSlide.addText(hero.tagline, {
6757
6825
  x: rect.x,
6758
6826
  y: curY,
6759
6827
  w: rect.w,
6760
- h: 0.3,
6828
+ h: 0.28,
6761
6829
  fontSize: 12,
6762
6830
  color: this.cleanHexColor(theme.colors.muted || theme.colors.text),
6763
6831
  fontFace: cleanFontFace(theme.typography.bodyFont),
6764
6832
  align
6765
6833
  });
6766
- curY += 0.35;
6767
6834
  }
6768
6835
  if (node.children) {
6769
6836
  for (const childNode of node.children) {
@@ -6995,7 +7062,7 @@ ${item.description}` : "";
6995
7062
  const { element, bounds } = node;
6996
7063
  const compare = element;
6997
7064
  const rect = this.toInches(bounds, scaleX, scaleY);
6998
- const colW = (rect.w - 0.4) / 2;
7065
+ const colW = (rect.w - 0.5) / 2;
6999
7066
  pptxSlide.addShape(pptx.ShapeType.roundRect, {
7000
7067
  x: rect.x,
7001
7068
  y: rect.y,
@@ -7017,7 +7084,7 @@ ${item.description}` : "";
7017
7084
  fontFace: cleanFontFace(theme.typography.headingFont)
7018
7085
  });
7019
7086
  }
7020
- const rightX = rect.x + colW + 0.4;
7087
+ const rightX = rect.x + colW + 0.5;
7021
7088
  pptxSlide.addShape(pptx.ShapeType.roundRect, {
7022
7089
  x: rightX,
7023
7090
  y: rect.y,
@@ -7039,26 +7106,30 @@ ${item.description}` : "";
7039
7106
  fontFace: cleanFontFace(theme.typography.headingFont)
7040
7107
  });
7041
7108
  }
7042
- const vsX = rect.x + colW + 0.05;
7043
- pptxSlide.addShape(pptx.ShapeType.roundRect, {
7109
+ const vsW = 0.42;
7110
+ const vsH = 0.42;
7111
+ const vsX = rect.x + colW + (0.5 - vsW) / 2;
7112
+ const vsY = rect.y + rect.h / 2 - vsH / 2;
7113
+ pptxSlide.addShape(pptx.ShapeType.ellipse, {
7044
7114
  x: vsX,
7045
- y: rect.y + rect.h / 2 - 0.18,
7046
- w: 0.3,
7047
- h: 0.3,
7048
- fill: { color: this.cleanHexColor(theme.colors.border || "#334155") },
7049
- rectRadius: 0.15
7115
+ y: vsY,
7116
+ w: vsW,
7117
+ h: vsH,
7118
+ fill: { color: this.cleanHexColor(theme.colors.surface || theme.colors.background) },
7119
+ line: { color: this.cleanHexColor(theme.colors.primary), width: 1.5 }
7050
7120
  });
7051
7121
  pptxSlide.addText("VS", {
7052
7122
  x: vsX,
7053
- y: rect.y + rect.h / 2 - 0.18,
7054
- w: 0.3,
7055
- h: 0.3,
7056
- fontSize: 10,
7123
+ y: vsY,
7124
+ w: vsW,
7125
+ h: vsH,
7126
+ fontSize: 11,
7057
7127
  bold: true,
7058
- color: this.cleanHexColor(theme.colors.muted || "#94a3b8"),
7128
+ color: this.cleanHexColor(theme.colors.text),
7059
7129
  fontFace: cleanFontFace(theme.typography.headingFont),
7060
7130
  align: "center",
7061
- valign: "middle"
7131
+ valign: "middle",
7132
+ margin: 0
7062
7133
  });
7063
7134
  if (node.children) {
7064
7135
  for (const childNode of node.children) {
@@ -7399,33 +7470,36 @@ ${mermaid.code}`, {
7399
7470
  const p2 = positions[e.to];
7400
7471
  if (!p1 || !p2)
7401
7472
  return;
7402
- const x1 = isLR ? p1.x + nodeW : p1.x + nodeW / 2;
7403
- const y1 = isLR ? p1.y + nodeH / 2 : p1.y + nodeH;
7404
- const x2 = isLR ? p2.x : p2.x + nodeW / 2;
7405
- const y2 = isLR ? p2.y + nodeH / 2 : p2.y;
7406
- const minX2 = Math.min(x1, x2);
7407
- const minY2 = Math.min(y1, y2);
7408
- const lineW = Math.max(0.01, Math.abs(x2 - x1));
7409
- const lineH = Math.max(0.01, Math.abs(y2 - y1));
7410
- const flipH = x2 < x1;
7411
- const flipV = y2 < y1;
7412
- pptxSlide.addShape(pptx.ShapeType.line, {
7413
- x: minX2,
7414
- y: minY2,
7415
- w: lineW,
7416
- h: lineH,
7417
- flipH,
7418
- flipV,
7419
- line: {
7420
- color: arrowColor,
7421
- width: 2,
7422
- endArrowType: "triangle",
7423
- dashType: e.style === "dashed" ? "dash" : "solid"
7424
- }
7425
- });
7473
+ const pts = orthogonalEdgePoints(isLR, p1, p2, nodeW, nodeH);
7474
+ if (pts.length < 2)
7475
+ return;
7476
+ for (let i = 0; i < pts.length - 1; i++) {
7477
+ const a = pts[i];
7478
+ const b = pts[i + 1];
7479
+ const segMinX = Math.min(a.x, b.x);
7480
+ const segMinY = Math.min(a.y, b.y);
7481
+ const lineW = Math.max(0.01, Math.abs(b.x - a.x));
7482
+ const lineH = Math.max(0.01, Math.abs(b.y - a.y));
7483
+ const isLast = i === pts.length - 2;
7484
+ pptxSlide.addShape(pptx.ShapeType.line, {
7485
+ x: segMinX,
7486
+ y: segMinY,
7487
+ w: lineW,
7488
+ h: lineH,
7489
+ flipH: b.x < a.x,
7490
+ flipV: b.y < a.y,
7491
+ line: {
7492
+ color: arrowColor,
7493
+ width: 2,
7494
+ endArrowType: isLast ? "triangle" : void 0,
7495
+ dashType: e.style === "dashed" ? "dash" : "solid"
7496
+ }
7497
+ });
7498
+ }
7426
7499
  if (e.label) {
7427
- const midX = (x1 + x2) / 2;
7428
- const midY = (y1 + y2) / 2;
7500
+ const mid = pts[Math.floor(pts.length / 2)];
7501
+ const midX = mid.x;
7502
+ const midY = mid.y;
7429
7503
  const pillW = Math.min(1.4, Math.max(0.7, e.label.length * 0.08 + 0.25));
7430
7504
  pptxSlide.addShape(pptx.ShapeType.roundRect, {
7431
7505
  x: midX - pillW / 2,
@@ -7490,17 +7564,18 @@ ${mermaid.code}`, {
7490
7564
  line: { color: nodeBorderColor, width: 1 }
7491
7565
  });
7492
7566
  }
7493
- pptxSlide.addText(fitDiagramLabel(n.label, Math.max(8, Math.floor(nodeW * 12))), {
7494
- x: p.x + 0.06,
7567
+ pptxSlide.addText(fitDiagramLabel(n.label, Math.max(10, Math.floor(nodeW * 72 / 6.8))), {
7568
+ x: p.x + 0.08,
7495
7569
  y: p.y + 0.06,
7496
- w: Math.max(0.1, nodeW - 0.12),
7570
+ w: Math.max(0.1, nodeW - 0.16),
7497
7571
  h: Math.max(0.1, nodeH - 0.12),
7498
7572
  align: "center",
7499
7573
  valign: "middle",
7500
- fontSize: n.label.length > 18 ? 10 : 11,
7574
+ fontSize: nodeW < 1.1 ? 10 : n.label.length > 22 ? 11 : 12,
7501
7575
  bold: true,
7502
7576
  color: this.cleanHexColor(theme.colors.text || "ffffff"),
7503
- fontFace: cleanFontFace(theme.typography.headingFont)
7577
+ fontFace: cleanFontFace(theme.typography.headingFont),
7578
+ margin: 0
7504
7579
  });
7505
7580
  });
7506
7581
  }
@@ -8124,10 +8199,12 @@ var HtmlRenderer = class {
8124
8199
  .yumia-grid {
8125
8200
  display: grid;
8126
8201
  width: 100%;
8127
- flex: 1;
8202
+ flex: 1 1 auto;
8128
8203
  min-height: 0;
8204
+ max-height: 100%;
8129
8205
  gap: clamp(1rem, 1.8vw, 1.6rem);
8130
8206
  align-items: stretch;
8207
+ align-content: start;
8131
8208
  margin-top: 0.4rem;
8132
8209
  }
8133
8210
 
@@ -8171,14 +8248,16 @@ var HtmlRenderer = class {
8171
8248
  background: var(--yumia-surface);
8172
8249
  border: 1.5px solid var(--yumia-border);
8173
8250
  border-radius: 14px;
8174
- padding: clamp(1.2rem, 2vw, 1.8rem);
8251
+ padding: clamp(1.1rem, 1.8vw, 1.5rem);
8175
8252
  display: flex;
8176
8253
  flex-direction: column;
8177
8254
  justify-content: flex-start;
8178
- gap: 0.7rem;
8255
+ gap: 0.45rem;
8179
8256
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
8180
8257
  height: 100%;
8258
+ max-height: 100%;
8181
8259
  min-height: 0;
8260
+ overflow: hidden;
8182
8261
  box-sizing: border-box;
8183
8262
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
8184
8263
  }
@@ -9824,7 +9903,8 @@ var HtmlRenderer = class {
9824
9903
  case "grid": {
9825
9904
  const g = element;
9826
9905
  const cols = typeof g.columns === "number" ? `repeat(${g.columns}, minmax(0, 1fr))` : g.columns;
9827
- const gap = g.gap !== void 0 ? typeof g.gap === "number" ? `${g.gap}px` : g.gap : "1.5rem";
9906
+ const gapRaw = g.gap !== void 0 ? g.gap : "1.5rem";
9907
+ const gap = typeof gapRaw === "number" ? `${gapRaw}px` : /^\d+(\.\d+)?$/.test(String(gapRaw).trim()) ? `${String(gapRaw).trim()}px` : String(gapRaw);
9828
9908
  const inner = g.elements.map((child) => this.renderElement(child, theme, presentation)).join("\n");
9829
9909
  return `<div class="yumia-grid" style="grid-template-columns:${cols}; gap:${gap};">${inner}</div>`;
9830
9910
  }
@@ -10646,12 +10726,7 @@ var PdfRenderer = class {
10646
10726
  gap: 24
10647
10727
  });
10648
10728
  for (const node of slideLayout.nodes) {
10649
- const x = node.bounds.x * scaleX;
10650
- const y = node.bounds.y * scaleY;
10651
- const w = node.bounds.width * scaleX;
10652
- if (y >= contentBottom - 8)
10653
- continue;
10654
- this.renderElement(doc, node.element, x, y, w, theme, presentation);
10729
+ this.paintLayoutNode(doc, node, scaleX, scaleY, theme, presentation, contentBottom);
10655
10730
  }
10656
10731
  const progressWidth = slideNum / totalSlides * pageWidth;
10657
10732
  doc.rect(0, pageHeight - 4, progressWidth, 4).fill(theme.colors.primary);
@@ -10667,6 +10742,132 @@ var PdfRenderer = class {
10667
10742
  align: "right"
10668
10743
  });
10669
10744
  }
10745
+ paintLayoutNode(doc, node, scaleX, scaleY, theme, presentation, contentBottom) {
10746
+ const el = node.element;
10747
+ const x = node.bounds.x * scaleX;
10748
+ const y = node.bounds.y * scaleY;
10749
+ const w = node.bounds.width * scaleX;
10750
+ const h = node.bounds.height * scaleY;
10751
+ if (y >= contentBottom - 8)
10752
+ return;
10753
+ const hasChildren = !!(node.children && node.children.length > 0);
10754
+ if (hasChildren && (el.type === "grid" || el.type === "stack" || el.type === "columns" || el.type === "column")) {
10755
+ for (const child of node.children) {
10756
+ this.paintLayoutNode(doc, child, scaleX, scaleY, theme, presentation, contentBottom);
10757
+ }
10758
+ return;
10759
+ }
10760
+ if (hasChildren && el.type === "card") {
10761
+ this.paintCardFrame(doc, el, x, y, w, h, theme);
10762
+ for (const child of node.children) {
10763
+ this.paintLayoutNode(doc, child, scaleX, scaleY, theme, presentation, contentBottom);
10764
+ }
10765
+ return;
10766
+ }
10767
+ if (hasChildren && el.type === "compare") {
10768
+ this.paintCompareFrame(doc, el, x, y, w, h, theme);
10769
+ for (const child of node.children) {
10770
+ this.paintLayoutNode(doc, child, scaleX, scaleY, theme, presentation, contentBottom);
10771
+ }
10772
+ return;
10773
+ }
10774
+ if (el.type === "hero") {
10775
+ this.paintHeroInBounds(doc, el, x, y, w, h, theme);
10776
+ if (hasChildren) {
10777
+ for (const child of node.children) {
10778
+ this.paintLayoutNode(doc, child, scaleX, scaleY, theme, presentation, contentBottom);
10779
+ }
10780
+ }
10781
+ return;
10782
+ }
10783
+ this.renderElement(doc, el, x, y, w, theme, presentation);
10784
+ }
10785
+ paintCardFrame(doc, card, x, y, width, height, theme) {
10786
+ const variantColor = this.getVariantColor(card.variant, theme);
10787
+ doc.save();
10788
+ doc.roundedRect(x, y, width, height, 8).fill(theme.colors.surface || "rgba(255,255,255,0.06)");
10789
+ doc.roundedRect(x, y, width, height, 8).lineWidth(1.5).strokeColor(variantColor).stroke();
10790
+ doc.restore();
10791
+ if (card.title) {
10792
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(13).fillColor(variantColor).text(this.stripFormatting(card.title), x + 12, y + 10, {
10793
+ width: width - 24,
10794
+ height: 22,
10795
+ ellipsis: true
10796
+ });
10797
+ }
10798
+ }
10799
+ paintCompareFrame(doc, compare, x, y, width, height, theme) {
10800
+ const gap = 16;
10801
+ const colW = (width - gap) / 2;
10802
+ const leftColor = theme.colors.primary;
10803
+ const rightColor = theme.colors.accent || theme.colors.secondary || theme.colors.primary;
10804
+ doc.save();
10805
+ doc.roundedRect(x, y, colW, height, 8).fill(theme.colors.surface || "#151522");
10806
+ doc.roundedRect(x, y, colW, height, 8).lineWidth(1.4).strokeColor(theme.colors.border || "rgba(255,255,255,0.15)").stroke();
10807
+ doc.roundedRect(x + colW + gap, y, colW, height, 8).fill(theme.colors.surface || "#151522");
10808
+ doc.roundedRect(x + colW + gap, y, colW, height, 8).lineWidth(1.4).strokeColor(theme.colors.border || "rgba(255,255,255,0.15)").stroke();
10809
+ doc.restore();
10810
+ const midX = x + colW + gap / 2;
10811
+ const midY = y + height / 2;
10812
+ doc.circle(midX, midY, 16).fill(theme.colors.surface || "#151522");
10813
+ doc.circle(midX, midY, 16).lineWidth(1.4).strokeColor(theme.colors.primary).stroke();
10814
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(10).fillColor(theme.colors.text).text("VS", midX - 14, midY - 5, { width: 28, align: "center" });
10815
+ if (compare.leftTitle) {
10816
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(11).fillColor(leftColor).text(this.stripFormatting(compare.leftTitle), x + 10, y + 10, {
10817
+ width: colW - 20,
10818
+ height: 28,
10819
+ ellipsis: true
10820
+ });
10821
+ }
10822
+ if (compare.rightTitle) {
10823
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(11).fillColor(rightColor).text(this.stripFormatting(compare.rightTitle), x + colW + gap + 10, y + 10, {
10824
+ width: colW - 20,
10825
+ height: 28,
10826
+ ellipsis: true
10827
+ });
10828
+ }
10829
+ }
10830
+ paintHeroInBounds(doc, hero, x, y, width, height, theme) {
10831
+ const align = hero.align || "center";
10832
+ const compact = height < 140;
10833
+ let curY = y;
10834
+ const maxY = y + height - 4;
10835
+ if (hero.badge && curY < maxY) {
10836
+ const label = this.stripFormatting(hero.badge).toUpperCase();
10837
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(compact ? 8 : 9);
10838
+ const labelW = Math.min(width, Math.max(70, doc.widthOfString(label) + 24));
10839
+ const badgeX = align === "left" ? x : align === "right" ? x + width - labelW : x + (width - labelW) / 2;
10840
+ doc.roundedRect(badgeX, curY, labelW, compact ? 16 : 18, 9).lineWidth(1.2).strokeColor(theme.colors.primary).fillColor(theme.colors.surface || "#f8fafc").fillAndStroke();
10841
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(compact ? 8 : 9).fillColor(theme.colors.primary).text(label, badgeX, curY + 3, { width: labelW, align: "center" });
10842
+ curY += compact ? 22 : 28;
10843
+ } else if (hero.tagline && curY < maxY) {
10844
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(compact ? 10 : 11).fillColor(theme.colors.primary);
10845
+ doc.text(this.stripFormatting(hero.tagline).toUpperCase(), x, curY, { width, align });
10846
+ curY += 20;
10847
+ }
10848
+ if (curY < maxY) {
10849
+ const titleSize = compact ? themeSizeToPdfPoints(theme.typography.sizes?.h1, 44) : themeSizeToPdfPoints(theme.typography.sizes?.display, 56);
10850
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(titleSize).fillColor(theme.colors.text);
10851
+ doc.text(this.stripFormatting(hero.title), x, curY, {
10852
+ width,
10853
+ height: Math.max(20, maxY - curY - (hero.subtitle ? 28 : 4)),
10854
+ lineGap: 4,
10855
+ align,
10856
+ ellipsis: true
10857
+ });
10858
+ curY += Math.min(maxY - curY, doc.heightOfString(this.stripFormatting(hero.title), { width }) + 8);
10859
+ }
10860
+ if (hero.subtitle && curY < maxY) {
10861
+ const subSize = themeSizeToPdfPoints(theme.typography.sizes?.body, 18);
10862
+ doc.font(this.getPdfFont(theme, "regular")).fontSize(compact ? subSize - 1 : subSize + 1).fillColor(theme.colors.muted || "#888888");
10863
+ doc.text(this.stripFormatting(hero.subtitle), x, curY, {
10864
+ width,
10865
+ height: Math.max(16, maxY - curY),
10866
+ align,
10867
+ ellipsis: true
10868
+ });
10869
+ }
10870
+ }
10670
10871
  renderElement(doc, element, x, y, width, theme, presentation) {
10671
10872
  switch (element.type) {
10672
10873
  case "hero": {
@@ -11114,7 +11315,10 @@ var PdfRenderer = class {
11114
11315
  doc.roundedRect(x, y, width, boxH2, 8).strokeColor(theme.colors.border || "rgba(255,255,255,0.1)").stroke();
11115
11316
  let topY2 = y + 8;
11116
11317
  if (ch.title) {
11117
- doc.font(this.getPdfFont(theme, "bold")).fontSize(13).fillColor(theme.colors.text).text(this.stripFormatting(ch.title), x + 14, topY2, { width: width - 28, align: "center" });
11318
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(13).fillColor(theme.colors.text).text(this.stripFormatting(ch.title), x + 14, topY2, {
11319
+ width: width - 28,
11320
+ align: "center"
11321
+ });
11118
11322
  topY2 += 18;
11119
11323
  }
11120
11324
  const values2 = ch.series[0]?.values || [];
@@ -11192,7 +11396,10 @@ var PdfRenderer = class {
11192
11396
  doc.roundedRect(x, y, width, boxH2, 8).strokeColor(theme.colors.border || "rgba(255,255,255,0.1)").stroke();
11193
11397
  let topY2 = y + 8;
11194
11398
  if (ch.title) {
11195
- doc.font(this.getPdfFont(theme, "bold")).fontSize(13).fillColor(theme.colors.text).text(this.stripFormatting(ch.title), x + 14, topY2, { width: width - 28, align: "center" });
11399
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(13).fillColor(theme.colors.text).text(this.stripFormatting(ch.title), x + 14, topY2, {
11400
+ width: width - 28,
11401
+ align: "center"
11402
+ });
11196
11403
  topY2 += 18;
11197
11404
  }
11198
11405
  const val = ch.series[0]?.values[0] || 0;
@@ -11232,7 +11439,10 @@ var PdfRenderer = class {
11232
11439
  }
11233
11440
  doc.font(this.getPdfFont(theme, "bold")).fontSize(20).fillColor(theme.colors.text).text(`${val}%`, cx - 40, cy - 16, { width: 80, align: "center" });
11234
11441
  if (ch.labels?.[0]) {
11235
- doc.font(this.getPdfFont(theme, "regular")).fontSize(9).fillColor(theme.colors.muted || "#888888").text(this.stripFormatting(ch.labels[0]), cx - 60, cy + 6, { width: 120, align: "center" });
11442
+ doc.font(this.getPdfFont(theme, "regular")).fontSize(9).fillColor(theme.colors.muted || "#888888").text(this.stripFormatting(ch.labels[0]), cx - 60, cy + 6, {
11443
+ width: 120,
11444
+ align: "center"
11445
+ });
11236
11446
  }
11237
11447
  return y + boxH2 + 8;
11238
11448
  }
@@ -11492,25 +11702,30 @@ var PdfRenderer = class {
11492
11702
  const p2 = positions[e.to];
11493
11703
  if (!p1 || !p2)
11494
11704
  return;
11495
- const x1 = isLR ? p1.x + nodeW : p1.x + nodeW / 2;
11496
- const y1 = isLR ? p1.y + nodeH / 2 : p1.y + nodeH;
11497
- const x2 = isLR ? p2.x : p2.x + nodeW / 2;
11498
- const y2 = isLR ? p2.y + nodeH / 2 : p2.y;
11705
+ const pts = orthogonalEdgePoints(isLR, p1, p2, nodeW, nodeH);
11706
+ if (pts.length < 2)
11707
+ return;
11708
+ const last = pts[pts.length - 1];
11709
+ const prev = pts[pts.length - 2];
11499
11710
  doc.save();
11500
11711
  doc.lineWidth(1.5).strokeColor(arrowColor);
11501
11712
  if (e.style === "dashed")
11502
11713
  doc.dash(4, { space: 3 });
11503
- doc.moveTo(x1, y1).lineTo(x2, y2).stroke();
11714
+ doc.moveTo(pts[0].x, pts[0].y);
11715
+ for (let i = 1; i < pts.length; i++)
11716
+ doc.lineTo(pts[i].x, pts[i].y);
11717
+ doc.stroke();
11504
11718
  doc.restore();
11505
- const angle = Math.atan2(y2 - y1, x2 - x1);
11719
+ const angle = Math.atan2(last.y - prev.y, last.x - prev.x);
11506
11720
  const headLen = 6;
11507
11721
  doc.save();
11508
11722
  doc.fillColor(arrowColor);
11509
- doc.moveTo(x2, y2).lineTo(x2 - headLen * Math.cos(angle - Math.PI / 6), y2 - headLen * Math.sin(angle - Math.PI / 6)).lineTo(x2 - headLen * Math.cos(angle + Math.PI / 6), y2 - headLen * Math.sin(angle + Math.PI / 6)).fill();
11723
+ doc.moveTo(last.x, last.y).lineTo(last.x - headLen * Math.cos(angle - Math.PI / 6), last.y - headLen * Math.sin(angle - Math.PI / 6)).lineTo(last.x - headLen * Math.cos(angle + Math.PI / 6), last.y - headLen * Math.sin(angle + Math.PI / 6)).fill();
11510
11724
  doc.restore();
11511
11725
  if (e.label) {
11512
- const midX = (x1 + x2) / 2;
11513
- const midY = (y1 + y2) / 2;
11726
+ const mid = pts[Math.floor(pts.length / 2)];
11727
+ const midX = mid.x;
11728
+ const midY = mid.y;
11514
11729
  const labelText = this.stripFormatting(e.label);
11515
11730
  const labelW = Math.min(90, Math.max(45, labelText.length * 5.5 + 16));
11516
11731
  doc.save();
@@ -11584,7 +11799,10 @@ var PdfRenderer = class {
11584
11799
  doc.restore();
11585
11800
  doc.roundedRect(boxX, topY, partW, 26, 5).fill(theme.colors.surface || "rgba(255,255,255,0.06)");
11586
11801
  doc.roundedRect(boxX, topY, partW, 26, 5).lineWidth(1.2).strokeColor(primaryColor).stroke();
11587
- doc.font(this.getPdfFont(theme, "bold")).fontSize(9).fillColor(theme.colors.text).text(this.stripFormatting(p.name), boxX + 2, topY + 8, { width: partW - 4, align: "center" });
11802
+ doc.font(this.getPdfFont(theme, "bold")).fontSize(9).fillColor(theme.colors.text).text(this.stripFormatting(p.name), boxX + 2, topY + 8, {
11803
+ width: partW - 4,
11804
+ align: "center"
11805
+ });
11588
11806
  });
11589
11807
  messages.forEach((msg, idx) => {
11590
11808
  const x1 = positions[msg.from] || x + 20;
@@ -11799,7 +12017,7 @@ function startDevServer(filePath, options = {}) {
11799
12017
  // src/cli.ts
11800
12018
  import { mkdirSync, readFileSync as readFileSync3, watch as fsWatch, writeFileSync } from "fs";
11801
12019
  import { basename, dirname, extname, join, resolve as resolve3 } from "path";
11802
- var VERSION = "0.1.30";
12020
+ var VERSION = "0.1.32";
11803
12021
  function printHelp() {
11804
12022
  return `
11805
12023
  YumiaMD \u2014 Markdown-based presentation compiler (v${VERSION})
@@ -12443,6 +12661,7 @@ export {
12443
12661
  parseYumia,
12444
12662
  migrateMarkdownToNative,
12445
12663
  computeDiagramLayout,
12664
+ orthogonalEdgePoints,
12446
12665
  fitDiagramLabel,
12447
12666
  estimateDiagramHeight,
12448
12667
  longestDiagramLabel,
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.30";
12
+ declare const VERSION = "0.1.32";
13
13
  declare function printHelp(): string;
14
14
  declare function runCli(argv: string[]): Promise<{
15
15
  exitCode: number;
package/dist/index.js CHANGED
@@ -71,6 +71,7 @@ import {
71
71
  nebulaTheme,
72
72
  neoBrutalistTheme,
73
73
  nordTheme,
74
+ orthogonalEdgePoints,
74
75
  parse,
75
76
  parseDataString,
76
77
  parseHighlightLines,
@@ -91,7 +92,7 @@ import {
91
92
  themeSizeToPptxPoints,
92
93
  tokyoNightTheme,
93
94
  validate
94
- } from "./chunk-MXMPDHHS.js";
95
+ } from "./chunk-Y2OWK5OP.js";
95
96
  export {
96
97
  DEFAULT_VIEWPORT,
97
98
  DefaultLayoutEngine,
@@ -165,6 +166,7 @@ export {
165
166
  nebulaTheme,
166
167
  neoBrutalistTheme,
167
168
  nordTheme,
169
+ orthogonalEdgePoints,
168
170
  parse,
169
171
  parseDataString,
170
172
  parseHighlightLines,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yumiamd",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "Command line interface and compiler for YumiaMD presentations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,15 +22,15 @@
22
22
  },
23
23
  "devDependencies": {
24
24
  "tsup": "^8.5.1",
25
- "@yumiamd/ast": "0.1.30",
26
- "@yumiamd/core": "0.1.30",
27
- "@yumiamd/renderer": "0.1.30",
28
- "@yumiamd/renderer-html": "0.1.30",
29
- "@yumiamd/renderer-pdf": "0.1.30",
30
- "@yumiamd/renderer-pptx": "0.1.30",
31
- "@yumiamd/theme": "0.1.30",
32
- "@yumiamd/layout": "0.1.30",
33
- "@yumiamd/parser": "0.1.30"
25
+ "@yumiamd/layout": "0.1.32",
26
+ "@yumiamd/ast": "0.1.32",
27
+ "@yumiamd/renderer-pdf": "0.1.32",
28
+ "@yumiamd/parser": "0.1.32",
29
+ "@yumiamd/renderer": "0.1.32",
30
+ "@yumiamd/theme": "0.1.32",
31
+ "@yumiamd/renderer-html": "0.1.32",
32
+ "@yumiamd/renderer-pptx": "0.1.32",
33
+ "@yumiamd/core": "0.1.32"
34
34
  },
35
35
  "keywords": [
36
36
  "yumia",