fix(tui): address Copilot follow-up on wire typing + escape precedence

Two follow-ups from the latest Copilot pass:

* **Config wire typing honesty (`gatewayTypes.ts`)**
  `config.get full` forwards raw `yaml.safe_load()` output, so
  `voice.record_key` can be any scalar/container when hand-edited.
  Typing it as `string` suggests a normalized contract that the
  backend does not guarantee and makes unsafe callers more likely.
  Change `ConfigVoiceConfig.record_key` to `unknown` with an
  explicit comment that callers must normalize at runtime.

* **Escape-based voice bindings were swallowed before voice check**
  `useInputHandlers()` handled `key.escape` for queue-edit cancel and
  selection clear before `isVoiceToggleKey(...)`, so configured
  `ctrl+escape` / `alt+escape` / `super+escape` chords were advertised
  but never toggled recording in those UI states.
  Add an early escape+voice check before generic Esc handlers so
  escape-based voice bindings win when configured, while plain Esc
  behavior remains unchanged.

Also updated PR #19835 description text to remove stale cmd/command
alias claims and match the current parser contract.
This commit is contained in:
Brooklyn Nicholson 2026-05-04 14:34:19 -05:00
parent 02374795a0
commit bae5a1bbe0
2 changed files with 14 additions and 4 deletions

View file

@ -348,9 +348,17 @@ export function useInputHandlers(ctx: InputHandlerContext): InputHandlerResult {
return scrollTranscript(key.pageUp ? -step : step)
}
// Queue-edit cancel beats selection-clear: the queue header explicitly
// promises "Esc cancel", so honoring it takes priority over the implicit
// selection-dismissal convention. Without an active edit, fall through.
// Escape-based voice bindings (ctrl/alt/super+escape) must win before the
// generic Esc handlers below; otherwise queue-edit cancel / selection-clear
// would swallow the chord and /voice would advertise a shortcut that never
// actually toggles recording in those UI states.
if (key.escape && isVoiceToggleKey(key, ch, voice.recordKey)) {
return voiceRecordToggle()
}
// Queue-edit cancel beats selection-clear for plain Esc: the queue header
// explicitly promises "Esc cancel", so honoring it takes priority over the
// implicit selection-dismissal convention. Without an active edit, fall through.
if (key.escape && cState.queueEditIdx !== null) {
return cActions.clearIn()
}

View file

@ -76,7 +76,9 @@ export interface ConfigDisplayConfig {
}
export interface ConfigVoiceConfig {
record_key?: string
// Raw `yaml.safe_load()` value from config; may be non-string if hand-edited.
// Callers must normalize/validate at runtime (parseVoiceRecordKey()).
record_key?: unknown
}
export interface ConfigFullResponse {