@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.
Files changed (80) 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/bun.lock +62 -19
  6. package/dist/bundle.js +25 -24
  7. package/dist/index.d.ts +26 -15
  8. package/dist/index.js +19 -18
  9. package/dist/schema.d.ts +32 -24
  10. package/dist/schema.js +7 -6
  11. package/fake-snippets-api/lib/db/db-client.ts +10 -1
  12. package/fake-snippets-api/lib/db/schema.ts +4 -3
  13. package/fake-snippets-api/lib/db/seed.ts +6 -9
  14. package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +0 -3
  15. package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +3 -0
  16. package/package.json +7 -8
  17. package/src/App.tsx +12 -11
  18. package/src/components/DownloadButtonAndMenu.tsx +133 -35
  19. package/src/components/FileSidebar.tsx +45 -193
  20. package/src/components/Footer.tsx +0 -1
  21. package/src/components/HeaderLogin.tsx +1 -1
  22. package/src/components/HiddenFilesDropdown.tsx +0 -2
  23. package/src/components/PackageBreadcrumb.tsx +1 -1
  24. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -2
  25. package/src/components/PackageBuildsPage/build-preview-content.tsx +34 -5
  26. package/src/components/PackageCard.tsx +0 -1
  27. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +20 -11
  28. package/src/components/ViewPackagePage/components/important-files-view.tsx +75 -59
  29. package/src/components/ViewPackagePage/components/main-content-header.tsx +4 -4
  30. package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +0 -1
  31. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -2
  32. package/src/components/ViewPackagePage/components/package-header.tsx +14 -17
  33. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +0 -1
  34. package/src/components/ViewPackagePage/components/repo-page-content.tsx +21 -20
  35. package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +18 -2
  36. package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +1 -1
  37. package/src/components/ViewPackagePage/components/sidebar.tsx +0 -2
  38. package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +18 -17
  39. package/src/components/ViewPackagePage/components/theme-toggle.tsx +0 -2
  40. package/src/components/ViewPackagePage/hooks/use-toast.tsx +0 -1
  41. package/src/components/package-port/CodeAndPreview.tsx +23 -40
  42. package/src/components/package-port/CodeEditor.tsx +24 -1
  43. package/src/components/package-port/CodeEditorHeader.tsx +5 -2
  44. package/src/components/preview/BuildsList.tsx +20 -9
  45. package/src/components/preview/ConnectedPackagesList.tsx +73 -60
  46. package/src/components/preview/ConnectedRepoOverview.tsx +160 -154
  47. package/src/components/preview/PackageReleasesDashboard.tsx +41 -30
  48. package/src/components/preview/index.tsx +16 -153
  49. package/src/hooks/use-current-package-id.ts +5 -30
  50. package/src/hooks/use-current-package-info.ts +29 -5
  51. package/src/hooks/use-global-store.ts +1 -1
  52. package/src/hooks/useFileManagement.ts +153 -34
  53. package/src/hooks/useOptimizedPackageFilesLoader.ts +149 -0
  54. package/src/hooks/useUpdatePackageFilesMutation.ts +2 -0
  55. package/src/index.css +24 -0
  56. package/src/lib/download-fns/download-circuit-png.ts +11 -3
  57. package/src/lib/download-fns/download-gltf-from-circuit-json.ts +44 -0
  58. package/src/lib/utils/isComponentExported.ts +9 -0
  59. package/src/lib/utils/transformFilesToTreeData.tsx +195 -0
  60. package/src/pages/404.tsx +3 -5
  61. package/src/pages/authorize.tsx +0 -2
  62. package/src/pages/landing.tsx +0 -1
  63. package/src/pages/preview-release.tsx +279 -0
  64. package/src/pages/release-builds.tsx +0 -8
  65. package/src/pages/release-detail.tsx +17 -15
  66. package/src/pages/releases.tsx +5 -1
  67. package/src/pages/view-package.tsx +14 -13
  68. package/src/components/Footer2.tsx +0 -100
  69. package/src/components/ShippingInformationForm.tsx +0 -423
  70. package/src/components/StaticViewSnippetHeader.tsx +0 -70
  71. package/src/components/ViewPackagePage/components/file-explorer.tsx +0 -67
  72. package/src/components/ViewPackagePage/components/readme-view.tsx +0 -58
  73. package/src/components/ViewPackagePage/components/repo-header-button.tsx +0 -36
  74. package/src/components/ViewPackagePage/components/repo-header.tsx +0 -4
  75. package/src/components/ViewPackagePage/components/sidebar-contributors-section.tsx +0 -31
  76. package/src/components/ViewSnippetHeader.tsx +0 -181
  77. package/src/components/ui/input-otp.tsx +0 -69
  78. package/src/hooks/use-snippets-base-api-url.ts +0 -3
  79. package/src/pages/preview-build.tsx +0 -380
  80. package/src/pages/settings.tsx +0 -25
