@tscircuit/fake-snippets 0.0.66 → 0.0.68

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 (89) hide show
  1. package/bun-tests/fake-snippets-api/fixtures/get-circuit-json.ts +5 -143
  2. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +1 -4
  3. package/bun-tests/fake-snippets-api/fixtures/start-server.ts +7 -3
  4. package/bun-tests/fake-snippets-api/routes/order_quotes/create.test.ts +20 -56
  5. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +2 -2
  6. package/bun-tests/fake-snippets-api/routes/package_releases/update.test.ts +1 -1
  7. package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +0 -11
  8. package/bun.lock +26 -75
  9. package/dist/bundle.js +32 -39
  10. package/fake-snippets-api/routes/api/order_quotes/create.ts +30 -37
  11. package/fake-snippets-api/routes/api/order_quotes/get.ts +5 -8
  12. package/package.json +4 -4
  13. package/src/App.tsx +0 -11
  14. package/src/ContextProviders.tsx +2 -0
  15. package/src/components/CmdKMenu.tsx +19 -19
  16. package/src/components/DownloadButtonAndMenu.tsx +1 -4
  17. package/src/components/FAQ.tsx +3 -1
  18. package/src/components/FileSidebar.tsx +50 -1
  19. package/src/components/Footer.tsx +5 -2
  20. package/src/components/Header2.tsx +20 -9
  21. package/src/components/HeaderLogin.tsx +37 -54
  22. package/src/components/ImageWithFallback.tsx +37 -0
  23. package/src/components/JLCPCBImportDialog.tsx +45 -29
  24. package/src/components/PackageCard.tsx +2 -2
  25. package/src/components/{SnippetLink.tsx → PackageLink.tsx} +8 -16
  26. package/src/components/PackageSearchResults.tsx +87 -0
  27. package/src/components/PackagesList.tsx +3 -3
  28. package/src/components/PageSearchComponent.tsx +9 -9
  29. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +5 -28
  30. package/src/components/ViewPackagePage/components/important-files-view.tsx +1 -1
  31. package/src/components/ViewPackagePage/components/main-content-header.tsx +8 -8
  32. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +24 -14
  33. package/src/components/ViewPackagePage/components/package-header.tsx +7 -2
  34. package/src/components/dialogs/confirm-delete-package-dialog.tsx +8 -0
  35. package/src/components/dialogs/edit-package-details-dialog.tsx +145 -138
  36. package/src/components/package-port/CodeAndPreview.tsx +40 -19
  37. package/src/components/package-port/CodeEditor.tsx +21 -37
  38. package/src/components/package-port/CodeEditorHeader.tsx +1 -1
  39. package/src/components/package-port/EditorNav.tsx +3 -13
  40. package/src/hooks/use-global-store.ts +1 -0
  41. package/src/hooks/use-shiki-highlighter.ts +13 -6
  42. package/src/hooks/useFileManagement.ts +59 -0
  43. package/src/lib/download-fns/download-gltf.ts +3 -10
  44. package/src/lib/handleManualEditsImport.tsx +1 -1
  45. package/src/lib/types.ts +4 -2
  46. package/src/lib/utils/isValidFileName.ts +5 -0
  47. package/src/pages/dashboard.tsx +4 -4
  48. package/src/pages/editor.tsx +20 -14
  49. package/src/pages/latest.tsx +25 -26
  50. package/src/pages/quickstart.tsx +5 -5
  51. package/src/pages/search.tsx +121 -20
  52. package/src/pages/trending.tsx +14 -58
  53. package/src/pages/user-profile.tsx +14 -8
  54. package/bun-tests/fake-snippets-api/routes/snippets/add_star.test.ts +0 -84
  55. package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +0 -53
  56. package/bun-tests/fake-snippets-api/routes/snippets/delete.test.ts +0 -82
  57. package/bun-tests/fake-snippets-api/routes/snippets/download.test.ts +0 -90
  58. package/bun-tests/fake-snippets-api/routes/snippets/generate_from_jlcpcb.test.ts +0 -16
  59. package/bun-tests/fake-snippets-api/routes/snippets/get.test.ts +0 -163
  60. package/bun-tests/fake-snippets-api/routes/snippets/get_image.test.ts +0 -117
  61. package/bun-tests/fake-snippets-api/routes/snippets/images.test.ts +0 -114
  62. package/bun-tests/fake-snippets-api/routes/snippets/list.test.ts +0 -169
  63. package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +0 -50
  64. package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +0 -72
  65. package/bun-tests/fake-snippets-api/routes/snippets/remove_star.test.ts +0 -80
  66. package/bun-tests/fake-snippets-api/routes/snippets/search.test.ts +0 -75
  67. package/bun-tests/fake-snippets-api/routes/snippets/star-count.test.ts +0 -51
  68. package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +0 -175
  69. package/src/components/AiChatInterface.tsx +0 -229
  70. package/src/components/CodeAndPreview.tsx +0 -289
  71. package/src/components/CodeEditor.tsx +0 -539
  72. package/src/components/CodeEditorHeader.tsx +0 -135
  73. package/src/components/EditorNav.tsx +0 -502
  74. package/src/components/OrderPreviewContent.tsx +0 -61
  75. package/src/components/PreviewContent.tsx +0 -372
  76. package/src/components/SnippetCard.tsx +0 -159
  77. package/src/components/SnippetList.tsx +0 -71
  78. package/src/components/ViewSnippetSidebar.tsx +0 -162
  79. package/src/components/dialogs/create-order-dialog.tsx +0 -146
  80. package/src/hooks/use-compiled-tsx.ts +0 -37
  81. package/src/hooks/use-run-tsx/construct-circuit.tsx +0 -62
  82. package/src/hooks/use-run-tsx/index.tsx +0 -256
  83. package/src/hooks/use-save-snippet.ts +0 -66
  84. package/src/hooks/use-typecheck.ts +0 -54
  85. package/src/lib/utils/getSyntaxError.ts +0 -13
  86. package/src/pages/ai.tsx +0 -92
  87. package/src/pages/preview.tsx +0 -44
  88. package/src/pages/view-order.tsx +0 -111
  89. package/src/pages/view-snippet.tsx +0 -166
