Ai Response
smoothui-registry/ai-responseFreeComponent
Streaming assistant text where words animate in as they arrive, with a caret that rides the last glyph and inline citation pills.
Install it
npx shadcn@latest add https://www.smoothui.dev/r/ai-response.jsonSource
1"use client";23import { cn } from "@/lib/utils";4import { motion, useReducedMotion } from "motion/react";5import { Fragment, useEffect, useRef } from "react";67const EASE_OUT = [0.23, 1, 0.32, 1] as const;8/**9 * Hoisted and frozen. Motion restarts an animation whenever the transition it is10 * given changes, and this component re-renders on every token — so the object11 * has to be the same reference every time.12 */13const WORD_TRANSITION = { duration: 0.22, ease: EASE_OUT } as const;14const WORD_BLUR_PX = 4;15/** `[1]` style markers become citation pills. */16const CITATION_MARKER = /^\[(\d+)\]$/;17/**18 * Split on whitespace *and* on markers, so a marker is its own token even when19 * punctuation is glued to it — `compute [1],` has to yield `[1]` and `,`20 * separately or the pill never matches.21 */22const TOKEN_SPLIT = /(\s+|\[\d+\])/;23/** Anything without a letter or digit is punctuation and is not animated. */24const HAS_WORD_CHARACTER = /[\p{L}\p{N}]/u;25const WHITESPACE_ONLY = /^\s+$/;2627export type AIResponseCitation = {28 id: string;29 /** The number shown in the pill, matching the `[n]` marker in the text. */30 index: number;31 title: string;32 /**33 * Where the source lives, when it lives anywhere.34 *35 * Optional on purpose: most retrieval is over internal documents that have no36 * public URL, and a required field there just pushes people into inventing37 * `example.com` links that go nowhere. Without a url the pill renders as plain38 * text instead of a dead link.39 */40 url?: string;41};4243export type AIResponseProps = {44 /** Sources referenced by `[n]` markers in the text. */45 citations?: AIResponseCitation[];46 className?: string;47 /** Shows a caret after the last word. */48 isStreaming?: boolean;49 /** The response so far. Re-render it as it grows. */50 text: string;51};5253type Token = {54 citation?: AIResponseCitation;55 value: string;56};5758const tokenize = (text: string, citations: AIResponseCitation[]): Token[] =>59 text60 .split(TOKEN_SPLIT)61 .filter((value) => value !== "")62 .map((value) => {63 const match = value.match(CITATION_MARKER);64 if (!match) {65 return { value };66 }67 const index = Number(match[1]);68 const citation = citations.find((entry) => entry.index === index);69 return citation ? { citation, value } : { value };70 });7172/**73 * Streaming assistant text.74 *75 * Words animate in as they *arrive*, not on a fixed timer — the component76// … 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 Artifactai-artifact | 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 |
