Uninote
Uninote
用户根目录
brdr
common
programming
docs
后端试题
问题讨论

快捷键设置:MVP P0 / P1

  • 对应 快捷键产品方案 第 31 节的 P0、P1 范围。
  • 入口:设置 → 快捷键设置;旁边的「热键设置」直接打开热键配置。
  • 页面通过「快捷键 / 热键」单选框切换,两类列表分别配置,切换时清空搜索与冲突筛选。
  • 「编辑 JSON」和「默认 JSON」仅显示当前类别;保存 JSON 保留另一类别的配置,两类仍写入本机 keybindings.json。
  • 若 JSON 文件语法损坏,编辑器显示完整原文件并提示修复范围;修复保存会更新全部配置。
  • 热键类别单独显示「启用热键」开关;热键录制一段后立即完成,可继续重新录制,Enter 保存。
  • 默认也可用 Ctrl+K Ctrl+S 打开;macOS 使用 Cmd+K Cmd+S
  • 修改立即生效,不需要点击外层「保存设置」。
  • 外层设置的取消按钮不会撤销已经保存的快捷键。

查看与编辑

  • 页面按绑定逐行显示命令、快捷键、When、Source 和操作。
  • 表头保持单行,不自动换行;列内容仍可换行,宽度不足时表格区域可横向滚动。
  • 同一命令支持多条绑定;全部删除后仍显示「未绑定」,可以再次添加或恢复默认。
  • 搜索支持命令名称、命令 ID、快捷键、生效条件和来源文本。
  • Source 的 system 表示默认规则,user 表示用户规则。
  • 「修改」支持同时修改按键和 When。
  • 「添加」为命令增加独立绑定。
  • 「删除」仅删除当前绑定;默认规则通过用户移除记录屏蔽,不修改默认数据。
  • 「恢复默认」清除该命令的全部用户绑定与移除记录。
  • 快捷键设置、编辑和 JSON 配置对话框保持固定高度(受窗口高度限制),内容过多时在内部滚动;搜索结果数量和错误提示不会撑高对话框。
  • 每次添加或修改快捷键默认进入按键录制;可点击「切换为手动输入」直接编辑文本,也可切回录制,两种方式保留已输入的快捷键。
  • 不显示「重新录制」按钮;超时或收满两段后自动等待下一组按键,保留上一组结果供保存,新一组录制完成后替换它。
  • 录制/手动输入模式同时作用于通用键位和 Windows、macOS、Linux 平台覆盖;录制模式下聚焦哪个键位输入框,按键就写入哪个字段,手动模式下四个字段均可直接编辑。
  • 离开当前键位输入框时保留已捕获的第一段并结束录制;When 始终可手动编辑,再次聚焦任一键位输入框会开始录制。
  • Tab 用于离开录制框,需要绑定 Tab 时可切换为手动输入。
  • 每组录制最多两段;第一段后停顿 1.8 秒完成,第二段按下后立即完成,并自动开始下一次录制。
  • 录制时 Esc 取消;Enter 仅在第二段作为快捷键录入,第一段按 Enter 会保存当前配置并关闭编辑对话框(校验或保存失败时保留对话框和草稿)。
  • 超时或收满两段后已自动进入下一组的第一段,此时按 Enter 同样保存当前结果;也可点击「保存」。
  • 关闭快捷键编辑对话框后恢复上一级页面原先聚焦的元素;保存重建列表后恢复到对应快捷键行的同一操作按钮,若该行不再符合筛选条件则聚焦搜索框。
  • 保存失败会显示错误,并保留原来的有效配置。

默认命令

  • 命令面板可通过 Ctrl/Cmd+Shift+PF1 或工具栏「命令」按钮打开。
  • 通过 Esc 或点击面板外部关闭后,恢复原先 textarea 或 input 的焦点、光标位置和选区方向;鼠标点击「命令」按钮也保留原先的输入焦点。
  • 执行命令前先恢复原先的焦点和选区,让命令使用原来的输入位置;命令自身仍可按其功能转移焦点。
  • 命令面板独立回归验证:npx electron test/electron-smoke.js --command-palette-only,覆盖输入框焦点、光标、选区方向和各关闭路径。

下表的主修饰键在 Windows/Linux 为 Ctrl,在 macOS 为 Cmd。

