assistant-ui Composer 原语
Composer 原语:消息输入框、提交模式、附件上传、语音输入、拖拽上传、@触发弹窗。
#type / snippet
#status / evergreen
#tech / dev / frontend
#tech / ai
[!info] related notes
- 概览: assistant-ui 概览
- Thread & Message: Thread & Message 原语
- ActionBar: ActionBar & BranchPicker 原语
assistant-ui Composer 原语
消息输入与编辑的统一接口。同一套原语在 Thread 中用于新消息输入,在 Message 中自动切换为消息编辑模式。
子组件
| 子组件 | 默认元素 | 职责 |
|---|---|---|
Root | <form> | 输入区容器,管理提交行为 |
Input | <textarea> | 文本输入,键盘快捷键处理 |
Send | <button> | 发送按钮,自动禁用条件 |
Cancel | <button> | 取消当前编辑/生成 |
基础用法
// components/Composer.tsx
import { ComposerPrimitive } from "@assistant-ui/react";
import { SendIcon } from "lucide-react";
function Composer() {
return (
<ComposerPrimitive.Root className="flex items-end gap-2 p-4 border-t">
<ComposerPrimitive.Input
autoFocus
placeholder="输入消息..."
rows={1}
className="flex-1 resize-none rounded-xl border p-3 focus:outline-none"
submitMode="enter" // Enter 发送,Shift+Enter 换行
/>
<ComposerPrimitive.Send className="rounded-xl bg-primary p-2 text-primary-foreground disabled:opacity-50">
<SendIcon size={20} />
</ComposerPrimitive.Send>
</ComposerPrimitive.Root>
);
}
提交模式
// submitMode 三种模式
<ComposerPrimitive.Input submitMode="enter" /> // Enter 发送 (默认)
<ComposerPrimitive.Input submitMode="ctrlEnter" /> // Ctrl+Enter 发送
<ComposerPrimitive.Input submitMode="none" /> // 禁用键盘提交
| 模式 | 发送键 | 换行键 | 适用场景 |
|---|---|---|---|
enter (默认) | Enter | Shift+Enter | 标准聊天 |
ctrlEnter | Ctrl/Cmd+Enter | Enter | 长文本编辑 |
none | 仅按钮 | Enter | 触屏设备 |
上下文自适应
// 同一套 ComposerPrimitive,在不同位置有不同行为:
<ThreadPrimitive.ViewportFooter>
<Composer /> // → 新消息输入模式
</ThreadPrimitive.ViewportFooter>
<MessagePrimitive.Root>
<Composer /> // → 消息编辑模式(自动切换)
</MessagePrimitive.Root>
附件功能
添加附件
// components/ComposerWithAttachments.tsx
import {
ComposerPrimitive,
ComposerAttachments,
ComposerAddAttachment,
} from "@/components/assistant-ui/attachment";
function ComposerWithAttachments() {
return (
<ComposerPrimitive.Root className="flex flex-col gap-2 p-4 border-t">
{/* 已添加的附件预览 */}
<ComposerAttachments />
<div className="flex items-end gap-2">
{/* 添加附件按钮 */}
<ComposerAddAttachment multiple />
<ComposerPrimitive.Input
autoFocus
placeholder="输入消息..."
rows={1}
className="flex-1 resize-none rounded-xl border p-3"
addAttachmentOnPaste={true} // 粘贴文件自动添加为附件
/>
<ComposerPrimitive.Send className="rounded-xl bg-primary p-2">
发送
</ComposerPrimitive.Send>
</div>
</ComposerPrimitive.Root>
);
}
拖拽上传
// components/ComposerWithDropzone.tsx
import { AttachmentDropzone } from "@assistant-ui/react";
function ComposerWithDropzone() {
return (
<AttachmentDropzone className="border-2 border-dashed data-[dragging]:border-primary">
<ComposerPrimitive.Root>
<ComposerPrimitive.Input placeholder="拖拽文件或输入消息..." />
<ComposerPrimitive.Send>发送</ComposerPrimitive.Send>
</ComposerPrimitive.Root>
</AttachmentDropzone>
);
}
拖拽时 data-dragging 属性自动切换,可用 CSS 控制视觉反馈。
语音输入
// components/ComposerWithVoice.tsx
import { ComposerPrimitive, AuiIf } from "@assistant-ui/react";
function ComposerWithVoice() {
return (
<ComposerPrimitive.Root>
<ComposerPrimitive.Input placeholder="输入消息..." />
<div className="flex gap-2">
{/* 语音录制按钮 */}
<AuiIf condition={(s) => !s.composer.dictation?.isActive}>
<ComposerPrimitive.Dictate className="rounded-full p-2">
🎤
</ComposerPrimitive.Dictate>
</AuiIf>
<AuiIf condition={(s) => s.composer.dictation?.isActive}>
<ComposerPrimitive.StopDictation className="rounded-full p-2 text-red-500">
⏹
</ComposerPrimitive.StopDictation>
</AuiIf>
<ComposerPrimitive.Send>发送</ComposerPrimitive.Send>
</div>
{/* 实时转录文本 */}
<ComposerPrimitive.DictationTranscript className="text-sm text-muted-foreground" />
</ComposerPrimitive.Root>
);
}
发送控制
// 禁用发送(输入框仍可用,用于等待外部条件)
// 通过 runtime adapter 设置 isSendDisabled
// 自定义提交拦截
<ComposerPrimitive.Root
onSubmit={(e) => {
e.preventDefault(); // 阻止发送
// 自定义逻辑:分析埋点、输入预处理等
}}
>
移动端适配
// 触屏设备上 Enter 插入换行而非发送
<ComposerPrimitive.Input
submitMode="enter"
unstable_insertNewlineOnTouchEnter={true} // 触屏设备 Enter → 换行
/>
匹配 WhatsApp / Slack / iMessage 的移动端交互模式。
Input 键盘配置
| 属性 | 默认值 | 说明 |
|---|---|---|
submitMode | "enter" | 提交模式 |
cancelOnEscape | true | Escape 取消编辑 |
addAttachmentOnPaste | true | 粘贴文件自动添加附件 |
unstable_focusOnRunStart | true | 开始生成时自动聚焦 |
unstable_focusOnScrollToBottom | true | 滚动到底部时自动聚焦 |
unstable_focusOnThreadSwitched | true | 切换线程时自动聚焦 |
优雅之处:
- 同一套原语在 Thread / Message 中自动切换输入/编辑模式,零配置
submitMode覆盖了桌面和移动端的所有提交习惯- 拖拽、粘贴、按钮三种附件添加方式统一由
AttachmentDropzone管理