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:
1 parent
c78604de02
commit
a7a8c661b7
9 files changed
+670
-360
No files matched your search
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user