@tscircuit/pcb-viewer 1.11.364 → 1.11.366

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
@@ -6679,6 +6679,7 @@ var createStore = (initialState = {}, disablePcbGroups = false) => createZustand
6679
6679
  DEFAULT_PCB_GROUP_VIEW_MODE
6680
6680
  ),
6681
6681
  hovered_error_id: null,
6682
+ focused_error_id: null,
6682
6683
  ...initialState,
6683
6684
  selectLayer: (layer) => set({ selected_layer: layer }),
6684
6685
  setEditMode: (mode) => set({
@@ -6735,7 +6736,8 @@ var createStore = (initialState = {}, disablePcbGroups = false) => createZustand
6735
6736
  setStoredString(STORAGE_KEYS.PCB_GROUP_VIEW_MODE, mode);
6736
6737
  set({ pcb_group_view_mode: mode });
6737
6738
  },
6738
- setHoveredErrorId: (errorId) => set({ hovered_error_id: errorId })
6739
+ setHoveredErrorId: (errorId) => set({ hovered_error_id: errorId }),
6740
+ setFocusedErrorId: (errorId) => set({ focused_error_id: errorId })
6739
6741
  })
6740
6742
  );
6741
6743
  var useGlobalStore = (s) => {
@@ -6788,7 +6790,7 @@ var ToastContainer = () => {
6788
6790
  };
6789
6791
 
6790
6792
  // src/PCBViewer.tsx
6791
- import { useCallback as useCallback11, useEffect as useEffect17, useMemo as useMemo9, useRef as useRef14, useState as useState13 } from "react";
6793
+ import { useCallback as useCallback12, useEffect as useEffect19, useMemo as useMemo9, useRef as useRef16, useState as useState13 } from "react";
6792
6794
 
6793
6795
  // node_modules/react-use/esm/useMountedState.js
6794
6796
  import { useCallback as useCallback2, useEffect as useEffect2, useRef } from "react";
@@ -6927,7 +6929,7 @@ var useMeasure_default = isBrowser && typeof window.ResizeObserver !== "undefine
6927
6929
  };
6928
6930
 
6929
6931
  // src/PCBViewer.tsx
6930
- import { compose as compose7, scale as scale5, translate as translate11 } from "transformation-matrix";
6932
+ import { compose as compose8, scale as scale6, translate as translate12 } from "transformation-matrix";
6931
6933
 
6932
6934
  // node_modules/use-mouse-matrix-transform/dist/chunk-TGYMZPTI.js
6933
6935
  import { compose, translate as translate5, scale } from "transformation-matrix";
