schematex 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +15 -7
  2. package/dist/ai/ai-sdk.cjs +18 -12
  3. package/dist/ai/ai-sdk.cjs.map +1 -1
  4. package/dist/ai/ai-sdk.d.cts +4 -3
  5. package/dist/ai/ai-sdk.d.ts +4 -3
  6. package/dist/ai/ai-sdk.js +14 -8
  7. package/dist/ai/ai-sdk.js.map +1 -1
  8. package/dist/ai/index.cjs +15 -11
  9. package/dist/ai/index.d.cts +20 -153
  10. package/dist/ai/index.d.ts +20 -153
  11. package/dist/ai/index.js +3 -3
  12. package/dist/api-C5SECOxZ.d.cts +69 -0
  13. package/dist/api-Cr_MxttI.d.ts +69 -0
  14. package/dist/browser.cjs +23 -5
  15. package/dist/browser.cjs.map +1 -1
  16. package/dist/browser.d.cts +12 -3
  17. package/dist/browser.d.ts +12 -3
  18. package/dist/browser.js +13 -5
  19. package/dist/browser.js.map +1 -1
  20. package/dist/{chunk-3YUUC6RN.cjs → chunk-4QPDZJAL.cjs} +1421 -37
  21. package/dist/chunk-4QPDZJAL.cjs.map +1 -0
  22. package/dist/{chunk-NWPCY65Z.cjs → chunk-HK56GQQP.cjs} +544 -61
  23. package/dist/chunk-HK56GQQP.cjs.map +1 -0
  24. package/dist/{chunk-GTDQAN2Z.js → chunk-OK5ZS3LU.js} +1419 -38
  25. package/dist/chunk-OK5ZS3LU.js.map +1 -0
  26. package/dist/{chunk-XRCY75UV.cjs → chunk-QMTWG6JL.cjs} +947 -916
  27. package/dist/chunk-QMTWG6JL.cjs.map +1 -0
  28. package/dist/{chunk-HUPDIRBX.js → chunk-VCH7RI5H.js} +947 -916
  29. package/dist/chunk-VCH7RI5H.js.map +1 -0
  30. package/dist/{chunk-IM4RCUHA.js → chunk-VKPCR7BG.js} +544 -62
  31. package/dist/chunk-VKPCR7BG.js.map +1 -0
  32. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  33. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  34. package/dist/diagrams/circuit/index.cjs +7 -7
  35. package/dist/diagrams/circuit/index.d.cts +1 -1
  36. package/dist/diagrams/circuit/index.d.ts +1 -1
  37. package/dist/diagrams/circuit/index.js +1 -1
  38. package/dist/diagrams/ecomap/index.d.cts +1 -1
  39. package/dist/diagrams/ecomap/index.d.ts +1 -1
  40. package/dist/diagrams/entity/index.d.cts +1 -1
  41. package/dist/diagrams/entity/index.d.ts +1 -1
  42. package/dist/diagrams/fishbone/index.d.cts +1 -1
  43. package/dist/diagrams/fishbone/index.d.ts +1 -1
  44. package/dist/diagrams/flowchart/index.d.cts +2 -2
  45. package/dist/diagrams/flowchart/index.d.ts +2 -2
  46. package/dist/diagrams/genogram/index.d.cts +1 -1
  47. package/dist/diagrams/genogram/index.d.ts +1 -1
  48. package/dist/diagrams/ladder/index.d.cts +1 -1
  49. package/dist/diagrams/ladder/index.d.ts +1 -1
  50. package/dist/diagrams/logic/index.d.cts +1 -1
  51. package/dist/diagrams/logic/index.d.ts +1 -1
  52. package/dist/diagrams/orgchart/index.d.cts +1 -1
  53. package/dist/diagrams/orgchart/index.d.ts +1 -1
  54. package/dist/diagrams/pedigree/index.d.cts +1 -1
  55. package/dist/diagrams/pedigree/index.d.ts +1 -1
  56. package/dist/diagrams/phylo/index.d.cts +1 -1
  57. package/dist/diagrams/phylo/index.d.ts +1 -1
  58. package/dist/diagrams/sld/index.d.cts +1 -1
  59. package/dist/diagrams/sld/index.d.ts +1 -1
  60. package/dist/diagrams/sociogram/index.d.cts +1 -1
  61. package/dist/diagrams/sociogram/index.d.ts +1 -1
  62. package/dist/diagrams/timing/index.d.cts +1 -1
  63. package/dist/diagrams/timing/index.d.ts +1 -1
  64. package/dist/diagrams/venn/index.d.cts +1 -1
  65. package/dist/diagrams/venn/index.d.ts +1 -1
  66. package/dist/{index-C9A0h-CB.d.cts → index-BD2yDfQM.d.cts} +1 -1
  67. package/dist/{index-CJai_TEZ.d.ts → index-C30zQWZI.d.ts} +1 -1
  68. package/dist/index.cjs +24 -12
  69. package/dist/index.d.cts +3 -3
  70. package/dist/index.d.ts +3 -3
  71. package/dist/index.js +2 -2
  72. package/dist/react.cjs +7 -9
  73. package/dist/react.cjs.map +1 -1
  74. package/dist/react.d.cts +3 -2
  75. package/dist/react.d.ts +3 -2
  76. package/dist/react.js +7 -9
  77. package/dist/react.js.map +1 -1
  78. package/dist/tools-BHWaJPOl.d.ts +153 -0
  79. package/dist/tools-DlpuE76u.d.cts +153 -0
  80. package/dist/{types-BOAsqHoU.d.cts → types-WTr9W5Ud.d.cts} +2 -2
  81. package/dist/{types-BOAsqHoU.d.ts → types-WTr9W5Ud.d.ts} +2 -2
  82. package/package.json +2 -2
  83. package/dist/api-C5UcmT7n.d.cts +0 -22
  84. package/dist/api-C5UcmT7n.d.ts +0 -22
  85. package/dist/chunk-3YUUC6RN.cjs.map +0 -1
  86. package/dist/chunk-GTDQAN2Z.js.map +0 -1
  87. package/dist/chunk-HUPDIRBX.js.map +0 -1
  88. package/dist/chunk-IM4RCUHA.js.map +0 -1
  89. package/dist/chunk-NWPCY65Z.cjs.map +0 -1
  90. package/dist/chunk-XRCY75UV.cjs.map +0 -1
@@ -4,644 +4,54 @@ var chunkD7EHZFK4_cjs = require('./chunk-D7EHZFK4.cjs');
4
4
  var chunkNAGUZFXX_cjs = require('./chunk-NAGUZFXX.cjs');
5
5
  var chunk3WNW5Y7P_cjs = require('./chunk-3WNW5Y7P.cjs');
6
6
 
