@tscircuit/fake-snippets 0.0.44 → 0.0.46

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 (35) hide show
  1. package/.github/workflows/bun-pver-release.yml +1 -0
  2. package/bun.lock +43 -11
  3. package/dist/bundle.js +405 -335
  4. package/dist/schema.d.ts +1845 -0
  5. package/dist/schema.js +251 -0
  6. package/fake-snippets-api/routes/api/_fake/received_quotes.ts +66 -0
  7. package/fake-snippets-api/routes/api/package_releases/update.ts +25 -18
  8. package/package.json +8 -3
  9. package/src/components/CodeAndPreview.tsx +0 -1
  10. package/src/components/CodeEditor.tsx +0 -1
  11. package/src/components/CodeEditorHeader.tsx +0 -25
  12. package/src/components/EditorNav.tsx +10 -8
  13. package/src/components/ErrorOutline.tsx +35 -0
  14. package/src/components/FileSidebar.tsx +46 -16
  15. package/src/components/NotFound.tsx +37 -0
  16. package/src/components/PreviewContent.tsx +0 -6
  17. package/src/components/TrendingSnippetCarousel.tsx +1 -1
  18. package/src/components/ViewPackagePage/components/package-header.tsx +24 -3
  19. package/src/components/ViewPackagePage/utils/is-hidden-file.ts +0 -1
  20. package/src/components/dialogs/package-visibility-settings-dialog.tsx +10 -1
  21. package/src/components/dialogs/view-ts-files-dialog.tsx +0 -6
  22. package/src/components/package-port/CodeAndPreview.tsx +24 -9
  23. package/src/components/package-port/CodeEditor.tsx +26 -38
  24. package/src/components/package-port/CodeEditorHeader.tsx +117 -39
  25. package/src/components/package-port/EditorNav.tsx +10 -8
  26. package/src/components/ui/tree-view.tsx +5 -1
  27. package/src/{prettier.ts → lib/types.ts} +3 -1
  28. package/src/lib/utils/findTargetFile.ts +62 -0
  29. package/src/lib/utils/load-prettier.ts +3 -0
  30. package/src/pages/404.tsx +2 -33
  31. package/src/pages/package-editor.tsx +14 -3
  32. package/src/pages/user-profile.tsx +66 -27
  33. package/src/components/FootprintDialog.tsx +0 -339
  34. package/src/components/ParametersEditor.tsx +0 -140
  35. package/src/lib/utils/parseFootprintParams.ts +0 -52
@@ -0,0 +1,37 @@
1
+ import { Button } from "@/components/ui/button"
2
+ import { PrefetchPageLink } from "@/components/PrefetchPageLink"
3
+
4
+ export function NotFound({ heading = "Page not found" }: { heading?: string }) {
5
+ return (
6
+ <section className="flex-1 flex items-center justify-center min-h-[90vh]">
7
+ <div className="container px-4 md:px-6 py-12 flex flex-col items-center text-center max-w-3xl">
8
+ <div className="mb-8 flex flex-col items-center justify-center">
9
+ <div className="mb-2">
10
+ <span className="text-3xl font-bold text-white bg-blue-500 px-4 py-2 rounded-md shadow-md inline-block">
11
+ 404
12
+ </span>
13
+ </div>
14
+ </div>
15
+ <h1 className="text-4xl font-extrabold tracking-tight lg:text-5xl mb-4">
16
+ {heading}
17
+ </h1>
18
+ <p className="text-xl text-muted-foreground mb-8">
19
+ The page you're looking for doesn't exist or has been moved to another
20
+ address.
21
+ </p>
22
+ <div className="flex flex-col sm:flex-row gap-4">
23
+ <PrefetchPageLink href="/">
24
+ <Button size="lg" className="bg-blue-500 hover:bg-blue-600">
25
+ Return Home
26
+ </Button>
27
+ </PrefetchPageLink>
28
+ <PrefetchPageLink href="/search">
29
+ <Button size="lg" variant="outline">
30
+ Search Packages
31
+ </Button>
32
+ </PrefetchPageLink>
33
+ </div>
34
+ </div>
35
+ </section>
36
+ )
37
+ }
@@ -55,12 +55,6 @@ export interface PreviewContentProps {
55
55
  onManualEditsFileContentChange?: (newmanualEditsFileContent: string) => void
56
56
  }
57
57
 
