Files
clarity-helper/components/SettingsModal.tsx
T
John Remillard a7a8c661b7 feat: Add user settings for voice and persona
Introduces a new `UserSettings` interface and default settings. This allows users to customize voice preferences (name, style, gender) and persona characteristics within the application. This enhances personalization and user experience by tailoring interactions to individual needs and preferences.
2025-12-17 12:14:29 -05:00

121 lines
5.7 KiB
TypeScript

import React from 'react';
import { UserSettings } from '../types';
import { X, Volume2, Check, Info, Mic2 } from 'lucide-react';
import Tooltip from './Tooltip';
interface SettingsModalProps {
settings: UserSettings;
onUpdate: (settings: UserSettings) => void;
onClose: () => void;
}
interface VoiceOption {
id: UserSettings['voiceName'];
label: string;
gender: 'Male' | 'Female' | 'Neutral';
style: string;
description: string;
}
const VOICE_OPTIONS: VoiceOption[] = [
{ id: 'Zephyr', label: 'Zephyr', gender: 'Neutral', style: 'Helpful', description: 'A balanced, clear, and professional tone suitable for most scenarios.' },
{ id: 'Puck', label: 'Puck', gender: 'Male', style: 'Youthful', description: 'Energetic and friendly, ideal for peer-to-peer or informal interactions.' },
{ id: 'Kore', label: 'Kore', gender: 'Female', style: 'Calm', description: 'Soothing and professional, perfect for clinical or high-stress roleplay.' },
{ id: 'Charon', label: 'Charon', gender: 'Male', style: 'Authoritative', description: 'Deep and commanding, useful for workplace or boundary-testing scenarios.' },
{ id: 'Fenrir', label: 'Fenrir', gender: 'Male', style: 'Rugged', description: 'Direct and matter-of-fact tone with a slightly more serious edge.' },
];
const SettingsModal: React.FC<SettingsModalProps> = ({ settings, onUpdate, onClose }) => {
return (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-md">
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-xl overflow-hidden animate-in fade-in zoom-in duration-300 border border-slate-100">
<div className="p-6 bg-slate-50 border-b border-slate-200 flex justify-between items-center">
<div className="flex items-center gap-3">
<div className="bg-slate-800 p-2 rounded-xl text-white">
<Volume2 size={20} />
</div>
<div>
<h2 className="text-lg font-bold text-slate-800">Voice & Persona Settings</h2>
<p className="text-[10px] text-slate-400 font-bold uppercase tracking-widest">Configure your AI clinical partner</p>
</div>
</div>
<button onClick={onClose} className="p-2 hover:bg-slate-200 rounded-xl transition-colors text-slate-400">
<X size={20} />
</button>
</div>
<div className="p-8 space-y-8">
<section className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-xs font-black text-slate-400 uppercase tracking-widest flex items-center gap-2">
<Mic2 size={14} className="text-teal-600" />
Vocal Profile
</h3>
<Tooltip content="Different voices can help simulate specific social or professional contexts.">
<Info size={14} className="text-slate-300" />
</Tooltip>
</div>
<div className="grid grid-cols-1 gap-3">
{VOICE_OPTIONS.map((voice) => (
<button
key={voice.id}
onClick={() => onUpdate({ ...settings, voiceName: voice.id, genderPreference: voice.gender, vocalStyle: voice.style })}
className={`flex items-center justify-between p-4 rounded-2xl border-2 transition-all text-left ${
settings.voiceName === voice.id
? 'bg-teal-50 border-teal-500 shadow-md shadow-teal-100'
: 'bg-white border-slate-100 hover:border-slate-200'
}`}
>
<div className="flex items-center gap-4">
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${
settings.voiceName === voice.id ? 'bg-teal-600 text-white' : 'bg-slate-100 text-slate-400'
}`}>
<Volume2 size={18} />
</div>
<div>
<div className="flex items-center gap-2">
<span className="font-bold text-slate-800">{voice.label}</span>
<span className="text-[9px] font-black uppercase text-slate-400 bg-slate-100 px-1.5 py-0.5 rounded">
{voice.gender} • {voice.style}
</span>
</div>
<p className="text-xs text-slate-500 font-light mt-0.5">{voice.description}</p>
</div>
</div>
{settings.voiceName === voice.id && (
<div className="bg-teal-600 text-white p-1 rounded-full">
<Check size={14} />
</div>
)}
</button>
))}
</div>
</section>
<div className="bg-amber-50 border border-amber-100 rounded-2xl p-4 flex gap-3">
<div className="text-amber-600 mt-0.5">
<Info size={18} />
</div>
<p className="text-xs text-amber-800 leading-relaxed">
These settings apply to <strong>Voice Mode</strong> interactions only. Text-based roleplay personas are dynamically generated based on each specific scenario.
</p>
</div>
</div>
<div className="p-6 bg-slate-50 border-t border-slate-200 flex justify-end">
<button
onClick={onClose}
className="bg-slate-800 text-white px-8 py-3 rounded-2xl font-bold hover:bg-slate-900 shadow-lg shadow-slate-200 transition-all uppercase tracking-widest text-[10px]"
>
Save Preferences
</button>
</div>
</div>
</div>
);
};
export default SettingsModal;