@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.
- 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/dist/bundle.js +23 -23
- package/dist/index.d.ts +21 -15
- package/dist/index.js +17 -17
- package/dist/schema.d.ts +24 -24
- package/dist/schema.js +5 -5
- package/fake-snippets-api/lib/db/db-client.ts +10 -1
- package/fake-snippets-api/lib/db/schema.ts +3 -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 +1 -1
- package/src/App.tsx +12 -9
- package/src/components/FileSidebar.tsx +14 -159
- package/src/components/PackageBreadcrumb.tsx +1 -1
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -1
- 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/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 +11 -5
- package/src/components/preview/index.tsx +16 -153
- package/src/index.css +24 -0
- package/src/lib/utils/transformFilesToTreeData.tsx +195 -0
- package/src/pages/404.tsx +3 -5
- package/src/pages/preview-release.tsx +269 -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/hooks/use-snippets-base-api-url.ts +0 -3
- package/src/pages/preview-build.tsx +0 -380
|
@@ -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
|
-
}
|
package/src/index.css
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
@tailwind base;
|
|
2
2
|
@tailwind components;
|
|
3
3
|
@tailwind utilities;
|
|
4
|
+
|
|
4
5
|
@layer base {
|
|
5
6
|
:root {
|
|
6
7
|
--radius: 0.5rem;
|
|
@@ -18,6 +19,10 @@
|
|
|
18
19
|
-ms-overflow-style: none; /* IE and Edge */
|
|
19
20
|
scrollbar-width: none; /* Firefox */
|
|
20
21
|
}
|
|
22
|
+
|
|
23
|
+
.shiki {
|
|
24
|
+
@apply no-scrollbar;
|
|
25
|
+
}
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
.shiki {
|
|
@@ -82,3 +87,22 @@
|
|
|
82
87
|
body {
|
|
83
88
|
overflow-x: hidden;
|
|
84
89
|
}
|
|
90
|
+
|
|
91
|
+
/* Subtle scrollbar styling for html and body */
|
|
92
|
+
html::-webkit-scrollbar,
|
|
93
|
+
body::-webkit-scrollbar {
|
|
94
|
+
width: 6px;
|
|
95
|
+
background-color: transparent;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
html::-webkit-scrollbar-thumb,
|
|
99
|
+
body::-webkit-scrollbar-thumb {
|
|
100
|
+
background-color: rgba(215, 215, 215, 0.564);
|
|
101
|
+
border-radius: 8px;
|
|
102
|
+
transition: background-color 0.2s;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
html::-webkit-scrollbar-thumb:hover,
|
|
106
|
+
body::-webkit-scrollbar-thumb:hover {
|
|
107
|
+
background-color: rgba(193, 193, 193, 0.455);
|
|
108
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { TreeDataItem } from "@/components/ui/tree-view"
|
|
2
|
+
import type {
|
|
3
|
+
IRenameFileProps,
|
|
4
|
+
IDeleteFileProps,
|
|
5
|
+
} from "@/hooks/useFileManagement"
|
|
6
|
+
import { isHiddenFile } from "@/components/ViewPackagePage/utils/is-hidden-file"
|
|
7
|
+
import { File, Folder, MoreVertical, Pencil, Trash2 } from "lucide-react"
|
|
8
|
+
import {
|
|
9
|
+
DropdownMenu,
|
|
10
|
+
DropdownMenuContent,
|
|
11
|
+
DropdownMenuGroup,
|
|
12
|
+
DropdownMenuItem,
|
|
13
|
+
DropdownMenuTrigger,
|
|
14
|
+
} from "@/components/ui/dropdown-menu"
|
|
15
|
+
import { useToast } from "@/hooks/use-toast"
|
|
16
|
+
|
|
17
|
+
type FileName = string
|
|
18
|
+
type TreeNode = Omit<TreeDataItem, "children"> & {
|
|
19
|
+
children?: Record<string, TreeNode>
|
|
20
|
+
}
|
|
21
|
+
interface TransformFilesToTreeDataProps {
|
|
22
|
+
files: Record<FileName, string>
|
|
23
|
+
currentFile: FileName | null
|
|
24
|
+
renamingFile: string | null
|
|
25
|
+
handleRenameFile: (props: IRenameFileProps) => { fileRenamed: boolean }
|
|
26
|
+
handleDeleteFile: (props: IDeleteFileProps) => { fileDeleted: boolean }
|
|
27
|
+
setRenamingFile: (filename: string | null) => void
|
|
28
|
+
onFileSelect: (filename: FileName) => void
|
|
29
|
+
onFolderSelect: (folderPath: string) => void
|
|
30
|
+
canModifyFiles: boolean
|
|
31
|
+
setErrorMessage: (message: string) => void
|
|
32
|
+
setSelectedFolderForCreation: (folder: string | null) => void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const transformFilesToTreeData = ({
|
|
36
|
+
files,
|
|
37
|
+
currentFile,
|
|
38
|
+
renamingFile,
|
|
39
|
+
handleRenameFile,
|
|
40
|
+
handleDeleteFile,
|
|
41
|
+
setRenamingFile,
|
|
42
|
+
onFileSelect,
|
|
43
|
+
onFolderSelect,
|
|
44
|
+
canModifyFiles,
|
|
45
|
+
setErrorMessage,
|
|
46
|
+
setSelectedFolderForCreation,
|
|
47
|
+
}: TransformFilesToTreeDataProps): TreeDataItem[] => {
|
|
48
|
+
const { toast } = useToast()
|
|
49
|
+
const root: Record<string, TreeNode> = {}
|
|
50
|
+
|
|
51
|
+
Object.keys(files).forEach((filePath) => {
|
|
52
|
+
const hasLeadingSlash = filePath.startsWith("/")
|
|
53
|
+
const pathSegments = (hasLeadingSlash ? filePath.slice(1) : filePath)
|
|
54
|
+
.trim()
|
|
55
|
+
.split("/")
|
|
56
|
+
let currentNode: Record<string, TreeNode> = root
|
|
57
|
+
|
|
58
|
+
pathSegments.forEach((segment, segmentIndex) => {
|
|
59
|
+
const isLeafNode = segmentIndex === pathSegments.length - 1
|
|
60
|
+
const ancestorPath = pathSegments.slice(0, segmentIndex).join("/")
|
|
61
|
+
const relativePath = ancestorPath ? `${ancestorPath}/${segment}` : segment
|
|
62
|
+
const absolutePath = hasLeadingSlash ? `/${relativePath}` : relativePath
|
|
63
|
+
const itemId = absolutePath
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
!currentNode[segment] &&
|
|
67
|
+
(!isHiddenFile(relativePath) ||
|
|
68
|
+
isHiddenFile(
|
|
69
|
+
currentFile?.startsWith("/")
|
|
70
|
+
? currentFile.slice(1)
|
|
71
|
+
: currentFile || "",
|
|
72
|
+
))
|
|
73
|
+
) {
|
|
74
|
+
currentNode[segment] = {
|
|
75
|
+
id: itemId,
|
|
76
|
+
name: segment,
|
|
77
|
+
isRenaming: renamingFile === itemId,
|
|
78
|
+
onRename: (newFilename: string) => {
|
|
79
|
+
const oldPath = itemId
|
|
80
|
+
const pathParts = oldPath.split("/").filter((part) => part !== "")
|
|
81
|
+
let newPath: string
|
|
82
|
+
|
|
83
|
+
if (pathParts.length > 1) {
|
|
84
|
+
const folderPath = pathParts.slice(0, -1).join("/")
|
|
85
|
+
newPath = folderPath + "/" + newFilename
|
|
86
|
+
} else {
|
|
87
|
+
newPath = newFilename
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (oldPath.startsWith("/") && !newPath.startsWith("/")) {
|
|
91
|
+
newPath = "/" + newPath
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const { fileRenamed } = handleRenameFile({
|
|
95
|
+
oldFilename: itemId,
|
|
96
|
+
newFilename: newPath,
|
|
97
|
+
onError: (error) => {
|
|
98
|
+
toast({
|
|
99
|
+
title: `Error renaming file`,
|
|
100
|
+
description: error.message,
|
|
101
|
+
variant: "destructive",
|
|
102
|
+
})
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
if (fileRenamed) {
|
|
106
|
+
setRenamingFile(null)
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
onCancelRename: () => {
|
|
110
|
+
setRenamingFile(null)
|
|
111
|
+
},
|
|
112
|
+
icon: isLeafNode ? File : Folder,
|
|
113
|
+
onClick: isLeafNode
|
|
114
|
+
? () => {
|
|
115
|
+
onFileSelect(absolutePath)
|
|
116
|
+
setSelectedFolderForCreation(null)
|
|
117
|
+
}
|
|
118
|
+
: () => onFolderSelect(absolutePath),
|
|
119
|
+
draggable: false,
|
|
120
|
+
droppable: !isLeafNode,
|
|
121
|
+
children: isLeafNode ? undefined : {},
|
|
122
|
+
actions: canModifyFiles ? (
|
|
123
|
+
<>
|
|
124
|
+
<DropdownMenu key={itemId}>
|
|
125
|
+
<DropdownMenuTrigger asChild>
|
|
126
|
+
<MoreVertical className="w-4 h-4 text-gray-500 hover:text-gray-700" />
|
|
127
|
+
</DropdownMenuTrigger>
|
|
128
|
+
<DropdownMenuContent
|
|
129
|
+
className="w-fit bg-white shadow-lg rounded-md border-4 z-[100] border-white"
|
|
130
|
+
style={{
|
|
131
|
+
position: "absolute",
|
|
132
|
+
top: "100%",
|
|
133
|
+
left: "0",
|
|
134
|
+
marginTop: "0.5rem",
|
|
135
|
+
width: "8rem",
|
|
136
|
+
padding: "0.01rem",
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
<DropdownMenuGroup>
|
|
140
|
+
{isLeafNode && (
|
|
141
|
+
<DropdownMenuItem
|
|
142
|
+
onClick={() => {
|
|
143
|
+
setRenamingFile(itemId)
|
|
144
|
+
}}
|
|
145
|
+
className="flex items-center px-3 py-1 text-xs text-black hover:bg-gray-100 cursor-pointer"
|
|
146
|
+
>
|
|
147
|
+
<Pencil className="mr-2 h-3 w-3" />
|
|
148
|
+
Rename
|
|
149
|
+
</DropdownMenuItem>
|
|
150
|
+
)}
|
|
151
|
+
<DropdownMenuItem
|
|
152
|
+
onClick={() => {
|
|
153
|
+
const { fileDeleted } = handleDeleteFile({
|
|
154
|
+
filename: itemId,
|
|
155
|
+
onError: (error) => {
|
|
156
|
+
toast({
|
|
157
|
+
title: `Error deleting file ${itemId}`,
|
|
158
|
+
description: error.message,
|
|
159
|
+
})
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
if (fileDeleted) {
|
|
163
|
+
setErrorMessage("")
|
|
164
|
+
}
|
|
165
|
+
}}
|
|
166
|
+
className="flex items-center px-3 py-1 text-xs text-red-600 hover:bg-gray-100 cursor-pointer"
|
|
167
|
+
>
|
|
168
|
+
<Trash2 className="mr-2 h-3 w-3" />
|
|
169
|
+
Delete
|
|
170
|
+
</DropdownMenuItem>
|
|
171
|
+
</DropdownMenuGroup>
|
|
172
|
+
</DropdownMenuContent>
|
|
173
|
+
</DropdownMenu>
|
|
174
|
+
</>
|
|
175
|
+
) : undefined,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!isLeafNode && currentNode[segment]?.children) {
|
|
180
|
+
currentNode = currentNode[segment].children
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const convertToArray = (items: Record<string, TreeNode>): TreeDataItem[] => {
|
|
186
|
+
return Object.values(items).map((item) => ({
|
|
187
|
+
...item,
|
|
188
|
+
children: item.children ? convertToArray(item.children) : undefined,
|
|
189
|
+
}))
|
|
190
|
+
}
|
|
191
|
+
return convertToArray(root).filter((x) => {
|
|
192
|
+
if (x.children?.length === 0) return false
|
|
193
|
+
return true
|
|
194
|
+
})
|
|
195
|
+
}
|
package/src/pages/404.tsx
CHANGED
|
@@ -5,15 +5,13 @@ import { NotFound } from "@/components/NotFound"
|
|
|
5
5
|
|
|
6
6
|
export function NotFoundPage({
|
|
7
7
|
heading = "Page Not Found",
|
|
8
|
-
|
|
8
|
+
subtitle = "The page you're looking for doesn't exist.",
|
|
9
|
+
}: { heading?: string; subtitle?: string }) {
|
|
9
10
|
return (
|
|
10
11
|
<div className="flex min-h-screen flex-col">
|
|
11
12
|
<Helmet>
|
|
12
13
|
<title>404 - {heading} | tscircuit</title>
|
|
13
|
-
<meta
|
|
14
|
-
name="description"
|
|
15
|
-
content="The page you're looking for doesn't exist."
|
|
16
|
-
/>
|
|
14
|
+
<meta name="description" content={subtitle} />
|
|
17
15
|
</Helmet>
|
|
18
16
|
<Header2 />
|
|
19
17
|
<NotFound heading={heading} />
|