news 2026/9/19 20:29:43

AionUi 对话发送草稿箱:AI 回复期间消息排队交互的完整设计与源码实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AionUi 对话发送草稿箱:AI 回复期间消息排队交互的完整设计与源码实现

AionUi 对话发送草稿箱:AI 回复期间消息排队交互的完整设计与源码实现

【免费下载链接】AionUi免费、本地、开源的 24/7 全天候 Cowork 应用,以及适用于 Gemini CLI、Claude Code、Codex、OpenCode、Qwen Code、Goose CLI、Auggie 等的 OpenClaw | 🌟 喜欢就点star吧项目地址: https://gitcode.com/iOfficeAI/AionUi

本文围绕 AionUi(iOfficeAI/AionUi 开源仓库)对话模块中的「发送草稿箱」(原「排队面板」)展开,系统梳理其从交互优化需求(PRD:docs/prds/conversations/send-drafts.md)到渲染层落地的完整链路:包括每条草稿的常驻操作、立即发送插队、自动/手动双模式、清空防误触、拖拽排序与窄栏适配等七项功能规格,并结合CommandQueuePanel组件、useConversationCommandQueue状态 hook 与配套单元测试,逐层讲解其数据模型、持久化策略、发送门控与容错机制。读完本文,你将掌握该功能的交互设计决策、状态机原理与源码级实现细节,可直接用于理解或二次开发同类"消息暂存/排队"能力。


一、背景与目标:从「排队面板」到「发送草稿箱」

在 AionUi 中,AI(如 Gemini CLI、Claude Code、Codex、OpenCode、Qwen Code、Goose CLI、Auggie 等 Agent 运行时)正在回复时,用户仍可继续输入并发送消息,这些消息会进入一个位于对话输入区上方的排队面板等待发出。这一能力原本已经存在:支持入队、编辑、删除、清空、当前 turn 结束后自动发送,以及拖拽调整顺序。

但根据微信群用户反馈,旧交互存在三个明显痛点:

  1. 每条排队消息只有「删除」直接可见,「编辑」被折叠进「⋯」更多菜单,要点两下才找得到;
  2. 没有「立即发送」能力——排队的消息只能等 AI 回复结束后被动等待,用户无法让某一条马上发出去;
  3. 排队面板缺少「是否自动发送」的总控开关,用户无法选择"我先攒着、想发再发"。

因此本次优化的核心目标,是把排队面板重新定位成一个「发送草稿箱」:AI 忙时用户写下的消息先存到这里,用户可以清楚看到并随手操作(立即发送 / 编辑 / 删除 / 调序),并且可以选择让草稿箱自动依次发出、还是手动由用户逐条发。按 PRD 约定,整个改造不改后端,全部在渲染层完成(涉及范围为对话输入区上方的排队面板)。

三个关键名词约定:

  • 发送草稿箱:面板的新名字,指 AI 回复期间用户已发送、正在等待发出的消息集合(下文沿用旧代码中的「排队 / 队列」时指同一事物);
  • 草稿:草稿箱里的单条待发消息,含文字与文件(以及会话引用);
  • 自动 / 手动:草稿箱的两种发送模式(见 F-DRAFT-03)。

在源码中,该功能对应的实现文件为:

  • 面板 UI:packages/desktop/src/renderer/components/chat/CommandQueuePanel.tsx
  • 状态与执行逻辑:packages/desktop/src/renderer/pages/conversation/platforms/useConversationCommandQueue.ts
  • 接入点:ACP 平台发送框AcpSendBox.tsx、AionRS 平台发送框AionrsSendBox.tsx

二、功能规格全景:七项 F-DRAFT 需求

PRD 将本次改造拆解为七个功能点,其中两项为新增、四项为优化、一项为保留:

编号功能类型
F-DRAFT-01每条草稿的三个常驻操作(立即发送 / 编辑 / 删除)优化
F-DRAFT-02立即发送(打断当前回复插队发出)新增
F-DRAFT-03自动 / 手动发送模式新增
F-DRAFT-04草稿箱标题行与说明优化
F-DRAFT-05清空草稿箱(防误触)优化
F-DRAFT-06拖拽调整顺序保留
F-DRAFT-07窄栏 / 移动端适配新增

