@tscircuit/core 0.0.2145 → 0.0.2147

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.js CHANGED
@@ -137,6 +137,68 @@ __export(components_exports, {
137
137
  VoltageSource: () => VoltageSource
138
138
  });
139
139
 
140
+ // lib/utils/schematic/getPinNumberFromPinLabelsKey.ts
141
+ var PIN_LABELS_KEY_RE = /^(?:pin)?(\d+)$/;
142
+ var getPinNumberFromPinLabelsKey = (pinKey) => {
143
+ const match = pinKey.match(PIN_LABELS_KEY_RE);
144
+ if (!match) return null;
145
+ return Number.parseInt(match[1], 10);
146
+ };
147
+
148
+ // lib/utils/schematic/resolvePinLabels.ts
149
+ var isPhysicalPinName = (name) => /^[A-Z]+[1-9]\d*$/.test(name);
150
+ function resolvePinLabels(pinLabels) {
151
+ const usedPinNumbers = new Set(
152
+ Object.keys(pinLabels).flatMap((key) => {
153
+ const pinNumber = getPinNumberFromPinLabelsKey(key);
154
+ return pinNumber === null ? [] : [pinNumber];
155
+ })
156
+ );
157
+ const physicalNames = Object.keys(pinLabels).filter(isPhysicalPinName).sort((a2, b2) => {
158
+ const [, rowA, columnA] = a2.match(/^([A-Z]+)(\d+)$/);
159
+ const [, rowB, columnB] = b2.match(/^([A-Z]+)(\d+)$/);
160
+ return rowA.length - rowB.length || (rowA < rowB ? -1 : rowA > rowB ? 1 : 0) || Number(columnA) - Number(columnB);
161
+ });
162
+ const physicalPins = [];
163
+ let nextPinNumber = 1;
164
+ for (const name of physicalNames) {
165
+ while (usedPinNumbers.has(nextPinNumber)) nextPinNumber++;
166
+ physicalPins.push({ physicalPinName: name, pinNumber: nextPinNumber++ });
167
+ }
168
+ return Object.entries(pinLabels).map(([key, labels]) => {
169
+ const numericPinNumber = getPinNumberFromPinLabelsKey(key);
170
+ const pinNumber = numericPinNumber ?? physicalPins.find((pin) => pin.physicalPinName === key)?.pinNumber;
171
+ if (pinNumber === void 0) {
172
+ throw new Error(
173
+ `Invalid pinLabels key "${key}". Expected a numeric pin (e.g. pin1) or physical ball name (e.g. A1).`
174
+ );
175
+ }
176
+ return {
177
+ pinNumber,
178
+ physicalPinName: numericPinNumber === null ? key : void 0,
179
+ labels: typeof labels === "string" ? [labels] : [...labels]
180
+ };
181
+ });
182
+ }
183
+ function getNumericPinLabels(pinLabels) {
184
+ if (!Object.keys(pinLabels).some(isPhysicalPinName)) {
185
+ return Object.fromEntries(
186
+ Object.entries(pinLabels).map(([key, labels]) => [
187
+ key,
188
+ typeof labels === "string" ? labels : [...labels]
189
+ ])
190
+ );
191
+ }
192
+ return Object.fromEntries(
193
+ resolvePinLabels(pinLabels).map(
194
+ ({ pinNumber, physicalPinName, labels }) => [
195
+ `pin${pinNumber}`,
196
+ physicalPinName ? [...labels, physicalPinName] : labels
197
+ ]
198
+ )
199
+ );
200
+ }
201
+
140
202
  // lib/components/base-components/NormalComponent/NormalComponent.ts
141
203
  import { resolveManufacturerPartNumber as resolveManufacturerPartNumber2 } from "@tscircuit/props";
142
204
 