7
- // src/diagrams/circuit/netlist.ts
8
- var NetlistParseError = class extends Error {
9
- constructor(message, line) {
10
- super(message);
11
- this.line = line;
12
- this.name = "NetlistParseError";
13
- }
14
- line;
7
+ // src/diagrams/circuit/symbols.ts
8
+ var BODY = 'class="schematex-circuit-body"';
9
+ var FILL = 'class="schematex-circuit-fill"';
10
+ var WIRE = 'class="schematex-circuit-wire"';
11
+ function lineWire(x1, y1, x2, y2) {
12
+ return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" ${WIRE}/>`;
13
+ }
14
+ var resistor = {
15
+ length: 40,
16
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
17
+ svg: () => `<path d="M 0,0 L 5,0 L 8,-8 L 12,8 L 16,-8 L 20,8 L 24,-8 L 28,8 L 32,-8 L 35,0 L 40,0" ${BODY}/>`
15
18
  };
16
- var PREFIX_MAP = {
17
- R: { type: "resistor", pins: ["start", "end"] },
18
- C: { type: "capacitor", pins: ["start", "end"] },
19
- L: { type: "inductor", pins: ["start", "end"] },
20
- D: { type: "diode", pins: ["start", "end"] },
21
- // anode start, cathode → end
22
- V: { type: "voltage_source", pins: ["plus", "minus"] },
23
- I: { type: "current_source", pins: ["plus", "minus"] },
24
- Q: { type: "npn", pins: ["c", "b", "e"] },
25
- M: { type: "nmos", pins: ["d", "g", "s"] },
26
- J: { type: "jfet_n", pins: ["d", "g", "s"] },
27
- S: { type: "switch_spst", pins: ["start", "end"] },
28
- F: { type: "fuse", pins: ["start", "end"] },
29
- B: { type: "battery", pins: ["plus", "minus"] },
30
- K: { type: "relay_coil", pins: ["start", "end"] },
31
- U: { type: "generic_ic", pins: [] },
32
- // pins declared via pins="..." attr
33
- X: { type: "generic_ic", pins: [] },
34
- W: { type: "wire", pins: ["start", "end"] },
35
- // explicit wire (non-SPICE convention, common in EE textbooks)
36
- T: { type: "terminal_block", pins: [] }
37
- // pins via pins="..."
19
+ var capacitor = {
20
+ length: 20,
21
+ anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
22
+ svg: () => [
23
+ lineWire(0, 0, 8, 0),
24
+ `<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
25
+ `<line x1="12" y1="-10" x2="12" y2="10" ${BODY}/>`,
26
+ lineWire(12, 0, 20, 0)
27
+ ].join("")
38
28
  };
39
- var TYPE_ALIASES = {
40
- vsource: "voltage_source",
41
- isource: "current_source",
42
- acsource: "ac_source",
43
- ecap: "electrolytic_cap",
44
- pot: "potentiometer",
45
- gnd: "ground",
46
- ic: "generic_ic",
47
- reg: "voltage_regulator",
48
- timer555: "555_timer",
49
- transistor: "npn",
50
- tb: "terminal_block",
51
- junction_box: "terminal_block",
52
- jbox: "terminal_block",
53
- enclosure: "terminal_block"
29
+ var electrolytic_cap = {
30
+ length: 20,
31
+ anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
32
+ svg: () => [
33
+ lineWire(0, 0, 8, 0),
34
+ `<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
35
+ `<path d="M 12,-10 Q 16,0 12,10" fill="none" class="schematex-circuit-body"/>`,
36
+ lineWire(12, 0, 20, 0),
37
+ `<text x="5" y="-12" class="schematex-circuit-pol">\u2212</text>`,
38
+ `<text x="15" y="-12" class="schematex-circuit-pol">+</text>`
39
+ ].join("")
54
40
  };
55
- var GROUND_REF = /^(0|gnd|ground|earth|pe|agnd|dgnd|gnda|gndd|vss|com)(_\w+|\d+)?$/i;
56
- function isGroundRef(s) {
57
- return GROUND_REF.test(s);
58
- }
59
- function tokenize(line) {
60
- const tokens = [];
61
- let i = 0;
62
- while (i < line.length) {
63
- const ch = line[i];
64
- if (ch === " " || ch === " ") {
65
- i++;
66
- continue;
67
- }
68
- if (ch === '"') {
69
- const end = line.indexOf('"', i + 1);
70
- tokens.push(line.slice(i, end < 0 ? line.length : end + 1));
71
- i = end < 0 ? line.length : end + 1;
72
- continue;
73
- }
74
- let j = i;
75
- while (j < line.length && line[j] !== " " && line[j] !== " ") {
76
- if (line[j] === "=" && line[j + 1] === '"') {
77
- const end = line.indexOf('"', j + 2);
78
- j = end < 0 ? line.length : end + 1;
79
- break;
80
- }
81
- j++;
82
- }
83
- tokens.push(line.slice(i, j));
84
- i = j;
85
- }
86
- return tokens;
87
- }
88
- function isKeyEqVal(tok) {
89
- const eq = tok.indexOf("=");
90
- return eq > 0 && /^[a-zA-Z_][\w]*$/.test(tok.slice(0, eq));
91
- }
92
- function parseKV(tok) {
93
- const eq = tok.indexOf("=");
94
- const key = tok.slice(0, eq).trim();
95
- let val = tok.slice(eq + 1).trim();
96
- if (val.startsWith('"') && val.endsWith('"')) val = val.slice(1, -1);
97
- return [key, val];
98
- }
99
- function parseNetlist(body, title2) {
100
- const components = [];
101
- const netByName = /* @__PURE__ */ new Map();
102
- const pinMap = {};
103
- let autoGnd = 0;
104
- const ensureNet = (name) => {
105
- let n = netByName.get(name);
106
- if (!n) {
107
- n = { id: name, anchors: [] };
108
- netByName.set(name, n);
109
- }
110
- return n;
111
- };
112
- const lines = body.split("\n").map((l) => l.replace(/\r$/, ""));
113
- for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
114
- const raw = lines[lineIdx];
115
- const stripped = raw.replace(/#.*$/, "").trim();
116
- if (!stripped) continue;
117
- const tokens = tokenize(stripped);
118
- if (tokens.length < 2) {
119
- throw new NetlistParseError(
120
- `Netlist line must have at least ID + one net: "${stripped}"`,
121
- lineIdx + 1
122
- );
123
- }
124
- const id = tokens[0];
125
- if (!/^[a-zA-Z_][\w]*$/.test(id)) {
126
- throw new NetlistParseError(`Invalid component id: "${id}"`, lineIdx + 1);
127
- }
128
- let cursor = 1;
129
- const netRefs = [];
130
- while (cursor < tokens.length && !isKeyEqVal(tokens[cursor])) {
131
- netRefs.push(tokens[cursor]);
132
- cursor++;
133
- }
134
- const kv = {};
135
- for (; cursor < tokens.length; cursor++) {
136
- if (!isKeyEqVal(tokens[cursor])) {
137
- throw new NetlistParseError(
138
- `Expected key=value after nets, got "${tokens[cursor]}"`,
139
- lineIdx + 1
140
- );
141
- }
142
- const [k, v] = parseKV(tokens[cursor]);
143
- kv[k] = v;
144
- }
145
- const prefix = id[0].toUpperCase();
146
- const defaults = PREFIX_MAP[prefix];
147
- let cType;
148
- let pinOrder;
149
- if (kv.type) {
150
- const t = kv.type.toLowerCase();
151
- cType = TYPE_ALIASES[t] ?? t;
152
- pinOrder = defaults?.pins ?? ["p1", "p2"];
153
- } else if (defaults) {
154
- cType = defaults.type;
155
- pinOrder = defaults.pins;
156
- } else if (isGroundRef(id)) {
157
- cType = "ground";
158
- pinOrder = ["start"];
159
- } else {
160
- throw new NetlistParseError(
161
- `Cannot infer type from id "${id}". Either rename to a SPICE-prefix id (R*, C*, L*, D*, V*, I*, Q*, M*, J*, S*, F*, B*, K*, U*, X*, W*, T*) or pass an explicit \`type=<name>\` attribute (e.g. \`${id} ... type=resistor\`). Schematex circuit covers electrical schematics only \u2014 hydraulic/pneumatic prefixes (EV*, BOMBA*, TANK*, etc.) are not supported.`,
162
- lineIdx + 1
163
- );
164
- }
165
- const expectedPins = pinOrder.length;
166
- let valueFromTail;
167
- if (netRefs.length > expectedPins && expectedPins > 0) {
168
- const tail = netRefs.slice(expectedPins);
169
- netRefs.length = expectedPins;
170
- const first = tail[0].toLowerCase();
171
- if (TYPE_ALIASES[first]) {
172
- cType = TYPE_ALIASES[first];
173
- } else if (first === "npn" || first === "pnp" || first === "nmos" || first === "pmos" || first === "jfet_n" || first === "jfet_p" || first === "zener" || first === "schottky" || first === "led" || first === "photodiode") {
174
- cType = first;
175
- if (first === "pnp") pinOrder = ["c", "b", "e"];
176
- if (first === "pmos" || first === "jfet_p") pinOrder = ["d", "g", "s"];
177
- } else {
178
- valueFromTail = tail.join(" ");
179
- }
180
- if (tail.length > 1 && !valueFromTail) {
181
- valueFromTail = tail.slice(1).join(" ");
182
- }
183
- }
184
- if (cType === "ground" || cType === "gnd_signal" || cType === "gnd_chassis" || cType === "gnd_digital") {
185
- pinOrder = ["start"];
186
- }
187
- if (cType === "terminal_block") {
188
- const pinsAttr = kv.pins ?? kv.terminals;
189
- const labels = pinsAttr ? pinsAttr.split(",").map((s) => s.trim()).filter(Boolean) : Array.from({ length: Math.max(netRefs.length, 1) }, (_, i) => `t${i + 1}`);
190
- pinOrder = labels.map(
191
- (label, idx) => label.toLowerCase().replace(/[^a-z0-9]+/g, "_") || `t${idx + 1}`
192
- );
193
- }
194
- if (netRefs.length < pinOrder.length) {
195
- throw new NetlistParseError(
196
- `Component ${id} (${cType}) expects ${pinOrder.length} nets, got ${netRefs.length}`,
197
- lineIdx + 1
198
- );
199
- }
200
- const pins = {};
201
- for (let p = 0; p < pinOrder.length; p++) {
202
- let net = netRefs[p];
203
- if (isGroundRef(net)) net = "GND";
204
- pins[pinOrder[p]] = net;
205
- ensureNet(net).anchors.push(`${id}.${pinOrder[p]}`);
206
- }
207
- pinMap[id] = pins;
208
- const comp = {
209
- id,
210
- componentType: cType,
211
- direction: "right",
212
- label: kv.label ?? id,
213
- value: kv.value ?? valueFromTail,
214
- attrs: {}
215
- };
216
- for (const [k, v] of Object.entries(kv)) {
217
- if (k === "label" || k === "value" || k === "type") continue;
218
- comp.attrs[k] = v;
219
- }
220
- components.push(comp);
221
- }
222
- if (netByName.has("GND")) {
223
- const hasGroundSym = components.some(
224
- (c) => c.componentType === "ground" || c.componentType === "gnd_signal" || c.componentType === "gnd_chassis" || c.componentType === "gnd_digital"
225
- );
226
- if (!hasGroundSym) {
227
- const gId = `_GND${autoGnd++}`;
228
- components.push({
229
- id: gId,
230
- componentType: "ground",
231
- direction: "down",
232
- label: void 0,
233
- value: void 0,
234
- attrs: { auto: "true" }
235
- });
236
- pinMap[gId] = { start: "GND" };
237
- ensureNet("GND").anchors.push(`${gId}.start`);
238
- }
239
- }
240
- const nets = Array.from(netByName.values());
241
- return {
242
- type: "circuit",
243
- title: title2,
244
- components,
245
- nets,
246
- pinMap,
247
- mode: "netlist"
248
- };
249
- }
250
-
251
- // src/diagrams/circuit/parser.ts
252
- var CircuitParseError = class extends Error {
253
- constructor(message) {
254
- super(message);
255
- this.name = "CircuitParseError";
256
- }
41
+ var inductor = {
42
+ length: 40,
43
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
44
+ svg: () => `<path d="M 0,0 L 5,0 A 5,5 0 0 1 15,0 A 5,5 0 0 1 25,0 A 5,5 0 0 1 35,0 L 40,0" fill="none" ${BODY}/>`
257
45
  };
258
- var COMPONENT_TYPES = /* @__PURE__ */ new Set([
259
- "resistor",
260
- "potentiometer",
261
- "rheostat",
262
- "thermistor_ntc",
263
- "thermistor_ptc",
264
- "ldr",
265
- "varistor",
266
- "fuse",
267
- "fuse_slow",
268
- "capacitor",
269
- "electrolytic_cap",
270
- "variable_cap",
271
- "inductor",
272
- "inductor_iron",
273
- "inductor_ferrite",
274
- "variable_inductor",
275
- "ferrite_bead",
276
- "crystal",
277
- "transformer",
278
- "diode",
279
- "zener",
280
- "schottky",
281
- "led",
282
- "photodiode",
283
- "varactor",
284
- "tvs_diode",
285
- "bridge_rectifier",
286
- "npn",
287
- "pnp",
288
- "darlington_npn",
289
- "darlington_pnp",
290
- "nmos",
291
- "pmos",
292
- "nmos_depletion",
293
- "jfet_n",
294
- "jfet_p",
295
- "igbt",
296
- "scr",
297
- "triac",
298
- "diac",
299
- "phototransistor",
300
- "optocoupler",
301
- "opamp",
302
- "comparator",
303
- "schmitt_buffer",
304
- "tri_state_buffer",
305
- "instrumentation_amp",
306
- "generic_ic",
307
- "voltage_regulator",
308
- "dc_dc_converter",
309
- "555_timer",
310
- "voltage_source",
311
- "current_source",
312
- "ac_source",
313
- "battery",
314
- "vcc",
315
- "ground",
316
- "gnd_signal",
317
- "gnd_chassis",
318
- "gnd_digital",
319
- "switch_spst",
320
- "switch_spdt",
321
- "switch_dpdt",
322
- "push_no",
323
- "push_nc",
324
- "relay_coil",
325
- "relay_no",
326
- "relay_nc",
327
- "contactor",
328
- "solenoid_valve",
329
- "thermal_overload",
330
- "disconnect_switch",
331
- "motor",
332
- "speaker",
333
- "microphone",
334
- "buzzer",
335
- "ammeter",
336
- "voltmeter",
337
- "wattmeter",
338
- "oscilloscope",
339
- "wire",
340
- "dot",
341
- "label",
342
- "port",
343
- "test_point",
344
- "no_connect",
345
- "antenna"
346
- ]);
347
- var ALIASES = {
348
- vsource: "voltage_source",
349
- isource: "current_source",
350
- acsource: "ac_source",
351
- ecap: "electrolytic_cap",
352
- pot: "potentiometer",
353
- xtal: "crystal",
354
- xfmr: "transformer",
355
- transistor: "npn",
356
- bjt_npn: "npn",
357
- bjt_pnp: "pnp",
358
- mosfet_n: "nmos",
359
- mosfet_p: "pmos",
360
- gnd: "ground",
361
- ic: "generic_ic",
362
- reg: "voltage_regulator",
363
- timer555: "555_timer",
364
- therm: "thermistor_ntc",
365
- ntc: "thermistor_ntc",
366
- ptc: "thermistor_ptc",
367
- ths: "thermistor_ntc",
368
- // Industrial control aliases (IEC letter codes)
369
- coil: "relay_coil",
370
- relay: "relay_coil",
371
- km: "contactor",
372
- solenoid: "solenoid_valve",
373
- ev: "solenoid_valve",
374
- overload: "thermal_overload",
375
- thermal: "thermal_overload",
376
- disconnect: "disconnect_switch",
377
- isolator: "disconnect_switch"
378
- };
379
- var EXTRA_TYPES = /* @__PURE__ */ new Set(["lamp"]);
380
- function normalizeType(raw) {
381
- const lower = raw.toLowerCase();
382
- if (ALIASES[lower]) return ALIASES[lower];
383
- if (COMPONENT_TYPES.has(lower)) {
384
- return lower;
385
- }
386
- if (EXTRA_TYPES.has(lower)) {
387
- return lower;
388
- }
389
- return null;
390
- }
391
- var DIRECTIONS = /* @__PURE__ */ new Set(["right", "left", "up", "down"]);
392
- function parseAttrs(rest) {
393
- const out = { attrs: {} };
394
- const tokens = [];
395
- let i = 0;
396
- while (i < rest.length) {
397
- const ch = rest[i];
398
- if (ch === " " || ch === " ") {
399
- i++;
400
- continue;
401
- }
402
- if (ch === '"') {
403
- const end = rest.indexOf('"', i + 1);
404
- const tok = rest.slice(i, end < 0 ? rest.length : end + 1);
405
- tokens.push(tok);
406
- i = end < 0 ? rest.length : end + 1;
407
- continue;
408
- }
409
- let j = i;
410
- while (j < rest.length && rest[j] !== " " && rest[j] !== " ") {
411
- if (rest[j] === "=" && rest[j + 1] === '"') {
412
- const end = rest.indexOf('"', j + 2);
413
- j = end < 0 ? rest.length : end + 1;
414
- break;
415
- }
416
- j++;
417
- }
418
- tokens.push(rest.slice(i, j));
419
- i = j;
420
- }
421
- for (const tok of tokens) {
422
- if (!tok) continue;
423
- if (DIRECTIONS.has(tok.toLowerCase())) {
424
- out.direction = tok.toLowerCase();
425
- continue;
426
- }
427
- const eq = tok.indexOf("=");
428
- if (eq > 0) {
429
- const key = tok.slice(0, eq).trim();
430
- let val = tok.slice(eq + 1).trim();
431
- if (val.startsWith('"') && val.endsWith('"')) {
432
- val = val.slice(1, -1);
433
- }
434
- if (key === "label") out.label = val;
435
- else if (key === "value") out.value = val;
436
- else if (key === "at") out.at = val;
437
- else if (key === "length") out.length = val;
438
- else out.attrs[key] = val;
439
- continue;
440
- }
441
- out.attrs[tok] = "true";
442
- }
443
- return out;
444
- }
445
- function parseCircuit(text2) {
446
- const rawLines = text2.split("\n");
447
- const firstMeaningful = rawLines.map((l) => l.replace(/#.*$/, "").trim()).find((l) => l.length > 0) ?? "";
448
- if (/^circuit\b.*\bnetlist\s*$/i.test(firstMeaningful)) {
449
- const netlistTitle = chunkNAGUZFXX_cjs.matchQuotedTitle(firstMeaningful);
450
- let headerIdx = -1;
451
- for (let i = 0; i < rawLines.length; i++) {
452
- const s = rawLines[i].replace(/#.*$/, "").trim();
453
- if (s.length > 0) {
454
- headerIdx = i;
455
- break;
456
- }
457
- }
458
- const body = rawLines.slice(headerIdx + 1).join("\n");
459
- return parseNetlist(body, netlistTitle);
460
- }
461
- const lines = text2.split("\n").map((l) => l.replace(/\r$/, ""));
462
- let title2;
463
- const components = [];
464
- const nets = [];
465
- const netByName = /* @__PURE__ */ new Map();
466
- let autoId = 0;
467
- let pendingAt;
468
- const mkId = (prefix) => `${prefix}_${autoId++}`;
469
- for (const rawLine of lines) {
470
- const stripped = rawLine.replace(/#.*$/, "").trim();
471
- if (!stripped) continue;
472
- if (/^circuit\b/i.test(stripped)) {
473
- const t = chunkNAGUZFXX_cjs.matchQuotedTitle(stripped);
474
- if (t !== void 0) title2 = t;
475
- continue;
476
- }
477
- const atMatch = stripped.match(/^at:\s*(.+)$/i);
478
- if (atMatch) {
479
- pendingAt = atMatch[1].trim();
480
- continue;
481
- }
482
- const netDecl = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*$/i);
483
- if (netDecl) {
484
- const name = netDecl[1];
485
- if (!netByName.has(name)) {
486
- const n = { id: name, anchors: [] };
487
- netByName.set(name, n);
488
- nets.push(n);
489
- }
490
- continue;
491
- }
492
- const netDotMatch = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*:\s*dot\s*$/i);
493
- if (netDotMatch) {
494
- const name = netDotMatch[1];
495
- let n = netByName.get(name);
496
- if (!n) {
497
- n = { id: name, anchors: [] };
498
- netByName.set(name, n);
499
- nets.push(n);
500
- }
501
- const id = mkId("dot");
502
- components.push({
503
- id,
504
- componentType: "dot",
505
- direction: "right",
506
- at: pendingAt,
507
- attrs: { net: name }
508
- });
509
- n.anchors.push(`${id}.end`);
510
- pendingAt = `${id}.end`;
511
- continue;
512
- }
513
- const labelMatch = stripped.match(/^label\s+"([^"]*)"(?:\s+(right|left|up|down))?\s*$/i);
514
- if (labelMatch) {
515
- const id = mkId("lbl");
516
- components.push({
517
- id,
518
- componentType: "label",
519
- direction: labelMatch[2]?.toLowerCase() ?? "right",
520
- at: pendingAt,
521
- label: labelMatch[1]
522
- });
523
- continue;
524
- }
525
- const wireMatch = stripped.match(/^wire(?:\s+(right|left|up|down))?(?:\s+(\d+)(?:px)?)?\s*$/i);
526
- if (wireMatch) {
527
- const id = mkId("w");
528
- components.push({
529
- id,
530
- componentType: "wire",
531
- direction: wireMatch[1]?.toLowerCase() ?? "right",
532
- at: pendingAt,
533
- attrs: wireMatch[2] ? { length: wireMatch[2] } : {}
534
- });
535
- pendingAt = `${id}.end`;
536
- continue;
537
- }
538
- const bareMatch = stripped.match(/^([a-zA-Z_][\w]*)(\s+.*)?$/);
539
- const colonMatch = stripped.match(/^([a-zA-Z_][\w]*)\s*:\s*([a-zA-Z_][\w]*)(\s+.*)?$/);
540
- if (colonMatch) {
541
- const id = colonMatch[1];
542
- const typeStr = colonMatch[2];
543
- const norm = normalizeType(typeStr);
544
- if (!norm) {
545
- throw new CircuitParseError(`Unknown component type: ${typeStr}`);
546
- }
547
- const rest = colonMatch[3] ?? "";
548
- const parsed = parseAttrs(rest);
549
- const comp = {
550
- id,
551
- componentType: norm,
552
- direction: parsed.direction ?? "right",
553
- at: parsed.at ?? pendingAt,
554
- label: parsed.label,
555
- value: parsed.value,
556
- attrs: parsed.attrs
557
- };
558
- if (parsed.length) {
559
- comp.attrs = { ...comp.attrs, length: parsed.length };
560
- }
561
- components.push(comp);
562
- pendingAt = `${id}.end`;
563
- continue;
564
- }
565
- if (bareMatch) {
566
- const typeStr = bareMatch[1];
567
- const norm = normalizeType(typeStr);
568
- if (!norm) {
569
- continue;
570
- }
571
- const rest = bareMatch[2] ?? "";
572
- const parsed = parseAttrs(rest);
573
- const id = mkId(norm);
574
- const comp = {
575
- id,
576
- componentType: norm,
577
- direction: parsed.direction ?? "right",
578
- at: parsed.at ?? pendingAt,
579
- label: parsed.label,
580
- value: parsed.value,
581
- attrs: parsed.attrs
582
- };
583
- components.push(comp);
584
- pendingAt = `${id}.end`;
585
- continue;
586
- }
587
- }
588
- return {
589
- type: "circuit",
590
- title: title2,
591
- components,
592
- nets,
593
- mode: "positional"
594
- };
595
- }
596
-
597
- // src/diagrams/circuit/symbols.ts
598
- var BODY = 'class="schematex-circuit-body"';
599
- var FILL = 'class="schematex-circuit-fill"';
600
- var WIRE = 'class="schematex-circuit-wire"';
601
- function lineWire(x1, y1, x2, y2) {
602
- return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" ${WIRE}/>`;
603
- }
604
- var resistor = {
605
- length: 40,
606
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
607
- svg: () => `<path d="M 0,0 L 5,0 L 8,-8 L 12,8 L 16,-8 L 20,8 L 24,-8 L 28,8 L 32,-8 L 35,0 L 40,0" ${BODY}/>`
608
- };
609
- var capacitor = {
610
- length: 20,
611
- anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
612
- svg: () => [
613
- lineWire(0, 0, 8, 0),
614
- `<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
615
- `<line x1="12" y1="-10" x2="12" y2="10" ${BODY}/>`,
616
- lineWire(12, 0, 20, 0)
617
- ].join("")
618
- };
619
- var electrolytic_cap = {
620
- length: 20,
621
- anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
622
- svg: () => [
623
- lineWire(0, 0, 8, 0),
624
- `<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
625
- `<path d="M 12,-10 Q 16,0 12,10" fill="none" class="schematex-circuit-body"/>`,
626
- lineWire(12, 0, 20, 0),
627
- `<text x="5" y="-12" class="schematex-circuit-pol">\u2212</text>`,
628
- `<text x="15" y="-12" class="schematex-circuit-pol">+</text>`
629
- ].join("")
630
- };
631
- var inductor = {
632
- length: 40,
633
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
634
- svg: () => `<path d="M 0,0 L 5,0 A 5,5 0 0 1 15,0 A 5,5 0 0 1 25,0 A 5,5 0 0 1 35,0 L 40,0" fill="none" ${BODY}/>`
635
- };
636
- var fuse = {
637
- length: 30,
638
- anchors: { start: { x: 0, y: 0 }, end: { x: 30, y: 0 } },
639
- svg: () => [
640
- lineWire(0, 0, 5, 0),
641
- `<rect x="5" y="-5" width="20" height="10" rx="5" fill="none" ${BODY}/>`,
642
- `<line x1="5" y1="0" x2="25" y2="0" ${BODY}/>`,
643
- lineWire(25, 0, 30, 0)
644
- ].join("")
46
+ var fuse = {
47
+ length: 30,
48
+ anchors: { start: { x: 0, y: 0 }, end: { x: 30, y: 0 } },
49
+ svg: () => [
50
+ lineWire(0, 0, 5, 0),
51
+ `<rect x="5" y="-5" width="20" height="10" rx="5" fill="none" ${BODY}/>`,
52
+ `<line x1="5" y1="0" x2="25" y2="0" ${BODY}/>`,
53
+ lineWire(25, 0, 30, 0)
54
+ ].join("")
645
55
  };
646
56
  var crystal = {
647
57
  length: 40,
@@ -656,6 +66,7 @@ var crystal = {
656
66
  };
657
67
  var transformer = {
658
68
  length: 60,
69
+ netlistPins: ["p1", "p2", "s1", "s2"],
659
70
  anchors: {
660
71
  start: { x: 0, y: 0 },
661
72
  end: { x: 60, y: 0 },
@@ -678,6 +89,7 @@ var transformer = {
678
89
  };
679
90
  var voltage_source = {
680
91
  length: 40,
92
+ netlistPins: ["plus", "minus"],
681
93
  // plus/minus aliases: end = +, start = − (matches on-symbol label)
682
94
  anchors: {
683
95
  start: { x: 0, y: 0 },
@@ -695,6 +107,7 @@ var voltage_source = {
695
107
  };
696
108
  var current_source = {
697
109
  length: 40,
110
+ netlistPins: ["plus", "minus"],
698
111
  anchors: {
699
112
  start: { x: 0, y: 0 },
700
113
  end: { x: 40, y: 0 },
@@ -711,6 +124,7 @@ var current_source = {
711
124
  };
712
125
  var ac_source = {
713
126
  length: 40,
127
+ netlistPins: ["plus", "minus"],
714
128
  anchors: {
715
129
  start: { x: 0, y: 0 },
716
130
  end: { x: 40, y: 0 },
@@ -726,6 +140,7 @@ var ac_source = {
726
140
  };
727
141
  var battery = {
728
142
  length: 24,
143
+ netlistPins: ["plus", "minus"],
729
144
  anchors: {
730
145
  start: { x: 0, y: 0 },
731
146
  end: { x: 24, y: 0 },
@@ -826,6 +241,7 @@ var photodiode = {
826
241
  };
827
242
  var npn = {
828
243
  length: 40,
244
+ netlistPins: ["c", "b", "e"],
829
245
  // base at left, collector up-right, emitter down-right
830
246
  anchors: {
831
247
  start: { x: 0, y: 0 },
@@ -851,6 +267,7 @@ var npn = {
851
267
  };
852
268
  var pnp = {
853
269
  length: 40,
270
+ netlistPins: ["c", "b", "e"],
854
271
  anchors: {
855
272
  start: { x: 0, y: 0 },
856
273
  end: { x: 40, y: -16 },
@@ -875,6 +292,7 @@ var pnp = {
875
292
  };
876
293
  var nmos = {
877
294
  length: 40,
295
+ netlistPins: ["d", "g", "s"],
878
296
  anchors: {
879
297
  start: { x: 0, y: 0 },
880
298
  end: { x: 40, y: -16 },
@@ -900,6 +318,7 @@ var nmos = {
900
318
  };
901
319
  var pmos = {
902
320
  length: 40,
321
+ netlistPins: ["d", "g", "s"],
903
322
  anchors: {
904
323
  start: { x: 0, y: 0 },
905
324
  end: { x: 40, y: -16 },
@@ -925,12 +344,16 @@ var pmos = {
925
344
  };
926
345
  var jfet_n = {
927
346
  length: 40,
347
+ netlistPins: ["d", "g", "s"],
928
348
  anchors: {
929
349
  start: { x: 0, y: 0 },
930
350
  end: { x: 40, y: -16 },
931
351
  gate: { x: 0, y: 0 },
932
352
  drain: { x: 40, y: -16 },
933
- source: { x: 40, y: 16 }
353
+ source: { x: 40, y: 16 },
354
+ g: { x: 0, y: 0 },
355
+ d: { x: 40, y: -16 },
356
+ s: { x: 40, y: 16 }
934
357
  },
935
358
  svg: () => [
936
359
  lineWire(0, 0, 14, 0),
@@ -944,12 +367,16 @@ var jfet_n = {
944
367
  };
945
368
  var jfet_p = {
946
369
  length: 40,
370
+ netlistPins: ["d", "g", "s"],
947
371
  anchors: {
948
372
  start: { x: 0, y: 0 },
949
373
  end: { x: 40, y: -16 },
950
374
  gate: { x: 0, y: 0 },
951
375
  drain: { x: 40, y: -16 },
952
- source: { x: 40, y: 16 }
376
+ source: { x: 40, y: 16 },
377
+ g: { x: 0, y: 0 },
378
+ d: { x: 40, y: -16 },
379
+ s: { x: 40, y: 16 }
953
380
  },
954
381
  svg: () => [
955
382
  lineWire(0, 0, 14, 0),
@@ -963,6 +390,7 @@ var jfet_p = {
963
390
  };
964
391
  var opamp = {
965
392
  length: 50,
393
+ netlistPins: ["plus", "minus", "out"],
966
394
  anchors: {
967
395
  start: { x: 0, y: 0 },
968
396
  end: { x: 50, y: 0 },
@@ -980,6 +408,7 @@ var opamp = {
980
408
  };
981
409
  var comparator = {
982
410
  length: 50,
411
+ netlistPins: ["plus", "minus", "out"],
983
412
  anchors: {
984
413
  start: { x: 0, y: 0 },
985
414
  end: { x: 50, y: 0 },
@@ -1128,6 +557,7 @@ var variable_inductor = {
1128
557
  };
1129
558
  var switch_spdt = {
1130
559
  length: 50,
560
+ netlistPins: ["common", "nc", "no"],
1131
561
  anchors: {
1132
562
  start: { x: 0, y: 0 },
1133
563
  end: { x: 50, y: 0 },
@@ -1202,292 +632,893 @@ var antenna = {
1202
632
  `<line x1="0" y1="-10" x2="10" y2="-24" ${BODY}/>`
1203
633
  ].join("")
1204
634
  };
1205
- function icSymbol(defaultLeft, defaultRight, bodyLabel) {
1206
- const BODY_W = 80;
1207
- return {
1208
- length: BODY_W,
1209
- anchors: {
1210
- start: { x: 0, y: 0 },
1211
- end: { x: BODY_W, y: 0 }
1212
- },
1213
- svg: (_label, _value, attrs) => {
1214
- const left = attrs?.pins_left ? attrs.pins_left.split(",").map((s) => s.trim()) : defaultLeft;
1215
- const right = attrs?.pins_right ? attrs.pins_right.split(",").map((s) => s.trim()) : defaultRight;
1216
- const n = Math.max(left.length, right.length, 2);
1217
- const pitch = 16;
1218
- const bodyH = pitch * (n + 1);
1219
- const topY = -bodyH / 2;
1220
- const parts = [];
1221
- parts.push(`<rect x="0" y="${topY}" width="${BODY_W}" height="${bodyH}" fill="white" ${BODY}/>`);
1222
- const labelText = attrs?.ic_label ?? bodyLabel ?? "";
1223
- if (labelText) {
1224
- parts.push(`<text x="${BODY_W / 2}" y="3" text-anchor="middle" class="schematex-circuit-meter">${labelText}</text>`);
1225
- }
1226
- for (let i = 0; i < left.length; i++) {
1227
- const y = topY + pitch * (i + 1);
1228
- parts.push(`<line x1="-8" y1="${y}" x2="0" y2="${y}" ${WIRE}/>`);
1229
- parts.push(`<text x="4" y="${y + 3}" class="schematex-circuit-pol">${left[i]}</text>`);
635
+ function icSymbol(defaultLeft, defaultRight, bodyLabel) {
636
+ const BODY_W = 80;
637
+ return {
638
+ length: BODY_W,
639
+ netlistPins: [],
640
+ anchors: {
641
+ start: { x: 0, y: 0 },
642
+ end: { x: BODY_W, y: 0 }
643
+ },
644
+ svg: (_label, _value, attrs) => {
645
+ const left = attrs?.pins_left ? attrs.pins_left.split(",").map((s) => s.trim()) : defaultLeft;
646
+ const right = attrs?.pins_right ? attrs.pins_right.split(",").map((s) => s.trim()) : defaultRight;
647
+ const n = Math.max(left.length, right.length, 2);
648
+ const pitch = 16;
649
+ const bodyH = pitch * (n + 1);
650
+ const topY = -bodyH / 2;
651
+ const parts = [];
652
+ parts.push(`<rect x="0" y="${topY}" width="${BODY_W}" height="${bodyH}" fill="white" ${BODY}/>`);
653
+ const labelText = attrs?.ic_label ?? bodyLabel ?? "";
654
+ if (labelText) {
655
+ parts.push(`<text x="${BODY_W / 2}" y="3" text-anchor="middle" class="schematex-circuit-meter">${labelText}</text>`);
656
+ }
657
+ for (let i = 0; i < left.length; i++) {
658
+ const y = topY + pitch * (i + 1);
659
+ parts.push(`<line x1="-8" y1="${y}" x2="0" y2="${y}" ${WIRE}/>`);
660
+ parts.push(`<text x="4" y="${y + 3}" class="schematex-circuit-pol">${left[i]}</text>`);
661
+ }
662
+ for (let i = 0; i < right.length; i++) {
663
+ const y = topY + pitch * (i + 1);
664
+ parts.push(`<line x1="${BODY_W}" y1="${y}" x2="${BODY_W + 8}" y2="${y}" ${WIRE}/>`);
665
+ parts.push(`<text x="${BODY_W - 4}" y="${y + 3}" text-anchor="end" class="schematex-circuit-pol">${right[i]}</text>`);
666
+ }
667
+ return parts.join("");
668
+ }
669
+ };
670
+ }
671
+ var generic_ic = icSymbol(
672
+ ["1", "2", "3", "4"],
673
+ ["8", "7", "6", "5"],
674
+ "IC"
675
+ );
676
+ var timer_555 = (() => {
677
+ const left = ["GND", "TRG", "OUT", "RST"];
678
+ const right = ["VCC", "DIS", "THR", "CTL"];
679
+ const sym = icSymbol(left, right, "555");
680
+ const BODY_W = 80;
681
+ const pitch = 16;
682
+ const n = 4;
683
+ const topY = -80 / 2;
684
+ const anchors = {
685
+ start: { x: 0, y: 0 },
686
+ end: { x: BODY_W, y: 0 }
687
+ };
688
+ const leftNames = ["gnd", "trg", "out", "rst"];
689
+ const rightNames = ["vcc", "dis", "thr", "ctl"];
690
+ for (let i = 0; i < n; i++) {
691
+ const y = topY + pitch * (i + 1);
692
+ anchors[leftNames[i]] = { x: -8, y };
693
+ anchors[rightNames[i]] = { x: BODY_W + 8, y };
694
+ }
695
+ return { ...sym, anchors };
696
+ })();
697
+ var voltage_regulator = {
698
+ length: 60,
699
+ netlistPins: ["in", "gnd", "out"],
700
+ anchors: {
701
+ start: { x: 0, y: 0 },
702
+ end: { x: 60, y: 0 },
703
+ in: { x: 0, y: 0 },
704
+ out: { x: 60, y: 0 },
705
+ gnd: { x: 30, y: 20 }
706
+ },
707
+ svg: (_l, _v, attrs) => [
708
+ lineWire(0, 0, 10, 0),
709
+ `<rect x="10" y="-15" width="40" height="30" fill="white" ${BODY}/>`,
710
+ `<text x="30" y="4" text-anchor="middle" class="schematex-circuit-meter">${attrs?.model ?? "REG"}</text>`,
711
+ lineWire(50, 0, 60, 0),
712
+ lineWire(30, 15, 30, 20)
713
+ ].join("")
714
+ };
715
+ var potentiometer = {
716
+ length: 50,
717
+ netlistPins: ["start", "wiper", "end"],
718
+ anchors: {
719
+ start: { x: 0, y: 0 },
720
+ end: { x: 50, y: 0 },
721
+ wiper: { x: 25, y: -22 }
722
+ },
723
+ svg: () => [
724
+ `<path d="M 0,0 L 10,0 L 13,-8 L 18,8 L 23,-8 L 28,8 L 33,-8 L 38,8 L 40,0 L 50,0" ${BODY}/>`,
725
+ `<line x1="25" y1="-22" x2="25" y2="-10" ${BODY}/>`,
726
+ `<polygon points="25,-10 21,-16 29,-16" ${FILL}/>`
727
+ ].join("")
728
+ };
729
+ var terminal_block = {
730
+ length: 80,
731
+ netlistPins: [],
732
+ anchors: { start: { x: 0, y: 0 }, end: { x: 80, y: 0 } },
733
+ svg: (label, _value, attrs) => {
734
+ const pinsAttr = attrs?.pins ?? attrs?.terminals ?? "1,2,3,4";
735
+ const pins = pinsAttr.split(",").map((s) => s.trim()).filter(Boolean);
736
+ const n = Math.max(pins.length, 1);
737
+ const BODY_W = 80;
738
+ const pitch = 18;
739
+ const bodyH = pitch * (n + 1);
740
+ const topY = -bodyH / 2;
741
+ const parts = [];
742
+ parts.push(
743
+ `<rect x="0" y="${topY}" width="${BODY_W}" height="${bodyH}" rx="3" fill="white" stroke-width="2" ${BODY}/>`
744
+ );
745
+ if (label) {
746
+ parts.push(
747
+ `<text x="${BODY_W / 2}" y="${topY + 14}" text-anchor="middle" class="schematex-circuit-meter">${label}</text>`
748
+ );
749
+ }
750
+ for (let i = 0; i < n; i++) {
751
+ const y = topY + pitch * (i + 1) + (label ? 8 : 0);
752
+ parts.push(`<line x1="-8" y1="${y}" x2="0" y2="${y}" ${WIRE}/>`);
753
+ parts.push(`<circle cx="6" cy="${y}" r="2" ${FILL}/>`);
754
+ parts.push(
755
+ `<text x="12" y="${y + 3}" class="schematex-circuit-pol">${pins[i] ?? `T${i + 1}`}</text>`
756
+ );
757
+ }
758
+ return parts.join("");
759
+ }
760
+ };
761
+ var relay_coil = {
762
+ length: 40,
763
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
764
+ svg: () => [
765
+ lineWire(0, 0, 8, 0),
766
+ `<rect x="8" y="-10" width="24" height="20" fill="white" ${BODY}/>`,
767
+ // Diagonal slash inside the box = coil winding indicator
768
+ `<line x1="8" y1="-10" x2="32" y2="10" ${BODY}/>`,
769
+ lineWire(32, 0, 40, 0)
770
+ ].join("")
771
+ };
772
+ var relay_no = {
773
+ length: 40,
774
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
775
+ svg: () => [
776
+ lineWire(0, 0, 10, 0),
777
+ `<line x1="10" y1="0" x2="30" y2="-10" ${BODY}/>`,
778
+ `<circle cx="10" cy="0" r="2" ${FILL}/>`,
779
+ `<circle cx="30" cy="0" r="2" ${FILL}/>`,
780
+ // Small dashed bar above to denote actuator-driven (vs hand switch)
781
+ `<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
782
+ lineWire(30, 0, 40, 0)
783
+ ].join("")
784
+ };
785
+ var relay_nc = {
786
+ length: 40,
787
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
788
+ svg: () => [
789
+ lineWire(0, 0, 10, 0),
790
+ `<line x1="10" y1="-10" x2="30" y2="-10" ${BODY}/>`,
791
+ // NC slash: line crosses both contact endpoints (closed by default)
792
+ `<line x1="8" y1="2" x2="32" y2="-12" ${BODY}/>`,
793
+ `<circle cx="10" cy="0" r="2" ${FILL}/>`,
794
+ `<circle cx="30" cy="0" r="2" ${FILL}/>`,
795
+ `<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
796
+ lineWire(30, 0, 40, 0)
797
+ ].join("")
798
+ };
799
+ var contactor = {
800
+ length: 44,
801
+ anchors: { start: { x: 0, y: 0 }, end: { x: 44, y: 0 } },
802
+ svg: () => [
803
+ lineWire(0, 0, 10, 0),
804
+ // Two parallel diagonal contacts = double-break (typical of contactors)
805
+ `<line x1="10" y1="0" x2="30" y2="-10" stroke-width="2.5" ${BODY}/>`,
806
+ `<line x1="14" y1="2" x2="34" y2="-8" stroke-width="2.5" ${BODY}/>`,
807
+ `<circle cx="10" cy="0" r="2" ${FILL}/>`,
808
+ `<circle cx="34" cy="0" r="2" ${FILL}/>`,
809
+ // Solid bar above = electromagnetic-driven actuator
810
+ `<line x1="14" y1="-14" x2="30" y2="-14" stroke-width="2" ${BODY}/>`,
811
+ lineWire(34, 0, 44, 0)
812
+ ].join("")
813
+ };
814
+ var solenoid_valve = {
815
+ length: 50,
816
+ anchors: { start: { x: 0, y: 0 }, end: { x: 50, y: 0 } },
817
+ svg: () => [
818
+ lineWire(0, 0, 8, 0),
819
+ // Solenoid box on top
820
+ `<rect x="8" y="-22" width="14" height="12" fill="white" ${BODY}/>`,
821
+ `<line x1="8" y1="-22" x2="22" y2="-10" ${BODY}/>`,
822
+ // Valve body (triangle pair = IEC valve symbol)
823
+ `<polygon points="22,0 38,-8 38,8" fill="white" ${BODY}/>`,
824
+ `<polygon points="22,0 8,-8 8,8" fill="white" ${BODY}/>`,
825
+ // Connect solenoid to valve body (actuator line)
826
+ `<line x1="15" y1="-10" x2="22" y2="0" ${BODY}/>`,
827
+ lineWire(38, 0, 50, 0)
828
+ ].join("")
829
+ };
830
+ var thermal_overload = {
831
+ length: 40,
832
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
833
+ svg: () => [
834
+ lineWire(0, 0, 8, 0),
835
+ `<rect x="8" y="-12" width="24" height="24" fill="white" ${BODY}/>`,
836
+ // Bimetal element: bent line inside box
837
+ `<path d="M 12,-6 L 16,4 L 20,-6 L 24,4 L 28,-6" fill="none" ${BODY}/>`,
838
+ lineWire(32, 0, 40, 0)
839
+ ].join("")
840
+ };
841
+ var disconnect_switch = {
842
+ length: 48,
843
+ anchors: { start: { x: 0, y: 0 }, end: { x: 48, y: 0 } },
844
+ svg: () => [
845
+ lineWire(0, 0, 10, 0),
846
+ `<line x1="10" y1="0" x2="34" y2="-12" ${BODY}/>`,
847
+ `<circle cx="10" cy="0" r="2" ${FILL}/>`,
848
+ // Hollow square at top of arm = visible-isolation indicator
849
+ `<rect x="30" y="-16" width="8" height="8" fill="white" ${BODY}/>`,
850
+ `<circle cx="38" cy="0" r="2" ${FILL}/>`,
851
+ lineWire(38, 0, 48, 0)
852
+ ].join("")
853
+ };
854
+ var SYMBOLS = {
855
+ resistor,
856
+ capacitor,
857
+ electrolytic_cap,
858
+ inductor,
859
+ fuse,
860
+ crystal,
861
+ transformer,
862
+ voltage_source,
863
+ current_source,
864
+ ac_source,
865
+ battery,
866
+ ground,
867
+ gnd_signal,
868
+ vcc,
869
+ diode,
870
+ zener,
871
+ schottky,
872
+ led,
873
+ photodiode,
874
+ npn,
875
+ pnp,
876
+ nmos,
877
+ pmos,
878
+ jfet_n,
879
+ jfet_p,
880
+ opamp,
881
+ comparator,
882
+ switch_spst,
883
+ push_no,
884
+ ammeter,
885
+ voltmeter,
886
+ wattmeter,
887
+ motor,
888
+ speaker,
889
+ microphone,
890
+ buzzer,
891
+ potentiometer,
892
+ rheostat,
893
+ thermistor_ntc,
894
+ thermistor_ptc,
895
+ ldr,
896
+ variable_cap,
897
+ variable_inductor,
898
+ switch_spdt,
899
+ push_nc,
900
+ gnd_chassis,
901
+ gnd_digital,
902
+ test_point,
903
+ no_connect,
904
+ antenna,
905
+ generic_ic,
906
+ terminal_block,
907
+ "555_timer": timer_555,
908
+ voltage_regulator,
909
+ // Industrial control / power electrical (IEC 60617)
910
+ relay_coil,
911
+ relay_no,
912
+ relay_nc,
913
+ contactor,
914
+ solenoid_valve,
915
+ thermal_overload,
916
+ disconnect_switch
917
+ // Lamp reuses buzzer slot? No, needs its own entry but our CircuitComponentType
918
+ // doesn't have "lamp". We map it via parser alias to "buzzer" or add specifically.
919
+ };
920
+ var EXTRA_SYMBOLS = {
921
+ lamp
922
+ };
923
+ function getSymbol(t) {
924
+ return SYMBOLS[t] ?? EXTRA_SYMBOLS[t];
925
+ }
926
+ function getNetlistPinOrder(t) {
927
+ const sym = getSymbol(t);
928
+ if (!sym) return void 0;
929
+ if (sym.netlistPins) return [...sym.netlistPins];
930
+ if (sym.anchors.start && sym.anchors.end) return ["start", "end"];
931
+ return Object.keys(sym.anchors);
932
+ }
933
+
934
+ // src/diagrams/circuit/netlist.ts
935
+ var NetlistParseError = class extends Error {
936
+ constructor(message, line) {
937
+ super(message);
938
+ this.line = line;
939
+ this.name = "NetlistParseError";
940
+ }
941
+ line;
942
+ };
943
+ var PREFIX_MAP = {
944
+ R: { type: "resistor", pins: ["start", "end"] },
945
+ C: { type: "capacitor", pins: ["start", "end"] },
946
+ L: { type: "inductor", pins: ["start", "end"] },
947
+ D: { type: "diode", pins: ["start", "end"] },
948
+ // anode → start, cathode → end
949
+ V: { type: "voltage_source", pins: ["plus", "minus"] },
950
+ I: { type: "current_source", pins: ["plus", "minus"] },
951
+ Q: { type: "npn", pins: ["c", "b", "e"] },
952
+ M: { type: "nmos", pins: ["d", "g", "s"] },
953
+ J: { type: "jfet_n", pins: ["d", "g", "s"] },
954
+ S: { type: "switch_spst", pins: ["start", "end"] },
955
+ F: { type: "fuse", pins: ["start", "end"] },
956
+ B: { type: "battery", pins: ["plus", "minus"] },
957
+ K: { type: "relay_coil", pins: ["start", "end"] },
958
+ U: { type: "generic_ic", pins: [] },
959
+ // pins declared via pins="..." attr
960
+ X: { type: "generic_ic", pins: [] },
961
+ W: { type: "wire", pins: ["start", "end"] },
962
+ // explicit wire (non-SPICE convention, common in EE textbooks)
963
+ T: { type: "terminal_block", pins: [] }
964
+ // pins via pins="..."
965
+ };
966
+ var TYPE_ALIASES = {
967
+ vsource: "voltage_source",
968
+ isource: "current_source",
969
+ acsource: "ac_source",
970
+ ecap: "electrolytic_cap",
971
+ pot: "potentiometer",
972
+ gnd: "ground",
973
+ ic: "generic_ic",
974
+ reg: "voltage_regulator",
975
+ timer555: "555_timer",
976
+ transistor: "npn",
977
+ tb: "terminal_block",
978
+ junction_box: "terminal_block",
979
+ jbox: "terminal_block",
980
+ enclosure: "terminal_block"
981
+ };
982
+ var GROUND_REF = /^(0|gnd|ground|earth|pe|agnd|dgnd|gnda|gndd|vss|com)(_\w+|\d+)?$/i;
983
+ function isGroundRef(s) {
984
+ return GROUND_REF.test(s);
985
+ }
986
+ function tokenize(line) {
987
+ const tokens = [];
988
+ let i = 0;
989
+ while (i < line.length) {
990
+ const ch = line[i];
991
+ if (ch === " " || ch === " ") {
992
+ i++;
993
+ continue;
994
+ }
995
+ if (ch === '"') {
996
+ const end = line.indexOf('"', i + 1);
997
+ tokens.push(line.slice(i, end < 0 ? line.length : end + 1));
998
+ i = end < 0 ? line.length : end + 1;
999
+ continue;
1000
+ }
1001
+ let j = i;
1002
+ while (j < line.length && line[j] !== " " && line[j] !== " ") {
1003
+ if (line[j] === "=" && line[j + 1] === '"') {
1004
+ const end = line.indexOf('"', j + 2);
1005
+ j = end < 0 ? line.length : end + 1;
1006
+ break;
1007
+ }
1008
+ j++;
1009
+ }
1010
+ tokens.push(line.slice(i, j));
1011
+ i = j;
1012
+ }
1013
+ return tokens;
1014
+ }
1015
+ function isKeyEqVal(tok) {
1016
+ const eq = tok.indexOf("=");
1017
+ return eq > 0 && /^[a-zA-Z_][\w]*$/.test(tok.slice(0, eq));
1018
+ }
1019
+ function parseKV(tok) {
1020
+ const eq = tok.indexOf("=");
1021
+ const key = tok.slice(0, eq).trim();
1022
+ let val = tok.slice(eq + 1).trim();
1023
+ if (val.startsWith('"') && val.endsWith('"')) val = val.slice(1, -1);
1024
+ return [key, val];
1025
+ }
1026
+ function parseNetlist(body, title2) {
1027
+ const components = [];
1028
+ const netByName = /* @__PURE__ */ new Map();
1029
+ const pinMap = {};
1030
+ let autoGnd = 0;
1031
+ const ensureNet = (name) => {
1032
+ let n = netByName.get(name);
1033
+ if (!n) {
1034
+ n = { id: name, anchors: [] };
1035
+ netByName.set(name, n);
1036
+ }
1037
+ return n;
1038
+ };
1039
+ const lines = body.split("\n").map((l) => l.replace(/\r$/, ""));
1040
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
1041
+ const raw = lines[lineIdx];
1042
+ const stripped = raw.replace(/#.*$/, "").trim();
1043
+ if (!stripped) continue;
1044
+ const tokens = tokenize(stripped);
1045
+ if (tokens.length < 2) {
1046
+ throw new NetlistParseError(
1047
+ `Netlist line must have at least ID + one net: "${stripped}"`,
1048
+ lineIdx + 1
1049
+ );
1050
+ }
1051
+ const id = tokens[0];
1052
+ if (!/^[a-zA-Z_][\w]*$/.test(id)) {
1053
+ throw new NetlistParseError(`Invalid component id: "${id}"`, lineIdx + 1);
1054
+ }
1055
+ let cursor = 1;
1056
+ const netRefs = [];
1057
+ while (cursor < tokens.length && !isKeyEqVal(tokens[cursor])) {
1058
+ netRefs.push(tokens[cursor]);
1059
+ cursor++;
1060
+ }
1061
+ const kv = {};
1062
+ for (; cursor < tokens.length; cursor++) {
1063
+ if (!isKeyEqVal(tokens[cursor])) {
1064
+ throw new NetlistParseError(
1065
+ `Expected key=value after nets, got "${tokens[cursor]}"`,
1066
+ lineIdx + 1
1067
+ );
1068
+ }
1069
+ const [k, v] = parseKV(tokens[cursor]);
1070
+ kv[k] = v;
1071
+ }
1072
+ const prefix = id[0].toUpperCase();
1073
+ const defaults = PREFIX_MAP[prefix];
1074
+ let cType;
1075
+ let pinOrder;
1076
+ if (kv.type) {
1077
+ const t = kv.type.toLowerCase();
1078
+ cType = TYPE_ALIASES[t] ?? t;
1079
+ pinOrder = getNetlistPinOrder(cType) ?? defaults?.pins ?? ["start", "end"];
1080
+ } else if (defaults) {
1081
+ cType = defaults.type;
1082
+ pinOrder = defaults.pins;
1083
+ } else if (isGroundRef(id)) {
1084
+ cType = "ground";
1085
+ pinOrder = ["start"];
1086
+ } else {
1087
+ throw new NetlistParseError(
1088
+ `Cannot infer type from id "${id}". Either rename to a SPICE-prefix id (R*, C*, L*, D*, V*, I*, Q*, M*, J*, S*, F*, B*, K*, U*, X*, W*, T*) or pass an explicit \`type=<name>\` attribute (e.g. \`${id} ... type=resistor\`). Schematex circuit covers electrical schematics only \u2014 hydraulic/pneumatic prefixes (EV*, BOMBA*, TANK*, etc.) are not supported.`,
1089
+ lineIdx + 1
1090
+ );
1091
+ }
1092
+ const expectedPins = pinOrder.length;
1093
+ let valueFromTail;
1094
+ if (netRefs.length > expectedPins && expectedPins > 0) {
1095
+ const tail = netRefs.slice(expectedPins);
1096
+ netRefs.length = expectedPins;
1097
+ const first = tail[0].toLowerCase();
1098
+ if (TYPE_ALIASES[first]) {
1099
+ cType = TYPE_ALIASES[first];
1100
+ pinOrder = getNetlistPinOrder(cType) ?? pinOrder;
1101
+ } else if (first === "npn" || first === "pnp" || first === "nmos" || first === "pmos" || first === "jfet_n" || first === "jfet_p" || first === "zener" || first === "schottky" || first === "led" || first === "photodiode") {
1102
+ cType = first;
1103
+ pinOrder = getNetlistPinOrder(cType) ?? pinOrder;
1104
+ } else {
1105
+ valueFromTail = tail.join(" ");
1106
+ }
1107
+ if (tail.length > 1 && !valueFromTail) {
1108
+ valueFromTail = tail.slice(1).join(" ");
1109
+ }
1110
+ }
1111
+ if (cType === "ground" || cType === "gnd_signal" || cType === "gnd_chassis" || cType === "gnd_digital") {
1112
+ pinOrder = ["start"];
1113
+ }
1114
+ if (cType === "terminal_block") {
1115
+ const pinsAttr = kv.pins ?? kv.terminals;
1116
+ const labels = pinsAttr ? pinsAttr.split(",").map((s) => s.trim()).filter(Boolean) : Array.from({ length: Math.max(netRefs.length, 1) }, (_, i) => `t${i + 1}`);
1117
+ pinOrder = labels.map(
1118
+ (label, idx) => label.toLowerCase().replace(/[^a-z0-9]+/g, "_") || `t${idx + 1}`
1119
+ );
1120
+ }
1121
+ if (netRefs.length < pinOrder.length) {
1122
+ throw new NetlistParseError(
1123
+ `Component ${id} (${cType}) expects ${pinOrder.length} nets, got ${netRefs.length}`,
1124
+ lineIdx + 1
1125
+ );
1126
+ }
1127
+ const pins = {};
1128
+ for (let p = 0; p < pinOrder.length; p++) {
1129
+ let net = netRefs[p];
1130
+ if (isGroundRef(net)) net = "GND";
1131
+ pins[pinOrder[p]] = net;
1132
+ ensureNet(net).anchors.push(`${id}.${pinOrder[p]}`);
1133
+ }
1134
+ pinMap[id] = pins;
1135
+ const comp = {
1136
+ id,
1137
+ componentType: cType,
1138
+ direction: "right",
1139
+ label: kv.label ?? id,
1140
+ value: kv.value ?? valueFromTail,
1141
+ attrs: {}
1142
+ };
1143
+ for (const [k, v] of Object.entries(kv)) {
1144
+ if (k === "label" || k === "value" || k === "type") continue;
1145
+ comp.attrs[k] = v;
1146
+ }
1147
+ components.push(comp);
1148
+ }
1149
+ if (netByName.has("GND")) {
1150
+ const hasGroundSym = components.some(
1151
+ (c) => c.componentType === "ground" || c.componentType === "gnd_signal" || c.componentType === "gnd_chassis" || c.componentType === "gnd_digital"
1152
+ );
1153
+ if (!hasGroundSym) {
1154
+ const gId = `_GND${autoGnd++}`;
1155
+ components.push({
1156
+ id: gId,
1157
+ componentType: "ground",
1158
+ direction: "down",
1159
+ label: void 0,
1160
+ value: void 0,
1161
+ attrs: { auto: "true" }
1162
+ });
1163
+ pinMap[gId] = { start: "GND" };
1164
+ ensureNet("GND").anchors.push(`${gId}.start`);
1165
+ }
1166
+ }
1167
+ const nets = Array.from(netByName.values());
1168
+ return {
1169
+ type: "circuit",
1170
+ title: title2,
1171
+ components,
1172
+ nets,
1173
+ pinMap,
1174
+ mode: "netlist"
1175
+ };
1176
+ }
1177
+
1178
+ // src/diagrams/circuit/parser.ts
1179
+ var CircuitParseError = class extends Error {
1180
+ constructor(message) {
1181
+ super(message);
1182
+ this.name = "CircuitParseError";
1183
+ }
1184
+ };
1185
+ var COMPONENT_TYPES = /* @__PURE__ */ new Set([
1186
+ "resistor",
1187
+ "potentiometer",
1188
+ "rheostat",
1189
+ "thermistor_ntc",
1190
+ "thermistor_ptc",
1191
+ "ldr",
1192
+ "varistor",
1193
+ "fuse",
1194
+ "fuse_slow",
1195
+ "capacitor",
1196
+ "electrolytic_cap",
1197
+ "variable_cap",
1198
+ "inductor",
1199
+ "inductor_iron",
1200
+ "inductor_ferrite",
1201
+ "variable_inductor",
1202
+ "ferrite_bead",
1203
+ "crystal",
1204
+ "transformer",
1205
+ "diode",
1206
+ "zener",
1207
+ "schottky",
1208
+ "led",
1209
+ "photodiode",
1210
+ "varactor",
1211
+ "tvs_diode",
1212
+ "bridge_rectifier",
1213
+ "npn",
1214
+ "pnp",
1215
+ "darlington_npn",
1216
+ "darlington_pnp",
1217
+ "nmos",
1218
+ "pmos",
1219
+ "nmos_depletion",
1220
+ "jfet_n",
1221
+ "jfet_p",
1222
+ "igbt",
1223
+ "scr",
1224
+ "triac",
1225
+ "diac",
1226
+ "phototransistor",
1227
+ "optocoupler",
1228
+ "opamp",
1229
+ "comparator",
1230
+ "schmitt_buffer",
1231
+ "tri_state_buffer",
1232
+ "instrumentation_amp",
1233
+ "generic_ic",
1234
+ "voltage_regulator",
1235
+ "dc_dc_converter",
1236
+ "555_timer",
1237
+ "voltage_source",
1238
+ "current_source",
1239
+ "ac_source",
1240
+ "battery",
1241
+ "vcc",
1242
+ "ground",
1243
+ "gnd_signal",
1244
+ "gnd_chassis",
1245
+ "gnd_digital",
1246
+ "switch_spst",
1247
+ "switch_spdt",
1248
+ "switch_dpdt",
1249
+ "push_no",
1250
+ "push_nc",
1251
+ "relay_coil",
1252
+ "relay_no",
1253
+ "relay_nc",
1254
+ "contactor",
1255
+ "solenoid_valve",
1256
+ "thermal_overload",
1257
+ "disconnect_switch",
1258
+ "motor",
1259
+ "speaker",
1260
+ "microphone",
1261
+ "buzzer",
1262
+ "ammeter",
1263
+ "voltmeter",
1264
+ "wattmeter",
1265
+ "oscilloscope",
1266
+ "wire",
1267
+ "dot",
1268
+ "label",
1269
+ "port",
1270
+ "test_point",
1271
+ "no_connect",
1272
+ "antenna"
1273
+ ]);
1274
+ var ALIASES = {
1275
+ vsource: "voltage_source",
1276
+ isource: "current_source",
1277
+ acsource: "ac_source",
1278
+ ecap: "electrolytic_cap",
1279
+ pot: "potentiometer",
1280
+ xtal: "crystal",
1281
+ xfmr: "transformer",
1282
+ transistor: "npn",
1283
+ bjt_npn: "npn",
1284
+ bjt_pnp: "pnp",
1285
+ mosfet_n: "nmos",
1286
+ mosfet_p: "pmos",
1287
+ gnd: "ground",
1288
+ ic: "generic_ic",
1289
+ reg: "voltage_regulator",
1290
+ timer555: "555_timer",
1291
+ therm: "thermistor_ntc",
1292
+ ntc: "thermistor_ntc",
1293
+ ptc: "thermistor_ptc",
1294
+ ths: "thermistor_ntc",
1295
+ // Industrial control aliases (IEC letter codes)
1296
+ coil: "relay_coil",
1297
+ relay: "relay_coil",
1298
+ km: "contactor",
1299
+ solenoid: "solenoid_valve",
1300
+ ev: "solenoid_valve",
1301
+ overload: "thermal_overload",
1302
+ thermal: "thermal_overload",
1303
+ disconnect: "disconnect_switch",
1304
+ isolator: "disconnect_switch"
1305
+ };
1306
+ var EXTRA_TYPES = /* @__PURE__ */ new Set(["lamp"]);
1307
+ function normalizeType(raw) {
1308
+ const lower = raw.toLowerCase();
1309
+ if (ALIASES[lower]) return ALIASES[lower];
1310
+ if (COMPONENT_TYPES.has(lower)) {
1311
+ return lower;
1312
+ }
1313
+ if (EXTRA_TYPES.has(lower)) {
1314
+ return lower;
1315
+ }
1316
+ return null;
1317
+ }
1318
+ var DIRECTIONS = /* @__PURE__ */ new Set(["right", "left", "up", "down"]);
1319
+ function parseAttrs(rest) {
1320
+ const out = { attrs: {} };
1321
+ const tokens = [];
1322
+ let i = 0;
1323
+ while (i < rest.length) {
1324
+ const ch = rest[i];
1325
+ if (ch === " " || ch === " ") {
1326
+ i++;
1327
+ continue;
1328
+ }
1329
+ if (ch === '"') {
1330
+ const end = rest.indexOf('"', i + 1);
1331
+ const tok = rest.slice(i, end < 0 ? rest.length : end + 1);
1332
+ tokens.push(tok);
1333
+ i = end < 0 ? rest.length : end + 1;
1334
+ continue;
1335
+ }
1336
+ let j = i;
1337
+ while (j < rest.length && rest[j] !== " " && rest[j] !== " ") {
1338
+ if (rest[j] === "=" && rest[j + 1] === '"') {
1339
+ const end = rest.indexOf('"', j + 2);
1340
+ j = end < 0 ? rest.length : end + 1;
1341
+ break;
1230
1342
  }
1231
- for (let i = 0; i < right.length; i++) {
1232
- const y = topY + pitch * (i + 1);
1233
- parts.push(`<line x1="${BODY_W}" y1="${y}" x2="${BODY_W + 8}" y2="${y}" ${WIRE}/>`);
1234
- parts.push(`<text x="${BODY_W - 4}" y="${y + 3}" text-anchor="end" class="schematex-circuit-pol">${right[i]}</text>`);
1343
+ j++;
1344
+ }
1345
+ tokens.push(rest.slice(i, j));
1346
+ i = j;
1347
+ }
1348
+ for (const tok of tokens) {
1349
+ if (!tok) continue;
1350
+ if (DIRECTIONS.has(tok.toLowerCase())) {
1351
+ out.direction = tok.toLowerCase();
1352
+ continue;
1353
+ }
1354
+ const eq = tok.indexOf("=");
1355
+ if (eq > 0) {
1356
+ const key = tok.slice(0, eq).trim();
1357
+ let val = tok.slice(eq + 1).trim();
1358
+ if (val.startsWith('"') && val.endsWith('"')) {
1359
+ val = val.slice(1, -1);
1235
1360
  }
1236
- return parts.join("");
1361
+ if (key === "label") out.label = val;
1362
+ else if (key === "value") out.value = val;
1363
+ else if (key === "at") out.at = val;
1364
+ else if (key === "length") out.length = val;
1365
+ else out.attrs[key] = val;
1366
+ continue;
1237
1367
  }
1238
- };
1368
+ out.attrs[tok] = "true";
1369
+ }
1370
+ return out;
1239
1371
  }
1240
- var generic_ic = icSymbol(
1241
- ["1", "2", "3", "4"],
1242
- ["8", "7", "6", "5"],
1243
- "IC"
1244
- );
1245
- var timer_555 = (() => {
1246
- const left = ["GND", "TRG", "OUT", "RST"];
1247
- const right = ["VCC", "DIS", "THR", "CTL"];
1248
- const sym = icSymbol(left, right, "555");
1249
- const BODY_W = 80;
1250
- const pitch = 16;
1251
- const n = 4;
1252
- const topY = -80 / 2;
1253
- const anchors = {
1254
- start: { x: 0, y: 0 },
1255
- end: { x: BODY_W, y: 0 }
1256
- };
1257
- const leftNames = ["gnd", "trg", "out", "rst"];
1258
- const rightNames = ["vcc", "dis", "thr", "ctl"];
1259
- for (let i = 0; i < n; i++) {
1260
- const y = topY + pitch * (i + 1);
1261
- anchors[leftNames[i]] = { x: -8, y };
1262
- anchors[rightNames[i]] = { x: BODY_W + 8, y };
1372
+ function parseCircuit(text2) {
1373
+ const rawLines = text2.split("\n");
1374
+ const firstMeaningful = rawLines.map((l) => l.replace(/#.*$/, "").trim()).find((l) => l.length > 0) ?? "";
1375
+ if (/^circuit\b.*\bnetlist\s*$/i.test(firstMeaningful)) {
1376
+ const netlistTitle = chunkNAGUZFXX_cjs.matchQuotedTitle(firstMeaningful);
1377
+ let headerIdx = -1;
1378
+ for (let i = 0; i < rawLines.length; i++) {
1379
+ const s = rawLines[i].replace(/#.*$/, "").trim();
1380
+ if (s.length > 0) {
1381
+ headerIdx = i;
1382
+ break;
1383
+ }
1384
+ }
1385
+ const body = rawLines.slice(headerIdx + 1).join("\n");
1386
+ return parseNetlist(body, netlistTitle);
1263
1387
  }
1264
- return { ...sym, anchors };
1265
- })();
1266
- var voltage_regulator = {
1267
- length: 60,
1268
- anchors: {
1269
- start: { x: 0, y: 0 },
1270
- end: { x: 60, y: 0 },
1271
- in: { x: 0, y: 0 },
1272
- out: { x: 60, y: 0 },
1273
- gnd: { x: 30, y: 20 }
1274
- },
1275
- svg: (_l, _v, attrs) => [
1276
- lineWire(0, 0, 10, 0),
1277
- `<rect x="10" y="-15" width="40" height="30" fill="white" ${BODY}/>`,
1278
- `<text x="30" y="4" text-anchor="middle" class="schematex-circuit-meter">${attrs?.model ?? "REG"}</text>`,
1279
- lineWire(50, 0, 60, 0),
1280
- lineWire(30, 15, 30, 20)
1281
- ].join("")
1282
- };
1283
- var potentiometer = {
1284
- length: 50,
1285
- anchors: {
1286
- start: { x: 0, y: 0 },
1287
- end: { x: 50, y: 0 },
1288
- wiper: { x: 25, y: -22 }
1289
- },
1290
- svg: () => [
1291
- `<path d="M 0,0 L 10,0 L 13,-8 L 18,8 L 23,-8 L 28,8 L 33,-8 L 38,8 L 40,0 L 50,0" ${BODY}/>`,
1292
- `<line x1="25" y1="-22" x2="25" y2="-10" ${BODY}/>`,
1293
- `<polygon points="25,-10 21,-16 29,-16" ${FILL}/>`
1294
- ].join("")
1295
- };
1296
- var terminal_block = {
1297
- length: 80,
1298
- anchors: { start: { x: 0, y: 0 }, end: { x: 80, y: 0 } },
1299
- svg: (label, _value, attrs) => {
1300
- const pinsAttr = attrs?.pins ?? attrs?.terminals ?? "1,2,3,4";
1301
- const pins = pinsAttr.split(",").map((s) => s.trim()).filter(Boolean);
1302
- const n = Math.max(pins.length, 1);
1303
- const BODY_W = 80;
1304
- const pitch = 18;
1305
- const bodyH = pitch * (n + 1);
1306
- const topY = -bodyH / 2;
1307
- const parts = [];
1308
- parts.push(
1309
- `<rect x="0" y="${topY}" width="${BODY_W}" height="${bodyH}" rx="3" fill="white" stroke-width="2" ${BODY}/>`
1310
- );
1311
- if (label) {
1312
- parts.push(
1313
- `<text x="${BODY_W / 2}" y="${topY + 14}" text-anchor="middle" class="schematex-circuit-meter">${label}</text>`
1314
- );
1388
+ const lines = text2.split("\n").map((l) => l.replace(/\r$/, ""));
1389
+ let title2;
1390
+ const components = [];
1391
+ const nets = [];
1392
+ const netByName = /* @__PURE__ */ new Map();
1393
+ let autoId = 0;
1394
+ let pendingAt;
1395
+ const mkId = (prefix) => `${prefix}_${autoId++}`;
1396
+ for (const rawLine of lines) {
1397
+ const stripped = rawLine.replace(/#.*$/, "").trim();
1398
+ if (!stripped) continue;
1399
+ if (/^circuit\b/i.test(stripped)) {
1400
+ const t = chunkNAGUZFXX_cjs.matchQuotedTitle(stripped);
1401
+ if (t !== void 0) title2 = t;
1402
+ continue;
1403
+ }
1404
+ const atMatch = stripped.match(/^at:\s*(.+)$/i);
1405
+ if (atMatch) {
1406
+ pendingAt = atMatch[1].trim();
1407
+ continue;
1408
+ }
1409
+ const netDecl = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*$/i);
1410
+ if (netDecl) {
1411
+ const name = netDecl[1];
1412
+ if (!netByName.has(name)) {
1413
+ const n = { id: name, anchors: [] };
1414
+ netByName.set(name, n);
1415
+ nets.push(n);
1416
+ }
1417
+ continue;
1418
+ }
1419
+ const netDotMatch = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*:\s*dot\s*$/i);
1420
+ if (netDotMatch) {
1421
+ const name = netDotMatch[1];
1422
+ let n = netByName.get(name);
1423
+ if (!n) {
1424
+ n = { id: name, anchors: [] };
1425
+ netByName.set(name, n);
1426
+ nets.push(n);
1427
+ }
1428
+ const id = mkId("dot");
1429
+ components.push({
1430
+ id,
1431
+ componentType: "dot",
1432
+ direction: "right",
1433
+ at: pendingAt,
1434
+ attrs: { net: name }
1435
+ });
1436
+ n.anchors.push(`${id}.end`);
1437
+ pendingAt = `${id}.end`;
1438
+ continue;
1315
1439
  }
1316
- for (let i = 0; i < n; i++) {
1317
- const y = topY + pitch * (i + 1) + (label ? 8 : 0);
1318
- parts.push(`<line x1="-8" y1="${y}" x2="0" y2="${y}" ${WIRE}/>`);
1319
- parts.push(`<circle cx="6" cy="${y}" r="2" ${FILL}/>`);
1320
- parts.push(
1321
- `<text x="12" y="${y + 3}" class="schematex-circuit-pol">${pins[i] ?? `T${i + 1}`}</text>`
1322
- );
1440
+ const labelMatch = stripped.match(/^label\s+"([^"]*)"(?:\s+(right|left|up|down))?\s*$/i);
1441
+ if (labelMatch) {
1442
+ const id = mkId("lbl");
1443
+ components.push({
1444
+ id,
1445
+ componentType: "label",
1446
+ direction: labelMatch[2]?.toLowerCase() ?? "right",
1447
+ at: pendingAt,
1448
+ label: labelMatch[1]
1449
+ });
1450
+ continue;
1451
+ }
1452
+ const wireMatch = stripped.match(/^wire(?:\s+(right|left|up|down))?(?:\s+(\d+)(?:px)?)?\s*$/i);
1453
+ if (wireMatch) {
1454
+ const id = mkId("w");
1455
+ components.push({
1456
+ id,
1457
+ componentType: "wire",
1458
+ direction: wireMatch[1]?.toLowerCase() ?? "right",
1459
+ at: pendingAt,
1460
+ attrs: wireMatch[2] ? { length: wireMatch[2] } : {}
1461
+ });
1462
+ pendingAt = `${id}.end`;
1463
+ continue;
1464
+ }
1465
+ const bareMatch = stripped.match(/^([a-zA-Z_][\w]*)(\s+.*)?$/);
1466
+ const colonMatch = stripped.match(/^([a-zA-Z_][\w]*)\s*:\s*([a-zA-Z_][\w]*)(\s+.*)?$/);
1467
+ if (colonMatch) {
1468
+ const id = colonMatch[1];
1469
+ const typeStr = colonMatch[2];
1470
+ const norm = normalizeType(typeStr);
1471
+ if (!norm) {
1472
+ throw new CircuitParseError(`Unknown component type: ${typeStr}`);
1473
+ }
1474
+ const rest = colonMatch[3] ?? "";
1475
+ const parsed = parseAttrs(rest);
1476
+ const comp = {
1477
+ id,
1478
+ componentType: norm,
1479
+ direction: parsed.direction ?? "right",
1480
+ at: parsed.at ?? pendingAt,
1481
+ label: parsed.label,
1482
+ value: parsed.value,
1483
+ attrs: parsed.attrs
1484
+ };
1485
+ if (parsed.length) {
1486
+ comp.attrs = { ...comp.attrs, length: parsed.length };
1487
+ }
1488
+ components.push(comp);
1489
+ pendingAt = `${id}.end`;
1490
+ continue;
1491
+ }
1492
+ if (bareMatch) {
1493
+ const typeStr = bareMatch[1];
1494
+ const norm = normalizeType(typeStr);
1495
+ if (!norm) {
1496
+ continue;
1497
+ }
1498
+ const rest = bareMatch[2] ?? "";
1499
+ const parsed = parseAttrs(rest);
1500
+ const id = mkId(norm);
1501
+ const comp = {
1502
+ id,
1503
+ componentType: norm,
1504
+ direction: parsed.direction ?? "right",
1505
+ at: parsed.at ?? pendingAt,
1506
+ label: parsed.label,
1507
+ value: parsed.value,
1508
+ attrs: parsed.attrs
1509
+ };
1510
+ components.push(comp);
1511
+ pendingAt = `${id}.end`;
1512
+ continue;
1323
1513
  }
1324
- return parts.join("");
1325
1514
  }
1326
- };
1327
- var relay_coil = {
1328
- length: 40,
1329
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
1330
- svg: () => [
1331
- lineWire(0, 0, 8, 0),
1332
- `<rect x="8" y="-10" width="24" height="20" fill="white" ${BODY}/>`,
1333
- // Diagonal slash inside the box = coil winding indicator
1334
- `<line x1="8" y1="-10" x2="32" y2="10" ${BODY}/>`,
1335
- lineWire(32, 0, 40, 0)
1336
- ].join("")
1337
- };
1338
- var relay_no = {
1339
- length: 40,
1340
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
1341
- svg: () => [
1342
- lineWire(0, 0, 10, 0),
1343
- `<line x1="10" y1="0" x2="30" y2="-10" ${BODY}/>`,
1344
- `<circle cx="10" cy="0" r="2" ${FILL}/>`,
1345
- `<circle cx="30" cy="0" r="2" ${FILL}/>`,
1346
- // Small dashed bar above to denote actuator-driven (vs hand switch)
1347
- `<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
1348
- lineWire(30, 0, 40, 0)
1349
- ].join("")
1350
- };
1351
- var relay_nc = {
1352
- length: 40,
1353
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
1354
- svg: () => [
1355
- lineWire(0, 0, 10, 0),
1356
- `<line x1="10" y1="-10" x2="30" y2="-10" ${BODY}/>`,
1357
- // NC slash: line crosses both contact endpoints (closed by default)
1358
- `<line x1="8" y1="2" x2="32" y2="-12" ${BODY}/>`,
1359
- `<circle cx="10" cy="0" r="2" ${FILL}/>`,
1360
- `<circle cx="30" cy="0" r="2" ${FILL}/>`,
1361
- `<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
1362
- lineWire(30, 0, 40, 0)
1363
- ].join("")
1364
- };
1365
- var contactor = {
1366
- length: 44,
1367
- anchors: { start: { x: 0, y: 0 }, end: { x: 44, y: 0 } },
1368
- svg: () => [
1369
- lineWire(0, 0, 10, 0),
1370
- // Two parallel diagonal contacts = double-break (typical of contactors)
1371
- `<line x1="10" y1="0" x2="30" y2="-10" stroke-width="2.5" ${BODY}/>`,
1372
- `<line x1="14" y1="2" x2="34" y2="-8" stroke-width="2.5" ${BODY}/>`,
1373
- `<circle cx="10" cy="0" r="2" ${FILL}/>`,
1374
- `<circle cx="34" cy="0" r="2" ${FILL}/>`,
1375
- // Solid bar above = electromagnetic-driven actuator
1376
- `<line x1="14" y1="-14" x2="30" y2="-14" stroke-width="2" ${BODY}/>`,
1377
- lineWire(34, 0, 44, 0)
1378
- ].join("")
1379
- };
1380
- var solenoid_valve = {
1381
- length: 50,
1382
- anchors: { start: { x: 0, y: 0 }, end: { x: 50, y: 0 } },
1383
- svg: () => [
1384
- lineWire(0, 0, 8, 0),
1385
- // Solenoid box on top
1386
- `<rect x="8" y="-22" width="14" height="12" fill="white" ${BODY}/>`,
1387
- `<line x1="8" y1="-22" x2="22" y2="-10" ${BODY}/>`,
1388
- // Valve body (triangle pair = IEC valve symbol)
1389
- `<polygon points="22,0 38,-8 38,8" fill="white" ${BODY}/>`,
1390
- `<polygon points="22,0 8,-8 8,8" fill="white" ${BODY}/>`,
1391
- // Connect solenoid to valve body (actuator line)
1392
- `<line x1="15" y1="-10" x2="22" y2="0" ${BODY}/>`,
1393
- lineWire(38, 0, 50, 0)
1394
- ].join("")
1395
- };
1396
- var thermal_overload = {
1397
- length: 40,
1398
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
1399
- svg: () => [
1400
- lineWire(0, 0, 8, 0),
1401
- `<rect x="8" y="-12" width="24" height="24" fill="white" ${BODY}/>`,
1402
- // Bimetal element: bent line inside box
1403
- `<path d="M 12,-6 L 16,4 L 20,-6 L 24,4 L 28,-6" fill="none" ${BODY}/>`,
1404
- lineWire(32, 0, 40, 0)
1405
- ].join("")
1406
- };
1407
- var disconnect_switch = {
1408
- length: 48,
1409
- anchors: { start: { x: 0, y: 0 }, end: { x: 48, y: 0 } },
1410
- svg: () => [
1411
- lineWire(0, 0, 10, 0),
1412
- `<line x1="10" y1="0" x2="34" y2="-12" ${BODY}/>`,
1413
- `<circle cx="10" cy="0" r="2" ${FILL}/>`,
1414
- // Hollow square at top of arm = visible-isolation indicator
1415
- `<rect x="30" y="-16" width="8" height="8" fill="white" ${BODY}/>`,
1416
- `<circle cx="38" cy="0" r="2" ${FILL}/>`,
1417
- lineWire(38, 0, 48, 0)
1418
- ].join("")
1419
- };
1420
- var SYMBOLS = {
1421
- resistor,
1422
- capacitor,
1423
- electrolytic_cap,
1424
- inductor,
1425
- fuse,
1426
- crystal,
1427
- transformer,
1428
- voltage_source,
1429
- current_source,
1430
- ac_source,
1431
- battery,
1432
- ground,
1433
- gnd_signal,
1434
- vcc,
1435
- diode,
1436
- zener,
1437
- schottky,
1438
- led,
1439
- photodiode,
1440
- npn,
1441
- pnp,
1442
- nmos,
1443
- pmos,
1444
- jfet_n,
1445
- jfet_p,
1446
- opamp,
1447
- comparator,
1448
- switch_spst,
1449
- push_no,
1450
- ammeter,
1451
- voltmeter,
1452
- wattmeter,
1453
- motor,
1454
- speaker,
1455
- microphone,
1456
- buzzer,
1457
- potentiometer,
1458
- rheostat,
1459
- thermistor_ntc,
1460
- thermistor_ptc,
1461
- ldr,
1462
- variable_cap,
1463
- variable_inductor,
1464
- switch_spdt,
1465
- push_nc,
1466
- gnd_chassis,
1467
- gnd_digital,
1468
- test_point,
1469
- no_connect,
1470
- antenna,
1471
- generic_ic,
1472
- terminal_block,
1473
- "555_timer": timer_555,
1474
- voltage_regulator,
1475
- // Industrial control / power electrical (IEC 60617)
1476
- relay_coil,
1477
- relay_no,
1478
- relay_nc,
1479
- contactor,
1480
- solenoid_valve,
1481
- thermal_overload,
1482
- disconnect_switch
1483
- // Lamp reuses buzzer slot? No, needs its own entry but our CircuitComponentType
1484
- // doesn't have "lamp". We map it via parser alias to "buzzer" or add specifically.
1485
- };
1486
- var EXTRA_SYMBOLS = {
1487
- lamp
1488
- };
1489
- function getSymbol(t) {
1490
- return SYMBOLS[t] ?? EXTRA_SYMBOLS[t];
1515
+ return {
1516
+ type: "circuit",
1517
+ title: title2,
1518
+ components,
1519
+ nets,
1520
+ mode: "positional"
1521
+ };
1491
1522
  }
1492
1523
 
1493
1524
  // src/diagrams/circuit/layout.ts
@@ -2017,5 +2048,5 @@ exports.layoutCircuitNetlist = layoutCircuitNetlist;
2017
2048
  exports.parseCircuit = parseCircuit;
2018
2049
  exports.parseNetlist = parseNetlist;
2019
2050
  exports.renderCircuit = renderCircuit;
2020
- //# sourceMappingURL=chunk-XRCY75UV.cjs.map
2021
- //# sourceMappingURL=chunk-XRCY75UV.cjs.map
2051
+ //# sourceMappingURL=chunk-QMTWG6JL.cjs.map
2052
+ //# sourceMappingURL=chunk-QMTWG6JL.cjs.map