schematex 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +89 -7
  2. package/dist/ai/ai-sdk.cjs +32 -26
  3. package/dist/ai/ai-sdk.cjs.map +1 -1
  4. package/dist/ai/ai-sdk.d.cts +4 -3
  5. package/dist/ai/ai-sdk.d.ts +4 -3
  6. package/dist/ai/ai-sdk.js +28 -22
  7. package/dist/ai/ai-sdk.js.map +1 -1
  8. package/dist/ai/index.cjs +37 -25
  9. package/dist/ai/index.d.cts +20 -153
  10. package/dist/ai/index.d.ts +20 -153
  11. package/dist/ai/index.js +17 -17
  12. package/dist/{api-C5UcmT7n.d.cts → api-XWHHAhQI.d.ts} +12 -2
  13. package/dist/{api-C5UcmT7n.d.ts → api-qVDutqXH.d.cts} +12 -2
  14. package/dist/browser.cjs +37 -19
  15. package/dist/browser.cjs.map +1 -1
  16. package/dist/browser.d.cts +12 -3
  17. package/dist/browser.d.ts +12 -3
  18. package/dist/browser.js +27 -19
  19. package/dist/browser.js.map +1 -1
  20. package/dist/{chunk-3YUUC6RN.cjs → chunk-25ZON47K.cjs} +4105 -267
  21. package/dist/chunk-25ZON47K.cjs.map +1 -0
  22. package/dist/{chunk-N7W5KZK7.cjs → chunk-2L4YXZAZ.cjs} +4 -4
  23. package/dist/{chunk-N7W5KZK7.cjs.map → chunk-2L4YXZAZ.cjs.map} +1 -1
  24. package/dist/{chunk-ZYRCPSBU.js → chunk-3IE7KZY4.js} +4 -4
  25. package/dist/{chunk-ZYRCPSBU.js.map → chunk-3IE7KZY4.js.map} +1 -1
  26. package/dist/{chunk-ZX74KJPM.js → chunk-3VB5AT4R.js} +3 -3
  27. package/dist/{chunk-ZX74KJPM.js.map → chunk-3VB5AT4R.js.map} +1 -1
  28. package/dist/{chunk-S2KJRHDZ.cjs → chunk-4UFR2LB6.cjs} +13 -13
  29. package/dist/{chunk-S2KJRHDZ.cjs.map → chunk-4UFR2LB6.cjs.map} +1 -1
  30. package/dist/{chunk-QTNPMIO2.cjs → chunk-6JI6FWLZ.cjs} +341 -35
  31. package/dist/chunk-6JI6FWLZ.cjs.map +1 -0
  32. package/dist/{chunk-GTDQAN2Z.js → chunk-7AFW2J6J.js} +4075 -247
  33. package/dist/chunk-7AFW2J6J.js.map +1 -0
  34. package/dist/{chunk-UFTYX73U.js → chunk-7F76AWOI.js} +339 -35
  35. package/dist/chunk-7F76AWOI.js.map +1 -0
  36. package/dist/{chunk-BW4KGTV7.cjs → chunk-C5C5EF3W.cjs} +4 -4
  37. package/dist/{chunk-BW4KGTV7.cjs.map → chunk-C5C5EF3W.cjs.map} +1 -1
  38. package/dist/{chunk-VJGMEGMR.js → chunk-DOK7LKLO.js} +125 -13
  39. package/dist/chunk-DOK7LKLO.js.map +1 -0
  40. package/dist/{chunk-3M6WB62Y.cjs → chunk-ECD5XHBM.cjs} +139 -7
  41. package/dist/chunk-ECD5XHBM.cjs.map +1 -0
  42. package/dist/{chunk-L3CTXXVZ.js → chunk-FBS3PACU.js} +3 -3
  43. package/dist/{chunk-L3CTXXVZ.js.map → chunk-FBS3PACU.js.map} +1 -1
  44. package/dist/{chunk-QUKVGHN4.cjs → chunk-FKJBXGWP.cjs} +4 -4
  45. package/dist/{chunk-QUKVGHN4.cjs.map → chunk-FKJBXGWP.cjs.map} +1 -1
  46. package/dist/{chunk-SZK376QB.js → chunk-H4MT5TJP.js} +3 -3
  47. package/dist/{chunk-SZK376QB.js.map → chunk-H4MT5TJP.js.map} +1 -1
  48. package/dist/{chunk-3M6T7KB4.js → chunk-HAZALB7U.js} +3 -3
  49. package/dist/{chunk-3M6T7KB4.js.map → chunk-HAZALB7U.js.map} +1 -1
  50. package/dist/{chunk-VFZOPRQP.cjs → chunk-HWVBHU3O.cjs} +5 -5
  51. package/dist/{chunk-VFZOPRQP.cjs.map → chunk-HWVBHU3O.cjs.map} +1 -1
  52. package/dist/{chunk-D7EHZFK4.cjs → chunk-L7POWM5B.cjs} +138 -2
  53. package/dist/chunk-L7POWM5B.cjs.map +1 -0
  54. package/dist/{chunk-6OSUNBZY.js → chunk-LGABFD3L.js} +135 -7
  55. package/dist/chunk-LGABFD3L.js.map +1 -0
  56. package/dist/{chunk-2VNMKOUO.js → chunk-LRI4RH2N.js} +135 -3
  57. package/dist/chunk-LRI4RH2N.js.map +1 -0
  58. package/dist/{chunk-VDSYMSUY.js → chunk-MVIEIKOI.js} +3 -3
  59. package/dist/{chunk-VDSYMSUY.js.map → chunk-MVIEIKOI.js.map} +1 -1
  60. package/dist/{chunk-ZL5RB4UV.js → chunk-N5B242WY.js} +3 -3
  61. package/dist/{chunk-ZL5RB4UV.js.map → chunk-N5B242WY.js.map} +1 -1
  62. package/dist/{chunk-IM4RCUHA.js → chunk-P63S7P6N.js} +1309 -69
  63. package/dist/chunk-P63S7P6N.js.map +1 -0
  64. package/dist/{chunk-YLEVMOK2.cjs → chunk-R66QG3XT.cjs} +5 -4
  65. package/dist/{chunk-YLEVMOK2.cjs.map → chunk-R66QG3XT.cjs.map} +1 -1
  66. package/dist/{chunk-I55HO32M.js → chunk-RJMCWT7Z.js} +3 -3
  67. package/dist/{chunk-I55HO32M.js.map → chunk-RJMCWT7Z.js.map} +1 -1
  68. package/dist/{chunk-TZTCIAYW.cjs → chunk-S3RMAXH5.cjs} +137 -25
  69. package/dist/chunk-S3RMAXH5.cjs.map +1 -0
  70. package/dist/{chunk-IBRW3UOA.js → chunk-TWLKXV2O.js} +3 -3
  71. package/dist/{chunk-IBRW3UOA.js.map → chunk-TWLKXV2O.js.map} +1 -1
  72. package/dist/{chunk-HUPDIRBX.js → chunk-UWA5MWCI.js} +949 -918
  73. package/dist/chunk-UWA5MWCI.js.map +1 -0
  74. package/dist/{chunk-RYVV5UVI.cjs → chunk-V4GILQR6.cjs} +4 -4
  75. package/dist/{chunk-RYVV5UVI.cjs.map → chunk-V4GILQR6.cjs.map} +1 -1
  76. package/dist/{chunk-XRCY75UV.cjs → chunk-V4RO5KYY.cjs} +951 -918
  77. package/dist/chunk-V4RO5KYY.cjs.map +1 -0
  78. package/dist/{chunk-VZ5LDNHK.cjs → chunk-VTSH4YPT.cjs} +12 -12
  79. package/dist/{chunk-VZ5LDNHK.cjs.map → chunk-VTSH4YPT.cjs.map} +1 -1
  80. package/dist/{chunk-UUBNQV2T.cjs → chunk-WQDIZH2Z.cjs} +12 -12
  81. package/dist/{chunk-UUBNQV2T.cjs.map → chunk-WQDIZH2Z.cjs.map} +1 -1
  82. package/dist/{chunk-EGSUMHCS.cjs → chunk-YB4XJY5L.cjs} +12 -12
  83. package/dist/{chunk-EGSUMHCS.cjs.map → chunk-YB4XJY5L.cjs.map} +1 -1
  84. package/dist/{chunk-JHDR56XO.js → chunk-YS6CGUNH.js} +3 -3
  85. package/dist/{chunk-JHDR56XO.js.map → chunk-YS6CGUNH.js.map} +1 -1
  86. package/dist/{chunk-NWPCY65Z.cjs → chunk-YVDUEUFV.cjs} +1311 -68
  87. package/dist/chunk-YVDUEUFV.cjs.map +1 -0
  88. package/dist/{types-BOAsqHoU.d.ts → diagnostics-DRxhodP6.d.cts} +74 -2
  89. package/dist/{types-BOAsqHoU.d.cts → diagnostics-DRxhodP6.d.ts} +74 -2
  90. package/dist/diagrams/blockdiagram/index.d.cts +1 -1
  91. package/dist/diagrams/blockdiagram/index.d.ts +1 -1
  92. package/dist/diagrams/circuit/index.cjs +8 -8
  93. package/dist/diagrams/circuit/index.d.cts +1 -1
  94. package/dist/diagrams/circuit/index.d.ts +1 -1
  95. package/dist/diagrams/circuit/index.js +2 -2
  96. package/dist/diagrams/ecomap/index.cjs +7 -7
  97. package/dist/diagrams/ecomap/index.d.cts +1 -1
  98. package/dist/diagrams/ecomap/index.d.ts +1 -1
  99. package/dist/diagrams/ecomap/index.js +2 -2
  100. package/dist/diagrams/entity/index.cjs +6 -6
  101. package/dist/diagrams/entity/index.d.cts +1 -1
  102. package/dist/diagrams/entity/index.d.ts +1 -1
  103. package/dist/diagrams/entity/index.js +2 -2
  104. package/dist/diagrams/fishbone/index.cjs +8 -8
  105. package/dist/diagrams/fishbone/index.d.cts +1 -1
  106. package/dist/diagrams/fishbone/index.d.ts +1 -1
  107. package/dist/diagrams/fishbone/index.js +2 -2
  108. package/dist/diagrams/flowchart/index.cjs +8 -8
  109. package/dist/diagrams/flowchart/index.d.cts +2 -2
  110. package/dist/diagrams/flowchart/index.d.ts +2 -2
  111. package/dist/diagrams/flowchart/index.js +2 -2
  112. package/dist/diagrams/genogram/index.cjs +9 -9
  113. package/dist/diagrams/genogram/index.d.cts +1 -1
  114. package/dist/diagrams/genogram/index.d.ts +1 -1
  115. package/dist/diagrams/genogram/index.js +2 -2
  116. package/dist/diagrams/ladder/index.cjs +6 -6
  117. package/dist/diagrams/ladder/index.d.cts +1 -1
  118. package/dist/diagrams/ladder/index.d.ts +1 -1
  119. package/dist/diagrams/ladder/index.js +2 -2
  120. package/dist/diagrams/logic/index.cjs +6 -6
  121. package/dist/diagrams/logic/index.d.cts +1 -1
  122. package/dist/diagrams/logic/index.d.ts +1 -1
  123. package/dist/diagrams/logic/index.js +2 -2
  124. package/dist/diagrams/orgchart/index.cjs +7 -7
  125. package/dist/diagrams/orgchart/index.d.cts +1 -1
  126. package/dist/diagrams/orgchart/index.d.ts +1 -1
  127. package/dist/diagrams/orgchart/index.js +2 -2
  128. package/dist/diagrams/pedigree/index.cjs +7 -7
  129. package/dist/diagrams/pedigree/index.d.cts +1 -1
  130. package/dist/diagrams/pedigree/index.d.ts +1 -1
  131. package/dist/diagrams/pedigree/index.js +2 -2
  132. package/dist/diagrams/phylo/index.cjs +7 -7
  133. package/dist/diagrams/phylo/index.d.cts +1 -1
  134. package/dist/diagrams/phylo/index.d.ts +1 -1
  135. package/dist/diagrams/phylo/index.js +2 -2
  136. package/dist/diagrams/sld/index.cjs +14 -6
  137. package/dist/diagrams/sld/index.d.cts +14 -2
  138. package/dist/diagrams/sld/index.d.ts +14 -2
  139. package/dist/diagrams/sld/index.js +2 -2
  140. package/dist/diagrams/sociogram/index.cjs +6 -6
  141. package/dist/diagrams/sociogram/index.d.cts +1 -1
  142. package/dist/diagrams/sociogram/index.d.ts +1 -1
  143. package/dist/diagrams/sociogram/index.js +2 -2
  144. package/dist/diagrams/timing/index.d.cts +1 -1
  145. package/dist/diagrams/timing/index.d.ts +1 -1
  146. package/dist/diagrams/venn/index.cjs +9 -9
  147. package/dist/diagrams/venn/index.d.cts +1 -1
  148. package/dist/diagrams/venn/index.d.ts +1 -1
  149. package/dist/diagrams/venn/index.js +2 -2
  150. package/dist/{index-CJai_TEZ.d.ts → index-BRIkOPnd.d.cts} +60 -2
  151. package/dist/{index-C9A0h-CB.d.cts → index-C7SN-FB3.d.ts} +60 -2
  152. package/dist/index.cjs +351 -51
  153. package/dist/index.cjs.map +1 -1
  154. package/dist/index.d.cts +38 -4
  155. package/dist/index.d.ts +38 -4
  156. package/dist/index.js +283 -16
  157. package/dist/index.js.map +1 -1
  158. package/dist/react.cjs +21 -23
  159. package/dist/react.cjs.map +1 -1
  160. package/dist/react.d.cts +3 -2
  161. package/dist/react.d.ts +3 -2
  162. package/dist/react.js +21 -23
  163. package/dist/react.js.map +1 -1
  164. package/dist/tools-BVeUNdsU.d.ts +161 -0
  165. package/dist/tools-DdhP1kWY.d.cts +161 -0
  166. package/package.json +2 -2
  167. package/dist/chunk-2VNMKOUO.js.map +0 -1
  168. package/dist/chunk-3M6WB62Y.cjs.map +0 -1
  169. package/dist/chunk-3YUUC6RN.cjs.map +0 -1
  170. package/dist/chunk-6OSUNBZY.js.map +0 -1
  171. package/dist/chunk-D7EHZFK4.cjs.map +0 -1
  172. package/dist/chunk-GTDQAN2Z.js.map +0 -1
  173. package/dist/chunk-HUPDIRBX.js.map +0 -1
  174. package/dist/chunk-IM4RCUHA.js.map +0 -1
  175. package/dist/chunk-NWPCY65Z.cjs.map +0 -1
  176. package/dist/chunk-QTNPMIO2.cjs.map +0 -1
  177. package/dist/chunk-TZTCIAYW.cjs.map +0 -1
  178. package/dist/chunk-UFTYX73U.js.map +0 -1
  179. package/dist/chunk-VJGMEGMR.js.map +0 -1
  180. package/dist/chunk-XRCY75UV.cjs.map +0 -1
