@tscircuit/pcb-viewer 1.11.267 → 1.11.269

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
@@ -6277,7 +6277,9 @@ import { useCallback } from "react";
6277
6277
  var STORAGE_KEYS = {
6278
6278
  IS_SHOWING_PCB_GROUPS: "pcb_viewer_is_showing_pcb_groups",
6279
6279
  PCB_GROUP_VIEW_MODE: "pcb_viewer_group_view_mode",
6280
- IS_SHOWING_COPPER_POURS: "pcb_viewer_is_showing_copper_pours"
6280
+ IS_SHOWING_COPPER_POURS: "pcb_viewer_is_showing_copper_pours",
6281
+ IS_SHOWING_GROUP_ANCHOR_OFFSETS: "pcb_viewer_is_showing_group_anchor_offsets",
6282
+ IS_SHOWING_SOLDER_MASK: "pcb_viewer_is_showing_solder_mask"
6281
6283
  };
6282
6284
  var getStoredBoolean = (key, defaultValue) => {
6283
6285
  if (typeof window === "undefined") return defaultValue;
@@ -6333,6 +6335,14 @@ var createStore = (initialState = {}, disablePcbGroups = false) => createZustand
6333
6335
  true
6334
6336
  ),
6335
6337
  is_showing_pcb_groups: disablePcbGroups ? false : getStoredBoolean(STORAGE_KEYS.IS_SHOWING_PCB_GROUPS, true),
6338
+ is_showing_group_anchor_offsets: getStoredBoolean(
6339
+ STORAGE_KEYS.IS_SHOWING_GROUP_ANCHOR_OFFSETS,
6340
+ true
6341
+ ),
6342
+ is_showing_solder_mask: getStoredBoolean(
6343
+ STORAGE_KEYS.IS_SHOWING_SOLDER_MASK,
6344
+ false
6345
+ ),
6336
6346
  pcb_group_view_mode: disablePcbGroups ? "all" : getStoredString(
6337
6347
  STORAGE_KEYS.PCB_GROUP_VIEW_MODE,
6338
6348
  DEFAULT_PCB_GROUP_VIEW_MODE
@@ -6363,6 +6373,17 @@ var createStore = (initialState = {}, disablePcbGroups = false) => createZustand
6363
6373
  setStoredBoolean(STORAGE_KEYS.IS_SHOWING_PCB_GROUPS, is_showing);
6364
6374
  set({ is_showing_pcb_groups: is_showing });
6365
6375
  },
