@tscircuit/fake-snippets 0.0.108 → 0.0.109

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 (57) hide show
  1. package/bun.lock +62 -19
  2. package/dist/bundle.js +3 -2
  3. package/dist/index.d.ts +5 -0
  4. package/dist/index.js +2 -1
  5. package/dist/schema.d.ts +8 -0
  6. package/dist/schema.js +2 -1
  7. package/fake-snippets-api/lib/db/schema.ts +1 -0
  8. package/package.json +7 -8
  9. package/src/App.tsx +0 -2
  10. package/src/components/DownloadButtonAndMenu.tsx +133 -35
  11. package/src/components/FileSidebar.tsx +31 -34
  12. package/src/components/Footer.tsx +0 -1
  13. package/src/components/HeaderLogin.tsx +1 -1
  14. package/src/components/HiddenFilesDropdown.tsx +0 -2
  15. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -2
  16. package/src/components/PackageBuildsPage/build-preview-content.tsx +34 -5
  17. package/src/components/PackageCard.tsx +0 -1
  18. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +20 -11
  19. package/src/components/ViewPackagePage/components/important-files-view.tsx +75 -59
  20. package/src/components/ViewPackagePage/components/main-content-header.tsx +4 -4
  21. package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +0 -1
  22. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +0 -1
  23. package/src/components/ViewPackagePage/components/package-header.tsx +14 -17
  24. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +0 -1
  25. package/src/components/ViewPackagePage/components/repo-page-content.tsx +21 -20
  26. package/src/components/ViewPackagePage/components/sidebar.tsx +0 -2
  27. package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +18 -17
  28. package/src/components/ViewPackagePage/components/theme-toggle.tsx +0 -2
  29. package/src/components/ViewPackagePage/hooks/use-toast.tsx +0 -1
  30. package/src/components/package-port/CodeAndPreview.tsx +23 -40
  31. package/src/components/package-port/CodeEditor.tsx +24 -1
  32. package/src/components/package-port/CodeEditorHeader.tsx +5 -2
  33. package/src/components/preview/PackageReleasesDashboard.tsx +30 -25
  34. package/src/hooks/use-current-package-id.ts +5 -30
  35. package/src/hooks/use-current-package-info.ts +29 -5
  36. package/src/hooks/use-global-store.ts +1 -1
  37. package/src/hooks/useFileManagement.ts +153 -34
  38. package/src/hooks/useOptimizedPackageFilesLoader.ts +149 -0
  39. package/src/hooks/useUpdatePackageFilesMutation.ts +2 -0
  40. package/src/lib/download-fns/download-circuit-png.ts +11 -3
  41. package/src/lib/download-fns/download-gltf-from-circuit-json.ts +44 -0
  42. package/src/lib/utils/isComponentExported.ts +9 -0
  43. package/src/pages/authorize.tsx +0 -2
  44. package/src/pages/landing.tsx +0 -1
  45. package/src/pages/preview-release.tsx +14 -4
  46. package/src/pages/view-package.tsx +14 -13
  47. package/src/components/Footer2.tsx +0 -100
  48. package/src/components/ShippingInformationForm.tsx +0 -423
  49. package/src/components/StaticViewSnippetHeader.tsx +0 -70
  50. package/src/components/ViewPackagePage/components/file-explorer.tsx +0 -67
  51. package/src/components/ViewPackagePage/components/readme-view.tsx +0 -58
  52. package/src/components/ViewPackagePage/components/repo-header-button.tsx +0 -36
  53. package/src/components/ViewPackagePage/components/repo-header.tsx +0 -4
  54. package/src/components/ViewPackagePage/components/sidebar-contributors-section.tsx +0 -31
  55. package/src/components/ViewSnippetHeader.tsx +0 -181
  56. package/src/components/ui/input-otp.tsx +0 -69
  57. package/src/pages/settings.tsx +0 -25
