关于 nexus-ui
Nexus UI 是用于构建 AI 界面的开源 React 组件库,面向需要聊天、流式输出和多模态界面的开发者。组件通过 shadcn registry 以源代码形式安装,基于 Tailwind CSS v4 和 Radix UI。
主要功能
- 01提供聊天与提示词输入组件
- 02支持模型选择和提示建议
- 03提供附件预览与管理界面
- 04包含消息、线程和引用组件
- 05呈现工具调用与推理步骤状态
适用场景
- 01搭建 AI 聊天输入与消息界面
- 02在对话中显示附件和引用来源
- 03展示工具运行、推理和加载状态
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ victorcodess/ nexus-ui --skill nexus-ui安装后在 Claude Code 中输入/nexus-ui确认已加载。
详细介绍
Nexus UI
用于构建 AI 驱动界面的开源组件库。为聊天、流式传输和多模态体验打造的可组合、可复制粘贴的基础组件,基于 React、Tailwind CSS v4 和 Radix UI 构建。
组件通过 shadcn 注册表分发,并以源代码的形式安装到用户的项目中。
IMPORTANT: 请根据项目的
packageManager,使用项目的包运行器执行 CLI 命令:npx shadcn@latest、pnpm dlx shadcn@latest或bunx --bun shadcn@latest。
安装
1. 添加注册表
将 Nexus UI 注册表添加到项目的 components.json 中:
{
"registries": {
"@nexus-ui": "https://nexus-ui.dev/r/{name}.json"
}
}
2. 添加组件
npx shadcn@latest add @nexus-ui/prompt-input
npx shadcn@latest add @nexus-ui/model-selector
npx shadcn@latest add @nexus-ui/suggestions
npx shadcn@latest add @nexus-ui/attachments
npx shadcn@latest add @nexus-ui/message
npx shadcn@latest add @nexus-ui/thread
npx shadcn@latest add @nexus-ui/citation
npx shadcn@latest add @nexus-ui/reasoning
npx shadcn@latest add @nexus-ui/text-shimmer
npx shadcn@latest add @nexus-ui/image
npx shadcn@latest add @nexus-ui/feedback-bar
npx shadcn@latest add @nexus-ui/toaster
npx shadcn@latest add @nexus-ui/chain-of-thought
npx shadcn@latest add @nexus-ui/tool
npx shadcn@latest add @nexus-ui/questions
也可以通过 URL 直接安装(无需配置注册表):
npx shadcn@latest add https://nexus-ui.dev/r/prompt-input.json
默认情况下,组件会安装到 components/nexus-ui/。
前置条件
- React 19+
- Tailwind CSS v4
- TypeScript
- 已初始化 shadcn/ui(
npx shadcn@latest init)
可用组件
| 组件 | 注册表名称 | 说明 |
|---|---|---|
| 提示词输入框 | prompt-input | 可组合的聊天输入框,带有自动调整大小的文本区域和操作插槽 |
| 模型选择器 | model-selector | 用于选择 AI 模型的下拉菜单,支持单选组、子菜单和自定义条目 |
| 建议 | suggestions | 用于引导用户输入的提示词建议标签 |
| 附件 | attachments | 用于聊天输入和消息的可组合文件附件,支持预览、变体和上传接线 |
| 消息 | message | 聊天消息布局,支持 Markdown、可选头像、操作和附件 |
| 对话线程 | thread | 可滚动的聊天线程,支持贴底滚动和跳转到底部的控件 |
| 引用 | citation | 内联来源引用,支持悬停预览和多来源轮播 |
| 推理 | reasoning | 可折叠的模型推理轨迹,带有适配流式传输的标签和 Markdown 正文 |
| 文本闪烁效果 | text-shimmer | 动画闪烁文本,用于加载、工具运行及其他进行中的 UI |
| 图像 | image | 用于 URL、base64 和字节载荷的图像渲染器,支持预览、加载器、灯箱和操作插槽 |
| 反馈栏 | feedback-bar | 反馈提示栏,可对单条消息或整个线程评分,并带有操作和关闭插槽 |
| 通知提示 | toaster | 由 Sonner 提供支持的无头 Toast 通知,支持按变体设置样式以及自定义操作/取消控件 |
| 思维链 | chain-of-thought | 结构化的多步思考时间线,支持步骤状态、可选的可展开输出,以及步骤完成时自动关闭 |
| 工具 | tool | 可感知状态的工具调用 UI,支持 JSON 输入/输出代码块(pending、ready、running、completed、error) |
| 问题 | questions | 后续澄清问题,支持单选或多选、轮播导航和批量提交 |
组件 API
每个部分都采用相同的大纲:导入 → 组成部分 → 根属性 → 属性和钩子 → 用法说明 → 示例(适用时提供简短代码片段)。
提示词输入
可组合的聊天输入框:自动增高的文本区域,以及用于附件、发送和类似控件的底部操作栏。
导入:
import {
PromptInput,
PromptInputTextarea,
PromptInputActions,
PromptInputActionGroup,
PromptInputAction,
} from "@/components/nexus-ui/prompt-input";
组成部分:
| 部分 | 用途 |
|---|---|
PromptInput | 根布局:带边框的卡片,包含文本区域和操作控件。 |
PromptInputTextarea | 自动调整大小的文本区域(最大高度约为 160px),位于 shadcn ScrollArea 内。 |
PromptInputActions | 底部栏;使用 justify-between 分隔左右两组操作控件。 |
PromptInputActionGroup | 操作控件的水平分组(gap-2)。 |
PromptInputAction | 单个控件的插槽;支持 asChild,并可通过 tooltip(字符串或对象)启用内置工具提示。 |
根组件属性: PromptInput 上的可选 onSubmit(value: string),用于在 PromptInputTextarea 中按 Enter 提交(Shift+Enter 换行),以及常规 DOM 属性 / className。
属性和钩子: 每个组成部分都接受 className。PromptInputTextarea 接受标准文本区域属性。PromptInputAction 支持 asChild 和 tooltip?: string | { content?: string; side?: "top" | "right" | "bottom" | "left"; shortcut?: string }(缺少 content 时不会渲染工具提示)。没有上下文钩子。
使用说明: 将附件 / 工具放在第一个 PromptInputActionGroup 中,将发送控件放在第二个分组中,以便通过 justify-between 将它们分开。建议在 Button 外层使用 PromptInputAction asChild,而不是直接将 PromptInputAction 用作可点击元素。
示例:
<PromptInput>
<PromptInputTextarea placeholder="Ask anything..." />
<PromptInputActions>
<PromptInputActionGroup>
<PromptInputAction asChild>
<Button variant="ghost" size="icon" className="rounded-full">
<Paperclip />
</Button>
</PromptInputAction>
</PromptInputActionGroup>
<PromptInputActionGroup>
<PromptInputAction asChild>
<Button size="icon" className="rounded-full">
<ArrowUp />
</Button>
</PromptInputAction>
</PromptInputActionGroup>
</PromptInputActions>
</PromptInput>
模型选择器
用于选择模型(或类似值)的下拉菜单,基于 Radix DropdownMenu 构建,并可选用 items 元数据提供标签、描述和图标。
导入:
import {
ModelSelector,
ModelSelectorPortal,
ModelSelectorTrigger,
ModelSelectorContent,
ModelSelectorGroup,
ModelSelectorLabel,
ModelSelectorItemTitle,
ModelSelectorItemDescription,
ModelSelectorItemIcon,
ModelSelectorItemIndicator,
ModelSelectorItem,
ModelSelectorCheckboxItem,
ModelSelectorRadioGroup,
ModelSelectorRadioItem,
ModelSelectorSeparator,
ModelSelectorSub,
ModelSelectorSubTrigger,
ModelSelectorSubContent,
} from "@/components/nexus-ui/model-selector";
组成部分:
| 部分 | 用途 |
|---|---|
ModelSelector | 根级提供者和下拉菜单根组件;管理选择上下文(参见 根组件属性)。 |
ModelSelectorPortal | 可选的菜单内容 portal 包装器(在将内容渲染到 portal 中时使用)。 |
ModelSelectorTrigger | 打开菜单;设置了 items 时显示所选标签。 |
ModelSelectorContent | 菜单表面 / 面板。 |
ModelSelectorGroup | 对行进行分组;通常与 ModelSelectorLabel 配合使用。 |
ModelSelectorLabel | 组内的分区标题。 |
ModelSelectorItem | 通用菜单行。 |
ModelSelectorItemTitle | 菜单项的主文本行。 |
ModelSelectorItemDescription | 菜单项的次文本行。 |
ModelSelectorItemIcon | 菜单项的前置图标插槽。 |
ModelSelectorItemIndicator | 选择指示器(例如勾选标记)。 |
ModelSelectorCheckboxItem | 复选框样式的菜单项。 |
ModelSelectorRadioGroup | 单选组;传入 Radix value / onValueChange。 |
ModelSelectorRadioItem | 单选行;支持 title、description、icon、indicator。 |
ModelSelectorSeparator | 分区之间的视觉分隔线。 |
ModelSelectorSub | 嵌套子菜单容器。 |
ModelSelectorSubTrigger | 用于展开子菜单的行。 |
ModelSelectorSubContent | 嵌套项的弹出层主体。 |
根组件属性: ModelSelector 上必须提供 value 和 onValueChange。可选的 items:{ value, title, description?, icon? }[],用于填充触发器文本和内部元数据。其他 Radix 菜单根组件属性均适用,但根组件上的 value / onValueChange 专用于此 API。
属性和钩子: ModelSelectorTrigger 增加了 variant:filled | outline | ghost(默认值为 filled)。选择状态由你的 React 状态(value / onValueChange)管理;除 Radix 行为外,没有其他钩子。
使用说明: 独占选择建议使用 ModelSelectorRadioGroup + ModelSelectorRadioItem;需要多个切换项时使用 ModelSelectorCheckboxItem。使用 ModelSelectorSub / SubTrigger / SubContent 嵌套高级菜单树。较长的菜单建议使用 ModelSelectorGroup、ModelSelectorLabel 和 ModelSelectorSeparator。
示例:
// models: { value: string; title: string; description?: string; icon?: ComponentType<{ className?: string }> }[]
const [model, setModel] = React.useState(models[0].value);
<ModelSelector value={model} onValueChange={setModel} items={models}>
<ModelSelectorTrigger />
<ModelSelectorContent align="start">
<ModelSelectorGroup>
<ModelSelectorLabel>Select model</ModelSelectorLabel>
<ModelSelectorRadioGroup value={model} onValueChange={setModel}>
{models.map((m) => (
<ModelSelectorRadioItem
key={m.value}
value={m.value}
icon={m.icon}
title={m.title}
description={m.description}
/>
))}
</ModelSelectorRadioGroup>
</ModelSelectorGroup>
</ModelSelectorContent>
</ModelSelector>
建议项
用于快速提示词的标签,可选择使用动画面板将建议项分组展示。
导入:
import Suggestions, {
SuggestionList,
Suggestion,
SuggestionPanel,
SuggestionPanelHeader,
SuggestionPanelTitle,
SuggestionPanelClose,
SuggestionPanelContent,
} from "@/components/nexus-ui/suggestions";
组成部分:
| 组成部分 | 用途 |
|---|---|
Suggestions | 根组件;可选的 onSelect(value: string),用于处理标签点击(通过上下文)。 |
SuggestionList | 标签的弹性布局列表;orientation 可设为 "horizontal" | "vertical"。 |
Suggestion | 标签 Button;variant 可设为 filled | outline | ghost;可选的 value、highlight。 |
SuggestionPanel | 包含分组建议项的动画容器。 |
SuggestionPanelHeader | 面板内的顶部栏。 |
SuggestionPanelTitle | 标题栏中的标题行。 |
SuggestionPanelClose | 关闭控件;支持 asChild。 |
SuggestionPanelContent | 主体区域;支持 asChild。 |
根组件属性: Suggestions — 可选的 onSelect(value: string)。SuggestionPanel — 可选的 open(默认值为 true)、onOpenChange、onClose(退出动画结束后运行)。
属性与钩子: 无选择器钩子;通过 onSelect 和 Suggestion 的 Button 属性连接行为。Suggestion 不使用外层的 variant,而是采用上方的 Nexus variant 联合类型。
使用说明: 可组合多个 SuggestionList 区块,也可将列表置于 SuggestionPanel 中,以逐步展示内容。highlight 会弱化标签文本中匹配的子字符串。可在标题栏中使用 SuggestionPanelClose 提供关闭操作。
示例:
// Often paired with prompt state: onSelect((q) => setInput(q))
<Suggestions onSelect={(q) => setInput(q)}>
<SuggestionList>
<Suggestion value="Summarize this thread">Summarize this thread</Suggestion>
<Suggestion value="Draft a reply">Draft a reply</Suggestion>
</SuggestionList>
</Suggestions>
附件
为提示词或消息行提供受控文件预览:隐藏的文件输入框、可选的窗口级拖放功能,以及每个项目的移除/进度 UI。
导入:
import Attachments, {
AttachmentTrigger,
AttachmentList,
Attachment,
AttachmentPreview,
AttachmentRemove,
AttachmentInfo,
AttachmentProperty,
AttachmentProgress,
AttachmentsDropOverlay,
useAttachments,
toAttachmentMeta,
filesFromDataTransfer,
} from "@/components/nexus-ui/attachments";
组成部分:
| 组成部分 | 用途 |
|---|---|
Attachments | 根级提供程序:隐藏的 <input type="file">、验证、Blob URL 生命周期管理,以及可选的 window 拖放监听器。 |
AttachmentTrigger | 打开文件选择器(构建自定义触发器时,可搭配 useAttachments().inputId/标签使用)。 |
AttachmentList | 承载各个 Attachment 的水平滚动区域/弹性布局行。 |
Attachment | 单个磁贴;传入 attachment: AttachmentMeta;variant 可设为 compact | inline | detailed | pasted。 |
AttachmentPreview | 缩略图或类型图标区域(上下文:当前附件)。 |
AttachmentInfo | AttachmentProperty 行的文本列包装器。 |
AttachmentProperty | 通过 as="name" | "size" | "kind" 渲染 name、size 或文件 kind。 |
AttachmentRemove | 移除当前项目(如果提供了 Attachment 的 onRemove,则调用它)。 |
AttachmentProgress | 细进度条;value 范围为 0–100。 |
AttachmentsDropOverlay | 拖动期间显示的投影叠加层;variant 可设为 fullscreen | contained。 |
根组件属性: Attachments 必须传入 attachments 和 onAttachmentsChange。可选属性:accept、multiple(默认值为 true)、maxFiles、maxSize(单个文件的大小上限,单位为字节)、disabled、onFileInputChange、onFilesRejected、windowDrop。
属性与钩子: useAttachments() — 仅可在 <Attachments> 内使用 — 提供 inputRef、inputId、openPicker、appendFiles、isDraggingFile、当前的 attachments、onAttachmentsChange、限制值和 disabled。导出的类型:AttachmentMeta、AttachmentsRejectedFiles、AppendFilesOptions。辅助函数:toAttachmentMeta(file, options?)、用于粘贴/拖放的 filesFromDataTransfer。
使用说明: 在自定义拖放/粘贴处理函数中调用 appendFiles,即可复用原生文件选择器的相同限制。启用 windowDrop 并搭配 AttachmentsDropOverlay,即可显示全应用范围的文件拖放提示。当受控的 attachments 数组中移除项目时,会撤销对应的对象 URL。
示例:
const [items, setItems] = React.useState<AttachmentMeta[]>([]);
<Attachments attachments={items} onAttachmentsChange={setItems} accept="image/*" maxFiles={5}>
<AttachmentTrigger />
<AttachmentList>
{items.map((meta, index) => (
<Attachment
key={meta.url ?? `${meta.name}-${index}`}
attachment={meta}
variant="compact"
onRemove={() => setItems(items.filter((_, i) => i !== index))}
/>
))}
</AttachmentList>
</Attachments>
消息
用户和助手消息行,包含可选头像、Markdown 正文(通过 Streamdown)和末尾操作。
导入:
import {
Message,
MessageStack,
MessageContent,
MessageMarkdown,
MessageActions,
MessageActionGroup,
MessageAction,
MessageAvatar,
} from "@/components/nexus-ui/message";
组成部分:
| 部分 | 用途 |
|---|---|
Message | 根组件;必填 from:"user" | "assistant"(布局 + role="article")。 |
MessageStack | 用于在一条消息内纵向堆叠多个区块。 |
MessageContent | 气泡 / 内容包装器(用户和助手的样式不同)。 |
MessageMarkdown | 使用 Streamdown 插件(代码、数学公式、mermaid、CJK)渲染 Markdown。 |
MessageActions | 操作行容器。 |
MessageActionGroup | 将操作按钮分组。 |
MessageAction | 单个操作;支持 asChild,并可通过 tooltip(字符串或对象)启用内置提示框。 |
MessageAvatar | 头像列;包含 src、可选的 fallback、alt、size、delayMs。 |
根组件属性: Message 上必填 from:"user" | "assistant"。标准的 HTMLAttributes<HTMLDivElement> 适用于 article 包装器。除非传入 aria-label 或 aria-labelledby,否则默认 aria-label 为 "User message" 或 "Assistant message"。
属性和 Hook: 不导出上下文 Hook。后代组件(MessageStack、MessageContent、MessageActions 等)从内部上下文读取 from(在 Message 外部使用时,回退为 assistant)。MessageMarkdown 接受 Streamdown 属性(ComponentProps<typeof Streamdown>):例如,通过 children 传入 Markdown,可选的 components 会与内置的 code / table / inlineCode 默认值合并,还可传入 className。MessageAction 支持 asChild(Radix Slot)和 tooltip?: string | { content?: string; side?: "top" | "right" | "bottom" | "left"; shortcut?: string }(缺少 content 时不渲染提示框)。MessageAvatar:必填 src;可选 alt、fallback、delayMs、size、className。
使用说明: 注册表安装会在 message.tsx 旁添加 codeblock.tsx。手动安装时,合并注册表条目中的 Streamdown 相关 CSS。建议在 Button 外使用 MessageAction asChild 来创建操作。
示例:
<Message from="assistant">
<MessageAvatar src="https://github.com/shadcn.png" fallback="AI" />
<MessageStack>
<MessageContent>
<MessageMarkdown>Here is **markdown** and `inline code`.</MessageMarkdown>
</MessageContent>
<MessageActions>
<MessageActionGroup>
<MessageAction asChild>
<Button type="button" variant="ghost" size="sm">
Copy
</Button>
</MessageAction>
</MessageActionGroup>
</MessageActions>
</MessageStack>
</Message>
<Message from="user">
<MessageContent>
<MessageMarkdown>Short user reply</MessageMarkdown>
</MessageContent>
</Message>
线程
封装 use-stick-to-bottom,用于创建可滚动的消息列表,使其始终停留在新内容处,并在用户向上滚动时显示浮动控件。
导入:
import {
Thread,
ThreadContent,
ThreadScrollToBottom,
} from "@/components/nexus-ui/thread";
组成部分:
| 部分 | 用途 |
|---|---|
Thread | 根组件;转发 StickToBottom 属性(resize、initial、弹簧选项等)。 |
ThreadContent | 消息的可滚动列(flex flex-col gap-*)。 |
ThreadScrollToBottom | 当未处于底部时显示的按钮(或 asChild);调用 scrollToBottom。 |
根组件属性: Thread 是带有默认值 resize="smooth" 和 initial="smooth" 的 StickToBottom。可选的 StickToBottomOptions:resize、initial、mass、damping、stiffness、targetScrollTop。还包括 instance(预先创建的 stick-to-bottom 实例)、contextRef,以及外层 div 上的标准 HTMLAttributes。
属性和 Hook: 在 Thread 内部,使用 use-stick-to-bottom 中的 useStickToBottomContext() 获取 isAtBottom、scrollToBottom、state 等。ThreadScrollToBottom 内部使用该上下文;如果已经处于底部,则返回 null。它接受 asChild 和标准按钮 HTML 属性(未设置 asChild 时,会渲染带样式的 <button>)。
使用说明: 将 ThreadContent 和 ThreadScrollToBottom 作为同级组件放在 Thread 内部。为 Thread 设置有界高度(例如 h-full / min-h-*),以定义滚动区域。
示例:
<Thread className="min-h-[480px]">
<ThreadContent>
{messages.map((m) => (
<Message key={m.id} from={m.role === "user" ? "user" : "assistant"}>
<MessageContent>
<MessageMarkdown>{m.text}</MessageMarkdown>
</MessageContent>
</Message>
))}
</ThreadContent>
<ThreadScrollToBottom />
</Thread>
引用
可组合的内联引用标记,带有 Radix HoverCard 预览。多个 URL 会在面板内使用轮播(CitationCarousel*)。
导入:
import {
Citation,
CitationTrigger,
CitationContent,
CitationSource,
CitationSourcesBadge,
CitationItem,
CitationFavicon,
CitationSiteName,
CitationCarousel,
CitationCarouselContent,
CitationCarouselHeader,
CitationCarouselItem,
CitationCarouselPrev,
CitationCarouselNext,
CitationCarouselPagination,
CitationCarouselIndex,
} from "@/components/nexus-ui/citation";
辅助函数: resolveCitationSources、resolveCitationSource、parseCitationUrl、rootDomainSiteName,类型 CitationSourceInput、ResolvedCitation。
组成部分:
| 部分 | 用途 |
|---|---|
Citation | 根 HoverCard;管理已解析的来源和轮播索引状态。 |
CitationTrigger | 标记触发器;单个来源渲染为链接,多个来源渲染为 span + “+N”。 |
CitationContent | 悬浮面板表面(默认 align="center"、sideOffset={4})。 |
CitationCarousel | Shadcn Carousel;将 setApi 接入引用上下文。 |
CitationCarouselContent / Item | 幻灯片;每个 CitationCarouselItem 都需要 index。 |
CitationCarouselPrev / Next / Index / Pagination / Header | 导航控件。 |
CitationItem | 默认预览正文(标题、描述、CitationSource)或自定义子项;锚点指向 href。 |
CitationSource | 页脚行:网站图标 + 网站名称。 |
CitationFavicon / CitationSiteName | 从当前轮播项或 CitationItem 作用域解析。 |
CitationSourcesBadge | 堆叠的网站图标 + “N 个来源”(或自定义 label);用于工具栏,而非悬浮标记。 |
根组件属性: 在 Citation 上,必须提供 citations: CitationSourceInput[](url,以及可选的 title、description)。其他所有属性均省略 HoverCard 的 children 并透传;可选的 children 用于组合触发器和内容。若 citations 为空,则渲染 null。根卡片的 openDelay / closeDelay 设为 50。
属性和 Hook: 不导出 Hook;CitationItem、CitationFavicon 等需要祖先 Citation,且(对于轮播内的项目)需要匹配的、带有 index 的 CitationCarouselItem。CitationTrigger:可选的 label、showFavicon(默认 true)、showSiteName(默认 true)。CitationCarouselItem:必需的 index: number。CitationItem:可选的 showTitle、showDescription、showSource(默认均为 true);href 默认为已解析的 URL。CitationSourcesBadge:可选的 showFavicons、label。
使用说明: 从 RAG / 搜索工作流构建 citations;如果自行规范化 URL,请使用 resolveCitationSources。对于两个或更多 URL,请在 CitationContent 内放置 CitationCarousel,并为每个索引放置一个 CitationCarouselItem。CitationSourcesBadge 是可选的界面控件,可用于消息标题或操作区,但仍须位于 Citation 下。
示例(单个来源):
<Citation
citations={[
{
url: "https://vercel.com/blog",
title: "Vercel Blog",
description: "Product and platform updates.",
},
]}
>
<CitationTrigger />
<CitationContent>
<CitationItem />
</CitationContent>
</Citation>
示例(多个来源 + 轮播):
const sources = [
{ url: "https://example.com/a", title: "Source A" },
{ url: "https://example.com/b", title: "Source B" },
];
<Citation citations={sources}>
<CitationTrigger />
<CitationContent>
<CitationCarousel>
<CitationCarouselHeader>
<CitationSourcesBadge />
<CitationCarouselPagination>
<CitationCarouselPrev />
<CitationCarouselIndex />
<CitationCarouselNext />
</CitationCarouselPagination>
</CitationCarouselHeader>
<CitationCarouselContent>
{sources.map((_, index) => (
<CitationCarouselItem key={index} index={index}>
<CitationItem />
</CitationCarouselItem>
))}
</CitationCarouselContent>
</CitationCarousel>
</CitationContent>
</Citation>
推理
基于 shadcn Collapsible 构建的可折叠“思考”区块:触发器标签会从流式状态文案切换为耗时摘要,正文通过 Streamdown 渲染 Markdown。
导入:
import {
Reasoning,
ReasoningTrigger,
ReasoningContent,
} from "@/components/nexus-ui/reasoning";
组成部分:
| 组成部分 | 用途 |
|---|---|
Reasoning | 根级 Provider 和可折叠根组件;管理打开状态与 isStreaming,并生成上下文相关的触发器标签(Thinking… → Thought for N seconds)。 |
ReasoningTrigger | 包含脑图标、标签文本(流式状态下会在 span 上使用 tw-shimmer 工具类)和在打开时旋转的箭头图标的行。 |
ReasoningContent | 展开后使用 Streamdown 渲染 Markdown,并应用弱化样式和左侧边框条。需要 children: string(Markdown 源文本)。 |
根组件属性: 在 Reasoning 上,isStreaming 为可选项(默认 false),用于在流式传输期间自动打开,并在流式传输结束时自动关闭和记录耗时;open / defaultOpen / onOpenChange 为可选的受控属性(继承可折叠组件的语义,但 Reasoning 的类型省略了 Radix 的 open/defaultOpen/onOpenChange,改用列出的属性)。还接受其余与 Collapsible 兼容的属性以及 className。
属性与钩子: 不导出钩子(仅使用内部上下文)。ReasoningTrigger/ReasoningContent 必须是 Reasoning 的后代。通过 Registry 安装会引入 collapsible、streamdown、Hugeicons 包、@import "tw-shimmer",以及用于可折叠高度动画的关键帧。
使用说明: 根据聊天传输层或部分流切换 isStreaming。省略 ReasoningTrigger 的子元素即可使用计算出的标签;只有在兼顾无障碍要求时,才传入子元素以覆盖可见标签。
示例:
<Reasoning isStreaming={status === "streaming"}>
<ReasoningTrigger />
<ReasoningContent>{reasoningMarkdownString}</ReasoningContent>
</Reasoning>
文本微光
单个基础组件,可在文本上动画显示移动的高亮(bg-clip-text + 注入的 @keyframes)。除了 @/lib/utils,不需要其他 UI 依赖。
导入:
import { TextShimmer } from "@/components/nexus-ui/text-shimmer";
组成部分: 单个组件 — TextShimmer。可选的多态根元素,通过 as 指定(默认 "span")。
根组件属性: duration — 高亮扫过的时长(秒,默认 2.5);repeatDelay — 下次扫过前的暂停时间(默认 0);spread — 高亮相对于中心的宽度(百分点,约束在 5–45 之间,默认 20);angle — 添加到渐变轴的角度(默认 0);color — 覆盖光束的色标颜色;invert — 翻转默认的明暗光束对比度;以及标准 HTML 属性、className 和 style。子元素为标签文本/节点树。
属性与钩子: 无上下文。通过 useId 为每个实例注入唯一的 @keyframes 名称。
使用说明: 建议用于较短的状态文本(“Running tools…”)。如果边缘颜色应继承自排版样式,可在包装元素上配合弱化的 text-muted-foreground 使用。
示例:
<TextShimmer repeatDelay={0.5}>Running tool calls</TextShimmer>
图像
适用于多模态模型的可组合图像卡片:接受 src、base64 或 uint8Array(以及 mediaType),将内容包裹在 Radix Dialog 根组件中,以便通过灯箱查看,并提供加载器和绝对定位的操作区域。
导入:
import {
Image,
ImagePreview,
ImageLightbox,
ImageLightboxOverlay,
ImageLightboxPreview,
ImageLightboxClose,
ImageLoader,
ImageActions,
ImageActionGroup,
ImageAction,
} from "@/components/nexus-ui/image";
组成部分:
| 部分 | 用途 |
|---|---|
Image | Dialog 内的根 div;解析 src、来自 base64 的数据 URL,或来自 uint8Array 的对象 URL;提供上下文;默认子项为 ImagePreview。 |
ImagePreview | 缩略图;如果 src 已解析,则将 <img> 包裹在 Dialog Trigger 中;内容为空时显示 ImageLoader。 |
ImageLightbox | Dialog.Portal 包装器。 |
ImageLightboxOverlay | 模态背景遮罩。 |
ImageLightboxPreview | Dialog.Content,包含大尺寸 <img>(object-contain)和屏幕阅读器标题。 |
ImageLightboxClose | 视觉隐藏的关闭控件(sr-only)。 |
ImageLoader | 脉冲骨架屏;通过上下文反映错误状态。 |
ImageActions | 绝对定位的操作栏;align:inline-start | inline-end | block-start | block-end。 |
ImageActionGroup / ImageAction | 分组控件;ImageAction 支持 asChild + Slot,并可通过 tooltip(字符串或对象)使用内置工具提示。 |
根组件属性: 在 Image 上传入 src、base64 或 uint8Array 之一;可选传入 mediaType、alt;还可传入 Radix 对话框根组件属性(open、defaultOpen、onOpenChange、modal)以及常规 div HTML 属性 / className。
属性与钩子: 仅使用内部上下文(不导出钩子)。嵌套部分必须位于 Image 下。
使用说明: 在 Image 上省略 children 可使用默认预览。将 ImageLightbox* 作为根卡片的同级元素组合使用(文档中的常见模式),这样点击预览即可打开模态框。使用 ImageActions + ImageAction asChild 包裹 Button 以进行下载或分享;在 ImageAction 上添加 tooltip 可设置标签、显示位置和可选快捷键。
示例(最简):
<Image base64={modelImageBase64} alt="Generated scene">
<ImagePreview />
<ImageLightbox>
<ImageLightboxOverlay />
<ImageLightboxPreview />
<ImageLightboxClose />
</ImageLightbox>
</Image>
反馈栏
用于消息或线程中“这有帮助吗?”类流程的紧凑栏:左侧放置 提示词(标签),右侧放置 操作(例如点赞),并可选添加 关闭列。FeedbackBarAction 可通过 asChild 包裹实际按钮,并显示带可选 键盘快捷键标记的 工具提示(shadcn Tooltip + Kbd)。
导入:
import {
FeedbackBar,
FeedbackBarContent,
FeedbackBarPrompt,
FeedbackBarLabel,
FeedbackBarActions,
FeedbackBarAction,
FeedbackBarClose,
} from "@/components/nexus-ui/feedback-bar";
组成部分:
| 部分 | 用途 |
|---|---|
FeedbackBar | 根 role="group" 条;默认高度为 h-12,宽度占满(最大约 28rem),带边框的 rounded-[12px] 表面。 |
FeedbackBarContent | 填满栏的 justify-between 弹性行。 |
FeedbackBarPrompt | 左侧区域(默认 pl-4),用于放置文案和前置内容。 |
FeedbackBarLabel | 主要提示词文本(font-[350])。 |
FeedbackBarActions | 右侧控件组区域(gap-1.5)。 |
FeedbackBarAction | 单个控件容器;支持 asChild + Slot;可选 tooltip:string 或 { content?, side?, shortcut? }。 |
FeedbackBarClose | 右侧窄栏,带前置边框;使用 FeedbackBarAction + asChild 包裹宿主 div;将关闭 Button/IconButton 作为子项传入。 |
根组件属性: 每个部分都使用普通的 HTMLAttributes;插槽部分接受 className。FeedbackBarAction / FeedbackBarClose:参见上文的工具提示说明。
属性与钩子: 无上下文。对于设置了 tooltip.content 的操作,每个操作都会单独渲染一个本地 TooltipProvider。
使用说明: 将图标按钮放入 FeedbackBarAction asChild 中,以提供合适的点击区域。若要对应实际的键盘操作,建议在 tooltip 中使用 shortcut。
示例:
<FeedbackBar>
<FeedbackBarContent>
<FeedbackBarPrompt>
<FeedbackBarLabel>Was this response helpful?</FeedbackBarLabel>
</FeedbackBarPrompt>
<FeedbackBarActions>
<FeedbackBarAction asChild tooltip={{ content: "Helpful", shortcut: "⌘Y" }}>
<button type="button" aria-label="Helpful">…</button>
</FeedbackBarAction>
</FeedbackBarActions>
<FeedbackBarClose>
<button type="button" aria-label="Dismiss">…</button>
</FeedbackBarClose>
</FeedbackBarContent>
</FeedbackBar>
Toaster
由 Sonner 驱动的无头 Toast 通知,支持按变体设置样式以及自定义操作/取消控件。使用 next-themes 支持浅色/深色/系统主题。导出 Toaster(在根布局中挂载一次)和带有便捷辅助方法及 toast.dismiss 的 toast 对象。
导入:
import { Toaster, toast } from "@/components/nexus-ui/toaster";
组成部分/API:
| 导出项 | 用途 |
|---|---|
Toaster | 使用 toastOptions={{ unstyled: true }} 渲染 <Sonner />,并通过 useTheme() 获取主题;展开传入额外的 ToasterProps。 |
toast.custom | 构建完全自定义的内容(title、可选的 description、variant、icon、action、cancel、Sonner 计时/选项)。 |
toast.default / success / info / warning / error / loading | 简写形式 (title, options?),委托给 toast.custom。 |
toast.dismiss | 转发到 Sonner 的关闭 API。 |
根级属性:Toaster 支持标准 Sonner 属性(position、duration、expand 等),以及 Sonner 提供的 className 合并行为。
**属性与钩子:**需要在渲染 Toaster 的布局上方提供 next-themes 中的 ThemeProvider,以便 useTheme() 正常解析。Toast 内容使用 shadcn 的 Button 作为操作/取消/关闭按钮。
**使用说明:**将 <Toaster /> 放在应用外框旁边(例如 layout.tsx)。快速反馈时优先使用 toast.success("Copied");需要取消按钮或自定义图标时使用 toast.custom(使用 icon: null 可隐藏默认图标)。
示例:
// layout.tsx
<Toaster position="bottom-right" />
// anywhere client-side
toast.success("Saved");
toast.error("Something went wrong", { description: "Try again.", duration: 5000 });
Chain of Thought
用于工具/推理步骤的结构化时间线:一个根级可折叠区域跟踪所有子步骤的状态;活动标签可选择显示闪烁效果(通过 tw-shimmer);useOnChange 会在步骤转换时控制自动展开/关闭。
导入:
import {
ChainOfThought,
ChainOfThoughtTrigger,
ChainOfThoughtContent,
ChainOfThoughtStep,
ChainOfThoughtStepTitle,
ChainOfThoughtStepContent,
ChainOfThoughtComplete,
type ChainOfThoughtStepStatus,
} from "@/components/nexus-ui/chain-of-thought";
组成部分:
| 组成部分 | 用途 |
|---|---|
ChainOfThought | 根级 Collapsible 和步骤上下文注册表;属性 autoCloseOnAllComplete(默认值为 true)会在每个步骤都为 completed 时关闭。 |
ChainOfThoughtTrigger | 带可选图标/标签和箭头的标题行;任务进行期间标签会闪烁。 |
ChainOfThoughtContent | 用于容纳 ChainOfThoughtStep 列表的主体容器。 |
ChainOfThoughtStep | 每个步骤的 Collapsible;status 控制注册与闪烁;hasContent 和 autoCloseOnComplete 会自动管理展开状态。 |
ChainOfThoughtStepTitle | 带图标和标签的行;存在可展开内容时会变为 CollapsibleTrigger(也可强制设置 collapsible)。 |
ChainOfThoughtStepContent | 步骤的可展开输出区域。 |
ChainOfThoughtComplete | 步骤之后可选的页脚行(“所有步骤均已完成”)。 |
**根级属性:**请参阅源代码类型:根级和步骤支持受控的 open / defaultOpen / onOpenChange;ChainOfThoughtStepStatus 为 "pending" | "active" | "completed" | "error"。
**属性与钩子:**仅使用内部上下文。通过注册表安装时会包含 lib/use-on-change.ts。
**使用说明:**若要支持展开/折叠,请为 hasContent 提供每个步骤稳定的可视内容。与流式传输/工具事件配合使用时,请在每个步骤上更新 status。
示例(结构):
<ChainOfThought>
<ChainOfThoughtTrigger>Chain of thought</ChainOfThoughtTrigger>
<ChainOfThoughtContent>
<ChainOfThoughtStep status="completed">
<ChainOfThoughtStepTitle icon={…} label="Search the web" />
</ChainOfThoughtStep>
<ChainOfThoughtStep status="active" hasContent>
<ChainOfThoughtStepTitle icon={…} label="Read results" />
<ChainOfThoughtStepContent>…</ChainOfThoughtStepContent>
</ChainOfThoughtStep>
</ChainOfThoughtContent>
</ChainOfThought>
工具
用于单次工具调用的状态感知型可折叠 UI:状态驱动的标题栏(图标 + 徽标)、JSON 输入,以及通过 Shiki 代码块展示的可选输出。可与 Vercel AI SDK 的工具部分状态直接对应。
导入:
import {
Tool,
ToolTrigger,
ToolContent,
ToolInput,
ToolOutput,
type ToolStatus,
} from "@/components/nexus-ui/tool";
组成部分:
| 部分 | 用途 |
|---|---|
Tool | 根 Collapsible;必需的 status(pending | ready | running | completed | error)用于设置 CSS 变量和徽标/图标样式上下文。 |
ToolTrigger | 标题行;必需的 name(工具标签),以及状态徽标和箭头。 |
ToolContent | ToolInput / ToolOutput 区块的主体。 |
ToolInput | 渲染格式化后的 JSON(或字符串)输入区块。 |
ToolOutput | 当 status 属于 showWhen 时渲染输出(默认 ["completed"]);发生 error 时显示 errorText,以及可选的负载代码块。 |
根组件属性: Tool 转发 Collapsible 属性;ToolOutput 接受 showWhen?: ToolStatus[] 和 errorText?: string。
属性与钩子: 仅使用内部上下文。注册表安装会在 tool.tsx 旁添加 codeblock-new.tsx 和 lib/shiki/highlighter.ts。
使用说明: 根据工具的生命周期设置 status(例如,将 AI SDK 的 tool-invocation 状态映射过来)。需要在失败时显示输出时,将 ToolOutput 与 showWhen={["completed", "error"]} 配合使用。
示例:
<Tool status="running">
<ToolTrigger name="search_web" />
<ToolContent>
<ToolInput payload={{ query: "nexus ui" }} />
<ToolOutput payload={result} showWhen={["completed", "error"]} errorText="Search failed" />
</ToolContent>
</Tool>
问题
用于聊天中后续澄清的卡片 UI:传入 items 元数据,使用 QuestionOption / QuestionOther 组合 Question 幻灯片,通过 QuestionsCarousel* 导航,并通过 onSubmit 收集答案。
导入:
import {
Questions,
Question,
QuestionOptions,
QuestionOption,
QuestionOther,
QuestionsTitle,
QuestionsHeader,
QuestionsFooter,
QuestionsSkip,
QuestionsSubmit,
QuestionsDismiss,
QuestionsCarousel,
QuestionsCarouselContent,
QuestionsCarouselItem,
QuestionsCarouselPagination,
QuestionsCarouselPrev,
QuestionsCarouselNext,
QuestionsCarouselIndex,
type QuestionsSubmission,
type QuestionInput,
} from "@/components/nexus-ui/questions";
组成部分:
| 部分 | 用途 |
|---|---|
Questions | 根 Card + provider;必需的 items: QuestionInput[];可选的 autoAdvance(默认 true)、onSubmit、onSkip、onDismiss。 |
QuestionsHeader / QuestionsTitle / QuestionsDismiss | 顶部区域和关闭控件。 |
QuestionsCarousel* | 用于多问题流程的 Shadcn Carousel 封装(Prev / Next / Index / Pagination)。 |
Question | 单张幻灯片;注册 id、type(single | multiple)、prompt、required。 |
QuestionOptions | 选项行的布局容器。 |
QuestionOption | 可选择的行(根据问题类型显示单选或复选框样式)。 |
QuestionOther | 使用 QUESTION_OTHER_VALUE 的自由文本“其他”选项。 |
QuestionsFooter / QuestionsSkip / QuestionsSubmit | 页脚操作;回答必答问题后才会启用提交。 |
根组件属性: Questions — items、autoAdvance、onSubmit(submission: QuestionsSubmission)、onSkip(questionId)、onDismiss()。跳过的可选问题会以 [No Preference] 提交。
属性与钩子: 仅使用内部上下文。常量:QUESTION_OTHER_VALUE、QUESTION_NO_PREFERENCE_VALUE、QUESTION_NO_PREFERENCE_LABEL。
使用说明: 当 autoAdvance 为 true 时,单选会自动前进。在多问题流程中,直到轮播的最后一张幻灯片时才显示或启用提交。
示例:
<Questions
items={[
{
id: "scope",
type: "single",
prompt: "What should we focus on?",
required: true,
options: [
{ value: "perf", label: "Performance" },
{ value: "ux", label: "UX" },
],
},
]}
onSubmit={(submission) => console.log(submission)}
>
<QuestionsHeader>
<QuestionsTitle>Quick questions</QuestionsTitle>
<QuestionsDismiss />
</QuestionsHeader>
<QuestionsCarousel>
<QuestionsCarouselContent>
{items.map((item) => (
<QuestionsCarouselItem key={item.id}>
<Question {...item}>
<QuestionOptions>
{item.options.map((option) => (
<QuestionOption key={option.value} value={option.value}>
{option.label}
</QuestionOption>
))}
<QuestionOther />
</QuestionOptions>
</Question>
</QuestionsCarouselItem>
))}
</QuestionsCarouselContent>
</QuestionsCarousel>
<QuestionsFooter>
<QuestionsSkip />
<QuestionsSubmit />
</QuestionsFooter>
</Questions>
AI SDK 集成
通过 @ai-sdk/react 使用 Vercel AI SDK。聊天钩子使用 transport(例如,指向你的 API 路由的 DefaultChatTransport)。使用本地状态(或你的表单库)连接文本区域,并在提交时调用 sendMessage。
import * as React from "react";
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import {
PromptInput,
PromptInputTextarea,
PromptInputActions,
PromptInputActionGroup,
PromptInputAction,
} from "@/components/nexus-ui/prompt-input";
import { Button } from "@/components/ui/button";
import { ArrowUp } from "lucide-react";
function ChatInput() {
const [input, setInput] = React.useState("");
const { sendMessage, status } = useChat({
transport: new DefaultChatTransport({ api: "/api/chat" }),
});
const busy = status === "streaming" || status === "submitted";
async function onSend() {
const text = input.trim();
if (!text || busy) return;
await sendMessage({ text });
setInput("");
}
return (
<PromptInput>
<PromptInputTextarea
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
void onSend();
}
}}
disabled={busy}
/>
<PromptInputActions>
<PromptInputActionGroup>
<PromptInputAction asChild>
<Button
type="button"
size="icon"
className="rounded-full"
disabled={!input.trim() || busy}
onClick={() => void onSend()}
>
<ArrowUp />
</Button>
</PromptInputAction>
</PromptInputActionGroup>
</PromptInputActions>
</PromptInput>
);
}
使用与 SDK 兼容的文件输入时,可通过 sendMessage({ text, files }) 在同一轮中传递附件(有关 FileList / 文件部分,请参阅当前 AI SDK 文档)。
组合原则
- 组合,而非配置。 优先使用小型基础组件,而非庞大的配置对象。
- 为提示词操作使用
asChild。 将Button包裹在PromptInputAction asChild中(适用时也可使用SuggestionPanelClose asChild)。 - 对提示词操作进行分组。 在
PromptInputActions内使用PromptInputActionGroup,将左侧操作(附件、工具)与右侧操作(发送)分开。 - 掌控源代码。 组件会被复制到项目中;可直接自定义。
样式
- 全面使用 Tailwind CSS v4 工具类。
- 可通过任意部分的
className覆盖样式。 - 深色模式:
dark:变体与常见的 shadcn 风格令牌保持一致。
项目结构
components/
├── nexus-ui/
│ ├── prompt-input.tsx
│ ├── model-selector.tsx
│ ├── suggestions.tsx
│ ├── attachments.tsx
│ ├── message.tsx
│ ├── codeblock.tsx
│ ├── thread.tsx
│ ├── citation.tsx
│ ├── reasoning.tsx
│ ├── text-shimmer.tsx
│ ├── image.tsx
│ ├── feedback-bar.tsx
│ ├── toaster.tsx
│ ├── chain-of-thought.tsx
│ ├── codeblock-new.tsx
│ ├── tool.tsx
│ └── questions.tsx
├── ui/
│ ├── button.tsx
│ ├── textarea.tsx
│ ├── scroll-area.tsx
│ └── ...
└── ...
依赖项
注册表项目会按需引入以下依赖项(shadcn CLI 会安装注册表依赖项):
- 提示词输入:
@radix-ui/react-slot、shadcntextarea、scroll-area、tooltip、kbd、@/lib/utils - 建议:
@radix-ui/react-presence、@radix-ui/react-slot、class-variance-authority、shadcnbutton - 模型选择器:
radix-ui(DropdownMenu)、class-variance-authority、@hugeicons/react、@hugeicons/core-free-icons - 附件:
@radix-ui/react-slot、class-variance-authority、@hugeicons/react、@hugeicons/core-free-icons - 消息:
streamdown+@streamdown/*插件、radix-ui、@radix-ui/react-slot、@hugeicons/react、@hugeicons/core-free-icons、shadcnbutton、avatar、tooltip、kbd - 线程:
@radix-ui/react-slot、use-stick-to-bottom、@hugeicons/react、@hugeicons/core-free-icons - 引用:
radix-ui、@hugeicons/react、@hugeicons/core-free-icons、shadcncarousel、hover-card - 推理:
streamdown、@hugeicons/react、@hugeicons/core-free-icons、tw-shimmer、shadcncollapsible、用于 shimmer + collapsible 高度关键帧的注册表 CSS - 文本闪烁: 仅需
@/lib/utils(cn) - 图像:
@radix-ui/react-dialog、@radix-ui/react-slot、shadcntooltip、kbd、@/lib/utils - 反馈栏:
@radix-ui/react-slot、shadcntooltip、kbd、@/lib/utils - 通知提示:
sonner、next-themes、@hugeicons/react、@hugeicons/core-free-icons、shadcnbutton、@/lib/utils - 思维链:
@hugeicons/react、@hugeicons/core-free-icons、shadcncollapsible、tw-shimmer(注册表 CSS)、@/lib/use-on-change、@/lib/utils - 工具:
@hugeicons/react、@hugeicons/core-free-icons、shiki、@react-symbols/icons、shadcnbadge、collapsible、注册表codeblock-new.tsx、lib/shiki/highlighter.ts、@/lib/utils - 问题:
@hugeicons/react、@hugeicons/core-free-icons、shadcncarousel、button、card、checkbox、@/lib/utils
文档
- 网站:https://nexus-ui.dev
- 文档:https://nexus-ui.dev/docs
- 组件:提示词输入 · 模型选择器 · 建议 · 附件 · 消息 · 线程 · 引用 · 推理 · 文本闪烁 · 图像 · 反馈栏 · 通知提示 · 思维链 · 工具 · 问题
- GitHub:https://github.com/victorcodess/nexus-ui
- 大模型上下文:https://nexus-ui.dev/llms.txt