命令 ID 功能 默认快捷键
file.new 新建文档 主修饰键+N
file.open 打开文件 主修饰键+O
file.quickOpen 快速打开 主修饰键+P
workspace.openRecent 打开最近的笔记库 Ctrl+R(所有平台)
workspace.openInVSCode 用 VS Code 打开当前仓库 Alt+Shift+V
workspace.revealInFileExplorer 在文件管理器中显示 Shift+Alt+R
file.save 保存文档 主修饰键+S
file.saveAs 另存为 未绑定
file.close 关闭文档 主修饰键+W
workspace.copyPath 复制文件或文件夹路径 Shift+Alt+C
workspace.copyRelativePath 复制文件或文件夹相对路径 主修饰键+K 主修饰键+Shift+C
editor.insertImage 插入本地图片 主修饰键+Shift+I
editor.gotoLine 跳转到行 主修饰键+G
editor.selectionToList 切换列表 Alt+Shift+L
sync.open 打开同步 Alt+S
outline.focus 聚焦大纲 主修饰键+Shift+O
search.open 页内查找 主修饰键+F
search.replace 替换当前文件 Ctrl+H;macOS:Cmd+Option+F
search.findInFiles 在文件中查找 主修饰键+Shift+F
search.replaceInFiles 在文件中替换 主修饰键+Shift+R
search.next 下一个匹配 F3
search.previous 上一个匹配 Shift+F3
tabs.mruNext 最近使用标签 Ctrl+Tab
tabs.mruPrevious 反向最近使用标签 Ctrl+Shift+Tab
tabs.previous 上一个标签 Ctrl+PageUp;macOS:Cmd+Shift+[
tabs.next 下一个标签 Ctrl+PageDown;macOS:Cmd+Shift+]
view.sidebar 切换侧边栏 未绑定
view.editor 切换编辑器 未绑定
view.preview 切换预览 未绑定
view.outline 切换大纲 未绑定
settings.keyboard 打开快捷键设置 主修饰键+K 主修饰键+S
  • 最近标签选择器保留原有控件操作:方向键选择、Enter 确认、Esc 取消、松开 Ctrl 确认。
  • 编辑区(含只读搜索页)聚焦时,Ctrl+↑ / Ctrl+↓ 每次向上 / 向下滚动一个行高,长按连续滚动;光标位置、选区及其方向保持不变,到达顶部或底部时也不移动光标。
  • 此滚动操作属于编辑区局部按键行为,各平台均使用 Ctrl;带 Shift、Alt、Cmd 的组合及输入法组合输入不触发该操作,现有用户自定义快捷键仍优先处理。
  • 大纲菜单点击入口保留;快捷键由渲染进程统一处理,以便修改和删除默认绑定生效。
  • 查找栏的 Enter/Esc、Alt+C/Alt+W、当前文件全部替换(Windows/Linux 为 Ctrl+Alt+Enter,macOS 为 Cmd+Enter),以及文件树、右键菜单等局部控件导航沿用原有实现,不属于本次可配置命令集合。

When 与分发规则

  • 支持 !&&||==!= 和括号。
  • 比较右侧可使用裸文本或引号包围的文本,例如 editorLanguage == markdown
  • 空条件视为 true,未知 Context Key 的布尔值视为 false。
  • 不使用 eval,无效表达式不能保存。
  • 当前提供的 Context Keys:
    • editorFocuseditorTextFocuseditorHasSelectioneditorReadonlyeditorLanguage
    • inputFocusdocumentOpenmarkdownDocumentworkspaceEntrySelected
    • pathCopyAvailablerelativePathCopyAvailable
    • searchVisiblesearchInputFocus
    • outlineFocusoutlineVisiblesidebarFocussidebarVisible
    • modalOpendialogOpenplatform
  • platform 的值为 windowsmaclinux
  • 大部分默认规则含 !modalOpen,保存等操作还要求已打开文档。
  • 弹窗打开时,仅显式声明弹窗上下文且条件成立的规则可执行。
  • 快捷键设置和录制弹窗始终暂停命令分发。
  • 普通输入控件保留字符、删除、光标移动及 Ctrl/Cmd+A/C/V/X/Z/Y 等编辑行为。
  • 需要在输入控件中覆盖这些按键时,规则必须显式指定 editorFocuseditorTextFocusinputFocussearchInputFocus
  • 忽略输入法组合输入、纯修饰键和 AltGraph;优先使用物理 KeyboardEvent.code 规范化字母、数字和标点。
  • 用户绑定优先于插件绑定,插件绑定优先于系统绑定;同一优先级按条件中的词项数量估计具体程度,再按最后注册者优先。
  • 此具体程度规则是确定性启发式,不做完整逻辑蕴含判断。
  • 同键或一段键与 Chord 前缀重叠时显示可能冲突,允许保存并保留两者。
  • 简单合取条件中的互斥值可判定为无冲突;其余条件保守提示。
  • 有效 Chord 的前缀优先等待第二段,不立即执行同前缀的单段绑定。
  • 等待超过 1.8 秒、Esc、窗口失焦或控件焦点切换会取消 Chord。
  • 第二段按下时重新检查上下文;不匹配时结束等待,当前按键继续交给原控件。
  • 操作系统及 Electron 原生菜单保留的按键仍可能被外层拦截,页面对常见组合显示提醒。

