Phase 2 complete.

This commit is contained in:
Aodhan Collins
2025-10-06 21:08:25 +01:00
parent 66749a5ce7
commit 8d6a681baa
26 changed files with 3163 additions and 164 deletions

View File

@@ -1,4 +1,4 @@
import { X, Key, Zap, Palette, User, Volume2, Moon, Sun, Monitor } from 'lucide-react'
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'
@@ -26,6 +26,8 @@ export function SettingsPanel({ onClose }: SettingsPanelProps) {
sttLanguage,
sttMode,
theme,
notificationsEnabled,
minimizeToTray,
setOpenRouterApiKey,
setElevenLabsApiKey,
setTemperature,
@@ -42,6 +44,8 @@ export function SettingsPanel({ onClose }: SettingsPanelProps) {
setSttLanguage,
setSttMode,
setTheme,
setNotificationsEnabled,
setMinimizeToTray,
} = useSettingsStore()
const [browserVoices, setBrowserVoices] = useState<SpeechSynthesisVoice[]>([])
@@ -56,29 +60,52 @@ export function SettingsPanel({ onClose }: SettingsPanelProps) {
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 = () => {
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)])
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()
window.speechSynthesis.addEventListener('voiceschanged', loadVoices)
try {
window.speechSynthesis.addEventListener('voiceschanged', loadVoices)
} catch (error) {
console.warn('⚠️ Failed to add voiceschanged listener:', error)
}
return () => {
window.speechSynthesis.removeEventListener('voiceschanged', loadVoices)
try {
if (window.speechSynthesis) {
window.speechSynthesis.removeEventListener('voiceschanged', loadVoices)
}
} catch (error) {
// Ignore cleanup errors
}
}
}, [])
@@ -695,6 +722,79 @@ export function SettingsPanel({ onClose }: SettingsPanelProps) {
</div>
</section>
{/* System Integration Section */}
<section className="mb-8">
<div className="flex items-center gap-2 mb-4">
<Bell className="w-5 h-5 text-green-500" />
<h3 className="text-lg font-semibold text-gray-800 dark:text-white">
System Integration
</h3>
</div>
<div className="space-y-4">
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div className="flex-1">
<div className="flex items-center gap-2">
<Bell className="w-4 h-4 text-green-500" />
<label className="font-medium text-gray-800 dark:text-white">
Desktop Notifications
</label>
</div>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
Get notified when EVE responds (even when window is hidden)
</p>
</div>
<input
type="checkbox"
checked={notificationsEnabled}
onChange={(e) => setNotificationsEnabled(e.target.checked)}
className="w-5 h-5 text-green-500 rounded focus:ring-2 focus:ring-green-500"
/>
</div>
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg opacity-50">
<div className="flex-1">
<div className="flex items-center gap-2">
<Minimize2 className="w-4 h-4 text-gray-500" />
<label className="font-medium text-gray-800 dark:text-white">
Minimize to Tray
</label>
<span className="text-xs bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400 px-2 py-0.5 rounded">
Linux: Coming Soon
</span>
</div>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
System tray temporarily unavailable on Linux (icon format issue)
</p>
</div>
<input
type="checkbox"
checked={minimizeToTray}
onChange={(e) => setMinimizeToTray(e.target.checked)}
disabled
className="w-5 h-5 text-gray-400 rounded cursor-not-allowed"
/>
</div>
<div className="p-3 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border border-yellow-200 dark:border-yellow-800">
<p className="text-sm text-gray-700 dark:text-gray-300">
<strong className="text-yellow-700 dark:text-yellow-400"> Global Shortcut:</strong> Press{' '}
<kbd className="px-2 py-1 bg-gray-200 dark:bg-gray-700 rounded text-xs font-mono">
Ctrl+Shift+E
</kbd>{' '}
(or{' '}
<kbd className="px-2 py-1 bg-gray-200 dark:bg-gray-700 rounded text-xs font-mono">
Cmd+Shift+E
</kbd>{' '}
on Mac) to quickly show/hide EVE.
<br />
<span className="text-xs text-yellow-600 dark:text-yellow-400 mt-1 inline-block">
Note: May not work on some Linux desktop environments due to permission restrictions.
</span>
</p>
</div>
</div>
</section>
{/* Info Section */}
<section className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4">
<div className="flex items-start gap-2">