@tscircuit/schematic-viewer 2.0.77 → 2.0.79

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
@@ -1,201 +1,51 @@
1
1
  // lib/components/SchematicViewer.tsx
2
+ import { su as su4 } from "@tscircuit/soup-util";
2
3
  import {
3
4
  convertCircuitJsonToSchematicSvg
4
5
  } from "circuit-to-svg";
5
- import { su as su7 } from "@tscircuit/soup-util";
6
-
7
- // lib/hooks/useChangeSchematicComponentLocationsInSvg.ts
8
- import "@tscircuit/soup-util";
9
- import "transformation-matrix";
10
- import { useEffect, useRef } from "react";
11
6
 
12
- // lib/utils/get-component-offset-due-to-events.ts
13
- var getComponentOffsetDueToEvents = ({
14
- editEvents,
15
- schematic_component_id
16
- }) => {
17
- const editEventsForComponent = editEvents.filter(
18
- (event) => "schematic_component_id" in event && event.schematic_component_id === schematic_component_id
19
- ).filter(
20
- (event) => "edit_event_type" in event && event.edit_event_type === "edit_schematic_component_location"
21
- );
22
- const totalOffsetX = editEventsForComponent.reduce((acc, event) => {
23
- return acc + event.new_center.x - event.original_center.x;
24
- }, 0);
25
- const totalOffsetY = editEventsForComponent.reduce((acc, event) => {
26
- return acc + event.new_center.y - event.original_center.y;
27
- }, 0);
28
- return {
29
- x: totalOffsetX,
30
- y: totalOffsetY
31
- };
7
+ // lib/hooks/useLocalStorage.ts
8
+ import { useCallback } from "react";
9
+ var STORAGE_KEYS = {
10
+ IS_SHOWING_SCHEMATIC_GROUPS: "schematic_viewer_show_groups",
11
+ IS_SHOWING_SCHEMATIC_PORTS: "schematic_viewer_show_ports",
12
+ SELECTED_SCHEMATIC_SHEET: "schematic_viewer_selected_sheet"
32
13
  };
33
-
34
- // lib/hooks/useChangeSchematicComponentLocationsInSvg.ts
35
- var useChangeSchematicComponentLocationsInSvg = ({
36
- svgDivRef,
37
- realToSvgProjection,
38
- svgToScreenProjection,
39
- activeEditEvent,
40
- editEvents
41
- }) => {
42
- const lastSvgContentRef = useRef(null);
43
- useEffect(() => {
44
- const svg = svgDivRef.current;
45
- if (!svg) return;
46
- const observer = new MutationObserver((mutations) => {
47
- const currentSvgContent = svg.innerHTML;
48
- if (currentSvgContent !== lastSvgContentRef.current) {
49
- lastSvgContentRef.current = currentSvgContent;
50
- applyTransforms();
51
- }
52
- });
53
- const applyTransforms = () => {
54
- const componentsThatHaveBeenMoved = /* @__PURE__ */ new Set();
55
- for (const event of editEvents) {
56
- if ("edit_event_type" in event && event.edit_event_type === "edit_schematic_component_location") {
57
- componentsThatHaveBeenMoved.add(event.schematic_component_id);
58
- }
59
- }
60
- if (activeEditEvent) {
61
- componentsThatHaveBeenMoved.add(activeEditEvent.schematic_component_id);
62
- }
63
- const allComponents = svg.querySelectorAll(
64
- '[data-circuit-json-type="schematic_component"]'
65
- );
66
- for (const component of Array.from(allComponents)) {
67
- const schematic_component_id = component.getAttribute(
68
- "data-schematic-component-id"
69
- );
70
- const offsetMm = getComponentOffsetDueToEvents({
71
- editEvents: [
72
- ...editEvents,
73
- ...activeEditEvent ? [activeEditEvent] : []
74
- ],
75
- schematic_component_id
76
- });
77
- const offsetPx = {
78
- x: offsetMm.x * realToSvgProjection.a,
79
- y: offsetMm.y * realToSvgProjection.d
80
- };
81
- const style = component.style;
82
- style.transform = `translate(${offsetPx.x}px, ${offsetPx.y}px)`;
83
- if (activeEditEvent?.schematic_component_id === schematic_component_id) {
84
- style.outline = "solid 2px rgba(255,0,0,0.5)";
85
- style.outlineOffset = "5px";
86
- } else if (style.outline) {
87
- style.outline = "";
88
- }
89
- }
90
- };
91
- observer.observe(svg, {
92
- childList: true,
93
- // Watch for changes to the child elements
94
- subtree: false,
95
- // Watch for changes in the entire subtree
96
- characterData: false
97
- // Watch for changes to text content
98
- });
99
- applyTransforms();
100
- return () => {
101
- observer.disconnect();
102
- };
103
- }, [svgDivRef, editEvents, activeEditEvent]);
14
+ var getStoredBoolean = (key, defaultValue) => {
15
+ if (typeof window === "undefined") return defaultValue;
16
+ try {
17
+ const stored = localStorage.getItem(key);
18
+ return stored !== null ? JSON.parse(stored) : defaultValue;
19
+ } catch {
20
+ return defaultValue;
21
+ }
104
22
  };
105
-
106
- // lib/hooks/useChangeSchematicTracesForMovedComponents.ts
107
- import { useEffect as useEffect2, useRef as useRef2 } from "react";
108
- import { su as su2 } from "@tscircuit/soup-util";
109
- var useChangeSchematicTracesForMovedComponents = ({
110
- svgDivRef,
111
- circuitJson,
112
- activeEditEvent,
113
- editEvents
114
- }) => {
115
- const lastSvgContentRef = useRef2(null);
116
- useEffect2(() => {
117
- const svg = svgDivRef.current;
118
- if (!svg) return;
119
- const updateTraceStyles = () => {
120
- const allTraces = svg.querySelectorAll(
121
- '[data-circuit-json-type="schematic_trace"] path'
122
- );
123
- for (const trace of Array.from(allTraces)) {
124
- trace.setAttribute("stroke-dasharray", "0");
125
- trace.style.animation = "";
126
- }
127
- for (const editEvent of [
128
- ...editEvents,
129
- ...activeEditEvent ? [activeEditEvent] : []
130
- ]) {
131
- if ("schematic_component_id" in editEvent && editEvent.edit_event_type === "edit_schematic_component_location") {
132
- const sch_component = su2(circuitJson).schematic_component.get(
133
- editEvent.schematic_component_id
134
- );
135
- if (!sch_component) return;
136
- const src_ports = su2(circuitJson).source_port.list({
137
- source_component_id: sch_component.source_component_id
138
- });
139
- const src_port_ids = new Set(src_ports.map((sp) => sp.source_port_id));
140
- const src_traces = su2(circuitJson).source_trace.list().filter(
141
- (st) => st.connected_source_port_ids?.some(
142
- (spi) => src_port_ids.has(spi)
143
- )
144
- );
145
- const src_trace_ids = new Set(
146
- src_traces.map((st) => st.source_trace_id)
147
- );
148
- const schematic_traces = su2(circuitJson).schematic_trace.list().filter((st) => src_trace_ids.has(st.source_trace_id));
149
- schematic_traces.forEach((trace) => {
150
- const traceElements = svg.querySelectorAll(
151
- `[data-schematic-trace-id="${trace.schematic_trace_id}"] path`
152
- );
153
- for (const traceElement of Array.from(traceElements)) {
154
- if (traceElement.getAttribute("class")?.includes("invisible"))
155
- continue;
156
- traceElement.setAttribute("stroke-dasharray", "20,20");
157
- traceElement.style.animation = "dash-animation 350ms linear infinite, pulse-animation 900ms linear infinite";
158
- if (!svg.querySelector("style#dash-animation")) {
159
- const style = document.createElement("style");
160
- style.id = "dash-animation";
161
- style.textContent = `
162
- @keyframes dash-animation {
163
- to {
164
- stroke-dashoffset: -40;
165
- }
166
- }
167
- @keyframes pulse-animation {
168
- 0% { opacity: 0.6; }
169
- 50% { opacity: 0.2; }
170
- 100% { opacity: 0.6; }
171
- }
172
- `;
173
- svg.appendChild(style);
174
- }
175
- }
176
- });
177
- }
178
- }
179
- };
180
- updateTraceStyles();
181
- const observer = new MutationObserver(updateTraceStyles);
182
- observer.observe(svg, {
183
- childList: true,
184
- // Watch for changes to the child elements
185
- subtree: false,
186
- // Watch for changes in the entire subtree
187
- characterData: false
188
- // Watch for changes to text content
189
- });
190
- return () => {
191
- observer.disconnect();
192
- };
193
- }, [svgDivRef, activeEditEvent, circuitJson, editEvents]);
23
+ var setStoredBoolean = (key, value) => {
24
+ if (typeof window === "undefined") return;
25
+ try {
26
+ localStorage.setItem(key, JSON.stringify(value));
27
+ } catch {
28
+ }
29
+ };
30
+ var getStoredString = (key) => {
31
+ if (typeof window === "undefined") return null;
32
+ try {
33
+ return localStorage.getItem(key);
34
+ } catch {
35
+ return null;
36
+ }
37
+ };
38
+ var setStoredString = (key, value) => {
39
+ if (typeof window === "undefined") return;
40
+ try {
41
+ localStorage.setItem(key, value);
42
+ } catch {
43
+ }
194
44
  };
195
45
 
196
46
  // lib/hooks/useSchematicGroupsOverlay.ts
197
- import { useEffect as useEffect3 } from "react";
198
- import { su as su3 } from "@tscircuit/soup-util";
47
+ import { useEffect } from "react";
48
+ import { su } from "@tscircuit/soup-util";
199
49
  var GROUP_COLORS = [
200
50
  "#8B0000",
201
51
  // Dark Red
@@ -220,7 +70,7 @@ var GROUP_COLORS = [
220
70
  ];
221
71
  var useSchematicGroupsOverlay = (options) => {
222
72
  const { svgDivRef, circuitJson, circuitJsonKey, showGroups } = options;
223
- useEffect3(() => {
73
+ useEffect(() => {
224
74
  if (svgDivRef.current) {
225
75
  const existingOverlays = svgDivRef.current.querySelectorAll(
226
76
  ".schematic-group-overlay"
@@ -239,8 +89,8 @@ var useSchematicGroupsOverlay = (options) => {
239
89
  const existingOverlays = svg.querySelectorAll(".schematic-group-overlay");
240
90
  existingOverlays.forEach((overlay) => overlay.remove());
241
91
  try {
242
- const sourceGroups = su3(circuitJson).source_group?.list().filter((x) => !!!x.is_subcircuit) || [];
243
- const schematicComponents = su3(circuitJson).schematic_component?.list() || [];
92
+ const sourceGroups = su(circuitJson).source_group?.list().filter((x) => !!!x.is_subcircuit) || [];
93
+ const schematicComponents = su(circuitJson).schematic_component?.list() || [];
244
94
  const sourceGroupHierarchy = /* @__PURE__ */ new Map();
245
95
  sourceGroups.forEach((group) => {
246
96
  const groupWithParent = group;
@@ -278,7 +128,7 @@ var useSchematicGroupsOverlay = (options) => {
278
128
  if (hasMeaningfulGroups) {
279
129
  const groupMap = /* @__PURE__ */ new Map();
280
130
  for (const comp of schematicComponents) {
281
- const sourceComp = su3(circuitJson).source_component.get(
131
+ const sourceComp = su(circuitJson).source_component.get(
282
132
  comp.source_component_id
283
133
  );
284
134
  if (sourceComp?.source_group_id) {
@@ -462,8 +312,8 @@ function calculateGroupBounds(components, svg) {
462
312
  }
463
313
 
464
314
  // lib/hooks/useSchematicNetHover.ts
465
- import { su as su4 } from "@tscircuit/soup-util";
466
- import { useEffect as useEffect4 } from "react";
315
+ import { su as su2 } from "@tscircuit/soup-util";
316
+ import { useEffect as useEffect2 } from "react";
467
317
  var FADED_CLASS = "sch-net-faded";
468
318
  var TRACE_SELECTOR = "g.trace[data-subcircuit-connectivity-map-key], g.trace-overlays[data-subcircuit-connectivity-map-key]";
469
319
  var NET_LABEL_SELECTOR = "[data-schematic-net-label-id]";
@@ -473,7 +323,7 @@ var useSchematicNetHover = ({
473
323
  circuitJsonKey,
474
324
  enabled
475
325
  }) => {
476
- useEffect4(() => {
326
+ useEffect2(() => {
477
327
  const svgDiv = svgDivRef.current;
478
328
  if (!enabled || !svgDiv) return;
479
329
  const { componentIdToKeys, netLabelIdToKey } = buildNetRegistry(circuitJson);
@@ -549,7 +399,7 @@ var useSchematicNetHover = ({
549
399
  }, [svgDivRef, circuitJsonKey, enabled]);
550
400
  };
551
401
  function buildNetRegistry(circuitJson) {
552
- const cju = su4(circuitJson);
402
+ const cju = su2(circuitJson);
553
403
  const srcCompToSchComp = /* @__PURE__ */ new Map();
554
404
  for (const c of cju.schematic_component.list()) {
555
405
  if (c.source_component_id) {
@@ -586,23 +436,18 @@ var debug = Debug("schematic-viewer");
586
436
  var enableDebug = () => {
587
437
  Debug.enable("schematic-viewer*");
588
438
  };
589
- var debug_default = debug;
590
439
 
591
440
  // lib/components/SchematicViewer.tsx
592
- import { useCallback as useCallback6, useEffect as useEffect11, useMemo as useMemo4, useRef as useRef8, useState as useState6 } from "react";
593
- import {
594
- fromString,
595
- identity,
596
- toString as transformToString
597
- } from "transformation-matrix";
441
+ import { useCallback as useCallback6, useEffect as useEffect9, useMemo as useMemo4, useRef as useRef6, useState as useState6 } from "react";
442
+ import { toString as transformToString } from "transformation-matrix";
598
443
  import { useMouseMatrixTransform } from "use-mouse-matrix-transform";
599
444
 
600
445
  // lib/hooks/use-resize-handling.ts
601
- import { useEffect as useEffect5, useState } from "react";
446
+ import { useEffect as useEffect3, useState } from "react";
602
447
  var useResizeHandling = (containerRef) => {
603
448
  const [containerWidth, setContainerWidth] = useState(0);
604
449
  const [containerHeight, setContainerHeight] = useState(0);
605
- useEffect5(() => {
450
+ useEffect3(() => {
606
451
  if (!containerRef.current) return;
607
452
  const updateDimensions = () => {
608
453
  const rect = containerRef.current?.getBoundingClientRect();
@@ -621,597 +466,123 @@ var useResizeHandling = (containerRef) => {
621
466
  return { containerWidth, containerHeight };
622
467
  };
623
468
 
624
- // lib/hooks/useComponentDragging.ts
625
- import { su as su5 } from "@tscircuit/soup-util";
626
- import { useCallback, useEffect as useEffect6, useRef as useRef3, useState as useState2 } from "react";
627
- import { compose as compose2 } from "transformation-matrix";
628
- var debug2 = debug_default.extend("useComponentDragging");
629
- var useComponentDragging = ({
630
- onEditEvent,
631
- editEvents = [],
632
- circuitJson,
633
- cancelDrag,
634
- svgToScreenProjection,
635
- realToSvgProjection,
636
- enabled = false,
637
- snapToGrid = false
638
- }) => {
639
- const [activeEditEvent, setActiveEditEvent] = useState2(null);
640
- const realToScreenProjection = compose2(
641
- realToSvgProjection,
642
- svgToScreenProjection
643
- );
644
- const dragStartPosRef = useRef3(null);
645
- const activeEditEventRef = useRef3(null);
646
- const componentPositionsRef = useRef3(
647
- /* @__PURE__ */ new Map()
648
- );
649
- useEffect6(() => {
650
- editEvents.forEach((event) => {
651
- if ("edit_event_type" in event && event.edit_event_type === "edit_schematic_component_location" && !event.in_progress) {
652
- componentPositionsRef.current.set(event.schematic_component_id, {
653
- ...event.new_center
654
- });
655
- }
656
- });
657
- }, [editEvents]);
658
- const startDrag = useCallback(
659
- (clientX, clientY, target) => {
660
- if (!enabled) return false;
661
- const componentGroup = target.closest(
662
- '[data-circuit-json-type="schematic_component"]'
663
- );
664
- if (!componentGroup) return false;
665
- const schematic_component_id = componentGroup.getAttribute(
666
- "data-schematic-component-id"
667
- );
668
- if (!schematic_component_id) return false;
669
- if (cancelDrag) cancelDrag();
670
- const schematic_component = su5(circuitJson).schematic_component.get(
671
- schematic_component_id
672
- );
673
- if (!schematic_component) return false;
674
- dragStartPosRef.current = { x: clientX, y: clientY };
675
- let current_position;
676
- const trackedPosition = componentPositionsRef.current.get(
677
- schematic_component_id
678
- );
679
- if (trackedPosition) {
680
- current_position = { ...trackedPosition };
681
- } else {
682
- const editEventOffset = getComponentOffsetDueToEvents({
683
- editEvents,
684
- schematic_component_id
685
- });
686
- current_position = {
687
- x: schematic_component.center.x + editEventOffset.x,
688
- y: schematic_component.center.y + editEventOffset.y
689
- };
690
- componentPositionsRef.current.set(schematic_component_id, {
691
- ...current_position
692
- });
469
+ // lib/hooks/useContextMenu.ts
470
+ import { useCallback as useCallback2, useEffect as useEffect4, useRef, useState as useState2 } from "react";
471
+ var LONG_PRESS_DURATION_MS = 600;
472
+ var MOVEMENT_THRESHOLD_PX = 10;
473
+ var useContextMenu = ({ containerRef }) => {
474
+ const [menuVisible, setMenuVisible] = useState2(false);
475
+ const [menuPos, setMenuPos] = useState2({ x: 0, y: 0 });
476
+ const menuRef = useRef(null);
477
+ const interactionOriginRef = useRef(null);
478
+ const longPressTimeoutRef = useRef(null);
479
+ const ignoreContextMenuUntilRef = useRef(0);
480
+ const clearLongPressTimeout = useCallback2(() => {
481
+ if (longPressTimeoutRef.current === null) return;
482
+ window.clearTimeout(longPressTimeoutRef.current);
483
+ longPressTimeoutRef.current = null;
484
+ }, []);
485
+ const handleContextMenu = useCallback2((event) => {
486
+ event.preventDefault();
487
+ if (Date.now() < ignoreContextMenuUntilRef.current) return;
488
+ const origin = interactionOriginRef.current;
489
+ if (!origin) return;
490
+ const movedTooFar = Math.abs(event.clientX - origin.x) > MOVEMENT_THRESHOLD_PX || Math.abs(event.clientY - origin.y) > MOVEMENT_THRESHOLD_PX;
491
+ interactionOriginRef.current = null;
492
+ if (movedTooFar) return;
493
+ setMenuPos({ x: event.clientX, y: event.clientY });
494
+ setMenuVisible(true);
495
+ }, []);
496
+ const handleTouchStart = useCallback2(
497
+ (event) => {
498
+ clearLongPressTimeout();
499
+ if (event.touches.length !== 1) {
500
+ interactionOriginRef.current = null;
501
+ return;
693
502
  }
694
- const newEditEvent = {
695
- edit_event_id: Math.random().toString(36).substr(2, 9),
696
- edit_event_type: "edit_schematic_component_location",
697
- schematic_component_id,
698
- original_center: current_position,
699
- new_center: { ...current_position },
700
- in_progress: true,
701
- created_at: Date.now(),
702
- _element: componentGroup
503
+ const touch = event.touches[0];
504
+ if (!touch) return;
505
+ interactionOriginRef.current = {
506
+ x: touch.clientX,
507
+ y: touch.clientY
703
508
  };
704
- activeEditEventRef.current = newEditEvent;
705
- setActiveEditEvent(newEditEvent);
706
- return true;
707
- },
708
- [cancelDrag, enabled, circuitJson, editEvents]
709
- );
710
- const handleMouseDown = useCallback(
711
- (e) => {
712
- startDrag(e.clientX, e.clientY, e.target);
713
- },
714
- [startDrag]
715
- );
716
- const handleTouchStart = useCallback(
717
- (e) => {
718
- if (e.touches.length !== 1) return;
719
- const touch = e.touches[0];
720
- if (startDrag(touch.clientX, touch.clientY, e.target)) {
721
- e.preventDefault();
722
- }
509
+ longPressTimeoutRef.current = window.setTimeout(() => {
510
+ const container = containerRef.current;
511
+ if (!container || !interactionOriginRef.current) return;
512
+ const rect = container.getBoundingClientRect();
513
+ setMenuPos({
514
+ x: rect.left + rect.width / 2,
515
+ y: rect.top + rect.height / 2
516
+ });
517
+ setMenuVisible(true);
518
+ ignoreContextMenuUntilRef.current = Date.now() + 1e3;
519
+ interactionOriginRef.current = null;
520
+ }, LONG_PRESS_DURATION_MS);
723
521
  },
724
- [startDrag]
522
+ [clearLongPressTimeout, containerRef]
725
523
  );
726
- const updateDragPosition = useCallback(
727
- (clientX, clientY) => {
728
- if (!activeEditEventRef.current || !dragStartPosRef.current) return;
729
- const screenDelta = {
730
- x: clientX - dragStartPosRef.current.x,
731
- y: clientY - dragStartPosRef.current.y
732
- };
733
- const mmDelta = {
734
- x: screenDelta.x / realToScreenProjection.a,
735
- y: screenDelta.y / realToScreenProjection.d
736
- };
737
- let newCenter = {
738
- x: activeEditEventRef.current.original_center.x + mmDelta.x,
739
- y: activeEditEventRef.current.original_center.y + mmDelta.y
740
- };
741
- if (snapToGrid) {
742
- const snap = (v) => Math.round(v * 10) / 10;
743
- newCenter = { x: snap(newCenter.x), y: snap(newCenter.y) };
524
+ const handleTouchMove = useCallback2(
525
+ (event) => {
526
+ const origin = interactionOriginRef.current;
527
+ if (!origin || event.touches.length !== 1) return;
528
+ const touch = event.touches[0];
529
+ const movedTooFar = !touch || Math.abs(touch.clientX - origin.x) > MOVEMENT_THRESHOLD_PX || Math.abs(touch.clientY - origin.y) > MOVEMENT_THRESHOLD_PX;
530
+ if (movedTooFar) {
531
+ interactionOriginRef.current = null;
532
+ clearLongPressTimeout();
744
533
  }
745
- const newEditEvent = {
746
- ...activeEditEventRef.current,
747
- new_center: newCenter
748
- };
749
- activeEditEventRef.current = newEditEvent;
750
- setActiveEditEvent(newEditEvent);
751
- },
752
- [realToScreenProjection, snapToGrid]
753
- );
754
- const handleMouseMove = useCallback(
755
- (e) => updateDragPosition(e.clientX, e.clientY),
756
- [updateDragPosition]
757
- );
758
- const handleTouchMove = useCallback(
759
- (e) => {
760
- if (e.touches.length !== 1 || !activeEditEventRef.current) return;
761
- e.preventDefault();
762
- const touch = e.touches[0];
763
- updateDragPosition(touch.clientX, touch.clientY);
764
534
  },
765
- [updateDragPosition]
535
+ [clearLongPressTimeout]
766
536
  );
767
- const endDrag = useCallback(() => {
768
- if (!activeEditEventRef.current) return;
769
- const finalEvent = {
770
- ...activeEditEventRef.current,
771
- in_progress: false
772
- };
773
- componentPositionsRef.current.set(finalEvent.schematic_component_id, {
774
- ...finalEvent.new_center
775
- });
776
- debug2("endDrag calling onEditEvent with new edit event", {
777
- newEditEvent: finalEvent
778
- });
779
- if (onEditEvent) onEditEvent(finalEvent);
780
- activeEditEventRef.current = null;
781
- dragStartPosRef.current = null;
782
- setActiveEditEvent(null);
783
- }, [onEditEvent]);
784
- const handleMouseUp = useCallback(() => endDrag(), [endDrag]);
785
- const handleTouchEnd = useCallback(() => endDrag(), [endDrag]);
786
- useEffect6(() => {
787
- window.addEventListener("mousemove", handleMouseMove);
788
- window.addEventListener("mouseup", handleMouseUp);
789
- window.addEventListener("touchmove", handleTouchMove, { passive: false });
790
- window.addEventListener("touchend", handleTouchEnd);
537
+ const handleTouchEnd = useCallback2(() => {
538
+ clearLongPressTimeout();
539
+ interactionOriginRef.current = null;
540
+ }, [clearLongPressTimeout]);
541
+ const handleClickAway = useCallback2((event) => {
542
+ const target = event.target;
543
+ if (menuRef.current?.contains(target)) return;
544
+ const isInRadixPortal = target.closest?.(
545
+ "[data-radix-popper-content-wrapper], [data-radix-dropdown-menu-content]"
546
+ );
547
+ if (isInRadixPortal) return;
548
+ setMenuVisible(false);
549
+ }, []);
550
+ useEffect4(() => {
551
+ if (!menuVisible) return;
552
+ document.addEventListener("mousedown", handleClickAway);
553
+ document.addEventListener("touchstart", handleClickAway);
791
554
  return () => {
792
- window.removeEventListener("mousemove", handleMouseMove);
793
- window.removeEventListener("mouseup", handleMouseUp);
794
- window.removeEventListener("touchmove", handleTouchMove);
795
- window.removeEventListener("touchend", handleTouchEnd);
555
+ document.removeEventListener("mousedown", handleClickAway);
556
+ document.removeEventListener("touchstart", handleClickAway);
796
557
  };
797
- }, [handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);
558
+ }, [handleClickAway, menuVisible]);
559
+ useEffect4(() => clearLongPressTimeout, [clearLongPressTimeout]);
798
560
  return {
799
- handleMouseDown,
800
- handleTouchStart,
801
- isDragging: !!activeEditEventRef.current,
802
- activeEditEvent
803
- };
804
- };
805
-
806
- // lib/utils/z-index-map.ts
807
- var zIndexMap = {
808
- schematicEditIcon: 50,
809
- schematicGridIcon: 49,
810
- viewMenuIcon: 48,
811
- viewMenu: 55,
812
- viewMenuBackdrop: 54,
813
- clickToInteractOverlay: 100,
814
- schematicComponentHoverOutline: 47,
815
- schematicPortHoverOutline: 48
816
- };
817
-
818
- // lib/components/EditIcon.tsx
819
- import { jsx, jsxs } from "react/jsx-runtime";
820
- var EditIcon = ({
821
- onClick,
822
- active
823
- }) => {
824
- const handleInteraction = (e) => {
825
- e.preventDefault();
826
- onClick();
827
- };
828
- return /* @__PURE__ */ jsx(
829
- "div",
830
- {
831
- onClick: handleInteraction,
832
- onTouchEnd: handleInteraction,
833
- title: active ? "Disable edit mode" : "Enable edit mode",
834
- style: {
835
- position: "absolute",
836
- top: "16px",
837
- right: "64px",
838
- backgroundColor: active ? "#4CAF50" : "#fff",
839
- color: active ? "#fff" : "#000",
840
- padding: "8px",
841
- borderRadius: "4px",
842
- cursor: "pointer",
843
- boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
844
- display: "flex",
845
- alignItems: "center",
846
- gap: "4px",
847
- zIndex: zIndexMap.schematicEditIcon
561
+ menuVisible,
562
+ menuPos,
563
+ menuRef,
564
+ setMenuVisible,
565
+ contextMenuEventHandlers: {
566
+ onMouseDown: (event) => {
567
+ interactionOriginRef.current = event.button === 2 || event.button === 0 && event.ctrlKey ? { x: event.clientX, y: event.clientY } : null;
848
568
  },
849
- children: /* @__PURE__ */ jsxs(
850
- "svg",
851
- {
852
- width: "16",
853
- height: "16",
854
- viewBox: "0 0 24 24",
855
- fill: "none",
856
- stroke: "currentColor",
857
- strokeWidth: "2",
858
- children: [
859
- /* @__PURE__ */ jsx("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
860
- /* @__PURE__ */ jsx("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
861
- ]
862
- }
863
- )
569
+ onContextMenu: handleContextMenu,
570
+ onTouchStart: handleTouchStart,
571
+ onTouchMove: handleTouchMove,
572
+ onTouchEnd: handleTouchEnd,
573
+ onTouchCancel: handleTouchEnd
864
574
  }
865
- );
866
- };
867
-
868
- // lib/components/GridIcon.tsx
869
- import { jsx as jsx2 } from "react/jsx-runtime";
870
- var GridIcon = ({
871
- onClick,
872
- active
873
- }) => {
874
- const handleInteraction = (e) => {
875
- e.preventDefault();
876
- onClick();
877
575
  };
878
- return /* @__PURE__ */ jsx2(
879
- "div",
880
- {
881
- onClick: handleInteraction,
882
- onTouchEnd: handleInteraction,
883
- title: active ? "Hide grid" : "Show grid",
884
- style: {
885
- position: "absolute",
886
- top: "56px",
887
- right: "64px",
888
- backgroundColor: active ? "#4CAF50" : "#fff",
889
- color: active ? "#fff" : "#000",
890
- padding: "8px",
891
- borderRadius: "4px",
892
- cursor: "pointer",
893
- boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
894
- display: "flex",
895
- alignItems: "center",
896
- gap: "4px",
897
- zIndex: zIndexMap.schematicGridIcon
898
- },
899
- children: /* @__PURE__ */ jsx2(
900
- "svg",
901
- {
902
- width: "16",
903
- height: "16",
904
- viewBox: "0 0 24 24",
905
- fill: "none",
906
- stroke: "currentColor",
907
- strokeWidth: "2",
908
- children: /* @__PURE__ */ jsx2("path", { d: "M3 3h7v7H3zM14 3h7v7h-7zM3 14h7v7H3zM14 14h7v7h-7z" })
909
- }
910
- )
911
- }
912
- );
913
- };
914
-
915
- // lib/components/ViewMenu.tsx
916
- import { useMemo } from "react";
917
- import { su as su6 } from "@tscircuit/soup-util";
918
- import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
919
-
920
- // package.json
921
- var package_default = {
922
- name: "@tscircuit/schematic-viewer",
923
- version: "2.0.76",
924
- main: "dist/index.js",
925
- type: "module",
926
- scripts: {
927
- start: "cosmos",
928
- build: "tsup-node ./lib/index.ts --dts --format esm --sourcemap",
929
- "build:site": "cosmos-export",
930
- "vercel-build": "bun run build:site",
931
- format: "biome format --write .",
932
- "format:check": "biome format ."
933
- },
934
- files: [
935
- "dist"
936
- ],
937
- devDependencies: {
938
- "@biomejs/biome": "^1.9.4",
939
- "@types/bun": "latest",
940
- "@types/debug": "^4.1.12",
941
- "@types/react": "^19.0.1",
942
- "@types/react-dom": "^19.0.2",
943
- "@vitejs/plugin-react": "^4.3.4",
944
- react: "^19.1.0",
945
- "react-cosmos": "^6.2.1",
946
- "react-cosmos-plugin-vite": "^6.2.0",
947
- "react-dom": "^19.1.0",
948
- "react-reconciler": "^0.31.0",
949
- semver: "^7.7.2",
950
- tscircuit: "^0.0.2112",
951
- tsup: "^8.3.5",
952
- vite: "^6.0.3"
953
- },
954
- peerDependencies: {
955
- typescript: "^5.0.0",
956
- tscircuit: "*"
957
- },
958
- dependencies: {
959
- "@radix-ui/react-dropdown-menu": "^2.1.16",
960
- "circuit-json": "^0.0.465",
961
- "circuit-to-svg": "^0.0.393",
962
- debug: "^4.4.0",
963
- "performance-now": "^2.1.0",
964
- "use-mouse-matrix-transform": "^1.2.2"
965
- }
966
- };
967
-
968
- // lib/components/ViewMenuIcon.tsx
969
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
970
- var ViewMenuIcon = ({
971
- active = false,
972
- ...props
973
- }) => {
974
- return /* @__PURE__ */ jsx3(
975
- "button",
976
- {
977
- type: "button",
978
- title: active ? "Hide view menu" : "Show view menu",
979
- ...props,
980
- style: {
981
- position: "absolute",
982
- top: "16px",
983
- right: "16px",
984
- backgroundColor: active ? "#4CAF50" : "#fff",
985
- color: active ? "#fff" : "#000",
986
- padding: "8px",
987
- border: "none",
988
- borderRadius: "4px",
989
- cursor: "pointer",
990
- outline: "none",
991
- boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
992
- display: "flex",
993
- alignItems: "center",
994
- gap: "4px",
995
- zIndex: zIndexMap.viewMenuIcon
996
- },
997
- children: /* @__PURE__ */ jsxs2(
998
- "svg",
999
- {
1000
- width: "16",
1001
- height: "16",
1002
- viewBox: "0 0 24 24",
1003
- fill: "none",
1004
- stroke: "currentColor",
1005
- strokeWidth: "2",
1006
- children: [
1007
- /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "1" }),
1008
- /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "5", r: "1" }),
1009
- /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "19", r: "1" })
1010
- ]
1011
- }
1012
- )
1013
- }
1014
- );
1015
- };
1016
-
1017
- // lib/components/ViewMenu.tsx
1018
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1019
- var FONT_FAMILY = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
1020
- var contentStyles = {
1021
- backgroundColor: "#ffffff",
1022
- color: "#111111",
1023
- borderRadius: 8,
1024
- boxShadow: "0 6px 24px rgba(0,0,0,0.12), 0 1px 3px rgba(0,0,0,0.08)",
1025
- border: "1px solid #e5e7eb",
1026
- padding: 4,
1027
- minWidth: 224,
1028
- fontSize: 13,
1029
- fontFamily: FONT_FAMILY,
1030
- outline: "none",
1031
- zIndex: zIndexMap.viewMenu
1032
- };
1033
- var itemStyles = {
1034
- display: "flex",
1035
- alignItems: "center",
1036
- gap: 8,
1037
- padding: "7px 10px 7px 8px",
1038
- borderRadius: 6,
1039
- cursor: "pointer",
1040
- outline: "none",
1041
- userSelect: "none",
1042
- color: "#111111",
1043
- fontSize: 13,
1044
- fontFamily: FONT_FAMILY
1045
- };
1046
- var iconSlotStyles = {
1047
- width: 16,
1048
- height: 16,
1049
- flexShrink: 0,
1050
- display: "inline-flex",
1051
- alignItems: "center",
1052
- justifyContent: "center",
1053
- color: "#111111"
1054
- };
1055
- var separatorStyles = {
1056
- height: 1,
1057
- backgroundColor: "#ececec",
1058
- margin: "4px 0"
1059
- };
1060
- var HIGHLIGHT_CSS = `
1061
- .sv-vm-item[data-highlighted]:not([data-disabled]),
1062
- .sv-vm-item:hover:not([data-disabled]) { background-color: #f1f3f5; }
1063
- .sv-vm-item[data-disabled] { opacity: 0.45; cursor: not-allowed; }
1064
- `;
1065
- var CheckIcon = () => /* @__PURE__ */ jsx4(
1066
- "svg",
1067
- {
1068
- width: "14",
1069
- height: "14",
1070
- viewBox: "0 0 24 24",
1071
- fill: "none",
1072
- stroke: "currentColor",
1073
- strokeWidth: "2.5",
1074
- strokeLinecap: "round",
1075
- strokeLinejoin: "round",
1076
- "aria-hidden": "true",
1077
- children: /* @__PURE__ */ jsx4("path", { d: "M20 6 9 17l-5-5" })
1078
- }
1079
- );
1080
- var ViewMenu = ({
1081
- circuitJson,
1082
- circuitJsonKey,
1083
- open,
1084
- onOpenChange,
1085
- showGroups,
1086
- onToggleGroups,
1087
- showGrid,
1088
- onToggleGrid
1089
- }) => {
1090
- const hasGroups = useMemo(() => {
1091
- if (!circuitJson || circuitJson.length === 0) return false;
1092
- try {
1093
- const sourceGroups = su6(circuitJson).source_group?.list() || [];
1094
- if (sourceGroups.length > 0) return true;
1095
- const schematicComponents = su6(circuitJson).schematic_component?.list() || [];
1096
- if (schematicComponents.length > 1) {
1097
- const componentTypes = /* @__PURE__ */ new Set();
1098
- for (const comp of schematicComponents) {
1099
- const sourceComp = su6(circuitJson).source_component.get(
1100
- comp.source_component_id
1101
- );
1102
- if (sourceComp?.ftype) {
1103
- componentTypes.add(sourceComp.ftype);
1104
- }
1105
- }
1106
- return componentTypes.size > 1;
1107
- }
1108
- return false;
1109
- } catch (error) {
1110
- console.error("Error checking for groups:", error);
1111
- return false;
1112
- }
1113
- }, [circuitJsonKey]);
1114
- return /* @__PURE__ */ jsxs3(DropdownMenu.Root, { open, onOpenChange, modal: false, children: [
1115
- /* @__PURE__ */ jsx4(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx4(ViewMenuIcon, { active: open }) }),
1116
- /* @__PURE__ */ jsx4(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs3(
1117
- DropdownMenu.Content,
1118
- {
1119
- style: contentStyles,
1120
- side: "bottom",
1121
- align: "end",
1122
- sideOffset: 8,
1123
- collisionPadding: 10,
1124
- children: [
1125
- /* @__PURE__ */ jsx4("style", { children: HIGHLIGHT_CSS }),
1126
- /* @__PURE__ */ jsxs3(
1127
- DropdownMenu.Item,
1128
- {
1129
- className: "sv-vm-item",
1130
- style: itemStyles,
1131
- disabled: !hasGroups,
1132
- title: hasGroups ? void 0 : "No groups found in this schematic",
1133
- onSelect: (e) => e.preventDefault(),
1134
- onPointerUp: () => {
1135
- if (hasGroups) onToggleGroups(!showGroups);
1136
- },
1137
- children: [
1138
- /* @__PURE__ */ jsx4("span", { style: iconSlotStyles, children: showGroups && /* @__PURE__ */ jsx4(CheckIcon, {}) }),
1139
- /* @__PURE__ */ jsx4("span", { children: "View Schematic Groups" })
1140
- ]
1141
- }
1142
- ),
1143
- /* @__PURE__ */ jsxs3(
1144
- DropdownMenu.Item,
1145
- {
1146
- className: "sv-vm-item",
1147
- style: itemStyles,
1148
- onSelect: (e) => e.preventDefault(),
1149
- onPointerUp: () => onToggleGrid(!showGrid),
1150
- children: [
1151
- /* @__PURE__ */ jsx4("span", { style: iconSlotStyles, children: showGrid && /* @__PURE__ */ jsx4(CheckIcon, {}) }),
1152
- /* @__PURE__ */ jsx4("span", { children: "Show Grid" })
1153
- ]
1154
- }
1155
- ),
1156
- /* @__PURE__ */ jsx4(DropdownMenu.Separator, { style: separatorStyles }),
1157
- /* @__PURE__ */ jsxs3(
1158
- "div",
1159
- {
1160
- style: {
1161
- padding: "4px 8px",
1162
- fontSize: 12,
1163
- color: "#9ca3af",
1164
- textAlign: "center",
1165
- fontFamily: FONT_FAMILY
1166
- },
1167
- children: [
1168
- "v",
1169
- String(package_default?.version)
1170
- ]
1171
- }
1172
- )
1173
- ]
1174
- }
1175
- ) })
1176
- ] });
1177
- };
1178
-
1179
- // lib/hooks/useLocalStorage.ts
1180
- import { useCallback as useCallback2 } from "react";
1181
- var STORAGE_KEYS = {
1182
- IS_SHOWING_SCHEMATIC_GROUPS: "schematic_viewer_show_groups",
1183
- SELECTED_SCHEMATIC_SHEET: "schematic_viewer_selected_sheet"
1184
- };
1185
- var getStoredBoolean = (key, defaultValue) => {
1186
- if (typeof window === "undefined") return defaultValue;
1187
- try {
1188
- const stored = localStorage.getItem(key);
1189
- return stored !== null ? JSON.parse(stored) : defaultValue;
1190
- } catch {
1191
- return defaultValue;
1192
- }
1193
- };
1194
- var setStoredBoolean = (key, value) => {
1195
- if (typeof window === "undefined") return;
1196
- try {
1197
- localStorage.setItem(key, JSON.stringify(value));
1198
- } catch {
1199
- }
1200
- };
1201
- var getStoredString = (key) => {
1202
- if (typeof window === "undefined") return null;
1203
- try {
1204
- return localStorage.getItem(key);
1205
- } catch {
1206
- return null;
1207
- }
1208
576
  };
1209
- var setStoredString = (key, value) => {
1210
- if (typeof window === "undefined") return;
1211
- try {
1212
- localStorage.setItem(key, value);
1213
- } catch {
1214
- }
577
+
578
+ // lib/utils/z-index-map.ts
579
+ var zIndexMap = {
580
+ contextMenu: 110,
581
+ viewMenu: 55,
582
+ viewMenuIcon: 48,
583
+ clickToInteractOverlay: 100,
584
+ schematicComponentHoverOutline: 47,
585
+ schematicPortHoverOutline: 48
1215
586
  };
1216
587
 
1217
588
  // lib/components/MouseTracker.tsx
@@ -1219,11 +590,11 @@ import {
1219
590
  createContext,
1220
591
  useCallback as useCallback3,
1221
592
  useContext,
1222
- useEffect as useEffect7,
1223
- useMemo as useMemo2,
1224
- useRef as useRef4
593
+ useEffect as useEffect5,
594
+ useMemo,
595
+ useRef as useRef2
1225
596
  } from "react";
1226
- import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
597
+ import { Fragment, jsx } from "react/jsx-runtime";
1227
598
  var MouseTrackerContext = createContext(null);
1228
599
  var DRAG_THRESHOLD_PX = 5;
1229
600
  var boundsAreEqual = (a, b) => {
@@ -1234,12 +605,12 @@ var boundsAreEqual = (a, b) => {
1234
605
  var MouseTracker = ({ children }) => {
1235
606
  const existingContext = useContext(MouseTrackerContext);
1236
607
  if (existingContext) {
1237
- return /* @__PURE__ */ jsx5(Fragment, { children });
608
+ return /* @__PURE__ */ jsx(Fragment, { children });
1238
609
  }
1239
- return /* @__PURE__ */ jsx5(MouseTrackerProvider, { children });
610
+ return /* @__PURE__ */ jsx(MouseTrackerProvider, { children });
1240
611
  };
1241
612
  var MouseTrackerProvider = ({ children }) => {
1242
- const storeRef = useRef4({
613
+ const storeRef = useRef2({
1243
614
  pointer: null,
1244
615
  boundingBoxes: /* @__PURE__ */ new Map(),
1245
616
  hoveringIds: /* @__PURE__ */ new Set(),
@@ -1306,7 +677,7 @@ var MouseTrackerProvider = ({ children }) => {
1306
677
  const isHovering = useCallback3((id) => {
1307
678
  return storeRef.current.hoveringIds.has(id);
1308
679
  }, []);
1309
- useEffect7(() => {
680
+ useEffect5(() => {
1310
681
  const handlePointerPosition = (event) => {
1311
682
  const { clientX, clientY } = event;
1312
683
  const pointer = storeRef.current.pointer;
@@ -1374,7 +745,7 @@ var MouseTrackerProvider = ({ children }) => {
1374
745
  window.removeEventListener("click", handleClick);
1375
746
  };
1376
747
  }, [updateHovering]);
1377
- const value = useMemo2(
748
+ const value = useMemo(
1378
749
  () => ({
1379
750
  registerBoundingBox,
1380
751
  updateBoundingBox,
@@ -1390,19 +761,19 @@ var MouseTrackerProvider = ({ children }) => {
1390
761
  isHovering
1391
762
  ]
1392
763
  );
1393
- return /* @__PURE__ */ jsx5(MouseTrackerContext.Provider, { value, children });
764
+ return /* @__PURE__ */ jsx(MouseTrackerContext.Provider, { value, children });
1394
765
  };
1395
766
 
1396
767
  // lib/components/SchematicComponentMouseTarget.tsx
1397
- import { useCallback as useCallback4, useEffect as useEffect9, useRef as useRef6, useState as useState3 } from "react";
768
+ import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef4, useState as useState3 } from "react";
1398
769
 
1399
770
  // lib/hooks/useMouseEventsOverBoundingBox.ts
1400
771
  import {
1401
772
  useContext as useContext2,
1402
- useEffect as useEffect8,
773
+ useEffect as useEffect6,
1403
774
  useId,
1404
- useMemo as useMemo3,
1405
- useRef as useRef5,
775
+ useMemo as useMemo2,
776
+ useRef as useRef3,
1406
777
  useSyncExternalStore
1407
778
  } from "react";
1408
779
  var useMouseEventsOverBoundingBox = (options) => {
@@ -1413,15 +784,15 @@ var useMouseEventsOverBoundingBox = (options) => {
1413
784
  );
1414
785
  }
1415
786
  const id = useId();
1416
- const latestOptionsRef = useRef5(options);
787
+ const latestOptionsRef = useRef3(options);
1417
788
  latestOptionsRef.current = options;
1418
- const handleClick = useMemo3(
789
+ const handleClick = useMemo2(
1419
790
  () => (event) => {
1420
791
  latestOptionsRef.current.onClick?.(event);
1421
792
  },
1422
793
  []
1423
794
  );
1424
- useEffect8(() => {
795
+ useEffect6(() => {
1425
796
  context.registerBoundingBox(id, {
1426
797
  bounds: latestOptionsRef.current.bounds,
1427
798
  onClick: latestOptionsRef.current.onClick ? handleClick : void 0
@@ -1430,7 +801,7 @@ var useMouseEventsOverBoundingBox = (options) => {
1430
801
  context.unregisterBoundingBox(id);
1431
802
  };
1432
803
  }, [context, handleClick, id]);
1433
- useEffect8(() => {
804
+ useEffect6(() => {
1434
805
  context.updateBoundingBox(id, {
1435
806
  bounds: latestOptionsRef.current.bounds,
1436
807
  onClick: latestOptionsRef.current.onClick ? handleClick : void 0
@@ -1454,7 +825,7 @@ var useMouseEventsOverBoundingBox = (options) => {
1454
825
  };
1455
826
 
1456
827
  // lib/components/SchematicComponentMouseTarget.tsx
1457
- import { jsx as jsx6 } from "react/jsx-runtime";
828
+ import { jsx as jsx2 } from "react/jsx-runtime";
1458
829
  var areMeasurementsEqual = (a, b) => {
1459
830
  if (!a && !b) return true;
1460
831
  if (!a || !b) return false;
@@ -1470,7 +841,7 @@ var SchematicComponentMouseTarget = ({
1470
841
  circuitJsonKey
1471
842
  }) => {
1472
843
  const [measurement, setMeasurement] = useState3(null);
1473
- const frameRef = useRef6(null);
844
+ const frameRef = useRef4(null);
1474
845
  const measure = useCallback4(() => {
1475
846
  frameRef.current = null;
1476
847
  const svgDiv = svgDivRef.current;
@@ -1510,10 +881,10 @@ var SchematicComponentMouseTarget = ({
1510
881
  if (frameRef.current !== null) return;
1511
882
  frameRef.current = window.requestAnimationFrame(measure);
1512
883
  }, [measure]);
1513
- useEffect9(() => {
884
+ useEffect7(() => {
1514
885
  scheduleMeasure();
1515
886
  }, [scheduleMeasure, circuitJsonKey]);
1516
- useEffect9(() => {
887
+ useEffect7(() => {
1517
888
  scheduleMeasure();
1518
889
  const svgDiv = svgDivRef.current;
1519
890
  const container = containerRef.current;
@@ -1558,7 +929,7 @@ var SchematicComponentMouseTarget = ({
1558
929
  bounds,
1559
930
  onClick: onComponentClick ? handleClick : void 0
1560
931
  });
1561
- useEffect9(() => {
932
+ useEffect7(() => {
1562
933
  if (onHoverChange) {
1563
934
  onHoverChange(componentId, hovering);
1564
935
  }
@@ -1567,7 +938,7 @@ var SchematicComponentMouseTarget = ({
1567
938
  return null;
1568
939
  }
1569
940
  const rect = measurement.rect;
1570
- return /* @__PURE__ */ jsx6(
941
+ return /* @__PURE__ */ jsx2(
1571
942
  "div",
1572
943
  {
1573
944
  style: {
@@ -1585,8 +956,8 @@ var SchematicComponentMouseTarget = ({
1585
956
  };
1586
957
 
1587
958
  // lib/components/SchematicPortMouseTarget.tsx
1588
- import { useCallback as useCallback5, useEffect as useEffect10, useRef as useRef7, useState as useState4 } from "react";
1589
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
959
+ import { useCallback as useCallback5, useEffect as useEffect8, useRef as useRef5, useState as useState4 } from "react";
960
+ import { Fragment as Fragment2, jsx as jsx3, jsxs } from "react/jsx-runtime";
1590
961
  var areMeasurementsEqual2 = (a, b) => {
1591
962
  if (!a && !b) return true;
1592
963
  if (!a || !b) return false;
@@ -1603,7 +974,7 @@ var SchematicPortMouseTarget = ({
1603
974
  circuitJsonKey
1604
975
  }) => {
1605
976
  const [measurement, setMeasurement] = useState4(null);
1606
- const frameRef = useRef7(null);
977
+ const frameRef = useRef5(null);
1607
978
  const measure = useCallback5(() => {
1608
979
  frameRef.current = null;
1609
980
  const svgDiv = svgDivRef.current;
@@ -1644,10 +1015,10 @@ var SchematicPortMouseTarget = ({
1644
1015
  if (frameRef.current !== null) return;
1645
1016
  frameRef.current = window.requestAnimationFrame(measure);
1646
1017
  }, [measure]);
1647
- useEffect10(() => {
1018
+ useEffect8(() => {
1648
1019
  scheduleMeasure();
1649
1020
  }, [scheduleMeasure, circuitJsonKey]);
1650
- useEffect10(() => {
1021
+ useEffect8(() => {
1651
1022
  scheduleMeasure();
1652
1023
  const svgDiv = svgDivRef.current;
1653
1024
  const container = containerRef.current;
@@ -1692,7 +1063,7 @@ var SchematicPortMouseTarget = ({
1692
1063
  bounds,
1693
1064
  onClick: onPortClick ? handleClick : void 0
1694
1065
  });
1695
- useEffect10(() => {
1066
+ useEffect8(() => {
1696
1067
  if (onHoverChange) {
1697
1068
  onHoverChange(portId, hovering);
1698
1069
  }
@@ -1701,8 +1072,8 @@ var SchematicPortMouseTarget = ({
1701
1072
  return null;
1702
1073
  }
1703
1074
  const rect = measurement.rect;
1704
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1705
- /* @__PURE__ */ jsx7(
1075
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
1076
+ /* @__PURE__ */ jsx3(
1706
1077
  "div",
1707
1078
  {
1708
1079
  style: {
@@ -1720,7 +1091,7 @@ var SchematicPortMouseTarget = ({
1720
1091
  }
1721
1092
  }
1722
1093
  ),
1723
- hovering && portLabel && /* @__PURE__ */ jsx7(
1094
+ hovering && portLabel && /* @__PURE__ */ jsx3(
1724
1095
  "div",
1725
1096
  {
1726
1097
  style: {
@@ -1746,10 +1117,10 @@ var SchematicPortMouseTarget = ({
1746
1117
 
1747
1118
  // lib/components/SchematicSheetSelector.tsx
1748
1119
  import { useState as useState5 } from "react";
1749
- import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
1750
- import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1751
- var FONT_FAMILY2 = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
1752
- var contentStyles2 = {
1120
+ import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
1121
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1122
+ var FONT_FAMILY = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
1123
+ var contentStyles = {
1753
1124
  backgroundColor: "#ffffff",
1754
1125
  color: "#111111",
1755
1126
  borderRadius: 8,
@@ -1761,11 +1132,11 @@ var contentStyles2 = {
1761
1132
  maxHeight: 320,
1762
1133
  overflowY: "auto",
1763
1134
  fontSize: 13,
1764
- fontFamily: FONT_FAMILY2,
1135
+ fontFamily: FONT_FAMILY,
1765
1136
  outline: "none",
1766
1137
  zIndex: zIndexMap.viewMenu
1767
1138
  };
1768
- var itemStyles2 = {
1139
+ var itemStyles = {
1769
1140
  display: "flex",
1770
1141
  alignItems: "center",
1771
1142
  gap: 8,
@@ -1776,9 +1147,9 @@ var itemStyles2 = {
1776
1147
  userSelect: "none",
1777
1148
  color: "#111111",
1778
1149
  fontSize: 13,
1779
- fontFamily: FONT_FAMILY2
1150
+ fontFamily: FONT_FAMILY
1780
1151
  };
1781
- var iconSlotStyles2 = {
1152
+ var iconSlotStyles = {
1782
1153
  width: 16,
1783
1154
  height: 16,
1784
1155
  flexShrink: 0,
@@ -1797,7 +1168,7 @@ var MENU_CSS = `
1797
1168
  .sv-sheet-chevron { transition: transform 0.2s ease; }
1798
1169
  [data-state="open"] > .sv-sheet-chevron { transform: rotate(180deg); }
1799
1170
  `;
1800
- var CheckIcon2 = () => /* @__PURE__ */ jsx8(
1171
+ var CheckIcon = () => /* @__PURE__ */ jsx4(
1801
1172
  "svg",
1802
1173
  {
1803
1174
  width: "14",
@@ -1809,10 +1180,10 @@ var CheckIcon2 = () => /* @__PURE__ */ jsx8(
1809
1180
  strokeLinecap: "round",
1810
1181
  strokeLinejoin: "round",
1811
1182
  "aria-hidden": "true",
1812
- children: /* @__PURE__ */ jsx8("path", { d: "M20 6 9 17l-5-5" })
1183
+ children: /* @__PURE__ */ jsx4("path", { d: "M20 6 9 17l-5-5" })
1813
1184
  }
1814
1185
  );
1815
- var ChevronDownIcon = ({ className }) => /* @__PURE__ */ jsx8(
1186
+ var ChevronDownIcon = ({ className }) => /* @__PURE__ */ jsx4(
1816
1187
  "svg",
1817
1188
  {
1818
1189
  className,
@@ -1826,7 +1197,7 @@ var ChevronDownIcon = ({ className }) => /* @__PURE__ */ jsx8(
1826
1197
  strokeLinejoin: "round",
1827
1198
  style: { opacity: 0.6, flexShrink: 0 },
1828
1199
  "aria-hidden": "true",
1829
- children: /* @__PURE__ */ jsx8("path", { d: "m6 9 6 6 6-6" })
1200
+ children: /* @__PURE__ */ jsx4("path", { d: "m6 9 6 6 6-6" })
1830
1201
  }
1831
1202
  );
1832
1203
  var SchematicSheetSelector = ({
@@ -1840,10 +1211,10 @@ var SchematicSheetSelector = ({
1840
1211
  (s) => s.schematic_sheet_id === selectedSheetId
1841
1212
  );
1842
1213
  const selectedLabel = selectedSheet?.name ?? "Select sheet";
1843
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
1844
- /* @__PURE__ */ jsx8("style", { children: MENU_CSS }),
1845
- /* @__PURE__ */ jsxs5(DropdownMenu2.Root, { open, onOpenChange: setOpen, modal: false, children: [
1846
- /* @__PURE__ */ jsx8(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs5(
1214
+ return /* @__PURE__ */ jsxs2(Fragment3, { children: [
1215
+ /* @__PURE__ */ jsx4("style", { children: MENU_CSS }),
1216
+ /* @__PURE__ */ jsxs2(DropdownMenu.Root, { open, onOpenChange: setOpen, modal: false, children: [
1217
+ /* @__PURE__ */ jsx4(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
1847
1218
  "button",
1848
1219
  {
1849
1220
  type: "button",
@@ -1866,31 +1237,31 @@ var SchematicSheetSelector = ({
1866
1237
  cursor: "pointer",
1867
1238
  boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
1868
1239
  fontSize: "13px",
1869
- fontFamily: FONT_FAMILY2,
1240
+ fontFamily: FONT_FAMILY,
1870
1241
  zIndex: zIndexMap.viewMenuIcon
1871
1242
  },
1872
1243
  children: [
1873
- /* @__PURE__ */ jsx8("span", { style: { color: "#888888", flexShrink: 0 }, children: "Sheet:" }),
1874
- /* @__PURE__ */ jsx8("span", { style: { ...ellipsisStyles, minWidth: 0 }, children: selectedLabel }),
1875
- /* @__PURE__ */ jsx8(ChevronDownIcon, { className: "sv-sheet-chevron" })
1244
+ /* @__PURE__ */ jsx4("span", { style: { color: "#888888", flexShrink: 0 }, children: "Sheet:" }),
1245
+ /* @__PURE__ */ jsx4("span", { style: { ...ellipsisStyles, minWidth: 0 }, children: selectedLabel }),
1246
+ /* @__PURE__ */ jsx4(ChevronDownIcon, { className: "sv-sheet-chevron" })
1876
1247
  ]
1877
1248
  }
1878
1249
  ) }),
1879
- /* @__PURE__ */ jsx8(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsx8(
1880
- DropdownMenu2.Content,
1250
+ /* @__PURE__ */ jsx4(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx4(
1251
+ DropdownMenu.Content,
1881
1252
  {
1882
- style: contentStyles2,
1253
+ style: contentStyles,
1883
1254
  side: "bottom",
1884
1255
  align: "start",
1885
1256
  sideOffset: 8,
1886
1257
  collisionPadding: 10,
1887
1258
  children: sheets.map((sheet) => {
1888
1259
  const selected = sheet.schematic_sheet_id === selectedSheetId;
1889
- return /* @__PURE__ */ jsxs5(
1890
- DropdownMenu2.Item,
1260
+ return /* @__PURE__ */ jsxs2(
1261
+ DropdownMenu.Item,
1891
1262
  {
1892
1263
  className: "sv-sheet-item",
1893
- style: itemStyles2,
1264
+ style: itemStyles,
1894
1265
  title: sheet.name,
1895
1266
  onSelect: (e) => e.preventDefault(),
1896
1267
  onPointerUp: () => {
@@ -1898,8 +1269,8 @@ var SchematicSheetSelector = ({
1898
1269
  setOpen(false);
1899
1270
  },
1900
1271
  children: [
1901
- /* @__PURE__ */ jsx8("span", { style: iconSlotStyles2, children: selected && /* @__PURE__ */ jsx8(CheckIcon2, {}) }),
1902
- /* @__PURE__ */ jsx8("span", { style: { ...ellipsisStyles, minWidth: 0 }, children: sheet.name })
1272
+ /* @__PURE__ */ jsx4("span", { style: iconSlotStyles, children: selected && /* @__PURE__ */ jsx4(CheckIcon, {}) }),
1273
+ /* @__PURE__ */ jsx4("span", { style: { ...ellipsisStyles, minWidth: 0 }, children: sheet.name })
1903
1274
  ]
1904
1275
  },
1905
1276
  sheet.schematic_sheet_id
@@ -1911,29 +1282,292 @@ var SchematicSheetSelector = ({
1911
1282
  ] });
1912
1283
  };
1913
1284
 
1285
+ // lib/components/ViewMenu.tsx
1286
+ import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
1287
+ import { su as su3 } from "@tscircuit/soup-util";
1288
+ import { useMemo as useMemo3 } from "react";
1289
+
1290
+ // package.json
1291
+ var package_default = {
1292
+ name: "@tscircuit/schematic-viewer",
1293
+ version: "2.0.78",
1294
+ main: "dist/index.js",
1295
+ type: "module",
1296
+ scripts: {
1297
+ start: "cosmos",
1298
+ build: "tsup-node ./lib/index.ts --dts --format esm --sourcemap",
1299
+ "build:site": "cosmos-export",
1300
+ "vercel-build": "bun run build:site",
1301
+ format: "biome format --write .",
1302
+ "format:check": "biome format ."
1303
+ },
1304
+ files: [
1305
+ "dist"
1306
+ ],
1307
+ devDependencies: {
1308
+ "@biomejs/biome": "^1.9.4",
1309
+ "@types/bun": "latest",
1310
+ "@types/debug": "^4.1.12",
1311
+ "@types/jsdom": "^21.1.7",
1312
+ "@types/react": "^19.0.1",
1313
+ "@types/react-dom": "^19.0.2",
1314
+ "@vitejs/plugin-react": "^4.3.4",
1315
+ jsdom: "^26.0.0",
1316
+ react: "^19.1.0",
1317
+ "react-cosmos": "^6.2.1",
1318
+ "react-cosmos-plugin-vite": "^6.2.0",
1319
+ "react-dom": "^19.1.0",
1320
+ "react-reconciler": "^0.31.0",
1321
+ semver: "^7.7.2",
1322
+ tscircuit: "^0.0.2112",
1323
+ tsup: "^8.3.5",
1324
+ vite: "^6.0.3"
1325
+ },
1326
+ peerDependencies: {
1327
+ typescript: "^5.0.0",
1328
+ tscircuit: "*"
1329
+ },
1330
+ dependencies: {
1331
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
1332
+ "circuit-json": "^0.0.465",
1333
+ "circuit-to-svg": "^0.0.393",
1334
+ debug: "^4.4.0",
1335
+ "performance-now": "^2.1.0",
1336
+ "use-mouse-matrix-transform": "^1.2.2"
1337
+ }
1338
+ };
1339
+
1340
+ // lib/components/ViewMenu.tsx
1341
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1342
+ var FONT_FAMILY2 = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
1343
+ var contentStyles2 = {
1344
+ backgroundColor: "#262626",
1345
+ color: "#fafafa",
1346
+ borderRadius: 6,
1347
+ boxShadow: "0px 12px 48px -12px rgba(0, 0, 0, 0.5), 0px 8px 24px -8px rgba(0, 0, 0, 0.3)",
1348
+ border: "1px solid #333333",
1349
+ padding: 4,
1350
+ minWidth: 208,
1351
+ fontSize: 14,
1352
+ fontWeight: 400,
1353
+ fontFamily: FONT_FAMILY2,
1354
+ outline: "none",
1355
+ zIndex: zIndexMap.contextMenu
1356
+ };
1357
+ var itemStyles2 = {
1358
+ display: "flex",
1359
+ alignItems: "center",
1360
+ gap: 8,
1361
+ padding: "6px 8px",
1362
+ borderRadius: 6,
1363
+ cursor: "default",
1364
+ outline: "none",
1365
+ userSelect: "none",
1366
+ color: "#fafafa",
1367
+ fontSize: 14,
1368
+ fontWeight: 400,
1369
+ fontFamily: FONT_FAMILY2
1370
+ };
1371
+ var iconSlotStyles2 = {
1372
+ width: 16,
1373
+ height: 16,
1374
+ flexShrink: 0,
1375
+ display: "inline-flex",
1376
+ alignItems: "center",
1377
+ justifyContent: "center",
1378
+ color: "#fafafa"
1379
+ };
1380
+ var separatorStyles = {
1381
+ height: 1,
1382
+ backgroundColor: "#ffffff1a",
1383
+ margin: "4px 0"
1384
+ };
1385
+ var HIGHLIGHT_CSS = `
1386
+ .sv-vm-item[data-highlighted]:not([data-disabled]),
1387
+ .sv-vm-item:hover:not([data-disabled]) { background-color: #404040; }
1388
+ .sv-vm-item[data-disabled] { opacity: 0.45; cursor: not-allowed; }
1389
+ `;
1390
+ var CheckIcon2 = () => /* @__PURE__ */ jsx5(
1391
+ "svg",
1392
+ {
1393
+ width: "14",
1394
+ height: "14",
1395
+ viewBox: "0 0 24 24",
1396
+ fill: "none",
1397
+ stroke: "currentColor",
1398
+ strokeWidth: "2.5",
1399
+ strokeLinecap: "round",
1400
+ strokeLinejoin: "round",
1401
+ "aria-hidden": "true",
1402
+ children: /* @__PURE__ */ jsx5("path", { d: "M20 6 9 17l-5-5" })
1403
+ }
1404
+ );
1405
+ var ViewMenu = ({
1406
+ circuitJson,
1407
+ circuitJsonKey,
1408
+ menuRef,
1409
+ menuPos,
1410
+ onOpenChange,
1411
+ showGroups,
1412
+ onToggleGroups,
1413
+ showGrid,
1414
+ onToggleGrid,
1415
+ showPorts,
1416
+ onTogglePorts
1417
+ }) => {
1418
+ const hasGroups = useMemo3(() => {
1419
+ if (!circuitJson || circuitJson.length === 0) return false;
1420
+ try {
1421
+ const sourceGroups = su3(circuitJson).source_group?.list() || [];
1422
+ if (sourceGroups.length > 0) return true;
1423
+ const schematicComponents = su3(circuitJson).schematic_component?.list() || [];
1424
+ if (schematicComponents.length > 1) {
1425
+ const componentTypes = /* @__PURE__ */ new Set();
1426
+ for (const comp of schematicComponents) {
1427
+ const sourceComp = su3(circuitJson).source_component.get(
1428
+ comp.source_component_id
1429
+ );
1430
+ if (sourceComp?.ftype) {
1431
+ componentTypes.add(sourceComp.ftype);
1432
+ }
1433
+ }
1434
+ return componentTypes.size > 1;
1435
+ }
1436
+ return false;
1437
+ } catch (error) {
1438
+ console.error("Error checking for groups:", error);
1439
+ return false;
1440
+ }
1441
+ }, [circuitJsonKey]);
1442
+ const hasPorts = useMemo3(() => {
1443
+ if (!circuitJson || circuitJson.length === 0) return false;
1444
+ try {
1445
+ return (su3(circuitJson).schematic_port?.list() || []).length > 0;
1446
+ } catch (error) {
1447
+ console.error("Error checking for schematic ports:", error);
1448
+ return false;
1449
+ }
1450
+ }, [circuitJsonKey]);
1451
+ return /* @__PURE__ */ jsx5(
1452
+ "div",
1453
+ {
1454
+ ref: menuRef,
1455
+ style: {
1456
+ position: "fixed",
1457
+ left: menuPos.x,
1458
+ top: menuPos.y,
1459
+ width: 0,
1460
+ height: 0
1461
+ },
1462
+ children: /* @__PURE__ */ jsxs3(DropdownMenu2.Root, { open: true, onOpenChange, modal: false, children: [
1463
+ /* @__PURE__ */ jsx5(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx5("div", { style: { position: "absolute", width: 1, height: 1 } }) }),
1464
+ /* @__PURE__ */ jsx5(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs3(
1465
+ DropdownMenu2.Content,
1466
+ {
1467
+ style: contentStyles2,
1468
+ align: "start",
1469
+ sideOffset: 0,
1470
+ collisionPadding: 10,
1471
+ avoidCollisions: true,
1472
+ children: [
1473
+ /* @__PURE__ */ jsx5("style", { children: HIGHLIGHT_CSS }),
1474
+ /* @__PURE__ */ jsxs3(
1475
+ DropdownMenu2.Item,
1476
+ {
1477
+ className: "sv-vm-item",
1478
+ style: itemStyles2,
1479
+ disabled: !hasPorts,
1480
+ title: hasPorts ? void 0 : "No ports found in this schematic",
1481
+ onSelect: (event) => event.preventDefault(),
1482
+ onPointerDown: (event) => {
1483
+ event.preventDefault();
1484
+ if (hasPorts) onTogglePorts(!showPorts);
1485
+ },
1486
+ children: [
1487
+ /* @__PURE__ */ jsx5("span", { style: iconSlotStyles2, children: showPorts && /* @__PURE__ */ jsx5(CheckIcon2, {}) }),
1488
+ /* @__PURE__ */ jsx5("span", { children: "Show Schematic Ports" })
1489
+ ]
1490
+ }
1491
+ ),
1492
+ /* @__PURE__ */ jsxs3(
1493
+ DropdownMenu2.Item,
1494
+ {
1495
+ className: "sv-vm-item",
1496
+ style: itemStyles2,
1497
+ disabled: !hasGroups,
1498
+ title: hasGroups ? void 0 : "No groups found in this schematic",
1499
+ onSelect: (event) => event.preventDefault(),
1500
+ onPointerDown: (event) => {
1501
+ event.preventDefault();
1502
+ if (hasGroups) onToggleGroups(!showGroups);
1503
+ },
1504
+ children: [
1505
+ /* @__PURE__ */ jsx5("span", { style: iconSlotStyles2, children: showGroups && /* @__PURE__ */ jsx5(CheckIcon2, {}) }),
1506
+ /* @__PURE__ */ jsx5("span", { children: "View Schematic Groups" })
1507
+ ]
1508
+ }
1509
+ ),
1510
+ /* @__PURE__ */ jsxs3(
1511
+ DropdownMenu2.Item,
1512
+ {
1513
+ className: "sv-vm-item",
1514
+ style: itemStyles2,
1515
+ onSelect: (event) => event.preventDefault(),
1516
+ onPointerDown: (event) => {
1517
+ event.preventDefault();
1518
+ onToggleGrid(!showGrid);
1519
+ },
1520
+ children: [
1521
+ /* @__PURE__ */ jsx5("span", { style: iconSlotStyles2, children: showGrid && /* @__PURE__ */ jsx5(CheckIcon2, {}) }),
1522
+ /* @__PURE__ */ jsx5("span", { children: "Show Grid" })
1523
+ ]
1524
+ }
1525
+ ),
1526
+ /* @__PURE__ */ jsx5(DropdownMenu2.Separator, { style: separatorStyles }),
1527
+ /* @__PURE__ */ jsxs3(
1528
+ "div",
1529
+ {
1530
+ style: {
1531
+ padding: "4px 8px 4px 32px",
1532
+ fontSize: 11,
1533
+ opacity: 0.35,
1534
+ color: "#a1a1aa",
1535
+ letterSpacing: "0.2px",
1536
+ fontFamily: FONT_FAMILY2
1537
+ },
1538
+ children: [
1539
+ "@tscircuit/schematic-viewer@",
1540
+ String(package_default?.version)
1541
+ ]
1542
+ }
1543
+ )
1544
+ ]
1545
+ }
1546
+ ) })
1547
+ ] })
1548
+ }
1549
+ );
1550
+ };
1551
+
1914
1552
  // lib/components/SchematicViewer.tsx
1915
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1553
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1916
1554
  var SchematicViewer = ({
1917
1555
  circuitJson,
1918
1556
  containerStyle,
1919
- editEvents: unappliedEditEvents = [],
1920
- onEditEvent,
1921
- defaultEditMode = false,
1922
1557
  debugGrid = false,
1923
- editingEnabled = false,
1924
- debug: debug3 = false,
1558
+ debug: debug2 = false,
1925
1559
  clickToInteractEnabled = false,
1926
1560
  colorOverrides,
1927
1561
  disableGroups = false,
1928
1562
  netHoverHighlightEnabled = true,
1929
1563
  onSchematicComponentClicked,
1930
- showSchematicPorts = false,
1564
+ showSchematicPorts,
1931
1565
  onSchematicPortClicked,
1932
1566
  onSchematicSheetChange,
1933
1567
  css,
1934
1568
  className
1935
1569
  }) => {
1936
- if (debug3) {
1570
+ if (debug2) {
1937
1571
  enableDebug();
1938
1572
  }
1939
1573
  const getCircuitHash = (circuitJson2) => {
@@ -1962,7 +1596,7 @@ var SchematicViewer = ({
1962
1596
  return defaultSheetId;
1963
1597
  }
1964
1598
  );
1965
- useEffect11(() => {
1599
+ useEffect9(() => {
1966
1600
  const stillExists = selectedSheetId !== void 0 && schematicSheets.some((s) => s.schematic_sheet_id === selectedSheetId);
1967
1601
  if (!stillExists) {
1968
1602
  setSelectedSheetId(defaultSheetId);
@@ -1977,20 +1611,25 @@ var SchematicViewer = ({
1977
1611
  },
1978
1612
  [onSchematicSheetChange]
1979
1613
  );
1980
- const [editModeEnabled, setEditModeEnabled] = useState6(defaultEditMode);
1981
- const [snapToGrid, setSnapToGrid] = useState6(true);
1982
1614
  const [showGridInternal, setShowGridInternal] = useState6(false);
1983
1615
  const showGrid = debugGrid || showGridInternal;
1984
1616
  const [isInteractionEnabled, setIsInteractionEnabled] = useState6(
1985
1617
  !clickToInteractEnabled
1986
1618
  );
1987
- const [showViewMenu, setShowViewMenu] = useState6(false);
1988
1619
  const [showSchematicGroups, setShowSchematicGroups] = useState6(() => {
1989
1620
  if (disableGroups) return false;
1990
- return getStoredBoolean("schematic_viewer_show_groups", false);
1621
+ return getStoredBoolean(STORAGE_KEYS.IS_SHOWING_SCHEMATIC_GROUPS, false);
1991
1622
  });
1623
+ const [showSchematicPortsInternal, setShowSchematicPortsInternal] = useState6(
1624
+ () => showSchematicPorts ?? getStoredBoolean(STORAGE_KEYS.IS_SHOWING_SCHEMATIC_PORTS, false)
1625
+ );
1626
+ useEffect9(() => {
1627
+ if (showSchematicPorts !== void 0) {
1628
+ setShowSchematicPortsInternal(showSchematicPorts);
1629
+ }
1630
+ }, [showSchematicPorts]);
1992
1631
  const [isHoveringClickableComponent, setIsHoveringClickableComponent] = useState6(false);
1993
- const hoveringComponentsRef = useRef8(/* @__PURE__ */ new Set());
1632
+ const hoveringComponentsRef = useRef6(/* @__PURE__ */ new Set());
1994
1633
  const handleComponentHoverChange = useCallback6(
1995
1634
  (componentId, isHovering) => {
1996
1635
  if (isHovering) {
@@ -2003,7 +1642,7 @@ var SchematicViewer = ({
2003
1642
  []
2004
1643
  );
2005
1644
  const [isHoveringClickablePort, setIsHoveringClickablePort] = useState6(false);
2006
- const hoveringPortsRef = useRef8(/* @__PURE__ */ new Set());
1645
+ const hoveringPortsRef = useRef6(/* @__PURE__ */ new Set());
2007
1646
  const handlePortHoverChange = useCallback6(
2008
1647
  (portId, isHovering) => {
2009
1648
  if (isHovering) {
@@ -2015,11 +1654,11 @@ var SchematicViewer = ({
2015
1654
  },
2016
1655
  []
2017
1656
  );
2018
- const svgDivRef = useRef8(null);
2019
- const touchStartRef = useRef8(null);
1657
+ const svgDivRef = useRef6(null);
1658
+ const touchStartRef = useRef6(null);
2020
1659
  const schematicComponentIds = useMemo4(() => {
2021
1660
  try {
2022
- const components = su7(circuitJson).schematic_component?.list() ?? [];
1661
+ const components = su4(circuitJson).schematic_component?.list() ?? [];
2023
1662
  return components.filter(
2024
1663
  (component) => !activeSheetId || component.schematic_sheet_id === activeSheetId
2025
1664
  ).map((component) => component.schematic_component_id);
@@ -2029,14 +1668,14 @@ var SchematicViewer = ({
2029
1668
  }
2030
1669
  }, [circuitJsonKey, circuitJson, activeSheetId]);
2031
1670
  const schematicPortsInfo = useMemo4(() => {
2032
- if (!showSchematicPorts) return [];
1671
+ if (!showSchematicPortsInternal) return [];
2033
1672
  try {
2034
- const ports = (su7(circuitJson).schematic_port?.list() ?? []).filter(
1673
+ const ports = (su4(circuitJson).schematic_port?.list() ?? []).filter(
2035
1674
  (port) => !activeSheetId || port.schematic_sheet_id === activeSheetId
2036
1675
  );
2037
1676
  return ports.map((port) => {
2038
- const sourcePort = su7(circuitJson).source_port.get(port.source_port_id);
2039
- const sourceComponent = sourcePort?.source_component_id ? su7(circuitJson).source_component.get(sourcePort.source_component_id) : null;
1677
+ const sourcePort = su4(circuitJson).source_port.get(port.source_port_id);
1678
+ const sourceComponent = sourcePort?.source_component_id ? su4(circuitJson).source_component.get(sourcePort.source_component_id) : null;
2040
1679
  const componentName = sourceComponent?.name ?? "?";
2041
1680
  const pinLabel = port.display_pin_label ?? sourcePort?.pin_number ?? sourcePort?.name ?? "?";
2042
1681
  return {
@@ -2048,7 +1687,7 @@ var SchematicViewer = ({
2048
1687
  console.error("Failed to derive schematic port info", err);
2049
1688
  return [];
2050
1689
  }
2051
- }, [circuitJsonKey, circuitJson, showSchematicPorts, activeSheetId]);
1690
+ }, [circuitJsonKey, circuitJson, showSchematicPortsInternal, activeSheetId]);
2052
1691
  const handleTouchStart = (e) => {
2053
1692
  const touch = e.touches[0];
2054
1693
  touchStartRef.current = {
@@ -2068,35 +1707,36 @@ var SchematicViewer = ({
2068
1707
  }
2069
1708
  touchStartRef.current = null;
2070
1709
  };
2071
- const [internalEditEvents, setInternalEditEvents] = useState6([]);
2072
- const circuitJsonRef = useRef8(circuitJson);
2073
- useEffect11(() => {
2074
- const circuitHash = getCircuitHash(circuitJson);
2075
- const circuitHashRef = getCircuitHash(circuitJsonRef.current);
2076
- if (circuitHash !== circuitHashRef) {
2077
- setInternalEditEvents([]);
2078
- circuitJsonRef.current = circuitJson;
2079
- }
2080
- }, [circuitJson]);
2081
- const {
2082
- ref: containerRef,
2083
- cancelDrag,
2084
- transform: svgToScreenProjection
2085
- } = useMouseMatrixTransform({
1710
+ const shouldPanSchematic = useCallback6(
1711
+ (event) => {
1712
+ if (event.type !== "mousedown" || !("button" in event)) return true;
1713
+ return event.button !== 2 && !(event.button === 0 && event.ctrlKey);
1714
+ },
1715
+ []
1716
+ );
1717
+ const { ref: containerRef } = useMouseMatrixTransform({
2086
1718
  onSetTransform(transform) {
2087
1719
  if (!svgDivRef.current) return;
2088
1720
  svgDivRef.current.style.transform = transformToString(transform);
2089
1721
  },
2090
1722
  // @ts-ignore disabled is a valid prop but not typed
2091
- enabled: isInteractionEnabled
1723
+ enabled: isInteractionEnabled,
1724
+ shouldDrag: shouldPanSchematic
2092
1725
  });
1726
+ const {
1727
+ menuVisible,
1728
+ menuPos,
1729
+ menuRef,
1730
+ setMenuVisible,
1731
+ contextMenuEventHandlers
1732
+ } = useContextMenu({ containerRef });
2093
1733
  const { containerWidth, containerHeight } = useResizeHandling(containerRef);
2094
1734
  const svgString = useMemo4(() => {
2095
1735
  if (!containerWidth || !containerHeight) return "";
2096
1736
  return convertCircuitJsonToSchematicSvg(circuitJson, {
2097
1737
  width: containerWidth,
2098
1738
  height: containerHeight || 720,
2099
- drawPorts: showSchematicPorts,
1739
+ drawPorts: showSchematicPortsInternal,
2100
1740
  schematicSheetId: activeSheetId,
2101
1741
  grid: !showGrid ? void 0 : {
2102
1742
  cellSize: 1,
@@ -2111,7 +1751,7 @@ var SchematicViewer = ({
2111
1751
  containerWidth,
2112
1752
  containerHeight,
2113
1753
  showGrid,
2114
- showSchematicPorts,
1754
+ showSchematicPortsInternal,
2115
1755
  activeSheetId
2116
1756
  ]);
2117
1757
  const containerBackgroundColor = useMemo4(() => {
@@ -2120,55 +1760,6 @@ var SchematicViewer = ({
2120
1760
  );
2121
1761
  return match?.[1] ?? "transparent";
2122
1762
  }, [svgString]);
2123
- const realToSvgProjection = useMemo4(() => {
2124
- if (!svgString) return identity();
2125
- const transformString = svgString.match(
2126
- /data-real-to-screen-transform="([^"]+)"/
2127
- )?.[1];
2128
- try {
2129
- return fromString(transformString);
2130
- } catch (e) {
2131
- console.error(e);
2132
- return identity();
2133
- }
2134
- }, [svgString]);
2135
- const handleEditEvent = (event) => {
2136
- setInternalEditEvents((prev) => [...prev, event]);
2137
- if (onEditEvent) {
2138
- onEditEvent(event);
2139
- }
2140
- };
2141
- const editEventsWithUnappliedEditEvents = useMemo4(() => {
2142
- return [...unappliedEditEvents, ...internalEditEvents];
2143
- }, [unappliedEditEvents, internalEditEvents]);
2144
- const {
2145
- handleMouseDown,
2146
- handleTouchStart: handleComponentTouchStart,
2147
- isDragging,
2148
- activeEditEvent
2149
- } = useComponentDragging({
2150
- onEditEvent: handleEditEvent,
2151
- cancelDrag,
2152
- realToSvgProjection,
2153
- svgToScreenProjection,
2154
- circuitJson,
2155
- editEvents: editEventsWithUnappliedEditEvents,
2156
- enabled: editModeEnabled && isInteractionEnabled,
2157
- snapToGrid
2158
- });
2159
- useChangeSchematicComponentLocationsInSvg({
2160
- svgDivRef,
2161
- editEvents: editEventsWithUnappliedEditEvents,
2162
- realToSvgProjection,
2163
- svgToScreenProjection,
2164
- activeEditEvent
2165
- });
2166
- useChangeSchematicTracesForMovedComponents({
2167
- svgDivRef,
2168
- circuitJson,
2169
- activeEditEvent,
2170
- editEvents: editEventsWithUnappliedEditEvents
2171
- });
2172
1763
  useSchematicGroupsOverlay({
2173
1764
  svgDivRef,
2174
1765
  circuitJson,
@@ -2181,12 +1772,8 @@ var SchematicViewer = ({
2181
1772
  circuitJsonKey: `${circuitJsonKey}_${activeSheetId ?? ""}`,
2182
1773
  enabled: netHoverHighlightEnabled
2183
1774
  });
2184
- const handleComponentTouchStartRef = useRef8(handleComponentTouchStart);
2185
- useEffect11(() => {
2186
- handleComponentTouchStartRef.current = handleComponentTouchStart;
2187
- }, [handleComponentTouchStart]);
2188
1775
  const svgDiv = useMemo4(
2189
- () => /* @__PURE__ */ jsx9(
1776
+ () => /* @__PURE__ */ jsx6(
2190
1777
  "div",
2191
1778
  {
2192
1779
  ref: svgDivRef,
@@ -2195,22 +1782,17 @@ var SchematicViewer = ({
2195
1782
  transformOrigin: "0 0"
2196
1783
  },
2197
1784
  className: onSchematicComponentClicked ? "schematic-component-clickable" : void 0,
2198
- onTouchStart: (e) => {
2199
- if (editModeEnabled && isInteractionEnabled) {
2200
- handleComponentTouchStartRef.current(e);
2201
- }
2202
- },
2203
1785
  dangerouslySetInnerHTML: { __html: svgString }
2204
1786
  }
2205
1787
  ),
2206
- [svgString, isInteractionEnabled, clickToInteractEnabled, editModeEnabled]
1788
+ [svgString, isInteractionEnabled, clickToInteractEnabled]
2207
1789
  );
2208
- return /* @__PURE__ */ jsxs6(MouseTracker, { children: [
2209
- netHoverHighlightEnabled && /* @__PURE__ */ jsx9("style", { children: `.sch-net-faded { opacity: 0.35; }
1790
+ return /* @__PURE__ */ jsxs4(MouseTracker, { children: [
1791
+ netHoverHighlightEnabled && /* @__PURE__ */ jsx6("style", { children: `.sch-net-faded { opacity: 0.35; }
2210
1792
  svg :is(g.trace, g.trace-overlays, g[data-schematic-component-id], [data-schematic-net-label-id]) { transition: opacity 0.12s ease-in-out; }` }),
2211
- onSchematicComponentClicked && /* @__PURE__ */ jsx9("style", { children: `.schematic-component-clickable [data-schematic-component-id]:hover { cursor: pointer !important; }` }),
2212
- onSchematicPortClicked && /* @__PURE__ */ jsx9("style", { children: `[data-schematic-port-id]:hover { cursor: pointer !important; }` }),
2213
- /* @__PURE__ */ jsxs6(
1793
+ onSchematicComponentClicked && /* @__PURE__ */ jsx6("style", { children: ".schematic-component-clickable [data-schematic-component-id]:hover { cursor: pointer !important; }" }),
1794
+ onSchematicPortClicked && /* @__PURE__ */ jsx6("style", { children: "[data-schematic-port-id]:hover { cursor: pointer !important; }" }),
1795
+ /* @__PURE__ */ jsxs4(
2214
1796
  "div",
2215
1797
  {
2216
1798
  ref: containerRef,
@@ -2218,29 +1800,34 @@ var SchematicViewer = ({
2218
1800
  position: "relative",
2219
1801
  backgroundColor: containerBackgroundColor,
2220
1802
  overflow: "hidden",
2221
- cursor: isDragging ? "grabbing" : clickToInteractEnabled && !isInteractionEnabled ? "pointer" : isHoveringClickableComponent && onSchematicComponentClicked ? "pointer" : isHoveringClickablePort && onSchematicPortClicked ? "pointer" : "grab",
1803
+ cursor: clickToInteractEnabled && !isInteractionEnabled ? "pointer" : isHoveringClickableComponent && onSchematicComponentClicked ? "pointer" : isHoveringClickablePort && onSchematicPortClicked ? "pointer" : "grab",
2222
1804
  minHeight: "300px",
1805
+ userSelect: "none",
1806
+ WebkitUserSelect: "none",
1807
+ WebkitTouchCallout: "none",
2223
1808
  ...containerStyle
2224
1809
  },
2225
- onMouseDown: (e) => {
2226
- if (clickToInteractEnabled && !isInteractionEnabled) {
2227
- e.preventDefault();
2228
- e.stopPropagation();
2229
- return;
2230
- }
2231
- handleMouseDown(e);
2232
- },
2233
1810
  onMouseDownCapture: (e) => {
1811
+ contextMenuEventHandlers.onMouseDown(e);
2234
1812
  if (clickToInteractEnabled && !isInteractionEnabled) {
2235
1813
  e.preventDefault();
2236
1814
  e.stopPropagation();
2237
1815
  return;
2238
1816
  }
2239
1817
  },
2240
- onTouchStart: handleTouchStart,
2241
- onTouchEnd: handleTouchEnd,
1818
+ onContextMenu: contextMenuEventHandlers.onContextMenu,
1819
+ onTouchStart: (event) => {
1820
+ handleTouchStart(event);
1821
+ contextMenuEventHandlers.onTouchStart(event);
1822
+ },
1823
+ onTouchMove: contextMenuEventHandlers.onTouchMove,
1824
+ onTouchEnd: (event) => {
1825
+ handleTouchEnd(event);
1826
+ contextMenuEventHandlers.onTouchEnd();
1827
+ },
1828
+ onTouchCancel: contextMenuEventHandlers.onTouchCancel,
2242
1829
  children: [
2243
- !isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx9(
1830
+ !isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx6(
2244
1831
  "div",
2245
1832
  {
2246
1833
  onClick: (e) => {
@@ -2259,7 +1846,7 @@ var SchematicViewer = ({
2259
1846
  pointerEvents: "all",
2260
1847
  touchAction: "pan-x pan-y pinch-zoom"
2261
1848
  },
2262
- children: /* @__PURE__ */ jsx9(
1849
+ children: /* @__PURE__ */ jsx6(
2263
1850
  "div",
2264
1851
  {
2265
1852
  style: {
@@ -2276,39 +1863,34 @@ var SchematicViewer = ({
2276
1863
  )
2277
1864
  }
2278
1865
  ),
2279
- editingEnabled && /* @__PURE__ */ jsx9(
2280
- EditIcon,
2281
- {
2282
- active: editModeEnabled,
2283
- onClick: () => setEditModeEnabled(!editModeEnabled)
2284
- }
2285
- ),
2286
- editingEnabled && editModeEnabled && /* @__PURE__ */ jsx9(
2287
- GridIcon,
2288
- {
2289
- active: snapToGrid,
2290
- onClick: () => setSnapToGrid(!snapToGrid)
2291
- }
2292
- ),
2293
- /* @__PURE__ */ jsx9(
1866
+ menuVisible && /* @__PURE__ */ jsx6(
2294
1867
  ViewMenu,
2295
1868
  {
2296
1869
  circuitJson,
2297
1870
  circuitJsonKey,
2298
- open: showViewMenu,
2299
- onOpenChange: setShowViewMenu,
1871
+ menuRef,
1872
+ menuPos,
1873
+ onOpenChange: setMenuVisible,
1874
+ showPorts: showSchematicPortsInternal,
1875
+ onTogglePorts: (value) => {
1876
+ setShowSchematicPortsInternal(value);
1877
+ setStoredBoolean(STORAGE_KEYS.IS_SHOWING_SCHEMATIC_PORTS, value);
1878
+ },
2300
1879
  showGroups: showSchematicGroups,
2301
1880
  onToggleGroups: (value) => {
2302
1881
  if (!disableGroups) {
2303
1882
  setShowSchematicGroups(value);
2304
- setStoredBoolean("schematic_viewer_show_groups", value);
1883
+ setStoredBoolean(
1884
+ STORAGE_KEYS.IS_SHOWING_SCHEMATIC_GROUPS,
1885
+ value
1886
+ );
2305
1887
  }
2306
1888
  },
2307
1889
  showGrid,
2308
1890
  onToggleGrid: setShowGridInternal
2309
1891
  }
2310
1892
  ),
2311
- /* @__PURE__ */ jsx9(
1893
+ /* @__PURE__ */ jsx6(
2312
1894
  SchematicSheetSelector,
2313
1895
  {
2314
1896
  sheets: schematicSheets,
@@ -2316,7 +1898,7 @@ var SchematicViewer = ({
2316
1898
  onSelectSheet: handleSelectSheet
2317
1899
  }
2318
1900
  ),
2319
- onSchematicComponentClicked && schematicComponentIds.map((componentId) => /* @__PURE__ */ jsx9(
1901
+ onSchematicComponentClicked && schematicComponentIds.map((componentId) => /* @__PURE__ */ jsx6(
2320
1902
  SchematicComponentMouseTarget,
2321
1903
  {
2322
1904
  componentId,
@@ -2335,7 +1917,7 @@ var SchematicViewer = ({
2335
1917
  componentId
2336
1918
  )),
2337
1919
  svgDiv,
2338
- showSchematicPorts && schematicPortsInfo.map(({ portId, label }) => /* @__PURE__ */ jsx9(
1920
+ showSchematicPortsInternal && schematicPortsInfo.map(({ portId, label }) => /* @__PURE__ */ jsx6(
2339
1921
  SchematicPortMouseTarget,
2340
1922
  {
2341
1923
  portId,
@@ -2365,7 +1947,7 @@ import {
2365
1947
  convertCircuitJsonToSchematicSimulationSvg,
2366
1948
  convertCircuitJsonToSimulationGraphSvg
2367
1949
  } from "circuit-to-svg";
2368
- import { useEffect as useEffect12, useMemo as useMemo5, useRef as useRef9, useState as useState8 } from "react";
1950
+ import { useEffect as useEffect10, useMemo as useMemo5, useRef as useRef7, useState as useState8 } from "react";
2369
1951
  import { toString as transformToString2 } from "transformation-matrix";
2370
1952
  import { useMouseMatrixTransform as useMouseMatrixTransform2 } from "use-mouse-matrix-transform";
2371
1953
 
@@ -2384,7 +1966,7 @@ var getAnalogSimulationBackgroundColor = (simulationSvg, colorOverrides) => getR
2384
1966
  // lib/components/AnalogSimulationSelector.tsx
2385
1967
  import * as DropdownMenu3 from "@radix-ui/react-dropdown-menu";
2386
1968
  import { useState as useState7 } from "react";
2387
- import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1969
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
2388
1970
  var FONT_FAMILY3 = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2389
1971
  var contentStyles3 = {
2390
1972
  backgroundColor: "#ffffff",
@@ -2434,7 +2016,7 @@ var MENU_CSS2 = `
2434
2016
  .sv-simulation-chevron { transition: transform 0.2s ease; }
2435
2017
  [data-state="open"] > .sv-simulation-chevron { transform: rotate(180deg); }
2436
2018
  `;
2437
- var CheckIcon3 = () => /* @__PURE__ */ jsx10(
2019
+ var CheckIcon3 = () => /* @__PURE__ */ jsx7(
2438
2020
  "svg",
2439
2021
  {
2440
2022
  width: "14",
@@ -2446,10 +2028,10 @@ var CheckIcon3 = () => /* @__PURE__ */ jsx10(
2446
2028
  strokeLinecap: "round",
2447
2029
  strokeLinejoin: "round",
2448
2030
  "aria-hidden": "true",
2449
- children: /* @__PURE__ */ jsx10("path", { d: "M20 6 9 17l-5-5" })
2031
+ children: /* @__PURE__ */ jsx7("path", { d: "M20 6 9 17l-5-5" })
2450
2032
  }
2451
2033
  );
2452
- var ChevronDownIcon2 = ({ className }) => /* @__PURE__ */ jsx10(
2034
+ var ChevronDownIcon2 = ({ className }) => /* @__PURE__ */ jsx7(
2453
2035
  "svg",
2454
2036
  {
2455
2037
  className,
@@ -2463,7 +2045,7 @@ var ChevronDownIcon2 = ({ className }) => /* @__PURE__ */ jsx10(
2463
2045
  strokeLinejoin: "round",
2464
2046
  style: { opacity: 0.6, flexShrink: 0 },
2465
2047
  "aria-hidden": "true",
2466
- children: /* @__PURE__ */ jsx10("path", { d: "m6 9 6 6 6-6" })
2048
+ children: /* @__PURE__ */ jsx7("path", { d: "m6 9 6 6 6-6" })
2467
2049
  }
2468
2050
  );
2469
2051
  var getSimulationLabels = (simulations) => {
@@ -2489,10 +2071,10 @@ var AnalogSimulationSelector = ({
2489
2071
  ({ simulation }) => simulation.simulation_experiment_id === selectedSimulationExperimentId
2490
2072
  );
2491
2073
  const selectedLabel = selectedSimulation?.label ?? "Select simulation";
2492
- return /* @__PURE__ */ jsxs7(Fragment4, { children: [
2493
- /* @__PURE__ */ jsx10("style", { children: MENU_CSS2 }),
2494
- /* @__PURE__ */ jsxs7(DropdownMenu3.Root, { open, onOpenChange: setOpen, modal: false, children: [
2495
- /* @__PURE__ */ jsx10(DropdownMenu3.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs7(
2074
+ return /* @__PURE__ */ jsxs5(Fragment4, { children: [
2075
+ /* @__PURE__ */ jsx7("style", { children: MENU_CSS2 }),
2076
+ /* @__PURE__ */ jsxs5(DropdownMenu3.Root, { open, onOpenChange: setOpen, modal: false, children: [
2077
+ /* @__PURE__ */ jsx7(DropdownMenu3.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs5(
2496
2078
  "button",
2497
2079
  {
2498
2080
  type: "button",
@@ -2519,13 +2101,13 @@ var AnalogSimulationSelector = ({
2519
2101
  zIndex: zIndexMap.viewMenuIcon
2520
2102
  },
2521
2103
  children: [
2522
- /* @__PURE__ */ jsx10("span", { style: { color: "#888888", flexShrink: 0 }, children: "Simulation:" }),
2523
- /* @__PURE__ */ jsx10("span", { style: { ...ellipsisStyles2, minWidth: 0 }, children: selectedLabel }),
2524
- /* @__PURE__ */ jsx10(ChevronDownIcon2, { className: "sv-simulation-chevron" })
2104
+ /* @__PURE__ */ jsx7("span", { style: { color: "#888888", flexShrink: 0 }, children: "Simulation:" }),
2105
+ /* @__PURE__ */ jsx7("span", { style: { ...ellipsisStyles2, minWidth: 0 }, children: selectedLabel }),
2106
+ /* @__PURE__ */ jsx7(ChevronDownIcon2, { className: "sv-simulation-chevron" })
2525
2107
  ]
2526
2108
  }
2527
2109
  ) }),
2528
- /* @__PURE__ */ jsx10(DropdownMenu3.Portal, { children: /* @__PURE__ */ jsx10(
2110
+ /* @__PURE__ */ jsx7(DropdownMenu3.Portal, { children: /* @__PURE__ */ jsx7(
2529
2111
  DropdownMenu3.Content,
2530
2112
  {
2531
2113
  style: contentStyles3,
@@ -2535,7 +2117,7 @@ var AnalogSimulationSelector = ({
2535
2117
  collisionPadding: 10,
2536
2118
  children: simulationLabels.map(({ simulation, label }) => {
2537
2119
  const selected = simulation.simulation_experiment_id === selectedSimulationExperimentId;
2538
- return /* @__PURE__ */ jsxs7(
2120
+ return /* @__PURE__ */ jsxs5(
2539
2121
  DropdownMenu3.Item,
2540
2122
  {
2541
2123
  className: "sv-simulation-item",
@@ -2547,8 +2129,8 @@ var AnalogSimulationSelector = ({
2547
2129
  setOpen(false);
2548
2130
  },
2549
2131
  children: [
2550
- /* @__PURE__ */ jsx10("span", { style: iconSlotStyles3, children: selected && /* @__PURE__ */ jsx10(CheckIcon3, {}) }),
2551
- /* @__PURE__ */ jsx10("span", { style: { ...ellipsisStyles2, minWidth: 0 }, children: label })
2132
+ /* @__PURE__ */ jsx7("span", { style: iconSlotStyles3, children: selected && /* @__PURE__ */ jsx7(CheckIcon3, {}) }),
2133
+ /* @__PURE__ */ jsx7("span", { style: { ...ellipsisStyles2, minWidth: 0 }, children: label })
2552
2134
  ]
2553
2135
  },
2554
2136
  simulation.simulation_experiment_id
@@ -2561,7 +2143,7 @@ var AnalogSimulationSelector = ({
2561
2143
  };
2562
2144
 
2563
2145
  // lib/components/AnalogSimulationViewer.tsx
2564
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2146
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2565
2147
  var DEFAULT_RENDER_WIDTH = 1200;
2566
2148
  var DEFAULT_COMBINED_RENDER_ASPECT_RATIO = 1;
2567
2149
  var DEFAULT_GRAPH_ONLY_RENDER_ASPECT_RATIO = 2;
@@ -2580,8 +2162,8 @@ var AnalogSimulationViewer = ({
2580
2162
  const [isLoading, setIsLoading] = useState8(true);
2581
2163
  const [error, setError] = useState8(null);
2582
2164
  const [svgObjectUrl, setSvgObjectUrl] = useState8(null);
2583
- const containerRef = useRef9(null);
2584
- const imgRef = useRef9(null);
2165
+ const containerRef = useRef7(null);
2166
+ const imgRef = useRef7(null);
2585
2167
  const { containerWidth } = useResizeHandling(
2586
2168
  containerRef
2587
2169
  );
@@ -2601,7 +2183,7 @@ var AnalogSimulationViewer = ({
2601
2183
  const renderAspectRatio = width && height ? width / height : defaultRenderAspectRatio;
2602
2184
  const effectiveWidth = width || (height ? height * renderAspectRatio : containerWidth) || DEFAULT_RENDER_WIDTH;
2603
2185
  const effectiveHeight = height || effectiveWidth / renderAspectRatio;
2604
- useEffect12(() => {
2186
+ useEffect10(() => {
2605
2187
  setIsLoading(true);
2606
2188
  setError(null);
2607
2189
  setCircuitJson(inputCircuitJson);
@@ -2617,7 +2199,7 @@ var AnalogSimulationViewer = ({
2617
2199
  const simulationExperimentId = (selectedSimulationExperimentId && simulationExperiments.some(
2618
2200
  (simulation) => simulation.simulation_experiment_id === selectedSimulationExperimentId
2619
2201
  ) ? selectedSimulationExperimentId : simulationExperiments[0]?.simulation_experiment_id) ?? null;
2620
- useEffect12(() => {
2202
+ useEffect10(() => {
2621
2203
  if (simulationExperimentId !== selectedSimulationExperimentId) {
2622
2204
  setSelectedSimulationExperimentId(simulationExperimentId);
2623
2205
  }
@@ -2654,7 +2236,7 @@ var AnalogSimulationViewer = ({
2654
2236
  simulationExperimentId,
2655
2237
  acSweepView
2656
2238
  ]);
2657
- useEffect12(() => {
2239
+ useEffect10(() => {
2658
2240
  if (!simulationSvg) {
2659
2241
  setSvgObjectUrl(null);
2660
2242
  return;
@@ -2680,7 +2262,7 @@ var AnalogSimulationViewer = ({
2680
2262
  const handleTouchStart = (_e) => {
2681
2263
  setIsDragging(true);
2682
2264
  };
2683
- useEffect12(() => {
2265
+ useEffect10(() => {
2684
2266
  const handleMouseUp = () => {
2685
2267
  setIsDragging(false);
2686
2268
  };
@@ -2695,7 +2277,7 @@ var AnalogSimulationViewer = ({
2695
2277
  };
2696
2278
  }, []);
2697
2279
  if (isLoading) {
2698
- return /* @__PURE__ */ jsx11(
2280
+ return /* @__PURE__ */ jsx8(
2699
2281
  "div",
2700
2282
  {
2701
2283
  style: {
@@ -2715,7 +2297,7 @@ var AnalogSimulationViewer = ({
2715
2297
  );
2716
2298
  }
2717
2299
  if (error) {
2718
- return /* @__PURE__ */ jsx11(
2300
+ return /* @__PURE__ */ jsx8(
2719
2301
  "div",
2720
2302
  {
2721
2303
  style: {
@@ -2730,15 +2312,15 @@ var AnalogSimulationViewer = ({
2730
2312
  ...containerStyle
2731
2313
  },
2732
2314
  className,
2733
- children: /* @__PURE__ */ jsxs8("div", { style: { textAlign: "center", padding: "20px" }, children: [
2734
- /* @__PURE__ */ jsx11("div", { style: { fontWeight: "bold", marginBottom: "8px" }, children: "Circuit Conversion Error" }),
2735
- /* @__PURE__ */ jsx11("div", { style: { fontSize: "14px" }, children: error })
2315
+ children: /* @__PURE__ */ jsxs6("div", { style: { textAlign: "center", padding: "20px" }, children: [
2316
+ /* @__PURE__ */ jsx8("div", { style: { fontWeight: "bold", marginBottom: "8px" }, children: "Circuit Conversion Error" }),
2317
+ /* @__PURE__ */ jsx8("div", { style: { fontSize: "14px" }, children: error })
2736
2318
  ] })
2737
2319
  }
2738
2320
  );
2739
2321
  }
2740
2322
  if (!simulationSvg) {
2741
- return /* @__PURE__ */ jsxs8(
2323
+ return /* @__PURE__ */ jsxs6(
2742
2324
  "div",
2743
2325
  {
2744
2326
  style: {
@@ -2754,11 +2336,11 @@ var AnalogSimulationViewer = ({
2754
2336
  },
2755
2337
  className,
2756
2338
  children: [
2757
- /* @__PURE__ */ jsx11("div", { style: { fontSize: "16px", color: "#475569", fontWeight: 500 }, children: "No Simulation Found" }),
2758
- /* @__PURE__ */ jsxs8("div", { style: { fontSize: "14px", color: "#64748b" }, children: [
2339
+ /* @__PURE__ */ jsx8("div", { style: { fontSize: "16px", color: "#475569", fontWeight: 500 }, children: "No Simulation Found" }),
2340
+ /* @__PURE__ */ jsxs6("div", { style: { fontSize: "14px", color: "#64748b" }, children: [
2759
2341
  "Use",
2760
2342
  " ",
2761
- /* @__PURE__ */ jsx11(
2343
+ /* @__PURE__ */ jsx8(
2762
2344
  "code",
2763
2345
  {
2764
2346
  style: {
@@ -2778,7 +2360,7 @@ var AnalogSimulationViewer = ({
2778
2360
  }
2779
2361
  );
2780
2362
  }
2781
- return /* @__PURE__ */ jsxs8(
2363
+ return /* @__PURE__ */ jsxs6(
2782
2364
  "div",
2783
2365
  {
2784
2366
  ref: (node) => {
@@ -2797,7 +2379,7 @@ var AnalogSimulationViewer = ({
2797
2379
  onMouseDown: handleMouseDown,
2798
2380
  onTouchStart: handleTouchStart,
2799
2381
  children: [
2800
- /* @__PURE__ */ jsx11(
2382
+ /* @__PURE__ */ jsx8(
2801
2383
  AnalogSimulationSelector,
2802
2384
  {
2803
2385
  simulations: simulationExperiments,
@@ -2805,7 +2387,7 @@ var AnalogSimulationViewer = ({
2805
2387
  onSelectSimulation: handleSelectSimulation
2806
2388
  }
2807
2389
  ),
2808
- svgObjectUrl ? /* @__PURE__ */ jsx11(
2390
+ svgObjectUrl ? /* @__PURE__ */ jsx8(
2809
2391
  "img",
2810
2392
  {
2811
2393
  ref: imgRef,
@@ -2819,7 +2401,7 @@ var AnalogSimulationViewer = ({
2819
2401
  objectFit: "contain"
2820
2402
  }
2821
2403
  }
2822
- ) : /* @__PURE__ */ jsx11(
2404
+ ) : /* @__PURE__ */ jsx8(
2823
2405
  "div",
2824
2406
  {
2825
2407
  style: {