@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,5 +1,5 @@
1
- import React, { useState } from "react"
2
- import { useQuery } from "react-query"
1
+ import { useCallback, useEffect, useMemo, useState } from "react"
2
+ import { useQuery, useQueryClient } from "react-query"
3
3
  import { useAxios } from "@/hooks/use-axios"
4
4
  import Header from "@/components/Header"
5
5
  import Footer from "@/components/Footer"
@@ -7,35 +7,64 @@ import { Package } from "fake-snippets-api/lib/db/schema"
7
7
  import { Button } from "@/components/ui/button"
8
8
  import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
9
9
  import { TypeBadge } from "@/components/TypeBadge"
10
- import { JLCPCBImportDialog } from "@/components/JLCPCBImportDialog"
11
10
  import { CircuitJsonImportDialog } from "@/components/CircuitJsonImportDialog"
12
- import { useNotImplementedToast } from "@/hooks/use-toast"
13
11
  import { useGlobalStore } from "@/hooks/use-global-store"
12
+ import { useImportComponentDialog } from "@/components/dialogs/import-component-dialog"
13
+ import { useJlcpcbComponentImport } from "@/hooks/use-jlcpcb-component-import"
14
+ import { JlcpcbComponentTsxLoadedPayload } from "@tscircuit/runframe/runner"
14
15
  import { cn } from "@/lib/utils"
15
- import { PrefetchPageLink } from "@/components/PrefetchPageLink"
16
+ import { Link } from "wouter"
17
+ import { Loader2 } from "lucide-react"
16
18
 
