@tscircuit/pcb-viewer 1.11.312 → 1.11.314

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
@@ -6451,7 +6451,7 @@ var ToastContainer = () => {
6451
6451
  };
6452
6452
 
6453
6453
  // src/PCBViewer.tsx
6454
- import { useEffect as useEffect16, useMemo as useMemo8, useRef as useRef11, useState as useState11 } from "react";
6454
+ import { useEffect as useEffect16, useMemo as useMemo8, useRef as useRef12, useState as useState11 } from "react";
6455
6455
 
6456
6456
  // node_modules/react-use/esm/misc/util.js
6457
6457
  var noop = function() {
@@ -7842,58 +7842,6 @@ var convertElementToPrimitives = (element, allElements) => {
7842
7842
  _parent_source_component
7843
7843
  });
7844
7844
  }
7845
- case "pcb_copper_pour": {
7846
- const pour = element;
7847
- switch (pour.shape) {
7848
- case "rect": {
7849
- const { center, width, height, layer, rotation } = pour;
7850
- return [
7851
- {
7852
- _pcb_drawing_object_id: getNewPcbDrawingObjectId(
7853
- "pcb_copper_pour_rect"
7854
- ),
7855
- pcb_drawing_type: "rect",
7856
- x: center.x,
7857
- y: center.y,
7858
- w: width,
7859
- h: height,
7860
- layer,
7861
- _element: element,
7862
- ccw_rotation: rotation
7863
- }
7864
- ];
7865
- }
7866
- case "polygon": {
7867
- const { points, layer } = pour;
7868
- return [
7869
- {
7870
- _pcb_drawing_object_id: getNewPcbDrawingObjectId(
7871
- "pcb_copper_pour_polygon"
7872
- ),
7873
- pcb_drawing_type: "polygon",
7874
- points: normalizePolygonPoints(points),
7875
- layer,
7876
- _element: element
7877
- }
7878
- ];
7879
- }
7880
- case "brep": {
7881
- const { brep_shape, layer } = pour;
7882
- return [
7883
- {
7884
- _pcb_drawing_object_id: getNewPcbDrawingObjectId(
7885
- "pcb_copper_pour_brep"
7886
- ),
7887
- pcb_drawing_type: "polygon_with_arcs",
7888
- brep_shape,
7889
- layer,
7890
- _element: element
7891
- }
7892
- ];
7893
- }
7894
- }
7895
- return [];
7896
- }
7897
7845
  }
7898
7846
  return [];
7899
7847
  };
@@ -9352,6 +9300,24 @@ function drawPcbViaElementsForLayer({
9352
9300
  }
9353
9301
  }
9354
9302
 
9303
+ // src/lib/draw-copper-pour.ts
9304
+ import { CircuitToCanvasDrawer as CircuitToCanvasDrawer11 } from "circuit-to-canvas";
9305
+ function isCopperPourElement(element) {
9306
+ return element.type === "pcb_copper_pour";
9307
+ }
9308
+ function drawCopperPourElementsForLayer({
9309
+ canvas,
9310
+ elements,
9311
+ layers,
9312
+ realToCanvasMat
9313
+ }) {
9314
+ const copperPourElements = elements.filter(isCopperPourElement);
9315
+ if (copperPourElements.length === 0) return;
9316
+ const drawer = new CircuitToCanvasDrawer11(canvas);
9317
+ drawer.realToCanvasMat = realToCanvasMat;
9318
+ drawer.drawElements(copperPourElements, { layers });
9319
+ }
9320
+
9355
9321
  // src/components/CanvasPrimitiveRenderer.tsx
9356
9322
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
9357
9323
  var orderedLayers = [
@@ -9465,6 +9431,22 @@ var CanvasPrimitiveRenderer = ({
9465
9431
  primitives
9466
9432
  });
9467
9433
  }
9434
+ if (topCanvas) {
9435
+ drawCopperPourElementsForLayer({
9436
+ canvas: topCanvas,
9437
+ elements,
9438
+ layers: ["top_copper"],
9439
+ realToCanvasMat: transform
9440
+ });
9441
+ }
9442
+ if (bottomCanvas) {
9443
+ drawCopperPourElementsForLayer({
9444
+ canvas: bottomCanvas,
9445
+ elements,
9446
+ layers: ["bottom_copper"],
9447
+ realToCanvasMat: transform
9448
+ });
9449
+ }
9468
9450
  const topSilkscreenCanvas = canvasRefs.current.top_silkscreen;
