schematex 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +89 -7
  2. package/dist/ai/ai-sdk.cjs +32 -26
  3. package/dist/ai/ai-sdk.cjs.map +1 -1
  4. package/dist/ai/ai-sdk.d.cts +4 -3
  5. package/dist/ai/ai-sdk.d.ts +4 -3
  6. package/dist/ai/ai-sdk.js +28 -22
  7. package/dist/ai/ai-sdk.js.map +1 -1
  8. package/dist/ai/index.cjs +37 -25
  9. package/dist/ai/index.d.cts +20 -153
  10. package/dist/ai/index.d.ts +20 -153
  11. package/dist/ai/index.js +17 -17
  12. package/dist/{api-C5UcmT7n.d.cts → api-XWHHAhQI.d.ts} +12 -2
  13. package/dist/{api-C5UcmT7n.d.ts → api-qVDutqXH.d.cts} +12 -2
  14. package/dist/browser.cjs +37 -19
  15. package/dist/browser.cjs.map +1 -1
  16. package/dist/browser.d.cts +12 -3
  17. package/dist/browser.d.ts +12 -3
  18. package/dist/browser.js +27 -19
  19. package/dist/browser.js.map +1 -1
  20. package/dist/{chunk-3YUUC6RN.cjs → chunk-25ZON47K.cjs} +4105 -267
  21. package/dist/chunk-25ZON47K.cjs.map +1 -0
  22. package/dist/{chunk-N7W5KZK7.cjs → chunk-2L4YXZAZ.cjs} +4 -4
  23. package/dist/{chunk-N7W5KZK7.cjs.map → chunk-2L4YXZAZ.cjs.map} +1 -1
  24. package/dist/{chunk-ZYRCPSBU.js → chunk-3IE7KZY4.js} +4 -4
  25. package/dist/{chunk-ZYRCPSBU.js.map → chunk-3IE7KZY4.js.map} +1 -1
  26. package/dist/{chunk-ZX74KJPM.js → chunk-3VB5AT4R.js} +3 -3
  27. package/dist/{chunk-ZX74KJPM.js.map → chunk-3VB5AT4R.js.map} +1 -1
  28. package/dist/{chunk-S2KJRHDZ.cjs → chunk-4UFR2LB6.cjs} +13 -13
  29. package/dist/{chunk-S2KJRHDZ.cjs.map → chunk-4UFR2LB6.cjs.map} +1 -1
  30. package/dist/{chunk-QTNPMIO2.cjs → chunk-6JI6FWLZ.cjs} +341 -35
  31. package/dist/chunk-6JI6FWLZ.cjs.map +1 -0
  32. package/dist/{chunk-GTDQAN2Z.js → chunk-7AFW2J6J.js} +4075 -247
  33. package/dist/chunk-7AFW2J6J.js.map +1 -0
  34. package/dist/{chunk-UFTYX73U.js → chunk-7F76AWOI.js} +339 -35
  35. package/dist/chunk-7F76AWOI.js.map +1 -0
  36. package/dist/{chunk-BW4KGTV7.cjs → chunk-C5C5EF3W.cjs} +4 -4
  37. package/dist/{chunk-BW4KGTV7.cjs.map → chunk-C5C5EF3W.cjs.map} +1 -1
  38. package/dist/{chunk-VJGMEGMR.js → chunk-DOK7LKLO.js} +125 -13
  39. package/dist/chunk-DOK7LKLO.js.map +1 -0
  40. package/dist/{chunk-3M6WB62Y.cjs → chunk-ECD5XHBM.cjs} +139 -7
  41. package/dist/chunk-ECD5XHBM.cjs.map +1 -0
  42. package/dist/{chunk-L3CTXXVZ.js → chunk-FBS3PACU.js} +3 -3
  43. package/dist/{chunk-L3CTXXVZ.js.map → chunk-FBS3PACU.js.map} +1 -1
  44. package/dist/{chunk-QUKVGHN4.cjs → chunk-FKJBXGWP.cjs} +4 -4
  45. package/dist/{chunk-QUKVGHN4.cjs.map → chunk-FKJBXGWP.cjs.map} +1 -1
  46. package/dist/{chunk-SZK376QB.js → chunk-H4MT5TJP.js} +3 -3
  47. package/dist/{chunk-SZK376QB.js.map → chunk-H4MT5TJP.js.map} +1 -1
  48. package/dist/{chunk-3M6T7KB4.js → chunk-HAZALB7U.js} +3 -3
  49. package/dist/{chunk-3M6T7KB4.js.map → chunk-HAZALB7U.js.map} +1 -1
  50. package/dist/{chunk-VFZOPRQP.cjs → chunk-HWVBHU3O.cjs} +5 -5
  51. package/dist/{chunk-VFZOPRQP.cjs.map → chunk-HWVBHU3O.cjs.map} +1 -1
  52. package/dist/{chunk-D7EHZFK4.cjs → chunk-L7POWM5B.cjs} +138 -2
  53. package/dist/chunk-L7POWM5B.cjs.map +1 -0
  54. package/dist/{chunk-6OSUNBZY.js → chunk-LGABFD3L.js} +135 -7
  55. package/dist/chunk-LGABFD3L.js.map +1 -0
  56. package/dist/{chunk-2VNMKOUO.js → chunk-LRI4RH2N.js} +135 -3
  57. package/dist/chunk-LRI4RH2N.js.map +1 -0
  58. package/dist/{chunk-VDSYMSUY.js → chunk-MVIEIKOI.js} +3 -3
  59. package/dist/{chunk-VDSYMSUY.js.map → chunk-MVIEIKOI.js.map} +1 -1
  60. package/dist/{chunk-ZL5RB4UV.js → chunk-N5B242WY.js} +3 -3
  61. package/dist/{chunk-ZL5RB4UV.js.map → chunk-N5B242WY.js.map} +1 -1
  62. package/dist/{chunk-IM4RCUHA.js → chunk-P63S7P6N.js} +1309 -69
  63. package/dist/chunk-P63S7P6N.js.map +1 -0
  64. package/dist/{chunk-YLEVMOK2.cjs → chunk-R66QG3XT.cjs} +5 -4
  65. package/dist/{chunk-YLEVMOK2.cjs.map → chunk-R66QG3XT.cjs.map} +1 -1
  66. package/dist/{chunk-I55HO32M.js → chunk-RJMCWT7Z.js} +3 -3
  67. package/dist/{chunk-I55HO32M.js.map → chunk-RJMCWT7Z.js.map} +1 -1
  68. package/dist/{chunk-TZTCIAYW.cjs → chunk-S3RMAXH5.cjs} +137 -25
  69. package/dist/chunk-S3RMAXH5.cjs.map +1 -0
  70. package/dist/{chunk-IBRW3UOA.js → chunk-TWLKXV2O.js} +3 -3
  71. package/dist/{chunk-IBRW3UOA.js.map → chunk-TWLKXV2O.js.map} +1 -1
  72. package/dist/{chunk-HUPDIRBX.js → chunk-UWA5MWCI.js} +949 -918
  73. package/dist/chunk-UWA5MWCI.js.map +1 -0
  74. package/dist/{chunk-RYVV5UVI.cjs → chunk-V4GILQR6.cjs} +4 -4
  75. package/dist/{chunk-RYVV5UVI.cjs.map → chunk-V4GILQR6.cjs.map} +1 -1
  76. package/dist/{chunk-XRCY75UV.cjs → chunk-V4RO5KYY.cjs} +951 -918
  77. package/dist/chunk-V4RO5KYY.cjs.map +1 -0
  78. package/dist/{chunk-VZ5LDNHK.cjs → chunk-VTSH4YPT.cjs} +12 -12
  79. package/dist/{chunk-VZ5LDNHK.cjs.map → chunk-VTSH4YPT.cjs.map} +1 -1
  80. package/dist/{chunk-UUBNQV2T.cjs → chunk-WQDIZH2Z.cjs} +12 -12
  81. package/dist/{chunk-UUBNQV2T.cjs.map → chunk-WQDIZH2Z.cjs.map} +1 -1
  82. package/dist/{chunk-EGSUMHCS.cjs → chunk-YB4XJY5L.cjs} +12 -12
  83. package/dist/{chunk-EGSUMHCS.cjs.map → chunk-YB4XJY5L.cjs.map} +1 -1
  84. package/dist/{chunk-JHDR56XO.js → chunk-YS6CGUNH.js} +3 -3
  85. package/dist/{chunk-JHDR56XO.js.map → chunk-YS6CGUNH.js.map} +1 -1
  86. package/dist/{chunk-NWPCY65Z.cjs → chunk-YVDUEUFV.cjs} +1311 -68
  87. package/dist/chunk-YVDUEUFV.cjs.map +1 -0
  88. package/dist/{types-BOAsqHoU.d.ts → diagnostics-DRxhodP6.d.cts} +74 -2
  89. package/dist/{types-BOAsqHoU.d.cts → diagnostics-DRxhodP6.d.ts} +74 -2
  90. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  91. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  92. package/dist/diagrams/circuit/index.cjs +8 -8
  93. package/dist/diagrams/circuit/index.d.cts +1 -1
  94. package/dist/diagrams/circuit/index.d.ts +1 -1
  95. package/dist/diagrams/circuit/index.js +2 -2
  96. package/dist/diagrams/ecomap/index.cjs +7 -7
  97. package/dist/diagrams/ecomap/index.d.cts +1 -1
  98. package/dist/diagrams/ecomap/index.d.ts +1 -1
  99. package/dist/diagrams/ecomap/index.js +2 -2
  100. package/dist/diagrams/entity/index.cjs +6 -6
  101. package/dist/diagrams/entity/index.d.cts +1 -1
  102. package/dist/diagrams/entity/index.d.ts +1 -1
  103. package/dist/diagrams/entity/index.js +2 -2
  104. package/dist/diagrams/fishbone/index.cjs +8 -8
  105. package/dist/diagrams/fishbone/index.d.cts +1 -1
  106. package/dist/diagrams/fishbone/index.d.ts +1 -1
  107. package/dist/diagrams/fishbone/index.js +2 -2
  108. package/dist/diagrams/flowchart/index.cjs +8 -8
  109. package/dist/diagrams/flowchart/index.d.cts +2 -2
  110. package/dist/diagrams/flowchart/index.d.ts +2 -2
  111. package/dist/diagrams/flowchart/index.js +2 -2
  112. package/dist/diagrams/genogram/index.cjs +9 -9
  113. package/dist/diagrams/genogram/index.d.cts +1 -1
  114. package/dist/diagrams/genogram/index.d.ts +1 -1
  115. package/dist/diagrams/genogram/index.js +2 -2
  116. package/dist/diagrams/ladder/index.cjs +6 -6
  117. package/dist/diagrams/ladder/index.d.cts +1 -1
  118. package/dist/diagrams/ladder/index.d.ts +1 -1
  119. package/dist/diagrams/ladder/index.js +2 -2
  120. package/dist/diagrams/logic/index.cjs +6 -6
  121. package/dist/diagrams/logic/index.d.cts +1 -1
  122. package/dist/diagrams/logic/index.d.ts +1 -1
  123. package/dist/diagrams/logic/index.js +2 -2
  124. package/dist/diagrams/orgchart/index.cjs +7 -7
  125. package/dist/diagrams/orgchart/index.d.cts +1 -1
  126. package/dist/diagrams/orgchart/index.d.ts +1 -1
  127. package/dist/diagrams/orgchart/index.js +2 -2
  128. package/dist/diagrams/pedigree/index.cjs +7 -7
  129. package/dist/diagrams/pedigree/index.d.cts +1 -1
  130. package/dist/diagrams/pedigree/index.d.ts +1 -1
  131. package/dist/diagrams/pedigree/index.js +2 -2
  132. package/dist/diagrams/phylo/index.cjs +7 -7
  133. package/dist/diagrams/phylo/index.d.cts +1 -1
  134. package/dist/diagrams/phylo/index.d.ts +1 -1
  135. package/dist/diagrams/phylo/index.js +2 -2
  136. package/dist/diagrams/sld/index.cjs +14 -6
  137. package/dist/diagrams/sld/index.d.cts +14 -2
  138. package/dist/diagrams/sld/index.d.ts +14 -2
  139. package/dist/diagrams/sld/index.js +2 -2
  140. package/dist/diagrams/sociogram/index.cjs +6 -6
  141. package/dist/diagrams/sociogram/index.d.cts +1 -1
  142. package/dist/diagrams/sociogram/index.d.ts +1 -1
  143. package/dist/diagrams/sociogram/index.js +2 -2
  144. package/dist/diagrams/timing/index.d.cts +1 -1
  145. package/dist/diagrams/timing/index.d.ts +1 -1
  146. package/dist/diagrams/venn/index.cjs +9 -9
  147. package/dist/diagrams/venn/index.d.cts +1 -1
  148. package/dist/diagrams/venn/index.d.ts +1 -1
  149. package/dist/diagrams/venn/index.js +2 -2
  150. package/dist/{index-CJai_TEZ.d.ts → index-BRIkOPnd.d.cts} +60 -2
  151. package/dist/{index-C9A0h-CB.d.cts → index-C7SN-FB3.d.ts} +60 -2
  152. package/dist/index.cjs +351 -51
  153. package/dist/index.cjs.map +1 -1
  154. package/dist/index.d.cts +38 -4
  155. package/dist/index.d.ts +38 -4
  156. package/dist/index.js +283 -16
  157. package/dist/index.js.map +1 -1
  158. package/dist/react.cjs +21 -23
  159. package/dist/react.cjs.map +1 -1
  160. package/dist/react.d.cts +3 -2
  161. package/dist/react.d.ts +3 -2
  162. package/dist/react.js +21 -23
  163. package/dist/react.js.map +1 -1
  164. package/dist/tools-BVeUNdsU.d.ts +161 -0
  165. package/dist/tools-DdhP1kWY.d.cts +161 -0
  166. package/package.json +2 -2
  167. package/dist/chunk-2VNMKOUO.js.map +0 -1
  168. package/dist/chunk-3M6WB62Y.cjs.map +0 -1
  169. package/dist/chunk-3YUUC6RN.cjs.map +0 -1
  170. package/dist/chunk-6OSUNBZY.js.map +0 -1
  171. package/dist/chunk-D7EHZFK4.cjs.map +0 -1
  172. package/dist/chunk-GTDQAN2Z.js.map +0 -1
  173. package/dist/chunk-HUPDIRBX.js.map +0 -1
  174. package/dist/chunk-IM4RCUHA.js.map +0 -1
  175. package/dist/chunk-NWPCY65Z.cjs.map +0 -1
  176. package/dist/chunk-QTNPMIO2.cjs.map +0 -1
  177. package/dist/chunk-TZTCIAYW.cjs.map +0 -1
  178. package/dist/chunk-UFTYX73U.js.map +0 -1
  179. package/dist/chunk-VJGMEGMR.js.map +0 -1
  180. package/dist/chunk-XRCY75UV.cjs.map +0 -1
@@ -1,23 +1,23 @@
1
1
  'use strict';
2
2
 
3
- var chunkQUKVGHN4_cjs = require('./chunk-QUKVGHN4.cjs');
4
- var chunkXRCY75UV_cjs = require('./chunk-XRCY75UV.cjs');
3
+ var chunkFKJBXGWP_cjs = require('./chunk-FKJBXGWP.cjs');
4
+ var chunkV4RO5KYY_cjs = require('./chunk-V4RO5KYY.cjs');
5
5
  var chunkSUIDD2C5_cjs = require('./chunk-SUIDD2C5.cjs');
6
- var chunkN7W5KZK7_cjs = require('./chunk-N7W5KZK7.cjs');
7
- var chunk3M6WB62Y_cjs = require('./chunk-3M6WB62Y.cjs');
8
- var chunkRYVV5UVI_cjs = require('./chunk-RYVV5UVI.cjs');
9
- var chunkVFZOPRQP_cjs = require('./chunk-VFZOPRQP.cjs');
10
- var chunkBW4KGTV7_cjs = require('./chunk-BW4KGTV7.cjs');
11
- var chunkQTNPMIO2_cjs = require('./chunk-QTNPMIO2.cjs');
12
- var chunkTZTCIAYW_cjs = require('./chunk-TZTCIAYW.cjs');
13
- var chunkVZ5LDNHK_cjs = require('./chunk-VZ5LDNHK.cjs');
14
- var chunkEGSUMHCS_cjs = require('./chunk-EGSUMHCS.cjs');
6
+ var chunk2L4YXZAZ_cjs = require('./chunk-2L4YXZAZ.cjs');
7
+ var chunkECD5XHBM_cjs = require('./chunk-ECD5XHBM.cjs');
8
+ var chunkV4GILQR6_cjs = require('./chunk-V4GILQR6.cjs');
9
+ var chunkHWVBHU3O_cjs = require('./chunk-HWVBHU3O.cjs');
10
+ var chunkC5C5EF3W_cjs = require('./chunk-C5C5EF3W.cjs');
11
+ var chunk6JI6FWLZ_cjs = require('./chunk-6JI6FWLZ.cjs');
12
+ var chunkS3RMAXH5_cjs = require('./chunk-S3RMAXH5.cjs');
13
+ var chunkVTSH4YPT_cjs = require('./chunk-VTSH4YPT.cjs');
14
+ var chunkYB4XJY5L_cjs = require('./chunk-YB4XJY5L.cjs');
15
15
  var chunk3KRL2EGN_cjs = require('./chunk-3KRL2EGN.cjs');
16
- var chunkS2KJRHDZ_cjs = require('./chunk-S2KJRHDZ.cjs');
17
- var chunkUUBNQV2T_cjs = require('./chunk-UUBNQV2T.cjs');
16
+ var chunk4UFR2LB6_cjs = require('./chunk-4UFR2LB6.cjs');
17
+ var chunkWQDIZH2Z_cjs = require('./chunk-WQDIZH2Z.cjs');
18
18
  var chunkST5YRTTV_cjs = require('./chunk-ST5YRTTV.cjs');
19
- var chunkYLEVMOK2_cjs = require('./chunk-YLEVMOK2.cjs');
20
- var chunkD7EHZFK4_cjs = require('./chunk-D7EHZFK4.cjs');
19
+ var chunkR66QG3XT_cjs = require('./chunk-R66QG3XT.cjs');
20
+ var chunkL7POWM5B_cjs = require('./chunk-L7POWM5B.cjs');
21
21
  var chunkNAGUZFXX_cjs = require('./chunk-NAGUZFXX.cjs');
22
22
  var chunk3WNW5Y7P_cjs = require('./chunk-3WNW5Y7P.cjs');
23
23
 
@@ -1080,7 +1080,7 @@ function wrapText(text2, maxChars) {
1080
1080
  return lines.slice(0, 2);
1081
1081
  }
