Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Agent 笔记:终端聚焦时保留切换终端快捷键

状态:已实现

## 先说结论

终端有焦点时,用户配置的“切换终端”快捷键仍须关闭终端面板;其他纯 Control 组合键继续交给 shell。先根据当前有效键位识别命令,再决定是否让终端处理,不能按来源 IDE 或某个固定按键写特例。

## 问题

VS Code 的默认终端切换键是 Control 加反引号。导入后它能从编辑器打开终端,却不能从已聚焦的终端关闭,因为 #1360 为保护 shell 将所有纯 Control 组合键交给了终端。该保护仍然需要:Control-D 是结束输入,Control-R 常用于历史搜索。

## 决策

macOS 事件监听器复用当前有效注册,先识别命令;终端持有焦点且修饰键只有 Control 时,仅让绑定到 `toggle-terminal` 的键交给应用,其余按键交回终端。用户主动把终端切换设为 Control-D 时,该显式绑定会优先于终端输入;无绑定时不截获。输入法标记文字和 HTML 预览原有优先级保持不变。

正确例子:导入的 Control 加反引号能打开和关闭终端,Control-D 仍能结束未显式将它设为切换键的 shell。不要在监听器中对 VS Code 或反引号硬编码,也不要让所有已注册的 Control 组合键抢走 shell 输入。

## 考虑过的备选方案

- 所有 Control 组合键都交给应用:会恢复 #1360 的 shell 输入回归。
- 仅放行 Control 加反引号:会忽略用户自定义的终端切换键,且把来源 IDE 键位写死在事件监听器。

## 后果

用户显式指定的终端切换键在终端内有效,其他应用快捷键仍不抢终端 Control 输入。显式选择与 shell 功能相同的键时,该键会用于切换终端,这是可配置快捷键的直接结果。

## 验证

- `TerminalShortcutInputPolicyTests` 使用真实 AppKit 事件监听器验证切换、Control-D、Control-R 和普通应用组合键;输入法及 HTML 预览回归与其共同运行。
- 隔离的 macOS Release 包导入 VS Code 键位后,从编辑器按 Control 加反引号打开终端,终端有焦点时再次按下成功关闭;重开后 Control-D 结束测试 shell。包内文件清单和 SHA-256 在流程前后保持一致。
- `./scripts/macos/verify/verify-runtime-bundle-immutability.sh` 和 `./scripts/agent-notes/verify-agent-notes.sh`。

## 适用范围

- `macos/Sources/Lithe/Platform/MacOS/UI/MacShortcutDetector.swift`
- `macos/Tests/LitheTests/TerminalShortcutInputPolicyTests.swift`
- `shared/platform-feature-matrix/features/terminal-interaction.json`
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,38 @@ VS Code/Cursor 用户规则按来源顺序处理按键占用。后续规则无
`titledPanelDetachesDuringDragAndCleansUp` 覆盖内容区点击不解除、标题栏解除、拖动期间不重新居中、松手恢复、移动结束兜底,以及拖动期间关闭弹窗或父窗口。独立的生产 presenter 原生检查在深浅色下共八个场景通过;这些生命周期检查不代表 macOS 27 实机帧率验收。

移动通知回归通过注入的按键状态读取闭包明确覆盖“按住时不挂回、释放后挂回”;生产默认仍读取 `NSEvent.pressedMouseButtons`。测试不再依赖用户当前是否按住实体鼠标,桌面隔离无需修改。

## 常用导航和查找快捷键

IDEA 的旧 `Mac OS X` 方案用 `⌘G` 跳转到行,而 Lithe 默认用它查找下一个结果。只导入前者会让整批导入因冲突失败,因此同时导入来源方案的查找前后项;新旧方案分别使用各自的参考键位。自定义 IDEA 动作也支持映射已有的查找、替换、定义和引用命令。VS Code 的参考默认值补充文件内查找和替换、跳转定义与查找引用,并支持这些命令的无条件用户覆盖。