9469
9451
  if (topSilkscreenCanvas) {
9470
9452
  drawSilkscreenElementsForLayer(
@@ -10473,6 +10455,7 @@ var DimensionOverlay = ({
10473
10455
  start: null,
10474
10456
  end: null
10475
10457
  });
10458
+ const isMouseOverContainer = useGlobalStore((s) => s.is_mouse_over_container);
10476
10459
  const disarmMeasure = useCallback3(() => {
10477
10460
  if (measureToolArmed) {
10478
10461
  setMeasureToolArmed(false);
@@ -10590,8 +10573,9 @@ var DimensionOverlay = ({
10590
10573
  [snappingPointsWithScreen, transform]
10591
10574
  );
10592
10575
  useEffect7(() => {
10593
- const container2 = containerRef.current;
10594
10576
  const down = (e) => {
10577
+ const containerHasFocus = containerRef.current?.contains(document.activeElement) || document.activeElement === containerRef.current;
10578
+ if (!isMouseOverContainer && !containerHasFocus) return;
10595
10579
  if (e.key === "d") {
10596
10580
  const snap = findSnap({
10597
10581
  x: mousePosRef.current.x,
@@ -10620,34 +10604,14 @@ var DimensionOverlay = ({
10620
10604
  const armMeasure = () => {
10621
10605
  setMeasureToolArmed(true);
10622
10606
  };
10623
- const addKeyListener = () => {
10624
- if (container2) {
10625
- window.addEventListener("keydown", down);
10626
- }
10627
- };
10628
- const removeKeyListener = () => {
10629
- if (container2) {
10630
- window.removeEventListener("keydown", down);
10631
- }
10632
- };
10607
+ window.addEventListener("keydown", down);
10633
10608
  window.addEventListener("arm-dimension-tool", armMeasure);
10634
- if (container2) {
10635
- container2.addEventListener("focus", addKeyListener);
10636
- container2.addEventListener("blur", removeKeyListener);
10637
- container2.addEventListener("mouseenter", addKeyListener);
10638
- container2.addEventListener("mouseleave", removeKeyListener);
10639
- }
10640
10609
  return () => {
10610
+ window.removeEventListener("keydown", down);
10641
10611
  window.removeEventListener("arm-dimension-tool", armMeasure);
10642
10612
  disarmMeasure();
10643
- if (container2) {
10644
- container2.removeEventListener("focus", addKeyListener);
10645
- container2.removeEventListener("blur", removeKeyListener);
10646
- container2.removeEventListener("mouseenter", addKeyListener);
10647
- container2.removeEventListener("mouseleave", removeKeyListener);
10648
- }
10649
10613
  };
10650
- }, [containerRef, dimensionToolVisible, disarmMeasure, findSnap]);
10614
+ }, [isMouseOverContainer, dimensionToolVisible, disarmMeasure, findSnap]);
10651
10615
  const screenDStart = applyToPoint8(transform, dStart);
10652
10616
  const screenDEnd = applyToPoint8(transform, dEnd);
10653
10617
  const arrowScreenBounds = {
@@ -10674,8 +10638,9 @@ var DimensionOverlay = ({
10674
10638
  "div",
10675
10639
  {
10676
10640
  ref: containerRef,
10641
+ "data-pcb-viewer": true,
10677
10642
  tabIndex: 0,
10678
- style: { position: "relative" },
10643
+ style: { position: "relative", outline: "none" },
10679
10644
  onMouseEnter: () => {
10680
10645
  if (focusOnHover && containerRef.current) {
10681
10646
  containerRef.current.focus();
@@ -10686,6 +10651,11 @@ var DimensionOverlay = ({
10686
10651
  containerRef.current.blur();
10687
10652
  }
10688
10653
  },
10654
+ onClick: () => {
10655
+ if (containerRef.current) {
10656
+ containerRef.current.focus();
10657
+ }
10658
+ },
10689
10659
  onMouseMove: (e) => {
10690
10660
  const rect = e.currentTarget.getBoundingClientRect();
10691
10661
  const x = e.clientX - rect.left;
@@ -13583,13 +13553,19 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
13583
13553
  };
13584
13554
 
13585
13555
  // src/components/ToolbarOverlay.tsx
13586
- import { useEffect as useEffect15, useState as useState9, useCallback as useCallback4, useRef as useRef10 } from "react";
13556
+ import {
13557
+ useEffect as useEffect15,
13558
+ useState as useState9,
13559
+ useCallback as useCallback4,
13560
+ useRef as useRef11,
13561
+ useLayoutEffect as useLayoutEffect2
13562
+ } from "react";
13587
13563
  import { css as css3 } from "@emotion/css";
13588
13564
 
13589
13565
  // package.json
13590
13566
  var package_default = {
13591
13567
  name: "@tscircuit/pcb-viewer",
13592
- version: "1.11.311",
13568
+ version: "1.11.313",
13593
13569
  main: "dist/index.js",
13594
13570
  type: "module",
13595
13571
  repository: "tscircuit/pcb-viewer",
@@ -13643,7 +13619,7 @@ var package_default = {
13643
13619
  "@tscircuit/math-utils": "^0.0.29",
13644
13620
  "@vitejs/plugin-react": "^5.0.2",
13645
13621
  "circuit-json": "^0.0.354",
13646
- "circuit-to-canvas": "^0.0.47",
13622
+ "circuit-to-canvas": "^0.0.48",
13647
13623
  "circuit-to-svg": "^0.0.271",
13648
13624
  color: "^4.2.3",
13649
13625
  "react-supergrid": "^1.0.10",
@@ -13654,13 +13630,21 @@ var package_default = {
13654
13630
  };
13655
13631
 
13656
13632
  // src/hooks/useHotKey.ts
13657
- import { useEffect as useEffect13 } from "react";
13658
- var useHotKey = (key, onUse) => {
13633
+ import { useEffect as useEffect13, useRef as useRef10 } from "react";
13634
+ var useHotKey = (key, onUse, containerRef) => {
13659
13635
  const isMouseOverContainer = useGlobalStore(
13660
13636
  (s) => s.is_mouse_over_container
13661
13637
  );
13638
+ const isMouseOverContainerRef = useRef10(isMouseOverContainer);
13639
+ const onUseRef = useRef10(onUse);
13640
+ useEffect13(() => {
13641
+ isMouseOverContainerRef.current = isMouseOverContainer;
13642
+ }, [isMouseOverContainer]);
13662
13643
  useEffect13(() => {
13663
- if (!key || typeof onUse !== "function") return;
13644
+ onUseRef.current = onUse;
13645
+ }, [onUse]);
13646
+ useEffect13(() => {
13647
+ if (!key) return;
13664
13648
  const handleKeyDown = (event) => {
13665
13649
  const keyParts = key.split("+");
13666
13650
  const ctrlRequired = keyParts.includes("ctrl");
@@ -13668,16 +13652,18 @@ var useHotKey = (key, onUse) => {
13668
13652
  const altRequired = keyParts.includes("alt");
13669
13653
  const metaRequired = keyParts.includes("meta");
13670
13654
  const mainKey = keyParts[keyParts.length - 1];
13671
- if (isMouseOverContainer && (!ctrlRequired || event.ctrlKey) && (!shiftRequired || event.shiftKey) && (!altRequired || event.altKey) && (!metaRequired || event.metaKey) && event.key.toLowerCase() === mainKey.toLowerCase()) {
13655
+ const containerHasFocus = containerRef?.current ? containerRef.current.contains(document.activeElement) || document.activeElement === containerRef.current : false;
13656
+ const shouldTrigger = isMouseOverContainerRef.current || containerHasFocus;
13657
+ if (shouldTrigger && (!ctrlRequired || event.ctrlKey) && (!shiftRequired || event.shiftKey) && (!altRequired || event.altKey) && (!metaRequired || event.metaKey) && event.key.toLowerCase() === mainKey.toLowerCase()) {
13672
13658
  event.preventDefault();
13673
- onUse();
13659
+ onUseRef.current();
13674
13660
  }
13675
13661
  };
13676
13662
  window.addEventListener("keydown", handleKeyDown);
13677
13663
  return () => {
13678
13664
  window.removeEventListener("keydown", handleKeyDown);
13679
13665
  };
13680
- }, [key, onUse]);
13666
+ }, [key]);
13681
13667
  };
13682
13668
 
13683
13669
  // src/hooks/useIsSmallScreen.ts
@@ -13897,8 +13883,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13897
13883
  const [isLayerMenuOpen, setLayerMenuOpen] = useState9(false);
13898
13884
  const [isErrorsOpen, setErrorsOpen] = useState9(false);
13899
13885
  const [measureToolArmed, setMeasureToolArmed] = useState9(false);
13900
- const errorElementsRef = useRef10(/* @__PURE__ */ new Map());
13901
- const arrowElementsRef = useRef10(/* @__PURE__ */ new Map());
13886
+ const errorElementsRef = useRef11(/* @__PURE__ */ new Map());
13887
+ const arrowElementsRef = useRef11(/* @__PURE__ */ new Map());
13902
13888
  useEffect15(() => {
13903
13889
  const arm = () => setMeasureToolArmed(true);
13904
13890
  const disarm = () => setMeasureToolArmed(false);
@@ -13919,6 +13905,8 @@ var ToolbarOverlay = ({ children, elements }) => {
13919
13905
  "bottom"
13920
13906
  ];
13921
13907
  const processedLayers = availableLayers;
13908
+ const hasRunInitialMouseCheck = useRef11(false);
13909
+ const hotkeyBoundaryRef = useRef11(null);
13922
13910
  const hotKeyCallbacks = {
13923
13911
  "1": availableLayers[0] ? () => selectLayer(availableLayers[0]) : () => {
13924
13912
  },
@@ -13937,17 +13925,40 @@ var ToolbarOverlay = ({ children, elements }) => {
13937
13925
  "8": availableLayers[7] ? () => selectLayer(availableLayers[7]) : () => {
13938
13926
  }
13939
13927
  };
13940
- useHotKey("1", hotKeyCallbacks["1"]);
13941
- useHotKey("2", hotKeyCallbacks["2"]);
13942
- useHotKey("3", hotKeyCallbacks["3"]);
13943
- useHotKey("4", hotKeyCallbacks["4"]);
13944
- useHotKey("5", hotKeyCallbacks["5"]);
13945
- useHotKey("6", hotKeyCallbacks["6"]);
13946
- useHotKey("7", hotKeyCallbacks["7"]);
13947
- useHotKey("8", hotKeyCallbacks["8"]);
13928
+ useHotKey("1", hotKeyCallbacks["1"], hotkeyBoundaryRef);
13929
+ useHotKey("2", hotKeyCallbacks["2"], hotkeyBoundaryRef);
13930
+ useHotKey("3", hotKeyCallbacks["3"], hotkeyBoundaryRef);
13931
+ useHotKey("4", hotKeyCallbacks["4"], hotkeyBoundaryRef);
13932
+ useHotKey("5", hotKeyCallbacks["5"], hotkeyBoundaryRef);
13933
+ useHotKey("6", hotKeyCallbacks["6"], hotkeyBoundaryRef);
13934
+ useHotKey("7", hotKeyCallbacks["7"], hotkeyBoundaryRef);
13935
+ useHotKey("8", hotKeyCallbacks["8"], hotkeyBoundaryRef);
13936
+ useLayoutEffect2(() => {
13937
+ if (hasRunInitialMouseCheck.current) return;
13938
+ hasRunInitialMouseCheck.current = true;
13939
+ const checkMousePosition = (e) => {
13940
+ if (hotkeyBoundaryRef.current) {
13941
+ const rect = hotkeyBoundaryRef.current.getBoundingClientRect();
13942
+ const isInside = e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom;
13943
+ if (isInside) {
13944
+ setIsMouseOverContainer(true);
13945
+ }
13946
+ }
13947
+ document.removeEventListener("mousemove", checkMousePosition);
13948
+ };
13949
+ document.addEventListener("mousemove", checkMousePosition);
13950
+ return () => {
13951
+ document.removeEventListener("mousemove", checkMousePosition);
13952
+ };
13953
+ }, [setIsMouseOverContainer]);
13948
13954
  const handleMouseEnter = useCallback4(() => {
13949
13955
  setIsMouseOverContainer(true);
13950
13956
  }, [setIsMouseOverContainer]);
13957
+ const handleMouseMove = useCallback4(() => {
13958
+ if (!isMouseOverContainer) {
13959
+ setIsMouseOverContainer(true);
13960
+ }
13961
+ }, [isMouseOverContainer, setIsMouseOverContainer]);
13951
13962
  const handleMouseLeave = useCallback4(() => {
13952
13963
  setIsMouseOverContainer(false);
13953
13964
  setLayerMenuOpen(false);
@@ -13991,9 +14002,11 @@ var ToolbarOverlay = ({ children, elements }) => {
13991
14002
  return /* @__PURE__ */ jsxs14(
13992
14003
  "div",
13993
14004
  {
14005
+ ref: hotkeyBoundaryRef,
13994
14006
  style: { position: "relative", zIndex: "999 !important" },
13995
14007
  onMouseEnter: handleMouseEnter,
13996
14008
  onMouseLeave: handleMouseLeave,
14009
+ onMouseMove: handleMouseMove,
13997
14010
  children: [
13998
14011
  children,
13999
14012
  /* @__PURE__ */ jsxs14(
@@ -14700,8 +14713,8 @@ var PCBViewer = ({
14700
14713
  });
14701
14714
  let [editEvents, setEditEvents] = useState11([]);
14702
14715
  editEvents = editEventsProp ?? editEvents;
14703
- const initialRenderCompleted = useRef11(false);
14704
- const touchStartRef = useRef11(null);
14716
+ const initialRenderCompleted = useRef12(false);
14717
+ const touchStartRef = useRef12(null);
14705
14718
  const circuitJsonKey = useMemo8(
14706
14719
  () => calculateCircuitJsonKey(circuitJson),
14707
14720
  [circuitJson]