SQLite + sqlite-vec replaces JSON memory files with semantic search, follow-up injection, privacy levels, and lifecycle management. Six prompt styles (quick/standard/creative/roleplayer/game-master/storyteller) with per-style Claude model tiering (Haiku/Sonnet/Opus), temperature control, and section stripping. Characters can set default style and per-style overrides. Dream character import and GAZE character linking in the dashboard editor with auto-populated fields, cover image resolution, and preset assignment. Bridge: session isolation (conversation_id / 12h satellite buckets), model routing refactor, PUT/DELETE support, memory REST endpoints. Dashboard: mobile-responsive sidebar, retry button, style picker in chat, follow-up banner, memory lifecycle/privacy UI, cloud model options in editor. Wyoming TTS: upgraded to v1.8.0 for HA 1.7.2 compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
107 lines
4.6 KiB
JavaScript
107 lines
4.6 KiB
JavaScript
import { VOICES, TTS_ENGINES } from '../lib/constants'
|
|
|
|
export default function SettingsDrawer({ isOpen, onClose, settings, onUpdate }) {
|
|
if (!isOpen) return null
|
|
|
|
const isKokoro = !settings.ttsEngine || settings.ttsEngine === 'kokoro'
|
|
|
|
return (
|
|
<>
|
|
<div className="fixed inset-0 bg-black/50 z-40" onClick={onClose} />
|
|
<div className="fixed right-0 top-0 bottom-0 w-full sm:w-80 bg-gray-900 border-l border-gray-800 z-50 flex flex-col">
|
|
<div className="flex items-center justify-between px-4 py-3 border-b border-gray-800">
|
|
<h2 className="text-sm font-medium text-gray-200">Settings</h2>
|
|
<button onClick={onClose} className="text-gray-500 hover:text-gray-300">
|
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div className="flex-1 overflow-y-auto p-4 space-y-5">
|
|
{/* TTS Engine */}
|
|
<div>
|
|
<label className="block text-xs font-medium text-gray-400 mb-1.5">TTS Engine</label>
|
|
<select
|
|
value={settings.ttsEngine || 'kokoro'}
|
|
onChange={(e) => onUpdate('ttsEngine', e.target.value)}
|
|
className="w-full bg-gray-800 text-gray-200 text-sm rounded-lg px-3 py-2 border border-gray-700 focus:outline-none focus:border-indigo-500"
|
|
>
|
|
{TTS_ENGINES.map((e) => (
|
|
<option key={e.id} value={e.id}>{e.label}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* Voice */}
|
|
<div>
|
|
<label className="block text-xs font-medium text-gray-400 mb-1.5">Voice</label>
|
|
{isKokoro ? (
|
|
<select
|
|
value={settings.voice}
|
|
onChange={(e) => onUpdate('voice', e.target.value)}
|
|
className="w-full bg-gray-800 text-gray-200 text-sm rounded-lg px-3 py-2 border border-gray-700 focus:outline-none focus:border-indigo-500"
|
|
>
|
|
{VOICES.map((v) => (
|
|
<option key={v.id} value={v.id}>{v.label}</option>
|
|
))}
|
|
</select>
|
|
) : (
|
|
<div>
|
|
<input
|
|
type="text"
|
|
value={settings.voice || ''}
|
|
onChange={(e) => onUpdate('voice', e.target.value)}
|
|
className="w-full bg-gray-800 text-gray-200 text-sm rounded-lg px-3 py-2 border border-gray-700 focus:outline-none focus:border-indigo-500"
|
|
placeholder={settings.ttsEngine === 'elevenlabs' ? 'ElevenLabs voice ID' : 'Voice identifier'}
|
|
readOnly
|
|
/>
|
|
<p className="text-xs text-gray-500 mt-1">
|
|
Set via active character profile
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Auto TTS */}
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<div className="text-sm text-gray-200">Auto-speak responses</div>
|
|
<div className="text-xs text-gray-500">Speak assistant replies aloud</div>
|
|
</div>
|
|
<button
|
|
onClick={() => onUpdate('autoTts', !settings.autoTts)}
|
|
className={`relative w-10 h-6 rounded-full transition-colors ${
|
|
settings.autoTts ? 'bg-indigo-600' : 'bg-gray-700'
|
|
}`}
|
|
>
|
|
<span
|
|
className={`absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white transition-transform ${
|
|
settings.autoTts ? 'translate-x-4' : ''
|
|
}`}
|
|
/>
|
|
</button>
|
|
</div>
|
|
|
|
{/* STT Mode */}
|
|
<div>
|
|
<label className="block text-xs font-medium text-gray-400 mb-1.5">Speech recognition</label>
|
|
<select
|
|
value={settings.sttMode}
|
|
onChange={(e) => onUpdate('sttMode', e.target.value)}
|
|
className="w-full bg-gray-800 text-gray-200 text-sm rounded-lg px-3 py-2 border border-gray-700 focus:outline-none focus:border-indigo-500"
|
|
>
|
|
<option value="bridge">Wyoming STT (local)</option>
|
|
<option value="webspeech">Web Speech API (browser)</option>
|
|
</select>
|
|
<p className="text-xs text-gray-500 mt-1">
|
|
{settings.sttMode === 'bridge'
|
|
? 'Uses Whisper via the local bridge server'
|
|
: 'Uses browser built-in speech recognition'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|