Yoda
参考Agent 设计指南UI 外显

Keybindings

CC 用 JSON 文件 + 上下文/动作模型 + chord 序列 + 热重载;Codex 用 config.toml 强类型 keymap,单键事件、无 chord

Keybindings

结论

两家都在 2026 年把键位开放成了用户配置,但模型深度差一代:CC 的 ~/.claude/keybindings.json 是 VS Code 式设计——20 个 UI context × 约 90 个 namespace:action 动作,支持多键 chord(ctrl+x ctrl+k)、command:* 直绑 slash command、null 解绑、chokidar 热重载,并自带"保留快捷键"校验器告诉你哪些键永远到不了应用层;Codex 的 [tui.keymap] 是 Rust 强类型配置——10 个 context 结构体、字段即动作,反序列化时就规范化 key spec 并拒绝未知字段,但明确不支持 chord(schema 注释直说"加 chord 需要另写运行时状态机")。终端本身才是共同的天花板:Shift+Enter、cmd 修饰键、Ctrl+M==Enter 这类问题两家都只能靠协议探测和外部终端配置绕过。

研究问题

  • CC keybindings.json 的绑定模型(context、action、chord、解绑)是什么?
  • Codex TUI 键位配置的 schema 与解析规则是什么?
  • 终端协议层有哪些不可逾越的限制,各家怎么缓解?

各 Agent 设计与实现

Claude Code

文件与加载~/.claude/keybindings.jsongetClaudeConfigHomeDir() 拼接,[一手源码] src_2026-03-31/keybindings/loadUserBindings.ts:116),chokidar 监听热重载,写入后等待 500ms 文件稳定再解析(loadUserBindings.ts:348、常量 L53-58)。快照期(2026-03-31)自定义键位还在 GrowthBook 门 tengu_keybinding_customization_release 后面、注释标注"仅 Anthropic 员工"(loadUserBindings.ts:7-10,42-47);线上文档显示 v2.1.18 起已对外 GA([一手文档] claude-code-docs/docs/keybindings.md)。

Schema([一手源码] keybindings/schema.ts):顶层 { $schema?, $docs?, bindings: Block[] },每个 Block 是 { context, bindings: { "keystroke": action | "command:xxx" | null } }

// schema.ts:64-171 节选:动作命名 namespace:action
'app:interrupt', 'app:exit', 'app:toggleTranscript',
'chat:submit', 'chat:newline', 'chat:cycleMode', 'chat:externalEditor', 'chat:stash',
'confirm:yes', 'confirm:no', 'confirm:cycleMode', ...
// schema.ts:195-200:command:* 直接把按键绑到 slash command
z.string().regex(/^command:[a-zA-Z0-9:\-_]+$/)  // "command:compact" 等价于输入 /compact
// null = 解绑默认快捷键

context 共 20 个(Global/Chat/Autocomplete/Confirmation/.../Pluginschema.ts:12-31;文档版又多了 Scroll/Doctor——快照滞后)。

按键解析parseKeystroke 接受修饰符别名(ctrl/control、alt/opt/option、cmd/command/super/win)与特殊键别名(esc→escape、return→enter、↑↓←→)([一手源码] keybindings/parser.ts:13-75);chord = 空格分隔的按键序列parseChord("ctrl+k ctrl+s")parser.ts:78-83),运行时由 resolver 维护 chord_started/chord_cancelled 状态机(keybindings/resolver.ts:15-20)。

保留键校验([一手源码] keybindings/reservedShortcuts.ts):三层清单——NON_REBINDABLE(ctrl+c 中断、ctrl+d 退出、ctrl+m 与 Enter 在终端中同为 CR 无法区分,L16-32)、TERMINAL_RESERVED(ctrl+z SIGTSTP、ctrl+\ SIGQUIT;注释特别说明 ctrl+s/ctrl+q 流控未列入因现代终端默认关闭,L34-53)、MACOS_RESERVED(cmd+c 等系统快捷键,L59+)。validate.ts(498 行)在加载时产出 warning 给 UI。

终端限制的缓解cmd 修饰键仅在支持 Kitty keyboard protocol 或 xterm modifyOtherKeys 的终端可用;Shift+Enter 换行依赖终端逐家支持,/terminal-setup 直接向 VS Code/Cursor/Alacritty/Zed 的配置文件写入键位映射,tmux 内还需 extended-keys 配置([一手文档] docs/terminal-config.mddocs/keybindings.md "Keystroke syntax")。

Codex CLI

文件与 schema~/.codex/config.toml[tui.keymap],由 codex-rs/config/src/tui_keymap.rs 定义强类型结构([一手源码] 文件头注释 L1-18 明确职责:schema 定义 + key spec 规范化 + 早期报错)。顶层 TuiKeymap 含 10 个 context:

// tui_keymap.rs:403-424
pub struct TuiKeymap {
    pub global: TuiGlobalKeymap,       // open_transcript / copy / submit / toggle_vim_mode ...
    pub chat: TuiChatKeymap,           // interrupt_turn / increase_reasoning_effort ...
    pub composer: TuiComposerKeymap,   // submit / queue / history_search_previous ...
    pub editor: TuiEditorKeymap,       // insert_newline / move_word_left / kill_whole_line ...
    pub vim_normal / vim_operator / vim_text_object / pager / list / approval,
}

approval context 把审批对话框动作也开放了:approve / approve_for_session / approve_for_prefix / deny / canceltui_keymap.rs:371-388)。

