copperhead 0.8.1 → 0.10.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 (105) hide show
  1. package/NOTICE +1 -1
  2. package/README.md +13 -5
  3. package/dist/agent/filetools.js +24 -1
  4. package/dist/agent/filetools.js.map +1 -1
  5. package/dist/agent/ledger.js +24 -0
  6. package/dist/agent/ledger.js.map +1 -1
  7. package/dist/agent/loop.js +67 -62
  8. package/dist/agent/loop.js.map +1 -1
  9. package/dist/agent/prompts.js +4 -3
  10. package/dist/agent/prompts.js.map +1 -1
  11. package/dist/agent/providers/openai.js +28 -6
  12. package/dist/agent/providers/openai.js.map +1 -1
  13. package/dist/agent/providers/tool-protocol.js +21 -0
  14. package/dist/agent/providers/tool-protocol.js.map +1 -1
  15. package/dist/agent/recovery.js +95 -1
  16. package/dist/agent/recovery.js.map +1 -1
  17. package/dist/agent/response-cache.js +18 -2
  18. package/dist/agent/response-cache.js.map +1 -1
  19. package/dist/agent/tools.js +185 -1
  20. package/dist/agent/tools.js.map +1 -1
  21. package/dist/agent/transcript.js +2 -0
  22. package/dist/agent/transcript.js.map +1 -1
  23. package/dist/cli.js +77 -2
  24. package/dist/cli.js.map +1 -1
  25. package/dist/commands/check.js +33 -1
  26. package/dist/commands/check.js.map +1 -1
  27. package/dist/commands/create.js +282 -26
  28. package/dist/commands/create.js.map +1 -1
  29. package/dist/commands/doctor.js +211 -11
  30. package/dist/commands/doctor.js.map +1 -1
  31. package/dist/config.js +61 -4
  32. package/dist/config.js.map +1 -1
  33. package/dist/kicad/bootstrap.js +24 -3
  34. package/dist/kicad/bootstrap.js.map +1 -1
  35. package/dist/kicad/cli.js +7 -26
  36. package/dist/kicad/cli.js.map +1 -1
  37. package/dist/kicad/dossier.js +207 -0
  38. package/dist/kicad/dossier.js.map +1 -0
  39. package/dist/kicad/draft/draft.js +132 -0
  40. package/dist/kicad/draft/draft.js.map +1 -0
  41. package/dist/kicad/draft/engine.js +2389 -0
  42. package/dist/kicad/draft/engine.js.map +1 -0
  43. package/dist/kicad/draft/ir.js +368 -0
  44. package/dist/kicad/draft/ir.js.map +1 -0
  45. package/dist/kicad/draft/symsource.js +490 -0
  46. package/dist/kicad/draft/symsource.js.map +1 -0
  47. package/dist/kicad/emit.js +181 -0
  48. package/dist/kicad/emit.js.map +1 -0
  49. package/dist/kicad/fab.js +13 -0
  50. package/dist/kicad/fab.js.map +1 -1
  51. package/dist/kicad/legibility.js +561 -0
  52. package/dist/kicad/legibility.js.map +1 -0
  53. package/dist/kicad/score.js +261 -0
  54. package/dist/kicad/score.js.map +1 -0
  55. package/dist/kicad/sexp.js +262 -10
  56. package/dist/kicad/sexp.js.map +1 -1
  57. package/dist/kicad/symlib.js +346 -16
  58. package/dist/kicad/symlib.js.map +1 -1
  59. package/dist/memory/bom-table.js +108 -34
  60. package/dist/memory/bom-table.js.map +1 -1
  61. package/dist/memory/scaffold.js +6 -0
  62. package/dist/memory/scaffold.js.map +1 -1
  63. package/dist/openspec/cli.js +2 -1
  64. package/dist/openspec/cli.js.map +1 -1
  65. package/dist/util/preflight.js +17 -0
  66. package/dist/util/preflight.js.map +1 -1
  67. package/dist/util/redact.js +12 -2
  68. package/dist/util/redact.js.map +1 -1
  69. package/package.json +9 -7
  70. package/src/agent/filetools.ts +26 -1
  71. package/src/agent/ledger.ts +24 -0
  72. package/src/agent/loop.ts +88 -65
  73. package/src/agent/prompts.ts +4 -3
  74. package/src/agent/providers/openai.ts +38 -4
  75. package/src/agent/providers/tool-protocol.ts +22 -0
  76. package/src/agent/recovery.ts +94 -1
  77. package/src/agent/response-cache.ts +17 -1
  78. package/src/agent/tools.ts +189 -1
  79. package/src/agent/transcript.ts +6 -0
  80. package/src/cli.ts +73 -2
  81. package/src/commands/check.ts +51 -1
  82. package/src/commands/create.ts +278 -22
  83. package/src/commands/doctor.ts +219 -12
  84. package/src/config.ts +107 -2
  85. package/src/kicad/bootstrap.ts +24 -3
  86. package/src/kicad/cli.ts +6 -19
  87. package/src/kicad/dossier.ts +217 -0
  88. package/src/kicad/draft/draft.ts +171 -0
  89. package/src/kicad/draft/engine.ts +2466 -0
  90. package/src/kicad/draft/ir.ts +416 -0
  91. package/src/kicad/draft/symsource.ts +535 -0
  92. package/src/kicad/emit.ts +236 -0
  93. package/src/kicad/fab.ts +15 -0
  94. package/src/kicad/legibility.ts +646 -0
  95. package/src/kicad/score.ts +323 -0
  96. package/src/kicad/sexp.ts +339 -10
  97. package/src/kicad/symlib.ts +364 -18
  98. package/src/memory/bom-table.ts +119 -31
  99. package/src/memory/scaffold.ts +6 -0
  100. package/src/openspec/cli.ts +3 -2
  101. package/src/util/preflight.ts +18 -0
  102. package/src/util/redact.ts +12 -2
  103. package/dist/memory/synap.js +0 -152
  104. package/dist/memory/synap.js.map +0 -1
  105. package/src/memory/synap.ts +0 -217