下文逐项说明其用户故事、正常流程、异常情况与验收标准,并在对应小节后给出源码实现证据。


三、(F-DRAFT-01) 每条草稿的三个常驻操作

用户故事:每条排队的消息都能一眼看到「立即发送 / 编辑 / 删除」三个操作,随手就能点,不用翻菜单。

正常流程(用户视角):

  1. AI 回复期间,用户发送的消息进入发送草稿箱,逐条列在输入框上方;
  2. 每条草稿右侧常驻三个操作按钮,鼠标不悬停也始终可见:
    • 立即发送:见 F-DRAFT-02;
    • 编辑:把这条草稿的文字与文件放回输入框供修改,该草稿从箱中移除,改完重新发送即重新入箱;
    • 删除:把这条草稿从箱中移除,不再发出;
  3. 三个操作在移动端、窄栏下同样常驻可点(不依赖 hover)。

异常情况:草稿箱为空时,面板整体不显示。

验收标准:三个按钮无需 hover 或展开菜单即常驻可见;「编辑」不再藏在「⋯」菜单里;「删除」将该条从草稿箱移除;「编辑」将该条内容与文件回填输入框并从箱中移除;三个按钮在移动端 / 窄栏下均可直接点击。

源码实现:在CommandQueuePanel.tsx中,每条草稿卡片的右侧渲染了三个renderQueueActionIconButton图标按钮,分别为:SendOne(立即发送,accent主色强调)、Edit(编辑)、Delete(删除,danger危险色),三者均以size='mini'、圆形文本按钮形式常驻显示,不依赖 hover。按钮通过aria-label提供无障碍访问,文案来自 i18n 的conversation.commandQueue.sendNow / edit / remove键。当items.length === 0时组件直接return null,即草稿箱为空时面板整体不渲染(对应代码)。


四、(F-DRAFT-02) 立即发送:打断当前回复插队发出

用户故事:当用户发现某条消息在排队、但希望它马上就走时,点一下就立刻发出去。

正常流程(用户视角):

  1. 用户点击某条草稿的「立即发送」;
  2. 系统打断 AI 当前正在进行的回复,将该条草稿立即出队并发送;
  3. 用户能理解这打断了当前回复——不额外弹提示条
  4. 草稿箱里剩余的草稿行为完全不变:原本自动就继续自动依次发,原本手动就继续等待用户逐条发;发送模式、内容、顺序都不受这次插队影响。

边界说明:「立即发送」只作用于被点击的这一条。它是一次"让这条插队立刻走"的动作,不是模式切换,不引入「暂停 / 继续」这类额外状态。

异常情况:AI 当前没有正在进行的回复(已空闲)时,直接发送该条,无需打断。

验收标准:点击「立即发送」会中止 AI 当前回复并立刻发出该条草稿;不出现"已插队 / 队列已暂停"之类的提示条;剩余草稿的发送模式、内容、顺序均不因这次插队而改变;AI 空闲时点击「立即发送」直接发出,不产生打断副作用。

源码实现:hook 中的sendNow(commandId)先按commandId定位目标草稿,仅删除目标项(其余项保持原 mode、顺序与 paused 标志),然后直接调用onExecuteRef.current(target)执行发送(useConversationCommandQueue.ts)。发送失败时,如果错误可归类为"会话忙"(classifyConversationBusyError),草稿会被放回队列头部并进入 busy-wait 等待状态;其他错误则恢复该条并将队列置为暂停态,同时弹出提示"下一条排队指令启动失败。请编辑、调整顺序或移除后再继续"。这保证了"立即发送"失败时用户的消息不会丢失,而是回到草稿箱待处理。


五、(F-DRAFT-03) 自动 / 手动发送模式

用户故事:用户能选择草稿箱是「AI 回完自动帮我依次发」还是「先攒着、我想发再发」。

