Implements the SpeechSettings component with four sections: - STT settings (enable/disable, language preference) - TTS settings (enable/disable, voice selector, tier preference, auto-play, speed control) - Voice preview with test button - Provider status with health indicators Also adds Slider UI component and getHealthStatus API client function. 30 unit tests covering all sections, toggles, voice loading, and PDA-friendly design. Fixes #404 Co-Authored-By: Claude Opus 4.6 <[email protected]>
83 lines
1.8 KiB
TypeScript
83 lines
1.8 KiB
TypeScript
/**
|
|
* Speech API client
|
|
* Handles text-to-speech synthesis and voice listing via /api/speech
|
|
*/
|
|
|
|
import { apiGet } from "./client";
|
|
import { API_BASE_URL } from "../config";
|
|
|
|
export type SpeechTier = "default" | "premium" | "fallback";
|
|
|
|
export interface VoiceInfo {
|
|
id: string;
|
|
name: string;
|
|
language: string;
|
|
gender?: string;
|
|
preview_url?: string;
|
|
tier?: SpeechTier;
|
|
isDefault?: boolean;
|
|
}
|
|
|
|
export interface SynthesizeOptions {
|
|
text: string;
|
|
voice?: string;
|
|
speed?: number;
|
|
format?: string;
|
|
tier?: string;
|
|
}
|
|
|
|
export interface VoicesResponse {
|
|
data: VoiceInfo[];
|
|
}
|
|
|
|
export interface ProviderHealth {
|
|
available: boolean;
|
|
}
|
|
|
|
export interface HealthResponse {
|
|
data: {
|
|
stt: ProviderHealth;
|
|
tts: ProviderHealth;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Fetch available TTS voices
|
|
* Optionally filter by tier (default, premium, fallback)
|
|
*/
|
|
export async function getVoices(tier?: SpeechTier): Promise<VoicesResponse> {
|
|
const endpoint = tier ? `/api/speech/voices?tier=${tier}` : "/api/speech/voices";
|
|
return apiGet<VoicesResponse>(endpoint);
|
|
}
|
|
|
|
/**
|
|
* Fetch health status of speech providers (STT and TTS)
|
|
*/
|
|
export async function getHealthStatus(): Promise<HealthResponse> {
|
|
return apiGet<HealthResponse>("/api/speech/health");
|
|
}
|
|
|
|
/**
|
|
* Synthesize text to speech audio
|
|
* Returns the audio as a Blob since the API returns binary audio data
|
|
*/
|
|
export async function synthesizeSpeech(options: SynthesizeOptions): Promise<Blob> {
|
|
const url = `${API_BASE_URL}/api/speech/synthesize`;
|
|
|
|
const response = await fetch(url, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
credentials: "include",
|
|
body: JSON.stringify(options),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text().catch(() => "Unknown error");
|
|
throw new Error(`Speech synthesis failed: ${errorText}`);
|
|
}
|
|
|
|
return response.blob();
|
|
}
|