1082
1082
  function renderDecisionTree(ast, config) {
1083
- const t = chunkD7EHZFK4_cjs.resolveBaseTheme(config?.theme ?? "default");
1083
+ const t = chunkL7POWM5B_cjs.resolveBaseTheme(config?.theme ?? "default");
1084
1084
  const layout = layoutDecisionTree(ast);
1085
1085
  const titleOffset = ast.title ? 36 : 10;
1086
1086
  const width = Math.ceil(layout.width);
@@ -2153,7 +2153,7 @@ var ERA_ROW_GAP = 2;
2153
2153
  function renderTimeline(src, config) {
2154
2154
  const ast = parseTimeline(src);
2155
2155
  const layout = layoutTimeline(ast);
2156
- const theme = chunkD7EHZFK4_cjs.resolveTimelineTheme(config?.theme ?? "default");
2156
+ const theme = chunkL7POWM5B_cjs.resolveTimelineTheme(config?.theme ?? "default");
2157
2157
  const styleBlock = styleForTheme(theme, config?.fontFamily);
2158
2158
  const children = [
2159
2159
  chunk3WNW5Y7P_cjs.title(ast.title ? `Timeline \u2014 ${ast.title}` : "Timeline"),
@@ -2198,7 +2198,7 @@ function renderTimeline(src, config) {
2198
2198
  function styleForTheme(theme, fontFamily) {
2199
2199
  const font = fontFamily ?? "system-ui, -apple-system, sans-serif";
2200
2200
  return `
2201
- .st-timeline { ${chunkD7EHZFK4_cjs.cssCustomProperties(theme)}
2201
+ .st-timeline { ${chunkL7POWM5B_cjs.cssCustomProperties(theme)}
2202
2202
  --st-axis: ${theme.axis};
2203
2203
  --st-axis-label: ${theme.axisLabel};
2204
2204
  --st-era-label: ${theme.eraLabel};
@@ -3334,7 +3334,7 @@ function layoutStateDiagram(ast) {
3334
3334
  }
3335
3335
  return true;
3336
3336
  });
3337
- const fcResult = chunkQTNPMIO2_cjs.layoutFlowchart(fcAst);
3337
+ const fcResult = chunk6JI6FWLZ_cjs.layoutFlowchart(fcAst);
3338
3338
  const stateById = /* @__PURE__ */ new Map();
3339
3339
  const collectStates = (s) => {
3340
3340
  stateById.set(s.id, s);
@@ -3937,7 +3937,7 @@ function renderLayout(layout) {
3937
3937
  var state = {
3938
3938
  type: "state",
3939
3939
  detect(text2) {
3940
- return /^\s*state\b/i.test(text2);
3940
+ return /^\s*(?:state\b|stateDiagram(?:-v2)?\b)/i.test(text2);
3941
3941
  },
3942
3942
  parse: parseStateDiagram,
3943
3943
  render(text2, config) {
@@ -5461,6 +5461,113 @@ function parseTag(tag) {
5461
5461
  return { letter: tag.slice(0, idx), number: tag.slice(idx + 1) };
5462
5462
  }
5463
5463
 
5464
+ // src/diagrams/pid/lint.ts
5465
+ function lintPid(text2) {
5466
+ let ast;
5467
+ try {
5468
+ ast = parsePid(text2);
5469
+ } catch {
5470
+ return [];
5471
+ }
5472
+ return lintPidAst(ast);
5473
+ }
5474
+ var SIGNAL_TYPES = /* @__PURE__ */ new Set([
5475
+ "pneumatic",
5476
+ "electric",
5477
+ "software",
5478
+ "capillary",
5479
+ "hydraulic",
5480
+ "mechanical"
5481
+ ]);
5482
+ function idLetters(tag) {
5483
+ const head = tag.split("-")[0] ?? tag;
5484
+ return head.replace(/[^A-Za-z]/g, "").toUpperCase();
5485
+ }
5486
+ function isController(tag) {
5487
+ return idLetters(tag).slice(1).includes("C");
5488
+ }
5489
+ function isTransmitter(tag) {
5490
+ return idLetters(tag).slice(1).includes("T");
5491
+ }
5492
+ function lintPidAst(ast) {
5493
+ const out = [];
5494
+ const instByTag = /* @__PURE__ */ new Map();
5495
+ for (const inst of ast.instruments) instByTag.set(inst.tag, inst);
5496
+ const controlValveIds = new Set(
5497
+ ast.equipment.filter((e) => e.equipType === "valve_control").map((e) => e.id)
5498
+ );
5499
+ const signalAdj = /* @__PURE__ */ new Map();
5500
+ const addEdge = (a, b) => {
5501
+ if (!signalAdj.has(a)) signalAdj.set(a, /* @__PURE__ */ new Set());
5502
+ signalAdj.get(a).add(b);
5503
+ };
5504
+ for (const ln of ast.lines) {
5505
+ if (!SIGNAL_TYPES.has(ln.lineType)) continue;
5506
+ const a = ln.from.id;
5507
+ const b = ln.to.id;
5508
+ if (instByTag.has(a) && instByTag.has(b)) {
5509
+ addEdge(a, b);
5510
+ addEdge(b, a);
5511
+ }
5512
+ }
5513
+ const canReachController = (start) => {
5514
+ const seen = /* @__PURE__ */ new Set([start]);
5515
+ const queue = [start];
5516
+ while (queue.length) {
5517
+ const cur = queue.shift();
5518
+ if (cur !== start && isController(cur)) return true;
5519
+ for (const nxt of signalAdj.get(cur) ?? []) {
5520
+ if (!seen.has(nxt)) {
5521
+ seen.add(nxt);
5522
+ queue.push(nxt);
5523
+ }
5524
+ }
5525
+ }
5526
+ return false;
5527
+ };
5528
+ for (const inst of ast.instruments) {
5529
+ if (!inst.measures || isController(inst.tag)) continue;
5530
+ if (!canReachController(inst.tag)) {
5531
+ out.push({
5532
+ severity: "warning",
5533
+ code: "PID_LOOP_INCOMPLETE",
5534
+ message: `instrument loop ${inst.tag} has no signal path to a controller`,
5535
+ hint: `${inst.tag} measures '${inst.measures}' but no signal line (electric/pneumatic/\u2026) connects it to a controller instrument (e.g. a tag with a 'C' function letter such as FIC). Add a signal line from ${inst.tag} to the controller.`,
5536
+ fatal: false
5537
+ });
5538
+ }
5539
+ }
5540
+ for (const ln of ast.lines) {
5541
+ if (!SIGNAL_TYPES.has(ln.lineType)) continue;
5542
+ const a = ln.from.id;
5543
+ const b = ln.to.id;
5544
+ const aInst = instByTag.get(a);
5545
+ const bInst = instByTag.get(b);
5546
+ const txToCtrl = aInst && isTransmitter(a) && !isController(a) && bInst && isController(b) || bInst && isTransmitter(b) && !isController(b) && aInst && isController(a);
5547
+ if (txToCtrl && ln.lineType !== "electric") {
5548
+ out.push({
5549
+ severity: "warning",
5550
+ code: "PID_SIGNAL_TYPE_MISMATCH",
5551
+ message: `signal line ${a}\u2192${b} is '${ln.lineType}' but a transmitter\u2192controller link should be 'electric' (ISA-5.1 \xA75.2)`,
5552
+ hint: `Set [type: electric] on this line, or rename the devices if the connection is not a transmitter\u2192controller measurement signal.`,
5553
+ fatal: false
5554
+ });
5555
+ continue;
5556
+ }
5557
+ const ctrlToValve = aInst && isController(a) && controlValveIds.has(b) || bInst && isController(b) && controlValveIds.has(a);
5558
+ if (ctrlToValve && ln.lineType !== "pneumatic") {
5559
+ out.push({
5560
+ severity: "warning",
5561
+ code: "PID_SIGNAL_TYPE_MISMATCH",
5562
+ message: `signal line ${a}\u2192${b} is '${ln.lineType}' but a controller\u2192control-valve link should be 'pneumatic' (ISA-5.1 \xA75.2)`,
5563
+ hint: `Set [type: pneumatic] on this line, or use [type: electric] only if the valve has an electric actuator.`,
5564
+ fatal: false
5565
+ });
5566
+ }
5567
+ }
5568
+ return out;
5569
+ }
5570
+
5464
5571
  // src/diagrams/pid/index.ts
5465
5572
  var pid = {
5466
5573
  type: "pid",
@@ -5468,6 +5575,7 @@ var pid = {
5468
5575
  return /^\s*pid\b/i.test(text2);
5469
5576
  },
5470
5577
  parse: parsePid,
5578
+ lint: lintPid,
5471
5579
  render(text2, config) {
5472
5580
  const ast = parsePid(text2);
5473
5581
  return renderPidAST(ast);
@@ -6512,7 +6620,7 @@ function layoutPrisma(ast) {
6512
6620
  lines: incResized.wrappedLines
6513
6621
  });
6514
6622
  cursorY = incY + incResized.height + PRISMA_CONST.OUTER_PAD_BOTTOM;
6515
- const arrow = (x1, y1, x2, y2) => `M ${x1} ${y1} L ${x2} ${y2}`;
6623
+ const arrow2 = (x1, y1, x2, y2) => `M ${x1} ${y1} L ${x2} ${y2}`;
6516
6624
  const mainColRight = mainColLeft + mainW;
6517
6625
  const rightColArrowEnd = rightColLeft - PRISMA_CONST.ARROWHEAD_LEN;
6518
6626
  if (idRemovedSpec) {
@@ -6521,7 +6629,7 @@ function layoutPrisma(ast) {
6521
6629
  kind: "exclusion",
6522
6630
  from: "id-databases",
6523
6631
  to: "id-removed",
6524
- d: arrow(mainColRight, ay, rightColArrowEnd, ay)
6632
+ d: arrow2(mainColRight, ay, rightColArrowEnd, ay)
6525
6633
  });
6526
6634
  }
6527
6635
  if (dual && idRightSpec) {
@@ -6530,7 +6638,7 @@ function layoutPrisma(ast) {
6530
6638
  kind: "merge-trunk",
6531
6639
  from: "id-databases",
6532
6640
  to: "screening",
6533
- d: arrow(mainColCenterX, idRowY + idDbBox.height, mainColCenterX, screeningY - PRISMA_CONST.ARROWHEAD_LEN)
6641
+ d: arrow2(mainColCenterX, idRowY + idDbBox.height, mainColCenterX, screeningY - PRISMA_CONST.ARROWHEAD_LEN)
6534
6642
  });
6535
6643
  edges.push({
6536
6644
  kind: "merge-leg",
@@ -6543,7 +6651,7 @@ function layoutPrisma(ast) {
6543
6651
  kind: "main",
6544
6652
  from: "id-databases",
6545
6653
  to: "screening",
6546
- d: arrow(mainColCenterX, idRowY + idDbBox.height, mainColCenterX, screeningY - PRISMA_CONST.ARROWHEAD_LEN)
6654
+ d: arrow2(mainColCenterX, idRowY + idDbBox.height, mainColCenterX, screeningY - PRISMA_CONST.ARROWHEAD_LEN)
6547
6655
  });
6548
6656
  }
6549
6657
  if (previousBoxBottom >= 0) {
@@ -6551,34 +6659,34 @@ function layoutPrisma(ast) {
6551
6659
  kind: "previous",
6552
6660
  from: "previous",
6553
6661
  to: "id-databases",
6554
- d: arrow(previousBoxCenterX, previousBoxBottom, mainColCenterX, headerRowY - PRISMA_CONST.ARROWHEAD_LEN)
6662
+ d: arrow2(previousBoxCenterX, previousBoxBottom, mainColCenterX, headerRowY - PRISMA_CONST.ARROWHEAD_LEN)
6555
6663
  });
6556
6664
  }
6557
6665
  edges.push({
6558
6666
  kind: "main",
6559
6667
  from: "screening",
6560
6668
  to: "eligibility",
6561
- d: arrow(mainColCenterX, screeningY + screeningResized.height, mainColCenterX, eligY - PRISMA_CONST.ARROWHEAD_LEN)
6669
+ d: arrow2(mainColCenterX, screeningY + screeningResized.height, mainColCenterX, eligY - PRISMA_CONST.ARROWHEAD_LEN)
6562
6670
  });
6563
6671
  edges.push({
6564
6672
  kind: "main",
6565
6673
  from: "eligibility",
6566
6674
  to: "included",
6567
- d: arrow(mainColCenterX, eligY + eligResized.height, mainColCenterX, incY - PRISMA_CONST.ARROWHEAD_LEN)
6675
+ d: arrow2(mainColCenterX, eligY + eligResized.height, mainColCenterX, incY - PRISMA_CONST.ARROWHEAD_LEN)
6568
6676
  });
6569
6677
  const screeningArrowY = screeningY + screeningResized.height / 2;
6570
6678
  edges.push({
6571
6679
  kind: "exclusion",
6572
6680
  from: "screening",
6573
6681
  to: "screening-excluded",
6574
- d: arrow(mainColRight, screeningArrowY, rightColArrowEnd, screeningArrowY)
6682
+ d: arrow2(mainColRight, screeningArrowY, rightColArrowEnd, screeningArrowY)
6575
6683
  });
6576
6684
  const eligArrowY = eligY + eligResized.height / 2;
6577
6685
  edges.push({
6578
6686
  kind: "exclusion",
6579
6687
  from: "eligibility",
6580
6688
  to: "eligibility-excluded",
6581
- d: arrow(mainColRight, eligArrowY, rightColArrowEnd, eligArrowY)
6689
+ d: arrow2(mainColRight, eligArrowY, rightColArrowEnd, eligArrowY)
6582
6690
  });
6583
6691
  let pageHeight = cursorY;
6584
6692
  if (warnings.length > 0) {
@@ -6911,7 +7019,7 @@ function approxLineWidth(line2) {
6911
7019
  return line2.text.length * charW;
6912
7020
  }
6913
7021
  function renderPrismaLayout(layout, config) {
6914
- const t = chunkD7EHZFK4_cjs.resolveBaseTheme(config?.theme ?? "default");
7022
+ const t = chunkL7POWM5B_cjs.resolveBaseTheme(config?.theme ?? "default");
6915
7023
  const children = [];
6916
7024
  children.push(chunk3WNW5Y7P_cjs.title(layout.title ?? "PRISMA 2020 flow diagram"));
6917
7025
  children.push(
@@ -7186,7 +7294,7 @@ function parseHeader(state2) {
7186
7294
  if (!m) break;
7187
7295
  const key = m[1].toLowerCase();
7188
7296
  const valueRaw = m[2].trim();
7189
- const value = stripQuotes(valueRaw);
7297
+ const value = stripQuotes2(valueRaw);
7190
7298
  if (key === "title") state2.ast.title = value;
7191
7299
  else if (key === "system") state2.ast.system = value;
7192
7300
  else if (key === "direction") {
@@ -7204,7 +7312,7 @@ function parseHeader(state2) {
7204
7312
  state2.i++;
7205
7313
  }
7206
7314
  }
7207
- function stripQuotes(s) {
7315
+ function stripQuotes2(s) {
7208
7316
  const t = s.trim();
7209
7317
  if (t.startsWith('"') && t.endsWith('"') && t.length >= 2) return t.slice(1, -1);
7210
7318
  return t;
@@ -7241,7 +7349,7 @@ function parseActorDecl(ln, state2) {
7241
7349
  id = asMatch[1];
7242
7350
  rest = rest.slice(0, asMatch.index).trim();
7243
7351
  }
7244
- const name = stripQuotes(rest);
7352
+ const name = stripQuotes2(rest);
7245
7353
  if (!name) throw new UsecaseParseError(`actor declaration missing name`, ln.line);
7246
7354
  const actorId = id ?? defaultIdFor(name);
7247
7355
  declareId(state2, actorId, "actor", ln.line);
@@ -7277,7 +7385,7 @@ function parseUsecaseDecl(ln, state2) {
7277
7385
  id = asMatch[1];
7278
7386
  rest = rest.slice(0, asMatch.index).trim();
7279
7387
  }
7280
- const name = stripQuotes(rest);
7388
+ const name = stripQuotes2(rest);
7281
7389
  if (!name) throw new UsecaseParseError(`usecase declaration missing name`, ln.line);
7282
7390
  const ucId = id ?? defaultIdFor(name);
7283
7391
  declareId(state2, ucId, "usecase", ln.line);
@@ -8032,13 +8140,13 @@ function layoutUsecase(ast) {
8032
8140
  pb = perimeter(r.target, a.cx, a.cy);
8033
8141
  }
8034
8142
  const dashed = r.kind === "include" || r.kind === "extend";
8035
- let arrowKind = "none";
8036
- if (r.kind === "directed" || r.kind === "include" || r.kind === "extend") arrowKind = "open";
8037
- else if (r.kind === "generalization") arrowKind = "hollow";
8143
+ let arrowKind2 = "none";
8144
+ if (r.kind === "directed" || r.kind === "include" || r.kind === "extend") arrowKind2 = "open";
8145
+ else if (r.kind === "generalization") arrowKind2 = "hollow";
8038
8146
  const edge = {
8039
8147
  relation: r,
8040
8148
  d: `M ${round(pa.x)} ${round(pa.y)} L ${round(pb.x)} ${round(pb.y)}`,
8041
- arrowKind,
8149
+ arrowKind: arrowKind2,
8042
8150
  dashed
8043
8151
  };
8044
8152
  if (r.kind === "include" || r.kind === "extend") {
@@ -8382,7 +8490,7 @@ function renderTree(tr) {
8382
8490
  );
8383
8491
  }
8384
8492
  function renderUsecaseLayout(layout, config) {
8385
- const t = chunkD7EHZFK4_cjs.resolveBaseTheme(config?.theme ?? "default");
8493
+ const t = chunkL7POWM5B_cjs.resolveBaseTheme(config?.theme ?? "default");
8386
8494
  const children = [];
8387
8495
  const nInclude = layout.ast.relations.filter((r) => r.kind === "include").length;
8388
8496
  const nExtend = layout.ast.relations.filter((r) => r.kind === "extend").length;
@@ -8521,7 +8629,7 @@ function preprocess7(src) {
8521
8629
  }
8522
8630
  return out;
8523
8631
  }
8524
- function stripQuotes2(s) {
8632
+ function stripQuotes3(s) {
8525
8633
  const t = s.trim();
8526
8634
  if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
8527
8635
  return t.slice(1, -1);
@@ -8539,7 +8647,7 @@ function parseHeaderLine(ln, ast) {
8539
8647
  if (!m) return false;
8540
8648
  const key = m[1].toLowerCase();
8541
8649
  const valueRaw = m[2].trim();
8542
- const value = stripQuotes2(valueRaw);
8650
+ const value = stripQuotes3(valueRaw);
8543
8651
  switch (key) {
8544
8652
  case "title":
8545
8653
  ast.title = value;
@@ -8668,23 +8776,23 @@ function parseTaskLine(ln, ast) {
8668
8776
  milestone = true;
8669
8777
  return "";
8670
8778
  }).trim();
8671
- const markers3 = [];
8779
+ const markers5 = [];
8672
8780
  KEY_RE.lastIndex = 0;
8673
8781
  let mm;
8674
8782
  while ((mm = KEY_RE.exec(rest)) !== null) {
8675
- markers3.push({ key: mm[1].toLowerCase(), start: mm.index, valStart: mm.index + mm[0].length });
8783
+ markers5.push({ key: mm[1].toLowerCase(), start: mm.index, valStart: mm.index + mm[0].length });
8676
8784
  }
8677
8785
  const values = {};
8678
- for (let i = 0; i < markers3.length; i++) {
8679
- const cur = markers3[i];
8680
- const nextStart = i + 1 < markers3.length ? markers3[i + 1].start : rest.length;
8786
+ for (let i = 0; i < markers5.length; i++) {
8787
+ const cur = markers5[i];
8788
+ const nextStart = i + 1 < markers5.length ? markers5[i + 1].start : rest.length;
8681
8789
  const val = rest.slice(cur.valStart, nextStart).trim();
8682
8790
  if (cur.key in values) {
8683
8791
  throw new PertParseError(`duplicate '${cur.key}:' on task '${id}'`, ln.line);
8684
8792
  }
8685
8793
  values[cur.key] = val;
8686
8794
  }
8687
- const preamble = (markers3.length ? rest.slice(0, markers3[0].start) : rest).trim();
8795
+ const preamble = (markers5.length ? rest.slice(0, markers5[0].start) : rest).trim();
8688
8796
  if (preamble) {
8689
8797
  throw new PertParseError(`unexpected text '${preamble}' in task '${id}'`, ln.line);
8690
8798
  }
@@ -8712,7 +8820,7 @@ function parseTaskLine(ln, ast) {
8712
8820
  }
8713
8821
  const tags = values.tags ? values.tags.split(",").map((t) => t.trim()).filter(Boolean) : [];
8714
8822
  const className = values.class ? values.class.trim() : void 0;
8715
- const lane = values.lane ? stripQuotes2(values.lane) : void 0;
8823
+ const lane = values.lane ? stripQuotes3(values.lane) : void 0;
8716
8824
  const task = {
8717
8825
  id,
8718
8826
  label,
@@ -10087,186 +10195,3832 @@ function renderLanes(lanes, width) {
10087
10195
  chunk3WNW5Y7P_cjs.text({ class: "sx-pert-lane-label", x: 16, y: ln.y + ln.height / 2 + 4, "text-anchor": "start" }, ln.name)
10088
10196
  );
10089
10197
  }
10090
- return chunk3WNW5Y7P_cjs.group({ class: "sx-pert-lane" }, parts);
10198
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-pert-lane" }, parts);
10199
+ }
10200
+ function renderSentinel(s) {
10201
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-pert-sentinel", "data-id": s.id }, [
10202
+ chunk3WNW5Y7P_cjs.circle({ cx: s.cx, cy: s.cy, r: s.r }),
10203
+ chunk3WNW5Y7P_cjs.text({ x: s.cx, y: s.cy + 3.5, "text-anchor": "middle" }, s.label)
10204
+ ]);
10205
+ }
10206
+ function renderAxis2(axis, gridTop) {
10207
+ const parts = [];
10208
+ for (const tk of axis.ticks) {
10209
+ if (tk.major) {
10210
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: "sx-pert-grid", x1: tk.pos, y1: gridTop, x2: tk.pos, y2: axis.baseline }));
10211
+ }
10212
+ }
10213
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: "baseline", x1: axis.start, y1: axis.baseline, x2: axis.end, y2: axis.baseline }));
10214
+ for (const tk of axis.ticks) {
10215
+ const len = tk.major ? 7 : 4;
10216
+ parts.push(chunk3WNW5Y7P_cjs.line({ x1: tk.pos, y1: axis.baseline, x2: tk.pos, y2: axis.baseline + len }));
10217
+ if (tk.major) {
10218
+ parts.push(
10219
+ chunk3WNW5Y7P_cjs.text({ x: tk.pos, y: axis.baseline + 18, "text-anchor": "middle" }, fmtVal(tk.value))
10220
+ );
10221
+ }
10222
+ }
10223
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-pert-axis" }, parts);
10224
+ }
10225
+ function renderAoa(aoa) {
10226
+ const arcEls = [];
10227
+ const labelEls = [];
10228
+ for (const a of aoa.arcs) {
10229
+ const cls = `sx-pert-aoa-arc${a.dummy ? " dummy" : ""}${a.critical ? " critical" : ""}`;
10230
+ const marker = a.critical ? "url(#sx-pert-arrow-crit)" : "url(#sx-pert-arrow)";
10231
+ arcEls.push(
10232
+ chunk3WNW5Y7P_cjs.path({
10233
+ class: cls,
10234
+ d: a.d,
10235
+ "marker-end": marker,
10236
+ "data-from": a.from,
10237
+ "data-to": a.to,
10238
+ "data-task": a.taskId ?? "",
10239
+ "data-dummy": String(a.dummy),
10240
+ "data-critical": String(a.critical)
10241
+ })
10242
+ );
10243
+ if (!a.dummy && a.label) {
10244
+ const critCls = a.critical ? " critical" : "";
10245
+ const durStr = fmtVal(a.duration ?? 0);
10246
+ const w = Math.max(a.label.length * 6.2, durStr.length * 6) + 8;
10247
+ labelEls.push(
10248
+ chunk3WNW5Y7P_cjs.rect({ class: "sx-pert-edge-halo", x: a.labelX - w / 2, y: a.labelY - 16, width: w, height: 31, rx: 3, ry: 3 })
10249
+ );
10250
+ labelEls.push(
10251
+ chunk3WNW5Y7P_cjs.text({ class: `sx-pert-aoa-name${critCls}`, x: a.labelX, y: a.labelY - 4, "text-anchor": "middle" }, a.label)
10252
+ );
10253
+ labelEls.push(
10254
+ chunk3WNW5Y7P_cjs.text({ class: `sx-pert-aoa-dur${critCls}`, x: a.labelX, y: a.labelY + 12, "text-anchor": "middle" }, durStr)
10255
+ );
10256
+ }
10257
+ }
10258
+ const eventEls = [];
10259
+ for (const e of aoa.events) {
10260
+ eventEls.push(
10261
+ chunk3WNW5Y7P_cjs.group({ class: `sx-pert-aoa-event${e.critical ? " critical" : ""}`, "data-event": e.id }, [
10262
+ chunk3WNW5Y7P_cjs.circle({ cx: e.x, cy: e.y, r: e.r }),
10263
+ chunk3WNW5Y7P_cjs.text({ x: e.x, y: e.y + 4.5, "text-anchor": "middle" }, String(e.id))
10264
+ ])
10265
+ );
10266
+ }
10267
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-pert-aoa" }, [
10268
+ chunk3WNW5Y7P_cjs.group({ class: "sx-pert-aoa-arcs" }, arcEls),
10269
+ chunk3WNW5Y7P_cjs.group({ class: "sx-pert-aoa-labels" }, labelEls),
10270
+ chunk3WNW5Y7P_cjs.group({ class: "sx-pert-aoa-events" }, eventEls)
10271
+ ]);
10272
+ }
10273
+ function summaryText(summary) {
10274
+ const u = unitWord(summary.unit);
10275
+ const dur = `${fmtVal(summary.projectDuration)}${u ? " " + u : ""}`;
10276
+ const sigma = summary.projectStdDev !== void 0 ? ` \xB7 \u03C3 \u2248 ${fmtVal(summary.projectStdDev)}` : "";
10277
+ const plain = `Project duration ${dur} \xB7 ${summary.taskCount} tasks \xB7 ${summary.depCount} dependencies \xB7 ${summary.criticalCount} critical${sigma}`;
10278
+ const critPath = summary.criticalPath.map((id) => id).join(" \u2192 ");
10279
+ return { plain, critPath };
10280
+ }
10281
+ function renderPertLayout(layout, config) {
10282
+ const t = chunkL7POWM5B_cjs.resolveBaseTheme(config?.theme ?? "default");
10283
+ const children = [];
10284
+ const cp = layout.summary.criticalPath;
10285
+ children.push(chunk3WNW5Y7P_cjs.title(`PERT network${layout.title ? " \u2014 " + layout.title : ""}`));
10286
+ children.push(
10287
+ chunk3WNW5Y7P_cjs.desc(
10288
+ `${layout.summary.taskCount} activities, project duration ${fmtVal(layout.summary.projectDuration)} ${unitWord(layout.unit)}` + (cp.length ? `, critical path ${cp.join(" \u2192 ")}` : "") + "."
10289
+ )
10290
+ );
10291
+ children.push(chunk3WNW5Y7P_cjs.el("style", {}, buildCss4(t)));
10292
+ children.push(markers2());
10293
+ if (layout.title) {
10294
+ children.push(
10295
+ chunk3WNW5Y7P_cjs.text({ x: layout.width / 2, y: 26, class: "sx-pert-title", "text-anchor": "middle" }, layout.title)
10296
+ );
10297
+ }
10298
+ if (layout.aoa) {
10299
+ children.push(renderAoa(layout.aoa));
10300
+ } else {
10301
+ if (layout.lanes && layout.lanes.length) {
10302
+ children.push(renderLanes(layout.lanes, layout.width));
10303
+ }
10304
+ if (layout.axis) {
10305
+ let minBoxY = Infinity;
10306
+ for (const b of layout.boxes) minBoxY = Math.min(minBoxY, b.y);
10307
+ children.push(renderAxis2(layout.axis, isFinite(minBoxY) ? minBoxY : layout.axis.baseline - 20));
10308
+ }
10309
+ const edgeEls = [];
10310
+ for (const e of layout.edges) edgeEls.push(renderEdge3(e));
10311
+ children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-edges" }, edgeEls));
10312
+ if (layout.sentinels.length) {
10313
+ children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-sentinels" }, layout.sentinels.map(renderSentinel)));
10314
+ }
10315
+ const boxEls = [];
10316
+ for (const b of layout.boxes) boxEls.push(renderBox2(b, layout.mode));
10317
+ children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-tasks" }, boxEls));
10318
+ const labelEls = [];
10319
+ for (const e of layout.edges) {
10320
+ const lbl = renderEdgeLabel2(e);
10321
+ if (lbl) labelEls.push(lbl);
10322
+ }
10323
+ children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-labels" }, labelEls));
10324
+ }
10325
+ const { plain, critPath } = summaryText(layout.summary);
10326
+ const footerY = layout.height - 12;
10327
+ const footerParts = [
10328
+ chunk3WNW5Y7P_cjs.text({ class: "sx-pert-summary", x: PERT_PAD, y: footerY, "text-anchor": "start" }, plain)
10329
+ ];
10330
+ if (critPath) {
10331
+ footerParts.push(
10332
+ chunk3WNW5Y7P_cjs.text(
10333
+ { class: "sx-pert-summary crit", x: layout.width - PERT_PAD, y: footerY, "text-anchor": "end" },
10334
+ `Critical path: ${critPath}`
10335
+ )
10336
+ );
10337
+ }
10338
+ children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-footer" }, footerParts));
10339
+ return chunk3WNW5Y7P_cjs.svgRoot(
10340
+ {
10341
+ class: "sx-pert",
10342
+ role: "img",
10343
+ "aria-label": chunk3WNW5Y7P_cjs.escapeXml(layout.title ?? "PERT network diagram"),
10344
+ width: layout.width,
10345
+ height: layout.height,
10346
+ viewBox: `0 0 ${layout.width} ${layout.height}`,
10347
+ "data-diagram-type": "pert"
10348
+ },
10349
+ children
10350
+ );
10351
+ }
10352
+ var PERT_PAD = 24;
10353
+ function renderPert(textOrAst, config) {
10354
+ const ast = typeof textOrAst === "string" ? parsePert(textOrAst) : textOrAst;
10355
+ const schedule = schedulePert(ast);
10356
+ const layout = layoutPert(ast, schedule);
10357
+ return renderPertLayout(layout, config);
10358
+ }
10359
+
10360
+ // src/diagrams/pert/index.ts
10361
+ var pert = {
10362
+ type: "pert",
10363
+ detect(text2) {
10364
+ for (const raw of text2.split(/\r?\n/)) {
10365
+ const t = raw.trim();
10366
+ if (!t) continue;
10367
+ if (t.startsWith("#") || t.startsWith("//")) continue;
10368
+ return /^pert\b/i.test(t);
10369
+ }
10370
+ return false;
10371
+ },
10372
+ parse: parsePert,
10373
+ render(text2, config) {
10374
+ return renderPert(text2, config);
10375
+ }
10376
+ };
10377
+
10378
+ // src/diagrams/sequence/parser.ts
10379
+ var SequenceParseError = class extends Error {
10380
+ line;
10381
+ constructor(message, line2) {
10382
+ super(line2 ? `${message} (line ${line2})` : message);
10383
+ this.name = "SequenceParseError";
10384
+ this.line = line2;
10385
+ }
10386
+ };
10387
+ var KINDS = /* @__PURE__ */ new Set([
10388
+ "participant",
10389
+ "actor",
10390
+ "boundary",
10391
+ "control",
10392
+ "entity",
10393
+ "database",
10394
+ "collections",
10395
+ "queue"
10396
+ ]);
10397
+ var SIMPLE_FRAG = /* @__PURE__ */ new Set([
10398
+ "opt",
10399
+ "loop",
10400
+ "break",
10401
+ "critical",
10402
+ "neg",
10403
+ "ignore",
10404
+ "consider",
10405
+ "assert"
10406
+ ]);
10407
+ var MULTI_FRAG = /* @__PURE__ */ new Set(["par", "seq", "strict"]);
10408
+ var MSGSET_FRAG = /* @__PURE__ */ new Set(["ignore", "consider"]);
10409
+ var ALL_FRAG = /* @__PURE__ */ new Set([
10410
+ "alt",
10411
+ ...SIMPLE_FRAG,
10412
+ ...MULTI_FRAG
10413
+ ]);
10414
+ var ARROW_RE = /(-->|->>|o->|-x|->)/;
10415
+ function arrowKind(token) {
10416
+ switch (token) {
10417
+ case "-->":
10418
+ return "reply";
10419
+ case "->>":
10420
+ return "async";
10421
+ case "o->":
10422
+ return "found";
10423
+ case "-x":
10424
+ return "lost";
10425
+ default:
10426
+ return "sync";
10427
+ }
10428
+ }
10429
+ function firstWord(s) {
10430
+ const m = /^(\S+)/.exec(s);
10431
+ return m ? m[1] : "";
10432
+ }
10433
+ function extractGuard(rest) {
10434
+ const t = rest.trim();
10435
+ if (!t) return void 0;
10436
+ if (t.startsWith("[") && t.endsWith("]")) return t.slice(1, -1).trim();
10437
+ if (t.startsWith("(") && t.endsWith(")")) return t.slice(1, -1).trim();
10438
+ return t;
10439
+ }
10440
+ var SequenceParser = class {
10441
+ lines;
10442
+ i = 0;
10443
+ order = [];
10444
+ byId = /* @__PURE__ */ new Map();
10445
+ warnings = [];
10446
+ constructor(source) {
10447
+ this.lines = source.split(/\r?\n/).map((raw, idx) => ({
10448
+ text: raw.trim(),
10449
+ n: idx + 1
10450
+ }));
10451
+ }
10452
+ parse() {
10453
+ const header = this.consumeHeader();
10454
+ const ast = {
10455
+ type: "sequence",
10456
+ participants: [],
10457
+ statements: [],
10458
+ warnings: this.warnings
10459
+ };
10460
+ if (header.title) ast.title = header.title;
10461
+ const statements = this.parseBlock();
10462
+ const leftover = this.peek();
10463
+ if (leftover) {
10464
+ const fw = firstWord(leftover.text).toLowerCase();
10465
+ throw new SequenceParseError(
10466
+ `'${fw}' without a matching combined fragment`,
10467
+ leftover.n
10468
+ );
10469
+ }
10470
+ if (this.autonumberSpec) ast.autonumber = this.autonumberSpec;
10471
+ ast.statements = statements;
10472
+ ast.participants = this.order.map((id) => this.byId.get(id));
10473
+ return ast;
10474
+ }
10475
+ autonumberSpec;
10476
+ // ── line cursor ──────────────────────────────────────────────
10477
+ peek() {
10478
+ while (this.i < this.lines.length) {
10479
+ const ln = this.lines[this.i];
10480
+ if (ln.text === "" || ln.text.startsWith("#") || ln.text.startsWith("//")) {
10481
+ this.i++;
10482
+ continue;
10483
+ }
10484
+ return ln;
10485
+ }
10486
+ return null;
10487
+ }
10488
+ next() {
10489
+ const ln = this.peek();
10490
+ if (ln) this.i++;
10491
+ return ln;
10492
+ }
10493
+ // ── header ───────────────────────────────────────────────────
10494
+ consumeHeader() {
10495
+ const ln = this.next();
10496
+ if (!ln || !/^sequence\b/i.test(ln.text)) {
10497
+ throw new SequenceParseError(
10498
+ "A sequence diagram must start with the keyword 'sequence'",
10499
+ ln?.n
10500
+ );
10501
+ }
10502
+ const title2 = chunkNAGUZFXX_cjs.matchQuotedTitle(ln.text);
10503
+ return title2 ? { title: title2 } : {};
10504
+ }
10505
+ // ── participants ─────────────────────────────────────────────
10506
+ ensure(id, kind = "participant", line2) {
10507
+ let p = this.byId.get(id);
10508
+ if (!p) {
10509
+ p = { id, name: id, kind, line: line2 };
10510
+ this.byId.set(id, p);
10511
+ this.order.push(id);
10512
+ }
10513
+ return p;
10514
+ }
10515
+ declare(line2) {
10516
+ const fw = firstWord(line2.text);
10517
+ const kind = fw.toLowerCase();
10518
+ let rest = line2.text.slice(fw.length).trim();
10519
+ let stereotype;
10520
+ const stereoMatch = /«([^»]*)»|<<([^>]*)>>/.exec(rest);
10521
+ if (stereoMatch) {
10522
+ stereotype = (stereoMatch[1] ?? stereoMatch[2] ?? "").trim();
10523
+ rest = (rest.slice(0, stereoMatch.index) + rest.slice(stereoMatch.index + stereoMatch[0].length)).trim();
10524
+ }
10525
+ let id;
10526
+ let name;
10527
+ const asMatch = /\sas\s/i.exec(rest);
10528
+ if (asMatch) {
10529
+ id = chunkNAGUZFXX_cjs.stripQuotes(rest.slice(0, asMatch.index).trim());
10530
+ name = chunkNAGUZFXX_cjs.stripQuotes(rest.slice(asMatch.index + asMatch[0].length).trim());
10531
+ } else {
10532
+ id = chunkNAGUZFXX_cjs.stripQuotes(rest);
10533
+ name = id;
10534
+ }
10535
+ if (!id) {
10536
+ throw new SequenceParseError(`'${fw}' declaration needs a name`, line2.n);
10537
+ }
10538
+ const existing = this.byId.get(id);
10539
+ if (existing) {
10540
+ existing.kind = kind;
10541
+ existing.name = name;
10542
+ if (stereotype) existing.stereotype = stereotype;
10543
+ } else {
10544
+ const p = { id, name, kind, line: line2.n };
10545
+ if (stereotype) p.stereotype = stereotype;
10546
+ this.byId.set(id, p);
10547
+ this.order.push(id);
10548
+ }
10549
+ }
10550
+ // ── block / statement dispatch ───────────────────────────────
10551
+ /** Parse statements until a terminator (`end`/`else`/`and`) or EOF. Terminators are left unconsumed. */
10552
+ parseBlock() {
10553
+ const out = [];
10554
+ for (; ; ) {
10555
+ const ln = this.peek();
10556
+ if (!ln) break;
10557
+ const fw = firstWord(ln.text).toLowerCase();
10558
+ if (fw === "end" || fw === "else" || fw === "and") break;
10559
+ if (KINDS.has(fw)) {
10560
+ this.i++;
10561
+ this.declare(ln);
10562
+ continue;
10563
+ }
10564
+ if (ALL_FRAG.has(fw)) {
10565
+ out.push(this.parseFragment(ln));
10566
+ continue;
10567
+ }
10568
+ this.i++;
10569
+ const stmt = this.parseSimpleStatement(ln);
10570
+ if (stmt) out.push(stmt);
10571
+ }
10572
+ return out;
10573
+ }
10574
+ parseFragment(open) {
10575
+ const fw = firstWord(open.text).toLowerCase();
10576
+ this.i++;
10577
+ let rest = open.text.slice(fw.length);
10578
+ let messageSet;
10579
+ if (MSGSET_FRAG.has(fw)) {
10580
+ const bm = /\{([^}]*)\}/.exec(rest);
10581
+ if (bm) {
10582
+ messageSet = bm[1].split(",").map((s) => s.trim()).filter(Boolean);
10583
+ rest = rest.slice(0, bm.index) + rest.slice(bm.index + bm[0].length);
10584
+ }
10585
+ }
10586
+ const firstGuard = extractGuard(rest);
10587
+ const operands = [
10588
+ { guard: firstGuard, statements: this.parseBlock() }
10589
+ ];
10590
+ for (; ; ) {
10591
+ const ln = this.peek();
10592
+ if (!ln) {
10593
+ throw new SequenceParseError(`Unterminated '${fw}' fragment`, open.n);
10594
+ }
10595
+ const term = firstWord(ln.text).toLowerCase();
10596
+ if (term === "end") {
10597
+ this.i++;
10598
+ break;
10599
+ }
10600
+ if (term === "else") {
10601
+ if (fw !== "alt") {
10602
+ throw new SequenceParseError(
10603
+ `'else' is only valid inside an 'alt' fragment, not '${fw}'`,
10604
+ ln.n
10605
+ );
10606
+ }
10607
+ this.i++;
10608
+ operands.push({
10609
+ guard: extractGuard(ln.text.slice(4)),
10610
+ statements: this.parseBlock()
10611
+ });
10612
+ continue;
10613
+ }
10614
+ if (term === "and") {
10615
+ if (!MULTI_FRAG.has(fw)) {
10616
+ throw new SequenceParseError(
10617
+ `'and' is only valid inside par/seq/strict, not '${fw}'`,
10618
+ ln.n
10619
+ );
10620
+ }
10621
+ this.i++;
10622
+ const label = ln.text.slice(3).trim();
10623
+ operands.push({
10624
+ guard: label ? extractGuard(label) : void 0,
10625
+ statements: this.parseBlock()
10626
+ });
10627
+ continue;
10628
+ }
10629
+ throw new SequenceParseError(`Unexpected '${term}' in '${fw}'`, ln.n);
10630
+ }
10631
+ const frag = { kind: "fragment", op: fw, operands, line: open.n };
10632
+ if (messageSet && messageSet.length) frag.messageSet = messageSet;
10633
+ return frag;
10634
+ }
10635
+ /** Non-fragment, non-participant statements. */
10636
+ parseSimpleStatement(ln) {
10637
+ const text2 = ln.text;
10638
+ const fw = firstWord(text2).toLowerCase();
10639
+ if (fw === "activate" || fw === "deactivate") {
10640
+ const m = /^(?:de)?activate\s+(\S+)$/i.exec(text2);
10641
+ if (!m) throw new SequenceParseError(`Malformed ${fw} statement`, ln.n);
10642
+ this.ensure(m[1], "participant", ln.n);
10643
+ return { kind: fw, id: m[1], line: ln.n };
10644
+ }
10645
+ if (fw === "note") {
10646
+ return this.parseNote(ln);
10647
+ }
10648
+ if (fw === "ref") {
10649
+ const m = /^ref\s+over\s+(.+?)\s*:\s*(.*)$/i.exec(text2);
10650
+ if (!m) throw new SequenceParseError("Malformed ref (expected: ref over A, B : text)", ln.n);
10651
+ const ids = m[1].split(",").map((s) => chunkNAGUZFXX_cjs.stripQuotes(s.trim())).filter(Boolean);
10652
+ ids.forEach((id) => this.ensure(id, "participant", ln.n));
10653
+ return { kind: "ref", ids, text: m[2].trim(), line: ln.n };
10654
+ }
10655
+ if (text2.startsWith("==")) {
10656
+ const m = /^==\s*(.*?)\s*==\s*$/.exec(text2);
10657
+ return { kind: "divider", text: m ? m[1].trim() : "", line: ln.n };
10658
+ }
10659
+ if (fw === "state") {
10660
+ const m = /^state\s+(\S+)\s*:\s*(.*)$/i.exec(text2);
10661
+ if (!m) throw new SequenceParseError("Malformed state invariant (expected: state A : text)", ln.n);
10662
+ this.ensure(m[1], "participant", ln.n);
10663
+ return { kind: "invariant", id: m[1], text: m[2].trim(), line: ln.n };
10664
+ }
10665
+ if (fw === "destroy") {
10666
+ const m = /^destroy\s+(\S+)$/i.exec(text2);
10667
+ if (!m) throw new SequenceParseError("Malformed destroy statement", ln.n);
10668
+ this.ensure(m[1], "participant", ln.n);
10669
+ return { kind: "destroy", id: m[1], line: ln.n };
10670
+ }
10671
+ if (fw === "autonumber") {
10672
+ const m = /^autonumber(?:\s+(\d+))?(?:\s+(\d+))?$/i.exec(text2);
10673
+ const start = m && m[1] ? parseInt(m[1], 10) : 1;
10674
+ const step = m && m[2] ? parseInt(m[2], 10) : 1;
10675
+ this.autonumberSpec = { start, step };
10676
+ return null;
10677
+ }
10678
+ return this.parseMessage(ln);
10679
+ }
10680
+ parseNote(ln) {
10681
+ const m = /^note\s+(over|left of|right of)\s+(.+?)\s*:\s*(.*)$/i.exec(ln.text);
10682
+ if (!m) {
10683
+ throw new SequenceParseError(
10684
+ "Malformed note (expected: note over|left of|right of A[, B] : text)",
10685
+ ln.n
10686
+ );
10687
+ }
10688
+ const placementRaw = m[1].toLowerCase();
10689
+ const placement = placementRaw === "over" ? "over" : placementRaw.startsWith("left") ? "left" : "right";
10690
+ const ids = m[2].split(",").map((s) => chunkNAGUZFXX_cjs.stripQuotes(s.trim())).filter(Boolean);
10691
+ ids.forEach((id) => this.ensure(id, "participant", ln.n));
10692
+ return { kind: "note", placement, ids, text: m[3].trim(), line: ln.n };
10693
+ }
10694
+ parseMessage(ln) {
10695
+ const text2 = ln.text;
10696
+ const arrowMatch = ARROW_RE.exec(text2);
10697
+ if (!arrowMatch) {
10698
+ throw new SequenceParseError(`Cannot parse statement: "${text2}"`, ln.n);
10699
+ }
10700
+ const token = arrowMatch[1];
10701
+ const at = arrowMatch.index;
10702
+ const leftRaw = text2.slice(0, at);
10703
+ let rightRaw = text2.slice(at + token.length);
10704
+ let label;
10705
+ const colon = rightRaw.indexOf(":");
10706
+ if (colon >= 0) {
10707
+ label = rightRaw.slice(colon + 1).trim();
10708
+ rightRaw = rightRaw.slice(0, colon);
10709
+ }
10710
+ let activateTarget = false;
10711
+ let deactivateSource = false;
10712
+ let left = leftRaw.trim();
10713
+ if (left.endsWith("+")) {
10714
+ activateTarget = true;
10715
+ left = left.slice(0, -1).trim();
10716
+ } else if (left.endsWith("-")) {
10717
+ deactivateSource = true;
10718
+ left = left.slice(0, -1).trim();
10719
+ }
10720
+ let right = rightRaw.trim();
10721
+ if (right.startsWith("+")) {
10722
+ activateTarget = true;
10723
+ right = right.slice(1).trim();
10724
+ } else if (right.startsWith("-")) {
10725
+ deactivateSource = true;
10726
+ right = right.slice(1).trim();
10727
+ }
10728
+ const arrow2 = arrowKind(token);
10729
+ let create = false;
10730
+ if (right.startsWith("*")) {
10731
+ create = true;
10732
+ right = right.slice(1).trim();
10733
+ }
10734
+ const from = arrow2 === "found" ? "" : chunkNAGUZFXX_cjs.stripQuotes(left);
10735
+ const to = arrow2 === "lost" ? "" : chunkNAGUZFXX_cjs.stripQuotes(right);
10736
+ if (from) this.ensure(from, "participant", ln.n);
10737
+ if (to) {
10738
+ const p = this.ensure(to, "participant", ln.n);
10739
+ if (create) p.createdInline = true;
10740
+ }
10741
+ const msg = {
10742
+ kind: "message",
10743
+ from,
10744
+ to,
10745
+ arrow: arrow2,
10746
+ line: ln.n
10747
+ };
10748
+ if (label) msg.label = label;
10749
+ if (activateTarget) msg.activateTarget = true;
10750
+ if (deactivateSource) msg.deactivateSource = true;
10751
+ if (create) msg.create = true;
10752
+ return msg;
10753
+ }
10754
+ };
10755
+ function parseSequence(text2) {
10756
+ return new SequenceParser(text2).parse();
10757
+ }
10758
+
10759
+ // src/diagrams/sequence/layout.ts
10760
+ var SEQ_CONST = {
10761
+ HEAD_W_MIN: 90,
10762
+ HEAD_H: 38,
10763
+ HEAD_PAD_X: 14,
10764
+ LIFELINE_GAP: 140,
10765
+ LEFT_MARGIN: 24,
10766
+ RIGHT_PAD: 28,
10767
+ TOP_PAD: 16,
10768
+ HEAD_TO_FIRST: 30,
10769
+ EVENT_GAP: 38,
10770
+ BOTTOM_PAD: 30,
10771
+ SELF_GAP: 30,
10772
+ SELF_LOOP_W: 48,
10773
+ ACT_W: 10,
10774
+ ACT_NEST_DX: 6,
10775
+ ACT_STEP: 30,
10776
+ FRAG_PAD_X: 10,
10777
+ FRAG_LABEL_H: 18,
10778
+ FRAG_PAD_TOP: 16,
10779
+ FRAG_PAD_BOTTOM: 12,
10780
+ FRAG_GAP_BEFORE: 12,
10781
+ FRAG_OPERAND_GAP: 18,
10782
+ FRAG_NEST_INSET: 8,
10783
+ REF_H: 40,
10784
+ REF_GAP_BEFORE: 12,
10785
+ NOTE_GAP_BEFORE: 10,
10786
+ NOTE_PAD: 8,
10787
+ NOTE_MIN_W: 70,
10788
+ NOTE_LINE_H: 15,
10789
+ NOTE_SIDE_GAP: 16,
10790
+ DIV_H: 26,
10791
+ DIV_GAP_BEFORE: 8,
10792
+ INV_GAP_BEFORE: 10,
10793
+ INV_PAD: 10,
10794
+ INV_H: 24,
10795
+ LABEL_GAP_PAD: 26,
10796
+ LOST_LEN: 72
10797
+ };
10798
+ var LABEL_SIZE = 11;
10799
+ var HEAD_TEXT_SIZE = 12.5;
10800
+ function textWidth(s, size) {
10801
+ let w = 0;
10802
+ for (const ch of s) {
10803
+ w += /[⺀-￿]/.test(ch) ? size : size * 0.55;
10804
+ }
10805
+ return w;
10806
+ }
10807
+ var SequenceLayout = class {
10808
+ ast;
10809
+ lifelines = [];
10810
+ colOf = /* @__PURE__ */ new Map();
10811
+ llById = /* @__PURE__ */ new Map();
10812
+ x = [];
10813
+ headW = [];
10814
+ y = 0;
10815
+ open = /* @__PURE__ */ new Map();
10816
+ colAccStack = [];
10817
+ messages = [];
10818
+ activations = [];
10819
+ fragments = [];
10820
+ refs = [];
10821
+ notes = [];
10822
+ dividers = [];
10823
+ invariants = [];
10824
+ destroys = [];
10825
+ warnings = [];
10826
+ autoCounter = null;
10827
+ contentRight = 0;
10828
+ constructor(ast) {
10829
+ this.ast = ast;
10830
+ }
10831
+ run() {
10832
+ this.buildColumns();
10833
+ if (this.ast.autonumber) this.autoCounter = this.ast.autonumber.start;
10834
+ const headBottom = SEQ_CONST.TOP_PAD + SEQ_CONST.HEAD_H;
10835
+ this.y = headBottom + SEQ_CONST.HEAD_TO_FIRST - SEQ_CONST.EVENT_GAP;
10836
+ this.walk(this.ast.statements, 0);
10837
+ const bottomY = this.y + SEQ_CONST.EVENT_GAP;
10838
+ for (const [id, stack] of this.open) {
10839
+ while (stack.length) {
10840
+ const b = stack.pop();
10841
+ this.activations.push({ id, x: b.x, yTop: b.yTop, yBottom: bottomY, level: b.level });
10842
+ }
10843
+ }
10844
+ const bodyBottom = bottomY + SEQ_CONST.BOTTOM_PAD;
10845
+ for (const ll of this.lifelines) {
10846
+ if (!ll.destroyed) ll.axisBottom = bodyBottom;
10847
+ }
10848
+ const lastCenter = this.x[this.x.length - 1] ?? SEQ_CONST.LEFT_MARGIN;
10849
+ const lastHalf = (this.headW[this.headW.length - 1] ?? SEQ_CONST.HEAD_W_MIN) / 2;
10850
+ this.contentRight = Math.max(this.contentRight, lastCenter + lastHalf);
10851
+ const width = Math.round(this.contentRight + SEQ_CONST.RIGHT_PAD);
10852
+ const height = Math.round(bodyBottom);
10853
+ for (const d of this.dividers) d.width = width;
10854
+ const result = {
10855
+ width,
10856
+ height,
10857
+ lifelines: this.lifelines,
10858
+ messages: this.messages,
10859
+ activations: this.activations,
10860
+ fragments: this.fragments,
10861
+ refs: this.refs,
10862
+ notes: this.notes,
10863
+ dividers: this.dividers,
10864
+ invariants: this.invariants,
10865
+ destroys: this.destroys,
10866
+ warnings: this.warnings.concat(this.ast.warnings),
10867
+ ast: this.ast
10868
+ };
10869
+ if (this.ast.title) result.title = this.ast.title;
10870
+ return result;
10871
+ }
10872
+ // ── horizontal pass ──────────────────────────────────────────
10873
+ buildColumns() {
10874
+ const ps = this.ast.participants;
10875
+ this.headW = ps.map(
10876
+ (p) => Math.max(SEQ_CONST.HEAD_W_MIN, Math.ceil(textWidth(p.name, HEAD_TEXT_SIZE)) + SEQ_CONST.HEAD_PAD_X * 2)
10877
+ );
10878
+ ps.forEach((p, i) => this.colOf.set(p.id, i));
10879
+ const n = ps.length;
10880
+ const gaps = new Array(Math.max(0, n - 1)).fill(SEQ_CONST.LIFELINE_GAP);
10881
+ for (let k = 0; k < gaps.length; k++) {
10882
+ gaps[k] = Math.max(gaps[k], this.headW[k] / 2 + this.headW[k + 1] / 2 + 28);
10883
+ }
10884
+ const msgs = collectMessages(this.ast.statements);
10885
+ for (const m of msgs) {
10886
+ const label = m.label ?? "";
10887
+ const w = textWidth(label, LABEL_SIZE) + SEQ_CONST.LABEL_GAP_PAD;
10888
+ const cf = this.colOf.get(m.from);
10889
+ const ct = this.colOf.get(m.to);
10890
+ if (cf === void 0 && ct === void 0) continue;
10891
+ if (cf === void 0 || ct === void 0) continue;
10892
+ if (cf === ct) {
10893
+ if (cf < gaps.length) gaps[cf] = Math.max(gaps[cf], SEQ_CONST.SELF_LOOP_W + w);
10894
+ continue;
10895
+ }
10896
+ const i = Math.min(cf, ct);
10897
+ const j = Math.max(cf, ct);
10898
+ if (j === i + 1) {
10899
+ gaps[i] = Math.max(gaps[i], w);
10900
+ } else {
10901
+ let span = 0;
10902
+ for (let k = i; k < j; k++) span += gaps[k];
10903
+ if (span < w) {
10904
+ const add = (w - span) / (j - i);
10905
+ for (let k = i; k < j; k++) gaps[k] += add;
10906
+ }
10907
+ }
10908
+ }
10909
+ this.x = new Array(n);
10910
+ let cx = SEQ_CONST.LEFT_MARGIN + (this.headW[0] ?? SEQ_CONST.HEAD_W_MIN) / 2;
10911
+ for (let i = 0; i < n; i++) {
10912
+ this.x[i] = cx;
10913
+ if (i < gaps.length) cx += gaps[i];
10914
+ }
10915
+ ps.forEach((p, i) => {
10916
+ const created = p.createdInline === true;
10917
+ const headY = created ? -1 : SEQ_CONST.TOP_PAD;
10918
+ const ll = {
10919
+ participant: p,
10920
+ index: i,
10921
+ x: this.x[i],
10922
+ headX: this.x[i] - this.headW[i] / 2,
10923
+ headY,
10924
+ headW: this.headW[i],
10925
+ headH: SEQ_CONST.HEAD_H,
10926
+ axisTop: created ? -1 : SEQ_CONST.TOP_PAD + SEQ_CONST.HEAD_H,
10927
+ axisBottom: 0,
10928
+ destroyed: false
10929
+ };
10930
+ this.lifelines.push(ll);
10931
+ this.llById.set(p.id, ll);
10932
+ });
10933
+ }
10934
+ // ── column accounting (for fragment extents) ─────────────────
10935
+ touch(col) {
10936
+ if (col === void 0) return;
10937
+ for (const acc of this.colAccStack) {
10938
+ if (col < acc.min) acc.min = col;
10939
+ if (col > acc.max) acc.max = col;
10940
+ }
10941
+ }
10942
+ noteRight(x, w) {
10943
+ this.contentRight = Math.max(this.contentRight, x + w);
10944
+ }
10945
+ // ── activation bars ──────────────────────────────────────────
10946
+ openBar(id) {
10947
+ const col = this.colOf.get(id);
10948
+ if (col === void 0) return;
10949
+ const stack = this.open.get(id) ?? [];
10950
+ const level = stack.length;
10951
+ const x = this.x[col] - SEQ_CONST.ACT_W / 2 + level * SEQ_CONST.ACT_NEST_DX;
10952
+ stack.push({ x, yTop: this.y, level });
10953
+ this.open.set(id, stack);
10954
+ }
10955
+ closeBar(id) {
10956
+ const stack = this.open.get(id);
10957
+ if (!stack || stack.length === 0) {
10958
+ this.warnings.push(`deactivate '${id}' with no matching activation`);
10959
+ return;
10960
+ }
10961
+ const b = stack.pop();
10962
+ this.activations.push({ id, x: b.x, yTop: b.yTop, yBottom: this.y, level: b.level });
10963
+ }
10964
+ faceEdge(id, col, towardRight, extraLevel = 0) {
10965
+ const stack = this.open.get(id) ?? [];
10966
+ const levels = stack.length + extraLevel;
10967
+ if (levels <= 0) return this.x[col];
10968
+ if (!towardRight) return this.x[col] - SEQ_CONST.ACT_W / 2;
10969
+ return this.x[col] + SEQ_CONST.ACT_W / 2 + (levels - 1) * SEQ_CONST.ACT_NEST_DX;
10970
+ }
10971
+ // ── vertical walk ────────────────────────────────────────────
10972
+ walk(stmts, depth) {
10973
+ for (const s of stmts) {
10974
+ switch (s.kind) {
10975
+ case "message":
10976
+ this.placeMessage(s);
10977
+ break;
10978
+ case "activate":
10979
+ this.y += SEQ_CONST.ACT_STEP;
10980
+ this.openBar(s.id);
10981
+ this.touch(this.colOf.get(s.id));
10982
+ break;
10983
+ case "deactivate":
10984
+ this.y += SEQ_CONST.ACT_STEP;
10985
+ this.closeBar(s.id);
10986
+ this.touch(this.colOf.get(s.id));
10987
+ break;
10988
+ case "note":
10989
+ this.placeNote(s);
10990
+ break;
10991
+ case "ref":
10992
+ this.placeRef(s.ids, s.text);
10993
+ break;
10994
+ case "divider":
10995
+ this.y += SEQ_CONST.DIV_GAP_BEFORE;
10996
+ this.dividers.push({ text: s.text, y: this.y + SEQ_CONST.DIV_H / 2, width: 0 });
10997
+ this.y += SEQ_CONST.DIV_H;
10998
+ break;
10999
+ case "invariant":
11000
+ this.placeInvariant(s.id, s.text);
11001
+ break;
11002
+ case "destroy":
11003
+ this.placeDestroy(s.id);
11004
+ break;
11005
+ case "fragment":
11006
+ this.placeFragment(s.op, s.operands, depth, s.messageSet);
11007
+ break;
11008
+ }
11009
+ }
11010
+ }
11011
+ nextNumber() {
11012
+ if (this.autoCounter === null || !this.ast.autonumber) return void 0;
11013
+ const n = this.autoCounter;
11014
+ this.autoCounter += this.ast.autonumber.step;
11015
+ return n;
11016
+ }
11017
+ placeMessage(m) {
11018
+ this.y += SEQ_CONST.EVENT_GAP;
11019
+ const y = this.y;
11020
+ const cf = this.colOf.get(m.from);
11021
+ const ct = this.colOf.get(m.to);
11022
+ const self = m.from !== "" && m.from === m.to;
11023
+ let x1;
11024
+ let x2;
11025
+ if (m.arrow === "found") {
11026
+ const c = ct;
11027
+ x2 = this.faceEdge(m.to, c, false);
11028
+ x1 = x2 - SEQ_CONST.LOST_LEN;
11029
+ } else if (m.arrow === "lost") {
11030
+ const c = cf;
11031
+ x1 = this.faceEdge(m.from, c, true);
11032
+ x2 = x1 + SEQ_CONST.LOST_LEN;
11033
+ } else if (self) {
11034
+ const c = cf;
11035
+ x1 = this.faceEdge(m.from, c, true);
11036
+ x2 = x1 + SEQ_CONST.SELF_LOOP_W;
11037
+ } else {
11038
+ const fromRight = (ct ?? 0) > (cf ?? 0);
11039
+ x1 = this.faceEdge(m.from, cf, fromRight);
11040
+ if (m.create) {
11041
+ const halfW = this.headW[ct] / 2;
11042
+ x2 = fromRight ? this.x[ct] - halfW : this.x[ct] + halfW;
11043
+ } else {
11044
+ x2 = this.faceEdge(m.to, ct, !fromRight, m.activateTarget ? 1 : 0);
11045
+ }
11046
+ }
11047
+ const row = { message: m, y, x1, x2, self };
11048
+ const num = this.nextNumber();
11049
+ if (num !== void 0) row.number = num;
11050
+ if (self) {
11051
+ row.selfBottomY = y + SEQ_CONST.SELF_GAP;
11052
+ this.y = row.selfBottomY;
11053
+ }
11054
+ this.messages.push(row);
11055
+ this.contentRight = Math.max(this.contentRight, x1, x2);
11056
+ this.touch(cf);
11057
+ this.touch(ct);
11058
+ if (m.create && ct !== void 0) {
11059
+ const ll = this.llById.get(m.to);
11060
+ ll.headY = y - SEQ_CONST.HEAD_H / 2;
11061
+ ll.axisTop = ll.headY + SEQ_CONST.HEAD_H;
11062
+ }
11063
+ if (m.activateTarget && ct !== void 0) this.openBar(m.to);
11064
+ if (m.deactivateSource && cf !== void 0) this.closeBar(m.from);
11065
+ }
11066
+ placeNote(note) {
11067
+ this.y += SEQ_CONST.NOTE_GAP_BEFORE;
11068
+ const lines = note.text.split(/<br\s*\/?>|\\n/);
11069
+ const textW = lines.reduce((mx, l) => Math.max(mx, textWidth(l, LABEL_SIZE)), 0);
11070
+ const w = Math.max(SEQ_CONST.NOTE_MIN_W, Math.ceil(textW) + SEQ_CONST.NOTE_PAD * 2);
11071
+ const h = lines.length * SEQ_CONST.NOTE_LINE_H + SEQ_CONST.NOTE_PAD * 2;
11072
+ const top = this.y;
11073
+ const cols = note.ids.map((id) => this.colOf.get(id)).filter((c) => c !== void 0);
11074
+ cols.forEach((c) => this.touch(c));
11075
+ let x;
11076
+ let boxW = w;
11077
+ if (note.placement === "over" && cols.length >= 2) {
11078
+ const a = this.x[Math.min(...cols)];
11079
+ const b = this.x[Math.max(...cols)];
11080
+ const span = b - a + this.headW[Math.min(...cols)] / 2 + this.headW[Math.max(...cols)] / 2;
11081
+ boxW = Math.max(w, span);
11082
+ x = (a + b) / 2 - boxW / 2;
11083
+ } else if (note.placement === "left" && cols.length) {
11084
+ x = this.x[cols[0]] - SEQ_CONST.NOTE_SIDE_GAP - w;
11085
+ } else if (note.placement === "right" && cols.length) {
11086
+ x = this.x[cols[0]] + SEQ_CONST.NOTE_SIDE_GAP;
11087
+ } else {
11088
+ const c = cols[0] ?? 0;
11089
+ x = this.x[c] - w / 2;
11090
+ }
11091
+ this.notes.push({ note, x, y: top, width: boxW, height: h });
11092
+ this.noteRight(x, boxW);
11093
+ this.y = top + h;
11094
+ }
11095
+ placeRef(ids, text2) {
11096
+ this.y += SEQ_CONST.REF_GAP_BEFORE;
11097
+ const cols = ids.map((id) => this.colOf.get(id)).filter((c) => c !== void 0);
11098
+ cols.forEach((c) => this.touch(c));
11099
+ const minC = cols.length ? Math.min(...cols) : 0;
11100
+ const maxC = cols.length ? Math.max(...cols) : 0;
11101
+ const left = this.x[minC] - SEQ_CONST.FRAG_PAD_X;
11102
+ const right = this.x[maxC] + SEQ_CONST.FRAG_PAD_X;
11103
+ const top = this.y;
11104
+ this.refs.push({ text: text2, x: left, y: top, width: right - left, height: SEQ_CONST.REF_H });
11105
+ this.noteRight(left, right - left);
11106
+ this.y = top + SEQ_CONST.REF_H;
11107
+ }
11108
+ placeInvariant(id, text2) {
11109
+ this.y += SEQ_CONST.INV_GAP_BEFORE;
11110
+ const col = this.colOf.get(id);
11111
+ this.touch(col);
11112
+ const w = Math.ceil(textWidth(text2, LABEL_SIZE)) + SEQ_CONST.INV_PAD * 2;
11113
+ const cx = col !== void 0 ? this.x[col] : SEQ_CONST.LEFT_MARGIN;
11114
+ const top = this.y;
11115
+ this.invariants.push({ text: text2, cx, y: top, width: w, height: SEQ_CONST.INV_H });
11116
+ this.noteRight(cx - w / 2, w);
11117
+ this.y = top + SEQ_CONST.INV_H;
11118
+ }
11119
+ placeDestroy(id) {
11120
+ this.y += SEQ_CONST.EVENT_GAP / 2;
11121
+ const ll = this.llById.get(id);
11122
+ const col = this.colOf.get(id);
11123
+ if (ll && col !== void 0) {
11124
+ ll.destroyed = true;
11125
+ ll.axisBottom = this.y;
11126
+ this.destroys.push({ x: this.x[col], y: this.y });
11127
+ this.touch(col);
11128
+ }
11129
+ }
11130
+ placeFragment(op, operands, depth, messageSet) {
11131
+ const frameTop = this.y + SEQ_CONST.FRAG_GAP_BEFORE;
11132
+ const acc = { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY };
11133
+ this.colAccStack.push(acc);
11134
+ const operandGeom = [];
11135
+ this.y = frameTop + SEQ_CONST.FRAG_LABEL_H + SEQ_CONST.FRAG_PAD_TOP - SEQ_CONST.EVENT_GAP;
11136
+ const firstGeom = {};
11137
+ if (operands[0]?.guard) firstGeom.guard = operands[0].guard;
11138
+ operandGeom.push(firstGeom);
11139
+ this.walk(operands[0]?.statements ?? [], depth + 1);
11140
+ for (let oi = 1; oi < operands.length; oi++) {
11141
+ const sepY = this.y + SEQ_CONST.FRAG_OPERAND_GAP;
11142
+ const geom = { sepY };
11143
+ if (operands[oi].guard) geom.guard = operands[oi].guard;
11144
+ operandGeom.push(geom);
11145
+ this.y = sepY + SEQ_CONST.FRAG_PAD_TOP - SEQ_CONST.EVENT_GAP;
11146
+ this.walk(operands[oi].statements, depth + 1);
11147
+ }
11148
+ const frameBottom = this.y + SEQ_CONST.FRAG_PAD_BOTTOM;
11149
+ this.colAccStack.pop();
11150
+ let minC = acc.min;
11151
+ let maxC = acc.max;
11152
+ if (!isFinite(minC)) {
11153
+ minC = 0;
11154
+ maxC = Math.max(0, this.lifelines.length - 1);
11155
+ }
11156
+ const inset = depth * SEQ_CONST.FRAG_NEST_INSET;
11157
+ let left = this.x[minC] - SEQ_CONST.FRAG_PAD_X + inset;
11158
+ let right = this.x[maxC] + SEQ_CONST.FRAG_PAD_X - inset;
11159
+ if (right - left < 40) {
11160
+ const mid = (left + right) / 2;
11161
+ left = mid - 20;
11162
+ right = mid + 20;
11163
+ }
11164
+ const frame = {
11165
+ op,
11166
+ x: left,
11167
+ y: frameTop,
11168
+ width: right - left,
11169
+ height: frameBottom - frameTop,
11170
+ operands: operandGeom
11171
+ };
11172
+ if (messageSet && messageSet.length) frame.messageSet = messageSet;
11173
+ this.fragments.push(frame);
11174
+ this.noteRight(left, right - left);
11175
+ this.y = frameBottom;
11176
+ }
11177
+ };
11178
+ function collectMessages(stmts) {
11179
+ const out = [];
11180
+ for (const s of stmts) {
11181
+ if (s.kind === "message") out.push(s);
11182
+ else if (s.kind === "fragment") {
11183
+ for (const op of s.operands) out.push(...collectMessages(op.statements));
11184
+ }
11185
+ }
11186
+ return out;
11187
+ }
11188
+ function layoutSequence(ast) {
11189
+ return new SequenceLayout(ast).run();
11190
+ }
11191
+
11192
+ // src/diagrams/sequence/renderer.ts
11193
+ var HEAD = "#e8f0fb";
11194
+ var HEAD_STROKE = "#5b85c0";
11195
+ function buildCss5(t) {
11196
+ return `
11197
+ .sx-seq { font-family: system-ui, -apple-system, sans-serif; }
11198
+ .sx-seq-axis { stroke: ${t.neutral}; stroke-width: 1; stroke-dasharray: 4 4; }
11199
+ .sx-seq-head rect, .sx-seq-head path { fill: ${HEAD}; stroke: ${HEAD_STROKE}; stroke-width: 1.4; }
11200
+ .sx-seq-head-name { font: 600 12.5px sans-serif; fill: ${t.text}; }
11201
+ .sx-seq-head-stereo { font: italic 10px sans-serif; fill: ${t.textMuted}; }
11202
+ .sx-seq-actor line, .sx-seq-actor circle { stroke: ${t.stroke}; stroke-width: 1.6; fill: none; stroke-linecap: round; }
11203
+ .sx-seq-actor-head { fill: ${t.bg}; }
11204
+ .sx-seq-icon { fill: ${t.bg}; stroke: ${HEAD_STROKE}; stroke-width: 1.6; }
11205
+ .sx-seq-icon-line { stroke: ${HEAD_STROKE}; stroke-width: 1.6; fill: none; stroke-linecap: round; }
11206
+ .sx-seq-icon-fill { fill: ${HEAD_STROKE}; stroke: none; }
11207
+ .sx-seq-act { fill: ${t.bg}; stroke: ${HEAD_STROKE}; stroke-width: 1.2; }
11208
+ .sx-seq-msg { stroke: ${t.stroke}; stroke-width: 1.5; fill: none; }
11209
+ .sx-seq-msg-reply { stroke: ${t.stroke}; stroke-width: 1.4; fill: none; stroke-dasharray: 6 4; }
11210
+ .sx-seq-msg-label { font: 11px sans-serif; fill: ${t.text}; }
11211
+ .sx-seq-msg-num { font: 600 10px sans-serif; fill: ${t.textMuted}; }
11212
+ .sx-seq-endpoint { fill: ${t.stroke}; }
11213
+ .sx-seq-frame { fill: none; stroke: ${t.neutral}; stroke-width: 1.2; }
11214
+ .sx-seq-frame-neg { fill: ${t.negative}; fill-opacity: 0.06; }
11215
+ .sx-seq-frame-sep { stroke: ${t.neutral}; stroke-width: 1; stroke-dasharray: 5 4; }
11216
+ .sx-seq-frame-tab { fill: ${t.fillMuted}; stroke: ${t.neutral}; stroke-width: 1.2; }
11217
+ .sx-seq-frame-op { font: 700 11px sans-serif; fill: ${t.text}; }
11218
+ .sx-seq-guard { font: italic 10.5px sans-serif; fill: ${t.textMuted}; }
11219
+ .sx-seq-ref-name { font: 600 12px sans-serif; fill: ${t.text}; }
11220
+ .sx-seq-note rect { fill: #fdf6da; stroke: #d9c97e; stroke-width: 1.1; }
11221
+ .sx-seq-note path { fill: #efe2a6; stroke: #d9c97e; stroke-width: 1.1; }
11222
+ .sx-seq-note-text { font: 11px sans-serif; fill: ${t.text}; }
11223
+ .sx-seq-div line { stroke: ${t.neutral}; stroke-width: 1; }
11224
+ .sx-seq-div rect { fill: ${t.fillMuted}; stroke: ${t.neutral}; stroke-width: 1; }
11225
+ .sx-seq-div-text { font: 600 11px sans-serif; fill: ${t.textMuted}; }
11226
+ .sx-seq-inv rect { fill: ${t.bg}; stroke: ${t.neutral}; stroke-width: 1.1; }
11227
+ .sx-seq-inv-text { font: italic 10.5px sans-serif; fill: ${t.textMuted}; }
11228
+ .sx-seq-destroy { stroke: ${t.negative}; stroke-width: 2; }
11229
+ .sx-seq-title { font: 700 16px sans-serif; fill: ${t.text}; }
11230
+ `.trim();
11231
+ }
11232
+ function markers3(t) {
11233
+ return chunk3WNW5Y7P_cjs.defs([
11234
+ chunk3WNW5Y7P_cjs.el(
11235
+ "marker",
11236
+ { id: "sx-seq-filled", viewBox: "0 0 12 10", refX: 10, refY: 5, markerWidth: 11, markerHeight: 9, orient: "auto" },
11237
+ [chunk3WNW5Y7P_cjs.el("polygon", { points: "0,0 11,5 0,10", fill: t.stroke })]
11238
+ ),
11239
+ chunk3WNW5Y7P_cjs.el(
11240
+ "marker",
11241
+ { id: "sx-seq-open", viewBox: "0 0 12 10", refX: 10, refY: 5, markerWidth: 12, markerHeight: 10, orient: "auto" },
11242
+ [chunk3WNW5Y7P_cjs.el("polyline", { points: "0,0 10,5 0,10", fill: "none", stroke: t.stroke, "stroke-width": 1.5 })]
11243
+ )
11244
+ ]);
11245
+ }
11246
+ var CHAR_W = 6;
11247
+ function renderLifeline(ll) {
11248
+ const parts = [];
11249
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: "sx-seq-axis", x1: ll.x, y1: ll.axisTop, x2: ll.x, y2: ll.axisBottom }));
11250
+ parts.push(renderHead(ll));
11251
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-lifeline", "data-id": ll.participant.id, "data-kind": ll.participant.kind }, parts);
11252
+ }
11253
+ function renderHead(ll) {
11254
+ const p = ll.participant;
11255
+ const cx = ll.x;
11256
+ const stereo = p.stereotype ? `\xAB${p.stereotype}\xBB` : null;
11257
+ if (p.kind === "actor") {
11258
+ const topY = ll.headY;
11259
+ const fig = [];
11260
+ if (stereo) {
11261
+ fig.push(chunk3WNW5Y7P_cjs.text({ class: "sx-seq-head-stereo", x: cx, y: topY - 4, "text-anchor": "middle" }, stereo));
11262
+ }
11263
+ fig.push(
11264
+ chunk3WNW5Y7P_cjs.circle({ class: "sx-seq-actor-head", cx, cy: topY + 6, r: 4.5 }),
11265
+ chunk3WNW5Y7P_cjs.line({ x1: cx, y1: topY + 10, x2: cx, y2: topY + 22 }),
11266
+ chunk3WNW5Y7P_cjs.line({ x1: cx - 8, y1: topY + 14, x2: cx + 8, y2: topY + 14 }),
11267
+ chunk3WNW5Y7P_cjs.line({ x1: cx, y1: topY + 22, x2: cx - 7, y2: topY + 32 }),
11268
+ chunk3WNW5Y7P_cjs.line({ x1: cx, y1: topY + 22, x2: cx + 7, y2: topY + 32 }),
11269
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-head-name", x: cx, y: topY + 44, "text-anchor": "middle" }, p.name)
11270
+ );
11271
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-actor sx-seq-head" }, fig);
11272
+ }
11273
+ if (p.kind === "boundary" || p.kind === "control" || p.kind === "entity") {
11274
+ return renderRobustnessIcon(ll, stereo);
11275
+ }
11276
+ if (p.kind === "database") {
11277
+ const w = ll.headW;
11278
+ const h = ll.headH;
11279
+ const x = ll.headX;
11280
+ const y = ll.headY;
11281
+ const ry = 5;
11282
+ const d = `M ${x} ${y + ry} a ${w / 2} ${ry} 0 0 1 ${w} 0 v ${h - 2 * ry} a ${w / 2} ${ry} 0 0 1 ${-w} 0 Z M ${x} ${y + ry} a ${w / 2} ${ry} 0 0 0 ${w} 0`;
11283
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-head" }, [
11284
+ chunk3WNW5Y7P_cjs.path({ d }),
11285
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-head-name", x: cx, y: y + h / 2 + 6, "text-anchor": "middle" }, p.name)
11286
+ ]);
11287
+ }
11288
+ const parts = [
11289
+ chunk3WNW5Y7P_cjs.rect({ x: ll.headX, y: ll.headY, width: ll.headW, height: ll.headH, rx: 3, ry: 3 })
11290
+ ];
11291
+ const stereoText = stereo ?? (p.kind === "collections" || p.kind === "queue" ? `\xAB${p.kind}\xBB` : null);
11292
+ if (stereoText) {
11293
+ parts.push(
11294
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-head-stereo", x: cx, y: ll.headY + 14, "text-anchor": "middle" }, stereoText)
11295
+ );
11296
+ parts.push(
11297
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-head-name", x: cx, y: ll.headY + 28, "text-anchor": "middle" }, p.name)
11298
+ );
11299
+ } else {
11300
+ parts.push(
11301
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-head-name", x: cx, y: ll.headY + ll.headH / 2 + 5, "text-anchor": "middle" }, p.name)
11302
+ );
11303
+ }
11304
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-head" }, parts);
11305
+ }
11306
+ function renderRobustnessIcon(ll, stereo) {
11307
+ const p = ll.participant;
11308
+ const cx = ll.x;
11309
+ const R = 11;
11310
+ const cy = ll.headY + 14;
11311
+ const parts = [];
11312
+ if (stereo) {
11313
+ parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-seq-head-stereo", x: cx, y: ll.headY - 2, "text-anchor": "middle" }, stereo));
11314
+ }
11315
+ parts.push(chunk3WNW5Y7P_cjs.circle({ class: "sx-seq-icon", cx, cy, r: R }));
11316
+ if (p.kind === "boundary") {
11317
+ const bx = cx - R - 9;
11318
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: "sx-seq-icon-line", x1: bx, y1: cy - R, x2: bx, y2: cy + R }));
11319
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: "sx-seq-icon-line", x1: bx, y1: cy, x2: cx - R, y2: cy }));
11320
+ } else if (p.kind === "entity") {
11321
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: "sx-seq-icon-line", x1: cx - R - 2, y1: cy + R + 1, x2: cx + R + 2, y2: cy + R + 1 }));
11322
+ } else {
11323
+ parts.push(
11324
+ chunk3WNW5Y7P_cjs.polygon({ class: "sx-seq-icon-fill", points: `${cx - 2},${cy - R - 6} ${cx + 5},${cy - R - 1} ${cx - 3},${cy - R + 2}` })
11325
+ );
11326
+ }
11327
+ parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-seq-head-name", x: cx, y: ll.headY + 44, "text-anchor": "middle" }, p.name));
11328
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-head sx-seq-icon-head", "data-icon": p.kind }, parts);
11329
+ }
11330
+ function renderActivation(a) {
11331
+ const h = Math.max(6, a.yBottom - a.yTop);
11332
+ return chunk3WNW5Y7P_cjs.rect({
11333
+ class: "sx-seq-act",
11334
+ "data-id": a.id,
11335
+ x: a.x,
11336
+ y: a.yTop,
11337
+ width: 10,
11338
+ height: h
11339
+ });
11340
+ }
11341
+ function renderMessage(m) {
11342
+ const k = m.message.arrow;
11343
+ const lineClass2 = k === "reply" ? "sx-seq-msg-reply" : "sx-seq-msg";
11344
+ const markerEnd = k === "async" || k === "reply" ? "url(#sx-seq-open)" : "url(#sx-seq-filled)";
11345
+ const parts = [];
11346
+ if (m.self) {
11347
+ const bottom = m.selfBottomY ?? m.y + 28;
11348
+ const right = m.x2;
11349
+ const d = `M ${m.x1} ${m.y} H ${right} V ${bottom} H ${m.x1}`;
11350
+ parts.push(chunk3WNW5Y7P_cjs.path({ class: lineClass2, d, "marker-end": markerEnd }));
11351
+ if (m.message.label) {
11352
+ parts.push(
11353
+ chunk3WNW5Y7P_cjs.text(
11354
+ { class: "sx-seq-msg-label", x: right + 6, y: (m.y + bottom) / 2 + 3 },
11355
+ numbered(m, m.message.label)
11356
+ )
11357
+ );
11358
+ }
11359
+ } else {
11360
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: lineClass2, x1: m.x1, y1: m.y, x2: m.x2, y2: m.y, "marker-end": markerEnd }));
11361
+ if (k === "lost") parts.push(chunk3WNW5Y7P_cjs.circle({ class: "sx-seq-endpoint", cx: m.x2, cy: m.y, r: 4 }));
11362
+ if (k === "found") parts.push(chunk3WNW5Y7P_cjs.circle({ class: "sx-seq-endpoint", cx: m.x1, cy: m.y, r: 4 }));
11363
+ if (m.message.label) {
11364
+ const mid = (m.x1 + m.x2) / 2;
11365
+ parts.push(
11366
+ chunk3WNW5Y7P_cjs.text(
11367
+ { class: "sx-seq-msg-label", x: mid, y: m.y - 6, "text-anchor": "middle" },
11368
+ numbered(m, m.message.label)
11369
+ )
11370
+ );
11371
+ }
11372
+ }
11373
+ return chunk3WNW5Y7P_cjs.group(
11374
+ {
11375
+ class: "sx-seq-message",
11376
+ "data-kind": k,
11377
+ "data-from": m.message.from,
11378
+ "data-to": m.message.to
11379
+ },
11380
+ parts
11381
+ );
11382
+ }
11383
+ function numbered(m, label) {
11384
+ return m.number !== void 0 ? `${m.number}. ${label}` : label;
11385
+ }
11386
+ function tabWidth(f) {
11387
+ return Math.max(40, f.op.length * 8 + 22);
11388
+ }
11389
+ function renderFragmentBox(f) {
11390
+ const frameClass = f.op === "neg" ? "sx-seq-frame sx-seq-frame-neg" : "sx-seq-frame";
11391
+ const parts = [
11392
+ chunk3WNW5Y7P_cjs.rect({ class: frameClass, x: f.x, y: f.y, width: f.width, height: f.height, rx: 2, ry: 2 })
11393
+ ];
11394
+ for (const op of f.operands) {
11395
+ if (op.sepY !== void 0) {
11396
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: "sx-seq-frame-sep", x1: f.x, y1: op.sepY, x2: f.x + f.width, y2: op.sepY }));
11397
+ }
11398
+ }
11399
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-fragment", "data-op": f.op }, parts);
11400
+ }
11401
+ function renderFragmentTab(f) {
11402
+ const tabW = tabWidth(f);
11403
+ const tabH = 18;
11404
+ const fold = 6;
11405
+ const tx = f.x;
11406
+ const ty = f.y;
11407
+ const tab = `M ${tx} ${ty} H ${tx + tabW} V ${ty + tabH - fold} L ${tx + tabW - fold} ${ty + tabH} H ${tx} Z`;
11408
+ const parts = [
11409
+ chunk3WNW5Y7P_cjs.path({ class: "sx-seq-frame-tab", d: tab }),
11410
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-frame-op", x: tx + 8, y: ty + 13 }, f.op)
11411
+ ];
11412
+ if (f.messageSet && f.messageSet.length) {
11413
+ parts.push(
11414
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-guard", x: tx + tabW + 8, y: ty + 13 }, `{${f.messageSet.join(", ")}}`)
11415
+ );
11416
+ }
11417
+ f.operands.forEach((op, i) => {
11418
+ if (!op.guard) return;
11419
+ const gx = i === 0 ? f.x + tabW + 8 : f.x + 8;
11420
+ const gy = i === 0 ? f.y + 13 : (op.sepY ?? f.y) + 14;
11421
+ parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-seq-guard", x: gx, y: gy }, `[${op.guard}]`));
11422
+ });
11423
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-fragment-tab-g", "data-op": f.op }, parts);
11424
+ }
11425
+ function renderRef(r) {
11426
+ const tabW = 40;
11427
+ const tabH = 18;
11428
+ const fold = 6;
11429
+ const tab = `M ${r.x} ${r.y} H ${r.x + tabW} V ${r.y + tabH - fold} L ${r.x + tabW - fold} ${r.y + tabH} H ${r.x} Z`;
11430
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-fragment", "data-op": "ref" }, [
11431
+ chunk3WNW5Y7P_cjs.rect({ class: "sx-seq-frame", x: r.x, y: r.y, width: r.width, height: r.height, rx: 2, ry: 2 }),
11432
+ chunk3WNW5Y7P_cjs.path({ class: "sx-seq-frame-tab", d: tab }),
11433
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-frame-op", x: r.x + 8, y: r.y + 13 }, "ref"),
11434
+ chunk3WNW5Y7P_cjs.text(
11435
+ { class: "sx-seq-ref-name", x: r.x + r.width / 2, y: r.y + r.height / 2 + 8, "text-anchor": "middle" },
11436
+ r.text
11437
+ )
11438
+ ]);
11439
+ }
11440
+ function renderNote2(nb) {
11441
+ const fold = 8;
11442
+ const x = nb.x;
11443
+ const y = nb.y;
11444
+ const w = nb.width;
11445
+ const h = nb.height;
11446
+ const body = `M ${x} ${y} H ${x + w - fold} L ${x + w} ${y + fold} V ${y + h} H ${x} Z`;
11447
+ const corner = `M ${x + w - fold} ${y} V ${y + fold} H ${x + w} Z`;
11448
+ const lines = nb.note.text.split(/<br\s*\/?>|\\n/);
11449
+ const startY = y + h / 2 - (lines.length - 1) * 15 / 2 + 4;
11450
+ const texts = lines.map(
11451
+ (l, i) => chunk3WNW5Y7P_cjs.text({ class: "sx-seq-note-text", x: x + w / 2, y: startY + i * 15, "text-anchor": "middle" }, l)
11452
+ );
11453
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-note" }, [chunk3WNW5Y7P_cjs.path({ d: body }), chunk3WNW5Y7P_cjs.path({ d: corner }), ...texts]);
11454
+ }
11455
+ function renderDivider(d) {
11456
+ const labelW = Math.max(60, d.text.length * CHAR_W + 24);
11457
+ const cx = d.width / 2;
11458
+ const parts = [
11459
+ chunk3WNW5Y7P_cjs.line({ x1: 8, y1: d.y, x2: cx - labelW / 2, y2: d.y }),
11460
+ chunk3WNW5Y7P_cjs.line({ x1: cx + labelW / 2, y1: d.y, x2: d.width - 8, y2: d.y }),
11461
+ chunk3WNW5Y7P_cjs.rect({ x: cx - labelW / 2, y: d.y - 11, width: labelW, height: 22, rx: 4, ry: 4 }),
11462
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-div-text", x: cx, y: d.y + 4, "text-anchor": "middle" }, d.text)
11463
+ ];
11464
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-div" }, parts);
11465
+ }
11466
+ function renderInvariant(iv) {
11467
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-inv" }, [
11468
+ chunk3WNW5Y7P_cjs.rect({ x: iv.cx - iv.width / 2, y: iv.y, width: iv.width, height: iv.height, rx: iv.height / 2, ry: iv.height / 2 }),
11469
+ chunk3WNW5Y7P_cjs.text({ class: "sx-seq-inv-text", x: iv.cx, y: iv.y + iv.height / 2 + 4, "text-anchor": "middle" }, `{${iv.text}}`)
11470
+ ]);
11471
+ }
11472
+ function renderDestroy(d) {
11473
+ const r = 7;
11474
+ return chunk3WNW5Y7P_cjs.group({ class: "sx-seq-destroy-g" }, [
11475
+ chunk3WNW5Y7P_cjs.line({ class: "sx-seq-destroy", x1: d.x - r, y1: d.y - r, x2: d.x + r, y2: d.y + r }),
11476
+ chunk3WNW5Y7P_cjs.line({ class: "sx-seq-destroy", x1: d.x - r, y1: d.y + r, x2: d.x + r, y2: d.y - r })
11477
+ ]);
11478
+ }
11479
+ function renderSequenceLayout(layout, config) {
11480
+ const t = chunkL7POWM5B_cjs.resolveBaseTheme(config?.theme ?? "default");
11481
+ const children = [];
11482
+ const nMsg = layout.messages.length;
11483
+ const nFrag = layout.fragments.length;
11484
+ children.push(chunk3WNW5Y7P_cjs.title(`Sequence Diagram${layout.title ? " \u2014 " + layout.title : ""}`));
11485
+ children.push(
11486
+ chunk3WNW5Y7P_cjs.desc(
11487
+ `${layout.lifelines.length} participants, ${nMsg} messages, ${nFrag} combined fragments.`
11488
+ )
11489
+ );
11490
+ children.push(chunk3WNW5Y7P_cjs.el("style", {}, buildCss5(t)));
11491
+ children.push(markers3(t));
11492
+ const titleBand = layout.title ? 32 : 0;
11493
+ if (layout.title) {
11494
+ children.push(
11495
+ chunk3WNW5Y7P_cjs.text({ x: layout.width / 2, y: 22, class: "sx-seq-title", "text-anchor": "middle" }, layout.title)
11496
+ );
11497
+ }
11498
+ const body = [];
11499
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-lifelines" }, layout.lifelines.map(renderLifeline)));
11500
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-frames" }, layout.fragments.map(renderFragmentBox)));
11501
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-acts" }, layout.activations.map(renderActivation)));
11502
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-frame-tabs" }, layout.fragments.map(renderFragmentTab)));
11503
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-refs" }, layout.refs.map(renderRef)));
11504
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-messages" }, layout.messages.map(renderMessage)));
11505
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-notes" }, layout.notes.map(renderNote2)));
11506
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-divs" }, layout.dividers.map(renderDivider)));
11507
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-invs" }, layout.invariants.map(renderInvariant)));
11508
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-seq-destroys" }, layout.destroys.map(renderDestroy)));
11509
+ children.push(
11510
+ titleBand ? chunk3WNW5Y7P_cjs.group({ transform: `translate(0, ${titleBand})` }, body) : chunk3WNW5Y7P_cjs.group({}, body)
11511
+ );
11512
+ const height = layout.height + titleBand;
11513
+ return chunk3WNW5Y7P_cjs.svgRoot(
11514
+ {
11515
+ class: "sx-seq",
11516
+ role: "img",
11517
+ "aria-label": chunk3WNW5Y7P_cjs.escapeXml(layout.title ?? "UML sequence diagram"),
11518
+ width: layout.width,
11519
+ height,
11520
+ viewBox: `0 0 ${layout.width} ${height}`,
11521
+ "data-diagram-type": "sequence"
11522
+ },
11523
+ children
11524
+ );
11525
+ }
11526
+ function renderSequence(textOrAst, config) {
11527
+ const ast = typeof textOrAst === "string" ? parseSequence(textOrAst) : textOrAst;
11528
+ const layout = layoutSequence(ast);
11529
+ return renderSequenceLayout(layout, config);
11530
+ }
11531
+
11532
+ // src/diagrams/sequence/index.ts
11533
+ var sequence = {
11534
+ type: "sequence",
11535
+ detect(text2) {
11536
+ for (const raw of text2.split(/\r?\n/)) {
11537
+ const t = raw.trim();
11538
+ if (!t) continue;
11539
+ if (t.startsWith("#") || t.startsWith("//")) continue;
11540
+ return /^sequence\b/i.test(t);
11541
+ }
11542
+ return false;
11543
+ },
11544
+ parse: parseSequence,
11545
+ render(text2, config) {
11546
+ return renderSequence(text2, config);
11547
+ }
11548
+ };
11549
+
11550
+ // src/diagrams/petri/parser.ts
11551
+ var PetriParseError = class extends Error {
11552
+ line;
11553
+ constructor(message, line2) {
11554
+ super(line2 !== void 0 ? `${message} (line ${line2})` : message);
11555
+ this.name = "PetriParseError";
11556
+ this.line = line2;
11557
+ }
11558
+ };
11559
+ var OPENERS = ['"', "\u201C", "\u300C", "\u300E", "\xAB"];
11560
+ var CLOSERS = ['"', "\u201D", "\u300D", "\u300F", "\xBB"];
11561
+ function tokenize3(s) {
11562
+ const out = [];
11563
+ let i = 0;
11564
+ while (i < s.length) {
11565
+ const c = s[i];
11566
+ if (c === " " || c === " ") {
11567
+ i++;
11568
+ continue;
11569
+ }
11570
+ const oi = OPENERS.indexOf(c);
11571
+ if (oi >= 0) {
11572
+ const close = CLOSERS[oi];
11573
+ let j2 = i + 1;
11574
+ let buf2 = "";
11575
+ while (j2 < s.length && s[j2] !== close && s[j2] !== '"') {
11576
+ buf2 += s[j2];
11577
+ j2++;
11578
+ }
11579
+ out.push({ text: buf2, quoted: true });
11580
+ i = j2 + 1;
11581
+ continue;
11582
+ }
11583
+ let j = i;
11584
+ let buf = "";
11585
+ while (j < s.length && s[j] !== " " && s[j] !== " ") {
11586
+ buf += s[j];
11587
+ j++;
11588
+ }
11589
+ out.push({ text: buf, quoted: false });
11590
+ i = j;
11591
+ }
11592
+ return out;
11593
+ }
11594
+ function normalizeKeyNums(line2) {
11595
+ return line2.replace(/([A-Za-z]+)\s*:\s*(-?\d+(?:\.\d+)?)/g, "$1:$2");
11596
+ }
11597
+ var DOT_RE = /^[•●・]+$/;
11598
+ var ARC_RE = /^(\S+?)\s*(->|-o|=>|--)\s*(\S+)(.*)$/;
11599
+ function toInt(raw, what, line2) {
11600
+ if (!/^-?\d+$/.test(raw)) {
11601
+ throw new PetriParseError(`${what} must be an integer, got "${raw}"`, line2);
11602
+ }
11603
+ return parseInt(raw, 10);
11604
+ }
11605
+ function parsePetri(text2) {
11606
+ const rawLines = text2.split(/\r?\n/);
11607
+ const ast = {
11608
+ type: "petri",
11609
+ direction: "lr",
11610
+ tokenStyle: "auto",
11611
+ places: [],
11612
+ transitions: [],
11613
+ arcs: [],
11614
+ fireSequence: [],
11615
+ warnings: []
11616
+ };
11617
+ const placeIds = /* @__PURE__ */ new Set();
11618
+ const transIds = /* @__PURE__ */ new Set();
11619
+ let sawHeader = false;
11620
+ for (let ln = 0; ln < rawLines.length; ln++) {
11621
+ const rawLine = rawLines[ln];
11622
+ const lineNo = ln + 1;
11623
+ const trimmed = rawLine.trim();
11624
+ if (!trimmed) continue;
11625
+ if (trimmed.startsWith("#") || trimmed.startsWith("//")) continue;
11626
+ if (!sawHeader && /^petri(net)?\b/i.test(trimmed)) {
11627
+ sawHeader = true;
11628
+ const toks = tokenize3(trimmed);
11629
+ const titleTok = toks.find((t, idx) => idx > 0 && t.quoted);
11630
+ if (titleTok) ast.title = titleTok.text;
11631
+ else if (toks[1] && !toks[1].quoted) ast.title = toks.slice(1).map((t) => t.text).join(" ");
11632
+ continue;
11633
+ }
11634
+ if (!sawHeader) {
11635
+ if (/^petri(net)?\b/i.test(trimmed)) {
11636
+ sawHeader = true;
11637
+ continue;
11638
+ }
11639
+ }
11640
+ const lower = trimmed.toLowerCase();
11641
+ if (lower.startsWith("layout:")) {
11642
+ const v = trimmed.slice(trimmed.indexOf(":") + 1).trim().toLowerCase();
11643
+ if (v === "lr" || v === "tb") ast.direction = v;
11644
+ else ast.warnings.push(`Unknown layout "${v}" (line ${lineNo}); using lr.`);
11645
+ continue;
11646
+ }
11647
+ if (lower.startsWith("tokens:")) {
11648
+ const v = trimmed.slice(trimmed.indexOf(":") + 1).trim().toLowerCase();
11649
+ if (v === "dots" || v === "count" || v === "auto") ast.tokenStyle = v;
11650
+ else ast.warnings.push(`Unknown token style "${v}" (line ${lineNo}); using auto.`);
11651
+ continue;
11652
+ }
11653
+ if (lower.startsWith("marking:")) {
11654
+ parseMarking(trimmed.slice(trimmed.indexOf(":") + 1), lineNo, ast, placeIds);
11655
+ continue;
11656
+ }
11657
+ if (lower.startsWith("fire:")) {
11658
+ const ids = trimmed.slice(trimmed.indexOf(":") + 1).split(",").map((s) => s.trim()).filter(Boolean);
11659
+ ast.fireSequence.push(...ids);
11660
+ continue;
11661
+ }
11662
+ if (/^place\b/i.test(trimmed)) {
11663
+ ast.places.push(parsePlace(trimmed, lineNo, placeIds, transIds));
11664
+ continue;
11665
+ }
11666
+ if (/^transition\b/i.test(trimmed)) {
11667
+ ast.transitions.push(parseTransition(rawLine.trim(), lineNo, placeIds, transIds));
11668
+ continue;
11669
+ }
11670
+ const arcM = ARC_RE.exec(trimmed);
11671
+ if (arcM) {
11672
+ ast.arcs.push(parseArc(arcM, lineNo, placeIds, transIds));
11673
+ continue;
11674
+ }
11675
+ ast.warnings.push(`Unrecognized line ${lineNo}: "${trimmed}"`);
11676
+ }
11677
+ for (const id of ast.fireSequence) {
11678
+ if (!transIds.has(id)) {
11679
+ throw new PetriParseError(`fire: references unknown transition "${id}"`);
11680
+ }
11681
+ }
11682
+ return ast;
11683
+ }
11684
+ function declareDup(id, lineNo, placeIds, transIds) {
11685
+ if (placeIds.has(id) || transIds.has(id)) {
11686
+ throw new PetriParseError(`duplicate node id "${id}"`, lineNo);
11687
+ }
11688
+ }
11689
+ function parsePlace(line2, lineNo, placeIds, transIds) {
11690
+ const toks = tokenize3(normalizeKeyNums(line2));
11691
+ const idTok = toks[1];
11692
+ if (!idTok || idTok.quoted) throw new PetriParseError(`place is missing an id`, lineNo);
11693
+ const id = idTok.text;
11694
+ declareDup(id, lineNo, placeIds, transIds);
11695
+ const place = { id, tokens: 0, line: lineNo };
11696
+ const labelParts = [];
11697
+ for (let i = 2; i < toks.length; i++) {
11698
+ const t = toks[i];
11699
+ if (t.quoted) {
11700
+ labelParts.push(t.text);
11701
+ continue;
11702
+ }
11703
+ const txt = t.text;
11704
+ if (/^\*\d+$/.test(txt)) {
11705
+ place.tokens = parseInt(txt.slice(1), 10);
11706
+ } else if (DOT_RE.test(txt)) {
11707
+ place.tokens = [...txt].length;
11708
+ } else if (/^tokens:/i.test(txt)) {
11709
+ place.tokens = toInt(txt.slice(txt.indexOf(":") + 1), "tokens", lineNo);
11710
+ } else if (/^capacity:/i.test(txt)) {
11711
+ const k = toInt(txt.slice(txt.indexOf(":") + 1), "capacity", lineNo);
11712
+ if (k <= 0) throw new PetriParseError(`capacity must be a positive integer`, lineNo);
11713
+ place.capacity = k;
11714
+ } else {
11715
+ labelParts.push(txt);
11716
+ }
11717
+ }
11718
+ if (place.tokens < 0) throw new PetriParseError(`token count cannot be negative`, lineNo);
11719
+ if (labelParts.length) place.label = labelParts.join(" ");
11720
+ placeIds.add(id);
11721
+ return place;
11722
+ }
11723
+ function parseTransition(line2, lineNo, placeIds, transIds) {
11724
+ let guard;
11725
+ const stripped = line2.replace(/\[([^\]]*)\]/, (_m, g) => {
11726
+ guard = String(g).trim();
11727
+ return " ";
11728
+ });
11729
+ const toks = tokenize3(normalizeKeyNums(stripped));
11730
+ const idTok = toks[1];
11731
+ if (!idTok || idTok.quoted) throw new PetriParseError(`transition is missing an id`, lineNo);
11732
+ const id = idTok.text;
11733
+ declareDup(id, lineNo, placeIds, transIds);
11734
+ const tr = { id, kind: "immediate", line: lineNo };
11735
+ if (guard) tr.guard = guard;
11736
+ const labelParts = [];
11737
+ for (let i = 2; i < toks.length; i++) {
11738
+ const t = toks[i];
11739
+ if (t.quoted) {
11740
+ labelParts.push(t.text);
11741
+ continue;
11742
+ }
11743
+ const txt = t.text;
11744
+ const low = txt.toLowerCase();
11745
+ if (low === "immediate") tr.kind = "immediate";
11746
+ else if (low === "timed") tr.kind = "timed";
11747
+ else if (/^rate:/i.test(txt)) {
11748
+ tr.rate = parseFloat(txt.slice(txt.indexOf(":") + 1));
11749
+ tr.kind = "timed";
11750
+ } else if (/^(prio|priority):/i.test(txt)) {
11751
+ tr.priority = toInt(txt.slice(txt.indexOf(":") + 1), "priority", lineNo);
11752
+ } else {
11753
+ labelParts.push(txt);
11754
+ }
11755
+ }
11756
+ if (labelParts.length) tr.label = labelParts.join(" ");
11757
+ transIds.add(id);
11758
+ return tr;
11759
+ }
11760
+ var ARROW_TYPE = {
11761
+ "->": "standard",
11762
+ "-o": "inhibitor",
11763
+ "--": "read",
11764
+ "=>": "reset"
11765
+ };
11766
+ function parseArc(m, lineNo, placeIds, transIds) {
11767
+ const from = m[1];
11768
+ const arrow2 = m[2];
11769
+ const to = m[3];
11770
+ const rest = m[4] ?? "";
11771
+ const type = ARROW_TYPE[arrow2];
11772
+ const known = (id) => placeIds.has(id) ? "place" : transIds.has(id) ? "transition" : null;
11773
+ const kf = known(from);
11774
+ const kt = known(to);
11775
+ if (!kf) throw new PetriParseError(`arc references unknown node "${from}" \u2014 declare it as a place or transition first`, lineNo);
11776
+ if (!kt) throw new PetriParseError(`arc references unknown node "${to}" \u2014 declare it as a place or transition first`, lineNo);
11777
+ if (kf === kt) {
11778
+ throw new PetriParseError(
11779
+ `arc connects two ${kf}s \u2014 a Petri net arc must go place\u2192transition or transition\u2192place`,
11780
+ lineNo
11781
+ );
11782
+ }
11783
+ if ((type === "inhibitor" || type === "reset") && kf !== "place") {
11784
+ throw new PetriParseError(`${type} arcs are place\u2192transition only`, lineNo);
11785
+ }
11786
+ const arc = { from, to, type, weight: 1, line: lineNo };
11787
+ const toks = tokenize3(normalizeKeyNums(rest));
11788
+ const labelParts = [];
11789
+ for (const t of toks) {
11790
+ if (t.quoted) {
11791
+ labelParts.push(t.text);
11792
+ continue;
11793
+ }
11794
+ const txt = t.text;
11795
+ if (/^\*\d+$/.test(txt)) arc.weight = parseInt(txt.slice(1), 10);
11796
+ else if (/^weight:/i.test(txt)) arc.weight = toInt(txt.slice(txt.indexOf(":") + 1), "weight", lineNo);
11797
+ else labelParts.push(txt);
11798
+ }
11799
+ if (arc.weight <= 0) throw new PetriParseError(`arc weight must be a positive integer`, lineNo);
11800
+ if (labelParts.length) arc.label = labelParts.join(" ");
11801
+ return arc;
11802
+ }
11803
+ function parseMarking(body, lineNo, ast, placeIds) {
11804
+ for (const part of body.split(",")) {
11805
+ const p = part.trim();
11806
+ if (!p) continue;
11807
+ const eq = p.indexOf("=");
11808
+ if (eq < 0) {
11809
+ ast.warnings.push(`Bad marking entry "${p}" (line ${lineNo}); expected id=n.`);
11810
+ continue;
11811
+ }
11812
+ const id = p.slice(0, eq).trim();
11813
+ const n = toInt(p.slice(eq + 1).trim(), "marking", lineNo);
11814
+ if (!placeIds.has(id)) {
11815
+ throw new PetriParseError(`marking references unknown place "${id}"`, lineNo);
11816
+ }
11817
+ const place = ast.places.find((pl) => pl.id === id);
11818
+ if (place) place.tokens = n;
11819
+ }
11820
+ }
11821
+
11822
+ // src/diagrams/petri/layout.ts
11823
+ var PETRI_CONST = {
11824
+ PLACE_R: 18,
11825
+ TRANS_BAR_W: 8,
11826
+ TRANS_BAR_H: 44,
11827
+ TRANS_BOX_W: 26,
11828
+ TRANS_BOX_H: 40,
11829
+ LAYER_GAP: 70,
11830
+ RANK_GAP: 46,
11831
+ TOKEN_R: 3.5,
11832
+ TOKEN_COUNT_MAX_DOTS: 4,
11833
+ ARC_WEIGHT_OFFSET: 9,
11834
+ LABEL_GAP: 6,
11835
+ MARGIN: 22,
11836
+ BACKEDGE_BOW: 30,
11837
+ LABEL_LINE_H: 13,
11838
+ CHAR_W: 6.2
11839
+ };
11840
+ function layoutPetri(ast) {
11841
+ const C2 = PETRI_CONST;
11842
+ const dir = ast.direction;
11843
+ const warnings = [...ast.warnings];
11844
+ const kindOf = /* @__PURE__ */ new Map();
11845
+ for (const p of ast.places) kindOf.set(p.id, "place");
11846
+ for (const t of ast.transitions) kindOf.set(t.id, "transition");
11847
+ const ids = [...ast.places.map((p) => p.id), ...ast.transitions.map((t) => t.id)];
11848
+ const declOrder = /* @__PURE__ */ new Map();
11849
+ ids.forEach((id, i) => declOrder.set(id, i));
11850
+ const edges = ast.arcs.map((a) => ({ from: a.from, to: a.to, reversed: false }));
11851
+ const adj = /* @__PURE__ */ new Map();
11852
+ ids.forEach((id) => adj.set(id, []));
11853
+ edges.forEach((e, i) => adj.get(e.from).push(i));
11854
+ const state2 = /* @__PURE__ */ new Map();
11855
+ ids.forEach((id) => state2.set(id, 0));
11856
+ const dfs = (u) => {
11857
+ state2.set(u, 1);
11858
+ for (const ei of adj.get(u)) {
11859
+ const e = edges[ei];
11860
+ const s = state2.get(e.to);
11861
+ if (s === 1) e.reversed = true;
11862
+ else if (s === 0) dfs(e.to);
11863
+ }
11864
+ state2.set(u, 2);
11865
+ };
11866
+ for (const id of ids) if (state2.get(id) === 0) dfs(id);
11867
+ const succ = /* @__PURE__ */ new Map();
11868
+ const indeg = /* @__PURE__ */ new Map();
11869
+ ids.forEach((id) => {
11870
+ succ.set(id, []);
11871
+ indeg.set(id, 0);
11872
+ });
11873
+ for (const e of edges) {
11874
+ const [from, to] = e.reversed ? [e.to, e.from] : [e.from, e.to];
11875
+ succ.get(from).push(to);
11876
+ indeg.set(to, indeg.get(to) + 1);
11877
+ }
11878
+ const layer = /* @__PURE__ */ new Map();
11879
+ ids.forEach((id) => layer.set(id, 0));
11880
+ const queue = ids.filter((id) => indeg.get(id) === 0);
11881
+ const indegWork = new Map(indeg);
11882
+ let qi = 0;
11883
+ const q = [...queue];
11884
+ while (qi < q.length) {
11885
+ const u = q[qi++];
11886
+ for (const v of succ.get(u)) {
11887
+ layer.set(v, Math.max(layer.get(v), layer.get(u) + 1));
11888
+ indegWork.set(v, indegWork.get(v) - 1);
11889
+ if (indegWork.get(v) === 0) q.push(v);
11890
+ }
11891
+ }
11892
+ const maxLayer = Math.max(0, ...ids.map((id) => layer.get(id)));
11893
+ const layers = Array.from({ length: maxLayer + 1 }, () => []);
11894
+ for (const id of ids) layers[layer.get(id)].push(id);
11895
+ layers.forEach((arr) => arr.sort((a, b) => declOrder.get(a) - declOrder.get(b)));
11896
+ const pos = /* @__PURE__ */ new Map();
11897
+ const reindex = () => layers.forEach((arr) => arr.forEach((id, i) => pos.set(id, i)));
11898
+ reindex();
11899
+ const preds = /* @__PURE__ */ new Map();
11900
+ const sucs = /* @__PURE__ */ new Map();
11901
+ ids.forEach((id) => {
11902
+ preds.set(id, []);
11903
+ sucs.set(id, []);
11904
+ });
11905
+ for (const e of edges) {
11906
+ const [from, to] = e.reversed ? [e.to, e.from] : [e.from, e.to];
11907
+ sucs.get(from).push(to);
11908
+ preds.get(to).push(from);
11909
+ }
11910
+ const bary = (id, neigh) => {
11911
+ const ns = neigh.get(id);
11912
+ if (!ns.length) return pos.get(id);
11913
+ return ns.reduce((s, n) => s + pos.get(n), 0) / ns.length;
11914
+ };
11915
+ for (let sweep = 0; sweep < 4; sweep++) {
11916
+ const downward = sweep % 2 === 0;
11917
+ const range = downward ? [...Array(layers.length).keys()].slice(1) : [...Array(layers.length).keys()].slice(0, -1).reverse();
11918
+ for (const L of range) {
11919
+ const neigh = downward ? preds : sucs;
11920
+ layers[L].sort((a, b) => bary(a, neigh) - bary(b, neigh) || declOrder.get(a) - declOrder.get(b));
11921
+ reindex();
11922
+ }
11923
+ }
11924
+ const sizeOf2 = (id) => {
11925
+ if (kindOf.get(id) === "place") return { halfW: C2.PLACE_R, halfH: C2.PLACE_R, r: C2.PLACE_R };
11926
+ const tr = ast.transitions.find((t) => t.id === id);
11927
+ const long = tr.kind === "timed" ? C2.TRANS_BOX_H : C2.TRANS_BAR_H;
11928
+ const thin = tr.kind === "timed" ? C2.TRANS_BOX_W : C2.TRANS_BAR_W;
11929
+ const halfW = dir === "lr" ? thin / 2 : long / 2;
11930
+ const halfH = dir === "lr" ? long / 2 : thin / 2;
11931
+ return { halfW, halfH, r: 0 };
11932
+ };
11933
+ const flowHalf = (id) => {
11934
+ const s = sizeOf2(id);
11935
+ return dir === "lr" ? s.halfW : s.halfH;
11936
+ };
11937
+ const crossHalf = (id) => {
11938
+ const s = sizeOf2(id);
11939
+ return dir === "lr" ? s.halfH : s.halfW;
11940
+ };
11941
+ const layerHalf = layers.map((arr) => Math.max(0, ...arr.map(flowHalf)));
11942
+ const slot = Math.max(0, ...ids.map(crossHalf)) * 2 + C2.RANK_GAP;
11943
+ const maxCount = Math.max(1, ...layers.map((a) => a.length));
11944
+ const crossCenter = C2.MARGIN + C2.LABEL_LINE_H * 2 + maxCount * slot / 2;
11945
+ const flowCenter = [];
11946
+ let acc = C2.MARGIN + C2.LABEL_LINE_H * 2;
11947
+ for (let L = 0; L < layers.length; L++) {
11948
+ acc += layerHalf[L];
11949
+ flowCenter[L] = acc;
11950
+ acc += layerHalf[L] + C2.LAYER_GAP;
11951
+ }
11952
+ const geom = /* @__PURE__ */ new Map();
11953
+ layers.forEach((arr, L) => {
11954
+ const n = arr.length;
11955
+ const total = (n - 1) * slot;
11956
+ arr.forEach((id, i) => {
11957
+ const cross = crossCenter - total / 2 + i * slot;
11958
+ const flow = flowCenter[L];
11959
+ const cx = dir === "lr" ? flow : cross;
11960
+ const cy = dir === "lr" ? cross : flow;
11961
+ const s = sizeOf2(id);
11962
+ geom.set(id, { id, kind: kindOf.get(id), cx, cy, halfW: s.halfW, halfH: s.halfH, r: s.r, layer: L });
11963
+ });
11964
+ });
11965
+ const marking = /* @__PURE__ */ new Map();
11966
+ for (const p of ast.places) marking.set(p.id, p.tokens);
11967
+ const inArcs = (tid) => ast.arcs.filter((a) => a.to === tid);
11968
+ const outArcs = (tid) => ast.arcs.filter((a) => a.from === tid);
11969
+ const capOf = (pid2) => ast.places.find((p) => p.id === pid2)?.capacity;
11970
+ const isEnabled = (tid, M) => {
11971
+ for (const a of inArcs(tid)) {
11972
+ const have = M.get(a.from) ?? 0;
11973
+ if (a.type === "standard" || a.type === "read") {
11974
+ if (have < a.weight) return false;
11975
+ } else if (a.type === "inhibitor") {
11976
+ if (have >= a.weight) return false;
11977
+ }
11978
+ }
11979
+ for (const a of outArcs(tid)) {
11980
+ const cap = capOf(a.to);
11981
+ if (cap !== void 0 && (M.get(a.to) ?? 0) + a.weight > cap) return false;
11982
+ }
11983
+ return true;
11984
+ };
11985
+ const applyFire = (tid, M) => {
11986
+ for (const a of inArcs(tid)) {
11987
+ if (a.type === "standard") M.set(a.from, (M.get(a.from) ?? 0) - a.weight);
11988
+ else if (a.type === "reset") M.set(a.from, 0);
11989
+ }
11990
+ for (const a of outArcs(tid)) M.set(a.to, (M.get(a.to) ?? 0) + a.weight);
11991
+ };
11992
+ ast.fireSequence.forEach((tid, i) => {
11993
+ if (isEnabled(tid, marking)) applyFire(tid, marking);
11994
+ else warnings.push(`fire step ${i + 1}: transition "${tid}" is not enabled in the current marking; skipped.`);
11995
+ });
11996
+ const producers = (pid2) => ast.arcs.filter((a) => a.to === pid2 && a.type === "standard").length;
11997
+ const enabledIds = [];
11998
+ const deadIds = /* @__PURE__ */ new Set();
11999
+ for (const tr of ast.transitions) {
12000
+ if (isEnabled(tr.id, marking)) {
12001
+ enabledIds.push(tr.id);
12002
+ continue;
12003
+ }
12004
+ const dead = inArcs(tr.id).some(
12005
+ (a) => (a.type === "standard" || a.type === "read") && (marking.get(a.from) ?? 0) < a.weight && producers(a.from) === 0
12006
+ );
12007
+ if (dead) deadIds.add(tr.id);
12008
+ }
12009
+ const boundary = (g, dx2, dy2) => {
12010
+ const len = Math.hypot(dx2, dy2) || 1;
12011
+ const ux = dx2 / len;
12012
+ const uy = dy2 / len;
12013
+ if (g.kind === "place") return { x: g.cx + ux * g.r, y: g.cy + uy * g.r };
12014
+ const tx = ux !== 0 ? g.halfW / Math.abs(ux) : Infinity;
12015
+ const ty = uy !== 0 ? g.halfH / Math.abs(uy) : Infinity;
12016
+ const t = Math.min(tx, ty);
12017
+ return { x: g.cx + ux * t, y: g.cy + uy * t };
12018
+ };
12019
+ const bandMaxCross = Math.max(...[...geom.values()].map((g) => dir === "lr" ? g.cy + g.halfH : g.cx + g.halfW));
12020
+ const arcGeoms = ast.arcs.map((a, i) => {
12021
+ const A = geom.get(a.from);
12022
+ const B = geom.get(a.to);
12023
+ const reversed = edges[i].reversed;
12024
+ let points;
12025
+ if (!reversed) {
12026
+ const pA = boundary(A, B.cx - A.cx, B.cy - A.cy);
12027
+ const pB = boundary(B, A.cx - B.cx, A.cy - B.cy);
12028
+ points = [pA, pB];
12029
+ } else {
12030
+ if (dir === "lr") {
12031
+ const pA = boundary(A, 0, 1);
12032
+ const pB = boundary(B, 0, 1);
12033
+ const bowY = bandMaxCross + C2.BACKEDGE_BOW;
12034
+ points = [pA, { x: pA.x, y: bowY }, { x: pB.x, y: bowY }, pB];
12035
+ } else {
12036
+ const pA = boundary(A, 1, 0);
12037
+ const pB = boundary(B, 1, 0);
12038
+ const bowX = bandMaxCross + C2.BACKEDGE_BOW;
12039
+ points = [pA, { x: bowX, y: pA.y }, { x: bowX, y: pB.y }, pB];
12040
+ }
12041
+ }
12042
+ const p0 = points[0];
12043
+ const p1 = points[points.length - 1];
12044
+ const mx = (p0.x + p1.x) / 2;
12045
+ const my = (p0.y + p1.y) / 2;
12046
+ const ddx = p1.x - p0.x;
12047
+ const ddy = p1.y - p0.y;
12048
+ const dl = Math.hypot(ddx, ddy) || 1;
12049
+ const labelX = mx - ddy / dl * C2.ARC_WEIGHT_OFFSET;
12050
+ const labelY = my + ddx / dl * C2.ARC_WEIGHT_OFFSET;
12051
+ return { arc: a, type: a.type, weight: a.weight, points, reversed, labelX, labelY };
12052
+ });
12053
+ const hasIncoming = (pid2) => ast.arcs.some((a) => a.to === pid2);
12054
+ const hasOutgoing = (pid2) => ast.arcs.some((a) => a.from === pid2);
12055
+ const placeBoxes = ast.places.map((p) => {
12056
+ const g = geom.get(p.id);
12057
+ return {
12058
+ place: p,
12059
+ cx: g.cx,
12060
+ cy: g.cy,
12061
+ r: g.r,
12062
+ tokens: marking.get(p.id) ?? 0,
12063
+ isSource: !hasIncoming(p.id),
12064
+ isSink: !hasOutgoing(p.id)
12065
+ };
12066
+ });
12067
+ const transBoxes = ast.transitions.map((t) => {
12068
+ const g = geom.get(t.id);
12069
+ return {
12070
+ transition: t,
12071
+ cx: g.cx,
12072
+ cy: g.cy,
12073
+ w: g.halfW * 2,
12074
+ h: g.halfH * 2,
12075
+ enabled: enabledIds.includes(t.id),
12076
+ dead: deadIds.has(t.id)
12077
+ };
12078
+ });
12079
+ const bb = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity };
12080
+ const addBox = (x0, y0, x1, y1) => {
12081
+ bb.minX = Math.min(bb.minX, x0, x1);
12082
+ bb.minY = Math.min(bb.minY, y0, y1);
12083
+ bb.maxX = Math.max(bb.maxX, x0, x1);
12084
+ bb.maxY = Math.max(bb.maxY, y0, y1);
12085
+ };
12086
+ const addPt = (p) => addBox(p.x, p.y, p.x, p.y);
12087
+ const labelW = (s) => s ? s.length * C2.CHAR_W : 0;
12088
+ for (const pb of placeBoxes) {
12089
+ addBox(pb.cx - pb.r, pb.cy - pb.r, pb.cx + pb.r, pb.cy + pb.r);
12090
+ const lw = Math.max(labelW(pb.place.id), labelW(pb.place.label)) / 2;
12091
+ addBox(pb.cx - lw, pb.cy - pb.r - C2.LABEL_LINE_H * 2, pb.cx + lw, pb.cy);
12092
+ if (pb.place.capacity !== void 0) addBox(pb.cx, pb.cy + pb.r, pb.cx + 24, pb.cy + pb.r + C2.LABEL_LINE_H);
12093
+ }
12094
+ for (const tb of transBoxes) {
12095
+ addBox(tb.cx - tb.w / 2, tb.cy - tb.h / 2, tb.cx + tb.w / 2, tb.cy + tb.h / 2);
12096
+ const lw = Math.max(labelW(tb.transition.id), labelW(tb.transition.label)) / 2;
12097
+ addBox(tb.cx - lw, tb.cy - tb.h / 2 - C2.LABEL_LINE_H * 2, tb.cx + lw, tb.cy);
12098
+ }
12099
+ for (const ag of arcGeoms) {
12100
+ ag.points.forEach(addPt);
12101
+ if (ag.weight > 1) addBox(ag.labelX - 6, ag.labelY - 8, ag.labelX + 6, ag.labelY + 4);
12102
+ }
12103
+ const dx = C2.MARGIN - bb.minX;
12104
+ const dy = C2.MARGIN - bb.minY;
12105
+ const shift = (p) => ({ x: p.x + dx, y: p.y + dy });
12106
+ placeBoxes.forEach((pb) => {
12107
+ pb.cx += dx;
12108
+ pb.cy += dy;
12109
+ });
12110
+ transBoxes.forEach((tb) => {
12111
+ tb.cx += dx;
12112
+ tb.cy += dy;
12113
+ });
12114
+ arcGeoms.forEach((ag) => {
12115
+ ag.points = ag.points.map(shift);
12116
+ ag.labelX += dx;
12117
+ ag.labelY += dy;
12118
+ });
12119
+ const width = bb.maxX - bb.minX + 2 * C2.MARGIN;
12120
+ const height = bb.maxY - bb.minY + 2 * C2.MARGIN;
12121
+ const subclass = detectSubclass(ast);
12122
+ return {
12123
+ width: Math.round(width),
12124
+ height: Math.round(height),
12125
+ title: ast.title,
12126
+ direction: dir,
12127
+ places: placeBoxes,
12128
+ transitions: transBoxes,
12129
+ arcs: arcGeoms,
12130
+ subclass,
12131
+ enabledIds,
12132
+ warnings,
12133
+ ast
12134
+ };
12135
+ }
12136
+ function detectSubclass(ast) {
12137
+ if (!ast.transitions.length || !ast.places.length) return void 0;
12138
+ const inT = (tid) => ast.arcs.filter((a) => a.to === tid).length;
12139
+ const outT = (tid) => ast.arcs.filter((a) => a.from === tid).length;
12140
+ const inP = (pid2) => ast.arcs.filter((a) => a.to === pid2).length;
12141
+ const outP = (pid2) => ast.arcs.filter((a) => a.from === pid2).length;
12142
+ const stateMachine = ast.transitions.every((t) => inT(t.id) === 1 && outT(t.id) === 1);
12143
+ const markedGraph = ast.places.every((p) => inP(p.id) === 1 && outP(p.id) === 1);
12144
+ const sources = ast.places.filter((p) => inP(p.id) === 0);
12145
+ const sinks = ast.places.filter((p) => outP(p.id) === 0);
12146
+ const workflowNet = sources.length === 1 && sinks.length === 1;
12147
+ const tags = [];
12148
+ if (stateMachine) tags.push("state machine");
12149
+ if (markedGraph) tags.push("marked graph");
12150
+ if (workflowNet) tags.push("workflow net");
12151
+ return tags.length ? tags.join(", ") : void 0;
12152
+ }
12153
+
12154
+ // src/diagrams/petri/renderer.ts
12155
+ function buildCss6(t) {
12156
+ return `
12157
+ .sx-petri { font-family: system-ui, -apple-system, sans-serif; }
12158
+ .sx-petri-place { fill: ${t.placeFill}; stroke: ${t.placeStroke}; stroke-width: 2; }
12159
+ .sx-petri-place-cap { stroke-dasharray: 4 3; }
12160
+ .sx-petri-token { fill: ${t.tokenFill}; stroke: none; }
12161
+ .sx-petri-token-num { font: 600 11px sans-serif; fill: ${t.tokenFill}; }
12162
+ .sx-petri-bar { fill: ${t.transitionBarFill}; stroke: ${t.transitionStroke}; stroke-width: 2; }
12163
+ .sx-petri-box { fill: ${t.transitionBoxFill}; stroke: ${t.transitionStroke}; stroke-width: 2; }
12164
+ .sx-petri-enabled { fill: ${t.enabledFill}; stroke: ${t.enabledStroke}; stroke-width: 3; }
12165
+ .sx-petri-dead .sx-petri-bar, .sx-petri-dead .sx-petri-box { stroke: ${t.deadStroke}; }
12166
+ .sx-petri-dead { opacity: 0.7; }
12167
+ .sx-petri-label { font: 12px sans-serif; fill: ${t.text}; }
12168
+ .sx-petri-sublabel { font: italic 10px sans-serif; fill: ${t.textMuted}; }
12169
+ .sx-petri-cap { font: 9px sans-serif; fill: ${t.textMuted}; }
12170
+ .sx-petri-rate { font: italic 9px sans-serif; fill: ${t.textMuted}; }
12171
+ .sx-petri-arc { stroke: ${t.arcStroke}; stroke-width: 2; fill: none; }
12172
+ .sx-petri-arc-inhibitor, .sx-petri-arc-reset { stroke: ${t.inhibitorStroke}; }
12173
+ .sx-petri-inhibitor-dot { fill: ${t.placeFill}; stroke: ${t.inhibitorStroke}; stroke-width: 2; }
12174
+ .sx-petri-weight { font: 600 9px sans-serif; fill: ${t.weightLabel}; }
12175
+ .sx-petri-title { font: 700 16px sans-serif; fill: ${t.text}; }
12176
+ `.trim();
12177
+ }
12178
+ function markers4(t) {
12179
+ return chunk3WNW5Y7P_cjs.defs([
12180
+ chunk3WNW5Y7P_cjs.el(
12181
+ "marker",
12182
+ { id: "sx-petri-head", viewBox: "0 0 10 10", refX: 9, refY: 5, markerWidth: 8, markerHeight: 8, orient: "auto-start-reverse" },
12183
+ [chunk3WNW5Y7P_cjs.polygon({ points: "0,0 9,5 0,10", fill: t.arcStroke })]
12184
+ ),
12185
+ chunk3WNW5Y7P_cjs.el(
12186
+ "marker",
12187
+ { id: "sx-petri-head-reset", viewBox: "0 0 16 10", refX: 15, refY: 5, markerWidth: 13, markerHeight: 9, orient: "auto-start-reverse" },
12188
+ [
12189
+ chunk3WNW5Y7P_cjs.polygon({ points: "0,0 7,5 0,10", fill: t.inhibitorStroke }),
12190
+ chunk3WNW5Y7P_cjs.polygon({ points: "7,0 14,5 7,10", fill: t.inhibitorStroke })
12191
+ ]
12192
+ )
12193
+ ]);
12194
+ }
12195
+ function tokenPositions(n) {
12196
+ const d = PETRI_CONST.TOKEN_R + 2.2;
12197
+ switch (n) {
12198
+ case 1:
12199
+ return [{ x: 0, y: 0 }];
12200
+ case 2:
12201
+ return [
12202
+ { x: -d, y: 0 },
12203
+ { x: d, y: 0 }
12204
+ ];
12205
+ case 3:
12206
+ return [
12207
+ { x: 0, y: -d },
12208
+ { x: -d, y: d },
12209
+ { x: d, y: d }
12210
+ ];
12211
+ default:
12212
+ return [
12213
+ { x: -d, y: -d },
12214
+ { x: d, y: -d },
12215
+ { x: -d, y: d },
12216
+ { x: d, y: d }
12217
+ ];
12218
+ }
12219
+ }
12220
+ function renderTokens(pb, style) {
12221
+ const n = pb.tokens;
12222
+ if (n <= 0) return "";
12223
+ const asDots = style !== "count" && n <= PETRI_CONST.TOKEN_COUNT_MAX_DOTS;
12224
+ if (asDots) {
12225
+ return tokenPositions(n).map((p) => chunk3WNW5Y7P_cjs.circle({ class: "sx-petri-token", cx: pb.cx + p.x, cy: pb.cy + p.y, r: PETRI_CONST.TOKEN_R })).join("");
12226
+ }
12227
+ return chunk3WNW5Y7P_cjs.text(
12228
+ { class: "sx-petri-token-num", x: pb.cx, y: pb.cy + 4, "text-anchor": "middle" },
12229
+ String(n)
12230
+ );
12231
+ }
12232
+ function renderPlace(pb, style) {
12233
+ const cls = pb.place.capacity !== void 0 ? "sx-petri-place sx-petri-place-cap" : "sx-petri-place";
12234
+ const parts = [chunk3WNW5Y7P_cjs.circle({ class: cls, cx: pb.cx, cy: pb.cy, r: pb.r })];
12235
+ parts.push(renderTokens(pb, style));
12236
+ const labelY = pb.cy - pb.r - PETRI_CONST.LABEL_GAP;
12237
+ parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-petri-label", x: pb.cx, y: labelY, "text-anchor": "middle" }, pb.place.id));
12238
+ if (pb.place.label) {
12239
+ parts.push(
12240
+ chunk3WNW5Y7P_cjs.text({ class: "sx-petri-sublabel", x: pb.cx, y: labelY - PETRI_CONST.LABEL_LINE_H, "text-anchor": "middle" }, pb.place.label)
12241
+ );
12242
+ }
12243
+ if (pb.place.capacity !== void 0) {
12244
+ parts.push(
12245
+ chunk3WNW5Y7P_cjs.text({ class: "sx-petri-cap", x: pb.cx + pb.r + 3, y: pb.cy + pb.r + 9 }, `K=${pb.place.capacity}`)
12246
+ );
12247
+ }
12248
+ const attrs = {
12249
+ class: "sx-petri-place-g",
12250
+ "data-id": pb.place.id,
12251
+ "data-tokens": pb.tokens
12252
+ };
12253
+ if (pb.place.capacity !== void 0) attrs["data-capacity"] = pb.place.capacity;
12254
+ if (pb.isSource) attrs["data-source"] = "true";
12255
+ if (pb.isSink) attrs["data-sink"] = "true";
12256
+ return chunk3WNW5Y7P_cjs.group(attrs, parts);
12257
+ }
12258
+ function renderTransition(tb) {
12259
+ const x = tb.cx - tb.w / 2;
12260
+ const y = tb.cy - tb.h / 2;
12261
+ const parts = [];
12262
+ if (tb.enabled) {
12263
+ parts.push(
12264
+ chunk3WNW5Y7P_cjs.rect({ class: "sx-petri-enabled", x: x - 4, y: y - 4, width: tb.w + 8, height: tb.h + 8, rx: 4, ry: 4 })
12265
+ );
12266
+ }
12267
+ const shapeClass = tb.transition.kind === "timed" ? "sx-petri-box" : "sx-petri-bar";
12268
+ parts.push(chunk3WNW5Y7P_cjs.rect({ class: shapeClass, x, y, width: tb.w, height: tb.h, rx: 1, ry: 1 }));
12269
+ const labelY = y - PETRI_CONST.LABEL_GAP;
12270
+ parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-petri-label", x: tb.cx, y: labelY, "text-anchor": "middle" }, tb.transition.id));
12271
+ if (tb.transition.label) {
12272
+ parts.push(
12273
+ chunk3WNW5Y7P_cjs.text({ class: "sx-petri-sublabel", x: tb.cx, y: labelY - PETRI_CONST.LABEL_LINE_H, "text-anchor": "middle" }, tb.transition.label)
12274
+ );
12275
+ }
12276
+ if (tb.transition.kind === "timed" && tb.transition.rate !== void 0) {
12277
+ parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-petri-rate", x: tb.cx, y: y + tb.h + 10, "text-anchor": "middle" }, `\u03BB=${tb.transition.rate}`));
12278
+ }
12279
+ if (tb.transition.guard) {
12280
+ parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-petri-rate", x: tb.cx + tb.w / 2 + 4, y: tb.cy + 3 }, `[${tb.transition.guard}]`));
12281
+ }
12282
+ return chunk3WNW5Y7P_cjs.group(
12283
+ {
12284
+ class: tb.dead ? "sx-petri-trans-g sx-petri-dead" : "sx-petri-trans-g",
12285
+ "data-id": tb.transition.id,
12286
+ "data-kind": tb.transition.kind,
12287
+ "data-enabled": tb.enabled ? "true" : "false"
12288
+ },
12289
+ parts
12290
+ );
12291
+ }
12292
+ function arcPath(ag) {
12293
+ const p = ag.points;
12294
+ if (p.length === 4) {
12295
+ return `M ${p[0].x} ${p[0].y} C ${p[1].x} ${p[1].y} ${p[2].x} ${p[2].y} ${p[3].x} ${p[3].y}`;
12296
+ }
12297
+ return `M ${p[0].x} ${p[0].y} L ${p[p.length - 1].x} ${p[p.length - 1].y}`;
12298
+ }
12299
+ function renderArc(ag) {
12300
+ const parts = [];
12301
+ let cls = "sx-petri-arc";
12302
+ let markerEnd;
12303
+ if (ag.type === "standard") {
12304
+ markerEnd = "url(#sx-petri-head)";
12305
+ } else if (ag.type === "reset") {
12306
+ cls += " sx-petri-arc-reset";
12307
+ markerEnd = "url(#sx-petri-head-reset)";
12308
+ } else if (ag.type === "inhibitor") {
12309
+ cls += " sx-petri-arc-inhibitor";
12310
+ }
12311
+ parts.push(chunk3WNW5Y7P_cjs.path({ class: cls, d: arcPath(ag), ...markerEnd ? { "marker-end": markerEnd } : {} }));
12312
+ if (ag.type === "inhibitor") {
12313
+ const pts = ag.points;
12314
+ const last = pts[pts.length - 1];
12315
+ const prev = pts[pts.length - 2];
12316
+ const dx = last.x - prev.x;
12317
+ const dy = last.y - prev.y;
12318
+ const len = Math.hypot(dx, dy) || 1;
12319
+ const r = 4;
12320
+ const cx = last.x - dx / len * r;
12321
+ const cy = last.y - dy / len * r;
12322
+ parts.push(chunk3WNW5Y7P_cjs.circle({ class: "sx-petri-inhibitor-dot", cx, cy, r }));
12323
+ }
12324
+ if (ag.weight > 1) {
12325
+ parts.push(
12326
+ chunk3WNW5Y7P_cjs.text({ class: "sx-petri-weight", x: ag.labelX, y: ag.labelY + 3, "text-anchor": "middle" }, String(ag.weight))
12327
+ );
12328
+ }
12329
+ return chunk3WNW5Y7P_cjs.group(
12330
+ { class: "sx-petri-arc-g", "data-from": ag.arc.from, "data-to": ag.arc.to, "data-type": ag.type, "data-weight": ag.weight },
12331
+ parts
12332
+ );
12333
+ }
12334
+ function renderPetriLayout(layout, config) {
12335
+ const t = chunkL7POWM5B_cjs.resolvePetriTheme(config?.theme ?? "default");
12336
+ const children = [];
12337
+ const marking = layout.places.filter((p) => p.tokens > 0).map((p) => `${p.place.id}:${p.tokens}`).join(", ");
12338
+ const descParts = [
12339
+ `${layout.places.length} places, ${layout.transitions.length} transitions, ${layout.arcs.length} arcs.`,
12340
+ marking ? `Marking {${marking}}.` : "Empty marking.",
12341
+ layout.enabledIds.length ? `Enabled: ${layout.enabledIds.join(", ")}.` : "No enabled transitions.",
12342
+ layout.subclass ? `Class: ${layout.subclass}.` : ""
12343
+ ].filter(Boolean);
12344
+ children.push(chunk3WNW5Y7P_cjs.title(`Petri net${layout.title ? " \u2014 " + layout.title : ""}`));
12345
+ children.push(chunk3WNW5Y7P_cjs.desc(descParts.join(" ")));
12346
+ children.push(chunk3WNW5Y7P_cjs.el("style", {}, buildCss6(t)));
12347
+ children.push(markers4(t));
12348
+ const titleBand = layout.title ? 32 : 0;
12349
+ if (layout.title) {
12350
+ children.push(chunk3WNW5Y7P_cjs.text({ x: layout.width / 2, y: 22, class: "sx-petri-title", "text-anchor": "middle" }, layout.title));
12351
+ }
12352
+ const body = [];
12353
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-petri-arcs" }, layout.arcs.map(renderArc)));
12354
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-petri-places" }, layout.places.map((p) => renderPlace(p, layout.ast.tokenStyle))));
12355
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-petri-transitions" }, layout.transitions.map(renderTransition)));
12356
+ children.push(titleBand ? chunk3WNW5Y7P_cjs.group({ transform: `translate(0, ${titleBand})` }, body) : chunk3WNW5Y7P_cjs.group({}, body));
12357
+ const height = layout.height + titleBand;
12358
+ return chunk3WNW5Y7P_cjs.svgRoot(
12359
+ {
12360
+ class: "sx-petri",
12361
+ role: "img",
12362
+ "aria-label": chunk3WNW5Y7P_cjs.escapeXml(layout.title ?? "Petri net"),
12363
+ width: layout.width,
12364
+ height,
12365
+ viewBox: `0 0 ${layout.width} ${height}`,
12366
+ "data-diagram-type": "petri"
12367
+ },
12368
+ children
12369
+ );
12370
+ }
12371
+ function renderPetri(textOrAst, config) {
12372
+ const ast = typeof textOrAst === "string" ? parsePetri(textOrAst) : textOrAst;
12373
+ const layout = layoutPetri(ast);
12374
+ return renderPetriLayout(layout, config);
12375
+ }
12376
+
12377
+ // src/diagrams/petri/index.ts
12378
+ var petri = {
12379
+ type: "petri",
12380
+ detect(text2) {
12381
+ for (const raw of text2.split(/\r?\n/)) {
12382
+ const t = raw.trim();
12383
+ if (!t) continue;
12384
+ if (t.startsWith("#") || t.startsWith("//")) continue;
12385
+ return /^petri(net)?\b/i.test(t);
12386
+ }
12387
+ return false;
12388
+ },
12389
+ parse: parsePetri,
12390
+ render(text2, config) {
12391
+ return renderPetri(text2, config);
12392
+ }
12393
+ };
12394
+
12395
+ // src/diagrams/network/parser.ts
12396
+ var NetworkParseError = class extends Error {
12397
+ name = "NetworkParseError";
12398
+ };
12399
+ var DEVICE_KINDS = /* @__PURE__ */ new Set([
12400
+ "router",
12401
+ "switch",
12402
+ "l3switch",
12403
+ "firewall",
12404
+ "loadbalancer",
12405
+ "ap",
12406
+ "wlc",
12407
+ "gateway",
12408
+ "modem",
12409
+ "ids",
12410
+ "proxy",
12411
+ "vpngw",
12412
+ "server",
12413
+ "serverfarm",
12414
+ "pc",
12415
+ "laptop",
12416
+ "mobile",
12417
+ "ipphone",
12418
+ "printer",
12419
+ "storage",
12420
+ "camera",
12421
+ "nvr",
12422
+ "dvr",
12423
+ "poeswitch",
12424
+ "encoder",
12425
+ "monitor",
12426
+ "internet",
12427
+ "wan",
12428
+ "cloud",
12429
+ "pstn",
12430
+ "lan"
12431
+ ]);
12432
+ var KIND_ALIASES = {
12433
+ multilayer: "l3switch",
12434
+ wifi: "ap",
12435
+ workstation: "pc",
12436
+ phone: "mobile",
12437
+ voip: "ipphone",
12438
+ nas: "storage",
12439
+ san: "storage",
12440
+ servers: "serverfarm",
12441
+ ips: "ids",
12442
+ decoder: "encoder",
12443
+ videowall: "monitor",
12444
+ segment: "lan"
12445
+ };
12446
+ var GROUP_KINDS = /* @__PURE__ */ new Set(["site", "building", "campus", "rack", "subnet", "vlan", "zone", "dmz"]);
12447
+ var GROUP_KIND_ALIAS = { building: "site", campus: "site" };
12448
+ var TIERS = /* @__PURE__ */ new Set(["edge", "core", "distribution", "access"]);
12449
+ var CAMERA_TYPES = /* @__PURE__ */ new Set(["fixed", "bullet", "dome", "ptz", "turret"]);
12450
+ var LAYOUT_MODES = /* @__PURE__ */ new Set([
12451
+ "tiered",
12452
+ "tree",
12453
+ "star",
12454
+ "ring",
12455
+ "bus",
12456
+ "mesh",
12457
+ "spine-leaf",
12458
+ "manual"
12459
+ ]);
12460
+ var LINK_TYPE_KEYWORDS = {
12461
+ copper: "copper",
12462
+ ethernet: "copper",
12463
+ fiber: "fiber",
12464
+ fibre: "fiber",
12465
+ wireless: "wireless",
12466
+ wifi: "wireless",
12467
+ serial: "serial",
12468
+ wan: "serial",
12469
+ poe: "poe",
12470
+ vpn: "vpn",
12471
+ lag: "lag",
12472
+ portchannel: "lag"
12473
+ };
12474
+ var CONNECTORS = /* @__PURE__ */ new Set(["--", "->", "=="]);
12475
+ var SPEED_RE = /^\d+(?:\.\d+)?[MGT]$/;
12476
+ var QUOTE_RE = /"([^"]*)"|「([^」]*)」|『([^』]*)』|“([^”]*)”|«([^»]*)»|'([^']*)'/g;
12477
+ function stripComment2(line2) {
12478
+ let inQuote = false;
12479
+ let close = "";
12480
+ const pairs = { '"': '"', "\u300C": "\u300D", "\u300E": "\u300F", "\u201C": "\u201D", "\xAB": "\xBB", "'": "'" };
12481
+ for (let i = 0; i < line2.length; i++) {
12482
+ const ch = line2[i];
12483
+ if (inQuote) {
12484
+ if (ch === close) inQuote = false;
12485
+ continue;
12486
+ }
12487
+ if (pairs[ch]) {
12488
+ inQuote = true;
12489
+ close = pairs[ch];
12490
+ continue;
12491
+ }
12492
+ if (ch === "#") return line2.slice(0, i);
12493
+ if (ch === "/" && line2[i + 1] === "/") return line2.slice(0, i);
12494
+ }
12495
+ return line2;
12496
+ }
12497
+ function splitStatements(line2) {
12498
+ const out = [];
12499
+ let buf = "";
12500
+ let inQuote = false;
12501
+ let close = "";
12502
+ const pairs = { '"': '"', "\u300C": "\u300D", "\u300E": "\u300F", "\u201C": "\u201D", "\xAB": "\xBB", "'": "'" };
12503
+ for (let i = 0; i < line2.length; i++) {
12504
+ const ch = line2[i];
12505
+ if (inQuote) {
12506
+ if (ch === close) inQuote = false;
12507
+ buf += ch;
12508
+ continue;
12509
+ }
12510
+ if (pairs[ch]) {
12511
+ inQuote = true;
12512
+ close = pairs[ch];
12513
+ buf += ch;
12514
+ continue;
12515
+ }
12516
+ if (ch === ";") {
12517
+ out.push(buf);
12518
+ buf = "";
12519
+ continue;
12520
+ }
12521
+ buf += ch;
12522
+ }
12523
+ out.push(buf);
12524
+ return out;
12525
+ }
12526
+ function tokenize4(raw) {
12527
+ const line2 = stripComment2(raw);
12528
+ const strings = [];
12529
+ const masked = line2.replace(QUOTE_RE, (...m) => {
12530
+ const inner = m.slice(1, 7).find((g) => g !== void 0) ?? "";
12531
+ strings.push(inner);
12532
+ return ` @@${strings.length - 1}@@ `;
12533
+ });
12534
+ const spaced = masked.replace(/(--|->|==)/g, " $1 ");
12535
+ const out = [];
12536
+ for (const w of spaced.split(/\s+/)) {
12537
+ if (!w) continue;
12538
+ const sm = /^@@(\d+)@@$/.exec(w);
12539
+ if (sm) {
12540
+ out.push({ value: strings[Number(sm[1])] ?? "", str: true });
12541
+ } else {
12542
+ out.push({ value: w, str: false });
12543
+ }
12544
+ }
12545
+ return out;
12546
+ }
12547
+ function levenshtein(a, b) {
12548
+ const m = a.length, n = b.length;
12549
+ const d = Array.from({ length: m + 1 }, (_, i) => [i, ...Array(n).fill(0)]);
12550
+ for (let j = 0; j <= n; j++) d[0][j] = j;
12551
+ for (let i = 1; i <= m; i++) {
12552
+ for (let j = 1; j <= n; j++) {
12553
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
12554
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
12555
+ }
12556
+ }
12557
+ return d[m][n];
12558
+ }
12559
+ function nearestKind(bad) {
12560
+ let best;
12561
+ let bestDist = Infinity;
12562
+ for (const k of [...DEVICE_KINDS, ...Object.keys(KIND_ALIASES)]) {
12563
+ const dist = levenshtein(bad.toLowerCase(), k);
12564
+ if (dist < bestDist) {
12565
+ bestDist = dist;
12566
+ best = k;
12567
+ }
12568
+ }
12569
+ return bestDist <= 3 ? best : void 0;
12570
+ }
12571
+ function ipToInt(ip) {
12572
+ const parts = ip.split(".");
12573
+ if (parts.length !== 4) return null;
12574
+ let v = 0;
12575
+ for (const p of parts) {
12576
+ const n = Number(p);
12577
+ if (!Number.isInteger(n) || n < 0 || n > 255) return null;
12578
+ v = v * 256 + n;
12579
+ }
12580
+ return v >>> 0;
12581
+ }
12582
+ function ipInCidr(ip, cidr) {
12583
+ const m = /^(\d+\.\d+\.\d+\.\d+)\s*\/\s*(\d+)$/.exec(cidr.trim());
12584
+ if (!m) return null;
12585
+ const netInt = ipToInt(m[1]);
12586
+ const prefix = Number(m[2]);
12587
+ const hostInt = ipToInt(ip.split("/")[0]);
12588
+ if (netInt === null || hostInt === null || prefix < 0 || prefix > 32) return null;
12589
+ const mask = prefix === 0 ? 0 : 4294967295 << 32 - prefix >>> 0;
12590
+ return (netInt & mask) === (hostInt & mask);
12591
+ }
12592
+ var SWITCH_CLASS = /* @__PURE__ */ new Set(["switch", "l3switch", "poeswitch"]);
12593
+ function parseNetwork(text2) {
12594
+ const ast = {
12595
+ type: "network",
12596
+ layout: "tiered",
12597
+ direction: "tb",
12598
+ spines: [],
12599
+ leaves: [],
12600
+ devices: [],
12601
+ links: [],
12602
+ groups: [],
12603
+ warnings: []
12604
+ };
12605
+ const deviceById = /* @__PURE__ */ new Map();
12606
+ const groupById = /* @__PURE__ */ new Map();
12607
+ const groupStack = [];
12608
+ let headerSeen = false;
12609
+ const rawLines = text2.split(/\r?\n/);
12610
+ const addDevice = (d, lineNo) => {
12611
+ if (deviceById.has(d.id)) {
12612
+ throw new NetworkParseError(`device id "${d.id}" already declared (line ${lineNo})`);
12613
+ }
12614
+ deviceById.set(d.id, d);
12615
+ ast.devices.push(d);
12616
+ const top = groupStack[groupStack.length - 1];
12617
+ if (top && !top.members.includes(d.id)) top.members.push(d.id);
12618
+ };
12619
+ const ensureDevice = (id, kind) => {
12620
+ let d = deviceById.get(id);
12621
+ if (!d) {
12622
+ d = { id, kind, groups: [] };
12623
+ deviceById.set(id, d);
12624
+ ast.devices.push(d);
12625
+ }
12626
+ return d;
12627
+ };
12628
+ const applyAttrs = (d, toks, start, lineNo) => {
12629
+ let i = start;
12630
+ while (i < toks.length) {
12631
+ const tk = toks[i];
12632
+ if (tk.str) {
12633
+ i++;
12634
+ continue;
12635
+ }
12636
+ const key = tk.value.endsWith(":") ? tk.value.slice(0, -1) : null;
12637
+ if (key === null) {
12638
+ i++;
12639
+ continue;
12640
+ }
12641
+ const val = toks[i + 1];
12642
+ i += 2;
12643
+ if (!val) continue;
12644
+ switch (key) {
12645
+ case "tier":
12646
+ if (TIERS.has(val.value)) d.tier = val.value;
12647
+ break;
12648
+ case "type":
12649
+ if (CAMERA_TYPES.has(val.value)) d.cameraType = val.value;
12650
+ break;
12651
+ case "ip":
12652
+ d.ip = val.value;
12653
+ break;
12654
+ case "model":
12655
+ d.model = val.value;
12656
+ break;
12657
+ case "count": {
12658
+ const n = Number(val.value);
12659
+ if (Number.isFinite(n)) d.count = n;
12660
+ break;
12661
+ }
12662
+ case "icon":
12663
+ d.icon = val.value;
12664
+ break;
12665
+ case "at": {
12666
+ const mm = /^(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)$/.exec(val.value);
12667
+ if (mm) d.at = { x: Number(mm[1]), y: Number(mm[2]) };
12668
+ break;
12669
+ }
12670
+ default:
12671
+ ast.warnings.push(`unknown attribute "${key}:" on device "${d.id}" (line ${lineNo})`);
12672
+ }
12673
+ }
12674
+ };
12675
+ const statements = [];
12676
+ rawLines.forEach((raw, i) => {
12677
+ for (const seg of splitStatements(stripComment2(raw))) statements.push({ text: seg, no: i + 1 });
12678
+ });
12679
+ for (const stmt of statements) {
12680
+ const lineNo = stmt.no;
12681
+ const toks = tokenize4(stmt.text);
12682
+ if (toks.length === 0) continue;
12683
+ const t0 = toks[0];
12684
+ if (!headerSeen) {
12685
+ if (!t0.str && /^(network|topology)$/i.test(t0.value)) {
12686
+ headerSeen = true;
12687
+ if (toks[1]?.str) ast.title = toks[1].value;
12688
+ continue;
12689
+ }
12690
+ throw new NetworkParseError(
12691
+ `network diagram must start with "network" or "topology" (line ${lineNo})`
12692
+ );
12693
+ }
12694
+ if (toks.length === 1 && t0.value === "}") {
12695
+ if (groupStack.length === 0) {
12696
+ throw new NetworkParseError(`unmatched "}" (line ${lineNo})`);
12697
+ }
12698
+ groupStack.pop();
12699
+ continue;
12700
+ }
12701
+ if (!t0.str && t0.value.endsWith(":")) {
12702
+ const key = t0.value.slice(0, -1).toLowerCase();
12703
+ const rest = toks.slice(1);
12704
+ switch (key) {
12705
+ case "layout": {
12706
+ const v = rest[0]?.value;
12707
+ if (v && LAYOUT_MODES.has(v)) ast.layout = v;
12708
+ else if (v) ast.warnings.push(`unknown layout "${v}" \u2014 using "tiered" (line ${lineNo})`);
12709
+ break;
12710
+ }
12711
+ case "direction": {
12712
+ const v = rest[0]?.value?.toLowerCase();
12713
+ if (v === "tb" || v === "lr") ast.direction = v;
12714
+ break;
12715
+ }
12716
+ case "title":
12717
+ if (rest[0]) ast.title = rest[0].value;
12718
+ break;
12719
+ case "spines":
12720
+ ast.spines.push(...rest.filter((r) => !r.str).map((r) => r.value));
12721
+ break;
12722
+ case "leaves":
12723
+ ast.leaves.push(...rest.filter((r) => !r.str).map((r) => r.value));
12724
+ break;
12725
+ case "legend":
12726
+ break;
12727
+ // parsed-and-ignored in v0.1
12728
+ default:
12729
+ ast.warnings.push(`unknown directive "${key}:" (line ${lineNo})`);
12730
+ }
12731
+ continue;
12732
+ }
12733
+ const ci = toks.findIndex((t) => !t.str && CONNECTORS.has(t.value));
12734
+ if (ci >= 0) {
12735
+ const fromT = toks[ci - 1];
12736
+ const toT = toks[ci + 1];
12737
+ if (!fromT || !toT || fromT.str || toT.str) {
12738
+ throw new NetworkParseError(`malformed link (line ${lineNo})`);
12739
+ }
12740
+ const conn = toks[ci].value;
12741
+ const link = {
12742
+ from: fromT.value,
12743
+ to: toT.value,
12744
+ directed: conn === "->",
12745
+ linkType: conn === "==" ? "lag" : "copper",
12746
+ line: lineNo
12747
+ };
12748
+ let si = ci + 2;
12749
+ if (toks[si] && !toks[si].str && toks[si].value === ":") si++;
12750
+ while (si < toks.length) {
12751
+ const tk = toks[si];
12752
+ if (tk.str) {
12753
+ link.label = tk.value;
12754
+ si++;
12755
+ continue;
12756
+ }
12757
+ const low = tk.value.toLowerCase();
12758
+ if (LINK_TYPE_KEYWORDS[low]) {
12759
+ link.linkType = LINK_TYPE_KEYWORDS[low];
12760
+ si++;
12761
+ continue;
12762
+ }
12763
+ if (low === "trunk" || low === "access") {
12764
+ link.mode = low;
12765
+ si++;
12766
+ continue;
12767
+ }
12768
+ if (SPEED_RE.test(tk.value)) {
12769
+ link.speed = tk.value;
12770
+ si++;
12771
+ continue;
12772
+ }
12773
+ if (low === "vlan:") {
12774
+ const v = toks[si + 1];
12775
+ if (v) {
12776
+ link.vlans = v.value.split(",").map((s) => Number(s.trim())).filter((n) => Number.isFinite(n));
12777
+ for (const vid of link.vlans) {
12778
+ if (vid < 1 || vid > 4094) {
12779
+ ast.warnings.push(`VLAN id ${vid} out of range 1\u20134094 (line ${lineNo})`);
12780
+ }
12781
+ }
12782
+ }
12783
+ si += 2;
12784
+ continue;
12785
+ }
12786
+ if (low === "port:") {
12787
+ const v = toks[si + 1];
12788
+ if (v) {
12789
+ const [near, far] = v.value.split(">");
12790
+ link.portNear = near;
12791
+ if (far) link.portFar = far;
12792
+ }
12793
+ si += 2;
12794
+ continue;
12795
+ }
12796
+ if (low === "ip:") {
12797
+ link.ip = toks[si + 1]?.value;
12798
+ si += 2;
12799
+ continue;
12800
+ }
12801
+ si++;
12802
+ }
12803
+ ast.links.push(link);
12804
+ continue;
12805
+ }
12806
+ if (!t0.str && GROUP_KINDS.has(t0.value) && toks[toks.length - 1].value === "{") {
12807
+ const idT = toks[1];
12808
+ if (!idT || idT.str) throw new NetworkParseError(`group needs an id (line ${lineNo})`);
12809
+ const kind = GROUP_KIND_ALIAS[t0.value] ?? t0.value;
12810
+ const label = toks[2]?.str ? toks[2].value : void 0;
12811
+ if (groupById.has(idT.value)) {
12812
+ throw new NetworkParseError(`group id "${idT.value}" already declared (line ${lineNo})`);
12813
+ }
12814
+ const parent = groupStack[groupStack.length - 1];
12815
+ const g = {
12816
+ id: idT.value,
12817
+ kind,
12818
+ label,
12819
+ members: [],
12820
+ children: [],
12821
+ parent: parent?.id,
12822
+ line: lineNo
12823
+ };
12824
+ if (parent) parent.children.push(g.id);
12825
+ groupById.set(g.id, g);
12826
+ ast.groups.push(g);
12827
+ groupStack.push(g);
12828
+ continue;
12829
+ }
12830
+ const canonKind = KIND_ALIASES[t0.value] ?? t0.value;
12831
+ if (!t0.str && DEVICE_KINDS.has(canonKind)) {
12832
+ const idT = toks[1];
12833
+ if (!idT || idT.str) throw new NetworkParseError(`device "${t0.value}" needs an id (line ${lineNo})`);
12834
+ let idx = 2;
12835
+ let label;
12836
+ if (toks[2]?.str) {
12837
+ label = toks[2].value;
12838
+ idx = 3;
12839
+ }
12840
+ const d = { id: idT.value, kind: canonKind, label, groups: groupStack.map((g) => g.id) };
12841
+ applyAttrs(d, toks, idx, lineNo);
12842
+ addDevice(d, lineNo);
12843
+ continue;
12844
+ }
12845
+ const sep = toks.findIndex((t) => !t.str && t.value === ":");
12846
+ if (sep > 0) {
12847
+ const ids = toks.slice(0, sep);
12848
+ const kindT = toks[sep + 1];
12849
+ if (kindT && !kindT.str && ids.every((t) => !t.str)) {
12850
+ const k = KIND_ALIASES[kindT.value] ?? kindT.value;
12851
+ if (!DEVICE_KINDS.has(k)) {
12852
+ const hint = nearestKind(kindT.value);
12853
+ throw new NetworkParseError(
12854
+ `unknown device kind "${kindT.value}"${hint ? ` \u2014 did you mean "${hint}"?` : ""} (line ${lineNo})`
12855
+ );
12856
+ }
12857
+ for (const idT of ids) {
12858
+ const d = { id: idT.value, kind: k, groups: groupStack.map((g) => g.id) };
12859
+ applyAttrs(d, toks, sep + 2, lineNo);
12860
+ addDevice(d, lineNo);
12861
+ }
12862
+ continue;
12863
+ }
12864
+ }
12865
+ if (toks.length === 1 && !t0.str && groupStack.length > 0) {
12866
+ const top = groupStack[groupStack.length - 1];
12867
+ if (!top.members.includes(t0.value)) top.members.push(t0.value);
12868
+ continue;
12869
+ }
12870
+ if (!t0.str && toks.length >= 2 && !toks[1].str) {
12871
+ const hint = nearestKind(t0.value);
12872
+ throw new NetworkParseError(
12873
+ `unknown device kind "${t0.value}"${hint ? ` \u2014 did you mean "${hint}"?` : ""} (line ${lineNo})`
12874
+ );
12875
+ }
12876
+ throw new NetworkParseError(`cannot parse line ${lineNo}: "${stmt.text.trim()}"`);
12877
+ }
12878
+ if (groupStack.length > 0) {
12879
+ throw new NetworkParseError(`unclosed group "${groupStack[groupStack.length - 1].id}"`);
12880
+ }
12881
+ for (const id of ast.spines) ensureDevice(id, "l3switch");
12882
+ for (const id of ast.leaves) ensureDevice(id, "switch");
12883
+ for (const link of ast.links) {
12884
+ for (const end of [link.from, link.to]) {
12885
+ if (!deviceById.has(end)) {
12886
+ throw new NetworkParseError(
12887
+ `link references undeclared device "${end}" (line ${link.line ?? "?"})`
12888
+ );
12889
+ }
12890
+ }
12891
+ if (link.mode === "trunk") {
12892
+ const a = deviceById.get(link.from), b = deviceById.get(link.to);
12893
+ if (!SWITCH_CLASS.has(a.kind) && !SWITCH_CLASS.has(b.kind)) {
12894
+ ast.warnings.push(`trunk link ${link.from}\u2013${link.to} connects no switch-class device (line ${link.line ?? "?"})`);
12895
+ }
12896
+ }
12897
+ }
12898
+ for (const g of ast.groups) {
12899
+ if (g.kind !== "subnet" || !g.label) continue;
12900
+ for (const memberId of g.members) {
12901
+ const d = deviceById.get(memberId);
12902
+ if (!d?.ip) continue;
12903
+ const inside = ipInCidr(d.ip, g.label);
12904
+ if (inside === false) {
12905
+ throw new NetworkParseError(
12906
+ `${d.id} ip ${d.ip} is not inside subnet ${g.label} (line ${d.line ?? g.line ?? "?"})`
12907
+ );
12908
+ }
12909
+ }
12910
+ }
12911
+ return ast;
12912
+ }
12913
+
12914
+ // src/diagrams/network/symbols.ts
12915
+ var BODY = "sx-net-body";
12916
+ var DET = "sx-net-detail";
12917
+ var GLY = "sx-net-glyph";
12918
+ var GLYL = "sx-net-glyph-line";
12919
+ var ITX = "sx-net-icontext";
12920
+ var CLOUD = "sx-net-cloud-body";
12921
+ var CTX = "sx-net-cloudtext";
12922
+ var r2 = (n) => Math.round(n * 100) / 100;
12923
+ function arrow(x1, y1, x2, y2, hs = 4) {
12924
+ const ang = Math.atan2(y2 - y1, x2 - x1);
12925
+ const a1 = ang + Math.PI - 0.5;
12926
+ const a2 = ang + Math.PI + 0.5;
12927
+ const p1x = r2(x2 + hs * Math.cos(a1)), p1y = r2(y2 + hs * Math.sin(a1));
12928
+ const p2x = r2(x2 + hs * Math.cos(a2)), p2y = r2(y2 + hs * Math.sin(a2));
12929
+ return chunk3WNW5Y7P_cjs.line({ class: GLYL, x1: r2(x1), y1: r2(y1), x2: r2(x2), y2: r2(y2) }) + chunk3WNW5Y7P_cjs.polygon({ class: GLY, points: `${r2(x2)},${r2(y2)} ${p1x},${p1y} ${p2x},${p2y}` });
12930
+ }
12931
+ function router(b) {
12932
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
12933
+ const rw = b.w * 0.78, rh = b.h * 0.62;
12934
+ const x = cx - rw / 2, y = cy - rh / 2;
12935
+ const parts = [
12936
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x), y: r2(y), width: r2(rw), height: r2(rh), rx: rh / 2, ry: rh / 2 })
12937
+ ];
12938
+ parts.push(arrow(cx - 2, cy - rh * 0.18, x + rw - 5, cy - rh * 0.18));
12939
+ parts.push(arrow(cx + 2, cy + rh * 0.18, x + 5, cy + rh * 0.18));
12940
+ parts.push(arrow(cx + rw * 0.18, cy + 2, cx + rw * 0.18, y + rh - 4));
12941
+ parts.push(arrow(cx - rw * 0.18, cy - 2, cx - rw * 0.18, y + 4));
12942
+ return chunk3WNW5Y7P_cjs.group({}, parts);
12943
+ }
12944
+ function switchBox(b, glyph) {
12945
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
12946
+ const rw = b.w * 0.86, rh = b.h * 0.5;
12947
+ const x = cx - rw / 2, y = cy - rh / 2;
12948
+ const parts = [
12949
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x), y: r2(y), width: r2(rw), height: r2(rh), rx: 3, ry: 3 })
12950
+ ];
12951
+ if (glyph === "circular") {
12952
+ parts.push(arrow(x + rw * 0.25, cy - rh * 0.16, x + rw * 0.75, cy - rh * 0.16));
12953
+ parts.push(arrow(x + rw * 0.75, cy + rh * 0.16, x + rw * 0.25, cy + rh * 0.16));
12954
+ parts.push(arrow(x + rw * 0.62, cy - rh * 0.16, x + rw * 0.78, cy - rh * 0.16));
12955
+ } else {
12956
+ parts.push(arrow(x + rw * 0.2, cy - rh * 0.18, x + rw * 0.8, cy - rh * 0.18));
12957
+ parts.push(arrow(x + rw * 0.8, cy + rh * 0.18, x + rw * 0.2, cy + rh * 0.18));
12958
+ }
12959
+ return chunk3WNW5Y7P_cjs.group({}, parts);
12960
+ }
12961
+ function poeSwitch(b) {
12962
+ const cx = b.x + b.w / 2;
12963
+ return chunk3WNW5Y7P_cjs.group({}, [switchBox(b, "straight"), chunk3WNW5Y7P_cjs.text({ class: ITX, x: r2(cx), y: r2(b.y + b.h * 0.78 + 8), "text-anchor": "middle" }, "PoE")]);
12964
+ }
12965
+ function firewall(b) {
12966
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
12967
+ const rw = b.w * 0.78, rh = b.h * 0.62;
12968
+ const x = cx - rw / 2, y = cy - rh / 2;
12969
+ const parts = [chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x), y: r2(y), width: r2(rw), height: r2(rh), rx: 2, ry: 2 })];
12970
+ const rows = 3;
12971
+ const rhh = rh / rows;
12972
+ for (let i = 1; i < rows; i++) parts.push(chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(x), y1: r2(y + i * rhh), x2: r2(x + rw), y2: r2(y + i * rhh) }));
12973
+ for (let i = 0; i < rows; i++) {
12974
+ const yy = y + i * rhh;
12975
+ const offset = i % 2 === 0 ? rw / 3 : rw / 6;
12976
+ for (let vx = x + offset; vx < x + rw - 2; vx += rw / 3) {
12977
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(vx), y1: r2(yy), x2: r2(vx), y2: r2(yy + rhh) }));
12978
+ }
12979
+ }
12980
+ return chunk3WNW5Y7P_cjs.group({}, parts);
12981
+ }
12982
+ function brickless(b, glyph) {
12983
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
12984
+ const rw = b.w * 0.78, rh = b.h * 0.6;
12985
+ const x = cx - rw / 2, y = cy - rh / 2;
12986
+ return chunk3WNW5Y7P_cjs.group({}, [
12987
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x), y: r2(y), width: r2(rw), height: r2(rh), rx: 3, ry: 3 }),
12988
+ chunk3WNW5Y7P_cjs.text({ class: ITX, x: r2(cx), y: r2(cy + 4), "text-anchor": "middle" }, glyph)
12989
+ ]);
12990
+ }
12991
+ function accessPoint(b) {
12992
+ const cx = b.x + b.w / 2, cy = b.y + b.h * 0.62;
12993
+ const rw = b.w * 0.5, rh = b.h * 0.34;
12994
+ const parts = [
12995
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(cx - rw / 2), y: r2(cy - rh / 2), width: r2(rw), height: r2(rh), rx: rh / 2, ry: rh / 2 })
12996
+ ];
12997
+ for (let i = 1; i <= 2; i++) {
12998
+ const rr = i * b.w * 0.16;
12999
+ parts.push(chunk3WNW5Y7P_cjs.path({ class: GLYL, d: `M ${r2(cx - rr)} ${r2(cy - rh / 2 - 2)} A ${r2(rr)} ${r2(rr)} 0 0 1 ${r2(cx + rr)} ${r2(cy - rh / 2 - 2)}` }));
13000
+ }
13001
+ return chunk3WNW5Y7P_cjs.group({}, parts);
13002
+ }
13003
+ function server(b) {
13004
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
13005
+ const rw = b.w * 0.46, rh = b.h * 0.82;
13006
+ const x = cx - rw / 2, y = cy - rh / 2;
13007
+ const parts = [chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x), y: r2(y), width: r2(rw), height: r2(rh), rx: 2, ry: 2 })];
13008
+ for (let i = 1; i <= 3; i++) parts.push(chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(x + 4), y1: r2(y + i * (rh / 4)), x2: r2(x + rw - 4), y2: r2(y + i * (rh / 4)) }));
13009
+ parts.push(chunk3WNW5Y7P_cjs.circle({ class: GLY, cx: r2(x + rw - 7), cy: r2(y + 6), r: 1.6 }));
13010
+ return chunk3WNW5Y7P_cjs.group({}, parts);
13011
+ }
13012
+ function serverFarm(b, d) {
13013
+ const parts = [];
13014
+ const off = Math.min(6, b.w * 0.1);
13015
+ for (let i = 2; i >= 0; i--) {
13016
+ parts.push(server({ x: b.x + i * off, y: b.y - i * off * 0.5, w: b.w - 2 * off, h: b.h - off }));
13017
+ }
13018
+ if (d.count) parts.push(chunk3WNW5Y7P_cjs.text({ class: ITX, x: r2(b.x + b.w - 6), y: r2(b.y + b.h - 2), "text-anchor": "end" }, `\xD7${d.count}`));
13019
+ return chunk3WNW5Y7P_cjs.group({}, parts);
13020
+ }
13021
+ function pc(b) {
13022
+ const cx = b.x + b.w / 2;
13023
+ const sw = b.w * 0.7, sh = b.h * 0.52;
13024
+ const sx = cx - sw / 2, sy = b.y + b.h * 0.12;
13025
+ return chunk3WNW5Y7P_cjs.group({}, [
13026
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(sx), y: r2(sy), width: r2(sw), height: r2(sh), rx: 2, ry: 2 }),
13027
+ chunk3WNW5Y7P_cjs.rect({ class: DET, x: r2(sx + 3), y: r2(sy + 3), width: r2(sw - 6), height: r2(sh - 6), rx: 1, ry: 1, fill: "none" }),
13028
+ chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(cx), y1: r2(sy + sh), x2: r2(cx), y2: r2(sy + sh + b.h * 0.16) }),
13029
+ chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(cx - sw * 0.28), y1: r2(b.y + b.h - 2), x2: r2(cx + sw * 0.28), y2: r2(b.y + b.h - 2) })
13030
+ ]);
13031
+ }
13032
+ function laptop(b) {
13033
+ const cx = b.x + b.w / 2;
13034
+ const sw = b.w * 0.6, sh = b.h * 0.46;
13035
+ const sx = cx - sw / 2, sy = b.y + b.h * 0.16;
13036
+ return chunk3WNW5Y7P_cjs.group({}, [
13037
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(sx), y: r2(sy), width: r2(sw), height: r2(sh), rx: 2, ry: 2 }),
13038
+ chunk3WNW5Y7P_cjs.polygon({ class: BODY, points: `${r2(sx - sw * 0.18)},${r2(sy + sh + b.h * 0.18)} ${r2(sx + sw + sw * 0.18)},${r2(sy + sh + b.h * 0.18)} ${r2(sx + sw)},${r2(sy + sh)} ${r2(sx)},${r2(sy + sh)}` })
13039
+ ]);
13040
+ }
13041
+ function mobile(b) {
13042
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
13043
+ const rw = b.w * 0.32, rh = b.h * 0.82;
13044
+ return chunk3WNW5Y7P_cjs.group({}, [
13045
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(cx - rw / 2), y: r2(cy - rh / 2), width: r2(rw), height: r2(rh), rx: 3, ry: 3 }),
13046
+ chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(cx - rw * 0.2), y1: r2(cy + rh / 2 - 4), x2: r2(cx + rw * 0.2), y2: r2(cy + rh / 2 - 4) })
13047
+ ]);
13048
+ }
13049
+ function ipphone(b) {
13050
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
13051
+ const rw = b.w * 0.6, rh = b.h * 0.6;
13052
+ const x = cx - rw / 2, y = cy - rh / 2;
13053
+ return chunk3WNW5Y7P_cjs.group({}, [
13054
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x), y: r2(y), width: r2(rw), height: r2(rh * 0.7), rx: 2, ry: 2 }),
13055
+ chunk3WNW5Y7P_cjs.path({ class: GLYL, d: `M ${r2(x + rw * 0.2)} ${r2(y + 5)} q ${r2(rw * 0.3)} ${r2(rh * 0.5)} ${r2(rw * 0.6)} 0` })
13056
+ ]);
13057
+ }
13058
+ function printer(b) {
13059
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
13060
+ const rw = b.w * 0.7, rh = b.h * 0.42;
13061
+ const x = cx - rw / 2, y = cy - rh * 0.1;
13062
+ return chunk3WNW5Y7P_cjs.group({}, [
13063
+ chunk3WNW5Y7P_cjs.rect({ class: DET, x: r2(x + rw * 0.18), y: r2(y - rh * 0.7), width: r2(rw * 0.64), height: r2(rh * 0.7), fill: "none" }),
13064
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x), y: r2(y), width: r2(rw), height: r2(rh), rx: 2, ry: 2 }),
13065
+ chunk3WNW5Y7P_cjs.rect({ class: DET, x: r2(x + rw * 0.18), y: r2(y + rh - 2), width: r2(rw * 0.64), height: r2(rh * 0.5), fill: "none" }),
13066
+ chunk3WNW5Y7P_cjs.circle({ class: GLY, cx: r2(x + rw - 7), cy: r2(y + rh / 2), r: 1.6 })
13067
+ ]);
13068
+ }
13069
+ function storage(b) {
13070
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
13071
+ const rw = b.w * 0.5, rh = b.h * 0.78;
13072
+ const x = cx - rw / 2, y = cy - rh / 2;
13073
+ const ry = rw * 0.18;
13074
+ const parts = [
13075
+ chunk3WNW5Y7P_cjs.path({ class: BODY, d: `M ${r2(x)} ${r2(y + ry)} A ${r2(rw / 2)} ${r2(ry)} 0 0 1 ${r2(x + rw)} ${r2(y + ry)} L ${r2(x + rw)} ${r2(y + rh - ry)} A ${r2(rw / 2)} ${r2(ry)} 0 0 1 ${r2(x)} ${r2(y + rh - ry)} Z` }),
13076
+ chunk3WNW5Y7P_cjs.path({ class: DET, fill: "none", d: `M ${r2(x)} ${r2(y + ry)} A ${r2(rw / 2)} ${r2(ry)} 0 0 0 ${r2(x + rw)} ${r2(y + ry)}` })
13077
+ ];
13078
+ return chunk3WNW5Y7P_cjs.group({}, parts);
13079
+ }
13080
+ function camera(b, d) {
13081
+ const t = d.cameraType ?? "fixed";
13082
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
13083
+ if (t === "dome" || t === "ptz") {
13084
+ const rr = b.w * (t === "ptz" ? 0.34 : 0.28);
13085
+ const parts = [
13086
+ chunk3WNW5Y7P_cjs.path({ class: BODY, d: `M ${r2(cx - rr)} ${r2(cy + rr * 0.2)} A ${r2(rr)} ${r2(rr)} 0 0 1 ${r2(cx + rr)} ${r2(cy + rr * 0.2)} Z` }),
13087
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(cx - rr * 1.15), y: r2(cy + rr * 0.2), width: r2(rr * 2.3), height: r2(b.h * 0.12), rx: 1, ry: 1 }),
13088
+ chunk3WNW5Y7P_cjs.circle({ class: GLY, cx: r2(cx), cy: r2(cy - rr * 0.15), r: r2(rr * 0.3) })
13089
+ ];
13090
+ return chunk3WNW5Y7P_cjs.group({}, parts);
13091
+ }
13092
+ if (t === "bullet") {
13093
+ const bw2 = b.w * 0.6, bh2 = b.h * 0.34;
13094
+ const x2 = cx - bw2 / 2, y2 = cy - bh2 / 2;
13095
+ return chunk3WNW5Y7P_cjs.group({}, [
13096
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x2), y: r2(y2), width: r2(bw2), height: r2(bh2), rx: bh2 / 2, ry: bh2 / 2 }),
13097
+ chunk3WNW5Y7P_cjs.circle({ class: GLY, cx: r2(x2 + bw2 - 4), cy: r2(cy), r: r2(bh2 * 0.28) }),
13098
+ chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(cx), y1: r2(y2 + bh2), x2: r2(cx), y2: r2(y2 + bh2 + b.h * 0.16) })
13099
+ ]);
13100
+ }
13101
+ const bw = b.w * 0.5, bh = b.h * 0.42;
13102
+ const x = cx - bw / 2, y = cy - bh / 2;
13103
+ return chunk3WNW5Y7P_cjs.group({}, [
13104
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x), y: r2(y), width: r2(bw), height: r2(bh), rx: 2, ry: 2 }),
13105
+ chunk3WNW5Y7P_cjs.circle({ class: GLY, cx: r2(cx), cy: r2(cy), r: r2(bh * 0.3) })
13106
+ ]);
13107
+ }
13108
+ function recorder(b, label) {
13109
+ const cx = b.x + b.w / 2, cy = b.y + b.h / 2;
13110
+ const rw = b.w * 0.82, rh = b.h * 0.46;
13111
+ const x = cx - rw / 2, y = cy - rh / 2;
13112
+ return chunk3WNW5Y7P_cjs.group({}, [
13113
+ chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(x), y: r2(y), width: r2(rw), height: r2(rh), rx: 2, ry: 2 }),
13114
+ chunk3WNW5Y7P_cjs.circle({ class: GLY, cx: r2(x + rw * 0.22), cy: r2(y + rh * 0.32), r: 2 }),
13115
+ chunk3WNW5Y7P_cjs.circle({ class: GLY, cx: r2(x + rw * 0.4), cy: r2(y + rh * 0.32), r: 2 }),
13116
+ chunk3WNW5Y7P_cjs.text({ class: ITX, x: r2(cx), y: r2(y + rh * 0.85), "text-anchor": "middle" }, label)
13117
+ ]);
13118
+ }
13119
+ function monitor(b, grid) {
13120
+ const cx = b.x + b.w / 2;
13121
+ const sw = b.w * 0.84, sh = b.h * 0.56;
13122
+ const sx = cx - sw / 2, sy = b.y + b.h * 0.1;
13123
+ const parts = [chunk3WNW5Y7P_cjs.rect({ class: BODY, x: r2(sx), y: r2(sy), width: r2(sw), height: r2(sh), rx: 2, ry: 2 })];
13124
+ if (grid) {
13125
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(cx), y1: r2(sy), x2: r2(cx), y2: r2(sy + sh) }));
13126
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(sx), y1: r2(sy + sh / 2), x2: r2(sx + sw), y2: r2(sy + sh / 2) }));
13127
+ }
13128
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(cx), y1: r2(sy + sh), x2: r2(cx), y2: r2(sy + sh + b.h * 0.14) }));
13129
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: DET, x1: r2(cx - sw * 0.2), y1: r2(b.y + b.h - 2), x2: r2(cx + sw * 0.2), y2: r2(b.y + b.h - 2) }));
13130
+ return chunk3WNW5Y7P_cjs.group({}, parts);
13131
+ }
13132
+ function cloud(b, label) {
13133
+ const mx = (dx) => r2(b.x + dx / 200 * b.w);
13134
+ const my = (dy) => r2(b.y + dy / 110 * b.h);
13135
+ const d = `M ${mx(50)} ${my(96)} C ${mx(24)} ${my(96)} ${mx(24)} ${my(62)} ${mx(50)} ${my(58)} C ${mx(50)} ${my(30)} ${mx(94)} ${my(26)} ${mx(100)} ${my(50)} C ${mx(106)} ${my(26)} ${mx(150)} ${my(30)} ${mx(150)} ${my(58)} C ${mx(176)} ${my(62)} ${mx(176)} ${my(96)} ${mx(150)} ${my(96)} Z`;
13136
+ return chunk3WNW5Y7P_cjs.group({}, [
13137
+ chunk3WNW5Y7P_cjs.path({ class: CLOUD, d }),
13138
+ chunk3WNW5Y7P_cjs.text({ class: CTX, x: r2(b.x + b.w / 2), y: my(80), "text-anchor": "middle" }, label)
13139
+ ]);
13140
+ }
13141
+ function busBar(b) {
13142
+ const y = b.y + b.h / 2;
13143
+ return chunk3WNW5Y7P_cjs.line({ class: "sx-net-bus", x1: r2(b.x), y1: r2(y), x2: r2(b.x + b.w), y2: r2(y) });
13144
+ }
13145
+ function iconSize(kind) {
13146
+ if (kind === "internet" || kind === "wan" || kind === "cloud" || kind === "pstn") return { w: 110, h: 64 };
13147
+ if (kind === "lan") return { w: 150, h: 24 };
13148
+ if (kind === "serverfarm") return { w: 74, h: 58 };
13149
+ return { w: 64, h: 48 };
13150
+ }
13151
+ function drawDeviceIcon(d, b) {
13152
+ switch (d.kind) {
13153
+ case "router":
13154
+ return router(b);
13155
+ case "gateway":
13156
+ return chunk3WNW5Y7P_cjs.group({}, [router(b), chunk3WNW5Y7P_cjs.text({ class: ITX, x: r2(b.x + b.w / 2), y: r2(b.y + b.h - 1), "text-anchor": "middle" }, "GW")]);
13157
+ case "vpngw":
13158
+ return chunk3WNW5Y7P_cjs.group({}, [router(b), chunk3WNW5Y7P_cjs.text({ class: ITX, x: r2(b.x + b.w / 2), y: r2(b.y + b.h - 1), "text-anchor": "middle" }, "VPN")]);
13159
+ case "switch":
13160
+ return switchBox(b, "straight");
13161
+ case "l3switch":
13162
+ return switchBox(b, "circular");
13163
+ case "poeswitch":
13164
+ return poeSwitch(b);
13165
+ case "firewall":
13166
+ return firewall(b);
13167
+ case "loadbalancer":
13168
+ return brickless(b, "LB");
13169
+ case "ids":
13170
+ return brickless(b, "IDS");
13171
+ case "proxy":
13172
+ return brickless(b, "PXY");
13173
+ case "modem":
13174
+ return brickless(b, "MDM");
13175
+ case "wlc":
13176
+ return brickless(b, "WLC");
13177
+ case "ap":
13178
+ return accessPoint(b);
13179
+ case "server":
13180
+ return server(b);
13181
+ case "serverfarm":
13182
+ return serverFarm(b, d);
13183
+ case "pc":
13184
+ return pc(b);
13185
+ case "laptop":
13186
+ return laptop(b);
13187
+ case "mobile":
13188
+ return mobile(b);
13189
+ case "ipphone":
13190
+ return ipphone(b);
13191
+ case "printer":
13192
+ return printer(b);
13193
+ case "storage":
13194
+ return storage(b);
13195
+ case "camera":
13196
+ return camera(b, d);
13197
+ case "nvr":
13198
+ return recorder(b, "NVR");
13199
+ case "dvr":
13200
+ return recorder(b, "DVR");
13201
+ case "encoder":
13202
+ return brickless(b, "ENC");
13203
+ case "monitor":
13204
+ return monitor(b, d.icon === "videowall");
13205
+ case "internet":
13206
+ return cloud(b, d.label ?? "Internet");
13207
+ case "wan":
13208
+ return cloud(b, d.label ?? "WAN");
13209
+ case "pstn":
13210
+ return cloud(b, d.label ?? "PSTN");
13211
+ case "cloud":
13212
+ return cloud(b, d.label ?? "Cloud");
13213
+ case "lan":
13214
+ return busBar(b);
13215
+ default:
13216
+ return brickless(b, "?");
13217
+ }
13218
+ }
13219
+ function isCloudKind(kind) {
13220
+ return kind === "internet" || kind === "wan" || kind === "pstn" || kind === "cloud";
13221
+ }
13222
+
13223
+ // src/diagrams/network/layout.ts
13224
+ var NET_CONST = {
13225
+ DEVICE_W: 64,
13226
+ DEVICE_H: 48,
13227
+ TIER_BAND_GAP: 104,
13228
+ SIBLING_GAP: 44,
13229
+ RING_RADIUS_MIN: 120,
13230
+ STAR_HUB_GAP: 130,
13231
+ SPINE_LEAF_GAP: 140,
13232
+ LABEL_GAP: 6,
13233
+ LABEL_H: 15,
13234
+ SUBLABEL_H: 12,
13235
+ GROUP_PAD: 18,
13236
+ GROUP_LABEL_INSET: 12,
13237
+ GROUP_HEADER: 16,
13238
+ CHAR_W: 6.3,
13239
+ PAD: 30
13240
+ };
13241
+ var ENDPOINT_KINDS = /* @__PURE__ */ new Set([
13242
+ "pc",
13243
+ "laptop",
13244
+ "mobile",
13245
+ "ipphone",
13246
+ "printer",
13247
+ "camera",
13248
+ "server",
13249
+ "serverfarm",
13250
+ "storage",
13251
+ "monitor",
13252
+ "nvr",
13253
+ "dvr"
13254
+ ]);
13255
+ function labelExtra(d) {
13256
+ if (isCloudKind(d.kind)) return 0;
13257
+ let h = NET_CONST.LABEL_GAP + NET_CONST.LABEL_H;
13258
+ if (d.ip || d.model) h += NET_CONST.SUBLABEL_H;
13259
+ return h;
13260
+ }
13261
+ function labelText(d) {
13262
+ return d.label ?? d.id;
13263
+ }
13264
+ function deviceFootprint(d) {
13265
+ return iconSize(d.kind);
13266
+ }
13267
+ function effBox(box) {
13268
+ const labelW = Math.max(box.w, labelText(box.device).length * NET_CONST.CHAR_W + 6);
13269
+ const half = labelW / 2;
13270
+ return {
13271
+ left: box.cx - half,
13272
+ top: box.y,
13273
+ right: box.cx + half,
13274
+ bottom: box.y + box.h + labelExtra(box.device)
13275
+ };
13276
+ }
13277
+ function edgePoint(box, tx, ty) {
13278
+ const dx = tx - box.cx;
13279
+ const dy = ty - box.cy;
13280
+ if (dx === 0 && dy === 0) return { x: box.cx, y: box.cy };
13281
+ const hw = box.w / 2;
13282
+ const hh = box.h / 2;
13283
+ const sx = dx !== 0 ? hw / Math.abs(dx) : Infinity;
13284
+ const sy = dy !== 0 ? hh / Math.abs(dy) : Infinity;
13285
+ const s = Math.min(sx, sy);
13286
+ return { x: box.cx + dx * s, y: box.cy + dy * s };
13287
+ }
13288
+ function placeBanded(ast, ranks) {
13289
+ const lr = ast.direction === "lr";
13290
+ const byRank = /* @__PURE__ */ new Map();
13291
+ for (const d of ast.devices) {
13292
+ const r = ranks.get(d.id) ?? 0;
13293
+ if (!byRank.has(r)) byRank.set(r, []);
13294
+ byRank.get(r).push(d);
13295
+ }
13296
+ const rankValues = [...byRank.keys()].sort((a, b) => a - b);
13297
+ const pos = /* @__PURE__ */ new Map();
13298
+ let maxRowSpan = 0;
13299
+ const rowSpans = /* @__PURE__ */ new Map();
13300
+ for (const r of rankValues) {
13301
+ const devs = byRank.get(r);
13302
+ let span = 0;
13303
+ devs.forEach((d, i) => {
13304
+ const fp = deviceFootprint(d);
13305
+ const cross = lr ? fp.h + labelExtra(d) : Math.max(fp.w, labelText(d).length * NET_CONST.CHAR_W + 6);
13306
+ span += cross + (i > 0 ? NET_CONST.SIBLING_GAP : 0);
13307
+ });
13308
+ rowSpans.set(r, span);
13309
+ maxRowSpan = Math.max(maxRowSpan, span);
13310
+ }
13311
+ rankValues.forEach((r, rowIdx) => {
13312
+ const devs = byRank.get(r);
13313
+ const along = rowIdx * NET_CONST.TIER_BAND_GAP;
13314
+ let cursor = (maxRowSpan - rowSpans.get(r)) / 2;
13315
+ for (const d of devs) {
13316
+ const fp = deviceFootprint(d);
13317
+ const cross = lr ? fp.h + labelExtra(d) : Math.max(fp.w, labelText(d).length * NET_CONST.CHAR_W + 6);
13318
+ const center = cursor + cross / 2;
13319
+ if (lr) pos.set(d.id, { x: along, y: center });
13320
+ else pos.set(d.id, { x: center, y: along });
13321
+ cursor += cross + NET_CONST.SIBLING_GAP;
13322
+ }
13323
+ });
13324
+ return pos;
13325
+ }
13326
+ function adjacency(devices, links) {
13327
+ const adj = /* @__PURE__ */ new Map();
13328
+ for (const d of devices) adj.set(d.id, /* @__PURE__ */ new Set());
13329
+ for (const l of links) {
13330
+ adj.get(l.from)?.add(l.to);
13331
+ adj.get(l.to)?.add(l.from);
13332
+ }
13333
+ return adj;
13334
+ }
13335
+ function tieredRanks(ast, links) {
13336
+ const TIER_RANK = { edge: 1, core: 2, distribution: 3, access: 4 };
13337
+ const rank = /* @__PURE__ */ new Map();
13338
+ for (const d of ast.devices) {
13339
+ if (isCloudKind(d.kind)) rank.set(d.id, 0);
13340
+ else if (d.tier) rank.set(d.id, TIER_RANK[d.tier]);
13341
+ }
13342
+ const adj = adjacency(ast.devices, links);
13343
+ for (let pass = 0; pass < ast.devices.length + 4; pass++) {
13344
+ let changed = false;
13345
+ for (const d of ast.devices) {
13346
+ if (rank.has(d.id)) continue;
13347
+ const known = [];
13348
+ for (const n of adj.get(d.id) ?? []) {
13349
+ const r = rank.get(n);
13350
+ if (r !== void 0) known.push(r);
13351
+ }
13352
+ if (known.length === 0) continue;
13353
+ const next = ENDPOINT_KINDS.has(d.kind) ? Math.max(...known) + 1 : Math.min(...known) + 1;
13354
+ rank.set(d.id, next);
13355
+ changed = true;
13356
+ }
13357
+ if (!changed) break;
13358
+ }
13359
+ const maxR = Math.max(0, ...[...rank.values()]);
13360
+ for (const d of ast.devices) {
13361
+ if (!rank.has(d.id)) rank.set(d.id, ENDPOINT_KINDS.has(d.kind) ? maxR + 1 : 2);
13362
+ }
13363
+ return rank;
13364
+ }
13365
+ function treeRanks(ast, links) {
13366
+ const adj = adjacency(ast.devices, links);
13367
+ const rank = /* @__PURE__ */ new Map();
13368
+ let root = ast.devices.find((d) => isCloudKind(d.kind));
13369
+ if (!root) {
13370
+ let best = -1;
13371
+ for (const d of ast.devices) {
13372
+ const deg = adj.get(d.id)?.size ?? 0;
13373
+ if (deg > best) {
13374
+ best = deg;
13375
+ root = d;
13376
+ }
13377
+ }
13378
+ }
13379
+ if (!root) return rank;
13380
+ const queue = [root.id];
13381
+ rank.set(root.id, 0);
13382
+ while (queue.length) {
13383
+ const id = queue.shift();
13384
+ const r = rank.get(id);
13385
+ for (const n of adj.get(id) ?? []) {
13386
+ if (!rank.has(n)) {
13387
+ rank.set(n, r + 1);
13388
+ queue.push(n);
13389
+ }
13390
+ }
13391
+ }
13392
+ for (const d of ast.devices) if (!rank.has(d.id)) rank.set(d.id, 0);
13393
+ return rank;
13394
+ }
13395
+ function placeCircle(ast, radiusBase) {
13396
+ const pos = /* @__PURE__ */ new Map();
13397
+ const n = ast.devices.length;
13398
+ const radius = Math.max(radiusBase, n * 56 / (2 * Math.PI));
13399
+ ast.devices.forEach((d, i) => {
13400
+ const ang = i / Math.max(1, n) * 2 * Math.PI - Math.PI / 2;
13401
+ pos.set(d.id, { x: radius * Math.cos(ang), y: radius * Math.sin(ang) });
13402
+ });
13403
+ return pos;
13404
+ }
13405
+ function placeStar(ast, links) {
13406
+ const adj = adjacency(ast.devices, links);
13407
+ let hub = ast.devices[0];
13408
+ let best = -1;
13409
+ for (const d of ast.devices) {
13410
+ const deg = adj.get(d.id)?.size ?? 0;
13411
+ if (deg > best) {
13412
+ best = deg;
13413
+ hub = d;
13414
+ }
13415
+ }
13416
+ const pos = /* @__PURE__ */ new Map();
13417
+ if (!hub) return pos;
13418
+ pos.set(hub.id, { x: 0, y: 0 });
13419
+ const spokes = ast.devices.filter((d) => d.id !== hub.id);
13420
+ const radius = Math.max(NET_CONST.STAR_HUB_GAP, spokes.length * 50 / (2 * Math.PI));
13421
+ spokes.forEach((d, i) => {
13422
+ const ang = i / Math.max(1, spokes.length) * 2 * Math.PI - Math.PI / 2;
13423
+ pos.set(d.id, { x: radius * Math.cos(ang), y: radius * Math.sin(ang) });
13424
+ });
13425
+ return pos;
13426
+ }
13427
+ function placeBus(ast) {
13428
+ const pos = /* @__PURE__ */ new Map();
13429
+ let cursor = 0;
13430
+ for (const d of ast.devices) {
13431
+ const fp = deviceFootprint(d);
13432
+ pos.set(d.id, { x: cursor + fp.w / 2, y: 0 });
13433
+ cursor += fp.w + NET_CONST.SIBLING_GAP;
13434
+ }
13435
+ return pos;
13436
+ }
13437
+ function placeSpineLeaf(ast, links) {
13438
+ const pos = /* @__PURE__ */ new Map();
13439
+ const spineSet = new Set(ast.spines);
13440
+ const leafSet = new Set(ast.leaves);
13441
+ const rowGap = NET_CONST.SPINE_LEAF_GAP;
13442
+ const step = NET_CONST.DEVICE_W + NET_CONST.SIBLING_GAP;
13443
+ const centerRow = (ids, y) => {
13444
+ const span = (ids.length - 1) * step;
13445
+ ids.forEach((id, i) => pos.set(id, { x: i * step - span / 2, y }));
13446
+ };
13447
+ centerRow(ast.spines, 0);
13448
+ centerRow(ast.leaves, rowGap);
13449
+ const adj = adjacency(ast.devices, links);
13450
+ const hosts = ast.devices.filter((d) => !spineSet.has(d.id) && !leafSet.has(d.id));
13451
+ let seq = 0;
13452
+ for (const h of hosts) {
13453
+ let anchorX;
13454
+ for (const n of adj.get(h.id) ?? []) {
13455
+ if (leafSet.has(n)) {
13456
+ anchorX = pos.get(n)?.x;
13457
+ break;
13458
+ }
13459
+ }
13460
+ pos.set(h.id, { x: anchorX ?? seq++ * step, y: rowGap * 2 });
13461
+ }
13462
+ return pos;
13463
+ }
13464
+ function placeManual(ast) {
13465
+ const pos = /* @__PURE__ */ new Map();
13466
+ let fallback = 0;
13467
+ for (const d of ast.devices) {
13468
+ if (d.at) pos.set(d.id, { x: d.at.x, y: d.at.y });
13469
+ else {
13470
+ pos.set(d.id, { x: fallback * 120, y: 0 });
13471
+ fallback++;
13472
+ }
13473
+ }
13474
+ return pos;
13475
+ }
13476
+ function withAutoLinks(ast) {
13477
+ if (ast.layout !== "spine-leaf" || ast.spines.length === 0 || ast.leaves.length === 0) {
13478
+ return ast.links;
13479
+ }
13480
+ const have = new Set(ast.links.map((l) => [l.from, l.to].sort().join("\u2194")));
13481
+ const extra = [];
13482
+ for (const s of ast.spines) {
13483
+ for (const lf of ast.leaves) {
13484
+ const key = [s, lf].sort().join("\u2194");
13485
+ if (!have.has(key)) {
13486
+ extra.push({ from: s, to: lf, directed: false, linkType: "copper", auto: true });
13487
+ have.add(key);
13488
+ }
13489
+ }
13490
+ }
13491
+ return [...ast.links, ...extra];
13492
+ }
13493
+ function classify(ast) {
13494
+ if (ast.devices.length === 2 && ast.links.length === 1) return "point-to-point";
13495
+ switch (ast.layout) {
13496
+ case "star":
13497
+ return "star";
13498
+ case "ring":
13499
+ return "ring";
13500
+ case "bus":
13501
+ return "bus";
13502
+ case "mesh":
13503
+ return "mesh";
13504
+ case "spine-leaf":
13505
+ return "spine-leaf";
13506
+ case "tree":
13507
+ return "tree";
13508
+ case "tiered":
13509
+ return ast.devices.some((d) => d.tier) ? "hierarchical" : "tree";
13510
+ default:
13511
+ return "general";
13512
+ }
13513
+ }
13514
+ function layoutNetwork2(ast) {
13515
+ const links = withAutoLinks(ast);
13516
+ let centers;
13517
+ switch (ast.layout) {
13518
+ case "tree":
13519
+ centers = placeBanded(ast, treeRanks(ast, links));
13520
+ break;
13521
+ case "star":
13522
+ centers = placeStar(ast, links);
13523
+ break;
13524
+ case "ring":
13525
+ centers = placeCircle(ast, NET_CONST.RING_RADIUS_MIN);
13526
+ break;
13527
+ case "mesh":
13528
+ centers = placeCircle(ast, NET_CONST.RING_RADIUS_MIN);
13529
+ break;
13530
+ case "bus":
13531
+ centers = placeBus(ast);
13532
+ break;
13533
+ case "spine-leaf":
13534
+ centers = placeSpineLeaf(ast, links);
13535
+ break;
13536
+ case "manual":
13537
+ centers = placeManual(ast);
13538
+ break;
13539
+ case "tiered":
13540
+ default:
13541
+ centers = placeBanded(ast, tieredRanks(ast, links));
13542
+ break;
13543
+ }
13544
+ const boxes = ast.devices.map((d) => {
13545
+ const fp = deviceFootprint(d);
13546
+ const c = centers.get(d.id) ?? { x: 0, y: 0 };
13547
+ return {
13548
+ device: d,
13549
+ cx: c.x,
13550
+ cy: c.y,
13551
+ x: c.x - fp.w / 2,
13552
+ y: c.y - fp.h / 2,
13553
+ w: fp.w,
13554
+ h: fp.h,
13555
+ band: 0
13556
+ };
13557
+ });
13558
+ const boxById = new Map(boxes.map((b) => [b.device.id, b]));
13559
+ const groupBoxesRaw = /* @__PURE__ */ new Map();
13560
+ const depthOf = (id) => {
13561
+ let depth = 0;
13562
+ let g = ast.groups.find((x) => x.id === id);
13563
+ while (g?.parent) {
13564
+ depth++;
13565
+ g = ast.groups.find((x) => x.id === g.parent);
13566
+ }
13567
+ return depth;
13568
+ };
13569
+ const groupsByDepth = [...ast.groups].sort((a, b) => depthOf(b.id) - depthOf(a.id));
13570
+ for (const g of groupsByDepth) {
13571
+ let l = Infinity, t = Infinity, r = -Infinity, bm = -Infinity;
13572
+ const addBox = (e) => {
13573
+ l = Math.min(l, e.left);
13574
+ t = Math.min(t, e.top);
13575
+ r = Math.max(r, e.right);
13576
+ bm = Math.max(bm, e.bottom);
13577
+ };
13578
+ for (const mid of g.members) {
13579
+ const mb = boxById.get(mid);
13580
+ if (mb) addBox(effBox(mb));
13581
+ }
13582
+ for (const cid of g.children) {
13583
+ const cb = groupBoxesRaw.get(cid);
13584
+ if (cb) addBox(cb);
13585
+ }
13586
+ if (l === Infinity) continue;
13587
+ const depth = depthOf(g.id);
13588
+ const pad = NET_CONST.GROUP_PAD;
13589
+ groupBoxesRaw.set(g.id, {
13590
+ left: l - pad,
13591
+ top: t - pad - NET_CONST.GROUP_HEADER,
13592
+ right: r + pad,
13593
+ bottom: bm + pad,
13594
+ depth
13595
+ });
13596
+ }
13597
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
13598
+ for (const b of boxes) {
13599
+ const e = effBox(b);
13600
+ minX = Math.min(minX, e.left);
13601
+ minY = Math.min(minY, e.top);
13602
+ maxX = Math.max(maxX, e.right);
13603
+ maxY = Math.max(maxY, e.bottom);
13604
+ }
13605
+ for (const gb of groupBoxesRaw.values()) {
13606
+ minX = Math.min(minX, gb.left);
13607
+ minY = Math.min(minY, gb.top);
13608
+ maxX = Math.max(maxX, gb.right);
13609
+ maxY = Math.max(maxY, gb.bottom);
13610
+ }
13611
+ if (!Number.isFinite(minX)) {
13612
+ minX = 0;
13613
+ minY = 0;
13614
+ maxX = NET_CONST.DEVICE_W;
13615
+ maxY = NET_CONST.DEVICE_H;
13616
+ }
13617
+ const dx = NET_CONST.PAD - minX;
13618
+ const dy = NET_CONST.PAD - minY;
13619
+ for (const b of boxes) {
13620
+ b.x += dx;
13621
+ b.y += dy;
13622
+ b.cx += dx;
13623
+ b.cy += dy;
13624
+ }
13625
+ const groups = [];
13626
+ for (const g of ast.groups) {
13627
+ const gb = groupBoxesRaw.get(g.id);
13628
+ if (!gb) continue;
13629
+ groups.push({
13630
+ group: g,
13631
+ x: gb.left + dx,
13632
+ y: gb.top + dy,
13633
+ w: gb.right - gb.left,
13634
+ h: gb.bottom - gb.top,
13635
+ depth: gb.depth
13636
+ });
13637
+ }
13638
+ const linkGeoms = links.map((link) => {
13639
+ const a = boxById.get(link.from);
13640
+ const b = boxById.get(link.to);
13641
+ const p1 = edgePoint(a, b.cx, b.cy);
13642
+ const p2 = edgePoint(b, a.cx, a.cy);
13643
+ return {
13644
+ link,
13645
+ points: [p1, p2],
13646
+ labelX: (p1.x + p2.x) / 2,
13647
+ labelY: (p1.y + p2.y) / 2
13648
+ };
13649
+ });
13650
+ const width = maxX - minX + 2 * NET_CONST.PAD;
13651
+ const height = maxY - minY + 2 * NET_CONST.PAD;
13652
+ return {
13653
+ ast,
13654
+ width: Math.max(width, 120),
13655
+ height: Math.max(height, 80),
13656
+ devices: boxes,
13657
+ links: linkGeoms,
13658
+ groups,
13659
+ topologyClass: classify(ast),
13660
+ warnings: ast.warnings,
13661
+ title: ast.title
13662
+ };
13663
+ }
13664
+
13665
+ // src/diagrams/network/renderer.ts
13666
+ var r22 = (n) => Math.round(n * 100) / 100;
13667
+ function buildCss7(t) {
13668
+ return `
13669
+ .sx-net { font-family: system-ui, -apple-system, sans-serif; }
13670
+ .sx-net-body { fill: ${t.deviceFill}; stroke: ${t.deviceStroke}; stroke-width: 2; }
13671
+ .sx-net-detail { fill: none; stroke: ${t.deviceStroke}; stroke-width: 1; }
13672
+ .sx-net-glyph { fill: ${t.deviceAccent}; stroke: none; }
13673
+ .sx-net-glyph-line { fill: none; stroke: ${t.deviceAccent}; stroke-width: 1.4; }
13674
+ .sx-net-icontext { font: 700 8px sans-serif; fill: ${t.deviceAccent}; }
13675
+ .sx-net-cloud-body { fill: ${t.cloudFill}; stroke: ${t.cloudStroke}; stroke-width: 2; }
13676
+ .sx-net-cloudtext { font: 600 13px sans-serif; fill: ${t.text}; }
13677
+ .sx-net-bus { stroke: ${t.deviceStroke}; stroke-width: 4; stroke-linecap: round; }
13678
+ .sx-net-label { font: 12px sans-serif; fill: ${t.label}; }
13679
+ .sx-net-sublabel { font: 10px sans-serif; fill: ${t.subLabel}; }
13680
+ .sx-net-link { fill: none; stroke-width: 2; }
13681
+ .sx-net-link-wireless, .sx-net-link-vpn { stroke-dasharray: 5 4; }
13682
+ .sx-net-link-lag { stroke-width: 3; }
13683
+ .sx-net-linklabel { font: 9px sans-serif; fill: ${t.linkLabel}; paint-order: stroke; stroke: ${t.bg}; stroke-width: 3px; stroke-linejoin: round; }
13684
+ .sx-net-port { font: 8px sans-serif; fill: ${t.linkLabel}; paint-order: stroke; stroke: ${t.bg}; stroke-width: 2.5px; stroke-linejoin: round; }
13685
+ .sx-net-boundary-site { fill: none; stroke: ${t.siteStroke}; stroke-width: 1.5; }
13686
+ .sx-net-boundary-rack { fill: none; stroke: ${t.siteStroke}; stroke-width: 1.5; stroke-dasharray: 1 2; }
13687
+ .sx-net-boundary-subnet, .sx-net-boundary-vlan { fill: ${t.subnetFill}; stroke: ${t.subnetStroke}; stroke-width: 1.2; stroke-dasharray: 5 3; }
13688
+ .sx-net-boundary-zone, .sx-net-boundary-dmz { fill: none; stroke: ${t.zoneStroke}; stroke-width: 1.2; stroke-dasharray: 5 3; }
13689
+ .sx-net-boundary-label { font: 600 10px sans-serif; }
13690
+ .sx-net-title { font: 700 16px sans-serif; fill: ${t.text}; }
13691
+ `.trim();
13692
+ }
13693
+ function linkColor(t, link) {
13694
+ if (link.linkType === "copper" && link.vlans && link.vlans.length === 1) {
13695
+ const vid = link.vlans[0];
13696
+ const pal = t.vlanPalette.filter((c) => c !== t.negative);
13697
+ const safe = pal.length ? pal : t.vlanPalette;
13698
+ return safe[vid % safe.length];
13699
+ }
13700
+ switch (link.linkType) {
13701
+ case "fiber":
13702
+ return t.linkFiber;
13703
+ case "wireless":
13704
+ return t.linkWireless;
13705
+ case "serial":
13706
+ return t.linkSerial;
13707
+ case "poe":
13708
+ return t.linkPoe;
13709
+ case "vpn":
13710
+ return t.linkVpn;
13711
+ case "lag":
13712
+ return t.linkLag;
13713
+ default:
13714
+ return t.linkCopper;
13715
+ }
10091
13716
  }
10092
- function renderSentinel(s) {
10093
- return chunk3WNW5Y7P_cjs.group({ class: "sx-pert-sentinel", "data-id": s.id }, [
10094
- chunk3WNW5Y7P_cjs.circle({ cx: s.cx, cy: s.cy, r: s.r }),
10095
- chunk3WNW5Y7P_cjs.text({ x: s.cx, y: s.cy + 3.5, "text-anchor": "middle" }, s.label)
10096
- ]);
13717
+ function annotation(link) {
13718
+ const parts = [];
13719
+ if (link.mode) parts.push(link.mode === "trunk" ? "Trunk" : "Access");
13720
+ if (link.vlans?.length) parts.push(`VLAN ${link.vlans.join(",")}`);
13721
+ if (link.speed) parts.push(link.speed);
13722
+ if (link.linkType === "poe") parts.push("PoE");
13723
+ if (link.linkType === "vpn") parts.push("VPN");
13724
+ if (link.label) parts.push(link.label);
13725
+ return parts.join(" \xB7 ");
13726
+ }
13727
+ function arrowHead(x1, y1, x2, y2, color, hs = 6) {
13728
+ const ang = Math.atan2(y2 - y1, x2 - x1);
13729
+ const a1 = ang + Math.PI - 0.45;
13730
+ const a2 = ang + Math.PI + 0.45;
13731
+ return chunk3WNW5Y7P_cjs.polygon({
13732
+ fill: color,
13733
+ points: `${r22(x2)},${r22(y2)} ${r22(x2 + hs * Math.cos(a1))},${r22(y2 + hs * Math.sin(a1))} ${r22(x2 + hs * Math.cos(a2))},${r22(y2 + hs * Math.sin(a2))}`
13734
+ });
10097
13735
  }
10098
- function renderAxis2(axis, gridTop) {
13736
+ function renderLink(lg, t) {
13737
+ const { link } = lg;
13738
+ const p1 = lg.points[0];
13739
+ const p2 = lg.points[lg.points.length - 1];
13740
+ const color = linkColor(t, link);
13741
+ const cls = `sx-net-link sx-net-link-${link.linkType}`;
10099
13742
  const parts = [];
10100
- for (const tk of axis.ticks) {
10101
- if (tk.major) {
10102
- parts.push(chunk3WNW5Y7P_cjs.line({ class: "sx-pert-grid", x1: tk.pos, y1: gridTop, x2: tk.pos, y2: axis.baseline }));
10103
- }
13743
+ if (link.linkType === "lag") {
13744
+ const ang = Math.atan2(p2.y - p1.y, p2.x - p1.x) + Math.PI / 2;
13745
+ const ox = 1.8 * Math.cos(ang);
13746
+ const oy = 1.8 * Math.sin(ang);
13747
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: cls, stroke: color, x1: r22(p1.x + ox), y1: r22(p1.y + oy), x2: r22(p2.x + ox), y2: r22(p2.y + oy) }));
13748
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: cls, stroke: color, x1: r22(p1.x - ox), y1: r22(p1.y - oy), x2: r22(p2.x - ox), y2: r22(p2.y - oy) }));
13749
+ } else {
13750
+ parts.push(chunk3WNW5Y7P_cjs.el("polyline", { class: cls, stroke: color, points: lg.points.map((p) => `${r22(p.x)},${r22(p.y)}`).join(" ") }));
10104
13751
  }
10105
- parts.push(chunk3WNW5Y7P_cjs.line({ class: "baseline", x1: axis.start, y1: axis.baseline, x2: axis.end, y2: axis.baseline }));
10106
- for (const tk of axis.ticks) {
10107
- const len = tk.major ? 7 : 4;
10108
- parts.push(chunk3WNW5Y7P_cjs.line({ x1: tk.pos, y1: axis.baseline, x2: tk.pos, y2: axis.baseline + len }));
10109
- if (tk.major) {
10110
- parts.push(
10111
- chunk3WNW5Y7P_cjs.text({ x: tk.pos, y: axis.baseline + 18, "text-anchor": "middle" }, fmtVal(tk.value))
10112
- );
13752
+ if (link.directed) parts.push(arrowHead(p1.x, p1.y, p2.x, p2.y, color));
13753
+ if (link.linkType === "fiber") {
13754
+ const ang = Math.atan2(p2.y - p1.y, p2.x - p1.x) + Math.PI / 2;
13755
+ for (const f of [0.4, 0.55]) {
13756
+ const mx = p1.x + (p2.x - p1.x) * f;
13757
+ const my = p1.y + (p2.y - p1.y) * f;
13758
+ parts.push(chunk3WNW5Y7P_cjs.line({ class: cls, stroke: color, x1: r22(mx - 3 * Math.cos(ang)), y1: r22(my - 3 * Math.sin(ang)), x2: r22(mx + 3 * Math.cos(ang)), y2: r22(my + 3 * Math.sin(ang)) }));
10113
13759
  }
10114
13760
  }
10115
- return chunk3WNW5Y7P_cjs.group({ class: "sx-pert-axis" }, parts);
13761
+ const ann = annotation(link);
13762
+ if (ann) parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-net-linklabel", x: r22(lg.labelX), y: r22(lg.labelY - 3), "text-anchor": "middle" }, ann));
13763
+ if (link.portNear) parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-net-port", x: r22(p1.x + (p2.x - p1.x) * 0.16), y: r22(p1.y + (p2.y - p1.y) * 0.16 - 3), "text-anchor": "middle" }, link.portNear));
13764
+ if (link.portFar) parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-net-port", x: r22(p2.x + (p1.x - p2.x) * 0.16), y: r22(p2.y + (p1.y - p2.y) * 0.16 - 3), "text-anchor": "middle" }, link.portFar));
13765
+ return chunk3WNW5Y7P_cjs.group(
13766
+ {
13767
+ class: "sx-net-link-g",
13768
+ "data-from": link.from,
13769
+ "data-to": link.to,
13770
+ "data-type": link.linkType,
13771
+ ...link.vlans?.length ? { "data-vlan": link.vlans.join(",") } : {},
13772
+ ...link.speed ? { "data-speed": link.speed } : {},
13773
+ ...link.mode ? { "data-mode": link.mode } : {}
13774
+ },
13775
+ parts
13776
+ );
10116
13777
  }
10117
- function renderAoa(aoa) {
10118
- const arcEls = [];
10119
- const labelEls = [];
10120
- for (const a of aoa.arcs) {
10121
- const cls = `sx-pert-aoa-arc${a.dummy ? " dummy" : ""}${a.critical ? " critical" : ""}`;
10122
- const marker = a.critical ? "url(#sx-pert-arrow-crit)" : "url(#sx-pert-arrow)";
10123
- arcEls.push(
10124
- chunk3WNW5Y7P_cjs.path({
10125
- class: cls,
10126
- d: a.d,
10127
- "marker-end": marker,
10128
- "data-from": a.from,
10129
- "data-to": a.to,
10130
- "data-task": a.taskId ?? "",
10131
- "data-dummy": String(a.dummy),
10132
- "data-critical": String(a.critical)
10133
- })
10134
- );
10135
- if (!a.dummy && a.label) {
10136
- const critCls = a.critical ? " critical" : "";
10137
- const durStr = fmtVal(a.duration ?? 0);
10138
- const w = Math.max(a.label.length * 6.2, durStr.length * 6) + 8;
10139
- labelEls.push(
10140
- chunk3WNW5Y7P_cjs.rect({ class: "sx-pert-edge-halo", x: a.labelX - w / 2, y: a.labelY - 16, width: w, height: 31, rx: 3, ry: 3 })
10141
- );
10142
- labelEls.push(
10143
- chunk3WNW5Y7P_cjs.text({ class: `sx-pert-aoa-name${critCls}`, x: a.labelX, y: a.labelY - 4, "text-anchor": "middle" }, a.label)
10144
- );
10145
- labelEls.push(
10146
- chunk3WNW5Y7P_cjs.text({ class: `sx-pert-aoa-dur${critCls}`, x: a.labelX, y: a.labelY + 12, "text-anchor": "middle" }, durStr)
10147
- );
10148
- }
10149
- }
10150
- const eventEls = [];
10151
- for (const e of aoa.events) {
10152
- eventEls.push(
10153
- chunk3WNW5Y7P_cjs.group({ class: `sx-pert-aoa-event${e.critical ? " critical" : ""}`, "data-event": e.id }, [
10154
- chunk3WNW5Y7P_cjs.circle({ cx: e.x, cy: e.y, r: e.r }),
10155
- chunk3WNW5Y7P_cjs.text({ x: e.x, y: e.y + 4.5, "text-anchor": "middle" }, String(e.id))
10156
- ])
10157
- );
10158
- }
10159
- return chunk3WNW5Y7P_cjs.group({ class: "sx-pert-aoa" }, [
10160
- chunk3WNW5Y7P_cjs.group({ class: "sx-pert-aoa-arcs" }, arcEls),
10161
- chunk3WNW5Y7P_cjs.group({ class: "sx-pert-aoa-labels" }, labelEls),
10162
- chunk3WNW5Y7P_cjs.group({ class: "sx-pert-aoa-events" }, eventEls)
10163
- ]);
13778
+ function renderGroup(gb, t) {
13779
+ const k = gb.group.kind;
13780
+ const cls = `sx-net-boundary-${k}`;
13781
+ const labelColor = k === "zone" || k === "dmz" ? t.zoneStroke : k === "subnet" || k === "vlan" ? t.subnetStroke : t.siteStroke;
13782
+ const label = gb.group.label ?? gb.group.id;
13783
+ const tag = k === "vlan" ? `VLAN ${label}` : label;
13784
+ return chunk3WNW5Y7P_cjs.group(
13785
+ { class: "sx-net-boundary", "data-kind": k, "data-label": chunk3WNW5Y7P_cjs.escapeXml(label) },
13786
+ [
13787
+ chunk3WNW5Y7P_cjs.rect({ class: cls, x: r22(gb.x), y: r22(gb.y), width: r22(gb.w), height: r22(gb.h), rx: 8, ry: 8 }),
13788
+ chunk3WNW5Y7P_cjs.text({ class: "sx-net-boundary-label", fill: labelColor, x: r22(gb.x + NET_CONST.GROUP_LABEL_INSET), y: r22(gb.y + 13) }, tag)
13789
+ ]
13790
+ );
10164
13791
  }
10165
- function summaryText(summary) {
10166
- const u = unitWord(summary.unit);
10167
- const dur = `${fmtVal(summary.projectDuration)}${u ? " " + u : ""}`;
10168
- const sigma = summary.projectStdDev !== void 0 ? ` \xB7 \u03C3 \u2248 ${fmtVal(summary.projectStdDev)}` : "";
10169
- const plain = `Project duration ${dur} \xB7 ${summary.taskCount} tasks \xB7 ${summary.depCount} dependencies \xB7 ${summary.criticalCount} critical${sigma}`;
10170
- const critPath = summary.criticalPath.map((id) => id).join(" \u2192 ");
10171
- return { plain, critPath };
13792
+ function renderDevice(b, t) {
13793
+ const d = b.device;
13794
+ const parts = [drawDeviceIcon(d, { x: b.x, y: b.y, w: b.w, h: b.h })];
13795
+ if (!isCloudKind(d.kind)) {
13796
+ const labelY = b.y + b.h + NET_CONST.LABEL_GAP + 11;
13797
+ parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-net-label", x: r22(b.cx), y: r22(labelY), "text-anchor": "middle" }, d.label ?? d.id));
13798
+ const sub = d.ip ?? d.model;
13799
+ if (sub) parts.push(chunk3WNW5Y7P_cjs.text({ class: "sx-net-sublabel", x: r22(b.cx), y: r22(labelY + NET_CONST.SUBLABEL_H), "text-anchor": "middle" }, sub));
13800
+ }
13801
+ const attrs = {
13802
+ class: "sx-net-device",
13803
+ "data-id": d.id,
13804
+ "data-kind": d.kind
13805
+ };
13806
+ if (d.tier) attrs["data-tier"] = d.tier;
13807
+ if (d.ip) attrs["data-ip"] = d.ip;
13808
+ if (d.cameraType) attrs["data-type"] = d.cameraType;
13809
+ return chunk3WNW5Y7P_cjs.group(attrs, parts);
10172
13810
  }
10173
- function renderPertLayout(layout, config) {
10174
- const t = chunkD7EHZFK4_cjs.resolveBaseTheme(config?.theme ?? "default");
13811
+ function renderNetworkLayout(layout, config) {
13812
+ const t = chunkL7POWM5B_cjs.resolveNetworkTheme(config?.theme ?? "default");
10175
13813
  const children = [];
10176
- const cp = layout.summary.criticalPath;
10177
- children.push(chunk3WNW5Y7P_cjs.title(`PERT network${layout.title ? " \u2014 " + layout.title : ""}`));
10178
- children.push(
10179
- chunk3WNW5Y7P_cjs.desc(
10180
- `${layout.summary.taskCount} activities, project duration ${fmtVal(layout.summary.projectDuration)} ${unitWord(layout.unit)}` + (cp.length ? `, critical path ${cp.join(" \u2192 ")}` : "") + "."
10181
- )
10182
- );
10183
- children.push(chunk3WNW5Y7P_cjs.el("style", {}, buildCss4(t)));
10184
- children.push(markers2());
13814
+ const counts = /* @__PURE__ */ new Map();
13815
+ for (const b of layout.devices) counts.set(b.device.kind, (counts.get(b.device.kind) ?? 0) + 1);
13816
+ const linkTypes = /* @__PURE__ */ new Map();
13817
+ for (const l of layout.links) linkTypes.set(l.link.linkType, (linkTypes.get(l.link.linkType) ?? 0) + 1);
13818
+ const descParts = [
13819
+ `${layout.devices.length} devices, ${layout.links.length} links, ${layout.groups.length} boundaries.`,
13820
+ `Topology: ${layout.topologyClass}.`,
13821
+ linkTypes.size ? `Links: ${[...linkTypes].map(([k, n]) => `${n} ${k}`).join(", ")}.` : "",
13822
+ layout.warnings.length ? `Warnings: ${layout.warnings.join("; ")}.` : ""
13823
+ ].filter(Boolean);
13824
+ children.push(chunk3WNW5Y7P_cjs.title(`Network diagram${layout.title ? " \u2014 " + layout.title : ""}`));
13825
+ children.push(chunk3WNW5Y7P_cjs.desc(descParts.join(" ")));
13826
+ children.push(chunk3WNW5Y7P_cjs.el("style", {}, buildCss7(t)));
13827
+ const titleBand = layout.title ? 30 : 0;
10185
13828
  if (layout.title) {
10186
- children.push(
10187
- chunk3WNW5Y7P_cjs.text({ x: layout.width / 2, y: 26, class: "sx-pert-title", "text-anchor": "middle" }, layout.title)
10188
- );
10189
- }
10190
- if (layout.aoa) {
10191
- children.push(renderAoa(layout.aoa));
10192
- } else {
10193
- if (layout.lanes && layout.lanes.length) {
10194
- children.push(renderLanes(layout.lanes, layout.width));
10195
- }
10196
- if (layout.axis) {
10197
- let minBoxY = Infinity;
10198
- for (const b of layout.boxes) minBoxY = Math.min(minBoxY, b.y);
10199
- children.push(renderAxis2(layout.axis, isFinite(minBoxY) ? minBoxY : layout.axis.baseline - 20));
10200
- }
10201
- const edgeEls = [];
10202
- for (const e of layout.edges) edgeEls.push(renderEdge3(e));
10203
- children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-edges" }, edgeEls));
10204
- if (layout.sentinels.length) {
10205
- children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-sentinels" }, layout.sentinels.map(renderSentinel)));
10206
- }
10207
- const boxEls = [];
10208
- for (const b of layout.boxes) boxEls.push(renderBox2(b, layout.mode));
10209
- children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-tasks" }, boxEls));
10210
- const labelEls = [];
10211
- for (const e of layout.edges) {
10212
- const lbl = renderEdgeLabel2(e);
10213
- if (lbl) labelEls.push(lbl);
10214
- }
10215
- children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-labels" }, labelEls));
13829
+ children.push(chunk3WNW5Y7P_cjs.text({ x: r22(layout.width / 2), y: 21, class: "sx-net-title", "text-anchor": "middle" }, layout.title));
10216
13830
  }
10217
- const { plain, critPath } = summaryText(layout.summary);
10218
- const footerY = layout.height - 12;
10219
- const footerParts = [
10220
- chunk3WNW5Y7P_cjs.text({ class: "sx-pert-summary", x: PERT_PAD, y: footerY, "text-anchor": "start" }, plain)
10221
- ];
10222
- if (critPath) {
10223
- footerParts.push(
10224
- chunk3WNW5Y7P_cjs.text(
10225
- { class: "sx-pert-summary crit", x: layout.width - PERT_PAD, y: footerY, "text-anchor": "end" },
10226
- `Critical path: ${critPath}`
10227
- )
10228
- );
10229
- }
10230
- children.push(chunk3WNW5Y7P_cjs.group({ class: "sx-pert-footer" }, footerParts));
13831
+ const body = [];
13832
+ const sortedGroups = [...layout.groups].sort((a, b) => a.depth - b.depth);
13833
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-net-boundaries" }, sortedGroups.map((g) => renderGroup(g, t))));
13834
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-net-links" }, layout.links.map((l) => renderLink(l, t))));
13835
+ body.push(chunk3WNW5Y7P_cjs.group({ class: "sx-net-devices" }, layout.devices.map((b) => renderDevice(b))));
13836
+ children.push(titleBand ? chunk3WNW5Y7P_cjs.group({ transform: `translate(0, ${titleBand})` }, body) : chunk3WNW5Y7P_cjs.group({}, body));
13837
+ const height = layout.height + titleBand;
10231
13838
  return chunk3WNW5Y7P_cjs.svgRoot(
10232
13839
  {
10233
- class: "sx-pert",
13840
+ class: "sx-net",
10234
13841
  role: "img",
10235
- "aria-label": chunk3WNW5Y7P_cjs.escapeXml(layout.title ?? "PERT network diagram"),
10236
- width: layout.width,
10237
- height: layout.height,
10238
- viewBox: `0 0 ${layout.width} ${layout.height}`,
10239
- "data-diagram-type": "pert"
13842
+ "aria-label": chunk3WNW5Y7P_cjs.escapeXml(layout.title ?? "Network diagram"),
13843
+ width: r22(layout.width),
13844
+ height: r22(height),
13845
+ viewBox: `0 0 ${r22(layout.width)} ${r22(height)}`,
13846
+ "data-diagram-type": "network"
10240
13847
  },
10241
13848
  children
10242
13849
  );
10243
13850
  }
10244
- var PERT_PAD = 24;
10245
- function renderPert(textOrAst, config) {
10246
- const ast = typeof textOrAst === "string" ? parsePert(textOrAst) : textOrAst;
10247
- const schedule = schedulePert(ast);
10248
- const layout = layoutPert(ast, schedule);
10249
- return renderPertLayout(layout, config);
13851
+ function renderNetwork(textOrAst, config) {
13852
+ const ast = typeof textOrAst === "string" ? parseNetwork(textOrAst) : textOrAst;
13853
+ const layout = layoutNetwork2(ast);
13854
+ return renderNetworkLayout(layout, config);
10250
13855
  }
10251
13856
 
10252
- // src/diagrams/pert/index.ts
10253
- var pert = {
10254
- type: "pert",
13857
+ // src/diagrams/network/index.ts
13858
+ var network = {
13859
+ type: "network",
10255
13860
  detect(text2) {
10256
13861
  for (const raw of text2.split(/\r?\n/)) {
10257
13862
  const t = raw.trim();
10258
13863
  if (!t) continue;
10259
13864
  if (t.startsWith("#") || t.startsWith("//")) continue;
10260
- return /^pert\b/i.test(t);
13865
+ return /^(network|topology)\b/i.test(t);
10261
13866
  }
10262
13867
  return false;
10263
13868
  },
10264
- parse: parsePert,
13869
+ parse: parseNetwork,
10265
13870
  render(text2, config) {
10266
- return renderPert(text2, config);
13871
+ return renderNetwork(text2, config);
10267
13872
  }
10268
13873
  };
10269
13874
 
13875
+ // src/core/diagnostics.ts
13876
+ var ENGINE_BUG_NAMES = /* @__PURE__ */ new Set([
13877
+ "ReferenceError",
13878
+ "TypeError",
13879
+ "RangeError"
13880
+ ]);
13881
+ function diagnosticFromError(err) {
13882
+ if (err instanceof Error) {
13883
+ const anyErr = err;
13884
+ const hasParseFields = typeof anyErr.line === "number";
13885
+ const isEngineBug = !hasParseFields && ENGINE_BUG_NAMES.has(err.name);
13886
+ const source = typeof anyErr.source === "string" ? anyErr.source : isEngineBug ? firstStackFrame(err.stack) : void 0;
13887
+ return {
13888
+ severity: "error",
13889
+ code: isEngineBug ? "ENGINE_BUG" : "DSL_INVALID",
13890
+ line: typeof anyErr.line === "number" ? anyErr.line : void 0,
13891
+ column: typeof anyErr.column === "number" ? anyErr.column : void 0,
13892
+ source,
13893
+ message: isEngineBug ? `[engine bug: ${err.name}] ${err.message}` : err.message,
13894
+ hint: typeof anyErr.hint === "string" ? anyErr.hint : isEngineBug ? "This looks like a Schematex internal error rather than a DSL syntax problem. Keep the failing DSL and file an issue." : void 0,
13895
+ fatal: true
13896
+ };
13897
+ }
13898
+ return {
13899
+ severity: "error",
13900
+ code: "UNKNOWN_THROW",
13901
+ message: String(err),
13902
+ fatal: true
13903
+ };
13904
+ }
13905
+ function renderDiagnosticSvg(diagnostics, type, config = {}) {
13906
+ const headline = type ? `${type} preview could not be rendered` : "Diagram preview could not be rendered";
13907
+ const detail = diagnostics[0]?.message ?? "Schematex could not parse this DSL.";
13908
+ const lines = wrapText3(detail, 88).slice(0, 5);
13909
+ const source = diagnostics[0]?.source ? wrapText3(`Source: ${diagnostics[0].source}`, 88).slice(0, 2) : [];
13910
+ const foot = "Strict validation failed before a diagram could be drawn.";
13911
+ const width = 760;
13912
+ const lineHeight = 20;
13913
+ const height = 174 + (lines.length + source.length) * lineHeight;
13914
+ const fontFamily = config.fontFamily ?? "system-ui, -apple-system, sans-serif";
13915
+ const children = [
13916
+ chunk3WNW5Y7P_cjs.title(headline),
13917
+ chunk3WNW5Y7P_cjs.desc(`${headline}. ${detail}`),
13918
+ chunk3WNW5Y7P_cjs.el(
13919
+ "style",
13920
+ {},
13921
+ `
13922
+ .schematex-preview-error { font-family: ${chunk3WNW5Y7P_cjs.escapeXml(fontFamily)}; }
13923
+ .schematex-preview-error-frame { fill: #fff7ed; stroke: #c2410c; stroke-width: 1.5; }
13924
+ .schematex-preview-error-mark { fill: #c2410c; }
13925
+ .schematex-preview-error-title { fill: #7c2d12; font-size: 18px; font-weight: 600; }
13926
+ .schematex-preview-error-copy { fill: #431407; font-size: 13px; }
13927
+ .schematex-preview-error-muted { fill: #9a3412; font-size: 12px; }
13928
+ `
13929
+ ),
13930
+ chunk3WNW5Y7P_cjs.el("g", { class: "schematex-preview-error" }, [
13931
+ chunk3WNW5Y7P_cjs.rect({
13932
+ x: 1,
13933
+ y: 1,
13934
+ width: width - 2,
13935
+ height: height - 2,
13936
+ rx: 6,
13937
+ class: "schematex-preview-error-frame"
13938
+ }),
13939
+ chunk3WNW5Y7P_cjs.rect({
13940
+ x: 28,
13941
+ y: 30,
13942
+ width: 8,
13943
+ height: 34,
13944
+ rx: 4,
13945
+ class: "schematex-preview-error-mark"
13946
+ }),
13947
+ chunk3WNW5Y7P_cjs.rect({
13948
+ x: 28,
13949
+ y: 72,
13950
+ width: 8,
13951
+ height: 8,
13952
+ rx: 4,
13953
+ class: "schematex-preview-error-mark"
13954
+ }),
13955
+ chunk3WNW5Y7P_cjs.text(
13956
+ { x: 58, y: 49, class: "schematex-preview-error-title" },
13957
+ headline
13958
+ ),
13959
+ chunk3WNW5Y7P_cjs.text(
13960
+ { x: 58, y: 76, class: "schematex-preview-error-muted" },
13961
+ "The DSL is still available to repair or retry."
13962
+ ),
13963
+ ...renderRows(lines, 58, 112, lineHeight, "schematex-preview-error-copy"),
13964
+ ...renderRows(
13965
+ source,
13966
+ 58,
13967
+ 112 + lines.length * lineHeight + 10,
13968
+ lineHeight,
13969
+ "schematex-preview-error-muted"
13970
+ ),
13971
+ chunk3WNW5Y7P_cjs.text(
13972
+ { x: 58, y: height - 28, class: "schematex-preview-error-muted" },
13973
+ foot
13974
+ )
13975
+ ])
13976
+ ];
13977
+ return chunk3WNW5Y7P_cjs.svgRoot(
13978
+ {
13979
+ width,
13980
+ height,
13981
+ viewBox: `0 0 ${width} ${height}`,
13982
+ role: "img",
13983
+ "aria-label": headline,
13984
+ "data-schematex-status": "invalid"
13985
+ },
13986
+ children
13987
+ );
13988
+ }
13989
+ function renderRows(rows, x, y, lineHeight, className) {
13990
+ return rows.map(
13991
+ (row, idx) => chunk3WNW5Y7P_cjs.text({ x, y: y + idx * lineHeight, class: className }, row)
13992
+ );
13993
+ }
13994
+ function wrapText3(value, max) {
13995
+ const words = value.replace(/\s+/g, " ").trim().split(" ");
13996
+ const rows = [];
13997
+ let current = "";
13998
+ for (const word of words) {
13999
+ if (!current) {
14000
+ current = word;
14001
+ continue;
14002
+ }
14003
+ if (current.length + word.length + 1 <= max) {
14004
+ current += ` ${word}`;
14005
+ continue;
14006
+ }
14007
+ rows.push(current);
14008
+ current = word;
14009
+ }
14010
+ if (current) rows.push(current);
14011
+ return rows.length > 0 ? rows : [value];
14012
+ }
14013
+ function firstStackFrame(stack) {
14014
+ if (!stack) return void 0;
14015
+ for (const line2 of stack.split("\n")) {
14016
+ const trimmed = line2.trim();
14017
+ if (trimmed.startsWith("at ")) {
14018
+ return trimmed.replace(/\((?:.*\/)?([^/]+)\)/, "($1)");
14019
+ }
14020
+ }
14021
+ return void 0;
14022
+ }
14023
+
10270
14024
  // src/diagrams/mindmap/inline.ts
10271
14025
  var RE_TASK = /^\[( |x|X)\]\s+(.*)$/;
10272
14026
  function tokenizeInline(raw) {
@@ -10983,7 +14737,7 @@ function renderNode2(n, color, theme, fontFamily) {
10983
14737
  );
10984
14738
  }
10985
14739
  function renderMindmapAST(ast, themeName = "default", fontFamily = "system-ui, -apple-system, sans-serif") {
10986
- const theme = chunkD7EHZFK4_cjs.resolveMindmapTheme(ast.themeOverride ?? themeName);
14740
+ const theme = chunkL7POWM5B_cjs.resolveMindmapTheme(ast.themeOverride ?? themeName);
10987
14741
  const layout = layoutMindmap(ast);
10988
14742
  const byId = new Map(layout.nodes.map((n) => [n.node.id, n]));
10989
14743
  const edgeSvgs = [];
@@ -11236,7 +14990,7 @@ function newAST() {
11236
14990
  config: { ...DEFAULT_CONFIG }
11237
14991
  };
11238
14992
  }
11239
- function stripQuotes3(s) {
14993
+ function stripQuotes4(s) {
11240
14994
  const t = s.trim();
11241
14995
  if (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'")) {
11242
14996
  return t.slice(1, -1);
@@ -11257,11 +15011,11 @@ function readQuoted2(line2, from) {
11257
15011
  function parseAxis(raw) {
11258
15012
  const arrowMatch = raw.match(/\s*(→|↑|->|>|↓|←|<-|<)\s*/);
11259
15013
  if (arrowMatch) {
11260
- const arrow = arrowMatch[1];
15014
+ const arrow2 = arrowMatch[1];
11261
15015
  const idx = arrowMatch.index;
11262
15016
  const left = raw.slice(0, idx).trim();
11263
15017
  const right = raw.slice(idx + arrowMatch[0].length).trim();
11264
- const reversed = arrow === "\u2190" || arrow === "<-" || arrow === "<";
15018
+ const reversed = arrow2 === "\u2190" || arrow2 === "<-" || arrow2 === "<";
11265
15019
  if (reversed) {
11266
15020
  return { low: right, high: left, reversed: true };
11267
15021
  }
@@ -11272,7 +15026,7 @@ function parseAxis(raw) {
11272
15026
  function parseNumberList2(raw) {
11273
15027
  const t = raw.trim();
11274
15028
  const inner = t.startsWith("[") && t.endsWith("]") ? t.slice(1, -1) : t;
11275
- return inner.split(",").map((s) => stripQuotes3(s.trim())).filter((s) => s.length > 0);
15029
+ return inner.split(",").map((s) => stripQuotes4(s.trim())).filter((s) => s.length > 0);
11276
15030
  }
11277
15031
  function parseProperties2(raw, point) {
11278
15032
  let i = 0;
@@ -11387,7 +15141,7 @@ function parseHeader2(line2, ast) {
11387
15141
  ast.cols = Number(heatMatch[1]);
11388
15142
  ast.rows = Number(heatMatch[2]);
11389
15143
  const title2 = heatMatch[3].trim();
11390
- if (title2) ast.title = stripQuotes3(title2);
15144
+ if (title2) ast.title = stripQuotes4(title2);
11391
15145
  return void 0;
11392
15146
  }
11393
15147
  const corrMatch = rest.match(/^correlation\s*(?:(\d+)\s*x\s*(\d+))?\s*(.*)$/i);
@@ -11399,7 +15153,7 @@ function parseHeader2(line2, ast) {
11399
15153
  ast.rows = Number(corrMatch[2]);
11400
15154
  }
11401
15155
  const title2 = corrMatch[3].trim();
11402
- if (title2) ast.title = stripQuotes3(title2);
15156
+ if (title2) ast.title = stripQuotes4(title2);
11403
15157
  return void 0;
11404
15158
  }
11405
15159
  const tokenMatch = rest.match(/^([a-zA-Z0-9_-]+)\s*(.*)$/);
@@ -11407,13 +15161,13 @@ function parseHeader2(line2, ast) {
11407
15161
  const tok = tokenMatch[1].toLowerCase();
11408
15162
  const remainder = tokenMatch[2].trim();
11409
15163
  if (TEMPLATE_NAMES.has(tok)) {
11410
- if (remainder) ast.title = stripQuotes3(remainder);
15164
+ if (remainder) ast.title = stripQuotes4(remainder);
11411
15165
  return tok;
11412
15166
  }
11413
15167
  if (rest.startsWith('"') || rest.startsWith("'")) {
11414
- ast.title = stripQuotes3(rest);
15168
+ ast.title = stripQuotes4(rest);
11415
15169
  } else if (rest.length > 0) {
11416
- ast.title = stripQuotes3(rest);
15170
+ ast.title = stripQuotes4(rest);
11417
15171
  }
11418
15172
  }
11419
15173
  return void 0;
@@ -11449,7 +15203,7 @@ function parseMatrix(text2) {
11449
15203
  inConfig = false;
11450
15204
  }
11451
15205
  if (/^title\s*:/i.test(line2)) {
11452
- st.ast.title = stripQuotes3(line2.replace(/^title\s*:\s*/i, ""));
15206
+ st.ast.title = stripQuotes4(line2.replace(/^title\s*:\s*/i, ""));
11453
15207
  continue;
11454
15208
  }
11455
15209
  if (/^x-axis\s*:/i.test(line2)) {
@@ -11534,7 +15288,7 @@ function parseMatrix(text2) {
11534
15288
  if (qShort) {
11535
15289
  const q = Number(qShort[1]);
11536
15290
  const labelRaw = qShort[2].trim();
11537
- const label = stripQuotes3(labelRaw);
15291
+ const label = stripQuotes4(labelRaw);
11538
15292
  const cell = quadrantToCell(q);
11539
15293
  st.ast.cellLabels.push({ col: cell.col, row: cell.row, label });
11540
15294
  continue;
@@ -11576,11 +15330,11 @@ var CANVAS_W = 720;
11576
15330
  var CANVAS_H = 560;
11577
15331
  var PADDING_X = 110;
11578
15332
  var PADDING_Y = 90;
11579
- var CHAR_W = 6.2;
15333
+ var CHAR_W2 = 6.2;
11580
15334
  var LABEL_H = 14;
11581
15335
  function estimateWidth(text2) {
11582
15336
  const cjk = (text2.match(/[\u3000-\u9fff]/g) ?? []).length;
11583
- return (text2.length - cjk) * CHAR_W + cjk * 12 + 8;
15337
+ return (text2.length - cjk) * CHAR_W2 + cjk * 12 + 8;
11584
15338
  }
11585
15339
  function clamp01(v) {
11586
15340
  return Math.max(0.02, Math.min(0.98, v));
@@ -12631,7 +16385,7 @@ var ErdParseError = class extends Error {
12631
16385
  }
12632
16386
  lineNumber;
12633
16387
  };
12634
- function stripComment2(s) {
16388
+ function stripComment3(s) {
12635
16389
  let out = "";
12636
16390
  let inQuote = false;
12637
16391
  for (let i = 0; i < s.length; i++) {
@@ -12685,7 +16439,7 @@ function parseCardToken(raw, side) {
12685
16439
  }
12686
16440
  function lex(text2) {
12687
16441
  return text2.split(/\r?\n/).map((raw, i) => ({
12688
- text: stripComment2(raw).trim(),
16442
+ text: stripComment3(raw).trim(),
12689
16443
  lineNumber: i + 1
12690
16444
  })).filter((l) => l.text.length > 0);
12691
16445
  }
@@ -13336,7 +17090,7 @@ function sideAnchor(e, side, col) {
13336
17090
  }
13337
17091
 
13338
17092
  // src/diagrams/erd/renderer.ts
13339
- function buildCss5(t) {
17093
+ function buildCss8(t) {
13340
17094
  return `
13341
17095
  .lt-erd { font-family: system-ui, -apple-system, sans-serif; }
13342
17096
  .lt-erd-title { font: bold 16px sans-serif; fill: ${t.text}; }
@@ -13422,10 +17176,10 @@ function renderEntity(e) {
13422
17176
  a.name
13423
17177
  )
13424
17178
  );
13425
- const markers3 = attrMarkers(a);
17179
+ const markers5 = attrMarkers(a);
13426
17180
  const markerW = 26;
13427
17181
  const markerGap = 4;
13428
- const markersBlockW = markers3.length * markerW + (markers3.length - 1) * markerGap;
17182
+ const markersBlockW = markers5.length * markerW + (markers5.length - 1) * markerGap;
13429
17183
  const markersStartX = e.x + e.width - C2.ENTITY_PADDING_X - markersBlockW;
13430
17184
  if (a.type) {
13431
17185
  body.push(
@@ -13440,8 +17194,8 @@ function renderEntity(e) {
13440
17194
  )
13441
17195
  );
13442
17196
  }
13443
- for (let j = 0; j < markers3.length; j++) {
13444
- const m = markers3[j];
17197
+ for (let j = 0; j < markers5.length; j++) {
17198
+ const m = markers5[j];
13445
17199
  const px = markersStartX + j * (markerW + markerGap);
13446
17200
  body.push(
13447
17201
  chunk3WNW5Y7P_cjs.rect({
@@ -13644,9 +17398,9 @@ function renderEdge4(edge) {
13644
17398
  return chunk3WNW5Y7P_cjs.group({ class: "lt-erd-edge-group" }, parts);
13645
17399
  }
13646
17400
  function renderErdAst(result, themeName = "default") {
13647
- const theme = chunkD7EHZFK4_cjs.resolveBaseTheme(themeName);
17401
+ const theme = chunkL7POWM5B_cjs.resolveBaseTheme(themeName);
13648
17402
  const { entities, edges, width, height, ast } = result;
13649
- const cssBlock = chunk3WNW5Y7P_cjs.el("style", {}, buildCss5(theme));
17403
+ const cssBlock = chunk3WNW5Y7P_cjs.el("style", {}, buildCss8(theme));
13650
17404
  const titleNode = chunk3WNW5Y7P_cjs.title(ast.title ?? "Schematex ERD");
13651
17405
  const descNode = chunk3WNW5Y7P_cjs.desc(
13652
17406
  `Entity-Relationship Diagram with ${entities.length} entities and ${edges.length} relationships.`
@@ -13713,7 +17467,7 @@ var BreadboardParseError = class extends Error {
13713
17467
  }
13714
17468
  lineNumber;
13715
17469
  };
13716
- function stripComment3(s) {
17470
+ function stripComment4(s) {
13717
17471
  let out = "";
13718
17472
  let inQuote = false;
13719
17473
  for (let i = 0; i < s.length; i++) {
@@ -13734,7 +17488,7 @@ function unquote6(v) {
13734
17488
  }
13735
17489
  function lex2(text2) {
13736
17490
  return text2.split(/\r?\n/).map((raw, i) => ({
13737
- text: stripComment3(raw).trimEnd().replace(/^\s+/, ""),
17491
+ text: stripComment4(raw).trimEnd().replace(/^\s+/, ""),
13738
17492
  lineNumber: i + 1
13739
17493
  })).filter((l) => l.text.length > 0);
13740
17494
  }
@@ -13781,7 +17535,7 @@ function parsePlacement(rest, lineNumber) {
13781
17535
  }
13782
17536
  return { kind: "point", at: parseCoord(body, lineNumber) };
13783
17537
  }
13784
- var KIND_ALIASES = {
17538
+ var KIND_ALIASES2 = {
13785
17539
  resistor: "resistor",
13786
17540
  led: "led",
13787
17541
  cap: "cap-elec",
@@ -13846,7 +17600,7 @@ function resolveKind(tokens, lineNumber) {
13846
17600
  if (!ACTUATOR_SUBTYPES[sub]) throw new BreadboardParseError(`Unknown actuator subtype '${sub}'`, lineNumber);
13847
17601
  return { kind: ACTUATOR_SUBTYPES[sub], consumed: 2 };
13848
17602
  }
13849
- if (KIND_ALIASES[head]) return { kind: KIND_ALIASES[head], consumed: 1 };
17603
+ if (KIND_ALIASES2[head]) return { kind: KIND_ALIASES2[head], consumed: 1 };
13850
17604
  throw new BreadboardParseError(`Unknown part kind '${head}'`, lineNumber);
13851
17605
  }
13852
17606
  function parsePart(rawLine, lineNumber) {
@@ -14716,7 +18470,7 @@ var WIRE_COLOR_MAP = {
14716
18470
  brown: "#78350f",
14717
18471
  grey: "#64748b"
14718
18472
  };
14719
- function buildCss6(t) {
18473
+ function buildCss9(t) {
14720
18474
  return `
14721
18475
  .lt-bb { font-family: system-ui, -apple-system, sans-serif; }
14722
18476
  .lt-bb-title { font: 600 16px sans-serif; fill: ${t.text}; }
@@ -14830,12 +18584,12 @@ function renderSubstrate(sub) {
14830
18584
  function renderPart(lp) {
14831
18585
  const spec = partSpec(lp.part.kind, lp.part.args);
14832
18586
  const body = spec.body(lp.part, lp.width, lp.height);
14833
- const labelText = lp.part.label ?? defaultPartLabel(lp);
14834
- const labelEl = labelText ? chunk3WNW5Y7P_cjs.text({
18587
+ const labelText2 = lp.part.label ?? defaultPartLabel(lp);
18588
+ const labelEl = labelText2 ? chunk3WNW5Y7P_cjs.text({
14835
18589
  x: lp.x + lp.width / 2,
14836
18590
  y: spec.category === "side" ? lp.y - 6 : lp.y - 4,
14837
18591
  class: "lt-bb-part-label"
14838
- }, labelText) : "";
18592
+ }, labelText2) : "";
14839
18593
  return chunk3WNW5Y7P_cjs.group(
14840
18594
  { class: `lt-bb-part lt-bb-part-${lp.part.kind}`, transform: `translate(${lp.x.toFixed(2)} ${lp.y.toFixed(2)})` },
14841
18595
  [body]
@@ -14863,8 +18617,8 @@ function renderWire(lw) {
14863
18617
  return path2 + dot1 + dot2;
14864
18618
  }
14865
18619
  function renderBreadboardLayout(layout, config) {
14866
- const theme = chunkD7EHZFK4_cjs.resolveBaseTheme(config?.theme ?? "default");
14867
- const css = buildCss6(theme);
18620
+ const theme = chunkL7POWM5B_cjs.resolveBaseTheme(config?.theme ?? "default");
18621
+ const css = buildCss9(theme);
14868
18622
  const titleStr = layout.ast.title ?? "Breadboard";
14869
18623
  const titleNode = layout.ast.title ? chunk3WNW5Y7P_cjs.text({ x: BB_CONST.MARGIN, y: 22, class: "lt-bb-title" }, layout.ast.title) : "";
14870
18624
  const substrate = renderSubstrate(layout.substrate);
@@ -17062,13 +20816,13 @@ function parseFbd(text2) {
17062
20816
  parseStatement(state2, stmt);
17063
20817
  i++;
17064
20818
  }
17065
- const network = {
20819
+ const network2 = {
17066
20820
  index: idx,
17067
20821
  blocks: state2.blocks.slice(blocksBefore),
17068
20822
  wires: state2.wires.slice(wiresBefore)
17069
20823
  };
17070
- if (nh.title !== void 0) network.title = nh.title;
17071
- networks.push(network);
20824
+ if (nh.title !== void 0) network2.title = nh.title;
20825
+ networks.push(network2);
17072
20826
  }
17073
20827
  for (const block of state2.blocks) {
17074
20828
  if (!block.instance) continue;
@@ -17207,18 +20961,18 @@ function makePort(p, side, edgeX, x, y) {
17207
20961
  if (p.negated) port.negated = true;
17208
20962
  return port;
17209
20963
  }
17210
- function buildGraph2(network) {
20964
+ function buildGraph2(network2) {
17211
20965
  const incoming = /* @__PURE__ */ new Map();
17212
20966
  const outgoing = /* @__PURE__ */ new Map();
17213
20967
  const inputVars = /* @__PURE__ */ new Map();
17214
20968
  const outputVars = /* @__PURE__ */ new Map();
17215
- for (const b of network.blocks) {
20969
+ for (const b of network2.blocks) {
17216
20970
  incoming.set(b.id, /* @__PURE__ */ new Set());
17217
20971
  outgoing.set(b.id, /* @__PURE__ */ new Set());
17218
20972
  inputVars.set(b.id, /* @__PURE__ */ new Set());
17219
20973
  outputVars.set(b.id, /* @__PURE__ */ new Set());
17220
20974
  }
17221
- for (const w of network.wires) {
20975
+ for (const w of network2.wires) {
17222
20976
  if (w.from.kind === "port" && w.to.kind === "port") {
17223
20977
  incoming.get(w.to.blockId)?.add(w.from.blockId);
17224
20978
  outgoing.get(w.from.blockId)?.add(w.to.blockId);
@@ -17230,7 +20984,7 @@ function buildGraph2(network) {
17230
20984
  }
17231
20985
  return { incoming, outgoing, inputVars, outputVars };
17232
20986
  }
17233
- function assignLayers(network, graph) {
20987
+ function assignLayers(network2, graph) {
17234
20988
  const layer = /* @__PURE__ */ new Map();
17235
20989
  const visiting = /* @__PURE__ */ new Set();
17236
20990
  const visit = (id) => {
@@ -17247,20 +21001,20 @@ function assignLayers(network, graph) {
17247
21001
  layer.set(id, max);
17248
21002
  return max;
17249
21003
  };
17250
- for (const b of network.blocks) visit(b.id);
21004
+ for (const b of network2.blocks) visit(b.id);
17251
21005
  return layer;
17252
21006
  }
17253
- function layoutNetwork2(network, originX, originY) {
17254
- const graph = buildGraph2(network);
21007
+ function layoutNetwork3(network2, originX, originY) {
21008
+ const graph = buildGraph2(network2);
17255
21009
  const sizes = /* @__PURE__ */ new Map();
17256
- for (const b of network.blocks) sizes.set(b.id, computeBlockSize(b));
17257
- const layerOf = assignLayers(network, graph);
21010
+ for (const b of network2.blocks) sizes.set(b.id, computeBlockSize(b));
21011
+ const layerOf = assignLayers(network2, graph);
17258
21012
  const maxLayer = Math.max(0, ...Array.from(layerOf.values()));
17259
21013
  const byLayer = Array.from({ length: maxLayer + 1 }, () => []);
17260
- for (const b of network.blocks) byLayer[layerOf.get(b.id)].push(b.id);
21014
+ for (const b of network2.blocks) byLayer[layerOf.get(b.id)].push(b.id);
17261
21015
  const inputVarsAll = /* @__PURE__ */ new Set();
17262
21016
  const outputVarsAll = /* @__PURE__ */ new Set();
17263
- for (const b of network.blocks) {
21017
+ for (const b of network2.blocks) {
17264
21018
  for (const v of graph.inputVars.get(b.id) ?? []) inputVarsAll.add(v);
17265
21019
  for (const v of graph.outputVars.get(b.id) ?? []) outputVarsAll.add(v);
17266
21020
  }
@@ -17293,7 +21047,7 @@ function layoutNetwork2(network, originX, originY) {
17293
21047
  yMax = Math.max(yMax, y);
17294
21048
  }
17295
21049
  const layoutBlocks = [];
17296
- for (const b of network.blocks) {
21050
+ for (const b of network2.blocks) {
17297
21051
  const bb = blockBboxes.get(b.id);
17298
21052
  layoutBlocks.push({
17299
21053
  block: b,
@@ -17341,7 +21095,7 @@ function layoutNetwork2(network, originX, originY) {
17341
21095
  const wires = [];
17342
21096
  const junctions = [];
17343
21097
  const fanOutCounts = /* @__PURE__ */ new Map();
17344
- for (const w of network.wires) {
21098
+ for (const w of network2.wires) {
17345
21099
  if (w.from.kind === "port") {
17346
21100
  const key = `${w.from.blockId}.${w.from.portName}`;
17347
21101
  fanOutCounts.set(key, (fanOutCounts.get(key) ?? 0) + 1);
@@ -17359,7 +21113,7 @@ function layoutNetwork2(network, originX, originY) {
17359
21113
  const varTermByName = /* @__PURE__ */ new Map();
17360
21114
  for (const t of varTerms) varTermByName.set(t.name, t);
17361
21115
  const colCursor = /* @__PURE__ */ new Map();
17362
- for (const w of network.wires) {
21116
+ for (const w of network2.wires) {
17363
21117
  const path2 = routeWire(w, portByKey, varTermByName, colCursor, layerX, layerWidths);
17364
21118
  if (path2) {
17365
21119
  wires.push({ wire: w, path: path2 });
@@ -17384,7 +21138,7 @@ function layoutNetwork2(network, originX, originY) {
17384
21138
  FBD_CONST.block_min_height + FBD_CONST.network_label_h + FBD_CONST.network_padding_y * 2
17385
21139
  );
17386
21140
  return {
17387
- network,
21141
+ network: network2,
17388
21142
  x: originX,
17389
21143
  y: originY,
17390
21144
  width,
@@ -17445,7 +21199,7 @@ function layoutFbd(ast) {
17445
21199
  let cursorY = 0;
17446
21200
  let maxWidth = 0;
17447
21201
  for (const net of ast.networks) {
17448
- const ln = layoutNetwork2(net, 0, cursorY);
21202
+ const ln = layoutNetwork3(net, 0, cursorY);
17449
21203
  networks.push(ln);
17450
21204
  cursorY = ln.y + ln.height + FBD_CONST.network_gap_y;
17451
21205
  maxWidth = Math.max(maxWidth, ln.width);
@@ -17595,7 +21349,7 @@ function renderBlock(lb) {
17595
21349
  parts
17596
21350
  );
17597
21351
  }
17598
- function renderNetwork(ln) {
21352
+ function renderNetwork2(ln) {
17599
21353
  const parts = [];
17600
21354
  parts.push(chunk3WNW5Y7P_cjs.rect({
17601
21355
  class: "lt-fbd-network-frame",
@@ -17661,7 +21415,7 @@ function renderFbdLayout(layout) {
17661
21415
  const descEl = chunk3WNW5Y7P_cjs.desc(`FBD with ${layout.networks.length} network(s).`);
17662
21416
  const root = chunk3WNW5Y7P_cjs.group(
17663
21417
  { transform: `translate(${margin},${margin})` },
17664
- layout.networks.map(renderNetwork)
21418
+ layout.networks.map(renderNetwork2)
17665
21419
  );
17666
21420
  return chunk3WNW5Y7P_cjs.svgRoot(
17667
21421
  {
@@ -18617,24 +22371,24 @@ var sfc = {
18617
22371
 
18618
22372
  // src/core/api.ts
18619
22373
  var plugins = [
18620
- chunkTZTCIAYW_cjs.genogram,
18621
- chunkVZ5LDNHK_cjs.ecomap,
18622
- chunkEGSUMHCS_cjs.pedigree,
18623
- chunkS2KJRHDZ_cjs.phylo,
18624
- chunkUUBNQV2T_cjs.sociogram,
22374
+ chunkS3RMAXH5_cjs.genogram,
22375
+ chunkVTSH4YPT_cjs.ecomap,
22376
+ chunkYB4XJY5L_cjs.pedigree,
22377
+ chunk4UFR2LB6_cjs.phylo,
22378
+ chunkWQDIZH2Z_cjs.sociogram,
18625
22379
  chunkST5YRTTV_cjs.timing,
18626
- chunkYLEVMOK2_cjs.logic,
18627
- chunkXRCY75UV_cjs.circuit,
22380
+ chunkR66QG3XT_cjs.logic,
22381
+ chunkV4RO5KYY_cjs.circuit,
18628
22382
  chunkSUIDD2C5_cjs.blockdiagram,
18629
- chunkN7W5KZK7_cjs.ladder,
18630
- chunk3M6WB62Y_cjs.sld,
18631
- chunkRYVV5UVI_cjs.entity,
18632
- chunkVFZOPRQP_cjs.fishbone,
18633
- chunkBW4KGTV7_cjs.venn,
18634
- chunkQTNPMIO2_cjs.flowchart,
22383
+ chunk2L4YXZAZ_cjs.ladder,
22384
+ chunkECD5XHBM_cjs.sld,
22385
+ chunkV4GILQR6_cjs.entity,
22386
+ chunkHWVBHU3O_cjs.fishbone,
22387
+ chunkC5C5EF3W_cjs.venn,
22388
+ chunk6JI6FWLZ_cjs.flowchart,
18635
22389
  mindmap,
18636
22390
  matrix,
18637
- chunkQUKVGHN4_cjs.orgchart,
22391
+ chunkFKJBXGWP_cjs.orgchart,
18638
22392
  decisiontree,
18639
22393
  timeline,
18640
22394
  state,
@@ -18646,7 +22400,10 @@ var plugins = [
18646
22400
  sfc,
18647
22401
  prisma,
18648
22402
  usecase,
18649
- pert
22403
+ pert,
22404
+ sequence,
22405
+ petri,
22406
+ network
18650
22407
  ];
18651
22408
  function detectPlugin(text2, config) {
18652
22409
  if (config?.type) {
@@ -18657,7 +22414,7 @@ function detectPlugin(text2, config) {
18657
22414
  if (plugin.detect(text2)) return plugin;
18658
22415
  }
18659
22416
  throw new Error(
18660
- "Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', or 'pert'."
22417
+ "Cannot detect diagram type. Start your text with 'genogram', 'ecomap', 'pedigree', 'phylo', 'sociogram', 'timing', 'logic', 'circuit', 'blockdiagram', 'ladder', 'sld', 'entity-structure', 'fishbone', 'venn', 'flowchart', 'mindmap', 'matrix', 'orgchart', 'state', 'pid', 'erd', 'breadboard', 'bpmn', 'fbd', 'sfc', 'prisma', 'usecase', 'pert', 'sequence', 'petri', or 'network'."
18661
22418
  );
18662
22419
  }
18663
22420
  function preprocess8(text2) {
@@ -18682,9 +22439,80 @@ function parse(text2, config) {
18682
22439
  `Diagram type '${plugin.type}' does not yet expose a parse() method.`
18683
22440
  );
18684
22441
  }
22442
+ function parseResult(text2, config) {
22443
+ let plugin;
22444
+ try {
22445
+ const prepared = preprocess8(text2);
22446
+ plugin = detectPlugin(prepared, config);
22447
+ if (!plugin.parse) {
22448
+ throw new Error(
22449
+ `Diagram type '${plugin.type}' does not yet expose a parse() method.`
22450
+ );
22451
+ }
22452
+ const ast = plugin.parse(prepared);
22453
+ const diagnostics = runLint(plugin, prepared);
22454
+ return {
22455
+ ok: true,
22456
+ status: diagnostics.length > 0 ? "partial" : "valid",
22457
+ type: plugin.type,
22458
+ ast,
22459
+ diagnostics
22460
+ };
22461
+ } catch (err) {
22462
+ return {
22463
+ ok: false,
22464
+ status: "invalid",
22465
+ type: plugin?.type ?? config?.type ?? null,
22466
+ diagnostics: [diagnosticFromError(err)]
22467
+ };
22468
+ }
22469
+ }
22470
+ function runLint(plugin, prepared) {
22471
+ if (!plugin.lint) return [];
22472
+ try {
22473
+ return plugin.lint(prepared);
22474
+ } catch {
22475
+ return [];
22476
+ }
22477
+ }
18685
22478
  function render(text2, config) {
22479
+ if (config?.mode === "preview") return renderResult(text2, config).svg;
18686
22480
  const prepared = preprocess8(text2);
18687
22481
  const plugin = detectPlugin(prepared, config);
22482
+ return renderWithPlugin(prepared, plugin, config);
22483
+ }
22484
+ function renderResult(text2, config) {
22485
+ let plugin;
22486
+ try {
22487
+ const prepared = preprocess8(text2);
22488
+ plugin = detectPlugin(prepared, config);
22489
+ const svg = renderWithPlugin(prepared, plugin, config);
22490
+ const diagnostics = runLint(plugin, prepared);
22491
+ return {
22492
+ ok: true,
22493
+ status: diagnostics.length > 0 ? "partial" : "valid",
22494
+ type: plugin.type,
22495
+ svg,
22496
+ diagnostics
22497
+ };
22498
+ } catch (err) {
22499
+ const type = plugin?.type ?? config?.type ?? null;
22500
+ const diagnostics = [diagnosticFromError(err)];
22501
+ return {
22502
+ ok: false,
22503
+ status: "invalid",
22504
+ type,
22505
+ svg: renderDiagnosticSvg(diagnostics, type, {
22506
+ fontFamily: config?.fontFamily
22507
+ }),
22508
+ diagnostics
22509
+ };
22510
+ }
22511
+ }
22512
+ function renderPreview(text2, config) {
22513
+ return renderResult(text2, config).svg;
22514
+ }
22515
+ function renderWithPlugin(prepared, plugin, config) {
18688
22516
  const renderConfig = {
18689
22517
  fontFamily: config?.fontFamily ?? "system-ui, -apple-system, sans-serif",
18690
22518
  fontSize: 12,
@@ -18694,14 +22522,24 @@ function render(text2, config) {
18694
22522
  return plugin.render(prepared, renderConfig);
18695
22523
  }
18696
22524
 
22525
+ exports.GEOMETRY = GEOMETRY;
18697
22526
  exports.decisiontree = decisiontree;
22527
+ exports.drawDeviceIcon = drawDeviceIcon;
22528
+ exports.iconSize = iconSize;
22529
+ exports.network = network;
18698
22530
  exports.parse = parse;
22531
+ exports.parseResult = parseResult;
18699
22532
  exports.pert = pert;
22533
+ exports.petri = petri;
18700
22534
  exports.pid = pid;
18701
22535
  exports.prisma = prisma;
18702
22536
  exports.render = render;
22537
+ exports.renderEquip = renderEquip;
22538
+ exports.renderPreview = renderPreview;
22539
+ exports.renderResult = renderResult;
22540
+ exports.sequence = sequence;
18703
22541
  exports.state = state;
18704
22542
  exports.timeline = timeline;
18705
22543
  exports.usecase = usecase;
18706
- //# sourceMappingURL=chunk-3YUUC6RN.cjs.map
18707
- //# sourceMappingURL=chunk-3YUUC6RN.cjs.map
22544
+ //# sourceMappingURL=chunk-25ZON47K.cjs.map
22545
+ //# sourceMappingURL=chunk-25ZON47K.cjs.map