@tscircuit/fake-snippets 0.0.108 → 0.0.110

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 (203) hide show
  1. package/.github/workflows/bun-formatcheck.yml +2 -2
  2. package/.github/workflows/bun-pver-release.yml +3 -3
  3. package/.github/workflows/bun-test.yml +1 -1
  4. package/.github/workflows/bun-typecheck.yml +2 -2
  5. package/.github/workflows/update-snapshots.yml +1 -1
  6. package/README.md +4 -0
  7. package/api/generated-index.js +37 -3
  8. package/biome.json +2 -1
  9. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +31 -3
  10. package/bun-tests/fake-snippets-api/fixtures/preload.ts +18 -0
  11. package/bun-tests/fake-snippets-api/routes/orgs/add_member.test.ts +26 -0
  12. package/bun-tests/fake-snippets-api/routes/orgs/create.test.ts +37 -0
  13. package/bun-tests/fake-snippets-api/routes/orgs/get.test.ts +52 -0
  14. package/bun-tests/fake-snippets-api/routes/orgs/list.test.ts +17 -0
  15. package/bun-tests/fake-snippets-api/routes/orgs/list_members.test.ts +23 -0
  16. package/bun-tests/fake-snippets-api/routes/orgs/remove_member.test.ts +81 -0
  17. package/bun-tests/fake-snippets-api/routes/orgs/update.test.ts +99 -0
  18. package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +1 -1
  19. package/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +15 -13
  20. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -24
  21. package/bun-tests/fake-snippets-api/routes/package_files/delete.test.ts +9 -9
  22. package/bun-tests/fake-snippets-api/routes/package_files/download.test.ts +4 -4
  23. package/bun-tests/fake-snippets-api/routes/package_files/get.test.ts +38 -28
  24. package/bun-tests/fake-snippets-api/routes/package_files/list.test.ts +23 -15
  25. package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +33 -0
  26. package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +4 -4
  27. package/bun-tests/fake-snippets-api/routes/package_releases/get_image_generation_fields.test.ts +38 -0
  28. package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +19 -0
  29. package/bun-tests/fake-snippets-api/routes/packages/fork.test.ts +3 -4
  30. package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +30 -0
  31. package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +4 -2
  32. package/bun-tests/fake-snippets-api/routes/packages/list-1.test.ts +34 -0
  33. package/bun.lock +389 -450
  34. package/bunfig.toml +2 -1
  35. package/dist/bundle.js +1255 -625
  36. package/dist/index.d.ts +296 -4
  37. package/dist/index.js +325 -24
  38. package/dist/schema.d.ts +282 -1
  39. package/dist/schema.js +54 -2
  40. package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
  41. package/fake-snippets-api/lib/db/db-client.ts +214 -3
  42. package/fake-snippets-api/lib/db/schema.ts +62 -0
  43. package/fake-snippets-api/lib/db/seed.ts +100 -0
  44. package/fake-snippets-api/lib/middleware/with-session-auth.ts +1 -1
  45. package/fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor.ts +2 -2
  46. package/fake-snippets-api/lib/public-mapping/public-map-org.ts +32 -0
  47. package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +10 -0
  48. package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +17 -0
  49. package/fake-snippets-api/routes/api/orgs/add_member.ts +52 -0
  50. package/fake-snippets-api/routes/api/orgs/create.ts +46 -0
  51. package/fake-snippets-api/routes/api/orgs/get.ts +39 -0
  52. package/fake-snippets-api/routes/api/orgs/list.ts +31 -0
  53. package/fake-snippets-api/routes/api/orgs/list_members.ts +67 -0
  54. package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
  55. package/fake-snippets-api/routes/api/orgs/update.ts +93 -0
  56. package/fake-snippets-api/routes/api/package_files/get.ts +3 -6
  57. package/fake-snippets-api/routes/api/package_files/list.ts +7 -4
  58. package/fake-snippets-api/routes/api/packages/create.ts +54 -10
  59. package/fake-snippets-api/routes/api/packages/get.ts +23 -0
  60. package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +13 -11
  61. package/fake-snippets-api/routes/api/packages/list.ts +29 -2
  62. package/fake-snippets-api/routes/api/packages/update_ai_description.ts +37 -0
  63. package/package.json +27 -24
  64. package/renovate.json +1 -1
  65. package/scripts/generate-sitemap.ts +1 -1
  66. package/src/App.tsx +29 -10
  67. package/src/ContextProviders.tsx +25 -2
  68. package/src/components/CircuitJsonImportDialog.tsx +1 -1
  69. package/src/components/CmdKMenu.tsx +281 -247
  70. package/src/components/DownloadButtonAndMenu.tsx +133 -36
  71. package/src/components/FileSidebar.tsx +41 -50
  72. package/src/components/Footer.tsx +8 -10
  73. package/src/components/Header.tsx +19 -32
  74. package/src/components/Header2.tsx +16 -32
  75. package/src/components/HeaderDropdown.tsx +13 -8
  76. package/src/components/HeaderLogin.tsx +44 -16
  77. package/src/components/HiddenFilesDropdown.tsx +0 -2
  78. package/src/components/NotFound.tsx +5 -5
  79. package/src/components/PackageBreadcrumb.tsx +6 -12
  80. package/src/components/PackageCard.tsx +0 -1
  81. package/src/components/PackageSearchResults.tsx +1 -1
  82. package/src/components/PrefetchPageLink.tsx +7 -1
  83. package/src/components/ProfileRouter.tsx +32 -0
  84. package/src/components/SearchComponent.tsx +12 -8
  85. package/src/components/UserCard.tsx +80 -0
  86. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +20 -11
  87. package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
  88. package/src/components/ViewPackagePage/components/important-files-view.tsx +174 -87
  89. package/src/components/ViewPackagePage/components/main-content-header.tsx +8 -4
  90. package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -2
  91. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -20
  92. package/src/components/ViewPackagePage/components/package-header.tsx +26 -37
  93. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -19
  94. package/src/components/ViewPackagePage/components/repo-page-content.tsx +33 -25
  95. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
  96. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
  97. package/src/components/ViewPackagePage/components/sidebar.tsx +0 -2
  98. package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +18 -17
  99. package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
  100. package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
  101. package/src/components/ViewPackagePage/components/theme-toggle.tsx +0 -2
  102. package/src/components/ViewPackagePage/hooks/use-toast.tsx +0 -1
  103. package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
  104. package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
  105. package/src/components/dialogs/import-component-dialog.tsx +16 -9
  106. package/src/components/dialogs/import-package-dialog.tsx +3 -2
  107. package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
  108. package/src/components/organization/OrganizationCard.tsx +204 -0
  109. package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
  110. package/src/components/organization/OrganizationHeader.tsx +154 -0
  111. package/src/components/organization/OrganizationMembers.tsx +146 -0
  112. package/src/components/package-port/CodeAndPreview.tsx +32 -46
  113. package/src/components/package-port/CodeEditor.tsx +28 -31
  114. package/src/components/package-port/CodeEditorHeader.tsx +128 -63
  115. package/src/components/package-port/EditorNav.tsx +32 -49
  116. package/src/components/preview/ConnectedPackagesList.tsx +8 -8
  117. package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
  118. package/src/components/preview/PackageReleasesDashboard.tsx +53 -36
  119. package/src/components/ui/tree-view.tsx +6 -3
  120. package/src/hooks/use-add-org-member-mutation.ts +51 -0
  121. package/src/hooks/use-create-org-mutation.ts +38 -0
  122. package/src/hooks/use-create-package-mutation.ts +3 -0
  123. package/src/hooks/use-current-package-id.ts +5 -30
  124. package/src/hooks/use-current-package-info.ts +29 -5
  125. package/src/hooks/use-current-package-release.ts +4 -3
  126. package/src/hooks/use-download-zip.ts +2 -2
  127. package/src/hooks/use-global-store.ts +6 -4
  128. package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
  129. package/src/hooks/use-list-org-members.ts +27 -0
  130. package/src/hooks/use-list-user-orgs.ts +25 -0
  131. package/src/hooks/use-org-by-github-handle.ts +26 -0
  132. package/src/hooks/use-org.ts +24 -0
  133. package/src/hooks/use-organization.ts +42 -0
  134. package/src/hooks/use-package-as-snippet.ts +4 -2
  135. package/src/hooks/use-package-builds.ts +6 -2
  136. package/src/hooks/use-package-files.ts +5 -3
  137. package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
  138. package/src/hooks/use-package-release-images.ts +105 -0
  139. package/src/hooks/use-package-release.ts +2 -2
  140. package/src/hooks/use-package-stars.ts +80 -4
  141. package/src/hooks/use-preview-images.ts +6 -3
  142. package/src/hooks/use-remove-org-member-mutation.ts +32 -0
  143. package/src/hooks/use-update-ai-description-mutation.ts +42 -0
  144. package/src/hooks/use-update-org-mutation.ts +41 -0
  145. package/src/hooks/use-warn-user-on-page-change.ts +71 -4
  146. package/src/hooks/useFileManagement.ts +183 -35
  147. package/src/hooks/useOptimizedPackageFilesLoader.ts +136 -0
  148. package/src/hooks/usePackageFilesLoader.ts +2 -2
  149. package/src/hooks/useUpdatePackageFilesMutation.ts +15 -1
  150. package/src/lib/download-fns/download-circuit-png.ts +11 -3
  151. package/src/lib/download-fns/download-gltf-from-circuit-json.ts +44 -0
  152. package/src/lib/download-fns/download-kicad-files.ts +12 -11
  153. package/src/lib/normalize-svg-for-tile.ts +50 -0
  154. package/src/lib/posthog.ts +11 -9
  155. package/src/lib/react-query-api-failure-tracking.ts +148 -0
  156. package/src/lib/sentry.ts +14 -0
  157. package/src/lib/templates/blank-circuit-board-template.ts +0 -4
  158. package/src/lib/ts-lib-cache.ts +122 -7
  159. package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
  160. package/src/lib/utils/findTargetFile.ts +45 -10
  161. package/src/lib/utils/isComponentExported.ts +10 -0
  162. package/src/main.tsx +2 -1
  163. package/src/pages/authorize.tsx +0 -2
  164. package/src/pages/create-organization.tsx +168 -0
  165. package/src/pages/dashboard.tsx +38 -6
  166. package/src/pages/datasheet.tsx +1 -1
  167. package/src/pages/datasheets.tsx +3 -3
  168. package/src/pages/editor.tsx +4 -6
  169. package/src/pages/landing.tsx +6 -7
  170. package/src/pages/latest.tsx +3 -0
  171. package/src/pages/organization-profile.tsx +199 -0
  172. package/src/pages/organization-settings.tsx +566 -0
  173. package/src/pages/package-editor.tsx +21 -21
  174. package/src/pages/preview-release.tsx +76 -136
  175. package/src/pages/quickstart.tsx +159 -123
  176. package/src/pages/release-detail.tsx +119 -31
  177. package/src/pages/search.tsx +192 -57
  178. package/src/pages/settings-redirect.tsx +44 -0
  179. package/src/pages/trending.tsx +29 -20
  180. package/src/pages/user-profile.tsx +58 -7
  181. package/src/pages/view-package.tsx +21 -26
  182. package/vite.config.ts +9 -0
  183. package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
  184. package/src/components/Footer2.tsx +0 -100
  185. package/src/components/JLCPCBImportDialog.tsx +0 -280
  186. package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
  187. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -115
  188. package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -27
  189. package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
  190. package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
  191. package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
  192. package/src/components/PageSearchComponent.tsx +0 -148
  193. package/src/components/ShippingInformationForm.tsx +0 -423
  194. package/src/components/StaticViewSnippetHeader.tsx +0 -70
  195. package/src/components/ViewPackagePage/components/file-explorer.tsx +0 -67
  196. package/src/components/ViewPackagePage/components/readme-view.tsx +0 -58
  197. package/src/components/ViewPackagePage/components/repo-header-button.tsx +0 -36
  198. package/src/components/ViewPackagePage/components/repo-header.tsx +0 -4
  199. package/src/components/ViewPackagePage/components/sidebar-contributors-section.tsx +0 -31
  200. package/src/components/ViewSnippetHeader.tsx +0 -181
  201. package/src/components/ui/input-otp.tsx +0 -69
  202. package/src/pages/package-builds.tsx +0 -33
  203. package/src/pages/settings.tsx +0 -25