存储与实现

  • 本地配置存于 Electron app.getPath('userData')/User/keybindings.json,目录和文件不存在时自动创建,初始内容为 []
  • Windows 默认路径为 %APPDATA%\mynote-client\User\keybindings.json,例如 C:\Users\Administrator\AppData\Roaming\mynote-client\User\keybindings.json
  • 实际路径由应用的用户数据目录决定,在快捷键列表和 JSON 编辑窗口中显示;不写入 VS Code 的 Code\User 目录。
  • 不再读取或写入快捷键 localStorage;按本次约定不迁移旧配置。
  • 配置属于本机应用配置,所有笔记库共用,不参与笔记库同步。
  • 文件只包含公开 JSON 格式的用户绑定与默认移除规则;重启后重新叠加到系统默认规则。
  • 写入采用 UTF-8、LF、两空格缩进;先写同目录临时文件,再替换目标文件。
  • 主进程只允许访问固定配置文件,大小上限为 1 MiB;渲染进程不能指定其他路径。
  • 启动时无法读取或解析配置会提示错误并暂用默认值,不覆盖原文件。
  • 主进程每 500 毫秒检测文件变化,窗口重新获得焦点和打开设置时也会重新读取;有效外部编辑自动更新列表和分发。
  • 运行期间外部编辑无效时保留最后有效快捷键;可打开 JSON 编辑器查看原文件并修复。
  • 保存时比较最近读取的文件内容,发现外部变更则拒绝覆盖;重新打开编辑器后再修改。
  • src/main/keybinding-file.js:固定路径文件读写、外部变更通知与 IPC;设置读写使用有大小限制的同步本地文件操作,以保持保存成功后才切换有效规则的行为。
  • src/renderer/keyboard/service.mjs:命令注册、覆盖存储、冲突检查和 Chord 分发。
  • src/renderer/keyboard/when.mjs:条件解析与求值。
  • src/renderer/keyboard/keys.mjs:按键规范化与显示。
  • src/renderer/keyboard/page.mjs:按需加载的设置及录制界面。
  • src/renderer/runtime/keyboard.js:应用命令与实际上下文接入。
  • P2 的 Profiles、云同步和导入导出未纳入本次。

P1:JSON 配置

  • 点击「编辑 JSON」查看和修改磁盘文件;列表与 JSON 共用 User/keybindings.json,不维护另一份存储。
  • 「编辑 JSON」旁提供「默认 JSON」按钮,弹出只读窗口展示全部内置及当前已加载插件的默认快捷键 JSON,包括通用键位、平台覆盖和生效条件,不受用户修改或删除规则影响。
  • 每条默认配置附带 description 命令描述,使用快捷键列表中的中文命令名称(插件使用其注册名称,缺失时回退为命令 ID),查看及复制均包含该字段;该字段仅用于默认配置参考,不属于用户配置格式。
  • 默认 JSON 支持选择文本复制及点击「复制 JSON」复制全部内容;复制成功或失败会在窗口内提示,查看和复制不会修改用户配置。
  • 复制接口允许最多 1 Mi 个字符,支持完整默认配置等长文本,并保留输入类型、非空及长度校验。
  • 每次打开默认 JSON 都读取最新默认规则;窗口内容可滚动,点击「关闭」或按 Esc 退出。
  • JSON 配置弹窗的文件路径旁提供「定位文件」链接,点击后在系统文件管理器中打开所在目录并选中 keybindings.json;不保存或关闭当前草稿,定位失败时在弹窗内提示。
  • 列表修改后重新打开 JSON 即可看到更新;JSON 保存成功后列表立即刷新。
  • JSON 草稿需点击「保存 JSON」才生效;取消不会修改配置。
  • 保存前完整校验数组、字段、键位和 When;解析或持久化失败会保留草稿及原有效配置。
  • 草稿打开期间配置发生变化时拒绝覆盖,需取消后重新打开。
  • 普通规则使用 commandkey、可选 whenwinmaclinux 字段。
  • command 前缀 - 表示移除匹配的默认绑定;按通用 key 匹配,可用 when 和平台覆盖进一步限定。
  • 插件未加载时,已有默认移除记录可用 {"remove":"绑定 ID"} 保留;文件中的负命令移除规则也会保留,插件注册后参与默认绑定过滤。
  • 未注册命令的用户规则会保留在 JSON 中,但不显示在列表,也不参与分发或冲突检查;命令注册后自动生效。
  • 未知字段会报错,避免拼写错误被静默忽略。
