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.
This commit is contained in:
John Remillard committed 2025-12-17 12:14:29 -05:00
1 parent c78604de02
commit a7a8c661b7
9 files changed
+670 -360

No files matched your search

+77 -65
View File
@@ -1,13 +1,21 @@
import React, { useState, useEffect } from 'react';
import { SCENARIOS } from './constants';
import { Scenario, SessionMode } from './types';
import { Scenario, SessionMode, UserSettings } from './types';
import ScenarioCard from './components/ScenarioCard';
import ChatSession from './components/ChatSession';
import VoiceSession from './components/VoiceSession';
import ScenarioGenerator from './components/ScenarioGenerator';
import CBTReference from './components/CBTReference';
import { Brain, Info, ShieldCheck, Filter, Plus, Sparkles, BookOpen, Loader2, Activity, Zap } from 'lucide-react';
import SettingsModal from './components/SettingsModal';
import Tooltip from './components/Tooltip';
import { Brain, Info, ShieldCheck, Filter, Plus, BookOpen, Activity, Zap, Settings } from 'lucide-react';
const DEFAULT_SETTINGS: UserSettings = {
voiceName: 'Zephyr',
vocalStyle: 'Helpful',
genderPreference: 'Neutral'
};
const App: React.FC = () => {
const [activeScenario, setActiveScenario] = useState<Scenario | null>(null);
@@ -19,11 +27,20 @@ const App: React.FC = () => {
const [customScenarios, setCustomScenarios] = useState<Scenario[]>([]);
const [showGenerator, setShowGenerator] = useState(false);
const [showReference, setShowReference] = useState(false);
const [showSettings, setShowSettings] = useState(false);
// User Settings State
const [userSettings, setUserSettings] = useState<UserSettings>(() => {
const saved = localStorage.getItem('cognitive-clarity-settings');
return saved ? JSON.parse(saved) : DEFAULT_SETTINGS;
});
useEffect(() => {
localStorage.setItem('cognitive-clarity-settings', JSON.stringify(userSettings));
}, [userSettings]);
const handleSelectScenario = (scenario: Scenario, mode: SessionMode) => {
setIsPreparing(true);
// Simulate "Clinical Environment Calibration"
// This provides a clearer boundary between dashboard and the specialized session environment
setTimeout(() => {
setActiveScenario(scenario);
setSessionMode(mode);
@@ -41,14 +58,12 @@ const App: React.FC = () => {
};
const allScenarios = [...customScenarios, ...SCENARIOS];
const filteredScenarios = allScenarios.filter(scenario =>
selectedDifficulty === 'All' || scenario.difficulty === selectedDifficulty
);
const difficulties = ['All', 'Beginner', 'Intermediate', 'Advanced'];
// Robust Session Preparation UI
if (isPreparing) {
return (
<div className="min-h-screen bg-slate-900 flex flex-col items-center justify-center p-6 text-white overflow-hidden relative">
@@ -82,7 +97,7 @@ const App: React.FC = () => {
</p>
<p className="text-[10px] uppercase font-black tracking-tighter flex items-center justify-center gap-2">
<Activity size={10} className="text-blue-400" />
Injecting {activeScenario?.cbtFocus[0] || 'Clinical'} Logic
Applying {userSettings.voiceName} Vocal Profile
</p>
</div>
</div>
@@ -106,7 +121,11 @@ const App: React.FC = () => {
<ChatSession scenario={activeScenario} onExit={handleExitSession} />
) : (
<div className="w-full max-w-4xl h-[80vh]">
<VoiceSession scenario={activeScenario} onExit={handleExitSession} />
<VoiceSession
scenario={activeScenario}
onExit={handleExitSession}
voiceSettings={userSettings}
/>
</div>
)}
</div>
@@ -115,18 +134,10 @@ const App: React.FC = () => {
return (
<div className="min-h-screen bg-[#FDFDFF] text-slate-900 font-sans relative animate-in fade-in duration-500">
{/* Generator Modal */}
{showGenerator && (
<ScenarioGenerator
onGenerate={handleScenarioGenerated}
onCancel={() => setShowGenerator(false)}
/>
)}
{/* Reference Modal */}
{showReference && (
<CBTReference onClose={() => setShowReference(false)} />
)}
{/* Modals */}
{showGenerator && <ScenarioGenerator onGenerate={handleScenarioGenerated} onCancel={() => setShowGenerator(false)} />}
{showReference && <CBTReference onClose={() => setShowReference(false)} />}
{showSettings && <SettingsModal settings={userSettings} onUpdate={setUserSettings} onClose={() => setShowSettings(false)} />}
{/* Hero Section */}
<div className="bg-white border-b border-slate-200">
@@ -139,41 +150,53 @@ const App: React.FC = () => {
<h1 className="text-3xl font-bold tracking-tight text-slate-900">Cognitive Clarity</h1>
</div>
<button
onClick={() => setShowReference(true)}
className="flex items-center justify-center gap-2 bg-slate-50 text-slate-600 hover:text-teal-700 hover:bg-teal-50 px-5 py-2.5 rounded-xl border border-slate-200 transition-all font-bold text-sm"
>
<BookOpen size={18} />
<span>Reference Guide</span>
</button>
<div className="flex items-center gap-2">
<Tooltip content="Adjust voice settings for audio sessions.">
<button
onClick={() => setShowSettings(true)}
className="p-2.5 rounded-xl bg-slate-50 border border-slate-200 text-slate-400 hover:text-slate-800 transition-all"
>
<Settings size={20} />
</button>
</Tooltip>
<Tooltip content="Explore the glossary of CBT distortions, techniques, and concepts.">
<button
onClick={() => setShowReference(true)}
className="flex items-center justify-center gap-2 bg-slate-50 text-slate-600 hover:text-teal-700 hover:bg-teal-50 px-5 py-2.5 rounded-xl border border-slate-200 transition-all font-bold text-sm"
>
<BookOpen size={18} />
<span>Reference Guide</span>
</button>
</Tooltip>
</div>
</div>
<p className="text-xl text-slate-600 max-w-2xl leading-relaxed font-light">
A clinical roleplaying platform utilizing CBT principles and AI to help you navigate social complexities and restructure internal dialogues.
An adaptive clinical training environment designed to strengthen cognitive flexibility and navigate social friction.
</p>
<div className="flex flex-wrap gap-4 mt-8">
<div className="flex items-center gap-2 text-sm text-slate-500 bg-slate-50 px-3 py-1.5 rounded-full border border-slate-100">
<ShieldCheck className="text-teal-600" size={18} />
<span>Evidence-Based Framework</span>
</div>
<div className="flex items-center gap-2 text-sm text-slate-500 bg-slate-50 px-3 py-1.5 rounded-full border border-slate-100">
<Info className="text-teal-600" size={18} />
<span>Personalized Adaptive Logic</span>
</div>
<Tooltip content="Our scenarios are built on the foundations of Cognitive Behavioral Therapy.">
<div className="flex items-center gap-2 text-sm text-slate-500 bg-slate-50 px-3 py-1.5 rounded-full border border-slate-100 cursor-help">
<ShieldCheck className="text-teal-600" size={18} />
<span>Evidence-Based Framework</span>
</div>
</Tooltip>
<Tooltip content="The AI character responds dynamically to your level of cognitive restructuring.">
<div className="flex items-center gap-2 text-sm text-slate-500 bg-slate-50 px-3 py-1.5 rounded-full border border-slate-100 cursor-help">
<Info className="text-teal-600" size={18} />
<span>Vocal Partner: {userSettings.voiceName}</span>
</div>
</Tooltip>
</div>
</div>
</div>
{/* Main Content */}
<main className="max-w-5xl mx-auto px-6 py-12">
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-8">
<h2 className="text-2xl font-medium text-slate-800">
Training Scenarios
</h2>
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-10">
<h2 className="text-2xl font-medium text-slate-800">Training Scenarios</h2>
<div className="flex flex-col sm:flex-row gap-3 w-full sm:w-auto">
{/* Filter Controls */}
<div className="flex items-center bg-white border border-slate-200 rounded-lg p-1 shadow-sm overflow-x-auto">
<div className="px-3 text-slate-400 flex items-center gap-2 text-sm font-medium border-r border-slate-100 mr-1">
<Filter size={14} />
@@ -196,32 +219,27 @@ const App: React.FC = () => {
</div>
</div>
<button
onClick={() => setShowGenerator(true)}
className="flex items-center justify-center gap-2 bg-teal-600 text-white px-5 py-2.5 rounded-lg hover:bg-teal-700 transition-all font-bold text-sm shadow-md shadow-teal-100"
>
<Plus size={18} />
<span>Create Custom</span>
</button>
<Tooltip content="Generate a unique roleplay situation based on your specific needs using AI." position="bottom">
<button
onClick={() => setShowGenerator(true)}
className="flex items-center justify-center gap-2 bg-teal-600 text-white px-5 py-2.5 rounded-lg hover:bg-teal-700 transition-all font-bold text-sm shadow-md shadow-teal-100"
>
<Plus size={18} />
<span>Create Custom</span>
</button>
</Tooltip>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredScenarios.map(scenario => (
<ScenarioCard
key={scenario.id}
scenario={scenario}
onSelect={handleSelectScenario}
/>
<ScenarioCard key={scenario.id} scenario={scenario} onSelect={handleSelectScenario} />
))}
<button
onClick={() => setShowGenerator(true)}
className="flex flex-col items-center justify-center gap-4 p-6 rounded-xl border-2 border-dashed border-slate-200 text-slate-400 hover:border-teal-300 hover:text-teal-600 hover:bg-teal-50/50 transition-all h-full min-h-[250px] group"
>
<div className="bg-slate-50 p-4 rounded-full group-hover:bg-white transition-colors">
<Plus size={32} />
</div>
<div className="bg-slate-50 p-4 rounded-full group-hover:bg-white transition-colors"><Plus size={32} /></div>
<div className="text-center">
<span className="block font-bold text-sm uppercase tracking-widest mb-1">Custom Scenario</span>
<span className="text-xs font-light">Generate a specific roleplay session</span>
@@ -233,13 +251,7 @@ const App: React.FC = () => {
<footer className="bg-white border-t border-slate-200 py-12">
<div className="max-w-5xl mx-auto px-6 flex flex-col md:flex-row justify-between items-center gap-8">
<div className="flex items-center gap-2 text-slate-400">
<Brain size={20} />
<span className="text-sm font-bold uppercase tracking-widest">Cognitive Clarity v1.0</span>
</div>
<div className="flex gap-8 text-xs font-bold text-slate-400 uppercase tracking-widest">
<a href="#" className="hover:text-teal-600 transition-colors">Privacy Protocol</a>
<a href="#" className="hover:text-teal-600 transition-colors">Clinical Terms</a>
<a href="#" className="hover:text-teal-600 transition-colors">Support</a>
<Brain size={20} /><span className="text-sm font-bold uppercase tracking-widest">Cognitive Clarity v1.2</span>
</div>
</div>
</footer>