@@ -0,0 +1,261 @@
1
+ import { readSheetGeometry } from './sexp.js';
2
+ import { checkLegibility } from './legibility.js';
3
+ const DEFAULT_WEIGHTS = {
4
+ 'wire-crossings': 10,
5
+ 'wire-bends': 5,
6
+ 'wire-length': 5,
7
+ 'label-to-wire-ratio': 5,
8
+ 'group-cohesion': 10,
9
+ 'flow-direction': 10,
10
+ utilization: 10,
11
+ 'axis-alignment': 10,
12
+ 'spacing-uniformity': 10,
13
+ 'straight-wire-ratio': 5,
14
+ 'label-alignment': 5,
15
+ 'whitespace-balance': 10,
16
+ 'pair-symmetry': 5,
17
+ };
18
+ /** Composite is capped this far below the Tier A floor when errors exist. */
19
+ const DEFAULT_FLOOR = 85;
20
+ const ERROR_CAP = 40;
21
+ /** Top-edge spread (mm) within which two group boxes count as the same row. */
22
+ const ROW_BAND = 12;
23
+ const PAPER = {
24
+ A5: { w: 210, h: 148 },
25
+ A4: { w: 297, h: 210 },
26
+ A3: { w: 420, h: 297 },
27
+ A2: { w: 594, h: 420 },
28
+ A1: { w: 841, h: 594 },
29
+ A0: { w: 1189, h: 841 },
30
+ };
31
+ const segLen = (s) => Math.hypot(s.x2 - s.x1, s.y2 - s.y1);
32
+ const isVert = (s) => s.x1 === s.x2;
33
+ function properCrossing(a, b) {
34
+ // orthogonal proper crossing (interiors intersect); shared endpoints are joins
35
+ const [v, h] = isVert(a) && !isVert(b) ? [a, b] : !isVert(a) && isVert(b) ? [b, a] : [null, null];
36
+ if (!v || !h)
37
+ return false;
38
+ const [hy, vx] = [h.y1, v.x1];
39
+ const [hx1, hx2] = [Math.min(h.x1, h.x2), Math.max(h.x1, h.x2)];
40
+ const [vy1, vy2] = [Math.min(v.y1, v.y2), Math.max(v.y1, v.y2)];
41
+ return vx > hx1 + 0.01 && vx < hx2 - 0.01 && hy > vy1 + 0.01 && hy < vy2 - 0.01;
42
+ }
43
+ export async function scoreSchematic(rootSch, opts = {}) {
44
+ const sheets = await readSheetGeometry(rootSch);
45
+ const legibility = await checkLegibility(rootSch, {
46
+ docsDir: opts.docsDir ?? null,
47
+ ...(opts.config ? { config: opts.config } : {}),
48
+ });
49
+ return scoreFromGeometry(sheets, legibility, opts.config);
50
+ }
51
+ export function scoreFromGeometry(sheets, legibility, config) {
52
+ const weights = { ...DEFAULT_WEIGHTS };
53
+ for (const [k, v] of Object.entries(config?.score?.weights ?? {})) {
54
+ if (k in weights && typeof v === 'number' && Number.isFinite(v) && v >= 0)
55
+ weights[k] = v;
56
+ }
57
+ // Sanitized like the weights above: a floor of 0 (or a non-number) would
58
+ // push the error cap `min(ERROR_CAP, floor - 1)` negative or NaN.
59
+ const rawFloor = config?.score?.floor;
60
+ const floor = typeof rawFloor === 'number' && Number.isFinite(rawFloor) && rawFloor >= 1 ? rawFloor : DEFAULT_FLOOR;
61
+ // ---- collect geometry across sheets ----
62
+ const segs = sheets.flatMap((s) => s.wires);
63
+ const labels = sheets.flatMap((s) => s.labels);
64
+ const realSyms = sheets.flatMap((s) => s.symbols.filter((y) => !y.isPower).map((y) => ({ ...y, sheet: s.sheetName })));
65
+ // wire crossings
66
+ let crossings = 0;
67
+ for (let i = 0; i < segs.length; i++) {
68
+ for (let j = i + 1; j < segs.length; j++)
69
+ if (properCrossing(segs[i], segs[j]))
70
+ crossings++;
71
+ }
72
+ // bends: points where exactly two segments of different orientation meet
73
+ const ends = new Map();
74
+ for (const s of segs) {
75
+ for (const [x, y] of [[s.x1, s.y1], [s.x2, s.y2]]) {
76
+ const k = `${x},${y}`;
77
+ ends.set(k, [...(ends.get(k) ?? []), s]);
78
+ }
79
+ }
80
+ let bends = 0;
81
+ for (const list of ends.values()) {
82
+ if (list.length === 2 && isVert(list[0]) !== isVert(list[1]))
83
+ bends++;
84
+ }
85
+ const totalLen = segs.reduce((s, w) => s + segLen(w), 0);
86
+ // group cohesion: same-name label pairs, mean anchor distance vs sheet diagonal
87
+ const byName = new Map();
88
+ for (const l of labels)
89
+ byName.set(l.name, [...(byName.get(l.name) ?? []), { x: l.x, y: l.y }]);
90
+ const paper = PAPER[sheets[0]?.paper.name ?? 'A4'] ?? PAPER.A4;
91
+ const diag = Math.hypot(paper.w, paper.h);
92
+ const pairDists = [];
93
+ for (const pts of byName.values()) {
94
+ for (let i = 0; i < pts.length; i++) {
95
+ for (let j = i + 1; j < pts.length; j++)
96
+ pairDists.push(Math.hypot(pts[i].x - pts[j].x, pts[i].y - pts[j].y));
97
+ }
98
+ }
99
+ const meanPair = pairDists.length ? pairDists.reduce((a, b) => a + b, 0) / pairDists.length : 0;
100
+ // flow-direction: cross-group label nets should connect adjacent groups in
101
+ // reading order; a hop across intervening groups reads as against-the-flow
102
+ // wiring
103
+ const rects = sheets.flatMap((s) => s.rectangles.map((r) => ({
104
+ minX: Math.min(r.x1, r.x2),
105
+ maxX: Math.max(r.x1, r.x2),
106
+ minY: Math.min(r.y1, r.y2),
107
+ maxY: Math.max(r.y1, r.y2),
108
+ })));
109
+ // Reading order is rows first, then left-to-right within a row. Ordering by
110
+ // minX alone was correct only while every group sat in one band; on a sheet
111
+ // whose groups wrap onto several rows it interleaves row 2 back into row 1
112
+ // and reports flow violations for a drawing that reads perfectly.
113
+ const byRow = [...rects].sort((a, b) => a.minY - b.minY || a.minX - b.minX);
114
+ const rows = [];
115
+ for (const r of byRow) {
116
+ const row = rows[rows.length - 1];
117
+ // Rows are separated by a group box height; members of one row share a top
118
+ // edge to within a symbol cell, so a generous band still cannot merge two.
119
+ if (row && r.minY - row[0].minY <= ROW_BAND)
120
+ row.push(r);
121
+ else
122
+ rows.push([r]);
123
+ }
124
+ const ordered = rows.flatMap((row) => [...row].sort((a, b) => a.minX - b.minX));
125
+ const groupIdx = (x, y) => ordered.findIndex((r) => x > r.minX && x < r.maxX && y > r.minY && y < r.maxY);
126
+ /** Which wrapped row a point falls in; -1 when it is in no group box. */
127
+ const rowIdx = (x, y) => rows.findIndex((row) => row.some((r) => x > r.minX && x < r.maxX && y > r.minY && y < r.maxY));
128
+ let crossNets = 0;
129
+ let flowViolations = 0;
130
+ for (const pts of byName.values()) {
131
+ const gs = [...new Set(pts.map((p) => groupIdx(p.x, p.y)).filter((g) => g >= 0))];
132
+ if (gs.length > 1) {
133
+ crossNets++;
134
+ if (Math.max(...gs) - Math.min(...gs) > 1)
135
+ flowViolations++;
136
+ }
137
+ }
138
+ // utilization + whitespace balance over the first sheet's usable frame
139
+ const items = [
140
+ ...realSyms.map((s) => ({ minX: s.at.x - 2, minY: s.at.y - 2, maxX: s.at.x + 2, maxY: s.at.y + 2 })),
141
+ ...rects,
142
+ ];
143
+ const content = items.length
144
+ ? items.reduce((a, b) => ({
145
+ minX: Math.min(a.minX, b.minX),
146
+ minY: Math.min(a.minY, b.minY),
147
+ maxX: Math.max(a.maxX, b.maxX),
148
+ maxY: Math.max(a.maxY, b.maxY),
149
+ }))
150
+ : null;
151
+ const usable = { minX: 10, minY: 10, maxX: paper.w - 10, maxY: paper.h - 10 - 30 };
152
+ const usableArea = (usable.maxX - usable.minX) * (usable.maxY - usable.minY);
153
+ const utilization = content
154
+ ? Math.min(1, ((content.maxX - content.minX) * (content.maxY - content.minY)) / usableArea)
155
+ : 0;
156
+ const centroidOff = content
157
+ ? Math.hypot((content.minX + content.maxX) / 2 - (usable.minX + usable.maxX) / 2, (content.minY + content.maxY) / 2 - (usable.minY + usable.maxY) / 2) / (diag / 2)
158
+ : 1;
159
+ // axis alignment: symbols sharing an origin axis with at least one other
160
+ const sharesAxis = (i) => realSyms.some((o, j) => j !== i && (Math.abs(o.at.x - realSyms[i].at.x) < 0.01 || Math.abs(o.at.y - realSyms[i].at.y) < 0.01));
161
+ const axisAligned = realSyms.length > 1 ? realSyms.filter((_, i) => sharesAxis(i)).length / realSyms.length : 1;
162
+ // spacing uniformity: variance of consecutive gaps within shared-x columns
163
+ const cols = new Map();
164
+ for (const s of realSyms) {
165
+ // Keyed by row as well as x: a column is a vertical run the eye reads as
166
+ // one stack. Once groups wrap, two symbols in different rows can share an x
167
+ // while sitting a whole row apart, and treating that jump as a "gap" pushes
168
+ // the coefficient of variation — and the score — off a cliff.
169
+ const k = `${s.sheet}:${Math.round(s.at.x * 100)}:${rowIdx(s.at.x, s.at.y)}`;
170
+ cols.set(k, [...(cols.get(k) ?? []), s.at.y]);
171
+ }
172
+ const gapCVs = [];
173
+ for (const ys of cols.values()) {
174
+ if (ys.length < 3)
175
+ continue;
176
+ ys.sort((a, b) => a - b);
177
+ const gaps = ys.slice(1).map((y, i) => y - ys[i]);
178
+ const mean = gaps.reduce((a, b) => a + b, 0) / gaps.length;
179
+ const sd = Math.sqrt(gaps.reduce((a, g) => a + (g - mean) ** 2, 0) / gaps.length);
180
+ gapCVs.push(mean > 0 ? sd / mean : 0);
181
+ }
182
+ const spacingUniformity = gapCVs.length ? Math.max(0, 1 - gapCVs.reduce((a, b) => a + b, 0) / gapCVs.length) : 1;
183
+ // pair symmetry: same-libId+value symbols in one group aligned on a shared axis
184
+ const pairKeys = new Map();
185
+ for (const s of realSyms) {
186
+ const g = groupIdx(s.at.x, s.at.y);
187
+ const k = `${g}:${s.libId}:${s.value}`;
188
+ pairKeys.set(k, [...(pairKeys.get(k) ?? []), { x: s.at.x, y: s.at.y }]);
189
+ }
190
+ let pairs = 0;
191
+ let symmetric = 0;
192
+ for (const pts of pairKeys.values()) {
193
+ if (pts.length < 2)
194
+ continue;
195
+ for (let i = 0; i < pts.length; i++) {
196
+ for (let j = i + 1; j < pts.length; j++) {
197
+ pairs++;
198
+ if (Math.abs(pts[i].x - pts[j].x) < 0.01 || Math.abs(pts[i].y - pts[j].y) < 0.01)
199
+ symmetric++;
200
+ }
201
+ }
202
+ }
203
+ const pairSymmetry = pairs ? symmetric / pairs : 1;
204
+ const horizLabels = labels.length
205
+ ? labels.filter((l) => Math.abs(l.rot % 180) !== 90).length / labels.length
206
+ : 1;
207
+ const straightRatio = segs.length ? Math.max(0, 1 - bends / segs.length) : 1;
208
+ const labelRatio = segs.length ? labels.length / segs.length : labels.length ? 1 : 0;
209
+ // ---- normalize to [0,1] goodness and compose ----
210
+ const raw = {
211
+ 'wire-crossings': { raw: crossings, score: 1 / (1 + crossings) },
212
+ 'wire-bends': { raw: bends, score: 1 / (1 + bends / 8) },
213
+ 'wire-length': { raw: totalLen, score: 1 / (1 + totalLen / (4 * diag)) },
214
+ 'label-to-wire-ratio': { raw: labelRatio, score: labelRatio <= 1.5 ? 1 : 1.5 / labelRatio },
215
+ 'group-cohesion': { raw: meanPair, score: pairDists.length ? Math.max(0, 1 - meanPair / diag) : 1 },
216
+ 'flow-direction': { raw: flowViolations, score: crossNets ? 1 - flowViolations / crossNets : 1 },
217
+ utilization: { raw: utilization, score: Math.min(1, utilization / 0.5) },
218
+ 'axis-alignment': { raw: axisAligned, score: axisAligned },
219
+ 'spacing-uniformity': { raw: spacingUniformity, score: spacingUniformity },
220
+ 'straight-wire-ratio': { raw: straightRatio, score: straightRatio },
221
+ 'label-alignment': { raw: horizLabels, score: horizLabels },
222
+ 'whitespace-balance': { raw: centroidOff, score: Math.max(0, 1 - centroidOff) },
223
+ 'pair-symmetry': { raw: pairSymmetry, score: pairSymmetry },
224
+ };
225
+ const totalWeight = Object.values(weights).reduce((a, b) => a + b, 0) || 1;
226
+ const metrics = Object.entries(raw).map(([name, m]) => ({
227
+ name,
228
+ raw: m.raw,
229
+ score: m.score,
230
+ weight: weights[name],
231
+ contribution: (100 * weights[name] * m.score) / totalWeight,
232
+ }));
233
+ let composite = metrics.reduce((a, m) => a + m.contribution, 0);
234
+ let cap = null;
235
+ if (legibility.counts.error > 0) {
236
+ const capValue = Math.min(ERROR_CAP, floor - 1);
237
+ if (composite > capValue) {
238
+ cap = {
239
+ appliedAt: capValue,
240
+ reason: `${legibility.counts.error} error-severity legibility finding(s) cap the composite below the known-good floor (${floor})`,
241
+ };
242
+ composite = capValue;
243
+ }
244
+ }
245
+ return {
246
+ composite: Math.round(composite * 100) / 100,
247
+ metrics,
248
+ cap,
249
+ legibility: legibility.counts,
250
+ };
251
+ }
252
+ export function formatScore(report) {
253
+ const lines = [`score: ${report.composite}/100`];
254
+ if (report.cap)
255
+ lines.push(` cap: ${report.cap.appliedAt} — ${report.cap.reason}`);
256
+ for (const m of report.metrics) {
257
+ lines.push(` ${m.name}: raw ${Math.round(m.raw * 10000) / 10000}, score ${Math.round(m.score * 1000) / 1000}, weight ${m.weight}, contributes ${Math.round(m.contribution * 100) / 100}`);
258
+ }
259
+ return lines.join('\n');
260
+ }
261
+ //# sourceMappingURL=score.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/kicad/score.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAmC,MAAM,WAAW,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAyB,MAAM,iBAAiB,CAAC;AA+BzE,MAAM,eAAe,GAA2B;IAC9C,gBAAgB,EAAE,EAAE;IACpB,YAAY,EAAE,CAAC;IACf,aAAa,EAAE,CAAC;IAChB,qBAAqB,EAAE,CAAC;IACxB,gBAAgB,EAAE,EAAE;IACpB,gBAAgB,EAAE,EAAE;IACpB,WAAW,EAAE,EAAE;IACf,gBAAgB,EAAE,EAAE;IACpB,oBAAoB,EAAE,EAAE;IACxB,qBAAqB,EAAE,CAAC;IACxB,iBAAiB,EAAE,CAAC;IACpB,oBAAoB,EAAE,EAAE;IACxB,eAAe,EAAE,CAAC;CACnB,CAAC;AAEF,6EAA6E;AAC7E,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,+EAA+E;AAC/E,MAAM,QAAQ,GAAG,EAAE,CAAC;AAEpB,MAAM,KAAK,GAA6C;IACtD,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE;IACtB,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE;IACtB,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE;IACtB,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE;IACtB,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE;IACtB,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE;CACxB,CAAC;AASF,MAAM,MAAM,GAAG,CAAC,CAAM,EAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AACxE,MAAM,MAAM,GAAG,CAAC,CAAM,EAAW,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;AAElD,SAAS,cAAc,CAAC,CAAM,EAAE,CAAM;IACpC,+EAA+E;IAC/E,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAClG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3B,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9B,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAChE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAChE,OAAO,EAAE,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC;AAClF,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAe,EAAE,OAAqB,EAAE;IAC3E,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE;QAChD,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI;QAC7B,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChD,CAAC,CAAC;IACH,OAAO,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAAuB,EACvB,UAA4B,EAC5B,MAA6B;IAE7B,MAAM,OAAO,GAAG,EAAE,GAAG,eAAe,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;QAClE,IAAI,CAAC,IAAI,OAAO,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5F,CAAC;IACD,yEAAyE;IACzE,kEAAkE;IAClE,MAAM,QAAQ,GAAG,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC;IACtC,MAAM,KAAK,GAAG,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC;IAEpH,2CAA2C;IAC3C,MAAM,IAAI,GAAU,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CACpC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAC/E,CAAC;IAEF,iBAAiB;IACjB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE;YAAE,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAC,CAAE,CAAC;gBAAE,SAAS,EAAE,CAAC;IAChG,CAAC;IAED,yEAAyE;IACzE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAiB,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAU,EAAE,CAAC;YAC3D,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC;YAAE,KAAK,EAAE,CAAC;IAC1E,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEzD,gFAAgF;IAChF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsC,CAAC;IAC7D,KAAK,MAAM,CAAC,IAAI,MAAM;QAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAChG,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;IAChE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;gBAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACpH,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhG,2EAA2E;IAC3E,2EAA2E;IAC3E,SAAS;IACT,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3D,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;QAC1B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;QAC1B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;QAC1B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;KAC3B,CAAC,CAAC,CAAC,CAAC;IACL,4EAA4E;IAC5E,4EAA4E;IAC5E,2EAA2E;IAC3E,kEAAkE;IAClE,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAqB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAClC,2EAA2E;QAC3E,2EAA2E;QAC3E,IAAI,GAAG,IAAI,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC,IAAI,IAAI,QAAQ;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;YACrD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChF,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAChD,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IACjF,yEAAyE;IACzE,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAC9C,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACjG,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAClF,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClB,SAAS,EAAE,CAAC;YACZ,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC;gBAAE,cAAc,EAAE,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,MAAM,KAAK,GAAa;QACtB,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpG,GAAG,KAAK;KACT,CAAC;IACF,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM;QAC1B,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACtB,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;YAC9B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;YAC9B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;YAC9B,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;SAC/B,CAAC,CAAC;QACL,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC;IACnF,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7E,MAAM,WAAW,GAAG,OAAO;QACzB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,UAAU,CAAC;QAC3F,CAAC,CAAC,CAAC,CAAC;IACN,MAAM,WAAW,GAAG,OAAO;QACzB,CAAC,CAAC,IAAI,CAAC,KAAK,CACR,CAAC,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EACnE,CAAC,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CACpE,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC,CAAC;IAEN,yEAAyE;IACzE,MAAM,UAAU,GAAG,CAAC,CAAS,EAAW,EAAE,CACxC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IACnI,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhH,2EAA2E;IAC3E,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,yEAAyE;QACzE,4EAA4E;QAC5E,4EAA4E;QAC5E,8DAA8D;QAC9D,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7E,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QAC/B,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAC5B,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzB,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAE,CAAC,CAAC;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3D,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QAClF,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjH,gFAAgF;IAChF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAsC,CAAC;IAC/D,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACvC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACxC,KAAK,EAAE,CAAC;gBACR,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,GAAG,IAAI;oBAAE,SAAS,EAAE,CAAC;YACpG,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAEnD,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM;QAC/B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM;QAC3E,CAAC,CAAC,CAAC,CAAC;IACN,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAErF,oDAAoD;IACpD,MAAM,GAAG,GAAmD;QAC1D,gBAAgB,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,EAAE;QAChE,YAAY,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,EAAE;QACxD,aAAa,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE;QACxE,qBAAqB,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,UAAU,EAAE;QAC3F,gBAAgB,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;QACnG,gBAAgB,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE;QAChG,WAAW,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,GAAG,CAAC,EAAE;QACxE,gBAAgB,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE;QAC1D,oBAAoB,EAAE,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,EAAE;QAC1E,qBAAqB,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE;QACnE,iBAAiB,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE;QAC3D,oBAAoB,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE;QAC/E,eAAe,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE;KAC5D,CAAC;IACF,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAkB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrE,IAAI;QACJ,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,MAAM,EAAE,OAAO,CAAC,IAAI,CAAE;QACtB,YAAY,EAAE,CAAC,GAAG,GAAG,OAAO,CAAC,IAAI,CAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,WAAW;KAC7D,CAAC,CAAC,CAAC;IACJ,IAAI,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IAEhE,IAAI,GAAG,GAAuB,IAAI,CAAC;IACnC,IAAI,UAAU,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAChD,IAAI,SAAS,GAAG,QAAQ,EAAE,CAAC;YACzB,GAAG,GAAG;gBACJ,SAAS,EAAE,QAAQ;gBACnB,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,uFAAuF,KAAK,GAAG;aAClI,CAAC;YACF,SAAS,GAAG,QAAQ,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,GAAG;QAC5C,OAAO;QACP,GAAG;QACH,UAAU,EAAE,UAAU,CAAC,MAAM;KAC9B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAmB;IAC7C,MAAM,KAAK,GAAG,CAAC,UAAU,MAAM,CAAC,SAAS,MAAM,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,GAAG,CAAC,SAAS,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACpF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,IAAI,SAAS,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,IAAI,YAAY,CAAC,CAAC,MAAM,iBAAiB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAC/K,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -14,7 +14,11 @@ export function parseSexp(text) {
14
14
  let s = '';
