+ {/* Voice Mode Enable/Disable */}
+
+
+
+
+
+ Voice Mode
+
+
+
+ Enable voice conversations with microphone input
+
+
+
+
+
+ {/* Voice provider selection - only show when voice mode is enabled */}
+ {voiceModeEnabled && (
+
+
+
+
+
+ Voice Provider
+
+
+
+ Choose your preferred text-to-speech provider
+
+
+
+
+
+ {isSayAvailable && (
+
+ )}
+
+
+ )}
+
+ {/* Provider description */}
+ {voiceModeEnabled && (
+
+
+
+ {voiceProvider === 'browser' ? 'Browser Voice:' : voiceProvider === 'openai' ? 'OpenAI:' : 'macOS Say:'}
+ {' '}
+ {voiceProvider === 'browser'
+ ? 'Free, works offline, but has limited mobile support. Best for desktop use.'
+ : voiceProvider === 'openai'
+ ? 'Higher quality voice synthesis that works reliably on mobile. Requires OpenAI API key.'
+ : 'Native macOS speech synthesis. Free, fast, and works offline. Desktop only.'}
+
+
+ )}
+
+ {/* OpenAI unavailable warning */}
+ {voiceModeEnabled && voiceProvider === 'openai' && !isOpenAIAvailable && (
+
+
+
+
+ OpenAI voice unavailable
+
+
+ OpenAI voice requires an OpenAI API key to be configured. Please set the OpenAI API key or switch to Browser voice.
+
+
+
+ )}
+
+ {/* OpenAI API Key Input - show when OpenAI is selected or when no server key is configured */}
+ {voiceModeEnabled && voiceProvider === 'openai' && (
+
+
+
+
+
+ OpenAI API Key
+
+
+
+ {isOpenAIAvailable && !openaiApiKey ? 'Using API key from OpenCode configuration' : 'Enter your OpenAI API key for voice synthesis'}
+
+
+
+ setOpenaiApiKey(e.target.value)}
+ placeholder="sk-..."
+ className="flex-1 px-3 py-2 text-sm bg-background border border-input rounded-md focus:outline-none focus:ring-2 focus:ring-ring"
+ />
+ {openaiApiKey && (
+
+ )}
+
+
+ )}
+
+ {/* OpenAI Voice Selection */}
+ {voiceModeEnabled && voiceProvider === 'openai' && isOpenAIAvailable && (
+
+
+
+
+
+ OpenAI Voice
+
+
+
+ Select an OpenAI voice for text-to-speech
+
+
+
+
+
+
+
+ )}
+
+ {/* macOS Say Voice Selection */}
+ {voiceModeEnabled && voiceProvider === 'say' && isSayAvailable && sayVoices.length > 0 && (
+
+
+
+
+
+ macOS Voice
+
+
+
+ Select a voice installed on your Mac
+
+
+
+
+
+
+
+ )}
+
+ {/* Browser Voice Selection */}
+ {voiceModeEnabled && voiceProvider === 'browser' && filteredBrowserVoices.length > 0 && (
+
+
+
+
+
+ Browser Voice
+
+
+
+ Select a voice from your browser
+
+
+
+
+
+
+
+ )}
+
+ {/* Language selection */}
+ {voiceModeEnabled && (
+
+
+
+
+
+ Language
+
+
+
+ Language for speech recognition and synthesis
+
+
+
+
+ )}
+
+ {/* Show TTS buttons on messages */}
+
+
+
+
+
+ Message Read Aloud
+
+
+
+ Show speaker button on AI responses to read them aloud
+
+
+
+
+
+ {/* Summarization Section */}
+
+
+
+
+ Summarization
+
+
+
+ {/* Summarize Message TTS */}
+
+
+
+ Summarize Message Playback
+
+
+ Summarize long messages before reading them aloud
+
+
+
+
+
+ {/* Summarize Voice Conversation */}
+ {voiceModeEnabled && (
+
+
+
+ Summarize Voice Responses
+
+
+ Summarize long AI responses during voice conversations
+
+
+
+
+ )}
+
+ {/* Character Threshold - only show if either summarization is enabled */}
+ {(summarizeMessageTTS || summarizeVoiceConversation) && (
+ <>
+
+
+
+ Character Threshold
+
+
+ Summarize text longer than this ({summarizeCharacterThreshold} chars)
+
+
+
+ `${v}`}
+ />
+
+
+
+
+
+
+ Summary Length Limit
+
+
+ Max characters for the summary ({summarizeMaxLength} chars)
+
+
+
+ `${v}`}
+ />
+
+
+ >
+ )}
+
+
+ {/* Speech Rate */}
+
+
+
+
+
+ Speech Rate
+
+
+
+ Speed of speech (0.5x - 2x)
+
+
+
+ `${v.toFixed(1)}x`}
+ />
+
+
+
+ {/* Speech Pitch */}
+
+
+
+
+
+ Speech Pitch
+
+
+
+ Voice pitch (0.5 - 2)
+
+
+
+
+
+
+
+ {/* Speech Volume */}
+
+
+
+
+
+ Speech Volume
+
+
+
+ Voice volume (0 - 100%)
+
+
+
+ `${Math.round(v * 100)}%`}
+ />
+
+
+
+ {/* Keyboard shortcut hint */}
+ {voiceModeEnabled && isSupported && (
+
+
+ Tip:{' '}
+ Press Shift +{' '}
+ Click{' '}
+ on the voice button to quickly toggle continuous mode
+
+
+ )}
+
+