@@ -0,0 +1,279 @@
1
+ import { useEffect, 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
+ import { findTargetFile } from "@/lib/utils/findTargetFile"
18
+
19
+ const StatusPill = ({ status }: { status: string }) => {
20
+ const color =
21
+ status === "success"
22
+ ? "bg-emerald-600"
23
+ : status === "failed"
24
+ ? "bg-red-600"
25
+ : status === "building"
26
+ ? "bg-blue-600 animate-pulse"
27
+ : "bg-gray-500"
28
+ return <span className={cn("inline-block w-2 h-2 rounded-full", color)} />
29
+ }
30
+
31
+ export default function PreviewBuildPage() {
32
+ const params = useParams<{
33
+ packageReleaseId: string
34
+ author: string
35
+ packageName: string
36
+ }>()
37
+ const packageReleaseId = params?.packageReleaseId || null
38
+ const author = params?.author || null
39
+ const packageName = params?.packageName || null
40
+
41
+ const [sidebarCollapsed, setSidebarCollapsed] = useState(true)
42
+ const [selectedFile, setSelectedFile] = useState<string | null>(null)
43
+ const [selectedItemId, setSelectedItemId] = useState<string>("")
44
+
45
+ const { data: packageRelease, isLoading: isLoadingRelease } =
46
+ usePackageReleaseById(packageReleaseId)
47
+ const { data: pkg, isLoading: isLoadingPackage } = usePackageByName(
48
+ author && packageName ? `${author}/${packageName}` : null,
49
+ )
50
+ const { data: build, isLoading: isLoadingBuild } = usePackageBuild(
51
+ packageRelease?.latest_package_build_id || null,
52
+ )
53
+ const { data: buildFiles = [], isLoading: isLoadingFiles } =
54
+ usePackageFilesLoader(pkg)
55
+
56
+ const buildFsMap = Object.fromEntries(
57
+ buildFiles.map((f) => [f.path, f.content]),
58
+ )
59
+
60
+ const targetFile = findTargetFile(buildFiles, selectedFile)
61
+ const mainComponentPath = targetFile?.path ?? null
62
+
63
+ useEffect(() => {
64
+ if (!selectedFile && mainComponentPath) {
65
+ setSelectedFile(mainComponentPath)
66
+ }
67
+ }, [mainComponentPath, selectedFile])
68
+
69
+ if (!packageReleaseId) {
70
+ return <NotFoundPage heading="Package Release Not Found" />
71
+ }
72
+
73
+ if (!packageRelease && !isLoadingRelease) {
74
+ return <NotFoundPage heading="Package Release Not Found" />
75
+ }
76
+ const isLoading =
77
+ isLoadingRelease || isLoadingPackage || isLoadingFiles || isLoadingBuild
78
+
79
+ if (!build && !isLoading) {
80
+ return <NotFoundPage heading="Package Build Not Found" />
81
+ }
82
+
83
+ const { status } = getBuildStatus(build as PackageBuild)
84
+
85
+ const treeData = transformFilesToTreeData({
86
+ files: buildFsMap,
87
+ currentFile: selectedFile ?? mainComponentPath,
88
+ renamingFile: null,
89
+ handleRenameFile: () => ({ fileRenamed: false }),
90
+ handleDeleteFile: () => ({ fileDeleted: false }),
91
+ setRenamingFile: () => {},
92
+ onFileSelect: setSelectedFile,
93
+ onFolderSelect: () => {},
94
+ canModifyFiles: false,
95
+ setErrorMessage: () => {},
96
+ setSelectedFolderForCreation: () => {},
97
+ })
98
+
99
+ return (
100
+ <>
101
+ <Header />
102
+ <div className="flex flex-col h-screen overflow-hidden !-mt-1">
103
+ <div className="flex flex-1 overflow-hidden">
104
+ <aside
105
+ className={cn(
106
+ "relative border-r border-gray-200 rounded-r-lg z-[5] h-full transition-all duration-300 ease-in-out bg-white",
107
+ sidebarCollapsed ? "w-2 md:w-3" : "w-80",
108
+ )}
109
+ >
110
+ <button
111
+ onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
112
+ className="absolute top-4 -right-3 z-10 bg-white border border-gray-200 rounded-full p-1 hover:bg-gray-50"
113
+ >
114
+ {sidebarCollapsed ? (
115
+ <ChevronRight size={20} />
116
+ ) : (
117
+ <ChevronLeft size={20} />
118
+ )}
119
+ </button>
120
+
121
+ {!sidebarCollapsed && (
122
+ <>
123
+ <div className="p-4 border-b border-gray-200">
124
+ <div className="space-y-3">
125
+ <div className="flex items-center justify-between">
126
+ <h2 className="text-lg font-semibold text-gray-900">
127
+ Deployment
128
+ </h2>
129
+ <StatusPill status={status} />
130
+ </div>
131
+
132
+ <div className="space-y-2">
133
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
134
+ <span className="text-xs text-gray-500 uppercase tracking-wide">
135
+ ID
136
+ </span>
137
+ <PrefetchPageLink
138
+ href={`/${pkg?.name}/releases/${build?.package_release_id}`}
139
+ title={build?.package_build_id}
140
+ className="font-mono text-sm truncate text-gray-900 bg-gray-100 w-full px-2 py-1 rounded"
141
+ >
142
+ {build?.package_build_id}
143
+ </PrefetchPageLink>
144
+ </div>
145
+ {packageRelease?.commit_message && (
146
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
147
+ <span className="text-xs text-gray-500 uppercase tracking-wide">
148
+ Commit
149
+ </span>
150
+ <a
151
+ title={packageRelease?.commit_message}
152
+ target="_blank"
153
+ rel="noopener noreferrer"
154
+ href={`https://github.com/${pkg?.github_repo_full_name}/commit/${packageRelease?.commit_message}`}
155
+ className="font-mono text-xs text-gray-600 bg-gray-50 px-2 text-right py-1 rounded truncate"
156
+ >
157
+ {packageRelease?.commit_message}
158
+ </a>
159
+ </div>
160
+ )}
161
+
162
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
163
+ <span className="text-xs text-gray-500 uppercase tracking-wide">
164
+ Status
165
+ </span>
166
+ <span
167
+ className={`text-xs font-medium px-2 py-1 w-fit rounded-full capitalize ${
168
+ status === "success"
169
+ ? "bg-emerald-100 text-emerald-800"
170
+ : status === "error"
171
+ ? "bg-red-100 text-red-800"
172
+ : status === "building"
173
+ ? "bg-blue-100 text-blue-800"
174
+ : "bg-gray-100 text-gray-800"
175
+ }`}
176
+ >
177
+ {status}
178
+ </span>
179
+ </div>
180
+ </div>
181
+ </div>
182
+ </div>
183
+
184
+ <div className="flex-1 overflow-hidden">
185
+ <div className="px-4 py-3 border-b border-gray-200">
186
+ <h3 className="text-sm font-semibold text-gray-900">
187
+ Files
188
+ </h3>
189
+ <p className="text-xs text-gray-500 mt-1">
190
+ {isLoadingFiles
191
+ ? "Loading files..."
192
+ : `${treeData.length} file${treeData.length !== 1 ? "s" : ""}`}
193
+ </p>
194
+ </div>
195
+ <div className="px-2 py-2 overflow-y-auto select-none">
196
+ {isLoadingFiles ? (
197
+ <div className="flex items-center justify-center py-8">
198
+ <Loader2 className="w-4 h-4 animate-spin" />
199
+ <span className="ml-2 text-sm text-gray-500">
200
+ Loading files...
201
+ </span>
202
+ </div>
203
+ ) : (
204
+ <TreeView
205
+ selectedItemId={selectedItemId || ""}
206
+ setSelectedItemId={(v) => setSelectedItemId(v || "")}
207
+ data={treeData}
208
+ className="w-full"
209
+ onSelectChange={(item) => {
210
+ if (item && !item.children) {
211
+ setSelectedFile(item.id)
212
+ }
213
+ }}
214
+ />
215
+ )}
216
+ </div>
217
+ </div>
218
+ </>
219
+ )}
220
+ </aside>
221
+
222
+ <main className="flex-1 overflow-y-auto">
223
+ <div className="flex flex-col h-full overflow-h-hidden">
224
+ {isLoading ? (
225
+ <div className="flex-1 flex items-center justify-center">
226
+ <div className="flex flex-col items-center gap-3 text-gray-500">
227
+ <Loader2 className="w-6 h-6 animate-spin" />
228
+ <p>Loading package contents...</p>
229
+ </div>
230
+ </div>
231
+ ) : status === "success" && buildFiles.length > 0 ? (
232
+ <SuspenseRunFrame
233
+ fsMap={buildFsMap}
234
+ mainComponentPath={mainComponentPath ?? "index.tsx"}
235
+ showRunButton={false}
236
+ className="[&>div]:overflow-y-hidden"
237
+ />
238
+ ) : (
239
+ <div className="flex-1 flex items-center justify-center">
240
+ {status === "building" ? (
241
+ <div className="flex flex-col items-center gap-3 text-gray-500">
242
+ <Loader2 className="w-6 h-6 animate-spin" />
243
+ <p>Building…</p>
244
+ </div>
245
+ ) : status === "error" ? (
246
+ <div className="text-center">
247
+ <p className="text-red-600 font-medium mb-2">
248
+ Build Failed
249
+ </p>
250
+ </div>
251
+ ) : buildFiles.length === 0 ? (
252
+ <div className="text-center">
253
+ <p className="text-gray-600 font-medium mb-2">
254
+ No files found
255
+ </p>
256
+ <p className="text-sm text-gray-500">
257
+ This package release doesn't have any files to preview.
258
+ </p>
259
+ </div>
260
+ ) : (
261
+ <div className="text-center p-4">
262
+ <p className="text-gray-600 font-medium mb-2">
263
+ Build Status: {status}
264
+ </p>
265
+ <p className="text-sm text-gray-500 max-w-lg">
266
+ Please wait while we process this build status. Try
267
+ refreshing the page in a few moments.
268
+ </p>
269
+ </div>
270
+ )}
271
+ </div>
272
+ )}
273
+ </div>
274
+ </main>
275
+ </div>
276
+ </div>
277
+ </>
278
+ )
279
+ }
@@ -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
  }