15
15
  while (j < text.length && text[j] !== '"') {
16
16
  if (text[j] === '\\' && j + 1 < text.length) {
17
- s += text[j + 1];
17
+ // KiCad writes multi-line text as `\n` (and tabs as `\t`) inside the
18
+ // quoted atom; mapping the escape to the literal letter would give
19
+ // the legibility width model a one-line `line1nline2` string.
20
+ const e = text[j + 1];
21
+ s += e === 'n' ? '\n' : e === 't' ? '\t' : e;
18
22
  j += 2;
19
23
  }
20
24
  else {
@@ -98,7 +102,10 @@ async function loadSheets(rootSch) {
98
102
  await load(rootSch, '/');
99
103
  return out;
100
104
  }
101
- /** Pin definitions per lib symbol name, in symbol coordinates. */
105
+ /** Pin definitions per lib symbol name, in symbol coordinates. Each pin
106
+ * carries the unit of the `_<unit>_<style>` child it was defined in (0 for
107
+ * the common unit); de Morgan alternates (style ≥ 2) are skipped so a pin is
108
+ * recorded once. */
102
109
  function libPinDefs(root) {
103
110
  const map = new Map();
104
111
  const libs = child(root, 'lib_symbols');
@@ -109,9 +116,17 @@ function libPinDefs(root) {
109
116
  if (!name)
110
117
  continue;
111
118
  const pins = [];
112
- const walk = (n) => {
119
+ const walk = (n, unit) => {
113
120
  if (!isList(n))
114
121
  return;
122
+ if (tag(n) === 'symbol') {
123
+ const m = /_(\d+)_(\d+)$/.exec(atomAt(n, 1) ?? '');
124
+ if (m) {
125
+ if (Number(m[2]) >= 2)
126
+ return; // de Morgan alternate body style
127
+ unit = Number(m[1]);
128
+ }
129
+ }
115
130
  if (tag(n) === 'pin') {
116
131
  const at = child(n, 'at');
117
132
  const num = atomAt(child(n, 'number'), 1);
@@ -122,17 +137,26 @@ function libPinDefs(root) {
122
137
  name: pinName ?? '~',
123
138
  x: parseFloat(atomAt(at, 1) ?? '0'),
124
139
  y: parseFloat(atomAt(at, 2) ?? '0'),
140
+ unit,
125
141
  });
126
142
  }
127
143
  }
128
144
  for (const c of n)
129
- walk(c);
145
+ walk(c, unit);
130
146
  };
131
- walk(sym);
147
+ walk(sym, 0);
132
148
  map.set(name, pins);
133
149
  }
134
150
  return map;
135
151
  }
152
+ /** The pins a PLACED instance actually shows: its own unit's plus the common
153
+ * unit's. A single-unit symbol keeps every pin (they are all unit ≤ 1). */
154
+ export function pinsOfUnit(defs, unit) {
155
+ const multi = defs.some((p) => p.unit >= 2);
156
+ if (!multi)
157
+ return defs;
158
+ return defs.filter((p) => p.unit === 0 || p.unit === unit);
159
+ }
136
160
  /** Symbol-space → schematic-space transform (schematic Y grows downward). */
137
161
  export function pinAbsolute(symAt, mirror, pin) {
138
162
  let px = pin.x;
@@ -189,19 +213,37 @@ function symbolsOf(sheet) {
189
213
  rot: parseFloat(atomAt(at, 3) ?? '0'),
190
214
  },
191
215
  uuid: atomAt(child(s, 'uuid'), 1) ?? '',
216
+ unit: parseInt(atomAt(child(s, 'unit'), 1) ?? '1', 10) || 1,
192
217
  },
193
218
  });
