@tscircuit/fake-snippets 0.0.73 → 0.0.75
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/dist/bundle.js +250 -202
- package/dist/index.d.ts +72 -2
- package/dist/index.js +24 -7
- package/dist/schema.d.ts +112 -0
- package/dist/schema.js +18 -1
- package/fake-snippets-api/lib/db/db-client.ts +11 -7
- package/fake-snippets-api/lib/db/schema.ts +25 -0
- package/fake-snippets-api/routes/api/package_releases/rebuild.ts +32 -0
- package/fake-snippets-api/routes/api/package_releases/update.ts +4 -1
- package/package.json +1 -1
- package/src/components/DownloadButtonAndMenu.tsx +26 -8
- package/src/components/Header.tsx +7 -7
- package/src/components/PackageBuildsPage/ErrorObject.ts +12 -0
- package/src/components/PackageBuildsPage/LogContent.tsx +32 -0
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +114 -0
- package/src/components/PackageBuildsPage/build-preview-content.tsx +21 -5
- package/src/components/PackageBuildsPage/capitalCase.ts +4 -0
- package/src/components/PackageBuildsPage/collapsible-section.tsx +34 -9
- package/src/components/PackageBuildsPage/getColorForDisplayStatus.ts +17 -0
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +147 -0
- package/src/components/PackageBuildsPage/{deployment-header.tsx → package-build-header.tsx} +16 -31
- package/src/components/ViewPackagePage/components/main-content-header.tsx +1 -1
- package/src/components/ViewPackagePage/components/package-header.tsx +0 -1
- package/src/components/dialogs/pcb-download-dialog.tsx +113 -0
- package/src/components/package-port/CodeAndPreview.tsx +3 -2
- package/src/components/package-port/CodeEditor.tsx +4 -2
- package/src/hooks/use-create-package-mutation.ts +0 -7
- package/src/hooks/use-current-package-release.ts +28 -0
- package/src/hooks/useFileManagement.ts +26 -21
- package/src/lib/download-fns/download-pcb-svg.ts +35 -0
- package/src/lib/utils/timeAgo.ts +14 -3
- package/src/pages/package-builds.tsx +2 -2
- package/src/components/PackageBuildsPage/DeploymentDetailsPage.tsx +0 -56
- package/src/components/PackageBuildsPage/deployment-details-panel.tsx +0 -84
|
@@ -2,14 +2,9 @@ import { HeaderLogin } from "@/components/HeaderLogin"
|
|
|
2
2
|
import { Button } from "@/components/ui/button"
|
|
3
3
|
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
4
4
|
import { cn } from "@/lib/utils"
|
|
5
|
-
import {
|
|
6
|
-
GitHubLogoIcon,
|
|
7
|
-
OpenInNewWindowIcon,
|
|
8
|
-
ChatBubbleIcon,
|
|
9
|
-
DiscordLogoIcon,
|
|
10
|
-
} from "@radix-ui/react-icons"
|
|
5
|
+
import { GitHubLogoIcon, DiscordLogoIcon } from "@radix-ui/react-icons"
|
|
11
6
|
import { Menu, X } from "lucide-react"
|
|
12
|
-
import React, { useState } from "react"
|
|
7
|
+
import React, { useEffect, useState } from "react"
|
|
13
8
|
import { useLocation } from "wouter"
|
|
14
9
|
import { PrefetchPageLink } from "./PrefetchPageLink"
|
|
15
10
|
import CmdKMenu from "./CmdKMenu"
|
|
@@ -55,6 +50,11 @@ const HeaderButton = ({
|
|
|
55
50
|
export default function Header() {
|
|
56
51
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
|
57
52
|
const isLoggedIn = useGlobalStore((s) => Boolean(s.session))
|
|
53
|
+
const sessionToken = useGlobalStore((s) => s.session?.token)
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
window.TSCIRCUIT_REGISTRY_TOKEN = sessionToken ?? ""
|
|
57
|
+
}, [sessionToken])
|
|
58
58
|
|
|
59
59
|
return (
|
|
60
60
|
<header className="px-4 py-3">
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
type ErrorObject =
|
|
2
|
+
| {
|
|
3
|
+
message: string
|
|
4
|
+
}
|
|
5
|
+
| string
|
|
6
|
+
|
|
7
|
+
const getErrorText = (error: ErrorObject | string) => {
|
|
8
|
+
if (typeof error === "string") {
|
|
9
|
+
return error
|
|
10
|
+
}
|
|
11
|
+
return error.message
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const LogContent = ({
|
|
15
|
+
logs,
|
|
16
|
+
error,
|
|
17
|
+
}: { logs: any[]; error?: ErrorObject | string | null }) => {
|
|
18
|
+
return (
|
|
19
|
+
<div className="whitespace-pre-wrap font-mono text-xs">
|
|
20
|
+
{logs.map((log) =>
|
|
21
|
+
log.msg || log.message ? (
|
|
22
|
+
<div>{log.msg ?? log.message}</div>
|
|
23
|
+
) : (
|
|
24
|
+
<div>
|
|
25
|
+
<pre>{log.message}</pre>
|
|
26
|
+
</div>
|
|
27
|
+
),
|
|
28
|
+
)}
|
|
29
|
+
{error && <div className="text-red-600">{getErrorText(error)}</div>}
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState } from "react"
|
|
4
|
+
import { BuildPreviewContent } from "./build-preview-content"
|
|
5
|
+
import { PackageBuildDetailsPanel } from "./package-build-details-panel"
|
|
6
|
+
import { PackageBuildHeader } from "./package-build-header"
|
|
7
|
+
import { CollapsibleSection } from "./collapsible-section"
|
|
8
|
+
import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
|
|
9
|
+
import { LogContent } from "./LogContent"
|
|
10
|
+
import { PackageRelease } from "fake-snippets-api/lib/db/schema"
|
|
11
|
+
|
|
12
|
+
function computeDuration(
|
|
13
|
+
startedAt: string | null | undefined,
|
|
14
|
+
completedAt: string | null | undefined,
|
|
15
|
+
) {
|
|
16
|
+
if (!startedAt || !completedAt) return ""
|
|
17
|
+
return `${Math.floor((new Date(completedAt).getTime() - new Date(startedAt).getTime()) / 1000)}s`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const PackageBuildDetailsPage = () => {
|
|
21
|
+
const { packageRelease } = useCurrentPackageRelease()
|
|
22
|
+
const [openSections, setOpenSections] = useState<Record<string, boolean>>({})
|
|
23
|
+
|
|
24
|
+
const {
|
|
25
|
+
circuit_json_build_logs,
|
|
26
|
+
circuit_json_build_completed_at,
|
|
27
|
+
circuit_json_build_in_progress,
|
|
28
|
+
circuit_json_build_is_stale,
|
|
29
|
+
circuit_json_build_started_at,
|
|
30
|
+
circuit_json_build_error,
|
|
31
|
+
circuit_json_build_error_last_updated_at,
|
|
32
|
+
transpilation_completed_at,
|
|
33
|
+
transpilation_in_progress,
|
|
34
|
+
transpilation_is_stale,
|
|
35
|
+
transpilation_logs,
|
|
36
|
+
transpilation_started_at,
|
|
37
|
+
circuit_json_build_display_status,
|
|
38
|
+
transpilation_display_status,
|
|
39
|
+
transpilation_error,
|
|
40
|
+
} = packageRelease ?? ({} as Partial<PackageRelease>)
|
|
41
|
+
|
|
42
|
+
const toggleSection = (section: string) => {
|
|
43
|
+
setOpenSections((prev) => ({
|
|
44
|
+
...prev,
|
|
45
|
+
[section]: !prev[section],
|
|
46
|
+
}))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="min-h-screen bg-gray-50 text-gray-900">
|
|
51
|
+
<PackageBuildHeader />
|
|
52
|
+
|
|
53
|
+
<div className="px-6 py-6 container mx-auto">
|
|
54
|
+
{/* Main Content */}
|
|
55
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8 items-start">
|
|
56
|
+
{/* Preview Section */}
|
|
57
|
+
<div className="lg:col-span-2">
|
|
58
|
+
<div className="bg-white border border-gray-200 rounded-lg p-4 flex items-center justify-center max-h-[420px]">
|
|
59
|
+
<BuildPreviewContent />
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
{/* Details Panel */}
|
|
64
|
+
<PackageBuildDetailsPanel />
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
{/* Collapsible Sections */}
|
|
68
|
+
<div className="space-y-4 mb-8">
|
|
69
|
+
<CollapsibleSection
|
|
70
|
+
title="Transpilation Logs"
|
|
71
|
+
duration={computeDuration(
|
|
72
|
+
transpilation_started_at,
|
|
73
|
+
transpilation_completed_at,
|
|
74
|
+
)}
|
|
75
|
+
displayStatus={transpilation_display_status}
|
|
76
|
+
error={transpilation_error}
|
|
77
|
+
isOpen={openSections.summary}
|
|
78
|
+
onToggle={() => toggleSection("summary")}
|
|
79
|
+
>
|
|
80
|
+
<LogContent
|
|
81
|
+
logs={
|
|
82
|
+
packageRelease?.transpilation_logs ?? [
|
|
83
|
+
{ msg: "No transpilation logs available" },
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
error={transpilation_error}
|
|
87
|
+
/>
|
|
88
|
+
</CollapsibleSection>
|
|
89
|
+
|
|
90
|
+
<CollapsibleSection
|
|
91
|
+
title="Circuit JSON Build Logs"
|
|
92
|
+
duration={computeDuration(
|
|
93
|
+
circuit_json_build_started_at,
|
|
94
|
+
circuit_json_build_completed_at,
|
|
95
|
+
)}
|
|
96
|
+
displayStatus={circuit_json_build_display_status}
|
|
97
|
+
error={circuit_json_build_error}
|
|
98
|
+
isOpen={openSections.logs}
|
|
99
|
+
onToggle={() => toggleSection("logs")}
|
|
100
|
+
>
|
|
101
|
+
<LogContent
|
|
102
|
+
logs={
|
|
103
|
+
packageRelease?.circuit_json_build_logs ?? [
|
|
104
|
+
{ msg: "No Circuit JSON logs available" },
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
error={circuit_json_build_error!}
|
|
108
|
+
/>
|
|
109
|
+
</CollapsibleSection>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
@@ -1,11 +1,27 @@
|
|
|
1
|
+
import { useCurrentPackageInfo } from "@/hooks/use-current-package-info"
|
|
2
|
+
import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
|
|
3
|
+
|
|
1
4
|
export function BuildPreviewContent() {
|
|
5
|
+
const { packageRelease } = useCurrentPackageRelease()
|
|
6
|
+
const { packageInfo } = useCurrentPackageInfo()
|
|
7
|
+
|
|
8
|
+
if (!packageRelease) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="flex items-center justify-center">
|
|
11
|
+
<div className="w-48 h-48 bg-gray-200 rounded animate-pulse-slow"></div>
|
|
12
|
+
</div>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
2
16
|
return (
|
|
3
17
|
<div className="flex items-center justify-center">
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
18
|
+
<div className="rounded overflow-hidden">
|
|
19
|
+
<img
|
|
20
|
+
src={`https://api.tscircuit.com/packages/images/${packageInfo?.name}/pcb.png`}
|
|
21
|
+
alt="Package build preview"
|
|
22
|
+
className="object-contain rounded max-h-[360px]"
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
9
25
|
</div>
|
|
10
26
|
)
|
|
11
27
|
}
|
|
@@ -6,24 +6,34 @@ import {
|
|
|
6
6
|
CollapsibleContent,
|
|
7
7
|
CollapsibleTrigger,
|
|
8
8
|
} from "@/components/ui/collapsible"
|
|
9
|
+
import { getColorForDisplayStatus } from "./getColorForDisplayStatus"
|
|
10
|
+
import { PackageRelease } from "fake-snippets-api/lib/db/schema"
|
|
11
|
+
import { ErrorObjectOrString, getErrorText } from "./ErrorObject"
|
|
12
|
+
import { capitalCase } from "./capitalCase"
|
|
13
|
+
|
|
14
|
+
type BadgeInfo = {
|
|
15
|
+
text: string
|
|
16
|
+
variant?: "default" | "secondary" | "destructive"
|
|
17
|
+
className?: string
|
|
18
|
+
icon?: React.ReactNode
|
|
19
|
+
}
|
|
9
20
|
|
|
10
21
|
interface CollapsibleSectionProps {
|
|
11
22
|
title: string
|
|
12
23
|
duration?: string
|
|
24
|
+
error?: ErrorObjectOrString | null
|
|
25
|
+
displayStatus?: PackageRelease["display_status"]
|
|
13
26
|
isOpen: boolean
|
|
14
27
|
onToggle: () => void
|
|
15
|
-
badges?: Array<
|
|
16
|
-
text: string
|
|
17
|
-
icon?: React.ReactNode
|
|
18
|
-
variant?: "default" | "secondary"
|
|
19
|
-
className?: string
|
|
20
|
-
}>
|
|
28
|
+
badges?: Array<BadgeInfo>
|
|
21
29
|
children?: React.ReactNode
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
export function CollapsibleSection({
|
|
25
33
|
title,
|
|
26
34
|
duration,
|
|
35
|
+
error,
|
|
36
|
+
displayStatus,
|
|
27
37
|
isOpen,
|
|
28
38
|
onToggle,
|
|
29
39
|
badges = [],
|
|
@@ -40,7 +50,17 @@ export function CollapsibleSection({
|
|
|
40
50
|
<span className="font-medium">{title}</span>
|
|
41
51
|
</div>
|
|
42
52
|
<div className="flex items-center gap-2">
|
|
43
|
-
{
|
|
53
|
+
{[
|
|
54
|
+
...badges,
|
|
55
|
+
...(error
|
|
56
|
+
? [
|
|
57
|
+
{
|
|
58
|
+
text: getErrorText(error),
|
|
59
|
+
variant: "destructive",
|
|
60
|
+
} as BadgeInfo,
|
|
61
|
+
]
|
|
62
|
+
: []),
|
|
63
|
+
].map((badge, index) => (
|
|
44
64
|
<Badge
|
|
45
65
|
key={index}
|
|
46
66
|
variant={badge.variant || "secondary"}
|
|
@@ -56,13 +76,18 @@ export function CollapsibleSection({
|
|
|
56
76
|
{duration && (
|
|
57
77
|
<span className="text-sm text-gray-600">{duration}</span>
|
|
58
78
|
)}
|
|
59
|
-
<
|
|
79
|
+
<div
|
|
80
|
+
className={`w-2 h-2 rounded-lg ${getColorForDisplayStatus(displayStatus)}`}
|
|
81
|
+
/>
|
|
82
|
+
<div className="text-gray-600 text-xs font-medium">
|
|
83
|
+
{capitalCase(displayStatus) || "???"}
|
|
84
|
+
</div>
|
|
60
85
|
</div>
|
|
61
86
|
</div>
|
|
62
87
|
</CollapsibleTrigger>
|
|
63
88
|
<CollapsibleContent>
|
|
64
89
|
<div className="p-4 bg-white border-x border-b border-gray-200 rounded-b-lg">
|
|
65
|
-
{children
|
|
90
|
+
{children}
|
|
66
91
|
</div>
|
|
67
92
|
</CollapsibleContent>
|
|
68
93
|
</Collapsible>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PackageRelease } from "fake-snippets-api/lib/db/schema"
|
|
2
|
+
|
|
3
|
+
export const getColorForDisplayStatus = (
|
|
4
|
+
display_status?: PackageRelease["display_status"] | null,
|
|
5
|
+
) => {
|
|
6
|
+
switch (display_status) {
|
|
7
|
+
case "pending":
|
|
8
|
+
return "bg-yellow-500"
|
|
9
|
+
case "building":
|
|
10
|
+
return "bg-blue-500"
|
|
11
|
+
case "successful":
|
|
12
|
+
return "bg-green-500"
|
|
13
|
+
case "failed":
|
|
14
|
+
return "bg-red-500"
|
|
15
|
+
}
|
|
16
|
+
return "bg-gray-500"
|
|
17
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Globe, GitBranch, GitCommit, Clock } from "lucide-react"
|
|
2
|
+
import { Badge } from "@/components/ui/badge"
|
|
3
|
+
import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
|
|
4
|
+
import { useParams } from "wouter"
|
|
5
|
+
import { timeAgo } from "@/lib/utils/timeAgo"
|
|
6
|
+
import { PackageRelease } from "fake-snippets-api/lib/db/schema"
|
|
7
|
+
|
|
8
|
+
const capitalCase = (str: string) => {
|
|
9
|
+
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getColorFromDisplayStatus(
|
|
13
|
+
display_status: PackageRelease["display_status"],
|
|
14
|
+
) {
|
|
15
|
+
switch (display_status) {
|
|
16
|
+
case "pending":
|
|
17
|
+
return "bg-yellow-500"
|
|
18
|
+
case "building":
|
|
19
|
+
return "bg-blue-500"
|
|
20
|
+
case "successful":
|
|
21
|
+
return "bg-green-500"
|
|
22
|
+
case "failed":
|
|
23
|
+
return "bg-red-500"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function PackageBuildDetailsPanel() {
|
|
28
|
+
const { packageRelease } = useCurrentPackageRelease()
|
|
29
|
+
const { author } = useParams() // TODO use packageRelease.author_account_id when it's added by backed
|
|
30
|
+
|
|
31
|
+
if (!packageRelease) {
|
|
32
|
+
// TODO show skeleton instead
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
circuit_json_build_display_status,
|
|
38
|
+
circuit_json_build_in_progress,
|
|
39
|
+
circuit_json_build_is_stale,
|
|
40
|
+
circuit_json_build_logs,
|
|
41
|
+
transpilation_display_status,
|
|
42
|
+
transpilation_in_progress,
|
|
43
|
+
transpilation_logs,
|
|
44
|
+
circuit_json_build_completed_at,
|
|
45
|
+
transpilation_is_stale,
|
|
46
|
+
display_status,
|
|
47
|
+
created_at,
|
|
48
|
+
has_transpiled,
|
|
49
|
+
circuit_json_build_started_at,
|
|
50
|
+
circuit_json_build_error,
|
|
51
|
+
circuit_json_build_error_last_updated_at,
|
|
52
|
+
total_build_duration_ms,
|
|
53
|
+
transpilation_completed_at,
|
|
54
|
+
transpilation_error,
|
|
55
|
+
transpilation_started_at,
|
|
56
|
+
commit_sha,
|
|
57
|
+
} = packageRelease
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className="space-y-6 bg-white p-4 border border-gray-200 rounded-lg">
|
|
61
|
+
{/* Created */}
|
|
62
|
+
<div>
|
|
63
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Created</h3>
|
|
64
|
+
<div className="flex items-center gap-2">
|
|
65
|
+
<div className="w-6 h-6 bg-orange-500 rounded-full flex items-center justify-center text-xs font-bold">
|
|
66
|
+
I
|
|
67
|
+
</div>
|
|
68
|
+
<span className="text-sm">{author}</span>
|
|
69
|
+
<span className="text-sm text-gray-500">
|
|
70
|
+
{timeAgo(packageRelease?.created_at, "")}
|
|
71
|
+
</span>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
{/* Status */}
|
|
76
|
+
<div>
|
|
77
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Status</h3>
|
|
78
|
+
<div className="flex items-center gap-2">
|
|
79
|
+
<div
|
|
80
|
+
className={`w-2 h-2 ${getColorFromDisplayStatus(display_status)} rounded-full`}
|
|
81
|
+
></div>
|
|
82
|
+
<span className="text-sm">{capitalCase(display_status)}</span>
|
|
83
|
+
{/* <Badge
|
|
84
|
+
variant="secondary"
|
|
85
|
+
className="bg-gray-200 text-gray-700 text-xs"
|
|
86
|
+
>
|
|
87
|
+
Latest
|
|
88
|
+
</Badge> */}
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div>
|
|
93
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Build Time</h3>
|
|
94
|
+
<div className="flex items-center gap-2">
|
|
95
|
+
<Clock className="w-4 h-4 text-gray-500" />
|
|
96
|
+
{circuit_json_build_completed_at && (
|
|
97
|
+
<span className="text-sm">
|
|
98
|
+
{total_build_duration_ms
|
|
99
|
+
? `${Math.floor(total_build_duration_ms / 1000)}s`
|
|
100
|
+
: ""}
|
|
101
|
+
</span>
|
|
102
|
+
)}
|
|
103
|
+
<span className="text-sm text-gray-500">
|
|
104
|
+
{timeAgo(circuit_json_build_completed_at, "waiting...")}
|
|
105
|
+
</span>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
{/* Version */}
|
|
110
|
+
<div>
|
|
111
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Version</h3>
|
|
112
|
+
<div className="flex items-center gap-2">
|
|
113
|
+
<Globe className="w-4 h-4 text-gray-500" />
|
|
114
|
+
<span className="text-sm">{packageRelease.version}</span>
|
|
115
|
+
{/* <Badge variant="default" className="bg-blue-600 text-white text-xs">
|
|
116
|
+
Current
|
|
117
|
+
</Badge> */}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{/* Outputs */}
|
|
122
|
+
<div>
|
|
123
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Outputs</h3>
|
|
124
|
+
<div>
|
|
125
|
+
<span className="text-sm text-gray-400">None</span>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
{/* Source */}
|
|
130
|
+
{/* <div>
|
|
131
|
+
<h3 className="text-sm font-medium text-gray-600 mb-2">Source</h3>
|
|
132
|
+
<div className="space-y-2">
|
|
133
|
+
<div className="flex items-center gap-2">
|
|
134
|
+
<GitBranch className="w-4 h-4 text-gray-500" />
|
|
135
|
+
<span className="text-sm">main</span>
|
|
136
|
+
</div>
|
|
137
|
+
<div className="flex items-center gap-2">
|
|
138
|
+
<GitCommit className="w-4 h-4 text-gray-500" />
|
|
139
|
+
<span className="text-sm text-gray-500">
|
|
140
|
+
edfdc67 support empty file creation (#356)
|
|
141
|
+
</span>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div> */}
|
|
145
|
+
</div>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChevronDown, Download, Github, Link } from "lucide-react"
|
|
1
|
+
import { ChevronDown, Download, Github, Link, RefreshCw } from "lucide-react"
|
|
2
2
|
import { Button } from "@/components/ui/button"
|
|
3
3
|
import {
|
|
4
4
|
DropdownMenu,
|
|
@@ -7,9 +7,12 @@ import {
|
|
|
7
7
|
DropdownMenuTrigger,
|
|
8
8
|
} from "@/components/ui/dropdown-menu"
|
|
9
9
|
import { useParams } from "wouter"
|
|
10
|
+
import { useCurrentPackageRelease } from "@/hooks/use-current-package-release"
|
|
11
|
+
import { DownloadButtonAndMenu } from "../DownloadButtonAndMenu"
|
|
10
12
|
|
|
11
|
-
export function
|
|
13
|
+
export function PackageBuildHeader() {
|
|
12
14
|
const { author, packageName } = useParams()
|
|
15
|
+
const { packageRelease } = useCurrentPackageRelease()
|
|
13
16
|
|
|
14
17
|
return (
|
|
15
18
|
<div className="border-b border-gray-200 bg-white px-6 py-4">
|
|
@@ -39,35 +42,17 @@ export function DeploymentHeader() {
|
|
|
39
42
|
Report Issue
|
|
40
43
|
</a>
|
|
41
44
|
</Button>
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<DropdownMenuContent
|
|
54
|
-
align="end"
|
|
55
|
-
className="bg-white border-gray-200"
|
|
56
|
-
>
|
|
57
|
-
<DropdownMenuItem className="text-gray-900 hover:bg-gray-100">
|
|
58
|
-
Circuit JSON
|
|
59
|
-
</DropdownMenuItem>
|
|
60
|
-
<DropdownMenuItem className="text-gray-900 hover:bg-gray-100">
|
|
61
|
-
PCB SVG
|
|
62
|
-
</DropdownMenuItem>
|
|
63
|
-
<DropdownMenuItem className="text-gray-900 hover:bg-gray-100">
|
|
64
|
-
Schematic SVG
|
|
65
|
-
</DropdownMenuItem>
|
|
66
|
-
<DropdownMenuItem className="text-gray-900 hover:bg-gray-100">
|
|
67
|
-
3D Model (stl)
|
|
68
|
-
</DropdownMenuItem>
|
|
69
|
-
</DropdownMenuContent>
|
|
70
|
-
</DropdownMenu>
|
|
45
|
+
<Button
|
|
46
|
+
variant="outline"
|
|
47
|
+
size="sm"
|
|
48
|
+
className="border-gray-300 bg-white hover:bg-gray-50 text-xs sm:text-sm"
|
|
49
|
+
>
|
|
50
|
+
<RefreshCw className="w-3 h-3 sm:w-4 sm:h-4 mr-1 sm:mr-2" />
|
|
51
|
+
Rebuild
|
|
52
|
+
</Button>
|
|
53
|
+
<DownloadButtonAndMenu
|
|
54
|
+
snippetUnscopedName={`${author}/${packageName}`}
|
|
55
|
+
/>
|
|
71
56
|
</div>
|
|
72
57
|
</div>
|
|
73
58
|
</div>
|
|
@@ -79,7 +79,7 @@ export default function MainContentHeader({
|
|
|
79
79
|
<DropdownMenuTrigger asChild>
|
|
80
80
|
<Button
|
|
81
81
|
size="sm"
|
|
82
|
-
className="
|
|
82
|
+
className="bg-green-600 hover:bg-green-700 dark:bg-[#238636] dark:hover:bg-[#2ea043] text-white"
|
|
83
83
|
>
|
|
84
84
|
<CodeIcon className="h-4 w-4 mr-1.5" />
|
|
85
85
|
Code
|
|
@@ -76,7 +76,6 @@ export default function PackageHeader({
|
|
|
76
76
|
window.TSCIRCUIT_REGISTRY_API_BASE_URL =
|
|
77
77
|
import.meta.env.VITE_TSCIRCUIT_REGISTRY_API_URL ??
|
|
78
78
|
`${window.location.origin}/api`
|
|
79
|
-
window.TSCIRCUIT_REGISTRY_TOKEN = sessionToken ?? ""
|
|
80
79
|
// TODO: replace with production stripe checkout base url
|
|
81
80
|
window.TSCIRCUIT_STRIPE_CHECKOUT_BASE_URL =
|
|
82
81
|
import.meta.env.VITE_TSCIRCUIT_STRIPE_CHECKOUT_BASE_URL
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useState } from "react"
|
|
2
|
+
import { Button } from "../ui/button"
|
|
3
|
+
import {
|
|
4
|
+
Dialog,
|
|
5
|
+
DialogContent,
|
|
6
|
+
DialogHeader,
|
|
7
|
+
DialogTitle,
|
|
8
|
+
DialogFooter,
|
|
9
|
+
} from "../ui/dialog"
|
|
10
|
+
import { Label } from "../ui/label"
|
|
11
|
+
import {
|
|
12
|
+
Select,
|
|
13
|
+
SelectContent,
|
|
14
|
+
SelectItem,
|
|
15
|
+
SelectTrigger,
|
|
16
|
+
SelectValue,
|
|
17
|
+
} from "../ui/select"
|
|
18
|
+
import { Checkbox } from "../ui/checkbox"
|
|
19
|
+
import { AnyCircuitElement } from "circuit-json"
|
|
20
|
+
import { createUseDialog } from "./create-use-dialog"
|
|
21
|
+
import {
|
|
22
|
+
downloadPcbSvg,
|
|
23
|
+
DownloadPcbSvgOptions,
|
|
24
|
+
} from "@/lib/download-fns/download-pcb-svg"
|
|
25
|
+
|
|
26
|
+
interface PcbDownloadDialogProps {
|
|
27
|
+
open: boolean
|
|
28
|
+
onOpenChange: (open: boolean) => void
|
|
29
|
+
circuitJson: AnyCircuitElement[]
|
|
30
|
+
fileName: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const PcbDownloadDialog = ({
|
|
34
|
+
open,
|
|
35
|
+
onOpenChange,
|
|
36
|
+
circuitJson,
|
|
37
|
+
fileName,
|
|
38
|
+
}: PcbDownloadDialogProps) => {
|
|
39
|
+
const [layer, setLayer] = useState<"all" | "top" | "bottom">("all")
|
|
40
|
+
const [drawPadding, setDrawPadding] = useState(true)
|
|
41
|
+
const [transparentBg, setTransparentBg] = useState(false)
|
|
42
|
+
const [matchAspectRatio, setMatchAspectRatio] = useState(false)
|
|
43
|
+
|
|
44
|
+
const handleDownload = () => {
|
|
45
|
+
const options: DownloadPcbSvgOptions = {
|
|
46
|
+
layer,
|
|
47
|
+
drawPaddingOutsideBoard: drawPadding,
|
|
48
|
+
backgroundColor: transparentBg ? "transparent" : "#000",
|
|
49
|
+
matchAspectRatio,
|
|
50
|
+
}
|
|
51
|
+
downloadPcbSvg(circuitJson, fileName, options)
|
|
52
|
+
onOpenChange(false)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
57
|
+
<DialogContent>
|
|
58
|
+
<DialogHeader>
|
|
59
|
+
<DialogTitle>Download PCB SVG</DialogTitle>
|
|
60
|
+
</DialogHeader>
|
|
61
|
+
<div className="space-y-4 py-2">
|
|
62
|
+
<div className="grid grid-cols-4 items-center gap-4">
|
|
63
|
+
<Label htmlFor="layer" className="text-right">
|
|
64
|
+
Layer
|
|
65
|
+
</Label>
|
|
66
|
+
<Select value={layer} onValueChange={(v) => setLayer(v as any)}>
|
|
67
|
+
<SelectTrigger id="layer" className="col-span-3">
|
|
68
|
+
<SelectValue placeholder="Layer" />
|
|
69
|
+
</SelectTrigger>
|
|
70
|
+
<SelectContent className="!z-[999]">
|
|
71
|
+
<SelectItem value="all">All</SelectItem>
|
|
72
|
+
<SelectItem value="top">Top</SelectItem>
|
|
73
|
+
<SelectItem value="bottom">Bottom</SelectItem>
|
|
74
|
+
</SelectContent>
|
|
75
|
+
</Select>
|
|
76
|
+
</div>
|
|
77
|
+
<div className="flex items-center space-x-2">
|
|
78
|
+
<Checkbox
|
|
79
|
+
id="padding"
|
|
80
|
+
checked={drawPadding}
|
|
81
|
+
onCheckedChange={(v) => setDrawPadding(Boolean(v))}
|
|
82
|
+
/>
|
|
83
|
+
<Label htmlFor="padding">Draw Padding and Board Outline</Label>
|
|
84
|
+
</div>
|
|
85
|
+
<div className="flex items-center space-x-2">
|
|
86
|
+
<Checkbox
|
|
87
|
+
id="transparentBg"
|
|
88
|
+
checked={transparentBg}
|
|
89
|
+
onCheckedChange={(v) => setTransparentBg(Boolean(v))}
|
|
90
|
+
/>
|
|
91
|
+
<Label htmlFor="transparentBg">Transparent Background</Label>
|
|
92
|
+
</div>
|
|
93
|
+
<div className="flex items-center space-x-2">
|
|
94
|
+
<Checkbox
|
|
95
|
+
id="matchAspectRatio"
|
|
96
|
+
checked={matchAspectRatio}
|
|
97
|
+
onCheckedChange={(v) => setMatchAspectRatio(Boolean(v))}
|
|
98
|
+
/>
|
|
99
|
+
<Label htmlFor="matchAspectRatio">Match Aspect Ratio</Label>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
<DialogFooter>
|
|
103
|
+
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
|
104
|
+
Cancel
|
|
105
|
+
</Button>
|
|
106
|
+
<Button onClick={handleDownload}>Download</Button>
|
|
107
|
+
</DialogFooter>
|
|
108
|
+
</DialogContent>
|
|
109
|
+
</Dialog>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const usePcbDownloadDialog = createUseDialog(PcbDownloadDialog)
|