@tscircuit/core 0.0.1179 → 0.0.1181

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/index.d.ts CHANGED
@@ -1647,10 +1647,10 @@ declare abstract class PrimitiveComponent<ZodProps extends ZodType = any> extend
1647
1647
  */
1648
1648
  getSelectableDescendants(): PrimitiveComponent[];
1649
1649
  /**
1650
- * Return the number of pins in this component, this is important for
1650
+ * Return the number of primary pins in this component, this is important for
1651
1651
  * NormalComponents
1652
1652
  */
1653
- _getPinCount(): number;
1653
+ _getPrimaryPinCount(): number;
1654
1654
  /**
1655
1655
  * If this component represents a SchematicBox (like a Chip), return the
1656
1656
  * dimensions of the box, which allows computing the position of ports etc.
@@ -1704,6 +1704,8 @@ declare class Port extends PrimitiveComponent<typeof portProps> {
1704
1704
  schematic_stem_line_id: string | null;
1705
1705
  schematicSymbolPortDef: SchSymbol["ports"][number] | null;
1706
1706
  matchedComponents: PrimitiveComponent[];
1707
+ _isPrimaryPort: boolean;
1708
+ _primaryPinNumber: number | null;
1707
1709
  facingDirection: "up" | "down" | "left" | "right" | null;
1708
1710
  originDescription: string | null;
1709
1711
  get config(): {
@@ -1873,6 +1875,7 @@ declare class NormalComponent<ZodProps extends z.ZodType = any, PortNames extend
1873
1875
  _asyncSupplierPartNumbers?: SupplierPartNumbers;
1874
1876
  _asyncFootprintCadModel?: CadModelProp;
1875
1877
  _isCadModelChild?: boolean;
1878
+ _inferredInternallyConnectedPinNames: string[][];
1876
1879
  pcb_missing_footprint_error_id?: string;
1877
1880
  _hasStartedFootprintUrlLoad: boolean;
1878
1881
  private _invalidFootprintPropMessages;
@@ -1966,6 +1969,7 @@ declare class NormalComponent<ZodProps extends z.ZodType = any, PortNames extend
1966
1969
  add(componentOrElm: PrimitiveComponent | ReactElement): void;
1967
1970
  getPortsFromFootprint(opts?: {
1968
1971
  additionalAliases?: Record<string, string[]>;
1972
+ collectInferredInternallyConnectedPins?: boolean;
1969
1973
  }): Port[];
1970
1974
  private _queueInvalidFootprintPropMessage;
1971
1975
  private _insertInvalidFootprintPropErrors;
@@ -1988,7 +1992,7 @@ declare class NormalComponent<ZodProps extends z.ZodType = any, PortNames extend
1988
1992
  height: number;
1989
1993
  };
1990
1994
  _getPinCountFromSchematicPortArrangement(): number;
1991
- _getPinCount(): number;
1995
+ _getPrimaryPinCount(): number;
1992
1996
  /**
1993
1997
  * Override the schematic port arrangement if you want to customize where pins
1994
1998
  * appear on a schematic box, e.g. for a pin header
package/dist/index.js CHANGED
@@ -2038,10 +2038,10 @@ var PrimitiveComponent2 = class extends Renderable {
2038
2038
  return descendants;
2039
2039
  }
2040
2040
  /**
2041
- * Return the number of pins in this component, this is important for
2041
+ * Return the number of primary pins in this component, this is important for
2042
2042
  * NormalComponents
2043
2043
  */
