@tscircuit/fake-snippets 0.0.67 → 0.0.69

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 (40) hide show
  1. package/bun-tests/fake-snippets-api/routes/packages/update.test.ts +104 -0
  2. package/bun.lock +26 -83
  3. package/dist/bundle.js +17 -5
  4. package/dist/index.d.ts +5 -0
  5. package/dist/index.js +2 -1
  6. package/dist/schema.d.ts +8 -0
  7. package/dist/schema.js +2 -1
  8. package/fake-snippets-api/lib/db/schema.ts +4 -0
  9. package/fake-snippets-api/routes/api/packages/create.ts +1 -0
  10. package/fake-snippets-api/routes/api/packages/update.ts +11 -2
  11. package/package.json +3 -4
  12. package/src/App.tsx +0 -4
  13. package/src/components/CmdKMenu.tsx +19 -19
  14. package/src/components/FAQ.tsx +3 -1
  15. package/src/components/FileSidebar.tsx +50 -1
  16. package/src/components/Header2.tsx +20 -9
  17. package/src/components/JLCPCBImportDialog.tsx +13 -16
  18. package/src/components/ViewPackagePage/components/important-files-view.tsx +1 -1
  19. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -0
  20. package/src/components/ViewPackagePage/components/package-header.tsx +22 -12
  21. package/src/components/ViewPackagePage/components/repo-page-content.tsx +23 -7
  22. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +1 -0
  23. package/src/components/dialogs/confirm-delete-package-dialog.tsx +8 -0
  24. package/src/components/dialogs/edit-package-details-dialog.tsx +177 -139
  25. package/src/components/package-port/CodeAndPreview.tsx +40 -19
  26. package/src/components/package-port/CodeEditor.tsx +8 -27
  27. package/src/components/package-port/EditorNav.tsx +1 -11
  28. package/src/hooks/use-package-details-form.ts +15 -1
  29. package/src/hooks/useFileManagement.ts +59 -0
  30. package/src/index.css +13 -0
  31. package/src/lib/utils/isValidFileName.ts +5 -0
  32. package/src/pages/dashboard.tsx +1 -0
  33. package/src/pages/quickstart.tsx +5 -5
  34. package/src/pages/search.tsx +1 -1
  35. package/src/pages/user-profile.tsx +1 -0
  36. package/src/components/OrderPreviewContent.tsx +0 -61
  37. package/src/components/ViewSnippetSidebar.tsx +0 -162
  38. package/src/components/dialogs/create-order-dialog.tsx +0 -146
  39. package/src/pages/preview.tsx +0 -44
  40. package/src/pages/view-order.tsx +0 -111