17
19
  export const QuickstartPage = () => {
18
20
  const axios = useAxios()
19
- const [isJLCPCBDialogOpen, setIsJLCPCBDialogOpen] = useState(false)
21
+ const queryClient = useQueryClient()
20
22
  const [isCircuitJsonImportDialogOpen, setIsCircuitJsonImportDialogOpen] =
21
23
  useState(false)
22
- const toastNotImplemented = useNotImplementedToast()
23
- const currentUser = useGlobalStore((s) => s.session?.github_username)
24
+ const session = useGlobalStore((s) => s.session)
25
+ const currentUser = session?.github_username
26
+ const isLoggedIn = Boolean(currentUser)
27
+ const { Dialog: ImportComponentDialog, openDialog: openImportDialog } =
28
+ useImportComponentDialog()
29
+ const { importComponent: importJlcpcbComponent } = useJlcpcbComponentImport()
30
+ const jlcpcbProxyRequestHeaders = useMemo(
31
+ () =>
32
+ session?.token
33
+ ? {
34
+ Authorization: `Bearer ${session.token}`,
35
+ }
36
+ : undefined,
37
+ [session?.token],
38
+ )
39
+ const handleJlcpcbComponentSelected = useCallback(
40
+ async (payload: JlcpcbComponentTsxLoadedPayload) => {
41
+ await importJlcpcbComponent(payload)
42
+ },
43
+ [importJlcpcbComponent],
44
+ )
45
+ useEffect(() => {
46
+ queryClient.removeQueries("userPackages")
47
+ }, [queryClient])
24
48
  const { data: myPackages, isLoading } = useQuery<Package[]>(
25
- "userPackages",
49
+ ["userPackages", currentUser],
26
50
  async () => {
27
51
  const response = await axios.post(`/packages/list`, {
28
52
  owner_github_username: currentUser,
29
53
  })
30
54
  return response.data.packages
31
55
  },
56
+ {
57
+ enabled: isLoggedIn,
58
+ },
32
59
  )
33
60
 
34
- const blankTemplates = [
61
+ const blankTemplates: Array<{
62
+ name: string
63
+ type: string
64
+ disabled?: boolean
65
+ }> = [
35
66
  { name: "Blank Circuit Board", type: "board" },
36
67
  { name: "Blank Circuit Module", type: "package" },
37
- { name: "Blank 3D Model", type: "model", disabled: true },
38
- { name: "Blank Footprint", type: "footprint", disabled: true },
39
68
  ]
40
69
 
41
70
  const templates = [
@@ -44,51 +73,63 @@ export const QuickstartPage = () => {
44
73
  ]
45
74
 
46
75
  return (
47
- <div>
76
+ <div className="min-h-screen">
48
77
  <Header />
49
- <div className="container mx-auto px-4 py-8">
50
- <div className="mb-8 hidden md:block">
51
- <h2 className="text-xl font-semibold mb-4">Recent Packages</h2>
52
- {isLoading ? (
53
- <div>Loading...</div>
54
- ) : (
55
- <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
56
- {myPackages
57
- ?.sort(
58
- (a, b) =>
59
- new Date(b.created_at).getTime() -
60
- new Date(a.created_at).getTime(),
61
- )
62
- .slice(0, 4)
63
- .map((pkg) => (
64
- <PrefetchPageLink
65
- key={pkg.package_id}
66
- href={`/editor?package_id=${pkg.package_id}`}
67
- >
68
- <Card className="hover:shadow-md transition-shadow rounded-md flex flex-col h-full">
69
- <CardHeader className="pb-0 p-4">
70
- <CardTitle className="text-md">
71
- {pkg.unscoped_name}
72
- </CardTitle>
73
- </CardHeader>
74
- <CardContent className="p-4 pt-0 mt-auto">
75
- <p className="text-sm text-gray-500">
76
- Last edited:{" "}
77
- {new Date(pkg.updated_at).toLocaleDateString()}
78
- </p>
79
- </CardContent>
80
- </Card>
81
- </PrefetchPageLink>
82
- ))}
78
+ <div className="container mx-auto px-6 py-12">
79
+ {isLoggedIn && (
80
+ <div className="mb-16 hidden md:block">
81
+ <div className="mb-8">
82
+ <h2 className="text-2xl font-semibold text-slate-900">
83
+ Recent Packages
84
+ </h2>
83
85
  </div>
84
- )}
85
- </div>
86
+ {isLoading ? (
87
+ <div className="flex items-center justify-center py-12">
88
+ <Loader2 className="w-10 h-10 animate-spin text-blue-500 mb-4" />
89
+ </div>
90
+ ) : (
91
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
92
+ {myPackages
93
+ ?.sort(
94
+ (a, b) =>
95
+ new Date(b.created_at).getTime() -
96
+ new Date(a.created_at).getTime(),
97
+ )
98
+ .slice(0, 4)
99
+ .map((pkg) => (
100
+ <Link
101
+ key={pkg.package_id}
102
+ href={`/editor?package_id=${pkg.package_id}`}
103
+ >
104
+ <Card className="hover:shadow-md border bg-white shadow-sm transition-shadow flex flex-col h-full rounded-md">
105
+ <CardHeader>
106
+ <CardTitle className="text-lg font-semibold text-slate-900">
107
+ {pkg.unscoped_name}
108
+ </CardTitle>
109
+ </CardHeader>
110
+ <CardContent className="-mt-4">
111
+ <p className="text-sm text-slate-500">
112
+ Last edited{" "}
113
+ {new Date(pkg.updated_at).toLocaleDateString()}
114
+ </p>
115
+ </CardContent>
116
+ </Card>
117
+ </Link>
118
+ ))}
119
+ </div>
120
+ )}
121
+ </div>
122
+ )}
86
123
 
87
- <div className="mb-8">
88
- <h2 className="text-xl font-semibold mb-4">Start Blank Packages</h2>
89
- <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
124
+ <div className="mb-16">
125
+ <div className="mb-8">
126
+ <h2 className="text-2xl font-semibold text-slate-900">
127
+ Start Blank
128
+ </h2>
129
+ </div>
130
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
90
131
  {blankTemplates.map((template, index) => (
91
- <PrefetchPageLink
132
+ <Link
92
133
  key={index}
93
134
  href={
94
135
  template.disabled
@@ -100,90 +141,81 @@ export const QuickstartPage = () => {
100
141
  >
101
142
  <Card
102
143
  className={cn(
103
- "hover:shadow-md transition-shadow rounded-md h-full flex flex-col",
144
+ "hover:shadow-md border bg-white transition-shadow h-full flex flex-col rounded-md",
104
145
  template.disabled && "opacity-50 cursor-not-allowed",
105
146
  )}
106
147
  >
107
- <CardHeader className="p-4 flex-grow flex flex-col justify-between">
108
- <CardTitle className="text-md">{template.name}</CardTitle>
109
- <div className="mt-2 flex">
110
- <TypeBadge type={template.type as any} />
148
+ <CardHeader className="p-6 flex-grow flex flex-col justify-between">
149
+ <div>
150
+ <CardTitle className="text-lg font-semibold text-slate-900 mb-3">
151
+ {template.name}
152
+ </CardTitle>
153
+ <div className="flex justify-between items-center">
154
+ <TypeBadge type={template.type as any} />
155
+ </div>
111
156
  </div>
112
157
  </CardHeader>
113
158
  </Card>
114
- </PrefetchPageLink>
159
+ </Link>
115
160
  ))}
116
161
  </div>
117
162
  </div>
118
163
 
119
- <div className="mt-12">
120
- <h2 className="text-xl font-semibold mb-4">Import as Package</h2>
121
- <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4">
122
- {/* {[
123
- { name: "KiCad Footprint", type: "footprint" },
124
- { name: "KiCad Project", type: "board" },
125
- { name: "KiCad Module", type: "package" },
126
- ].map((template, index) => (
127
- <Card
128
- key={index}
129
- className="hover:shadow-md transition-shadow rounded-md opacity-50 flex flex-col"
130
- >
131
- <CardHeader className="p-4 pb-0">
132
- <CardTitle className="text-lg flex items-center justify-between">
133
- {template.name}
134
- <TypeBadge type={template.type as any} className="ml-2" />
164
+ <div className="mb-16">
165
+ <div className="mb-8">
166
+ <h2 className="text-2xl font-semibold text-slate-900">
167
+ Import Components
168
+ </h2>
169
+ </div>
170
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
171
+ <Card className="hover:shadow-md border bg-white transition-shadow flex flex-col rounded-md">
172
+ <CardHeader className="p-6 pb-4">
173
+ <div className="flex items-center justify-between mb-2">
174
+ <CardTitle className="text-lg font-semibold text-slate-900">
175
+ JLCPCB Component
135
176
  </CardTitle>
136
- </CardHeader>
137
- <CardContent className="p-4 mt-auto">
138
- <Button
139
- className="w-full"
140
- onClick={() => {
141
- toastNotImplemented("Kicad Imports")
142
- }}
143
- >
144
- Import {template.name}
145
- </Button>
146
- </CardContent>
147
- </Card>
148
- ))} */}
149
- <Card className="hover:shadow-md transition-shadow rounded-md flex flex-col">
150
- <CardHeader className="p-4 pb-0">
151
- <CardTitle className="text-lg flex items-center justify-between">
152
- JLCPCB Component
153
- <TypeBadge type="package" className="ml-2" />
154
- </CardTitle>
177
+ <TypeBadge type="package" />
178
+ </div>
179
+ <p className="text-sm text-slate-500">
180
+ Import components from JLCPCB library
181
+ </p>
155
182
  </CardHeader>
156
- <CardContent className="p-4 mt-auto">
183
+ <CardContent className="p-6 pt-0 mt-auto">
157
184
  <Button
158
- className="w-full"
159
- onClick={() => setIsJLCPCBDialogOpen(true)}
185
+ className="w-full text-white"
186
+ onClick={() => openImportDialog()}
160
187
  >
161
- Import JLCPCB Component
188
+ Import JLCPCB
162
189
  </Button>
163
190
  </CardContent>
164
191
  </Card>
165
- <Card className="hover:shadow-md transition-shadow rounded-md flex flex-col">
166
- <CardHeader className="p-4 pb-0">
167
- <CardTitle className="text-lg flex items-center justify-between">
168
- Circuit Json
169
- <TypeBadge type="package" className="ml-2" />
170
- </CardTitle>
192
+ <Card className="hover:shadow-md border bg-white transition-shadow flex flex-col rounded-md">
193
+ <CardHeader className="p-6 pb-4">
194
+ <div className="flex items-center justify-between mb-2">
195
+ <CardTitle className="text-lg font-semibold text-slate-900">
196
+ Circuit JSON
197
+ </CardTitle>
198
+ <TypeBadge type="package" />
199
+ </div>
200
+ <p className="text-sm text-slate-500">
201
+ Import from Circuit JSON format
202
+ </p>
171
203
  </CardHeader>
172
- <CardContent className="p-4 mt-auto">
204
+ <CardContent className="p-6 pt-0 mt-auto">
173
205
  <Button
174
- className="w-full"
206
+ className="w-full text-white"
175
207
  onClick={() => setIsCircuitJsonImportDialogOpen(true)}
176
208
  >
177
- Import Circuit JSON
209
+ Import JSON
178
210
  </Button>
179
211
  </CardContent>
180
212
  </Card>
181
213
  </div>
182
214
  </div>
183
215
 
184
- <JLCPCBImportDialog
185
- open={isJLCPCBDialogOpen}
186
- onOpenChange={setIsJLCPCBDialogOpen}
216
+ <ImportComponentDialog
217
+ onJlcpcbComponentTsxLoaded={handleJlcpcbComponentSelected}
218
+ jlcpcbProxyRequestHeaders={jlcpcbProxyRequestHeaders}
187
219
  />
188
220
 
189
221
  <CircuitJsonImportDialog
@@ -191,27 +223,31 @@ export const QuickstartPage = () => {
191
223
  onOpenChange={setIsCircuitJsonImportDialogOpen}
192
224
  />
193
225
 
194
- <div>
195
- <h2 className="text-xl font-semibold mb-4 mt-12">
196
- Start from a Template
197
- </h2>
198
- <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
226
+ <div className="mb-16">
227
+ <div className="mb-8">
228
+ <h2 className="text-2xl font-semibold text-slate-900">
229
+ Popular Templates
230
+ </h2>
231
+ </div>
232
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
199
233
  {templates.map((template, index) => (
200
- <PrefetchPageLink
234
+ <Link
201
235
  key={index}
202
236
  href={`/editor?template=${template.name
203
237
  .toLowerCase()
204
238
  .replace(/ /g, "-")}`}
205
239
  >
206
- <Card className="hover:shadow-md transition-shadow rounded-md">
207
- <CardHeader className="p-4">
208
- <CardTitle className="text-lg flex items-center justify-between">
209
- {template.name}
210
- <TypeBadge type={template.type as any} className="ml-2" />
211
- </CardTitle>
240
+ <Card className="hover:shadow-md border bg-white transition-shadow rounded-md">
241
+ <CardHeader className="p-6">
242
+ <div className="flex items-center justify-between ">
243
+ <CardTitle className="text-lg font-semibold text-slate-900">
244
+ {template.name}
245
+ </CardTitle>
246
+ <TypeBadge type={template.type as any} />
247
+ </div>
212
248
  </CardHeader>
213
249
  </Card>
214
- </PrefetchPageLink>
250
+ </Link>
215
251
  ))}
216
252
  </div>
217
253
  </div>
@@ -6,10 +6,14 @@ import { usePackageBuild } from "@/hooks/use-package-builds"
6
6
  import { ConnectedRepoOverview } from "@/components/preview/ConnectedRepoOverview"
7
7
  import Header from "@/components/Header"
8
8
  import { Badge } from "@/components/ui/badge"
9
- import { Calendar, GitBranch } from "lucide-react"
10
- import { useState } from "react"
9
+ import { Button } from "@/components/ui/button"
10
+ import { Calendar, GitBranch, RefreshCw } from "lucide-react"
11
11
  import { formatTimeAgo } from "@/lib/utils/formatTimeAgo"
12
12
  import { PackageBreadcrumb } from "@/components/PackageBreadcrumb"
13
+ import { usePackageReleaseImages } from "@/hooks/use-package-release-images"
14
+ import { Skeleton } from "@/components/ui/skeleton"
15
+ import { useRebuildPackageReleaseMutation } from "@/hooks/use-rebuild-package-release-mutation"
16
+ import { useGlobalStore } from "@/hooks/use-global-store"
13
17
 
14
18
  export default function ReleaseDetailPage() {
15
19
  const params = useParams<{
@@ -24,8 +28,6 @@ export default function ReleaseDetailPage() {
24
28
  ? `${params.author}/${params.packageName}`
25
29
  : null
26
30
 
27
- const [copied, setCopied] = useState(false)
28
-
29
31
  const {
30
32
  data: pkg,
31
33
  isLoading: isLoadingPackage,
@@ -39,16 +41,58 @@ export default function ReleaseDetailPage() {
39
41
  data: packageRelease,
40
42
  isLoading: isLoadingRelease,
41
43
  error: releaseError,
42
- } = usePackageReleaseByIdOrVersion(releaseIdOrVersion, packageName)
44
+ } = usePackageReleaseByIdOrVersion(releaseIdOrVersion, packageName, {
45
+ include_logs: true,
46
+ })
43
47
 
44
48
  const {
45
49
  data: latestBuild,
46
50
  isLoading: isLoadingBuild,
47
51
  error: buildError,
48
- } = usePackageBuild(packageRelease?.latest_package_build_id ?? null)
52
+ } = usePackageBuild(packageRelease?.latest_package_build_id ?? null, {
53
+ include_logs: true,
54
+ })
55
+
56
+ const { availableViews } = usePackageReleaseImages({
57
+ packageReleaseId: packageRelease?.package_release_id,
58
+ })
59
+
60
+ const session = useGlobalStore((s) => s.session)
61
+ const { mutate: rebuildPackage, isLoading: isRebuildLoading } =
62
+ useRebuildPackageReleaseMutation()
49
63
 
50
64
  if (isLoadingPackage || isLoadingRelease) {
51
- return null
65
+ return (
66
+ <>
67
+ <Header />
68
+ <div className="min-h-screen bg-white">
69
+ {/* Page Header Skeleton */}
70
+ <div className="bg-gray-50 border-b py-6">
71
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
72
+ <Skeleton className="h-6 w-64 mb-4" />
73
+ <div className="flex items-center gap-4">
74
+ <Skeleton className="h-4 w-20" />
75
+ <Skeleton className="h-4 w-32" />
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ {/* Images Skeleton */}
81
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
82
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
83
+ {[1, 2, 3].map((i) => (
84
+ <Skeleton key={i} className="h-48 rounded-lg" />
85
+ ))}
86
+ </div>
87
+ </div>
88
+
89
+ {/* Main Content Skeleton */}
90
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
91
+ <Skeleton className="h-64 w-full" />
92
+ </div>
93
+ </div>
94
+ </>
95
+ )
52
96
  }
53
97
 
54
98
  if (packageError?.status === 404 || !pkg) {
@@ -65,7 +109,7 @@ export default function ReleaseDetailPage() {
65
109
  <div className="min-h-screen bg-white">
66
110
  {/* Page Header */}
67
111
  <div className="bg-gray-50 border-b py-6">
68
- <div className="max-w-7xl lg:flex lg:justify-between mx-auto px-4 sm:px-6 lg:px-8">
112
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
69
113
  {/* Breadcrumb */}
70
114
  <PackageBreadcrumb
71
115
  author={pkg.owner_github_username || ""}
@@ -78,35 +122,79 @@ export default function ReleaseDetailPage() {
78
122
  />
79
123
 
80
124
  {/* Header Content */}
81
- <div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-2">
82
- <div className="flex-1">
83
- <div className="flex items-center gap-4 text-sm text-gray-600">
84
- {packageRelease.is_pr_preview && (
85
- <a
86
- href={`https://github.com/${pkg.github_repo_full_name}/pull/${packageRelease.github_pr_number}`}
87
- target="_blank"
88
- rel="noopener noreferrer"
89
- >
90
- <div className="flex items-center gap-1">
91
- <GitBranch className="w-4 h-4" />
92
- <Badge variant="outline" className="text-xs">
93
- PR #{packageRelease.github_pr_number}
94
- </Badge>
95
- </div>
96
- </a>
97
- )}
98
- <div className="flex items-center gap-1">
99
- <Calendar className="w-4 h-4" />
100
- <span>
101
- Created {formatTimeAgo(packageRelease.created_at)}
102
- </span>
103
- </div>
125
+ <div className="flex flex-wrap items-center justify-between gap-3 mt-4">
126
+ <div className="flex flex-wrap items-center gap-4 text-sm text-gray-600">
127
+ {packageRelease.is_pr_preview && (
128
+ <a
129
+ href={`https://github.com/${pkg.github_repo_full_name}/pull/${packageRelease.github_pr_number}`}
130
+ target="_blank"
131
+ rel="noopener noreferrer"
132
+ className="flex items-center gap-1 hover:text-gray-800 transition-colors"
133
+ >
134
+ <GitBranch className="w-4 h-4" />
135
+ <Badge variant="outline" className="text-xs">
136
+ PR #{packageRelease.github_pr_number}
137
+ </Badge>
138
+ </a>
139
+ )}
140
+ <div className="flex items-center gap-1">
141
+ <Calendar className="w-4 h-4" />
142
+ <span>
143
+ Created {formatTimeAgo(packageRelease.created_at)}
144
+ </span>
104
145
  </div>
105
146
  </div>
147
+
148
+ {/* Rebuild Button */}
149
+ {session?.github_username === pkg.owner_github_username && (
150
+ <Button
151
+ variant="outline"
152
+ size="sm"
153
+ className="border-gray-300 bg-white hover:bg-gray-50 flex-shrink-0"
154
+ disabled={isRebuildLoading || !packageRelease}
155
+ onClick={() =>
156
+ packageRelease &&
157
+ rebuildPackage({
158
+ package_release_id: packageRelease.package_release_id,
159
+ })
160
+ }
161
+ >
162
+ <RefreshCw
163
+ className={`w-4 h-4 mr-2 ${isRebuildLoading ? "animate-spin" : ""}`}
164
+ />
165
+ {isRebuildLoading ? "Rebuilding..." : "Rebuild"}
166
+ </Button>
167
+ )}
106
168
  </div>
107
169
  </div>
108
170
  </div>
109
171
 
172
+ {/* Images Section - Always show with skeletons while loading */}
173
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
174
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
175
+ {availableViews.length > 0
176
+ ? availableViews.map((view) => (
177
+ <div
178
+ key={view.id}
179
+ className="flex items-center justify-center border rounded-lg bg-gray-50 overflow-hidden h-48"
180
+ >
181
+ {view.isLoading ? (
182
+ <Skeleton className="w-full h-full" />
183
+ ) : (
184
+ <img
185
+ src={view.imageUrl}
186
+ alt={`${view.label} preview`}
187
+ className={`w-full h-full object-contain ${view.label.toLowerCase() == "pcb" ? "bg-black" : view.label.toLowerCase() == "schematic" ? "bg-[#F5F1ED]" : "bg-gray-100"}`}
188
+ />
189
+ )}
190
+ </div>
191
+ ))
192
+ : [1, 2, 3].map((i) => (
193
+ <Skeleton key={i} className="h-48 rounded-lg" />
194
+ ))}
195
+ </div>
196
+ </div>
197
+
110
198
  {/* Main Content */}
111
199
  <ConnectedRepoOverview
112
200
  packageBuild={latestBuild ?? null}