@tscircuit/fake-snippets 0.0.70 → 0.0.72
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.
- package/api/generated-index.js +77 -3
- package/bun-tests/fake-snippets-api/routes/accounts/get.test.ts +53 -0
- package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -0
- package/bun.lock +30 -46
- package/dist/bundle.js +17 -6
- package/fake-snippets-api/routes/api/accounts/get.ts +16 -3
- package/fake-snippets-api/routes/api/package_files/create_or_update.ts +3 -3
- package/index.html +3 -0
- package/package.json +7 -7
- package/src/ContextProviders.tsx +2 -0
- package/src/components/FileSidebar.tsx +111 -37
- package/src/components/HeaderLogin.tsx +2 -2
- package/src/components/package-port/CodeAndPreview.tsx +78 -267
- package/src/components/package-port/CodeEditor.tsx +29 -18
- package/src/components/package-port/CodeEditorHeader.tsx +7 -6
- package/src/components/ui/tree-view.tsx +3 -3
- package/src/hooks/useFileManagement.ts +257 -38
- package/src/hooks/usePackageFilesLoader.ts +2 -2
- package/src/hooks/useUpdatePackageFilesMutation.ts +50 -24
- package/src/lib/populate-query-cache-with-ssr-data.ts +52 -0
- package/src/pages/dashboard.tsx +2 -2
- package/src/pages/landing.tsx +14 -3
|
@@ -1,59 +1,278 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useMemo, useState, useCallback } from "react"
|
|
2
2
|
import { isValidFileName } from "@/lib/utils/isValidFileName"
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
)
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
setNewFileName,
|
|
17
|
-
setIsCreatingFile,
|
|
18
|
-
}: CreateFileProps) => {
|
|
145
|
+
onError,
|
|
146
|
+
}: ICreateFileProps): ICreateFileResult => {
|
|
19
147
|
newFileName = newFileName.trim()
|
|
20
148
|
if (!newFileName) {
|
|
21
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
33
|
+
newPackage: Pick<Package, "package_id" | "name"> & {
|
|
29
34
|
package_name_with_version: string
|
|
30
35
|
},
|
|
31
36
|
) => {
|
|
32
|
-
if (
|
|
33
|
-
|
|
37
|
+
if (currentPackage) {
|
|
38
|
+
newPackage = { ...currentPackage, ...newPackage }
|
|
34
39
|
}
|
|
35
|
-
if (!
|
|
40
|
+
if (!newPackage) throw new Error("No package to update")
|
|
36
41
|
|
|
37
42
|
let updatedFilesCount = 0
|
|
38
43
|
|
|
39
|
-
for (const file of
|
|
40
|
-
const initialFile =
|
|
41
|
-
if (file.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
|
-
|
|
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: `${
|
|
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:
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { QueryClient } from "react-query"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Populates React Query cache with SSR data to prevent unnecessary API calls
|
|
5
|
+
* on initial page load. Should be called after QueryClient creation.
|
|
6
|
+
*/
|
|
7
|
+
export function populateQueryCacheWithSSRData(queryClient: QueryClient) {
|
|
8
|
+
if (typeof window === "undefined") return
|
|
9
|
+
|
|
10
|
+
const ssrPackage = (window as any).SSR_PACKAGE
|
|
11
|
+
const ssrPackageRelease = (window as any).SSR_PACKAGE_RELEASE
|
|
12
|
+
const ssrPackageFiles = (window as any).SSR_PACKAGE_FILES
|
|
13
|
+
|
|
14
|
+
if (ssrPackage) {
|
|
15
|
+
// Cache package data with all possible query keys
|
|
16
|
+
queryClient.setQueryData(["package", ssrPackage.package_id], ssrPackage)
|
|
17
|
+
queryClient.setQueryData(["package", ssrPackage.name], ssrPackage)
|
|
18
|
+
queryClient.setQueryData(["packages", ssrPackage.package_id], ssrPackage)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (ssrPackageRelease && ssrPackage) {
|
|
22
|
+
// Cache package release with various query patterns
|
|
23
|
+
queryClient.setQueryData(
|
|
24
|
+
[
|
|
25
|
+
"packageRelease",
|
|
26
|
+
{ package_id: ssrPackage.package_id, is_latest: true },
|
|
27
|
+
],
|
|
28
|
+
ssrPackageRelease,
|
|
29
|
+
)
|
|
30
|
+
queryClient.setQueryData(
|
|
31
|
+
["packageRelease", { package_name: ssrPackage.name, is_latest: true }],
|
|
32
|
+
ssrPackageRelease,
|
|
33
|
+
)
|
|
34
|
+
if (ssrPackageRelease.package_release_id) {
|
|
35
|
+
queryClient.setQueryData(
|
|
36
|
+
[
|
|
37
|
+
"packageRelease",
|
|
38
|
+
{ package_release_id: ssrPackageRelease.package_release_id },
|
|
39
|
+
],
|
|
40
|
+
ssrPackageRelease,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Cache package files if available
|
|
45
|
+
if (ssrPackageFiles && ssrPackageRelease.package_release_id) {
|
|
46
|
+
queryClient.setQueryData(
|
|
47
|
+
["packageFiles", ssrPackageRelease.package_release_id],
|
|
48
|
+
ssrPackageFiles,
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/pages/dashboard.tsx
CHANGED
|
@@ -94,10 +94,10 @@ export const DashboardPage = () => {
|
|
|
94
94
|
</h2>
|
|
95
95
|
|
|
96
96
|
<p className="text-gray-600 mb-6 text-center max-w-md text-sm sm:text-base">
|
|
97
|
-
Log in to access your dashboard and manage your
|
|
97
|
+
Log in to access your dashboard and manage your packages.
|
|
98
98
|
</p>
|
|
99
99
|
<Button onClick={() => signIn()} variant="outline">
|
|
100
|
-
Log
|
|
100
|
+
Log In
|
|
101
101
|
</Button>
|
|
102
102
|
</div>
|
|
103
103
|
) : (
|
package/src/pages/landing.tsx
CHANGED
|
@@ -60,16 +60,27 @@ export function LandingPage() {
|
|
|
60
60
|
</p>
|
|
61
61
|
</div>
|
|
62
62
|
<div className="flex flex-col items-center gap-2 min-[500px]:flex-row">
|
|
63
|
-
<a
|
|
64
|
-
|
|
63
|
+
<a
|
|
64
|
+
href="https://docs.tscircuit.com"
|
|
65
|
+
className="w-[70vw] min-[500px]:w-auto"
|
|
66
|
+
>
|
|
67
|
+
<Button
|
|
68
|
+
size="lg"
|
|
69
|
+
aria-label="Get started with TSCircuit"
|
|
70
|
+
className="w-full min-[500px]:w-auto"
|
|
71
|
+
>
|
|
65
72
|
Get Started
|
|
66
73
|
</Button>
|
|
67
74
|
</a>
|
|
68
|
-
<PrefetchPageLink
|
|
75
|
+
<PrefetchPageLink
|
|
76
|
+
href="/seveibar/usb-c-flashlight#3d"
|
|
77
|
+
className="w-[70vw] min-[500px]:w-auto"
|
|
78
|
+
>
|
|
69
79
|
<Button
|
|
70
80
|
size="lg"
|
|
71
81
|
variant="outline"
|
|
72
82
|
aria-label="Open online example of TSCircuit"
|
|
83
|
+
className="w-full min-[500px]:w-auto"
|
|
73
84
|
>
|
|
74
85
|
Open Online Example
|
|
75
86
|
</Button>
|