@tscircuit/fake-snippets 0.0.109 → 0.0.111

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 (185) 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 +32 -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 +151 -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 +361 -453
  34. package/bunfig.toml +2 -1
  35. package/dist/bundle.js +1313 -639
  36. package/dist/index.d.ts +313 -6
  37. package/dist/index.js +328 -24
  38. package/dist/schema.d.ts +290 -1
  39. package/dist/schema.js +54 -1
  40. package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
  41. package/fake-snippets-api/lib/db/db-client.ts +219 -4
  42. package/fake-snippets-api/lib/db/schema.ts +63 -1
  43. package/fake-snippets-api/lib/db/seed.ts +100 -0
  44. package/fake-snippets-api/lib/middleware/with-session-auth.ts +60 -8
  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 +33 -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 +48 -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 +60 -0
  54. package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
  55. package/fake-snippets-api/routes/api/orgs/update.ts +118 -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 +57 -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 +25 -19
  64. package/renovate.json +1 -1
  65. package/scripts/generate-sitemap.ts +1 -1
  66. package/src/App.tsx +27 -8
  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 +17 -5
  71. package/src/components/FileSidebar.tsx +11 -17
  72. package/src/components/Footer.tsx +8 -9
  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 +43 -15
  77. package/src/components/NotFound.tsx +5 -5
  78. package/src/components/PackageBreadcrumb.tsx +6 -12
  79. package/src/components/PackageSearchResults.tsx +1 -1
  80. package/src/components/PrefetchPageLink.tsx +7 -1
  81. package/src/components/ProfileRouter.tsx +32 -0
  82. package/src/components/SearchComponent.tsx +12 -8
  83. package/src/components/SentryNotFoundReporter.tsx +44 -0
  84. package/src/components/UserCard.tsx +80 -0
  85. package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
  86. package/src/components/ViewPackagePage/components/important-files-view.tsx +105 -34
  87. package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -6
  88. package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -1
  89. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -19
  90. package/src/components/ViewPackagePage/components/package-header.tsx +25 -33
  91. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -18
  92. package/src/components/ViewPackagePage/components/repo-page-content.tsx +12 -5
  93. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
  94. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
  95. package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
  96. package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
  97. package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
  98. package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
  99. package/src/components/dialogs/import-component-dialog.tsx +16 -9
  100. package/src/components/dialogs/import-package-dialog.tsx +3 -2
  101. package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
  102. package/src/components/organization/OrganizationCard.tsx +206 -0
  103. package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
  104. package/src/components/organization/OrganizationHeader.tsx +154 -0
  105. package/src/components/organization/OrganizationMembers.tsx +146 -0
  106. package/src/components/package-port/CodeAndPreview.tsx +15 -12
  107. package/src/components/package-port/CodeEditor.tsx +4 -30
  108. package/src/components/package-port/CodeEditorHeader.tsx +123 -61
  109. package/src/components/package-port/EditorNav.tsx +32 -49
  110. package/src/components/preview/ConnectedPackagesList.tsx +8 -8
  111. package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
  112. package/src/components/preview/PackageReleasesDashboard.tsx +23 -11
  113. package/src/components/ui/tree-view.tsx +6 -3
  114. package/src/hooks/use-add-org-member-mutation.ts +51 -0
  115. package/src/hooks/use-create-org-mutation.ts +38 -0
  116. package/src/hooks/use-create-package-mutation.ts +3 -0
  117. package/src/hooks/use-current-package-release.ts +4 -3
  118. package/src/hooks/use-download-zip.ts +2 -2
  119. package/src/hooks/use-global-store.ts +6 -4
  120. package/src/hooks/use-hydration.ts +30 -0
  121. package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
  122. package/src/hooks/use-list-org-members.ts +27 -0
  123. package/src/hooks/use-list-user-orgs.ts +25 -0
  124. package/src/hooks/use-org-by-github-handle.ts +26 -0
  125. package/src/hooks/use-org.ts +24 -0
  126. package/src/hooks/use-organization.ts +42 -0
  127. package/src/hooks/use-package-as-snippet.ts +4 -2
  128. package/src/hooks/use-package-builds.ts +6 -2
  129. package/src/hooks/use-package-files.ts +5 -3
  130. package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
  131. package/src/hooks/use-package-release-images.ts +105 -0
  132. package/src/hooks/use-package-release.ts +2 -2
  133. package/src/hooks/use-package-stars.ts +80 -4
  134. package/src/hooks/use-preview-images.ts +6 -3
  135. package/src/hooks/use-remove-org-member-mutation.ts +32 -0
  136. package/src/hooks/use-update-ai-description-mutation.ts +42 -0
  137. package/src/hooks/use-update-org-mutation.ts +41 -0
  138. package/src/hooks/use-warn-user-on-page-change.ts +71 -4
  139. package/src/hooks/useFileManagement.ts +51 -22
  140. package/src/hooks/useOptimizedPackageFilesLoader.ts +11 -24
  141. package/src/hooks/usePackageFilesLoader.ts +2 -2
  142. package/src/hooks/useUpdatePackageFilesMutation.ts +13 -1
  143. package/src/lib/download-fns/download-gltf-from-circuit-json.ts +1 -1
  144. package/src/lib/download-fns/download-kicad-files.ts +22 -11
  145. package/src/lib/download-fns/download-step.ts +12 -0
  146. package/src/lib/normalize-svg-for-tile.ts +50 -0
  147. package/src/lib/posthog.ts +11 -9
  148. package/src/lib/react-query-api-failure-tracking.ts +148 -0
  149. package/src/lib/sentry.ts +14 -0
  150. package/src/lib/templates/blank-circuit-board-template.ts +0 -4
  151. package/src/lib/ts-lib-cache.ts +122 -7
  152. package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
  153. package/src/lib/utils/findTargetFile.ts +45 -10
  154. package/src/lib/utils/isComponentExported.ts +2 -1
  155. package/src/main.tsx +2 -1
  156. package/src/pages/create-organization.tsx +169 -0
  157. package/src/pages/dashboard.tsx +38 -6
  158. package/src/pages/datasheet.tsx +1 -1
  159. package/src/pages/datasheets.tsx +3 -3
  160. package/src/pages/editor.tsx +4 -6
  161. package/src/pages/landing.tsx +6 -6
  162. package/src/pages/latest.tsx +3 -0
  163. package/src/pages/organization-profile.tsx +199 -0
  164. package/src/pages/organization-settings.tsx +569 -0
  165. package/src/pages/package-editor.tsx +21 -21
  166. package/src/pages/preview-release.tsx +75 -145
  167. package/src/pages/quickstart.tsx +159 -123
  168. package/src/pages/release-detail.tsx +119 -31
  169. package/src/pages/search.tsx +197 -57
  170. package/src/pages/settings-redirect.tsx +44 -0
  171. package/src/pages/trending.tsx +29 -20
  172. package/src/pages/user-profile.tsx +58 -7
  173. package/src/pages/user-settings.tsx +161 -0
  174. package/src/pages/view-package.tsx +30 -16
  175. package/vite.config.ts +9 -0
  176. package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
  177. package/src/components/JLCPCBImportDialog.tsx +0 -280
  178. package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
  179. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -113
  180. package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -56
  181. package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
  182. package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
  183. package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
  184. package/src/components/PageSearchComponent.tsx +0 -148
  185. package/src/pages/package-builds.tsx +0 -33