6376
+ setIsShowingGroupAnchorOffsets: (is_showing) => {
6377
+ setStoredBoolean(
6378
+ STORAGE_KEYS.IS_SHOWING_GROUP_ANCHOR_OFFSETS,
6379
+ is_showing
6380
+ );
6381
+ set({ is_showing_group_anchor_offsets: is_showing });
6382
+ },
6383
+ setIsShowingSolderMask: (is_showing) => {
6384
+ setStoredBoolean(STORAGE_KEYS.IS_SHOWING_SOLDER_MASK, is_showing);
6385
+ set({ is_showing_solder_mask: is_showing });
6386
+ },
6366
6387
  setPcbGroupViewMode: (mode) => {
6367
6388
  if (disablePcbGroups) return;
6368
6389
  setStoredString(STORAGE_KEYS.PCB_GROUP_VIEW_MODE, mode);
@@ -6766,6 +6787,7 @@ var useMouseMatrixTransform = (props = {}) => {
6766
6787
  var src_default = useMouseMatrixTransform;
6767
6788
 
6768
6789
  // node_modules/circuit-json-to-connectivity-map/dist/index.js
6790
+ import { doesLineIntersectLine } from "@tscircuit/math-utils";
6769
6791
  function findConnectedNetworks(connections) {
6770
6792
  const networks = /* @__PURE__ */ new Map();
6771
6793
  let netCounter = 0;
@@ -7052,12 +7074,12 @@ function getExpandedStroke(strokeInput, defaultWidth) {
7052
7074
  }
7053
7075
 
7054
7076
  // src/lib/convert-element-to-primitive.ts
7055
- import { distance as distance2 } from "circuit-json";
7077
+ import { distance } from "circuit-json";
7056
7078
  var globalPcbDrawingObjectCount = 0;
7057
7079
  var getNewPcbDrawingObjectId = (prefix) => `${prefix}_${globalPcbDrawingObjectCount++}`;
7058
7080
  var normalizePolygonPoints = (points) => (points ?? []).map((point) => ({
7059
- x: distance2.parse(point.x),
7060
- y: distance2.parse(point.y)
7081
+ x: distance.parse(point.x),
7082
+ y: distance.parse(point.y)
7061
7083
  }));
7062
7084
  var convertElementToPrimitives = (element, allElements) => {
7063
7085
  const _parent_pcb_component = "pcb_component_id" in element ? allElements.find(
@@ -7126,76 +7148,123 @@ var convertElementToPrimitives = (element, allElements) => {
7126
7148
  }
7127
7149
  case "pcb_board": {
7128
7150
  const { width, height, center, outline } = element;
7151
+ const primitives = [];
7152
+ const hasSolderMask = allElements.some(
7153
+ (elm) => elm.type === "pcb_smtpad" && elm.is_covered_with_solder_mask === true
7154
+ );
7155
+ if (hasSolderMask) {
7156
+ if (outline && outline.length > 2) {
7157
+ primitives.push({
7158
+ _pcb_drawing_object_id: `polygon_${globalPcbDrawingObjectCount++}`,
7159
+ pcb_drawing_type: "polygon",
7160
+ points: normalizePolygonPoints(outline),
7161
+ layer: "soldermask_top",
7162
+ _element: element
7163
+ });
7164
+ primitives.push({
7165
+ _pcb_drawing_object_id: `polygon_${globalPcbDrawingObjectCount++}`,
7166
+ pcb_drawing_type: "polygon",
7167
+ points: normalizePolygonPoints(outline),
7168
+ layer: "soldermask_bottom",
7169
+ _element: element
7170
+ });
7171
+ } else if (width && height) {
7172
+ primitives.push({
7173
+ _pcb_drawing_object_id: `rect_${globalPcbDrawingObjectCount++}`,
7174
+ pcb_drawing_type: "rect",
7175
+ x: center.x,
7176
+ y: center.y,
7177
+ w: width,
7178
+ h: height,
7179
+ layer: "soldermask_top",
7180
+ _element: element
7181
+ });
7182
+ primitives.push({
7183
+ _pcb_drawing_object_id: `rect_${globalPcbDrawingObjectCount++}`,
7184
+ pcb_drawing_type: "rect",
7185
+ x: center.x,
7186
+ y: center.y,
7187
+ w: width,
7188
+ h: height,
7189
+ layer: "soldermask_bottom",
7190
+ _element: element
7191
+ });
7192
+ }
7193
+ }
7129
7194
  if (outline && outline.length > 2) {
7130
- return outline.map((point, index, array) => ({
7131
- _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7132
- pcb_drawing_type: "line",
7133
- x1: point.x,
7134
- y1: point.y,
7135
- x2: index === array.length - 1 ? array[0].x : array[index + 1].x,
7136
- y2: index === array.length - 1 ? array[0].y : array[index + 1].y,
7137
- width: 1,
7138
- zoomIndependent: true,
7139
- layer: "board",
7140
- _element: element
7141
- }));
7195
+ primitives.push(
7196
+ ...outline.map((point, index, array) => ({
7197
+ _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7198
+ pcb_drawing_type: "line",
7199
+ x1: point.x,
7200
+ y1: point.y,
7201
+ x2: index === array.length - 1 ? array[0].x : array[index + 1].x,
7202
+ y2: index === array.length - 1 ? array[0].y : array[index + 1].y,
7203
+ width: 1,
7204
+ zoomIndependent: true,
7205
+ layer: "board",
7206
+ _element: element
7207
+ }))
7208
+ );
7209
+ } else {
7210
+ primitives.push(
7211
+ {
7212
+ _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7213
+ pcb_drawing_type: "line",
7214
+ x1: center.x - width / 2,
7215
+ y1: center.y - height / 2,
7216
+ x2: center.x + width / 2,
7217
+ y2: center.y - height / 2,
7218
+ width: 1,
7219
+ zoomIndependent: true,
7220
+ layer: "board",
7221
+ _element: element
7222
+ },
7223
+ {
7224
+ _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7225
+ pcb_drawing_type: "line",
7226
+ x1: center.x - width / 2,
7227
+ y1: center.y + height / 2,
7228
+ x2: center.x + width / 2,
7229
+ y2: center.y + height / 2,
7230
+ width: 1,
7231
+ zoomIndependent: true,
7232
+ layer: "board",
7233
+ _element: element
7234
+ },
7235
+ {
7236
+ _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7237
+ pcb_drawing_type: "line",
7238
+ x1: center.x - width / 2,
7239
+ y1: center.y - height / 2,
7240
+ x2: center.x - width / 2,
7241
+ y2: center.y + height / 2,
7242
+ width: 1,
7243
+ zoomIndependent: true,
7244
+ layer: "board",
7245
+ _element: element
7246
+ },
7247
+ {
7248
+ _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7249
+ pcb_drawing_type: "line",
7250
+ x1: center.x + width / 2,
7251
+ y1: center.y - height / 2,
7252
+ x2: center.x + width / 2,
7253
+ y2: center.y + height / 2,
7254
+ width: 1,
7255
+ zoomIndependent: true,
7256
+ layer: "board",
7257
+ _element: element
7258
+ }
7259
+ );
7142
7260
  }
7143
- return [
7144
- {
7145
- _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7146
- pcb_drawing_type: "line",
7147
- x1: center.x - width / 2,
7148
- y1: center.y - height / 2,
7149
- x2: center.x + width / 2,
7150
- y2: center.y - height / 2,
7151
- width: 1,
7152
- zoomIndependent: true,
7153
- layer: "board",
7154
- _element: element
7155
- },
7156
- {
7157
- _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7158
- pcb_drawing_type: "line",
7159
- x1: center.x - width / 2,
7160
- y1: center.y + height / 2,
7161
- x2: center.x + width / 2,
7162
- y2: center.y + height / 2,
7163
- width: 1,
7164
- zoomIndependent: true,
7165
- layer: "board",
7166
- _element: element
7167
- },
7168
- {
7169
- _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7170
- pcb_drawing_type: "line",
7171
- x1: center.x - width / 2,
7172
- y1: center.y - height / 2,
7173
- x2: center.x - width / 2,
7174
- y2: center.y + height / 2,
7175
- width: 1,
7176
- zoomIndependent: true,
7177
- layer: "board",
7178
- _element: element
7179
- },
7180
- {
7181
- _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7182
- pcb_drawing_type: "line",
7183
- x1: center.x + width / 2,
7184
- y1: center.y - height / 2,
7185
- x2: center.x + width / 2,
7186
- y2: center.y + height / 2,
7187
- width: 1,
7188
- zoomIndependent: true,
7189
- layer: "board",
7190
- _element: element
7191
- }
7192
- ];
7261
+ return primitives;
7193
7262
  }
7194
7263
  case "pcb_smtpad": {
7195
7264
  if (element.shape === "rect" || element.shape === "rotated_rect") {
7196
7265
  const { shape, x, y, width, height, layer, rect_border_radius } = element;
7197
7266
  const corner_radius = element.corner_radius ?? rect_border_radius ?? 0;
7198
- return [
7267
+ const primitives = [
7199
7268
  {
7200
7269
  _pcb_drawing_object_id: `rect_${globalPcbDrawingObjectCount++}`,
7201
7270
  pcb_drawing_type: "rect",
@@ -7212,9 +7281,32 @@ var convertElementToPrimitives = (element, allElements) => {
7212
7281
  roundness: corner_radius
7213
7282
  }
7214
7283
  ];
7284
+ if (element.is_covered_with_solder_mask) {
7285
+ const maskLayer = layer === "bottom" ? "soldermask_with_copper_bottom" : "soldermask_with_copper_top";
7286
+ const maskPrimitive = {
7287
+ _pcb_drawing_object_id: `rect_${globalPcbDrawingObjectCount++}`,
7288
+ pcb_drawing_type: "rect",
7289
+ x,
7290
+ y,
7291
+ w: width,
7292
+ h: height,
7293
+ layer: maskLayer,
7294
+ _element: element,
7295
+ _parent_pcb_component,
7296
+ _parent_source_component,
7297
+ _source_port,
7298
+ ccw_rotation: element.ccw_rotation,
7299
+ roundness: corner_radius
7300
+ };
7301
+ if (element.solder_mask_color) {
7302
+ maskPrimitive.color = element.solder_mask_color;
7303
+ }
7304
+ primitives.push(maskPrimitive);
7305
+ }
7306
+ return primitives;
7215
7307
  } else if (element.shape === "circle") {
7216
7308
  const { x, y, radius, layer } = element;
7217
- return [
7309
+ const primitives = [
7218
7310
  {
7219
7311
  _pcb_drawing_object_id: `circle_${globalPcbDrawingObjectCount++}`,
7220
7312
  pcb_drawing_type: "circle",
@@ -7228,9 +7320,29 @@ var convertElementToPrimitives = (element, allElements) => {
7228
7320
  _source_port
7229
7321
  }
7230
7322
  ];
7323
+ if (element.is_covered_with_solder_mask) {
7324
+ const maskLayer = layer === "bottom" ? "soldermask_with_copper_bottom" : "soldermask_with_copper_top";
7325
+ const maskPrimitive = {
7326
+ _pcb_drawing_object_id: `circle_${globalPcbDrawingObjectCount++}`,
7327
+ pcb_drawing_type: "circle",
7328
+ x,
7329
+ y,
7330
+ r: radius,
7331
+ layer: maskLayer,
7332
+ _element: element,
7333
+ _parent_pcb_component,
7334
+ _parent_source_component,
7335
+ _source_port
7336
+ };
7337
+ if (element.solder_mask_color) {
7338
+ maskPrimitive.color = element.solder_mask_color;
7339
+ }
7340
+ primitives.push(maskPrimitive);
7341
+ }
7342
+ return primitives;
7231
7343
  } else if (element.shape === "polygon") {
7232
7344
  const { layer, points } = element;
7233
- return [
7345
+ const primitives = [
7234
7346
  {
7235
7347
  _pcb_drawing_object_id: `polygon_${globalPcbDrawingObjectCount++}`,
7236
7348
  pcb_drawing_type: "polygon",
@@ -7242,9 +7354,27 @@ var convertElementToPrimitives = (element, allElements) => {
7242
7354
  _source_port
7243
7355
  }
7244
7356
  ];
7357
+ if (element.is_covered_with_solder_mask) {
7358
+ const maskLayer = layer === "bottom" ? "soldermask_with_copper_bottom" : "soldermask_with_copper_top";
7359
+ const maskPrimitive = {
7360
+ _pcb_drawing_object_id: `polygon_${globalPcbDrawingObjectCount++}`,
7361
+ pcb_drawing_type: "polygon",
7362
+ points: normalizePolygonPoints(points),
7363
+ layer: maskLayer,
7364
+ _element: element,
7365
+ _parent_pcb_component,
7366
+ _parent_source_component,
7367
+ _source_port
7368
+ };
7369
+ if (element.solder_mask_color) {
7370
+ maskPrimitive.color = element.solder_mask_color;
7371
+ }
7372
+ primitives.push(maskPrimitive);
7373
+ }
7374
+ return primitives;
7245
7375
  } else if (element.shape === "pill" || element.shape === "rotated_pill") {
7246
7376
  const { x, y, width, height, layer } = element;
7247
- return [
7377
+ const primitives = [
7248
7378
  {
7249
7379
  _pcb_drawing_object_id: `pill_${globalPcbDrawingObjectCount++}`,
7250
7380
  pcb_drawing_type: "pill",
@@ -7260,6 +7390,28 @@ var convertElementToPrimitives = (element, allElements) => {
7260
7390
  ccw_rotation: element.ccw_rotation
7261
7391
  }
7262
7392
  ];
7393
+ if (element.is_covered_with_solder_mask) {
7394
+ const maskLayer = layer === "bottom" ? "soldermask_with_copper_bottom" : "soldermask_with_copper_top";
7395
+ const maskPrimitive = {
7396
+ _pcb_drawing_object_id: `pill_${globalPcbDrawingObjectCount++}`,
7397
+ pcb_drawing_type: "pill",
7398
+ x,
7399
+ y,
7400
+ w: width,
7401
+ h: height,
7402
+ layer: maskLayer,
7403
+ _element: element,
7404
+ _parent_pcb_component,
7405
+ _parent_source_component,
7406
+ _source_port,
7407
+ ccw_rotation: element.ccw_rotation
7408
+ };
7409
+ if (element.solder_mask_color) {
7410
+ maskPrimitive.color = element.solder_mask_color;
7411
+ }
7412
+ primitives.push(maskPrimitive);
7413
+ }
7414
+ return primitives;
7263
7415
  }
7264
7416
  return [];
7265
7417
  }
@@ -7584,10 +7736,10 @@ var convertElementToPrimitives = (element, allElements) => {
7584
7736
  hole_height,
7585
7737
  layers
7586
7738
  } = element;
7587
- const hole_offset_x = distance2.parse(
7739
+ const hole_offset_x = distance.parse(
7588
7740
  element.hole_offset_x ?? 0
7589
7741
  );
7590
- const hole_offset_y = distance2.parse(
7742
+ const hole_offset_y = distance.parse(
7591
7743
  element.hole_offset_y ?? 0
7592
7744
  );
7593
7745
  const pcb_outline = pad_outline;
@@ -8530,6 +8682,14 @@ var colors_default = {
8530
8682
  plated_hole: "rgb(26, 196, 210)",
8531
8683
  ratsnest: "rgba(245, 255, 213, 0.702)",
8532
8684
  select_overlay: "rgb(4, 255, 67)",
8685
+ soldermaskWithCopper: {
8686
+ top: "rgb(18, 82, 50)",
8687
+ bottom: "rgb(77, 127, 196)"
8688
+ },
8689
+ soldermask: {
8690
+ top: "rgb(12, 55, 33)",
8691
+ bottom: "rgb(12, 55, 33)"
8692
+ },
8533
8693
  through_via: "rgb(236, 236, 236)",
8534
8694
  user_1: "rgb(194, 194, 194)",
8535
8695
  user_2: "rgb(89, 148, 220)",
@@ -8726,6 +8886,10 @@ var LAYER_NAME_TO_COLOR = {
8726
8886
  bottom_silkscreen: colors_default.board.b_silks,
8727
8887
  top_fabrication: colors_default.board.f_fab,
8728
8888
  bottom_fabrication: colors_default.board.b_fab,
8889
+ soldermask_top: colors_default.board.soldermask.top,
8890
+ soldermask_bottom: colors_default.board.soldermask.bottom,
8891
+ soldermask_with_copper_top: colors_default.board.soldermaskWithCopper.top,
8892
+ soldermask_with_copper_bottom: colors_default.board.soldermaskWithCopper.bottom,
8729
8893
  notes: colors_default.board.user_2,
8730
8894
  ...colors_default.board
8731
8895
  };
@@ -8737,9 +8901,13 @@ var DEFAULT_DRAW_ORDER = [
8737
8901
  "inner2",
8738
8902
  "inner1",
8739
8903
  "bottom",
8904
+ "soldermask_bottom",
8740
8905
  "bottom_silkscreen",
8741
8906
  "top",
8907
+ "soldermask_top",
8742
8908
  "top_silkscreen",
8909
+ "soldermask_with_copper_bottom",
8910
+ "soldermask_with_copper_top",
8743
8911
  "board"
8744
8912
  ];
8745
8913
  var Drawer = class {
@@ -9031,23 +9199,27 @@ var Drawer = class {
9031
9199
  */
9032
9200
  orderAndFadeLayers() {
9033
9201
  const { canvasLayerMap, foregroundLayer } = this;
9202
+ const associatedSilkscreen = foregroundLayer === "top" ? "top_silkscreen" : foregroundLayer === "bottom" ? "bottom_silkscreen" : void 0;
9203
+ const maskWithCopperLayerForForeground = foregroundLayer === "top" ? "soldermask_with_copper_top" : foregroundLayer === "bottom" ? "soldermask_with_copper_bottom" : void 0;
9034
9204
  const opaqueLayers = /* @__PURE__ */ new Set([
9035
9205
  foregroundLayer,
9036
9206
  "drill",
9037
9207
  "other",
9038
9208
  "board",
9039
- foregroundLayer === "top" ? "top_silkscreen" : foregroundLayer === "bottom" ? "bottom_silkscreen" : ""
9209
+ ...associatedSilkscreen ? [associatedSilkscreen] : [],
9210
+ ...maskWithCopperLayerForForeground ? [maskWithCopperLayerForForeground] : []
9040
9211
  ]);
9041
- const associatedSilkscreen = foregroundLayer === "top" ? "top_silkscreen" : foregroundLayer === "bottom" ? "bottom_silkscreen" : void 0;
9042
9212
  const layersToShiftToTop = [
9043
9213
  foregroundLayer,
9044
- ...associatedSilkscreen ? [associatedSilkscreen] : []
9214
+ ...associatedSilkscreen ? [associatedSilkscreen] : [],
9215
+ ...maskWithCopperLayerForForeground ? [maskWithCopperLayerForForeground] : []
9045
9216
  ];
9046
9217
  const order = [
9047
9218
  ...DEFAULT_DRAW_ORDER.filter(
9048
9219
  (l) => !layersToShiftToTop.includes(l)
9049
9220
  ),
9050
9221
  foregroundLayer,
9222
+ ...maskWithCopperLayerForForeground ? [maskWithCopperLayerForForeground] : [],
9051
9223
  "drill",
9052
9224
  ...associatedSilkscreen ? [associatedSilkscreen] : []
9053
9225
  ];
@@ -9484,7 +9656,11 @@ var orderedLayers = [
9484
9656
  "board",
9485
9657
  "bottom_silkscreen",
9486
9658
  "bottom",
9659
+ "soldermask_bottom",
9487
9660
  "top",
9661
+ "soldermask_top",
9662
+ "soldermask_with_copper_bottom",
9663
+ "soldermask_with_copper_top",
9488
9664
  "top_silkscreen",
9489
9665
  "inner1",
9490
9666
  "inner2",
@@ -9499,22 +9675,34 @@ var orderedLayers = [
9499
9675
  var CanvasPrimitiveRenderer = ({
9500
9676
  primitives,
9501
9677
  transform,
9502
- grid: grid3,
9678
+ grid,
9503
9679
  width = 500,
9504
9680
  height = 500
9505
9681
  }) => {
9506
9682
  const canvasRefs = useRef2({});
9507
9683
  const selectedLayer = useGlobalStore((s) => s.selected_layer);
9684
+ const isShowingSolderMask = useGlobalStore((s) => s.is_showing_solder_mask);
9508
9685
  useEffect4(() => {
9509
9686
  if (!canvasRefs.current) return;
9510
9687
  if (Object.keys(canvasRefs.current).length === 0) return;
9511
- const drawer = new Drawer(canvasRefs.current);
9688
+ const filteredCanvasRefs = Object.fromEntries(
9689
+ Object.entries(canvasRefs.current).filter(([layer, canvas]) => {
9690
+ if (!canvas) return false;
9691
+ if (!isShowingSolderMask && layer.includes("soldermask")) {
9692
+ return false;
9693
+ }
9694
+ return true;
9695
+ })
9696
+ );
9697
+ if (Object.keys(filteredCanvasRefs).length === 0) return;
9698
+ const drawer = new Drawer(filteredCanvasRefs);
9512
9699
  if (transform) drawer.transform = transform;
9513
9700
  drawer.clear();
9514
9701
  drawer.foregroundLayer = selectedLayer;
9515
- drawPrimitives(drawer, primitives);
9702
+ const filteredPrimitives = isShowingSolderMask ? primitives : primitives.filter((p) => !p.layer?.includes("soldermask"));
9703
+ drawPrimitives(drawer, filteredPrimitives);
9516
9704
  drawer.orderAndFadeLayers();
9517
- }, [primitives, transform, selectedLayer]);
9705
+ }, [primitives, transform, selectedLayer, isShowingSolderMask]);
9518
9706
  return /* @__PURE__ */ jsxs(
9519
9707
  "div",
9520
9708
  {
@@ -9538,13 +9726,22 @@ var CanvasPrimitiveRenderer = ({
9538
9726
  stringifyCoord: (x, y, z) => `${toMMSI(x, z)}, ${toMMSI(y, z)}`
9539
9727
  }
9540
9728
  ),
9541
- orderedLayers.map((l) => l.replace(/-/g, "")).map((layer, i) => /* @__PURE__ */ jsx3(
9729
+ orderedLayers.filter((layer) => {
9730
+ if (!isShowingSolderMask) {
9731
+ return !layer.includes("soldermask");
9732
+ }
9733
+ return true;
9734
+ }).map((l) => l.replace(/-/g, "")).map((layer, i) => /* @__PURE__ */ jsx3(
9542
9735
  "canvas",
9543
9736
  {
9544
9737
  className: `pcb-layer-${layer}`,
9545
9738
  ref: (el) => {
9546
9739
  canvasRefs.current ??= {};
9547
- canvasRefs.current[layer] = el;
9740
+ if (el) {
9741
+ canvasRefs.current[layer] = el;
9742
+ } else {
9743
+ delete canvasRefs.current[layer];
9744
+ }
9548
9745
  },
9549
9746
  style: {
9550
9747
  position: "absolute",
@@ -10047,6 +10244,7 @@ var createBoxFromPoints = (points) => {
10047
10244
  }
10048
10245
  return { minX, maxX, minY, maxY };
10049
10246
  };
10247
+ var getBoundsFromPoints = createBoxFromPoints;
10050
10248
  var mergeBoundingBoxesInternal = (a, b) => ({
10051
10249
  minX: Math.min(a.minX, b.minX),
10052
10250
  maxX: Math.max(a.maxX, b.maxX),
@@ -10183,7 +10381,7 @@ function calculateDiagonalLabel(params) {
10183
10381
  } = params;
10184
10382
  const deltaX = dimensionEnd.x - dimensionStart.x;
10185
10383
  const deltaY = dimensionEnd.y - dimensionStart.y;
10186
- const distance5 = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
10384
+ const distance3 = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
10187
10385
  const screenDeltaX = screenDimensionEnd.x - screenDimensionStart.x;
10188
10386
  const screenDeltaY = screenDimensionEnd.y - screenDimensionStart.y;
10189
10387
  const screenDistance = Math.sqrt(
@@ -10225,11 +10423,11 @@ function calculateDiagonalLabel(params) {
10225
10423
  const x = midX + offsetX;
10226
10424
  const y = midY + offsetY;
10227
10425
  return {
10228
- distance: distance5,
10426
+ distance: distance3,
10229
10427
  screenDistance,
10230
10428
  x,
10231
10429
  y,
10232
- show: distance5 > 0.01 && screenDistance > 30 && isDiagonal
10430
+ show: distance3 > 0.01 && screenDistance > 30 && isDiagonal
10233
10431
  };
10234
10432
  }
10235
10433
 
@@ -10523,11 +10721,11 @@ var DimensionOverlay = ({
10523
10721
  for (const snap of snappingPointsWithScreen) {
10524
10722
  const dx = snap.screenPoint.x - screenPoint.x;
10525
10723
  const dy = snap.screenPoint.y - screenPoint.y;
10526
- const distance5 = Math.hypot(dx, dy);
10527
- if (distance5 > SNAP_THRESHOLD_PX) continue;
10528
- if (!bestMatch || distance5 < bestMatch.distance) {
10724
+ const distance3 = Math.hypot(dx, dy);
10725
+ if (distance3 > SNAP_THRESHOLD_PX) continue;
10726
+ if (!bestMatch || distance3 < bestMatch.distance) {
10529
10727
  bestMatch = {
10530
- distance: distance5,
10728
+ distance: distance3,
10531
10729
  id: snap.id,
10532
10730
  point: snap.point
10533
10731
  };
@@ -11126,10 +11324,10 @@ var isInsideOfSmtpad = (elm, point, padding = 0) => {
11126
11324
  };
11127
11325
  var isInsideOfPlatedHole = (hole, point, padding = 0) => {
11128
11326
  if (hole.shape === "circle") {
11129
- const distance5 = Math.sqrt(
11327
+ const distance3 = Math.sqrt(
11130
11328
  (point.x - hole.x) ** 2 + (point.y - hole.y) ** 2
11131
11329
  );
11132
- return distance5 <= hole.outer_diameter / 2 + padding;
11330
+ return distance3 <= hole.outer_diameter / 2 + padding;
11133
11331
  } else if (hole.shape === "circular_hole_with_rect_pad") {
11134
11332
  const dx = Math.abs(point.x - hole.x);
11135
11333
  const dy = Math.abs(point.y - hole.y);
@@ -11955,9 +12153,19 @@ var ErrorOverlay = ({
11955
12153
  ] });
11956
12154
  };
11957
12155
 
12156
+ // src/components/MouseElementTracker.tsx
12157
+ import { pointToSegmentDistance } from "@tscircuit/math-utils";
12158
+ import { distance as distance2 } from "circuit-json";
12159
+
12160
+ // src/lib/util/if-sets-match-exactly.ts
12161
+ function ifSetsMatchExactly(set1, set2) {
12162
+ if (set1.size !== set2.size) return false;
12163
+ return Array.from(set1).every((item) => set2.has(item));
12164
+ }
12165
+
11958
12166
  // src/components/MouseElementTracker.tsx
11959
12167
  import { useState as useState7, useMemo as useMemo5 } from "react";
11960
- import { applyToPoint as applyToPoint12, inverse as inverse5 } from "transformation-matrix";
12168
+ import { applyToPoint as applyToPoint13, inverse as inverse5 } from "transformation-matrix";
11961
12169
 
11962
12170
  // src/components/ElementOverlayBox.tsx
11963
12171
  import { useEffect as useEffect11, useState as useState6 } from "react";
@@ -12234,35 +12442,228 @@ var ElementOverlayBox = ({
12234
12442
  )) });
12235
12443
  };
12236
12444
 
12237
- // src/components/MouseElementTracker.tsx
12238
- import { distance as distance4 } from "circuit-json";
12445
+ // src/components/GroupAnchorOffsetOverlay/index.tsx
12446
+ import { applyToPoint as applyToPoint12 } from "transformation-matrix";
12239
12447
 
12240
- // src/lib/util/if-sets-match-exactly.ts
12241
- function ifSetsMatchExactly(set1, set2) {
12242
- if (set1.size !== set2.size) return false;
12243
- return Array.from(set1).every((item) => set2.has(item));
12244
- }
12448
+ // src/components/GroupAnchorOffsetOverlay/calculateGroupBoundingBox.ts
12449
+ var calculateGroupBoundingBox = (groupComponents) => {
12450
+ const points = [];
12451
+ for (const comp of groupComponents) {
12452
+ if (!comp.center || typeof comp.width !== "number" || typeof comp.height !== "number") {
12453
+ continue;
12454
+ }
12455
+ const halfWidth = comp.width / 2;
12456
+ const halfHeight = comp.height / 2;
12457
+ points.push({ x: comp.center.x - halfWidth, y: comp.center.y - halfHeight });
12458
+ points.push({ x: comp.center.x + halfWidth, y: comp.center.y + halfHeight });
12459
+ }
12460
+ return getBoundsFromPoints(points);
12461
+ };
12245
12462
 
12246
- // node_modules/@tscircuit/math-utils/dist/chunk-EFLPMB4J.js
12247
- function pointToSegmentDistance2(p, v, w) {
12248
- const l2 = (w.x - v.x) ** 2 + (w.y - v.y) ** 2;
12249
- if (l2 === 0) return distance3(p, v);
12250
- let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
12251
- t = Math.max(0, Math.min(1, t));
12252
- const projection = {
12253
- x: v.x + t * (w.x - v.x),
12254
- y: v.y + t * (w.y - v.y)
12463
+ // src/components/GroupAnchorOffsetOverlay/constants.ts
12464
+ var VISUAL_CONFIG = {
12465
+ GROUP_PADDING: 1,
12466
+ MIN_LINE_LENGTH_FOR_LABEL: 40,
12467
+ LABEL_OFFSET_ABOVE: 2,
12468
+ LABEL_OFFSET_BELOW: -18,
12469
+ LABEL_OFFSET_RIGHT: 8,
12470
+ LABEL_OFFSET_LEFT: -80,
12471
+ LINE_STROKE_WIDTH: 1.5,
12472
+ LINE_DASH_PATTERN: "4,4",
12473
+ COMPONENT_MARKER_RADIUS: 3,
12474
+ LABEL_FONT_SIZE: 11
12475
+ };
12476
+ var COLORS = {
12477
+ OFFSET_LINE: "white",
12478
+ COMPONENT_MARKER_FILL: "#66ccff",
12479
+ COMPONENT_MARKER_STROKE: "white",
12480
+ LABEL_TEXT: "white"
12481
+ };
12482
+
12483
+ // src/components/GroupAnchorOffsetOverlay/findAnchorMarkerPosition.ts
12484
+ var findAnchorMarkerPosition = (anchor, bounds) => {
12485
+ const { minX, maxX, minY, maxY } = bounds;
12486
+ const distToLeft = Math.abs(anchor.x - minX);
12487
+ const distToRight = Math.abs(anchor.x - maxX);
12488
+ const distToTop = Math.abs(anchor.y - maxY);
12489
+ const distToBottom = Math.abs(anchor.y - minY);
12490
+ const minDist = Math.min(distToLeft, distToRight, distToTop, distToBottom);
12491
+ if (minDist === distToLeft) return { x: minX, y: anchor.y };
12492
+ if (minDist === distToRight) return { x: maxX, y: anchor.y };
12493
+ if (minDist === distToTop) return { x: anchor.x, y: maxY };
12494
+ return { x: anchor.x, y: minY };
12495
+ };
12496
+
12497
+ // src/components/GroupAnchorOffsetOverlay/index.tsx
12498
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
12499
+ var GroupAnchorOffsetOverlay = ({
12500
+ elements,
12501
+ highlightedPrimitives,
12502
+ transform,
12503
+ containerWidth,
12504
+ containerHeight,
12505
+ children
12506
+ }) => {
12507
+ const is_showing_group_anchor_offsets = useGlobalStore(
12508
+ (s) => s.is_showing_group_anchor_offsets
12509
+ );
12510
+ if (!is_showing_group_anchor_offsets || highlightedPrimitives.length === 0) {
12511
+ return null;
12512
+ }
12513
+ const hoveredPrimitive = highlightedPrimitives.find(
12514
+ (p) => p._parent_pcb_component?.type === "pcb_component" || p._element?.type === "pcb_component"
12515
+ );
12516
+ if (!hoveredPrimitive) return null;
12517
+ const pcbComponent = hoveredPrimitive._parent_pcb_component || hoveredPrimitive._element;
12518
+ if (!pcbComponent?.pcb_group_id) return null;
12519
+ const parentGroup = elements.filter((el) => el.type === "pcb_group").find((group) => group.pcb_group_id === pcbComponent.pcb_group_id);
12520
+ if (!parentGroup?.anchor_position) return null;
12521
+ const targetCenter = hoveredPrimitive._element?.type === "pcb_smtpad" ? { x: hoveredPrimitive.x, y: hoveredPrimitive.y } : pcbComponent.center || {
12522
+ x: hoveredPrimitive.x,
12523
+ y: hoveredPrimitive.y
12255
12524
  };
12256
- return distance3(p, projection);
12257
- }
12258
- function distance3(p1, p2) {
12259
- const dx = p1.x - p2.x;
12260
- const dy = p1.y - p2.y;
12261
- return Math.sqrt(dx * dx + dy * dy);
12262
- }
12525
+ const groupComponents = elements.filter((el) => el.type === "pcb_component").filter((comp) => comp.pcb_group_id === parentGroup.pcb_group_id);
12526
+ const boundingBox = calculateGroupBoundingBox(groupComponents);
12527
+ if (!boundingBox) return null;
12528
+ const groupBounds = {
12529
+ minX: boundingBox.minX - VISUAL_CONFIG.GROUP_PADDING,
12530
+ maxX: boundingBox.maxX + VISUAL_CONFIG.GROUP_PADDING,
12531
+ minY: boundingBox.minY - VISUAL_CONFIG.GROUP_PADDING,
12532
+ maxY: boundingBox.maxY + VISUAL_CONFIG.GROUP_PADDING
12533
+ };
12534
+ const anchorMarkerPosition = findAnchorMarkerPosition(
12535
+ parentGroup.anchor_position,
12536
+ groupBounds
12537
+ );
12538
+ const offsetX = targetCenter.x - anchorMarkerPosition.x;
12539
+ const offsetY = targetCenter.y - anchorMarkerPosition.y;
12540
+ const anchorMarkerScreen = applyToPoint12(transform, anchorMarkerPosition);
12541
+ const componentScreen = applyToPoint12(transform, targetCenter);
12542
+ const xLineLength = Math.abs(componentScreen.x - anchorMarkerScreen.x);
12543
+ const yLineLength = Math.abs(componentScreen.y - anchorMarkerScreen.y);
12544
+ const isComponentAboveAnchor = componentScreen.y < anchorMarkerScreen.y;
12545
+ const isComponentRightOfAnchor = componentScreen.x > anchorMarkerScreen.x;
12546
+ const xLabelOffset = isComponentAboveAnchor ? VISUAL_CONFIG.LABEL_OFFSET_ABOVE : VISUAL_CONFIG.LABEL_OFFSET_BELOW;
12547
+ const yLabelOffset = isComponentRightOfAnchor ? VISUAL_CONFIG.LABEL_OFFSET_RIGHT : VISUAL_CONFIG.LABEL_OFFSET_LEFT;
12548
+ const shouldShowXLabel = xLineLength > VISUAL_CONFIG.MIN_LINE_LENGTH_FOR_LABEL;
12549
+ const shouldShowYLabel = yLineLength > VISUAL_CONFIG.MIN_LINE_LENGTH_FOR_LABEL;
12550
+ const labelStyle = {
12551
+ color: COLORS.LABEL_TEXT,
12552
+ mixBlendMode: "difference",
12553
+ pointerEvents: "none",
12554
+ fontSize: VISUAL_CONFIG.LABEL_FONT_SIZE,
12555
+ fontFamily: "monospace",
12556
+ fontWeight: "bold"
12557
+ };
12558
+ return /* @__PURE__ */ jsxs9(
12559
+ "div",
12560
+ {
12561
+ style: {
12562
+ position: "absolute",
12563
+ left: 0,
12564
+ top: 0,
12565
+ width: containerWidth,
12566
+ height: containerHeight,
12567
+ overflow: "hidden",
12568
+ pointerEvents: "none",
12569
+ zIndex: zIndexMap.dimensionOverlay
12570
+ },
12571
+ children: [
12572
+ /* @__PURE__ */ jsxs9(
12573
+ "svg",
12574
+ {
12575
+ style: {
12576
+ position: "absolute",
12577
+ left: 0,
12578
+ top: 0,
12579
+ pointerEvents: "none"
12580
+ },
12581
+ width: containerWidth,
12582
+ height: containerHeight,
12583
+ children: [
12584
+ /* @__PURE__ */ jsx12(
12585
+ "line",
12586
+ {
12587
+ x1: anchorMarkerScreen.x,
12588
+ y1: anchorMarkerScreen.y,
12589
+ x2: componentScreen.x,
12590
+ y2: anchorMarkerScreen.y,
12591
+ stroke: COLORS.OFFSET_LINE,
12592
+ strokeWidth: VISUAL_CONFIG.LINE_STROKE_WIDTH,
12593
+ strokeDasharray: VISUAL_CONFIG.LINE_DASH_PATTERN
12594
+ }
12595
+ ),
12596
+ /* @__PURE__ */ jsx12(
12597
+ "line",
12598
+ {
12599
+ x1: componentScreen.x,
12600
+ y1: anchorMarkerScreen.y,
12601
+ x2: componentScreen.x,
12602
+ y2: componentScreen.y,
12603
+ stroke: COLORS.OFFSET_LINE,
12604
+ strokeWidth: VISUAL_CONFIG.LINE_STROKE_WIDTH,
12605
+ strokeDasharray: VISUAL_CONFIG.LINE_DASH_PATTERN
12606
+ }
12607
+ ),
12608
+ /* @__PURE__ */ jsx12(
12609
+ "circle",
12610
+ {
12611
+ cx: componentScreen.x,
12612
+ cy: componentScreen.y,
12613
+ r: VISUAL_CONFIG.COMPONENT_MARKER_RADIUS,
12614
+ fill: COLORS.COMPONENT_MARKER_FILL,
12615
+ stroke: COLORS.COMPONENT_MARKER_STROKE,
12616
+ strokeWidth: 1
12617
+ }
12618
+ )
12619
+ ]
12620
+ }
12621
+ ),
12622
+ shouldShowXLabel && /* @__PURE__ */ jsxs9(
12623
+ "div",
12624
+ {
12625
+ style: {
12626
+ ...labelStyle,
12627
+ position: "absolute",
12628
+ left: Math.min(anchorMarkerScreen.x, componentScreen.x),
12629
+ top: anchorMarkerScreen.y + xLabelOffset,
12630
+ width: Math.abs(componentScreen.x - anchorMarkerScreen.x),
12631
+ textAlign: "center"
12632
+ },
12633
+ children: [
12634
+ "\u0394x: ",
12635
+ offsetX.toFixed(2),
12636
+ "mm"
12637
+ ]
12638
+ }
12639
+ ),
12640
+ shouldShowYLabel && /* @__PURE__ */ jsxs9(
12641
+ "div",
12642
+ {
12643
+ style: {
12644
+ ...labelStyle,
12645
+ position: "absolute",
12646
+ left: componentScreen.x + yLabelOffset,
12647
+ top: Math.min(anchorMarkerScreen.y, componentScreen.y),
12648
+ height: Math.abs(componentScreen.y - anchorMarkerScreen.y),
12649
+ display: "flex",
12650
+ flexDirection: "column",
12651
+ justifyContent: "center"
12652
+ },
12653
+ children: [
12654
+ "\u0394y: ",
12655
+ offsetY.toFixed(2),
12656
+ "mm"
12657
+ ]
12658
+ }
12659
+ )
12660
+ ]
12661
+ }
12662
+ );
12663
+ };
12263
12664
 
12264
12665
  // src/components/MouseElementTracker.tsx
12265
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
12666
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
12266
12667
  var getPolygonBoundingBox = (points) => {
12267
12668
  if (points.length === 0) return null;
12268
12669
  let minX = points[0].x;
@@ -12304,22 +12705,22 @@ var getPrimitivesUnderPoint = (primitives, rwPoint, transform) => {
12304
12705
  for (const primitive of primitives) {
12305
12706
  if (!primitive._element) continue;
12306
12707
  if ("x1" in primitive && primitive._element?.type === "pcb_trace") {
12307
- const distance5 = pointToSegmentDistance2(
12708
+ const distance3 = pointToSegmentDistance(
12308
12709
  { x: rwPoint.x, y: rwPoint.y },
12309
12710
  { x: primitive.x1, y: primitive.y1 },
12310
12711
  { x: primitive.x2, y: primitive.y2 }
12311
12712
  );
12312
12713
  const lineWidth = primitive.width || 0.5;
12313
12714
  const detectionThreshold = Math.max(lineWidth * 25, 2) / transform.a;
12314
- if (distance5 < detectionThreshold) {
12715
+ if (distance3 < detectionThreshold) {
12315
12716
  newMousedPrimitives.push(primitive);
12316
12717
  }
12317
12718
  continue;
12318
12719
  }
12319
12720
  if (primitive.pcb_drawing_type === "polygon") {
12320
12721
  const points = primitive.points.map((point) => ({
12321
- x: distance4.parse(point.x),
12322
- y: distance4.parse(point.y)
12722
+ x: distance2.parse(point.x),
12723
+ y: distance2.parse(point.y)
12323
12724
  }));
12324
12725
  const boundingBox = getPolygonBoundingBox(points);
12325
12726
  if (!boundingBox) continue;
@@ -12333,8 +12734,8 @@ var getPrimitivesUnderPoint = (primitives, rwPoint, transform) => {
12333
12734
  }
12334
12735
  if (primitive.pcb_drawing_type === "polygon_with_arcs") {
12335
12736
  const points = primitive.brep_shape.outer_ring.vertices.map((v) => ({
12336
- x: distance4.parse(v.x),
12337
- y: distance4.parse(v.y)
12737
+ x: distance2.parse(v.x),
12738
+ y: distance2.parse(v.y)
12338
12739
  }));
12339
12740
  const boundingBox = getPolygonBoundingBox(points);
12340
12741
  if (!boundingBox) continue;
@@ -12376,6 +12777,7 @@ var MouseElementTracker = ({
12376
12777
  }) => {
12377
12778
  const [mousedPrimitives, setMousedPrimitives] = useState7([]);
12378
12779
  const [mousePos, setMousePos] = useState7({ x: 0, y: 0 });
12780
+ const [containerRef, { width, height }] = useMeasure_default();
12379
12781
  const highlightedPrimitives = useMemo5(() => {
12380
12782
  const highlightedPrimitives2 = [];
12381
12783
  for (const primitive of mousedPrimitives) {
@@ -12407,7 +12809,7 @@ var MouseElementTracker = ({
12407
12809
  h = "h" in primitive ? primitive.h : "r" in primitive ? primitive.r * 2 : "rX" in primitive && "rY" in primitive ? primitive.rY * 2 : 0;
12408
12810
  }
12409
12811
  if (!basePoint) continue;
12410
- const screenPos = applyToPoint12(transform, basePoint);
12812
+ const screenPos = applyToPoint13(transform, basePoint);
12411
12813
  const screenSize = {
12412
12814
  w: w * transform.a,
12413
12815
  h: h * transform.a
@@ -12432,7 +12834,7 @@ var MouseElementTracker = ({
12432
12834
  }, [mousedPrimitives, transform]);
12433
12835
  const handleInteraction = (x, y, transform2, primitives2) => {
12434
12836
  setMousePos({ x, y });
12435
- const rwPoint = applyToPoint12(inverse5(transform2), { x, y });
12837
+ const rwPoint = applyToPoint13(inverse5(transform2), { x, y });
12436
12838
  const newMousedPrimitives = getPrimitivesUnderPoint(
12437
12839
  primitives2,
12438
12840
  rwPoint,
@@ -12447,10 +12849,11 @@ var MouseElementTracker = ({
12447
12849
  setMousedPrimitives(newMousedPrimitives);
12448
12850
  onMouseHoverOverPrimitives(newMousedPrimitives);
12449
12851
  };
12450
- return /* @__PURE__ */ jsxs9(
12852
+ return /* @__PURE__ */ jsxs10(
12451
12853
  "div",
12452
12854
  {
12453
- style: { position: "relative" },
12855
+ ref: containerRef,
12856
+ style: { position: "relative", width: "100%", height: "100%" },
12454
12857
  onMouseMove: (e) => {
12455
12858
  if (transform) {
12456
12859
  const rect = e.currentTarget.getBoundingClientRect();
@@ -12470,13 +12873,23 @@ var MouseElementTracker = ({
12470
12873
  },
12471
12874
  children: [
12472
12875
  children,
12473
- /* @__PURE__ */ jsx12(
12876
+ /* @__PURE__ */ jsx13(
12474
12877
  ElementOverlayBox,
12475
12878
  {
12476
12879
  elements,
12477
12880
  mousePos,
12478
12881
  highlightedPrimitives
12479
12882
  }
12883
+ ),
12884
+ transform && /* @__PURE__ */ jsx13(
12885
+ GroupAnchorOffsetOverlay,
12886
+ {
12887
+ elements,
12888
+ highlightedPrimitives,
12889
+ transform,
12890
+ containerWidth: width,
12891
+ containerHeight: height
12892
+ }
12480
12893
  )
12481
12894
  ]
12482
12895
  }
@@ -12484,10 +12897,10 @@ var MouseElementTracker = ({
12484
12897
  };
12485
12898
 
12486
12899
  // src/components/PcbGroupOverlay.tsx
12487
- import { applyToPoint as applyToPoint13 } from "transformation-matrix";
12900
+ import { applyToPoint as applyToPoint14 } from "transformation-matrix";
12488
12901
  import { identity as identity8 } from "transformation-matrix";
12489
12902
  import { useRef as useRef9, useEffect as useEffect12 } from "react";
12490
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
12903
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
12491
12904
  var GROUP_COLORS = [
12492
12905
  "rgb(255, 100, 100)",
12493
12906
  "rgb(100, 255, 100)",
@@ -12616,10 +13029,10 @@ var PcbGroupOverlay = ({
12616
13029
  maxX += totalPadding;
12617
13030
  minY -= totalPadding;
12618
13031
  maxY += totalPadding;
12619
- const topLeft = applyToPoint13(transform, { x: minX, y: maxY });
12620
- const topRight = applyToPoint13(transform, { x: maxX, y: maxY });
12621
- const bottomLeft = applyToPoint13(transform, { x: minX, y: minY });
12622
- const bottomRight = applyToPoint13(transform, { x: maxX, y: minY });
13032
+ const topLeft = applyToPoint14(transform, { x: minX, y: maxY });
13033
+ const topRight = applyToPoint14(transform, { x: maxX, y: maxY });
13034
+ const bottomLeft = applyToPoint14(transform, { x: minX, y: minY });
13035
+ const bottomRight = applyToPoint14(transform, { x: maxX, y: minY });
12623
13036
  const groupColor = GROUP_COLORS[groupIndex % GROUP_COLORS.length];
12624
13037
  ctx.strokeStyle = groupColor;
12625
13038
  ctx.lineWidth = 2;
@@ -12689,7 +13102,7 @@ var PcbGroupOverlay = ({
12689
13102
  } else {
12690
13103
  edgePoint = { x: anchor.x, y: groupBottom };
12691
13104
  }
12692
- const anchorScreenPos = applyToPoint13(transform, edgePoint);
13105
+ const anchorScreenPos = applyToPoint14(transform, edgePoint);
12693
13106
  ctx.strokeStyle = "white";
12694
13107
  ctx.lineWidth = 1.5;
12695
13108
  ctx.setLineDash([]);
@@ -12712,14 +13125,14 @@ var PcbGroupOverlay = ({
12712
13125
  is_showing_pcb_groups,
12713
13126
  pcb_group_view_mode
12714
13127
  ]);
12715
- return /* @__PURE__ */ jsxs10(
13128
+ return /* @__PURE__ */ jsxs11(
12716
13129
  "div",
12717
13130
  {
12718
13131
  ref: containerRef,
12719
13132
  style: { position: "relative", width: "100%", height: "100%" },
12720
13133
  children: [
12721
13134
  children,
12722
- /* @__PURE__ */ jsx13(
13135
+ /* @__PURE__ */ jsx14(
12723
13136
  "canvas",
12724
13137
  {
12725
13138
  ref: canvasRef,
@@ -12739,9 +13152,9 @@ var PcbGroupOverlay = ({
12739
13152
  };
12740
13153
 
12741
13154
  // src/components/RatsNestOverlay.tsx
12742
- import { applyToPoint as applyToPoint14, identity as identity9 } from "transformation-matrix";
13155
+ import { applyToPoint as applyToPoint15, identity as identity9 } from "transformation-matrix";
12743
13156
  import { useMemo as useMemo6 } from "react";
12744
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
13157
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
12745
13158
  var RatsNestOverlay = ({ transform, soup, children }) => {
12746
13159
  const isShowingRatsNest = useGlobalStore((s) => s.is_showing_rats_nest);
12747
13160
  const { netMap, idToNetMap } = useMemo6(
@@ -12764,11 +13177,11 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12764
13177
  connectedIds.forEach((id) => {
12765
13178
  const pos = getElementPosition(id);
12766
13179
  if (pos) {
12767
- const distance5 = Math.sqrt(
13180
+ const distance3 = Math.sqrt(
12768
13181
  (sourcePoint.x - pos.x) ** 2 + (sourcePoint.y - pos.y) ** 2
12769
13182
  );
12770
- if (distance5 < minDistance && distance5 > 0) {
12771
- minDistance = distance5;
13183
+ if (distance3 < minDistance && distance3 > 0) {
13184
+ minDistance = distance3;
12772
13185
  nearestPoint = pos;
12773
13186
  }
12774
13187
  }
@@ -12804,9 +13217,9 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12804
13217
  }, [soup, netMap, idToNetMap, isShowingRatsNest]);
12805
13218
  if (!soup || !isShowingRatsNest) return children;
12806
13219
  if (!transform) transform = identity9();
12807
- return /* @__PURE__ */ jsxs11("div", { style: { position: "relative" }, children: [
13220
+ return /* @__PURE__ */ jsxs12("div", { style: { position: "relative" }, children: [
12808
13221
  children,
12809
- /* @__PURE__ */ jsx14(
13222
+ /* @__PURE__ */ jsx15(
12810
13223
  "svg",
12811
13224
  {
12812
13225
  style: {
@@ -12820,9 +13233,9 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12820
13233
  zIndex: zIndexMap.ratsNestOverlay
12821
13234
  },
12822
13235
  children: ratsNestLines.map(({ key, startPoint, endPoint, isInNet }) => {
12823
- const transformedStart = applyToPoint14(transform, startPoint);
12824
- const transformedEnd = applyToPoint14(transform, endPoint);
12825
- return /* @__PURE__ */ jsx14(
13236
+ const transformedStart = applyToPoint15(transform, startPoint);
13237
+ const transformedEnd = applyToPoint15(transform, endPoint);
13238
+ return /* @__PURE__ */ jsx15(
12826
13239
  "line",
12827
13240
  {
12828
13241
  x1: transformedStart.x,
@@ -12842,18 +13255,13 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12842
13255
  };
12843
13256
 
12844
13257
  // src/components/ToolbarOverlay.tsx
12845
- import {
12846
- useEffect as useEffect15,
12847
- useState as useState9,
12848
- useCallback as useCallback4,
12849
- useRef as useRef10
12850
- } from "react";
13258
+ import { useEffect as useEffect15, useState as useState9, useCallback as useCallback4, useRef as useRef10 } from "react";
12851
13259
  import { css as css3 } from "@emotion/css";
12852
13260
 
12853
13261
  // package.json
12854
13262
  var package_default = {
12855
13263
  name: "@tscircuit/pcb-viewer",
12856
- version: "1.11.265",
13264
+ version: "1.11.268",
12857
13265
  main: "dist/index.js",
12858
13266
  type: "module",
12859
13267
  repository: "tscircuit/pcb-viewer",
@@ -12904,6 +13312,7 @@ var package_default = {
12904
13312
  dependencies: {
12905
13313
  "@emotion/css": "^11.11.2",
12906
13314
  "@tscircuit/alphabet": "^0.0.3",
13315
+ "@tscircuit/math-utils": "^0.0.29",
12907
13316
  "@vitejs/plugin-react": "^5.0.2",
12908
13317
  "circuit-json": "^0.0.321",
12909
13318
  "circuit-to-svg": "^0.0.271",
@@ -12958,9 +13367,9 @@ var useIsSmallScreen = () => {
12958
13367
  };
12959
13368
 
12960
13369
  // src/components/ToolbarOverlay.tsx
12961
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
13370
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
12962
13371
  var LayerButton = ({ name, selected, onClick }) => {
12963
- return /* @__PURE__ */ jsxs12(
13372
+ return /* @__PURE__ */ jsxs13(
12964
13373
  "div",
12965
13374
  {
12966
13375
  className: css3`
@@ -12976,8 +13385,8 @@ var LayerButton = ({ name, selected, onClick }) => {
12976
13385
  `,
12977
13386
  onClick,
12978
13387
  children: [
12979
- /* @__PURE__ */ jsx15("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
12980
- /* @__PURE__ */ jsx15(
13388
+ /* @__PURE__ */ jsx16("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
13389
+ /* @__PURE__ */ jsx16(
12981
13390
  "span",
12982
13391
  {
12983
13392
  style: {
@@ -12997,7 +13406,7 @@ var ToolbarButton = ({
12997
13406
  isSmallScreen,
12998
13407
  onClick,
12999
13408
  ...props
13000
- }) => /* @__PURE__ */ jsx15(
13409
+ }) => /* @__PURE__ */ jsx16(
13001
13410
  "div",
13002
13411
  {
13003
13412
  ...props,
@@ -13034,7 +13443,7 @@ var CheckboxMenuItem = ({
13034
13443
  checked,
13035
13444
  onClick
13036
13445
  }) => {
13037
- return /* @__PURE__ */ jsxs12(
13446
+ return /* @__PURE__ */ jsxs13(
13038
13447
  "div",
13039
13448
  {
13040
13449
  className: css3`
@@ -13061,15 +13470,15 @@ var CheckboxMenuItem = ({
13061
13470
  onClick();
13062
13471
  },
13063
13472
  children: [
13064
- /* @__PURE__ */ jsx15("input", { type: "checkbox", checked, onChange: () => {
13473
+ /* @__PURE__ */ jsx16("input", { type: "checkbox", checked, onChange: () => {
13065
13474
  }, readOnly: true }),
13066
- /* @__PURE__ */ jsx15("span", { style: { color: "#eee" }, children: label })
13475
+ /* @__PURE__ */ jsx16("span", { style: { color: "#eee" }, children: label })
13067
13476
  ]
13068
13477
  }
13069
13478
  );
13070
13479
  };
13071
13480
  var RadioMenuItem = ({ label, checked, onClick }) => {
13072
- return /* @__PURE__ */ jsxs12(
13481
+ return /* @__PURE__ */ jsxs13(
13073
13482
  "div",
13074
13483
  {
13075
13484
  className: css3`
@@ -13096,9 +13505,9 @@ var RadioMenuItem = ({ label, checked, onClick }) => {
13096
13505
  onClick();
13097
13506
  },
13098
13507
  children: [
13099
- /* @__PURE__ */ jsx15("input", { type: "radio", checked, onChange: () => {
13508
+ /* @__PURE__ */ jsx16("input", { type: "radio", checked, onChange: () => {
13100
13509
  }, readOnly: true }),
13101
- /* @__PURE__ */ jsx15("span", { style: { color: "#eee" }, children: label })
13510
+ /* @__PURE__ */ jsx16("span", { style: { color: "#eee" }, children: label })
13102
13511
  ]
13103
13512
  }
13104
13513
  );
@@ -13119,6 +13528,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13119
13528
  setIsShowingDrcErrors,
13120
13529
  setIsShowingCopperPours,
13121
13530
  setIsShowingPcbGroups,
13531
+ setIsShowingGroupAnchorOffsets,
13532
+ setIsShowingSolderMask,
13122
13533
  setPcbGroupViewMode,
13123
13534
  setHoveredErrorId
13124
13535
  } = useGlobalStore((s) => ({
@@ -13137,6 +13548,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13137
13548
  is_showing_drc_errors: s.is_showing_drc_errors,
13138
13549
  is_showing_copper_pours: s.is_showing_copper_pours,
13139
13550
  is_showing_pcb_groups: s.is_showing_pcb_groups,
13551
+ is_showing_group_anchor_offsets: s.is_showing_group_anchor_offsets,
13552
+ is_showing_solder_mask: s.is_showing_solder_mask,
13140
13553
  pcb_group_view_mode: s.pcb_group_view_mode
13141
13554
  },
13142
13555
  setEditMode: s.setEditMode,
@@ -13146,6 +13559,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13146
13559
  setIsShowingDrcErrors: s.setIsShowingDrcErrors,
13147
13560
  setIsShowingCopperPours: s.setIsShowingCopperPours,
13148
13561
  setIsShowingPcbGroups: s.setIsShowingPcbGroups,
13562
+ setIsShowingGroupAnchorOffsets: s.setIsShowingGroupAnchorOffsets,
13563
+ setIsShowingSolderMask: s.setIsShowingSolderMask,
13149
13564
  setPcbGroupViewMode: s.setPcbGroupViewMode,
13150
13565
  setHoveredErrorId: s.setHoveredErrorId
13151
13566
  }));
@@ -13244,7 +13659,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13244
13659
  setErrorsOpen(false);
13245
13660
  }
13246
13661
  }, [isViewMenuOpen]);
13247
- return /* @__PURE__ */ jsxs12(
13662
+ return /* @__PURE__ */ jsxs13(
13248
13663
  "div",
13249
13664
  {
13250
13665
  style: { position: "relative", zIndex: "999 !important" },
@@ -13252,7 +13667,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13252
13667
  onMouseLeave: handleMouseLeave,
13253
13668
  children: [
13254
13669
  children,
13255
- /* @__PURE__ */ jsxs12(
13670
+ /* @__PURE__ */ jsxs13(
13256
13671
  "div",
13257
13672
  {
13258
13673
  style: {
@@ -13273,7 +13688,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13273
13688
  ]
13274
13689
  }
13275
13690
  ),
13276
- /* @__PURE__ */ jsxs12(
13691
+ /* @__PURE__ */ jsxs13(
13277
13692
  "div",
13278
13693
  {
13279
13694
  "data-toolbar-overlay": true,
@@ -13296,7 +13711,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13296
13711
  fontFamily: "sans-serif"
13297
13712
  },
13298
13713
  children: [
13299
- /* @__PURE__ */ jsxs12(
13714
+ /* @__PURE__ */ jsxs13(
13300
13715
  ToolbarButton,
13301
13716
  {
13302
13717
  isSmallScreen,
@@ -13307,10 +13722,10 @@ var ToolbarOverlay = ({ children, elements }) => {
13307
13722
  }
13308
13723
  },
13309
13724
  children: [
13310
- /* @__PURE__ */ jsxs12("div", { children: [
13725
+ /* @__PURE__ */ jsxs13("div", { children: [
13311
13726
  "layer:",
13312
13727
  " ",
13313
- /* @__PURE__ */ jsx15(
13728
+ /* @__PURE__ */ jsx16(
13314
13729
  "span",
13315
13730
  {
13316
13731
  style: {
@@ -13322,7 +13737,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13322
13737
  }
13323
13738
  )
13324
13739
  ] }),
13325
- isLayerMenuOpen && /* @__PURE__ */ jsx15("div", { style: { marginTop: 4, minWidth: 120 }, children: processedLayers.map((layer) => /* @__PURE__ */ jsx15(
13740
+ isLayerMenuOpen && /* @__PURE__ */ jsx16("div", { style: { marginTop: 4, minWidth: 120 }, children: processedLayers.map((layer) => /* @__PURE__ */ jsx16(
13326
13741
  LayerButton,
13327
13742
  {
13328
13743
  name: layer,
@@ -13336,7 +13751,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13336
13751
  ]
13337
13752
  }
13338
13753
  ),
13339
- /* @__PURE__ */ jsx15(
13754
+ /* @__PURE__ */ jsx16(
13340
13755
  ToolbarButton,
13341
13756
  {
13342
13757
  isSmallScreen,
@@ -13345,13 +13760,13 @@ var ToolbarOverlay = ({ children, elements }) => {
13345
13760
  ...errorCount > 0 ? { color: "red" } : {}
13346
13761
  },
13347
13762
  onClick: handleErrorsToggle,
13348
- children: /* @__PURE__ */ jsxs12("div", { children: [
13763
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13349
13764
  errorCount,
13350
13765
  " errors"
13351
13766
  ] })
13352
13767
  }
13353
13768
  ),
13354
- isErrorsOpen && errorCount > 0 && /* @__PURE__ */ jsx15(
13769
+ isErrorsOpen && errorCount > 0 && /* @__PURE__ */ jsx16(
13355
13770
  "div",
13356
13771
  {
13357
13772
  style: {
@@ -13369,14 +13784,14 @@ var ToolbarOverlay = ({ children, elements }) => {
13369
13784
  },
13370
13785
  children: errorElements.map((e, i) => {
13371
13786
  const errorId = e.pcb_trace_error_id || `error_${i}_${e.error_type}_${e.message?.slice(0, 20)}`;
13372
- return /* @__PURE__ */ jsxs12(
13787
+ return /* @__PURE__ */ jsxs13(
13373
13788
  "div",
13374
13789
  {
13375
13790
  style: {
13376
13791
  borderBottom: i < errorElements.length - 1 ? "1px solid #444" : "none"
13377
13792
  },
13378
13793
  children: [
13379
- /* @__PURE__ */ jsxs12(
13794
+ /* @__PURE__ */ jsxs13(
13380
13795
  "div",
13381
13796
  {
13382
13797
  style: {
@@ -13427,7 +13842,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13427
13842
  }
13428
13843
  },
13429
13844
  children: [
13430
- /* @__PURE__ */ jsx15(
13845
+ /* @__PURE__ */ jsx16(
13431
13846
  "div",
13432
13847
  {
13433
13848
  style: {
@@ -13441,7 +13856,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13441
13856
  children: e.error_type
13442
13857
  }
13443
13858
  ),
13444
- /* @__PURE__ */ jsx15(
13859
+ /* @__PURE__ */ jsx16(
13445
13860
  "div",
13446
13861
  {
13447
13862
  style: {
@@ -13456,7 +13871,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13456
13871
  children: e.message
13457
13872
  }
13458
13873
  ),
13459
- /* @__PURE__ */ jsx15(
13874
+ /* @__PURE__ */ jsx16(
13460
13875
  "div",
13461
13876
  {
13462
13877
  ref: (el) => {
@@ -13476,7 +13891,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13476
13891
  ]
13477
13892
  }
13478
13893
  ),
13479
- /* @__PURE__ */ jsx15(
13894
+ /* @__PURE__ */ jsx16(
13480
13895
  "div",
13481
13896
  {
13482
13897
  ref: (el) => {
@@ -13489,7 +13904,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13489
13904
  backgroundColor: "#1a1a1a",
13490
13905
  borderTop: "1px solid #444"
13491
13906
  },
13492
- children: /* @__PURE__ */ jsx15(
13907
+ children: /* @__PURE__ */ jsx16(
13493
13908
  "div",
13494
13909
  {
13495
13910
  style: {
@@ -13512,58 +13927,58 @@ var ToolbarOverlay = ({ children, elements }) => {
13512
13927
  })
13513
13928
  }
13514
13929
  ),
13515
- /* @__PURE__ */ jsx15(
13930
+ /* @__PURE__ */ jsx16(
13516
13931
  ToolbarButton,
13517
13932
  {
13518
13933
  isSmallScreen,
13519
13934
  style: {},
13520
13935
  onClick: handleEditTraceToggle,
13521
- children: /* @__PURE__ */ jsxs12("div", { children: [
13936
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13522
13937
  editModes.in_draw_trace_mode ? "\u2716 " : "",
13523
13938
  "Edit Traces"
13524
13939
  ] })
13525
13940
  }
13526
13941
  ),
13527
- /* @__PURE__ */ jsx15(
13942
+ /* @__PURE__ */ jsx16(
13528
13943
  ToolbarButton,
13529
13944
  {
13530
13945
  isSmallScreen,
13531
13946
  style: {},
13532
13947
  onClick: handleMoveComponentToggle,
13533
- children: /* @__PURE__ */ jsxs12("div", { children: [
13948
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13534
13949
  editModes.in_move_footprint_mode ? "\u2716 " : "",
13535
13950
  "Move Components"
13536
13951
  ] })
13537
13952
  }
13538
13953
  ),
13539
- /* @__PURE__ */ jsx15(
13954
+ /* @__PURE__ */ jsx16(
13540
13955
  ToolbarButton,
13541
13956
  {
13542
13957
  isSmallScreen,
13543
13958
  style: {},
13544
13959
  onClick: handleRatsNestToggle,
13545
- children: /* @__PURE__ */ jsxs12("div", { children: [
13960
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13546
13961
  viewSettings.is_showing_rats_nest ? "\u2716 " : "",
13547
13962
  "Rats Nest"
13548
13963
  ] })
13549
13964
  }
13550
13965
  ),
13551
- /* @__PURE__ */ jsx15(
13966
+ /* @__PURE__ */ jsx16(
13552
13967
  ToolbarButton,
13553
13968
  {
13554
13969
  isSmallScreen,
13555
13970
  style: measureToolArmed ? { backgroundColor: "#444" } : {},
13556
13971
  onClick: handleMeasureToolClick,
13557
- children: /* @__PURE__ */ jsx15("div", { children: "\u{1F4CF}" })
13972
+ children: /* @__PURE__ */ jsx16("div", { children: "\u{1F4CF}" })
13558
13973
  }
13559
13974
  ),
13560
- /* @__PURE__ */ jsx15(
13975
+ /* @__PURE__ */ jsx16(
13561
13976
  ToolbarButton,
13562
13977
  {
13563
13978
  isSmallScreen,
13564
13979
  onClick: handleViewMenuToggle,
13565
- children: /* @__PURE__ */ jsxs12("div", { children: [
13566
- /* @__PURE__ */ jsxs12(
13980
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13981
+ /* @__PURE__ */ jsxs13(
13567
13982
  "div",
13568
13983
  {
13569
13984
  style: {
@@ -13573,7 +13988,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13573
13988
  },
13574
13989
  children: [
13575
13990
  "View",
13576
- /* @__PURE__ */ jsx15(
13991
+ /* @__PURE__ */ jsx16(
13577
13992
  "span",
13578
13993
  {
13579
13994
  style: {
@@ -13588,8 +14003,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13588
14003
  ]
13589
14004
  }
13590
14005
  ),
13591
- isViewMenuOpen && /* @__PURE__ */ jsxs12("div", { style: { marginTop: 4, minWidth: 120 }, children: [
13592
- /* @__PURE__ */ jsx15(
14006
+ isViewMenuOpen && /* @__PURE__ */ jsxs13("div", { style: { marginTop: 4, minWidth: 120 }, children: [
14007
+ /* @__PURE__ */ jsx16(
13593
14008
  CheckboxMenuItem,
13594
14009
  {
13595
14010
  label: "Show All Trace Lengths",
@@ -13601,7 +14016,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13601
14016
  }
13602
14017
  }
13603
14018
  ),
13604
- /* @__PURE__ */ jsx15(
14019
+ /* @__PURE__ */ jsx16(
13605
14020
  CheckboxMenuItem,
13606
14021
  {
13607
14022
  label: "Show Autorouting Animation",
@@ -13613,7 +14028,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13613
14028
  }
13614
14029
  }
13615
14030
  ),
13616
- /* @__PURE__ */ jsx15(
14031
+ /* @__PURE__ */ jsx16(
13617
14032
  CheckboxMenuItem,
13618
14033
  {
13619
14034
  label: "Show DRC Errors",
@@ -13623,7 +14038,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13623
14038
  }
13624
14039
  }
13625
14040
  ),
13626
- /* @__PURE__ */ jsx15(
14041
+ /* @__PURE__ */ jsx16(
13627
14042
  CheckboxMenuItem,
13628
14043
  {
13629
14044
  label: "Show Copper Pours",
@@ -13635,7 +14050,29 @@ var ToolbarOverlay = ({ children, elements }) => {
13635
14050
  }
13636
14051
  }
13637
14052
  ),
13638
- /* @__PURE__ */ jsx15(
14053
+ /* @__PURE__ */ jsx16(
14054
+ CheckboxMenuItem,
14055
+ {
14056
+ label: "Show Solder Mask",
14057
+ checked: viewSettings.is_showing_solder_mask,
14058
+ onClick: () => {
14059
+ setIsShowingSolderMask(!viewSettings.is_showing_solder_mask);
14060
+ }
14061
+ }
14062
+ ),
14063
+ /* @__PURE__ */ jsx16(
14064
+ CheckboxMenuItem,
14065
+ {
14066
+ label: "Show Group Anchor Offsets",
14067
+ checked: viewSettings.is_showing_group_anchor_offsets,
14068
+ onClick: () => {
14069
+ setIsShowingGroupAnchorOffsets(
14070
+ !viewSettings.is_showing_group_anchor_offsets
14071
+ );
14072
+ }
14073
+ }
14074
+ ),
14075
+ /* @__PURE__ */ jsx16(
13639
14076
  CheckboxMenuItem,
13640
14077
  {
13641
14078
  label: "Show PCB Groups",
@@ -13645,8 +14082,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13645
14082
  }
13646
14083
  }
13647
14084
  ),
13648
- viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs12("div", { style: { marginLeft: 16 }, children: [
13649
- /* @__PURE__ */ jsx15(
14085
+ viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs13("div", { style: { marginLeft: 16 }, children: [
14086
+ /* @__PURE__ */ jsx16(
13650
14087
  RadioMenuItem,
13651
14088
  {
13652
14089
  label: "Show All Groups",
@@ -13656,7 +14093,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13656
14093
  }
13657
14094
  }
13658
14095
  ),
13659
- /* @__PURE__ */ jsx15(
14096
+ /* @__PURE__ */ jsx16(
13660
14097
  RadioMenuItem,
13661
14098
  {
13662
14099
  label: "Show Named Groups",
@@ -13680,7 +14117,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13680
14117
  };
13681
14118
 
13682
14119
  // src/components/CanvasElementsRenderer.tsx
13683
- import { jsx as jsx16 } from "react/jsx-runtime";
14120
+ import { jsx as jsx17 } from "react/jsx-runtime";
13684
14121
  var CanvasElementsRenderer = (props) => {
13685
14122
  const { transform, elements } = props;
13686
14123
  const hoveredErrorId = useGlobalStore((state) => state.hovered_error_id);
@@ -13757,14 +14194,14 @@ var CanvasElementsRenderer = (props) => {
13757
14194
  },
13758
14195
  [connectivityMap]
13759
14196
  );
13760
- return /* @__PURE__ */ jsx16(
14197
+ return /* @__PURE__ */ jsx17(
13761
14198
  MouseElementTracker,
13762
14199
  {
13763
14200
  elements: elementsToRender,
13764
14201
  transform,
13765
14202
  primitives: primitivesWithoutInteractionMetadata,
13766
14203
  onMouseHoverOverPrimitives: onMouseOverPrimitives,
13767
- children: /* @__PURE__ */ jsx16(
14204
+ children: /* @__PURE__ */ jsx17(
13768
14205
  EditPlacementOverlay,
13769
14206
  {
13770
14207
  disabled: !props.allowEditing,
@@ -13773,7 +14210,7 @@ var CanvasElementsRenderer = (props) => {
13773
14210
  cancelPanDrag: props.cancelPanDrag,
13774
14211
  onCreateEditEvent: props.onCreateEditEvent,
13775
14212
  onModifyEditEvent: props.onModifyEditEvent,
13776
- children: /* @__PURE__ */ jsx16(
14213
+ children: /* @__PURE__ */ jsx17(
13777
14214
  EditTraceHintOverlay,
13778
14215
  {
13779
14216
  disabled: !props.allowEditing,
@@ -13782,23 +14219,23 @@ var CanvasElementsRenderer = (props) => {
13782
14219
  cancelPanDrag: props.cancelPanDrag,
13783
14220
  onCreateEditEvent: props.onCreateEditEvent,
13784
14221
  onModifyEditEvent: props.onModifyEditEvent,
13785
- children: /* @__PURE__ */ jsx16(
14222
+ children: /* @__PURE__ */ jsx17(
13786
14223
  DimensionOverlay,
13787
14224
  {
13788
14225
  transform,
13789
14226
  focusOnHover: props.focusOnHover,
13790
14227
  primitives: primitivesWithoutInteractionMetadata,
13791
- children: /* @__PURE__ */ jsx16(ToolbarOverlay, { elements, children: /* @__PURE__ */ jsx16(ErrorOverlay, { transform, elements, children: /* @__PURE__ */ jsx16(RatsNestOverlay, { transform, soup: elements, children: /* @__PURE__ */ jsx16(PcbGroupOverlay, { transform, elements, children: /* @__PURE__ */ jsx16(
14228
+ children: /* @__PURE__ */ jsx17(ToolbarOverlay, { elements, children: /* @__PURE__ */ jsx17(ErrorOverlay, { transform, elements, children: /* @__PURE__ */ jsx17(RatsNestOverlay, { transform, soup: elements, children: /* @__PURE__ */ jsx17(PcbGroupOverlay, { transform, elements, children: /* @__PURE__ */ jsx17(
13792
14229
  DebugGraphicsOverlay,
13793
14230
  {
13794
14231
  transform,
13795
14232
  debugGraphics: props.debugGraphics,
13796
- children: /* @__PURE__ */ jsx16(
14233
+ children: /* @__PURE__ */ jsx17(
13797
14234
  WarningGraphicsOverlay,
13798
14235
  {
13799
14236
  transform,
13800
14237
  elements,
13801
- children: /* @__PURE__ */ jsx16(
14238
+ children: /* @__PURE__ */ jsx17(
13802
14239
  CanvasPrimitiveRenderer,
13803
14240
  {
13804
14241
  transform,
@@ -13865,7 +14302,7 @@ var calculateCircuitJsonKey = (circuitJson) => {
13865
14302
  };
13866
14303
 
13867
14304
  // src/PCBViewer.tsx
13868
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
14305
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
13869
14306
  var defaultTransform = compose7(translate11(400, 300), scale5(40, -40));
13870
14307
  var PCBViewer = ({
13871
14308
  circuitJson,
@@ -13959,14 +14396,14 @@ var PCBViewer = ({
13959
14396
  }),
13960
14397
  [initialState, disablePcbGroups]
13961
14398
  );
13962
- return /* @__PURE__ */ jsxs13("div", { ref: transformRef, style: { position: "relative" }, children: [
13963
- /* @__PURE__ */ jsx17("div", { ref, children: /* @__PURE__ */ jsxs13(
14399
+ return /* @__PURE__ */ jsxs14("div", { ref: transformRef, style: { position: "relative" }, children: [
14400
+ /* @__PURE__ */ jsx18("div", { ref, children: /* @__PURE__ */ jsxs14(
13964
14401
  ContextProviders,
13965
14402
  {
13966
14403
  initialState: mergedInitialState,
13967
14404
  disablePcbGroups,
13968
14405
  children: [
13969
- /* @__PURE__ */ jsx17(
14406
+ /* @__PURE__ */ jsx18(
13970
14407
  CanvasElementsRenderer,
13971
14408
  {
13972
14409
  transform,
@@ -13991,11 +14428,11 @@ var PCBViewer = ({
13991
14428
  },
13992
14429
  refDimensions.width
13993
14430
  ),
13994
- /* @__PURE__ */ jsx17(ToastContainer, {})
14431
+ /* @__PURE__ */ jsx18(ToastContainer, {})
13995
14432
  ]
13996
14433
  }
13997
14434
  ) }),
13998
- clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx17(
14435
+ clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx18(
13999
14436
  "div",
14000
14437
  {
14001
14438
  onClick: () => {
@@ -14032,7 +14469,7 @@ var PCBViewer = ({
14032
14469
  justifyContent: "center",
14033
14470
  touchAction: "pan-x pan-y pinch-zoom"
14034
14471
  },
14035
- children: /* @__PURE__ */ jsx17(
14472
+ children: /* @__PURE__ */ jsx18(
14036
14473
  "div",
14037
14474
  {
14038
14475
  style: {