@tscircuit/pcb-viewer 1.11.267 → 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;
@@ -9499,7 +9512,7 @@ var orderedLayers = [
9499
9512
  var CanvasPrimitiveRenderer = ({
9500
9513
  primitives,
9501
9514
  transform,
9502
- grid: grid3,
9515
+ grid,
9503
9516
  width = 500,
9504
9517
  height = 500
9505
9518
  }) => {
@@ -10047,6 +10060,7 @@ var createBoxFromPoints = (points) => {
10047
10060
  }
10048
10061
  return { minX, maxX, minY, maxY };
10049
10062
  };
10063
+ var getBoundsFromPoints = createBoxFromPoints;
10050
10064
  var mergeBoundingBoxesInternal = (a, b) => ({
10051
10065
  minX: Math.min(a.minX, b.minX),
10052
10066
  maxX: Math.max(a.maxX, b.maxX),
@@ -10183,7 +10197,7 @@ function calculateDiagonalLabel(params) {
10183
10197
  } = params;
10184
10198
  const deltaX = dimensionEnd.x - dimensionStart.x;
10185
10199
  const deltaY = dimensionEnd.y - dimensionStart.y;
10186
- const distance5 = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
10200
+ const distance3 = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
10187
10201
  const screenDeltaX = screenDimensionEnd.x - screenDimensionStart.x;
10188
10202
  const screenDeltaY = screenDimensionEnd.y - screenDimensionStart.y;
10189
10203
  const screenDistance = Math.sqrt(
@@ -10225,11 +10239,11 @@ function calculateDiagonalLabel(params) {
10225
10239
  const x = midX + offsetX;
10226
10240
  const y = midY + offsetY;
10227
10241
  return {
10228
- distance: distance5,
10242
+ distance: distance3,
10229
10243
  screenDistance,
10230
10244
  x,
10231
10245
  y,
10232
- show: distance5 > 0.01 && screenDistance > 30 && isDiagonal
10246
+ show: distance3 > 0.01 && screenDistance > 30 && isDiagonal
10233
10247
  };
10234
10248
  }
10235
10249
 
@@ -10523,11 +10537,11 @@ var DimensionOverlay = ({
10523
10537
  for (const snap of snappingPointsWithScreen) {
10524
10538
  const dx = snap.screenPoint.x - screenPoint.x;
10525
10539
  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) {
10540
+ const distance3 = Math.hypot(dx, dy);
10541
+ if (distance3 > SNAP_THRESHOLD_PX) continue;
10542
+ if (!bestMatch || distance3 < bestMatch.distance) {
10529
10543
  bestMatch = {
10530
- distance: distance5,
10544
+ distance: distance3,
10531
10545
  id: snap.id,
10532
10546
  point: snap.point
10533
10547
  };
@@ -11126,10 +11140,10 @@ var isInsideOfSmtpad = (elm, point, padding = 0) => {
11126
11140
  };
11127
11141
  var isInsideOfPlatedHole = (hole, point, padding = 0) => {
11128
11142
  if (hole.shape === "circle") {
11129
- const distance5 = Math.sqrt(
11143
+ const distance3 = Math.sqrt(
11130
11144
  (point.x - hole.x) ** 2 + (point.y - hole.y) ** 2
11131
11145
  );
11132
- return distance5 <= hole.outer_diameter / 2 + padding;
11146
+ return distance3 <= hole.outer_diameter / 2 + padding;
11133
11147
  } else if (hole.shape === "circular_hole_with_rect_pad") {
11134
11148
  const dx = Math.abs(point.x - hole.x);
11135
11149
  const dy = Math.abs(point.y - hole.y);
@@ -11955,9 +11969,19 @@ var ErrorOverlay = ({
11955
11969
  ] });
11956
11970
  };
11957
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
+
11958
11982
  // src/components/MouseElementTracker.tsx
11959
11983
  import { useState as useState7, useMemo as useMemo5 } from "react";
11960
- import { applyToPoint as applyToPoint12, inverse as inverse5 } from "transformation-matrix";
11984
+ import { applyToPoint as applyToPoint13, inverse as inverse5 } from "transformation-matrix";
11961
11985
 
11962
11986
  // src/components/ElementOverlayBox.tsx
11963
11987
  import { useEffect as useEffect11, useState as useState6 } from "react";
@@ -12234,35 +12258,228 @@ var ElementOverlayBox = ({
12234
12258
  )) });
12235
12259
  };
12236
12260
 
12237
- // src/components/MouseElementTracker.tsx
12238
- import { distance as distance4 } from "circuit-json";
12261
+ // src/components/GroupAnchorOffsetOverlay/index.tsx
12262
+ import { applyToPoint as applyToPoint12 } from "transformation-matrix";
12239
12263
 
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
- }
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
+ };
12245
12278
 
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)
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
12255
12340
  };
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
- }
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
+ };
12263
12480
 
12264
12481
  // src/components/MouseElementTracker.tsx