@@ -1,13 +1,18 @@
1
1
  import { GitFork, Star, Tag, Settings, LinkIcon } from "lucide-react"
2
2
  import { Badge } from "@/components/ui/badge"
3
3
  import { Skeleton } from "@/components/ui/skeleton"
4
+ import { usePackageReleaseImages } from "@/hooks/use-package-release-images"
4
5
  import { usePreviewImages } from "@/hooks/use-preview-images"
5
6
  import { useGlobalStore } from "@/hooks/use-global-store"
6
7
  import { Button } from "@/components/ui/button"
7
8
  import { useEditPackageDetailsDialog } from "@/components/dialogs/edit-package-details-dialog"
8
9
  import React, { useState, useEffect, useMemo, useCallback } from "react"
10
+ import {
11
+ normalizeSvgForSquareTile,
12
+ svgToDataUrl,
13
+ } from "@/lib/normalize-svg-for-tile"
9
14
  import { useCurrentPackageInfo } from "@/hooks/use-current-package-info"
10
- import { usePackageFile } from "@/hooks/use-package-files"
15
+ import { usePackageFileById, usePackageFiles } from "@/hooks/use-package-files"
11
16
  import { getLicenseFromLicenseContent } from "@/lib/getLicenseFromLicenseContent"
12
17
 
