@tscircuit/runframe 0.0.11 → 0.0.12
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/README.md +31 -1
- package/package.json +10 -2
- package/.github/workflows/bun-formatcheck.yml +0 -26
- package/.github/workflows/bun-pver-release.yml +0 -25
- package/.github/workflows/bun-typecheck.yml +0 -26
- package/biome.json +0 -57
- package/bun.lockb +0 -0
- package/cosmos.config.json +0 -3
- package/cosmos.decorator.tsx +0 -3
- package/examples/resistor.fixture.tsx +0 -12
- package/examples/runframe-with-api-1.fixture.tsx +0 -41
- package/examples/runframe1-basic.fixture.tsx +0 -19
- package/index.html +0 -13
- package/lib/components/BomTable.tsx +0 -69
- package/lib/components/CircuitJsonPreview.tsx +0 -348
- package/lib/components/CircuitJsonTableViewer/CircuitJsonTableViewer.tsx +0 -315
- package/lib/components/CircuitJsonTableViewer/ClickableText.tsx +0 -20
- package/lib/components/CircuitJsonTableViewer/HeaderCell.tsx +0 -26
- package/lib/components/CircuitJsonTableViewer/Modal.tsx +0 -38
- package/lib/components/ErrorFallback.tsx +0 -25
- package/lib/components/ErrorTabContent.tsx +0 -86
- package/lib/components/PcbViewerWithContainerHeight.tsx +0 -47
- package/lib/components/PreviewEmptyState.tsx +0 -14
- package/lib/components/RunFrame.tsx +0 -68
- package/lib/components/RunFrameWithApi/index.tsx +0 -44
- package/lib/components/RunFrameWithApi/standalone.tsx +0 -6
- package/lib/components/RunFrameWithApi/store.ts +0 -122
- package/lib/components/RunFrameWithApi/types.ts +0 -43
- package/lib/components/ui/button.tsx +0 -57
- package/lib/components/ui/dropdown-menu.tsx +0 -203
- package/lib/components/ui/tabs.tsx +0 -53
- package/lib/dev/render-to-circuit-json.ts +0 -7
- package/lib/hooks/use-styles.ts +0 -17
- package/lib/preview.ts +0 -1
- package/lib/runner.ts +0 -2
- package/lib/utils/index.ts +0 -6
- package/lib/utils/pcbManualEditEventHandler.ts +0 -156
- package/postcss.config.js +0 -8
- package/renovate.json +0 -15
- package/scripts/build-css.ts +0 -26
- package/src/main.tsx +0 -13
- package/tailwind.config.js +0 -3
- package/tsconfig.json +0 -45
- package/vite.config.ts +0 -72
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import type React from "react"
|
|
2
|
-
|
|
3
|
-
interface ModalProps {
|
|
4
|
-
open: boolean
|
|
5
|
-
children: React.ReactNode
|
|
6
|
-
title: string
|
|
7
|
-
onClose: () => void
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const Modal: React.FC<ModalProps> = ({ open, children, title, onClose }) => {
|
|
11
|
-
if (!open) return null
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<div
|
|
15
|
-
className="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50"
|
|
16
|
-
onClick={onClose}
|
|
17
|
-
>
|
|
18
|
-
<div
|
|
19
|
-
className="bg-white p-5 rounded-lg relative w-11/12 max-w-2xl"
|
|
20
|
-
onClick={(e) => {
|
|
21
|
-
e.preventDefault()
|
|
22
|
-
e.stopPropagation()
|
|
23
|
-
}}
|
|
24
|
-
>
|
|
25
|
-
<h2 className="mt-0 text-xl">{title}</h2>
|
|
26
|
-
<button
|
|
27
|
-
className="absolute top-4 right-4 text-2xl font-bold"
|
|
28
|
-
onClick={onClose}
|
|
29
|
-
>
|
|
30
|
-
×
|
|
31
|
-
</button>
|
|
32
|
-
{children}
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export default Modal
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
|
|
3
|
-
export const ErrorFallback = ({ error }: { error: Error }) => {
|
|
4
|
-
return (
|
|
5
|
-
<div
|
|
6
|
-
data-testid="error-container"
|
|
7
|
-
className="error-container mt-4 bg-red-50 rounded-md border border-red-200"
|
|
8
|
-
>
|
|
9
|
-
<div className="p-4">
|
|
10
|
-
<h2 className="text-lg font-semibold text-red-800 mb-3">
|
|
11
|
-
Error Loading 3D Viewer
|
|
12
|
-
</h2>
|
|
13
|
-
<p className="text-xs font-mono whitespace-pre-wrap text-red-700">
|
|
14
|
-
{error.message}
|
|
15
|
-
</p>
|
|
16
|
-
<details
|
|
17
|
-
style={{ whiteSpace: "pre-wrap" }}
|
|
18
|
-
className="text-xs font-mono text-red-600 mt-2"
|
|
19
|
-
>
|
|
20
|
-
{error.stack}
|
|
21
|
-
</details>
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { GitHubLogoIcon } from "@radix-ui/react-icons"
|
|
2
|
-
import { ClipboardIcon } from "lucide-react"
|
|
3
|
-
import { Button } from "lib/components/ui/button"
|
|
4
|
-
|
|
5
|
-
export const ErrorTabContent = ({
|
|
6
|
-
code,
|
|
7
|
-
isStreaming,
|
|
8
|
-
errorMessage,
|
|
9
|
-
}: {
|
|
10
|
-
code?: string
|
|
11
|
-
isStreaming?: boolean
|
|
12
|
-
errorMessage?: string | null
|
|
13
|
-
}) => {
|
|
14
|
-
if (!errorMessage) {
|
|
15
|
-
return (
|
|
16
|
-
<div className="mt-4 bg-green-50 rounded-md border border-green-200">
|
|
17
|
-
<div className="p-4">
|
|
18
|
-
<h3 className="text-lg font-semibold text-green-800 mb-3">
|
|
19
|
-
No Errors 👌
|
|
20
|
-
</h3>
|
|
21
|
-
<p className="text-sm text-green-700">
|
|
22
|
-
Your code is running without any errors.
|
|
23
|
-
</p>
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<>
|
|
31
|
-
<div className="mt-4 bg-red-50 rounded-md border border-red-200 max-h-[500px] overflow-y-auto">
|
|
32
|
-
<div className="p-4">
|
|
33
|
-
<h3 className="text-lg font-semibold text-red-800 mb-3">Error</h3>
|
|
34
|
-
<p className="text-xs font-mono whitespace-pre-wrap text-red-600 mt-2">
|
|
35
|
-
{errorMessage}
|
|
36
|
-
</p>
|
|
37
|
-
</div>
|
|
38
|
-
</div>
|
|
39
|
-
<div className="flex gap-2 mt-4 justify-end">
|
|
40
|
-
<Button
|
|
41
|
-
variant="outline"
|
|
42
|
-
onClick={() => {
|
|
43
|
-
if (!errorMessage) return
|
|
44
|
-
navigator.clipboard.writeText(errorMessage)
|
|
45
|
-
alert("Error copied to clipboard!")
|
|
46
|
-
}}
|
|
47
|
-
>
|
|
48
|
-
<ClipboardIcon className="w-4 h-4 mr-2" />
|
|
49
|
-
Copy Error
|
|
50
|
-
</Button>
|
|
51
|
-
<Button
|
|
52
|
-
variant="outline"
|
|
53
|
-
onClick={() => {
|
|
54
|
-
window.alert(
|
|
55
|
-
"Not supported yet! Please report/upvote an issue on github.com/tscircuit/tscircuit",
|
|
56
|
-
)
|
|
57
|
-
// const title = `Error: ${errorMessage
|
|
58
|
-
// .replace("Render Error:", "")
|
|
59
|
-
// .replace(/\"_errors\":\[\]/g, "")
|
|
60
|
-
// .replace(/\{,/g, "{")
|
|
61
|
-
// .replace(/"_errors":\[/g, "")
|
|
62
|
-
// .replace(/[^a-zA-Z0-9 ]/g, " ")
|
|
63
|
-
// .replace(/\s+/g, " ")
|
|
64
|
-
// .replace(/ \d+ /g, " ")
|
|
65
|
-
// .slice(0, 100)}`
|
|
66
|
-
// const url = encodeTextToUrlHash(code ?? "").replace(
|
|
67
|
-
// "http://localhost:5173",
|
|
68
|
-
// "https://snippets.tscircuit.com",
|
|
69
|
-
// )
|
|
70
|
-
// let body = `[Snippet code to reproduce](${url})\n\n### Error\n\`\`\`\n${errorMessage.slice(0, 600)}\n\`\`\``
|
|
71
|
-
// if (body.length > 4000) {
|
|
72
|
-
// body = `\`\`\`tsx\n// Please paste the code here\`\`\`\n\n### Error\n\`\`\`\n${errorMessage.slice(0, 2000)}\n\`\`\``
|
|
73
|
-
// }
|
|
74
|
-
// window.open(
|
|
75
|
-
// `https://github.com/tscircuit/snippets/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}`,
|
|
76
|
-
// "_blank",
|
|
77
|
-
// )
|
|
78
|
-
}}
|
|
79
|
-
>
|
|
80
|
-
<GitHubLogoIcon className="w-4 h-4 mr-2" />
|
|
81
|
-
Report Issue
|
|
82
|
-
</Button>
|
|
83
|
-
</div>
|
|
84
|
-
</>
|
|
85
|
-
)
|
|
86
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { useRef, useState, useLayoutEffect, type ComponentProps } from "react"
|
|
2
|
-
import { PCBViewer } from "@tscircuit/pcb-viewer"
|
|
3
|
-
|
|
4
|
-
export const PcbViewerWithContainerHeight = ({
|
|
5
|
-
containerClassName,
|
|
6
|
-
...props
|
|
7
|
-
}: {
|
|
8
|
-
containerClassName?: string
|
|
9
|
-
} & ComponentProps<typeof PCBViewer>) => {
|
|
10
|
-
const containerRef = useRef<HTMLDivElement>(null)
|
|
11
|
-
const [computedHeight, setComputedHeight] = useState(620)
|
|
12
|
-
|
|
13
|
-
useLayoutEffect(() => {
|
|
14
|
-
const updateHeight = () => {
|
|
15
|
-
if (containerRef.current) {
|
|
16
|
-
const containerHeight = containerRef.current.clientHeight
|
|
17
|
-
const screenHeight = window.innerHeight
|
|
18
|
-
setComputedHeight(
|
|
19
|
-
Math.min(Math.max(containerHeight, 620), screenHeight),
|
|
20
|
-
)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Immediate synchronous calculation
|
|
25
|
-
updateHeight()
|
|
26
|
-
|
|
27
|
-
// Resize listener for dynamic changes
|
|
28
|
-
const resizeObserver = new ResizeObserver(updateHeight)
|
|
29
|
-
if (containerRef.current) {
|
|
30
|
-
resizeObserver.observe(containerRef.current)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Fallback for window resize
|
|
34
|
-
window.addEventListener("resize", updateHeight)
|
|
35
|
-
|
|
36
|
-
return () => {
|
|
37
|
-
resizeObserver.disconnect()
|
|
38
|
-
window.removeEventListener("resize", updateHeight)
|
|
39
|
-
}
|
|
40
|
-
}, [])
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<div ref={containerRef} className={containerClassName || "w-full h-full"}>
|
|
44
|
-
<PCBViewer {...props} height={computedHeight} />
|
|
45
|
-
</div>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { Button } from "lib/components/ui/button"
|
|
2
|
-
import { PlayIcon } from "lucide-react"
|
|
3
|
-
|
|
4
|
-
const PreviewEmptyState = ({ onRunClicked }: { onRunClicked: () => void }) => (
|
|
5
|
-
<div className="flex items-center gap-3 bg-gray-100 text-center justify-center py-10">
|
|
6
|
-
No circuit json loaded
|
|
7
|
-
<Button className="bg-blue-600 hover:bg-blue-500" onClick={onRunClicked}>
|
|
8
|
-
Run Code
|
|
9
|
-
<PlayIcon className="w-3 h-3 ml-2" />
|
|
10
|
-
</Button>
|
|
11
|
-
</div>
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
export default PreviewEmptyState
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { createCircuitWebWorker } from "@tscircuit/eval-webworker"
|
|
2
|
-
import { CircuitJsonPreview } from "./CircuitJsonPreview"
|
|
3
|
-
import { useEffect, useState } from "react"
|
|
4
|
-
|
|
5
|
-
// @ts-ignore
|
|
6
|
-
import evalWebWorkerBlobUrl from "@tscircuit/eval-webworker/blob-url"
|
|
7
|
-
|
|
8
|
-
interface Props {
|
|
9
|
-
/**
|
|
10
|
-
* Map of filenames to file contents that will be available in the worker
|
|
11
|
-
*/
|
|
12
|
-
fsMap: { [filename: string]: string }
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The entry point file that will be executed first
|
|
16
|
-
*/
|
|
17
|
-
entrypoint: string
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Called when the circuit JSON changes
|
|
21
|
-
*/
|
|
22
|
-
onCircuitJsonChange?: (circuitJson: any) => void
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Called when rendering is finished
|
|
26
|
-
*/
|
|
27
|
-
onRenderingFinished?: (params: { circuitJson: any }) => void
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Called for each render event
|
|
31
|
-
*/
|
|
32
|
-
onRenderEvent?: (event: any) => void
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Called when an error occurs
|
|
36
|
-
*/
|
|
37
|
-
onError?: (error: Error) => void
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const RunFrame = (props: Props) => {
|
|
41
|
-
const [circuitJson, setCircuitJson] = useState<any>(null)
|
|
42
|
-
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
async function runWorker() {
|
|
45
|
-
const worker = await createCircuitWebWorker({
|
|
46
|
-
webWorkerUrl: evalWebWorkerBlobUrl,
|
|
47
|
-
verbose: true,
|
|
48
|
-
})
|
|
49
|
-
const $finished = worker.executeWithFsMap({
|
|
50
|
-
entrypoint: props.entrypoint,
|
|
51
|
-
fsMap: props.fsMap,
|
|
52
|
-
})
|
|
53
|
-
console.log("waiting for execution to finish...")
|
|
54
|
-
await $finished
|
|
55
|
-
console.log("waiting for initial circuit json...")
|
|
56
|
-
setCircuitJson(await worker.getCircuitJson())
|
|
57
|
-
console.log("got initial circuit json")
|
|
58
|
-
await $finished.catch((e) => {
|
|
59
|
-
console.error(e)
|
|
60
|
-
})
|
|
61
|
-
setCircuitJson(await worker.getCircuitJson())
|
|
62
|
-
}
|
|
63
|
-
runWorker()
|
|
64
|
-
}, [props.fsMap])
|
|
65
|
-
|
|
66
|
-
console.log({ circuitJson })
|
|
67
|
-
return <CircuitJsonPreview circuitJson={circuitJson} />
|
|
68
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { useEffect } from "react"
|
|
2
|
-
import { RunFrame } from "../RunFrame"
|
|
3
|
-
import { useRunFrameStore, selectCurrentFileMap } from "./store"
|
|
4
|
-
import type { RunFrameWithApiProps } from "./types"
|
|
5
|
-
|
|
6
|
-
const guessEntrypoint = (files: string[]) =>
|
|
7
|
-
files.find((file) => file.includes("entrypoint.")) ??
|
|
8
|
-
files.find((file) => file.includes("index.")) ??
|
|
9
|
-
files.find((file) => file.includes("main.")) ??
|
|
10
|
-
files.find((file) => file.endsWith(".tsx"))
|
|
11
|
-
|
|
12
|
-
export const RunFrameWithApi = ({ apiBaseUrl }: RunFrameWithApiProps) => {
|
|
13
|
-
const { startPolling, stopPolling } = useRunFrameStore()
|
|
14
|
-
const fsMap = useRunFrameStore(selectCurrentFileMap)
|
|
15
|
-
|
|
16
|
-
// Initialize API base URL
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (apiBaseUrl) {
|
|
19
|
-
window.API_BASE_URL = apiBaseUrl
|
|
20
|
-
}
|
|
21
|
-
}, [apiBaseUrl])
|
|
22
|
-
|
|
23
|
-
// Start/stop polling
|
|
24
|
-
useEffect(() => {
|
|
25
|
-
startPolling()
|
|
26
|
-
return () => stopPolling()
|
|
27
|
-
}, [startPolling, stopPolling])
|
|
28
|
-
|
|
29
|
-
const entrypoint = guessEntrypoint(Object.keys(fsMap)) ?? "entrypoint.tsx"
|
|
30
|
-
|
|
31
|
-
if (!fsMap[entrypoint]) {
|
|
32
|
-
return <div>No entrypoint found for Run Frame!</div>
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<RunFrame
|
|
37
|
-
fsMap={fsMap}
|
|
38
|
-
entrypoint={entrypoint}
|
|
39
|
-
onError={(error) => {
|
|
40
|
-
console.error("RunFrame error:", error)
|
|
41
|
-
}}
|
|
42
|
-
/>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { create } from "zustand"
|
|
2
|
-
import { devtools } from "zustand/middleware"
|
|
3
|
-
import type {
|
|
4
|
-
FilePath,
|
|
5
|
-
FileContent,
|
|
6
|
-
File,
|
|
7
|
-
FileEvent,
|
|
8
|
-
RunFrameState,
|
|
9
|
-
} from "./types"
|
|
10
|
-
|
|
11
|
-
const API_BASE = window.API_BASE_URL ?? "/api"
|
|
12
|
-
|
|
13
|
-
async function upsertFileApi(
|
|
14
|
-
path: FilePath,
|
|
15
|
-
content: FileContent,
|
|
16
|
-
): Promise<File> {
|
|
17
|
-
const response = await fetch(`${API_BASE}/files/upsert`, {
|
|
18
|
-
method: "POST",
|
|
19
|
-
headers: { "Content-Type": "application/json" },
|
|
20
|
-
body: JSON.stringify({ file_path: path, text_content: content }),
|
|
21
|
-
})
|
|
22
|
-
const data = await response.json()
|
|
23
|
-
return data.file
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function getFileApi(path: FilePath): Promise<File> {
|
|
27
|
-
const response = await fetch(
|
|
28
|
-
`${API_BASE}/files/get?file_path=${encodeURIComponent(path)}`,
|
|
29
|
-
)
|
|
30
|
-
const data = await response.json()
|
|
31
|
-
return data.file
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function getEvents(since: string | null): Promise<FileEvent[]> {
|
|
35
|
-
const url = since
|
|
36
|
-
? `${API_BASE}/events/list?since=${encodeURIComponent(since)}`
|
|
37
|
-
: `${API_BASE}/events/list`
|
|
38
|
-
const response = await fetch(url)
|
|
39
|
-
const data = await response.json()
|
|
40
|
-
return data.event_list
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Create store
|
|
44
|
-
export const useRunFrameStore = create<RunFrameState>()(
|
|
45
|
-
devtools(
|
|
46
|
-
(set, get) => ({
|
|
47
|
-
fsMap: new Map(),
|
|
48
|
-
lastEventTime: null,
|
|
49
|
-
isPolling: false,
|
|
50
|
-
error: null,
|
|
51
|
-
|
|
52
|
-
upsertFile: async (path, content) => {
|
|
53
|
-
try {
|
|
54
|
-
const file = await upsertFileApi(path, content)
|
|
55
|
-
set((state) => ({
|
|
56
|
-
fsMap: new Map(state.fsMap).set(file.file_path, file.text_content),
|
|
57
|
-
}))
|
|
58
|
-
} catch (error) {
|
|
59
|
-
set({ error: error as Error })
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
getFile: async (path) => {
|
|
64
|
-
try {
|
|
65
|
-
const file = await getFileApi(path)
|
|
66
|
-
set((state) => ({
|
|
67
|
-
fsMap: new Map(state.fsMap).set(file.file_path, file.text_content),
|
|
68
|
-
}))
|
|
69
|
-
} catch (error) {
|
|
70
|
-
set({ error: error as Error })
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
startPolling: () => {
|
|
75
|
-
const poll = async () => {
|
|
76
|
-
const state = get()
|
|
77
|
-
if (!state.isPolling) return
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const events = await getEvents(state.lastEventTime)
|
|
81
|
-
|
|
82
|
-
if (events.length > 0) {
|
|
83
|
-
// Update lastEventTime to most recent event
|
|
84
|
-
const newLastEventTime = events[events.length - 1].created_at
|
|
85
|
-
|
|
86
|
-
// Process all file updates
|
|
87
|
-
const updates = new Map(state.fsMap)
|
|
88
|
-
for (const event of events) {
|
|
89
|
-
if (event.event_type === "FILE_UPDATED") {
|
|
90
|
-
const file = await getFileApi(event.file_path)
|
|
91
|
-
updates.set(file.file_path, file.text_content)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
set({
|
|
96
|
-
fsMap: updates,
|
|
97
|
-
lastEventTime: newLastEventTime,
|
|
98
|
-
})
|
|
99
|
-
}
|
|
100
|
-
} catch (error) {
|
|
101
|
-
set({ error: error as Error })
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Schedule next poll
|
|
105
|
-
setTimeout(poll, 1000)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
set({ isPolling: true })
|
|
109
|
-
poll()
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
stopPolling: () => {
|
|
113
|
-
set({ isPolling: false })
|
|
114
|
-
},
|
|
115
|
-
}),
|
|
116
|
-
{ name: "run-frame-store" },
|
|
117
|
-
),
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
// Export selector for current file map
|
|
121
|
-
export const selectCurrentFileMap = (state: RunFrameState) =>
|
|
122
|
-
Object.fromEntries(state.fsMap.entries())
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// Types
|
|
2
|
-
export type FilePath = string
|
|
3
|
-
export type FileContent = string
|
|
4
|
-
export type FileId = string
|
|
5
|
-
|
|
6
|
-
export interface File {
|
|
7
|
-
file_id: FileId
|
|
8
|
-
file_path: FilePath
|
|
9
|
-
text_content: FileContent
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface FileEvent {
|
|
13
|
-
event_id: string
|
|
14
|
-
event_type: "FILE_UPDATED"
|
|
15
|
-
file_path: FilePath
|
|
16
|
-
created_at: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface RunFrameState {
|
|
20
|
-
fsMap: Map<FilePath, FileContent>
|
|
21
|
-
lastEventTime: string | null
|
|
22
|
-
isPolling: boolean
|
|
23
|
-
error: Error | null
|
|
24
|
-
|
|
25
|
-
// Actions
|
|
26
|
-
upsertFile: (path: FilePath, content: FileContent) => Promise<void>
|
|
27
|
-
getFile: (path: FilePath) => Promise<void>
|
|
28
|
-
startPolling: () => void
|
|
29
|
-
stopPolling: () => void
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface RunFrameWithApiProps {
|
|
33
|
-
/**
|
|
34
|
-
* Base URL for the API endpoints
|
|
35
|
-
*/
|
|
36
|
-
apiBaseUrl?: string
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
declare global {
|
|
40
|
-
interface Window {
|
|
41
|
-
API_BASE_URL: string
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
|
|
4
|
-
import { cn } from "lib/utils"
|
|
5
|
-
|
|
6
|
-
const buttonVariants = cva(
|
|
7
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-slate-950 disabled:pointer-events-none disabled:opacity-50 dark:focus-visible:ring-slate-300",
|
|
8
|
-
{
|
|
9
|
-
variants: {
|
|
10
|
-
variant: {
|
|
11
|
-
default:
|
|
12
|
-
"bg-slate-900 text-slate-50 shadow hover:bg-slate-900/90 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50/90",
|
|
13
|
-
destructive:
|
|
14
|
-
"bg-red-500 text-slate-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-slate-50 dark:hover:bg-red-900/90",
|
|
15
|
-
outline:
|
|
16
|
-
"border border-slate-200 bg-white shadow-sm hover:bg-slate-100 hover:text-slate-900 dark:border-slate-800 dark:bg-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50",
|
|
17
|
-
secondary:
|
|
18
|
-
"bg-slate-100 text-slate-900 shadow-sm hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80",
|
|
19
|
-
ghost:
|
|
20
|
-
"hover:bg-slate-100 hover:text-slate-900 dark:hover:bg-slate-800 dark:hover:text-slate-50",
|
|
21
|
-
link: "text-slate-900 underline-offset-4 hover:underline dark:text-slate-50",
|
|
22
|
-
},
|
|
23
|
-
size: {
|
|
24
|
-
default: "h-9 px-4 py-2",
|
|
25
|
-
sm: "h-8 rounded-md px-3 text-xs",
|
|
26
|
-
lg: "h-10 rounded-md px-8",
|
|
27
|
-
icon: "h-9 w-9",
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
defaultVariants: {
|
|
31
|
-
variant: "default",
|
|
32
|
-
size: "default",
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
export interface ButtonProps
|
|
38
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
|
-
VariantProps<typeof buttonVariants> {
|
|
40
|
-
asChild?: boolean
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
-
const Comp = "button"
|
|
46
|
-
return (
|
|
47
|
-
<Comp
|
|
48
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
49
|
-
ref={ref}
|
|
50
|
-
{...props}
|
|
51
|
-
/>
|
|
52
|
-
)
|
|
53
|
-
},
|
|
54
|
-
)
|
|
55
|
-
Button.displayName = "Button"
|
|
56
|
-
|
|
57
|
-
export { Button, buttonVariants }
|