Skip to content

Conversation

@beyondkmp
Copy link
Collaborator

@beyondkmp beyondkmp commented Oct 30, 2025

Cherry Studio 快捷键系统重构设计文档 v2.0

最近更新:2025-01-30
维护者:Architecture Team

目录


背景与目标

旧版快捷键系统存在以下问题:

  1. 依赖已弃用的 configManager,与 v2 架构不兼容;
  2. Redux store 与本地存储重复维护状态;
  3. 处理器通过 switch-case 硬编码,可维护性差;
  4. 快捷键定义分散,缺乏统一真相源;
  5. 新增快捷键需要触达多处文件,易错且低效。

新版系统要实现:

  • 单一真相源:快捷键定义集中管理,保证一致性;
  • 偏好服务优先:所有运行时状态通过 preferenceService 管理;
  • 处理器注册表:解除 switch-case 依赖,改用 Map 注册;
  • 类型安全:从定义、存储到消费全链路具备 TypeScript 约束;
  • 易扩展:新增快捷键仅需「定义 → 注册处理器 → 使用」三步;
  • 性能稳定:支持 100+ 快捷键规模,主/渲染进程高效同步;
  • 多窗口同步:借助 preferenceService 自动推送变更。

核心原则

  1. 关注点分离

    • 定义层:静态元数据(名称、默认绑定、作用域、分类等);
    • 偏好层:用户可变配置(绑定、启用状态等);
    • 服务层:主进程注册、电焦/失焦时的生命周期管理;
    • UI 层:设置面板、快捷键提示等。
  2. 复用基础设施

    • 所有持久化均依赖 preferenceService(SQLite + 内存缓存 + IPC);
    • 变更通过订阅自动广播至所有窗口;
    • 新增键位无需改动主进程/渲染进程的底层框架代码。

架构分层

┌──────────────────────────────────────────────┐
│                   Shortcut 系统               │
├──────────────────────────────────────────────┤
│ 📋 Definitions (packages/shared/shortcuts)   │
│   - types.ts:类型、作用域、分类              │
│   - definitions.ts:静态定义(真相之源)      │
│   - utils.ts:转换/校验工具                   │
│                                              │
│ 💾 Preferences (preferenceService)            │
│   - preferenceSchemas.ts 默认值               │
│   - preferenceTypes.ts 类型导出               │
│                                              │
│ ⚙️ Services                                   │
│   - src/main/services/ShortcutService.ts      │
│     · 处理器注册表、focus/blur 生命周期       │
│     · preference 订阅、主进程快捷键注册       │
│   - 渲染进程 useShortcut/useShortcutDisplay   │
│                                              │
│ 🎨 UI                                         │
│   - 设置页 ShortcutSettings                   │
│   - 各功能模块中的 useShortcut/useShortcutDisplay │
└──────────────────────────────────────────────┘

关键实现

1. 静态定义

  • 所有快捷键在 packages/shared/shortcuts/definitions.ts 中集中维护;
  • 包含 scope(main / renderer / both)、categorypersistOnBlur 等元信息;
  • enabledWhen 支持动态启用(如 mini window 与 quick assistant 开关关联);
  • 新增快捷键步骤:
    1. preferenceSchemas.ts 中声明默认值;
    2. definitions.ts 中补充静态定义;
    3. 在主/渲染进程相关模块注册处理器或消费 Hook。

2. 偏好系统

  • 所有运行时配置通过 preferenceService 读写;
  • 默认值与 PreferenceShortcutType 结构保持一致;
  • ShortcutService / useShortcuts 访问偏好时统一调用 coerceShortcutPreference,确保 fallback 与类型安全;
  • 批量重置通过 preferenceService.setMultiple 实现。

3. 主进程服务

  • ShortcutService 负责:
    • 生命周期:随着窗口 focus/blur 注册或卸载快捷键;
    • 处理器注册:Map 替换 switch-case
    • 订阅偏好变更:自动重新注册;
    • persistOnBlur:例如 show_main_window 在窗口失焦时仍可触发;
    • shortcut.app.show_settings 会在需要时唤起窗口并调用 window.navigate('/settings/provider'),避免重复 blur/focus。

4. 渲染进程 Hook

  • useShortcut:从偏好获取绑定 → 转为 react-hotkeys-hook 字符串 → 注册快捷键;
  • useShortcutDisplay:转换为 UI 显示字符串( / Ctrl+ 等);
  • useAllShortcuts:批量拉取配置 + diff 默认值,供设置面板使用;
  • 新增 enableOnContentEditable 等配置支撑设置页和富文本场景。

5. 设置界面

  • ShortcutSettings 直接消费 useAllShortcuts
  • 支持录制、清空、重置默认、启用/禁用、冲突检测;
  • 重新绑定时使用 convertKeyToAccelerator / isValidShortcut / formatShortcutDisplay
  • “重置全部” 通过 preferenceService.setMultiple 一次性写入默认配置;
  • 新增表格展示 hasCustomBinding,区分用户自定义与继承默认值。