13
18
  interface MobileSidebarProps {
@@ -20,19 +25,25 @@ const MobileSidebar = ({
20
25
  onViewChange,
21
26
  }: MobileSidebarProps) => {
22
27
  const { packageInfo, refetch: refetchPackageInfo } = useCurrentPackageInfo()
23
- const { data: licenseFileMeta } = usePackageFile({
24
- package_release_id: packageInfo?.latest_package_release_id ?? "",
25
- file_path: "LICENSE",
26
- })
28
+ const { data: releaseFiles } = usePackageFiles(
29
+ packageInfo?.latest_package_release_id,
30
+ )
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)
27
38
  const currentLicense = useMemo(() => {
28
39
  if (packageInfo?.latest_license) {
29
40
  return packageInfo?.latest_license
30
41
  }
31
42
  if (licenseFileMeta?.content_text) {
32
- return getLicenseFromLicenseContent(licenseFileMeta?.content_text)
43
+ return getLicenseFromLicenseContent(licenseFileMeta.content_text)
33
44
  }
34
45
  return undefined
35
- }, [licenseFileMeta?.content_text, packageInfo?.latest_license])
46
+ }, [licenseFileMeta, packageInfo?.latest_license])
36
47
  const topics = useMemo(
37
48
  () => (packageInfo?.is_package ? ["Package"] : ["Board"]),
38
49
  [packageInfo?.is_package],
@@ -69,11 +80,21 @@ const MobileSidebar = ({
69
80
  [refetchPackageInfo],
70
81
  )
71
82
 
72
- const { availableViews } = usePreviewImages({
83
+ const { availableViews: imageViews } = usePackageReleaseImages({
84
+ packageReleaseId: packageInfo?.latest_package_release_id,
85
+ })
86
+
87
+ const { availableViews: pngViews } = usePreviewImages({
73
88
  packageName: packageInfo?.name,
74
89
  fsMapHash: packageInfo?.latest_package_release_fs_sha ?? "",
75
90
  })
76
91
 
92
+ const viewsToRender =
93
+ imageViews.length === 0 ||
94
+ imageViews.every((v) => !v.isLoading && !v.imageUrl)
95
+ ? (pngViews as any)
96
+ : imageViews
97
+
77
98
  const handleViewClick = useCallback(
78
99
  (viewId: string) => {
79
100
  onViewChange?.(viewId as "3d" | "pcb" | "schematic")
@@ -185,11 +206,14 @@ const MobileSidebar = ({
185
206
  </div>
186
207
 
187
208
  <div className="grid grid-cols-3 gap-2">
188
- {availableViews.map((view) => (
209
+ {viewsToRender.map((view: any) => (
189
210
  <PreviewButton
190
211
  key={view.id}
191
212
  view={view.label}
192
213
  onClick={() => handleViewClick(view.id)}
214
+ backgroundClass={view.backgroundClass}
215
+ svg={view.svg}
216
+ isLoading={view.isLoading}
193
217
  imageUrl={view.imageUrl}
194
218
  status={view.status}
195
219
  onLoad={view.onLoad}
@@ -225,6 +249,9 @@ export default React.memo(MobileSidebar)
225
249
  function PreviewButton({
226
250
  view,
227
251
  onClick,
252
+ backgroundClass,
253
+ svg,
254
+ isLoading,
228
255
  imageUrl,
229
256
  status,
230
257
  onLoad,
@@ -232,30 +259,38 @@ function PreviewButton({
232
259
  }: {
233
260
  view: string
234
261
  onClick: () => void
262
+ backgroundClass?: string
263
+ svg?: string | null
264
+ isLoading?: boolean
235
265
  imageUrl?: string
236
- status: "loading" | "loaded" | "error"
237
- onLoad: () => void
238
- onError: () => void
266
+ status?: "loading" | "loaded" | "error"
267
+ onLoad?: () => void
268
+ onError?: () => void
239
269
  }) {
240
- if (status === "error") {
270
+ if (!svg && !isLoading && !imageUrl) {
241
271
  return null
242
272
  }
243
273
 
244
274
  return (
245
275
  <button
246
276
  onClick={onClick}
247
- 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 mt-4"
277
+ className={`aspect-square ${backgroundClass ?? "bg-gray-100"} rounded-lg border border-gray-200 dark:border-[#30363d] flex items-center justify-center transition-colors mt-4 overflow-hidden`}
248
278
  >
249
- {status === "loading" && (
279
+ {(isLoading || status === "loading") && (
250
280
  <Skeleton className="w-full h-full rounded-lg" />
251
281
  )}
252
- {imageUrl && (
282
+ {!isLoading && !status && svg && (
283
+ <img
284
+ src={svgToDataUrl(normalizeSvgForSquareTile(svg))}
285
+ alt={view}
286
+ className="w-full h-full object-contain"
287
+ />
288
+ )}
289
+ {imageUrl && !isLoading && (
253
290
  <img
254
291
  src={imageUrl}
255
292
  alt={view}
256
- className={`w-full h-full object-cover rounded-lg ${
257
- status === "loaded" ? "block" : "hidden"
258
- }`}
293
+ className="w-full h-full object-cover rounded-lg"
259
294
  onLoad={onLoad}
260
295
  onError={onError}
261
296
  />
@@ -12,7 +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 { usePackageStarMutationByName } from "@/hooks/use-package-stars"
15
+ import { usePackageStarringByName } from "@/hooks/use-package-stars"
16
16
  import { useOrderDialog } from "@tscircuit/runframe"
17
17
  import { useGlobalStore } from "@/hooks/use-global-store"
18
18
  import { Package as PackageType } from "fake-snippets-api/lib/db/schema"
@@ -29,7 +29,7 @@ export default function PackageHeader({
29
29
  isPrivate = false,
30
30
  isCurrentUserAuthor = false,
31
31
  }: PackageHeaderProps) {
32
- const author = packageInfo?.owner_github_username
32
+ const accountName = packageInfo?.owner_github_username
33
33
  const packageName = packageInfo?.unscoped_name
34
34
  const sessionToken = useGlobalStore((s) => s.session?.token)
35
35
  const isOwner =
@@ -43,8 +43,8 @@ export default function PackageHeader({
43
43
  packageReleaseId: packageInfo?.latest_package_release_id ?? "",
44
44
  })
45
45
 
46
- const { addStar, removeStar } = usePackageStarMutationByName(
47
- packageInfo?.name ?? "",
46
+ const { isStarred, starCount, toggleStar } = usePackageStarringByName(
47
+ packageInfo?.name ?? null,
48
48
  )
49
49
 
50
50
  const { mutateAsync: forkPackage, isLoading: isForkLoading } =
@@ -52,12 +52,7 @@ export default function PackageHeader({
52
52
 
53
53
  const handleStarClick = async () => {
54
54
  if (!packageInfo?.name || !isLoggedIn) return
55
-
56
- if (packageInfo?.is_starred) {
57
- await removeStar.mutateAsync()
58
- } else {
59
- await addStar.mutateAsync()
60
- }
55
+ await toggleStar()
61
56
  }
62
57
 
63
58
  const handleForkClick = async () => {
@@ -65,8 +60,6 @@ export default function PackageHeader({
65
60
  await forkPackage(packageInfo.package_id)
66
61
  }
67
62
 
68
- const isStarLoading = addStar.isLoading || removeStar.isLoading
69
-
70
63
  useEffect(() => {
71
64
  window.TSCIRCUIT_REGISTRY_API_BASE_URL =
72
65
  import.meta.env.VITE_TSCIRCUIT_REGISTRY_API_URL ??
@@ -81,18 +74,18 @@ export default function PackageHeader({
81
74
  <div className="max-w-[1200px] mx-auto px-4">
82
75
  <div className="flex items-center justify-between flex-wrap gap-y-2">
83
76
  <div className="flex items-center min-w-0 flex-wrap">
84
- {author && packageName ? (
77
+ {accountName && packageName ? (
85
78
  <>
86
79
  <h1 className="text-lg md:text-xl font-bold mr-2 break-words">
87
80
  <Link
88
- href={`/${author}`}
81
+ href={`/${accountName}`}
89
82
  className="text-blue-600 hover:underline"
90
83
  >
91
- {author}
84
+ {accountName}
92
85
  </Link>
93
86
  <span className="px-1 text-gray-500">/</span>
94
87
  <Link
95
- href={`/${author}/${packageName}`}
88
+ href={`/${accountName}/${packageName}`}
96
89
  className="text-blue-600 hover:underline"
97
90
  >
98
91
  {packageName}
@@ -126,7 +119,12 @@ export default function PackageHeader({
126
119
  </div>
127
120
 
128
121
  <div className="hidden md:flex items-center space-x-2">
129
- <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
+ >
130
128
  <Package className="w-4 h-4 mr-2" />
131
129
  Order
132
130
  </Button>
@@ -144,21 +142,17 @@ export default function PackageHeader({
144
142
  : ""
145
143
  }
146
144
  onClick={handleStarClick}
147
- disabled={
148
- isStarLoading || !packageInfo?.name || !isLoggedIn
149
- }
145
+ disabled={!packageInfo?.name || !isLoggedIn}
150
146
  >
151
147
  <Star
152
148
  className={`w-4 h-4 mr-2 ${
153
- packageInfo?.is_starred
154
- ? "fill-yellow-500 text-yellow-500"
155
- : ""
149
+ isStarred ? "fill-yellow-500 text-yellow-500" : ""
156
150
  }`}
157
151
  />
158
- {packageInfo?.is_starred ? "Starred" : "Star"}
159
- {(packageInfo?.star_count ?? 0) > 0 && (
152
+ {isStarred ? "Starred" : "Star"}
153
+ {(starCount ?? 0) > 0 && (
160
154
  <span className="ml-1.5 bg-gray-100 text-gray-700 rounded-full px-1.5 py-0.5 text-xs font-medium">
161
- {packageInfo?.star_count}
155
+ {starCount}
162
156
  </span>
163
157
  )}
164
158
  </Button>
@@ -220,19 +214,17 @@ export default function PackageHeader({
220
214
  : ""
221
215
  }
222
216
  onClick={handleStarClick}
223
- disabled={isStarLoading || !packageInfo?.name}
217
+ disabled={!packageInfo?.name || !isLoggedIn}
224
218
  >
225
219
  <Star
226
220
  className={`w-4 h-4 mr-2 ${
227
- packageInfo?.is_starred
228
- ? "fill-yellow-500 text-yellow-500"
229
- : ""
221
+ isStarred ? "fill-yellow-500 text-yellow-500" : ""
230
222
  }`}
231
223
  />
232
- {packageInfo?.is_starred ? "Starred" : "Star"}
233
- {(packageInfo?.star_count ?? 0) > 0 && (
224
+ {isStarred ? "Starred" : "Star"}
225
+ {(starCount ?? 0) > 0 && (
234
226
  <span className="ml-1.5 bg-gray-100 text-gray-700 rounded-full px-1.5 py-0.5 text-xs font-medium">
235
- {packageInfo?.star_count}
227
+ {starCount}
236
228
  </span>
237
229
  )}
238
230
  </Button>
@@ -1,9 +1,11 @@
1
- import { Skeleton } from "@/components/ui/skeleton"
2
1
  import { usePreviewImages } from "@/hooks/use-preview-images"
3
2
  import type { Package } from "fake-snippets-api/lib/db/schema"
4
3
 
5
4
  interface ViewPlaceholdersProps {
6
- 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
+ >
7
9
  onViewChange?: (view: "3d" | "pcb" | "schematic") => void
8
10
  }
9
11
 
@@ -15,7 +17,6 @@ export default function PreviewImageSquares({
15
17
  packageName: packageInfo?.name,
16
18
  fsMapHash: packageInfo?.latest_package_release_fs_sha ?? "",
17
19
  })
18
-
19
20
  const handleViewClick = (viewId: string) => {
20
21
  onViewChange?.(viewId as "3d" | "pcb" | "schematic")
21
22
  }
@@ -25,24 +26,16 @@ export default function PreviewImageSquares({
25
26
  {availableViews.map((view) => (
26
27
  <button
27
28
  key={view.id}
28
- 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`}
29
30
  onClick={() => handleViewClick(view.id)}
30
31
  >
31
32
  {view.imageUrl && (
32
- <>
33
- {view.status === "loading" && (
34
- <Skeleton className="w-full h-full rounded-lg" />
35
- )}
36
- <img
37
- src={view.imageUrl}
38
- alt={view.label}
39
- className={`w-full h-full object-cover rounded-lg ${
40
- view.status === "loaded" ? "block" : "hidden"
41
- }`}
42
- onLoad={view.onLoad}
43
- onError={view.onError}
44
- />
45
- </>
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
+ />
46
39
  )}
47
40
  </button>
48
41
  ))}
@@ -18,12 +18,12 @@ import Header from "@/components/Header"
18
18
  import Footer from "@/components/Footer"
19
19
  import PackageHeader from "./package-header"
20
20
  import { useGlobalStore } from "@/hooks/use-global-store"
21
- import { useLocation } from "wouter"
22
21
  import type {
23
22
  Package,
24
23
  PackageFile as ApiPackageFile,
25
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"
@@ -81,6 +81,9 @@ export default function RepoPageContent({
81
81
  },
82
82
  })
83
83
 
84
+ const { mutate: updateAiDescription, isLoading: isUpdatingAiDescription } =
85
+ useUpdateAiDescriptionMutation()
86
+
84
87
  const aiReviewRequested =
85
88
  Boolean(packageRelease?.ai_review_requested) ||
86
89
  Boolean(pendingAiReviewId) ||
@@ -212,15 +215,12 @@ export default function RepoPageContent({
212
215
 
213
216
  {/* Dynamic Content based on active view */}
214
217
  {renderContent()}
215
-
216
218
  {/* Important Files View - Always shown */}
217
219
  <ImportantFilesView
218
220
  importantFiles={importantFiles}
219
221
  isFetched={arePackageFilesFetched}
222
+ pkg={packageInfo}
220
223
  onEditClicked={onEditClicked}
221
- packageAuthorOwner={packageInfo?.owner_github_username}
222
- aiDescription={packageInfo?.ai_description ?? ""}
223
- aiUsageInstructions={packageInfo?.ai_usage_instructions ?? ""}
224
224
  aiReviewText={packageRelease?.ai_review_text ?? null}
225
225
  aiReviewRequested={aiReviewRequested}
226
226
  onRequestAiReview={() => {
@@ -230,6 +230,13 @@ export default function RepoPageContent({
230
230
  })
231
231
  }
232
232
  }}
233
+ onRequestAiDescriptionUpdate={() => {
234
+ if (packageInfo) {
235
+ updateAiDescription({
236
+ package_id: packageInfo.package_id,
237
+ })
238
+ }
239
+ }}
233
240
  onLicenseFileRequested={licenseFileRequested}
234
241
  />
235
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,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={{