diff --git a/client/src/components/LivePreview.tsx b/client/src/components/LivePreview.tsx new file mode 100644 index 0000000..2a6d534 --- /dev/null +++ b/client/src/components/LivePreview.tsx @@ -0,0 +1,50 @@ +import { useMemo } from 'react' + +/** + * Pull the most likely runnable code out of an artifact: the largest fenced code block if the agent + * wrapped its answer in markdown, otherwise the whole text. + */ +export function extractCode(artifact: string): string { + const blocks = [...artifact.matchAll(/```(?:tsx|jsx|ts|js|javascript|typescript)?\s*\n([\s\S]*?)```/g)].map((m) => m[1]) + if (blocks.length === 0) return artifact.trim() + return blocks.sort((a, b) => b.length - a.length)[0].trim() +} + +/** Builds a self-contained HTML document that transpiles the component (Babel) and renders it with + * React + Tailwind from CDN. The agent's code must define a component named `App`. */ +function harness(code: string): string { + const json = JSON.stringify(code) + return `
+ + + + + + + +` +} + +/** Runs an agent's React component artifact live, in a sandboxed iframe. */ +export function LivePreview({ artifact, className }: { artifact: string; className?: string }) { + const srcDoc = useMemo(() => harness(extractCode(artifact)), [artifact]) + return ( + + ) +} diff --git a/client/src/pages/BoardPage.tsx b/client/src/pages/BoardPage.tsx index cb7417b..ff3a44f 100644 --- a/client/src/pages/BoardPage.tsx +++ b/client/src/pages/BoardPage.tsx @@ -8,9 +8,10 @@ import { useSensors, type DragEndEvent, } from '@dnd-kit/core' -import { Bot, Plus, Trash2 } from 'lucide-react' +import { Bot, Play, Plus, Trash2 } from 'lucide-react' import { toast } from 'sonner' import { AppShell, REVIEWS_CHANGED } from '@/components/AppShell' +import { LivePreview } from '@/components/LivePreview' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { @@ -405,6 +406,7 @@ function TaskDrawer({ }) { const [busy, setBusy] = useState(false) const [seatId, setSeatId] = useState+ Runs the artifact as a React component (Babel + Tailwind, no build). Define a component named App. +
+