@@ -1,645 +1,55 @@
1
- import { resolveIndustrialTheme } from './chunk-2VNMKOUO.js';
1
+ import { resolveIndustrialTheme } from './chunk-LRI4RH2N.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;
5
+ // src/diagrams/circuit/symbols.ts
6
+ var BODY = 'class="schematex-circuit-body"';
7
+ var FILL = 'class="schematex-circuit-fill"';
8
+ var WIRE = 'class="schematex-circuit-wire"';
9
+ function lineWire(x1, y1, x2, y2) {
10
+ return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" ${WIRE}/>`;
11
+ }
12
+ var resistor = {
13
+ length: 40,
14
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
15
+ svg: () => `<path d="M 0,0 L 5,0 L 8,-8 L 12,8 L 16,-8 L 20,8 L 24,-8 L 28,8 L 32,-8 L 35,0 L 40,0" ${BODY}/>`
13
16
  };
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="..."
17
+ var capacitor = {
18
+ length: 20,
19
+ anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
20
+ svg: () => [
21
+ lineWire(0, 0, 8, 0),
22
+ `<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
23
+ `<line x1="12" y1="-10" x2="12" y2="10" ${BODY}/>`,
24
+ lineWire(12, 0, 20, 0)
25
+ ].join("")
36
26
  };
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"
27
+ var electrolytic_cap = {
28
+ length: 20,
29
+ anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
30
+ svg: () => [
31
+ lineWire(0, 0, 8, 0),
32
+ `<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
33
+ `<path d="M 12,-10 Q 16,0 12,10" fill="none" class="schematex-circuit-body"/>`,
34
+ lineWire(12, 0, 20, 0),
35
+ `<text x="5" y="-12" class="schematex-circuit-pol">\u2212</text>`,
36
+ `<text x="15" y="-12" class="schematex-circuit-pol">+</text>`
37
+ ].join("")
52
38
  };
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
- }
39
+ var inductor = {
40
+ length: 40,
41
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
42
+ svg: () => `<path d="M 0,0 L 5,0 A 5,5 0 0 1 15,0 A 5,5 0 0 1 25,0 A 5,5 0 0 1 35,0 L 40,0" fill="none" ${BODY}/>`
255
43
  };
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
- "contactor",
326
- "solenoid_valve",
327
- "thermal_overload",
328
- "disconnect_switch",
329
- "motor",
330
- "speaker",
331
- "microphone",
332
- "buzzer",
333
- "ammeter",
334
- "voltmeter",
335
- "wattmeter",
336
- "oscilloscope",
337
- "wire",
338
- "dot",
339
- "label",
340
- "port",
341
- "test_point",
342
- "no_connect",
343
- "antenna"
344
- ]);
345
- var ALIASES = {
346
- vsource: "voltage_source",
347
- isource: "current_source",
348
- acsource: "ac_source",
349
- ecap: "electrolytic_cap",
350
- pot: "potentiometer",
351
- xtal: "crystal",
352
- xfmr: "transformer",
353
- transistor: "npn",
354
- bjt_npn: "npn",
355
- bjt_pnp: "pnp",
356
- mosfet_n: "nmos",
357
- mosfet_p: "pmos",
358
- gnd: "ground",
359
- ic: "generic_ic",
360
- reg: "voltage_regulator",
361
- timer555: "555_timer",
362
- therm: "thermistor_ntc",
363
- ntc: "thermistor_ntc",
364
- ptc: "thermistor_ptc",
365
- ths: "thermistor_ntc",
366
- // Industrial control aliases (IEC letter codes)
367
- coil: "relay_coil",
368
- relay: "relay_coil",
369
- km: "contactor",
370
- solenoid: "solenoid_valve",
371
- ev: "solenoid_valve",
372
- overload: "thermal_overload",
373
- thermal: "thermal_overload",
374
- disconnect: "disconnect_switch",
375
- isolator: "disconnect_switch"
376
- };
377
- var EXTRA_TYPES = /* @__PURE__ */ new Set(["lamp"]);
378
- function normalizeType(raw) {
379
- const lower = raw.toLowerCase();
380
- if (ALIASES[lower]) return ALIASES[lower];
381
- if (COMPONENT_TYPES.has(lower)) {
382
- return lower;
383
- }
384
- if (EXTRA_TYPES.has(lower)) {
385
- return lower;
386
- }
387
- return null;
388
- }
389
- var DIRECTIONS = /* @__PURE__ */ new Set(["right", "left", "up", "down"]);
390
- function parseAttrs(rest) {
391
- const out = { attrs: {} };
392
- const tokens = [];
393
- let i = 0;
394
- while (i < rest.length) {
395
- const ch = rest[i];
396
- if (ch === " " || ch === " ") {
397
- i++;
398
- continue;
399
- }
400
- if (ch === '"') {
401
- const end = rest.indexOf('"', i + 1);
402
- const tok = rest.slice(i, end < 0 ? rest.length : end + 1);
403
- tokens.push(tok);
404
- i = end < 0 ? rest.length : end + 1;
405
- continue;
406
- }
407
- let j = i;
408
- while (j < rest.length && rest[j] !== " " && rest[j] !== " ") {
409
- if (rest[j] === "=" && rest[j + 1] === '"') {
410
- const end = rest.indexOf('"', j + 2);
411
- j = end < 0 ? rest.length : end + 1;
412
- break;
413
- }
414
- j++;
415
- }
416
- tokens.push(rest.slice(i, j));
417
- i = j;
418
- }
419
- for (const tok of tokens) {
420
- if (!tok) continue;
421
- if (DIRECTIONS.has(tok.toLowerCase())) {
422
- out.direction = tok.toLowerCase();
423
- continue;
424
- }
425
- const eq = tok.indexOf("=");
426
- if (eq > 0) {
427
- const key = tok.slice(0, eq).trim();
428
- let val = tok.slice(eq + 1).trim();
429
- if (val.startsWith('"') && val.endsWith('"')) {
430
- val = val.slice(1, -1);
431
- }
432
- if (key === "label") out.label = val;
433
- else if (key === "value") out.value = val;
434
- else if (key === "at") out.at = val;
435
- else if (key === "length") out.length = val;
436
- else out.attrs[key] = val;
437
- continue;
438
- }
439
- out.attrs[tok] = "true";
440
- }
441
- return out;
442
- }
443
- function parseCircuit(text2) {
444
- const rawLines = text2.split("\n");
445
- const firstMeaningful = rawLines.map((l) => l.replace(/#.*$/, "").trim()).find((l) => l.length > 0) ?? "";
446
- if (/^circuit\b.*\bnetlist\s*$/i.test(firstMeaningful)) {
447
- const netlistTitle = matchQuotedTitle(firstMeaningful);
448
- let headerIdx = -1;
449
- for (let i = 0; i < rawLines.length; i++) {
450
- const s = rawLines[i].replace(/#.*$/, "").trim();
451
- if (s.length > 0) {
452
- headerIdx = i;
453
- break;
454
- }
455
- }
456
- const body = rawLines.slice(headerIdx + 1).join("\n");
457
- return parseNetlist(body, netlistTitle);
458
- }
459
- const lines = text2.split("\n").map((l) => l.replace(/\r$/, ""));
460
- let title2;
461
- const components = [];
462
- const nets = [];
463
- const netByName = /* @__PURE__ */ new Map();
464
- let autoId = 0;
465
- let pendingAt;
466
- const mkId = (prefix) => `${prefix}_${autoId++}`;
467
- for (const rawLine of lines) {
468
- const stripped = rawLine.replace(/#.*$/, "").trim();
469
- if (!stripped) continue;
470
- if (/^circuit\b/i.test(stripped)) {
471
- const t = matchQuotedTitle(stripped);
472
- if (t !== void 0) title2 = t;
473
- continue;
474
- }
475
- const atMatch = stripped.match(/^at:\s*(.+)$/i);
476
- if (atMatch) {
477
- pendingAt = atMatch[1].trim();
478
- continue;
479
- }
480
- const netDecl = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*$/i);
481
- if (netDecl) {
482
- const name = netDecl[1];
483
- if (!netByName.has(name)) {
484
- const n = { id: name, anchors: [] };
485
- netByName.set(name, n);
486
- nets.push(n);
487
- }
488
- continue;
489
- }
490
- const netDotMatch = stripped.match(/^net\s+([a-zA-Z_][\w]*)\s*:\s*dot\s*$/i);
491
- if (netDotMatch) {
492
- const name = netDotMatch[1];
493
- let n = netByName.get(name);
494
- if (!n) {
495
- n = { id: name, anchors: [] };
496
- netByName.set(name, n);
497
- nets.push(n);
498
- }
499
- const id = mkId("dot");
500
- components.push({
501
- id,
502
- componentType: "dot",
503
- direction: "right",
504
- at: pendingAt,
505
- attrs: { net: name }
506
- });
507
- n.anchors.push(`${id}.end`);
508
- pendingAt = `${id}.end`;
509
- continue;
510
- }
511
- const labelMatch = stripped.match(/^label\s+"([^"]*)"(?:\s+(right|left|up|down))?\s*$/i);
512
- if (labelMatch) {
513
- const id = mkId("lbl");
514
- components.push({
515
- id,
516
- componentType: "label",
517
- direction: labelMatch[2]?.toLowerCase() ?? "right",
518
- at: pendingAt,
519
- label: labelMatch[1]
520
- });
521
- continue;
522
- }
523
- const wireMatch = stripped.match(/^wire(?:\s+(right|left|up|down))?(?:\s+(\d+)(?:px)?)?\s*$/i);
524
- if (wireMatch) {
525
- const id = mkId("w");
526
- components.push({
527
- id,
528
- componentType: "wire",
529
- direction: wireMatch[1]?.toLowerCase() ?? "right",
530
- at: pendingAt,
531
- attrs: wireMatch[2] ? { length: wireMatch[2] } : {}
532
- });
533
- pendingAt = `${id}.end`;
534
- continue;
535
- }
536
- const bareMatch = stripped.match(/^([a-zA-Z_][\w]*)(\s+.*)?$/);
537
- const colonMatch = stripped.match(/^([a-zA-Z_][\w]*)\s*:\s*([a-zA-Z_][\w]*)(\s+.*)?$/);
538
- if (colonMatch) {
539
- const id = colonMatch[1];
540
- const typeStr = colonMatch[2];
541
- const norm = normalizeType(typeStr);
542
- if (!norm) {
543
- throw new CircuitParseError(`Unknown component type: ${typeStr}`);
544
- }
545
- const rest = colonMatch[3] ?? "";
546
- const parsed = parseAttrs(rest);
547
- const comp = {
548
- id,
549
- componentType: norm,
550
- direction: parsed.direction ?? "right",
551
- at: parsed.at ?? pendingAt,
552
- label: parsed.label,
553
- value: parsed.value,
554
- attrs: parsed.attrs
555
- };
556
- if (parsed.length) {
557
- comp.attrs = { ...comp.attrs, length: parsed.length };
558
- }
559
- components.push(comp);
560
- pendingAt = `${id}.end`;
561
- continue;
562
- }
563
- if (bareMatch) {
564
- const typeStr = bareMatch[1];
565
- const norm = normalizeType(typeStr);
566
- if (!norm) {
567
- continue;
568
- }
569
- const rest = bareMatch[2] ?? "";
570
- const parsed = parseAttrs(rest);
571
- const id = mkId(norm);
572
- const comp = {
573
- id,
574
- componentType: norm,
575
- direction: parsed.direction ?? "right",
576
- at: parsed.at ?? pendingAt,
577
- label: parsed.label,
578
- value: parsed.value,
579
- attrs: parsed.attrs
580
- };
581
- components.push(comp);
582
- pendingAt = `${id}.end`;
583
- continue;
584
- }
585
- }
586
- return {
587
- type: "circuit",
588
- title: title2,
589
- components,
590
- nets,
591
- mode: "positional"
592
- };
593
- }
594
-
595
- // src/diagrams/circuit/symbols.ts
596
- var BODY = 'class="schematex-circuit-body"';
597
- var FILL = 'class="schematex-circuit-fill"';
598
- var WIRE = 'class="schematex-circuit-wire"';
599
- function lineWire(x1, y1, x2, y2) {
600
- return `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" ${WIRE}/>`;
601
- }
602
- var resistor = {
603
- length: 40,
604
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
605
- svg: () => `<path d="M 0,0 L 5,0 L 8,-8 L 12,8 L 16,-8 L 20,8 L 24,-8 L 28,8 L 32,-8 L 35,0 L 40,0" ${BODY}/>`
606
- };
607
- var capacitor = {
608
- length: 20,
609
- anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
610
- svg: () => [
611
- lineWire(0, 0, 8, 0),
612
- `<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
613
- `<line x1="12" y1="-10" x2="12" y2="10" ${BODY}/>`,
614
- lineWire(12, 0, 20, 0)
615
- ].join("")
616
- };
617
- var electrolytic_cap = {
618
- length: 20,
619
- anchors: { start: { x: 0, y: 0 }, end: { x: 20, y: 0 } },
620
- svg: () => [
621
- lineWire(0, 0, 8, 0),
622
- `<line x1="8" y1="-10" x2="8" y2="10" ${BODY}/>`,
623
- `<path d="M 12,-10 Q 16,0 12,10" fill="none" class="schematex-circuit-body"/>`,
624
- lineWire(12, 0, 20, 0),
625
- `<text x="5" y="-12" class="schematex-circuit-pol">\u2212</text>`,
626
- `<text x="15" y="-12" class="schematex-circuit-pol">+</text>`
627
- ].join("")
628
- };
629
- var inductor = {
630
- length: 40,
631
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
632
- svg: () => `<path d="M 0,0 L 5,0 A 5,5 0 0 1 15,0 A 5,5 0 0 1 25,0 A 5,5 0 0 1 35,0 L 40,0" fill="none" ${BODY}/>`
633
- };
634
- var fuse = {
635
- length: 30,
636
- anchors: { start: { x: 0, y: 0 }, end: { x: 30, y: 0 } },
637
- svg: () => [
638
- lineWire(0, 0, 5, 0),
639
- `<rect x="5" y="-5" width="20" height="10" rx="5" fill="none" ${BODY}/>`,
640
- `<line x1="5" y1="0" x2="25" y2="0" ${BODY}/>`,
641
- lineWire(25, 0, 30, 0)
642
- ].join("")
44
+ var fuse = {
45
+ length: 30,
46
+ anchors: { start: { x: 0, y: 0 }, end: { x: 30, y: 0 } },
47
+ svg: () => [
48
+ lineWire(0, 0, 5, 0),
49
+ `<rect x="5" y="-5" width="20" height="10" rx="5" fill="none" ${BODY}/>`,
50
+ `<line x1="5" y1="0" x2="25" y2="0" ${BODY}/>`,
51
+ lineWire(25, 0, 30, 0)
52
+ ].join("")
643
53
  };
644
54
  var crystal = {
645
55
  length: 40,
@@ -654,6 +64,7 @@ var crystal = {
654
64
  };
655
65
  var transformer = {
656
66
  length: 60,
67
+ netlistPins: ["p1", "p2", "s1", "s2"],
657
68
  anchors: {
658
69
  start: { x: 0, y: 0 },
659
70
  end: { x: 60, y: 0 },
@@ -676,6 +87,7 @@ var transformer = {
676
87
  };
677
88
  var voltage_source = {
678
89
  length: 40,
90
+ netlistPins: ["plus", "minus"],
679
91
  // plus/minus aliases: end = +, start = − (matches on-symbol label)
680
92
  anchors: {
681
93
  start: { x: 0, y: 0 },
@@ -693,6 +105,7 @@ var voltage_source = {
693
105
  };
694
106
  var current_source = {
695
107
  length: 40,
108
+ netlistPins: ["plus", "minus"],
696
109
  anchors: {
697
110
  start: { x: 0, y: 0 },
698
111
  end: { x: 40, y: 0 },
@@ -709,6 +122,7 @@ var current_source = {
709
122
  };
710
123
  var ac_source = {
711
124
  length: 40,
125
+ netlistPins: ["plus", "minus"],
712
126
  anchors: {
713
127
  start: { x: 0, y: 0 },
714
128
  end: { x: 40, y: 0 },
@@ -724,6 +138,7 @@ var ac_source = {
724
138
  };
725
139
  var battery = {
726
140
  length: 24,
141
+ netlistPins: ["plus", "minus"],
727
142
  anchors: {
728
143
  start: { x: 0, y: 0 },
729
144
  end: { x: 24, y: 0 },
@@ -824,6 +239,7 @@ var photodiode = {
824
239
  };
825
240
  var npn = {
826
241
  length: 40,
242
+ netlistPins: ["c", "b", "e"],
827
243
  // base at left, collector up-right, emitter down-right
828
244
  anchors: {
829
245
  start: { x: 0, y: 0 },
@@ -849,6 +265,7 @@ var npn = {
849
265
  };
850
266
  var pnp = {
851
267
  length: 40,
268
+ netlistPins: ["c", "b", "e"],
852
269
  anchors: {
853
270
  start: { x: 0, y: 0 },
854
271
  end: { x: 40, y: -16 },
@@ -873,6 +290,7 @@ var pnp = {
873
290
  };
874
291
  var nmos = {
875
292
  length: 40,
293
+ netlistPins: ["d", "g", "s"],
876
294
  anchors: {
877
295
  start: { x: 0, y: 0 },
878
296
  end: { x: 40, y: -16 },
@@ -898,6 +316,7 @@ var nmos = {
898
316
  };
899
317
  var pmos = {
900
318
  length: 40,
319
+ netlistPins: ["d", "g", "s"],
901
320
  anchors: {
902
321
  start: { x: 0, y: 0 },
903
322
  end: { x: 40, y: -16 },
@@ -923,12 +342,16 @@ var pmos = {
923
342
  };
924
343
  var jfet_n = {
925
344
  length: 40,
345
+ netlistPins: ["d", "g", "s"],
926
346
  anchors: {
927
347
  start: { x: 0, y: 0 },
928
348
  end: { x: 40, y: -16 },
929
349
  gate: { x: 0, y: 0 },
930
350
  drain: { x: 40, y: -16 },
931
- source: { 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 }
932
355
  },
933
356
  svg: () => [
934
357
  lineWire(0, 0, 14, 0),
@@ -942,12 +365,16 @@ var jfet_n = {
942
365
  };
943
366
  var jfet_p = {
944
367
  length: 40,
368
+ netlistPins: ["d", "g", "s"],
945
369
  anchors: {
946
370
  start: { x: 0, y: 0 },
947
371
  end: { x: 40, y: -16 },
948
372
  gate: { x: 0, y: 0 },
949
373
  drain: { x: 40, y: -16 },
950
- source: { 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 }
951
378
  },
952
379
  svg: () => [
953
380
  lineWire(0, 0, 14, 0),
@@ -961,6 +388,7 @@ var jfet_p = {
961
388
  };
962
389
  var opamp = {
963
390
  length: 50,
391
+ netlistPins: ["plus", "minus", "out"],
964
392
  anchors: {
965
393
  start: { x: 0, y: 0 },
966
394
  end: { x: 50, y: 0 },
@@ -978,6 +406,7 @@ var opamp = {
978
406
  };
979
407
  var comparator = {
980
408
  length: 50,
409
+ netlistPins: ["plus", "minus", "out"],
981
410
  anchors: {
982
411
  start: { x: 0, y: 0 },
983
412
  end: { x: 50, y: 0 },
@@ -1126,6 +555,7 @@ var variable_inductor = {
1126
555
  };
1127
556
  var switch_spdt = {
1128
557
  length: 50,
558
+ netlistPins: ["common", "nc", "no"],
1129
559
  anchors: {
1130
560
  start: { x: 0, y: 0 },
1131
561
  end: { x: 50, y: 0 },
@@ -1200,292 +630,893 @@ var antenna = {
1200
630
  `<line x1="0" y1="-10" x2="10" y2="-24" ${BODY}/>`
1201
631
  ].join("")
1202
632
  };
1203
- function icSymbol(defaultLeft, defaultRight, bodyLabel) {
1204
- const BODY_W = 80;
1205
- return {
1206
- length: BODY_W,
1207
- anchors: {
1208
- start: { x: 0, y: 0 },
1209
- end: { x: BODY_W, y: 0 }
1210
- },
1211
- svg: (_label, _value, attrs) => {
1212
- const left = attrs?.pins_left ? attrs.pins_left.split(",").map((s) => s.trim()) : defaultLeft;
1213
- const right = attrs?.pins_right ? attrs.pins_right.split(",").map((s) => s.trim()) : defaultRight;
1214
- const n = Math.max(left.length, right.length, 2);
1215
- const pitch = 16;
1216
- const bodyH = pitch * (n + 1);
1217
- const topY = -bodyH / 2;
1218
- const parts = [];
1219
- parts.push(`<rect x="0" y="${topY}" width="${BODY_W}" height="${bodyH}" fill="white" ${BODY}/>`);
1220
- const labelText = attrs?.ic_label ?? bodyLabel ?? "";
1221
- if (labelText) {
1222
- parts.push(`<text x="${BODY_W / 2}" y="3" text-anchor="middle" class="schematex-circuit-meter">${labelText}</text>`);
1223
- }
1224
- for (let i = 0; i < left.length; i++) {
1225
- const y = topY + pitch * (i + 1);
1226
- parts.push(`<line x1="-8" y1="${y}" x2="0" y2="${y}" ${WIRE}/>`);
1227
- parts.push(`<text x="4" y="${y + 3}" class="schematex-circuit-pol">${left[i]}</text>`);
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"],
698
+ anchors: {
699
+ start: { x: 0, y: 0 },
700
+ end: { x: 60, y: 0 },
701
+ in: { x: 0, y: 0 },
702
+ out: { x: 60, y: 0 },
703
+ gnd: { x: 30, y: 20 }
704
+ },
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)
711
+ ].join("")
712
+ };
713
+ var potentiometer = {
714
+ length: 50,
715
+ netlistPins: ["start", "wiper", "end"],
716
+ anchors: {
717
+ start: { x: 0, y: 0 },
718
+ end: { x: 50, y: 0 },
719
+ wiper: { x: 25, y: -22 }
720
+ },
721
+ svg: () => [
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}/>`
725
+ ].join("")
726
+ };
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
+ }
758
+ };
759
+ var relay_coil = {
760
+ length: 40,
761
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
762
+ svg: () => [
763
+ lineWire(0, 0, 8, 0),
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}/>`,
767
+ lineWire(32, 0, 40, 0)
768
+ ].join("")
769
+ };
770
+ var relay_no = {
771
+ length: 40,
772
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
773
+ svg: () => [
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)
781
+ ].join("")
782
+ };
783
+ var relay_nc = {
784
+ length: 40,
785
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
786
+ svg: () => [
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)
795
+ ].join("")
796
+ };
797
+ var contactor = {
798
+ length: 44,
799
+ anchors: { start: { x: 0, y: 0 }, end: { x: 44, y: 0 } },
800
+ svg: () => [
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)
810
+ ].join("")
811
+ };
812
+ var solenoid_valve = {
813
+ length: 50,
814
+ anchors: { start: { x: 0, y: 0 }, end: { x: 50, y: 0 } },
815
+ svg: () => [
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)
826
+ ].join("")
827
+ };
828
+ var thermal_overload = {
829
+ length: 40,
830
+ anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
831
+ svg: () => [
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)
837
+ ].join("")
838
+ };
839
+ var disconnect_switch = {
840
+ length: 48,
841
+ anchors: { start: { x: 0, y: 0 }, end: { x: 48, y: 0 } },
842
+ svg: () => [
843
+ lineWire(0, 0, 10, 0),
844
+ `<line x1="10" y1="0" x2="34" y2="-12" ${BODY}/>`,
845
+ `<circle cx="10" cy="0" r="2" ${FILL}/>`,
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)
850
+ ].join("")
851
+ };
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.
917
+ };
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;
940
+ };
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="..."
963
+ };
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"
979
+ };
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
+ }
1182
+ };
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"
1303
+ };
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;
1228
1340
  }
1229
- for (let i = 0; i < right.length; i++) {
1230
- const y = topY + pitch * (i + 1);
1231
- parts.push(`<line x1="${BODY_W}" y1="${y}" x2="${BODY_W + 8}" y2="${y}" ${WIRE}/>`);
1232
- parts.push(`<text x="${BODY_W - 4}" y="${y + 3}" text-anchor="end" class="schematex-circuit-pol">${right[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);
1233
1358
  }
1234
- return parts.join("");
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;
1235
1365
  }
