@tscircuit/fake-snippets 0.0.107 → 0.0.108

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 (35) hide show
  1. package/api/generated-index.js +82 -22
  2. package/biome.json +7 -1
  3. package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +0 -15
  4. package/bun-tests/fake-snippets-api/routes/package_builds/list.test.ts +0 -12
  5. package/dist/bundle.js +23 -23
  6. package/dist/index.d.ts +21 -15
  7. package/dist/index.js +17 -17
  8. package/dist/schema.d.ts +24 -24
  9. package/dist/schema.js +5 -5
  10. package/fake-snippets-api/lib/db/db-client.ts +10 -1
  11. package/fake-snippets-api/lib/db/schema.ts +3 -3
  12. package/fake-snippets-api/lib/db/seed.ts +6 -9
  13. package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +0 -3
  14. package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +3 -0
  15. package/package.json +1 -1
  16. package/src/App.tsx +12 -9
  17. package/src/components/FileSidebar.tsx +14 -159
  18. package/src/components/PackageBreadcrumb.tsx +1 -1
  19. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -1
  20. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +18 -2
  21. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +1 -1
  22. package/src/components/preview/BuildsList.tsx +20 -9
  23. package/src/components/preview/ConnectedPackagesList.tsx +73 -60
  24. package/src/components/preview/ConnectedRepoOverview.tsx +160 -154
  25. package/src/components/preview/PackageReleasesDashboard.tsx +11 -5
  26. package/src/components/preview/index.tsx +16 -153
  27. package/src/index.css +24 -0
  28. package/src/lib/utils/transformFilesToTreeData.tsx +195 -0
  29. package/src/pages/404.tsx +3 -5
  30. package/src/pages/preview-release.tsx +269 -0
  31. package/src/pages/release-builds.tsx +0 -8
  32. package/src/pages/release-detail.tsx +17 -15
  33. package/src/pages/releases.tsx +5 -1
  34. package/src/hooks/use-snippets-base-api-url.ts +0 -3
  35. package/src/pages/preview-build.tsx +0 -380