package/dist/index.d.ts CHANGED
@@ -508,6 +508,7 @@ declare const packageSchema: z.ZodObject<{
508
508
  ai_description: z.ZodNullable<z.ZodString>;
509
509
  latest_license: z.ZodOptional<z.ZodNullable<z.ZodString>>;
510
510
  ai_usage_instructions: z.ZodNullable<z.ZodString>;
511
+ default_view: z.ZodOptional<z.ZodDefault<z.ZodEnum<["files", "3d", "pcb", "schematic"]>>>;
511
512
  }, "strip", z.ZodTypeAny, {
512
513
  name: string;
513
514
  unscoped_name: string;
@@ -536,6 +537,7 @@ declare const packageSchema: z.ZodObject<{
536
537
  ai_usage_instructions: string | null;
537
538
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
538
539
  latest_license?: string | null | undefined;
540
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
539
541
  }, {
540
542
  name: string;
541
543
  unscoped_name: string;
@@ -564,6 +566,7 @@ declare const packageSchema: z.ZodObject<{
564
566
  is_source_from_github?: boolean | undefined;
565
567
  website?: string | null | undefined;
566
568
  latest_license?: string | null | undefined;
569
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
567
570
  }>;
568
571
  type Package = z.infer<typeof packageSchema>;
569
572
  declare const jlcpcbOrderStateSchema: z.ZodObject<{
@@ -778,6 +781,7 @@ declare const createDatabase: ({ seed }?: {
778
781
  ai_usage_instructions: string | null;
779
782
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
780
783
  latest_license?: string | null | undefined;
784
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
781
785
  }[];
782
786
  orders: {
783
787
  error: z.objectOutputType<{
@@ -1068,6 +1072,7 @@ declare const createDatabase: ({ seed }?: {
1068
1072
  ai_usage_instructions: string | null;
1069
1073
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
1070
1074
  latest_license?: string | null | undefined;
1075
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
1071
1076
  }[];
1072
1077
  orders: {
1073
1078
  error: z.objectOutputType<{
package/dist/index.js CHANGED
@@ -187,7 +187,8 @@ var packageSchema = z.object({
187
187
  star_count: z.number().default(0),
188
188
  ai_description: z.string().nullable(),
189
189
  latest_license: z.string().nullable().optional(),
190
- ai_usage_instructions: z.string().nullable()
190
+ ai_usage_instructions: z.string().nullable(),
191
+ default_view: z.enum(["files", "3d", "pcb", "schematic"]).default("files").optional()
191
192
  });
192
193
  var jlcpcbOrderStateSchema = z.object({
193
194
  jlcpcb_order_state_id: z.string(),
package/dist/schema.d.ts CHANGED
@@ -633,6 +633,7 @@ declare const packageSchema: z.ZodObject<{
633
633
  ai_description: z.ZodNullable<z.ZodString>;
634
634
  latest_license: z.ZodOptional<z.ZodNullable<z.ZodString>>;
635
635
  ai_usage_instructions: z.ZodNullable<z.ZodString>;
636
+ default_view: z.ZodOptional<z.ZodDefault<z.ZodEnum<["files", "3d", "pcb", "schematic"]>>>;
636
637
  }, "strip", z.ZodTypeAny, {
637
638
  name: string;
638
639
  unscoped_name: string;
@@ -661,6 +662,7 @@ declare const packageSchema: z.ZodObject<{
661
662
  ai_usage_instructions: string | null;
662
663
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
663
664
  latest_license?: string | null | undefined;
665
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
664
666
  }, {
665
667
  name: string;
666
668
  unscoped_name: string;
@@ -689,6 +691,7 @@ declare const packageSchema: z.ZodObject<{
689
691
  is_source_from_github?: boolean | undefined;
690
692
  website?: string | null | undefined;
691
693
  latest_license?: string | null | undefined;
694
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
692
695
  }>;
693
696
  type Package = z.infer<typeof packageSchema>;
694
697
  declare const jlcpcbOrderStateSchema: z.ZodObject<{
@@ -1068,6 +1071,7 @@ declare const databaseSchema: z.ZodObject<{
1068
1071
  ai_description: z.ZodNullable<z.ZodString>;
1069
1072
  latest_license: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1070
1073
  ai_usage_instructions: z.ZodNullable<z.ZodString>;
1074
+ default_view: z.ZodOptional<z.ZodDefault<z.ZodEnum<["files", "3d", "pcb", "schematic"]>>>;
1071
1075
  }, "strip", z.ZodTypeAny, {
1072
1076
  name: string;
1073
1077
  unscoped_name: string;
@@ -1096,6 +1100,7 @@ declare const databaseSchema: z.ZodObject<{
1096
1100
  ai_usage_instructions: string | null;
1097
1101
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
1098
1102
  latest_license?: string | null | undefined;
1103
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
1099
1104
  }, {
1100
1105
  name: string;
1101
1106
  unscoped_name: string;
@@ -1124,6 +1129,7 @@ declare const databaseSchema: z.ZodObject<{
1124
1129
  is_source_from_github?: boolean | undefined;
1125
1130
  website?: string | null | undefined;
1126
1131
  latest_license?: string | null | undefined;
1132
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
1127
1133
  }>, "many">>;
1128
1134
  orders: z.ZodDefault<z.ZodArray<z.ZodObject<{
1129
1135
  order_id: z.ZodString;
@@ -1572,6 +1578,7 @@ declare const databaseSchema: z.ZodObject<{
1572
1578
  ai_usage_instructions: string | null;
1573
1579
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
1574
1580
  latest_license?: string | null | undefined;
1581
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
1575
1582
  }[];
1576
1583
  orders: {
1577
1584
  error: z.objectOutputType<{
@@ -1788,6 +1795,7 @@ declare const databaseSchema: z.ZodObject<{
1788
1795
  is_source_from_github?: boolean | undefined;
1789
1796
  website?: string | null | undefined;
1790
1797
  latest_license?: string | null | undefined;
1798
+ default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
1791
1799
  }[] | undefined;
1792
1800
  orders?: {
1793
1801
  error: z.objectInputType<{
package/dist/schema.js CHANGED
@@ -182,7 +182,8 @@ var packageSchema = z.object({
182
182
  star_count: z.number().default(0),
183
183
  ai_description: z.string().nullable(),
184
184
  latest_license: z.string().nullable().optional(),
185
- ai_usage_instructions: z.string().nullable()
185
+ ai_usage_instructions: z.string().nullable(),
186
+ default_view: z.enum(["files", "3d", "pcb", "schematic"]).default("files").optional()
186
187
  });
187
188
  var jlcpcbOrderStateSchema = z.object({
188
189
  jlcpcb_order_state_id: z.string(),
@@ -219,6 +219,10 @@ export const packageSchema = z.object({
219
219
  ai_description: z.string().nullable(),
220
220
  latest_license: z.string().nullable().optional(),
221
221
  ai_usage_instructions: z.string().nullable(),
222
+ default_view: z
223
+ .enum(["files", "3d", "pcb", "schematic"])
224
+ .default("files")
225
+ .optional(),
222
226
  })
223
227
  export type Package = z.infer<typeof packageSchema>
224
228
 
@@ -55,6 +55,7 @@ export default withRouteSpec({
55
55
  is_public: is_private === true ? false : true,
56
56
  is_unlisted: is_private === true ? true : (is_unlisted ?? false),
57
57
  ai_usage_instructions: "placeholder ai usage instructions",
58
+ default_view: "files",
58
59
  })
59
60
 
60
61
  if (!newPackage) {
@@ -20,6 +20,7 @@ export default withRouteSpec({
20
20
  website: z.string().optional(),
21
21
  is_private: z.boolean().optional(),
22
22
  is_unlisted: z.boolean().optional(),
23
+ default_view: z.enum(["files", "3d", "pcb", "schematic"]).optional(),
23
24
  })
24
25
  .transform((data) => ({
25
26
  ...data,
@@ -30,8 +31,15 @@ export default withRouteSpec({
30
31
  package: packageSchema,
31
32
  }),
32
33
  })(async (req, ctx) => {
33
- const { package_id, name, description, website, is_private, is_unlisted } =
34
- req.jsonBody
34
+ const {
35
+ package_id,
36
+ name,
37
+ description,
38
+ website,
39
+ is_private,
40
+ is_unlisted,
41
+ default_view,
42
+ } = req.jsonBody
35
43
 
36
44
  const packageIndex = ctx.db.packages.findIndex(
37
45
  (p) => p.package_id === package_id,
@@ -77,6 +85,7 @@ export default withRouteSpec({
77
85
  is_public:
78
86
  is_private !== undefined ? !is_private : existingPackage.is_public,
79
87
  is_unlisted: is_unlisted ?? existingPackage.is_unlisted,
88
+ default_view: default_view ?? existingPackage.default_view,
80
89
  updated_at: new Date().toISOString(),
81
90
  })
82
91
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.67",
3
+ "version": "0.0.69",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -70,13 +70,12 @@
70
70
  "@radix-ui/react-toggle": "^1.1.0",
71
71
  "@radix-ui/react-toggle-group": "^1.1.0",
72
72
  "@radix-ui/react-tooltip": "^1.1.2",
73
- "@tscircuit/3d-viewer": "^0.0.142",
74
73
  "@tscircuit/eval": "^0.0.198",
75
74
  "@tscircuit/footprinter": "^0.0.124",
76
75
  "@tscircuit/layout": "^0.0.29",
77
76
  "@tscircuit/math-utils": "^0.0.10",
78
77
  "@tscircuit/mm": "^0.0.8",
79
- "@tscircuit/props": "^0.0.143",
78
+ "@tscircuit/props": "^0.0.186",
80
79
  "@types/file-saver": "^2.0.7",
81
80
  "@types/ms": "^0.7.34",
82
81
  "@typescript/ata": "^0.9.7",
@@ -148,7 +147,7 @@
148
147
  "@tailwindcss/typography": "^0.5.16",
149
148
  "@tscircuit/core": "^0.0.384",
150
149
  "@tscircuit/prompt-benchmarks": "^0.0.28",
151
- "@tscircuit/runframe": "^0.0.461",
150
+ "@tscircuit/runframe": "^0.0.494",
152
151
  "@types/babel__standalone": "^7.1.7",
153
152
  "@types/bun": "^1.1.10",
154
153
  "@types/country-list": "^2.1.4",
package/src/App.tsx CHANGED
@@ -61,12 +61,10 @@ const EditorPage = lazyImport(async () => {
61
61
  const LandingPage = lazyImport(() => import("@/pages/landing"))
62
62
  const MyOrdersPage = lazyImport(() => import("@/pages/my-orders"))
63
63
  const LatestPage = lazyImport(() => import("@/pages/latest"))
64
- const PreviewPage = lazyImport(() => import("@/pages/preview"))
65
64
  const QuickstartPage = lazyImport(() => import("@/pages/quickstart"))
66
65
  const SearchPage = lazyImport(() => import("@/pages/search"))
67
66
  const SettingsPage = lazyImport(() => import("@/pages/settings"))
68
67
  const UserProfilePage = lazyImport(() => import("@/pages/user-profile"))
69
- const ViewOrderPage = lazyImport(() => import("@/pages/view-order"))
70
68
  const DevLoginPage = lazyImport(() => import("@/pages/dev-login"))
71
69
  const BetaPage = lazyImport(() => import("@/pages/beta"))
72
70
  const ViewPackagePage = lazyImport(() => import("@/pages/view-package"))
@@ -123,8 +121,6 @@ function App() {
123
121
  <Route path="/trending" component={TrendingPage} />
124
122
  <Route path="/authorize" component={AuthenticatePage} />
125
123
  <Route path="/my-orders" component={MyOrdersPage} />
126
- <Route path="/orders/:orderId" component={ViewOrderPage} />
127
- <Route path="/preview" component={PreviewPage} />
128
124
  <Route path="/dev-login" component={DevLoginPage} />
129
125
  <Route path="/:username" component={UserProfilePage} />
130
126
  <Route path="/:author/:packageName" component={ViewPackagePage} />
@@ -7,7 +7,7 @@ import { Package, Snippet } from "fake-snippets-api/lib/db/schema"
7
7
  import React from "react"
8
8
  import { useQuery } from "react-query"
9
9
 
10
- type SnippetType = "board" | "package" | "model" | "footprint" | "snippet"
10
+ type SnippetType = "board" | "package" | "model" | "footprint"
11
11
 
12
12
  interface Template {
13
13
  name: string
@@ -31,13 +31,13 @@ const CmdKMenu = () => {
31
31
 
32
32
  // Search results query
33
33
  const { data: searchResults = [], isLoading: isSearching } = useQuery(
34
- ["snippetSearch", searchQuery],
34
+ ["packageSearch", searchQuery],
35
35
  async () => {
36
36
  if (!searchQuery) return []
37
- const { data } = await axios.get("/snippets/search", {
38
- params: { q: searchQuery },
37
+ const { data } = await axios.post("/packages/search", {
38
+ query: searchQuery,
39
39
  })
40
- return data.snippets || []
40
+ return data.packages || []
41
41
  },
42
42
  {
43
43
  enabled: Boolean(searchQuery),
@@ -114,7 +114,7 @@ const CmdKMenu = () => {
114
114
  />
115
115
  </svg>
116
116
  <Command.Input
117
- placeholder="Search snippets and commands..."
117
+ placeholder="Search packages and commands..."
118
118
  value={searchQuery}
119
119
  onValueChange={setSearchQuery}
120
120
  className="w-full h-12 bg-transparent border-none outline-none text-gray-900 dark:text-gray-100 placeholder-gray-500"
@@ -133,27 +133,27 @@ const CmdKMenu = () => {
133
133
  heading="Search Results"
134
134
  className="px-2 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400"
135
135
  >
136
- {searchResults.map((snippet: Snippet) => (
136
+ {searchResults.map((pkg: Package) => (
137
137
  <Command.Item
138
- key={snippet.snippet_id}
139
- value={snippet.name || snippet.unscoped_name}
138
+ key={pkg.package_id}
139
+ value={pkg.name}
140
140
  onSelect={() => {
141
- window.location.href = `/editor?snippet_id=${snippet.snippet_id}`
141
+ window.location.href = `/editor?package_id=${pkg.package_id}`
142
142
  setOpen(false)
143
143
  }}
144
144
  className="flex items-center justify-between px-2 py-1.5 rounded-sm text-sm hover:bg-gray-100 dark:hover:bg-gray-700 cursor-default aria-selected:bg-gray-100 dark:aria-selected:bg-gray-700"
145
145
  >
146
146
  <div className="flex flex-col">
147
147
  <span className="text-gray-900 dark:text-gray-100">
148
- {snippet.name || snippet.unscoped_name}
148
+ {pkg.name}
149
149
  </span>
150
- {snippet.description && (
150
+ {pkg.description && (
151
151
  <span className="text-sm text-gray-500">
152
- {snippet.description}
152
+ {pkg.description}
153
153
  </span>
154
154
  )}
155
155
  </div>
156
- <span className="text-sm text-gray-500">snippet</span>
156
+ <span className="text-sm text-gray-500">package</span>
157
157
  </Command.Item>
158
158
  ))}
159
159
  </Command.Group>
@@ -161,7 +161,7 @@ const CmdKMenu = () => {
161
161
 
162
162
  {!searchQuery && recentPackages.length > 0 && (
163
163
  <Command.Group
164
- heading="Recent Snippets"
164
+ heading="Recent Packages"
165
165
  className="px-2 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400"
166
166
  >
167
167
  {recentPackages.slice(0, 6).map((pkg) => (
@@ -169,28 +169,28 @@ const CmdKMenu = () => {
169
169
  key={pkg.package_id}
170
170
  value={pkg.unscoped_name}
171
171
  onSelect={() => {
172
- window.location.href = `/editor?snippet_id=${pkg.package_id}`
172
+ window.location.href = `/editor?package_id=${pkg.package_id}`
173
173
  setOpen(false)
174
174
  }}
175
175
  className="flex items-center justify-between px-2 py-1.5 rounded-sm text-sm hover:bg-gray-100 dark:hover:bg-gray-700 cursor-default aria-selected:bg-gray-100 dark:aria-selected:bg-gray-700"
176
176
  >
177
177
  <div className="flex flex-col">
178
178
  <span className="text-gray-900 dark:text-gray-100">
179
- {pkg.unscoped_name}
179
+ {pkg.name}
180
180
  </span>
181
181
  <span className="text-sm text-gray-500">
182
182
  Last edited:{" "}
183
183
  {new Date(pkg.updated_at).toLocaleDateString()}
184
184
  </span>
185
185
  </div>
186
- <span className="text-sm text-gray-500">snippet</span>
186
+ <span className="text-sm text-gray-500">package</span>
187
187
  </Command.Item>
188
188
  ))}
189
189
  </Command.Group>
190
190
  )}
191
191
 
192
192
  <Command.Group
193
- heading="Start Blank Snippet"
193
+ heading="Start Blank Package"
194
194
  className="px-2 py-1.5 text-xs font-medium text-gray-500 dark:text-gray-400"
195
195
  >
196
196
  {blankTemplates.map((template) => (
@@ -176,7 +176,9 @@ export const FAQ = () => (
176
176
  <Accordion type="single" collapsible>
177
177
  {QUESTIONS.map((q, i) => (
178
178
  <AccordionItem key={i} value={`item-${i + 1}`}>
179
- <AccordionTrigger>{q.question}</AccordionTrigger>
179
+ <AccordionTrigger className="text-left">
180
+ {q.question}
181
+ </AccordionTrigger>{" "}
180
182
  <AccordionContent>{q.answer}</AccordionContent>
181
183
  </AccordionItem>
182
184
  ))}
@@ -1,8 +1,10 @@
1
1
  import React, { useState } from "react"
2
2
  import { cn } from "@/lib/utils"
3
- import { File, Folder, PanelRightOpen } from "lucide-react"
3
+ import { File, Folder, 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
+ import { Input } from "@/components/ui/input"
7
+ import { CreateFileProps } from "./package-port/CodeAndPreview"
6
8
 
7
9
  type FileName = string
8
10
 
@@ -12,6 +14,7 @@ interface FileSidebarProps {
12
14
  onFileSelect: (filename: FileName) => void
13
15
  className?: string
14
16
  fileSidebarState: ReturnType<typeof useState<boolean>>
17
+ handleCreateFile: (props: CreateFileProps) => void
15
18
  }
16
19
 
17
20
  const FileSidebar: React.FC<FileSidebarProps> = ({
@@ -20,8 +23,13 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
20
23
  onFileSelect,
21
24
  className,
22
25
  fileSidebarState,
26
+ handleCreateFile,
23
27
  }) => {
24
28
  const [sidebarOpen, setSidebarOpen] = fileSidebarState
29
+ const [newFileName, setNewFileName] = useState("")
30
+ const [isCreatingFile, setIsCreatingFile] = useState(false)
31
+ const [errorMessage, setErrorMessage] = useState("")
32
+
25
33
  const transformFilesToTreeData = (
26
34
  files: Record<FileName, string>,
27
35
  ): TreeDataItem[] => {
@@ -82,6 +90,17 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
82
90
  }
83
91
 
84
92
  const treeData = transformFilesToTreeData(files)
93
+
94
+ const handleCreateFileInline = () => {
95
+ handleCreateFile({
96
+ newFileName,
97
+ setErrorMessage,
98
+ onFileSelect,
99
+ setNewFileName,
100
+ setIsCreatingFile,
101
+ })
102
+ }
103
+
85
104
  return (
86
105
  <div
87
106
  className={cn(
@@ -98,6 +117,36 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
98
117
  >
99
118
  <PanelRightOpen />
100
119
  </button>
120
+ <button
121
+ onClick={() => setIsCreatingFile(true)}
122
+ className="absolute top-2 right-2 text-gray-400 hover:text-gray-600"
123
+ aria-label="Create new file"
124
+ >
125
+ <Plus className="w-5 h-5" />
126
+ </button>
127
+ {isCreatingFile && (
128
+ <div className="p-2">
129
+ <Input
130
+ autoFocus
131
+ value={newFileName}
132
+ onChange={(e) => setNewFileName(e.target.value)}
133
+ onBlur={handleCreateFileInline}
134
+ onKeyDown={(e) => {
135
+ if (e.key === "Enter") {
136
+ handleCreateFileInline()
137
+ } else if (e.key === "Escape") {
138
+ setIsCreatingFile(false)
139
+ setNewFileName("")
140
+ setErrorMessage("")
141
+ }
142
+ }}
143
+ placeholder="Enter file name"
144
+ />
145
+ {errorMessage && (
146
+ <div className="text-red-500 mt-1">{errorMessage}</div>
147
+ )}
148
+ </div>
149
+ )}
101
150
  <TreeView
102
151
  data={treeData}
103
152
  initialSelectedItemId={currentFile}
@@ -29,15 +29,26 @@ const SearchButtonComponent = () => {
29
29
  </Button> */}
30
30
  </div>
31
31
  ) : (
32
- <Button
33
- variant="ghost"
34
- size="icon"
35
- onClick={() => setIsExpanded(true)}
36
- className="h-8 w-8"
37
- aria-label="Open search"
38
- >
39
- <Search className="h-4 w-4" />
40
- </Button>
32
+ <>
33
+ <Button
34
+ variant="ghost"
35
+ size="icon"
36
+ onClick={() => setIsExpanded(true)}
37
+ className="h-8 w-8 hidden sm:flex"
38
+ aria-label="Open search"
39
+ >
40
+ <Search className="size-4" />
41
+ </Button>
42
+ <Button
43
+ variant="ghost"
44
+ size="icon"
45
+ onClick={() => (window.location.href = "/search")}
46
+ className="h-8 w-8 flex sm:hidden"
47
+ aria-label="Go to search"
48
+ >
49
+ <Search className="size-4" />
50
+ </Button>
51
+ </>
41
52
  )}
42
53
  </div>
43
54
  )
@@ -27,9 +27,8 @@ export function JLCPCBImportDialog({
27
27
  const [partNumber, setPartNumber] = useState("")
28
28
  const [isLoading, setIsLoading] = useState(false)
29
29
  const [error, setError] = useState<string | null>(null)
30
- const [alreadyImportedPackageId, setAlreadyImportedPackageId] = useState<
31
- string | null
32
- >(null)
30
+ const [hasBeenImportedToAccountAlready, setHasBeenImportedToAccountAlready] =
31
+ useState<boolean>(false)
33
32
  const axios = useAxios()
34
33
  const { toast } = useToast()
35
34
  const [, navigate] = useLocation()
@@ -48,19 +47,17 @@ export function JLCPCBImportDialog({
48
47
 
49
48
  setIsLoading(true)
50
49
  setError(null)
51
- setAlreadyImportedPackageId(null)
50
+ setHasBeenImportedToAccountAlready(false)
52
51
 
53
52
  try {
54
- const existingPackageRes = await axios.get(
55
- `/snippets/get?owner_name=${session?.github_username}&unscoped_name=${partNumber}`,
56
- {
57
- validateStatus: (status) => true,
58
- },
59
- )
53
+ const apiUrl = `/snippets/get?owner_name=${session?.github_username}&unscoped_name=${partNumber}`
54
+
55
+ const existingPackageRes = await axios.get(apiUrl, {
56
+ validateStatus: (status) => true,
57
+ })
60
58
 
61
59
  if (existingPackageRes.status !== 404) {
62
- const packageId = existingPackageRes.data.snippet.snippet_id
63
- setAlreadyImportedPackageId(packageId)
60
+ setHasBeenImportedToAccountAlready(true)
64
61
  setIsLoading(false)
65
62
  return
66
63
  }
@@ -122,14 +119,14 @@ export function JLCPCBImportDialog({
122
119
  onChange={(e) => {
123
120
  setPartNumber(e.target.value)
124
121
  setError(null)
125
- setAlreadyImportedPackageId(null)
122
+ setHasBeenImportedToAccountAlready(false)
126
123
  }}
127
124
  />
128
- {error && !alreadyImportedPackageId && (
125
+ {error && !hasBeenImportedToAccountAlready && (
129
126
  <p className="bg-red-100 p-2 mt-2 pre-wrap">{error}</p>
130
127
  )}
131
128
 
132
- {error && !alreadyImportedPackageId && (
129
+ {error && !hasBeenImportedToAccountAlready && (
133
130
  <div className="flex justify-end mt-2">
134
131
  <Button
135
132
  variant="default"
@@ -150,7 +147,7 @@ export function JLCPCBImportDialog({
150
147
  </div>
151
148
  )}
152
149
 
153
- {alreadyImportedPackageId && (
150
+ {hasBeenImportedToAccountAlready && (
154
151
  <p className="p-2 mt-2 pre-wrap text-md text-green-600">
155
152
  This part number has already been imported to your profile.{" "}
156
153
  <PrefetchPageLink
@@ -65,7 +65,7 @@ export default function ImportantFilesView({
65
65
  setActiveFilePath(importantFiles[0].file_path)
66
66
  setActiveTab("file")
67
67
  }
68
- }, [aiDescription, aiUsageInstructions, hasAiContent])
68
+ }, [aiDescription, aiUsageInstructions, hasAiContent, importantFiles])
69
69
 
70
70
  // Get file name from path
71
71
  const getFileName = (path: string) => {
@@ -211,6 +211,7 @@ const MobileSidebar = ({
211
211
  packageAuthor={packageInfo.owner_github_username}
212
212
  onUpdate={handlePackageUpdate}
213
213
  packageName={packageInfo.name}
214
+ currentDefaultView={packageInfo.default_view}
214
215
  />
215
216
  )}
216
217
  </div>
@@ -1,7 +1,5 @@
1
- import React, { useEffect } from "react"
1
+ import { useEffect } from "react"
2
2
  import { Link } from "wouter"
3
-
4
- import { TypeBadge } from "@/components/TypeBadge"
5
3
  import { Button } from "@/components/ui/button"
6
4
  import { Skeleton } from "@/components/ui/skeleton"
7
5
  import {
@@ -10,7 +8,7 @@ import {
10
8
  TooltipProvider,
11
9
  TooltipTrigger,
12
10
  } from "@/components/ui/tooltip"
13
- import { LockClosedIcon } from "@radix-ui/react-icons"
11
+ import { Lock, Globe } from "lucide-react"
14
12
  import { GitFork, Package, Star } from "lucide-react"
15
13
 
16
14
  import { useForkPackageMutation } from "@/hooks/use-fork-package-mutation"
@@ -45,6 +43,7 @@ export default function PackageHeader({
45
43
  const { OrderDialog, isOpen, open, close, stage, setStage } = useOrderDialog({
46
44
  onSignIn: signIn,
47
45
  isLoggedIn,
46
+ packageReleaseId: packageInfo?.latest_package_release_id ?? "",
48
47
  })
49
48
  const { data: starData, isLoading: isStarDataLoading } =
50
49
  usePackageStarsByName(packageInfo?.name ?? null)
@@ -105,13 +104,25 @@ export default function PackageHeader({
105
104
  {packageName}
106
105
  </Link>
107
106
  </h1>
108
- {packageInfo?.name && <TypeBadge type="package" />}
109
- {isPrivate && (
110
- <div className="relative group pl-2">
111
- <LockClosedIcon className="h-4 w-4 text-gray-700" />
112
- <span className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-1 hidden group-hover:block bg-black text-white text-xs rounded py-1 px-2">
113
- private
114
- </span>
107
+ {packageInfo?.name && (
108
+ <div
109
+ className={`select-none inline-flex items-center px-2 py-1 rounded text-xs font-medium ${
110
+ isPrivate
111
+ ? "bg-gray-100 text-gray-700 border border-gray-200"
112
+ : "bg-blue-50 text-blue-700 border border-blue-200"
113
+ }`}
114
+ >
115
+ {isPrivate ? (
116
+ <>
117
+ <Lock className="w-3 h-3 mr-1 flex-shrink-0" />
118
+ <span className="leading-none">Private</span>
119
+ </>
120
+ ) : (
121
+ <>
122
+ <Globe className="w-3 h-3 mr-1 flex-shrink-0" />
123
+ <span className="leading-none">Public</span>
124
+ </>
125
+ )}
115
126
  </div>
116
127
  )}
117
128
  </>
@@ -259,7 +270,6 @@ export default function PackageHeader({
259
270
  onClose={close}
260
271
  stage={stage}
261
272
  setStage={setStage}
262
- packageReleaseId={packageInfo?.latest_package_release_id ?? ""}
263
273
  />
264
274
  </header>
265
275
  )