schematex 0.2.5 → 0.3.2

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 (194) hide show
  1. package/README.md +4 -3
  2. package/dist/ai/ai-sdk.cjs +25 -25
  3. package/dist/ai/ai-sdk.d.cts +2 -2
  4. package/dist/ai/ai-sdk.d.ts +2 -2
  5. package/dist/ai/ai-sdk.js +20 -20
  6. package/dist/ai/index.cjs +28 -28
  7. package/dist/ai/index.d.cts +3 -3
  8. package/dist/ai/index.d.ts +3 -3
  9. package/dist/ai/index.js +20 -20
  10. package/dist/{api-bQZ98gkJ.d.cts → api-BuFilDQB.d.cts} +1 -1
  11. package/dist/{api-bQZ98gkJ.d.ts → api-BuFilDQB.d.ts} +1 -1
  12. package/dist/browser.cjs +22 -22
  13. package/dist/browser.d.cts +2 -2
  14. package/dist/browser.d.ts +2 -2
  15. package/dist/browser.js +20 -20
  16. package/dist/{chunk-J2LVOWVY.js → chunk-2J2QWNGI.js} +46 -24
  17. package/dist/chunk-2J2QWNGI.js.map +1 -0
  18. package/dist/{chunk-MCFQAUQV.cjs → chunk-2UJAVPA4.cjs} +58 -39
  19. package/dist/chunk-2UJAVPA4.cjs.map +1 -0
  20. package/dist/{chunk-5AEN2PLB.cjs → chunk-3YXXZ4LT.cjs} +33 -33
  21. package/dist/{chunk-5AEN2PLB.cjs.map → chunk-3YXXZ4LT.cjs.map} +1 -1
  22. package/dist/{chunk-RP5UATRA.js → chunk-56LXBM45.js} +4 -4
  23. package/dist/{chunk-RP5UATRA.js.map → chunk-56LXBM45.js.map} +1 -1
  24. package/dist/{chunk-OC22GGQN.js → chunk-6BKUD5EJ.js} +54 -4
  25. package/dist/chunk-6BKUD5EJ.js.map +1 -0
  26. package/dist/{chunk-ULYRO2KY.cjs → chunk-75BMFCP5.cjs} +44 -44
  27. package/dist/{chunk-ULYRO2KY.cjs.map → chunk-75BMFCP5.cjs.map} +1 -1
  28. package/dist/{chunk-ZNDIGQJD.js → chunk-7BEJHG43.js} +3 -3
  29. package/dist/{chunk-ZNDIGQJD.js.map → chunk-7BEJHG43.js.map} +1 -1
  30. package/dist/{chunk-WYFXOXVK.cjs → chunk-7MVDN5UC.cjs} +35 -35
  31. package/dist/{chunk-WYFXOXVK.cjs.map → chunk-7MVDN5UC.cjs.map} +1 -1
  32. package/dist/{chunk-JDTB7IKL.js → chunk-BJ65PKDU.js} +3 -3
  33. package/dist/{chunk-JDTB7IKL.js.map → chunk-BJ65PKDU.js.map} +1 -1
  34. package/dist/{chunk-FE6GAUNW.js → chunk-BJWMPPEA.js} +5 -5
  35. package/dist/{chunk-FE6GAUNW.js.map → chunk-BJWMPPEA.js.map} +1 -1
  36. package/dist/{chunk-MJGDP3CS.cjs → chunk-BUN3CRMP.cjs} +488 -109
  37. package/dist/chunk-BUN3CRMP.cjs.map +1 -0
  38. package/dist/{chunk-COLTVQWR.cjs → chunk-C3IVD7DI.cjs} +25 -25
  39. package/dist/{chunk-COLTVQWR.cjs.map → chunk-C3IVD7DI.cjs.map} +1 -1
  40. package/dist/{chunk-3YZ6FPQW.cjs → chunk-EYHD7LV3.cjs} +131 -131
  41. package/dist/{chunk-3YZ6FPQW.cjs.map → chunk-EYHD7LV3.cjs.map} +1 -1
  42. package/dist/{chunk-UGCUNADI.js → chunk-F6OROIHS.js} +35 -16
  43. package/dist/chunk-F6OROIHS.js.map +1 -0
  44. package/dist/{chunk-UAGSCTYI.cjs → chunk-GYTWJ6VB.cjs} +85 -63
  45. package/dist/chunk-GYTWJ6VB.cjs.map +1 -0
  46. package/dist/{chunk-MR5HU5WU.js → chunk-H4CJTKEH.js} +50 -4
  47. package/dist/chunk-H4CJTKEH.js.map +1 -0
  48. package/dist/{chunk-45KP67RR.js → chunk-HIQPEAL7.js} +5 -5
  49. package/dist/chunk-HIQPEAL7.js.map +1 -0
  50. package/dist/{chunk-LR4X4ZRG.js → chunk-IT2TVXC7.js} +20 -16
  51. package/dist/chunk-IT2TVXC7.js.map +1 -0
  52. package/dist/{chunk-2JDVJRR3.cjs → chunk-K5QG53GT.cjs} +28 -28
  53. package/dist/chunk-K5QG53GT.cjs.map +1 -0
  54. package/dist/{chunk-B37IKTI7.cjs → chunk-KUXOHLGC.cjs} +97 -47
  55. package/dist/chunk-KUXOHLGC.cjs.map +1 -0
  56. package/dist/{chunk-A5D2IMOX.cjs → chunk-L2KUGWFR.cjs} +47 -47
  57. package/dist/{chunk-A5D2IMOX.cjs.map → chunk-L2KUGWFR.cjs.map} +1 -1
  58. package/dist/{chunk-HDKDQAEQ.cjs → chunk-LFZZ4NCP.cjs} +14 -2
  59. package/dist/chunk-LFZZ4NCP.cjs.map +1 -0
  60. package/dist/{chunk-U6L3FAML.js → chunk-M3R6RCXY.js} +3 -3
  61. package/dist/{chunk-U6L3FAML.js.map → chunk-M3R6RCXY.js.map} +1 -1
  62. package/dist/{chunk-5YYAYW67.js → chunk-M5ZC3LFJ.js} +3 -3
  63. package/dist/{chunk-5YYAYW67.js.map → chunk-M5ZC3LFJ.js.map} +1 -1
  64. package/dist/{chunk-B6INLQBU.cjs → chunk-MKKFIPKU.cjs} +46 -46
  65. package/dist/{chunk-B6INLQBU.cjs.map → chunk-MKKFIPKU.cjs.map} +1 -1
  66. package/dist/{chunk-M5B2UUNW.js → chunk-NFT6VW73.js} +4 -4
  67. package/dist/{chunk-M5B2UUNW.js.map → chunk-NFT6VW73.js.map} +1 -1
  68. package/dist/{chunk-X7RPFTTR.cjs → chunk-OK5RYX55.cjs} +26 -26
  69. package/dist/{chunk-X7RPFTTR.cjs.map → chunk-OK5RYX55.cjs.map} +1 -1
  70. package/dist/{chunk-VPKCW4PB.js → chunk-RNGYXGHS.js} +2854 -42
  71. package/dist/chunk-RNGYXGHS.js.map +1 -0
  72. package/dist/{chunk-DPQYGWCT.cjs → chunk-RODV6PC4.cjs} +52 -6
  73. package/dist/chunk-RODV6PC4.cjs.map +1 -0
  74. package/dist/{chunk-H2OEUBPO.js → chunk-SE23X5OE.js} +441 -62
  75. package/dist/chunk-SE23X5OE.js.map +1 -0
  76. package/dist/{chunk-FCGHV6ZK.js → chunk-SFSZUOFT.js} +4 -4
  77. package/dist/{chunk-FCGHV6ZK.js.map → chunk-SFSZUOFT.js.map} +1 -1
  78. package/dist/{chunk-E65ITQXV.cjs → chunk-UK7JF5QB.cjs} +52 -48
  79. package/dist/chunk-UK7JF5QB.cjs.map +1 -0
  80. package/dist/{chunk-QSQX77S2.cjs → chunk-W7GIQTJV.cjs} +21 -21
  81. package/dist/{chunk-QSQX77S2.cjs.map → chunk-W7GIQTJV.cjs.map} +1 -1
  82. package/dist/{chunk-KLJEK547.js → chunk-WHJXRLFD.js} +14 -3
  83. package/dist/chunk-WHJXRLFD.js.map +1 -0
  84. package/dist/{chunk-YQANC7HQ.js → chunk-X4F6VVEJ.js} +3 -3
  85. package/dist/{chunk-YQANC7HQ.js.map → chunk-X4F6VVEJ.js.map} +1 -1
  86. package/dist/{chunk-ZNOD4VZT.cjs → chunk-XI6JOG76.cjs} +46 -46
  87. package/dist/{chunk-ZNOD4VZT.cjs.map → chunk-XI6JOG76.cjs.map} +1 -1
  88. package/dist/{chunk-4S2WILLW.cjs → chunk-XUEROLSB.cjs} +72 -19
  89. package/dist/chunk-XUEROLSB.cjs.map +1 -0
  90. package/dist/{chunk-MSYBSOU2.cjs → chunk-YTEEZV6J.cjs} +3055 -241
  91. package/dist/chunk-YTEEZV6J.cjs.map +1 -0
  92. package/dist/{chunk-PGALHQFF.js → chunk-ZNLEUL7T.js} +60 -7
  93. package/dist/chunk-ZNLEUL7T.js.map +1 -0
  94. package/dist/{chunk-DNZFOCV7.js → chunk-ZTSO3S4P.js} +3 -3
  95. package/dist/{chunk-DNZFOCV7.js.map → chunk-ZTSO3S4P.js.map} +1 -1
  96. package/dist/diagrams/blockdiagram/index.cjs +6 -6
  97. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  98. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  99. package/dist/diagrams/blockdiagram/index.js +2 -2
  100. package/dist/diagrams/circuit/index.cjs +8 -8
  101. package/dist/diagrams/circuit/index.d.cts +1 -1
  102. package/dist/diagrams/circuit/index.d.ts +1 -1
  103. package/dist/diagrams/circuit/index.js +2 -2
  104. package/dist/diagrams/ecomap/index.cjs +8 -8
  105. package/dist/diagrams/ecomap/index.d.cts +1 -1
  106. package/dist/diagrams/ecomap/index.d.ts +1 -1
  107. package/dist/diagrams/ecomap/index.js +3 -3
  108. package/dist/diagrams/entity/index.cjs +6 -6
  109. package/dist/diagrams/entity/index.d.cts +1 -1
  110. package/dist/diagrams/entity/index.d.ts +1 -1
  111. package/dist/diagrams/entity/index.js +2 -2
  112. package/dist/diagrams/fishbone/index.cjs +8 -8
  113. package/dist/diagrams/fishbone/index.d.cts +1 -1
  114. package/dist/diagrams/fishbone/index.d.ts +1 -1
  115. package/dist/diagrams/fishbone/index.js +2 -2
  116. package/dist/diagrams/flowchart/index.cjs +8 -8
  117. package/dist/diagrams/flowchart/index.d.cts +2 -2
  118. package/dist/diagrams/flowchart/index.d.ts +2 -2
  119. package/dist/diagrams/flowchart/index.js +2 -2
  120. package/dist/diagrams/genogram/index.cjs +10 -10
  121. package/dist/diagrams/genogram/index.d.cts +1 -1
  122. package/dist/diagrams/genogram/index.d.ts +1 -1
  123. package/dist/diagrams/genogram/index.js +3 -3
  124. package/dist/diagrams/ladder/index.cjs +6 -6
  125. package/dist/diagrams/ladder/index.d.cts +1 -1
  126. package/dist/diagrams/ladder/index.d.ts +1 -1
  127. package/dist/diagrams/ladder/index.js +2 -2
  128. package/dist/diagrams/logic/index.cjs +6 -6
  129. package/dist/diagrams/logic/index.d.cts +1 -1
  130. package/dist/diagrams/logic/index.d.ts +1 -1
  131. package/dist/diagrams/logic/index.js +2 -2
  132. package/dist/diagrams/orgchart/index.cjs +7 -7
  133. package/dist/diagrams/orgchart/index.d.cts +1 -1
  134. package/dist/diagrams/orgchart/index.d.ts +1 -1
  135. package/dist/diagrams/orgchart/index.js +2 -2
  136. package/dist/diagrams/pedigree/index.cjs +8 -8
  137. package/dist/diagrams/pedigree/index.d.cts +1 -1
  138. package/dist/diagrams/pedigree/index.d.ts +1 -1
  139. package/dist/diagrams/pedigree/index.js +3 -3
  140. package/dist/diagrams/phylo/index.cjs +7 -7
  141. package/dist/diagrams/phylo/index.d.cts +1 -1
  142. package/dist/diagrams/phylo/index.d.ts +1 -1
  143. package/dist/diagrams/phylo/index.js +2 -2
  144. package/dist/diagrams/sld/index.cjs +6 -6
  145. package/dist/diagrams/sld/index.d.cts +1 -1
  146. package/dist/diagrams/sld/index.d.ts +1 -1
  147. package/dist/diagrams/sld/index.js +2 -2
  148. package/dist/diagrams/sociogram/index.cjs +7 -7
  149. package/dist/diagrams/sociogram/index.d.cts +1 -1
  150. package/dist/diagrams/sociogram/index.d.ts +1 -1
  151. package/dist/diagrams/sociogram/index.js +3 -3
  152. package/dist/diagrams/timing/index.cjs +5 -5
  153. package/dist/diagrams/timing/index.d.cts +1 -1
  154. package/dist/diagrams/timing/index.d.ts +1 -1
  155. package/dist/diagrams/timing/index.js +2 -2
  156. package/dist/diagrams/venn/index.cjs +9 -9
  157. package/dist/diagrams/venn/index.d.cts +1 -1
  158. package/dist/diagrams/venn/index.d.ts +1 -1
  159. package/dist/diagrams/venn/index.js +2 -2
  160. package/dist/{index-BTZEka65.d.cts → index-BrLxEzSQ.d.cts} +1 -1
  161. package/dist/{index-DcU88F9i.d.ts → index-dWDwG6BW.d.ts} +1 -1
  162. package/dist/index.cjs +47 -39
  163. package/dist/index.d.cts +8 -4
  164. package/dist/index.d.ts +8 -4
  165. package/dist/index.js +19 -19
  166. package/dist/react.cjs +20 -20
  167. package/dist/react.cjs.map +1 -1
  168. package/dist/react.d.cts +1 -1
  169. package/dist/react.d.ts +1 -1
  170. package/dist/react.js +19 -19
  171. package/dist/react.js.map +1 -1
  172. package/dist/{types-C4LnMEcB.d.cts → types-BtiUg7Gx.d.cts} +25 -4
  173. package/dist/{types-C4LnMEcB.d.ts → types-BtiUg7Gx.d.ts} +25 -4
  174. package/package.json +2 -2
  175. package/dist/chunk-2JDVJRR3.cjs.map +0 -1
  176. package/dist/chunk-45KP67RR.js.map +0 -1
  177. package/dist/chunk-4S2WILLW.cjs.map +0 -1
  178. package/dist/chunk-B37IKTI7.cjs.map +0 -1
  179. package/dist/chunk-DPQYGWCT.cjs.map +0 -1
  180. package/dist/chunk-E65ITQXV.cjs.map +0 -1
  181. package/dist/chunk-H2OEUBPO.js.map +0 -1
  182. package/dist/chunk-HDKDQAEQ.cjs.map +0 -1
  183. package/dist/chunk-J2LVOWVY.js.map +0 -1
  184. package/dist/chunk-KLJEK547.js.map +0 -1
  185. package/dist/chunk-LR4X4ZRG.js.map +0 -1
  186. package/dist/chunk-MCFQAUQV.cjs.map +0 -1
  187. package/dist/chunk-MJGDP3CS.cjs.map +0 -1
  188. package/dist/chunk-MR5HU5WU.js.map +0 -1
  189. package/dist/chunk-MSYBSOU2.cjs.map +0 -1
  190. package/dist/chunk-OC22GGQN.js.map +0 -1
  191. package/dist/chunk-PGALHQFF.js.map +0 -1
  192. package/dist/chunk-UAGSCTYI.cjs.map +0 -1
  193. package/dist/chunk-UGCUNADI.js.map +0 -1
  194. package/dist/chunk-VPKCW4PB.js.map +0 -1
@@ -1,21 +1,21 @@
1
- import { orgchart } from './chunk-FCGHV6ZK.js';
2
- import { circuit } from './chunk-PGALHQFF.js';
3
- import { blockdiagram } from './chunk-UGCUNADI.js';
4
- import { ladder } from './chunk-U6L3FAML.js';
5
- import { sld } from './chunk-FE6GAUNW.js';
6
- import { entity } from './chunk-M5B2UUNW.js';
7
- import { fishbone } from './chunk-ZNDIGQJD.js';
8
- import { venn } from './chunk-JDTB7IKL.js';
9
- import { flowchart } from './chunk-J2LVOWVY.js';
10
- import { genogram } from './chunk-H2OEUBPO.js';
11
- import { ecomap } from './chunk-LR4X4ZRG.js';
12
- import { pedigree } from './chunk-OC22GGQN.js';
13
- import { phylo } from './chunk-YQANC7HQ.js';
14
- import { sociogram } from './chunk-RP5UATRA.js';
15
- import { timing } from './chunk-45KP67RR.js';
16
- import { logic } from './chunk-5YYAYW67.js';
1
+ import { orgchart } from './chunk-SFSZUOFT.js';
2
+ import { circuit } from './chunk-ZNLEUL7T.js';
3
+ import { blockdiagram } from './chunk-F6OROIHS.js';
4
+ import { ladder } from './chunk-M3R6RCXY.js';
5
+ import { sld } from './chunk-BJWMPPEA.js';
6
+ import { entity } from './chunk-NFT6VW73.js';
7
+ import { fishbone } from './chunk-7BEJHG43.js';
8
+ import { venn } from './chunk-BJ65PKDU.js';
9
+ import { flowchart, layoutFlowchart } from './chunk-2J2QWNGI.js';
10
+ import { genogram } from './chunk-SE23X5OE.js';
11
+ import { ecomap } from './chunk-IT2TVXC7.js';
12
+ import { pedigree } from './chunk-6BKUD5EJ.js';
13
+ import { phylo } from './chunk-X4F6VVEJ.js';
14
+ import { sociogram } from './chunk-56LXBM45.js';
15
+ import { timing } from './chunk-HIQPEAL7.js';
16
+ import { logic } from './chunk-M5ZC3LFJ.js';
17
17
  import { resolveBaseTheme, resolveTimelineTheme, cssCustomProperties, resolveMindmapTheme } from './chunk-2VNMKOUO.js';
