@tscircuit/pcb-viewer 1.11.365 → 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) => ({
@@ -10371,17 +10768,17 @@ var FONT_SIZE_HEIGHT_RATIO = 1;
10371
10768
  var import_svgson = __toESM(require_svgson_umd(), 1);
10372
10769
  var import_pretty = __toESM(require_pretty(), 1);
10373
10770
  import {
10374
- compose as compose5,
10375
- translate as translate9,
10376
- scale as scale3,
10771
+ compose as compose6,
10772
+ translate as translate10,
10773
+ scale as scale4,
10377
10774
  applyToPoint as applyToPoint5
10378
10775
  } from "transformation-matrix";
10379
10776
 
10380
10777
  // node_modules/graphics-debug/dist/chunk-ARYXS3GC.js
10381
10778
  import {
10382
- compose as compose6,
10383
- scale as scale4,
10384
- translate as translate10,
10779
+ compose as compose7,
10780
+ scale as scale5,
10781
+ translate as translate11,
10385
10782
  applyToPoint as applyToPoint6
10386
10783
  } from "transformation-matrix";
10387
10784
  var defaultColors = [
@@ -10444,10 +10841,10 @@ function computeTransformFromViewbox(viewbox, canvasWidth, canvasHeight, options
10444
10841
  (canvasWidth - 2 * padding) / width,
10445
10842
  (canvasHeight - 2 * padding) / height
10446
10843
  );
10447
- return compose6(
10448
- translate10(canvasWidth / 2, canvasHeight / 2),
10449
- scale4(scale_factor, yFlip ? -scale_factor : scale_factor),
10450
- 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))
10451
10848
  );
10452
10849
  }
10453
10850
  function getBounds(graphics) {
@@ -12274,15 +12671,94 @@ var getPopupPosition = (errorCenter, containerRef) => {
12274
12671
  };
12275
12672
  };
12276
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
+
12277
12753
  // src/components/ErrorOverlay.tsx
