- encodeWav: 44-byte WAV header encoder (RIFF/WAVE/fmt/data), PCM mono 16-bit - useVadRecorder: wraps useMicVAD with startOnLoad:false, auto-stop on speech end, POSTs to /api/transcribe - useVoiceMode: reads/writes voiceMode from GET/PATCH /api/nexus/settings with optimistic update
71 lines
1.8 KiB
TypeScript
71 lines
1.8 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
|
|
type VoiceMode = "text" | "voice_input" | "full_voice";
|
|
|
|
interface UseVoiceModeReturn {
|
|
mode: VoiceMode;
|
|
setMode: (next: VoiceMode) => Promise<void>;
|
|
isLoading: boolean;
|
|
}
|
|
|
|
export function useVoiceMode(): UseVoiceModeReturn {
|
|
const [mode, setModeState] = useState<VoiceMode>("text");
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
// Load current voiceMode from nexus-settings on mount
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
const load = async () => {
|
|
try {
|
|
const res = await fetch("/api/nexus/settings", {
|
|
credentials: "include",
|
|
});
|
|
|
|
if (res.ok && !cancelled) {
|
|
const data = (await res.json()) as { voiceMode?: string };
|
|
const raw = data.voiceMode;
|
|
if (raw === "voice_input" || raw === "full_voice" || raw === "text") {
|
|
setModeState(raw as VoiceMode);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error("[useVoiceMode] Failed to load settings:", err);
|
|
} finally {
|
|
if (!cancelled) {
|
|
setIsLoading(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
load();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
|
|
const setMode = async (next: VoiceMode): Promise<void> => {
|
|
const previous = mode;
|
|
// Optimistic update
|
|
setModeState(next);
|
|
|
|
try {
|
|
const res = await fetch("/api/nexus/settings", {
|
|
method: "PATCH",
|
|
credentials: "include",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ voiceMode: next }),
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`PATCH /api/nexus/settings returned ${res.status}`);
|
|
}
|
|
} catch (err) {
|
|
console.error("[useVoiceMode] Failed to update voiceMode:", err);
|
|
// Revert on error
|
|
setModeState(previous);
|
|
}
|
|
};
|
|
|
|
return { mode, setMode, isLoading };
|
|
}
|