'use client'; import { useState, useCallback, useMemo } from 'react'; import { Volume2, Play, Loader2 } from 'lucide-react'; import { toast } from 'sonner'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Switch } from '@/components/ui/switch'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; import { cn } from '@/lib/utils'; import { useI18n } from '@/lib/hooks/use-i18n'; import { useSettingsStore } from '@/lib/store/settings'; import { getTTSVoices } from '@/lib/audio/constants'; import { useTTSPreview } from '@/lib/audio/use-tts-preview'; /** Extract the English name from voice name format "ChineseName (English)" */ function getVoiceDisplayName(name: string, lang: string): string { if (lang === 'en-US') { const match = name.match(/\(([^)]+)\)/); return match ? match[1] : name; } return name; } export function TtsConfigPopover() { const { t, locale } = useI18n(); const [open, setOpen] = useState(false); const { previewing, startPreview, stopPreview } = useTTSPreview(); const ttsEnabled = useSettingsStore((s) => s.ttsEnabled); const setTTSEnabled = useSettingsStore((s) => s.setTTSEnabled); const ttsProviderId = useSettingsStore((s) => s.ttsProviderId); const ttsVoice = useSettingsStore((s) => s.ttsVoice); const ttsSpeed = useSettingsStore((s) => s.ttsSpeed); const ttsProvidersConfig = useSettingsStore((s) => s.ttsProvidersConfig); const setTTSVoice = useSettingsStore((s) => s.setTTSVoice); const voices = getTTSVoices(ttsProviderId); const localizedVoices = useMemo( () => voices.map((v) => ({ ...v, displayName: getVoiceDisplayName(v.name, locale), })), [voices, locale], ); const pillCls = 'inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium transition-all cursor-pointer select-none whitespace-nowrap border'; const handlePreview = useCallback(async () => { if (previewing) { stopPreview(); return; } try { const providerConfig = ttsProvidersConfig[ttsProviderId]; await startPreview({ text: t('settings.ttsTestTextDefault'), providerId: ttsProviderId, modelId: providerConfig?.modelId, voice: ttsVoice, speed: ttsSpeed, apiKey: providerConfig?.apiKey, baseUrl: providerConfig?.baseUrl, }); } catch (error) { const message = error instanceof Error && error.message ? error.message : t('settings.ttsTestFailed'); toast.error(message); } }, [ previewing, startPreview, stopPreview, t, ttsProviderId, ttsProvidersConfig, ttsSpeed, ttsVoice, ]); const handleOpenChange = useCallback( (nextOpen: boolean) => { if (!nextOpen) { stopPreview(); } setOpen(nextOpen); }, [stopPreview], ); return ( {t('toolbar.ttsHint')} {/* Header with toggle */}
{t('toolbar.ttsTitle')}
{/* Config body */} {ttsEnabled && (
{/* Voice + Preview row */}
)}
); }