assistant-ui Composer 原语

Composer 原语:消息输入框、提交模式、附件上传、语音输入、拖拽上传、@触发弹窗。

#type / snippet #status / evergreen #tech / dev / frontend #tech / ai

[!info] related notes

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 (默认)EnterShift+Enter标准聊天
ctrlEnterCtrl/Cmd+EnterEnter长文本编辑
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"提交模式
cancelOnEscapetrueEscape 取消编辑
addAttachmentOnPastetrue粘贴文件自动添加附件
unstable_focusOnRunStarttrue开始生成时自动聚焦
unstable_focusOnScrollToBottomtrue滚动到底部时自动聚焦
unstable_focusOnThreadSwitchedtrue切换线程时自动聚焦

优雅之处

  • 同一套原语在 Thread / Message 中自动切换输入/编辑模式,零配置
  • submitMode 覆盖了桌面和移动端的所有提交习惯
  • 拖拽、粘贴、按钮三种附件添加方式统一由 AttachmentDropzone 管理

创建于 2026/6/25 更新于 2026/6/25