12265
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
12482
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
12266
12483
  var getPolygonBoundingBox = (points) => {
12267
12484
  if (points.length === 0) return null;
12268
12485
  let minX = points[0].x;
@@ -12304,22 +12521,22 @@ var getPrimitivesUnderPoint = (primitives, rwPoint, transform) => {
12304
12521
  for (const primitive of primitives) {
12305
12522
  if (!primitive._element) continue;
12306
12523
  if ("x1" in primitive && primitive._element?.type === "pcb_trace") {
12307
- const distance5 = pointToSegmentDistance2(
12524
+ const distance3 = pointToSegmentDistance(
12308
12525
  { x: rwPoint.x, y: rwPoint.y },
12309
12526
  { x: primitive.x1, y: primitive.y1 },
12310
12527
  { x: primitive.x2, y: primitive.y2 }
12311
12528
  );
12312
12529
  const lineWidth = primitive.width || 0.5;
12313
12530
  const detectionThreshold = Math.max(lineWidth * 25, 2) / transform.a;
12314
- if (distance5 < detectionThreshold) {
12531
+ if (distance3 < detectionThreshold) {
12315
12532
  newMousedPrimitives.push(primitive);
12316
12533
  }
12317
12534
  continue;
12318
12535
  }
12319
12536
  if (primitive.pcb_drawing_type === "polygon") {
12320
12537
  const points = primitive.points.map((point) => ({
12321
- x: distance4.parse(point.x),
12322
- y: distance4.parse(point.y)
12538
+ x: distance2.parse(point.x),
12539
+ y: distance2.parse(point.y)
12323
12540
  }));
12324
12541
  const boundingBox = getPolygonBoundingBox(points);
12325
12542
  if (!boundingBox) continue;
@@ -12333,8 +12550,8 @@ var getPrimitivesUnderPoint = (primitives, rwPoint, transform) => {
12333
12550
  }
12334
12551
  if (primitive.pcb_drawing_type === "polygon_with_arcs") {
12335
12552
  const points = primitive.brep_shape.outer_ring.vertices.map((v) => ({
12336
- x: distance4.parse(v.x),
12337
- y: distance4.parse(v.y)
12553
+ x: distance2.parse(v.x),
12554
+ y: distance2.parse(v.y)
12338
12555
  }));
12339
12556
  const boundingBox = getPolygonBoundingBox(points);
12340
12557
  if (!boundingBox) continue;
@@ -12376,6 +12593,7 @@ var MouseElementTracker = ({
12376
12593
  }) => {
12377
12594
  const [mousedPrimitives, setMousedPrimitives] = useState7([]);
12378
12595
  const [mousePos, setMousePos] = useState7({ x: 0, y: 0 });
12596
+ const [containerRef, { width, height }] = useMeasure_default();
12379
12597
  const highlightedPrimitives = useMemo5(() => {
12380
12598
  const highlightedPrimitives2 = [];
12381
12599
  for (const primitive of mousedPrimitives) {
@@ -12407,7 +12625,7 @@ var MouseElementTracker = ({
12407
12625
  h = "h" in primitive ? primitive.h : "r" in primitive ? primitive.r * 2 : "rX" in primitive && "rY" in primitive ? primitive.rY * 2 : 0;
12408
12626
  }
12409
12627
  if (!basePoint) continue;
12410
- const screenPos = applyToPoint12(transform, basePoint);
12628
+ const screenPos = applyToPoint13(transform, basePoint);
12411
12629
  const screenSize = {
12412
12630
  w: w * transform.a,
12413
12631
  h: h * transform.a
@@ -12432,7 +12650,7 @@ var MouseElementTracker = ({
12432
12650
  }, [mousedPrimitives, transform]);
12433
12651
  const handleInteraction = (x, y, transform2, primitives2) => {
12434
12652
  setMousePos({ x, y });
12435
- const rwPoint = applyToPoint12(inverse5(transform2), { x, y });
12653
+ const rwPoint = applyToPoint13(inverse5(transform2), { x, y });
12436
12654
  const newMousedPrimitives = getPrimitivesUnderPoint(
12437
12655
  primitives2,
12438
12656
  rwPoint,
@@ -12447,10 +12665,11 @@ var MouseElementTracker = ({
12447
12665
  setMousedPrimitives(newMousedPrimitives);
12448
12666
  onMouseHoverOverPrimitives(newMousedPrimitives);
12449
12667
  };
12450
- return /* @__PURE__ */ jsxs9(
12668
+ return /* @__PURE__ */ jsxs10(
12451
12669
  "div",
12452
12670
  {
12453
- style: { position: "relative" },
12671
+ ref: containerRef,
12672
+ style: { position: "relative", width: "100%", height: "100%" },
12454
12673
  onMouseMove: (e) => {
12455
12674
  if (transform) {
12456
12675
  const rect = e.currentTarget.getBoundingClientRect();
@@ -12470,13 +12689,23 @@ var MouseElementTracker = ({
12470
12689
  },
12471
12690
  children: [
12472
12691
  children,
12473
- /* @__PURE__ */ jsx12(
12692
+ /* @__PURE__ */ jsx13(
12474
12693
  ElementOverlayBox,
12475
12694
  {
12476
12695
  elements,
12477
12696
  mousePos,
12478
12697
  highlightedPrimitives
12479
12698
  }
12699
+ ),
12700
+ transform && /* @__PURE__ */ jsx13(
12701
+ GroupAnchorOffsetOverlay,
12702
+ {
12703
+ elements,
12704
+ highlightedPrimitives,
12705
+ transform,
12706
+ containerWidth: width,
12707
+ containerHeight: height
12708
+ }
12480
12709
  )
12481
12710
  ]
12482
12711
  }
@@ -12484,10 +12713,10 @@ var MouseElementTracker = ({
12484
12713
  };
12485
12714
 
12486
12715
  // src/components/PcbGroupOverlay.tsx
12487
- import { applyToPoint as applyToPoint13 } from "transformation-matrix";
12716
+ import { applyToPoint as applyToPoint14 } from "transformation-matrix";
12488
12717
  import { identity as identity8 } from "transformation-matrix";
12489
12718
  import { useRef as useRef9, useEffect as useEffect12 } from "react";
12490
- import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
12719
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
12491
12720
  var GROUP_COLORS = [
12492
12721
  "rgb(255, 100, 100)",
12493
12722
  "rgb(100, 255, 100)",
@@ -12616,10 +12845,10 @@ var PcbGroupOverlay = ({
12616
12845
  maxX += totalPadding;
12617
12846
  minY -= totalPadding;
12618
12847
  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 });
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 });
12623
12852
  const groupColor = GROUP_COLORS[groupIndex % GROUP_COLORS.length];
12624
12853
  ctx.strokeStyle = groupColor;
12625
12854
  ctx.lineWidth = 2;
@@ -12689,7 +12918,7 @@ var PcbGroupOverlay = ({
12689
12918
  } else {
12690
12919
  edgePoint = { x: anchor.x, y: groupBottom };
12691
12920
  }
12692
- const anchorScreenPos = applyToPoint13(transform, edgePoint);
12921
+ const anchorScreenPos = applyToPoint14(transform, edgePoint);
12693
12922
  ctx.strokeStyle = "white";
12694
12923
  ctx.lineWidth = 1.5;
12695
12924
  ctx.setLineDash([]);
@@ -12712,14 +12941,14 @@ var PcbGroupOverlay = ({
12712
12941
  is_showing_pcb_groups,
12713
12942
  pcb_group_view_mode
12714
12943
  ]);
12715
- return /* @__PURE__ */ jsxs10(
12944
+ return /* @__PURE__ */ jsxs11(
12716
12945
  "div",
12717
12946
  {
12718
12947
  ref: containerRef,
12719
12948
  style: { position: "relative", width: "100%", height: "100%" },
12720
12949
  children: [
12721
12950
  children,
12722
- /* @__PURE__ */ jsx13(
12951
+ /* @__PURE__ */ jsx14(
12723
12952
  "canvas",
12724
12953
  {
12725
12954
  ref: canvasRef,
@@ -12739,9 +12968,9 @@ var PcbGroupOverlay = ({
12739
12968
  };
12740
12969
 
12741
12970
  // src/components/RatsNestOverlay.tsx
12742
- import { applyToPoint as applyToPoint14, identity as identity9 } from "transformation-matrix";
12971
+ import { applyToPoint as applyToPoint15, identity as identity9 } from "transformation-matrix";
12743
12972
  import { useMemo as useMemo6 } from "react";
12744
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
12973
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
12745
12974
  var RatsNestOverlay = ({ transform, soup, children }) => {
12746
12975
  const isShowingRatsNest = useGlobalStore((s) => s.is_showing_rats_nest);
12747
12976
  const { netMap, idToNetMap } = useMemo6(
@@ -12764,11 +12993,11 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12764
12993
  connectedIds.forEach((id) => {
12765
12994
  const pos = getElementPosition(id);
12766
12995
  if (pos) {
12767
- const distance5 = Math.sqrt(
12996
+ const distance3 = Math.sqrt(
12768
12997
  (sourcePoint.x - pos.x) ** 2 + (sourcePoint.y - pos.y) ** 2
12769
12998
  );
12770
- if (distance5 < minDistance && distance5 > 0) {
12771
- minDistance = distance5;
12999
+ if (distance3 < minDistance && distance3 > 0) {
13000
+ minDistance = distance3;
12772
13001
  nearestPoint = pos;
12773
13002
  }
12774
13003
  }
@@ -12804,9 +13033,9 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12804
13033
  }, [soup, netMap, idToNetMap, isShowingRatsNest]);
12805
13034
  if (!soup || !isShowingRatsNest) return children;
12806
13035
  if (!transform) transform = identity9();
12807
- return /* @__PURE__ */ jsxs11("div", { style: { position: "relative" }, children: [
13036
+ return /* @__PURE__ */ jsxs12("div", { style: { position: "relative" }, children: [
12808
13037
  children,
12809
- /* @__PURE__ */ jsx14(
13038
+ /* @__PURE__ */ jsx15(
12810
13039
  "svg",
12811
13040
  {
12812
13041
  style: {
@@ -12820,9 +13049,9 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12820
13049
  zIndex: zIndexMap.ratsNestOverlay
12821
13050
  },
12822
13051
  children: ratsNestLines.map(({ key, startPoint, endPoint, isInNet }) => {
12823
- const transformedStart = applyToPoint14(transform, startPoint);
12824
- const transformedEnd = applyToPoint14(transform, endPoint);
12825
- return /* @__PURE__ */ jsx14(
13052
+ const transformedStart = applyToPoint15(transform, startPoint);
13053
+ const transformedEnd = applyToPoint15(transform, endPoint);
13054
+ return /* @__PURE__ */ jsx15(
12826
13055
  "line",
12827
13056
  {
12828
13057
  x1: transformedStart.x,
@@ -12842,18 +13071,13 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
12842
13071
  };
12843
13072
 
12844
13073
  // 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";
13074
+ import { useEffect as useEffect15, useState as useState9, useCallback as useCallback4, useRef as useRef10 } from "react";
12851
13075
  import { css as css3 } from "@emotion/css";
12852
13076
 
12853
13077
  // package.json
12854
13078
  var package_default = {
12855
13079
  name: "@tscircuit/pcb-viewer",
12856
- version: "1.11.265",
13080
+ version: "1.11.266",
12857
13081
  main: "dist/index.js",
12858
13082
  type: "module",
12859
13083
  repository: "tscircuit/pcb-viewer",
@@ -12904,6 +13128,7 @@ var package_default = {
12904
13128
  dependencies: {
12905
13129
  "@emotion/css": "^11.11.2",
12906
13130
  "@tscircuit/alphabet": "^0.0.3",
13131
+ "@tscircuit/math-utils": "^0.0.29",
12907
13132
  "@vitejs/plugin-react": "^5.0.2",
12908
13133
  "circuit-json": "^0.0.321",
12909
13134
  "circuit-to-svg": "^0.0.271",
@@ -12958,9 +13183,9 @@ var useIsSmallScreen = () => {
12958
13183
  };
12959
13184
 
12960
13185
  // src/components/ToolbarOverlay.tsx
12961
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
13186
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
12962
13187
  var LayerButton = ({ name, selected, onClick }) => {
12963
- return /* @__PURE__ */ jsxs12(
13188
+ return /* @__PURE__ */ jsxs13(
12964
13189
  "div",
12965
13190
  {
12966
13191
  className: css3`
@@ -12976,8 +13201,8 @@ var LayerButton = ({ name, selected, onClick }) => {
12976
13201
  `,
12977
13202
  onClick,
12978
13203
  children: [
12979
- /* @__PURE__ */ jsx15("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
12980
- /* @__PURE__ */ jsx15(
13204
+ /* @__PURE__ */ jsx16("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
13205
+ /* @__PURE__ */ jsx16(
12981
13206
  "span",
12982
13207
  {
12983
13208
  style: {
@@ -12997,7 +13222,7 @@ var ToolbarButton = ({
12997
13222
  isSmallScreen,
12998
13223
  onClick,
12999
13224
  ...props
13000
- }) => /* @__PURE__ */ jsx15(
13225
+ }) => /* @__PURE__ */ jsx16(
13001
13226
  "div",
13002
13227
  {
13003
13228
  ...props,
@@ -13034,7 +13259,7 @@ var CheckboxMenuItem = ({
13034
13259
  checked,
13035
13260
  onClick
13036
13261
  }) => {
13037
- return /* @__PURE__ */ jsxs12(
13262
+ return /* @__PURE__ */ jsxs13(
13038
13263
  "div",
13039
13264
  {
13040
13265
  className: css3`
@@ -13061,15 +13286,15 @@ var CheckboxMenuItem = ({
13061
13286
  onClick();
13062
13287
  },
13063
13288
  children: [
13064
- /* @__PURE__ */ jsx15("input", { type: "checkbox", checked, onChange: () => {
13289
+ /* @__PURE__ */ jsx16("input", { type: "checkbox", checked, onChange: () => {
13065
13290
  }, readOnly: true }),
13066
- /* @__PURE__ */ jsx15("span", { style: { color: "#eee" }, children: label })
13291
+ /* @__PURE__ */ jsx16("span", { style: { color: "#eee" }, children: label })
13067
13292
  ]
13068
13293
  }
13069
13294
  );
13070
13295
  };
13071
13296
  var RadioMenuItem = ({ label, checked, onClick }) => {
13072
- return /* @__PURE__ */ jsxs12(
13297
+ return /* @__PURE__ */ jsxs13(
13073
13298
  "div",
13074
13299
  {
13075
13300
  className: css3`
@@ -13096,9 +13321,9 @@ var RadioMenuItem = ({ label, checked, onClick }) => {
13096
13321
  onClick();
13097
13322
  },
13098
13323
  children: [
13099
- /* @__PURE__ */ jsx15("input", { type: "radio", checked, onChange: () => {
13324
+ /* @__PURE__ */ jsx16("input", { type: "radio", checked, onChange: () => {
13100
13325
  }, readOnly: true }),
13101
- /* @__PURE__ */ jsx15("span", { style: { color: "#eee" }, children: label })
13326
+ /* @__PURE__ */ jsx16("span", { style: { color: "#eee" }, children: label })
13102
13327
  ]
13103
13328
  }
13104
13329
  );
@@ -13119,6 +13344,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13119
13344
  setIsShowingDrcErrors,
13120
13345
  setIsShowingCopperPours,
13121
13346
  setIsShowingPcbGroups,
13347
+ setIsShowingGroupAnchorOffsets,
13122
13348
  setPcbGroupViewMode,
13123
13349
  setHoveredErrorId
13124
13350
  } = useGlobalStore((s) => ({
@@ -13137,6 +13363,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13137
13363
  is_showing_drc_errors: s.is_showing_drc_errors,
13138
13364
  is_showing_copper_pours: s.is_showing_copper_pours,
13139
13365
  is_showing_pcb_groups: s.is_showing_pcb_groups,
13366
+ is_showing_group_anchor_offsets: s.is_showing_group_anchor_offsets,
13140
13367
  pcb_group_view_mode: s.pcb_group_view_mode
13141
13368
  },
13142
13369
  setEditMode: s.setEditMode,
@@ -13146,6 +13373,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13146
13373
  setIsShowingDrcErrors: s.setIsShowingDrcErrors,
13147
13374
  setIsShowingCopperPours: s.setIsShowingCopperPours,
13148
13375
  setIsShowingPcbGroups: s.setIsShowingPcbGroups,
13376
+ setIsShowingGroupAnchorOffsets: s.setIsShowingGroupAnchorOffsets,
13149
13377
  setPcbGroupViewMode: s.setPcbGroupViewMode,
13150
13378
  setHoveredErrorId: s.setHoveredErrorId
13151
13379
  }));
@@ -13244,7 +13472,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13244
13472
  setErrorsOpen(false);
13245
13473
  }
13246
13474
  }, [isViewMenuOpen]);
13247
- return /* @__PURE__ */ jsxs12(
13475
+ return /* @__PURE__ */ jsxs13(
13248
13476
  "div",
13249
13477
  {
13250
13478
  style: { position: "relative", zIndex: "999 !important" },
@@ -13252,7 +13480,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13252
13480
  onMouseLeave: handleMouseLeave,
13253
13481
  children: [
13254
13482
  children,
13255
- /* @__PURE__ */ jsxs12(
13483
+ /* @__PURE__ */ jsxs13(
13256
13484
  "div",
13257
13485
  {
13258
13486
  style: {
@@ -13273,7 +13501,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13273
13501
  ]
13274
13502
  }
13275
13503
  ),
13276
- /* @__PURE__ */ jsxs12(
13504
+ /* @__PURE__ */ jsxs13(
13277
13505
  "div",
13278
13506
  {
13279
13507
  "data-toolbar-overlay": true,
@@ -13296,7 +13524,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13296
13524
  fontFamily: "sans-serif"
13297
13525
  },
13298
13526
  children: [
13299
- /* @__PURE__ */ jsxs12(
13527
+ /* @__PURE__ */ jsxs13(
13300
13528
  ToolbarButton,
13301
13529
  {
13302
13530
  isSmallScreen,
@@ -13307,10 +13535,10 @@ var ToolbarOverlay = ({ children, elements }) => {
13307
13535
  }
13308
13536
  },
13309
13537
  children: [
13310
- /* @__PURE__ */ jsxs12("div", { children: [
13538
+ /* @__PURE__ */ jsxs13("div", { children: [
13311
13539
  "layer:",
13312
13540
  " ",
13313
- /* @__PURE__ */ jsx15(
13541
+ /* @__PURE__ */ jsx16(
13314
13542
  "span",
13315
13543
  {
13316
13544
  style: {
@@ -13322,7 +13550,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13322
13550
  }
13323
13551
  )
13324
13552
  ] }),
13325
- 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(
13326
13554
  LayerButton,
13327
13555
  {
13328
13556
  name: layer,
@@ -13336,7 +13564,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13336
13564
  ]
13337
13565
  }
13338
13566
  ),
13339
- /* @__PURE__ */ jsx15(
13567
+ /* @__PURE__ */ jsx16(
13340
13568
  ToolbarButton,
13341
13569
  {
13342
13570
  isSmallScreen,
@@ -13345,13 +13573,13 @@ var ToolbarOverlay = ({ children, elements }) => {
13345
13573
  ...errorCount > 0 ? { color: "red" } : {}
13346
13574
  },
13347
13575
  onClick: handleErrorsToggle,
13348
- children: /* @__PURE__ */ jsxs12("div", { children: [
13576
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13349
13577
  errorCount,
13350
13578
  " errors"
13351
13579
  ] })
13352
13580
  }
13353
13581
  ),
13354
- isErrorsOpen && errorCount > 0 && /* @__PURE__ */ jsx15(
13582
+ isErrorsOpen && errorCount > 0 && /* @__PURE__ */ jsx16(
13355
13583
  "div",
13356
13584
  {
13357
13585
  style: {
@@ -13369,14 +13597,14 @@ var ToolbarOverlay = ({ children, elements }) => {
13369
13597
  },
13370
13598
  children: errorElements.map((e, i) => {
13371
13599
  const errorId = e.pcb_trace_error_id || `error_${i}_${e.error_type}_${e.message?.slice(0, 20)}`;
13372
- return /* @__PURE__ */ jsxs12(
13600
+ return /* @__PURE__ */ jsxs13(
13373
13601
  "div",
13374
13602
  {
13375
13603
  style: {
13376
13604
  borderBottom: i < errorElements.length - 1 ? "1px solid #444" : "none"
13377
13605
  },
13378
13606
  children: [
13379
- /* @__PURE__ */ jsxs12(
13607
+ /* @__PURE__ */ jsxs13(
13380
13608
  "div",
13381
13609
  {
13382
13610
  style: {
@@ -13427,7 +13655,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13427
13655
  }
13428
13656
  },
13429
13657
  children: [
13430
- /* @__PURE__ */ jsx15(
13658
+ /* @__PURE__ */ jsx16(
13431
13659
  "div",
13432
13660
  {
13433
13661
  style: {
@@ -13441,7 +13669,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13441
13669
  children: e.error_type
13442
13670
  }
13443
13671
  ),
13444
- /* @__PURE__ */ jsx15(
13672
+ /* @__PURE__ */ jsx16(
13445
13673
  "div",
13446
13674
  {
13447
13675
  style: {
@@ -13456,7 +13684,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13456
13684
  children: e.message
13457
13685
  }
13458
13686
  ),
13459
- /* @__PURE__ */ jsx15(
13687
+ /* @__PURE__ */ jsx16(
13460
13688
  "div",
13461
13689
  {
13462
13690
  ref: (el) => {
@@ -13476,7 +13704,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13476
13704
  ]
13477
13705
  }
13478
13706
  ),
13479
- /* @__PURE__ */ jsx15(
13707
+ /* @__PURE__ */ jsx16(
13480
13708
  "div",
13481
13709
  {
13482
13710
  ref: (el) => {
@@ -13489,7 +13717,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13489
13717
  backgroundColor: "#1a1a1a",
13490
13718
  borderTop: "1px solid #444"
13491
13719
  },
13492
- children: /* @__PURE__ */ jsx15(
13720
+ children: /* @__PURE__ */ jsx16(
13493
13721
  "div",
13494
13722
  {
13495
13723
  style: {
@@ -13512,58 +13740,58 @@ var ToolbarOverlay = ({ children, elements }) => {
13512
13740
  })
13513
13741
  }
13514
13742
  ),
13515
- /* @__PURE__ */ jsx15(
13743
+ /* @__PURE__ */ jsx16(
13516
13744
  ToolbarButton,
13517
13745
  {
13518
13746
  isSmallScreen,
13519
13747
  style: {},
13520
13748
  onClick: handleEditTraceToggle,
13521
- children: /* @__PURE__ */ jsxs12("div", { children: [
13749
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13522
13750
  editModes.in_draw_trace_mode ? "\u2716 " : "",
13523
13751
  "Edit Traces"
13524
13752
  ] })
13525
13753
  }
13526
13754
  ),
13527
- /* @__PURE__ */ jsx15(
13755
+ /* @__PURE__ */ jsx16(
13528
13756
  ToolbarButton,
13529
13757
  {
13530
13758
  isSmallScreen,
13531
13759
  style: {},
13532
13760
  onClick: handleMoveComponentToggle,
13533
- children: /* @__PURE__ */ jsxs12("div", { children: [
13761
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13534
13762
  editModes.in_move_footprint_mode ? "\u2716 " : "",
13535
13763
  "Move Components"
13536
13764
  ] })
13537
13765
  }
13538
13766
  ),
13539
- /* @__PURE__ */ jsx15(
13767
+ /* @__PURE__ */ jsx16(
13540
13768
  ToolbarButton,
13541
13769
  {
13542
13770
  isSmallScreen,
13543
13771
  style: {},
13544
13772
  onClick: handleRatsNestToggle,
13545
- children: /* @__PURE__ */ jsxs12("div", { children: [
13773
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13546
13774
  viewSettings.is_showing_rats_nest ? "\u2716 " : "",
13547
13775
  "Rats Nest"
13548
13776
  ] })
13549
13777
  }
13550
13778
  ),
13551
- /* @__PURE__ */ jsx15(
13779
+ /* @__PURE__ */ jsx16(
13552
13780
  ToolbarButton,
13553
13781
  {
13554
13782
  isSmallScreen,
13555
13783
  style: measureToolArmed ? { backgroundColor: "#444" } : {},
13556
13784
  onClick: handleMeasureToolClick,
13557
- children: /* @__PURE__ */ jsx15("div", { children: "\u{1F4CF}" })
13785
+ children: /* @__PURE__ */ jsx16("div", { children: "\u{1F4CF}" })
13558
13786
  }
13559
13787
  ),
13560
- /* @__PURE__ */ jsx15(
13788
+ /* @__PURE__ */ jsx16(
13561
13789
  ToolbarButton,
13562
13790
  {
13563
13791
  isSmallScreen,
13564
13792
  onClick: handleViewMenuToggle,
13565
- children: /* @__PURE__ */ jsxs12("div", { children: [
13566
- /* @__PURE__ */ jsxs12(
13793
+ children: /* @__PURE__ */ jsxs13("div", { children: [
13794
+ /* @__PURE__ */ jsxs13(
13567
13795
  "div",
13568
13796
  {
13569
13797
  style: {
@@ -13573,7 +13801,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13573
13801
  },
13574
13802
  children: [
13575
13803
  "View",
13576
- /* @__PURE__ */ jsx15(
13804
+ /* @__PURE__ */ jsx16(
13577
13805
  "span",
13578
13806
  {
13579
13807
  style: {
@@ -13588,8 +13816,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13588
13816
  ]
13589
13817
  }
13590
13818
  ),
13591
- isViewMenuOpen && /* @__PURE__ */ jsxs12("div", { style: { marginTop: 4, minWidth: 120 }, children: [
13592
- /* @__PURE__ */ jsx15(
13819
+ isViewMenuOpen && /* @__PURE__ */ jsxs13("div", { style: { marginTop: 4, minWidth: 120 }, children: [
13820
+ /* @__PURE__ */ jsx16(
13593
13821
  CheckboxMenuItem,
13594
13822
  {
13595
13823
  label: "Show All Trace Lengths",
@@ -13601,7 +13829,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13601
13829
  }
13602
13830
  }
13603
13831
  ),
13604
- /* @__PURE__ */ jsx15(
13832
+ /* @__PURE__ */ jsx16(
13605
13833
  CheckboxMenuItem,
13606
13834
  {
13607
13835
  label: "Show Autorouting Animation",
@@ -13613,7 +13841,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13613
13841
  }
13614
13842
  }
13615
13843
  ),
13616
- /* @__PURE__ */ jsx15(
13844
+ /* @__PURE__ */ jsx16(
13617
13845
  CheckboxMenuItem,
13618
13846
  {
13619
13847
  label: "Show DRC Errors",
@@ -13623,7 +13851,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13623
13851
  }
13624
13852
  }
13625
13853
  ),
13626
- /* @__PURE__ */ jsx15(
13854
+ /* @__PURE__ */ jsx16(
13627
13855
  CheckboxMenuItem,
13628
13856
  {
13629
13857
  label: "Show Copper Pours",
@@ -13635,7 +13863,19 @@ var ToolbarOverlay = ({ children, elements }) => {
13635
13863
  }
13636
13864
  }
13637
13865
  ),
13638
- /* @__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(
13639
13879
  CheckboxMenuItem,
13640
13880
  {
13641
13881
  label: "Show PCB Groups",
@@ -13645,8 +13885,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13645
13885
  }
13646
13886
  }
13647
13887
  ),
13648
- viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs12("div", { style: { marginLeft: 16 }, children: [
13649
- /* @__PURE__ */ jsx15(
13888
+ viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs13("div", { style: { marginLeft: 16 }, children: [
13889
+ /* @__PURE__ */ jsx16(
13650
13890
  RadioMenuItem,
13651
13891
  {
13652
13892
  label: "Show All Groups",
@@ -13656,7 +13896,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13656
13896
  }
13657
13897
  }
13658
13898
  ),
13659
- /* @__PURE__ */ jsx15(
13899
+ /* @__PURE__ */ jsx16(
13660
13900
  RadioMenuItem,
13661
13901
  {
13662
13902
  label: "Show Named Groups",
@@ -13680,7 +13920,7 @@ var ToolbarOverlay = ({ children, elements }) => {
13680
13920
  };
13681
13921
 
13682
13922
  // src/components/CanvasElementsRenderer.tsx
13683
- import { jsx as jsx16 } from "react/jsx-runtime";
13923
+ import { jsx as jsx17 } from "react/jsx-runtime";
13684
13924
  var CanvasElementsRenderer = (props) => {
13685
13925
  const { transform, elements } = props;
13686
13926
  const hoveredErrorId = useGlobalStore((state) => state.hovered_error_id);
@@ -13757,14 +13997,14 @@ var CanvasElementsRenderer = (props) => {
13757
13997
  },
13758
13998
  [connectivityMap]
13759
13999
  );
13760
- return /* @__PURE__ */ jsx16(
14000
+ return /* @__PURE__ */ jsx17(
13761
14001
  MouseElementTracker,
13762
14002
  {
13763
14003
  elements: elementsToRender,
13764
14004
  transform,
13765
14005
  primitives: primitivesWithoutInteractionMetadata,
13766
14006
  onMouseHoverOverPrimitives: onMouseOverPrimitives,
13767
- children: /* @__PURE__ */ jsx16(
14007
+ children: /* @__PURE__ */ jsx17(
13768
14008
  EditPlacementOverlay,
13769
14009
  {
13770
14010
  disabled: !props.allowEditing,
@@ -13773,7 +14013,7 @@ var CanvasElementsRenderer = (props) => {
13773
14013
  cancelPanDrag: props.cancelPanDrag,
13774
14014
  onCreateEditEvent: props.onCreateEditEvent,
13775
14015
  onModifyEditEvent: props.onModifyEditEvent,
13776
- children: /* @__PURE__ */ jsx16(
14016
+ children: /* @__PURE__ */ jsx17(
13777
14017
  EditTraceHintOverlay,
13778
14018
  {
13779
14019
  disabled: !props.allowEditing,
@@ -13782,23 +14022,23 @@ var CanvasElementsRenderer = (props) => {
13782
14022
  cancelPanDrag: props.cancelPanDrag,
13783
14023
  onCreateEditEvent: props.onCreateEditEvent,
13784
14024
  onModifyEditEvent: props.onModifyEditEvent,
13785
- children: /* @__PURE__ */ jsx16(
14025
+ children: /* @__PURE__ */ jsx17(
13786
14026
  DimensionOverlay,
13787
14027
  {
13788
14028
  transform,
13789
14029
  focusOnHover: props.focusOnHover,
13790
14030
  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(
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(
13792
14032
  DebugGraphicsOverlay,
13793
14033
  {
13794
14034
  transform,
13795
14035
  debugGraphics: props.debugGraphics,
13796
- children: /* @__PURE__ */ jsx16(
14036
+ children: /* @__PURE__ */ jsx17(
13797
14037
  WarningGraphicsOverlay,
13798
14038
  {
13799
14039
  transform,
13800
14040
  elements,
13801
- children: /* @__PURE__ */ jsx16(
14041
+ children: /* @__PURE__ */ jsx17(
13802
14042
  CanvasPrimitiveRenderer,
13803
14043
  {
13804
14044
  transform,
@@ -13865,7 +14105,7 @@ var calculateCircuitJsonKey = (circuitJson) => {
13865
14105
  };
13866
14106
 
13867
14107
  // src/PCBViewer.tsx
13868
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
14108
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
13869
14109
  var defaultTransform = compose7(translate11(400, 300), scale5(40, -40));
13870
14110
  var PCBViewer = ({
13871
14111
  circuitJson,
@@ -13959,14 +14199,14 @@ var PCBViewer = ({
13959
14199
  }),
13960
14200
  [initialState, disablePcbGroups]
13961
14201
  );
13962
- return /* @__PURE__ */ jsxs13("div", { ref: transformRef, style: { position: "relative" }, children: [
13963
- /* @__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(
13964
14204
  ContextProviders,
13965
14205
  {
13966
14206
  initialState: mergedInitialState,
13967
14207
  disablePcbGroups,
13968
14208
  children: [
13969
- /* @__PURE__ */ jsx17(
14209
+ /* @__PURE__ */ jsx18(
13970
14210
  CanvasElementsRenderer,
13971
14211
  {
13972
14212
  transform,
@@ -13991,11 +14231,11 @@ var PCBViewer = ({
13991
14231
  },
13992
14232
  refDimensions.width
13993
14233
  ),
13994
- /* @__PURE__ */ jsx17(ToastContainer, {})
14234
+ /* @__PURE__ */ jsx18(ToastContainer, {})
13995
14235
  ]
13996
14236
  }
13997
14237
  ) }),
13998
- clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx17(
14238
+ clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx18(
13999
14239
  "div",
14000
14240
  {
14001
14241
  onClick: () => {
@@ -14032,7 +14272,7 @@ var PCBViewer = ({
14032
14272
  justifyContent: "center",
14033
14273
  touchAction: "pan-x pan-y pinch-zoom"
14034
14274
  },
14035
- children: /* @__PURE__ */ jsx17(
14275
+ children: /* @__PURE__ */ jsx18(
14036
14276
  "div",
14037
14277
  {
14038
14278
  style: {