Files
hermes-agent/apps/desktop/src/lib/audio-context.ts
T
Brooklyn Nicholson 5a027a0081 refactor(desktop): give duplicated helpers one owner
Splitting the god files made a pile of copy-paste helpers visible and,
for the first time, fixable — sharing them previously meant importing a
god file. Hashing function bodies through the TypeScript AST found
twelve groups desktop-wide; production code is now at zero duplicates.

Each helper went to the module that already owns its concern:
firstStringField to lib/text, the two REST 404 predicates to
lib/gateway-rpc beside isMissingRpcMethod, useDebounced and
prefersReducedMotion to their hooks, the superseded-bootstrap guard to
electron/ssh-connection, the composer keyup handler to the trigger hook
that owns the rest of that state machine, and clampDataUrlReadMaxMb to
apps/shared, replacing a "keep these in sync" comment between two
copies.

Only helpers with no existing owner got a new file: lib/mcp-servers,
lib/audio-context, lib/keyed-timeouts, lib/pointer-drag, and the command
palette's status row. Error-shape predicates are the worst thing to
copy — when the backend changes how it reports a missing route, every
copy has to be found.
2026-08-19 10:50:52 -05:00

38 lines
1.1 KiB
TypeScript

// One lazily-created AudioContext for the app's synthesized UI cues (wake
// chime, turn-completion sound, thinking blips). Browsers cap how many
// contexts a page may open, and these cues are short oscillator bursts that
// need no isolation from each other, so they share one instead of each holding
// its own. Nothing closes it — it lives for the window.
let ctx: AudioContext | null = null
/** The shared AudioContext, or null where WebAudio is unavailable. */
export function getAudioContext(): AudioContext | null {
if (typeof window === 'undefined') {
return null
}
try {
if (!ctx) {
const Ctor =
window.AudioContext || (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext
if (!Ctor) {
return null
}
ctx = new Ctor()
}
// Autoplay policies can leave the context suspended until a gesture; a
// resume() here recovers it once the user has interacted with the window.
if (ctx.state === 'suspended') {
void ctx.resume().catch(() => undefined)
}
return ctx
} catch {
return null
}
}