@tscircuit/core 0.0.29 → 0.0.30

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
@@ -9,9 +9,11 @@ import * as Props from '@tscircuit/props';
9
9
  import { traceProps, groupProps, boardProps, footprintProps, smtPadProps, resistorProps, ledProps, capacitorProps, traceHintProps, chipProps, jumperProps, silkscreenPathProps, platedHoleProps } from '@tscircuit/props';
10
10
 
11
11
  declare class Circuit {
12
- rootComponent: PrimitiveComponent | null;
12
+ firstChild: PrimitiveComponent | null;
13
13
  children: PrimitiveComponent[];
14
14
  db: SoupUtilObjects;
15
+ root: Circuit | null;
16
+ isRoot: boolean;
15
17
  _hasRenderedAtleastOnce: boolean;
16
18
  constructor();
17
19
  add(componentOrElm: PrimitiveComponent | ReactElement): void;
@@ -27,8 +29,8 @@ declare class Circuit {
27
29
  previewName: string;
28
30
  tscircuitApiKey?: string;
29
31
  }): Promise<void>;
30
- computeGlobalSchematicTransform(): Matrix;
31
- computeGlobalPcbTransform(): Matrix;
32
+ computeSchematicGlobalTransform(): Matrix;
33
+ computePcbGlobalTransform(): Matrix;
32
34
  selectAll(selector: string): PrimitiveComponent[];