数据流

启动阶段

  1. preferenceService.initialize() 载入缓存;
  2. shortcutService 构造时注册处理器与订阅;
  3. 窗口创建后调用 shortcutService.registerForWindow,在 focus 时注册主进程快捷键。

运行时变更

  1. 设置页或其他模块调用 preferenceService.set / setMultiple
  2. 主进程订阅触发 → globalShortcut.unregisterAll() → 按新配置重注册;
  3. 渲染进程通过 usePreference/useMultiplePreferences 自动收到更新,UI 即时刷新。

默认快捷键

preference key 默认绑定 描述 / 备注
shortcut.app.show_main_window Cmd/Ctrl + Shift + A 主窗口显示(失焦持久)
shortcut.app.show_mini_window Cmd/Ctrl + E Mini 窗口(与 quick assistant 联动)
shortcut.app.show_settings Cmd/Ctrl + , 设置页入口
shortcut.app.toggle_show_assistants Cmd/Ctrl + [ 助手侧边栏
shortcut.app.exit_fullscreen Escape 系统级,不可编辑
shortcut.app.zoom_in/out/reset Cmd/Ctrl + = / - / 0 包含数字键盘变体
shortcut.app.search_message Cmd/Ctrl + Shift + F 全局搜索
shortcut.chat.clear Cmd/Ctrl + L 清空消息
shortcut.chat.search_message Cmd/Ctrl + F 聊天内搜索
shortcut.chat.toggle_new_context Cmd/Ctrl + K 新上下文
shortcut.chat.copy_last_message Cmd/Ctrl + Shift + C 复制最后一条
shortcut.chat.edit_last_user_message Cmd/Ctrl + Shift + E 编辑最后一条用户消息
shortcut.topic.new Cmd/Ctrl + N 新增话题(默认启用)
shortcut.topic.rename Cmd/Ctrl + T 重命名话题(默认启用,自 2025-01 调整)
shortcut.topic.toggle_show_topics Cmd/Ctrl + ] 话题侧边栏
shortcut.selection.* 无默认绑定 划词助手开关、取词

具体配置以 preferenceSchemas.ts 为准,可在设置页查看或调整。


迁移与兼容性

  • 已有用户偏好:沿用旧值;新增键(如 shortcut.topic.rename)在数据库不存在时继承新默认;
  • 旧版 Redux store / configManager 已彻底移除;
  • IpcChannel.Shortcuts_Updatewindow.api.shortcuts.update 相关逻辑已弃用;
  • PreferenceMigrator 中保留与旧 keys 的映射,确保升级顺畅。

后续演进方向

  1. 冲突检测增强:主/渲染进程联动校验冲突并提示;
  2. 导入导出:允许用户批量备份/恢复自定义快捷键;
  3. 多作用域绑定:同一逻辑支持按窗口类型或上下文切换;
  4. 可视化录制:增加「录制模式」避免输入框手动录制;
  5. 自动化测试:补充主进程/渲染进程快捷键单元测试样板。

如需扩展或有疑问,请联系架构团队或在仓库中提交 Issue。
设计文档 v2.1 同步最新实现(2025-01),包含 shortcut.topic.rename 默认启用、show_settings 优化等补充说明。

@beyondkmp beyondkmp requested a review from 0xfullex as a code owner October 30, 2025 03:30
@beyondkmp beyondkmp marked this pull request as draft October 30, 2025 03:31
@DeJeune DeJeune added the v2 label Oct 30, 2025
@DeJeune DeJeune added this to the v2.0.0 milestone Oct 30, 2025
Copy link
Collaborator

@0xfullex 0xfullex Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This issue/comment/review was translated by Claude.

In the preference storage, it is recommended to only save { enabled: boolean; key: string[] }, while all other non-modifiable properties should be defined in the template definition.


Original Content

preference的存储中,建议只保存 { enabled: bolean; key: string[] },其他不可修改的,全部在template def中定义即可

})

useShortcut('search_message', () => {
useShortcut('shortcut.app.search_message', () => {
Copy link
Collaborator

@0xfullex 0xfullex Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This issue/comment/review was translated by Claude.

You actually don't need to add the "shortcut." prefix anymore, you can simplify it a bit. BTW, TypeScript supports type inference with automatic prefix addition, so it won't affect TypeScript.


Original Content 这里其实不需要再加 shortcut. 前缀了,可以简化一些。btw,ts支持自动添加前缀后的类型推断,所以不会影响ts
Copy link
Collaborator

@0xfullex 0xfullex Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This issue/comment/review was translated by Claude.

Some shortcuts may not be supported on all operating systems. For example, the text selection assistant is currently not supported on Linux. Therefore, it would be better to have system-specific settings.


Original Content

有些快捷键可能不是所有操作系统都支持,例如划词助手,现在linux是不支持的。所以如果有按系统的设定,会比较好。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4 participants