- 人工智能
- 大模型
- AI 应用
- 交互助手
- 本地部署
【免费下载链接】cherry-studio
🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端
Cherry Studio 从 v2 重构的某次变更(PR #18642,2026-08-15)起,将聊天、Agent 与绘画三类消息输入框的换行行为从「Enter / Shift+Enter / Cmd+Ctrl+Enter 一律插入换行」改为「单一可配置的换行快捷键」,并顺势引入Steer(转向)快捷键用于在 Agent 运行中把草稿直接送入当前轮次。本文以官方 breaking-change 文档为主线,结合仓库源码说明新快捷键模型、互斥解析逻辑与底层按键分发实现,帮助用户与开发者理解变化并完成迁移。
变更概览:换行不再是「三键通用」
在旧版本中,聊天、Agent、绘画消息输入框对Enter、Shift+Enter、Cmd/Ctrl+Enter一视同仁——任意组合都会插入一个换行符。从 2026-08-15 起(变更文档见 v2-refactor-temp/docs/breaking-changes/2026-08-15-composer-mod-enter-no-longer-inserts-line-break.md),行为被收敛为:
- 换行只由一个可配置快捷键完成,默认
Shift+Enter; - 配置入口位于Settings > Chat > Input > Line break shortcut;
- 发送(send)、换行(line break)、Steer 三个角色必须使用不同的按键组合,不允许互相占用;
- 默认情况下
Cmd/Ctrl+Enter处于空闲状态,并成为Steer 快捷键:在 Agent 运行时,按下它会把当前草稿送入正在进行的这一轮对话,而不是排入队列等待。
文档同时指出:任何既不属于发送、也不属于换行、也不属于 Steer 的 Enter 组合,将什么都不做(不再退回为插入换行)。
为什么这对用户有影响
这是一次明显的行为破坏(breaking change),对两类用户影响最大:
- 习惯用
Cmd/Ctrl+Enter换行的用户:该组合不再插入换行;在 Agent 会话中它现在会执行 Steer,即把草稿直接注入当前运行轮次。 - Windows / Linux 用户:发送快捷键失去了
Win/Super+Enter选项。该组合被操作系统保留,在非 macOS 平台上永远不会触发(文档称其 "was reserved by the OS and could never fire")。如果此前选择了这一项,迁移后发送将自动回落到Ctrl+Enter。
另外,三个快捷键的标签显示也切换到应用的共享按键词表(shared key vocabulary):例如 macOS 上显示为⌘Enter而非⌘ + Enter,与其他设置页(Settings > Shortcuts)的写法保持一致。
新快捷键模型:发送 / 换行 / Steer 三角色互斥
新模型把输入框的 Enter 组合划分为三个互斥角色,每个角色各占一个组合,且不可复用:
| 角色 | 默认值(macOS) | 默认值(Windows / Linux) | 说明 |
|---|---|---|---|
| Send(发送) | Enter | Enter | 发送当前草稿 |
| Line break(换行) | Shift+Enter | Shift+Enter | 在输入框中插入换行 |
| Steer(转向) | ⌘Enter(Cmd+Enter) | Ctrl+Enter | Agent 运行时,把草稿送入当前轮次而非排队 |
三者互斥的规则体现在设置界面中:在 ChatPreferenceSections.tsx 中,每个下拉框都会隐藏另外两个角色已经占用的按键:
// Send / newline / steer are mutually exclusive: each select hides the two keys already in use. // Bindings are arrays, so the select trades in their ids and maps back on change. const shortcutItemsExcluding = useCallback((...taken: ComposerShortcut[]): SelectOption<string>[] => { const takenIds = taken.map(composerShortcutId) return COMPOSER_SHORTCUTS.filter((shortcut) => !takenIds.includes(composerShortcutId(shortcut))).map( (shortcut) => ({ value: composerShortcutId(shortcut), label: getComposerShortcutLabel(shortcut) }) ) }, [])发送下拉排除当前换行与 Steer 组合(L211),换行下拉排除发送与 Steer(L229),Steer 下拉排除发送与换行(L247),从 UI 层面就杜绝了按键冲突。
源码级解析:快捷键解析与互斥的兜底逻辑
互斥不仅停留在 UI 层面,存储与解析层同样有兜底。核心实现位于 src/renderer/utils/input.ts:
可选项集合(L85-L91):
export const COMPOSER_SHORTCUTS: readonly ComposerShortcut[] = [ ['Enter'], ['Shift', 'Enter'], ['CommandOrControl', 'Enter'], ['Alt', 'Enter'], ...(isMac ? [['Ctrl', 'Enter'] as ComposerShortcut] : []) ]注意CommandOrControl在 macOS 上代表 Command(⌘),在其他平台代表 Control(Ctrl),因此纯 Ctrl 组合只在 macOS 上才是独立选项(macOS 上 ⌘ 与 Ctrl 是两颗不同的键)。
默认值解析(L126-L135):
export const resolveSendShortcut = (stored: unknown): ComposerShortcut => resolveComposerShortcut(stored, [], ['Enter']) export const resolveNewlineShortcut = (stored: unknown, send: ComposerShortcut): ComposerShortcut => resolveComposerShortcut(stored, [send], ['Shift', 'Enter']) export const resolveSteerShortcut = ( stored: unknown, send: ComposerShortcut, newline: ComposerShortcut ): ComposerShortcut => resolveComposerShortcut(stored, [send, newline], ['CommandOrControl', 'Enter'])三者各自的偏好(preference)默认值定义为:发送['Enter']、换行与 Steer 均为null(表示未覆盖,使用生效默认值),见 preferenceSchemas.ts。换行与 Steer 存null的设计意味着:即使用户修改过发送快捷键,未显式覆盖的换行 / Steer 也会随发送键自动漂移到新的空闲组合。
互斥兜底(resolveComposerShortcut):如果存储值被另一角色占用、或首选默认值被占用,则回落到第一个空闲组合;极端情况下才使用首选值兜底:
const resolveComposerShortcut = ( stored: unknown, taken: readonly ComposerShortcut[], preferred: ComposerShortcut ): ComposerShortcut => { const takenIds = taken.map(composerShortcutId) const shortcut = normalizeComposerShortcut(stored) if (shortcut && !takenIds.includes(composerShortcutId(shortcut))) return shortcut if (!takenIds.includes(composerShortcutId(preferred))) return preferred return COMPOSER_SHORTCUTS.find((candidate) => !takenIds.includes(composerShortcutId(candidate))) ?? preferred }旧值迁移(LEGACY_COMPOSER_SHORTCUTS):v1(以及 v2 早期)存储的是五种固定字符串而非绑定数组,迁移映射中'Command+Enter'当年匹配的是Meta 键,即非 macOS 上的 Win/Super 键——这正是文档所说"被 OS 保留、永不触发"的历史根源。迁移后该组合统一映射为['CommandOrControl', 'Enter'],因此 Windows/Linux 上先前选择它的人现在实际以Ctrl+Enter发送。
源码级解析:输入框按键分发与 IME 防护
按键的实际分发发生在共享输入框组件 ComposerSurface.tsx。其onKeyDown处理逻辑严格遵循"Steer 优先于 Send、其余 Enter 组合一律吞掉"的优先级顺序,与运行时输入框(runtime surface)保持一致,避免两处逻辑漂移:
const isEnterPressed = (event.key === 'Enter' || event.key === 'NumpadEnter') && !event.nativeEvent.isComposing if (!isEnterPressed) return event.preventDefault() const isSteerPressed = !!props.steerShortcut && matchesComposerShortcut(event, props.steerShortcut) if (isSteerPressed || matchesComposerShortcut(event, sendMessageShortcut)) { // Holding the key must not send twice; holding the newline key still repeats. if (event.repeat) return if (props.sendDisabled) { showBlockedSendReason() } else if (isSteerPressed) { void props.onSendDraft(getFallbackDraft(), { steer: true }) } else { void props.onSendDraft(getFallbackDraft()) } return } if (matchesComposerShortcut(event, newlineShortcut)) insertFallbackNewline(event.currentTarget)这段实现中有三个值得注意的工程细节:
- IME 组合防护:
!event.nativeEvent.isComposing确保中文等输入法组合过程中的 Enter(用于选词上屏)不会被误判为发送/换行,onCompositionStart/End同步维护组合状态(L299-L307); - 按住防重复:
event.repeat使按住发送/Steer 不会重复触发,而按住换行键仍可连续插入空行; - 未绑定组合被吞掉:既非 Steer、非 Send、也非 Newline 的 Enter 组合在
event.preventDefault()之后直接返回,不再插入换行——与 breaking-change 文档描述完全一致。
按键匹配通过 matchesComposerShortcut 完成,它调用共享工具 getShortcutBindingFromKeyboardEvent 将键盘事件归一化为绑定数组(含平台差异:darwin 下metaKey→CommandOrControl、ctrlKey→Ctrl;其他平台ctrlKey→CommandOrControl、metaKey→Meta),再与目标快捷键比对。
Steer 的语义:送入当前轮次而非排队
Steer 的核心语义在 AgentComposer.tsx 的handleSendDraft中实现:
// Busy (streaming) → queue the follow-up; the head auto-drains when the session goes idle and // the dock lets the user steer/edit/remove items. The steer shortcut opts out of the queue and // falls through to the direct send below, mirroring the dock's "insert" action. if (isStreaming && !options?.steer) { enqueueFollowup(draft, payload) clearCurrentDraft() return }即:Agent 正在流式输出(isStreaming)时,普通发送会把草稿排入后续队列(enqueue),等待当前轮次空闲后自动消化;而携带steer: true的发送则跳过队列直接发送,把草稿注入当前运行轮次。这与输入框 Dock 上的"insert"(插入当前轮)动作互为镜像——Steer 快捷键就是它的键盘入口。同时 Steer 会保留在 Dock 中并弹出 toast,与直接发送/自动排空路径的反馈一致(L1747、L1773)。
用户需要做什么
无需任何操作——迁移是自动的:
Shift+Enter继续作为换行快捷键工作,行为与旧版一致;- 若想恢复
Cmd/Ctrl+Enter换行的旧习惯,只需在Settings > Chat > Input > Line break shortcut中把它设为换行快捷键; - 设置为换行后,Steer 会按互斥规则自动让位到下一个空闲组合;
- 若之前在 Windows/Linux 上选择了
Win/Super+Enter作为发送键,现在将以Ctrl+Enter发送。
相关偏好键速查
新快捷键模型对应三个偏好键,定义与默认值见 preferenceSchemas.ts:
| 偏好键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
chat.input.send_message_shortcut | ComposerShortcut | ['Enter'] | 发送快捷键 |
chat.input.newline_shortcut | ComposerShortcut \| null | null | 换行快捷键,null表示使用生效默认值Shift+Enter |
chat.input.steer_shortcut | ComposerShortcut \| null | null | Steer 快捷键,null表示使用生效默认值Cmd/Ctrl+Enter |
三个角色的互斥约束由 resolveComposerShortcut 在运行时兜底,即使偏好数据被外部写入冲突值也不会导致两个角色共用一个按键。
- 人工智能
- 大模型
- AI 应用
- 交互助手
- 本地部署
【免费下载链接】cherry-studio
🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端
相关推荐
Voyager 发送行为调整指南:将 Gemini 发送快捷键改为 Ctrl+Enter(多行提示词输入)
Voyager 发送行为调整指南:将 Gemini 发送快捷键改为 Ctrl+Enter(多行提示词输入) 导读 本文讲解 Voyager 扩展的“发送行为(S
AI 应用前端Voyager 发送行为指南:将 Gemini 的 Enter 改为换行、用 Ctrl+Enter 发送
Voyager 发送行为指南:将 Gemini 的 Enter 改为换行、用 Ctrl+Enter 发送 适用版本 :Voyager 1.3.7 及后续版本。本
AI 应用前端Cherry Studio File Manager v2 迁移执行计划:FileMetadata 字段级退役与消费域切换实战指南
Cherry Studio File Manager v2 迁移执行计划:FileMetadata 字段级退役与消费域切换实战指南 本指南以 Cherry St
人工智能大模型AI 应用交互助手本地部署
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考