@tscircuit/schematic-viewer 2.0.78 → 2.0.80
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.d.ts +1 -1
- package/dist/index.js +1014 -442
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,47 @@
|
|
|
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
|
-
|
|
6
|
+
|
|
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"
|
|
13
|
+
};
|
|
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
|
+
}
|
|
22
|
+
};
|
|
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
|
+
}
|
|
44
|
+
};
|
|
6
45
|
|
|
7
46
|
// lib/hooks/useSchematicGroupsOverlay.ts
|
|
8
47
|
import { useEffect } from "react";
|
|
@@ -399,7 +438,7 @@ var enableDebug = () => {
|
|
|
399
438
|
};
|
|
400
439
|
|
|
401
440
|
// lib/components/SchematicViewer.tsx
|
|
402
|
-
import { useCallback as
|
|
441
|
+
import { useCallback as useCallback6, useEffect as useEffect9, useMemo as useMemo5, useRef as useRef6, useState as useState6 } from "react";
|
|
403
442
|
import { toString as transformToString } from "transformation-matrix";
|
|
404
443
|
import { useMouseMatrixTransform } from "use-mouse-matrix-transform";
|
|
405
444
|
|
|
@@ -427,328 +466,268 @@ var useResizeHandling = (containerRef) => {
|
|
|
427
466
|
return { containerWidth, containerHeight };
|
|
428
467
|
};
|
|
429
468
|
|
|
430
|
-
// lib/
|
|
431
|
-
import {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
485
|
-
"circuit-json": "^0.0.465",
|
|
486
|
-
"circuit-to-svg": "^0.0.393",
|
|
487
|
-
debug: "^4.4.0",
|
|
488
|
-
"performance-now": "^2.1.0",
|
|
489
|
-
"use-mouse-matrix-transform": "^1.2.2"
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
// lib/components/ViewMenuIcon.tsx
|
|
494
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
495
|
-
var ViewMenuIcon = ({
|
|
496
|
-
active = false,
|
|
497
|
-
...props
|
|
498
|
-
}) => {
|
|
499
|
-
return /* @__PURE__ */ jsx(
|
|
500
|
-
"button",
|
|
501
|
-
{
|
|
502
|
-
type: "button",
|
|
503
|
-
title: active ? "Hide view menu" : "Show view menu",
|
|
504
|
-
...props,
|
|
505
|
-
style: {
|
|
506
|
-
position: "absolute",
|
|
507
|
-
top: "16px",
|
|
508
|
-
right: "16px",
|
|
509
|
-
backgroundColor: active ? "#4CAF50" : "#fff",
|
|
510
|
-
color: active ? "#fff" : "#000",
|
|
511
|
-
padding: "8px",
|
|
512
|
-
border: "none",
|
|
513
|
-
borderRadius: "4px",
|
|
514
|
-
cursor: "pointer",
|
|
515
|
-
outline: "none",
|
|
516
|
-
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
|
517
|
-
display: "flex",
|
|
518
|
-
alignItems: "center",
|
|
519
|
-
gap: "4px",
|
|
520
|
-
zIndex: zIndexMap.viewMenuIcon
|
|
521
|
-
},
|
|
522
|
-
children: /* @__PURE__ */ jsxs(
|
|
523
|
-
"svg",
|
|
524
|
-
{
|
|
525
|
-
width: "16",
|
|
526
|
-
height: "16",
|
|
527
|
-
viewBox: "0 0 24 24",
|
|
528
|
-
fill: "none",
|
|
529
|
-
stroke: "currentColor",
|
|
530
|
-
strokeWidth: "2",
|
|
531
|
-
children: [
|
|
532
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1" }),
|
|
533
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1" }),
|
|
534
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1" })
|
|
535
|
-
]
|
|
536
|
-
}
|
|
537
|
-
)
|
|
538
|
-
}
|
|
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;
|
|
502
|
+
}
|
|
503
|
+
const touch = event.touches[0];
|
|
504
|
+
if (!touch) return;
|
|
505
|
+
interactionOriginRef.current = {
|
|
506
|
+
x: touch.clientX,
|
|
507
|
+
y: touch.clientY
|
|
508
|
+
};
|
|
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);
|
|
521
|
+
},
|
|
522
|
+
[clearLongPressTimeout, containerRef]
|
|
539
523
|
);
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
boxShadow: "0 6px 24px rgba(0,0,0,0.12), 0 1px 3px rgba(0,0,0,0.08)",
|
|
550
|
-
border: "1px solid #e5e7eb",
|
|
551
|
-
padding: 4,
|
|
552
|
-
minWidth: 224,
|
|
553
|
-
fontSize: 13,
|
|
554
|
-
fontFamily: FONT_FAMILY,
|
|
555
|
-
outline: "none",
|
|
556
|
-
zIndex: zIndexMap.viewMenu
|
|
557
|
-
};
|
|
558
|
-
var itemStyles = {
|
|
559
|
-
display: "flex",
|
|
560
|
-
alignItems: "center",
|
|
561
|
-
gap: 8,
|
|
562
|
-
padding: "7px 10px 7px 8px",
|
|
563
|
-
borderRadius: 6,
|
|
564
|
-
cursor: "pointer",
|
|
565
|
-
outline: "none",
|
|
566
|
-
userSelect: "none",
|
|
567
|
-
color: "#111111",
|
|
568
|
-
fontSize: 13,
|
|
569
|
-
fontFamily: FONT_FAMILY
|
|
570
|
-
};
|
|
571
|
-
var iconSlotStyles = {
|
|
572
|
-
width: 16,
|
|
573
|
-
height: 16,
|
|
574
|
-
flexShrink: 0,
|
|
575
|
-
display: "inline-flex",
|
|
576
|
-
alignItems: "center",
|
|
577
|
-
justifyContent: "center",
|
|
578
|
-
color: "#111111"
|
|
579
|
-
};
|
|
580
|
-
var separatorStyles = {
|
|
581
|
-
height: 1,
|
|
582
|
-
backgroundColor: "#ececec",
|
|
583
|
-
margin: "4px 0"
|
|
584
|
-
};
|
|
585
|
-
var HIGHLIGHT_CSS = `
|
|
586
|
-
.sv-vm-item[data-highlighted]:not([data-disabled]),
|
|
587
|
-
.sv-vm-item:hover:not([data-disabled]) { background-color: #f1f3f5; }
|
|
588
|
-
.sv-vm-item[data-disabled] { opacity: 0.45; cursor: not-allowed; }
|
|
589
|
-
`;
|
|
590
|
-
var CheckIcon = () => /* @__PURE__ */ jsx2(
|
|
591
|
-
"svg",
|
|
592
|
-
{
|
|
593
|
-
width: "14",
|
|
594
|
-
height: "14",
|
|
595
|
-
viewBox: "0 0 24 24",
|
|
596
|
-
fill: "none",
|
|
597
|
-
stroke: "currentColor",
|
|
598
|
-
strokeWidth: "2.5",
|
|
599
|
-
strokeLinecap: "round",
|
|
600
|
-
strokeLinejoin: "round",
|
|
601
|
-
"aria-hidden": "true",
|
|
602
|
-
children: /* @__PURE__ */ jsx2("path", { d: "M20 6 9 17l-5-5" })
|
|
603
|
-
}
|
|
604
|
-
);
|
|
605
|
-
var ViewMenu = ({
|
|
606
|
-
circuitJson,
|
|
607
|
-
circuitJsonKey,
|
|
608
|
-
open,
|
|
609
|
-
onOpenChange,
|
|
610
|
-
showGroups,
|
|
611
|
-
onToggleGroups,
|
|
612
|
-
showGrid,
|
|
613
|
-
onToggleGrid
|
|
614
|
-
}) => {
|
|
615
|
-
const hasGroups = useMemo(() => {
|
|
616
|
-
if (!circuitJson || circuitJson.length === 0) return false;
|
|
617
|
-
try {
|
|
618
|
-
const sourceGroups = su3(circuitJson).source_group?.list() || [];
|
|
619
|
-
if (sourceGroups.length > 0) return true;
|
|
620
|
-
const schematicComponents = su3(circuitJson).schematic_component?.list() || [];
|
|
621
|
-
if (schematicComponents.length > 1) {
|
|
622
|
-
const componentTypes = /* @__PURE__ */ new Set();
|
|
623
|
-
for (const comp of schematicComponents) {
|
|
624
|
-
const sourceComp = su3(circuitJson).source_component.get(
|
|
625
|
-
comp.source_component_id
|
|
626
|
-
);
|
|
627
|
-
if (sourceComp?.ftype) {
|
|
628
|
-
componentTypes.add(sourceComp.ftype);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
return componentTypes.size > 1;
|
|
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();
|
|
632
533
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
534
|
+
},
|
|
535
|
+
[clearLongPressTimeout]
|
|
536
|
+
);
|
|
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);
|
|
554
|
+
return () => {
|
|
555
|
+
document.removeEventListener("mousedown", handleClickAway);
|
|
556
|
+
document.removeEventListener("touchstart", handleClickAway);
|
|
557
|
+
};
|
|
558
|
+
}, [handleClickAway, menuVisible]);
|
|
559
|
+
useEffect4(() => clearLongPressTimeout, [clearLongPressTimeout]);
|
|
560
|
+
return {
|
|
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;
|
|
568
|
+
},
|
|
569
|
+
onContextMenu: handleContextMenu,
|
|
570
|
+
onTouchStart: handleTouchStart,
|
|
571
|
+
onTouchMove: handleTouchMove,
|
|
572
|
+
onTouchEnd: handleTouchEnd,
|
|
573
|
+
onTouchCancel: handleTouchEnd
|
|
637
574
|
}
|
|
638
|
-
}
|
|
639
|
-
return /* @__PURE__ */ jsxs2(DropdownMenu.Root, { open, onOpenChange, modal: false, children: [
|
|
640
|
-
/* @__PURE__ */ jsx2(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx2(ViewMenuIcon, { active: open }) }),
|
|
641
|
-
/* @__PURE__ */ jsx2(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs2(
|
|
642
|
-
DropdownMenu.Content,
|
|
643
|
-
{
|
|
644
|
-
style: contentStyles,
|
|
645
|
-
side: "bottom",
|
|
646
|
-
align: "end",
|
|
647
|
-
sideOffset: 8,
|
|
648
|
-
collisionPadding: 10,
|
|
649
|
-
children: [
|
|
650
|
-
/* @__PURE__ */ jsx2("style", { children: HIGHLIGHT_CSS }),
|
|
651
|
-
/* @__PURE__ */ jsxs2(
|
|
652
|
-
DropdownMenu.Item,
|
|
653
|
-
{
|
|
654
|
-
className: "sv-vm-item",
|
|
655
|
-
style: itemStyles,
|
|
656
|
-
disabled: !hasGroups,
|
|
657
|
-
title: hasGroups ? void 0 : "No groups found in this schematic",
|
|
658
|
-
onSelect: (e) => e.preventDefault(),
|
|
659
|
-
onPointerUp: () => {
|
|
660
|
-
if (hasGroups) onToggleGroups(!showGroups);
|
|
661
|
-
},
|
|
662
|
-
children: [
|
|
663
|
-
/* @__PURE__ */ jsx2("span", { style: iconSlotStyles, children: showGroups && /* @__PURE__ */ jsx2(CheckIcon, {}) }),
|
|
664
|
-
/* @__PURE__ */ jsx2("span", { children: "View Schematic Groups" })
|
|
665
|
-
]
|
|
666
|
-
}
|
|
667
|
-
),
|
|
668
|
-
/* @__PURE__ */ jsxs2(
|
|
669
|
-
DropdownMenu.Item,
|
|
670
|
-
{
|
|
671
|
-
className: "sv-vm-item",
|
|
672
|
-
style: itemStyles,
|
|
673
|
-
onSelect: (e) => e.preventDefault(),
|
|
674
|
-
onPointerUp: () => onToggleGrid(!showGrid),
|
|
675
|
-
children: [
|
|
676
|
-
/* @__PURE__ */ jsx2("span", { style: iconSlotStyles, children: showGrid && /* @__PURE__ */ jsx2(CheckIcon, {}) }),
|
|
677
|
-
/* @__PURE__ */ jsx2("span", { children: "Show Grid" })
|
|
678
|
-
]
|
|
679
|
-
}
|
|
680
|
-
),
|
|
681
|
-
/* @__PURE__ */ jsx2(DropdownMenu.Separator, { style: separatorStyles }),
|
|
682
|
-
/* @__PURE__ */ jsxs2(
|
|
683
|
-
"div",
|
|
684
|
-
{
|
|
685
|
-
style: {
|
|
686
|
-
padding: "4px 8px",
|
|
687
|
-
fontSize: 12,
|
|
688
|
-
color: "#9ca3af",
|
|
689
|
-
textAlign: "center",
|
|
690
|
-
fontFamily: FONT_FAMILY
|
|
691
|
-
},
|
|
692
|
-
children: [
|
|
693
|
-
"v",
|
|
694
|
-
String(package_default?.version)
|
|
695
|
-
]
|
|
696
|
-
}
|
|
697
|
-
)
|
|
698
|
-
]
|
|
699
|
-
}
|
|
700
|
-
) })
|
|
701
|
-
] });
|
|
575
|
+
};
|
|
702
576
|
};
|
|
703
577
|
|
|
704
|
-
// lib/
|
|
705
|
-
import {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
578
|
+
// lib/utils/component-details.ts
|
|
579
|
+
import {
|
|
580
|
+
getPcbElementBounds,
|
|
581
|
+
getPcbElementsWithinBounds
|
|
582
|
+
} from "@tscircuit/circuit-json-util";
|
|
583
|
+
import { gzipSync, strToU8 } from "fflate";
|
|
584
|
+
var hiddenSourceComponentKeys = /* @__PURE__ */ new Set([
|
|
585
|
+
"type",
|
|
586
|
+
"source_component_id",
|
|
587
|
+
"source_group_id",
|
|
588
|
+
"subcircuit_id",
|
|
589
|
+
"name",
|
|
590
|
+
"display_name",
|
|
591
|
+
"ftype",
|
|
592
|
+
"are_pins_interchangeable"
|
|
593
|
+
]);
|
|
594
|
+
var priorityKeys = [
|
|
595
|
+
"resistance",
|
|
596
|
+
"capacitance",
|
|
597
|
+
"inductance",
|
|
598
|
+
"frequency",
|
|
599
|
+
"voltage",
|
|
600
|
+
"current",
|
|
601
|
+
"max_voltage_rating",
|
|
602
|
+
"max_current_rating",
|
|
603
|
+
"power_rating",
|
|
604
|
+
"manufacturer_part_number",
|
|
605
|
+
"supplier_part_numbers"
|
|
606
|
+
];
|
|
607
|
+
var getKeyPriority = (key) => {
|
|
608
|
+
const priority = priorityKeys.indexOf(key);
|
|
609
|
+
return priority === -1 ? priorityKeys.length : priority;
|
|
709
610
|
};
|
|
710
|
-
var
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
611
|
+
var humanizeComponentField = (field) => field.replace(/^simple_/, "").replaceAll("_", " ").replace(/\b\w/g, (character) => character.toUpperCase());
|
|
612
|
+
var formatObjectValue = (value) => Object.entries(value).map(([key, nestedValue]) => {
|
|
613
|
+
const formattedNestedValue = Array.isArray(nestedValue) ? nestedValue.join(", ") : String(nestedValue);
|
|
614
|
+
return `${humanizeComponentField(key)}: ${formattedNestedValue}`;
|
|
615
|
+
}).join(" \xB7 ");
|
|
616
|
+
var formatComponentValue = (sourceComponent, key, value) => {
|
|
617
|
+
const displayValue = sourceComponent[`display_${key}`];
|
|
618
|
+
if (typeof displayValue === "string" && displayValue.trim()) {
|
|
619
|
+
return displayValue;
|
|
717
620
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
if (typeof
|
|
721
|
-
|
|
722
|
-
localStorage.setItem(key, JSON.stringify(value));
|
|
723
|
-
} catch {
|
|
621
|
+
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
622
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
623
|
+
if (value && typeof value === "object") {
|
|
624
|
+
return formatObjectValue(value);
|
|
724
625
|
}
|
|
626
|
+
return String(value);
|
|
725
627
|
};
|
|
726
|
-
var
|
|
727
|
-
if (
|
|
728
|
-
|
|
729
|
-
return
|
|
730
|
-
} catch {
|
|
731
|
-
return null;
|
|
628
|
+
var getSourceComponentInfoEntries = (sourceComponent) => Object.entries(sourceComponent).filter(([key, value]) => {
|
|
629
|
+
if (hiddenSourceComponentKeys.has(key)) return false;
|
|
630
|
+
if (key.startsWith("display_") && key.slice("display_".length) in sourceComponent) {
|
|
631
|
+
return false;
|
|
732
632
|
}
|
|
633
|
+
return value !== void 0 && value !== null && value !== "";
|
|
634
|
+
}).sort(([keyA], [keyB]) => {
|
|
635
|
+
const priorityDifference = getKeyPriority(keyA) - getKeyPriority(keyB);
|
|
636
|
+
return priorityDifference || keyA.localeCompare(keyB);
|
|
637
|
+
}).map(([key, value]) => ({
|
|
638
|
+
key,
|
|
639
|
+
label: humanizeComponentField(key),
|
|
640
|
+
value: formatComponentValue(sourceComponent, key, value)
|
|
641
|
+
}));
|
|
642
|
+
var getSchematicComponentDetails = (circuitJson, schematicComponentId) => {
|
|
643
|
+
const schematicComponent = circuitJson.find(
|
|
644
|
+
(element) => element.type === "schematic_component" && element.schematic_component_id === schematicComponentId
|
|
645
|
+
);
|
|
646
|
+
if (!schematicComponent) return null;
|
|
647
|
+
const sourceComponent = circuitJson.find(
|
|
648
|
+
(element) => element.type === "source_component" && element.source_component_id === schematicComponent.source_component_id
|
|
649
|
+
);
|
|
650
|
+
if (!sourceComponent) return null;
|
|
651
|
+
const pcbComponent = circuitJson.find(
|
|
652
|
+
(element) => element.type === "pcb_component" && element.source_component_id === sourceComponent.source_component_id
|
|
653
|
+
);
|
|
654
|
+
const cadComponent = circuitJson.find(
|
|
655
|
+
(element) => element.type === "cad_component" && element.source_component_id === sourceComponent.source_component_id && (!pcbComponent || element.pcb_component_id === pcbComponent.pcb_component_id)
|
|
656
|
+
);
|
|
657
|
+
const footprinterString = pcbComponent?.footprinter_string ?? cadComponent?.footprinter_string;
|
|
658
|
+
const footprintPreview = pcbComponent ? getPcbComponentPreview(circuitJson, pcbComponent.pcb_component_id) : null;
|
|
659
|
+
return {
|
|
660
|
+
schematicComponent,
|
|
661
|
+
sourceComponent,
|
|
662
|
+
pcbComponent,
|
|
663
|
+
footprinterString,
|
|
664
|
+
footprintPreviewCircuitJson: footprintPreview?.circuitJson,
|
|
665
|
+
footprintPreviewViewBox: footprintPreview?.viewBox
|
|
666
|
+
};
|
|
733
667
|
};
|
|
734
|
-
var
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
668
|
+
var PCB_PREVIEW_PADDING_MM = 2;
|
|
669
|
+
var getPcbComponentPreview = (circuitJson, pcbComponentId) => {
|
|
670
|
+
const pcbComponent = circuitJson.find(
|
|
671
|
+
(element) => element.type === "pcb_component" && element.pcb_component_id === pcbComponentId
|
|
672
|
+
);
|
|
673
|
+
if (!pcbComponent) return null;
|
|
674
|
+
const componentBounds = getPcbElementBounds(pcbComponent);
|
|
675
|
+
if (!componentBounds) return null;
|
|
676
|
+
const viewBox = {
|
|
677
|
+
minX: componentBounds.minX - PCB_PREVIEW_PADDING_MM,
|
|
678
|
+
minY: componentBounds.minY - PCB_PREVIEW_PADDING_MM,
|
|
679
|
+
maxX: componentBounds.maxX + PCB_PREVIEW_PADDING_MM,
|
|
680
|
+
maxY: componentBounds.maxY + PCB_PREVIEW_PADDING_MM
|
|
681
|
+
};
|
|
682
|
+
return {
|
|
683
|
+
circuitJson: getPcbElementsWithinBounds(circuitJson, viewBox),
|
|
684
|
+
viewBox
|
|
685
|
+
};
|
|
686
|
+
};
|
|
687
|
+
var bytesToBase64 = (bytes) => {
|
|
688
|
+
let binary = "";
|
|
689
|
+
const chunkSize = 8192;
|
|
690
|
+
for (let index = 0; index < bytes.length; index += chunkSize) {
|
|
691
|
+
binary += String.fromCharCode(...bytes.subarray(index, index + chunkSize));
|
|
739
692
|
}
|
|
693
|
+
return btoa(binary);
|
|
694
|
+
};
|
|
695
|
+
var getFootprintPreviewUrl = (footprintPreviewCircuitJson, viewBox) => {
|
|
696
|
+
const encodedCircuitJson = bytesToBase64(
|
|
697
|
+
gzipSync(strToU8(JSON.stringify(footprintPreviewCircuitJson)))
|
|
698
|
+
);
|
|
699
|
+
const url = new URL("https://svg.tscircuit.com/");
|
|
700
|
+
url.searchParams.set("svg_type", "pcb");
|
|
701
|
+
url.searchParams.set("circuit_json", encodedCircuitJson);
|
|
702
|
+
url.searchParams.set(
|
|
703
|
+
"viewbox",
|
|
704
|
+
[viewBox.minX, viewBox.minY, viewBox.maxX, viewBox.maxY].join(",")
|
|
705
|
+
);
|
|
706
|
+
url.searchParams.set("background_color", "#f8fafc");
|
|
707
|
+
return url.toString();
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// lib/utils/z-index-map.ts
|
|
711
|
+
var zIndexMap = {
|
|
712
|
+
contextMenu: 110,
|
|
713
|
+
viewMenu: 55,
|
|
714
|
+
viewMenuIcon: 48,
|
|
715
|
+
clickToInteractOverlay: 100,
|
|
716
|
+
schematicComponentDetailsTooltip: 105,
|
|
717
|
+
schematicComponentHoverOutline: 47,
|
|
718
|
+
schematicPortHoverOutline: 48
|
|
740
719
|
};
|
|
741
720
|
|
|
742
721
|
// lib/components/MouseTracker.tsx
|
|
743
722
|
import {
|
|
744
723
|
createContext,
|
|
745
|
-
useCallback as
|
|
724
|
+
useCallback as useCallback3,
|
|
746
725
|
useContext,
|
|
747
|
-
useEffect as
|
|
748
|
-
useMemo
|
|
749
|
-
useRef
|
|
726
|
+
useEffect as useEffect5,
|
|
727
|
+
useMemo,
|
|
728
|
+
useRef as useRef2
|
|
750
729
|
} from "react";
|
|
751
|
-
import { Fragment, jsx
|
|
730
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
752
731
|
var MouseTrackerContext = createContext(null);
|
|
753
732
|
var DRAG_THRESHOLD_PX = 5;
|
|
754
733
|
var boundsAreEqual = (a, b) => {
|
|
@@ -759,24 +738,24 @@ var boundsAreEqual = (a, b) => {
|
|
|
759
738
|
var MouseTracker = ({ children }) => {
|
|
760
739
|
const existingContext = useContext(MouseTrackerContext);
|
|
761
740
|
if (existingContext) {
|
|
762
|
-
return /* @__PURE__ */
|
|
741
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
763
742
|
}
|
|
764
|
-
return /* @__PURE__ */
|
|
743
|
+
return /* @__PURE__ */ jsx(MouseTrackerProvider, { children });
|
|
765
744
|
};
|
|
766
745
|
var MouseTrackerProvider = ({ children }) => {
|
|
767
|
-
const storeRef =
|
|
746
|
+
const storeRef = useRef2({
|
|
768
747
|
pointer: null,
|
|
769
748
|
boundingBoxes: /* @__PURE__ */ new Map(),
|
|
770
749
|
hoveringIds: /* @__PURE__ */ new Set(),
|
|
771
750
|
subscribers: /* @__PURE__ */ new Set(),
|
|
772
751
|
mouseDownPosition: null
|
|
773
752
|
});
|
|
774
|
-
const notifySubscribers =
|
|
753
|
+
const notifySubscribers = useCallback3(() => {
|
|
775
754
|
for (const callback of storeRef.current.subscribers) {
|
|
776
755
|
callback();
|
|
777
756
|
}
|
|
778
757
|
}, []);
|
|
779
|
-
const updateHovering =
|
|
758
|
+
const updateHovering = useCallback3(() => {
|
|
780
759
|
const pointer = storeRef.current.pointer;
|
|
781
760
|
const newHovering = /* @__PURE__ */ new Set();
|
|
782
761
|
if (pointer) {
|
|
@@ -795,14 +774,14 @@ var MouseTrackerProvider = ({ children }) => {
|
|
|
795
774
|
storeRef.current.hoveringIds = newHovering;
|
|
796
775
|
notifySubscribers();
|
|
797
776
|
}, [notifySubscribers]);
|
|
798
|
-
const registerBoundingBox =
|
|
777
|
+
const registerBoundingBox = useCallback3(
|
|
799
778
|
(id, registration) => {
|
|
800
779
|
storeRef.current.boundingBoxes.set(id, registration);
|
|
801
780
|
updateHovering();
|
|
802
781
|
},
|
|
803
782
|
[updateHovering]
|
|
804
783
|
);
|
|
805
|
-
const updateBoundingBox =
|
|
784
|
+
const updateBoundingBox = useCallback3(
|
|
806
785
|
(id, registration) => {
|
|
807
786
|
const existing = storeRef.current.boundingBoxes.get(id);
|
|
808
787
|
if (existing && boundsAreEqual(existing.bounds, registration.bounds) && existing.onClick === registration.onClick) {
|
|
@@ -813,7 +792,7 @@ var MouseTrackerProvider = ({ children }) => {
|
|
|
813
792
|
},
|
|
814
793
|
[updateHovering]
|
|
815
794
|
);
|
|
816
|
-
const unregisterBoundingBox =
|
|
795
|
+
const unregisterBoundingBox = useCallback3(
|
|
817
796
|
(id) => {
|
|
818
797
|
const removed = storeRef.current.boundingBoxes.delete(id);
|
|
819
798
|
if (removed) {
|
|
@@ -822,16 +801,16 @@ var MouseTrackerProvider = ({ children }) => {
|
|
|
822
801
|
},
|
|
823
802
|
[updateHovering]
|
|
824
803
|
);
|
|
825
|
-
const subscribe =
|
|
804
|
+
const subscribe = useCallback3((listener) => {
|
|
826
805
|
storeRef.current.subscribers.add(listener);
|
|
827
806
|
return () => {
|
|
828
807
|
storeRef.current.subscribers.delete(listener);
|
|
829
808
|
};
|
|
830
809
|
}, []);
|
|
831
|
-
const isHovering =
|
|
810
|
+
const isHovering = useCallback3((id) => {
|
|
832
811
|
return storeRef.current.hoveringIds.has(id);
|
|
833
812
|
}, []);
|
|
834
|
-
|
|
813
|
+
useEffect5(() => {
|
|
835
814
|
const handlePointerPosition = (event) => {
|
|
836
815
|
const { clientX, clientY } = event;
|
|
837
816
|
const pointer = storeRef.current.pointer;
|
|
@@ -899,7 +878,7 @@ var MouseTrackerProvider = ({ children }) => {
|
|
|
899
878
|
window.removeEventListener("click", handleClick);
|
|
900
879
|
};
|
|
901
880
|
}, [updateHovering]);
|
|
902
|
-
const value =
|
|
881
|
+
const value = useMemo(
|
|
903
882
|
() => ({
|
|
904
883
|
registerBoundingBox,
|
|
905
884
|
updateBoundingBox,
|
|
@@ -915,19 +894,222 @@ var MouseTrackerProvider = ({ children }) => {
|
|
|
915
894
|
isHovering
|
|
916
895
|
]
|
|
917
896
|
);
|
|
918
|
-
return /* @__PURE__ */
|
|
897
|
+
return /* @__PURE__ */ jsx(MouseTrackerContext.Provider, { value, children });
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
// lib/components/SchematicComponentDetailsTooltip.tsx
|
|
901
|
+
import { useMemo as useMemo2 } from "react";
|
|
902
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
903
|
+
var detailLabelStyle = {
|
|
904
|
+
color: "#64748b",
|
|
905
|
+
fontSize: "11px",
|
|
906
|
+
fontWeight: 700,
|
|
907
|
+
letterSpacing: "0.04em",
|
|
908
|
+
textTransform: "uppercase"
|
|
909
|
+
};
|
|
910
|
+
var SchematicComponentDetailsTooltip = ({
|
|
911
|
+
sourceComponent,
|
|
912
|
+
footprinterString,
|
|
913
|
+
footprintPreviewCircuitJson,
|
|
914
|
+
footprintPreviewViewBox,
|
|
915
|
+
left,
|
|
916
|
+
top,
|
|
917
|
+
width,
|
|
918
|
+
maxHeight,
|
|
919
|
+
onClose
|
|
920
|
+
}) => {
|
|
921
|
+
const infoEntries = useMemo2(
|
|
922
|
+
() => getSourceComponentInfoEntries(sourceComponent),
|
|
923
|
+
[sourceComponent]
|
|
924
|
+
);
|
|
925
|
+
const footprintPreviewUrl = useMemo2(
|
|
926
|
+
() => footprintPreviewCircuitJson?.length && footprintPreviewViewBox ? getFootprintPreviewUrl(
|
|
927
|
+
footprintPreviewCircuitJson,
|
|
928
|
+
footprintPreviewViewBox
|
|
929
|
+
) : void 0,
|
|
930
|
+
[footprintPreviewCircuitJson, footprintPreviewViewBox]
|
|
931
|
+
);
|
|
932
|
+
const componentType = sourceComponent.ftype ? humanizeComponentField(sourceComponent.ftype) : "Component";
|
|
933
|
+
return /* @__PURE__ */ jsxs(
|
|
934
|
+
"dialog",
|
|
935
|
+
{
|
|
936
|
+
open: true,
|
|
937
|
+
"aria-label": `${sourceComponent.name} component details`,
|
|
938
|
+
"data-schematic-component-details-tooltip": true,
|
|
939
|
+
style: {
|
|
940
|
+
position: "absolute",
|
|
941
|
+
left,
|
|
942
|
+
right: "auto",
|
|
943
|
+
top,
|
|
944
|
+
bottom: "auto",
|
|
945
|
+
width,
|
|
946
|
+
maxHeight,
|
|
947
|
+
margin: 0,
|
|
948
|
+
padding: 0,
|
|
949
|
+
overflowY: "auto",
|
|
950
|
+
boxSizing: "border-box",
|
|
951
|
+
border: "1px solid #cbd5e1",
|
|
952
|
+
borderRadius: "12px",
|
|
953
|
+
backgroundColor: "rgba(255, 255, 255, 0.98)",
|
|
954
|
+
boxShadow: "0 20px 25px -5px rgba(15, 23, 42, 0.18), 0 8px 10px -6px rgba(15, 23, 42, 0.12)",
|
|
955
|
+
color: "#0f172a",
|
|
956
|
+
fontFamily: 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
957
|
+
pointerEvents: "auto",
|
|
958
|
+
userSelect: "text",
|
|
959
|
+
WebkitUserSelect: "text",
|
|
960
|
+
zIndex: zIndexMap.schematicComponentDetailsTooltip
|
|
961
|
+
},
|
|
962
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
963
|
+
onClick: (event) => event.stopPropagation(),
|
|
964
|
+
children: [
|
|
965
|
+
/* @__PURE__ */ jsxs(
|
|
966
|
+
"div",
|
|
967
|
+
{
|
|
968
|
+
style: {
|
|
969
|
+
display: "flex",
|
|
970
|
+
alignItems: "flex-start",
|
|
971
|
+
justifyContent: "space-between",
|
|
972
|
+
gap: "16px",
|
|
973
|
+
padding: "18px 18px 14px",
|
|
974
|
+
borderBottom: "1px solid #e2e8f0"
|
|
975
|
+
},
|
|
976
|
+
children: [
|
|
977
|
+
/* @__PURE__ */ jsxs("div", { style: { minWidth: 0 }, children: [
|
|
978
|
+
/* @__PURE__ */ jsx2(
|
|
979
|
+
"div",
|
|
980
|
+
{
|
|
981
|
+
style: {
|
|
982
|
+
fontSize: "20px",
|
|
983
|
+
fontWeight: 750,
|
|
984
|
+
lineHeight: 1.2,
|
|
985
|
+
overflowWrap: "anywhere"
|
|
986
|
+
},
|
|
987
|
+
children: sourceComponent.display_name ?? sourceComponent.name
|
|
988
|
+
}
|
|
989
|
+
),
|
|
990
|
+
/* @__PURE__ */ jsx2("div", { style: { color: "#64748b", fontSize: "13px", marginTop: 4 }, children: componentType })
|
|
991
|
+
] }),
|
|
992
|
+
/* @__PURE__ */ jsx2(
|
|
993
|
+
"button",
|
|
994
|
+
{
|
|
995
|
+
type: "button",
|
|
996
|
+
"aria-label": "Close component details",
|
|
997
|
+
onClick: onClose,
|
|
998
|
+
style: {
|
|
999
|
+
display: "grid",
|
|
1000
|
+
placeItems: "center",
|
|
1001
|
+
flex: "0 0 auto",
|
|
1002
|
+
width: "30px",
|
|
1003
|
+
height: "30px",
|
|
1004
|
+
padding: 0,
|
|
1005
|
+
border: "1px solid #e2e8f0",
|
|
1006
|
+
borderRadius: "8px",
|
|
1007
|
+
background: "#f8fafc",
|
|
1008
|
+
color: "#475569",
|
|
1009
|
+
cursor: "pointer",
|
|
1010
|
+
fontSize: "20px",
|
|
1011
|
+
lineHeight: 1
|
|
1012
|
+
},
|
|
1013
|
+
children: "\xD7"
|
|
1014
|
+
}
|
|
1015
|
+
)
|
|
1016
|
+
]
|
|
1017
|
+
}
|
|
1018
|
+
),
|
|
1019
|
+
infoEntries.length > 0 && /* @__PURE__ */ jsx2(
|
|
1020
|
+
"dl",
|
|
1021
|
+
{
|
|
1022
|
+
style: {
|
|
1023
|
+
display: "grid",
|
|
1024
|
+
gridTemplateColumns: "minmax(110px, 0.8fr) minmax(0, 1.2fr)",
|
|
1025
|
+
gap: "10px 18px",
|
|
1026
|
+
margin: 0,
|
|
1027
|
+
padding: "16px 18px",
|
|
1028
|
+
borderBottom: footprinterString ? "1px solid #e2e8f0" : void 0
|
|
1029
|
+
},
|
|
1030
|
+
children: infoEntries.map((entry) => /* @__PURE__ */ jsxs("div", { style: { display: "contents" }, children: [
|
|
1031
|
+
/* @__PURE__ */ jsx2("dt", { style: detailLabelStyle, children: entry.label }),
|
|
1032
|
+
/* @__PURE__ */ jsx2(
|
|
1033
|
+
"dd",
|
|
1034
|
+
{
|
|
1035
|
+
style: {
|
|
1036
|
+
minWidth: 0,
|
|
1037
|
+
margin: 0,
|
|
1038
|
+
color: "#1e293b",
|
|
1039
|
+
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace',
|
|
1040
|
+
fontSize: "12px",
|
|
1041
|
+
lineHeight: 1.45,
|
|
1042
|
+
overflowWrap: "anywhere"
|
|
1043
|
+
},
|
|
1044
|
+
children: entry.value
|
|
1045
|
+
}
|
|
1046
|
+
)
|
|
1047
|
+
] }, entry.key))
|
|
1048
|
+
}
|
|
1049
|
+
),
|
|
1050
|
+
footprinterString && /* @__PURE__ */ jsxs("div", { style: { padding: "16px 18px 18px" }, children: [
|
|
1051
|
+
/* @__PURE__ */ jsx2("div", { style: { ...detailLabelStyle, marginBottom: 7 }, children: "Footprint" }),
|
|
1052
|
+
/* @__PURE__ */ jsx2(
|
|
1053
|
+
"code",
|
|
1054
|
+
{
|
|
1055
|
+
style: {
|
|
1056
|
+
display: "block",
|
|
1057
|
+
marginBottom: "12px",
|
|
1058
|
+
padding: "8px 10px",
|
|
1059
|
+
borderRadius: "7px",
|
|
1060
|
+
background: "#f1f5f9",
|
|
1061
|
+
color: "#334155",
|
|
1062
|
+
fontSize: "12px",
|
|
1063
|
+
lineHeight: 1.4,
|
|
1064
|
+
overflowWrap: "anywhere",
|
|
1065
|
+
whiteSpace: "pre-wrap"
|
|
1066
|
+
},
|
|
1067
|
+
children: footprinterString
|
|
1068
|
+
}
|
|
1069
|
+
),
|
|
1070
|
+
footprintPreviewUrl && /* @__PURE__ */ jsx2(
|
|
1071
|
+
"div",
|
|
1072
|
+
{
|
|
1073
|
+
style: {
|
|
1074
|
+
height: "210px",
|
|
1075
|
+
overflow: "hidden",
|
|
1076
|
+
border: "1px solid #e2e8f0",
|
|
1077
|
+
borderRadius: "9px",
|
|
1078
|
+
background: "#f8fafc"
|
|
1079
|
+
},
|
|
1080
|
+
children: /* @__PURE__ */ jsx2(
|
|
1081
|
+
"img",
|
|
1082
|
+
{
|
|
1083
|
+
src: footprintPreviewUrl,
|
|
1084
|
+
alt: `${sourceComponent.name} ${footprinterString} PCB footprint`,
|
|
1085
|
+
loading: "lazy",
|
|
1086
|
+
referrerPolicy: "no-referrer",
|
|
1087
|
+
style: {
|
|
1088
|
+
display: "block",
|
|
1089
|
+
width: "100%",
|
|
1090
|
+
height: "100%",
|
|
1091
|
+
objectFit: "contain"
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
)
|
|
1095
|
+
}
|
|
1096
|
+
)
|
|
1097
|
+
] })
|
|
1098
|
+
]
|
|
1099
|
+
}
|
|
1100
|
+
);
|
|
919
1101
|
};
|
|
920
1102
|
|
|
921
1103
|
// lib/components/SchematicComponentMouseTarget.tsx
|
|
922
|
-
import { useCallback as
|
|
1104
|
+
import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef4, useState as useState3 } from "react";
|
|
923
1105
|
|
|
924
1106
|
// lib/hooks/useMouseEventsOverBoundingBox.ts
|
|
925
1107
|
import {
|
|
926
1108
|
useContext as useContext2,
|
|
927
|
-
useEffect as
|
|
1109
|
+
useEffect as useEffect6,
|
|
928
1110
|
useId,
|
|
929
1111
|
useMemo as useMemo3,
|
|
930
|
-
useRef as
|
|
1112
|
+
useRef as useRef3,
|
|
931
1113
|
useSyncExternalStore
|
|
932
1114
|
} from "react";
|
|
933
1115
|
var useMouseEventsOverBoundingBox = (options) => {
|
|
@@ -938,7 +1120,7 @@ var useMouseEventsOverBoundingBox = (options) => {
|
|
|
938
1120
|
);
|
|
939
1121
|
}
|
|
940
1122
|
const id = useId();
|
|
941
|
-
const latestOptionsRef =
|
|
1123
|
+
const latestOptionsRef = useRef3(options);
|
|
942
1124
|
latestOptionsRef.current = options;
|
|
943
1125
|
const handleClick = useMemo3(
|
|
944
1126
|
() => (event) => {
|
|
@@ -946,7 +1128,7 @@ var useMouseEventsOverBoundingBox = (options) => {
|
|
|
946
1128
|
},
|
|
947
1129
|
[]
|
|
948
1130
|
);
|
|
949
|
-
|
|
1131
|
+
useEffect6(() => {
|
|
950
1132
|
context.registerBoundingBox(id, {
|
|
951
1133
|
bounds: latestOptionsRef.current.bounds,
|
|
952
1134
|
onClick: latestOptionsRef.current.onClick ? handleClick : void 0
|
|
@@ -955,7 +1137,7 @@ var useMouseEventsOverBoundingBox = (options) => {
|
|
|
955
1137
|
context.unregisterBoundingBox(id);
|
|
956
1138
|
};
|
|
957
1139
|
}, [context, handleClick, id]);
|
|
958
|
-
|
|
1140
|
+
useEffect6(() => {
|
|
959
1141
|
context.updateBoundingBox(id, {
|
|
960
1142
|
bounds: latestOptionsRef.current.bounds,
|
|
961
1143
|
onClick: latestOptionsRef.current.onClick ? handleClick : void 0
|
|
@@ -979,7 +1161,7 @@ var useMouseEventsOverBoundingBox = (options) => {
|
|
|
979
1161
|
};
|
|
980
1162
|
|
|
981
1163
|
// lib/components/SchematicComponentMouseTarget.tsx
|
|
982
|
-
import { jsx as
|
|
1164
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
983
1165
|
var areMeasurementsEqual = (a, b) => {
|
|
984
1166
|
if (!a && !b) return true;
|
|
985
1167
|
if (!a || !b) return false;
|
|
@@ -994,9 +1176,9 @@ var SchematicComponentMouseTarget = ({
|
|
|
994
1176
|
showOutline,
|
|
995
1177
|
circuitJsonKey
|
|
996
1178
|
}) => {
|
|
997
|
-
const [measurement, setMeasurement] =
|
|
998
|
-
const frameRef =
|
|
999
|
-
const measure =
|
|
1179
|
+
const [measurement, setMeasurement] = useState3(null);
|
|
1180
|
+
const frameRef = useRef4(null);
|
|
1181
|
+
const measure = useCallback4(() => {
|
|
1000
1182
|
frameRef.current = null;
|
|
1001
1183
|
const svgDiv = svgDivRef.current;
|
|
1002
1184
|
const container = containerRef.current;
|
|
@@ -1031,14 +1213,14 @@ var SchematicComponentMouseTarget = ({
|
|
|
1031
1213
|
(prev) => areMeasurementsEqual(prev, nextMeasurement) ? prev : nextMeasurement
|
|
1032
1214
|
);
|
|
1033
1215
|
}, [componentId, containerRef, svgDivRef]);
|
|
1034
|
-
const scheduleMeasure =
|
|
1216
|
+
const scheduleMeasure = useCallback4(() => {
|
|
1035
1217
|
if (frameRef.current !== null) return;
|
|
1036
1218
|
frameRef.current = window.requestAnimationFrame(measure);
|
|
1037
1219
|
}, [measure]);
|
|
1038
|
-
|
|
1220
|
+
useEffect7(() => {
|
|
1039
1221
|
scheduleMeasure();
|
|
1040
1222
|
}, [scheduleMeasure, circuitJsonKey]);
|
|
1041
|
-
|
|
1223
|
+
useEffect7(() => {
|
|
1042
1224
|
scheduleMeasure();
|
|
1043
1225
|
const svgDiv = svgDivRef.current;
|
|
1044
1226
|
const container = containerRef.current;
|
|
@@ -1070,7 +1252,7 @@ var SchematicComponentMouseTarget = ({
|
|
|
1070
1252
|
}
|
|
1071
1253
|
};
|
|
1072
1254
|
}, [scheduleMeasure, svgDivRef, containerRef]);
|
|
1073
|
-
const handleClick =
|
|
1255
|
+
const handleClick = useCallback4(
|
|
1074
1256
|
(event) => {
|
|
1075
1257
|
if (onComponentClick) {
|
|
1076
1258
|
onComponentClick(componentId, event);
|
|
@@ -1083,7 +1265,7 @@ var SchematicComponentMouseTarget = ({
|
|
|
1083
1265
|
bounds,
|
|
1084
1266
|
onClick: onComponentClick ? handleClick : void 0
|
|
1085
1267
|
});
|
|
1086
|
-
|
|
1268
|
+
useEffect7(() => {
|
|
1087
1269
|
if (onHoverChange) {
|
|
1088
1270
|
onHoverChange(componentId, hovering);
|
|
1089
1271
|
}
|
|
@@ -1092,7 +1274,7 @@ var SchematicComponentMouseTarget = ({
|
|
|
1092
1274
|
return null;
|
|
1093
1275
|
}
|
|
1094
1276
|
const rect = measurement.rect;
|
|
1095
|
-
return /* @__PURE__ */
|
|
1277
|
+
return /* @__PURE__ */ jsx3(
|
|
1096
1278
|
"div",
|
|
1097
1279
|
{
|
|
1098
1280
|
style: {
|
|
@@ -1110,8 +1292,8 @@ var SchematicComponentMouseTarget = ({
|
|
|
1110
1292
|
};
|
|
1111
1293
|
|
|
1112
1294
|
// lib/components/SchematicPortMouseTarget.tsx
|
|
1113
|
-
import { useCallback as
|
|
1114
|
-
import { Fragment as Fragment2, jsx as
|
|
1295
|
+
import { useCallback as useCallback5, useEffect as useEffect8, useRef as useRef5, useState as useState4 } from "react";
|
|
1296
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1115
1297
|
var areMeasurementsEqual2 = (a, b) => {
|
|
1116
1298
|
if (!a && !b) return true;
|
|
1117
1299
|
if (!a || !b) return false;
|
|
@@ -1127,9 +1309,9 @@ var SchematicPortMouseTarget = ({
|
|
|
1127
1309
|
showOutline,
|
|
1128
1310
|
circuitJsonKey
|
|
1129
1311
|
}) => {
|
|
1130
|
-
const [measurement, setMeasurement] =
|
|
1131
|
-
const frameRef =
|
|
1132
|
-
const measure =
|
|
1312
|
+
const [measurement, setMeasurement] = useState4(null);
|
|
1313
|
+
const frameRef = useRef5(null);
|
|
1314
|
+
const measure = useCallback5(() => {
|
|
1133
1315
|
frameRef.current = null;
|
|
1134
1316
|
const svgDiv = svgDivRef.current;
|
|
1135
1317
|
const container = containerRef.current;
|
|
@@ -1165,14 +1347,14 @@ var SchematicPortMouseTarget = ({
|
|
|
1165
1347
|
(prev) => areMeasurementsEqual2(prev, nextMeasurement) ? prev : nextMeasurement
|
|
1166
1348
|
);
|
|
1167
1349
|
}, [portId, containerRef, svgDivRef]);
|
|
1168
|
-
const scheduleMeasure =
|
|
1350
|
+
const scheduleMeasure = useCallback5(() => {
|
|
1169
1351
|
if (frameRef.current !== null) return;
|
|
1170
1352
|
frameRef.current = window.requestAnimationFrame(measure);
|
|
1171
1353
|
}, [measure]);
|
|
1172
|
-
|
|
1354
|
+
useEffect8(() => {
|
|
1173
1355
|
scheduleMeasure();
|
|
1174
1356
|
}, [scheduleMeasure, circuitJsonKey]);
|
|
1175
|
-
|
|
1357
|
+
useEffect8(() => {
|
|
1176
1358
|
scheduleMeasure();
|
|
1177
1359
|
const svgDiv = svgDivRef.current;
|
|
1178
1360
|
const container = containerRef.current;
|
|
@@ -1204,7 +1386,7 @@ var SchematicPortMouseTarget = ({
|
|
|
1204
1386
|
}
|
|
1205
1387
|
};
|
|
1206
1388
|
}, [scheduleMeasure, svgDivRef, containerRef]);
|
|
1207
|
-
const handleClick =
|
|
1389
|
+
const handleClick = useCallback5(
|
|
1208
1390
|
(event) => {
|
|
1209
1391
|
if (onPortClick) {
|
|
1210
1392
|
onPortClick(portId, event);
|
|
@@ -1217,7 +1399,7 @@ var SchematicPortMouseTarget = ({
|
|
|
1217
1399
|
bounds,
|
|
1218
1400
|
onClick: onPortClick ? handleClick : void 0
|
|
1219
1401
|
});
|
|
1220
|
-
|
|
1402
|
+
useEffect8(() => {
|
|
1221
1403
|
if (onHoverChange) {
|
|
1222
1404
|
onHoverChange(portId, hovering);
|
|
1223
1405
|
}
|
|
@@ -1226,8 +1408,8 @@ var SchematicPortMouseTarget = ({
|
|
|
1226
1408
|
return null;
|
|
1227
1409
|
}
|
|
1228
1410
|
const rect = measurement.rect;
|
|
1229
|
-
return /* @__PURE__ */
|
|
1230
|
-
/* @__PURE__ */
|
|
1411
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1412
|
+
/* @__PURE__ */ jsx4(
|
|
1231
1413
|
"div",
|
|
1232
1414
|
{
|
|
1233
1415
|
style: {
|
|
@@ -1245,7 +1427,7 @@ var SchematicPortMouseTarget = ({
|
|
|
1245
1427
|
}
|
|
1246
1428
|
}
|
|
1247
1429
|
),
|
|
1248
|
-
hovering && portLabel && /* @__PURE__ */
|
|
1430
|
+
hovering && portLabel && /* @__PURE__ */ jsx4(
|
|
1249
1431
|
"div",
|
|
1250
1432
|
{
|
|
1251
1433
|
style: {
|
|
@@ -1270,11 +1452,11 @@ var SchematicPortMouseTarget = ({
|
|
|
1270
1452
|
};
|
|
1271
1453
|
|
|
1272
1454
|
// lib/components/SchematicSheetSelector.tsx
|
|
1273
|
-
import { useState as
|
|
1274
|
-
import * as
|
|
1275
|
-
import { Fragment as Fragment3, jsx as
|
|
1276
|
-
var
|
|
1277
|
-
var
|
|
1455
|
+
import { useState as useState5 } from "react";
|
|
1456
|
+
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
|
1457
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1458
|
+
var FONT_FAMILY = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
1459
|
+
var contentStyles = {
|
|
1278
1460
|
backgroundColor: "#ffffff",
|
|
1279
1461
|
color: "#111111",
|
|
1280
1462
|
borderRadius: 8,
|
|
@@ -1286,11 +1468,11 @@ var contentStyles2 = {
|
|
|
1286
1468
|
maxHeight: 320,
|
|
1287
1469
|
overflowY: "auto",
|
|
1288
1470
|
fontSize: 13,
|
|
1289
|
-
fontFamily:
|
|
1471
|
+
fontFamily: FONT_FAMILY,
|
|
1290
1472
|
outline: "none",
|
|
1291
1473
|
zIndex: zIndexMap.viewMenu
|
|
1292
1474
|
};
|
|
1293
|
-
var
|
|
1475
|
+
var itemStyles = {
|
|
1294
1476
|
display: "flex",
|
|
1295
1477
|
alignItems: "center",
|
|
1296
1478
|
gap: 8,
|
|
@@ -1301,9 +1483,9 @@ var itemStyles2 = {
|
|
|
1301
1483
|
userSelect: "none",
|
|
1302
1484
|
color: "#111111",
|
|
1303
1485
|
fontSize: 13,
|
|
1304
|
-
fontFamily:
|
|
1486
|
+
fontFamily: FONT_FAMILY
|
|
1305
1487
|
};
|
|
1306
|
-
var
|
|
1488
|
+
var iconSlotStyles = {
|
|
1307
1489
|
width: 16,
|
|
1308
1490
|
height: 16,
|
|
1309
1491
|
flexShrink: 0,
|
|
@@ -1322,7 +1504,7 @@ var MENU_CSS = `
|
|
|
1322
1504
|
.sv-sheet-chevron { transition: transform 0.2s ease; }
|
|
1323
1505
|
[data-state="open"] > .sv-sheet-chevron { transform: rotate(180deg); }
|
|
1324
1506
|
`;
|
|
1325
|
-
var
|
|
1507
|
+
var CheckIcon = () => /* @__PURE__ */ jsx5(
|
|
1326
1508
|
"svg",
|
|
1327
1509
|
{
|
|
1328
1510
|
width: "14",
|
|
@@ -1334,10 +1516,10 @@ var CheckIcon2 = () => /* @__PURE__ */ jsx6(
|
|
|
1334
1516
|
strokeLinecap: "round",
|
|
1335
1517
|
strokeLinejoin: "round",
|
|
1336
1518
|
"aria-hidden": "true",
|
|
1337
|
-
children: /* @__PURE__ */
|
|
1519
|
+
children: /* @__PURE__ */ jsx5("path", { d: "M20 6 9 17l-5-5" })
|
|
1338
1520
|
}
|
|
1339
1521
|
);
|
|
1340
|
-
var ChevronDownIcon = ({ className }) => /* @__PURE__ */
|
|
1522
|
+
var ChevronDownIcon = ({ className }) => /* @__PURE__ */ jsx5(
|
|
1341
1523
|
"svg",
|
|
1342
1524
|
{
|
|
1343
1525
|
className,
|
|
@@ -1351,7 +1533,7 @@ var ChevronDownIcon = ({ className }) => /* @__PURE__ */ jsx6(
|
|
|
1351
1533
|
strokeLinejoin: "round",
|
|
1352
1534
|
style: { opacity: 0.6, flexShrink: 0 },
|
|
1353
1535
|
"aria-hidden": "true",
|
|
1354
|
-
children: /* @__PURE__ */
|
|
1536
|
+
children: /* @__PURE__ */ jsx5("path", { d: "m6 9 6 6 6-6" })
|
|
1355
1537
|
}
|
|
1356
1538
|
);
|
|
1357
1539
|
var SchematicSheetSelector = ({
|
|
@@ -1359,16 +1541,16 @@ var SchematicSheetSelector = ({
|
|
|
1359
1541
|
selectedSheetId,
|
|
1360
1542
|
onSelectSheet
|
|
1361
1543
|
}) => {
|
|
1362
|
-
const [open, setOpen] =
|
|
1544
|
+
const [open, setOpen] = useState5(false);
|
|
1363
1545
|
if (sheets.length <= 1) return null;
|
|
1364
1546
|
const selectedSheet = sheets.find(
|
|
1365
1547
|
(s) => s.schematic_sheet_id === selectedSheetId
|
|
1366
1548
|
);
|
|
1367
1549
|
const selectedLabel = selectedSheet?.name ?? "Select sheet";
|
|
1368
|
-
return /* @__PURE__ */
|
|
1369
|
-
/* @__PURE__ */
|
|
1370
|
-
/* @__PURE__ */
|
|
1371
|
-
/* @__PURE__ */
|
|
1550
|
+
return /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1551
|
+
/* @__PURE__ */ jsx5("style", { children: MENU_CSS }),
|
|
1552
|
+
/* @__PURE__ */ jsxs3(DropdownMenu.Root, { open, onOpenChange: setOpen, modal: false, children: [
|
|
1553
|
+
/* @__PURE__ */ jsx5(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
|
|
1372
1554
|
"button",
|
|
1373
1555
|
{
|
|
1374
1556
|
type: "button",
|
|
@@ -1391,31 +1573,31 @@ var SchematicSheetSelector = ({
|
|
|
1391
1573
|
cursor: "pointer",
|
|
1392
1574
|
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
|
1393
1575
|
fontSize: "13px",
|
|
1394
|
-
fontFamily:
|
|
1576
|
+
fontFamily: FONT_FAMILY,
|
|
1395
1577
|
zIndex: zIndexMap.viewMenuIcon
|
|
1396
1578
|
},
|
|
1397
1579
|
children: [
|
|
1398
|
-
/* @__PURE__ */
|
|
1399
|
-
/* @__PURE__ */
|
|
1400
|
-
/* @__PURE__ */
|
|
1580
|
+
/* @__PURE__ */ jsx5("span", { style: { color: "#888888", flexShrink: 0 }, children: "Sheet:" }),
|
|
1581
|
+
/* @__PURE__ */ jsx5("span", { style: { ...ellipsisStyles, minWidth: 0 }, children: selectedLabel }),
|
|
1582
|
+
/* @__PURE__ */ jsx5(ChevronDownIcon, { className: "sv-sheet-chevron" })
|
|
1401
1583
|
]
|
|
1402
1584
|
}
|
|
1403
1585
|
) }),
|
|
1404
|
-
/* @__PURE__ */
|
|
1405
|
-
|
|
1586
|
+
/* @__PURE__ */ jsx5(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx5(
|
|
1587
|
+
DropdownMenu.Content,
|
|
1406
1588
|
{
|
|
1407
|
-
style:
|
|
1589
|
+
style: contentStyles,
|
|
1408
1590
|
side: "bottom",
|
|
1409
1591
|
align: "start",
|
|
1410
1592
|
sideOffset: 8,
|
|
1411
1593
|
collisionPadding: 10,
|
|
1412
1594
|
children: sheets.map((sheet) => {
|
|
1413
1595
|
const selected = sheet.schematic_sheet_id === selectedSheetId;
|
|
1414
|
-
return /* @__PURE__ */
|
|
1415
|
-
|
|
1596
|
+
return /* @__PURE__ */ jsxs3(
|
|
1597
|
+
DropdownMenu.Item,
|
|
1416
1598
|
{
|
|
1417
1599
|
className: "sv-sheet-item",
|
|
1418
|
-
style:
|
|
1600
|
+
style: itemStyles,
|
|
1419
1601
|
title: sheet.name,
|
|
1420
1602
|
onSelect: (e) => e.preventDefault(),
|
|
1421
1603
|
onPointerUp: () => {
|
|
@@ -1423,8 +1605,8 @@ var SchematicSheetSelector = ({
|
|
|
1423
1605
|
setOpen(false);
|
|
1424
1606
|
},
|
|
1425
1607
|
children: [
|
|
1426
|
-
/* @__PURE__ */
|
|
1427
|
-
/* @__PURE__ */
|
|
1608
|
+
/* @__PURE__ */ jsx5("span", { style: iconSlotStyles, children: selected && /* @__PURE__ */ jsx5(CheckIcon, {}) }),
|
|
1609
|
+
/* @__PURE__ */ jsx5("span", { style: { ...ellipsisStyles, minWidth: 0 }, children: sheet.name })
|
|
1428
1610
|
]
|
|
1429
1611
|
},
|
|
1430
1612
|
sheet.schematic_sheet_id
|
|
@@ -1436,6 +1618,275 @@ var SchematicSheetSelector = ({
|
|
|
1436
1618
|
] });
|
|
1437
1619
|
};
|
|
1438
1620
|
|
|
1621
|
+
// lib/components/ViewMenu.tsx
|
|
1622
|
+
import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
|
|
1623
|
+
import { su as su3 } from "@tscircuit/soup-util";
|
|
1624
|
+
import { useMemo as useMemo4 } from "react";
|
|
1625
|
+
|
|
1626
|
+
// package.json
|
|
1627
|
+
var package_default = {
|
|
1628
|
+
name: "@tscircuit/schematic-viewer",
|
|
1629
|
+
version: "2.0.79",
|
|
1630
|
+
main: "dist/index.js",
|
|
1631
|
+
type: "module",
|
|
1632
|
+
scripts: {
|
|
1633
|
+
start: "cosmos",
|
|
1634
|
+
build: "tsup-node ./lib/index.ts --dts --format esm --sourcemap",
|
|
1635
|
+
"build:site": "cosmos-export",
|
|
1636
|
+
"vercel-build": "bun run build:site",
|
|
1637
|
+
format: "biome format --write .",
|
|
1638
|
+
"format:check": "biome format ."
|
|
1639
|
+
},
|
|
1640
|
+
files: [
|
|
1641
|
+
"dist"
|
|
1642
|
+
],
|
|
1643
|
+
devDependencies: {
|
|
1644
|
+
"@biomejs/biome": "^1.9.4",
|
|
1645
|
+
"@types/bun": "latest",
|
|
1646
|
+
"@types/debug": "^4.1.12",
|
|
1647
|
+
"@types/jsdom": "^21.1.7",
|
|
1648
|
+
"@types/react": "^19.0.1",
|
|
1649
|
+
"@types/react-dom": "^19.0.2",
|
|
1650
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
1651
|
+
jsdom: "^26.0.0",
|
|
1652
|
+
react: "^19.1.0",
|
|
1653
|
+
"react-cosmos": "^6.2.1",
|
|
1654
|
+
"react-cosmos-plugin-vite": "^6.2.0",
|
|
1655
|
+
"react-dom": "^19.1.0",
|
|
1656
|
+
"react-reconciler": "^0.31.0",
|
|
1657
|
+
semver: "^7.7.2",
|
|
1658
|
+
tscircuit: "^0.0.2112",
|
|
1659
|
+
tsup: "^8.3.5",
|
|
1660
|
+
vite: "^6.0.3"
|
|
1661
|
+
},
|
|
1662
|
+
peerDependencies: {
|
|
1663
|
+
typescript: "^5.0.0",
|
|
1664
|
+
tscircuit: "*"
|
|
1665
|
+
},
|
|
1666
|
+
dependencies: {
|
|
1667
|
+
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
|
1668
|
+
"@tscircuit/circuit-json-util": "^0.0.108",
|
|
1669
|
+
"circuit-json": "^0.0.465",
|
|
1670
|
+
"circuit-to-svg": "^0.0.393",
|
|
1671
|
+
debug: "^4.4.0",
|
|
1672
|
+
fflate: "^0.8.3",
|
|
1673
|
+
"performance-now": "^2.1.0",
|
|
1674
|
+
"use-mouse-matrix-transform": "^1.2.2"
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
|
|
1678
|
+
// lib/components/ViewMenu.tsx
|
|
1679
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1680
|
+
var FONT_FAMILY2 = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
1681
|
+
var contentStyles2 = {
|
|
1682
|
+
backgroundColor: "#262626",
|
|
1683
|
+
color: "#fafafa",
|
|
1684
|
+
borderRadius: 6,
|
|
1685
|
+
boxShadow: "0px 12px 48px -12px rgba(0, 0, 0, 0.5), 0px 8px 24px -8px rgba(0, 0, 0, 0.3)",
|
|
1686
|
+
border: "1px solid #333333",
|
|
1687
|
+
padding: 4,
|
|
1688
|
+
minWidth: 208,
|
|
1689
|
+
fontSize: 14,
|
|
1690
|
+
fontWeight: 400,
|
|
1691
|
+
fontFamily: FONT_FAMILY2,
|
|
1692
|
+
outline: "none",
|
|
1693
|
+
zIndex: zIndexMap.contextMenu
|
|
1694
|
+
};
|
|
1695
|
+
var itemStyles2 = {
|
|
1696
|
+
display: "flex",
|
|
1697
|
+
alignItems: "center",
|
|
1698
|
+
gap: 8,
|
|
1699
|
+
padding: "6px 8px",
|
|
1700
|
+
borderRadius: 6,
|
|
1701
|
+
cursor: "default",
|
|
1702
|
+
outline: "none",
|
|
1703
|
+
userSelect: "none",
|
|
1704
|
+
color: "#fafafa",
|
|
1705
|
+
fontSize: 14,
|
|
1706
|
+
fontWeight: 400,
|
|
1707
|
+
fontFamily: FONT_FAMILY2
|
|
1708
|
+
};
|
|
1709
|
+
var iconSlotStyles2 = {
|
|
1710
|
+
width: 16,
|
|
1711
|
+
height: 16,
|
|
1712
|
+
flexShrink: 0,
|
|
1713
|
+
display: "inline-flex",
|
|
1714
|
+
alignItems: "center",
|
|
1715
|
+
justifyContent: "center",
|
|
1716
|
+
color: "#fafafa"
|
|
1717
|
+
};
|
|
1718
|
+
var separatorStyles = {
|
|
1719
|
+
height: 1,
|
|
1720
|
+
backgroundColor: "#ffffff1a",
|
|
1721
|
+
margin: "4px 0"
|
|
1722
|
+
};
|
|
1723
|
+
var HIGHLIGHT_CSS = `
|
|
1724
|
+
.sv-vm-item[data-highlighted]:not([data-disabled]),
|
|
1725
|
+
.sv-vm-item:hover:not([data-disabled]) { background-color: #404040; }
|
|
1726
|
+
.sv-vm-item[data-disabled] { opacity: 0.45; cursor: not-allowed; }
|
|
1727
|
+
`;
|
|
1728
|
+
var CheckIcon2 = () => /* @__PURE__ */ jsx6(
|
|
1729
|
+
"svg",
|
|
1730
|
+
{
|
|
1731
|
+
width: "14",
|
|
1732
|
+
height: "14",
|
|
1733
|
+
viewBox: "0 0 24 24",
|
|
1734
|
+
fill: "none",
|
|
1735
|
+
stroke: "currentColor",
|
|
1736
|
+
strokeWidth: "2.5",
|
|
1737
|
+
strokeLinecap: "round",
|
|
1738
|
+
strokeLinejoin: "round",
|
|
1739
|
+
"aria-hidden": "true",
|
|
1740
|
+
children: /* @__PURE__ */ jsx6("path", { d: "M20 6 9 17l-5-5" })
|
|
1741
|
+
}
|
|
1742
|
+
);
|
|
1743
|
+
var ViewMenu = ({
|
|
1744
|
+
circuitJson,
|
|
1745
|
+
circuitJsonKey,
|
|
1746
|
+
menuRef,
|
|
1747
|
+
menuPos,
|
|
1748
|
+
onOpenChange,
|
|
1749
|
+
showGroups,
|
|
1750
|
+
onToggleGroups,
|
|
1751
|
+
showGrid,
|
|
1752
|
+
onToggleGrid,
|
|
1753
|
+
showPorts,
|
|
1754
|
+
onTogglePorts
|
|
1755
|
+
}) => {
|
|
1756
|
+
const hasGroups = useMemo4(() => {
|
|
1757
|
+
if (!circuitJson || circuitJson.length === 0) return false;
|
|
1758
|
+
try {
|
|
1759
|
+
const sourceGroups = su3(circuitJson).source_group?.list() || [];
|
|
1760
|
+
if (sourceGroups.length > 0) return true;
|
|
1761
|
+
const schematicComponents = su3(circuitJson).schematic_component?.list() || [];
|
|
1762
|
+
if (schematicComponents.length > 1) {
|
|
1763
|
+
const componentTypes = /* @__PURE__ */ new Set();
|
|
1764
|
+
for (const comp of schematicComponents) {
|
|
1765
|
+
const sourceComp = su3(circuitJson).source_component.get(
|
|
1766
|
+
comp.source_component_id
|
|
1767
|
+
);
|
|
1768
|
+
if (sourceComp?.ftype) {
|
|
1769
|
+
componentTypes.add(sourceComp.ftype);
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
return componentTypes.size > 1;
|
|
1773
|
+
}
|
|
1774
|
+
return false;
|
|
1775
|
+
} catch (error) {
|
|
1776
|
+
console.error("Error checking for groups:", error);
|
|
1777
|
+
return false;
|
|
1778
|
+
}
|
|
1779
|
+
}, [circuitJsonKey]);
|
|
1780
|
+
const hasPorts = useMemo4(() => {
|
|
1781
|
+
if (!circuitJson || circuitJson.length === 0) return false;
|
|
1782
|
+
try {
|
|
1783
|
+
return (su3(circuitJson).schematic_port?.list() || []).length > 0;
|
|
1784
|
+
} catch (error) {
|
|
1785
|
+
console.error("Error checking for schematic ports:", error);
|
|
1786
|
+
return false;
|
|
1787
|
+
}
|
|
1788
|
+
}, [circuitJsonKey]);
|
|
1789
|
+
return /* @__PURE__ */ jsx6(
|
|
1790
|
+
"div",
|
|
1791
|
+
{
|
|
1792
|
+
ref: menuRef,
|
|
1793
|
+
style: {
|
|
1794
|
+
position: "fixed",
|
|
1795
|
+
left: menuPos.x,
|
|
1796
|
+
top: menuPos.y,
|
|
1797
|
+
width: 0,
|
|
1798
|
+
height: 0
|
|
1799
|
+
},
|
|
1800
|
+
children: /* @__PURE__ */ jsxs4(DropdownMenu2.Root, { open: true, onOpenChange, modal: false, children: [
|
|
1801
|
+
/* @__PURE__ */ jsx6(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx6("div", { style: { position: "absolute", width: 1, height: 1 } }) }),
|
|
1802
|
+
/* @__PURE__ */ jsx6(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs4(
|
|
1803
|
+
DropdownMenu2.Content,
|
|
1804
|
+
{
|
|
1805
|
+
style: contentStyles2,
|
|
1806
|
+
align: "start",
|
|
1807
|
+
sideOffset: 0,
|
|
1808
|
+
collisionPadding: 10,
|
|
1809
|
+
avoidCollisions: true,
|
|
1810
|
+
children: [
|
|
1811
|
+
/* @__PURE__ */ jsx6("style", { children: HIGHLIGHT_CSS }),
|
|
1812
|
+
/* @__PURE__ */ jsxs4(
|
|
1813
|
+
DropdownMenu2.Item,
|
|
1814
|
+
{
|
|
1815
|
+
className: "sv-vm-item",
|
|
1816
|
+
style: itemStyles2,
|
|
1817
|
+
disabled: !hasPorts,
|
|
1818
|
+
title: hasPorts ? void 0 : "No ports found in this schematic",
|
|
1819
|
+
onSelect: (event) => event.preventDefault(),
|
|
1820
|
+
onPointerDown: (event) => {
|
|
1821
|
+
event.preventDefault();
|
|
1822
|
+
if (hasPorts) onTogglePorts(!showPorts);
|
|
1823
|
+
},
|
|
1824
|
+
children: [
|
|
1825
|
+
/* @__PURE__ */ jsx6("span", { style: iconSlotStyles2, children: showPorts && /* @__PURE__ */ jsx6(CheckIcon2, {}) }),
|
|
1826
|
+
/* @__PURE__ */ jsx6("span", { children: "Show Schematic Ports" })
|
|
1827
|
+
]
|
|
1828
|
+
}
|
|
1829
|
+
),
|
|
1830
|
+
/* @__PURE__ */ jsxs4(
|
|
1831
|
+
DropdownMenu2.Item,
|
|
1832
|
+
{
|
|
1833
|
+
className: "sv-vm-item",
|
|
1834
|
+
style: itemStyles2,
|
|
1835
|
+
disabled: !hasGroups,
|
|
1836
|
+
title: hasGroups ? void 0 : "No groups found in this schematic",
|
|
1837
|
+
onSelect: (event) => event.preventDefault(),
|
|
1838
|
+
onPointerDown: (event) => {
|
|
1839
|
+
event.preventDefault();
|
|
1840
|
+
if (hasGroups) onToggleGroups(!showGroups);
|
|
1841
|
+
},
|
|
1842
|
+
children: [
|
|
1843
|
+
/* @__PURE__ */ jsx6("span", { style: iconSlotStyles2, children: showGroups && /* @__PURE__ */ jsx6(CheckIcon2, {}) }),
|
|
1844
|
+
/* @__PURE__ */ jsx6("span", { children: "View Schematic Groups" })
|
|
1845
|
+
]
|
|
1846
|
+
}
|
|
1847
|
+
),
|
|
1848
|
+
/* @__PURE__ */ jsxs4(
|
|
1849
|
+
DropdownMenu2.Item,
|
|
1850
|
+
{
|
|
1851
|
+
className: "sv-vm-item",
|
|
1852
|
+
style: itemStyles2,
|
|
1853
|
+
onSelect: (event) => event.preventDefault(),
|
|
1854
|
+
onPointerDown: (event) => {
|
|
1855
|
+
event.preventDefault();
|
|
1856
|
+
onToggleGrid(!showGrid);
|
|
1857
|
+
},
|
|
1858
|
+
children: [
|
|
1859
|
+
/* @__PURE__ */ jsx6("span", { style: iconSlotStyles2, children: showGrid && /* @__PURE__ */ jsx6(CheckIcon2, {}) }),
|
|
1860
|
+
/* @__PURE__ */ jsx6("span", { children: "Show Grid" })
|
|
1861
|
+
]
|
|
1862
|
+
}
|
|
1863
|
+
),
|
|
1864
|
+
/* @__PURE__ */ jsx6(DropdownMenu2.Separator, { style: separatorStyles }),
|
|
1865
|
+
/* @__PURE__ */ jsxs4(
|
|
1866
|
+
"div",
|
|
1867
|
+
{
|
|
1868
|
+
style: {
|
|
1869
|
+
padding: "4px 8px 4px 32px",
|
|
1870
|
+
fontSize: 11,
|
|
1871
|
+
opacity: 0.35,
|
|
1872
|
+
color: "#a1a1aa",
|
|
1873
|
+
letterSpacing: "0.2px",
|
|
1874
|
+
fontFamily: FONT_FAMILY2
|
|
1875
|
+
},
|
|
1876
|
+
children: [
|
|
1877
|
+
"@tscircuit/schematic-viewer@",
|
|
1878
|
+
String(package_default?.version)
|
|
1879
|
+
]
|
|
1880
|
+
}
|
|
1881
|
+
)
|
|
1882
|
+
]
|
|
1883
|
+
}
|
|
1884
|
+
) })
|
|
1885
|
+
] })
|
|
1886
|
+
}
|
|
1887
|
+
);
|
|
1888
|
+
};
|
|
1889
|
+
|
|
1439
1890
|
// lib/components/SchematicViewer.tsx
|
|
1440
1891
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1441
1892
|
var SchematicViewer = ({
|
|
@@ -1448,7 +1899,7 @@ var SchematicViewer = ({
|
|
|
1448
1899
|
disableGroups = false,
|
|
1449
1900
|
netHoverHighlightEnabled = true,
|
|
1450
1901
|
onSchematicComponentClicked,
|
|
1451
|
-
showSchematicPorts
|
|
1902
|
+
showSchematicPorts,
|
|
1452
1903
|
onSchematicPortClicked,
|
|
1453
1904
|
onSchematicSheetChange,
|
|
1454
1905
|
css,
|
|
@@ -1460,11 +1911,11 @@ var SchematicViewer = ({
|
|
|
1460
1911
|
const getCircuitHash = (circuitJson2) => {
|
|
1461
1912
|
return `${circuitJson2?.length || 0}_${circuitJson2?.editCount || 0}`;
|
|
1462
1913
|
};
|
|
1463
|
-
const circuitJsonKey =
|
|
1914
|
+
const circuitJsonKey = useMemo5(
|
|
1464
1915
|
() => getCircuitHash(circuitJson),
|
|
1465
1916
|
[circuitJson]
|
|
1466
1917
|
);
|
|
1467
|
-
const schematicSheets =
|
|
1918
|
+
const schematicSheets = useMemo5(() => {
|
|
1468
1919
|
try {
|
|
1469
1920
|
return circuitJson.filter((elm) => elm?.type === "schematic_sheet").slice().sort((a, b) => (a.sheet_index ?? 0) - (b.sheet_index ?? 0));
|
|
1470
1921
|
} catch (err) {
|
|
@@ -1474,7 +1925,7 @@ var SchematicViewer = ({
|
|
|
1474
1925
|
}, [circuitJsonKey]);
|
|
1475
1926
|
const hasMultipleSheets = schematicSheets.length > 1;
|
|
1476
1927
|
const defaultSheetId = schematicSheets[0]?.schematic_sheet_id;
|
|
1477
|
-
const [selectedSheetId, setSelectedSheetId] =
|
|
1928
|
+
const [selectedSheetId, setSelectedSheetId] = useState6(
|
|
1478
1929
|
() => {
|
|
1479
1930
|
const stored = getStoredString(STORAGE_KEYS.SELECTED_SCHEMATIC_SHEET);
|
|
1480
1931
|
if (stored && schematicSheets.some((s) => s.schematic_sheet_id === stored)) {
|
|
@@ -1483,14 +1934,14 @@ var SchematicViewer = ({
|
|
|
1483
1934
|
return defaultSheetId;
|
|
1484
1935
|
}
|
|
1485
1936
|
);
|
|
1486
|
-
|
|
1937
|
+
useEffect9(() => {
|
|
1487
1938
|
const stillExists = selectedSheetId !== void 0 && schematicSheets.some((s) => s.schematic_sheet_id === selectedSheetId);
|
|
1488
1939
|
if (!stillExists) {
|
|
1489
1940
|
setSelectedSheetId(defaultSheetId);
|
|
1490
1941
|
}
|
|
1491
1942
|
}, [circuitJsonKey]);
|
|
1492
1943
|
const activeSheetId = hasMultipleSheets ? selectedSheetId ?? defaultSheetId : void 0;
|
|
1493
|
-
const handleSelectSheet =
|
|
1944
|
+
const handleSelectSheet = useCallback6(
|
|
1494
1945
|
(sheetId) => {
|
|
1495
1946
|
setSelectedSheetId(sheetId);
|
|
1496
1947
|
setStoredString(STORAGE_KEYS.SELECTED_SCHEMATIC_SHEET, sheetId);
|
|
@@ -1498,19 +1949,27 @@ var SchematicViewer = ({
|
|
|
1498
1949
|
},
|
|
1499
1950
|
[onSchematicSheetChange]
|
|
1500
1951
|
);
|
|
1501
|
-
const [showGridInternal, setShowGridInternal] =
|
|
1952
|
+
const [showGridInternal, setShowGridInternal] = useState6(false);
|
|
1502
1953
|
const showGrid = debugGrid || showGridInternal;
|
|
1503
|
-
const [isInteractionEnabled, setIsInteractionEnabled] =
|
|
1954
|
+
const [isInteractionEnabled, setIsInteractionEnabled] = useState6(
|
|
1504
1955
|
!clickToInteractEnabled
|
|
1505
1956
|
);
|
|
1506
|
-
const [
|
|
1507
|
-
const [showSchematicGroups, setShowSchematicGroups] = useState5(() => {
|
|
1957
|
+
const [showSchematicGroups, setShowSchematicGroups] = useState6(() => {
|
|
1508
1958
|
if (disableGroups) return false;
|
|
1509
|
-
return getStoredBoolean(
|
|
1959
|
+
return getStoredBoolean(STORAGE_KEYS.IS_SHOWING_SCHEMATIC_GROUPS, false);
|
|
1510
1960
|
});
|
|
1511
|
-
const [
|
|
1512
|
-
|
|
1513
|
-
|
|
1961
|
+
const [showSchematicPortsInternal, setShowSchematicPortsInternal] = useState6(
|
|
1962
|
+
() => showSchematicPorts ?? getStoredBoolean(STORAGE_KEYS.IS_SHOWING_SCHEMATIC_PORTS, false)
|
|
1963
|
+
);
|
|
1964
|
+
useEffect9(() => {
|
|
1965
|
+
if (showSchematicPorts !== void 0) {
|
|
1966
|
+
setShowSchematicPortsInternal(showSchematicPorts);
|
|
1967
|
+
}
|
|
1968
|
+
}, [showSchematicPorts]);
|
|
1969
|
+
const [isHoveringClickableComponent, setIsHoveringClickableComponent] = useState6(false);
|
|
1970
|
+
const hoveringComponentsRef = useRef6(/* @__PURE__ */ new Set());
|
|
1971
|
+
const [selectedSchematicComponent, setSelectedSchematicComponent] = useState6(null);
|
|
1972
|
+
const handleComponentHoverChange = useCallback6(
|
|
1514
1973
|
(componentId, isHovering) => {
|
|
1515
1974
|
if (isHovering) {
|
|
1516
1975
|
hoveringComponentsRef.current.add(componentId);
|
|
@@ -1521,9 +1980,9 @@ var SchematicViewer = ({
|
|
|
1521
1980
|
},
|
|
1522
1981
|
[]
|
|
1523
1982
|
);
|
|
1524
|
-
const [isHoveringClickablePort, setIsHoveringClickablePort] =
|
|
1525
|
-
const hoveringPortsRef =
|
|
1526
|
-
const handlePortHoverChange =
|
|
1983
|
+
const [isHoveringClickablePort, setIsHoveringClickablePort] = useState6(false);
|
|
1984
|
+
const hoveringPortsRef = useRef6(/* @__PURE__ */ new Set());
|
|
1985
|
+
const handlePortHoverChange = useCallback6(
|
|
1527
1986
|
(portId, isHovering) => {
|
|
1528
1987
|
if (isHovering) {
|
|
1529
1988
|
hoveringPortsRef.current.add(portId);
|
|
@@ -1534,9 +1993,9 @@ var SchematicViewer = ({
|
|
|
1534
1993
|
},
|
|
1535
1994
|
[]
|
|
1536
1995
|
);
|
|
1537
|
-
const svgDivRef =
|
|
1538
|
-
const touchStartRef =
|
|
1539
|
-
const schematicComponentIds =
|
|
1996
|
+
const svgDivRef = useRef6(null);
|
|
1997
|
+
const touchStartRef = useRef6(null);
|
|
1998
|
+
const schematicComponentIds = useMemo5(() => {
|
|
1540
1999
|
try {
|
|
1541
2000
|
const components = su4(circuitJson).schematic_component?.list() ?? [];
|
|
1542
2001
|
return components.filter(
|
|
@@ -1547,8 +2006,8 @@ var SchematicViewer = ({
|
|
|
1547
2006
|
return [];
|
|
1548
2007
|
}
|
|
1549
2008
|
}, [circuitJsonKey, circuitJson, activeSheetId]);
|
|
1550
|
-
const schematicPortsInfo =
|
|
1551
|
-
if (!
|
|
2009
|
+
const schematicPortsInfo = useMemo5(() => {
|
|
2010
|
+
if (!showSchematicPortsInternal) return [];
|
|
1552
2011
|
try {
|
|
1553
2012
|
const ports = (su4(circuitJson).schematic_port?.list() ?? []).filter(
|
|
1554
2013
|
(port) => !activeSheetId || port.schematic_sheet_id === activeSheetId
|
|
@@ -1567,7 +2026,7 @@ var SchematicViewer = ({
|
|
|
1567
2026
|
console.error("Failed to derive schematic port info", err);
|
|
1568
2027
|
return [];
|
|
1569
2028
|
}
|
|
1570
|
-
}, [circuitJsonKey, circuitJson,
|
|
2029
|
+
}, [circuitJsonKey, circuitJson, showSchematicPortsInternal, activeSheetId]);
|
|
1571
2030
|
const handleTouchStart = (e) => {
|
|
1572
2031
|
const touch = e.touches[0];
|
|
1573
2032
|
touchStartRef.current = {
|
|
@@ -1587,21 +2046,106 @@ var SchematicViewer = ({
|
|
|
1587
2046
|
}
|
|
1588
2047
|
touchStartRef.current = null;
|
|
1589
2048
|
};
|
|
2049
|
+
const shouldPanSchematic = useCallback6(
|
|
2050
|
+
(event) => {
|
|
2051
|
+
if (event.type !== "mousedown" || !("button" in event)) return true;
|
|
2052
|
+
return event.button !== 2 && !(event.button === 0 && event.ctrlKey);
|
|
2053
|
+
},
|
|
2054
|
+
[]
|
|
2055
|
+
);
|
|
1590
2056
|
const { ref: containerRef } = useMouseMatrixTransform({
|
|
1591
2057
|
onSetTransform(transform) {
|
|
1592
2058
|
if (!svgDivRef.current) return;
|
|
1593
2059
|
svgDivRef.current.style.transform = transformToString(transform);
|
|
1594
2060
|
},
|
|
1595
2061
|
// @ts-ignore disabled is a valid prop but not typed
|
|
1596
|
-
enabled: isInteractionEnabled
|
|
2062
|
+
enabled: isInteractionEnabled,
|
|
2063
|
+
shouldDrag: shouldPanSchematic
|
|
1597
2064
|
});
|
|
2065
|
+
const {
|
|
2066
|
+
menuVisible,
|
|
2067
|
+
menuPos,
|
|
2068
|
+
menuRef,
|
|
2069
|
+
setMenuVisible,
|
|
2070
|
+
contextMenuEventHandlers
|
|
2071
|
+
} = useContextMenu({ containerRef });
|
|
1598
2072
|
const { containerWidth, containerHeight } = useResizeHandling(containerRef);
|
|
1599
|
-
const
|
|
2073
|
+
const selectedComponentDetails = useMemo5(
|
|
2074
|
+
() => selectedSchematicComponent ? getSchematicComponentDetails(
|
|
2075
|
+
circuitJson,
|
|
2076
|
+
selectedSchematicComponent.schematicComponentId
|
|
2077
|
+
) : null,
|
|
2078
|
+
[circuitJsonKey, circuitJson, selectedSchematicComponent]
|
|
2079
|
+
);
|
|
2080
|
+
const componentTooltipLayout = useMemo5(() => {
|
|
2081
|
+
if (!selectedSchematicComponent || !containerWidth || !containerHeight) {
|
|
2082
|
+
return null;
|
|
2083
|
+
}
|
|
2084
|
+
const margin = 12;
|
|
2085
|
+
const gap = 14;
|
|
2086
|
+
const width = Math.min(420, containerWidth - margin * 2);
|
|
2087
|
+
const maxHeight = Math.min(520, containerHeight - margin * 2);
|
|
2088
|
+
const { anchorX, anchorY } = selectedSchematicComponent;
|
|
2089
|
+
const rightOfAnchor = anchorX + gap;
|
|
2090
|
+
const leftOfAnchor = anchorX - width - gap;
|
|
2091
|
+
const left = rightOfAnchor + width <= containerWidth - margin ? rightOfAnchor : leftOfAnchor >= margin ? leftOfAnchor : Math.max(
|
|
2092
|
+
margin,
|
|
2093
|
+
Math.min(anchorX - width / 2, containerWidth - width - margin)
|
|
2094
|
+
);
|
|
2095
|
+
const top = Math.max(
|
|
2096
|
+
margin,
|
|
2097
|
+
Math.min(anchorY - 24, containerHeight - maxHeight - margin)
|
|
2098
|
+
);
|
|
2099
|
+
return { left, top, width, maxHeight };
|
|
2100
|
+
}, [selectedSchematicComponent, containerWidth, containerHeight]);
|
|
2101
|
+
const handleSchematicComponentClick = useCallback6(
|
|
2102
|
+
(schematicComponentId, event) => {
|
|
2103
|
+
if (event.target instanceof Element && event.target.closest("[data-schematic-component-details-tooltip]")) {
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
const containerRect = containerRef.current?.getBoundingClientRect();
|
|
2107
|
+
if (!containerRect) return;
|
|
2108
|
+
setSelectedSchematicComponent({
|
|
2109
|
+
schematicComponentId,
|
|
2110
|
+
anchorX: event.clientX - containerRect.left,
|
|
2111
|
+
anchorY: event.clientY - containerRect.top
|
|
2112
|
+
});
|
|
2113
|
+
onSchematicComponentClicked?.({
|
|
2114
|
+
schematicComponentId,
|
|
2115
|
+
event
|
|
2116
|
+
});
|
|
2117
|
+
},
|
|
2118
|
+
[containerRef, onSchematicComponentClicked]
|
|
2119
|
+
);
|
|
2120
|
+
useEffect9(() => {
|
|
2121
|
+
setSelectedSchematicComponent(null);
|
|
2122
|
+
}, [circuitJsonKey, activeSheetId]);
|
|
2123
|
+
useEffect9(() => {
|
|
2124
|
+
if (!selectedSchematicComponent) return;
|
|
2125
|
+
const handleKeyDown = (event) => {
|
|
2126
|
+
if (event.key === "Escape") {
|
|
2127
|
+
setSelectedSchematicComponent(null);
|
|
2128
|
+
}
|
|
2129
|
+
};
|
|
2130
|
+
const handleDocumentMouseDown = (event) => {
|
|
2131
|
+
if (event.target instanceof Element && event.target.closest("[data-schematic-component-details-tooltip]")) {
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
2134
|
+
setSelectedSchematicComponent(null);
|
|
2135
|
+
};
|
|
2136
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
2137
|
+
document.addEventListener("mousedown", handleDocumentMouseDown);
|
|
2138
|
+
return () => {
|
|
2139
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
2140
|
+
document.removeEventListener("mousedown", handleDocumentMouseDown);
|
|
2141
|
+
};
|
|
2142
|
+
}, [selectedSchematicComponent]);
|
|
2143
|
+
const svgString = useMemo5(() => {
|
|
1600
2144
|
if (!containerWidth || !containerHeight) return "";
|
|
1601
2145
|
return convertCircuitJsonToSchematicSvg(circuitJson, {
|
|
1602
2146
|
width: containerWidth,
|
|
1603
2147
|
height: containerHeight || 720,
|
|
1604
|
-
drawPorts:
|
|
2148
|
+
drawPorts: showSchematicPortsInternal,
|
|
1605
2149
|
schematicSheetId: activeSheetId,
|
|
1606
2150
|
grid: !showGrid ? void 0 : {
|
|
1607
2151
|
cellSize: 1,
|
|
@@ -1616,10 +2160,10 @@ var SchematicViewer = ({
|
|
|
1616
2160
|
containerWidth,
|
|
1617
2161
|
containerHeight,
|
|
1618
2162
|
showGrid,
|
|
1619
|
-
|
|
2163
|
+
showSchematicPortsInternal,
|
|
1620
2164
|
activeSheetId
|
|
1621
2165
|
]);
|
|
1622
|
-
const containerBackgroundColor =
|
|
2166
|
+
const containerBackgroundColor = useMemo5(() => {
|
|
1623
2167
|
const match = svgString.match(
|
|
1624
2168
|
/<svg[^>]*style="[^"]*background-color:\s*([^;\"]+)/i
|
|
1625
2169
|
);
|
|
@@ -1637,7 +2181,7 @@ var SchematicViewer = ({
|
|
|
1637
2181
|
circuitJsonKey: `${circuitJsonKey}_${activeSheetId ?? ""}`,
|
|
1638
2182
|
enabled: netHoverHighlightEnabled
|
|
1639
2183
|
});
|
|
1640
|
-
const svgDiv =
|
|
2184
|
+
const svgDiv = useMemo5(
|
|
1641
2185
|
() => /* @__PURE__ */ jsx7(
|
|
1642
2186
|
"div",
|
|
1643
2187
|
{
|
|
@@ -1646,7 +2190,7 @@ var SchematicViewer = ({
|
|
|
1646
2190
|
pointerEvents: clickToInteractEnabled ? isInteractionEnabled ? "auto" : "none" : "auto",
|
|
1647
2191
|
transformOrigin: "0 0"
|
|
1648
2192
|
},
|
|
1649
|
-
className:
|
|
2193
|
+
className: "schematic-component-clickable",
|
|
1650
2194
|
dangerouslySetInnerHTML: { __html: svgString }
|
|
1651
2195
|
}
|
|
1652
2196
|
),
|
|
@@ -1655,8 +2199,8 @@ var SchematicViewer = ({
|
|
|
1655
2199
|
return /* @__PURE__ */ jsxs5(MouseTracker, { children: [
|
|
1656
2200
|
netHoverHighlightEnabled && /* @__PURE__ */ jsx7("style", { children: `.sch-net-faded { opacity: 0.35; }
|
|
1657
2201
|
svg :is(g.trace, g.trace-overlays, g[data-schematic-component-id], [data-schematic-net-label-id]) { transition: opacity 0.12s ease-in-out; }` }),
|
|
1658
|
-
|
|
1659
|
-
onSchematicPortClicked && /* @__PURE__ */ jsx7("style", { children:
|
|
2202
|
+
/* @__PURE__ */ jsx7("style", { children: ".schematic-component-clickable [data-schematic-component-id]:hover { cursor: pointer !important; }" }),
|
|
2203
|
+
onSchematicPortClicked && /* @__PURE__ */ jsx7("style", { children: "[data-schematic-port-id]:hover { cursor: pointer !important; }" }),
|
|
1660
2204
|
/* @__PURE__ */ jsxs5(
|
|
1661
2205
|
"div",
|
|
1662
2206
|
{
|
|
@@ -1665,19 +2209,32 @@ var SchematicViewer = ({
|
|
|
1665
2209
|
position: "relative",
|
|
1666
2210
|
backgroundColor: containerBackgroundColor,
|
|
1667
2211
|
overflow: "hidden",
|
|
1668
|
-
cursor: clickToInteractEnabled && !isInteractionEnabled ? "pointer" : isHoveringClickableComponent
|
|
2212
|
+
cursor: clickToInteractEnabled && !isInteractionEnabled ? "pointer" : isHoveringClickableComponent ? "pointer" : isHoveringClickablePort && onSchematicPortClicked ? "pointer" : "grab",
|
|
1669
2213
|
minHeight: "300px",
|
|
2214
|
+
userSelect: "none",
|
|
2215
|
+
WebkitUserSelect: "none",
|
|
2216
|
+
WebkitTouchCallout: "none",
|
|
1670
2217
|
...containerStyle
|
|
1671
2218
|
},
|
|
1672
2219
|
onMouseDownCapture: (e) => {
|
|
2220
|
+
contextMenuEventHandlers.onMouseDown(e);
|
|
1673
2221
|
if (clickToInteractEnabled && !isInteractionEnabled) {
|
|
1674
2222
|
e.preventDefault();
|
|
1675
2223
|
e.stopPropagation();
|
|
1676
2224
|
return;
|
|
1677
2225
|
}
|
|
1678
2226
|
},
|
|
1679
|
-
|
|
1680
|
-
|
|
2227
|
+
onContextMenu: contextMenuEventHandlers.onContextMenu,
|
|
2228
|
+
onTouchStart: (event) => {
|
|
2229
|
+
handleTouchStart(event);
|
|
2230
|
+
contextMenuEventHandlers.onTouchStart(event);
|
|
2231
|
+
},
|
|
2232
|
+
onTouchMove: contextMenuEventHandlers.onTouchMove,
|
|
2233
|
+
onTouchEnd: (event) => {
|
|
2234
|
+
handleTouchEnd(event);
|
|
2235
|
+
contextMenuEventHandlers.onTouchEnd();
|
|
2236
|
+
},
|
|
2237
|
+
onTouchCancel: contextMenuEventHandlers.onTouchCancel,
|
|
1681
2238
|
children: [
|
|
1682
2239
|
!isInteractionEnabled && clickToInteractEnabled && /* @__PURE__ */ jsx7(
|
|
1683
2240
|
"div",
|
|
@@ -1715,18 +2272,27 @@ var SchematicViewer = ({
|
|
|
1715
2272
|
)
|
|
1716
2273
|
}
|
|
1717
2274
|
),
|
|
1718
|
-
/* @__PURE__ */ jsx7(
|
|
2275
|
+
menuVisible && /* @__PURE__ */ jsx7(
|
|
1719
2276
|
ViewMenu,
|
|
1720
2277
|
{
|
|
1721
2278
|
circuitJson,
|
|
1722
2279
|
circuitJsonKey,
|
|
1723
|
-
|
|
1724
|
-
|
|
2280
|
+
menuRef,
|
|
2281
|
+
menuPos,
|
|
2282
|
+
onOpenChange: setMenuVisible,
|
|
2283
|
+
showPorts: showSchematicPortsInternal,
|
|
2284
|
+
onTogglePorts: (value) => {
|
|
2285
|
+
setShowSchematicPortsInternal(value);
|
|
2286
|
+
setStoredBoolean(STORAGE_KEYS.IS_SHOWING_SCHEMATIC_PORTS, value);
|
|
2287
|
+
},
|
|
1725
2288
|
showGroups: showSchematicGroups,
|
|
1726
2289
|
onToggleGroups: (value) => {
|
|
1727
2290
|
if (!disableGroups) {
|
|
1728
2291
|
setShowSchematicGroups(value);
|
|
1729
|
-
setStoredBoolean(
|
|
2292
|
+
setStoredBoolean(
|
|
2293
|
+
STORAGE_KEYS.IS_SHOWING_SCHEMATIC_GROUPS,
|
|
2294
|
+
value
|
|
2295
|
+
);
|
|
1730
2296
|
}
|
|
1731
2297
|
},
|
|
1732
2298
|
showGrid,
|
|
@@ -1741,7 +2307,7 @@ var SchematicViewer = ({
|
|
|
1741
2307
|
onSelectSheet: handleSelectSheet
|
|
1742
2308
|
}
|
|
1743
2309
|
),
|
|
1744
|
-
|
|
2310
|
+
schematicComponentIds.map((componentId) => /* @__PURE__ */ jsx7(
|
|
1745
2311
|
SchematicComponentMouseTarget,
|
|
1746
2312
|
{
|
|
1747
2313
|
componentId,
|
|
@@ -1750,17 +2316,23 @@ var SchematicViewer = ({
|
|
|
1750
2316
|
showOutline: true,
|
|
1751
2317
|
circuitJsonKey,
|
|
1752
2318
|
onHoverChange: handleComponentHoverChange,
|
|
1753
|
-
onComponentClick:
|
|
1754
|
-
onSchematicComponentClicked?.({
|
|
1755
|
-
schematicComponentId: id,
|
|
1756
|
-
event
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
2319
|
+
onComponentClick: handleSchematicComponentClick
|
|
1759
2320
|
},
|
|
1760
2321
|
componentId
|
|
1761
2322
|
)),
|
|
1762
2323
|
svgDiv,
|
|
1763
|
-
|
|
2324
|
+
selectedComponentDetails && componentTooltipLayout && /* @__PURE__ */ jsx7(
|
|
2325
|
+
SchematicComponentDetailsTooltip,
|
|
2326
|
+
{
|
|
2327
|
+
sourceComponent: selectedComponentDetails.sourceComponent,
|
|
2328
|
+
footprinterString: selectedComponentDetails.footprinterString,
|
|
2329
|
+
footprintPreviewCircuitJson: selectedComponentDetails.footprintPreviewCircuitJson,
|
|
2330
|
+
footprintPreviewViewBox: selectedComponentDetails.footprintPreviewViewBox,
|
|
2331
|
+
...componentTooltipLayout,
|
|
2332
|
+
onClose: () => setSelectedSchematicComponent(null)
|
|
2333
|
+
}
|
|
2334
|
+
),
|
|
2335
|
+
showSchematicPortsInternal && schematicPortsInfo.map(({ portId, label }) => /* @__PURE__ */ jsx7(
|
|
1764
2336
|
SchematicPortMouseTarget,
|
|
1765
2337
|
{
|
|
1766
2338
|
portId,
|
|
@@ -1790,7 +2362,7 @@ import {
|
|
|
1790
2362
|
convertCircuitJsonToSchematicSimulationSvg,
|
|
1791
2363
|
convertCircuitJsonToSimulationGraphSvg
|
|
1792
2364
|
} from "circuit-to-svg";
|
|
1793
|
-
import { useEffect as
|
|
2365
|
+
import { useEffect as useEffect10, useMemo as useMemo6, useRef as useRef7, useState as useState8 } from "react";
|
|
1794
2366
|
import { toString as transformToString2 } from "transformation-matrix";
|
|
1795
2367
|
import { useMouseMatrixTransform as useMouseMatrixTransform2 } from "use-mouse-matrix-transform";
|
|
1796
2368
|
|
|
@@ -1808,7 +2380,7 @@ var getAnalogSimulationBackgroundColor = (simulationSvg, colorOverrides) => getR
|
|
|
1808
2380
|
|
|
1809
2381
|
// lib/components/AnalogSimulationSelector.tsx
|
|
1810
2382
|
import * as DropdownMenu3 from "@radix-ui/react-dropdown-menu";
|
|
1811
|
-
import { useState as
|
|
2383
|
+
import { useState as useState7 } from "react";
|
|
1812
2384
|
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1813
2385
|
var FONT_FAMILY3 = 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
1814
2386
|
var contentStyles3 = {
|
|
@@ -1907,7 +2479,7 @@ var AnalogSimulationSelector = ({
|
|
|
1907
2479
|
selectedSimulationExperimentId,
|
|
1908
2480
|
onSelectSimulation
|
|
1909
2481
|
}) => {
|
|
1910
|
-
const [open, setOpen] =
|
|
2482
|
+
const [open, setOpen] = useState7(false);
|
|
1911
2483
|
if (simulations.length <= 1) return null;
|
|
1912
2484
|
const simulationLabels = getSimulationLabels(simulations);
|
|
1913
2485
|
const selectedSimulation = simulationLabels.find(
|
|
@@ -2001,16 +2573,16 @@ var AnalogSimulationViewer = ({
|
|
|
2001
2573
|
onSimulationChange,
|
|
2002
2574
|
acSweepView = "magnitude"
|
|
2003
2575
|
}) => {
|
|
2004
|
-
const [circuitJson, setCircuitJson] =
|
|
2005
|
-
const [isLoading, setIsLoading] =
|
|
2006
|
-
const [error, setError] =
|
|
2007
|
-
const [svgObjectUrl, setSvgObjectUrl] =
|
|
2008
|
-
const containerRef =
|
|
2009
|
-
const imgRef =
|
|
2576
|
+
const [circuitJson, setCircuitJson] = useState8(null);
|
|
2577
|
+
const [isLoading, setIsLoading] = useState8(true);
|
|
2578
|
+
const [error, setError] = useState8(null);
|
|
2579
|
+
const [svgObjectUrl, setSvgObjectUrl] = useState8(null);
|
|
2580
|
+
const containerRef = useRef7(null);
|
|
2581
|
+
const imgRef = useRef7(null);
|
|
2010
2582
|
const { containerWidth } = useResizeHandling(
|
|
2011
2583
|
containerRef
|
|
2012
2584
|
);
|
|
2013
|
-
const [isDragging, setIsDragging] =
|
|
2585
|
+
const [isDragging, setIsDragging] = useState8(false);
|
|
2014
2586
|
const {
|
|
2015
2587
|
ref: transformRef,
|
|
2016
2588
|
cancelDrag: _cancelDrag,
|
|
@@ -2026,23 +2598,23 @@ var AnalogSimulationViewer = ({
|
|
|
2026
2598
|
const renderAspectRatio = width && height ? width / height : defaultRenderAspectRatio;
|
|
2027
2599
|
const effectiveWidth = width || (height ? height * renderAspectRatio : containerWidth) || DEFAULT_RENDER_WIDTH;
|
|
2028
2600
|
const effectiveHeight = height || effectiveWidth / renderAspectRatio;
|
|
2029
|
-
|
|
2601
|
+
useEffect10(() => {
|
|
2030
2602
|
setIsLoading(true);
|
|
2031
2603
|
setError(null);
|
|
2032
2604
|
setCircuitJson(inputCircuitJson);
|
|
2033
2605
|
setIsLoading(false);
|
|
2034
2606
|
}, [inputCircuitJson]);
|
|
2035
|
-
const simulationExperiments =
|
|
2607
|
+
const simulationExperiments = useMemo6(() => {
|
|
2036
2608
|
if (!circuitJson) return [];
|
|
2037
2609
|
return circuitJson.filter(
|
|
2038
2610
|
(element) => element.type === "simulation_experiment"
|
|
2039
2611
|
);
|
|
2040
2612
|
}, [circuitJson]);
|
|
2041
|
-
const [selectedSimulationExperimentId, setSelectedSimulationExperimentId] =
|
|
2613
|
+
const [selectedSimulationExperimentId, setSelectedSimulationExperimentId] = useState8(null);
|
|
2042
2614
|
const simulationExperimentId = (selectedSimulationExperimentId && simulationExperiments.some(
|
|
2043
2615
|
(simulation) => simulation.simulation_experiment_id === selectedSimulationExperimentId
|
|
2044
2616
|
) ? selectedSimulationExperimentId : simulationExperiments[0]?.simulation_experiment_id) ?? null;
|
|
2045
|
-
|
|
2617
|
+
useEffect10(() => {
|
|
2046
2618
|
if (simulationExperimentId !== selectedSimulationExperimentId) {
|
|
2047
2619
|
setSelectedSimulationExperimentId(simulationExperimentId);
|
|
2048
2620
|
}
|
|
@@ -2051,7 +2623,7 @@ var AnalogSimulationViewer = ({
|
|
|
2051
2623
|
setSelectedSimulationExperimentId(nextSimulationExperimentId);
|
|
2052
2624
|
onSimulationChange?.(nextSimulationExperimentId);
|
|
2053
2625
|
};
|
|
2054
|
-
const simulationSvg =
|
|
2626
|
+
const simulationSvg = useMemo6(() => {
|
|
2055
2627
|
if (!circuitJson || !effectiveWidth || !effectiveHeight || !simulationExperimentId)
|
|
2056
2628
|
return "";
|
|
2057
2629
|
try {
|
|
@@ -2079,7 +2651,7 @@ var AnalogSimulationViewer = ({
|
|
|
2079
2651
|
simulationExperimentId,
|
|
2080
2652
|
acSweepView
|
|
2081
2653
|
]);
|
|
2082
|
-
|
|
2654
|
+
useEffect10(() => {
|
|
2083
2655
|
if (!simulationSvg) {
|
|
2084
2656
|
setSvgObjectUrl(null);
|
|
2085
2657
|
return;
|
|
@@ -2096,7 +2668,7 @@ var AnalogSimulationViewer = ({
|
|
|
2096
2668
|
setSvgObjectUrl(null);
|
|
2097
2669
|
}
|
|
2098
2670
|
}, [simulationSvg]);
|
|
2099
|
-
const containerBackgroundColor =
|
|
2671
|
+
const containerBackgroundColor = useMemo6(() => {
|
|
2100
2672
|
return getAnalogSimulationBackgroundColor(simulationSvg, colorOverrides);
|
|
2101
2673
|
}, [simulationSvg, colorOverrides]);
|
|
2102
2674
|
const handleMouseDown = (_e) => {
|
|
@@ -2105,7 +2677,7 @@ var AnalogSimulationViewer = ({
|
|
|
2105
2677
|
const handleTouchStart = (_e) => {
|
|
2106
2678
|
setIsDragging(true);
|
|
2107
2679
|
};
|
|
2108
|
-
|
|
2680
|
+
useEffect10(() => {
|
|
2109
2681
|
const handleMouseUp = () => {
|
|
2110
2682
|
setIsDragging(false);
|
|
2111
2683
|
};
|