Files
hermes-agent/apps/desktop/electron/renderer-accessibility.ts
Brooklyn Nicholson a2587b8c9f fix(desktop): honor every documented renderer_accessibility off value (#122080 review)
YAML parses a bare `0` as int, which hit neither the bool nor the str
branch of _desktop_launch_options, so the unquoted off-switch silently
kept the tree on. Accept numeric scalars explicitly (bool checked first —
it is an int subclass), and add `disabled`/`enabled` to all three word
lists (launcher _A11Y_OFF_WORDS, readDesktopLaunchConfig, RENDERER_
ACCESSIBILITY_OFF_WORDS) so both launch paths accept the same
vocabulary.
2026-09-26 21:57:37 -05:00

68 lines
3.1 KiB
TypeScript

/**
* renderer-accessibility.ts
*
* Chromium only builds the renderer's accessibility tree when it detects an
* assistive technology — and dictation tools that insert text through the OS
* accessibility APIs (Wispr Flow and similar) do not register the way a
* screen reader does, so the tree never appears: the window exposes only its
* chrome and the contenteditable composer is invisible to them, on macOS
* (#118271) and on Windows (#92607) alike. Electron's manual switch for that
* is `app.setAccessibilitySupportEnabled(true)` once the app is ready.
*
* Default ON for the platforms where the typed API exists (darwin, win32) —
* the composer being reachable by every accessibility-driven input method is
* the point of the bug fix; the tree's rendering cost is the price. A
* perf-sensitive setup can opt out with `desktop.renderer_accessibility:
* false` in config.yaml, which becomes HERMES_DESKTOP_RENDERER_ACCESSIBILITY=0
* — bridged by the `hermes desktop` launcher and, for packaged launches, by
* main.ts's pre-ready config read (readDesktopLaunchConfig). Setting the
* variable directly works too.
*
* Extracted dependency-free — the Electron app object is injected — so the
* platform/env decision is unit-testable per branch without booting Electron,
* per the windows-child-options.ts convention.
*/
/** Values that turn the feature OFF when HERMES_DESKTOP_RENDERER_ACCESSIBILITY
* carries them (same words the launcher accepts for false). */
const RENDERER_ACCESSIBILITY_OFF_WORDS = new Set(['0', 'false', 'no', 'off', 'disabled'])
/** Whether this boot must expose the renderer's accessibility tree to the OS.
*
* @param env - environment to read HERMES_DESKTOP_RENDERER_ACCESSIBILITY
* from (defaults to the real process env).
* @param platform - defaults to the real platform; injectable for tests.
*/
export function shouldEnableRendererAccessibility(
env: { HERMES_DESKTOP_RENDERER_ACCESSIBILITY?: string | undefined } = process.env,
platform: NodeJS.Platform = process.platform
): boolean {
if (platform !== 'darwin' && platform !== 'win32') {
// The typed Electron API is darwin/win32. Linux accessibility flows
// through the platform's own ATK stack and needs no manual enable here.
return false
}
const raw = (env.HERMES_DESKTOP_RENDERER_ACCESSIBILITY ?? '').trim().toLowerCase()
return !RENDERER_ACCESSIBILITY_OFF_WORDS.has(raw)
}
/** Minimal slice of Electron's app API this module needs — injected so the
* module stays importable without Electron. */
export interface RendererAccessibilityApp {
setAccessibilitySupportEnabled(enabled: boolean): void
}
/** Enable the renderer accessibility tree when this boot's platform/env says
* to. Call after the app `ready` event (the Electron API's requirement). */
export function enableRendererAccessibility(options: {
appApi: RendererAccessibilityApp
env?: { HERMES_DESKTOP_RENDERER_ACCESSIBILITY?: string | undefined }
platform?: NodeJS.Platform
}): void {
if (shouldEnableRendererAccessibility(options.env, options.platform)) {
options.appApi.setAccessibilitySupportEnabled(true)
}
}