@tscircuit/fake-snippets 0.0.66 → 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 (73) 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 +15 -17
  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 -3
  13. package/src/App.tsx +0 -7
  14. package/src/ContextProviders.tsx +2 -0
  15. package/src/components/DownloadButtonAndMenu.tsx +1 -4
  16. package/src/components/Footer.tsx +5 -2
  17. package/src/components/HeaderLogin.tsx +37 -54
  18. package/src/components/ImageWithFallback.tsx +37 -0
  19. package/src/components/JLCPCBImportDialog.tsx +43 -24
  20. package/src/components/PackageCard.tsx +2 -2
  21. package/src/components/{SnippetLink.tsx → PackageLink.tsx} +8 -16
  22. package/src/components/PackageSearchResults.tsx +87 -0
  23. package/src/components/PackagesList.tsx +3 -3
  24. package/src/components/PageSearchComponent.tsx +9 -9
  25. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +5 -28
  26. package/src/components/ViewPackagePage/components/main-content-header.tsx +8 -8
  27. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +24 -14
  28. package/src/components/ViewPackagePage/components/package-header.tsx +6 -1
  29. package/src/components/package-port/CodeEditor.tsx +13 -10
  30. package/src/components/package-port/CodeEditorHeader.tsx +1 -1
  31. package/src/components/package-port/EditorNav.tsx +2 -2
  32. package/src/hooks/use-global-store.ts +1 -0
  33. package/src/hooks/use-shiki-highlighter.ts +13 -6
  34. package/src/lib/download-fns/download-gltf.ts +3 -10
  35. package/src/lib/handleManualEditsImport.tsx +1 -1
  36. package/src/lib/types.ts +4 -2
  37. package/src/pages/dashboard.tsx +3 -4
  38. package/src/pages/editor.tsx +20 -14
  39. package/src/pages/latest.tsx +25 -26
  40. package/src/pages/search.tsx +120 -19
  41. package/src/pages/trending.tsx +14 -58
  42. package/src/pages/user-profile.tsx +13 -8
  43. package/bun-tests/fake-snippets-api/routes/snippets/add_star.test.ts +0 -84
  44. package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +0 -53
  45. package/bun-tests/fake-snippets-api/routes/snippets/delete.test.ts +0 -82
  46. package/bun-tests/fake-snippets-api/routes/snippets/download.test.ts +0 -90
  47. package/bun-tests/fake-snippets-api/routes/snippets/generate_from_jlcpcb.test.ts +0 -16
  48. package/bun-tests/fake-snippets-api/routes/snippets/get.test.ts +0 -163
  49. package/bun-tests/fake-snippets-api/routes/snippets/get_image.test.ts +0 -117
  50. package/bun-tests/fake-snippets-api/routes/snippets/images.test.ts +0 -114
  51. package/bun-tests/fake-snippets-api/routes/snippets/list.test.ts +0 -169
  52. package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +0 -50
  53. package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +0 -72
  54. package/bun-tests/fake-snippets-api/routes/snippets/remove_star.test.ts +0 -80
  55. package/bun-tests/fake-snippets-api/routes/snippets/search.test.ts +0 -75
  56. package/bun-tests/fake-snippets-api/routes/snippets/star-count.test.ts +0 -51
  57. package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +0 -175
  58. package/src/components/AiChatInterface.tsx +0 -229
  59. package/src/components/CodeAndPreview.tsx +0 -289
  60. package/src/components/CodeEditor.tsx +0 -539
  61. package/src/components/CodeEditorHeader.tsx +0 -135
  62. package/src/components/EditorNav.tsx +0 -502
  63. package/src/components/PreviewContent.tsx +0 -372
  64. package/src/components/SnippetCard.tsx +0 -159
  65. package/src/components/SnippetList.tsx +0 -71
  66. package/src/hooks/use-compiled-tsx.ts +0 -37
  67. package/src/hooks/use-run-tsx/construct-circuit.tsx +0 -62
  68. package/src/hooks/use-run-tsx/index.tsx +0 -256
  69. package/src/hooks/use-save-snippet.ts +0 -66
  70. package/src/hooks/use-typecheck.ts +0 -54
  71. package/src/lib/utils/getSyntaxError.ts +0 -13
  72. package/src/pages/ai.tsx +0 -92
  73. 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}
