From e26f30467503877ae31516a4d5a5b4a0b5c2633a Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Tue, 16 Jun 2026 23:35:28 +0330 Subject: [PATCH] Run it in TeamUp: live React preview of a task's artifact MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds LivePreview — a sandboxed iframe that transpiles an agent's component with Babel and renders it live with React + Tailwind from CDN (no build step), so a generated page runs inside TeamUp. A "Preview" button on any task with an artifact opens a full-screen live view. Pairs with steering the engineer agent to output a single self-contained App component. Co-Authored-By: Claude Opus 4.8 --- client/src/components/LivePreview.tsx | 50 +++++++++++++++++++++++++++ client/src/pages/BoardPage.tsx | 25 ++++++++++++-- 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 client/src/components/LivePreview.tsx 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 ( +