@tscircuit/fake-snippets 0.0.81 → 0.0.83
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_releases/create.test.ts +3 -3
- package/dist/bundle.js +41 -13
- package/dist/index.d.ts +24 -4
- package/dist/index.js +5 -1
- package/dist/schema.d.ts +37 -5
- package/dist/schema.js +5 -1
- package/fake-snippets-api/lib/db/schema.ts +5 -1
- package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +14 -1
- package/fake-snippets-api/routes/api/package_releases/get.ts +11 -3
- package/fake-snippets-api/routes/api/package_releases/list.ts +8 -1
- package/fake-snippets-api/routes/api/packages/generate_from_jlcpcb.ts +3 -3
- package/package.json +1 -1
- package/src/App.tsx +0 -2
- package/src/components/JLCPCBImportDialog.tsx +164 -62
- package/src/components/PackageBuildsPage/LogContent.tsx +12 -5
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +8 -7
- package/src/components/PackageBuildsPage/build-preview-content.tsx +1 -1
- package/src/components/PackageBuildsPage/collapsible-section.tsx +14 -46
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +28 -10
- package/src/components/PackageBuildsPage/package-build-header.tsx +16 -4
- package/src/components/ViewPackagePage/components/build-status.tsx +24 -85
- package/src/components/ViewPackagePage/components/important-files-view.tsx +8 -1
- package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +28 -5
- package/src/components/ViewPackagePage/hooks/use-toast.tsx +70 -0
- package/src/components/dialogs/{import-snippet-dialog.tsx → import-package-dialog.tsx} +25 -24
- package/src/components/package-port/CodeEditor.tsx +9 -1
- package/src/components/package-port/CodeEditorHeader.tsx +7 -6
- package/src/components/ui/toaster.tsx +1 -33
- package/src/hooks/use-current-package-release.ts +14 -3
- package/src/hooks/use-now.ts +12 -0
- package/src/hooks/use-package-release.ts +17 -15
- package/src/hooks/use-toast.tsx +50 -169
- package/src/pages/dashboard.tsx +3 -1
- package/src/pages/user-profile.tsx +9 -2
- package/src/pages/view-package.tsx +1 -0
- package/.github/workflows/formatbot.yml +0 -63
- package/src/components/ViewPackagePage/hooks/use-toast.ts +0 -191
|
@@ -1,99 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
Dialog,
|
|
4
|
-
DialogContent,
|
|
5
|
-
DialogHeader,
|
|
6
|
-
DialogTitle,
|
|
7
|
-
} from "@/components/ui/dialog"
|
|
8
|
-
import { CheckCircle, XCircle, Check, X } from "lucide-react"
|
|
9
|
-
import { cn } from "@/lib/utils"
|
|
1
|
+
import { CheckCircle, XCircle, Clock, Loader2 } from "lucide-react"
|
|
2
|
+
import { Link, useParams } from "wouter"
|
|
10
3
|
|
|
11
4
|
export interface BuildStep {
|
|
12
5
|
id: string
|
|
13
6
|
name: string
|
|
14
|
-
status: "success" | "
|
|
15
|
-
message?: string
|
|
7
|
+
status: "pending" | "running" | "success" | "error"
|
|
16
8
|
}
|
|
17
9
|
|
|
18
10
|
export interface BuildStatusProps {
|
|
19
11
|
step: BuildStep
|
|
12
|
+
packageReleaseId: string
|
|
20
13
|
}
|
|
21
14
|
|
|
22
|
-
export const BuildStatus = ({ step }: BuildStatusProps) => {
|
|
23
|
-
const
|
|
15
|
+
export const BuildStatus = ({ step, packageReleaseId }: BuildStatusProps) => {
|
|
16
|
+
const { author, packageName } = useParams()
|
|
17
|
+
const href = `/${author}/${packageName}/builds?package_release_id=${packageReleaseId}`
|
|
24
18
|
|
|
25
19
|
return (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<DialogHeader>
|
|
44
|
-
<DialogTitle className="flex items-center gap-2">
|
|
45
|
-
{step.status === "success" ? (
|
|
46
|
-
<>
|
|
47
|
-
<CheckCircle className="h-5 w-5 text-green-600" />
|
|
48
|
-
<span>Build Status: Passing</span>
|
|
49
|
-
</>
|
|
50
|
-
) : (
|
|
51
|
-
<>
|
|
52
|
-
<XCircle className="h-5 w-5 text-red-600" />
|
|
53
|
-
<span>Build Status: Failing</span>
|
|
54
|
-
</>
|
|
55
|
-
)}
|
|
56
|
-
</DialogTitle>
|
|
57
|
-
</DialogHeader>
|
|
58
|
-
|
|
59
|
-
<div className="space-y-4">
|
|
60
|
-
<div className="space-y-3">
|
|
61
|
-
<div
|
|
62
|
-
key={step.id}
|
|
63
|
-
className={cn(
|
|
64
|
-
"flex items-start gap-3 rounded-md border p-3",
|
|
65
|
-
step.status === "success"
|
|
66
|
-
? "bg-green-50 border-green-200"
|
|
67
|
-
: "bg-red-50 border-red-200",
|
|
68
|
-
)}
|
|
69
|
-
>
|
|
70
|
-
<div
|
|
71
|
-
className={cn(
|
|
72
|
-
"rounded-full p-1 mt-0.5",
|
|
73
|
-
step.status === "success"
|
|
74
|
-
? "bg-green-100 text-green-600"
|
|
75
|
-
: "bg-red-100 text-red-600",
|
|
76
|
-
)}
|
|
77
|
-
>
|
|
78
|
-
{step.status === "success" ? (
|
|
79
|
-
<Check className="h-4 w-4" />
|
|
80
|
-
) : (
|
|
81
|
-
<X className="h-4 w-4" />
|
|
82
|
-
)}
|
|
83
|
-
</div>
|
|
84
|
-
<div>
|
|
85
|
-
<div className="font-medium">{step.name}</div>
|
|
86
|
-
{step.message && (
|
|
87
|
-
<div className="text-sm text-muted-foreground mt-1">
|
|
88
|
-
{step.message}
|
|
89
|
-
</div>
|
|
90
|
-
)}
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
</DialogContent>
|
|
96
|
-
</Dialog>
|
|
97
|
-
</>
|
|
20
|
+
<Link href={href} className="flex items-center gap-2">
|
|
21
|
+
{step.status === "success" && (
|
|
22
|
+
<CheckCircle className="h-4 w-4 text-green-600 dark:text-[#8b949e]" />
|
|
23
|
+
)}
|
|
24
|
+
{step.status === "error" && (
|
|
25
|
+
<XCircle className="h-4 w-4 text-red-600 dark:text-[#8b949e]" />
|
|
26
|
+
)}
|
|
27
|
+
{step.status === "running" && (
|
|
28
|
+
<Loader2 className="h-4 w-4 text-blue-600 animate-spin dark:text-[#8b949e]" />
|
|
29
|
+
)}
|
|
30
|
+
{step.status === "pending" && (
|
|
31
|
+
<Clock className="h-4 w-4 text-yellow-600 dark:text-[#8b949e]" />
|
|
32
|
+
)}
|
|
33
|
+
<span className="text-sm text-gray-500 dark:text-[#8b949e]">
|
|
34
|
+
{step.name}
|
|
35
|
+
</span>
|
|
36
|
+
</Link>
|
|
98
37
|
)
|
|
99
38
|
}
|
|
@@ -51,8 +51,13 @@ export default function ImportantFilesView({
|
|
|
51
51
|
const hasAiContent = Boolean(aiDescription || aiUsageInstructions)
|
|
52
52
|
const hasAiReview = Boolean(aiReviewText)
|
|
53
53
|
|
|
54
|
-
// Select the appropriate tab/file when content changes
|
|
54
|
+
// Select the appropriate tab/file when content changes. Once the user has
|
|
55
|
+
// interacted with the tabs we keep their selection and only run this logic
|
|
56
|
+
// if no tab has been chosen yet.
|
|
55
57
|
useEffect(() => {
|
|
58
|
+
if (activeTab !== null) return
|
|
59
|
+
if (isLoading) return
|
|
60
|
+
|
|
56
61
|
// First priority: README file if it exists
|
|
57
62
|
const readmeFile = importantFiles.find(
|
|
58
63
|
(file) =>
|
|
@@ -82,6 +87,8 @@ export default function ImportantFilesView({
|
|
|
82
87
|
hasAiContent,
|
|
83
88
|
hasAiReview,
|
|
84
89
|
importantFiles,
|
|
90
|
+
activeTab,
|
|
91
|
+
isLoading,
|
|
85
92
|
])
|
|
86
93
|
|
|
87
94
|
// Get file name from path
|
|
@@ -4,6 +4,27 @@ import { useCurrentPackageInfo } from "@/hooks/use-current-package-info"
|
|
|
4
4
|
import { usePackageReleaseById } from "@/hooks/use-package-release"
|
|
5
5
|
import { timeAgo } from "@/lib/utils/timeAgo"
|
|
6
6
|
import { BuildStatus, BuildStep } from "./build-status"
|
|
7
|
+
import type { PackageRelease } from "fake-snippets-api/lib/db/schema"
|
|
8
|
+
|
|
9
|
+
function getTranspilationStatus(
|
|
10
|
+
pr?: PackageRelease | null,
|
|
11
|
+
): BuildStep["status"] {
|
|
12
|
+
if (!pr) return "pending"
|
|
13
|
+
if (pr.transpilation_error) return "error"
|
|
14
|
+
if (pr.transpilation_in_progress) return "running"
|
|
15
|
+
if (pr.transpilation_completed_at) return "success"
|
|
16
|
+
if (pr.transpilation_started_at) return "running"
|
|
17
|
+
return "pending"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getCircuitJsonStatus(pr?: PackageRelease | null): BuildStep["status"] {
|
|
21
|
+
if (!pr) return "pending"
|
|
22
|
+
if (pr.circuit_json_build_error) return "error"
|
|
23
|
+
if (pr.circuit_json_build_in_progress) return "running"
|
|
24
|
+
if (pr.circuit_json_build_completed_at) return "success"
|
|
25
|
+
if (pr.circuit_json_build_started_at) return "running"
|
|
26
|
+
return "pending"
|
|
27
|
+
}
|
|
7
28
|
|
|
8
29
|
export default function SidebarReleasesSection() {
|
|
9
30
|
const { packageInfo } = useCurrentPackageInfo()
|
|
@@ -15,14 +36,12 @@ export default function SidebarReleasesSection() {
|
|
|
15
36
|
{
|
|
16
37
|
id: "package_transpilation",
|
|
17
38
|
name: "Package Transpilation",
|
|
18
|
-
status: packageRelease
|
|
19
|
-
message: packageRelease?.transpilation_error || undefined,
|
|
39
|
+
status: getTranspilationStatus(packageRelease),
|
|
20
40
|
},
|
|
21
41
|
{
|
|
22
42
|
id: "circuit_json_build",
|
|
23
43
|
name: "Circuit JSON Build",
|
|
24
|
-
status: packageRelease
|
|
25
|
-
message: packageRelease?.circuit_json_build_error || undefined,
|
|
44
|
+
status: getCircuitJsonStatus(packageRelease),
|
|
26
45
|
},
|
|
27
46
|
]
|
|
28
47
|
|
|
@@ -56,7 +75,11 @@ export default function SidebarReleasesSection() {
|
|
|
56
75
|
</span>
|
|
57
76
|
</div>
|
|
58
77
|
{buildSteps.map((step) => (
|
|
59
|
-
<BuildStatus
|
|
78
|
+
<BuildStatus
|
|
79
|
+
key={step.id}
|
|
80
|
+
step={step}
|
|
81
|
+
packageReleaseId={packageRelease.package_release_id}
|
|
82
|
+
/>
|
|
60
83
|
))}
|
|
61
84
|
</div>
|
|
62
85
|
{/* <a href="#" className="text-blue-600 dark:text-[#58a6ff] hover:underline text-sm">
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import toastLibrary, { Toaster, type Toast } from "react-hot-toast"
|
|
3
|
+
import React from "react"
|
|
4
|
+
|
|
5
|
+
export interface ToasterToast {
|
|
6
|
+
title?: React.ReactNode
|
|
7
|
+
description?: React.ReactNode
|
|
8
|
+
variant?: "default" | "destructive"
|
|
9
|
+
duration?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function ToastContent({
|
|
13
|
+
title,
|
|
14
|
+
description,
|
|
15
|
+
variant,
|
|
16
|
+
t,
|
|
17
|
+
}: ToasterToast & { t: Toast }) {
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
className={`rounded-md border p-4 shadow-lg transition-all ${
|
|
21
|
+
t.visible
|
|
22
|
+
? "animate-in fade-in slide-in-from-top-full"
|
|
23
|
+
: "animate-out fade-out slide-out-to-right-full"
|
|
24
|
+
} ${
|
|
25
|
+
variant === "destructive"
|
|
26
|
+
? "border-red-500 bg-red-500 text-slate-50"
|
|
27
|
+
: "border-slate-200 bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50"
|
|
28
|
+
}`}
|
|
29
|
+
>
|
|
30
|
+
{title && <div className="text-sm font-semibold">{title}</div>}
|
|
31
|
+
{description && <div className="text-sm opacity-90">{description}</div>}
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const toast = ({
|
|
37
|
+
duration,
|
|
38
|
+
description,
|
|
39
|
+
variant = "default",
|
|
40
|
+
title,
|
|
41
|
+
}: ToasterToast) => {
|
|
42
|
+
if (description) {
|
|
43
|
+
return toastLibrary.custom(
|
|
44
|
+
(t) => (
|
|
45
|
+
<ToastContent
|
|
46
|
+
title={title}
|
|
47
|
+
description={description}
|
|
48
|
+
variant={variant}
|
|
49
|
+
t={t}
|
|
50
|
+
/>
|
|
51
|
+
),
|
|
52
|
+
{ duration },
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (variant === "destructive") {
|
|
57
|
+
return toastLibrary.error(<>{title}</>, { duration })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return toastLibrary(<>{title}</>, { duration })
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function useToast() {
|
|
64
|
+
return {
|
|
65
|
+
toast,
|
|
66
|
+
dismiss: toastLibrary.dismiss,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { useToast, toast, Toaster }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useAxios } from "@/hooks/use-axios"
|
|
2
2
|
import { useDebounce } from "@/hooks/use-debounce"
|
|
3
|
-
import type {
|
|
3
|
+
import type { Package } from "fake-snippets-api/lib/db/schema"
|
|
4
4
|
import { useState } from "react"
|
|
5
5
|
import { useQuery } from "react-query"
|
|
6
6
|
import { Button } from "../ui/button"
|
|
@@ -8,25 +8,25 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
|
|
|
8
8
|
import { Input } from "../ui/input"
|
|
9
9
|
import { createUseDialog } from "./create-use-dialog"
|
|
10
10
|
|
|
11
|
-
export const
|
|
11
|
+
export const ImportPackageDialog = ({
|
|
12
12
|
open,
|
|
13
13
|
onOpenChange,
|
|
14
|
-
|
|
14
|
+
onPackageSelected,
|
|
15
15
|
}: {
|
|
16
16
|
open: boolean
|
|
17
17
|
onOpenChange: (open: boolean) => any
|
|
18
|
-
|
|
18
|
+
onPackageSelected: (pkg: Package) => any
|
|
19
19
|
}) => {
|
|
20
20
|
const [searchText, setSearchText] = useState("")
|
|
21
21
|
const debouncedSearch = useDebounce(searchText, 300)
|
|
22
22
|
const axios = useAxios()
|
|
23
23
|
const { data: snippets, isLoading } = useQuery(
|
|
24
|
-
["
|
|
24
|
+
["packageSearch", debouncedSearch],
|
|
25
25
|
async () => {
|
|
26
|
-
const response = await axios.
|
|
27
|
-
|
|
28
|
-
)
|
|
29
|
-
return response.data.
|
|
26
|
+
const response = await axios.post("/packages/search", {
|
|
27
|
+
query: debouncedSearch,
|
|
28
|
+
})
|
|
29
|
+
return response.data.packages
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
enabled: debouncedSearch.length > 0,
|
|
@@ -35,41 +35,42 @@ export const ImportSnippetDialog = ({
|
|
|
35
35
|
|
|
36
36
|
return (
|
|
37
37
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
38
|
-
<DialogContent className="z-[100]">
|
|
38
|
+
<DialogContent className="z-[100] p-4 sm:p-6">
|
|
39
39
|
<DialogHeader>
|
|
40
|
-
<DialogTitle>Import
|
|
40
|
+
<DialogTitle>Import Package</DialogTitle>
|
|
41
41
|
</DialogHeader>
|
|
42
42
|
<Input
|
|
43
|
-
placeholder="Search
|
|
43
|
+
placeholder="Search packages..."
|
|
44
44
|
value={searchText}
|
|
45
45
|
onChange={(e) => setSearchText(e.target.value)}
|
|
46
|
+
className="w-full mb-4"
|
|
46
47
|
/>
|
|
47
48
|
<div className="h-64 overflow-y-auto">
|
|
48
49
|
{isLoading ? (
|
|
49
|
-
<div>Loading...</div>
|
|
50
|
+
<div className="text-center">Loading...</div>
|
|
50
51
|
) : (
|
|
51
52
|
<ul className="w-full">
|
|
52
|
-
{snippets?.map((
|
|
53
|
+
{snippets?.map((pkg: Package) => (
|
|
53
54
|
<li
|
|
54
|
-
className="flex items-center my-
|
|
55
|
-
key={
|
|
55
|
+
className="flex flex-col sm:flex-row items-start sm:items-center my-2 text-sm w-full"
|
|
56
|
+
key={pkg.package_id}
|
|
56
57
|
>
|
|
57
58
|
<a
|
|
58
|
-
href={`/${
|
|
59
|
+
href={`/${pkg.name}`}
|
|
59
60
|
target="_blank"
|
|
60
|
-
className="
|
|
61
|
+
className="text-blue-500 hover:underline cursor-pointer flex-shrink-0 mb-1 sm:mb-0 sm:mr-2"
|
|
61
62
|
>
|
|
62
|
-
{
|
|
63
|
+
{pkg.name}
|
|
63
64
|
</a>
|
|
64
|
-
<div className="text-
|
|
65
|
-
{
|
|
65
|
+
<div className="text-gray-500 flex-grow overflow-hidden text-ellipsis whitespace-nowrap mb-1 sm:mb-0">
|
|
66
|
+
{pkg.description}
|
|
66
67
|
</div>
|
|
67
68
|
<Button
|
|
68
69
|
size="sm"
|
|
69
|
-
className="
|
|
70
|
+
className="flex-shrink-0"
|
|
70
71
|
variant="outline"
|
|
71
72
|
onClick={() => {
|
|
72
|
-
|
|
73
|
+
onPackageSelected(pkg)
|
|
73
74
|
onOpenChange(false)
|
|
74
75
|
}}
|
|
75
76
|
>
|
|
@@ -85,4 +86,4 @@ export const ImportSnippetDialog = ({
|
|
|
85
86
|
)
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
export const
|
|
89
|
+
export const useImportPackageDialog = createUseDialog(ImportPackageDialog)
|
|
@@ -4,7 +4,7 @@ import { autocompletion } from "@codemirror/autocomplete"
|
|
|
4
4
|
import { indentWithTab } from "@codemirror/commands"
|
|
5
5
|
import { javascript } from "@codemirror/lang-javascript"
|
|
6
6
|
import { json } from "@codemirror/lang-json"
|
|
7
|
-
import { EditorState } from "@codemirror/state"
|
|
7
|
+
import { EditorState, Prec } from "@codemirror/state"
|
|
8
8
|
import { Decoration, hoverTooltip, keymap } from "@codemirror/view"
|
|
9
9
|
import { getImportsFromCode } from "@tscircuit/prompt-benchmarks/code-runner-utils"
|
|
10
10
|
import type { ATABootstrapConfig } from "@typescript/ata"
|
|
@@ -237,6 +237,14 @@ export const CodeEditor = ({
|
|
|
237
237
|
currentFile?.endsWith(".json")
|
|
238
238
|
? json()
|
|
239
239
|
: javascript({ typescript: true, jsx: true }),
|
|
240
|
+
Prec.high(
|
|
241
|
+
keymap.of([
|
|
242
|
+
{
|
|
243
|
+
key: "Mod-Enter",
|
|
244
|
+
run: () => true,
|
|
245
|
+
},
|
|
246
|
+
]),
|
|
247
|
+
),
|
|
240
248
|
keymap.of([indentWithTab]),
|
|
241
249
|
EditorState.readOnly.of(readOnly || isSaving),
|
|
242
250
|
EditorView.updateListener.of((update) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useCallback } from "react"
|
|
2
2
|
import { Button } from "@/components/ui/button"
|
|
3
3
|
import { handleManualEditsImportWithSupportForMultipleFiles } from "@/lib/handleManualEditsImportWithSupportForMultipleFiles"
|
|
4
|
-
import {
|
|
4
|
+
import { useImportPackageDialog } from "@/components/dialogs/import-package-dialog"
|
|
5
5
|
import { useToast } from "@/hooks/use-toast"
|
|
6
6
|
import {
|
|
7
7
|
DropdownMenu,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
SelectValue,
|
|
20
20
|
} from "../ui/select"
|
|
21
21
|
import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
|
|
22
|
+
import { Package } from "fake-snippets-api/lib/db/schema"
|
|
22
23
|
|
|
23
24
|
export type FileName = string
|
|
24
25
|
|
|
@@ -39,8 +40,8 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
|
|
|
39
40
|
handleFileChange,
|
|
40
41
|
entrypointFileName = "index.tsx",
|
|
41
42
|
}) => {
|
|
42
|
-
const { Dialog:
|
|
43
|
-
|
|
43
|
+
const { Dialog: ImportPackageDialog, openDialog: openImportDialog } =
|
|
44
|
+
useImportPackageDialog()
|
|
44
45
|
const { toast } = useToast()
|
|
45
46
|
const [sidebarOpen, setSidebarOpen] = fileSidebarState
|
|
46
47
|
|
|
@@ -234,9 +235,9 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
|
|
|
234
235
|
Format
|
|
235
236
|
</Button>
|
|
236
237
|
</div>
|
|
237
|
-
<
|
|
238
|
-
|
|
239
|
-
const newContent = `import {} from "@tsci/${
|
|
238
|
+
<ImportPackageDialog
|
|
239
|
+
onPackageSelected={(pkg: Package) => {
|
|
240
|
+
const newContent = `import {} from "@tsci/${pkg.owner_github_username}.${pkg.unscoped_name}"\n${files[currentFile || ""]}`
|
|
240
241
|
updateFileContent(currentFile, newContent)
|
|
241
242
|
}}
|
|
242
243
|
/>
|
|
@@ -1,33 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
Toast,
|
|
4
|
-
ToastClose,
|
|
5
|
-
ToastDescription,
|
|
6
|
-
ToastProvider,
|
|
7
|
-
ToastTitle,
|
|
8
|
-
ToastViewport,
|
|
9
|
-
} from "@/components/ui/toast"
|
|
10
|
-
|
|
11
|
-
export function Toaster() {
|
|
12
|
-
const { toasts } = useToast()
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<ToastProvider>
|
|
16
|
-
{toasts.map(function ({ id, title, description, action, ...props }) {
|
|
17
|
-
return (
|
|
18
|
-
<Toast key={id} {...props}>
|
|
19
|
-
<div className="grid gap-1">
|
|
20
|
-
{title && <ToastTitle>{title}</ToastTitle>}
|
|
21
|
-
{description && (
|
|
22
|
-
<ToastDescription>{description}</ToastDescription>
|
|
23
|
-
)}
|
|
24
|
-
</div>
|
|
25
|
-
{action}
|
|
26
|
-
<ToastClose />
|
|
27
|
-
</Toast>
|
|
28
|
-
)
|
|
29
|
-
})}
|
|
30
|
-
<ToastViewport />
|
|
31
|
-
</ToastProvider>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
1
|
+
export { Toaster } from "react-hot-toast"
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { useParams } from "wouter"
|
|
2
2
|
import { useCurrentPackageId } from "./use-current-package-id"
|
|
3
|
-
import { useUrlParams } from "./use-url-params"
|
|
4
3
|
import { usePackageRelease } from "./use-package-release"
|
|
4
|
+
import { useUrlParams } from "./use-url-params"
|
|
5
5
|
|
|
6
6
|
export const useCurrentPackageRelease = (options?: {
|
|
7
|
-
|
|
7
|
+
include_ai_review?: boolean
|
|
8
|
+
include_logs?: boolean
|
|
9
|
+
refetchInterval?: number
|
|
8
10
|
}) => {
|
|
9
11
|
const { packageId } = useCurrentPackageId()
|
|
10
12
|
const urlParams = useUrlParams()
|
|
@@ -25,8 +27,17 @@ export const useCurrentPackageRelease = (options?: {
|
|
|
25
27
|
query = { package_id: packageId, is_latest: true }
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
if (query && options?.include_logs !== undefined) {
|
|
31
|
+
query.include_logs = options.include_logs
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (query && options?.include_ai_review !== undefined) {
|
|
35
|
+
query.include_ai_review = options.include_ai_review
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
const { data: packageRelease, ...rest } = usePackageRelease(query, {
|
|
29
|
-
|
|
39
|
+
refetchInterval: options?.refetchInterval,
|
|
30
40
|
})
|
|
41
|
+
|
|
31
42
|
return { packageRelease, ...rest }
|
|
32
43
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useEffect, useState } from "react"
|
|
2
|
+
|
|
3
|
+
export const useNow = (intervalMs: number = 1000) => {
|
|
4
|
+
const [now, setNow] = useState(Date.now())
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const id = setInterval(() => setNow(Date.now()), intervalMs)
|
|
8
|
+
return () => clearInterval(id)
|
|
9
|
+
}, [intervalMs])
|
|
10
|
+
|
|
11
|
+
return now
|
|
12
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { PackageRelease } from "fake-snippets-api/lib/db/schema"
|
|
2
|
-
import { useQuery } from "react-query"
|
|
2
|
+
import { type UseQueryOptions, useQuery } from "react-query"
|
|
3
3
|
import { useAxios } from "./use-axios"
|
|
4
4
|
|
|
5
|
-
type PackageReleaseQuery =
|
|
5
|
+
type PackageReleaseQuery = (
|
|
6
6
|
| {
|
|
7
7
|
package_release_id: string
|
|
8
8
|
}
|
|
@@ -17,29 +17,30 @@ type PackageReleaseQuery =
|
|
|
17
17
|
package_id: string
|
|
18
18
|
is_latest: boolean
|
|
19
19
|
}
|
|
20
|
+
) & {
|
|
21
|
+
include_logs?: boolean | null | undefined
|
|
22
|
+
include_ai_review?: boolean | null | undefined
|
|
23
|
+
}
|
|
20
24
|
|
|
21
25
|
export const usePackageRelease = (
|
|
22
26
|
query: PackageReleaseQuery | null,
|
|
23
|
-
options?: {
|
|
27
|
+
options?: {
|
|
28
|
+
refetchInterval?: number
|
|
29
|
+
},
|
|
24
30
|
) => {
|
|
25
31
|
const axios = useAxios()
|
|
26
32
|
|
|
27
33
|
return useQuery<PackageRelease, Error & { status: number }>(
|
|
28
|
-
["packageRelease", query
|
|
34
|
+
["packageRelease", query],
|
|
29
35
|
async () => {
|
|
30
36
|
if (!query) return
|
|
31
37
|
|
|
32
|
-
const { data } = await axios.post(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
include_logs: true,
|
|
39
|
-
},
|
|
40
|
-
}
|
|
41
|
-
: undefined,
|
|
42
|
-
)
|
|
38
|
+
const { data } = await axios.post("/package_releases/get", query, {
|
|
39
|
+
params: {
|
|
40
|
+
include_logs: query.include_logs,
|
|
41
|
+
include_ai_review: query.include_ai_review,
|
|
42
|
+
},
|
|
43
|
+
})
|
|
43
44
|
|
|
44
45
|
if (!data.package_release) {
|
|
45
46
|
throw new Error("Package release not found")
|
|
@@ -50,6 +51,7 @@ export const usePackageRelease = (
|
|
|
50
51
|
{
|
|
51
52
|
retry: false,
|
|
52
53
|
enabled: Boolean(query),
|
|
54
|
+
refetchInterval: options?.refetchInterval,
|
|
53
55
|
},
|
|
54
56
|
)
|
|
55
57
|
}
|