@tscircuit/schematic-viewer 2.0.25 → 2.0.27
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 +432 -65
- package/dist/index.js.map +1 -1
- package/examples/example10-groups-view-schematic-groups.fixture.tsx +90 -0
- package/examples/example11-automatic-grouping-view-schematic-groups.fixture.tsx +99 -0
- package/lib/components/EditIcon.tsx +1 -1
- package/lib/components/GridIcon.tsx +1 -1
- package/lib/components/SchematicViewer.tsx +20 -0
- package/lib/components/SpiceSimulationIcon.tsx +1 -1
- package/lib/components/ViewMenu.tsx +147 -0
- package/lib/components/ViewMenuIcon.tsx +40 -0
- package/lib/hooks/useSchematicGroupsOverlay.ts +217 -0
- package/lib/hooks/useSpiceSimulation.ts +8 -18
- package/lib/utils/z-index-map.ts +3 -0
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
} from "circuit-to-svg"
|
|
5
5
|
import { useChangeSchematicComponentLocationsInSvg } from "lib/hooks/useChangeSchematicComponentLocationsInSvg"
|
|
6
6
|
import { useChangeSchematicTracesForMovedComponents } from "lib/hooks/useChangeSchematicTracesForMovedComponents"
|
|
7
|
+
import { useSchematicGroupsOverlay } from "lib/hooks/useSchematicGroupsOverlay"
|
|
7
8
|
import { enableDebug } from "lib/utils/debug"
|
|
8
9
|
import { useEffect, useMemo, useRef, useState } from "react"
|
|
9
10
|
import {
|
|
@@ -17,6 +18,8 @@ import { useComponentDragging } from "../hooks/useComponentDragging"
|
|
|
17
18
|
import type { ManualEditEvent } from "../types/edit-events"
|
|
18
19
|
import { EditIcon } from "./EditIcon"
|
|
19
20
|
import { GridIcon } from "./GridIcon"
|
|
21
|
+
import { ViewMenuIcon } from "./ViewMenuIcon"
|
|
22
|
+
import { ViewMenu } from "./ViewMenu"
|
|
20
23
|
import type { CircuitJson } from "circuit-json"
|
|
21
24
|
import { SpiceSimulationIcon } from "./SpiceSimulationIcon"
|
|
22
25
|
import { SpiceSimulationOverlay } from "./SpiceSimulationOverlay"
|
|
@@ -87,6 +90,8 @@ export const SchematicViewer = ({
|
|
|
87
90
|
const [isInteractionEnabled, setIsInteractionEnabled] = useState<boolean>(
|
|
88
91
|
!clickToInteractEnabled,
|
|
89
92
|
)
|
|
93
|
+
const [showViewMenu, setShowViewMenu] = useState(false)
|
|
94
|
+
const [showSchematicGroups, setShowSchematicGroups] = useState(false)
|
|
90
95
|
const svgDivRef = useRef<HTMLDivElement>(null)
|
|
91
96
|
const touchStartRef = useRef<{ x: number; y: number } | null>(null)
|
|
92
97
|
|
|
@@ -219,6 +224,9 @@ export const SchematicViewer = ({
|
|
|
219
224
|
editEvents: editEventsWithUnappliedEditEvents,
|
|
220
225
|
})
|
|
221
226
|
|
|
227
|
+
// Add group overlays when enabled
|
|
228
|
+
useSchematicGroupsOverlay(svgDivRef, circuitJson, circuitJsonKey, showSchematicGroups)
|
|
229
|
+
|
|
222
230
|
const svgDiv = useMemo(
|
|
223
231
|
() => (
|
|
224
232
|
<div
|
|
@@ -321,6 +329,10 @@ export const SchematicViewer = ({
|
|
|
321
329
|
</div>
|
|
322
330
|
</div>
|
|
323
331
|
)}
|
|
332
|
+
<ViewMenuIcon
|
|
333
|
+
active={showViewMenu}
|
|
334
|
+
onClick={() => setShowViewMenu(!showViewMenu)}
|
|
335
|
+
/>
|
|
324
336
|
{editingEnabled && (
|
|
325
337
|
<EditIcon
|
|
326
338
|
active={editModeEnabled}
|
|
@@ -333,6 +345,14 @@ export const SchematicViewer = ({
|
|
|
333
345
|
onClick={() => setSnapToGrid(!snapToGrid)}
|
|
334
346
|
/>
|
|
335
347
|
)}
|
|
348
|
+
<ViewMenu
|
|
349
|
+
circuitJson={circuitJson}
|
|
350
|
+
circuitJsonKey={circuitJsonKey}
|
|
351
|
+
isVisible={showViewMenu}
|
|
352
|
+
onClose={() => setShowViewMenu(false)}
|
|
353
|
+
showGroups={showSchematicGroups}
|
|
354
|
+
onToggleGroups={setShowSchematicGroups}
|
|
355
|
+
/>
|
|
336
356
|
{spiceSimulationEnabled && (
|
|
337
357
|
<SpiceSimulationIcon onClick={() => setShowSpiceOverlay(true)} />
|
|
338
358
|
)}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { useMemo } from "react"
|
|
2
|
+
import { su } from "@tscircuit/soup-util"
|
|
3
|
+
import type { CircuitJson } from "circuit-json"
|
|
4
|
+
import { zIndexMap } from "../utils/z-index-map"
|
|
5
|
+
|
|
6
|
+
interface ViewMenuProps {
|
|
7
|
+
circuitJson: CircuitJson
|
|
8
|
+
circuitJsonKey: string
|
|
9
|
+
isVisible: boolean
|
|
10
|
+
onClose: () => void
|
|
11
|
+
showGroups: boolean
|
|
12
|
+
onToggleGroups: (show: boolean) => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const ViewMenu = ({
|
|
16
|
+
circuitJson,
|
|
17
|
+
circuitJsonKey,
|
|
18
|
+
isVisible,
|
|
19
|
+
onClose,
|
|
20
|
+
showGroups,
|
|
21
|
+
onToggleGroups,
|
|
22
|
+
}: ViewMenuProps) => {
|
|
23
|
+
const hasGroups = useMemo(() => {
|
|
24
|
+
if (!circuitJson || circuitJson.length === 0) return false
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Check if there are explicit groups
|
|
28
|
+
const sourceGroups = su(circuitJson).source_group?.list() || []
|
|
29
|
+
if (sourceGroups.length > 0) return true
|
|
30
|
+
|
|
31
|
+
// Check if we can create virtual groups by component type
|
|
32
|
+
const schematicComponents =
|
|
33
|
+
su(circuitJson).schematic_component?.list() || []
|
|
34
|
+
if (schematicComponents.length > 1) {
|
|
35
|
+
const componentTypes = new Set()
|
|
36
|
+
for (const comp of schematicComponents) {
|
|
37
|
+
const sourceComp = su(circuitJson).source_component.get(
|
|
38
|
+
comp.source_component_id,
|
|
39
|
+
)
|
|
40
|
+
if (sourceComp?.ftype) {
|
|
41
|
+
componentTypes.add(sourceComp.ftype)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return componentTypes.size > 1 // Only show if there are multiple types
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return false
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error("Error checking for groups:", error)
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
}, [circuitJsonKey])
|
|
53
|
+
|
|
54
|
+
if (!isVisible) return null
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<>
|
|
58
|
+
{/* Backdrop */}
|
|
59
|
+
<div
|
|
60
|
+
onClick={onClose}
|
|
61
|
+
style={{
|
|
62
|
+
position: "absolute",
|
|
63
|
+
inset: 0,
|
|
64
|
+
backgroundColor: "transparent",
|
|
65
|
+
zIndex: zIndexMap.viewMenuBackdrop,
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
|
|
69
|
+
{/* Menu */}
|
|
70
|
+
<div
|
|
71
|
+
style={{
|
|
72
|
+
position: "absolute",
|
|
73
|
+
top: "56px",
|
|
74
|
+
right: "16px",
|
|
75
|
+
backgroundColor: "#ffffff",
|
|
76
|
+
color: "#000000",
|
|
77
|
+
border: "1px solid #ccc",
|
|
78
|
+
borderRadius: "4px",
|
|
79
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
|
|
80
|
+
minWidth: "200px",
|
|
81
|
+
zIndex: zIndexMap.viewMenu,
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
{/* Groups Toggle Option */}
|
|
85
|
+
<div
|
|
86
|
+
onClick={() => {
|
|
87
|
+
if (hasGroups) {
|
|
88
|
+
onToggleGroups(!showGroups)
|
|
89
|
+
}
|
|
90
|
+
}}
|
|
91
|
+
style={{
|
|
92
|
+
padding: "8px 12px",
|
|
93
|
+
cursor: hasGroups ? "pointer" : "not-allowed",
|
|
94
|
+
opacity: hasGroups ? 1 : 0.5,
|
|
95
|
+
fontSize: "13px",
|
|
96
|
+
color: "#000000",
|
|
97
|
+
fontFamily: "sans-serif",
|
|
98
|
+
display: "flex",
|
|
99
|
+
alignItems: "center",
|
|
100
|
+
gap: "8px",
|
|
101
|
+
}}
|
|
102
|
+
onMouseEnter={(e) => {
|
|
103
|
+
if (hasGroups) {
|
|
104
|
+
e.currentTarget.style.backgroundColor = "#f0f0f0"
|
|
105
|
+
}
|
|
106
|
+
}}
|
|
107
|
+
onMouseLeave={(e) => {
|
|
108
|
+
if (hasGroups) {
|
|
109
|
+
e.currentTarget.style.backgroundColor = "transparent"
|
|
110
|
+
}
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<div
|
|
114
|
+
style={{
|
|
115
|
+
width: "16px",
|
|
116
|
+
height: "16px",
|
|
117
|
+
border: "2px solid #000",
|
|
118
|
+
borderRadius: "2px",
|
|
119
|
+
backgroundColor: "transparent",
|
|
120
|
+
display: "flex",
|
|
121
|
+
alignItems: "center",
|
|
122
|
+
justifyContent: "center",
|
|
123
|
+
fontSize: "10px",
|
|
124
|
+
fontWeight: "bold",
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
{showGroups && "✓"}
|
|
128
|
+
</div>
|
|
129
|
+
View Schematic Groups
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
{!hasGroups && (
|
|
133
|
+
<div
|
|
134
|
+
style={{
|
|
135
|
+
padding: "8px 12px",
|
|
136
|
+
fontSize: "11px",
|
|
137
|
+
color: "#666",
|
|
138
|
+
fontStyle: "italic",
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
No groups found in this schematic
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
|
+
</>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { zIndexMap } from "../utils/z-index-map"
|
|
2
|
+
|
|
3
|
+
export const ViewMenuIcon = ({
|
|
4
|
+
onClick,
|
|
5
|
+
active,
|
|
6
|
+
}: { onClick: () => void; active: boolean }) => {
|
|
7
|
+
return (
|
|
8
|
+
<div
|
|
9
|
+
onClick={onClick}
|
|
10
|
+
style={{
|
|
11
|
+
position: "absolute",
|
|
12
|
+
top: "16px",
|
|
13
|
+
right: "16px",
|
|
14
|
+
backgroundColor: active ? "#4CAF50" : "#fff",
|
|
15
|
+
color: active ? "#fff" : "#000",
|
|
16
|
+
padding: "8px",
|
|
17
|
+
borderRadius: "4px",
|
|
18
|
+
cursor: "pointer",
|
|
19
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
|
20
|
+
display: "flex",
|
|
21
|
+
alignItems: "center",
|
|
22
|
+
gap: "4px",
|
|
23
|
+
zIndex: zIndexMap.viewMenuIcon,
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
<svg
|
|
27
|
+
width="16"
|
|
28
|
+
height="16"
|
|
29
|
+
viewBox="0 0 24 24"
|
|
30
|
+
fill="none"
|
|
31
|
+
stroke="currentColor"
|
|
32
|
+
strokeWidth="2"
|
|
33
|
+
>
|
|
34
|
+
<circle cx="12" cy="12" r="1" />
|
|
35
|
+
<circle cx="12" cy="5" r="1" />
|
|
36
|
+
<circle cx="12" cy="19" r="1" />
|
|
37
|
+
</svg>
|
|
38
|
+
</div>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { useEffect } from "react"
|
|
2
|
+
import { su } from "@tscircuit/soup-util"
|
|
3
|
+
import type { CircuitJson } from "circuit-json"
|
|
4
|
+
|
|
5
|
+
export const useSchematicGroupsOverlay = (
|
|
6
|
+
svgDivRef: React.RefObject<HTMLDivElement | null>,
|
|
7
|
+
circuitJson: CircuitJson,
|
|
8
|
+
circuitJsonKey: string,
|
|
9
|
+
showGroups: boolean,
|
|
10
|
+
) => {
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (
|
|
13
|
+
!svgDivRef.current ||
|
|
14
|
+
!showGroups ||
|
|
15
|
+
!circuitJson ||
|
|
16
|
+
circuitJson.length === 0
|
|
17
|
+
) {
|
|
18
|
+
// Remove any existing group overlays
|
|
19
|
+
if (svgDivRef.current) {
|
|
20
|
+
const existingOverlays = svgDivRef.current.querySelectorAll(
|
|
21
|
+
".schematic-group-overlay",
|
|
22
|
+
)
|
|
23
|
+
existingOverlays.forEach((overlay) => overlay.remove())
|
|
24
|
+
}
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const svg = svgDivRef.current.querySelector("svg")
|
|
29
|
+
if (!svg) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Remove existing overlays first
|
|
34
|
+
const existingOverlays = svg.querySelectorAll(".schematic-group-overlay")
|
|
35
|
+
existingOverlays.forEach((overlay) => overlay.remove())
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
// Get explicit groups first
|
|
39
|
+
const sourceGroups = su(circuitJson).source_group?.list() || []
|
|
40
|
+
const schematicComponents =
|
|
41
|
+
su(circuitJson).schematic_component?.list() || []
|
|
42
|
+
|
|
43
|
+
let groupsToRender: Array<{
|
|
44
|
+
id: string
|
|
45
|
+
name: string
|
|
46
|
+
components: any[]
|
|
47
|
+
color: string
|
|
48
|
+
}> = []
|
|
49
|
+
|
|
50
|
+
// Check if we have meaningful explicit groups (not just auto-generated default groups)
|
|
51
|
+
const hasMeaningfulGroups = sourceGroups.length > 0 &&
|
|
52
|
+
sourceGroups.some(group => group.name && group.name !== "default" && group.name !== "")
|
|
53
|
+
|
|
54
|
+
if (hasMeaningfulGroups) {
|
|
55
|
+
// Use explicit groups
|
|
56
|
+
const groupMap = new Map<string, any[]>()
|
|
57
|
+
|
|
58
|
+
for (const comp of schematicComponents) {
|
|
59
|
+
const sourceComp = su(circuitJson).source_component.get(
|
|
60
|
+
comp.source_component_id,
|
|
61
|
+
)
|
|
62
|
+
if (sourceComp?.source_group_id) {
|
|
63
|
+
if (!groupMap.has(sourceComp.source_group_id)) {
|
|
64
|
+
groupMap.set(sourceComp.source_group_id, [])
|
|
65
|
+
}
|
|
66
|
+
groupMap.get(sourceComp.source_group_id)!.push(comp)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
groupsToRender = Array.from(groupMap.entries()).map(
|
|
71
|
+
([groupId, components], index) => {
|
|
72
|
+
const group = sourceGroups.find(
|
|
73
|
+
(g) => g.source_group_id === groupId,
|
|
74
|
+
)
|
|
75
|
+
return {
|
|
76
|
+
id: groupId,
|
|
77
|
+
name: group?.name || `Group ${index + 1}`,
|
|
78
|
+
components,
|
|
79
|
+
color: getGroupColor(index),
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
} else {
|
|
84
|
+
// Create virtual groups by component type
|
|
85
|
+
const componentTypeGroups = new Map<string, any[]>()
|
|
86
|
+
|
|
87
|
+
for (const comp of schematicComponents) {
|
|
88
|
+
const sourceComp = su(circuitJson).source_component.get(
|
|
89
|
+
comp.source_component_id,
|
|
90
|
+
)
|
|
91
|
+
if (sourceComp) {
|
|
92
|
+
const componentType = sourceComp.ftype || "other"
|
|
93
|
+
if (!componentTypeGroups.has(componentType)) {
|
|
94
|
+
componentTypeGroups.set(componentType, [])
|
|
95
|
+
}
|
|
96
|
+
componentTypeGroups.get(componentType)!.push(comp)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
groupsToRender = Array.from(componentTypeGroups.entries()).map(
|
|
101
|
+
([type, components], index) => ({
|
|
102
|
+
id: `type_${type}`,
|
|
103
|
+
name: `${type.charAt(0).toUpperCase() + type.slice(1)}s`,
|
|
104
|
+
components,
|
|
105
|
+
color: getGroupColor(index),
|
|
106
|
+
}),
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Render group overlays
|
|
111
|
+
groupsToRender.forEach((group, groupIndex) => {
|
|
112
|
+
if (group.components.length === 0) return
|
|
113
|
+
|
|
114
|
+
// Calculate bounding box for the group
|
|
115
|
+
const groupBounds = calculateGroupBounds(group.components, svg)
|
|
116
|
+
if (!groupBounds) return
|
|
117
|
+
|
|
118
|
+
// Create group overlay rectangle
|
|
119
|
+
const groupOverlay = document.createElementNS(
|
|
120
|
+
"http://www.w3.org/2000/svg",
|
|
121
|
+
"rect",
|
|
122
|
+
)
|
|
123
|
+
groupOverlay.setAttribute("class", "schematic-group-overlay")
|
|
124
|
+
groupOverlay.setAttribute("x", (groupBounds.minX - 25).toString())
|
|
125
|
+
groupOverlay.setAttribute("y", (groupBounds.minY - 25).toString())
|
|
126
|
+
groupOverlay.setAttribute(
|
|
127
|
+
"width",
|
|
128
|
+
(groupBounds.maxX - groupBounds.minX + 50).toString(),
|
|
129
|
+
)
|
|
130
|
+
groupOverlay.setAttribute(
|
|
131
|
+
"height",
|
|
132
|
+
(groupBounds.maxY - groupBounds.minY + 50).toString(),
|
|
133
|
+
)
|
|
134
|
+
groupOverlay.setAttribute("fill", "none")
|
|
135
|
+
groupOverlay.setAttribute("stroke", group.color)
|
|
136
|
+
groupOverlay.setAttribute("stroke-width", "3")
|
|
137
|
+
groupOverlay.setAttribute("stroke-dasharray", "8,4")
|
|
138
|
+
groupOverlay.setAttribute("opacity", "0.8")
|
|
139
|
+
groupOverlay.setAttribute("rx", "4")
|
|
140
|
+
groupOverlay.setAttribute("ry", "4")
|
|
141
|
+
|
|
142
|
+
// Create group label
|
|
143
|
+
const groupLabel = document.createElementNS(
|
|
144
|
+
"http://www.w3.org/2000/svg",
|
|
145
|
+
"text",
|
|
146
|
+
)
|
|
147
|
+
groupLabel.setAttribute("class", "schematic-group-overlay")
|
|
148
|
+
groupLabel.setAttribute("x", (groupBounds.minX - 10).toString())
|
|
149
|
+
groupLabel.setAttribute("y", (groupBounds.minY - 8).toString())
|
|
150
|
+
groupLabel.setAttribute("fill", group.color)
|
|
151
|
+
groupLabel.setAttribute("font-size", "14")
|
|
152
|
+
groupLabel.setAttribute("font-family", "Arial, sans-serif")
|
|
153
|
+
groupLabel.setAttribute("font-weight", "bold")
|
|
154
|
+
groupLabel.setAttribute("stroke", "#fff")
|
|
155
|
+
groupLabel.setAttribute("stroke-width", "0.5")
|
|
156
|
+
groupLabel.setAttribute("paint-order", "stroke fill")
|
|
157
|
+
groupLabel.textContent = group.name
|
|
158
|
+
|
|
159
|
+
// Add overlays to the SVG (use appendChild to ensure they're on top)
|
|
160
|
+
svg.appendChild(groupOverlay)
|
|
161
|
+
svg.appendChild(groupLabel)
|
|
162
|
+
})
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error("Error creating group overlays:", error)
|
|
165
|
+
}
|
|
166
|
+
}, [svgDivRef, circuitJsonKey, showGroups])
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function getGroupColor(index: number): string {
|
|
170
|
+
const colors = [
|
|
171
|
+
"#FF6B6B", // Red
|
|
172
|
+
"#4ECDC4", // Teal
|
|
173
|
+
"#45B7D1", // Blue
|
|
174
|
+
"#96CEB4", // Green
|
|
175
|
+
"#FF8C42", // Orange
|
|
176
|
+
"#DDA0DD", // Plum
|
|
177
|
+
"#98D8C8", // Mint
|
|
178
|
+
"#F7DC6F", // Light Yellow
|
|
179
|
+
]
|
|
180
|
+
return colors[index % colors.length]
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function calculateGroupBounds(components: any[], svg: SVGElement) {
|
|
184
|
+
let minX = Infinity,
|
|
185
|
+
minY = Infinity,
|
|
186
|
+
maxX = -Infinity,
|
|
187
|
+
maxY = -Infinity
|
|
188
|
+
|
|
189
|
+
for (const component of components) {
|
|
190
|
+
// Look for the component group element (based on circuit-to-svg documentation)
|
|
191
|
+
let componentElement = svg.querySelector(
|
|
192
|
+
`g[data-schematic-component-id="${component.schematic_component_id}"]`,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if (!componentElement) {
|
|
196
|
+
// Fallback to any element with the data attribute
|
|
197
|
+
componentElement = svg.querySelector(
|
|
198
|
+
`[data-schematic-component-id="${component.schematic_component_id}"]`,
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (componentElement) {
|
|
203
|
+
const bbox = (componentElement as SVGGraphicsElement).getBBox()
|
|
204
|
+
minX = Math.min(minX, bbox.x)
|
|
205
|
+
minY = Math.min(minY, bbox.y)
|
|
206
|
+
maxX = Math.max(maxX, bbox.x + bbox.width)
|
|
207
|
+
maxY = Math.max(maxY, bbox.y + bbox.height)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (minX === Infinity) {
|
|
212
|
+
return null
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const bounds = { minX, minY, maxX, maxY }
|
|
216
|
+
return bounds
|
|
217
|
+
}
|
|
@@ -108,26 +108,16 @@ export const useSpiceSimulation = (spiceString: string | null) => {
|
|
|
108
108
|
setPlotData([])
|
|
109
109
|
setNodes([])
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
new URL("../workers/spice-simulation.worker.ts", import.meta.url),
|
|
118
|
-
{ type: "module" },
|
|
119
|
-
)
|
|
120
|
-
} else {
|
|
121
|
-
const workerUrl = getSpiceSimulationWorkerBlobUrl()
|
|
122
|
-
|
|
123
|
-
if (!workerUrl) {
|
|
124
|
-
setError("Could not create SPICE simulation worker.")
|
|
125
|
-
setIsLoading(false)
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
worker = new Worker(workerUrl, { type: "module" })
|
|
111
|
+
const workerUrl = getSpiceSimulationWorkerBlobUrl()
|
|
112
|
+
|
|
113
|
+
if (!workerUrl) {
|
|
114
|
+
setError("Could not create SPICE simulation worker.")
|
|
115
|
+
setIsLoading(false)
|
|
116
|
+
return
|
|
129
117
|
}
|
|
130
118
|
|
|
119
|
+
const worker = new Worker(workerUrl, { type: "module" })
|
|
120
|
+
|
|
131
121
|
worker.onmessage = (event: MessageEvent<WorkerMessage>) => {
|
|
132
122
|
if (event.data.type === "result") {
|
|
133
123
|
try {
|
package/lib/utils/z-index-map.ts
CHANGED