@@ -27,6 +27,8 @@ export function JLCPCBImportDialog({
27
27
  const [partNumber, setPartNumber] = useState("")
28
28
  const [isLoading, setIsLoading] = useState(false)
29
29
  const [error, setError] = useState<string | null>(null)
30
+ const [hasBeenImportedToAccountAlready, setHasBeenImportedToAccountAlready] =
31
+ useState<boolean>(false)
30
32
  const axios = useAxios()
31
33
  const { toast } = useToast()
32
34
  const [, navigate] = useLocation()
@@ -45,29 +47,18 @@ export function JLCPCBImportDialog({
45
47
 
46
48
  setIsLoading(true)
47
49
  setError(null)
50
+ setHasBeenImportedToAccountAlready(false)
51
+
48
52
  try {
49
- // Check that module doesn't already exist
50
- const existingSnippetRes = await axios.get(
51
- `/snippets/get?owner_name=${session?.github_username}&unscoped_name=${partNumber}`,
52
- {
53
- validateStatus: (status) => true,
54
- },
55
- )
53
+ const apiUrl = `/snippets/get?owner_name=${session?.github_username}&unscoped_name=${partNumber}`
56
54
 
57
- if (existingSnippetRes.status !== 404) {
58
- toast({
59
- title: "JLCPCB Part Already Imported",
60
- description: (
61
- <div>
62
- <PrefetchPageLink
63
- className="text-blue-500 hover:underline"
64
- href={`/editor?package_id=${existingSnippetRes.data.snippet.snippet_id}`}
65
- >
66
- View {partNumber}
67
- </PrefetchPageLink>
68
- </div>
69
- ),
70
- })
55
+ const existingPackageRes = await axios.get(apiUrl, {
56
+ validateStatus: (status) => true,
57
+ })
58
+
59
+ if (existingPackageRes.status !== 404) {
60
+ setHasBeenImportedToAccountAlready(true)
61
+ setIsLoading(false)
71
62
  return
72
63
  }
73
64
 
@@ -76,16 +67,20 @@ export function JLCPCBImportDialog({
76
67
  jlcpcb_part_number: partNumber,
77
68
  })
78
69
  .catch((e) => e)
70
+
79
71
  const { snippet, error } = response.data
72
+
80
73
  if (error) {
81
74
  setError(error.message)
82
75
  setIsLoading(false)
83
76
  return
84
77
  }
78
+
85
79
  toast({
86
80
  title: "Import Successful",
87
81
  description: "JLCPCB component has been imported successfully.",
88
82
  })
83
+
89
84
  onOpenChange(false)
90
85
  navigate(`/editor?package_id=${snippet.snippet_id}`)
91
86
  } catch (error) {
@@ -121,10 +116,17 @@ export function JLCPCBImportDialog({
121
116
  className="mt-3"
122
117
  placeholder="Enter JLCPCB part number (e.g., C46749)"
123
118
  value={partNumber}
124
- onChange={(e) => setPartNumber(e.target.value)}
119
+ onChange={(e) => {
120
+ setPartNumber(e.target.value)
121
+ setError(null)
122
+ setHasBeenImportedToAccountAlready(false)
123
+ }}
125
124
  />
126
- {error && <p className="bg-red-100 p-2 mt-2 pre-wrap">{error}</p>}
127
- {error && (
125
+ {error && !hasBeenImportedToAccountAlready && (
126
+ <p className="bg-red-100 p-2 mt-2 pre-wrap">{error}</p>
127
+ )}
128
+
129
+ {error && !hasBeenImportedToAccountAlready && (
128
130
  <div className="flex justify-end mt-2">
129
131
  <Button
130
132
  variant="default"
@@ -132,21 +134,35 @@ export function JLCPCBImportDialog({
132
134
  const issueTitle = `[${partNumber}] Failed to import from JLCPCB`
133
135
  const issueBody = `I tried to import the part number ${partNumber} from JLCPCB, but it failed. Here's the error I got:\n\`\`\`\n${error}\n\`\`\`\n\nCould be an issue in \`fetchEasyEDAComponent\` or \`convertRawEasyEdaToTs\``
134
136
  const issueLabels = "snippets,good first issue"
135
- const url = `https://github.com/tscircuit/easyeda-converter/issues/new?title=${encodeURIComponent(issueTitle)}&body=${encodeURIComponent(issueBody)}&labels=${encodeURIComponent(issueLabels)}`
136
-
137
- // Open the issue in a new tab
137
+ const url = `https://github.com/tscircuit/easyeda-converter/issues/new?title=${encodeURIComponent(
138
+ issueTitle,
139
+ )}&body=${encodeURIComponent(
140
+ issueBody,
141
+ )}&labels=${encodeURIComponent(issueLabels)}`
138
142
  window.open(url, "_blank")
139
143
  }}
140
144
  >
141
- File Issue on Github (prefilled)
145
+ File Issue on GitHub (prefilled)
142
146
  </Button>
143
147
  </div>
144
148
  )}
149
+
150
+ {hasBeenImportedToAccountAlready && (
151
+ <p className="p-2 mt-2 pre-wrap text-md text-green-600">
152
+ This part number has already been imported to your profile.{" "}
153
+ <PrefetchPageLink
154
+ className="text-blue-500 hover:underline"
155
+ href={`/${session?.github_username}/${partNumber}`}
156
+ >
157
+ View it here
158
+ </PrefetchPageLink>
159
+ </p>
160
+ )}
145
161
  </div>
146
162
  <DialogFooter>
147
163
  <Button onClick={handleImport} disabled={isLoading || !isLoggedIn}>
148
164
  {!isLoggedIn
149
- ? "Must be logged in for jlcpcb import"
165
+ ? "You must be logged in to import from JLCPCB"
150
166
  : isLoading
151
167
  ? "Importing..."
152
168
  : "Import"}
@@ -10,10 +10,10 @@ import {
10
10
  DropdownMenuItem,
11
11
  DropdownMenuTrigger,
12
12
  } from "@/components/ui/dropdown-menu"
13
- import { OptimizedImage } from "./OptimizedImage"
14
13
  import { SnippetType, SnippetTypeIcon } from "./SnippetTypeIcon"
15
14
  import { timeAgo } from "@/lib/utils/timeAgo"
16
15
  import { useGetFsMapHashForPackage } from "@/hooks/use-get-fsmap-hash-for-package"
16
+ import { ImageWithFallback } from "./ImageWithFallback"
17
17
 
18
18
  export interface PackageCardProps {
19
19
  /** The package data to display */
@@ -69,7 +69,7 @@ export const PackageCard: React.FC<PackageCardProps> = ({
69
69
  <div
70
70
  className={`${imageSize} flex-shrink-0 rounded-md overflow-hidden`}
71
71
  >
72
- <img
72
+ <ImageWithFallback
73
73
  src={`${baseUrl}/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.svg?${new URLSearchParams(
74
74
  {
75
75
  fs_sha: fsMapHash ?? "",
@@ -1,35 +1,27 @@
1
1
  import { Link } from "wouter"
2
2
  import { Star } from "lucide-react"
3
+ import { Package } from "fake-snippets-api/lib/db/schema"
3
4
 
4
- export const SnippetLink = ({
5
- snippet,
6
- }: {
7
- snippet: {
8
- owner_name: string
9
- name: string
10
- unscoped_name: string
11
- star_count?: number
12
- }
13
- }) => {
5
+ export const PackageLink = (pkg: Package) => {
14
6
  return (
15
7
  <>
16
8
  <Link
17
9
  className="text-blue-500 font-semibold hover:underline"
18
- href={`/${snippet.owner_name}`}
10
+ href={`/${pkg.owner_github_username}`}
19
11
  >
20
- {snippet.owner_name}
12
+ {pkg.owner_github_username}
21
13
  </Link>
22
14
  <span className="px-0.5 text-gray-500">/</span>
23
15
  <Link
24
16
  className="text-blue-500 font-semibold hover:underline"
25
- href={`/${snippet.name}`}
17
+ href={`/${pkg.unscoped_name}`}
26
18
  >
27
- {snippet.unscoped_name}
19
+ {pkg.unscoped_name}
28
20
  </Link>
29
- {snippet.star_count !== undefined && (
21
+ {pkg.star_count !== undefined && (
30
22
  <span className="ml-2 text-gray-500 text-xs flex items-center">
31
23
  <Star className="w-3 h-3 mr-1" />
32
- {snippet.star_count}
24
+ {pkg.star_count}
33
25
  </span>
34
26
  )}
35
27
  </>
@@ -0,0 +1,87 @@
1
+ import React from "react"
2
+ import { Package } from "fake-snippets-api/lib/db/schema"
3
+ import { PackageCardSkeleton } from "./PackageCardSkeleton"
4
+ import { Search } from "lucide-react"
5
+ import { PackageCard } from "./PackageCard"
6
+
7
+ interface PackageSearchResultsProps {
8
+ isLoading: boolean
9
+ error: unknown
10
+ filteredPackages: Package[] | undefined
11
+ apiBaseUrl: string
12
+ emptyStateMessage: string
13
+ }
14
+
15
+ const PackageGrid = ({
16
+ packages,
17
+ baseUrl,
18
+ }: { packages: Package[]; baseUrl: string }) => (
19
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
20
+ {packages.map((pkg) => (
21
+ <PackageCard
22
+ key={pkg.package_id}
23
+ pkg={pkg}
24
+ baseUrl={baseUrl}
25
+ showOwner={true}
26
+ />
27
+ ))}
28
+ </div>
29
+ )
30
+
31
+ const LoadingState = () => (
32
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
33
+ {[...Array(6)].map((_, i) => (
34
+ <PackageCardSkeleton key={i} />
35
+ ))}
36
+ </div>
37
+ )
38
+
39
+ const ErrorState = () => (
40
+ <div className="bg-red-50 border border-red-200 text-red-700 p-6 rounded-xl shadow-sm max-w-2xl mx-auto">
41
+ <div className="flex items-start">
42
+ <div className="mr-4 bg-red-100 p-2 rounded-full">
43
+ <Search className="w-6 h-6 text-red-600" />
44
+ </div>
45
+ <div>
46
+ <h3 className="text-lg font-semibold mb-2">Error Loading packages</h3>
47
+ <p className="text-red-600">
48
+ We couldn't load the trending packages. Please try again later.
49
+ </p>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ )
54
+
55
+ const EmptyState = ({
56
+ message,
57
+ }: {
58
+ message?: string
59
+ }) => (
60
+ <div className="text-center py-12 px-4">
61
+ <div className="bg-slate-50 inline-flex rounded-full p-4 mb-4">
62
+ <Search className="w-8 h-8 text-slate-400" />
63
+ </div>
64
+ <h3 className="text-xl font-medium text-slate-900 mb-2">
65
+ No Matching Packages
66
+ </h3>
67
+ {message && (
68
+ <p className="text-slate-500 max-w-md mx-auto mb-6">{message}</p>
69
+ )}
70
+ </div>
71
+ )
72
+
73
+ const PackageSearchResults: React.FC<PackageSearchResultsProps> = ({
74
+ isLoading,
75
+ error,
76
+ filteredPackages,
77
+ apiBaseUrl,
78
+ emptyStateMessage,
79
+ }) => {
80
+ if (isLoading) return <LoadingState />
81
+ if (error) return <ErrorState />
82
+ if (!filteredPackages?.length)
83
+ return <EmptyState message={emptyStateMessage} />
84
+ return <PackageGrid packages={filteredPackages} baseUrl={apiBaseUrl} />
85
+ }
86
+
87
+ export default PackageSearchResults
@@ -3,7 +3,7 @@ import { ChevronDown, ChevronUp, Star } from "lucide-react"
3
3
  import { Link } from "wouter"
4
4
  import { Package } from "fake-snippets-api/lib/db/schema"
5
5
 
6
- interface PackageListProps {
6
+ interface PackagesListProps {
7
7
  title: string
8
8
  packages?: Package[]
9
9
  showAll?: boolean
@@ -11,13 +11,13 @@ interface PackageListProps {
11
11
  maxItems?: number
12
12
  }
13
13
 
14
- export const PackageList = ({
14
+ export const PackagesList = ({
15
15
  title,
16
16
  packages = [],
17
17
  showAll = false,
18
18
  onToggleShowAll,
19
19
  maxItems = 5,
20
- }: PackageListProps) => {
20
+ }: PackagesListProps) => {
21
21
  const displayedPackages = showAll ? packages : packages.slice(0, maxItems)
22
22
 
23
23
  return (
@@ -5,9 +5,9 @@ import React, { useState } from "react"
5
5
  import { useQuery } from "react-query"
6
6
  import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
7
7
  import { Search } from "lucide-react"
8
- import { SnippetCard } from "./SnippetCard"
9
8
  import { Button } from "./ui/button"
10
9
  import { PackageCardSkeleton } from "./PackageCardSkeleton"
10
+ import { PackageCard } from "./PackageCard"
11
11
 
12
12
  interface PageSearchComponentProps {
13
13
  onResultsFetched?: (results: any[]) => void
@@ -28,16 +28,16 @@ const PageSearchComponent: React.FC<PageSearchComponentProps> = ({
28
28
  )
29
29
 
30
30
  const { data: searchResults, isLoading: isLoadingSearchResults } = useQuery(
31
- ["snippetSearch", searchQuery],
31
+ ["packageSearch", searchQuery],
32
32
  async () => {
33
33
  if (!searchQuery) return []
34
- const { data } = await axios.get("/snippets/search", {
34
+ const { data } = await axios.get("/packages/search", {
35
35
  params: { q: searchQuery },
36
36
  })
37
37
  if (onResultsFetched) {
38
- onResultsFetched(data.snippets)
38
+ onResultsFetched(data.packages)
39
39
  }
40
- return data.snippets
40
+ return data.packages
41
41
  },
42
42
  { enabled: Boolean(searchQuery) },
43
43
  )
@@ -94,10 +94,10 @@ const PageSearchComponent: React.FC<PageSearchComponentProps> = ({
94
94
  </div>
95
95
  ) : searchResults && searchResults.length > 0 ? (
96
96
  <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
97
- {searchResults.map((snippet: any) => (
98
- <SnippetCard
99
- key={snippet.snippet_id}
100
- snippet={snippet}
97
+ {searchResults.map((pkg: any) => (
98
+ <PackageCard
99
+ key={pkg.package_id}
100
+ pkg={pkg}
101
101
  baseUrl={snippetsBaseApiUrl}
102
102
  showOwner={true}
103
103
  withLink={true}
@@ -1,43 +1,20 @@
1
+ import { useShikiHighlighter } from "@/hooks/use-shiki-highlighter"
1
2
  import { useEffect, useMemo } from "react"
2
3
  import { useQuery } from "react-query"
3
- import { createHighlighter, Highlighter } from "shiki"
4
-
5
- let globalHighlighter$: any
6
- let globalHighlighter: Highlighter
7
-
8
- const fileExtensionsToLanguages = {
9
- ts: "typescript",
10
- tsx: "typescript",
11
- js: "javascript",
12
- jsx: "javascript",
13
- }
14
4
 
15
5
  export const ShikiCodeViewer = ({
16
6
  code,
17
7
  filePath,
18
8
  }: { code: string; filePath: string }) => {
19
- useEffect(() => {
20
- async function setupHighlighter() {
21
- if (globalHighlighter$) return
22
- globalHighlighter$ = await createHighlighter({
23
- langs: ["typescript"],
24
- themes: ["vitesse-light"],
25
- })
26
- globalHighlighter = await globalHighlighter$
27
- }
28
- setupHighlighter()
29
- }, [])
9
+ const { highlighter } = useShikiHighlighter()
30
10
 
31
11
  const html = useMemo(
32
12
  () =>
33
- globalHighlighter?.codeToHtml(code, {
34
- lang:
35
- fileExtensionsToLanguages[
36
- filePath.split(".").pop() as keyof typeof fileExtensionsToLanguages
37
- ] || "typescript",
13
+ highlighter?.codeToHtml(code, {
14
+ lang: "tsx",
38
15
  theme: "vitesse-light",
39
16
  }),
40
- [filePath, code, globalHighlighter],
17
+ [filePath, code, highlighter],
41
18
  )
42
19
 
43
20
  if (!html) {
@@ -65,7 +65,7 @@ export default function ImportantFilesView({
65
65
  setActiveFilePath(importantFiles[0].file_path)
66
66
  setActiveTab("file")
67
67
  }
68
- }, [aiDescription, aiUsageInstructions, hasAiContent])
68
+ }, [aiDescription, aiUsageInstructions, hasAiContent, importantFiles])
69
69
 
70
70
  // Get file name from path
71
71
  const getFileName = (path: string) => {
@@ -87,14 +87,14 @@ export default function MainContentHeader({
87
87
  </Button>
88
88
  </DropdownMenuTrigger>
89
89
  <DropdownMenuContent align="end" className="w-72">
90
- <DropdownMenuItem
91
- onClick={() => {
92
- setLocation(`/editor?package_id=${packageInfo?.package_id}`)
93
- }}
94
- className="cursor-pointer p-2 py-4"
95
- >
96
- <Pencil className="h-4 w-4 mx-3" />
97
- Edit Online
90
+ <DropdownMenuItem asChild>
91
+ <a
92
+ href={`/editor?package_id=${packageInfo?.package_id}`}
93
+ className="cursor-pointer p-2 py-4"
94
+ >
95
+ <Pencil className="h-4 w-4 mx-3" />
96
+ Edit Online
97
+ </a>
98
98
  </DropdownMenuItem>
99
99
  <DropdownMenuSeparator />
100
100
 
@@ -6,21 +6,20 @@ import { usePreviewImages } from "@/hooks/use-preview-images"
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
- import { useState, useEffect, useMemo } from "react"
9
+ import React, { useState, useEffect, useMemo, useCallback } from "react"
10
10
  import { useCurrentPackageInfo } from "@/hooks/use-current-package-info"
11
11
  import { usePackageFile } from "@/hooks/use-package-files"
12
12
  import { getLicenseFromLicenseContent } from "@/lib/getLicenseFromLicenseContent"
13
- import { PackageInfo } from "@/lib/types"
14
13
 
15
14
  interface MobileSidebarProps {
16
15
  isLoading?: boolean
17
16
  onViewChange: (view: "schematic" | "pcb" | "3d") => void
18
17
  }
19
18
 
20
- export default function MobileSidebar({
19
+ const MobileSidebar = ({
21
20
  isLoading = false,
22
21
  onViewChange,
23
- }: MobileSidebarProps) {
22
+ }: MobileSidebarProps) => {
24
23
  const { packageInfo, refetch: refetchPackageInfo } = useCurrentPackageInfo()
25
24
  const { data: licenseFileMeta } = usePackageFile({
26
25
  package_release_id: packageInfo?.latest_package_release_id ?? "",
@@ -34,8 +33,11 @@ export default function MobileSidebar({
34
33
  return getLicenseFromLicenseContent(licenseFileMeta?.content_text)
35
34
  }
36
35
  return undefined
37
- }, [licenseFileMeta?.content_text])
38
- const topics = packageInfo?.is_package ? ["Package"] : ["Board"]
36
+ }, [licenseFileMeta?.content_text, packageInfo?.latest_license])
37
+ const topics = useMemo(
38
+ () => (packageInfo?.is_package ? ["Package"] : ["Board"]),
39
+ [packageInfo?.is_package],
40
+ )
39
41
  const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
40
42
  const isOwner =
41
43
  isLoggedIn &&
@@ -59,20 +61,26 @@ export default function MobileSidebar({
59
61
  }
60
62
  }, [packageInfo])
61
63
 
62
- const handlePackageUpdate = (newDescription: string, newWebsite: string) => {
63
- setLocalDescription(newDescription)
64
- setLocalWebsite(newWebsite)
65
- refetchPackageInfo()
66
- }
64
+ const handlePackageUpdate = useCallback(
65
+ (newDescription: string, newWebsite: string) => {
66
+ setLocalDescription(newDescription)
67
+ setLocalWebsite(newWebsite)
68
+ refetchPackageInfo()
69
+ },
70
+ [refetchPackageInfo],
71
+ )
67
72
 
68
73
  const { availableViews } = usePreviewImages({
69
74
  packageName: packageInfo?.name,
70
75
  fsMapHash: packageInfo?.latest_package_release_id ?? "",
71
76
  })
72
77
 
73
- const handleViewClick = (viewId: string) => {
74
- onViewChange?.(viewId as "3d" | "pcb" | "schematic")
75
- }
78
+ const handleViewClick = useCallback(
79
+ (viewId: string) => {
80
+ onViewChange?.(viewId as "3d" | "pcb" | "schematic")
81
+ },
82
+ [onViewChange],
83
+ )
76
84
 
77
85
  if (isLoading) {
78
86
  return (
@@ -209,6 +217,8 @@ export default function MobileSidebar({
209
217
  )
210
218
  }
211
219
 
220
+ export default React.memo(MobileSidebar)
221
+
212
222
  function PreviewButton({
213
223
  view,
214
224
  onClick,
@@ -21,6 +21,7 @@ import {
21
21
  import { useOrderDialog } from "@tscircuit/runframe"
22
22
  import { useGlobalStore } from "@/hooks/use-global-store"
23
23
  import { Package as PackageType } from "fake-snippets-api/lib/db/schema"
24
+ import { useSignIn } from "@/hooks/use-sign-in"
24
25
 
25
26
  interface PackageHeaderProps {
26
27
  packageInfo?: PackageType
@@ -40,7 +41,12 @@ export default function PackageHeader({
40
41
  packageInfo?.owner_github_username ===
41
42
  useGlobalStore((s) => s.session?.github_username)
42
43
  const isLoggedIn = useGlobalStore((s) => s.session != null)
43
- const { OrderDialog, isOpen, open, close, stage, setStage } = useOrderDialog()
44
+ const signIn = useSignIn()
45
+ const { OrderDialog, isOpen, open, close, stage, setStage } = useOrderDialog({
46
+ onSignIn: signIn,
47
+ isLoggedIn,
48
+ packageReleaseId: packageInfo?.latest_package_release_id ?? "",
49
+ })
44
50
  const { data: starData, isLoading: isStarDataLoading } =
45
51
  usePackageStarsByName(packageInfo?.name ?? null)
46
52
  const { addStar, removeStar } = usePackageStarMutationByName(
@@ -254,7 +260,6 @@ export default function PackageHeader({
254
260
  onClose={close}
255
261
  stage={stage}
256
262
  setStage={setStage}
257
- packageReleaseId={packageInfo?.latest_package_release_id ?? ""}
258
263
  />
259
264
  </header>
260
265
  )
@@ -2,24 +2,32 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
2
2
  import { Button } from "../ui/button"
3
3
  import { createUseDialog } from "./create-use-dialog"
4
4
  import { useDeletePackage } from "@/hooks/use-delete-package"
5
+ import { useQueryClient } from "react-query"
5
6
 
6
7
  export const ConfirmDeletePackageDialog = ({
7
8
  open,
8
9
  onOpenChange,
9
10
  packageId,
10
11
  packageName,
12
+ packageOwner,
11
13
  refetchUserPackages,
12
14
  }: {
13
15
  open: boolean
14
16
  onOpenChange: (open: boolean) => void
15
17
  packageId: string
16
18
  packageName: string
19
+ packageOwner: string
17
20
  refetchUserPackages?: () => void
18
21
  }) => {
22
+ const queryClient = useQueryClient()
23
+
19
24
  const { mutate: deletePackage, isLoading } = useDeletePackage({
20
25
  onSuccess: () => {
21
26
  onOpenChange(false)
22
27
  refetchUserPackages?.()
28
+ queryClient.invalidateQueries({
29
+ queryKey: ["userPackages", packageOwner],
30
+ })
23
31
  },
24
32
  })
25
33