@tscircuit/pcb-viewer 1.11.266 → 1.11.268

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,8 @@ 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"
6281
6282
  };
6282
6283
  var getStoredBoolean = (key, defaultValue) => {
6283
6284
  if (typeof window === "undefined") return defaultValue;
@@ -6333,6 +6334,10 @@ var createStore = (initialState = {}, disablePcbGroups = false) => createZustand
6333
6334
  true
6334
6335
  ),
6335
6336
  is_showing_pcb_groups: disablePcbGroups ? false : getStoredBoolean(STORAGE_KEYS.IS_SHOWING_PCB_GROUPS, true),
6337
+ is_showing_group_anchor_offsets: getStoredBoolean(
6338
+ STORAGE_KEYS.IS_SHOWING_GROUP_ANCHOR_OFFSETS,
6339
+ true
6340
+ ),
6336
6341
  pcb_group_view_mode: disablePcbGroups ? "all" : getStoredString(
6337
6342
  STORAGE_KEYS.PCB_GROUP_VIEW_MODE,
6338
6343
  DEFAULT_PCB_GROUP_VIEW_MODE
@@ -6363,6 +6368,13 @@ var createStore = (initialState = {}, disablePcbGroups = false) => createZustand
6363
6368
  setStoredBoolean(STORAGE_KEYS.IS_SHOWING_PCB_GROUPS, is_showing);
6364
6369
  set({ is_showing_pcb_groups: is_showing });
6365
6370
  },
