@tscircuit/fake-snippets 0.0.67 → 0.0.69
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/routes/packages/update.test.ts +104 -0
- package/bun.lock +26 -83
- package/dist/bundle.js +17 -5
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -1
- package/dist/schema.d.ts +8 -0
- package/dist/schema.js +2 -1
- package/fake-snippets-api/lib/db/schema.ts +4 -0
- package/fake-snippets-api/routes/api/packages/create.ts +1 -0
- package/fake-snippets-api/routes/api/packages/update.ts +11 -2
- package/package.json +3 -4
- package/src/App.tsx +0 -4
- package/src/components/CmdKMenu.tsx +19 -19
- package/src/components/FAQ.tsx +3 -1
- package/src/components/FileSidebar.tsx +50 -1
- package/src/components/Header2.tsx +20 -9
- package/src/components/JLCPCBImportDialog.tsx +13 -16
- package/src/components/ViewPackagePage/components/important-files-view.tsx +1 -1
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -0
- package/src/components/ViewPackagePage/components/package-header.tsx +22 -12
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +23 -7
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +1 -0
- package/src/components/dialogs/confirm-delete-package-dialog.tsx +8 -0
- package/src/components/dialogs/edit-package-details-dialog.tsx +177 -139
- package/src/components/package-port/CodeAndPreview.tsx +40 -19
- package/src/components/package-port/CodeEditor.tsx +8 -27
- package/src/components/package-port/EditorNav.tsx +1 -11
- package/src/hooks/use-package-details-form.ts +15 -1
- package/src/hooks/useFileManagement.ts +59 -0
- package/src/index.css +13 -0
- package/src/lib/utils/isValidFileName.ts +5 -0
- package/src/pages/dashboard.tsx +1 -0
- package/src/pages/quickstart.tsx +5 -5
- package/src/pages/search.tsx +1 -1
- package/src/pages/user-profile.tsx +1 -0
- package/src/components/OrderPreviewContent.tsx +0 -61
- package/src/components/ViewSnippetSidebar.tsx +0 -162
- package/src/components/dialogs/create-order-dialog.tsx +0 -146
- package/src/pages/preview.tsx +0 -44
- package/src/pages/view-order.tsx +0 -111
|
@@ -28,7 +28,7 @@ import CodeEditorHeader from "@/components/package-port/CodeEditorHeader"
|
|
|
28
28
|
import { useCodeCompletionApi } from "@/hooks/use-code-completion-ai-api"
|
|
29
29
|
import FileSidebar from "../FileSidebar"
|
|
30
30
|
import { findTargetFile } from "@/lib/utils/findTargetFile"
|
|
31
|
-
import type { PackageFile } from "./CodeAndPreview"
|
|
31
|
+
import type { CreateFileProps, PackageFile } from "./CodeAndPreview"
|
|
32
32
|
import { useShikiHighlighter } from "@/hooks/use-shiki-highlighter"
|
|
33
33
|
|
|
34
34
|
const defaultImports = `
|
|
@@ -39,7 +39,6 @@ import type { CommonLayoutProps } from "@tscircuit/props"
|
|
|
39
39
|
|
|
40
40
|
export const CodeEditor = ({
|
|
41
41
|
onCodeChange,
|
|
42
|
-
onDtsChange,
|
|
43
42
|
readOnly = false,
|
|
44
43
|
files = [],
|
|
45
44
|
isStreaming = false,
|
|
@@ -48,10 +47,11 @@ export const CodeEditor = ({
|
|
|
48
47
|
pkgFilesLoaded,
|
|
49
48
|
currentFile,
|
|
50
49
|
setCurrentFile,
|
|
50
|
+
handleCreateFile,
|
|
51
51
|
}: {
|
|
52
52
|
onCodeChange: (code: string, filename?: string) => void
|
|
53
|
-
onDtsChange?: (dts: string) => void
|
|
54
53
|
files: PackageFile[]
|
|
54
|
+
handleCreateFile: (props: CreateFileProps) => void
|
|
55
55
|
readOnly?: boolean
|
|
56
56
|
isStreaming?: boolean
|
|
57
57
|
pkgFilesLoaded?: boolean
|
|
@@ -67,9 +67,8 @@ export const CodeEditor = ({
|
|
|
67
67
|
const codeCompletionApi = useCodeCompletionApi()
|
|
68
68
|
const [cursorPosition, setCursorPosition] = useState<number | null>(null)
|
|
69
69
|
const [code, setCode] = useState(files[0]?.content || "")
|
|
70
|
-
const [isCodeEditorReady, setIsCodeEditorReady] = useState(false)
|
|
71
70
|
|
|
72
|
-
const { highlighter
|
|
71
|
+
const { highlighter } = useShikiHighlighter()
|
|
73
72
|
|
|
74
73
|
// Get URL search params for file_path
|
|
75
74
|
const urlParams = new URLSearchParams(window.location.search)
|
|
@@ -191,9 +190,6 @@ export const CodeEditor = ({
|
|
|
191
190
|
return fetch(input, init)
|
|
192
191
|
},
|
|
193
192
|
delegate: {
|
|
194
|
-
finished: () => {
|
|
195
|
-
setIsCodeEditorReady(true)
|
|
196
|
-
},
|
|
197
193
|
started: () => {
|
|
198
194
|
const manualEditsTypeDeclaration = `
|
|
199
195
|
declare module "manual-edits.json" {
|
|
@@ -248,20 +244,6 @@ export const CodeEditor = ({
|
|
|
248
244
|
// setCode(newContent)
|
|
249
245
|
onCodeChange(newContent, currentFile)
|
|
250
246
|
onFileContentChanged?.(currentFile, newContent)
|
|
251
|
-
|
|
252
|
-
// Generate TypeScript declarations for TypeScript/TSX files
|
|
253
|
-
if (currentFile.endsWith(".ts") || currentFile.endsWith(".tsx")) {
|
|
254
|
-
const { outputFiles } = env.languageService.getEmitOutput(
|
|
255
|
-
currentFile,
|
|
256
|
-
true,
|
|
257
|
-
)
|
|
258
|
-
const dtsFile = outputFiles.find((file) =>
|
|
259
|
-
file.name.endsWith(".d.ts"),
|
|
260
|
-
)
|
|
261
|
-
if (dtsFile?.text && onDtsChange) {
|
|
262
|
-
onDtsChange(dtsFile.text)
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
247
|
}
|
|
266
248
|
if (update.selectionSet) {
|
|
267
249
|
const pos = update.state.selection.main.head
|
|
@@ -452,8 +434,6 @@ export const CodeEditor = ({
|
|
|
452
434
|
|
|
453
435
|
if (currentFile.endsWith(".tsx") || currentFile.endsWith(".ts")) {
|
|
454
436
|
ata(`${defaultImports}${code}`)
|
|
455
|
-
} else if (!!currentFile) {
|
|
456
|
-
setIsCodeEditorReady(true)
|
|
457
437
|
}
|
|
458
438
|
|
|
459
439
|
return () => {
|
|
@@ -542,6 +522,7 @@ export const CodeEditor = ({
|
|
|
542
522
|
[sidebarOpen, setSidebarOpen] as ReturnType<typeof useState<boolean>>
|
|
543
523
|
}
|
|
544
524
|
onFileSelect={handleFileChange}
|
|
525
|
+
handleCreateFile={handleCreateFile}
|
|
545
526
|
/>
|
|
546
527
|
<div className="flex flex-col flex-1 w-full min-w-0 h-full">
|
|
547
528
|
{showImportAndFormatButtons && (
|
|
@@ -560,9 +541,9 @@ export const CodeEditor = ({
|
|
|
560
541
|
)}
|
|
561
542
|
<div
|
|
562
543
|
ref={editorRef}
|
|
563
|
-
className={
|
|
564
|
-
|
|
565
|
-
}
|
|
544
|
+
className={
|
|
545
|
+
"flex-1 overflow-auto [&_.cm-editor]:h-full [&_.cm-scroller]:!h-full"
|
|
546
|
+
}
|
|
566
547
|
/>
|
|
567
548
|
</div>
|
|
568
549
|
</div>
|
|
@@ -39,7 +39,6 @@ import { Link, useLocation } from "wouter"
|
|
|
39
39
|
import { useAxios } from "@/hooks/use-axios"
|
|
40
40
|
import { useToast } from "@/hooks/use-toast"
|
|
41
41
|
import { useConfirmDeletePackageDialog } from "@/components/dialogs/confirm-delete-package-dialog"
|
|
42
|
-
import { useCreateOrderDialog } from "@/components/dialogs/create-order-dialog"
|
|
43
42
|
import { useFilesDialog } from "@/components/dialogs/files-dialog"
|
|
44
43
|
import { useViewTsFilesDialog } from "@/components/dialogs/view-ts-files-dialog"
|
|
45
44
|
import { DownloadButtonAndMenu } from "@/components/DownloadButtonAndMenu"
|
|
@@ -81,8 +80,6 @@ export default function EditorNav({
|
|
|
81
80
|
} = useUpdatePackageDescriptionDialog()
|
|
82
81
|
const { Dialog: DeleteDialog, openDialog: openDeleteDialog } =
|
|
83
82
|
useConfirmDeletePackageDialog()
|
|
84
|
-
const { Dialog: CreateOrderDialog, openDialog: openCreateOrderDialog } =
|
|
85
|
-
useCreateOrderDialog()
|
|
86
83
|
const { Dialog: FilesDialog, openDialog: openFilesDialog } = useFilesDialog()
|
|
87
84
|
const { Dialog: ViewTsFilesDialog, openDialog: openViewTsFilesDialog } =
|
|
88
85
|
useViewTsFilesDialog()
|
|
@@ -358,13 +355,6 @@ export default function EditorNav({
|
|
|
358
355
|
</Button>
|
|
359
356
|
</DropdownMenuTrigger>
|
|
360
357
|
<DropdownMenuContent>
|
|
361
|
-
<DropdownMenuItem
|
|
362
|
-
className="text-xs"
|
|
363
|
-
onClick={() => openCreateOrderDialog()}
|
|
364
|
-
>
|
|
365
|
-
<PackageIcon className="mr-2 h-3 w-3" />
|
|
366
|
-
Submit Order
|
|
367
|
-
</DropdownMenuItem>
|
|
368
358
|
<DropdownMenuItem
|
|
369
359
|
className="text-xs"
|
|
370
360
|
onClick={() => openFilesDialog()}
|
|
@@ -519,8 +509,8 @@ export default function EditorNav({
|
|
|
519
509
|
<DeleteDialog
|
|
520
510
|
packageId={pkg?.package_id ?? ""}
|
|
521
511
|
packageName={pkg?.unscoped_name ?? ""}
|
|
512
|
+
packageOwner={pkg?.owner_github_username ?? ""}
|
|
522
513
|
/>
|
|
523
|
-
<CreateOrderDialog />
|
|
524
514
|
<FilesDialog snippetId={pkg?.package_id ?? ""} />
|
|
525
515
|
<ViewTsFilesDialog />
|
|
526
516
|
</nav>
|
|
@@ -15,6 +15,7 @@ interface PackageDetailsForm {
|
|
|
15
15
|
website: string
|
|
16
16
|
license: string | null
|
|
17
17
|
visibility: string
|
|
18
|
+
defaultView: string
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
interface UsePackageDetailsFormProps {
|
|
@@ -22,6 +23,7 @@ interface UsePackageDetailsFormProps {
|
|
|
22
23
|
initialWebsite: string
|
|
23
24
|
initialLicense: string | null
|
|
24
25
|
initialVisibility: string
|
|
26
|
+
initialDefaultView: string
|
|
25
27
|
isDialogOpen: boolean
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -30,6 +32,7 @@ export const usePackageDetailsForm = ({
|
|
|
30
32
|
initialWebsite,
|
|
31
33
|
initialLicense,
|
|
32
34
|
initialVisibility,
|
|
35
|
+
initialDefaultView,
|
|
33
36
|
isDialogOpen,
|
|
34
37
|
}: UsePackageDetailsFormProps) => {
|
|
35
38
|
const [formData, setFormData] = useState<PackageDetailsForm>({
|
|
@@ -37,6 +40,7 @@ export const usePackageDetailsForm = ({
|
|
|
37
40
|
website: initialWebsite,
|
|
38
41
|
license: initialLicense || null,
|
|
39
42
|
visibility: initialVisibility,
|
|
43
|
+
defaultView: initialDefaultView,
|
|
40
44
|
})
|
|
41
45
|
const [websiteError, setWebsiteError] = useState<string | null>(null)
|
|
42
46
|
|
|
@@ -47,6 +51,7 @@ export const usePackageDetailsForm = ({
|
|
|
47
51
|
website: initialWebsite,
|
|
48
52
|
license: initialLicense || null,
|
|
49
53
|
visibility: initialVisibility,
|
|
54
|
+
defaultView: initialDefaultView,
|
|
50
55
|
})
|
|
51
56
|
setWebsiteError(null)
|
|
52
57
|
}
|
|
@@ -56,6 +61,7 @@ export const usePackageDetailsForm = ({
|
|
|
56
61
|
initialWebsite,
|
|
57
62
|
initialLicense,
|
|
58
63
|
initialVisibility,
|
|
64
|
+
initialDefaultView,
|
|
59
65
|
])
|
|
60
66
|
|
|
61
67
|
useEffect(() => {
|
|
@@ -76,18 +82,25 @@ export const usePackageDetailsForm = ({
|
|
|
76
82
|
[formData.visibility, initialVisibility],
|
|
77
83
|
)
|
|
78
84
|
|
|
85
|
+
const hasDefaultViewChanged = useMemo(
|
|
86
|
+
() => formData.defaultView !== initialDefaultView,
|
|
87
|
+
[formData.defaultView, initialDefaultView],
|
|
88
|
+
)
|
|
89
|
+
|
|
79
90
|
const hasChanges = useMemo(
|
|
80
91
|
() =>
|
|
81
92
|
formData.description !== initialDescription ||
|
|
82
93
|
formData.website !== initialWebsite ||
|
|
83
94
|
formData.license !== initialLicense ||
|
|
84
|
-
formData.visibility !== initialVisibility
|
|
95
|
+
formData.visibility !== initialVisibility ||
|
|
96
|
+
formData.defaultView !== initialDefaultView,
|
|
85
97
|
[
|
|
86
98
|
formData,
|
|
87
99
|
initialDescription,
|
|
88
100
|
initialWebsite,
|
|
89
101
|
initialLicense,
|
|
90
102
|
initialVisibility,
|
|
103
|
+
initialDefaultView,
|
|
91
104
|
],
|
|
92
105
|
)
|
|
93
106
|
|
|
@@ -99,6 +112,7 @@ export const usePackageDetailsForm = ({
|
|
|
99
112
|
websiteError,
|
|
100
113
|
hasLicenseChanged,
|
|
101
114
|
hasVisibilityChanged,
|
|
115
|
+
hasDefaultViewChanged,
|
|
102
116
|
hasChanges,
|
|
103
117
|
isFormValid,
|
|
104
118
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from "react"
|
|
2
|
+
import { isValidFileName } from "@/lib/utils/isValidFileName"
|
|
3
|
+
import {
|
|
4
|
+
CodeAndPreviewState,
|
|
5
|
+
CreateFileProps,
|
|
6
|
+
} from "../components/package-port/CodeAndPreview"
|
|
7
|
+
|
|
8
|
+
export function useFileManagement(
|
|
9
|
+
state: CodeAndPreviewState,
|
|
10
|
+
setState: Dispatch<SetStateAction<CodeAndPreviewState>>,
|
|
11
|
+
) {
|
|
12
|
+
const handleCreateFile = async ({
|
|
13
|
+
newFileName,
|
|
14
|
+
setErrorMessage,
|
|
15
|
+
onFileSelect,
|
|
16
|
+
setNewFileName,
|
|
17
|
+
setIsCreatingFile,
|
|
18
|
+
}: CreateFileProps) => {
|
|
19
|
+
newFileName = newFileName.trim()
|
|
20
|
+
if (!newFileName) {
|
|
21
|
+
setErrorMessage("File name cannot be empty")
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
if (!isValidFileName(newFileName)) {
|
|
25
|
+
setErrorMessage(
|
|
26
|
+
'Invalid file name. Avoid using special characters like <>:"/\\|?*',
|
|
27
|
+
)
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
setErrorMessage("")
|
|
31
|
+
|
|
32
|
+
const fileExists = state.pkgFilesWithContent.some(
|
|
33
|
+
(file) => file.path === newFileName,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if (fileExists) {
|
|
37
|
+
setErrorMessage("A file with this name already exists")
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
setState((prev) => {
|
|
42
|
+
const updatedFiles = [
|
|
43
|
+
...prev.pkgFilesWithContent,
|
|
44
|
+
{ path: newFileName, content: "" },
|
|
45
|
+
]
|
|
46
|
+
return {
|
|
47
|
+
...prev,
|
|
48
|
+
pkgFilesWithContent: updatedFiles,
|
|
49
|
+
} as CodeAndPreviewState
|
|
50
|
+
})
|
|
51
|
+
onFileSelect(newFileName)
|
|
52
|
+
setIsCreatingFile(false)
|
|
53
|
+
setNewFileName("")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
handleCreateFile,
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/index.css
CHANGED
|
@@ -7,6 +7,19 @@
|
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/* add the code bellow */
|
|
11
|
+
@layer utilities {
|
|
12
|
+
/* Hide scrollbar for Chrome, Safari and Opera */
|
|
13
|
+
.no-scrollbar::-webkit-scrollbar {
|
|
14
|
+
display: none;
|
|
15
|
+
}
|
|
16
|
+
/* Hide scrollbar for IE, Edge and Firefox */
|
|
17
|
+
.no-scrollbar {
|
|
18
|
+
-ms-overflow-style: none; /* IE and Edge */
|
|
19
|
+
scrollbar-width: none; /* Firefox */
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
10
23
|
.shiki {
|
|
11
24
|
font-family: "Fira Code", monospace;
|
|
12
25
|
font-size: 14px;
|
package/src/pages/dashboard.tsx
CHANGED
package/src/pages/quickstart.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { useQuery } from "react-query"
|
|
|
3
3
|
import { useAxios } from "@/hooks/use-axios"
|
|
4
4
|
import Header from "@/components/Header"
|
|
5
5
|
import Footer from "@/components/Footer"
|
|
6
|
-
import { Package
|
|
6
|
+
import { Package } from "fake-snippets-api/lib/db/schema"
|
|
7
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"
|
|
@@ -48,7 +48,7 @@ export const QuickstartPage = () => {
|
|
|
48
48
|
<Header />
|
|
49
49
|
<div className="container mx-auto px-4 py-8">
|
|
50
50
|
<div className="mb-8 hidden md:block">
|
|
51
|
-
<h2 className="text-xl font-semibold mb-4">Recent
|
|
51
|
+
<h2 className="text-xl font-semibold mb-4">Recent Packages</h2>
|
|
52
52
|
{isLoading ? (
|
|
53
53
|
<div>Loading...</div>
|
|
54
54
|
) : (
|
|
@@ -63,7 +63,7 @@ export const QuickstartPage = () => {
|
|
|
63
63
|
.map((pkg) => (
|
|
64
64
|
<PrefetchPageLink
|
|
65
65
|
key={pkg.package_id}
|
|
66
|
-
href={`/editor?
|
|
66
|
+
href={`/editor?package_id=${pkg.package_id}`}
|
|
67
67
|
>
|
|
68
68
|
<Card className="hover:shadow-md transition-shadow rounded-md flex flex-col h-full">
|
|
69
69
|
<CardHeader className="pb-0 p-4">
|
|
@@ -85,7 +85,7 @@ export const QuickstartPage = () => {
|
|
|
85
85
|
</div>
|
|
86
86
|
|
|
87
87
|
<div className="mb-8">
|
|
88
|
-
<h2 className="text-xl font-semibold mb-4">Start Blank
|
|
88
|
+
<h2 className="text-xl font-semibold mb-4">Start Blank Packages</h2>
|
|
89
89
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
90
90
|
{blankTemplates.map((template, index) => (
|
|
91
91
|
<PrefetchPageLink
|
|
@@ -117,7 +117,7 @@ export const QuickstartPage = () => {
|
|
|
117
117
|
</div>
|
|
118
118
|
|
|
119
119
|
<div className="mt-12">
|
|
120
|
-
<h2 className="text-xl font-semibold mb-4">Import as
|
|
120
|
+
<h2 className="text-xl font-semibold mb-4">Import as Package</h2>
|
|
121
121
|
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
122
122
|
{[
|
|
123
123
|
{ name: "KiCad Footprint", type: "footprint" },
|
package/src/pages/search.tsx
CHANGED
|
@@ -97,7 +97,7 @@ export const SearchPage = () => {
|
|
|
97
97
|
<div className="mb-6">
|
|
98
98
|
<div className="flex items-center gap-2 mb-3">
|
|
99
99
|
<h1 className="text-3xl font-bold text-gray-900">
|
|
100
|
-
Search
|
|
100
|
+
Search Packages
|
|
101
101
|
</h1>
|
|
102
102
|
</div>
|
|
103
103
|
<div className="flex flex-col sm:flex-row gap-4 mb-4">
|
|
@@ -212,6 +212,7 @@ export const UserProfilePage = () => {
|
|
|
212
212
|
<DeleteDialog
|
|
213
213
|
packageId={packageToDelete.package_id}
|
|
214
214
|
packageName={packageToDelete.unscoped_name}
|
|
215
|
+
packageOwner={packageToDelete.owner_github_username ?? ""}
|
|
215
216
|
refetchUserPackages={refetchUserPackages}
|
|
216
217
|
/>
|
|
217
218
|
)}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
|
3
|
-
import { PCBViewer } from "@tscircuit/pcb-viewer"
|
|
4
|
-
import { CadViewer } from "@tscircuit/3d-viewer"
|
|
5
|
-
import { CircuitJsonTableViewer } from "./TableViewer/CircuitJsonTableViewer"
|
|
6
|
-
import { AnyCircuitElement } from "circuit-json"
|
|
7
|
-
|
|
8
|
-
interface OrderPreviewContentProps {
|
|
9
|
-
circuitJson: AnyCircuitElement[] | null
|
|
10
|
-
className?: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export const OrderPreviewContent: React.FC<OrderPreviewContentProps> = ({
|
|
14
|
-
circuitJson,
|
|
15
|
-
className,
|
|
16
|
-
}) => {
|
|
17
|
-
return (
|
|
18
|
-
<div className={className}>
|
|
19
|
-
<Tabs defaultValue="pcb" className="w-full">
|
|
20
|
-
<TabsList>
|
|
21
|
-
<TabsTrigger value="pcb">PCB</TabsTrigger>
|
|
22
|
-
<TabsTrigger value="cad">3D</TabsTrigger>
|
|
23
|
-
<TabsTrigger value="json-table">JSON</TabsTrigger>
|
|
24
|
-
</TabsList>
|
|
25
|
-
<TabsContent value="pcb">
|
|
26
|
-
<div className="h-[500px] shadow overflow-hidden sm:rounded-lg mb-6">
|
|
27
|
-
{circuitJson ? (
|
|
28
|
-
<PCBViewer height={500} circuitJson={circuitJson} />
|
|
29
|
-
) : (
|
|
30
|
-
<div className="flex items-center justify-center h-full bg-gray-100">
|
|
31
|
-
No PCB data available
|
|
32
|
-
</div>
|
|
33
|
-
)}
|
|
34
|
-
</div>
|
|
35
|
-
</TabsContent>
|
|
36
|
-
<TabsContent value="cad">
|
|
37
|
-
<div className="h-[500px] shadow overflow-hidden sm:rounded-lg mb-6">
|
|
38
|
-
{circuitJson ? (
|
|
39
|
-
<CadViewer soup={circuitJson as any} />
|
|
40
|
-
) : (
|
|
41
|
-
<div className="flex items-center justify-center h-full bg-gray-100">
|
|
42
|
-
No 3D data available
|
|
43
|
-
</div>
|
|
44
|
-
)}
|
|
45
|
-
</div>
|
|
46
|
-
</TabsContent>
|
|
47
|
-
<TabsContent value="json-table">
|
|
48
|
-
<div className="h-[500px] shadow overflow-hidden sm:rounded-lg mb-6">
|
|
49
|
-
{circuitJson ? (
|
|
50
|
-
<CircuitJsonTableViewer elements={circuitJson} />
|
|
51
|
-
) : (
|
|
52
|
-
<div className="flex items-center justify-center h-full bg-gray-100">
|
|
53
|
-
No JSON data available
|
|
54
|
-
</div>
|
|
55
|
-
)}
|
|
56
|
-
</div>
|
|
57
|
-
</TabsContent>
|
|
58
|
-
</Tabs>
|
|
59
|
-
</div>
|
|
60
|
-
)
|
|
61
|
-
}
|
|
@@ -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
|
-
}
|