/** * Settings Modal Component * * System settings modal for HomePilot Voice UI. * Contains advanced audio settings and content preferences. * * Features: * - Audio meter toggle * - Browser voice selection * - Language settings * - Adult content gating (18+) */ import React, { useState, useEffect } from 'react'; import { X, Activity, Volume2, Globe, Settings, Shield, AlertTriangle, Users, Link2, } from 'lucide-react'; import { isAdultContentEnabled, isAgeConfirmed, setAdultContentEnabled, setAgeConfirmed, isPersonasEnabled, setPersonasEnabled, isVoiceLinkedToProject, setVoiceLinkedToProject, LS_PERSONA_CACHE, } from './personalityGating'; import TtsEngineSection from '../components/TtsEngineSection'; // Side-effect import: ensures the TTS plugin registry is populated the // first time the System Settings modal mounts — same registry the // Enterprise Settings voice block already uses, so both UIs are in // sync automatically (single active-engine pointer in localStorage, // scoped per user). import '../tts'; import { getActiveTtsEngineId, onActiveTtsEngineChange, } from '../tts'; export default function SettingsModal({ isOpen, onClose, showAudioMeter, setShowAudioMeter, browserVoices, selectedBrowserVoice, setSelectedBrowserVoice, }) { // Adult content state const [adultEnabled, setAdultEnabled] = useState(false); const [ageConfirmed, setAgeConfirmedState] = useState(false); const [showAgeConfirmation, setShowAgeConfirmation] = useState(false); // Personas in Voice state const [personasEnabled, setPersonasEnabledState] = useState(false); // Linked-to-project state const [linkedToProject, setLinkedToProjectState] = useState(false); // Track active TTS engine so we can hide the legacy "System Voice" // dropdown when the user picks a non-default engine (Piper etc.) — // prevents the confusing two-voice-pickers state. const [ttsEngineId, setTtsEngineId] = useState(() => getActiveTtsEngineId()); useEffect(() => onActiveTtsEngineChange(setTtsEngineId), []); // Load settings on mount useEffect(() => { setAdultEnabled(isAdultContentEnabled()); setAgeConfirmedState(isAgeConfirmed()); setPersonasEnabledState(isPersonasEnabled()); setLinkedToProjectState(isVoiceLinkedToProject()); }, [isOpen]); // Handle personas toggle const handlePersonasToggle = () => { const next = !personasEnabled; setPersonasEnabled(next); setPersonasEnabledState(next); if (!next) { // Clear persona cache, unlink, and reset personality if it was a persona localStorage.removeItem(LS_PERSONA_CACHE); const pid = localStorage.getItem('homepilot_personality_id'); if (pid?.startsWith('persona:')) { localStorage.removeItem('homepilot_personality_id'); } setVoiceLinkedToProject(false); setLinkedToProjectState(false); } }; // Handle linked-to-project toggle const handleLinkedToggle = () => { const next = !linkedToProject; setVoiceLinkedToProject(next); setLinkedToProjectState(next); }; // Handle adult content toggle const handleAdultToggle = () => { if (!adultEnabled) { // Trying to enable - check age confirmation if (ageConfirmed) { setAdultContentEnabled(true); setAdultEnabled(true); } else { // Show age confirmation dialog setShowAgeConfirmation(true); } } else { // Disabling setAdultContentEnabled(false); setAdultEnabled(false); } }; // Handle age confirmation const handleAgeConfirm = () => { setAgeConfirmed(true); setAgeConfirmedState(true); setAdultContentEnabled(true); setAdultEnabled(true); setShowAgeConfirmation(false); }; if (!isOpen) return null; const toggleOn = 'bg-white/15 border border-white/20'; const toggleOff = 'bg-[#1F1F1F] border border-transparent'; // Filter English voices for cleaner display const englishVoices = browserVoices?.filter((v) => v.lang.startsWith('en')) || []; // Group voices by language const voicesByLang = browserVoices?.reduce((acc, voice) => { const lang = voice.lang.split('-')[0]; if (!acc[lang]) acc[lang] = []; acc[lang].push(voice); return acc; }, {}) || {}; return (
{/* Header */}

System Settings

HomePilot Voice Configuration

{/* Content */}
{/* Audio Settings Section */}
Audio Settings
{/* Audio Level Meter Toggle */} {setShowAudioMeter && (
Show Audio Meter Display real-time audio level in hands-free mode
)} {/* Browser Voice Selection — only shown for the default engine. On Piper (or any other non-default engine) the engine owns its own voice picker below. */} {ttsEngineId === 'web-speech-api' && browserVoices && browserVoices.length > 0 && setSelectedBrowserVoice && (
System Voice

{browserVoices.length} system voices available

)} {/* TTS Engine — additive plugin picker. Shares the SAME registry used by Enterprise Settings → Voice Assistant, so picking Piper here is reflected there on next render (and vice versa). Uses the native browser voice list so the Web Speech path keeps showing the user's actually-installed voices. */}
{/* Language Section */}
Language & Recognition

Voice recognition uses your browser's default language settings. To change the recognition language, update your browser's language preferences.

{/* Content Preferences Section */}
Content Preferences
{/* Adult Content Toggle */}
Enable 18+ Personalities Unlock adult-only conversation modes
{adultEnabled && (

Adult personalities are now visible in the personality selector. These modes may contain mature themes, strong language, and explicit content.

)} {/* Personas in Voice Toggle */}
Enable Personas Use custom Personas as voice identities
{personasEnabled && (

Your Personas from Settings & Projects are now available in the Personality selector.

)} {/* Link to Project Toggle — only visible when personas are enabled */} {personasEnabled && (<>
Link to Project Enable memory, RAG & tools for persona sessions
{linkedToProject && (

Linked mode: Voice sessions use the persona's full project context — memory, documents, and tools persist across sessions.

)} {!linkedToProject && (

Unlinked: Voice is fast & ephemeral — no conversation history saved.

)} )}
{/* Age Confirmation Modal */} {showAgeConfirmation && (

Age Verification

Adult content requires confirmation

You are about to enable access to adult-only personalities that may contain:

  • • Mature themes and conversations
  • • Strong language and profanity
  • • Sexual or romantic content
  • • Dark humor and edgy content

By continuing, you confirm that you are 18 years or older and consent to accessing this content.

)} {/* Info Section */}

Studio Quality Audio

HomePilot Voice uses adaptive noise suppression, echo cancellation, and automatic gain control for crystal-clear audio capture.

{/* Footer */}
); }