@tscircuit/fake-snippets 0.0.43 → 0.0.45
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.lock +378 -26
- package/dist/bundle.js +411 -335
- package/fake-snippets-api/routes/api/_fake/received_quotes.ts +66 -0
- package/fake-snippets-api/routes/api/package_files/create_or_update.ts +9 -0
- package/fake-snippets-api/routes/api/package_releases/update.ts +25 -18
- package/fake-snippets-api/routes/api/packages/update.ts +1 -0
- package/package.json +6 -6
- package/src/App.tsx +8 -0
- package/src/components/CodeAndPreview.tsx +0 -1
- package/src/components/CodeEditor.tsx +5 -4
- package/src/components/CodeEditorHeader.tsx +1 -26
- package/src/components/DownloadButtonAndMenu.tsx +3 -2
- package/src/components/EditorNav.tsx +13 -11
- package/src/components/ErrorOutline.tsx +35 -0
- package/src/components/FileSidebar.tsx +114 -0
- package/src/components/NotFound.tsx +37 -0
- package/src/components/PreviewContent.tsx +0 -6
- package/src/components/TrendingSnippetCarousel.tsx +1 -1
- package/src/components/ViewPackagePage/components/package-header.tsx +24 -3
- package/src/components/ViewPackagePage/utils/is-hidden-file.ts +0 -1
- package/src/components/dialogs/package-visibility-settings-dialog.tsx +10 -1
- package/src/components/dialogs/rename-package-dialog.tsx +81 -0
- package/src/components/dialogs/update-package-description-dialog.tsx +96 -0
- package/src/components/dialogs/view-ts-files-dialog.tsx +0 -6
- package/src/components/package-port/CodeAndPreview.tsx +419 -0
- package/src/components/package-port/CodeEditor.tsx +498 -0
- package/src/components/package-port/CodeEditorHeader.tsx +231 -0
- package/src/components/package-port/EditorNav.tsx +520 -0
- package/src/components/ui/tree-view.tsx +494 -0
- package/src/hooks/use-package.ts +23 -0
- package/src/hooks/useForkPackageMutation.ts +49 -0
- package/src/hooks/usePackageFilesLoader.ts +56 -0
- package/src/hooks/useUpdatePackageFilesMutation.ts +86 -0
- package/src/hooks/useUpdatePackageMutation.ts +63 -0
- package/src/{prettier.ts → lib/types.ts} +3 -1
- package/src/lib/utils/checkIfManualEditsImported.ts +1 -1
- package/src/lib/utils/findTargetFile.ts +62 -0
- package/src/lib/utils/load-prettier.ts +3 -0
- package/src/pages/404.tsx +2 -33
- package/src/pages/package-editor.tsx +55 -0
- package/src/pages/user-profile.tsx +66 -27
- package/src/components/FootprintDialog.tsx +0 -339
- package/src/components/ParametersEditor.tsx +0 -140
- package/src/lib/utils/parseFootprintParams.ts +0 -52
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import React, { useState, useCallback } from "react"
|
|
2
|
+
import { Button } from "@/components/ui/button"
|
|
3
|
+
import { handleManualEditsImport } from "@/lib/handleManualEditsImport"
|
|
4
|
+
import { useImportSnippetDialog } from "@/components/dialogs/import-snippet-dialog"
|
|
5
|
+
import { useToast } from "@/hooks/use-toast"
|
|
6
|
+
import {
|
|
7
|
+
DropdownMenu,
|
|
8
|
+
DropdownMenuContent,
|
|
9
|
+
DropdownMenuItem,
|
|
10
|
+
DropdownMenuTrigger,
|
|
11
|
+
} from "@/components/ui/dropdown-menu"
|
|
12
|
+
import { AlertTriangle, PanelRightClose } from "lucide-react"
|
|
13
|
+
import { checkIfManualEditsImported } from "@/lib/utils/checkIfManualEditsImported"
|
|
14
|
+
import {
|
|
15
|
+
Select,
|
|
16
|
+
SelectContent,
|
|
17
|
+
SelectItem,
|
|
18
|
+
SelectTrigger,
|
|
19
|
+
SelectValue,
|
|
20
|
+
} from "../ui/select"
|
|
21
|
+
import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
|
|
22
|
+
export type FileName = string
|
|
23
|
+
|
|
24
|
+
interface CodeEditorHeaderProps {
|
|
25
|
+
currentFile: FileName
|
|
26
|
+
files: Record<FileName, string>
|
|
27
|
+
updateFileContent: (filename: FileName, content: string) => void
|
|
28
|
+
fileSidebarState: ReturnType<typeof useState<boolean>>
|
|
29
|
+
handleFileChange: (filename: FileName) => void
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
|
|
33
|
+
currentFile,
|
|
34
|
+
files,
|
|
35
|
+
updateFileContent,
|
|
36
|
+
fileSidebarState,
|
|
37
|
+
handleFileChange,
|
|
38
|
+
}) => {
|
|
39
|
+
const { Dialog: ImportSnippetDialog, openDialog: openImportDialog } =
|
|
40
|
+
useImportSnippetDialog()
|
|
41
|
+
const { toast } = useToast()
|
|
42
|
+
const [sidebarOpen, setSidebarOpen] = fileSidebarState
|
|
43
|
+
|
|
44
|
+
const handleFormatFile = useCallback(() => {
|
|
45
|
+
if (!window.prettier || !window.prettierPlugins) return
|
|
46
|
+
try {
|
|
47
|
+
const currentContent = files[currentFile]
|
|
48
|
+
let fileExtension = currentFile.split(".").pop()?.toLowerCase()
|
|
49
|
+
if (currentContent.trim().length === 0) {
|
|
50
|
+
toast({
|
|
51
|
+
title: "Empty file",
|
|
52
|
+
description: "Cannot format an empty file.",
|
|
53
|
+
})
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
if (!fileExtension) {
|
|
57
|
+
toast({
|
|
58
|
+
title: "Cannot determine file type",
|
|
59
|
+
description: "Unable to format file without an extension.",
|
|
60
|
+
})
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (["readme"].includes(currentFile.toLowerCase())) {
|
|
65
|
+
fileExtension = "md"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (fileExtension === currentFile.toLowerCase()) {
|
|
69
|
+
toast({
|
|
70
|
+
title: "Cannot determine file type",
|
|
71
|
+
description: "Unable to format file without an extension.",
|
|
72
|
+
})
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Handle JSON formatting separately
|
|
77
|
+
if (fileExtension === "json") {
|
|
78
|
+
try {
|
|
79
|
+
const jsonObj = JSON.parse(currentContent)
|
|
80
|
+
const formattedJson = JSON.stringify(jsonObj, null, 2)
|
|
81
|
+
updateFileContent(currentFile, formattedJson)
|
|
82
|
+
} catch (jsonError) {
|
|
83
|
+
toast({
|
|
84
|
+
title: "Invalid JSON",
|
|
85
|
+
description: "Failed to format JSON: invalid syntax.",
|
|
86
|
+
variant: "destructive",
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const parserMap: Record<string, string> = {
|
|
93
|
+
js: "babel",
|
|
94
|
+
jsx: "babel",
|
|
95
|
+
ts: "typescript",
|
|
96
|
+
tsx: "typescript",
|
|
97
|
+
md: "markdown",
|
|
98
|
+
markdown: "markdown",
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const parser = parserMap[fileExtension] || "typescript"
|
|
102
|
+
const formattedCode = window.prettier.format(currentContent, {
|
|
103
|
+
semi: false,
|
|
104
|
+
parser: parser,
|
|
105
|
+
plugins: window.prettierPlugins,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
updateFileContent(currentFile, formattedCode)
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error("Formatting error:", error)
|
|
111
|
+
if (
|
|
112
|
+
error instanceof Error &&
|
|
113
|
+
error.message.includes("No parser could be inferred")
|
|
114
|
+
) {
|
|
115
|
+
toast({
|
|
116
|
+
title: "Unsupported File Type",
|
|
117
|
+
description: `Formatting not supported for .${currentFile.split(".").pop()?.toLowerCase()} files. Tried default parser.`,
|
|
118
|
+
})
|
|
119
|
+
} else {
|
|
120
|
+
toast({
|
|
121
|
+
title: "Formatting error",
|
|
122
|
+
description:
|
|
123
|
+
error instanceof Error
|
|
124
|
+
? error.message
|
|
125
|
+
: "Failed to format the code. Please check for syntax errors.",
|
|
126
|
+
variant: "destructive",
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}, [currentFile, files, toast, updateFileContent])
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<>
|
|
134
|
+
<div className="flex items-center gap-2 px-2 border-b border-gray-200">
|
|
135
|
+
<button
|
|
136
|
+
className={`text-gray-400 scale-90 transition-opacity duration-200 ${
|
|
137
|
+
sidebarOpen ? "opacity-0 pointer-events-none" : "opacity-100"
|
|
138
|
+
}`}
|
|
139
|
+
onClick={() => setSidebarOpen(true)}
|
|
140
|
+
>
|
|
141
|
+
<PanelRightClose />
|
|
142
|
+
</button>
|
|
143
|
+
<div>
|
|
144
|
+
<Select value={currentFile} onValueChange={handleFileChange}>
|
|
145
|
+
<SelectTrigger className="h-7 px-3 bg-white">
|
|
146
|
+
<SelectValue placeholder="Select file" />
|
|
147
|
+
</SelectTrigger>
|
|
148
|
+
<SelectContent>
|
|
149
|
+
{Object.keys(files)
|
|
150
|
+
.filter(
|
|
151
|
+
(filename) =>
|
|
152
|
+
!isHiddenFile(
|
|
153
|
+
filename.startsWith("/") ? filename.slice(1) : filename,
|
|
154
|
+
),
|
|
155
|
+
)
|
|
156
|
+
.map((filename) => (
|
|
157
|
+
<SelectItem className="py-1" key={filename} value={filename}>
|
|
158
|
+
<span
|
|
159
|
+
className={`text-xs pr-1 block truncate ${
|
|
160
|
+
sidebarOpen ? "max-w-[5rem]" : "max-w-[10rem]"
|
|
161
|
+
}`}
|
|
162
|
+
>
|
|
163
|
+
{filename}
|
|
164
|
+
</span>
|
|
165
|
+
</SelectItem>
|
|
166
|
+
))}
|
|
167
|
+
{currentFile &&
|
|
168
|
+
Object.keys(files).includes(currentFile) &&
|
|
169
|
+
isHiddenFile(
|
|
170
|
+
currentFile.startsWith("/")
|
|
171
|
+
? currentFile.slice(1)
|
|
172
|
+
: currentFile,
|
|
173
|
+
) && (
|
|
174
|
+
<SelectItem className="py-1" value={currentFile}>
|
|
175
|
+
<span
|
|
176
|
+
className={`text-xs pr-1 block truncate ${
|
|
177
|
+
sidebarOpen ? "max-w-[5rem]" : "max-w-[10rem]"
|
|
178
|
+
}`}
|
|
179
|
+
>
|
|
180
|
+
{currentFile}
|
|
181
|
+
</span>
|
|
182
|
+
</SelectItem>
|
|
183
|
+
)}
|
|
184
|
+
</SelectContent>
|
|
185
|
+
</Select>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div className="flex items-center overflow-x-hidden gap-2 px-2 py-1 ml-auto">
|
|
189
|
+
{checkIfManualEditsImported(files) && (
|
|
190
|
+
<DropdownMenu>
|
|
191
|
+
<DropdownMenuTrigger asChild>
|
|
192
|
+
<Button
|
|
193
|
+
size="sm"
|
|
194
|
+
variant="ghost"
|
|
195
|
+
className="text-red-500 hover:bg-red-50"
|
|
196
|
+
>
|
|
197
|
+
<AlertTriangle className="mr-2 h-4 w-4" />
|
|
198
|
+
Error
|
|
199
|
+
</Button>
|
|
200
|
+
</DropdownMenuTrigger>
|
|
201
|
+
<DropdownMenuContent>
|
|
202
|
+
<DropdownMenuItem
|
|
203
|
+
className="text-red-600 cursor-pointer"
|
|
204
|
+
onClick={() =>
|
|
205
|
+
handleManualEditsImport(files, updateFileContent, toast)
|
|
206
|
+
}
|
|
207
|
+
>
|
|
208
|
+
Manual edits exist but have not been imported. (Click to fix)
|
|
209
|
+
</DropdownMenuItem>
|
|
210
|
+
</DropdownMenuContent>
|
|
211
|
+
</DropdownMenu>
|
|
212
|
+
)}
|
|
213
|
+
<Button size="sm" variant="ghost" onClick={() => openImportDialog()}>
|
|
214
|
+
Import
|
|
215
|
+
</Button>
|
|
216
|
+
<Button size="sm" variant="ghost" onClick={handleFormatFile}>
|
|
217
|
+
Format
|
|
218
|
+
</Button>
|
|
219
|
+
</div>
|
|
220
|
+
<ImportSnippetDialog
|
|
221
|
+
onSnippetSelected={(snippet: any) => {
|
|
222
|
+
const newContent = `import {} from "@tsci/${snippet.owner_name}.${snippet.unscoped_name}"\n${files[currentFile]}`
|
|
223
|
+
updateFileContent(currentFile, newContent)
|
|
224
|
+
}}
|
|
225
|
+
/>
|
|
226
|
+
</div>
|
|
227
|
+
</>
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export default CodeEditorHeader
|