semajsx 0.7.0 → 0.9.0

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 (47) hide show
  1. package/dist/{client-BrupjhG0.mjs → client-CEJQ4fit.mjs} +3 -3
  2. package/dist/{client-BrupjhG0.mjs.map → client-CEJQ4fit.mjs.map} +1 -1
  3. package/dist/{document-DsiJO2jG.mjs → document-Cbz4084O.mjs} +2 -2
  4. package/dist/{document-XKyAs62C.mjs → document-Cfdhi7vG.mjs} +2 -2
  5. package/dist/{document-XKyAs62C.mjs.map → document-Cfdhi7vG.mjs.map} +1 -1
  6. package/dist/dom/index.mjs +2 -2
  7. package/dist/dom/jsx-dev-runtime.mjs +1 -1
  8. package/dist/dom/jsx-runtime.mjs +1 -1
  9. package/dist/index.d.mts +11 -0
  10. package/dist/index.d.mts.map +1 -1
  11. package/dist/index.mjs +1 -1
  12. package/dist/{jsx-runtime-Dc77fsnM.d.mts → jsx-runtime-tdaY-P9K.d.mts} +2 -2
  13. package/dist/{jsx-runtime-Dc77fsnM.d.mts.map → jsx-runtime-tdaY-P9K.d.mts.map} +1 -1
  14. package/dist/{lucide-Ddt_N9dJ.mjs → lucide-DWk3itzO.mjs} +3 -3
  15. package/dist/{lucide-Ddt_N9dJ.mjs.map → lucide-DWk3itzO.mjs.map} +1 -1
  16. package/dist/{resource-pm7qP-jV.mjs → resource-BU0Po0ez.mjs} +2 -2
  17. package/dist/{resource-pm7qP-jV.mjs.map → resource-BU0Po0ez.mjs.map} +1 -1
  18. package/dist/{src-Cv4rRVzv.mjs → src--YS4EvMz.mjs} +9 -6
  19. package/dist/src--YS4EvMz.mjs.map +1 -0
  20. package/dist/{src-CXY-7FC3.mjs → src-77V1Plyd.mjs} +665 -129
  21. package/dist/src-77V1Plyd.mjs.map +1 -0
  22. package/dist/{src-SqJ6k7Xv.mjs → src-BTG08Qnh.mjs} +4 -4
  23. package/dist/{src-SqJ6k7Xv.mjs.map → src-BTG08Qnh.mjs.map} +1 -1
  24. package/dist/{src-C_aFsFJ3.mjs → src-Cm12Y2XV.mjs} +2 -2
  25. package/dist/{src-C_aFsFJ3.mjs.map → src-Cm12Y2XV.mjs.map} +1 -1
  26. package/dist/{src-CAyv9Uf9.mjs → src-Mucdq4zw.mjs} +6 -6
  27. package/dist/{src-CAyv9Uf9.mjs.map → src-Mucdq4zw.mjs.map} +1 -1
  28. package/dist/ssg/index.mjs +6 -6
  29. package/dist/ssg/plugins/docs-theme.d.mts +8 -0
  30. package/dist/ssg/plugins/docs-theme.d.mts.map +1 -1
  31. package/dist/ssg/plugins/docs-theme.mjs +1039 -222
  32. package/dist/ssg/plugins/docs-theme.mjs.map +1 -1
  33. package/dist/ssg/plugins/lucide.mjs +3 -3
  34. package/dist/ssr/client.mjs +4 -4
  35. package/dist/ssr/index.mjs +5 -5
  36. package/dist/terminal/index.d.mts +248 -4
  37. package/dist/terminal/index.d.mts.map +1 -1
  38. package/dist/terminal/index.mjs +3 -3
  39. package/dist/terminal/jsx-dev-runtime.d.mts +2 -2
  40. package/dist/terminal/jsx-dev-runtime.mjs +1 -1
  41. package/dist/terminal/jsx-runtime.d.mts +2 -2
  42. package/dist/terminal/jsx-runtime.mjs +1 -1
  43. package/dist/{types-Bj5q5x2Q.d.mts → types-Bm8rZGKW.d.mts} +2 -2
  44. package/dist/{types-Bj5q5x2Q.d.mts.map → types-Bm8rZGKW.d.mts.map} +1 -1
  45. package/package.json +1 -1
  46. package/dist/src-CXY-7FC3.mjs.map +0 -1
  47. package/dist/src-Cv4rRVzv.mjs.map +0 -1
@@ -1,17 +1,17 @@
1
1
  import { t as signal } from "../../signal-4PgGfydw.mjs";
2
2
  import { n as unwrap } from "../../utils-BrGmTgfG.mjs";
3
- import { a as jsxs, i as jsx, u as context, v as Fragment } from "../../src-Cv4rRVzv.mjs";
4
- import "../../src-C_aFsFJ3.mjs";
3
+ import { a as jsxs, i as jsx, u as context, v as Fragment } from "../../src--YS4EvMz.mjs";
4
+ import "../../src-Cm12Y2XV.mjs";
5
5
  import { G as rules, K as classes, M as createTheme, N as defineTokens, W as rule } from "../../src-DV9uwtE5.mjs";
6
6
  import "../../jsx-runtime-BFs1c0xz.mjs";
7
- import "../../src-CXY-7FC3.mjs";
7
+ import "../../src-77V1Plyd.mjs";
8
8
  import "../../jsx-runtime-kv_6vBiR.mjs";
9
- import { d as island } from "../../resource-pm7qP-jV.mjs";
10
- import "../../src-CAyv9Uf9.mjs";
11
- import "../../document-XKyAs62C.mjs";
12
- import "../../client-BrupjhG0.mjs";
13
- import { d as defineCollection } from "../../src-SqJ6k7Xv.mjs";
14
- import { n as Icon, t as lucide } from "../../lucide-Ddt_N9dJ.mjs";
9
+ import { d as island } from "../../resource-BU0Po0ez.mjs";
10
+ import "../../src-Mucdq4zw.mjs";
11
+ import "../../document-Cfdhi7vG.mjs";
12
+ import "../../client-CEJQ4fit.mjs";
13
+ import { d as defineCollection } from "../../src-BTG08Qnh.mjs";
14
+ import { n as Icon, t as lucide } from "../../lucide-DWk3itzO.mjs";
15
15
  import { dirname, join } from "path";
16
16
  import { mkdir, writeFile } from "fs/promises";
17
17
  import { z } from "zod";
@@ -464,6 +464,7 @@ function parseNodeRef(state) {
464
464
  const shape = parseNodeShape(state, id);
465
465
  if (shape) {
466
466
  if (!state.nodes.has(id)) state.nodes.set(id, shape);
467
+ state.nodeRefCollector?.push(id);
467
468
  return id;
468
469
  }
469
470
  }
@@ -472,6 +473,7 @@ function parseNodeRef(state) {
472
473
  label: id,
473
474
  shape: "rect"
474
475
  });
476
+ state.nodeRefCollector?.push(id);
475
477
  return id;
476
478
  }
477
479
  function parseNodeShape(state, id) {
@@ -607,7 +609,8 @@ function parseSubgraph(state) {
607
609
  }
608
610
  skipSemicolon(state);
609
611
  }
610
- const nodesBefore = new Set(state.nodes.keys());
612
+ const childSubgraphs = [];
613
+ const childNodeIds = /* @__PURE__ */ new Set();
611
614
  const nodeIds = [];
612
615
  while (!isEof$1(state)) {
613
616
  skipNewlines$1(state);
@@ -617,18 +620,39 @@ function parseSubgraph(state) {
617
620
  advance$1(state);
618
621
  break;
619
622
  }
623
+ if (token.type === "keyword" && token.value === "subgraph") {
624
+ const sgCountBefore = state.subgraphs.length;
625
+ const result = parseSubgraph(state);
626
+ if ("message" in result) return result;
627
+ if (state.subgraphs.length > sgCountBefore) {
628
+ const child = state.subgraphs.pop();
629
+ childSubgraphs.push(child);
630
+ collectAllNodes(child, childNodeIds);
631
+ }
632
+ continue;
633
+ }
634
+ state.nodeRefCollector = [];
620
635
  const result = parseStatement(state);
636
+ const refs = state.nodeRefCollector;
637
+ state.nodeRefCollector = void 0;
621
638
  if (result && "message" in result) return result;
622
- for (const [nodeId] of state.nodes) if (!nodesBefore.has(nodeId) && !nodeIds.includes(nodeId)) nodeIds.push(nodeId);
639
+ for (const nodeId of refs) if (!childNodeIds.has(nodeId) && !nodeIds.includes(nodeId)) nodeIds.push(nodeId);
623
640
  }
624
- state.subgraphs.push({
641
+ const sg = {
625
642
  id: id || `subgraph_${state.subgraphs.length}`,
626
643
  label,
627
644
  nodes: nodeIds,
628
645
  direction
629
- });
646
+ };
647
+ if (childSubgraphs.length > 0) sg.subgraphs = childSubgraphs;
648
+ state.subgraphs.push(sg);
630
649
  return { ok: true };
631
650
  }
651
+ /** Recursively collect all node IDs owned by a subgraph and its children. */
652
+ function collectAllNodes(sg, out) {
653
+ for (const id of sg.nodes) out.add(id);
654
+ for (const child of sg.subgraphs ?? []) collectAllNodes(child, out);
655
+ }
632
656
  function parseArrow(arrow) {
633
657
  const raw = arrow.endsWith("|") ? arrow.slice(0, -1) : arrow;
634
658
  let sourceMarker = "none";
@@ -713,7 +737,8 @@ function parseSequence(input) {
713
737
  participants: /* @__PURE__ */ new Map(),
714
738
  messages: [],
715
739
  blocks: [],
716
- notes: []
740
+ notes: [],
741
+ noteMessageCounts: []
717
742
  };
718
743
  skipNewlines(state);
719
744
  const header = peek(state);
@@ -762,7 +787,8 @@ function parseSequence(input) {
762
787
  participants: Array.from(state.participants.values()),
763
788
  messages: state.messages,
764
789
  blocks: state.blocks,
765
- notes: state.notes
790
+ notes: state.notes,
791
+ _noteMessageCounts: state.noteMessageCounts
766
792
  };
767
793
  }
