@tscircuit/fake-snippets 0.0.3 → 0.0.5
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/.github/workflows/bundle-size-analysis.yml +2 -2
- package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +131 -0
- package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +89 -0
- package/bun-tests/fake-snippets-api/routes/package_releases/list.test.ts +157 -0
- package/bun-tests/fake-snippets-api/routes/package_releases/update.test.ts +189 -0
- package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +26 -0
- package/bun-tests/fake-snippets-api/routes/packages/delete.test.ts +100 -0
- package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +59 -0
- package/bun-tests/fake-snippets-api/routes/packages/list.test.ts +167 -0
- package/bun.lock +3514 -0
- package/dist/bundle.js +741 -108
- package/fake-snippets-api/lib/db/db-client.ts +79 -2
- package/fake-snippets-api/lib/db/schema.ts +55 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +10 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package.ts +31 -0
- package/fake-snippets-api/lib/with-winter-spec.ts +1 -0
- package/fake-snippets-api/routes/api/package_files/download.ts +170 -0
- package/fake-snippets-api/routes/api/package_files/get.ts +52 -0
- package/fake-snippets-api/routes/api/package_files/list.ts +60 -0
- package/fake-snippets-api/routes/api/package_releases/create.ts +83 -0
- package/fake-snippets-api/routes/api/package_releases/get.ts +34 -0
- package/fake-snippets-api/routes/api/package_releases/list.ts +77 -0
- package/fake-snippets-api/routes/api/package_releases/update.ts +96 -0
- package/fake-snippets-api/routes/api/packages/create.ts +58 -0
- package/fake-snippets-api/routes/api/packages/delete.ts +43 -0
- package/fake-snippets-api/routes/api/packages/get.ts +36 -0
- package/fake-snippets-api/routes/api/packages/list.ts +56 -0
- package/fake-snippets-api/routes/api/snippets/create.ts +5 -3
- package/package.json +12 -9
- package/playwright-tests/circuit-json-import.spec.ts +133 -0
- package/playwright-tests/cmd-click.spec.ts +1 -1
- package/playwright-tests/editor-page.spec.ts +1 -1
- package/playwright-tests/exampleCircuitJson.ts +498 -0
- package/playwright-tests/preview-page.spec.ts +2 -9
- package/playwright-tests/snapshots/cmd-click.spec.ts-underlined-imports.png +0 -0
- package/playwright-tests/snapshots/editor-page.spec.ts-editor-with-snippet.png +0 -0
- package/playwright-tests/snapshots/preview-page.spec.ts-preview-snippet-pcb.png +0 -0
- package/src/components/CircuitJsonImportDialog.tsx +186 -0
- package/src/components/CodeAndPreview.tsx +60 -2
- package/src/components/EditorNav.tsx +23 -2
- package/src/components/FootprintDialog.tsx +3 -6
- package/src/components/Header2.tsx +7 -0
- package/src/components/PrefetchPageLink.tsx +4 -1
- package/src/components/SuspenseRunFrame.tsx +16 -0
- package/src/components/dialogs/import-snippet-dialog.tsx +12 -8
- package/src/hooks/use-debounce.ts +17 -0
- package/src/hooks/use-global-store.ts +5 -0
- package/src/hooks/use-run-tsx/index.tsx +1 -0
- package/src/pages/landing.tsx +2 -2
- package/src/pages/preview.tsx +2 -28
- package/src/pages/quickstart.tsx +24 -0
- package/vite.config.ts +1 -1
- package/bun.lockb +0 -0
- package/playwright-tests/snapshots/preview-page.spec.ts-preview-snippet-schematic.png +0 -0
|
@@ -15,8 +15,9 @@ import { Loader2 } from "lucide-react"
|
|
|
15
15
|
import { useEffect, useMemo, useState } from "react"
|
|
16
16
|
import { useMutation, useQueryClient } from "react-query"
|
|
17
17
|
import EditorNav from "./EditorNav"
|
|
18
|
-
import { PreviewContent } from "./PreviewContent"
|
|
19
18
|
import { parseJsonOrNull } from "@/lib/utils/parseJsonOrNull"
|
|
19
|
+
import { PreviewContent } from "./PreviewContent"
|
|
20
|
+
import { SuspenseRunFrame } from "./SuspenseRunFrame"
|
|
20
21
|
|
|
21
22
|
interface Props {
|
|
22
23
|
snippet?: Snippet | null
|
|
@@ -50,6 +51,9 @@ export function CodeAndPreview({ snippet }: Props) {
|
|
|
50
51
|
const [showPreview, setShowPreview] = useState(true)
|
|
51
52
|
const [lastRunCode, setLastRunCode] = useState(defaultCode ?? "")
|
|
52
53
|
const [fullScreen, setFullScreen] = useState(false)
|
|
54
|
+
const shouldUseWebworkerForRun = useGlobalStore(
|
|
55
|
+
(s) => s.should_use_webworker_for_run,
|
|
56
|
+
)
|
|
53
57
|
|
|
54
58
|
const snippetType: "board" | "package" | "model" | "footprint" =
|
|
55
59
|
snippet?.snippet_type ??
|
|
@@ -187,6 +191,41 @@ export function CodeAndPreview({ snippet }: Props) {
|
|
|
187
191
|
|
|
188
192
|
useWarnUserOnPageChange({ hasUnsavedChanges })
|
|
189
193
|
|
|
194
|
+
const fsMap = useMemo(() => {
|
|
195
|
+
const possibleExportNames = [
|
|
196
|
+
...(code.match(/export function (\w+)/)?.slice(1) ?? []),
|
|
197
|
+
...(code.match(/export const (\w+) ?=/)?.slice(1) ?? []),
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
console.log(possibleExportNames)
|
|
201
|
+
|
|
202
|
+
const exportName = possibleExportNames[0]
|
|
203
|
+
|
|
204
|
+
let entrypointContent: string
|
|
205
|
+
if (snippetType === "board") {
|
|
206
|
+
entrypointContent = `
|
|
207
|
+
import ${exportName ? `{ ${exportName} as Snippet }` : "Snippet"} from "./index.tsx"
|
|
208
|
+
circuit.add(<Snippet />)
|
|
209
|
+
`.trim()
|
|
210
|
+
} else {
|
|
211
|
+
entrypointContent = `
|
|
212
|
+
import ${exportName ? `{ ${exportName} as Snippet }` : "Snippet"} from "./index.tsx"
|
|
213
|
+
circuit.add(
|
|
214
|
+
<board width="10mm" height="10mm">
|
|
215
|
+
<Snippet name="U1" />
|
|
216
|
+
</board>
|
|
217
|
+
)
|
|
218
|
+
`.trim()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
"index.tsx": code,
|
|
223
|
+
"manual-edits.json": manualEditsFileContent ?? "{}",
|
|
224
|
+
"main.tsx": entrypointContent,
|
|
225
|
+
}
|
|
226
|
+
}, [code, manualEditsFileContent])
|
|
227
|
+
console.log(fsMap)
|
|
228
|
+
|
|
190
229
|
if (!snippet && (urlParams.snippet_id || urlParams.should_create_snippet)) {
|
|
191
230
|
return (
|
|
192
231
|
<div className="flex items-center justify-center h-64">
|
|
@@ -231,7 +270,7 @@ export function CodeAndPreview({ snippet }: Props) {
|
|
|
231
270
|
onDtsChange={(newDts) => setDts(newDts)}
|
|
232
271
|
/>
|
|
233
272
|
</div>
|
|
234
|
-
{showPreview && (
|
|
273
|
+
{showPreview && !shouldUseWebworkerForRun && (
|
|
235
274
|
<PreviewContent
|
|
236
275
|
className={cn(
|
|
237
276
|
"flex p-2 flex-col min-h-[640px]",
|
|
@@ -252,6 +291,25 @@ export function CodeAndPreview({ snippet }: Props) {
|
|
|
252
291
|
isFullScreen={fullScreen}
|
|
253
292
|
/>
|
|
254
293
|
)}
|
|
294
|
+
{showPreview && shouldUseWebworkerForRun && (
|
|
295
|
+
<div
|
|
296
|
+
className={cn(
|
|
297
|
+
"flex p-0 flex-col min-h-[640px]",
|
|
298
|
+
fullScreen
|
|
299
|
+
? "fixed inset-0 z-50 bg-white p-4 overflow-hidden"
|
|
300
|
+
: "w-full md:w-1/2",
|
|
301
|
+
)}
|
|
302
|
+
>
|
|
303
|
+
<SuspenseRunFrame
|
|
304
|
+
showRunButton
|
|
305
|
+
onEditEvent={() => {
|
|
306
|
+
// TODO
|
|
307
|
+
}}
|
|
308
|
+
fsMap={fsMap}
|
|
309
|
+
entrypoint="main.tsx"
|
|
310
|
+
/>
|
|
311
|
+
</div>
|
|
312
|
+
)}
|
|
255
313
|
</div>
|
|
256
314
|
</div>
|
|
257
315
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Button } from "@/components/ui/button"
|
|
2
|
-
import { GitFork } from "lucide-react"
|
|
2
|
+
import { Check, CircleCheckBig, Cpu, GitFork, Square } from "lucide-react"
|
|
3
3
|
import {
|
|
4
4
|
DropdownMenu,
|
|
5
5
|
DropdownMenuContent,
|
|
@@ -75,6 +75,12 @@ export default function EditorNav({
|
|
|
75
75
|
const [, navigate] = useLocation()
|
|
76
76
|
const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
|
|
77
77
|
const session = useGlobalStore((s) => s.session)
|
|
78
|
+
const shouldUseWebworkerForRun = useGlobalStore(
|
|
79
|
+
(s) => s.should_use_webworker_for_run,
|
|
80
|
+
)
|
|
81
|
+
const setShouldUseWebworkerForRun = useGlobalStore(
|
|
82
|
+
(s) => s.setShouldUseWebworkerForRun,
|
|
83
|
+
)
|
|
78
84
|
const { Dialog: RenameDialog, openDialog: openRenameDialog } =
|
|
79
85
|
useRenameSnippetDialog()
|
|
80
86
|
const {
|
|
@@ -275,10 +281,25 @@ export default function EditorNav({
|
|
|
275
281
|
variant="ghost"
|
|
276
282
|
size="sm"
|
|
277
283
|
className="hidden md:flex px-2 text-xs"
|
|
284
|
+
onClick={() =>
|
|
285
|
+
setShouldUseWebworkerForRun(!shouldUseWebworkerForRun)
|
|
286
|
+
}
|
|
287
|
+
>
|
|
288
|
+
{shouldUseWebworkerForRun ? (
|
|
289
|
+
<CircleCheckBig className="mr-1 h-3 w-3" />
|
|
290
|
+
) : (
|
|
291
|
+
<Square className="mr-1 h-3 w-3" />
|
|
292
|
+
)}
|
|
293
|
+
Webworker (Beta)
|
|
294
|
+
</Button>
|
|
295
|
+
{/* <Button
|
|
296
|
+
variant="ghost"
|
|
297
|
+
size="sm"
|
|
298
|
+
className="hidden md:flex px-2 text-xs"
|
|
278
299
|
>
|
|
279
300
|
<Eye className="mr-1 h-3 w-3" />
|
|
280
301
|
Public
|
|
281
|
-
</Button>
|
|
302
|
+
</Button> */}
|
|
282
303
|
<DropdownMenu>
|
|
283
304
|
<DropdownMenuTrigger asChild>
|
|
284
305
|
<Button variant="ghost" size="icon" className="hidden md:flex">
|
|
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from "react"
|
|
|
3
3
|
import { parseFootprintParams } from "../lib/utils/parseFootprintParams"
|
|
4
4
|
import ParametersEditor from "./ParametersEditor"
|
|
5
5
|
import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
|
|
6
|
-
import { fp } from "@tscircuit/footprinter"
|
|
6
|
+
import { fp, getFootprintNamesByType } from "@tscircuit/footprinter"
|
|
7
7
|
import { useToast } from "../hooks/use-toast"
|
|
8
8
|
import { Button } from "./ui/button"
|
|
9
9
|
import { FileName } from "./CodeEditorHeader"
|
|
@@ -53,10 +53,7 @@ export const FootprintDialog = ({
|
|
|
53
53
|
const [error, setError] = useState<string | null>(null)
|
|
54
54
|
const { toast } = useToast()
|
|
55
55
|
|
|
56
|
-
const
|
|
57
|
-
const footprintNames = fp
|
|
58
|
-
.getFootprintNames()
|
|
59
|
-
.filter((footprintName) => !PASSIVE_COMPONENTS.includes(footprintName))
|
|
56
|
+
const { normalFootprintNames } = getFootprintNamesByType()
|
|
60
57
|
|
|
61
58
|
useEffect(() => {
|
|
62
59
|
if (copied) {
|
|
@@ -266,7 +263,7 @@ export const FootprintDialog = ({
|
|
|
266
263
|
handleFootprintPreview(value)
|
|
267
264
|
}
|
|
268
265
|
}}
|
|
269
|
-
options={
|
|
266
|
+
options={normalFootprintNames}
|
|
270
267
|
placeholder="Select footprint..."
|
|
271
268
|
searchPlaceholder="Search footprints..."
|
|
272
269
|
emptyText="No footprints found."
|
|
@@ -64,6 +64,13 @@ export const Header2 = () => {
|
|
|
64
64
|
const isLoggedIn = useGlobalStore((state) => Boolean(state.session))
|
|
65
65
|
return (
|
|
66
66
|
<>
|
|
67
|
+
{/* <div className="absolute left-0 top-0 z-[9999999]">
|
|
68
|
+
<div className="hidden xl:block">xl</div>
|
|
69
|
+
<div className="hidden lg:block xl:hidden">lg</div>
|
|
70
|
+
<div className="hidden md:block lg:hidden">md</div>
|
|
71
|
+
<div className="hidden sm:block md:hidden">sm</div>
|
|
72
|
+
<div className="hidden xs:block sm:hidden">xs</div>
|
|
73
|
+
</div> */}
|
|
67
74
|
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
68
75
|
<div className="container mx-auto flex h-16 items-center justify-between px-2 md:px-6">
|
|
69
76
|
<div className="flex items-center gap-2">
|
|
@@ -30,7 +30,10 @@ const PrefetchPageLink = ({
|
|
|
30
30
|
|
|
31
31
|
useEffect(() => {
|
|
32
32
|
if (inView) {
|
|
33
|
-
const
|
|
33
|
+
const link = href === "/" ? "landing" : href.slice(1)
|
|
34
|
+
if (!link) return
|
|
35
|
+
const pageName = link.split("?")[0]
|
|
36
|
+
|
|
34
37
|
import(`@/pages/${pageName}.tsx`).catch((error) => {
|
|
35
38
|
console.error(`Failed to prefetch page module ${pageName}:`, error)
|
|
36
39
|
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { lazy, Suspense } from "react"
|
|
2
|
+
|
|
3
|
+
const RunFrame = lazy(async () => {
|
|
4
|
+
const { RunFrame } = await import("@tscircuit/runframe/runner")
|
|
5
|
+
return { default: RunFrame }
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
export const SuspenseRunFrame = (
|
|
9
|
+
props: React.ComponentProps<typeof RunFrame>,
|
|
10
|
+
) => {
|
|
11
|
+
return (
|
|
12
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
13
|
+
<RunFrame {...props} />
|
|
14
|
+
</Suspense>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { Button } from "../ui/button"
|
|
1
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
2
|
+
import { useDebounce } from "@/hooks/use-debounce"
|
|
3
|
+
import type { Snippet } from "fake-snippets-api/lib/db/schema"
|
|
5
4
|
import { useState } from "react"
|
|
6
5
|
import { useQuery } from "react-query"
|
|
7
|
-
import {
|
|
6
|
+
import { Button } from "../ui/button"
|
|
7
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
|
|
8
|
+
import { Input } from "../ui/input"
|
|
8
9
|
import { createUseDialog } from "./create-use-dialog"
|
|
9
10
|
|
|
10
11
|
export const ImportSnippetDialog = ({
|
|
@@ -17,15 +18,18 @@ export const ImportSnippetDialog = ({
|
|
|
17
18
|
onSnippetSelected: (snippet: Snippet) => any
|
|
18
19
|
}) => {
|
|
19
20
|
const [searchText, setSearchText] = useState("")
|
|
21
|
+
const debouncedSearch = useDebounce(searchText, 300)
|
|
20
22
|
const axios = useAxios()
|
|
21
23
|
const { data: snippets, isLoading } = useQuery(
|
|
22
|
-
["snippetSearch",
|
|
24
|
+
["snippetSearch", debouncedSearch],
|
|
23
25
|
async () => {
|
|
24
|
-
const response = await axios.get(
|
|
26
|
+
const response = await axios.get(
|
|
27
|
+
`/snippets/search?q=${encodeURIComponent(debouncedSearch)}`,
|
|
28
|
+
)
|
|
25
29
|
return response.data.snippets.slice(0, 12)
|
|
26
30
|
},
|
|
27
31
|
{
|
|
28
|
-
enabled:
|
|
32
|
+
enabled: debouncedSearch.length > 0,
|
|
29
33
|
},
|
|
30
34
|
)
|
|
31
35
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useEffect, useState } from "react"
|
|
2
|
+
|
|
3
|
+
export function useDebounce<T>(value: T, delay: number): T {
|
|
4
|
+
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const handler = setTimeout(() => {
|
|
8
|
+
setDebouncedValue(value)
|
|
9
|
+
}, delay)
|
|
10
|
+
|
|
11
|
+
return () => {
|
|
12
|
+
clearTimeout(handler)
|
|
13
|
+
}
|
|
14
|
+
}, [value, delay])
|
|
15
|
+
|
|
16
|
+
return debouncedValue
|
|
17
|
+
}
|
|
@@ -10,6 +10,8 @@ export type Store = {
|
|
|
10
10
|
} | null
|
|
11
11
|
setSession: (session: Store["session"]) => any
|
|
12
12
|
should_onboarding_tips_be_closed: boolean
|
|
13
|
+
should_use_webworker_for_run?: boolean
|
|
14
|
+
setShouldUseWebworkerForRun: (should_use_webworker_for_run: boolean) => any
|
|
13
15
|
setOnboardingTipsClosed: (closed: boolean) => any
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -17,6 +19,9 @@ export const useGlobalStore = create<Store>()(
|
|
|
17
19
|
persist(
|
|
18
20
|
(set) => ({
|
|
19
21
|
session: null,
|
|
22
|
+
should_use_webworker_for_run: false,
|
|
23
|
+
setShouldUseWebworkerForRun: (should_use_webworker_for_run: boolean) =>
|
|
24
|
+
set({ should_use_webworker_for_run }),
|
|
20
25
|
setSession: (session) => set({ session }),
|
|
21
26
|
should_onboarding_tips_be_closed: false,
|
|
22
27
|
setOnboardingTipsClosed: (closed) =>
|
|
@@ -108,6 +108,7 @@ export const useRunTsx = ({
|
|
|
108
108
|
}
|
|
109
109
|
;(globalThis as any).__tscircuit_require = __tscircuit_require
|
|
110
110
|
preSuppliedImports["@tscircuit/core"] = tscircuitCore
|
|
111
|
+
preSuppliedImports["tscircuit"] = tscircuitCore
|
|
111
112
|
preSuppliedImports["@tscircuit/math-utils"] = tscircuitMathUtils
|
|
112
113
|
preSuppliedImports["react"] = React
|
|
113
114
|
preSuppliedImports["jscad-fiber"] = jscadFiber
|
package/src/pages/landing.tsx
CHANGED
|
@@ -41,7 +41,7 @@ export function LandingPage() {
|
|
|
41
41
|
</Helmet>
|
|
42
42
|
<Header2 />
|
|
43
43
|
<main className="flex-1">
|
|
44
|
-
<section className="w-full py-
|
|
44
|
+
<section className="w-full py-8 md:py-12 lg:py-20 xl:py-36">
|
|
45
45
|
<div className="container px-4 md:px-6 mx-auto">
|
|
46
46
|
<div className="container mx-auto max-w-7xl">
|
|
47
47
|
<div className="grid gap-6 lg:grid-cols-[1fr_400px] lg:gap-12 xl:grid-cols-[1fr_600px]">
|
|
@@ -116,7 +116,7 @@ export function LandingPage() {
|
|
|
116
116
|
<div className="w-full aspect-video relative">
|
|
117
117
|
<OptimizedImage
|
|
118
118
|
alt="Product preview"
|
|
119
|
-
className="mx-auto overflow-hidden rounded-xl object-cover object-center absolute inset-0 w-full h-full"
|
|
119
|
+
className="mx-auto overflow-hidden rounded-xl object-cover object-center absolute inset-0 w-full h-full mt-8 lg:mt-0"
|
|
120
120
|
src="/assets/editor_example_1_more_square.webp"
|
|
121
121
|
priority={true}
|
|
122
122
|
/>
|
package/src/pages/preview.tsx
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { CircuitToSvgWithMouseControl } from "@/components/CircuitToSvgWithMouseControl"
|
|
2
1
|
import { useSnippet } from "@/hooks/use-snippet"
|
|
3
2
|
import { useUrlParams } from "@/hooks/use-url-params"
|
|
4
|
-
import {
|
|
5
|
-
import { PCBViewer } from "@tscircuit/pcb-viewer"
|
|
3
|
+
import { CircuitJsonPreview } from "@tscircuit/runframe"
|
|
6
4
|
import { Loader2 } from "lucide-react"
|
|
7
5
|
|
|
8
6
|
export const PreviewPage = () => {
|
|
9
7
|
const urlParams = useUrlParams()
|
|
10
8
|
const snippetId = urlParams.snippet_id
|
|
11
|
-
const view = urlParams.view || "pcb"
|
|
12
9
|
const { data: snippet, isLoading, error } = useSnippet(snippetId)
|
|
13
10
|
|
|
14
11
|
if (isLoading) {
|
|
@@ -43,28 +40,5 @@ export const PreviewPage = () => {
|
|
|
43
40
|
)
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
if (!validViews.includes(view)) {
|
|
48
|
-
return (
|
|
49
|
-
<div className="w-full h-screen">
|
|
50
|
-
{view === "pcb" && (
|
|
51
|
-
<PCBViewer soup={snippet.circuit_json} height={window.innerHeight} />
|
|
52
|
-
)}
|
|
53
|
-
</div>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<div className="w-full h-screen">
|
|
59
|
-
{view === "pcb" && (
|
|
60
|
-
<PCBViewer soup={snippet.circuit_json} height={window.innerHeight} />
|
|
61
|
-
)}
|
|
62
|
-
{view === "3d" && <CadViewer soup={snippet.circuit_json as any} />}
|
|
63
|
-
{view === "schematic" && (
|
|
64
|
-
<CircuitToSvgWithMouseControl
|
|
65
|
-
circuitJson={snippet.circuit_json as any}
|
|
66
|
-
/>
|
|
67
|
-
)}
|
|
68
|
-
</div>
|
|
69
|
-
)
|
|
43
|
+
return <CircuitJsonPreview circuitJson={snippet.circuit_json as any} />
|
|
70
44
|
}
|
package/src/pages/quickstart.tsx
CHANGED
|
@@ -8,6 +8,7 @@ import { Button } from "@/components/ui/button"
|
|
|
8
8
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
9
9
|
import { TypeBadge } from "@/components/TypeBadge"
|
|
10
10
|
import { JLCPCBImportDialog } from "@/components/JLCPCBImportDialog"
|
|
11
|
+
import { CircuitJsonImportDialog } from "@/components/CircuitJsonImportDialog"
|
|
11
12
|
import { useNotImplementedToast } from "@/hooks/use-toast"
|
|
12
13
|
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
13
14
|
import { cn } from "@/lib/utils"
|
|
@@ -16,6 +17,8 @@ import { PrefetchPageLink } from "@/components/PrefetchPageLink"
|
|
|
16
17
|
export const QuickstartPage = () => {
|
|
17
18
|
const axios = useAxios()
|
|
18
19
|
const [isJLCPCBDialogOpen, setIsJLCPCBDialogOpen] = useState(false)
|
|
20
|
+
const [isCircuitJsonImportDialogOpen, setIsCircuitJsonImportDialogOpen] =
|
|
21
|
+
useState(false)
|
|
19
22
|
const toastNotImplemented = useNotImplementedToast()
|
|
20
23
|
const currentUser = useGlobalStore((s) => s.session?.github_username)
|
|
21
24
|
const { data: mySnippets, isLoading } = useQuery<Snippet[]>(
|
|
@@ -159,6 +162,22 @@ export const QuickstartPage = () => {
|
|
|
159
162
|
</Button>
|
|
160
163
|
</CardContent>
|
|
161
164
|
</Card>
|
|
165
|
+
<Card className="hover:shadow-md transition-shadow rounded-md">
|
|
166
|
+
<CardHeader className="p-4 pb-0">
|
|
167
|
+
<CardTitle className="text-lg flex items-center justify-between">
|
|
168
|
+
Circuit Json
|
|
169
|
+
<TypeBadge type="module" className="ml-2" />
|
|
170
|
+
</CardTitle>
|
|
171
|
+
</CardHeader>
|
|
172
|
+
<CardContent className="p-4">
|
|
173
|
+
<Button
|
|
174
|
+
className="w-full"
|
|
175
|
+
onClick={() => setIsCircuitJsonImportDialogOpen(true)}
|
|
176
|
+
>
|
|
177
|
+
Import Circuit JSON
|
|
178
|
+
</Button>
|
|
179
|
+
</CardContent>
|
|
180
|
+
</Card>
|
|
162
181
|
</div>
|
|
163
182
|
</div>
|
|
164
183
|
|
|
@@ -167,6 +186,11 @@ export const QuickstartPage = () => {
|
|
|
167
186
|
onOpenChange={setIsJLCPCBDialogOpen}
|
|
168
187
|
/>
|
|
169
188
|
|
|
189
|
+
<CircuitJsonImportDialog
|
|
190
|
+
open={isCircuitJsonImportDialogOpen}
|
|
191
|
+
onOpenChange={setIsCircuitJsonImportDialogOpen}
|
|
192
|
+
/>
|
|
193
|
+
|
|
170
194
|
<div>
|
|
171
195
|
<h2 className="text-xl font-semibold mb-4 mt-12">
|
|
172
196
|
Start from a Template
|
package/vite.config.ts
CHANGED
package/bun.lockb
DELETED
|
Binary file
|