@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.
Files changed (44) hide show
  1. package/bun.lock +378 -26
  2. package/dist/bundle.js +411 -335
  3. package/fake-snippets-api/routes/api/_fake/received_quotes.ts +66 -0
  4. package/fake-snippets-api/routes/api/package_files/create_or_update.ts +9 -0
  5. package/fake-snippets-api/routes/api/package_releases/update.ts +25 -18
  6. package/fake-snippets-api/routes/api/packages/update.ts +1 -0
  7. package/package.json +6 -6
  8. package/src/App.tsx +8 -0
  9. package/src/components/CodeAndPreview.tsx +0 -1
  10. package/src/components/CodeEditor.tsx +5 -4
  11. package/src/components/CodeEditorHeader.tsx +1 -26
  12. package/src/components/DownloadButtonAndMenu.tsx +3 -2
  13. package/src/components/EditorNav.tsx +13 -11
  14. package/src/components/ErrorOutline.tsx +35 -0
  15. package/src/components/FileSidebar.tsx +114 -0
  16. package/src/components/NotFound.tsx +37 -0
  17. package/src/components/PreviewContent.tsx +0 -6
  18. package/src/components/TrendingSnippetCarousel.tsx +1 -1
  19. package/src/components/ViewPackagePage/components/package-header.tsx +24 -3
  20. package/src/components/ViewPackagePage/utils/is-hidden-file.ts +0 -1
  21. package/src/components/dialogs/package-visibility-settings-dialog.tsx +10 -1
  22. package/src/components/dialogs/rename-package-dialog.tsx +81 -0
  23. package/src/components/dialogs/update-package-description-dialog.tsx +96 -0
  24. package/src/components/dialogs/view-ts-files-dialog.tsx +0 -6
  25. package/src/components/package-port/CodeAndPreview.tsx +419 -0
  26. package/src/components/package-port/CodeEditor.tsx +498 -0
  27. package/src/components/package-port/CodeEditorHeader.tsx +231 -0
  28. package/src/components/package-port/EditorNav.tsx +520 -0
  29. package/src/components/ui/tree-view.tsx +494 -0
  30. package/src/hooks/use-package.ts +23 -0
  31. package/src/hooks/useForkPackageMutation.ts +49 -0
  32. package/src/hooks/usePackageFilesLoader.ts +56 -0
  33. package/src/hooks/useUpdatePackageFilesMutation.ts +86 -0
  34. package/src/hooks/useUpdatePackageMutation.ts +63 -0
  35. package/src/{prettier.ts → lib/types.ts} +3 -1
  36. package/src/lib/utils/checkIfManualEditsImported.ts +1 -1
  37. package/src/lib/utils/findTargetFile.ts +62 -0
  38. package/src/lib/utils/load-prettier.ts +3 -0
  39. package/src/pages/404.tsx +2 -33
  40. package/src/pages/package-editor.tsx +55 -0
  41. package/src/pages/user-profile.tsx +66 -27
  42. package/src/components/FootprintDialog.tsx +0 -339
  43. package/src/components/ParametersEditor.tsx +0 -140
  44. 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