768
794
  function parseParticipant(state, type) {
@@ -851,6 +877,7 @@ function parseNote(state) {
851
877
  advance(state);
852
878
  text = readRestOfLine(state);
853
879
  }
880
+ state.noteMessageCounts.push(state.messages.length);
854
881
  state.notes.push({
855
882
  position,
856
883
  participants,
@@ -900,11 +927,16 @@ function parseBlock(state) {
900
927
  parseNote(state);
901
928
  continue;
902
929
  }
930
+ if (token.type === "keyword" && isBlockKeyword(token.value)) {
931
+ const result = parseBlock(state);
932
+ if ("message" in result) return result;
933
+ continue;
934
+ }
903
935
  if (token.type === "identifier" || token.type === "keyword") {
904
936
  const beforeLen = state.messages.length;
905
937
  parseMessage(state);
906
938
  if (state.messages.length > beforeLen) {
907
- const msg = state.messages.pop();
939
+ const msg = state.messages[state.messages.length - 1];
908
940
  if (currentSection) currentSection.messages.push(msg);
909
941
  else messages.push(msg);
910
942
  }
@@ -1057,6 +1089,30 @@ const tokens$1 = defineTokens(tokenDefinition$1);
1057
1089
  */
1058
1090
  const defaultTheme = createTheme(tokens$1);
1059
1091
 
1092
+ //#endregion
1093
+ //#region ../mermaid/src/base.style.ts
1094
+ /**
1095
+ * Common SVG text label properties.
1096
+ * Produces: fill, stroke:none, font-family, font-size, text-anchor, dominant-baseline.
1097
+ */
1098
+ function textLabel(fill, fontSize) {
1099
+ return `fill: ${fill};
1100
+ stroke: none;
1101
+ font-family: ${tokens$1.fontFamily};
1102
+ font-size: ${fontSize};
1103
+ text-anchor: middle;
1104
+ dominant-baseline: central;`;
1105
+ }
1106
+ /**
1107
+ * Common SVG box/container properties.
1108
+ * Produces: fill, stroke, stroke-width.
1109
+ */
1110
+ function boxShape(fill, stroke, strokeWidth = 1) {
1111
+ return `fill: ${fill};
1112
+ stroke: ${stroke};
1113
+ stroke-width: ${strokeWidth};`;
1114
+ }
1115
+
1060
1116
  //#endregion
1061
1117
  //#region ../mermaid/src/edge.style.ts
1062
1118
  const c$14 = classes([
@@ -1103,12 +1159,7 @@ const edgeThick = rule`${c$14.edgeThick} {
1103
1159
  stroke-width: 3;
1104
1160
  }`;
1105
1161
  const edgeLabel = rule`${c$14.edgeLabel} {
1106
- fill: ${tokens$1.edgeLabelText};
1107
- stroke: none;
1108
- font-family: ${tokens$1.fontFamily};
1109
- font-size: 12px;
1110
- text-anchor: middle;
1111
- dominant-baseline: central;
1162
+ ${textLabel(tokens$1.edgeLabelText, "12px")}
1112
1163
  }`;
1113
1164
  const edgeLabelBg = rule`${c$14.edgeLabelBg} {
1114
1165
  fill: ${tokens$1.edgeLabelBg};
@@ -1267,9 +1318,7 @@ const c$13 = classes([
1267
1318
  "nodeLabel"
1268
1319
  ]);
1269
1320
  const nodeShape = rule`${c$13.nodeShape} {
1270
- fill: ${tokens$1.nodeFill};
1271
- stroke: ${tokens$1.nodeStroke};
1272
- stroke-width: ${tokens$1.edgeWidth};
1321
+ ${boxShape(tokens$1.nodeFill, tokens$1.nodeStroke, tokens$1.edgeWidth)}
1273
1322
  }`;
1274
1323
  const nodeShapeHover = rule`${c$13.nodeShape} {
1275
1324
  transition: filter 0.15s ease;
@@ -1279,12 +1328,7 @@ ${c$13.nodeShape}:hover {
1279
1328
  filter: brightness(0.93);
1280
1329
  }`;
1281
1330
  const nodeLabel = rule`${c$13.nodeLabel} {
1282
- fill: ${tokens$1.nodeText};
1283
- stroke: none;
1284
- font-family: ${tokens$1.fontFamily};
1285
- font-size: ${tokens$1.fontSize}px;
1286
- text-anchor: middle;
1287
- dominant-baseline: central;
1331
+ ${textLabel(tokens$1.nodeText, `${tokens$1.fontSize}px`)}
1288
1332
  pointer-events: none;
1289
1333
  }`;
1290
1334
 
@@ -1583,18 +1627,42 @@ const shapeMap = {
1583
1627
  };
1584
1628
 
1585
1629
  //#endregion
1586
- //#region ../mermaid/src/components/edge.tsx
1587
- const LINE_STYLE = {
1588
- solid: void 0,
1589
- dotted: edgeDotted,
1590
- thick: edgeThick
1591
- };
1630
+ //#region ../mermaid/src/components/markers.ts
1631
+ /** SVG marker URL by EdgeMarker type (used by flowchart edges). */
1592
1632
  const MARKER_URL = {
1593
1633
  arrow: "url(#mmd-arrow)",
1594
1634
  dot: "url(#mmd-dot)",
1595
1635
  cross: "url(#mmd-cross)",
1596
1636
  none: void 0
1597
1637
  };
1638
+ /**
1639
+ * Resolve an ArrowType (sequence diagram) to a marker URL.
1640
+ * This bridges the sequence-specific enum to the shared marker definitions in defs.tsx.
1641
+ */
1642
+ const SEQUENCE_MARKER = {
1643
+ solid: "url(#mmd-arrow)",
1644
+ dotted: "url(#mmd-arrow)",
1645
+ solidCross: "url(#mmd-cross)",
1646
+ dottedCross: "url(#mmd-cross)",
1647
+ solidOpen: "url(#mmd-arrow-open)",
1648
+ dottedOpen: "url(#mmd-arrow-open)"
1649
+ };
1650
+ /** Get marker-end URL for a sequence message arrow type. */
1651
+ function sequenceMarker(arrow) {
1652
+ return SEQUENCE_MARKER[arrow];
1653
+ }
1654
+ /** Check if an ArrowType renders with a dashed stroke. */
1655
+ function isDottedArrow(arrow) {
1656
+ return arrow === "dotted" || arrow === "dottedCross" || arrow === "dottedOpen";
1657
+ }
1658
+
1659
+ //#endregion
1660
+ //#region ../mermaid/src/components/edge.tsx
1661
+ const LINE_STYLE = {
1662
+ solid: void 0,
1663
+ dotted: edgeDotted,
1664
+ thick: edgeThick
1665
+ };
1598
1666
  function Edge(props) {
1599
1667
  const { edge, path, labelPosition, labelSize } = props.positioned;
1600
1668
  const lineStyle = LINE_STYLE[edge.lineStyle];
@@ -1634,9 +1702,7 @@ function Edge(props) {
1634
1702
  //#region ../mermaid/src/subgraph.style.ts
1635
1703
  const c$12 = classes(["subgraphBg", "subgraphTitle"]);
1636
1704
  const subgraphBg = rule`${c$12.subgraphBg} {
1637
- fill: ${tokens$1.subgraphFill};
1638
- stroke: ${tokens$1.subgraphStroke};
1639
- stroke-width: 1;
1705
+ ${boxShape(tokens$1.subgraphFill, tokens$1.subgraphStroke, 1)}
1640
1706
  }`;
1641
1707
  const subgraphTitle = rule`${c$12.subgraphTitle} {
1642
1708
  fill: ${tokens$1.subgraphTitleText};
@@ -1666,6 +1732,171 @@ function SubgraphBox(props) {
1666
1732
  });
1667
1733
  }
1668
1734
 
1735
+ //#endregion
1736
+ //#region ../mermaid/src/layout/measure.ts
1737
+ /**
1738
+ * Per-character width multipliers relative to fontSize, derived from
1739
+ * font metrics of common sans-serif typefaces (Helvetica / Arial).
1740
+ * Covers ASCII printable range; anything outside falls back to a
1741
+ * sensible default (0.55 for Latin, 1.0 for CJK / fullwidth).
1742
+ */
1743
+ const CHAR_WIDTH = {
1744
+ " ": .28,
1745
+ "!": .28,
1746
+ "'": .19,
1747
+ ",": .28,
1748
+ ".": .28,
1749
+ ":": .28,
1750
+ ";": .28,
1751
+ i: .22,
1752
+ j: .22,
1753
+ l: .22,
1754
+ f: .28,
1755
+ r: .33,
1756
+ t: .33,
1757
+ I: .22,
1758
+ "|": .22,
1759
+ a: .5,
1760
+ b: .55,
1761
+ c: .5,
1762
+ d: .55,
1763
+ e: .5,
1764
+ g: .55,
1765
+ h: .55,
1766
+ k: .5,
1767
+ n: .55,
1768
+ o: .55,
1769
+ p: .55,
1770
+ q: .55,
1771
+ s: .5,
1772
+ u: .55,
1773
+ v: .5,
1774
+ x: .5,
1775
+ y: .5,
1776
+ z: .5,
1777
+ "0": .55,
1778
+ "1": .55,
1779
+ "2": .55,
1780
+ "3": .55,
1781
+ "4": .55,
1782
+ "5": .55,
1783
+ "6": .55,
1784
+ "7": .55,
1785
+ "8": .55,
1786
+ "9": .55,
1787
+ "-": .33,
1788
+ "(": .33,
1789
+ ")": .33,
1790
+ "/": .28,
1791
+ m: .83,
1792
+ w: .72,
1793
+ M: .72,
1794
+ W: .83,
1795
+ A: .67,
1796
+ B: .67,
1797
+ C: .67,
1798
+ D: .72,
1799
+ E: .61,
1800
+ F: .56,
1801
+ G: .72,
1802
+ H: .72,
1803
+ J: .5,
1804
+ K: .67,
1805
+ L: .56,
1806
+ N: .72,
1807
+ O: .72,
1808
+ P: .67,
1809
+ Q: .72,
1810
+ R: .67,
1811
+ S: .67,
1812
+ T: .61,
1813
+ U: .72,
1814
+ V: .67,
1815
+ X: .67,
1816
+ Y: .67,
1817
+ Z: .61,
1818
+ "@": .92,
1819
+ "#": .55,
1820
+ $: .55,
1821
+ "%": .89,
1822
+ "&": .67,
1823
+ "+": .58,
1824
+ "=": .58,
1825
+ "<": .58,
1826
+ ">": .58,
1827
+ _: .55,
1828
+ "{": .33,
1829
+ "}": .33,
1830
+ "[": .28,
1831
+ "]": .28,
1832
+ "\"": .35,
1833
+ "~": .58,
1834
+ "^": .47,
1835
+ "*": .39,
1836
+ "\\": .28,
1837
+ "`": .33,
1838
+ "?": .56
1839
+ };
1840
+ /** Default multiplier for unlisted Latin / symbol characters. */
1841
+ const DEFAULT_CHAR_WIDTH = .55;
1842
+ /**
1843
+ * Check whether a code point is in a CJK / fullwidth Unicode range.
1844
+ * These characters are typically rendered at roughly 1em width.
1845
+ */
1846
+ function isCJKOrFullwidth(code) {
1847
+ return code >= 11904 && code <= 40959 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65103 || code >= 65280 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 131072 && code <= 195103 || code >= 12288 && code <= 12351 || code >= 12352 && code <= 12543 || code >= 44032 && code <= 55215;
1848
+ }
1849
+ /**
1850
+ * Estimate text dimensions using per-character width tables.
1851
+ * Falls back to this when no Canvas / DOM measurement is available.
1852
+ */
1853
+ function estimateTextSize(text, fontSize) {
1854
+ let totalWidth = 0;
1855
+ for (let i = 0; i < text.length; i++) {
1856
+ const ch = text[i];
1857
+ const known = CHAR_WIDTH[ch];
1858
+ if (known !== void 0) totalWidth += known;
1859
+ else {
1860
+ const code = ch.charCodeAt(0);
1861
+ totalWidth += isCJKOrFullwidth(code) ? 1 : DEFAULT_CHAR_WIDTH;
1862
+ }
1863
+ }
1864
+ return {
1865
+ width: totalWidth * fontSize,
1866
+ height: fontSize * 1.4
1867
+ };
1868
+ }
1869
+ let _ctx;
1870
+ function canvasMeasureText(text, fontSize) {
1871
+ if (_ctx === void 0) {
1872
+ _ctx = null;
1873
+ try {
1874
+ if (typeof OffscreenCanvas !== "undefined") _ctx = new OffscreenCanvas(1, 1).getContext("2d");
1875
+ else if (typeof document !== "undefined") _ctx = document.createElement("canvas").getContext("2d");
1876
+ } catch {}
1877
+ }
1878
+ if (_ctx) {
1879
+ _ctx.font = `${fontSize}px sans-serif`;
1880
+ return {
1881
+ width: _ctx.measureText(text).width,
1882
+ height: fontSize * 1.2
1883
+ };
1884
+ }
1885
+ return estimateTextSize(text, fontSize);
1886
+ }
1887
+ /** Measure a node label (default 14px). */
1888
+ function measureNode(label, opts) {
1889
+ const textSize = (opts.measureText ?? canvasMeasureText)(label, 14);
1890
+ return {
1891
+ width: Math.max(textSize.width + opts.nodePadding * 2, opts.nodeWidth),
1892
+ height: Math.max(textSize.height + opts.nodePadding * 2, opts.nodeHeight)
1893
+ };
1894
+ }
1895
+ /** Measure an edge / message label (default 12px). */
1896
+ function measureLabel(text, opts) {
1897
+ return (opts.measureText ?? canvasMeasureText)(text, 12);
1898
+ }
1899
+
1669
1900
  //#endregion
1670
1901
  //#region ../mermaid/src/layout/flowchart.ts
1671
1902
  const DEFAULT_OPTIONS$1 = {
@@ -1692,31 +1923,37 @@ function flowchartLayout(diagram, options) {
1692
1923
  subgraphs: []
1693
1924
  };
1694
1925
  const nodeSizes = /* @__PURE__ */ new Map();
1695
- for (const node of nodes) nodeSizes.set(node.id, measureNode(node, opts));
1926
+ for (const node of nodes) nodeSizes.set(node.id, measureNode(node.label, opts));
1927
+ const selfLoops = [];
1928
+ const nonSelfEdges = [];
1929
+ for (const edge of edges) if (edge.source === edge.target) selfLoops.push(edge);
1930
+ else nonSelfEdges.push(edge);
1931
+ const backEdgeSet = /* @__PURE__ */ new Set();
1696
1932
  const adj = /* @__PURE__ */ new Map();
1697
- const safeEdges = [];
1698
1933
  const visited = /* @__PURE__ */ new Set();
1699
1934
  const inStack = /* @__PURE__ */ new Set();
1700
1935
  for (const node of nodes) adj.set(node.id, []);
1701
- for (const edge of edges) {
1702
- const targets = adj.get(edge.source);
1703
- if (targets) targets.push(edge.target);
1704
- }
1936
+ for (const edge of nonSelfEdges) adj.get(edge.source)?.push(edge.target);
1705
1937
  function dfs(node) {
1706
1938
  visited.add(node);
1707
1939
  inStack.add(node);
1708
1940
  for (const neighbor of adj.get(node) ?? []) if (inStack.has(neighbor)) {
1709
- const edge = edges.find((e) => e.source === node && e.target === neighbor);
1710
- if (edge) safeEdges.push({
1711
- ...edge,
1712
- source: neighbor,
1713
- target: node
1714
- });
1941
+ const edge = nonSelfEdges.find((e) => e.source === node && e.target === neighbor);
1942
+ if (edge) backEdgeSet.add(edge);
1715
1943
  } else if (!visited.has(neighbor)) dfs(neighbor);
1716
1944
  inStack.delete(node);
1717
1945
  }
1718
1946
  for (const node of nodes) if (!visited.has(node.id)) dfs(node.id);
1719
- for (const edge of edges) if (!safeEdges.some((e) => e.source === edge.target && e.target === edge.source)) safeEdges.push(edge);
1947
+ const origToSafe = /* @__PURE__ */ new Map();
1948
+ const safeEdges = nonSelfEdges.map((edge) => {
1949
+ const safe = backEdgeSet.has(edge) ? {
1950
+ ...edge,
1951
+ source: edge.target,
1952
+ target: edge.source
1953
+ } : edge;
1954
+ origToSafe.set(edge, safe);
1955
+ return safe;
1956
+ });
1720
1957
  const layers = /* @__PURE__ */ new Map();
1721
1958
  const safeAdj = /* @__PURE__ */ new Map();
1722
1959
  const inDegree = /* @__PURE__ */ new Map();
@@ -1744,87 +1981,276 @@ function flowchartLayout(diagram, options) {
1744
1981
  }
1745
1982
  }
1746
1983
  for (const node of nodes) if (!layers.has(node.id)) layers.set(node.id, 0);
1984
+ const DUMMY_DIM = 8;
1985
+ const dummySet = /* @__PURE__ */ new Set();
1986
+ const dummySize = {
1987
+ width: DUMMY_DIM,
1988
+ height: DUMMY_DIM
1989
+ };
1990
+ const edgeDummies = /* @__PURE__ */ new Map();
1991
+ let workEdges = [...safeEdges];
1992
+ {
1993
+ const newEdges = [];
1994
+ for (const edge of workEdges) {
1995
+ const srcLayer = layers.get(edge.source) ?? 0;
1996
+ const tgtLayer = layers.get(edge.target) ?? 0;
1997
+ if (tgtLayer - srcLayer <= 1) {
1998
+ newEdges.push(edge);
1999
+ continue;
2000
+ }
2001
+ const dummies = [];
2002
+ let prev = edge.source;
2003
+ for (let l = srcLayer + 1; l < tgtLayer; l++) {
2004
+ const dummyId = `__dummy_${edge.source}_${edge.target}_${l}`;
2005
+ dummies.push(dummyId);
2006
+ dummySet.add(dummyId);
2007
+ layers.set(dummyId, l);
2008
+ nodeSizes.set(dummyId, dummySize);
2009
+ newEdges.push({
2010
+ source: prev,
2011
+ target: dummyId,
2012
+ lineStyle: edge.lineStyle,
2013
+ sourceMarker: "none",
2014
+ targetMarker: "none"
2015
+ });
2016
+ prev = dummyId;
2017
+ }
2018
+ newEdges.push({
2019
+ source: prev,
2020
+ target: edge.target,
2021
+ lineStyle: edge.lineStyle,
2022
+ sourceMarker: "none",
2023
+ targetMarker: "none"
2024
+ });
2025
+ edgeDummies.set(edge, dummies);
2026
+ }
2027
+ workEdges = newEdges;
2028
+ }
1747
2029
  const maxLayer = Math.max(...Array.from(layers.values()), 0);
1748
2030
  const layerGroups = Array.from({ length: maxLayer + 1 }, () => []);
1749
2031
  for (const node of nodes) layerGroups[layers.get(node.id) ?? 0].push(node.id);
1750
- for (let pass = 0; pass < 2; pass++) for (let l = 1; l <= maxLayer; l++) {
1751
- const layer = layerGroups[l];
1752
- const barycenters = /* @__PURE__ */ new Map();
1753
- for (const nodeId of layer) {
1754
- const prevLayer = layerGroups[l - 1];
1755
- let sum = 0;
1756
- let count = 0;
1757
- for (const edge of safeEdges) if (edge.target === nodeId && prevLayer.includes(edge.source)) {
1758
- sum += prevLayer.indexOf(edge.source);
1759
- count++;
2032
+ for (const dummyId of dummySet) layerGroups[layers.get(dummyId) ?? 0].push(dummyId);
2033
+ const nodeSubgraph = /* @__PURE__ */ new Map();
2034
+ for (const sg of subgraphs) mapNodesToSubgraph(sg, nodeSubgraph);
2035
+ const allNodeIds = [...nodes.map((n) => n.id), ...dummySet];
2036
+ const parents = /* @__PURE__ */ new Map();
2037
+ const children = /* @__PURE__ */ new Map();
2038
+ for (const id of allNodeIds) {
2039
+ parents.set(id, []);
2040
+ children.set(id, []);
2041
+ }
2042
+ for (const edge of workEdges) {
2043
+ children.get(edge.source)?.push(edge.target);
2044
+ parents.get(edge.target)?.push(edge.source);
2045
+ }
2046
+ for (let pass = 0; pass < 4; pass++) if (pass % 2 === 0) for (let l = 1; l <= maxLayer; l++) orderLayerByBarycenter(layerGroups[l], layerGroups[l - 1], parents, nodeSubgraph);
2047
+ else for (let l = maxLayer - 1; l >= 0; l--) orderLayerByBarycenter(layerGroups[l], layerGroups[l + 1], children, nodeSubgraph);
2048
+ for (let pass = 0; pass < 3; pass++) {
2049
+ let improved = false;
2050
+ for (let l = 0; l < maxLayer; l++) {
2051
+ const layer = layerGroups[l];
2052
+ const nextLayer = layerGroups[l + 1];
2053
+ for (let i = 0; i < layer.length - 1; i++) {
2054
+ const crossBefore = countCrossings(layer, nextLayer, children);
2055
+ const tmp = layer[i];
2056
+ layer[i] = layer[i + 1];
2057
+ layer[i + 1] = tmp;
2058
+ if (countCrossings(layer, nextLayer, children) < crossBefore) improved = true;
2059
+ else {
2060
+ layer[i + 1] = layer[i];
2061
+ layer[i] = tmp;
2062
+ }
1760
2063
  }
1761
- barycenters.set(nodeId, count > 0 ? sum / count : prevLayer.length / 2);
1762
2064
  }
1763
- layer.sort((a, b) => (barycenters.get(a) ?? 0) - (barycenters.get(b) ?? 0));
2065
+ if (!improved) break;
1764
2066
  }
1765
2067
  const isVertical = direction === "TB" || direction === "TD" || direction === "BT";
1766
- const positions = /* @__PURE__ */ new Map();
1767
- for (let l = 0; l <= maxLayer; l++) {
1768
- const layer = layerGroups[l];
1769
- let offset = -layer.reduce((sum, id) => {
1770
- const size = nodeSizes.get(id);
1771
- return sum + (isVertical ? size.width : size.height) + opts.nodeSpacing;
1772
- }, -opts.nodeSpacing) / 2;
1773
- for (const nodeId of layer) {
1774
- const size = nodeSizes.get(nodeId);
1775
- const primaryDim = isVertical ? size.width : size.height;
1776
- if (isVertical) positions.set(nodeId, {
1777
- x: offset + primaryDim / 2,
1778
- y: l * (opts.nodeHeight + opts.rankSpacing)
1779
- });
1780
- else positions.set(nodeId, {
1781
- x: l * (opts.nodeWidth + opts.rankSpacing),
1782
- y: offset + primaryDim / 2
1783
- });
1784
- offset += primaryDim + opts.nodeSpacing;
2068
+ const sgLayerRanges = computeSubgraphLayerRanges(subgraphs, layers);
2069
+ const allSubgraphs = flattenSubgraphs(subgraphs);
2070
+ const rankNodeDim = isVertical ? opts.nodeHeight : opts.nodeWidth;
2071
+ const sgTitleHeight = 20;
2072
+ const rankPos = [0];
2073
+ for (let l = 0; l < maxLayer; l++) {
2074
+ let overhead = 0;
2075
+ for (const sg of allSubgraphs) {
2076
+ const range = sgLayerRanges.get(sg.id);
2077
+ if (!range || range.minLayer > range.maxLayer) continue;
2078
+ if (range.maxLayer === l) overhead += opts.nodePadding;
2079
+ if (range.minLayer === l + 1) overhead += opts.nodePadding + sgTitleHeight;
1785
2080
  }
2081
+ const extra = overhead > 0 ? Math.max(0, overhead - opts.rankSpacing + opts.nodePadding) : 0;
2082
+ rankPos.push(rankPos[l] + rankNodeDim + opts.rankSpacing + extra);
2083
+ }
2084
+ const dim = (id) => {
2085
+ if (dummySet.has(id)) return DUMMY_DIM;
2086
+ const size = nodeSizes.get(id);
2087
+ return isVertical ? size.width : size.height;
2088
+ };
2089
+ const gap = (a, b) => {
2090
+ if (nodeSubgraph.get(a) !== nodeSubgraph.get(b)) return opts.nodeSpacing + opts.nodePadding * 2;
2091
+ return opts.nodeSpacing;
2092
+ };
2093
+ const bkResults = [];
2094
+ for (let dir = 0; dir < 4; dir++) {
2095
+ const isDown = dir < 2;
2096
+ const isLeft = dir % 2 === 0;
2097
+ const alignment = isDown ? parents : children;
2098
+ const p = /* @__PURE__ */ new Map();
2099
+ for (let l = 0; l <= maxLayer; l++) packLayerCentered(layerGroups[l], p, dim, gap);
2100
+ const sweeps = 4;
2101
+ for (let s = 0; s < sweeps; s++) if (isDown) for (let l = 1; l <= maxLayer; l++) alignLayerBK(layerGroups[l], alignment, p, dim, gap, isLeft);
2102
+ else for (let l = maxLayer - 1; l >= 0; l--) alignLayerBK(layerGroups[l], alignment, p, dim, gap, isLeft);
2103
+ bkResults.push(p);
2104
+ }
2105
+ const pos = /* @__PURE__ */ new Map();
2106
+ for (const id of allNodeIds) {
2107
+ const vals = bkResults.map((r) => r.get(id) ?? 0).sort((a, b) => a - b);
2108
+ pos.set(id, (vals[1] + vals[2]) / 2);
2109
+ }
2110
+ for (const [, dummies] of edgeDummies) {
2111
+ if (dummies.length === 0) continue;
2112
+ let sum = 0;
2113
+ for (const id of dummies) sum += pos.get(id) ?? 0;
2114
+ const avg = sum / dummies.length;
2115
+ for (const id of dummies) pos.set(id, avg);
2116
+ }
2117
+ const nodeLayer = /* @__PURE__ */ new Map();
2118
+ for (let l = 0; l <= maxLayer; l++) for (const id of layerGroups[l]) nodeLayer.set(id, l);
2119
+ const positions = /* @__PURE__ */ new Map();
2120
+ for (const id of allNodeIds) {
2121
+ const p = pos.get(id) ?? 0;
2122
+ const l = nodeLayer.get(id) ?? 0;
2123
+ if (isVertical) positions.set(id, {
2124
+ x: p,
2125
+ y: rankPos[l]
2126
+ });
2127
+ else positions.set(id, {
2128
+ x: rankPos[l],
2129
+ y: p
2130
+ });
1786
2131
  }
1787
2132
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
1788
- for (const [id, pos] of positions) {
2133
+ for (const [id, p] of positions) {
2134
+ if (dummySet.has(id)) continue;
1789
2135
  const size = nodeSizes.get(id);
1790
- minX = Math.min(minX, pos.x - size.width / 2);
1791
- minY = Math.min(minY, pos.y - size.height / 2);
1792
- maxX = Math.max(maxX, pos.x + size.width / 2);
1793
- maxY = Math.max(maxY, pos.y + size.height / 2);
2136
+ minX = Math.min(minX, p.x - size.width / 2);
2137
+ minY = Math.min(minY, p.y - size.height / 2);
2138
+ maxX = Math.max(maxX, p.x + size.width / 2);
2139
+ maxY = Math.max(maxY, p.y + size.height / 2);
1794
2140
  }
1795
2141
  const offsetX = opts.diagramPadding - minX;
1796
2142
  const offsetY = opts.diagramPadding - minY;
1797
- for (const [, pos] of positions) {
1798
- pos.x += offsetX;
1799
- pos.y += offsetY;
2143
+ for (const [, p] of positions) {
2144
+ p.x += offsetX;
2145
+ p.y += offsetY;
1800
2146
  }
1801
2147
  if (direction === "BT") {
1802
2148
  const totalHeight = maxY - minY;
1803
- for (const [, pos] of positions) pos.y = totalHeight - (pos.y - opts.diagramPadding) + opts.diagramPadding;
2149
+ for (const [, p] of positions) p.y = totalHeight - (p.y - opts.diagramPadding) + opts.diagramPadding;
1804
2150
  }
1805
2151
  if (direction === "RL") {
1806
2152
  const totalWidth = maxX - minX;
1807
- for (const [, pos] of positions) pos.x = totalWidth - (pos.x - opts.diagramPadding) + opts.diagramPadding;
2153
+ for (const [, p] of positions) p.x = totalWidth - (p.x - opts.diagramPadding) + opts.diagramPadding;
2154
+ }
2155
+ if (subgraphs.length > 0) {
2156
+ let extraShiftX = 0;
2157
+ let extraShiftY = 0;
2158
+ for (const sg of subgraphs) {
2159
+ const sgMin = precomputeSgMin(sg, positions, nodeSizes, opts);
2160
+ extraShiftX = Math.max(extraShiftX, opts.diagramPadding - sgMin.minX);
2161
+ extraShiftY = Math.max(extraShiftY, opts.diagramPadding - sgMin.minY);
2162
+ }
2163
+ if (extraShiftX > 0 || extraShiftY > 0) for (const [, p] of positions) {
2164
+ p.x += extraShiftX;
2165
+ p.y += extraShiftY;
2166
+ }
1808
2167
  }
1809
2168
  const positionedNodes = nodes.map((node) => {
1810
- const pos = positions.get(node.id);
2169
+ const p = positions.get(node.id);
1811
2170
  const size = nodeSizes.get(node.id);
1812
2171
  return {
1813
2172
  node,
1814
- x: pos.x,
1815
- y: pos.y,
2173
+ x: p.x,
2174
+ y: p.y,
1816
2175
  width: size.width,
1817
2176
  height: size.height
1818
2177
  };
1819
2178
  });
1820
2179
  const positionedEdges = edges.map((edge) => {
2180
+ if (edge.source === edge.target) {
2181
+ const p = positions.get(edge.source);
2182
+ const size = nodeSizes.get(edge.source);
2183
+ if (!p || !size) return {
2184
+ edge,
2185
+ path: "M 0 0"
2186
+ };
2187
+ const loopSize = 30;
2188
+ const rx = size.width / 2;
2189
+ const ry = size.height / 2;
2190
+ let path;
2191
+ let labelMid;
2192
+ if (isVertical) {
2193
+ const sx = p.x + rx;
2194
+ const sy = p.y - ry * .3;
2195
+ const tx = p.x + rx;
2196
+ const ty = p.y + ry * .3;
2197
+ const cx = p.x + rx + loopSize;
2198
+ path = `M ${sx} ${sy} C ${cx} ${sy - loopSize} ${cx} ${ty + loopSize} ${tx} ${ty}`;
2199
+ labelMid = {
2200
+ x: cx + 4,
2201
+ y: p.y
2202
+ };
2203
+ } else {
2204
+ const sx = p.x - rx * .3;
2205
+ const sy = p.y + ry;
2206
+ const tx = p.x + rx * .3;
2207
+ const ty = p.y + ry;
2208
+ const cy = p.y + ry + loopSize;
2209
+ path = `M ${sx} ${sy} C ${sx - loopSize} ${cy} ${tx + loopSize} ${cy} ${tx} ${ty}`;
2210
+ labelMid = {
2211
+ x: p.x,
2212
+ y: cy + 4
2213
+ };
2214
+ }
2215
+ let labelPosition;
2216
+ let labelSize;
2217
+ if (edge.label) {
2218
+ labelPosition = labelMid;
2219
+ labelSize = measureLabel(edge.label, opts);
2220
+ }
2221
+ return {
2222
+ edge,
2223
+ path,
2224
+ labelPosition,
2225
+ labelSize
2226
+ };
2227
+ }
1821
2228
  const sourcePos = positions.get(edge.source);
1822
2229
  const targetPos = positions.get(edge.target);
1823
2230
  if (!sourcePos || !targetPos) return {
1824
2231
  edge,
1825
2232
  path: "M 0 0"
1826
2233
  };
1827
- const result = buildEdgePath(sourcePos, targetPos, nodeSizes.get(edge.source), nodeSizes.get(edge.target), isVertical, opts);
2234
+ const sourceSize = nodeSizes.get(edge.source);
2235
+ const targetSize = nodeSizes.get(edge.target);
2236
+ const safeEdge = origToSafe.get(edge);
2237
+ const dummies = safeEdge ? edgeDummies.get(safeEdge) : void 0;
2238
+ if (dummies && dummies.length > 0) {
2239
+ const result = buildLongEdgePath(sourcePos, targetPos, sourceSize, targetSize, dummies.map((id) => positions.get(id)), isVertical, opts);
2240
+ let labelPosition;
2241
+ let labelSize;
2242
+ if (edge.label) {
2243
+ labelPosition = result.labelMid;
2244
+ labelSize = measureLabel(edge.label, opts);
2245
+ }
2246
+ return {
2247
+ edge,
2248
+ path: result.path,
2249
+ labelPosition,
2250
+ labelSize
2251
+ };
2252
+ }
2253
+ const result = buildEdgePath(sourcePos, targetPos, sourceSize, targetSize, isVertical, opts);
1828
2254
  let labelPosition;
1829
2255
  let labelSize;
1830
2256
  if (edge.label) {
@@ -1838,29 +2264,51 @@ function flowchartLayout(diagram, options) {
1838
2264
  labelSize
1839
2265
  };
1840
2266
  });
1841
- const positionedSubgraphs = subgraphs.map((sg) => {
2267
+ const positionedSubgraphs = [];
2268
+ function layoutSubgraph(sg) {
2269
+ const childPositioned = [];
2270
+ for (const child of sg.subgraphs ?? []) childPositioned.push(layoutSubgraph(child));
1842
2271
  let sgMinX = Infinity, sgMinY = Infinity, sgMaxX = -Infinity, sgMaxY = -Infinity;
1843
2272
  for (const nodeId of sg.nodes) {
1844
- const pos = positions.get(nodeId);
2273
+ const p = positions.get(nodeId);
1845
2274
  const size = nodeSizes.get(nodeId);
1846
- if (pos && size) {
1847
- sgMinX = Math.min(sgMinX, pos.x - size.width / 2);
1848
- sgMinY = Math.min(sgMinY, pos.y - size.height / 2);
1849
- sgMaxX = Math.max(sgMaxX, pos.x + size.width / 2);
1850
- sgMaxY = Math.max(sgMaxY, pos.y + size.height / 2);
2275
+ if (p && size) {
2276
+ sgMinX = Math.min(sgMinX, p.x - size.width / 2);
2277
+ sgMinY = Math.min(sgMinY, p.y - size.height / 2);
2278
+ sgMaxX = Math.max(sgMaxX, p.x + size.width / 2);
2279
+ sgMaxY = Math.max(sgMaxY, p.y + size.height / 2);
1851
2280
  }
1852
2281
  }
2282
+ for (const child of childPositioned) {
2283
+ sgMinX = Math.min(sgMinX, child.x);
2284
+ sgMinY = Math.min(sgMinY, child.y);
2285
+ sgMaxX = Math.max(sgMaxX, child.x + child.width);
2286
+ sgMaxY = Math.max(sgMaxY, child.y + child.height);
2287
+ }
1853
2288
  const padding = opts.nodePadding;
1854
- return {
2289
+ const positioned = {
1855
2290
  subgraph: sg,
1856
2291
  x: sgMinX - padding,
1857
2292
  y: sgMinY - padding - 20,
1858
2293
  width: sgMaxX - sgMinX + padding * 2,
1859
2294
  height: sgMaxY - sgMinY + padding * 2 + 20
1860
2295
  };
1861
- });
1862
- const diagramWidth = maxX - minX + opts.diagramPadding * 2;
1863
- const diagramHeight = maxY - minY + opts.diagramPadding * 2;
2296
+ positionedSubgraphs.push(positioned);
2297
+ return positioned;
2298
+ }
2299
+ for (const sg of subgraphs) layoutSubgraph(sg);
2300
+ let extMaxX = -Infinity;
2301
+ let extMaxY = -Infinity;
2302
+ for (const pn of positionedNodes) {
2303
+ extMaxX = Math.max(extMaxX, pn.x + pn.width / 2);
2304
+ extMaxY = Math.max(extMaxY, pn.y + pn.height / 2);
2305
+ }
2306
+ for (const psg of positionedSubgraphs) {
2307
+ extMaxX = Math.max(extMaxX, psg.x + psg.width);
2308
+ extMaxY = Math.max(extMaxY, psg.y + psg.height);
2309
+ }
2310
+ const diagramWidth = extMaxX + opts.diagramPadding;
2311
+ const diagramHeight = extMaxY + opts.diagramPadding;
1864
2312
  return {
1865
2313
  width: diagramWidth,
1866
2314
  height: diagramHeight,
@@ -1870,6 +2318,136 @@ function flowchartLayout(diagram, options) {
1870
2318
  subgraphs: positionedSubgraphs
1871
2319
  };
1872
2320
  }
2321
+ /** Map each node to its most immediate (deepest) containing subgraph. */
2322
+ function mapNodesToSubgraph(sg, out) {
2323
+ for (const nodeId of sg.nodes) out.set(nodeId, sg.id);
2324
+ for (const child of sg.subgraphs ?? []) mapNodesToSubgraph(child, out);
2325
+ }
2326
+ /**
2327
+ * Order a layer using barycenter heuristic with subgraph clustering.
2328
+ * `neighbors` maps each node to its connected nodes in the reference layer.
2329
+ * Nodes in the same subgraph are kept adjacent.
2330
+ */
2331
+ function orderLayerByBarycenter(layer, refLayer, neighborMap, nodeSubgraph) {
2332
+ const barycenters = /* @__PURE__ */ new Map();
2333
+ const refIndex = /* @__PURE__ */ new Map();
2334
+ for (let i = 0; i < refLayer.length; i++) refIndex.set(refLayer[i], i);
2335
+ for (const nodeId of layer) {
2336
+ const nbrs = neighborMap.get(nodeId) ?? [];
2337
+ let sum = 0;
2338
+ let count = 0;
2339
+ for (const n of nbrs) {
2340
+ const idx = refIndex.get(n);
2341
+ if (idx !== void 0) {
2342
+ sum += idx;
2343
+ count++;
2344
+ }
2345
+ }
2346
+ if (count > 0) barycenters.set(nodeId, sum / count);
2347
+ }
2348
+ const groupBary = /* @__PURE__ */ new Map();
2349
+ for (const nodeId of layer) {
2350
+ const sgId = nodeSubgraph.get(nodeId) ?? "";
2351
+ if (!groupBary.has(sgId)) {
2352
+ const connected = layer.filter((n) => (nodeSubgraph.get(n) ?? "") === sgId).filter((n) => barycenters.has(n));
2353
+ if (connected.length > 0) {
2354
+ const avg = connected.reduce((s, n) => s + barycenters.get(n), 0) / connected.length;
2355
+ groupBary.set(sgId, avg);
2356
+ }
2357
+ }
2358
+ }
2359
+ const original = [...layer];
2360
+ layer.sort((a, b) => {
2361
+ const sgA = nodeSubgraph.get(a) ?? "";
2362
+ const sgB = nodeSubgraph.get(b) ?? "";
2363
+ if (sgA !== sgB) {
2364
+ const ga = groupBary.get(sgA);
2365
+ const gb = groupBary.get(sgB);
2366
+ if (ga !== void 0 && gb !== void 0 && ga !== gb) return ga - gb;
2367
+ if (ga !== void 0 && gb === void 0) return -1;
2368
+ if (ga === void 0 && gb !== void 0) return 1;
2369
+ }
2370
+ const ba = barycenters.get(a);
2371
+ const bb = barycenters.get(b);
2372
+ if (ba !== void 0 && bb !== void 0 && ba !== bb) return ba - bb;
2373
+ if (ba !== void 0 && bb === void 0) return -1;
2374
+ if (ba === void 0 && bb !== void 0) return 1;
2375
+ return original.indexOf(a) - original.indexOf(b);
2376
+ });
2377
+ }
2378
+ /**
2379
+ * Count edge crossings between two adjacent layers.
2380
+ * Two edges (u1→v1) and (u2→v2) cross iff u1 < u2 and v1 > v2 (or vice versa).
2381
+ */
2382
+ function countCrossings(upperLayer, lowerLayer, children) {
2383
+ const lowerIdx = /* @__PURE__ */ new Map();
2384
+ for (let i = 0; i < lowerLayer.length; i++) lowerIdx.set(lowerLayer[i], i);
2385
+ const edgePairs = [];
2386
+ for (let ui = 0; ui < upperLayer.length; ui++) for (const child of children.get(upperLayer[ui]) ?? []) {
2387
+ const li = lowerIdx.get(child);
2388
+ if (li !== void 0) edgePairs.push([ui, li]);
2389
+ }
2390
+ let crossings = 0;
2391
+ for (let i = 0; i < edgePairs.length; i++) for (let j = i + 1; j < edgePairs.length; j++) {
2392
+ const [u1, v1] = edgePairs[i];
2393
+ const [u2, v2] = edgePairs[j];
2394
+ if (u1 < u2 && v1 > v2 || u1 > u2 && v1 < v2) crossings++;
2395
+ }
2396
+ return crossings;
2397
+ }
2398
+ /** Pack a layer centered at x=0 with minimum spacing. */
2399
+ function packLayerCentered(layer, pos, dim, gap) {
2400
+ if (layer.length === 0) return;
2401
+ let totalWidth = dim(layer[0]);
2402
+ for (let i = 1; i < layer.length; i++) totalWidth += gap(layer[i - 1], layer[i]) + dim(layer[i]);
2403
+ let offset = -totalWidth / 2;
2404
+ for (let i = 0; i < layer.length; i++) {
2405
+ const id = layer[i];
2406
+ const d = dim(id);
2407
+ if (i > 0) offset += gap(layer[i - 1], id);
2408
+ pos.set(id, offset + d / 2);
2409
+ offset += d;
2410
+ }
2411
+ }
2412
+ /**
2413
+ * Brandes-Köpf style single-direction alignment.
2414
+ * `isLeft`: when true, prefer the left (lower-index) median neighbor;
2415
+ * when false, prefer the right (higher-index) median neighbor.
2416
+ * This produces 4 distinct alignments that are then merged via median.
2417
+ */
2418
+ function alignLayerBK(layer, neighborMap, pos, dim, gap, isLeft) {
2419
+ if (layer.length === 0) return;
2420
+ const ideal = /* @__PURE__ */ new Map();
2421
+ for (const id of layer) {
2422
+ const nbrs = neighborMap.get(id) ?? [];
2423
+ if (nbrs.length > 0) {
2424
+ const xs = nbrs.map((n) => pos.get(n)).sort((a, b) => a - b);
2425
+ const mid = Math.floor(xs.length / 2);
2426
+ if (xs.length % 2 === 1) ideal.set(id, xs[mid]);
2427
+ else ideal.set(id, isLeft ? xs[mid - 1] : xs[mid]);
2428
+ }
2429
+ }
2430
+ if (ideal.size === 0) return;
2431
+ const indices = layer.map((_, i) => i);
2432
+ if (!isLeft) indices.reverse();
2433
+ for (const i of indices) {
2434
+ const id = layer[i];
2435
+ const target = ideal.get(id);
2436
+ if (target === void 0) continue;
2437
+ const halfDim = dim(id) / 2;
2438
+ let minPos = -Infinity;
2439
+ if (i > 0) {
2440
+ const leftId = layer[i - 1];
2441
+ minPos = pos.get(leftId) + dim(leftId) / 2 + gap(leftId, id) + halfDim;
2442
+ }
2443
+ let maxPos = Infinity;
2444
+ if (i < layer.length - 1) {
2445
+ const rightId = layer[i + 1];
2446
+ maxPos = pos.get(rightId) - dim(rightId) / 2 - gap(id, rightId) - halfDim;
2447
+ }
2448
+ pos.set(id, Math.max(minPos, Math.min(maxPos, target)));
2449
+ }
2450
+ }
1873
2451
  /** Default curvature factor (matches xyflow) */
1874
2452
  const DEFAULT_CURVATURE = .25;
1875
2453
  /**
@@ -1931,6 +2509,25 @@ function buildEdgePath(source, target, sourceSize, targetSize, isVertical, opts)
1931
2509
  labelMid: mid
1932
2510
  };
1933
2511
  }
2512
+ if (opts.edgeRouting === "orthogonal") if (isVertical) {
2513
+ const midY = (sy + ty) / 2;
2514
+ return {
2515
+ path: `M ${sx} ${sy} L ${sx} ${midY} L ${tx} ${midY} L ${tx} ${ty}`,
2516
+ labelMid: {
2517
+ x: (sx + tx) / 2,
2518
+ y: midY
2519
+ }
2520
+ };
2521
+ } else {
2522
+ const midX = (sx + tx) / 2;
2523
+ return {
2524
+ path: `M ${sx} ${sy} L ${midX} ${sy} L ${midX} ${ty} L ${tx} ${ty}`,
2525
+ labelMid: {
2526
+ x: midX,
2527
+ y: (sy + ty) / 2
2528
+ }
2529
+ };
2530
+ }
1934
2531
  return {
1935
2532
  path: `M ${sx} ${sy} L ${tx} ${ty}`,
1936
2533
  labelMid: {
@@ -1939,21 +2536,146 @@ function buildEdgePath(source, target, sourceSize, targetSize, isVertical, opts)
1939
2536
  }
1940
2537
  };
1941
2538
  }
1942
- function measureNode(node, opts) {
1943
- const textSize = (opts.measureText ?? estimateTextSize$1)(node.label, 14);
2539
+ /**
2540
+ * Build a smooth path through dummy waypoints for edges spanning multiple layers.
2541
+ * Uses cubic Bezier spline fitting through the waypoints for bezier mode,
2542
+ * or polyline/orthogonal segments through the waypoints for other modes.
2543
+ */
2544
+ function buildLongEdgePath(source, target, sourceSize, targetSize, waypoints, isVertical, opts) {
2545
+ let sx, sy, tx, ty;
2546
+ if (isVertical) {
2547
+ sx = source.x;
2548
+ sy = source.y + sourceSize.height / 2;
2549
+ tx = target.x;
2550
+ ty = target.y - targetSize.height / 2;
2551
+ if (source.y > target.y) {
2552
+ sy = source.y - sourceSize.height / 2;
2553
+ ty = target.y + targetSize.height / 2;
2554
+ }
2555
+ } else {
2556
+ sx = source.x + sourceSize.width / 2;
2557
+ sy = source.y;
2558
+ tx = target.x - targetSize.width / 2;
2559
+ ty = target.y;
2560
+ if (source.x > target.x) {
2561
+ sx = source.x - sourceSize.width / 2;
2562
+ tx = target.x + targetSize.width / 2;
2563
+ }
2564
+ }
2565
+ const points = [
2566
+ {
2567
+ x: sx,
2568
+ y: sy
2569
+ },
2570
+ ...waypoints,
2571
+ {
2572
+ x: tx,
2573
+ y: ty
2574
+ }
2575
+ ];
2576
+ const labelMid = points[Math.floor(points.length / 2)];
2577
+ if (opts.edgeRouting === "bezier" && points.length >= 3) {
2578
+ let d = `M ${points[0].x} ${points[0].y}`;
2579
+ for (let i = 0; i < points.length - 1; i++) {
2580
+ const p0 = points[Math.max(0, i - 1)];
2581
+ const p1 = points[i];
2582
+ const p2 = points[i + 1];
2583
+ const p3 = points[Math.min(points.length - 1, i + 2)];
2584
+ const cp1x = p1.x + (p2.x - p0.x) / 6;
2585
+ const cp1y = p1.y + (p2.y - p0.y) / 6;
2586
+ const cp2x = p2.x - (p3.x - p1.x) / 6;
2587
+ const cp2y = p2.y - (p3.y - p1.y) / 6;
2588
+ d += ` C ${cp1x} ${cp1y} ${cp2x} ${cp2y} ${p2.x} ${p2.y}`;
2589
+ }
2590
+ return {
2591
+ path: d,
2592
+ labelMid
2593
+ };
2594
+ }
2595
+ if (opts.edgeRouting === "orthogonal") {
2596
+ let d = `M ${points[0].x} ${points[0].y}`;
2597
+ for (let i = 1; i < points.length; i++) {
2598
+ const prev = points[i - 1];
2599
+ const curr = points[i];
2600
+ if (isVertical) {
2601
+ const midY = (prev.y + curr.y) / 2;
2602
+ d += ` L ${prev.x} ${midY} L ${curr.x} ${midY} L ${curr.x} ${curr.y}`;
2603
+ } else {
2604
+ const midX = (prev.x + curr.x) / 2;
2605
+ d += ` L ${midX} ${prev.y} L ${midX} ${curr.y} L ${curr.x} ${curr.y}`;
2606
+ }
2607
+ }
2608
+ return {
2609
+ path: d,
2610
+ labelMid
2611
+ };
2612
+ }
2613
+ let d = `M ${points[0].x} ${points[0].y}`;
2614
+ for (let i = 1; i < points.length; i++) d += ` L ${points[i].x} ${points[i].y}`;
1944
2615
  return {
1945
- width: Math.max(textSize.width + opts.nodePadding * 2, opts.nodeWidth),
1946
- height: Math.max(textSize.height + opts.nodePadding * 2, opts.nodeHeight)
2616
+ path: d,
2617
+ labelMid
1947
2618
  };
1948
2619
  }
1949
- function measureLabel(text, opts) {
1950
- return (opts.measureText ?? estimateTextSize$1)(text, 12);
1951
- }
1952
- function estimateTextSize$1(text, fontSize) {
1953
- const avgCharWidth = fontSize * .6;
2620
+ /** Compute the layer range (min, max) for each subgraph including nested children. */
2621
+ function computeSubgraphLayerRanges(subgraphs, layers) {
2622
+ const ranges = /* @__PURE__ */ new Map();
2623
+ function compute(sg) {
2624
+ let minL = Infinity;
2625
+ let maxL = -Infinity;
2626
+ for (const nodeId of sg.nodes) {
2627
+ const l = layers.get(nodeId);
2628
+ if (l !== void 0) {
2629
+ minL = Math.min(minL, l);
2630
+ maxL = Math.max(maxL, l);
2631
+ }
2632
+ }
2633
+ for (const child of sg.subgraphs ?? []) {
2634
+ const childRange = compute(child);
2635
+ minL = Math.min(minL, childRange.minLayer);
2636
+ maxL = Math.max(maxL, childRange.maxLayer);
2637
+ }
2638
+ const range = {
2639
+ minLayer: minL,
2640
+ maxLayer: maxL
2641
+ };
2642
+ ranges.set(sg.id, range);
2643
+ return range;
2644
+ }
2645
+ for (const sg of subgraphs) compute(sg);
2646
+ return ranges;
2647
+ }
2648
+ /** Flatten a subgraph tree into a flat array of all subgraphs. */
2649
+ function flattenSubgraphs(subgraphs) {
2650
+ const result = [];
2651
+ function collect(sg) {
2652
+ result.push(sg);
2653
+ for (const child of sg.subgraphs ?? []) collect(child);
2654
+ }
2655
+ for (const sg of subgraphs) collect(sg);
2656
+ return result;
2657
+ }
2658
+ /** Pre-compute the minimum x/y bounds of a subgraph (including nested children). */
2659
+ function precomputeSgMin(sg, positions, nodeSizes, opts) {
2660
+ const titleHeight = 20;
2661
+ let minX = Infinity;
2662
+ let minY = Infinity;
2663
+ for (const nodeId of sg.nodes) {
2664
+ const p = positions.get(nodeId);
2665
+ const size = nodeSizes.get(nodeId);
2666
+ if (p && size) {
2667
+ minX = Math.min(minX, p.x - size.width / 2);
2668
+ minY = Math.min(minY, p.y - size.height / 2);
2669
+ }
2670
+ }
2671
+ for (const child of sg.subgraphs ?? []) {
2672
+ const childMin = precomputeSgMin(child, positions, nodeSizes, opts);
2673
+ minX = Math.min(minX, childMin.minX);
2674
+ minY = Math.min(minY, childMin.minY);
2675
+ }
1954
2676
  return {
1955
- width: text.length * avgCharWidth,
1956
- height: fontSize * 1.4
2677
+ minX: minX - opts.nodePadding,
2678
+ minY: minY - opts.nodePadding - titleHeight
1957
2679
  };
1958
2680
  }
1959
2681
 
@@ -1985,20 +2707,72 @@ function sequenceLayout(diagram, options) {
1985
2707
  blocks: [],
1986
2708
  notes: []
1987
2709
  };
2710
+ const participantIndex = /* @__PURE__ */ new Map();
2711
+ const participantSizes = /* @__PURE__ */ new Map();
2712
+ for (let i = 0; i < participants.length; i++) {
2713
+ const p = participants[i];
2714
+ participantIndex.set(p.id, i);
2715
+ participantSizes.set(p.id, measureNode(p.label, opts));
2716
+ }
2717
+ const pairSpacing = [];
2718
+ for (let i = 0; i < participants.length - 1; i++) {
2719
+ const left = participants[i];
2720
+ const right = participants[i + 1];
2721
+ const leftSize = participantSizes.get(left.id);
2722
+ const rightSize = participantSizes.get(right.id);
2723
+ let minDist = leftSize.width / 2 + rightSize.width / 2 + opts.nodePadding * 2;
2724
+ for (const msg of messages) {
2725
+ const fi = participantIndex.get(msg.from);
2726
+ const ti = participantIndex.get(msg.to);
2727
+ if (fi === void 0 || ti === void 0) continue;
2728
+ const lo = Math.min(fi, ti);
2729
+ const hi = Math.max(fi, ti);
2730
+ if (lo <= i && hi >= i + 1 && msg.text) {
2731
+ const labelSize = measureLabel(msg.text, opts);
2732
+ const spanCols = hi - lo;
2733
+ const perGap = (labelSize.width + opts.nodePadding * 2) / spanCols;
2734
+ minDist = Math.max(minDist, perGap);
2735
+ }
2736
+ }
2737
+ pairSpacing.push(Math.max(minDist, opts.nodeSpacing));
2738
+ }
1988
2739
  const participantX = /* @__PURE__ */ new Map();
1989
- participants.forEach((p, i) => {
1990
- participantX.set(p.id, opts.diagramPadding + opts.nodeWidth / 2 + i * opts.nodeSpacing);
1991
- });
2740
+ let currentX = opts.diagramPadding + (participantSizes.get(participants[0].id)?.width ?? opts.nodeWidth) / 2;
2741
+ participantX.set(participants[0].id, currentX);
2742
+ for (let i = 1; i < participants.length; i++) {
2743
+ currentX += pairSpacing[i - 1];
2744
+ participantX.set(participants[i].id, currentX);
2745
+ }
1992
2746
  const headerY = opts.diagramPadding + opts.nodeHeight / 2;
1993
2747
  const headerBottom = headerY + opts.nodeHeight / 2 + 20;
1994
- const positionedMessages = messages.map((msg, i) => {
1995
- return {
2748
+ const selfMessageWidth = 40;
2749
+ const selfMessageHeight = 30;
2750
+ const positionedMessages = [];
2751
+ const positionedNotes = [];
2752
+ let currentY = headerBottom;
2753
+ const noteMessageCounts = diagram._noteMessageCounts ?? notes.map(() => messages.length);
2754
+ let nextNoteIdx = 0;
2755
+ for (let msgIdx = 0; msgIdx < messages.length; msgIdx++) {
2756
+ while (nextNoteIdx < notes.length && (noteMessageCounts[nextNoteIdx] ?? messages.length) <= msgIdx) {
2757
+ currentY = positionNote(notes[nextNoteIdx], nextNoteIdx, currentY, positionedNotes, participantX, opts);
2758
+ nextNoteIdx++;
2759
+ }
2760
+ const msg = messages[msgIdx];
2761
+ const fromX = participantX.get(msg.from) ?? 0;
2762
+ const toX = participantX.get(msg.to) ?? 0;
2763
+ const isSelf = msg.from === msg.to;
2764
+ positionedMessages.push({
1996
2765
  message: msg,
1997
- fromX: participantX.get(msg.from) ?? 0,
1998
- toX: participantX.get(msg.to) ?? 0,
1999
- y: headerBottom + i * opts.rankSpacing
2000
- };
2001
- });
2766
+ fromX,
2767
+ toX: isSelf ? fromX + selfMessageWidth : toX,
2768
+ y: currentY
2769
+ });
2770
+ currentY += isSelf ? selfMessageHeight + opts.rankSpacing * .5 : opts.rankSpacing;
2771
+ }
2772
+ while (nextNoteIdx < notes.length) {
2773
+ currentY = positionNote(notes[nextNoteIdx], nextNoteIdx, currentY, positionedNotes, participantX, opts);
2774
+ nextNoteIdx++;
2775
+ }
2002
2776
  const activationStarts = /* @__PURE__ */ new Map();
2003
2777
  const positionedActivations = [];
2004
2778
  for (const pmsg of positionedMessages) {
@@ -2021,7 +2795,10 @@ function sequenceLayout(diagram, options) {
2021
2795
  }
2022
2796
  }
2023
2797
  const lastMsg = positionedMessages[positionedMessages.length - 1];
2024
- const diagramBottom = (lastMsg ? lastMsg.y : headerBottom) + opts.rankSpacing;
2798
+ const lastNote = positionedNotes[positionedNotes.length - 1];
2799
+ const lastMessageY = lastMsg ? lastMsg.y : headerBottom;
2800
+ const lastNoteY = lastNote ? lastNote.y + lastNote.height : headerBottom;
2801
+ const diagramBottom = Math.max(lastMessageY, lastNoteY) + opts.rankSpacing;
2025
2802
  const positionedLifelines = participants.map((p) => {
2026
2803
  return {
2027
2804
  participant: p,
@@ -2030,6 +2807,8 @@ function sequenceLayout(diagram, options) {
2030
2807
  y2: diagramBottom
2031
2808
  };
2032
2809
  });
2810
+ const msgIndexMap = /* @__PURE__ */ new Map();
2811
+ for (let i = 0; i < messages.length; i++) msgIndexMap.set(messages[i], i);
2033
2812
  const positionedBlocks = blocks.map((block) => {
2034
2813
  const allMsgs = [...block.messages, ...block.sections?.flatMap((s) => s.messages) ?? []];
2035
2814
  let minX = Infinity;
@@ -2040,48 +2819,48 @@ function sequenceLayout(diagram, options) {
2040
2819
  minX = Math.min(minX, fromX, toX);
2041
2820
  maxX = Math.max(maxX, fromX, toX);
2042
2821
  }
2043
- const msgIndices = allMsgs.map((m) => messages.findIndex((pm) => pm.from === m.from && pm.to === m.to && pm.text === m.text)).filter((i) => i >= 0);
2822
+ const msgIndices = allMsgs.map((m) => msgIndexMap.get(m) ?? -1).filter((i) => i >= 0);
2044
2823
  const firstBlockMsg = msgIndices.length > 0 ? positionedMessages[Math.min(...msgIndices)] : void 0;
2045
2824
  const lastBlockMsg = msgIndices.length > 0 ? positionedMessages[Math.max(...msgIndices)] : void 0;
2046
2825
  const startY = firstBlockMsg ? firstBlockMsg.y - 20 : headerBottom;
2047
2826
  const endY = lastBlockMsg ? lastBlockMsg.y + 20 : headerBottom + opts.rankSpacing;
2827
+ let sectionDividers;
2828
+ if (block.sections && block.sections.length > 0) {
2829
+ sectionDividers = [];
2830
+ for (const section of block.sections) {
2831
+ const sectionMsgIndices = section.messages.map((m) => msgIndexMap.get(m) ?? -1).filter((i) => i >= 0);
2832
+ if (sectionMsgIndices.length > 0) {
2833
+ const firstSectionMsg = positionedMessages[Math.min(...sectionMsgIndices)];
2834
+ if (firstSectionMsg) sectionDividers.push({
2835
+ y: firstSectionMsg.y - 15,
2836
+ label: section.label
2837
+ });
2838
+ }
2839
+ }
2840
+ }
2048
2841
  const padding = opts.nodePadding;
2049
2842
  return {
2050
2843
  block,
2051
2844
  x: minX - padding - opts.nodeWidth / 2,
2052
2845
  y: startY,
2053
2846
  width: maxX - minX + opts.nodeWidth + padding * 2,
2054
- height: endY - startY
2055
- };
2056
- });
2057
- const positionedNotes = notes.map((note, i) => {
2058
- const xs = note.participants.map((id) => participantX.get(id) ?? 0).filter((x) => x > 0);
2059
- let x;
2060
- if (note.position === "left of") x = (xs[0] ?? opts.diagramPadding) - opts.nodeWidth;
2061
- else if (note.position === "right of") x = (xs[0] ?? opts.diagramPadding) + opts.nodeWidth;
2062
- else x = xs.length > 0 ? xs.reduce((a, b) => a + b, 0) / xs.length : opts.diagramPadding;
2063
- const y = headerBottom + (messages.length + i) * opts.rankSpacing * .5;
2064
- const size = estimateTextSize(note.text, 12);
2065
- return {
2066
- note,
2067
- x,
2068
- y,
2069
- width: Math.max(size.width + opts.nodePadding * 2, 80),
2070
- height: Math.max(size.height + opts.nodePadding, 30)
2847
+ height: endY - startY,
2848
+ sectionDividers
2071
2849
  };
2072
2850
  });
2073
2851
  const positionedParticipants = participants.map((p) => {
2074
- const x = participantX.get(p.id) ?? 0;
2075
- const size = estimateTextSize(p.label, 14);
2076
2852
  return {
2077
2853
  participant: p,
2078
- x,
2854
+ x: participantX.get(p.id) ?? 0,
2079
2855
  y: headerY,
2080
- width: Math.max(size.width + opts.nodePadding * 2, opts.nodeWidth),
2856
+ width: participantSizes.get(p.id).width,
2081
2857
  height: opts.nodeHeight
2082
2858
  };
2083
2859
  });
2084
- const diagramWidth = (participants.length - 1) * opts.nodeSpacing + opts.nodeWidth + opts.diagramPadding * 2;
2860
+ const firstX = participantX.get(participants[0].id);
2861
+ const lastX = participantX.get(participants[participants.length - 1].id);
2862
+ const firstHalf = (participantSizes.get(participants[0].id)?.width ?? opts.nodeWidth) / 2;
2863
+ const diagramWidth = lastX + (participantSizes.get(participants[participants.length - 1].id)?.width ?? opts.nodeWidth) / 2 + opts.diagramPadding - (firstX - firstHalf - opts.diagramPadding);
2085
2864
  const diagramHeight = diagramBottom + opts.diagramPadding;
2086
2865
  return {
2087
2866
  width: diagramWidth,
@@ -2095,12 +2874,23 @@ function sequenceLayout(diagram, options) {
2095
2874
  notes: positionedNotes
2096
2875
  };
2097
2876
  }
2098
- function estimateTextSize(text, fontSize) {
2099
- const avgCharWidth = fontSize * .6;
2100
- return {
2101
- width: text.length * avgCharWidth,
2102
- height: fontSize * 1.4
2103
- };
2877
+ /** Position a single note and advance currentY, returning the new currentY */
2878
+ function positionNote(note, _index, currentY, out, participantX, opts) {
2879
+ const xs = note.participants.map((id) => participantX.get(id) ?? 0).filter((x) => x > 0);
2880
+ let x;
2881
+ if (note.position === "left of") x = (xs[0] ?? opts.diagramPadding) - opts.nodeWidth;
2882
+ else if (note.position === "right of") x = (xs[0] ?? opts.diagramPadding) + opts.nodeWidth;
2883
+ else x = xs.length > 0 ? xs.reduce((a, b) => a + b, 0) / xs.length : opts.diagramPadding;
2884
+ const size = estimateTextSize(note.text, 12);
2885
+ const noteHeight = Math.max(size.height + opts.nodePadding, 30);
2886
+ out.push({
2887
+ note,
2888
+ x,
2889
+ y: currentY,
2890
+ width: Math.max(size.width + opts.nodePadding * 2, 80),
2891
+ height: noteHeight
2892
+ });
2893
+ return currentY + noteHeight + opts.rankSpacing * .3;
2104
2894
  }
2105
2895
 
2106
2896
  //#endregion
@@ -2169,21 +2959,16 @@ const c$11 = classes([
2169
2959
  "blockBg",
2170
2960
  "blockLabel",
2171
2961
  "blockLabelText",
2962
+ "blockSectionLine",
2963
+ "blockSectionLabel",
2172
2964
  "noteBg",
2173
2965
  "noteText"
2174
2966
  ]);
2175
2967
  const participantBox = rule`${c$11.participantBox} {
2176
- fill: ${tokens$1.actorFill};
2177
- stroke: ${tokens$1.actorStroke};
2178
- stroke-width: 2;
2968
+ ${boxShape(tokens$1.actorFill, tokens$1.actorStroke, 2)}
2179
2969
  }`;
2180
2970
  const participantLabel = rule`${c$11.participantLabel} {
2181
- fill: ${tokens$1.nodeText};
2182
- stroke: none;
2183
- font-family: ${tokens$1.fontFamily};
2184
- font-size: ${tokens$1.fontSize}px;
2185
- text-anchor: middle;
2186
- dominant-baseline: central;
2971
+ ${textLabel(tokens$1.nodeText, `${tokens$1.fontSize}px`)}
2187
2972
  }`;
2188
2973
  const lifeline = rule`${c$11.lifeline} {
2189
2974
  stroke: ${tokens$1.lifelineStroke};
@@ -2191,9 +2976,7 @@ const lifeline = rule`${c$11.lifeline} {
2191
2976
  stroke-dasharray: 6, 4;
2192
2977
  }`;
2193
2978
  const activation = rule`${c$11.activation} {
2194
- fill: ${tokens$1.activationFill};
2195
- stroke: ${tokens$1.actorStroke};
2196
- stroke-width: 1;
2979
+ ${boxShape(tokens$1.activationFill, tokens$1.actorStroke, 1)}
2197
2980
  }`;
2198
2981
  const messageLine = rule`${c$11.messageLine} {
2199
2982
  stroke: ${tokens$1.messageStroke};
@@ -2204,39 +2987,33 @@ const messageDotted = rule`${c$11.messageDotted} {
2204
2987
  stroke-dasharray: 6, 4;
2205
2988
  }`;
2206
2989
  const messageText = rule`${c$11.messageText} {
2207
- fill: ${tokens$1.nodeText};
2208
- stroke: none;
2209
- font-family: ${tokens$1.fontFamily};
2210
- font-size: 12px;
2211
- text-anchor: middle;
2990
+ ${textLabel(tokens$1.nodeText, "12px")}
2212
2991
  }`;
2213
2992
  const blockBg = rule`${c$11.blockBg} {
2214
- fill: ${tokens$1.blockFill};
2215
- stroke: ${tokens$1.blockStroke};
2216
- stroke-width: 1;
2993
+ ${boxShape(tokens$1.blockFill, tokens$1.blockStroke, 1)}
2217
2994
  }`;
2218
2995
  const blockLabel = rule`${c$11.blockLabel} {
2219
2996
  fill: ${tokens$1.blockStroke};
2220
2997
  }`;
2221
2998
  const blockLabelText = rule`${c$11.blockLabelText} {
2222
- fill: ${tokens$1.nodeText};
2223
- stroke: none;
2224
- font-family: ${tokens$1.fontFamily};
2225
- font-size: 11px;
2999
+ ${textLabel(tokens$1.nodeText, "11px")}
2226
3000
  font-weight: 600;
2227
3001
  }`;
2228
- const noteBg = rule`${c$11.noteBg} {
2229
- fill: ${tokens$1.noteBg};
2230
- stroke: ${tokens$1.noteStroke};
3002
+ const blockSectionLine = rule`${c$11.blockSectionLine} {
3003
+ stroke: ${tokens$1.blockStroke};
2231
3004
  stroke-width: 1;
3005
+ stroke-dasharray: 6, 4;
3006
+ }`;
3007
+ const blockSectionLabel = rule`${c$11.blockSectionLabel} {
3008
+ ${textLabel(tokens$1.nodeText, "11px")}
3009
+ font-weight: 600;
3010
+ font-style: italic;
3011
+ }`;
3012
+ const noteBg = rule`${c$11.noteBg} {
3013
+ ${boxShape(tokens$1.noteBg, tokens$1.noteStroke, 1)}
2232
3014
  }`;
2233
3015
  const noteText = rule`${c$11.noteText} {
2234
- fill: ${tokens$1.noteText};
2235
- stroke: none;
2236
- font-family: ${tokens$1.fontFamily};
2237
- font-size: 12px;
2238
- text-anchor: middle;
2239
- dominant-baseline: central;
3016
+ ${textLabel(tokens$1.noteText, "12px")}
2240
3017
  }`;
2241
3018
 
2242
3019
  //#endregion
@@ -2262,19 +3039,36 @@ function Participant(props) {
2262
3039
 
2263
3040
  //#endregion
2264
3041
  //#region ../mermaid/src/components/sequence/message.tsx
2265
- const MARKER = {
2266
- solid: "url(#mmd-arrow)",
2267
- dotted: "url(#mmd-arrow)",
2268
- solidCross: "url(#mmd-cross)",
2269
- dottedCross: "url(#mmd-cross)",
2270
- solidOpen: "url(#mmd-arrow-open)",
2271
- dottedOpen: "url(#mmd-arrow-open)"
2272
- };
2273
3042
  function Message(props) {
2274
3043
  const { message, fromX, toX, y } = props;
2275
- const isDotted = message.arrow === "dotted" || message.arrow === "dottedCross" || message.arrow === "dottedOpen";
3044
+ const isDotted = isDottedArrow(message.arrow);
3045
+ const markerEnd = sequenceMarker(message.arrow);
3046
+ if (message.from === message.to) {
3047
+ const loopWidth = toX - fromX;
3048
+ const loopHeight = 30;
3049
+ const path = [
3050
+ `M ${fromX} ${y}`,
3051
+ `L ${fromX + loopWidth} ${y}`,
3052
+ `L ${fromX + loopWidth} ${y + loopHeight}`,
3053
+ `L ${fromX} ${y + loopHeight}`
3054
+ ].join(" ");
3055
+ return /* @__PURE__ */ jsxs("g", {
3056
+ class: props.class,
3057
+ children: [/* @__PURE__ */ jsx("path", {
3058
+ class: [messageLine, isDotted ? messageDotted : void 0],
3059
+ d: path,
3060
+ fill: "none",
3061
+ "marker-end": markerEnd
3062
+ }), /* @__PURE__ */ jsx("text", {
3063
+ class: messageText,
3064
+ x: fromX + loopWidth / 2 + loopWidth / 2 + 6,
3065
+ y: y + loopHeight / 2,
3066
+ "text-anchor": "start",
3067
+ children: message.text
3068
+ })]
3069
+ });
3070
+ }
2276
3071
  const midX = (fromX + toX) / 2;
2277
- const markerEnd = MARKER[message.arrow];
2278
3072
  return /* @__PURE__ */ jsxs("g", {
2279
3073
  class: props.class,
2280
3074
  children: [/* @__PURE__ */ jsx("line", {
@@ -2320,7 +3114,10 @@ function Activation(props) {
2320
3114
  //#endregion
2321
3115
  //#region ../mermaid/src/components/sequence/block.tsx
2322
3116
  function Block(props) {
2323
- const { block, x, y, width, height } = props;
3117
+ const { block, x, y, width, height, sectionDividers } = props;
3118
+ const typeText = block.type;
3119
+ const labelText = block.label ? `${typeText} [${block.label}]` : typeText;
3120
+ const labelWidth = Math.max(labelText.length * 7 + 10, 50);
2324
3121
  return /* @__PURE__ */ jsxs("g", {
2325
3122
  class: props.class,
2326
3123
  children: [
@@ -2336,16 +3133,33 @@ function Block(props) {
2336
3133
  class: blockLabel,
2337
3134
  x,
2338
3135
  y,
2339
- width: 50,
3136
+ width: labelWidth,
2340
3137
  height: 20,
2341
3138
  rx: 4
2342
3139
  }),
2343
3140
  /* @__PURE__ */ jsx("text", {
2344
3141
  class: blockLabelText,
2345
- x: x + 5,
2346
- y: y + 14,
2347
- children: block.type
2348
- })
3142
+ x: x + labelWidth / 2,
3143
+ y: y + 10,
3144
+ children: labelText
3145
+ }),
3146
+ sectionDividers?.map((div) => /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("line", {
3147
+ class: blockSectionLine,
3148
+ x1: x,
3149
+ y1: div.y,
3150
+ x2: x + width,
3151
+ y2: div.y
3152
+ }), div.label && /* @__PURE__ */ jsxs("text", {
3153
+ class: blockSectionLabel,
3154
+ x: x + 8,
3155
+ y: div.y + 14,
3156
+ "text-anchor": "start",
3157
+ children: [
3158
+ "[",
3159
+ div.label,
3160
+ "]"
3161
+ ]
3162
+ })] }))
2349
3163
  ]
2350
3164
  });
2351
3165
  }
@@ -2451,7 +3265,8 @@ function Sequence(props, ctx) {
2451
3265
  x: b.x,
2452
3266
  y: b.y,
2453
3267
  width: b.width,
2454
- height: b.height
3268
+ height: b.height,
3269
+ sectionDividers: b.sectionDividers
2455
3270
  })),
2456
3271
  positioned.activations.map((a) => /* @__PURE__ */ jsx(ActivationComp, {
2457
3272
  participant: a.participant,
@@ -2509,18 +3324,20 @@ function walk(node) {
2509
3324
  if (!node.children) return;
2510
3325
  for (let i = 0; i < node.children.length; i++) {
2511
3326
  const child = node.children[i];
2512
- if (child.type === "code" && child.lang === "mermaid") node.children[i] = {
2513
- type: "mdxJsxFlowElement",
2514
- name: "Mermaid",
2515
- attributes: [{
2516
- type: "mdxJsxAttribute",
2517
- name: "code",
2518
- value: child.value ?? ""
2519
- }],
2520
- children: [],
2521
- data: { _mdxExplicitJsx: true }
2522
- };
2523
- else walk(child);
3327
+ if (child.type === "code" && child.lang === "mermaid") {
3328
+ if (child.meta && /\braw\b/.test(child.meta)) continue;
3329
+ node.children[i] = {
3330
+ type: "mdxJsxFlowElement",
3331
+ name: "Mermaid",
3332
+ attributes: [{
3333
+ type: "mdxJsxAttribute",
3334
+ name: "code",
3335
+ value: child.value ?? ""
3336
+ }],
3337
+ children: [],
3338
+ data: { _mdxExplicitJsx: true }
3339
+ };
3340
+ } else walk(child);
2524
3341
  }
2525
3342
  }
2526
3343
  function remarkMermaid() {