@@ -0,0 +1,269 @@
1
+ import { useState } from "react"
2
+ import { useParams } from "wouter"
3
+ import { Loader2, ChevronLeft, ChevronRight } from "lucide-react"
4
+ import Header from "@/components/Header"
5
+ import { SuspenseRunFrame } from "@/components/SuspenseRunFrame"
6
+ import { TreeView } from "@/components/ui/tree-view"
7
+ import { transformFilesToTreeData } from "@/lib/utils/transformFilesToTreeData"
8
+ import { cn } from "@/lib/utils"
9
+ import { PrefetchPageLink } from "@/components/PrefetchPageLink"
10
+ import NotFoundPage from "./404"
11
+ import { getBuildStatus } from "@/components/preview"
12
+ import { usePackageReleaseById } from "@/hooks/use-package-release"
13
+ import { usePackageFilesLoader } from "@/hooks/usePackageFilesLoader"
14
+ import { usePackageBuild } from "@/hooks/use-package-builds"
15
+ import { PackageBuild } from "fake-snippets-api/lib/db/schema"
16
+ import { usePackageByName } from "@/hooks/use-package-by-package-name"
17
+
18
+ const StatusPill = ({ status }: { status: string }) => {
19
+ const color =
20
+ status === "success"
21
+ ? "bg-emerald-600"
22
+ : status === "failed"
23
+ ? "bg-red-600"
24
+ : status === "building"
25
+ ? "bg-blue-600 animate-pulse"
26
+ : "bg-gray-500"
27
+ return <span className={cn("inline-block w-2 h-2 rounded-full", color)} />
28
+ }
29
+
30
+ export default function PreviewBuildPage() {
31
+ const params = useParams<{
32
+ packageReleaseId: string
33
+ author: string
34
+ packageName: string
35
+ }>()
36
+ const packageReleaseId = params?.packageReleaseId || null
37
+ const author = params?.author || null
38
+ const packageName = params?.packageName || null
39
+
40
+ const [sidebarCollapsed, setSidebarCollapsed] = useState(true)
41
+ const [selectedFile, setSelectedFile] = useState<string | null>("index.tsx")
42
+ const [selectedItemId, setSelectedItemId] = useState<string>("")
43
+
44
+ const { data: packageRelease, isLoading: isLoadingRelease } =
45
+ usePackageReleaseById(packageReleaseId)
46
+ const { data: pkg, isLoading: isLoadingPackage } = usePackageByName(
47
+ author && packageName ? `${author}/${packageName}` : null,
48
+ )
49
+ const { data: build, isLoading: isLoadingBuild } = usePackageBuild(
50
+ packageRelease?.latest_package_build_id || null,
51
+ )
52
+ const { data: buildFiles = [], isLoading: isLoadingFiles } =
53
+ usePackageFilesLoader(pkg)
54
+
55
+ const buildFsMap = Object.fromEntries(
56
+ buildFiles.map((f) => [f.path, f.content]),
57
+ )
58
+
59
+ if (!packageReleaseId) {
60
+ return <NotFoundPage heading="Package Release Not Found" />
61
+ }
62
+
63
+ if (!packageRelease && !isLoadingRelease) {
64
+ return <NotFoundPage heading="Package Release Not Found" />
65
+ }
66
+ const isLoading =
67
+ isLoadingRelease || isLoadingPackage || isLoadingFiles || isLoadingBuild
68
+
69
+ if (!build && !isLoading) {
70
+ return <NotFoundPage heading="Package Build Not Found" />
71
+ }
72
+
73
+ const { status } = getBuildStatus(build as PackageBuild)
74
+
75
+ const treeData = transformFilesToTreeData({
76
+ files: buildFsMap,
77
+ currentFile: selectedFile,
78
+ renamingFile: null,
79
+ handleRenameFile: () => ({ fileRenamed: false }),
80
+ handleDeleteFile: () => ({ fileDeleted: false }),
81
+ setRenamingFile: () => {},
82
+ onFileSelect: setSelectedFile,
83
+ onFolderSelect: () => {},
84
+ canModifyFiles: false,
85
+ setErrorMessage: () => {},
86
+ setSelectedFolderForCreation: () => {},
87
+ })
88
+
89
+ return (
90
+ <>
91
+ <Header />
92
+ <div className="flex flex-col h-screen overflow-hidden !-mt-1">
93
+ <div className="flex flex-1 overflow-hidden">
94
+ <aside
95
+ className={cn(
96
+ "relative border-r border-gray-200 rounded-r-lg z-[5] h-full transition-all duration-300 ease-in-out bg-white",
97
+ sidebarCollapsed ? "w-2 md:w-3" : "w-80",
98
+ )}
99
+ >
100
+ <button
101
+ onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
102
+ className="absolute top-4 -right-3 z-10 bg-white border border-gray-200 rounded-full p-1 hover:bg-gray-50"
103
+ >
104
+ {sidebarCollapsed ? (
105
+ <ChevronRight size={20} />
106
+ ) : (
107
+ <ChevronLeft size={20} />
108
+ )}
109
+ </button>
110
+
111
+ {!sidebarCollapsed && (
112
+ <>
113
+ <div className="p-4 border-b border-gray-200">
114
+ <div className="space-y-3">
115
+ <div className="flex items-center justify-between">
116
+ <h2 className="text-lg font-semibold text-gray-900">
117
+ Deployment
118
+ </h2>
119
+ <StatusPill status={status} />
120
+ </div>
121
+
122
+ <div className="space-y-2">
123
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
124
+ <span className="text-xs text-gray-500 uppercase tracking-wide">
125
+ ID
126
+ </span>
127
+ <PrefetchPageLink
128
+ href={`/${pkg?.name}/releases/${build?.package_release_id}`}
129
+ title={build?.package_build_id}
130
+ className="font-mono text-sm truncate text-gray-900 bg-gray-100 w-full px-2 py-1 rounded"
131
+ >
132
+ {build?.package_build_id}
133
+ </PrefetchPageLink>
134
+ </div>
135
+ {packageRelease?.commit_message && (
136
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
137
+ <span className="text-xs text-gray-500 uppercase tracking-wide">
138
+ Commit
139
+ </span>
140
+ <a
141
+ title={packageRelease?.commit_message}
142
+ target="_blank"
143
+ rel="noopener noreferrer"
144
+ href={`https://github.com/${pkg?.github_repo_full_name}/commit/${packageRelease?.commit_message}`}
145
+ className="font-mono text-xs text-gray-600 bg-gray-50 px-2 text-right py-1 rounded truncate"
146
+ >
147
+ {packageRelease?.commit_message}
148
+ </a>
149
+ </div>
150
+ )}
151
+
152
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
153
+ <span className="text-xs text-gray-500 uppercase tracking-wide">
154
+ Status
155
+ </span>
156
+ <span
157
+ className={`text-xs font-medium px-2 py-1 w-fit rounded-full capitalize ${
158
+ status === "success"
159
+ ? "bg-emerald-100 text-emerald-800"
160
+ : status === "error"
161
+ ? "bg-red-100 text-red-800"
162
+ : status === "building"
163
+ ? "bg-blue-100 text-blue-800"
164
+ : "bg-gray-100 text-gray-800"
165
+ }`}
166
+ >
167
+ {status}
168
+ </span>
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+
174
+ <div className="flex-1 overflow-hidden">
175
+ <div className="px-4 py-3 border-b border-gray-200">
176
+ <h3 className="text-sm font-semibold text-gray-900">
177
+ Files
178
+ </h3>
179
+ <p className="text-xs text-gray-500 mt-1">
180
+ {isLoadingFiles
181
+ ? "Loading files..."
182
+ : `${treeData.length} file${treeData.length !== 1 ? "s" : ""}`}
183
+ </p>
184
+ </div>
185
+ <div className="px-2 py-2 overflow-y-auto select-none">
186
+ {isLoadingFiles ? (
187
+ <div className="flex items-center justify-center py-8">
188
+ <Loader2 className="w-4 h-4 animate-spin" />
189
+ <span className="ml-2 text-sm text-gray-500">
190
+ Loading files...
191
+ </span>
192
+ </div>
193
+ ) : (
194
+ <TreeView
195
+ selectedItemId={selectedItemId || ""}
196
+ setSelectedItemId={(v) => setSelectedItemId(v || "")}
197
+ data={treeData}
198
+ className="w-full"
199
+ onSelectChange={(item) => {
200
+ if (item && !item.children) {
201
+ setSelectedFile(item.id)
202
+ }
203
+ }}
204
+ />
205
+ )}
206
+ </div>
207
+ </div>
208
+ </>
209
+ )}
210
+ </aside>
211
+
212
+ <main className="flex-1 overflow-y-auto">
213
+ <div className="flex flex-col h-full overflow-h-hidden">
214
+ {isLoading ? (
215
+ <div className="flex-1 flex items-center justify-center">
216
+ <div className="flex flex-col items-center gap-3 text-gray-500">
217
+ <Loader2 className="w-6 h-6 animate-spin" />
218
+ <p>Loading package contents...</p>
219
+ </div>
220
+ </div>
221
+ ) : status === "success" && buildFiles.length > 0 ? (
222
+ <SuspenseRunFrame
223
+ fsMap={buildFsMap}
224
+ mainComponentPath={selectedFile ?? "index.tsx"}
225
+ showRunButton={false}
226
+ className="[&>div]:overflow-y-hidden"
227
+ />
228
+ ) : (
229
+ <div className="flex-1 flex items-center justify-center">
230
+ {status === "building" ? (
231
+ <div className="flex flex-col items-center gap-3 text-gray-500">
232
+ <Loader2 className="w-6 h-6 animate-spin" />
233
+ <p>Building…</p>
234
+ </div>
235
+ ) : status === "error" ? (
236
+ <div className="text-center">
237
+ <p className="text-red-600 font-medium mb-2">
238
+ Build Failed
239
+ </p>
240
+ </div>
241
+ ) : buildFiles.length === 0 ? (
242
+ <div className="text-center">
243
+ <p className="text-gray-600 font-medium mb-2">
244
+ No files found
245
+ </p>
246
+ <p className="text-sm text-gray-500">
247
+ This package release doesn't have any files to preview.
248
+ </p>
249
+ </div>
250
+ ) : (
251
+ <div className="text-center p-4">
252
+ <p className="text-gray-600 font-medium mb-2">
253
+ Build Status: {status}
254
+ </p>
255
+ <p className="text-sm text-gray-500 max-w-lg">
256
+ Please wait while we process this build status. Try
257
+ refreshing the page in a few moments.
258
+ </p>
259
+ </div>
260
+ )}
261
+ </div>
262
+ )}
263
+ </div>
264
+ </main>
265
+ </div>
266
+ </div>
267
+ </>
268
+ )
269
+ }
@@ -41,10 +41,6 @@ export default function ReleaseBuildsPage() {
41
41
  error: buildsError,
42
42
  } = usePackageBuildsByReleaseId(params?.releaseId ?? null)
