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
Expand Up @@ -4,7 +4,7 @@

## 先说结论

快捷键只有一个集中目录,菜单、搜索、设置和实际分发都从这个目录生成。持久化使用稳定的命令 ID,显示文字或分组变化不能随意改变 ID;普通输入按键也不能被误识别成全局命令。
快捷键只有一个集中命令目录,菜单、搜索、设置和实际分发都从这个目录生成。用户可以在 macOS、IntelliJ IDEA Classic 和 Eclipse 三套预设间切换;每套预设的自定义修改分别保存。持久化使用稳定的命令 ID,显示文字或分组变化不能随意改变 ID;普通输入按键也不能被误识别成全局命令。

## 问题

Expand All @@ -27,7 +27,8 @@ ID、本地化标题、分组和默认绑定;菜单、Search Everywhere、设
`⌘`、`⌥` 等字符串只在渲染时生成。普通字符必须带有
Command、Control 或 Option 之一,功能键、方向键等特殊按键可以单独使用。

`AppSettings` 在版本化的 `keyboardShortcutOverrides` 载荷中保存用户覆盖:
`AppSettings` 保存当前预设和按预设分开的版本化用户覆盖。旧版单套
`keyboardShortcutOverrides` 载荷只在首次迁移时归入 macOS 预设:

- 没有命令条目时使用目录默认值;
- 非空列表替换该命令的全部绑定;
Expand All @@ -36,15 +37,29 @@ Command、Control 或 Option 之一,功能键、方向键等特殊按键可以
- 未知命令、非法绑定和重复绑定被忽略;
- 整个载荷无法解码时回退到全部默认值。

