@tscircuit/cli 0.1.1248 → 0.1.1249

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.
package/dist/cli/main.js CHANGED
@@ -99964,7 +99964,7 @@ var import_perfect_cli = __toESM2(require_dist2(), 1);
99964
99964
  // lib/getVersion.ts
99965
99965
  import { createRequire as createRequire2 } from "node:module";
99966
99966
  // package.json
99967
- var version = "0.1.1247";
99967
+ var version = "0.1.1248";
99968
99968
  var package_default = {
99969
99969
  name: "@tscircuit/cli",
99970
99970
  version,
@@ -99998,7 +99998,7 @@ var package_default = {
99998
99998
  "circuit-json": "^0.0.403",
99999
99999
  "circuit-json-to-bom-csv": "^0.0.7",
100000
100000
  "circuit-json-to-gerber": "^0.0.48",
100001
- "circuit-json-to-kicad": "^0.0.96",
100001
+ "circuit-json-to-kicad": "^0.0.105",
100002
100002
  "circuit-json-to-pnp-csv": "^0.0.7",
100003
100003
  "circuit-json-to-readable-netlist": "^0.0.15",
100004
100004
  "circuit-json-to-spice": "^0.0.10",
@@ -105297,27 +105297,37 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
105297
105297
  y: netLabel.anchor_position?.y ?? netLabel.center?.y ?? 0
105298
105298
  });
105299
105299
  const anchorSide = netLabel.anchor_side || "left";
105300
- const angleMap = {
105301
- left: 0,
105302
- right: 180,
105303
- top: 270,
105304
- bottom: 90
105305
- };
105306
- const angle = angleMap[anchorSide] || 0;
105307
- const justifyMap = {
105308
- left: { horizontal: "left" },
105309
- right: { horizontal: "right" },
105310
- top: { vertical: "top" },
105311
- bottom: { vertical: "bottom" }
105312
- };
105313
- const justify = justifyMap[anchorSide] || {};
105300
+ const orientationMap = {
105301
+ left: {
105302
+ angle: 0,
105303
+ shape: "input",
105304
+ justify: { horizontal: "left" }
105305
+ },
105306
+ right: {
105307
+ angle: 180,
105308
+ shape: "input",
105309
+ justify: { horizontal: "right" }
105310
+ },
105311
+ top: {
105312
+ angle: 270,
105313
+ shape: "input",
105314
+ justify: { horizontal: "right" }
105315
+ },
105316
+ bottom: {
105317
+ angle: 90,
105318
+ shape: "input",
105319
+ justify: { horizontal: "left" }
105320
+ }
105321
+ };
105322
+ const orientation = orientationMap[anchorSide] || orientationMap["left"];
105314
105323
  const effects = this.createTextEffects(1.27, false);
