From a5f30b8e80dad18241e8eb28eae6827a77b380f5 Mon Sep 17 00:00:00 2001 From: seveibar Date: Thu, 10 Oct 2024 15:51:01 -0700 Subject: [PATCH 1/6] refactor previewcontent --- src/components/CodeAndPreview.tsx | 60 ++++------ src/components/ErrorTabContent.tsx | 2 +- src/components/PreviewContent.tsx | 119 ++++++++++++++++++++ src/hooks/use-run-tsx/construct-circuit.tsx | 1 - src/hooks/use-run-tsx/index.tsx | 36 ++++-- src/pages/ai.tsx | 4 +- src/pages/view-snippet.tsx | 8 +- 7 files changed, 171 insertions(+), 59 deletions(-) create mode 100644 src/components/PreviewContent.tsx diff --git a/src/components/CodeAndPreview.tsx b/src/components/CodeAndPreview.tsx index f32f453a..054a798c 100644 --- a/src/components/CodeAndPreview.tsx +++ b/src/components/CodeAndPreview.tsx @@ -15,11 +15,12 @@ import { useAxios } from "@/hooks/use-axios" import { TypeBadge } from "./TypeBadge" import { useToast } from "@/hooks/use-toast" import { useMutation, useQueryClient } from "react-query" -import { ClipboardIcon, Share, Eye, EyeOff } from "lucide-react" +import { ClipboardIcon, Share, Eye, EyeOff, PlayIcon } from "lucide-react" import { MagicWandIcon } from "@radix-ui/react-icons" import { ErrorBoundary } from "react-error-boundary" import { ErrorTabContent } from "./ErrorTabContent" import { cn } from "@/lib/utils" +import { PreviewContent } from "./PreviewContent" interface Props { snippet?: Snippet | null @@ -41,10 +42,16 @@ export function CodeAndPreview({ snippet }: Props) { }, [snippet?.code]) const { toast } = useToast() - const { message, circuitJson, compiledJs } = useRunTsx( + const { + message, + circuitJson, + compiledJs, + triggerRunTsx, + tsxRunTriggerCount, + } = useRunTsx({ code, - snippet?.snippet_type, - ) + type: snippet?.snippet_type, + }) const qc = useQueryClient() const updateSnippetMutation = useMutation({ @@ -113,43 +120,14 @@ export function CodeAndPreview({ snippet }: Props) { /> {showPreview && ( -
- - - PCB - 3D - JSON - - Errors - {message && ( - - 1 - - )} - - - -
- -
-
- -
- Error loading 3D viewer
}> - - -
- - -
- -
-
- - - - - + )} diff --git a/src/components/ErrorTabContent.tsx b/src/components/ErrorTabContent.tsx index 3538f478..7b86f260 100644 --- a/src/components/ErrorTabContent.tsx +++ b/src/components/ErrorTabContent.tsx @@ -12,7 +12,7 @@ export const ErrorTabContent = ({ }: { code?: string isStreaming?: boolean - errorMessage?: string + errorMessage?: string | null }) => { const anthropic = useAiApi() const simplifiedErrorMessage = useAsyncMemo(async () => { diff --git a/src/components/PreviewContent.tsx b/src/components/PreviewContent.tsx new file mode 100644 index 00000000..94ebff6f --- /dev/null +++ b/src/components/PreviewContent.tsx @@ -0,0 +1,119 @@ +import { useEffect, useMemo, useState } from "react" +import { CodeEditor } from "@/components/CodeEditor" +import { PCBViewer } from "@tscircuit/pcb-viewer" +import { CadViewer } from "@tscircuit/3d-viewer" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { defaultCodeForBlankPage } from "@/lib/defaultCodeForBlankCode" +import { decodeUrlHashToText } from "@/lib/decodeUrlHashToText" +import { encodeTextToUrlHash } from "@/lib/encodeTextToUrlHash" +import { Button } from "@/components/ui/button" +import { useRunTsx } from "@/hooks/use-run-tsx" +import EditorNav from "./EditorNav" +import { CircuitJsonTableViewer } from "./TableViewer/CircuitJsonTableViewer" +import { Snippet } from "fake-snippets-api/lib/db/schema" +import { useAxios } from "@/hooks/use-axios" +import { TypeBadge } from "./TypeBadge" +import { useToast } from "@/hooks/use-toast" +import { useMutation, useQueryClient } from "react-query" +import { ClipboardIcon, Share, Eye, EyeOff, PlayIcon } from "lucide-react" +import { MagicWandIcon } from "@radix-ui/react-icons" +import { ErrorBoundary } from "react-error-boundary" +import { ErrorTabContent } from "./ErrorTabContent" +import { cn } from "@/lib/utils" + +export interface PreviewContentProps { + code: string + triggerRunTsx: () => void + hasUnsavedChanges: boolean + tsxRunTriggerCount: number + errorMessage: string | null + circuitJson: any +} + +const PreviewEmptyState = ({ + triggerRunTsx, +}: { triggerRunTsx: () => void }) => ( +
+ No circuit json loaded + +
+) + +export const PreviewContent = ({ + code, + triggerRunTsx, + hasUnsavedChanges, + tsxRunTriggerCount, + errorMessage, + circuitJson, +}: PreviewContentProps) => { + return ( +
+ +
+ +
+ + PCB + 3D + JSON + + Errors + {errorMessage && ( + + 1 + + )} + + +
+ +
+ Error loading PCB viewer
}> + {circuitJson ? ( + + ) : ( + + )} + +
+ + +
+ Error loading 3D viewer
}> + {circuitJson ? ( + + ) : ( + + )} + +
+ + +
+ Error loading 3D viewer
}> + {circuitJson ? ( + + ) : ( + + )} + + +
+ + + + + + ) +} diff --git a/src/hooks/use-run-tsx/construct-circuit.tsx b/src/hooks/use-run-tsx/construct-circuit.tsx index c291465f..60c66dfd 100644 --- a/src/hooks/use-run-tsx/construct-circuit.tsx +++ b/src/hooks/use-run-tsx/construct-circuit.tsx @@ -4,7 +4,6 @@ import * as React from "react" import { useCompiledTsx } from "../use-compiled-tsx" import { createJSCADRenderer } from "jscad-fiber" import { jscadPlanner } from "jscad-planner" -import { getImportsFromCode } from "@tscircuit/prompt-benchmarks/code-runner-utils" export const constructCircuit = ( UserElm: any, diff --git a/src/hooks/use-run-tsx/index.tsx b/src/hooks/use-run-tsx/index.tsx index 77552e19..0287238c 100644 --- a/src/hooks/use-run-tsx/index.tsx +++ b/src/hooks/use-run-tsx/index.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from "react" +import { useEffect, useMemo, useReducer, useState } from "react" import * as React from "react" import { useCompiledTsx } from "../use-compiled-tsx" import { Circuit } from "@tscircuit/core" @@ -17,13 +17,24 @@ type RunTsxResult = { isLoading: boolean } -export const useRunTsx = ( - code?: string, - type?: "board" | "footprint" | "package" | "model", - { isStreaming = false }: { isStreaming?: boolean } = {}, -): RunTsxResult => { +export const useRunTsx = ({ + code, + type, + isStreaming = false, +}: { + code?: string + type?: "board" | "footprint" | "package" | "model" + isStreaming?: boolean +} = {}): RunTsxResult & { + triggerRunTsx: () => void + tsxRunTriggerCount: number +} => { type ??= "board" const compiledJs = useCompiledTsx(code, { isStreaming }) + const [tsxRunTriggerCount, incTsxRunTriggerCount] = useReducer( + (c) => c + 1, + 0, + ) const [tsxResult, setTsxResult] = useState({ compiledModule: null, message: "", @@ -33,6 +44,7 @@ export const useRunTsx = ( const apiBaseUrl = useSnippetsBaseApiUrl() useEffect(() => { + if (tsxRunTriggerCount === 0) return async function run() { if (isStreaming || !compiledJs || !code) { setTsxResult({ @@ -61,11 +73,9 @@ export const useRunTsx = ( ).then((res) => res.json()) try { - console.log("importedSnippet", importedSnippet) - // eval the imported snippet compiled_js preSuppliedImports[importName] = evalCompiledJs( importedSnippet.compiled_js, - ) + ).default.exports } catch (e) { console.error("Error importing snippet", e) } @@ -127,7 +137,11 @@ export const useRunTsx = ( } } run() - }, [compiledJs, isStreaming]) + }, [tsxRunTriggerCount, compiledJs, isStreaming]) - return tsxResult + return { + ...tsxResult, + triggerRunTsx: incTsxRunTriggerCount, + tsxRunTriggerCount, + } } diff --git a/src/pages/ai.tsx b/src/pages/ai.tsx index 8d4d340a..c1647ff4 100644 --- a/src/pages/ai.tsx +++ b/src/pages/ai.tsx @@ -19,7 +19,9 @@ export const AiPage = () => { const [code, setCode] = useState("") const [dts, setDts] = useState("") const [isStreaming, setIsStreaming] = useState(false) - const { message: errorMessage, circuitJson } = useRunTsx(code, "board", { + const { message: errorMessage, circuitJson } = useRunTsx({ + code, + type: "board", isStreaming, }) const { saveSnippet, isLoading: isSaving } = useSaveSnippet() diff --git a/src/pages/view-snippet.tsx b/src/pages/view-snippet.tsx index 4178920a..d8db8258 100644 --- a/src/pages/view-snippet.tsx +++ b/src/pages/view-snippet.tsx @@ -18,10 +18,10 @@ export const ViewSnippetPage = () => { const { author, snippetName } = useParams() const { snippet } = useCurrentSnippet() - const { circuitJson, message } = useRunTsx( - snippet?.code ?? "", - snippet?.snippet_type, - ) + const { circuitJson, message } = useRunTsx({ + code: snippet?.code ?? "", + type: snippet?.snippet_type, + }) return (
From 4da21dc384fbbe54c6a89a0c438dfac197359f65 Mon Sep 17 00:00:00 2001 From: seveibar Date: Thu, 10 Oct 2024 15:53:38 -0700 Subject: [PATCH 2/6] refactor ai page to use preview content --- src/components/PreviewContent.tsx | 2 +- src/pages/ai.tsx | 95 +++++-------------------------- 2 files changed, 16 insertions(+), 81 deletions(-) diff --git a/src/components/PreviewContent.tsx b/src/components/PreviewContent.tsx index 94ebff6f..15a8b52c 100644 --- a/src/components/PreviewContent.tsx +++ b/src/components/PreviewContent.tsx @@ -81,7 +81,7 @@ export const PreviewContent = ({
Error loading PCB viewer
}> {circuitJson ? ( - + ) : ( )} diff --git a/src/pages/ai.tsx b/src/pages/ai.tsx index c1647ff4..18ff0761 100644 --- a/src/pages/ai.tsx +++ b/src/pages/ai.tsx @@ -14,12 +14,18 @@ import { useLocation } from "wouter" import { useSaveSnippet } from "@/hooks/use-save-snippet" import { useToast } from "@/hooks/use-toast" import { useSnippet } from "@/hooks/use-snippet" +import { PreviewContent } from "@/components/PreviewContent" export const AiPage = () => { const [code, setCode] = useState("") const [dts, setDts] = useState("") const [isStreaming, setIsStreaming] = useState(false) - const { message: errorMessage, circuitJson } = useRunTsx({ + const { + message: errorMessage, + circuitJson, + triggerRunTsx, + tsxRunTriggerCount, + } = useRunTsx({ code, type: "board", isStreaming, @@ -63,85 +69,14 @@ export const AiPage = () => {
- -
- - Code - PCB - 3D - - Errors - {errorMessage && ( - - 1 - - )} - - -
-
- -
-
- -
- -
-
- -
- {circuitJson ? ( - - ) : ( - "No Circuit JSON (might be an error in the snippet)" - )} -
-
- -
- {circuitJson ? ( - - ) : ( - "No Circuit JSON (might be an error in the snippet)" - )} -
-
- - - - +
From 0f139ab5acb290adddf79178b9b46633856f475e Mon Sep 17 00:00:00 2001 From: seveibar Date: Thu, 10 Oct 2024 15:57:16 -0700 Subject: [PATCH 3/6] much clearer error and empty states, basic error tab switching --- src/components/PreviewContent.tsx | 17 +++++++++++++++-- src/hooks/use-run-tsx/index.tsx | 4 +++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/components/PreviewContent.tsx b/src/components/PreviewContent.tsx index 15a8b52c..5e07a595 100644 --- a/src/components/PreviewContent.tsx +++ b/src/components/PreviewContent.tsx @@ -20,6 +20,7 @@ import { MagicWandIcon } from "@radix-ui/react-icons" import { ErrorBoundary } from "react-error-boundary" import { ErrorTabContent } from "./ErrorTabContent" import { cn } from "@/lib/utils" +import { useCallback } from "react" export interface PreviewContentProps { code: string @@ -50,9 +51,17 @@ export const PreviewContent = ({ errorMessage, circuitJson, }: PreviewContentProps) => { + const [activeTab, setActiveTab] = useState("pcb") + + useEffect(() => { + if (errorMessage) { + setActiveTab("error") + } + }, [errorMessage]) + return (
- +
diff --git a/src/hooks/use-run-tsx/index.tsx b/src/hooks/use-run-tsx/index.tsx index 0287238c..1b18e16a 100644 --- a/src/hooks/use-run-tsx/index.tsx +++ b/src/hooks/use-run-tsx/index.tsx @@ -83,7 +83,9 @@ export const useRunTsx = ({ const __tscircuit_require = (name: string) => { if (!preSuppliedImports[name]) { - throw new Error(`Import "${name}" not found`) + throw new Error( + `Import "${name}" not found (imports available: ${Object.keys(preSuppliedImports).join(",")})`, + ) } return preSuppliedImports[name] } From 424be5d40a6da4367c9e403310c2871e4c5a48b3 Mon Sep 17 00:00:00 2001 From: seveibar Date: Thu, 10 Oct 2024 16:07:13 -0700 Subject: [PATCH 4/6] intelligent run button updates, importing working --- src/components/CodeAndPreview.tsx | 1 - src/components/PreviewContent.tsx | 9 +++++++-- src/hooks/use-compiled-tsx.ts | 4 ++-- src/hooks/use-run-tsx/index.tsx | 24 ++++++++++++++++-------- src/pages/ai.tsx | 1 - 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/components/CodeAndPreview.tsx b/src/components/CodeAndPreview.tsx index 054a798c..fbcc9104 100644 --- a/src/components/CodeAndPreview.tsx +++ b/src/components/CodeAndPreview.tsx @@ -123,7 +123,6 @@ export function CodeAndPreview({ snippet }: Props) { { const [activeTab, setActiveTab] = useState("pcb") + const [versionOfCodeLastRun, setVersionOfCodeLastRun] = useState("") + + useEffect(() => { + if (tsxRunTriggerCount === 0) return + setVersionOfCodeLastRun(code) + }, [tsxRunTriggerCount]) useEffect(() => { if (errorMessage) { @@ -66,7 +71,7 @@ export const PreviewContent = ({
{showPreview && ( void - hasUnsavedChanges: boolean - tsxRunTriggerCount: number - errorMessage: string | null - circuitJson: any -} +export type PreviewContentProps = + | { + code: string + triggerRunTsx: () => void + tsxRunTriggerCount: number + errorMessage: string | null + circuitJson: any + className?: string + showCodeTab?: false + isStreaming?: boolean + onCodeChange?: (code: string) => void + onDtsChange?: (dts: string) => void + } + | { + code: string + triggerRunTsx: () => void + tsxRunTriggerCount: number + errorMessage: string | null + circuitJson: any + className?: string + showCodeTab: true + isStreaming: boolean + onCodeChange: (code: string) => void + onDtsChange: (dts: string) => void + } const PreviewEmptyState = ({ triggerRunTsx, @@ -49,8 +66,13 @@ export const PreviewContent = ({ tsxRunTriggerCount, errorMessage, circuitJson, + showCodeTab = false, + className, + isStreaming, + onCodeChange, + onDtsChange, }: PreviewContentProps) => { - const [activeTab, setActiveTab] = useState("pcb") + const [activeTab, setActiveTab] = useState(showCodeTab ? "code" : "pcb") const [versionOfCodeLastRun, setVersionOfCodeLastRun] = useState("") useEffect(() => { @@ -64,8 +86,14 @@ export const PreviewContent = ({ } }, [errorMessage]) + useEffect(() => { + if (activeTab === "code" && circuitJson && !errorMessage) { + setActiveTab("pcb") + } + }, [circuitJson]) + return ( -
+
-
- -
+
From 386e23fcb772ebd2486d809c3eb0adf225a06d8f Mon Sep 17 00:00:00 2001 From: seveibar Date: Thu, 10 Oct 2024 16:44:57 -0700 Subject: [PATCH 6/6] minor typefix --- src/hooks/use-run-tsx/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/use-run-tsx/index.tsx b/src/hooks/use-run-tsx/index.tsx index 03f36199..e9721cdc 100644 --- a/src/hooks/use-run-tsx/index.tsx +++ b/src/hooks/use-run-tsx/index.tsx @@ -102,7 +102,7 @@ export const useRunTsx = ({ try { globalThis.React = React - const module = evalCompiledJs(compiledJs) + const module = evalCompiledJs(compiledJs!) if (Object.keys(module.exports).length > 1) { throw new Error(