12278
- 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";
12279
12755
  var ErrorSVG = ({
12280
12756
  screenPort1,
12281
12757
  screenPort2,
12282
12758
  errorCenter,
12283
12759
  canLineBeDrawn,
12284
12760
  isHighlighted = false
12285
- }) => /* @__PURE__ */ jsx10(
12761
+ }) => /* @__PURE__ */ jsx11(
12286
12762
  "svg",
12287
12763
  {
12288
12764
  style: {
@@ -12295,8 +12771,8 @@ var ErrorSVG = ({
12295
12771
  },
12296
12772
  width: "100%",
12297
12773
  height: "100%",
12298
- children: canLineBeDrawn && /* @__PURE__ */ jsxs8(Fragment4, { children: [
12299
- /* @__PURE__ */ jsx10(
12774
+ children: canLineBeDrawn && /* @__PURE__ */ jsxs9(Fragment4, { children: [
12775
+ /* @__PURE__ */ jsx11(
12300
12776
  "line",
12301
12777
  {
12302
12778
  x1: screenPort1.x,
@@ -12308,7 +12784,7 @@ var ErrorSVG = ({
12308
12784
  stroke: isHighlighted ? "#ff4444" : "red"
12309
12785
  }
12310
12786
  ),
12311
- /* @__PURE__ */ jsx10(
12787
+ /* @__PURE__ */ jsx11(
12312
12788
  "line",
12313
12789
  {
12314
12790
  x1: errorCenter.x,
@@ -12320,7 +12796,7 @@ var ErrorSVG = ({
12320
12796
  stroke: isHighlighted ? "#ff4444" : "red"
12321
12797
  }
12322
12798
  ),
12323
- isHighlighted ? /* @__PURE__ */ jsx10(
12799
+ isHighlighted ? /* @__PURE__ */ jsx11(
12324
12800
  "rect",
12325
12801
  {
12326
12802
  x: errorCenter.x - 7,
@@ -12330,7 +12806,7 @@ var ErrorSVG = ({
12330
12806
  transform: `rotate(45 ${errorCenter.x} ${errorCenter.y})`,
12331
12807
  fill: "#ff4444"
12332
12808
  }
12333
- ) : /* @__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" })
12334
12810
  ] })
12335
12811
  }
12336
12812
  );
@@ -12338,7 +12814,7 @@ var RouteSVG = ({
12338
12814
  points,
12339
12815
  errorCenter,
12340
12816
  isHighlighted = false
12341
- }) => /* @__PURE__ */ jsxs8(
12817
+ }) => /* @__PURE__ */ jsxs9(
12342
12818
  "svg",
12343
12819
  {
12344
12820
  style: {
@@ -12352,7 +12828,7 @@ var RouteSVG = ({
12352
12828
  width: "100%",
12353
12829
  height: "100%",
12354
12830
  children: [
12355
- points.length > 1 && /* @__PURE__ */ jsx10(
12831
+ points.length > 1 && /* @__PURE__ */ jsx11(
12356
12832
  "polyline",
12357
12833
  {
12358
12834
  points: points.map((pt) => `${pt.x},${pt.y}`).join(" "),
@@ -12362,7 +12838,7 @@ var RouteSVG = ({
12362
12838
  strokeDasharray: "2,2"
12363
12839
  }
12364
12840
  ),
12365
- isHighlighted ? /* @__PURE__ */ jsx10(
12841
+ isHighlighted ? /* @__PURE__ */ jsx11(
12366
12842
  "rect",
12367
12843
  {
12368
12844
  x: errorCenter.x - 7,
@@ -12372,7 +12848,7 @@ var RouteSVG = ({
12372
12848
  transform: `rotate(45 ${errorCenter.x} ${errorCenter.y})`,
12373
12849
  fill: "#ff4444"
12374
12850
  }
12375
- ) : /* @__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" })
12376
12852
  ]
12377
12853
  }
12378
12854
  );
@@ -12396,12 +12872,16 @@ var ErrorOverlay = ({
12396
12872
  elements
12397
12873
  }) => {
12398
12874
  const containerRef = useRef9(null);
12399
- const { isShowingDRCErrors, hoveredErrorId } = useGlobalStore((state) => ({
12400
- isShowingDRCErrors: state.is_showing_drc_errors,
12401
- hoveredErrorId: state.hovered_error_id
12402
- }));
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;
12403
12883
  if (!elements) {
12404
- return /* @__PURE__ */ jsx10("div", { style: { position: "relative" }, ref: containerRef, children });
12884
+ return /* @__PURE__ */ jsx11("div", { style: { position: "relative" }, ref: containerRef, children });
12405
12885
  }
12406
12886
  const traceErrors = elements.filter(
12407
12887
  (el) => el.type === "pcb_trace_error"
@@ -12412,21 +12892,30 @@ var ErrorOverlay = ({
12412
12892
  const componentErrors = elements.filter(
12413
12893
  (el) => el.type === "pcb_trace_error" && el.message?.includes("Multiple components found with name")
12414
12894
  );
12415
- const portsMap = /* @__PURE__ */ new Map();
12416
- elements.forEach((el) => {
12417
- if (el.type === "pcb_port") {
12418
- 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
+ }
12419
12908
  }
12420
- });
12421
- return /* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, ref: containerRef, children: [
12909
+ }
12910
+ return /* @__PURE__ */ jsxs9("div", { style: { position: "relative" }, ref: containerRef, children: [
12422
12911
  children,
12423
12912
  traceErrors.map((el, index) => {
12424
12913
  const { pcb_port_ids, pcb_trace_id } = el;
12425
- const port1 = pcb_port_ids?.[0] ? portsMap.get(pcb_port_ids[0]) : void 0;
12426
- const port2 = pcb_port_ids?.[1] ? portsMap.get(pcb_port_ids[1]) : void 0;
12427
- const trace = elements ? su(elements).pcb_trace.get(pcb_trace_id) : void 0;
12428
- const errorId = el.pcb_trace_error_id;
12429
- 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;
12430
12919
  if (port1 && port2) {
12431
12920
  const screenPort1 = applyToPoint11(transform, {
12432
12921
  x: port1.x,
@@ -12448,8 +12937,8 @@ var ErrorOverlay = ({
12448
12937
  return null;
12449
12938
  }
12450
12939
  const popupPosition = getPopupPosition(errorCenter, containerRef);
12451
- return /* @__PURE__ */ jsxs8(Fragment3, { children: [
12452
- /* @__PURE__ */ jsx10(
12940
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
12941
+ /* @__PURE__ */ jsx11(
12453
12942
  ErrorSVG,
12454
12943
  {
12455
12944
  screenPort1,
@@ -12459,7 +12948,7 @@ var ErrorOverlay = ({
12459
12948
  isHighlighted
12460
12949
  }
12461
12950
  ),
12462
- /* @__PURE__ */ jsx10(
12951
+ /* @__PURE__ */ jsx11(
12463
12952
  "div",
12464
12953
  {
12465
12954
  style: {
@@ -12494,7 +12983,7 @@ var ErrorOverlay = ({
12494
12983
  }
12495
12984
  }
12496
12985
  ),
12497
- /* @__PURE__ */ jsx10(
12986
+ /* @__PURE__ */ jsx11(
12498
12987
  "div",
12499
12988
  {
12500
12989
  style: {
@@ -12512,7 +13001,7 @@ var ErrorOverlay = ({
12512
13001
  pointerEvents: "none",
12513
13002
  transform: popupPosition.transform
12514
13003
  },
12515
- children: /* @__PURE__ */ jsx10(
13004
+ children: /* @__PURE__ */ jsx11(
12516
13005
  "div",
12517
13006
  {
12518
13007
  className: `error-message ${errorMessageStyles}`,
@@ -12531,13 +13020,15 @@ var ErrorOverlay = ({
12531
13020
  const screenPoints = trace.route.map(
12532
13021
  (pt) => applyToPoint11(transform, { x: pt.x, y: pt.y })
12533
13022
  );
12534
- 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
+ ))
12535
13026
  return null;
12536
13027
  const mid = Math.floor(screenPoints.length / 2);
12537
13028
  const errorCenter = screenPoints[mid];
12538
13029
  const popupPosition = getPopupPosition(errorCenter, containerRef);
12539
- return /* @__PURE__ */ jsxs8(Fragment3, { children: [
12540
- /* @__PURE__ */ jsx10(
13030
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
13031
+ /* @__PURE__ */ jsx11(
12541
13032
  RouteSVG,
12542
13033
  {
12543
13034
  points: screenPoints,
@@ -12545,7 +13036,7 @@ var ErrorOverlay = ({
12545
13036
  isHighlighted
12546
13037
  }
12547
13038
  ),
12548
- /* @__PURE__ */ jsx10(
13039
+ /* @__PURE__ */ jsx11(
12549
13040
  "div",
12550
13041
  {
12551
13042
  style: {
@@ -12580,7 +13071,7 @@ var ErrorOverlay = ({
12580
13071
  }
12581
13072
  }
12582
13073
  ),
12583
- /* @__PURE__ */ jsx10(
13074
+ /* @__PURE__ */ jsx11(
12584
13075
  "div",
12585
13076
  {
12586
13077
  style: {
@@ -12598,7 +13089,7 @@ var ErrorOverlay = ({
12598
13089
  pointerEvents: "none",
12599
13090
  transform: popupPosition.transform
12600
13091
  },
12601
- children: /* @__PURE__ */ jsx10(
13092
+ children: /* @__PURE__ */ jsx11(
12602
13093
  "div",
12603
13094
  {
12604
13095
  className: `error-message ${errorMessageStyles}`,
@@ -12617,8 +13108,8 @@ var ErrorOverlay = ({
12617
13108
  }),
12618
13109
  viaClearanceErrors.map((el, index) => {
12619
13110
  if (!el.pcb_center) return null;
12620
- const errorId = el.pcb_via_ids;
12621
- const isHighlighted = hoveredErrorId === errorId[0];
13111
+ const errorId = getErrorId(el, index);
13112
+ const isHighlighted = activeErrorId === errorId;
12622
13113
  if (!isHighlighted && !isShowingDRCErrors) return null;
12623
13114
  const errorCenter = applyToPoint11(transform, {
12624
13115
  x: el.pcb_center.x,
@@ -12630,8 +13121,8 @@ var ErrorOverlay = ({
12630
13121
  { x: errorCenter.x, y: errorCenter.y },
12631
13122
  containerRef
12632
13123
  );
12633
- return /* @__PURE__ */ jsxs8(Fragment3, { children: [
12634
- /* @__PURE__ */ jsx10(
13124
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
13125
+ /* @__PURE__ */ jsx11(
12635
13126
  RouteSVG,
12636
13127
  {
12637
13128
  points: [],
@@ -12639,7 +13130,7 @@ var ErrorOverlay = ({
12639
13130
  isHighlighted
12640
13131
  }
12641
13132
  ),
12642
- /* @__PURE__ */ jsx10(
13133
+ /* @__PURE__ */ jsx11(
12643
13134
  "div",
12644
13135
  {
12645
13136
  style: {
@@ -12674,7 +13165,7 @@ var ErrorOverlay = ({
12674
13165
  }
12675
13166
  }
12676
13167
  ),
12677
- /* @__PURE__ */ jsx10(
13168
+ /* @__PURE__ */ jsx11(
12678
13169
  "div",
12679
13170
  {
12680
13171
  style: {
@@ -12692,7 +13183,7 @@ var ErrorOverlay = ({
12692
13183
  pointerEvents: "none",
12693
13184
  transform: popupPosition.transform
12694
13185
  },
12695
- children: /* @__PURE__ */ jsx10(
13186
+ children: /* @__PURE__ */ jsx11(
12696
13187
  "div",
12697
13188
  {
12698
13189
  className: `error-message ${errorMessageStyles}`,
@@ -12705,7 +13196,7 @@ var ErrorOverlay = ({
12705
13196
  )
12706
13197
  }
12707
13198
  )
12708
- ] }, errorId[0]);
13199
+ ] }, errorId);
12709
13200
  }),
12710
13201
  componentErrors.map((el, index) => {
12711
13202
  const componentName = el.component_name || el.message?.match(/name "([^"]+)"/)?.[1];
@@ -12715,8 +13206,8 @@ var ErrorOverlay = ({
12715
13206
  (src) => src.type === "source_component" && src.source_component_id === comp.source_component_id && src.name === componentName
12716
13207
  )
12717
13208
  ) || [];
12718
- const errorId = el.error_id;
12719
- const isHighlighted = hoveredErrorId === errorId;
13209
+ const errorId = getErrorId(el, index);
13210
+ const isHighlighted = activeErrorId === errorId;
12720
13211
  if (!isHighlighted && !isShowingDRCErrors) return null;
12721
13212
  return components.map((comp, compIndex) => {
12722
13213
  let center = { x: 0, y: 0 };
@@ -12732,17 +13223,17 @@ var ErrorOverlay = ({
12732
13223
  const screenCenter = applyToPoint11(transform, center);
12733
13224
  if (Number.isNaN(screenCenter.x) || Number.isNaN(screenCenter.y))
12734
13225
  return null;
12735
- const scale6 = Math.abs(transform.a);
13226
+ const scale7 = Math.abs(transform.a);
12736
13227
  const baseRadius = 0.5;
12737
13228
  const minRadius = 8;
12738
13229
  const maxRadius = 30;
12739
13230
  const scaledRadius = Math.max(
12740
13231
  minRadius,
12741
- Math.min(maxRadius, baseRadius * scale6)
13232
+ Math.min(maxRadius, baseRadius * scale7)
12742
13233
  );
12743
13234
  const popupPosition = getPopupPosition(screenCenter, containerRef);
12744
- return /* @__PURE__ */ jsxs8(Fragment3, { children: [
12745
- /* @__PURE__ */ jsx10(
13235
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
13236
+ /* @__PURE__ */ jsx11(
12746
13237
  "svg",
12747
13238
  {
12748
13239
  style: {
@@ -12755,13 +13246,13 @@ var ErrorOverlay = ({
12755
13246
  },
12756
13247
  width: "100%",
12757
13248
  height: "100%",
12758
- children: isHighlighted ? /* @__PURE__ */ jsx10(
13249
+ children: isHighlighted ? /* @__PURE__ */ jsx11(
12759
13250
  "polygon",
12760
13251
  {
12761
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}`,
12762
13253
  fill: "#ff4444"
12763
13254
  }
12764
- ) : /* @__PURE__ */ jsx10(
13255
+ ) : /* @__PURE__ */ jsx11(
12765
13256
  "circle",
12766
13257
  {
12767
13258
  cx: screenCenter.x,
@@ -12775,7 +13266,7 @@ var ErrorOverlay = ({
12775
13266
  )
12776
13267
  }
12777
13268
  ),
12778
- /* @__PURE__ */ jsx10(
13269
+ /* @__PURE__ */ jsx11(
12779
13270
  "div",
12780
13271
  {
12781
13272
  style: {
@@ -12810,7 +13301,7 @@ var ErrorOverlay = ({
12810
13301
  }
12811
13302
  }
12812
13303
  ),
12813
- /* @__PURE__ */ jsx10(
13304
+ /* @__PURE__ */ jsx11(
12814
13305
  "div",
12815
13306
  {
12816
13307
  style: {
@@ -12828,7 +13319,7 @@ var ErrorOverlay = ({
12828
13319
  pointerEvents: "none",
12829
13320
  transform: popupPosition.transform
12830
13321
  },
12831
- children: /* @__PURE__ */ jsx10(
13322
+ children: /* @__PURE__ */ jsx11(
12832
13323
  "div",
12833
13324
  {
12834
13325
  className: `error-message ${errorMessageStyles}`,
@@ -12841,9 +13332,10 @@ var ErrorOverlay = ({
12841
13332
  )
12842
13333
  }
12843
13334
  )
12844
- ] }, errorId);
13335
+ ] }, `${errorId}_${compIndex}`);
12845
13336
  });
12846
- })
13337
+ }),
13338
+ focusScreenCenter && /* @__PURE__ */ jsx11(FocusMarkerSVG, { center: focusScreenCenter })
12847
13339
  ] });
12848
13340
  };
12849
13341
 
@@ -12917,7 +13409,7 @@ function filterTracesIfMultiple(filterTraces) {
12917
13409
  }
12918
13410
 
12919
13411
  // src/components/ElementOverlayBox.tsx
12920
- import { jsx as jsx11 } from "react/jsx-runtime";
13412
+ import { jsx as jsx12 } from "react/jsx-runtime";
12921
13413
  var containerStyle = {
12922
13414
  position: "absolute",
12923
13415
  left: 0,
@@ -13009,7 +13501,7 @@ var HighlightedPrimitiveBoxWithText = ({
13009
13501
  const overlayInfo = getTraceOverlayInfo(traceTextContext);
13010
13502
  if (!overlayInfo) return null;
13011
13503
  const yOffset = mousePos.y - 35;
13012
- return /* @__PURE__ */ jsx11(
13504
+ return /* @__PURE__ */ jsx12(
13013
13505
  "div",
13014
13506
  {
13015
13507
  style: {
@@ -13021,7 +13513,7 @@ var HighlightedPrimitiveBoxWithText = ({
13021
13513
  pointerEvents: "none",
13022
13514
  transform: "translateX(-50%)"
13023
13515
  },
13024
- children: /* @__PURE__ */ jsx11(
13516
+ children: /* @__PURE__ */ jsx12(
13025
13517
  "div",
13026
13518
  {
13027
13519
  style: {
@@ -13047,7 +13539,7 @@ var HighlightedPrimitiveBoxWithText = ({
13047
13539
  if (label.trim().length === 0) {
13048
13540
  return null;
13049
13541
  }
13050
- return /* @__PURE__ */ jsx11(
13542
+ return /* @__PURE__ */ jsx12(
13051
13543
  "div",
13052
13544
  {
13053
13545
  style: {
@@ -13061,7 +13553,7 @@ var HighlightedPrimitiveBoxWithText = ({
13061
13553
  transform: `rotate(${-rotation}deg)`,
13062
13554
  transformOrigin: "center center"
13063
13555
  },
13064
- children: /* @__PURE__ */ jsx11(
13556
+ children: /* @__PURE__ */ jsx12(
13065
13557
  "div",
13066
13558
  {
13067
13559
  style: {
@@ -13077,7 +13569,7 @@ var HighlightedPrimitiveBoxWithText = ({
13077
13569
  opacity: finalState ? 1 : si === 0 ? 1 : 0,
13078
13570
  transition: "width 0.2s, height 0.2s, margin-left 0.2s, margin-top 0.2s, opacity 0.2s"
13079
13571
  },
13080
- children: /* @__PURE__ */ jsx11(
13572
+ children: /* @__PURE__ */ jsx12(
13081
13573
  "div",
13082
13574
  {
13083
13575
  style: {
@@ -13125,7 +13617,7 @@ var ElementOverlayBox = ({
13125
13617
  is_showing_multiple_traces_length,
13126
13618
  elements
13127
13619
  });
13128
- 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(
13129
13621
  HighlightedPrimitiveBoxWithText,
13130
13622
  {
13131
13623
  primitive,
@@ -13169,7 +13661,7 @@ var COLORS = {
13169
13661
  };
13170
13662
 
13171
13663
  // src/components/AnchorOffsetOverlay/AnchorOffsetOverlay.tsx
13172
- 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";
13173
13665
  var AnchorOffsetOverlay = ({
13174
13666
  targets,
13175
13667
  transform,
@@ -13192,7 +13684,7 @@ var AnchorOffsetOverlay = ({
13192
13684
  anchorScreens.set(target.anchor_id, screenPoint);
13193
13685
  }
13194
13686
  });
13195
- return /* @__PURE__ */ jsx12(
13687
+ return /* @__PURE__ */ jsx13(
13196
13688
  "div",
13197
13689
  {
13198
13690
  style: {
@@ -13205,7 +13697,7 @@ var AnchorOffsetOverlay = ({
13205
13697
  pointerEvents: "none",
13206
13698
  zIndex: zIndexMap.dimensionOverlay
13207
13699
  },
13208
- children: /* @__PURE__ */ jsxs9(
13700
+ children: /* @__PURE__ */ jsxs10(
13209
13701
  "svg",
13210
13702
  {
13211
13703
  style: {
@@ -13235,8 +13727,8 @@ var AnchorOffsetOverlay = ({
13235
13727
  const shouldShowYLabel = yLineLength > VISUAL_CONFIG.MIN_LINE_LENGTH_FOR_LABEL;
13236
13728
  const xLabelText = `${target.display_offset_x ?? offsetX.toFixed(2)}mm`;
13237
13729
  const yLabelText = `${target.display_offset_y ?? offsetY.toFixed(2)}mm`;
13238
- return /* @__PURE__ */ jsxs9("g", { children: [
13239
- /* @__PURE__ */ jsx12(
13730
+ return /* @__PURE__ */ jsxs10("g", { children: [
13731
+ /* @__PURE__ */ jsx13(
13240
13732
  "line",
13241
13733
  {
13242
13734
  x1: anchorMarkerScreen.x,
@@ -13248,7 +13740,7 @@ var AnchorOffsetOverlay = ({
13248
13740
  strokeDasharray: VISUAL_CONFIG.LINE_DASH_PATTERN
13249
13741
  }
13250
13742
  ),
13251
- /* @__PURE__ */ jsx12(
13743
+ /* @__PURE__ */ jsx13(
13252
13744
  "line",
13253
13745
  {
13254
13746
  x1: targetScreen.x,
@@ -13260,7 +13752,7 @@ var AnchorOffsetOverlay = ({
13260
13752
  strokeDasharray: VISUAL_CONFIG.LINE_DASH_PATTERN
13261
13753
  }
13262
13754
  ),
13263
- target.type === "component" || target.type === "board" ? /* @__PURE__ */ jsx12(
13755
+ target.type === "component" || target.type === "board" ? /* @__PURE__ */ jsx13(
13264
13756
  "circle",
13265
13757
  {
13266
13758
  cx: targetScreen.x,
@@ -13272,8 +13764,8 @@ var AnchorOffsetOverlay = ({
13272
13764
  }
13273
13765
  ) : (
13274
13766
  // assumes "group"
13275
- /* @__PURE__ */ jsxs9(Fragment5, { children: [
13276
- /* @__PURE__ */ jsx12(
13767
+ /* @__PURE__ */ jsxs10(Fragment5, { children: [
13768
+ /* @__PURE__ */ jsx13(
13277
13769
  "line",
13278
13770
  {
13279
13771
  x1: targetScreen.x - VISUAL_CONFIG.ANCHOR_MARKER_SIZE,
@@ -13284,7 +13776,7 @@ var AnchorOffsetOverlay = ({
13284
13776
  strokeWidth: VISUAL_CONFIG.ANCHOR_MARKER_STROKE_WIDTH
13285
13777
  }
13286
13778
  ),
13287
- /* @__PURE__ */ jsx12(
13779
+ /* @__PURE__ */ jsx13(
13288
13780
  "line",
13289
13781
  {
13290
13782
  x1: targetScreen.x,
@@ -13297,7 +13789,7 @@ var AnchorOffsetOverlay = ({
13297
13789
  )
13298
13790
  ] })
13299
13791
  ),
13300
- shouldShowXLabel && /* @__PURE__ */ jsx12(
13792
+ shouldShowXLabel && /* @__PURE__ */ jsx13(
13301
13793
  "foreignObject",
13302
13794
  {
13303
13795
  x: Math.min(anchorMarkerScreen.x, targetScreen.x),
@@ -13305,10 +13797,10 @@ var AnchorOffsetOverlay = ({
13305
13797
  width: Math.abs(targetScreen.x - anchorMarkerScreen.x),
13306
13798
  height: 20,
13307
13799
  style: { overflow: "visible" },
13308
- children: /* @__PURE__ */ jsx12("div", { style: { ...labelStyle, textAlign: "center" }, children: xLabelText })
13800
+ children: /* @__PURE__ */ jsx13("div", { style: { ...labelStyle, textAlign: "center" }, children: xLabelText })
13309
13801
  }
13310
13802
  ),
13311
- shouldShowYLabel && /* @__PURE__ */ jsx12(
13803
+ shouldShowYLabel && /* @__PURE__ */ jsx13(
13312
13804
  "foreignObject",
13313
13805
  {
13314
13806
  x: targetScreen.x + yLabelOffset,
@@ -13316,7 +13808,7 @@ var AnchorOffsetOverlay = ({
13316
13808
  width: VISUAL_CONFIG.Y_LABEL_MIN_WIDTH,
13317
13809
  height: Math.abs(targetScreen.y - anchorMarkerScreen.y),
13318
13810
  style: { overflow: "visible" },
13319
- children: /* @__PURE__ */ jsx12(
13811
+ children: /* @__PURE__ */ jsx13(
13320
13812
  "div",
13321
13813
  {
13322
13814
  style: {
@@ -13335,8 +13827,8 @@ var AnchorOffsetOverlay = ({
13335
13827
  )
13336
13828
  ] }, target.id);
13337
13829
  }),
13338
- Array.from(anchorScreens.entries()).map(([anchorId, anchorScreen]) => /* @__PURE__ */ jsxs9("g", { children: [
13339
- /* @__PURE__ */ jsx12(
13830
+ Array.from(anchorScreens.entries()).map(([anchorId, anchorScreen]) => /* @__PURE__ */ jsxs10("g", { children: [
13831
+ /* @__PURE__ */ jsx13(
13340
13832
  "line",
13341
13833
  {
13342
13834
  x1: anchorScreen.x - VISUAL_CONFIG.ANCHOR_MARKER_SIZE,
@@ -13347,7 +13839,7 @@ var AnchorOffsetOverlay = ({
13347
13839
  strokeWidth: VISUAL_CONFIG.ANCHOR_MARKER_STROKE_WIDTH
13348
13840
  }
13349
13841
  ),
13350
- /* @__PURE__ */ jsx12(
13842
+ /* @__PURE__ */ jsx13(
13351
13843
  "line",
13352
13844
  {
13353
13845
  x1: anchorScreen.x,
@@ -13367,7 +13859,7 @@ var AnchorOffsetOverlay = ({
13367
13859
  };
13368
13860
 
13369
13861
  // src/components/AnchorOffsetOverlay/BoardAnchorOffsetOverlay.tsx
13370
- import { jsx as jsx13 } from "react/jsx-runtime";
13862
+ import { jsx as jsx14 } from "react/jsx-runtime";
13371
13863
  var BoardAnchorOffsetOverlay = ({
13372
13864
  elements,
13373
13865
  highlightedPrimitives,
@@ -13454,7 +13946,7 @@ var BoardAnchorOffsetOverlay = ({
13454
13946
  display_offset_y: target.group.display_offset_y
13455
13947
  };
13456
13948
  }).filter((t) => Boolean(t));
13457
- return /* @__PURE__ */ jsx13(
13949
+ return /* @__PURE__ */ jsx14(
13458
13950
  AnchorOffsetOverlay,
13459
13951
  {
13460
13952
  targets: sharedTargets,
@@ -13466,7 +13958,7 @@ var BoardAnchorOffsetOverlay = ({
13466
13958
  };
13467
13959
 
13468
13960
  // src/components/AnchorOffsetOverlay/GroupAnchorOffsetOverlay.tsx
13469
- import { jsx as jsx14 } from "react/jsx-runtime";
13961
+ import { jsx as jsx15 } from "react/jsx-runtime";
13470
13962
  var GroupAnchorOffsetOverlay = ({
13471
13963
  elements,
13472
13964
  highlightedPrimitives,
@@ -13582,7 +14074,7 @@ var GroupAnchorOffsetOverlay = ({
13582
14074
  display_offset_y: target.group.display_offset_y
13583
14075
  };
13584
14076
  }).filter((t) => Boolean(t));
13585
- return /* @__PURE__ */ jsx14(
14077
+ return /* @__PURE__ */ jsx15(
13586
14078
  AnchorOffsetOverlay,
13587
14079
  {
13588
14080
  targets: sharedTargets,
@@ -13595,7 +14087,7 @@ var GroupAnchorOffsetOverlay = ({
13595
14087
 
13596
14088
  // src/components/AnchorOffsetOverlay/ComponentBoundingBoxOverlay.tsx
13597
14089
  import { applyToPoint as applyToPoint13 } from "transformation-matrix";
13598
- import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
14090
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
13599
14091
  var calculateComponentBoundingBox = (component, elements) => {
13600
14092
  const componentId = component.pcb_component_id;
13601
14093
  const padsAndHoles = elements.filter(
@@ -13638,7 +14130,7 @@ var ComponentBoundingBoxOverlay = ({
13638
14130
  renderData.push({ component, bbox, group });
13639
14131
  }
13640
14132
  if (renderData.length === 0) return null;
13641
- return /* @__PURE__ */ jsx15(
14133
+ return /* @__PURE__ */ jsx16(
13642
14134
  "div",
13643
14135
  {
13644
14136
  style: {
@@ -13651,7 +14143,7 @@ var ComponentBoundingBoxOverlay = ({
13651
14143
  pointerEvents: "none",
13652
14144
  zIndex: zIndexMap.dimensionOverlay
13653
14145
  },
13654
- children: /* @__PURE__ */ jsx15(
14146
+ children: /* @__PURE__ */ jsx16(
13655
14147
  "svg",
13656
14148
  {
13657
14149
  style: { position: "absolute", left: 0, top: 0, pointerEvents: "none" },
@@ -13677,8 +14169,8 @@ var ComponentBoundingBoxOverlay = ({
13677
14169
  y: (bbox.minY + bbox.maxY) / 2
13678
14170
  };
13679
14171
  const componentCenterScreen = applyToPoint13(transform, componentCenter);
13680
- return /* @__PURE__ */ jsxs10("g", { children: [
13681
- /* @__PURE__ */ jsx15(
14172
+ return /* @__PURE__ */ jsxs11("g", { children: [
14173
+ /* @__PURE__ */ jsx16(
13682
14174
  "rect",
13683
14175
  {
13684
14176
  x: screenBbox.x,
@@ -13691,7 +14183,7 @@ var ComponentBoundingBoxOverlay = ({
13691
14183
  strokeDasharray: "4,4"
13692
14184
  }
13693
14185
  ),
13694
- /* @__PURE__ */ jsx15(
14186
+ /* @__PURE__ */ jsx16(
13695
14187
  "line",
13696
14188
  {
13697
14189
  x1: componentCenterScreen.x - 6,
@@ -13702,7 +14194,7 @@ var ComponentBoundingBoxOverlay = ({
13702
14194
  strokeWidth: 1.5
13703
14195
  }
13704
14196
  ),
13705
- /* @__PURE__ */ jsx15(
14197
+ /* @__PURE__ */ jsx16(
13706
14198
  "line",
13707
14199
  {
13708
14200
  x1: componentCenterScreen.x,
@@ -13713,7 +14205,7 @@ var ComponentBoundingBoxOverlay = ({
13713
14205
  strokeWidth: 1.5
13714
14206
  }
13715
14207
  ),
13716
- /* @__PURE__ */ jsx15(
14208
+ /* @__PURE__ */ jsx16(
13717
14209
  "circle",
13718
14210
  {
13719
14211
  cx: componentCenterScreen.x,
@@ -13733,7 +14225,7 @@ var ComponentBoundingBoxOverlay = ({
13733
14225
  };
13734
14226
 
13735
14227
  // src/components/AnchorOffsetOverlay/PanelAnchorOffsetOverlay.tsx
13736
- import { jsx as jsx16 } from "react/jsx-runtime";
14228
+ import { jsx as jsx17 } from "react/jsx-runtime";
13737
14229
  var PanelAnchorOffsetOverlay = ({
13738
14230
  elements,
13739
14231
  highlightedPrimitives,
@@ -13782,7 +14274,7 @@ var PanelAnchorOffsetOverlay = ({
13782
14274
  display_offset_y: target.board.display_offset_y
13783
14275
  };
13784
14276
  }).filter((t) => Boolean(t));
13785
- return /* @__PURE__ */ jsx16(
14277
+ return /* @__PURE__ */ jsx17(
13786
14278
  AnchorOffsetOverlay,
13787
14279
  {
13788
14280
  targets: sharedTargets,
@@ -13794,7 +14286,7 @@ var PanelAnchorOffsetOverlay = ({
13794
14286
  };
13795
14287
 
13796
14288
  // src/components/MouseElementTracker.tsx
13797
- 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";
13798
14290
  var getPolygonBoundingBox = (points) => {
13799
14291
  if (points.length === 0) return null;
13800
14292
  let minX = points[0].x;
@@ -13981,7 +14473,7 @@ var MouseElementTracker = ({
13981
14473
  setMousedPrimitives(newMousedPrimitives);
13982
14474
  onMouseHoverOverPrimitives(newMousedPrimitives);
13983
14475
  };
13984
- return /* @__PURE__ */ jsxs11(
14476
+ return /* @__PURE__ */ jsxs12(
13985
14477
  "div",
13986
14478
  {
13987
14479
  ref: containerRef,
@@ -14005,7 +14497,7 @@ var MouseElementTracker = ({
14005
14497
  },
14006
14498
  children: [
14007
14499
  children,
14008
- /* @__PURE__ */ jsx17(
14500
+ /* @__PURE__ */ jsx18(
14009
14501
  ElementOverlayBox,
14010
14502
  {
14011
14503
  elements,
@@ -14013,8 +14505,8 @@ var MouseElementTracker = ({
14013
14505
  highlightedPrimitives
14014
14506
  }
14015
14507
  ),
14016
- transform && /* @__PURE__ */ jsxs11(Fragment6, { children: [
14017
- /* @__PURE__ */ jsx17(
14508
+ transform && /* @__PURE__ */ jsxs12(Fragment6, { children: [
14509
+ /* @__PURE__ */ jsx18(
14018
14510
  BoardAnchorOffsetOverlay,
14019
14511
  {
14020
14512
  elements,
@@ -14024,7 +14516,7 @@ var MouseElementTracker = ({
14024
14516
  containerHeight: height
14025
14517
  }
14026
14518
  ),
14027
- /* @__PURE__ */ jsx17(
14519
+ /* @__PURE__ */ jsx18(
14028
14520
  GroupAnchorOffsetOverlay,
14029
14521
  {
14030
14522
  elements,
@@ -14034,7 +14526,7 @@ var MouseElementTracker = ({
14034
14526
  containerHeight: height
14035
14527
  }
14036
14528
  ),
14037
- /* @__PURE__ */ jsx17(
14529
+ /* @__PURE__ */ jsx18(
14038
14530
  ComponentBoundingBoxOverlay,
14039
14531
  {
14040
14532
  elements,
@@ -14044,7 +14536,7 @@ var MouseElementTracker = ({
14044
14536
  containerHeight: height
14045
14537
  }
14046
14538
  ),
14047
- /* @__PURE__ */ jsx17(
14539
+ /* @__PURE__ */ jsx18(
14048
14540
  PanelAnchorOffsetOverlay,
14049
14541
  {
14050
14542
  elements,
@@ -14064,7 +14556,7 @@ var MouseElementTracker = ({
14064
14556
  import { applyToPoint as applyToPoint15 } from "transformation-matrix";
14065
14557
  import { identity as identity8 } from "transformation-matrix";
14066
14558
  import { useRef as useRef10, useEffect as useEffect13 } from "react";
14067
- import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
14559
+ import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
14068
14560
  var GROUP_COLORS = [
14069
14561
  "rgb(255, 100, 100)",
14070
14562
  "rgb(100, 255, 100)",
@@ -14300,14 +14792,14 @@ var PcbGroupOverlay = ({
14300
14792
  is_showing_group_anchor_offsets,
14301
14793
  hoveredComponentIds
14302
14794
  ]);
14303
- return /* @__PURE__ */ jsxs12(
14795
+ return /* @__PURE__ */ jsxs13(
14304
14796
  "div",
14305
14797
  {
14306
14798
  ref: containerRef,
14307
14799
  style: { position: "relative", width: "100%", height: "100%" },
14308
14800
  children: [
14309
14801
  children,
14310
- /* @__PURE__ */ jsx18(
14802
+ /* @__PURE__ */ jsx19(
14311
14803
  "canvas",
14312
14804
  {
14313
14805
  ref: canvasRef,
@@ -14329,7 +14821,7 @@ var PcbGroupOverlay = ({
14329
14821
  // src/components/RatsNestOverlay.tsx
14330
14822
  import { applyToPoint as applyToPoint16, identity as identity9 } from "transformation-matrix";
14331
14823
  import { useMemo as useMemo6 } from "react";
14332
- import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
14824
+ import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
14333
14825
  var RatsNestOverlay = ({ transform, soup, children }) => {
14334
14826
  const isShowingRatsNest = useGlobalStore((s) => s.is_showing_rats_nest);
14335
14827
  const { netMap, idToNetMap } = useMemo6(
@@ -14392,9 +14884,9 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
14392
14884
  }, [soup, netMap, idToNetMap, isShowingRatsNest]);
14393
14885
  if (!soup || !isShowingRatsNest) return children;
14394
14886
  if (!transform) transform = identity9();
14395
- return /* @__PURE__ */ jsxs13("div", { style: { position: "relative" }, children: [
14887
+ return /* @__PURE__ */ jsxs14("div", { style: { position: "relative" }, children: [
14396
14888
  children,
14397
- /* @__PURE__ */ jsx19(
14889
+ /* @__PURE__ */ jsx20(
14398
14890
  "svg",
14399
14891
  {
14400
14892
  style: {
@@ -14410,7 +14902,7 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
14410
14902
  children: ratsNestLines.map(({ key, startPoint, endPoint, isInNet }) => {
14411
14903
  const transformedStart = applyToPoint16(transform, startPoint);
14412
14904
  const transformedEnd = applyToPoint16(transform, endPoint);
14413
- return /* @__PURE__ */ jsx19(
14905
+ return /* @__PURE__ */ jsx20(
14414
14906
  "line",
14415
14907
  {
14416
14908
  x1: transformedStart.x,
@@ -14431,10 +14923,10 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
14431
14923
 
14432
14924
  // src/components/ToolbarOverlay.tsx
14433
14925
  import {
14434
- useEffect as useEffect16,
14926
+ useEffect as useEffect17,
14435
14927
  useState as useState11,
14436
- useCallback as useCallback9,
14437
- useRef as useRef13,
14928
+ useCallback as useCallback10,
14929
+ useRef as useRef14,
14438
14930
  useLayoutEffect as useLayoutEffect2
14439
14931
  } from "react";
14440
14932
  import { css as css3 } from "@emotion/css";
@@ -14442,7 +14934,7 @@ import { css as css3 } from "@emotion/css";
14442
14934
  // package.json
14443
14935
  var package_default = {
14444
14936
  name: "@tscircuit/pcb-viewer",
14445
- version: "1.11.364",
14937
+ version: "1.11.365",
14446
14938
  main: "dist/index.js",
14447
14939
  type: "module",
14448
14940
  repository: "tscircuit/pcb-viewer",
@@ -14597,7 +15089,7 @@ var useMobileTouch = (onClick, options = { stopPropagation: true }) => {
14597
15089
  };
14598
15090
 
14599
15091
  // src/components/ToolbarButton.tsx
14600
- import { jsx as jsx20 } from "react/jsx-runtime";
15092
+ import { jsx as jsx21 } from "react/jsx-runtime";
14601
15093
  var ToolbarButton = ({
14602
15094
  children,
14603
15095
  isSmallScreen,
@@ -14605,7 +15097,7 @@ var ToolbarButton = ({
14605
15097
  ...props
14606
15098
  }) => {
14607
15099
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
14608
- return /* @__PURE__ */ jsx20(
15100
+ return /* @__PURE__ */ jsx21(
14609
15101
  "div",
14610
15102
  {
14611
15103
  ...props,
@@ -14633,8 +15125,87 @@ var ToolbarButton = ({
14633
15125
  };
14634
15126
 
14635
15127
  // src/components/ToolbarErrorDropdown.tsx
14636
- import { useCallback as useCallback8, useMemo as useMemo7, useState as useState10 } from "react";
14637
- 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";
14638
15209
  var CopyErrorButton = ({
14639
15210
  errorId,
14640
15211
  errorMessage,
@@ -14644,7 +15215,7 @@ var CopyErrorButton = ({
14644
15215
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(
14645
15216
  () => onCopy(errorMessage, errorId)
14646
15217
  );
14647
- return /* @__PURE__ */ jsx21(
15218
+ return /* @__PURE__ */ jsx22(
14648
15219
  "button",
14649
15220
  {
14650
15221
  type: "button",
@@ -14664,7 +15235,7 @@ var CopyErrorButton = ({
14664
15235
  ...touchStyle
14665
15236
  },
14666
15237
  ...touchHandlers,
14667
- 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" }) })
14668
15239
  }
14669
15240
  );
14670
15241
  };
@@ -14673,7 +15244,9 @@ var ToolbarErrorDropdown = ({
14673
15244
  isOpen,
14674
15245
  isSmallScreen,
14675
15246
  onToggle,
14676
- setHoveredErrorId
15247
+ onClose,
15248
+ setHoveredErrorId,
15249
+ setFocusedErrorId
14677
15250
  }) => {
14678
15251
  const [copiedErrorId, setCopiedErrorId] = useState10(null);
14679
15252
  const [collapsedErrorGroups, setCollapsedErrorGroups] = useState10(
@@ -14683,6 +15256,11 @@ var ToolbarErrorDropdown = ({
14683
15256
  /* @__PURE__ */ new Set()
14684
15257
  );
14685
15258
  const [, copyToClipboard] = useCopyToClipboard_default();
15259
+ const { handleErrorSelect, isSelectedError } = useToolbarErrorFocus({
15260
+ onClose,
15261
+ setHoveredErrorId,
15262
+ setFocusedErrorId
15263
+ });
14686
15264
  const errorElements = useMemo7(
14687
15265
  () => elements?.filter(
14688
15266
  (el) => el.type.includes("error")
@@ -14690,24 +15268,11 @@ var ToolbarErrorDropdown = ({
14690
15268
  [elements]
14691
15269
  );
14692
15270
  const errorCount = errorElements.length;
14693
- const groupedErrorElements = useMemo7(() => {
14694
- const groups = /* @__PURE__ */ new Map();
14695
- errorElements.forEach((error, index) => {
14696
- const errorType = error.error_type || "unknown_error";
14697
- const existingGroup = groups.get(errorType) || [];
14698
- existingGroup.push({
14699
- error,
14700
- index,
14701
- errorId: error.pcb_trace_error_id
14702
- });
14703
- groups.set(errorType, existingGroup);
14704
- });
14705
- return Array.from(groups.entries()).map(([errorType, errors]) => ({
14706
- errorType,
14707
- errors
14708
- }));
14709
- }, [errorElements]);
14710
- const toggleErrorGroup = useCallback8((errorType) => {
15271
+ const groupedErrorElements = useMemo7(
15272
+ () => groupErrorsByType(errorElements),
15273
+ [errorElements]
15274
+ );
15275
+ const toggleErrorGroup = useCallback9((errorType) => {
14711
15276
  setCollapsedErrorGroups((prev) => {
14712
15277
  const next = new Set(prev);
14713
15278
  if (next.has(errorType)) {
@@ -14718,7 +15283,7 @@ var ToolbarErrorDropdown = ({
14718
15283
  return next;
14719
15284
  });
14720
15285
  }, []);
14721
- const toggleExpandedError = useCallback8((errorId) => {
15286
+ const toggleExpandedError = useCallback9((errorId) => {
14722
15287
  setExpandedErrorIds((prev) => {
14723
15288
  const next = new Set(prev);
14724
15289
  if (next.has(errorId)) {
@@ -14729,20 +15294,20 @@ var ToolbarErrorDropdown = ({
14729
15294
  return next;
14730
15295
  });
14731
15296
  }, []);
14732
- return /* @__PURE__ */ jsxs14("div", { style: { position: "relative" }, children: [
14733
- /* @__PURE__ */ jsx21(
15297
+ return /* @__PURE__ */ jsxs15("div", { style: { position: "relative" }, children: [
15298
+ /* @__PURE__ */ jsx22(
14734
15299
  ToolbarButton,
14735
15300
  {
14736
15301
  isSmallScreen,
14737
15302
  style: errorCount > 0 ? { color: "red" } : void 0,
14738
15303
  onClick: onToggle,
14739
- children: /* @__PURE__ */ jsxs14("div", { children: [
15304
+ children: /* @__PURE__ */ jsxs15("div", { children: [
14740
15305
  errorCount,
14741
15306
  " errors"
14742
15307
  ] })
14743
15308
  }
14744
15309
  ),
14745
- isOpen && errorCount > 0 && /* @__PURE__ */ jsx21(
15310
+ isOpen && errorCount > 0 && /* @__PURE__ */ jsx22(
14746
15311
  "div",
14747
15312
  {
14748
15313
  style: {
@@ -14762,14 +15327,14 @@ var ToolbarErrorDropdown = ({
14762
15327
  },
14763
15328
  children: groupedErrorElements.map(({ errorType, errors }, groupIndex) => {
14764
15329
  const isGroupCollapsed = collapsedErrorGroups.has(errorType);
14765
- return /* @__PURE__ */ jsxs14(
15330
+ return /* @__PURE__ */ jsxs15(
14766
15331
  "div",
14767
15332
  {
14768
15333
  style: {
14769
15334
  borderBottom: groupIndex < groupedErrorElements.length - 1 ? "1px solid #444" : "none"
14770
15335
  },
14771
15336
  children: [
14772
- /* @__PURE__ */ jsxs14(
15337
+ /* @__PURE__ */ jsxs15(
14773
15338
  "div",
14774
15339
  {
14775
15340
  style: {
@@ -14806,7 +15371,7 @@ var ToolbarErrorDropdown = ({
14806
15371
  toggleErrorGroup(errorType);
14807
15372
  },
14808
15373
  children: [
14809
- /* @__PURE__ */ jsxs14(
15374
+ /* @__PURE__ */ jsxs15(
14810
15375
  "div",
14811
15376
  {
14812
15377
  style: {
@@ -14816,7 +15381,7 @@ var ToolbarErrorDropdown = ({
14816
15381
  color: "#ff6b6b"
14817
15382
  },
14818
15383
  children: [
14819
- /* @__PURE__ */ jsx21(
15384
+ /* @__PURE__ */ jsx22(
14820
15385
  "div",
14821
15386
  {
14822
15387
  style: {
@@ -14829,7 +15394,7 @@ var ToolbarErrorDropdown = ({
14829
15394
  children: "\u203A"
14830
15395
  }
14831
15396
  ),
14832
- /* @__PURE__ */ jsx21(
15397
+ /* @__PURE__ */ jsx22(
14833
15398
  "div",
14834
15399
  {
14835
15400
  style: {
@@ -14842,7 +15407,7 @@ var ToolbarErrorDropdown = ({
14842
15407
  ]
14843
15408
  }
14844
15409
  ),
14845
- /* @__PURE__ */ jsx21(
15410
+ /* @__PURE__ */ jsx22(
14846
15411
  "div",
14847
15412
  {
14848
15413
  style: {
@@ -14860,14 +15425,14 @@ var ToolbarErrorDropdown = ({
14860
15425
  !isGroupCollapsed && errors.map(({ error, index, errorId }) => {
14861
15426
  const isExpanded = expandedErrorIds.has(errorId);
14862
15427
  const errorMessage = error.message ?? "No error message";
14863
- return /* @__PURE__ */ jsxs14(
15428
+ return /* @__PURE__ */ jsxs15(
14864
15429
  "div",
14865
15430
  {
14866
15431
  style: {
14867
15432
  borderTop: "1px solid #3a3a3a"
14868
15433
  },
14869
15434
  children: [
14870
- /* @__PURE__ */ jsxs14(
15435
+ /* @__PURE__ */ jsxs15(
14871
15436
  "div",
14872
15437
  {
14873
15438
  style: {
@@ -14886,7 +15451,9 @@ var ToolbarErrorDropdown = ({
14886
15451
  },
14887
15452
  onMouseLeave: (e) => {
14888
15453
  e.currentTarget.style.backgroundColor = "#2a2a2a";
14889
- setHoveredErrorId(null);
15454
+ if (!isSelectedError(errorId)) {
15455
+ setHoveredErrorId(null);
15456
+ }
14890
15457
  },
14891
15458
  onTouchStart: (e) => {
14892
15459
  e.stopPropagation();
@@ -14897,15 +15464,14 @@ var ToolbarErrorDropdown = ({
14897
15464
  e.stopPropagation();
14898
15465
  e.preventDefault();
14899
15466
  e.currentTarget.style.backgroundColor = "#2a2a2a";
14900
- setHoveredErrorId(null);
14901
- toggleExpandedError(errorId);
15467
+ handleErrorSelect(errorId);
14902
15468
  },
14903
15469
  onClick: (e) => {
14904
15470
  e.stopPropagation();
14905
- toggleExpandedError(errorId);
15471
+ handleErrorSelect(errorId);
14906
15472
  },
14907
15473
  children: [
14908
- /* @__PURE__ */ jsx21(
15474
+ /* @__PURE__ */ jsx22(
14909
15475
  "div",
14910
15476
  {
14911
15477
  style: {
@@ -14918,20 +15484,34 @@ var ToolbarErrorDropdown = ({
14918
15484
  whiteSpace: "nowrap",
14919
15485
  userSelect: "text"
14920
15486
  },
14921
- onMouseDown: (event) => event.stopPropagation(),
14922
- onClick: (event) => event.stopPropagation(),
14923
15487
  children: errorMessage
14924
15488
  }
14925
15489
  ),
14926
- /* @__PURE__ */ jsx21(
14927
- "div",
15490
+ /* @__PURE__ */ jsx22(
15491
+ "button",
14928
15492
  {
15493
+ type: "button",
15494
+ "aria-label": isExpanded ? "Collapse error details" : "Expand error details",
14929
15495
  style: {
14930
15496
  color: "#888",
14931
15497
  fontSize: "16px",
14932
15498
  transform: isExpanded ? "rotate(0deg)" : "rotate(90deg)",
14933
15499
  transition: "transform 0.2s ease",
14934
- 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);
14935
15515
  },
14936
15516
  children: "\u203A"
14937
15517
  }
@@ -14939,7 +15519,7 @@ var ToolbarErrorDropdown = ({
14939
15519
  ]
14940
15520
  }
14941
15521
  ),
14942
- isExpanded && /* @__PURE__ */ jsxs14(
15522
+ isExpanded && /* @__PURE__ */ jsxs15(
14943
15523
  "div",
14944
15524
  {
14945
15525
  "data-error-id": index,
@@ -14951,7 +15531,7 @@ var ToolbarErrorDropdown = ({
14951
15531
  position: "relative"
14952
15532
  },
14953
15533
  children: [
14954
- /* @__PURE__ */ jsx21(
15534
+ /* @__PURE__ */ jsx22(
14955
15535
  "div",
14956
15536
  {
14957
15537
  style: {
@@ -14969,7 +15549,7 @@ var ToolbarErrorDropdown = ({
14969
15549
  children: errorMessage
14970
15550
  }
14971
15551
  ),
14972
- /* @__PURE__ */ jsx21(
15552
+ /* @__PURE__ */ jsx22(
14973
15553
  CopyErrorButton,
14974
15554
  {
14975
15555
  errorId,
@@ -15001,10 +15581,10 @@ var ToolbarErrorDropdown = ({
15001
15581
  };
15002
15582
 
15003
15583
  // src/components/ToolbarOverlay.tsx
15004
- import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
15584
+ import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
15005
15585
  var LayerButton = ({ name, selected, onClick }) => {
15006
15586
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
15007
- return /* @__PURE__ */ jsxs15(
15587
+ return /* @__PURE__ */ jsxs16(
15008
15588
  "div",
15009
15589
  {
15010
15590
  className: css3`
@@ -15021,8 +15601,8 @@ var LayerButton = ({ name, selected, onClick }) => {
15021
15601
  ...touchHandlers,
15022
15602
  style: touchStyle,
15023
15603
  children: [
15024
- /* @__PURE__ */ jsx22("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
15025
- /* @__PURE__ */ jsx22(
15604
+ /* @__PURE__ */ jsx23("span", { style: { marginRight: 2, opacity: selected ? 1 : 0 }, children: "\u2022" }),
15605
+ /* @__PURE__ */ jsx23(
15026
15606
  "span",
15027
15607
  {
15028
15608
  style: {
@@ -15043,7 +15623,7 @@ var CheckboxMenuItem = ({
15043
15623
  onClick
15044
15624
  }) => {
15045
15625
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
15046
- return /* @__PURE__ */ jsxs15(
15626
+ return /* @__PURE__ */ jsxs16(
15047
15627
  "div",
15048
15628
  {
15049
15629
  className: css3`
@@ -15063,16 +15643,16 @@ var CheckboxMenuItem = ({
15063
15643
  ...touchHandlers,
15064
15644
  style: touchStyle,
15065
15645
  children: [
15066
- /* @__PURE__ */ jsx22("input", { type: "checkbox", checked, onChange: () => {
15646
+ /* @__PURE__ */ jsx23("input", { type: "checkbox", checked, onChange: () => {
15067
15647
  }, readOnly: true }),
15068
- /* @__PURE__ */ jsx22("span", { style: { color: "#eee" }, children: label })
15648
+ /* @__PURE__ */ jsx23("span", { style: { color: "#eee" }, children: label })
15069
15649
  ]
15070
15650
  }
15071
15651
  );
15072
15652
  };
15073
15653
  var RadioMenuItem = ({ label, checked, onClick }) => {
15074
15654
  const { style: touchStyle, ...touchHandlers } = useMobileTouch(onClick);
15075
- return /* @__PURE__ */ jsxs15(
15655
+ return /* @__PURE__ */ jsxs16(
15076
15656
  "div",
15077
15657
  {
15078
15658
  className: css3`
@@ -15092,9 +15672,9 @@ var RadioMenuItem = ({ label, checked, onClick }) => {
15092
15672
  ...touchHandlers,
15093
15673
  style: touchStyle,
15094
15674
  children: [
15095
- /* @__PURE__ */ jsx22("input", { type: "radio", checked, onChange: () => {
15675
+ /* @__PURE__ */ jsx23("input", { type: "radio", checked, onChange: () => {
15096
15676
  }, readOnly: true }),
15097
- /* @__PURE__ */ jsx22("span", { style: { color: "#eee" }, children: label })
15677
+ /* @__PURE__ */ jsx23("span", { style: { color: "#eee" }, children: label })
15098
15678
  ]
15099
15679
  }
15100
15680
  );
@@ -15121,7 +15701,8 @@ var ToolbarOverlay = ({ children, elements }) => {
15121
15701
  setIsShowingSilkscreen,
15122
15702
  setIsShowingFabricationNotes,
15123
15703
  setPcbGroupViewMode,
15124
- setHoveredErrorId
15704
+ setHoveredErrorId,
15705
+ setFocusedErrorId
15125
15706
  } = useGlobalStore((s) => ({
15126
15707
  isMouseOverContainer: s.is_mouse_over_container,
15127
15708
  setIsMouseOverContainer: s.setIsMouseOverContainer,
@@ -15158,13 +15739,14 @@ var ToolbarOverlay = ({ children, elements }) => {
15158
15739
  setIsShowingSilkscreen: s.setIsShowingSilkscreen,
15159
15740
  setIsShowingFabricationNotes: s.setIsShowingFabricationNotes,
15160
15741
  setPcbGroupViewMode: s.setPcbGroupViewMode,
15161
- setHoveredErrorId: s.setHoveredErrorId
15742
+ setHoveredErrorId: s.setHoveredErrorId,
15743
+ setFocusedErrorId: s.setFocusedErrorId
15162
15744
  }));
15163
15745
  const [isViewMenuOpen, setViewMenuOpen] = useState11(false);
15164
15746
  const [isLayerMenuOpen, setLayerMenuOpen] = useState11(false);
15165
15747
  const [isErrorsOpen, setErrorsOpen] = useState11(false);
15166
15748
  const [measureToolArmed, setMeasureToolArmed] = useState11(false);
15167
- useEffect16(() => {
15749
+ useEffect17(() => {
15168
15750
  const arm = () => setMeasureToolArmed(true);
15169
15751
  const disarm = () => setMeasureToolArmed(false);
15170
15752
  window.addEventListener("arm-dimension-tool", arm);
@@ -15182,8 +15764,8 @@ var ToolbarOverlay = ({ children, elements }) => {
15182
15764
  "bottom"
15183
15765
  ];
15184
15766
  const processedLayers = availableLayers;
15185
- const hasRunInitialMouseCheck = useRef13(false);
15186
- const hotkeyBoundaryRef = useRef13(null);
15767
+ const hasRunInitialMouseCheck = useRef14(false);
15768
+ const hotkeyBoundaryRef = useRef14(null);
15187
15769
  const hotKeyCallbacks = {
15188
15770
  "1": availableLayers[0] ? () => selectLayer(availableLayers[0]) : () => {
15189
15771
  },
@@ -15228,25 +15810,24 @@ var ToolbarOverlay = ({ children, elements }) => {
15228
15810
  document.removeEventListener("mousemove", checkMousePosition);
15229
15811
  };
15230
15812
  }, [setIsMouseOverContainer]);
15231
- const handleMouseEnter = useCallback9(() => {
15813
+ const handleMouseEnter = useCallback10(() => {
15232
15814
  setIsMouseOverContainer(true);
15233
15815
  }, [setIsMouseOverContainer]);
15234
- const handleMouseMove = useCallback9(() => {
15816
+ const handleMouseMove = useCallback10(() => {
15235
15817
  if (!isMouseOverContainer) {
15236
15818
  setIsMouseOverContainer(true);
15237
15819
  }
15238
15820
  }, [isMouseOverContainer, setIsMouseOverContainer]);
15239
- const handleMouseLeave = useCallback9(() => {
15821
+ const handleMouseLeave = useCallback10(() => {
15240
15822
  setIsMouseOverContainer(false);
15241
15823
  setLayerMenuOpen(false);
15242
15824
  setViewMenuOpen(false);
15243
15825
  setErrorsOpen(false);
15244
- setHoveredErrorId(null);
15245
- }, [setIsMouseOverContainer, setHoveredErrorId]);
15246
- const handleLayerMenuToggle = useCallback9(() => {
15826
+ }, [setIsMouseOverContainer]);
15827
+ const handleLayerMenuToggle = useCallback10(() => {
15247
15828
  setLayerMenuOpen(!isLayerMenuOpen);
15248
15829
  }, [isLayerMenuOpen]);
15249
- const handleErrorsToggle = useCallback9(() => {
15830
+ const handleErrorsToggle = useCallback10(() => {
15250
15831
  const newErrorsOpen = !isErrorsOpen;
15251
15832
  setErrorsOpen(newErrorsOpen);
15252
15833
  if (newErrorsOpen) {
@@ -15256,33 +15837,36 @@ var ToolbarOverlay = ({ children, elements }) => {
15256
15837
  setHoveredErrorId(null);
15257
15838
  }
15258
15839
  }, [isErrorsOpen, setHoveredErrorId]);
15259
- const handleEditTraceToggle = useCallback9(() => {
15840
+ const closeErrorsMenu = useCallback10(() => {
15841
+ setErrorsOpen(false);
15842
+ }, []);
15843
+ const handleEditTraceToggle = useCallback10(() => {
15260
15844
  setEditMode(editModes.in_draw_trace_mode ? "off" : "draw_trace");
15261
15845
  }, [editModes.in_draw_trace_mode, setEditMode]);
15262
- const handleMoveComponentToggle = useCallback9(() => {
15846
+ const handleMoveComponentToggle = useCallback10(() => {
15263
15847
  setEditMode(editModes.in_move_footprint_mode ? "off" : "move_footprint");
15264
15848
  }, [editModes.in_move_footprint_mode, setEditMode]);
15265
- const handleRatsNestToggle = useCallback9(() => {
15849
+ const handleRatsNestToggle = useCallback10(() => {
15266
15850
  setIsShowingRatsNest(!viewSettings.is_showing_rats_nest);
15267
15851
  }, [viewSettings.is_showing_rats_nest, setIsShowingRatsNest]);
15268
- const handleMeasureToolClick = useCallback9(() => {
15852
+ const handleMeasureToolClick = useCallback10(() => {
15269
15853
  setMeasureToolArmed(true);
15270
15854
  window.dispatchEvent(new Event("arm-dimension-tool"));
15271
15855
  }, []);
15272
- const handleViewMenuToggle = useCallback9(() => {
15856
+ const handleViewMenuToggle = useCallback10(() => {
15273
15857
  const newViewMenuOpen = !isViewMenuOpen;
15274
15858
  setViewMenuOpen(newViewMenuOpen);
15275
15859
  if (newViewMenuOpen) {
15276
15860
  setErrorsOpen(false);
15277
15861
  }
15278
15862
  }, [isViewMenuOpen]);
15279
- const stopCanvasInteractionPropagation = useCallback9(
15863
+ const stopCanvasInteractionPropagation = useCallback10(
15280
15864
  (event) => {
15281
15865
  event.stopPropagation();
15282
15866
  },
15283
15867
  []
15284
15868
  );
15285
- return /* @__PURE__ */ jsxs15(
15869
+ return /* @__PURE__ */ jsxs16(
15286
15870
  "div",
15287
15871
  {
15288
15872
  ref: hotkeyBoundaryRef,
@@ -15292,7 +15876,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15292
15876
  onMouseMove: handleMouseMove,
15293
15877
  children: [
15294
15878
  children,
15295
- /* @__PURE__ */ jsxs15(
15879
+ /* @__PURE__ */ jsxs16(
15296
15880
  "div",
15297
15881
  {
15298
15882
  style: {
@@ -15313,7 +15897,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15313
15897
  ]
15314
15898
  }
15315
15899
  ),
15316
- /* @__PURE__ */ jsxs15(
15900
+ /* @__PURE__ */ jsxs16(
15317
15901
  "div",
15318
15902
  {
15319
15903
  "data-toolbar-overlay": true,
@@ -15343,7 +15927,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15343
15927
  fontFamily: "sans-serif"
15344
15928
  },
15345
15929
  children: [
15346
- /* @__PURE__ */ jsxs15(
15930
+ /* @__PURE__ */ jsxs16(
15347
15931
  ToolbarButton,
15348
15932
  {
15349
15933
  isSmallScreen,
@@ -15354,10 +15938,10 @@ var ToolbarOverlay = ({ children, elements }) => {
15354
15938
  }
15355
15939
  },
15356
15940
  children: [
15357
- /* @__PURE__ */ jsxs15("div", { children: [
15941
+ /* @__PURE__ */ jsxs16("div", { children: [
15358
15942
  "layer:",
15359
15943
  " ",
15360
- /* @__PURE__ */ jsx22(
15944
+ /* @__PURE__ */ jsx23(
15361
15945
  "span",
15362
15946
  {
15363
15947
  style: {
@@ -15369,7 +15953,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15369
15953
  }
15370
15954
  )
15371
15955
  ] }),
15372
- 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(
15373
15957
  LayerButton,
15374
15958
  {
15375
15959
  name: layer,
@@ -15383,68 +15967,70 @@ var ToolbarOverlay = ({ children, elements }) => {
15383
15967
  ]
15384
15968
  }
15385
15969
  ),
15386
- /* @__PURE__ */ jsx22(
15970
+ /* @__PURE__ */ jsx23(
15387
15971
  ToolbarErrorDropdown,
15388
15972
  {
15389
15973
  elements,
15390
15974
  isOpen: isErrorsOpen,
15391
15975
  isSmallScreen,
15392
15976
  onToggle: handleErrorsToggle,
15393
- setHoveredErrorId
15977
+ onClose: closeErrorsMenu,
15978
+ setHoveredErrorId,
15979
+ setFocusedErrorId
15394
15980
  }
15395
15981
  ),
15396
- /* @__PURE__ */ jsx22(
15982
+ /* @__PURE__ */ jsx23(
15397
15983
  ToolbarButton,
15398
15984
  {
15399
15985
  isSmallScreen,
15400
15986
  style: {},
15401
15987
  onClick: handleEditTraceToggle,
15402
- children: /* @__PURE__ */ jsxs15("div", { children: [
15988
+ children: /* @__PURE__ */ jsxs16("div", { children: [
15403
15989
  editModes.in_draw_trace_mode ? "\u2716 " : "",
15404
15990
  "Edit Traces"
15405
15991
  ] })
15406
15992
  }
15407
15993
  ),
15408
- /* @__PURE__ */ jsx22(
15994
+ /* @__PURE__ */ jsx23(
15409
15995
  ToolbarButton,
15410
15996
  {
15411
15997
  isSmallScreen,
15412
15998
  style: {},
15413
15999
  onClick: handleMoveComponentToggle,
15414
- children: /* @__PURE__ */ jsxs15("div", { children: [
16000
+ children: /* @__PURE__ */ jsxs16("div", { children: [
15415
16001
  editModes.in_move_footprint_mode ? "\u2716 " : "",
15416
16002
  "Move Components"
15417
16003
  ] })
15418
16004
  }
15419
16005
  ),
15420
- /* @__PURE__ */ jsx22(
16006
+ /* @__PURE__ */ jsx23(
15421
16007
  ToolbarButton,
15422
16008
  {
15423
16009
  isSmallScreen,
15424
16010
  style: {},
15425
16011
  onClick: handleRatsNestToggle,
15426
- children: /* @__PURE__ */ jsxs15("div", { children: [
16012
+ children: /* @__PURE__ */ jsxs16("div", { children: [
15427
16013
  viewSettings.is_showing_rats_nest ? "\u2716 " : "",
15428
16014
  "Rats Nest"
15429
16015
  ] })
15430
16016
  }
15431
16017
  ),
15432
- /* @__PURE__ */ jsx22(
16018
+ /* @__PURE__ */ jsx23(
15433
16019
  ToolbarButton,
15434
16020
  {
15435
16021
  isSmallScreen,
15436
16022
  style: measureToolArmed ? { backgroundColor: "#444" } : {},
15437
16023
  onClick: handleMeasureToolClick,
15438
- children: /* @__PURE__ */ jsx22("div", { children: "\u{1F4CF}" })
16024
+ children: /* @__PURE__ */ jsx23("div", { children: "\u{1F4CF}" })
15439
16025
  }
15440
16026
  ),
15441
- /* @__PURE__ */ jsx22(
16027
+ /* @__PURE__ */ jsx23(
15442
16028
  ToolbarButton,
15443
16029
  {
15444
16030
  isSmallScreen,
15445
16031
  onClick: handleViewMenuToggle,
15446
- children: /* @__PURE__ */ jsxs15("div", { children: [
15447
- /* @__PURE__ */ jsxs15(
16032
+ children: /* @__PURE__ */ jsxs16("div", { children: [
16033
+ /* @__PURE__ */ jsxs16(
15448
16034
  "div",
15449
16035
  {
15450
16036
  style: {
@@ -15454,7 +16040,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15454
16040
  },
15455
16041
  children: [
15456
16042
  "View",
15457
- /* @__PURE__ */ jsx22(
16043
+ /* @__PURE__ */ jsx23(
15458
16044
  "span",
15459
16045
  {
15460
16046
  style: {
@@ -15469,8 +16055,8 @@ var ToolbarOverlay = ({ children, elements }) => {
15469
16055
  ]
15470
16056
  }
15471
16057
  ),
15472
- isViewMenuOpen && /* @__PURE__ */ jsxs15("div", { style: { marginTop: 4, minWidth: 120 }, children: [
15473
- /* @__PURE__ */ jsx22(
16058
+ isViewMenuOpen && /* @__PURE__ */ jsxs16("div", { style: { marginTop: 4, minWidth: 120 }, children: [
16059
+ /* @__PURE__ */ jsx23(
15474
16060
  CheckboxMenuItem,
15475
16061
  {
15476
16062
  label: "Show All Trace Lengths",
@@ -15482,7 +16068,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15482
16068
  }
15483
16069
  }
15484
16070
  ),
15485
- /* @__PURE__ */ jsx22(
16071
+ /* @__PURE__ */ jsx23(
15486
16072
  CheckboxMenuItem,
15487
16073
  {
15488
16074
  label: "Show Autorouting Animation",
@@ -15494,7 +16080,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15494
16080
  }
15495
16081
  }
15496
16082
  ),
15497
- /* @__PURE__ */ jsx22(
16083
+ /* @__PURE__ */ jsx23(
15498
16084
  CheckboxMenuItem,
15499
16085
  {
15500
16086
  label: "Show DRC Errors",
@@ -15504,7 +16090,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15504
16090
  }
15505
16091
  }
15506
16092
  ),
15507
- /* @__PURE__ */ jsx22(
16093
+ /* @__PURE__ */ jsx23(
15508
16094
  CheckboxMenuItem,
15509
16095
  {
15510
16096
  label: "Show Copper Pours",
@@ -15516,7 +16102,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15516
16102
  }
15517
16103
  }
15518
16104
  ),
15519
- /* @__PURE__ */ jsx22(
16105
+ /* @__PURE__ */ jsx23(
15520
16106
  CheckboxMenuItem,
15521
16107
  {
15522
16108
  label: "Show Courtyards",
@@ -15526,7 +16112,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15526
16112
  }
15527
16113
  }
15528
16114
  ),
15529
- /* @__PURE__ */ jsx22(
16115
+ /* @__PURE__ */ jsx23(
15530
16116
  CheckboxMenuItem,
15531
16117
  {
15532
16118
  label: "Show Solder Mask",
@@ -15536,7 +16122,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15536
16122
  }
15537
16123
  }
15538
16124
  ),
15539
- /* @__PURE__ */ jsx22(
16125
+ /* @__PURE__ */ jsx23(
15540
16126
  CheckboxMenuItem,
15541
16127
  {
15542
16128
  label: "Show Silkscreen",
@@ -15546,7 +16132,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15546
16132
  }
15547
16133
  }
15548
16134
  ),
15549
- /* @__PURE__ */ jsx22(
16135
+ /* @__PURE__ */ jsx23(
15550
16136
  CheckboxMenuItem,
15551
16137
  {
15552
16138
  label: "Show Fabrication Notes",
@@ -15558,7 +16144,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15558
16144
  }
15559
16145
  }
15560
16146
  ),
15561
- /* @__PURE__ */ jsx22(
16147
+ /* @__PURE__ */ jsx23(
15562
16148
  CheckboxMenuItem,
15563
16149
  {
15564
16150
  label: "Show Group Anchor Offsets",
@@ -15570,7 +16156,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15570
16156
  }
15571
16157
  }
15572
16158
  ),
15573
- /* @__PURE__ */ jsx22(
16159
+ /* @__PURE__ */ jsx23(
15574
16160
  CheckboxMenuItem,
15575
16161
  {
15576
16162
  label: "Show PCB Groups",
@@ -15580,8 +16166,8 @@ var ToolbarOverlay = ({ children, elements }) => {
15580
16166
  }
15581
16167
  }
15582
16168
  ),
15583
- viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs15("div", { style: { marginLeft: 16 }, children: [
15584
- /* @__PURE__ */ jsx22(
16169
+ viewSettings.is_showing_pcb_groups && /* @__PURE__ */ jsxs16("div", { style: { marginLeft: 16 }, children: [
16170
+ /* @__PURE__ */ jsx23(
15585
16171
  RadioMenuItem,
15586
16172
  {
15587
16173
  label: "Show All Groups",
@@ -15591,7 +16177,7 @@ var ToolbarOverlay = ({ children, elements }) => {
15591
16177
  }
15592
16178
  }
15593
16179
  ),
15594
- /* @__PURE__ */ jsx22(
16180
+ /* @__PURE__ */ jsx23(
15595
16181
  RadioMenuItem,
15596
16182
  {
15597
16183
  label: "Show Named Groups",
@@ -15615,13 +16201,15 @@ var ToolbarOverlay = ({ children, elements }) => {
15615
16201
  };
15616
16202
 
15617
16203
  // src/components/CanvasElementsRenderer.tsx
15618
- import { jsx as jsx23 } from "react/jsx-runtime";
16204
+ import { jsx as jsx24 } from "react/jsx-runtime";
15619
16205
  var CanvasElementsRenderer = (props) => {
15620
16206
  const { transform, elements } = props;
15621
- const hoveredErrorId = useGlobalStore((state) => state.hovered_error_id);
15622
- const isShowingCopperPours = useGlobalStore(
15623
- (state) => state.is_showing_copper_pours
15624
- );
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;
15625
16213
  const elementsToRender = useMemo8(
15626
16214
  () => isShowingCopperPours ? elements : elements.filter((elm) => elm.type !== "pcb_copper_pour"),
15627
16215
  [elements, isShowingCopperPours]
@@ -15640,27 +16228,71 @@ var CanvasElementsRenderer = (props) => {
15640
16228
  primitiveIdsInMousedOverNet: []
15641
16229
  });
15642
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
+ );
15643
16237
  const errorRelatedIds = useMemo8(() => {
15644
- if (!hoveredErrorId) return [];
16238
+ if (!activeErrorId) return [];
15645
16239
  const errorElements = elements.filter(
15646
16240
  (el) => el.type.includes("error")
15647
16241
  );
15648
- const hoveredError = errorElements.find((el) => {
15649
- return el.error_id === hoveredErrorId;
16242
+ const activeError = errorElements.find((el, index) => {
16243
+ return getErrorId(el, index) === activeErrorId;
15650
16244
  });
15651
- if (!hoveredError) return [];
15652
- const relatedIds = [];
15653
- if (hoveredError.pcb_trace_id) {
15654
- relatedIds.push(hoveredError.pcb_trace_id);
15655
- }
15656
- if (hoveredError.pcb_port_ids) {
15657
- relatedIds.push(...hoveredError.pcb_port_ids);
15658
- }
15659
- if (hoveredError.pcb_via_ids) {
15660
- 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;
15661
16251
  }
15662
- return relatedIds;
15663
- }, [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
+ ]);
15664
16296
  const primitives = useMemo8(() => {
15665
16297
  const combinedPrimitiveIds = [
15666
16298
  ...hoverState.primitiveIdsInMousedOverNet,
@@ -15672,7 +16304,7 @@ var CanvasElementsRenderer = (props) => {
15672
16304
  primitiveIdsInMousedOverNet: combinedPrimitiveIds
15673
16305
  });
15674
16306
  }, [primitivesWithoutInteractionMetadata, hoverState, errorRelatedIds]);
15675
- const onMouseOverPrimitives = useCallback10(
16307
+ const onMouseOverPrimitives = useCallback11(
15676
16308
  (primitivesHoveredOver) => {
15677
16309
  const primitiveIdsInMousedOverNet = [];
15678
16310
  for (const primitive of primitivesHoveredOver) {
@@ -15705,14 +16337,14 @@ var CanvasElementsRenderer = (props) => {
15705
16337
  },
15706
16338
  [connectivityMap]
15707
16339
  );
15708
- return /* @__PURE__ */ jsx23(
16340
+ return /* @__PURE__ */ jsx24(
15709
16341
  MouseElementTracker,
15710
16342
  {
15711
16343
  elements: elementsToRender,
15712
16344
  transform,
15713
16345
  primitives: primitivesWithoutInteractionMetadata,
15714
16346
  onMouseHoverOverPrimitives: onMouseOverPrimitives,
15715
- children: /* @__PURE__ */ jsx23(
16347
+ children: /* @__PURE__ */ jsx24(
15716
16348
  EditPlacementOverlay,
15717
16349
  {
15718
16350
  disabled: !props.allowEditing,
@@ -15721,7 +16353,7 @@ var CanvasElementsRenderer = (props) => {
15721
16353
  cancelPanDrag: props.cancelPanDrag,
15722
16354
  onCreateEditEvent: props.onCreateEditEvent,
15723
16355
  onModifyEditEvent: props.onModifyEditEvent,
15724
- children: /* @__PURE__ */ jsx23(
16356
+ children: /* @__PURE__ */ jsx24(
15725
16357
  EditTraceHintOverlay,
15726
16358
  {
15727
16359
  disabled: !props.allowEditing,
@@ -15730,29 +16362,29 @@ var CanvasElementsRenderer = (props) => {
15730
16362
  cancelPanDrag: props.cancelPanDrag,
15731
16363
  onCreateEditEvent: props.onCreateEditEvent,
15732
16364
  onModifyEditEvent: props.onModifyEditEvent,
15733
- children: /* @__PURE__ */ jsx23(
16365
+ children: /* @__PURE__ */ jsx24(
15734
16366
  DimensionOverlay,
15735
16367
  {
15736
16368
  transform,
15737
16369
  focusOnHover: props.focusOnHover,
15738
16370
  primitives: primitivesWithoutInteractionMetadata,
15739
- 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(
15740
16372
  PcbGroupOverlay,
15741
16373
  {
15742
16374
  transform,
15743
16375
  elements,
15744
16376
  hoveredComponentIds,
15745
- children: /* @__PURE__ */ jsx23(
16377
+ children: /* @__PURE__ */ jsx24(
15746
16378
  DebugGraphicsOverlay,
15747
16379
  {
15748
16380
  transform,
15749
16381
  debugGraphics: props.debugGraphics,
15750
- children: /* @__PURE__ */ jsx23(
16382
+ children: /* @__PURE__ */ jsx24(
15751
16383
  WarningGraphicsOverlay,
15752
16384
  {
15753
16385
  transform,
15754
16386
  elements,
15755
- children: /* @__PURE__ */ jsx23(
16387
+ children: /* @__PURE__ */ jsx24(
15756
16388
  CanvasPrimitiveRenderer,
15757
16389
  {
15758
16390
  transform,
@@ -15834,8 +16466,8 @@ var calculateBoardSizeKey = (circuitJson) => {
15834
16466
  };
15835
16467
 
15836
16468
  // src/PCBViewer.tsx
15837
- import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
15838
- 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));
15839
16471
  var PCBViewer = ({
15840
16472
  circuitJson,
15841
16473
  debugGraphics,
@@ -15853,7 +16485,7 @@ var PCBViewer = ({
15853
16485
  );
15854
16486
  const [ref, refDimensions] = useMeasure_default();
15855
16487
  const [transform, setTransformInternal] = useState13(defaultTransform);
15856
- const shouldAllowCanvasInteraction = useCallback11(
16488
+ const shouldAllowCanvasInteraction = useCallback12(
15857
16489
  (event) => {
15858
16490
  const target = event.target;
15859
16491
  if (!(target instanceof Element)) return true;
@@ -15873,8 +16505,8 @@ var PCBViewer = ({
15873
16505
  });
15874
16506
  let [editEvents, setEditEvents] = useState13([]);
15875
16507
  editEvents = editEventsProp ?? editEvents;
15876
- const initialRenderCompleted = useRef14(false);
15877
- const touchStartRef = useRef14(null);
16508
+ const initialRenderCompleted = useRef16(false);
16509
+ const touchStartRef = useRef16(null);
15878
16510
  const circuitJsonKey = useMemo9(
15879
16511
  () => calculateCircuitJsonKey(circuitJson),
15880
16512
  [circuitJson]
@@ -15892,15 +16524,15 @@ var PCBViewer = ({
15892
16524
  (elmBounds.height ?? 0) / height2,
15893
16525
  100
15894
16526
  ) * 0.75;
15895
- const targetTransform = compose7(
15896
- translate11((elmBounds.width ?? 0) / 2, (elmBounds.height ?? 0) / 2),
15897
- scale5(scaleFactor, -scaleFactor, 0, 0),
15898
- 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)
15899
16531
  );
15900
16532
  setTransform(targetTransform);
15901
16533
  return;
15902
16534
  };
15903
- useEffect17(() => {
16535
+ useEffect19(() => {
15904
16536
  if (!refDimensions?.width) return;
15905
16537
  if (!circuitJson) return;
15906
16538
  if (circuitJson.length === 0) return;
@@ -15909,7 +16541,7 @@ var PCBViewer = ({
15909
16541
  initialRenderCompleted.current = true;
15910
16542
  }
15911
16543
  }, [circuitJson, refDimensions]);
15912
- useEffect17(() => {
16544
+ useEffect19(() => {
15913
16545
  if (initialRenderCompleted.current === true) {
15914
16546
  resetTransform();
15915
16547
  }
@@ -15943,23 +16575,24 @@ var PCBViewer = ({
15943
16575
  }),
15944
16576
  [initialState, disablePcbGroups]
15945
16577
  );
15946
- return /* @__PURE__ */ jsxs16(
16578
+ return /* @__PURE__ */ jsxs17(
15947
16579
  "div",
15948
16580
  {
15949
16581
  ref: transformRef,
15950
16582
  style: { position: "relative" },
15951
16583
  onContextMenu: (event) => event.preventDefault(),
15952
16584
  children: [
15953
- /* @__PURE__ */ jsx24("div", { ref, children: /* @__PURE__ */ jsxs16(
16585
+ /* @__PURE__ */ jsx25("div", { ref, children: /* @__PURE__ */ jsxs17(
15954
16586
  ContextProviders,
15955
16587
  {
15956
16588
  initialState: mergedInitialState,
15957
16589
  disablePcbGroups,
15958
16590
  children: [
15959
- /* @__PURE__ */ jsx24(
16591
+ /* @__PURE__ */ jsx25(
15960
16592
  CanvasElementsRenderer,
15961
16593
  {
15962
16594
  transform,
16595
+ setTransform,
15963
16596
  height,
15964
16597
  width: refDimensions.width,
15965
16598
  allowEditing,
@@ -15981,11 +16614,11 @@ var PCBViewer = ({
15981
16614
  },
15982
16615
  refDimensions.width
15983
16616
  ),
15984
- /* @__PURE__ */ jsx24(ToastContainer, {})
16617
+ /* @__PURE__ */ jsx25(ToastContainer, {})
15985
16618
  ]
15986
16619
  }
15987
16620
  ) }),
15988
- clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx24(
16621
+ clickToInteractEnabled && !isInteractionEnabled && /* @__PURE__ */ jsx25(
15989
16622
  "div",
15990
16623
  {
15991
16624
  onClick: () => {
@@ -16022,7 +16655,7 @@ var PCBViewer = ({
16022
16655
  justifyContent: "center",
16023
16656
  touchAction: "pan-x pan-y pinch-zoom"
16024
16657
  },
16025
- children: /* @__PURE__ */ jsx24(
16658
+ children: /* @__PURE__ */ jsx25(
16026
16659
  "div",
16027
16660
  {
16028
16661
  style: {