鸿蒙 PC Markdown 编辑器快捷键配置:录制、冲突检测与持久化
鸿蒙 PC Markdown 编辑器快捷键配置:录制、冲突检测与持久化
快捷键是 PC 编辑器效率的核心,但“支持快捷键”和“支持可靠的自定义快捷键”是两回事。前者只需在keydown里判断几个按键,后者必须回答一整组产品与工程问题:命令 ID 是否稳定,组合键怎样规范化,Ctrl 与 Meta 如何兼容,中文输入法组合期间会不会误触发,重复组合怎样解释,复制粘贴等基础编辑键能否被覆盖,损坏配置如何恢复,Web 与 ArkUI 谁负责持久化,应用重启后怎样保证全量状态一致。
OhMarkdown 面向 HarmonyOS PC / 2in1,把快捷键配置作为 G3 PC 完整交互的一部分,而不是一份只读帮助表。本文使用公开仓库 https://gitcode.com/VON-/codex_md_oh 中提交b11519c的真实代码,拆解十二项高频命令的录制、冲突检测、恢复默认、Bridge 双重校验和 Preferences 持久化。Web 自动化与 ArkTS 构建已经通过,设备重启恢复和物理键盘焦点仍等待模拟器恢复后验收。
为什么不能只提供三套预设
“默认、VS Code、Emacs”一类预设实现简单,却不能解决真实冲突。用户可能只想把快速打开从Ctrl+P改成Ctrl+Alt+P,保留其他默认行为;也可能使用系统输入法、辅助工具或企业软件占用了某个组合。强迫用户整套切换,会把一个局部需求扩大为大量不熟悉的变化。
本轮采用逐命令录制:设置面板列出新建、打开、保存、当前查找、工作区搜索、快速打开、插入链接、校验链接、源码、分栏、预览和命令面板十二项。点击右侧组合后进入录制态,直接按新组合;冲突不会覆盖旧值,恢复默认也不会立即写盘,只有点击保存才提交。
这套范围刻意不包含复制、粘贴、撤销、重做等编辑基础键。它们属于 CodeMirror 与系统文本编辑共同约定的保留能力,允许覆盖会让用户失去最基本的恢复路径。后续可扩展更多命令,但必须继续维持“用户可配置命令”和“不可破坏系统编辑契约”的边界。
命令 ID 必须与显示名称分离
快捷键不能以“保存文档”或“Save Document”作为持久化键。用户切换语言后显示名称会变化,翻译也可能修订;如果设置文件依赖文案,旧配置就会失效。OhMarkdown 使用稳定命令 ID,例如file.save、workspace.quickOpen和view.preview。
默认映射由独立模块定义:
exportconstDEFAULT_SHORTCUT_BINDINGS:ShortcutBindings={'file.new':'Ctrl+N','file.open':'Ctrl+O','file.save':'Ctrl+S','edit.find':'Ctrl+F','workspace.find':'Ctrl+Shift+F','workspace.quickOpen':'Ctrl+P','navigation.insertLink':'Ctrl+K','navigation.validateLinks':'Ctrl+Shift+K','view.source':'Ctrl+1','view.split':'Ctrl+2','view.preview':'Ctrl+3','app.commandPalette':'Ctrl+Shift+P'};面板标题从命令翻译表动态获取,只有命令面板本身使用单独消息。这样快捷键与命令面板看到的是同一组本地化名称,而存储仍保持语言无关。未来调整中文译名不需要迁移 Preferences。
规范化比字符串比较重要
同一个组合可能被写成Control+p、Ctrl+P、Meta+P或Command+P。若直接比较原始字符串,重复检测会漏掉等价组合。Web 模块先把修饰键归一为固定顺序Ctrl、Shift、Alt,再规范主键大小写和标点名称。
空格被表示为Space,逗号、句点、斜杠和反斜杠使用Comma、Period、Slash、Backslash,方向键使用ArrowUp等稳定名称。主键只允许字母、数字、F1-F12、方向键和有限标点,避免把输入法过程中的不可打印键或平台差异键写入设置。
录制事件同时接受ctrlKey和metaKey,最终存为Ctrl。这让自动化和未来跨桌面环境可以使用统一表示,但 HarmonyOS PC 的默认物理键仍按 Ctrl 展示。单独按 Control、Shift、Alt 或 Meta 不构成组合,面板继续等待,不会保存一个永远无法触发的修饰键。
为什么所有可配置组合都要求 Ctrl
允许单个字母或数字作为全局快捷键会直接破坏输入。用户在正文输入p时若触发快速打开,编辑器就不再可用。只要求 Shift 也不够,因为大写输入会冲突。Alt 在输入法和系统窗口操作中存在平台差异。
本轮要求组合必须含 Ctrl,可以再加 Shift 或 Alt。这个约束减少了组合空间,却换来明确的输入安全。真正支持任意和弦、双击键或模式化按键需要更完整的作用域、输入法和系统保留键模型,不属于当前 Beta 的必要复杂度。
规范函数还拒绝Ctrl+A/C/V/X/Y/Z及 Shift 变体。这些组合即使格式合法,也属于全选、复制、粘贴、剪切、重做和撤销。快捷键系统不能为了“自由”让用户配置出一个无法恢复默认的编辑器。
冲突检测必须指出占用者
只显示“快捷键冲突”会让用户逐项查找。录制得到规范字符串后,系统遍历全部命令,排除当前命令,返回第一个占用者:
exportfunctionfindShortcutConflict(bindings:ShortcutBindings,commandId:ShortcutCommandId,shortcut:string):ShortcutCommandId|undefined{returnSHORTCUT_COMMAND_IDS.find((candidate)=>candidate!==commandId&&bindings[candidate]===shortcut);}界面用当前语言显示“已被‘保存文档’占用”或对应英文。冲突发生时不修改草稿,不退出录制态,用户可以立即按另一个组合;按 Escape 只取消当前录制,仍留在快捷键设置页。这种局部恢复比关闭整个对话框更符合连续配置任务。
为什么不自动交换两个命令?交换看起来高效,却会在用户不注意时改变另一个高频操作,尤其Ctrl+S一类肌肉记忆命令。Beta 选择显式拒绝,避免隐式副作用。未来若提供交换,必须把受影响命令和确认结果清楚展示。
草稿、当前值和持久化值是三层状态
快捷键面板打开时复制当前运行配置到shortcutDraftBindings。用户录制和恢复默认只修改草稿;取消直接丢弃草稿;保存才替换当前配置并通过 Bridge 发送完整集合。这三层状态保证用户试验组合时不会立即改变正在使用的快捷键。
保存发送全量而不是单项增量。全量 JSON 只有十二个字段,体积很小,却能让原生侧判断“配置是否完整”。如果只发送{workspaceQuickOpen: 'Ctrl+Alt+P'},ArkTS 必须合并旧状态;应用崩溃或并发提交时容易形成半套配置。全量快照与文档保存基线的思路一致:一次提交描述完整事实。
恢复默认同样只替换草稿。用户可以检查十二项,再决定保存或取消。界面底部把“恢复默认”放在左侧,把取消和主保存动作放在右侧,减少误点后立即持久化的风险。
存储键为什么不用带点命令 ID
Web 运行时内部继续使用file.new等领域清晰的命令 ID,但跨 ArkTS 的存储 JSON 使用fileNew、workspaceQuickOpen等固定字段。原因不是 JSON 不能带点,而是 ArkTS 静态检查禁止通过动态索引访问对象字段;使用明确接口和点访问可以在编译期验证字段完整性。
Web 的序列化层维护命令 ID 到存储字段的映射,解析时兼容两种键,便于处理开发阶段已有数据。ArkTS 定义ShortcutBindingsPayload,逐字段组成数组进行合法性与重复检查,再显式构造输出对象。没有any、动态属性或任意键写入 Preferences。
这也是混合应用常见经验:跨层协议不必暴露每一层内部最方便的结构。只要映射稳定、测试完整,使用静态友好的 wire format 能显著降低 ArkTS 风险。
原生侧为什么还要重新校验
Web 已经规范化和冲突检测,仍不能把 Bridge 当可信来源。Markdown 内容运行在 ArkWeb,未来代码变更、调试调用或异常脚本都可能直接调用onCommand('saveShortcuts', payload)。原生侧负责持久化,就必须守住最终边界。
SettingsService 限制负载不超过 4096 字符,解析为完整接口,逐项检查规范格式、保留键和重复集合。任一字段缺失、类型错误、组合非法或冲突,整组回退默认:
exportfunctionsanitizeShortcutBindings(payload:string):string{if(payload.length===0||payload.length>4096){returnDEFAULT_SHORTCUT_BINDINGS_JSON;}try{constparsed=JSON.parse(payload)asShortcutBindingsPayload;constoccupied:Set<string>=newSet<string>();constshortcuts:Array<string>=[parsed.fileNew,parsed.fileOpen,parsed.fileSave,parsed.editFind,parsed.workspaceFind,parsed.workspaceQuickOpen,parsed.navigationInsertLink,parsed.navigationValidateLinks,parsed.viewSource,parsed.viewSplit,parsed.viewPreview,parsed.appCommandPalette];// 逐项检查格式、保留键和重复组合// 任一失败立即返回完整默认 JSON}catch(_){returnDEFAULT_SHORTCUT_BINDINGS_JSON;}}选择“整组回退”而非只修复坏字段,是为了避免默认值与保留下来的自定义值再次冲突。十二项配置很小,用户也有可见恢复默认入口;确定性比尽量抢救部分损坏数据更重要。
Preferences 持久化与启动顺序
SettingsService 使用已有ohmarkdown-settingsPreferences 文件和独立键shortcut-bindings。保存经过put和flush,返回净化后的 JSON;WorkspaceShell 把返回值作为内存事实,再同步到 ArkWeb。Web 不自行使用 localStorage,工程继续保持domStorageAccess(false)。
应用启动时,ArkWeb 可能先 ready,Preferences 异步读取稍后完成。WorkspaceShell 因此先发送默认配置,读取成功后再发送持久化配置。两个调用都走setShortcuts,Web 每次解析完整集合,后到的持久化配置替换默认值。若读取失败,仍发送默认,不让快捷键处于未初始化状态。
loadShortcutBindings(context).then((payload:string)=>{this.shortcutBindingsJson=payload;this.setEditorShortcuts();}).catch(()=>{this.shortcutBindingsJson=DEFAULT_SHORTCUT_BINDINGS_JSON;this.setEditorShortcuts();});这种顺序不依赖“设置一定比 Web 快”,适合混合应用的异步启动。设备验收需要保存自定义组合、完全退出、重新启动,再确认快速打开使用新组合;只有重启路径通过,才能把 Preferences 结论从编译与纯函数提升为设备证据。
键盘路由从硬编码升级为数据驱动
旧实现直接判断key === 'p' && shiftKey等条件。加入配置后,全局处理器遍历十二个命令,用规范化后的当前事件与绑定比较。命中后preventDefault(),再通过命令 ID执行。命令面板是特殊入口,其余全部回到命令注册中心。
constmatchedCommand=SHORTCUT_COMMAND_IDS.find((commandId)=>keyboardEventMatchesShortcut(event,shortcutBindings[commandId]));if(matchedCommand){event.preventDefault();executeShortcutCommand(matchedCommand);}路由仍处于 capture 阶段,确保Ctrl+P等组合在 CodeMirror 或浏览器默认行为之前处理。保留键不在配置集合中,因此Ctrl+Z/C/V/A继续交给编辑器和系统。中文输入法组合输入没有 Ctrl 时不会触发;物理键盘与触控板焦点切换仍需模拟器验证。
快捷键面板本身也要全键盘可用
设置不是键盘用户必须用鼠标完成的例外。默认Ctrl+Comma打开快捷键设置,命令面板也可以搜索“键盘快捷键”。打开后第一项获得焦点,Tab 在命令按钮和底部动作之间移动;点击或键盘激活绑定按钮进入录制。录制中 Escape 取消当前项,非录制状态 Escape 关闭整个对话框。
面板使用稳定网格:左侧命令名、右侧组合,最长组合不会改变行高。列表在窗口高度不足时内部滚动,头部、状态和底部动作保持可见。录制按钮用边框、浅色背景和可见提示区分,不依赖动画或尺寸变化。
开发约束要求界面不要用说明性大段文案,因此面板只呈现任务必要信息:标题、命令、当前按键、冲突或非法状态、恢复默认、取消、保存。组合录制的提示出现在当前按钮和状态行,不额外增加教程卡片。
应用内部证据
下图是当前生产 Web 单页中的真实快捷键设置。界面已切换简体中文,列出十二项命令;“快速打开”正处于录制态,底部状态要求按下包含 Ctrl 的组合。图中能看到恢复默认、取消和保存的事务边界。
这张图证明 ArkWeb 内部面板的当前实现和视觉状态,不代表 HarmonyOS Preferences 重启恢复已经在本轮设备执行。原生 HAP、ArkTS 和资源均已编译成功,但 Mac 锁定导致模拟器无法在线,文章保留这一证据边界。
自动化用例覆盖什么
Playwright 打开设置,把快速打开录制为Ctrl+Alt+P,断言按钮显示规范字符串。随后尝试把新建文档设置为已被保存占用的Ctrl+S,状态必须指出冲突,旧值保持Ctrl+N。按 Escape 取消当前录制后保存,Bridge 最后一条消息必须是saveShortcuts,JSON 中快速打开字段为新值。
对话框关闭后测试直接按Ctrl+Alt+P,预期发送quickOpen,证明新配置不是只改了显示。再次打开并恢复默认,快速打开回到Ctrl+P。另一条用例从原生 API注入只包含Ctrl+C的非法配置,Web 必须恢复完整默认集合,随后Ctrl+P仍然工作。
ArkTS 单元测试构建覆盖合法自定义、重复组合、编辑保留键和非 JSON。完整./scripts/verify-local.sh执行 Playwright 43/43、生产单 HTML、Debug HAP 和 UnitTestBuild。测试保护了规范化、路由和持久化边界,但系统级按键占用、物理键盘布局与重启恢复继续列为设备待测。
输入法与平台键位的风险
中文输入法在候选选择和组合输入期间可能产生不同的 key、code 和 modifier。当前规则要求 Ctrl,普通拼音输入不会命中;但某些输入法快捷键也使用 Ctrl+Shift,可能与工作区搜索或链接校验冲突。设备测试应在至少一种系统中文输入法下验证输入、候选、撤销和十二项组合。
键盘布局也会影响标点的event.key。本轮把逗号、句点、斜杠和反斜杠规范为名称,却没有开放所有符号,正是为了减少布局差异。F1-F12 可能被系统功能键层拦截,保存合法不等于系统一定把事件交给应用;设备验收应记录未到达应用的组合并在 UI 中避免推荐。
Ctrl 与 Meta 统一是为跨平台代码稳定,不表示 HarmonyOS PC 一定存在 Command 键。显示仍使用 Ctrl,避免给目标用户造成错误暗示。后续若扩展 macOS 或其他系统,应由平台展示层决定 Mod 文案,而不是改变存储协议。
安全与隐私
快捷键配置只保存命令与组合,不保存用户输入、文档内容或按键历史。录制态只处理当前一次 KeyboardEvent,不实现全局键盘监听,也不在应用后台采集按键。Preferences 位于应用私有目录,不同步网络。
Bridge 只接受固定saveShortcuts命令和最大 4096 字符 JSON。原生不根据命令 ID执行任意反射,也不允许配置引入新命令;十二个存储字段与十二个注册 ID一一映射。损坏配置回退默认,避免通过异常字符串制造无法退出的快捷键状态。
可访问性与可恢复性
每个绑定按钮包含“命令名:当前组合”的 aria label,录制态通过焦点边框和文本同时表达,不只依赖颜色。冲突状态使用 live region,让辅助技术可以获得变化。关闭、取消和保存都是原生按钮语义,Tab 顺序与视觉顺序一致。
即使用户保存了一套不习惯的合法组合,设置仍可通过更多面板按钮或命令面板打开,恢复默认不依赖自定义快捷键本身。原生侧任何解析错误也回退默认,因此不会出现启动后所有命令都失效、又无法进入设置的死锁。
性能与维护成本
十二项线性遍历发生在 keydown,每次只做少量字符串规范化,远低于输入渲染成本。配置面板打开时创建十二行节点,关闭后保留静态对话框容器但重建列表,不会随编辑器输入更新。Preferences 只在用户保存时 flush,不在每次按键或录制时写盘。
独立shortcut-config.ts把命令集合、规范化、冲突、事件匹配和序列化从已经很大的编辑器主文件中拆出。这符合 G3 已批准的 D2 边界:规则有 Web 运行、设置 UI、Bridge、ArkTS 校验和测试多个真实调用点,不是为了未来想象建立框架。它没有引入快捷键库、状态管理框架或插件 API。
竞争优势的验证方式
自定义快捷键并非独有功能,VS Code、Obsidian 等产品已经非常成熟。OhMarkdown 的优势目标是用较低配置成本覆盖写作高频任务,并把冲突和恢复做得明确。统一对比任务可要求用户把快速打开改为指定组合、完成打开文档、工作区搜索、视图切换、保存与导出,再恢复默认,记录完成率、操作数、错误次数和学习时间。
当前只能给全键盘工作流 2 分:配置面板、冲突检测、双层校验、自动化和构建已经齐全,但物理键盘、输入法、系统保留键和重启恢复没有最新设备证据。达到 3 分需要 HarmonyOS PC 模拟器或真机闭环;达到 4 分还需要与对标产品在相同任务下证明效率优于中位数,并完成无障碍审查。
结语
可靠的快捷键配置不是把按键字符串放进设置页,而是建立一个从稳定命令 ID、事件规范化、冲突保护、草稿事务、Bridge 验证到 Preferences 恢复的完整协议。OhMarkdown 的实现坚持全量快照、编辑保留键和失败回退,让用户获得自由,却不把基本编辑能力变成可破坏选项。
提交b11519c已推送 GitCode,Playwright 已验证自定义组合真正改变命令路由,ArkTS 已验证异常配置不能进入持久化。最终完整回归还发现通用命令执行器会在下一帧把链接助手和快捷键设置的焦点夺回编辑器,修复提交7410227让这些模态命令自行管理焦点,并增加从命令面板打开设置后首项保持焦点的断言。剩余工作集中在鸿蒙 PC 设备:物理键盘、中文输入法、应用重启和自由窗口焦点。设备证据补齐后,这套快捷键系统才具备 Beta 阶段完整结论。
设备闭环:保存、强停、重启和恢复默认
模拟器验收没有停在“设置页显示了新字符串”。快速打开原默认组合先改为Ctrl+Alt+P,保存后立即执行,应用打开快速打开面板;随后通过aa force-stop结束应用并重新启动,再次按相同组合,面板仍然打开。这证明配置确实经过 ArkTS 校验写入 Preferences,并在新 ArkWeb 页面准备后回灌,不是浏览器内存中的一次性变量。
conststoredBindings=awaitloadShortcutBindings(context);awaitthis.editorController.runJavaScript(`window.ohMarkdownEditor?.setShortcutBindings(${storedBindings})`);设备截图同时保留十二项命令列表、当前组合、恢复默认、取消和保存。完成重启验证后又执行“恢复默认 -> 保存”,辅助功能树确认弹层关闭,状态栏显示Keyboard shortcuts saved,避免把测试组合留给下一轮回归。
本轮还有一个容易被忽略的焦点事实:从Ctrl+Shift+P命令面板进入快捷键设置时,设置首项保持焦点;保存或取消后焦点才回到编辑器。若通用命令执行器无条件在下一帧调用编辑器focus(),快捷键面板虽然“打开”,键盘输入却会落回正文。修复7410227后 Playwright43/43与设备操作都通过。
快捷键小阶段现可从 2 分提升到模拟器 3 分:录制、冲突、保留键、双层校验、强停恢复和默认恢复形成闭环。仍然没有鸿蒙 PC 真机键盘矩阵、物理触控板、系统快捷键冲突和竞品统一任务计时,所以文章不会把它描述为行业领先。