@@ -7406,15 +7408,19 @@ function addInteractionMetadataToPrimitives({
7406
7408
  const newPrimitives = [];
7407
7409
  for (const primitive of primitivesWithoutInteractionMetadata) {
7408
7410
  const newPrimitive = { ...primitive };
7411
+ const primitiveElement = primitive._element;
7412
+ const parentComponent = primitive._parent_pcb_component;
7409
7413
  if (primitive?.layer === "drill") {
7410
7414
  newPrimitive.is_in_highlighted_net = false;
7411
7415
  newPrimitive.is_mouse_over = false;
7412
7416
  } else if (drawingObjectIdsWithMouseOver.has(primitive._pcb_drawing_object_id)) {
7413
7417
  newPrimitive.is_mouse_over = true;
7414
- } else if (primitive._element && ("pcb_trace_id" in primitive._element && primitiveIdsInMousedOverNet.includes(
7415
- primitive._element.pcb_trace_id
7416
- ) || "pcb_port_id" in primitive._element && primitiveIdsInMousedOverNet.includes(
7417
- primitive._element.pcb_port_id
7418
+ } else if (primitiveElement && ("pcb_trace_id" in primitiveElement && primitiveIdsInMousedOverNet.includes(primitiveElement.pcb_trace_id) || "pcb_port_id" in primitiveElement && primitiveIdsInMousedOverNet.includes(
7419
+ primitiveElement.pcb_port_id
7420
+ ) || "pcb_via_id" in primitiveElement && primitiveIdsInMousedOverNet.includes(primitiveElement.pcb_via_id) || "pcb_component_id" in primitiveElement && primitiveIdsInMousedOverNet.includes(
7421
+ primitiveElement.pcb_component_id
7422
+ ) || parentComponent && "pcb_component_id" in parentComponent && primitiveIdsInMousedOverNet.includes(
7423
+ parentComponent.pcb_component_id
7418
7424
  ))) {
7419
7425
  newPrimitive.is_in_highlighted_net = true;
7420
7426
  } else {
@@ -7426,8 +7432,399 @@ function addInteractionMetadataToPrimitives({
7426
7432
  return newPrimitives;
7427
7433
  }
7428
7434
 
7435
+ // src/lib/util/get-error-id.ts
7436
+ var getErrorId = (error, index) => {
7437
+ const explicitIdKeys = [
7438
+ "pcb_trace_error_id",
7439
+ "pcb_via_clearance_error_id",
7440
+ "pcb_component_outside_board_error_id",
7441
+ "source_failed_to_create_component_error_id",
7442
+ "pcb_error_id"
7443
+ ];
7444
+ for (const key of explicitIdKeys) {
7445
+ if (typeof error?.[key] === "string" && error[key].length > 0) {
7446
+ return error[key];
7447
+ }
7448
+ }
7449
+ for (const [key, value] of Object.entries(error ?? {})) {
7450
+ if (key.endsWith("_error_id") && typeof value === "string" && value.length > 0) {
7451
+ return value;
7452
+ }
7453
+ }
7454
+ return `error_${index}_${error?.error_type}_${error?.message?.slice(0, 20)}`;
7455
+ };
7456
+ var findErrorElementById = (elements, errorId) => {
7457
+ if (!errorId) return null;
7458
+ return elements.find((element, index) => {
7459
+ if (!element.type?.includes("error")) return false;
7460
+ return getErrorId(element, index) === errorId;
7461
+ }) ?? null;
7462
+ };
7463
+
7464
+ // src/lib/util/error-preview.ts
7465
+ import {
7466
+ getBoundsFromPoints
7467
+ } from "@tscircuit/math-utils";
7468
+ import { compose as compose3, scale as scale3, translate as translate7 } from "transformation-matrix";
7469
+ var buildErrorPreviewElementIndexes = (elements) => {
7470
+ const tracesById = /* @__PURE__ */ new Map();
7471
+ const portsById = /* @__PURE__ */ new Map();
7472
+ const viasById = /* @__PURE__ */ new Map();
7473
+ const componentsById = /* @__PURE__ */ new Map();
7474
+ const boardsById = /* @__PURE__ */ new Map();
7475
+ for (const element of elements) {
7476
+ if (element.type === "pcb_trace" && element.pcb_trace_id) {
7477
+ tracesById.set(element.pcb_trace_id, element);
7478
+ } else if (element.type === "pcb_port" && element.pcb_port_id) {
7479
+ portsById.set(element.pcb_port_id, element);
7480
+ } else if (element.type === "pcb_via" && element.pcb_via_id) {
7481
+ viasById.set(element.pcb_via_id, element);
7482
+ } else if (element.type === "pcb_component" && element.pcb_component_id) {
7483
+ componentsById.set(element.pcb_component_id, element);
7484
+ } else if (element.type === "pcb_board" && element.pcb_board_id) {
7485
+ boardsById.set(element.pcb_board_id, element);
7486
+ }
7487
+ }
7488
+ return {
7489
+ tracesById,
7490
+ portsById,
7491
+ viasById,
7492
+ componentsById,
7493
+ boardsById
7494
+ };
7495
+ };
7496
+ var mergeBounds2 = (existing, next) => {
7497
+ if (!next) return existing;
7498
+ if (!existing) return next;
7499
+ return {
7500
+ minX: Math.min(existing.minX, next.minX),
7501
+ maxX: Math.max(existing.maxX, next.maxX),
7502
+ minY: Math.min(existing.minY, next.minY),
7503
+ maxY: Math.max(existing.maxY, next.maxY)
7504
+ };
7505
+ };
7506
+ var createBoundsFromCenter = (center, radius = 0.45) => ({
7507
+ minX: center.x - radius,
7508
+ maxX: center.x + radius,
7509
+ minY: center.y - radius,
7510
+ maxY: center.y + radius
7511
+ });
7512
+ var createBoundsFromRoute = (route) => {
7513
+ if (route.length === 0) return null;
7514
+ let bounds = null;
7515
+ for (const point of route) {
7516
+ const radius = Math.max((point.width ?? 0.2) / 2, 0.3);
7517
+ bounds = mergeBounds2(bounds, createBoundsFromCenter(point, radius));
7518
+ }
7519
+ return bounds;
7520
+ };
7521
+ var createBoundsFromComponent = (component) => {
7522
+ if (!component?.center) return null;
7523
+ const width = Math.max(component.width ?? 0, 1.2);
7524
+ const height = Math.max(component.height ?? 0, 1.2);
7525
+ return {
7526
+ minX: component.center.x - width / 2,
7527
+ maxX: component.center.x + width / 2,
7528
+ minY: component.center.y - height / 2,
7529
+ maxY: component.center.y + height / 2
7530
+ };
7531
+ };
7532
+ var createBoardBounds = (board) => {
7533
+ if (!board?.center || typeof board.width !== "number" || typeof board.height !== "number") {
7534
+ return null;
7535
+ }
7536
+ return {
7537
+ minX: board.center.x - board.width / 2,
7538
+ maxX: board.center.x + board.width / 2,
7539
+ minY: board.center.y - board.height / 2,
7540
+ maxY: board.center.y + board.height / 2
7541
+ };
7542
+ };
7543
+ var expandBounds = (bounds, padding) => ({
7544
+ minX: bounds.minX - padding,
7545
+ maxX: bounds.maxX + padding,
7546
+ minY: bounds.minY - padding,
7547
+ maxY: bounds.maxY + padding
7548
+ });
7549
+ var createBoundsFromPoints = (points, radius = 0.35) => {
7550
+ const bounds = getBoundsFromPoints(points);
7551
+ if (!bounds) return null;
7552
+ return expandBounds(bounds, radius);
7553
+ };
7554
+ var getErrorFocusCenter = ({
7555
+ error,
7556
+ tracesById,
7557
+ portsById,
7558
+ viasById,
7559
+ componentsById
7560
+ }) => {
7561
+ if (error.center) return error.center;
7562
+ if (error.pcb_center) return error.pcb_center;
7563
+ if (error.component_center) return error.component_center;
7564
+ if (error.pcb_via_ids?.length) {
7565
+ const vias = error.pcb_via_ids.map((pcbViaId) => viasById.get(pcbViaId)).filter(Boolean);
7566
+ if (vias.length > 0) {
7567
+ return {
7568
+ x: vias.reduce((sum, via) => sum + via.x, 0) / vias.length,
7569
+ y: vias.reduce((sum, via) => sum + via.y, 0) / vias.length
7570
+ };
7571
+ }
7572
+ }
7573
+ if (error.pcb_port_ids?.length) {
7574
+ const ports = error.pcb_port_ids.map((pcbPortId) => portsById.get(pcbPortId)).filter(Boolean);
7575
+ if (ports.length > 0) {
7576
+ return {
7577
+ x: ports.reduce((sum, port) => sum + port.x, 0) / ports.length,
7578
+ y: ports.reduce((sum, port) => sum + port.y, 0) / ports.length
7579
+ };
7580
+ }
7581
+ }
7582
+ if (error.pcb_trace_id) {
7583
+ const trace = tracesById.get(error.pcb_trace_id);
7584
+ if (trace?.route?.length) {
7585
+ const middleIndex = Math.floor(trace.route.length / 2);
7586
+ return {
7587
+ x: trace.route[middleIndex].x,
7588
+ y: trace.route[middleIndex].y
7589
+ };
7590
+ }
7591
+ }
7592
+ if (error.pcb_component_id) {
7593
+ return componentsById.get(error.pcb_component_id)?.center ?? null;
7594
+ }
7595
+ return null;
7596
+ };
7597
+ var getErrorFocusPoint = ({
7598
+ error,
7599
+ indexes
7600
+ }) => getErrorFocusCenter({
7601
+ error,
7602
+ ...indexes
7603
+ });
7604
+ var getComponentBoundaryViolationBounds = ({
7605
+ error,
7606
+ boardsById
7607
+ }) => {
7608
+ if (!error.component_bounds || !error.pcb_board_id) return null;
7609
+ const boardBounds = createBoardBounds(boardsById.get(error.pcb_board_id));
7610
+ if (!boardBounds) return null;
7611
+ const componentBounds = error.component_bounds;
7612
+ const violationPoints = [];
7613
+ if (componentBounds.min_x < boardBounds.minX) {
7614
+ violationPoints.push({
7615
+ x: boardBounds.minX,
7616
+ y: Math.max(componentBounds.min_y, boardBounds.minY)
7617
+ });
7618
+ violationPoints.push({
7619
+ x: boardBounds.minX,
7620
+ y: Math.min(componentBounds.max_y, boardBounds.maxY)
7621
+ });
7622
+ }
7623
+ if (componentBounds.max_x > boardBounds.maxX) {
7624
+ violationPoints.push({
7625
+ x: boardBounds.maxX,
7626
+ y: Math.max(componentBounds.min_y, boardBounds.minY)
7627
+ });
7628
+ violationPoints.push({
7629
+ x: boardBounds.maxX,
7630
+ y: Math.min(componentBounds.max_y, boardBounds.maxY)
7631
+ });
7632
+ }
7633
+ if (componentBounds.min_y < boardBounds.minY) {
7634
+ violationPoints.push({
7635
+ x: Math.max(componentBounds.min_x, boardBounds.minX),
7636
+ y: boardBounds.minY
7637
+ });
7638
+ violationPoints.push({
7639
+ x: Math.min(componentBounds.max_x, boardBounds.maxX),
7640
+ y: boardBounds.minY
7641
+ });
7642
+ }
7643
+ if (componentBounds.max_y > boardBounds.maxY) {
7644
+ violationPoints.push({
7645
+ x: Math.max(componentBounds.min_x, boardBounds.minX),
7646
+ y: boardBounds.maxY
7647
+ });
7648
+ violationPoints.push({
7649
+ x: Math.min(componentBounds.max_x, boardBounds.maxX),
7650
+ y: boardBounds.maxY
7651
+ });
7652
+ }
7653
+ return createBoundsFromPoints(violationPoints, 0.3);
7654
+ };
7655
+ var nonZoomableErrorTypes = /* @__PURE__ */ new Set([
7656
+ "source_failed_to_create_component_error"
7657
+ ]);
7658
+ var getErrorPreviewBounds = ({
7659
+ error,
7660
+ indexes
7661
+ }) => {
7662
+ if (nonZoomableErrorTypes.has(error.error_type) || nonZoomableErrorTypes.has(error.type)) {
7663
+ return null;
7664
+ }
7665
+ let bounds = null;
7666
+ const focusCenter = getErrorFocusCenter({
7667
+ error,
7668
+ ...indexes
7669
+ });
7670
+ if (focusCenter) {
7671
+ bounds = mergeBounds2(bounds, createBoundsFromCenter(focusCenter, 0.35));
7672
+ }
7673
+ if (error.error_type === "pcb_component_outside_board_error") {
7674
+ bounds = mergeBounds2(
7675
+ bounds,
7676
+ getComponentBoundaryViolationBounds({
7677
+ error,
7678
+ boardsById: indexes.boardsById
7679
+ })
7680
+ );
7681
+ } else if (error.component_bounds && !focusCenter) {
7682
+ bounds = mergeBounds2(bounds, {
7683
+ minX: error.component_bounds.min_x,
7684
+ maxX: error.component_bounds.max_x,
7685
+ minY: error.component_bounds.min_y,
7686
+ maxY: error.component_bounds.max_y
7687
+ });
7688
+ }
7689
+ if (error.center && !focusCenter) {
7690
+ bounds = mergeBounds2(bounds, createBoundsFromCenter(error.center));
7691
+ }
7692
+ if (error.pcb_center && !focusCenter) {
7693
+ bounds = mergeBounds2(bounds, createBoundsFromCenter(error.pcb_center));
7694
+ }
7695
+ if (error.component_center && !focusCenter) {
7696
+ bounds = mergeBounds2(bounds, createBoundsFromCenter(error.component_center));
7697
+ }
7698
+ if (error.pcb_port_ids && !focusCenter) {
7699
+ for (const pcbPortId of error.pcb_port_ids) {
7700
+ const port = indexes.portsById.get(pcbPortId);
7701
+ if (port) {
7702
+ bounds = mergeBounds2(
7703
+ bounds,
7704
+ createBoundsFromCenter({ x: port.x, y: port.y }, 0.4)
7705
+ );
7706
+ }
7707
+ }
7708
+ }
7709
+ if (error.pcb_trace_id && !focusCenter) {
7710
+ const trace = indexes.tracesById.get(error.pcb_trace_id);
7711
+ if (trace?.route) {
7712
+ bounds = mergeBounds2(bounds, createBoundsFromRoute(trace.route));
7713
+ }
7714
+ }
7715
+ if (error.pcb_via_ids) {
7716
+ for (const pcbViaId of error.pcb_via_ids) {
7717
+ const via = indexes.viasById.get(pcbViaId);
7718
+ if (via) {
7719
+ bounds = mergeBounds2(
7720
+ bounds,
7721
+ createBoundsFromCenter(
7722
+ { x: via.x, y: via.y },
7723
+ Math.max((via.outer_diameter ?? 0.5) / 2, 0.28)
7724
+ )
7725
+ );
7726
+ }
7727
+ }
7728
+ }
7729
+ if (error.pcb_component_id && !focusCenter) {
7730
+ bounds = mergeBounds2(
7731
+ bounds,
7732
+ createBoundsFromComponent(
7733
+ indexes.componentsById.get(error.pcb_component_id)
7734
+ )
7735
+ );
7736
+ }
7737
+ if (error.pcb_component_ids && !focusCenter) {
7738
+ for (const pcbComponentId of error.pcb_component_ids) {
7739
+ bounds = mergeBounds2(
7740
+ bounds,
7741
+ createBoundsFromComponent(indexes.componentsById.get(pcbComponentId))
7742
+ );
7743
+ }
7744
+ }
7745
+ return bounds;
7746
+ };
7747
+ var createTransformForBounds = ({
7748
+ bounds,
7749
+ width,
7750
+ height
7751
+ }) => {
7752
+ const center = {
7753
+ x: (bounds.minX + bounds.maxX) / 2,
7754
+ y: (bounds.minY + bounds.maxY) / 2
7755
+ };
7756
+ const targetWidth = Math.max(bounds.maxX - bounds.minX, 0.8) + 1.4;
7757
+ const targetHeight = Math.max(bounds.maxY - bounds.minY, 0.8) + 1.4;
7758
+ const scaleFactor = Math.min(width / targetWidth, height / targetHeight, 240) * 0.92;
7759
+ return compose3(
7760
+ translate7(width / 2, height / 2),
7761
+ scale3(scaleFactor, -scaleFactor, 0, 0),
7762
+ translate7(-center.x, -center.y)
7763
+ );
7764
+ };
7765
+ var getRelatedIdsForError = (error) => {
7766
+ const relatedIds = /* @__PURE__ */ new Set();
7767
+ if (error.pcb_trace_id) {
7768
+ relatedIds.add(error.pcb_trace_id);
7769
+ }
7770
+ for (const pcbPortId of error.pcb_port_ids ?? []) {
7771
+ relatedIds.add(pcbPortId);
7772
+ }
7773
+ for (const pcbViaId of error.pcb_via_ids ?? []) {
7774
+ relatedIds.add(pcbViaId);
7775
+ }
7776
+ if (error.pcb_component_id) {
7777
+ relatedIds.add(error.pcb_component_id);
7778
+ }
7779
+ for (const pcbComponentId of error.pcb_component_ids ?? []) {
7780
+ relatedIds.add(pcbComponentId);
7781
+ }
7782
+ return [...relatedIds];
7783
+ };
7784
+
7785
+ // src/lib/util/transform-animation.ts
7786
+ var easeOutCubic = (progress) => 1 - (1 - progress) ** 3;
7787
+ var interpolateTransform = (start, end, progress) => ({
7788
+ a: start.a + (end.a - start.a) * progress,
7789
+ b: start.b + (end.b - start.b) * progress,
7790
+ c: start.c + (end.c - start.c) * progress,
7791
+ d: start.d + (end.d - start.d) * progress,
7792
+ e: start.e + (end.e - start.e) * progress,
7793
+ f: start.f + (end.f - start.f) * progress
7794
+ });
7795
+ var cancelTransformAnimation = (animationFrameId) => {
7796
+ if (animationFrameId !== null) {
7797
+ window.cancelAnimationFrame(animationFrameId);
7798
+ }
7799
+ };
7800
+ var animateTransform = ({
7801
+ startTransform,
7802
+ endTransform,
7803
+ durationMs,
7804
+ onUpdate,
7805
+ setAnimationFrameId,
7806
+ onComplete
7807
+ }) => {
7808
+ const startTime = performance.now();
7809
+ const tick = (timestamp) => {
7810
+ const elapsed = timestamp - startTime;
7811
+ const rawProgress = Math.min(1, elapsed / durationMs);
7812
+ const easedProgress = easeOutCubic(rawProgress);
7813
+ onUpdate(interpolateTransform(startTransform, endTransform, easedProgress));
7814
+ if (rawProgress < 1) {
7815
+ setAnimationFrameId(window.requestAnimationFrame(tick));
7816
+ return;
7817
+ }
7818
+ setAnimationFrameId(null);
7819
+ onComplete?.();
7820
+ };
7821
+ const animationFrameId = window.requestAnimationFrame(tick);
7822
+ setAnimationFrameId(animationFrameId);
7823
+ return animationFrameId;
7824
+ };
7825
+
7429
7826
  // src/components/CanvasElementsRenderer.tsx
7430
- import { useCallback as useCallback10, useMemo as useMemo8, useState as useState12 } from "react";
7827
+ import { useCallback as useCallback11, useEffect as useEffect18, useMemo as useMemo8, useRef as useRef15, useState as useState12 } from "react";
7431
7828
 
7432
7829
  // src/lib/util/expand-stroke.ts
7433
7830
  function getExpandedStroke(strokeInput, defaultWidth) {
@@ -7481,16 +7878,16 @@ function getExpandedStroke(strokeInput, defaultWidth) {
7481
7878
  addPoint(current, normalPrev, -1, currentWidth);
7482
7879
  addPoint(current, normalNext, -1, currentWidth);
7483
7880
  } else {
7484
- const scale6 = 1 / miterLength;
7881
+ const scale7 = 1 / miterLength;
7485
7882
  addPoint(
7486
7883
  current,
7487
- { x: miterX * scale6, y: miterY * scale6 },
7884
+ { x: miterX * scale7, y: miterY * scale7 },
7488
7885
  1,
7489
7886
  currentWidth
7490
7887
  );
7491
7888
  addPoint(
7492
7889
  current,
7493
- { x: miterX * scale6, y: miterY * scale6 },
7890
+ { x: miterX * scale7, y: miterY * scale7 },
7494
7891
  -1,
7495
7892
  currentWidth
7496
7893
  );
@@ -8180,10 +8577,10 @@ var convertElementToPrimitives = (element, allElements) => {
8180
8577
  import colorParser from "color";
8181
8578
  import {
8182
8579
  applyToPoint as applyToPoint3,
8183
- compose as compose3,
8580
+ compose as compose4,
8184
8581
  identity as identity2,
8185
8582
  inverse,
8186
- translate as translate7
8583
+ translate as translate8
8187
8584
  } from "transformation-matrix";
8188
8585
 
8189
8586
  // src/lib/colors.ts
@@ -8524,7 +8921,7 @@ var Drawer = class {
8524
8921
  );
8525
8922
  this.transform = identity2();
8526
8923
  this.transform.d *= -1;
8527
- this.transform = compose3(this.transform, translate7(0, -500));
8924
+ this.transform = compose4(this.transform, translate8(0, -500));
8528
8925
  this.lastPoint = { x: 0, y: 0 };
8529
8926
  this.equip({});
8530
8927
  }
@@ -8619,10 +9016,10 @@ var Drawer = class {
8619
9016
  }
8620
9017
  if (is_stroke_dashed) {
8621
9018
  let dashPattern = [];
8622
- const scale6 = Math.abs(this.transform.a);
8623
- if (scale6 > 0) {
9019
+ const scale7 = Math.abs(this.transform.a);
9020
+ if (scale7 > 0) {
8624
9021
  const SEGMENT_LENGTH = 0.1;
8625
- const dash = SEGMENT_LENGTH * scale6;
9022
+ const dash = SEGMENT_LENGTH * scale7;
8626
9023
  const gap = dash * 1.3;
8627
9024
  if (dash > 0.5) {
8628
9025
  dashPattern = [dash, gap];
@@ -9369,16 +9766,16 @@ function drawPlatedHolePads({
9369
9766
  }
9370
9767
 
9371
9768
  // src/lib/util/rotate-text.ts
9372
- import { compose as compose4, translate as translate8, rotate, applyToPoint as applyToPoint4 } from "transformation-matrix";
9769
+ import { compose as compose5, translate as translate9, rotate, applyToPoint as applyToPoint4 } from "transformation-matrix";
9373
9770
  function rotateText(rotateTextParams) {
9374
9771
  const { lines, anchorPoint, ccwRotation } = rotateTextParams;
9375
9772
  if (!ccwRotation) return lines;
9376
9773
  const rad = ccwRotation * Math.PI / 180;
9377
9774
  const rotationMatrix = rotate(rad);
9378
- const transform = compose4(
9379
- translate8(anchorPoint.x, anchorPoint.y),
9775
+ const transform = compose5(
9776
+ translate9(anchorPoint.x, anchorPoint.y),
9380
9777
  rotationMatrix,
9381
- translate8(-anchorPoint.x, -anchorPoint.y)
9778
+ translate9(-anchorPoint.x, -anchorPoint.y)
9382
9779
  );
9383
9780
  applyToPoint4(transform, anchorPoint);
9384
9781
  return lines.map((line) => ({
@@ -10100,19 +10497,12 @@ var CanvasPrimitiveRenderer = ({
10100
10497
  drawSoldermask: isShowingSolderMask
10101
10498
  });
10102
10499
  }
10103
- if (topCanvas) {
10104
- drawCopperPourElementsForLayer({
10105
- canvas: topCanvas,
10106
- elements,
10107
- layers: ["top_copper"],
10108
- realToCanvasMat: transform
10109
- });
10110
- }
10111
- if (bottomCanvas) {
10500
+ for (const { canvas, copperLayer } of copperLayers) {
10501
+ if (!canvas) continue;
10112
10502
  drawCopperPourElementsForLayer({
10113
- canvas: bottomCanvas,
10503
+ canvas,
10114
10504
  elements,
10115
- layers: ["bottom_copper"],
10505
+ layers: [copperLayer],
10116
10506
  realToCanvasMat: transform
10117
10507
  });
10118
10508
  }
@@ -10176,22 +10566,6 @@ var CanvasPrimitiveRenderer = ({
10176
10566
  });
10177
10567
  }
10178
10568
  }
10179
- if (topCanvas) {
10180
- drawCopperPourElementsForLayer({
10181
- canvas: topCanvas,
10182
- elements,
10183
- layers: ["top_copper"],
10184
- realToCanvasMat: transform
10185
- });
10186
- }
10187
- if (bottomCanvas) {
10188
- drawCopperPourElementsForLayer({
10189
- canvas: bottomCanvas,
10190
- elements,
10191
- layers: ["bottom_copper"],
10192
- realToCanvasMat: transform
10193
- });
10194
- }
10195
10569
  const drillCanvas = canvasRefs.current.drill;
10196
10570
  if (drillCanvas) {
10197
10571
  drawPcbHoleElementsForLayer({
@@ -10394,17 +10768,17 @@ var FONT_SIZE_HEIGHT_RATIO = 1;
10394
10768
  var import_svgson = __toESM(require_svgson_umd(), 1);
10395
10769
  var import_pretty = __toESM(require_pretty(), 1);
10396
10770
  import {
10397
- compose as compose5,
10398
- translate as translate9,
10399
- scale as scale3,
10771
+ compose as compose6,
10772
+ translate as translate10,
10773
+ scale as scale4,
10400
10774
  applyToPoint as applyToPoint5
10401
10775
  } from "transformation-matrix";
10402
10776
 
10403
10777
  // node_modules/graphics-debug/dist/chunk-ARYXS3GC.js
10404
10778
  import {
10405
- compose as compose6,
10406
- scale as scale4,
10407
- translate as translate10,
10779
+ compose as compose7,
10780
+ scale as scale5,
10781
+ translate as translate11,
10408
10782
  applyToPoint as applyToPoint6
10409
10783
  } from "transformation-matrix";
10410
10784
  var defaultColors = [
@@ -10467,10 +10841,10 @@ function computeTransformFromViewbox(viewbox, canvasWidth, canvasHeight, options
10467
10841
  (canvasWidth - 2 * padding) / width,
10468
10842
  (canvasHeight - 2 * padding) / height
10469
10843
  );
10470
- return compose6(
10471
- translate10(canvasWidth / 2, canvasHeight / 2),
10472
- scale4(scale_factor, yFlip ? -scale_factor : scale_factor),
10473
- translate10(-(bounds.minX + width / 2), -(bounds.minY + height / 2))
10844
+ return compose7(
10845
+ translate11(canvasWidth / 2, canvasHeight / 2),
10846
+ scale5(scale_factor, yFlip ? -scale_factor : scale_factor),
10847
+ translate11(-(bounds.minX + width / 2), -(bounds.minY + height / 2))
10474
10848
  );
10475
10849
  }
10476
10850
  function getBounds(graphics) {
@@ -12297,15 +12671,94 @@ var getPopupPosition = (errorCenter, containerRef) => {
12297
12671
  };
12298
12672
  };
12299
12673
 
12674
+ // src/components/FocusMarkerSVG.tsx
12675
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
12676
+ var FocusMarkerSVG = ({
12677
+ center
12678
+ }) => /* @__PURE__ */ jsxs8(
12679
+ "svg",
12680
+ {
12681
+ style: {
12682
+ position: "absolute",
12683
+ left: 0,
12684
+ top: 0,
12685
+ pointerEvents: "none",
12686
+ mixBlendMode: "difference",
12687
+ zIndex: zIndexMap.errorOverlay + 30
12688
+ },
12689
+ width: "100%",
12690
+ height: "100%",
12691
+ children: [
12692
+ /* @__PURE__ */ jsx10(
12693
+ "circle",
12694
+ {
12695
+ cx: center.x,
12696
+ cy: center.y,
12697
+ r: 11,
12698
+ fill: "none",
12699
+ stroke: "#ff6b6b",
12700
+ strokeWidth: 2.5,
12701
+ opacity: 0.95
12702
+ }
12703
+ ),
12704
+ /* @__PURE__ */ jsx10("circle", { cx: center.x, cy: center.y, r: 4.5, fill: "#ff6b6b", opacity: 0.95 }),
12705
+ /* @__PURE__ */ jsx10(
12706
+ "line",
12707
+ {
12708
+ x1: center.x - 18,
12709
+ y1: center.y,
12710
+ x2: center.x - 8,
12711
+ y2: center.y,
12712
+ stroke: "#ff6b6b",
12713
+ strokeWidth: 2
12714
+ }
12715
+ ),
12716
+ /* @__PURE__ */ jsx10(
12717
+ "line",
12718
+ {
12719
+ x1: center.x + 8,
12720
+ y1: center.y,
12721
+ x2: center.x + 18,
12722
+ y2: center.y,
12723
+ stroke: "#ff6b6b",
12724
+ strokeWidth: 2
12725
+ }
12726
+ ),
12727
+ /* @__PURE__ */ jsx10(
12728
+ "line",
12729
+ {
12730
+ x1: center.x,
12731
+ y1: center.y - 18,
12732
+ x2: center.x,
12733
+ y2: center.y - 8,
12734
+ stroke: "#ff6b6b",
12735
+ strokeWidth: 2
12736
+ }
12737
+ ),
12738
+ /* @__PURE__ */ jsx10(
12739
+ "line",
12740
+ {
12741
+ x1: center.x,
12742
+ y1: center.y + 8,
12743
+ x2: center.x,
12744
+ y2: center.y + 18,
12745
+ stroke: "#ff6b6b",
12746
+ strokeWidth: 2
12747
+ }
12748
+ )
12749
+ ]
12750
+ }
12751
+ );
12752
+
12300
12753
  // src/components/ErrorOverlay.tsx
12301
- import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
12754
+ import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
12302
12755
  var ErrorSVG = ({
12303
12756
  screenPort1,
12304
12757
  screenPort2,
12305
12758
  errorCenter,
12306
12759
  canLineBeDrawn,
12307
12760
  isHighlighted = false
12308
- }) => /* @__PURE__ */ jsx10(
12761
+ }) => /* @__PURE__ */ jsx11(
12309
12762
  "svg",
12310
12763
  {
12311
12764
  style: {
@@ -12318,8 +12771,8 @@ var ErrorSVG = ({
12318
12771
  },
12319
12772
  width: "100%",
12320
12773
  height: "100%",
12321
- children: canLineBeDrawn && /* @__PURE__ */ jsxs8(Fragment4, { children: [
12322
- /* @__PURE__ */ jsx10(
12774
+ children: canLineBeDrawn && /* @__PURE__ */ jsxs9(Fragment4, { children: [
12775
+ /* @__PURE__ */ jsx11(
12323
12776
  "line",
12324
12777
  {
12325
12778
  x1: screenPort1.x,
@@ -12331,7 +12784,7 @@ var ErrorSVG = ({
12331
12784
  stroke: isHighlighted ? "#ff4444" : "red"
12332
12785
  }
12333
12786
  ),
12334
- /* @__PURE__ */ jsx10(
12787
+ /* @__PURE__ */ jsx11(
12335
12788
  "line",
12336
12789
  {
12337
12790
  x1: errorCenter.x,
@@ -12343,7 +12796,7 @@ var ErrorSVG = ({
12343
12796
  stroke: isHighlighted ? "#ff4444" : "red"
12344
12797
  }
12345
12798
  ),
12346
- isHighlighted ? /* @__PURE__ */ jsx10(
12799
+ isHighlighted ? /* @__PURE__ */ jsx11(
12347
12800
  "rect",
12348
12801
  {
12349
12802
  x: errorCenter.x - 7,
@@ -12353,7 +12806,7 @@ var ErrorSVG = ({
12353
12806
  transform: `rotate(45 ${errorCenter.x} ${errorCenter.y})`,
12354
12807
  fill: "#ff4444"
12355
12808
  }
12356
- ) : /* @__PURE__ */ jsx10("circle", { cx: errorCenter.x, cy: errorCenter.y, r: 5, fill: "red" })
12809
+ ) : /* @__PURE__ */ jsx11("circle", { cx: errorCenter.x, cy: errorCenter.y, r: 5, fill: "red" })
12357
12810
  ] })
12358
12811
  }
12359
12812
  );
@@ -12361,7 +12814,7 @@ var RouteSVG = ({
12361
12814
  points,
12362
12815
  errorCenter,
12363
12816
  isHighlighted = false
12364
- }) => /* @__PURE__ */ jsxs8(
12817
+ }) => /* @__PURE__ */ jsxs9(
12365
12818
  "svg",
12366
12819
  {
12367
12820
  style: {
@@ -12375,7 +12828,7 @@ var RouteSVG = ({
12375
12828
  width: "100%",
12376
12829
  height: "100%",
12377
12830
  children: [
12378
- points.length > 1 && /* @__PURE__ */ jsx10(
12831
+ points.length > 1 && /* @__PURE__ */ jsx11(
12379
12832
  "polyline",
12380
12833
  {
12381
12834
  points: points.map((pt) => `${pt.x},${pt.y}`).join(" "),
@@ -12385,7 +12838,7 @@ var RouteSVG = ({
12385
12838
  strokeDasharray: "2,2"
12386
12839
  }
12387
12840
  ),
12388
- isHighlighted ? /* @__PURE__ */ jsx10(
12841
+ isHighlighted ? /* @__PURE__ */ jsx11(
12389
12842
  "rect",
12390
12843
  {
12391
12844
  x: errorCenter.x - 7,
@@ -12395,7 +12848,7 @@ var RouteSVG = ({
12395
12848
  transform: `rotate(45 ${errorCenter.x} ${errorCenter.y})`,
12396
12849
  fill: "#ff4444"
12397
12850
  }
12398
- ) : /* @__PURE__ */ jsx10("circle", { cx: errorCenter.x, cy: errorCenter.y, r: 5, fill: "red" })
12851
+ ) : /* @__PURE__ */ jsx11("circle", { cx: errorCenter.x, cy: errorCenter.y, r: 5, fill: "red" })
12399
12852
  ]
12400
12853
  }
12401
12854
  );
@@ -12419,12 +12872,16 @@ var ErrorOverlay = ({
12419
12872
  elements
12420
12873
  }) => {
12421
12874
  const containerRef = useRef9(null);
12422
- const { isShowingDRCErrors, hoveredErrorId } = useGlobalStore((state) => ({
12423
- isShowingDRCErrors: state.is_showing_drc_errors,
12424
- hoveredErrorId: state.hovered_error_id
12425
- }));
12875
+ const { isShowingDRCErrors, hoveredErrorId, focusedErrorId } = useGlobalStore(
12876
+ (state) => ({
12877
+ isShowingDRCErrors: state.is_showing_drc_errors,
12878
+ hoveredErrorId: state.hovered_error_id,
12879
+ focusedErrorId: state.focused_error_id
12880
+ })
12881
+ );
12882
+ const activeErrorId = focusedErrorId ?? hoveredErrorId;
12426
12883
  if (!elements) {
12427
- return /* @__PURE__ */ jsx10("div", { style: { position: "relative" }, ref: containerRef, children });
12884
+ return /* @__PURE__ */ jsx11("div", { style: { position: "relative" }, ref: containerRef, children });
12428
12885
  }
12429
12886
  const traceErrors = elements.filter(
12430
12887
  (el) => el.type === "pcb_trace_error"
@@ -12435,21 +12892,30 @@ var ErrorOverlay = ({
12435
12892
  const componentErrors = elements.filter(
12436
12893
  (el) => el.type === "pcb_trace_error" && el.message?.includes("Multiple components found with name")
12437
12894
  );
12438
- const portsMap = /* @__PURE__ */ new Map();
12439
- elements.forEach((el) => {
12440
- if (el.type === "pcb_port") {
12441
- portsMap.set(el.pcb_port_id, el);
12895
+ const elementIndexes = buildErrorPreviewElementIndexes(elements);
12896
+ const focusedErrorElement = findErrorElementById(elements, activeErrorId);
12897
+ let focusScreenCenter = null;
12898
+ if (focusedErrorElement) {
12899
+ const focusCenter = getErrorFocusPoint({
12900
+ error: focusedErrorElement,
12901
+ indexes: elementIndexes
12902
+ });
12903
+ if (focusCenter && typeof focusCenter.x === "number" && typeof focusCenter.y === "number") {
12904
+ const screenCenter = applyToPoint11(transform, focusCenter);
12905
+ if (!isNaN(screenCenter.x) && !isNaN(screenCenter.y)) {
12906
+ focusScreenCenter = screenCenter;
12907
+ }
12442
12908
  }
12443
- });
12444
- return /* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, ref: containerRef, children: [
12909
+ }
12910
+ return /* @__PURE__ */ jsxs9("div", { style: { position: "relative" }, ref: containerRef, children: [
12445
12911
  children,
12446
12912
  traceErrors.map((el, index) => {
12447
12913
  const { pcb_port_ids, pcb_trace_id } = el;
12448
- const port1 = pcb_port_ids?.[0] ? portsMap.get(pcb_port_ids[0]) : void 0;
12449
- const port2 = pcb_port_ids?.[1] ? portsMap.get(pcb_port_ids[1]) : void 0;
12450
- const trace = elements ? su(elements).pcb_trace.get(pcb_trace_id) : void 0;
12451
- const errorId = el.pcb_trace_error_id;
12452
- const isHighlighted = hoveredErrorId === errorId;
12914
+ const port1 = pcb_port_ids?.[0] ? elementIndexes.portsById.get(pcb_port_ids[0]) : void 0;
12915
+ const port2 = pcb_port_ids?.[1] ? elementIndexes.portsById.get(pcb_port_ids[1]) : void 0;
12916
+ const trace = pcb_trace_id ? elementIndexes.tracesById.get(pcb_trace_id) : void 0;
12917
+ const errorId = getErrorId(el, index);
12918
+ const isHighlighted = activeErrorId === errorId;
12453
12919
  if (port1 && port2) {
12454
12920
  const screenPort1 = applyToPoint11(transform, {
12455
12921
  x: port1.x,
@@ -12471,8 +12937,8 @@ var ErrorOverlay = ({
12471
12937
  return null;
12472
12938
  }
12473
12939
  const popupPosition = getPopupPosition(errorCenter, containerRef);
12474
- return /* @__PURE__ */ jsxs8(Fragment3, { children: [
12475
- /* @__PURE__ */ jsx10(
12940
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
12941
+ /* @__PURE__ */ jsx11(
12476
12942
  ErrorSVG,
12477
12943
  {
12478
12944
  screenPort1,
@@ -12482,7 +12948,7 @@ var ErrorOverlay = ({
12482
12948
  isHighlighted
12483
12949
  }
12484
12950
  ),
12485
- /* @__PURE__ */ jsx10(
12951
+ /* @__PURE__ */ jsx11(
12486
12952
  "div",
12487
12953
  {
12488
12954
  style: {
@@ -12517,7 +12983,7 @@ var ErrorOverlay = ({
12517
12983
  }
12518
12984
  }
12519
12985
  ),
12520
- /* @__PURE__ */ jsx10(
12986
+ /* @__PURE__ */ jsx11(
12521
12987
  "div",
12522
12988
  {
12523
12989
  style: {
@@ -12535,7 +13001,7 @@ var ErrorOverlay = ({
12535
13001
  pointerEvents: "none",
12536
13002
  transform: popupPosition.transform
12537
13003
  },
12538
- children: /* @__PURE__ */ jsx10(
13004
+ children: /* @__PURE__ */ jsx11(
12539
13005
  "div",
12540
13006
  {
12541
13007
  className: `error-message ${errorMessageStyles}`,
@@ -12554,13 +13020,15 @@ var ErrorOverlay = ({
12554
13020
  const screenPoints = trace.route.map(
12555
13021
  (pt) => applyToPoint11(transform, { x: pt.x, y: pt.y })
12556
13022
  );
12557
- if (screenPoints.some((pt) => Number.isNaN(pt.x) || Number.isNaN(pt.y)))
13023
+ if (screenPoints.some(
13024
+ (pt) => Number.isNaN(pt.x) || Number.isNaN(pt.y)
13025
+ ))
12558
13026
  return null;
12559
13027
  const mid = Math.floor(screenPoints.length / 2);
12560
13028
  const errorCenter = screenPoints[mid];
12561
13029
  const popupPosition = getPopupPosition(errorCenter, containerRef);
12562
- return /* @__PURE__ */ jsxs8(Fragment3, { children: [
12563
- /* @__PURE__ */ jsx10(
13030
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
13031
+ /* @__PURE__ */ jsx11(
12564
13032
  RouteSVG,
12565
13033
  {
12566
13034
  points: screenPoints,
@@ -12568,7 +13036,7 @@ var ErrorOverlay = ({
12568
13036
  isHighlighted
12569
13037
  }
12570
13038
  ),
12571
- /* @__PURE__ */ jsx10(
13039
+ /* @__PURE__ */ jsx11(
12572
13040
  "div",
12573
13041
  {
12574
13042
  style: {
@@ -12603,7 +13071,7 @@ var ErrorOverlay = ({
12603
13071
  }
12604
13072
  }
12605
13073
  ),
12606
- /* @__PURE__ */ jsx10(
13074
+ /* @__PURE__ */ jsx11(
12607
13075
  "div",
12608
13076
  {
12609
13077
  style: {
@@ -12621,7 +13089,7 @@ var ErrorOverlay = ({
12621
13089
  pointerEvents: "none",
12622
13090
  transform: popupPosition.transform
12623
13091
  },
12624
- children: /* @__PURE__ */ jsx10(
13092
+ children: /* @__PURE__ */ jsx11(
12625
13093
  "div",
12626
13094
  {
12627
13095
  className: `error-message ${errorMessageStyles}`,
@@ -12640,8 +13108,8 @@ var ErrorOverlay = ({
12640
13108
  }),
12641
13109
  viaClearanceErrors.map((el, index) => {
12642
13110
  if (!el.pcb_center) return null;
12643
- const errorId = el.pcb_via_ids;
12644
- const isHighlighted = hoveredErrorId === errorId[0];
13111
+ const errorId = getErrorId(el, index);
13112
+ const isHighlighted = activeErrorId === errorId;
12645
13113
  if (!isHighlighted && !isShowingDRCErrors) return null;
12646
13114
  const errorCenter = applyToPoint11(transform, {
12647
13115
  x: el.pcb_center.x,
@@ -12653,8 +13121,8 @@ var ErrorOverlay = ({
12653
13121
  { x: errorCenter.x, y: errorCenter.y },
12654
13122
  containerRef
12655
13123
  );
12656
- return /* @__PURE__ */ jsxs8(Fragment3, { children: [
12657
- /* @__PURE__ */ jsx10(
13124
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
13125
+ /* @__PURE__ */ jsx11(
12658
13126
  RouteSVG,
12659
13127
  {
12660
13128
  points: [],
@@ -12662,7 +13130,7 @@ var ErrorOverlay = ({
12662
13130
  isHighlighted
12663
13131
  }
12664
13132
  ),
12665
- /* @__PURE__ */ jsx10(
13133
+ /* @__PURE__ */ jsx11(
12666
13134
  "div",
12667
13135
  {
12668
13136
  style: {
@@ -12697,7 +13165,7 @@ var ErrorOverlay = ({
12697
13165
  }
12698
13166
  }
12699
13167
  ),
12700
- /* @__PURE__ */ jsx10(
13168
+ /* @__PURE__ */ jsx11(
12701
13169
  "div",
12702
13170
  {
12703
13171
  style: {
@@ -12715,7 +13183,7 @@ var ErrorOverlay = ({
12715
13183
  pointerEvents: "none",
12716
13184
  transform: popupPosition.transform
12717
13185
  },
12718
- children: /* @__PURE__ */ jsx10(
13186
+ children: /* @__PURE__ */ jsx11(
12719
13187
  "div",
12720
13188
  {
12721
13189
  className: `error-message ${errorMessageStyles}`,
@@ -12728,7 +13196,7 @@ var ErrorOverlay = ({
12728
13196
  )
12729
13197
  }
12730
13198
  )
12731
- ] }, errorId[0]);
13199
+ ] }, errorId);
12732
13200
  }),
12733
13201
  componentErrors.map((el, index) => {
12734
13202
  const componentName = el.component_name || el.message?.match(/name "([^"]+)"/)?.[1];
@@ -12738,8 +13206,8 @@ var ErrorOverlay = ({
12738
13206
  (src) => src.type === "source_component" && src.source_component_id === comp.source_component_id && src.name === componentName
12739
13207
  )
12740
13208
  ) || [];
12741
- const errorId = el.error_id;
12742
- const isHighlighted = hoveredErrorId === errorId;
13209
+ const errorId = getErrorId(el, index);
13210
+ const isHighlighted = activeErrorId === errorId;
12743
13211
  if (!isHighlighted && !isShowingDRCErrors) return null;
12744
13212
  return components.map((comp, compIndex) => {
12745
13213
  let center = { x: 0, y: 0 };
@@ -12755,17 +13223,17 @@ var ErrorOverlay = ({
12755
13223
  const screenCenter = applyToPoint11(transform, center);
12756
13224
  if (Number.isNaN(screenCenter.x) || Number.isNaN(screenCenter.y))
12757
13225
  return null;
12758
- const scale6 = Math.abs(transform.a);
13226
+ const scale7 = Math.abs(transform.a);
12759
13227
  const baseRadius = 0.5;
12760
13228
  const minRadius = 8;
12761
13229
  const maxRadius = 30;
12762
13230
  const scaledRadius = Math.max(
12763
13231
  minRadius,
12764
- Math.min(maxRadius, baseRadius * scale6)
13232
+ Math.min(maxRadius, baseRadius * scale7)
12765
13233
  );
12766
13234
  const popupPosition = getPopupPosition(screenCenter, containerRef);
12767
- return /* @__PURE__ */ jsxs8(Fragment3, { children: [
12768
- /* @__PURE__ */ jsx10(
13235
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
13236
+ /* @__PURE__ */ jsx11(
12769
13237
  "svg",
12770
13238
  {
12771
13239
  style: {
@@ -12778,13 +13246,13 @@ var ErrorOverlay = ({
12778
13246
  },
12779
13247
  width: "100%",
12780
13248
  height: "100%",
12781
- children: isHighlighted ? /* @__PURE__ */ jsx10(
13249
+ children: isHighlighted ? /* @__PURE__ */ jsx11(
12782
13250
  "polygon",
12783
13251
  {
12784
13252
  points: `${screenCenter.x},${screenCenter.y - scaledRadius * 1.25} ${screenCenter.x + scaledRadius},${screenCenter.y} ${screenCenter.x},${screenCenter.y + scaledRadius * 1.25} ${screenCenter.x - scaledRadius},${screenCenter.y}`,
12785
13253
  fill: "#ff4444"
12786
13254
  }
12787
- ) : /* @__PURE__ */ jsx10(
13255
+ ) : /* @__PURE__ */ jsx11(
12788
13256
  "circle",
12789
13257
  {
12790
13258
  cx: screenCenter.x,
@@ -12798,7 +13266,7 @@ var ErrorOverlay = ({
12798
13266
  )
12799
13267
  }
12800
13268
  ),
12801
- /* @__PURE__ */ jsx10(
13269
+ /* @__PURE__ */ jsx11(
12802
13270
  "div",
12803
13271
  {
12804
13272
  style: {
@@ -12833,7 +13301,7 @@ var ErrorOverlay = ({
12833
13301
  }
12834
13302
  }
12835
13303
  ),
12836
- /* @__PURE__ */ jsx10(
13304
+ /* @__PURE__ */ jsx11(
12837
13305
  "div",
12838
13306
  {
12839
13307
  style: {
@@ -12851,7 +13319,7 @@ var ErrorOverlay = ({
12851
13319
  pointerEvents: "none",
12852
13320
  transform: popupPosition.transform
12853
13321
  },
12854
- children: /* @__PURE__ */ jsx10(
13322
+ children: /* @__PURE__ */ jsx11(
12855
13323
  "div",
12856
13324
  {
12857
13325
  className: `error-message ${errorMessageStyles}`,
@@ -12864,9 +13332,10 @@ var ErrorOverlay = ({
12864
13332
  )
12865
13333
  }
12866
13334
  )
12867
- ] }, errorId);
13335
+ ] }, `${errorId}_${compIndex}`);
12868
13336
  });
12869
- })
13337
+ }),
13338
+ focusScreenCenter && /* @__PURE__ */ jsx11(FocusMarkerSVG, { center: focusScreenCenter })
12870
13339
  ] });
12871
13340
  };
12872
13341
 
@@ -12940,7 +13409,7 @@ function filterTracesIfMultiple(filterTraces) {
12940
13409
  }
12941
13410
 
12942
13411
  // src/components/ElementOverlayBox.tsx
12943
- import { jsx as jsx11 } from "react/jsx-runtime";
13412
+ import { jsx as jsx12 } from "react/jsx-runtime";
12944
13413
  var containerStyle = {
12945
13414
  position: "absolute",
12946
13415
  left: 0,
@@ -13032,7 +13501,7 @@ var HighlightedPrimitiveBoxWithText = ({
13032
13501
  const overlayInfo = getTraceOverlayInfo(traceTextContext);
13033
13502
  if (!overlayInfo) return null;
13034
13503
  const yOffset = mousePos.y - 35;
13035
- return /* @__PURE__ */ jsx11(
13504
+ return /* @__PURE__ */ jsx12(
13036
13505
  "div",
13037
13506
  {
13038
13507
  style: {
@@ -13044,7 +13513,7 @@ var HighlightedPrimitiveBoxWithText = ({
13044
13513
  pointerEvents: "none",
13045
13514
  transform: "translateX(-50%)"
13046
13515
  },
13047
- children: /* @__PURE__ */ jsx11(
13516
+ children: /* @__PURE__ */ jsx12(
13048
13517
  "div",
13049
13518
  {
13050
13519
  style: {
@@ -13070,7 +13539,7 @@ var HighlightedPrimitiveBoxWithText = ({
13070
13539
  if (label.trim().length === 0) {
13071
13540
  return null;
13072
13541
  }
13073
- return /* @__PURE__ */ jsx11(
13542
+ return /* @__PURE__ */ jsx12(
13074
13543
  "div",
13075
13544
  {
13076
13545
  style: {
@@ -13084,7 +13553,7 @@ var HighlightedPrimitiveBoxWithText = ({
13084
13553
  transform: `rotate(${-rotation}deg)`,
13085
13554
  transformOrigin: "center center"
13086
13555
  },
13087
- children: /* @__PURE__ */ jsx11(
13556
+ children: /* @__PURE__ */ jsx12(
13088
13557
  "div",
13089
13558
  {
13090
13559
  style: {
@@ -13100,7 +13569,7 @@ var HighlightedPrimitiveBoxWithText = ({
13100
13569
  opacity: finalState ? 1 : si === 0 ? 1 : 0,
13101
13570
  transition: "width 0.2s, height 0.2s, margin-left 0.2s, margin-top 0.2s, opacity 0.2s"
13102
13571
  },
13103
- children: /* @__PURE__ */ jsx11(
13572
+ children: /* @__PURE__ */ jsx12(
13104
13573
  "div",
13105
13574
  {
13106
13575
  style: {
@@ -13148,7 +13617,7 @@ var ElementOverlayBox = ({
13148
13617
  is_showing_multiple_traces_length,
13149
13618
  elements
13150
13619
  });
13151
- return /* @__PURE__ */ jsx11("div", { style: containerStyle, children: !is_moving_component && primitives.map((primitive, i) => /* @__PURE__ */ jsx11(
13620
+ return /* @__PURE__ */ jsx12("div", { style: containerStyle, children: !is_moving_component && primitives.map((primitive, i) => /* @__PURE__ */ jsx12(
13152
13621
  HighlightedPrimitiveBoxWithText,
13153
13622
  {
13154
13623
  primitive,
@@ -13192,7 +13661,7 @@ var COLORS = {
13192
13661
  };
13193
13662
 
13194
13663
  // src/components/AnchorOffsetOverlay/AnchorOffsetOverlay.tsx
13195
- import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
13664
+ import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
13196
13665
  var AnchorOffsetOverlay = ({
13197
13666
  targets,
13198
13667
  transform,
@@ -13215,7 +13684,7 @@ var AnchorOffsetOverlay = ({
13215
13684
  anchorScreens.set(target.anchor_id, screenPoint);
13216
13685
  }
13217
13686
  });
13218
- return /* @__PURE__ */ jsx12(
13687
+ return /* @__PURE__ */ jsx13(
13219
13688
  "div",
13220
13689
  {
13221
13690
  style: {
@@ -13228,7 +13697,7 @@ var AnchorOffsetOverlay = ({
13228
13697
  pointerEvents: "none",
13229
13698
  zIndex: zIndexMap.dimensionOverlay
13230
13699
  },
13231
- children: /* @__PURE__ */ jsxs9(
13700
+ children: /* @__PURE__ */ jsxs10(
13232
13701
  "svg",
13233
13702
  {
13234
13703
  style: {
@@ -13258,8 +13727,8 @@ var AnchorOffsetOverlay = ({
13258
13727
  const shouldShowYLabel = yLineLength > VISUAL_CONFIG.MIN_LINE_LENGTH_FOR_LABEL;
13259
13728
  const xLabelText = `${target.display_offset_x ?? offsetX.toFixed(2)}mm`;
13260
13729
  const yLabelText = `${target.display_offset_y ?? offsetY.toFixed(2)}mm`;
13261
- return /* @__PURE__ */ jsxs9("g", { children: [
13262
- /* @__PURE__ */ jsx12(
13730
+ return /* @__PURE__ */ jsxs10("g", { children: [
13731
+ /* @__PURE__ */ jsx13(
13263
13732
  "line",
13264
13733
  {
13265
13734
  x1: anchorMarkerScreen.x,
@@ -13271,7 +13740,7 @@ var AnchorOffsetOverlay = ({
13271
13740
  strokeDasharray: VISUAL_CONFIG.LINE_DASH_PATTERN
13272
13741
  }
13273
13742
  ),
13274
- /* @__PURE__ */ jsx12(
13743
+ /* @__PURE__ */ jsx13(
13275
13744
  "line",
13276
13745
  {
13277
13746
  x1: targetScreen.x,
@@ -13283,7 +13752,7 @@ var AnchorOffsetOverlay = ({
13283
13752
  strokeDasharray: VISUAL_CONFIG.LINE_DASH_PATTERN
13284
13753
  }
13285
13754
  ),
13286
- target.type === "component" || target.type === "board" ? /* @__PURE__ */ jsx12(
13755
+ target.type === "component" || target.type === "board" ? /* @__PURE__ */ jsx13(
13287
13756
  "circle",
13288
13757
  {
13289
13758
  cx: targetScreen.x,
@@ -13295,8 +13764,8 @@ var AnchorOffsetOverlay = ({
13295
13764
  }
13296
13765
  ) : (
13297
13766
  // assumes "group"
13298
- /* @__PURE__ */ jsxs9(Fragment5, { children: [
13299
- /* @__PURE__ */ jsx12(
13767
+ /* @__PURE__ */ jsxs10(Fragment5, { children: [
13768
+ /* @__PURE__ */ jsx13(
13300
13769
  "line",
13301
13770
  {
13302
13771
  x1: targetScreen.x - VISUAL_CONFIG.ANCHOR_MARKER_SIZE,
@@ -13307,7 +13776,7 @@ var AnchorOffsetOverlay = ({
13307
13776
  strokeWidth: VISUAL_CONFIG.ANCHOR_MARKER_STROKE_WIDTH
13308
13777
  }
13309
13778
  ),
13310
- /* @__PURE__ */ jsx12(
13779
+ /* @__PURE__ */ jsx13(
13311
13780
  "line",
13312
13781
  {
13313
13782
  x1: targetScreen.x,
@@ -13320,7 +13789,7 @@ var AnchorOffsetOverlay = ({
13320
13789
  )
13321
13790
  ] })
13322
13791
  ),
13323
- shouldShowXLabel && /* @__PURE__ */ jsx12(
13792
+ shouldShowXLabel && /* @__PURE__ */ jsx13(
13324
13793
  "foreignObject",
13325
13794
  {
13326
13795
  x: Math.min(anchorMarkerScreen.x, targetScreen.x),
@@ -13328,10 +13797,10 @@ var AnchorOffsetOverlay = ({
13328
13797
  width: Math.abs(targetScreen.x - anchorMarkerScreen.x),
13329
13798
  height: 20,
13330
13799
  style: { overflow: "visible" },
13331
- children: /* @__PURE__ */ jsx12("div", { style: { ...labelStyle, textAlign: "center" }, children: xLabelText })
13800
+ children: /* @__PURE__ */ jsx13("div", { style: { ...labelStyle, textAlign: "center" }, children: xLabelText })
13332
13801
  }
13333
13802
  ),
13334
- shouldShowYLabel && /* @__PURE__ */ jsx12(
13803
+ shouldShowYLabel && /* @__PURE__ */ jsx13(
13335
13804
  "foreignObject",
13336
13805
  {
13337
13806
  x: targetScreen.x + yLabelOffset,
@@ -13339,7 +13808,7 @@ var AnchorOffsetOverlay = ({
13339
13808
  width: VISUAL_CONFIG.Y_LABEL_MIN_WIDTH,
13340
13809
  height: Math.abs(targetScreen.y - anchorMarkerScreen.y),
13341
13810
  style: { overflow: "visible" },
13342
- children: /* @__PURE__ */ jsx12(
13811
+ children: /* @__PURE__ */ jsx13(
13343
13812
  "div",
13344
13813
  {
13345
13814
  style: {
@@ -13358,8 +13827,8 @@ var AnchorOffsetOverlay = ({
13358
13827
  )
13359
13828
  ] }, target.id);
13360
13829
  }),
13361
- Array.from(anchorScreens.entries()).map(([anchorId, anchorScreen]) => /* @__PURE__ */ jsxs9("g", { children: [
13362
- /* @__PURE__ */ jsx12(
13830
+ Array.from(anchorScreens.entries()).map(([anchorId, anchorScreen]) => /* @__PURE__ */ jsxs10("g", { children: [
13831
+ /* @__PURE__ */ jsx13(
13363
13832
  "line",
13364
13833
  {
13365
13834
  x1: anchorScreen.x - VISUAL_CONFIG.ANCHOR_MARKER_SIZE,
@@ -13370,7 +13839,7 @@ var AnchorOffsetOverlay = ({
13370
13839
  strokeWidth: VISUAL_CONFIG.ANCHOR_MARKER_STROKE_WIDTH
13371
13840
  }
13372
13841
  ),
13373
- /* @__PURE__ */ jsx12(
13842
+ /* @__PURE__ */ jsx13(
13374
13843
  "line",
13375
13844
  {
13376
13845
  x1: anchorScreen.x,
@@ -13390,7 +13859,7 @@ var AnchorOffsetOverlay = ({
13390
13859
  };
13391
13860
 
13392
13861
  // src/components/AnchorOffsetOverlay/BoardAnchorOffsetOverlay.tsx
13393
- import { jsx as jsx13 } from "react/jsx-runtime";
13862
+ import { jsx as jsx14 } from "react/jsx-runtime";
13394
13863
  var BoardAnchorOffsetOverlay = ({
13395
13864
  elements,
13396
13865
  highlightedPrimitives,
@@ -13477,7 +13946,7 @@ var BoardAnchorOffsetOverlay = ({
13477
13946
  display_offset_y: target.group.display_offset_y
13478
13947
  };
13479
13948
  }).filter((t) => Boolean(t));
13480
- return /* @__PURE__ */ jsx13(
13949
+ return /* @__PURE__ */ jsx14(
13481
13950
  AnchorOffsetOverlay,
13482
13951
  {
13483
13952
  targets: sharedTargets,
@@ -13489,7 +13958,7 @@ var BoardAnchorOffsetOverlay = ({
13489
13958
  };
13490
13959
 
13491
13960
  // src/components/AnchorOffsetOverlay/GroupAnchorOffsetOverlay.tsx
13492
- import { jsx as jsx14 } from "react/jsx-runtime";
13961
+ import { jsx as jsx15 } from "react/jsx-runtime";
13493
13962
  var GroupAnchorOffsetOverlay = ({
13494
13963
  elements,
13495
13964
  highlightedPrimitives,
@@ -13605,7 +14074,7 @@ var GroupAnchorOffsetOverlay = ({
13605
14074
  display_offset_y: target.group.display_offset_y
13606
14075
  };
13607
14076
  }).filter((t) => Boolean(t));
13608
- return /* @__PURE__ */ jsx14(
14077
+ return /* @__PURE__ */ jsx15(
13609
14078
  AnchorOffsetOverlay,
13610
14079
  {
13611
14080
  targets: sharedTargets,
@@ -13618,7 +14087,7 @@ var GroupAnchorOffsetOverlay = ({
13618
14087
 
13619
14088
  // src/components/AnchorOffsetOverlay/ComponentBoundingBoxOverlay.tsx
13620
14089
  import { applyToPoint as applyToPoint13 } from "transformation-matrix";
13621
- import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
14090
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
13622
14091
  var calculateComponentBoundingBox = (component, elements) => {
13623
14092
  const componentId = component.pcb_component_id;
13624
14093
  const padsAndHoles = elements.filter(
@@ -13661,7 +14130,7 @@ var ComponentBoundingBoxOverlay = ({
13661
14130
  renderData.push({ component, bbox, group });
13662
14131
  }
13663
14132
  if (renderData.length === 0) return null;
13664
- return /* @__PURE__ */ jsx15(
14133
+ return /* @__PURE__ */ jsx16(
13665
14134
  "div",
13666
14135
  {
13667
14136
  style: {
@@ -13674,7 +14143,7 @@ var ComponentBoundingBoxOverlay = ({
13674
14143
  pointerEvents: "none",
13675
14144
  zIndex: zIndexMap.dimensionOverlay
13676
14145
  },
13677
- children: /* @__PURE__ */ jsx15(
14146
+ children: /* @__PURE__ */ jsx16(
13678
14147
  "svg",
13679
14148
  {
13680
14149
  style: { position: "absolute", left: 0, top: 0, pointerEvents: "none" },
@@ -13700,8 +14169,8 @@ var ComponentBoundingBoxOverlay = ({
13700
14169
  y: (bbox.minY + bbox.maxY) / 2
13701
14170
  };
13702
14171
  const componentCenterScreen = applyToPoint13(transform, componentCenter);
13703
- return /* @__PURE__ */ jsxs10("g", { children: [
13704
- /* @__PURE__ */ jsx15(
14172
+ return /* @__PURE__ */ jsxs11("g", { children: [
14173
+ /* @__PURE__ */ jsx16(
13705
14174
  "rect",
13706
14175
  {
13707
14176
  x: screenBbox.x,
@@ -13714,7 +14183,7 @@ var ComponentBoundingBoxOverlay = ({
13714
14183
  strokeDasharray: "4,4"
13715
14184
  }
13716
14185
  ),
13717
- /* @__PURE__ */ jsx15(
14186
+ /* @__PURE__ */ jsx16(
13718
14187
  "line",
13719
14188
  {
13720
14189
  x1: componentCenterScreen.x - 6,
@@ -13725,7 +14194,7 @@ var ComponentBoundingBoxOverlay = ({
13725
14194
  strokeWidth: 1.5
13726
14195
  }
13727
14196
  ),
13728
- /* @__PURE__ */ jsx15(
14197
+ /* @__PURE__ */ jsx16(
13729
14198
  "line",
13730
14199
  {
13731
14200
  x1: componentCenterScreen.x,
@@ -13736,7 +14205,7 @@ var ComponentBoundingBoxOverlay = ({
13736
14205
  strokeWidth: 1.5
13737
14206
  }
13738
14207
  ),
13739
- /* @__PURE__ */ jsx15(
14208
+ /* @__PURE__ */ jsx16(
13740
14209
  "circle",
13741
14210
  {
13742
14211
  cx: componentCenterScreen.x,
@@ -13756,7 +14225,7 @@ var ComponentBoundingBoxOverlay = ({
13756
14225
  };
13757
14226
 
13758
14227
  // src/components/AnchorOffsetOverlay/PanelAnchorOffsetOverlay.tsx
13759
- import { jsx as jsx16 } from "react/jsx-runtime";
14228
+ import { jsx as jsx17 } from "react/jsx-runtime";
13760
14229
  var PanelAnchorOffsetOverlay = ({
13761
14230
  elements,
13762
14231
  highlightedPrimitives,
@@ -13805,7 +14274,7 @@ var PanelAnchorOffsetOverlay = ({
13805
14274
  display_offset_y: target.board.display_offset_y
13806
14275
  };
13807
14276
  }).filter((t) => Boolean(t));
13808
- return /* @__PURE__ */ jsx16(
14277
+ return /* @__PURE__ */ jsx17(
13809
14278
  AnchorOffsetOverlay,
13810
14279
  {
13811
14280
  targets: sharedTargets,
@@ -13817,7 +14286,7 @@ var PanelAnchorOffsetOverlay = ({
13817
14286
  };
13818
14287
 
13819
14288
  // src/components/MouseElementTracker.tsx
13820
- import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
14289
+ import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
13821
14290
  var getPolygonBoundingBox = (points) => {
13822
14291
  if (points.length === 0) return null;
13823
14292
  let minX = points[0].x;
@@ -14004,7 +14473,7 @@ var MouseElementTracker = ({
14004
14473
  setMousedPrimitives(newMousedPrimitives);
14005
14474
  onMouseHoverOverPrimitives(newMousedPrimitives);
14006
14475
  };
14007
- return /* @__PURE__ */ jsxs11(
14476
+ return /* @__PURE__ */ jsxs12(
14008
14477
  "div",
14009
14478
  {
14010
14479
  ref: containerRef,
@@ -14028,7 +14497,7 @@ var MouseElementTracker = ({
14028
14497
  },
14029
14498
  children: [
14030
14499
  children,
14031
- /* @__PURE__ */ jsx17(
14500
+ /* @__PURE__ */ jsx18(
14032
14501
  ElementOverlayBox,
14033
14502
  {
14034
14503
  elements,
@@ -14036,8 +14505,8 @@ var MouseElementTracker = ({
14036
14505
  highlightedPrimitives
14037
14506
  }
14038
14507
  ),
14039
- transform && /* @__PURE__ */ jsxs11(Fragment6, { children: [
14040
- /* @__PURE__ */ jsx17(
14508
+ transform && /* @__PURE__ */ jsxs12(Fragment6, { children: [
14509
+ /* @__PURE__ */ jsx18(
14041
14510
  BoardAnchorOffsetOverlay,
14042
14511
  {
14043
14512
  elements,
@@ -14047,7 +14516,7 @@ var MouseElementTracker = ({
14047
14516
  containerHeight: height
14048
14517
  }
14049
14518
  ),
14050
- /* @__PURE__ */ jsx17(
14519
+ /* @__PURE__ */ jsx18(
14051
14520
  GroupAnchorOffsetOverlay,
14052
14521
  {
14053
14522
  elements,
@@ -14057,7 +14526,7 @@ var MouseElementTracker = ({
14057
14526
  containerHeight: height
14058
14527
  }
14059
14528
  ),
14060
- /* @__PURE__ */ jsx17(
14529
+ /* @__PURE__ */ jsx18(
14061
14530
  ComponentBoundingBoxOverlay,
14062
14531
  {
14063
14532
  elements,
@@ -14067,7 +14536,7 @@ var MouseElementTracker = ({
14067
14536
  containerHeight: height
14068
14537
  }
14069
14538
  ),
14070
- /* @__PURE__ */ jsx17(
14539
+ /* @__PURE__ */ jsx18(
14071
14540
  PanelAnchorOffsetOverlay,
14072
14541
  {
14073
14542
  elements,
@@ -14087,7 +14556,7 @@ var MouseElementTracker = ({
14087
14556
  import { applyToPoint as applyToPoint15 } from "transformation-matrix";
14088
14557
  import { identity as identity8 } from "transformation-matrix";
14089
14558
  import { useRef as useRef10, useEffect as useEffect13 } from "react";
14090
- import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
14559
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
14091
14560
  var GROUP_COLORS = [
14092
14561
  "rgb(255, 100, 100)",
14093
14562
  "rgb(100, 255, 100)",
@@ -14323,14 +14792,14 @@ var PcbGroupOverlay = ({
14323
14792
  is_showing_group_anchor_offsets,
14324
14793
  hoveredComponentIds
14325
14794
  ]);
14326
- return /* @__PURE__ */ jsxs12(
14795
+ return /* @__PURE__ */ jsxs13(
14327
14796
  "div",
14328
14797
  {
14329
14798
  ref: containerRef,
14330
14799
  style: { position: "relative", width: "100%", height: "100%" },
14331
14800
  children: [
14332
14801
  children,
14333
- /* @__PURE__ */ jsx18(
14802
+ /* @__PURE__ */ jsx19(
14334
14803
  "canvas",
14335
14804
  {
14336
14805
  ref: canvasRef,
@@ -14352,7 +14821,7 @@ var PcbGroupOverlay = ({
14352
14821
  // src/components/RatsNestOverlay.tsx
14353
14822
  import { applyToPoint as applyToPoint16, identity as identity9 } from "transformation-matrix";
14354
14823
  import { useMemo as useMemo6 } from "react";
14355
- import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
14824
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
14356
14825
  var RatsNestOverlay = ({ transform, soup, children }) => {
14357
14826
  const isShowingRatsNest = useGlobalStore((s) => s.is_showing_rats_nest);
14358
14827
  const { netMap, idToNetMap } = useMemo6(
@@ -14415,9 +14884,9 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
14415
14884
  }, [soup, netMap, idToNetMap, isShowingRatsNest]);
14416
14885
  if (!soup || !isShowingRatsNest) return children;
14417
14886
  if (!transform) transform = identity9();
14418
- return /* @__PURE__ */ jsxs13("div", { style: { position: "relative" }, children: [
14887
+ return /* @__PURE__ */ jsxs14("div", { style: { position: "relative" }, children: [
14419
14888
  children,
14420
- /* @__PURE__ */ jsx19(
14889
+ /* @__PURE__ */ jsx20(
14421
14890
  "svg",
14422
14891
  {
14423
14892
  style: {
@@ -14433,7 +14902,7 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
14433
14902
  children: ratsNestLines.map(({ key, startPoint, endPoint, isInNet }) => {
14434
14903
  const transformedStart = applyToPoint16(transform, startPoint);
14435
14904
  const transformedEnd = applyToPoint16(transform, endPoint);
14436
- return /* @__PURE__ */ jsx19(
14905
+ return /* @__PURE__ */ jsx20(
14437
14906
  "line",
14438
14907
  {
14439
14908
  x1: transformedStart.x,
@@ -14454,10 +14923,10 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
14454
14923
 
14455
14924
  // src/components/ToolbarOverlay.tsx
14456
14925
  import {
14457
- useEffect as useEffect16,
14926
+ useEffect as useEffect17,
14458
14927
  useState as useState11,
14459
- useCallback as useCallback9,
14460
- useRef as useRef13,
14928
+ useCallback as useCallback10,
14929
+ useRef as useRef14,
14461
14930
  useLayoutEffect as useLayoutEffect2
14462
14931
  } from "react";
14463
14932
  import { css as css3 } from "@emotion/css";
@@ -14465,7 +14934,7 @@ import { css as css3 } from "@emotion/css";
14465
14934
  // package.json
14466
14935
  var package_default = {
14467
14936
  name: "@tscircuit/pcb-viewer",
14468
- version: "1.11.363",
14937
+ version: "1.11.365",
14469
14938
  main: "dist/index.js",
14470
14939
  type: "module",
14471
14940
  repository: "tscircuit/pcb-viewer",
@@ -14519,7 +14988,7 @@ var package_default = {
14519
14988
  "@tscircuit/math-utils": "^0.0.29",
14520
14989
  "@vitejs/plugin-react": "^5.0.2",
14521
14990
  "circuit-json": "^0.0.403",
14522
- "circuit-to-canvas": "^0.0.95",
14991
+ "circuit-to-canvas": "^0.0.98",
14523
14992
  "circuit-to-svg": "^0.0.337",
14524
14993
  color: "^4.2.3",
14525
14994
  "react-supergrid": "^1.0.10",
@@ -14620,7 +15089,7 @@ var useMobileTouch = (onClick, options = { stopPropagation: true }) => {
14620
15089
  };
14621
15090
 
14622
15091
  // src/components/ToolbarButton.tsx
14623
- import { jsx as jsx20 } from "react/jsx-runtime";
15092
+ import { jsx as jsx21 } from "react/jsx-runtime";
14624
15093
  var ToolbarButton = ({
14625
15094
  children,
14626
15095
  isSmallScreen,
@@ -14628,7 +15097,7 @@ var ToolbarButton = ({
14628
15097
  ...props
14629
15098
  }) => {
14630
15099
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14631
- return /* @__PURE__ */ jsx20(
15100
+ return /* @__PURE__ */ jsx21(
14632
15101
  "div",
14633
15102
  {
14634
15103
  ...props,
@@ -14656,8 +15125,87 @@ var ToolbarButton = ({
14656
15125
  };
14657
15126
 
14658
15127
  // src/components/ToolbarErrorDropdown.tsx
14659
- import { useCallback as useCallback8, useMemo as useMemo7, useState as useState10 } from "react";
14660
- import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
15128
+ import { useCallback as useCallback9, useMemo as useMemo7, useState as useState10 } from "react";
15129
+
15130
+ // src/lib/util/group-errors-by-type.ts
15131
+ var groupErrorsByType = (errors) => {
15132
+ const groups = /* @__PURE__ */ new Map();
15133
+ errors.forEach((error, index) => {
15134
+ const errorType = error.error_type || "unknown_error";
15135
+ const existingGroup = groups.get(errorType) || [];
15136
+ existingGroup.push({
15137
+ error,
15138
+ index,
15139
+ errorId: getErrorId(error, index)
15140
+ });
15141
+ groups.set(errorType, existingGroup);
15142
+ });
15143
+ return Array.from(groups.entries()).map(([errorType, groupedErrors]) => ({
15144
+ errorType,
15145
+ errors: groupedErrors
15146
+ }));
15147
+ };
15148
+
15149
+ // src/components/useToolbarErrorFocus.ts
15150
+ import { useCallback as useCallback8, useEffect as useEffect16, useRef as useRef13 } from "react";
15151
+ var useToolbarErrorFocus = ({
15152
+ onClose,
15153
+ setHoveredErrorId,
15154
+ setFocusedErrorId,
15155
+ highlightDurationMs = 3e3
15156
+ }) => {
15157
+ const clearHighlightTimeoutRef = useRef13(null);
15158
+ const focusRequestRef = useRef13(null);
15159
+ const selectedErrorIdRef = useRef13(null);
15160
+ const clearPendingSelection = useCallback8(() => {
15161
+ if (clearHighlightTimeoutRef.current !== null) {
15162
+ window.clearTimeout(clearHighlightTimeoutRef.current);
15163
+ clearHighlightTimeoutRef.current = null;
15164
+ }
15165
+ if (focusRequestRef.current !== null) {
15166
+ window.cancelAnimationFrame(focusRequestRef.current);
15167
+ focusRequestRef.current = null;
15168
+ }
15169
+ }, []);
15170
+ useEffect16(() => clearPendingSelection, [clearPendingSelection]);
15171
+ const handleErrorSelect = useCallback8(
15172
+ (errorId) => {
15173
+ clearPendingSelection();
15174
+ selectedErrorIdRef.current = errorId;
15175
+ setHoveredErrorId(errorId);
15176
+ setFocusedErrorId(null);
15177
+ focusRequestRef.current = window.requestAnimationFrame(() => {
15178
+ setFocusedErrorId(errorId);
15179
+ focusRequestRef.current = null;
15180
+ });
15181
+ onClose();
15182
+ clearHighlightTimeoutRef.current = window.setTimeout(() => {
15183
+ selectedErrorIdRef.current = null;
15184
+ setHoveredErrorId(null);
15185
+ setFocusedErrorId(null);
15186
+ clearHighlightTimeoutRef.current = null;
15187
+ }, highlightDurationMs);
15188
+ },
15189
+ [
15190
+ clearPendingSelection,
15191
+ highlightDurationMs,
15192
+ onClose,
15193
+ setFocusedErrorId,
15194
+ setHoveredErrorId
15195
+ ]
15196
+ );
15197
+ const isSelectedError = useCallback8(
15198
+ (errorId) => selectedErrorIdRef.current === errorId,
15199
+ []
15200
+ );
15201
+ return {
15202
+ handleErrorSelect,
15203
+ isSelectedError
15204
+ };
15205
+ };
15206
+
15207
+ // src/components/ToolbarErrorDropdown.tsx
15208
+ import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
14661
15209
  var CopyErrorButton = ({
14662
15210
  errorId,
14663
15211
  errorMessage,
@@ -14667,7 +15215,7 @@ var CopyErrorButton = ({
14667
15215
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(
14668
15216
  () => onCopy(errorMessage, errorId)
14669
15217
  );
14670
- return /* @__PURE__ */ jsx21(
15218
+ return /* @__PURE__ */ jsx22(
14671
15219
  "button",
14672
15220
  {
14673
15221
  type: "button",
@@ -14687,7 +15235,7 @@ var CopyErrorButton = ({
14687
15235
  ...touchStyle
14688
15236
  },
14689
15237
  ...touchHandlers,
14690
- children: copiedErrorId === errorId ? /* @__PURE__ */ jsx21("span", { style: { color: "#4caf50", fontSize: 12 }, children: "Copied!" }) : /* @__PURE__ */ jsx21("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx21("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" }) })
15238
+ children: copiedErrorId === errorId ? /* @__PURE__ */ jsx22("span", { style: { color: "#4caf50", fontSize: 12 }, children: "Copied!" }) : /* @__PURE__ */ jsx22("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx22("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" }) })
14691
15239
  }
14692
15240
  );
14693
15241
  };
@@ -14696,7 +15244,9 @@ var ToolbarErrorDropdown = ({
14696
15244
  isOpen,
14697
15245
  isSmallScreen,
14698
15246
  onToggle,
14699
- setHoveredErrorId
15247
+ onClose,
15248
+ setHoveredErrorId,
15249
+ setFocusedErrorId
14700
15250
  }) => {
14701
15251
  const [copiedErrorId, setCopiedErrorId] = useState10(null);
14702
15252
  const [collapsedErrorGroups, setCollapsedErrorGroups] = useState10(
@@ -14706,6 +15256,11 @@ var ToolbarErrorDropdown = ({
14706
15256
  /* @__PURE__ */ new Set()
14707
15257
  );
14708
15258
  const [, copyToClipboard] = useCopyToClipboard_default();
15259
+ const { handleErrorSelect, isSelectedError } = useToolbarErrorFocus({
15260
+ onClose,
15261
+ setHoveredErrorId,
15262
+ setFocusedErrorId
15263
+ });
14709
15264
  const errorElements = useMemo7(
14710
15265
  () => elements?.filter(
14711
15266
  (el) => el.type.includes("error")
@@ -14713,24 +15268,11 @@ var ToolbarErrorDropdown = ({
14713
15268
  [elements]
14714
15269
  );
14715
15270
  const errorCount = errorElements.length;
14716
- const groupedErrorElements = useMemo7(() => {
14717
- const groups = /* @__PURE__ */ new Map();
14718
- errorElements.forEach((error, index) => {
14719
- const errorType = error.error_type || "unknown_error";
14720
- const existingGroup = groups.get(errorType) || [];
14721
- existingGroup.push({
14722
- error,
14723
- index,
14724
- errorId: error.pcb_trace_error_id
14725
- });
14726
- groups.set(errorType, existingGroup);
14727
- });
14728
- return Array.from(groups.entries()).map(([errorType, errors]) => ({
14729
- errorType,
14730
- errors
14731
- }));
14732
- }, [errorElements]);
14733
- const toggleErrorGroup = useCallback8((errorType) => {
15271
+ const groupedErrorElements = useMemo7(
15272
+ () => groupErrorsByType(errorElements),
15273
+ [errorElements]
15274
+ );
15275
+ const toggleErrorGroup = useCallback9((errorType) => {
14734
15276
  setCollapsedErrorGroups((prev) => {
14735
15277
  const next = new Set(prev);
14736
15278
  if (next.has(errorType)) {
@@ -14741,7 +15283,7 @@ var ToolbarErrorDropdown = ({
14741
15283
  return next;
14742
15284
  });
14743
15285
  }, []);
14744
- const toggleExpandedError = useCallback8((errorId) => {
15286
+ const toggleExpandedError = useCallback9((errorId) => {
14745
15287
  setExpandedErrorIds((prev) => {
14746
15288
  const next = new Set(prev);
14747
15289
  if (next.has(errorId)) {
@@ -14752,20 +15294,20 @@ var ToolbarErrorDropdown = ({
14752
15294
  return next;
14753
15295
  });
14754
15296
  }, []);
14755
- return /* @__PURE__ */ jsxs14("div", { style: { position: "relative" }, children: [
14756
- /* @__PURE__ */ jsx21(
15297
+ return /* @__PURE__ */ jsxs15("div", { style: { position: "relative" }, children: [
15298
+ /* @__PURE__ */ jsx22(
14757
15299
  ToolbarButton,
14758
15300
  {
14759
15301
  isSmallScreen,
14760
15302
  style: errorCount > 0 ? { color: "red" } : void 0,
14761
15303
  onClick: onToggle,
14762
- children: /* @__PURE__ */ jsxs14("div", { children: [
15304
+ children: /* @__PURE__ */ jsxs15("div", { children: [
14763
15305
  errorCount,
14764
15306
  " errors"
14765
15307
  ] })
14766
15308
  }
14767
15309
  ),
14768
- isOpen && errorCount > 0 && /* @__PURE__ */ jsx21(
15310
+ isOpen && errorCount > 0 && /* @__PURE__ */ jsx22(
14769
15311
  "div",
14770
15312
  {
14771
15313
  style: {
@@ -14785,14 +15327,14 @@ var ToolbarErrorDropdown = ({
14785
15327
  },
14786
15328
  children: groupedErrorElements.map(({ errorType, errors }, groupIndex) => {
14787
15329
  const isGroupCollapsed = collapsedErrorGroups.has(errorType);
14788
- return /* @__PURE__ */ jsxs14(
15330
+ return /* @__PURE__ */ jsxs15(
14789
15331
  "div",
14790
15332
  {
14791
15333
  style: {
14792
15334
  borderBottom: groupIndex < groupedErrorElements.length - 1 ? "1px solid #444" : "none"
14793
15335
  },
14794
15336
  children: [
14795
- /* @__PURE__ */ jsxs14(
15337
+ /* @__PURE__ */ jsxs15(
14796
15338
  "div",
14797
15339
  {
14798
15340
  style: {
@@ -14829,7 +15371,7 @@ var ToolbarErrorDropdown = ({
14829
15371
  toggleErrorGroup(errorType);
14830
15372
  },
14831
15373
  children: [
14832
- /* @__PURE__ */ jsxs14(
15374
+ /* @__PURE__ */ jsxs15(
14833
15375
  "div",
14834
15376
  {
14835
15377
  style: {
@@ -14839,7 +15381,7 @@ var ToolbarErrorDropdown = ({
14839
15381
  color: "#ff6b6b"
14840
15382
  },
14841
15383
  children: [
14842
- /* @__PURE__ */ jsx21(
15384
+ /* @__PURE__ */ jsx22(
14843
15385
  "div",
14844
15386
  {
14845
15387
  style: {
@@ -14852,7 +15394,7 @@ var ToolbarErrorDropdown = ({
14852
15394
  children: "\u203A"
14853
15395
  }
14854
15396
  ),
14855
- /* @__PURE__ */ jsx21(
15397
+ /* @__PURE__ */ jsx22(
14856
15398
  "div",
14857
15399
  {
14858
15400
  style: {
@@ -14865,7 +15407,7 @@ var ToolbarErrorDropdown = ({
14865
15407
  ]
14866
15408
  }
14867
15409
  ),
14868
- /* @__PURE__ */ jsx21(
15410
+ /* @__PURE__ */ jsx22(
14869
15411
  "div",
14870
15412
  {
14871
15413
  style: {
@@ -14883,14 +15425,14 @@ var ToolbarErrorDropdown = ({
14883
15425
  !isGroupCollapsed && errors.map(({ error, index, errorId }) => {
14884
15426
  const isExpanded = expandedErrorIds.has(errorId);
14885
15427
  const errorMessage = error.message ?? "No error message";
14886
- return /* @__PURE__ */ jsxs14(
15428
+ return /* @__PURE__ */ jsxs15(
14887
15429
  "div",
14888
15430
  {
14889
15431
  style: {
14890
15432
  borderTop: "1px solid #3a3a3a"
14891
15433
  },
14892
15434
  children: [
14893
- /* @__PURE__ */ jsxs14(
15435
+ /* @__PURE__ */ jsxs15(
14894
15436
  "div",
14895
15437
  {
14896
15438
  style: {
@@ -14909,7 +15451,9 @@ var ToolbarErrorDropdown = ({
14909
15451
  },
14910
15452
  onMouseLeave: (e) => {
14911
15453
  e.currentTarget.style.backgroundColor = "#2a2a2a";
14912
- setHoveredErrorId(null);
15454
+ if (!isSelectedError(errorId)) {
15455
+ setHoveredErrorId(null);
15456
+ }
14913
15457
  },
14914
15458
  onTouchStart: (e) => {
14915
15459
  e.stopPropagation();
@@ -14920,15 +15464,14 @@ var ToolbarErrorDropdown = ({
14920
15464
  e.stopPropagation();
14921
15465
  e.preventDefault();
14922
15466
  e.currentTarget.style.backgroundColor = "#2a2a2a";
14923
- setHoveredErrorId(null);
14924
- toggleExpandedError(errorId);
15467
+ handleErrorSelect(errorId);
14925
15468
  },
14926
15469
  onClick: (e) => {
14927
15470
  e.stopPropagation();
14928
- toggleExpandedError(errorId);
15471
+ handleErrorSelect(errorId);
14929
15472
  },
14930
15473
  children: [
14931
- /* @__PURE__ */ jsx21(
15474
+ /* @__PURE__ */ jsx22(
14932
15475
  "div",
14933
15476
  {
14934
15477
  style: {
@@ -14941,20 +15484,34 @@ var ToolbarErrorDropdown = ({
14941
15484
  whiteSpace: "nowrap",
14942
15485
  userSelect: "text"
14943
15486
  },
14944
- onMouseDown: (event) => event.stopPropagation(),
14945
- onClick: (event) => event.stopPropagation(),
14946
15487
  children: errorMessage
14947
15488
  }
14948
15489
  ),
14949
- /* @__PURE__ */ jsx21(
14950
- "div",
15490
+ /* @__PURE__ */ jsx22(
15491
+ "button",
14951
15492
  {
15493
+ type: "button",
15494
+ "aria-label": isExpanded ? "Collapse error details" : "Expand error details",
14952
15495
  style: {
14953
15496
  color: "#888",
14954
15497
  fontSize: "16px",
14955
15498
  transform: isExpanded ? "rotate(0deg)" : "rotate(90deg)",
14956
15499
  transition: "transform 0.2s ease",
14957
- flexShrink: 0
15500
+ flexShrink: 0,
15501
+ background: "none",
15502
+ border: "none",
15503
+ padding: 0,
15504
+ cursor: "pointer"
15505
+ },
15506
+ onMouseDown: (event) => event.stopPropagation(),
15507
+ onClick: (event) => {
15508
+ event.stopPropagation();
15509
+ toggleExpandedError(errorId);
15510
+ },
15511
+ onTouchEnd: (event) => {
15512
+ event.stopPropagation();
15513
+ event.preventDefault();
15514
+ toggleExpandedError(errorId);
14958
15515
  },
14959
15516
  children: "\u203A"
14960
15517
  }
@@ -14962,7 +15519,7 @@ var ToolbarErrorDropdown = ({
14962
15519
  ]
14963
15520
  }
14964
15521
  ),
14965
- isExpanded && /* @__PURE__ */ jsxs14(
15522
+ isExpanded && /* @__PURE__ */ jsxs15(
14966
15523
  "div",
14967
15524
  {
14968
15525
  "data-error-id": index,
@@ -14974,7 +15531,7 @@ var ToolbarErrorDropdown = ({
14974
15531
  position: "relative"
14975
15532
  },
14976
15533
  children: [
14977
- /* @__PURE__ */ jsx21(
15534
+ /* @__PURE__ */ jsx22(
14978
15535
  "div",
14979
15536
  {
14980
15537
  style: {
@@ -14992,7 +15549,7 @@ var ToolbarErrorDropdown = ({
14992
15549
  children: errorMessage
14993
15550
  }
14994
15551
  ),
14995
- /* @__PURE__ */ jsx21(
15552
+ /* @__PURE__ */ jsx22(
14996
15553
  CopyErrorButton,
14997
15554
  {
14998
15555
  errorId,
@@ -15024,10 +15581,10 @@ var ToolbarErrorDropdown = ({
15024
15581
  };
15025
15582
 
15026
15583
  // src/components/ToolbarOverlay.tsx
15027
- import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
15584
+ import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
15028
15585
  var LayerButton = ({ name, selected, onClick }) => {
15029
15586
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
15030
- return /* @__PURE__ */ jsxs15(
15587
+ return /* @__PURE__ */ jsxs16(
15031
15588
  "div",
15032
15589
  {
15033
15590
  className: css3`
@@ -15044,8 +15601,8 @@ var LayerButton = ({ name, selected, onClick }) => {
15044
15601
  ...touchHandlers,
15045
15602
  style: touchStyle,
15046
15603
  children: [
15047
- /* @__PURE__ */ jsx22("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
15048
- /* @__PURE__ */ jsx22(
15604
+ /* @__PURE__ */ jsx23("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
15605
+ /* @__PURE__ */ jsx23(
15049
15606
  "span",
15050
15607
  {
15051
15608
  style: {
@@ -15066,7 +15623,7 @@ var CheckboxMenuItem = ({
15066
15623
  onClick
15067
15624
  }) => {
15068
15625
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
15069
- return /* @__PURE__ */ jsxs15(
15626
+ return /* @__PURE__ */ jsxs16(
15070
15627
  "div",
15071
15628
  {
15072
15629
  className: css3`
@@ -15086,16 +15643,16 @@ var CheckboxMenuItem = ({
15086
15643
  ...touchHandlers,
15087
15644
  style: touchStyle,
15088
15645
  children: [
15089
- /* @__PURE__ */ jsx22("input", { type: "checkbox", checked, onChange: () => {
15646
+ /* @__PURE__ */ jsx23("input", { type: "checkbox", checked, onChange: () => {
15090
15647
  }, readOnly: true }),
15091
- /* @__PURE__ */ jsx22("span", { style: { color: "#eee" }, children: label })
15648
+ /* @__PURE__ */ jsx23("span", { style: { color: "#eee" }, children: label })
15092
15649
  ]
15093
15650
  }
15094
15651
  );
15095
15652
  };
15096
15653
  var RadioMenuItem = ({ label, checked, onClick }) => {
15097
15654
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
15098
- return /* @__PURE__ */ jsxs15(
15655
+ return /* @__PURE__ */ jsxs16(
15099
15656
  "div",
15100
15657
  {
15101
15658
  className: css3`
@@ -15115,9 +15672,9 @@ var RadioMenuItem = ({ label, checked, onClick }) => {
15115
15672
  ...touchHandlers,
15116
15673
  style: touchStyle,
15117
15674
  children: [
15118
- /* @__PURE__ */ jsx22("input", { type: "radio", checked, onChange: () => {
15675
+ /* @__PURE__ */ jsx23("input", { type: "radio", checked, onChange: () => {
15119
15676
  }, readOnly: true }),
15120
- /* @__PURE__ */ jsx22("span", { style: { color: "#eee" }, children: label })
15677
+ /* @__PURE__ */ jsx23("span", { style: { color: "#eee" }, children: label })
15121
15678
  ]
15122
15679
  }
15123
15680
  );
@@ -15144,7 +15701,8 @@ var ToolbarOverlay = ({ children, elements }) => {
15144
15701
  setIsShowingSilkscreen,
15145
15702
  setIsShowingFabricationNotes,
15146
15703
  setPcbGroupViewMode,
15147
- setHoveredErrorId
15704
+ setHoveredErrorId,
15705
+ setFocusedErrorId
15148
15706
  } = useGlobalStore((s) => ({
15149
15707
  isMouseOverContainer: s.is_mouse_over_container,
15150
15708
  setIsMouseOverContainer: s.setIsMouseOverContainer,
@@ -15181,13 +15739,14 @@ var ToolbarOverlay = ({ children, elements }) => {
15181
15739
  setIsShowingSilkscreen: s.setIsShowingSilkscreen,
15182
15740
  setIsShowingFabricationNotes: s.setIsShowingFabricationNotes,
15183
15741
  setPcbGroupViewMode: s.setPcbGroupViewMode,
15184
- setHoveredErrorId: s.setHoveredErrorId
15742
+ setHoveredErrorId: s.setHoveredErrorId,
15743
+ setFocusedErrorId: s.setFocusedErrorId
15185
15744
  }));
15186
15745
  const [isViewMenuOpen, setViewMenuOpen] = useState11(false);
15187
15746
  const [isLayerMenuOpen, setLayerMenuOpen] = useState11(false);
15188
15747
  const [isErrorsOpen, setErrorsOpen] = useState11(false);
15189
15748
  const [measureToolArmed, setMeasureToolArmed] = useState11(false);
15190
- useEffect16(() => {
15749
+ useEffect17(() => {
15191
15750
  const arm = () => setMeasureToolArmed(true);
15192
15751
  const disarm = () => setMeasureToolArmed(false);
15193
15752
  window.addEventListener("arm-dimension-tool", arm);
@@ -15205,8 +15764,8 @@ var ToolbarOverlay = ({ children, elements }) => {
15205
15764
  "bottom"
15206
15765
  ];
15207
15766
  const processedLayers = availableLayers;
15208
- const hasRunInitialMouseCheck = useRef13(false);
15209
- const hotkeyBoundaryRef = useRef13(null);
15767
+ const hasRunInitialMouseCheck = useRef14(false);
15768
+ const hotkeyBoundaryRef = useRef14(null);
15210
15769
  const hotKeyCallbacks = {
15211
15770
  "1": availableLayers[0] ? () => selectLayer(availableLayers[0]) : () => {
15212
15771
  },
@@ -15251,25 +15810,24 @@ var ToolbarOverlay = ({ children, elements }) => {
15251
15810
  document.removeEventListener("mousemove", checkMousePosition);
15252
15811
  };
15253
15812
  }, [setIsMouseOverContainer]);
15254
- const handleMouseEnter = useCallback9(() => {
15813
+ const handleMouseEnter = useCallback10(() => {
15255
15814
  setIsMouseOverContainer(true);
15256
15815
  }, [setIsMouseOverContainer]);
15257
- const handleMouseMove = useCallback9(() => {
15816
+ const handleMouseMove = useCallback10(() => {
15258
15817
  if (!isMouseOverContainer) {
15259
15818
  setIsMouseOverContainer(true);
15260
15819
  }
15261
15820
  }, [isMouseOverContainer, setIsMouseOverContainer]);
15262
- const handleMouseLeave = useCallback9(() => {
15821
+ const handleMouseLeave = useCallback10(() => {
15263
15822
  setIsMouseOverContainer(false);
15264
15823
  setLayerMenuOpen(false);
15265
15824
  setViewMenuOpen(false);
15266
15825
  setErrorsOpen(false);
15267
- setHoveredErrorId(null);
15268
- }, [setIsMouseOverContainer, setHoveredErrorId]);
15269
- const handleLayerMenuToggle = useCallback9(() => {
15826
+ }, [setIsMouseOverContainer]);
15827
+ const handleLayerMenuToggle = useCallback10(() => {
15270
15828
  setLayerMenuOpen(!isLayerMenuOpen);
15271
15829
  }, [isLayerMenuOpen]);
15272
- const handleErrorsToggle = useCallback9(() => {
15830
+ const handleErrorsToggle = useCallback10(() => {
15273
15831
  const newErrorsOpen = !isErrorsOpen;
15274
15832
  setErrorsOpen(newErrorsOpen);
15275
15833
  if (newErrorsOpen) {
@@ -15279,33 +15837,36 @@ var ToolbarOverlay = ({ children, elements }) => {
15279
15837
  setHoveredErrorId(null);
15280
15838
  }
15281
15839
  }, [isErrorsOpen, setHoveredErrorId]);
15282
- const handleEditTraceToggle = useCallback9(() => {
15840
+ const closeErrorsMenu = useCallback10(() => {
15841
+ setErrorsOpen(false);
15842
+ }, []);
15843
+ const handleEditTraceToggle = useCallback10(() => {
15283
15844
  setEditMode(editModes.in_draw_trace_mode ? "off" : "draw_trace");
15284
15845
  }, [editModes.in_draw_trace_mode, setEditMode]);
15285
- const handleMoveComponentToggle = useCallback9(() => {
15846
+ const handleMoveComponentToggle = useCallback10(() => {
15286
15847
  setEditMode(editModes.in_move_footprint_mode ? "off" : "move_footprint");
15287
15848
  }, [editModes.in_move_footprint_mode, setEditMode]);
15288
- const handleRatsNestToggle = useCallback9(() => {
15849
+ const handleRatsNestToggle = useCallback10(() => {
15289
15850
  setIsShowingRatsNest(!viewSettings.is_showing_rats_nest);
15290
15851
  }, [viewSettings.is_showing_rats_nest, setIsShowingRatsNest]);
15291
- const handleMeasureToolClick = useCallback9(() => {
15852
+ const handleMeasureToolClick = useCallback10(() => {
15292
15853
  setMeasureToolArmed(true);
15293
15854
  window.dispatchEvent(new Event("arm-dimension-tool"));
15294
15855
  }, []);
15295
- const handleViewMenuToggle = useCallback9(() => {
15856
+ const handleViewMenuToggle = useCallback10(() => {
15296
15857
  const newViewMenuOpen = !isViewMenuOpen;
15297
15858
  setViewMenuOpen(newViewMenuOpen);
15298
15859
  if (newViewMenuOpen) {
15299
15860
  setErrorsOpen(false);
15300
15861
  }
15301
15862
  }, [isViewMenuOpen]);
15302
- const stopCanvasInteractionPropagation = useCallback9(
15863
+ const stopCanvasInteractionPropagation = useCallback10(
15303
15864
  (event) => {
15304
15865
  event.stopPropagation();
15305
15866
  },
15306
15867
  []
15307
15868
  );
15308
- return /* @__PURE__ */ jsxs15(
15869
+ return /* @__PURE__ */ jsxs16(
15309
15870
  "div",
15310
15871
  {
15311
15872
  ref: hotkeyBoundaryRef,
@@ -15315,7 +15876,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15315
15876
  onMouseMove: handleMouseMove,
15316
15877
  children: [
15317
15878
  children,
15318
- /* @__PURE__ */ jsxs15(
15879
+ /* @__PURE__ */ jsxs16(
15319
15880
  "div",
15320
15881
  {
15321
15882
  style: {
@@ -15336,7 +15897,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15336
15897
  ]
15337
15898
  }
15338
15899
  ),
15339
- /* @__PURE__ */ jsxs15(
15900
+ /* @__PURE__ */ jsxs16(
15340
15901
  "div",
15341
15902
  {
15342
15903
  "data-toolbar-overlay": true,
@@ -15366,7 +15927,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15366
15927
  fontFamily: "sans-serif"
15367
15928
  },
15368
15929
  children: [
15369
- /* @__PURE__ */ jsxs15(
15930
+ /* @__PURE__ */ jsxs16(
15370
15931
  ToolbarButton,
15371
15932
  {
15372
15933
  isSmallScreen,
@@ -15377,10 +15938,10 @@ var ToolbarOverlay = ({ children, elements }) => {
15377
15938
  }
15378
15939
  },
15379
15940
  children: [
15380
- /* @__PURE__ */ jsxs15("div", { children: [
15941
+ /* @__PURE__ */ jsxs16("div", { children: [
15381
15942
  "layer:",
15382
15943
  " ",
15383
- /* @__PURE__ */ jsx22(
15944
+ /* @__PURE__ */ jsx23(
15384
15945
  "span",
15385
15946
  {
15386
15947
  style: {
@@ -15392,7 +15953,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15392
15953
  }
15393
15954
  )
15394
15955
  ] }),
15395
- isLayerMenuOpen && /* @__PURE__ */ jsx22("div", { style: { marginTop: 4, minWidth: 120 }, children: processedLayers.map((layer) => /* @__PURE__ */ jsx22(
15956
+ isLayerMenuOpen && /* @__PURE__ */ jsx23("div", { style: { marginTop: 4, minWidth: 120 }, children: processedLayers.map((layer) => /* @__PURE__ */ jsx23(
15396
15957
  LayerButton,
15397
15958
  {
15398
15959
  name: layer,
@@ -15406,68 +15967,70 @@ var ToolbarOverlay = ({ children, elements }) => {
15406
15967
  ]
15407
15968
  }
15408
15969
  ),
15409
- /* @__PURE__ */ jsx22(
15970
+ /* @__PURE__ */ jsx23(
15410
15971
  ToolbarErrorDropdown,
15411
15972
  {
15412
15973
  elements,
15413
15974
  isOpen: isErrorsOpen,
15414
15975
  isSmallScreen,
15415
15976
  onToggle: handleErrorsToggle,
15416
- setHoveredErrorId
15977
+ onClose: closeErrorsMenu,
15978
+ setHoveredErrorId,
15979
+ setFocusedErrorId
15417
15980
  }
15418
15981
  ),
15419
- /* @__PURE__ */ jsx22(
15982
+ /* @__PURE__ */ jsx23(
15420
15983
  ToolbarButton,
15421
15984
  {
15422
15985
  isSmallScreen,
15423
15986
  style: {},
15424
15987
  onClick: handleEditTraceToggle,
15425
- children: /* @__PURE__ */ jsxs15("div", { children: [
15988
+ children: /* @__PURE__ */ jsxs16("div", { children: [
15426
15989
  editModes.in_draw_trace_mode ? "\u2716 " : "",
15427
15990
  "Edit Traces"
15428
15991
  ] })
15429
15992
  }
15430
15993
  ),
15431
- /* @__PURE__ */ jsx22(
15994
+ /* @__PURE__ */ jsx23(
15432
15995
  ToolbarButton,
15433
15996
  {
15434
15997
  isSmallScreen,
15435
15998
  style: {},
15436
15999
  onClick: handleMoveComponentToggle,
15437
- children: /* @__PURE__ */ jsxs15("div", { children: [
16000
+ children: /* @__PURE__ */ jsxs16("div", { children: [
15438
16001
  editModes.in_move_footprint_mode ? "\u2716 " : "",
15439
16002
  "Move Components"
15440
16003
  ] })
15441
16004
  }
15442
16005
  ),
15443
- /* @__PURE__ */ jsx22(
16006
+ /* @__PURE__ */ jsx23(
15444
16007
  ToolbarButton,
15445
16008
  {
15446
16009
  isSmallScreen,
15447
16010
  style: {},
15448
16011
  onClick: handleRatsNestToggle,
15449
- children: /* @__PURE__ */ jsxs15("div", { children: [
16012
+ children: /* @__PURE__ */ jsxs16("div", { children: [
15450
16013
  viewSettings.is_showing_rats_nest ? "\u2716 " : "",
15451
16014
  "Rats Nest"
15452
16015
  ] })
15453
16016
  }
15454
16017
  ),
15455
- /* @__PURE__ */ jsx22(
16018
+ /* @__PURE__ */ jsx23(
15456
16019
  ToolbarButton,
15457
16020
  {
15458
16021
  isSmallScreen,
15459
16022
  style: measureToolArmed ? { backgroundColor: "#444" } : {},
15460
16023
  onClick: handleMeasureToolClick,
15461
- children: /* @__PURE__ */ jsx22("div", { children: "\u{1F4CF}" })
16024
+ children: /* @__PURE__ */ jsx23("div", { children: "\u{1F4CF}" })
15462
16025
  }
15463
16026
  ),
15464
- /* @__PURE__ */ jsx22(
16027
+ /* @__PURE__ */ jsx23(
15465
16028
  ToolbarButton,
15466
16029
  {
15467
16030
  isSmallScreen,
15468
16031
  onClick: handleViewMenuToggle,
15469
- children: /* @__PURE__ */ jsxs15("div", { children: [
15470
- /* @__PURE__ */ jsxs15(
16032
+ children: /* @__PURE__ */ jsxs16("div", { children: [
16033
+ /* @__PURE__ */ jsxs16(
15471
16034
  "div",
15472
16035
  {
15473
16036
  style: {
@@ -15477,7 +16040,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15477
16040
  },
15478
16041
  children: [
15479
16042
  "View",
15480
- /* @__PURE__ */ jsx22(
16043
+ /* @__PURE__ */ jsx23(
15481
16044
  "span",
15482
16045
  {
15483
16046
  style: {
@@ -15492,8 +16055,8 @@ var ToolbarOverlay = ({ children, elements }) => {
15492
16055
  ]
15493
16056
  }
15494
16057
  ),
15495
- isViewMenuOpen && /* @__PURE__ */ jsxs15("div", { style: { marginTop: 4, minWidth: 120 }, children: [
15496
- /* @__PURE__ */ jsx22(
16058
+ isViewMenuOpen && /* @__PURE__ */ jsxs16("div", { style: { marginTop: 4, minWidth: 120 }, children: [
16059
+ /* @__PURE__ */ jsx23(
15497
16060
  CheckboxMenuItem,
15498
16061
  {
15499
16062
  label: "Show All Trace Lengths",
@@ -15505,7 +16068,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15505
16068
  }
15506
16069
  }
15507
16070
  ),
15508
- /* @__PURE__ */ jsx22(
16071
+ /* @__PURE__ */ jsx23(
15509
16072
  CheckboxMenuItem,
15510
16073
  {
15511
16074
  label: "Show Autorouting Animation",
@@ -15517,7 +16080,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15517
16080
  }
15518
16081
  }
15519
16082
  ),
15520
- /* @__PURE__ */ jsx22(
16083
+ /* @__PURE__ */ jsx23(
15521
16084
  CheckboxMenuItem,
15522
16085
  {
15523
16086
  label: "Show DRC Errors",
@@ -15527,7 +16090,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15527
16090
  }
15528
16091
  }
15529
16092
  ),
15530
- /* @__PURE__ */ jsx22(
16093
+ /* @__PURE__ */ jsx23(
15531
16094
  CheckboxMenuItem,
15532
16095
  {
15533
16096
  label: "Show Copper Pours",
@@ -15539,7 +16102,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15539
16102
  }
15540
16103
  }
15541
16104
  ),
15542
- /* @__PURE__ */ jsx22(
16105
+ /* @__PURE__ */ jsx23(
15543
16106
  CheckboxMenuItem,
15544
16107
  {
15545
16108
  label: "Show Courtyards",
@@ -15549,7 +16112,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15549
16112
  }
15550
16113
  }
15551
16114
  ),
15552
- /* @__PURE__ */ jsx22(
16115
+ /* @__PURE__ */ jsx23(
15553
16116
  CheckboxMenuItem,
15554
16117
  {
15555
16118
  label: "Show Solder Mask",
@@ -15559,7 +16122,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15559
16122
  }
15560
16123
  }
15561
16124
  ),
15562
- /* @__PURE__ */ jsx22(
16125
+ /* @__PURE__ */ jsx23(
15563
16126
  CheckboxMenuItem,
15564
16127
  {
15565
16128
  label: "Show Silkscreen",
@@ -15569,7 +16132,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15569
16132
  }
15570
16133
  }
15571
16134
  ),
15572
- /* @__PURE__ */ jsx22(
16135
+ /* @__PURE__ */ jsx23(
15573
16136
  CheckboxMenuItem,
15574
16137
  {
15575
16138
  label: "Show Fabrication Notes",
@@ -15581,7 +16144,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15581
16144
  }
15582
16145
  }
15583
16146
  ),
15584
- /* @__PURE__ */ jsx22(
16147
+ /* @__PURE__ */ jsx23(
15585
16148
  CheckboxMenuItem,
15586
16149
  {
15587
16150
  label: "Show Group Anchor Offsets",
@@ -15593,7 +16156,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15593
16156
  }
15594
16157
  }
15595
16158
  ),
15596
- /* @__PURE__ */ jsx22(
16159
+ /* @__PURE__ */ jsx23(
15597
16160
  CheckboxMenuItem,
15598
16161
  {
15599
16162
  label: "Show PCB Groups",
@@ -15603,8 +16166,8 @@ var ToolbarOverlay = ({ children, elements }) => {
15603
16166
  }
15604
16167
  }
15605
16168
  ),
15606
- viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs15("div", { style: { marginLeft: 16 }, children: [
15607
- /* @__PURE__ */ jsx22(
16169
+ viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs16("div", { style: { marginLeft: 16 }, children: [
16170
+ /* @__PURE__ */ jsx23(
15608
16171
  RadioMenuItem,
15609
16172
  {
15610
16173
  label: "Show All Groups",
@@ -15614,7 +16177,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15614
16177
  }
15615
16178
  }
15616
16179
  ),
15617
- /* @__PURE__ */ jsx22(
16180
+ /* @__PURE__ */ jsx23(
15618
16181
  RadioMenuItem,
15619
16182
  {
15620
16183
  label: "Show Named Groups",
@@ -15638,13 +16201,15 @@ var ToolbarOverlay = ({ children, elements }) => {
15638
16201
  };
15639
16202
 
15640
16203
  // src/components/CanvasElementsRenderer.tsx
15641
- import { jsx as jsx23 } from "react/jsx-runtime";
16204
+ import { jsx as jsx24 } from "react/jsx-runtime";
15642
16205
  var CanvasElementsRenderer = (props) => {
15643
16206
  const { transform, elements } = props;
15644
- const hoveredErrorId = useGlobalStore((state) => state.hovered_error_id);
15645
- const isShowingCopperPours = useGlobalStore(
15646
- (state) => state.is_showing_copper_pours
15647
- );
16207
+ const { hoveredErrorId, focusedErrorId, isShowingCopperPours } = useGlobalStore((state) => ({
16208
+ hoveredErrorId: state.hovered_error_id,
16209
+ focusedErrorId: state.focused_error_id,
16210
+ isShowingCopperPours: state.is_showing_copper_pours
16211
+ }));
16212
+ const activeErrorId = focusedErrorId ?? hoveredErrorId;
15648
16213
  const elementsToRender = useMemo8(
15649
16214
  () => isShowingCopperPours ? elements : elements.filter((elm) => elm.type !== "pcb_copper_pour"),
15650
16215
  [elements, isShowingCopperPours]
@@ -15663,27 +16228,71 @@ var CanvasElementsRenderer = (props) => {
15663
16228
  primitiveIdsInMousedOverNet: []
15664
16229
  });
15665
16230
  const [hoveredComponentIds, setHoveredComponentIds] = useState12([]);
16231
+ const currentTransformRef = useRef15(transform ?? null);
16232
+ const zoomAnimationFrameRef = useRef15(null);
16233
+ const elementIndexes = useMemo8(
16234
+ () => buildErrorPreviewElementIndexes(elements),
16235
+ [elements]
16236
+ );
15666
16237
  const errorRelatedIds = useMemo8(() => {
15667
- if (!hoveredErrorId) return [];
16238
+ if (!activeErrorId) return [];
15668
16239
  const errorElements = elements.filter(
15669
16240
  (el) => el.type.includes("error")
15670
16241
  );
15671
- const hoveredError = errorElements.find((el) => {
15672
- return el.error_id === hoveredErrorId;
16242
+ const activeError = errorElements.find((el, index) => {
16243
+ return getErrorId(el, index) === activeErrorId;
15673
16244
  });
15674
- if (!hoveredError) return [];
15675
- const relatedIds = [];
15676
- if (hoveredError.pcb_trace_id) {
15677
- relatedIds.push(hoveredError.pcb_trace_id);
15678
- }
15679
- if (hoveredError.pcb_port_ids) {
15680
- relatedIds.push(...hoveredError.pcb_port_ids);
15681
- }
15682
- if (hoveredError.pcb_via_ids) {
15683
- relatedIds.push(...hoveredError.pcb_via_ids);
16245
+ if (!activeError) return [];
16246
+ return getRelatedIdsForError(activeError);
16247
+ }, [activeErrorId, elements]);
16248
+ useEffect18(() => {
16249
+ if (transform) {
16250
+ currentTransformRef.current = transform;
15684
16251
  }
15685
- return relatedIds;
15686
- }, [hoveredErrorId, elements]);
16252
+ }, [transform]);
16253
+ useEffect18(() => {
16254
+ return () => {
16255
+ cancelTransformAnimation(zoomAnimationFrameRef.current);
16256
+ };
16257
+ }, []);
16258
+ useEffect18(() => {
16259
+ if (!props.width || !props.height || !props.setTransform || !focusedErrorId)
16260
+ return;
16261
+ const focusedError = findErrorElementById(elements, focusedErrorId);
16262
+ if (!focusedError) return;
16263
+ const previewBounds = getErrorPreviewBounds({
16264
+ error: focusedError,
16265
+ indexes: elementIndexes
16266
+ });
16267
+ if (!previewBounds) return;
16268
+ const startTransform = currentTransformRef.current ?? transform;
16269
+ if (!startTransform) return;
16270
+ const targetTransform = createTransformForBounds({
16271
+ bounds: previewBounds,
16272
+ width: props.width,
16273
+ height: props.height
16274
+ });
16275
+ cancelTransformAnimation(zoomAnimationFrameRef.current);
16276
+ animateTransform({
16277
+ startTransform,
16278
+ endTransform: targetTransform,
16279
+ durationMs: 420,
16280
+ setAnimationFrameId: (animationFrameId) => {
16281
+ zoomAnimationFrameRef.current = animationFrameId;
16282
+ },
16283
+ onUpdate: (nextTransform) => {
16284
+ currentTransformRef.current = nextTransform;
16285
+ props.setTransform?.(nextTransform);
16286
+ }
16287
+ });
16288
+ }, [
16289
+ focusedErrorId,
16290
+ elements,
16291
+ elementIndexes,
16292
+ props.height,
16293
+ props.setTransform,
16294
+ props.width
16295
+ ]);
15687
16296
  const primitives = useMemo8(() => {
15688
16297
  const combinedPrimitiveIds = [
15689
16298
  ...hoverState.primitiveIdsInMousedOverNet,
@@ -15695,7 +16304,7 @@ var CanvasElementsRenderer = (props) => {
15695
16304
  primitiveIdsInMousedOverNet: combinedPrimitiveIds
15696
16305
  });
15697
16306
  }, [primitivesWithoutInteractionMetadata, hoverState, errorRelatedIds]);
15698
- const onMouseOverPrimitives = useCallback10(
16307
+ const onMouseOverPrimitives = useCallback11(
15699
16308
  (primitivesHoveredOver) => {
15700
16309
  const primitiveIdsInMousedOverNet = [];
15701
16310
  for (const primitive of primitivesHoveredOver) {
@@ -15728,14 +16337,14 @@ var CanvasElementsRenderer = (props) => {
15728
16337
  },
15729
16338
  [connectivityMap]
15730
16339
  );
15731
- return /* @__PURE__ */ jsx23(
16340
+ return /* @__PURE__ */ jsx24(
15732
16341
  MouseElementTracker,
15733
16342
  {
15734
16343
  elements: elementsToRender,
15735
16344
  transform,
15736
16345
  primitives: primitivesWithoutInteractionMetadata,
15737
16346
  onMouseHoverOverPrimitives: onMouseOverPrimitives,
15738
- children: /* @__PURE__ */ jsx23(
16347
+ children: /* @__PURE__ */ jsx24(
15739
16348
  EditPlacementOverlay,
15740
16349
  {
15741
16350
  disabled: !props.allowEditing,
@@ -15744,7 +16353,7 @@ var CanvasElementsRenderer = (props) => {
15744
16353
  cancelPanDrag: props.cancelPanDrag,
15745
16354
  onCreateEditEvent: props.onCreateEditEvent,
15746
16355
  onModifyEditEvent: props.onModifyEditEvent,
15747
- children: /* @__PURE__ */ jsx23(
16356
+ children: /* @__PURE__ */ jsx24(
15748
16357
  EditTraceHintOverlay,
15749
16358
  {
15750
16359
  disabled: !props.allowEditing,
@@ -15753,29 +16362,29 @@ var CanvasElementsRenderer = (props) => {
15753
16362
  cancelPanDrag: props.cancelPanDrag,
15754
16363
  onCreateEditEvent: props.onCreateEditEvent,
15755
16364
  onModifyEditEvent: props.onModifyEditEvent,
15756
- children: /* @__PURE__ */ jsx23(
16365
+ children: /* @__PURE__ */ jsx24(
15757
16366
  DimensionOverlay,
15758
16367
  {
15759
16368
  transform,
15760
16369
  focusOnHover: props.focusOnHover,
15761
16370
  primitives: primitivesWithoutInteractionMetadata,
15762
- children: /* @__PURE__ */ jsx23(ToolbarOverlay, { elements, children: /* @__PURE__ */ jsx23(ErrorOverlay, { transform, elements, children: /* @__PURE__ */ jsx23(RatsNestOverlay, { transform, soup: elements, children: /* @__PURE__ */ jsx23(
16371
+ children: /* @__PURE__ */ jsx24(ToolbarOverlay, { elements, children: /* @__PURE__ */ jsx24(ErrorOverlay, { transform, elements, children: /* @__PURE__ */ jsx24(RatsNestOverlay, { transform, soup: elements, children: /* @__PURE__ */ jsx24(
15763
16372
  PcbGroupOverlay,
15764
16373
  {
15765
16374
  transform,
15766
16375
  elements,
15767
16376
  hoveredComponentIds,
15768
- children: /* @__PURE__ */ jsx23(
16377
+ children: /* @__PURE__ */ jsx24(
15769
16378
  DebugGraphicsOverlay,
15770
16379
  {
15771
16380
  transform,
15772
16381
  debugGraphics: props.debugGraphics,
15773
- children: /* @__PURE__ */ jsx23(
16382
+ children: /* @__PURE__ */ jsx24(
15774
16383
  WarningGraphicsOverlay,
15775
16384
  {
15776
16385
  transform,
15777
16386
  elements,
15778
- children: /* @__PURE__ */ jsx23(
16387
+ children: /* @__PURE__ */ jsx24(
15779
16388
  CanvasPrimitiveRenderer,
15780
16389
  {
15781
16390
  transform,
@@ -15857,8 +16466,8 @@ var calculateBoardSizeKey = (circuitJson) => {
15857
16466
  };
15858
16467
 
15859
16468
  // src/PCBViewer.tsx
15860
- import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
15861
- var defaultTransform = compose7(translate11(400, 300), scale5(40, -40));
16469
+ import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
16470
+ var defaultTransform = compose8(translate12(400, 300), scale6(40, -40));
15862
16471
  var PCBViewer = ({
15863
16472
  circuitJson,
15864
16473
  debugGraphics,
@@ -15876,7 +16485,7 @@ var PCBViewer = ({
15876
16485
  );
15877
16486
  const [ref, refDimensions] = useMeasure_default();
15878
16487
  const [transform, setTransformInternal] = useState13(defaultTransform);
15879
- const shouldAllowCanvasInteraction = useCallback11(
16488
+ const shouldAllowCanvasInteraction = useCallback12(
15880
16489
  (event) => {
15881
16490
  const target = event.target;
15882
16491
  if (!(target instanceof Element)) return true;
@@ -15896,8 +16505,8 @@ var PCBViewer = ({
15896
16505
  });
15897
16506
  let [editEvents, setEditEvents] = useState13([]);
15898
16507
  editEvents = editEventsProp ?? editEvents;
15899
- const initialRenderCompleted = useRef14(false);
15900
- const touchStartRef = useRef14(null);
16508
+ const initialRenderCompleted = useRef16(false);
16509
+ const touchStartRef = useRef16(null);
15901
16510
  const circuitJsonKey = useMemo9(
15902
16511
  () => calculateCircuitJsonKey(circuitJson),
15903
16512
  [circuitJson]
@@ -15915,15 +16524,15 @@ var PCBViewer = ({
15915
16524
  (elmBounds.height ?? 0) / height2,
15916
16525
  100
15917
16526
  ) * 0.75;
15918
- const targetTransform = compose7(
15919
- translate11((elmBounds.width ?? 0) / 2, (elmBounds.height ?? 0) / 2),
15920
- scale5(scaleFactor, -scaleFactor, 0, 0),
15921
- translate11(-center.x, -center.y)
16527
+ const targetTransform = compose8(
16528
+ translate12((elmBounds.width ?? 0) / 2, (elmBounds.height ?? 0) / 2),
16529
+ scale6(scaleFactor, -scaleFactor, 0, 0),
16530
+ translate12(-center.x, -center.y)
15922
16531
  );
15923
16532
  setTransform(targetTransform);
15924
16533
  return;
15925
16534
  };
15926
- useEffect17(() => {
16535
+ useEffect19(() => {
15927
16536
  if (!refDimensions?.width) return;
15928
16537
  if (!circuitJson) return;
15929
16538
  if (circuitJson.length === 0) return;
@@ -15932,7 +16541,7 @@ var PCBViewer = ({
15932
16541
  initialRenderCompleted.current = true;
15933
16542
  }
15934
16543
  }, [circuitJson, refDimensions]);
15935
- useEffect17(() => {
16544
+ useEffect19(() => {
15936
16545
  if (initialRenderCompleted.current === true) {
15937
16546
  resetTransform();
15938
16547
  }
@@ -15966,23 +16575,24 @@ var PCBViewer = ({
15966
16575
  }),
15967
16576
  [initialState, disablePcbGroups]
15968
16577
  );
15969
- return /* @__PURE__ */ jsxs16(
16578
+ return /* @__PURE__ */ jsxs17(
15970
16579
  "div",
15971
16580
  {
15972
16581
  ref: transformRef,
15973
16582
  style: { position: "relative" },
15974
16583
  onContextMenu: (event) => event.preventDefault(),
15975
16584
  children: [
15976
- /* @__PURE__ */ jsx24("div", { ref, children: /* @__PURE__ */ jsxs16(
16585
+ /* @__PURE__ */ jsx25("div", { ref, children: /* @__PURE__ */ jsxs17(
15977
16586
  ContextProviders,
15978
16587
  {
15979
16588
  initialState: mergedInitialState,
15980
16589
  disablePcbGroups,
15981
16590
  children: [
15982
- /* @__PURE__ */ jsx24(
16591
+ /* @__PURE__ */ jsx25(
15983
16592
  CanvasElementsRenderer,
15984
16593
  {
15985
16594
  transform,
16595
+ setTransform,
15986
16596
  height,
15987
16597
  width: refDimensions.width,
15988
16598
  allowEditing,
@@ -16004,11 +16614,11 @@ var PCBViewer = ({
16004
16614
  },
16005
16615
  refDimensions.width
16006
16616
  ),
16007
- /* @__PURE__ */ jsx24(ToastContainer, {})
16617
+ /* @__PURE__ */ jsx25(ToastContainer, {})
16008
16618
  ]
16009
16619
  }
16010
16620
  ) }),
16011
- clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx24(
16621
+ clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx25(
16012
16622
  "div",
16013
16623
  {
16014
16624
  onClick: () => {
@@ -16045,7 +16655,7 @@ var PCBViewer = ({
16045
16655
  justifyContent: "center",
16046
16656
  touchAction: "pan-x pan-y pinch-zoom"
16047
16657
  },
16048
- children: /* @__PURE__ */ jsx24(
16658
+ children: /* @__PURE__ */ jsx25(
16049
16659
  "div",
16050
16660
  {
16051
16661
  style: {