6371
+ setIsShowingGroupAnchorOffsets: (is_showing) => {
6372
+ setStoredBoolean(
6373
+ STORAGE_KEYS.IS_SHOWING_GROUP_ANCHOR_OFFSETS,
6374
+ is_showing
6375
+ );
6376
+ set({ is_showing_group_anchor_offsets: is_showing });
6377
+ },
6366
6378
  setPcbGroupViewMode: (mode) => {
6367
6379
  if (disablePcbGroups) return;
6368
6380
  setStoredString(STORAGE_KEYS.PCB_GROUP_VIEW_MODE, mode);
@@ -6766,6 +6778,7 @@ var useMouseMatrixTransform = (props = {}) => {
6766
6778
  var src_default = useMouseMatrixTransform;
6767
6779
 
6768
6780
  // node_modules/circuit-json-to-connectivity-map/dist/index.js
6781
+ import { doesLineIntersectLine } from "@tscircuit/math-utils";
6769
6782
  function findConnectedNetworks(connections) {
6770
6783
  const networks = /* @__PURE__ */ new Map();
6771
6784
  let netCounter = 0;
@@ -7052,12 +7065,12 @@ function getExpandedStroke(strokeInput, defaultWidth) {
7052
7065
  }
7053
7066
 
7054
7067
  // src/lib/convert-element-to-primitive.ts
7055
- import { distance as distance2 } from "circuit-json";
7068
+ import { distance } from "circuit-json";
7056
7069
  var globalPcbDrawingObjectCount = 0;
7057
7070
  var getNewPcbDrawingObjectId = (prefix) => `${prefix}_${globalPcbDrawingObjectCount++}`;
7058
7071
  var normalizePolygonPoints = (points) => (points ?? []).map((point) => ({
7059
- x: distance2.parse(point.x),
7060
- y: distance2.parse(point.y)
7072
+ x: distance.parse(point.x),
7073
+ y: distance.parse(point.y)
7061
7074
  }));
7062
7075
  var convertElementToPrimitives = (element, allElements) => {
7063
7076
  const _parent_pcb_component = "pcb_component_id" in element ? allElements.find(
@@ -7584,10 +7597,10 @@ var convertElementToPrimitives = (element, allElements) => {
7584
7597
  hole_height,
7585
7598
  layers
7586
7599
  } = element;
7587
- const hole_offset_x = distance2.parse(
7600
+ const hole_offset_x = distance.parse(
7588
7601
  element.hole_offset_x ?? 0
7589
7602
  );
7590
- const hole_offset_y = distance2.parse(
7603
+ const hole_offset_y = distance.parse(
7591
7604
  element.hole_offset_y ?? 0
7592
7605
  );
7593
7606
  const pcb_outline = pad_outline;
@@ -8168,7 +8181,12 @@ var convertElementToPrimitives = (element, allElements) => {
8168
8181
  layer: element.layer === "bottom" ? "bottom_fabrication" : "top_fabrication",
8169
8182
  size: element.font_size,
8170
8183
  align: element.anchor_alignment ?? "center",
8171
- text: element.text
8184
+ text: element.text,
8185
+ color: element.color,
8186
+ _element: element,
8187
+ _parent_pcb_component,
8188
+ _parent_source_component,
8189
+ _source_port
8172
8190
  }
8173
8191
  ];
8174
8192
  }
@@ -9250,7 +9268,8 @@ var convertTextToLines = (text) => {
9250
9268
  y2: text.y + y_baseline_offset + centerline_span_y * norm_y2,
9251
9269
  width: strokeWidth,
9252
9270
  layer: text.layer,
9253
- unit: text.unit
9271
+ unit: text.unit,
9272
+ color: text.color
9254
9273
  });
9255
9274
  }
9256
9275
  current_x_origin_for_char_box += target_width_char + space_between_chars;
@@ -9493,7 +9512,7 @@ var orderedLayers = [
9493
9512
  var CanvasPrimitiveRenderer = ({
9494
9513
  primitives,
9495
9514
  transform,
9496
- grid: grid3,
9515
+ grid,
9497
9516
  width = 500,
9498
9517
  height = 500
9499
9518
  }) => {
@@ -10041,6 +10060,7 @@ var createBoxFromPoints = (points) => {
10041
10060
  }
10042
10061
  return { minX, maxX, minY, maxY };
10043
10062
  };
10063
+ var getBoundsFromPoints = createBoxFromPoints;
10044
10064
  var mergeBoundingBoxesInternal = (a, b) => ({
10045
10065
  minX: Math.min(a.minX, b.minX),
10046
10066
  maxX: Math.max(a.maxX, b.maxX),
@@ -10177,7 +10197,7 @@ function calculateDiagonalLabel(params) {
10177
10197
  } = params;
10178
10198
  const deltaX = dimensionEnd.x - dimensionStart.x;
10179
10199
  const deltaY = dimensionEnd.y - dimensionStart.y;
10180
- const distance5 = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
10200
+ const distance3 = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
10181
10201
  const screenDeltaX = screenDimensionEnd.x - screenDimensionStart.x;
10182
10202
  const screenDeltaY = screenDimensionEnd.y - screenDimensionStart.y;
10183
10203
  const screenDistance = Math.sqrt(
@@ -10219,11 +10239,11 @@ function calculateDiagonalLabel(params) {
10219
10239
  const x = midX + offsetX;
10220
10240
  const y = midY + offsetY;
10221
10241
  return {
10222
- distance: distance5,
10242
+ distance: distance3,
10223
10243
  screenDistance,
10224
10244
  x,
10225
10245
  y,
10226
- show: distance5 > 0.01 && screenDistance > 30 && isDiagonal
10246
+ show: distance3 > 0.01 && screenDistance > 30 && isDiagonal
10227
10247
  };
10228
10248
  }
10229
10249
 
@@ -10517,11 +10537,11 @@ var DimensionOverlay = ({
10517
10537
  for (const snap of snappingPointsWithScreen) {
10518
10538
  const dx = snap.screenPoint.x - screenPoint.x;
10519
10539
  const dy = snap.screenPoint.y - screenPoint.y;
10520
- const distance5 = Math.hypot(dx, dy);
10521
- if (distance5 > SNAP_THRESHOLD_PX) continue;
10522
- if (!bestMatch || distance5 < bestMatch.distance) {
10540
+ const distance3 = Math.hypot(dx, dy);
10541
+ if (distance3 > SNAP_THRESHOLD_PX) continue;
10542
+ if (!bestMatch || distance3 < bestMatch.distance) {
10523
10543
  bestMatch = {
10524
- distance: distance5,
10544
+ distance: distance3,
10525
10545
  id: snap.id,
10526
10546
  point: snap.point
10527
10547
  };
@@ -11120,10 +11140,10 @@ var isInsideOfSmtpad = (elm, point, padding = 0) => {
11120
11140
  };
11121
11141
  var isInsideOfPlatedHole = (hole, point, padding = 0) => {
11122
11142
  if (hole.shape === "circle") {
11123
- const distance5 = Math.sqrt(
11143
+ const distance3 = Math.sqrt(
11124
11144
  (point.x - hole.x) ** 2 + (point.y - hole.y) ** 2
11125
11145
  );
11126
- return distance5 <= hole.outer_diameter / 2 + padding;
11146
+ return distance3 <= hole.outer_diameter / 2 + padding;
11127
11147
  } else if (hole.shape === "circular_hole_with_rect_pad") {
11128
11148
  const dx = Math.abs(point.x - hole.x);
11129
11149
  const dy = Math.abs(point.y - hole.y);
@@ -11949,9 +11969,19 @@ var ErrorOverlay = ({
11949
11969
  ] });
11950
11970
  };
11951
11971
 
11972
+ // src/components/MouseElementTracker.tsx
11973
+ import { pointToSegmentDistance } from "@tscircuit/math-utils";
11974
+ import { distance as distance2 } from "circuit-json";
11975
+
11976
+ // src/lib/util/if-sets-match-exactly.ts
11977
+ function ifSetsMatchExactly(set1, set2) {
11978
+ if (set1.size !== set2.size) return false;
11979
+ return Array.from(set1).every((item) => set2.has(item));
11980
+ }
11981
+
11952
11982
  // src/components/MouseElementTracker.tsx
11953
11983
  import { useState as useState7, useMemo as useMemo5 } from "react";
11954
- import { applyToPoint as applyToPoint12, inverse as inverse5 } from "transformation-matrix";
11984
+ import { applyToPoint as applyToPoint13, inverse as inverse5 } from "transformation-matrix";
11955
11985
 
11956
11986
  // src/components/ElementOverlayBox.tsx
11957
11987
  import { useEffect as useEffect11, useState as useState6 } from "react";
@@ -12228,35 +12258,228 @@ var ElementOverlayBox = ({
12228
12258
  )) });
12229
12259
  };
12230
12260
 
12231
- // src/components/MouseElementTracker.tsx
12232
- import { distance as distance4 } from "circuit-json";
12261
+ // src/components/GroupAnchorOffsetOverlay/index.tsx
12262
+ import { applyToPoint as applyToPoint12 } from "transformation-matrix";
12233
12263
 
12234
- // src/lib/util/if-sets-match-exactly.ts
12235
- function ifSetsMatchExactly(set1, set2) {
12236
- if (set1.size !== set2.size) return false;
12237
- return Array.from(set1).every((item) => set2.has(item));
12238
- }
12264
+ // src/components/GroupAnchorOffsetOverlay/calculateGroupBoundingBox.ts
12265
+ var calculateGroupBoundingBox = (groupComponents) => {
12266
+ const points = [];
12267
+ for (const comp of groupComponents) {
12268
+ if (!comp.center || typeof comp.width !== "number" || typeof comp.height !== "number") {
12269
+ continue;
12270
+ }
12271
+ const halfWidth = comp.width / 2;
12272
+ const halfHeight = comp.height / 2;
12273
+ points.push({ x: comp.center.x - halfWidth, y: comp.center.y - halfHeight });
12274
+ points.push({ x: comp.center.x + halfWidth, y: comp.center.y + halfHeight });
12275
+ }
12276
+ return getBoundsFromPoints(points);
12277
+ };
12239
12278
 
12240
- // node_modules/@tscircuit/math-utils/dist/chunk-EFLPMB4J.js
12241
- function pointToSegmentDistance2(p, v, w) {
12242
- const l2 = (w.x - v.x) ** 2 + (w.y - v.y) ** 2;
12243
- if (l2 === 0) return distance3(p, v);
12244
- let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
12245
- t = Math.max(0, Math.min(1, t));
12246
- const projection = {
12247
- x: v.x + t * (w.x - v.x),
12248
- y: v.y + t * (w.y - v.y)
12279
+ // src/components/GroupAnchorOffsetOverlay/constants.ts
12280
+ var VISUAL_CONFIG = {
12281
+ GROUP_PADDING: 1,
12282
+ MIN_LINE_LENGTH_FOR_LABEL: 40,
12283
+ LABEL_OFFSET_ABOVE: 2,
12284
+ LABEL_OFFSET_BELOW: -18,
12285
+ LABEL_OFFSET_RIGHT: 8,
12286
+ LABEL_OFFSET_LEFT: -80,
12287
+ LINE_STROKE_WIDTH: 1.5,
12288
+ LINE_DASH_PATTERN: "4,4",
12289
+ COMPONENT_MARKER_RADIUS: 3,
12290
+ LABEL_FONT_SIZE: 11
12291
+ };
12292
+ var COLORS = {
12293
+ OFFSET_LINE: "white",
12294
+ COMPONENT_MARKER_FILL: "#66ccff",
12295
+ COMPONENT_MARKER_STROKE: "white",
12296
+ LABEL_TEXT: "white"
12297
+ };
12298
+
12299
+ // src/components/GroupAnchorOffsetOverlay/findAnchorMarkerPosition.ts
12300
+ var findAnchorMarkerPosition = (anchor, bounds) => {
12301
+ const { minX, maxX, minY, maxY } = bounds;
12302
+ const distToLeft = Math.abs(anchor.x - minX);
12303
+ const distToRight = Math.abs(anchor.x - maxX);
12304
+ const distToTop = Math.abs(anchor.y - maxY);
12305
+ const distToBottom = Math.abs(anchor.y - minY);
12306
+ const minDist = Math.min(distToLeft, distToRight, distToTop, distToBottom);
12307
+ if (minDist === distToLeft) return { x: minX, y: anchor.y };
12308
+ if (minDist === distToRight) return { x: maxX, y: anchor.y };
12309
+ if (minDist === distToTop) return { x: anchor.x, y: maxY };
12310
+ return { x: anchor.x, y: minY };
12311
+ };
12312
+
12313
+ // src/components/GroupAnchorOffsetOverlay/index.tsx
12314
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
12315
+ var GroupAnchorOffsetOverlay = ({
12316
+ elements,
12317
+ highlightedPrimitives,
12318
+ transform,
12319
+ containerWidth,
12320
+ containerHeight,
12321
+ children
12322
+ }) => {
12323
+ const is_showing_group_anchor_offsets = useGlobalStore(
12324
+ (s) => s.is_showing_group_anchor_offsets
12325
+ );
12326
+ if (!is_showing_group_anchor_offsets || highlightedPrimitives.length === 0) {
12327
+ return null;
12328
+ }
12329
+ const hoveredPrimitive = highlightedPrimitives.find(
12330
+ (p) => p._parent_pcb_component?.type === "pcb_component" || p._element?.type === "pcb_component"
12331
+ );
12332
+ if (!hoveredPrimitive) return null;
12333
+ const pcbComponent = hoveredPrimitive._parent_pcb_component || hoveredPrimitive._element;
12334
+ if (!pcbComponent?.pcb_group_id) return null;
12335
+ const parentGroup = elements.filter((el) => el.type === "pcb_group").find((group) => group.pcb_group_id === pcbComponent.pcb_group_id);
12336
+ if (!parentGroup?.anchor_position) return null;
12337
+ const targetCenter = hoveredPrimitive._element?.type === "pcb_smtpad" ? { x: hoveredPrimitive.x, y: hoveredPrimitive.y } : pcbComponent.center || {
12338
+ x: hoveredPrimitive.x,
12339
+ y: hoveredPrimitive.y
12249
12340
  };
12250
- return distance3(p, projection);
12251
- }
12252
- function distance3(p1, p2) {
12253
- const dx = p1.x - p2.x;
12254
- const dy = p1.y - p2.y;
12255
- return Math.sqrt(dx * dx + dy * dy);
12256
- }
12341
+ const groupComponents = elements.filter((el) => el.type === "pcb_component").filter((comp) => comp.pcb_group_id === parentGroup.pcb_group_id);
12342
+ const boundingBox = calculateGroupBoundingBox(groupComponents);
12343
+ if (!boundingBox) return null;
12344
+ const groupBounds = {
12345
+ minX: boundingBox.minX - VISUAL_CONFIG.GROUP_PADDING,
12346
+ maxX: boundingBox.maxX + VISUAL_CONFIG.GROUP_PADDING,
12347
+ minY: boundingBox.minY - VISUAL_CONFIG.GROUP_PADDING,
12348
+ maxY: boundingBox.maxY + VISUAL_CONFIG.GROUP_PADDING
12349
+ };
12350
+ const anchorMarkerPosition = findAnchorMarkerPosition(
12351
+ parentGroup.anchor_position,
12352
+ groupBounds
12353
+ );
12354
+ const offsetX = targetCenter.x - anchorMarkerPosition.x;
12355
+ const offsetY = targetCenter.y - anchorMarkerPosition.y;
12356
+ const anchorMarkerScreen = applyToPoint12(transform, anchorMarkerPosition);
12357
+ const componentScreen = applyToPoint12(transform, targetCenter);
12358
+ const xLineLength = Math.abs(componentScreen.x - anchorMarkerScreen.x);
12359
+ const yLineLength = Math.abs(componentScreen.y - anchorMarkerScreen.y);
12360
+ const isComponentAboveAnchor = componentScreen.y < anchorMarkerScreen.y;
12361
+ const isComponentRightOfAnchor = componentScreen.x > anchorMarkerScreen.x;
12362
+ const xLabelOffset = isComponentAboveAnchor ? VISUAL_CONFIG.LABEL_OFFSET_ABOVE : VISUAL_CONFIG.LABEL_OFFSET_BELOW;
12363
+ const yLabelOffset = isComponentRightOfAnchor ? VISUAL_CONFIG.LABEL_OFFSET_RIGHT : VISUAL_CONFIG.LABEL_OFFSET_LEFT;
12364
+ const shouldShowXLabel = xLineLength > VISUAL_CONFIG.MIN_LINE_LENGTH_FOR_LABEL;
12365
+ const shouldShowYLabel = yLineLength > VISUAL_CONFIG.MIN_LINE_LENGTH_FOR_LABEL;
12366
+ const labelStyle = {
12367
+ color: COLORS.LABEL_TEXT,
12368
+ mixBlendMode: "difference",
12369
+ pointerEvents: "none",
12370
+ fontSize: VISUAL_CONFIG.LABEL_FONT_SIZE,
12371
+ fontFamily: "monospace",
12372
+ fontWeight: "bold"
12373
+ };
12374
+ return /* @__PURE__ */ jsxs9(
12375
+ "div",
12376
+ {
12377
+ style: {
12378
+ position: "absolute",
12379
+ left: 0,
12380
+ top: 0,
12381
+ width: containerWidth,
12382
+ height: containerHeight,
12383
+ overflow: "hidden",
12384
+ pointerEvents: "none",
12385
+ zIndex: zIndexMap.dimensionOverlay
12386
+ },
12387
+ children: [
12388
+ /* @__PURE__ */ jsxs9(
12389
+ "svg",
12390
+ {
12391
+ style: {
12392
+ position: "absolute",
12393
+ left: 0,
12394
+ top: 0,
12395
+ pointerEvents: "none"
12396
+ },
12397
+ width: containerWidth,
12398
+ height: containerHeight,
12399
+ children: [
12400
+ /* @__PURE__ */ jsx12(
12401
+ "line",
12402
+ {
12403
+ x1: anchorMarkerScreen.x,
12404
+ y1: anchorMarkerScreen.y,
12405
+ x2: componentScreen.x,
12406
+ y2: anchorMarkerScreen.y,
12407
+ stroke: COLORS.OFFSET_LINE,
12408
+ strokeWidth: VISUAL_CONFIG.LINE_STROKE_WIDTH,
12409
+ strokeDasharray: VISUAL_CONFIG.LINE_DASH_PATTERN
12410
+ }
12411
+ ),
12412
+ /* @__PURE__ */ jsx12(
12413
+ "line",
12414
+ {
12415
+ x1: componentScreen.x,
12416
+ y1: anchorMarkerScreen.y,
12417
+ x2: componentScreen.x,
12418
+ y2: componentScreen.y,
12419
+ stroke: COLORS.OFFSET_LINE,
12420
+ strokeWidth: VISUAL_CONFIG.LINE_STROKE_WIDTH,
12421
+ strokeDasharray: VISUAL_CONFIG.LINE_DASH_PATTERN
12422
+ }
12423
+ ),
12424
+ /* @__PURE__ */ jsx12(
12425
+ "circle",
12426
+ {
12427
+ cx: componentScreen.x,
12428
+ cy: componentScreen.y,
12429
+ r: VISUAL_CONFIG.COMPONENT_MARKER_RADIUS,
12430
+ fill: COLORS.COMPONENT_MARKER_FILL,
12431
+ stroke: COLORS.COMPONENT_MARKER_STROKE,
12432
+ strokeWidth: 1
12433
+ }
12434
+ )
12435
+ ]
12436
+ }
12437
+ ),
12438
+ shouldShowXLabel && /* @__PURE__ */ jsxs9(
12439
+ "div",
12440
+ {
12441
+ style: {
12442
+ ...labelStyle,
12443
+ position: "absolute",
12444
+ left: Math.min(anchorMarkerScreen.x, componentScreen.x),
12445
+ top: anchorMarkerScreen.y + xLabelOffset,
12446
+ width: Math.abs(componentScreen.x - anchorMarkerScreen.x),
12447
+ textAlign: "center"
12448
+ },
12449
+ children: [
12450
+ "\u0394x: ",
12451
+ offsetX.toFixed(2),
12452
+ "mm"
12453
+ ]
12454
+ }
12455
+ ),
12456
+ shouldShowYLabel && /* @__PURE__ */ jsxs9(
12457
+ "div",
12458
+ {
12459
+ style: {
12460
+ ...labelStyle,
12461
+ position: "absolute",
12462
+ left: componentScreen.x + yLabelOffset,
12463
+ top: Math.min(anchorMarkerScreen.y, componentScreen.y),
12464
+ height: Math.abs(componentScreen.y - anchorMarkerScreen.y),
12465
+ display: "flex",
12466
+ flexDirection: "column",
12467
+ justifyContent: "center"
12468
+ },
12469
+ children: [
12470
+ "\u0394y: ",
12471
+ offsetY.toFixed(2),
12472
+ "mm"
12473
+ ]
12474
+ }
12475
+ )
12476
+ ]
12477
+ }
12478
+ );
12479
+ };
12257
12480
 
12258
12481
  // src/components/MouseElementTracker.tsx
12259
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
12482
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
12260
12483
  var getPolygonBoundingBox = (points) => {
12261
12484
  if (points.length === 0) return null;
12262
12485
  let minX = points[0].x;
@@ -12298,22 +12521,22 @@ var getPrimitivesUnderPoint = (primitives, rwPoint, transform) => {
12298
12521
  for (const primitive of primitives) {
12299
12522
  if (!primitive._element) continue;
12300
12523
  if ("x1" in primitive && primitive._element?.type === "pcb_trace") {
12301
- const distance5 = pointToSegmentDistance2(
12524
+ const distance3 = pointToSegmentDistance(
12302
12525
  { x: rwPoint.x, y: rwPoint.y },
12303
12526
  { x: primitive.x1, y: primitive.y1 },
12304
12527
  { x: primitive.x2, y: primitive.y2 }
12305
12528
  );
12306
12529
  const lineWidth = primitive.width || 0.5;
12307
12530
  const detectionThreshold = Math.max(lineWidth * 25, 2) / transform.a;
12308
- if (distance5 < detectionThreshold) {
12531
+ if (distance3 < detectionThreshold) {
12309
12532
  newMousedPrimitives.push(primitive);
12310
12533
  }
12311
12534
  continue;
12312
12535
  }
12313
12536
  if (primitive.pcb_drawing_type === "polygon") {
12314
12537
  const points = primitive.points.map((point) => ({
12315
- x: distance4.parse(point.x),
12316
- y: distance4.parse(point.y)
12538
+ x: distance2.parse(point.x),
12539
+ y: distance2.parse(point.y)
12317
12540
  }));
12318
12541
  const boundingBox = getPolygonBoundingBox(points);
12319
12542
  if (!boundingBox) continue;
@@ -12327,8 +12550,8 @@ var getPrimitivesUnderPoint = (primitives, rwPoint, transform) => {
12327
12550
  }
12328
12551
  if (primitive.pcb_drawing_type === "polygon_with_arcs") {
12329
12552
  const points = primitive.brep_shape.outer_ring.vertices.map((v) => ({
12330
- x: distance4.parse(v.x),
12331
- y: distance4.parse(v.y)
12553
+ x: distance2.parse(v.x),
12554
+ y: distance2.parse(v.y)
12332
12555
  }));
12333
12556
  const boundingBox = getPolygonBoundingBox(points);
12334
12557
  if (!boundingBox) continue;
@@ -12370,6 +12593,7 @@ var MouseElementTracker = ({
12370
12593
  }) => {
12371
12594
  const [mousedPrimitives, setMousedPrimitives] = useState7([]);
12372
12595
  const [mousePos, setMousePos] = useState7({ x: 0, y: 0 });
12596
+ const [containerRef, { width, height }] = useMeasure_default();
12373
12597
  const highlightedPrimitives = useMemo5(() => {
12374
12598
  const highlightedPrimitives2 = [];
12375
12599
  for (const primitive of mousedPrimitives) {
@@ -12401,7 +12625,7 @@ var MouseElementTracker = ({
12401
12625
  h = "h" in primitive ? primitive.h : "r" in primitive ? primitive.r * 2 : "rX" in primitive && "rY" in primitive ? primitive.rY * 2 : 0;
12402
12626
  }
12403
12627
  if (!basePoint) continue;
12404
- const screenPos = applyToPoint12(transform, basePoint);
12628
+ const screenPos = applyToPoint13(transform, basePoint);
12405
12629
  const screenSize = {
12406
12630
  w: w * transform.a,
12407
12631
  h: h * transform.a
@@ -12426,7 +12650,7 @@ var MouseElementTracker = ({
12426
12650
  }, [mousedPrimitives, transform]);
12427
12651
  const handleInteraction = (x, y, transform2, primitives2) => {
12428
12652
  setMousePos({ x, y });
12429
- const rwPoint = applyToPoint12(inverse5(transform2), { x, y });
12653
+ const rwPoint = applyToPoint13(inverse5(transform2), { x, y });
12430
12654
  const newMousedPrimitives = getPrimitivesUnderPoint(
12431
12655
  primitives2,
12432
12656
  rwPoint,
@@ -12441,10 +12665,11 @@ var MouseElementTracker = ({
12441
12665
  setMousedPrimitives(newMousedPrimitives);
12442
12666
  onMouseHoverOverPrimitives(newMousedPrimitives);
12443
12667
  };
12444
- return /* @__PURE__ */ jsxs9(
12668
+ return /* @__PURE__ */ jsxs10(
12445
12669
  "div",
12446
12670
  {
12447
- style: { position: "relative" },
12671
+ ref: containerRef,
12672
+ style: { position: "relative", width: "100%", height: "100%" },
12448
12673
  onMouseMove: (e) => {
12449
12674
  if (transform) {
12450
12675
  const rect = e.currentTarget.getBoundingClientRect();
@@ -12464,13 +12689,23 @@ var MouseElementTracker = ({
12464
12689
  },
12465
12690
  children: [
12466
12691
  children,
12467
- /* @__PURE__ */ jsx12(
12692
+ /* @__PURE__ */ jsx13(
12468
12693
  ElementOverlayBox,
12469
12694
  {
12470
12695
  elements,
12471
12696
  mousePos,
12472
12697
  highlightedPrimitives
12473
12698
  }
12699
+ ),
12700
+ transform && /* @__PURE__ */ jsx13(
12701
+ GroupAnchorOffsetOverlay,
12702
+ {
12703
+ elements,
12704
+ highlightedPrimitives,
12705
+ transform,
12706
+ containerWidth: width,
12707
+ containerHeight: height
12708
+ }
12474
12709
  )
12475
12710
  ]
12476
12711
  }
@@ -12478,10 +12713,10 @@ var MouseElementTracker = ({
12478
12713
  };
12479
12714
 
12480
12715
  // src/components/PcbGroupOverlay.tsx
12481
- import { applyToPoint as applyToPoint13 } from "transformation-matrix";
12716
+ import { applyToPoint as applyToPoint14 } from "transformation-matrix";
12482
12717
  import { identity as identity8 } from "transformation-matrix";
12483
12718
  import { useRef as useRef9, useEffect as useEffect12 } from "react";
12484
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
12719
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
12485
12720
  var GROUP_COLORS = [
12486
12721
  "rgb(255, 100, 100)",
12487
12722
  "rgb(100, 255, 100)",
@@ -12610,10 +12845,10 @@ var PcbGroupOverlay = ({
12610
12845
  maxX += totalPadding;
12611
12846
  minY -= totalPadding;
12612
12847
  maxY += totalPadding;
12613
- const topLeft = applyToPoint13(transform, { x: minX, y: maxY });
12614
- const topRight = applyToPoint13(transform, { x: maxX, y: maxY });
12615
- const bottomLeft = applyToPoint13(transform, { x: minX, y: minY });
12616
- const bottomRight = applyToPoint13(transform, { x: maxX, y: minY });
12848
+ const topLeft = applyToPoint14(transform, { x: minX, y: maxY });
12849
+ const topRight = applyToPoint14(transform, { x: maxX, y: maxY });
12850
+ const bottomLeft = applyToPoint14(transform, { x: minX, y: minY });
12851
+ const bottomRight = applyToPoint14(transform, { x: maxX, y: minY });
12617
12852
  const groupColor = GROUP_COLORS[groupIndex % GROUP_COLORS.length];
12618
12853
  ctx.strokeStyle = groupColor;
12619
12854
  ctx.lineWidth = 2;
@@ -12683,7 +12918,7 @@ var PcbGroupOverlay = ({
12683
12918
  } else {
12684
12919
  edgePoint = { x: anchor.x, y: groupBottom };
12685
12920
  }
12686
- const anchorScreenPos = applyToPoint13(transform, edgePoint);
12921
+ const anchorScreenPos = applyToPoint14(transform, edgePoint);
12687
12922
  ctx.strokeStyle = "white";
12688
12923
  ctx.lineWidth = 1.5;
12689
12924
  ctx.setLineDash([]);
@@ -12706,14 +12941,14 @@ var PcbGroupOverlay = ({
12706
12941
  is_showing_pcb_groups,
12707
12942
  pcb_group_view_mode
12708
12943
  ]);
12709
- return /* @__PURE__ */ jsxs10(
12944
+ return /* @__PURE__ */ jsxs11(
12710
12945
  "div",
12711
12946
  {
12712
12947
  ref: containerRef,
12713
12948
  style: { position: "relative", width: "100%", height: "100%" },
12714
12949
  children: [
12715
12950
  children,
12716
- /* @__PURE__ */ jsx13(
12951
+ /* @__PURE__ */ jsx14(
12717
12952
  "canvas",
12718
12953
  {
12719
12954
  ref: canvasRef,
@@ -12733,9 +12968,9 @@ var PcbGroupOverlay = ({
12733
12968
  };
12734
12969
 
12735
12970
  // src/components/RatsNestOverlay.tsx
12736
- import { applyToPoint as applyToPoint14, identity as identity9 } from "transformation-matrix";
12971
+ import { applyToPoint as applyToPoint15, identity as identity9 } from "transformation-matrix";
12737
12972
  import { useMemo as useMemo6 } from "react";
12738
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
12973
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
12739
12974
  var RatsNestOverlay = ({ transform, soup, children }) => {
12740
12975
  const isShowingRatsNest = useGlobalStore((s) => s.is_showing_rats_nest);
12741
12976
  const { netMap, idToNetMap } = useMemo6(
@@ -12758,11 +12993,11 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12758
12993
  connectedIds.forEach((id) => {
12759
12994
  const pos = getElementPosition(id);
12760
12995
  if (pos) {
12761
- const distance5 = Math.sqrt(
12996
+ const distance3 = Math.sqrt(
12762
12997
  (sourcePoint.x - pos.x) ** 2 + (sourcePoint.y - pos.y) ** 2
12763
12998
  );
12764
- if (distance5 < minDistance && distance5 > 0) {
12765
- minDistance = distance5;
12999
+ if (distance3 < minDistance && distance3 > 0) {
13000
+ minDistance = distance3;
12766
13001
  nearestPoint = pos;
12767
13002
  }
12768
13003
  }
@@ -12798,9 +13033,9 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12798
13033
  }, [soup, netMap, idToNetMap, isShowingRatsNest]);
12799
13034
  if (!soup || !isShowingRatsNest) return children;
12800
13035
  if (!transform) transform = identity9();
12801
- return /* @__PURE__ */ jsxs11("div", { style: { position: "relative" }, children: [
13036
+ return /* @__PURE__ */ jsxs12("div", { style: { position: "relative" }, children: [
12802
13037
  children,
12803
- /* @__PURE__ */ jsx14(
13038
+ /* @__PURE__ */ jsx15(
12804
13039
  "svg",
12805
13040
  {
12806
13041
  style: {
@@ -12814,9 +13049,9 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12814
13049
  zIndex: zIndexMap.ratsNestOverlay
12815
13050
  },
12816
13051
  children: ratsNestLines.map(({ key, startPoint, endPoint, isInNet }) => {
12817
- const transformedStart = applyToPoint14(transform, startPoint);
12818
- const transformedEnd = applyToPoint14(transform, endPoint);
12819
- return /* @__PURE__ */ jsx14(
13052
+ const transformedStart = applyToPoint15(transform, startPoint);
13053
+ const transformedEnd = applyToPoint15(transform, endPoint);
13054
+ return /* @__PURE__ */ jsx15(
12820
13055
  "line",
12821
13056
  {
12822
13057
  x1: transformedStart.x,
@@ -12836,18 +13071,13 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12836
13071
  };
12837
13072
 
12838
13073
  // src/components/ToolbarOverlay.tsx
12839
- import {
12840
- useEffect as useEffect15,
12841
- useState as useState9,
12842
- useCallback as useCallback4,
12843
- useRef as useRef10
12844
- } from "react";
13074
+ import { useEffect as useEffect15, useState as useState9, useCallback as useCallback4, useRef as useRef10 } from "react";
12845
13075
  import { css as css3 } from "@emotion/css";
12846
13076
 
12847
13077
  // package.json
12848
13078
  var package_default = {
12849
13079
  name: "@tscircuit/pcb-viewer",
12850
- version: "1.11.265",
13080
+ version: "1.11.266",
12851
13081
  main: "dist/index.js",
12852
13082
  type: "module",
12853
13083
  repository: "tscircuit/pcb-viewer",
@@ -12898,6 +13128,7 @@ var package_default = {
12898
13128
  dependencies: {
12899
13129
  "@emotion/css": "^11.11.2",
12900
13130
  "@tscircuit/alphabet": "^0.0.3",
13131
+ "@tscircuit/math-utils": "^0.0.29",
12901
13132
  "@vitejs/plugin-react": "^5.0.2",
12902
13133
  "circuit-json": "^0.0.321",
12903
13134
  "circuit-to-svg": "^0.0.271",
@@ -12952,9 +13183,9 @@ var useIsSmallScreen = () => {
12952
13183
  };
12953
13184
 
12954
13185
  // src/components/ToolbarOverlay.tsx
12955
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
13186
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
12956
13187
  var LayerButton = ({ name, selected, onClick }) => {
12957
- return /* @__PURE__ */ jsxs12(
13188
+ return /* @__PURE__ */ jsxs13(
12958
13189
  "div",
12959
13190
  {
12960
13191
  className: css3`
@@ -12970,8 +13201,8 @@ var LayerButton = ({ name, selected, onClick }) => {
12970
13201
  `,
12971
13202
  onClick,
12972
13203
  children: [
12973
- /* @__PURE__ */ jsx15("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
12974
- /* @__PURE__ */ jsx15(
13204
+ /* @__PURE__ */ jsx16("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
13205
+ /* @__PURE__ */ jsx16(
12975
13206
  "span",
12976
13207
  {
12977
13208
  style: {
@@ -12991,7 +13222,7 @@ var ToolbarButton = ({
12991
13222
  isSmallScreen,
12992
13223
  onClick,
12993
13224
  ...props
12994
- }) => /* @__PURE__ */ jsx15(
13225
+ }) => /* @__PURE__ */ jsx16(
12995
13226
  "div",
12996
13227
  {
12997
13228
  ...props,
@@ -13028,7 +13259,7 @@ var CheckboxMenuItem = ({
13028
13259
  checked,
13029
13260
  onClick
13030
13261
  }) => {
13031
- return /* @__PURE__ */ jsxs12(
13262
+ return /* @__PURE__ */ jsxs13(
13032
13263
  "div",
13033
13264
  {
13034
13265
  className: css3`
@@ -13055,15 +13286,15 @@ var CheckboxMenuItem = ({
13055
13286
  onClick();
13056
13287
  },
13057
13288
  children: [
13058
- /* @__PURE__ */ jsx15("input", { type: "checkbox", checked, onChange: () => {
13289
+ /* @__PURE__ */ jsx16("input", { type: "checkbox", checked, onChange: () => {
13059
13290
  }, readOnly: true }),
13060
- /* @__PURE__ */ jsx15("span", { style: { color: "#eee" }, children: label })
13291
+ /* @__PURE__ */ jsx16("span", { style: { color: "#eee" }, children: label })
13061
13292
  ]
13062
13293
  }
13063
13294
  );
13064
13295
  };
13065
13296
  var RadioMenuItem = ({ label, checked, onClick }) => {
13066
- return /* @__PURE__ */ jsxs12(
13297
+ return /* @__PURE__ */ jsxs13(
13067
13298
  "div",
13068
13299
  {
13069
13300
  className: css3`
@@ -13090,9 +13321,9 @@ var RadioMenuItem = ({ label, checked, onClick }) => {
13090
13321
  onClick();
13091
13322
  },
13092
13323
  children: [
13093
- /* @__PURE__ */ jsx15("input", { type: "radio", checked, onChange: () => {
13324
+ /* @__PURE__ */ jsx16("input", { type: "radio", checked, onChange: () => {
13094
13325
  }, readOnly: true }),
13095
- /* @__PURE__ */ jsx15("span", { style: { color: "#eee" }, children: label })
13326
+ /* @__PURE__ */ jsx16("span", { style: { color: "#eee" }, children: label })
13096
13327
  ]
13097
13328
  }
13098
13329
  );
@@ -13113,6 +13344,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13113
13344
  setIsShowingDrcErrors,
13114
13345
  setIsShowingCopperPours,
13115
13346
  setIsShowingPcbGroups,
13347
+ setIsShowingGroupAnchorOffsets,
13116
13348
  setPcbGroupViewMode,
13117
13349
  setHoveredErrorId
13118
13350
  } = useGlobalStore((s) => ({
@@ -13131,6 +13363,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13131
13363
  is_showing_drc_errors: s.is_showing_drc_errors,
13132
13364
  is_showing_copper_pours: s.is_showing_copper_pours,
13133
13365
  is_showing_pcb_groups: s.is_showing_pcb_groups,
13366
+ is_showing_group_anchor_offsets: s.is_showing_group_anchor_offsets,
13134
13367
  pcb_group_view_mode: s.pcb_group_view_mode
13135
13368
  },
13136
13369
  setEditMode: s.setEditMode,
@@ -13140,6 +13373,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13140
13373
  setIsShowingDrcErrors: s.setIsShowingDrcErrors,
13141
13374
  setIsShowingCopperPours: s.setIsShowingCopperPours,
13142
13375
  setIsShowingPcbGroups: s.setIsShowingPcbGroups,
13376
+ setIsShowingGroupAnchorOffsets: s.setIsShowingGroupAnchorOffsets,
13143
13377
  setPcbGroupViewMode: s.setPcbGroupViewMode,
13144
13378
  setHoveredErrorId: s.setHoveredErrorId
13145
13379
  }));
@@ -13238,7 +13472,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13238
13472
  setErrorsOpen(false);
13239
13473
  }
13240
13474
  }, [isViewMenuOpen]);
13241
- return /* @__PURE__ */ jsxs12(
13475
+ return /* @__PURE__ */ jsxs13(
13242
13476
  "div",
13243
13477
  {
13244
13478
  style: { position: "relative", zIndex: "999 !important" },
@@ -13246,7 +13480,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13246
13480
  onMouseLeave: handleMouseLeave,
13247
13481
  children: [
13248
13482
  children,
13249
- /* @__PURE__ */ jsxs12(
13483
+ /* @__PURE__ */ jsxs13(
13250
13484
  "div",
13251
13485
  {
13252
13486
  style: {
@@ -13267,7 +13501,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13267
13501
  ]
13268
13502
  }
13269
13503
  ),
13270
- /* @__PURE__ */ jsxs12(
13504
+ /* @__PURE__ */ jsxs13(
13271
13505
  "div",
13272
13506
  {
13273
13507
  "data-toolbar-overlay": true,
@@ -13290,7 +13524,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13290
13524
  fontFamily: "sans-serif"
13291
13525
  },
13292
13526
  children: [
13293
- /* @__PURE__ */ jsxs12(
13527
+ /* @__PURE__ */ jsxs13(
13294
13528
  ToolbarButton,
13295
13529
  {
13296
13530
  isSmallScreen,
@@ -13301,10 +13535,10 @@ var ToolbarOverlay = ({ children, elements }) => {
13301
13535
  }
13302
13536
  },
13303
13537
  children: [
13304
- /* @__PURE__ */ jsxs12("div", { children: [
13538
+ /* @__PURE__ */ jsxs13("div", { children: [
13305
13539
  "layer:",
13306
13540
  " ",
13307
- /* @__PURE__ */ jsx15(
13541
+ /* @__PURE__ */ jsx16(
13308
13542
  "span",
13309
13543
  {
13310
13544
  style: {
@@ -13316,7 +13550,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13316
13550
  }
13317
13551
  )
13318
13552
  ] }),
13319
- isLayerMenuOpen && /* @__PURE__ */ jsx15("div", { style: { marginTop: 4, minWidth: 120 }, children: processedLayers.map((layer) => /* @__PURE__ */ jsx15(
13553
+ isLayerMenuOpen && /* @__PURE__ */ jsx16("div", { style: { marginTop: 4, minWidth: 120 }, children: processedLayers.map((layer) => /* @__PURE__ */ jsx16(
13320
13554
  LayerButton,
13321
13555
  {
13322
13556
  name: layer,
@@ -13330,7 +13564,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13330
13564
  ]
13331
13565
  }
13332
13566
  ),
13333
- /* @__PURE__ */ jsx15(
13567
+ /* @__PURE__ */ jsx16(
13334
13568
  ToolbarButton,
13335
13569
  {
13336
13570
  isSmallScreen,
@@ -13339,13 +13573,13 @@ var ToolbarOverlay = ({ children, elements }) => {
13339
13573
  ...errorCount > 0 ? { color: "red" } : {}
13340
13574
  },
13341
13575
  onClick: handleErrorsToggle,
13342
- children: /* @__PURE__ */ jsxs12("div", { children: [
13576
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13343
13577
  errorCount,
13344
13578
  " errors"
13345
13579
  ] })
13346
13580
  }
13347
13581
  ),
13348
- isErrorsOpen && errorCount > 0 && /* @__PURE__ */ jsx15(
13582
+ isErrorsOpen && errorCount > 0 && /* @__PURE__ */ jsx16(
13349
13583
  "div",
13350
13584
  {
13351
13585
  style: {
@@ -13363,14 +13597,14 @@ var ToolbarOverlay = ({ children, elements }) => {
13363
13597
  },
13364
13598
  children: errorElements.map((e, i) => {
13365
13599
  const errorId = e.pcb_trace_error_id || `error_${i}_${e.error_type}_${e.message?.slice(0, 20)}`;
13366
- return /* @__PURE__ */ jsxs12(
13600
+ return /* @__PURE__ */ jsxs13(
13367
13601
  "div",
13368
13602
  {
13369
13603
  style: {
13370
13604
  borderBottom: i < errorElements.length - 1 ? "1px solid #444" : "none"
13371
13605
  },
13372
13606
  children: [
13373
- /* @__PURE__ */ jsxs12(
13607
+ /* @__PURE__ */ jsxs13(
13374
13608
  "div",
13375
13609
  {
13376
13610
  style: {
@@ -13421,7 +13655,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13421
13655
  }
13422
13656
  },
13423
13657
  children: [
13424
- /* @__PURE__ */ jsx15(
13658
+ /* @__PURE__ */ jsx16(
13425
13659
  "div",
13426
13660
  {
13427
13661
  style: {
@@ -13435,7 +13669,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13435
13669
  children: e.error_type
13436
13670
  }
13437
13671
  ),
13438
- /* @__PURE__ */ jsx15(
13672
+ /* @__PURE__ */ jsx16(
13439
13673
  "div",
13440
13674
  {
13441
13675
  style: {
@@ -13450,7 +13684,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13450
13684
  children: e.message
13451
13685
  }
13452
13686
  ),
13453
- /* @__PURE__ */ jsx15(
13687
+ /* @__PURE__ */ jsx16(
13454
13688
  "div",
13455
13689
  {
13456
13690
  ref: (el) => {
@@ -13470,7 +13704,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13470
13704
  ]
13471
13705
  }
13472
13706
  ),
13473
- /* @__PURE__ */ jsx15(
13707
+ /* @__PURE__ */ jsx16(
13474
13708
  "div",
13475
13709
  {
13476
13710
  ref: (el) => {
@@ -13483,7 +13717,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13483
13717
  backgroundColor: "#1a1a1a",
13484
13718
  borderTop: "1px solid #444"
13485
13719
  },
13486
- children: /* @__PURE__ */ jsx15(
13720
+ children: /* @__PURE__ */ jsx16(
13487
13721
  "div",
13488
13722
  {
13489
13723
  style: {
@@ -13506,58 +13740,58 @@ var ToolbarOverlay = ({ children, elements }) => {
13506
13740
  })
13507
13741
  }
13508
13742
  ),
13509
- /* @__PURE__ */ jsx15(
13743
+ /* @__PURE__ */ jsx16(
13510
13744
  ToolbarButton,
13511
13745
  {
13512
13746
  isSmallScreen,
13513
13747
  style: {},
13514
13748
  onClick: handleEditTraceToggle,
13515
- children: /* @__PURE__ */ jsxs12("div", { children: [
13749
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13516
13750
  editModes.in_draw_trace_mode ? "\u2716 " : "",
13517
13751
  "Edit Traces"
13518
13752
  ] })
13519
13753
  }
13520
13754
  ),
13521
- /* @__PURE__ */ jsx15(
13755
+ /* @__PURE__ */ jsx16(
13522
13756
  ToolbarButton,
13523
13757
  {
13524
13758
  isSmallScreen,
13525
13759
  style: {},
13526
13760
  onClick: handleMoveComponentToggle,
13527
- children: /* @__PURE__ */ jsxs12("div", { children: [
13761
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13528
13762
  editModes.in_move_footprint_mode ? "\u2716 " : "",
13529
13763
  "Move Components"
13530
13764
  ] })
13531
13765
  }
13532
13766
  ),
13533
- /* @__PURE__ */ jsx15(
13767
+ /* @__PURE__ */ jsx16(
13534
13768
  ToolbarButton,
13535
13769
  {
13536
13770
  isSmallScreen,
13537
13771
  style: {},
13538
13772
  onClick: handleRatsNestToggle,
13539
- children: /* @__PURE__ */ jsxs12("div", { children: [
13773
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13540
13774
  viewSettings.is_showing_rats_nest ? "\u2716 " : "",
13541
13775
  "Rats Nest"
13542
13776
  ] })
13543
13777
  }
13544
13778
  ),
13545
- /* @__PURE__ */ jsx15(
13779
+ /* @__PURE__ */ jsx16(
13546
13780
  ToolbarButton,
13547
13781
  {
13548
13782
  isSmallScreen,
13549
13783
  style: measureToolArmed ? { backgroundColor: "#444" } : {},
13550
13784
  onClick: handleMeasureToolClick,
13551
- children: /* @__PURE__ */ jsx15("div", { children: "\u{1F4CF}" })
13785
+ children: /* @__PURE__ */ jsx16("div", { children: "\u{1F4CF}" })
13552
13786
  }
13553
13787
  ),
13554
- /* @__PURE__ */ jsx15(
13788
+ /* @__PURE__ */ jsx16(
13555
13789
  ToolbarButton,
13556
13790
  {
13557
13791
  isSmallScreen,
13558
13792
  onClick: handleViewMenuToggle,
13559
- children: /* @__PURE__ */ jsxs12("div", { children: [
13560
- /* @__PURE__ */ jsxs12(
13793
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13794
+ /* @__PURE__ */ jsxs13(
13561
13795
  "div",
13562
13796
  {
13563
13797
  style: {
@@ -13567,7 +13801,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13567
13801
  },
13568
13802
  children: [
13569
13803
  "View",
13570
- /* @__PURE__ */ jsx15(
13804
+ /* @__PURE__ */ jsx16(
13571
13805
  "span",
13572
13806
  {
13573
13807
  style: {
@@ -13582,8 +13816,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13582
13816
  ]
13583
13817
  }
13584
13818
  ),
13585
- isViewMenuOpen && /* @__PURE__ */ jsxs12("div", { style: { marginTop: 4, minWidth: 120 }, children: [
13586
- /* @__PURE__ */ jsx15(
13819
+ isViewMenuOpen && /* @__PURE__ */ jsxs13("div", { style: { marginTop: 4, minWidth: 120 }, children: [
13820
+ /* @__PURE__ */ jsx16(
13587
13821
  CheckboxMenuItem,
13588
13822
  {
13589
13823
  label: "Show All Trace Lengths",
@@ -13595,7 +13829,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13595
13829
  }
13596
13830
  }
13597
13831
  ),
13598
- /* @__PURE__ */ jsx15(
13832
+ /* @__PURE__ */ jsx16(
13599
13833
  CheckboxMenuItem,
13600
13834
  {
13601
13835
  label: "Show Autorouting Animation",
@@ -13607,7 +13841,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13607
13841
  }
13608
13842
  }
13609
13843
  ),
13610
- /* @__PURE__ */ jsx15(
13844
+ /* @__PURE__ */ jsx16(
13611
13845
  CheckboxMenuItem,
13612
13846
  {
13613
13847
  label: "Show DRC Errors",
@@ -13617,7 +13851,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13617
13851
  }
13618
13852
  }
13619
13853
  ),
13620
- /* @__PURE__ */ jsx15(
13854
+ /* @__PURE__ */ jsx16(
13621
13855
  CheckboxMenuItem,
13622
13856
  {
13623
13857
  label: "Show Copper Pours",
@@ -13629,7 +13863,19 @@ var ToolbarOverlay = ({ children, elements }) => {
13629
13863
  }
13630
13864
  }
13631
13865
  ),
13632
- /* @__PURE__ */ jsx15(
13866
+ /* @__PURE__ */ jsx16(
13867
+ CheckboxMenuItem,
13868
+ {
13869
+ label: "Show Group Anchor Offsets",
13870
+ checked: viewSettings.is_showing_group_anchor_offsets,
13871
+ onClick: () => {
13872
+ setIsShowingGroupAnchorOffsets(
13873
+ !viewSettings.is_showing_group_anchor_offsets
13874
+ );
13875
+ }
13876
+ }
13877
+ ),
13878
+ /* @__PURE__ */ jsx16(
13633
13879
  CheckboxMenuItem,
13634
13880
  {
13635
13881
  label: "Show PCB Groups",
@@ -13639,8 +13885,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13639
13885
  }
13640
13886
  }
13641
13887
  ),
13642
- viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs12("div", { style: { marginLeft: 16 }, children: [
13643
- /* @__PURE__ */ jsx15(
13888
+ viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs13("div", { style: { marginLeft: 16 }, children: [
13889
+ /* @__PURE__ */ jsx16(
13644
13890
  RadioMenuItem,
13645
13891
  {
13646
13892
  label: "Show All Groups",
@@ -13650,7 +13896,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13650
13896
  }
13651
13897
  }
13652
13898
  ),
13653
- /* @__PURE__ */ jsx15(
13899
+ /* @__PURE__ */ jsx16(
13654
13900
  RadioMenuItem,
13655
13901
  {
13656
13902
  label: "Show Named Groups",
@@ -13674,7 +13920,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13674
13920
  };
13675
13921
 
13676
13922
  // src/components/CanvasElementsRenderer.tsx
13677
- import { jsx as jsx16 } from "react/jsx-runtime";
13923
+ import { jsx as jsx17 } from "react/jsx-runtime";
13678
13924
  var CanvasElementsRenderer = (props) => {
13679
13925
  const { transform, elements } = props;
13680
13926
  const hoveredErrorId = useGlobalStore((state) => state.hovered_error_id);
@@ -13751,14 +13997,14 @@ var CanvasElementsRenderer = (props) => {
13751
13997
  },
13752
13998
  [connectivityMap]
13753
13999
  );
13754
- return /* @__PURE__ */ jsx16(
14000
+ return /* @__PURE__ */ jsx17(
13755
14001
  MouseElementTracker,
13756
14002
  {
13757
14003
  elements: elementsToRender,
13758
14004
  transform,
13759
14005
  primitives: primitivesWithoutInteractionMetadata,
13760
14006
  onMouseHoverOverPrimitives: onMouseOverPrimitives,
13761
- children: /* @__PURE__ */ jsx16(
14007
+ children: /* @__PURE__ */ jsx17(
13762
14008
  EditPlacementOverlay,
13763
14009
  {
13764
14010
  disabled: !props.allowEditing,
@@ -13767,7 +14013,7 @@ var CanvasElementsRenderer = (props) => {
13767
14013
  cancelPanDrag: props.cancelPanDrag,
13768
14014
  onCreateEditEvent: props.onCreateEditEvent,
13769
14015
  onModifyEditEvent: props.onModifyEditEvent,
13770
- children: /* @__PURE__ */ jsx16(
14016
+ children: /* @__PURE__ */ jsx17(
13771
14017
  EditTraceHintOverlay,
13772
14018
  {
13773
14019
  disabled: !props.allowEditing,
@@ -13776,23 +14022,23 @@ var CanvasElementsRenderer = (props) => {
13776
14022
  cancelPanDrag: props.cancelPanDrag,
13777
14023
  onCreateEditEvent: props.onCreateEditEvent,
13778
14024
  onModifyEditEvent: props.onModifyEditEvent,
13779
- children: /* @__PURE__ */ jsx16(
14025
+ children: /* @__PURE__ */ jsx17(
13780
14026
  DimensionOverlay,
13781
14027
  {
13782
14028
  transform,
13783
14029
  focusOnHover: props.focusOnHover,
13784
14030
  primitives: primitivesWithoutInteractionMetadata,
13785
- 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(
14031
+ 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(
13786
14032
  DebugGraphicsOverlay,
13787
14033
  {
13788
14034
  transform,
13789
14035
  debugGraphics: props.debugGraphics,
13790
- children: /* @__PURE__ */ jsx16(
14036
+ children: /* @__PURE__ */ jsx17(
13791
14037
  WarningGraphicsOverlay,
13792
14038
  {
13793
14039
  transform,
13794
14040
  elements,
13795
- children: /* @__PURE__ */ jsx16(
14041
+ children: /* @__PURE__ */ jsx17(
13796
14042
  CanvasPrimitiveRenderer,
13797
14043
  {
13798
14044
  transform,
@@ -13859,7 +14105,7 @@ var calculateCircuitJsonKey = (circuitJson) => {
13859
14105
  };
13860
14106
 
13861
14107
  // src/PCBViewer.tsx
13862
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
14108
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
13863
14109
  var defaultTransform = compose7(translate11(400, 300), scale5(40, -40));
13864
14110
  var PCBViewer = ({
13865
14111
  circuitJson,
@@ -13953,14 +14199,14 @@ var PCBViewer = ({
13953
14199
  }),
13954
14200
  [initialState, disablePcbGroups]
13955
14201
  );
13956
- return /* @__PURE__ */ jsxs13("div", { ref: transformRef, style: { position: "relative" }, children: [
13957
- /* @__PURE__ */ jsx17("div", { ref, children: /* @__PURE__ */ jsxs13(
14202
+ return /* @__PURE__ */ jsxs14("div", { ref: transformRef, style: { position: "relative" }, children: [
14203
+ /* @__PURE__ */ jsx18("div", { ref, children: /* @__PURE__ */ jsxs14(
13958
14204
  ContextProviders,
13959
14205
  {
13960
14206
  initialState: mergedInitialState,
13961
14207
  disablePcbGroups,
13962
14208
  children: [
13963
- /* @__PURE__ */ jsx17(
14209
+ /* @__PURE__ */ jsx18(
13964
14210
  CanvasElementsRenderer,
13965
14211
  {
13966
14212
  transform,
@@ -13985,11 +14231,11 @@ var PCBViewer = ({
13985
14231
  },
13986
14232
  refDimensions.width
13987
14233
  ),
13988
- /* @__PURE__ */ jsx17(ToastContainer, {})
14234
+ /* @__PURE__ */ jsx18(ToastContainer, {})
13989
14235
  ]
13990
14236
  }
13991
14237
  ) }),
13992
- clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx17(
14238
+ clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx18(
13993
14239
  "div",
13994
14240
  {
13995
14241
  onClick: () => {
@@ -14026,7 +14272,7 @@ var PCBViewer = ({
14026
14272
  justifyContent: "center",
14027
14273
  touchAction: "pan-x pan-y pinch-zoom"
14028
14274
  },
14029
- children: /* @__PURE__ */ jsx17(
14275
+ children: /* @__PURE__ */ jsx18(
14030
14276
  "div",
14031
14277
  {
14032
14278
  style: {