Kun 的 DSH 风格 Composer QueueDock:繁忙轮次待发消息的呈现、编辑与排序管理
人工智能AI Agent自主智能体桌面应用MCP Clients【免费下载链接】KunLocal-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.项目地址https://gitcode.com/gh_mirrors/de/Kun点击查看免费下载导读本篇文章基于 Kun 仓库中的 OpenSpec 变更提案《match-deepseek-harness-composer-queue-ui》展开讲解 Kun 如何在桌面 GUI 中把繁忙轮次期间排队等待发送的后续消息从旧的QueueStrip hover Portal交互重构为贴合 DeepSeek Harness 冻结参考实现的Composer QueueDock依附于输入框的队列停靠坞。读完本文你将掌握单行/多行折叠展开的 disclosure 语义、把排队消息还原到输入框的安全编辑策略、Guide/Retry/Remove 动作的串行化与失败保留、展开队列内的拖拽与键盘排序以及 36px/28px/12px/180px 等固定几何参数与 Kun 语义主题令牌的映射方式并了解对应组件、store 与测试在仓库中的具体位置。一、为什么改旧队列呈现的三个痛点Kun 此前已经在 renderer 侧实现了完整的排队消息机制线程级持久化、FIFO 顺序排空drain、暂停/失败恢复以及显式的当前轮次引导mid-turn guidance。但它的呈现层存在明显短板提案 proposal.md 将其归纳为三点信息被压缩隐藏旧实现只把第一条消息投影到一条脱离输入框的QueueStrip上其余消息折叠成N徽标完整列表要悬停或聚焦触发 Portal 才可见。多条待发输入难以被发现也更难控制。编辑语义是破坏性的旧交互中的编辑overflow Edit menu会执行出队到草稿dequeue-to-draft操作本质上是把消息从队列里取出容易让人误以为只是修改。失败消息被静默过滤失败状态的排队消息被直接隐藏用户无法看到失败原因也没有重试入口。而 DeepSeek Harness 的冻结参考实现master0a53fb55bea101816fa226bb964ae2bed71c343b采用依附输入框的 QueueDock单条直接展示、多条折叠成总数、编辑把消息交回输入框、失败时队列仍然可见。Kun 据此发起本次 UI 对齐改造设计文档见 design.md验收需求见 specs/composer-queue-dock/spec.md。二、目标与非目标对齐外观不动运行时语义设计文档明确划定了本次改造的边界Goals要做完全对齐冻结参考 QueueDock 的层级、几何、折叠规则、编辑流程、动作顺序、可访问性、主题行为与窄宽度约束保留 Kun 现有的 FIFO 交付、图片/Plan/Graph 引导资格、暂停/失败恢复、幂等性、冻结的 model/provider/routing 快照以及线程级持久化让失败消息可见且可恢复允许用户在展开的 QueueDock 内通过直接操作把靠后的消息移到更前补充确定性的组件测试、store 测试与 Electron 交互/视觉证据。Non-Goals明确不做不改变运行时 steering 路由、终态密封terminal sealing、交付顺序、持久化格式、发送快捷键偏好或普通繁忙轮次的提交行为不引入 DSH 的全局队列/steer 快捷键策略也不会自动 steer 排队消息不在列表折叠、行还原或其它队列变更进行中时提供排序或 overflow 菜单。设计文档还特别强调历史上存在一个被完全 revert 的提交8fbb9885c混入了无关的消息时间线行为因此实现必须从冻结的 Harness 源码按当前 Kun 合约重建 QueueDock而不是直接 cherry-pick 那个提交。三、核心交互一单行直显 / 多行折叠展开Disclosure组件 FloatingComposerQueuedMessages.tsx 实现了 DSH 风格停靠坞。其规则完全对应 spec 中的 Requirement QueueDock uses reference disclosure behavior可见排队消息数呈现方式0 条不渲染任何 dockqueue.length 0时组件直接返回null1 条直接渲染完整行无计数头部、无 Portal2 条及以上默认折叠成总计数头部按钮点击在原位展开/折叠完整的 FIFO 列表关键实现细节在 FloatingComposerQueuedMessages.tsxif (queue.length 0) return null const interactionActive busy ! null const expanded queue.length 1 || !collapsed || interactionActivevisibleQueue负责过滤可见行L124-L135pending、paused、failed、starting、in_flight均可见起始/在途行保持可见是有意设计其用户消息已归属一个被受理的 turn但用户仍可查看状态一旦 runtime 真正开始执行该 turnqueuedMessageStartedByRuntime排空逻辑会将其从队列中消费掉避免与时间线中的用户消息重复见 queued-message-persistence.ts交互期间强制保持展开当 restore 或队列变更进行中且队列含多条消息时头部折叠按钮被disableddisabled{interactionActive}防止用户在异步动作进行中误折叠队列清空后重置折叠状态useEffect在队列长度归零时setCollapsed(true)L167-L169保证下一次多行队列重新以折叠形态出现。计数头部的文案由queuedMessagesTitle/queuedMessagesConfirming两个 i18n key 提供并带count参数多语言文案集中在 phone-composer.json 等 locale 文件中。停靠坞如何长在输入框上设计上QueueDock 不再属于绝对定位的 hover 状态栈而是通过FloatingComposerAboveInputStack新增的attachedDock座位渲染在浮动状态之前FloatingComposerAboveInputStack.tsx实际装配点在 FloatingComposerStackView.tsx 的attachedDock{(FloatingComposerQueuedMessages ... /)}。这样 dock 的底边被 composer 卡片物理封住不会被 Todo/Graph/Goal 等浮层从中间隔开。组件根节点上的data-composer-stack-itemqueue标记也使其可以参与 composer 栈的布局管理。四、核心交互二编辑 还原到输入框且冻结投递设置这是本次改造中最关键的语义变化不再提供行内编辑器编辑动作一律把消息从队列还原回 composer 输入框随后输入框获得焦点供继续修改。4.1 安全还原策略restore eligibility不是所有排队消息都能被忠实重建回输入框。纯策略函数位于 queued-message-edit.tsqueuedMessageEditBlockReason对以下情况返回阻止原因存在steeringRequest引导请求在途waitForRuntimeAdmission为 true暂定受理未定deliveryState是starting确认中携带 persona、guiPlan、graph 编排等派生结构agentSurface为write/design携带 subagentResume、messageSource、fileReferences、composerContexts、设计画布/设计模式/设计工件、writeContext 等无法重建的负载附件中包含document类附件文档内容已被内联进 prompt无法还原为输入框附件文本与附件均为空。只有pending 的纯文本可选displayText缺失或与text完全一致或图片附件消息才允许还原。queuedMessageComposerRestoreText还处理了一个细节Plan/auto 队列消息会把用户原始请求存在displayText、内部计划 prompt 存在text还原时取回displayText纯图片消息的text是合成 prompt还原时保留空文本、依赖附件恢复。4.2 store 侧的还原动作store 动作restoreQueuedMessage(id, accept)实现在 chat-store-thread-queue-actions.ts流程是通过withQueueMutation加互斥锁见下文动作串行化若行处于starting且受理未定先awaitQueueAdmission等待受理落定用queuedMessageEditBlockReason做二次校验被拒绝则走现有error通知路径行保持排队不动通过restoreQueuedMessageFromQueuequeued-message-edit.ts按稳定 id从数组中移除该消息不触碰其它记录、不改变顺序若该行已进入 runtime 队列有deliveryTurnId先saveQueueEditIntent把行标记为editIntent: cancelling并调用provider.cancelQueuedTurn取消服务端排队 turn再标记为restoring通过accept(message)回调把文本追加到现有草稿、恢复图片附件、重放冻结的 mode/model/reasoning/permission 设置然后持久化并从队列中删除该行。设计文档强调还原被拒的场景id 不存在、负载不支持、行已 paused/failed/starting/in-flight都不会删除原行还原过程中若队列实时更新导致该行被移除composer 草稿保持完整用户继续编辑即可。五、核心交互三动作串行化与失败可见Guide / Retry / Remove5.1 动作单飞行single-flight组件维护一个本地busy: { id, kind }状态FloatingComposerQueuedMessages.tsxapplyAction包装器保证任意时刻只有一个动作在途L255-L269busy非空时折叠头部、所有行的编辑/移除/引导按钮全部禁用行内图标替换为旋转的Loader2spinner。store 层还有第二道互斥queue-mutation-fence.ts维护线程级mutations集合withQueueMutation在动作执行期间占用、结束后释放queue-mutation-fence.ts。这与组件层busy配合防止并发 drain 与用户操作竞争。5.2 Guide 与 Retry普通 pending 行的动作顺序为Edit → Remove → Guide参考顺序。canGuide判定规则FloatingComposerQueuedMessages.tsx很细致pending 行仅当有活跃 turn、guidanceEligible ! false且queuedMessageGuidancePayload非空时可引导引导受steeringRequest/deliveryTurnId等合约约束paused/failed 行按钮文案变为Retry仅当当前没有活跃 turn 且该行没有等待 runtime 受理!message.waitForRuntimeAdmission时可用waitForRuntimeAdmission的失败行保留 Remove 但禁用 Retry——因为其原始受理等待器已落定重放会导致在 drain 期间消息再次消失。对应 i18n 文案为queuedMessageRetryUnavailableThis provisional message cannot be retried safely; remove it and submit again。store 动作guideQueuedMessagechat-store-thread-queue-actions.ts负责真正执行对 graph turn 走steerSourceTurn否则调用 provider 的steerUserMessage对 paused已 admission行优先resumeQueuedTurns恢复服务端队列对 failed 行重新生成clientRequestId并标记为 pending 后重新 drain。引导成功后该行从队列移除并乐观插入用户块失败时行保留在队列中error路径负责通知。5.3 失败消息保持可见spec 的 Requirement Queue actions are lossless and serialized 明确失败行在 QueueDock 中始终可见行内显示失败状态与errorMessage/errorCode36px 行高不变完整错误通过title属性暴露并提供 Remove仅当活跃 turn 空闲且行未被已落定的受理等待器绑定时才启用 Retry。reconcileQueuedMessagesqueued-message-persistence.ts保证终态失败跨 reconciliation 保持failed只有显式 Retry 或 Remove 才会改变它。六、核心交互四展开列表内的拖拽与键盘排序DSH 参考实现本身不提供排序这是 Kun 在保持参考几何不变的前提下为用户需求增加的扩展见 design.md 的 Reorder expanded queues by stable identity。启用条件FloatingComposerQueuedMessages.tsxconst reorderEnabled Boolean(onReorder expanded queue.length 1 !interactionActive !queue.some((message) message.editIntent || message.deliveryState starting))即列表已展开、可见行数 ≥ 2、无进行中的编辑/变更、无 restoring/starting 行时才显示 28px 的拖拽把手GripVertical。拖拽逻辑HTML5 drag 事件中dragover时用指针与目标行中点的clientY比较选择before/afterL218-L232CSS 通过[data-drop-positionbefore]::before/after渲染 2px 插入指示线FloatingComposerQueuedMessages.module.cssdrop时校验源/目标 id 都仍存在再调用持久化的reorderQueuedMessage(sourceId, targetId, position)。键盘排序把手聚焦后按ArrowUp/ArrowDown每次移动一个可见位置focusReorderHandle保证焦点停留在稳定把手上同时通过rolestatus的aria-live区域播报新位置L190-L206。store 侧排序chat-store-thread-queue-actions.ts若两行都已进入 runtime 队列有deliveryTurnId先调用provider.moveQueuedTurn同步服务端顺序随后在本地数组中按稳定 id 重新插入并持久化。queue store 是唯一顺序权威——组件本地拖拽状态从不修改影子数组queueRevision变化即清空临时拖拽状态源行/目标行在 drop 前被实时队列移除时不会误排序其它行。七、几何与主题固定 Harness 尺寸 Kun 语义令牌CSS Module FloatingComposerQueuedMessages.module.css 完整落地了参考几何几何参数值CSS 位置头部/行高36px.header, .row动作按钮尺寸28pxwidth/height.action拖拽把手尺寸28px.dragHandle动作间距10pxgap.actions顶部圆角12px底部直角贴合 composer.panel内部滚动上限180pxmax-heightoverflow-y: auto.listcomposer 上缘 3px 收进margin: 0 auto -3px.dock色彩方面不引入任何硬编码的仅亮色颜色或独立的深色分支全部映射到 Kun 语义令牌--ds-surface-subtle面板底、--ds-border/--ds-border-muted边框与分隔、--ds-text/--ds-text-muted/--ds-text-faint文本层级、--ds-accent焦点环与插入指示线、--ds-danger失败状态暂停状态用#d97706与文本色混合。prefers-reduced-motion: reduce下 spinner 动画被禁用状态传达不依赖动画。窄布局约束dock 宽度为calc(100% - 1rem)并居中预览文本text-overflow: ellipsis单行省略状态徽标限宽 30%不再有固定 Portal 溢出视口。八、可访问性与本地化spec 的 Requirement QueueDock matches reference geometry and accessibility 对无指针用户有明确要求实现要点折叠头部按钮带aria-controls{listId}与aria-expanded{expanded}每个动作按钮有可访问名称aria-label与禁用说明title编辑按钮不可用时label 切换为queuedMessageEditUnsupported等阻止原因文案还原成功后 composer 输入框获得焦点排序把手带aria-label含位置序号与消息摘要与aria-describedby提示位置变更通过rolestatusaria-livepolite播报主题行为语义表面/边框/文本令牌保证亮色、深色与自定义主题下状态对比度可读。九、兼容性与迁移持久化与交付合约不变spec 的最后一个 Requirement Existing queue delivery contract remains compatible 与 design 的 Migration Plan 呼应无持久化迁移排队记录仍存于kun.queuedMessages.v1注册表queued-message-persistence.ts旧的 pending/paused/failed/starting/in_flight 记录由新 dock 的可见性规则直接投影不重写存储普通繁忙轮次提交未激活 Guide仍按原 FIFO 语义进入下一轮次交付drainQueuedMessages循环chat-store-thread-queue-actions.ts行为不变回滚只需还原旧 renderer 组件store/runtime 队列数据保持兼容。十、验证与测试从组件到 Electron 冒烟任务清单 tasks.md 记录了四轮验证工作Store/契约新增并持久化restoreQueuedMessage(id)纯函数安全还原策略restore 经 Workbench 与 composer props 接通RendererattachedDock座位、单/多行 disclosure、restore-to-composer 编辑、动作单飞行、失败保留与 Retry、CSS Module 几何与多语言文案组件测试FloatingComposerQueuedMessages.dock.test.ts 覆盖单/多行 disclosure、实时更新、还原编辑、动作锁定、失败状态与可访问性以及拖拽/键盘排序与竞态安全 no-opElectron 冒烟FloatingComposerQueueDockSmokeFixture.tsx 提供single/multi/long/failed四种确定性场景覆盖 36/28/12/180 几何、与 composer 的贴合、窄布局以及亮/暗双主题截图最后跑npm run typecheck、npm run build:kun、npm run build、ESLint、文件行数/OpenSpec/diff 检查并基于独立 worktree 从本地developHEAD 以 fast-forward 合入、验证改动范围仅限 queue/composer/OpenSpec/测试文件。十一、风险与权衡来自 design.md风险缓解还原派生 prompt 导致可见文本与 runtime 文本失同步仅允许 composer 可忠实重建的消息纯文本镜像或图片附件拒绝文档/结构化负载实时队列更新移除了正在还原的行行离开队列composer 草稿完整编辑在输入框继续还原期间第二条消息到达交互状态强制新多行 dock 展开长队列盖过 composer列表上限 180px仅列表内部滚动拖拽与队列投递竞争以稳定 id 寻址、变更期间禁拖、实时退役时清空临时拖拽态、store 空操作安全收敛失败项包含超长错误文本行保持 36px显示紧凑失败指示完整错误经 title 属性暴露暂定受理失败被重试waiter 已落定禁用不安全 Retry保留行引导用户 Remove 后重新提交结语本次改造把 Kun 的排队消息呈现从隐藏 Portal升级为依附输入框的停靠坞用一套精确到像素的几何参数对齐 DeepSeek Harness 参考实现同时把编辑语义从破坏性出队改为安全的还原到输入框让失败消息可见可恢复并以store 为唯一顺序权威的方式增加了拖拽/键盘排序。整套设计围绕一个不变式展开UI 可以重做但 FIFO 交付、持久化格式与运行时语义必须原样保留。如果你需要在 Kun 桌面端定位排队消息相关的进一步开发可以从 FloatingComposerQueuedMessages.tsx呈现、chat-store-thread-queue-actions.tsstore 动作与 queued-message-persistence.ts持久化与调和三条主线入手。赞分享人工智能AI Agent自主智能体桌面应用MCP Clients【免费下载链接】KunLocal-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.项目地址https://gitcode.com/gh_mirrors/de/Kun点击查看免费下载相关推荐DeepSeek Harness 待处理队列的寻址编辑与移除InboxItemId、权威快照与 QueueDock 交互设计DeepSeek Harness 待处理队列的寻址编辑与移除InboxItemId、权威快照与 QueueDock 交互设计 本文依据仓库内 Agent No人工智能AI AgentAgent 框架DeepSeek彻底解决消息乱序nats-server消息排序的实现与最佳实践彻底解决消息乱序nats server消息排序的实现与最佳实践 你是否曾因分布式系统中的消息乱序问题而头疼订单支付通知比下单消息先到达导致状态异常传感器后端消息队列消息路由通信消息管理终极指南Rocket.Chat消息撤回与编辑全攻略消息管理终极指南Rocket.Chat消息撤回与编辑全攻略 在团队协作中发送错误消息或需要修改已发送内容的情况时有发生。Rocket.Chat作为一款注重数即时通讯后端前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考