43
43
 
44
- const handleSelectBuild = (build: PackageBuild) => {
45
- setLocation(`/build/${build.package_build_id}`)
46
- }
47
-
48
44
  if (isLoadingPackage || isLoadingRelease || isLoadingBuilds) {
49
45
  return null
50
46
  }
@@ -57,10 +53,6 @@ export default function ReleaseBuildsPage() {
57
53
  return <NotFoundPage heading="Release Not Found" />
58
54
  }
59
55
 
60
- if (buildsError?.status === 404 || !builds?.length) {
61
- return <NotFoundPage heading="No Builds Found for Release" />
62
- }
63
-
64
56
  return (
65
57
  <>
66
58
  <Header />
@@ -6,8 +6,7 @@ import { usePackageBuild } from "@/hooks/use-package-builds"
6
6
  import { ConnectedRepoOverview } from "@/components/preview/ConnectedRepoOverview"
7
7
  import Header from "@/components/Header"
8
8
  import { Badge } from "@/components/ui/badge"
9
- import { Button } from "@/components/ui/button"
10
- import { Calendar, GitBranch, Hash, Copy, Check } from "lucide-react"
9
+ import { Calendar, GitBranch } from "lucide-react"
11
10
  import { useState } from "react"
12
11
  import { formatTimeAgo } from "@/lib/utils/formatTimeAgo"
13
12
  import { PackageBreadcrumb } from "@/components/PackageBreadcrumb"
@@ -48,7 +47,7 @@ export default function ReleaseDetailPage() {
48
47
  error: buildError,
49
48
  } = usePackageBuild(packageRelease?.latest_package_build_id ?? null)
50
49
 
51
- if (isLoadingPackage || isLoadingRelease || isLoadingBuild) {
50
+ if (isLoadingPackage || isLoadingRelease) {
52
51
  return null
53
52
  }
54
53
 
@@ -60,17 +59,13 @@ export default function ReleaseDetailPage() {
60
59
  return <NotFoundPage heading="Release Not Found" />
61
60
  }
62
61
 
63
- if (buildError?.status === 404 || !latestBuild) {
64
- return <NotFoundPage heading="No Builds Found for Release" />
65
- }
66
-
67
62
  return (
68
63
  <>
69
64
  <Header />
70
65
  <div className="min-h-screen bg-white">
71
66
  {/* Page Header */}
72
67
  <div className="bg-gray-50 border-b py-6">
73
- <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
68
+ <div className="max-w-7xl lg:flex lg:justify-between mx-auto px-4 sm:px-6 lg:px-8">
74
69
  {/* Breadcrumb */}
75
70
  <PackageBreadcrumb
76
71
  author={pkg.owner_github_username || ""}
@@ -87,12 +82,18 @@ export default function ReleaseDetailPage() {
87
82
  <div className="flex-1">
88
83
  <div className="flex items-center gap-4 text-sm text-gray-600">
89
84
  {packageRelease.is_pr_preview && (
90
- <div className="flex items-center gap-1">
91
- <GitBranch className="w-4 h-4" />
92
- <Badge variant="outline" className="text-xs">
93
- PR #{packageRelease.github_pr_number}
94
- </Badge>
95
- </div>
85
+ <a
86
+ href={`https://github.com/${pkg.github_repo_full_name}/pull/${packageRelease.github_pr_number}`}
87
+ target="_blank"
88
+ rel="noopener noreferrer"
89
+ >
90
+ <div className="flex items-center gap-1">
91
+ <GitBranch className="w-4 h-4" />
92
+ <Badge variant="outline" className="text-xs">
93
+ PR #{packageRelease.github_pr_number}
94
+ </Badge>
95
+ </div>
96
+ </a>
96
97
  )}
97
98
  <div className="flex items-center gap-1">
98
99
  <Calendar className="w-4 h-4" />
@@ -108,7 +109,8 @@ export default function ReleaseDetailPage() {
108
109
 
109
110
  {/* Main Content */}
110
111
  <ConnectedRepoOverview
111
- build={latestBuild}
112
+ packageBuild={latestBuild ?? null}
113
+ isLoadingBuild={isLoadingBuild}
112
114
  pkg={pkg}
113
115
  packageRelease={packageRelease}
114
116
  />
@@ -46,6 +46,10 @@ export default function ReleasesPage() {
46
46
  // If there's no build, we still want to show the releases page
47
47
  // The PackageReleasesDashboard will handle the case where latestBuild is null
48
48
  return (
49
- <PackageReleasesDashboard latestBuild={latestBuild ?? null} pkg={pkg} />
49
+ <PackageReleasesDashboard
50
+ latestRelease={latestRelease}
51
+ latestBuild={latestBuild ?? null}
52
+ pkg={pkg}
53
+ />
50
54
  )
51
55
  }
@@ -1,3 +0,0 @@
1
- export const useSnippetsBaseApiUrl = () => {
2
- return import.meta.env.VITE_SNIPPETS_API_URL ?? "/api"
3
- }