import React, { useState, useRef } from 'react'; import { VoiceName } from '../types'; import { AVAILABLE_VOICES } from '../constants'; import { Play, Pause, Loader2, Check, Volume2 } from 'lucide-react'; import { generateSpeechFromText } from '../services/geminiService'; import { base64ToUint8Array, createWavBlob } from '../services/audioUtils'; interface VoiceSelectorProps { selectedVoice: VoiceName; onVoiceChange: (voice: VoiceName) => void; } // Compact selector for header export const VoiceSelectorCompact: React.FC = ({ selectedVoice, onVoiceChange }) => { const currentVoice = AVAILABLE_VOICES.find(v => v.name === selectedVoice); return (
{currentVoice?.emoji}
); }; // Full voice panel with cards and preview export const VoicePanel: React.FC = ({ selectedVoice, onVoiceChange }) => { const [previewingVoice, setPreviewingVoice] = useState(null); const [loadingPreview, setLoadingPreview] = useState(null); const audioRef = useRef(null); const handlePreview = async (voice: typeof AVAILABLE_VOICES[0]) => { // If already previewing this voice, stop it if (previewingVoice === voice.name) { if (audioRef.current) { audioRef.current.pause(); audioRef.current.src = ''; } setPreviewingVoice(null); return; } // Stop any current preview if (audioRef.current) { audioRef.current.pause(); audioRef.current.src = ''; } setLoadingPreview(voice.name); setPreviewingVoice(null); try { const base64Audio = await generateSpeechFromText(voice.previewText, voice.name); const pcmData = base64ToUint8Array(base64Audio); const wavBlob = createWavBlob(pcmData); const audioUrl = URL.createObjectURL(wavBlob); if (!audioRef.current) { audioRef.current = new Audio(); } audioRef.current.src = audioUrl; audioRef.current.onended = () => { setPreviewingVoice(null); URL.revokeObjectURL(audioUrl); }; audioRef.current.onerror = () => { setPreviewingVoice(null); URL.revokeObjectURL(audioUrl); }; await audioRef.current.play(); setPreviewingVoice(voice.name); } catch (error) { console.error('Preview failed:', error); } finally { setLoadingPreview(null); } }; const handleSelect = (voiceName: VoiceName) => { onVoiceChange(voiceName); }; return (

Choose Your Voice

{AVAILABLE_VOICES.map((voice) => { const isSelected = selectedVoice === voice.name; const isPreviewing = previewingVoice === voice.name; const isLoading = loadingPreview === voice.name; return (
handleSelect(voice.name)} > {/* Selected checkmark */} {isSelected && (
)} {/* Voice info */}
{voice.emoji}

{voice.label}

{voice.description}

{/* Preview button */}
); })}
); }; // Keep backward compatibility export const VoiceSelector = VoiceSelectorCompact;