package/dist/index.d.ts CHANGED
@@ -667,6 +667,7 @@ declare const packageSchema: z.ZodObject<{
667
667
  latest_package_release_fs_sha: z.ZodDefault<z.ZodNullable<z.ZodString>>;
668
668
  default_view: z.ZodOptional<z.ZodDefault<z.ZodEnum<["files", "3d", "pcb", "schematic"]>>>;
669
669
  allow_pr_previews: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
670
+ is_starred: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
670
671
  }, "strip", z.ZodTypeAny, {
671
672
  name: string;
672
673
  unscoped_name: string;
@@ -695,6 +696,7 @@ declare const packageSchema: z.ZodObject<{
695
696
  website: string | null;
696
697
  ai_usage_instructions: string | null;
697
698
  latest_package_release_fs_sha: string | null;
699
+ is_starred?: boolean | undefined;
698
700
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
699
701
  latest_license?: string | null | undefined;
700
702
  default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
@@ -715,6 +717,7 @@ declare const packageSchema: z.ZodObject<{
715
717
  latest_package_release_id: string | null;
716
718
  latest_version: string | null;
717
719
  ai_usage_instructions: string | null;
720
+ is_starred?: boolean | undefined;
718
721
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
719
722
  star_count?: number | undefined;
720
723
  is_private?: boolean | null | undefined;
@@ -1036,6 +1039,7 @@ declare const createDatabase: ({ seed }?: {
1036
1039
  website: string | null;
1037
1040
  ai_usage_instructions: string | null;
1038
1041
  latest_package_release_fs_sha: string | null;
1042
+ is_starred?: boolean | undefined;
1039
1043
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
1040
1044
  latest_license?: string | null | undefined;
1041
1045
  default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
@@ -1441,6 +1445,7 @@ declare const createDatabase: ({ seed }?: {
1441
1445
  website: string | null;
1442
1446
  ai_usage_instructions: string | null;
1443
1447
  latest_package_release_fs_sha: string | null;
1448
+ is_starred?: boolean | undefined;
1444
1449
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
1445
1450
  latest_license?: string | null | undefined;
1446
1451
  default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
package/dist/index.js CHANGED
@@ -260,7 +260,8 @@ var packageSchema = z.object({
260
260
  ai_usage_instructions: z.string().nullable(),
261
261
  latest_package_release_fs_sha: z.string().nullable().default(null),
262
262
  default_view: z.enum(["files", "3d", "pcb", "schematic"]).default("files").optional(),
263
- allow_pr_previews: z.boolean().default(false).optional()
263
+ allow_pr_previews: z.boolean().default(false).optional(),
264
+ is_starred: z.boolean().default(false).optional()
264
265
  });
265
266
  var jlcpcbOrderStateSchema = z.object({
266
267
  jlcpcb_order_state_id: z.string(),
package/dist/schema.d.ts CHANGED
@@ -840,6 +840,7 @@ declare const packageSchema: z.ZodObject<{
840
840
  latest_package_release_fs_sha: z.ZodDefault<z.ZodNullable<z.ZodString>>;
841
841
  default_view: z.ZodOptional<z.ZodDefault<z.ZodEnum<["files", "3d", "pcb", "schematic"]>>>;
842
842
  allow_pr_previews: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
843
+ is_starred: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
843
844
  }, "strip", z.ZodTypeAny, {
844
845
  name: string;
845
846
  unscoped_name: string;
@@ -868,6 +869,7 @@ declare const packageSchema: z.ZodObject<{
868
869
  website: string | null;
869
870
  ai_usage_instructions: string | null;
870
871
  latest_package_release_fs_sha: string | null;
872
+ is_starred?: boolean | undefined;
871
873
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
872
874
  latest_license?: string | null | undefined;
873
875
  default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
@@ -888,6 +890,7 @@ declare const packageSchema: z.ZodObject<{
888
890
  latest_package_release_id: string | null;
889
891
  latest_version: string | null;
890
892
  ai_usage_instructions: string | null;
893
+ is_starred?: boolean | undefined;
891
894
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
892
895
  star_count?: number | undefined;
893
896
  is_private?: boolean | null | undefined;
@@ -1430,6 +1433,7 @@ declare const databaseSchema: z.ZodObject<{
1430
1433
  latest_package_release_fs_sha: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1431
1434
  default_view: z.ZodOptional<z.ZodDefault<z.ZodEnum<["files", "3d", "pcb", "schematic"]>>>;
1432
1435
  allow_pr_previews: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
1436
+ is_starred: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
1433
1437
  }, "strip", z.ZodTypeAny, {
1434
1438
  name: string;
1435
1439
  unscoped_name: string;
@@ -1458,6 +1462,7 @@ declare const databaseSchema: z.ZodObject<{
1458
1462
  website: string | null;
1459
1463
  ai_usage_instructions: string | null;
1460
1464
  latest_package_release_fs_sha: string | null;
1465
+ is_starred?: boolean | undefined;
1461
1466
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
1462
1467
  latest_license?: string | null | undefined;
1463
1468
  default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
@@ -1478,6 +1483,7 @@ declare const databaseSchema: z.ZodObject<{
1478
1483
  latest_package_release_id: string | null;
1479
1484
  latest_version: string | null;
1480
1485
  ai_usage_instructions: string | null;
1486
+ is_starred?: boolean | undefined;
1481
1487
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
1482
1488
  star_count?: number | undefined;
1483
1489
  is_private?: boolean | null | undefined;
@@ -2138,6 +2144,7 @@ declare const databaseSchema: z.ZodObject<{
2138
2144
  website: string | null;
2139
2145
  ai_usage_instructions: string | null;
2140
2146
  latest_package_release_fs_sha: string | null;
2147
+ is_starred?: boolean | undefined;
2141
2148
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
2142
2149
  latest_license?: string | null | undefined;
2143
2150
  default_view?: "files" | "3d" | "pcb" | "schematic" | undefined;
@@ -2428,6 +2435,7 @@ declare const databaseSchema: z.ZodObject<{
2428
2435
  latest_package_release_id: string | null;
2429
2436
  latest_version: string | null;
2430
2437
  ai_usage_instructions: string | null;
2438
+ is_starred?: boolean | undefined;
2431
2439
  snippet_type?: "board" | "package" | "model" | "footprint" | undefined;
2432
2440
  star_count?: number | undefined;
2433
2441
  is_private?: boolean | null | undefined;
package/dist/schema.js CHANGED
@@ -255,7 +255,8 @@ var packageSchema = z.object({
255
255
  ai_usage_instructions: z.string().nullable(),
256
256
  latest_package_release_fs_sha: z.string().nullable().default(null),
257
257
  default_view: z.enum(["files", "3d", "pcb", "schematic"]).default("files").optional(),
258
- allow_pr_previews: z.boolean().default(false).optional()
258
+ allow_pr_previews: z.boolean().default(false).optional(),
259
+ is_starred: z.boolean().default(false).optional()
259
260
  });
260
261
  var jlcpcbOrderStateSchema = z.object({
261
262
  jlcpcb_order_state_id: z.string(),
@@ -311,6 +311,7 @@ export const packageSchema = z.object({
311
311
  .default("files")
312
312
  .optional(),
313
313
  allow_pr_previews: z.boolean().default(false).optional(),
314
+ is_starred: z.boolean().default(false).optional(),
314
315
  })
315
316
  export type Package = z.infer<typeof packageSchema>
316
317
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.108",
3
+ "version": "0.0.109",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -75,6 +75,7 @@
75
75
  "@radix-ui/react-toggle": "^1.1.0",
76
76
  "@radix-ui/react-toggle-group": "^1.1.0",
77
77
  "@radix-ui/react-tooltip": "^1.1.2",
78
+ "@resvg/resvg-wasm": "^2.6.2",
78
79
  "@tailwindcss/typography": "^0.5.16",
79
80
  "@tscircuit/3d-viewer": "^0.0.303",
80
81
  "@tscircuit/assembly-viewer": "^0.0.1",
@@ -84,8 +85,9 @@
84
85
  "@tscircuit/mm": "^0.0.8",
85
86
  "@tscircuit/pcb-viewer": "^1.11.194",
86
87
  "@tscircuit/prompt-benchmarks": "^0.0.28",
87
- "@tscircuit/runframe": "0.0.764",
88
+ "@tscircuit/runframe": "^0.0.781",
88
89
  "@tscircuit/schematic-viewer": "^2.0.21",
90
+ "@tscircuit/simple-3d-svg": "^0.0.41",
89
91
  "@types/babel__standalone": "^7.1.7",
90
92
  "@types/bun": "^1.1.10",
91
93
  "@types/country-list": "^2.1.4",
@@ -93,7 +95,6 @@
93
95
  "@types/md5": "^2.3.5",
94
96
  "@types/ms": "^0.7.34",
95
97
  "@types/node": "^22.13.0",
96
- "@types/prismjs": "^1.26.4",
97
98
  "@types/react": "^18.3.9",
98
99
  "@types/react-dom": "^18.3.0",
99
100
  "@types/react-helmet": "^6.1.11",
@@ -104,11 +105,12 @@
104
105
  "@vercel/analytics": "^1.4.1",
105
106
  "@vitejs/plugin-react": "^4.3.1",
106
107
  "autoprefixer": "^10.4.20",
107
- "change-case": "^5.4.4",
108
108
  "circuit-json-to-bom-csv": "^0.0.7",
109
109
  "circuit-json-to-gerber": "^0.0.29",
110
+ "circuit-json-to-gltf": "^0.0.4",
110
111
  "circuit-json-to-pnp-csv": "^0.0.7",
111
112
  "circuit-json-to-readable-netlist": "^0.0.13",
113
+ "circuit-json-to-simple-3d": "^0.0.6",
112
114
  "circuit-json-to-spice": "^0.0.6",
113
115
  "circuit-json-to-tscircuit": "^0.0.4",
114
116
  "circuit-to-svg": "^0.0.167",
@@ -117,8 +119,6 @@
117
119
  "cmdk": "^1.0.4",
118
120
  "codemirror": "^6.0.1",
119
121
  "codemirror-copilot": "^0.0.7",
120
- "country-list": "^2.3.0",
121
- "date-fns": "^4.1.0",
122
122
  "dotenv": "^16.5.0",
123
123
  "dsn-converter": "^0.0.60",
124
124
  "easyeda": "^0.0.203",
@@ -131,7 +131,6 @@
131
131
  "he": "^1.2.0",
132
132
  "idb-keyval": "^6.2.2",
133
133
  "immer": "^10.1.1",
134
- "input-otp": "^1.2.4",
135
134
  "javascript-time-ago": "^2.5.11",
136
135
  "jose": "^5.9.3",
137
136
  "jscad-electronics": "^0.0.25",
@@ -147,7 +146,6 @@
147
146
  "openai": "^5.6.0",
148
147
  "postcss": "^8.4.47",
149
148
  "posthog-js": "^1.203.2",
150
- "prismjs": "^1.29.0",
151
149
  "prompts": "^2.4.2",
152
150
  "react": "^18.3.1",
153
151
  "react-cookie-consent": "^9.0.0",
@@ -172,6 +170,7 @@
172
170
  "sitemap": "^8.0.0",
173
171
  "sonner": "^1.5.0",
174
172
  "states-us": "^1.1.1",
173
+ "svgo": "^4.0.0",
175
174
  "tailwind-merge": "^2.5.2",
176
175
  "tailwindcss": "^3.4.13",
177
176
  "tailwindcss-animate": "^1.0.7",
package/src/App.tsx CHANGED
@@ -64,7 +64,6 @@ const MyOrdersPage = lazyImport(() => import("@/pages/my-orders"))
64
64
  const LatestPage = lazyImport(() => import("@/pages/latest"))
65
65
  const QuickstartPage = lazyImport(() => import("@/pages/quickstart"))
66
66
  const SearchPage = lazyImport(() => import("@/pages/search"))
67
- const SettingsPage = lazyImport(() => import("@/pages/settings"))
68
67
  const UserProfilePage = lazyImport(() => import("@/pages/user-profile"))
69
68
  const DevLoginPage = lazyImport(() => import("@/pages/dev-login"))
70
69
  const ViewPackagePage = lazyImport(() => import("@/pages/view-package"))
@@ -252,7 +251,6 @@ function App() {
252
251
  <Route path="/quickstart" component={QuickstartPage} />
253
252
  <Route path="/dashboard" component={DashboardPage} />
254
253
  <Route path="/latest" component={LatestPage} />
255
- <Route path="/settings" component={SettingsPage} />
256
254
  <Route path="/search" component={SearchPage} />
257
255
  <Route path="/trending" component={TrendingPage} />
258
256
  <Route path="/datasheets" component={DatasheetsPage} />
@@ -18,18 +18,22 @@ import { usePcbDownloadDialog } from "@/components/dialogs/pcb-download-dialog"
18
18
  import { downloadKicadFiles } from "@/lib/download-fns/download-kicad-files"
19
19
  import { AnyCircuitElement } from "circuit-json"
20
20
  import { ChevronDown, Download, Hammer } from "lucide-react"
21
- import { downloadGltf } from "@/lib/download-fns/download-gltf"
21
+ import { downloadGltfFromCircuitJson } from "@/lib/download-fns/download-gltf-from-circuit-json"
22
22
  import { downloadPngImage } from "@/lib/download-fns/download-png-utils"
23
23
  import { ImageFormat } from "@/lib/download-fns/download-circuit-png"
24
24
  import { CubeIcon } from "@radix-ui/react-icons"
25
+ import { useState } from "react"
26
+ import { useAxios } from "@/hooks/use-axios"
27
+ import { useCurrentPackageId } from "@/hooks/use-current-package-id"
25
28
 
26
29
  interface DownloadButtonAndMenuProps {
27
30
  className?: string
28
31
  unscopedName?: string
29
32
  author?: string
30
- circuitJson?: AnyCircuitElement[] | null
33
+ hasCircuitJson?: boolean
31
34
  desiredImageType?: string
32
35
  offerMultipleImageFormats?: boolean
36
+ circuitJson?: AnyCircuitElement[] | null
33
37
  }
34
38
 
35
39
  export function DownloadButtonAndMenu({
@@ -37,14 +41,52 @@ export function DownloadButtonAndMenu({
37
41
  unscopedName,
38
42
  author,
39
43
  desiredImageType = "pcb",
40
- circuitJson,
44
+ hasCircuitJson,
41
45
  offerMultipleImageFormats = false,
46
+ circuitJson,
42
47
  }: DownloadButtonAndMenuProps) {
43
48
  const notImplemented = useNotImplementedToast()
44
49
  const { Dialog: PcbDownloadDialog, openDialog: openPcbDownloadDialog } =
45
50
  usePcbDownloadDialog()
51
+ const axios = useAxios()
52
+ const { packageId } = useCurrentPackageId()
53
+ const [fetchedCircuitJson, setFetchedCircuitJson] = useState<
54
+ AnyCircuitElement[] | null
55
+ >(null)
56
+ const [isFetchingCircuitJson, setIsFetchingCircuitJson] = useState(false)
57
+
58
+ const canDownload = Boolean(
59
+ hasCircuitJson || (circuitJson && circuitJson.length),
60
+ )
61
+
62
+ const getCircuitJson = async (): Promise<AnyCircuitElement[]> => {
63
+ if (circuitJson && circuitJson.length) return circuitJson
64
+ if (fetchedCircuitJson && fetchedCircuitJson.length)
65
+ return fetchedCircuitJson
66
+ setIsFetchingCircuitJson(true)
67
+ try {
68
+ const { data } = await axios.post("/package_files/get", {
69
+ package_id: packageId,
70
+ file_path: "dist/circuit.json",
71
+ })
72
+ const content = data?.package_file?.content_text
73
+ if (!content) throw new Error("Circuit JSON not found")
74
+ const parsed = JSON.parse(content)
75
+ setFetchedCircuitJson(parsed)
76
+ return parsed
77
+ } catch (error: any) {
78
+ toast({
79
+ title: "Failed to fetch Circuit JSON",
80
+ description: error?.message || error?.toString?.() || "Unknown error",
81
+ variant: "destructive",
82
+ })
83
+ throw error
84
+ } finally {
85
+ setIsFetchingCircuitJson(false)
86
+ }
87
+ }
46
88
 
47
- if (!circuitJson) {
89
+ if (!canDownload) {
48
90
  return (
49
91
  <div className={className}>
50
92
  <Button
@@ -76,11 +118,9 @@ export function DownloadButtonAndMenu({
76
118
  <DropdownMenuContent className="!z-[101]">
77
119
  <DropdownMenuItem
78
120
  className="text-xs"
79
- onSelect={() => {
80
- downloadCircuitJson(
81
- circuitJson,
82
- unscopedName || "circuit" + ".json",
83
- )
121
+ onSelect={async () => {
122
+ const cj = await getCircuitJson()
123
+ downloadCircuitJson(cj, unscopedName || "circuit" + ".json")
84
124
  }}
85
125
  >
86
126
  <Download className="mr-1 h-3 w-3" />
@@ -93,7 +133,12 @@ export function DownloadButtonAndMenu({
93
133
  className="text-xs"
94
134
  onClick={async () => {
95
135
  try {
96
- await downloadGltf(unscopedName || "circuit")
136
+ const cj = await getCircuitJson()
137
+ await downloadGltfFromCircuitJson(
138
+ cj,
139
+ unscopedName || "circuit",
140
+ { format: "glb", boardTextureResolution: 2048 },
141
+ )
97
142
  } catch (error: any) {
98
143
  toast({
99
144
  title: "Error Downloading 3D Model",
@@ -112,8 +157,34 @@ export function DownloadButtonAndMenu({
112
157
  <DropdownMenuItem
113
158
  className="text-xs"
114
159
  onClick={async () => {
160
+ try {
161
+ const cj = await getCircuitJson()
162
+ await downloadGltfFromCircuitJson(
163
+ cj,
164
+ unscopedName || "circuit",
165
+ { format: "gltf", boardTextureResolution: 2048 },
166
+ )
167
+ } catch (error: any) {
168
+ toast({
169
+ title: "Error Downloading 3D Model (GLTF)",
170
+ description: error.toString(),
171
+ variant: "destructive",
172
+ })
173
+ }
174
+ }}
175
+ >
176
+ <CubeIcon className="mr-1 h-3 w-3" />
177
+ <span className="flex-grow mr-6">3D Model</span>
178
+ <span className="text-[0.6rem] bg-green-500 opacity-80 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
179
+ gltf
180
+ </span>
181
+ </DropdownMenuItem>
182
+ <DropdownMenuItem
183
+ className="text-xs"
184
+ onClick={async () => {
185
+ const cj = await getCircuitJson()
115
186
  await downloadFabricationFiles({
116
- circuitJson,
187
+ circuitJson: cj,
117
188
  snippetUnscopedName: unscopedName || "snippet",
118
189
  }).catch((error) => {
119
190
  console.error(error)
@@ -144,8 +215,9 @@ export function DownloadButtonAndMenu({
144
215
  </DropdownMenuItem>
145
216
  <DropdownMenuItem
146
217
  className="text-xs"
147
- onSelect={() => {
148
- downloadKicadFiles(circuitJson, unscopedName || "kicad_project")
218
+ onSelect={async () => {
219
+ const cj = await getCircuitJson()
220
+ downloadKicadFiles(cj, unscopedName || "kicad_project")
149
221
  }}
150
222
  >
151
223
  <Download className="mr-1 h-3 w-3" />
@@ -157,8 +229,9 @@ export function DownloadButtonAndMenu({
157
229
 
158
230
  <DropdownMenuItem
159
231
  className="text-xs"
160
- onSelect={() => {
161
- downloadSchematicSvg(circuitJson, unscopedName || "circuit")
232
+ onSelect={async () => {
233
+ const cj = await getCircuitJson()
234
+ downloadSchematicSvg(cj, unscopedName || "circuit")
162
235
  }}
163
236
  >
164
237
  <Download className="mr-1 h-3 w-3" />
@@ -169,8 +242,9 @@ export function DownloadButtonAndMenu({
169
242
  </DropdownMenuItem>
170
243
  <DropdownMenuItem
171
244
  className="text-xs"
172
- onSelect={() => {
173
- downloadAssemblySvg(circuitJson, unscopedName || "circuit")
245
+ onSelect={async () => {
246
+ const cj = await getCircuitJson()
247
+ downloadAssemblySvg(cj, unscopedName || "circuit")
174
248
  }}
175
249
  >
176
250
  <Download className="mr-1 h-3 w-3" />
@@ -181,7 +255,9 @@ export function DownloadButtonAndMenu({
181
255
  </DropdownMenuItem>
182
256
  <DropdownMenuItem
183
257
  className="text-xs"
184
- onSelect={() => {
258
+ onSelect={async () => {
259
+ const cj = await getCircuitJson()
260
+ setFetchedCircuitJson(cj)
185
261
  openPcbDownloadDialog()
186
262
  }}
187
263
  >
@@ -193,8 +269,9 @@ export function DownloadButtonAndMenu({
193
269
  </DropdownMenuItem>
194
270
  <DropdownMenuItem
195
271
  className="text-xs"
196
- onSelect={() => {
197
- downloadDsnFile(circuitJson, unscopedName || "circuit")
272
+ onSelect={async () => {
273
+ const cj = await getCircuitJson()
274
+ downloadDsnFile(cj, unscopedName || "circuit")
198
275
  }}
199
276
  >
200
277
  <Download className="mr-1 h-3 w-3" />
@@ -205,8 +282,9 @@ export function DownloadButtonAndMenu({
205
282
  </DropdownMenuItem>
206
283
  <DropdownMenuItem
207
284
  className="text-xs"
208
- onClick={() => {
209
- downloadReadableNetlist(circuitJson, unscopedName || "circuit")
285
+ onClick={async () => {
286
+ const cj = await getCircuitJson()
287
+ downloadReadableNetlist(cj, unscopedName || "circuit")
210
288
  }}
211
289
  >
212
290
  <Download className="mr-1 h-3 w-3" />
@@ -217,8 +295,9 @@ export function DownloadButtonAndMenu({
217
295
  </DropdownMenuItem>
218
296
  <DropdownMenuItem
219
297
  className="text-xs"
220
- onSelect={() => {
221
- downloadSpiceFile(circuitJson, unscopedName || "circuit")
298
+ onSelect={async () => {
299
+ const cj = await getCircuitJson()
300
+ downloadSpiceFile(cj, unscopedName || "circuit")
222
301
  }}
223
302
  >
224
303
  <Download className="mr-1 h-3 w-3" />
@@ -229,8 +308,9 @@ export function DownloadButtonAndMenu({
229
308
  </DropdownMenuItem>
230
309
  <DropdownMenuItem
231
310
  className="text-xs"
232
- onSelect={() => {
233
- downloadSimpleRouteJson(circuitJson, unscopedName || "circuit")
311
+ onSelect={async () => {
312
+ const cj = await getCircuitJson()
313
+ downloadSimpleRouteJson(cj, unscopedName || "circuit")
234
314
  }}
235
315
  >
236
316
  <Download className="mr-1 h-3 w-3" />
@@ -243,7 +323,7 @@ export function DownloadButtonAndMenu({
243
323
  {!offerMultipleImageFormats && (
244
324
  <DropdownMenuItem
245
325
  className="text-xs"
246
- onClick={() => {
326
+ onClick={async () => {
247
327
  const desiredImageFormat = [
248
328
  "pcb",
249
329
  "schematic",
@@ -252,8 +332,9 @@ export function DownloadButtonAndMenu({
252
332
  ].includes(desiredImageType)
253
333
  ? desiredImageType
254
334
  : "pcb"
335
+ const cj = await getCircuitJson()
255
336
  downloadPngImage({
256
- circuitJson,
337
+ circuitJson: cj,
257
338
  unscopedName,
258
339
  author,
259
340
  format: desiredImageFormat as ImageFormat,
@@ -271,9 +352,9 @@ export function DownloadButtonAndMenu({
271
352
  <>
272
353
  <DropdownMenuItem
273
354
  className="text-xs"
274
- onClick={() =>
355
+ onClick={async () =>
275
356
  downloadPngImage({
276
- circuitJson,
357
+ circuitJson: await getCircuitJson(),
277
358
  unscopedName,
278
359
  author,
279
360
  format: "schematic",
@@ -288,9 +369,9 @@ export function DownloadButtonAndMenu({
288
369
  </DropdownMenuItem>
289
370
  <DropdownMenuItem
290
371
  className="text-xs"
291
- onClick={() =>
372
+ onClick={async () =>
292
373
  downloadPngImage({
293
- circuitJson,
374
+ circuitJson: await getCircuitJson(),
294
375
  unscopedName,
295
376
  author,
296
377
  format: "pcb",
@@ -305,9 +386,9 @@ export function DownloadButtonAndMenu({
305
386
  </DropdownMenuItem>
306
387
  <DropdownMenuItem
307
388
  className="text-xs"
308
- onClick={() =>
389
+ onClick={async () =>
309
390
  downloadPngImage({
310
- circuitJson,
391
+ circuitJson: await getCircuitJson(),
311
392
  unscopedName,
312
393
  author,
313
394
  format: "assembly",
@@ -320,12 +401,29 @@ export function DownloadButtonAndMenu({
320
401
  png
321
402
  </span>
322
403
  </DropdownMenuItem>
404
+ <DropdownMenuItem
405
+ className="text-xs"
406
+ onClick={async () =>
407
+ downloadPngImage({
408
+ circuitJson: await getCircuitJson(),
409
+ unscopedName,
410
+ author,
411
+ format: "3d",
412
+ })
413
+ }
414
+ >
415
+ <Download className="mr-1 h-3 w-3" />
416
+ <span className="flex-grow mr-6">3D PNG</span>
417
+ <span className="text-[0.6rem] opacity-80 bg-teal-600 text-white font-mono rounded-md px-1 text-center py-0.5 mr-1">
418
+ png
419
+ </span>
420
+ </DropdownMenuItem>
323
421
  </>
324
422
  )}
325
423
  </DropdownMenuContent>
326
424
  </DropdownMenu>
327
425
  <PcbDownloadDialog
328
- circuitJson={circuitJson}
426
+ circuitJson={fetchedCircuitJson || circuitJson || []}
329
427
  fileName={unscopedName || "circuit"}
330
428
  />
331
429
  </div>
@@ -1,25 +1,9 @@
1
1
  import React, { useState } from "react"
2
2
  import { cn } from "@/lib/utils"
3
- import {
4
- File,
5
- Folder,
6
- MoreVertical,
7
- PanelRightOpen,
8
- Plus,
9
- Trash2,
10
- Pencil,
11
- } from "lucide-react"
12
- import { TreeView, TreeDataItem } from "@/components/ui/tree-view"
13
- import { isHiddenFile } from "./ViewPackagePage/utils/is-hidden-file"
3
+ import { PanelRightOpen, Plus, Loader2 } from "lucide-react"
4
+ import { TreeView } from "@/components/ui/tree-view"
14
5
  import { Input } from "@/components/ui/input"
15
6
  import { transformFilesToTreeData } from "@/lib/utils/transformFilesToTreeData"
16
- import {
17
- DropdownMenu,
18
- DropdownMenuContent,
19
- DropdownMenuGroup,
20
- DropdownMenuItem,
21
- DropdownMenuTrigger,
22
- } from "./ui/dropdown-menu"
23
7
  import type {
24
8
  ICreateFileProps,
25
9
  ICreateFileResult,
@@ -28,8 +12,6 @@ import type {
28
12
  IRenameFileProps,
29
13
  IRenameFileResult,
30
14
  } from "@/hooks/useFileManagement"
31
- import { useToast } from "@/hooks/use-toast"
32
- import { useGlobalStore } from "@/hooks/use-global-store"
33
15
  import type { Package } from "fake-snippets-api/lib/db/schema"
34
16
  type FileName = string
35
17
 
@@ -45,6 +27,8 @@ interface FileSidebarProps {
45
27
  isCreatingFile: boolean
46
28
  setIsCreatingFile: React.Dispatch<React.SetStateAction<boolean>>
47
29
  pkg?: Package
30
+ isLoadingFiles?: boolean
31
+ loadingProgress?: string | null
48
32
  }
49
33
 
50
34
  const FileSidebar: React.FC<FileSidebarProps> = ({
@@ -58,6 +42,8 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
58
42
  handleRenameFile,
59
43
  isCreatingFile,
60
44
  setIsCreatingFile,
45
+ isLoadingFiles = true,
46
+ loadingProgress = null,
61
47
  }) => {
62
48
  const [sidebarOpen, setSidebarOpen] = fileSidebarState
63
49
  const [newFileName, setNewFileName] = useState("")
@@ -69,7 +55,6 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
69
55
  const [selectedItemId, setSelectedItemId] = React.useState<string>(
70
56
  currentFile || "",
71
57
  )
72
- const { toast } = useToast()
73
58
  const canModifyFiles = true
74
59
 
75
60
  const onFolderSelect = (folderPath: string) => {
@@ -184,19 +169,31 @@ const FileSidebar: React.FC<FileSidebarProps> = ({
184
169
  className,
185
170
  )}
186
171
  >
187
- <button
188
- onClick={toggleSidebar}
189
- className={`z-[99] mt-2 ml-2 text-gray-400 scale-90 transition-opacity duration-200 ${!sidebarOpen ? "opacity-0 pointer-events-none" : "opacity-100"}`}
190
- >
191
- <PanelRightOpen />
192
- </button>
193
- <button
194
- onClick={() => setIsCreatingFile(true)}
195
- className="absolute top-2 right-2 text-gray-400 hover:text-gray-600"
196
- aria-label="Create new file"
197
- >
198
- <Plus className="w-5 h-5" />
199
- </button>
172
+ <div className="flex items-center justify-between px-2 pt-2">
173
+ <button
174
+ onClick={toggleSidebar}
175
+ className={`text-gray-400 scale-90 transition-opacity duration-200 ${!sidebarOpen ? "opacity-0 pointer-events-none" : "opacity-100"}`}
176
+ >
177
+ <PanelRightOpen />
178
+ </button>
179
+ <div className="flex items-center gap-2">
180
+ {isLoadingFiles && (
181
+ <div className="flex items-center gap-1">
182
+ <Loader2 className="w-3 h-3 animate-spin text-gray-400" />
183
+ {loadingProgress && (
184
+ <span className="text-xs text-gray-400">{loadingProgress}</span>
185
+ )}
186
+ </div>
187
+ )}
188
+ <button
189
+ onClick={() => setIsCreatingFile(true)}
190
+ className="text-gray-400 hover:text-gray-600"
191
+ aria-label="Create new file"
192
+ >
193
+ <Plus className="w-5 h-5" />
194
+ </button>
195
+ </div>
196
+ </div>
200
197
  {isCreatingFile && (
201
198
  <div className="p-2">
202
199
  <Input
@@ -31,7 +31,6 @@ export default function Footer() {
31
31
  href: `/${session?.github_username}`,
32
32
  hidden: !isLoggedIn,
33
33
  },
34
- { name: "Settings", href: "/settings" },
35
34
  ]
36
35
  .filter((item) => !item.hidden)
37
36
  .map((item) => (