
鸿蒙 PC Markdown 编辑器桌面无障碍与焦点键鼠、Slider 与 ArkWeb 焦点归还PC Markdown 编辑器的无障碍不是给按钮补几个名称就结束。OhMarkdown 同时包含 ArkUI 活动栏、搜索输入、可调侧栏、设置分段控件和 ArkWeb CodeMirror键盘焦点会跨原生与 Web 两个运行时移动。命令执行后若焦点丢失用户不能继续输入语言按钮若只有颜色辅助技术不知道当前选项侧栏若只支持拖动键盘用户无法改变宽度隐藏文本若被裁切辅助功能树也会暴露不完整布局。本文基于公开仓库 https://gitcode.com/VON-/codex_md_oh 的已完成实现核心提交包括命令与焦点基线ad1e31a、语言无障碍状态ed13ee0、侧栏 Slider 与标签修复358eb3f、窄搜索完整性0d8d38b。文章只陈述已有代码与 MateBook Pro 2in1 模拟器证据不把尚未执行的全量屏幕阅读器真机审计写成通过。焦点是桌面工作流状态移动端常以触控命中为主PC 编辑器却要求连续键盘操作。用户在 CodeMirror 输入按CtrlP打开快速打开输入文件名方向键选择Enter 打开再继续输入或者按CtrlShiftP执行命令。每个步骤都必须明确焦点转移和归还。焦点错误有三种常见表现快捷键打开面板但输入框没有焦点用户文字仍写进文档执行命令后焦点停在隐藏面板编辑器无法输入ArkUI 重建布局时焦点被丢到窗口起点键盘用户需要重新 Tab 很远。因此焦点不应依赖“浏览器或系统大概会处理”。入口函数在面板切换后延迟请求具体 focus key结果打开后 Web 的jumpToOffset主动editor.focus()命令面板关闭也归还编辑器。活动栏图标必须有可读名称活动栏使用 SymbolGlyph不显示“文件、搜索、大纲”文字。视觉用户依赖熟悉图标辅助技术不能从图形猜用途所以 Builder 接收 Resource label 并设置accessibilityText。BuilderprivateactivityButton(icon:Resource,label:Resource,panel:string){Button(){SymbolGlyph(icon).fontSize(21).fontColor([this.activePanelpanelthis.sidebarOpen?#087A63:$r(app.color.workspace_text_secondary)])}.type(ButtonType.Normal).width(36).height(36).accessibilityText(label).onClick(()this.selectPanel(panel))}名称来自中英文资源语言切换后辅助功能文字同步。36 × 36 vp 命中区域稳定选中时背景和颜色改变但尺寸不变。当前 activePanel 与 sidebarOpen 的视觉选中状态还可进一步补accessibilitySelected现有语言分段按钮已经采用该模式。工具栏纯图标按钮也传入open/new/close/toggle sidebar等资源名称。使用标准 SymbolGlyph 而非手绘 SVG使视觉与平台语义保持一致但真正的朗读仍由 accessibilityText 明确提供。分段选择需要 selected 语义界面语言有跟随系统、简体中文、English 三项。背景色只能说明视觉状态辅助功能树必须知道哪个按钮选中。实现把applicationLanguage的比较结果同时用于背景和accessibilitySelected。Button(label).type(ButtonType.Normal).layoutWeight(1).height(28).backgroundColor(this.applicationLanguagelanguage?$r(app.color.workspace_sync_selected):Color.Transparent).accessibilitySelected(this.applicationLanguagelanguage).onClick(()this.updateApplicationLanguage(language))模拟器最终返回跟随系统 Button selectedtrue简体中文和 English 为 false。强制重启后结果不变既证明语义也证明持久化保存的是策略。自动保存、图片目录和拖放模式也是互斥分段控件当前视觉选中清晰但代码还没有为所有这些 Builder 都补齐accessibilitySelected。这是已知审计项不能因为语言控件通过就宣称整个设置面板已经完整无障碍。可调侧栏应当是 Slider侧栏分隔线没有文字也不像传统按钮其交互本质是有限范围数值调整。实现设置AccessibilityRoleType.SLIDER、资源化名称和当前 vp description并允许获得 Tab 焦点。.focusable(true).tabStop(true).accessibilityText($r(app.string.resize_sidebar)).accessibilityDescription(${Math.round(this.sidebarWidth)}vp).accessibilityRole(AccessibilityRoleType.SLIDER).onKeyEvent((event:KeyEvent):booleanthis.handleSidebarResizeKey(event))模拟器辅助功能树在重启后显示description392 vp。这让不可见的 Preferences 数值成为可感知状态。角色告诉辅助技术它是调整控件而不是装饰分隔线。当前平台代码没有提供标准化 min/max/value 属性只通过 description 报告当前值。后续应评估更完整的范围语义使屏幕阅读器可以朗读“392最小 220最大 480”。现有实现是可操作基线不是最终审计结论。方向键提供精确替代路径Slider 获得焦点后左右方向键以 16 vp 步长调整。只处理 Key Down 与两个方向键其他事件继续传播。每次更新通过动态窗口 clamp并持久化。privatehandleSidebarResizeKey(event:KeyEvent):boolean{if(event.type!KeyType.Down||(event.keyCode!KeyCode.KEYCODE_DPAD_LEFTevent.keyCode!KeyCode.KEYCODE_DPAD_RIGHT)){returnfalse;}constdeltaevent.keyCodeKeyCode.KEYCODE_DPAD_LEFT?-16:16;this.sidebarWidththis.clampSidebarWidth(this.sidebarWidthdelta);this.persistSidebarWidth();returntrue;}键盘路径不只是无障碍备用也适合精确微调。鼠标拖到大致宽度再用方向键调整 16 vp比尝试停在特定像素更可控。边界 220/480 由同一 clamp 保护不会因为输入方式不同得到越界状态。当前尚未支持 Home/End 直接到最小/最大也没有双击恢复默认。它们可以作为后续效率功能但不能替代已存在的基本方向键操作。搜索输入的焦点请求打开搜索面板或切换搜索模式后WorkspaceShell 使用稳定 focus keysearch-query-input请求焦点。延迟 60 或 100 ms 是为了等待条件 Builder 完成布局如果在状态切换同一同步栈立即请求目标可能尚未挂载。privatesetSearchPanelMode(mode:SearchPanelMode):void{this.searchPanelModemode;if(modeSearchPanelMode.QUICK_OPENthis.workspaceRootUri.length0){this.executeWorkspaceSearch();}setTimeout((){focusControl.requestFocus(search-query-input);},60);}Quick Open 允许空查询并立即加载候选输入框仍获得焦点用户可直接键入。Current/Workspace 切换也复用同一 focus key不因 placeholder 变化创建新的键盘路径。定时请求是实用方案但固定 60 ms 不是强同步保证。低性能真机或复杂动画下仍需验证更理想是组件挂载回调或明确的下一帧机制。当前 MateBook Pro 2in1 模拟器纯键盘CtrlP - plan - Enter已通过。搜索列表的键盘导航输入框处理非 Current 模式的方向键、Enter 与 Escape。上下键更新选中索引并让 Scroller 滚动Enter 打开当前项搜索运行中 Escape 取消。没有结果时不消费方向键保留输入控件默认行为。if(event.keyCodeKeyCode.KEYCODE_DPAD_DOWN||event.keyCodeKeyCode.KEYCODE_DPAD_UP){constdeltaevent.keyCodeKeyCode.KEYCODE_DPAD_DOWN?1:-1;constnextIndexMath.max(0,Math.min(this.workspaceSearchResults.length-1,this.workspaceSearchSelectedIndexdelta));this.workspaceSearchSelectedIndexnextIndex;this.workspaceSearchScroller.scrollToIndex(nextIndex,true);returntrue;}选中索引始终钳制不会越界。列表滚动与选中同步键盘用户不需要猜当前项。结果行稳定高度和路径省略避免选中背景改变布局。搜索条件切换会清理旧结果避免键盘 Enter 打开按旧 Regex 条件生成的候选。布局从两行切单行不会改变索引或条件它只是视觉变化。跨 ArkUI 与 ArkWeb 的快捷键路由CodeMirror 处于焦点时Web 监听CtrlP和CtrlShiftP。前者向原生发quickOpen后者在 Web 内打开命令面板。修饰键分支明确避免两者同时触发。if(keypevent.shiftKey){event.preventDefault();commandPalette.hidden?openCommandPalette():closeCommandPalette();return;}if(keyp!event.shiftKey){event.preventDefault();window.OhMarkdownEditor?.requestCommand(quickOpen);return;}Web 只发送受限命令名原生负责打开侧栏和聚焦输入。文件枚举权限不进入 ArkWeb。Meta 兼容保留但鸿蒙 PC 主路径用 Control 验证。快捷键命令 ID不随语言变化。中文命令面板可以用“保存”搜索但执行仍是file.save焦点路由基于稳定 ID不依赖翻译文本。精确跳转后归还编辑器焦点工作区搜索结果打开后原生读取最新文件、解析偏移、切到源码模式再调用 WebjumpToOffset。Web 设置 selection、滚动到可见位置最后editor.focus()。functionjumpToOffset(offset:number,length:number0):boolean{if(!Number.isInteger(offset)||!Number.isInteger(length)||offset0||length0||offsetlengtheditor.state.doc.length){returnfalse;}if(currentModepreview){setMode(source);}editor.dispatch({selection:{anchor:offset,head:offsetlength},effects:EditorView.scrollIntoView(offset,{y:start,yMargin:18})});editor.focus();returntrue;}用户按 Enter 打开结果后可以立即输入或用键盘扩展选择不需要再点击编辑区。函数对非法偏移返回 false原生把它视为失败不假装焦点已正确落位。预览模式下先切源码避免在不可编辑 DOM 中设置 CodeMirror 选择。焦点和视图模式作为跳转语义的一部分保证结果可见且可继续操作。命令面板关闭后的焦点连续性命令面板是 Web 内部 overlay。打开时查询输入获得焦点方向键在候选间移动Enter 执行Escape 关闭。关闭后应把焦点还给 EditorView否则用户下一次按键可能落到 body 或隐藏 input。命令执行函数与面板状态分开disabled 命令不能执行原生命令通过受限 Bridge纯 Web 命令在 EditorView 上操作。无论哪条路径面板关闭不重建编辑器。Playwright 命令面板测试覆盖打开、筛选、键盘执行和 Bridge mock。运行时语言切换测试还证明中文关键词更新后面板继续可操作。真实设备命令截图展示筛选与执行同步但完整屏幕阅读器朗读顺序仍需真机专项审计。本地化必须同步 ARIAArkWebsetLocale不只更改可见文本还更新documentElement.lang、workspace、editorHost、preview、commandPalette、查询和候选列表 aria-label。三方差异的标题、图例、栏名和关闭按钮也同步。document.documentElement.langeditorLocale;workspace.setAttribute(aria-label,messages.workspaceLabel);editorHost.setAttribute(aria-label,messages.sourceEditorLabel);preview.setAttribute(aria-label,messages.previewLabel);commandPalette.setAttribute(aria-label,messages.commandPaletteLabel);commandQuery.setAttribute(aria-label,messages.searchCommands);如果只改变视觉文案屏幕阅读器可能仍以英文语言规则朗读中文。lang 与 aria 同步让两套 UI 栈在切换后保持一致。CodeMirror 不重建焦点和 selection 保留。中文系统跟随模式重启后原生辅助功能树显示中文并保留 selected英文模式则显示英文原生与 Web 占位。这是本地化和无障碍交叉验证而不是两个互不相关功能。窄侧栏中文本完整也是无障碍问题220 vp 侧栏中旧布局裁切“正则表达式”。修复后辅助功能树给出三个完整文本边界区分大小写[706,627][811,651]全词匹配[926,627][1010,651]正则表达式[706,680][811,704]。界面截图能看出两行辅助树边界进一步确认文本节点没有落到容器外。扩大到 408 vp 后三者纵坐标相同恢复单行。状态与标签不因布局分支丢失。无障碍测试因此也是响应式布局测试。文字被视觉裁切但 accessibilityText 仍完整并不代表可接受因为低视力用户仍依赖视觉反过来视觉完整但节点无名称也不合格。两条证据都需要。真实应用截图下面截图来自 HarmonyOS MateBook Pro 2in1 模拟器。侧栏调整柄位于文件面板与编辑区之间可获得 392 vp description工作台中活动栏、标签、模式按钮和状态栏保持稳定。设备还验证纯键盘快速打开编辑器中CtrlP输入plan方向或默认选中后 Enter目标docs/plan.md打开且编辑器获得焦点。命令面板和搜索准确跳转各有真实应用截图支持功能结果。截图不能展示焦点朗读内容辅助功能树和自动化补足这部分。当前没有录制完整屏幕阅读器语音因此不宣称 WCAG 或某等级认证只记录已实现语义与剩余审计项。自动化与设备验证分层Playwright30/30覆盖命令面板键盘、快捷键路由、运行时语言、搜索跳转和 Web 焦点相关行为。ArkTSUnitTestBuild覆盖语言与侧栏解析。Debug HAP、ohosTest HAP 构建通过MateBook Pro 2in1 模拟器 ohosTest7/7。最终 Debug HAP SHA-256 为367ab8650479aa1fa8fe73bd1ebadd9a53f46659c850c2e388fc799d5cb88e5bohosTest HAP 为b7230037b51044fe16168d2c835fb891e1c70f675941a1046165bc895217592c。两者未签名。Web 自动化擅长精确断言 activeElement、selection 和 aria模拟器擅长真实 ArkUI 焦点、辅助树和键盘事件真机还需要验证物理键盘、触控板、屏幕阅读器和显示缩放。三者不能互相冒充。已知缺口与审计清单自动保存和图片分段按钮还应统一补 selected 语义活动栏可补当前面板 selected/expanded文档标签应朗读文件名、活动状态和是否已修改侧栏 Slider 可补 min/max/value搜索项整行点击与 checked 朗读需要专项验证。跨 300 vp 断点时 Checkbox 焦点是否保持尚未记录虽然拖动时焦点通常在调整柄。系统大字体、RTL、高对比度、仅键盘关闭冲突弹窗、焦点陷阱和屏幕阅读器完整顺序都应进入后续真机矩阵。这些缺口不否定现有能力但决定了报告用词可以说关键路径具备名称、selected、Slider、方向键和焦点归还不能说整个应用已经完成全面无障碍认证。结论OhMarkdown 已把焦点与无障碍作为鸿蒙 PC 工作流状态处理图标按钮有资源化名称语言选择暴露 selected侧栏以 Slider 角色进入 Tab 顺序并支持方向键搜索模式主动聚焦输入结果跳转和命令关闭归还 CodeMirrorArkWeb 切换语言同步 lang 与 ARIA。真实模拟器辅助树证明跟随系统选中、392 vp Slider 和窄侧栏完整文本纯键盘快速打开与自动化继续通过。下一步不是宣布审计完成而是沿现有模式补齐所有分段控件、标签脏状态、标准范围值和真机屏幕阅读器矩阵让 PC 高效路径与无障碍路径真正使用同一套稳定交互。