@@ -12,10 +12,7 @@ import { Lock, Globe } from "lucide-react"
12
12
  import { GitFork, Package, Star } from "lucide-react"
13
13
 
14
14
  import { useForkPackageMutation } from "@/hooks/use-fork-package-mutation"
15
- import {
16
- usePackageStarMutationByName,
17
- usePackageStarsByName,
18
- } from "@/hooks/use-package-stars"
15
+ import { usePackageStarringByName } from "@/hooks/use-package-stars"
19
16
  import { useOrderDialog } from "@tscircuit/runframe"
20
17
  import { useGlobalStore } from "@/hooks/use-global-store"
21
18
  import { Package as PackageType } from "fake-snippets-api/lib/db/schema"
@@ -32,7 +29,7 @@ export default function PackageHeader({
32
29
  isPrivate = false,
33
30
  isCurrentUserAuthor = false,
34
31
  }: PackageHeaderProps) {
35
- const author = packageInfo?.owner_github_username
32
+ const accountName = packageInfo?.owner_github_username
36
33
  const packageName = packageInfo?.unscoped_name
37
34
  const sessionToken = useGlobalStore((s) => s.session?.token)
38
35
  const isOwner =
@@ -45,10 +42,9 @@ export default function PackageHeader({
45
42
  isLoggedIn,
46
43
  packageReleaseId: packageInfo?.latest_package_release_id ?? "",
47
44
  })
48
- const { data: starData, isLoading: isStarDataLoading } =
49
- usePackageStarsByName(packageInfo?.name ?? null)
50
- const { addStar, removeStar } = usePackageStarMutationByName(
51
- packageInfo?.name ?? "",
45
+
46
+ const { isStarred, starCount, toggleStar } = usePackageStarringByName(
47
+ packageInfo?.name ?? null,
52
48
  )
53
49
 
54
50
  const { mutateAsync: forkPackage, isLoading: isForkLoading } =
@@ -56,12 +52,7 @@ export default function PackageHeader({
56
52
 
57
53
  const handleStarClick = async () => {
58
54
  if (!packageInfo?.name || !isLoggedIn) return
59
-
60
- if (starData?.is_starred) {
61
- await removeStar.mutateAsync()
62
- } else {
63
- await addStar.mutateAsync()
64
- }
55
+ await toggleStar()
65
56
  }
66
57
 
67
58
  const handleForkClick = async () => {
@@ -69,9 +60,6 @@ export default function PackageHeader({
69
60
  await forkPackage(packageInfo.package_id)
70
61
  }
71
62
 
72
- const isStarLoading =
73
- isStarDataLoading || addStar.isLoading || removeStar.isLoading
74
-
75
63
  useEffect(() => {
76
64
  window.TSCIRCUIT_REGISTRY_API_BASE_URL =
77
65
  import.meta.env.VITE_TSCIRCUIT_REGISTRY_API_URL ??
@@ -86,18 +74,18 @@ export default function PackageHeader({
86
74
  <div className="max-w-[1200px] mx-auto px-4">
87
75
  <div className="flex items-center justify-between flex-wrap gap-y-2">
88
76
  <div className="flex items-center min-w-0 flex-wrap">
89
- {author && packageName ? (
77
+ {accountName && packageName ? (
90
78
  <>
91
79
  <h1 className="text-lg md:text-xl font-bold mr-2 break-words">
92
80
  <Link
93
- href={`/${author}`}
81
+ href={`/${accountName}`}
94
82
  className="text-blue-600 hover:underline"
95
83
  >
96
- {author}
84
+ {accountName}
97
85
  </Link>
98
86
  <span className="px-1 text-gray-500">/</span>
99
87
  <Link
100
- href={`/${author}/${packageName}`}
88
+ href={`/${accountName}/${packageName}`}
101
89
  className="text-blue-600 hover:underline"
102
90
  >
103
91
  {packageName}
@@ -131,7 +119,12 @@ export default function PackageHeader({
131
119
  </div>
132
120
 
133
121
  <div className="hidden md:flex items-center space-x-2">
134
- <Button variant="outline" size="sm" onClick={open}>
122
+ <Button
123
+ variant="outline"
124
+ size="sm"
125
+ onClick={open}
126
+ disabled={!packageInfo?.latest_package_release_id}
127
+ >
135
128
  <Package className="w-4 h-4 mr-2" />
136
129
  Order
137
130
  </Button>
@@ -149,21 +142,17 @@ export default function PackageHeader({
149
142
  : ""
150
143
  }
151
144
  onClick={handleStarClick}
152
- disabled={
153
- isStarLoading || !packageInfo?.name || !isLoggedIn
154
- }
145
+ disabled={!packageInfo?.name || !isLoggedIn}
155
146
  >
156
147
  <Star
157
148
  className={`w-4 h-4 mr-2 ${
158
- starData?.is_starred
159
- ? "fill-yellow-500 text-yellow-500"
160
- : ""
149
+ isStarred ? "fill-yellow-500 text-yellow-500" : ""
161
150
  }`}
162
151
  />
163
- {starData?.is_starred ? "Starred" : "Star"}
164
- {(starData?.star_count ?? 0) > 0 && (
152
+ {isStarred ? "Starred" : "Star"}
153
+ {(starCount ?? 0) > 0 && (
165
154
  <span className="ml-1.5 bg-gray-100 text-gray-700 rounded-full px-1.5 py-0.5 text-xs font-medium">
166
- {starData?.star_count}
155
+ {starCount}
167
156
  </span>
168
157
  )}
169
158
  </Button>
@@ -225,17 +214,17 @@ export default function PackageHeader({
225
214
  : ""
226
215
  }
227
216
  onClick={handleStarClick}
228
- disabled={isStarLoading || !packageInfo?.name}
217
+ disabled={!packageInfo?.name || !isLoggedIn}
229
218
  >
230
219
  <Star
231
220
  className={`w-4 h-4 mr-2 ${
232
- starData?.is_starred ? "fill-yellow-500 text-yellow-500" : ""
221
+ isStarred ? "fill-yellow-500 text-yellow-500" : ""
233
222
  }`}
234
223
  />
235
- {starData?.is_starred ? "Starred" : "Star"}
236
- {(starData?.star_count ?? 0) > 0 && (
224
+ {isStarred ? "Starred" : "Star"}
225
+ {(starCount ?? 0) > 0 && (
237
226
  <span className="ml-1.5 bg-gray-100 text-gray-700 rounded-full px-1.5 py-0.5 text-xs font-medium">
238
- {starData?.star_count}
227
+ {starCount}
239
228
  </span>
240
229
  )}
241
230
  </Button>
@@ -1,10 +1,11 @@
1
- "use client"
2
- import { Skeleton } from "@/components/ui/skeleton"
3
1
  import { usePreviewImages } from "@/hooks/use-preview-images"
4
2
  import type { Package } from "fake-snippets-api/lib/db/schema"
5
3
 
6
4
  interface ViewPlaceholdersProps {
7
- packageInfo?: Pick<Package, "name" | "latest_package_release_fs_sha">
5
+ packageInfo?: Pick<
6
+ Package,
7
+ "name" | "latest_package_release_fs_sha" | "latest_package_release_id"
8
+ >
8
9
  onViewChange?: (view: "3d" | "pcb" | "schematic") => void
9
10
  }
10
11
 
@@ -16,7 +17,6 @@ export default function PreviewImageSquares({
16
17
  packageName: packageInfo?.name,
17
18
  fsMapHash: packageInfo?.latest_package_release_fs_sha ?? "",
18
19
  })
19
-
20
20
  const handleViewClick = (viewId: string) => {
21
21
  onViewChange?.(viewId as "3d" | "pcb" | "schematic")
22
22
  }
@@ -26,24 +26,16 @@ export default function PreviewImageSquares({
26
26
  {availableViews.map((view) => (
27
27
  <button
28
28
  key={view.id}
29
- className={`aspect-square bg-gray-100 dark:bg-[#161b22] rounded-lg border border-gray-200 dark:border-[#30363d] hover:bg-gray-200 dark:hover:bg-[#21262d] flex items-center justify-center transition-colors overflow-hidden mb-6`}
29
+ className={`aspect-square ${view.status == "loading" ? "bg-slate-900/10 animate-pulse" : (view.backgroundClass ?? "bg-gray-100")} rounded-lg border border-gray-200 dark:border-[#30363d] flex items-center justify-center transition-colors overflow-hidden mb-6`}
30
30
  onClick={() => handleViewClick(view.id)}
31
31
  >
32
32
  {view.imageUrl && (
33
- <>
34
- {view.status === "loading" && (
35
- <Skeleton className="w-full h-full rounded-lg" />
36
- )}
37
- <img
38
- src={view.imageUrl}
39
- alt={view.label}
40
- className={`w-full h-full object-cover rounded-lg ${
41
- view.status === "loaded" ? "block" : "hidden"
42
- }`}
43
- onLoad={view.onLoad}
44
- onError={view.onError}
45
- />
46
- </>
33
+ <img
34
+ src={view.imageUrl}
35
+ className="w-full h-full object-cover rounded-lg"
36
+ onLoad={view.onLoad}
37
+ onError={view.onError}
38
+ />
47
39
  )}
48
40
  </button>
49
41
  ))}
@@ -1,5 +1,3 @@
1
- "use client"
2
-
3
1
  import { useState, useEffect, useMemo } from "react"
4
2
  import MainContentHeader from "./main-content-header"
5
3
  import Sidebar from "./sidebar"
@@ -20,21 +18,19 @@ import Header from "@/components/Header"
20
18
  import Footer from "@/components/Footer"
21
19
  import PackageHeader from "./package-header"
22
20
  import { useGlobalStore } from "@/hooks/use-global-store"
23
- import { useLocation } from "wouter"
24
- import { Package } from "fake-snippets-api/lib/db/schema"
25
- import { useCurrentPackageCircuitJson } from "../hooks/use-current-package-circuit-json"
21
+ import type {
22
+ Package,
23
+ PackageFile as ApiPackageFile,
24
+ } from "fake-snippets-api/lib/db/schema"
26
25
  import { useRequestAiReviewMutation } from "@/hooks/use-request-ai-review-mutation"
26
+ import { useUpdateAiDescriptionMutation } from "@/hooks/use-update-ai-description-mutation"
27
27
  import { useAiReview } from "@/hooks/use-ai-review"
28
28
  import { useQueryClient } from "react-query"
29
29
  import SidebarReleasesSection from "./sidebar-releases-section"
30
30
 
31
- interface PackageFile {
32
- package_file_id: string
33
- package_release_id: string
34
- file_path: string
35
- file_content: string
36
- content_text?: string // Keep for backward compatibility
37
- created_at: string // iso-8601
31
+ interface PackageFile extends ApiPackageFile {
32
+ file_content?: string
33
+ content_text?: string | null // Keep for backward compatibility
38
34
  }
39
35
 
40
36
  interface RepoPageContentProps {
@@ -44,10 +40,12 @@ interface RepoPageContentProps {
44
40
  packageRelease?: import("fake-snippets-api/lib/db/schema").PackageRelease
45
41
  onFileClicked?: (file: PackageFile) => void
46
42
  onEditClicked?: () => void
43
+ arePackageFilesFetched?: boolean
47
44
  }
48
45
 
49
46
  export default function RepoPageContent({
50
47
  packageFiles,
48
+ arePackageFilesFetched = false,
51
49
  packageInfo,
52
50
  packageRelease,
53
51
  onFileClicked,
@@ -70,8 +68,12 @@ export default function RepoPageContent({
70
68
  }
71
69
  }, [aiReview?.ai_review_text, queryClient])
72
70
  const session = useGlobalStore((s) => s.session)
73
- const { circuitJson, isLoading: isCircuitJsonLoading } =
74
- useCurrentPackageCircuitJson()
71
+
72
+ // Check if circuit.json exists without downloading it
73
+ const circuitJsonExists = useMemo(() => {
74
+ return packageFiles?.some((file) => file.file_path === "dist/circuit.json")
75
+ }, [packageFiles])
76
+
75
77
  const { mutate: requestAiReview, isLoading: isRequestingAiReview } =
76
78
  useRequestAiReviewMutation({
77
79
  onSuccess: (_packageRelease, aiReview) => {
@@ -79,6 +81,9 @@ export default function RepoPageContent({
79
81
  },
80
82
  })
81
83
 
84
+ const { mutate: updateAiDescription, isLoading: isUpdatingAiDescription } =
85
+ useUpdateAiDescriptionMutation()
86
+
82
87
  const aiReviewRequested =
83
88
  Boolean(packageRelease?.ai_review_requested) ||
84
89
  Boolean(pendingAiReviewId) ||
@@ -91,13 +96,12 @@ export default function RepoPageContent({
91
96
 
92
97
  // Handle initial view selection and hash-based view changes
93
98
  useEffect(() => {
94
- if (isCircuitJsonLoading) return
95
- if (!packageInfo) return
99
+ if (!packageInfo || !arePackageFilesFetched) return
96
100
  const hash = window.location.hash.slice(1)
97
101
  const validViews = ["files", "3d", "pcb", "schematic", "bom"]
98
102
  const circuitDependentViews = ["3d", "pcb", "schematic", "bom"]
99
103
 
100
- const availableViews = circuitJson
104
+ const availableViews = circuitJsonExists
101
105
  ? validViews
102
106
  : validViews.filter((view) => !circuitDependentViews.includes(view))
103
107
 
@@ -115,7 +119,7 @@ export default function RepoPageContent({
115
119
  window.location.hash = "files"
116
120
  }
117
121
  }
118
- }, [packageInfo?.default_view, circuitJson, isCircuitJsonLoading])
122
+ }, [packageInfo?.default_view, circuitJsonExists])
119
123
 
120
124
  const importantFilePaths = packageFiles
121
125
  ?.filter((pf) => isPackageFileImportant(pf.file_path))
@@ -146,7 +150,7 @@ export default function RepoPageContent({
146
150
  return (
147
151
  <FilesView
148
152
  packageFiles={packageFiles}
149
- isLoading={!packageFiles}
153
+ arePackageFilesFetched={arePackageFilesFetched}
150
154
  onFileClicked={onFileClicked}
151
155
  />
152
156
  )
@@ -162,7 +166,7 @@ export default function RepoPageContent({
162
166
  return (
163
167
  <FilesView
164
168
  packageFiles={packageFiles}
165
- isLoading={!packageFiles}
169
+ arePackageFilesFetched={arePackageFilesFetched}
166
170
  onFileClicked={onFileClicked}
167
171
  />
168
172
  )
@@ -211,15 +215,12 @@ export default function RepoPageContent({
211
215
 
212
216
  {/* Dynamic Content based on active view */}
213
217
  {renderContent()}
214
-
215
218
  {/* Important Files View - Always shown */}
216
219
  <ImportantFilesView
217
220
  importantFiles={importantFiles}
218
- isLoading={!packageFiles}
221
+ isFetched={arePackageFilesFetched}
222
+ pkg={packageInfo}
219
223
  onEditClicked={onEditClicked}
220
- packageAuthorOwner={packageInfo?.owner_github_username}
221
- aiDescription={packageInfo?.ai_description ?? ""}
222
- aiUsageInstructions={packageInfo?.ai_usage_instructions ?? ""}
223
224
  aiReviewText={packageRelease?.ai_review_text ?? null}
224
225
  aiReviewRequested={aiReviewRequested}
225
226
  onRequestAiReview={() => {
@@ -229,6 +230,13 @@ export default function RepoPageContent({
229
230
  })
230
231
  }
231
232
  }}
233
+ onRequestAiDescriptionUpdate={() => {
234
+ if (packageInfo) {
235
+ updateAiDescription({
236
+ package_id: packageInfo.package_id,
237
+ })
238
+ }
239
+ }}
232
240
  onLicenseFileRequested={licenseFileRequested}
233
241
  />
234
242
  </div>
@@ -2,12 +2,12 @@ import { Badge } from "@/components/ui/badge"
2
2
  import { GitFork, Star, Settings, LinkIcon, Github, Plus } from "lucide-react"
3
3
  import { Skeleton } from "@/components/ui/skeleton"
4
4
  import { useCurrentPackageInfo } from "@/hooks/use-current-package-info"
5
- import { usePackageReleaseById } from "@/hooks/use-package-release"
5
+ import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
6
6
  import { useGlobalStore } from "@/hooks/use-global-store"
7
7
  import { Button } from "@/components/ui/button"
8
8
  import { useEditPackageDetailsDialog } from "@/components/dialogs/edit-package-details-dialog"
9
9
  import { useState, useEffect, useMemo } from "react"
10
- import { usePackageFile } from "@/hooks/use-package-files"
10
+ import { usePackageFileById, usePackageFiles } from "@/hooks/use-package-files"
11
11
  import { getLicenseFromLicenseContent } from "@/lib/getLicenseFromLicenseContent"
12
12
  import { PackageInfo } from "@/lib/types"
13
13
 
@@ -21,23 +21,29 @@ export default function SidebarAboutSection({
21
21
  onLicenseClick,
22
22
  }: SidebarAboutSectionProps = {}) {
23
23
  const { packageInfo, refetch: refetchPackageInfo } = useCurrentPackageInfo()
24
- const { data: packageRelease } = usePackageReleaseById(
24
+ const { packageRelease } = useCurrentPackageRelease({
25
+ include_ai_review: true,
26
+ })
27
+
28
+ const { data: releaseFiles } = usePackageFiles(
25
29
  packageInfo?.latest_package_release_id,
26
30
  )
27
-
28
- const { data: licenseFileMeta, refetch: refetchLicense } = usePackageFile({
29
- package_release_id: packageInfo?.latest_package_release_id ?? "",
30
- file_path: "LICENSE",
31
- })
31
+ const licenseFileId = useMemo(() => {
32
+ return (
33
+ releaseFiles?.find((f) => f.file_path === "LICENSE")?.package_file_id ||
34
+ null
35
+ )
36
+ }, [releaseFiles])
37
+ const { data: licenseFileMeta } = usePackageFileById(licenseFileId)
32
38
  const currentLicense = useMemo(() => {
33
39
  if (packageInfo?.latest_license) {
34
40
  return packageInfo?.latest_license
35
41
  }
36
42
  if (licenseFileMeta?.content_text) {
37
- return getLicenseFromLicenseContent(licenseFileMeta?.content_text)
43
+ return getLicenseFromLicenseContent(licenseFileMeta.content_text)
38
44
  }
39
45
  return null
40
- }, [licenseFileMeta])
46
+ }, [licenseFileMeta, packageInfo?.latest_license])
41
47
  const topics = packageInfo?.is_package ? ["Package"] : ["Board"]
42
48
  const isLoading = !packageInfo || !packageRelease
43
49
  const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
@@ -1,12 +1,12 @@
1
1
  import { Tag, Clock } from "lucide-react"
2
2
  import { Skeleton } from "@/components/ui/skeleton"
3
3
  import { useCurrentPackageInfo } from "@/hooks/use-current-package-info"
4
- import { usePackageReleaseById } from "@/hooks/use-package-release"
4
+ import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
5
5
  import { timeAgo } from "@/lib/utils/timeAgo"
6
6
  import { BuildStatus, BuildStep } from "./build-status"
7
7
  import type { PackageRelease } from "fake-snippets-api/lib/db/schema"
8
8
  import { getBuildStatus, StatusIcon } from "@/components/preview"
9
- import { PrefetchPageLink } from "@/components/PrefetchPageLink"
9
+ import { Link } from "wouter"
10
10
  import { usePackageBuild } from "@/hooks/use-package-builds"
11
11
 
12
12
  function getTranspilationStatus(
@@ -39,9 +39,9 @@ function getCircuitJsonStatus(pr?: PackageRelease | null): BuildStep["status"] {
39
39
 
40
40
  export default function SidebarReleasesSection() {
41
41
  const { packageInfo } = useCurrentPackageInfo()
42
- const { data: packageRelease } = usePackageReleaseById(
43
- packageInfo?.latest_package_release_id,
44
- )
42
+ const { packageRelease } = useCurrentPackageRelease({
43
+ include_ai_review: true,
44
+ })
45
45
  const { data: latestBuild } = usePackageBuild(
46
46
  packageRelease?.latest_package_build_id ?? null,
47
47
  )
@@ -80,21 +80,21 @@ export default function SidebarReleasesSection() {
80
80
  return (
81
81
  <div className="mb-6">
82
82
  <h2 className="text-lg font-semibold mb-2">
83
- <PrefetchPageLink
83
+ <Link
84
84
  href={`/${packageInfo?.owner_github_username}/${packageInfo?.unscoped_name}/releases`}
85
85
  className="hover:underline"
86
86
  >
87
87
  Releases
88
- </PrefetchPageLink>
88
+ </Link>
89
89
  </h2>
90
90
  <div className="flex flex-col space-y-2">
91
- <PrefetchPageLink
91
+ <Link
92
92
  href={`/${packageInfo?.owner_github_username}/${packageInfo?.unscoped_name}/releases`}
93
93
  className="flex items-center hover:underline"
94
94
  >
95
95
  <Tag className="h-4 w-4 mr-2 text-gray-500 dark:text-[#8b949e]" />
96
96
  <span className="text-sm font-medium">v{packageRelease.version}</span>
97
- </PrefetchPageLink>
97
+ </Link>
98
98
  <div className="flex items-center">
99
99
  <Clock className="h-4 w-4 mr-2 text-gray-500 dark:text-[#8b949e]" />
100
100
  <span className="text-sm text-gray-500 dark:text-[#8b949e]">
@@ -109,13 +109,13 @@ export default function SidebarReleasesSection() {
109
109
  />
110
110
  ))}
111
111
  {latestBuild && (
112
- <PrefetchPageLink
112
+ <Link
113
113
  href={`/${packageInfo?.name}/releases`}
114
114
  className="flex items-center gap-2 text-sm text-gray-500 dark:text-[#8b949e]"
115
115
  >
116
116
  <StatusIcon status={status} />
117
117
  <span>Package Preview {label}</span>
118
- </PrefetchPageLink>
118
+ </Link>
119
119
  )}
120
120
  </div>
121
121
  {/* <a href="#" className="text-blue-600 dark:text-[#58a6ff] hover:underline text-sm">
@@ -1,5 +1,3 @@
1
- "use client"
2
-
3
1
  import { Package } from "fake-snippets-api/lib/db/schema"
4
2
  import SidebarAboutSection from "./sidebar-about-section"
5
3
  import SidebarReleasesSection from "./sidebar-releases-section"
@@ -1,11 +1,10 @@
1
- "use client"
2
-
3
1
  import { Skeleton } from "@/components/ui/skeleton"
4
2
  import { FileText, Folder } from "lucide-react"
5
3
  import { useMemo, useState } from "react"
6
4
  import { isHiddenFile } from "../../utils/is-hidden-file"
7
5
  import { isWithinDirectory } from "../../utils/is-within-directory"
8
6
  import HiddenFilesDropdown from "@/components/HiddenFilesDropdown"
7
+ import type { PackageFile as ApiPackageFile } from "fake-snippets-api/lib/db/schema"
9
8
 
10
9
  interface Directory {
11
10
  type: "directory"
@@ -21,24 +20,20 @@ interface File {
21
20
  created_at: string
22
21
  }
23
22
 
24
- interface PackageFile {
25
- package_file_id: string
26
- package_release_id: string
27
- file_path: string
28
- file_content: string
29
- content_text?: string // Keep for backward compatibility
30
- created_at: string // iso-8601
23
+ interface PackageFile extends ApiPackageFile {
24
+ file_content?: string
25
+ content_text?: string | null // Keep for backward compatibility
31
26
  }
32
27
 
33
28
  interface FilesViewProps {
34
29
  packageFiles?: PackageFile[]
35
- isLoading?: boolean
36
30
  onFileClicked?: (file: PackageFile) => void
31
+ arePackageFilesFetched?: boolean
37
32
  }
38
33
 
39
34
  export default function FilesView({
40
35
  packageFiles = [],
41
- isLoading = false,
36
+ arePackageFilesFetched = false,
42
37
  onFileClicked,
43
38
  }: FilesViewProps) {
44
39
  const [activeDir, setActiveDir] = useState("")
@@ -106,12 +101,19 @@ export default function FilesView({
106
101
  }, [packageFiles, showHiddenFiles])
107
102
  // Format date for display
108
103
  const formatDate = (dateString: string) => {
109
- const date = new Date(dateString)
104
+ const parsedDate = new Date(dateString)
105
+ if (Number.isNaN(parsedDate.getTime())) return ""
106
+
110
107
  const now = new Date()
111
- const diffTime = Math.abs(now.getTime() - date.getTime())
112
- const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
108
+ const diffMs = now.getTime() - parsedDate.getTime()
109
+ const oneDayMs = 1000 * 60 * 60 * 24
110
+
111
+ // Treat future dates as today
112
+ if (diffMs <= 0) return "today"
113
113
 
114
- if (diffDays < 1) return "today"
114
+ if (diffMs < oneDayMs) return "today"
115
+
116
+ const diffDays = Math.floor(diffMs / oneDayMs)
115
117
  if (diffDays === 1) return "yesterday"
116
118
  if (diffDays < 7) return `${diffDays} days ago`
117
119
  if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`
@@ -160,7 +162,7 @@ export default function FilesView({
160
162
 
161
163
  const toggleHiddenFiles = () => setShowHiddenFiles((prev) => !prev)
162
164
 
163
- if (isLoading) {
165
+ if (!arePackageFilesFetched) {
164
166
  return (
165
167
  <div className="mb-4 border border-gray-200 dark:border-[#30363d] rounded-md overflow-hidden">
166
168
  <div className="flex items-center px-4 py-2 md:py-3 bg-gray-100 dark:bg-[#161b22] border-b border-gray-200 dark:border-[#30363d]">
@@ -225,7 +227,6 @@ export default function FilesView({
225
227
  </div>
226
228
  </div>
227
229
  </div>
228
-
229
230
  {/* Files and Directories */}
230
231
  <div className="bg-white dark:bg-[#0d1117]">
231
232
  {items.length === 0 && !activeDir ? (
@@ -1,6 +1,4 @@
1
- import { usePackageFile, usePackageFileByPath } from "@/hooks/use-package-files"
2
1
  import { PcbViewerWithContainerHeight } from "@/components/PcbViewerWithContainerHeight"
3
- import { useEffect, useState } from "react"
4
2
  import { useCurrentPackageCircuitJson } from "../../hooks/use-current-package-circuit-json"
5
3
 
6
4
  export default function PCBView() {
@@ -27,6 +25,7 @@ export default function PCBView() {
27
25
  return (
28
26
  <div className="border border-gray-200 dark:border-[#30363d] rounded-md p-4 mb-4 bg-white dark:bg-[#0d1117]">
29
27
  <PcbViewerWithContainerHeight
28
+ disablePcbGroups
30
29
  clickToInteractEnabled
31
30
  circuitJson={circuitJson}
32
31
  containerClassName="w-full h-[620px]"
@@ -1,5 +1,5 @@
1
1
  import { useCurrentPackageCircuitJson } from "../../hooks/use-current-package-circuit-json"
2
- import { SchematicViewer } from "@tscircuit/runframe"
2
+ import { SchematicViewer } from "@tscircuit/schematic-viewer"
3
3
 
4
4
  export default function SchematicView() {
5
5
  const { circuitJson, isLoading, error } = useCurrentPackageCircuitJson()
@@ -26,6 +26,7 @@ export default function SchematicView() {
26
26
  return (
27
27
  <div className="h-[620px]">
28
28
  <SchematicViewer
29
+ disableGroups
29
30
  clickToInteractEnabled
30
31
  circuitJson={circuitJson}
31
32
  containerStyle={{
@@ -1,5 +1,3 @@
1
- "use client"
2
-
3
1
  import { useTheme } from "next-themes"
4
2
  import { Moon, Sun } from "lucide-react"
5
3
  import { Button } from "@/components/ui/button"
@@ -1,4 +1,3 @@
1
- "use client"
2
1
  import toastLibrary, { Toaster, type Toast } from "react-hot-toast"
3
2
  import React from "react"
4
3