diff --git a/tui_gateway/server.py b/tui_gateway/server.py index 67b1f96d264..35b13a65914 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -5869,6 +5869,9 @@ def _(rid, params: dict) -> dict: except Exception as e: logger.warning("voice: stop_continuous failed during toggle off: %s", e) + # Clear TTS so it can be toggled independently after voice is off. + os.environ["HERMES_VOICE_TTS"] = "0" + return _ok( rid, { diff --git a/ui-tui/src/app/interfaces.ts b/ui-tui/src/app/interfaces.ts index b71e34188ef..cb2788bbf4f 100644 --- a/ui-tui/src/app/interfaces.ts +++ b/ui-tui/src/app/interfaces.ts @@ -216,6 +216,7 @@ export interface InputHandlerContext { setProcessing: StateSetter setRecording: StateSetter setVoiceEnabled: StateSetter + setVoiceTts: StateSetter } wheelStep: number } @@ -254,6 +255,7 @@ export interface GatewayEventHandlerContext { setProcessing: StateSetter setRecording: StateSetter setVoiceEnabled: StateSetter + setVoiceTts: StateSetter } } @@ -296,6 +298,7 @@ export interface SlashHandlerContext { voice: { setVoiceEnabled: StateSetter setVoiceRecordKey: (v: ParsedVoiceRecordKey) => void + setVoiceTts: StateSetter } } diff --git a/ui-tui/src/app/slash/commands/session.ts b/ui-tui/src/app/slash/commands/session.ts index 30ae7d8204e..fb990ef11be 100644 --- a/ui-tui/src/app/slash/commands/session.ts +++ b/ui-tui/src/app/slash/commands/session.ts @@ -232,6 +232,7 @@ export const sessionCommands: SlashCommand[] = [ ctx.gateway.rpc('voice.toggle', { action }).then( ctx.guarded(r => { ctx.voice.setVoiceEnabled(!!r.enabled) + ctx.voice.setVoiceTts(!!r.tts) // Render the configured record key (config.yaml ``voice.record_key``) // instead of hardcoded "Ctrl+B" — the gateway response carries the diff --git a/ui-tui/src/app/useMainApp.ts b/ui-tui/src/app/useMainApp.ts index 71768bc2b0a..fde5231c278 100644 --- a/ui-tui/src/app/useMainApp.ts +++ b/ui-tui/src/app/useMainApp.ts @@ -102,6 +102,7 @@ export function useMainApp(gw: GatewayClient) { const [stickyPrompt, setStickyPrompt] = useState('') const [catalog, setCatalog] = useState(null) const [voiceEnabled, setVoiceEnabled] = useState(false) + const [voiceTts, setVoiceTts] = useState(false) const [voiceRecording, setVoiceRecording] = useState(false) const [voiceProcessing, setVoiceProcessing] = useState(false) const [voiceRecordKey, setVoiceRecordKey] = useState(DEFAULT_VOICE_RECORD_KEY) @@ -555,7 +556,8 @@ export function useMainApp(gw: GatewayClient) { recording: voiceRecording, setProcessing: setVoiceProcessing, setRecording: setVoiceRecording, - setVoiceEnabled + setVoiceEnabled, + setVoiceTts }, wheelStep: WHEEL_SCROLL_STEP }) @@ -579,7 +581,8 @@ export function useMainApp(gw: GatewayClient) { voice: { setProcessing: setVoiceProcessing, setRecording: setVoiceRecording, - setVoiceEnabled + setVoiceEnabled, + setVoiceTts } }), [ @@ -830,7 +833,7 @@ export function useMainApp(gw: GatewayClient) { turnStartedAt: ui.sid ? turnStartedAt : null, // CLI parity: the classic prompt_toolkit status bar shows a red dot // on REC (cli.py:_get_voice_status_fragments line 2344). - voiceLabel: voiceRecording ? '● REC' : voiceProcessing ? '◉ STT' : `voice ${voiceEnabled ? 'on' : 'off'}` + voiceLabel: voiceRecording ? '● REC' : voiceProcessing ? '◉ STT' : `voice ${voiceEnabled ? 'on' : 'off'}${voiceTts ? ' [tts]' : ''}` }), [ cwd, @@ -842,7 +845,8 @@ export function useMainApp(gw: GatewayClient) { ui, voiceEnabled, voiceProcessing, - voiceRecording + voiceRecording, + voiceTts ] )