Ai Artifact
smoothui-registry/ai-artifactFreeComponent
Generated-artifact frame whose preview and code panes swap along a shared axis rather than cross-fading.
Install it
npx shadcn@latest add https://www.smoothui.dev/r/ai-artifact.jsonSource
1"use client";23import { cn } from "@/lib/utils";4import { Check, Copy } from "lucide-react";5import { AnimatePresence, motion, useReducedMotion } from "motion/react";6import { type ReactNode, useEffect, useId, useState } from "react";78const SPRING_DEFAULT = {9 bounce: 0.1,10 duration: 0.25,11 type: "spring" as const,12};13const EASE_OUT = [0.23, 1, 0.32, 1] as const;14const TRAVEL_PX = 24;15const COPIED_RESET_MS = 1600;1617export type AIArtifactPane = "preview" | "code";1819export type AIArtifactProps = {20 className?: string;21 /** The raw form — source, JSON, markup. */22 code?: ReactNode;23 /** Plain text handed to the clipboard. Omit to hide the copy action. */24 copyText?: string;25 /** Which pane to start on. */26 defaultPane?: AIArtifactPane;27 /** The rendered form. */28 preview?: ReactNode;29 /** Name the artifact so it can be referred to in conversation. */30 title: string;31};3233const PANES: AIArtifactPane[] = ["preview", "code"];3435/**36 * A frame around something the model produced.37 *38 * The two panes travel along a single horizontal axis — preview sits to the left39 * of code, always — so the swap has a direction and, after one go, a memory. A40 * cross-fade between them would leave the user with no sense of where the other41 * pane went, and no expectation of where it will come back from.42 */43const AIArtifact = ({44 className,45 code,46 copyText,47 defaultPane = "preview",48 preview,49 title,50}: AIArtifactProps) => {51 const shouldReduceMotion = useReducedMotion();52 const [pane, setPane] = useState<AIArtifactPane>(defaultPane);53 const [hasCopied, setHasCopied] = useState(false);54 const indicatorId = useId();5556 const available = PANES.filter((candidate) =>57 candidate === "preview" ? Boolean(preview) : Boolean(code)58 );5960 useEffect(() => {61 if (!hasCopied) {62 return;63 }64 const timeout = setTimeout(() => setHasCopied(false), COPIED_RESET_MS);65 return () => clearTimeout(timeout);66 }, [hasCopied]);6768 const copy = async () => {69 if (!copyText) {70 return;71 }72 try {73 await navigator.clipboard.writeText(copyText);74 setHasCopied(true);75 } catch {76 // A blocked clipboard is not worth an error state.77 }78 };7980 const direction = pane === "code" ? 1 : -1;8182 return (83 <div84 className={cn(85 "w-full overflow-hidden rounded-xl border border-border bg-background",86 className87 )}88 >89 <div className="flex items-center gap-2 border-border border-b px-2 py-1.5">90// … truncated
What it pulls in
npm packages
Files it writes
More from SmoothUI Registry
All 178 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Agent Avataragent-avatar | SmoothUI Registry | Components | Free | no deps | |
| Ai Approvalai-approval | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Branchai-branch | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Citationai-citation | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Context Meterai-context-meter | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Conversationai-conversation | SmoothUI Registry | Components | Free | 2 deps | |
| Ai Coreai-core | SmoothUI Registry | Components | Free | 1 dep | |
| Ai Diffai-diff | SmoothUI Registry | Components | Free | 2 deps |
