@tscircuit/fake-snippets 0.0.71 → 0.0.72
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/package_files/create_or_update.test.ts +26 -0
- package/bun.lock +30 -46
- package/dist/bundle.js +4 -4
- package/fake-snippets-api/routes/api/package_files/create_or_update.ts +3 -3
- package/package.json +7 -7
- package/src/components/FileSidebar.tsx +111 -37
- package/src/components/package-port/CodeAndPreview.tsx +78 -267
- package/src/components/package-port/CodeEditor.tsx +29 -18
- package/src/components/package-port/CodeEditorHeader.tsx +7 -6
- package/src/components/ui/tree-view.tsx +3 -3
- package/src/hooks/useFileManagement.ts +257 -38
- package/src/hooks/usePackageFilesLoader.ts +2 -2
- package/src/hooks/useUpdatePackageFilesMutation.ts +50 -24
|
@@ -28,7 +28,7 @@ const routeSpec = {
|
|
|
28
28
|
}, "Cannot specify both package_release_id and package_name_with_version")
|
|
29
29
|
.refine((v) => {
|
|
30
30
|
if (v.content_base64 && v.content_text) return false
|
|
31
|
-
if (!v.content_base64 &&
|
|
31
|
+
if (!v.content_base64 && v.content_text === undefined) return false
|
|
32
32
|
return true
|
|
33
33
|
}, "Either content_base64 or content_text is required"),
|
|
34
34
|
jsonResponse: z.object({
|
|
@@ -136,7 +136,7 @@ export default withRouteSpec(routeSpec)(async (req, ctx) => {
|
|
|
136
136
|
exisitingFile.package_file_id,
|
|
137
137
|
{
|
|
138
138
|
content_text:
|
|
139
|
-
content_text
|
|
139
|
+
content_text ??
|
|
140
140
|
(content_base64
|
|
141
141
|
? Buffer.from(content_base64, "base64").toString()
|
|
142
142
|
: null),
|
|
@@ -167,7 +167,7 @@ export default withRouteSpec(routeSpec)(async (req, ctx) => {
|
|
|
167
167
|
package_release_id: packageReleaseId,
|
|
168
168
|
file_path,
|
|
169
169
|
content_text:
|
|
170
|
-
content_text
|
|
170
|
+
content_text ??
|
|
171
171
|
(content_base64
|
|
172
172
|
? Buffer.from(content_base64, "base64").toString()
|
|
173
173
|
: null),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/fake-snippets",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.72",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -71,11 +71,11 @@
|
|
|
71
71
|
"@radix-ui/react-toggle-group": "^1.1.0",
|
|
72
72
|
"@radix-ui/react-tooltip": "^1.1.2",
|
|
73
73
|
"@tscircuit/eval": "^0.0.198",
|
|
74
|
-
"@tscircuit/footprinter": "^0.0.
|
|
74
|
+
"@tscircuit/footprinter": "^0.0.169",
|
|
75
75
|
"@tscircuit/layout": "^0.0.29",
|
|
76
76
|
"@tscircuit/math-utils": "^0.0.10",
|
|
77
77
|
"@tscircuit/mm": "^0.0.8",
|
|
78
|
-
"@tscircuit/props": "^0.0.
|
|
78
|
+
"@tscircuit/props": "^0.0.194",
|
|
79
79
|
"@types/file-saver": "^2.0.7",
|
|
80
80
|
"@types/ms": "^0.7.34",
|
|
81
81
|
"@typescript/ata": "^0.9.7",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@valtown/codemirror-ts": "^2.2.0",
|
|
84
84
|
"@vercel/analytics": "^1.4.1",
|
|
85
85
|
"change-case": "^5.4.4",
|
|
86
|
-
"circuit-json": "^0.0.
|
|
86
|
+
"circuit-json": "^0.0.190",
|
|
87
87
|
"circuit-json-to-bom-csv": "^0.0.6",
|
|
88
88
|
"circuit-json-to-gerber": "^0.0.21",
|
|
89
89
|
"circuit-json-to-pnp-csv": "^0.0.6",
|
|
@@ -145,9 +145,9 @@
|
|
|
145
145
|
"@biomejs/biome": "^1.9.2",
|
|
146
146
|
"@playwright/test": "^1.48.0",
|
|
147
147
|
"@tailwindcss/typography": "^0.5.16",
|
|
148
|
-
"@tscircuit/core": "^0.0.
|
|
148
|
+
"@tscircuit/core": "^0.0.428",
|
|
149
149
|
"@tscircuit/prompt-benchmarks": "^0.0.28",
|
|
150
|
-
"@tscircuit/runframe": "^0.0.
|
|
150
|
+
"@tscircuit/runframe": "^0.0.507",
|
|
151
151
|
"@types/babel__standalone": "^7.1.7",
|
|
152
152
|
"@types/bun": "^1.1.10",
|
|
153
153
|
"@types/country-list": "^2.1.4",
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
"@typescript/vfs": "^1.6.0",
|
|
162
162
|
"@vitejs/plugin-react": "^4.3.1",
|
|
163
163
|
"autoprefixer": "^10.4.20",
|
|
164
|
-
"circuit-to-svg": "^0.0.
|
|
164
|
+
"circuit-to-svg": "^0.0.131",
|
|
165
165
|
"get-port": "^7.1.0",
|
|
166
166
|
"globals": "^15.9.0",
|
|
167
167
|
"he": "^1.2.0",
|
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
import React, { useState } from "react"
|
|
2
2
|
import { cn } from "@/lib/utils"
|
|
3
|
-
import { File, Folder, PanelRightOpen, Plus } from "lucide-react"
|
|
3
|
+
import { File, Folder, MoreVertical, PanelRightOpen, Plus } from "lucide-react"
|
|
4
4
|
import { TreeView, TreeDataItem } from "@/components/ui/tree-view"
|
|
5
5
|
import { isHiddenFile } from "./ViewPackagePage/utils/is-hidden-file"
|
|
6
6
|
import { Input } from "@/components/ui/input"
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
DropdownMenu,
|
|
9
|
+
DropdownMenuContent,
|
|
10
|
+
DropdownMenuGroup,
|
|
11
|
+
DropdownMenuItem,
|
|
12
|
+
DropdownMenuTrigger,
|
|
13
|
+
} from "./ui/dropdown-menu"
|
|
14
|
+
import type {
|
|
15
|
+
ICreateFileProps,
|
|
16
|
+
ICreateFileResult,
|
|
17
|
+
IDeleteFileProps,
|
|
18
|
+
IDeleteFileResult,
|
|
19
|
+
} from "@/hooks/useFileManagement"
|
|
20
|
+
import { useToast } from "@/hooks/use-toast"
|
|
9
21
|
type FileName = string
|
|
10
22
|
|
|
11
23
|
interface FileSidebarProps {
|
|
12
24
|
files: Record<FileName, string>
|
|
13
|
-
currentFile: FileName
|
|
25
|
+
currentFile: FileName | null
|
|
14
26
|
onFileSelect: (filename: FileName) => void
|
|
15
27
|
className?: string
|
|
16
28
|
fileSidebarState: ReturnType<typeof useState<boolean>>
|
|
17
|
-
handleCreateFile: (props:
|
|
29
|
+
handleCreateFile: (props: ICreateFileProps) => ICreateFileResult
|
|
30
|
+
handleDeleteFile: (props: IDeleteFileProps) => IDeleteFileResult
|
|
18
31
|
}
|
|
19
32
|
|
|
20
33
|
const FileSidebar: React.FC<FileSidebarProps> = ({
|
|
@@ -24,11 +37,13 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
|
|
|
24
37
|
className,
|
|
25
38
|
fileSidebarState,
|
|
26
39
|
handleCreateFile,
|
|
40
|
+
handleDeleteFile,
|
|
27
41
|
}) => {
|
|
28
42
|
const [sidebarOpen, setSidebarOpen] = fileSidebarState
|
|
29
43
|
const [newFileName, setNewFileName] = useState("")
|
|
30
44
|
const [isCreatingFile, setIsCreatingFile] = useState(false)
|
|
31
45
|
const [errorMessage, setErrorMessage] = useState("")
|
|
46
|
+
const { toast } = useToast()
|
|
32
47
|
|
|
33
48
|
const transformFilesToTreeData = (
|
|
34
49
|
files: Record<FileName, string>,
|
|
@@ -38,38 +53,85 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
|
|
|
38
53
|
}
|
|
39
54
|
const root: Record<string, TreeNode> = {}
|
|
40
55
|
|
|
41
|
-
Object.keys(files).forEach((
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
56
|
+
Object.keys(files).forEach((filePath) => {
|
|
57
|
+
const hasLeadingSlash = filePath.startsWith("/")
|
|
58
|
+
const pathSegments = (hasLeadingSlash ? filePath.slice(1) : filePath)
|
|
59
|
+
.trim()
|
|
60
|
+
.split("/")
|
|
61
|
+
let currentNode: Record<string, TreeNode> = root
|
|
45
62
|
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
pathSegments.forEach((segment, segmentIndex) => {
|
|
64
|
+
const isLeafNode = segmentIndex === pathSegments.length - 1
|
|
65
|
+
const ancestorPath = pathSegments.slice(0, segmentIndex).join("/")
|
|
66
|
+
const relativePath = ancestorPath
|
|
67
|
+
? `${ancestorPath}/${segment}`
|
|
68
|
+
: segment
|
|
69
|
+
const absolutePath = hasLeadingSlash ? `/${relativePath}` : relativePath
|
|
70
|
+
const itemId = absolutePath
|
|
53
71
|
if (
|
|
54
|
-
!
|
|
55
|
-
(!isHiddenFile(
|
|
72
|
+
!currentNode[segment] &&
|
|
73
|
+
(!isHiddenFile(relativePath) ||
|
|
56
74
|
isHiddenFile(
|
|
57
|
-
currentFile
|
|
75
|
+
currentFile?.startsWith("/")
|
|
76
|
+
? currentFile.slice(1)
|
|
77
|
+
: currentFile || "",
|
|
58
78
|
))
|
|
59
79
|
) {
|
|
60
|
-
|
|
61
|
-
id:
|
|
62
|
-
name:
|
|
63
|
-
icon:
|
|
64
|
-
onClick:
|
|
65
|
-
draggable:
|
|
66
|
-
droppable: !
|
|
67
|
-
children:
|
|
80
|
+
currentNode[segment] = {
|
|
81
|
+
id: itemId,
|
|
82
|
+
name: isLeafNode ? segment : segment,
|
|
83
|
+
icon: isLeafNode ? File : Folder,
|
|
84
|
+
onClick: isLeafNode ? () => onFileSelect(absolutePath) : undefined,
|
|
85
|
+
draggable: false,
|
|
86
|
+
droppable: !isLeafNode,
|
|
87
|
+
children: isLeafNode ? undefined : {},
|
|
88
|
+
actions: (
|
|
89
|
+
<>
|
|
90
|
+
<DropdownMenu key={itemId}>
|
|
91
|
+
<DropdownMenuTrigger asChild>
|
|
92
|
+
<MoreVertical className="w-4 h-4 text-gray-500 hover:text-gray-700" />
|
|
93
|
+
</DropdownMenuTrigger>
|
|
94
|
+
<DropdownMenuContent
|
|
95
|
+
className="w-48 bg-white shadow-lg rounded-md border-4 z-[100] border-white"
|
|
96
|
+
style={{
|
|
97
|
+
position: "absolute",
|
|
98
|
+
top: "100%",
|
|
99
|
+
left: "0",
|
|
100
|
+
marginTop: "0.5rem",
|
|
101
|
+
width: "8rem",
|
|
102
|
+
padding: "0.01rem",
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<DropdownMenuGroup>
|
|
106
|
+
<DropdownMenuItem
|
|
107
|
+
onClick={() => {
|
|
108
|
+
const { fileDeleted } = handleDeleteFile({
|
|
109
|
+
filename: relativePath,
|
|
110
|
+
onError: (error) => {
|
|
111
|
+
toast({
|
|
112
|
+
title: `Error deleting file ${relativePath}`,
|
|
113
|
+
description: error.message,
|
|
114
|
+
})
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
if (fileDeleted) {
|
|
118
|
+
setErrorMessage("")
|
|
119
|
+
}
|
|
120
|
+
}}
|
|
121
|
+
className="flex items-center px-4 py-1 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer"
|
|
122
|
+
>
|
|
123
|
+
Delete
|
|
124
|
+
</DropdownMenuItem>
|
|
125
|
+
</DropdownMenuGroup>
|
|
126
|
+
</DropdownMenuContent>
|
|
127
|
+
</DropdownMenu>
|
|
128
|
+
</>
|
|
129
|
+
),
|
|
68
130
|
}
|
|
69
131
|
}
|
|
70
132
|
|
|
71
|
-
if (!
|
|
72
|
-
|
|
133
|
+
if (!isLeafNode && currentNode[segment].children) {
|
|
134
|
+
currentNode = currentNode[segment].children
|
|
73
135
|
}
|
|
74
136
|
})
|
|
75
137
|
})
|
|
@@ -90,15 +152,26 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
|
|
|
90
152
|
}
|
|
91
153
|
|
|
92
154
|
const treeData = transformFilesToTreeData(files)
|
|
93
|
-
|
|
155
|
+
// console.log("treeData", files)
|
|
94
156
|
const handleCreateFileInline = () => {
|
|
95
|
-
handleCreateFile({
|
|
157
|
+
const { newFileCreated } = handleCreateFile({
|
|
96
158
|
newFileName,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
setIsCreatingFile,
|
|
159
|
+
onError: (error) => {
|
|
160
|
+
setErrorMessage(error.message)
|
|
161
|
+
},
|
|
101
162
|
})
|
|
163
|
+
if (newFileCreated) {
|
|
164
|
+
setIsCreatingFile(false)
|
|
165
|
+
setNewFileName("")
|
|
166
|
+
setErrorMessage("")
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const toggleSidebar = () => {
|
|
171
|
+
setSidebarOpen(!sidebarOpen)
|
|
172
|
+
setErrorMessage("")
|
|
173
|
+
setIsCreatingFile(false)
|
|
174
|
+
setNewFileName("")
|
|
102
175
|
}
|
|
103
176
|
|
|
104
177
|
return (
|
|
@@ -110,7 +183,7 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
|
|
|
110
183
|
)}
|
|
111
184
|
>
|
|
112
185
|
<button
|
|
113
|
-
onClick={
|
|
186
|
+
onClick={toggleSidebar}
|
|
114
187
|
className={`z-[99] mt-2 ml-2 text-gray-400 scale-90 transition-opacity duration-200 ${
|
|
115
188
|
!sidebarOpen ? "opacity-0 pointer-events-none" : "opacity-100"
|
|
116
189
|
}`}
|
|
@@ -129,6 +202,7 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
|
|
|
129
202
|
<Input
|
|
130
203
|
autoFocus
|
|
131
204
|
value={newFileName}
|
|
205
|
+
spellCheck={false}
|
|
132
206
|
onChange={(e) => setNewFileName(e.target.value)}
|
|
133
207
|
onBlur={handleCreateFileInline}
|
|
134
208
|
onKeyDown={(e) => {
|
|
@@ -149,7 +223,7 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
|
|
|
149
223
|
)}
|
|
150
224
|
<TreeView
|
|
151
225
|
data={treeData}
|
|
152
|
-
initialSelectedItemId={currentFile}
|
|
226
|
+
initialSelectedItemId={currentFile || ""}
|
|
153
227
|
onSelectChange={(item) => {
|
|
154
228
|
if (item?.onClick) {
|
|
155
229
|
item.onClick()
|