2044
- _getPinCount() {
2044
+ _getPrimaryPinCount() {
2045
2045
  return 0;
2046
2046
  }
2047
2047
  /**
@@ -3948,9 +3948,36 @@ function formatPcbPrimitiveForError(component) {
3948
3948
  return `<${component.lowercaseComponentName} />`;
3949
3949
  }
3950
3950
  function getPcbSelectorErrorForTracePort(selector, port) {
3951
- if (port.pcb_port_id) return null;
3952
3951
  const pcbMatches = port.matchedComponents.filter((c) => c.isPcbPrimitive);
3953
3952
  const parentName = port.getParentNormalComponent()?.props.name ?? port.parent?.props.name ?? "unknown";
3953
+ const parentNormalComponent = port.getParentNormalComponent();
3954
+ const selectorToken = selector.trim().split(/\s+|>/).filter(Boolean).at(-1)?.replace(/^port\./, "").replace(/^\./, "");
3955
+ if (selectorToken && !/^(pin)?\d+$/.test(selectorToken)) {
3956
+ const siblingPorts = (parentNormalComponent?.children ?? []).filter(
3957
+ (child) => child.componentName === "Port" && child.isMatchingAnyOf([selectorToken])
3958
+ );
3959
+ const internallyConnectedGroups = parentNormalComponent?._getInternallyConnectedPins?.() ?? [];
3960
+ const matchingPortsAreInternallyConnected = internallyConnectedGroups.some(
3961
+ (group) => siblingPorts.every((siblingPort) => group.includes(siblingPort))
3962
+ );
3963
+ if (siblingPorts.length > 1 && !matchingPortsAreInternallyConnected) {
3964
+ const rawPinSelectors2 = Array.from(
3965
+ new Set(
3966
+ siblingPorts.flatMap(
3967
+ (siblingPort) => siblingPort.getNameAndAliases().filter((alias) => /^pin\d+$/.test(alias)).map((alias) => `${parentName}.${alias}`)
3968
+ )
3969
+ )
3970
+ );
3971
+ const suggestion = rawPinSelectors2.length > 0 ? ` Use a raw pin selector like ${rawPinSelectors2.map((s) => `"${s}"`).join(" or ")}.` : "";
3972
+ const siblingPcbMatches = siblingPorts.flatMap(
3973
+ (siblingPort) => siblingPort.matchedComponents.filter(
3974
+ (component) => component.isPcbPrimitive
3975
+ )
3976
+ );
3977
+ return `Trace selector "${selector}" resolved to "${parentName}.${port.props.name}", but alias "${selectorToken}" matches multiple PCB pads: ${siblingPcbMatches.map((component) => formatPcbPrimitiveForError(component)).join(", ")}.${suggestion}`;
3978
+ }
3979
+ }
3980
+ if (port.pcb_port_id) return null;
3954
3981
  const rawPinSelectors = Array.from(
3955
3982
  new Set(
3956
3983
  port.getNameAndAliases().filter((alias) => /^pin\d+$/.test(alias)).map((alias) => `${parentName}.${alias}`)
@@ -6752,6 +6779,81 @@ var PcbTrace = class extends PrimitiveComponent2 {
6752
6779
  import { platedHoleProps } from "@tscircuit/props";
6753
6780
  import "circuit-json";
6754
6781
  import { decomposeTSR as decomposeTSR3 } from "transformation-matrix";
6782
+
6783
+ // lib/components/primitive-components/Port/pcbPrimitiveOverlapBeforeRender.ts
6784
+ function getPcbPrimitiveBoundsBeforeRender(primitive) {
6785
+ try {
6786
+ const center = primitive._getGlobalPcbPositionBeforeLayout();
6787
+ const size = primitive.getPcbSize();
6788
+ return {
6789
+ left: center.x - size.width / 2,
6790
+ right: center.x + size.width / 2,
6791
+ top: center.y + size.height / 2,
6792
+ bottom: center.y - size.height / 2
6793
+ };
6794
+ } catch {
6795
+ return null;
6796
+ }
6797
+ }
6798
+ function doPcbPrimitivesOverlapBeforeRender(a, b) {
6799
+ const aBounds = getPcbPrimitiveBoundsBeforeRender(a);
6800
+ const bBounds = getPcbPrimitiveBoundsBeforeRender(b);
6801
+ if (!aBounds || !bBounds) return false;
6802
+ return !(aBounds.right < bBounds.left || aBounds.left > bBounds.right || aBounds.bottom > bBounds.top || aBounds.top < bBounds.bottom);
6803
+ }
6804
+ function getConnectedPcbPrimitiveClustersBeforeRender(primitives) {
6805
+ const clusters = [];
6806
+ const visited = /* @__PURE__ */ new Set();
6807
+ for (const primitive of primitives) {
6808
+ if (visited.has(primitive)) continue;
6809
+ const cluster = [];
6810
+ const stack = [primitive];
6811
+ visited.add(primitive);
6812
+ while (stack.length > 0) {
6813
+ const current = stack.pop();
6814
+ cluster.push(current);
6815
+ for (const candidate of primitives) {
6816
+ if (visited.has(candidate)) continue;
6817
+ if (!doPcbPrimitivesOverlapBeforeRender(current, candidate)) continue;
6818
+ visited.add(candidate);
6819
+ stack.push(candidate);
6820
+ }
6821
+ }
6822
+ clusters.push(cluster);
6823
+ }
6824
+ return clusters;
6825
+ }
6826
+
6827
+ // lib/components/primitive-components/Port/selectPortForPcbPrimitive.ts
6828
+ var getPinNumberFromHints = (hints) => {
6829
+ for (const hint of hints) {
6830
+ const normalizedHint = hint.toString();
6831
+ if (!/^(pin)?\d+$/.test(normalizedHint)) continue;
6832
+ return Number.parseInt(normalizedHint.replace(/^pin/, ""));
6833
+ }
6834
+ return null;
6835
+ };
6836
+ function selectPortForPcbPrimitive(ports, primitive, hints) {
6837
+ const pinNumber = getPinNumberFromHints(hints);
6838
+ const matchingPorts = ports.filter((port) => port.isMatchingAnyOf(hints));
6839
+ if (matchingPorts.length === 0) return null;
6840
+ const pinMatchedPorts = pinNumber === null ? matchingPorts : matchingPorts.filter(
6841
+ (port) => port._parsedProps.pinNumber === pinNumber || port._primaryPinNumber === pinNumber
6842
+ );
6843
+ const candidates = pinMatchedPorts.length > 0 ? pinMatchedPorts : matchingPorts;
6844
+ const overlappingPort = candidates.find(
6845
+ (port) => port.matchedComponents.some(
6846
+ (component) => component.isPcbPrimitive && doPcbPrimitivesOverlapBeforeRender(component, primitive)
6847
+ )
6848
+ );
6849
+ if (overlappingPort) return overlappingPort;
6850
+ const unmatchedPort = candidates.find(
6851
+ (port) => !port.matchedComponents.some((component) => component.isPcbPrimitive)
6852
+ );
6853
+ return unmatchedPort ?? candidates[0];
6854
+ }
6855
+
6856
+ // lib/components/primitive-components/PlatedHole.ts
6755
6857
  var PlatedHole = class extends PrimitiveComponent2 {
6756
6858
  pcb_plated_hole_id = null;
6757
6859
  matchedPort = null;
@@ -6832,13 +6934,14 @@ var PlatedHole = class extends PrimitiveComponent2 {
6832
6934
  if (!this._parsedProps.portHints) {
6833
6935
  return;
6834
6936
  }
6835
- for (const port of parentPorts) {
6836
- if (port.isMatchingAnyOf(this._parsedProps.portHints)) {
6837
- this.matchedPort = port;
6838
- port.registerMatch(this);
6839
- return;
6840
- }
6841
- }
6937
+ const port = selectPortForPcbPrimitive(
6938
+ parentPorts,
6939
+ this,
6940
+ this._parsedProps.portHints
6941
+ );
6942
+ if (!port) return;
6943
+ this.matchedPort = port;
6944
+ port.registerMatch(this);
6842
6945
  }
6843
6946
  doInitialPcbPrimitiveRender() {
6844
6947
  if (this.root?.pcbDisabled) return;
@@ -7614,13 +7717,14 @@ var SmtPad = class extends PrimitiveComponent2 {
7614
7717
  if (!this.props.portHints) {
7615
7718
  return;
7616
7719
  }
7617
- for (const port of parentPorts) {
7618
- if (port.isMatchingAnyOf(this.props.portHints)) {
7619
- this.matchedPort = port;
7620
- port.registerMatch(this);
7621
- return;
7622
- }
7623
- }
7720
+ const port = selectPortForPcbPrimitive(
7721
+ parentPorts,
7722
+ this,
7723
+ this.props.portHints
7724
+ );
7725
+ if (!port) return;
7726
+ this.matchedPort = port;
7727
+ port.registerMatch(this);
7624
7728
  }
7625
7729
  doInitialPcbPrimitiveRender() {
7626
7730
  if (this.root?.pcbDisabled) return;
@@ -8656,6 +8760,8 @@ var Port = class extends PrimitiveComponent2 {
8656
8760
  schematic_stem_line_id = null;
8657
8761
  schematicSymbolPortDef = null;
8658
8762
  matchedComponents;
8763
+ _isPrimaryPort = true;
8764
+ _primaryPinNumber = null;
8659
8765
  facingDirection = null;
8660
8766
  originDescription = null;
8661
8767
  get config() {
@@ -10576,10 +10682,73 @@ var shouldCheckPortForMissingTrace = (component, port) => {
10576
10682
  return true;
10577
10683
  };
10578
10684
 
10579
- // lib/components/base-components/NormalComponent/utils/getLogicalPortsFromPortHintGroups.ts
10580
- function getLogicalPortsFromPortHintGroups(portHintGroups, opts) {
10685
+ // lib/components/base-components/NormalComponent/utils/canMergePortDefinitions.ts
10686
+ var canMergePortDefinitions = (a, b) => {
10687
+ const aPinNumber = a._parsedProps.pinNumber;
10688
+ const bPinNumber = b._parsedProps.pinNumber;
10689
+ if (aPinNumber !== void 0 && bPinNumber !== void 0 && aPinNumber !== bPinNumber) {
10690
+ return false;
10691
+ }
10692
+ return a.isMatchingAnyOf(b.getNameAndAliases());
10693
+ };
10694
+
10695
+ // lib/components/base-components/NormalComponent/utils/inferInternallyConnectedPinNamesFromPorts.ts
10696
+ var hasExactSameMembers = (a, b) => {
10697
+ const sortedA = a.toSorted();
10698
+ const sortedB = b.toSorted();
10699
+ return sortedA.length === sortedB.length && sortedA.every((name, index) => name === sortedB[index]);
10700
+ };
10701
+ var getPinNumberedName = (port) => port.getNameAndAliases().find((alias) => /^pin\d+$/.test(alias)) ?? null;
10702
+ var getPortReferenceName = (port, portNameCounts) => {
10703
+ if (!port._isPrimaryPort && port.props.name) {
10704
+ return port.props.name;
10705
+ }
10706
+ if (port.props.name && (portNameCounts.get(port.props.name) ?? 0) <= 1) {
10707
+ return port.props.name;
10708
+ }
10709
+ const pinNumberedName = getPinNumberedName(port);
10710
+ if (pinNumberedName) return pinNumberedName;
10711
+ return port.props.name ?? null;
10712
+ };
10713
+ function inferInternallyConnectedPinNamesFromPorts(ports, inferredInternallyConnectedPinNames) {
10714
+ const portsByAlias = /* @__PURE__ */ new Map();
10715
+ for (const port of ports) {
10716
+ for (const alias of new Set(port.getNameAndAliases())) {
10717
+ if (/^(pin)?\d+$/.test(alias)) continue;
10718
+ if (!port._isPrimaryPort && alias === port.props.name) {
10719
+ continue;
10720
+ }
10721
+ if (!portsByAlias.has(alias)) portsByAlias.set(alias, []);
10722
+ portsByAlias.get(alias).push(port);
10723
+ }
10724
+ }
10725
+ for (const aliasPorts of portsByAlias.values()) {
10726
+ const uniquePorts = Array.from(new Set(aliasPorts));
10727
+ if (uniquePorts.length < 2) continue;
10728
+ const portNameCounts = /* @__PURE__ */ new Map();
10729
+ for (const port of uniquePorts) {
10730
+ if (!port.props.name) continue;
10731
+ portNameCounts.set(
10732
+ port.props.name,
10733
+ (portNameCounts.get(port.props.name) ?? 0) + 1
10734
+ );
10735
+ }
10736
+ const portNames = uniquePorts.map((port) => getPortReferenceName(port, portNameCounts)).filter((portName) => Boolean(portName));
10737
+ const uniquePortNames = Array.from(new Set(portNames));
10738
+ if (uniquePortNames.length < 2) continue;
10739
+ if (inferredInternallyConnectedPinNames.some(
10740
+ (group) => hasExactSameMembers(group, uniquePortNames)
10741
+ )) {
10742
+ continue;
10743
+ }
10744
+ inferredInternallyConnectedPinNames.push(uniquePortNames);
10745
+ }
10746
+ }
10747
+
10748
+ // lib/components/base-components/NormalComponent/utils/getPrimaryPortsFromPortHintGroups.ts
10749
+ function getPrimaryPortsFromPortHintGroups(portHintGroups, opts) {
10581
10750
  let implicitPinNumber = 1;
10582
- const portsByPinNumber = /* @__PURE__ */ new Map();
10751
+ const entriesByPinNumber = /* @__PURE__ */ new Map();
10583
10752
  for (const portHintGroup of portHintGroups) {
10584
10753
  const filteredPortHints = portHintGroup.hints.filter(
10585
10754
  (hint) => hint && hint.trim() !== ""
@@ -10598,15 +10767,74 @@ function getLogicalPortsFromPortHintGroups(portHintGroups, opts) {
10598
10767
  newPort.originDescription = portHintGroup.originDescription;
10599
10768
  const pinNumber = newPort._parsedProps.pinNumber;
10600
10769
  if (pinNumber === void 0) continue;
10601
- const existingPort = portsByPinNumber.get(pinNumber);
10602
- if (!existingPort) {
10603
- portsByPinNumber.set(pinNumber, newPort);
10770
+ if (!entriesByPinNumber.has(pinNumber))
10771
+ entriesByPinNumber.set(pinNumber, []);
10772
+ entriesByPinNumber.get(pinNumber).push({
10773
+ port: newPort,
10774
+ component: portHintGroup.component
10775
+ });
10776
+ }
10777
+ const ports = [];
10778
+ for (const [pinNumber, entries] of entriesByPinNumber) {
10779
+ const mergeAliases = (target, source) => {
10780
+ const mergedAliases = source.getNameAndAliases().filter((alias) => !target.getNameAndAliases().includes(alias));
10781
+ target.externallyAddedAliases.push(...mergedAliases);
10782
+ };
10783
+ const primaryPort = entries[0].port;
10784
+ const componentEntries = entries.filter(
10785
+ (entry) => Boolean(entry.component)
10786
+ );
10787
+ if (componentEntries.length !== entries.length) {
10788
+ for (const entry of entries.slice(1)) {
10789
+ mergeAliases(primaryPort, entry.port);
10790
+ }
10791
+ ports.push(primaryPort);
10792
+ continue;
10793
+ }
10794
+ const clusters = getConnectedPcbPrimitiveClustersBeforeRender(
10795
+ componentEntries.map((entry) => entry.component)
10796
+ );
10797
+ if (clusters.length <= 1) {
10798
+ for (const entry of entries.slice(1)) {
10799
+ mergeAliases(primaryPort, entry.port);
10800
+ }
10801
+ ports.push(primaryPort);
10604
10802
  continue;
10605
10803
  }
10606
- const mergedAliases = newPort.getNameAndAliases().filter((alias) => !existingPort.getNameAndAliases().includes(alias));
10607
- existingPort.externallyAddedAliases.push(...mergedAliases);
10804
+ const internallyConnectedPinNames = [primaryPort.props.name];
10805
+ for (const [clusterIndex, cluster] of clusters.entries()) {
10806
+ const clusterEntries = componentEntries.filter(
10807
+ (entry) => cluster.includes(entry.component)
10808
+ );
10809
+ if (clusterIndex === 0) {
10810
+ for (const entry of clusterEntries.slice(1)) {
10811
+ mergeAliases(primaryPort, entry.port);
10812
+ }
10813
+ ports.push(primaryPort);
10814
+ continue;
10815
+ }
10816
+ const internalPort = new Port({
10817
+ name: `pin${pinNumber}_internal_${clusterIndex}`,
10818
+ aliases: primaryPort.getNameAndAliases()
10819
+ });
10820
+ internalPort._isPrimaryPort = false;
10821
+ internalPort._primaryPinNumber = pinNumber;
10822
+ internalPort.originDescription = `implicitInternalPhysicalPort:pin${pinNumber}:${clusterIndex}`;
10823
+ for (const entry of clusterEntries) {
10824
+ mergeAliases(internalPort, entry.port);
10825
+ }
10826
+ internallyConnectedPinNames.push(internalPort.props.name);
10827
+ ports.push(internalPort);
10828
+ }
10829
+ opts?.inferredInternallyConnectedPinNames?.push(internallyConnectedPinNames);
10830
+ }
10831
+ if (opts?.inferredInternallyConnectedPinNames) {
10832
+ inferInternallyConnectedPinNamesFromPorts(
10833
+ ports,
10834
+ opts.inferredInternallyConnectedPinNames
10835
+ );
10608
10836
  }
10609
- return Array.from(portsByPinNumber.values());
10837
+ return ports;
10610
10838
  }
10611
10839
 
10612
10840
  // lib/components/base-components/NormalComponent/NormalComponent.ts
@@ -10632,6 +10860,7 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
10632
10860
  _asyncSupplierPartNumbers;
10633
10861
  _asyncFootprintCadModel;
10634
10862
  _isCadModelChild;
10863
+ _inferredInternallyConnectedPinNames = [];
10635
10864
  pcb_missing_footprint_error_id;
10636
10865
  _hasStartedFootprintUrlLoad = false;
10637
10866
  _invalidFootprintPropMessages = [];
@@ -10648,7 +10877,7 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
10648
10877
  }
10649
10878
  get internallyConnectedPinNames() {
10650
10879
  const rawPins = this._parsedProps.internallyConnectedPins ?? this.defaultInternallyConnectedPinNames;
10651
- return rawPins.map(
10880
+ return [...rawPins, ...this._inferredInternallyConnectedPinNames].map(
10652
10881
  (pinGroup) => pinGroup.map(
10653
10882
  (pin) => typeof pin === "number" ? `pin${pin}` : pin
10654
10883
  )
@@ -10723,6 +10952,7 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
10723
10952
  */
10724
10953
  initPorts(opts = {}) {
10725
10954
  if (this.root?.schematicDisabled) return;
10955
+ this._inferredInternallyConnectedPinNames = [];
10726
10956
  const { config } = this;
10727
10957
  const portsToCreate = [];
10728
10958
  const schPortArrangement = this._getSchematicPortArrangement();
@@ -10836,14 +11066,17 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
10836
11066
  );
10837
11067
  if (hasReactSymbol && !symbolAlreadyAdded) {
10838
11068
  } else {
10839
- const portsFromFootprint = this.getPortsFromFootprint(opts);
11069
+ const portsFromFootprint = this.getPortsFromFootprint({
11070
+ ...opts,
11071
+ collectInferredInternallyConnectedPins: true
11072
+ });
10840
11073
  const existingPorts = this._getAllPortsFromChildren();
10841
11074
  for (const port of portsFromFootprint) {
10842
- const matchingPort = existingPorts.find(
10843
- (p) => p.isMatchingAnyOf(port.getNameAndAliases())
10844
- ) ?? portsToCreate.find(
10845
- (p) => p.isMatchingAnyOf(port.getNameAndAliases())
10846
- );
11075
+ if (!port._isPrimaryPort) {
11076
+ portsToCreate.push(port);
11077
+ continue;
11078
+ }
11079
+ const matchingPort = existingPorts.find((p) => canMergePortDefinitions(p, port)) ?? portsToCreate.find((p) => canMergePortDefinitions(p, port));
10847
11080
  if (matchingPort) {
10848
11081
  const mergedAliases = port.getNameAndAliases().filter(
10849
11082
  (alias) => !matchingPort.getNameAndAliases().includes(alias)
@@ -10855,7 +11088,7 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
10855
11088
  }
10856
11089
  }
10857
11090
  }
10858
- const requiredPinCount = opts.pinCount ?? this._getPinCount() ?? 0;
11091
+ const requiredPinCount = opts.pinCount ?? this._getPrimaryPinCount() ?? 0;
10859
11092
  for (let pn = 1; pn <= requiredPinCount; pn++) {
10860
11093
  if (portsToCreate.find((p) => p._parsedProps.pinNumber === pn)) continue;
10861
11094
  if (!schPortArrangement) {
@@ -10886,7 +11119,7 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
10886
11119
  "bottomPinCount"
10887
11120
  ].some((key) => key in schPortArrangement)) {
10888
11121
  explicitlyListedPinNumbersInSchPortArrangement = Array.from(
10889
- { length: this._getPinCount() },
11122
+ { length: this._getPrimaryPinCount() },
10890
11123
  (_, i) => i + 1
10891
11124
  );
10892
11125
  }
@@ -10905,6 +11138,12 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
10905
11138
  )
10906
11139
  );
10907
11140
  }
11141
+ inferInternallyConnectedPinNamesFromPorts(
11142
+ Array.from(
11143
+ /* @__PURE__ */ new Set([...this._getAllPortsFromChildren(), ...portsToCreate])
11144
+ ),
11145
+ this._inferredInternallyConnectedPinNames
11146
+ );
10908
11147
  if (portsToCreate.length > 0) {
10909
11148
  this.addAll(portsToCreate);
10910
11149
  }
@@ -11440,6 +11679,14 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
11440
11679
  super.add(component);
11441
11680
  }
11442
11681
  getPortsFromFootprint(opts) {
11682
+ let inferredInternallyConnectedPinNames = void 0;
11683
+ if (opts?.collectInferredInternallyConnectedPins) {
11684
+ inferredInternallyConnectedPinNames = this._inferredInternallyConnectedPinNames;
11685
+ }
11686
+ const primaryPortOpts = {
11687
+ ...opts,
11688
+ inferredInternallyConnectedPinNames
11689
+ };
11443
11690
  let { footprint } = this.props;
11444
11691
  if (typeof footprint === "string" && parseLibraryFootprintRef(footprint) && this.children.some((c) => c.componentName === "Footprint")) {
11445
11692
  footprint = this.children.find((c) => c.componentName === "Footprint");
@@ -11457,7 +11704,7 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
11457
11704
  this._queueInvalidFootprintPropMessage(footprint, error);
11458
11705
  return [];
11459
11706
  }
11460
- return getLogicalPortsFromPortHintGroups(
11707
+ return getPrimaryPortsFromPortHintGroups(
11461
11708
  fpCircuitJson.flatMap(
11462
11709
  (elm) => "port_hints" in elm && elm.port_hints ? [
11463
11710
  {
@@ -11466,21 +11713,22 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
11466
11713
  }
11467
11714
  ] : []
11468
11715
  ),
11469
- opts
11716
+ primaryPortOpts
11470
11717
  );
11471
11718
  }
11472
11719
  if (!isValidElement(footprint) && footprint && footprint.componentName === "Footprint") {
11473
11720
  const fp2 = footprint;
11474
- return getLogicalPortsFromPortHintGroups(
11721
+ return getPrimaryPortsFromPortHintGroups(
11475
11722
  fp2.children.flatMap(
11476
11723
  (fpChild) => fpChild.props.portHints ? [
11477
11724
  {
11478
11725
  hints: fpChild.props.portHints,
11479
- originDescription: `footprint:${footprint}`
11726
+ originDescription: `footprint:${footprint}`,
11727
+ component: fpChild
11480
11728
  }
11481
11729
  ] : []
11482
11730
  ),
11483
- opts
11731
+ primaryPortOpts
11484
11732
  );
11485
11733
  }
11486
11734
  const newPorts = [];
@@ -11580,7 +11828,7 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
11580
11828
  ...bottomSide?.pins ?? []
11581
11829
  );
11582
11830
  }
11583
- _getPinCount() {
11831
+ _getPrimaryPinCount() {
11584
11832
  const schPortArrangement = this._getSchematicPortArrangement();
11585
11833
  if (schPortArrangement) {
11586
11834
  const pinCountFromSchematicPortArrangement = this._getPinCountFromSchematicPortArrangement();
@@ -11588,7 +11836,7 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
11588
11836
  }
11589
11837
  const portsFromFootprint = this.getPortsFromFootprint();
11590
11838
  if (portsFromFootprint.length > 0) {
11591
- return portsFromFootprint.length;
11839
+ return portsFromFootprint.filter((port) => port._isPrimaryPort).length;
11592
11840
  }
11593
11841
  const { pinLabels } = this._parsedProps;
11594
11842
  if (pinLabels) {
@@ -11631,7 +11879,7 @@ var NormalComponent3 = class extends PrimitiveComponent2 {
11631
11879
  if (this.getSchematicSymbol()) return null;
11632
11880
  if (!this.config.shouldRenderAsSchematicBox) return null;
11633
11881
  const { _parsedProps: props } = this;
11634
- const pinCount = this._getPinCount();
11882
+ const pinCount = this._getPrimaryPinCount();
11635
11883
  const pinSpacing = props.schPinSpacing ?? 0.2;
11636
11884
  const pinLabelsFromPorts = this._getPinLabelsFromPorts();
11637
11885
  const allPinLabels = {
@@ -19624,7 +19872,7 @@ import { identity as identity5 } from "transformation-matrix";
19624
19872
  var package_default = {
19625
19873
  name: "@tscircuit/core",
19626
19874
  type: "module",
19627
- version: "0.0.1178",
19875
+ version: "0.0.1180",
19628
19876
  types: "dist/index.d.ts",
19629
19877
  main: "dist/index.js",
19630
19878
  module: "dist/index.js",
@@ -19657,7 +19905,7 @@ var package_default = {
19657
19905
  "@resvg/resvg-js": "^2.6.2",
19658
19906
  "@tscircuit/alphabet": "0.0.25",
19659
19907
  "@tscircuit/capacity-autorouter": "^0.0.434",
19660
- "@tscircuit/checks": "0.0.119",
19908
+ "@tscircuit/checks": "0.0.120",
19661
19909
  "@tscircuit/circuit-json-util": "^0.0.93",
19662
19910
  "@tscircuit/common": "^0.0.20",
19663
19911
  "@tscircuit/copper-pour-solver": "^0.0.23",
@@ -23790,7 +24038,7 @@ var Connector = class extends Chip {
23790
24038
  if (this.getSchematicSymbol()) return null;
23791
24039
  if (!this.config.shouldRenderAsSchematicBox) return null;
23792
24040
  const { _parsedProps: props } = this;
23793
- const pinCount = this._getPinCount();
24041
+ const pinCount = this._getPrimaryPinCount();
23794
24042
  const pinSpacing = props.schPinSpacing ?? 0.2;
23795
24043
  const pinLabelsFromPorts = this._getPinLabelsFromPorts();
23796
24044
  const allPinLabels = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.1179",
4
+ "version": "0.0.1181",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -34,7 +34,7 @@
34
34
  "@resvg/resvg-js": "^2.6.2",
35
35
  "@tscircuit/alphabet": "0.0.25",
36
36
  "@tscircuit/capacity-autorouter": "^0.0.434",
37
- "@tscircuit/checks": "0.0.119",
37
+ "@tscircuit/checks": "0.0.120",
38
38
  "@tscircuit/circuit-json-util": "^0.0.93",
39
39
  "@tscircuit/common": "^0.0.20",
40
40
  "@tscircuit/copper-pour-solver": "^0.0.23",