[
  { "command": "-file.save", "key": "ctrl+s" },
  {
    "command": "file.save",
    "key": "ctrl+shift+s",
    "mac": "cmd+shift+s",
    "linux": "",
    "when": "documentOpen && !modalOpen"
  }
]

P1:平台覆盖、搜索与冲突视图

  • key 是通用键位,winmaclinux 分别覆盖 Windows、macOS、Linux。
  • 省略平台字段表示继承通用键位;JSON 空字符串表示在该平台禁用。
  • 编辑界面提供三平台输入框及「禁用此平台」复选框;点击复选框或文字均可切换勾选状态。
  • 添加绑定或修改内置默认绑定时,平台覆盖默认留空且不勾选禁用;保存后留空的平台继承通用快捷键。
  • 修改已保存的用户绑定或插件绑定时回显已有平台覆盖和禁用状态;已有平台覆盖需要单独修改,修改通用键位不会覆盖它们。
  • 平台覆盖与通用键位共用当前的按键录制/手动输入模式;录制结果写入当前聚焦的字段。
  • 顶部「查看平台」仅改变列表和冲突预览;实际分发始终使用当前操作系统。
  • 平台禁用的绑定仍可在列表中修改、删除或恢复默认。
  • Command ID 显示在命令名下,并可直接搜索。
  • 高级过滤器支持 @command:@keybinding:@when:@source:@conflict
  • 多个条件同时匹配;带空格的值用引号,例如 @source:user @keybinding:"ctrl+k enter"
  • @source:plugin 查看所有插件规则;@source:plugin:demo 查看指定插件。
  • 点击「仅查看冲突」进入冲突视图,可叠加搜索和平台选择;再次点击返回全部绑定。
  • 冲突行列出对方命令 ID、同键或 Chord 前缀关系、When 和来源,可直接修改或删除。
  • 冲突视图沿用 P0 的保守条件重叠判断,不阻止保存。

P1:插件快捷键接口

  • 为受信任的本地渲染模块提供 window.mynoteKeyboard.registerPlugin(id, contributions)
  • 此接口负责快捷键贡献注册,不提供插件下载、安装或执行 JSON 代码的能力。
  • 命令 ID 必须以 插件ID. 为前缀,且全局唯一;命令需提供标题与执行函数。
  • 插件绑定只能引用本插件命令,支持与用户 JSON 相同的键位和平台覆盖。
  • 注册会先整体校验,失败时不留下部分命令。
  • 来源显示为 plugin:插件ID,用户可修改、删除并恢复插件默认绑定。
  • 返回的卸载函数移除命令和默认绑定、取消等待中的 Chord,并刷新页面;用户覆盖和移除记录保留。
  • 默认绑定 ID 按 plugin.插件ID.数组下标 生成;插件更新需保持绑定顺序稳定,以维持用户移除记录的对应关系。
const dispose = window.mynoteKeyboard.registerPlugin('demo', {
  commands: [{ id: 'demo.run', title: '运行示例', execute: () => console.log('demo') }],
  bindings: [{ command: 'demo.run', key: 'ctrl+alt+j', mac: 'meta+alt+j', when: '!modalOpen' }]
});
// 插件卸载时调用;可重复调用,不影响后续重新注册的插件。
dispose();
  • src/renderer/keyboard/config.mjs:公开 JSON 格式转换和整体校验。
  • src/renderer/keyboard/filter.mjs:高级搜索过滤。
  • src/renderer/keyboard/json-editor.mjs:JSON 草稿编辑界面,随快捷键设置按需加载。
  • src/renderer/keyboard/default-json.mjs:默认快捷键 JSON 只读查看和复制界面,首次点击按钮时创建弹窗。

