@tscircuit/pcb-viewer 1.11.199 → 1.11.201

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
@@ -101565,6 +101565,7 @@ var createStore = (initialState = {}) => createZustandStore(
101565
101565
  is_showing_rats_nest: false,
101566
101566
  is_showing_autorouting: true,
101567
101567
  is_showing_drc_errors: true,
101568
+ is_showing_pcb_groups: false,
101568
101569
  ...initialState,
101569
101570
  selectLayer: (layer) => set({ selected_layer: layer }),
101570
101571
  setEditMode: (mode) => set({
@@ -101580,7 +101581,8 @@ var createStore = (initialState = {}) => createZustandStore(
101580
101581
  setIsMouseOverContainer: (is_focused) => set({ is_mouse_over_container: is_focused }),
101581
101582
  setIsShowingMultipleTracesLength: (is_showing) => set({ is_showing_multiple_traces_length: is_showing }),
101582
101583
  setIsShowingAutorouting: (is_showing) => set({ is_showing_autorouting: is_showing }),
101583
- setIsShowingDrcErrors: (is_showing) => set({ is_showing_drc_errors: is_showing })
101584
+ setIsShowingDrcErrors: (is_showing) => set({ is_showing_drc_errors: is_showing }),
101585
+ setIsShowingPcbGroups: (is_showing) => set({ is_showing_pcb_groups: is_showing })
101584
101586
  })
101585
101587
  );
101586
101588
  var useGlobalStore = (s) => {
@@ -101629,7 +101631,7 @@ var ToastContainer = () => {
101629
101631
  };
101630
101632
 
101631
101633
  // src/PCBViewer.tsx
101632
- import { useEffect as useEffect14, useMemo as useMemo6, useRef as useRef9, useState as useState10 } from "react";
101634
+ import { useEffect as useEffect15, useMemo as useMemo6, useRef as useRef10, useState as useState10 } from "react";
101633
101635
 
101634
101636
  // node_modules/react-use/esm/misc/util.js
101635
101637
  var noop = function() {
@@ -102186,7 +102188,8 @@ var convertElementToPrimitives = (element, allElements) => {
102186
102188
  _element: element,
102187
102189
  _parent_pcb_component,
102188
102190
  _parent_source_component,
102189
- _source_port
102191
+ _source_port,
102192
+ ccw_rotation: element.ccw_rotation
102190
102193
  }
102191
102194
  ];
102192
102195
  } else if (element.shape === "circle") {
@@ -102377,6 +102380,46 @@ var convertElementToPrimitives = (element, allElements) => {
102377
102380
  // Pill-shaped hole in drill layer
102378
102381
  }
102379
102382
  ];
102383
+ } else if (element.shape === "rotated_pill_hole_with_rect_pad") {
102384
+ const {
102385
+ x,
102386
+ y,
102387
+ hole_width,
102388
+ hole_height,
102389
+ hole_ccw_rotation,
102390
+ rect_pad_width,
102391
+ rect_pad_height,
102392
+ rect_ccw_rotation
102393
+ } = element;
102394
+ return [
102395
+ {
102396
+ _pcb_drawing_object_id: `rect_${globalPcbDrawingObjectCount++}`,
102397
+ pcb_drawing_type: "rect",
102398
+ x,
102399
+ y,
102400
+ w: rect_pad_width,
102401
+ h: rect_pad_height,
102402
+ layer: "top",
102403
+ // Rectangular pad on top layer
102404
+ _element: element,
102405
+ _parent_pcb_component,
102406
+ _parent_source_component,
102407
+ _source_port,
102408
+ ccw_rotation: rect_ccw_rotation
102409
+ },
102410
+ {
102411
+ _pcb_drawing_object_id: `pill_${globalPcbDrawingObjectCount++}`,
102412
+ _element: element,
102413
+ pcb_drawing_type: "pill",
102414
+ x,
102415
+ y,
102416
+ w: hole_width,
102417
+ h: hole_height,
102418
+ layer: "drill",
102419
+ // Pill-shaped hole in drill layer
102420
+ ccw_rotation: hole_ccw_rotation
102421
+ }
102422
+ ];
102380
102423
  } else {
102381
102424
  return [];
102382
102425
  }
@@ -102972,6 +103015,7 @@ var zIndexMap = {
102972
103015
  dimensionOverlay: 30,
102973
103016
  editTraceHintOverlay: 30,
102974
103017
  errorOverlay: 30,
103018
+ pcbGroupOverlay: 25,
102975
103019
  ratsNestOverlay: 20,
102976
103020
  toolbarOverlay: 60,
102977
103021
  warnings: 20,
@@ -103160,6 +103204,18 @@ var Drawer = class {
103160
103204
  }
103161
103205
  ctx.restore();
103162
103206
  }
103207
+ rotatedPill(x, y, w, h, ccw_rotation) {
103208
+ const ctx = this.getLayerCtx();
103209
+ this.applyAperture();
103210
+ ctx.save();
103211
+ const [centerX, centerY] = applyToPoint9(this.transform, [x, y]);
103212
+ ctx.translate(centerX, centerY);
103213
+ const cw_rotation = 360 - ccw_rotation;
103214
+ if (ccw_rotation) ctx.rotate(cw_rotation * Math.PI / 180);
103215
+ ctx.translate(-centerX, -centerY);
103216
+ this.pill(x, y, w, h);
103217
+ ctx.restore();
103218
+ }
103163
103219
  circle(x, y, r, mesh_fill) {
103164
103220
  const r$ = scaleOnly(this.transform, r);
103165
103221
  const [x$, y$] = applyToPoint9(this.transform, [x, y]);
@@ -103606,6 +103662,13 @@ var drawRotatedRect = (drawer, rect) => {
103606
103662
  });