预设只映射 Lithe 已有且与 IntelliJ Community 源码能对应的操作,真正没有
对应上游动作的 Lithe 命令才沿用 Lithe 默认绑定。Eclipse 预设先解析
`$default`、`Mac OS X 10.5+`、`Eclipse (Mac OS X)` 的继承顺序;子层
没有声明动作时继承父层,声明空动作时清除父层绑定。例如 Eclipse 的
`Find` 为空,不能回退成 Lithe 的 `⌘F`。切换预设时先切换预设身份,再发布该预设的覆盖值,
使设置页、菜单和原生监听器同时看到新绑定;录制中的操作在切换前取消。
IDEA 的 `Stop` 是按上下文停止运行或调试的单一动作;Lithe 目前仍有独立的
`stop-run` 和 `stop-debug`,因此 `Control-F2` 暂时只绑定 `stop-run`。
要同时覆盖调试场景,需要先提供能识别当前会话的统一 Stop 命令。
应用级恢复默认清除所有预设的用户覆盖并返回 macOS,Keymap 页的恢复默认
只清除当前预设的覆盖。官方映射依据是 IntelliJ Community 的
[`$default.xml`](https://github.com/JetBrains/intellij-community/blob/master/platform/platform-resources/src/keymaps/%24default.xml)
和 [`Eclipse (Mac OS X).xml`](https://github.com/JetBrains/intellij-community/blob/master/plugins/keymaps/eclipse-keymap/resources/keymaps/Eclipse%20%28Mac%20OS%20X%29.xml);
Eclipse 文件继承的 macOS 映射见 [`Mac OS X 10.5+.xml`](https://github.com/JetBrains/intellij-community/blob/master/platform/platform-resources/src/keymaps/Mac%20OS%20X%2010.5%2B.xml)。

`KeyboardShortcutFeatureModel` 负责合成有效值、录制状态、冲突检测、单项
恢复和全部恢复。相同绑定发生冲突时指出占用它的命令并拒绝保存,不自动
覆盖已有命令。`ShortcutSessionCoordinator` 将有效注册同步给 macOS
监听器;录制快捷键时暂停命令分发,非活动会话和已关闭会话不执行排队的
旧命令。

Search Everywhere 保留双击 Shift 和 `⇧⌘O` 两个默认入口。应用级
“恢复全部默认”会同时清除快捷键覆盖,Keymap 页面自己的“全部恢复默认”
只清除快捷键覆盖。该功能只属于 macOS,不把 Windows keymap 或新的跨平台
Search Everywhere 的 macOS 预设保留双击 Shift 和 `⇧⌘O` 两个默认入口。
该功能只属于 macOS,不把 Windows keymap 或新的跨平台
快捷键契约纳入本决策。

## 考虑过的备选方案
Expand All @@ -59,8 +74,9 @@ Search Everywhere 保留双击 Shift 和 `⇧⌘O` 两个默认入口。应用
明确报告冲突并拒绝保存。
- **立即设计 Windows 与 macOS 共用 keymap 契约**:两端事件模型和原生
编辑行为不同,当前收益不足以抵消跨平台兼容面,暂不扩展范围。
- **加入 IDEA keymap 导入、预设、导出或云同步**:这些能力会扩大稳定性
和数据兼容范围,当前需求不包含它们。
- **直接导入 IDEA 全量 keymap 文件**:Lithe 没有 IDEA 的全部命令和动作
语义,全量导入会产生无法执行的配置;只映射现有对应操作。自定义 keymap
文件导入、导出和云同步仍不在本次范围。

## 后果

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ jobs:
needs: changes
if: needs.changes.outputs.rust_core == 'true'
runs-on: macos-26
timeout-minutes: 20
timeout-minutes: 30

steps:
- name: Check out source
Expand Down
2 changes: 1 addition & 1 deletion docs/development/platform-parity-matrix.csv
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ references-pane,编辑器,代码结构,引用结果面板与引用跳转,已实
diagnostics-workspace,可观测性,诊断与日志,工作区诊断与问题面板,已实现,待验证,已实现,待验证,Diagnostics,制造多个来源的错误和警告,验证筛选、排序、定位和清除。,,macos/Sources/Lithe/Application/Features/DiagnosticsFeatureModel.swift; macos/Sources/Lithe/Views/Language/JavaProblemsView.swift,windows/tauri/src/features/diagnostics; windows/tauri/src/features/logging; windows/tauri/src/ui/dialog.tsx
diagnostics-export,可观测性,诊断与日志,诊断包导出与脱敏,已实现,待验证,已实现,待验证,Diagnostics,导出诊断包,确认敏感路径、凭据和大文件被正确脱敏或限制;检查预览说明和文件列表在确认弹窗内完整显示、长内容正常换行且不溢出。,,macos/Sources/Lithe/Views/Diagnostics/DiagnosticsExportSheet.swift; macos/Sources/Lithe/Services/Diagnostics,windows/tauri/src/features/diagnostics
application-settings,设置,应用配置,应用、项目与运行设置,已实现,待验证,已实现,待验证,Settings,修改应用级和项目级设置,验证持久化、迁移、重启恢复和无效值提示;macOS 保持设置窗口打开切换亮色、暗色和跟随系统模式,并改变系统外观,确认标题栏与面板背景同色、窗口按钮可用且可拖动;设置窗口应使用普通窗口层级,切换到其他应用时不置顶遮挡,关闭后重开再检查。,,macos/Sources/Lithe/LitheApp.swift; macos/Sources/Lithe/Views/App/SettingsView.swift; macos/Sources/Lithe/Views/App/ProjectRuntimeSettingsView.swift; macos/Tests/LitheTests/SettingsAppearanceContainerTests.swift,windows/tauri/src/features/settings
keymap-customization,设置,应用配置,快捷键查看与自定义,已实现,待验证,已实现,待验证,Settings,从设置 → 快捷键打开编辑器,按命令名称、ID 和按键搜索,修改跳转到实现的快捷键、制造冲突并恢复默认;导出再导入 keybindings.json,确认预设与自定义绑定恢复且命令实际执行。,Windows 已接入现有快捷键编辑器,修复 #718 的占位入口;Linux 前端回归验证不替代 Windows WebView2、原生文件对话框和快捷键实际执行验收。,macos/Sources/Lithe/Views/App/KeyboardShortcutSettingsView.swift; macos/Sources/Lithe/Models/Keymap,windows/tauri/src/features/keymaps; windows/tauri/src/features/settings/components/macos-settings-panels.tsx; windows/tauri/src/features/settings/components/tabs/keyboard-settings.tsx; windows/tauri/src/features/settings/components/keyboard-settings-routing.test.tsx
keymap-customization,设置,应用配置,快捷键查看与自定义,已实现,待验证,已实现,待验证,Settings,macOS 从设置 → 快捷键依次选择 macOS、IntelliJ IDEA Classic、Eclipse,核对运行、调试、查找等对应操作在界面和实际监听器中的绑定;IDEA Classic 的 Stop Run 应为 Control-F2,Eclipse 的 Resume 应为 F8、继承父 keymap 的命令应使用上游绑定,Find、Replace、GotoImplementation 等显式清空的命令应无绑定;分别修改两套预设的同一命令,切换与重启后确认自定义绑定互不覆盖,并验证旧版单套覆盖迁移到 macOS;按命令名称、ID 和按键搜索,制造冲突并恢复当前预设或应用全部默认。Windows 保持现有快捷键编辑、导出和导入验证。,Windows 已接入现有快捷键编辑器,修复 #718 的占位入口;Linux 前端回归验证不替代 Windows WebView2、原生文件对话框和快捷键实际执行验收。,macos/Sources/Lithe/Views/App/KeyboardShortcutSettingsView.swift; macos/Sources/Lithe/Models/Keymap,windows/tauri/src/features/keymaps; windows/tauri/src/features/settings/components/macos-settings-panels.tsx; windows/tauri/src/features/settings/components/tabs/keyboard-settings.tsx; windows/tauri/src/features/settings/components/keyboard-settings-routing.test.tsx
plugin-extension-management,扩展,插件与主题,插件、语言服务与主题管理,已实现,待验证,已实现,待验证,Extensions,启用、禁用和恢复插件/主题,确认启动加载、失败隔离和设置持久化。,,macos/Sources/Lithe/Views/App/PluginManagementView.swift; macos/Sources/Lithe/Platform/MacOS/Plugins,windows/tauri/src/extensions; windows/tauri/src/features/settings
onboarding-welcome,启动与窗口,首次启动,欢迎页、项目入口与首次启动引导,已实现,待验证,已实现,待验证,Onboarding,清空首次启动状态,验证打开项目、克隆项目、最近项目和跳过引导。,,macos/Sources/Lithe/Views/App/WelcomeView.swift; macos/Sources/Lithe/Views/Workspace,windows/tauri/src/features/onboarding; windows/tauri/src/features/bootstrap
command-palette,工作台,命令与布局,命令面板与全局动作,已实现,待验证,已实现,待验证,Workbench,搜索并执行打开面板、运行、Git 和设置命令,确认快捷键和不可用命令状态。,,macos/Sources/Lithe/Models/Keymap/LitheCommandCatalog.swift; macos/Sources/Lithe/Views/Search/SearchEverywhereView.swift,windows/tauri/src/features/command-palette
Expand Down
2 changes: 1 addition & 1 deletion docs/development/platform-parity-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
| 功能组 | 能力点 | macOS | Windows | 负责人 | 验证方式 | 备注 |
| --- | --- | --- | --- | --- | --- | --- |
| 应用配置 | **应用、项目与运行设置**<br><sub>application-settings</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/LitheApp.swift`、`macos/Sources/Lithe/Views/App/SettingsView.swift`、`macos/Sources/Lithe/Views/App/ProjectRuntimeSettingsView.swift`、`macos/Tests/LitheTests/SettingsAppearanceContainerTests.swift`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/settings`</sub> | Settings | 修改应用级和项目级设置,验证持久化、迁移、重启恢复和无效值提示;macOS 保持设置窗口打开切换亮色、暗色和跟随系统模式,并改变系统外观,确认标题栏与面板背景同色、窗口按钮可用且可拖动;设置窗口应使用普通窗口层级,切换到其他应用时不置顶遮挡,关闭后重开再检查。 | |
| 应用配置 | **快捷键查看与自定义**<br><sub>keymap-customization</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Views/App/KeyboardShortcutSettingsView.swift`、`macos/Sources/Lithe/Models/Keymap`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/keymaps`、`windows/tauri/src/features/settings/components/macos-settings-panels.tsx`、`windows/tauri/src/features/settings/components/tabs/keyboard-settings.tsx`、`windows/tauri/src/features/settings/components/keyboard-settings-routing.test.tsx`</sub> | Settings | 从设置 → 快捷键打开编辑器,按命令名称、ID 和按键搜索,修改跳转到实现的快捷键、制造冲突并恢复默认;导出再导入 keybindings.json,确认预设与自定义绑定恢复且命令实际执行。 | Windows 已接入现有快捷键编辑器,修复 #718 的占位入口;Linux 前端回归验证不替代 Windows WebView2、原生文件对话框和快捷键实际执行验收。 |
| 应用配置 | **快捷键查看与自定义**<br><sub>keymap-customization</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Views/App/KeyboardShortcutSettingsView.swift`、`macos/Sources/Lithe/Models/Keymap`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/keymaps`、`windows/tauri/src/features/settings/components/macos-settings-panels.tsx`、`windows/tauri/src/features/settings/components/tabs/keyboard-settings.tsx`、`windows/tauri/src/features/settings/components/keyboard-settings-routing.test.tsx`</sub> | Settings | macOS 从设置 → 快捷键依次选择 macOS、IntelliJ IDEA Classic、Eclipse,核对运行、调试、查找等对应操作在界面和实际监听器中的绑定;IDEA Classic 的 Stop Run 应为 Control-F2,Eclipse 的 Resume 应为 F8、继承父 keymap 的命令应使用上游绑定,Find、Replace、GotoImplementation 等显式清空的命令应无绑定;分别修改两套预设的同一命令,切换与重启后确认自定义绑定互不覆盖,并验证旧版单套覆盖迁移到 macOS;按命令名称、ID 和按键搜索,制造冲突并恢复当前预设或应用全部默认。Windows 保持现有快捷键编辑、导出和导入验证。 | Windows 已接入现有快捷键编辑器,修复 #718 的占位入口;Linux 前端回归验证不替代 Windows WebView2、原生文件对话框和快捷键实际执行验收。 |

</details>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ final class KeyboardShortcutFeatureModel: ObservableObject {
LitheCommandCatalog.commands
}

var selectedPreset: KeyboardShortcutPreset {
settings.keyboardShortcutPreset
}

func selectPreset(_ preset: KeyboardShortcutPreset) {
settings.selectKeyboardShortcutPreset(preset)
}

var registrations: [KeyboardShortcutRegistration] {
registrations(for: settings.keyboardShortcutOverrides)
}
Expand All @@ -45,7 +53,7 @@ final class KeyboardShortcutFeatureModel: ObservableObject {
commands.map { command in
KeyboardShortcutRegistration(
commandID: command.id,
bindings: overrides[command.id] ?? command.defaultBindings
bindings: overrides[command.id] ?? settings.keyboardShortcutPreset.bindings(for: command)
)
}
}
Expand Down Expand Up @@ -85,7 +93,8 @@ final class KeyboardShortcutFeatureModel: ObservableObject {
if let override = settings.keyboardShortcutOverrides[commandID] {
return override
}
return LitheCommandCatalog.command(id: commandID)?.defaultBindings ?? []
guard let command = LitheCommandCatalog.command(id: commandID) else { return [] }
return settings.keyboardShortcutPreset.bindings(for: command)
}

func displayText(for commandID: String) -> String? {
Expand Down
Loading
Loading