@tscircuit/pcb-viewer 1.11.238 → 1.11.240

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
@@ -6412,7 +6412,7 @@ var ToastContainer = () => {
6412
6412
  };
6413
6413
 
6414
6414
  // src/PCBViewer.tsx
6415
- import { useEffect as useEffect16, useMemo as useMemo6, useRef as useRef11, useState as useState11 } from "react";
6415
+ import { useEffect as useEffect16, useMemo as useMemo7, useRef as useRef11, useState as useState11 } from "react";
6416
6416
 
6417
6417
  // node_modules/react-use/esm/misc/util.js
6418
6418
  var noop = function() {
@@ -6963,7 +6963,7 @@ function addInteractionMetadataToPrimitives({
6963
6963
  }
6964
6964
 
6965
6965
  // src/components/CanvasElementsRenderer.tsx
6966
- import { useCallback as useCallback4, useMemo as useMemo5, useState as useState10 } from "react";
6966
+ import { useCallback as useCallback5, useMemo as useMemo6, useState as useState10 } from "react";
6967
6967
 
6968
6968
  // src/lib/util/expand-stroke.ts
6969
6969
  function getExpandedStroke(strokeInput, defaultWidth) {
@@ -7062,6 +7062,59 @@ var convertElementToPrimitives = (element, allElements) => {
7062
7062
  (e) => e.type === "source_port" && e.source_port_id === _source_port_id
7063
7063
  ) : void 0;
7064
7064
  switch (element.type) {
7065
+ case "pcb_panel": {
7066
+ const { width, height } = element;
7067
+ return [
7068
+ {
7069
+ _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7070
+ pcb_drawing_type: "line",
7071
+ x1: 0,
7072
+ y1: 0,
7073
+ x2: width,
7074
+ y2: 0,
7075
+ width: 1,
7076
+ zoomIndependent: true,
7077
+ layer: "board",
7078
+ _element: element
7079
+ },
7080
+ {
7081
+ _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7082
+ pcb_drawing_type: "line",
7083
+ x1: 0,
7084
+ y1: height,
7085
+ x2: width,
7086
+ y2: height,
7087
+ width: 1,
7088
+ zoomIndependent: true,
7089
+ layer: "board",
7090
+ _element: element
7091
+ },
7092
+ {
7093
+ _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7094
+ pcb_drawing_type: "line",
7095
+ x1: 0,
7096
+ y1: 0,
7097
+ x2: 0,
7098
+ y2: height,
7099
+ width: 1,
7100
+ zoomIndependent: true,
7101
+ layer: "board",
7102
+ _element: element
7103
+ },
7104
+ {
7105
+ _pcb_drawing_object_id: `line_${globalPcbDrawingObjectCount++}`,
7106
+ pcb_drawing_type: "line",
7107
+ x1: width,
7108
+ y1: 0,
7109
+ x2: width,
7110
+ y2: height,
7111
+ width: 1,
7112
+ zoomIndependent: true,
7113
+ layer: "board",
7114
+ _element: element
7115
+ }
7116
+ ];
7117
+ }
7065
7118
  case "pcb_board": {
7066
7119
  const { width, height, center, outline } = element;
7067
7120
  if (outline && outline.length > 2) {
@@ -8017,7 +8070,7 @@ var convertElementToPrimitives = (element, allElements) => {
8017
8070
  y: noteTextElement.anchor_position.y,
8018
8071
  layer: "notes",
8019
8072
  align: noteTextElement.anchor_alignment ?? "center",
8020
- text: noteTextElement.text,
8073
+ text: noteTextElement.text || "",
8021
8074
  size: noteTextElement.font_size,
8022
8075
  color: noteTextElement.color,
8023
8076
  _element: element,
@@ -9702,43 +9755,306 @@ var WarningGraphicsOverlay = ({
9702
9755
  };
9703
9756
 
9704
9757
  // src/components/DimensionOverlay.tsx
9705
- import { useEffect as useEffect7, useRef as useRef5, useState as useState3 } from "react";
9758
+ import { useCallback as useCallback3, useEffect as useEffect7, useMemo as useMemo3, useRef as useRef5, useState as useState3 } from "react";
9706
9759
  import { applyToPoint as applyToPoint8, identity as identity4, inverse as inverse2 } from "transformation-matrix";
9760
+
9761
+ // src/lib/util/get-primitive-bounding-box.ts
9762
+ var mergeNumber = (a, b, fn) => fn(a, b);
9763
+ var rotatePoint = (x, y, centerX, centerY, rotationDeg) => {
9764
+ const radians = rotationDeg * Math.PI / 180;
9765
+ const cos = Math.cos(radians);
9766
+ const sin = Math.sin(radians);
9767
+ const translatedX = x - centerX;
9768
+ const translatedY = y - centerY;
9769
+ const rotatedX = translatedX * cos - translatedY * sin;
9770
+ const rotatedY = translatedX * sin + translatedY * cos;
9771
+ return {
9772
+ x: rotatedX + centerX,
9773
+ y: rotatedY + centerY
9774
+ };
9775
+ };
9776
+ var createBoxFromPoints = (points) => {
9777
+ if (points.length === 0) return null;
9778
+ let minX = points[0].x;
9779
+ let maxX = points[0].x;
9780
+ let minY = points[0].y;
9781
+ let maxY = points[0].y;
9782
+ for (const point of points) {
9783
+ minX = mergeNumber(minX, point.x, Math.min);
9784
+ maxX = mergeNumber(maxX, point.x, Math.max);
9785
+ minY = mergeNumber(minY, point.y, Math.min);
9786
+ maxY = mergeNumber(maxY, point.y, Math.max);
9787
+ }
9788
+ return { minX, maxX, minY, maxY };
9789
+ };
9790
+ var mergeBoundingBoxesInternal = (a, b) => ({
9791
+ minX: Math.min(a.minX, b.minX),
9792
+ maxX: Math.max(a.maxX, b.maxX),
9793
+ minY: Math.min(a.minY, b.minY),
9794
+ maxY: Math.max(a.maxY, b.maxY)
9795
+ });
9796
+ var mergeBoundingBoxes = (existing, next) => {
9797
+ if (!existing) return next;
9798
+ return mergeBoundingBoxesInternal(existing, next);
9799
+ };
9800
+ var getPrimitiveBoundingBox = (primitive) => {
9801
+ switch (primitive.pcb_drawing_type) {
9802
+ case "line": {
9803
+ const halfWidth = (primitive.width ?? 0) / 2;
9804
+ const points = [
9805
+ { x: primitive.x1, y: primitive.y1 },
9806
+ { x: primitive.x2, y: primitive.y2 }
9807
+ ];
9808
+ const baseBox = createBoxFromPoints(points);
9809
+ if (!baseBox) return null;
9810
+ return {
9811
+ minX: baseBox.minX - halfWidth,
9812
+ maxX: baseBox.maxX + halfWidth,
9813
+ minY: baseBox.minY - halfWidth,
9814
+ maxY: baseBox.maxY + halfWidth
9815
+ };
9816
+ }
9817
+ case "rect": {
9818
+ const halfW = primitive.w / 2;
9819
+ const halfH = primitive.h / 2;
9820
+ const corners = [
9821
+ { x: primitive.x - halfW, y: primitive.y - halfH },
9822
+ { x: primitive.x + halfW, y: primitive.y - halfH },
9823
+ { x: primitive.x + halfW, y: primitive.y + halfH },
9824
+ { x: primitive.x - halfW, y: primitive.y + halfH }
9825
+ ];
9826
+ const rotation = primitive.ccw_rotation ?? 0;
9827
+ const rotatedCorners = rotation === 0 ? corners : corners.map(
9828
+ (corner) => rotatePoint(
9829
+ corner.x,
9830
+ corner.y,
9831
+ primitive.x,
9832
+ primitive.y,
9833
+ rotation
9834
+ )
9835
+ );
9836
+ return createBoxFromPoints(rotatedCorners);
9837
+ }
9838
+ case "circle": {
9839
+ return {
9840
+ minX: primitive.x - primitive.r,
9841
+ maxX: primitive.x + primitive.r,
9842
+ minY: primitive.y - primitive.r,
9843
+ maxY: primitive.y + primitive.r
9844
+ };
9845
+ }
9846
+ case "oval": {
9847
+ return {
9848
+ minX: primitive.x - primitive.rX,
9849
+ maxX: primitive.x + primitive.rX,
9850
+ minY: primitive.y - primitive.rY,
9851
+ maxY: primitive.y + primitive.rY
9852
+ };
9853
+ }
9854
+ case "pill": {
9855
+ const halfW = primitive.w / 2;
9856
+ const halfH = primitive.h / 2;
9857
+ const corners = [
9858
+ { x: primitive.x - halfW, y: primitive.y - halfH },
9859
+ { x: primitive.x + halfW, y: primitive.y - halfH },
9860
+ { x: primitive.x + halfW, y: primitive.y + halfH },
9861
+ { x: primitive.x - halfW, y: primitive.y + halfH }
9862
+ ];
9863
+ const rotation = primitive.ccw_rotation ?? 0;
9864
+ const rotatedCorners = rotation === 0 ? corners : corners.map(
9865
+ (corner) => rotatePoint(
9866
+ corner.x,
9867
+ corner.y,
9868
+ primitive.x,
9869
+ primitive.y,
9870
+ rotation
9871
+ )
9872
+ );
9873
+ return createBoxFromPoints(rotatedCorners);
9874
+ }
9875
+ case "polygon": {
9876
+ return createBoxFromPoints(primitive.points);
9877
+ }
9878
+ case "polygon_with_arcs": {
9879
+ const points = primitive.brep_shape.outer_ring.vertices.map((vertex) => ({
9880
+ x: vertex.x,
9881
+ y: vertex.y
9882
+ }));
9883
+ return createBoxFromPoints(points);
9884
+ }
9885
+ case "text": {
9886
+ const size = primitive.size ?? 0;
9887
+ const width = primitive.text ? primitive.text.length * size * 0.6 : 0;
9888
+ const height = size;
9889
+ if (width === 0 && height === 0) {
9890
+ return {
9891
+ minX: primitive.x,
9892
+ maxX: primitive.x,
9893
+ minY: primitive.y,
9894
+ maxY: primitive.y
9895
+ };
9896
+ }
9897
+ const halfW = width / 2;
9898
+ const halfH = height / 2;
9899
+ return {
9900
+ minX: primitive.x - halfW,
9901
+ maxX: primitive.x + halfW,
9902
+ minY: primitive.y - halfH,
9903
+ maxY: primitive.y + halfH
9904
+ };
9905
+ }
9906
+ default:
9907
+ return null;
9908
+ }
9909
+ };
9910
+
9911
+ // src/components/DimensionOverlay.tsx
9707
9912
  import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
9913
+ var SNAP_THRESHOLD_PX = 16;
9914
+ var SNAP_MARKER_SIZE = 5;
9915
+ var shouldExcludePrimitiveFromSnapping = (primitive) => {
9916
+ if (primitive.pcb_drawing_type === "text") return true;
9917
+ const element = primitive._element;
9918
+ if (!element || typeof element !== "object") {
9919
+ return false;
9920
+ }
9921
+ const elementType = typeof element.type === "string" ? element.type : void 0;
9922
+ if (!elementType) return false;
9923
+ if (elementType.startsWith("pcb_silkscreen_")) return true;
9924
+ if (elementType.startsWith("pcb_note_")) return true;
9925
+ if (elementType === "pcb_text") return true;
9926
+ if (elementType.startsWith("pcb_fabrication_note_") && elementType !== "pcb_fabrication_note_rect") {
9927
+ return true;
9928
+ }
9929
+ return false;
9930
+ };
9708
9931
  var DimensionOverlay = ({
9709
9932
  children,
9710
9933
  transform,
9711
- focusOnHover = false
9934
+ focusOnHover = false,
9935
+ primitives = []
9712
9936
  }) => {
9713
9937
  if (!transform) transform = identity4();
9714
9938
  const [dimensionToolVisible, setDimensionToolVisible] = useState3(false);
9715
9939
  const [dimensionToolStretching, setDimensionToolStretching] = useState3(false);
9716
9940
  const [measureToolArmed, setMeasureToolArmed] = useState3(false);
9717
- const disarmMeasure = () => {
9941
+ const [activeSnapIds, setActiveSnapIds] = useState3({
9942
+ start: null,
9943
+ end: null
9944
+ });
9945
+ const disarmMeasure = useCallback3(() => {
9718
9946
  if (measureToolArmed) {
9719
9947
  setMeasureToolArmed(false);
9720
9948
  window.dispatchEvent(new Event("disarm-dimension-tool"));
9721
9949
  }
9722
- };
9950
+ }, [measureToolArmed]);
9723
9951
  const [dStart, setDStart] = useState3({ x: 0, y: 0 });
9724
9952
  const [dEnd, setDEnd] = useState3({ x: 0, y: 0 });
9725
9953
  const mousePosRef = useRef5({ x: 0, y: 0 });
9726
9954
  const containerRef = useRef5(null);
9727
9955
  const container = containerRef.current;
9728
9956
  const containerBounds = container?.getBoundingClientRect();
9957
+ const elementBoundingBoxes = useMemo3(() => {
9958
+ const boundingBoxes = /* @__PURE__ */ new Map();
9959
+ for (const primitive of primitives) {
9960
+ if (!primitive._element) continue;
9961
+ if (shouldExcludePrimitiveFromSnapping(primitive)) continue;
9962
+ const bbox = getPrimitiveBoundingBox(primitive);
9963
+ if (!bbox) continue;
9964
+ const existing = boundingBoxes.get(primitive._element);
9965
+ boundingBoxes.set(
9966
+ primitive._element,
9967
+ mergeBoundingBoxes(existing ?? void 0, bbox)
9968
+ );
9969
+ }
9970
+ return boundingBoxes;
9971
+ }, [primitives]);
9972
+ const snappingPoints = useMemo3(() => {
9973
+ const points = [];
9974
+ elementBoundingBoxes.forEach((bounds, element) => {
9975
+ if (!bounds) return;
9976
+ const centerX = (bounds.minX + bounds.maxX) / 2;
9977
+ const centerY = (bounds.minY + bounds.maxY) / 2;
9978
+ const anchorPoints = {
9979
+ top_left: { x: bounds.minX, y: bounds.minY },
9980
+ top_center: { x: centerX, y: bounds.minY },
9981
+ top_right: { x: bounds.maxX, y: bounds.minY },
9982
+ center_left: { x: bounds.minX, y: centerY },
9983
+ center: { x: centerX, y: centerY },
9984
+ center_right: { x: bounds.maxX, y: centerY },
9985
+ bottom_left: { x: bounds.minX, y: bounds.maxY },
9986
+ bottom_center: { x: centerX, y: bounds.maxY },
9987
+ bottom_right: { x: bounds.maxX, y: bounds.maxY }
9988
+ };
9989
+ for (const [anchor, point] of Object.entries(anchorPoints)) {
9990
+ points.push({
9991
+ anchor,
9992
+ point,
9993
+ element
9994
+ });
9995
+ }
9996
+ });
9997
+ return points;
9998
+ }, [elementBoundingBoxes]);
9999
+ const snappingPointsWithScreen = useMemo3(() => {
10000
+ return snappingPoints.map((snap, index) => ({
10001
+ ...snap,
10002
+ id: `${index}-${snap.anchor}`,
10003
+ screenPoint: applyToPoint8(transform, snap.point)
10004
+ }));
10005
+ }, [snappingPoints, transform]);
10006
+ const findSnap = useCallback3(
10007
+ (rwPoint) => {
10008
+ if (snappingPointsWithScreen.length === 0) {
10009
+ return { point: rwPoint, id: null };
10010
+ }
10011
+ const screenPoint = applyToPoint8(transform, rwPoint);
10012
+ let bestMatch = null;
10013
+ for (const snap of snappingPointsWithScreen) {
10014
+ const dx = snap.screenPoint.x - screenPoint.x;
10015
+ const dy = snap.screenPoint.y - screenPoint.y;
10016
+ const distance5 = Math.hypot(dx, dy);
10017
+ if (distance5 > SNAP_THRESHOLD_PX) continue;
10018
+ if (!bestMatch || distance5 < bestMatch.distance) {
10019
+ bestMatch = {
10020
+ distance: distance5,
10021
+ id: snap.id,
10022
+ point: snap.point
10023
+ };
10024
+ }
10025
+ }
10026
+ if (!bestMatch) {
10027
+ return { point: rwPoint, id: null };
10028
+ }
10029
+ return { point: bestMatch.point, id: bestMatch.id };
10030
+ },
10031
+ [snappingPointsWithScreen, transform]
10032
+ );
9729
10033
  useEffect7(() => {
9730
10034
  const container2 = containerRef.current;
9731
10035
  const down = (e) => {
9732
10036
  if (e.key === "d") {
9733
- setDStart({ x: mousePosRef.current.x, y: mousePosRef.current.y });
9734
- setDEnd({ x: mousePosRef.current.x, y: mousePosRef.current.y });
9735
- setDimensionToolVisible((visible) => !visible);
9736
- setDimensionToolStretching(true);
10037
+ const snap = findSnap({
10038
+ x: mousePosRef.current.x,
10039
+ y: mousePosRef.current.y
10040
+ });
10041
+ setDStart({ x: snap.point.x, y: snap.point.y });
10042
+ setDEnd({ x: snap.point.x, y: snap.point.y });
10043
+ setActiveSnapIds({ start: snap.id, end: snap.id });
10044
+ if (dimensionToolVisible) {
10045
+ setDimensionToolVisible(false);
10046
+ setDimensionToolStretching(false);
10047
+ setActiveSnapIds({ start: null, end: null });
10048
+ } else {
10049
+ setDimensionToolVisible(true);
10050
+ setDimensionToolStretching(true);
10051
+ }
9737
10052
  disarmMeasure();
9738
10053
  }
9739
10054
  if (e.key === "Escape") {
9740
10055
  setDimensionToolVisible(false);
9741
10056
  setDimensionToolStretching(false);
10057
+ setActiveSnapIds({ start: null, end: null });
9742
10058
  disarmMeasure();
9743
10059
  }
9744
10060
  };
@@ -9772,7 +10088,7 @@ var DimensionOverlay = ({
9772
10088
  container2.removeEventListener("mouseleave", removeKeyListener);
9773
10089
  }
9774
10090
  };
9775
- }, [containerRef]);
10091
+ }, [containerRef, dimensionToolVisible, disarmMeasure, findSnap]);
9776
10092
  const screenDStart = applyToPoint8(transform, dStart);
9777
10093
  const screenDEnd = applyToPoint8(transform, dEnd);
9778
10094
  const arrowScreenBounds = {
@@ -9811,7 +10127,9 @@ var DimensionOverlay = ({
9811
10127
  mousePosRef.current.x = rwPoint.x;
9812
10128
  mousePosRef.current.y = rwPoint.y;
9813
10129
  if (dimensionToolStretching) {
9814
- setDEnd({ x: rwPoint.x, y: rwPoint.y });
10130
+ const snap = findSnap(rwPoint);
10131
+ setDEnd({ x: snap.point.x, y: snap.point.y });
10132
+ setActiveSnapIds((prev) => ({ ...prev, end: snap.id }));
9815
10133
  }
9816
10134
  },
9817
10135
  onMouseDown: (e) => {
@@ -9820,15 +10138,19 @@ var DimensionOverlay = ({
9820
10138
  const y = e.clientY - rect.top;
9821
10139
  const rwPoint = applyToPoint8(inverse2(transform), { x, y });
9822
10140
  if (measureToolArmed && !dimensionToolVisible) {
9823
- setDStart({ x: rwPoint.x, y: rwPoint.y });
9824
- setDEnd({ x: rwPoint.x, y: rwPoint.y });
10141
+ const snap = findSnap(rwPoint);
10142
+ setDStart({ x: snap.point.x, y: snap.point.y });
10143
+ setDEnd({ x: snap.point.x, y: snap.point.y });
10144
+ setActiveSnapIds({ start: snap.id, end: snap.id });
9825
10145
  setDimensionToolVisible(true);
9826
10146
  setDimensionToolStretching(true);
9827
10147
  disarmMeasure();
9828
10148
  } else if (dimensionToolStretching) {
9829
10149
  setDimensionToolStretching(false);
10150
+ setActiveSnapIds((prev) => ({ ...prev, end: null }));
9830
10151
  } else if (dimensionToolVisible) {
9831
10152
  setDimensionToolVisible(false);
10153
+ setActiveSnapIds({ start: null, end: null });
9832
10154
  }
9833
10155
  },
9834
10156
  children: [
@@ -9952,6 +10274,49 @@ var DimensionOverlay = ({
9952
10274
  ]
9953
10275
  }
9954
10276
  ),
10277
+ dimensionToolStretching && snappingPointsWithScreen.map((snap) => {
10278
+ const isActive = snap.id === activeSnapIds.start || snap.id === activeSnapIds.end;
10279
+ const half = SNAP_MARKER_SIZE / 2;
10280
+ return /* @__PURE__ */ jsxs4(
10281
+ "svg",
10282
+ {
10283
+ width: SNAP_MARKER_SIZE,
10284
+ height: SNAP_MARKER_SIZE,
10285
+ style: {
10286
+ position: "absolute",
10287
+ left: snap.screenPoint.x - half,
10288
+ top: snap.screenPoint.y - half,
10289
+ pointerEvents: "none",
10290
+ zIndex: zIndexMap.dimensionOverlay
10291
+ },
10292
+ children: [
10293
+ /* @__PURE__ */ jsx6(
10294
+ "line",
10295
+ {
10296
+ x1: 0,
10297
+ y1: 0,
10298
+ x2: SNAP_MARKER_SIZE,
10299
+ y2: SNAP_MARKER_SIZE,
10300
+ stroke: isActive ? "#66ccff" : "white",
10301
+ strokeWidth: 1
10302
+ }
10303
+ ),
10304
+ /* @__PURE__ */ jsx6(
10305
+ "line",
10306
+ {
10307
+ x1: SNAP_MARKER_SIZE,
10308
+ y1: 0,
10309
+ x2: 0,
10310
+ y2: SNAP_MARKER_SIZE,
10311
+ stroke: isActive ? "#66ccff" : "white",
10312
+ strokeWidth: 1
10313
+ }
10314
+ )
10315
+ ]
10316
+ },
10317
+ snap.id
10318
+ );
10319
+ }),
9955
10320
  /* @__PURE__ */ jsxs4(
9956
10321
  "div",
9957
10322
  {
@@ -11055,7 +11420,7 @@ var ErrorOverlay = ({
11055
11420
  };
11056
11421
 
11057
11422
  // src/components/MouseElementTracker.tsx
11058
- import { useState as useState7, useMemo as useMemo3 } from "react";
11423
+ import { useState as useState7, useMemo as useMemo4 } from "react";
11059
11424
  import { applyToPoint as applyToPoint12, inverse as inverse5 } from "transformation-matrix";
11060
11425
 
11061
11426
  // src/components/ElementOverlayBox.tsx
@@ -11472,7 +11837,7 @@ var MouseElementTracker = ({
11472
11837
  }) => {
11473
11838
  const [mousedPrimitives, setMousedPrimitives] = useState7([]);
11474
11839
  const [mousePos, setMousePos] = useState7({ x: 0, y: 0 });
11475
- const highlightedPrimitives = useMemo3(() => {
11840
+ const highlightedPrimitives = useMemo4(() => {
11476
11841
  const highlightedPrimitives2 = [];
11477
11842
  for (const primitive of mousedPrimitives) {
11478
11843
  if (primitive._element?.type === "pcb_via") continue;
@@ -11836,15 +12201,15 @@ var PcbGroupOverlay = ({
11836
12201
 
11837
12202
  // src/components/RatsNestOverlay.tsx
11838
12203
  import { applyToPoint as applyToPoint14, identity as identity9 } from "transformation-matrix";
11839
- import { useMemo as useMemo4 } from "react";
12204
+ import { useMemo as useMemo5 } from "react";
11840
12205
  import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
11841
12206
  var RatsNestOverlay = ({ transform, soup, children }) => {
11842
12207
  const isShowingRatsNest = useGlobalStore((s) => s.is_showing_rats_nest);
11843
- const { netMap, idToNetMap } = useMemo4(
12208
+ const { netMap, idToNetMap } = useMemo5(
11844
12209
  () => getFullConnectivityMapFromCircuitJson(soup || []),
11845
12210
  [soup]
11846
12211
  );
11847
- const ratsNestLines = useMemo4(() => {
12212
+ const ratsNestLines = useMemo5(() => {
11848
12213
  if (!soup || !isShowingRatsNest) return [];
11849
12214
  const getElementPosition = (id) => {
11850
12215
  const element = su(soup)[id.replace(/_\d+$/, "")].get(id);
@@ -11941,7 +12306,7 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
11941
12306
  import {
11942
12307
  useEffect as useEffect15,
11943
12308
  useState as useState9,
11944
- useCallback as useCallback3,
12309
+ useCallback as useCallback4,
11945
12310
  useRef as useRef10
11946
12311
  } from "react";
11947
12312
  import { css as css3 } from "@emotion/css";
@@ -11949,7 +12314,7 @@ import { css as css3 } from "@emotion/css";
11949
12314
  // package.json
11950
12315
  var package_default = {
11951
12316
  name: "@tscircuit/pcb-viewer",
11952
- version: "1.11.237",
12317
+ version: "1.11.239",
11953
12318
  main: "dist/index.js",
11954
12319
  type: "module",
11955
12320
  repository: "tscircuit/pcb-viewer",
@@ -12001,7 +12366,7 @@ var package_default = {
12001
12366
  "@emotion/css": "^11.11.2",
12002
12367
  "@tscircuit/alphabet": "^0.0.3",
12003
12368
  "@vitejs/plugin-react": "^5.0.2",
12004
- "circuit-json": "^0.0.282",
12369
+ "circuit-json": "^0.0.295",
12005
12370
  "circuit-to-svg": "^0.0.240",
12006
12371
  color: "^4.2.3",
12007
12372
  "react-supergrid": "^1.0.10",
@@ -12294,20 +12659,20 @@ var ToolbarOverlay = ({ children, elements }) => {
12294
12659
  useHotKey("6", hotKeyCallbacks["6"]);
12295
12660
  useHotKey("7", hotKeyCallbacks["7"]);
12296
12661
  useHotKey("8", hotKeyCallbacks["8"]);
12297
- const handleMouseEnter = useCallback3(() => {
12662
+ const handleMouseEnter = useCallback4(() => {
12298
12663
  setIsMouseOverContainer(true);
12299
12664
  }, [setIsMouseOverContainer]);
12300
- const handleMouseLeave = useCallback3(() => {
12665
+ const handleMouseLeave = useCallback4(() => {
12301
12666
  setIsMouseOverContainer(false);
12302
12667
  setLayerMenuOpen(false);
12303
12668
  setViewMenuOpen(false);
12304
12669
  setErrorsOpen(false);
12305
12670
  setHoveredErrorId(null);
12306
12671
  }, [setIsMouseOverContainer, setHoveredErrorId]);
12307
- const handleLayerMenuToggle = useCallback3(() => {
12672
+ const handleLayerMenuToggle = useCallback4(() => {
12308
12673
  setLayerMenuOpen(!isLayerMenuOpen);
12309
12674
  }, [isLayerMenuOpen]);
12310
- const handleErrorsToggle = useCallback3(() => {
12675
+ const handleErrorsToggle = useCallback4(() => {
12311
12676
  const newErrorsOpen = !isErrorsOpen;
12312
12677
  setErrorsOpen(newErrorsOpen);
12313
12678
  if (newErrorsOpen) {
@@ -12317,20 +12682,20 @@ var ToolbarOverlay = ({ children, elements }) => {
12317
12682
  setHoveredErrorId(null);
12318
12683
  }
12319
12684
  }, [isErrorsOpen, setHoveredErrorId]);
12320
- const handleEditTraceToggle = useCallback3(() => {
12685
+ const handleEditTraceToggle = useCallback4(() => {
12321
12686
  setEditMode(editModes.in_draw_trace_mode ? "off" : "draw_trace");
12322
12687
  }, [editModes.in_draw_trace_mode, setEditMode]);
12323
- const handleMoveComponentToggle = useCallback3(() => {
12688
+ const handleMoveComponentToggle = useCallback4(() => {
12324
12689
  setEditMode(editModes.in_move_footprint_mode ? "off" : "move_footprint");
12325
12690
  }, [editModes.in_move_footprint_mode, setEditMode]);
12326
- const handleRatsNestToggle = useCallback3(() => {
12691
+ const handleRatsNestToggle = useCallback4(() => {
12327
12692
  setIsShowingRatsNest(!viewSettings.is_showing_rats_nest);
12328
12693
  }, [viewSettings.is_showing_rats_nest, setIsShowingRatsNest]);
12329
- const handleMeasureToolClick = useCallback3(() => {
12694
+ const handleMeasureToolClick = useCallback4(() => {
12330
12695
  setMeasureToolArmed(true);
12331
12696
  window.dispatchEvent(new Event("arm-dimension-tool"));
12332
12697
  }, []);
12333
- const handleViewMenuToggle = useCallback3(() => {
12698
+ const handleViewMenuToggle = useCallback4(() => {
12334
12699
  const newViewMenuOpen = !isViewMenuOpen;
12335
12700
  setViewMenuOpen(newViewMenuOpen);
12336
12701
  if (newViewMenuOpen) {
@@ -12765,7 +13130,7 @@ import { jsx as jsx16 } from "react/jsx-runtime";
12765
13130
  var CanvasElementsRenderer = (props) => {
12766
13131
  const { transform, elements } = props;
12767
13132
  const hoveredErrorId = useGlobalStore((state) => state.hovered_error_id);
12768
- const [primitivesWithoutInteractionMetadata, connectivityMap] = useMemo5(() => {
13133
+ const [primitivesWithoutInteractionMetadata, connectivityMap] = useMemo6(() => {
12769
13134
  const primitivesWithoutInteractionMetadata2 = props.elements.flatMap(
12770
13135
  (elm) => convertElementToPrimitives(elm, props.elements)
12771
13136
  );
@@ -12778,7 +13143,7 @@ var CanvasElementsRenderer = (props) => {
12778
13143
  drawingObjectIdsWithMouseOver: /* @__PURE__ */ new Set(),
12779
13144
  primitiveIdsInMousedOverNet: []
12780
13145
  });
12781
- const errorRelatedIds = useMemo5(() => {
13146
+ const errorRelatedIds = useMemo6(() => {
12782
13147
  if (!hoveredErrorId) return [];
12783
13148
  const errorElements = elements.filter(
12784
13149
  (el) => el.type.includes("error")
@@ -12797,7 +13162,7 @@ var CanvasElementsRenderer = (props) => {
12797
13162
  }
12798
13163
  return relatedIds;
12799
13164
  }, [hoveredErrorId, elements]);
12800
- const primitives = useMemo5(() => {
13165
+ const primitives = useMemo6(() => {
12801
13166
  const combinedPrimitiveIds = [
12802
13167
  ...hoverState.primitiveIdsInMousedOverNet,
12803
13168
  ...errorRelatedIds
@@ -12808,7 +13173,7 @@ var CanvasElementsRenderer = (props) => {
12808
13173
  primitiveIdsInMousedOverNet: combinedPrimitiveIds
12809
13174
  });
12810
13175
  }, [primitivesWithoutInteractionMetadata, hoverState, errorRelatedIds]);
12811
- const onMouseOverPrimitives = useCallback4(
13176
+ const onMouseOverPrimitives = useCallback5(
12812
13177
  (primitivesHoveredOver) => {
12813
13178
  const primitiveIdsInMousedOverNet = [];
12814
13179
  for (const primitive of primitivesHoveredOver) {
@@ -12861,6 +13226,7 @@ var CanvasElementsRenderer = (props) => {
12861
13226
  {
12862
13227
  transform,
12863
13228
  focusOnHover: props.focusOnHover,
13229
+ primitives: primitivesWithoutInteractionMetadata,
12864
13230
  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(
12865
13231
  DebugGraphicsOverlay,
12866
13232
  {
@@ -12970,7 +13336,7 @@ var PCBViewer = ({
12970
13336
  editEvents = editEventsProp ?? editEvents;
12971
13337
  const initialRenderCompleted = useRef11(false);
12972
13338
  const touchStartRef = useRef11(null);
12973
- const circuitJsonKey = useMemo6(
13339
+ const circuitJsonKey = useMemo7(
12974
13340
  () => calculateCircuitJsonKey(circuitJson),
12975
13341
  [circuitJson]
12976
13342
  );
@@ -13003,12 +13369,12 @@ var PCBViewer = ({
13003
13369
  initialRenderCompleted.current = true;
13004
13370
  }
13005
13371
  }, [circuitJson, refDimensions]);
13006
- const pcbElmsPreEdit = useMemo6(() => {
13372
+ const pcbElmsPreEdit = useMemo7(() => {
13007
13373
  return circuitJson?.filter(
13008
13374
  (e) => e.type.startsWith("pcb_") || e.type.startsWith("source_")
13009
13375
  ) ?? [];
13010
13376
  }, [circuitJsonKey]);
13011
- const elements = useMemo6(() => {
13377
+ const elements = useMemo7(() => {
13012
13378
  return applyEditEvents({
13013
13379
  circuitJson: pcbElmsPreEdit,
13014
13380
  editEvents
@@ -13025,7 +13391,7 @@ var PCBViewer = ({
13025
13391
  setEditEvents(newEditEvents);
13026
13392
  onEditEventsChanged?.(newEditEvents);
13027
13393
  };
13028
- const mergedInitialState = useMemo6(
13394
+ const mergedInitialState = useMemo7(
13029
13395
  () => ({
13030
13396
  ...initialState,
13031
13397
  ...disablePcbGroups && { is_showing_pcb_groups: false }