ooxml-core 0.21.2 → 0.22.1

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.
Files changed (85) hide show
  1. package/README.md +3 -4
  2. package/dist/automation/index.cjs +86 -22
  3. package/dist/automation/index.mjs +86 -22
  4. package/dist/chart/index.cjs +242 -0
  5. package/dist/chart/index.d.ts +5 -0
  6. package/dist/chart/index.d.ts.map +1 -1
  7. package/dist/chart/index.mjs +227 -1
  8. package/dist/chart/pie-best-fit.d.ts +51 -0
  9. package/dist/chart/pie-best-fit.d.ts.map +1 -0
  10. package/dist/chart/pie-label-collision.d.ts +48 -0
  11. package/dist/chart/pie-label-collision.d.ts.map +1 -0
  12. package/dist/chart/radar-geometry.d.ts +19 -0
  13. package/dist/chart/radar-geometry.d.ts.map +1 -0
  14. package/dist/chart/treemap-squarify.d.ts +31 -0
  15. package/dist/chart/treemap-squarify.d.ts.map +1 -0
  16. package/dist/chart/trendline-equation.d.ts +39 -0
  17. package/dist/chart/trendline-equation.d.ts.map +1 -0
  18. package/dist/collab/index.cjs +6 -1
  19. package/dist/collab/index.mjs +6 -1
  20. package/dist/color/contrast.d.ts +18 -0
  21. package/dist/color/contrast.d.ts.map +1 -0
  22. package/dist/color/index.cjs +33 -0
  23. package/dist/color/index.d.ts +2 -0
  24. package/dist/color/index.d.ts.map +1 -1
  25. package/dist/color/index.mjs +31 -1
  26. package/dist/color/unit-rgb.d.ts +21 -0
  27. package/dist/color/unit-rgb.d.ts.map +1 -0
  28. package/dist/docx/embedded.cjs +1 -1
  29. package/dist/docx/embedded.mjs +1 -1
  30. package/dist/docx/index.cjs +1 -1
  31. package/dist/docx/index.mjs +1 -1
  32. package/dist/docx/load/index.cjs +1 -1
  33. package/dist/docx/load/index.mjs +1 -1
  34. package/dist/geometry/align-distribute.d.ts +145 -0
  35. package/dist/geometry/align-distribute.d.ts.map +1 -0
  36. package/dist/geometry/index.cjs +318 -0
  37. package/dist/geometry/index.d.ts +2 -0
  38. package/dist/geometry/index.d.ts.map +1 -1
  39. package/dist/geometry/index.mjs +307 -1
  40. package/dist/geometry/snap-guides.d.ts +125 -0
  41. package/dist/geometry/snap-guides.d.ts.map +1 -0
  42. package/dist/index.cjs +665 -0
  43. package/dist/index.mjs +665 -0
  44. package/dist/pptx/automation/index.cjs +24 -1
  45. package/dist/pptx/automation/index.d.ts +3 -3
  46. package/dist/pptx/automation/index.mjs +24 -1
  47. package/dist/pptx/chunks/{index-JaNVJ6Tf.d.ts → index-D2y7ehZ5.d.ts} +3 -3
  48. package/dist/pptx/chunks/{index-JaNVJ6Tf.d.ts.map → index-D2y7ehZ5.d.ts.map} +1 -1
  49. package/dist/pptx/chunks/{index-DHdHU9Qr.d.ts → index-ucqKcqvI.d.ts} +10 -2
  50. package/dist/pptx/chunks/{index-DHdHU9Qr.d.ts.map → index-ucqKcqvI.d.ts.map} +1 -1
  51. package/dist/pptx/chunks/{text-operations-DUEhSW5Z.d.ts → text-operations-CR3pUJi-.d.ts} +14 -2
  52. package/dist/pptx/chunks/text-operations-CR3pUJi-.d.ts.map +1 -0
  53. package/dist/pptx/cli/index.cjs +23 -0
  54. package/dist/pptx/cli/index.d.ts +1 -1
  55. package/dist/pptx/cli/index.mjs +23 -0
  56. package/dist/pptx/converter/index.d.ts +1 -1
  57. package/dist/pptx/index.cjs +33 -3
  58. package/dist/pptx/index.d.ts +3 -3
  59. package/dist/pptx/index.mjs +33 -3
  60. package/dist/teams/index.cjs +7 -2
  61. package/dist/teams/index.mjs +7 -2
  62. package/dist/text/decimal-tab.d.ts +24 -0
  63. package/dist/text/decimal-tab.d.ts.map +1 -0
  64. package/dist/text/index.cjs +72 -0
  65. package/dist/text/index.d.ts +2 -0
  66. package/dist/text/index.d.ts.map +1 -1
  67. package/dist/text/index.mjs +68 -1
  68. package/dist/text/tab-leader.d.ts +36 -0
  69. package/dist/text/tab-leader.d.ts.map +1 -0
  70. package/dist/xlsx/edit/charts.d.ts.map +1 -1
  71. package/dist/xlsx/edit/clipboard-html-parse.d.ts.map +1 -1
  72. package/dist/xlsx/edit/fill-series.d.ts.map +1 -1
  73. package/dist/xlsx/index.cjs +85 -21
  74. package/dist/xlsx/index.mjs +85 -21
  75. package/dist/xlsx/load/csv.d.ts.map +1 -1
  76. package/dist/xlsx/load/index.cjs +17 -8
  77. package/dist/xlsx/load/index.mjs +17 -8
  78. package/dist/xlsx/numfmt/input-date.d.ts.map +1 -1
  79. package/dist/xlsx/ui/grid/cell-paint.d.ts.map +1 -1
  80. package/dist/xlsx/ui/index.cjs +86 -25
  81. package/dist/xlsx/ui/index.mjs +86 -25
  82. package/dist/xlsx/ui/selection.d.ts.map +1 -1
  83. package/dist/xlsx/write/drawing.d.ts.map +1 -1
  84. package/package.json +2 -2
  85. package/dist/pptx/chunks/text-operations-DUEhSW5Z.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -12,13 +12,16 @@ __export(color_exports, {
12
12
  blendColorOntoWhite: () => blendColorOntoWhite,
13
13
  clampUnitInterval: () => clampUnitInterval,
14
14
  colorWithOpacity: () => colorWithOpacity,
15
+ contrastTextColor: () => contrastTextColor,
15
16
  hexToRgbChannels: () => hexToRgbChannels,
17
+ hexToRgbUnit: () => hexToRgbUnit,
16
18
  hslToRgb: () => hslToRgb,
17
19
  linearToSrgb255: () => linearToSrgb255,
18
20
  normalizeHexColor: () => normalizeHexColor,
19
21
  parseDrawingFraction: () => parseDrawingFraction,
20
22
  parseDrawingHueDegrees: () => parseDrawingHueDegrees,
21
23
  parseDrawingPercent: () => parseDrawingPercent,
24
+ parseHex: () => parseHex,
22
25
  parseOoxmlPercent: () => parseOoxmlPercent,
23
26
  rgbToHsl: () => rgbToHsl,
24
27
  scrgbLinearToSrgb8: () => scrgbLinearToSrgb8,
@@ -186,22 +189,68 @@ function linearToSrgb255(linear) {
186
189
  return 255 * (c <= 31308e-7 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055);
187
190
  }
188
191
 
192
+ // color/contrast.ts
193
+ function parseHex(hex) {
194
+ let h = hex.trim().replace(/^#/u, "");
195
+ if (h.length === 3) {
196
+ h = h.split("").map((c) => c + c).join("");
197
+ }
198
+ if (h.length !== 6 || /[^0-9a-fA-F]/u.test(h)) {
199
+ return [128, 128, 128];
200
+ }
201
+ return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
202
+ }
203
+ function contrastTextColor(fill2) {
204
+ const [r, g, b] = parseHex(fill2);
205
+ const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
206
+ return lum > 0.6 ? "#1a1a1a" : "#ffffff";
207
+ }
208
+
209
+ // color/unit-rgb.ts
210
+ function hexToRgbUnit(hex) {
211
+ const clean = hex.replace(/^#/u, "");
212
+ const r = Number.parseInt(clean.substring(0, 2), 16) / 255;
213
+ const g = Number.parseInt(clean.substring(2, 4), 16) / 255;
214
+ const b = Number.parseInt(clean.substring(4, 6), 16) / 255;
215
+ return {
216
+ r: Number.isFinite(r) ? r : 0,
217
+ g: Number.isFinite(g) ? g : 0,
218
+ b: Number.isFinite(b) ? b : 0
219
+ };
220
+ }
221
+
189
222
  // chart/index.ts
190
223
  var chart_exports = {};
191
224
  __export(chart_exports, {
225
+ BEST_FIT_EDGE_TOLERANCE: () => BEST_FIT_EDGE_TOLERANCE,
226
+ BEST_FIT_OUTSIDE_GAP: () => BEST_FIT_OUTSIDE_GAP,
227
+ BEST_FIT_RIM_INSET: () => BEST_FIT_RIM_INSET,
228
+ LABEL_COLLISION_GAP: () => LABEL_COLLISION_GAP,
192
229
  aggregateByCategory: () => aggregateByCategory,
193
230
  axisTargetIntervals: () => axisTargetIntervals,
194
231
  computeBoxStats: () => computeBoxStats,
195
232
  computeHistogramBins: () => computeHistogramBins,
196
233
  computeLinearRegression: () => computeLinearRegression,
197
234
  computeRSquared: () => computeRSquared,
235
+ computeRadarPoints: () => computeRadarPoints,
198
236
  computeStackedSeriesPlots: () => computeStackedSeriesPlots,
199
237
  fitPolynomial: () => fitPolynomial,
238
+ formatExponentialEquation: () => formatExponentialEquation,
239
+ formatLinearEquation: () => formatLinearEquation,
240
+ formatLogarithmicEquation: () => formatLogarithmicEquation,
241
+ formatPolynomialEquation: () => formatPolynomialEquation,
242
+ formatPowerEquation: () => formatPowerEquation,
243
+ formatTrendlineCoefficient: () => formatTrendlineCoefficient,
200
244
  groupRowsByCategory: () => groupRowsByCategory,
201
245
  niceAxisStep: () => niceAxisStep,
202
246
  niceValueAxisBounds: () => niceValueAxisBounds,
247
+ nudgeOutsideLabels: () => nudgeOutsideLabels,
248
+ placeBestFitLabel: () => placeBestFitLabel,
249
+ radarAngle: () => radarAngle,
250
+ radarRingPoints: () => radarRingPoints,
203
251
  resolveBlankDisplay: () => resolveBlankDisplay,
204
252
  scottBinCount: () => scottBinCount,
253
+ squarify: () => squarify,
205
254
  visibleRuns: () => visibleRuns
206
255
  });
207
256
 
@@ -585,10 +634,241 @@ function aggregateByCategory(values, rawCategories) {
585
634
  });
586
635
  }
587
636
 
637
+ // chart/treemap-squarify.ts
638
+ function worstAspectRatio(areas, side) {
639
+ const sum = areas.reduce((total, area) => total + area, 0);
640
+ const max = Math.max(...areas);
641
+ const min = Math.min(...areas);
642
+ const sideSq = side * side;
643
+ const sumSq = sum * sum;
644
+ return Math.max(sideSq * max / sumSq, sumSq / (sideSq * min));
645
+ }
646
+ function squarify(nodes, box) {
647
+ const sorted = [...nodes].sort((a, b) => b.weight - a.weight);
648
+ const total = sorted.reduce((sum, node) => sum + node.weight, 0);
649
+ const scale = total > 0 ? box.w * box.h / total : box.w * box.h / sorted.length;
650
+ const items = sorted.map((node) => ({ node, area: (total > 0 ? node.weight : 1) * scale }));
651
+ const result = [];
652
+ let rect2 = { ...box };
653
+ let remaining = items;
654
+ for (let first2 = remaining[0]; first2 !== void 0; first2 = remaining[0]) {
655
+ const side = Math.min(rect2.w, rect2.h);
656
+ let row = [first2];
657
+ let rowAreas = [first2.area];
658
+ let worst = worstAspectRatio(rowAreas, side);
659
+ let cursor = 1;
660
+ for (let next = remaining[cursor]; next !== void 0; next = remaining[cursor]) {
661
+ const candidateAreas = [...rowAreas, next.area];
662
+ const candidateWorst = worstAspectRatio(candidateAreas, side);
663
+ if (candidateWorst > worst) {
664
+ break;
665
+ }
666
+ row = [...row, next];
667
+ rowAreas = candidateAreas;
668
+ worst = candidateWorst;
669
+ cursor++;
670
+ }
671
+ const rowTotalArea = rowAreas.reduce((sum, area) => sum + area, 0);
672
+ const thickness = side > 0 ? rowTotalArea / side : 0;
673
+ const layoutVertically = rect2.w >= rect2.h;
674
+ let offset = layoutVertically ? rect2.y : rect2.x;
675
+ for (const item of row) {
676
+ const length = thickness > 0 ? item.area / thickness : 0;
677
+ if (layoutVertically) {
678
+ result.push([item.node, { x: rect2.x, y: offset, w: thickness, h: length }]);
679
+ } else {
680
+ result.push([item.node, { x: offset, y: rect2.y, w: length, h: thickness }]);
681
+ }
682
+ offset += length;
683
+ }
684
+ rect2 = layoutVertically ? { ...rect2, x: rect2.x + thickness, w: rect2.w - thickness } : { ...rect2, y: rect2.y + thickness, h: rect2.h - thickness };
685
+ remaining = remaining.slice(row.length);
686
+ }
687
+ return result;
688
+ }
689
+
690
+ // chart/trendline-equation.ts
691
+ var SIGNIFICANT_DIGITS = 4;
692
+ function formatTrendlineCoefficient(value) {
693
+ if (!Number.isFinite(value) || value === 0) {
694
+ return "0";
695
+ }
696
+ const rounded = Number(value.toPrecision(SIGNIFICANT_DIGITS));
697
+ return rounded === 0 ? "0" : String(rounded);
698
+ }
699
+ function signedTerm(value) {
700
+ return { sign: value < 0 ? "-" : "+", abs: formatTrendlineCoefficient(Math.abs(value)) };
701
+ }
702
+ function formatLinearEquation(slope, intercept) {
703
+ const { sign, abs } = signedTerm(intercept);
704
+ return `y = ${formatTrendlineCoefficient(slope)}x ${sign} ${abs}`;
705
+ }
706
+ function formatExponentialEquation(a, b) {
707
+ return `y = ${formatTrendlineCoefficient(a)} e^(${formatTrendlineCoefficient(b)}x)`;
708
+ }
709
+ function formatLogarithmicEquation(slope, intercept) {
710
+ const { sign, abs } = signedTerm(intercept);
711
+ return `y = ${formatTrendlineCoefficient(slope)}ln(x) ${sign} ${abs}`;
712
+ }
713
+ function formatPowerEquation(a, b) {
714
+ return `y = ${formatTrendlineCoefficient(a)}x^${formatTrendlineCoefficient(b)}`;
715
+ }
716
+ function formatPolynomialEquation(coeffsAscending) {
717
+ const order = coeffsAscending.length - 1;
718
+ let out = "y = ";
719
+ for (let power = order; power >= 0; power--) {
720
+ const coeff = coeffsAscending[power] ?? 0;
721
+ const { sign, abs } = signedTerm(coeff);
722
+ const variable = power === 0 ? "" : power === 1 ? "x" : `x^${power}`;
723
+ out += power === order ? `${sign === "-" ? "-" : ""}${abs}${variable}` : ` ${sign} ${abs}${variable}`;
724
+ }
725
+ return out;
726
+ }
727
+
728
+ // chart/radar-geometry.ts
729
+ function radarAngle(index, catCount) {
730
+ const n = Math.max(catCount, 1);
731
+ return Math.PI * 2 * index / n - Math.PI / 2;
732
+ }
733
+ function computeRadarPoints(values, maxVal, radius, cx, cy, catCount) {
734
+ const denom = maxVal > 0 ? maxVal : 1;
735
+ return values.slice(0, Math.max(catCount, 1)).map((val2, i) => {
736
+ const angle = radarAngle(i, catCount), r = Math.abs(val2) / denom * radius;
737
+ return { x: cx + r * Math.cos(angle), y: cy + r * Math.sin(angle) };
738
+ });
739
+ }
740
+ function radarRingPoints(cx, cy, rr, catCount) {
741
+ const n = Math.max(catCount, 1);
742
+ return Array.from({ length: n }, (_, i) => {
743
+ const angle = radarAngle(i, n);
744
+ return `${(cx + rr * Math.cos(angle)).toFixed(2)},${(cy + rr * Math.sin(angle)).toFixed(2)}`;
745
+ }).join(" ");
746
+ }
747
+
748
+ // chart/pie-best-fit.ts
749
+ var PX_PER_PT = 4 / 3;
750
+ var BEST_FIT_RIM_INSET = 4 * PX_PER_PT;
751
+ var BEST_FIT_EDGE_TOLERANCE = 1.5 * PX_PER_PT;
752
+ var BEST_FIT_OUTSIDE_GAP = 2 * PX_PER_PT;
753
+ function farCorner(x, y, w, h) {
754
+ return Math.hypot(Math.abs(x) + w / 2, Math.abs(y) + h / 2);
755
+ }
756
+ function insideDistance(ux, uy, w, h, limit) {
757
+ if (farCorner(0, 0, w, h) > limit) {
758
+ return void 0;
759
+ }
760
+ let lo = 0;
761
+ let hi = limit;
762
+ for (let i = 0; i < 40; i++) {
763
+ const mid = (lo + hi) / 2;
764
+ if (farCorner(mid * ux, mid * uy, w, h) > limit) {
765
+ hi = mid;
766
+ } else {
767
+ lo = mid;
768
+ }
769
+ }
770
+ return lo;
771
+ }
772
+ function edgeOverhang(x, y, w, h, slice) {
773
+ let worst = Number.NEGATIVE_INFINITY;
774
+ const corners = [
775
+ [-w / 2, -h / 2],
776
+ [w / 2, -h / 2],
777
+ [-w / 2, h / 2],
778
+ [w / 2, h / 2]
779
+ ];
780
+ for (const [dx, dy] of corners) {
781
+ const px2 = x + dx;
782
+ const py = y + dy;
783
+ const fromStart = Math.cos(slice.startAngle) * py - Math.sin(slice.startAngle) * px2;
784
+ const fromEnd = Math.sin(slice.endAngle) * px2 - Math.cos(slice.endAngle) * py;
785
+ worst = Math.max(worst, -fromStart, -fromEnd);
786
+ }
787
+ return worst;
788
+ }
789
+ function placeBestFitLabel(slice, w, h) {
790
+ const mid = (slice.startAngle + slice.endAngle) / 2;
791
+ const ux = Math.cos(mid);
792
+ const uy = Math.sin(mid);
793
+ const distance = insideDistance(ux, uy, w, h, slice.outerR - BEST_FIT_RIM_INSET);
794
+ if (distance !== void 0) {
795
+ const x = distance * ux;
796
+ const y = distance * uy;
797
+ const reflex = slice.endAngle - slice.startAngle >= Math.PI;
798
+ if (reflex || edgeOverhang(x, y, w, h, slice) <= BEST_FIT_EDGE_TOLERANCE) {
799
+ return { x: slice.cx + x, y: slice.cy + y, inside: true };
800
+ }
801
+ }
802
+ const extent = Math.abs(ux) * w / 2 + Math.abs(uy) * h / 2;
803
+ const outside = slice.outerR + BEST_FIT_OUTSIDE_GAP + extent;
804
+ return { x: slice.cx + outside * ux, y: slice.cy + outside * uy, inside: false };
805
+ }
806
+
807
+ // chart/pie-label-collision.ts
808
+ var LABEL_COLLISION_GAP = 2;
809
+ function boxDistanceToCentre(box, y, disc) {
810
+ const nearestX = Math.max(box.x - box.w / 2, Math.min(disc.cx, box.x + box.w / 2));
811
+ const nearestY = Math.max(y - box.h / 2, Math.min(disc.cy, y + box.h / 2));
812
+ return Math.hypot(nearestX - disc.cx, nearestY - disc.cy);
813
+ }
814
+ function clearOfDisc(box, y, disc) {
815
+ if (boxDistanceToCentre(box, y, disc) >= disc.r) {
816
+ return 0;
817
+ }
818
+ const sign = box.x >= disc.cx ? 1 : -1;
819
+ let dx = 0;
820
+ while (dx < disc.r * 2 && boxDistanceToCentre({ ...box, x: box.x + sign * dx }, y, disc) < disc.r) {
821
+ dx += 1;
822
+ }
823
+ return sign * dx;
824
+ }
825
+ function overlapsX(a, b) {
826
+ return Math.abs(a.x - b.x) < (a.w + b.w) / 2;
827
+ }
828
+ function sweepSide(boxes, minY, maxY) {
829
+ const sorted = [...boxes].sort((a, b) => a.y - b.y || a.id - b.id);
830
+ const ys = sorted.map((box) => box.y);
831
+ for (let i = 1; i < sorted.length; i++) {
832
+ const prev = sorted[i - 1];
833
+ const box = sorted[i];
834
+ if (overlapsX(prev, box)) {
835
+ ys[i] = Math.max(ys[i], ys[i - 1] + (prev.h + box.h) / 2 + LABEL_COLLISION_GAP);
836
+ }
837
+ }
838
+ for (let i = sorted.length - 1; i >= 0; i--) {
839
+ const box = sorted[i];
840
+ const limit = i === sorted.length - 1 ? maxY - box.h / 2 : ys[i + 1] - (box.h + sorted[i + 1].h) / 2 - LABEL_COLLISION_GAP;
841
+ if (ys[i] > limit) {
842
+ ys[i] = Math.max(limit, minY + box.h / 2);
843
+ }
844
+ }
845
+ return new Map(sorted.map((box, i) => [box.id, ys[i]]));
846
+ }
847
+ function nudgeOutsideLabels(boxes, disc, minY, maxY) {
848
+ const resolved = /* @__PURE__ */ new Map();
849
+ for (const side of [
850
+ boxes.filter((box) => box.x >= disc.cx),
851
+ boxes.filter((box) => box.x < disc.cx)
852
+ ]) {
853
+ for (const [id, y] of sweepSide(side, minY, maxY)) {
854
+ resolved.set(id, y);
855
+ }
856
+ }
857
+ return boxes.map((box) => {
858
+ const y = resolved.get(box.id) ?? box.y;
859
+ return { id: box.id, dx: y === box.y ? 0 : clearOfDisc(box, y, disc), dy: y - box.y };
860
+ });
861
+ }
862
+
588
863
  // text/index.ts
589
864
  var text_exports = {};
590
865
  __export(text_exports, {
866
+ buildLeaderString: () => buildLeaderString,
867
+ decimalAnchorIndex: () => decimalAnchorIndex,
868
+ decimalSeparatorForLanguage: () => decimalSeparatorForLanguage,
591
869
  detectFontScript: () => detectFontScript,
870
+ getLeaderCharacter: () => getLeaderCharacter,
871
+ getLeaderStyles: () => getLeaderStyles,
592
872
  hasDistinctScriptFonts: () => hasDistinctScriptFonts,
593
873
  resolveFontForScript: () => resolveFontForScript,
594
874
  segmentByScript: () => segmentByScript
@@ -710,6 +990,73 @@ function hasDistinctScriptFonts(fonts) {
710
990
  return Boolean(fonts.eastAsia) && fonts.eastAsia !== base || Boolean(fonts.complexScript) && fonts.complexScript !== base || Boolean(fonts.symbol) && fonts.symbol !== base;
711
991
  }
712
992
 
993
+ // text/tab-leader.ts
994
+ function getLeaderCharacter(leader) {
995
+ switch (leader) {
996
+ case "dot":
997
+ return ".";
998
+ case "hyphen":
999
+ return "-";
1000
+ case "underscore":
1001
+ return "_";
1002
+ case "heavy":
1003
+ return "\u2501";
1004
+ // box-drawing heavy horizontal
1005
+ case "middleDot":
1006
+ return "\xB7";
1007
+ // middle dot
1008
+ default:
1009
+ return void 0;
1010
+ }
1011
+ }
1012
+ function getLeaderStyles(widthPx, color) {
1013
+ if (widthPx <= 0) {
1014
+ return void 0;
1015
+ }
1016
+ return {
1017
+ display: "inline-block",
1018
+ width: `${widthPx}px`,
1019
+ overflow: "hidden",
1020
+ whiteSpace: "pre",
1021
+ verticalAlign: "baseline",
1022
+ ...color ? { color } : {}
1023
+ };
1024
+ }
1025
+ function buildLeaderString(leader, widthPx, fontSizePx = 16) {
1026
+ const char = getLeaderCharacter(leader);
1027
+ if (!char || widthPx <= 0) {
1028
+ return " ";
1029
+ }
1030
+ const charWidth = Math.max(1, fontSizePx * 0.6);
1031
+ const count = Math.max(1, Math.ceil(widthPx / charWidth));
1032
+ return char.repeat(count);
1033
+ }
1034
+
1035
+ // text/decimal-tab.ts
1036
+ function decimalSeparatorForLanguage(language) {
1037
+ if (!language) {
1038
+ return ".";
1039
+ }
1040
+ try {
1041
+ const part = new Intl.NumberFormat(language).formatToParts(1.5).find((p) => p.type === "decimal");
1042
+ return part?.value ?? ".";
1043
+ } catch {
1044
+ return ".";
1045
+ }
1046
+ }
1047
+ function decimalAnchorIndex(text, separator) {
1048
+ const first2 = text.search(/[0-9]/u);
1049
+ if (first2 < 0) {
1050
+ return text.length;
1051
+ }
1052
+ let end = first2;
1053
+ while (end < text.length && /[0-9.,]/u.test(text.charAt(end))) {
1054
+ end++;
1055
+ }
1056
+ const sep = text.slice(first2, end).indexOf(separator);
1057
+ return sep >= 0 ? first2 + sep : end;
1058
+ }
1059
+
713
1060
  // diagram/index.ts
714
1061
  var diagram_exports = {};
715
1062
  __export(diagram_exports, {
@@ -3442,6 +3789,7 @@ __export(geometry_exports, {
3442
3789
  RIBBON_CONNECTION_SITES: () => RIBBON_CONNECTION_SITES,
3443
3790
  RIBBON_TEXT_RECTS: () => RIBBON_TEXT_RECTS,
3444
3791
  SCROLLS_ACCENT_CALLOUTS_PRESET_DEFINITIONS: () => SCROLLS_ACCENT_CALLOUTS_PRESET_DEFINITIONS,
3792
+ SNAP_THRESHOLD: () => SNAP_THRESHOLD,
3445
3793
  STAR_CONNECTION_SITES: () => STAR_CONNECTION_SITES,
3446
3794
  STAR_TEXT_RECTS: () => STAR_TEXT_RECTS,
3447
3795
  ST_SHAPE_TYPE_VALUES: () => ST_SHAPE_TYPE_VALUES,
@@ -3449,17 +3797,23 @@ __export(geometry_exports, {
3449
3797
  TAB_CONNECTION_SITES: () => TAB_CONNECTION_SITES,
3450
3798
  TAB_TEXT_RECTS: () => TAB_TEXT_RECTS,
3451
3799
  _internalForTests: () => _internalForTests,
3800
+ alignElements: () => alignElements,
3452
3801
  angleToRadians: () => angleToRadians,
3453
3802
  at: () => at,
3454
3803
  buildCalloutLeaderLineSvgPath: () => buildCalloutLeaderLineSvgPath,
3455
3804
  clipPolygons: () => clipPolygons,
3456
3805
  combineShapes: () => combineShapes,
3806
+ computeAlign: () => computeAlign,
3807
+ computeDistribute: () => computeDistribute,
3808
+ computeSnap: () => computeSnap,
3809
+ computeSnapToShape: () => computeSnapToShape,
3457
3810
  computeUnionWalk: () => computeUnionWalk,
3458
3811
  convexHull: () => convexHull,
3459
3812
  createBuiltinVariables: () => createBuiltinVariables,
3460
3813
  cross2: () => cross2,
3461
3814
  cxn: () => cxn,
3462
3815
  dedupPoly: () => dedupPoly,
3816
+ distributeElements: () => distributeElements,
3463
3817
  elbowCurveSegments: () => elbowCurveSegments,
3464
3818
  elbowWaypoints: () => elbowWaypoints,
3465
3819
  ensureCCW: () => ensureCCW,
@@ -3503,10 +3857,14 @@ __export(geometry_exports, {
3503
3857
  pointInTriangle: () => pointInTriangle,
3504
3858
  polygonArea: () => polygonArea,
3505
3859
  polygonsToSvgPath: () => polygonsToSvgPath,
3860
+ resolveAlignReferenceBox: () => resolveAlignReferenceBox,
3506
3861
  resolveCoordinate: () => resolveCoordinate,
3507
3862
  resolveOperand: () => resolveOperand,
3508
3863
  segmentIntersection: () => segmentIntersection,
3509
3864
  signedArea: () => signedArea,
3865
+ snapBox: () => snapBox,
3866
+ snapToGridStep: () => snapToGridStep,
3867
+ snapValue: () => snapValue,
3510
3868
  subtractPolygons: () => subtractPolygons,
3511
3869
  subtractShapes: () => subtractShapes,
3512
3870
  subtractSvgPaths: () => subtractSvgPaths,
@@ -3516,6 +3874,7 @@ __export(geometry_exports, {
3516
3874
  unionPolygons: () => unionPolygons,
3517
3875
  unionShapes: () => unionShapes,
3518
3876
  unionSvgPaths: () => unionSvgPaths,
3877
+ usesSlideReference: () => usesSlideReference,
3519
3878
  vec2Eq: () => vec2Eq
3520
3879
  });
3521
3880
 
@@ -18606,6 +18965,312 @@ function flattenSvgPath(d, curveSegments = 16) {
18606
18965
  return loops;
18607
18966
  }
18608
18967
 
18968
+ // geometry/snap-guides.ts
18969
+ var SNAP_THRESHOLD = 6;
18970
+ function computeSnapToShape(dragX, dragY, dragW, dragH, siblings, draggedIds, guides, threshold = SNAP_THRESHOLD) {
18971
+ let bestDx = Infinity;
18972
+ let bestDy = Infinity;
18973
+ let snapX = dragX;
18974
+ let snapY = dragY;
18975
+ const lines = [];
18976
+ const dragCx = dragX + dragW / 2;
18977
+ const dragCy = dragY + dragH / 2;
18978
+ const dragRight = dragX + dragW;
18979
+ const dragBottom = dragY + dragH;
18980
+ const hRefs = [dragX, dragCx, dragRight];
18981
+ const vRefs = [dragY, dragCy, dragBottom];
18982
+ for (const sib of siblings) {
18983
+ if (draggedIds.has(sib.id)) {
18984
+ continue;
18985
+ }
18986
+ const sibCx = sib.x + sib.width / 2;
18987
+ const sibCy = sib.y + sib.height / 2;
18988
+ const sibRight = sib.x + sib.width;
18989
+ const sibBottom = sib.y + sib.height;
18990
+ for (const ref of [sib.x, sibCx, sibRight]) {
18991
+ for (const hr of hRefs) {
18992
+ const dx = Math.abs(ref - hr);
18993
+ if (dx < threshold && dx < bestDx) {
18994
+ bestDx = dx;
18995
+ snapX = dragX + (ref - hr);
18996
+ }
18997
+ }
18998
+ }
18999
+ for (const ref of [sib.y, sibCy, sibBottom]) {
19000
+ for (const vr of vRefs) {
19001
+ const dy = Math.abs(ref - vr);
19002
+ if (dy < threshold && dy < bestDy) {
19003
+ bestDy = dy;
19004
+ snapY = dragY + (ref - vr);
19005
+ }
19006
+ }
19007
+ }
19008
+ }
19009
+ for (const guide of guides) {
19010
+ if (guide.axis === "v") {
19011
+ for (const hr of hRefs) {
19012
+ const dx = Math.abs(guide.position - hr);
19013
+ if (dx < threshold && dx < bestDx) {
19014
+ bestDx = dx;
19015
+ snapX = dragX + (guide.position - hr);
19016
+ }
19017
+ }
19018
+ } else {
19019
+ for (const vr of vRefs) {
19020
+ const dy = Math.abs(guide.position - vr);
19021
+ if (dy < threshold && dy < bestDy) {
19022
+ bestDy = dy;
19023
+ snapY = dragY + (guide.position - vr);
19024
+ }
19025
+ }
19026
+ }
19027
+ }
19028
+ if (bestDx < threshold) {
19029
+ const snappedCx = snapX + dragW / 2;
19030
+ const snappedRight = snapX + dragW;
19031
+ for (const sib of siblings) {
19032
+ if (draggedIds.has(sib.id)) {
19033
+ continue;
19034
+ }
19035
+ for (const ref of [sib.x, sib.x + sib.width / 2, sib.x + sib.width]) {
19036
+ if (Math.abs(ref - snapX) < 1 || Math.abs(ref - snappedCx) < 1 || Math.abs(ref - snappedRight) < 1) {
19037
+ lines.push({ axis: "v", position: ref });
19038
+ }
19039
+ }
19040
+ }
19041
+ for (const g of guides) {
19042
+ if (g.axis === "v" && (Math.abs(g.position - snapX) < 1 || Math.abs(g.position - snappedCx) < 1 || Math.abs(g.position - snappedRight) < 1)) {
19043
+ lines.push({ axis: "v", position: g.position });
19044
+ }
19045
+ }
19046
+ }
19047
+ if (bestDy < threshold) {
19048
+ const snappedCy = snapY + dragH / 2;
19049
+ const snappedBottom = snapY + dragH;
19050
+ for (const sib of siblings) {
19051
+ if (draggedIds.has(sib.id)) {
19052
+ continue;
19053
+ }
19054
+ for (const ref of [sib.y, sib.y + sib.height / 2, sib.y + sib.height]) {
19055
+ if (Math.abs(ref - snapY) < 1 || Math.abs(ref - snappedCy) < 1 || Math.abs(ref - snappedBottom) < 1) {
19056
+ lines.push({ axis: "h", position: ref });
19057
+ }
19058
+ }
19059
+ }
19060
+ for (const g of guides) {
19061
+ if (g.axis === "h" && (Math.abs(g.position - snapY) < 1 || Math.abs(g.position - snappedCy) < 1 || Math.abs(g.position - snappedBottom) < 1)) {
19062
+ lines.push({ axis: "h", position: g.position });
19063
+ }
19064
+ }
19065
+ }
19066
+ return { x: snapX, y: snapY, lines };
19067
+ }
19068
+ function xCandidates(box) {
19069
+ return {
19070
+ leading: box.x,
19071
+ centre: box.x + box.width / 2,
19072
+ trailing: box.x + box.width
19073
+ };
19074
+ }
19075
+ function yCandidates(box) {
19076
+ return {
19077
+ leading: box.y,
19078
+ centre: box.y + box.height / 2,
19079
+ trailing: box.y + box.height
19080
+ };
19081
+ }
19082
+ function findSnapHit(movingValues, others, axisCandidates, threshold) {
19083
+ let best = null;
19084
+ let bestDist = threshold + 1;
19085
+ for (const other of others) {
19086
+ const target = axisCandidates(other);
19087
+ const targetValues = [target.leading, target.centre, target.trailing];
19088
+ for (const mv of movingValues) {
19089
+ for (const tv of targetValues) {
19090
+ const dist = mv > tv ? mv - tv : tv - mv;
19091
+ if (dist <= threshold && dist < bestDist) {
19092
+ bestDist = dist;
19093
+ best = { delta: tv - mv, matchedLine: tv, otherBox: other };
19094
+ }
19095
+ }
19096
+ }
19097
+ }
19098
+ return best;
19099
+ }
19100
+ function unionExtent(aStart, aEnd, bStart, bEnd) {
19101
+ const start = aStart < bStart ? aStart : bStart;
19102
+ const end = aEnd > bEnd ? aEnd : bEnd;
19103
+ return { start, end };
19104
+ }
19105
+ function computeSnap(box, others, threshold) {
19106
+ const guides = [];
19107
+ let snappedX = box.x;
19108
+ let snappedY = box.y;
19109
+ const xMoving = xCandidates(box);
19110
+ const hitX = findSnapHit(
19111
+ [xMoving.leading, xMoving.centre, xMoving.trailing],
19112
+ others,
19113
+ xCandidates,
19114
+ threshold
19115
+ );
19116
+ if (hitX !== null) {
19117
+ snappedX = box.x + hitX.delta;
19118
+ const { start, end } = unionExtent(
19119
+ box.y,
19120
+ box.y + box.height,
19121
+ hitX.otherBox.y,
19122
+ hitX.otherBox.y + hitX.otherBox.height
19123
+ );
19124
+ guides.push({ axis: "x", pos: hitX.matchedLine, start, end });
19125
+ }
19126
+ const yMoving = yCandidates(box);
19127
+ const hitY = findSnapHit(
19128
+ [yMoving.leading, yMoving.centre, yMoving.trailing],
19129
+ others,
19130
+ yCandidates,
19131
+ threshold
19132
+ );
19133
+ if (hitY !== null) {
19134
+ snappedY = box.y + hitY.delta;
19135
+ const { start, end } = unionExtent(
19136
+ snappedX,
19137
+ snappedX + box.width,
19138
+ hitY.otherBox.x,
19139
+ hitY.otherBox.x + hitY.otherBox.width
19140
+ );
19141
+ guides.push({ axis: "y", pos: hitY.matchedLine, start, end });
19142
+ }
19143
+ return { x: snappedX, y: snappedY, guides };
19144
+ }
19145
+ function snapToGridStep(value, step) {
19146
+ if (step <= 0) {
19147
+ return value;
19148
+ }
19149
+ return Math.round(value / step) * step || 0;
19150
+ }
19151
+ function snapValue(value, size) {
19152
+ return Math.round(value / size) * size;
19153
+ }
19154
+ function snapBox(box, size) {
19155
+ return {
19156
+ x: snapValue(box.x, size),
19157
+ y: snapValue(box.y, size),
19158
+ width: Math.max(size, snapValue(box.width, size)),
19159
+ height: Math.max(size, snapValue(box.height, size))
19160
+ };
19161
+ }
19162
+
19163
+ // geometry/align-distribute.ts
19164
+ function usesSlideReference(elementCount, options) {
19165
+ if (!options?.slideSize) {
19166
+ return false;
19167
+ }
19168
+ return options.reference === "slide" || options.reference === void 0 && elementCount === 1;
19169
+ }
19170
+ function resolveAlignReferenceBox(elements2, options) {
19171
+ if (elements2.length === 0) {
19172
+ return null;
19173
+ }
19174
+ if (usesSlideReference(elements2.length, options)) {
19175
+ return { minX: 0, minY: 0, maxX: options.slideSize.width, maxY: options.slideSize.height };
19176
+ }
19177
+ if (elements2.length < 2) {
19178
+ return null;
19179
+ }
19180
+ return {
19181
+ minX: Math.min(...elements2.map((e) => e.x)),
19182
+ minY: Math.min(...elements2.map((e) => e.y)),
19183
+ maxX: Math.max(...elements2.map((e) => e.x + e.width)),
19184
+ maxY: Math.max(...elements2.map((e) => e.y + e.height))
19185
+ };
19186
+ }
19187
+ function alignElements(elements2, edge, options) {
19188
+ const result = /* @__PURE__ */ new Map();
19189
+ const ref = resolveAlignReferenceBox(elements2, options);
19190
+ if (!ref) {
19191
+ return result;
19192
+ }
19193
+ const { minX, minY, maxX, maxY } = ref;
19194
+ const centerX = (minX + maxX) / 2;
19195
+ const centerY = (minY + maxY) / 2;
19196
+ for (const el of elements2) {
19197
+ switch (edge) {
19198
+ case "left":
19199
+ result.set(el.id, { x: minX });
19200
+ break;
19201
+ case "right":
19202
+ result.set(el.id, { x: maxX - el.width });
19203
+ break;
19204
+ case "centerH":
19205
+ result.set(el.id, { x: centerX - el.width / 2 });
19206
+ break;
19207
+ case "top":
19208
+ result.set(el.id, { y: minY });
19209
+ break;
19210
+ case "bottom":
19211
+ result.set(el.id, { y: maxY - el.height });
19212
+ break;
19213
+ case "middle":
19214
+ result.set(el.id, { y: centerY - el.height / 2 });
19215
+ break;
19216
+ }
19217
+ }
19218
+ return result;
19219
+ }
19220
+ function distributeElements(elements2, axis, options) {
19221
+ const result = /* @__PURE__ */ new Map();
19222
+ const horizontal = axis === "horizontal";
19223
+ const start = (e) => horizontal ? e.x : e.y;
19224
+ const size = (e) => horizontal ? e.width : e.height;
19225
+ const sorted = [...elements2].sort((a, b) => start(a) - start(b));
19226
+ const totalSize = sorted.reduce((sum, e) => sum + size(e), 0);
19227
+ let cursor;
19228
+ let gap;
19229
+ if (usesSlideReference(elements2.length, options)) {
19230
+ if (sorted.length === 0) {
19231
+ return result;
19232
+ }
19233
+ const span = horizontal ? options.slideSize.width : options.slideSize.height;
19234
+ gap = (span - totalSize) / (sorted.length + 1);
19235
+ cursor = gap;
19236
+ } else {
19237
+ if (sorted.length < 3) {
19238
+ return result;
19239
+ }
19240
+ const first2 = sorted[0];
19241
+ const last = sorted[sorted.length - 1];
19242
+ const span = start(last) + size(last) - start(first2);
19243
+ gap = (span - totalSize) / (sorted.length - 1);
19244
+ cursor = start(first2);
19245
+ }
19246
+ for (const el of sorted) {
19247
+ result.set(el.id, horizontal ? { x: cursor } : { y: cursor });
19248
+ cursor += size(el) + gap;
19249
+ }
19250
+ return result;
19251
+ }
19252
+ function onlyMoved(boxes, positions) {
19253
+ const byId = new Map(boxes.map((box) => [box.id, box]));
19254
+ const result = /* @__PURE__ */ new Map();
19255
+ for (const [id, pos] of positions) {
19256
+ const box = byId.get(id);
19257
+ if (!box) {
19258
+ continue;
19259
+ }
19260
+ const moved = pos.x !== void 0 && pos.x !== box.x || pos.y !== void 0 && pos.y !== box.y;
19261
+ if (moved) {
19262
+ result.set(id, pos);
19263
+ }
19264
+ }
19265
+ return result;
19266
+ }
19267
+ function computeAlign(boxes, mode, options) {
19268
+ return onlyMoved(boxes, alignElements(boxes, mode, options));
19269
+ }
19270
+ function computeDistribute(boxes, mode, options) {
19271
+ return onlyMoved(boxes, distributeElements(boxes, mode, options));
19272
+ }
19273
+
18609
19274
  // units/index.ts
18610
19275
  var units_exports = {};
18611
19276
  __export(units_exports, {