circuit-json-to-tscircuit 0.0.26 → 0.0.28

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.
@@ -1,137 +1,127 @@
1
- // lib/generate-footprint-tsx.tsx
2
- import { mmStr } from "@tscircuit/mm";
1
+ // lib/generate-footprint-tsx/convert-copper-text.ts
3
2
  import { su } from "@tscircuit/soup-util";
4
- var generateFootprintTsx = (circuitJson) => {
5
- const holes = su(circuitJson).pcb_hole.list();
6
- const platedHoles = su(circuitJson).pcb_plated_hole.list();
7
- const smtPads = su(circuitJson).pcb_smtpad.list();
8
- const silkscreenLines = su(circuitJson).pcb_silkscreen_line.list();
9
- const silkscreenPaths = su(circuitJson).pcb_silkscreen_path.list();
10
- const silkscreenRects = su(circuitJson).pcb_silkscreen_rect.list();
11
- const silkscreenCircles = su(circuitJson).pcb_silkscreen_circle.list();
12
- const fabricationNotePaths = su(circuitJson).pcb_fabrication_note_path.list();
13
- const fabricationNoteTexts = su(circuitJson).pcb_fabrication_note_text.list();
14
- const fabricationNoteRects = su(circuitJson).pcb_fabrication_note_rect.list();
15
- const fabricationNoteDimensions = su(circuitJson).pcb_fabrication_note_dimension.list();
3
+
4
+ // lib/generate-footprint-tsx/helpers.ts
5
+ import { mmStr } from "@tscircuit/mm";
6
+ var escapeJsxText = (value) => String(value ?? "").replace(/"/g, '\\"');
7
+ var formatMm = (value) => mmStr(value ?? 0);
8
+ var formatPcbRotationAttr = (rotation, attrName = "pcbRotation") => {
9
+ if (rotation === void 0) return "";
10
+ const formatted = typeof rotation === "number" ? `${rotation}deg` : rotation;
11
+ return ` ${attrName}="${formatted}"`;
12
+ };
13
+
14
+ // lib/generate-footprint-tsx/convert-copper-text.ts
15
+ var convertCopperText = (circuitJson) => {
16
16
  const copperTexts = su(circuitJson).pcb_copper_text.list();
17
- const silkscreenTexts = su(circuitJson).pcb_silkscreen_text.list();
18
- const pcbCutouts = su(circuitJson).pcb_cutout.list();
19
- const noteTexts = su(circuitJson).pcb_note_text.list();
20
- const noteRects = su(circuitJson).pcb_note_rect.list();
21
- const notePaths = su(circuitJson).pcb_note_path.list();
22
- const noteLines = su(circuitJson).pcb_note_line.list();
23
- const noteDimensions = su(circuitJson).pcb_note_dimension.list();
24
- const courtyardOutlines = su(circuitJson).pcb_courtyard_outline.list();
25
- const courtyardRects = su(circuitJson).pcb_courtyard_rect.list();
26
- const courtyardCircles = su(circuitJson).pcb_courtyard_circle.list();
27
17
  const elementStrings = [];
28
- for (const hole of holes) {
29
- if (hole.hole_shape === "circle") {
30
- elementStrings.push(
31
- `<hole pcbX="${mmStr(hole.x)}" pcbY="${mmStr(hole.y)}" diameter="${mmStr(hole.hole_diameter)}" />`
32
- );
33
- } else if (hole.hole_shape === "rect") {
34
- elementStrings.push(
35
- `<hole pcbX="${mmStr(hole.x)}" pcbY="${mmStr(hole.y)}" width="${mmStr(hole.hole_width)}" height="${mmStr(hole.hole_height)}" shape="rect" />`
36
- );
37
- } else if (hole.hole_shape === "oval" || hole.hole_shape === "pill" || hole.hole_shape === "rotated_pill") {
38
- const pcbRotation = "ccw_rotation" in hole && hole.ccw_rotation !== void 0 ? ` pcbRotation="${typeof hole.ccw_rotation === "number" ? `${hole.ccw_rotation}deg` : hole.ccw_rotation}"` : "";
39
- elementStrings.push(
40
- `<hole pcbX="${mmStr(hole.x)}" pcbY="${mmStr(hole.y)}" width="${mmStr(hole.hole_width)}" height="${mmStr(hole.hole_height)}" shape="pill"${pcbRotation} />`
41
- );
18
+ for (const copperText of copperTexts) {
19
+ const anchorPosition = copperText.anchor_position ?? { x: 0, y: 0 };
20
+ const attrs = [
21
+ `pcbX={${anchorPosition.x}}`,
22
+ `pcbY={${anchorPosition.y}}`,
23
+ `anchorAlignment="${copperText.anchor_alignment ?? "center"}"`,
24
+ `text="${escapeJsxText(copperText.text)}"`
25
+ ];
26
+ if (copperText.font !== void 0) {
27
+ attrs.push(`font="${copperText.font}"`);
42
28
  }
43
- }
44
- for (const platedHole of platedHoles) {
45
- if (platedHole.shape === "oval" || platedHole.shape === "pill") {
46
- const pcbRotation = "ccw_rotation" in platedHole && platedHole.ccw_rotation !== void 0 ? ` pcbRotation="${typeof platedHole.ccw_rotation === "number" ? `${platedHole.ccw_rotation}deg` : platedHole.ccw_rotation}"` : "";
47
- elementStrings.push(
48
- `<platedhole portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr(platedHole.x)}" pcbY="${mmStr(platedHole.y)}" outerHeight="${mmStr(platedHole.outer_height)}" outerWidth="${mmStr(platedHole.outer_width)}" holeHeight="${mmStr(platedHole.hole_height)}" holeWidth="${mmStr(platedHole.hole_width)}" height="${mmStr(platedHole.hole_height)}" shape="${platedHole.shape}"${pcbRotation} />`
49
- );
50
- } else if (platedHole.shape === "circle") {
51
- elementStrings.push(
52
- `<platedhole portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr(platedHole.x)}" pcbY="${mmStr(platedHole.y)}" outerDiameter="${mmStr(platedHole.outer_diameter)}" holeDiameter="${mmStr(platedHole.hole_diameter)}" shape="circle" />`
53
- );
29
+ if (copperText.font_size !== void 0) {
30
+ attrs.push(`fontSize={${copperText.font_size}}`);
54
31
  }
55
- }
56
- for (const smtPad of smtPads) {
57
- if (smtPad.shape === "circle") {
58
- elementStrings.push(
59
- `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr(smtPad.x)}" pcbY="${mmStr(smtPad.y)}" radius="${mmStr(smtPad.radius)}" shape="circle" />`
60
- );
61
- } else if (smtPad.shape === "rect") {
62
- elementStrings.push(
63
- `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr(smtPad.x)}" pcbY="${mmStr(smtPad.y)}" width="${mmStr(smtPad.width)}" height="${mmStr(smtPad.height)}" shape="rect" />`
64
- );
65
- } else if (smtPad.shape === "pill") {
66
- elementStrings.push(
67
- `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr(smtPad.x)}" pcbY="${mmStr(smtPad.y)}" width="${mmStr(smtPad.width)}" height="${mmStr(smtPad.height)}" radius="${mmStr(smtPad.radius)}" shape="pill" />`
68
- );
69
- } else if (smtPad.shape === "polygon") {
70
- elementStrings.push(
71
- `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} shape="polygon" points={${JSON.stringify(smtPad.points)}} />`
72
- );
73
- } else if (smtPad.shape === "rotated_rect") {
74
- elementStrings.push(
75
- `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr(smtPad.x)}" pcbY="${mmStr(smtPad.y)}" width="${mmStr(smtPad.width)}" height="${mmStr(smtPad.height)}" ccwRotation={${smtPad.ccw_rotation}} shape="rotated_rect" />`
76
- );
32
+ if (copperText.ccw_rotation !== void 0) {
33
+ attrs.push(`pcbRotation="${copperText.ccw_rotation}deg"`);
77
34
  }
78
- }
79
- for (const silkscreenPath of silkscreenPaths) {
80
- elementStrings.push(
81
- `<silkscreenpath route={${JSON.stringify(silkscreenPath.route)}} />`
82
- );
83
- }
84
- for (const silkscreenRect of silkscreenRects) {
85
- const center = silkscreenRect.center ?? { x: 0, y: 0 };
86
- const attrs = [
87
- `pcbX={${center.x}}`,
88
- `pcbY={${center.y}}`,
89
- `width={${silkscreenRect.width ?? 0}}`,
90
- `height={${silkscreenRect.height ?? 0}}`,
91
- `layer="${silkscreenRect.layer}"`
92
- ];
93
- if (silkscreenRect.stroke_width !== void 0) {
94
- attrs.push(`strokeWidth={${silkscreenRect.stroke_width}}`);
35
+ if (copperText.is_knockout !== void 0) {
36
+ attrs.push(`knockout={${copperText.is_knockout}}`);
95
37
  }
96
- if (silkscreenRect.is_filled !== void 0) {
97
- attrs.push(`filled={${silkscreenRect.is_filled}}`);
38
+ if (copperText.is_mirrored !== void 0) {
39
+ attrs.push(`mirrored={${copperText.is_mirrored}}`);
98
40
  }
99
- if (silkscreenRect.corner_radius !== void 0) {
100
- attrs.push(`cornerRadius={${silkscreenRect.corner_radius}}`);
41
+ if (copperText.layer !== void 0 && copperText.layer !== "top") {
42
+ attrs.push(`layer="${copperText.layer}"`);
101
43
  }
102
- elementStrings.push(`<silkscreenrect ${attrs.join(" ")} />`);
44
+ elementStrings.push(`<coppertext ${attrs.join(" ")} />`);
103
45
  }
104
- for (const silkscreenCircle of silkscreenCircles) {
105
- const center = silkscreenCircle.center ?? { x: 0, y: 0 };
46
+ return elementStrings;
47
+ };
48
+
49
+ // lib/generate-footprint-tsx/convert-courtyard.ts
50
+ import { su as su2 } from "@tscircuit/soup-util";
51
+ var convertCourtyard = (circuitJson) => {
52
+ const courtyardOutlines = su2(circuitJson).pcb_courtyard_outline.list();
53
+ const courtyardRects = su2(circuitJson).pcb_courtyard_rect.list();
54
+ const courtyardCircles = su2(circuitJson).pcb_courtyard_circle.list();
55
+ const elementStrings = [];
56
+ for (const courtyardOutline of courtyardOutlines) {
106
57
  const attrs = [
107
- `pcbX={${center.x}}`,
108
- `pcbY={${center.y}}`,
109
- `radius={${silkscreenCircle.radius ?? 0}}`,
110
- `layer="${silkscreenCircle.layer}"`
58
+ `outline={${JSON.stringify(courtyardOutline.outline ?? [])}}`
111
59
  ];
112
- if (silkscreenCircle.stroke_width !== void 0) {
113
- attrs.push(`strokeWidth={${silkscreenCircle.stroke_width}}`);
60
+ if (courtyardOutline.layer !== void 0) {
61
+ attrs.push(`layer="${courtyardOutline.layer}"`);
114
62
  }
115
- if (silkscreenCircle.is_filled !== void 0) {
116
- attrs.push(`isFilled={${silkscreenCircle.is_filled}}`);
63
+ elementStrings.push(`<courtyardoutline ${attrs.join(" ")} />`);
64
+ }
65
+ for (const courtyardRect of courtyardRects) {
66
+ const attrs = [
67
+ `pcbX={${courtyardRect.center?.x ?? 0}}`,
68
+ `pcbY={${courtyardRect.center?.y ?? 0}}`,
69
+ `width={${courtyardRect.width ?? 0}}`,
70
+ `height={${courtyardRect.height ?? 0}}`
71
+ ];
72
+ if (courtyardRect.layer !== void 0) {
73
+ attrs.push(`layer="${courtyardRect.layer}"`);
117
74
  }
118
- elementStrings.push(`<silkscreencircle ${attrs.join(" ")} />`);
75
+ elementStrings.push(`<courtyardrect ${attrs.join(" ")} />`);
119
76
  }
120
- for (const silkscreenLine of silkscreenLines) {
77
+ for (const courtyardCircle of courtyardCircles) {
121
78
  const attrs = [
122
- `x1={${silkscreenLine.x1 ?? 0}}`,
123
- `y1={${silkscreenLine.y1 ?? 0}}`,
124
- `x2={${silkscreenLine.x2 ?? 0}}`,
125
- `y2={${silkscreenLine.y2 ?? 0}}`
79
+ `pcbX={${courtyardCircle.center?.x ?? 0}}`,
80
+ `pcbY={${courtyardCircle.center?.y ?? 0}}`,
81
+ `radius={${courtyardCircle.radius ?? 0}}`
126
82
  ];
127
- if (silkscreenLine.stroke_width !== void 0) {
128
- attrs.push(`strokeWidth={${silkscreenLine.stroke_width}}`);
83
+ if (courtyardCircle.layer !== void 0) {
84
+ attrs.push(`layer="${courtyardCircle.layer}"`);
129
85
  }
130
- if (silkscreenLine.layer === "bottom") {
131
- attrs.push(`layer="bottom"`);
86
+ elementStrings.push(`<courtyardcircle ${attrs.join(" ")} />`);
87
+ }
88
+ return elementStrings;
89
+ };
90
+
91
+ // lib/generate-footprint-tsx/convert-cutouts.ts
92
+ import { su as su3 } from "@tscircuit/soup-util";
93
+ var convertCutouts = (circuitJson) => {
94
+ const pcbCutouts = su3(circuitJson).pcb_cutout.list();
95
+ const elementStrings = [];
96
+ for (const cutout of pcbCutouts) {
97
+ if (cutout.shape === "rect") {
98
+ const rotation = cutout.rotation !== void 0 ? ` pcbRotation="${formatMm(cutout.rotation)}"` : "";
99
+ elementStrings.push(
100
+ `<cutout shape="rect" pcbX="${formatMm(cutout.center.x)}" pcbY="${formatMm(cutout.center.y)}" width="${formatMm(cutout.width)}" height="${formatMm(cutout.height)}"${rotation} />`
101
+ );
102
+ } else if (cutout.shape === "circle") {
103
+ elementStrings.push(
104
+ `<cutout shape="circle" pcbX="${formatMm(cutout.center.x)}" pcbY="${formatMm(cutout.center.y)}" radius="${formatMm(cutout.radius)}" />`
105
+ );
106
+ } else if (cutout.shape === "polygon") {
107
+ elementStrings.push(
108
+ `<cutout shape="polygon" points={${JSON.stringify(cutout.points)}} />`
109
+ );
110
+ } else {
111
+ console.warn(`Unhandled pcb_cutout shape: ${cutout.shape}`);
132
112
  }
133
- elementStrings.push(`<silkscreenline ${attrs.join(" ")} />`);
134
113
  }
114
+ return elementStrings;
115
+ };
116
+
117
+ // lib/generate-footprint-tsx/convert-fabrication.ts
118
+ import { su as su4 } from "@tscircuit/soup-util";
119
+ var convertFabrication = (circuitJson) => {
120
+ const fabricationNotePaths = su4(circuitJson).pcb_fabrication_note_path.list();
121
+ const fabricationNoteTexts = su4(circuitJson).pcb_fabrication_note_text.list();
122
+ const fabricationNoteRects = su4(circuitJson).pcb_fabrication_note_rect.list();
123
+ const fabricationNoteDimensions = su4(circuitJson).pcb_fabrication_note_dimension.list();
124
+ const elementStrings = [];
135
125
  for (const fabPath of fabricationNotePaths) {
136
126
  const attrs = [`route={${JSON.stringify(fabPath.route)}}`];
137
127
  if ("stroke_width" in fabPath && fabPath.stroke_width !== void 0) {
@@ -147,14 +137,11 @@ var generateFootprintTsx = (circuitJson) => {
147
137
  }
148
138
  for (const fabText of fabricationNoteTexts) {
149
139
  const anchorPosition = fabText.anchor_position ?? { x: 0, y: 0 };
150
- const anchorAlignment = fabText.anchor_alignment ?? "center";
151
- const rawText = String(fabText.text ?? "");
152
- const escapedText = rawText.replace(/"/g, '\\"');
153
140
  const attrs = [
154
141
  `pcbX={${anchorPosition.x}}`,
155
142
  `pcbY={${anchorPosition.y}}`,
156
- `anchorAlignment="${anchorAlignment}"`,
157
- `text="${escapedText}"`
143
+ `anchorAlignment="${fabText.anchor_alignment ?? "center"}"`,
144
+ `text="${escapeJsxText(fabText.text)}"`
158
145
  ];
159
146
  if (fabText.font !== void 0) {
160
147
  attrs.push(`font="${fabText.font}"`);
@@ -209,8 +196,7 @@ var generateFootprintTsx = (circuitJson) => {
209
196
  `to={{ x: ${toPoint.x}, y: ${toPoint.y} }}`
210
197
  ];
211
198
  if (fabDimension.text !== void 0) {
212
- const escapedText = String(fabDimension.text).replace(/"/g, '\\"');
213
- attrs.push(`text="${escapedText}"`);
199
+ attrs.push(`text="${escapeJsxText(fabDimension.text)}"`);
214
200
  }
215
201
  if (fabDimension.font !== void 0) {
216
202
  attrs.push(`font="${fabDimension.font}"`);
@@ -234,95 +220,57 @@ var generateFootprintTsx = (circuitJson) => {
234
220
  }
235
221
  elementStrings.push(`<fabricationnotedimension ${attrs.join(" ")} />`);
236
222
  }
237
- for (const copperText of copperTexts) {
238
- const anchorPosition = copperText.anchor_position ?? { x: 0, y: 0 };
239
- const anchorAlignment = copperText.anchor_alignment ?? "center";
240
- const rawText = String(copperText.text ?? "");
241
- const escapedText = rawText.replace(/"/g, '\\"');
242
- const attrs = [
243
- `pcbX={${anchorPosition.x}}`,
244
- `pcbY={${anchorPosition.y}}`,
245
- `anchorAlignment="${anchorAlignment}"`,
246
- `text="${escapedText}"`
247
- ];
248
- if (copperText.font !== void 0) {
249
- attrs.push(`font="${copperText.font}"`);
250
- }
251
- if (copperText.font_size !== void 0) {
252
- attrs.push(`fontSize={${copperText.font_size}}`);
253
- }
254
- if (copperText.ccw_rotation !== void 0) {
255
- attrs.push(`pcbRotation="${copperText.ccw_rotation}deg"`);
256
- }
257
- if (copperText.is_knockout !== void 0) {
258
- attrs.push(`knockout={${copperText.is_knockout}}`);
259
- }
260
- if (copperText.is_mirrored !== void 0) {
261
- attrs.push(`mirrored={${copperText.is_mirrored}}`);
262
- }
263
- if (copperText.layer !== void 0 && copperText.layer !== "top") {
264
- attrs.push(`layer="${copperText.layer}"`);
265
- }
266
- elementStrings.push(`<coppertext ${attrs.join(" ")} />`);
267
- }
268
- for (const stext of silkscreenTexts) {
269
- const pcbX = stext.anchor_position?.x ?? 0;
270
- const pcbY = stext.anchor_position?.y ?? 0;
271
- const anchorAlignment = stext.anchor_alignment;
272
- const fontSize = stext.font_size;
273
- const rawText = String(stext.text ?? "");
274
- const escapedText = rawText.replace(/"/g, '\\"');
275
- elementStrings.push(
276
- `<silkscreentext pcbX={${pcbX}} pcbY={${pcbY}} anchorAlignment="${anchorAlignment}" fontSize={${fontSize}} text="${escapedText}" />`
277
- );
278
- }
279
- for (const cutout of pcbCutouts) {
280
- if (cutout.shape === "rect") {
281
- const pcbX = cutout.center.x;
282
- const pcbY = cutout.center.y;
283
- const width = mmStr(cutout.width);
284
- const height = mmStr(cutout.height);
285
- const rotation = cutout.rotation !== void 0 ? ` pcbRotation="${mmStr(cutout.rotation)}"` : "";
223
+ return elementStrings;
224
+ };
225
+
226
+ // lib/generate-footprint-tsx/convert-holes.ts
227
+ import { su as su5 } from "@tscircuit/soup-util";
228
+ import { mmStr as mmStr2 } from "@tscircuit/mm";
229
+ var convertHoles = (circuitJson) => {
230
+ const holes = su5(circuitJson).pcb_hole.list();
231
+ const elementStrings = [];
232
+ for (const hole of holes) {
233
+ if (hole.hole_shape === "circle") {
286
234
  elementStrings.push(
287
- `<cutout shape="rect" pcbX="${mmStr(pcbX)}" pcbY="${mmStr(pcbY)}" width="${width}" height="${height}"${rotation} />`
235
+ `<hole pcbX="${mmStr2(hole.x)}" pcbY="${mmStr2(hole.y)}" diameter="${mmStr2(hole.hole_diameter)}" />`
288
236
  );
289
- } else if (cutout.shape === "circle") {
290
- const pcbX = cutout.center.x;
291
- const pcbY = cutout.center.y;
292
- const radius = mmStr(cutout.radius);
237
+ } else if (hole.hole_shape === "rect") {
293
238
  elementStrings.push(
294
- `<cutout shape="circle" pcbX="${mmStr(pcbX)}" pcbY="${mmStr(pcbY)}" radius="${radius}" />`
239
+ `<hole pcbX="${mmStr2(hole.x)}" pcbY="${mmStr2(hole.y)}" width="${mmStr2(hole.hole_width)}" height="${mmStr2(hole.hole_height)}" shape="rect" />`
240
+ );
241
+ } else if (hole.hole_shape === "oval" || hole.hole_shape === "pill" || hole.hole_shape === "rotated_pill") {
242
+ elementStrings.push(
243
+ `<hole pcbX="${mmStr2(hole.x)}" pcbY="${mmStr2(hole.y)}" width="${mmStr2(hole.hole_width)}" height="${mmStr2(hole.hole_height)}" shape="pill"${formatPcbRotationAttr("ccw_rotation" in hole ? hole.ccw_rotation : void 0)} />`
295
244
  );
296
- } else if (cutout.shape === "polygon") {
297
- const pointsJson = JSON.stringify(cutout.points);
298
- elementStrings.push(`<cutout shape="polygon" points={${pointsJson}} />`);
299
- } else {
300
- console.warn(`Unhandled pcb_cutout shape: ${cutout.shape}`);
301
245
  }
302
246
  }
247
+ return elementStrings;
248
+ };
249
+
250
+ // lib/generate-footprint-tsx/convert-notes.ts
251
+ import { su as su6 } from "@tscircuit/soup-util";
252
+ var convertNotes = (circuitJson) => {
253
+ const noteTexts = su6(circuitJson).pcb_note_text.list();
254
+ const noteRects = su6(circuitJson).pcb_note_rect.list();
255
+ const notePaths = su6(circuitJson).pcb_note_path.list();
256
+ const noteLines = su6(circuitJson).pcb_note_line.list();
257
+ const noteDimensions = su6(circuitJson).pcb_note_dimension.list();
258
+ const elementStrings = [];
303
259
  for (const noteText of noteTexts) {
304
260
  const anchorPosition = noteText.anchor_position ?? { x: 0, y: 0 };
305
- const anchorAlignment = noteText.anchor_alignment ?? "center";
306
- const font = noteText.font ?? "tscircuit2024";
307
- const fontSize = noteText.font_size ?? 0;
308
261
  const colorAttr = noteText.color ? ` color="${noteText.color}"` : "";
309
- const rawText = String(noteText.text ?? "");
310
- const escapedText = rawText.replace(/"/g, '\\"');
311
262
  elementStrings.push(
312
- `<pcbnotetext pcbX={${anchorPosition.x}} pcbY={${anchorPosition.y}} anchorAlignment="${anchorAlignment}" font="${font}" fontSize={${fontSize}} text="${escapedText}"${colorAttr} />`
263
+ `<pcbnotetext pcbX={${anchorPosition.x}} pcbY={${anchorPosition.y}} anchorAlignment="${noteText.anchor_alignment ?? "center"}" font="${noteText.font ?? "tscircuit2024"}" fontSize={${noteText.font_size ?? 0}} text="${escapeJsxText(noteText.text)}"${colorAttr} />`
313
264
  );
314
265
  }
315
266
  for (const noteRect of noteRects) {
316
267
  const center = noteRect.center ?? { x: 0, y: 0 };
317
- const width = noteRect.width ?? 0;
318
- const height = noteRect.height ?? 0;
319
- const strokeWidth = noteRect.stroke_width ?? 0;
320
268
  const attrs = [
321
269
  `pcbX={${center.x}}`,
322
270
  `pcbY={${center.y}}`,
323
- `width={${width}}`,
324
- `height={${height}}`,
325
- `strokeWidth={${strokeWidth}}`
271
+ `width={${noteRect.width ?? 0}}`,
272
+ `height={${noteRect.height ?? 0}}`,
273
+ `strokeWidth={${noteRect.stroke_width ?? 0}}`
326
274
  ];
327
275
  if (noteRect.is_filled !== void 0) {
328
276
  attrs.push(`isFilled={${noteRect.is_filled}}`);
@@ -339,8 +287,7 @@ var generateFootprintTsx = (circuitJson) => {
339
287
  elementStrings.push(`<pcbnoterect ${attrs.join(" ")} />`);
340
288
  }
341
289
  for (const notePath of notePaths) {
342
- const routeJson = JSON.stringify(notePath.route ?? []);
343
- const attrs = [`route={${routeJson}}`];
290
+ const attrs = [`route={${JSON.stringify(notePath.route ?? [])}}`];
344
291
  if (notePath.stroke_width !== void 0) {
345
292
  attrs.push(`strokeWidth={${notePath.stroke_width}}`);
346
293
  }
@@ -370,17 +317,14 @@ var generateFootprintTsx = (circuitJson) => {
370
317
  for (const noteDimension of noteDimensions) {
371
318
  const fromPoint = noteDimension.from ?? { x: 0, y: 0 };
372
319
  const toPoint = noteDimension.to ?? { x: 0, y: 0 };
373
- const font = noteDimension.font ?? "tscircuit2024";
374
- const fontSize = noteDimension.font_size ?? 0;
375
- const arrowSize = noteDimension.arrow_size;
376
320
  const attrs = [
377
321
  `from={{ x: ${fromPoint.x}, y: ${fromPoint.y} }}`,
378
322
  `to={{ x: ${toPoint.x}, y: ${toPoint.y} }}`,
379
- `font="${font}"`,
380
- `fontSize={${fontSize}}`
323
+ `font="${noteDimension.font ?? "tscircuit2024"}"`,
324
+ `fontSize={${noteDimension.font_size ?? 0}}`
381
325
  ];
382
- if (arrowSize !== void 0) {
383
- attrs.push(`arrowSize={${arrowSize}}`);
326
+ if (noteDimension.arrow_size !== void 0) {
327
+ attrs.push(`arrowSize={${noteDimension.arrow_size}}`);
384
328
  }
385
329
  if ("offset" in noteDimension) {
386
330
  const offsetValue = noteDimension.offset;
@@ -389,46 +333,182 @@ var generateFootprintTsx = (circuitJson) => {
389
333
  }
390
334
  }
391
335
  if (noteDimension.text !== void 0) {
392
- const escapedText = String(noteDimension.text).replace(/"/g, '\\"');
393
- attrs.push(`text="${escapedText}"`);
336
+ attrs.push(`text="${escapeJsxText(noteDimension.text)}"`);
394
337
  }
395
338
  if (noteDimension.color !== void 0) {
396
339
  attrs.push(`color="${noteDimension.color}"`);
397
340
  }
398
341
  elementStrings.push(`<pcbnotedimension ${attrs.join(" ")} />`);
399
342
  }
400
- for (const courtyardOutline of courtyardOutlines) {
343
+ return elementStrings;
344
+ };
345
+
346
+ // lib/generate-footprint-tsx/convert-plated-holes.ts
347
+ import { su as su7 } from "@tscircuit/soup-util";
348
+ import { mmStr as mmStr3 } from "@tscircuit/mm";
349
+ var formatOptionalMmAttr = (attrName, value) => {
350
+ if (value === void 0) return "";
351
+ return ` ${attrName}="${mmStr3(value)}"`;
352
+ };
353
+ var convertPlatedHoles = (circuitJson) => {
354
+ const platedHoles = su7(circuitJson).pcb_plated_hole.list();
355
+ const elementStrings = [];
356
+ for (const platedHole of platedHoles) {
357
+ if (platedHole.shape === "oval" || platedHole.shape === "pill") {
358
+ elementStrings.push(
359
+ `<platedhole portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr3(platedHole.x)}" pcbY="${mmStr3(platedHole.y)}" outerHeight="${mmStr3(platedHole.outer_height)}" outerWidth="${mmStr3(platedHole.outer_width)}" holeHeight="${mmStr3(platedHole.hole_height)}" holeWidth="${mmStr3(platedHole.hole_width)}" height="${mmStr3(platedHole.hole_height)}" shape="${platedHole.shape}"${formatPcbRotationAttr("ccw_rotation" in platedHole ? platedHole.ccw_rotation : void 0)} />`
360
+ );
361
+ } else if (platedHole.shape === "circle") {
362
+ elementStrings.push(
363
+ `<platedhole portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr3(platedHole.x)}" pcbY="${mmStr3(platedHole.y)}" outerDiameter="${mmStr3(platedHole.outer_diameter)}" holeDiameter="${mmStr3(platedHole.hole_diameter)}" shape="circle" />`
364
+ );
365
+ } else if (platedHole.shape === "circular_hole_with_rect_pad") {
366
+ elementStrings.push(
367
+ `<platedhole portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr3(platedHole.x)}" pcbY="${mmStr3(platedHole.y)}" holeDiameter="${mmStr3(platedHole.hole_diameter)}" rectPadWidth="${mmStr3(platedHole.rect_pad_width)}" rectPadHeight="${mmStr3(platedHole.rect_pad_height)}"${formatOptionalMmAttr("rectBorderRadius", platedHole.rect_border_radius)}${formatOptionalMmAttr("holeOffsetX", platedHole.hole_offset_x)}${formatOptionalMmAttr("holeOffsetY", platedHole.hole_offset_y)} shape="circular_hole_with_rect_pad" />`
368
+ );
369
+ } else if (platedHole.shape === "pill_hole_with_rect_pad") {
370
+ elementStrings.push(
371
+ `<platedhole portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr3(platedHole.x)}" pcbY="${mmStr3(platedHole.y)}" holeWidth="${mmStr3(platedHole.hole_width)}" holeHeight="${mmStr3(platedHole.hole_height)}" rectPadWidth="${mmStr3(platedHole.rect_pad_width)}" rectPadHeight="${mmStr3(platedHole.rect_pad_height)}"${formatOptionalMmAttr("rectBorderRadius", platedHole.rect_border_radius)}${formatOptionalMmAttr("holeOffsetX", platedHole.hole_offset_x)}${formatOptionalMmAttr("holeOffsetY", platedHole.hole_offset_y)} shape="pill_hole_with_rect_pad" />`
372
+ );
373
+ } else if (platedHole.shape === "hole_with_polygon_pad") {
374
+ const holeSizeAttrs = platedHole.hole_shape === "circle" ? ` holeDiameter="${mmStr3(platedHole.hole_diameter ?? 0)}"` : `${formatOptionalMmAttr("holeWidth", platedHole.hole_width)}${formatOptionalMmAttr("holeHeight", platedHole.hole_height)}`;
375
+ elementStrings.push(
376
+ `<platedhole portHints={${JSON.stringify(platedHole.port_hints)}} pcbX="${mmStr3(platedHole.x)}" pcbY="${mmStr3(platedHole.y)}" holeShape="${platedHole.hole_shape}"${holeSizeAttrs} padOutline={${JSON.stringify(platedHole.pad_outline)}} holeOffsetX="${mmStr3(platedHole.hole_offset_x)}" holeOffsetY="${mmStr3(platedHole.hole_offset_y)}" shape="hole_with_polygon_pad" />`
377
+ );
378
+ }
379
+ }
380
+ return elementStrings;
381
+ };
382
+
383
+ // lib/generate-footprint-tsx/convert-silkscreen.ts
384
+ import { su as su8 } from "@tscircuit/soup-util";
385
+ var convertSilkscreen = (circuitJson) => {
386
+ const silkscreenLines = su8(circuitJson).pcb_silkscreen_line.list();
387
+ const silkscreenPaths = su8(circuitJson).pcb_silkscreen_path.list();
388
+ const silkscreenRects = su8(circuitJson).pcb_silkscreen_rect.list();
389
+ const silkscreenCircles = su8(circuitJson).pcb_silkscreen_circle.list();
390
+ const elementStrings = [];
391
+ for (const silkscreenPath of silkscreenPaths) {
392
+ elementStrings.push(
393
+ `<silkscreenpath route={${JSON.stringify(silkscreenPath.route)}} />`
394
+ );
395
+ }
396
+ for (const silkscreenRect of silkscreenRects) {
397
+ const center = silkscreenRect.center ?? { x: 0, y: 0 };
401
398
  const attrs = [
402
- `outline={${JSON.stringify(courtyardOutline.outline ?? [])}}`
399
+ `pcbX={${center.x}}`,
400
+ `pcbY={${center.y}}`,
401
+ `width={${silkscreenRect.width ?? 0}}`,
402
+ `height={${silkscreenRect.height ?? 0}}`,
403
+ `layer="${silkscreenRect.layer}"`
403
404
  ];
404
- if (courtyardOutline.layer !== void 0) {
405
- attrs.push(`layer="${courtyardOutline.layer}"`);
405
+ if (silkscreenRect.stroke_width !== void 0) {
406
+ attrs.push(`strokeWidth={${silkscreenRect.stroke_width}}`);
406
407
  }
407
- elementStrings.push(`<courtyardoutline ${attrs.join(" ")} />`);
408
+ if (silkscreenRect.is_filled !== void 0) {
409
+ attrs.push(`filled={${silkscreenRect.is_filled}}`);
410
+ }
411
+ if (silkscreenRect.corner_radius !== void 0) {
412
+ attrs.push(`cornerRadius={${silkscreenRect.corner_radius}}`);
413
+ }
414
+ elementStrings.push(`<silkscreenrect ${attrs.join(" ")} />`);
408
415
  }
409
- for (const courtyardRect of courtyardRects) {
416
+ for (const silkscreenCircle of silkscreenCircles) {
417
+ const center = silkscreenCircle.center ?? { x: 0, y: 0 };
410
418
  const attrs = [
411
- `pcbX={${courtyardRect.center?.x ?? 0}}`,
412
- `pcbY={${courtyardRect.center?.y ?? 0}}`,
413
- `width={${courtyardRect.width ?? 0}}`,
414
- `height={${courtyardRect.height ?? 0}}`
419
+ `pcbX={${center.x}}`,
420
+ `pcbY={${center.y}}`,
421
+ `radius={${silkscreenCircle.radius ?? 0}}`,
422
+ `layer="${silkscreenCircle.layer}"`
415
423
  ];
416
- if (courtyardRect.layer !== void 0) {
417
- attrs.push(`layer="${courtyardRect.layer}"`);
424
+ if (silkscreenCircle.stroke_width !== void 0) {
425
+ attrs.push(`strokeWidth={${silkscreenCircle.stroke_width}}`);
418
426
  }
419
- elementStrings.push(`<courtyardrect ${attrs.join(" ")} />`);
427
+ if (silkscreenCircle.is_filled !== void 0) {
428
+ attrs.push(`isFilled={${silkscreenCircle.is_filled}}`);
429
+ }
430
+ elementStrings.push(`<silkscreencircle ${attrs.join(" ")} />`);
420
431
  }
421
- for (const courtyardCircle of courtyardCircles) {
432
+ for (const silkscreenLine of silkscreenLines) {
422
433
  const attrs = [
423
- `pcbX={${courtyardCircle.center?.x ?? 0}}`,
424
- `pcbY={${courtyardCircle.center?.y ?? 0}}`,
425
- `radius={${courtyardCircle.radius ?? 0}}`
434
+ `x1={${silkscreenLine.x1 ?? 0}}`,
435
+ `y1={${silkscreenLine.y1 ?? 0}}`,
436
+ `x2={${silkscreenLine.x2 ?? 0}}`,
437
+ `y2={${silkscreenLine.y2 ?? 0}}`
426
438
  ];
427
- if (courtyardCircle.layer !== void 0) {
428
- attrs.push(`layer="${courtyardCircle.layer}"`);
439
+ if (silkscreenLine.stroke_width !== void 0) {
440
+ attrs.push(`strokeWidth={${silkscreenLine.stroke_width}}`);
441
+ }
442
+ if (silkscreenLine.layer === "bottom") {
443
+ attrs.push(`layer="bottom"`);
444
+ }
445
+ elementStrings.push(`<silkscreenline ${attrs.join(" ")} />`);
446
+ }
447
+ return elementStrings;
448
+ };
449
+
450
+ // lib/generate-footprint-tsx/convert-silkscreen-text.ts
451
+ import { su as su9 } from "@tscircuit/soup-util";
452
+ var convertSilkscreenText = (circuitJson) => {
453
+ const silkscreenTexts = su9(circuitJson).pcb_silkscreen_text.list();
454
+ const elementStrings = [];
455
+ for (const silkscreenText of silkscreenTexts) {
456
+ const pcbX = silkscreenText.anchor_position?.x ?? 0;
457
+ const pcbY = silkscreenText.anchor_position?.y ?? 0;
458
+ elementStrings.push(
459
+ `<silkscreentext pcbX={${pcbX}} pcbY={${pcbY}} anchorAlignment="${silkscreenText.anchor_alignment}" fontSize={${silkscreenText.font_size}} text="${escapeJsxText(silkscreenText.text)}" />`
460
+ );
461
+ }
462
+ return elementStrings;
463
+ };
464
+
465
+ // lib/generate-footprint-tsx/convert-smt-pads.ts
466
+ import { su as su10 } from "@tscircuit/soup-util";
467
+ import { mmStr as mmStr4 } from "@tscircuit/mm";
468
+ var convertSmtPads = (circuitJson) => {
469
+ const smtPads = su10(circuitJson).pcb_smtpad.list();
470
+ const elementStrings = [];
471
+ for (const smtPad of smtPads) {
472
+ if (smtPad.shape === "circle") {
473
+ elementStrings.push(
474
+ `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr4(smtPad.x)}" pcbY="${mmStr4(smtPad.y)}" radius="${mmStr4(smtPad.radius)}" shape="circle" />`
475
+ );
476
+ } else if (smtPad.shape === "rect") {
477
+ elementStrings.push(
478
+ `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr4(smtPad.x)}" pcbY="${mmStr4(smtPad.y)}" width="${mmStr4(smtPad.width)}" height="${mmStr4(smtPad.height)}" shape="rect" />`
479
+ );
480
+ } else if (smtPad.shape === "pill") {
481
+ elementStrings.push(
482
+ `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr4(smtPad.x)}" pcbY="${mmStr4(smtPad.y)}" width="${mmStr4(smtPad.width)}" height="${mmStr4(smtPad.height)}" radius="${mmStr4(smtPad.radius)}" shape="pill" />`
483
+ );
484
+ } else if (smtPad.shape === "polygon") {
485
+ elementStrings.push(
486
+ `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} shape="polygon" points={${JSON.stringify(smtPad.points)}} />`
487
+ );
488
+ } else if (smtPad.shape === "rotated_rect") {
489
+ elementStrings.push(
490
+ `<smtpad portHints={${JSON.stringify(smtPad.port_hints)}} pcbX="${mmStr4(smtPad.x)}" pcbY="${mmStr4(smtPad.y)}" width="${mmStr4(smtPad.width)}" height="${mmStr4(smtPad.height)}" ccwRotation={${smtPad.ccw_rotation}} shape="rotated_rect" />`
491
+ );
429
492
  }
430
- elementStrings.push(`<courtyardcircle ${attrs.join(" ")} />`);
431
493
  }
494
+ return elementStrings;
495
+ };
496
+
497
+ // lib/generate-footprint-tsx.tsx
498
+ var generateFootprintTsx = (circuitJson) => {
499
+ const converters = [
500
+ convertHoles,
501
+ convertPlatedHoles,
502
+ convertSmtPads,
503
+ convertSilkscreen,
504
+ convertFabrication,
505
+ convertSilkscreenText,
506
+ convertCopperText,
507
+ convertCutouts,
508
+ convertNotes,
509
+ convertCourtyard
510
+ ];
511
+ const elementStrings = converters.flatMap((convert) => convert(circuitJson));
432
512
  if (elementStrings.length === 0) {
433
513
  return null;
434
514
  }
@@ -440,15 +520,15 @@ var generateFootprintTsx = (circuitJson) => {
440
520
  };
441
521
 
442
522
  // lib/generate-symbol-tsx.ts
443
- import { su as su2 } from "@tscircuit/soup-util";
523
+ import { su as su11 } from "@tscircuit/soup-util";
444
524
  var generateSymbolTsx = (circuitJson) => {
445
- const schematicArcs = su2(circuitJson).schematic_arc.list();
446
- const schematicLines = su2(circuitJson).schematic_line.list();
447
- const schematicPaths = su2(circuitJson).schematic_path.list();
448
- const schematicTexts = su2(circuitJson).schematic_text.list();
449
- const schematicCircles = su2(circuitJson).schematic_circle.list();
450
- const schematicBoxes = su2(circuitJson).schematic_box.list();
451
- const schematicRects = su2(circuitJson).schematic_rect.list();
525
+ const schematicArcs = su11(circuitJson).schematic_arc.list();
526
+ const schematicLines = su11(circuitJson).schematic_line.list();
527
+ const schematicPaths = su11(circuitJson).schematic_path.list();
528
+ const schematicTexts = su11(circuitJson).schematic_text.list();
529
+ const schematicCircles = su11(circuitJson).schematic_circle.list();
530
+ const schematicBoxes = su11(circuitJson).schematic_box.list();
531
+ const schematicRects = su11(circuitJson).schematic_rect.list();
452
532
  const elementStrings = [];
453
533
  for (const arc of schematicArcs) {
454
534
  const center = arc.center ?? { x: 0, y: 0 };