Artifact panel The conversation stays about the work and the panel holds the work. A generated document gets its own version pager, its own actions and its own scroll, while the transcript keeps a short reference to what was produced. npx shadcn@latest add resizable button npm i lucide-react Tokens this needs: --foreground, --background, --card, --card-foreground, --accent, --accent-foreground, --muted, --muted-foreground, --border, --input, --ring, --chart-1 ──────────────────────────────────────────────────────────────────────── // ArtifactPanel.tsx ──────────────────────────────────────────────────────────────────────── import { useState, type ReactNode } from "react"; import { Copy, Download, Play, Eye, Globe, FileText } from "lucide-react"; import { Button } from "@/components/ui/button"; import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"; export type Artifact = { title: string; /** Document, code or page. The actions follow the type. */ kind: "document" | "code" | "page"; /** Every version, oldest first. Six near-identical drafts in a transcript * have to be diffed by eye; here they page. */ versions: string[]; }; /** Copy and download for a document, run and preview for code, publish for * a page. A panel with the wrong verbs is a panel nobody uses. */ const ACTIONS = { document: [{ label: "copy", Icon: Copy }, { label: "download", Icon: Download }], code: [{ label: "run", Icon: Play }, { label: "preview", Icon: Eye }], page: [{ label: "publish", Icon: Globe }], } as const; /** The transcript keeps a short pointer, so the conversation stays about the * work and stays navigable. The work itself lives in the panel. */ export function ArtifactReference({ artifact, version, onOpen }: { artifact: Artifact; version: number; onOpen: () => void }) { return ( ); } export function ArtifactPanel({ artifact, onAction, onEdit, defaultVersion }: { artifact: Artifact; /** Which version opens first. Defaults to the newest. */ defaultVersion?: number; onAction: (label: string, version: number) => void; /** Fires on every edit made in the panel. The model has to see this next * turn; a panel whose contents diverge from what the model holds is the * worst failure here. */ onEdit: (version: number, text: string) => void; }) { const n = artifact.versions.length; const [v, setV] = useState(defaultVersion ?? n); const [dirty, setDirty] = useState(false); return (
{artifact.title}
edited here, and the model sees it next turn
)}make the intro shorter
Cut it to two sentences and moved the plan comparison up. v3 is in the panel.