正常流程(用户视角):

  1. 草稿箱标题行右侧有一个模式切换按钮,显示当前模式(自动 / 手动),点一下即切到另一模式,无需二次确认;
  2. 自动模式:AI 每结束一轮回复,草稿箱自动把下一条发出去,一条接一条,直到发完,用户无需操作;
  3. 手动模式:草稿只暂存在箱中、不自动发出,由用户逐条点「立即发送」决定何时发。

作用范围(会话级,纯前端)

  • 模式是每个对话各自记录的,A 对话设手动不影响 B 对话;
  • 每个对话启动时默认「自动」(PRD 设计;当前仓库实现中默认值见下文差异说明);
  • 该设置与现有排队内容一样保存在前端会话级存储中:应用运行期间切走再切回该对话,模式与草稿都保留;整个应用关闭重开后重置(草稿与模式都回到初始,与现有排队行为一致)。

异常情况:草稿箱为空时面板不显示,但模式设置在后台保留,下次有草稿入箱时按该模式运作。

验收标准:切换按钮显示当前模式、点击即切换无二次确认;自动模式下 AI 每轮结束后自动依次发出草稿;手动模式下草稿不自动发出;新对话默认自动模式;模式按对话独立记录互不影响;应用关闭重开后模式回到默认。

源码实现:类型层面定义ConversationCommandQueueMode = 'auto' | 'manual'useConversationCommandQueue.ts)。toggleMode()automanual之间翻转并记录mode-changed日志(L1019-L1032)。自动发送的主循环位于组件内一个useEffect中:仅当mode === 'auto'、执行门控可执行、未暂停、无正在等待的 turn 且队列非空时,取出队首草稿发送,并通过turnCompleted事件驱动"每轮结束后发下一条"的节奏(L1080-L1164)。此外还实现了后台 runner:对话切走(组件卸载)后,drainBackgroundCommandQueue仍会监听ipcBridge.conversation.turnCompleted事件继续把自动模式的队列发完(L432-L561)。

PRD 与实现的差异说明:PRD 设计"新对话默认自动",而从当前仓库源码看,createDefaultQueueState()返回的默认 mode 为'manual'normalizeQueueMode()也把非'auto'的值一律归一为'manual'(L100-L106)。可以推断实现侧选择了更保守的默认值——避免用户未察觉时消息被自动发出。若要以 PRD 的"默认自动"落地,只需调整默认状态与归一化逻辑,属纯前端改动。


六、(F-DRAFT-04) 草稿箱标题行与说明

用户故事:用户一眼看懂这个面板是什么、当前处于什么模式,需要时能查到自动 / 手动的含义。

标题行布局(草稿箱框内顶部一行,自左到右):

  1. 标题「发送草稿箱」+ 数量徽标(显示当前草稿条数);
  2. 模式切换按钮(F-DRAFT-03);
  3. 帮助「?」:hover 显示说明——草稿箱是什么,以及自动 / 手动分别的含义;
  4. 更多「⋯」:见 F-DRAFT-05。

帮助文案(tooltip):

发送草稿箱:AI 回复期间你发的消息会先存到这里。 自动:AI 回复结束后,自动依次发送草稿箱里的消息。 手动:消息只暂存、不自动发,由你逐条点「立即发送」。

验收标准:标题行显示「发送草稿箱」与草稿数量;「?」hover 出说明,覆盖草稿箱含义与两种模式。

源码实现:标题行位于CommandQueuePanel.tsx。左侧为自定义DraftBoxActionIconSVG 图标 + 标题(i18nconversation.commandQueue.title,中文为「草稿箱」)与数量徽标(直接渲染items.length);右侧为模式切换按钮与「⋯」下拉菜单。值得注意的是,当前实现把帮助入口与模式切换按钮合二为一Tooltip包裹模式按钮,hover 即显示三行帮助文案(helpIntro/helpAuto/helpManual),组件注释明确说明"模式切换按钮兼作帮助入口,因此无需单独的「?」按钮"(L505-L526)。这与 PRD 中"单独「?」帮助按钮"的设计略有出入,属实现侧的等价简化——更省宽度,且语义信息没有丢失。中文帮助文案见conversation.jsoncommandQueue.help*键。