33
35
  selectOne(selector: string, opts?: {
34
36
  type?: "component" | "port";
@@ -36,7 +38,7 @@ declare class Circuit {
36
38
  }
37
39
  declare const Project: typeof Circuit;
38
40
 
39
- declare const orderedRenderPhases: readonly ["ReactSubtreesRender", "CreateNetsFromProps", "CreateTracesFromProps", "SourceRender", "SourceParentAttachment", "PortDiscovery", "PortMatching", "SourceTraceRender", "SchematicComponentRender", "SchematicLayout", "SchematicPortRender", "SchematicTraceRender", "PcbComponentRender", "PcbPortRender", "PcbPrimitiveRender", "PcbParentAttachment", "PcbLayout", "PcbTraceRender", "PcbRouteNetIslands", "PcbComponentSizeCalculation", "CadModelRender"];
41
+ declare const orderedRenderPhases: readonly ["ReactSubtreesRender", "InitializePortsFromChildren", "CreateNetsFromProps", "CreateTracesFromProps", "SourceRender", "SourceParentAttachment", "PortDiscovery", "PortMatching", "SourceTraceRender", "SchematicComponentRender", "SchematicLayout", "SchematicPortRender", "SchematicTraceRender", "PcbComponentRender", "PcbPortRender", "PcbPrimitiveRender", "PcbParentAttachment", "PcbLayout", "PcbTraceRender", "PcbRouteNetIslands", "PcbComponentSizeCalculation", "CadModelRender"];
40
42
  type RenderPhase = (typeof orderedRenderPhases)[number];
41
43
  type RenderPhaseFn<K extends RenderPhase = RenderPhase> = `doInitial${K}` | `update${K}` | `remove${K}`;
42
44
  type RenderPhaseStates = Record<RenderPhase, {
@@ -91,7 +93,6 @@ declare abstract class PrimitiveComponent<ZodProps extends ZodType = any> extend
91
93
  children: PrimitiveComponent[];
92
94
  childrenPendingRemoval: PrimitiveComponent[];
93
95
  get config(): BaseComponentConfig;
94
- project: Circuit | null;
95
96
  props: z.input<ZodProps>;
96
97
  _parsedProps: z.infer<ZodProps>;
97
98
  componentName: string;
@@ -103,14 +104,13 @@ declare abstract class PrimitiveComponent<ZodProps extends ZodType = any> extend
103
104
  * subcircuits and their selectors will not interact with each other (even if the
104
105
  * components share the same names) unless you explicitly break out some ports
105
106
  */
106
- get isSubcircuit(): boolean;
107
+ get isSubcircuit(): any;
107
108
  source_group_id: string | null;
108
109
  source_component_id: string | null;
109
110
  schematic_component_id: string | null;
110
111
  pcb_component_id: string | null;
111
112
  cad_component_id: string | null;
112
113
  constructor(props: z.input<ZodProps>);
113
- setProject(project: Circuit): void;
114
114
  setProps(props: Partial<z.input<ZodProps>>): void;
115
115
  /**
116
116
  * Computes a transformation matrix from the props of this component for PCB
@@ -152,6 +152,7 @@ declare abstract class PrimitiveComponent<ZodProps extends ZodType = any> extend
152
152
  x: number;
153
153
  y: number;
154
154
  };
155
+ get root(): Circuit | null;
155
156
  onAddToParent(parent: PrimitiveComponent): void;
156
157
  /**
157
158
  * Called whenever the props change
@@ -558,7 +559,9 @@ declare class NormalComponent<ZodProps extends ZodType = any, PortNames extends
558
559
  */
559
560
  doInitialPcbComponentSizeCalculation(): void;
560
561
  _renderReactSubtree(element: ReactElement): ReactSubtree;
562
+ doInitialInitializePortsFromChildren(): void;
561
563
  doInitialReactSubtreesRender(): void;
564
+ _hasExistingPortExactly(port1: Port): boolean;
562
565
  add(componentOrElm: PrimitiveComponent | ReactElement): void;
563
566
  getPortsFromFootprint(): Port[];
564
567
  getPortsFromSchematicSymbol(): Port[];
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ import "react";
37
37
  var orderedRenderPhases = [
38
38
  "ReactSubtreesRender",
39
39
  // probably going to be removed b/c subtrees should render instantly
40
+ "InitializePortsFromChildren",
40
41
  "CreateNetsFromProps",
41
42
  "CreateTracesFromProps",
42
43
  "SourceRender",
@@ -169,7 +170,6 @@ var PrimitiveComponent = class extends Renderable {
169
170
  zodProps: z.object({}).passthrough()
170
171
  };
171
172
  }
172
- project = null;
173
173
  props;
174
174
  _parsedProps;
175
175
  componentName = "";
@@ -183,7 +183,7 @@ var PrimitiveComponent = class extends Renderable {
183
183
  */
184
184
  get isSubcircuit() {
185
185
  return Boolean(this.props.subcircuit) || // Implied opaque group for top-level group
186
- this.lowercaseComponentName === "group" && this?.parent?.props?.name === "$root";
186
+ this.lowercaseComponentName === "group" && this?.parent?.isRoot;
187
187
  }
188
188
  source_group_id = null;
189
189
  source_component_id = null;
@@ -204,12 +204,6 @@ var PrimitiveComponent = class extends Renderable {
204
204
  this.lowercaseComponentName = this.componentName.toLowerCase();
205
205
  }
206
206
  }
207
- setProject(project) {
208
- this.project = project;
209
- for (const c of this.children) {
210
- c.setProject(project);
211
- }
212
- }
213
207
  setProps(props) {
214
208
  const newProps = this.config.zodProps.parse({
215
209
  ...this.props,
@@ -315,9 +309,11 @@ var PrimitiveComponent = class extends Renderable {
315
309
  getGlobalSchematicPosition() {
316
310
  return applyToPoint(this.computeSchematicGlobalTransform(), { x: 0, y: 0 });
317
311
  }
312
+ get root() {
313
+ return this.parent?.root ?? null;
314
+ }
318
315
  onAddToParent(parent) {
319
316
  this.parent = parent;
320
- this.project = parent.project;
321
317
  }
322
318
  /**
323
319
  * Called whenever the props change
@@ -581,7 +577,7 @@ var Port = class extends PrimitiveComponent {
581
577
  return connectedTraces;
582
578
  }
583
579
  doInitialSourceRender() {
584
- const { db } = this.project;
580
+ const { db } = this.root;
585
581
  const { _parsedProps: props } = this;
586
582
  const port_hints = this.getNameAndAliases();
587
583
  const source_port = db.source_port.insert({
@@ -593,7 +589,7 @@ var Port = class extends PrimitiveComponent {
593
589
  this.source_port_id = source_port.source_port_id;
594
590
  }
595
591
  doInitialSourceParentAttachment() {
596
- const { db } = this.project;
592
+ const { db } = this.root;
597
593
  if (!this.parent?.source_component_id) {
598
594
  throw new Error(
599
595
  `${this.getString()} has no parent source component (parent: ${this.parent?.getString()})`
@@ -610,7 +606,7 @@ var Port = class extends PrimitiveComponent {
610
606
  * to exist)
611
607
  */
612
608
  doInitialPcbPortRender() {
613
- const { db } = this.project;
609
+ const { db } = this.root;
614
610
  const { matchedComponents } = this;
615
611
  if (!this.parent?.pcb_component_id) {
616
612
  throw new Error(
@@ -640,7 +636,7 @@ var Port = class extends PrimitiveComponent {
640
636
  }
641
637
  }
642
638
  doInitialSchematicPortRender() {
643
- const { db } = this.project;
639
+ const { db } = this.root;
644
640
  const { _parsedProps: props } = this;
645
641
  if (!this.parent) return;
646
642
  const center = this.getGlobalSchematicPosition();
@@ -871,7 +867,7 @@ var SmtPad = class extends PrimitiveComponent {
871
867
  }
872
868
  }
873
869
  doInitialPcbPrimitiveRender() {
874
- const { db } = this.project;
870
+ const { db } = this.root;
875
871
  const { _parsedProps: props } = this;
876
872
  if (!props.portHints) return;
877
873
  const position = this.getGlobalPcbPosition();
@@ -919,7 +915,7 @@ var SilkscreenPath = class extends PrimitiveComponent {
919
915
  };
920
916
  }
921
917
  doInitialPcbPrimitiveRender() {
922
- const { db } = this.project;
918
+ const { db } = this.root;
923
919
  const { _parsedProps: props } = this;
924
920
  const layer = props.layer ?? "top";
925
921
  if (layer !== "top" && layer !== "bottom") {
@@ -987,7 +983,7 @@ var PlatedHole = class extends PrimitiveComponent {
987
983
  }
988
984
  }
989
985
  doInitialPcbPrimitiveRender() {
990
- const { db } = this.project;
986
+ const { db } = this.root;
991
987
  const { _parsedProps: props } = this;
992
988
  if (!props.portHints) return;
993
989
  const position = this.getGlobalPcbPosition();
@@ -1085,7 +1081,7 @@ var Net = class extends PrimitiveComponent {
1085
1081
  return `net.${this.props.name}`;
1086
1082
  }
1087
1083
  doInitialSourceComponentRender() {
1088
- const { db } = this.project;
1084
+ const { db } = this.root;
1089
1085
  const { _parsedProps: props } = this;
1090
1086
  const net = db.source_net.insert({
1091
1087
  name: props.name,
@@ -1134,7 +1130,7 @@ var Net = class extends PrimitiveComponent {
1134
1130
  * such that the nets are fully connected
1135
1131
  */
1136
1132
  doInitialPcbRouteNetIslands() {
1137
- const { db } = this.project;
1133
+ const { db } = this.root;
1138
1134
  const { _parsedProps: props } = this;
1139
1135
  const traces = this._getAllDirectlyConnectedTraces().filter(
1140
1136
  (trace) => (trace._portsRoutedOnPcb?.length ?? 0) > 0
@@ -1240,6 +1236,7 @@ var NormalComponent = class extends PrimitiveComponent {
1240
1236
  */
1241
1237
  initPorts() {
1242
1238
  const { config } = this;
1239
+ const portsToCreate = [];
1243
1240
  if (config.schematicSymbolName) {
1244
1241
  const sym = symbols3[`${config.schematicSymbolName}_horz`];
1245
1242
  if (!sym) return;
@@ -1247,27 +1244,31 @@ var NormalComponent = class extends PrimitiveComponent {
1247
1244
  const port = getPortFromHints(symPort.labels);
1248
1245
  if (port) {
1249
1246
  port.schematicSymbolPortDef = symPort;
1250
- this.add(port);
1247
+ portsToCreate.push(port);
1251
1248
  }
1252
1249
  }
1250
+ this.addAll(portsToCreate);
1253
1251
  return;
1254
1252
  }
1255
1253
  const portsFromFootprint = this.getPortsFromFootprint();
1256
- this.addAll(portsFromFootprint);
1254
+ portsToCreate.push(...portsFromFootprint);
1257
1255
  const pinLabels = this._parsedProps.pinLabels;
1258
1256
  if (pinLabels) {
1259
1257
  for (let [pinNumber, label] of Object.entries(pinLabels)) {
1260
1258
  pinNumber = pinNumber.replace("pin", "");
1261
- const port = this.selectOne(`port[pinNumber='${pinNumber}']`);
1262
- if (!port) {
1259
+ const existingPort = portsToCreate.find(
1260
+ (p) => p._parsedProps.pinNumber === Number(pinNumber)
1261
+ );
1262
+ if (!existingPort) {
1263
1263
  throw new Error(
1264
1264
  `Could not find port for pin number ${pinNumber} in chip ${this.getString()}`
1265
1265
  );
1266
1266
  }
1267
- port.externallyAddedAliases.push(label);
1268
- port.props.name = label;
1267
+ existingPort.externallyAddedAliases.push(label);
1268
+ existingPort.props.name = label;
1269
1269
  }
1270
1270
  }
1271
+ this.addAll(portsToCreate);
1271
1272
  }
1272
1273
  _addChildrenFromStringFootprint() {
1273
1274
  const { footprint } = this.props;
@@ -1305,7 +1306,7 @@ var NormalComponent = class extends PrimitiveComponent {
1305
1306
  doInitialSourceRender() {
1306
1307
  const ftype = this.config.sourceFtype;
1307
1308
  if (!ftype) return;
1308
- const { db } = this.project;
1309
+ const { db } = this.root;
1309
1310
  const { _parsedProps: props } = this;
1310
1311
  const source_component = db.source_component.insert({
1311
1312
  ftype,
@@ -1322,7 +1323,7 @@ var NormalComponent = class extends PrimitiveComponent {
1322
1323
  * You can override this method to do more complicated things.
1323
1324
  */
1324
1325
  doInitialSchematicComponentRender() {
1325
- const { db } = this.project;
1326
+ const { db } = this.root;
1326
1327
  const { schematicSymbolName } = this.config;
1327
1328
  if (!schematicSymbolName) return;
1328
1329
  const symbol_name = `${this.config.schematicSymbolName}_horz`;
@@ -1341,7 +1342,7 @@ var NormalComponent = class extends PrimitiveComponent {
1341
1342
  this.schematic_component_id = schematic_component2.schematic_component_id;
1342
1343
  }
1343
1344
  doInitialPcbComponentRender() {
1344
- const { db } = this.project;
1345
+ const { db } = this.root;
1345
1346
  const { _parsedProps: props } = this;
1346
1347
  const pcb_component = db.pcb_component.insert({
1347
1348
  center: this.getGlobalPcbPosition(),
@@ -1360,7 +1361,7 @@ var NormalComponent = class extends PrimitiveComponent {
1360
1361
  */
1361
1362
  doInitialPcbComponentSizeCalculation() {
1362
1363
  if (!this.pcb_component_id) return;
1363
- const { db } = this.project;
1364
+ const { db } = this.root;
1364
1365
  const { _parsedProps: props } = this;
1365
1366
  let minX = Infinity;
1366
1367
  let minY = Infinity;
@@ -1389,6 +1390,9 @@ var NormalComponent = class extends PrimitiveComponent {
1389
1390
  component: createInstanceFromReactElement(element)
1390
1391
  };
1391
1392
  }
1393
+ doInitialInitializePortsFromChildren() {
1394
+ this.initPorts();
1395
+ }
1392
1396
  doInitialReactSubtreesRender() {
1393
1397
  if (isReactElement2(this.props.footprint)) {
1394
1398
  if (this.reactSubtrees.some((rs) => rs.element === this.props.footprint))
@@ -1398,6 +1402,16 @@ var NormalComponent = class extends PrimitiveComponent {
1398
1402
  this.add(subtree.component);
1399
1403
  }
1400
1404
  }
1405
+ _hasExistingPortExactly(port1) {
1406
+ const existingPorts = this.children.filter(
1407
+ (c) => c.componentName === "Port"
1408
+ );
1409
+ return existingPorts.some((port2) => {
1410
+ const aliases1 = port1.getNameAndAliases();
1411
+ const aliases2 = port2.getNameAndAliases();
1412
+ return aliases1.length === aliases2.length && aliases1.every((alias) => aliases2.includes(alias));
1413
+ });
1414
+ }
1401
1415
  add(componentOrElm) {
1402
1416
  let component;
1403
1417
  if (isReactElement2(componentOrElm)) {
@@ -1407,6 +1421,20 @@ var NormalComponent = class extends PrimitiveComponent {
1407
1421
  } else {
1408
1422
  component = componentOrElm;
1409
1423
  }
1424
+ if (component.componentName === "Port") {
1425
+ if (this._hasExistingPortExactly(component)) return;
1426
+ const existingPorts = this.children.filter(
1427
+ (c) => c.componentName === "Port"
1428
+ );
1429
+ const conflictingPort = existingPorts.find(
1430
+ (p) => p.isMatchingAnyOf(component.getNameAndAliases())
1431
+ );
1432
+ if (conflictingPort) {
1433
+ throw new Error(
1434
+ `Conflicting ports added. Port 1: ${conflictingPort}, Port 2: ${component}`
1435
+ );
1436
+ }
1437
+ }
1410
1438
  super.add(component);
1411
1439
  }
1412
1440
  getPortsFromFootprint() {
@@ -1434,6 +1462,11 @@ var NormalComponent = class extends PrimitiveComponent {
1434
1462
  if (!newPort) continue;
1435
1463
  newPorts2.push(newPort);
1436
1464
  }
1465
+ if (newPorts2.length === 0) {
1466
+ throw new Error(
1467
+ `No ports found in chip ${this} (define a schPortArrangement, a footprint or add portHints to elements of the footprint)`
1468
+ );
1469
+ }
1437
1470
  return newPorts2;
1438
1471
  }
1439
1472
  const newPorts = [];
@@ -1526,7 +1559,7 @@ var Board = class extends Group {
1526
1559
  };
1527
1560
  }
1528
1561
  doInitialPcbComponentRender() {
1529
- const { db } = this.project;
1562
+ const { db } = this.root;
1530
1563
  const { _parsedProps: props } = this;
1531
1564
  if (!props.width || !props.height) {
1532
1565
  throw new Error("Board width and height or an outline are required");
@@ -1539,7 +1572,7 @@ var Board = class extends Group {
1539
1572
  this.pcb_board_id = pcb_board.pcb_board_id;
1540
1573
  }
1541
1574
  removePcbComponentRender() {
1542
- const { db } = this.project;
1575
+ const { db } = this.root;
1543
1576
  if (!this.pcb_board_id) return;
1544
1577
  db.pcb_board.delete(this.pcb_board_id);
1545
1578
  this.pcb_board_id = null;
@@ -1752,7 +1785,7 @@ var Trace = class extends PrimitiveComponent {
1752
1785
  );
1753
1786
  }
1754
1787
  _findConnectedPorts() {
1755
- const { db } = this.project;
1788
+ const { db } = this.root;
1756
1789
  const { _parsedProps: props, parent } = this;
1757
1790
  if (!parent) throw new Error("Trace has no parent");
1758
1791
  const portSelectors = this.getTracePortPathSelectors();
@@ -1836,7 +1869,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1836
1869
  createNetsFromProps(this, this.getTracePathNetSelectors());
1837
1870
  }
1838
1871
  doInitialSourceTraceRender() {
1839
- const { db } = this.project;
1872
+ const { db } = this.root;
1840
1873
  const { _parsedProps: props, parent } = this;
1841
1874
  if (!parent) {
1842
1875
  this.renderError("Trace has no parent");
@@ -1852,7 +1885,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1852
1885
  this.source_trace_id = trace.source_trace_id;
1853
1886
  }
1854
1887
  doInitialPcbTraceRender() {
1855
- const { db } = this.project;
1888
+ const { db } = this.root;
1856
1889
  const { _parsedProps: props, parent } = this;
1857
1890
  if (!parent) throw new Error("Trace has no parent");
1858
1891
  const { allPortsFound, ports } = this._findConnectedPorts();
@@ -1933,7 +1966,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1933
1966
  `Could not find a common layer (using hints) for trace ${this.getString()}`
1934
1967
  );
1935
1968
  }
1936
- const obstacles = getObstaclesFromSoup(this.project.db.toArray());
1969
+ const obstacles = getObstaclesFromSoup(this.root.db.toArray());
1937
1970
  markObstaclesAsConnected(
1938
1971
  obstacles,
1939
1972
  orderedRouteObjectives,
@@ -1999,7 +2032,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1999
2032
  this.pcb_trace_id = pcb_trace.pcb_trace_id;
2000
2033
  }
2001
2034
  doInitialSchematicTraceRender() {
2002
- const { db } = this.project;
2035
+ const { db } = this.root;
2003
2036
  const { _parsedProps: props, parent } = this;
2004
2037
  if (!parent) throw new Error("Trace has no parent");
2005
2038
  const { allPortsFound, portsWithSelectors: ports } = this._findConnectedPorts();
@@ -2093,7 +2126,7 @@ var Resistor = class extends NormalComponent {
2093
2126
  }
2094
2127
  }
2095
2128
  doInitialSourceRender() {
2096
- const { db } = this.project;
2129
+ const { db } = this.root;
2097
2130
  const { _parsedProps: props } = this;
2098
2131
  const source_component = db.source_component.insert({
2099
2132
  ftype: "simple_resistor",
@@ -2171,7 +2204,7 @@ var Capacitor = class extends NormalComponent {
2171
2204
  }
2172
2205
  }
2173
2206
  doInitialSourceRender() {
2174
- const { db } = this.project;
2207
+ const { db } = this.root;
2175
2208
  const { _parsedProps: props } = this;
2176
2209
  const source_component = db.source_component.insert({
2177
2210
  ftype: "simple_capacitor",
@@ -2191,7 +2224,7 @@ import { applyToPoint as applyToPoint4 } from "transformation-matrix";
2191
2224
  var TraceHint = class extends PrimitiveComponent {
2192
2225
  matchedPort = null;
2193
2226
  doInitialPortMatching() {
2194
- const { db } = this.project;
2227
+ const { db } = this.root;
2195
2228
  const { _parsedProps: props, parent } = this;
2196
2229
  if (!parent) return;
2197
2230
  if (parent.componentName === "Trace") {
@@ -2522,7 +2555,7 @@ var Chip = class extends NormalComponent {
2522
2555
  };
2523
2556
  }
2524
2557
  doInitialSourceRender() {
2525
- const { db } = this.project;
2558
+ const { db } = this.root;
2526
2559
  const { _parsedProps: props } = this;
2527
2560
  const source_component = db.source_component.insert({
2528
2561
  ftype: "simple_chip",
@@ -2533,7 +2566,7 @@ var Chip = class extends NormalComponent {
2533
2566
  this.source_component_id = source_component.source_component_id;
2534
2567
  }
2535
2568
  doInitialSchematicComponentRender() {
2536
- const { db } = this.project;
2569
+ const { db } = this.root;
2537
2570
  const { _parsedProps: props } = this;
2538
2571
  const ports = this.children.filter((child) => child instanceof Port);
2539
2572
  const pinSpacing = props.schPinSpacing ?? 0.2;
@@ -2565,7 +2598,7 @@ var Chip = class extends NormalComponent {
2565
2598
  this.schematic_component_id = schematic_component2.schematic_component_id;
2566
2599
  }
2567
2600
  doInitialPcbComponentRender() {
2568
- const { db } = this.project;
2601
+ const { db } = this.root;
2569
2602
  const { _parsedProps: props } = this;
2570
2603
  const pcb_component = db.pcb_component.insert({
2571
2604
  center: { x: props.pcbX ?? 0, y: props.pcbY ?? 0 },
@@ -2591,7 +2624,7 @@ var Jumper = class extends NormalComponent {
2591
2624
  };
2592
2625
  }
2593
2626
  doInitialSourceRender() {
2594
- const { db } = this.project;
2627
+ const { db } = this.root;
2595
2628
  const { _parsedProps: props } = this;
2596
2629
  const source_component = db.source_component.insert({
2597
2630
  ftype: "simple_chip",
@@ -2603,7 +2636,7 @@ var Jumper = class extends NormalComponent {
2603
2636
  this.source_component_id = source_component.source_component_id;
2604
2637
  }
2605
2638
  doInitialSchematicComponentRender() {
2606
- const { db } = this.project;
2639
+ const { db } = this.root;
2607
2640
  const { _parsedProps: props } = this;
2608
2641
  const ports = this.children.filter((child) => child instanceof Port);
2609
2642
  const pinSpacing = props.schPinSpacing ?? 0.2;
@@ -2638,7 +2671,7 @@ var Jumper = class extends NormalComponent {
2638
2671
  this.schematic_component_id = schematic_component2.schematic_component_id;
2639
2672
  }
2640
2673
  doInitialPcbComponentRender() {
2641
- const { db } = this.project;
2674
+ const { db } = this.root;
2642
2675
  const { _parsedProps: props } = this;
2643
2676
  const pcb_component = db.pcb_component.insert({
2644
2677
  center: { x: props.pcbX ?? 0, y: props.pcbY ?? 0 },
@@ -2659,13 +2692,16 @@ import { su } from "@tscircuit/soup-util";
2659
2692
  import { isValidElement as isValidElement2 } from "react";
2660
2693
  import { identity as identity5 } from "transformation-matrix";
2661
2694
  var Circuit = class {
2662
- rootComponent = null;
2695
+ firstChild = null;
2663
2696
  children;
2664
2697
  db;
2698
+ root = null;
2699
+ isRoot = true;
2665
2700
  _hasRenderedAtleastOnce = false;
2666
2701
  constructor() {
2667
2702
  this.children = [];
2668
2703
  this.db = su([]);
2704
+ this.root = this;
2669
2705
  }
2670
2706
  add(componentOrElm) {
2671
2707
  let component;
@@ -2677,9 +2713,9 @@ var Circuit = class {
2677
2713
  this.children.push(component);
2678
2714
  }
2679
2715
  _guessRootComponent() {
2680
- if (this.rootComponent) return;
2716
+ if (this.firstChild) return;
2681
2717
  if (this.children.length === 1) {
2682
- this.rootComponent = this.children[0];
2718
+ this.firstChild = this.children[0];
2683
2719
  return;
2684
2720
  }
2685
2721
  if (this.children.length === 0) {
@@ -2690,7 +2726,7 @@ var Circuit = class {
2690
2726
  if (this.children.length > 0) {
2691
2727
  const board = this.children.find((c) => c.componentName === "Board") ?? null;
2692
2728
  if (board) {
2693
- this.rootComponent = board;
2729
+ this.firstChild = board;
2694
2730
  return;
2695
2731
  }
2696
2732
  }
@@ -2699,13 +2735,13 @@ var Circuit = class {
2699
2735
  );
2700
2736
  }
2701
2737
  render() {
2702
- if (!this.rootComponent) {
2738
+ if (!this.firstChild) {
2703
2739
  this._guessRootComponent();
2704
2740
  }
2705
- const { rootComponent, db } = this;
2706
- if (!rootComponent) throw new Error("Project has no root component");
2707
- rootComponent.setProject(this);
2708
- rootComponent.runRenderCycle();
2741
+ const { firstChild, db } = this;
2742
+ if (!firstChild) throw new Error("Project has no root component");
2743
+ firstChild.parent = this;
2744
+ firstChild.runRenderCycle();
2709
2745
  this._hasRenderedAtleastOnce = true;
2710
2746
  }
2711
2747
  getSoup() {
@@ -2729,17 +2765,17 @@ var Circuit = class {
2729
2765
  const previewOpts = typeof previewNameOrOpts === "object" ? previewNameOrOpts : { previewName: previewNameOrOpts };
2730
2766
  throw new Error("project.preview is not yet implemented");
2731
2767
  }
2732
- computeGlobalSchematicTransform() {
2768
+ computeSchematicGlobalTransform() {
2733
2769
  return identity5();
2734
2770
  }
2735
- computeGlobalPcbTransform() {
2771
+ computePcbGlobalTransform() {
2736
2772
  return identity5();
2737
2773
  }
2738
2774
  selectAll(selector) {
2739
- return this.rootComponent?.selectAll(selector) ?? [];
2775
+ return this.firstChild?.selectAll(selector) ?? [];
2740
2776
  }
2741
2777
  selectOne(selector, opts) {
2742
- return this.rootComponent?.selectOne(selector, opts) ?? null;
2778
+ return this.firstChild?.selectOne(selector, opts) ?? null;
2743
2779
  }
2744
2780
  };
2745
2781
  var Project = Circuit;
package/lib/Project.ts CHANGED
@@ -7,15 +7,18 @@ import { createInstanceFromReactElement } from "./fiber/create-instance-from-rea
7
7
  import { identity, type Matrix } from "transformation-matrix"
8
8
 
9
9
  export class Circuit {
10
- rootComponent: PrimitiveComponent | null = null
10
+ firstChild: PrimitiveComponent | null = null
11
11
  children: PrimitiveComponent[]
12
12
  db: SoupUtilObjects
13
+ root: Circuit | null = null
14
+ isRoot = true
13
15
 
14
16
  _hasRenderedAtleastOnce = false
15
17
 
16
18
  constructor() {
17
19
  this.children = []
18
20
  this.db = su([])
21
+ this.root = this
19
22
  }
20
23
 
21
24
  add(componentOrElm: PrimitiveComponent | ReactElement) {
@@ -30,9 +33,9 @@ export class Circuit {
30
33
  }
31
34
 
32
35
  _guessRootComponent() {
33
- if (this.rootComponent) return
36
+ if (this.firstChild) return
34
37
  if (this.children.length === 1) {
35
- this.rootComponent = this.children[0]
38
+ this.firstChild = this.children[0]
36
39
  return
37
40
  }
38
41
  if (this.children.length === 0) {
@@ -46,7 +49,7 @@ export class Circuit {
46
49
  this.children.find((c) => c.componentName === "Board") ?? null
47
50
 
48
51
  if (board) {
49
- this.rootComponent = board
52
+ this.firstChild = board
50
53
  return
51
54
  }
52
55
  }
@@ -56,16 +59,14 @@ export class Circuit {
56
59
  }
57
60
 
58
61
  render() {
59
- if (!this.rootComponent) {
62
+ if (!this.firstChild) {
60
63
  this._guessRootComponent()
61
64
  }
62
- const { rootComponent, db } = this
65
+ const { firstChild, db } = this
63
66
 
64
- if (!rootComponent) throw new Error("Project has no root component")
65
-
66
- rootComponent.setProject(this)
67
-
68
- rootComponent.runRenderCycle()
67
+ if (!firstChild) throw new Error("Project has no root component")
68
+ firstChild.parent = this as any
69
+ firstChild.runRenderCycle()
69
70
  this._hasRenderedAtleastOnce = true
70
71
  }
71
72
 
@@ -103,23 +104,23 @@ export class Circuit {
103
104
  throw new Error("project.preview is not yet implemented")
104
105
  }
105
106
 
106
- computeGlobalSchematicTransform(): Matrix {
107
+ computeSchematicGlobalTransform(): Matrix {
107
108
  return identity()
108
109
  }
109
110
 
110
- computeGlobalPcbTransform(): Matrix {
111
+ computePcbGlobalTransform(): Matrix {
111
112
  return identity()
112
113
  }
113
114
 
114
115
  selectAll(selector: string): PrimitiveComponent[] {
115
- return this.rootComponent?.selectAll(selector) ?? []
116
+ return this.firstChild?.selectAll(selector) ?? []
116
117
  }
117
118
 
118
119
  selectOne(
119
120
  selector: string,
120
121
  opts?: { type?: "component" | "port" },
121
122
  ): PrimitiveComponent | null {
122
- return this.rootComponent?.selectOne(selector, opts) ?? null
123
+ return this.firstChild?.selectOne(selector, opts) ?? null
123
124
  }
124
125
  }
125
126
 
@@ -68,6 +68,7 @@ export class NormalComponent<
68
68
  */
69
69
  initPorts() {
70
70
  const { config } = this
71
+ const portsToCreate: Port[] = []
71
72
  if (config.schematicSymbolName) {
72
73
  const sym = symbols[
73
74
  `${config.schematicSymbolName}_horz` as keyof typeof symbols
@@ -79,32 +80,36 @@ export class NormalComponent<
79
80
 
80
81
  if (port) {
81
82
  port.schematicSymbolPortDef = symPort
82
- this.add(port)
83
+ portsToCreate.push(port)
83
84
  }
84
85
  }
85
86
 
87
+ this.addAll(portsToCreate)
86
88
  return
87
89
  }
88
90
 
89
91
  const portsFromFootprint = this.getPortsFromFootprint()
90
-
91
- this.addAll(portsFromFootprint)
92
+ portsToCreate.push(...portsFromFootprint)
92
93
 
93
94
  const pinLabels: Record<string, string> | undefined =
94
95
  this._parsedProps.pinLabels
95
96
  if (pinLabels) {
96
97
  for (let [pinNumber, label] of Object.entries(pinLabels)) {
97
98
  pinNumber = pinNumber.replace("pin", "")
98
- const port = this.selectOne(`port[pinNumber='${pinNumber}']`)
99
- if (!port) {
99
+ const existingPort = portsToCreate.find(
100
+ (p) => p._parsedProps.pinNumber === Number(pinNumber),
101
+ )
102
+ if (!existingPort) {
100
103
  throw new Error(
101
104
  `Could not find port for pin number ${pinNumber} in chip ${this.getString()}`,
102
105
  )
103
106
  }
104
- port.externallyAddedAliases.push(label)
105
- port.props.name = label
107
+ existingPort.externallyAddedAliases.push(label)
108
+ existingPort.props.name = label
106
109
  }
107
110
  }
111
+
112
+ this.addAll(portsToCreate)
108
113
  }
109
114
 
110
115
  _addChildrenFromStringFootprint() {
@@ -148,7 +153,7 @@ export class NormalComponent<
148
153
  doInitialSourceRender() {
149
154
  const ftype = this.config.sourceFtype
150
155
  if (!ftype) return
151
- const { db } = this.project!
156
+ const { db } = this.root!
152
157
  const { _parsedProps: props } = this
153
158
  const source_component = db.source_component.insert({
154
159
  ftype,
@@ -166,7 +171,7 @@ export class NormalComponent<
166
171
  * You can override this method to do more complicated things.
167
172
  */
168
173
  doInitialSchematicComponentRender() {
169
- const { db } = this.project!
174
+ const { db } = this.root!
170
175
  const { schematicSymbolName } = this.config
171
176
  if (!schematicSymbolName) return
172
177
  // TODO switch between horizontal and vertical based on schRotation
@@ -191,7 +196,7 @@ export class NormalComponent<
191
196
  }
192
197
 
193
198
  doInitialPcbComponentRender() {
194
- const { db } = this.project!
199
+ const { db } = this.root!
195
200
  const { _parsedProps: props } = this
196
201
  const pcb_component = db.pcb_component.insert({
197
202
  center: this.getGlobalPcbPosition(),
@@ -211,7 +216,7 @@ export class NormalComponent<
211
216
  */
212
217
  doInitialPcbComponentSizeCalculation(): void {
213
218
  if (!this.pcb_component_id) return
214
- const { db } = this.project!
219
+ const { db } = this.root!
215
220
  const { _parsedProps: props } = this
216
221
 
217
222
  let minX = Infinity
@@ -246,6 +251,10 @@ export class NormalComponent<
246
251
  }
247
252
  }
248
253
 
254
+ doInitialInitializePortsFromChildren(): void {
255
+ this.initPorts()
256
+ }
257
+
249
258
  doInitialReactSubtreesRender(): void {
250
259
  if (isReactElement(this.props.footprint)) {
251
260
  if (this.reactSubtrees.some((rs) => rs.element === this.props.footprint))
@@ -256,6 +265,20 @@ export class NormalComponent<
256
265
  }
257
266
  }
258
267
 
268
+ _hasExistingPortExactly(port1: Port): boolean {
269
+ const existingPorts = this.children.filter(
270
+ (c) => c.componentName === "Port",
271
+ ) as Port[]
272
+ return existingPorts.some((port2) => {
273
+ const aliases1 = port1.getNameAndAliases()
274
+ const aliases2 = port2.getNameAndAliases()
275
+ return (
276
+ aliases1.length === aliases2.length &&
277
+ aliases1.every((alias) => aliases2.includes(alias))
278
+ )
279
+ })
280
+ }
281
+
259
282
  add(componentOrElm: PrimitiveComponent | ReactElement) {
260
283
  let component: PrimitiveComponent
261
284
  if (isReactElement(componentOrElm)) {
@@ -266,6 +289,23 @@ export class NormalComponent<
266
289
  component = componentOrElm as PrimitiveComponent
267
290
  }
268
291
 
292
+ if (component.componentName === "Port") {
293
+ if (this._hasExistingPortExactly(component as Port)) return
294
+ // Check if this port is already contained in the children, skip if it's
295
+ // already defined
296
+ const existingPorts = this.children.filter(
297
+ (c) => c.componentName === "Port",
298
+ ) as Port[]
299
+ const conflictingPort = existingPorts.find((p) =>
300
+ p.isMatchingAnyOf(component.getNameAndAliases()),
301
+ )
302
+ if (conflictingPort) {
303
+ throw new Error(
304
+ `Conflicting ports added. Port 1: ${conflictingPort}, Port 2: ${component}`,
305
+ )
306
+ }
307
+ }
308
+
269
309
  super.add(component)
270
310
  }
271
311
 
@@ -304,6 +344,12 @@ export class NormalComponent<
304
344
  newPorts.push(newPort)
305
345
  }
306
346
 
347
+ if (newPorts.length === 0) {
348
+ throw new Error(
349
+ `No ports found in chip ${this} (define a schPortArrangement, a footprint or add portHints to elements of the footprint)`,
350
+ )
351
+ }
352
+
307
353
  return newPorts
308
354
  }
309
355
 
@@ -41,7 +41,6 @@ export abstract class PrimitiveComponent<
41
41
  }
42
42
  }
43
43
 
44
- project: Circuit | null = null
45
44
  props: z.input<ZodProps>
46
45
  _parsedProps: z.infer<ZodProps>
47
46
 
@@ -60,8 +59,7 @@ export abstract class PrimitiveComponent<
60
59
  return (
61
60
  Boolean(this.props.subcircuit) ||
62
61
  // Implied opaque group for top-level group
63
- (this.lowercaseComponentName === "group" &&
64
- this?.parent?.props?.name === "$root")
62
+ (this.lowercaseComponentName === "group" && (this?.parent as any)?.isRoot)
65
63
  )
66
64
  }
67
65
 
@@ -86,13 +84,6 @@ export abstract class PrimitiveComponent<
86
84
  }
87
85
  }
88
86
 
89
- setProject(project: Circuit) {
90
- this.project = project
91
- for (const c of this.children) {
92
- c.setProject(project)
93
- }
94
- }
95
-
96
87
  setProps(props: Partial<z.input<ZodProps>>) {
97
88
  const newProps = this.config.zodProps.parse({
98
89
  ...this.props,
@@ -227,9 +218,12 @@ export abstract class PrimitiveComponent<
227
218
  return applyToPoint(this.computeSchematicGlobalTransform(), { x: 0, y: 0 })
228
219
  }
229
220
 
221
+ get root(): Circuit | null {
222
+ return this.parent?.root ?? null
223
+ }
224
+
230
225
  onAddToParent(parent: PrimitiveComponent) {
231
226
  this.parent = parent
232
- this.project = parent.project
233
227
  }
234
228
 
235
229
  /**
@@ -3,6 +3,7 @@ import { Component, createElement, type ReactElement } from "react"
3
3
 
4
4
  export const orderedRenderPhases = [
5
5
  "ReactSubtreesRender", // probably going to be removed b/c subtrees should render instantly
6
+ "InitializePortsFromChildren",
6
7
  "CreateNetsFromProps",
7
8
  "CreateTracesFromProps",
8
9
  "SourceRender",
@@ -18,7 +18,7 @@ export class Board extends Group<typeof boardProps> {
18
18
  }
19
19
 
20
20
  doInitialPcbComponentRender(): void {
21
- const { db } = this.project!
21
+ const { db } = this.root!
22
22
  const { _parsedProps: props } = this
23
23
 
24
24
  if (!props.width || !props.height) {
@@ -36,7 +36,7 @@ export class Board extends Group<typeof boardProps> {
36
36
  }
37
37
 
38
38
  removePcbComponentRender(): void {
39
- const { db } = this.project!
39
+ const { db } = this.root!
40
40
  if (!this.pcb_board_id) return
41
41
  db.pcb_board.delete(this.pcb_board_id!)
42
42
  this.pcb_board_id = null
@@ -54,7 +54,7 @@ export class Capacitor extends NormalComponent<
54
54
  }
55
55
 
56
56
  doInitialSourceRender() {
57
- const { db } = this.project!
57
+ const { db } = this.root!
58
58
  const { _parsedProps: props } = this
59
59
  const source_component = db.source_component.insert({
60
60
  ftype: "simple_capacitor",
@@ -22,7 +22,7 @@ export class Chip<PinLabels extends string = never> extends NormalComponent<
22
22
  }
23
23
 
24
24
  doInitialSourceRender(): void {
25
- const { db } = this.project!
25
+ const { db } = this.root!
26
26
  const { _parsedProps: props } = this
27
27
 
28
28
  const source_component = db.source_component.insert({
@@ -36,7 +36,7 @@ export class Chip<PinLabels extends string = never> extends NormalComponent<
36
36
  }
37
37
 
38
38
  doInitialSchematicComponentRender() {
39
- const { db } = this.project!
39
+ const { db } = this.root!
40
40
  const { _parsedProps: props } = this
41
41
 
42
42
  const ports = this.children.filter((child) => child instanceof Port)
@@ -81,7 +81,7 @@ export class Chip<PinLabels extends string = never> extends NormalComponent<
81
81
  }
82
82
 
83
83
  doInitialPcbComponentRender() {
84
- const { db } = this.project!
84
+ const { db } = this.root!
85
85
  const { _parsedProps: props } = this
86
86
 
87
87
  const pcb_component = db.pcb_component.insert({
@@ -22,7 +22,7 @@ export class Jumper<PinLabels extends string = never> extends NormalComponent<
22
22
  }
23
23
 
24
24
  doInitialSourceRender(): void {
25
- const { db } = this.project!
25
+ const { db } = this.root!
26
26
  const { _parsedProps: props } = this
27
27
 
28
28
  const source_component = db.source_component.insert({
@@ -36,7 +36,7 @@ export class Jumper<PinLabels extends string = never> extends NormalComponent<
36
36
  }
37
37
 
38
38
  doInitialSchematicComponentRender() {
39
- const { db } = this.project!
39
+ const { db } = this.root!
40
40
  const { _parsedProps: props } = this
41
41
 
42
42
  const ports = this.children.filter((child) => child instanceof Port)
@@ -84,7 +84,7 @@ export class Jumper<PinLabels extends string = never> extends NormalComponent<
84
84
  }
85
85
 
86
86
  doInitialPcbComponentRender() {
87
- const { db } = this.project!
87
+ const { db } = this.root!
88
88
  const { _parsedProps: props } = this
89
89
 
90
90
  const pcb_component = db.pcb_component.insert({
@@ -43,7 +43,7 @@ export class Resistor extends NormalComponent<
43
43
  }
44
44
 
45
45
  doInitialSourceRender() {
46
- const { db } = this.project!
46
+ const { db } = this.root!
47
47
  const { _parsedProps: props } = this
48
48
  const source_component = db.source_component.insert({
49
49
  ftype: "simple_resistor",
@@ -18,7 +18,7 @@ export class Net extends PrimitiveComponent<typeof netProps> {
18
18
  }
19
19
 
20
20
  doInitialSourceComponentRender(): void {
21
- const { db } = this.project!
21
+ const { db } = this.root!
22
22
  const { _parsedProps: props } = this
23
23
 
24
24
  const net = db.source_net.insert({
@@ -77,7 +77,7 @@ export class Net extends PrimitiveComponent<typeof netProps> {
77
77
  * such that the nets are fully connected
78
78
  */
79
79
  doInitialPcbRouteNetIslands(): void {
80
- const { db } = this.project!
80
+ const { db } = this.root!
81
81
  const { _parsedProps: props } = this
82
82
 
83
83
  const traces = this._getAllDirectlyConnectedTraces().filter(
@@ -46,7 +46,7 @@ export class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
46
46
  }
47
47
 
48
48
  doInitialPcbPrimitiveRender(): void {
49
- const { db } = this.project!
49
+ const { db } = this.root!
50
50
  const { _parsedProps: props } = this
51
51
  if (!props.portHints) return
52
52
  const position = this.getGlobalPcbPosition()
@@ -116,7 +116,7 @@ export class Port extends PrimitiveComponent<typeof portProps> {
116
116
  }
117
117
 
118
118
  doInitialSourceRender(): void {
119
- const { db } = this.project!
119
+ const { db } = this.root!
120
120
  const { _parsedProps: props } = this
121
121
 
122
122
  const port_hints = this.getNameAndAliases()
@@ -132,7 +132,7 @@ export class Port extends PrimitiveComponent<typeof portProps> {
132
132
  }
133
133
 
134
134
  doInitialSourceParentAttachment(): void {
135
- const { db } = this.project!
135
+ const { db } = this.root!
136
136
  if (!this.parent?.source_component_id) {
137
137
  throw new Error(
138
138
  `${this.getString()} has no parent source component (parent: ${this.parent?.getString()})`,
@@ -152,7 +152,7 @@ export class Port extends PrimitiveComponent<typeof portProps> {
152
152
  * to exist)
153
153
  */
154
154
  doInitialPcbPortRender(): void {
155
- const { db } = this.project!
155
+ const { db } = this.root!
156
156
  const { matchedComponents } = this
157
157
 
158
158
  if (!this.parent?.pcb_component_id) {
@@ -191,7 +191,7 @@ export class Port extends PrimitiveComponent<typeof portProps> {
191
191
  }
192
192
 
193
193
  doInitialSchematicPortRender(): void {
194
- const { db } = this.project!
194
+ const { db } = this.root!
195
195
  const { _parsedProps: props } = this
196
196
 
197
197
  if (!this.parent) return
@@ -14,7 +14,7 @@ export class SilkscreenPath extends PrimitiveComponent<
14
14
  }
15
15
 
16
16
  doInitialPcbPrimitiveRender(): void {
17
- const { db } = this.project!
17
+ const { db } = this.root!
18
18
  const { _parsedProps: props } = this
19
19
 
20
20
  const layer = props.layer ?? "top"
@@ -50,7 +50,7 @@ export class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
50
50
  }
51
51
 
52
52
  doInitialPcbPrimitiveRender(): void {
53
- const { db } = this.project!
53
+ const { db } = this.root!
54
54
  const { _parsedProps: props } = this
55
55
  if (!props.portHints) return
56
56
  const position = this.getGlobalPcbPosition()
@@ -104,7 +104,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
104
104
  ports?: undefined
105
105
  portsWithSelectors?: undefined
106
106
  } {
107
- const { db } = this.project!
107
+ const { db } = this.root!
108
108
  const { _parsedProps: props, parent } = this
109
109
 
110
110
  if (!parent) throw new Error("Trace has no parent")
@@ -215,7 +215,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
215
215
  }
216
216
 
217
217
  doInitialSourceTraceRender(): void {
218
- const { db } = this.project!
218
+ const { db } = this.root!
219
219
  const { _parsedProps: props, parent } = this
220
220
 
221
221
  if (!parent) {
@@ -238,7 +238,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
238
238
  }
239
239
 
240
240
  doInitialPcbTraceRender(): void {
241
- const { db } = this.project!
241
+ const { db } = this.root!
242
242
  const { _parsedProps: props, parent } = this
243
243
 
244
244
  if (!parent) throw new Error("Trace has no parent")
@@ -365,7 +365,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
365
365
 
366
366
  // Cache the PCB obstacles, they'll be needed for each segment between
367
367
  // ports/hints
368
- const obstacles = getObstaclesFromSoup(this.project!.db.toArray())
368
+ const obstacles = getObstaclesFromSoup(this.root!.db.toArray())
369
369
  markObstaclesAsConnected(
370
370
  obstacles,
371
371
  orderedRouteObjectives,
@@ -445,7 +445,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
445
445
  }
446
446
 
447
447
  doInitialSchematicTraceRender(): void {
448
- const { db } = this.project!
448
+ const { db } = this.root!
449
449
  const { _parsedProps: props, parent } = this
450
450
 
451
451
  if (!parent) throw new Error("Trace has no parent")
@@ -8,7 +8,7 @@ export class TraceHint extends PrimitiveComponent<typeof traceHintProps> {
8
8
  matchedPort: Port | null = null
9
9
 
10
10
  doInitialPortMatching(): void {
11
- const { db } = this.project!
11
+ const { db } = this.root!
12
12
  const { _parsedProps: props, parent } = this
13
13
 
14
14
  if (!parent) return
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tscircuit/core",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.0.29",
5
+ "version": "0.0.30",
6
6
  "types": "dist/index.d.ts",
7
7
  "main": "dist/index.js",
8
8
  "files": [
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@tscircuit/infgrid-ijump-astar": "^0.0.6",
35
- "@tscircuit/props": "^0.0.51",
35
+ "@tscircuit/props": "^0.0.52",
36
36
  "@tscircuit/soup": "^0.0.58",
37
37
  "@tscircuit/soup-util": "0.0.18",
38
38
  "footprinter": "^0.0.44",