194
219
  }
195
220
  return out;
196
221
  }
197
- const isPowerSymbol = (libId) => libId.startsWith('power:');
222
+ function collectPowerSymbols(sheets) {
223
+ const set = new Set();
224
+ for (const sheet of sheets) {
225
+ const libs = child(sheet.root, 'lib_symbols');
226
+ if (!libs)
227
+ continue;
228
+ for (const sym of children(libs, 'symbol')) {
229
+ const name = atomAt(sym, 1);
230
+ const p = child(sym, 'power');
231
+ if (name && p !== undefined && atomAt(p, 1) !== 'no') {
232
+ set.add(name);
233
+ }
234
+ }
235
+ }
236
+ return set;
237
+ }
238
+ const isPowerSymbol = (libId, powerSyms) => libId.startsWith('power:') || powerSyms.has(libId);
198
239
  /** One row per real component (power symbols excluded), across all sheets. */
199
240
  export async function listSymbols(rootSch) {
200
241
  const sheets = await loadSheets(rootSch);
242
+ const powerSyms = collectPowerSymbols(sheets);
201
243
  const out = [];
202
244
  for (const sheet of sheets) {
203
245
  for (const { sym } of symbolsOf(sheet)) {
204
- if (!isPowerSymbol(sym.libId))
246
+ if (!isPowerSymbol(sym.libId, powerSyms))
205
247
  out.push(sym);
206
248
  }
207
249
  }
@@ -210,6 +252,7 @@ export async function listSymbols(rootSch) {
210
252
  /** All net names visible via labels and power symbols, across all sheets. */
211
253
  export async function listNets(rootSch) {
212
254
  const sheets = await loadSheets(rootSch);
255
+ const powerSyms = collectPowerSymbols(sheets);
213
256
  const names = new Set();
214
257
  for (const sheet of sheets) {
215
258
  for (const kind of ['label', 'global_label', 'hierarchical_label']) {
@@ -220,12 +263,217 @@ export async function listNets(rootSch) {
220
263
  }
221
264
  }
222
265
  for (const { sym } of symbolsOf(sheet)) {
223
- if (isPowerSymbol(sym.libId))
266
+ if (isPowerSymbol(sym.libId, powerSyms))
224
267
  names.add(sym.value);
225
268
  }
226
269
  }
227
270
  return [...names].sort();
228
271
  }
272
+ const num = (n, idx, fallback = 0) => {
273
+ const v = atomAt(n, idx);
274
+ return v === undefined ? fallback : parseFloat(v);
275
+ };
276
+ function effectsOf(node) {
277
+ const effects = child(node, 'effects');
278
+ const font = effects ? child(effects, 'font') : undefined;
279
+ const size = font ? child(font, 'size') : undefined;
280
+ // hidden: legacy bare `hide` atom, or v9 `(hide yes)`
281
+ const hideNode = effects ? child(effects, 'hide') : undefined;
282
+ const hidden = (effects?.some((c) => c === 'hide') ?? false) || (hideNode !== undefined && atomAt(hideNode, 1) !== 'no');
283
+ return { height: size ? num(size, 1, 1.27) : 1.27, hidden };
284
+ }
285
+ function libBodyBounds(root) {
286
+ const map = new Map();
287
+ const libs = child(root, 'lib_symbols');
288
+ if (!libs)
289
+ return map;
290
+ for (const sym of children(libs, 'symbol')) {
291
+ const name = atomAt(sym, 1);
292
+ if (!name)
293
+ continue;
294
+ /** Union across the whole symbol under `name`, per-unit union (common
295
+ * graphics included) under `name#<unit>` — a placed unit of a multi-unit
296
+ * symbol draws only its own unit's graphics, so measuring it with the
297
+ * package union would size every instance like the whole gate pack. */
298
+ const perUnit = new Map();
299
+ let b = null;
300
+ const grow = (prev, x, y) => {
301
+ if (!prev)
302
+ return { minX: x, minY: y, maxX: x, maxY: y };
303
+ prev.minX = Math.min(prev.minX, x);
304
+ prev.minY = Math.min(prev.minY, y);
305
+ prev.maxX = Math.max(prev.maxX, x);
306
+ prev.maxY = Math.max(prev.maxY, y);
307
+ return prev;
308
+ };
309
+ const walk = (n, unit) => {
310
+ if (!isList(n))
311
+ return;
312
+ const t = tag(n);
313
+ if (t === 'symbol') {
314
+ const m = /_(\d+)_(\d+)$/.exec(atomAt(n, 1) ?? '');
315
+ if (m) {
316
+ if (Number(m[2]) >= 2)
317
+ return; // de Morgan alternate body style
318
+ unit = Number(m[1]);
319
+ }
320
+ }
321
+ const extend = (x, y) => {
322
+ b = grow(b, x, y);
323
+ if (unit !== null)
324
+ perUnit.set(unit, grow(perUnit.get(unit) ?? null, x, y));
325
+ };
326
+ if (t === 'rectangle') {
327
+ const s = child(n, 'start');
328
+ const e = child(n, 'end');
329
+ extend(num(s, 1), num(s, 2));
330
+ extend(num(e, 1), num(e, 2));
331
+ }
332
+ else if (t === 'circle') {
333
+ const c = child(n, 'center');
334
+ const r = num(child(n, 'radius'), 1);
335
+ extend(num(c, 1) - r, num(c, 2) - r);
336
+ extend(num(c, 1) + r, num(c, 2) + r);
337
+ }
338
+ else if (t === 'arc') {
339
+ for (const part of ['start', 'mid', 'end']) {
340
+ const p = child(n, part);
341
+ if (p)
342
+ extend(num(p, 1), num(p, 2));
343
+ }
344
+ }
345
+ else if (t === 'polyline') {
346
+ for (const xy of children(child(n, 'pts') ?? [], 'xy'))
347
+ extend(num(xy, 1), num(xy, 2));
348
+ }
349
+ // pin/text graphics are deliberately excluded from the body box (design C2)
350
+ if (t !== 'pin' && t !== 'text')
351
+ for (const c of n)
352
+ walk(c, unit);
353
+ };
354
+ walk(sym, null);
355
+ map.set(name, b);
356
+ const common = perUnit.get(0) ?? null;
357
+ for (const [unit, ub] of perUnit) {
358
+ if (unit === 0)
359
+ continue;
360
+ const merged = ub && common
361
+ ? {
362
+ minX: Math.min(ub.minX, common.minX),
363
+ minY: Math.min(ub.minY, common.minY),
364
+ maxX: Math.max(ub.maxX, common.maxX),
365
+ maxY: Math.max(ub.maxY, common.maxY),
366
+ }
367
+ : (ub ?? (common ? { ...common } : null));
368
+ map.set(`${name}#${unit}`, merged);
369
+ }
370
+ }
371
+ return map;
372
+ }
373
+ function textItemsOf(sym) {
374
+ const out = [];
375
+ for (const p of children(sym, 'property')) {
376
+ const key = atomAt(p, 1);
377
+ if (key !== 'Reference' && key !== 'Value')
378
+ continue;
379
+ const at = child(p, 'at');
380
+ const fx = effectsOf(p);
381
+ out.push({
382
+ text: atomAt(p, 2) ?? '',
383
+ x: num(at, 1),
384
+ y: num(at, 2),
385
+ rot: num(at, 3),
386
+ height: fx.height,
387
+ hidden: fx.hidden,
388
+ });
389
+ }
390
+ return out;
391
+ }
392
+ /**
393
+ * Everything the legibility checker needs to see per sheet, extracted read-only.
394
+ * Never serializes and never mutates the file.
395
+ */
396
+ export async function readSheetGeometry(rootSch) {
397
+ const sheets = await loadSheets(rootSch);
398
+ const powerSyms = collectPowerSymbols(sheets);
399
+ return sheets.map((sheet) => {
400
+ const paperNode = child(sheet.root, 'paper');
401
+ const paperName = atomAt(paperNode, 1) ?? null;
402
+ const portrait = paperNode?.includes('portrait') ?? false;
403
+ const explicitW = num(paperNode, 2, NaN);
404
+ const explicitH = num(paperNode, 3, NaN);
405
+ const tb = child(sheet.root, 'title_block');
406
+ const tbField = (name) => (tb ? (atomAt(child(tb, name), 1) ?? '') : '');
407
+ const wires = [];
408
+ for (const w of children(sheet.root, 'wire')) {
409
+ const pts = children(child(w, 'pts') ?? [], 'xy');
410
+ for (let i = 1; i < pts.length; i++) {
411
+ wires.push({ x1: num(pts[i - 1], 1), y1: num(pts[i - 1], 2), x2: num(pts[i], 1), y2: num(pts[i], 2) });
412
+ }
413
+ }
414
+ const labels = [];
415
+ for (const kind of ['label', 'global_label', 'hierarchical_label']) {
416
+ for (const l of children(sheet.root, kind)) {
417
+ const at = child(l, 'at');
418
+ const effects = child(l, 'effects');
419
+ const justify = effects ? child(effects, 'justify') : undefined;
420
+ labels.push({
421
+ name: atomAt(l, 1) ?? '',
422
+ kind,
423
+ x: num(at, 1),
424
+ y: num(at, 2),
425
+ rot: num(at, 3),
426
+ height: effectsOf(l).height,
427
+ justify: justify?.some((c) => c === 'right') ? 'right' : justify?.some((c) => c === 'left') ? 'left' : null,
428
+ });
429
+ }
430
+ }
431
+ const texts = children(sheet.root, 'text').map((t) => {
432
+ const at = child(t, 'at');
433
+ const fx = effectsOf(t);
434
+ return { text: atomAt(t, 1) ?? '', x: num(at, 1), y: num(at, 2), rot: num(at, 3), height: fx.height, hidden: fx.hidden };
435
+ });
436
+ const rectangles = children(sheet.root, 'rectangle').map((r) => {
437
+ const s = child(r, 'start');
438
+ const e = child(r, 'end');
439
+ const stroke = child(r, 'stroke');
440
+ return {
441
+ x1: num(s, 1),
442
+ y1: num(s, 2),
443
+ x2: num(e, 1),
444
+ y2: num(e, 2),
445
+ strokeType: atomAt(child(stroke ?? [], 'type'), 1) ?? 'default',
446
+ };
447
+ });
448
+ return {
449
+ filePath: sheet.filePath,
450
+ sheetName: sheet.sheetName,
451
+ paper: {
452
+ name: paperName,
453
+ width: Number.isNaN(explicitW) ? null : explicitW,
454
+ height: Number.isNaN(explicitH) ? null : explicitH,
455
+ portrait,
456
+ },
457
+ titleBlock: tb ? { title: tbField('title'), date: tbField('date'), rev: tbField('rev') } : null,
458
+ symbols: symbolsOf(sheet).map(({ sym, mirror, node }) => ({
459
+ ref: sym.ref,
460
+ libId: sym.libId,
461
+ value: sym.value,
462
+ at: sym.at,
463
+ mirror,
464
+ isPower: isPowerSymbol(sym.libId, powerSyms),
465
+ unit: sym.unit,
466
+ props: textItemsOf(node),
467
+ })),
468
+ wires,
469
+ labels,
470
+ texts,
471
+ rectangles,
472
+ libBounds: libBodyBounds(sheet.root),
473
+ libPins: libPinDefs(sheet.root),
474
+ };
475
+ });
476
+ }
229
477
  /**
230
478
  * Geometric connectivity per sheet: pins, labels, and wire endpoints that share
231
479
  * coordinates (or are joined by wires) form a group; a group's net name comes
@@ -234,6 +482,7 @@ export async function listNets(rootSch) {
234
482
  */
235
483
  export async function pinNets(rootSch) {
236
484
  const sheets = await loadSheets(rootSch);
485
+ const powerSyms = collectPowerSymbols(sheets);
237
486
  const out = [];
238
487
  for (const sheet of sheets) {
239
488
  const pinDefs = libPinDefs(sheet.root);
@@ -261,12 +510,15 @@ export async function pinNets(rootSch) {
261
510
  }
262
511
  const symPins = [];
263
512
  for (const { sym, mirror } of symbolsOf(sheet)) {
264
- const defs = pinDefs.get(sym.libId) ?? [];
513
+ // a placed unit of a multi-unit symbol shows only its own unit's pins;
514
+ // resolving the whole package at each instance would invent phantom
515
+ // connection points at every other unit's coordinates
516
+ const defs = pinsOfUnit(pinDefs.get(sym.libId) ?? [], sym.unit);
265
517
  for (const pin of defs) {
266
518
  const abs = pinAbsolute(sym.at, mirror, pin);
267
519
  const k = key(abs.x, abs.y);
268
520
  uf.find(k);
269
- if (isPowerSymbol(sym.libId)) {
521
+ if (isPowerSymbol(sym.libId, powerSyms)) {
270
522
  netNameAt.set(k, sym.value);
271
523
  }
272
524
  else {