@@ -937,6 +999,18 @@ var mergeRoutes = (routes) => {
937
999
  // lib/components/primitive-components/Net.ts
938
1000
  import { netProps } from "@tscircuit/props";
939
1001
 
1002
+ // lib/components/primitive-components/Port/assertUnambiguousPhysicalPortSelection.ts
1003
+ function assertUnambiguousPhysicalPortSelection(ports, selector) {
1004
+ if (ports.length < 2 || !ports.some((port) => port.physicalPinName)) return;
1005
+ const names = ports.map((port) => {
1006
+ const componentName = port.getParentNormalComponent()?.name ?? "unnamed component";
1007
+ return `${componentName}.${port.physicalPinName ?? port.name}`;
1008
+ });
1009
+ throw new Error(
1010
+ `Ambiguous port "${selector}"; use a physical ball name: ${names.join(", ")}.`
1011
+ );
1012
+ }
1013
+
940
1014
  // lib/components/base-components/PrimitiveComponent/get-construction-props-schema.ts
941
1015
  var constructionSchemas = /* @__PURE__ */ new WeakMap();
942
1016
  var getConstructionPropsSchema = (schema) => {
@@ -2431,6 +2505,11 @@ var PrimitiveComponent2 = class extends Renderable {
2431
2505
  if (this.isPrimitiveContainer) return this;
2432
2506
  return this.parent?.getPrimitiveContainer?.() ?? null;
2433
2507
  }
2508
+ /** Resolve mounting intent from the container owning this primitive. */
2509
+ _getIsSmt() {
2510
+ const container = this.getPrimitiveContainer();
2511
+ return container && container !== this ? container._getIsSmt() : true;
2512
+ }
2434
2513
  /**
2435
2514
  * Get the Symbol ancestor if this component is inside a Symbol primitive container.
2436
2515
  * Used by schematic primitives to access the symbol's resize transform.
@@ -2999,9 +3078,16 @@ var PrimitiveComponent2 = class extends Renderable {
2999
3078
  this,
3000
3079
  cssSelectOptionsInsideSubcircuit
3001
3080
  );
3002
- result = allMatching.find(
3081
+ const matchingComponents = allMatching.filter(
3003
3082
  (n2) => n2.lowercaseComponentName === options.type
3004
3083
  );
3084
+ if (options.type === "port") {
3085
+ assertUnambiguousPhysicalPortSelection(
3086
+ matchingComponents,
3087
+ selectorRaw
3088
+ );
3089
+ }
3090
+ result = matchingComponents[0] ?? null;
3005
3091
  }
3006
3092
  if (!options?.type) {
3007
3093
  result ??= selectOne(
@@ -3011,6 +3097,14 @@ var PrimitiveComponent2 = class extends Renderable {
3011
3097
  );
3012
3098
  }
3013
3099
  if (result) {
3100
+ if (!options?.type && result.componentName === "Port") {
3101
+ assertUnambiguousPhysicalPortSelection(
3102
+ this.selectAll(selectorRaw).filter(
3103
+ (component) => component.componentName === "Port"
3104
+ ),
3105
+ selectorRaw
3106
+ );
3107
+ }
3014
3108
  this._cachedSelectOneQueries.set(selectorRaw, result);
3015
3109
  return result;
3016
3110
  }
@@ -5211,10 +5305,13 @@ var Port = class extends PrimitiveComponent2 {
5211
5305
  registerMatch(component) {
5212
5306
  this.matchedComponents.push(component);
5213
5307
  }
5308
+ /** Physical package ball name; pinNumber remains a schematic layout ordinal. */
5309
+ physicalPinName;
5214
5310
  getNameAndAliases() {
5215
5311
  const { _parsedProps: props } = this;
5216
5312
  return Array.from(
5217
5313
  /* @__PURE__ */ new Set([
5314
+ ...this.physicalPinName ? [this.physicalPinName] : [],
5218
5315
  ...props.name ? [props.name] : [],
5219
5316
  ...props.aliases ?? [],
5220
5317
  ...typeof props.pinNumber === "number" ? [`pin${props.pinNumber}`, props.pinNumber.toString()] : [],
@@ -5308,8 +5405,10 @@ var Port = class extends PrimitiveComponent2 {
5308
5405
  const source_component_id = parentWithSourceId?.source_component_id ?? null;
5309
5406
  const portAttributesFromParent = resolvePortSourcePinAttributes(this);
5310
5407
  const source_port = db2.source_port.insert({
5311
- name: props.name,
5312
- pin_number: props.pinNumber,
5408
+ name: this.physicalPinName ?? props.name,
5409
+ // Physical balls have a string package identity. Keep their numeric
5410
+ // layout ordinal on schematic_port, not the physical source pin number.
5411
+ pin_number: this.physicalPinName ? void 0 : props.pinNumber,
5313
5412
  port_hints,
5314
5413
  source_component_id,
5315
5414
  subcircuit_id: this.getSubcircuit()?.subcircuit_id,
@@ -9242,14 +9341,6 @@ var createInstanceFromReactElement = (reactElm) => {
9242
9341
  import "circuit-json";
9243
9342
  import "zod";
9244
9343
 
9245
- // lib/utils/schematic/getPinNumberFromPinLabelsKey.ts
9246
- var PIN_LABELS_KEY_RE = /^(?:pin)?(\d+)$/;
9247
- var getPinNumberFromPinLabelsKey = (pinKey) => {
9248
- const match = pinKey.match(PIN_LABELS_KEY_RE);
9249
- if (!match) return null;
9250
- return Number.parseInt(match[1], 10);
9251
- };
9252
-
9253
9344
  // lib/utils/filterPinLabels.ts
9254
9345
  function filterPinLabels(pinLabels) {
9255
9346
  if (!pinLabels)
@@ -9323,18 +9414,20 @@ var parsePinNumberFromLabelsOrThrow = (pinNumberOrLabel, pinLabels) => {
9323
9414
  if (!pinLabels) {
9324
9415
  throw new Error(getInvalidPinLabelMessage(pinNumberOrLabel));
9325
9416
  }
9326
- for (const pinNumberKey in pinLabels) {
9327
- const aliases = Array.isArray(pinLabels[pinNumberKey]) ? pinLabels[pinNumberKey] : [pinLabels[pinNumberKey]];
9328
- if (aliases.includes(pinNumberOrLabel)) {
9329
- const pinNumber = getPinNumberFromPinLabelsKey(pinNumberKey);
9330
- if (pinNumber === null) {
9331
- throw new Error(
9332
- `Invalid pinLabels key "${pinNumberKey}". Expected "pin\${number}" (e.g. pin1, pin2).`
9333
- );
9334
- }
9335
- return pinNumber;
9336
- }
9417
+ const resolvedPins = resolvePinLabels(pinLabels);
9418
+ const physicalPin = resolvedPins.find(
9419
+ (pin) => pin.physicalPinName === pinNumberOrLabel
9420
+ );
9421
+ if (physicalPin) return physicalPin.pinNumber;
9422
+ const matches = resolvedPins.filter(
9423
+ (pin) => pin.labels.includes(pinNumberOrLabel)
9424
+ );
9425
+ if (matches.length > 1 && matches.some((pin) => pin.physicalPinName)) {
9426
+ throw new Error(
9427
+ `Ambiguous pin label "${pinNumberOrLabel}"; use a physical ball name: ${matches.map((pin) => pin.physicalPinName ?? `pin${pin.pinNumber}`).join(", ")}.`
9428
+ );
9337
9429
  }
9430
+ if (matches.length > 0) return matches[0].pinNumber;
9338
9431
  throw new Error(getInvalidPinLabelMessage(pinNumberOrLabel));
9339
9432
  };
9340
9433
 
@@ -11078,10 +11171,15 @@ function selectPortForPcbPrimitive(ports, primitive, hints) {
11078
11171
  const pinNumber = getPinNumberFromHints(hints);
11079
11172
  const matchingPorts = ports.filter((port) => port.isMatchingAnyOf(hints));
11080
11173
  if (matchingPorts.length === 0) return null;
11174
+ const physicalCandidates = matchingPorts.filter(
11175
+ (port) => port.physicalPinName && hints.includes(port.physicalPinName)
11176
+ );
11177
+ if (physicalCandidates.length === 1) return physicalCandidates[0];
11081
11178
  const pinMatchedPorts = pinNumber === null ? matchingPorts : matchingPorts.filter(
11082
11179
  (port) => port._parsedProps.pinNumber === pinNumber || port._primaryPinNumber === pinNumber
11083
11180
  );
11084
11181
  const candidates = pinMatchedPorts.length > 0 ? pinMatchedPorts : matchingPorts;
11182
+ assertUnambiguousPhysicalPortSelection(candidates, hints.join(", "));
11085
11183
  const overlappingPort = candidates.find(
11086
11184
  (port) => port.matchedComponents.some(
11087
11185
  (component) => component.isPcbPrimitive && doPcbPrimitivesOverlapBeforeRender(component, primitive)
@@ -11191,6 +11289,7 @@ var PlatedHole = class extends PrimitiveComponent2 {
11191
11289
  const position = this._getGlobalPcbPositionBeforeLayout();
11192
11290
  const pcb_component_id = this.parent?.pcb_component_id ?? this.getPrimitiveContainer()?.pcb_component_id;
11193
11291
  const subcircuit = this.getSubcircuit();
11292
+ const shouldCreateSolderPaste = this._getIsSmt();
11194
11293
  const platedHoleLayers = this.getAvailablePcbLayers();
11195
11294
  const soldermaskMargin = props.solderMaskMargin;
11196
11295
  const isCoveredWithSolderMask = props.coveredWithSolderMask ?? false;
@@ -11219,26 +11318,28 @@ var PlatedHole = class extends PrimitiveComponent2 {
11219
11318
  pcb_group_id: this.getGroup()?.pcb_group_id ?? void 0
11220
11319
  });
11221
11320
  this.pcb_plated_hole_id = pcb_plated_hole.pcb_plated_hole_id;
11222
- db2.pcb_solder_paste.insert({
11223
- layer: "top",
11224
- shape: "circle",
11225
- // @ts-ignore: no idea why this is triggering
11226
- radius: props.outerDiameter / 2,
11227
- x: position.x,
11228
- y: position.y,
11229
- subcircuit_id: subcircuit?.subcircuit_id ?? void 0,
11230
- pcb_group_id: this.getGroup()?.pcb_group_id ?? void 0
11231
- });
11232
- db2.pcb_solder_paste.insert({
11233
- layer: "bottom",
11234
- shape: "circle",
11235
- // @ts-ignore: no idea why this is triggering
11236
- radius: props.outerDiameter / 2,
11237
- x: position.x,
11238
- y: position.y,
11239
- subcircuit_id: subcircuit?.subcircuit_id ?? void 0,
11240
- pcb_group_id: this.getGroup()?.pcb_group_id ?? void 0
11241
- });
11321
+ if (shouldCreateSolderPaste) {
11322
+ db2.pcb_solder_paste.insert({
11323
+ layer: "top",
11324
+ shape: "circle",
11325
+ // @ts-ignore: no idea why this is triggering
11326
+ radius: props.outerDiameter / 2,
11327
+ x: position.x,
11328
+ y: position.y,
11329
+ subcircuit_id: subcircuit?.subcircuit_id ?? void 0,
11330
+ pcb_group_id: this.getGroup()?.pcb_group_id ?? void 0
11331
+ });
11332
+ db2.pcb_solder_paste.insert({
11333
+ layer: "bottom",
11334
+ shape: "circle",
11335
+ // @ts-ignore: no idea why this is triggering
11336
+ radius: props.outerDiameter / 2,
11337
+ x: position.x,
11338
+ y: position.y,
11339
+ subcircuit_id: subcircuit?.subcircuit_id ?? void 0,
11340
+ pcb_group_id: this.getGroup()?.pcb_group_id ?? void 0
11341
+ });
11342
+ }
11242
11343
  } else if (props.shape === "pill" && props.rectPad) {
11243
11344
  const pcb_plated_hole = db2.pcb_plated_hole.insert({
11244
11345
  pcb_component_id,
@@ -11288,7 +11389,7 @@ var PlatedHole = class extends PrimitiveComponent2 {
11288
11389
  // NOTE: currently PcbPlatedHoleOval erroneously includes both the shape "pill" and "oval"
11289
11390
  });
11290
11391
  this.pcb_plated_hole_id = pcb_plated_hole.pcb_plated_hole_id;
11291
- if (props.shape === "pill") {
11392
+ if (props.shape === "pill" && shouldCreateSolderPaste) {
11292
11393
  db2.pcb_solder_paste.insert({
11293
11394
  layer: "top",
11294
11395
  shape: "rotated_pill",
@@ -11313,7 +11414,7 @@ var PlatedHole = class extends PrimitiveComponent2 {
11313
11414
  subcircuit_id: subcircuit?.subcircuit_id ?? void 0,
11314
11415
  pcb_group_id: this.getGroup()?.pcb_group_id ?? void 0
11315
11416
  });
11316
- } else if (props.shape === "oval") {
11417
+ } else if (props.shape === "oval" && shouldCreateSolderPaste) {
11317
11418
  db2.pcb_solder_paste.insert({
11318
11419
  layer: "top",
11319
11420
  shape: "oval",
@@ -12667,7 +12768,7 @@ var SmtPad = class extends PrimitiveComponent2 {
12667
12768
  const { db: db2 } = this.root;
12668
12769
  const { _parsedProps: props } = this;
12669
12770
  const isCoveredWithSolderMask = props.coveredWithSolderMask ?? false;
12670
- const shouldCreateSolderPaste = !isCoveredWithSolderMask;
12771
+ const shouldCreateSolderPaste = !isCoveredWithSolderMask && this._getIsSmt();
12671
12772
  const soldermaskMargin = props.solderMaskMargin;
12672
12773
  const solderPasteMargin = props.solderPasteMargin;
12673
12774
  const getSolderPasteSize = (padSize2, marginScale = 2) => solderPasteMargin !== void 0 ? Math.max(padSize2 + marginScale * solderPasteMargin, 0) : padSize2 * 0.7;
@@ -17499,6 +17600,15 @@ function getPrimaryPortsFromPortHintGroups(portHintGroups, opts) {
17499
17600
  );
17500
17601
  if (filteredPortHints.length === 0) continue;
17501
17602
  let portHintsList = filteredPortHints;
17603
+ const physicalPinNumber = portHintsList.map((hint) => opts?.implicitPinNumberByHint?.get(hint)).find(
17604
+ (pinNumber2) => pinNumber2 !== void 0 && portHintsList.includes(opts?.getPhysicalPinName?.(pinNumber2) ?? "")
17605
+ );
17606
+ if (physicalPinNumber !== void 0) {
17607
+ portHintsList = [
17608
+ ...portHintsList.filter((hint) => !/^(?:pin)?\d+$/.test(hint)),
17609
+ `pin${physicalPinNumber}`
17610
+ ];
17611
+ }
17502
17612
  const hasPinIdentifier = portHintsList.some(
17503
17613
  (hint) => hint.startsWith("pin") || /^(?:pin)?\d+$/.test(hint)
17504
17614
  );
@@ -17518,6 +17628,7 @@ function getPrimaryPortsFromPortHintGroups(portHintGroups, opts) {
17518
17628
  newPort.originDescription = portHintGroup.originDescription;
17519
17629
  const pinNumber = newPort._parsedProps.pinNumber;
17520
17630
  if (pinNumber === void 0) continue;
17631
+ newPort.physicalPinName = opts?.getPhysicalPinName?.(pinNumber);
17521
17632
  if (!entriesByPinNumber.has(pinNumber))
17522
17633
  entriesByPinNumber.set(pinNumber, []);
17523
17634
  entriesByPinNumber.get(pinNumber).push({
@@ -17579,7 +17690,7 @@ function getPrimaryPortsFromPortHintGroups(portHintGroups, opts) {
17579
17690
  }
17580
17691
  opts?.inferredInternallyConnectedPinNames?.push(internallyConnectedPinNames);
17581
17692
  }
17582
- if (opts?.inferredInternallyConnectedPinNames) {
17693
+ if (opts?.inferredInternallyConnectedPinNames && opts.inferSharedAliases !== false) {
17583
17694
  inferInternallyConnectedPinNamesFromPorts(
17584
17695
  ports,
17585
17696
  opts.inferredInternallyConnectedPinNames
@@ -17603,6 +17714,12 @@ var NormalComponent = class extends PrimitiveComponent2 {
17603
17714
  _resolvedPcbCalcOffsetY;
17604
17715
  isPrimitiveContainer = true;
17605
17716
  _isNormalComponent = true;
17717
+ _getIsSmt() {
17718
+ if (this._parsedProps.isSmt !== void 0) return this._parsedProps.isSmt;
17719
+ return this.selectAll("smtpad").some(
17720
+ (pad) => pad.getPrimitiveContainer() === this
17721
+ );
17722
+ }
17606
17723
  // Mapping from camelCase attribute names to their lowercase equivalents
17607
17724
  // This is used by the CSS selector adapter for fast attribute lookups
17608
17725
  // Reverse mapping from lowercase to camelCase for O(1) lookups
@@ -17653,14 +17770,14 @@ var NormalComponent = class extends PrimitiveComponent2 {
17653
17770
  super(filteredProps);
17654
17771
  if (filteredProps.pinLabels && !Array.isArray(filteredProps.pinLabels)) {
17655
17772
  const invalidPinKey = Object.keys(filteredProps.pinLabels).find(
17656
- (pinKey) => getPinNumberFromPinLabelsKey(pinKey) === null
17773
+ (pinKey) => getPinNumberFromPinLabelsKey(pinKey) === null && !isPhysicalPinName(pinKey)
17657
17774
  );
17658
17775
  if (invalidPinKey) {
17659
17776
  throw new InvalidProps(this.lowercaseComponentName, this.props, {
17660
17777
  _errors: [],
17661
17778
  pinLabels: {
17662
17779
  _errors: [
17663
- `Invalid pinLabels key "${invalidPinKey}". Expected "pin\${number}" (e.g. pin1, pin2).`
17780
+ `Invalid pinLabels key "${invalidPinKey}". Expected "pin\${number}" (e.g. pin1, pin2) or a physical ball name (e.g. A1).`
17664
17781
  ]
17665
17782
  }
17666
17783
  });
@@ -17720,7 +17837,7 @@ var NormalComponent = class extends PrimitiveComponent2 {
17720
17837
  const propsPinLabels = this._parsedProps.pinLabels;
17721
17838
  const pinLabelsFromProps = propsPinLabels && Array.isArray(propsPinLabels) ? Object.fromEntries(
17722
17839
  propsPinLabels.map((label, index2) => [`pin${index2 + 1}`, label])
17723
- ) : propsPinLabels;
17840
+ ) : propsPinLabels ? getNumericPinLabels(propsPinLabels) : void 0;
17724
17841
  const existingChildPorts = this._getAllPortsFromChildren();
17725
17842
  const hasExistingOrQueuedPortWithPinNumber = (pinNumber) => existingChildPorts.some((p2) => p2._parsedProps.pinNumber === pinNumber) || portsToCreate.some((p2) => p2._parsedProps.pinNumber === pinNumber);
17726
17843
  const schPortArrangement = this._getSchematicPortArrangement();
@@ -17774,7 +17891,7 @@ var NormalComponent = class extends PrimitiveComponent2 {
17774
17891
  const pinNumber = getPinNumberFromPinLabelsKey(pinKey);
17775
17892
  if (pinNumber === null) {
17776
17893
  throw new Error(
17777
- `Invalid pinLabels key "${pinKey}". Expected "pin\${number}" (e.g. pin1, pin2).`
17894
+ `Invalid pinLabels key "${pinKey}". Expected "pin\${number}" (e.g. pin1, pin2) or a physical ball name (e.g. A1).`
17778
17895
  );
17779
17896
  }
17780
17897
  let existingPort = existingChildPorts.find(
@@ -17783,8 +17900,9 @@ var NormalComponent = class extends PrimitiveComponent2 {
17783
17900
  const labelList = Array.isArray(label) ? label : [label];
17784
17901
  const propLabel = pinLabelsFromProps?.[pinKey];
17785
17902
  const hasPropLabel = propLabel !== void 0;
17786
- const primaryLabel = hasPropLabel ? labelList[0] : `pin${pinNumber}`;
17787
- const otherLabels = hasPropLabel ? labelList.slice(1) : labelList;
17903
+ const physicalPinName = this._getPhysicalPinName(pinNumber);
17904
+ const primaryLabel = physicalPinName ?? (hasPropLabel ? labelList[0] : `pin${pinNumber}`);
17905
+ const otherLabels = physicalPinName ? labelList : hasPropLabel ? labelList.slice(1) : labelList;
17788
17906
  if (!existingPort) {
17789
17907
  existingPort = new Port(
17790
17908
  {
@@ -17804,6 +17922,7 @@ var NormalComponent = class extends PrimitiveComponent2 {
17804
17922
  } else {
17805
17923
  existingPort.externallyAddedAliases.push(primaryLabel, ...otherLabels);
17806
17924
  }
17925
+ existingPort.physicalPinName = physicalPinName;
17807
17926
  }
17808
17927
  }
17809
17928
  if (config.schematicSymbolName && !opts.ignoreSymbolPorts) {
@@ -17935,7 +18054,11 @@ var NormalComponent = class extends PrimitiveComponent2 {
17935
18054
  }
17936
18055
  inferInternallyConnectedPinNamesFromPorts(
17937
18056
  Array.from(
17938
- /* @__PURE__ */ new Set([...this._getAllPortsFromChildren(), ...portsToCreate])
18057
+ new Set(
18058
+ [...this._getAllPortsFromChildren(), ...portsToCreate].filter(
18059
+ (port) => !port.physicalPinName
18060
+ )
18061
+ )
17939
18062
  ),
17940
18063
  this._inferredInternallyConnectedPinNames
17941
18064
  );
@@ -17947,7 +18070,7 @@ var NormalComponent = class extends PrimitiveComponent2 {
17947
18070
  const pinLabels = this._parsedProps.pinLabels;
17948
18071
  const parsedPinLabels = pinLabels && Array.isArray(pinLabels) ? Object.fromEntries(
17949
18072
  pinLabels.map((label, index2) => [`pin${index2 + 1}`, label])
17950
- ) : pinLabels;
18073
+ ) : pinLabels ? getNumericPinLabels(pinLabels) : void 0;
17951
18074
  const resolvedPinLabels = {
17952
18075
  ...this._impliedFootprintPinLabels ?? {},
17953
18076
  ...parsedPinLabels ?? {}
@@ -17955,6 +18078,14 @@ var NormalComponent = class extends PrimitiveComponent2 {
17955
18078
  if (Object.keys(resolvedPinLabels).length === 0) return void 0;
17956
18079
  return resolvedPinLabels;
17957
18080
  }
18081
+ _resolvedPackagePins;
18082
+ /** Physical package name, separate from the internal schematic ordinal. */
18083
+ _getPhysicalPinName(pinNumber) {
18084
+ const pinLabels = this._parsedProps?.pinLabels ?? this.props.pinLabels;
18085
+ if (!pinLabels || Array.isArray(pinLabels)) return void 0;
18086
+ this._resolvedPackagePins ??= resolvePinLabels(pinLabels);
18087
+ return this._resolvedPackagePins.find((pin) => pin.pinNumber === pinNumber)?.physicalPinName;
18088
+ }
17958
18089
  _getImpliedFootprintString() {
17959
18090
  return null;
17960
18091
  }
@@ -18634,8 +18765,12 @@ var NormalComponent = class extends PrimitiveComponent2 {
18634
18765
  if (opts?.collectInferredInternallyConnectedPins) {
18635
18766
  inferredInternallyConnectedPinNames = this._inferredInternallyConnectedPinNames;
18636
18767
  }
18768
+ const physicalPinLabels = this._parsedProps?.pinLabels ?? this.props.pinLabels;
18769
+ const hasPhysicalPins = physicalPinLabels && !Array.isArray(physicalPinLabels) && Object.keys(physicalPinLabels).some(isPhysicalPinName);
18637
18770
  const implicitPinNumberByHint = /* @__PURE__ */ new Map();
18638
- const explicitPinLabels = this._parsedProps?.pinLabels ?? this.props.pinLabels;
18771
+ const ambiguousHints = /* @__PURE__ */ new Set();
18772
+ const rawExplicitPinLabels = this._parsedProps?.pinLabels ?? this.props.pinLabels;
18773
+ const explicitPinLabels = rawExplicitPinLabels && !Array.isArray(rawExplicitPinLabels) ? getNumericPinLabels(rawExplicitPinLabels) : rawExplicitPinLabels;
18639
18774
  if (explicitPinLabels) {
18640
18775
  const pinLabelEntries = Array.isArray(explicitPinLabels) ? explicitPinLabels.map((label, index2) => [`pin${index2 + 1}`, label]) : Object.entries(explicitPinLabels);
18641
18776
  for (const [pinKey, labelOrLabels] of pinLabelEntries) {
@@ -18644,13 +18779,20 @@ var NormalComponent = class extends PrimitiveComponent2 {
18644
18779
  const labels = Array.isArray(labelOrLabels) ? labelOrLabels : [labelOrLabels];
18645
18780
  for (const label of labels) {
18646
18781
  if (typeof label === "string" && label.length > 0) {
18782
+ const previousPinNumber = implicitPinNumberByHint.get(label);
18783
+ if (hasPhysicalPins && previousPinNumber !== void 0 && previousPinNumber !== pinNumber) {
18784
+ ambiguousHints.add(label);
18785
+ }
18647
18786
  implicitPinNumberByHint.set(label, pinNumber);
18648
18787
  }
18649
18788
  }
18650
18789
  }
18651
18790
  }
18791
+ for (const hint of ambiguousHints) implicitPinNumberByHint.delete(hint);
18652
18792
  const primaryPortOpts = {
18653
18793
  ...opts,
18794
+ inferSharedAliases: !hasPhysicalPins,
18795
+ getPhysicalPinName: (pinNumber) => this._getPhysicalPinName(pinNumber),
18654
18796
  inferredInternallyConnectedPinNames,
18655
18797
  implicitPinNumberByHint
18656
18798
  };
@@ -18896,19 +19038,24 @@ var NormalComponent = class extends PrimitiveComponent2 {
18896
19038
  `pin${pinIndex + 1}`,
18897
19039
  label
18898
19040
  ])
18899
- ) : props.pinLabels;
19041
+ ) : props.pinLabels ? getNumericPinLabels(props.pinLabels) : void 0;
18900
19042
  const allPinLabels = {
18901
19043
  ...pinLabelsFromPorts,
18902
19044
  ...pinLabelsFromProps
18903
19045
  };
18904
19046
  const schPortArrangement = this._getSchematicPortArrangement() ?? this._getImplicitSparseSchematicPortArrangement(pinCount);
19047
+ if (props.pinLabels && !Array.isArray(props.pinLabels) && Object.keys(props.pinLabels).some(isPhysicalPinName) && schPortArrangement) {
19048
+ for (const pin of getPinsFromPortArrangement(schPortArrangement)) {
19049
+ parsePinNumberFromLabelsOrThrow(pin, props.pinLabels);
19050
+ }
19051
+ }
18905
19052
  return getAllDimensionsForSchematicBox({
18906
19053
  schWidth: props.schWidth,
18907
19054
  schHeight: props.schHeight,
18908
19055
  schPinSpacing: pinSpacing,
18909
19056
  numericSchPinStyle: getNumericSchPinStyle(
18910
19057
  props.schPinStyle,
18911
- allPinLabels
19058
+ !Array.isArray(props.pinLabels) ? props.pinLabels ?? allPinLabels : allPinLabels
18912
19059
  ),
18913
19060
  pinCount,
18914
19061
  schPortArrangement,
@@ -110242,20 +110389,22 @@ function getTitleAnchorAndPosition({
110242
110389
  // lib/components/primitive-components/SchematicBox/SchematicBox_doInitialSchematicComponentRender.ts
110243
110390
  var getSchematicBoxPinLabels = (pinLabels) => {
110244
110391
  if (!pinLabels) return [];
110245
- return Object.entries(pinLabels).map(([pinKey, pinLabel]) => {
110246
- const pinNumber = getPinNumberFromPinLabelsKey(pinKey);
110247
- if (pinNumber === null) {
110248
- throw new Error(
110249
- `Invalid schematicbox pinLabels key "${pinKey}". Expected pin<number>.`
110250
- );
110392
+ return Object.entries(getNumericPinLabels(pinLabels)).map(
110393
+ ([pinKey, pinLabel]) => {
110394
+ const pinNumber = getPinNumberFromPinLabelsKey(pinKey);
110395
+ if (pinNumber === null) {
110396
+ throw new Error(
110397
+ `Invalid schematicbox pinLabels key "${pinKey}". Expected pin<number>.`
110398
+ );
110399
+ }
110400
+ const pinAliases = typeof pinLabel === "string" ? [pinLabel] : [...pinLabel];
110401
+ return {
110402
+ pinNumber,
110403
+ displayPinLabel: pinAliases[0] ?? pinKey,
110404
+ pinAliases
110405
+ };
110251
110406
  }
110252
- const pinAliases = typeof pinLabel === "string" ? [pinLabel] : [...pinLabel];
110253
- return {
110254
- pinNumber,
110255
- displayPinLabel: pinAliases[0] ?? pinKey,
110256
- pinAliases
110257
- };
110258
- });
110407
+ );
110259
110408
  };
110260
110409
  var getSchematicBoxPinStyleFromReferencedChip = ({
110261
110410
  referencedChip,
@@ -118543,7 +118692,7 @@ var RoutingPhasePreparationError = class extends Error {
118543
118692
  var package_default = {
118544
118693
  name: "@tscircuit/core",
118545
118694
  type: "module",
118546
- version: "0.0.2144",
118695
+ version: "0.0.2146",
118547
118696
  types: "dist/index.d.ts",
118548
118697
  main: "dist/index.js",
118549
118698
  module: "dist/index.js",
@@ -118602,7 +118751,7 @@ var package_default = {
118602
118751
  "@tscircuit/miniflex": "^0.0.4",
118603
118752
  "@tscircuit/modelprinter": "^0.0.18",
118604
118753
  "@tscircuit/ngspice-spice-engine": "^0.0.20",
118605
- "@tscircuit/props": "^0.0.700",
118754
+ "@tscircuit/props": "^0.0.701",
118606
118755
  "@tscircuit/schematic-match-adapt": "^0.0.18",
118607
118756
  "@tscircuit/schematic-trace-solver": "https://jscdn.tscircuit.com/@tscircuit/schematic-trace-solver/0.0.230.tgz",
118608
118757
  "@tscircuit/solver-utils": "^0.0.16",
@@ -118686,7 +118835,7 @@ var package_default = {
118686
118835
  },
118687
118836
  overrides: {
118688
118837
  "@tscircuit/circuit-json-util": "^0.0.121",
118689
- "@tscircuit/props": "^0.0.700",
118838
+ "@tscircuit/props": "^0.0.701",
118690
118839
  "circuit-json": "^0.0.525",
118691
118840
  "@tscircuit/cableprinter": "^0.0.6"
118692
118841
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.2145",
4
+ "version": "0.0.2147",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -60,7 +60,7 @@
60
60
  "@tscircuit/miniflex": "^0.0.4",
61
61
  "@tscircuit/modelprinter": "^0.0.18",
62
62
  "@tscircuit/ngspice-spice-engine": "^0.0.20",
63
- "@tscircuit/props": "^0.0.700",
63
+ "@tscircuit/props": "^0.0.701",
64
64
  "@tscircuit/schematic-match-adapt": "^0.0.18",
65
65
  "@tscircuit/schematic-trace-solver": "https://jscdn.tscircuit.com/@tscircuit/schematic-trace-solver/0.0.230.tgz",
66
66
  "@tscircuit/solver-utils": "^0.0.16",
@@ -144,7 +144,7 @@
144
144
  },
145
145
  "overrides": {
146
146
  "@tscircuit/circuit-json-util": "^0.0.121",
147
- "@tscircuit/props": "^0.0.700",
147
+ "@tscircuit/props": "^0.0.701",
148
148
  "circuit-json": "^0.0.525",
149
149
  "@tscircuit/cableprinter": "^0.0.6"
150
150
  }