全局热键配置

  • 「查看平台」旁提供「启用热键」复选框,默认勾选;取消勾选后立即注销全部全局热键,重新勾选后按当前保存的配置恢复注册。
  • 开关独立保存在本机 User/hotkeys.json,重启后保留;关闭期间修改快捷键配置不会重新注册热键,已有绑定和应用内快捷键保持可用。
  • 重新启用时若发生系统占用或配置无效,开关保持关闭并显示错误;开关文件保存失败时保持此前状态。
  • 在「设置 → 快捷键设置」搜索「全局热键」或 window.toggleVisibility,可修改「显示/最小化窗口(全局热键)」。
  • 默认仍为 Alt+Shift+S:窗口已聚焦时最小化;其他情况下恢复、显示并聚焦窗口。
  • 支持添加多个热键、删除、恢复默认、JSON 编辑和 Windows / macOS / Linux 平台覆盖;平台值为空字符串表示禁用。
  • 全局热键在其他应用获得焦点时也能触发,仅支持一段组合键,When 必须留空,不支持 ContextMenu 键。
  • 配置保存在本机 User/keybindings.json,保存后立即重新注册;下次启动自动读取,外部文件修改也会重载。
  • 新热键被系统或其他应用占用时,保存失败并显示原因,原热键和配置文件保持不变。
  • 文件写入失败时撤销新注册;外部配置无效或注册失败时保留上次有效热键,JSON 编辑器仍可打开文件修复。
  • 首次运行包含此功能的新代码需要重启应用;此后修改热键无需重启。
  • 全局热键由 Electron 主进程独立处理,渲染进程不重复分发。

验证

  • node --test test/hotkey-settings.test.js:总开关注销多个热键、关闭期间读写、重启持久化、重新启用冲突、保存失败回滚和无效配置下关闭。

  • node --test test/window-hotkey.test.js test/keybinding-file.test.js:全局热键注册、替换、系统占用回退、平台禁用、恢复默认、写入失败回滚和无效配置修复。

  • node --test test/keybinding-file.test.js test/keybinding-p1.test.mjs test/keybinding-service.test.mjs test/keyboard-shortcuts.test.mjs test/menu.test.js:磁盘读写、引擎、JSON、平台、插件、过滤器与菜单单元测试。

  • npm run test:smoke:keyboard:真实 Electron 设置入口、录制 Enter Chord、修改、删除、恢复默认、旧查找快捷键停用、磁盘持久化、外部修改、无效 JSON 修复和渲染进程重新加载。

  • npm run test:smoke:完整界面回归。

  • node scripts/renderer/lint.jsnpx eslint .:渲染脚本及全量 ESLint。

  • 原始产品方案文件超过现有 600 行检查上限;保留其完整内容,因此该文档仍会触发 check:file-length

P1 验证结果(2026-09-05)

  • 16 项相关单元测试通过。
  • 快捷键 Electron 回归通过,覆盖原 P0 操作、JSON 无效草稿/保存、双向同步、高级过滤、Chord 冲突详情和跨平台预览。
  • 渲染脚本检查和全量 ESLint 通过。
  • 修复新增绑定时误清除既有默认移除记录的问题,并增加回归验证。

磁盘配置验证结果(2026-09-05)

  • 21 项相关单元测试通过,包括磁盘创建、UTF-8/LF、写失败保护、过期保存保护、外部配置重载和延迟加载插件移除规则。
  • Electron 快捷键回归通过,覆盖真实文件写入、外部变更通知、错误文件修复和重新加载后的持久化。
  • 粘贴撤销回归第一次在原生重做断言失败,未改动代码重跑后通过。
  • 渲染脚本检查和全量 ESLint 通过。
  • 原始产品方案文件的 600 行限制问题仍保留。

P0 验证记录(2026-09-05)

  • 快捷键专项 Electron 测试、11 项相关单元测试、ESLint 和 Windows 目录打包通过。
  • 全量单元测试为 195/196;既有 macOS BCompare 路径测试在 Windows 下返回反斜杠,与预期正斜杠不一致。
  • 完整 Electron 回归的文件树滚动可见性检查未通过;在修改前的 9ee4507 上使用隔离存储与相同等待时限复现相同失败。
  • 完整 lint 被原始方案文档的 600 行限制阻断;渲染脚本检查与 ESLint 单独执行均通过。

README

phase-0-baseline

点赞(0) 阅读(7) 举报
目录
标题