@tscircuit/schematic-viewer 2.0.49 → 2.0.51
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/bun.lockb +0 -0
- package/dist/index.d.ts +13 -3
- package/dist/index.js +263 -3
- package/dist/index.js.map +1 -1
- package/examples/example15-analog-simulation-viewer.fixture.tsx +145 -0
- package/examples/example16-no-analog-simulation.fixture.tsx +13 -0
- package/lib/components/AnalogSimulationViewer.tsx +300 -0
- package/lib/components/SpicePlot.tsx +1 -1
- package/lib/components/ViewMenu.tsx +1 -1
- package/lib/hooks/useChangeSchematicTracesForMovedComponents.ts +2 -2
- package/lib/hooks/useSchematicGroupsOverlay.ts +1 -1
- package/lib/index.ts +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { AnalogSimulationViewer } from "../lib/components/AnalogSimulationViewer"
|
|
3
|
+
import * as Core from "@tscircuit/core"
|
|
4
|
+
import createNgspiceSpiceEngine from "@tscircuit/ngspice-spice-engine"
|
|
5
|
+
|
|
6
|
+
// TSX circuit definition
|
|
7
|
+
const SwitchCircuitElement = (
|
|
8
|
+
<board schMaxTraceDistance={10} routingDisabled>
|
|
9
|
+
<voltagesource name="V1" voltage="5V" />
|
|
10
|
+
<resistor name="R_base" resistance="10k" schY={2} />
|
|
11
|
+
<switch name="SW1" simSwitchFrequency="1kHz" schX={1.5} schY={2} />
|
|
12
|
+
<transistor
|
|
13
|
+
name="Q1"
|
|
14
|
+
type="npn"
|
|
15
|
+
footprint="sot23"
|
|
16
|
+
schX={2}
|
|
17
|
+
schY={0.3}
|
|
18
|
+
schRotation={180}
|
|
19
|
+
/>
|
|
20
|
+
<resistor name="R_collector" resistance="10k" schY={-2} />
|
|
21
|
+
|
|
22
|
+
<trace from=".V1 > .pin1" to=".R_base > .pin1" />
|
|
23
|
+
<trace from=".R_base > .pin2" to=".SW1 > .pin1" />
|
|
24
|
+
<trace from=".SW1 > .pin2" to=".Q1 > .base" />
|
|
25
|
+
|
|
26
|
+
<trace from=".V1 > .pin1" to=".R_collector > .pin1" />
|
|
27
|
+
<trace from=".R_collector > .pin2" to=".Q1 > .collector" />
|
|
28
|
+
|
|
29
|
+
<trace from=".Q1 > .emitter" to=".V1 > .pin2" />
|
|
30
|
+
|
|
31
|
+
<voltageprobe name="VP_COLLECTOR" connectsTo=".R_collector > .pin2" />
|
|
32
|
+
|
|
33
|
+
<analogsimulation duration="4ms" timePerStep="1us" spiceEngine="ngspice" />
|
|
34
|
+
</board>
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
// Convert TSX to CircuitJSON and add simulation data
|
|
38
|
+
const createSimulatedCircuitJson = async () => {
|
|
39
|
+
try {
|
|
40
|
+
// Step 1: Create circuit with platform configuration
|
|
41
|
+
const circuit = new Core.Circuit()
|
|
42
|
+
|
|
43
|
+
const ngspiceEngine = await createNgspiceSpiceEngine()
|
|
44
|
+
circuit.setPlatform({
|
|
45
|
+
spiceEngineMap: {
|
|
46
|
+
ngspice: ngspiceEngine,
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Step 2: Add the circuit element
|
|
51
|
+
circuit.add(SwitchCircuitElement)
|
|
52
|
+
await circuit.renderUntilSettled()
|
|
53
|
+
|
|
54
|
+
// Step 3: Get CircuitJSON (includes simulation data if produced by the platform)
|
|
55
|
+
return circuit.getCircuitJson()
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error("Simulation failed:", error)
|
|
58
|
+
// Return basic CircuitJSON if simulation fails
|
|
59
|
+
const fallbackCircuit = new Core.Circuit()
|
|
60
|
+
fallbackCircuit.add(SwitchCircuitElement)
|
|
61
|
+
await fallbackCircuit.renderUntilSettled()
|
|
62
|
+
return fallbackCircuit.getCircuitJson()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default () => {
|
|
67
|
+
const [simulatedCircuitJson, setSimulatedCircuitJson] = React.useState<
|
|
68
|
+
any[] | null
|
|
69
|
+
>(null)
|
|
70
|
+
const [isLoading, setIsLoading] = React.useState(true)
|
|
71
|
+
const [error, setError] = React.useState<string | null>(null)
|
|
72
|
+
|
|
73
|
+
React.useEffect(() => {
|
|
74
|
+
const loadAndSimulateCircuit = async () => {
|
|
75
|
+
try {
|
|
76
|
+
setIsLoading(true)
|
|
77
|
+
setError(null)
|
|
78
|
+
|
|
79
|
+
const result = await createSimulatedCircuitJson()
|
|
80
|
+
setSimulatedCircuitJson(result)
|
|
81
|
+
} catch (err) {
|
|
82
|
+
setError(err instanceof Error ? err.message : "Failed to load circuit")
|
|
83
|
+
console.error("Error loading circuit:", err)
|
|
84
|
+
} finally {
|
|
85
|
+
setIsLoading(false)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
loadAndSimulateCircuit()
|
|
90
|
+
}, [])
|
|
91
|
+
|
|
92
|
+
if (isLoading) {
|
|
93
|
+
return (
|
|
94
|
+
<div style={{ padding: "20px", textAlign: "center" }}>
|
|
95
|
+
<h2>Analog Simulation Viewer Example</h2>
|
|
96
|
+
<p>Converting TSX to CircuitJSON and running SPICE simulation...</p>
|
|
97
|
+
<div
|
|
98
|
+
style={{
|
|
99
|
+
display: "inline-block",
|
|
100
|
+
padding: "20px",
|
|
101
|
+
backgroundColor: "#f5f5f5",
|
|
102
|
+
borderRadius: "8px",
|
|
103
|
+
marginTop: "20px",
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
Loading and simulating circuit...
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (error) {
|
|
113
|
+
return (
|
|
114
|
+
<div style={{ padding: "20px", textAlign: "center" }}>
|
|
115
|
+
<h2>Analog Simulation Viewer Example</h2>
|
|
116
|
+
<div
|
|
117
|
+
style={{
|
|
118
|
+
display: "inline-block",
|
|
119
|
+
padding: "20px",
|
|
120
|
+
backgroundColor: "#fef2f2",
|
|
121
|
+
borderRadius: "8px",
|
|
122
|
+
border: "1px solid #fecaca",
|
|
123
|
+
color: "#dc2626",
|
|
124
|
+
marginTop: "20px",
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
<h4>Error:</h4>
|
|
128
|
+
<p>{error}</p>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
simulatedCircuitJson && (
|
|
136
|
+
<AnalogSimulationViewer
|
|
137
|
+
circuitJson={simulatedCircuitJson}
|
|
138
|
+
containerStyle={{
|
|
139
|
+
width: "100vw",
|
|
140
|
+
height: "100vh",
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AnalogSimulationViewer } from "lib/components/AnalogSimulationViewer"
|
|
2
|
+
import { renderToCircuitJson } from "lib/dev/render-to-circuit-json"
|
|
3
|
+
|
|
4
|
+
const circuitJson = renderToCircuitJson(
|
|
5
|
+
<board width="10mm" height="10mm" routingDisabled></board>,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
export default () => (
|
|
9
|
+
<AnalogSimulationViewer
|
|
10
|
+
circuitJson={circuitJson}
|
|
11
|
+
containerStyle={{ height: "100%" }}
|
|
12
|
+
/>
|
|
13
|
+
)
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import {
|
|
2
|
+
convertCircuitJsonToSchematicSimulationSvg,
|
|
3
|
+
type ColorOverrides,
|
|
4
|
+
} from "circuit-to-svg"
|
|
5
|
+
import { useEffect, useState, useMemo, useRef } from "react"
|
|
6
|
+
import { useResizeHandling } from "../hooks/use-resize-handling"
|
|
7
|
+
import { useMouseMatrixTransform } from "use-mouse-matrix-transform"
|
|
8
|
+
import { toString as transformToString } from "transformation-matrix"
|
|
9
|
+
import type { CircuitJson } from "circuit-json"
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
circuitJson: CircuitJson
|
|
13
|
+
containerStyle?: React.CSSProperties
|
|
14
|
+
colorOverrides?: ColorOverrides
|
|
15
|
+
width?: number
|
|
16
|
+
height?: number
|
|
17
|
+
className?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const AnalogSimulationViewer = ({
|
|
21
|
+
circuitJson: inputCircuitJson,
|
|
22
|
+
containerStyle,
|
|
23
|
+
colorOverrides,
|
|
24
|
+
width,
|
|
25
|
+
height,
|
|
26
|
+
className,
|
|
27
|
+
}: Props) => {
|
|
28
|
+
const [circuitJson, setCircuitJson] = useState<CircuitJson | null>(null)
|
|
29
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
30
|
+
const [error, setError] = useState<string | null>(null)
|
|
31
|
+
const [svgObjectUrl, setSvgObjectUrl] = useState<string | null>(null)
|
|
32
|
+
const containerRef = useRef<HTMLDivElement>(null)
|
|
33
|
+
const imgRef = useRef<HTMLImageElement>(null)
|
|
34
|
+
|
|
35
|
+
const { containerWidth, containerHeight } = useResizeHandling(
|
|
36
|
+
containerRef as React.RefObject<HTMLElement>,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
const [isDragging, setIsDragging] = useState(false)
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
ref: transformRef,
|
|
43
|
+
cancelDrag: _cancelDrag,
|
|
44
|
+
transform: _svgToScreenProjection,
|
|
45
|
+
} = useMouseMatrixTransform({
|
|
46
|
+
onSetTransform(transform) {
|
|
47
|
+
if (imgRef.current) {
|
|
48
|
+
imgRef.current.style.transform = transformToString(transform)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const effectiveWidth = width || containerWidth || 1000
|
|
54
|
+
const effectiveHeight = height || containerHeight || 600
|
|
55
|
+
|
|
56
|
+
// Set CircuitJSON from props
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
setIsLoading(true)
|
|
59
|
+
setError(null)
|
|
60
|
+
setCircuitJson(inputCircuitJson)
|
|
61
|
+
setIsLoading(false)
|
|
62
|
+
}, [inputCircuitJson])
|
|
63
|
+
|
|
64
|
+
// Find simulation experiment ID from circuit JSON
|
|
65
|
+
const simulationExperimentId = useMemo(() => {
|
|
66
|
+
if (!circuitJson) return null
|
|
67
|
+
const simulationElement = circuitJson.find(
|
|
68
|
+
(el) => el.type === "simulation_experiment",
|
|
69
|
+
)
|
|
70
|
+
return simulationElement?.simulation_experiment_id || null
|
|
71
|
+
}, [circuitJson])
|
|
72
|
+
|
|
73
|
+
// Find simulation graph IDs from circuit JSON
|
|
74
|
+
const simulationGraphIds = useMemo(() => {
|
|
75
|
+
if (!circuitJson) return []
|
|
76
|
+
return circuitJson
|
|
77
|
+
.filter((el) => el.type === "simulation_transient_voltage_graph")
|
|
78
|
+
.map((el) => el.simulation_transient_voltage_graph_id)
|
|
79
|
+
}, [circuitJson])
|
|
80
|
+
|
|
81
|
+
// Generate SVG from CircuitJSON
|
|
82
|
+
const simulationSvg = useMemo(() => {
|
|
83
|
+
if (
|
|
84
|
+
!circuitJson ||
|
|
85
|
+
!effectiveWidth ||
|
|
86
|
+
!effectiveHeight ||
|
|
87
|
+
!simulationExperimentId
|
|
88
|
+
)
|
|
89
|
+
return ""
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
return convertCircuitJsonToSchematicSimulationSvg({
|
|
93
|
+
circuitJson,
|
|
94
|
+
simulation_experiment_id: simulationExperimentId,
|
|
95
|
+
simulation_transient_voltage_graph_ids: simulationGraphIds,
|
|
96
|
+
width: effectiveWidth,
|
|
97
|
+
height: effectiveHeight,
|
|
98
|
+
schematicOptions: { colorOverrides },
|
|
99
|
+
})
|
|
100
|
+
} catch (fallbackErr) {
|
|
101
|
+
console.error("Failed to generate fallback schematic SVG:", fallbackErr)
|
|
102
|
+
return ""
|
|
103
|
+
}
|
|
104
|
+
}, [
|
|
105
|
+
circuitJson,
|
|
106
|
+
effectiveWidth,
|
|
107
|
+
effectiveHeight,
|
|
108
|
+
colorOverrides,
|
|
109
|
+
simulationExperimentId,
|
|
110
|
+
simulationGraphIds,
|
|
111
|
+
])
|
|
112
|
+
|
|
113
|
+
// Create/revoke object URL whenever the SVG changes
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
if (!simulationSvg) {
|
|
116
|
+
setSvgObjectUrl(null)
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const blob = new Blob([simulationSvg], { type: "image/svg+xml" })
|
|
122
|
+
const url = URL.createObjectURL(blob)
|
|
123
|
+
setSvgObjectUrl(url)
|
|
124
|
+
return () => {
|
|
125
|
+
URL.revokeObjectURL(url)
|
|
126
|
+
}
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error("Failed to create SVG object URL:", error)
|
|
129
|
+
setSvgObjectUrl(null)
|
|
130
|
+
}
|
|
131
|
+
}, [simulationSvg])
|
|
132
|
+
|
|
133
|
+
const containerBackgroundColor = useMemo(() => {
|
|
134
|
+
if (!simulationSvg) return "transparent"
|
|
135
|
+
const match = simulationSvg.match(
|
|
136
|
+
/<svg[^>]*style="[^"]*background-color:\s*([^;\"]+)/i,
|
|
137
|
+
)
|
|
138
|
+
return match?.[1] ?? "transparent"
|
|
139
|
+
}, [simulationSvg])
|
|
140
|
+
|
|
141
|
+
const handleMouseDown = (_e: React.MouseEvent) => {
|
|
142
|
+
setIsDragging(true)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const handleTouchStart = (_e: React.TouchEvent) => {
|
|
146
|
+
setIsDragging(true)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const handleMouseUp = () => {
|
|
151
|
+
setIsDragging(false)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const handleTouchEnd = () => {
|
|
155
|
+
setIsDragging(false)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
window.addEventListener("mouseup", handleMouseUp)
|
|
159
|
+
window.addEventListener("touchend", handleTouchEnd)
|
|
160
|
+
|
|
161
|
+
return () => {
|
|
162
|
+
window.removeEventListener("mouseup", handleMouseUp)
|
|
163
|
+
window.removeEventListener("touchend", handleTouchEnd)
|
|
164
|
+
}
|
|
165
|
+
}, [])
|
|
166
|
+
|
|
167
|
+
if (isLoading) {
|
|
168
|
+
return (
|
|
169
|
+
<div
|
|
170
|
+
style={{
|
|
171
|
+
display: "flex",
|
|
172
|
+
alignItems: "center",
|
|
173
|
+
justifyContent: "center",
|
|
174
|
+
backgroundColor: "#f5f5f5",
|
|
175
|
+
minHeight: "300px",
|
|
176
|
+
fontFamily: "sans-serif",
|
|
177
|
+
fontSize: "16px",
|
|
178
|
+
color: "#666",
|
|
179
|
+
...containerStyle,
|
|
180
|
+
}}
|
|
181
|
+
className={className}
|
|
182
|
+
>
|
|
183
|
+
Loading circuit...
|
|
184
|
+
</div>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (error) {
|
|
189
|
+
return (
|
|
190
|
+
<div
|
|
191
|
+
style={{
|
|
192
|
+
display: "flex",
|
|
193
|
+
alignItems: "center",
|
|
194
|
+
justifyContent: "center",
|
|
195
|
+
backgroundColor: "#fef2f2",
|
|
196
|
+
minHeight: "300px",
|
|
197
|
+
fontFamily: "sans-serif",
|
|
198
|
+
fontSize: "16px",
|
|
199
|
+
color: "#dc2626",
|
|
200
|
+
...containerStyle,
|
|
201
|
+
}}
|
|
202
|
+
className={className}
|
|
203
|
+
>
|
|
204
|
+
<div style={{ textAlign: "center", padding: "20px" }}>
|
|
205
|
+
<div style={{ fontWeight: "bold", marginBottom: "8px" }}>
|
|
206
|
+
Circuit Conversion Error
|
|
207
|
+
</div>
|
|
208
|
+
<div style={{ fontSize: "14px" }}>{error}</div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!simulationSvg) {
|
|
215
|
+
return (
|
|
216
|
+
<div
|
|
217
|
+
style={{
|
|
218
|
+
display: "flex",
|
|
219
|
+
flexDirection: "column",
|
|
220
|
+
alignItems: "center",
|
|
221
|
+
justifyContent: "center",
|
|
222
|
+
backgroundColor: "#f8fafc",
|
|
223
|
+
minHeight: "300px",
|
|
224
|
+
fontFamily: "sans-serif",
|
|
225
|
+
gap: "12px",
|
|
226
|
+
...containerStyle,
|
|
227
|
+
}}
|
|
228
|
+
className={className}
|
|
229
|
+
>
|
|
230
|
+
<div style={{ fontSize: "16px", color: "#475569", fontWeight: 500 }}>
|
|
231
|
+
No Simulation Found
|
|
232
|
+
</div>
|
|
233
|
+
<div style={{ fontSize: "14px", color: "#64748b" }}>
|
|
234
|
+
Use{" "}
|
|
235
|
+
<code
|
|
236
|
+
style={{
|
|
237
|
+
backgroundColor: "#e2e8f0",
|
|
238
|
+
padding: "2px 6px",
|
|
239
|
+
borderRadius: "4px",
|
|
240
|
+
fontFamily: "monospace",
|
|
241
|
+
fontSize: "13px",
|
|
242
|
+
}}
|
|
243
|
+
>
|
|
244
|
+
{"<analogsimulation />"}
|
|
245
|
+
</code>{" "}
|
|
246
|
+
to create simulations
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<div
|
|
254
|
+
ref={(node) => {
|
|
255
|
+
containerRef.current = node
|
|
256
|
+
transformRef.current = node
|
|
257
|
+
}}
|
|
258
|
+
style={{
|
|
259
|
+
position: "relative",
|
|
260
|
+
backgroundColor: containerBackgroundColor,
|
|
261
|
+
overflow: "hidden",
|
|
262
|
+
minHeight: "300px",
|
|
263
|
+
cursor: isDragging ? "grabbing" : "grab",
|
|
264
|
+
...containerStyle,
|
|
265
|
+
}}
|
|
266
|
+
className={className}
|
|
267
|
+
onMouseDown={handleMouseDown}
|
|
268
|
+
onTouchStart={handleTouchStart}
|
|
269
|
+
>
|
|
270
|
+
{svgObjectUrl ? (
|
|
271
|
+
<img
|
|
272
|
+
ref={imgRef}
|
|
273
|
+
src={svgObjectUrl}
|
|
274
|
+
alt="Circuit Simulation"
|
|
275
|
+
style={{
|
|
276
|
+
transformOrigin: "0 0",
|
|
277
|
+
width: "100%",
|
|
278
|
+
height: "100%",
|
|
279
|
+
display: "block",
|
|
280
|
+
objectFit: "contain",
|
|
281
|
+
}}
|
|
282
|
+
/>
|
|
283
|
+
) : (
|
|
284
|
+
<div
|
|
285
|
+
style={{
|
|
286
|
+
display: "flex",
|
|
287
|
+
alignItems: "center",
|
|
288
|
+
justifyContent: "center",
|
|
289
|
+
height: "100%",
|
|
290
|
+
minHeight: "300px",
|
|
291
|
+
color: "#666",
|
|
292
|
+
fontFamily: "sans-serif",
|
|
293
|
+
}}
|
|
294
|
+
>
|
|
295
|
+
Failed to render SVG
|
|
296
|
+
</div>
|
|
297
|
+
)}
|
|
298
|
+
</div>
|
|
299
|
+
)
|
|
300
|
+
}
|
|
@@ -172,7 +172,7 @@ export const SpicePlot = ({
|
|
|
172
172
|
title: (tooltipItems) => {
|
|
173
173
|
if (tooltipItems.length > 0) {
|
|
174
174
|
const item = tooltipItems[0]
|
|
175
|
-
return formatTimeWithUnits(item.parsed.x)
|
|
175
|
+
return formatTimeWithUnits(item.parsed.x as number)
|
|
176
176
|
}
|
|
177
177
|
return ""
|
|
178
178
|
},
|
|
@@ -36,7 +36,7 @@ export const ViewMenu = ({
|
|
|
36
36
|
const componentTypes = new Set()
|
|
37
37
|
for (const comp of schematicComponents) {
|
|
38
38
|
const sourceComp = su(circuitJson).source_component.get(
|
|
39
|
-
comp.source_component_id
|
|
39
|
+
comp.source_component_id!,
|
|
40
40
|
)
|
|
41
41
|
if (sourceComp?.ftype) {
|
|
42
42
|
componentTypes.add(sourceComp.ftype)
|
|
@@ -57,7 +57,7 @@ export const useChangeSchematicTracesForMovedComponents = ({
|
|
|
57
57
|
const src_traces = su(circuitJson)
|
|
58
58
|
.source_trace.list()
|
|
59
59
|
.filter((st) =>
|
|
60
|
-
st.connected_source_port_ids?.some((spi) =>
|
|
60
|
+
st.connected_source_port_ids?.some((spi: string) =>
|
|
61
61
|
src_port_ids.has(spi),
|
|
62
62
|
),
|
|
63
63
|
)
|
|
@@ -66,7 +66,7 @@ export const useChangeSchematicTracesForMovedComponents = ({
|
|
|
66
66
|
)
|
|
67
67
|
const schematic_traces = su(circuitJson)
|
|
68
68
|
.schematic_trace.list()
|
|
69
|
-
.filter((st) => src_trace_ids.has(st.source_trace_id))
|
|
69
|
+
.filter((st) => src_trace_ids.has(st.source_trace_id!))
|
|
70
70
|
|
|
71
71
|
// Make the connected traces dashed
|
|
72
72
|
schematic_traces.forEach((trace) => {
|
|
@@ -122,7 +122,7 @@ export const useSchematicGroupsOverlay = (
|
|
|
122
122
|
|
|
123
123
|
for (const comp of schematicComponents) {
|
|
124
124
|
const sourceComp = su(circuitJson).source_component.get(
|
|
125
|
-
comp.source_component_id
|
|
125
|
+
comp.source_component_id!,
|
|
126
126
|
)
|
|
127
127
|
if (sourceComp?.source_group_id) {
|
|
128
128
|
if (!groupMap.has(sourceComp.source_group_id)) {
|
package/lib/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { SchematicViewer } from "./components/SchematicViewer"
|
|
2
2
|
export { MouseTracker } from "./components/MouseTracker"
|
|
3
3
|
export { useMouseEventsOverBoundingBox } from "./hooks/useMouseEventsOverBoundingBox"
|
|
4
|
+
export { AnalogSimulationViewer } from "./components/AnalogSimulationViewer"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/schematic-viewer",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.51",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"react-dom": "^19.1.0",
|
|
28
28
|
"react-reconciler": "^0.31.0",
|
|
29
29
|
"semver": "^7.7.2",
|
|
30
|
-
"tscircuit": "^0.0.
|
|
30
|
+
"tscircuit": "^0.0.1037",
|
|
31
31
|
"tsup": "^8.3.5",
|
|
32
32
|
"vite": "^6.0.3"
|
|
33
33
|
},
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"chart.js": "^4.5.0",
|
|
40
|
-
"circuit-json-to-spice": "^0.0.
|
|
40
|
+
"circuit-json-to-spice": "^0.0.30",
|
|
41
41
|
"debug": "^4.4.0",
|
|
42
42
|
"performance-now": "^2.1.0",
|
|
43
43
|
"react-chartjs-2": "^5.3.0",
|