1236
- };
1366
+ out.attrs[tok] = "true";
1367
+ }
1368
+ return out;
1237
1369
  }
1238
- var generic_ic = icSymbol(
1239
- ["1", "2", "3", "4"],
1240
- ["8", "7", "6", "5"],
1241
- "IC"
1242
- );
1243
- var timer_555 = (() => {
1244
- const left = ["GND", "TRG", "OUT", "RST"];
1245
- const right = ["VCC", "DIS", "THR", "CTL"];
1246
- const sym = icSymbol(left, right, "555");
1247
- const BODY_W = 80;
1248
- const pitch = 16;
1249
- const n = 4;
1250
- const topY = -80 / 2;
1251
- const anchors = {
1252
- start: { x: 0, y: 0 },
1253
- end: { x: BODY_W, y: 0 }
1254
- };
1255
- const leftNames = ["gnd", "trg", "out", "rst"];
1256
- const rightNames = ["vcc", "dis", "thr", "ctl"];
1257
- for (let i = 0; i < n; i++) {
1258
- const y = topY + pitch * (i + 1);
1259
- anchors[leftNames[i]] = { x: -8, y };
1260
- anchors[rightNames[i]] = { x: BODY_W + 8, y };
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);
1261
1385
  }
1262
- return { ...sym, anchors };
1263
- })();
1264
- var voltage_regulator = {
1265
- length: 60,
1266
- anchors: {
1267
- start: { x: 0, y: 0 },
1268
- end: { x: 60, y: 0 },
1269
- in: { x: 0, y: 0 },
1270
- out: { x: 60, y: 0 },
1271
- gnd: { x: 30, y: 20 }
1272
- },
1273
- svg: (_l, _v, attrs) => [
1274
- lineWire(0, 0, 10, 0),
1275
- `<rect x="10" y="-15" width="40" height="30" fill="white" ${BODY}/>`,
1276
- `<text x="30" y="4" text-anchor="middle" class="schematex-circuit-meter">${attrs?.model ?? "REG"}</text>`,
1277
- lineWire(50, 0, 60, 0),
1278
- lineWire(30, 15, 30, 20)
1279
- ].join("")
1280
- };
1281
- var potentiometer = {
1282
- length: 50,
1283
- anchors: {
1284
- start: { x: 0, y: 0 },
1285
- end: { x: 50, y: 0 },
1286
- wiper: { x: 25, y: -22 }
1287
- },
1288
- svg: () => [
1289
- `<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}/>`,
1290
- `<line x1="25" y1="-22" x2="25" y2="-10" ${BODY}/>`,
1291
- `<polygon points="25,-10 21,-16 29,-16" ${FILL}/>`
1292
- ].join("")
1293
- };
1294
- var terminal_block = {
1295
- length: 80,
1296
- anchors: { start: { x: 0, y: 0 }, end: { x: 80, y: 0 } },
1297
- svg: (label, _value, attrs) => {
1298
- const pinsAttr = attrs?.pins ?? attrs?.terminals ?? "1,2,3,4";
1299
- const pins = pinsAttr.split(",").map((s) => s.trim()).filter(Boolean);
1300
- const n = Math.max(pins.length, 1);
1301
- const BODY_W = 80;
1302
- const pitch = 18;
1303
- const bodyH = pitch * (n + 1);
1304
- const topY = -bodyH / 2;
1305
- const parts = [];
1306
- parts.push(
1307
- `<rect x="0" y="${topY}" width="${BODY_W}" height="${bodyH}" rx="3" fill="white" stroke-width="2" ${BODY}/>`
1308
- );
1309
- if (label) {
1310
- parts.push(
1311
- `<text x="${BODY_W / 2}" y="${topY + 14}" text-anchor="middle" class="schematex-circuit-meter">${label}</text>`
1312
- );
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);
1414
+ }
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);
1425
+ }
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;
1313
1437
  }
