DeepChat 无障碍(Accessibility)规范解析:键盘导航、辅助技术与渲染完整性设计

发布时间:2026/9/17 22:02:21
DeepChat 无障碍(Accessibility)规范解析:键盘导航、辅助技术与渲染完整性设计 DeepChat 无障碍Accessibility规范解析键盘导航、辅助技术与渲染完整性设计【免费下载链接】deepchatDeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat导读本文基于 DeepChat 仓库中的 无障碍设计规范docs/features/accessibility/spec.md展开系统梳理这款桌面 AI 助手如何面向不使用视觉、依赖键盘与辅助技术导航的用户群体开放全部第一方功能。文章覆盖交互布局改造、语义化控件与可访问名称约束、键盘与焦点契约、辅助技术下的完整渲染策略并结合仓库源码useAccessibilitySupport.ts、DeviceService、ChatPage.vue 等印证其底层实现。读完你可以掌握 DeepChat 无障碍能力的边界、实现路径与验证方法并理解完整渲染 vs 虚拟化窗口这一关键取舍背后的工程理由。背景与目标为什么视觉可用不等于可用DeepChat 的渲染层renderer横跨主聊天外壳、独立设置窗口、引导流程onboarding、消息列表、工具审批、插件、工作区工具以及各类辅助窗口。规范开宗明义地指出仅靠视觉上的可点击、可悬停、可拖拽并不构成可用的界面。对于使用键盘和屏幕阅读器的用户界面必须满足所有第一方操作都能按名称被发现并且可以用键盘操作焦点可见、遵循逻辑顺序且永不进入隐藏/惰性inert表面导航与异步反馈不剥夺用户对焦点的控制权可访问名称跟随当前语言环境与可见标签。这一目标覆盖从主界面到设置、插件、工作区工具的全部第一方能力属于整体产品级而非某个组件级的要求。设计与归属语义控件、共享原语与职责边界规范给出的设计基调非常克制不要为无障碍另起炉灶。具体约束包括使用原生语义控件以及仓库中已有的 Reka/shadcn 基础组件src/shadcn/components下约 305 个 Vue 组件即为此类原语文案标签统一放在vue-i18n中维护对应 i18n 契约 与 i18n.d.ts图标按钮必须有名称可访问名称表单标签与错误信息要与对应输入框关联向辅助技术暴露selected / expanded / busy / disabled等状态共享原语shared primitives只在契约共享时才统一修复功能特有的命名与焦点行为由表面所有者surface owner负责避免一刀切保留现有的类型化 preload/IPC 边界与持久化行为不允许为了无障碍重写通信层。导航与焦点要求提供具名的导航区与主内容地标landmarks并提供跳转到主要内容的键盘入口导航后要有有意义的焦点落点对话框dialog通过现有原语保持初始焦点、焦点陷阱trapping、Escape 关闭、焦点恢复悬停动作与拖拽操作必须提供键盘替代方案编辑器必须暴露具名的多行输入并且允许焦点离开不把用户困在编辑器里。规范同时明确不做的事不新增平行的无障碍 UI、不套用全局 role、不做全 DOM 标签推断、不引入新依赖、不强制修改操作系统无障碍设置。交互布局改造前与改造后规范给出了界面信息的排布对照BEFORE [Icon rail] [Session text / mouse actions] | [Visual content] [Unnamed editor] AFTER [Skip to content] [Named navigation] [Session buttons] | [Named main content] [Keyboard actions] | [Named editor status]改造后左侧导航有具名、会话按钮可键盘操作右侧主内容与编辑器、状态区均有具名且整体前置了Skip to content跳转入口。不变式与兼容性约束所有第一方动作按名可发现、按键盘可操作焦点可见、顺序合理、不进入隐藏/惰性表面导航与异步反馈保持用户对焦点的控制可访问名称跟随活动 locale 与可见标签本地化后名称同步更新测试与探索使用隔离配置文件凭证与私人对话永不提交第三方内容与外部服务可用性问题与第一方缺陷分开识别。验证契约本地 fixture 能证明什么、不能证明什么回归保护覆盖键盘可发现性、可访问名称、焦点恢复、完整加载内容、生命周期清理、类型化的原生焦点边界。验证范围包括 onboarding、聊天与历史、提供商/模型配置、设置、集成、工作区工具、辅助窗口并且要求使用隔离配置本地协议 fixture 用于验证第一方控件无需外部账号。同时规范划出了一条非常重要的边界键盘与 Chromium 可访问性树检查不能证明人类 VoiceOver/NVDA 的语音质量也不构成其他操作系统上的验收OS 自持对话框与权限语音、真实麦克风输入、加密存储持久化、外部账号授权、第三方内容、运行时安装等需要各自独立的验证不得把本地 fixture 证据当作这些边界已通过的报告依据。原生交互契约浏览器、ACP 认证与窗口切换浏览器BrowserView场景暴露显式的 Enter webpage 动作与F6 返回宿主控件主进程只接受可见的、附着于请求渲染器当前活动会话、且原生宿主窗口处于聚焦状态的浏览器进入请求ACP 认证将 F6 保留给从终端输入返回认证控件不把该键透传给终端进程插件设置关闭后恢复发起控件的焦点原生窗口切换必须同时保持操作系统焦点与渲染器焦点。辅助技术渲染完整渲染 vs 虚拟化窗口这是规范最具工程含量的部分也是与源码强相关的核心机制。能力探测与事件订阅主进程通过 Electron 的app.isAccessibilitySupportEnabled()探测辅助技术支持状态并通过accessibility-support-changed事件向渲染器推送变化。从 DeviceService.getDeviceInfo 可以看到关键实现return { // Electron cannot report assistive-technology detection on Linux. accessibilitySupportEnabled: process.platform linux || app.isAccessibilitySupportEnabled(), platform, arch: process.arch, // ... }macOS 与 Windows使用 Electron 官方探测 APILinux没有可靠的 Electron 检测 API因此直接返回 true强制完整渲染快照失败时同样保持完整渲染保守策略宁可多渲染不可漏内容。事件契约定义在 app-runtime.events.tsexport const appRuntimeAccessibilityChangedEvent defineEventContract({ name: appRuntime.accessibilityChanged, payload: z.object({ enabled: z.boolean() }) })主进程在 composition.ts 中把原生事件转为应用内事件发布app.on(accessibility-support-changed, (_event, enabled) { publishDeepchatEvent(appRuntime.accessibilityChanged, { enabled }) })渲染器端共享订阅渲染器侧的 useAccessibilitySupport.ts 使用createSharedComposable创建单一共享订阅供消息列表、Markdown 渲染、编辑器等消费者复用避免每个组件各自订阅订阅onAccessibilityChanged收到事件后更新accessibilityEnabled并置accessibilityReady true通过getDeviceInfo()拉取初始快照若在快照返回前已收到更新事件receivedUpdate以较新的原生事件为准防止旧快照覆盖新状态late snapshot cannot overwrite a newer native eventonScopeDispose时取消订阅随 Vue 作用域释放监听器初值accessibilityEnabled true、accessibilityReady false即未确认前按完整渲染处理与规范的保守策略一致。export const useAccessibilitySupport createSharedComposable(() { const accessibilityEnabled ref(true) const accessibilityReady ref(false) const deviceClient createDeviceClient() let disposed false let receivedUpdate false const unsubscribe deviceClient.onAccessibilityChanged((enabled) { receivedUpdate true accessibilityEnabled.value enabled accessibilityReady.value true }) void deviceClient.getDeviceInfo().then((info) { if (!disposed !receivedUpdate) { accessibilityEnabled.value info?.accessibilitySupportEnabled ?? true } }) // ... })三个消费方的落地1. 消息列表ChatPage.vue辅助技术支持开启时关闭消息窗口化windowing虚拟化保留全部已加载消息行第 778 行usesWindowedMessages !accessibilityEnabled.value (previousEntryCount threshold || ...)第 955 行虚拟化配置传入disableWindowing: accessibilityEnabled。即无障碍开启 → 全部消息行进入可访问性树 → 历史可被屏幕阅读器完整朗读老消息仍用显式分页加载不采用隐形滚动窗口。2. Markdown 渲染MarkdownRenderer.vueconst { accessibilityEnabled } useAccessibilitySupport() const canVirtualizeNodes computed(() props.virtualizeNodes !accessibilityEnabled.value)辅助技术开启时保留所有 Markdown 节点含流式输出前缀不做节点级虚拟化。3. 提供商模型目录ProviderModelList.vue 消费同一 composable模型目录与消息/Markdown 共享同一份原生订阅保证设置页与聊天页对辅助技术状态判断一致。4. ACP 终端AcpAuthDialog.vueconst { accessibilityEnabled } useAccessibilitySupport() watch([accessibilityEnabled, terminalLabel], ([enabled, label]) { terminal.options.screenReaderMode enabled })辅助技术开启时xterm 终端切换到screenReaderMode确保终端输出可被屏幕阅读器读取——这正是规范中ACP 认证保留 F6 从终端返回认证控件的渲染侧配套。代价与权衡规范明确承认完整渲染意味着长对话场景下 DOM/内存占用上升。因此当确认原生辅助技术关闭时现有窗口化windowing保持启用性能不受影响订阅随 Vue 作用域释放避免泄漏迟到快照不得覆盖更新事件前述receivedUpdate保护。参考Electron 官方app.isAccessibilitySupportEnabled()macOS/Windows文档。实操建议如何在本仓库中验证无障碍行为隔离配置验证使用独立 profile 启动应用避免真实凭证与私人对话进入日志或被提交规范第 5 条不变式本地协议 fixture优先用本地协议 fixture 验证第一方控件覆盖 onboarding、聊天/历史、提供商/模型设置、设置页、集成、工作区工具与辅助窗口人工辅助技术验收在 macOS/Windows 上用 VoiceOver/NVDA 做语音质量与操作体验验收注意 Chromium 可访问性树检查结果不能等同于人工朗读验收确认平台差异Linux 与快照失败场景下应用按完整渲染运行可据此评估长对话内存占用回归重点键盘可发现性、可访问名称、焦点恢复、完整内容加载、生命周期清理、类型化原生焦点边界。小结DeepChat 的无障碍规范不是一套花哨的 ARIA 补丁清单而是一份以键盘与辅助技术为第一公民的工程契约用语义控件与既有 shadcn 原语打底用 vue-i18n 保证名称跟随语言环境用共享的原生订阅统一渲染策略用完整渲染 vs 窗口化的动态切换平衡可读性与性能并在验证边界上保持诚实——本地 fixture 证明第一方契约人工辅助技术与外部依赖各自独立验收。这种默认保守、确认后优化、边界清晰的设计正是桌面 AI 应用做好无障碍的可复用范本。【免费下载链接】deepchatDeepChat - A smart assistant that connects powerful AI to your personal world项目地址: https://gitcode.com/GitHub_Trending/dee/deepchat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考