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