- 后端
- 前端
- CRM
- 人工智能
- AI Agent
【免费下载链接】crm
Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.
Attachments 是 AI Elements 组件库中负责"附件与来源文档展示"的组件族,为 AI 聊天界面中的文件、图片、视频、音频与引用来源提供统一、可组合的渲染方案。本文以仓库内 .agents/skills/ai-elements/references/attachments.md 为核心,结合同目录可运行示例脚本与仓库中的附件数据实现,完整讲解安装、AI SDK 集成、三种布局变体、全部 Props 与工具函数,读完即可在 Next.js + AI SDK 的聊天应用中落地一个支持缩略图、徽章、文件列表与悬停预览的附件区。
组件定位与能力概览
Attachment组件族解决的是 AI 对话场景中一个高频问题:用户上传的文件、模型引用的来源文档,如何以一致且美观的方式展示?它提供的是一套统一的附件展示入口,同时覆盖FileUIPart(用户上传文件)与SourceDocumentUIPart(AI 引用的来源文档)两类 AI SDK 数据。
组件核心能力包括:
- 三种展示变体:
grid(缩略图网格)、inline(紧凑徽章,配合悬停预览)、list(带完整元数据的文件行); - 同时支持 AI SDK 的
FileUIPart与SourceDocumentUIPart数据; - 自动媒体类型检测(image、video、audio、document、source),由
getMediaCategory工具函数驱动; - inline 预览的悬停卡片支持(
AttachmentHoverCard系列); - 带可自定义回调的移除按钮,悬停时出现;
- 组合式(composable)架构:
Attachments、Attachment、AttachmentPreview、AttachmentInfo、AttachmentRemove等子组件自由拼装; - 无障碍支持(移除按钮带 ARIA 屏幕阅读器标签);
- TypeScript 支持,并导出
getMediaCategory、getAttachmentLabel等工具函数。
从仓库结构看,该组件文档与示例统一存放在 .agents/skills/ai-elements 目录下,配套三个可直接运行的示例脚本:scripts/attachments.tsx(grid)、scripts/attachments-inline.tsx(inline)、scripts/attachments-list.tsx(list),可作为独立调试与效果预览的参照。
安装与前置条件
附件组件属于 AI Elements 组件库,通过其 CLI 安装,安装命令会将组件源码直接下载并集成到项目components目录下(默认路径为@/components/ai-elements/):
npx ai-elements@latest add attachments根据项目包管理器不同,也可以使用等价命令:pnpm dlx ai-elements@latest或bunx --bun ai-elements@latest。
安装前需要确认环境满足以下前提:
- Node.js 18 或更高版本;
- Next.js 项目,且已安装 AI SDK;
- 已配置 shadcn/ui(若未安装,运行安装命令时会自动安装)。
组件安装后即为项目代码的一部分(而非隐藏在库中的黑盒),可以直接阅读源码、按需修改样式(Tailwind 类)与行为,与项目自身组件没有区别。
注意:仓库中的 AI Elements 技能文档说明(.agents/skills/ai-elements/SKILL.md)建议运行所有 CLI 命令时使用与项目
packageManager匹配的包运行器,上述npx示例应替换为项目实际使用的命令。
与 AI SDK 集成:展示用户上传文件
附件组件最常见的用法是在聊天消息或输入区中展示用户上传的文件。AI SDK 的消息parts中,上传文件对应FileUIPart类型,因此只需要把FileUIPart & { id: string }数组交给Attachments,并为每个文件渲染一个Attachment即可:
"use client"; import { Attachments, Attachment, AttachmentPreview, AttachmentInfo, AttachmentRemove, } from "@/components/ai-elements/attachments"; import type { FileUIPart } from "ai"; interface MessageProps { attachments: (FileUIPart & { id: string })[]; onRemove?: (id: string) => void; } const MessageAttachments = ({ attachments, onRemove }: MessageProps) => ( <Attachments variant="grid"> {attachments.map((file) => ( <Attachment key={file.id} data={file} onRemove={onRemove ? () => onRemove(file.id) : undefined} > <AttachmentPreview /> <AttachmentRemove /> </Attachment> ))} </Attachments> ); export default MessageAttachments;要点说明:
data接收附件数据对象,需包含id;组件内部通过mediaType、type、url、filename等字段进行媒体类型判断与预览渲染;onRemove为可选回调,仅在需要删除能力时传入;内部通过闭包把当前附件的id绑定到对应按钮;AttachmentPreview负责渲染图片/视频预览或类型图标,AttachmentRemove是悬停时出现的移除按钮;- 由于整个组件支持组合,你可以自行决定子组件的排列与嵌套层级,例如只渲染
AttachmentPreview而不显示移除按钮。
三种布局变体
Attachments通过variant属性切换整体布局,三种变体分别对应不同的展示场景,仓库内三个示例脚本即为各自变体的完整实现。
Grid 变体:消息内的缩略图网格
适用于消息中带视觉缩略图的附件展示。示例 scripts/attachments.tsx 中,附件数据包含filename、id、mediaType、type: "file"与url字段;图片类附件(如image/jpeg)渲染缩略图,无url的 PDF、视频则回退为类型图标。
<Attachments variant="grid"> {attachments.map((attachment) => ( <AttachmentItem key={attachment.id} attachment={attachment} onRemove={handleRemove} /> ))} </Attachments>示例用memo包裹AttachmentItem,并用useCallback稳定handleRemove,避免列表重渲染时不必要的子组件更新——这是在聊天消息列表中渲染大量附件时值得借鉴的性能实践。
Inline 变体:输入区的紧凑徽章
适用于输入区等紧凑场景,以徽章形式展示附件,并在悬停时弹出预览卡片。示例 scripts/attachments-inline.tsx 展示了完整的 hover 交互组合:
<AttachmentHoverCard key={attachment.id}> <AttachmentHoverCardTrigger asChild> <Attachment data={attachment} onRemove={handleRemove}> <div className="relative size-5 shrink-0"> <div className="absolute inset-0 transition-opacity group-hover:opacity-0"> <AttachmentPreview /> </div> <AttachmentRemove className="absolute inset-0" /> </div> <AttachmentInfo /> </Attachment> </AttachmentHoverCardTrigger> <AttachmentHoverCardContent> {/* 图片类附件展示大图预览 + 文件名 + mediaType */} </AttachmentHoverCardContent> </AttachmentHoverCard>该示例的核心逻辑在于:先用getMediaCategory(attachment)得到媒体分类,再通过mediaCategory === "image" && attachment.type === "file" && attachment.url判断是否渲染图片大图预览;卡片内展示文件名(getAttachmentLabel结果)与mediaType元数据。此外,示例中同时包含type: "file"与type: "source-document"两类数据,验证了组件对来源文档的统一支持。
List 变体:带完整元数据的文件列表
适用于文件列表场景,每行展示图标、文件名、媒体类型与移除按钮。示例 scripts/attachments-list.tsx 展示了关键差异——为AttachmentInfo传入showMediaType以在文件名下方显示媒体类型:
<Attachment data={attachment} key={attachment.id} onRemove={handleRemove}> <AttachmentPreview /> <AttachmentInfo showMediaType /> <AttachmentRemove /> </Attachment>示例中容器额外传入了className="w-full max-w-md",说明Attachments会透传 HTML 属性,方便限制宽度、对齐等布局需求。
Props 参考
<Attachments />
容器组件,负责设置布局变体。
| Prop | Type | Default | Description |
|---|---|---|---|
variant | unknown | - | 展示布局变体(grid / inline / list)。 |
...props | React.HTMLAttributes<HTMLDivElement> | - | 透传到底层 div 元素。 |
<Attachment />
单个附件条目包装器。
| Prop | Type | Default | Description |
|---|---|---|---|
data | unknown | - | 附件数据(带 id 的 FileUIPart 或 SourceDocumentUIPart)。 |
onRemove | () => void | - | 点击移除按钮时触发的回调。 |
...props | React.HTMLAttributes<HTMLDivElement> | - | 透传到底层 div 元素。 |
<AttachmentPreview />
渲染媒体预览(图片、视频或类型图标)。
| Prop | Type | Default | Description |
|---|---|---|---|
fallbackIcon | React.ReactNode | - | 无预览可用时展示的自定义图标。 |
...props | React.HTMLAttributes<HTMLDivElement> | - | 透传到底层 div 元素。 |
<AttachmentInfo />
展示文件名与可选媒体类型。
| Prop | Type | Default | Description |
|---|---|---|---|
showMediaType | boolean | false | 是否在文件名下方展示媒体类型。 |
...props | React.HTMLAttributes<HTMLDivElement> | - | 透传到底层 div 元素。 |
<AttachmentRemove />
悬停时出现的移除按钮。
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | - | 按钮的屏幕阅读器标签(无障碍必需)。 |
...props | React.ComponentProps<typeof Button> | - | 透传到底层 Button 组件。 |
<AttachmentHoverCard />
悬停预览功能的外层包装。
| Prop | Type | Default | Description |
|---|---|---|---|
openDelay | number | 0 | 打开悬停卡片的延迟(毫秒)。 |
closeDelay | number | 0 | 关闭悬停卡片的延迟(毫秒)。 |
...props | React.ComponentProps<typeof HoverCard> | - | 透传到底层 HoverCard 组件。 |
<AttachmentHoverCardTrigger />
悬停卡片的触发元素。
| Prop | Type | Default | Description |
|---|---|---|---|
...props | React.ComponentProps<typeof HoverCardTrigger> | - | 透传到底层 HoverCardTrigger 组件。 |
<AttachmentHoverCardContent />
悬停卡片中展示的内容。
| Prop | Type | Default | Description |
|---|---|---|---|
align | unknown | - | 悬停卡片内容的对齐方式。 |
...props | React.ComponentProps<typeof HoverCardContent> | - | 透传到底层 HoverCardContent 组件。 |
<AttachmentEmpty />
无附件时的空状态组件。
| Prop | Type | Default | Description |
|---|---|---|---|
...props | React.HTMLAttributes<HTMLDivElement> | - | 透传到底层 div 元素。 |
需要说明的是,原文档中variant、data、align等 Prop 的类型标注为unknown,实际取值以源码实现为准:variant对应三种布局变体名,data对应 AI SDK 的FileUIPart/SourceDocumentUIPart,align对应 HoverCard 的对齐选项。
工具函数
组件包导出两个可在业务代码中直接复用的工具函数。
getMediaCategory(data)
返回附件的媒体分类,用于自定义预览逻辑(如 inline 示例中判断是否渲染图片大图):
import { getMediaCategory } from "@/components/ai-elements/attachments"; const category = getMediaCategory(attachment); // Returns: "image" | "video" | "audio" | "document" | "source" | "unknown"从示例脚本看,该函数依据mediaType与type字段进行判定:image/jpeg等归于image,application/pdf归于document,video/mp4归于video,audio/mp3归于audio,type: "source-document"归于source。
getAttachmentLabel(data)
返回附件的展示标签,优先取文件名,无文件名时回退为 "Image" 或 "Attachment" 等通用文案:
import { getAttachmentLabel } from "@/components/ai-elements/attachments"; const label = getAttachmentLabel(attachment); // Returns filename or fallback like "Image" or "Attachment"该函数适用于需要统一展示附件名称的场景,如 hover 卡片标题、无障碍标签或列表首列。
仓库中的附件数据处理对照
AI Elements 的 Attachments 组件解决的是前端展示,而附件数据从上传到可预览 URL 的链路在仓库中另有实现可对照参考。在 apps/api/src/conversations/conversation-attachments.ts 中,可以看到与服务端对话附件同构的数据形态:
StoredBuilderAttachment类型包含id、name、mediaType、size四个字段,与前端附件组件消费的filename/mediaType/id数据一一对应;builderMessageWithAttachments将数据库中的 JSON 消息与附件列表合并,为每个附件生成id、name、type、size,并通过isPreviewableImage判断是否为可预览图片(image/gif、image/jpeg、image/png、image/webp),是则生成预览 URL,否则置为null;attachmentUrl生成/api/conversations/attachments/{id}形式的预览地址,并支持带share分享令牌的变体。
从源码结构看,这与前端getMediaCategory的"按媒体类型决定展示形态"思路一致:服务端决定"哪些类型值得生成预览 URL",前端决定"预览以何种视觉形态呈现",两者共同构成了完整的附件展示链路。仓库中 apps/app/components/agent-builder 下的聊天相关组件(如agent-builder-chat.tsx、agent-composer.tsx)也在消息交互中涉及附件处理,可作为 AI 对话界面集成附件的进一步参考。
定制与无障碍实践
由于组件代码直接进入项目目录,定制非常直接:
- 样式定制:修改
components/ai-elements/attachments.tsx中的 Tailwind 类即可,例如调整缩略图圆角、徽章配色或移除按钮样式;所有子组件均透传HTMLAttributes,也可通过className就地覆盖; - 行为定制:
onRemove回调由你完全掌控,可对接状态管理、乐观更新或后端删除接口;hover 卡片的开关延迟通过AttachmentHoverCard的openDelay/closeDelay调节,避免误触; - 无障碍:
AttachmentRemove的label属性用于屏幕阅读器朗读;建议为Attachment容器补充对应的 aria-label(如文件名),使附件区对辅助技术可感知。
常见问题速查
- 组件无样式:确认项目按 shadcn/ui(Tailwind 4)规范配置
globals.css,包含 Tailwind 导入与 shadcn/ui 基础样式; - CLI 未添加任何文件:确认当前工作目录是项目根目录(存在
package.json)、components.json配置正确,并使用最新版 CLI(npx ai-elements@latest); @/别名导入失败(module not found):检查tsconfig.json中是否配置了@/*路径别名,需包含"baseUrl": "."与"paths": { "@/*": ["./*"] };- 缩略图不显示:确认附件数据的
url非空且mediaType属于图片类型——inline 示例中明确以attachment.type === "file" && attachment.url为前提才渲染大图。
结语
Attachments 组件族以"容器 + 子组件自由组合"的方式,把 AI 聊天中最常见的附件展示需求(网格缩略图、输入区徽章、文件列表、来源文档)收敛为一套可预测、可定制、可无障碍访问的 API。配合getMediaCategory/getAttachmentLabel工具函数与 AI SDK 原生数据类型的直接支持,它可以无缝嵌入任意基于 Next.js + AI SDK 的对话界面。仓库中该组件的中文文档、三份可运行示例脚本,以及与 apps/api/src/conversations/conversation-attachments.ts 对应的服务端附件链路,为直接上手和二次开发提供了完整的参考。
- 后端
- 前端
- CRM
- 人工智能
- AI Agent
【免费下载链接】crm
Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.
相关推荐
Comp AI CRM 中的 AI 推理展示:ai-elements Reasoning 组件集成实战指南
Comp AI CRM 中的 AI 推理展示:ai elements Reasoning 组件集成实战指南 本文是一份以 ai elements 技能文档 ht
后端前端CRM人工智能AI Agent如何用 forge-fhevm 的 deploy-local.sh 在本地 Anvil 节点部署 FHEVM 宿主合约栈
如何用 forge fhevm 的 deploy local.sh 在本地 Anvil 节点部署 FHEVM 宿主合约栈 用 Foundry 在本地开发 FHE
后端前端CRM人工智能AI AgentDB-GPT Agent 框架深度解析:ConversableAgent 架构、多智能体协作与三层记忆体系
DB GPT Agent 框架深度解析:ConversableAgent 架构、多智能体协作与三层记忆体系 本文以 DB GPT 官方的 Agent 框架概念文
后端前端CRM人工智能AI Agent
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考