@tscircuit/pcb-viewer 1.11.336 → 1.11.338

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) {
@@ -8098,6 +8322,7 @@ var LAYER_NAME_TO_COLOR = {
8098
8322
  ...colors_default.board
8099
8323
  };
8100
8324
  var DEFAULT_DRAW_ORDER = [
8325
+ "board",
8101
8326
  "inner6",
8102
8327
  "inner5",
8103
8328
  "inner4",
@@ -8109,15 +8334,12 @@ var DEFAULT_DRAW_ORDER = [
8109
8334
  "bottom_silkscreen",
8110
8335
  "top",
8111
8336
  "soldermask_top",
8112
- "soldermask_with_copper_bottom",
8113
- "soldermask_with_copper_top",
8114
8337
  "bottom_courtyard",
8115
8338
  "bottom_fabrication",
8116
8339
  "top_courtyard",
8117
8340
  "top_fabrication",
8118
8341
  "edge_cuts",
8119
- "top_silkscreen",
8120
- "board"
8342
+ "top_silkscreen"
8121
8343
  ];
8122
8344
  var Drawer = class {
8123
8345
  canvasLayerMap;
@@ -8408,35 +8630,38 @@ var Drawer = class {
8408
8630
  */
8409
8631
  orderAndFadeLayers() {
8410
8632
  const { canvasLayerMap, foregroundLayer } = this;
8633
+ const associatedSoldermask = foregroundLayer === "top" ? "soldermask_top" : foregroundLayer === "bottom" ? "soldermask_bottom" : void 0;
8411
8634
  const associatedSilkscreen = foregroundLayer === "top" ? "top_silkscreen" : foregroundLayer === "bottom" ? "bottom_silkscreen" : void 0;
8412
8635
  const associatedNotes = foregroundLayer === "top" ? "top_notes" : foregroundLayer === "bottom" ? "bottom_notes" : void 0;
8413
8636
  const associatedFabrication = foregroundLayer === "top" ? "top_fabrication" : foregroundLayer === "bottom" ? "bottom_fabrication" : void 0;
8414
- const maskWithCopperLayerForForeground = foregroundLayer === "top" ? "soldermask_with_copper_top" : foregroundLayer === "bottom" ? "soldermask_with_copper_bottom" : void 0;
8415
8637
  const opaqueLayers = /* @__PURE__ */ new Set([
8416
8638
  foregroundLayer,
8417
8639
  "drill",
8418
8640
  "edge_cuts",
8419
8641
  "other",
8420
8642
  "board",
8643
+ ...associatedSoldermask ? [associatedSoldermask] : [],
8421
8644
  ...associatedSilkscreen ? [associatedSilkscreen] : [],
8422
8645
  ...associatedNotes ? [associatedNotes] : [],
8423
- ...associatedFabrication ? [associatedFabrication] : [],
8424
- ...maskWithCopperLayerForForeground ? [maskWithCopperLayerForForeground] : []
8646
+ ...associatedFabrication ? [associatedFabrication] : []
8425
8647
  ]);
8426
8648
  const layersToShiftToTop = [
8427
8649
  foregroundLayer,
8428
8650
  "edge_cuts",
8651
+ ...associatedSoldermask ? [associatedSoldermask] : [],
8429
8652
  ...associatedSilkscreen ? [associatedSilkscreen] : [],
8430
8653
  ...associatedNotes ? [associatedNotes] : [],
8431
- ...associatedFabrication ? [associatedFabrication] : [],
8432
- ...maskWithCopperLayerForForeground ? [maskWithCopperLayerForForeground] : []
8654
+ ...associatedFabrication ? [associatedFabrication] : []
8433
8655
  ];
8656
+ const drillBeforeForeground = foregroundLayer === "drill" ? [] : ["drill"];
8657
+ const drillAfterForeground = foregroundLayer === "drill" ? ["drill"] : [];
8434
8658
  const order = [
8435
8659
  ...DEFAULT_DRAW_ORDER.filter((l) => !layersToShiftToTop.includes(l)),
8660
+ ...drillBeforeForeground,
8436
8661
  foregroundLayer,
8437
- ...maskWithCopperLayerForForeground ? [maskWithCopperLayerForForeground] : [],
8662
+ ...associatedSoldermask ? [associatedSoldermask] : [],
8663
+ ...drillAfterForeground,
8438
8664
  "edge_cuts",
8439
- "drill",
8440
8665
  ...associatedSilkscreen ? [associatedSilkscreen] : [],
8441
8666
  ...associatedNotes ? [associatedNotes] : [],
8442
8667
  ...associatedFabrication ? [associatedFabrication] : []
@@ -8660,7 +8885,8 @@ function drawPcbBoardElements({
8660
8885
  for (const element of pcbBoardElements) {
8661
8886
  drawer.drawElements([element], {
8662
8887
  layers,
8663
- drawSoldermask,
8888
+ drawSoldermask: false,
8889
+ drawBoardMaterial: drawSoldermask ?? false,
8664
8890
  minBoardOutlineStrokePx: 2
8665
8891
  });
8666
8892
  }
@@ -8736,7 +8962,7 @@ function drawPcbPanelElements({
8736
8962
  drawer.realToCanvasMat = realToCanvasMat;
8737
8963
  const pcbPanelElements = elements.filter(isPcbPanelElement);
8738
8964
  for (const element of pcbPanelElements) {
8739
- drawer.drawElements([element], { layers, drawSoldermask });
8965
+ drawer.drawElements([element], { layers });
8740
8966
  }
8741
8967
  }
8742
8968
 
@@ -9367,7 +9593,25 @@ function drawSoldermaskElementsForLayer({
9367
9593
  }) {
9368
9594
  const drawer = new CircuitToCanvasDrawer12(canvas);
9369
9595
  drawer.realToCanvasMat = realToCanvasMat;
9370
- drawer.drawElements(elements, { layers, drawSoldermask: true });
9596
+ const boards = elements.filter((element) => element.type === "pcb_board");
9597
+ if (boards.length <= 1) {
9598
+ drawer.drawElements(elements, {
9599
+ layers,
9600
+ drawSoldermask: true,
9601
+ drawBoardMaterial: false
9602
+ });
9603
+ } else {
9604
+ const nonBoardElements = elements.filter(
9605
+ (element) => element.type !== "pcb_board"
9606
+ );
9607
+ for (const board of boards) {
9608
+ drawer.drawElements([board, ...nonBoardElements], {
9609
+ layers,
9610
+ drawSoldermask: true,
9611
+ drawBoardMaterial: false
9612
+ });
9613
+ }
9614
+ }
9371
9615
  if (!primitives) return;
9372
9616
  const hoveredElementIds = /* @__PURE__ */ new Set();
9373
9617
  for (const primitive of primitives) {
@@ -9398,7 +9642,11 @@ function drawSoldermaskElementsForLayer({
9398
9642
  const hoverDrawer = new CircuitToCanvasDrawer12(canvas);
9399
9643
  hoverDrawer.configure({ colorOverrides: HOVER_SOLDERMASK_COLOR_MAP });
9400
9644
  hoverDrawer.realToCanvasMat = realToCanvasMat;
9401
- hoverDrawer.drawElements(hoveredElements, { layers, drawSoldermask: true });
9645
+ hoverDrawer.drawElements(hoveredElements, {
9646
+ layers,
9647
+ drawSoldermask: true,
9648
+ drawBoardMaterial: false
9649
+ });
9402
9650
  }
9403
9651
 
9404
9652
  // src/lib/draw-silkscreen.ts
@@ -9521,7 +9769,7 @@ function drawCourtyardElementsForLayer({
9521
9769
  }
9522
9770
 
9523
9771
  // src/components/CanvasPrimitiveRenderer.tsx
9524
- import { useEffect as useEffect4, useRef as useRef2 } from "react";
9772
+ import { useEffect as useEffect5, useRef as useRef3 } from "react";
9525
9773
  import { SuperGrid, toMMSI } from "react-supergrid";
9526
9774
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
9527
9775
  var orderedLayers = [
@@ -9559,10 +9807,10 @@ var CanvasPrimitiveRenderer = ({
9559
9807
  width = 500,
9560
9808
  height = 500
9561
9809
  }) => {
9562
- const canvasRefs = useRef2({});
9810
+ const canvasRefs = useRef3({});
9563
9811
  const selectedLayer = useGlobalStore((s) => s.selected_layer);
9564
9812
  const isShowingSolderMask = useGlobalStore((s) => s.is_showing_solder_mask);
9565
- useEffect4(() => {
9813
+ useEffect5(() => {
9566
9814
  if (!canvasRefs.current) return;
9567
9815
  if (Object.keys(canvasRefs.current).length === 0) return;
9568
9816
  const filteredCanvasRefs = Object.fromEntries(
@@ -9656,15 +9904,6 @@ var CanvasPrimitiveRenderer = ({
9656
9904
  primitives,
9657
9905
  drawSoldermask: isShowingSolderMask
9658
9906
  });
9659
- if (isShowingSolderMask) {
9660
- drawSoldermaskElementsForLayer({
9661
- canvas,
9662
- elements,
9663
- layers: [copperLayer],
9664
- realToCanvasMat: transform,
9665
- primitives
9666
- });
9667
- }
9668
9907
  }
9669
9908
  if (topCanvas) {
9670
9909
  drawPcbViaElementsForLayer({
@@ -9732,15 +9971,6 @@ var CanvasPrimitiveRenderer = ({
9732
9971
  layers: ["drill"],
9733
9972
  realToCanvasMat: transform
9734
9973
  });
9735
- if (isShowingSolderMask) {
9736
- drawSoldermaskElementsForLayer({
9737
- canvas: drillCanvas,
9738
- elements,
9739
- layers: ["drill"],
9740
- realToCanvasMat: transform,
9741
- primitives
9742
- });
9743
- }
9744
9974
  }
9745
9975
  const topSilkscreenCanvas = canvasRefs.current.top_silkscreen;
9746
9976
  if (topSilkscreenCanvas) {
@@ -10226,17 +10456,17 @@ function drawGraphicsToCanvas(graphics, target, options = {}) {
10226
10456
  }
10227
10457
 
10228
10458
  // src/components/DebugGraphicsOverlay.tsx
10229
- import { useEffect as useEffect5, useRef as useRef3 } from "react";
10459
+ import { useEffect as useEffect6, useRef as useRef4 } from "react";
10230
10460
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
10231
10461
  var DebugGraphicsOverlay = ({
10232
10462
  children,
10233
10463
  transform,
10234
10464
  debugGraphics
10235
10465
  }) => {
10236
- const canvasRef = useRef3(null);
10466
+ const canvasRef = useRef4(null);
10237
10467
  const [containerRef, { width, height }] = useMeasure_default();
10238
10468
  const is_showing_autorouting = useGlobalStore((s) => s.is_showing_autorouting);
10239
- useEffect5(() => {
10469
+ useEffect6(() => {
10240
10470
  if (!is_showing_autorouting) return;
10241
10471
  if (canvasRef.current && debugGraphics && width && height) {
10242
10472
  canvasRef.current.width = width;
@@ -10277,7 +10507,7 @@ var DebugGraphicsOverlay = ({
10277
10507
  };
10278
10508
 
10279
10509
  // src/components/WarningGraphicsOverlay.tsx
10280
- import { useEffect as useEffect6, useRef as useRef4 } from "react";
10510
+ import { useEffect as useEffect7, useRef as useRef5 } from "react";
10281
10511
  import { applyToPoint as applyToPoint7, identity as identity3 } from "transformation-matrix";
10282
10512
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
10283
10513
  var WarningGraphicsOverlay = ({
@@ -10286,8 +10516,8 @@ var WarningGraphicsOverlay = ({
10286
10516
  elements = []
10287
10517
  }) => {
10288
10518
  const [containerRef, { width, height }] = useMeasure_default();
10289
- const canvasRef = useRef4(null);
10290
- useEffect6(() => {
10519
+ const canvasRef = useRef5(null);
10520
+ useEffect7(() => {
10291
10521
  const canvas = canvasRef.current;
10292
10522
  if (!canvas || !width || !height) return;
10293
10523
  canvas.width = width;
@@ -10365,7 +10595,7 @@ var WarningGraphicsOverlay = ({
10365
10595
  };
10366
10596
 
10367
10597
  // src/components/DimensionOverlay.tsx
10368
- import { useCallback as useCallback3, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef5, useState as useState3 } from "react";
10598
+ import { useCallback as useCallback6, useEffect as useEffect8, useMemo as useMemo4, useRef as useRef6, useState as useState4 } from "react";
10369
10599
  import { applyToPoint as applyToPoint8, identity as identity4, inverse as inverse2 } from "transformation-matrix";
10370
10600
 
10371
10601
  // src/lib/util/get-primitive-bounding-box.ts
@@ -10767,24 +10997,24 @@ var DimensionOverlay = ({
10767
10997
  primitives = []
10768
10998
  }) => {
10769
10999
  if (!transform) transform = identity4();
10770
- const [dimensionToolVisible, setDimensionToolVisible] = useState3(false);
10771
- const [dimensionToolStretching, setDimensionToolStretching] = useState3(false);
10772
- const [measureToolArmed, setMeasureToolArmed] = useState3(false);
10773
- const [activeSnapIds, setActiveSnapIds] = useState3({
11000
+ const [dimensionToolVisible, setDimensionToolVisible] = useState4(false);
11001
+ const [dimensionToolStretching, setDimensionToolStretching] = useState4(false);
11002
+ const [measureToolArmed, setMeasureToolArmed] = useState4(false);
11003
+ const [activeSnapIds, setActiveSnapIds] = useState4({
10774
11004
  start: null,
10775
11005
  end: null
10776
11006
  });
10777
11007
  const isMouseOverContainer = useGlobalStore((s) => s.is_mouse_over_container);
10778
- const disarmMeasure = useCallback3(() => {
11008
+ const disarmMeasure = useCallback6(() => {
10779
11009
  if (measureToolArmed) {
10780
11010
  setMeasureToolArmed(false);
10781
11011
  window.dispatchEvent(new Event("disarm-dimension-tool"));
10782
11012
  }
10783
11013
  }, [measureToolArmed]);
10784
- const [dStart, setDStart] = useState3({ x: 0, y: 0 });
10785
- const [dEnd, setDEnd] = useState3({ x: 0, y: 0 });
10786
- const mousePosRef = useRef5({ x: 0, y: 0 });
10787
- const containerRef = useRef5(null);
11014
+ const [dStart, setDStart] = useState4({ x: 0, y: 0 });
11015
+ const [dEnd, setDEnd] = useState4({ x: 0, y: 0 });
11016
+ const mousePosRef = useRef6({ x: 0, y: 0 });
11017
+ const containerRef = useRef6(null);
10788
11018
  const container = containerRef.current;
10789
11019
  const containerBounds = container?.getBoundingClientRect();
10790
11020
  const elementBoundingBoxes = useMemo4(() => {
@@ -10864,7 +11094,7 @@ var DimensionOverlay = ({
10864
11094
  screenPoint: applyToPoint8(transform, snap.point)
10865
11095
  }));
10866
11096
  }, [snappingPoints, transform]);
10867
- const findSnap = useCallback3(
11097
+ const findSnap = useCallback6(
10868
11098
  (rwPoint) => {
10869
11099
  if (snappingPointsWithScreen.length === 0) {
10870
11100
  return { point: rwPoint, id: null };
@@ -10891,7 +11121,7 @@ var DimensionOverlay = ({
10891
11121
  },
10892
11122
  [snappingPointsWithScreen, transform]
10893
11123
  );
10894
- useEffect7(() => {
11124
+ useEffect8(() => {
10895
11125
  const down = (e) => {
10896
11126
  const target = e.target;
10897
11127
  if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
@@ -11235,7 +11465,7 @@ var DimensionOverlay = ({
11235
11465
  };
11236
11466
 
11237
11467
  // src/components/EditPlacementOverlay.tsx
11238
- import { useRef as useRef6, useState as useState4 } from "react";
11468
+ import { useRef as useRef7, useState as useState5 } from "react";
11239
11469
  import { applyToPoint as applyToPoint9, identity as identity5, inverse as inverse3 } from "transformation-matrix";
11240
11470
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
11241
11471
  var isInsideOf = (pcb_component, point, padding = 0) => {
@@ -11257,11 +11487,11 @@ var EditPlacementOverlay = ({
11257
11487
  onModifyEditEvent
11258
11488
  }) => {
11259
11489
  if (!transform) transform = identity5();
11260
- const containerRef = useRef6(null);
11261
- const [activePcbComponentId, setActivePcbComponent] = useState4(
11490
+ const containerRef = useRef7(null);
11491
+ const [activePcbComponentId, setActivePcbComponent] = useState5(
11262
11492
  null
11263
11493
  );
11264
- const [dragState, setDragState] = useState4(null);
11494
+ const [dragState, setDragState] = useState5(null);
11265
11495
  const isPcbComponentActive = activePcbComponentId !== null;
11266
11496
  const in_edit_mode = useGlobalStore((s) => s.in_edit_mode);
11267
11497
  const in_move_footprint_mode = useGlobalStore((s) => s.in_move_footprint_mode);
@@ -11377,7 +11607,7 @@ var EditPlacementOverlay = ({
11377
11607
  };
11378
11608
 
11379
11609
  // src/components/EditTraceHintOverlay.tsx
11380
- import { Fragment as Fragment2, useEffect as useEffect10, useRef as useRef7, useState as useState5 } from "react";
11610
+ import { Fragment as Fragment2, useEffect as useEffect11, useRef as useRef8, useState as useState6 } from "react";
11381
11611
  import {
11382
11612
  applyToPoint as applyToPoint10,
11383
11613
  identity as identity6,
@@ -11386,12 +11616,12 @@ import {
11386
11616
 
11387
11617
  // src/components/HotkeyActionMenu.tsx
11388
11618
  import { css } from "@emotion/css";
11389
- import { useEffect as useEffect9 } from "react";
11619
+ import { useEffect as useEffect10 } from "react";
11390
11620
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
11391
11621
  var HotkeyActionMenu = ({
11392
11622
  hotkeys
11393
11623
  }) => {
11394
- useEffect9(() => {
11624
+ useEffect10(() => {
11395
11625
  const handleKeyDown = (event) => {
11396
11626
  hotkeys.forEach((hotkey) => {
11397
11627
  if (event.key === hotkey.key) {
@@ -11500,12 +11730,12 @@ var EditTraceHintOverlay = ({
11500
11730
  onModifyEditEvent
11501
11731
  }) => {
11502
11732
  if (!transform) transform = identity6();
11503
- const containerRef = useRef7(null);
11733
+ const containerRef = useRef8(null);
11504
11734
  const containerBounds = containerRef.current?.getBoundingClientRect();
11505
- const [selectedElement, setSelectedElement] = useState5(null);
11735
+ const [selectedElement, setSelectedElement] = useState6(null);
11506
11736
  const toast = useToast();
11507
- const [dragState, setDragState] = useState5(null);
11508
- const [shouldCreateAsVia, setShouldCreateAsVia] = useState5(false);
11737
+ const [dragState, setDragState] = useState6(null);
11738
+ const [shouldCreateAsVia, setShouldCreateAsVia] = useState6(false);
11509
11739
  const isElementSelected = selectedElement !== null;
11510
11740
  const in_edit_trace_mode = useGlobalStore((s) => s.in_draw_trace_mode);
11511
11741
  const disabled = disabledProp || !in_edit_trace_mode;
@@ -11514,7 +11744,7 @@ var EditTraceHintOverlay = ({
11514
11744
  ogCenterScreen = applyToPoint10(transform, dragState?.originalCenter);
11515
11745
  dragEndScreen = applyToPoint10(transform, dragState?.dragEnd);
11516
11746
  }
11517
- useEffect10(() => {
11747
+ useEffect11(() => {
11518
11748
  if (!isElementSelected) return;
11519
11749
  function keyDown(e) {
11520
11750
  if (e.key === "Escape") {
@@ -11786,7 +12016,7 @@ var EditTraceHintOverlay = ({
11786
12016
  };
11787
12017
 
11788
12018
  // src/components/ErrorOverlay.tsx
11789
- import { useRef as useRef8, Fragment as Fragment3 } from "react";
12019
+ import { useRef as useRef9, Fragment as Fragment3 } from "react";
11790
12020
  import { css as css2 } from "@emotion/css";
11791
12021
  import { applyToPoint as applyToPoint11, identity as identity7 } from "transformation-matrix";
11792
12022
 
@@ -11945,7 +12175,7 @@ var ErrorOverlay = ({
11945
12175
  transform = identity7(),
11946
12176
  elements
11947
12177
  }) => {
11948
- const containerRef = useRef8(null);
12178
+ const containerRef = useRef9(null);
11949
12179
  const { isShowingDRCErrors, hoveredErrorId } = useGlobalStore((state) => ({
11950
12180
  isShowingDRCErrors: state.is_showing_drc_errors,
11951
12181
  hoveredErrorId: state.hovered_error_id
@@ -12311,11 +12541,11 @@ function ifSetsMatchExactly(set1, set2) {
12311
12541
  }
12312
12542
 
12313
12543
  // src/components/MouseElementTracker.tsx
12314
- import { useState as useState7, useMemo as useMemo5 } from "react";
12544
+ import { useState as useState8, useMemo as useMemo5 } from "react";
12315
12545
  import { applyToPoint as applyToPoint14, inverse as inverse5 } from "transformation-matrix";
12316
12546
 
12317
12547
  // src/components/ElementOverlayBox.tsx
12318
- import { useEffect as useEffect11, useState as useState6 } from "react";
12548
+ import { useEffect as useEffect12, useState as useState7 } from "react";
12319
12549
 
12320
12550
  // src/lib/get-trace-overlay-text.ts
12321
12551
  function getTraceOverlayInfo({
@@ -12433,9 +12663,9 @@ var HighlightedPrimitiveBoxWithText = ({
12433
12663
  mousePos,
12434
12664
  elements
12435
12665
  }) => {
12436
- const [finalState, setFinalState] = useState6(false);
12666
+ const [finalState, setFinalState] = useState7(false);
12437
12667
  const primitiveElement = primitive._element;
12438
- useEffect11(() => {
12668
+ useEffect12(() => {
12439
12669
  setTimeout(() => {
12440
12670
  setFinalState(true);
12441
12671
  }, 100);
@@ -13359,8 +13589,8 @@ var MouseElementTracker = ({
13359
13589
  primitives,
13360
13590
  onMouseHoverOverPrimitives
13361
13591
  }) => {
13362
- const [mousedPrimitives, setMousedPrimitives] = useState7([]);
13363
- const [mousePos, setMousePos] = useState7({ x: 0, y: 0 });
13592
+ const [mousedPrimitives, setMousedPrimitives] = useState8([]);
13593
+ const [mousePos, setMousePos] = useState8({ x: 0, y: 0 });
13364
13594
  const [containerRef, { width, height }] = useMeasure_default();
13365
13595
  const highlightedPrimitives = useMemo5(() => {
13366
13596
  const highlightedPrimitives2 = [];
@@ -13515,7 +13745,7 @@ var MouseElementTracker = ({
13515
13745
  // src/components/PcbGroupOverlay.tsx
13516
13746
  import { applyToPoint as applyToPoint15 } from "transformation-matrix";
13517
13747
  import { identity as identity8 } from "transformation-matrix";
13518
- import { useRef as useRef9, useEffect as useEffect12 } from "react";
13748
+ import { useRef as useRef10, useEffect as useEffect13 } from "react";
13519
13749
  import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
13520
13750
  var GROUP_COLORS = [
13521
13751
  "rgb(255, 100, 100)",
@@ -13536,7 +13766,7 @@ var PcbGroupOverlay = ({
13536
13766
  hoveredComponentIds = []
13537
13767
  }) => {
13538
13768
  const [containerRef, { width, height }] = useMeasure_default();
13539
- const canvasRef = useRef9(null);
13769
+ const canvasRef = useRef10(null);
13540
13770
  const {
13541
13771
  is_showing_pcb_groups,
13542
13772
  pcb_group_view_mode,
@@ -13546,7 +13776,7 @@ var PcbGroupOverlay = ({
13546
13776
  pcb_group_view_mode: s.pcb_group_view_mode,
13547
13777
  is_showing_group_anchor_offsets: s.is_showing_group_anchor_offsets
13548
13778
  }));
13549
- useEffect12(() => {
13779
+ useEffect13(() => {
13550
13780
  const canvas = canvasRef.current;
13551
13781
  if (!canvas || !width || !height) return;
13552
13782
  canvas.width = width;
@@ -13880,10 +14110,10 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
13880
14110
 
13881
14111
  // src/components/ToolbarOverlay.tsx
13882
14112
  import {
13883
- useEffect as useEffect15,
13884
- useState as useState9,
13885
- useCallback as useCallback4,
13886
- useRef as useRef11,
14113
+ useEffect as useEffect16,
14114
+ useState as useState10,
14115
+ useCallback as useCallback8,
14116
+ useRef as useRef13,
13887
14117
  useLayoutEffect as useLayoutEffect2
13888
14118
  } from "react";
13889
14119
  import { css as css3 } from "@emotion/css";
@@ -13891,7 +14121,7 @@ import { css as css3 } from "@emotion/css";
13891
14121
  // package.json
13892
14122
  var package_default = {
13893
14123
  name: "@tscircuit/pcb-viewer",
13894
- version: "1.11.335",
14124
+ version: "1.11.337",
13895
14125
  main: "dist/index.js",
13896
14126
  type: "module",
13897
14127
  repository: "tscircuit/pcb-viewer",
@@ -13945,7 +14175,7 @@ var package_default = {
13945
14175
  "@tscircuit/math-utils": "^0.0.29",
13946
14176
  "@vitejs/plugin-react": "^5.0.2",
13947
14177
  "circuit-json": "^0.0.374",
13948
- "circuit-to-canvas": "^0.0.69",
14178
+ "circuit-to-canvas": "^0.0.72",
13949
14179
  "circuit-to-svg": "^0.0.323",
13950
14180
  color: "^4.2.3",
13951
14181
  "react-supergrid": "^1.0.10",
@@ -13956,20 +14186,20 @@ var package_default = {
13956
14186
  };
13957
14187
 
13958
14188
  // src/hooks/useHotKey.ts
13959
- import { useEffect as useEffect13, useRef as useRef10 } from "react";
14189
+ import { useEffect as useEffect14, useRef as useRef11 } from "react";
13960
14190
  var useHotKey = (key, onUse, containerRef) => {
13961
14191
  const isMouseOverContainer = useGlobalStore(
13962
14192
  (s) => s.is_mouse_over_container
13963
14193
  );
13964
- const isMouseOverContainerRef = useRef10(isMouseOverContainer);
13965
- const onUseRef = useRef10(onUse);
13966
- useEffect13(() => {
14194
+ const isMouseOverContainerRef = useRef11(isMouseOverContainer);
14195
+ const onUseRef = useRef11(onUse);
14196
+ useEffect14(() => {
13967
14197
  isMouseOverContainerRef.current = isMouseOverContainer;
13968
14198
  }, [isMouseOverContainer]);
13969
- useEffect13(() => {
14199
+ useEffect14(() => {
13970
14200
  onUseRef.current = onUse;
13971
14201
  }, [onUse]);
13972
- useEffect13(() => {
14202
+ useEffect14(() => {
13973
14203
  if (!key) return;
13974
14204
  const handleKeyDown = (event) => {
13975
14205
  const target = event.target;
@@ -13997,10 +14227,10 @@ var useHotKey = (key, onUse, containerRef) => {
13997
14227
  };
13998
14228
 
13999
14229
  // src/hooks/useIsSmallScreen.ts
14000
- import { useEffect as useEffect14, useState as useState8 } from "react";
14230
+ import { useEffect as useEffect15, useState as useState9 } from "react";
14001
14231
  var useIsSmallScreen = () => {
14002
- const [isSmallScreen, setIsSmallScreen] = useState8(false);
14003
- useEffect14(() => {
14232
+ const [isSmallScreen, setIsSmallScreen] = useState9(false);
14233
+ useEffect15(() => {
14004
14234
  const checkIsMobile = () => {
14005
14235
  setIsSmallScreen(window.innerWidth <= 768);
14006
14236
  };
@@ -14011,9 +14241,44 @@ var useIsSmallScreen = () => {
14011
14241
  return isSmallScreen;
14012
14242
  };
14013
14243
 
14244
+ // src/hooks/useMobileTouch.ts
14245
+ import {
14246
+ useCallback as useCallback7,
14247
+ useRef as useRef12
14248
+ } from "react";
14249
+ var useMobileTouch = (onClick, options = { stopPropagation: true }) => {
14250
+ const lastInteractionRef = useRef12(0);
14251
+ const handleInteraction = useCallback7(
14252
+ (e) => {
14253
+ if (options.stopPropagation) {
14254
+ e.stopPropagation();
14255
+ }
14256
+ const now = Date.now();
14257
+ if (now - lastInteractionRef.current < 300) return;
14258
+ lastInteractionRef.current = now;
14259
+ onClick?.(e);
14260
+ },
14261
+ [onClick, options.stopPropagation]
14262
+ );
14263
+ const onTouchEnd = useCallback7(
14264
+ (e) => {
14265
+ e.preventDefault();
14266
+ handleInteraction(e);
14267
+ },
14268
+ [handleInteraction]
14269
+ );
14270
+ const style = { touchAction: "manipulation" };
14271
+ return {
14272
+ onClick: handleInteraction,
14273
+ onTouchEnd,
14274
+ style
14275
+ };
14276
+ };
14277
+
14014
14278
  // src/components/ToolbarOverlay.tsx
14015
14279
  import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
14016
14280
  var LayerButton = ({ name, selected, onClick }) => {
14281
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14017
14282
  return /* @__PURE__ */ jsxs14(
14018
14283
  "div",
14019
14284
  {
@@ -14028,7 +14293,8 @@ var LayerButton = ({ name, selected, onClick }) => {
14028
14293
  background-color: rgba(255, 255, 255, 0.1);
14029
14294
  }
14030
14295
  `,
14031
- onClick,
14296
+ ...touchHandlers,
14297
+ style: touchStyle,
14032
14298
  children: [
14033
14299
  /* @__PURE__ */ jsx20("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
14034
14300
  /* @__PURE__ */ jsx20(
@@ -14051,43 +14317,40 @@ var ToolbarButton = ({
14051
14317
  isSmallScreen,
14052
14318
  onClick,
14053
14319
  ...props
14054
- }) => /* @__PURE__ */ jsx20(
14055
- "div",
14056
- {
14057
- ...props,
14058
- onClick,
14059
- onTouchEnd: (e) => {
14060
- e.preventDefault();
14061
- e.stopPropagation();
14062
- if (onClick) {
14063
- onClick(e);
14064
- }
14065
- },
14066
- style: {
14067
- backgroundColor: "#1F1F1F",
14068
- border: "1px solid #666",
14069
- margin: 0,
14070
- padding: 4,
14071
- paddingLeft: isSmallScreen ? 8 : 6,
14072
- paddingRight: isSmallScreen ? 8 : 6,
14073
- borderRadius: 2,
14074
- color: "#eee",
14075
- cursor: "pointer",
14076
- fontSize: 12,
14077
- height: "fit-content",
14078
- touchAction: "manipulation",
14079
- userSelect: "none",
14080
- whiteSpace: "nowrap",
14081
- ...props.style
14082
- },
14083
- children
14084
- }
14085
- );
14320
+ }) => {
14321
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14322
+ return /* @__PURE__ */ jsx20(
14323
+ "div",
14324
+ {
14325
+ ...props,
14326
+ ...touchHandlers,
14327
+ style: {
14328
+ backgroundColor: "#1F1F1F",
14329
+ border: "1px solid #666",
14330
+ margin: 0,
14331
+ padding: 4,
14332
+ paddingLeft: isSmallScreen ? 8 : 6,
14333
+ paddingRight: isSmallScreen ? 8 : 6,
14334
+ borderRadius: 2,
14335
+ color: "#eee",
14336
+ cursor: "pointer",
14337
+ fontSize: 12,
14338
+ height: "fit-content",
14339
+ userSelect: "none",
14340
+ whiteSpace: "nowrap",
14341
+ ...touchStyle,
14342
+ ...props.style
14343
+ },
14344
+ children
14345
+ }
14346
+ );
14347
+ };
14086
14348
  var CheckboxMenuItem = ({
14087
14349
  label,
14088
14350
  checked,
14089
14351
  onClick
14090
14352
  }) => {
14353
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14091
14354
  return /* @__PURE__ */ jsxs14(
14092
14355
  "div",
14093
14356
  {
@@ -14105,15 +14368,8 @@ var CheckboxMenuItem = ({
14105
14368
  background-color: rgba(255, 255, 255, 0.1);
14106
14369
  }
14107
14370
  `,
14108
- onClick: (e) => {
14109
- e.stopPropagation();
14110
- onClick();
14111
- },
14112
- onTouchEnd: (e) => {
14113
- e.preventDefault();
14114
- e.stopPropagation();
14115
- onClick();
14116
- },
14371
+ ...touchHandlers,
14372
+ style: touchStyle,
14117
14373
  children: [
14118
14374
  /* @__PURE__ */ jsx20("input", { type: "checkbox", checked, onChange: () => {
14119
14375
  }, readOnly: true }),
@@ -14123,6 +14379,7 @@ var CheckboxMenuItem = ({
14123
14379
  );
14124
14380
  };
14125
14381
  var RadioMenuItem = ({ label, checked, onClick }) => {
14382
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14126
14383
  return /* @__PURE__ */ jsxs14(
14127
14384
  "div",
14128
14385
  {
@@ -14140,15 +14397,8 @@ var RadioMenuItem = ({ label, checked, onClick }) => {
14140
14397
  background-color: rgba(255, 255, 255, 0.1);
14141
14398
  }
14142
14399
  `,
14143
- onClick: (e) => {
14144
- e.stopPropagation();
14145
- onClick();
14146
- },
14147
- onTouchEnd: (e) => {
14148
- e.preventDefault();
14149
- e.stopPropagation();
14150
- onClick();
14151
- },
14400
+ ...touchHandlers,
14401
+ style: touchStyle,
14152
14402
  children: [
14153
14403
  /* @__PURE__ */ jsx20("input", { type: "radio", checked, onChange: () => {
14154
14404
  }, readOnly: true }),
@@ -14157,6 +14407,39 @@ var RadioMenuItem = ({ label, checked, onClick }) => {
14157
14407
  }
14158
14408
  );
14159
14409
  };
14410
+ var CopyErrorButton = ({
14411
+ errorId,
14412
+ errorMessage,
14413
+ copiedErrorId,
14414
+ onCopy
14415
+ }) => {
14416
+ const { style: touchStyle, ...touchHandlers } = useMobileTouch(
14417
+ () => onCopy(errorMessage, errorId)
14418
+ );
14419
+ return /* @__PURE__ */ jsx20(
14420
+ "button",
14421
+ {
14422
+ type: "button",
14423
+ "aria-label": copiedErrorId === errorId ? "Error message copied" : "Copy error message",
14424
+ style: {
14425
+ position: "absolute",
14426
+ top: 12,
14427
+ right: 16,
14428
+ cursor: "pointer",
14429
+ color: "#888",
14430
+ fontSize: 16,
14431
+ background: "none",
14432
+ border: "none",
14433
+ padding: 0,
14434
+ display: "flex",
14435
+ alignItems: "center",
14436
+ ...touchStyle
14437
+ },
14438
+ ...touchHandlers,
14439
+ 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" }) })
14440
+ }
14441
+ );
14442
+ };
14160
14443
  var ToolbarOverlay = ({ children, elements }) => {
14161
14444
  const isSmallScreen = useIsSmallScreen();
14162
14445
  const {
@@ -14209,13 +14492,15 @@ var ToolbarOverlay = ({ children, elements }) => {
14209
14492
  setPcbGroupViewMode: s.setPcbGroupViewMode,
14210
14493
  setHoveredErrorId: s.setHoveredErrorId
14211
14494
  }));
14212
- const [isViewMenuOpen, setViewMenuOpen] = useState9(false);
14213
- const [isLayerMenuOpen, setLayerMenuOpen] = useState9(false);
14214
- const [isErrorsOpen, setErrorsOpen] = useState9(false);
14215
- const [measureToolArmed, setMeasureToolArmed] = useState9(false);
14216
- const errorElementsRef = useRef11(/* @__PURE__ */ new Map());
14217
- const arrowElementsRef = useRef11(/* @__PURE__ */ new Map());
14218
- useEffect15(() => {
14495
+ const [isViewMenuOpen, setViewMenuOpen] = useState10(false);
14496
+ const [isLayerMenuOpen, setLayerMenuOpen] = useState10(false);
14497
+ const [isErrorsOpen, setErrorsOpen] = useState10(false);
14498
+ const [measureToolArmed, setMeasureToolArmed] = useState10(false);
14499
+ const [copiedErrorId, setCopiedErrorId] = useState10(null);
14500
+ const [, copyToClipboard] = useCopyToClipboard_default();
14501
+ const errorElementsRef = useRef13(/* @__PURE__ */ new Map());
14502
+ const arrowElementsRef = useRef13(/* @__PURE__ */ new Map());
14503
+ useEffect16(() => {
14219
14504
  const arm = () => setMeasureToolArmed(true);
14220
14505
  const disarm = () => setMeasureToolArmed(false);
14221
14506
  window.addEventListener("arm-dimension-tool", arm);
@@ -14235,8 +14520,8 @@ var ToolbarOverlay = ({ children, elements }) => {
14235
14520
  "bottom"
14236
14521
  ];
14237
14522
  const processedLayers = availableLayers;
14238
- const hasRunInitialMouseCheck = useRef11(false);
14239
- const hotkeyBoundaryRef = useRef11(null);
14523
+ const hasRunInitialMouseCheck = useRef13(false);
14524
+ const hotkeyBoundaryRef = useRef13(null);
14240
14525
  const hotKeyCallbacks = {
14241
14526
  "1": availableLayers[0] ? () => selectLayer(availableLayers[0]) : () => {
14242
14527
  },
@@ -14281,25 +14566,25 @@ var ToolbarOverlay = ({ children, elements }) => {
14281
14566
  document.removeEventListener("mousemove", checkMousePosition);
14282
14567
  };
14283
14568
  }, [setIsMouseOverContainer]);
14284
- const handleMouseEnter = useCallback4(() => {
14569
+ const handleMouseEnter = useCallback8(() => {
14285
14570
  setIsMouseOverContainer(true);
14286
14571
  }, [setIsMouseOverContainer]);
14287
- const handleMouseMove = useCallback4(() => {
14572
+ const handleMouseMove = useCallback8(() => {
14288
14573
  if (!isMouseOverContainer) {
14289
14574
  setIsMouseOverContainer(true);
14290
14575
  }
14291
14576
  }, [isMouseOverContainer, setIsMouseOverContainer]);
14292
- const handleMouseLeave = useCallback4(() => {
14577
+ const handleMouseLeave = useCallback8(() => {
14293
14578
  setIsMouseOverContainer(false);
14294
14579
  setLayerMenuOpen(false);
14295
14580
  setViewMenuOpen(false);
14296
14581
  setErrorsOpen(false);
14297
14582
  setHoveredErrorId(null);
14298
14583
  }, [setIsMouseOverContainer, setHoveredErrorId]);
14299
- const handleLayerMenuToggle = useCallback4(() => {
14584
+ const handleLayerMenuToggle = useCallback8(() => {
14300
14585
  setLayerMenuOpen(!isLayerMenuOpen);
14301
14586
  }, [isLayerMenuOpen]);
14302
- const handleErrorsToggle = useCallback4(() => {
14587
+ const handleErrorsToggle = useCallback8(() => {
14303
14588
  const newErrorsOpen = !isErrorsOpen;
14304
14589
  setErrorsOpen(newErrorsOpen);
14305
14590
  if (newErrorsOpen) {
@@ -14309,20 +14594,20 @@ var ToolbarOverlay = ({ children, elements }) => {
14309
14594
  setHoveredErrorId(null);
14310
14595
  }
14311
14596
  }, [isErrorsOpen, setHoveredErrorId]);
14312
- const handleEditTraceToggle = useCallback4(() => {
14597
+ const handleEditTraceToggle = useCallback8(() => {
14313
14598
  setEditMode(editModes.in_draw_trace_mode ? "off" : "draw_trace");
14314
14599
  }, [editModes.in_draw_trace_mode, setEditMode]);
14315
- const handleMoveComponentToggle = useCallback4(() => {
14600
+ const handleMoveComponentToggle = useCallback8(() => {
14316
14601
  setEditMode(editModes.in_move_footprint_mode ? "off" : "move_footprint");
14317
14602
  }, [editModes.in_move_footprint_mode, setEditMode]);
14318
- const handleRatsNestToggle = useCallback4(() => {
14603
+ const handleRatsNestToggle = useCallback8(() => {
14319
14604
  setIsShowingRatsNest(!viewSettings.is_showing_rats_nest);
14320
14605
  }, [viewSettings.is_showing_rats_nest, setIsShowingRatsNest]);
14321
- const handleMeasureToolClick = useCallback4(() => {
14606
+ const handleMeasureToolClick = useCallback8(() => {
14322
14607
  setMeasureToolArmed(true);
14323
14608
  window.dispatchEvent(new Event("arm-dimension-tool"));
14324
14609
  }, []);
14325
- const handleViewMenuToggle = useCallback4(() => {
14610
+ const handleViewMenuToggle = useCallback8(() => {
14326
14611
  const newViewMenuOpen = !isViewMenuOpen;
14327
14612
  setViewMenuOpen(newViewMenuOpen);
14328
14613
  if (newViewMenuOpen) {
@@ -14567,7 +14852,7 @@ var ToolbarOverlay = ({ children, elements }) => {
14567
14852
  ]
14568
14853
  }
14569
14854
  ),
14570
- /* @__PURE__ */ jsx20(
14855
+ /* @__PURE__ */ jsxs14(
14571
14856
  "div",
14572
14857
  {
14573
14858
  ref: (el) => {
@@ -14578,30 +14863,48 @@ var ToolbarOverlay = ({ children, elements }) => {
14578
14863
  display: "none",
14579
14864
  padding: "12px 16px",
14580
14865
  backgroundColor: "#1a1a1a",
14581
- borderTop: "1px solid #444"
14866
+ borderTop: "1px solid #444",
14867
+ position: "relative"
14582
14868
  },
14583
- children: /* @__PURE__ */ jsx20(
14584
- "div",
14585
- {
14586
- style: {
14587
- fontSize: isSmallScreen ? "11px" : "12px",
14588
- color: "#ccc",
14589
- lineHeight: 1.5,
14590
- wordWrap: "break-word",
14591
- overflowWrap: "break-word",
14592
- hyphens: "auto",
14593
- userSelect: "text"
14594
- },
14595
- onMouseDown: (event) => event.stopPropagation(),
14596
- onClick: (event) => event.stopPropagation(),
14597
- children: e.message
14598
- }
14599
- )
14869
+ children: [
14870
+ /* @__PURE__ */ jsx20(
14871
+ "div",
14872
+ {
14873
+ style: {
14874
+ fontSize: isSmallScreen ? "11px" : "12px",
14875
+ color: "#ccc",
14876
+ lineHeight: 1.5,
14877
+ wordWrap: "break-word",
14878
+ overflowWrap: "break-word",
14879
+ hyphens: "auto",
14880
+ userSelect: "text",
14881
+ paddingRight: 30
14882
+ // Space for the copy icon
14883
+ },
14884
+ onMouseDown: (event) => event.stopPropagation(),
14885
+ onClick: (event) => event.stopPropagation(),
14886
+ children: e.message
14887
+ }
14888
+ ),
14889
+ /* @__PURE__ */ jsx20(
14890
+ CopyErrorButton,
14891
+ {
14892
+ errorId,
14893
+ errorMessage: e.message,
14894
+ copiedErrorId,
14895
+ onCopy: (msg, id) => {
14896
+ copyToClipboard(msg);
14897
+ setCopiedErrorId(id);
14898
+ setTimeout(() => setCopiedErrorId(null), 2e3);
14899
+ }
14900
+ }
14901
+ )
14902
+ ]
14600
14903
  }
14601
14904
  )
14602
14905
  ]
14603
14906
  },
14604
- i
14907
+ errorId
14605
14908
  );
14606
14909
  })
14607
14910
  }
@@ -14816,11 +15119,11 @@ var CanvasElementsRenderer = (props) => {
14816
15119
  );
14817
15120
  return [primitivesWithoutInteractionMetadata2, connectivityMap2];
14818
15121
  }, [elementsToRender, props.elements]);
14819
- const [hoverState, setHoverState] = useState10({
15122
+ const [hoverState, setHoverState] = useState11({
14820
15123
  drawingObjectIdsWithMouseOver: /* @__PURE__ */ new Set(),
14821
15124
  primitiveIdsInMousedOverNet: []
14822
15125
  });
14823
- const [hoveredComponentIds, setHoveredComponentIds] = useState10([]);
15126
+ const [hoveredComponentIds, setHoveredComponentIds] = useState11([]);
14824
15127
  const errorRelatedIds = useMemo7(() => {
14825
15128
  if (!hoveredErrorId) return [];
14826
15129
  const errorElements = elements.filter(
@@ -14851,7 +15154,7 @@ var CanvasElementsRenderer = (props) => {
14851
15154
  primitiveIdsInMousedOverNet: combinedPrimitiveIds
14852
15155
  });
14853
15156
  }, [primitivesWithoutInteractionMetadata, hoverState, errorRelatedIds]);
14854
- const onMouseOverPrimitives = useCallback5(
15157
+ const onMouseOverPrimitives = useCallback9(
14855
15158
  (primitivesHoveredOver) => {
14856
15159
  const primitiveIdsInMousedOverNet = [];
14857
15160
  for (const primitive of primitivesHoveredOver) {
@@ -15027,11 +15330,11 @@ var PCBViewer = ({
15027
15330
  clickToInteractEnabled = false,
15028
15331
  disablePcbGroups = false
15029
15332
  }) => {
15030
- const [isInteractionEnabled, setIsInteractionEnabled] = useState11(
15333
+ const [isInteractionEnabled, setIsInteractionEnabled] = useState12(
15031
15334
  !clickToInteractEnabled
15032
15335
  );
15033
15336
  const [ref, refDimensions] = useMeasure_default();
15034
- const [transform, setTransformInternal] = useState11(defaultTransform);
15337
+ const [transform, setTransformInternal] = useState12(defaultTransform);
15035
15338
  const {
15036
15339
  ref: transformRef,
15037
15340
  setTransform,
@@ -15041,10 +15344,10 @@ var PCBViewer = ({
15041
15344
  onSetTransform: setTransformInternal,
15042
15345
  enabled: isInteractionEnabled
15043
15346
  });
15044
- let [editEvents, setEditEvents] = useState11([]);
15347
+ let [editEvents, setEditEvents] = useState12([]);
15045
15348
  editEvents = editEventsProp ?? editEvents;
15046
- const initialRenderCompleted = useRef12(false);
15047
- const touchStartRef = useRef12(null);
15349
+ const initialRenderCompleted = useRef14(false);
15350
+ const touchStartRef = useRef14(null);
15048
15351
  const circuitJsonKey = useMemo8(
15049
15352
  () => calculateCircuitJsonKey(circuitJson),
15050
15353
  [circuitJson]
@@ -15070,7 +15373,7 @@ var PCBViewer = ({
15070
15373
  setTransform(targetTransform);
15071
15374
  return;
15072
15375
  };
15073
- useEffect16(() => {
15376
+ useEffect17(() => {
15074
15377
  if (!refDimensions?.width) return;
15075
15378
  if (!circuitJson) return;
15076
15379
  if (circuitJson.length === 0) return;
@@ -15079,7 +15382,7 @@ var PCBViewer = ({
15079
15382
  initialRenderCompleted.current = true;
15080
15383
  }
15081
15384
  }, [circuitJson, refDimensions]);
15082
- useEffect16(() => {
15385
+ useEffect17(() => {
15083
15386
  if (initialRenderCompleted.current === true) {
15084
15387
  resetTransform();
15085
15388
  }