参考 [OpenIDEA 的平台预设](https://github.com/l0ng-ai/openidea/blob/main/src/platform/keymaps/vscode-macos.ts) 按动作同时处理相关快捷键,来源键值以 [VS Code 官方参考](https://code.visualstudio.com/docs/reference/default-keybindings) 和既有固定版本的 JetBrains Community 键位为准。Lithe 仍合并到用户选中的预设,不复制上游整个预设;条件规则及连续键不会被提升为全局命令,真实冲突仍拒绝写入,避免静默覆盖用户设置。回归检查导入、实际注册、重开和撤销;真实产品与其他键盘布局仍需单独验收。

2026-11-03 打包应用验收:隔离开发版实际读取本机 IDEA 2025.3 和 VS Code 配置,只导入快捷键;重启后分别验证 IDEA `⌘L` 跳行、`⌘G` 查找下一个,以及 VS Code `⌃G` 跳行、`⌥⌘F` 替换、`⇧⌘F` 项目搜索。隔离配置覆盖旧 `Mac OS X` 方案,确认 `⌘G` 跳行和 `⌃L` 查找下一个;另一隔离 VS Code Profile 验证自定义 `⌥⌘G`。Java 测试文件中,导入的 `F12` 从调用跳到定义,`⇧F12` 展示引用。Control 加反引号 能打开终端,但终端有焦点时再按不会关闭,焦点移到搜索框后可关闭:纯 Control 键当前让给终端输入,不能宣称这个切换流程完全通过。IDEA 和 VS Code 的其他版本、条件规则、连续键及非美式键盘布局仍待验证。

上述终端切换问题的修复和后续实机复验见 [终端聚焦时保留切换终端快捷键](../bug-fix/2026-11-03-macos-terminal-toggle-shortcut.md)。


## 导入快捷键与目标默认键冲突

来源导入键和目标未修改的默认键重叠时,Core 根据目标当前注册与自定义标记规划让位,只移除被占用的默认按键,保留同一动作的其他按键。用户自定义键位、来源内两个动作争用同一键时报告冲突并阻止整批写入;来源中同时重设两个动作时允许交换。macOS 导入事务在既有源版本和目标基线检查之后应用计划,并保留撤销能力。计划失败或存在不可表达的来源键时拒绝导入,不丢弃自定义绑定。

定向回归覆盖 F9 让出默认调试继续键、用户自定义键保护、冲突无部分应用及撤销。该测试使用构造的来源结果;IDEA/VS Code 插件解析及修改后的打包应用验收尚未完成,不据此声称实际来源中的 F9 已可导入。

## 快捷键来源插件与受限快照

2026-11-03 起,前述主程序内维护 IDEA 与 VS Code 快捷键解析的做法由此节替代:官方 `editor-keymap-import` 插件拥有来源格式、命令映射与参考默认键;macOS 只在用户选中来源后按声明做元数据识别,通过插件专属、短期有效的快照提供有限输入,不向插件暴露任意路径。Core 验证插件声明和返回动作、规划默认键让位与冲突;不支持的原生键、重复来源提供者或读取异常不会部分应用。来源数据和摘要在应用前重新核对,取消、替换插件或完成请求后撤销快照;仅服务导入的后台进程在最后一次请求结束时停止。默认发现不启动插件。

插件构建产物和 Cargo Git 检出归本工作树 `.artifacts/`,随应用打包的插件可执行文件和许可在构建期固定并签名;运行时只读取插件包,源配置只读,短期快照只在内存及打开的只读描述符中存在,不向发行包写入,也不改变代码签名或 Sparkle 增量更新基线。跨工作树复用规则见 `scripts/resources/worktree-resources.json`。旧的原生快捷键解析器不再参与导入;有条件规则和连续键不会被推广为全局按键,来源移除规则会遮蔽相关参考键。IDEA 已知父方案和 VS Code 的常用动作有插件及真实 Core 会话测试,但插件版打包应用的人工按键验收仍需完成。

VS Code 的 F5 是结合状态的启动或继续调试动作:Core 在未启动、已暂停、运行中和请求未完成等状态之间选择操作,平台执行时再次由实际调试会话保护;F9 断点与 Lithe 未更改的默认调试继续键冲突时,由通用导入规划只让出该默认键。

2026-11-03 插件版 Release 包复验:主机固定官方非语言插件的签名策略,随包副本只在宿主签名封装内可用,外部副本仍需发布者签名。本机 VS Code 与 IDEA 来源均显示并成功导入键位,VS Code 的 F9 断点、F12 定义、Shift-F12 引用、Control 加反引号终端开关和 Control-D shell 输入、F5 启动并在断点暂停后继续,以及 IDEA 的 Command-L 跳行、Command-F8 断点、Command-G 查找下一个均经实际操作;自定义 VS Code 键位与源文件变化后拒绝旧预览、重新扫描及撤销也经实际操作。包文件哈希和签名在验收前后不变。覆盖范围是本机所装版本和隔离测试配置,不代表任意 IDE 扩展、键盘布局或 Windows 产品已经验收。

VS Code 移除规则按上游解析器的两阶段行为处理:只移除指定命令匹配的参考默认键,不清除其他命令的同键绑定或用户新增绑定;再依来源顺序处理单键覆盖及不能转成全局按键的条件/连续键。该修正有先失败后通过的插件回归,插件版打包应用仍需再次复验该边界。

2026-11-03 继续对照固定版本的 JetBrains Community 键位表,补齐 `$default` 和旧 `Mac OS X` 方案继承的文件内查找、替换、定义、实现、引用、命令搜索和断点键。VS Code 带上下文条件的移除规则不能安全推广为无条件移除;除明确为 `true` 的规则外跳过,并在导入提示中说明。两类修复均有先失败再通过的插件回归,覆盖源规则和应用后的完整插件测试。

性能对照使用同一台 macOS、500 KiB 文件和三轮连续输入:稳定 RSS 中位数从 237,072 KiB 到 237,152 KiB;单次采样波动较大,不能据此推断精确内存变化。采样脚本两侧均未采到 `editor.input` 延迟事件,不能据此声称输入延迟改善,也不能代替聚焦终端时 Control 组合键的专项性能测量。性能包、开发版偏好及其数据均使用隔离测试位置。

最终重打包复验:隔离开发版从本机 VS Code 和 IDEA 2025.3 实际导入快捷键,分别验证 VS Code `Ctrl+G`、IDEA `Command+L` 与 `Command+F8`;隔离 VS Code 配置验证用户自定义跳行、重启持久化、撤销、`F9` 断点、`F12` 跳转定义,以及不相关命令和条件移除规则不会误删 `Command+S`。隔离旧版 IDEA 方案预览确认上述常用键,实际验证 `Ctrl+F` 查找和 `Ctrl+F8` 断点。终端先执行测试命令确认焦点,再验证 Control 加反引号关闭和 `Ctrl+D` 结束 shell。修改来源后应用旧预览被拒绝,日志实际记录 `editor.settings_import.failed`、`stage=apply` 和 `changed`,未记录来源内容;本次核对的是日志文件,未完成日志窗口中的逐项查看。验收前后签名有效,1,433 个普通包文件的 SHA-256 清单逐项一致;应用、插件及语言服务退出,隔离偏好已恢复。前文要求再次复验 VS Code 移除边界的状态由本段更新。
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.build/
rust/target/
rust/*/target/
Plugins/mac/Official/EditorKeymapImport/target/
/.worktrees/
.swiftpm/
.idea/
Expand Down
Loading
Loading