七、(F-DRAFT-05) 清空草稿箱(防误触)

用户故事:能一次清空整箱草稿,但不要轻易误点到。

正常流程(用户视角):

  1. 「清空草稿箱」放在标题行「⋯」更多菜单里,菜单项以危险色(红)呈现;
  2. 点击后再弹一次二次确认,确认后才清空全部草稿。

验收标准:「清空草稿箱」位于「⋯」菜单内而非标题行直接可点;菜单项以危险色呈现;点击后需二次确认才执行清空。

源码实现moreMenu下拉菜单中,清空项Menu.Item的内联样式使用color: 'rgb(var(--danger-6))'呈现危险色(CommandQueuePanel.tsx)。点击后调用Modal.confirm弹出二次确认框,标题"确定清空发送草稿箱?"、正文"所有待发送的消息都会被移除,且无法恢复。",确认按钮同样以status: 'danger'呈现(L434-L444)。确认后执行onClear,对应 hook 中的clear()——它会重置所有等待标记并清除持久化状态(L748-L756)。


八、(F-DRAFT-06) 拖拽调整顺序(保留能力)

用户故事:用户能调整草稿的发送先后顺序。

正常流程(用户视角):

  1. 每条草稿最前有一个拖拽手柄,hover 时显现,按住可上下拖动调整顺序;
  2. 顺序即草稿箱自动 / 手动发出的先后次序。

验收标准:桌面端 hover 草稿时显现拖拽手柄,可上下拖动排序;排序结果即后续发送次序。

源码实现:拖拽基于@dnd-kit/core@dnd-kit/sortableverticalListSortingStrategy)。每条草稿的拖拽手柄是一个带Drag图标的按钮,默认opacity-0group-hover:opacity-100即在 hover 时显现(CommandQueuePanel.tsx)。为了把拖动限制在队列容器内,组件实现了createRestrictToQueueContainerModifier,与restrictToVerticalAxis组合使用,保证拖拽不会超出面板边界(L43-L62)。onReorder(activeCommandId, overCommandId)对应 hook 中纯函数reorderQueuedCommand——按 id 找到起止索引后 splice 移动(L326-L342)。排序结果即队列顺序,也就是自动 / 手动发送时的出队次序。


九、(F-DRAFT-07) 窄栏 / 移动端适配

用户故事:在小屏、移动端或团队并排 Agent 视图下,草稿箱在窄栏里也摆得下、点得到。

适配策略(约 300px 宽及以下):

  • 标题「发送草稿箱」收成一个图标 + 数量徽标,hover / 长按显示全名,省出宽度;
  • 模式切换按钮始终保留(核心功能),文字压到「自动 / 手动」两字;
  • 「?」帮助并入「⋯」菜单:窄栏头部只保留一个「⋯」,其中包含「使用说明」与「清空草稿箱」;
  • 每条草稿的三个操作按钮不变,消息文字自动截断;
  • 拖拽手柄在窄栏隐藏,改为长按整条拖动。

验收标准:窄栏下标题收成图标 + 徽标、切换按钮保留;「?」并入「⋯」菜单、头部仅一个「⋯」;每条三个操作按钮仍可点击、文字自动截断;团队并排 Agent 视图下草稿箱布局不溢出、不错位。

源码实现CommandQueuePanel接收isMobile属性。窄栏时左侧标题用Tooltip包裹的DraftBoxActionIcon图标代替文字标题(hover 显示全名)(L485-L496);「⋯」菜单在isMobile时额外加入一个help菜单项,把帮助内容以多行文本形式放进菜单(L452-L466)。拖拽方面,移动端dragViaCard为 true:隐藏手柄、改为长按整条 200ms触发拖动(PointerSensoractivationConstraint在移动端为{ delay: 200, tolerance: 6 },桌面端为{ distance: 8 }),200ms 的延迟同时也避免普通点击操作按钮被误判为拖拽(L360-L367)。列表容器最大高度在移动端为min(48vh, 320px),桌面端为min(36vh, 320px)(L560-L562)。


