@tscircuit/fake-snippets 0.0.65 → 0.0.67

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 (87) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/README.md +2 -2
  3. package/bun-tests/fake-snippets-api/fixtures/get-circuit-json.ts +5 -143
  4. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +1 -4
  5. package/bun-tests/fake-snippets-api/fixtures/start-server.ts +7 -3
  6. package/bun-tests/fake-snippets-api/routes/order_quotes/create.test.ts +20 -56
  7. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +2 -2
  8. package/bun-tests/fake-snippets-api/routes/package_releases/update.test.ts +1 -1
  9. package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +1 -16
  10. package/bun.lock +42 -31
  11. package/dist/bundle.js +34 -41
  12. package/fake-snippets-api/routes/api/order_quotes/create.ts +30 -37
  13. package/fake-snippets-api/routes/api/order_quotes/get.ts +5 -8
  14. package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +3 -3
  15. package/package.json +7 -5
  16. package/src/App.tsx +0 -7
  17. package/src/ContextProviders.tsx +2 -0
  18. package/src/components/DownloadButtonAndMenu.tsx +1 -4
  19. package/src/components/ErrorTabContent.tsx +1 -1
  20. package/src/components/Footer.tsx +5 -2
  21. package/src/components/HeaderLogin.tsx +37 -54
  22. package/src/components/ImageWithFallback.tsx +37 -0
  23. package/src/components/JLCPCBImportDialog.tsx +43 -24
  24. package/src/components/PackageCard.tsx +12 -3
  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/ShippingInformationForm.tsx +1 -1
  30. package/src/components/TrendingPackagesCarousel.tsx +43 -23
  31. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +5 -28
  32. package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -22
  33. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +25 -14
  34. package/src/components/ViewPackagePage/components/package-header.tsx +9 -4
  35. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +6 -1
  36. package/src/components/ViewPackagePage/components/repo-page-content.tsx +4 -4
  37. package/src/components/ViewPackagePage/components/sidebar.tsx +2 -14
  38. package/src/components/ViewSnippetSidebar.tsx +1 -1
  39. package/src/components/package-port/CodeEditor.tsx +13 -10
  40. package/src/components/package-port/CodeEditorHeader.tsx +1 -1
  41. package/src/components/package-port/EditorNav.tsx +2 -2
  42. package/src/hooks/use-get-fsmap-hash-for-package.ts +19 -0
  43. package/src/hooks/use-global-store.ts +1 -0
  44. package/src/hooks/use-preview-images.ts +20 -4
  45. package/src/hooks/use-shiki-highlighter.ts +13 -6
  46. package/src/hooks/use-toast.tsx +1 -1
  47. package/src/lib/download-fns/download-gltf.ts +3 -10
  48. package/src/lib/handleManualEditsImport.tsx +1 -1
  49. package/src/lib/types.ts +4 -2
  50. package/src/pages/dashboard.tsx +3 -4
  51. package/src/pages/editor.tsx +20 -14
  52. package/src/pages/latest.tsx +25 -26
  53. package/src/pages/package-editor.tsx +14 -2
  54. package/src/pages/search.tsx +120 -19
  55. package/src/pages/trending.tsx +14 -59
  56. package/src/pages/user-profile.tsx +13 -8
  57. package/bun-tests/fake-snippets-api/routes/snippets/add_star.test.ts +0 -84
  58. package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +0 -53
  59. package/bun-tests/fake-snippets-api/routes/snippets/delete.test.ts +0 -82
  60. package/bun-tests/fake-snippets-api/routes/snippets/download.test.ts +0 -90
  61. package/bun-tests/fake-snippets-api/routes/snippets/generate_from_jlcpcb.test.ts +0 -16
  62. package/bun-tests/fake-snippets-api/routes/snippets/get.test.ts +0 -163
  63. package/bun-tests/fake-snippets-api/routes/snippets/get_image.test.ts +0 -117
  64. package/bun-tests/fake-snippets-api/routes/snippets/images.test.ts +0 -114
  65. package/bun-tests/fake-snippets-api/routes/snippets/list.test.ts +0 -169
  66. package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +0 -50
  67. package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +0 -72
  68. package/bun-tests/fake-snippets-api/routes/snippets/remove_star.test.ts +0 -80
  69. package/bun-tests/fake-snippets-api/routes/snippets/search.test.ts +0 -75
  70. package/bun-tests/fake-snippets-api/routes/snippets/star-count.test.ts +0 -51
  71. package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +0 -175
  72. package/src/components/AiChatInterface.tsx +0 -229
  73. package/src/components/CodeAndPreview.tsx +0 -289
  74. package/src/components/CodeEditor.tsx +0 -539
  75. package/src/components/CodeEditorHeader.tsx +0 -135
  76. package/src/components/EditorNav.tsx +0 -502
  77. package/src/components/PreviewContent.tsx +0 -372
  78. package/src/components/SnippetCard.tsx +0 -159
  79. package/src/components/SnippetList.tsx +0 -71
  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/view-snippet.tsx +0 -166
