@tscircuit/core 0.0.994 → 0.0.995

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +1162 -4
  2. package/dist/index.js +181 -60
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -78,6 +78,7 @@ __export(components_exports, {
78
78
  SmtPad: () => SmtPad,
79
79
  SolderJumper: () => SolderJumper,
80
80
  Subcircuit: () => Subcircuit,
81
+ Subpanel: () => Subpanel,
81
82
  Switch: () => Switch,
82
83
  Symbol: () => SymbolComponent,
83
84
  TestPoint: () => TestPoint,
@@ -16682,6 +16683,10 @@ var Board = class extends Group6 {
16682
16683
 
16683
16684
  // lib/components/normal-components/Panel.ts
16684
16685
  import { panelProps } from "@tscircuit/props";
16686
+ import { distance as distance12 } from "circuit-json";
16687
+
16688
+ // lib/components/normal-components/Subpanel.ts
16689
+ import { subpanelProps } from "@tscircuit/props";
16685
16690
  import { distance as distance11 } from "circuit-json";
16686
16691
 
16687
16692
  // lib/utils/panels/generate-panel-tabs-and-mouse-bites.ts
@@ -17017,16 +17022,19 @@ var packBoardsIntoGrid = ({
17017
17022
  return { positions, gridWidth: totalGridWidth, gridHeight: totalGridHeight };
17018
17023
  };
17019
17024
 
17020
- // lib/components/normal-components/Panel.ts
17021
- var Panel = class extends Group6 {
17025
+ // lib/components/normal-components/Subpanel.ts
17026
+ var Subpanel = class _Subpanel extends Group6 {
17022
17027
  pcb_panel_id = null;
17023
17028
  _tabsAndMouseBitesGenerated = false;
17024
17029
  get config() {
17025
17030
  return {
17026
- componentName: "Panel",
17027
- zodProps: panelProps
17031
+ componentName: "Subpanel",
17032
+ zodProps: subpanelProps
17028
17033
  };
17029
17034
  }
17035
+ get _errorComponentName() {
17036
+ return this.componentName.toLowerCase();
17037
+ }
17030
17038
  get isGroup() {
17031
17039
  return true;
17032
17040
  }
@@ -17034,19 +17042,55 @@ var Panel = class extends Group6 {
17034
17042
  return true;
17035
17043
  }
17036
17044
  add(component) {
17037
- if (component.lowercaseComponentName !== "board") {
17038
- throw new Error("<panel> can only contain <board> elements");
17045
+ if (component.lowercaseComponentName !== "board" && component.lowercaseComponentName !== "subpanel") {
17046
+ throw new Error(
17047
+ `<${this._errorComponentName}> can only contain <board> or <subpanel> elements`
17048
+ );
17039
17049
  }
17040
17050
  super.add(component);
17041
17051
  }
17042
17052
  _cachedGridWidth = 0;
17043
17053
  _cachedGridHeight = 0;
17054
+ /**
17055
+ * Get all board instances from this subpanel and nested subpanels
17056
+ */
17057
+ _getAllBoardInstances() {
17058
+ const boards = [];
17059
+ for (const child of this.children) {
17060
+ if (child instanceof Board) {
17061
+ boards.push(child);
17062
+ } else if (child instanceof _Subpanel) {
17063
+ boards.push(...child._getAllBoardInstances());
17064
+ }
17065
+ }
17066
+ return boards;
17067
+ }
17068
+ /**
17069
+ * Check if this subpanel contains at least one board (directly or through nested subpanels)
17070
+ */
17071
+ _containsBoards() {
17072
+ for (const child of this.children) {
17073
+ if (child.componentName === "Board") {
17074
+ return true;
17075
+ }
17076
+ if (child.componentName === "Subpanel" && "_containsBoards" in child) {
17077
+ if (child._containsBoards()) {
17078
+ return true;
17079
+ }
17080
+ }
17081
+ }
17082
+ return false;
17083
+ }
17084
+ /**
17085
+ * Get direct board children only (not from nested subpanels)
17086
+ */
17087
+ _getDirectBoardChildren() {
17088
+ return this.children.filter((c) => c instanceof Board);
17089
+ }
17044
17090
  doInitialPanelBoardLayout() {
17045
17091
  if (this.root?.pcbDisabled) return;
17046
17092
  const layoutMode = this._parsedProps.layoutMode ?? "none";
17047
- const childBoardInstances = this.children.filter(
17048
- (c) => c instanceof Board
17049
- );
17093
+ const childBoardInstances = this._getDirectBoardChildren();
17050
17094
  if (layoutMode !== "none") {
17051
17095
  for (const board of childBoardInstances) {
17052
17096
  const hasPcbX = board._parsedProps.pcbX !== void 0;
@@ -17059,7 +17103,7 @@ var Panel = class extends Group6 {
17059
17103
  this.root.db.source_property_ignored_warning.insert({
17060
17104
  source_component_id: board.source_component_id,
17061
17105
  property_name: propertyNames,
17062
- message: `Board has manual positioning (${propertyNames}) but panel layout mode is "${layoutMode}". Manual positioning will be ignored.`,
17106
+ message: `Board has manual positioning (${propertyNames}) but ${this._errorComponentName} layout mode is "${layoutMode}". Manual positioning will be ignored.`,
17063
17107
  error_type: "source_property_ignored_warning"
17064
17108
  });
17065
17109
  }
@@ -17074,7 +17118,7 @@ var Panel = class extends Group6 {
17074
17118
  if (boardsWithoutPosition.length > 1) {
17075
17119
  this.root.db.pcb_placement_error.insert({
17076
17120
  error_type: "pcb_placement_error",
17077
- message: `Multiple boards in panel without pcbX/pcbY positions. When layoutMode="none", each board must have explicit pcbX and pcbY coordinates to avoid overlapping. Either set pcbX/pcbY on each board, or use layoutMode="grid" for automatic positioning.`
17121
+ message: `Multiple boards in ${this._errorComponentName} without pcbX/pcbY positions. When layoutMode="none", each board must have explicit pcbX and pcbY coordinates to avoid overlapping. Either set pcbX/pcbY on each board, or use layoutMode="grid" for automatic positioning.`
17078
17122
  });
17079
17123
  }
17080
17124
  }
@@ -17098,9 +17142,7 @@ var Panel = class extends Group6 {
17098
17142
  doInitialPanelLayout() {
17099
17143
  if (this.root?.pcbDisabled) return;
17100
17144
  const { db } = this.root;
17101
- const childBoardInstances = this.children.filter(
17102
- (c) => c instanceof Board
17103
- );
17145
+ const childBoardInstances = this._getDirectBoardChildren();
17104
17146
  const layoutMode = this._parsedProps.layoutMode ?? "none";
17105
17147
  if (layoutMode === "grid") {
17106
17148
  for (const board of childBoardInstances) {
@@ -17111,39 +17153,7 @@ var Panel = class extends Group6 {
17111
17153
  display_offset_y: `${board._panelPositionOffset.y}mm`
17112
17154
  });
17113
17155
  }
17114
- const hasExplicitWidth = this._parsedProps.width !== void 0;
17115
- const hasExplicitHeight = this._parsedProps.height !== void 0;
17116
- const gridWidth = this._cachedGridWidth;
17117
- const gridHeight = this._cachedGridHeight;
17118
- if (hasExplicitWidth && hasExplicitHeight) {
17119
- db.pcb_panel.update(this.pcb_panel_id, {
17120
- width: distance11.parse(this._parsedProps.width),
17121
- height: distance11.parse(this._parsedProps.height)
17122
- });
17123
- } else if (gridWidth > 0 || gridHeight > 0) {
17124
- const {
17125
- edgePadding: edgePaddingProp,
17126
- edgePaddingLeft: edgePaddingLeftProp,
17127
- edgePaddingRight: edgePaddingRightProp,
17128
- edgePaddingTop: edgePaddingTopProp,
17129
- edgePaddingBottom: edgePaddingBottomProp
17130
- } = this._parsedProps;
17131
- const edgePadding = distance11.parse(edgePaddingProp ?? 5);
17132
- const edgePaddingLeft = distance11.parse(
17133
- edgePaddingLeftProp ?? edgePadding
17134
- );
17135
- const edgePaddingRight = distance11.parse(
17136
- edgePaddingRightProp ?? edgePadding
17137
- );
17138
- const edgePaddingTop = distance11.parse(edgePaddingTopProp ?? edgePadding);
17139
- const edgePaddingBottom = distance11.parse(
17140
- edgePaddingBottomProp ?? edgePadding
17141
- );
17142
- db.pcb_panel.update(this.pcb_panel_id, {
17143
- width: hasExplicitWidth ? distance11.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
17144
- height: hasExplicitHeight ? distance11.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
17145
- });
17146
- }
17156
+ this._updatePanelDimensions();
17147
17157
  } else {
17148
17158
  const panelGlobalPos = this._getGlobalPcbPositionBeforeLayout();
17149
17159
  for (const board of childBoardInstances) {
@@ -17158,9 +17168,55 @@ var Panel = class extends Group6 {
17158
17168
  });
17159
17169
  }
17160
17170
  }
17171
+ this._generateTabsAndMouseBites();
17172
+ }
17173
+ /**
17174
+ * Update dimensions for the subpanel. Subpanel updates pcb_group,
17175
+ */
17176
+ _updatePanelDimensions() {
17177
+ const { db } = this.root;
17178
+ const hasExplicitWidth = this._parsedProps.width !== void 0;
17179
+ const hasExplicitHeight = this._parsedProps.height !== void 0;
17180
+ const gridWidth = this._cachedGridWidth;
17181
+ const gridHeight = this._cachedGridHeight;
17182
+ if (!this.pcb_group_id) return;
17183
+ if (hasExplicitWidth && hasExplicitHeight) {
17184
+ db.pcb_group.update(this.pcb_group_id, {
17185
+ width: distance11.parse(this._parsedProps.width),
17186
+ height: distance11.parse(this._parsedProps.height)
17187
+ });
17188
+ } else if (gridWidth > 0 || gridHeight > 0) {
17189
+ const {
17190
+ edgePadding: edgePaddingProp,
17191
+ edgePaddingLeft: edgePaddingLeftProp,
17192
+ edgePaddingRight: edgePaddingRightProp,
17193
+ edgePaddingTop: edgePaddingTopProp,
17194
+ edgePaddingBottom: edgePaddingBottomProp
17195
+ } = this._parsedProps;
17196
+ const edgePadding = distance11.parse(edgePaddingProp ?? 5);
17197
+ const edgePaddingLeft = distance11.parse(edgePaddingLeftProp ?? edgePadding);
17198
+ const edgePaddingRight = distance11.parse(
17199
+ edgePaddingRightProp ?? edgePadding
17200
+ );
17201
+ const edgePaddingTop = distance11.parse(edgePaddingTopProp ?? edgePadding);
17202
+ const edgePaddingBottom = distance11.parse(
17203
+ edgePaddingBottomProp ?? edgePadding
17204
+ );
17205
+ db.pcb_group.update(this.pcb_group_id, {
17206
+ width: hasExplicitWidth ? distance11.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
17207
+ height: hasExplicitHeight ? distance11.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
17208
+ });
17209
+ }
17210
+ }
17211
+ /**
17212
+ * Generate tabs and mouse bites for panelization
17213
+ */
17214
+ _generateTabsAndMouseBites() {
17161
17215
  if (this._tabsAndMouseBitesGenerated) return;
17216
+ const { db } = this.root;
17162
17217
  const props = this._parsedProps;
17163
17218
  const panelizationMethod = props.panelizationMethod ?? "none";
17219
+ const childBoardInstances = this._getDirectBoardChildren();
17164
17220
  if (panelizationMethod !== "none") {
17165
17221
  const childBoardIds = childBoardInstances.map((c) => c.pcb_board_id).filter((id) => !!id);
17166
17222
  const boardsInPanel = db.pcb_board.list().filter((b) => childBoardIds.includes(b.pcb_board_id));
@@ -17185,24 +17241,88 @@ var Panel = class extends Group6 {
17185
17241
  }
17186
17242
  this._tabsAndMouseBitesGenerated = true;
17187
17243
  }
17188
- runRenderCycle() {
17189
- if (!this.children.some((child) => child.componentName === "Board")) {
17190
- throw new Error("<panel> must contain at least one <board>");
17244
+ /**
17245
+ * Override to validate board containment before rendering.
17246
+ * Subpanel uses parent Group's pcb_group rendering.
17247
+ */
17248
+ doInitialPcbComponentRender() {
17249
+ if (this.root?.pcbDisabled) return;
17250
+ if (!this._containsBoards()) {
17251
+ throw new Error(
17252
+ `<${this._errorComponentName}> must contain at least one <board>`
17253
+ );
17191
17254
  }
17192
- super.runRenderCycle();
17255
+ super.doInitialPcbComponentRender();
17193
17256
  }
17257
+ };
17258
+
17259
+ // lib/components/normal-components/Panel.ts
17260
+ var Panel = class extends Subpanel {
17261
+ get config() {
17262
+ return {
17263
+ componentName: "Panel",
17264
+ zodProps: panelProps
17265
+ };
17266
+ }
17267
+ /**
17268
+ * Panel creates a pcb_panel record for the physical manufacturing panel.
17269
+ * This overrides the Subpanel behavior which uses pcb_group.
17270
+ */
17194
17271
  doInitialPcbComponentRender() {
17195
17272
  if (this.root?.pcbDisabled) return;
17273
+ if (!this._containsBoards()) {
17274
+ throw new Error(
17275
+ `<${this._errorComponentName}> must contain at least one <board>`
17276
+ );
17277
+ }
17196
17278
  const { db } = this.root;
17197
17279
  const props = this._parsedProps;
17198
17280
  const inserted = db.pcb_panel.insert({
17199
- width: props.width !== void 0 ? distance11.parse(props.width) : 0,
17200
- height: props.height !== void 0 ? distance11.parse(props.height) : 0,
17281
+ width: props.width !== void 0 ? distance12.parse(props.width) : 0,
17282
+ height: props.height !== void 0 ? distance12.parse(props.height) : 0,
17201
17283
  center: this._getGlobalPcbPositionBeforeLayout(),
17202
17284
  covered_with_solder_mask: !(props.noSolderMask ?? false)
17203
17285
  });
17204
17286
  this.pcb_panel_id = inserted.pcb_panel_id;
17205
17287
  }
17288
+ /**
17289
+ * Panel updates pcb_panel dimensions instead of pcb_group
17290
+ */
17291
+ _updatePanelDimensions() {
17292
+ const { db } = this.root;
17293
+ const hasExplicitWidth = this._parsedProps.width !== void 0;
17294
+ const hasExplicitHeight = this._parsedProps.height !== void 0;
17295
+ const gridWidth = this._cachedGridWidth;
17296
+ const gridHeight = this._cachedGridHeight;
17297
+ if (!this.pcb_panel_id) return;
17298
+ if (hasExplicitWidth && hasExplicitHeight) {
17299
+ db.pcb_panel.update(this.pcb_panel_id, {
17300
+ width: distance12.parse(this._parsedProps.width),
17301
+ height: distance12.parse(this._parsedProps.height)
17302
+ });
17303
+ } else if (gridWidth > 0 || gridHeight > 0) {
17304
+ const {
17305
+ edgePadding: edgePaddingProp,
17306
+ edgePaddingLeft: edgePaddingLeftProp,
17307
+ edgePaddingRight: edgePaddingRightProp,
17308
+ edgePaddingTop: edgePaddingTopProp,
17309
+ edgePaddingBottom: edgePaddingBottomProp
17310
+ } = this._parsedProps;
17311
+ const edgePadding = distance12.parse(edgePaddingProp ?? 5);
17312
+ const edgePaddingLeft = distance12.parse(edgePaddingLeftProp ?? edgePadding);
17313
+ const edgePaddingRight = distance12.parse(
17314
+ edgePaddingRightProp ?? edgePadding
17315
+ );
17316
+ const edgePaddingTop = distance12.parse(edgePaddingTopProp ?? edgePadding);
17317
+ const edgePaddingBottom = distance12.parse(
17318
+ edgePaddingBottomProp ?? edgePadding
17319
+ );
17320
+ db.pcb_panel.update(this.pcb_panel_id, {
17321
+ width: hasExplicitWidth ? distance12.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
17322
+ height: hasExplicitHeight ? distance12.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
17323
+ });
17324
+ }
17325
+ }
17206
17326
  updatePcbComponentRender() {
17207
17327
  if (this.root?.pcbDisabled) return;
17208
17328
  if (!this.pcb_panel_id) return;
@@ -17210,8 +17330,8 @@ var Panel = class extends Group6 {
17210
17330
  const props = this._parsedProps;
17211
17331
  const currentPanel = db.pcb_panel.get(this.pcb_panel_id);
17212
17332
  db.pcb_panel.update(this.pcb_panel_id, {
17213
- width: props.width !== void 0 ? distance11.parse(props.width) : currentPanel?.width,
17214
- height: props.height !== void 0 ? distance11.parse(props.height) : currentPanel?.height,
17333
+ width: props.width !== void 0 ? distance12.parse(props.width) : currentPanel?.width,
17334
+ height: props.height !== void 0 ? distance12.parse(props.height) : currentPanel?.height,
17215
17335
  center: this._getGlobalPcbPositionBeforeLayout(),
17216
17336
  covered_with_solder_mask: !(props.noSolderMask ?? false)
17217
17337
  });
@@ -19223,7 +19343,7 @@ var SilkscreenLine = class extends PrimitiveComponent2 {
19223
19343
 
19224
19344
  // lib/components/primitive-components/Fiducial.ts
19225
19345
  import "zod";
19226
- import { distance as distance12 } from "circuit-json";
19346
+ import { distance as distance13 } from "circuit-json";
19227
19347
  import { fiducialProps } from "@tscircuit/props";
19228
19348
  var Fiducial = class extends PrimitiveComponent2 {
19229
19349
  pcb_smtpad_id = null;
@@ -19248,15 +19368,15 @@ var Fiducial = class extends PrimitiveComponent2 {
19248
19368
  shape: "circle",
19249
19369
  x: position.x,
19250
19370
  y: position.y,
19251
- radius: distance12.parse(props.padDiameter) / 2,
19252
- soldermask_margin: props.soldermaskPullback ? distance12.parse(props.soldermaskPullback) : distance12.parse(props.padDiameter) / 2,
19371
+ radius: distance13.parse(props.padDiameter) / 2,
19372
+ soldermask_margin: props.soldermaskPullback ? distance13.parse(props.soldermaskPullback) : distance13.parse(props.padDiameter) / 2,
19253
19373
  is_covered_with_solder_mask: true
19254
19374
  });
19255
19375
  this.pcb_smtpad_id = pcb_smtpad.pcb_smtpad_id;
19256
19376
  }
19257
19377
  getPcbSize() {
19258
19378
  const { _parsedProps: props } = this;
19259
- const d = distance12.parse(props.padDiameter);
19379
+ const d = distance13.parse(props.padDiameter);
19260
19380
  return { width: d, height: d };
19261
19381
  }
19262
19382
  _setPositionFromLayout(newCenter) {
@@ -21197,7 +21317,7 @@ import { identity as identity5 } from "transformation-matrix";
21197
21317
  var package_default = {
21198
21318
  name: "@tscircuit/core",
21199
21319
  type: "module",
21200
- version: "0.0.993",
21320
+ version: "0.0.994",
21201
21321
  types: "dist/index.d.ts",
21202
21322
  main: "dist/index.js",
21203
21323
  module: "dist/index.js",
@@ -21847,6 +21967,7 @@ export {
21847
21967
  SmtPad,
21848
21968
  SolderJumper,
21849
21969
  Subcircuit,
21970
+ Subpanel,
21850
21971
  Switch,
21851
21972
  SymbolComponent as Symbol,
21852
21973
  TestPoint,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.994",
4
+ "version": "0.0.995",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",