十、源码级纵深:状态模型、持久化与执行门控

10.1 数据模型与常量

队列项类型ConversationCommandQueueItem包含五个字段(useConversationCommandQueue.ts):

字段类型说明
idstring唯一标识(uuid()生成)
inputstring消息正文
filesChatFileRef[]附件文件引用(按chatFileRefKey去重)
sessionsSessionRef[]@@会话引用(必须随消息存活,否则 Agent 静默丢失会话块)
created_atnumber入队时间戳

队列状态ConversationCommandQueueState = { items, isPaused, mode }。配套的硬性容量上限常量(超出即拒绝入队并弹提示):

  • MAX_QUEUED_COMMANDS = 20(最多 20 条);
  • MAX_QUEUED_COMMAND_INPUT_LENGTH = 20_000(单条正文最长 20000 字符);
  • MAX_QUEUED_COMMAND_FILES = 50(单条最多 50 个文件);
  • MAX_QUEUED_COMMAND_STATE_BYTES = 256 * 1024(整个队列持久化状态不超过 256KB)。

入队前会通过validateQueuedCommandItem做完整校验,失败原因枚举包括emptyInput / inputTooLong / tooManyFiles / queueFull / queueTooLarge,每种都有对应的 i18n 提示文案(中文见conversation.json)。

10.2 会话级持久化:sessionStorage

模式与草稿按对话隔离存储:存储键为`conversation-command-queue/${conversation_id}`,写入window.sessionStorage(L110、L303-L319)。这与 PRD 的"会话级存储"设计完全一致:

  • 应用运行期间切走再切回,模式与草稿从 sessionStorage 恢复保留;
  • 整个应用关闭重开后 sessionStorage 清空,草稿与模式回到初始;
  • 当队列为空、未暂停且为默认模式时,持久化键会被主动删除,避免残留脏数据。

读取时使用normalizeQueueState做严格的数据净化:逐条校验 id/input/files/created_at 类型,非法的sessions引用被丢弃而非让整条消息作废("丢失过期引用可恢复,丢失用户输入不可恢复"),并再次套用容量上限(L168-L200)。

10.3 执行门控(Execution Gate)

队列是否允许出队发送由getCommandQueueExecutionGate统一裁决:常规场景下canExecute = !isBusy;在团队运行时场景下,则透传runtimeGatecanSendMessage && !isProcessing),与团队并排 Agent 视图的运行时状态联动(L387-L409)。hook 内部用一组 ref 状态机跟踪waitingForTurnStart / waitingForTurnCompletion / waitingForBusyRelease,并通过conversation.turnCompleted事件精确感知"一轮回复结束",从而决定何时发送下一条(L621-L680)。发送失败时按"忙等待 / 真失败"分流:忙则把草稿放回队首继续等;真失败则暂停队列并提示用户编辑、重排或移除问题项。

10.4 与发送框的接线

在 ACP 平台发送框AcpSendBox.tsx中,useConversationCommandQueue被接入(L421-L431),CommandQueuePanel渲染在输入区上方(L783),其isBusy判断为isCancelling || runtimeGate.isProcessing || !runtimeGate.canSendMessage(L249)。发送框还提供「存到草稿箱」能力:AI 处理中时直接发送被拦截,用户可按⌘ + Enter / Ctrl + Enter将输入存为草稿(对应 i18naddToQueueShortcut)。

10.5 配套测试

该功能在仓库中有完整的测试覆盖,可作为行为契约参考:

  • CommandQueuePanel.dom.test.tsx:面板交互(常驻操作、模式切换、清空确认、拖拽等)的 DOM 级测试;
  • conversationCommandQueueMode.dom.test.tsx:自动 / 手动模式行为;
  • conversationCommandQueueDrain.dom.test.tsx:turn 结束后自动出队发送链路;
  • conversationCommandQueueGate.test.ts:执行门控裁决逻辑;
  • conversationCommandQueueSessions.test.tsconversationCommandQueueChatFileRef.test.ts@@会话引用与文件引用的持久化保真。