1314
- for (let i = 0; i < n; i++) {
1315
- const y = topY + pitch * (i + 1) + (label ? 8 : 0);
1316
- parts.push(`<line x1="-8" y1="${y}" x2="0" y2="${y}" ${WIRE}/>`);
1317
- parts.push(`<circle cx="6" cy="${y}" r="2" ${FILL}/>`);
1318
- parts.push(
1319
- `<text x="12" y="${y + 3}" class="schematex-circuit-pol">${pins[i] ?? `T${i + 1}`}</text>`
1320
- );
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;
1449
+ }
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;
1321
1511
  }
1322
- return parts.join("");
1323
1512
  }
1324
- };
1325
- var relay_coil = {
1326
- length: 40,
1327
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
1328
- svg: () => [
1329
- lineWire(0, 0, 8, 0),
1330
- `<rect x="8" y="-10" width="24" height="20" fill="white" ${BODY}/>`,
1331
- // Diagonal slash inside the box = coil winding indicator
1332
- `<line x1="8" y1="-10" x2="32" y2="10" ${BODY}/>`,
1333
- lineWire(32, 0, 40, 0)
1334
- ].join("")
1335
- };
1336
- var relay_no = {
1337
- length: 40,
1338
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
1339
- svg: () => [
1340
- lineWire(0, 0, 10, 0),
1341
- `<line x1="10" y1="0" x2="30" y2="-10" ${BODY}/>`,
1342
- `<circle cx="10" cy="0" r="2" ${FILL}/>`,
1343
- `<circle cx="30" cy="0" r="2" ${FILL}/>`,
1344
- // Small dashed bar above to denote actuator-driven (vs hand switch)
1345
- `<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
1346
- lineWire(30, 0, 40, 0)
1347
- ].join("")
1348
- };
1349
- var relay_nc = {
1350
- length: 40,
1351
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
1352
- svg: () => [
1353
- lineWire(0, 0, 10, 0),
1354
- `<line x1="10" y1="-10" x2="30" y2="-10" ${BODY}/>`,
1355
- // NC slash: line crosses both contact endpoints (closed by default)
1356
- `<line x1="8" y1="2" x2="32" y2="-12" ${BODY}/>`,
1357
- `<circle cx="10" cy="0" r="2" ${FILL}/>`,
1358
- `<circle cx="30" cy="0" r="2" ${FILL}/>`,
1359
- `<line x1="14" y1="-14" x2="26" y2="-14" stroke-dasharray="2,2" ${BODY}/>`,
1360
- lineWire(30, 0, 40, 0)
1361
- ].join("")
1362
- };
1363
- var contactor = {
1364
- length: 44,
1365
- anchors: { start: { x: 0, y: 0 }, end: { x: 44, y: 0 } },
1366
- svg: () => [
1367
- lineWire(0, 0, 10, 0),
1368
- // Two parallel diagonal contacts = double-break (typical of contactors)
1369
- `<line x1="10" y1="0" x2="30" y2="-10" stroke-width="2.5" ${BODY}/>`,
1370
- `<line x1="14" y1="2" x2="34" y2="-8" stroke-width="2.5" ${BODY}/>`,
1371
- `<circle cx="10" cy="0" r="2" ${FILL}/>`,
1372
- `<circle cx="34" cy="0" r="2" ${FILL}/>`,
1373
- // Solid bar above = electromagnetic-driven actuator
1374
- `<line x1="14" y1="-14" x2="30" y2="-14" stroke-width="2" ${BODY}/>`,
1375
- lineWire(34, 0, 44, 0)
1376
- ].join("")
1377
- };
1378
- var solenoid_valve = {
1379
- length: 50,
1380
- anchors: { start: { x: 0, y: 0 }, end: { x: 50, y: 0 } },
1381
- svg: () => [
1382
- lineWire(0, 0, 8, 0),
1383
- // Solenoid box on top
1384
- `<rect x="8" y="-22" width="14" height="12" fill="white" ${BODY}/>`,
1385
- `<line x1="8" y1="-22" x2="22" y2="-10" ${BODY}/>`,
1386
- // Valve body (triangle pair = IEC valve symbol)
1387
- `<polygon points="22,0 38,-8 38,8" fill="white" ${BODY}/>`,
1388
- `<polygon points="22,0 8,-8 8,8" fill="white" ${BODY}/>`,
1389
- // Connect solenoid to valve body (actuator line)
1390
- `<line x1="15" y1="-10" x2="22" y2="0" ${BODY}/>`,
1391
- lineWire(38, 0, 50, 0)
1392
- ].join("")
1393
- };
1394
- var thermal_overload = {
1395
- length: 40,
1396
- anchors: { start: { x: 0, y: 0 }, end: { x: 40, y: 0 } },
1397
- svg: () => [
1398
- lineWire(0, 0, 8, 0),
1399
- `<rect x="8" y="-12" width="24" height="24" fill="white" ${BODY}/>`,
1400
- // Bimetal element: bent line inside box
1401
- `<path d="M 12,-6 L 16,4 L 20,-6 L 24,4 L 28,-6" fill="none" ${BODY}/>`,
1402
- lineWire(32, 0, 40, 0)
1403
- ].join("")
1404
- };
1405
- var disconnect_switch = {
1406
- length: 48,
1407
- anchors: { start: { x: 0, y: 0 }, end: { x: 48, y: 0 } },
1408
- svg: () => [
1409
- lineWire(0, 0, 10, 0),
1410
- `<line x1="10" y1="0" x2="34" y2="-12" ${BODY}/>`,
1411
- `<circle cx="10" cy="0" r="2" ${FILL}/>`,
1412
- // Hollow square at top of arm = visible-isolation indicator
1413
- `<rect x="30" y="-16" width="8" height="8" fill="white" ${BODY}/>`,
1414
- `<circle cx="38" cy="0" r="2" ${FILL}/>`,
1415
- lineWire(38, 0, 48, 0)
1416
- ].join("")
1417
- };
1418
- var SYMBOLS = {
1419
- resistor,
1420
- capacitor,
1421
- electrolytic_cap,
1422
- inductor,
1423
- fuse,
1424
- crystal,
1425
- transformer,
1426
- voltage_source,
1427
- current_source,
1428
- ac_source,
1429
- battery,
1430
- ground,
1431
- gnd_signal,
1432
- vcc,
1433
- diode,
1434
- zener,
1435
- schottky,
1436
- led,
1437
- photodiode,
1438
- npn,
1439
- pnp,
1440
- nmos,
1441
- pmos,
1442
- jfet_n,
1443
- jfet_p,
1444
- opamp,
1445
- comparator,
1446
- switch_spst,
1447
- push_no,
1448
- ammeter,
1449
- voltmeter,
1450
- wattmeter,
1451
- motor,
1452
- speaker,
1453
- microphone,
1454
- buzzer,
1455
- potentiometer,
1456
- rheostat,
1457
- thermistor_ntc,
1458
- thermistor_ptc,
1459
- ldr,
1460
- variable_cap,
1461
- variable_inductor,
1462
- switch_spdt,
1463
- push_nc,
1464
- gnd_chassis,
1465
- gnd_digital,
1466
- test_point,
1467
- no_connect,
1468
- antenna,
1469
- generic_ic,
1470
- terminal_block,
1471
- "555_timer": timer_555,
1472
- voltage_regulator,
1473
- // Industrial control / power electrical (IEC 60617)
1474
- relay_coil,
1475
- relay_no,
1476
- relay_nc,
1477
- contactor,
1478
- solenoid_valve,
1479
- thermal_overload,
1480
- disconnect_switch
1481
- // Lamp reuses buzzer slot? No, needs its own entry but our CircuitComponentType
1482
- // doesn't have "lamp". We map it via parser alias to "buzzer" or add specifically.
1483
- };
1484
- var EXTRA_SYMBOLS = {
1485
- lamp
1486
- };
1487
- function getSymbol(t) {
1488
- return SYMBOLS[t] ?? EXTRA_SYMBOLS[t];
1513
+ return {
1514
+ type: "circuit",
1515
+ title: title2,
1516
+ components,
1517
+ nets,
1518
+ mode: "positional"
1519
+ };
1489
1520
  }
1490
1521
 
1491
1522
  // src/diagrams/circuit/layout.ts
@@ -2009,6 +2040,6 @@ var circuit = {
2009
2040
  }
2010
2041
  };
2011
2042
 
2012
- export { circuit, layoutCircuit, layoutCircuitNetlist, parseCircuit, parseNetlist, renderCircuit };
2013
- //# sourceMappingURL=chunk-HUPDIRBX.js.map
2014
- //# sourceMappingURL=chunk-HUPDIRBX.js.map
2043
+ export { EXTRA_SYMBOLS, SYMBOLS, circuit, layoutCircuit, layoutCircuitNetlist, parseCircuit, parseNetlist, renderCircuit };
2044
+ //# sourceMappingURL=chunk-UWA5MWCI.js.map
2045
+ //# sourceMappingURL=chunk-UWA5MWCI.js.map