58
- declare global {
59
- interface Window {
60
- TSCIRCUIT_3D_OBJECT_REF: any
61
- }
62
- }
63
-
64
58
  export const PreviewContent = ({
65
59
  code,
66
60
  triggerRunTsx,
@@ -32,7 +32,7 @@ export const TrendingSnippetCarousel = () => {
32
32
  {trendingSnippets?.length ? (
33
33
  <>
34
34
  <div className="container mx-auto px-4">
35
- <h2 className="text-2xl font-semibold mb-6">Trending Snippets</h2>
35
+ <h2 className="text-2xl font-semibold mb-6">Trending Packages</h2>
36
36
  </div>
37
37
  <div
38
38
  className="flex gap-6 overflow-x-hidden relative"
@@ -8,8 +8,9 @@ import {
8
8
  } from "@/hooks/use-package-stars"
9
9
  import { LockClosedIcon } from "@radix-ui/react-icons"
10
10
  import { GitFork, Star } from "lucide-react"
11
- import { useToast } from "@/hooks/use-toast"
12
11
  import { Link } from "wouter"
12
+ import { useOrderDialog } from "@tscircuit/runframe"
13
+ import { useEffect } from "react"
13
14
 
14
15
  interface PackageInfo {
15
16
  name: string
@@ -37,8 +38,7 @@ export default function PackageHeader({
37
38
  const author = packageInfo?.owner_github_username
38
39
  const packageName = packageInfo?.unscoped_name
39
40
 
40
- const { toast } = useToast()
41
-
41
+ const { OrderDialog, isOpen, open, close, stage, setStage } = useOrderDialog()
42
42
  const { data: starData, isLoading: isStarDataLoading } =
43
43
  usePackageStarsByName(packageInfo?.name ?? null)
44
44
  const { addStar, removeStar } = usePackageStarMutationByName(
@@ -66,6 +66,12 @@ export default function PackageHeader({
66
66
  const isStarLoading =
67
67
  isStarDataLoading || addStar.isLoading || removeStar.isLoading
68
68
 
69
+ useEffect(() => {
70
+ window.TSCIRCUIT_REGISTRY_API_BASE_URL =
71
+ import.meta.env.VITE_TSCIRCUIT_REGISTRY_API_URL ??
72
+ `${window.location.origin}/api`
73
+ }, [])
74
+
69
75
  return (
70
76
  <header className="bg-white border-b border-gray-200 py-4">
71
77
  <div className="max-w-[1200px] mx-auto px-4">
@@ -103,6 +109,15 @@ export default function PackageHeader({
103
109
  )}
104
110
  </div>
105
111
  <div className="items-center space-x-2 hidden md:flex">
112
+ {/* WIP: add order button */}
113
+ {/* <Button
114
+ size="sm"
115
+ variant="default"
116
+ className="bg-blue-600 hover:bg-blue-700"
117
+ onClick={open}
118
+ >
119
+ Place Order
120
+ </Button> */}
106
121
  <Button
107
122
  variant="outline"
108
123
  size="sm"
@@ -165,6 +180,12 @@ export default function PackageHeader({
165
180
  </div>
166
181
  </div>
167
182
  </div>
183
+ <OrderDialog
184
+ isOpen={isOpen}
185
+ onClose={close}
186
+ stage={stage}
187
+ setStage={setStage}
188
+ />
168
189
  </header>
169
190
  )
170
191
  }
@@ -25,7 +25,6 @@ export const isHiddenFile = (filePath: string): boolean => {
25
25
  /\.eslintrc(\.[^/]*)?$/,
26
26
  /\.prettierrc(\.[^/]*)?$/,
27
27
  /\.babelrc$/,
28
- /tscircuit\.config\.json/,
29
28
  /tsconfig\.json$/,
30
29
  /jest\.config\.[^/]*$/,
31
30
  /vite\.config\.[^/]*$/,
@@ -1,6 +1,12 @@
1
1
  import { useState } from "react"
2
2
  import { Button } from "../ui/button"
3
- import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
3
+ import {
4
+ Dialog,
5
+ DialogContent,
6
+ DialogHeader,
7
+ DialogTitle,
8
+ DialogDescription,
9
+ } from "../ui/dialog"
4
10
  import { Label } from "../ui/label"
5
11
  import { RadioGroup, RadioGroupItem } from "../ui/radio-group"
6
12
  import { createUseDialog } from "./create-use-dialog"
@@ -23,6 +29,9 @@ export const PackageVisibilitySettingsDialog = ({
23
29
  <DialogContent>
24
30
  <DialogHeader>
25
31
  <DialogTitle>Package Privacy Settings</DialogTitle>
32
+ <DialogDescription>
33
+ Control whether your package is publicly visible or private.
34
+ </DialogDescription>
26
35
  </DialogHeader>
27
36
  <div className="py-1">
28
37
  <RadioGroup
@@ -13,12 +13,6 @@ interface ViewTsFilesDialogProps {
13
13
  onOpenChange: (open: boolean) => void
14
14
  }
15
15
 
16
- declare global {
17
- interface Window {
18
- __DEBUG_CODE_EDITOR_FS_MAP: Map<string, string>
19
- }
20
- }
21
-
22
16
  export const ViewTsFilesDialog: React.FC<ViewTsFilesDialogProps> = ({
23
17
  open,
24
18
  onOpenChange,
@@ -9,7 +9,6 @@ import { decodeUrlHashToText } from "@/lib/decodeUrlHashToText"
9
9
  import { getSnippetTemplate } from "@/lib/get-snippet-template"
10
10
  import { cn } from "@/lib/utils"
11
11
  import { parseJsonOrNull } from "@/lib/utils/parseJsonOrNull"
12
- import "@/prettier"
13
12
  import type { Package } from "fake-snippets-api/lib/db/schema"
14
13
  import { Loader2 } from "lucide-react"
15
14
  import { useEffect, useMemo, useState } from "react"
@@ -23,12 +22,13 @@ import { useCreatePackageReleaseMutation } from "@/hooks/use-create-package-rele
23
22
  import { useUpdatePackageFilesMutation } from "@/hooks/useUpdatePackageFilesMutation"
24
23
  import { useUpdatePackageMutation } from "@/hooks/useUpdatePackageMutation"
25
24
  import { usePackageFilesLoader } from "@/hooks/usePackageFilesLoader"
25
+ import { findTargetFile } from "@/lib/utils/findTargetFile"
26
26
 
27
27
  interface Props {
28
28
  pkg?: Package
29
29
  }
30
30
 
31
- interface PackageFile {
31
+ export interface PackageFile {
32
32
  path: string
33
33
  content: string
34
34
  }
@@ -94,6 +94,8 @@ export function CodeAndPreview({ pkg }: Props) {
94
94
  const [isPrivate, setIsPrivate] = useState(false)
95
95
 
96
96
  const entryPointCode = useMemo(() => {
97
+ const entryPointFile = findTargetFile(pkgFilesWithContent, null)
98
+ if (entryPointFile && entryPointFile.content) return entryPointFile.content
97
99
  return (
98
100
  pkgFilesWithContent.find((x) => x.path === "index.tsx")?.content ??
99
101
  defaultCode
@@ -147,6 +149,10 @@ export function CodeAndPreview({ pkg }: Props) {
147
149
  const { data: loadedFiles, isLoading: isLoadingFiles } =
148
150
  usePackageFilesLoader(pkg)
149
151
 
152
+ const [pkgFilesLoaded, setPkgFilesLoaded] = useState<boolean>(
153
+ urlParams.package_id ? false : true,
154
+ )
155
+
150
156
  useEffect(() => {
151
157
  if (!pkgFiles.data?.length) {
152
158
  if (pkg && pkgFilesWithContent.length === 0) {
@@ -171,6 +177,7 @@ export function CodeAndPreview({ pkg }: Props) {
171
177
  }
172
178
 
173
179
  setPkgFilesWithContent(processedResults)
180
+ setPkgFilesLoaded(true)
174
181
  setInitialFilesLoad(processedResults)
175
182
  setLastRunCode(
176
183
  processedResults.find((x) => x.path === "index.tsx")?.content ??
@@ -185,12 +192,6 @@ export function CodeAndPreview({ pkg }: Props) {
185
192
  defaultCode,
186
193
  ])
187
194
 
188
- // useEffect(() => {
189
- // if (pkg && pkgFiles.data) {
190
- // loadPkgFiles()
191
- // }
192
- // }, [pkg, pkgFiles.data])
193
-
194
195
  const createPackageMutation = useCreatePackageMutation()
195
196
 
196
197
  const { mutate: createRelease, isLoading: isCreatingRelease } =
@@ -303,11 +304,24 @@ export function CodeAndPreview({ pkg }: Props) {
303
304
  : `${importStatement}\ncircuit.add(\n <board>\n <Snippet name="U1" />\n </board>\n)`
304
305
 
305
306
  return {
307
+ ...pkgFilesWithContent.reduce(
308
+ (acc, file) => {
309
+ acc[file.path] = file.content
310
+ return acc
311
+ },
312
+ {} as Record<string, string>,
313
+ ),
306
314
  "index.tsx": entryPointCode ?? "// No Default Code Found",
307
315
  "manual-edits.json": manualEditsFileContent ?? "{}",
308
316
  "main.tsx": entrypointContent.trim(),
309
317
  }
310
- }, [manualEditsFileContent, entryPointCode, code, packageType])
318
+ }, [
319
+ manualEditsFileContent,
320
+ entryPointCode,
321
+ code,
322
+ packageType,
323
+ pkgFilesWithContent,
324
+ ])
311
325
 
312
326
  if ((!pkg && urlParams.package_id) || pkgFiles.isLoading) {
313
327
  return (
@@ -355,6 +369,7 @@ export function CodeAndPreview({ pkg }: Props) {
355
369
  )
356
370
  }}
357
371
  onDtsChange={setDts}
372
+ pkgFilesLoaded={pkgFilesLoaded}
358
373
  />
359
374
  </div>
360
375
  {showPreview && (
@@ -26,19 +26,15 @@ import { EditorView } from "codemirror"
26
26
  import { useEffect, useMemo, useRef, useState } from "react"
27
27
  import ts from "typescript"
28
28
  import CodeEditorHeader from "@/components/package-port/CodeEditorHeader"
29
- // import { copilotPlugin, Language } from "@valtown/codemirror-codeium"
30
29
  import { useCodeCompletionApi } from "@/hooks/use-code-completion-ai-api"
31
30
  import FileSidebar from "../FileSidebar"
32
-
31
+ import { findTargetFile } from "@/lib/utils/findTargetFile"
32
+ import type { PackageFile } from "./CodeAndPreview"
33
33
  const defaultImports = `
34
34
  import React from "@types/react/jsx-runtime"
35
35
  import { Circuit, createUseComponent } from "@tscircuit/core"
36
36
  import type { CommonLayoutProps } from "@tscircuit/props"
37
37
  `
38
- export interface FileContent {
39
- path: string
40
- content: string
41
- }
42
38
 
43
39
  export const CodeEditor = ({
44
40
  onCodeChange,
@@ -48,12 +44,14 @@ export const CodeEditor = ({
48
44
  isStreaming = false,
49
45
  showImportAndFormatButtons = true,
50
46
  onFileContentChanged,
47
+ pkgFilesLoaded,
51
48
  }: {
52
49
  onCodeChange: (code: string, filename?: string) => void
53
50
  onDtsChange?: (dts: string) => void
54
- files: FileContent[]
51
+ files: PackageFile[]
55
52
  readOnly?: boolean
56
53
  isStreaming?: boolean
54
+ pkgFilesLoaded?: boolean
57
55
  showImportAndFormatButtons?: boolean
58
56
  onFileContentChanged?: (path: string, content: string) => void
59
57
  }) => {
@@ -62,7 +60,6 @@ export const CodeEditor = ({
62
60
  const ataRef = useRef<ReturnType<typeof setupTypeAcquisition> | null>(null)
63
61
  const apiUrl = useSnippetsBaseApiUrl()
64
62
  const codeCompletionApi = useCodeCompletionApi()
65
-
66
63
  const [cursorPosition, setCursorPosition] = useState<number | null>(null)
67
64
  const [code, setCode] = useState(files[0]?.content || "")
68
65
  const [currentFile, setCurrentFile] = useState<string>("")
@@ -70,35 +67,17 @@ export const CodeEditor = ({
70
67
  // Get URL search params for file_path
71
68
  const urlParams = new URLSearchParams(window.location.search)
72
69
  const filePathFromUrl = urlParams.get("file_path")
73
-
74
70
  // Set current file on component mount
75
71
  useEffect(() => {
76
- if (files.length > 0 && currentFile === "") {
77
- // Priority 1: Use file_path from URL if it exists in files
78
- if (
79
- filePathFromUrl &&
80
- files.some((file) => file.path === filePathFromUrl)
81
- ) {
82
- setCurrentFile(filePathFromUrl)
83
- }
84
- // Priority 2: Use index.tsx if it exists in files
85
- else if (files.some((file) => file.path === "index.tsx")) {
86
- setCurrentFile("index.tsx")
87
- }
88
- // Priority 3: Use the first file with .tsx extension
89
- else {
90
- const tsxFile = files.find((file) => file.path.endsWith(".tsx"))
91
- if (tsxFile) {
92
- setCurrentFile(tsxFile.path)
93
- }
94
- // Fallback: Use the first file in the array
95
- else if (files[0]) {
96
- setCurrentFile(files[0].path)
97
- }
98
- }
99
- return
72
+ if (files.length === 0 || !pkgFilesLoaded || currentFile) return
73
+
74
+ const targetFile = findTargetFile(files, filePathFromUrl)
75
+
76
+ if (targetFile) {
77
+ setCurrentFile(targetFile.path)
78
+ setCode(targetFile.content)
100
79
  }
101
- }, [files])
80
+ }, [filePathFromUrl, pkgFilesLoaded])
102
81
 
103
82
  const fileMap = useMemo(() => {
104
83
  const map: Record<string, string> = {}
@@ -448,6 +427,12 @@ export const CodeEditor = ({
448
427
 
449
428
  const handleFileChange = (path: string) => {
450
429
  setCurrentFile(path)
430
+ try {
431
+ // Set url query to file path
432
+ const urlParams = new URLSearchParams(window.location.search)
433
+ urlParams.set("file_path", path)
434
+ window.history.replaceState(null, "", `?${urlParams.toString()}`)
435
+ } catch {}
451
436
  }
452
437
 
453
438
  const updateFileContent = (path: string, newContent: string) => {
@@ -478,7 +463,7 @@ export const CodeEditor = ({
478
463
  }
479
464
  const [sidebarOpen, setSidebarOpen] = useState(false)
480
465
  return (
481
- <div className="flex h-full">
466
+ <div className="flex h-full w-full">
482
467
  <FileSidebar
483
468
  files={Object.fromEntries(files.map((f) => [f.path, f.content]))}
484
469
  currentFile={currentFile}
@@ -487,7 +472,7 @@ export const CodeEditor = ({
487
472
  }
488
473
  onFileSelect={handleFileChange}
489
474
  />
490
- <div className="flex flex-col flex-1">
475
+ <div className="flex flex-col flex-1 w-full min-w-0">
491
476
  {showImportAndFormatButtons && (
492
477
  <CodeEditorHeader
493
478
  fileSidebarState={
@@ -500,10 +485,13 @@ export const CodeEditor = ({
500
485
  updateFileContent={(...args) => {
501
486
  return updateFileContent(...args)
502
487
  }}
503
- cursorPosition={cursorPosition}
488
+ handleFileChange={handleFileChange}
504
489
  />
505
490
  )}
506
- <div ref={editorRef} className="flex-1 overflow-auto max-w-[100%]" />
491
+ <div
492
+ ref={editorRef}
493
+ className="flex-1 overflow-auto [&_.cm-editor]:h-full"
494
+ />
507
495
  </div>
508
496
  </div>
509
497
  )
@@ -3,7 +3,6 @@ import { Button } from "@/components/ui/button"
3
3
  import { handleManualEditsImport } from "@/lib/handleManualEditsImport"
4
4
  import { useImportSnippetDialog } from "@/components/dialogs/import-snippet-dialog"
5
5
  import { useToast } from "@/hooks/use-toast"
6
- import { FootprintDialog } from "@/components/FootprintDialog"
7
6
  import {
8
7
  DropdownMenu,
9
8
  DropdownMenuContent,
@@ -12,36 +11,70 @@ import {
12
11
  } from "@/components/ui/dropdown-menu"
13
12
  import { AlertTriangle, PanelRightClose } from "lucide-react"
14
13
  import { checkIfManualEditsImported } from "@/lib/utils/checkIfManualEditsImported"
15
-
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"
16
22
  export type FileName = string
17
23
 
18
24
  interface CodeEditorHeaderProps {
19
25
  currentFile: FileName
20
26
  files: Record<FileName, string>
21
27
  updateFileContent: (filename: FileName, content: string) => void
22
- cursorPosition: number | null
23
28
  fileSidebarState: ReturnType<typeof useState<boolean>>
29
+ handleFileChange: (filename: FileName) => void
24
30
  }
25
31
 
26
32
  export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
27
33
  currentFile,
28
34
  files,
29
35
  updateFileContent,
30
- cursorPosition,
31
36
  fileSidebarState,
37
+ handleFileChange,
32
38
  }) => {
33
39
  const { Dialog: ImportSnippetDialog, openDialog: openImportDialog } =
34
40
  useImportSnippetDialog()
35
- const [footprintDialogOpen, setFootprintDialogOpen] = useState(false)
36
41
  const { toast } = useToast()
37
42
  const [sidebarOpen, setSidebarOpen] = fileSidebarState
43
+
38
44
  const handleFormatFile = useCallback(() => {
39
45
  if (!window.prettier || !window.prettierPlugins) return
40
-
41
46
  try {
42
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
+ }
43
67
 
44
- if (currentFile.endsWith(".json")) {
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") {
45
78
  try {
46
79
  const jsonObj = JSON.parse(currentContent)
47
80
  const formattedJson = JSON.stringify(jsonObj, null, 2)
@@ -52,28 +85,47 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
52
85
  description: "Failed to format JSON: invalid syntax.",
53
86
  variant: "destructive",
54
87
  })
55
- return
56
88
  }
57
89
  return
58
90
  }
59
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"
60
102
  const formattedCode = window.prettier.format(currentContent, {
61
103
  semi: false,
62
- parser: "typescript",
104
+ parser: parser,
63
105
  plugins: window.prettierPlugins,
64
106
  })
65
107
 
66
108
  updateFileContent(currentFile, formattedCode)
67
109
  } catch (error) {
68
110
  console.error("Formatting error:", error)
69
- toast({
70
- title: "Formatting error",
71
- description:
72
- error instanceof Error
73
- ? error.message
74
- : "Failed to format the code. Please check for syntax errors.",
75
- variant: "destructive",
76
- })
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
+ }
77
129
  }
78
130
  }, [currentFile, files, toast, updateFileContent])
79
131
 
@@ -81,13 +133,59 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
81
133
  <>
82
134
  <div className="flex items-center gap-2 px-2 border-b border-gray-200">
83
135
  <button
84
- className={`text-black/60 scale-90 transition-opacity duration-200 ${sidebarOpen ? "opacity-0 pointer-events-none" : "opacity-100"}`}
136
+ className={`text-gray-400 scale-90 transition-opacity duration-200 ${
137
+ sidebarOpen ? "opacity-0 pointer-events-none" : "opacity-100"
138
+ }`}
85
139
  onClick={() => setSidebarOpen(true)}
86
140
  >
87
141
  <PanelRightClose />
88
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>
89
187
 
90
- <div className="flex items-center gap-2 px-2 py-1 ml-auto">
188
+ <div className="flex items-center overflow-x-hidden gap-2 px-2 py-1 ml-auto">
91
189
  {checkIfManualEditsImported(files) && (
92
190
  <DropdownMenu>
93
191
  <DropdownMenuTrigger asChild>
@@ -112,18 +210,6 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
112
210
  </DropdownMenuContent>
113
211
  </DropdownMenu>
114
212
  )}
115
- <DropdownMenu>
116
- <DropdownMenuTrigger asChild>
117
- <Button size="sm" variant="ghost">
118
- Insert
119
- </Button>
120
- </DropdownMenuTrigger>
121
- <DropdownMenuContent>
122
- <DropdownMenuItem onClick={() => setFootprintDialogOpen(true)}>
123
- Chip
124
- </DropdownMenuItem>
125
- </DropdownMenuContent>
126
- </DropdownMenu>
127
213
  <Button size="sm" variant="ghost" onClick={() => openImportDialog()}>
128
214
  Import
129
215
  </Button>
@@ -137,14 +223,6 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
137
223
  updateFileContent(currentFile, newContent)
138
224
  }}
139
225
  />
140
- <FootprintDialog
141
- currentFile={currentFile as `${string}.${string}`}
142
- open={footprintDialogOpen}
143
- onOpenChange={setFootprintDialogOpen}
144
- updateFileContent={updateFileContent}
145
- files={files}
146
- cursorPosition={cursorPosition}
147
- />
148
226
  </div>
149
227
  </>
150
228
  )
@@ -218,14 +218,16 @@ export default function EditorNav({
218
218
  {pkg.star_count}
219
219
  </span>
220
220
  )}
221
- <Button
222
- variant="ghost"
223
- size="icon"
224
- className="h-6 w-6 ml-2"
225
- onClick={() => openRenameDialog()}
226
- >
227
- <Pencil className="h-3 w-3 text-gray-700" />
228
- </Button>
221
+ {pkg.owner_github_username === session?.github_username && (
222
+ <Button
223
+ variant="ghost"
224
+ size="icon"
225
+ className="h-6 w-6 ml-2"
226
+ onClick={() => openRenameDialog()}
227
+ >
228
+ <Pencil className="h-3 w-3 text-gray-700" />
229
+ </Button>
230
+ )}
229
231
  {isPrivate && (
230
232
  <div className="relative group">
231
233
  <LockClosedIcon className="h-3 w-3 text-gray-700" />