import { X, Key, Zap, Palette, User, Volume2, Moon, Sun, Monitor, Bell, Minimize2 } from 'lucide-react' import { useSettingsStore } from '../stores/settingsStore' import { getAllCharacters, getCharacter } from '../lib/characters' import { getElevenLabsClient, Voice } from '../lib/elevenlabs' import { useEffect, useState } from 'react' interface SettingsPanelProps { onClose: () => void } export function SettingsPanel({ onClose }: SettingsPanelProps) { const { openrouterApiKey, elevenLabsApiKey, temperature, maxTokens, currentCharacter, customSystemPrompt, voiceEnabled, ttsVoice, ttsModel, ttsSpeed, ttsStability, ttsSimilarityBoost, ttsConversationMode, sttLanguage, sttMode, theme, notificationsEnabled, minimizeToTray, setOpenRouterApiKey, setElevenLabsApiKey, setTemperature, setMaxTokens, setCurrentCharacter, setCustomSystemPrompt, setVoiceEnabled, setTtsVoice, setTtsModel, setTtsSpeed, setTtsStability, setTtsSimilarityBoost, setTtsConversationMode, setSttLanguage, setSttMode, setTheme, setNotificationsEnabled, setMinimizeToTray, } = useSettingsStore() const [browserVoices, setBrowserVoices] = useState([]) const [elevenLabsVoices, setElevenLabsVoices] = useState([]) const [loadingVoices, setLoadingVoices] = useState(false) const [voiceError, setVoiceError] = useState(null) const characters = getAllCharacters() const selectedCharacter = getCharacter(currentCharacter) // Debug: Log current settings on mount useEffect(() => { console.log('⚙️ SettingsPanel mounted') console.log('📥 Current ttsVoice from store:', ttsVoice) console.log('🔑 OpenRouter API Key exists:', !!openrouterApiKey, 'Length:', openrouterApiKey?.length || 0) console.log('🔑 ElevenLabs API Key exists:', !!elevenLabsApiKey, 'Length:', elevenLabsApiKey?.length || 0) console.log('💾 LocalStorage contents:', localStorage.getItem('eve-settings')) }, [openrouterApiKey, elevenLabsApiKey, ttsVoice]) // Load browser voices useEffect(() => { // Check if speechSynthesis is available (may not be in Tauri WebView) if (typeof window === 'undefined' || !window.speechSynthesis) { console.warn('⚠️ Speech Synthesis API not available in this environment') return } const loadVoices = () => { try { const voices = window.speechSynthesis.getVoices() setBrowserVoices(voices) console.log(`🔊 Loaded ${voices.length} browser voices`) // Check for duplicate voiceURIs const voiceURIs = voices.map(v => v.voiceURI) const duplicates = voiceURIs.filter((uri, index) => voiceURIs.indexOf(uri) !== index) if (duplicates.length > 0) { console.warn('⚠️ Found duplicate voice URIs:', [...new Set(duplicates)]) } } catch (error) { console.warn('⚠️ Failed to load browser voices:', error) } } loadVoices() try { window.speechSynthesis.addEventListener('voiceschanged', loadVoices) } catch (error) { console.warn('⚠️ Failed to add voiceschanged listener:', error) } return () => { try { if (window.speechSynthesis) { window.speechSynthesis.removeEventListener('voiceschanged', loadVoices) } } catch (error) { // Ignore cleanup errors } } }, []) // Load ElevenLabs voices when API key is configured useEffect(() => { const loadElevenLabsVoices = async () => { if (!elevenLabsApiKey) { setElevenLabsVoices([]) return } setLoadingVoices(true) setVoiceError(null) try { const client = getElevenLabsClient(elevenLabsApiKey) const voices = await client.getVoices() console.log('🎵 ElevenLabs voices loaded:', voices.length) console.log('🎵 Sample voice:', voices[0]) console.log('🎵 All voice IDs:', voices.map(v => `${v.name}: ${v.voice_id}`)) setElevenLabsVoices(voices) } catch (error) { console.error('Failed to load ElevenLabs voices:', error) setVoiceError('Failed to load ElevenLabs voices. Check your API key.') setElevenLabsVoices([]) } finally { setLoadingVoices(false) } } loadElevenLabsVoices() }, [elevenLabsApiKey]) return (
{/* Header */}

Settings

{/* Content */}
{/* API Keys Section */}

API Keys

{openrouterApiKey ? (
Key loaded from environment.
) : ( setOpenRouterApiKey(e.target.value)} placeholder="sk-or-v1-..." className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-800 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500" /> )}

Required for AI chat functionality

{elevenLabsApiKey ? (
Key loaded from environment.
) : ( setElevenLabsApiKey(e.target.value)} placeholder="..." className="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-800 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500" /> )}

For text-to-speech features

{/* Appearance Section */}

Appearance

Choose your preferred color theme. System follows your OS settings.

{/* Character/Personality Section */}

Character & Personality

Current Personality

{selectedCharacter.systemPrompt.slice(0, 200)}...

{currentCharacter === 'custom' && (