schematex 0.1.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 (181) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +379 -0
  3. package/dist/chunk-2MQWZ2XY.cjs +453 -0
  4. package/dist/chunk-2MQWZ2XY.cjs.map +1 -0
  5. package/dist/chunk-2UKC6ZCY.cjs +1803 -0
  6. package/dist/chunk-2UKC6ZCY.cjs.map +1 -0
  7. package/dist/chunk-34X3ZJ6E.cjs +783 -0
  8. package/dist/chunk-34X3ZJ6E.cjs.map +1 -0
  9. package/dist/chunk-3FTUWAXK.cjs +1220 -0
  10. package/dist/chunk-3FTUWAXK.cjs.map +1 -0
  11. package/dist/chunk-3J7TFUOC.js +745 -0
  12. package/dist/chunk-3J7TFUOC.js.map +1 -0
  13. package/dist/chunk-47ZC6EMJ.js +1009 -0
  14. package/dist/chunk-47ZC6EMJ.js.map +1 -0
  15. package/dist/chunk-4DBRNOPA.cjs +750 -0
  16. package/dist/chunk-4DBRNOPA.cjs.map +1 -0
  17. package/dist/chunk-4G7ZIBHN.js +778 -0
  18. package/dist/chunk-4G7ZIBHN.js.map +1 -0
  19. package/dist/chunk-5C7DPDHQ.js +1321 -0
  20. package/dist/chunk-5C7DPDHQ.js.map +1 -0
  21. package/dist/chunk-ADOXGKAK.js +1251 -0
  22. package/dist/chunk-ADOXGKAK.js.map +1 -0
  23. package/dist/chunk-BE5HNDA5.cjs +874 -0
  24. package/dist/chunk-BE5HNDA5.cjs.map +1 -0
  25. package/dist/chunk-CZRM7LT7.js +889 -0
  26. package/dist/chunk-CZRM7LT7.js.map +1 -0
  27. package/dist/chunk-D4JTSPOL.js +1795 -0
  28. package/dist/chunk-D4JTSPOL.js.map +1 -0
  29. package/dist/chunk-DS47NTWZ.cjs +1034 -0
  30. package/dist/chunk-DS47NTWZ.cjs.map +1 -0
  31. package/dist/chunk-FDLZEKEB.js +449 -0
  32. package/dist/chunk-FDLZEKEB.js.map +1 -0
  33. package/dist/chunk-FGPTCDUT.cjs +1851 -0
  34. package/dist/chunk-FGPTCDUT.cjs.map +1 -0
  35. package/dist/chunk-HDKDQAEQ.cjs +86 -0
  36. package/dist/chunk-HDKDQAEQ.cjs.map +1 -0
  37. package/dist/chunk-IX554O5K.js +346 -0
  38. package/dist/chunk-IX554O5K.js.map +1 -0
  39. package/dist/chunk-KLJEK547.js +71 -0
  40. package/dist/chunk-KLJEK547.js.map +1 -0
  41. package/dist/chunk-LMFSHK45.js +1028 -0
  42. package/dist/chunk-LMFSHK45.js.map +1 -0
  43. package/dist/chunk-MDICUK6F.cjs +1258 -0
  44. package/dist/chunk-MDICUK6F.cjs.map +1 -0
  45. package/dist/chunk-N7KOXOMX.cjs +363 -0
  46. package/dist/chunk-N7KOXOMX.cjs.map +1 -0
  47. package/dist/chunk-NYCIK4SU.cjs +775 -0
  48. package/dist/chunk-NYCIK4SU.cjs.map +1 -0
  49. package/dist/chunk-PDPHRZZT.js +770 -0
  50. package/dist/chunk-PDPHRZZT.js.map +1 -0
  51. package/dist/chunk-ROFLJ74T.js +1212 -0
  52. package/dist/chunk-ROFLJ74T.js.map +1 -0
  53. package/dist/chunk-S6BK5DB6.cjs +845 -0
  54. package/dist/chunk-S6BK5DB6.cjs.map +1 -0
  55. package/dist/chunk-U4I37IBN.js +874 -0
  56. package/dist/chunk-U4I37IBN.js.map +1 -0
  57. package/dist/chunk-U5GGE6PJ.js +839 -0
  58. package/dist/chunk-U5GGE6PJ.js.map +1 -0
  59. package/dist/chunk-UHLYS3W5.cjs +1015 -0
  60. package/dist/chunk-UHLYS3W5.cjs.map +1 -0
  61. package/dist/chunk-URSKIHSY.cjs +881 -0
  62. package/dist/chunk-URSKIHSY.cjs.map +1 -0
  63. package/dist/chunk-V6WO7RK7.cjs +1056 -0
  64. package/dist/chunk-V6WO7RK7.cjs.map +1 -0
  65. package/dist/chunk-VFQCTXOX.js +869 -0
  66. package/dist/chunk-VFQCTXOX.js.map +1 -0
  67. package/dist/chunk-XQ52ICHU.cjs +895 -0
  68. package/dist/chunk-XQ52ICHU.cjs.map +1 -0
  69. package/dist/chunk-XX4BKS7Y.js +1051 -0
  70. package/dist/chunk-XX4BKS7Y.js.map +1 -0
  71. package/dist/chunk-XXU36667.js +1844 -0
  72. package/dist/chunk-XXU36667.js.map +1 -0
  73. package/dist/chunk-ZX7QKZK2.cjs +1326 -0
  74. package/dist/chunk-ZX7QKZK2.cjs.map +1 -0
  75. package/dist/diagrams/blockdiagram/index.cjs +25 -0
  76. package/dist/diagrams/blockdiagram/index.cjs.map +1 -0
  77. package/dist/diagrams/blockdiagram/index.d.cts +67 -0
  78. package/dist/diagrams/blockdiagram/index.d.ts +67 -0
  79. package/dist/diagrams/blockdiagram/index.js +4 -0
  80. package/dist/diagrams/blockdiagram/index.js.map +1 -0
  81. package/dist/diagrams/circuit/index.cjs +34 -0
  82. package/dist/diagrams/circuit/index.cjs.map +1 -0
  83. package/dist/diagrams/circuit/index.d.cts +138 -0
  84. package/dist/diagrams/circuit/index.d.ts +138 -0
  85. package/dist/diagrams/circuit/index.js +5 -0
  86. package/dist/diagrams/circuit/index.js.map +1 -0
  87. package/dist/diagrams/ecomap/index.cjs +30 -0
  88. package/dist/diagrams/ecomap/index.cjs.map +1 -0
  89. package/dist/diagrams/ecomap/index.d.cts +15 -0
  90. package/dist/diagrams/ecomap/index.d.ts +15 -0
  91. package/dist/diagrams/ecomap/index.js +5 -0
  92. package/dist/diagrams/ecomap/index.js.map +1 -0
  93. package/dist/diagrams/entity/index.cjs +26 -0
  94. package/dist/diagrams/entity/index.cjs.map +1 -0
  95. package/dist/diagrams/entity/index.d.cts +54 -0
  96. package/dist/diagrams/entity/index.d.ts +54 -0
  97. package/dist/diagrams/entity/index.js +5 -0
  98. package/dist/diagrams/entity/index.js.map +1 -0
  99. package/dist/diagrams/fishbone/index.cjs +34 -0
  100. package/dist/diagrams/fishbone/index.cjs.map +1 -0
  101. package/dist/diagrams/fishbone/index.d.cts +185 -0
  102. package/dist/diagrams/fishbone/index.d.ts +185 -0
  103. package/dist/diagrams/fishbone/index.js +5 -0
  104. package/dist/diagrams/fishbone/index.js.map +1 -0
  105. package/dist/diagrams/flowchart/index.cjs +34 -0
  106. package/dist/diagrams/flowchart/index.cjs.map +1 -0
  107. package/dist/diagrams/flowchart/index.d.cts +2 -0
  108. package/dist/diagrams/flowchart/index.d.ts +2 -0
  109. package/dist/diagrams/flowchart/index.js +5 -0
  110. package/dist/diagrams/flowchart/index.js.map +1 -0
  111. package/dist/diagrams/genogram/index.cjs +38 -0
  112. package/dist/diagrams/genogram/index.cjs.map +1 -0
  113. package/dist/diagrams/genogram/index.d.cts +20 -0
  114. package/dist/diagrams/genogram/index.d.ts +20 -0
  115. package/dist/diagrams/genogram/index.js +5 -0
  116. package/dist/diagrams/genogram/index.js.map +1 -0
  117. package/dist/diagrams/ladder/index.cjs +26 -0
  118. package/dist/diagrams/ladder/index.cjs.map +1 -0
  119. package/dist/diagrams/ladder/index.d.cts +49 -0
  120. package/dist/diagrams/ladder/index.d.ts +49 -0
  121. package/dist/diagrams/ladder/index.js +5 -0
  122. package/dist/diagrams/ladder/index.js.map +1 -0
  123. package/dist/diagrams/logic/index.cjs +26 -0
  124. package/dist/diagrams/logic/index.cjs.map +1 -0
  125. package/dist/diagrams/logic/index.d.cts +73 -0
  126. package/dist/diagrams/logic/index.d.ts +73 -0
  127. package/dist/diagrams/logic/index.js +5 -0
  128. package/dist/diagrams/logic/index.js.map +1 -0
  129. package/dist/diagrams/orgchart/index.cjs +30 -0
  130. package/dist/diagrams/orgchart/index.cjs.map +1 -0
  131. package/dist/diagrams/orgchart/index.d.cts +100 -0
  132. package/dist/diagrams/orgchart/index.d.ts +100 -0
  133. package/dist/diagrams/orgchart/index.js +5 -0
  134. package/dist/diagrams/orgchart/index.js.map +1 -0
  135. package/dist/diagrams/pedigree/index.cjs +30 -0
  136. package/dist/diagrams/pedigree/index.cjs.map +1 -0
  137. package/dist/diagrams/pedigree/index.d.cts +15 -0
  138. package/dist/diagrams/pedigree/index.d.ts +15 -0
  139. package/dist/diagrams/pedigree/index.js +5 -0
  140. package/dist/diagrams/pedigree/index.js.map +1 -0
  141. package/dist/diagrams/phylo/index.cjs +30 -0
  142. package/dist/diagrams/phylo/index.cjs.map +1 -0
  143. package/dist/diagrams/phylo/index.d.cts +32 -0
  144. package/dist/diagrams/phylo/index.d.ts +32 -0
  145. package/dist/diagrams/phylo/index.js +5 -0
  146. package/dist/diagrams/phylo/index.js.map +1 -0
  147. package/dist/diagrams/sld/index.cjs +26 -0
  148. package/dist/diagrams/sld/index.cjs.map +1 -0
  149. package/dist/diagrams/sld/index.d.cts +58 -0
  150. package/dist/diagrams/sld/index.d.ts +58 -0
  151. package/dist/diagrams/sld/index.js +5 -0
  152. package/dist/diagrams/sld/index.js.map +1 -0
  153. package/dist/diagrams/sociogram/index.cjs +26 -0
  154. package/dist/diagrams/sociogram/index.cjs.map +1 -0
  155. package/dist/diagrams/sociogram/index.d.cts +76 -0
  156. package/dist/diagrams/sociogram/index.d.ts +76 -0
  157. package/dist/diagrams/sociogram/index.js +5 -0
  158. package/dist/diagrams/sociogram/index.js.map +1 -0
  159. package/dist/diagrams/timing/index.cjs +21 -0
  160. package/dist/diagrams/timing/index.cjs.map +1 -0
  161. package/dist/diagrams/timing/index.d.cts +9 -0
  162. package/dist/diagrams/timing/index.d.ts +9 -0
  163. package/dist/diagrams/timing/index.js +4 -0
  164. package/dist/diagrams/timing/index.js.map +1 -0
  165. package/dist/diagrams/venn/index.cjs +38 -0
  166. package/dist/diagrams/venn/index.cjs.map +1 -0
  167. package/dist/diagrams/venn/index.d.cts +69 -0
  168. package/dist/diagrams/venn/index.d.ts +69 -0
  169. package/dist/diagrams/venn/index.js +5 -0
  170. package/dist/diagrams/venn/index.js.map +1 -0
  171. package/dist/index-BSlza1YY.d.ts +150 -0
  172. package/dist/index-BXefHVce.d.cts +150 -0
  173. package/dist/index.cjs +2033 -0
  174. package/dist/index.cjs.map +1 -0
  175. package/dist/index.d.cts +29 -0
  176. package/dist/index.d.ts +29 -0
  177. package/dist/index.js +1944 -0
  178. package/dist/index.js.map +1 -0
  179. package/dist/types-DqfcYkcY.d.cts +741 -0
  180. package/dist/types-DqfcYkcY.d.ts +741 -0
  181. package/package.json +163 -0
