@tscircuit/fake-snippets 0.0.67 → 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.lock +19 -66
- package/dist/bundle.js +1 -1
- package/package.json +2 -3
- 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/package-header.tsx +1 -1
- 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 +8 -27
- package/src/components/package-port/EditorNav.tsx +1 -11
- package/src/hooks/useFileManagement.ts +59 -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
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
Dialog,
|
|
17
17
|
DialogContent,
|
|
18
18
|
DialogDescription,
|
|
19
|
+
DialogFooter,
|
|
19
20
|
DialogHeader,
|
|
20
21
|
DialogTitle,
|
|
21
22
|
} from "../ui/dialog"
|
|
@@ -217,151 +218,157 @@ export const EditPackageDetailsDialog = ({
|
|
|
217
218
|
</DialogContent>
|
|
218
219
|
</Dialog>
|
|
219
220
|
<Dialog open={open !== showConfirmDelete} onOpenChange={onOpenChange}>
|
|
220
|
-
<DialogContent className="sm:max-w-[500px] lg:h-[70vh] sm:h-[90vh] overflow-y-auto w-[95vw] p-6 gap-6 rounded-2xl shadow-lg">
|
|
221
|
-
<
|
|
222
|
-
<
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
221
|
+
<DialogContent className="sm:max-w-[500px] lg:h-[70vh] sm:h-[90vh] overflow-y-auto w-[95vw] h-[80vh] p-6 gap-6 rounded-2xl shadow-lg">
|
|
222
|
+
<div className="flex flex-col gap-10">
|
|
223
|
+
<DialogHeader>
|
|
224
|
+
<DialogTitle>Edit Package Details</DialogTitle>
|
|
225
|
+
<DialogDescription>
|
|
226
|
+
Update your package's description, website, visibility, or
|
|
227
|
+
delete it.
|
|
228
|
+
</DialogDescription>
|
|
229
|
+
</DialogHeader>
|
|
230
|
+
<div className="">
|
|
231
|
+
<div className="grid gap-2">
|
|
232
|
+
<div className="space-y-1">
|
|
233
|
+
<Label htmlFor="website">Website</Label>
|
|
234
|
+
<Input
|
|
235
|
+
id="website"
|
|
236
|
+
value={formData.website}
|
|
237
|
+
onChange={(e) =>
|
|
238
|
+
setFormData((prev) => ({
|
|
239
|
+
...prev,
|
|
240
|
+
website: e.target.value,
|
|
241
|
+
}))
|
|
242
|
+
}
|
|
243
|
+
placeholder="https://example.com"
|
|
244
|
+
disabled={updatePackageDetailsMutation.isLoading}
|
|
245
|
+
className="w-full"
|
|
246
|
+
aria-invalid={!!websiteError}
|
|
247
|
+
/>
|
|
248
|
+
{websiteError && (
|
|
249
|
+
<p className="text-sm text-red-500">{websiteError}</p>
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
252
|
+
<div className="space-y-1">
|
|
253
|
+
<Label htmlFor="visibility">Visibility</Label>
|
|
254
|
+
<Select
|
|
255
|
+
value={formData.visibility}
|
|
256
|
+
onValueChange={(val) => {
|
|
257
|
+
setFormData((prev) => ({
|
|
258
|
+
...prev,
|
|
259
|
+
visibility: val,
|
|
260
|
+
}))
|
|
261
|
+
}}
|
|
262
|
+
disabled={updatePackageDetailsMutation.isLoading}
|
|
263
|
+
>
|
|
264
|
+
<SelectTrigger className="w-full">
|
|
265
|
+
<SelectValue placeholder="Select visibility" />
|
|
266
|
+
</SelectTrigger>
|
|
267
|
+
<SelectContent className="!z-[999]">
|
|
268
|
+
<SelectItem value="public">public</SelectItem>
|
|
269
|
+
<SelectItem value="private">private</SelectItem>
|
|
270
|
+
</SelectContent>
|
|
271
|
+
</Select>
|
|
272
|
+
</div>
|
|
273
|
+
<div className="space-y-1">
|
|
274
|
+
<Label htmlFor="description">Description</Label>
|
|
275
|
+
<Textarea
|
|
276
|
+
id="description"
|
|
277
|
+
value={formData.description}
|
|
278
|
+
onChange={(e) =>
|
|
279
|
+
setFormData((prev) => ({
|
|
280
|
+
...prev,
|
|
281
|
+
description: e.target.value,
|
|
282
|
+
}))
|
|
283
|
+
}
|
|
284
|
+
placeholder="Enter package description"
|
|
285
|
+
disabled={updatePackageDetailsMutation.isLoading}
|
|
286
|
+
className="w-full min-h-[80px] resize-none"
|
|
287
|
+
/>
|
|
288
|
+
</div>
|
|
289
|
+
<div className="space-y-1">
|
|
290
|
+
<Label htmlFor="license">License</Label>
|
|
291
|
+
<Select
|
|
292
|
+
value={formData.license || "unset"}
|
|
293
|
+
onValueChange={(value) =>
|
|
294
|
+
setFormData((prev) => ({
|
|
295
|
+
...prev,
|
|
296
|
+
license: value === "unset" ? null : value,
|
|
297
|
+
}))
|
|
298
|
+
}
|
|
299
|
+
disabled={updatePackageDetailsMutation.isLoading}
|
|
300
|
+
>
|
|
301
|
+
<SelectTrigger className="w-full">
|
|
302
|
+
<SelectValue placeholder="Select a license" />
|
|
303
|
+
</SelectTrigger>
|
|
304
|
+
<SelectContent className="!z-[999]">
|
|
305
|
+
<SelectItem value="MIT">MIT</SelectItem>
|
|
306
|
+
<SelectItem value="Apache-2.0">Apache-2.0</SelectItem>
|
|
307
|
+
<SelectItem value="BSD-3-Clause">BSD-3-Clause</SelectItem>
|
|
308
|
+
<SelectItem value="GPL-3.0">GPL-3.0</SelectItem>
|
|
309
|
+
<SelectItem value="unset">Unset</SelectItem>
|
|
310
|
+
</SelectContent>
|
|
311
|
+
</Select>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
228
314
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
<Input
|
|
233
|
-
id="website"
|
|
234
|
-
value={formData.website}
|
|
235
|
-
onChange={(e) =>
|
|
236
|
-
setFormData((prev) => ({
|
|
237
|
-
...prev,
|
|
238
|
-
website: e.target.value,
|
|
239
|
-
}))
|
|
240
|
-
}
|
|
241
|
-
placeholder="https://example.com"
|
|
242
|
-
disabled={updatePackageDetailsMutation.isLoading}
|
|
243
|
-
className="w-full"
|
|
244
|
-
aria-invalid={!!websiteError}
|
|
245
|
-
/>
|
|
246
|
-
{websiteError && (
|
|
247
|
-
<p className="text-sm text-red-500">{websiteError}</p>
|
|
248
|
-
)}
|
|
249
|
-
</div>
|
|
250
|
-
<div className="space-y-1">
|
|
251
|
-
<Label htmlFor="visibility">Visibility</Label>
|
|
252
|
-
<Select
|
|
253
|
-
value={formData.visibility}
|
|
254
|
-
onValueChange={(val) => {
|
|
255
|
-
setFormData((prev) => ({
|
|
256
|
-
...prev,
|
|
257
|
-
visibility: val,
|
|
258
|
-
}))
|
|
259
|
-
}}
|
|
260
|
-
disabled={updatePackageDetailsMutation.isLoading}
|
|
315
|
+
<details
|
|
316
|
+
className="mt-2 rounded-md"
|
|
317
|
+
onToggle={(e) => setDangerOpen(e.currentTarget.open)}
|
|
261
318
|
>
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
319
|
+
<summary className="cursor-pointer p-2 font-medium text-sm text-black list-none flex justify-between items-center">
|
|
320
|
+
Danger Zone
|
|
321
|
+
<ChevronDown
|
|
322
|
+
className={`w-4 h-4 mr-1 transition-transform ${dangerOpen ? "rotate-180" : ""}`}
|
|
323
|
+
/>
|
|
324
|
+
</summary>
|
|
325
|
+
<div className="p-2 pr-2">
|
|
326
|
+
<div className="flex justify-between items-center">
|
|
327
|
+
<div>
|
|
328
|
+
<p className="text-sm text-muted-foreground">
|
|
329
|
+
Once deleted, it cannot be recovered.
|
|
330
|
+
</p>
|
|
331
|
+
</div>
|
|
332
|
+
<Button
|
|
333
|
+
variant="destructive"
|
|
334
|
+
size="default"
|
|
335
|
+
onClick={() => setShowConfirmDelete(true)}
|
|
336
|
+
disabled={deleting}
|
|
337
|
+
className="shrink-0 lg:w-[115px] w-[70px]"
|
|
338
|
+
>
|
|
339
|
+
{deleting ? "Deleting..." : "Delete"}
|
|
340
|
+
</Button>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
</details>
|
|
270
344
|
</div>
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
...prev,
|
|
279
|
-
description: e.target.value,
|
|
280
|
-
}))
|
|
281
|
-
}
|
|
282
|
-
placeholder="Enter package description"
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<DialogFooter className="mt-auto">
|
|
348
|
+
<div className="lg:px-2 flex flex-col sm:flex-row justify-end gap-2">
|
|
349
|
+
<Button
|
|
350
|
+
variant="outline"
|
|
351
|
+
onClick={() => onOpenChange(false)}
|
|
283
352
|
disabled={updatePackageDetailsMutation.isLoading}
|
|
284
|
-
className="w-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
<
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
license: value === "unset" ? null : value,
|
|
295
|
-
}))
|
|
353
|
+
className="sm:w-auto w-full"
|
|
354
|
+
>
|
|
355
|
+
Cancel
|
|
356
|
+
</Button>
|
|
357
|
+
<Button
|
|
358
|
+
onClick={() => updatePackageDetailsMutation.mutate()}
|
|
359
|
+
disabled={
|
|
360
|
+
updatePackageDetailsMutation.isLoading ||
|
|
361
|
+
!hasChanges ||
|
|
362
|
+
!isFormValid
|
|
296
363
|
}
|
|
297
|
-
|
|
364
|
+
className="sm:w-auto lg:w-[115px]"
|
|
298
365
|
>
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
<SelectItem value="MIT">MIT</SelectItem>
|
|
304
|
-
<SelectItem value="Apache-2.0">Apache-2.0</SelectItem>
|
|
305
|
-
<SelectItem value="BSD-3-Clause">BSD-3-Clause</SelectItem>
|
|
306
|
-
<SelectItem value="GPL-3.0">GPL-3.0</SelectItem>
|
|
307
|
-
<SelectItem value="unset">Unset</SelectItem>
|
|
308
|
-
</SelectContent>
|
|
309
|
-
</Select>
|
|
310
|
-
</div>
|
|
311
|
-
</div>
|
|
312
|
-
<details
|
|
313
|
-
className="mt-2 rounded-md"
|
|
314
|
-
onToggle={(e) => setDangerOpen(e.currentTarget.open)}
|
|
315
|
-
>
|
|
316
|
-
<summary className="cursor-pointer p-2 font-medium text-sm text-black list-none flex justify-between items-center">
|
|
317
|
-
Danger Zone
|
|
318
|
-
<ChevronDown
|
|
319
|
-
className={`w-4 h-4 mr-1 transition-transform ${dangerOpen ? "rotate-180" : ""}`}
|
|
320
|
-
/>
|
|
321
|
-
</summary>
|
|
322
|
-
<div className="p-2 pr-2">
|
|
323
|
-
<div className="flex justify-between items-center">
|
|
324
|
-
<div>
|
|
325
|
-
<p className="text-sm text-muted-foreground">
|
|
326
|
-
Once deleted, it cannot be recovered.
|
|
327
|
-
</p>
|
|
328
|
-
</div>
|
|
329
|
-
<Button
|
|
330
|
-
variant="destructive"
|
|
331
|
-
size="default"
|
|
332
|
-
onClick={() => setShowConfirmDelete(true)}
|
|
333
|
-
disabled={deleting}
|
|
334
|
-
className="shrink-0 lg:w-[115px] w-[70px]"
|
|
335
|
-
>
|
|
336
|
-
{deleting ? "Deleting..." : "Delete"}
|
|
337
|
-
</Button>
|
|
338
|
-
</div>
|
|
366
|
+
{updatePackageDetailsMutation.isLoading
|
|
367
|
+
? "Updating..."
|
|
368
|
+
: "Save Changes"}
|
|
369
|
+
</Button>
|
|
339
370
|
</div>
|
|
340
|
-
</
|
|
341
|
-
|
|
342
|
-
<div className=" lg:px-2 flex flex-col sm:flex-row justify-end gap-3">
|
|
343
|
-
<Button
|
|
344
|
-
variant="outline"
|
|
345
|
-
onClick={() => onOpenChange(false)}
|
|
346
|
-
disabled={updatePackageDetailsMutation.isLoading}
|
|
347
|
-
className="sm:w-auto w-full"
|
|
348
|
-
>
|
|
349
|
-
Cancel
|
|
350
|
-
</Button>
|
|
351
|
-
<Button
|
|
352
|
-
onClick={() => updatePackageDetailsMutation.mutate()}
|
|
353
|
-
disabled={
|
|
354
|
-
updatePackageDetailsMutation.isLoading ||
|
|
355
|
-
!hasChanges ||
|
|
356
|
-
!isFormValid
|
|
357
|
-
}
|
|
358
|
-
className="sm:w-auto lg:w-[115px]"
|
|
359
|
-
>
|
|
360
|
-
{updatePackageDetailsMutation.isLoading
|
|
361
|
-
? "Updating..."
|
|
362
|
-
: "Save Changes"}
|
|
363
|
-
</Button>
|
|
364
|
-
</div>
|
|
371
|
+
</DialogFooter>
|
|
365
372
|
</DialogContent>
|
|
366
373
|
</Dialog>
|
|
367
374
|
</div>
|
|
@@ -22,6 +22,8 @@ import { usePackageFilesLoader } from "@/hooks/usePackageFilesLoader"
|
|
|
22
22
|
import { findTargetFile } from "@/lib/utils/findTargetFile"
|
|
23
23
|
import { toastManualEditConflicts } from "@/lib/utils/toastManualEditConflicts"
|
|
24
24
|
import { ManualEditEvent } from "@tscircuit/props"
|
|
25
|
+
import { isValidFileName } from "@/lib/utils/isValidFileName"
|
|
26
|
+
import { useFileManagement } from "@/hooks/useFileManagement"
|
|
25
27
|
|
|
26
28
|
interface Props {
|
|
27
29
|
pkg?: Package
|
|
@@ -32,12 +34,19 @@ export interface PackageFile {
|
|
|
32
34
|
content: string
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
interface
|
|
37
|
+
export interface CreateFileProps {
|
|
38
|
+
newFileName: string
|
|
39
|
+
setErrorMessage: (message: string) => void
|
|
40
|
+
onFileSelect: (fileName: string) => void
|
|
41
|
+
setNewFileName: (fileName: string) => void
|
|
42
|
+
setIsCreatingFile: (isCreatingFile: boolean) => void
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface CodeAndPreviewState {
|
|
36
46
|
pkgFilesWithContent: PackageFile[]
|
|
37
47
|
initialFilesLoad: PackageFile[]
|
|
38
48
|
showPreview: boolean
|
|
39
49
|
fullScreen: boolean
|
|
40
|
-
dts: string
|
|
41
50
|
lastSavedAt: number
|
|
42
51
|
circuitJson: null | any
|
|
43
52
|
isPrivate: boolean
|
|
@@ -92,7 +101,6 @@ export function CodeAndPreview({ pkg }: Props) {
|
|
|
92
101
|
initialFilesLoad: [],
|
|
93
102
|
showPreview: true,
|
|
94
103
|
fullScreen: false,
|
|
95
|
-
dts: "",
|
|
96
104
|
lastSavedAt: Date.now(),
|
|
97
105
|
circuitJson: null,
|
|
98
106
|
isPrivate: false,
|
|
@@ -148,7 +156,6 @@ export function CodeAndPreview({ pkg }: Props) {
|
|
|
148
156
|
|
|
149
157
|
if (loadedFiles && !isLoadingFiles) {
|
|
150
158
|
const processedResults = [...loadedFiles]
|
|
151
|
-
|
|
152
159
|
setState((prev) => ({
|
|
153
160
|
...prev,
|
|
154
161
|
pkgFilesWithContent: processedResults,
|
|
@@ -159,13 +166,7 @@ export function CodeAndPreview({ pkg }: Props) {
|
|
|
159
166
|
defaultCode,
|
|
160
167
|
}))
|
|
161
168
|
}
|
|
162
|
-
}, [
|
|
163
|
-
isLoadingFiles,
|
|
164
|
-
pkg,
|
|
165
|
-
pkgFiles.data,
|
|
166
|
-
state.pkgFilesWithContent.length,
|
|
167
|
-
defaultCode,
|
|
168
|
-
])
|
|
169
|
+
}, [isLoadingFiles, pkg, pkgFiles.data, defaultCode])
|
|
169
170
|
|
|
170
171
|
const createPackageMutation = useCreatePackageMutation()
|
|
171
172
|
const { mutate: createRelease } = useCreatePackageReleaseMutation({
|
|
@@ -250,10 +251,21 @@ export function CodeAndPreview({ pkg }: Props) {
|
|
|
250
251
|
setState((prev) => ({ ...prev, lastSavedAt: Date.now() }))
|
|
251
252
|
|
|
252
253
|
if (pkg) {
|
|
253
|
-
updatePackageFilesMutation.mutate(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
updatePackageFilesMutation.mutate(
|
|
255
|
+
{
|
|
256
|
+
package_name_with_version: `${pkg.name}@latest`,
|
|
257
|
+
...pkg,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
onSuccess: () => {
|
|
261
|
+
setState((prev) => ({
|
|
262
|
+
...prev,
|
|
263
|
+
initialFilesLoad: [...prev.pkgFilesWithContent],
|
|
264
|
+
}))
|
|
265
|
+
pkgFiles.refetch()
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
)
|
|
257
269
|
}
|
|
258
270
|
}
|
|
259
271
|
|
|
@@ -284,10 +296,17 @@ export function CodeAndPreview({ pkg }: Props) {
|
|
|
284
296
|
state.pkgFilesWithContent,
|
|
285
297
|
])
|
|
286
298
|
const mainComponentPath = useMemo(() => {
|
|
287
|
-
|
|
299
|
+
const isReactComponentExported =
|
|
300
|
+
/export function\s+\w+/.test(currentFileCode) ||
|
|
301
|
+
/export const\s+\w+\s*=/.test(currentFileCode) ||
|
|
302
|
+
/export default\s+\w+/.test(currentFileCode) ||
|
|
303
|
+
/export default\s+function\s*(\w*)\s*\(/.test(currentFileCode) ||
|
|
304
|
+
/export default\s*\(\s*\)\s*=>/.test(currentFileCode)
|
|
305
|
+
|
|
306
|
+
return (state.currentFile?.endsWith(".tsx") ||
|
|
307
|
+
state.currentFile?.endsWith(".ts")) &&
|
|
288
308
|
!!state.pkgFilesWithContent.some((x) => x.path == state.currentFile) &&
|
|
289
|
-
|
|
290
|
-
currentFileCode.match(/export const (\w+) ?=/))
|
|
309
|
+
isReactComponentExported
|
|
291
310
|
? state.currentFile
|
|
292
311
|
: state.defaultComponentFile
|
|
293
312
|
}, [state.currentFile, state.pkgFilesWithContent, currentFileCode])
|
|
@@ -327,6 +346,8 @@ export function CodeAndPreview({ pkg }: Props) {
|
|
|
327
346
|
})
|
|
328
347
|
}
|
|
329
348
|
|
|
349
|
+
const { handleCreateFile } = useFileManagement(state, setState)
|
|
350
|
+
|
|
330
351
|
if ((!pkg && urlParams.package_id) || pkgFiles.isLoading || isLoadingFiles) {
|
|
331
352
|
return (
|
|
332
353
|
<div className="flex items-center justify-center h-64">
|
|
@@ -363,6 +384,7 @@ export function CodeAndPreview({ pkg }: Props) {
|
|
|
363
384
|
)}
|
|
364
385
|
>
|
|
365
386
|
<CodeEditor
|
|
387
|
+
handleCreateFile={handleCreateFile}
|
|
366
388
|
currentFile={state.currentFile}
|
|
367
389
|
setCurrentFile={(file) =>
|
|
368
390
|
setState((prev) => ({ ...prev, currentFile: file }))
|
|
@@ -379,7 +401,6 @@ export function CodeAndPreview({ pkg }: Props) {
|
|
|
379
401
|
),
|
|
380
402
|
}))
|
|
381
403
|
}}
|
|
382
|
-
onDtsChange={(dts) => setState((prev) => ({ ...prev, dts }))}
|
|
383
404
|
pkgFilesLoaded={state.pkgFilesLoaded}
|
|
384
405
|
/>
|
|
385
406
|
</div>
|
|
@@ -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>
|
|
@@ -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/pages/dashboard.tsx
CHANGED