@@ -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) {
@@ -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,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(
@@ -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}
@@ -30,4 +30,5 @@ export const useGlobalStore = create<Store>()(
30
30
 
31
31
  useGlobalStore.subscribe((state, prevState) => {
32
32
  ;(window as any).globalStore = state
33
+ window.TSCIRCUIT_REGISTRY_TOKEN = state.session?.token ?? null
33
34
  })
@@ -1,18 +1,25 @@
1
1
  import { useState, useEffect } from "react"
2
- import { getSingletonHighlighter, Highlighter } from "shiki"
2
+ import { createHighlighterCore, type HighlighterCore } from "shiki/core"
3
+ import { createOnigurumaEngine } from "shiki/engine/oniguruma"
3
4
 
4
- let cachedHighlighter: Highlighter | null = null
5
+ let cachedHighlighter: HighlighterCore | null = null
5
6
 
6
7
  export const useShikiHighlighter = () => {
7
- const [highlighter, setHighlighter] = useState<Highlighter | null>(null)
8
+ const [highlighter, setHighlighter] = useState<HighlighterCore | null>(null)
8
9
  const [isLoading, setIsLoading] = useState(true)
9
10
 
10
11
  useEffect(() => {
11
12
  const fetchHighlighter = async () => {
12
13
  if (!cachedHighlighter) {
13
- cachedHighlighter = await getSingletonHighlighter({
14
- themes: ["github-dark", "github-light"],
15
- langs: ["typescript", "tsx"],
14
+ cachedHighlighter = await createHighlighterCore({
15
+ themes: [
16
+ import("@shikijs/themes/github-dark"),
17
+ import("@shikijs/themes/github-light"),
18
+ import("@shikijs/themes/vitesse-light"),
19
+ ],
20
+ langs: [import("@shikijs/langs/tsx")],
21
+ // `shiki/wasm` contains the wasm binary inlined as base64 string.
22
+ engine: createOnigurumaEngine(import("shiki/wasm")),
16
23
  })
17
24
  }
18
25
  setHighlighter(cachedHighlighter)
@@ -1,19 +1,12 @@
1
- import { AnyCircuitElement } from "circuit-json"
2
- import { convertCircuitJsonToAssemblySvg } from "circuit-to-svg"
3
1
  import { GLTFExporter, type GLTFExporterOptions } from "three-stdlib"
4
2
  import { saveAs } from "file-saver"
5
3
  import * as THREE from "three"
6
4
 
7
- export const downloadGltf = async (
8
- circuitJson: AnyCircuitElement[],
9
- fileName: string,
10
- ) => {
11
- const threeJsObject = window.TSCIRCUIT_3D_OBJECT_REF
12
- ?.current as THREE.Object3D
13
-
5
+ export const downloadGltf = async (fileName: string) => {
6
+ const threeJsObject = window.TSCIRCUIT_3D_OBJECT_REF as THREE.Object3D
14
7
  if (!threeJsObject) {
15
8
  throw new Error(
16
- "To download the 3D model, please open the 3D view first and run the snippet",
9
+ "To download the 3D model, please open the 3D view first and run the package",
17
10
  )
18
11
  }
19
12
 
@@ -1,4 +1,4 @@
1
- import { FileName } from "@/components/CodeEditorHeader"
1
+ import { FileName } from "@/components/package-port/CodeEditorHeader"
2
2
 
3
3
  export const handleManualEditsImport = (
4
4
  files: Record<string, string>,
package/src/lib/types.ts CHANGED
@@ -1,9 +1,11 @@
1
+ import { Object3D, Object3DEventMap } from "three"
2
+
1
3
  declare global {
2
4
  interface Window {
3
5
  TSCIRCUIT_REGISTRY_API_BASE_URL: string
4
- TSCIRCUIT_REGISTRY_TOKEN: string
6
+ TSCIRCUIT_REGISTRY_TOKEN: string | null
5
7
  TSCIRCUIT_STRIPE_CHECKOUT_BASE_URL: string
6
- TSCIRCUIT_3D_OBJECT_REF: any
8
+ TSCIRCUIT_3D_OBJECT_REF: Object3D<Object3DEventMap> | undefined
7
9
  __DEBUG_CODE_EDITOR_FS_MAP: Map<string, string>
8
10
  prettier: {
9
11
  format: (code: string, options: any) => string
@@ -9,8 +9,7 @@ import { Edit2, KeyRound } from "lucide-react"
9
9
  import { Button } from "@/components/ui/button"
10
10
  import { useGlobalStore } from "@/hooks/use-global-store"
11
11
  import { PrefetchPageLink } from "@/components/PrefetchPageLink"
12
- import { PackageList } from "@/components/PackagesList"
13
- import { SnippetList } from "@/components/SnippetList"
12
+ import { PackagesList } from "@/components/PackagesList"
14
13
  import { Helmet } from "react-helmet-async"
15
14
  import { useSignIn } from "@/hooks/use-sign-in"
16
15
  import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
@@ -175,14 +174,14 @@ export const DashboardPage = () => {
175
174
  )}
176
175
  </div>
177
176
  <div className="md:w-1/4">
178
- <PackageList
177
+ <PackagesList
179
178
  title="Trending Packages"
180
179
  packages={trendingPackages}
181
180
  showAll={showAllTrending}
182
181
  onToggleShowAll={() => setShowAllTrending(!showAllTrending)}
183
182
  />
184
183
  <div className="mt-8">
185
- <PackageList
184
+ <PackagesList
186
185
  title="Latest Packages"
187
186
  packages={latestPackages}
188
187
  showAll={showAllLatest}
@@ -1,45 +1,51 @@
1
- import { CodeAndPreview } from "@/components/CodeAndPreview"
1
+ import { CodeAndPreview } from "@/components/package-port/CodeAndPreview"
2
2
  import Footer from "@/components/Footer"
3
3
  import Header from "@/components/Header"
4
- import { useCurrentSnippetId } from "@/hooks/use-current-snippet-id"
5
- import { useSnippet } from "@/hooks/use-snippet"
6
4
  import { Helmet } from "react-helmet-async"
5
+ import { useCurrentPackageId } from "@/hooks/use-current-package-id"
6
+ import { usePackage } from "@/hooks/use-package"
7
+ import { useGetFsMapHashForPackage } from "@/hooks/use-get-fsmap-hash-for-package"
7
8
 
8
9
  export const EditorPage = () => {
9
- const { snippetId } = useCurrentSnippetId()
10
- const { data: snippet, isLoading, error } = useSnippet(snippetId)
10
+ const { packageId } = useCurrentPackageId()
11
+ const { data: pkg, isLoading, error } = usePackage(packageId)
12
+ const fsMapHash = useGetFsMapHashForPackage(
13
+ pkg?.latest_package_release_id ?? "",
14
+ )
11
15
 
12
16
  return (
13
17
  <div className="overflow-x-hidden">
14
18
  <Helmet>
15
19
  <title>
16
- {snippet
17
- ? `${snippet.unscoped_name} - tscircuit`
18
- : "tscircuit editor"}
20
+ {pkg ? `${pkg.unscoped_name} - tscircuit` : "tscircuit editor"}
19
21
  </title>
20
- {snippet && (
22
+ {pkg && (
21
23
  <>
22
24
  <meta
23
25
  property="og:title"
24
- content={`${snippet.unscoped_name} - tscircuit`}
26
+ content={`${pkg.unscoped_name} - tscircuit`}
25
27
  />
26
28
  <meta
27
29
  property="og:image"
28
- content={`https://registry-api.tscircuit.com/snippets/images/${snippet.owner_name}/${snippet.unscoped_name}/pcb.png`}
30
+ content={`https://registry-api.tscircuit.com/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.png?${new URLSearchParams(
31
+ {
32
+ fs_sha: fsMapHash ?? "",
33
+ },
34
+ ).toString()}`}
29
35
  />
30
36
  <meta name="twitter:card" content="summary_large_image" />
31
37
  <meta
32
38
  name="twitter:image"
33
- content={`https://registry-api.tscircuit.com/snippets/images/${snippet.owner_name}/${snippet.unscoped_name}/pcb.png`}
39
+ content={`https://registry-api.tscircuit.com/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.png`}
34
40
  />
35
41
  </>
36
42
  )}
37
43
  </Helmet>
38
44
  <Header />
39
- {!error && <CodeAndPreview snippet={snippet} />}
45
+ {!error && <CodeAndPreview pkg={pkg} />}
40
46
  {error && error.status === 404 && (
41
47
  <div className="w-full h-[calc(100vh-20rem)] text-xl text-center flex justify-center items-center">
42
- Snippet not found
48
+ Package not found
43
49
  </div>
44
50
  )}
45
51
  {error && error.status !== 404 && (