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