一套可以直接交给开发人员实现的「快捷键配置产品设计方案」,类似 VS Code 截图里的 Keyboard Shortcuts 页面,包含数据模型、交互、冲突处理、上下文条件、用户配置和实现边界。
下面我按产品规格的方式来设计。
快捷键配置功能产品方案
1. 产品目标
提供一套统一的快捷键管理系统,让用户可以:
- 查看系统内所有命令及其快捷键
- 搜索命令、快捷键和上下文条件
- 修改已有快捷键
- 为命令增加多个快捷键
- 删除/恢复快捷键
- 查看快捷键来源
- 检测快捷键冲突
- 基于上下文
when条件复用同一个快捷键 - 支持系统默认、插件默认、用户自定义三级覆盖
- 支持 JSON / UI 两种配置方式
- 支持 Windows / macOS / Linux 不同按键映射
核心设计原则:
快捷键不是“按键 → 功能”的简单映射,而应该是 按键 + 上下文条件 → 命令。
2. 页面结构
整体参考 VS Code。
建议页面由 4 个区域组成:
┌────────────────────────────────────────────────────┐
│ 搜索框 操作菜单 │
├──────────────┬─────────────┬────────────┬──────────┤
│ Command │ Keybinding │ When │ Source │
├──────────────┼─────────────┼────────────┼──────────┤
│ 保存 │ Ctrl + S │ editorFocus│ System │
│ 打开搜索 │ Ctrl + F │ editorFocus│ System │
│ 打开搜索结果 │ Enter │ searchFocus│ System │
│ ... │ │ │ │
└──────────────┴─────────────┴────────────┴──────────┘
四列建议固定为:
| 字段 | 用途 |
|---|---|
| Command | 命令显示名称 |
| Keybinding | 快捷键 |
| When | 生效条件 |
| Source | 来源 |
建议允许额外开启:
- Command ID
- Category
- Conflict
- Platform
但默认隐藏,避免页面过重。
3. Command 数据模型
每个可执行功能都必须注册成 Command。
建议定义:
interface Command {
id: string
title: string
category?: string
description?: string
icon?: string
enablement?: string
source: 'system' | 'extension'
}
例如:
{
id: 'editor.action.save',
title: 'Save',
category: 'File',
source: 'system'
}
Markdown 产品可以是:
{
id: 'markdown.openPreview',
title: 'Open Preview',
category: 'Markdown',
source: 'system'
}
Command ID 设计要求
必须:
- 全局唯一
- 稳定
- 不随 UI 文案改变
推荐格式:
domain.action
domain.object.action
例如:
file.save
file.open
editor.copy
editor.insertLineAfter
search.open
search.openResult
outline.reveal
outline.refresh
markdown.openPreview
markdown.insertTable
不要使用:
saveButtonClicked
openSearchPanel2
这种和 UI 实现耦合的命名。
4. Keybinding 数据模型
这是整个系统的核心。
建议:
interface Keybinding {
id: string
command: string
key: string
when?: string
args?: unknown
source:
| 'system'
| 'extension'
| 'user'
platform?: {
windows?: string
mac?: string
linux?: string
}
}
例如:
{
id: 'system.save',
command: 'file.save',
key: 'ctrl+s',
when: 'editorFocus',
source: 'system'
}
5. 支持 Chord 快捷键
必须支持 VS Code 式的二段快捷键:
Ctrl+K Ctrl+S
内部不要把它当作一个普通按键字符串处理。
建议模型:
type KeyStroke = {
ctrl?: boolean
shift?: boolean
alt?: boolean
meta?: boolean
key: string
}
interface KeyboardShortcut {
strokes: KeyStroke[]
}
例如:
{
strokes: [
{
ctrl: true,
key: 'k'
},
{
ctrl: true,
key: 's'
}
]
}
第一阶段建议最多支持:
2 strokes
也就是:
Ctrl+K Ctrl+S
不要一开始支持无限 chord。
6. When Context 系统
这个模块建议直接借鉴 VS Code 思路。
一个快捷键是否触发,取决于:
Key Match
+
When Expression Match
例如:
{
key: 'enter',
command: 'search.openResult',
when: 'searchResultFocus'
}
同时:
{
key: 'enter',
command: 'dialog.confirm',
when: 'dialogFocus'
}
完全可以共存。
7. Context Key 设计
应用内部维护一个 Context Store。
例如:
{
editorFocus: true,
editorHasSelection: false,
searchVisible: false,
searchInputFocus: false,
outlineVisible: true,
outlineFocus: false,
modalOpen: false,
platform: 'windows'
}
每一个状态称为:
Context Key
建议常用 Context Keys:
Editor
editorFocus
editorTextFocus
editorHasSelection
editorReadonly
editorLanguage
editorDirty
Search
searchVisible
searchFocus
searchInputFocus
searchResultFocus
searchHasResults
Outline
outlineVisible
outlineFocus
outlineItemFocus
UI
sidebarVisible
sidebarFocus
panelVisible
panelFocus
dialogOpen
modalOpen
inputFocus
Document
documentOpen
documentDirty
markdownDocument
textDocument
8. When 表达式语法
建议第一版支持:
&&
||
!
==
!=
例如:
editorFocus && !editorReadonly
或者:
searchVisible && searchResultFocus
或者:
editorLanguage == markdown
暂时不建议第一版支持:
regex
in
not in
复杂函数调用
否则解析器和调试复杂度会明显提高。
9. 快捷键触发算法
建议实现流程:
keydown
↓
Normalize KeyboardEvent
↓
匹配 key
↓
查找所有候选 Keybinding
↓
检查 when
↓
按照优先级排序
↓
执行最高优先级 command
候选项:
const candidates =
keybindings.filter(binding =>
matchesKey(binding, event)
)
然后:
const valid = candidates.filter(binding =>
evaluateWhen(binding.when, context)
)
最后选择:
User
>
Extension
>
System
10. 优先级规则
建议明确规定:
User Keybinding
↓
Extension Keybinding
↓
System Keybinding
用户配置永远最高。
如果同一来源内部有重复:
更具体的 when
>
更宽泛的 when
例如:
Ctrl+Enter
when: searchResultFocus
优先于:
Ctrl+Enter
when: editorFocus
如果仍然无法区分:
最后注册者优先
但这种情况 UI 中应该提示冲突。
11. Source 设计
截图中的 Source 很重要。
建议来源分为:
System
User
Extension
Extension 再显示:
Git
Markdown
Your Extension Name
例如:
System
Markdown Tools
GitLens
User
UI 可以表现:
Markdown Preview Ctrl+K V markdownEditor System
Git: Commit Ctrl+Enter gitCommitFocus Git
12. 搜索设计
顶部搜索框不能只搜索 Command。
必须同时支持:
命令名称
Command ID
快捷键
When
Source
例如搜索:
ctrl+k
返回所有以:
Ctrl+K
开头的快捷键。
搜索:
@source:user
只查看用户快捷键。
13. 建议支持 Search Filter Syntax
可以参考 VS Code,但第一版不用过多。
建议支持:
@keybinding:
@command:
@when:
@source:
例如:
@keybinding:ctrl+k
@source:user
@command:search
也可以支持:
@conflict
快速查看冲突快捷键。
14. 编辑快捷键交互
用户点击快捷键单元格:
Ctrl + S
进入录制模式:
┌─────────────────────┐
│ Press desired keys │
│ │
│ Ctrl + Shift + S │
└─────────────────────┘
实现上:
keydown => preventDefault()
然后记录。
Esc
取消录制
Enter
确认快捷键
但注意:
如果正在录制:
Enter
本身也可能是用户想绑定的键。
因此建议:
快捷键捕获完成后自动确认
而不是:
Enter = 保存
否则用户无法配置:
Enter
15. 行操作
鼠标悬停到某一行:
✏
点击后菜单:
Change Keybinding
Add Keybinding
Remove Keybinding
Change When Expression
Reset Keybinding
Copy Command ID
Copy as JSON
建议对应中文:
更改快捷键
添加快捷键
删除快捷键
修改生效条件
恢复默认
复制命令 ID
复制为 JSON
16. 一个 Command 支持多个 Keybinding
例如:
Copy
Ctrl+C
Ctrl+Insert
不要把它建模为:
command.keys = []
更推荐:
一条 Binding 一行
例如:
Copy Ctrl+C
Copy Ctrl+Insert
原因是每一个 Binding 可以拥有自己的:
when
source
platform
例如:
Copy Ctrl+C editorFocus
Copy Ctrl+C treeFocus
这是两条不同规则。
17. 冲突检测
冲突不能简单定义成:
key 相同 = conflict
例如:
Enter → search.openResult
when searchResultFocus
以及:
Enter → dialog.confirm
when dialogOpen
通常不冲突。
真正冲突是:
相同快捷键
+
when 条件可能同时成立
第一版不需要构建完整逻辑 SAT 求解器。
可以使用一个保守算法:
明确无冲突
例如:
editorFocus
vs
searchFocus
如果它们被定义为 mutually exclusive context。
可能冲突
否则:
Potential Conflict
UI 显示:
⚠
而不是阻止用户保存。
18. 禁止硬性阻止快捷键冲突
这是一个比较重要的产品决策。
不要:
此快捷键已经使用,无法保存
应该:
Ctrl+Enter is already used by:
Search: Open Result
[Replace]
[Keep Both]
[Cancel]
因为:
不同 when 条件
本来就允许相同快捷键。
19. 删除快捷键的模型
不要修改 System 默认数据。
例如默认存在:
{
command: 'file.save',
key: 'ctrl+s',
source: 'system'
}
用户删除它时:
不要 delete system row。
应该新增一条:
{
command: '-file.save',
key: 'ctrl+s',
source: 'user'
}
或者内部:
{
type: 'remove',
command: 'file.save',
key: 'ctrl+s'
}
这样:
Reset
时只需要删除用户 override。
系统默认立刻恢复。
20. 用户配置格式
建议暴露一个:
keybindings.json
例如:
[
{
"key": "ctrl+k ctrl+o",
"command": "outline.focus"
},
{
"key": "ctrl+enter",
"command": "search.openResult",
"when": "searchResultFocus"
}
]
删除默认快捷键:
{
"key": "ctrl+s",
"command": "-file.save"
}
这个设计和 VS Code 用户认知非常接近。
21. UI 和 JSON 必须双向同步
用户在 UI 中修改:
Ctrl+K O
立即写入:
keybindings.json
用户直接编辑:
keybindings.json
UI 也实时刷新。
不要维护两套数据。
应该:
UI
↓
Keybinding Service
↓
User Keybinding Config
JSON Editor
↓
Keybinding Service
↓
UI
22. 跨平台键位
内部建议统一保存:
ctrl
shift
alt
meta
显示层:
Windows:
Ctrl+Shift+P
macOS:
⇧⌘P
或者:
Cmd+Shift+P
不要在底层保存:
Command
Option
应该 Normalize 为:
meta
alt
23. 推荐支持 Platform Override
例如:
{
"command": "file.save",
"key": "ctrl+s",
"mac": "cmd+s"
}
或者程序内部:
{
command: 'file.save',
win: 'ctrl+s',
linux: 'ctrl+s',
mac: 'meta+s'
}
24. KeyboardEvent Normalization
这是开发时最容易出问题的地方之一。
不要直接依赖:
event.key
建议产生内部标准:
{
ctrl: true,
shift: false,
alt: false,
meta: false,
code: 'KeyK'
}
推荐优先:
KeyboardEvent.code
表达物理键。
同时根据产品需要决定:
layout dependent
还是:
layout independent
这一点建议早期明确,否则中文键盘、德语键盘、法语键盘后面会很麻烦。
25. Chord 状态机
例如:
Ctrl+K
可能既是:
一个完整快捷键
也可能是:
Ctrl+K Ctrl+S
的前缀。
建议规定:
只要某个 shortcut 是 chord prefix,就不要允许它同时作为完整 command。
这样可以避免:
Ctrl+K
到底立即执行还是等待第二键的问题。
状态机:
Idle
↓ Ctrl+K
ChordWaiting
↓ Ctrl+S
Execute
等待时间:
1500–2000ms
超时:
Cancel chord
页面底部可以提示:
Ctrl+K was pressed. Waiting for second key...
26. 和浏览器快捷键冲突
如果你的产品运行在 Web 环境,这部分必须单独设计。
例如浏览器保留:
Ctrl+L
Ctrl+T
Ctrl+N
Ctrl+W
Ctrl+Shift+T
F5
Ctrl+R
其中部分可以:
preventDefault()
部分浏览器不允许覆盖。
因此每个按键建议有:
reserved?: boolean
UI 中提示:
⚠ May be intercepted by browser
不要保证:
一定可以触发
27. 输入框场景必须保护
这是编辑器产品非常重要的一条。
例如:
Delete
Backspace
Ctrl+A
Ctrl+C
Ctrl+V
在:
<input>
<textarea>
contenteditable
中应该优先保持原生行为。
可以定义:
inputFocus
系统默认规则:
global shortcuts
when: !inputFocus
例如:
{
key: 'backspace',
command: 'navigation.goBack',
when: '!inputFocus'
}
而不是全局拦截。
28. 命令执行层必须和快捷键解耦
架构上应该是:
Keyboard
↓
Keybinding Service
↓
Command Service
↓
Command Handler
而不是:
if (ctrl && key === 's') {
saveFile()
}
正确:
commandService.executeCommand('file.save')
快捷键只是其中一个入口。
其他入口:
菜单
右键菜单
命令面板
按钮
快捷键
全部调用同一个:
Command ID
29. 推荐整体架构
┌───────────────────┐
Keyboard Event ──▶│ KeybindingService │
└─────────┬─────────┘
│
▼
┌─────────────────┐
│ ContextKeyService│
└─────────────────┘
│
▼
┌─────────────────┐
│ CommandService │
└────────┬────────┘
│
▼
Command Handler
配置:
System Keybindings ─┐
│
Extension Keybindings ──▶ Keybinding Registry
│
User Keybindings ───┘
30. 推荐模块拆分
开发可以直接按下面拆:
keyboard/
├── command-registry.ts
├── command-service.ts
│
├── keybinding-registry.ts
├── keybinding-service.ts
├── keybinding-parser.ts
├── keybinding-resolver.ts
├── keybinding-normalizer.ts
│
├── context-key-service.ts
├── context-expression-parser.ts
│
├── chord-state-machine.ts
│
├── conflict-detector.ts
│
└── user-keybinding-storage.ts
UI:
keyboard-shortcuts/
├── KeyboardShortcutsPage
├── KeyboardShortcutTable
├── ShortcutRecorder
├── ConflictDialog
├── WhenEditor
└── ShortcutSearch
31. MVP 范围
我建议第一版不要一次做成完整 VS Code。
P0
必须:
- Command Registry
- 系统默认快捷键
- 用户修改快捷键
- 添加/删除快捷键
- 单键组合
- 双段 Chord
when- 搜索
- Source
- Reset
- 冲突提示
- 本地持久化
P1
增加:
- JSON 配置
- Command ID 搜索
- 高级过滤器
- macOS / Windows / Linux 独立配置
- 插件快捷键
- 冲突页面
P2
再考虑:
- Keybinding Profiles
- 云同步
- 导入/导出
- Vim / Emacs preset
- Shortcut Cheatsheet
- 用户统计和推荐
32. 最终用户视角
用户进入:
Settings
→ Keyboard Shortcuts
看到:
Command Keybinding When Source
Save Ctrl+S editorFocus System
Open File Ctrl+O System
Quick Open Ctrl+P System
Command Palette Ctrl+Shift+P System
Outline: Focus Ctrl+K Ctrl+O documentOpen User
Search: Open Result Enter searchResultFocus System
右键某一项:
Change Keybinding
Add Keybinding
Remove Keybinding
Change When Expression
Reset
Copy Command ID
这基本就是一套可以真正开发,而不是只停留在快捷键推荐层面的产品方案。
如果你这个需求是准备实际落地,我建议开发时把核心抽象固定成这四个:
Command
Keybinding
ContextKey
KeybindingResolver
只要这四层设计正确,后面无论加菜单、命令面板、插件、用户自定义、同步,基本都不用推翻快捷键系统。
MVP P0 实施记录
- P0 已实现,入口为「设置 → 快捷键设置」。
- 具体命令、使用方法、条件规则和验证方式见 快捷键 MVP P0 使用与实现说明。
