@tscircuit/runframe 0.0.11 → 0.0.12
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/README.md +31 -1
- package/package.json +10 -2
- package/.github/workflows/bun-formatcheck.yml +0 -26
- package/.github/workflows/bun-pver-release.yml +0 -25
- package/.github/workflows/bun-typecheck.yml +0 -26
- package/biome.json +0 -57
- package/bun.lockb +0 -0
- package/cosmos.config.json +0 -3
- package/cosmos.decorator.tsx +0 -3
- package/examples/resistor.fixture.tsx +0 -12
- package/examples/runframe-with-api-1.fixture.tsx +0 -41
- package/examples/runframe1-basic.fixture.tsx +0 -19
- package/index.html +0 -13
- package/lib/components/BomTable.tsx +0 -69
- package/lib/components/CircuitJsonPreview.tsx +0 -348
- package/lib/components/CircuitJsonTableViewer/CircuitJsonTableViewer.tsx +0 -315
- package/lib/components/CircuitJsonTableViewer/ClickableText.tsx +0 -20
- package/lib/components/CircuitJsonTableViewer/HeaderCell.tsx +0 -26
- package/lib/components/CircuitJsonTableViewer/Modal.tsx +0 -38
- package/lib/components/ErrorFallback.tsx +0 -25
- package/lib/components/ErrorTabContent.tsx +0 -86
- package/lib/components/PcbViewerWithContainerHeight.tsx +0 -47
- package/lib/components/PreviewEmptyState.tsx +0 -14
- package/lib/components/RunFrame.tsx +0 -68
- package/lib/components/RunFrameWithApi/index.tsx +0 -44
- package/lib/components/RunFrameWithApi/standalone.tsx +0 -6
- package/lib/components/RunFrameWithApi/store.ts +0 -122
- package/lib/components/RunFrameWithApi/types.ts +0 -43
- package/lib/components/ui/button.tsx +0 -57
- package/lib/components/ui/dropdown-menu.tsx +0 -203
- package/lib/components/ui/tabs.tsx +0 -53
- package/lib/dev/render-to-circuit-json.ts +0 -7
- package/lib/hooks/use-styles.ts +0 -17
- package/lib/preview.ts +0 -1
- package/lib/runner.ts +0 -2
- package/lib/utils/index.ts +0 -6
- package/lib/utils/pcbManualEditEventHandler.ts +0 -156
- package/postcss.config.js +0 -8
- package/renovate.json +0 -15
- package/scripts/build-css.ts +0 -26
- package/src/main.tsx +0 -13
- package/tailwind.config.js +0 -3
- package/tsconfig.json +0 -45
- package/vite.config.ts +0 -72
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Tabs,
|
|
3
|
-
TabsContent,
|
|
4
|
-
TabsList,
|
|
5
|
-
TabsTrigger,
|
|
6
|
-
} from "lib/components/ui/tabs"
|
|
7
|
-
import { cn } from "lib/utils"
|
|
8
|
-
import { applyPcbEditEvents } from "lib/utils/pcbManualEditEventHandler"
|
|
9
|
-
import { CadViewer } from "@tscircuit/3d-viewer"
|
|
10
|
-
import { PCBViewer } from "@tscircuit/pcb-viewer"
|
|
11
|
-
import { useEffect, useState } from "react"
|
|
12
|
-
import { ErrorFallback } from "./ErrorFallback"
|
|
13
|
-
import { ErrorBoundary } from "react-error-boundary"
|
|
14
|
-
import { ErrorTabContent } from "./ErrorTabContent"
|
|
15
|
-
import { SchematicViewer } from "@tscircuit/schematic-viewer"
|
|
16
|
-
import PreviewEmptyState from "./PreviewEmptyState"
|
|
17
|
-
import { CircuitJsonTableViewer } from "./CircuitJsonTableViewer/CircuitJsonTableViewer"
|
|
18
|
-
import { BomTable } from "./BomTable"
|
|
19
|
-
import {
|
|
20
|
-
CheckIcon,
|
|
21
|
-
EllipsisIcon,
|
|
22
|
-
EllipsisVerticalIcon,
|
|
23
|
-
FullscreenIcon,
|
|
24
|
-
MinimizeIcon,
|
|
25
|
-
} from "lucide-react"
|
|
26
|
-
import {
|
|
27
|
-
DropdownMenu,
|
|
28
|
-
DropdownMenuContent,
|
|
29
|
-
DropdownMenuItem,
|
|
30
|
-
DropdownMenuTrigger,
|
|
31
|
-
} from "./ui/dropdown-menu"
|
|
32
|
-
import { Button } from "./ui/button"
|
|
33
|
-
import { PcbViewerWithContainerHeight } from "./PcbViewerWithContainerHeight"
|
|
34
|
-
import { useStyles } from "lib/hooks/use-styles"
|
|
35
|
-
|
|
36
|
-
export interface PreviewContentProps {
|
|
37
|
-
code?: string
|
|
38
|
-
readOnly?: boolean
|
|
39
|
-
onRunClicked?: () => void
|
|
40
|
-
tsxRunTriggerCount?: number
|
|
41
|
-
errorMessage?: string | null
|
|
42
|
-
circuitJson: any
|
|
43
|
-
circuitJsonKey?: string
|
|
44
|
-
className?: string
|
|
45
|
-
showCodeTab?: boolean
|
|
46
|
-
codeTabContent?: React.ReactNode
|
|
47
|
-
showJsonTab?: boolean
|
|
48
|
-
showImportAndFormatButtons?: boolean
|
|
49
|
-
headerClassName?: string
|
|
50
|
-
leftHeaderContent?: React.ReactNode
|
|
51
|
-
isRunningCode?: boolean
|
|
52
|
-
isStreaming?: boolean
|
|
53
|
-
onToggleFullScreen?: () => void
|
|
54
|
-
isFullScreen?: boolean
|
|
55
|
-
onCodeChange?: (code: string) => void
|
|
56
|
-
onDtsChange?: (dts: string) => void
|
|
57
|
-
manualEditsFileContent?: string
|
|
58
|
-
hasCodeChangedSinceLastRun?: boolean
|
|
59
|
-
onManualEditsFileContentChange?: (newmanualEditsFileContent: string) => void
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export const CircuitJsonPreview = ({
|
|
63
|
-
code,
|
|
64
|
-
onRunClicked = () => {},
|
|
65
|
-
tsxRunTriggerCount,
|
|
66
|
-
errorMessage,
|
|
67
|
-
circuitJsonKey = "",
|
|
68
|
-
circuitJson,
|
|
69
|
-
showCodeTab = false,
|
|
70
|
-
codeTabContent,
|
|
71
|
-
showJsonTab = true,
|
|
72
|
-
showImportAndFormatButtons = true,
|
|
73
|
-
className,
|
|
74
|
-
headerClassName,
|
|
75
|
-
leftHeaderContent,
|
|
76
|
-
readOnly,
|
|
77
|
-
isStreaming,
|
|
78
|
-
onToggleFullScreen,
|
|
79
|
-
isFullScreen,
|
|
80
|
-
isRunningCode,
|
|
81
|
-
hasCodeChangedSinceLastRun,
|
|
82
|
-
onCodeChange,
|
|
83
|
-
onDtsChange,
|
|
84
|
-
manualEditsFileContent,
|
|
85
|
-
onManualEditsFileContentChange,
|
|
86
|
-
}: PreviewContentProps) => {
|
|
87
|
-
useStyles()
|
|
88
|
-
const [activeTab, setActiveTab] = useState(showCodeTab ? "code" : "pcb")
|
|
89
|
-
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
if (errorMessage) {
|
|
92
|
-
setActiveTab("error")
|
|
93
|
-
}
|
|
94
|
-
}, [errorMessage])
|
|
95
|
-
|
|
96
|
-
useEffect(() => {
|
|
97
|
-
if (activeTab === "code" && circuitJson && !errorMessage) {
|
|
98
|
-
setActiveTab("pcb")
|
|
99
|
-
}
|
|
100
|
-
}, [circuitJson])
|
|
101
|
-
|
|
102
|
-
return (
|
|
103
|
-
<div className={cn("flex flex-col relative", className)}>
|
|
104
|
-
<div className="md:sticky md:top-2">
|
|
105
|
-
<Tabs
|
|
106
|
-
value={activeTab}
|
|
107
|
-
onValueChange={setActiveTab}
|
|
108
|
-
className="flex-grow flex flex-col p-2"
|
|
109
|
-
>
|
|
110
|
-
<div className={cn("flex items-center gap-2", headerClassName)}>
|
|
111
|
-
{leftHeaderContent}
|
|
112
|
-
{leftHeaderContent && <div className="flex-grow" />}
|
|
113
|
-
{/* <RunButton
|
|
114
|
-
onClick={() => triggerRunTsx()}
|
|
115
|
-
disabled={!hasCodeChangedSinceLastRun && tsxRunTriggerCount !== 0}
|
|
116
|
-
isRunningCode={isRunningCode}
|
|
117
|
-
/> */}
|
|
118
|
-
{!leftHeaderContent && <div className="flex-grow" />}
|
|
119
|
-
<TabsList>
|
|
120
|
-
{showCodeTab && <TabsTrigger value="code">Code</TabsTrigger>}
|
|
121
|
-
<TabsTrigger value="pcb" className="whitespace-nowrap">
|
|
122
|
-
{circuitJson && (
|
|
123
|
-
<span
|
|
124
|
-
className={cn(
|
|
125
|
-
"inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
|
|
126
|
-
!hasCodeChangedSinceLastRun
|
|
127
|
-
? "bg-blue-500"
|
|
128
|
-
: "bg-gray-500",
|
|
129
|
-
)}
|
|
130
|
-
/>
|
|
131
|
-
)}
|
|
132
|
-
PCB
|
|
133
|
-
</TabsTrigger>
|
|
134
|
-
<TabsTrigger value="schematic" className="whitespace-nowrap">
|
|
135
|
-
{circuitJson && (
|
|
136
|
-
<span
|
|
137
|
-
className={cn(
|
|
138
|
-
"inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
|
|
139
|
-
!hasCodeChangedSinceLastRun
|
|
140
|
-
? "bg-blue-500"
|
|
141
|
-
: "bg-gray-500",
|
|
142
|
-
)}
|
|
143
|
-
/>
|
|
144
|
-
)}
|
|
145
|
-
Schematic
|
|
146
|
-
</TabsTrigger>
|
|
147
|
-
<TabsTrigger value="cad">
|
|
148
|
-
{circuitJson && (
|
|
149
|
-
<span
|
|
150
|
-
className={cn(
|
|
151
|
-
"inline-flex items-center justify-center w-2 h-2 mr-1 text-xs font-bold text-white rounded-full",
|
|
152
|
-
!hasCodeChangedSinceLastRun
|
|
153
|
-
? "bg-blue-500"
|
|
154
|
-
: "bg-gray-500",
|
|
155
|
-
)}
|
|
156
|
-
/>
|
|
157
|
-
)}
|
|
158
|
-
3D
|
|
159
|
-
</TabsTrigger>
|
|
160
|
-
<DropdownMenu>
|
|
161
|
-
<DropdownMenuTrigger asChild>
|
|
162
|
-
<div className="whitespace-nowrap p-2 mr-1 cursor-pointer relative">
|
|
163
|
-
<EllipsisIcon className="w-4 h-4" />
|
|
164
|
-
{errorMessage && (
|
|
165
|
-
<span className="inline-flex absolute top-[6px] right-[4px] items-center justify-center w-1 h-1 ml-2 text-[8px] font-bold text-white bg-red-500 rounded-full" />
|
|
166
|
-
)}
|
|
167
|
-
</div>
|
|
168
|
-
</DropdownMenuTrigger>
|
|
169
|
-
<DropdownMenuContent className="*:text-xs">
|
|
170
|
-
<DropdownMenuItem
|
|
171
|
-
onSelect={() => setActiveTab("error")}
|
|
172
|
-
className="flex"
|
|
173
|
-
>
|
|
174
|
-
<CheckIcon
|
|
175
|
-
className={cn(
|
|
176
|
-
"w-3 h-3 mr-2",
|
|
177
|
-
activeTab !== "error" && "invisible",
|
|
178
|
-
)}
|
|
179
|
-
/>
|
|
180
|
-
<div className="flex-grow">Errors</div>
|
|
181
|
-
{errorMessage && (
|
|
182
|
-
<span className="inline-flex items-center justify-center w-3 h-3 ml-2 text-[8px] font-bold text-white bg-red-500 rounded-full">
|
|
183
|
-
1
|
|
184
|
-
</span>
|
|
185
|
-
)}
|
|
186
|
-
</DropdownMenuItem>
|
|
187
|
-
<DropdownMenuItem onSelect={() => setActiveTab("bom")}>
|
|
188
|
-
<CheckIcon
|
|
189
|
-
className={cn(
|
|
190
|
-
"w-3 h-3 mr-2",
|
|
191
|
-
activeTab !== "bom" && "invisible",
|
|
192
|
-
)}
|
|
193
|
-
/>
|
|
194
|
-
Bill of Materials
|
|
195
|
-
</DropdownMenuItem>
|
|
196
|
-
<DropdownMenuItem
|
|
197
|
-
onSelect={() => setActiveTab("circuitjson")}
|
|
198
|
-
>
|
|
199
|
-
<CheckIcon
|
|
200
|
-
className={cn(
|
|
201
|
-
"w-3 h-3 mr-2",
|
|
202
|
-
activeTab !== "circuitjson" && "invisible",
|
|
203
|
-
)}
|
|
204
|
-
/>
|
|
205
|
-
JSON
|
|
206
|
-
</DropdownMenuItem>
|
|
207
|
-
</DropdownMenuContent>
|
|
208
|
-
</DropdownMenu>
|
|
209
|
-
</TabsList>
|
|
210
|
-
{onToggleFullScreen && (
|
|
211
|
-
<Button onClick={onToggleFullScreen} variant="ghost">
|
|
212
|
-
{isFullScreen ? (
|
|
213
|
-
<MinimizeIcon size={16} />
|
|
214
|
-
) : (
|
|
215
|
-
<FullscreenIcon size={16} />
|
|
216
|
-
)}
|
|
217
|
-
</Button>
|
|
218
|
-
)}
|
|
219
|
-
</div>
|
|
220
|
-
{showCodeTab && (
|
|
221
|
-
<TabsContent value="code" className="flex-grow overflow-hidden">
|
|
222
|
-
<div className="h-full">{codeTabContent}</div>
|
|
223
|
-
</TabsContent>
|
|
224
|
-
)}
|
|
225
|
-
|
|
226
|
-
<TabsContent value="pcb">
|
|
227
|
-
<div
|
|
228
|
-
className={cn(
|
|
229
|
-
"overflow-hidden",
|
|
230
|
-
isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
|
|
231
|
-
)}
|
|
232
|
-
>
|
|
233
|
-
<ErrorBoundary fallback={<div>Error loading PCB viewer</div>}>
|
|
234
|
-
{circuitJson ? (
|
|
235
|
-
<PcbViewerWithContainerHeight
|
|
236
|
-
key={circuitJsonKey}
|
|
237
|
-
soup={circuitJson}
|
|
238
|
-
containerClassName={cn(
|
|
239
|
-
"h-full w-full",
|
|
240
|
-
isFullScreen
|
|
241
|
-
? "min-h-[calc(100vh-240px)]"
|
|
242
|
-
: "min-h-[620px]",
|
|
243
|
-
)}
|
|
244
|
-
// onEditEventsChanged={(editEvents) => {
|
|
245
|
-
// if (editEvents.some((editEvent) => editEvent.in_progress))
|
|
246
|
-
// return
|
|
247
|
-
// // Update state with new edit events
|
|
248
|
-
// const newManualEditsFileContent = applyPcbEditEvents({
|
|
249
|
-
// editEvents,
|
|
250
|
-
// circuitJson,
|
|
251
|
-
// manualEditsFileContent,
|
|
252
|
-
// })
|
|
253
|
-
// onManualEditsFileContentChange?.(
|
|
254
|
-
// JSON.stringify(newManualEditsFileContent, null, 2),
|
|
255
|
-
// )
|
|
256
|
-
// }}
|
|
257
|
-
/>
|
|
258
|
-
) : (
|
|
259
|
-
<PreviewEmptyState onRunClicked={onRunClicked} />
|
|
260
|
-
)}
|
|
261
|
-
</ErrorBoundary>
|
|
262
|
-
</div>
|
|
263
|
-
</TabsContent>
|
|
264
|
-
|
|
265
|
-
<TabsContent value="schematic">
|
|
266
|
-
<div
|
|
267
|
-
className={cn(
|
|
268
|
-
"overflow-auto",
|
|
269
|
-
isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
|
|
270
|
-
)}
|
|
271
|
-
>
|
|
272
|
-
<ErrorBoundary fallback={<div>Error loading Schematic</div>}>
|
|
273
|
-
{circuitJson ? (
|
|
274
|
-
<SchematicViewer
|
|
275
|
-
circuitJson={circuitJson}
|
|
276
|
-
containerStyle={{
|
|
277
|
-
height: "100%",
|
|
278
|
-
}}
|
|
279
|
-
/>
|
|
280
|
-
) : (
|
|
281
|
-
<PreviewEmptyState onRunClicked={onRunClicked} />
|
|
282
|
-
)}
|
|
283
|
-
</ErrorBoundary>
|
|
284
|
-
</div>
|
|
285
|
-
</TabsContent>
|
|
286
|
-
|
|
287
|
-
<TabsContent value="cad">
|
|
288
|
-
<div
|
|
289
|
-
className={cn(
|
|
290
|
-
"overflow-auto",
|
|
291
|
-
isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
|
|
292
|
-
)}
|
|
293
|
-
>
|
|
294
|
-
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
|
295
|
-
{circuitJson ? (
|
|
296
|
-
<CadViewer soup={circuitJson as any} />
|
|
297
|
-
) : (
|
|
298
|
-
<PreviewEmptyState onRunClicked={onRunClicked} />
|
|
299
|
-
)}
|
|
300
|
-
</ErrorBoundary>
|
|
301
|
-
</div>
|
|
302
|
-
</TabsContent>
|
|
303
|
-
|
|
304
|
-
<TabsContent value="bom">
|
|
305
|
-
<div
|
|
306
|
-
className={cn(
|
|
307
|
-
"overflow-auto",
|
|
308
|
-
isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
|
|
309
|
-
)}
|
|
310
|
-
>
|
|
311
|
-
<ErrorBoundary fallback={<div>Error loading BOM</div>}>
|
|
312
|
-
{circuitJson ? (
|
|
313
|
-
<BomTable circuitJson={circuitJson} />
|
|
314
|
-
) : (
|
|
315
|
-
<PreviewEmptyState onRunClicked={onRunClicked} />
|
|
316
|
-
)}
|
|
317
|
-
</ErrorBoundary>
|
|
318
|
-
</div>
|
|
319
|
-
</TabsContent>
|
|
320
|
-
|
|
321
|
-
<TabsContent value="circuitjson">
|
|
322
|
-
<div
|
|
323
|
-
className={cn(
|
|
324
|
-
"overflow-auto",
|
|
325
|
-
isFullScreen ? "h-[calc(100vh-96px)]" : "h-[620px]",
|
|
326
|
-
)}
|
|
327
|
-
>
|
|
328
|
-
<ErrorBoundary fallback={<div>Error loading JSON viewer</div>}>
|
|
329
|
-
{circuitJson ? (
|
|
330
|
-
<CircuitJsonTableViewer elements={circuitJson as any} />
|
|
331
|
-
) : (
|
|
332
|
-
<PreviewEmptyState onRunClicked={onRunClicked} />
|
|
333
|
-
)}
|
|
334
|
-
</ErrorBoundary>
|
|
335
|
-
</div>
|
|
336
|
-
</TabsContent>
|
|
337
|
-
<TabsContent value="error">
|
|
338
|
-
{circuitJson || errorMessage ? (
|
|
339
|
-
<ErrorTabContent code={code} errorMessage={errorMessage} />
|
|
340
|
-
) : (
|
|
341
|
-
<PreviewEmptyState onRunClicked={onRunClicked} />
|
|
342
|
-
)}
|
|
343
|
-
</TabsContent>
|
|
344
|
-
</Tabs>
|
|
345
|
-
</div>
|
|
346
|
-
</div>
|
|
347
|
-
)
|
|
348
|
-
}
|
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
import { useReducer, useState } from "react"
|
|
2
|
-
import { ClickableText } from "./ClickableText"
|
|
3
|
-
import { HeaderCell } from "./HeaderCell"
|
|
4
|
-
import Modal from "./Modal"
|
|
5
|
-
|
|
6
|
-
type Filters = {
|
|
7
|
-
component_type_filter?:
|
|
8
|
-
| "any"
|
|
9
|
-
| "source"
|
|
10
|
-
| "source/pcb"
|
|
11
|
-
| "source/schematic"
|
|
12
|
-
| string
|
|
13
|
-
id_search?: string
|
|
14
|
-
name_search?: string
|
|
15
|
-
selector_search?: string
|
|
16
|
-
focused_id?: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
type CommonProps = { name?: string; type: string }
|
|
20
|
-
|
|
21
|
-
type ModalState =
|
|
22
|
-
| { open: false; element?: never }
|
|
23
|
-
| { open: true; element: Element; title: string }
|
|
24
|
-
|
|
25
|
-
interface Element {
|
|
26
|
-
[key: string]: any
|
|
27
|
-
type: string
|
|
28
|
-
name?: string
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface ProcessedElement extends CommonProps {
|
|
32
|
-
primary_id: string
|
|
33
|
-
other_ids: { [key: string]: string }
|
|
34
|
-
selector_path?: string
|
|
35
|
-
_og_elm: Element
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface Column {
|
|
39
|
-
key: string
|
|
40
|
-
name: string
|
|
41
|
-
renderCell?: (row: ProcessedElement) => React.ReactNode
|
|
42
|
-
renderHeaderCell?: (col: Column) => React.ReactNode
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export const CircuitJsonTableViewer: React.FC<{ elements: Element[] }> = ({
|
|
46
|
-
elements,
|
|
47
|
-
}) => {
|
|
48
|
-
const [modal, setModal] = useState<ModalState>({ open: false })
|
|
49
|
-
const [filters, setFilter] = useReducer(
|
|
50
|
-
(s: Filters, a: Filters) => ({
|
|
51
|
-
...s,
|
|
52
|
-
...a,
|
|
53
|
-
}),
|
|
54
|
-
{},
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
const element_types = [...new Set(elements.map((e) => e.type))]
|
|
58
|
-
|
|
59
|
-
// Process elements to separate primary and non-primary ids
|
|
60
|
-
const elements2: ProcessedElement[] = elements.map((e) => {
|
|
61
|
-
const primary_id = e[`${e.type}_id`]
|
|
62
|
-
|
|
63
|
-
const other_ids = Object.fromEntries(
|
|
64
|
-
Object.entries(e).filter(([k]) => {
|
|
65
|
-
if (k === `${e.type}_id`) return false
|
|
66
|
-
if (!k.endsWith("_id")) return false
|
|
67
|
-
return true
|
|
68
|
-
}),
|
|
69
|
-
) as { [key: string]: string }
|
|
70
|
-
|
|
71
|
-
const other_props: CommonProps = Object.fromEntries(
|
|
72
|
-
Object.entries(e).filter(([k]) => !k.endsWith("_id")),
|
|
73
|
-
) as CommonProps
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
primary_id,
|
|
77
|
-
other_ids,
|
|
78
|
-
...other_props,
|
|
79
|
-
_og_elm: e,
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
const elements3 = elements2.map((e) => {
|
|
84
|
-
let selector_path = ""
|
|
85
|
-
|
|
86
|
-
const getSelectorPath = (e2: ProcessedElement): string => {
|
|
87
|
-
const parent_key = Object.keys(e2.other_ids).find((k) =>
|
|
88
|
-
k.startsWith("source_"),
|
|
89
|
-
)
|
|
90
|
-
if (!parent_key) return `.${e2.name}`
|
|
91
|
-
const parent_type = parent_key.slice(0, -3) // trim "_id"
|
|
92
|
-
|
|
93
|
-
const parent = elements2.find(
|
|
94
|
-
(p) =>
|
|
95
|
-
p.type === parent_type && p.primary_id === e2.other_ids[parent_key],
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
if (!parent) return `??? > .${e2.name}`
|
|
99
|
-
|
|
100
|
-
if (!("name" in parent)) return `#${parent.primary_id} > .${e2.name}`
|
|
101
|
-
|
|
102
|
-
return `${getSelectorPath(parent)} > .${e2.name}`
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if ("name" in e) {
|
|
106
|
-
selector_path = getSelectorPath(e)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
...e,
|
|
111
|
-
selector_path,
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
const columns: Column[] = [
|
|
116
|
-
{
|
|
117
|
-
key: "primary_id",
|
|
118
|
-
name: "primary_id",
|
|
119
|
-
renderCell: (row: ProcessedElement) => (
|
|
120
|
-
<div className="flex items-center">
|
|
121
|
-
<ClickableText
|
|
122
|
-
text={row.primary_id}
|
|
123
|
-
onClick={() =>
|
|
124
|
-
setFilter({
|
|
125
|
-
focused_id: row.primary_id,
|
|
126
|
-
id_search: undefined,
|
|
127
|
-
selector_search: undefined,
|
|
128
|
-
})
|
|
129
|
-
}
|
|
130
|
-
/>
|
|
131
|
-
<span className="flex-grow" />
|
|
132
|
-
<ClickableText
|
|
133
|
-
text="(JSON)"
|
|
134
|
-
onClick={() =>
|
|
135
|
-
setModal({
|
|
136
|
-
open: true,
|
|
137
|
-
element: row._og_elm,
|
|
138
|
-
title: row.primary_id,
|
|
139
|
-
})
|
|
140
|
-
}
|
|
141
|
-
/>
|
|
142
|
-
</div>
|
|
143
|
-
),
|
|
144
|
-
renderHeaderCell: (col: Column) => (
|
|
145
|
-
<HeaderCell
|
|
146
|
-
column={col}
|
|
147
|
-
onTextChange={(v) => setFilter({ id_search: v })}
|
|
148
|
-
field={
|
|
149
|
-
!filters.focused_id
|
|
150
|
-
? undefined
|
|
151
|
-
: () => (
|
|
152
|
-
<div>
|
|
153
|
-
Focus:{" "}
|
|
154
|
-
<span className="underline">{filters.focused_id}</span>
|
|
155
|
-
<ClickableText
|
|
156
|
-
text="(unfocus)"
|
|
157
|
-
onClick={() => setFilter({ focused_id: undefined })}
|
|
158
|
-
/>
|
|
159
|
-
</div>
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
/>
|
|
163
|
-
),
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
key: "type",
|
|
167
|
-
name: "type",
|
|
168
|
-
renderHeaderCell: (col: Column) => (
|
|
169
|
-
<HeaderCell
|
|
170
|
-
column={col}
|
|
171
|
-
field={() => (
|
|
172
|
-
<select
|
|
173
|
-
onChange={(e) =>
|
|
174
|
-
setFilter({ component_type_filter: e.target.value })
|
|
175
|
-
}
|
|
176
|
-
className="border rounded p-1 w-full"
|
|
177
|
-
>
|
|
178
|
-
<option key="any" value="any">
|
|
179
|
-
any
|
|
180
|
-
</option>
|
|
181
|
-
<option key="source" value="source">
|
|
182
|
-
source
|
|
183
|
-
</option>
|
|
184
|
-
<option key="source/pcb" value="source/pcb">
|
|
185
|
-
source/pcb
|
|
186
|
-
</option>
|
|
187
|
-
<option key="source/schematic" value="source/schematic">
|
|
188
|
-
source/schematic
|
|
189
|
-
</option>
|
|
190
|
-
{element_types.map((type) => (
|
|
191
|
-
<option key={type} value={type}>
|
|
192
|
-
{type}
|
|
193
|
-
</option>
|
|
194
|
-
))}
|
|
195
|
-
</select>
|
|
196
|
-
)}
|
|
197
|
-
/>
|
|
198
|
-
),
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
key: "name",
|
|
202
|
-
name: "name",
|
|
203
|
-
renderHeaderCell: (col: Column) => (
|
|
204
|
-
<HeaderCell
|
|
205
|
-
column={col}
|
|
206
|
-
onTextChange={(t) => setFilter({ name_search: t })}
|
|
207
|
-
/>
|
|
208
|
-
),
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
key: "selector_path",
|
|
212
|
-
name: "selector_path",
|
|
213
|
-
renderHeaderCell: (col: Column) => (
|
|
214
|
-
<HeaderCell
|
|
215
|
-
column={col}
|
|
216
|
-
onTextChange={(t) => setFilter({ selector_search: t })}
|
|
217
|
-
/>
|
|
218
|
-
),
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
key: "other_ids",
|
|
222
|
-
name: "other_ids",
|
|
223
|
-
renderCell: (row: ProcessedElement) => (
|
|
224
|
-
<div className="space-x-2">
|
|
225
|
-
{Object.entries(row.other_ids).map(([other_id, v]) => (
|
|
226
|
-
<ClickableText
|
|
227
|
-
key={v}
|
|
228
|
-
text={v}
|
|
229
|
-
onClick={() => setFilter({ focused_id: v })}
|
|
230
|
-
/>
|
|
231
|
-
))}
|
|
232
|
-
</div>
|
|
233
|
-
),
|
|
234
|
-
},
|
|
235
|
-
]
|
|
236
|
-
|
|
237
|
-
const elements4 = elements3
|
|
238
|
-
.filter((e) => {
|
|
239
|
-
if (!filters.name_search) return true
|
|
240
|
-
return e.name?.toLowerCase()?.includes(filters.name_search.toLowerCase())
|
|
241
|
-
})
|
|
242
|
-
.filter((e) => {
|
|
243
|
-
if (!filters.component_type_filter) return true
|
|
244
|
-
if (filters.component_type_filter === "any") return true
|
|
245
|
-
if (filters.component_type_filter === "source") {
|
|
246
|
-
return e.type.startsWith("source_")
|
|
247
|
-
}
|
|
248
|
-
if (filters.component_type_filter === "source/pcb") {
|
|
249
|
-
return e.type.startsWith("source_") || e.type.startsWith("pcb_")
|
|
250
|
-
}
|
|
251
|
-
if (filters.component_type_filter === "source/schematic") {
|
|
252
|
-
return e.type.startsWith("source_") || e.type.startsWith("schematic_")
|
|
253
|
-
}
|
|
254
|
-
return e.type?.includes(filters.component_type_filter)
|
|
255
|
-
})
|
|
256
|
-
.filter((e) => {
|
|
257
|
-
if (!filters.selector_search) return true
|
|
258
|
-
const parts = filters.selector_search
|
|
259
|
-
.split(" ")
|
|
260
|
-
.filter((p) => p.length > 0)
|
|
261
|
-
return parts.every((part) => e.selector_path?.includes(part))
|
|
262
|
-
})
|
|
263
|
-
.filter((e) => {
|
|
264
|
-
if (!filters.id_search) return true
|
|
265
|
-
return e.primary_id?.includes(filters.id_search)
|
|
266
|
-
})
|
|
267
|
-
.filter((e) => {
|
|
268
|
-
if (!filters.focused_id) return true
|
|
269
|
-
if (e.primary_id === filters.focused_id) return true
|
|
270
|
-
if (Object.values(e.other_ids).includes(filters.focused_id)) return true
|
|
271
|
-
return false
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
return (
|
|
275
|
-
<div className="font-mono text-xs">
|
|
276
|
-
<div className="overflow-x-auto">
|
|
277
|
-
<table className="table-auto w-full text-left">
|
|
278
|
-
<thead>
|
|
279
|
-
<tr>
|
|
280
|
-
{columns.map((col) => (
|
|
281
|
-
<th key={col.key} className="px-4 py-2 border-b">
|
|
282
|
-
{col.renderHeaderCell ? col.renderHeaderCell(col) : col.name}
|
|
283
|
-
</th>
|
|
284
|
-
))}
|
|
285
|
-
</tr>
|
|
286
|
-
</thead>
|
|
287
|
-
<tbody>
|
|
288
|
-
{elements4.map((row, rowIndex) => (
|
|
289
|
-
<tr key={rowIndex} className="hover:bg-gray-100">
|
|
290
|
-
{columns.map((col) => (
|
|
291
|
-
<td key={col.key} className="px-4 py-2 border-b">
|
|
292
|
-
{col.renderCell
|
|
293
|
-
? col.renderCell(row)
|
|
294
|
-
: (row as any)[col.key]}
|
|
295
|
-
</td>
|
|
296
|
-
))}
|
|
297
|
-
</tr>
|
|
298
|
-
))}
|
|
299
|
-
</tbody>
|
|
300
|
-
</table>
|
|
301
|
-
</div>
|
|
302
|
-
<Modal
|
|
303
|
-
open={modal.open}
|
|
304
|
-
onClose={() => setModal({ open: false })}
|
|
305
|
-
title={modal.open ? modal.title : ""}
|
|
306
|
-
>
|
|
307
|
-
<div className="bg-gray-800 p-3 text-white rounded">
|
|
308
|
-
<pre className="whitespace-pre-wrap">
|
|
309
|
-
{modal.open ? JSON.stringify(modal.element, null, 2) : ""}
|
|
310
|
-
</pre>
|
|
311
|
-
</div>
|
|
312
|
-
</Modal>
|
|
313
|
-
</div>
|
|
314
|
-
)
|
|
315
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type React from "react"
|
|
2
|
-
|
|
3
|
-
interface ClickableTextProps {
|
|
4
|
-
text: string
|
|
5
|
-
onClick: () => void
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const ClickableText: React.FC<ClickableTextProps> = ({
|
|
9
|
-
text,
|
|
10
|
-
onClick,
|
|
11
|
-
}) => {
|
|
12
|
-
return (
|
|
13
|
-
<span
|
|
14
|
-
className="cursor-pointer underline text-blue-300 mx-2"
|
|
15
|
-
onClick={onClick}
|
|
16
|
-
>
|
|
17
|
-
{text}
|
|
18
|
-
</span>
|
|
19
|
-
)
|
|
20
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type React from "react"
|
|
2
|
-
|
|
3
|
-
interface HeaderCellProps {
|
|
4
|
-
column: { name: string }
|
|
5
|
-
field?: () => React.ReactNode
|
|
6
|
-
onTextChange?: (value: string) => void
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const HeaderCell: React.FC<HeaderCellProps> = (p) => {
|
|
10
|
-
return (
|
|
11
|
-
<div className="leading-5">
|
|
12
|
-
<div className="py-2 font-bold">{p.column.name}</div>
|
|
13
|
-
<div>
|
|
14
|
-
{p.field?.() ?? (
|
|
15
|
-
<input
|
|
16
|
-
type="text"
|
|
17
|
-
className="border rounded p-1 w-full"
|
|
18
|
-
onChange={(e) => {
|
|
19
|
-
p.onTextChange?.(e.target.value)
|
|
20
|
-
}}
|
|
21
|
-
/>
|
|
22
|
-
)}
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
)
|
|
26
|
-
}
|