@@ -5,18 +5,19 @@ import { useLocation, useParams } from "wouter"
5
5
  import { Helmet } from "react-helmet-async"
6
6
  import { useEffect, useState } from "react"
7
7
  import NotFoundPage from "./404"
8
- import { useCurrentPackageId } from "@/hooks/use-current-package-id"
9
- import { usePackage } from "@/hooks/use-package"
8
+ import { usePackageByName } from "@/hooks/use-package-by-package-name"
10
9
 
11
10
  export const ViewPackagePage = () => {
12
- const {
13
- packageId,
14
- error: packageIdError,
15
- isLoading: isLoadingPackageId,
16
- } = useCurrentPackageId()
17
- const { data: packageInfo } = usePackage(packageId)
18
11
  const { author, packageName } = useParams()
12
+ const packageNameFull = `${author}/${packageName}`
19
13
  const [, setLocation] = useLocation()
14
+
15
+ // Get package data directly by name - this will also cache by ID
16
+ const {
17
+ data: packageInfo,
18
+ error: packageError,
19
+ isLoading: isLoadingPackage,
20
+ } = usePackageByName(packageNameFull)
20
21
  const {
21
22
  data: packageRelease,
22
23
  error: packageReleaseError,
@@ -33,11 +34,10 @@ export const ViewPackagePage = () => {
33
34
  },
34
35
  )
35
36
 
36
- const { data: packageFiles } = usePackageFiles(
37
- packageRelease?.package_release_id,
38
- )
37
+ const { data: packageFiles, isFetched: arePackageFilesFetched } =
38
+ usePackageFiles(packageRelease?.package_release_id)
39
39
 
40
- if (!isLoadingPackageId && packageIdError) {
40
+ if (!isLoadingPackage && packageError) {
41
41
  return <NotFoundPage heading="Package Not Found" />
42
42
  }
43
43
 
@@ -51,7 +51,8 @@ export const ViewPackagePage = () => {
51
51
  <title>{`${author}/${packageName} - tscircuit`}</title>
52
52
  </Helmet>
53
53
  <RepoPageContent
54
- packageFiles={packageFiles as any}
54
+ packageFiles={packageFiles ?? []}
55
+ arePackageFilesFetched={arePackageFilesFetched}
55
56
  packageInfo={packageInfo}
56
57
  packageRelease={packageRelease}
57
58
  importantFilePaths={["README.md", "LICENSE", "package.json"]}
@@ -1,100 +0,0 @@
1
- import { Button } from "@/components/ui/button"
2
- import { Card, CardContent } from "@/components/ui/card"
3
- import { Badge } from "@/components/ui/badge"
4
- import {
5
- Accordion,
6
- AccordionContent,
7
- AccordionItem,
8
- AccordionTrigger,
9
- } from "@/components/ui/accordion"
10
- import {
11
- CircuitBoard,
12
- Cpu,
13
- Layers,
14
- CloudLightningIcon as Lightning,
15
- Maximize2,
16
- Zap,
17
- } from "lucide-react"
18
- import { Link } from "wouter"
19
- import { Header2 } from "@/components/Header2"
20
-
21
- export const Footer2 = () => (
22
- <footer className="w-full py-6 bg-background">
23
- <div className="container px-4 md:px-6 mx-auto">
24
- <div className="grid gap-8 md:grid-cols-2 lg:grid-cols-4">
25
- <div className="space-y-4">
26
- <div className="flex items-center gap-2">
27
- <CircuitBoard className="h-6 w-6" />
28
- <span className="text-lg font-bold">tscircuit</span>
29
- </div>
30
- <p className="text-sm text-muted-foreground">
31
- Transforming electronic design with AI-powered tools.
32
- </p>
33
- </div>
34
- <div className="space-y-4">
35
- <h4 className="text-sm font-bold">Product</h4>
36
- <ul className="space-y-2 text-sm">
37
- <li>
38
- <Link className="text-muted-foreground hover:underline" href="#">
39
- Features
40
- </Link>
41
- </li>
42
- <li>
43
- <Link className="text-muted-foreground hover:underline" href="#">
44
- Pricing
45
- </Link>
46
- </li>
47
- <li>
48
- <Link className="text-muted-foreground hover:underline" href="#">
49
- Tutorials
50
- </Link>
51
- </li>
52
- </ul>
53
- </div>
54
- <div className="space-y-4">
55
- <h4 className="text-sm font-bold">Company</h4>
56
- <ul className="space-y-2 text-sm">
57
- <li>
58
- <Link className="text-muted-foreground hover:underline" href="#">
59
- About
60
- </Link>
61
- </li>
62
- <li>
63
- <Link className="text-muted-foreground hover:underline" href="#">
64
- Blog
65
- </Link>
66
- </li>
67
- <li>
68
- <Link className="text-muted-foreground hover:underline" href="#">
69
- Careers
70
- </Link>
71
- </li>
72
- </ul>
73
- </div>
74
- <div className="space-y-4">
75
- <h4 className="text-sm font-bold">Legal</h4>
76
- <ul className="space-y-2 text-sm">
77
- <li>
78
- <Link className="text-muted-foreground hover:underline" href="#">
79
- Privacy
80
- </Link>
81
- </li>
82
- <li>
83
- <Link className="text-muted-foreground hover:underline" href="#">
84
- Terms
85
- </Link>
86
- </li>
87
- <li>
88
- <Link className="text-muted-foreground hover:underline" href="#">
89
- Cookie Policy
90
- </Link>
91
- </li>
92
- </ul>
93
- </div>
94
- </div>
95
- <div className="mt-8 border-t pt-8 text-center text-sm text-muted-foreground">
96
- © {new Date().getFullYear()} tscircuit. All rights reserved.
97
- </div>
98
- </div>
99
- </footer>
100
- )