@@ -0,0 +1,745 @@
1
+ import { resolveIndustrialTheme } from './chunk-IX554O5K.js';
2
+ import { title, desc, el, text, path, group, svgRoot } from './chunk-KLJEK547.js';
3
+
4
+ // src/diagrams/ladder/parser.ts
5
+ var LadderParseError = class extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "LadderParseError";
9
+ }
10
+ };
11
+ var CONTACT_TYPES = /* @__PURE__ */ new Set(["XIC", "XIO", "ONS", "OSF"]);
12
+ var COIL_TYPES = /* @__PURE__ */ new Set(["OTE", "OTL", "OTU", "OTN"]);
13
+ var FB_TYPES = /* @__PURE__ */ new Set([
14
+ "TON",
15
+ "TOFF",
16
+ "TP",
17
+ "CTU",
18
+ "CTD",
19
+ "CTUD",
20
+ "ADD",
21
+ "SUB",
22
+ "MUL",
23
+ "DIV",
24
+ "MOV",
25
+ "EQU",
26
+ "NEQ",
27
+ "GRT",
28
+ "LES",
29
+ "GEQ",
30
+ "LEQ"
31
+ ]);
32
+ function splitArgs(s) {
33
+ const out = [];
34
+ let cur = "";
35
+ let inQuote = false;
36
+ for (const ch of s) {
37
+ if (ch === '"') inQuote = !inQuote;
38
+ if (ch === "," && !inQuote) {
39
+ out.push(cur.trim());
40
+ cur = "";
41
+ } else {
42
+ cur += ch;
43
+ }
44
+ }
45
+ if (cur.trim()) out.push(cur.trim());
46
+ return out;
47
+ }
48
+ function stripQuotes(v) {
49
+ const t = v.trim();
50
+ if (t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
51
+ return t;
52
+ }
53
+ function parseElement(line, lineNo) {
54
+ const m = line.match(/^([A-Z][A-Z0-9_]*)\s*\(\s*([^)]*)\s*\)\s*$/);
55
+ if (!m) {
56
+ throw new LadderParseError(`Line ${lineNo}: invalid element syntax: ${line}`);
57
+ }
58
+ const op = m[1];
59
+ const args = splitArgs(m[2]);
60
+ if (args.length === 0 || !args[0]) {
61
+ throw new LadderParseError(`Line ${lineNo}: element missing tag: ${line}`);
62
+ }
63
+ const tag = args[0];
64
+ if (CONTACT_TYPES.has(op)) {
65
+ const contact = {
66
+ elementType: "contact",
67
+ contactType: op,
68
+ tag
69
+ };
70
+ for (let i = 1; i < args.length; i++) {
71
+ const a = args[i];
72
+ const kv = a.match(/^(\w+)\s*=\s*(.+)$/);
73
+ if (kv) {
74
+ const key = kv[1].toLowerCase();
75
+ if (key === "address") contact.address = stripQuotes(kv[2]);
76
+ else if (key === "name") contact.name = stripQuotes(kv[2]);
77
+ } else {
78
+ contact.address = stripQuotes(a);
79
+ }
80
+ }
81
+ return contact;
82
+ }
83
+ if (COIL_TYPES.has(op)) {
84
+ const coil = {
85
+ elementType: "coil",
86
+ coilType: op,
87
+ tag
88
+ };
89
+ for (let i = 1; i < args.length; i++) {
90
+ const a = args[i];
91
+ const kv = a.match(/^(\w+)\s*=\s*(.+)$/);
92
+ if (kv) {
93
+ const key = kv[1].toLowerCase();
94
+ if (key === "address") coil.address = stripQuotes(kv[2]);
95
+ else if (key === "name") coil.name = stripQuotes(kv[2]);
96
+ } else {
97
+ coil.address = stripQuotes(a);
98
+ }
99
+ }
100
+ return coil;
101
+ }
102
+ if (FB_TYPES.has(op)) {
103
+ const params = {};
104
+ for (let i = 1; i < args.length; i++) {
105
+ const a = args[i];
106
+ const kv = a.match(/^(\w+)\s*=\s*(.+)$/);
107
+ if (kv) {
108
+ const v = stripQuotes(kv[2]);
109
+ const n = Number(v);
110
+ params[kv[1]] = Number.isFinite(n) && v !== "" && /^-?\d+(\.\d+)?$/.test(v) ? n : v;
111
+ }
112
+ }
113
+ const fb = {
114
+ elementType: "function_block",
115
+ fbType: op,
116
+ tag,
117
+ params
118
+ };
119
+ return fb;
120
+ }
121
+ throw new LadderParseError(`Line ${lineNo}: unknown element type "${op}"`);
122
+ }
123
+ function parseLadderDSL(text2) {
124
+ const rawLines = text2.split("\n").map((l) => l.replace(/\r$/, ""));
125
+ let title2;
126
+ const rungs = [];
127
+ let currentRung = null;
128
+ let currentParallel = null;
129
+ let currentBranch = null;
130
+ let parallelIndent = -1;
131
+ let branchIndent = -1;
132
+ function finishParallel() {
133
+ if (currentRung && currentParallel) {
134
+ currentRung.elements.push({ parallel: currentParallel });
135
+ }
136
+ currentParallel = null;
137
+ currentBranch = null;
138
+ parallelIndent = -1;
139
+ branchIndent = -1;
140
+ }
141
+ for (let i = 0; i < rawLines.length; i++) {
142
+ const raw = rawLines[i];
143
+ const lineNo = i + 1;
144
+ const stripped = raw.replace(/#.*$/, "");
145
+ if (!stripped.trim()) continue;
146
+ let indent = 0;
147
+ for (const ch of stripped) {
148
+ if (ch === " ") indent++;
149
+ else if (ch === " ") indent += 2;
150
+ else break;
151
+ }
152
+ const line = stripped.trim();
153
+ if (/^ladder\b/i.test(line)) {
154
+ const m = line.match(/"([^"]*)"/);
155
+ if (m) title2 = m[1];
156
+ continue;
157
+ }
158
+ const rungMatch = line.match(/^rung\s+(\d+)(?:\s+"([^"]*)")?\s*:\s*$/i);
159
+ if (rungMatch) {
160
+ finishParallel();
161
+ if (currentRung) {
162
+ if (currentRung.elements.length === 0) {
163
+ throw new LadderParseError(`Rung ${currentRung.number}: empty rung`);
164
+ }
165
+ rungs.push(currentRung);
166
+ }
167
+ currentRung = {
168
+ number: Number(rungMatch[1]),
169
+ comment: rungMatch[2],
170
+ elements: []
171
+ };
172
+ continue;
173
+ }
174
+ if (!currentRung) {
175
+ throw new LadderParseError(`Line ${lineNo}: element outside of rung: ${line}`);
176
+ }
177
+ if (/^parallel\s*:\s*$/i.test(line)) {
178
+ finishParallel();
179
+ currentParallel = [];
180
+ parallelIndent = indent;
181
+ continue;
182
+ }
183
+ if (/^branch\s*:\s*$/i.test(line)) {
184
+ if (!currentParallel) {
185
+ throw new LadderParseError(`Line ${lineNo}: branch: without parallel:`);
186
+ }
187
+ currentBranch = { elements: [] };
188
+ currentParallel.push(currentBranch);
189
+ branchIndent = indent;
190
+ continue;
191
+ }
192
+ if (currentParallel && indent <= parallelIndent) {
193
+ finishParallel();
194
+ }
195
+ const element = parseElement(line, lineNo);
196
+ if (currentParallel && currentBranch && indent > branchIndent) {
197
+ currentBranch.elements.push(element);
198
+ } else {
199
+ if (currentParallel) finishParallel();
200
+ currentRung.elements.push(element);
201
+ }
202
+ }
203
+ finishParallel();
204
+ if (currentRung) {
205
+ if (currentRung.elements.length === 0) {
206
+ throw new LadderParseError(`Rung ${currentRung.number}: empty rung`);
207
+ }
208
+ rungs.push(currentRung);
209
+ }
210
+ if (rungs.length === 0) {
211
+ throw new LadderParseError("Ladder diagram has no rungs");
212
+ }
213
+ return { type: "ladder", title: title2, rungs };
214
+ }
215
+
216
+ // src/diagrams/ladder/layout.ts
217
+ var LEFT_RAIL_X = 60;
218
+ var RIGHT_RAIL_MARGIN = 30;
219
+ var BASE_RUNG_HEIGHT = 100;
220
+ var ELEMENT_WIDTH = 32;
221
+ var ELEMENT_HEIGHT = 24;
222
+ var FB_WIDTH = 80;
223
+ var FB_HEIGHT = 56;
224
+ var CMP_WIDTH = 56;
225
+ var CMP_HEIGHT = 34;
226
+ var RUNG_Y_OFFSET = 70;
227
+ var H_SPACING = 16;
228
+ var PARALLEL_GAP = 14;
229
+ var CANVAS_PADDING = 30;
230
+ var CHAR_W = 6.1;
231
+ var LINE_H = 11;
232
+ var COMPARE_TYPES = /* @__PURE__ */ new Set(["EQU", "NEQ", "GRT", "LES", "GEQ", "LEQ"]);
233
+ function bodyWidth(el2) {
234
+ if (el2.elementType === "function_block") {
235
+ if (COMPARE_TYPES.has(el2.fbType)) return CMP_WIDTH;
236
+ return FB_WIDTH;
237
+ }
238
+ return ELEMENT_WIDTH;
239
+ }
240
+ function bodyHeight(el2) {
241
+ if (el2.elementType === "function_block") {
242
+ if (COMPARE_TYPES.has(el2.fbType)) return CMP_HEIGHT;
243
+ return FB_HEIGHT;
244
+ }
245
+ return ELEMENT_HEIGHT;
246
+ }
247
+ function elementKind(el2) {
248
+ if (el2.elementType === "contact") return "contact";
249
+ if (el2.elementType === "coil") return "coil";
250
+ if (COMPARE_TYPES.has(el2.fbType)) return "compare";
251
+ return "function_block";
252
+ }
253
+ function wrapName(name, maxChars = 14) {
254
+ const words = name.split(/\s+/);
255
+ const lines = [];
256
+ let cur = "";
257
+ for (const w of words) {
258
+ const trial = cur ? `${cur} ${w}` : w;
259
+ if (trial.length > maxChars && cur) {
260
+ lines.push(cur);
261
+ cur = w;
262
+ } else {
263
+ cur = trial;
264
+ }
265
+ }
266
+ if (cur) lines.push(cur);
267
+ return lines.slice(0, 3);
268
+ }
269
+ function labelLinesForContactOrCoil(el2) {
270
+ if (el2.elementType === "function_block") return [el2.tag];
271
+ const lines = [];
272
+ if (el2.name) lines.push(...wrapName(el2.name));
273
+ lines.push(el2.tag);
274
+ if (el2.address) lines.push(el2.address);
275
+ return lines;
276
+ }
277
+ function labelWidth(el2) {
278
+ const lines = labelLinesForContactOrCoil(el2);
279
+ let maxChars = 0;
280
+ for (const l of lines) if (l.length > maxChars) maxChars = l.length;
281
+ return maxChars * CHAR_W;
282
+ }
283
+ function slotWidth(el2) {
284
+ return Math.max(bodyWidth(el2), labelWidth(el2)) + 6;
285
+ }
286
+ function labelRowsAbove(el2) {
287
+ if (el2.elementType === "function_block") return 1;
288
+ const name = el2.name ? wrapName(el2.name).length : 0;
289
+ return name + 1;
290
+ }
291
+ function labelRowsBelow(el2) {
292
+ if (el2.elementType === "function_block") return 0;
293
+ if (el2.address) return 1;
294
+ return 0;
295
+ }
296
+ function rungLabelHeaderHeight(rung) {
297
+ let maxAbovePx = 0;
298
+ for (const item of rung.elements) {
299
+ if ("parallel" in item) {
300
+ const ph = parallelHeight(item.parallel);
301
+ const topBranch = item.parallel[0];
302
+ if (!topBranch) continue;
303
+ const aboveRows = Math.max(
304
+ 1,
305
+ ...topBranch.elements.map((e) => labelRowsAbove(e))
306
+ );
307
+ const px = ph / 2 + aboveRows * LINE_H + 4;
308
+ if (px > maxAbovePx) maxAbovePx = px;
309
+ } else {
310
+ const rows = labelRowsAbove(item);
311
+ const px = bodyHeight(item) / 2 + rows * LINE_H + 4;
312
+ if (px > maxAbovePx) maxAbovePx = px;
313
+ }
314
+ }
315
+ return maxAbovePx;
316
+ }
317
+ function rungLabelFooterHeight(rung) {
318
+ let maxBelowPx = 0;
319
+ for (const item of rung.elements) {
320
+ if ("parallel" in item) {
321
+ const ph = parallelHeight(item.parallel);
322
+ const bottomBranch = item.parallel[item.parallel.length - 1];
323
+ if (!bottomBranch) continue;
324
+ const belowRows = Math.max(
325
+ 0,
326
+ ...bottomBranch.elements.map((e) => labelRowsBelow(e))
327
+ );
328
+ const px = ph / 2 + belowRows * LINE_H + 4;
329
+ if (px > maxBelowPx) maxBelowPx = px;
330
+ } else {
331
+ const rows = labelRowsBelow(item);
332
+ const px = bodyHeight(item) / 2 + rows * LINE_H + 4;
333
+ if (px > maxBelowPx) maxBelowPx = px;
334
+ }
335
+ }
336
+ return maxBelowPx;
337
+ }
338
+ function branchInlineWidth(branch) {
339
+ if (branch.elements.length === 0) return ELEMENT_WIDTH;
340
+ let w = 0;
341
+ branch.elements.forEach((el2, i) => {
342
+ w += slotWidth(el2);
343
+ if (i < branch.elements.length - 1) w += H_SPACING;
344
+ });
345
+ return w;
346
+ }
347
+ function parallelWidth(branches) {
348
+ const maxBranch = Math.max(
349
+ ELEMENT_WIDTH,
350
+ ...branches.map(branchInlineWidth)
351
+ );
352
+ return maxBranch + 2 * H_SPACING;
353
+ }
354
+ function branchRowHeight(branch) {
355
+ const bodyH = Math.max(
356
+ ELEMENT_HEIGHT,
357
+ ...branch.elements.map((e) => bodyHeight(e))
358
+ );
359
+ const above = Math.max(1, ...branch.elements.map((e) => labelRowsAbove(e)));
360
+ const below = Math.max(0, ...branch.elements.map((e) => labelRowsBelow(e)));
361
+ return bodyH + above * LINE_H + below * LINE_H + 6;
362
+ }
363
+ function parallelHeight(branches) {
364
+ let total = 0;
365
+ branches.forEach((br, i) => {
366
+ total += branchRowHeight(br);
367
+ if (i < branches.length - 1) total += PARALLEL_GAP;
368
+ });
369
+ return Math.max(total, ELEMENT_HEIGHT);
370
+ }
371
+ function layoutLadder(ast) {
372
+ const nodes = [];
373
+ const wires = [];
374
+ const rungs = [];
375
+ let maxEndX = LEFT_RAIL_X + 200;
376
+ let cursorY = RUNG_Y_OFFSET;
377
+ ast.rungs.forEach((rung, rungIndex) => {
378
+ const headerH = rungLabelHeaderHeight(rung);
379
+ const footerH = rungLabelFooterHeight(rung);
380
+ const rungHeight = Math.max(BASE_RUNG_HEIGHT, 16 + headerH + footerH + 16);
381
+ const rungY = cursorY + 16 + headerH;
382
+ let x = LEFT_RAIL_X + H_SPACING;
383
+ let prevRightX = LEFT_RAIL_X;
384
+ let prevRightY = rungY;
385
+ const addWire = (x1, y1, x2, y2) => {
386
+ wires.push({ path: `M ${x1} ${y1} L ${x2} ${y2}` });
387
+ };
388
+ rung.elements.forEach((item, elIdx) => {
389
+ if ("parallel" in item) {
390
+ const branches = item.parallel;
391
+ const pw = parallelWidth(branches);
392
+ const ph = parallelHeight(branches);
393
+ const bx = x;
394
+ const ex = x + pw;
395
+ addWire(prevRightX, prevRightY, bx, rungY);
396
+ const busTop = rungY - ph / 2;
397
+ const busBot = rungY + ph / 2;
398
+ wires.push({ path: `M ${bx} ${busTop} L ${bx} ${busBot}` });
399
+ wires.push({ path: `M ${ex} ${busTop} L ${ex} ${busBot}` });
400
+ let rowY = busTop;
401
+ branches.forEach((branch, bIdx) => {
402
+ const rowH = branchRowHeight(branch);
403
+ const bodyMax = Math.max(
404
+ ELEMENT_HEIGHT,
405
+ ...branch.elements.map((e) => bodyHeight(e))
406
+ );
407
+ const above = Math.max(
408
+ 1,
409
+ ...branch.elements.map((e) => labelRowsAbove(e))
410
+ );
411
+ const branchY = rowY + above * LINE_H + bodyMax / 2;
412
+ const bInline = branchInlineWidth(branch);
413
+ const startX = bx + H_SPACING + (ex - bx - 2 * H_SPACING - bInline) / 2;
414
+ let bx0 = startX;
415
+ let prevBX = bx;
416
+ branch.elements.forEach((bel, beIdx) => {
417
+ const w2 = bodyWidth(bel);
418
+ const h2 = bodyHeight(bel);
419
+ const slot2 = slotWidth(bel);
420
+ const nx2 = bx0 + (slot2 - w2) / 2;
421
+ const ny2 = branchY - h2 / 2;
422
+ const id2 = `R${rung.number}_P${elIdx}_B${bIdx}_E${beIdx}`;
423
+ nodes.push({
424
+ id: id2,
425
+ rungIndex,
426
+ element: bel,
427
+ kind: elementKind(bel),
428
+ x: nx2,
429
+ y: ny2,
430
+ width: w2,
431
+ height: h2,
432
+ rungY: branchY
433
+ });
434
+ addWire(prevBX, branchY, nx2, branchY);
435
+ prevBX = nx2 + w2;
436
+ bx0 += slot2 + H_SPACING;
437
+ });
438
+ addWire(prevBX, branchY, ex, branchY);
439
+ rowY += rowH + PARALLEL_GAP;
440
+ });
441
+ prevRightX = ex;
442
+ prevRightY = rungY;
443
+ x = ex + H_SPACING;
444
+ return;
445
+ }
446
+ const el2 = item;
447
+ const w = bodyWidth(el2);
448
+ const h = bodyHeight(el2);
449
+ const slot = slotWidth(el2);
450
+ const nx = x + (slot - w) / 2;
451
+ const ny = rungY - h / 2;
452
+ const id = `R${rung.number}_E${elIdx}`;
453
+ nodes.push({
454
+ id,
455
+ rungIndex,
456
+ element: el2,
457
+ kind: elementKind(el2),
458
+ x: nx,
459
+ y: ny,
460
+ width: w,
461
+ height: h,
462
+ rungY
463
+ });
464
+ addWire(prevRightX, prevRightY, nx, rungY);
465
+ prevRightX = nx + w;
466
+ prevRightY = rungY;
467
+ x += slot + H_SPACING;
468
+ });
469
+ rungs.push({
470
+ rung,
471
+ index: rungIndex,
472
+ y: rungY,
473
+ endX: prevRightX,
474
+ height: rungHeight,
475
+ headerHeight: headerH
476
+ });
477
+ if (prevRightX > maxEndX) maxEndX = prevRightX;
478
+ cursorY += rungHeight;
479
+ });
480
+ const rightRailX = maxEndX + H_SPACING * 2;
481
+ const width = rightRailX + RIGHT_RAIL_MARGIN;
482
+ const height = cursorY + CANVAS_PADDING;
483
+ for (const r of rungs) {
484
+ wires.push({ path: `M ${r.endX} ${r.y} L ${rightRailX} ${r.y}` });
485
+ }
486
+ return {
487
+ width,
488
+ height,
489
+ leftRailX: LEFT_RAIL_X,
490
+ rightRailX,
491
+ rungs,
492
+ nodes,
493
+ wires
494
+ };
495
+ }
496
+
497
+ // src/diagrams/ladder/renderer.ts
498
+ function buildCss(t) {
499
+ return `
500
+ .lt-ladder { background: ${t.bg}; font-family: system-ui, -apple-system, sans-serif; }
501
+ .lt-ladder-rail { stroke: ${t.strokeHeavy}; stroke-width: 4; stroke-linecap: square; }
502
+ .lt-ladder-wire { stroke: ${t.stroke}; stroke-width: 1.5; fill: none; }
503
+ .lt-ladder-element { stroke: ${t.stroke}; stroke-width: 2; fill: none; }
504
+ .lt-ladder-coil { stroke: ${t.stroke}; stroke-width: 2; fill: none; }
505
+ .lt-ladder-fb { fill: ${t.bg}; stroke: ${t.stroke}; stroke-width: 2; }
506
+ .lt-ladder-fb-name { font: bold 11px sans-serif; fill: ${t.text}; text-anchor: middle; }
507
+ .lt-ladder-pin { font: 8px sans-serif; fill: ${t.stroke}; }
508
+ .lt-ladder-name { font: 9px sans-serif; fill: ${t.text}; text-anchor: middle; }
509
+ .lt-ladder-tag { font: 600 9px ui-monospace, SFMono-Regular, Menlo, monospace; fill: ${t.accent}; text-anchor: middle; }
510
+ .lt-ladder-addr { font: 600 8.5px ui-monospace, monospace; fill: ${t.error}; text-anchor: middle; }
511
+ .lt-ladder-rung-num { font: 10px sans-serif; fill: ${t.textMuted}; text-anchor: end; }
512
+ .lt-ladder-comment { font: italic 10px sans-serif; fill: ${t.textMuted}; }
513
+ .lt-ladder-title { font: bold 15px sans-serif; fill: ${t.text}; }
514
+ .lt-ladder-symbol-label { font: bold 10px sans-serif; fill: ${t.stroke}; text-anchor: middle; }
515
+ `.trim();
516
+ }
517
+ var LINE_H2 = 11;
518
+ function labelsAbove(name, tag, cx, bodyTopY) {
519
+ const pieces = [];
520
+ const nameLines = name ? wrapName(name) : [];
521
+ const totalLines = nameLines.length + 1;
522
+ const firstY = bodyTopY - 4 - (totalLines - 1) * LINE_H2;
523
+ nameLines.forEach((line, i) => {
524
+ pieces.push(
525
+ text({ x: cx, y: firstY + i * LINE_H2, class: "lt-ladder-name" }, line)
526
+ );
527
+ });
528
+ pieces.push(
529
+ text(
530
+ { x: cx, y: firstY + nameLines.length * LINE_H2, class: "lt-ladder-tag" },
531
+ tag
532
+ )
533
+ );
534
+ return pieces;
535
+ }
536
+ function renderContact(node) {
537
+ const c = node.element;
538
+ const { x, y, width: w, height: h } = node;
539
+ const cy = y + h / 2;
540
+ const leftBarX = x + 4;
541
+ const rightBarX = x + w - 4;
542
+ const cx = x + w / 2;
543
+ const pieces = [];
544
+ pieces.push(el("line", { x1: x, y1: cy, x2: leftBarX, y2: cy, class: "lt-ladder-wire" }));
545
+ pieces.push(el("line", { x1: rightBarX, y1: cy, x2: x + w, y2: cy, class: "lt-ladder-wire" }));
546
+ pieces.push(el("line", { x1: leftBarX, y1: y, x2: leftBarX, y2: y + h, class: "lt-ladder-element" }));
547
+ pieces.push(el("line", { x1: rightBarX, y1: y, x2: rightBarX, y2: y + h, class: "lt-ladder-element" }));
548
+ if (c.contactType === "XIO") {
549
+ pieces.push(
550
+ el("line", {
551
+ x1: leftBarX + 2,
552
+ y1: y + h - 2,
553
+ x2: rightBarX - 2,
554
+ y2: y + 2,
555
+ class: "lt-ladder-element"
556
+ })
557
+ );
558
+ } else if (c.contactType === "ONS") {
559
+ pieces.push(text({ x: cx, y: cy + 3, class: "lt-ladder-symbol-label" }, "\u2191"));
560
+ } else if (c.contactType === "OSF") {
561
+ pieces.push(text({ x: cx, y: cy + 3, class: "lt-ladder-symbol-label" }, "\u2193"));
562
+ }
563
+ pieces.push(...labelsAbove(c.name, c.tag, cx, y));
564
+ if (c.address) {
565
+ pieces.push(
566
+ text({ x: cx, y: y + h + 10, class: "lt-ladder-addr" }, c.address)
567
+ );
568
+ }
569
+ return group({ "data-element": "contact", "data-tag": c.tag }, pieces);
570
+ }
571
+ function renderCoil(node) {
572
+ const c = node.element;
573
+ const { x, y, width: w, height: h } = node;
574
+ const cy = y + h / 2;
575
+ const cx = x + w / 2;
576
+ const pieces = [];
577
+ const leftArc = `M ${x + 6} ${y} A 10 ${h / 2} 0 0 0 ${x + 6} ${y + h}`;
578
+ const rightArc = `M ${x + w - 6} ${y} A 10 ${h / 2} 0 0 1 ${x + w - 6} ${y + h}`;
579
+ pieces.push(path({ d: leftArc, class: "lt-ladder-coil" }));
580
+ pieces.push(path({ d: rightArc, class: "lt-ladder-coil" }));
581
+ pieces.push(el("line", { x1: x, y1: cy, x2: x + 6, y2: cy, class: "lt-ladder-wire" }));
582
+ pieces.push(el("line", { x1: x + w - 6, y1: cy, x2: x + w, y2: cy, class: "lt-ladder-wire" }));
583
+ let inner = "";
584
+ if (c.coilType === "OTL") inner = "S";
585
+ else if (c.coilType === "OTU") inner = "R";
586
+ else if (c.coilType === "OTN") inner = "/";
587
+ if (inner) {
588
+ pieces.push(text({ x: cx, y: cy + 3, class: "lt-ladder-symbol-label" }, inner));
589
+ }
590
+ pieces.push(...labelsAbove(c.name, c.tag, cx, y));
591
+ if (c.address) {
592
+ pieces.push(
593
+ text({ x: cx, y: y + h + 10, class: "lt-ladder-addr" }, c.address)
594
+ );
595
+ }
596
+ return group({ "data-element": "coil", "data-tag": c.tag }, pieces);
597
+ }
598
+ function renderCompare(node) {
599
+ const fb = node.element;
600
+ const { x, y, width: w, height: h } = node;
601
+ const cy = y + h / 2;
602
+ const cx = x + w / 2;
603
+ const pieces = [];
604
+ pieces.push(el("rect", { x, y, width: w, height: h, class: "lt-ladder-fb" }));
605
+ pieces.push(text({ x: cx, y: y + 12, class: "lt-ladder-fb-name" }, fb.fbType));
606
+ const entries = Object.entries(fb.params);
607
+ entries.slice(0, 2).forEach(([k, v], i) => {
608
+ pieces.push(
609
+ text({ x: x + 4, y: y + 22 + i * 9, class: "lt-ladder-pin" }, `${k}:${v}`)
610
+ );
611
+ });
612
+ pieces.push(el("line", { x1: x - 2, y1: cy, x2: x, y2: cy, class: "lt-ladder-wire" }));
613
+ pieces.push(el("line", { x1: x + w, y1: cy, x2: x + w + 2, y2: cy, class: "lt-ladder-wire" }));
614
+ pieces.push(text({ x: cx, y: y - 4, class: "lt-ladder-tag" }, fb.tag));
615
+ return group({ "data-element": "compare", "data-tag": fb.tag }, pieces);
616
+ }
617
+ function renderFunctionBlock(node) {
618
+ const fb = node.element;
619
+ const { x, y, width: w, height: h } = node;
620
+ const cy = y + h / 2;
621
+ const cx = x + w / 2;
622
+ const pieces = [];
623
+ pieces.push(el("rect", { x, y, width: w, height: h, class: "lt-ladder-fb" }));
624
+ pieces.push(text({ x: cx, y: y + 14, class: "lt-ladder-fb-name" }, fb.fbType));
625
+ pieces.push(
626
+ el("line", { x1: x - 6, y1: cy, x2: x, y2: cy, class: "lt-ladder-wire" })
627
+ );
628
+ pieces.push(
629
+ el("line", { x1: x + w, y1: cy, x2: x + w + 6, y2: cy, class: "lt-ladder-wire" })
630
+ );
631
+ const entries = Object.entries(fb.params);
632
+ entries.slice(0, 3).forEach(([k, v], i) => {
633
+ pieces.push(
634
+ text({ x: x + 4, y: y + 28 + i * 9, class: "lt-ladder-pin" }, `${k}=${v}`)
635
+ );
636
+ });
637
+ pieces.push(text({ x: cx, y: y - 4, class: "lt-ladder-tag" }, fb.tag));
638
+ return group({ "data-element": "function_block", "data-tag": fb.tag }, pieces);
639
+ }
640
+ function renderNode(node) {
641
+ switch (node.kind) {
642
+ case "contact":
643
+ return renderContact(node);
644
+ case "coil":
645
+ return renderCoil(node);
646
+ case "compare":
647
+ return renderCompare(node);
648
+ case "function_block":
649
+ return renderFunctionBlock(node);
650
+ }
651
+ }
652
+ function renderLadder(ast, config) {
653
+ const layout = layoutLadder(ast);
654
+ const t = resolveIndustrialTheme(config?.theme ?? "default");
655
+ const titleOffset = ast.title ? 30 : 0;
656
+ const width = layout.width;
657
+ const height = layout.height + titleOffset;
658
+ const children = [];
659
+ children.push(title(ast.title ?? "Ladder Logic"));
660
+ children.push(
661
+ desc(`PLC ladder logic diagram with ${ast.rungs.length} rung${ast.rungs.length === 1 ? "" : "s"}`)
662
+ );
663
+ children.push(el("style", {}, buildCss(t)));
664
+ if (ast.title) {
665
+ children.push(
666
+ text({ x: layout.leftRailX, y: 20, class: "lt-ladder-title" }, ast.title)
667
+ );
668
+ }
669
+ const inner = [];
670
+ inner.push(
671
+ el("line", {
672
+ x1: layout.leftRailX,
673
+ y1: 0,
674
+ x2: layout.leftRailX,
675
+ y2: layout.height,
676
+ class: "lt-ladder-rail"
677
+ })
678
+ );
679
+ inner.push(
680
+ el("line", {
681
+ x1: layout.rightRailX,
682
+ y1: 0,
683
+ x2: layout.rightRailX,
684
+ y2: layout.height,
685
+ class: "lt-ladder-rail"
686
+ })
687
+ );
688
+ for (const r of layout.rungs) {
689
+ inner.push(
690
+ text(
691
+ { x: layout.leftRailX - 6, y: r.y + 3, class: "lt-ladder-rung-num" },
692
+ `Rung ${String(r.rung.number).padStart(3, "0")}`
693
+ )
694
+ );
695
+ if (r.rung.comment) {
696
+ const commentY = Math.max(14, r.y - (r.headerHeight + 6));
697
+ inner.push(
698
+ text(
699
+ {
700
+ x: layout.leftRailX + 10,
701
+ y: commentY,
702
+ class: "lt-ladder-comment"
703
+ },
704
+ `\u2014 ${r.rung.comment} \u2014`
705
+ )
706
+ );
707
+ }
708
+ }
709
+ for (const w of layout.wires) {
710
+ inner.push(path({ d: w.path, class: "lt-ladder-wire" }));
711
+ }
712
+ for (const n of layout.nodes) {
713
+ inner.push(renderNode(n));
714
+ }
715
+ const wrap = group({ transform: `translate(0, ${titleOffset})` }, inner);
716
+ children.push(wrap);
717
+ return svgRoot(
718
+ {
719
+ class: "lt-ladder",
720
+ role: "img",
721
+ "aria-labelledby": "lt-ladder-title lt-ladder-desc",
722
+ width,
723
+ height,
724
+ viewBox: `0 0 ${width} ${height}`
725
+ },
726
+ children
727
+ );
728
+ }
729
+
730
+ // src/diagrams/ladder/index.ts
731
+ var ladder = {
732
+ type: "ladder",
733
+ detect(text2) {
734
+ const first = text2.trim().split("\n")[0]?.trim().toLowerCase() ?? "";
735
+ return first.startsWith("ladder");
736
+ },
737
+ render(text2, config) {
738
+ const ast = parseLadderDSL(text2);
739
+ return renderLadder(ast, config);
740
+ }
741
+ };
742
+
743
+ export { ladder, layoutLadder, parseLadderDSL, renderLadder };
744
+ //# sourceMappingURL=chunk-3J7TFUOC.js.map
745
+ //# sourceMappingURL=chunk-3J7TFUOC.js.map