@tscircuit/fake-snippets 0.0.107 → 0.0.109
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 +82 -22
- package/biome.json +7 -1
- package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +0 -15
- package/bun-tests/fake-snippets-api/routes/package_builds/list.test.ts +0 -12
- package/bun.lock +62 -19
- package/dist/bundle.js +25 -24
- package/dist/index.d.ts +26 -15
- package/dist/index.js +19 -18
- package/dist/schema.d.ts +32 -24
- package/dist/schema.js +7 -6
- package/fake-snippets-api/lib/db/db-client.ts +10 -1
- package/fake-snippets-api/lib/db/schema.ts +4 -3
- package/fake-snippets-api/lib/db/seed.ts +6 -9
- package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +0 -3
- package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +3 -0
- package/package.json +7 -8
- package/src/App.tsx +12 -11
- package/src/components/DownloadButtonAndMenu.tsx +133 -35
- package/src/components/FileSidebar.tsx +45 -193
- package/src/components/Footer.tsx +0 -1
- package/src/components/HeaderLogin.tsx +1 -1
- package/src/components/HiddenFilesDropdown.tsx +0 -2
- package/src/components/PackageBreadcrumb.tsx +1 -1
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -2
- package/src/components/PackageBuildsPage/build-preview-content.tsx +34 -5
- package/src/components/PackageCard.tsx +0 -1
- package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +20 -11
- package/src/components/ViewPackagePage/components/important-files-view.tsx +75 -59
- package/src/components/ViewPackagePage/components/main-content-header.tsx +4 -4
- package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +0 -1
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -2
- package/src/components/ViewPackagePage/components/package-header.tsx +14 -17
- package/src/components/ViewPackagePage/components/preview-image-squares.tsx +0 -1
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +21 -20
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +18 -2
- package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +1 -1
- package/src/components/ViewPackagePage/components/sidebar.tsx +0 -2
- package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +18 -17
- package/src/components/ViewPackagePage/components/theme-toggle.tsx +0 -2
- package/src/components/ViewPackagePage/hooks/use-toast.tsx +0 -1
- package/src/components/package-port/CodeAndPreview.tsx +23 -40
- package/src/components/package-port/CodeEditor.tsx +24 -1
- package/src/components/package-port/CodeEditorHeader.tsx +5 -2
- package/src/components/preview/BuildsList.tsx +20 -9
- package/src/components/preview/ConnectedPackagesList.tsx +73 -60
- package/src/components/preview/ConnectedRepoOverview.tsx +160 -154
- package/src/components/preview/PackageReleasesDashboard.tsx +41 -30
- package/src/components/preview/index.tsx +16 -153
- package/src/hooks/use-current-package-id.ts +5 -30
- package/src/hooks/use-current-package-info.ts +29 -5
- package/src/hooks/use-global-store.ts +1 -1
- package/src/hooks/useFileManagement.ts +153 -34
- package/src/hooks/useOptimizedPackageFilesLoader.ts +149 -0
- package/src/hooks/useUpdatePackageFilesMutation.ts +2 -0
- package/src/index.css +24 -0
- package/src/lib/download-fns/download-circuit-png.ts +11 -3
- package/src/lib/download-fns/download-gltf-from-circuit-json.ts +44 -0
- package/src/lib/utils/isComponentExported.ts +9 -0
- package/src/lib/utils/transformFilesToTreeData.tsx +195 -0
- package/src/pages/404.tsx +3 -5
- package/src/pages/authorize.tsx +0 -2
- package/src/pages/landing.tsx +0 -1
- package/src/pages/preview-release.tsx +279 -0
- package/src/pages/release-builds.tsx +0 -8
- package/src/pages/release-detail.tsx +17 -15
- package/src/pages/releases.tsx +5 -1
- package/src/pages/view-package.tsx +14 -13
- package/src/components/Footer2.tsx +0 -100
- package/src/components/ShippingInformationForm.tsx +0 -423
- package/src/components/StaticViewSnippetHeader.tsx +0 -70
- package/src/components/ViewPackagePage/components/file-explorer.tsx +0 -67
- package/src/components/ViewPackagePage/components/readme-view.tsx +0 -58
- package/src/components/ViewPackagePage/components/repo-header-button.tsx +0 -36
- package/src/components/ViewPackagePage/components/repo-header.tsx +0 -4
- package/src/components/ViewPackagePage/components/sidebar-contributors-section.tsx +0 -31
- package/src/components/ViewSnippetHeader.tsx +0 -181
- package/src/components/ui/input-otp.tsx +0 -69
- package/src/hooks/use-snippets-base-api-url.ts +0 -3
- package/src/pages/preview-build.tsx +0 -380
- package/src/pages/settings.tsx +0 -25
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
export { ConnectedRepoOverview } from "./ConnectedRepoOverview"
|
|
2
2
|
export { BuildsList } from "./BuildsList"
|
|
3
3
|
export { PackageReleasesDashboard } from "./PackageReleasesDashboard"
|
|
4
|
-
import {
|
|
5
|
-
Package,
|
|
6
|
-
PackageBuild,
|
|
7
|
-
PackageRelease,
|
|
8
|
-
} from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
import { PackageBuild } from "fake-snippets-api/lib/db/schema"
|
|
9
5
|
import { Clock, CheckCircle, AlertCircle, Loader2 } from "lucide-react"
|
|
10
|
-
export const getBuildStatus = (
|
|
6
|
+
export const getBuildStatus = (
|
|
7
|
+
build?: PackageBuild | null,
|
|
8
|
+
): {
|
|
9
|
+
status: "pending" | "building" | "success" | "error" | "queued"
|
|
10
|
+
label: string
|
|
11
|
+
} => {
|
|
11
12
|
if (!build) {
|
|
12
13
|
return { status: "pending", label: "No builds" }
|
|
13
14
|
}
|
|
@@ -25,154 +26,20 @@ export const getBuildStatus = (build: PackageBuild | null) => {
|
|
|
25
26
|
) {
|
|
26
27
|
return { status: "building", label: "Building" }
|
|
27
28
|
}
|
|
28
|
-
if (
|
|
29
|
+
if (
|
|
30
|
+
!build?.build_error &&
|
|
31
|
+
!build?.transpilation_error &&
|
|
32
|
+
!build?.circuit_json_build_error &&
|
|
33
|
+
!build?.build_in_progress &&
|
|
34
|
+
!build?.transpilation_in_progress &&
|
|
35
|
+
!build?.circuit_json_build_in_progress &&
|
|
36
|
+
build?.transpilation_completed_at
|
|
37
|
+
) {
|
|
29
38
|
return { status: "success", label: "Ready" }
|
|
30
39
|
}
|
|
31
40
|
return { status: "queued", label: "Queued" }
|
|
32
41
|
}
|
|
33
42
|
|
|
34
|
-
export const MOCK_PACKAGE_BUILDS: PackageBuild[] = [
|
|
35
|
-
{
|
|
36
|
-
package_build_id: "pb_1a2b3c4d",
|
|
37
|
-
package_release_id: "pr_5e6f7g8h",
|
|
38
|
-
created_at: new Date(Date.now() - 1000 * 60 * 30).toISOString(),
|
|
39
|
-
transpilation_in_progress: false,
|
|
40
|
-
transpilation_started_at: new Date(
|
|
41
|
-
Date.now() - 1000 * 60 * 35,
|
|
42
|
-
).toISOString(),
|
|
43
|
-
transpilation_completed_at: new Date(
|
|
44
|
-
Date.now() - 1000 * 60 * 32,
|
|
45
|
-
).toISOString(),
|
|
46
|
-
transpilation_logs: [],
|
|
47
|
-
transpilation_error: null,
|
|
48
|
-
circuit_json_build_in_progress: false,
|
|
49
|
-
circuit_json_build_started_at: new Date(
|
|
50
|
-
Date.now() - 1000 * 60 * 32,
|
|
51
|
-
).toISOString(),
|
|
52
|
-
circuit_json_build_completed_at: new Date(
|
|
53
|
-
Date.now() - 1000 * 60 * 30,
|
|
54
|
-
).toISOString(),
|
|
55
|
-
circuit_json_build_logs: [],
|
|
56
|
-
circuit_json_build_error: null,
|
|
57
|
-
build_in_progress: false,
|
|
58
|
-
build_started_at: new Date(Date.now() - 1000 * 60 * 30).toISOString(),
|
|
59
|
-
build_completed_at: new Date(Date.now() - 1000 * 60 * 25).toISOString(),
|
|
60
|
-
build_error: null,
|
|
61
|
-
build_error_last_updated_at: new Date(
|
|
62
|
-
Date.now() - 1000 * 60 * 25,
|
|
63
|
-
).toISOString(),
|
|
64
|
-
build_logs: null,
|
|
65
|
-
preview_url: "https://preview.tscircuit.com/pb_1a2b3c4d",
|
|
66
|
-
branch_name: "main",
|
|
67
|
-
commit_message: "Add new LED component with improved brightness control",
|
|
68
|
-
commit_author: "john.doe",
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
package_build_id: "pb_9i8j7k6l",
|
|
72
|
-
package_release_id: "pr_5m4n3o2p",
|
|
73
|
-
created_at: new Date(Date.now() - 1000 * 60 * 60 * 2).toISOString(),
|
|
74
|
-
transpilation_in_progress: false,
|
|
75
|
-
transpilation_started_at: new Date(
|
|
76
|
-
Date.now() - 1000 * 60 * 60 * 2,
|
|
77
|
-
).toISOString(),
|
|
78
|
-
transpilation_completed_at: new Date(
|
|
79
|
-
Date.now() - 1000 * 60 * 60 * 2 + 1000 * 60 * 3,
|
|
80
|
-
).toISOString(),
|
|
81
|
-
transpilation_logs: [],
|
|
82
|
-
transpilation_error: null,
|
|
83
|
-
circuit_json_build_in_progress: true,
|
|
84
|
-
circuit_json_build_started_at: new Date(
|
|
85
|
-
Date.now() - 1000 * 60 * 5,
|
|
86
|
-
).toISOString(),
|
|
87
|
-
circuit_json_build_completed_at: null,
|
|
88
|
-
circuit_json_build_logs: [],
|
|
89
|
-
circuit_json_build_error: null,
|
|
90
|
-
build_in_progress: false,
|
|
91
|
-
build_started_at: null,
|
|
92
|
-
build_completed_at: null,
|
|
93
|
-
build_error: null,
|
|
94
|
-
build_error_last_updated_at: new Date(
|
|
95
|
-
Date.now() - 1000 * 60 * 60 * 2,
|
|
96
|
-
).toISOString(),
|
|
97
|
-
build_logs: null,
|
|
98
|
-
preview_url: null,
|
|
99
|
-
branch_name: "feature/resistor-update",
|
|
100
|
-
commit_message: "Update resistor component with new tolerance values",
|
|
101
|
-
commit_author: "jane.smith",
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
package_build_id: "pb_1q2w3e4r",
|
|
105
|
-
package_release_id: "pr_5t6y7u8i",
|
|
106
|
-
created_at: new Date(Date.now() - 1000 * 60 * 60 * 6).toISOString(),
|
|
107
|
-
transpilation_in_progress: false,
|
|
108
|
-
transpilation_started_at: new Date(
|
|
109
|
-
Date.now() - 1000 * 60 * 60 * 6,
|
|
110
|
-
).toISOString(),
|
|
111
|
-
transpilation_completed_at: new Date(
|
|
112
|
-
Date.now() - 1000 * 60 * 60 * 6 + 1000 * 60 * 2,
|
|
113
|
-
).toISOString(),
|
|
114
|
-
transpilation_logs: [],
|
|
115
|
-
transpilation_error:
|
|
116
|
-
"TypeScript compilation failed: Cannot find module 'missing-dependency'",
|
|
117
|
-
circuit_json_build_in_progress: false,
|
|
118
|
-
circuit_json_build_started_at: null,
|
|
119
|
-
circuit_json_build_completed_at: null,
|
|
120
|
-
circuit_json_build_logs: [],
|
|
121
|
-
circuit_json_build_error: null,
|
|
122
|
-
build_in_progress: false,
|
|
123
|
-
build_started_at: null,
|
|
124
|
-
build_completed_at: null,
|
|
125
|
-
build_error: null,
|
|
126
|
-
build_error_last_updated_at: new Date(
|
|
127
|
-
Date.now() - 1000 * 60 * 60 * 6,
|
|
128
|
-
).toISOString(),
|
|
129
|
-
build_logs: null,
|
|
130
|
-
preview_url: null,
|
|
131
|
-
branch_name: "hotfix/critical-bug",
|
|
132
|
-
commit_message: "Fix critical issue with capacitor placement",
|
|
133
|
-
commit_author: "alex.wilson",
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
package_build_id: "pb_9o8i7u6y",
|
|
137
|
-
package_release_id: "pr_5t4r3e2w",
|
|
138
|
-
created_at: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(),
|
|
139
|
-
transpilation_in_progress: false,
|
|
140
|
-
transpilation_started_at: new Date(
|
|
141
|
-
Date.now() - 1000 * 60 * 60 * 24,
|
|
142
|
-
).toISOString(),
|
|
143
|
-
transpilation_completed_at: new Date(
|
|
144
|
-
Date.now() - 1000 * 60 * 60 * 24 + 1000 * 60 * 4,
|
|
145
|
-
).toISOString(),
|
|
146
|
-
transpilation_logs: [],
|
|
147
|
-
transpilation_error: null,
|
|
148
|
-
circuit_json_build_in_progress: false,
|
|
149
|
-
circuit_json_build_started_at: new Date(
|
|
150
|
-
Date.now() - 1000 * 60 * 60 * 24 + 1000 * 60 * 4,
|
|
151
|
-
).toISOString(),
|
|
152
|
-
circuit_json_build_completed_at: new Date(
|
|
153
|
-
Date.now() - 1000 * 60 * 60 * 24 + 1000 * 60 * 8,
|
|
154
|
-
).toISOString(),
|
|
155
|
-
circuit_json_build_logs: [],
|
|
156
|
-
circuit_json_build_error: null,
|
|
157
|
-
build_in_progress: false,
|
|
158
|
-
build_started_at: new Date(
|
|
159
|
-
Date.now() - 1000 * 60 * 60 * 24 + 1000 * 60 * 8,
|
|
160
|
-
).toISOString(),
|
|
161
|
-
build_completed_at: new Date(
|
|
162
|
-
Date.now() - 1000 * 60 * 60 * 24 + 1000 * 60 * 12,
|
|
163
|
-
).toISOString(),
|
|
164
|
-
build_error: null,
|
|
165
|
-
build_error_last_updated_at: new Date(
|
|
166
|
-
Date.now() - 1000 * 60 * 60 * 24 + 1000 * 60 * 12,
|
|
167
|
-
).toISOString(),
|
|
168
|
-
build_logs: null,
|
|
169
|
-
preview_url: "https://preview.tscircuit.com/pb_9o8i7u6y",
|
|
170
|
-
branch_name: "main",
|
|
171
|
-
commit_message: "Initial project setup with basic components",
|
|
172
|
-
commit_author: "sarah.johnson",
|
|
173
|
-
},
|
|
174
|
-
]
|
|
175
|
-
|
|
176
43
|
export const StatusIcon = ({ status }: { status: string }) => {
|
|
177
44
|
switch (status) {
|
|
178
45
|
case "success":
|
|
@@ -185,7 +52,3 @@ export const StatusIcon = ({ status }: { status: string }) => {
|
|
|
185
52
|
return <Clock className="w-4 h-4 text-gray-500" />
|
|
186
53
|
}
|
|
187
54
|
}
|
|
188
|
-
|
|
189
|
-
export const getLatestBuildForPackage = (pkg: Package): PackageBuild => {
|
|
190
|
-
return MOCK_PACKAGE_BUILDS[0]
|
|
191
|
-
}
|
|
@@ -1,41 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useParams } from "wouter"
|
|
3
|
-
import { usePackageById } from "./use-package-by-package-id"
|
|
4
|
-
import { usePackageByName } from "./use-package-by-package-name"
|
|
5
|
-
import { useUrlParams } from "./use-url-params"
|
|
1
|
+
import { useCurrentPackageInfo } from "./use-current-package-info"
|
|
6
2
|
|
|
7
3
|
export const useCurrentPackageId = (): {
|
|
8
4
|
packageId: string | null
|
|
9
5
|
isLoading: boolean
|
|
10
6
|
error: (Error & { status: number }) | null
|
|
11
7
|
} => {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const wouter = useParams()
|
|
15
|
-
const [packageIdFromUrl, setPackageId] = useState<string | null>(urlPackageId)
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (urlPackageId) {
|
|
19
|
-
setPackageId(urlPackageId)
|
|
20
|
-
}
|
|
21
|
-
}, [urlPackageId])
|
|
22
|
-
|
|
23
|
-
const packageName =
|
|
24
|
-
wouter.author && wouter.packageName
|
|
25
|
-
? `${wouter.author}/${wouter.packageName}`
|
|
26
|
-
: null
|
|
27
|
-
|
|
28
|
-
const {
|
|
29
|
-
data: packageByName,
|
|
30
|
-
isLoading: isLoadingPackageByName,
|
|
31
|
-
error: errorPackageByName,
|
|
32
|
-
} = usePackageByName(packageName)
|
|
33
|
-
|
|
34
|
-
const packageId = packageIdFromUrl ?? packageByName?.package_id ?? null
|
|
8
|
+
const { packageInfo, isLoading, error } = useCurrentPackageInfo()
|
|
9
|
+
const packageId = packageInfo?.package_id ?? null
|
|
35
10
|
|
|
36
11
|
return {
|
|
37
12
|
packageId,
|
|
38
|
-
isLoading
|
|
39
|
-
error
|
|
13
|
+
isLoading,
|
|
14
|
+
error,
|
|
40
15
|
}
|
|
41
16
|
}
|
|
@@ -1,8 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useParams } from "wouter"
|
|
2
2
|
import { usePackageById } from "./use-package-by-package-id"
|
|
3
|
+
import { usePackageByName } from "./use-package-by-package-name"
|
|
4
|
+
import { useUrlParams } from "./use-url-params"
|
|
5
|
+
import type { Package } from "fake-snippets-api/lib/db/schema"
|
|
3
6
|
|
|
4
|
-
export const useCurrentPackageInfo = ()
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
export const useCurrentPackageInfo = (): {
|
|
8
|
+
packageInfo: Package | undefined
|
|
9
|
+
isLoading: boolean
|
|
10
|
+
error: (Error & { status: number }) | null
|
|
11
|
+
refetch: () => Promise<unknown>
|
|
12
|
+
} => {
|
|
13
|
+
const urlParams = useUrlParams()
|
|
14
|
+
const packageIdFromQuery = urlParams.package_id ?? null
|
|
15
|
+
|
|
16
|
+
const { author, packageName } = useParams()
|
|
17
|
+
const packageSlug = author && packageName ? `${author}/${packageName}` : null
|
|
18
|
+
|
|
19
|
+
const queryById = usePackageById(packageIdFromQuery)
|
|
20
|
+
const queryByName = usePackageByName(packageSlug)
|
|
21
|
+
|
|
22
|
+
const data = queryById.data ?? queryByName.data
|
|
23
|
+
const isLoading = queryById.isLoading || queryByName.isLoading
|
|
24
|
+
const error =
|
|
25
|
+
(queryById.error as (Error & { status: number }) | null) ??
|
|
26
|
+
(queryByName.error as (Error & { status: number }) | null) ??
|
|
27
|
+
null
|
|
28
|
+
|
|
29
|
+
const refetch = packageIdFromQuery ? queryById.refetch : queryByName.refetch
|
|
30
|
+
|
|
31
|
+
return { packageInfo: data, isLoading, error, refetch }
|
|
8
32
|
}
|
|
@@ -28,7 +28,7 @@ export const useGlobalStore = create<Store>()(
|
|
|
28
28
|
),
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
useGlobalStore.subscribe((state
|
|
31
|
+
useGlobalStore.subscribe((state) => {
|
|
32
32
|
;(window as any).globalStore = state
|
|
33
33
|
window.TSCIRCUIT_REGISTRY_TOKEN = state.session?.token ?? null
|
|
34
34
|
})
|
|
@@ -6,7 +6,7 @@ import { Package } from "fake-snippets-api/lib/db/schema"
|
|
|
6
6
|
import { usePackageFiles } from "./use-package-files"
|
|
7
7
|
import { decodeUrlHashToText } from "@/lib/decodeUrlHashToText"
|
|
8
8
|
import { decodeUrlHashToFsMap } from "@/lib/decodeUrlHashToFsMap"
|
|
9
|
-
import {
|
|
9
|
+
import { useOptimizedPackageFilesLoader } from "./useOptimizedPackageFilesLoader"
|
|
10
10
|
import { useGlobalStore } from "./use-global-store"
|
|
11
11
|
import { useToast } from "@/components/ViewPackagePage/hooks/use-toast"
|
|
12
12
|
import { useUpdatePackageFilesMutation } from "./useUpdatePackageFilesMutation"
|
|
@@ -15,6 +15,7 @@ import { useCreatePackageMutation } from "./use-create-package-mutation"
|
|
|
15
15
|
import { findTargetFile } from "@/lib/utils/findTargetFile"
|
|
16
16
|
import { encodeFsMapToUrlHash } from "@/lib/encodeFsMapToUrlHash"
|
|
17
17
|
import { isHiddenFile } from "@/components/ViewPackagePage/utils/is-hidden-file"
|
|
18
|
+
import { isComponentExported } from "@/lib/utils/isComponentExported"
|
|
18
19
|
|
|
19
20
|
export interface ICreateFileProps {
|
|
20
21
|
newFileName: string
|
|
@@ -47,27 +48,37 @@ export interface IRenameFileResult {
|
|
|
47
48
|
export function useFileManagement({
|
|
48
49
|
templateCode,
|
|
49
50
|
currentPackage,
|
|
50
|
-
fileChoosen,
|
|
51
51
|
openNewPackageSaveDialog,
|
|
52
52
|
updateLastUpdated,
|
|
53
|
+
urlParams,
|
|
53
54
|
}: {
|
|
54
55
|
templateCode?: string
|
|
55
56
|
currentPackage?: Package
|
|
56
|
-
fileChoosen: string | null
|
|
57
57
|
openNewPackageSaveDialog: () => void
|
|
58
|
+
urlParams: Record<string, string>
|
|
58
59
|
updateLastUpdated: () => void
|
|
59
60
|
}) {
|
|
61
|
+
const fileChosen = urlParams.file_path ?? null
|
|
60
62
|
const [localFiles, setLocalFiles] = useState<PackageFile[]>([])
|
|
61
63
|
const [initialFiles, setInitialFiles] = useState<PackageFile[]>([])
|
|
62
64
|
const [currentFile, setCurrentFile] = useState<string | null>(null)
|
|
63
65
|
const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
|
|
64
|
-
const loggedInUser = useGlobalStore((s) => s.session)
|
|
65
66
|
const { toast } = useToast()
|
|
66
67
|
const debounceTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
67
68
|
const {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
priorityFile,
|
|
70
|
+
allFiles: packageFilesWithContent,
|
|
71
|
+
isPriorityLoading,
|
|
72
|
+
areAllFilesLoading: isLoadingPackageFilesWithContent,
|
|
73
|
+
totalFilesCount,
|
|
74
|
+
loadedFilesCount,
|
|
75
|
+
isPriorityFileFetched,
|
|
76
|
+
} = useOptimizedPackageFilesLoader(
|
|
77
|
+
currentPackage,
|
|
78
|
+
fileChosen,
|
|
79
|
+
urlParams.package_id,
|
|
80
|
+
)
|
|
81
|
+
|
|
71
82
|
const { data: packageFilesMeta, isLoading: isLoadingPackageFiles } =
|
|
72
83
|
usePackageFiles(currentPackage?.latest_package_release_id)
|
|
73
84
|
const initialCodeContent = useMemo(() => {
|
|
@@ -127,7 +138,7 @@ export function useFileManagement({
|
|
|
127
138
|
})
|
|
128
139
|
|
|
129
140
|
useEffect(() => {
|
|
130
|
-
if (!currentPackage ||
|
|
141
|
+
if (!currentPackage || isPriorityLoading) {
|
|
131
142
|
const decodedFsMap = decodeUrlHashToFsMap(window.location.toString())
|
|
132
143
|
|
|
133
144
|
if (decodedFsMap && Object.keys(decodedFsMap).length > 0) {
|
|
@@ -137,38 +148,114 @@ export function useFileManagement({
|
|
|
137
148
|
content: String(content),
|
|
138
149
|
}),
|
|
139
150
|
)
|
|
140
|
-
const targetFile = findTargetFile(filesFromUrl,
|
|
151
|
+
const targetFile = findTargetFile(filesFromUrl, fileChosen)
|
|
141
152
|
setLocalFiles(filesFromUrl)
|
|
142
153
|
setInitialFiles([])
|
|
143
154
|
setCurrentFile(targetFile?.path || filesFromUrl[0]?.path || null)
|
|
144
155
|
return
|
|
145
156
|
}
|
|
146
157
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
158
|
+
if (!urlParams.package_id) {
|
|
159
|
+
setLocalFiles([
|
|
160
|
+
{
|
|
161
|
+
path: "index.tsx",
|
|
162
|
+
content: initialCodeContent || "",
|
|
163
|
+
},
|
|
164
|
+
])
|
|
165
|
+
setInitialFiles([])
|
|
166
|
+
setCurrentFile("index.tsx")
|
|
167
|
+
}
|
|
155
168
|
return
|
|
156
|
-
} else {
|
|
157
|
-
const targetFile = findTargetFile(
|
|
158
|
-
packageFilesWithContent || [],
|
|
159
|
-
fileChoosen,
|
|
160
|
-
)
|
|
161
|
-
setLocalFiles(packageFilesWithContent || [])
|
|
162
|
-
setInitialFiles(packageFilesWithContent || [])
|
|
163
|
-
setCurrentFile(targetFile?.path || null)
|
|
164
169
|
}
|
|
165
|
-
}, [currentPackage,
|
|
170
|
+
}, [currentPackage, isPriorityLoading])
|
|
171
|
+
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (priorityFile && !isPriorityLoading && currentPackage) {
|
|
174
|
+
setLocalFiles((prev) => {
|
|
175
|
+
const existingIndex = prev.findIndex(
|
|
176
|
+
(f) => f.path === priorityFile.path,
|
|
177
|
+
)
|
|
178
|
+
if (existingIndex >= 0) {
|
|
179
|
+
const updated = [...prev]
|
|
180
|
+
updated[existingIndex] = priorityFile
|
|
181
|
+
return updated
|
|
182
|
+
}
|
|
183
|
+
return [...prev, priorityFile]
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
setCurrentFile((prevCurrentFile) => {
|
|
187
|
+
if (fileChosen && priorityFile.path === fileChosen) {
|
|
188
|
+
return priorityFile.path
|
|
189
|
+
} else if (!prevCurrentFile) {
|
|
190
|
+
return priorityFile.path
|
|
191
|
+
} else {
|
|
192
|
+
// If priority file is index.tsx, always update to it
|
|
193
|
+
const isPriorityFileBetter = priorityFile.path === "index.tsx"
|
|
194
|
+
if (isPriorityFileBetter) {
|
|
195
|
+
return priorityFile.path
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return prevCurrentFile
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
}, [priorityFile, isPriorityLoading, currentPackage, fileChosen])
|
|
202
|
+
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (packageFilesWithContent.length > 0 && currentPackage) {
|
|
205
|
+
setLocalFiles(packageFilesWithContent)
|
|
206
|
+
setInitialFiles(packageFilesWithContent)
|
|
207
|
+
|
|
208
|
+
if (fileChosen) {
|
|
209
|
+
const targetFile =
|
|
210
|
+
packageFilesWithContent.find((f) => f.path === fileChosen) ||
|
|
211
|
+
findTargetFile(packageFilesWithContent, fileChosen)
|
|
212
|
+
if (targetFile) {
|
|
213
|
+
setCurrentFile((prevCurrentFile) => {
|
|
214
|
+
return targetFile.path !== prevCurrentFile
|
|
215
|
+
? targetFile.path
|
|
216
|
+
: prevCurrentFile
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
setCurrentFile((prevCurrentFile) => {
|
|
221
|
+
if (!prevCurrentFile) {
|
|
222
|
+
// Wait for priority file to load before making selection to avoid flicker
|
|
223
|
+
// Only select if we have a good candidate (tsx/ts file) or priority file isn't loading
|
|
224
|
+
const targetFile = findTargetFile(
|
|
225
|
+
packageFilesWithContent,
|
|
226
|
+
fileChosen,
|
|
227
|
+
)
|
|
228
|
+
// Only consider it a "good enough" candidate if it's index.tsx
|
|
229
|
+
// Otherwise, wait for the actual priority file (index.tsx) to load
|
|
230
|
+
const isTopPriorityFile =
|
|
231
|
+
targetFile && targetFile.path === "index.tsx"
|
|
232
|
+
const shouldWaitForPriority =
|
|
233
|
+
isPriorityLoading && !isTopPriorityFile
|
|
234
|
+
|
|
235
|
+
if (shouldWaitForPriority) {
|
|
236
|
+
return prevCurrentFile // Keep null to avoid flicker
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return targetFile ? targetFile.path : prevCurrentFile
|
|
240
|
+
}
|
|
241
|
+
return prevCurrentFile
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}, [
|
|
246
|
+
packageFilesWithContent.length,
|
|
247
|
+
currentPackage,
|
|
248
|
+
fileChosen,
|
|
249
|
+
isPriorityLoading,
|
|
250
|
+
])
|
|
166
251
|
|
|
167
252
|
const isLoading = useMemo(() => {
|
|
168
|
-
return
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
253
|
+
return isPriorityLoading || isLoadingPackageFiles
|
|
254
|
+
}, [isPriorityLoading, isLoadingPackageFiles])
|
|
255
|
+
|
|
256
|
+
const isFullyLoaded = useMemo(() => {
|
|
257
|
+
return !isLoadingPackageFilesWithContent && !isLoadingPackageFiles
|
|
258
|
+
}, [isLoadingPackageFilesWithContent, isLoadingPackageFiles])
|
|
172
259
|
|
|
173
260
|
const fsMap = useMemo(() => {
|
|
174
261
|
const map = localFiles.reduce(
|
|
@@ -178,6 +265,7 @@ export function useFileManagement({
|
|
|
178
265
|
},
|
|
179
266
|
{} as Record<string, string>,
|
|
180
267
|
)
|
|
268
|
+
|
|
181
269
|
return map
|
|
182
270
|
}, [localFiles, manualEditsFileContent])
|
|
183
271
|
|
|
@@ -215,7 +303,6 @@ export function useFileManagement({
|
|
|
215
303
|
}
|
|
216
304
|
}
|
|
217
305
|
|
|
218
|
-
// Check if file already exists
|
|
219
306
|
const fileExists = localFiles?.some((file) => file.path === newFileName)
|
|
220
307
|
if (fileExists) {
|
|
221
308
|
onError(new Error(`File '${newFileName}' already exists`))
|
|
@@ -224,7 +311,6 @@ export function useFileManagement({
|
|
|
224
311
|
}
|
|
225
312
|
}
|
|
226
313
|
|
|
227
|
-
// Ensure file name is not empty after path construction
|
|
228
314
|
const fileName = newFileName.split("/").pop() || ""
|
|
229
315
|
if (!fileName.trim()) {
|
|
230
316
|
onError(new Error("File name cannot be empty"))
|
|
@@ -282,7 +368,6 @@ export function useFileManagement({
|
|
|
282
368
|
}
|
|
283
369
|
}
|
|
284
370
|
|
|
285
|
-
// Extract just the filename from the path for validation
|
|
286
371
|
const fileNameOnly = newFilename.split("/").pop() || ""
|
|
287
372
|
if (!isValidFileName(fileNameOnly)) {
|
|
288
373
|
onError(new Error("Invalid file name"))
|
|
@@ -395,7 +480,6 @@ export function useFileManagement({
|
|
|
395
480
|
|
|
396
481
|
const saveFiles = () => {
|
|
397
482
|
if (!isLoggedIn) {
|
|
398
|
-
// For non-logged-in users, trigger immediate URL save
|
|
399
483
|
if (debounceTimeoutRef.current) {
|
|
400
484
|
clearTimeout(debounceTimeoutRef.current)
|
|
401
485
|
}
|
|
@@ -433,20 +517,55 @@ export function useFileManagement({
|
|
|
433
517
|
isCreatingRelease,
|
|
434
518
|
])
|
|
435
519
|
|
|
520
|
+
const currentFileCode = useMemo(
|
|
521
|
+
() =>
|
|
522
|
+
localFiles.find((x) => x.path === currentFile)?.content ?? DEFAULT_CODE,
|
|
523
|
+
[localFiles, currentFile],
|
|
524
|
+
)
|
|
525
|
+
const mainComponentPath = useMemo(() => {
|
|
526
|
+
const isComponentExportedInCurrentFile =
|
|
527
|
+
isComponentExported(currentFileCode)
|
|
528
|
+
|
|
529
|
+
const selectedComponent =
|
|
530
|
+
(currentFile?.endsWith(".tsx") || currentFile?.endsWith(".ts")) &&
|
|
531
|
+
!!localFiles.some((x) => x.path === currentFile) &&
|
|
532
|
+
isComponentExportedInCurrentFile
|
|
533
|
+
? currentFile
|
|
534
|
+
: localFiles.find((x) => {
|
|
535
|
+
const isComponentExportedInFile = isComponentExported(x.content)
|
|
536
|
+
return (
|
|
537
|
+
x.path.endsWith(".tsx") ||
|
|
538
|
+
(x.path.endsWith(".ts") && isComponentExportedInFile)
|
|
539
|
+
)
|
|
540
|
+
})?.path || localFiles[0]?.path
|
|
541
|
+
|
|
542
|
+
return selectedComponent
|
|
543
|
+
}, [currentFile, localFiles, currentFileCode, packageFilesWithContent])
|
|
544
|
+
|
|
545
|
+
const priorityFileFetched = useMemo(() => {
|
|
546
|
+
return urlParams.package_id && isPriorityFileFetched
|
|
547
|
+
}, [urlParams.package_id, isPriorityFileFetched])
|
|
548
|
+
|
|
436
549
|
return {
|
|
437
550
|
fsMap,
|
|
438
551
|
createFile,
|
|
552
|
+
priorityFileFetched,
|
|
439
553
|
deleteFile,
|
|
440
554
|
renameFile,
|
|
441
555
|
saveFiles,
|
|
442
556
|
localFiles,
|
|
557
|
+
mainComponentPath,
|
|
558
|
+
currentFileCode,
|
|
443
559
|
initialFiles,
|
|
444
560
|
currentFile,
|
|
445
561
|
setLocalFiles,
|
|
446
562
|
onFileSelect,
|
|
447
563
|
isLoading,
|
|
564
|
+
isFullyLoaded,
|
|
448
565
|
isSaving,
|
|
449
566
|
savePackage,
|
|
450
567
|
packageFilesMeta,
|
|
568
|
+
totalFilesCount,
|
|
569
|
+
loadedFilesCount,
|
|
451
570
|
}
|
|
452
571
|
}
|