105315
- if (Object.keys(justify).length > 0) {
105316
- effects.justify = new TextEffectsJustify2(justify);
105324
+ if (Object.keys(orientation.justify).length > 0) {
105325
+ effects.justify = new TextEffectsJustify2(orientation.justify);
105317
105326
  }
105318
105327
  const globalLabel = new GlobalLabel({
105319
105328
  value: labelText,
105320
- at: [x, y, angle],
105329
+ shape: orientation.shape,
105330
+ at: [x, y, orientation.angle],
105321
105331
  effects,
105322
105332
  uuid: crypto.randomUUID(),
105323
105333
  fieldsAutoplaced: true
@@ -105905,69 +105915,71 @@ function createTextEffects2(metadataEffects) {
105905
105915
  }
105906
105916
  return new TextEffects7({ font });
105907
105917
  }
105908
- function applyMetadataToFootprint(footprint, metadata, componentName) {
105909
- if (metadata.properties) {
105910
- const newProperties = [];
105911
- const refMeta = metadata.properties.Reference;
105912
- newProperties.push(new Property2({
105913
- key: "Reference",
105914
- value: refMeta?.value ?? componentName,
105915
- position: refMeta?.at ? [
105916
- Number(refMeta.at.x),
105917
- Number(refMeta.at.y),
105918
- Number(refMeta.at.rotation ?? 0)
105919
- ] : [0, -3, 0],
105920
- layer: refMeta?.layer ?? "F.SilkS",
105921
- uuid: generateDeterministicUuid(`${componentName}-property-Reference`),
105922
- effects: createTextEffects2(refMeta?.effects),
105923
- hidden: refMeta?.hide
105924
- }));
105925
- const valMeta = metadata.properties.Value;
105926
- const valueText = valMeta?.value ?? metadata.footprintName ?? "";
105927
- newProperties.push(new Property2({
105928
- key: "Value",
105929
- value: valueText,
105930
- position: valMeta?.at ? [
105931
- Number(valMeta.at.x),
105932
- Number(valMeta.at.y),
105933
- Number(valMeta.at.rotation ?? 0)
105934
- ] : [0, 3, 0],
105935
- layer: valMeta?.layer ?? "F.Fab",
105936
- uuid: generateDeterministicUuid(`${componentName}-property-Value`),
105937
- effects: createTextEffects2(valMeta?.effects),
105938
- hidden: valMeta?.hide
105939
- }));
105940
- const dsMeta = metadata.properties.Datasheet;
105941
- newProperties.push(new Property2({
105942
- key: "Datasheet",
105943
- value: dsMeta?.value ?? "",
105944
- position: dsMeta?.at ? [
105945
- Number(dsMeta.at.x),
105946
- Number(dsMeta.at.y),
105947
- Number(dsMeta.at.rotation ?? 0)
105948
- ] : [0, 0, 0],
105949
- layer: dsMeta?.layer ?? "F.Fab",
105950
- uuid: generateDeterministicUuid(`${componentName}-property-Datasheet`),
105951
- effects: createTextEffects2(dsMeta?.effects),
105952
- hidden: dsMeta?.hide ?? true
105953
- }));
105954
- const descMeta = metadata.properties.Description;
105955
- newProperties.push(new Property2({
105956
- key: "Description",
105957
- value: descMeta?.value ?? "",
105958
- position: descMeta?.at ? [
105959
- Number(descMeta.at.x),
105960
- Number(descMeta.at.y),
105961
- Number(descMeta.at.rotation ?? 0)
105962
- ] : [0, 0, 0],
105963
- layer: descMeta?.layer ?? "F.Fab",
105964
- uuid: generateDeterministicUuid(`${componentName}-property-Description`),
105965
- effects: createTextEffects2(descMeta?.effects),
105966
- hidden: descMeta?.hide ?? true
105967
- }));
105968
- footprint.properties = newProperties;
105969
- }
105970
- if (metadata.attributes) {
105918
+ function applyMetadataToFootprint({
105919
+ footprint,
105920
+ metadata,
105921
+ componentProperty
105922
+ }) {
105923
+ let newProperties = [];
105924
+ const refMeta = metadata?.properties?.Reference;
105925
+ newProperties.push(new Property2({
105926
+ key: "Reference",
105927
+ value: refMeta?.value ?? componentProperty.reference,
105928
+ position: refMeta?.at ? [
105929
+ Number(refMeta.at.x),
105930
+ Number(refMeta.at.y),
105931
+ Number(refMeta.at.rotation ?? 0)
105932
+ ] : [0, -3, 0],
105933
+ layer: refMeta?.layer ?? "F.SilkS",
105934
+ uuid: generateDeterministicUuid(`${componentProperty.reference}-property-Reference`),
105935
+ effects: createTextEffects2(refMeta?.effects),
105936
+ hidden: refMeta?.hide ?? true
105937
+ }));
105938
+ const valMeta = metadata?.properties?.Value;
105939
+ const valueText = valMeta?.value ?? componentProperty.kicadComponentValue ?? "";
105940
+ newProperties.push(new Property2({
105941
+ key: "Value",
105942
+ value: valueText,
105943
+ position: valMeta?.at ? [
105944
+ Number(valMeta.at.x),
105945
+ Number(valMeta.at.y),
105946
+ Number(valMeta.at.rotation ?? 0)
105947
+ ] : [0, 3, 0],
105948
+ layer: valMeta?.layer ?? "F.Fab",
105949
+ uuid: generateDeterministicUuid(`${componentProperty.reference}-property-Value`),
105950
+ effects: createTextEffects2(valMeta?.effects),
105951
+ hidden: valMeta?.hide ?? true
105952
+ }));
105953
+ const dsMeta = metadata?.properties?.Datasheet;
105954
+ newProperties.push(new Property2({
105955
+ key: "Datasheet",
105956
+ value: dsMeta?.value ?? "",
105957
+ position: dsMeta?.at ? [
105958
+ Number(dsMeta.at.x),
105959
+ Number(dsMeta.at.y),
105960
+ Number(dsMeta.at.rotation ?? 0)
105961
+ ] : [0, 0, 0],
105962
+ layer: dsMeta?.layer ?? "F.Fab",
105963
+ uuid: generateDeterministicUuid(`${componentProperty.reference}-property-Datasheet`),
105964
+ effects: createTextEffects2(dsMeta?.effects),
105965
+ hidden: dsMeta?.hide ?? true
105966
+ }));
105967
+ const descMeta = metadata?.properties?.Description;
105968
+ newProperties.push(new Property2({
105969
+ key: "Description",
105970
+ value: descMeta?.value ?? "",
105971
+ position: descMeta?.at ? [
105972
+ Number(descMeta.at.x),
105973
+ Number(descMeta.at.y),
105974
+ Number(descMeta.at.rotation ?? 0)
105975
+ ] : [0, 0, 0],
105976
+ layer: descMeta?.layer ?? "F.Fab",
105977
+ uuid: generateDeterministicUuid(`${componentProperty.reference}-property-Description`),
105978
+ effects: createTextEffects2(descMeta?.effects),
105979
+ hidden: descMeta?.hide ?? true
105980
+ }));
105981
+ footprint.properties = newProperties;
105982
+ if (metadata?.attributes) {
105971
105983
  if (!footprint.attr) {
105972
105984
  footprint.attr = new FootprintAttr2;
105973
105985
  }
@@ -105983,16 +105995,16 @@ function applyMetadataToFootprint(footprint, metadata, componentName) {
105983
105995
  footprint.attr.excludeFromBom = metadata.attributes.exclude_from_bom;
105984
105996
  }
105985
105997
  }
105986
- if (metadata.footprintName) {
105998
+ if (metadata?.footprintName) {
105987
105999
  footprint.libraryLink = metadata.footprintName;
105988
106000
  }
105989
- if (metadata.layer) {
106001
+ if (metadata?.layer) {
105990
106002
  footprint.layer = metadata.layer;
105991
106003
  }
105992
- if (metadata.embeddedFonts !== undefined) {
106004
+ if (metadata?.embeddedFonts !== undefined) {
105993
106005
  footprint.embeddedFonts = new EmbeddedFonts5(metadata.embeddedFonts);
105994
106006
  }
105995
- if (metadata.model) {
106007
+ if (metadata?.model) {
105996
106008
  const model = new FootprintModel2(metadata.model.path);
105997
106009
  if (metadata.model.offset) {
105998
106010
  model.offset = {
@@ -106019,6 +106031,66 @@ function applyMetadataToFootprint(footprint, metadata, componentName) {
106019
106031
  footprint.models = [model, ...existingModels];
106020
106032
  }
106021
106033
  }
106034
+ function getkicadComponentProperty(sourceComp) {
106035
+ const name = sourceComp.name || "?";
106036
+ const reference = getReferenceDesignator(sourceComp);
106037
+ if (sourceComp.ftype === "simple_resistor") {
106038
+ const resistor = sourceComp;
106039
+ return {
106040
+ reference,
106041
+ kicadComponentValue: resistor.display_resistance || "R"
106042
+ };
106043
+ }
106044
+ if (sourceComp.ftype === "simple_capacitor") {
106045
+ const capacitor = sourceComp;
106046
+ return {
106047
+ reference,
106048
+ kicadComponentValue: capacitor.display_capacitance || "C"
106049
+ };
106050
+ }
106051
+ if (sourceComp.ftype === "simple_inductor") {
106052
+ const inductor = sourceComp;
106053
+ return {
106054
+ reference,
106055
+ kicadComponentValue: inductor.display_inductance || "L"
106056
+ };
106057
+ }
106058
+ if (sourceComp.ftype === "simple_diode") {
106059
+ return {
106060
+ reference,
106061
+ kicadComponentValue: "D"
106062
+ };
106063
+ }
106064
+ if (sourceComp.ftype === "simple_chip") {
106065
+ return {
106066
+ reference,
106067
+ kicadComponentValue: name
106068
+ };
106069
+ }
106070
+ if (sourceComp.ftype === "simple_led") {
106071
+ return {
106072
+ reference,
106073
+ kicadComponentValue: sourceComp.manufacturer_part_number || "LED"
106074
+ };
106075
+ }
106076
+ if (sourceComp.ftype === "simple_switch") {
106077
+ return {
106078
+ reference,
106079
+ kicadComponentValue: sourceComp.manufacturer_part_number || "SW"
106080
+ };
106081
+ }
106082
+ if (sourceComp.ftype === "simple_potentiometer") {
106083
+ const potentiometer = sourceComp;
106084
+ return {
106085
+ reference,
106086
+ kicadComponentValue: potentiometer.display_max_resistance || "POT"
106087
+ };
106088
+ }
106089
+ return {
106090
+ reference,
106091
+ kicadComponentValue: name
106092
+ };
106093
+ }
106022
106094
  function convertSilkscreenCircles(silkscreenCircles, componentCenter) {
106023
106095
  const fpCircles = [];
106024
106096
  for (const circle of silkscreenCircles) {
@@ -106165,6 +106237,7 @@ function convertCourtyardOutlines(courtyardOutlines, componentCenter) {
106165
106237
  fpPoly.points = new Pts3(xyPoints);
106166
106238
  fpPoly.layer = kicadLayer;
106167
106239
  fpPoly.fill = false;
106240
+ fpPoly.uuid = generateDeterministicUuid(outline.pcb_courtyard_outline_id);
106168
106241
  const stroke = new Stroke9;
106169
106242
  stroke.width = 0.05;
106170
106243
  stroke.type = "default";
@@ -106541,6 +106614,7 @@ function createNpthPadFromCircuitJson({
106541
106614
  let padShape = "circle";
106542
106615
  let padSize;
106543
106616
  let drill;
106617
+ let rotation = 0;
106544
106618
  if (pcbHole.hole_shape === "circle") {
106545
106619
  padShape = "circle";
106546
106620
  const diameter = pcbHole.hole_diameter;
@@ -106558,6 +106632,17 @@ function createNpthPadFromCircuitJson({
106558
106632
  diameter: width,
106559
106633
  width: height
106560
106634
  });
106635
+ } else if (pcbHole.hole_shape === "rotated_pill") {
106636
+ padShape = "oval";
106637
+ const width = pcbHole.hole_width;
106638
+ const height = pcbHole.hole_height;
106639
+ padSize = [width, height];
106640
+ drill = new PadDrill2({
106641
+ oval: true,
106642
+ diameter: width,
106643
+ width: height
106644
+ });
106645
+ rotation = pcbHole.ccw_rotation || 0;
106561
106646
  } else {
106562
106647
  padShape = "circle";
106563
106648
  const diameter = "hole_diameter" in pcbHole ? pcbHole.hole_diameter : 1;
@@ -106568,7 +106653,7 @@ function createNpthPadFromCircuitJson({
106568
106653
  number: "",
106569
106654
  padType: "np_thru_hole",
106570
106655
  shape: padShape,
106571
- at: [rotatedPos.x, rotatedPos.y, 0],
106656
+ at: [rotatedPos.x, rotatedPos.y, rotation],
106572
106657
  size: padSize,
106573
106658
  drill,
106574
106659
  layers: ["*.Cu", "*.Mask"],
@@ -106725,8 +106810,13 @@ var AddFootprintsStage = class extends ConverterStage {
106725
106810
  }
106726
106811
  }
106727
106812
  const footprintMetadata = component.metadata?.kicad_footprint;
106728
- if (footprintMetadata && sourceComponent?.name) {
106729
- applyMetadataToFootprint(footprint, footprintMetadata, sourceComponent.name);
106813
+ if (sourceComponent) {
106814
+ const kicadComponentProperty = getkicadComponentProperty(sourceComponent);
106815
+ applyMetadataToFootprint({
106816
+ footprint,
106817
+ metadata: footprintMetadata,
106818
+ componentProperty: kicadComponentProperty
106819
+ });
106730
106820
  }
106731
106821
  const footprints = kicadPcb.footprints;
106732
106822
  footprints.push(footprint);
@@ -106831,7 +106921,7 @@ var AddTracesStage = class extends ConverterStage {
106831
106921
  start: { x: transformedStart.x, y: transformedStart.y },
106832
106922
  end: { x: transformedEnd.x, y: transformedEnd.y },
106833
106923
  layer: kicadLayer,
106834
- width: trace.width || 0.25,
106924
+ width: startPoint.width ?? endPoint.width ?? trace.width ?? 0.25,
106835
106925
  net: new SegmentNet(netInfo?.id ?? 0),
106836
106926
  uuid: generateDeterministicUuid(segmentData)
106837
106927
  });
@@ -106856,7 +106946,47 @@ var AddViasStage = class extends ConverterStage {
106856
106946
  pcbVias = [];
106857
106947
  constructor(input, ctx) {
106858
106948
  super(input, ctx);
106859
- this.pcbVias = this.ctx.db.pcb_via?.list() || [];
106949
+ this.pcbVias = this.collectPcbVias();
106950
+ }
106951
+ collectPcbVias() {
106952
+ const standaloneVias = this.ctx.db.pcb_via?.list() || [];
106953
+ const seenViaKeys = new Set(standaloneVias.map((via) => this.getViaDedupeKey(via)));
106954
+ const routeDefinedVias = (this.ctx.db.pcb_trace?.list() || []).flatMap((trace) => (trace.route || []).filter((point) => point.route_type === "via").map((point) => ({
106955
+ x: point.x,
106956
+ y: point.y,
106957
+ outer_diameter: point.outer_diameter,
106958
+ hole_diameter: point.hole_diameter,
106959
+ from_layer: point.from_layer,
106960
+ to_layer: point.to_layer,
106961
+ pcb_trace_id: trace.pcb_trace_id,
106962
+ subcircuit_connectivity_map_key: trace.subcircuit_connectivity_map_key,
106963
+ connection_name: trace.connection_name
106964
+ })).filter((via) => {
106965
+ const viaKey = this.getViaDedupeKey(via);
106966
+ if (seenViaKeys.has(viaKey)) {
106967
+ return false;
106968
+ }
106969
+ seenViaKeys.add(viaKey);
106970
+ return true;
106971
+ }));
106972
+ return [...standaloneVias, ...routeDefinedVias];
106973
+ }
106974
+ getViaDedupeKey(via) {
106975
+ const layers = this.getRawViaLayers(via).sort().join(",");
106976
+ return `${via.pcb_trace_id ?? ""}:${via.x}:${via.y}:${layers}`;
106977
+ }
106978
+ getRawViaLayers(via) {
106979
+ if (via.layers?.length) {
106980
+ return [...via.layers];
106981
+ }
106982
+ return [via.from_layer, via.to_layer].filter((layer) => Boolean(layer));
106983
+ }
106984
+ getKicadViaLayers(via) {
106985
+ const rawLayers = this.getRawViaLayers(via);
106986
+ if (rawLayers.length > 0) {
106987
+ return rawLayers.map((layer) => getKicadLayer(layer));
106988
+ }
106989
+ return getViaLayers(this.ctx.numLayers ?? 2);
106860
106990
  }
106861
106991
  _step() {
106862
106992
  const { kicadPcb, c2kMatPcb, pcbNetMap } = this.ctx;
@@ -106871,6 +107001,10 @@ var AddViasStage = class extends ConverterStage {
106871
107001
  return;
106872
107002
  }
106873
107003
  const via = this.pcbVias[this.viasProcessed];
107004
+ if (!via) {
107005
+ this.finished = true;
107006
+ return;
107007
+ }
106874
107008
  const transformedPos = applyToPoint15(c2kMatPcb, {
106875
107009
  x: via.x,
106876
107010
  y: via.y
@@ -106900,6 +107034,12 @@ var AddViasStage = class extends ConverterStage {
106900
107034
  }
106901
107035
  }
106902
107036
  }
107037
+ if (!connectivityKey) {
107038
+ const sourceNet = this.ctx.db.source_net?.get(pcbTrace.source_trace_id);
107039
+ if (sourceNet?.subcircuit_connectivity_map_key) {
107040
+ connectivityKey = sourceNet.subcircuit_connectivity_map_key;
107041
+ }
107042
+ }
106903
107043
  }
106904
107044
  }
106905
107045
  }
@@ -106913,13 +107053,14 @@ var AddViasStage = class extends ConverterStage {
106913
107053
  netInfo = pcbNetMap.get(connectivityKey);
106914
107054
  }
106915
107055
  }
106916
- const numLayers = this.ctx.numLayers ?? 2;
106917
- const viaLayers = via.layers ? via.layers.map((l) => l === "top" ? "F.Cu" : l === "bottom" ? "B.Cu" : `In${l.replace("inner", "")}.Cu`) : getViaLayers(numLayers);
106918
- const viaData = `via:${transformedPos.x},${transformedPos.y}:${via.outer_diameter || 0.8}:${via.hole_diameter || 0.4}:${netInfo?.id ?? 0}`;
107056
+ const viaLayers = this.getKicadViaLayers(via);
107057
+ const viaSize = via.outer_diameter ?? 0.8;
107058
+ const viaDrill = via.hole_diameter ?? 0.4;
107059
+ const viaData = `via:${transformedPos.x},${transformedPos.y}:${viaSize}:${viaDrill}:${netInfo?.id ?? 0}`;
106919
107060
  const kicadVia = new Via({
106920
107061
  at: [transformedPos.x, transformedPos.y],
106921
- size: via.outer_diameter || 0.8,
106922
- drill: via.hole_diameter || 0.4,
107062
+ size: viaSize,
107063
+ drill: viaDrill,
106923
107064
  layers: viaLayers,
106924
107065
  net: new ViaNet(netInfo?.id ?? 0),
106925
107066
  uuid: generateDeterministicUuid(viaData)
@@ -107008,6 +107149,9 @@ var AddStandalonePcbElements = class extends ConverterStage {
107008
107149
  const h = hole;
107009
107150
  return `tscircuit:hole_${shape}_holeWidth${h.hole_width}mm_holeHeight${h.hole_height}mm`;
107010
107151
  }
107152
+ if (shape === "rotated_pill") {
107153
+ return `tscircuit:hole_${shape}_holeWidth${hole.hole_width}mm_holeHeight${hole.hole_height}mm_ccwRotation${hole.ccw_rotation}deg`;
107154
+ }
107011
107155
  return "tscircuit:hole";
107012
107156
  }
107013
107157
  getPlatedHoleLibraryLink(hole) {
package/dist/lib/index.js CHANGED
@@ -60678,7 +60678,7 @@ var getNodeHandler = (winterSpec, { port, middleware = [] }) => {
60678
60678
  }));
60679
60679
  };
60680
60680
  // package.json
60681
- var version = "0.1.1247";
60681
+ var version = "0.1.1248";
60682
60682
  var package_default = {
60683
60683
  name: "@tscircuit/cli",
60684
60684
  version,
@@ -60712,7 +60712,7 @@ var package_default = {
60712
60712
  "circuit-json": "^0.0.403",
60713
60713
  "circuit-json-to-bom-csv": "^0.0.7",
60714
60714
  "circuit-json-to-gerber": "^0.0.48",
60715
- "circuit-json-to-kicad": "^0.0.96",
60715
+ "circuit-json-to-kicad": "^0.0.105",
60716
60716
  "circuit-json-to-pnp-csv": "^0.0.7",
60717
60717
  "circuit-json-to-readable-netlist": "^0.0.15",
60718
60718
  "circuit-json-to-spice": "^0.0.10",
@@ -64706,27 +64706,37 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
64706
64706
  y: netLabel.anchor_position?.y ?? netLabel.center?.y ?? 0
64707
64707
  });
64708
64708
  const anchorSide = netLabel.anchor_side || "left";
64709
- const angleMap = {
64710
- left: 0,
64711
- right: 180,
64712
- top: 270,
64713
- bottom: 90
64714
- };
64715
- const angle = angleMap[anchorSide] || 0;
64716
- const justifyMap = {
64717
- left: { horizontal: "left" },
64718
- right: { horizontal: "right" },
64719
- top: { vertical: "top" },
64720
- bottom: { vertical: "bottom" }
64709
+ const orientationMap = {
64710
+ left: {
64711
+ angle: 0,
64712
+ shape: "input",
64713
+ justify: { horizontal: "left" }
64714
+ },
64715
+ right: {
64716
+ angle: 180,
64717
+ shape: "input",
64718
+ justify: { horizontal: "right" }
64719
+ },
64720
+ top: {
64721
+ angle: 270,
64722
+ shape: "input",
64723
+ justify: { horizontal: "right" }
64724
+ },
64725
+ bottom: {
64726
+ angle: 90,
64727
+ shape: "input",
64728
+ justify: { horizontal: "left" }
64729
+ }
64721
64730
  };
64722
- const justify = justifyMap[anchorSide] || {};
64731
+ const orientation = orientationMap[anchorSide] || orientationMap["left"];
64723
64732
  const effects = this.createTextEffects(1.27, false);
64724
- if (Object.keys(justify).length > 0) {
64725
- effects.justify = new TextEffectsJustify2(justify);
64733
+ if (Object.keys(orientation.justify).length > 0) {
64734
+ effects.justify = new TextEffectsJustify2(orientation.justify);
64726
64735
  }
64727
64736
  const globalLabel = new GlobalLabel({
64728
64737
  value: labelText,
64729
- at: [x, y, angle],
64738
+ shape: orientation.shape,
64739
+ at: [x, y, orientation.angle],
64730
64740
  effects,
64731
64741
  uuid: crypto.randomUUID(),
64732
64742
  fieldsAutoplaced: true
@@ -65314,69 +65324,71 @@ function createTextEffects2(metadataEffects) {
65314
65324
  }
65315
65325
  return new TextEffects7({ font });
65316
65326
  }
65317
- function applyMetadataToFootprint(footprint, metadata, componentName) {
65318
- if (metadata.properties) {
65319
- const newProperties = [];
65320
- const refMeta = metadata.properties.Reference;
65321
- newProperties.push(new Property2({
65322
- key: "Reference",
65323
- value: refMeta?.value ?? componentName,
65324
- position: refMeta?.at ? [
65325
- Number(refMeta.at.x),
65326
- Number(refMeta.at.y),
65327
- Number(refMeta.at.rotation ?? 0)
65328
- ] : [0, -3, 0],
65329
- layer: refMeta?.layer ?? "F.SilkS",
65330
- uuid: generateDeterministicUuid(`${componentName}-property-Reference`),
65331
- effects: createTextEffects2(refMeta?.effects),
65332
- hidden: refMeta?.hide
65333
- }));
65334
- const valMeta = metadata.properties.Value;
65335
- const valueText = valMeta?.value ?? metadata.footprintName ?? "";
65336
- newProperties.push(new Property2({
65337
- key: "Value",
65338
- value: valueText,
65339
- position: valMeta?.at ? [
65340
- Number(valMeta.at.x),
65341
- Number(valMeta.at.y),
65342
- Number(valMeta.at.rotation ?? 0)
65343
- ] : [0, 3, 0],
65344
- layer: valMeta?.layer ?? "F.Fab",
65345
- uuid: generateDeterministicUuid(`${componentName}-property-Value`),
65346
- effects: createTextEffects2(valMeta?.effects),
65347
- hidden: valMeta?.hide
65348
- }));
65349
- const dsMeta = metadata.properties.Datasheet;
65350
- newProperties.push(new Property2({
65351
- key: "Datasheet",
65352
- value: dsMeta?.value ?? "",
65353
- position: dsMeta?.at ? [
65354
- Number(dsMeta.at.x),
65355
- Number(dsMeta.at.y),
65356
- Number(dsMeta.at.rotation ?? 0)
65357
- ] : [0, 0, 0],
65358
- layer: dsMeta?.layer ?? "F.Fab",
65359
- uuid: generateDeterministicUuid(`${componentName}-property-Datasheet`),
65360
- effects: createTextEffects2(dsMeta?.effects),
65361
- hidden: dsMeta?.hide ?? true
65362
- }));
65363
- const descMeta = metadata.properties.Description;
65364
- newProperties.push(new Property2({
65365
- key: "Description",
65366
- value: descMeta?.value ?? "",
65367
- position: descMeta?.at ? [
65368
- Number(descMeta.at.x),
65369
- Number(descMeta.at.y),
65370
- Number(descMeta.at.rotation ?? 0)
65371
- ] : [0, 0, 0],
65372
- layer: descMeta?.layer ?? "F.Fab",
65373
- uuid: generateDeterministicUuid(`${componentName}-property-Description`),
65374
- effects: createTextEffects2(descMeta?.effects),
65375
- hidden: descMeta?.hide ?? true
65376
- }));
65377
- footprint.properties = newProperties;
65378
- }
65379
- if (metadata.attributes) {
65327
+ function applyMetadataToFootprint({
65328
+ footprint,
65329
+ metadata,
65330
+ componentProperty
65331
+ }) {
65332
+ let newProperties = [];
65333
+ const refMeta = metadata?.properties?.Reference;
65334
+ newProperties.push(new Property2({
65335
+ key: "Reference",
65336
+ value: refMeta?.value ?? componentProperty.reference,
65337
+ position: refMeta?.at ? [
65338
+ Number(refMeta.at.x),
65339
+ Number(refMeta.at.y),
65340
+ Number(refMeta.at.rotation ?? 0)
65341
+ ] : [0, -3, 0],
65342
+ layer: refMeta?.layer ?? "F.SilkS",
65343
+ uuid: generateDeterministicUuid(`${componentProperty.reference}-property-Reference`),
65344
+ effects: createTextEffects2(refMeta?.effects),
65345
+ hidden: refMeta?.hide ?? true
65346
+ }));
65347
+ const valMeta = metadata?.properties?.Value;
65348
+ const valueText = valMeta?.value ?? componentProperty.kicadComponentValue ?? "";
65349
+ newProperties.push(new Property2({
65350
+ key: "Value",
65351
+ value: valueText,
65352
+ position: valMeta?.at ? [
65353
+ Number(valMeta.at.x),
65354
+ Number(valMeta.at.y),
65355
+ Number(valMeta.at.rotation ?? 0)
65356
+ ] : [0, 3, 0],
65357
+ layer: valMeta?.layer ?? "F.Fab",
65358
+ uuid: generateDeterministicUuid(`${componentProperty.reference}-property-Value`),
65359
+ effects: createTextEffects2(valMeta?.effects),
65360
+ hidden: valMeta?.hide ?? true
65361
+ }));
65362
+ const dsMeta = metadata?.properties?.Datasheet;
65363
+ newProperties.push(new Property2({
65364
+ key: "Datasheet",
65365
+ value: dsMeta?.value ?? "",
65366
+ position: dsMeta?.at ? [
65367
+ Number(dsMeta.at.x),
65368
+ Number(dsMeta.at.y),
65369
+ Number(dsMeta.at.rotation ?? 0)
65370
+ ] : [0, 0, 0],
65371
+ layer: dsMeta?.layer ?? "F.Fab",
65372
+ uuid: generateDeterministicUuid(`${componentProperty.reference}-property-Datasheet`),
65373
+ effects: createTextEffects2(dsMeta?.effects),
65374
+ hidden: dsMeta?.hide ?? true
65375
+ }));
65376
+ const descMeta = metadata?.properties?.Description;
65377
+ newProperties.push(new Property2({
65378
+ key: "Description",
65379
+ value: descMeta?.value ?? "",
65380
+ position: descMeta?.at ? [
65381
+ Number(descMeta.at.x),
65382
+ Number(descMeta.at.y),
65383
+ Number(descMeta.at.rotation ?? 0)
65384
+ ] : [0, 0, 0],
65385
+ layer: descMeta?.layer ?? "F.Fab",
65386
+ uuid: generateDeterministicUuid(`${componentProperty.reference}-property-Description`),
65387
+ effects: createTextEffects2(descMeta?.effects),
65388
+ hidden: descMeta?.hide ?? true
65389
+ }));
65390
+ footprint.properties = newProperties;
65391
+ if (metadata?.attributes) {
65380
65392
  if (!footprint.attr) {
65381
65393
  footprint.attr = new FootprintAttr2;
65382
65394
  }
@@ -65392,16 +65404,16 @@ function applyMetadataToFootprint(footprint, metadata, componentName) {
65392
65404
  footprint.attr.excludeFromBom = metadata.attributes.exclude_from_bom;
65393
65405
  }
65394
65406
  }
65395
- if (metadata.footprintName) {
65407
+ if (metadata?.footprintName) {
65396
65408
  footprint.libraryLink = metadata.footprintName;
65397
65409
  }
65398
- if (metadata.layer) {
65410
+ if (metadata?.layer) {
65399
65411
  footprint.layer = metadata.layer;
65400
65412
  }
65401
- if (metadata.embeddedFonts !== undefined) {
65413
+ if (metadata?.embeddedFonts !== undefined) {
65402
65414
  footprint.embeddedFonts = new EmbeddedFonts5(metadata.embeddedFonts);
65403
65415
  }
65404
- if (metadata.model) {
65416
+ if (metadata?.model) {
65405
65417
  const model = new FootprintModel2(metadata.model.path);
65406
65418
  if (metadata.model.offset) {
65407
65419
  model.offset = {
@@ -65428,6 +65440,66 @@ function applyMetadataToFootprint(footprint, metadata, componentName) {
65428
65440
  footprint.models = [model, ...existingModels];
65429
65441
  }
65430
65442
  }
65443
+ function getkicadComponentProperty(sourceComp) {
65444
+ const name = sourceComp.name || "?";
65445
+ const reference = getReferenceDesignator(sourceComp);
65446
+ if (sourceComp.ftype === "simple_resistor") {
65447
+ const resistor = sourceComp;
65448
+ return {
65449
+ reference,
65450
+ kicadComponentValue: resistor.display_resistance || "R"
65451
+ };
65452
+ }
65453
+ if (sourceComp.ftype === "simple_capacitor") {
65454
+ const capacitor = sourceComp;
65455
+ return {
65456
+ reference,
65457
+ kicadComponentValue: capacitor.display_capacitance || "C"
65458
+ };
65459
+ }
65460
+ if (sourceComp.ftype === "simple_inductor") {
65461
+ const inductor = sourceComp;
65462
+ return {
65463
+ reference,
65464
+ kicadComponentValue: inductor.display_inductance || "L"
65465
+ };
65466
+ }
65467
+ if (sourceComp.ftype === "simple_diode") {
65468
+ return {
65469
+ reference,
65470
+ kicadComponentValue: "D"
65471
+ };
65472
+ }
65473
+ if (sourceComp.ftype === "simple_chip") {
65474
+ return {
65475
+ reference,
65476
+ kicadComponentValue: name
65477
+ };
65478
+ }
65479
+ if (sourceComp.ftype === "simple_led") {
65480
+ return {
65481
+ reference,
65482
+ kicadComponentValue: sourceComp.manufacturer_part_number || "LED"
65483
+ };
65484
+ }
65485
+ if (sourceComp.ftype === "simple_switch") {
65486
+ return {
65487
+ reference,
65488
+ kicadComponentValue: sourceComp.manufacturer_part_number || "SW"
65489
+ };
65490
+ }
65491
+ if (sourceComp.ftype === "simple_potentiometer") {
65492
+ const potentiometer = sourceComp;
65493
+ return {
65494
+ reference,
65495
+ kicadComponentValue: potentiometer.display_max_resistance || "POT"
65496
+ };
65497
+ }
65498
+ return {
65499
+ reference,
65500
+ kicadComponentValue: name
65501
+ };
65502
+ }
65431
65503
  function convertSilkscreenCircles(silkscreenCircles, componentCenter) {
65432
65504
  const fpCircles = [];
65433
65505
  for (const circle of silkscreenCircles) {
@@ -65574,6 +65646,7 @@ function convertCourtyardOutlines(courtyardOutlines, componentCenter) {
65574
65646
  fpPoly.points = new Pts3(xyPoints);
65575
65647
  fpPoly.layer = kicadLayer;
65576
65648
  fpPoly.fill = false;
65649
+ fpPoly.uuid = generateDeterministicUuid(outline.pcb_courtyard_outline_id);
65577
65650
  const stroke = new Stroke9;
65578
65651
  stroke.width = 0.05;
65579
65652
  stroke.type = "default";
@@ -65950,6 +66023,7 @@ function createNpthPadFromCircuitJson({
65950
66023
  let padShape = "circle";
65951
66024
  let padSize;
65952
66025
  let drill;
66026
+ let rotation = 0;
65953
66027
  if (pcbHole.hole_shape === "circle") {
65954
66028
  padShape = "circle";
65955
66029
  const diameter = pcbHole.hole_diameter;
@@ -65967,6 +66041,17 @@ function createNpthPadFromCircuitJson({
65967
66041
  diameter: width,
65968
66042
  width: height
65969
66043
  });
66044
+ } else if (pcbHole.hole_shape === "rotated_pill") {
66045
+ padShape = "oval";
66046
+ const width = pcbHole.hole_width;
66047
+ const height = pcbHole.hole_height;
66048
+ padSize = [width, height];
66049
+ drill = new PadDrill2({
66050
+ oval: true,
66051
+ diameter: width,
66052
+ width: height
66053
+ });
66054
+ rotation = pcbHole.ccw_rotation || 0;
65970
66055
  } else {
65971
66056
  padShape = "circle";
65972
66057
  const diameter = "hole_diameter" in pcbHole ? pcbHole.hole_diameter : 1;
@@ -65977,7 +66062,7 @@ function createNpthPadFromCircuitJson({
65977
66062
  number: "",
65978
66063
  padType: "np_thru_hole",
65979
66064
  shape: padShape,
65980
- at: [rotatedPos.x, rotatedPos.y, 0],
66065
+ at: [rotatedPos.x, rotatedPos.y, rotation],
65981
66066
  size: padSize,
65982
66067
  drill,
65983
66068
  layers: ["*.Cu", "*.Mask"],
@@ -66134,8 +66219,13 @@ var AddFootprintsStage = class extends ConverterStage {
66134
66219
  }
66135
66220
  }
66136
66221
  const footprintMetadata = component.metadata?.kicad_footprint;
66137
- if (footprintMetadata && sourceComponent?.name) {
66138
- applyMetadataToFootprint(footprint, footprintMetadata, sourceComponent.name);
66222
+ if (sourceComponent) {
66223
+ const kicadComponentProperty = getkicadComponentProperty(sourceComponent);
66224
+ applyMetadataToFootprint({
66225
+ footprint,
66226
+ metadata: footprintMetadata,
66227
+ componentProperty: kicadComponentProperty
66228
+ });
66139
66229
  }
66140
66230
  const footprints = kicadPcb.footprints;
66141
66231
  footprints.push(footprint);
@@ -66240,7 +66330,7 @@ var AddTracesStage = class extends ConverterStage {
66240
66330
  start: { x: transformedStart.x, y: transformedStart.y },
66241
66331
  end: { x: transformedEnd.x, y: transformedEnd.y },
66242
66332
  layer: kicadLayer,
66243
- width: trace.width || 0.25,
66333
+ width: startPoint.width ?? endPoint.width ?? trace.width ?? 0.25,
66244
66334
  net: new SegmentNet(netInfo?.id ?? 0),
66245
66335
  uuid: generateDeterministicUuid(segmentData)
66246
66336
  });
@@ -66265,7 +66355,47 @@ var AddViasStage = class extends ConverterStage {
66265
66355
  pcbVias = [];
66266
66356
  constructor(input, ctx) {
66267
66357
  super(input, ctx);
66268
- this.pcbVias = this.ctx.db.pcb_via?.list() || [];
66358
+ this.pcbVias = this.collectPcbVias();
66359
+ }
66360
+ collectPcbVias() {
66361
+ const standaloneVias = this.ctx.db.pcb_via?.list() || [];
66362
+ const seenViaKeys = new Set(standaloneVias.map((via) => this.getViaDedupeKey(via)));
66363
+ const routeDefinedVias = (this.ctx.db.pcb_trace?.list() || []).flatMap((trace) => (trace.route || []).filter((point) => point.route_type === "via").map((point) => ({
66364
+ x: point.x,
66365
+ y: point.y,
66366
+ outer_diameter: point.outer_diameter,
66367
+ hole_diameter: point.hole_diameter,
66368
+ from_layer: point.from_layer,
66369
+ to_layer: point.to_layer,
66370
+ pcb_trace_id: trace.pcb_trace_id,
66371
+ subcircuit_connectivity_map_key: trace.subcircuit_connectivity_map_key,
66372
+ connection_name: trace.connection_name
66373
+ })).filter((via) => {
66374
+ const viaKey = this.getViaDedupeKey(via);
66375
+ if (seenViaKeys.has(viaKey)) {
66376
+ return false;
66377
+ }
66378
+ seenViaKeys.add(viaKey);
66379
+ return true;
66380
+ }));
66381
+ return [...standaloneVias, ...routeDefinedVias];
66382
+ }
66383
+ getViaDedupeKey(via) {
66384
+ const layers = this.getRawViaLayers(via).sort().join(",");
66385
+ return `${via.pcb_trace_id ?? ""}:${via.x}:${via.y}:${layers}`;
66386
+ }
66387
+ getRawViaLayers(via) {
66388
+ if (via.layers?.length) {
66389
+ return [...via.layers];
66390
+ }
66391
+ return [via.from_layer, via.to_layer].filter((layer) => Boolean(layer));
66392
+ }
66393
+ getKicadViaLayers(via) {
66394
+ const rawLayers = this.getRawViaLayers(via);
66395
+ if (rawLayers.length > 0) {
66396
+ return rawLayers.map((layer) => getKicadLayer(layer));
66397
+ }
66398
+ return getViaLayers(this.ctx.numLayers ?? 2);
66269
66399
  }
66270
66400
  _step() {
66271
66401
  const { kicadPcb, c2kMatPcb, pcbNetMap } = this.ctx;
@@ -66280,6 +66410,10 @@ var AddViasStage = class extends ConverterStage {
66280
66410
  return;
66281
66411
  }
66282
66412
  const via = this.pcbVias[this.viasProcessed];
66413
+ if (!via) {
66414
+ this.finished = true;
66415
+ return;
66416
+ }
66283
66417
  const transformedPos = applyToPoint15(c2kMatPcb, {
66284
66418
  x: via.x,
66285
66419
  y: via.y
@@ -66309,6 +66443,12 @@ var AddViasStage = class extends ConverterStage {
66309
66443
  }
66310
66444
  }
66311
66445
  }
66446
+ if (!connectivityKey) {
66447
+ const sourceNet = this.ctx.db.source_net?.get(pcbTrace.source_trace_id);
66448
+ if (sourceNet?.subcircuit_connectivity_map_key) {
66449
+ connectivityKey = sourceNet.subcircuit_connectivity_map_key;
66450
+ }
66451
+ }
66312
66452
  }
66313
66453
  }
66314
66454
  }
@@ -66322,13 +66462,14 @@ var AddViasStage = class extends ConverterStage {
66322
66462
  netInfo = pcbNetMap.get(connectivityKey);
66323
66463
  }
66324
66464
  }
66325
- const numLayers = this.ctx.numLayers ?? 2;
66326
- const viaLayers = via.layers ? via.layers.map((l) => l === "top" ? "F.Cu" : l === "bottom" ? "B.Cu" : `In${l.replace("inner", "")}.Cu`) : getViaLayers(numLayers);
66327
- const viaData = `via:${transformedPos.x},${transformedPos.y}:${via.outer_diameter || 0.8}:${via.hole_diameter || 0.4}:${netInfo?.id ?? 0}`;
66465
+ const viaLayers = this.getKicadViaLayers(via);
66466
+ const viaSize = via.outer_diameter ?? 0.8;
66467
+ const viaDrill = via.hole_diameter ?? 0.4;
66468
+ const viaData = `via:${transformedPos.x},${transformedPos.y}:${viaSize}:${viaDrill}:${netInfo?.id ?? 0}`;
66328
66469
  const kicadVia = new Via({
66329
66470
  at: [transformedPos.x, transformedPos.y],
66330
- size: via.outer_diameter || 0.8,
66331
- drill: via.hole_diameter || 0.4,
66471
+ size: viaSize,
66472
+ drill: viaDrill,
66332
66473
  layers: viaLayers,
66333
66474
  net: new ViaNet(netInfo?.id ?? 0),
66334
66475
  uuid: generateDeterministicUuid(viaData)
@@ -66417,6 +66558,9 @@ var AddStandalonePcbElements = class extends ConverterStage {
66417
66558
  const h = hole;
66418
66559
  return `tscircuit:hole_${shape}_holeWidth${h.hole_width}mm_holeHeight${h.hole_height}mm`;
66419
66560
  }
66561
+ if (shape === "rotated_pill") {
66562
+ return `tscircuit:hole_${shape}_holeWidth${hole.hole_width}mm_holeHeight${hole.hole_height}mm_ccwRotation${hole.ccw_rotation}deg`;
66563
+ }
66420
66564
  return "tscircuit:hole";
66421
66565
  }
66422
66566
  getPlatedHoleLibraryLink(hole) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/cli",
3
- "version": "0.1.1248",
3
+ "version": "0.1.1249",
4
4
  "main": "dist/cli/main.js",
5
5
  "exports": {
6
6
  ".": "./dist/cli/main.js",
@@ -31,7 +31,7 @@
31
31
  "circuit-json": "^0.0.403",
32
32
  "circuit-json-to-bom-csv": "^0.0.7",
33
33
  "circuit-json-to-gerber": "^0.0.48",
34
- "circuit-json-to-kicad": "^0.0.96",
34
+ "circuit-json-to-kicad": "^0.0.105",
35
35
  "circuit-json-to-pnp-csv": "^0.0.7",
36
36
  "circuit-json-to-readable-netlist": "^0.0.15",
37
37
  "circuit-json-to-spice": "^0.0.10",