18
- import { title, desc, el, text, path, rect, group, svgRoot, escapeXml, defs, circle, polygon, line } from './chunk-KLJEK547.js';
18
+ import { title, desc, el, text, path, rect, group, svgRoot, escapeXml, defs, circle, polygon, line } from './chunk-WHJXRLFD.js';
19
19
 
20
20
  // src/diagrams/decisiontree/parser.ts
21
21
  var DTreeParseError = class extends Error {
@@ -621,7 +621,7 @@ function layoutDecisionTree(ast) {
621
621
  enforceSibGap(root, sibH, sibGap);
622
622
  const levelOffsets = computeLevelOffsets(root, sibH, levelGap);
623
623
  setFinal(root, sibH, levelOffsets);
624
- const PADDING2 = 40;
624
+ const PADDING3 = 40;
625
625
  const extraLeft = ast.mode === "decision" && !sibH ? 110 : 0;
626
626
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
627
627
  for (const n of all) {
@@ -646,8 +646,8 @@ function layoutDecisionTree(ast) {
646
646
  maxX = Math.max(maxX, n.xFinal + n.size.w / 2);
647
647
  }
648
648
  }
649
- const offsetX = PADDING2 + extraLeft - minX;
650
- const offsetY = PADDING2 - minY;
649
+ const offsetX = PADDING3 + extraLeft - minX;
650
+ const offsetY = PADDING3 - minY;
651
651
  const layoutNodes = all.map((w) => ({
652
652
  node: w.node,
653
653
  x: w.xFinal + offsetX,
@@ -708,8 +708,8 @@ function layoutDecisionTree(ast) {
708
708
  for (const n of layoutNodes) if (n.node.kind === "end") endX = Math.max(endX, n.x + n.width / 2);
709
709
  payoffColumnX = endX + payoffColGap;
710
710
  }
711
- const width = Math.ceil(maxX - minX + PADDING2 * 2 + extraRight + extraLeft);
712
- const height = Math.ceil(maxY - minY + PADDING2 * 2);
711
+ const width = Math.ceil(maxX - minX + PADDING3 * 2 + extraRight + extraLeft);
712
+ const height = Math.ceil(maxY - minY + PADDING3 * 2);
713
713
  return {
714
714
  width,
715
715
  height,
@@ -1397,23 +1397,35 @@ function parseTimeline(src) {
1397
1397
  i++;
1398
1398
  continue;
1399
1399
  }
1400
- if (/^track\b/i.test(text2)) {
1401
- const body = text2.replace(/^track\s+/i, "");
1402
- const [name, restAfter] = readQuoted(body, L.line);
1403
- if (!restAfter.trim().startsWith(":")) {
1404
- throw new TimelineParseError(`Expected ':' after track name`, L.line);
1400
+ const isTrack = /^track\b/i.test(text2);
1401
+ const isSection = /^section\b/i.test(text2);
1402
+ if (isTrack || isSection) {
1403
+ const keyword = isTrack ? "track" : "section";
1404
+ const body = text2.replace(new RegExp(`^${keyword}\\s+`, "i"), "");
1405
+ let name;
1406
+ if (body.startsWith('"')) {
1407
+ const [n] = readQuoted(body, L.line);
1408
+ name = n;
1409
+ } else {
1410
+ name = body.replace(/:\s*$/, "").trim();
1411
+ if (!name) {
1412
+ throw new TimelineParseError(`Expected name after '${keyword}'`, L.line);
1413
+ }
1405
1414
  }
1406
1415
  const trackId = nextId2("track");
1407
1416
  ast.tracks.push({ id: trackId, label: name });
1408
1417
  i++;
1409
- while (i < lines.length && lines[i].indent > L.indent) {
1418
+ const baseIndent = L.indent;
1419
+ while (i < lines.length) {
1410
1420
  const child = lines[i];
1421
+ if (child.indent <= baseIndent && /^(section|track)\b/i.test(child.text)) break;
1422
+ if (isTrack && child.indent <= baseIndent) break;
1411
1423
  if (/^note\s*:/i.test(child.text)) {
1412
1424
  i++;
1413
1425
  continue;
1414
1426
  }
1415
1427
  const parsed2 = parseEventLine(child.text, child.line, nextId2);
1416
- if (!parsed2) throw new TimelineParseError(`Unrecognized line in track: ${child.text}`, child.line);
1428
+ if (!parsed2) throw new TimelineParseError(`Unrecognized line in ${keyword}: ${child.text}`, child.line);
1417
1429
  parsed2.event.trackId = trackId;
1418
1430
  ast.events.push(parsed2.event);
1419
1431
  i++;
@@ -2626,6 +2638,2804 @@ var timeline = {
2626
2638
  }
2627
2639
  };
2628
2640
 
2641
+ // src/diagrams/state/parser.ts
2642
+ var StateParseError = class extends Error {
2643
+ constructor(message, line2) {
2644
+ super(line2 !== void 0 ? `Line ${line2}: ${message}` : message);
2645
+ this.line = line2;
2646
+ this.name = "StateParseError";
2647
+ }
2648
+ line;
2649
+ };
2650
+ var PSEUDO_KEYWORDS = {
2651
+ initial: "initial",
2652
+ final: "final",
2653
+ choice: "choice",
2654
+ junction: "junction",
2655
+ fork: "fork",
2656
+ join: "join",
2657
+ history: "history",
2658
+ dhistory: "dhistory",
2659
+ terminate: "terminate",
2660
+ entry_point: "entry_point",
2661
+ exit_point: "exit_point"
2662
+ };
2663
+ var MERMAID_STEREOTYPE = {
2664
+ choice: "choice",
2665
+ fork: "fork",
2666
+ join: "join",
2667
+ end: "final"
2668
+ };
2669
+ function preprocess3(src) {
2670
+ const out = [];
2671
+ const lines = src.split(/\r?\n/);
2672
+ for (let i = 0; i < lines.length; i++) {
2673
+ const raw = lines[i];
2674
+ if (raw === void 0) continue;
2675
+ const trimmed = raw.trim();
2676
+ if (!trimmed) continue;
2677
+ if (trimmed.startsWith("#") || trimmed.startsWith("//") || trimmed.startsWith("%%")) continue;
2678
+ const indent = raw.length - raw.replace(/^\s+/, "").length;
2679
+ out.push({ indent, text: trimmed, line: i + 1 });
2680
+ }
2681
+ return out;
2682
+ }
2683
+ function unquote2(s) {
2684
+ if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
2685
+ return s.slice(1, -1);
2686
+ }
2687
+ return s;
2688
+ }
2689
+ function parseProps(s) {
2690
+ const out = {};
2691
+ const inner = s.replace(/^\[/, "").replace(/\]$/, "");
2692
+ if (!inner.trim()) return out;
2693
+ for (const part of inner.split(",")) {
2694
+ const idx = part.indexOf(":");
2695
+ if (idx < 0) continue;
2696
+ out[part.slice(0, idx).trim()] = unquote2(part.slice(idx + 1).trim());
2697
+ }
2698
+ return out;
2699
+ }
2700
+ function parseTransitionLabel(label) {
2701
+ const out = {};
2702
+ let rest = label.trim();
2703
+ if (!rest) return out;
2704
+ let depth = 0;
2705
+ let slashIdx = -1;
2706
+ for (let i = 0; i < rest.length; i++) {
2707
+ const c = rest[i];
2708
+ if (c === "[") depth++;
2709
+ else if (c === "]") depth--;
2710
+ else if (c === "/" && depth === 0) {
2711
+ slashIdx = i;
2712
+ break;
2713
+ }
2714
+ }
2715
+ if (slashIdx >= 0) {
2716
+ out.action = rest.slice(slashIdx + 1).trim();
2717
+ rest = rest.slice(0, slashIdx).trim();
2718
+ }
2719
+ const gMatch = rest.match(/^(?<trig>[^[]*?)\s*\[(?<guard>[^\]]*)\]\s*$/);
2720
+ if (gMatch?.groups) {
2721
+ out.guard = gMatch.groups.guard.trim();
2722
+ const trig = gMatch.groups.trig.trim();
2723
+ if (trig) out.trigger = trig;
2724
+ } else if (rest.length) {
2725
+ out.trigger = rest;
2726
+ }
2727
+ return out;
2728
+ }
2729
+ function parseActivityLine(text2) {
2730
+ const match = text2.match(/^(entry|exit|do)\s*\/\s*(.*)$/);
2731
+ if (match) {
2732
+ return { kind: match[1], action: match[2].trim() };
2733
+ }
2734
+ const parsed = parseTransitionLabel(text2);
2735
+ if (!parsed.trigger && !parsed.guard && !parsed.action) return void 0;
2736
+ return {
2737
+ kind: "internal",
2738
+ trigger: parsed.trigger,
2739
+ guard: parsed.guard,
2740
+ action: parsed.action
2741
+ };
2742
+ }
2743
+ function newPseudoId(ctx, kind) {
2744
+ ctx.pseudoCounter += 1;
2745
+ return `__${kind}_${ctx.pseudoCounter}`;
2746
+ }
2747
+ function ensureInitialAlias(ctx, parent) {
2748
+ if (!parent) {
2749
+ if (ctx.initialAlias) {
2750
+ const existing = ctx.byId.get(ctx.initialAlias);
2751
+ if (existing) return existing;
2752
+ }
2753
+ const node2 = {
2754
+ id: newPseudoId(ctx, "initial"),
2755
+ label: "",
2756
+ kind: "pseudo",
2757
+ pseudoKind: "initial",
2758
+ activities: [],
2759
+ children: []
2760
+ };
2761
+ ctx.initialAlias = node2.id;
2762
+ ctx.byId.set(node2.id, node2);
2763
+ ctx.states.push(node2);
2764
+ return node2;
2765
+ }
2766
+ for (const child of parent.children) {
2767
+ if (child.id.startsWith("__initial_") && child.label === "") return child;
2768
+ }
2769
+ const node = {
2770
+ id: newPseudoId(ctx, "initial"),
2771
+ label: "",
2772
+ kind: "pseudo",
2773
+ pseudoKind: "initial",
2774
+ activities: [],
2775
+ children: [],
2776
+ parent: parent.id
2777
+ };
2778
+ ctx.byId.set(node.id, node);
2779
+ parent.children.push(node);
2780
+ return node;
2781
+ }
2782
+ function ensureFinalAlias(ctx, parent) {
2783
+ if (!parent) {
2784
+ if (ctx.finalAlias) {
2785
+ const existing = ctx.byId.get(ctx.finalAlias);
2786
+ if (existing) return existing;
2787
+ }
2788
+ const node2 = {
2789
+ id: newPseudoId(ctx, "final"),
2790
+ label: "",
2791
+ kind: "pseudo",
2792
+ pseudoKind: "final",
2793
+ activities: [],
2794
+ children: []
2795
+ };
2796
+ ctx.finalAlias = node2.id;
2797
+ ctx.byId.set(node2.id, node2);
2798
+ ctx.states.push(node2);
2799
+ return node2;
2800
+ }
2801
+ for (const child of parent.children) {
2802
+ if (child.id.startsWith("__final_") && child.label === "") return child;
2803
+ }
2804
+ const node = {
2805
+ id: newPseudoId(ctx, "final"),
2806
+ label: "",
2807
+ kind: "pseudo",
2808
+ pseudoKind: "final",
2809
+ activities: [],
2810
+ children: [],
2811
+ parent: parent.id
2812
+ };
2813
+ ctx.byId.set(node.id, node);
2814
+ parent.children.push(node);
2815
+ return node;
2816
+ }
2817
+ function ensureSimpleState(ctx, id, parent) {
2818
+ const existing = ctx.byId.get(id);
2819
+ if (existing) return existing;
2820
+ const node = {
2821
+ id,
2822
+ label: id,
2823
+ kind: "simple",
2824
+ activities: [],
2825
+ children: [],
2826
+ parent: parent?.id
2827
+ };
2828
+ ctx.byId.set(id, node);
2829
+ if (parent) parent.children.push(node);
2830
+ else ctx.states.push(node);
2831
+ return node;
2832
+ }
2833
+ function isIdent(tok) {
2834
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(tok);
2835
+ }
2836
+ function parseStateDiagram(src) {
2837
+ const lines = preprocess3(src);
2838
+ if (lines.length === 0) {
2839
+ throw new StateParseError("Empty document");
2840
+ }
2841
+ const header = lines[0];
2842
+ const headerTok = header.text.match(/^(stateDiagram-v2|stateDiagram|state)\b/i);
2843
+ if (!headerTok) {
2844
+ throw new StateParseError(
2845
+ `Expected 'state' or 'stateDiagram' header, got '${header.text}'`,
2846
+ header.line
2847
+ );
2848
+ }
2849
+ let title2;
2850
+ let direction = "TB";
2851
+ const headerRest = header.text.slice(headerTok[0].length).trim();
2852
+ const propsMatch = headerRest.match(/\[[^\]]*\]\s*$/);
2853
+ let beforeProps = headerRest;
2854
+ if (propsMatch) {
2855
+ const props = parseProps(propsMatch[0]);
2856
+ if (props.direction === "TB" || props.direction === "LR") direction = props.direction;
2857
+ beforeProps = headerRest.slice(0, propsMatch.index).trim();
2858
+ }
2859
+ if (beforeProps.startsWith('"')) title2 = unquote2(beforeProps);
2860
+ else if (beforeProps.length > 0) title2 = beforeProps;
2861
+ const ctx = {
2862
+ states: [],
2863
+ transitions: [],
2864
+ notes: [],
2865
+ pseudoCounter: 0,
2866
+ noteCounter: 0,
2867
+ transCounter: 0,
2868
+ byId: /* @__PURE__ */ new Map()
2869
+ };
2870
+ const compositeStack = [{ parent: void 0, regionMode: false }];
2871
+ let i = 1;
2872
+ while (i < lines.length) {
2873
+ const ln = lines[i];
2874
+ const text2 = ln.text;
2875
+ const ctxTop = compositeStack[compositeStack.length - 1];
2876
+ const parent = ctxTop.parent;
2877
+ if (text2 === "}") {
2878
+ if (compositeStack.length <= 1) {
2879
+ throw new StateParseError("Unexpected '}'", ln.line);
2880
+ }
2881
+ compositeStack.pop();
2882
+ i++;
2883
+ continue;
2884
+ }
2885
+ if (text2 === "---" || text2 === "--") {
2886
+ if (!parent) {
2887
+ throw new StateParseError(
2888
+ "Region separator only allowed inside a composite",
2889
+ ln.line
2890
+ );
2891
+ }
2892
+ ctxTop.regionMode = true;
2893
+ if (!parent.regions) parent.regions = [];
2894
+ const lastIdx = parent.regions.reduce((s, r) => s + r.length, 0);
2895
+ const slice = parent.children.slice(lastIdx);
2896
+ parent.regions.push(slice);
2897
+ i++;
2898
+ continue;
2899
+ }
2900
+ const dirMatch = text2.match(/^direction\s+(TB|BT|LR|RL)\s*$/);
2901
+ if (dirMatch) {
2902
+ const d = dirMatch[1];
2903
+ direction = d === "BT" ? "TB" : d === "RL" ? "LR" : d;
2904
+ i++;
2905
+ continue;
2906
+ }
2907
+ const compMatch = text2.match(/^(?:composite|state)\s+([A-Za-z_][A-Za-z0-9_]*)\s*\{?\s*$/);
2908
+ const isCompositeWithBrace = compMatch && text2.endsWith("{");
2909
+ if (isCompositeWithBrace) {
2910
+ const id = compMatch[1];
2911
+ const node = ensureSimpleState(ctx, id, parent);
2912
+ node.kind = "composite";
2913
+ compositeStack.push({ parent: node, regionMode: false });
2914
+ i++;
2915
+ continue;
2916
+ }
2917
+ const aliasMatch = text2.match(/^state\s+"([^"]*)"\s+as\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/);
2918
+ if (aliasMatch) {
2919
+ const node = ensureSimpleState(ctx, aliasMatch[2], parent);
2920
+ node.label = aliasMatch[1];
2921
+ i++;
2922
+ continue;
2923
+ }
2924
+ const stereoMatch = text2.match(/^state\s+([A-Za-z_][A-Za-z0-9_]*)\s+<<\s*(choice|fork|join|end)\s*>>\s*$/);
2925
+ if (stereoMatch) {
2926
+ const id = stereoMatch[1];
2927
+ const kind = MERMAID_STEREOTYPE[stereoMatch[2]];
2928
+ const node = {
2929
+ id,
2930
+ label: "",
2931
+ kind: "pseudo",
2932
+ pseudoKind: kind,
2933
+ activities: [],
2934
+ children: [],
2935
+ parent: parent?.id
2936
+ };
2937
+ ctx.byId.set(id, node);
2938
+ if (parent) parent.children.push(node);
2939
+ else ctx.states.push(node);
2940
+ i++;
2941
+ continue;
2942
+ }
2943
+ const stateLabelMatch = text2.match(/^state\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.+)$/);
2944
+ if (stateLabelMatch) {
2945
+ const node = ensureSimpleState(ctx, stateLabelMatch[1], parent);
2946
+ node.label = unquote2(stateLabelMatch[2].trim());
2947
+ i++;
2948
+ continue;
2949
+ }
2950
+ const pseudoMatch = text2.match(
2951
+ /^(initial|final|choice|junction|fork|join|history|dhistory|terminate|entry_point|exit_point)\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/
2952
+ );
2953
+ if (pseudoMatch) {
2954
+ const kindKw = pseudoMatch[1];
2955
+ const id = pseudoMatch[2];
2956
+ const pkind = PSEUDO_KEYWORDS[kindKw];
2957
+ const node = {
2958
+ id,
2959
+ label: "",
2960
+ kind: "pseudo",
2961
+ pseudoKind: pkind,
2962
+ activities: [],
2963
+ children: [],
2964
+ parent: parent?.id
2965
+ };
2966
+ ctx.byId.set(id, node);
2967
+ if (parent) parent.children.push(node);
2968
+ else ctx.states.push(node);
2969
+ i++;
2970
+ continue;
2971
+ }
2972
+ if (parent) {
2973
+ const activity = parseActivityLine(text2);
2974
+ if (activity && (activity.kind === "entry" || activity.kind === "exit" || activity.kind === "do")) {
2975
+ parent.activities.push(activity);
2976
+ i++;
2977
+ continue;
2978
+ }
2979
+ }
2980
+ const noteSimple = text2.match(
2981
+ /^note\s+(left[_ ]of|right[_ ]of)\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.*)$/
2982
+ );
2983
+ if (noteSimple) {
2984
+ const side = noteSimple[1].startsWith("left") ? "left" : "right";
2985
+ const target = noteSimple[2];
2986
+ ctx.noteCounter += 1;
2987
+ ctx.notes.push({
2988
+ id: `__note_${ctx.noteCounter}`,
2989
+ target,
2990
+ side,
2991
+ text: noteSimple[3].trim()
2992
+ });
2993
+ i++;
2994
+ continue;
2995
+ }
2996
+ const noteBlockMermaid = text2.match(
2997
+ /^note\s+(left[_ ]of|right[_ ]of)\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/
2998
+ );
2999
+ if (noteBlockMermaid) {
3000
+ const side = noteBlockMermaid[1].startsWith("left") ? "left" : "right";
3001
+ const target = noteBlockMermaid[2];
3002
+ const buf = [];
3003
+ i++;
3004
+ while (i < lines.length) {
3005
+ const t = lines[i].text;
3006
+ if (t === "end note" || t === "}") break;
3007
+ buf.push(t);
3008
+ i++;
3009
+ }
3010
+ if (i >= lines.length) {
3011
+ throw new StateParseError("Unterminated note block", ln.line);
3012
+ }
3013
+ i++;
3014
+ ctx.noteCounter += 1;
3015
+ ctx.notes.push({
3016
+ id: `__note_${ctx.noteCounter}`,
3017
+ target,
3018
+ side,
3019
+ text: buf.join("\n")
3020
+ });
3021
+ continue;
3022
+ }
3023
+ const noteBlockSchematex = text2.match(
3024
+ /^note\s+(left[_ ]of\s+|right[_ ]of\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*\{\s*$/
3025
+ );
3026
+ if (noteBlockSchematex) {
3027
+ const side = noteBlockSchematex[1]?.startsWith("left") ? "left" : "right";
3028
+ const target = noteBlockSchematex[2];
3029
+ const buf = [];
3030
+ i++;
3031
+ while (i < lines.length && lines[i].text !== "}") {
3032
+ buf.push(lines[i].text);
3033
+ i++;
3034
+ }
3035
+ if (i >= lines.length) {
3036
+ throw new StateParseError("Unterminated note block", ln.line);
3037
+ }
3038
+ i++;
3039
+ ctx.noteCounter += 1;
3040
+ ctx.notes.push({
3041
+ id: `__note_${ctx.noteCounter}`,
3042
+ target,
3043
+ side,
3044
+ text: buf.join("\n")
3045
+ });
3046
+ continue;
3047
+ }
3048
+ const transMatch = text2.match(
3049
+ /^(\[\*\]|[A-Za-z_][A-Za-z0-9_]*)\s*-+>\s*(\[\*\]|[A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*(.*))?$/
3050
+ );
3051
+ if (transMatch) {
3052
+ const fromTok = transMatch[1];
3053
+ const toTok = transMatch[2];
3054
+ const labelRaw = transMatch[3];
3055
+ const resolveTok = (tok, position) => {
3056
+ if (tok === "[*]") {
3057
+ if (position === "from") return ensureInitialAlias(ctx, parent).id;
3058
+ return ensureFinalAlias(ctx, parent).id;
3059
+ }
3060
+ return ensureSimpleState(ctx, tok, parent).id;
3061
+ };
3062
+ const fromId = resolveTok(fromTok, "from");
3063
+ const toId = resolveTok(toTok, "to");
3064
+ ctx.transCounter += 1;
3065
+ const tid = `t${ctx.transCounter}`;
3066
+ const parsedLabel = labelRaw ? parseTransitionLabel(labelRaw) : {};
3067
+ ctx.transitions.push({
3068
+ id: tid,
3069
+ from: fromId,
3070
+ to: toId,
3071
+ trigger: parsedLabel.trigger,
3072
+ guard: parsedLabel.guard,
3073
+ action: parsedLabel.action
3074
+ });
3075
+ i++;
3076
+ continue;
3077
+ }
3078
+ const labelOnlyMatch = text2.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.+)$/);
3079
+ if (labelOnlyMatch && isIdent(labelOnlyMatch[1])) {
3080
+ const node = ensureSimpleState(ctx, labelOnlyMatch[1], parent);
3081
+ node.label = unquote2(labelOnlyMatch[2].trim());
3082
+ i++;
3083
+ continue;
3084
+ }
3085
+ const bareIdent = text2.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*$/);
3086
+ if (bareIdent) {
3087
+ ensureSimpleState(ctx, bareIdent[1], parent);
3088
+ i++;
3089
+ continue;
3090
+ }
3091
+ throw new StateParseError(`Unparseable line: ${text2}`, ln.line);
3092
+ }
3093
+ if (compositeStack.length > 1) {
3094
+ throw new StateParseError("Unclosed composite block (expected '}')");
3095
+ }
3096
+ function finalizeRegions(node) {
3097
+ if (node.regions) {
3098
+ const consumed = node.regions.reduce((s, r) => s + r.length, 0);
3099
+ if (node.children.length > consumed) {
3100
+ node.regions.push(node.children.slice(consumed));
3101
+ }
3102
+ }
3103
+ for (const child of node.children) finalizeRegions(child);
3104
+ }
3105
+ for (const s of ctx.states) finalizeRegions(s);
3106
+ return {
3107
+ type: "state",
3108
+ title: title2,
3109
+ direction,
3110
+ states: ctx.states,
3111
+ transitions: ctx.transitions,
3112
+ notes: ctx.notes
3113
+ };
3114
+ }
3115
+
3116
+ // src/diagrams/state/layout.ts
3117
+ var PSEUDO_BBOX = {
3118
+ initial: { w: 22, h: 22 },
3119
+ final: { w: 28, h: 28 },
3120
+ choice: { w: 32, h: 32 },
3121
+ junction: { w: 16, h: 16 },
3122
+ fork: { w: 100, h: 8 },
3123
+ // horizontal bar — rotated for LR direction below
3124
+ join: { w: 100, h: 8 },
3125
+ history: { w: 26, h: 26 },
3126
+ dhistory: { w: 30, h: 30 },
3127
+ terminate: { w: 22, h: 22 },
3128
+ entry_point: { w: 18, h: 18 },
3129
+ exit_point: { w: 18, h: 18 }
3130
+ };
3131
+ var NOTE_W = 180;
3132
+ var NOTE_LINE_H = 14;
3133
+ function shapeForState(node) {
3134
+ if (node.kind === "simple") return "round";
3135
+ if (node.kind === "composite") return "round";
3136
+ switch (node.pseudoKind) {
3137
+ case "initial":
3138
+ case "final":
3139
+ case "junction":
3140
+ case "history":
3141
+ case "dhistory":
3142
+ case "terminate":
3143
+ case "entry_point":
3144
+ case "exit_point":
3145
+ return "circle";
3146
+ case "choice":
3147
+ return "diamond";
3148
+ case "fork":
3149
+ case "join":
3150
+ return "rect";
3151
+ default:
3152
+ return "round";
3153
+ }
3154
+ }
3155
+ function buildLabel(t) {
3156
+ const parts = [];
3157
+ if (t.trigger) parts.push(t.trigger);
3158
+ if (t.guard) parts.push(`[${t.guard}]`);
3159
+ let s = parts.join(" ");
3160
+ if (t.action) s = s ? `${s} / ${t.action}` : `/ ${t.action}`;
3161
+ return s.trim() || void 0;
3162
+ }
3163
+ function convertToFlowchart(ast) {
3164
+ const fcNodes = [];
3165
+ const fcSubgraphs = [];
3166
+ const pseudoSizes = /* @__PURE__ */ new Map();
3167
+ const compositeEntryFor = /* @__PURE__ */ new Map();
3168
+ const compositeExitFor = /* @__PURE__ */ new Map();
3169
+ const visit = (s, parentId) => {
3170
+ if (s.kind === "composite") {
3171
+ const childIds = [];
3172
+ const childSgIds = [];
3173
+ for (const child of s.children) {
3174
+ if (child.kind === "composite") childSgIds.push(child.id);
3175
+ else childIds.push(child.id);
3176
+ }
3177
+ let entryChild;
3178
+ let exitChild;
3179
+ for (const child of s.children) {
3180
+ if (child.kind === "pseudo" && child.pseudoKind === "initial" && !entryChild) {
3181
+ entryChild = child.id;
3182
+ }
3183
+ if (child.kind === "pseudo" && child.pseudoKind === "final") {
3184
+ exitChild = child.id;
3185
+ }
3186
+ }
3187
+ if (!entryChild) entryChild = s.children[0]?.id;
3188
+ if (!exitChild) exitChild = s.children[s.children.length - 1]?.id;
3189
+ if (entryChild) compositeEntryFor.set(s.id, entryChild);
3190
+ if (exitChild) compositeExitFor.set(s.id, exitChild);
3191
+ fcSubgraphs.push({
3192
+ id: s.id,
3193
+ label: s.label || s.id,
3194
+ direction: ast.direction,
3195
+ children: childIds,
3196
+ subgraphs: childSgIds
3197
+ });
3198
+ for (const child of s.children) visit(child, s.id);
3199
+ return;
3200
+ }
3201
+ const node = {
3202
+ id: s.id,
3203
+ label: labelForFlowchart(s),
3204
+ shape: shapeForState(s),
3205
+ parent: parentId
3206
+ };
3207
+ fcNodes.push(node);
3208
+ if (s.kind === "pseudo" && s.pseudoKind) {
3209
+ pseudoSizes.set(s.id, { ...PSEUDO_BBOX[s.pseudoKind] });
3210
+ }
3211
+ };
3212
+ for (const s of ast.states) visit(s, void 0);
3213
+ const fcEdges = ast.transitions.map((t) => {
3214
+ let from = t.from;
3215
+ let to = t.to;
3216
+ const fromExit = compositeExitFor.get(t.from);
3217
+ if (fromExit) from = fromExit;
3218
+ const toEntry = compositeEntryFor.get(t.to);
3219
+ if (toEntry) to = toEntry;
3220
+ return {
3221
+ id: t.id,
3222
+ from,
3223
+ to,
3224
+ kind: "solid",
3225
+ label: buildLabel(t)
3226
+ };
3227
+ });
3228
+ const fc = {
3229
+ type: "flowchart",
3230
+ title: ast.title,
3231
+ direction: ast.direction === "LR" ? "LR" : "TB",
3232
+ nodes: fcNodes,
3233
+ edges: fcEdges,
3234
+ subgraphs: fcSubgraphs,
3235
+ classDefs: [],
3236
+ linkStyles: /* @__PURE__ */ new Map()
3237
+ };
3238
+ return { ast: fc, pseudoSizes };
3239
+ }
3240
+ function labelForFlowchart(s) {
3241
+ if (s.kind === "pseudo") return "";
3242
+ if (s.activities.length === 0) return s.label || s.id;
3243
+ const activityWidth = s.activities.map((a) => activityText(a).length).reduce((m, n) => Math.max(m, n), 0);
3244
+ const label = s.label || s.id;
3245
+ return label.length >= activityWidth ? label : "x".repeat(activityWidth);
3246
+ }
3247
+ function activityText(a) {
3248
+ if (a.kind === "entry" || a.kind === "exit" || a.kind === "do") {
3249
+ return `${a.kind} / ${a.action ?? ""}`;
3250
+ }
3251
+ const parts = [];
3252
+ if (a.trigger) parts.push(a.trigger);
3253
+ if (a.guard) parts.push(`[${a.guard}]`);
3254
+ let s = parts.join(" ");
3255
+ if (a.action) s = s ? `${s} / ${a.action}` : `/ ${a.action}`;
3256
+ return s;
3257
+ }
3258
+ function wrapNoteText(text2, charsPerLine = 28) {
3259
+ const out = [];
3260
+ for (const para of text2.split(/\n/)) {
3261
+ const words = para.split(/\s+/);
3262
+ let cur = "";
3263
+ for (const w of words) {
3264
+ if (!cur) cur = w;
3265
+ else if (cur.length + w.length + 1 <= charsPerLine) cur += ` ${w}`;
3266
+ else {
3267
+ out.push(cur);
3268
+ cur = w;
3269
+ }
3270
+ }
3271
+ if (cur) out.push(cur);
3272
+ if (para === "") out.push("");
3273
+ }
3274
+ return out.length ? out : [""];
3275
+ }
3276
+ function selfLoopPath(cx, cy, w, h) {
3277
+ const startX = cx + w / 2;
3278
+ const startY = cy - h * 0.15;
3279
+ const endX = cx + w * 0.15;
3280
+ const endY = cy - h / 2;
3281
+ const c1x = startX + 28;
3282
+ const c1y = startY - 12;
3283
+ const c2x = endX + 28;
3284
+ const c2y = endY - 28;
3285
+ const path2 = `M ${startX} ${startY} C ${c1x} ${c1y}, ${c2x} ${c2y}, ${endX} ${endY}`;
3286
+ return { path: path2, labelX: startX + 28, labelY: startY - 18 };
3287
+ }
3288
+ function layoutStateDiagram(ast) {
3289
+ const { ast: fcAst, pseudoSizes } = convertToFlowchart(ast);
3290
+ const selfLoops = [];
3291
+ fcAst.edges = fcAst.edges.filter((e) => {
3292
+ if (e.from === e.to) {
3293
+ const t = ast.transitions.find((tr) => tr.id === e.id);
3294
+ if (t) selfLoops.push(t);
3295
+ return false;
3296
+ }
3297
+ return true;
3298
+ });
3299
+ const fcResult = layoutFlowchart(fcAst);
3300
+ const stateById = /* @__PURE__ */ new Map();
3301
+ const collectStates = (s) => {
3302
+ stateById.set(s.id, s);
3303
+ for (const c of s.children) collectStates(c);
3304
+ };
3305
+ for (const s of ast.states) collectStates(s);
3306
+ const stateNodes = [];
3307
+ for (const fcNode of fcResult.nodes) {
3308
+ if (fcNode.isDummy) continue;
3309
+ const s = stateById.get(fcNode.node.id);
3310
+ if (!s) continue;
3311
+ if (s.kind === "composite") continue;
3312
+ const cx = fcNode.x + fcNode.width / 2;
3313
+ const cy = fcNode.y + fcNode.height / 2;
3314
+ let w = fcNode.width;
3315
+ let h = fcNode.height;
3316
+ if (s.kind === "pseudo" && s.pseudoKind) {
3317
+ const ps = pseudoSizes.get(s.id);
3318
+ if (ps) {
3319
+ if ((s.pseudoKind === "fork" || s.pseudoKind === "join") && ast.direction === "LR") {
3320
+ w = 8;
3321
+ h = 100;
3322
+ } else {
3323
+ w = ps.w;
3324
+ h = ps.h;
3325
+ }
3326
+ }
3327
+ }
3328
+ stateNodes.push({
3329
+ id: s.id,
3330
+ x: cx - w / 2,
3331
+ y: cy - h / 2,
3332
+ width: w,
3333
+ height: h,
3334
+ cx,
3335
+ cy,
3336
+ layer: fcNode.layer,
3337
+ node: s,
3338
+ parent: s.parent
3339
+ });
3340
+ }
3341
+ const clusters = fcResult.clusters.map((c) => {
3342
+ const s = stateById.get(c.subgraph.id);
3343
+ if (!s) {
3344
+ return {
3345
+ id: c.subgraph.id,
3346
+ state: { id: c.subgraph.id, label: c.subgraph.label, kind: "composite", activities: [], children: [] },
3347
+ x: c.x,
3348
+ y: c.y,
3349
+ width: c.width,
3350
+ height: c.height
3351
+ };
3352
+ }
3353
+ return {
3354
+ id: c.subgraph.id,
3355
+ state: s,
3356
+ x: c.x,
3357
+ y: c.y,
3358
+ width: c.width,
3359
+ height: c.height
3360
+ };
3361
+ });
3362
+ const stateById2 = new Map(stateNodes.map((n) => [n.id, n]));
3363
+ const stateEdges = [];
3364
+ for (const fcEdge of fcResult.edges) {
3365
+ const t = ast.transitions.find((tr) => tr.id === fcEdge.edge.id);
3366
+ if (!t) continue;
3367
+ let path2 = fcEdge.path;
3368
+ const sourceNode = stateById2.get(fcEdge.edge.from);
3369
+ const targetNode = stateById2.get(fcEdge.edge.to);
3370
+ if (sourceNode && sourceNode.node.kind === "pseudo") {
3371
+ path2 = trimPathStart(path2, sourceNode.cx, sourceNode.cy, symbolRadius(sourceNode));
3372
+ }
3373
+ if (targetNode && targetNode.node.kind === "pseudo") {
3374
+ path2 = trimPathEnd(path2, targetNode.cx, targetNode.cy, symbolRadius(targetNode));
3375
+ }
3376
+ stateEdges.push({
3377
+ id: t.id,
3378
+ from: t.from,
3379
+ to: t.to,
3380
+ path: path2,
3381
+ label: buildLabel(t),
3382
+ labelX: fcEdge.labelAnchor?.x ?? 0,
3383
+ labelY: fcEdge.labelAnchor?.y ?? 0,
3384
+ labelAnchor: fcEdge.labelAnchor?.textAnchor ?? "middle"
3385
+ });
3386
+ }
3387
+ for (const sl of selfLoops) {
3388
+ const host = stateById2.get(sl.from);
3389
+ if (!host) continue;
3390
+ const { path: path2, labelX, labelY } = selfLoopPath(host.cx, host.cy, host.width, host.height);
3391
+ stateEdges.push({
3392
+ id: sl.id,
3393
+ from: sl.from,
3394
+ to: sl.to,
3395
+ path: path2,
3396
+ label: buildLabel(sl),
3397
+ labelX,
3398
+ labelY,
3399
+ labelAnchor: "start",
3400
+ selfLoop: true
3401
+ });
3402
+ }
3403
+ const notes = [];
3404
+ for (const note of ast.notes) {
3405
+ const target = stateById2.get(note.target);
3406
+ if (!target) continue;
3407
+ const lines = wrapNoteText(note.text);
3408
+ const w = NOTE_W;
3409
+ const h = lines.length * NOTE_LINE_H + 14;
3410
+ let x;
3411
+ let y;
3412
+ let leaderX1;
3413
+ let leaderX2;
3414
+ if (note.side === "left") {
3415
+ x = target.x - w - 24;
3416
+ y = target.cy - h / 2;
3417
+ leaderX1 = target.x;
3418
+ leaderX2 = x + w;
3419
+ } else {
3420
+ x = target.x + target.width + 24;
3421
+ y = target.cy - h / 2;
3422
+ leaderX1 = target.x + target.width;
3423
+ leaderX2 = x;
3424
+ }
3425
+ notes.push({
3426
+ note,
3427
+ x,
3428
+ y,
3429
+ width: w,
3430
+ height: h,
3431
+ lines,
3432
+ leader: { x1: leaderX1, y1: target.cy, x2: leaderX2, y2: y + h / 2 }
3433
+ });
3434
+ }
3435
+ let maxX = fcResult.width;
3436
+ let maxY = fcResult.height;
3437
+ let minX = 0;
3438
+ for (const n of notes) {
3439
+ maxX = Math.max(maxX, n.x + n.width + 8);
3440
+ maxY = Math.max(maxY, n.y + n.height + 8);
3441
+ minX = Math.min(minX, n.x - 8);
3442
+ }
3443
+ if (minX < 0) {
3444
+ const dx = -minX;
3445
+ for (const n of stateNodes) {
3446
+ n.x += dx;
3447
+ n.cx += dx;
3448
+ }
3449
+ for (const c of clusters) {
3450
+ c.x += dx;
3451
+ }
3452
+ for (const e of stateEdges) {
3453
+ e.path = shiftPathX(e.path, dx);
3454
+ e.labelX += dx;
3455
+ }
3456
+ for (const n of notes) {
3457
+ n.x += dx;
3458
+ n.leader.x1 += dx;
3459
+ n.leader.x2 += dx;
3460
+ }
3461
+ maxX += dx;
3462
+ }
3463
+ for (const e of stateEdges) {
3464
+ if (!e.selfLoop) continue;
3465
+ maxX = Math.max(maxX, e.labelX + 60);
3466
+ maxY = Math.max(maxY, e.labelY + 60);
3467
+ }
3468
+ const titleOffset = ast.title ? 28 : 0;
3469
+ if (titleOffset) {
3470
+ for (const n of stateNodes) {
3471
+ n.y += titleOffset;
3472
+ n.cy += titleOffset;
3473
+ }
3474
+ for (const c of clusters) {
3475
+ c.y += titleOffset;
3476
+ }
3477
+ for (const e of stateEdges) {
3478
+ e.path = shiftPathY(e.path, titleOffset);
3479
+ e.labelY += titleOffset;
3480
+ }
3481
+ for (const n of notes) {
3482
+ n.y += titleOffset;
3483
+ n.leader.y1 += titleOffset;
3484
+ n.leader.y2 += titleOffset;
3485
+ }
3486
+ maxY += titleOffset;
3487
+ }
3488
+ return {
3489
+ width: maxX,
3490
+ height: maxY,
3491
+ nodes: stateNodes,
3492
+ edges: stateEdges,
3493
+ clusters,
3494
+ notes,
3495
+ title: ast.title,
3496
+ direction: ast.direction
3497
+ };
3498
+ }
3499
+ function symbolRadius(node) {
3500
+ const k = node.node.pseudoKind;
3501
+ switch (k) {
3502
+ case "initial":
3503
+ return 8;
3504
+ case "final":
3505
+ return 11;
3506
+ case "choice":
3507
+ return 14;
3508
+ case "junction":
3509
+ return 6;
3510
+ case "history":
3511
+ return 12;
3512
+ case "dhistory":
3513
+ return 13;
3514
+ case "terminate":
3515
+ return 8;
3516
+ case "entry_point":
3517
+ case "exit_point":
3518
+ return 7;
3519
+ case "fork":
3520
+ case "join":
3521
+ return Math.min(node.width, node.height) / 2;
3522
+ default:
3523
+ return Math.min(node.width, node.height) / 2;
3524
+ }
3525
+ }
3526
+ function parsePathPoints(d) {
3527
+ if (/[CSQTA]/.test(d)) return null;
3528
+ const tokens = d.match(/[ML]\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)/g) ?? [];
3529
+ const out = [];
3530
+ for (const tok of tokens) {
3531
+ const m = tok.match(/[ML]\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)/);
3532
+ if (!m) continue;
3533
+ out.push({ x: parseFloat(m[1]), y: parseFloat(m[2]) });
3534
+ }
3535
+ return out;
3536
+ }
3537
+ function pointsToPath(pts) {
3538
+ if (pts.length === 0) return "";
3539
+ const head = `M ${pts[0].x} ${pts[0].y}`;
3540
+ const rest = pts.slice(1).map((p) => `L ${p.x} ${p.y}`).join(" ");
3541
+ return rest ? `${head} ${rest}` : head;
3542
+ }
3543
+ function trimPathStart(d, cx, cy, r) {
3544
+ const pts = parsePathPoints(d);
3545
+ if (!pts || pts.length < 2) return d;
3546
+ const p0 = pts[0];
3547
+ const p1 = pts[1];
3548
+ const newP0 = projectOnPerimeter(p0, p1, cx, cy, r);
3549
+ pts[0] = newP0;
3550
+ return pointsToPath(pts);
3551
+ }
3552
+ function trimPathEnd(d, cx, cy, r) {
3553
+ const pts = parsePathPoints(d);
3554
+ if (!pts || pts.length < 2) return d;
3555
+ const last = pts[pts.length - 1];
3556
+ const prev = pts[pts.length - 2];
3557
+ const newLast = projectOnPerimeter(last, prev, cx, cy, r);
3558
+ pts[pts.length - 1] = newLast;
3559
+ return pointsToPath(pts);
3560
+ }
3561
+ function projectOnPerimeter(endpoint, neighbor, cx, cy, r) {
3562
+ const dx = endpoint.x - neighbor.x;
3563
+ const dy = endpoint.y - neighbor.y;
3564
+ if (Math.abs(dx) > Math.abs(dy)) {
3565
+ const sign2 = dx >= 0 ? 1 : -1;
3566
+ return { x: cx - sign2 * r, y: cy };
3567
+ }
3568
+ const sign = dy >= 0 ? 1 : -1;
3569
+ return { x: cx, y: cy - sign * r };
3570
+ }
3571
+ function shiftPathX(d, dx) {
3572
+ return d.replace(/([MLCQ])\s*((?:-?\d+(?:\.\d+)?\s+){1,5}-?\d+(?:\.\d+)?)/g, (_, cmd, args) => {
3573
+ const nums = args.trim().split(/\s+/).map(Number);
3574
+ const out = [];
3575
+ for (let i = 0; i < nums.length; i++) {
3576
+ out.push(i % 2 === 0 ? nums[i] + dx : nums[i]);
3577
+ }
3578
+ return `${cmd} ${out.join(" ")}`;
3579
+ });
3580
+ }
3581
+ function shiftPathY(d, dy) {
3582
+ return d.replace(/([MLCQ])\s*((?:-?\d+(?:\.\d+)?\s+){1,5}-?\d+(?:\.\d+)?)/g, (_, cmd, args) => {
3583
+ const nums = args.trim().split(/\s+/).map(Number);
3584
+ const out = [];
3585
+ for (let i = 0; i < nums.length; i++) {
3586
+ out.push(i % 2 === 1 ? nums[i] + dy : nums[i]);
3587
+ }
3588
+ return `${cmd} ${out.join(" ")}`;
3589
+ });
3590
+ }
3591
+
3592
+ // src/diagrams/state/renderer.ts
3593
+ var ARROW_MARKER_ID = "lt-state-arrow";
3594
+ var STYLE = `
3595
+ .lt-state-body { fill: #ffffff; stroke: #2a2a2a; stroke-width: 1.6; }
3596
+ .lt-state-name { font: 600 12px system-ui, sans-serif; fill: #1a1a1a; }
3597
+ .lt-state-div { stroke: #2a2a2a; stroke-width: 1; }
3598
+ .lt-state-activity { font: 11px ui-monospace, monospace; fill: #444; }
3599
+
3600
+ .lt-composite-body { fill: #fafafa; stroke: #2a2a2a; stroke-width: 1.6; }
3601
+ .lt-composite-title { font: 600 12px system-ui, sans-serif; fill: #1a1a1a; }
3602
+ .lt-composite-titlebar { fill: #f0f0f0; stroke: #2a2a2a; stroke-width: 1; }
3603
+ .lt-region-div { stroke: #888; stroke-width: 1; stroke-dasharray: 6 4; }
3604
+
3605
+ .lt-ps-initial { fill: #1a1a1a; }
3606
+ .lt-ps-final-outer { fill: #ffffff; stroke: #1a1a1a; stroke-width: 1.6; }
3607
+ .lt-ps-final-inner { fill: #1a1a1a; }
3608
+ .lt-ps-choice { fill: #ffffff; stroke: #1a1a1a; stroke-width: 1.6; }
3609
+ .lt-ps-junction { fill: #1a1a1a; }
3610
+ .lt-ps-bar { fill: #1a1a1a; }
3611
+ .lt-ps-history-body { fill: #ffffff; stroke: #1a1a1a; stroke-width: 1.6; }
3612
+ .lt-ps-history-text { font: 600 11px serif; fill: #1a1a1a; }
3613
+ .lt-ps-terminate { stroke: #1a1a1a; stroke-width: 2; }
3614
+ .lt-ps-entrypoint { fill: #ffffff; stroke: #1a1a1a; stroke-width: 1.6; }
3615
+ .lt-ps-exitpoint { fill: #ffffff; stroke: #1a1a1a; stroke-width: 1.6; }
3616
+
3617
+ .lt-transition { stroke: #2a2a2a; stroke-width: 1.4; fill: none; }
3618
+ .lt-transition-label { font: 11px system-ui, sans-serif; fill: #1a1a1a; }
3619
+ .lt-transition-label-bg { fill: #ffffff; opacity: 0.92; }
3620
+
3621
+ .lt-note-body { fill: #fff8c5; stroke: #b79400; stroke-width: 1; }
3622
+ .lt-note-text { font: 11px system-ui, sans-serif; fill: #2a2a2a; }
3623
+ .lt-note-leader { stroke: #b79400; stroke-width: 1; stroke-dasharray: 3 3; fill: none; }
3624
+
3625
+ .lt-title { font: 600 14px system-ui, sans-serif; fill: #1a1a1a; }
3626
+ `;
3627
+ function renderArrowMarker() {
3628
+ return el(
3629
+ "marker",
3630
+ {
3631
+ id: ARROW_MARKER_ID,
3632
+ markerWidth: 10,
3633
+ markerHeight: 10,
3634
+ refX: 9,
3635
+ refY: 3,
3636
+ orient: "auto",
3637
+ markerUnits: "strokeWidth"
3638
+ },
3639
+ [polygon({ points: "0,0 10,3 0,6", fill: "#2a2a2a" })]
3640
+ );
3641
+ }
3642
+ function activityText2(a) {
3643
+ if (a.kind === "entry" || a.kind === "exit" || a.kind === "do") {
3644
+ return `${a.kind} / ${a.action ?? ""}`;
3645
+ }
3646
+ const parts = [];
3647
+ if (a.trigger) parts.push(a.trigger);
3648
+ if (a.guard) parts.push(`[${a.guard}]`);
3649
+ let s = parts.join(" ");
3650
+ if (a.action) s = s ? `${s} / ${a.action}` : `/ ${a.action}`;
3651
+ return s;
3652
+ }
3653
+ function renderSimple(node) {
3654
+ const { x, y, width, height } = node;
3655
+ const children = [
3656
+ rect({ x, y, width, height, rx: 8, ry: 8, class: "lt-state-body" })
3657
+ ];
3658
+ const label = node.node.label || node.id;
3659
+ if (node.node.activities.length === 0) {
3660
+ children.push(
3661
+ text(
3662
+ { x: x + width / 2, y: y + height / 2 + 4, "text-anchor": "middle", class: "lt-state-name" },
3663
+ label
3664
+ )
3665
+ );
3666
+ } else {
3667
+ children.push(
3668
+ text(
3669
+ { x: x + width / 2, y: y + 16, "text-anchor": "middle", class: "lt-state-name" },
3670
+ label
3671
+ )
3672
+ );
3673
+ children.push(
3674
+ line({ x1: x, y1: y + 22, x2: x + width, y2: y + 22, class: "lt-state-div" })
3675
+ );
3676
+ let cy = y + 36;
3677
+ for (const a of node.node.activities) {
3678
+ children.push(text({ x: x + 8, y: cy, class: "lt-state-activity" }, activityText2(a)));
3679
+ cy += 14;
3680
+ }
3681
+ }
3682
+ return group({ class: "lt-state lt-simple", "data-id": node.id }, children);
3683
+ }
3684
+ function renderComposite(c) {
3685
+ const { x, y, width, height } = c;
3686
+ const titleBarH = 22;
3687
+ const acts = c.state.activities;
3688
+ const actsH = acts.length ? acts.length * 14 + 6 : 0;
3689
+ const parts = [
3690
+ rect({ x, y, width, height, rx: 10, ry: 10, class: "lt-composite-body" }),
3691
+ // Title bar background — only the top strip
3692
+ path({
3693
+ d: `M ${x + 1} ${y + titleBarH} L ${x + width - 1} ${y + titleBarH}`,
3694
+ class: "lt-state-div"
3695
+ }),
3696
+ text(
3697
+ { x: x + 12, y: y + 16, class: "lt-composite-title" },
3698
+ c.state.label || c.state.id
3699
+ )
3700
+ ];
3701
+ if (acts.length) {
3702
+ let cy = y + titleBarH + 14;
3703
+ for (const a of acts) {
3704
+ parts.push(text({ x: x + 12, y: cy, class: "lt-state-activity" }, activityText2(a)));
3705
+ cy += 14;
3706
+ }
3707
+ parts.push(
3708
+ path({
3709
+ d: `M ${x + 1} ${y + titleBarH + actsH} L ${x + width - 1} ${y + titleBarH + actsH}`,
3710
+ class: "lt-state-div"
3711
+ })
3712
+ );
3713
+ }
3714
+ if (c.regionDividers) {
3715
+ for (const yy of c.regionDividers) {
3716
+ parts.push(
3717
+ line({ x1: x + 1, y1: yy, x2: x + width - 1, y2: yy, class: "lt-region-div" })
3718
+ );
3719
+ }
3720
+ }
3721
+ return group(
3722
+ { class: "lt-state lt-composite", "data-id": c.id },
3723
+ parts
3724
+ );
3725
+ }
3726
+ function renderPseudo(node) {
3727
+ const cx = node.cx;
3728
+ const cy = node.cy;
3729
+ const k = node.node.pseudoKind;
3730
+ switch (k) {
3731
+ case "initial":
3732
+ return group(
3733
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": "initial" },
3734
+ [circle({ cx, cy, r: 8, class: "lt-ps-initial" })]
3735
+ );
3736
+ case "final":
3737
+ return group(
3738
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": "final" },
3739
+ [
3740
+ circle({ cx, cy, r: 11, class: "lt-ps-final-outer" }),
3741
+ circle({ cx, cy, r: 6, class: "lt-ps-final-inner" })
3742
+ ]
3743
+ );
3744
+ case "choice": {
3745
+ const r = 14;
3746
+ return group(
3747
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": "choice" },
3748
+ [
3749
+ polygon({
3750
+ points: `${cx},${cy - r} ${cx + r},${cy} ${cx},${cy + r} ${cx - r},${cy}`,
3751
+ class: "lt-ps-choice"
3752
+ })
3753
+ ]
3754
+ );
3755
+ }
3756
+ case "junction":
3757
+ return group(
3758
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": "junction" },
3759
+ [circle({ cx, cy, r: 6, class: "lt-ps-junction" })]
3760
+ );
3761
+ case "fork":
3762
+ case "join":
3763
+ return group(
3764
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": k },
3765
+ [rect({ x: node.x, y: node.y, width: node.width, height: node.height, rx: 2, ry: 2, class: "lt-ps-bar" })]
3766
+ );
3767
+ case "history":
3768
+ return group(
3769
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": "history" },
3770
+ [
3771
+ circle({ cx, cy, r: 12, class: "lt-ps-history-body" }),
3772
+ text({ x: cx, y: cy + 4, "text-anchor": "middle", class: "lt-ps-history-text" }, "H")
3773
+ ]
3774
+ );
3775
+ case "dhistory":
3776
+ return group(
3777
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": "dhistory" },
3778
+ [
3779
+ circle({ cx, cy, r: 13, class: "lt-ps-history-body" }),
3780
+ text({ x: cx, y: cy + 4, "text-anchor": "middle", class: "lt-ps-history-text" }, "H*")
3781
+ ]
3782
+ );
3783
+ case "terminate":
3784
+ return group(
3785
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": "terminate" },
3786
+ [
3787
+ line({ x1: cx - 8, y1: cy - 8, x2: cx + 8, y2: cy + 8, class: "lt-ps-terminate" }),
3788
+ line({ x1: cx + 8, y1: cy - 8, x2: cx - 8, y2: cy + 8, class: "lt-ps-terminate" })
3789
+ ]
3790
+ );
3791
+ case "entry_point":
3792
+ return group(
3793
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": "entry_point" },
3794
+ [circle({ cx, cy, r: 7, class: "lt-ps-entrypoint" })]
3795
+ );
3796
+ case "exit_point":
3797
+ return group(
3798
+ { class: "lt-state lt-pseudo", "data-id": node.id, "data-kind": "exit_point" },
3799
+ [
3800
+ circle({ cx, cy, r: 7, class: "lt-ps-exitpoint" }),
3801
+ line({ x1: cx - 4, y1: cy - 4, x2: cx + 4, y2: cy + 4, class: "lt-ps-terminate" }),
3802
+ line({ x1: cx + 4, y1: cy - 4, x2: cx - 4, y2: cy + 4, class: "lt-ps-terminate" })
3803
+ ]
3804
+ );
3805
+ default:
3806
+ return "";
3807
+ }
3808
+ }
3809
+ function renderNode(node) {
3810
+ if (node.node.kind === "pseudo") return renderPseudo(node);
3811
+ return renderSimple(node);
3812
+ }
3813
+ function renderEdge(edge) {
3814
+ const parts = [
3815
+ path({
3816
+ d: edge.path,
3817
+ class: "lt-transition",
3818
+ "marker-end": `url(#${ARROW_MARKER_ID})`,
3819
+ "data-from": edge.from,
3820
+ "data-to": edge.to
3821
+ })
3822
+ ];
3823
+ if (edge.label) {
3824
+ const w = Math.max(20, edge.label.length * 6.4 + 8);
3825
+ const anchor = edge.labelAnchor ?? "middle";
3826
+ const dx = anchor === "start" ? 0 : anchor === "end" ? -w : -w / 2;
3827
+ parts.push(
3828
+ rect({
3829
+ x: edge.labelX + dx,
3830
+ y: edge.labelY - 10,
3831
+ width: w,
3832
+ height: 14,
3833
+ rx: 2,
3834
+ ry: 2,
3835
+ class: "lt-transition-label-bg"
3836
+ })
3837
+ );
3838
+ parts.push(
3839
+ text(
3840
+ {
3841
+ x: edge.labelX,
3842
+ y: edge.labelY,
3843
+ "text-anchor": anchor,
3844
+ class: "lt-transition-label"
3845
+ },
3846
+ edge.label
3847
+ )
3848
+ );
3849
+ }
3850
+ return group({ class: "lt-edge", "data-edge-id": edge.id }, parts);
3851
+ }
3852
+ function renderNote(n) {
3853
+ const parts = [
3854
+ line({
3855
+ x1: n.leader.x1,
3856
+ y1: n.leader.y1,
3857
+ x2: n.leader.x2,
3858
+ y2: n.leader.y2,
3859
+ class: "lt-note-leader"
3860
+ }),
3861
+ rect({ x: n.x, y: n.y, width: n.width, height: n.height, rx: 4, ry: 4, class: "lt-note-body" })
3862
+ ];
3863
+ let yy = n.y + 14;
3864
+ for (const ln of n.lines) {
3865
+ parts.push(text({ x: n.x + 8, y: yy, class: "lt-note-text" }, ln));
3866
+ yy += 14;
3867
+ }
3868
+ return group({ class: "lt-note", "data-target": n.note.target }, parts);
3869
+ }
3870
+ function renderStateDiagram(ast, _config) {
3871
+ const layout = layoutStateDiagram(ast);
3872
+ return renderLayout(layout);
3873
+ }
3874
+ function renderLayout(layout) {
3875
+ const titleNode = layout.title ? text({ x: 16, y: 22, class: "lt-title" }, layout.title) : "";
3876
+ return svgRoot(
3877
+ {
3878
+ width: layout.width,
3879
+ height: layout.height,
3880
+ viewBox: `0 0 ${layout.width} ${layout.height}`,
3881
+ class: "lt-state",
3882
+ "data-diagram-type": "state"
3883
+ },
3884
+ [
3885
+ el("title", {}, escapeXml(`State Diagram${layout.title ? " \u2014 " + layout.title : ""}`)),
3886
+ el("desc", {}, "UML 2.5 / Harel statechart rendered by Schematex"),
3887
+ defs([renderArrowMarker(), el("style", {}, STYLE)]),
3888
+ titleNode,
3889
+ // Composite clusters first so simple-state bodies sit on top.
3890
+ group({ class: "lt-clusters" }, layout.clusters.map(renderComposite)),
3891
+ group({ class: "lt-state-bodies" }, layout.nodes.map(renderNode)),
3892
+ group({ class: "lt-edges" }, layout.edges.map(renderEdge)),
3893
+ group({ class: "lt-notes" }, layout.notes.map(renderNote))
3894
+ ]
3895
+ );
3896
+ }
3897
+
3898
+ // src/diagrams/state/index.ts
3899
+ var state = {
3900
+ type: "state",
3901
+ detect(text2) {
3902
+ return /^\s*state\b/i.test(text2);
3903
+ },
3904
+ parse: parseStateDiagram,
3905
+ render(text2, config) {
3906
+ const ast = parseStateDiagram(text2);
3907
+ return renderStateDiagram(ast);
3908
+ }
3909
+ };
3910
+
3911
+ // src/diagrams/pid/parser.ts
3912
+ var PidParseError = class extends Error {
3913
+ constructor(message, line2) {
3914
+ super(line2 !== void 0 ? `Line ${line2}: ${message}` : message);
3915
+ this.line = line2;
3916
+ this.name = "PidParseError";
3917
+ }
3918
+ line;
3919
+ };
3920
+ var EQUIP_TYPES = /* @__PURE__ */ new Set([
3921
+ "tank_atm",
3922
+ "tank_cone_roof",
3923
+ "vessel_v",
3924
+ "vessel_h",
3925
+ "sphere",
3926
+ "column_tray",
3927
+ "column_packed",
3928
+ "hx_shell_tube",
3929
+ "hx_air_cooled",
3930
+ "reboiler",
3931
+ "condenser",
3932
+ "pump_centrifugal",
3933
+ "pump_pd",
3934
+ "compressor",
3935
+ "blower",
3936
+ "reactor_cstr",
3937
+ "reactor_pfr",
3938
+ "filter",
3939
+ "cyclone",
3940
+ "flare",
3941
+ "cooling_tower",
3942
+ "valve_gate",
3943
+ "valve_ball",
3944
+ "valve_globe",
3945
+ "valve_butterfly",
3946
+ "valve_check",
3947
+ "valve_control",
3948
+ "valve_psv"
3949
+ ]);
3950
+ var LINE_TYPES = /* @__PURE__ */ new Set([
3951
+ "process",
3952
+ "process_minor",
3953
+ "pneumatic",
3954
+ "electric",
3955
+ "hydraulic",
3956
+ "capillary",
3957
+ "software",
3958
+ "mechanical"
3959
+ ]);
3960
+ var INST_CATEGORIES = /* @__PURE__ */ new Set([
3961
+ "field_discrete",
3962
+ "field_shared",
3963
+ "field_computer",
3964
+ "field_plc",
3965
+ "cr_discrete",
3966
+ "cr_shared",
3967
+ "cr_computer",
3968
+ "cr_plc",
3969
+ "local_discrete",
3970
+ "local_shared"
3971
+ ]);
3972
+ function preprocess4(src) {
3973
+ const out = [];
3974
+ const lines = src.split(/\r?\n/);
3975
+ for (let i = 0; i < lines.length; i++) {
3976
+ const raw = lines[i];
3977
+ if (raw === void 0) continue;
3978
+ let stripped = "";
3979
+ let inQuote = false;
3980
+ for (const ch of raw) {
3981
+ if (ch === '"') inQuote = !inQuote;
3982
+ if (ch === "#" && !inQuote) break;
3983
+ stripped += ch;
3984
+ }
3985
+ const trimmed = stripped.trim();
3986
+ if (!trimmed) continue;
3987
+ const indent = stripped.length - stripped.replace(/^\s+/, "").length;
3988
+ out.push({ text: trimmed, indent, line: i + 1 });
3989
+ }
3990
+ return out;
3991
+ }
3992
+ function unquote3(s) {
3993
+ const t = s.trim();
3994
+ if (t.length >= 2 && t.startsWith('"') && t.endsWith('"')) {
3995
+ return t.slice(1, -1).replace(/\\"/g, '"');
3996
+ }
3997
+ return t;
3998
+ }
3999
+ function parseAttrList(inside) {
4000
+ const out = {};
4001
+ const parts = [];
4002
+ let depth = 0;
4003
+ let inQuote = false;
4004
+ let cur = "";
4005
+ for (const ch of inside) {
4006
+ if (ch === '"') inQuote = !inQuote;
4007
+ if (!inQuote && ch === "[") depth++;
4008
+ if (!inQuote && ch === "]") depth--;
4009
+ if (!inQuote && depth === 0 && ch === ",") {
4010
+ parts.push(cur);
4011
+ cur = "";
4012
+ } else {
4013
+ cur += ch;
4014
+ }
4015
+ }
4016
+ if (cur.trim()) parts.push(cur);
4017
+ for (const p of parts) {
4018
+ const idx = p.indexOf(":");
4019
+ if (idx < 0) continue;
4020
+ const key = p.slice(0, idx).trim();
4021
+ const val = unquote3(p.slice(idx + 1).trim());
4022
+ out[key] = val;
4023
+ }
4024
+ return out;
4025
+ }
4026
+ function extractAttrs(text2) {
4027
+ let depth = 0;
4028
+ let inQuote = false;
4029
+ let openIdx = -1;
4030
+ for (let i = 0; i < text2.length; i++) {
4031
+ const ch = text2[i];
4032
+ if (ch === '"') inQuote = !inQuote;
4033
+ if (inQuote) continue;
4034
+ if (ch === "[") {
4035
+ if (depth === 0) openIdx = i;
4036
+ depth++;
4037
+ } else if (ch === "]") {
4038
+ depth--;
4039
+ if (depth === 0 && i === text2.length - 1) {
4040
+ const inside = text2.slice(openIdx + 1, i);
4041
+ return {
4042
+ rest: text2.slice(0, openIdx).trim(),
4043
+ attrs: parseAttrList(inside)
4044
+ };
4045
+ }
4046
+ }
4047
+ }
4048
+ return { rest: text2, attrs: {} };
4049
+ }
4050
+ function parseAnchor(tok) {
4051
+ const dot = tok.indexOf(".");
4052
+ if (dot < 0) return { id: tok };
4053
+ return { id: tok.slice(0, dot), port: tok.slice(dot + 1) };
4054
+ }
4055
+ function parsePid(src) {
4056
+ const lines = preprocess4(src);
4057
+ if (lines.length === 0) {
4058
+ throw new PidParseError("Empty document");
4059
+ }
4060
+ const header = lines[0];
4061
+ if (!/^pid\b/i.test(header.text)) {
4062
+ throw new PidParseError(`Expected 'pid' header, got '${header.text}'`, header.line);
4063
+ }
4064
+ let title2;
4065
+ let direction = "LR";
4066
+ const headerRest = header.text.replace(/^pid\b/i, "").trim();
4067
+ const { rest, attrs: headerAttrs } = extractAttrs(headerRest);
4068
+ if (headerAttrs.direction === "TB") direction = "TB";
4069
+ if (headerAttrs.direction === "LR") direction = "LR";
4070
+ if (rest) title2 = unquote3(rest);
4071
+ const equipment = [];
4072
+ const linesAst = [];
4073
+ const instruments = [];
4074
+ let currentInst;
4075
+ for (let idx = 1; idx < lines.length; idx++) {
4076
+ const ln = lines[idx];
4077
+ const text2 = ln.text;
4078
+ const measuresMatch = text2.match(/^measures\s+(.+)$/);
4079
+ if (measuresMatch && currentInst) {
4080
+ currentInst.measures = measuresMatch[1].trim();
4081
+ continue;
4082
+ }
4083
+ const controlsMatch = text2.match(/^controls\s+(.+)$/);
4084
+ if (controlsMatch && currentInst) {
4085
+ currentInst.controls = controlsMatch[1].trim();
4086
+ continue;
4087
+ }
4088
+ currentInst = void 0;
4089
+ const equipMatch = text2.match(/^equip\s+([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(.+)$/);
4090
+ if (equipMatch) {
4091
+ const id = equipMatch[1];
4092
+ const tail = equipMatch[2];
4093
+ const { rest: typeRest, attrs } = extractAttrs(tail);
4094
+ const equipType = typeRest.trim();
4095
+ if (!EQUIP_TYPES.has(equipType)) {
4096
+ throw new PidParseError(`Unknown equipment type '${equipType}'`, ln.line);
4097
+ }
4098
+ equipment.push({
4099
+ id,
4100
+ equipType,
4101
+ tag: attrs.tag ?? id,
4102
+ attrs
4103
+ });
4104
+ continue;
4105
+ }
4106
+ const lineMatch = text2.match(/^line\s+([A-Za-z0-9_-]+)\s+from\s+(\S+)\s+to\s+(\S+)\s*(.*)$/);
4107
+ if (lineMatch) {
4108
+ const id = lineMatch[1];
4109
+ const fromTok = lineMatch[2];
4110
+ const toTok = lineMatch[3];
4111
+ const tail = lineMatch[4];
4112
+ const { attrs } = extractAttrs(tail);
4113
+ const lt = attrs.type ?? "process";
4114
+ if (!LINE_TYPES.has(lt)) {
4115
+ throw new PidParseError(`Unknown line type '${lt}'`, ln.line);
4116
+ }
4117
+ linesAst.push({
4118
+ id,
4119
+ from: parseAnchor(fromTok),
4120
+ to: parseAnchor(toTok),
4121
+ lineType: lt,
4122
+ tag: attrs.tag,
4123
+ size: attrs.size,
4124
+ service: attrs.service,
4125
+ attrs
4126
+ });
4127
+ continue;
4128
+ }
4129
+ const instMatch = text2.match(/^inst\s+([A-Z][A-Z0-9]*-[A-Za-z0-9]+)\s*:\s*(.+)$/);
4130
+ if (instMatch) {
4131
+ const tag = instMatch[1];
4132
+ const { rest: catRest, attrs } = extractAttrs(instMatch[2]);
4133
+ const category = catRest.trim();
4134
+ if (!INST_CATEGORIES.has(category)) {
4135
+ throw new PidParseError(`Unknown instrument category '${category}'`, ln.line);
4136
+ }
4137
+ const inst = {
4138
+ tag,
4139
+ category,
4140
+ attrs
4141
+ };
4142
+ instruments.push(inst);
4143
+ currentInst = inst;
4144
+ continue;
4145
+ }
4146
+ throw new PidParseError(`Unparseable line: ${text2}`, ln.line);
4147
+ }
4148
+ return {
4149
+ type: "pid",
4150
+ title: title2,
4151
+ direction,
4152
+ equipment,
4153
+ lines: linesAst,
4154
+ instruments
4155
+ };
4156
+ }
4157
+
4158
+ // src/diagrams/pid/symbols.ts
4159
+ var GEOMETRY = {
4160
+ // ── Tanks & vessels ─────────────────────────────────────
4161
+ tank_atm: {
4162
+ width: 90,
4163
+ height: 90,
4164
+ ports: {
4165
+ top: { x: 0, y: -45 },
4166
+ bottom: { x: 0, y: 45 },
4167
+ left: { x: -45, y: 0 },
4168
+ right: { x: 45, y: 0 },
4169
+ in: { x: -45, y: 0 },
4170
+ out: { x: 45, y: 0 }
4171
+ }
4172
+ },
4173
+ tank_cone_roof: {
4174
+ width: 90,
4175
+ height: 100,
4176
+ ports: {
4177
+ top: { x: 0, y: -50 },
4178
+ bottom: { x: 0, y: 50 },
4179
+ left: { x: -45, y: 0 },
4180
+ right: { x: 45, y: 0 }
4181
+ }
4182
+ },
4183
+ vessel_v: {
4184
+ width: 70,
4185
+ height: 130,
4186
+ ports: {
4187
+ top: { x: 0, y: -65 },
4188
+ bottom: { x: 0, y: 65 },
4189
+ left: { x: -35, y: 0 },
4190
+ right: { x: 35, y: 0 },
4191
+ in: { x: -35, y: -25 },
4192
+ out: { x: 35, y: 25 }
4193
+ }
4194
+ },
4195
+ vessel_h: {
4196
+ width: 130,
4197
+ height: 70,
4198
+ ports: {
4199
+ top: { x: 0, y: -35 },
4200
+ bottom: { x: 0, y: 35 },
4201
+ left: { x: -65, y: 0 },
4202
+ right: { x: 65, y: 0 },
4203
+ in: { x: -65, y: 0 },
4204
+ out: { x: 65, y: 0 }
4205
+ }
4206
+ },
4207
+ sphere: {
4208
+ width: 90,
4209
+ height: 90,
4210
+ ports: {
4211
+ top: { x: 0, y: -45 },
4212
+ bottom: { x: 0, y: 45 },
4213
+ left: { x: -45, y: 0 },
4214
+ right: { x: 45, y: 0 }
4215
+ }
4216
+ },
4217
+ column_tray: {
4218
+ width: 60,
4219
+ height: 180,
4220
+ ports: {
4221
+ top: { x: 0, y: -90 },
4222
+ bottom: { x: 0, y: 90 },
4223
+ feed: { x: -30, y: 0 },
4224
+ reflux: { x: 0, y: -90 },
4225
+ bottom_return: { x: -30, y: 70 },
4226
+ vapor_out: { x: 0, y: -90 },
4227
+ liquid_out: { x: 0, y: 90 }
4228
+ }
4229
+ },
4230
+ column_packed: {
4231
+ width: 60,
4232
+ height: 180,
4233
+ ports: {
4234
+ top: { x: 0, y: -90 },
4235
+ bottom: { x: 0, y: 90 },
4236
+ feed: { x: -30, y: 0 }
4237
+ }
4238
+ },
4239
+ hx_shell_tube: {
4240
+ width: 130,
4241
+ height: 60,
4242
+ ports: {
4243
+ shell_in: { x: 0, y: -30 },
4244
+ shell_out: { x: 0, y: 30 },
4245
+ tube_in: { x: -65, y: 0 },
4246
+ tube_out: { x: 65, y: 0 },
4247
+ in: { x: -65, y: 0 },
4248
+ out: { x: 65, y: 0 }
4249
+ }
4250
+ },
4251
+ hx_air_cooled: {
4252
+ width: 130,
4253
+ height: 80,
4254
+ ports: {
4255
+ in: { x: -65, y: 10 },
4256
+ out: { x: 65, y: 10 }
4257
+ }
4258
+ },
4259
+ reboiler: {
4260
+ width: 110,
4261
+ height: 60,
4262
+ ports: {
4263
+ in: { x: -55, y: 0 },
4264
+ out: { x: 55, y: 0 },
4265
+ bottom: { x: 0, y: 30 }
4266
+ }
4267
+ },
4268
+ condenser: {
4269
+ width: 130,
4270
+ height: 60,
4271
+ ports: {
4272
+ shell_in: { x: -50, y: -30 },
4273
+ shell_out: { x: 50, y: 30 },
4274
+ tube_in: { x: -65, y: 0 },
4275
+ tube_out: { x: 65, y: 0 },
4276
+ in: { x: -65, y: 0 },
4277
+ out: { x: 65, y: 0 }
4278
+ }
4279
+ },
4280
+ pump_centrifugal: {
4281
+ width: 70,
4282
+ height: 60,
4283
+ ports: {
4284
+ in: { x: -30, y: 0 },
4285
+ out: { x: 28, y: -22 },
4286
+ top: { x: 28, y: -22 },
4287
+ left: { x: -30, y: 0 },
4288
+ right: { x: 28, y: -22 }
4289
+ }
4290
+ },
4291
+ pump_pd: {
4292
+ width: 70,
4293
+ height: 60,
4294
+ ports: {
4295
+ in: { x: -35, y: 0 },
4296
+ out: { x: 35, y: 0 }
4297
+ }
4298
+ },
4299
+ compressor: {
4300
+ width: 90,
4301
+ height: 60,
4302
+ ports: {
4303
+ in: { x: -45, y: 0 },
4304
+ out: { x: 45, y: 0 }
4305
+ }
4306
+ },
4307
+ blower: {
4308
+ width: 70,
4309
+ height: 60,
4310
+ ports: {
4311
+ in: { x: -35, y: 0 },
4312
+ out: { x: 35, y: 0 }
4313
+ }
4314
+ },
4315
+ reactor_cstr: {
4316
+ width: 90,
4317
+ height: 110,
4318
+ ports: {
4319
+ top: { x: 0, y: -55 },
4320
+ bottom: { x: 0, y: 55 },
4321
+ in: { x: -45, y: -10 },
4322
+ out: { x: 0, y: 55 }
4323
+ }
4324
+ },
4325
+ reactor_pfr: {
4326
+ width: 130,
4327
+ height: 50,
4328
+ ports: {
4329
+ in: { x: -65, y: 0 },
4330
+ out: { x: 65, y: 0 }
4331
+ }
4332
+ },
4333
+ filter: {
4334
+ width: 70,
4335
+ height: 70,
4336
+ ports: {
4337
+ in: { x: -35, y: 0 },
4338
+ out: { x: 35, y: 0 }
4339
+ }
4340
+ },
4341
+ cyclone: {
4342
+ width: 70,
4343
+ height: 100,
4344
+ ports: {
4345
+ top: { x: 0, y: -50 },
4346
+ in: { x: -35, y: -30 },
4347
+ bottom: { x: 0, y: 50 },
4348
+ out: { x: 0, y: -50 }
4349
+ }
4350
+ },
4351
+ flare: {
4352
+ width: 30,
4353
+ height: 110,
4354
+ ports: {
4355
+ top: { x: 0, y: -55 },
4356
+ bottom: { x: 0, y: 55 },
4357
+ in: { x: 0, y: 55 }
4358
+ }
4359
+ },
4360
+ cooling_tower: {
4361
+ width: 100,
4362
+ height: 90,
4363
+ ports: {
4364
+ top: { x: 0, y: -45 },
4365
+ in: { x: -50, y: 0 },
4366
+ out: { x: 50, y: 0 }
4367
+ }
4368
+ },
4369
+ // ── Valves (in-line) ──────────────────────────────────
4370
+ valve_gate: {
4371
+ width: 36,
4372
+ height: 22,
4373
+ ports: { in: { x: -18, y: 0 }, out: { x: 18, y: 0 }, left: { x: -18, y: 0 }, right: { x: 18, y: 0 } }
4374
+ },
4375
+ valve_ball: {
4376
+ width: 36,
4377
+ height: 22,
4378
+ ports: { in: { x: -18, y: 0 }, out: { x: 18, y: 0 } }
4379
+ },
4380
+ valve_globe: {
4381
+ width: 36,
4382
+ height: 28,
4383
+ ports: { in: { x: -18, y: 0 }, out: { x: 18, y: 0 } }
4384
+ },
4385
+ valve_butterfly: {
4386
+ width: 36,
4387
+ height: 22,
4388
+ ports: { in: { x: -18, y: 0 }, out: { x: 18, y: 0 } }
4389
+ },
4390
+ valve_check: {
4391
+ width: 36,
4392
+ height: 22,
4393
+ ports: { in: { x: -18, y: 0 }, out: { x: 18, y: 0 } }
4394
+ },
4395
+ valve_control: {
4396
+ width: 36,
4397
+ height: 60,
4398
+ ports: { in: { x: -18, y: 12 }, out: { x: 18, y: 12 }, top: { x: 0, y: -22 } }
4399
+ },
4400
+ valve_psv: {
4401
+ width: 36,
4402
+ height: 60,
4403
+ ports: { in: { x: -18, y: 12 }, out: { x: 18, y: -8 } }
4404
+ }
4405
+ };
4406
+ var STROKE_BLACK = "#1d1d1d";
4407
+ var FILL_WHITE = "#ffffff";
4408
+ function bowtie() {
4409
+ return polygon({
4410
+ points: "-18,-11 0,0 -18,11 18,11 0,0 18,-11",
4411
+ class: "lt-pid-valve-body"
4412
+ });
4413
+ }
4414
+ function renderEquip(type, label) {
4415
+ switch (type) {
4416
+ case "tank_atm": {
4417
+ const w = 90;
4418
+ const h = 90;
4419
+ const cylTop = -h / 2 + 14;
4420
+ const parts = [
4421
+ // dome top
4422
+ path({
4423
+ d: `M ${-w / 2} ${cylTop} A ${w / 2} 14 0 0 1 ${w / 2} ${cylTop}`,
4424
+ class: "lt-pid-equip"
4425
+ }),
4426
+ // body
4427
+ rect({
4428
+ x: -w / 2,
4429
+ y: cylTop,
4430
+ width: w,
4431
+ height: h - 14,
4432
+ class: "lt-pid-equip"
4433
+ }),
4434
+ // tag
4435
+ text(
4436
+ { x: 0, y: 4, "text-anchor": "middle", class: "lt-pid-equip-tag" },
4437
+ label
4438
+ )
4439
+ ];
4440
+ return group({ class: "lt-pid-equip-group" }, parts);
4441
+ }
4442
+ case "tank_cone_roof": {
4443
+ const w = 90;
4444
+ const h = 100;
4445
+ const roofH = 18;
4446
+ const top = -h / 2;
4447
+ return group({}, [
4448
+ polygon({
4449
+ points: `${-w / 2},${top + roofH} 0,${top} ${w / 2},${top + roofH}`,
4450
+ class: "lt-pid-equip"
4451
+ }),
4452
+ rect({
4453
+ x: -w / 2,
4454
+ y: top + roofH,
4455
+ width: w,
4456
+ height: h - roofH,
4457
+ class: "lt-pid-equip"
4458
+ }),
4459
+ text({ x: 0, y: 4, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4460
+ ]);
4461
+ }
4462
+ case "vessel_v": {
4463
+ const w = 70;
4464
+ const h = 130;
4465
+ const headH = 16;
4466
+ const top = -h / 2;
4467
+ const bot = h / 2;
4468
+ return group({}, [
4469
+ path({
4470
+ d: `M ${-w / 2} ${top + headH}
4471
+ A ${w / 2} ${headH} 0 0 1 ${w / 2} ${top + headH}
4472
+ L ${w / 2} ${bot - headH}
4473
+ A ${w / 2} ${headH} 0 0 1 ${-w / 2} ${bot - headH}
4474
+ Z`,
4475
+ class: "lt-pid-equip"
4476
+ }),
4477
+ text(
4478
+ { x: 0, y: 4, "text-anchor": "middle", class: "lt-pid-equip-tag" },
4479
+ label
4480
+ )
4481
+ ]);
4482
+ }
4483
+ case "vessel_h": {
4484
+ const w = 130;
4485
+ const h = 70;
4486
+ const headW = 14;
4487
+ const left = -w / 2;
4488
+ const right = w / 2;
4489
+ return group({}, [
4490
+ path({
4491
+ d: `M ${left + headW} ${-h / 2}
4492
+ L ${right - headW} ${-h / 2}
4493
+ A ${headW} ${h / 2} 0 0 1 ${right - headW} ${h / 2}
4494
+ L ${left + headW} ${h / 2}
4495
+ A ${headW} ${h / 2} 0 0 1 ${left + headW} ${-h / 2}
4496
+ Z`,
4497
+ class: "lt-pid-equip"
4498
+ }),
4499
+ text(
4500
+ { x: 0, y: 4, "text-anchor": "middle", class: "lt-pid-equip-tag" },
4501
+ label
4502
+ )
4503
+ ]);
4504
+ }
4505
+ case "sphere": {
4506
+ return group({}, [
4507
+ circle({ cx: 0, cy: 0, r: 45, class: "lt-pid-equip" }),
4508
+ text({ x: 0, y: 4, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4509
+ ]);
4510
+ }
4511
+ case "column_tray": {
4512
+ const w = 60;
4513
+ const h = 180;
4514
+ const headH = 12;
4515
+ const trays = 12;
4516
+ const inner = h - headH * 2;
4517
+ const traySpacing = inner / (trays + 1);
4518
+ const trayLines = [];
4519
+ for (let i = 1; i <= trays; i++) {
4520
+ const y = -h / 2 + headH + traySpacing * i;
4521
+ trayLines.push(
4522
+ line({
4523
+ x1: -w / 2 + 6,
4524
+ y1: y,
4525
+ x2: w / 2 - 6,
4526
+ y2: y,
4527
+ class: "lt-pid-tray-line"
4528
+ })
4529
+ );
4530
+ }
4531
+ return group({}, [
4532
+ path({
4533
+ d: `M ${-w / 2} ${-h / 2 + headH}
4534
+ A ${w / 2} ${headH} 0 0 1 ${w / 2} ${-h / 2 + headH}
4535
+ L ${w / 2} ${h / 2 - headH}
4536
+ A ${w / 2} ${headH} 0 0 1 ${-w / 2} ${h / 2 - headH}
4537
+ Z`,
4538
+ class: "lt-pid-equip"
4539
+ }),
4540
+ ...trayLines,
4541
+ text(
4542
+ {
4543
+ x: 0,
4544
+ y: -h / 2 - 6,
4545
+ "text-anchor": "middle",
4546
+ class: "lt-pid-equip-tag"
4547
+ },
4548
+ label
4549
+ )
4550
+ ]);
4551
+ }
4552
+ case "column_packed": {
4553
+ const w = 60;
4554
+ const h = 180;
4555
+ const headH = 12;
4556
+ const packing = path({
4557
+ d: `M ${-w / 2 + 6} ${-h / 2 + headH + 6}
4558
+ L ${w / 2 - 6} ${h / 2 - headH - 6}
4559
+ M ${w / 2 - 6} ${-h / 2 + headH + 6}
4560
+ L ${-w / 2 + 6} ${h / 2 - headH - 6}`,
4561
+ class: "lt-pid-tray-line"
4562
+ });
4563
+ return group({}, [
4564
+ path({
4565
+ d: `M ${-w / 2} ${-h / 2 + headH}
4566
+ A ${w / 2} ${headH} 0 0 1 ${w / 2} ${-h / 2 + headH}
4567
+ L ${w / 2} ${h / 2 - headH}
4568
+ A ${w / 2} ${headH} 0 0 1 ${-w / 2} ${h / 2 - headH}
4569
+ Z`,
4570
+ class: "lt-pid-equip"
4571
+ }),
4572
+ packing,
4573
+ text(
4574
+ {
4575
+ x: 0,
4576
+ y: -h / 2 - 6,
4577
+ "text-anchor": "middle",
4578
+ class: "lt-pid-equip-tag"
4579
+ },
4580
+ label
4581
+ )
4582
+ ]);
4583
+ }
4584
+ case "hx_shell_tube": {
4585
+ const w = 130;
4586
+ const h = 60;
4587
+ const headW = 12;
4588
+ const left = -w / 2;
4589
+ const right = w / 2;
4590
+ const tubes = [];
4591
+ for (let yy = -h / 2 + 12; yy <= h / 2 - 12; yy += 8) {
4592
+ tubes.push(
4593
+ line({
4594
+ x1: left + headW + 4,
4595
+ y1: yy,
4596
+ x2: right - headW - 4,
4597
+ y2: yy,
4598
+ class: "lt-pid-tray-line"
4599
+ })
4600
+ );
4601
+ }
4602
+ return group({}, [
4603
+ path({
4604
+ d: `M ${left + headW} ${-h / 2}
4605
+ L ${right - headW} ${-h / 2}
4606
+ A ${headW} ${h / 2} 0 0 1 ${right - headW} ${h / 2}
4607
+ L ${left + headW} ${h / 2}
4608
+ A ${headW} ${h / 2} 0 0 1 ${left + headW} ${-h / 2}
4609
+ Z`,
4610
+ class: "lt-pid-equip"
4611
+ }),
4612
+ ...tubes,
4613
+ text(
4614
+ {
4615
+ x: 0,
4616
+ y: h / 2 + 14,
4617
+ "text-anchor": "middle",
4618
+ class: "lt-pid-equip-tag"
4619
+ },
4620
+ label
4621
+ )
4622
+ ]);
4623
+ }
4624
+ case "hx_air_cooled": {
4625
+ const w = 130;
4626
+ const h = 80;
4627
+ return group({}, [
4628
+ rect({
4629
+ x: -w / 2,
4630
+ y: -h / 2 + 18,
4631
+ width: w,
4632
+ height: h - 18,
4633
+ class: "lt-pid-equip"
4634
+ }),
4635
+ circle({ cx: 0, cy: -h / 2 + 14, r: 18, class: "lt-pid-equip" }),
4636
+ // 3-blade fan
4637
+ path({
4638
+ d: `M 0 ${-h / 2 + 14} L 14 ${-h / 2 + 6}
4639
+ M 0 ${-h / 2 + 14} L -14 ${-h / 2 + 6}
4640
+ M 0 ${-h / 2 + 14} L 0 ${-h / 2 + 30}`,
4641
+ class: "lt-pid-tray-line"
4642
+ }),
4643
+ text(
4644
+ { x: 0, y: h / 2 + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" },
4645
+ label
4646
+ )
4647
+ ]);
4648
+ }
4649
+ case "reboiler": {
4650
+ const w = 110;
4651
+ const h = 60;
4652
+ const headW = 14;
4653
+ return group({}, [
4654
+ path({
4655
+ d: `M ${-w / 2 + headW} ${-h / 2}
4656
+ L ${w / 2 - headW} ${-h / 2}
4657
+ A ${headW} ${h / 2} 0 0 1 ${w / 2 - headW} ${h / 2}
4658
+ L ${-w / 2 + headW} ${h / 2}
4659
+ A ${headW} ${h / 2} 0 0 1 ${-w / 2 + headW} ${-h / 2}
4660
+ Z`,
4661
+ class: "lt-pid-equip"
4662
+ }),
4663
+ line({
4664
+ x1: -w / 2 + headW + 4,
4665
+ y1: -10,
4666
+ x2: w / 2 - headW - 4,
4667
+ y2: -10,
4668
+ class: "lt-pid-tray-line"
4669
+ }),
4670
+ line({
4671
+ x1: -w / 2 + headW + 4,
4672
+ y1: 0,
4673
+ x2: w / 2 - headW - 4,
4674
+ y2: 0,
4675
+ class: "lt-pid-tray-line"
4676
+ }),
4677
+ line({
4678
+ x1: -w / 2 + headW + 4,
4679
+ y1: 10,
4680
+ x2: w / 2 - headW - 4,
4681
+ y2: 10,
4682
+ class: "lt-pid-tray-line"
4683
+ }),
4684
+ text({ x: 0, y: h / 2 + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4685
+ ]);
4686
+ }
4687
+ case "condenser": {
4688
+ const w = 130;
4689
+ const h = 60;
4690
+ const headW = 12;
4691
+ const left = -w / 2;
4692
+ const right = w / 2;
4693
+ const tubes = [];
4694
+ for (let yy = -h / 2 + 12; yy <= h / 2 - 12; yy += 10) {
4695
+ tubes.push(
4696
+ line({
4697
+ x1: left + headW + 4,
4698
+ y1: yy,
4699
+ x2: right - headW - 4,
4700
+ y2: yy,
4701
+ class: "lt-pid-tray-line"
4702
+ })
4703
+ );
4704
+ }
4705
+ return group({}, [
4706
+ path({
4707
+ d: `M ${left + headW} ${-h / 2}
4708
+ L ${right - headW} ${-h / 2}
4709
+ A ${headW} ${h / 2} 0 0 1 ${right - headW} ${h / 2}
4710
+ L ${left + headW} ${h / 2}
4711
+ A ${headW} ${h / 2} 0 0 1 ${left + headW} ${-h / 2}
4712
+ Z`,
4713
+ class: "lt-pid-equip"
4714
+ }),
4715
+ ...tubes,
4716
+ text({ x: 0, y: h / 2 + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4717
+ ]);
4718
+ }
4719
+ case "pump_centrifugal": {
4720
+ const r = 22;
4721
+ return group({}, [
4722
+ circle({ cx: 0, cy: 0, r, class: "lt-pid-equip" }),
4723
+ polygon({
4724
+ points: `${r * 0.4},${-r * 0.9} ${r + 6},${-r * 0.9} ${r * 0.4},${0}`,
4725
+ class: "lt-pid-equip"
4726
+ }),
4727
+ text(
4728
+ { x: 0, y: r + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" },
4729
+ label
4730
+ )
4731
+ ]);
4732
+ }
4733
+ case "pump_pd": {
4734
+ const r = 22;
4735
+ return group({}, [
4736
+ circle({ cx: 0, cy: 0, r, class: "lt-pid-equip" }),
4737
+ circle({ cx: -8, cy: 0, r: 6, class: "lt-pid-tray-line", fill: "none" }),
4738
+ circle({ cx: 8, cy: 0, r: 6, class: "lt-pid-tray-line", fill: "none" }),
4739
+ text(
4740
+ { x: 0, y: r + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" },
4741
+ label
4742
+ )
4743
+ ]);
4744
+ }
4745
+ case "compressor": {
4746
+ return group({}, [
4747
+ polygon({
4748
+ points: "-45,-20 45,-12 45,12 -45,20",
4749
+ class: "lt-pid-equip"
4750
+ }),
4751
+ text({ x: 0, y: 36, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4752
+ ]);
4753
+ }
4754
+ case "blower": {
4755
+ const r = 22;
4756
+ return group({}, [
4757
+ circle({ cx: 0, cy: 0, r, class: "lt-pid-equip" }),
4758
+ path({
4759
+ d: `M 0 0 L ${r * 0.8} ${-r * 0.5} M 0 0 L ${-r * 0.6} ${-r * 0.6} M 0 0 L 0 ${r * 0.8}`,
4760
+ class: "lt-pid-tray-line"
4761
+ }),
4762
+ text({ x: 0, y: r + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4763
+ ]);
4764
+ }
4765
+ case "reactor_cstr": {
4766
+ const w = 90;
4767
+ const h = 110;
4768
+ const headH = 14;
4769
+ const top = -h / 2;
4770
+ const bot = h / 2;
4771
+ return group({}, [
4772
+ path({
4773
+ d: `M ${-w / 2} ${top + headH}
4774
+ A ${w / 2} ${headH} 0 0 1 ${w / 2} ${top + headH}
4775
+ L ${w / 2} ${bot - headH}
4776
+ A ${w / 2} ${headH} 0 0 1 ${-w / 2} ${bot - headH}
4777
+ Z`,
4778
+ class: "lt-pid-equip"
4779
+ }),
4780
+ // agitator shaft + paddle
4781
+ line({ x1: 0, y1: top - 14, x2: 0, y2: 4, class: "lt-pid-tray-line" }),
4782
+ rect({ x: -10, y: 4, width: 20, height: 4, class: "lt-pid-equip" }),
4783
+ text(
4784
+ { x: 0, y: bot + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" },
4785
+ label
4786
+ )
4787
+ ]);
4788
+ }
4789
+ case "reactor_pfr": {
4790
+ const w = 130;
4791
+ const h = 50;
4792
+ const headW = 12;
4793
+ const left = -w / 2;
4794
+ const right = w / 2;
4795
+ const dots = [];
4796
+ for (let xx = left + headW + 8; xx <= right - headW - 8; xx += 12) {
4797
+ for (let yy = -h / 2 + 12; yy <= h / 2 - 8; yy += 10) {
4798
+ dots.push(circle({ cx: xx, cy: yy, r: 1.6, fill: STROKE_BLACK }));
4799
+ }
4800
+ }
4801
+ return group({}, [
4802
+ path({
4803
+ d: `M ${left + headW} ${-h / 2}
4804
+ L ${right - headW} ${-h / 2}
4805
+ A ${headW} ${h / 2} 0 0 1 ${right - headW} ${h / 2}
4806
+ L ${left + headW} ${h / 2}
4807
+ A ${headW} ${h / 2} 0 0 1 ${left + headW} ${-h / 2}
4808
+ Z`,
4809
+ class: "lt-pid-equip"
4810
+ }),
4811
+ ...dots,
4812
+ text({ x: 0, y: h / 2 + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4813
+ ]);
4814
+ }
4815
+ case "filter": {
4816
+ const w = 70;
4817
+ const h = 70;
4818
+ return group({}, [
4819
+ rect({ x: -w / 2, y: -h / 2, width: w, height: h, class: "lt-pid-equip" }),
4820
+ line({ x1: -w / 2, y1: 0, x2: w / 2, y2: 0, class: "lt-pid-tray-line" }),
4821
+ line({ x1: -w / 2 + 8, y1: -h / 2 + 8, x2: w / 2 - 8, y2: -8, class: "lt-pid-tray-line" }),
4822
+ line({ x1: -w / 2 + 8, y1: 8, x2: w / 2 - 8, y2: h / 2 - 8, class: "lt-pid-tray-line" }),
4823
+ text({ x: 0, y: h / 2 + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4824
+ ]);
4825
+ }
4826
+ case "cyclone": {
4827
+ const w = 70;
4828
+ const h = 100;
4829
+ const cyl = 30;
4830
+ return group({}, [
4831
+ path({
4832
+ d: `M ${-w / 2} ${-h / 2}
4833
+ L ${w / 2} ${-h / 2}
4834
+ L ${w / 2} ${-h / 2 + cyl}
4835
+ L 0 ${h / 2}
4836
+ L ${-w / 2} ${-h / 2 + cyl}
4837
+ Z`,
4838
+ class: "lt-pid-equip"
4839
+ }),
4840
+ text({ x: 0, y: h / 2 + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4841
+ ]);
4842
+ }
4843
+ case "flare": {
4844
+ const w = 30;
4845
+ const h = 110;
4846
+ return group({}, [
4847
+ rect({ x: -w / 2, y: -h / 2, width: w, height: h, class: "lt-pid-equip" }),
4848
+ polygon({
4849
+ points: `${ -8},${-h / 2 - 4} 0,${-h / 2 - 22} 8,${-h / 2 - 4}`,
4850
+ fill: "#ff7755",
4851
+ stroke: STROKE_BLACK
4852
+ }),
4853
+ text({ x: 0, y: h / 2 + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4854
+ ]);
4855
+ }
4856
+ case "cooling_tower": {
4857
+ const w = 100;
4858
+ const h = 90;
4859
+ return group({}, [
4860
+ path({
4861
+ d: `M ${-w / 2} ${-h / 2}
4862
+ L ${w / 2} ${-h / 2}
4863
+ L ${w / 4} 0
4864
+ L ${w / 2} ${h / 2}
4865
+ L ${-w / 2} ${h / 2}
4866
+ L ${-w / 4} 0
4867
+ Z`,
4868
+ class: "lt-pid-equip"
4869
+ }),
4870
+ text({ x: 0, y: h / 2 + 14, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4871
+ ]);
4872
+ }
4873
+ // ── Valves ──────────────────────────────────────────
4874
+ case "valve_gate":
4875
+ return group({}, [
4876
+ bowtie(),
4877
+ text({ x: 0, y: 22, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4878
+ ]);
4879
+ case "valve_ball":
4880
+ return group({}, [
4881
+ bowtie(),
4882
+ circle({ cx: 0, cy: 0, r: 4, fill: STROKE_BLACK }),
4883
+ text({ x: 0, y: 22, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4884
+ ]);
4885
+ case "valve_globe":
4886
+ return group({}, [
4887
+ bowtie(),
4888
+ circle({ cx: 0, cy: -5, r: 5, class: "lt-pid-valve-body", fill: FILL_WHITE }),
4889
+ text({ x: 0, y: 22, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4890
+ ]);
4891
+ case "valve_butterfly":
4892
+ return group({}, [
4893
+ bowtie(),
4894
+ line({ x1: 0, y1: -10, x2: 0, y2: 10, class: "lt-pid-tray-line" }),
4895
+ text({ x: 0, y: 22, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4896
+ ]);
4897
+ case "valve_check":
4898
+ return group({}, [
4899
+ bowtie(),
4900
+ path({ d: "M -10 -8 A 10 10 0 0 1 -10 8", class: "lt-pid-tray-line", fill: "none" }),
4901
+ text({ x: 0, y: 22, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4902
+ ]);
4903
+ case "valve_control":
4904
+ return group({}, [
4905
+ bowtie(),
4906
+ // actuator: diaphragm
4907
+ line({ x1: 0, y1: -11, x2: 0, y2: -22, class: "lt-pid-tray-line" }),
4908
+ path({ d: "M -14 -22 A 14 8 0 0 1 14 -22 L -14 -22 Z", class: "lt-pid-equip" }),
4909
+ text({ x: 0, y: 24, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4910
+ ]);
4911
+ case "valve_psv":
4912
+ return group({}, [
4913
+ bowtie(),
4914
+ // diagonal outlet (45°)
4915
+ line({
4916
+ x1: 18,
4917
+ y1: -11,
4918
+ x2: 26,
4919
+ y2: -22,
4920
+ class: "lt-pid-process"
4921
+ }),
4922
+ // spring stack
4923
+ path({
4924
+ d: "M -6 -11 L -10 -16 L -2 -20 L -10 -24 L -2 -28",
4925
+ class: "lt-pid-tray-line",
4926
+ fill: "none"
4927
+ }),
4928
+ text({ x: 0, y: 24, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4929
+ ]);
4930
+ default:
4931
+ return group({}, [
4932
+ rect({ x: -30, y: -20, width: 60, height: 40, class: "lt-pid-equip" }),
4933
+ text({ x: 0, y: 4, "text-anchor": "middle", class: "lt-pid-equip-tag" }, label)
4934
+ ]);
4935
+ }
4936
+ }
4937
+ function renderInstrument(category, letterCode, loopNumber) {
4938
+ const r = 14;
4939
+ const isComputer = category.endsWith("computer");
4940
+ const isPlc = category.endsWith("plc");
4941
+ const isShared = category.endsWith("shared");
4942
+ const isControlRoom = category.startsWith("cr_");
4943
+ const isLocal = category.startsWith("local_");
4944
+ const parts = [];
4945
+ if (isComputer) {
4946
+ parts.push(circle({ cx: 0, cy: 0, r, class: "lt-inst-body" }));
4947
+ parts.push(
4948
+ polygon({
4949
+ points: `0,${-r + 1} ${r - 1},0 0,${r - 1} ${ -13},0`,
4950
+ class: "lt-inst-body",
4951
+ fill: "none"
4952
+ })
4953
+ );
4954
+ } else if (isPlc) {
4955
+ parts.push(circle({ cx: 0, cy: 0, r, class: "lt-inst-body" }));
4956
+ const side = r * Math.SQRT1_2 * 2 - 2;
4957
+ parts.push(
4958
+ rect({
4959
+ x: -side / 2,
4960
+ y: -side / 2,
4961
+ width: side,
4962
+ height: side,
4963
+ class: "lt-inst-body",
4964
+ fill: "none"
4965
+ })
4966
+ );
4967
+ } else if (isShared) {
4968
+ parts.push(circle({ cx: 0, cy: 0, r, class: "lt-inst-body" }));
4969
+ const hex = [];
4970
+ for (let i = 0; i < 6; i++) {
4971
+ const a = Math.PI / 3 * i - Math.PI / 2;
4972
+ hex.push(`${(r - 2) * Math.cos(a)},${(r - 2) * Math.sin(a)}`);
4973
+ }
4974
+ parts.push(
4975
+ polygon({
4976
+ points: hex.join(" "),
4977
+ class: "lt-inst-body",
4978
+ fill: "none"
4979
+ })
4980
+ );
4981
+ } else {
4982
+ parts.push(circle({ cx: 0, cy: 0, r, class: "lt-inst-body" }));
4983
+ }
4984
+ if (isControlRoom) {
4985
+ parts.push(line({ x1: -r, y1: 0, x2: r, y2: 0, class: "lt-inst-cr-line" }));
4986
+ } else if (isLocal) {
4987
+ parts.push(line({ x1: -r, y1: 0, x2: r, y2: 0, class: "lt-inst-local-line" }));
4988
+ }
4989
+ parts.push(
4990
+ text(
4991
+ {
4992
+ x: 0,
4993
+ y: -3,
4994
+ "text-anchor": "middle",
4995
+ class: "lt-inst-tag"
4996
+ },
4997
+ letterCode
4998
+ )
4999
+ );
5000
+ parts.push(
5001
+ text(
5002
+ {
5003
+ x: 0,
5004
+ y: 9,
5005
+ "text-anchor": "middle",
5006
+ class: "lt-inst-tag"
5007
+ },
5008
+ loopNumber
5009
+ )
5010
+ );
5011
+ return group({ class: "lt-inst-symbol" }, parts);
5012
+ }
5013
+
5014
+ // src/diagrams/pid/layout.ts
5015
+ var PADDING = 30;
5016
+ var TITLE_AREA = 26;
5017
+ var EQUIP_GAP_X = 70;
5018
+ var INST_RADIUS = 14;
5019
+ var INST_OFFSET = 38;
5020
+ function defaultPort(direction, equip) {
5021
+ if (equip.equipType === "tank_atm" || equip.equipType === "tank_cone_roof") {
5022
+ return direction === "out" ? "bottom" : "top";
5023
+ }
5024
+ if (equip.equipType === "vessel_v" || equip.equipType === "column_tray" || equip.equipType === "column_packed") {
5025
+ return direction === "out" ? "bottom" : "top";
5026
+ }
5027
+ return direction === "out" ? "out" : "in";
5028
+ }
5029
+ function resolveSide(port) {
5030
+ if (port === "top" || port === "vapor_out" || port === "reflux") return "top";
5031
+ if (port === "bottom" || port === "liquid_out" || port === "bottom_return") return "bottom";
5032
+ if (port === "left" || port === "in" || port === "feed" || port === "tube_in" || port === "shell_in") return "left";
5033
+ return "right";
5034
+ }
5035
+ function getAnchor(layoutEq, port, fallback) {
5036
+ const ports = layoutEq.ports;
5037
+ let key = port;
5038
+ if (!key || !(key in ports)) {
5039
+ key = defaultPort(fallback, layoutEq.equip);
5040
+ }
5041
+ const p = ports[key] ?? ports.right ?? ports.out ?? ports.left ?? ports.in;
5042
+ if (!p) return { x: layoutEq.cx, y: layoutEq.cy, side: "right" };
5043
+ return { x: p.x, y: p.y, side: resolveSide(key ?? "right") };
5044
+ }
5045
+ function manhattanPath(fromX, fromY, fromSide, toX, toY, toSide) {
5046
+ const isHFrom = fromSide === "left" || fromSide === "right";
5047
+ const isHTo = toSide === "left" || toSide === "right";
5048
+ if (isHFrom && isHTo) {
5049
+ const midX = (fromX + toX) / 2;
5050
+ return {
5051
+ d: `M ${fromX} ${fromY} L ${midX} ${fromY} L ${midX} ${toY} L ${toX} ${toY}`,
5052
+ midX,
5053
+ midY: (fromY + toY) / 2
5054
+ };
5055
+ }
5056
+ if (!isHFrom && !isHTo) {
5057
+ const midY = (fromY + toY) / 2;
5058
+ return {
5059
+ d: `M ${fromX} ${fromY} L ${fromX} ${midY} L ${toX} ${midY} L ${toX} ${toY}`,
5060
+ midX: (fromX + toX) / 2,
5061
+ midY
5062
+ };
5063
+ }
5064
+ if (isHFrom) {
5065
+ return {
5066
+ d: `M ${fromX} ${fromY} L ${toX} ${fromY} L ${toX} ${toY}`,
5067
+ midX: toX,
5068
+ midY: (fromY + toY) / 2
5069
+ };
5070
+ }
5071
+ return {
5072
+ d: `M ${fromX} ${fromY} L ${fromX} ${toY} L ${toX} ${toY}`,
5073
+ midX: (fromX + toX) / 2,
5074
+ midY: toY
5075
+ };
5076
+ }
5077
+ function layoutPid(ast) {
5078
+ const equipment = [];
5079
+ const equipById = /* @__PURE__ */ new Map();
5080
+ const heights = ast.equipment.map((e) => GEOMETRY[e.equipType]?.height ?? 60);
5081
+ const maxH = Math.max(...heights, 0);
5082
+ const rowY = PADDING + TITLE_AREA + maxH / 2 + 30;
5083
+ let cursorX = PADDING + 40;
5084
+ for (const equip of ast.equipment) {
5085
+ const geo = GEOMETRY[equip.equipType] ?? { width: 60, height: 40, ports: {} };
5086
+ const cx = cursorX + geo.width / 2;
5087
+ const cy = rowY;
5088
+ const x = cx - geo.width / 2;
5089
+ const y = cy - geo.height / 2;
5090
+ const ports = {};
5091
+ for (const [name, p] of Object.entries(geo.ports)) {
5092
+ ports[name] = { x: cx + p.x, y: cy + p.y };
5093
+ }
5094
+ const layoutEq = {
5095
+ equip,
5096
+ x,
5097
+ y,
5098
+ width: geo.width,
5099
+ height: geo.height,
5100
+ cx,
5101
+ cy,
5102
+ ports
5103
+ };
5104
+ equipment.push(layoutEq);
5105
+ equipById.set(equip.id, layoutEq);
5106
+ cursorX += geo.width + EQUIP_GAP_X;
5107
+ }
5108
+ const instruments = [];
5109
+ const instById = /* @__PURE__ */ new Map();
5110
+ const crBandY = PADDING + TITLE_AREA + 40;
5111
+ let crSlot = 0;
5112
+ for (const inst of ast.instruments) {
5113
+ let cx = 0;
5114
+ let cy = 0;
5115
+ if (inst.category.startsWith("cr_")) {
5116
+ const tgt = inst.controls ?? inst.measures ?? "";
5117
+ const tgtEq = equipById.get(tgt);
5118
+ cx = tgtEq ? tgtEq.cx : PADDING + 80 + crSlot * (INST_RADIUS * 2 + 28);
5119
+ cy = crBandY;
5120
+ crSlot += 1;
5121
+ } else if (inst.category.startsWith("local_")) {
5122
+ cy = rowY + maxH / 2 + INST_OFFSET + INST_RADIUS;
5123
+ const tgt = inst.measures ?? inst.controls ?? "";
5124
+ const tgtEq = equipById.get(tgt);
5125
+ cx = tgtEq ? tgtEq.cx : PADDING + 80;
5126
+ } else {
5127
+ cy = rowY + maxH / 2 + INST_OFFSET;
5128
+ const tgt = inst.measures ?? inst.controls ?? "";
5129
+ const tgtEq = equipById.get(tgt);
5130
+ cx = tgtEq ? tgtEq.cx : PADDING + 80;
5131
+ }
5132
+ const lay = {
5133
+ inst,
5134
+ cx,
5135
+ cy,
5136
+ r: INST_RADIUS
5137
+ };
5138
+ instruments.push(lay);
5139
+ instById.set(inst.tag, lay);
5140
+ }
5141
+ const sameRow = (a, b) => Math.abs(a.cy - b.cy) < INST_RADIUS && Math.abs(a.cx - b.cx) < INST_RADIUS * 2 + 8;
5142
+ const sortedByX = [...instruments].sort((a, b) => a.cx - b.cx);
5143
+ for (let i = 1; i < sortedByX.length; i++) {
5144
+ const prev = sortedByX[i - 1];
5145
+ const cur = sortedByX[i];
5146
+ if (sameRow(prev, cur)) {
5147
+ cur.cx = prev.cx + INST_RADIUS * 2 + 14;
5148
+ }
5149
+ }
5150
+ const lines = [];
5151
+ for (const ln of ast.lines) {
5152
+ const path2 = routeLine(ln, equipById, instById);
5153
+ if (path2) lines.push(path2);
5154
+ }
5155
+ const allX = [];
5156
+ const allY = [];
5157
+ for (const e of equipment) {
5158
+ const tagPad = Math.max(0, ((e.equip.tag ?? e.equip.id).length * 6.6 - e.width) / 2 + 4);
5159
+ allX.push(e.x - tagPad, e.x + e.width + tagPad);
5160
+ allY.push(e.y, e.y + e.height + 30);
5161
+ }
5162
+ for (const i of instruments) {
5163
+ allX.push(i.cx - i.r, i.cx + i.r);
5164
+ allY.push(i.cy - i.r, i.cy + i.r + 14);
5165
+ }
5166
+ if (allX.length === 0) {
5167
+ allX.push(0, 400);
5168
+ allY.push(0, 200);
5169
+ }
5170
+ const maxX = Math.max(...allX);
5171
+ const maxY = Math.max(...allY);
5172
+ return {
5173
+ width: Math.max(maxX + PADDING, 400),
5174
+ height: Math.max(maxY + PADDING, 200),
5175
+ equipment,
5176
+ instruments,
5177
+ lines,
5178
+ title: ast.title
5179
+ };
5180
+ }
5181
+ function routeLine(ln, equipById, instById) {
5182
+ const fromAnchor = resolveAnchor(ln.from.id, ln.from.port, "out", equipById, instById);
5183
+ const toAnchor = resolveAnchor(ln.to.id, ln.to.port, "in", equipById, instById);
5184
+ if (!fromAnchor || !toAnchor) return void 0;
5185
+ const { d, midX, midY } = manhattanPath(
5186
+ fromAnchor.x,
5187
+ fromAnchor.y,
5188
+ fromAnchor.side,
5189
+ toAnchor.x,
5190
+ toAnchor.y,
5191
+ toAnchor.side
5192
+ );
5193
+ return {
5194
+ line: ln,
5195
+ path: d,
5196
+ midX,
5197
+ midY
5198
+ };
5199
+ }
5200
+ function resolveAnchor(id, port, fallback, equipById, instById) {
5201
+ const eq = equipById.get(id);
5202
+ if (eq) return getAnchor(eq, port, fallback);
5203
+ const inst = instById.get(id);
5204
+ if (inst) {
5205
+ return {
5206
+ x: inst.cx,
5207
+ y: inst.cy,
5208
+ side: fallback === "out" ? "right" : "left"
5209
+ };
5210
+ }
5211
+ return void 0;
5212
+ }
5213
+
5214
+ // src/diagrams/pid/renderer.ts
5215
+ var STYLE2 = `
5216
+ .lt-pid-equip { fill: #ffffff; stroke: #1d1d1d; stroke-width: 1.6; }
5217
+ .lt-pid-equip-tag { font: 600 11px system-ui, sans-serif; fill: #1d1d1d; }
5218
+ .lt-pid-tray-line { stroke: #555; stroke-width: 1; fill: none; }
5219
+
5220
+ .lt-pid-process { stroke: #1d1d1d; stroke-width: 2.6; fill: none; }
5221
+ .lt-pid-process-min { stroke: #1d1d1d; stroke-width: 1.5; fill: none; }
5222
+ .lt-pid-pneumatic { stroke: #1d1d1d; stroke-width: 1.4; fill: none; }
5223
+ .lt-pid-electric { stroke: #1d1d1d; stroke-width: 1.4; fill: none; stroke-dasharray: 6 4; }
5224
+ .lt-pid-hydraulic { stroke: #1d1d1d; stroke-width: 1.4; fill: none; stroke-dasharray: 10 4; }
5225
+ .lt-pid-capillary { stroke: #1d1d1d; stroke-width: 1.4; fill: none; stroke-dasharray: 1 5; stroke-linecap: round; }
5226
+ .lt-pid-software { stroke: #6a6a6a; stroke-width: 1.3; fill: none; stroke-dasharray: 2 4; }
5227
+ .lt-pid-mechanical { stroke: #1d1d1d; stroke-width: 1.4; fill: none; stroke-dasharray: 3 2 1 2; }
5228
+
5229
+ .lt-pid-pneumatic-tick { stroke: #1d1d1d; stroke-width: 1.2; }
5230
+
5231
+ .lt-inst-body { fill: #ffffff; stroke: #1d1d1d; stroke-width: 1.4; }
5232
+ .lt-inst-tag { font: 600 9.5px system-ui, sans-serif; fill: #1d1d1d; }
5233
+ .lt-inst-cr-line { stroke: #1d1d1d; stroke-width: 1; }
5234
+ .lt-inst-local-line { stroke: #1d1d1d; stroke-width: 1; stroke-dasharray: 2 2; }
5235
+ .lt-pid-valve-body { fill: #ffffff; stroke: #1d1d1d; stroke-width: 1.4; }
5236
+
5237
+ .lt-pid-line-tag-bg { fill: #ffffff; stroke: #1d1d1d; stroke-width: 0.6; }
5238
+ .lt-pid-line-tag-text { font: 9px ui-monospace, monospace; fill: #1d1d1d; }
5239
+
5240
+ .lt-pid-title { font: 600 14px system-ui, sans-serif; fill: #1d1d1d; }
5241
+ `;
5242
+ var ARROW_ID = "lt-pid-arrow";
5243
+ function lineClass(t) {
5244
+ return `lt-pid-${t.replace("_", "-")}`;
5245
+ }
5246
+ function renderLine(l) {
5247
+ const cls = lineClass(l.line.lineType);
5248
+ const parts = [
5249
+ path({
5250
+ d: l.path,
5251
+ class: cls,
5252
+ "data-line-id": l.line.id,
5253
+ "data-service": l.line.service ?? ""
5254
+ })
5255
+ ];
5256
+ if (l.line.lineType === "pneumatic") {
5257
+ parts.push(...pneumaticTicks(l.path));
5258
+ }
5259
+ if (l.line.tag) {
5260
+ const w = Math.max(28, l.line.tag.length * 6);
5261
+ parts.push(
5262
+ rect({
5263
+ x: l.midX - w / 2,
5264
+ y: l.midY - 8,
5265
+ width: w,
5266
+ height: 14,
5267
+ rx: 2,
5268
+ ry: 2,
5269
+ class: "lt-pid-line-tag-bg"
5270
+ })
5271
+ );
5272
+ parts.push(
5273
+ text(
5274
+ {
5275
+ x: l.midX,
5276
+ y: l.midY + 3,
5277
+ "text-anchor": "middle",
5278
+ class: "lt-pid-line-tag-text"
5279
+ },
5280
+ l.line.tag
5281
+ )
5282
+ );
5283
+ }
5284
+ return group({ class: "lt-pid-line", "data-id": l.line.id }, parts);
5285
+ }
5286
+ function pneumaticTicks(d) {
5287
+ const tokens = d.match(/([MLC])\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)/g) ?? [];
5288
+ const points = [];
5289
+ for (const t of tokens) {
5290
+ const m = t.match(/[MLC]\s+(-?\d+(?:\.\d+)?)\s+(-?\d+(?:\.\d+)?)/);
5291
+ if (!m) continue;
5292
+ points.push({ x: parseFloat(m[1]), y: parseFloat(m[2]) });
5293
+ }
5294
+ const ticks = [];
5295
+ for (let i = 1; i < points.length; i++) {
5296
+ const a = points[i - 1];
5297
+ const b = points[i];
5298
+ const dx = b.x - a.x;
5299
+ const dy = b.y - a.y;
5300
+ const len = Math.hypot(dx, dy);
5301
+ if (len < 12) continue;
5302
+ const ux = dx / len;
5303
+ const uy = dy / len;
5304
+ const px = -uy;
5305
+ const py = ux;
5306
+ for (let s = 18; s < len; s += 26) {
5307
+ const cx = a.x + ux * s;
5308
+ const cy = a.y + uy * s;
5309
+ ticks.push(
5310
+ line({
5311
+ x1: cx + px * 4 - ux * 4,
5312
+ y1: cy + py * 4 - uy * 4,
5313
+ x2: cx - px * 4 + ux * 4,
5314
+ y2: cy - py * 4 + uy * 4,
5315
+ class: "lt-pid-pneumatic-tick"
5316
+ })
5317
+ );
5318
+ }
5319
+ }
5320
+ return ticks;
5321
+ }
5322
+ function renderPidAST(ast, _config) {
5323
+ const layout = layoutPid(ast);
5324
+ return renderLayout2(layout);
5325
+ }
5326
+ function renderLayout2(layout) {
5327
+ const equipNodes = layout.equipment.map((eq) => {
5328
+ const symbol = renderEquip(eq.equip.equipType, eq.equip.tag ?? eq.equip.id);
5329
+ return group(
5330
+ {
5331
+ class: "lt-pid-equip-wrap",
5332
+ "data-id": eq.equip.id,
5333
+ "data-type": eq.equip.equipType,
5334
+ transform: `translate(${eq.cx} ${eq.cy})`
5335
+ },
5336
+ [symbol]
5337
+ );
5338
+ });
5339
+ const instNodes = layout.instruments.map((i) => {
5340
+ const { letter, number } = parseTag(i.inst.tag);
5341
+ return group(
5342
+ {
5343
+ class: "lt-inst",
5344
+ "data-tag": i.inst.tag,
5345
+ "data-category": i.inst.category,
5346
+ transform: `translate(${i.cx} ${i.cy})`
5347
+ },
5348
+ [renderInstrument(i.inst.category, letter, number)]
5349
+ );
5350
+ });
5351
+ const autoSignals = [];
5352
+ for (const i of layout.instruments) {
5353
+ if (i.inst.measures) {
5354
+ const eq = layout.equipment.find((e) => e.equip.id === i.inst.measures);
5355
+ if (eq) {
5356
+ const ax = i.cx;
5357
+ const ay = i.cy;
5358
+ const bx = eq.cx;
5359
+ const by = eq.cy + eq.height / 2;
5360
+ autoSignals.push(
5361
+ path({
5362
+ d: `M ${ax} ${ay} L ${ax} ${by + 8} L ${bx} ${by + 8} L ${bx} ${by}`,
5363
+ class: "lt-pid-electric"
5364
+ })
5365
+ );
5366
+ }
5367
+ }
5368
+ if (i.inst.controls) {
5369
+ const eq = layout.equipment.find((e) => e.equip.id === i.inst.controls);
5370
+ if (eq) {
5371
+ const ax = i.cx;
5372
+ const ay = i.cy + i.r;
5373
+ const bx = eq.cx;
5374
+ const by = eq.y;
5375
+ autoSignals.push(
5376
+ path({
5377
+ d: `M ${ax} ${ay} L ${bx} ${ay} L ${bx} ${by}`,
5378
+ class: "lt-pid-pneumatic"
5379
+ })
5380
+ );
5381
+ }
5382
+ }
5383
+ }
5384
+ const titleNode = layout.title ? text({ x: 16, y: 22, class: "lt-pid-title" }, layout.title) : "";
5385
+ return svgRoot(
5386
+ {
5387
+ width: layout.width,
5388
+ height: layout.height,
5389
+ viewBox: `0 0 ${layout.width} ${layout.height}`,
5390
+ class: "lt-pid",
5391
+ "data-diagram-type": "pid"
5392
+ },
5393
+ [
5394
+ el("title", {}, escapeXml(`P&ID${layout.title ? " \u2014 " + layout.title : ""}`)),
5395
+ el("desc", {}, "ISA-5.1 / ISO 10628 P&ID rendered by Schematex"),
5396
+ defs([
5397
+ el(
5398
+ "marker",
5399
+ {
5400
+ id: ARROW_ID,
5401
+ markerWidth: 10,
5402
+ markerHeight: 10,
5403
+ refX: 9,
5404
+ refY: 3,
5405
+ orient: "auto",
5406
+ markerUnits: "strokeWidth"
5407
+ },
5408
+ [polygon({ points: "0,0 10,3 0,6", fill: "#1d1d1d" })]
5409
+ ),
5410
+ el("style", {}, STYLE2)
5411
+ ]),
5412
+ titleNode,
5413
+ group({ class: "lt-pid-equipment" }, equipNodes),
5414
+ group({ class: "lt-pid-lines" }, layout.lines.map(renderLine)),
5415
+ group({ class: "lt-pid-signals" }, autoSignals),
5416
+ group({ class: "lt-pid-instruments" }, instNodes)
5417
+ ]
5418
+ );
5419
+ }
5420
+ function parseTag(tag) {
5421
+ const idx = tag.indexOf("-");
5422
+ if (idx < 0) return { letter: tag, number: "" };
5423
+ return { letter: tag.slice(0, idx), number: tag.slice(idx + 1) };
5424
+ }
5425
+
5426
+ // src/diagrams/pid/index.ts
5427
+ var pid = {
5428
+ type: "pid",
5429
+ detect(text2) {
5430
+ return /^\s*pid\b/i.test(text2);
5431
+ },
5432
+ parse: parsePid,
5433
+ render(text2, config) {
5434
+ const ast = parsePid(text2);
5435
+ return renderPidAST(ast);
5436
+ }
5437
+ };
5438
+
2629
5439
  // src/diagrams/mindmap/inline.ts
2630
5440
  var RE_TASK = /^\[( |x|X)\]\s+(.*)$/;
2631
5441
  function tokenizeInline(raw) {
@@ -2901,7 +5711,7 @@ function parseMindmap(text2) {
2901
5711
  }
2902
5712
 
2903
5713
  // src/diagrams/mindmap/layout.ts
2904
- var PADDING = 40;
5714
+ var PADDING2 = 40;
2905
5715
  var SIBLING_GAP = 18;
2906
5716
  var MAIN_GAP = 44;
2907
5717
  var UNDERLINE_GAP = 4;
@@ -3048,13 +5858,13 @@ function normalize(nodes) {
3048
5858
  minY = Math.min(minY, n.y - lh / 2);
3049
5859
  maxY = Math.max(maxY, n.y + lh / 2);
3050
5860
  }
3051
- const dx = PADDING - minX;
3052
- const dy = PADDING - minY;
5861
+ const dx = PADDING2 - minX;
5862
+ const dy = PADDING2 - minY;
3053
5863
  for (const n of nodes) {
3054
5864
  n.x += dx;
3055
5865
  n.y += dy;
3056
5866
  }
3057
- return { width: maxX - minX + PADDING * 2, height: maxY - minY + PADDING * 2 };
5867
+ return { width: maxX - minX + PADDING2 * 2, height: maxY - minY + PADDING2 * 2 };
3058
5868
  }
3059
5869
  function underlineY(n) {
3060
5870
  return n.y + n.labelHeight / 2 - UNDERLINE_GAP / 2;
@@ -3182,7 +5992,7 @@ function paletteColor(theme, branchIndex) {
3182
5992
  if (branchIndex < 0) return theme.centralFill;
3183
5993
  return theme.branchPalette[branchIndex % theme.branchPalette.length];
3184
5994
  }
3185
- function renderLine(line2, leftX, cy, fontSize, fontFamily, fontWeight, theme) {
5995
+ function renderLine2(line2, leftX, cy, fontSize, fontFamily, fontWeight, theme) {
3186
5996
  const baselineY = cy + fontSize * 0.35;
3187
5997
  const tspans = [];
3188
5998
  const decorations = [];
@@ -3297,7 +6107,7 @@ function tspan(attrs, content) {
3297
6107
  }
3298
6108
  return `<tspan ${pairs.join(" ")}>${escapeXml(content)}</tspan>`;
3299
6109
  }
3300
- function renderNode(n, color, theme, fontFamily) {
6110
+ function renderNode2(n, color, theme, fontFamily) {
3301
6111
  const isRoot = n.node.depth === 0;
3302
6112
  const isMain = n.node.depth === 1;
3303
6113
  const fs = n.fontSize;
@@ -3312,7 +6122,7 @@ function renderNode(n, color, theme, fontFamily) {
3312
6122
  for (let i = 0; i < lineCount; i++) {
3313
6123
  const line2 = n.lines[i];
3314
6124
  const cy = topY + (i + 0.5) * lh;
3315
- const r = renderLine(line2, textLeft, cy, fs, fontFamily, weight, theme);
6125
+ const r = renderLine2(line2, textLeft, cy, fs, fontFamily, weight, theme);
3316
6126
  decorations.push(...r.decorations);
3317
6127
  children.push(r.textElement);
3318
6128
  }
@@ -3363,7 +6173,7 @@ function renderMindmapAST(ast, themeName = "default", fontFamily = "system-ui, -
3363
6173
  const nodeSvgs = [];
3364
6174
  for (const n of layout.nodes) {
3365
6175
  const color = n.node.depth === 0 ? theme.centralFill : paletteColor(theme, n.branchIndex);
3366
- nodeSvgs.push(renderNode(n, color, theme, fontFamily));
6176
+ nodeSvgs.push(renderNode2(n, color, theme, fontFamily));
3367
6177
  }
3368
6178
  const title2 = ast.title ?? tokensToPlainText(ast.root.tokens);
3369
6179
  return svgRoot(
@@ -3770,7 +6580,7 @@ function parseMatrix(text2) {
3770
6580
  const lines = text2.split(/\r?\n/);
3771
6581
  let templateName;
3772
6582
  let inConfig = false;
3773
- for (let raw of lines) {
6583
+ for (const raw of lines) {
3774
6584
  let line2 = raw;
3775
6585
  const hashIdx = findCommentStart(line2);
3776
6586
  if (hashIdx >= 0) line2 = line2.slice(0, hashIdx);
@@ -4823,7 +7633,9 @@ var plugins = [
4823
7633
  matrix,
4824
7634
  orgchart,
4825
7635
  decisiontree,
4826
- timeline
7636
+ timeline,
7637
+ state,
7638
+ pid
4827
7639
  ];
4828
7640
  function detectPlugin(text2, config) {
4829
7641
  if (config?.type) {
@@ -4855,6 +7667,6 @@ function render(text2, config) {
4855
7667
  return plugin.render(text2, renderConfig);
4856
7668
  }
4857
7669
 
4858
- export { decisiontree, parse, render, timeline };
4859
- //# sourceMappingURL=chunk-VPKCW4PB.js.map
4860
- //# sourceMappingURL=chunk-VPKCW4PB.js.map
7670
+ export { decisiontree, parse, pid, render, state, timeline };
7671
+ //# sourceMappingURL=chunk-RNGYXGHS.js.map
7672
+ //# sourceMappingURL=chunk-RNGYXGHS.js.map