十一、待讨论事项与落地取舍

PRD 在「待讨论模块」中记录了两个问题,可作为后续迭代方向参考:

  1. 控件形态已定:模式切换采用「单按钮状态切换」(按钮显示当前模式,点击切到另一模式),不采用拨动开关或分段按钮。当前实现与此一致——标题行右侧就是一个带模式文字与SortTwo图标的胶囊按钮。
  2. 是否需要「全局默认模式」:当前为纯会话级、新对话默认(PRD 规划为自动,当前实现为手动)。若后续收到"每次开新对话都要重设太麻烦"的反馈,可再评估增加一个全局默认值设置项。此项本期不做

此外,实现相对 PRD 有两处值得注意的取舍:

  • 默认模式:PRD 设计默认「自动」,当前源码默认「手动」(见 F-DRAFT-03 差异说明),建议以仓库实际行为为准进行体验评估;
  • 帮助入口:PRD 规划独立「?」按钮,当前实现将帮助 tooltip 合并到模式切换按钮上,窄栏下再并入「⋯」菜单,兼顾了信息可达性与宽度占用。

结语

AionUi 的「发送草稿箱」是一个"纯渲染层、不改后端"的典型交互升级:它把原本被动等待的排队面板,升级为具备常驻操作、立即插队、自动/手动双模式、防误触清空、拖拽排序与窄栏适配的完整消息暂存与发送控制台。从CommandQueuePanel的 UI 实现到useConversationCommandQueue的状态机、持久化与执行门控,再到覆盖行为契约的单元测试,整个能力链路清晰可查。若你正在设计同类"AI 忙碌时的消息缓冲"交互,这份 PRD 与其落地源码是极具参考价值的完整范例。

【免费下载链接】AionUi免费、本地、开源的 24/7 全天候 Cowork 应用,以及适用于 Gemini CLI、Claude Code、Codex、OpenCode、Qwen Code、Goose CLI、Auggie 等的 OpenClaw | 🌟 喜欢就点star吧项目地址: https://gitcode.com/iOfficeAI/AionUi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 20:29:32

大模型学习宝典:从Transformer到高效微调实战

1. 项目概述"大模型学习宝典"是一套面向AI从业者和深度学习爱好者的系统性学习指南,重点覆盖从Transformer基础架构到高效微调技术的完整知识体系。这个手册的独特价值在于:它不像传统教材那样按部就班讲解理论,而是以工业级应用为…

作者头像 李华
网站建设 2026/9/19 20:26:45

基于小波变换与信息熵的自适应图像去雾技术

1. 项目背景与核心价值图像去雾技术是计算机视觉领域的重要研究方向,主要解决雾霾天气下拍摄的图像对比度低、色彩失真等问题。传统去雾算法往往存在边缘细节丢失、色彩偏移等缺陷,而小波变换凭借其多尺度分析特性,能够有效保留图像高频信息&…

作者头像 李华
网站建设 2026/9/19 20:25:43

Edge鼠标手势完全指南:扩展选型与标签页控制实战

1. 为什么鼠标手势在Edge里值得单独折腾用Edge的人越来越多,但真正把鼠标手势用起来的人其实不多。大部分人日常操作标签页的方式还是老三样:鼠标移到标签栏、找到那个小小的叉、点一下;或者按CtrlW;再或者右键菜单里翻半天。这些…

作者头像 李华
网站建设 2026/9/19 20:24:11

告别论文“硬伤”:让汇写AI辅助写作,回归精准与合规

每逢毕业季,撰写论文都是一场考验耐力与智力的漫长战役。从选题的迷茫、文献的搜集,到逻辑框架的搭建和最终格式的排版,每一个环节都可能成为压垮同学们的最后一根稻草。为了帮助广大学子高效、高质量地完成学术任务,全新升级的智…

作者头像 李华