@tscircuit/fake-snippets 0.0.71 → 0.0.73

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 (43) hide show
  1. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -0
  2. package/bun.lock +58 -58
  3. package/dist/bundle.js +56 -5
  4. package/dist/index.d.ts +15 -2
  5. package/dist/index.js +47 -1
  6. package/dist/schema.d.ts +8 -0
  7. package/dist/schema.js +1 -0
  8. package/fake-snippets-api/lib/db/db-client.ts +47 -0
  9. package/fake-snippets-api/lib/db/schema.ts +1 -0
  10. package/fake-snippets-api/lib/package_file/generate-fs-sha.ts +20 -0
  11. package/fake-snippets-api/lib/public-mapping/public-map-package.ts +1 -0
  12. package/fake-snippets-api/routes/api/package_files/create.ts +3 -0
  13. package/fake-snippets-api/routes/api/package_files/create_or_update.ts +9 -3
  14. package/fake-snippets-api/routes/api/package_files/delete.ts +3 -0
  15. package/fake-snippets-api/routes/api/packages/create.ts +1 -0
  16. package/package.json +11 -11
  17. package/src/App.tsx +5 -0
  18. package/src/components/FileSidebar.tsx +111 -37
  19. package/src/components/JLCPCBImportDialog.tsx +1 -1
  20. package/src/components/PackageBuildsPage/DeploymentDetailsPage.tsx +56 -0
  21. package/src/components/PackageBuildsPage/build-preview-content.tsx +11 -0
  22. package/src/components/PackageBuildsPage/collapsible-section.tsx +70 -0
  23. package/src/components/PackageBuildsPage/deployment-details-panel.tsx +84 -0
  24. package/src/components/PackageBuildsPage/deployment-header.tsx +75 -0
  25. package/src/components/PackageCard.tsx +1 -10
  26. package/src/components/TrendingPackagesCarousel.tsx +5 -16
  27. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -1
  28. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +2 -6
  29. package/src/components/package-port/CodeAndPreview.tsx +78 -267
  30. package/src/components/package-port/CodeEditor.tsx +30 -19
  31. package/src/components/package-port/CodeEditorHeader.tsx +7 -6
  32. package/src/components/package-port/EditorNav.tsx +17 -13
  33. package/src/components/ui/tree-view.tsx +3 -3
  34. package/src/hooks/use-current-package-id.ts +2 -8
  35. package/src/hooks/use-preview-images.ts +3 -15
  36. package/src/hooks/useFileManagement.ts +257 -38
  37. package/src/hooks/usePackageFilesLoader.ts +2 -2
  38. package/src/hooks/useUpdatePackageFilesMutation.ts +50 -24
  39. package/src/lib/utils/checkIfManualEditsImported.ts +9 -0
  40. package/src/pages/editor.tsx +2 -10
  41. package/src/pages/package-builds.tsx +33 -0
  42. package/src/pages/package-editor.tsx +2 -14
  43. package/src/hooks/use-get-fsmap-hash-for-package.ts +0 -19
