feat: add voice capability status reporting to client

This commit is contained in:
Mohamed Boudra
2026-02-14 13:47:07 +07:00
parent a644adbf9b
commit deeba6d6eb
5 changed files with 197 additions and 15 deletions
@@ -4,7 +4,6 @@ import {
Text,
ActivityIndicator,
Platform,
Alert,
} from "react-native";
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
@@ -32,6 +31,7 @@ import { encodeImages } from "@/utils/encode-images";
import { useKeyboardNavStore } from "@/stores/keyboard-nav-store";
import { focusWithRetries } from "@/utils/web-focus";
import { useVoiceOptional } from "@/contexts/voice-context";
import { useToast } from "@/contexts/toast-context";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Shortcut } from "@/components/ui/shortcut";
@@ -80,6 +80,7 @@ export function AgentInputArea({
const client = useSessionStore(
(state) => state.sessions[serverId]?.client ?? null
);
const toast = useToast();
const voice = useVoiceOptional();
const isConnected = client?.isConnected ?? false;
@@ -470,10 +471,17 @@ export function AgentInputArea({
}
void voice.startVoice(serverId, agentId).catch((error) => {
console.error("[AgentInputArea] Failed to start voice mode", error);
const message = error instanceof Error ? error.message : "Voice features are not available right now.";
Alert.alert("Voice unavailable", message);
const message =
error instanceof Error
? error.message
: typeof error === "string"
? error
: null;
if (message && message.trim().length > 0) {
toast.error(message);
}
});
}, [agentId, isConnected, serverId, voice]);
}, [agentId, isConnected, serverId, toast, voice]);
function handleEditQueuedMessage(id: string) {
const item = queuedMessages.find((q) => q.id === id);
+24 -7
View File
@@ -10,7 +10,6 @@ import {
Image,
Platform,
BackHandler,
Alert,
} from "react-native";
import {
useState,
@@ -34,6 +33,7 @@ import type { DaemonClient } from "@server/client/daemon-client";
import { usePanelStore } from "@/stores/panel-store";
import { useSessionStore } from "@/stores/session-store";
import { useVoiceOptional } from "@/contexts/voice-context";
import { useToast } from "@/contexts/toast-context";
import { resolveVoiceUnavailableMessage } from "@/utils/server-info-capabilities";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Shortcut } from "@/components/ui/shortcut";
@@ -136,6 +136,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
ref
) {
const { theme } = useUnistyles();
const toast = useToast();
const voice = useVoiceOptional();
const toggleAgentList = usePanelStore((state) => state.toggleAgentList);
const toggleFileExplorer = usePanelStore((state) => state.toggleFileExplorer);
@@ -288,11 +289,11 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
const startDictationIfAvailable = useCallback(async () => {
if (dictationUnavailableMessage) {
Alert.alert("Dictation unavailable", dictationUnavailableMessage);
toast.error(dictationUnavailableMessage);
return;
}
await startDictation();
}, [dictationUnavailableMessage, startDictation]);
}, [dictationUnavailableMessage, startDictation, toast]);
// Cmd+D to start/submit dictation, Cmd+Shift+D toggles realtime voice, Escape cancels dictation
useEffect(() => {
@@ -324,8 +325,15 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
}
void voice.startVoice(voiceServerId, voiceAgentId).catch((error) => {
console.error("[MessageInput] Failed to start realtime voice", error);
const message = error instanceof Error ? error.message : String(error);
Alert.alert("Voice unavailable", message);
const message =
error instanceof Error
? error.message
: typeof error === "string"
? error
: null;
if (message && message.trim().length > 0) {
toast.error(message);
}
});
};
@@ -396,6 +404,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
isRealtimeVoiceForCurrentAgent,
isScreenFocused,
startDictationIfAvailable,
toast,
voiceAgentId,
voiceServerId,
voice,
@@ -490,13 +499,21 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
}
void voice.startVoice(voiceServerId, voiceAgentId).catch((error) => {
console.error("[MessageInput] Failed to start realtime voice", error);
const message = error instanceof Error ? error.message : String(error);
Alert.alert("Voice unavailable", message);
const message =
error instanceof Error
? error.message
: typeof error === "string"
? error
: null;
if (message && message.trim().length > 0) {
toast.error(message);
}
});
}, [
disabled,
handleStopRealtimeVoice,
isConnected,
toast,
voice,
voiceAgentId,
voiceServerId,