@@ -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}
@@ -154,7 +154,7 @@ const ShippingInformationForm: React.FC = () => {
154
154
  <AlertDescription>
155
155
  Currently, only shipping to the US is supported.{" "}
156
156
  <a
157
- href={`https://github.com/tscircuit/snippets/issues/new?title=${encodeURIComponent("Shipping to " + form.country)}&body=${encodeURIComponent("Please add support for shipping to " + form.country + ".")}`}
157
+ href={`https://github.com/tscircuit/tscircuit.com/issues/new?title=${encodeURIComponent("Shipping to " + form.country)}&body=${encodeURIComponent("Please add support for shipping to " + form.country + ".")}`}
158
158
  target="_blank"
159
159
  rel="noopener noreferrer"
160
160
  className="font-medium underline"
@@ -2,10 +2,46 @@ import { useQuery } from "react-query"
2
2
  import { useAxios } from "@/hooks/use-axios"
3
3
  import { StarFilledIcon } from "@radix-ui/react-icons"
4
4
  import { Link } from "wouter"
5
- import { Package, Snippet } from "fake-snippets-api/lib/db/schema"
6
- import { useEffect, useRef, useState } from "react"
5
+ import { Package } from "fake-snippets-api/lib/db/schema"
6
+ import { useRef, useState } from "react"
7
7
  import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
8
- import { OptimizedImage } from "./OptimizedImage"
8
+ import { useGetFsMapHashForPackage } from "@/hooks/use-get-fsmap-hash-for-package"
9
+
10
+ const CarouselItem = ({
11
+ pkg,
12
+ apiBaseUrl,
13
+ }: { pkg: Package; apiBaseUrl: string }) => {
14
+ const fsMapHash = useGetFsMapHashForPackage(
15
+ pkg.latest_package_release_id ?? "",
16
+ )
17
+
18
+ return (
19
+ <Link href={`/${pkg.owner_github_username}/${pkg.unscoped_name}`}>
20
+ <div className="flex-shrink-0 w-[200px] bg-white p-3 py-2 rounded-lg shadow-sm border border-gray-200 hover:border-gray-300 transition-colors">
21
+ <div className="font-medium text-blue-600 mb-1 truncate text-sm">
22
+ {pkg.owner_github_username}/{pkg.unscoped_name}
23
+ </div>
24
+ <div className="mb-2 h-24 w-full bg-black rounded overflow-hidden">
25
+ {fsMapHash && (
26
+ <img
27
+ src={`${apiBaseUrl}/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.svg?${new URLSearchParams(
28
+ {
29
+ fs_sha: fsMapHash,
30
+ },
31
+ ).toString()}`}
32
+ alt="PCB preview"
33
+ className="w-full h-full object-contain p-2 scale-[3] rotate-45 hover:scale-[3.5] transition-transform"
34
+ />
35
+ )}
36
+ </div>
37
+ <div className="flex items-center text-xs text-gray-500">
38
+ <StarFilledIcon className="w-3 h-3 mr-1" />
39
+ {pkg.star_count || 0} stars
40
+ </div>
41
+ </div>
42
+ </Link>
43
+ )
44
+ }
9
45
 
10
46
  export const TrendingPackagesCarousel = () => {
11
47
  const axios = useAxios()
@@ -45,27 +81,11 @@ export const TrendingPackagesCarousel = () => {
45
81
  >
46
82
  {[...(trendingPackages ?? []), ...(trendingPackages ?? [])].map(
47
83
  (pkg, i) => (
48
- <Link
84
+ <CarouselItem
49
85
  key={`${pkg.package_id}-${i}`}
50
- href={`/${pkg.owner_github_username}/${pkg.unscoped_name}`}
51
- >
52
- <div className="flex-shrink-0 w-[200px] bg-white p-3 py-2 rounded-lg shadow-sm border border-gray-200 hover:border-gray-300 transition-colors">
53
- <div className="font-medium text-blue-600 mb-1 truncate text-sm">
54
- {pkg.owner_github_username}/{pkg.unscoped_name}
55
- </div>
56
- <div className="mb-2 h-24 w-full bg-black rounded overflow-hidden">
57
- <OptimizedImage
58
- src={`${apiBaseUrl}/snippets/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.svg`}
59
- alt="PCB preview"
60
- className="w-full h-full object-contain p-2 scale-[3] rotate-45 hover:scale-[3.5] transition-transform"
61
- />
62
- </div>
63
- <div className="flex items-center text-xs text-gray-500">
64
- <StarFilledIcon className="w-3 h-3 mr-1" />
65
- {pkg.star_count || 0} stars
66
- </div>
67
- </div>
68
- </Link>
86
+ pkg={pkg}
87
+ apiBaseUrl={apiBaseUrl}
88
+ />
69
89
  ),
70
90
  )}
71
91
  </div>
@@ -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) {
@@ -24,24 +24,12 @@ import {
24
24
  import { DownloadButtonAndMenu } from "@/components/DownloadButtonAndMenu"
25
25
  import { useCurrentPackageCircuitJson } from "../hooks/use-current-package-circuit-json"
26
26
  import { useLocation } from "wouter"
27
-
28
- interface PackageInfo {
29
- package_id: string
30
- name: string
31
- unscoped_name: string
32
- owner_github_username: string
33
- star_count: string
34
- description: string
35
- ai_description: string
36
- creator_account_id?: string
37
- owner_org_id?: string
38
- }
39
-
27
+ import { Package } from "fake-snippets-api/lib/db/schema"
40
28
  interface MainContentHeaderProps {
41
29
  activeView: string
42
30
  onViewChange: (view: string) => void
43
31
  onExportClicked?: (exportType: string) => void
44
- packageInfo?: PackageInfo
32
+ packageInfo?: Package
45
33
  }
46
34
 
47
35
  export default function MainContentHeader({
@@ -99,14 +87,14 @@ export default function MainContentHeader({
99
87
  </Button>
100
88
  </DropdownMenuTrigger>
101
89
  <DropdownMenuContent align="end" className="w-72">
102
- <DropdownMenuItem
103
- onClick={() => {
104
- setLocation(`/editor?package_id=${packageInfo?.package_id}`)
105
- }}
106
- className="cursor-pointer p-2 py-4"
107
- >
108
- <Pencil className="h-4 w-4 mx-3" />
109
- 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>
110
98
  </DropdownMenuItem>
111
99
  <DropdownMenuSeparator />
112
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,19 +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,
75
+ fsMapHash: packageInfo?.latest_package_release_id ?? "",
70
76
  })
71
77
 
72
- const handleViewClick = (viewId: string) => {
73
- onViewChange?.(viewId as "3d" | "pcb" | "schematic")
74
- }
78
+ const handleViewClick = useCallback(
79
+ (viewId: string) => {
80
+ onViewChange?.(viewId as "3d" | "pcb" | "schematic")
81
+ },
82
+ [onViewChange],
83
+ )
75
84
 
76
85
  if (isLoading) {
77
86
  return (
@@ -208,6 +217,8 @@ export default function MobileSidebar({
208
217
  )
209
218
  }
210
219
 
220
+ export default React.memo(MobileSidebar)
221
+
211
222
  function PreviewButton({
212
223
  view,
213
224
  onClick,
@@ -20,10 +20,11 @@ import {
20
20
  } from "@/hooks/use-package-stars"
21
21
  import { useOrderDialog } from "@tscircuit/runframe"
22
22
  import { useGlobalStore } from "@/hooks/use-global-store"
23
- import { PackageInfo } from "@/lib/types"
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
- packageInfo?: PackageInfo
27
+ packageInfo?: PackageType
27
28
  isPrivate?: boolean
28
29
  isCurrentUserAuthor?: boolean
29
30
  }
@@ -40,7 +41,11 @@ 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
+ })
44
49
  const { data: starData, isLoading: isStarDataLoading } =
45
50
  usePackageStarsByName(packageInfo?.name ?? null)
46
51
  const { addStar, removeStar } = usePackageStarMutationByName(
@@ -254,7 +259,7 @@ export default function PackageHeader({
254
259
  onClose={close}
255
260
  stage={stage}
256
261
  setStage={setStage}
257
- packageReleaseId={packageInfo?.latest_package_release_id}
262
+ packageReleaseId={packageInfo?.latest_package_release_id ?? ""}
258
263
  />
259
264
  </header>
260
265
  )
@@ -1,10 +1,11 @@
1
1
  "use client"
2
2
  import { Skeleton } from "@/components/ui/skeleton"
3
+ import { useGetFsMapHashForPackage } from "@/hooks/use-get-fsmap-hash-for-package"
3
4
  import { usePreviewImages } from "@/hooks/use-preview-images"
4
5
  import type { Package } from "fake-snippets-api/lib/db/schema"
5
6
 
6
7
  interface ViewPlaceholdersProps {
7
- packageInfo?: Pick<Package, "name">
8
+ packageInfo?: Pick<Package, "name" | "latest_package_release_id">
8
9
  onViewChange?: (view: "3d" | "pcb" | "schematic") => void
9
10
  }
10
11
 
@@ -12,8 +13,12 @@ export default function PreviewImageSquares({
12
13
  packageInfo,
13
14
  onViewChange,
14
15
  }: ViewPlaceholdersProps) {
16
+ const fsMapHash = useGetFsMapHashForPackage(
17
+ packageInfo?.latest_package_release_id ?? "",
18
+ )
15
19
  const { availableViews } = usePreviewImages({
16
20
  packageName: packageInfo?.name,
21
+ fsMapHash: fsMapHash ?? "",
17
22
  })
18
23
 
19
24
  const handleViewClick = (viewId: string) => {
@@ -18,7 +18,7 @@ import Footer from "@/components/Footer"
18
18
  import PackageHeader from "./package-header"
19
19
  import { useGlobalStore } from "@/hooks/use-global-store"
20
20
  import { useLocation } from "wouter"
21
- import { PackageInfo } from "@/lib/types"
21
+ import { Package } from "fake-snippets-api/lib/db/schema"
22
22
 
23
23
  interface PackageFile {
24
24
  package_file_id: string
@@ -32,7 +32,7 @@ interface PackageFile {
32
32
  interface RepoPageContentProps {
33
33
  packageFiles?: PackageFile[]
34
34
  importantFilePaths?: string[]
35
- packageInfo?: PackageInfo
35
+ packageInfo?: Package
36
36
  onFileClicked?: (file: PackageFile) => void
37
37
  onEditClicked?: () => void
38
38
  }
@@ -179,8 +179,8 @@ export default function RepoPageContent({
179
179
  importantFiles={importantFiles}
180
180
  isLoading={!packageFiles}
181
181
  onEditClicked={onEditClicked}
182
- aiDescription={packageInfo?.ai_description}
183
- aiUsageInstructions={packageInfo?.ai_usage_instructions}
182
+ aiDescription={packageInfo?.ai_description ?? ""}
183
+ aiUsageInstructions={packageInfo?.ai_usage_instructions ?? ""}
184
184
  />
185
185
  </div>
186
186
 
@@ -1,24 +1,12 @@
1
1
  "use client"
2
2
 
3
- import { useState } from "react"
3
+ import { Package } from "fake-snippets-api/lib/db/schema"
4
4
  import SidebarAboutSection from "./sidebar-about-section"
5
5
  import SidebarReleasesSection from "./sidebar-releases-section"
6
6
  import PreviewImageSquares from "./preview-image-squares"
7
- import { PackageFile } from "fake-snippets-api/lib/db/schema"
8
-
9
- interface PackageInfo {
10
- name: string
11
- unscoped_name: string
12
- owner_github_username: string
13
- star_count: string
14
- description: string
15
- ai_description: string
16
- creator_account_id?: string
17
- owner_org_id?: string
18
- }
19
7
 
20
8
  interface SidebarProps {
21
- packageInfo?: PackageInfo
9
+ packageInfo?: Package
22
10
  isLoading?: boolean
23
11
  onViewChange?: (view: "3d" | "pcb" | "schematic") => void
24
12
  }
@@ -87,7 +87,7 @@ export default function ViewSnippetSidebar({
87
87
  Help us out!{" "}
88
88
  <a
89
89
  className="text-blue-500 hover:underline font-semibold"
90
- href="https://github.com/tscircuit/snippets"
90
+ href="https://github.com/tscircuit/tscircuit.com"
91
91
  >
92
92
  Check out our Github
93
93
  </a>
@@ -23,7 +23,7 @@ import {
23
23
  } from "@valtown/codemirror-ts"
24
24
  import { EditorView } from "codemirror"
25
25
  import { useEffect, useMemo, useRef, useState } from "react"
26
- import ts from "typescript"
26
+ import tsModule from "typescript"
27
27
  import CodeEditorHeader from "@/components/package-port/CodeEditorHeader"
28
28
  import { useCodeCompletionApi } from "@/hooks/use-code-completion-ai-api"
29
29
  import FileSidebar from "../FileSidebar"
@@ -134,10 +134,10 @@ export const CodeEditor = ({
134
134
  ;(window as any).__DEBUG_CODE_EDITOR_FS_MAP = fsMap
135
135
 
136
136
  createDefaultMapFromCDN(
137
- { target: ts.ScriptTarget.ES2022 },
137
+ { target: tsModule.ScriptTarget.ES2022 },
138
138
  "5.6.3",
139
139
  true,
140
- ts,
140
+ tsModule,
141
141
  ).then((defaultFsMap) => {
142
142
  defaultFsMap.forEach((content, filename) => {
143
143
  fsMap.set(filename, content)
@@ -145,11 +145,12 @@ export const CodeEditor = ({
145
145
  })
146
146
 
147
147
  const system = createSystem(fsMap)
148
- const env = createVirtualTypeScriptEnvironment(system, [], ts, {
149
- jsx: ts.JsxEmit.ReactJSX,
148
+
149
+ const env = createVirtualTypeScriptEnvironment(system, [], tsModule, {
150
+ jsx: tsModule.JsxEmit.ReactJSX,
150
151
  declaration: true,
151
152
  allowJs: true,
152
- target: ts.ScriptTarget.ES2022,
153
+ target: tsModule.ScriptTarget.ES2022,
153
154
  resolveJsonModule: true,
154
155
  })
155
156
 
@@ -160,7 +161,7 @@ export const CodeEditor = ({
160
161
  // Initialize ATA
161
162
  const ataConfig: ATABootstrapConfig = {
162
163
  projectName: "my-project",
163
- typescript: ts,
164
+ typescript: tsModule,
164
165
  logger: console,
165
166
  fetcher: async (input: RequestInfo | URL, init?: RequestInit) => {
166
167
  const registryPrefixes = [
@@ -336,12 +337,14 @@ export const CodeEditor = ({
336
337
 
337
338
  const start = info.textSpan.start
338
339
  const end = start + info.textSpan.length
339
- const content = ts.displayPartsToString(info.displayParts || [])
340
+ const content = tsModule?.displayPartsToString(
341
+ info.displayParts || [],
342
+ )
340
343
 
341
344
  const dom = document.createElement("div")
342
345
  if (highlighter) {
343
346
  dom.innerHTML = highlighter.codeToHtml(content, {
344
- lang: "typescript",
347
+ lang: "tsx",
345
348
  themes: {
346
349
  light: "github-light",
347
350
  dark: "github-dark",
@@ -456,7 +459,7 @@ export const CodeEditor = ({
456
459
  return () => {
457
460
  view.destroy()
458
461
  }
459
- }, [!isStreaming, currentFile, code !== ""])
462
+ }, [!isStreaming, currentFile, code !== "", Boolean(highlighter)])
460
463
 
461
464
  const updateCurrentEditorContent = (newContent: string) => {
462
465
  if (viewRef.current) {
@@ -101,7 +101,7 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
101
101
  markdown: "markdown",
102
102
  }
103
103
 
104
- const parser = parserMap[fileExtension] || "typescript"
104
+ const parser = parserMap[fileExtension] || "tsx"
105
105
  const formattedCode = window.prettier.format(currentContent, {
106
106
  semi: false,
107
107
  parser: parser,
@@ -315,7 +315,7 @@ export default function EditorNav({
315
315
  <div className="flex items-center justify-end -space-x-1">
316
316
  <div className="flex mx-2 items-center space-x-1">
317
317
  {pkg && <TypeBadge type={`${packageType ?? pkg.snippet_type}`} />}
318
- <Button
318
+ {/* <Button
319
319
  variant="ghost"
320
320
  size="sm"
321
321
  disabled={hasUnsavedChanges || isSaving || !pkg}
@@ -323,7 +323,7 @@ export default function EditorNav({
323
323
  >
324
324
  <Sparkles className="mr-1 h-3 w-3" />
325
325
  Edit with AI
326
- </Button>
326
+ </Button> */}
327
327
  <DownloadButtonAndMenu
328
328
  snippetUnscopedName={pkg?.unscoped_name}
329
329
  circuitJson={circuitJson}