值形态:每个动作接受单个 key spec("ctrl-a")或列表(["ctrl-a","alt-a"]);空列表 [] 显式解绑,且解绑后不回退到全局/内置默认(KeybindingsSpec 注释,tui_keymap.rs:60-75)。Key spec 在反序列化时即规范化为 ctrl-alt-shift-<key> 顺序,别名归一(escape→escpageup→page-up),非法 spec 直接报配置错误(tui_keymap.rs:426-438;运行时错误提示见 tui/src/keymap.rs:1953)。功能键上限 F24(MAX_FUNCTION_KEYtui_keymap.rs:28)。

无 chord,是有意为之:schema 注释明说一个 spec 只表示"一个终端按键事件",ctrl-x ctrl-s 不是 chord,"加多步 chord 需要单独的运行时状态机"([一手源码] tui_keymap.rs:33-36)。shift-enterctrl-shift-enter 等组合是合法 spec(keymap.rs:1966keymap_setup.rs:1703),能否收到仍取决于终端协议。

交互配置/keymap slash command("remap TUI shortcuts")提供 picker 式改键([一手源码] tui/src/slash_command.rs:18,126;实现 tui/src/keymap_setup.rs 1932 行 + keymap_setup/{actions,picker,debug}.rs),写回 config.toml。deny_unknown_fields 让拼错的 context/action 在启动时即报错——与 CC"加载时 warning"相比是更硬的失败。

差异矩阵

维度Claude CodeCodex CLI
配置载体~/.claude/keybindings.json(JSON Schema 可发布给编辑器)~/.codex/config.toml [tui.keymap]
绑定模型keystroke → action 字符串映射,20 context × ~90 actioncontext 结构体字段即动作,10 context
chord 序列支持(空格分隔 + 运行时状态机)不支持(schema 注释明确拒绝)
解绑语义null[](且不回退默认)
绑 slash commandcommand:* 正则放行无对应机制
校验时机加载时 warning(保留键三层清单)反序列化即 error(unknown field / 非法 spec)
热重载chokidar 监听 + 500ms 稳定窗口未发现热重载路径(改 /keymap 即时生效)
交互配置/keybindings 打开文件(文档)/keymap 内置 picker
发布状态快照期 ant-gated,文档 v2.1.18 GA主干默认可用

最小复现

// ~/.claude/keybindings.json — 交换 Enter 与 Shift+Enter,并加一个 chord
{ "bindings": [ { "context": "Chat", "bindings": {
    "shift+enter": "chat:submit",
    "enter": "chat:newline",
    "ctrl+x ctrl+k": "chat:killAgents",
    "ctrl+u": null } } ] }
# ~/.codex/config.toml
[tui.keymap.composer]
submit = "ctrl-enter"
[tui.keymap.global]
toggle_vim_mode = ["ctrl-alt-v"]
copy = []   # 显式解绑

Harness 接入建议(Yoda 实践)

  1. GUI harness 的天然优势:Yoda 跑在 Electron 里,没有终端协议限制——Shift+Enter、cmd 组合、F13+ 都能原生捕获。两家 CLI 花在"绕终端"上的复杂度(/terminal-setup、tmux extended-keys、Kitty 协议探测)在 Yoda 内嵌终端场景反而要主动补:xterm.js 需开启 Kitty keyboard protocol / modifyOtherKeys 上报,否则用户在 Yoda 终端里的 CC 体验会差于 iTerm2。
  2. 键位配置检测与校验:把 CC schema.ts 的 zod schema(官方有 schemastore URL)和 Codex 的 JsonSchema(tui_keymap.rs 带 schemars derive,codex-rs/core/config.schema.json 已生成)拿来做 Yoda 的配置面板校验源,避免自己手维护动作清单。
  3. 冲突诊断:CC 的 reservedShortcuts 三层清单(不可绑/终端拦截/macOS 拦截)值得照抄为 Yoda 的通用规则库——它编码了"哪些键永远到不了应用"这一终端世界的隐性知识。
  4. 统一抽象要谨慎:CC 是 keystroke→action 映射、Codex 是 action→keystrokes 映射,方向相反;Yoda 若做统一改键 UI,内部模型应取 Codex 方向(action 为主键),导出时再各自转换。

失效条件

  • CC tengu_keybinding_customization_release 门已开(文档 GA),但 context/action 清单每版本都在涨(快照 20 context vs 文档 22)——需按 schemastore 的 JSON Schema 定期 diff
  • Codex 若实现 chord 状态机,"无 chord"结论作废(schema 注释已预留了说法)
  • 两家对 Kitty keyboard protocol 的支持范围变化会改写"终端限制"一节
  • CC 重建源码仅到 2026-03-31,Scroll/Doctor context、chat:clearScreen 等仅见于文档,未经源码确认

参考资料

  • [一手源码] claude-code-source-code/src_2026-03-31/keybindings/(schema.ts / parser.ts / resolver.ts / reservedShortcuts.ts / loadUserBindings.ts,重建源码,仅作架构描述与短引用)
  • [一手文档] claude-code-docs/docs/keybindings.mdclaude-code-docs/docs/terminal-config.md
  • [一手源码] codex/codex-rs/config/src/tui_keymap.rscodex/codex-rs/tui/src/keymap.rscodex/codex-rs/tui/src/keymap_setup.rscodex/codex-rs/tui/src/slash_command.rs:18,126

On this page