@tscircuit/fake-snippets 0.0.66 → 0.0.68
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-tests/fake-snippets-api/fixtures/get-circuit-json.ts +5 -143
- package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +1 -4
- package/bun-tests/fake-snippets-api/fixtures/start-server.ts +7 -3
- package/bun-tests/fake-snippets-api/routes/order_quotes/create.test.ts +20 -56
- package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +2 -2
- package/bun-tests/fake-snippets-api/routes/package_releases/update.test.ts +1 -1
- package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +0 -11
- package/bun.lock +26 -75
- package/dist/bundle.js +32 -39
- package/fake-snippets-api/routes/api/order_quotes/create.ts +30 -37
- package/fake-snippets-api/routes/api/order_quotes/get.ts +5 -8
- package/package.json +4 -4
- package/src/App.tsx +0 -11
- package/src/ContextProviders.tsx +2 -0
- package/src/components/CmdKMenu.tsx +19 -19
- package/src/components/DownloadButtonAndMenu.tsx +1 -4
- package/src/components/FAQ.tsx +3 -1
- package/src/components/FileSidebar.tsx +50 -1
- package/src/components/Footer.tsx +5 -2
- package/src/components/Header2.tsx +20 -9
- package/src/components/HeaderLogin.tsx +37 -54
- package/src/components/ImageWithFallback.tsx +37 -0
- package/src/components/JLCPCBImportDialog.tsx +45 -29
- package/src/components/PackageCard.tsx +2 -2
- package/src/components/{SnippetLink.tsx → PackageLink.tsx} +8 -16
- package/src/components/PackageSearchResults.tsx +87 -0
- package/src/components/PackagesList.tsx +3 -3
- package/src/components/PageSearchComponent.tsx +9 -9
- package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +5 -28
- package/src/components/ViewPackagePage/components/important-files-view.tsx +1 -1
- package/src/components/ViewPackagePage/components/main-content-header.tsx +8 -8
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +24 -14
- package/src/components/ViewPackagePage/components/package-header.tsx +7 -2
- package/src/components/dialogs/confirm-delete-package-dialog.tsx +8 -0
- package/src/components/dialogs/edit-package-details-dialog.tsx +145 -138
- package/src/components/package-port/CodeAndPreview.tsx +40 -19
- package/src/components/package-port/CodeEditor.tsx +21 -37
- package/src/components/package-port/CodeEditorHeader.tsx +1 -1
- package/src/components/package-port/EditorNav.tsx +3 -13
- package/src/hooks/use-global-store.ts +1 -0
- package/src/hooks/use-shiki-highlighter.ts +13 -6
- package/src/hooks/useFileManagement.ts +59 -0
- package/src/lib/download-fns/download-gltf.ts +3 -10
- package/src/lib/handleManualEditsImport.tsx +1 -1
- package/src/lib/types.ts +4 -2
- package/src/lib/utils/isValidFileName.ts +5 -0
- package/src/pages/dashboard.tsx +4 -4
- package/src/pages/editor.tsx +20 -14
- package/src/pages/latest.tsx +25 -26
- package/src/pages/quickstart.tsx +5 -5
- package/src/pages/search.tsx +121 -20
- package/src/pages/trending.tsx +14 -58
- package/src/pages/user-profile.tsx +14 -8
- package/bun-tests/fake-snippets-api/routes/snippets/add_star.test.ts +0 -84
- package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +0 -53
- package/bun-tests/fake-snippets-api/routes/snippets/delete.test.ts +0 -82
- package/bun-tests/fake-snippets-api/routes/snippets/download.test.ts +0 -90
- package/bun-tests/fake-snippets-api/routes/snippets/generate_from_jlcpcb.test.ts +0 -16
- package/bun-tests/fake-snippets-api/routes/snippets/get.test.ts +0 -163
- package/bun-tests/fake-snippets-api/routes/snippets/get_image.test.ts +0 -117
- package/bun-tests/fake-snippets-api/routes/snippets/images.test.ts +0 -114
- package/bun-tests/fake-snippets-api/routes/snippets/list.test.ts +0 -169
- package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +0 -50
- package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +0 -72
- package/bun-tests/fake-snippets-api/routes/snippets/remove_star.test.ts +0 -80
- package/bun-tests/fake-snippets-api/routes/snippets/search.test.ts +0 -75
- package/bun-tests/fake-snippets-api/routes/snippets/star-count.test.ts +0 -51
- package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +0 -175
- package/src/components/AiChatInterface.tsx +0 -229
- package/src/components/CodeAndPreview.tsx +0 -289
- package/src/components/CodeEditor.tsx +0 -539
- package/src/components/CodeEditorHeader.tsx +0 -135
- package/src/components/EditorNav.tsx +0 -502
- package/src/components/OrderPreviewContent.tsx +0 -61
- package/src/components/PreviewContent.tsx +0 -372
- package/src/components/SnippetCard.tsx +0 -159
- package/src/components/SnippetList.tsx +0 -71
- package/src/components/ViewSnippetSidebar.tsx +0 -162
- package/src/components/dialogs/create-order-dialog.tsx +0 -146
- package/src/hooks/use-compiled-tsx.ts +0 -37
- package/src/hooks/use-run-tsx/construct-circuit.tsx +0 -62
- package/src/hooks/use-run-tsx/index.tsx +0 -256
- package/src/hooks/use-save-snippet.ts +0 -66
- package/src/hooks/use-typecheck.ts +0 -54
- package/src/lib/utils/getSyntaxError.ts +0 -13
- package/src/pages/ai.tsx +0 -92
- package/src/pages/preview.tsx +0 -44
- package/src/pages/view-order.tsx +0 -111
- package/src/pages/view-snippet.tsx +0 -166
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
|
|
2
|
-
import { useCurrentSnippet } from "@/hooks/use-current-snippet"
|
|
3
|
-
import { useToast } from "@/hooks/use-toast"
|
|
4
|
-
import { cn } from "@/lib/utils"
|
|
5
|
-
import { AtSign, Bot, Clock, Code, File, GitFork, Package } from "lucide-react"
|
|
6
|
-
import { useFilesDialog } from "./dialogs/files-dialog"
|
|
7
|
-
import { PrefetchPageLink } from "./PrefetchPageLink"
|
|
8
|
-
|
|
9
|
-
export default function ViewSnippetSidebar({
|
|
10
|
-
className,
|
|
11
|
-
}: {
|
|
12
|
-
className?: string
|
|
13
|
-
}) {
|
|
14
|
-
const { snippet } = useCurrentSnippet()
|
|
15
|
-
const { toast } = useToast()
|
|
16
|
-
const { Dialog: FilesDialog, openDialog: openFilesDialog } = useFilesDialog()
|
|
17
|
-
const { copyToClipboard } = useCopyToClipboard()
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div
|
|
21
|
-
className={cn(
|
|
22
|
-
"w-64 h-full bg-gray-100 text-gray-700 flex flex-col flex-shrink-0",
|
|
23
|
-
"hidden sm:block h-screen sticky top-0",
|
|
24
|
-
className,
|
|
25
|
-
)}
|
|
26
|
-
>
|
|
27
|
-
<nav className="flex-grow overflow-y-auto">
|
|
28
|
-
<ul className="p-2 space-y-2">
|
|
29
|
-
{[
|
|
30
|
-
{
|
|
31
|
-
icon: <Code className="w-5 h-5" />,
|
|
32
|
-
label: "Edit Code",
|
|
33
|
-
href: `/editor?snippet_id=${snippet?.snippet_id}`,
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
icon: <Bot className="w-5 h-5" />,
|
|
37
|
-
label: "Edit with AI",
|
|
38
|
-
badge: "AI",
|
|
39
|
-
href: `/ai?snippet_id=${snippet?.snippet_id}`,
|
|
40
|
-
},
|
|
41
|
-
// {
|
|
42
|
-
// icon: <GitHubLogoIcon className="w-5 h-5" />,
|
|
43
|
-
// label: "Github",
|
|
44
|
-
// },
|
|
45
|
-
{
|
|
46
|
-
icon: <GitFork className="w-5 h-5" />,
|
|
47
|
-
label: "Forks",
|
|
48
|
-
notImplemented: true,
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
icon: <AtSign className="w-5 h-5" />,
|
|
52
|
-
label: "References",
|
|
53
|
-
notImplemented: true,
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
icon: <Package className="w-5 h-5" />,
|
|
57
|
-
label: "Dependencies",
|
|
58
|
-
notImplemented: true,
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
icon: <Clock className="w-5 h-5" />,
|
|
62
|
-
label: "Versions",
|
|
63
|
-
notImplemented: true,
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
icon: <File className="w-5 h-5" />,
|
|
67
|
-
label: "Files",
|
|
68
|
-
onClick: () => {
|
|
69
|
-
if (snippet) {
|
|
70
|
-
openFilesDialog()
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
// { icon: <Settings className="w-5 h-5" />, label: "Settings" },
|
|
75
|
-
].map((item, index) => (
|
|
76
|
-
<li key={index}>
|
|
77
|
-
<PrefetchPageLink
|
|
78
|
-
href={item.href ?? "#"}
|
|
79
|
-
onClick={
|
|
80
|
-
item.notImplemented
|
|
81
|
-
? () => {
|
|
82
|
-
toast({
|
|
83
|
-
title: "Not Implemented!",
|
|
84
|
-
description: (
|
|
85
|
-
<div>
|
|
86
|
-
The {item.label} selection is not implemented yet.
|
|
87
|
-
Help us out!{" "}
|
|
88
|
-
<a
|
|
89
|
-
className="text-blue-500 hover:underline font-semibold"
|
|
90
|
-
href="https://github.com/tscircuit/tscircuit.com"
|
|
91
|
-
>
|
|
92
|
-
Check out our Github
|
|
93
|
-
</a>
|
|
94
|
-
</div>
|
|
95
|
-
),
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
: item.onClick
|
|
99
|
-
}
|
|
100
|
-
className="flex items-center gap-3 px-2 py-1.5 hover:bg-gray-200 rounded-md"
|
|
101
|
-
>
|
|
102
|
-
{item.icon}
|
|
103
|
-
<span className="text-sm">{item.label}</span>
|
|
104
|
-
{item.badge && (
|
|
105
|
-
<span className="ml-auto bg-blue-500 text-white text-xs px-1.5 py-0.5 rounded">
|
|
106
|
-
{item.badge}
|
|
107
|
-
</span>
|
|
108
|
-
)}
|
|
109
|
-
</PrefetchPageLink>
|
|
110
|
-
</li>
|
|
111
|
-
))}
|
|
112
|
-
</ul>
|
|
113
|
-
</nav>
|
|
114
|
-
<div className="p-4 border-t border-gray-200 space-y-4">
|
|
115
|
-
<div className="space-y-1">
|
|
116
|
-
<div className="text-xs font-medium">Copy embed code</div>
|
|
117
|
-
<div
|
|
118
|
-
className="text-[0.5em] p-2 rounded-sm bg-blue-50 border border-blue-200 cursor-pointer font-mono whitespace-nowrap overflow-hidden text-ellipsis"
|
|
119
|
-
onClick={() => {
|
|
120
|
-
const embedCode = `<iframe src="${window.location.origin}/preview?snippet_id=${snippet?.snippet_id}" width="100%" height="500" frameborder="0"></iframe>`
|
|
121
|
-
navigator.clipboard.writeText(embedCode)
|
|
122
|
-
toast({
|
|
123
|
-
title: "Copied!",
|
|
124
|
-
description: "Embed code copied to clipboard",
|
|
125
|
-
})
|
|
126
|
-
}}
|
|
127
|
-
>
|
|
128
|
-
{`<iframe src="${window.location.origin}/preview?snippet_id=${snippet?.snippet_id}" width="100%" height="500" frameborder="0"></iframe>`}
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
<div className="space-y-1">
|
|
132
|
-
<div className="text-xs font-medium">Copy import code</div>
|
|
133
|
-
<div
|
|
134
|
-
className="text-[0.5em] p-2 rounded-sm bg-blue-50 border border-blue-200 cursor-pointer font-mono whitespace-nowrap overflow-hidden text-ellipsis"
|
|
135
|
-
onClick={() =>
|
|
136
|
-
copyToClipboard(
|
|
137
|
-
`import CircuitModule from "@tsci/${snippet?.owner_name}.${snippet?.unscoped_name}"`,
|
|
138
|
-
)
|
|
139
|
-
}
|
|
140
|
-
>
|
|
141
|
-
import CircuitModule from "@tsci/{snippet?.owner_name}.
|
|
142
|
-
{snippet?.unscoped_name}"
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
<div className="space-y-1">
|
|
146
|
-
<div className="text-xs font-medium">Copy install command</div>
|
|
147
|
-
<div
|
|
148
|
-
className="text-[0.5em] p-2 rounded-sm bg-blue-50 border border-blue-200 cursor-pointer font-mono whitespace-nowrap overflow-hidden text-ellipsis"
|
|
149
|
-
onClick={() =>
|
|
150
|
-
copyToClipboard(
|
|
151
|
-
`tsci add @tsci/${snippet?.owner_name}.${snippet?.unscoped_name}`,
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
>
|
|
155
|
-
tsci add @tsci/{snippet?.owner_name}.{snippet?.unscoped_name}
|
|
156
|
-
</div>
|
|
157
|
-
</div>
|
|
158
|
-
</div>
|
|
159
|
-
{snippet && <FilesDialog snippetId={snippet.snippet_id} />}
|
|
160
|
-
</div>
|
|
161
|
-
)
|
|
162
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
|
|
2
|
-
import { Button } from "../ui/button"
|
|
3
|
-
import { Checkbox } from "../ui/checkbox"
|
|
4
|
-
import { useState, useEffect } from "react"
|
|
5
|
-
import { createUseDialog } from "./create-use-dialog"
|
|
6
|
-
import { useAxios } from "@/hooks/use-axios"
|
|
7
|
-
import { useToast } from "@/hooks/use-toast"
|
|
8
|
-
import { useQueryClient } from "react-query"
|
|
9
|
-
|
|
10
|
-
export const CreateOrderDialog = ({
|
|
11
|
-
open,
|
|
12
|
-
onOpenChange,
|
|
13
|
-
}: {
|
|
14
|
-
open: boolean
|
|
15
|
-
onOpenChange: (open: boolean) => void
|
|
16
|
-
}) => {
|
|
17
|
-
const axios = useAxios()
|
|
18
|
-
const { toast } = useToast()
|
|
19
|
-
const qc = useQueryClient()
|
|
20
|
-
const [pending, setPending] = useState(false)
|
|
21
|
-
const [checkpoints, setCheckpoints] = useState({
|
|
22
|
-
shipping: false,
|
|
23
|
-
errors: false,
|
|
24
|
-
parts: false,
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
if (open) {
|
|
29
|
-
validateCheckpoints()
|
|
30
|
-
}
|
|
31
|
-
}, [open])
|
|
32
|
-
|
|
33
|
-
const validateCheckpoints = async () => {
|
|
34
|
-
try {
|
|
35
|
-
// Placeholder: Check if shipping information is in profile
|
|
36
|
-
const hasShippingInfo = await checkShippingInfo()
|
|
37
|
-
|
|
38
|
-
// Placeholder: Check if PCB has no errors
|
|
39
|
-
const hasNoErrors = await checkPCBErrors()
|
|
40
|
-
|
|
41
|
-
// Placeholder: Check if all parts are available at PCB fab
|
|
42
|
-
const allPartsAvailable = await checkPartsAvailability()
|
|
43
|
-
|
|
44
|
-
setCheckpoints({
|
|
45
|
-
shipping: hasShippingInfo,
|
|
46
|
-
errors: hasNoErrors,
|
|
47
|
-
parts: allPartsAvailable,
|
|
48
|
-
})
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error("Error validating checkpoints:", error)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const checkShippingInfo = async () => {
|
|
55
|
-
// Placeholder: Implement actual check for shipping info
|
|
56
|
-
return true
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const checkPCBErrors = async () => {
|
|
60
|
-
// Placeholder: Implement actual check for PCB errors
|
|
61
|
-
return true
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const checkPartsAvailability = async () => {
|
|
65
|
-
// Placeholder: Implement actual check for parts availability
|
|
66
|
-
return true
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const handleSubmit = async () => {
|
|
70
|
-
try {
|
|
71
|
-
setPending(true)
|
|
72
|
-
// TODO: Implement order submission logic
|
|
73
|
-
onOpenChange(false)
|
|
74
|
-
setPending(false)
|
|
75
|
-
toast({
|
|
76
|
-
title: "Order submitted",
|
|
77
|
-
description: "Your order has been successfully submitted.",
|
|
78
|
-
})
|
|
79
|
-
qc.invalidateQueries({ queryKey: ["orders"] })
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error("Error submitting order:", error)
|
|
82
|
-
toast({
|
|
83
|
-
title: "Error",
|
|
84
|
-
description: "Failed to submit the order. Please try again.",
|
|
85
|
-
variant: "destructive",
|
|
86
|
-
})
|
|
87
|
-
} finally {
|
|
88
|
-
setPending(false)
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
94
|
-
<DialogContent>
|
|
95
|
-
<DialogHeader>
|
|
96
|
-
<DialogTitle>Create Order</DialogTitle>
|
|
97
|
-
</DialogHeader>
|
|
98
|
-
<p className="text-sm text-gray-500 mb-4">
|
|
99
|
-
Order the circuit board fully assembled from a PCB fabricator
|
|
100
|
-
</p>
|
|
101
|
-
<div className="space-y-4">
|
|
102
|
-
<div className="flex items-center space-x-2">
|
|
103
|
-
<Checkbox id="shipping" checked={checkpoints.shipping} />
|
|
104
|
-
<label
|
|
105
|
-
htmlFor="shipping"
|
|
106
|
-
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
107
|
-
>
|
|
108
|
-
Shipping Information in Profile
|
|
109
|
-
</label>
|
|
110
|
-
</div>
|
|
111
|
-
<div className="flex items-center space-x-2">
|
|
112
|
-
<Checkbox id="errors" checked={checkpoints.errors} />
|
|
113
|
-
<label
|
|
114
|
-
htmlFor="errors"
|
|
115
|
-
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
116
|
-
>
|
|
117
|
-
PCB Has No Errors
|
|
118
|
-
</label>
|
|
119
|
-
</div>
|
|
120
|
-
<div className="flex items-center space-x-2">
|
|
121
|
-
<Checkbox id="parts" checked={checkpoints.parts} />
|
|
122
|
-
<label
|
|
123
|
-
htmlFor="parts"
|
|
124
|
-
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
125
|
-
>
|
|
126
|
-
All parts available at PCB fabricator
|
|
127
|
-
</label>
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
<div className="flex justify-end space-x-2 mt-4">
|
|
131
|
-
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
|
132
|
-
Cancel
|
|
133
|
-
</Button>
|
|
134
|
-
<Button
|
|
135
|
-
onClick={handleSubmit}
|
|
136
|
-
disabled={pending || !Object.values(checkpoints).every(Boolean)}
|
|
137
|
-
>
|
|
138
|
-
{pending ? "Submitting..." : "Submit Order"}
|
|
139
|
-
</Button>
|
|
140
|
-
</div>
|
|
141
|
-
</DialogContent>
|
|
142
|
-
</Dialog>
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export const useCreateOrderDialog = createUseDialog(CreateOrderDialog)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { useMemo } from "react"
|
|
2
|
-
import * as Babel from "@babel/standalone"
|
|
3
|
-
|
|
4
|
-
export const safeCompileTsx = (
|
|
5
|
-
code: string,
|
|
6
|
-
):
|
|
7
|
-
| { success: true; compiledTsx: string; error?: undefined }
|
|
8
|
-
| { success: false; error: Error; compiledTsx?: undefined } => {
|
|
9
|
-
try {
|
|
10
|
-
return {
|
|
11
|
-
success: true,
|
|
12
|
-
compiledTsx:
|
|
13
|
-
Babel.transform(code, {
|
|
14
|
-
presets: ["react", "typescript"],
|
|
15
|
-
plugins: ["transform-modules-commonjs"],
|
|
16
|
-
filename: "virtual.tsx",
|
|
17
|
-
}).code || "",
|
|
18
|
-
}
|
|
19
|
-
} catch (error: any) {
|
|
20
|
-
return { success: false, error }
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const useCompiledTsx = (
|
|
25
|
-
code?: string,
|
|
26
|
-
{ isStreaming = false }: { isStreaming?: boolean } = {},
|
|
27
|
-
) => {
|
|
28
|
-
return useMemo(() => {
|
|
29
|
-
if (!code) return ""
|
|
30
|
-
if (isStreaming) return ""
|
|
31
|
-
const result = safeCompileTsx(code)
|
|
32
|
-
if (result.success) {
|
|
33
|
-
return result.compiledTsx
|
|
34
|
-
}
|
|
35
|
-
return `Error: ${result.error.message}`
|
|
36
|
-
}, [code, isStreaming])
|
|
37
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { Circuit } from "@tscircuit/core"
|
|
2
|
-
import { useEffect, useMemo, useState } from "react"
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { useCompiledTsx } from "../use-compiled-tsx"
|
|
5
|
-
import { createJSCADRenderer } from "jscad-fiber"
|
|
6
|
-
import { jscadPlanner } from "jscad-planner"
|
|
7
|
-
|
|
8
|
-
export const constructCircuit = ({
|
|
9
|
-
UserElm,
|
|
10
|
-
type,
|
|
11
|
-
circuitDisplayName,
|
|
12
|
-
}: {
|
|
13
|
-
UserElm: any
|
|
14
|
-
type: "board" | "footprint" | "package" | "model"
|
|
15
|
-
circuitDisplayName?: string
|
|
16
|
-
}) => {
|
|
17
|
-
const circuit = new Circuit()
|
|
18
|
-
|
|
19
|
-
if (circuitDisplayName) {
|
|
20
|
-
circuit.name = circuitDisplayName
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (type === "board") {
|
|
24
|
-
circuit.add(<UserElm />)
|
|
25
|
-
// HACK: switch to selectOne when root fixes bug with selecting board
|
|
26
|
-
const board = circuit.root?.children[0]
|
|
27
|
-
// const board = circuit.selectOne("board")
|
|
28
|
-
if (board) {
|
|
29
|
-
board.setProps({
|
|
30
|
-
...board.props,
|
|
31
|
-
})
|
|
32
|
-
}
|
|
33
|
-
} else if (type === "package") {
|
|
34
|
-
circuit.add(
|
|
35
|
-
<board width="50mm" height="50mm">
|
|
36
|
-
<UserElm name="U1" />
|
|
37
|
-
</board>,
|
|
38
|
-
)
|
|
39
|
-
} else if (type === "footprint") {
|
|
40
|
-
circuit.add(
|
|
41
|
-
<board width="10mm" height="10mm">
|
|
42
|
-
<chip name="U1" footprint={<UserElm />} />
|
|
43
|
-
</board>,
|
|
44
|
-
)
|
|
45
|
-
} else if (type === "model") {
|
|
46
|
-
const jscadGeoms: any[] = []
|
|
47
|
-
const { createJSCADRoot } = createJSCADRenderer(jscadPlanner as any)
|
|
48
|
-
const jscadRoot = createJSCADRoot(jscadGeoms)
|
|
49
|
-
jscadRoot.render(<UserElm />)
|
|
50
|
-
circuit.add(
|
|
51
|
-
<board width="10mm" height="10mm">
|
|
52
|
-
<chip
|
|
53
|
-
name="U1"
|
|
54
|
-
cadModel={{
|
|
55
|
-
jscad: jscadGeoms[0],
|
|
56
|
-
}}
|
|
57
|
-
/>
|
|
58
|
-
</board>,
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
return circuit
|
|
62
|
-
}
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import * as tscircuitCore from "@tscircuit/core"
|
|
2
|
-
import { getImportsFromCode } from "@tscircuit/prompt-benchmarks/code-runner-utils"
|
|
3
|
-
import type { AnyCircuitElement } from "circuit-json"
|
|
4
|
-
import * as jscadFiber from "jscad-fiber"
|
|
5
|
-
import * as React from "react"
|
|
6
|
-
import * as tscircuitMathUtils from "@tscircuit/math-utils"
|
|
7
|
-
import { useEffect, useMemo, useReducer, useRef, useState } from "react"
|
|
8
|
-
import { safeCompileTsx } from "../use-compiled-tsx"
|
|
9
|
-
import { useSnippetsBaseApiUrl } from "../use-snippets-base-api-url"
|
|
10
|
-
import { constructCircuit } from "./construct-circuit"
|
|
11
|
-
import { evalCompiledJs } from "./eval-compiled-js"
|
|
12
|
-
import { getSyntaxError } from "@/lib/utils/getSyntaxError"
|
|
13
|
-
|
|
14
|
-
type RunTsxResult = {
|
|
15
|
-
compiledModule: any
|
|
16
|
-
message: string
|
|
17
|
-
circuitJson: AnyCircuitElement[] | null
|
|
18
|
-
compiledJs?: string
|
|
19
|
-
isRunningCode: boolean
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @deprecated will be removed in future version, use @tscircuit/eval or
|
|
24
|
-
* @tscircuit/runframe
|
|
25
|
-
*/
|
|
26
|
-
export const useRunTsx = ({
|
|
27
|
-
code,
|
|
28
|
-
userImports,
|
|
29
|
-
type,
|
|
30
|
-
isStreaming = false,
|
|
31
|
-
circuitDisplayName,
|
|
32
|
-
}: {
|
|
33
|
-
code?: string
|
|
34
|
-
userImports?: Record<string, object>
|
|
35
|
-
type?: "board" | "footprint" | "package" | "model"
|
|
36
|
-
isStreaming?: boolean
|
|
37
|
-
circuitDisplayName?: string
|
|
38
|
-
} = {}): RunTsxResult & {
|
|
39
|
-
circuitJsonKey: string
|
|
40
|
-
triggerRunTsx: () => void
|
|
41
|
-
tsxRunTriggerCount: number
|
|
42
|
-
} => {
|
|
43
|
-
type ??= "board"
|
|
44
|
-
const [tsxRunTriggerCount, incTsxRunTriggerCount] = useReducer(
|
|
45
|
-
(c) => c + 1,
|
|
46
|
-
0,
|
|
47
|
-
)
|
|
48
|
-
const [tsxResult, setTsxResult] = useState<RunTsxResult>({
|
|
49
|
-
compiledModule: null,
|
|
50
|
-
message: "",
|
|
51
|
-
circuitJson: null,
|
|
52
|
-
isRunningCode: false,
|
|
53
|
-
})
|
|
54
|
-
const apiBaseUrl = useSnippetsBaseApiUrl()
|
|
55
|
-
const preSuppliedImportsRef = useRef<Record<string, any>>({})
|
|
56
|
-
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
if (tsxRunTriggerCount === 0) return
|
|
59
|
-
if (isStreaming) {
|
|
60
|
-
setTsxResult({
|
|
61
|
-
compiledModule: null,
|
|
62
|
-
message: "",
|
|
63
|
-
circuitJson: null,
|
|
64
|
-
isRunningCode: false,
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
if (!code) return
|
|
68
|
-
const syntaxError = getSyntaxError(code)
|
|
69
|
-
if (syntaxError) {
|
|
70
|
-
setTsxResult({
|
|
71
|
-
compiledModule: null,
|
|
72
|
-
message: syntaxError,
|
|
73
|
-
circuitJson: null,
|
|
74
|
-
isRunningCode: false,
|
|
75
|
-
})
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
async function run() {
|
|
79
|
-
setTsxResult({
|
|
80
|
-
compiledModule: null,
|
|
81
|
-
message: "",
|
|
82
|
-
circuitJson: null,
|
|
83
|
-
isRunningCode: true,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
const userCodeTsciImports = getImportsFromCode(code!).filter((imp) =>
|
|
87
|
-
imp.startsWith("@tsci/"),
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
const preSuppliedImports: Record<string, any> =
|
|
91
|
-
preSuppliedImportsRef.current
|
|
92
|
-
|
|
93
|
-
for (const [importName, importValue] of Object.entries(
|
|
94
|
-
userImports ?? {},
|
|
95
|
-
)) {
|
|
96
|
-
preSuppliedImports[importName] = importValue
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const __tscircuit_require = (name: string) => {
|
|
100
|
-
if (
|
|
101
|
-
name === "./manual-edits.json" &&
|
|
102
|
-
preSuppliedImports["./manual-edits.json"] === ""
|
|
103
|
-
) {
|
|
104
|
-
return preSuppliedImports["./manual-edits.json"]
|
|
105
|
-
}
|
|
106
|
-
if (!preSuppliedImports[name]) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
`Import "${name}" not found (imports available: ${Object.keys(preSuppliedImports).join(",")})`,
|
|
109
|
-
)
|
|
110
|
-
}
|
|
111
|
-
return preSuppliedImports[name]
|
|
112
|
-
}
|
|
113
|
-
;(globalThis as any).__tscircuit_require = __tscircuit_require
|
|
114
|
-
preSuppliedImports["@tscircuit/core"] = tscircuitCore
|
|
115
|
-
preSuppliedImports["tscircuit"] = tscircuitCore
|
|
116
|
-
preSuppliedImports["@tscircuit/math-utils"] = tscircuitMathUtils
|
|
117
|
-
preSuppliedImports["react"] = React
|
|
118
|
-
preSuppliedImports["jscad-fiber"] = jscadFiber
|
|
119
|
-
globalThis.React = React
|
|
120
|
-
|
|
121
|
-
async function addImport(importName: string, depth = 0) {
|
|
122
|
-
if (!importName.startsWith("@tsci/")) return
|
|
123
|
-
if (preSuppliedImports[importName]) return
|
|
124
|
-
if (depth > 5) {
|
|
125
|
-
console.log("Max depth for imports reached")
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const fullSnippetName = importName
|
|
130
|
-
.replace("@tsci/", "")
|
|
131
|
-
.replace(".", "/")
|
|
132
|
-
const { snippet: importedSnippet, error } = await fetch(
|
|
133
|
-
`${apiBaseUrl}/snippets/get?name=${fullSnippetName}`,
|
|
134
|
-
)
|
|
135
|
-
.then((res) => res.json())
|
|
136
|
-
.catch((e) => ({ error: e }))
|
|
137
|
-
|
|
138
|
-
if (error) {
|
|
139
|
-
console.error("Error fetching import", importName, error)
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const { compiled_js, code } = importedSnippet
|
|
144
|
-
|
|
145
|
-
const importNames = getImportsFromCode(code!)
|
|
146
|
-
|
|
147
|
-
for (const importName of importNames) {
|
|
148
|
-
if (!preSuppliedImports[importName]) {
|
|
149
|
-
await addImport(importName, depth + 1)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
preSuppliedImports[importName] = evalCompiledJs(compiled_js).exports
|
|
155
|
-
} catch (e) {
|
|
156
|
-
console.error("Error importing snippet", e)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
for (const userCodeTsciImport of userCodeTsciImports) {
|
|
161
|
-
await addImport(userCodeTsciImport)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const { success, compiledTsx: compiledJs, error } = safeCompileTsx(code!)
|
|
165
|
-
|
|
166
|
-
if (!success) {
|
|
167
|
-
setTsxResult({
|
|
168
|
-
compiledModule: null,
|
|
169
|
-
message: `Compile Error: ${error.message}`,
|
|
170
|
-
circuitJson: null,
|
|
171
|
-
isRunningCode: false,
|
|
172
|
-
})
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
try {
|
|
176
|
-
const module = evalCompiledJs(compiledJs!)
|
|
177
|
-
|
|
178
|
-
const componentExportKeys = Object.keys(module.exports).filter(
|
|
179
|
-
(key) => !key.startsWith("use"),
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
if (componentExportKeys.length > 1) {
|
|
183
|
-
throw new Error(
|
|
184
|
-
`Too many exports, only export one component. You exported: ${JSON.stringify(Object.keys(module.exports))}`,
|
|
185
|
-
)
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const primaryKey = componentExportKeys[0]
|
|
189
|
-
|
|
190
|
-
const UserElm = (props: any) =>
|
|
191
|
-
React.createElement(module.exports[primaryKey], props)
|
|
192
|
-
|
|
193
|
-
try {
|
|
194
|
-
const circuit = constructCircuit({
|
|
195
|
-
UserElm,
|
|
196
|
-
type: type as any,
|
|
197
|
-
circuitDisplayName,
|
|
198
|
-
})
|
|
199
|
-
const renderPromise = circuit.renderUntilSettled()
|
|
200
|
-
|
|
201
|
-
// wait one tick to allow a single render pass
|
|
202
|
-
await new Promise((resolve) => setTimeout(resolve, 1))
|
|
203
|
-
|
|
204
|
-
let circuitJson = circuit.getCircuitJson()
|
|
205
|
-
setTsxResult({
|
|
206
|
-
compiledModule: module,
|
|
207
|
-
compiledJs,
|
|
208
|
-
message: "",
|
|
209
|
-
circuitJson: circuitJson as AnyCircuitElement[],
|
|
210
|
-
isRunningCode: true,
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
await renderPromise
|
|
214
|
-
|
|
215
|
-
circuitJson = circuit.getCircuitJson()
|
|
216
|
-
setTsxResult({
|
|
217
|
-
compiledModule: module,
|
|
218
|
-
compiledJs,
|
|
219
|
-
message: "",
|
|
220
|
-
circuitJson: circuitJson as AnyCircuitElement[],
|
|
221
|
-
isRunningCode: false,
|
|
222
|
-
})
|
|
223
|
-
} catch (error: any) {
|
|
224
|
-
console.error("Evaluation error:", error)
|
|
225
|
-
setTsxResult({
|
|
226
|
-
compiledModule: module,
|
|
227
|
-
message: `Render Error: ${error.message}`,
|
|
228
|
-
circuitJson: null,
|
|
229
|
-
isRunningCode: false,
|
|
230
|
-
})
|
|
231
|
-
}
|
|
232
|
-
} catch (error: any) {
|
|
233
|
-
console.error("Evaluation error:", error)
|
|
234
|
-
setTsxResult({
|
|
235
|
-
compiledModule: null,
|
|
236
|
-
message: `Eval Error: ${error.message}\n\n${error.stack}`,
|
|
237
|
-
circuitJson: null,
|
|
238
|
-
isRunningCode: false,
|
|
239
|
-
})
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
run()
|
|
243
|
-
}, [tsxRunTriggerCount])
|
|
244
|
-
|
|
245
|
-
const circuitJsonKey: string = useMemo(() => {
|
|
246
|
-
if (!tsxResult.circuitJson) return ""
|
|
247
|
-
return `cj-${Math.random().toString(36).substring(2, 15)}`
|
|
248
|
-
}, [tsxResult.circuitJson, tsxResult.circuitJson?.length])
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
...tsxResult,
|
|
252
|
-
circuitJsonKey: circuitJsonKey,
|
|
253
|
-
triggerRunTsx: incTsxRunTriggerCount,
|
|
254
|
-
tsxRunTriggerCount,
|
|
255
|
-
}
|
|
256
|
-
}
|