@@ -207,19 +207,23 @@ export default function EditorNav({
207
207
  <div className="flex items-center space-x-1">
208
208
  {pkg && (
209
209
  <>
210
- <Link
211
- className="text-blue-500 font-semibold hover:underline"
212
- href={`/${pkg.owner_github_username}`}
213
- >
214
- {pkg.owner_github_username}
215
- </Link>
216
- <span className="px-0.5 text-gray-500">/</span>
217
- <Link
218
- className="text-blue-500 font-semibold hover:underline"
219
- href={`/${pkg.name}`}
220
- >
221
- {pkg.unscoped_name}
222
- </Link>
210
+ <div className="flex items-center space-x-1 overflow-hidden">
211
+ <Link
212
+ className="text-blue-500 font-semibold hover:underline truncate"
213
+ href={`/${pkg.owner_github_username}`}
214
+ title={pkg.owner_github_username || ""}
215
+ >
216
+ {pkg.owner_github_username}
217
+ </Link>
218
+ <span className="px-0.5 text-gray-500">/</span>
219
+ <Link
220
+ className="text-blue-500 font-semibold hover:underline truncate"
221
+ href={`/${pkg.name}`}
222
+ title={pkg.unscoped_name}
223
+ >
224
+ {pkg.unscoped_name}
225
+ </Link>
226
+ </div>
223
227
  {pkg.star_count !== undefined && (
224
228
  <span className="ml-2 text-gray-500 text-xs flex items-center">
225
229
  <Star className="w-3 h-3 mr-1" />
@@ -404,9 +404,9 @@ const TreeLeaf = React.forwardRef<
404
404
  default={defaultLeafIcon}
405
405
  />
406
406
  <span className="flex-grow text-sm truncate">{item.name}</span>
407
- <TreeActions isSelected={selectedItemId === item.id}>
408
- {item.actions}
409
- </TreeActions>
407
+ <div onClick={(e) => e.stopPropagation()}>
408
+ <TreeActions isSelected={true}>{item.actions}</TreeActions>
409
+ </div>
410
410
  </div>
411
411
  )
412
412
  },
@@ -31,17 +31,11 @@ export const useCurrentPackageId = (): {
31
31
  error: errorPackageByName,
32
32
  } = usePackageByName(packageName)
33
33
 
34
- const {
35
- data: packageById,
36
- isLoading: isLoadingPackageById,
37
- error: errorPackageById,
38
- } = usePackageById(packageIdFromUrl)
39
-
40
34
  const packageId = packageIdFromUrl ?? packageByName?.package_id ?? null
41
35
 
42
36
  return {
43
37
  packageId,
44
- isLoading: isLoadingPackageByName || isLoadingPackageById,
45
- error: errorPackageByName || errorPackageById,
38
+ isLoading: isLoadingPackageByName,
39
+ error: errorPackageByName,
46
40
  }
47
41
  }
@@ -22,33 +22,21 @@ export function usePreviewImages({
22
22
  id: "3d",
23
23
  label: "3D View",
24
24
  imageUrl: packageName
25
- ? `https://registry-api.tscircuit.com/packages/images/${packageName}/3d.png?${new URLSearchParams(
26
- {
27
- fs_sha: fsMapHash ?? "",
28
- },
29
- ).toString()}`
25
+ ? `https://registry-api.tscircuit.com/packages/images/${packageName}/3d.png?fs_sha=${fsMapHash}`
30
26
  : undefined,
31
27
  },
32
28
  {
33
29
  id: "pcb",
34
30
  label: "PCB View",
35
31
  imageUrl: packageName
36
- ? `https://registry-api.tscircuit.com/packages/images/${packageName}/pcb.png?${new URLSearchParams(
37
- {
38
- fs_sha: fsMapHash ?? "",
39
- },
40
- ).toString()}`
32
+ ? `https://registry-api.tscircuit.com/packages/images/${packageName}/pcb.png?fs_sha=${fsMapHash}`
41
33
  : undefined,
42
34
  },
43
35
  {
44
36
  id: "schematic",
45
37
  label: "Schematic View",
46
38
  imageUrl: packageName
47
- ? `https://registry-api.tscircuit.com/packages/images/${packageName}/schematic.png?${new URLSearchParams(
48
- {
49
- fs_sha: fsMapHash ?? "",
50
- },
51
- ).toString()}`
39
+ ? `https://registry-api.tscircuit.com/packages/images/${packageName}/schematic.png?fs_sha=${fsMapHash}`
52
40
  : undefined,
53
41
  },
54
42
  ]
@@ -1,59 +1,278 @@
1
- import { Dispatch, SetStateAction } from "react"
1
+ import { useEffect, useMemo, useState, useCallback } from "react"
2
2
  import { isValidFileName } from "@/lib/utils/isValidFileName"
3
3
  import {
4
- CodeAndPreviewState,
5
- CreateFileProps,
4
+ DEFAULT_CODE,
5
+ generateRandomPackageName,
6
+ PackageFile,
6
7
  } from "../components/package-port/CodeAndPreview"
8
+ import { Package } from "fake-snippets-api/lib/db/schema"
9
+ import {
10
+ usePackageFile,
11
+ usePackageFileById,
12
+ usePackageFiles,
13
+ } from "./use-package-files"
14
+ import { decodeUrlHashToText } from "@/lib/decodeUrlHashToText"
15
+ import { usePackageFilesLoader } from "./usePackageFilesLoader"
16
+ import { useGlobalStore } from "./use-global-store"
17
+ import { useToast } from "@/components/ViewPackagePage/hooks/use-toast"
18
+ import { useUpdatePackageFilesMutation } from "./useUpdatePackageFilesMutation"
19
+ import { useCreatePackageReleaseMutation } from "./use-create-package-release-mutation"
20
+ import { useCreatePackageMutation } from "./use-create-package-mutation"
21
+ import { findTargetFile } from "@/lib/utils/findTargetFile"
22
+
23
+ export interface ICreateFileProps {
24
+ newFileName: string
25
+ onError: (error: Error) => void
26
+ }
27
+ export interface ICreateFileResult {
28
+ newFileCreated: boolean
29
+ }
30
+
31
+ export interface IDeleteFileResult {
32
+ fileDeleted: boolean
33
+ }
34
+ export interface IDeleteFileProps {
35
+ filename: string
36
+ onError: (error: Error) => void
37
+ }
38
+
39
+ export function useFileManagement({
40
+ templateCode,
41
+ currentPackage,
42
+ fileChoosen,
43
+ openNewPackageSaveDialog,
44
+ updateLastUpdated,
45
+ }: {
46
+ templateCode?: string
47
+ currentPackage?: Package
48
+ fileChoosen: string | null
49
+ openNewPackageSaveDialog: () => void
50
+ updateLastUpdated: () => void
51
+ }) {
52
+ const [localFiles, setLocalFiles] = useState<PackageFile[]>([])
53
+ const [initialFiles, setInitialFiles] = useState<PackageFile[]>([])
54
+ const [currentFile, setCurrentFile] = useState<string | null>(null)
55
+ const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
56
+ const loggedInUser = useGlobalStore((s) => s.session)
57
+ const { toast } = useToast()
58
+ const {
59
+ data: packageFilesWithContent,
60
+ isLoading: isLoadingPackageFilesWithContent,
61
+ } = usePackageFilesLoader(currentPackage)
62
+ const { data: packageFilesMeta, isLoading: isLoadingPackageFiles } =
63
+ usePackageFiles(currentPackage?.latest_package_release_id)
7
64
 
8
- export function useFileManagement(
9
- state: CodeAndPreviewState,
10
- setState: Dispatch<SetStateAction<CodeAndPreviewState>>,
11
- ) {
12
- const handleCreateFile = async ({
65
+ const initialCodeContent = useMemo(() => {
66
+ return (
67
+ templateCode ??
68
+ decodeUrlHashToText(window.location.toString()) ??
69
+ DEFAULT_CODE
70
+ )
71
+ }, [templateCode, currentPackage])
72
+ const manualEditsFileContent = useMemo(() => {
73
+ return (
74
+ localFiles?.find((file) => file.path === "manual-edits.json")?.content ||
75
+ "{}"
76
+ )
77
+ }, [localFiles])
78
+
79
+ const updatePackageFilesMutation = useUpdatePackageFilesMutation({
80
+ currentPackage,
81
+ localFiles,
82
+ initialFiles,
83
+ packageFilesMeta: packageFilesMeta || [],
84
+ })
85
+ const { mutate: createRelease, isLoading: isCreatingRelease } =
86
+ useCreatePackageReleaseMutation({
87
+ onSuccess: () => {
88
+ toast({
89
+ title: "Package released",
90
+ description: "Your package has been released successfully.",
91
+ })
92
+ },
93
+ })
94
+ const createPackageMutation = useCreatePackageMutation()
95
+
96
+ useEffect(() => {
97
+ if (!currentPackage || isLoadingPackageFilesWithContent) {
98
+ setLocalFiles([
99
+ {
100
+ path: "index.tsx",
101
+ content: initialCodeContent || "",
102
+ },
103
+ ])
104
+ setInitialFiles([])
105
+ setCurrentFile("index.tsx")
106
+ return
107
+ } else {
108
+ const targetFile = findTargetFile(
109
+ packageFilesWithContent || [],
110
+ fileChoosen,
111
+ )
112
+ setLocalFiles(packageFilesWithContent || [])
113
+ setInitialFiles(packageFilesWithContent || [])
114
+ setCurrentFile(targetFile?.path || null)
115
+ }
116
+ }, [currentPackage, isLoadingPackageFilesWithContent])
117
+
118
+ const isLoading = useMemo(() => {
119
+ return (
120
+ isLoadingPackageFilesWithContent || isLoadingPackageFiles || !localFiles
121
+ )
122
+ }, [isLoadingPackageFilesWithContent, localFiles, isLoadingPackageFiles])
123
+
124
+ const fsMap = useMemo(() => {
125
+ const map = localFiles.reduce(
126
+ (acc, file) => {
127
+ acc[file.path] = file.content || ""
128
+ return acc
129
+ },
130
+ {} as Record<string, string>,
131
+ )
132
+ return map
133
+ }, [localFiles, manualEditsFileContent])
134
+
135
+ const onFileSelect = (fileName: string) => {
136
+ if (localFiles.some((file) => file.path === fileName)) {
137
+ setCurrentFile(fileName)
138
+ } else {
139
+ setCurrentFile(null)
140
+ }
141
+ }
142
+
143
+ const createFile = ({
13
144
  newFileName,
14
- setErrorMessage,
15
- onFileSelect,
16
- setNewFileName,
17
- setIsCreatingFile,
18
- }: CreateFileProps) => {
145
+ onError,
146
+ }: ICreateFileProps): ICreateFileResult => {
19
147
  newFileName = newFileName.trim()
20
148
  if (!newFileName) {
21
- setErrorMessage("File name cannot be empty")
22
- return
149
+ onError(new Error("File name cannot be empty"))
150
+ return {
151
+ newFileCreated: false,
152
+ }
23
153
  }
24
154
  if (!isValidFileName(newFileName)) {
25
- setErrorMessage(
26
- 'Invalid file name. Avoid using special characters like <>:"/\\|?*',
27
- )
28
- return
155
+ onError(new Error("Invalid file name"))
156
+ return {
157
+ newFileCreated: false,
158
+ }
29
159
  }
30
- setErrorMessage("")
31
-
32
- const fileExists = state.pkgFilesWithContent.some(
33
- (file) => file.path === newFileName,
34
- )
35
160
 
161
+ const fileExists = localFiles?.some((file) => file.path === newFileName)
36
162
  if (fileExists) {
37
- setErrorMessage("A file with this name already exists")
38
- return
163
+ onError(new Error("File already exists"))
164
+ return {
165
+ newFileCreated: false,
166
+ }
167
+ }
168
+ const updatedFiles = [
169
+ ...(localFiles || []),
170
+ { path: newFileName, content: "" },
171
+ ]
172
+ setLocalFiles(updatedFiles)
173
+ onFileSelect(newFileName)
174
+ return {
175
+ newFileCreated: true,
39
176
  }
177
+ }
40
178
 
41
- setState((prev) => {
42
- const updatedFiles = [
43
- ...prev.pkgFilesWithContent,
44
- { path: newFileName, content: "" },
45
- ]
179
+ const deleteFile = ({
180
+ filename,
181
+ onError,
182
+ }: IDeleteFileProps): IDeleteFileResult => {
183
+ const fileExists = localFiles?.some((file) => file.path === filename)
184
+ if (!fileExists) {
185
+ onError(new Error("File does not exist"))
46
186
  return {
47
- ...prev,
48
- pkgFilesWithContent: updatedFiles,
49
- } as CodeAndPreviewState
187
+ fileDeleted: false,
188
+ }
189
+ }
190
+ const updatedFiles = localFiles.filter((file) => file.path !== filename)
191
+ setLocalFiles(updatedFiles)
192
+ onFileSelect(updatedFiles[0]?.path || "")
193
+ return {
194
+ fileDeleted: true,
195
+ }
196
+ }
197
+
198
+ const savePackage = async (isPrivate: boolean) => {
199
+ if (!isLoggedIn) {
200
+ toast({
201
+ title: "Not Logged In",
202
+ description: "You must be logged in to save your package.",
203
+ })
204
+ return
205
+ }
206
+
207
+ const newPackage = await createPackageMutation.mutateAsync({
208
+ name: `${loggedInUser?.github_username}/${generateRandomPackageName()}`,
209
+ is_private: isPrivate,
50
210
  })
51
- onFileSelect(newFileName)
52
- setIsCreatingFile(false)
53
- setNewFileName("")
211
+
212
+ if (newPackage) {
213
+ createRelease(
214
+ {
215
+ package_name_with_version: `${newPackage.name}@latest`,
216
+ },
217
+ {
218
+ onSuccess: () => {
219
+ updatePackageFilesMutation.mutate({
220
+ package_name_with_version: `${newPackage.name}@latest`,
221
+ ...newPackage,
222
+ })
223
+ updateLastUpdated()
224
+ setInitialFiles([...localFiles])
225
+ },
226
+ },
227
+ )
228
+ }
229
+ updateLastUpdated()
230
+ }
231
+
232
+ const saveFiles = () => {
233
+ if (!isLoggedIn) {
234
+ toast({
235
+ title: "Not Logged In",
236
+ description: "You must be logged in to save your package.",
237
+ })
238
+ return
239
+ }
240
+ if (!currentPackage) {
241
+ openNewPackageSaveDialog()
242
+ return
243
+ }
244
+ updatePackageFilesMutation.mutate({
245
+ package_name_with_version: `${currentPackage.name}@latest`,
246
+ ...currentPackage,
247
+ })
248
+ updateLastUpdated()
249
+ setInitialFiles([...localFiles])
54
250
  }
55
251
 
252
+ const isSaving = useMemo(() => {
253
+ return (
254
+ updatePackageFilesMutation.isLoading ||
255
+ createPackageMutation.isLoading ||
256
+ isCreatingRelease
257
+ )
258
+ }, [
259
+ updatePackageFilesMutation.isLoading,
260
+ createPackageMutation.isLoading,
261
+ isCreatingRelease,
262
+ ])
263
+
56
264
  return {
57
- handleCreateFile,
265
+ fsMap,
266
+ createFile,
267
+ deleteFile,
268
+ saveFiles,
269
+ localFiles,
270
+ initialFiles,
271
+ currentFile,
272
+ setLocalFiles,
273
+ onFileSelect,
274
+ isLoading,
275
+ isSaving,
276
+ savePackage,
58
277
  }
59
278
  }
@@ -33,8 +33,8 @@ export function usePackageFilesLoader(pkg?: Package) {
33
33
  const response = await axios.post(`/package_files/get`, {
34
34
  package_file_id: file.package_file_id,
35
35
  })
36
- const content = response.data.package_file?.content_text ?? ""
37
- return content ? { path: file.file_path, content } : null
36
+ const content = response.data.package_file?.content_text
37
+ return { path: file.file_path, content: content ?? "" }
38
38
  },
39
39
  staleTime: 2,
40
40
  })) ?? [],
@@ -1,5 +1,7 @@
1
1
  import { useMutation } from "react-query"
2
2
  import type { Package } from "fake-snippets-api/lib/db/schema"
3
+ import { useAxios } from "./use-axios"
4
+ import { useToast } from "@/components/ViewPackagePage/hooks/use-toast"
3
5
 
4
6
  interface PackageFile {
5
7
  path: string
@@ -7,47 +9,49 @@ interface PackageFile {
7
9
  }
8
10
 
9
11
  interface UseUpdatePackageFilesMutationProps {
10
- pkg: Package | undefined
11
- pkgFilesWithContent: PackageFile[]
12
- initialFilesLoad: PackageFile[]
13
- pkgFiles: any
14
- axios: any
15
- toast: any
12
+ currentPackage: Package | undefined
13
+ localFiles: PackageFile[]
14
+ initialFiles: PackageFile[]
15
+ packageFilesMeta: {
16
+ created_at: string
17
+ file_path: string
18
+ package_file_id: string
19
+ package_release_id: string
20
+ }[]
16
21
  }
17
22
 
18
23
  export function useUpdatePackageFilesMutation({
19
- pkg,
20
- pkgFilesWithContent,
21
- initialFilesLoad,
22
- pkgFiles,
23
- axios,
24
- toast,
24
+ currentPackage,
25
+ localFiles,
26
+ initialFiles,
27
+ packageFilesMeta,
25
28
  }: UseUpdatePackageFilesMutationProps) {
29
+ const axios = useAxios()
30
+ const { toast } = useToast()
26
31
  return useMutation({
27
32
  mutationFn: async (
28
- newpackage: Pick<Package, "package_id" | "name"> & {
33
+ newPackage: Pick<Package, "package_id" | "name"> & {
29
34
  package_name_with_version: string
30
35
  },
31
36
  ) => {
32
- if (pkg) {
33
- newpackage = { ...pkg, ...newpackage }
37
+ if (currentPackage) {
38
+ newPackage = { ...currentPackage, ...newPackage }
34
39
  }
35
- if (!newpackage) throw new Error("No package to update")
40
+ if (!newPackage) throw new Error("No package to update")
36
41
 
37
42
  let updatedFilesCount = 0
38
43
 
39
- for (const file of pkgFilesWithContent) {
40
- const initialFile = initialFilesLoad.find((x) => x.path === file.path)
41
- if (file.content && file.content !== initialFile?.content) {
44
+ for (const file of localFiles) {
45
+ const initialFile = initialFiles.find((x) => x.path === file.path)
46
+ if (file.content !== initialFile?.content) {
42
47
  const updatePkgFilePayload = {
43
48
  package_file_id:
44
- pkgFiles.data?.find((x: any) => x.file_path === file.path)
49
+ packageFilesMeta.find((x) => x.file_path === file.path)
45
50
  ?.package_file_id ?? null,
46
51
  content_text: file.content,
47
52
  file_path: file.path,
48
- package_name_with_version: `${newpackage.name}`,
53
+ package_name_with_version: `${newPackage.name}`,
49
54
  }
50
-
51
55
  const response = await axios.post(
52
56
  "/package_files/create_or_update",
53
57
  updatePkgFilePayload,
@@ -58,10 +62,33 @@ export function useUpdatePackageFilesMutation({
58
62
  }
59
63
  }
60
64
  }
65
+
66
+ for (const initialFile of initialFiles) {
67
+ const fileStillExists = localFiles.some(
68
+ (x) => x.path === initialFile.path,
69
+ )
70
+
71
+ if (!fileStillExists) {
72
+ const fileToDelete = packageFilesMeta.find(
73
+ (x) => x.file_path === initialFile.path,
74
+ )
75
+
76
+ if (fileToDelete?.package_file_id) {
77
+ const response = await axios.post("/package_files/delete", {
78
+ package_name_with_version: `${newPackage.name}`,
79
+ file_path: initialFile.path,
80
+ })
81
+
82
+ if (response.status === 200) {
83
+ updatedFilesCount++
84
+ }
85
+ }
86
+ }
87
+ }
61
88
  return updatedFilesCount
62
89
  },
63
90
  onSuccess: (updatedFilesCount) => {
64
- if (updatedFilesCount) {
91
+ if (updatedFilesCount > 0) {
65
92
  toast({
66
93
  title: `Package's ${updatedFilesCount} files saved`,
67
94
  description: "Your changes have been saved successfully.",
@@ -69,7 +96,6 @@ export function useUpdatePackageFilesMutation({
69
96
  }
70
97
  },
71
98
  onError: (error: any) => {
72
- console.error("Error updating pkg files:", error)
73
99
  toast({
74
100
  title: "Error",
75
101
  description:
@@ -1,8 +1,17 @@
1
+ import { findTargetFile } from "./findTargetFile"
2
+
1
3
  export const checkIfManualEditsImported = (
2
4
  files: Record<string, string>,
3
5
  file: string = "index.tsx",
4
6
  ) => {
5
7
  if (!files[file]) return false
8
+ const targetFile = findTargetFile(
9
+ Object.keys(files).map((f) => ({ path: f, content: files[f] })),
10
+ null,
11
+ )
12
+ if (targetFile && file !== targetFile.path) {
13
+ return false
14
+ }
6
15
  if (!file.endsWith(".tsx") && !file.endsWith(".ts")) return false
7
16
  const importRegex =
8
17
  /import\s+(?:\*\s+as\s+)?([a-zA-Z_$][\w$]*)\s+from\s+["'](?:\.\/)?manual-edits\.json["'];?/
@@ -4,14 +4,10 @@ import Header from "@/components/Header"
4
4
  import { Helmet } from "react-helmet-async"
5
5
  import { useCurrentPackageId } from "@/hooks/use-current-package-id"
6
6
  import { usePackage } from "@/hooks/use-package"
7
- import { useGetFsMapHashForPackage } from "@/hooks/use-get-fsmap-hash-for-package"
8
7
 
9
8
  export const EditorPage = () => {
10
9
  const { packageId } = useCurrentPackageId()
11
10
  const { data: pkg, isLoading, error } = usePackage(packageId)
12
- const fsMapHash = useGetFsMapHashForPackage(
13
- pkg?.latest_package_release_id ?? "",
14
- )
15
11
 
16
12
  return (
17
13
  <div className="overflow-x-hidden">
@@ -27,16 +23,12 @@ export const EditorPage = () => {
27
23
  />
28
24
  <meta
29
25
  property="og:image"
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()}`}
26
+ content={`https://registry-api.tscircuit.com/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.png?fs_sha=${pkg.latest_package_release_fs_sha}`}
35
27
  />
36
28
  <meta name="twitter:card" content="summary_large_image" />
37
29
  <meta
38
30
  name="twitter:image"
39
- content={`https://registry-api.tscircuit.com/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.png`}
31
+ content={`https://registry-api.tscircuit.com/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.png?fs_sha=${pkg.latest_package_release_fs_sha}`}
40
32
  />
41
33
  </>
42
34
  )}
@@ -0,0 +1,33 @@
1
+ import { CodeAndPreview } from "@/components/package-port/CodeAndPreview"
2
+ import Footer from "@/components/Footer"
3
+ import Header from "@/components/Header"
4
+ import { usePackage } from "@/hooks/use-package"
5
+ import { Helmet } from "react-helmet-async"
6
+ import { useCurrentPackageId } from "@/hooks/use-current-package-id"
7
+ import { NotFound } from "@/components/NotFound"
8
+ import { ErrorOutline } from "@/components/ErrorOutline"
9
+ import { DeploymentDetailsPage } from "@/components/PackageBuildsPage/DeploymentDetailsPage"
10
+
11
+ export const EditorPage = () => {
12
+ const { packageId } = useCurrentPackageId()
13
+ const { data: pkg, error } = usePackage(packageId)
14
+ const uuid4RegExp = new RegExp(
15
+ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/,
16
+ )
17
+
18
+ return (
19
+ <div className="overflow-x-hidden">
20
+ <Helmet>
21
+ <title>{pkg ? `${pkg.name} Package Builds` : "Package Builds"}</title>
22
+ {pkg && (
23
+ <>
24
+ <meta property="og:title" content={`${pkg.name} Package Builds`} />
25
+ </>
26
+ )}
27
+ </Helmet>
28
+ <Header />
29
+ <DeploymentDetailsPage />
30
+ <Footer />
31
+ </div>
32
+ )
33
+ }