@tscircuit/pcb-viewer 1.11.337 → 1.11.339

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
@@ -24,6 +24,141 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  mod
25
25
  ));
26
26
 
27
+ // node_modules/toggle-selection/index.js
28
+ var require_toggle_selection = __commonJS({
29
+ "node_modules/toggle-selection/index.js"(exports, module) {
30
+ module.exports = function() {
31
+ var selection = document.getSelection();
32
+ if (!selection.rangeCount) {
33
+ return function() {
34
+ };
35
+ }
36
+ var active = document.activeElement;
37
+ var ranges = [];
38
+ for (var i = 0; i < selection.rangeCount; i++) {
39
+ ranges.push(selection.getRangeAt(i));
40
+ }
41
+ switch (active.tagName.toUpperCase()) {
42
+ // .toUpperCase handles XHTML
43
+ case "INPUT":
44
+ case "TEXTAREA":
45
+ active.blur();
46
+ break;
47
+ default:
48
+ active = null;
49
+ break;
50
+ }
51
+ selection.removeAllRanges();
52
+ return function() {
53
+ selection.type === "Caret" && selection.removeAllRanges();
54
+ if (!selection.rangeCount) {
55
+ ranges.forEach(function(range) {
56
+ selection.addRange(range);
57
+ });
58
+ }
59
+ active && active.focus();
60
+ };
61
+ };
62
+ }
63
+ });
64
+
65
+ // node_modules/copy-to-clipboard/index.js
66
+ var require_copy_to_clipboard = __commonJS({
67
+ "node_modules/copy-to-clipboard/index.js"(exports, module) {
68
+ "use strict";
69
+ var deselectCurrent = require_toggle_selection();
70
+ var clipboardToIE11Formatting = {
71
+ "text/plain": "Text",
72
+ "text/html": "Url",
73
+ "default": "Text"
74
+ };
75
+ var defaultMessage = "Copy to clipboard: #{key}, Enter";
76
+ function format(message) {
77
+ var copyKey = (/mac os x/i.test(navigator.userAgent) ? "\u2318" : "Ctrl") + "+C";
78
+ return message.replace(/#{\s*key\s*}/g, copyKey);
79
+ }
80
+ function copy(text, options) {
81
+ var debug, message, reselectPrevious, range, selection, mark, success = false;
82
+ if (!options) {
83
+ options = {};
84
+ }
85
+ debug = options.debug || false;
86
+ try {
87
+ reselectPrevious = deselectCurrent();
88
+ range = document.createRange();
89
+ selection = document.getSelection();
90
+ mark = document.createElement("span");
91
+ mark.textContent = text;
92
+ mark.ariaHidden = "true";
93
+ mark.style.all = "unset";
94
+ mark.style.position = "fixed";
95
+ mark.style.top = 0;
96
+ mark.style.clip = "rect(0, 0, 0, 0)";
97
+ mark.style.whiteSpace = "pre";
98
+ mark.style.webkitUserSelect = "text";
99
+ mark.style.MozUserSelect = "text";
100
+ mark.style.msUserSelect = "text";
101
+ mark.style.userSelect = "text";
102
+ mark.addEventListener("copy", function(e) {
103
+ e.stopPropagation();
104
+ if (options.format) {
105
+ e.preventDefault();
106
+ if (typeof e.clipboardData === "undefined") {
107
+ debug && console.warn("unable to use e.clipboardData");
108
+ debug && console.warn("trying IE specific stuff");
109
+ window.clipboardData.clearData();
110
+ var format2 = clipboardToIE11Formatting[options.format] || clipboardToIE11Formatting["default"];
111
+ window.clipboardData.setData(format2, text);
112
+ } else {
113
+ e.clipboardData.clearData();
114
+ e.clipboardData.setData(options.format, text);
115
+ }
116
+ }
117
+ if (options.onCopy) {
118
+ e.preventDefault();
119
+ options.onCopy(e.clipboardData);
120
+ }
121
+ });
122
+ document.body.appendChild(mark);
123
+ range.selectNodeContents(mark);
124
+ selection.addRange(range);
125
+ var successful = document.execCommand("copy");
126
+ if (!successful) {
127
+ throw new Error("copy command was unsuccessful");
128
+ }
129
+ success = true;
130
+ } catch (err) {
131
+ debug && console.error("unable to copy using execCommand: ", err);
132
+ debug && console.warn("trying IE specific stuff");
133
+ try {
134
+ window.clipboardData.setData(options.format || "text", text);
135
+ options.onCopy && options.onCopy(window.clipboardData);
136
+ success = true;
137
+ } catch (err2) {
138
+ debug && console.error("unable to copy using clipboardData: ", err2);
139
+ debug && console.error("falling back to prompt");
140
+ message = format("message" in options ? options.message : defaultMessage);
141
+ window.prompt(message, text);
142
+ }
143
+ } finally {
144
+ if (selection) {
145
+ if (typeof selection.removeRange == "function") {
146
+ selection.removeRange(range);
147
+ } else {
148
+ selection.removeAllRanges();
149
+ }
150
+ }
151
+ if (mark) {
152
+ document.body.removeChild(mark);
153
+ }
154
+ reselectPrevious();
155
+ }
156
+ return success;
157
+ }
158
+ module.exports = copy;
159
+ }
160
+ });
161
+
27
162
  // node_modules/svgson/dist/svgson.umd.js
28
163
  var require_svgson_umd = __commonJS({
29
164
  "node_modules/svgson/dist/svgson.umd.js"(exports, module) {
@@ -6451,20 +6586,109 @@ var ToastContainer = () => {
6451
6586
  };
6452
6587
 
6453
6588
  // src/PCBViewer.tsx
6454
- import { useEffect as useEffect16, useMemo as useMemo8, useRef as useRef12, useState as useState11 } from "react";
6589
+ import { useEffect as useEffect17, useMemo as useMemo8, useRef as useRef14, useState as useState12 } from "react";
6590
+
6591
+ // node_modules/react-use/esm/useMountedState.js
6592
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef } from "react";
6593
+ function useMountedState() {
6594
+ var mountedRef = useRef(false);
6595
+ var get = useCallback2(function() {
6596
+ return mountedRef.current;
6597
+ }, []);
6598
+ useEffect2(function() {
6599
+ mountedRef.current = true;
6600
+ return function() {
6601
+ mountedRef.current = false;
6602
+ };
6603
+ }, []);
6604
+ return get;
6605
+ }
6606
+
6607
+ // node_modules/react-use/esm/useSetState.js
6608
+ import { useCallback as useCallback3, useState } from "react";
6609
+ var useSetState = function(initialState) {
6610
+ if (initialState === void 0) {
6611
+ initialState = {};
6612
+ }
6613
+ var _a = useState(initialState), state = _a[0], set = _a[1];
6614
+ var setState = useCallback3(function(patch) {
6615
+ set(function(prevState) {
6616
+ return Object.assign({}, prevState, patch instanceof Function ? patch(prevState) : patch);
6617
+ });
6618
+ }, []);
6619
+ return [state, setState];
6620
+ };
6621
+ var useSetState_default = useSetState;
6455
6622
 
6456
6623
  // node_modules/react-use/esm/misc/util.js
6457
6624
  var noop = function() {
6458
6625
  };
6459
6626
  var isBrowser = typeof window !== "undefined";
6460
6627
 
6628
+ // node_modules/react-use/esm/useCopyToClipboard.js
6629
+ var import_copy_to_clipboard = __toESM(require_copy_to_clipboard());
6630
+ import { useCallback as useCallback4 } from "react";
6631
+ var useCopyToClipboard = function() {
6632
+ var isMounted = useMountedState();
6633
+ var _a = useSetState_default({
6634
+ value: void 0,
6635
+ error: void 0,
6636
+ noUserInteraction: true
6637
+ }), state = _a[0], setState = _a[1];
6638
+ var copyToClipboard = useCallback4(function(value) {
6639
+ if (!isMounted()) {
6640
+ return;
6641
+ }
6642
+ var noUserInteraction;
6643
+ var normalizedValue;
6644
+ try {
6645
+ if (typeof value !== "string" && typeof value !== "number") {
6646
+ var error = new Error("Cannot copy typeof " + typeof value + " to clipboard, must be a string");
6647
+ if (true)
6648
+ console.error(error);
6649
+ setState({
6650
+ value,
6651
+ error,
6652
+ noUserInteraction: true
6653
+ });
6654
+ return;
6655
+ } else if (value === "") {
6656
+ var error = new Error("Cannot copy empty string to clipboard.");
6657
+ if (true)
6658
+ console.error(error);
6659
+ setState({
6660
+ value,
6661
+ error,
6662
+ noUserInteraction: true
6663
+ });
6664
+ return;
6665
+ }
6666
+ normalizedValue = value.toString();
6667
+ noUserInteraction = (0, import_copy_to_clipboard.default)(normalizedValue);
6668
+ setState({
6669
+ value: normalizedValue,
6670
+ error: void 0,
6671
+ noUserInteraction
6672
+ });
6673
+ } catch (error2) {
6674
+ setState({
6675
+ value: normalizedValue,
6676
+ error: error2,
6677
+ noUserInteraction
6678
+ });
6679
+ }
6680
+ }, []);
6681
+ return [state, copyToClipboard];
6682
+ };
6683
+ var useCopyToClipboard_default = useCopyToClipboard;
6684
+
6461
6685
  // node_modules/react-use/esm/useIsomorphicLayoutEffect.js
6462
- import { useEffect as useEffect2, useLayoutEffect } from "react";
6463
- var useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect2;
6686
+ import { useEffect as useEffect3, useLayoutEffect } from "react";
6687
+ var useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect3;
6464
6688
  var useIsomorphicLayoutEffect_default = useIsomorphicLayoutEffect;
6465
6689
 
6466
6690
  // node_modules/react-use/esm/useMeasure.js
6467
- import { useMemo as useMemo2, useState } from "react";
6691
+ import { useMemo as useMemo2, useState as useState2 } from "react";
6468
6692
  var defaultState = {
6469
6693
  x: 0,
6470
6694
  y: 0,
@@ -6476,8 +6700,8 @@ var defaultState = {
6476
6700
  right: 0
6477
6701
  };
6478
6702
  function useMeasure() {
6479
- var _a = useState(null), element = _a[0], ref = _a[1];
6480
- var _b = useState(defaultState), rect = _b[0], setRect = _b[1];
6703
+ var _a = useState2(null), element = _a[0], ref = _a[1];
6704
+ var _b = useState2(defaultState), rect = _b[0], setRect = _b[1];
6481
6705
  var observer = useMemo2(function() {
6482
6706
  return new window.ResizeObserver(function(entries) {
6483
6707
  if (entries[0]) {
@@ -6552,17 +6776,17 @@ import {
6552
6776
  applyToPoint as applyToPoint2,
6553
6777
  scale as scale2
6554
6778
  } from "transformation-matrix";
6555
- import { useCallback as useCallback2, useEffect as useEffect3, useReducer, useRef, useState as useState2 } from "react";
6779
+ import { useCallback as useCallback5, useEffect as useEffect4, useReducer, useRef as useRef2, useState as useState3 } from "react";
6556
6780
  var useMouseMatrixTransform = (props = {}) => {
6557
- const extRef = useRef(null);
6558
- const [lastDragCancelTime, setLastDragCancelTime] = useState2(0);
6781
+ const extRef = useRef2(null);
6782
+ const [lastDragCancelTime, setLastDragCancelTime] = useState3(0);
6559
6783
  const outerCanvasElm = props.canvasElm ?? extRef.current;
6560
- const [internalTransform, setInternalTransform] = useState2(
6784
+ const [internalTransform, setInternalTransform] = useState3(
6561
6785
  props.initialTransform ?? identity()
6562
6786
  );
6563
- const [waitCounter, setWaitCounter] = useState2(0);
6787
+ const [waitCounter, setWaitCounter] = useState3(0);
6564
6788
  const [extChangeCounter, incExtChangeCounter] = useReducer((s) => s + 1, 0);
6565
- const setTransform = useCallback2(
6789
+ const setTransform = useCallback5(
6566
6790
  (newTransform) => {
6567
6791
  if (props.onSetTransform) {
6568
6792
  props.onSetTransform(newTransform);
@@ -6573,7 +6797,7 @@ var useMouseMatrixTransform = (props = {}) => {
6573
6797
  },
6574
6798
  [props.onSetTransform, setInternalTransform]
6575
6799
  );
6576
- const setTransformExt = useCallback2(
6800
+ const setTransformExt = useCallback5(
6577
6801
  (newTransform) => {
6578
6802
  setTransform(newTransform);
6579
6803
  incExtChangeCounter();
@@ -6581,13 +6805,13 @@ var useMouseMatrixTransform = (props = {}) => {
6581
6805
  [setTransform]
6582
6806
  );
6583
6807
  const transform = props.transform ?? internalTransform;
6584
- const cancelDrag = useCallback2(() => {
6808
+ const cancelDrag = useCallback5(() => {
6585
6809
  setLastDragCancelTime(Date.now());
6586
6810
  }, []);
6587
- const gestureModeRef = useRef("none");
6588
- const dragDataRef = useRef(null);
6589
- const pinchDataRef = useRef(null);
6590
- useEffect3(() => {
6811
+ const gestureModeRef = useRef2("none");
6812
+ const dragDataRef = useRef2(null);
6813
+ const pinchDataRef = useRef2(null);
6814
+ useEffect4(() => {
6591
6815
  const canvasElm = props.canvasElm ?? extRef.current;
6592
6816
  if (canvasElm && !outerCanvasElm) {
6593
6817
  setWaitCounter(waitCounter + 1);
@@ -6781,7 +7005,7 @@ var useMouseMatrixTransform = (props = {}) => {
6781
7005
  props.enabled,
6782
7006
  props.shouldDrag
6783
7007
  ]);
6784
- const applyTransformToPoint = useCallback2(
7008
+ const applyTransformToPoint = useCallback5(
6785
7009
  (obj) => applyToPoint2(transform, obj),
6786
7010
  [transform]
6787
7011
  );
@@ -7001,7 +7225,7 @@ function addInteractionMetadataToPrimitives({
7001
7225
  }
7002
7226
 
7003
7227
  // src/components/CanvasElementsRenderer.tsx
7004
- import { useCallback as useCallback5, useMemo as useMemo7, useState as useState10 } from "react";
7228
+ import { useCallback as useCallback9, useMemo as useMemo7, useState as useState11 } from "react";
7005
7229
 
7006
7230
  // src/lib/util/expand-stroke.ts
7007
7231
  function getExpandedStroke(strokeInput, defaultWidth) {
@@ -9365,6 +9589,8 @@ function drawSoldermaskElementsForLayer({
9365
9589
  elements,
9366
9590
  layers,
9367
9591
  realToCanvasMat,
9592
+ drawSoldermaskTop,
9593
+ drawSoldermaskBottom,
9368
9594
  primitives
9369
9595
  }) {
9370
9596
  const drawer = new CircuitToCanvasDrawer12(canvas);
@@ -9374,6 +9600,8 @@ function drawSoldermaskElementsForLayer({
9374
9600
  drawer.drawElements(elements, {
9375
9601
  layers,
9376
9602
  drawSoldermask: true,
9603
+ drawSoldermaskTop,
9604
+ drawSoldermaskBottom,
9377
9605
  drawBoardMaterial: false
9378
9606
  });
9379
9607
  } else {
@@ -9384,6 +9612,8 @@ function drawSoldermaskElementsForLayer({
9384
9612
  drawer.drawElements([board, ...nonBoardElements], {
9385
9613
  layers,
9386
9614
  drawSoldermask: true,
9615
+ drawSoldermaskTop,
9616
+ drawSoldermaskBottom,
9387
9617
  drawBoardMaterial: false
9388
9618
  });
9389
9619
  }
@@ -9421,6 +9651,8 @@ function drawSoldermaskElementsForLayer({
9421
9651
  hoverDrawer.drawElements(hoveredElements, {
9422
9652
  layers,
9423
9653
  drawSoldermask: true,
9654
+ drawSoldermaskTop,
9655
+ drawSoldermaskBottom,
9424
9656
  drawBoardMaterial: false
9425
9657
  });
9426
9658
  }
@@ -9545,7 +9777,7 @@ function drawCourtyardElementsForLayer({
9545
9777
  }
9546
9778
 
9547
9779
  // src/components/CanvasPrimitiveRenderer.tsx
9548
- import { useEffect as useEffect4, useRef as useRef2 } from "react";
9780
+ import { useEffect as useEffect5, useRef as useRef3 } from "react";
9549
9781
  import { SuperGrid, toMMSI } from "react-supergrid";
9550
9782
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
9551
9783
  var orderedLayers = [
@@ -9583,10 +9815,10 @@ var CanvasPrimitiveRenderer = ({
9583
9815
  width = 500,
9584
9816
  height = 500
9585
9817
  }) => {
9586
- const canvasRefs = useRef2({});
9818
+ const canvasRefs = useRef3({});
9587
9819
  const selectedLayer = useGlobalStore((s) => s.selected_layer);
9588
9820
  const isShowingSolderMask = useGlobalStore((s) => s.is_showing_solder_mask);
9589
- useEffect4(() => {
9821
+ useEffect5(() => {
9590
9822
  if (!canvasRefs.current) return;
9591
9823
  if (Object.keys(canvasRefs.current).length === 0) return;
9592
9824
  const filteredCanvasRefs = Object.fromEntries(
@@ -9702,23 +9934,30 @@ var CanvasPrimitiveRenderer = ({
9702
9934
  });
9703
9935
  }
9704
9936
  if (isShowingSolderMask) {
9937
+ const soldermaskLayer = selectedLayer === "bottom" ? "bottom" : "top";
9938
+ const drawSoldermaskTop = soldermaskLayer === "top";
9939
+ const drawSoldermaskBottom = soldermaskLayer === "bottom";
9705
9940
  const topSoldermaskCanvas = canvasRefs.current.soldermask_top;
9706
- if (topSoldermaskCanvas) {
9941
+ if (topSoldermaskCanvas && soldermaskLayer === "top") {
9707
9942
  drawSoldermaskElementsForLayer({
9708
9943
  canvas: topSoldermaskCanvas,
9709
9944
  elements,
9710
9945
  layers: ["top_soldermask"],
9711
9946
  realToCanvasMat: transform,
9947
+ drawSoldermaskTop,
9948
+ drawSoldermaskBottom,
9712
9949
  primitives
9713
9950
  });
9714
9951
  }
9715
9952
  const bottomSoldermaskCanvas = canvasRefs.current.soldermask_bottom;
9716
- if (bottomSoldermaskCanvas) {
9953
+ if (bottomSoldermaskCanvas && soldermaskLayer === "bottom") {
9717
9954
  drawSoldermaskElementsForLayer({
9718
9955
  canvas: bottomSoldermaskCanvas,
9719
9956
  elements,
9720
9957
  layers: ["bottom_soldermask"],
9721
9958
  realToCanvasMat: transform,
9959
+ drawSoldermaskTop,
9960
+ drawSoldermaskBottom,
9722
9961
  primitives
9723
9962
  });
9724
9963
  }
@@ -10232,17 +10471,17 @@ function drawGraphicsToCanvas(graphics, target, options = {}) {
10232
10471
  }
10233
10472
 
10234
10473
  // src/components/DebugGraphicsOverlay.tsx
10235
- import { useEffect as useEffect5, useRef as useRef3 } from "react";
10474
+ import { useEffect as useEffect6, useRef as useRef4 } from "react";
10236
10475
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
10237
10476
  var DebugGraphicsOverlay = ({
10238
10477
  children,
10239
10478
  transform,
10240
10479
  debugGraphics
10241
10480
  }) => {
10242
- const canvasRef = useRef3(null);
10481
+ const canvasRef = useRef4(null);
10243
10482
  const [containerRef, { width, height }] = useMeasure_default();
10244
10483
  const is_showing_autorouting = useGlobalStore((s) => s.is_showing_autorouting);
10245
- useEffect5(() => {
10484
+ useEffect6(() => {
10246
10485
  if (!is_showing_autorouting) return;
10247
10486
  if (canvasRef.current && debugGraphics && width && height) {
10248
10487
  canvasRef.current.width = width;
@@ -10283,7 +10522,7 @@ var DebugGraphicsOverlay = ({
10283
10522
  };
10284
10523
 
10285
10524
  // src/components/WarningGraphicsOverlay.tsx
10286
- import { useEffect as useEffect6, useRef as useRef4 } from "react";
10525
+ import { useEffect as useEffect7, useRef as useRef5 } from "react";
10287
10526
  import { applyToPoint as applyToPoint7, identity as identity3 } from "transformation-matrix";
10288
10527
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
10289
10528
  var WarningGraphicsOverlay = ({
@@ -10292,8 +10531,8 @@ var WarningGraphicsOverlay = ({
10292
10531
  elements = []
10293
10532
  }) => {
10294
10533
  const [containerRef, { width, height }] = useMeasure_default();
10295
- const canvasRef = useRef4(null);
10296
- useEffect6(() => {
10534
+ const canvasRef = useRef5(null);
10535
+ useEffect7(() => {
10297
10536
  const canvas = canvasRef.current;
10298
10537
  if (!canvas || !width || !height) return;
10299
10538
  canvas.width = width;
@@ -10371,7 +10610,7 @@ var WarningGraphicsOverlay = ({
10371
10610
  };
10372
10611
 
10373
10612
  // src/components/DimensionOverlay.tsx
10374
- import { useCallback as useCallback3, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef5, useState as useState3 } from "react";
10613
+ import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef6, useState as useState4 } from "react";
10375
10614
  import { applyToPoint as applyToPoint8, identity as identity4, inverse as inverse2 } from "transformation-matrix";
10376
10615
 
10377
10616
  // src/lib/util/get-primitive-bounding-box.ts
@@ -10773,24 +11012,24 @@ var DimensionOverlay = ({
10773
11012
  primitives = []
10774
11013
  }) => {
10775
11014
  if (!transform) transform = identity4();
10776
- const [dimensionToolVisible, setDimensionToolVisible] = useState3(false);
10777
- const [dimensionToolStretching, setDimensionToolStretching] = useState3(false);
10778
- const [measureToolArmed, setMeasureToolArmed] = useState3(false);
10779
- const [activeSnapIds, setActiveSnapIds] = useState3({
11015
+ const [dimensionToolVisible, setDimensionToolVisible] = useState4(false);
11016
+ const [dimensionToolStretching, setDimensionToolStretching] = useState4(false);
11017
+ const [measureToolArmed, setMeasureToolArmed] = useState4(false);
11018
+ const [activeSnapIds, setActiveSnapIds] = useState4({
10780
11019
  start: null,
10781
11020
  end: null
10782
11021
  });
10783
11022
  const isMouseOverContainer = useGlobalStore((s) => s.is_mouse_over_container);
10784
- const disarmMeasure = useCallback3(() => {
11023
+ const disarmMeasure = useCallback6(() => {
10785
11024
  if (measureToolArmed) {
10786
11025
  setMeasureToolArmed(false);
10787
11026
  window.dispatchEvent(new Event("disarm-dimension-tool"));
10788
11027
  }
10789
11028
  }, [measureToolArmed]);
10790
- const [dStart, setDStart] = useState3({ x: 0, y: 0 });
10791
- const [dEnd, setDEnd] = useState3({ x: 0, y: 0 });
10792
- const mousePosRef = useRef5({ x: 0, y: 0 });
10793
- const containerRef = useRef5(null);
11029
+ const [dStart, setDStart] = useState4({ x: 0, y: 0 });
11030
+ const [dEnd, setDEnd] = useState4({ x: 0, y: 0 });
11031
+ const mousePosRef = useRef6({ x: 0, y: 0 });
11032
+ const containerRef = useRef6(null);
10794
11033
  const container = containerRef.current;
10795
11034
  const containerBounds = container?.getBoundingClientRect();
10796
11035
  const elementBoundingBoxes = useMemo4(() => {
@@ -10870,7 +11109,7 @@ var DimensionOverlay = ({
10870
11109
  screenPoint: applyToPoint8(transform, snap.point)
10871
11110
  }));
10872
11111
  }, [snappingPoints, transform]);
10873
- const findSnap = useCallback3(
11112
+ const findSnap = useCallback6(
10874
11113
  (rwPoint) => {
10875
11114
  if (snappingPointsWithScreen.length === 0) {
10876
11115
  return { point: rwPoint, id: null };
@@ -10897,7 +11136,7 @@ var DimensionOverlay = ({
10897
11136
  },
10898
11137
  [snappingPointsWithScreen, transform]
10899
11138
  );
10900
- useEffect7(() => {
11139
+ useEffect8(() => {
10901
11140
  const down = (e) => {
10902
11141
  const target = e.target;
10903
11142
  if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
@@ -11241,7 +11480,7 @@ var DimensionOverlay = ({
11241
11480
  };
11242
11481
 
11243
11482
  // src/components/EditPlacementOverlay.tsx
11244
- import { useRef as useRef6, useState as useState4 } from "react";
11483
+ import { useRef as useRef7, useState as useState5 } from "react";
11245
11484
  import { applyToPoint as applyToPoint9, identity as identity5, inverse as inverse3 } from "transformation-matrix";
11246
11485
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
11247
11486
  var isInsideOf = (pcb_component, point, padding = 0) => {
@@ -11263,11 +11502,11 @@ var EditPlacementOverlay = ({
11263
11502
  onModifyEditEvent
11264
11503
  }) => {
11265
11504
  if (!transform) transform = identity5();
11266
- const containerRef = useRef6(null);
11267
- const [activePcbComponentId, setActivePcbComponent] = useState4(
11505
+ const containerRef = useRef7(null);
11506
+ const [activePcbComponentId, setActivePcbComponent] = useState5(
11268
11507
  null
11269
11508
  );
11270
- const [dragState, setDragState] = useState4(null);
11509
+ const [dragState, setDragState] = useState5(null);
11271
11510
  const isPcbComponentActive = activePcbComponentId !== null;
11272
11511
  const in_edit_mode = useGlobalStore((s) => s.in_edit_mode);
11273
11512
  const in_move_footprint_mode = useGlobalStore((s) => s.in_move_footprint_mode);
@@ -11383,7 +11622,7 @@ var EditPlacementOverlay = ({
11383
11622
  };
11384
11623
 
11385
11624
  // src/components/EditTraceHintOverlay.tsx
11386
- import { Fragment as Fragment2, useEffect as useEffect10, useRef as useRef7, useState as useState5 } from "react";
11625
+ import { Fragment as Fragment2, useEffect as useEffect11, useRef as useRef8, useState as useState6 } from "react";
11387
11626
  import {
11388
11627
  applyToPoint as applyToPoint10,
11389
11628
  identity as identity6,
@@ -11392,12 +11631,12 @@ import {
11392
11631
 
11393
11632
  // src/components/HotkeyActionMenu.tsx
11394
11633
  import { css } from "@emotion/css";
11395
- import { useEffect as useEffect9 } from "react";
11634
+ import { useEffect as useEffect10 } from "react";
11396
11635
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
11397
11636
  var HotkeyActionMenu = ({
11398
11637
  hotkeys
11399
11638
  }) => {
11400
- useEffect9(() => {
11639
+ useEffect10(() => {
11401
11640
  const handleKeyDown = (event) => {
11402
11641
  hotkeys.forEach((hotkey) => {
11403
11642
  if (event.key === hotkey.key) {
@@ -11506,12 +11745,12 @@ var EditTraceHintOverlay = ({
11506
11745
  onModifyEditEvent
11507
11746
  }) => {
11508
11747
  if (!transform) transform = identity6();
11509
- const containerRef = useRef7(null);
11748
+ const containerRef = useRef8(null);
11510
11749
  const containerBounds = containerRef.current?.getBoundingClientRect();
11511
- const [selectedElement, setSelectedElement] = useState5(null);
11750
+ const [selectedElement, setSelectedElement] = useState6(null);
11512
11751
  const toast = useToast();
11513
- const [dragState, setDragState] = useState5(null);
11514
- const [shouldCreateAsVia, setShouldCreateAsVia] = useState5(false);
11752
+ const [dragState, setDragState] = useState6(null);
11753
+ const [shouldCreateAsVia, setShouldCreateAsVia] = useState6(false);
11515
11754
  const isElementSelected = selectedElement !== null;
11516
11755
  const in_edit_trace_mode = useGlobalStore((s) => s.in_draw_trace_mode);
11517
11756
  const disabled = disabledProp || !in_edit_trace_mode;
@@ -11520,7 +11759,7 @@ var EditTraceHintOverlay = ({
11520
11759
  ogCenterScreen = applyToPoint10(transform, dragState?.originalCenter);
11521
11760
  dragEndScreen = applyToPoint10(transform, dragState?.dragEnd);
11522
11761
  }
11523
- useEffect10(() => {
11762
+ useEffect11(() => {
11524
11763
  if (!isElementSelected) return;
11525
11764
  function keyDown(e) {
11526
11765
  if (e.key === "Escape") {
@@ -11792,7 +12031,7 @@ var EditTraceHintOverlay = ({
11792
12031
  };
11793
12032
 
11794
12033
  // src/components/ErrorOverlay.tsx
11795
- import { useRef as useRef8, Fragment as Fragment3 } from "react";
12034
+ import { useRef as useRef9, Fragment as Fragment3 } from "react";
11796
12035
  import { css as css2 } from "@emotion/css";
11797
12036
  import { applyToPoint as applyToPoint11, identity as identity7 } from "transformation-matrix";
11798
12037
 
@@ -11951,7 +12190,7 @@ var ErrorOverlay = ({
11951
12190
  transform = identity7(),
11952
12191
  elements
11953
12192
  }) => {
11954
- const containerRef = useRef8(null);
12193
+ const containerRef = useRef9(null);
11955
12194
  const { isShowingDRCErrors, hoveredErrorId } = useGlobalStore((state) => ({
11956
12195
  isShowingDRCErrors: state.is_showing_drc_errors,
11957
12196
  hoveredErrorId: state.hovered_error_id
@@ -12317,11 +12556,11 @@ function ifSetsMatchExactly(set1, set2) {
12317
12556
  }
12318
12557
 
12319
12558
  // src/components/MouseElementTracker.tsx
12320
- import { useState as useState7, useMemo as useMemo5 } from "react";
12559
+ import { useState as useState8, useMemo as useMemo5 } from "react";
12321
12560
  import { applyToPoint as applyToPoint14, inverse as inverse5 } from "transformation-matrix";
12322
12561
 
12323
12562
  // src/components/ElementOverlayBox.tsx
12324
- import { useEffect as useEffect11, useState as useState6 } from "react";
12563
+ import { useEffect as useEffect12, useState as useState7 } from "react";
12325
12564
 
12326
12565
  // src/lib/get-trace-overlay-text.ts
12327
12566
  function getTraceOverlayInfo({
@@ -12439,9 +12678,9 @@ var HighlightedPrimitiveBoxWithText = ({
12439
12678
  mousePos,
12440
12679
  elements
12441
12680
  }) => {
12442
- const [finalState, setFinalState] = useState6(false);
12681
+ const [finalState, setFinalState] = useState7(false);
12443
12682
  const primitiveElement = primitive._element;
12444
- useEffect11(() => {
12683
+ useEffect12(() => {
12445
12684
  setTimeout(() => {
12446
12685
  setFinalState(true);
12447
12686
  }, 100);
@@ -13365,8 +13604,8 @@ var MouseElementTracker = ({
13365
13604
  primitives,
13366
13605
  onMouseHoverOverPrimitives
13367
13606
  }) => {
13368
- const [mousedPrimitives, setMousedPrimitives] = useState7([]);
13369
- const [mousePos, setMousePos] = useState7({ x: 0, y: 0 });
13607
+ const [mousedPrimitives, setMousedPrimitives] = useState8([]);
13608
+ const [mousePos, setMousePos] = useState8({ x: 0, y: 0 });
13370
13609
  const [containerRef, { width, height }] = useMeasure_default();
13371
13610
  const highlightedPrimitives = useMemo5(() => {
13372
13611
  const highlightedPrimitives2 = [];
@@ -13521,7 +13760,7 @@ var MouseElementTracker = ({
13521
13760
  // src/components/PcbGroupOverlay.tsx
13522
13761
  import { applyToPoint as applyToPoint15 } from "transformation-matrix";
13523
13762
  import { identity as identity8 } from "transformation-matrix";
13524
- import { useRef as useRef9, useEffect as useEffect12 } from "react";
13763
+ import { useRef as useRef10, useEffect as useEffect13 } from "react";
13525
13764
  import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
13526
13765
  var GROUP_COLORS = [
13527
13766
  "rgb(255, 100, 100)",
@@ -13542,7 +13781,7 @@ var PcbGroupOverlay = ({
13542
13781
  hoveredComponentIds = []
13543
13782
  }) => {
13544
13783
  const [containerRef, { width, height }] = useMeasure_default();
13545
- const canvasRef = useRef9(null);
13784
+ const canvasRef = useRef10(null);
13546
13785
  const {
13547
13786
  is_showing_pcb_groups,
13548
13787
  pcb_group_view_mode,
@@ -13552,7 +13791,7 @@ var PcbGroupOverlay = ({
13552
13791
  pcb_group_view_mode: s.pcb_group_view_mode,
13553
13792
  is_showing_group_anchor_offsets: s.is_showing_group_anchor_offsets
13554
13793
  }));
13555
- useEffect12(() => {
13794
+ useEffect13(() => {
13556
13795
  const canvas = canvasRef.current;
13557
13796
  if (!canvas || !width || !height) return;
13558
13797
  canvas.width = width;
@@ -13886,10 +14125,10 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
13886
14125
 
13887
14126
  // src/components/ToolbarOverlay.tsx
13888
14127
  import {
13889
- useEffect as useEffect15,
13890
- useState as useState9,
13891
- useCallback as useCallback4,
13892
- useRef as useRef11,
14128
+ useEffect as useEffect16,
14129
+ useState as useState10,
14130
+ useCallback as useCallback8,
14131
+ useRef as useRef13,
13893
14132
  useLayoutEffect as useLayoutEffect2
13894
14133
  } from "react";
13895
14134
  import { css as css3 } from "@emotion/css";
@@ -13897,7 +14136,7 @@ import { css as css3 } from "@emotion/css";
13897
14136
  // package.json
13898
14137
  var package_default = {
13899
14138
  name: "@tscircuit/pcb-viewer",
13900
- version: "1.11.336",
14139
+ version: "1.11.338",
13901
14140
  main: "dist/index.js",
13902
14141
  type: "module",
13903
14142
  repository: "tscircuit/pcb-viewer",
@@ -13951,7 +14190,7 @@ var package_default = {
13951
14190
  "@tscircuit/math-utils": "^0.0.29",
13952
14191
  "@vitejs/plugin-react": "^5.0.2",
13953
14192
  "circuit-json": "^0.0.374",
13954
- "circuit-to-canvas": "^0.0.72",
14193
+ "circuit-to-canvas": "^0.0.75",
13955
14194
  "circuit-to-svg": "^0.0.323",
13956
14195
  color: "^4.2.3",
13957
14196
  "react-supergrid": "^1.0.10",
@@ -13962,20 +14201,20 @@ var package_default = {
13962
14201
  };
13963
14202
 
13964
14203
  // src/hooks/useHotKey.ts
13965
- import { useEffect as useEffect13, useRef as useRef10 } from "react";
14204
+ import { useEffect as useEffect14, useRef as useRef11 } from "react";
13966
14205
  var useHotKey = (key, onUse, containerRef) => {
13967
14206
  const isMouseOverContainer = useGlobalStore(
13968
14207
  (s) => s.is_mouse_over_container
13969
14208
  );
13970
- const isMouseOverContainerRef = useRef10(isMouseOverContainer);
13971
- const onUseRef = useRef10(onUse);
13972
- useEffect13(() => {
14209
+ const isMouseOverContainerRef = useRef11(isMouseOverContainer);
14210
+ const onUseRef = useRef11(onUse);
14211
+ useEffect14(() => {
13973
14212
  isMouseOverContainerRef.current = isMouseOverContainer;
13974
14213
  }, [isMouseOverContainer]);
13975
- useEffect13(() => {
14214
+ useEffect14(() => {
13976
14215
  onUseRef.current = onUse;
13977
14216
  }, [onUse]);
13978
- useEffect13(() => {
14217
+ useEffect14(() => {
13979
14218
  if (!key) return;
13980
14219
  const handleKeyDown = (event) => {
13981
14220
  const target = event.target;
@@ -14003,10 +14242,10 @@ var useHotKey = (key, onUse, containerRef) => {
14003
14242
  };
14004
14243
 
14005
14244
  // src/hooks/useIsSmallScreen.ts
14006
- import { useEffect as useEffect14, useState as useState8 } from "react";
14245
+ import { useEffect as useEffect15, useState as useState9 } from "react";
14007
14246
  var useIsSmallScreen = () => {
14008
- const [isSmallScreen, setIsSmallScreen] = useState8(false);
14009
- useEffect14(() => {
14247
+ const [isSmallScreen, setIsSmallScreen] = useState9(false);
14248
+ useEffect15(() => {
14010
14249
  const checkIsMobile = () => {
14011
14250
  setIsSmallScreen(window.innerWidth <= 768);
14012
14251
  };
@@ -14017,9 +14256,44 @@ var useIsSmallScreen = () => {
14017
14256
  return isSmallScreen;
14018
14257
  };
14019
14258
 
14259
+ // src/hooks/useMobileTouch.ts
14260
+ import {
14261
+ useCallback as useCallback7,
14262
+ useRef as useRef12
14263
+ } from "react";
14264
+ var useMobileTouch = (onClick, options = { stopPropagation: true }) => {
14265
+ const lastInteractionRef = useRef12(0);
14266
+ const handleInteraction = useCallback7(
14267
+ (e) => {
14268
+ if (options.stopPropagation) {
14269
+ e.stopPropagation();
14270
+ }
14271
+ const now = Date.now();
14272
+ if (now - lastInteractionRef.current < 300) return;
14273
+ lastInteractionRef.current = now;
14274
+ onClick?.(e);
14275
+ },
14276
+ [onClick, options.stopPropagation]
14277
+ );
14278
+ const onTouchEnd = useCallback7(
14279
+ (e) => {
14280
+ e.preventDefault();
14281
+ handleInteraction(e);
14282
+ },
14283
+ [handleInteraction]
14284
+ );
14285
+ const style = { touchAction: "manipulation" };
14286
+ return {
14287
+ onClick: handleInteraction,
14288
+ onTouchEnd,
14289
+ style
14290
+ };
14291
+ };
14292
+
14020
14293
  // src/components/ToolbarOverlay.tsx
14021
14294
  import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
14022
14295
  var LayerButton = ({ name, selected, onClick }) => {
14296
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14023
14297
  return /* @__PURE__ */ jsxs14(
14024
14298
  "div",
14025
14299
  {
@@ -14034,7 +14308,8 @@ var LayerButton = ({ name, selected, onClick }) => {
14034
14308
  background-color: rgba(255, 255, 255, 0.1);
14035
14309
  }
14036
14310
  `,
14037
- onClick,
14311
+ ...touchHandlers,
14312
+ style: touchStyle,
14038
14313
  children: [
14039
14314
  /* @__PURE__ */ jsx20("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
14040
14315
  /* @__PURE__ */ jsx20(
@@ -14057,43 +14332,40 @@ var ToolbarButton = ({
14057
14332
  isSmallScreen,
14058
14333
  onClick,
14059
14334
  ...props
14060
- }) => /* @__PURE__ */ jsx20(
14061
- "div",
14062
- {
14063
- ...props,
14064
- onClick,
14065
- onTouchEnd: (e) => {
14066
- e.preventDefault();
14067
- e.stopPropagation();
14068
- if (onClick) {
14069
- onClick(e);
14070
- }
14071
- },
14072
- style: {
14073
- backgroundColor: "#1F1F1F",
14074
- border: "1px solid #666",
14075
- margin: 0,
14076
- padding: 4,
14077
- paddingLeft: isSmallScreen ? 8 : 6,
14078
- paddingRight: isSmallScreen ? 8 : 6,
14079
- borderRadius: 2,
14080
- color: "#eee",
14081
- cursor: "pointer",
14082
- fontSize: 12,
14083
- height: "fit-content",
14084
- touchAction: "manipulation",
14085
- userSelect: "none",
14086
- whiteSpace: "nowrap",
14087
- ...props.style
14088
- },
14089
- children
14090
- }
14091
- );
14335
+ }) => {
14336
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14337
+ return /* @__PURE__ */ jsx20(
14338
+ "div",
14339
+ {
14340
+ ...props,
14341
+ ...touchHandlers,
14342
+ style: {
14343
+ backgroundColor: "#1F1F1F",
14344
+ border: "1px solid #666",
14345
+ margin: 0,
14346
+ padding: 4,
14347
+ paddingLeft: isSmallScreen ? 8 : 6,
14348
+ paddingRight: isSmallScreen ? 8 : 6,
14349
+ borderRadius: 2,
14350
+ color: "#eee",
14351
+ cursor: "pointer",
14352
+ fontSize: 12,
14353
+ height: "fit-content",
14354
+ userSelect: "none",
14355
+ whiteSpace: "nowrap",
14356
+ ...touchStyle,
14357
+ ...props.style
14358
+ },
14359
+ children
14360
+ }
14361
+ );
14362
+ };
14092
14363
  var CheckboxMenuItem = ({
14093
14364
  label,
14094
14365
  checked,
14095
14366
  onClick
14096
14367
  }) => {
14368
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14097
14369
  return /* @__PURE__ */ jsxs14(
14098
14370
  "div",
14099
14371
  {
@@ -14111,15 +14383,8 @@ var CheckboxMenuItem = ({
14111
14383
  background-color: rgba(255, 255, 255, 0.1);
14112
14384
  }
14113
14385
  `,
14114
- onClick: (e) => {
14115
- e.stopPropagation();
14116
- onClick();
14117
- },
14118
- onTouchEnd: (e) => {
14119
- e.preventDefault();
14120
- e.stopPropagation();
14121
- onClick();
14122
- },
14386
+ ...touchHandlers,
14387
+ style: touchStyle,
14123
14388
  children: [
14124
14389
  /* @__PURE__ */ jsx20("input", { type: "checkbox", checked, onChange: () => {
14125
14390
  }, readOnly: true }),
@@ -14129,6 +14394,7 @@ var CheckboxMenuItem = ({
14129
14394
  );
14130
14395
  };
14131
14396
  var RadioMenuItem = ({ label, checked, onClick }) => {
14397
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14132
14398
  return /* @__PURE__ */ jsxs14(
14133
14399
  "div",
14134
14400
  {
@@ -14146,15 +14412,8 @@ var RadioMenuItem = ({ label, checked, onClick }) => {
14146
14412
  background-color: rgba(255, 255, 255, 0.1);
14147
14413
  }
14148
14414
  `,
14149
- onClick: (e) => {
14150
- e.stopPropagation();
14151
- onClick();
14152
- },
14153
- onTouchEnd: (e) => {
14154
- e.preventDefault();
14155
- e.stopPropagation();
14156
- onClick();
14157
- },
14415
+ ...touchHandlers,
14416
+ style: touchStyle,
14158
14417
  children: [
14159
14418
  /* @__PURE__ */ jsx20("input", { type: "radio", checked, onChange: () => {
14160
14419
  }, readOnly: true }),
@@ -14163,6 +14422,39 @@ var RadioMenuItem = ({ label, checked, onClick }) => {
14163
14422
  }
14164
14423
  );
14165
14424
  };
14425
+ var CopyErrorButton = ({
14426
+ errorId,
14427
+ errorMessage,
14428
+ copiedErrorId,
14429
+ onCopy
14430
+ }) => {
14431
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(
14432
+ () => onCopy(errorMessage, errorId)
14433
+ );
14434
+ return /* @__PURE__ */ jsx20(
14435
+ "button",
14436
+ {
14437
+ type: "button",
14438
+ "aria-label": copiedErrorId === errorId ? "Error message copied" : "Copy error message",
14439
+ style: {
14440
+ position: "absolute",
14441
+ top: 12,
14442
+ right: 16,
14443
+ cursor: "pointer",
14444
+ color: "#888",
14445
+ fontSize: 16,
14446
+ background: "none",
14447
+ border: "none",
14448
+ padding: 0,
14449
+ display: "flex",
14450
+ alignItems: "center",
14451
+ ...touchStyle
14452
+ },
14453
+ ...touchHandlers,
14454
+ children: copiedErrorId === errorId ? /* @__PURE__ */ jsx20("span", { style: { color: "#4caf50", fontSize: 12 }, children: "Copied!" }) : /* @__PURE__ */ jsx20("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx20("path", { d: "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" }) })
14455
+ }
14456
+ );
14457
+ };
14166
14458
  var ToolbarOverlay = ({ children, elements }) => {
14167
14459
  const isSmallScreen = useIsSmallScreen();
14168
14460
  const {
@@ -14215,13 +14507,15 @@ var ToolbarOverlay = ({ children, elements }) => {
14215
14507
  setPcbGroupViewMode: s.setPcbGroupViewMode,
14216
14508
  setHoveredErrorId: s.setHoveredErrorId
14217
14509
  }));
14218
- const [isViewMenuOpen, setViewMenuOpen] = useState9(false);
14219
- const [isLayerMenuOpen, setLayerMenuOpen] = useState9(false);
14220
- const [isErrorsOpen, setErrorsOpen] = useState9(false);
14221
- const [measureToolArmed, setMeasureToolArmed] = useState9(false);
14222
- const errorElementsRef = useRef11(/* @__PURE__ */ new Map());
14223
- const arrowElementsRef = useRef11(/* @__PURE__ */ new Map());
14224
- useEffect15(() => {
14510
+ const [isViewMenuOpen, setViewMenuOpen] = useState10(false);
14511
+ const [isLayerMenuOpen, setLayerMenuOpen] = useState10(false);
14512
+ const [isErrorsOpen, setErrorsOpen] = useState10(false);
14513
+ const [measureToolArmed, setMeasureToolArmed] = useState10(false);
14514
+ const [copiedErrorId, setCopiedErrorId] = useState10(null);
14515
+ const [, copyToClipboard] = useCopyToClipboard_default();
14516
+ const errorElementsRef = useRef13(/* @__PURE__ */ new Map());
14517
+ const arrowElementsRef = useRef13(/* @__PURE__ */ new Map());
14518
+ useEffect16(() => {
14225
14519
  const arm = () => setMeasureToolArmed(true);
14226
14520
  const disarm = () => setMeasureToolArmed(false);
14227
14521
  window.addEventListener("arm-dimension-tool", arm);
@@ -14241,8 +14535,8 @@ var ToolbarOverlay = ({ children, elements }) => {
14241
14535
  "bottom"
14242
14536
  ];
14243
14537
  const processedLayers = availableLayers;
14244
- const hasRunInitialMouseCheck = useRef11(false);
14245
- const hotkeyBoundaryRef = useRef11(null);
14538
+ const hasRunInitialMouseCheck = useRef13(false);
14539
+ const hotkeyBoundaryRef = useRef13(null);
14246
14540
  const hotKeyCallbacks = {
14247
14541
  "1": availableLayers[0] ? () => selectLayer(availableLayers[0]) : () => {
14248
14542
  },
@@ -14287,25 +14581,25 @@ var ToolbarOverlay = ({ children, elements }) => {
14287
14581
  document.removeEventListener("mousemove", checkMousePosition);
14288
14582
  };
14289
14583
  }, [setIsMouseOverContainer]);
14290
- const handleMouseEnter = useCallback4(() => {
14584
+ const handleMouseEnter = useCallback8(() => {
14291
14585
  setIsMouseOverContainer(true);
14292
14586
  }, [setIsMouseOverContainer]);
14293
- const handleMouseMove = useCallback4(() => {
14587
+ const handleMouseMove = useCallback8(() => {
14294
14588
  if (!isMouseOverContainer) {
14295
14589
  setIsMouseOverContainer(true);
14296
14590
  }
14297
14591
  }, [isMouseOverContainer, setIsMouseOverContainer]);
14298
- const handleMouseLeave = useCallback4(() => {
14592
+ const handleMouseLeave = useCallback8(() => {
14299
14593
  setIsMouseOverContainer(false);
14300
14594
  setLayerMenuOpen(false);
14301
14595
  setViewMenuOpen(false);
14302
14596
  setErrorsOpen(false);
14303
14597
  setHoveredErrorId(null);
14304
14598
  }, [setIsMouseOverContainer, setHoveredErrorId]);
14305
- const handleLayerMenuToggle = useCallback4(() => {
14599
+ const handleLayerMenuToggle = useCallback8(() => {
14306
14600
  setLayerMenuOpen(!isLayerMenuOpen);
14307
14601
  }, [isLayerMenuOpen]);
14308
- const handleErrorsToggle = useCallback4(() => {
14602
+ const handleErrorsToggle = useCallback8(() => {
14309
14603
  const newErrorsOpen = !isErrorsOpen;
14310
14604
  setErrorsOpen(newErrorsOpen);
14311
14605
  if (newErrorsOpen) {
@@ -14315,20 +14609,20 @@ var ToolbarOverlay = ({ children, elements }) => {
14315
14609
  setHoveredErrorId(null);
14316
14610
  }
14317
14611
  }, [isErrorsOpen, setHoveredErrorId]);
14318
- const handleEditTraceToggle = useCallback4(() => {
14612
+ const handleEditTraceToggle = useCallback8(() => {
14319
14613
  setEditMode(editModes.in_draw_trace_mode ? "off" : "draw_trace");
14320
14614
  }, [editModes.in_draw_trace_mode, setEditMode]);
14321
- const handleMoveComponentToggle = useCallback4(() => {
14615
+ const handleMoveComponentToggle = useCallback8(() => {
14322
14616
  setEditMode(editModes.in_move_footprint_mode ? "off" : "move_footprint");
14323
14617
  }, [editModes.in_move_footprint_mode, setEditMode]);
14324
- const handleRatsNestToggle = useCallback4(() => {
14618
+ const handleRatsNestToggle = useCallback8(() => {
14325
14619
  setIsShowingRatsNest(!viewSettings.is_showing_rats_nest);
14326
14620
  }, [viewSettings.is_showing_rats_nest, setIsShowingRatsNest]);
14327
- const handleMeasureToolClick = useCallback4(() => {
14621
+ const handleMeasureToolClick = useCallback8(() => {
14328
14622
  setMeasureToolArmed(true);
14329
14623
  window.dispatchEvent(new Event("arm-dimension-tool"));
14330
14624
  }, []);
14331
- const handleViewMenuToggle = useCallback4(() => {
14625
+ const handleViewMenuToggle = useCallback8(() => {
14332
14626
  const newViewMenuOpen = !isViewMenuOpen;
14333
14627
  setViewMenuOpen(newViewMenuOpen);
14334
14628
  if (newViewMenuOpen) {
@@ -14573,7 +14867,7 @@ var ToolbarOverlay = ({ children, elements }) => {
14573
14867
  ]
14574
14868
  }
14575
14869
  ),
14576
- /* @__PURE__ */ jsx20(
14870
+ /* @__PURE__ */ jsxs14(
14577
14871
  "div",
14578
14872
  {
14579
14873
  ref: (el) => {
@@ -14584,30 +14878,48 @@ var ToolbarOverlay = ({ children, elements }) => {
14584
14878
  display: "none",
14585
14879
  padding: "12px 16px",
14586
14880
  backgroundColor: "#1a1a1a",
14587
- borderTop: "1px solid #444"
14881
+ borderTop: "1px solid #444",
14882
+ position: "relative"
14588
14883
  },
14589
- children: /* @__PURE__ */ jsx20(
14590
- "div",
14591
- {
14592
- style: {
14593
- fontSize: isSmallScreen ? "11px" : "12px",
14594
- color: "#ccc",
14595
- lineHeight: 1.5,
14596
- wordWrap: "break-word",
14597
- overflowWrap: "break-word",
14598
- hyphens: "auto",
14599
- userSelect: "text"
14600
- },
14601
- onMouseDown: (event) => event.stopPropagation(),
14602
- onClick: (event) => event.stopPropagation(),
14603
- children: e.message
14604
- }
14605
- )
14884
+ children: [
14885
+ /* @__PURE__ */ jsx20(
14886
+ "div",
14887
+ {
14888
+ style: {
14889
+ fontSize: isSmallScreen ? "11px" : "12px",
14890
+ color: "#ccc",
14891
+ lineHeight: 1.5,
14892
+ wordWrap: "break-word",
14893
+ overflowWrap: "break-word",
14894
+ hyphens: "auto",
14895
+ userSelect: "text",
14896
+ paddingRight: 30
14897
+ // Space for the copy icon
14898
+ },
14899
+ onMouseDown: (event) => event.stopPropagation(),
14900
+ onClick: (event) => event.stopPropagation(),
14901
+ children: e.message
14902
+ }
14903
+ ),
14904
+ /* @__PURE__ */ jsx20(
14905
+ CopyErrorButton,
14906
+ {
14907
+ errorId,
14908
+ errorMessage: e.message,
14909
+ copiedErrorId,
14910
+ onCopy: (msg, id) => {
14911
+ copyToClipboard(msg);
14912
+ setCopiedErrorId(id);
14913
+ setTimeout(() => setCopiedErrorId(null), 2e3);
14914
+ }
14915
+ }
14916
+ )
14917
+ ]
14606
14918
  }
14607
14919
  )
14608
14920
  ]
14609
14921
  },
14610
- i
14922
+ errorId
14611
14923
  );
14612
14924
  })
14613
14925
  }
@@ -14822,11 +15134,11 @@ var CanvasElementsRenderer = (props) => {
14822
15134
  );
14823
15135
  return [primitivesWithoutInteractionMetadata2, connectivityMap2];
14824
15136
  }, [elementsToRender, props.elements]);
14825
- const [hoverState, setHoverState] = useState10({
15137
+ const [hoverState, setHoverState] = useState11({
14826
15138
  drawingObjectIdsWithMouseOver: /* @__PURE__ */ new Set(),
14827
15139
  primitiveIdsInMousedOverNet: []
14828
15140
  });
14829
- const [hoveredComponentIds, setHoveredComponentIds] = useState10([]);
15141
+ const [hoveredComponentIds, setHoveredComponentIds] = useState11([]);
14830
15142
  const errorRelatedIds = useMemo7(() => {
14831
15143
  if (!hoveredErrorId) return [];
14832
15144
  const errorElements = elements.filter(
@@ -14857,7 +15169,7 @@ var CanvasElementsRenderer = (props) => {
14857
15169
  primitiveIdsInMousedOverNet: combinedPrimitiveIds
14858
15170
  });
14859
15171
  }, [primitivesWithoutInteractionMetadata, hoverState, errorRelatedIds]);
14860
- const onMouseOverPrimitives = useCallback5(
15172
+ const onMouseOverPrimitives = useCallback9(
14861
15173
  (primitivesHoveredOver) => {
14862
15174
  const primitiveIdsInMousedOverNet = [];
14863
15175
  for (const primitive of primitivesHoveredOver) {
@@ -15033,11 +15345,11 @@ var PCBViewer = ({
15033
15345
  clickToInteractEnabled = false,
15034
15346
  disablePcbGroups = false
15035
15347
  }) => {
15036
- const [isInteractionEnabled, setIsInteractionEnabled] = useState11(
15348
+ const [isInteractionEnabled, setIsInteractionEnabled] = useState12(
15037
15349
  !clickToInteractEnabled
15038
15350
  );
15039
15351
  const [ref, refDimensions] = useMeasure_default();
15040
- const [transform, setTransformInternal] = useState11(defaultTransform);
15352
+ const [transform, setTransformInternal] = useState12(defaultTransform);
15041
15353
  const {
15042
15354
  ref: transformRef,
15043
15355
  setTransform,
@@ -15047,10 +15359,10 @@ var PCBViewer = ({
15047
15359
  onSetTransform: setTransformInternal,
15048
15360
  enabled: isInteractionEnabled
15049
15361
  });
15050
- let [editEvents, setEditEvents] = useState11([]);
15362
+ let [editEvents, setEditEvents] = useState12([]);
15051
15363
  editEvents = editEventsProp ?? editEvents;
15052
- const initialRenderCompleted = useRef12(false);
15053
- const touchStartRef = useRef12(null);
15364
+ const initialRenderCompleted = useRef14(false);
15365
+ const touchStartRef = useRef14(null);
15054
15366
  const circuitJsonKey = useMemo8(
15055
15367
  () => calculateCircuitJsonKey(circuitJson),
15056
15368
  [circuitJson]
@@ -15076,7 +15388,7 @@ var PCBViewer = ({
15076
15388
  setTransform(targetTransform);
15077
15389
  return;
15078
15390
  };
15079
- useEffect16(() => {
15391
+ useEffect17(() => {
15080
15392
  if (!refDimensions?.width) return;
15081
15393
  if (!circuitJson) return;
15082
15394
  if (circuitJson.length === 0) return;
@@ -15085,7 +15397,7 @@ var PCBViewer = ({
15085
15397
  initialRenderCompleted.current = true;
15086
15398
  }
15087
15399
  }, [circuitJson, refDimensions]);
15088
- useEffect16(() => {
15400
+ useEffect17(() => {
15089
15401
  if (initialRenderCompleted.current === true) {
15090
15402
  resetTransform();
15091
15403
  }