@tscircuit/schematic-viewer 2.0.55 → 2.0.57

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.
Files changed (68) hide show
  1. package/dist/index.js +7 -3
  2. package/dist/index.js.map +1 -1
  3. package/package.json +4 -1
  4. package/.github/workflows/bun-formatcheck.yml +0 -26
  5. package/.github/workflows/bun-pver-release.yml +0 -59
  6. package/.github/workflows/bun-typecheck.yml +0 -26
  7. package/.github/workflows/on-merge-inform-release-tracker.yml +0 -24
  8. package/CLAUDE.md +0 -1
  9. package/biome.json +0 -56
  10. package/bun.lockb +0 -0
  11. package/cosmos.config.json +0 -3
  12. package/cosmos.decorator.tsx +0 -3
  13. package/docs/circuit-to-svg-metadata.md +0 -151
  14. package/docs/dragndrop-spec.md +0 -39
  15. package/examples/example1-resistor-and-capacitor.fixture.tsx +0 -16
  16. package/examples/example10-groups-view-schematic-groups.fixture.tsx +0 -76
  17. package/examples/example11-automatic-grouping-view-schematic-groups.fixture.tsx +0 -109
  18. package/examples/example12-spice-boost-converter.fixture.tsx +0 -78
  19. package/examples/example13-disablegroups.fixture.tsx +0 -30
  20. package/examples/example14-schematic-component-click.fixture.tsx +0 -46
  21. package/examples/example15-analog-simulation-viewer.fixture.tsx +0 -145
  22. package/examples/example16-no-analog-simulation.fixture.tsx +0 -13
  23. package/examples/example17-schematic-ports.fixture.tsx +0 -49
  24. package/examples/example2-small-circuit.fixture.tsx +0 -48
  25. package/examples/example3-small-circuit-without-debug-grid.fixture.tsx +0 -44
  26. package/examples/example4-reset-edit-events.fixture.tsx +0 -57
  27. package/examples/example5-circuit-json-rerender.fixture.tsx +0 -110
  28. package/examples/example6-click-to-interact.fixture.tsx +0 -36
  29. package/examples/example7-schematic-viewer-fix-snapping.fixture.tsx +0 -123
  30. package/examples/example8-color-overrides.fixture.tsx +0 -52
  31. package/examples/example9-spice-rc-charging-voltage-divider.fixture.tsx +0 -77
  32. package/index.html +0 -12
  33. package/lib/components/AnalogSimulationViewer.tsx +0 -300
  34. package/lib/components/ControlledSchematicViewer.tsx +0 -40
  35. package/lib/components/EditIcon.tsx +0 -46
  36. package/lib/components/GridIcon.tsx +0 -45
  37. package/lib/components/MouseTracker.tsx +0 -257
  38. package/lib/components/SchematicComponentMouseTarget.tsx +0 -189
  39. package/lib/components/SchematicPortMouseTarget.tsx +0 -224
  40. package/lib/components/SchematicViewer.tsx +0 -582
  41. package/lib/components/SpiceIcon.tsx +0 -14
  42. package/lib/components/SpicePlot.tsx +0 -221
  43. package/lib/components/SpiceSimulationIcon.tsx +0 -32
  44. package/lib/components/SpiceSimulationOverlay.tsx +0 -250
  45. package/lib/components/ViewMenu.tsx +0 -218
  46. package/lib/components/ViewMenuIcon.tsx +0 -47
  47. package/lib/dev/render-to-circuit-json.ts +0 -8
  48. package/lib/hooks/use-resize-handling.ts +0 -35
  49. package/lib/hooks/useChangeSchematicComponentLocationsInSvg.ts +0 -117
  50. package/lib/hooks/useChangeSchematicTracesForMovedComponents.ts +0 -121
  51. package/lib/hooks/useComponentDragging.ts +0 -251
  52. package/lib/hooks/useLocalStorage.ts +0 -63
  53. package/lib/hooks/useMouseEventsOverBoundingBox.ts +0 -74
  54. package/lib/hooks/useSchematicGroupsOverlay.ts +0 -364
  55. package/lib/hooks/useSpiceSimulation.ts +0 -149
  56. package/lib/index.ts +0 -4
  57. package/lib/types/edit-events.ts +0 -16
  58. package/lib/types/eecircuit-engine.d.ts +0 -147
  59. package/lib/utils/debug.ts +0 -9
  60. package/lib/utils/get-component-offset-due-to-events.ts +0 -43
  61. package/lib/utils/spice-utils.ts +0 -128
  62. package/lib/utils/z-index-map.ts +0 -11
  63. package/lib/workers/spice-simulation.worker.ts +0 -51
  64. package/scripts/build-worker-blob-url.ts +0 -55
  65. package/src/main.tsx +0 -21
  66. package/tsconfig.json +0 -33
  67. package/tsup-webworker.config.ts +0 -13
  68. package/vite.config.js +0 -15
