@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.
Files changed (44) hide show
  1. package/README.md +31 -1
  2. package/package.json +10 -2
  3. package/.github/workflows/bun-formatcheck.yml +0 -26
  4. package/.github/workflows/bun-pver-release.yml +0 -25
  5. package/.github/workflows/bun-typecheck.yml +0 -26
  6. package/biome.json +0 -57
  7. package/bun.lockb +0 -0
  8. package/cosmos.config.json +0 -3
  9. package/cosmos.decorator.tsx +0 -3
  10. package/examples/resistor.fixture.tsx +0 -12
  11. package/examples/runframe-with-api-1.fixture.tsx +0 -41
  12. package/examples/runframe1-basic.fixture.tsx +0 -19
  13. package/index.html +0 -13
  14. package/lib/components/BomTable.tsx +0 -69
  15. package/lib/components/CircuitJsonPreview.tsx +0 -348
  16. package/lib/components/CircuitJsonTableViewer/CircuitJsonTableViewer.tsx +0 -315
  17. package/lib/components/CircuitJsonTableViewer/ClickableText.tsx +0 -20
  18. package/lib/components/CircuitJsonTableViewer/HeaderCell.tsx +0 -26
  19. package/lib/components/CircuitJsonTableViewer/Modal.tsx +0 -38
  20. package/lib/components/ErrorFallback.tsx +0 -25
  21. package/lib/components/ErrorTabContent.tsx +0 -86
  22. package/lib/components/PcbViewerWithContainerHeight.tsx +0 -47
  23. package/lib/components/PreviewEmptyState.tsx +0 -14
  24. package/lib/components/RunFrame.tsx +0 -68
  25. package/lib/components/RunFrameWithApi/index.tsx +0 -44
  26. package/lib/components/RunFrameWithApi/standalone.tsx +0 -6
  27. package/lib/components/RunFrameWithApi/store.ts +0 -122
  28. package/lib/components/RunFrameWithApi/types.ts +0 -43
  29. package/lib/components/ui/button.tsx +0 -57
  30. package/lib/components/ui/dropdown-menu.tsx +0 -203
  31. package/lib/components/ui/tabs.tsx +0 -53
  32. package/lib/dev/render-to-circuit-json.ts +0 -7
  33. package/lib/hooks/use-styles.ts +0 -17
  34. package/lib/preview.ts +0 -1
  35. package/lib/runner.ts +0 -2
  36. package/lib/utils/index.ts +0 -6
  37. package/lib/utils/pcbManualEditEventHandler.ts +0 -156
  38. package/postcss.config.js +0 -8
  39. package/renovate.json +0 -15
  40. package/scripts/build-css.ts +0 -26
  41. package/src/main.tsx +0 -13
  42. package/tailwind.config.js +0 -3
  43. package/tsconfig.json +0 -45
  44. 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
- &times;
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,6 +0,0 @@
1
- import { createRoot } from "react-dom/client"
2
- import { RunFrameWithApi } from "./index"
3
-
4
- const root = createRoot(document.getElementById("root")!)
5
-
6
- root.render(<RunFrameWithApi />)
@@ -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 }