103607
103663
  drawer.rotatedRect(rect.x, rect.y, rect.w, rect.h, rect.ccw_rotation);
103608
103664
  };
103665
+ var drawRotatedPill = (drawer, pill) => {
103666
+ drawer.equip({
103667
+ color: getColor(pill),
103668
+ layer: pill.layer
103669
+ });
103670
+ drawer.rotatedPill(pill.x, pill.y, pill.w, pill.h, pill.ccw_rotation);
103671
+ };
103609
103672
  var drawCircle = (drawer, circle) => {
103610
103673
  drawer.equip({
103611
103674
  color: getColor(circle),
@@ -103641,13 +103704,8 @@ var drawPrimitive = (drawer, primitive) => {
103641
103704
  case "text":
103642
103705
  return drawText(drawer, primitive);
103643
103706
  case "rect":
103644
- if (primitive._element?.shape === "rotated_rect") {
103645
- return drawRotatedRect(drawer, {
103646
- ...primitive,
103647
- // @ts-ignore
103648
- ccw_rotation: primitive._element.ccw_rotation,
103649
- mesh_fill: primitive.mesh_fill
103650
- });
103707
+ if (primitive.ccw_rotation) {
103708
+ return drawRotatedRect(drawer, primitive);
103651
103709
  }
103652
103710
  return drawRect(drawer, primitive);
103653
103711
  case "circle":
@@ -103655,6 +103713,9 @@ var drawPrimitive = (drawer, primitive) => {
103655
103713
  case "oval":
103656
103714
  return drawOval(drawer, primitive);
103657
103715
  case "pill":
103716
+ if (primitive.ccw_rotation) {
103717
+ return drawRotatedPill(drawer, primitive);
103718
+ }
103658
103719
  return drawPill(drawer, primitive);
103659
103720
  case "polygon":
103660
103721
  return drawPolygon(drawer, primitive);
@@ -105374,10 +105435,203 @@ var MouseElementTracker = ({
105374
105435
  );
105375
105436
  };
105376
105437
 
105438
+ // src/components/PcbGroupOverlay.tsx
105439
+ import { applyToPoint as applyToPoint17 } from "transformation-matrix";
105440
+ import { identity as identity12 } from "transformation-matrix";
105441
+ import { useRef as useRef9, useEffect as useEffect12 } from "react";
105442
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
105443
+ var GROUP_COLORS = [
105444
+ "rgb(255, 100, 100)",
105445
+ "rgb(100, 255, 100)",
105446
+ "rgb(100, 100, 255)",
105447
+ "rgb(255, 255, 100)",
105448
+ "rgb(255, 100, 255)",
105449
+ "rgb(100, 255, 255)",
105450
+ "rgb(255, 150, 100)",
105451
+ "rgb(150, 100, 255)",
105452
+ "rgb(100, 255, 150)",
105453
+ "rgb(255, 100, 150)"
105454
+ ];
105455
+ var PcbGroupOverlay = ({
105456
+ children,
105457
+ transform: transform2 = identity12(),
105458
+ elements = []
105459
+ }) => {
105460
+ const [containerRef, { width, height }] = useMeasure_default();
105461
+ const canvasRef = useRef9(null);
105462
+ const is_showing_pcb_groups = useGlobalStore((s) => s.is_showing_pcb_groups);
105463
+ useEffect12(() => {
105464
+ const canvas = canvasRef.current;
105465
+ if (!canvas || !width || !height) return;
105466
+ canvas.width = width;
105467
+ canvas.height = height;
105468
+ const ctx = canvas.getContext("2d");
105469
+ if (!ctx) return;
105470
+ ctx.clearRect(0, 0, width, height);
105471
+ if (!is_showing_pcb_groups) return;
105472
+ const pcbGroups = elements.filter(
105473
+ (el) => el.type === "pcb_group"
105474
+ );
105475
+ const pcbComponents = elements.filter(
105476
+ (el) => el.type === "pcb_component"
105477
+ );
105478
+ const sourceGroups = elements.filter(
105479
+ (el) => el.type === "source_group"
105480
+ );
105481
+ const sourceGroupHierarchy = /* @__PURE__ */ new Map();
105482
+ sourceGroups.forEach((group) => {
105483
+ const groupWithParent = group;
105484
+ if (groupWithParent.parent_source_group_id) {
105485
+ const children2 = sourceGroupHierarchy.get(groupWithParent.parent_source_group_id) || [];
105486
+ children2.push(group.source_group_id);
105487
+ sourceGroupHierarchy.set(
105488
+ groupWithParent.parent_source_group_id,
105489
+ children2
105490
+ );
105491
+ }
105492
+ });
105493
+ const getAllDescendantSourceGroups = (sourceGroupId) => {
105494
+ const descendants = [];
105495
+ const children2 = sourceGroupHierarchy.get(sourceGroupId) || [];
105496
+ for (const child of children2) {
105497
+ descendants.push(child);
105498
+ descendants.push(...getAllDescendantSourceGroups(child));
105499
+ }
105500
+ return descendants;
105501
+ };
105502
+ const getGroupDepthLevel = (sourceGroupId) => {
105503
+ const groupWithParent = sourceGroups.find(
105504
+ (g) => g.source_group_id === sourceGroupId
105505
+ );
105506
+ if (!groupWithParent?.parent_source_group_id) {
105507
+ return 0;
105508
+ }
105509
+ return 1 + getGroupDepthLevel(groupWithParent.parent_source_group_id);
105510
+ };
105511
+ pcbGroups.forEach((group, groupIndex) => {
105512
+ let groupComponents = pcbComponents.filter(
105513
+ (comp) => comp.pcb_group_id === group.pcb_group_id
105514
+ );
105515
+ if (group.source_group_id) {
105516
+ const descendantSourceGroups = getAllDescendantSourceGroups(
105517
+ group.source_group_id
105518
+ );
105519
+ const childPcbGroups = pcbGroups.filter(
105520
+ (pcbGroup) => pcbGroup.source_group_id && descendantSourceGroups.includes(pcbGroup.source_group_id)
105521
+ );
105522
+ for (const childPcbGroup of childPcbGroups) {
105523
+ const childComponents = pcbComponents.filter(
105524
+ (comp) => comp.pcb_group_id === childPcbGroup.pcb_group_id
105525
+ );
105526
+ groupComponents = [...groupComponents, ...childComponents];
105527
+ }
105528
+ }
105529
+ if (groupComponents.length === 0) return;
105530
+ let minX = Infinity;
105531
+ let minY = Infinity;
105532
+ let maxX = -Infinity;
105533
+ let maxY = -Infinity;
105534
+ groupComponents.forEach((comp) => {
105535
+ if (comp.center && typeof comp.width === "number" && typeof comp.height === "number") {
105536
+ const left = comp.center.x - comp.width / 2;
105537
+ const right = comp.center.x + comp.width / 2;
105538
+ const top = comp.center.y + comp.height / 2;
105539
+ const bottom = comp.center.y - comp.height / 2;
105540
+ minX = Math.min(minX, left);
105541
+ maxX = Math.max(maxX, right);
105542
+ minY = Math.min(minY, bottom);
105543
+ maxY = Math.max(maxY, top);
105544
+ }
105545
+ });
105546
+ if (minX === Infinity || maxX === -Infinity) return;
105547
+ const depthLevel = group.source_group_id ? getGroupDepthLevel(group.source_group_id) : 0;
105548
+ const hasChildren2 = group.source_group_id ? getAllDescendantSourceGroups(group.source_group_id).length > 0 : false;
105549
+ const basePadding = 1;
105550
+ const hierarchyPadding = hasChildren2 ? 0.5 : 0;
105551
+ const totalPadding = basePadding + hierarchyPadding;
105552
+ minX -= totalPadding;
105553
+ maxX += totalPadding;
105554
+ minY -= totalPadding;
105555
+ maxY += totalPadding;
105556
+ const topLeft = applyToPoint17(transform2, { x: minX, y: maxY });
105557
+ const topRight = applyToPoint17(transform2, { x: maxX, y: maxY });
105558
+ const bottomLeft = applyToPoint17(transform2, { x: minX, y: minY });
105559
+ const bottomRight = applyToPoint17(transform2, { x: maxX, y: minY });
105560
+ const groupColor = GROUP_COLORS[groupIndex % GROUP_COLORS.length];
105561
+ ctx.strokeStyle = groupColor;
105562
+ ctx.lineWidth = 2;
105563
+ const baseDashSize = Math.max(4, Math.min(12, 8 * Math.abs(transform2.a)));
105564
+ const baseGapSize = Math.max(2, Math.min(6, 4 * Math.abs(transform2.a)));
105565
+ const dashMultiplier = hasChildren2 ? 1.3 : 1;
105566
+ const dashSize = baseDashSize * dashMultiplier;
105567
+ const gapSize = baseGapSize;
105568
+ ctx.setLineDash([dashSize, gapSize]);
105569
+ ctx.beginPath();
105570
+ ctx.moveTo(topLeft.x, topLeft.y);
105571
+ ctx.lineTo(topRight.x, topRight.y);
105572
+ ctx.lineTo(bottomRight.x, bottomRight.y);
105573
+ ctx.lineTo(bottomLeft.x, bottomLeft.y);
105574
+ ctx.closePath();
105575
+ ctx.stroke();
105576
+ const baseFontSize = Math.max(8, Math.min(12, 10 * Math.abs(transform2.a)));
105577
+ const fontSizeReduction = depthLevel == 0 || depthLevel == 1 ? 0 : depthLevel * 0.11;
105578
+ const fontSize = baseFontSize * (1 - fontSizeReduction);
105579
+ const labelPadding = 4;
105580
+ const labelText = group.name || `Group ${groupIndex + 1}`;
105581
+ ctx.font = `${fontSize}px sans-serif`;
105582
+ ctx.setLineDash([]);
105583
+ const labelMetrics = ctx.measureText(labelText);
105584
+ const labelWidth = labelMetrics.width + labelPadding * 2;
105585
+ const labelHeight = fontSize + labelPadding * 2;
105586
+ const labelX = topLeft.x - 5;
105587
+ const labelY = topLeft.y - 5;
105588
+ const borderRadius = 3;
105589
+ ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
105590
+ ctx.beginPath();
105591
+ ctx.roundRect(
105592
+ labelX,
105593
+ labelY - labelHeight,
105594
+ labelWidth,
105595
+ labelHeight,
105596
+ borderRadius
105597
+ );
105598
+ ctx.fill();
105599
+ ctx.fillStyle = groupColor;
105600
+ ctx.textAlign = "left";
105601
+ ctx.textBaseline = "middle";
105602
+ ctx.fillText(labelText, labelX + labelPadding, labelY - labelHeight / 2);
105603
+ });
105604
+ }, [elements, transform2, width, height, is_showing_pcb_groups]);
105605
+ return /* @__PURE__ */ jsxs11(
105606
+ "div",
105607
+ {
105608
+ ref: containerRef,
105609
+ style: { position: "relative", width: "100%", height: "100%" },
105610
+ children: [
105611
+ children,
105612
+ /* @__PURE__ */ jsx19(
105613
+ "canvas",
105614
+ {
105615
+ ref: canvasRef,
105616
+ style: {
105617
+ position: "absolute",
105618
+ top: 0,
105619
+ left: 0,
105620
+ pointerEvents: "none",
105621
+ zIndex: zIndexMap.pcbGroupOverlay,
105622
+ display: is_showing_pcb_groups ? "block" : "none"
105623
+ }
105624
+ }
105625
+ )
105626
+ ]
105627
+ }
105628
+ );
105629
+ };
105630
+
105377
105631
  // src/components/RatsNestOverlay.tsx
105378
- import { applyToPoint as applyToPoint17, identity as identity12 } from "transformation-matrix";
105632
+ import { applyToPoint as applyToPoint18, identity as identity13 } from "transformation-matrix";
105379
105633
  import { useMemo as useMemo4 } from "react";
105380
- import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
105634
+ import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
105381
105635
  var RatsNestOverlay = ({ transform: transform2, soup, children }) => {
105382
105636
  const isShowingRatsNest = useGlobalStore((s) => s.is_showing_rats_nest);
105383
105637
  const { netMap, idToNetMap } = useMemo4(
@@ -105439,10 +105693,10 @@ var RatsNestOverlay = ({ transform: transform2, soup, children }) => {
105439
105693
  return lines;
105440
105694
  }, [soup, netMap, idToNetMap, isShowingRatsNest]);
105441
105695
  if (!soup || !isShowingRatsNest) return children;
105442
- if (!transform2) transform2 = identity12();
105443
- return /* @__PURE__ */ jsxs11("div", { style: { position: "relative" }, children: [
105696
+ if (!transform2) transform2 = identity13();
105697
+ return /* @__PURE__ */ jsxs12("div", { style: { position: "relative" }, children: [
105444
105698
  children,
105445
- /* @__PURE__ */ jsx19(
105699
+ /* @__PURE__ */ jsx20(
105446
105700
  "svg",
105447
105701
  {
105448
105702
  style: {
@@ -105456,9 +105710,9 @@ var RatsNestOverlay = ({ transform: transform2, soup, children }) => {
105456
105710
  zIndex: zIndexMap.ratsNestOverlay
105457
105711
  },
105458
105712
  children: ratsNestLines.map(({ key, startPoint, endPoint, isInNet }) => {
105459
- const transformedStart = applyToPoint17(transform2, startPoint);
105460
- const transformedEnd = applyToPoint17(transform2, endPoint);
105461
- return /* @__PURE__ */ jsx19(
105713
+ const transformedStart = applyToPoint18(transform2, startPoint);
105714
+ const transformedEnd = applyToPoint18(transform2, endPoint);
105715
+ return /* @__PURE__ */ jsx20(
105462
105716
  "line",
105463
105717
  {
105464
105718
  x1: transformedStart.x,
@@ -105478,13 +105732,13 @@ var RatsNestOverlay = ({ transform: transform2, soup, children }) => {
105478
105732
  };
105479
105733
 
105480
105734
  // src/components/ToolbarOverlay.tsx
105481
- import { Fragment as Fragment5, useEffect as useEffect13, useState as useState8 } from "react";
105735
+ import { Fragment as Fragment5, useEffect as useEffect14, useState as useState8 } from "react";
105482
105736
  import { css as css3 } from "@emotion/css";
105483
105737
 
105484
105738
  // package.json
105485
105739
  var package_default2 = {
105486
105740
  name: "@tscircuit/pcb-viewer",
105487
- version: "1.11.198",
105741
+ version: "1.11.200",
105488
105742
  main: "dist/index.js",
105489
105743
  type: "module",
105490
105744
  repository: "tscircuit/pcb-viewer",
@@ -105545,12 +105799,12 @@ var package_default2 = {
105545
105799
  };
105546
105800
 
105547
105801
  // src/hooks/useHotKey.ts
105548
- import { useEffect as useEffect12 } from "react";
105802
+ import { useEffect as useEffect13 } from "react";
105549
105803
  var useHotKey = (key, onUse) => {
105550
105804
  const isMouseOverContainer = useGlobalStore(
105551
105805
  (s) => s.is_mouse_over_container
105552
105806
  );
105553
- useEffect12(() => {
105807
+ useEffect13(() => {
105554
105808
  if (!key || typeof onUse !== "function") return;
105555
105809
  const handleKeyDown = (event) => {
105556
105810
  const keyParts = key.split("+");
@@ -105572,13 +105826,13 @@ var useHotKey = (key, onUse) => {
105572
105826
  };
105573
105827
 
105574
105828
  // src/components/ToolbarOverlay.tsx
105575
- import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
105829
+ import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
105576
105830
  var LayerButton = ({
105577
105831
  name,
105578
105832
  selected,
105579
105833
  onClick
105580
105834
  }) => {
105581
- return /* @__PURE__ */ jsxs12(
105835
+ return /* @__PURE__ */ jsxs13(
105582
105836
  "div",
105583
105837
  {
105584
105838
  className: css3`
@@ -105594,8 +105848,8 @@ var LayerButton = ({
105594
105848
  `,
105595
105849
  onClick,
105596
105850
  children: [
105597
- /* @__PURE__ */ jsx20("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
105598
- /* @__PURE__ */ jsx20(
105851
+ /* @__PURE__ */ jsx21("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
105852
+ /* @__PURE__ */ jsx21(
105599
105853
  "span",
105600
105854
  {
105601
105855
  style: {
@@ -105610,7 +105864,7 @@ var LayerButton = ({
105610
105864
  }
105611
105865
  );
105612
105866
  };
105613
- var ToolbarButton = ({ children, ...props }) => /* @__PURE__ */ jsx20(
105867
+ var ToolbarButton = ({ children, ...props }) => /* @__PURE__ */ jsx21(
105614
105868
  "div",
105615
105869
  {
105616
105870
  ...props,
@@ -105635,7 +105889,7 @@ var CheckboxMenuItem = ({
105635
105889
  checked,
105636
105890
  onClick
105637
105891
  }) => {
105638
- return /* @__PURE__ */ jsxs12(
105892
+ return /* @__PURE__ */ jsxs13(
105639
105893
  "div",
105640
105894
  {
105641
105895
  className: css3`
@@ -105657,8 +105911,9 @@ var CheckboxMenuItem = ({
105657
105911
  onClick();
105658
105912
  },
105659
105913
  children: [
105660
- /* @__PURE__ */ jsx20("input", { type: "checkbox", checked }),
105661
- /* @__PURE__ */ jsx20("span", { style: { color: "#eee" }, children: label })
105914
+ /* @__PURE__ */ jsx21("input", { type: "checkbox", checked, onChange: () => {
105915
+ }, readOnly: true }),
105916
+ /* @__PURE__ */ jsx21("span", { style: { color: "#eee" }, children: label })
105662
105917
  ]
105663
105918
  }
105664
105919
  );
@@ -105680,14 +105935,16 @@ var ToolbarOverlay = ({ children, elements }) => {
105680
105935
  is_showing_rats_nest,
105681
105936
  is_showing_multiple_traces_length,
105682
105937
  is_showing_autorouting,
105683
- is_showing_drc_errors
105938
+ is_showing_drc_errors,
105939
+ is_showing_pcb_groups
105684
105940
  ] = useGlobalStore((s) => [
105685
105941
  s.in_move_footprint_mode,
105686
105942
  s.in_draw_trace_mode,
105687
105943
  s.is_showing_rats_nest,
105688
105944
  s.is_showing_multiple_traces_length,
105689
105945
  s.is_showing_autorouting,
105690
- s.is_showing_drc_errors
105946
+ s.is_showing_drc_errors,
105947
+ s.is_showing_pcb_groups
105691
105948
  ]);
105692
105949
  const setEditMode = useGlobalStore((s) => s.setEditMode);
105693
105950
  const setIsShowingRatsNest = useGlobalStore((s) => s.setIsShowingRatsNest);
@@ -105698,7 +105955,8 @@ var ToolbarOverlay = ({ children, elements }) => {
105698
105955
  (s) => s.setIsShowingAutorouting
105699
105956
  );
105700
105957
  const setIsShowingDrcErrors = useGlobalStore((s) => s.setIsShowingDrcErrors);
105701
- useEffect13(() => {
105958
+ const setIsShowingPcbGroups = useGlobalStore((s) => s.setIsShowingPcbGroups);
105959
+ useEffect14(() => {
105702
105960
  const arm = () => setMeasureToolArmed(true);
105703
105961
  const disarm = () => setMeasureToolArmed(false);
105704
105962
  window.addEventListener("arm-dimension-tool", arm);
@@ -105717,7 +105975,7 @@ var ToolbarOverlay = ({ children, elements }) => {
105717
105975
  useHotKey("7", () => selectLayer("inner5"));
105718
105976
  useHotKey("8", () => selectLayer("inner6"));
105719
105977
  const errorCount = elements?.filter((e) => e.type.includes("error")).length ?? 0;
105720
- return /* @__PURE__ */ jsxs12(
105978
+ return /* @__PURE__ */ jsxs13(
105721
105979
  "div",
105722
105980
  {
105723
105981
  style: { position: "relative" },
@@ -105730,7 +105988,7 @@ var ToolbarOverlay = ({ children, elements }) => {
105730
105988
  },
105731
105989
  children: [
105732
105990
  children,
105733
- /* @__PURE__ */ jsxs12(
105991
+ /* @__PURE__ */ jsxs13(
105734
105992
  "div",
105735
105993
  {
105736
105994
  style: {
@@ -105751,7 +106009,7 @@ var ToolbarOverlay = ({ children, elements }) => {
105751
106009
  ]
105752
106010
  }
105753
106011
  ),
105754
- /* @__PURE__ */ jsxs12(
106012
+ /* @__PURE__ */ jsxs13(
105755
106013
  "div",
105756
106014
  {
105757
106015
  style: {
@@ -105768,7 +106026,7 @@ var ToolbarOverlay = ({ children, elements }) => {
105768
106026
  fontFamily: "sans-serif"
105769
106027
  },
105770
106028
  children: [
105771
- /* @__PURE__ */ jsxs12(
106029
+ /* @__PURE__ */ jsxs13(
105772
106030
  ToolbarButton,
105773
106031
  {
105774
106032
  onClick: () => {
@@ -105780,10 +106038,10 @@ var ToolbarOverlay = ({ children, elements }) => {
105780
106038
  }
105781
106039
  },
105782
106040
  children: [
105783
- /* @__PURE__ */ jsxs12("div", { children: [
106041
+ /* @__PURE__ */ jsxs13("div", { children: [
105784
106042
  "layer:",
105785
106043
  " ",
105786
- /* @__PURE__ */ jsx20(
106044
+ /* @__PURE__ */ jsx21(
105787
106045
  "span",
105788
106046
  {
105789
106047
  style: {
@@ -105795,7 +106053,7 @@ var ToolbarOverlay = ({ children, elements }) => {
105795
106053
  }
105796
106054
  )
105797
106055
  ] }),
105798
- isLayerMenuOpen && /* @__PURE__ */ jsx20("div", { style: { marginTop: 4, minWidth: 120 }, children: all_layers.map((l) => l.replace(/-/g, "")).map((layer) => /* @__PURE__ */ jsx20(
106056
+ isLayerMenuOpen && /* @__PURE__ */ jsx21("div", { style: { marginTop: 4, minWidth: 120 }, children: all_layers.map((l) => l.replace(/-/g, "")).map((layer) => /* @__PURE__ */ jsx21(
105799
106057
  LayerButton,
105800
106058
  {
105801
106059
  name: layer,
@@ -105809,70 +106067,70 @@ var ToolbarOverlay = ({ children, elements }) => {
105809
106067
  ]
105810
106068
  }
105811
106069
  ),
105812
- /* @__PURE__ */ jsxs12(
106070
+ /* @__PURE__ */ jsxs13(
105813
106071
  ToolbarButton,
105814
106072
  {
105815
106073
  style: errorCount > 0 ? { color: "red" } : {},
105816
106074
  onClick: () => setErrorsOpen(!isErrorsOpen),
105817
106075
  onMouseLeave: () => setErrorsOpen(false),
105818
106076
  children: [
105819
- /* @__PURE__ */ jsxs12("div", { children: [
106077
+ /* @__PURE__ */ jsxs13("div", { children: [
105820
106078
  errorCount,
105821
106079
  " errors"
105822
106080
  ] }),
105823
- isErrorsOpen && /* @__PURE__ */ jsx20(
106081
+ isErrorsOpen && /* @__PURE__ */ jsx21(
105824
106082
  "div",
105825
106083
  {
105826
106084
  style: { display: "grid", gridTemplateColumns: "100px 300px" },
105827
- children: elements?.filter((e) => e.type.includes("error")).map((e, i) => /* @__PURE__ */ jsxs12(Fragment5, { children: [
105828
- /* @__PURE__ */ jsx20("div", { children: e.error_type }),
105829
- /* @__PURE__ */ jsx20("div", { children: e.message })
106085
+ children: elements?.filter((e) => e.type.includes("error")).map((e, i) => /* @__PURE__ */ jsxs13(Fragment5, { children: [
106086
+ /* @__PURE__ */ jsx21("div", { children: e.error_type }),
106087
+ /* @__PURE__ */ jsx21("div", { children: e.message })
105830
106088
  ] }, i))
105831
106089
  }
105832
106090
  )
105833
106091
  ]
105834
106092
  }
105835
106093
  ),
105836
- /* @__PURE__ */ jsx20(
106094
+ /* @__PURE__ */ jsx21(
105837
106095
  ToolbarButton,
105838
106096
  {
105839
106097
  style: {},
105840
106098
  onClick: () => {
105841
106099
  setEditMode(in_draw_trace_mode ? "off" : "draw_trace");
105842
106100
  },
105843
- children: /* @__PURE__ */ jsxs12("div", { children: [
106101
+ children: /* @__PURE__ */ jsxs13("div", { children: [
105844
106102
  in_draw_trace_mode ? "\u2716 " : "",
105845
106103
  "Edit Traces"
105846
106104
  ] })
105847
106105
  }
105848
106106
  ),
105849
- /* @__PURE__ */ jsx20(
106107
+ /* @__PURE__ */ jsx21(
105850
106108
  ToolbarButton,
105851
106109
  {
105852
106110
  style: {},
105853
106111
  onClick: () => {
105854
106112
  setEditMode(in_move_footprint_mode ? "off" : "move_footprint");
105855
106113
  },
105856
- children: /* @__PURE__ */ jsxs12("div", { children: [
106114
+ children: /* @__PURE__ */ jsxs13("div", { children: [
105857
106115
  in_move_footprint_mode ? "\u2716 " : "",
105858
106116
  "Move Components"
105859
106117
  ] })
105860
106118
  }
105861
106119
  ),
105862
- /* @__PURE__ */ jsx20(
106120
+ /* @__PURE__ */ jsx21(
105863
106121
  ToolbarButton,
105864
106122
  {
105865
106123
  style: {},
105866
106124
  onClick: () => {
105867
106125
  setIsShowingRatsNest(!is_showing_rats_nest);
105868
106126
  },
105869
- children: /* @__PURE__ */ jsxs12("div", { children: [
106127
+ children: /* @__PURE__ */ jsxs13("div", { children: [
105870
106128
  is_showing_rats_nest ? "\u2716 " : "",
105871
106129
  "Rats Nest"
105872
106130
  ] })
105873
106131
  }
105874
106132
  ),
105875
- /* @__PURE__ */ jsx20(
106133
+ /* @__PURE__ */ jsx21(
105876
106134
  ToolbarButton,
105877
106135
  {
105878
106136
  style: measureToolArmed ? { backgroundColor: "#444" } : {},
@@ -105880,17 +106138,17 @@ var ToolbarOverlay = ({ children, elements }) => {
105880
106138
  setMeasureToolArmed(true);
105881
106139
  window.dispatchEvent(new Event("arm-dimension-tool"));
105882
106140
  },
105883
- children: /* @__PURE__ */ jsx20("div", { children: "\u{1F4CF}" })
106141
+ children: /* @__PURE__ */ jsx21("div", { children: "\u{1F4CF}" })
105884
106142
  }
105885
106143
  ),
105886
- /* @__PURE__ */ jsx20(
106144
+ /* @__PURE__ */ jsx21(
105887
106145
  ToolbarButton,
105888
106146
  {
105889
106147
  onClick: () => {
105890
106148
  setViewMenuOpen(!isViewMenuOpen);
105891
106149
  },
105892
- children: /* @__PURE__ */ jsxs12("div", { children: [
105893
- /* @__PURE__ */ jsxs12(
106150
+ children: /* @__PURE__ */ jsxs13("div", { children: [
106151
+ /* @__PURE__ */ jsxs13(
105894
106152
  "div",
105895
106153
  {
105896
106154
  style: {
@@ -105901,7 +106159,7 @@ var ToolbarOverlay = ({ children, elements }) => {
105901
106159
  children: [
105902
106160
  "View",
105903
106161
  " ",
105904
- /* @__PURE__ */ jsx20(
106162
+ /* @__PURE__ */ jsx21(
105905
106163
  "span",
105906
106164
  {
105907
106165
  style: {
@@ -105916,8 +106174,8 @@ var ToolbarOverlay = ({ children, elements }) => {
105916
106174
  ]
105917
106175
  }
105918
106176
  ),
105919
- isViewMenuOpen && /* @__PURE__ */ jsxs12("div", { style: { marginTop: 4, minWidth: 120 }, children: [
105920
- /* @__PURE__ */ jsx20(
106177
+ isViewMenuOpen && /* @__PURE__ */ jsxs13("div", { style: { marginTop: 4, minWidth: 120 }, children: [
106178
+ /* @__PURE__ */ jsx21(
105921
106179
  CheckboxMenuItem,
105922
106180
  {
105923
106181
  label: "Show All Trace Lengths",
@@ -105929,7 +106187,7 @@ var ToolbarOverlay = ({ children, elements }) => {
105929
106187
  }
105930
106188
  }
105931
106189
  ),
105932
- /* @__PURE__ */ jsx20(
106190
+ /* @__PURE__ */ jsx21(
105933
106191
  CheckboxMenuItem,
105934
106192
  {
105935
106193
  label: "Show Autorouting Animation",
@@ -105939,7 +106197,7 @@ var ToolbarOverlay = ({ children, elements }) => {
105939
106197
  }
105940
106198
  }
105941
106199
  ),
105942
- /* @__PURE__ */ jsx20(
106200
+ /* @__PURE__ */ jsx21(
105943
106201
  CheckboxMenuItem,
105944
106202
  {
105945
106203
  label: "Show DRC Errors",
@@ -105948,6 +106206,16 @@ var ToolbarOverlay = ({ children, elements }) => {
105948
106206
  setIsShowingDrcErrors(!is_showing_drc_errors);
105949
106207
  }
105950
106208
  }
106209
+ ),
106210
+ /* @__PURE__ */ jsx21(
106211
+ CheckboxMenuItem,
106212
+ {
106213
+ label: "Show PCB Groups",
106214
+ checked: is_showing_pcb_groups,
106215
+ onClick: () => {
106216
+ setIsShowingPcbGroups(!is_showing_pcb_groups);
106217
+ }
106218
+ }
105951
106219
  )
105952
106220
  ] })
105953
106221
  ] })
@@ -105962,7 +106230,7 @@ var ToolbarOverlay = ({ children, elements }) => {
105962
106230
  };
105963
106231
 
105964
106232
  // src/components/CanvasElementsRenderer.tsx
105965
- import { jsx as jsx21 } from "react/jsx-runtime";
106233
+ import { jsx as jsx22 } from "react/jsx-runtime";
105966
106234
  var CanvasElementsRenderer = (props) => {
105967
106235
  const { transform: transform2, elements } = props;
105968
106236
  const [primitivesWithoutInteractionMetadata, connectivityMap] = useMemo5(() => {
@@ -106008,14 +106276,14 @@ var CanvasElementsRenderer = (props) => {
106008
106276
  },
106009
106277
  [connectivityMap]
106010
106278
  );
106011
- return /* @__PURE__ */ jsx21(
106279
+ return /* @__PURE__ */ jsx22(
106012
106280
  MouseElementTracker,
106013
106281
  {
106014
106282
  elements,
106015
106283
  transform: transform2,
106016
106284
  primitives: primitivesWithoutInteractionMetadata,
106017
106285
  onMouseHoverOverPrimitives: onMouseOverPrimitives,
106018
- children: /* @__PURE__ */ jsx21(
106286
+ children: /* @__PURE__ */ jsx22(
106019
106287
  EditPlacementOverlay,
106020
106288
  {
106021
106289
  disabled: !props.allowEditing,
@@ -106024,7 +106292,7 @@ var CanvasElementsRenderer = (props) => {
106024
106292
  cancelPanDrag: props.cancelPanDrag,
106025
106293
  onCreateEditEvent: props.onCreateEditEvent,
106026
106294
  onModifyEditEvent: props.onModifyEditEvent,
106027
- children: /* @__PURE__ */ jsx21(
106295
+ children: /* @__PURE__ */ jsx22(
106028
106296
  EditTraceHintOverlay,
106029
106297
  {
106030
106298
  disabled: !props.allowEditing,
@@ -106033,22 +106301,22 @@ var CanvasElementsRenderer = (props) => {
106033
106301
  cancelPanDrag: props.cancelPanDrag,
106034
106302
  onCreateEditEvent: props.onCreateEditEvent,
106035
106303
  onModifyEditEvent: props.onModifyEditEvent,
106036
- children: /* @__PURE__ */ jsx21(
106304
+ children: /* @__PURE__ */ jsx22(
106037
106305
  DimensionOverlay,
106038
106306
  {
106039
106307
  transform: transform2,
106040
106308
  focusOnHover: props.focusOnHover,
106041
- children: /* @__PURE__ */ jsx21(ToolbarOverlay, { elements, children: /* @__PURE__ */ jsx21(ErrorOverlay, { transform: transform2, elements, children: /* @__PURE__ */ jsx21(RatsNestOverlay, { transform: transform2, soup: elements, children: /* @__PURE__ */ jsx21(
106309
+ children: /* @__PURE__ */ jsx22(ToolbarOverlay, { elements, children: /* @__PURE__ */ jsx22(ErrorOverlay, { transform: transform2, elements, children: /* @__PURE__ */ jsx22(RatsNestOverlay, { transform: transform2, soup: elements, children: /* @__PURE__ */ jsx22(PcbGroupOverlay, { transform: transform2, elements, children: /* @__PURE__ */ jsx22(
106042
106310
  DebugGraphicsOverlay,
106043
106311
  {
106044
106312
  transform: transform2,
106045
106313
  debugGraphics: props.debugGraphics,
106046
- children: /* @__PURE__ */ jsx21(
106314
+ children: /* @__PURE__ */ jsx22(
106047
106315
  WarningGraphicsOverlay,
106048
106316
  {
106049
106317
  transform: transform2,
106050
106318
  elements,
106051
- children: /* @__PURE__ */ jsx21(
106319
+ children: /* @__PURE__ */ jsx22(
106052
106320
  CanvasPrimitiveRenderer,
106053
106321
  {
106054
106322
  transform: transform2,
@@ -106061,7 +106329,7 @@ var CanvasElementsRenderer = (props) => {
106061
106329
  }
106062
106330
  )
106063
106331
  }
106064
- ) }) }) })
106332
+ ) }) }) }) })
106065
106333
  }
106066
106334
  )
106067
106335
  }
@@ -106073,7 +106341,7 @@ var CanvasElementsRenderer = (props) => {
106073
106341
  };
106074
106342
 
106075
106343
  // src/PCBViewer.tsx
106076
- import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
106344
+ import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
106077
106345
  var defaultTransform = compose8(translate10(400, 300), scale5(40, -40));
106078
106346
  var PCBViewer = ({
106079
106347
  circuitJson,
@@ -106102,8 +106370,8 @@ var PCBViewer = ({
106102
106370
  });
106103
106371
  let [editEvents, setEditEvents] = useState10([]);
106104
106372
  editEvents = editEventsProp ?? editEvents;
106105
- const initialRenderCompleted = useRef9(false);
106106
- const touchStartRef = useRef9(null);
106373
+ const initialRenderCompleted = useRef10(false);
106374
+ const touchStartRef = useRef10(null);
106107
106375
  const circuitJsonKey = `${circuitJson?.length || 0}_${circuitJson?.editCount || 0}`;
106108
106376
  const resetTransform = () => {
106109
106377
  const elmBounds = refDimensions?.width > 0 ? refDimensions : { width: 500, height: 500 };
@@ -106125,7 +106393,7 @@ var PCBViewer = ({
106125
106393
  setTransform(targetTransform);
106126
106394
  return;
106127
106395
  };
106128
- useEffect14(() => {
106396
+ useEffect15(() => {
106129
106397
  if (!refDimensions?.width) return;
106130
106398
  if (!circuitJson) return;
106131
106399
  if (circuitJson.length === 0) return;
@@ -106156,9 +106424,9 @@ var PCBViewer = ({
106156
106424
  setEditEvents(newEditEvents);
106157
106425
  onEditEventsChanged?.(newEditEvents);
106158
106426
  };
106159
- return /* @__PURE__ */ jsxs13("div", { ref: transformRef, style: { position: "relative" }, children: [
106160
- /* @__PURE__ */ jsx22("div", { ref: ref65, children: /* @__PURE__ */ jsxs13(ContextProviders, { initialState, children: [
106161
- /* @__PURE__ */ jsx22(
106427
+ return /* @__PURE__ */ jsxs14("div", { ref: transformRef, style: { position: "relative" }, children: [
106428
+ /* @__PURE__ */ jsx23("div", { ref: ref65, children: /* @__PURE__ */ jsxs14(ContextProviders, { initialState, children: [
106429
+ /* @__PURE__ */ jsx23(
106162
106430
  CanvasElementsRenderer,
106163
106431
  {
106164
106432
  transform: transform2,
@@ -106183,9 +106451,9 @@ var PCBViewer = ({
106183
106451
  },
106184
106452
  refDimensions.width
106185
106453
  ),
106186
- /* @__PURE__ */ jsx22(ToastContainer, {})
106454
+ /* @__PURE__ */ jsx23(ToastContainer, {})
106187
106455
  ] }) }),
106188
- clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx22(
106456
+ clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx23(
106189
106457
  "div",
106190
106458
  {
106191
106459
  onClick: () => {
@@ -106222,7 +106490,7 @@ var PCBViewer = ({
106222
106490
  justifyContent: "center",
106223
106491
  touchAction: "pan-x pan-y pinch-zoom"
106224
106492
  },
106225
- children: /* @__PURE__ */ jsx22(
106493
+ children: /* @__PURE__ */ jsx23(
106226
106494
  "div",
106227
106495
  {
106228
106496
  style: {