@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
@@ -1,7 +1,6 @@
1
1
  import { Button } from "@/components/ui/button"
2
- import { CircuitBoard, Search, Menu, X } from "lucide-react"
2
+ import { Search, Menu, X } from "lucide-react"
3
3
  import { Link } from "wouter"
4
- import { PrefetchPageLink } from "./PrefetchPageLink"
5
4
  import { HeaderLogin } from "./HeaderLogin"
6
5
  import SearchComponent from "./SearchComponent"
7
6
  import HeaderDropdown from "./HeaderDropdown"
@@ -60,36 +59,28 @@ export const Header2 = () => {
60
59
  <>
61
60
  <header className="sticky top-0 z-50 w-full border-b bg-white md:bg-white/80 md:backdrop-blur supports-[backdrop-filter]:md:bg-white/60">
62
61
  <div className="container mx-auto flex h-16 items-center justify-between px-2 md:px-6">
63
- <PrefetchPageLink
64
- href="/"
65
- className="flex select-none items-center gap-2"
66
- >
67
- <CircuitBoard className="h-6 w-6" />
68
- <span className="text-lg font-bold">tscircuit</span>
69
- </PrefetchPageLink>
62
+ <Link href="/" className="flex select-none items-center">
63
+ <span className="bg-blue-500 px-2 py-1 rounded-md text-white text-sm font-semibold sm:text-base">
64
+ tscircuit
65
+ </span>
66
+ </Link>
70
67
 
71
68
  {/* Desktop Navigation */}
72
69
  <nav className="hidden md:flex gap-6">
73
70
  {isLoggedIn && (
74
- <PrefetchPageLink
71
+ <Link
75
72
  className="text-sm font-medium hover:underline underline-offset-4"
76
73
  href="/dashboard"
77
74
  >
78
75
  Dashboard
79
- </PrefetchPageLink>
76
+ </Link>
80
77
  )}
81
- <PrefetchPageLink
78
+ <Link
82
79
  className="text-sm font-medium hover:underline underline-offset-4"
83
80
  href="/quickstart"
84
81
  >
85
82
  Editor
86
- </PrefetchPageLink>
87
- <PrefetchPageLink
88
- className="text-sm font-medium hover:underline underline-offset-4"
89
- href="/datasheets"
90
- >
91
- Datasheets
92
- </PrefetchPageLink>
83
+ </Link>
93
84
  <a
94
85
  className="text-sm font-medium hover:underline underline-offset-4"
95
86
  href="https://docs.tscircuit.com"
@@ -131,11 +122,11 @@ export const Header2 = () => {
131
122
  {isLoggedIn ? (
132
123
  <HeaderLogin />
133
124
  ) : (
134
- <PrefetchPageLink href="/quickstart">
125
+ <Link href="/quickstart">
135
126
  <Button size="sm" className="text-xs px-3 py-1">
136
127
  Get Started
137
128
  </Button>
138
- </PrefetchPageLink>
129
+ </Link>
139
130
  )}
140
131
  <button
141
132
  className="p-2"
@@ -154,28 +145,21 @@ export const Header2 = () => {
154
145
  <nav className="mb-4">
155
146
  <div className="flex flex-col items-center gap-1">
156
147
  {isLoggedIn && (
157
- <PrefetchPageLink
148
+ <Link
158
149
  className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
159
150
  href="/dashboard"
160
151
  onClick={() => setMobileMenuOpen(false)}
161
152
  >
162
153
  Dashboard
163
- </PrefetchPageLink>
154
+ </Link>
164
155
  )}
165
- <PrefetchPageLink
156
+ <Link
166
157
  className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
167
158
  href="/quickstart"
168
159
  onClick={() => setMobileMenuOpen(false)}
169
160
  >
170
161
  Editor
171
- </PrefetchPageLink>
172
- <PrefetchPageLink
173
- className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
174
- href="/datasheets"
175
- onClick={() => setMobileMenuOpen(false)}
176
- >
177
- Datasheets
178
- </PrefetchPageLink>
162
+ </Link>
179
163
  <a
180
164
  className="text-sm font-medium hover:underline underline-offset-4 py-2 w-full text-center"
181
165
  href="https://docs.tscircuit.com"
@@ -1,3 +1,4 @@
1
+ import { useState } from "react"
1
2
  import { Button } from "@/components/ui/button"
2
3
  import {
3
4
  DropdownMenu,
@@ -7,9 +8,10 @@ import {
7
8
  } from "@/components/ui/dropdown-menu"
8
9
  import { cn } from "@/lib/utils"
9
10
  import { ChevronDown, FileUp, Upload, Zap } from "lucide-react"
10
- import { PrefetchPageLink } from "./PrefetchPageLink"
11
+ import { Link } from "wouter"
11
12
 
12
13
  export default function HeaderDropdown() {
14
+ const [open, setOpen] = useState(false)
13
15
  const blankTemplates = [
14
16
  { name: "Blank Circuit Board", type: "board", badgeColor: "bg-blue-500" },
15
17
  {
@@ -32,7 +34,7 @@ export default function HeaderDropdown() {
32
34
  ]
33
35
 
34
36
  return (
35
- <DropdownMenu>
37
+ <DropdownMenu open={open} onOpenChange={setOpen}>
36
38
  <DropdownMenuTrigger asChild>
37
39
  <Button
38
40
  size="sm"
@@ -44,38 +46,41 @@ export default function HeaderDropdown() {
44
46
  </DropdownMenuTrigger>
45
47
  <DropdownMenuContent className="w-fit">
46
48
  <DropdownMenuItem asChild>
47
- <PrefetchPageLink
49
+ <Link
48
50
  href="/quickstart"
49
51
  className="flex items-center cursor-pointer"
52
+ onClick={() => setOpen(false)}
50
53
  >
51
54
  <Zap className="mr-2 h-3 w-3" />
52
55
  Quickstart Templates
53
- </PrefetchPageLink>
56
+ </Link>
54
57
  </DropdownMenuItem>
55
58
  {blankTemplates.map((template, index) => (
56
59
  <DropdownMenuItem key={index} asChild disabled={template.disabled}>
57
- <a
60
+ <Link
58
61
  href={`/editor?template=${template.name.toLowerCase().replace(/ /g, "-")}`}
59
62
  className={cn(
60
63
  "flex items-center cursor-pointer",
61
64
  template.disabled && "opacity-50 cursor-not-allowed",
62
65
  )}
66
+ onClick={() => !template.disabled && setOpen(false)}
63
67
  >
64
68
  <span
65
69
  className={`w-2 h-2 rounded-full mr-2 ${template.badgeColor}`}
66
70
  />
67
71
  {template.name}
68
- </a>
72
+ </Link>
69
73
  </DropdownMenuItem>
70
74
  ))}
71
75
  <DropdownMenuItem asChild>
72
- <PrefetchPageLink
76
+ <Link
73
77
  href="/quickstart"
74
78
  className="flex items-center cursor-pointer"
79
+ onClick={() => setOpen(false)}
75
80
  >
76
81
  <Upload className="mr-2 h-3 w-3" />
77
82
  Import Part
78
- </PrefetchPageLink>
83
+ </Link>
79
84
  </DropdownMenuItem>
80
85
  </DropdownMenuContent>
81
86
  </DropdownMenu>
@@ -1,4 +1,3 @@
1
- import React from "react"
2
1
  import { Button } from "@/components/ui/button"
3
2
  import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
4
3
  import {
@@ -9,15 +8,16 @@ import {
9
8
  } from "@/components/ui/dropdown-menu"
10
9
  import { User } from "lucide-react"
11
10
  import { useGlobalStore } from "@/hooks/use-global-store"
12
- import { useAccountBalance } from "@/hooks/use-account-balance"
13
11
  import { useSignIn } from "@/hooks/use-sign-in"
12
+ import { Link } from "wouter"
13
+ import { useState } from "react"
14
14
 
15
15
  export const HeaderLogin = () => {
16
16
  const session = useGlobalStore((s) => s.session)
17
17
  const isLoggedIn = Boolean(session)
18
18
  const setSession = useGlobalStore((s) => s.setSession)
19
19
  const signIn = useSignIn()
20
- const { data: accountBalance } = useAccountBalance()
20
+ const [isOpen, setIsOpen] = useState(false)
21
21
 
22
22
  if (!isLoggedIn) {
23
23
  return (
@@ -31,7 +31,7 @@ export const HeaderLogin = () => {
31
31
  }
32
32
 
33
33
  return (
34
- <DropdownMenu>
34
+ <DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
35
35
  <DropdownMenuTrigger asChild>
36
36
  <Avatar className="w-8 h-8 login-avatar">
37
37
  <AvatarImage
@@ -43,33 +43,61 @@ export const HeaderLogin = () => {
43
43
  </AvatarFallback>
44
44
  </Avatar>
45
45
  </DropdownMenuTrigger>
46
- <DropdownMenuContent className="ml-1 md:ml-0 md:mr-1">
47
- <DropdownMenuItem asChild className="text-gray-500 text-xs" disabled>
46
+ <DropdownMenuContent className="ml-1 mr-1 md:ml-0 md:mr-1">
47
+ {/* <DropdownMenuItem asChild className="text-gray-500 text-xs" disabled>
48
48
  <div>
49
49
  AI Usage $
50
50
  {accountBalance?.monthly_ai_budget_used_usd.toFixed(2) ?? "0.00"} /
51
51
  $5.00
52
52
  </div>
53
- </DropdownMenuItem>
53
+ </DropdownMenuItem> */}
54
54
  <DropdownMenuItem asChild>
55
- <a href={`/${session?.github_username}`} className="cursor-pointer">
55
+ <Link
56
+ href={`/${session?.github_username}`}
57
+ className="cursor-pointer"
58
+ onClick={() => setIsOpen(false)}
59
+ >
56
60
  My Profile
57
- </a>
61
+ </Link>
58
62
  </DropdownMenuItem>
59
63
  <DropdownMenuItem asChild>
60
- <a href="/dashboard" className="cursor-pointer">
64
+ <Link
65
+ href="/dashboard"
66
+ className="cursor-pointer"
67
+ onClick={() => setIsOpen(false)}
68
+ >
61
69
  Dashboard
62
- </a>
70
+ </Link>
63
71
  </DropdownMenuItem>
64
72
  <DropdownMenuItem asChild>
65
- <a href="/settings" className="cursor-pointer">
73
+ <Link
74
+ href={`/${session?.github_username}/settings`}
75
+ className="cursor-pointer"
76
+ onClick={() => setIsOpen(false)}
77
+ >
66
78
  Settings
67
- </a>
79
+ </Link>
80
+ </DropdownMenuItem>
81
+ <DropdownMenuItem asChild>
82
+ <Link
83
+ href="/orgs/new"
84
+ className="cursor-pointer"
85
+ onClick={() => setIsOpen(false)}
86
+ >
87
+ Create Organization
88
+ </Link>
68
89
  </DropdownMenuItem>
69
- <DropdownMenuItem asChild onClick={() => setSession(null)}>
70
- <a href="/" className="cursor-pointer">
90
+ <DropdownMenuItem asChild>
91
+ <Link
92
+ href="/"
93
+ className="cursor-pointer"
94
+ onClick={() => {
95
+ setSession(null)
96
+ setIsOpen(false)
97
+ }}
98
+ >
71
99
  Sign Out
72
- </a>
100
+ </Link>
73
101
  </DropdownMenuItem>
74
102
  </DropdownMenuContent>
75
103
  </DropdownMenu>
@@ -1,5 +1,3 @@
1
- "use client"
2
-
3
1
  import {
4
2
  DropdownMenu,
5
3
  DropdownMenuContent,
@@ -1,5 +1,5 @@
1
1
  import { Button } from "@/components/ui/button"
2
- import { PrefetchPageLink } from "@/components/PrefetchPageLink"
2
+ import { Link } from "wouter"
3
3
 
4
4
  export function NotFound({ heading = "Page not found" }: { heading?: string }) {
5
5
  return (
@@ -20,16 +20,16 @@ export function NotFound({ heading = "Page not found" }: { heading?: string }) {
20
20
  address.
21
21
  </p>
22
22
  <div className="flex flex-col sm:flex-row gap-4">
23
- <PrefetchPageLink href="/">
23
+ <Link href="/">
24
24
  <Button size="lg" className="bg-blue-500 hover:bg-blue-600">
25
25
  Return Home
26
26
  </Button>
27
- </PrefetchPageLink>
28
- <PrefetchPageLink href="/search">
27
+ </Link>
28
+ <Link href="/search">
29
29
  <Button size="lg" variant="outline">
30
30
  Search Packages
31
31
  </Button>
32
- </PrefetchPageLink>
32
+ </Link>
33
33
  </div>
34
34
  </div>
35
35
  </section>
@@ -1,4 +1,4 @@
1
- import { PrefetchPageLink } from "@/components/PrefetchPageLink"
1
+ import { Link } from "wouter"
2
2
  import {
3
3
  Breadcrumb,
4
4
  BreadcrumbList,
@@ -29,7 +29,7 @@ export function PackageBreadcrumb({
29
29
  {/* Author */}
30
30
  <BreadcrumbItem>
31
31
  <BreadcrumbLink asChild>
32
- <PrefetchPageLink href={`/${author}`}>{author}</PrefetchPageLink>
32
+ <Link href={`/${author}`}>{author}</Link>
33
33
  </BreadcrumbLink>
34
34
  </BreadcrumbItem>
35
35
  <BreadcrumbSeparator>
@@ -39,9 +39,7 @@ export function PackageBreadcrumb({
39
39
  {/* Package */}
40
40
  <BreadcrumbItem>
41
41
  <BreadcrumbLink asChild>
42
- <PrefetchPageLink href={`/${packageName}`}>
43
- {unscopedName || packageName}
44
- </PrefetchPageLink>
42
+ <Link href={`/${packageName}`}>{unscopedName || packageName}</Link>
45
43
  </BreadcrumbLink>
46
44
  </BreadcrumbItem>
47
45
  <BreadcrumbSeparator>
@@ -58,9 +56,7 @@ export function PackageBreadcrumb({
58
56
  <BreadcrumbPage>releases</BreadcrumbPage>
59
57
  ) : (
60
58
  <BreadcrumbLink asChild>
61
- <PrefetchPageLink href={`/${packageName}/releases`}>
62
- releases
63
- </PrefetchPageLink>
59
+ <Link href={`/${packageName}/releases`}>releases</Link>
64
60
  </BreadcrumbLink>
65
61
  )}
66
62
  </BreadcrumbItem>
@@ -76,11 +72,9 @@ export function PackageBreadcrumb({
76
72
  <BreadcrumbItem>
77
73
  {currentPage === "builds" ? (
78
74
  <BreadcrumbLink asChild>
79
- <PrefetchPageLink
80
- href={`/${packageName}/releases/${releaseVersion}`}
81
- >
75
+ <Link href={`/${packageName}/releases/${releaseVersion}`}>
82
76
  {releaseVersion}
83
- </PrefetchPageLink>
77
+ </Link>
84
78
  </BreadcrumbLink>
85
79
  ) : (
86
80
  <BreadcrumbPage>{releaseVersion}</BreadcrumbPage>
@@ -19,7 +19,6 @@ import {
19
19
  } from "@/components/ui/dropdown-menu"
20
20
  import { SnippetType, SnippetTypeIcon } from "./SnippetTypeIcon"
21
21
  import { timeAgo } from "@/lib/utils/timeAgo"
22
- import { ImageWithFallback } from "./ImageWithFallback"
23
22
  import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
24
23
 
25
24
  export interface PackageCardProps {
@@ -28,7 +28,7 @@ const PackageGrid = ({
28
28
  </div>
29
29
  )
30
30
 
31
- const LoadingState = () => (
31
+ export const LoadingState = () => (
32
32
  <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
33
33
  {[...Array(6)].map((_, i) => (
34
34
  <PackageCardSkeleton key={i} />
@@ -101,7 +101,13 @@ export const PrefetchPageLink = ({
101
101
  }, [inView, href])
102
102
 
103
103
  return (
104
- <Link {...props} href={href} className={className} ref={ref}>
104
+ <Link
105
+ {...props}
106
+ href={href}
107
+ className={className}
108
+ draggable={false}
109
+ ref={ref}
110
+ >
105
111
  {children}
106
112
  </Link>
107
113
  )
@@ -0,0 +1,32 @@
1
+ import React from "react"
2
+ import { useParams } from "wouter"
3
+ import { useOrgByGithubHandle } from "@/hooks/use-org-by-github-handle"
4
+ import { OrganizationProfilePageContent } from "@/pages/organization-profile"
5
+ import { UserProfilePage } from "@/pages/user-profile"
6
+ import NotFoundPage from "@/pages/404"
7
+ import { FullPageLoader } from "@/App"
8
+
9
+ const ProfileRouter: React.FC = () => {
10
+ const { username } = useParams()
11
+ const {
12
+ data: organization,
13
+ isLoading,
14
+ error,
15
+ } = useOrgByGithubHandle(username || null)
16
+
17
+ if (!username) {
18
+ return <NotFoundPage heading="Username Not Provided" />
19
+ }
20
+
21
+ if (isLoading) {
22
+ return <FullPageLoader />
23
+ }
24
+
25
+ if (organization && !organization.is_personal_org && !error) {
26
+ return <OrganizationProfilePageContent org={organization} />
27
+ }
28
+
29
+ return <UserProfilePage />
30
+ }
31
+
32
+ export default ProfileRouter
@@ -5,7 +5,7 @@ import React, { useEffect, useRef, useState } from "react"
5
5
  import { useQuery } from "react-query"
6
6
  import { Alert } from "./ui/alert"
7
7
  import { useApiBaseUrl } from "@/hooks/use-packages-base-api-url"
8
- import { PrefetchPageLink } from "./PrefetchPageLink"
8
+ import { Link } from "wouter"
9
9
  import { CircuitBoard } from "lucide-react"
10
10
  import { cn } from "@/lib/utils"
11
11
 
@@ -42,9 +42,9 @@ const LinkWithNewTabHandling = ({
42
42
  )
43
43
  }
44
44
  return (
45
- <PrefetchPageLink onClick={onClick} className={className} href={href}>
45
+ <Link onClick={onClick} className={className} href={href}>
46
46
  {children}
47
- </PrefetchPageLink>
47
+ </Link>
48
48
  )
49
49
  }
50
50
 
@@ -60,7 +60,7 @@ const SearchComponent: React.FC<SearchComponentProps> = ({
60
60
  const resultsRef = useRef<HTMLDivElement>(null)
61
61
  const inputRef = useRef<HTMLInputElement>(null)
62
62
  const [location, setLocation] = useLocation()
63
- const snippetsBaseApiUrl = useApiBaseUrl()
63
+ const apiBaseUrl = useApiBaseUrl()
64
64
 
65
65
  const { data: searchResults, isLoading } = useQuery(
66
66
  ["packageSearch", searchQuery],
@@ -137,10 +137,13 @@ const SearchComponent: React.FC<SearchComponentProps> = ({
137
137
  <Input
138
138
  autoComplete="off"
139
139
  spellCheck={false}
140
+ autoCorrect="off"
141
+ autoCapitalize="off"
140
142
  ref={inputRef}
141
143
  type="search"
144
+ aria-autocomplete="none"
142
145
  placeholder="Search"
143
- className="pl-4 focus:border-blue-500 placeholder-gray-400 text-sm"
146
+ className="pl-4 focus:border-blue-500 placeholder-gray-400 text-sm select-none"
144
147
  value={searchQuery}
145
148
  onChange={(e) => {
146
149
  setSearchQuery(e.target.value)
@@ -195,10 +198,10 @@ const SearchComponent: React.FC<SearchComponentProps> = ({
195
198
  {showResults && searchResults && (
196
199
  <div
197
200
  ref={resultsRef}
198
- className="absolute top-full md:left-0 right-0 mt-2 bg-white shadow-lg rounded-md z-50 w-80 max-h-screen overflow-y-auto overflow-x-visible"
201
+ className="absolute top-full md:left-0 right-0 no-scrollbar mt-2 bg-white shadow-lg rounded-md z-50 w-80 max-h-screen overflow-y-auto overflow-x-visible"
199
202
  >
200
203
  {searchResults.length > 0 ? (
201
- <ul className="divide-y divide-gray-200">
204
+ <ul className="divide-y divide-gray-200 no-scrollbar">
202
205
  {searchResults.map((pkg: any, index: number) => (
203
206
  <li
204
207
  key={pkg.package_id}
@@ -222,8 +225,9 @@ const SearchComponent: React.FC<SearchComponentProps> = ({
222
225
  >
223
226
  <div className="w-12 h-12 overflow-hidden mr-2 flex-shrink-0 rounded-sm bg-gray-50 border flex items-center justify-center">
224
227
  <img
225
- src={`${snippetsBaseApiUrl}/snippets/images/${pkg.name}/pcb.svg`}
228
+ src={`${apiBaseUrl}/packages/images/${pkg.name}/pcb.svg`}
226
229
  alt={`PCB preview for ${pkg.name}`}
230
+ draggable={false}
227
231
  className="w-12 h-12 object-contain p-1 scale-[4] rotate-45"
228
232
  onError={(e) => {
229
233
  e.currentTarget.style.display = "none"
@@ -0,0 +1,80 @@
1
+ import React from "react"
2
+ import { Link } from "wouter"
3
+ import { Account } from "fake-snippets-api/lib/db/schema"
4
+ import { User } from "lucide-react"
5
+
6
+ export interface UserCardProps {
7
+ /** The account data to display */
8
+ account: Account | Omit<Account, "account_id">
9
+ /** Whether to render the card with a link to the user profile page */
10
+ withLink?: boolean
11
+ /** Custom class name for the card container */
12
+ className?: string
13
+ /** Custom onClick handler */
14
+ onClick?: (account: Account | Omit<Account, "account_id">) => void
15
+ }
16
+
17
+ export const UserCard: React.FC<UserCardProps> = ({
18
+ account,
19
+ withLink = true,
20
+ className = "",
21
+ onClick,
22
+ }) => {
23
+ const handleClick = () => {
24
+ if (onClick) {
25
+ onClick(account)
26
+ } else if (!withLink) {
27
+ window.location.href = `/${account.github_username}`
28
+ }
29
+ }
30
+
31
+ const cardContent = (
32
+ <div
33
+ className={`border p-4 rounded-md hover:shadow-md transition-shadow flex flex-col gap-4 cursor-pointer ${className}`}
34
+ onClick={!withLink ? handleClick : undefined}
35
+ >
36
+ <div className="flex items-start gap-4">
37
+ <div className="w-16 h-16 flex-shrink-0 rounded-md overflow-hidden bg-gray-50 border flex items-center justify-center">
38
+ <img
39
+ src={`https://github.com/${account.github_username}.png`}
40
+ alt={`${account.github_username} avatar`}
41
+ className="object-cover h-full w-full transition-transform duration-300 hover:scale-110"
42
+ onError={(e) => {
43
+ const target = e.target as HTMLImageElement
44
+ target.style.display = "none"
45
+ target.nextElementSibling?.classList.remove("hidden")
46
+ target.nextElementSibling?.classList.add("flex")
47
+ }}
48
+ />
49
+ <div className="hidden items-center justify-center h-full w-full">
50
+ <User className="w-6 h-6 text-gray-300" />
51
+ </div>
52
+ </div>
53
+ <div className="flex-1 min-w-0 flex flex-col justify-center my-auto">
54
+ <div className="flex justify-between items-start">
55
+ <h2 className="text-md font-semibold truncate pr-[30px]">
56
+ <span className="text-gray-900">{account.github_username}</span>
57
+ </h2>
58
+ </div>
59
+ <p className="text-sm text-gray-500 truncate max-w-xs">
60
+ @{account.github_username}
61
+ </p>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ )
66
+
67
+ if (withLink) {
68
+ return (
69
+ <Link
70
+ key={account.github_username}
71
+ href={`/${account.github_username}`}
72
+ onClick={onClick ? () => onClick(account) : undefined}
73
+ >
74
+ {cardContent}
75
+ </Link>
76
+ )
77
+ }
78
+
79
+ return cardContent
80
+ }
@@ -1,11 +1,17 @@
1
1
  import { useShikiHighlighter } from "@/hooks/use-shiki-highlighter"
2
- import { useEffect, useMemo } from "react"
3
- import { useQuery } from "react-query"
2
+ import { useMemo } from "react"
4
3
  import { Skeleton } from "@/components/ui/skeleton"
5
4
 
6
5
  // Pre-randomized array to avoid flickering on re-renders
7
- const SKELETON_WIDTHS = ["w-2/3", "w-1/4", "w-5/6", "w-1/3", "w-1/2", "w-3/4"]
8
-
6
+ export const SKELETON_WIDTHS = [
7
+ "w-2/3",
8
+ "w-1/4",
9
+ "w-5/6",
10
+ "w-1/3",
11
+ "w-1/2",
12
+ "w-3/4",
13
+ ]
14
+ const PLACEHOLDER_SHIKI_HTML = `<pre class="shiki vitesse-light" style="background-color:#ffffff;color:#393a34" tabindex="0"><code><span class="line"></span></code></pre>`
9
15
  export const ShikiCodeViewer = ({
10
16
  code,
11
17
  filePath,
@@ -24,17 +30,20 @@ export const ShikiCodeViewer = ({
24
30
  [filePath, code, highlighter],
25
31
  )
26
32
 
27
- if (!html) {
33
+ if (html && html?.trim() !== PLACEHOLDER_SHIKI_HTML) {
28
34
  return (
29
- <div className="text-sm p-4">
30
- {SKELETON_WIDTHS.map((w, i) => (
31
- <Skeleton key={i} className={`h-4 mb-2 ${w}`} />
32
- ))}
33
- </div>
35
+ <div
36
+ className="text-sm shiki"
37
+ dangerouslySetInnerHTML={{ __html: html }}
38
+ />
34
39
  )
35
40
  }
36
41
 
37
42
  return (
38
- <div className="text-sm shiki" dangerouslySetInnerHTML={{ __html: html }} />
43
+ <div className="text-sm p-4">
44
+ {SKELETON_WIDTHS.map((w, i) => (
45
+ <Skeleton key={i} className={`h-4 mb-2 ${w}`} />
46
+ ))}
47
+ </div>
39
48
  )
40
49
  }
@@ -14,7 +14,7 @@ export interface BuildStatusProps {
14
14
 
15
15
  export const BuildStatus = ({ step, packageReleaseId }: BuildStatusProps) => {
16
16
  const { author, packageName } = useParams()
17
- const href = `/${author}/${packageName}/builds?package_release_id=${packageReleaseId}`
17
+ const href = `/${author}/${packageName}/releases/${packageReleaseId}`
18
18
 
19
19
  return (
20
20
  <Link href={href} className="flex items-center gap-2">