@@ -1,221 +0,0 @@
1
- import { useMemo } from "react"
2
- import {
3
- Chart as ChartJS,
4
- type ChartOptions,
5
- CategoryScale,
6
- LinearScale,
7
- PointElement,
8
- LineElement,
9
- Title,
10
- Tooltip,
11
- Legend,
12
- } from "chart.js"
13
- import { Line } from "react-chartjs-2"
14
- import type { PlotPoint } from "../hooks/useSpiceSimulation"
15
-
16
- ChartJS.register(
17
- CategoryScale,
18
- LinearScale,
19
- PointElement,
20
- LineElement,
21
- Title,
22
- Tooltip,
23
- Legend,
24
- )
25
-
26
- const colors = ["#8884d8", "#82ca9d", "#ffc658", "#ff7300", "#387908"]
27
-
28
- const formatTimeWithUnits = (seconds: number) => {
29
- if (seconds === 0) return "0s"
30
- const absSeconds = Math.abs(seconds)
31
-
32
- let unit = "s"
33
- let scale = 1
34
- if (absSeconds < 1e-12) {
35
- unit = "fs"
36
- scale = 1e15
37
- } else if (absSeconds < 1e-9) {
38
- unit = "ps"
39
- scale = 1e12
40
- } else if (absSeconds < 1e-6) {
41
- unit = "ns"
42
- scale = 1e9
43
- } else if (absSeconds < 1e-3) {
44
- unit = "us"
45
- scale = 1e6
46
- } else if (absSeconds < 1) {
47
- unit = "ms"
48
- scale = 1e3
49
- }
50
-
51
- return `${parseFloat((seconds * scale).toPrecision(3))}${unit}`
52
- }
53
-
54
- export const SpicePlot = ({
55
- plotData,
56
- nodes,
57
- isLoading,
58
- error,
59
- hasRun,
60
- }: {
61
- plotData: PlotPoint[]
62
- nodes: string[]
63
- isLoading: boolean
64
- error: string | null
65
- hasRun: boolean
66
- }) => {
67
- const yAxisLabel = useMemo(() => {
68
- const hasVoltage = nodes.some((n) => n.toLowerCase().startsWith("v("))
69
- const hasCurrent = nodes.some((n) => n.toLowerCase().startsWith("i("))
70
- if (hasVoltage && hasCurrent) return "Value"
71
- if (hasVoltage) return "Voltage (V)"
72
- if (hasCurrent) return "Current (A)"
73
- return "Value"
74
- }, [nodes])
75
-
76
- if (isLoading) {
77
- return (
78
- <div
79
- style={{
80
- height: "300px",
81
- width: "100%",
82
- display: "flex",
83
- alignItems: "center",
84
- justifyContent: "center",
85
- }}
86
- >
87
- Running simulation...
88
- </div>
89
- )
90
- }
91
-
92
- if (!hasRun) {
93
- return (
94
- <div
95
- style={{
96
- height: "300px",
97
- width: "100%",
98
- display: "flex",
99
- alignItems: "center",
100
- justifyContent: "center",
101
- }}
102
- >
103
- Click "Run" to start the simulation.
104
- </div>
105
- )
106
- }
107
-
108
- if (error) {
109
- return (
110
- <div
111
- style={{
112
- height: "300px",
113
- width: "100%",
114
- display: "flex",
115
- alignItems: "center",
116
- justifyContent: "center",
117
- color: "red",
118
- }}
119
- >
120
- Error: {error}
121
- </div>
122
- )
123
- }
124
-
125
- if (plotData.length === 0) {
126
- return (
127
- <div
128
- style={{
129
- height: "300px",
130
- width: "100%",
131
- display: "flex",
132
- alignItems: "center",
133
- justifyContent: "center",
134
- }}
135
- >
136
- No data to plot. Check simulation output or SPICE netlist.
137
- </div>
138
- )
139
- }
140
-
141
- const chartData = {
142
- datasets: nodes.map((node, i) => ({
143
- label: node,
144
- data: plotData.map((p) => ({
145
- x: Number(p.name),
146
- y: p[node] as number,
147
- })),
148
- borderColor: colors[i % colors.length],
149
- backgroundColor: colors[i % colors.length],
150
- fill: false,
151
- tension: 0.1,
152
- })),
153
- }
154
-
155
- const options: ChartOptions<"line"> = {
156
- responsive: true,
157
- maintainAspectRatio: false,
158
- plugins: {
159
- legend: {
160
- position: "top" as const,
161
- labels: {
162
- font: {
163
- family: "sans-serif",
164
- },
165
- },
166
- },
167
- title: {
168
- display: false,
169
- },
170
- tooltip: {
171
- callbacks: {
172
- title: (tooltipItems) => {
173
- if (tooltipItems.length > 0) {
174
- const item = tooltipItems[0]
175
- return formatTimeWithUnits(item.parsed.x as number)
176
- }
177
- return ""
178
- },
179
- },
180
- },
181
- },
182
- scales: {
183
- x: {
184
- type: "linear",
185
- title: {
186
- display: true,
187
- text: "Time",
188
- font: {
189
- family: "sans-serif",
190
- },
191
- },
192
- ticks: {
193
- callback: (value) => formatTimeWithUnits(value as number),
194
- font: {
195
- family: "sans-serif",
196
- },
197
- },
198
- },
199
- y: {
200
- title: {
201
- display: true,
202
- text: yAxisLabel,
203
- font: {
204
- family: "sans-serif",
205
- },
206
- },
207
- ticks: {
208
- font: {
209
- family: "sans-serif",
210
- },
211
- },
212
- },
213
- },
214
- }
215
-
216
- return (
217
- <div style={{ position: "relative", height: "300px", width: "100%" }}>
218
- <Line options={options} data={chartData} />
219
- </div>
220
- )
221
- }
@@ -1,32 +0,0 @@
1
- import { SpiceIcon } from "./SpiceIcon"
2
- import { zIndexMap } from "../utils/z-index-map"
3
-
4
- export const SpiceSimulationIcon = ({
5
- onClick,
6
- }: {
7
- onClick: () => void
8
- }) => {
9
- return (
10
- <div
11
- onClick={onClick}
12
- title="Run SPICE simulation"
13
- style={{
14
- position: "absolute",
15
- top: "16px",
16
- right: "112px",
17
- backgroundColor: "#fff",
18
- color: "#000",
19
- padding: "8px",
20
- borderRadius: "4px",
21
- cursor: "pointer",
22
- boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
23
- display: "flex",
24
- alignItems: "center",
25
- gap: "4px",
26
- zIndex: zIndexMap.spiceSimulationIcon,
27
- }}
28
- >
29
- <SpiceIcon />
30
- </div>
31
- )
32
- }
@@ -1,250 +0,0 @@
1
- import { SpicePlot } from "./SpicePlot"
2
- import type { PlotPoint } from "../hooks/useSpiceSimulation"
3
- import { useEffect, useState } from "react"
4
-
5
- interface SpiceSimulationOverlayProps {
6
- spiceString: string | null
7
- onClose: () => void
8
- plotData: PlotPoint[]
9
- nodes: string[]
10
- isLoading: boolean
11
- error: string | null
12
- simOptions: {
13
- showVoltage: boolean
14
- showCurrent: boolean
15
- startTime: number
16
- duration: number
17
- }
18
- onSimOptionsChange: (
19
- options: SpiceSimulationOverlayProps["simOptions"],
20
- ) => void
21
- hasRun: boolean
22
- }
23
-
24
- export const SpiceSimulationOverlay = ({
25
- spiceString,
26
- onClose,
27
- plotData,
28
- nodes,
29
- isLoading,
30
- error,
31
- simOptions,
32
- onSimOptionsChange,
33
- hasRun,
34
- }: SpiceSimulationOverlayProps) => {
35
- const [startTimeDraft, setStartTimeDraft] = useState(
36
- String(simOptions.startTime),
37
- )
38
- const [durationDraft, setDurationDraft] = useState(
39
- String(simOptions.duration),
40
- )
41
-
42
- useEffect(() => {
43
- setStartTimeDraft(String(simOptions.startTime))
44
- setDurationDraft(String(simOptions.duration))
45
- }, [simOptions.startTime, simOptions.duration])
46
-
47
- const handleRerun = () => {
48
- onSimOptionsChange({
49
- ...simOptions,
50
- startTime: Number(startTimeDraft),
51
- duration: Number(durationDraft),
52
- })
53
- }
54
-
55
- const filteredNodes = nodes.filter((node) => {
56
- const isVoltage = node.toLowerCase().startsWith("v(")
57
- const isCurrent = node.toLowerCase().startsWith("i(")
58
- if (simOptions.showVoltage && isVoltage) return true
59
- if (simOptions.showCurrent && isCurrent) return true
60
- return false
61
- })
62
-
63
- return (
64
- <div
65
- style={{
66
- position: "fixed",
67
- top: 0,
68
- left: 0,
69
- right: 0,
70
- bottom: 0,
71
- backgroundColor: "rgba(0, 0, 0, 0.5)",
72
- display: "flex",
73
- alignItems: "center",
74
- justifyContent: "center",
75
- zIndex: 1002,
76
- fontFamily: "sans-serif",
77
- }}
78
- >
79
- <div
80
- style={{
81
- backgroundColor: "white",
82
- padding: "24px",
83
- borderRadius: "12px",
84
- width: "90%",
85
- maxWidth: "900px",
86
- boxShadow: "0 4px 20px rgba(0, 0, 0, 0.15)",
87
- }}
88
- >
89
- <div
90
- style={{
91
- display: "flex",
92
- justifyContent: "space-between",
93
- alignItems: "center",
94
- marginBottom: "24px",
95
- borderBottom: "1px solid #eee",
96
- paddingBottom: "16px",
97
- }}
98
- >
99
- <h2
100
- style={{
101
- margin: 0,
102
- fontSize: "22px",
103
- fontWeight: 600,
104
- color: "#333",
105
- }}
106
- >
107
- SPICE Simulation
108
- </h2>
109
- <button
110
- onClick={onClose}
111
- style={{
112
- background: "none",
113
- border: "none",
114
- fontSize: "28px",
115
- cursor: "pointer",
116
- color: "#888",
117
- padding: 0,
118
- lineHeight: 1,
119
- }}
120
- >
121
- &times;
122
- </button>
123
- </div>
124
- <div>
125
- <SpicePlot
126
- plotData={plotData}
127
- nodes={filteredNodes}
128
- isLoading={isLoading}
129
- error={error}
130
- hasRun={hasRun}
131
- />
132
- </div>
133
- <div
134
- style={{
135
- marginTop: "16px",
136
- padding: "12px",
137
- backgroundColor: "#f7f7f7",
138
- borderRadius: "6px",
139
- display: "flex",
140
- flexWrap: "wrap",
141
- gap: "24px",
142
- alignItems: "center",
143
- fontSize: "14px",
144
- }}
145
- >
146
- <div style={{ display: "flex", gap: "16px" }}>
147
- <label
148
- style={{ display: "flex", alignItems: "center", gap: "6px" }}
149
- >
150
- <input
151
- type="checkbox"
152
- checked={simOptions.showVoltage}
153
- onChange={(e) =>
154
- onSimOptionsChange({
155
- ...simOptions,
156
- showVoltage: e.target.checked,
157
- })
158
- }
159
- />
160
- Voltage
161
- </label>
162
- <label
163
- style={{ display: "flex", alignItems: "center", gap: "6px" }}
164
- >
165
- <input
166
- type="checkbox"
167
- checked={simOptions.showCurrent}
168
- onChange={(e) =>
169
- onSimOptionsChange({
170
- ...simOptions,
171
- showCurrent: e.target.checked,
172
- })
173
- }
174
- />
175
- Current
176
- </label>
177
- </div>
178
- <div style={{ display: "flex", gap: "16px", alignItems: "center" }}>
179
- <label htmlFor="startTime">Start Time (ms):</label>
180
- <input
181
- id="startTime"
182
- type="number"
183
- value={startTimeDraft}
184
- onChange={(e) => setStartTimeDraft(e.target.value)}
185
- style={{
186
- width: "80px",
187
- padding: "4px 8px",
188
- borderRadius: "4px",
189
- border: "1px solid #ccc",
190
- }}
191
- />
192
- <label htmlFor="duration">Duration (ms):</label>
193
- <input
194
- id="duration"
195
- type="number"
196
- value={durationDraft}
197
- onChange={(e) => setDurationDraft(e.target.value)}
198
- style={{
199
- width: "80px",
200
- padding: "4px 8px",
201
- borderRadius: "4px",
202
- border: "1px solid #ccc",
203
- }}
204
- />
205
- <button
206
- onClick={handleRerun}
207
- style={{
208
- padding: "4px 12px",
209
- borderRadius: "4px",
210
- border: "1px solid #ccc",
211
- backgroundColor: "#f0f0f0",
212
- cursor: "pointer",
213
- }}
214
- >
215
- {hasRun ? "Rerun" : "Run"}
216
- </button>
217
- </div>
218
- </div>
219
- <div style={{ marginTop: "24px" }}>
220
- <h3
221
- style={{
222
- marginTop: 0,
223
- marginBottom: "12px",
224
- fontSize: "18px",
225
- fontWeight: 600,
226
- color: "#333",
227
- }}
228
- >
229
- SPICE Netlist
230
- </h3>
231
- <pre
232
- style={{
233
- backgroundColor: "#fafafa",
234
- padding: "16px",
235
- borderRadius: "6px",
236
- maxHeight: "150px",
237
- overflowY: "auto",
238
- border: "1px solid #eee",
239
- color: "#333",
240
- fontSize: "13px",
241
- fontFamily: "monospace",
242
- }}
243
- >
244
- {spiceString}
245
- </pre>
246
- </div>
247
- </div>
248
- </div>
249
- )
250
- }
@@ -1,218 +0,0 @@
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
- import packageJson from "../../package.json"
6
-
7
- interface ViewMenuProps {
8
- circuitJson: CircuitJson
9
- circuitJsonKey: string
10
- isVisible: boolean
11
- onClose: () => void
12
- showGroups: boolean
13
- onToggleGroups: (show: boolean) => void
14
- showGrid: boolean
15
- onToggleGrid: (show: boolean) => void
16
- }
17
-
18
- export const ViewMenu = ({
19
- circuitJson,
20
- circuitJsonKey,
21
- isVisible,
22
- onClose,
23
- showGroups,
24
- onToggleGroups,
25
- showGrid,
26
- onToggleGrid,
27
- }: ViewMenuProps) => {
28
- const hasGroups = useMemo(() => {
29
- if (!circuitJson || circuitJson.length === 0) return false
30
-
31
- try {
32
- // Check if there are explicit groups
33
- const sourceGroups = su(circuitJson).source_group?.list() || []
34
- if (sourceGroups.length > 0) return true
35
-
36
- // Check if we can create virtual groups by component type
37
- const schematicComponents =
38
- su(circuitJson).schematic_component?.list() || []
39
- if (schematicComponents.length > 1) {
40
- const componentTypes = new Set()
41
- for (const comp of schematicComponents) {
42
- const sourceComp = su(circuitJson).source_component.get(
43
- comp.source_component_id!,
44
- )
45
- if (sourceComp?.ftype) {
46
- componentTypes.add(sourceComp.ftype)
47
- }
48
- }
49
- return componentTypes.size > 1 // Only show if there are multiple types
50
- }
51
-
52
- return false
53
- } catch (error) {
54
- console.error("Error checking for groups:", error)
55
- return false
56
- }
57
- }, [circuitJsonKey])
58
-
59
- if (!isVisible) return null
60
-
61
- return (
62
- <>
63
- {/* Backdrop */}
64
- <div
65
- onClick={onClose}
66
- onTouchEnd={(e) => {
67
- e.preventDefault()
68
- onClose()
69
- }}
70
- style={{
71
- position: "absolute",
72
- inset: 0,
73
- backgroundColor: "transparent",
74
- zIndex: zIndexMap.viewMenuBackdrop,
75
- }}
76
- />
77
-
78
- {/* Menu */}
79
- <div
80
- style={{
81
- position: "absolute",
82
- top: "56px",
83
- right: "16px",
84
- backgroundColor: "#ffffff",
85
- color: "#000000",
86
- border: "1px solid #ccc",
87
- borderRadius: "4px",
88
- boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
89
- minWidth: "200px",
90
- zIndex: zIndexMap.viewMenu,
91
- }}
92
- >
93
- {/* Groups Toggle Option */}
94
- <div
95
- onClick={() => {
96
- if (hasGroups) {
97
- onToggleGroups(!showGroups)
98
- }
99
- }}
100
- onTouchEnd={(e) => {
101
- e.preventDefault()
102
- if (hasGroups) {
103
- onToggleGroups(!showGroups)
104
- }
105
- }}
106
- style={{
107
- padding: "8px 12px",
108
- cursor: hasGroups ? "pointer" : "not-allowed",
109
- opacity: hasGroups ? 1 : 0.5,
110
- fontSize: "13px",
111
- color: "#000000",
112
- fontFamily: "sans-serif",
113
- display: "flex",
114
- alignItems: "center",
115
- gap: "8px",
116
- }}
117
- onMouseEnter={(e) => {
118
- if (hasGroups) {
119
- e.currentTarget.style.backgroundColor = "#f0f0f0"
120
- }
121
- }}
122
- onMouseLeave={(e) => {
123
- if (hasGroups) {
124
- e.currentTarget.style.backgroundColor = "transparent"
125
- }
126
- }}
127
- >
128
- <div
129
- style={{
130
- width: "16px",
131
- height: "16px",
132
- border: "2px solid #000",
133
- borderRadius: "2px",
134
- backgroundColor: "transparent",
135
- display: "flex",
136
- alignItems: "center",
137
- justifyContent: "center",
138
- fontSize: "10px",
139
- fontWeight: "bold",
140
- }}
141
- >
142
- {showGroups && "✓"}
143
- </div>
144
- View Schematic Groups
145
- </div>
146
-
147
- {!hasGroups && (
148
- <div
149
- style={{
150
- padding: "8px 12px",
151
- fontSize: "11px",
152
- color: "#666",
153
- fontStyle: "italic",
154
- }}
155
- >
156
- No groups found in this schematic
157
- </div>
158
- )}
159
-
160
- {/* Grid Toggle Option */}
161
- <div
162
- onClick={() => onToggleGrid(!showGrid)}
163
- onTouchEnd={(e) => {
164
- e.preventDefault()
165
- onToggleGrid(!showGrid)
166
- }}
167
- style={{
168
- padding: "8px 12px",
169
- cursor: "pointer",
170
- fontSize: "13px",
171
- color: "#000000",
172
- fontFamily: "sans-serif",
173
- display: "flex",
174
- alignItems: "center",
175
- gap: "8px",
176
- }}
177
- onMouseEnter={(e) => {
178
- e.currentTarget.style.backgroundColor = "#f0f0f0"
179
- }}
180
- onMouseLeave={(e) => {
181
- e.currentTarget.style.backgroundColor = "transparent"
182
- }}
183
- >
184
- <div
185
- style={{
186
- width: "16px",
187
- height: "16px",
188
- border: "2px solid #000",
189
- borderRadius: "2px",
190
- backgroundColor: "transparent",
191
- display: "flex",
192
- alignItems: "center",
193
- justifyContent: "center",
194
- fontSize: "10px",
195
- fontWeight: "bold",
196
- }}
197
- >
198
- {showGrid && "✓"}
199
- </div>
200
- Show Grid
201
- </div>
202
-
203
- {/* Version Info */}
204
- <div
205
- style={{
206
- padding: "4px 8px",
207
- fontSize: "12px",
208
- color: "#999",
209
- borderTop: "1px solid #eee",
210
- textAlign: "center",
211
- }}
212
- >
213
- v{String(packageJson?.version)}
214
- </div>
215
- </div>
216
- </>
217
- )
218
- }