@tscircuit/fake-snippets 0.0.26 → 0.0.28
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/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +5 -6
- package/bun-tests/fake-snippets-api/routes/packages/list-1.test.ts +3 -0
- package/bun-tests/fake-snippets-api/routes/packages/list-2.test.ts +2 -0
- package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +5 -3
- package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +8 -5
- package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +1 -1
- package/bun.lock +110 -5
- package/dist/bundle.js +62 -11
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -2
- package/fake-snippets-api/lib/db/autoload-snippets.json +4 -0
- package/fake-snippets-api/lib/db/db-client.ts +2 -1
- package/fake-snippets-api/lib/db/schema.ts +1 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package.ts +1 -0
- package/fake-snippets-api/routes/api/package_files/list.ts +6 -3
- package/fake-snippets-api/routes/api/package_releases/get.ts +67 -1
- package/fake-snippets-api/routes/api/packages/create.ts +2 -1
- package/fake-snippets-api/routes/api/snippets/create.ts +1 -0
- package/package.json +2 -1
- package/public/placeholder-logo.png +0 -0
- package/public/placeholder-logo.svg +1 -0
- package/public/placeholder-user.jpg +0 -0
- package/public/placeholder.jpg +0 -0
- package/public/placeholder.svg +1 -0
- package/src/App.tsx +6 -0
- package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +50 -0
- package/src/components/ViewPackagePage/components/file-explorer.tsx +118 -0
- package/src/components/ViewPackagePage/components/important-files-view.tsx +231 -0
- package/src/components/ViewPackagePage/components/main-content-header.tsx +172 -0
- package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +106 -0
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +130 -0
- package/src/components/ViewPackagePage/components/package-header.tsx +107 -0
- package/src/components/ViewPackagePage/components/preview-image-squares.tsx +63 -0
- package/src/components/ViewPackagePage/components/readme-view.tsx +58 -0
- package/src/components/ViewPackagePage/components/repo-header-button.tsx +36 -0
- package/src/components/ViewPackagePage/components/repo-header.tsx +4 -0
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +213 -0
- package/src/components/ViewPackagePage/components/repo-tab-header.tsx +12 -0
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +103 -0
- package/src/components/ViewPackagePage/components/sidebar-contributors-section.tsx +31 -0
- package/src/components/ViewPackagePage/components/sidebar-packages-section.tsx +16 -0
- package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +44 -0
- package/src/components/ViewPackagePage/components/sidebar.tsx +40 -0
- package/src/components/ViewPackagePage/components/tab-views/3d-view.tsx +9 -0
- package/src/components/ViewPackagePage/components/tab-views/bom-view.tsx +9 -0
- package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +166 -0
- package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +9 -0
- package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +9 -0
- package/src/components/ViewPackagePage/components/theme-toggle.tsx +42 -0
- package/src/components/ViewPackagePage/hooks/use-mobile.tsx +19 -0
- package/src/components/ViewPackagePage/hooks/use-toast.ts +191 -0
- package/src/components/ViewPackagePage/simulate-page.tsx +120 -0
- package/src/components/ViewPackagePage/utils/is-package-file-important.ts +21 -0
- package/src/hooks/use-package-files.ts +29 -0
- package/src/hooks/use-package-release.ts +22 -0
- package/src/index.css +15 -0
- package/src/pages/beta.tsx +282 -99
- package/src/pages/view-package.tsx +38 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { TypeBadge } from "@/components/TypeBadge"
|
|
2
|
+
import { Button } from "@/components/ui/button"
|
|
3
|
+
import { LockClosedIcon } from "@radix-ui/react-icons"
|
|
4
|
+
import { Eye, GitFork, Star } from "lucide-react"
|
|
5
|
+
import { Link } from "wouter"
|
|
6
|
+
import { Skeleton } from "@/components/ui/skeleton"
|
|
7
|
+
|
|
8
|
+
interface PackageInfo {
|
|
9
|
+
name: string
|
|
10
|
+
unscoped_name: string
|
|
11
|
+
owner_github_username: string
|
|
12
|
+
star_count: string
|
|
13
|
+
description: string
|
|
14
|
+
ai_description: string
|
|
15
|
+
creator_account_id?: string
|
|
16
|
+
owner_org_id?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface PackageHeaderProps {
|
|
20
|
+
packageInfo?: PackageInfo
|
|
21
|
+
isPrivate?: boolean
|
|
22
|
+
isStarred?: boolean
|
|
23
|
+
onStarClick?: () => void
|
|
24
|
+
onForkClick?: () => void
|
|
25
|
+
isCurrentUserAuthor?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default function PackageHeader({
|
|
29
|
+
packageInfo,
|
|
30
|
+
isPrivate = false,
|
|
31
|
+
isStarred = false,
|
|
32
|
+
onStarClick,
|
|
33
|
+
onForkClick,
|
|
34
|
+
isCurrentUserAuthor = false,
|
|
35
|
+
}: PackageHeaderProps) {
|
|
36
|
+
const author = packageInfo?.owner_github_username
|
|
37
|
+
const packageName = packageInfo?.unscoped_name
|
|
38
|
+
const starCount = packageInfo?.star_count
|
|
39
|
+
? parseInt(packageInfo.star_count)
|
|
40
|
+
: 0
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<header className="bg-white border-b border-gray-200 py-4">
|
|
44
|
+
<div className="max-w-[1200px] mx-auto px-4">
|
|
45
|
+
<div className="flex items-center justify-between">
|
|
46
|
+
<div className="flex items-center">
|
|
47
|
+
{author && packageName ? (
|
|
48
|
+
<>
|
|
49
|
+
<h1 className="text-xl font-bold mr-2">
|
|
50
|
+
<Link href={`/${author}`} className="text-blue-600">
|
|
51
|
+
{author}
|
|
52
|
+
</Link>
|
|
53
|
+
<span className="px-1 text-gray-500">/</span>
|
|
54
|
+
<Link
|
|
55
|
+
className="text-blue-600"
|
|
56
|
+
href={`/${author}/${packageName}`}
|
|
57
|
+
>
|
|
58
|
+
{packageName}
|
|
59
|
+
</Link>
|
|
60
|
+
</h1>
|
|
61
|
+
{packageInfo?.name && <TypeBadge type="package" />}
|
|
62
|
+
{isPrivate && (
|
|
63
|
+
<div className="relative group pl-2">
|
|
64
|
+
<LockClosedIcon className="h-4 w-4 text-gray-700" />
|
|
65
|
+
<span className="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-1 hidden group-hover:block bg-black text-white text-xs rounded py-1 px-2">
|
|
66
|
+
private
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</>
|
|
71
|
+
) : (
|
|
72
|
+
<Skeleton className="h-6 w-72" />
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
<div className="flex items-center space-x-2">
|
|
76
|
+
<Button
|
|
77
|
+
variant="outline"
|
|
78
|
+
size="sm"
|
|
79
|
+
onClick={onStarClick}
|
|
80
|
+
disabled={!onStarClick}
|
|
81
|
+
>
|
|
82
|
+
<Star
|
|
83
|
+
className={`w-4 h-4 mr-2 ${isStarred ? "fill-yellow-500 text-yellow-500" : ""}`}
|
|
84
|
+
/>
|
|
85
|
+
{isStarred ? "Starred" : "Star"}
|
|
86
|
+
{starCount > 0 && (
|
|
87
|
+
<span className="ml-1.5 bg-gray-100 text-gray-700 rounded-full px-1.5 py-0.5 text-xs font-medium">
|
|
88
|
+
{starCount}
|
|
89
|
+
</span>
|
|
90
|
+
)}
|
|
91
|
+
</Button>
|
|
92
|
+
|
|
93
|
+
<Button
|
|
94
|
+
variant="outline"
|
|
95
|
+
size="sm"
|
|
96
|
+
onClick={onForkClick}
|
|
97
|
+
disabled={!onForkClick}
|
|
98
|
+
>
|
|
99
|
+
<GitFork className="w-4 h-4 mr-2" />
|
|
100
|
+
{isCurrentUserAuthor ? "Save" : "Fork"}
|
|
101
|
+
</Button>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</header>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { Package } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
import { useState } from "react"
|
|
5
|
+
|
|
6
|
+
interface ViewPlaceholdersProps {
|
|
7
|
+
packageInfo?: Pick<Package, "name">
|
|
8
|
+
onViewChange?: (view: "3d" | "pcb" | "schematic") => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function PreviewImageSquares({
|
|
12
|
+
packageInfo,
|
|
13
|
+
onViewChange,
|
|
14
|
+
}: ViewPlaceholdersProps) {
|
|
15
|
+
const [activeView, setActiveView] = useState("code")
|
|
16
|
+
|
|
17
|
+
const views = [
|
|
18
|
+
{ id: "3d", label: "3D View" },
|
|
19
|
+
{
|
|
20
|
+
id: "pcb",
|
|
21
|
+
label: "PCB View",
|
|
22
|
+
imageUrl: `https://registry-api.tscircuit.com/snippets/images/${packageInfo?.name}/pcb.png`,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "schematic",
|
|
26
|
+
label: "Schematic View",
|
|
27
|
+
imageUrl: `https://registry-api.tscircuit.com/snippets/images/${packageInfo?.name}/schematic.png`,
|
|
28
|
+
},
|
|
29
|
+
] satisfies {
|
|
30
|
+
id: "3d" | "pcb" | "schematic"
|
|
31
|
+
label: string
|
|
32
|
+
imageUrl?: string
|
|
33
|
+
}[]
|
|
34
|
+
|
|
35
|
+
const handleViewClick = (viewId: string) => {
|
|
36
|
+
setActiveView(viewId)
|
|
37
|
+
onViewChange?.(viewId as "3d" | "pcb" | "schematic")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="grid grid-cols-3 gap-2 mb-6">
|
|
42
|
+
{views.map((view) => (
|
|
43
|
+
<button
|
|
44
|
+
key={view.id}
|
|
45
|
+
className={`aspect-square bg-gray-100 dark:bg-[#161b22] rounded-lg border border-gray-200 dark:border-[#30363d] hover:bg-gray-200 dark:hover:bg-[#21262d] flex items-center justify-center transition-colors`}
|
|
46
|
+
onClick={() => handleViewClick(view.id)}
|
|
47
|
+
>
|
|
48
|
+
{view.imageUrl ? (
|
|
49
|
+
<img
|
|
50
|
+
src={view.imageUrl}
|
|
51
|
+
alt={view.label}
|
|
52
|
+
className="w-full h-full object-cover"
|
|
53
|
+
/>
|
|
54
|
+
) : (
|
|
55
|
+
<span className="text-xs font-medium text-gray-700 dark:text-[#c9d1d9]">
|
|
56
|
+
{view.label}
|
|
57
|
+
</span>
|
|
58
|
+
)}
|
|
59
|
+
</button>
|
|
60
|
+
))}
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Edit, FileText, List } from "lucide-react"
|
|
2
|
+
|
|
3
|
+
export default function ReadmeView() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="mt-4 border border-gray-200 dark:border-[#30363d] rounded-md overflow-hidden">
|
|
6
|
+
<div className="flex items-center px-4 py-2 bg-gray-100 dark:bg-[#161b22] border-b border-gray-200 dark:border-[#30363d]">
|
|
7
|
+
<div className="flex items-center">
|
|
8
|
+
<FileText className="h-4 w-4 mr-2" />
|
|
9
|
+
<span className="font-semibold">README</span>
|
|
10
|
+
</div>
|
|
11
|
+
<div className="ml-4 px-2 py-0.5 bg-gray-200 dark:bg-[#30363d] rounded-md text-xs">
|
|
12
|
+
MIT license
|
|
13
|
+
</div>
|
|
14
|
+
<div className="ml-auto flex items-center">
|
|
15
|
+
<Edit className="h-4 w-4 mr-1" />
|
|
16
|
+
<List className="h-4 w-4 ml-2" />
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<div className="p-4 bg-white dark:bg-[#0d1117]">
|
|
20
|
+
<h2 className="text-2xl font-bold mb-4">@tscircuit/core</h2>
|
|
21
|
+
<p className="mb-4">
|
|
22
|
+
The core logic used to build Circuit JSON from tscircuit React
|
|
23
|
+
elements.
|
|
24
|
+
</p>
|
|
25
|
+
<div className="mb-4">
|
|
26
|
+
<a
|
|
27
|
+
href="#"
|
|
28
|
+
className="text-blue-600 dark:text-[#58a6ff] hover:underline"
|
|
29
|
+
>
|
|
30
|
+
tscircuit
|
|
31
|
+
</a>
|
|
32
|
+
<span className="mx-2">•</span>
|
|
33
|
+
<a
|
|
34
|
+
href="#"
|
|
35
|
+
className="text-blue-600 dark:text-[#58a6ff] hover:underline"
|
|
36
|
+
>
|
|
37
|
+
Development Guide
|
|
38
|
+
</a>
|
|
39
|
+
<span className="mx-2">•</span>
|
|
40
|
+
<a
|
|
41
|
+
href="#"
|
|
42
|
+
className="text-blue-600 dark:text-[#58a6ff] hover:underline"
|
|
43
|
+
>
|
|
44
|
+
Core Benchmarks
|
|
45
|
+
</a>
|
|
46
|
+
</div>
|
|
47
|
+
<p>
|
|
48
|
+
You can use{" "}
|
|
49
|
+
<code className="bg-gray-100 dark:bg-[#161b22] px-1 py-0.5 rounded">
|
|
50
|
+
core
|
|
51
|
+
</code>{" "}
|
|
52
|
+
to create Circuit JSON, which can then be converted into Gerbers,
|
|
53
|
+
viewed online, and much
|
|
54
|
+
</p>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Badge } from "@/components/ui/badge"
|
|
2
|
+
import { Button } from "@/components/ui/button"
|
|
3
|
+
import { ChevronDown } from "lucide-react"
|
|
4
|
+
import type { ReactNode } from "react"
|
|
5
|
+
|
|
6
|
+
interface RepoHeaderButtonProps {
|
|
7
|
+
icon: ReactNode
|
|
8
|
+
label: string
|
|
9
|
+
count?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function RepoHeaderButton({
|
|
13
|
+
icon,
|
|
14
|
+
label,
|
|
15
|
+
count,
|
|
16
|
+
}: RepoHeaderButtonProps) {
|
|
17
|
+
return (
|
|
18
|
+
<Button
|
|
19
|
+
variant="outline"
|
|
20
|
+
size="sm"
|
|
21
|
+
className="border-gray-300 dark:border-[#30363d] bg-gray-100 hover:bg-gray-200 dark:bg-[#21262d] dark:hover:bg-[#30363d] text-gray-700 dark:text-[#c9d1d9]"
|
|
22
|
+
>
|
|
23
|
+
{icon}
|
|
24
|
+
{label}
|
|
25
|
+
{count !== undefined && (
|
|
26
|
+
<Badge
|
|
27
|
+
variant="outline"
|
|
28
|
+
className="ml-2 text-xs rounded-full px-2 py-0.5 bg-transparent border-gray-300 dark:border-[#30363d]"
|
|
29
|
+
>
|
|
30
|
+
{count}
|
|
31
|
+
</Badge>
|
|
32
|
+
)}
|
|
33
|
+
<ChevronDown className="h-4 w-4 ml-1" />
|
|
34
|
+
</Button>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useMemo } from "react"
|
|
4
|
+
import MainContentHeader from "./main-content-header"
|
|
5
|
+
import Sidebar from "./sidebar"
|
|
6
|
+
import MobileSidebar from "./mobile-sidebar"
|
|
7
|
+
import ImportantFilesView from "./important-files-view"
|
|
8
|
+
import { ShikiCodeViewer } from "./ShikiCodeViewer"
|
|
9
|
+
|
|
10
|
+
// Tab Views
|
|
11
|
+
import FilesView from "./tab-views/files-view"
|
|
12
|
+
import ThreeDView from "./tab-views/3d-view"
|
|
13
|
+
import PCBView from "./tab-views/pcb-view"
|
|
14
|
+
import SchematicView from "./tab-views/schematic-view"
|
|
15
|
+
import BOMView from "./tab-views/bom-view"
|
|
16
|
+
import { isPackageFileImportant } from "../utils/is-package-file-important"
|
|
17
|
+
import Header from "@/components/Header"
|
|
18
|
+
import Footer from "@/components/Footer"
|
|
19
|
+
import ViewSnippetHeader from "@/components/ViewSnippetHeader"
|
|
20
|
+
import PackageHeader from "./package-header"
|
|
21
|
+
|
|
22
|
+
interface PackageFile {
|
|
23
|
+
package_file_id: string
|
|
24
|
+
package_release_id: string
|
|
25
|
+
file_path: string
|
|
26
|
+
content_text: string
|
|
27
|
+
created_at: string // iso-8601
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface PackageInfo {
|
|
31
|
+
name: string
|
|
32
|
+
unscoped_name: string
|
|
33
|
+
owner_github_username: string
|
|
34
|
+
star_count: string
|
|
35
|
+
description: string
|
|
36
|
+
ai_description: string
|
|
37
|
+
ai_usage_instructions: string
|
|
38
|
+
creator_account_id?: string
|
|
39
|
+
owner_org_id?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface RepoPageContentProps {
|
|
43
|
+
packageFiles?: PackageFile[]
|
|
44
|
+
importantFilePaths?: string[]
|
|
45
|
+
packageInfo?: PackageInfo
|
|
46
|
+
onFileClicked?: (file: any) => void
|
|
47
|
+
onDirectoryClicked?: (directory: any) => void
|
|
48
|
+
onExportClicked?: (exportType: string) => void
|
|
49
|
+
onEditClicked?: () => void
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export default function RepoPageContent({
|
|
53
|
+
packageFiles,
|
|
54
|
+
packageInfo,
|
|
55
|
+
onFileClicked,
|
|
56
|
+
onDirectoryClicked,
|
|
57
|
+
onExportClicked,
|
|
58
|
+
onEditClicked,
|
|
59
|
+
}: RepoPageContentProps) {
|
|
60
|
+
const [activeView, setActiveView] = useState("files")
|
|
61
|
+
|
|
62
|
+
const importantFilePaths = packageFiles
|
|
63
|
+
?.filter((pf) => isPackageFileImportant(pf.file_path))
|
|
64
|
+
?.map((pf) => pf.file_path)
|
|
65
|
+
|
|
66
|
+
// Parse package files to determine directories and files structure
|
|
67
|
+
const { directories, files } = useMemo(() => {
|
|
68
|
+
if (!packageFiles) {
|
|
69
|
+
return { directories: [], files: [] }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const dirs = new Set<string>()
|
|
73
|
+
const filesList: Array<{
|
|
74
|
+
type: "file"
|
|
75
|
+
path: string
|
|
76
|
+
name: string
|
|
77
|
+
created_at: string
|
|
78
|
+
}> = []
|
|
79
|
+
|
|
80
|
+
packageFiles.forEach((file) => {
|
|
81
|
+
// Extract directory path
|
|
82
|
+
const pathParts = file.file_path.split("/")
|
|
83
|
+
const fileName = pathParts.pop() || ""
|
|
84
|
+
|
|
85
|
+
// Add all parent directories
|
|
86
|
+
let currentPath = ""
|
|
87
|
+
pathParts.forEach((part) => {
|
|
88
|
+
currentPath += (currentPath ? "/" : "") + part
|
|
89
|
+
dirs.add(currentPath)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
filesList.push({
|
|
93
|
+
type: "file",
|
|
94
|
+
path: file.file_path,
|
|
95
|
+
name: fileName,
|
|
96
|
+
created_at: file.created_at,
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// Convert directories set to array of directory objects
|
|
101
|
+
const dirsList = Array.from(dirs)
|
|
102
|
+
.map((path) => {
|
|
103
|
+
const pathParts = path.split("/")
|
|
104
|
+
if (!path) return null
|
|
105
|
+
return {
|
|
106
|
+
type: "directory",
|
|
107
|
+
path,
|
|
108
|
+
name: pathParts[pathParts.length - 1],
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
.filter((dir) => dir !== null)
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
directories: dirsList,
|
|
115
|
+
files: filesList,
|
|
116
|
+
}
|
|
117
|
+
}, [packageFiles])
|
|
118
|
+
|
|
119
|
+
// Find important files based on importantFilePaths
|
|
120
|
+
const importantFiles = useMemo(() => {
|
|
121
|
+
if (!packageFiles || !importantFilePaths) return []
|
|
122
|
+
|
|
123
|
+
return packageFiles.filter((file) =>
|
|
124
|
+
importantFilePaths.some((path) => file.file_path.endsWith(path)),
|
|
125
|
+
)
|
|
126
|
+
}, [packageFiles, importantFilePaths])
|
|
127
|
+
|
|
128
|
+
// Render the appropriate content based on the active view
|
|
129
|
+
const renderContent = () => {
|
|
130
|
+
switch (activeView) {
|
|
131
|
+
case "files":
|
|
132
|
+
return (
|
|
133
|
+
<FilesView
|
|
134
|
+
directories={directories as any}
|
|
135
|
+
files={files as any}
|
|
136
|
+
isLoading={!packageFiles}
|
|
137
|
+
onFileClicked={onFileClicked}
|
|
138
|
+
onDirectoryClicked={onDirectoryClicked}
|
|
139
|
+
/>
|
|
140
|
+
)
|
|
141
|
+
case "3d":
|
|
142
|
+
return <ThreeDView />
|
|
143
|
+
case "pcb":
|
|
144
|
+
return <PCBView />
|
|
145
|
+
case "schematic":
|
|
146
|
+
return <SchematicView />
|
|
147
|
+
case "bom":
|
|
148
|
+
return <BOMView />
|
|
149
|
+
default:
|
|
150
|
+
return (
|
|
151
|
+
<FilesView
|
|
152
|
+
directories={directories as any}
|
|
153
|
+
files={files as any}
|
|
154
|
+
isLoading={!packageFiles}
|
|
155
|
+
onFileClicked={onFileClicked}
|
|
156
|
+
onDirectoryClicked={onDirectoryClicked}
|
|
157
|
+
/>
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div className="min-h-screen bg-white dark:bg-[#0d1117] text-gray-900 dark:text-[#c9d1d9] font-sans">
|
|
164
|
+
<Header />
|
|
165
|
+
<PackageHeader packageInfo={packageInfo} />
|
|
166
|
+
|
|
167
|
+
{/* Mobile Sidebar */}
|
|
168
|
+
<div className="max-w-[1200px] mx-auto">
|
|
169
|
+
<MobileSidebar packageInfo={packageInfo} isLoading={!packageInfo} />
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
{/* Main Content */}
|
|
173
|
+
<div className="max-w-[1200px] mx-auto">
|
|
174
|
+
<div className="flex flex-col md:flex-row">
|
|
175
|
+
{/* Main Content Area */}
|
|
176
|
+
<div className="w-full md:flex-1 border-r border-gray-200 dark:border-[#30363d] p-4 md:max-w-[calc(100%-296px)] max-w-full">
|
|
177
|
+
{/* Main Content Header with Tabs */}
|
|
178
|
+
<MainContentHeader
|
|
179
|
+
activeView={activeView}
|
|
180
|
+
onViewChange={setActiveView}
|
|
181
|
+
onExportClicked={onExportClicked}
|
|
182
|
+
packageInfo={packageInfo}
|
|
183
|
+
/>
|
|
184
|
+
|
|
185
|
+
{/* Dynamic Content based on active view */}
|
|
186
|
+
{renderContent()}
|
|
187
|
+
|
|
188
|
+
{/* Important Files View - Always shown */}
|
|
189
|
+
<ImportantFilesView
|
|
190
|
+
importantFiles={importantFiles}
|
|
191
|
+
isLoading={!packageFiles}
|
|
192
|
+
onEditClicked={onEditClicked}
|
|
193
|
+
aiDescription={packageInfo?.ai_description}
|
|
194
|
+
aiUsageInstructions={packageInfo?.ai_usage_instructions}
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
{/* Sidebar - Hidden on mobile, shown on md and up */}
|
|
199
|
+
<div className="hidden md:block md:w-[296px] flex-shrink-0">
|
|
200
|
+
<Sidebar
|
|
201
|
+
packageInfo={packageInfo}
|
|
202
|
+
isLoading={!packageInfo}
|
|
203
|
+
onViewChange={(view) => {
|
|
204
|
+
setActiveView(view)
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
<Footer />
|
|
211
|
+
</div>
|
|
212
|
+
)
|
|
213
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface RepoTabHeaderProps {
|
|
2
|
+
activeTab: string
|
|
3
|
+
setActiveTab: (tab: string) => void
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export default function RepoTabHeader({
|
|
7
|
+
activeTab,
|
|
8
|
+
setActiveTab,
|
|
9
|
+
}: RepoTabHeaderProps) {
|
|
10
|
+
// This component is being handled by a different team
|
|
11
|
+
return null
|
|
12
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Badge } from "@/components/ui/badge"
|
|
2
|
+
import { GitFork, Star } from "lucide-react"
|
|
3
|
+
import { Skeleton } from "@/components/ui/skeleton"
|
|
4
|
+
|
|
5
|
+
interface PackageInfo {
|
|
6
|
+
name: string
|
|
7
|
+
unscoped_name: string
|
|
8
|
+
owner_github_username: string
|
|
9
|
+
star_count: string
|
|
10
|
+
description: string
|
|
11
|
+
ai_description: string
|
|
12
|
+
creator_account_id?: string
|
|
13
|
+
owner_org_id?: string
|
|
14
|
+
is_package?: boolean
|
|
15
|
+
website?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface SidebarAboutSectionProps {
|
|
19
|
+
packageInfo?: PackageInfo
|
|
20
|
+
isLoading?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const LinkIcon = () => (
|
|
24
|
+
<svg className="h-4 w-4 mr-1" viewBox="0 0 16 16" fill="currentColor">
|
|
25
|
+
<path d="M7.775 3.275a.75.75 0 0 0 1.06 1.06l1.25-1.25a2 2 0 1 1 2.83 2.83l-2.5 2.5a2 2 0 0 1-2.83 0 .75.75 0 0 0-1.06 1.06 3.5 3.5 0 0 0 4.95 0l2.5-2.5a3.5 3.5 0 0 0-4.95-4.95l-1.25 1.25Zm-4.69 9.64a2 2 0 0 1 0-2.83l2.5-2.5a2 2 0 0 1 2.83 0 .75.75 0 0 0 1.06-1.06 3.5 3.5 0 0 0-4.95 0l-2.5 2.5a3.5 3.5 0 0 0 4.95 4.95l1.25-1.25a.75.75 0 0 0-1.06-1.06l-1.25 1.25a2 2 0 0 1-2.83 0Z"></path>
|
|
26
|
+
</svg>
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
export default function SidebarAboutSection({
|
|
30
|
+
packageInfo,
|
|
31
|
+
isLoading = false,
|
|
32
|
+
}: SidebarAboutSectionProps) {
|
|
33
|
+
const topics = packageInfo?.is_package ? ["Package"] : ["Board"]
|
|
34
|
+
|
|
35
|
+
if (isLoading) {
|
|
36
|
+
return (
|
|
37
|
+
<div className="mb-6">
|
|
38
|
+
<h2 className="text-lg font-semibold mb-2">About</h2>
|
|
39
|
+
<Skeleton className="h-4 w-full mb-3" />
|
|
40
|
+
<Skeleton className="h-4 w-3/4 mb-4" />
|
|
41
|
+
<div className="flex flex-wrap gap-2 mb-4">
|
|
42
|
+
{[1, 2, 3].map((i) => (
|
|
43
|
+
<Skeleton key={i} className="h-6 w-20 rounded-full" />
|
|
44
|
+
))}
|
|
45
|
+
</div>
|
|
46
|
+
<div className="space-y-2">
|
|
47
|
+
<Skeleton className="h-4 w-24" />
|
|
48
|
+
<Skeleton className="h-4 w-20" />
|
|
49
|
+
<Skeleton className="h-4 w-20" />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className="mb-6">
|
|
57
|
+
<h2 className="text-lg font-semibold mb-2">About</h2>
|
|
58
|
+
<p className="text-sm mb-3">
|
|
59
|
+
{packageInfo?.description || packageInfo?.ai_description}
|
|
60
|
+
</p>
|
|
61
|
+
{packageInfo?.website && (
|
|
62
|
+
<a
|
|
63
|
+
href="#"
|
|
64
|
+
className="text-blue-600 dark:text-[#58a6ff] hover:underline text-sm flex items-center mb-4"
|
|
65
|
+
>
|
|
66
|
+
<LinkIcon />
|
|
67
|
+
{packageInfo?.website}
|
|
68
|
+
</a>
|
|
69
|
+
)}
|
|
70
|
+
<div className="flex flex-wrap gap-2 mb-4">
|
|
71
|
+
{topics.map((topic, index) => (
|
|
72
|
+
<Badge
|
|
73
|
+
key={index}
|
|
74
|
+
variant="outline"
|
|
75
|
+
className="text-xs rounded-full px-2 py-0.5 bg-blue-100 dark:bg-[#1f6feb33] text-blue-600 dark:text-[#58a6ff] border-blue-300 dark:border-[#1f6feb66]"
|
|
76
|
+
>
|
|
77
|
+
{topic}
|
|
78
|
+
</Badge>
|
|
79
|
+
))}
|
|
80
|
+
</div>
|
|
81
|
+
<div className="space-y-2 text-sm">
|
|
82
|
+
<div className="flex items-center">
|
|
83
|
+
<svg
|
|
84
|
+
className="h-4 w-4 mr-2 text-gray-500 dark:text-[#8b949e]"
|
|
85
|
+
viewBox="0 0 16 16"
|
|
86
|
+
fill="currentColor"
|
|
87
|
+
>
|
|
88
|
+
<path d="M8.75.75V2h.985c.304 0 .603.08.867.231l1.29.736c.038.022.08.033.124.033h2.234a.75.75 0 0 1 0 1.5h-.427l2.111 4.692a.75.75 0 0 1-.154.838l-.53-.53.53.53-.001.002-.002.002-.006.006-.006.005-.01.01-.045.04c-.21.176-.441.327-.686.45C14.556 10.78 13.88 11 13 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L12.178 4.5h-.162c-.305 0-.604-.079-.868-.231l-1.29-.736a.245.245 0 0 0-.124-.033H8.75V13h2.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.5V3.5h-.984a.245.245 0 0 0-.124.033l-1.29.736c-.264.152-.563.231-.868.231h-.162l2.112 4.692a.75.75 0 0 1-.154.838l-.53-.53.53.53-.001.002-.002.002-.006.006-.016.015-.045.04c-.21.176-.441.327-.686.45C4.556 10.78 3.88 11 3 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L2.178 4.5H1.75a.75.75 0 0 1 0-1.5h2.234a.249.249 0 0 0 .125-.033l1.29-.736c.263-.15.561-.231.865-.231H7.25V.75a.75.75 0 0 1 1.5 0Zm2.945 8.477c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327Zm-10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327Z"></path>
|
|
89
|
+
</svg>
|
|
90
|
+
<span>MIT license</span>
|
|
91
|
+
</div>
|
|
92
|
+
<div className="flex items-center">
|
|
93
|
+
<Star className="h-4 w-4 mr-2 text-gray-500 dark:text-[#8b949e]" />
|
|
94
|
+
<span>{packageInfo?.star_count || "16"} stars</span>
|
|
95
|
+
</div>
|
|
96
|
+
<div className="flex items-center">
|
|
97
|
+
<GitFork className="h-4 w-4 mr-2 text-gray-500 dark:text-[#8b949e]" />
|
|
98
|
+
<span>39 forks</span>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Badge } from "@/components/ui/badge"
|
|
2
|
+
|
|
3
|
+
export default function SidebarContributorsSection() {
|
|
4
|
+
return (
|
|
5
|
+
<div>
|
|
6
|
+
<div className="flex items-center justify-between mb-2">
|
|
7
|
+
<h2 className="text-lg font-semibold">Contributors</h2>
|
|
8
|
+
<Badge
|
|
9
|
+
variant="outline"
|
|
10
|
+
className="text-xs rounded-full px-2 py-0.5 bg-transparent border-gray-300 dark:border-[#30363d]"
|
|
11
|
+
>
|
|
12
|
+
18
|
|
13
|
+
</Badge>
|
|
14
|
+
</div>
|
|
15
|
+
<div className="flex flex-wrap gap-1 mb-2">
|
|
16
|
+
{[...Array(10)].map((_, i) => (
|
|
17
|
+
<div
|
|
18
|
+
key={i}
|
|
19
|
+
className="w-8 h-8 rounded-full bg-gradient-to-br from-blue-500 to-purple-500"
|
|
20
|
+
></div>
|
|
21
|
+
))}
|
|
22
|
+
</div>
|
|
23
|
+
<a
|
|
24
|
+
href="#"
|
|
25
|
+
className="text-blue-600 dark:text-[#58a6ff] hover:underline text-sm"
|
|
26
|
+
>
|
|
27
|
+
+ 4 contributors
|
|
28
|
+
</a>
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default function SidebarPackagesSection() {
|
|
2
|
+
return (
|
|
3
|
+
<div className="mb-6">
|
|
4
|
+
<h2 className="text-lg font-semibold mb-2">Packages</h2>
|
|
5
|
+
<p className="text-sm text-gray-500 dark:text-[#8b949e] mb-1">
|
|
6
|
+
No packages published
|
|
7
|
+
</p>
|
|
8
|
+
<a
|
|
9
|
+
href="#"
|
|
10
|
+
className="text-blue-600 dark:text-[#58a6ff] hover:underline text-sm"
|
|
11
|
+
>
|
|
12
|
+
Publish your first package
|
|
13
|
+
</a>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
}
|