feat: Initialize project structure
Sets up the basic project configuration for Cognitive Clarity. This includes: - Initializing `package.json` with necessary dependencies (React, Gemini API, Lucide React) and build scripts for Vite. - Creating a basic `index.html` with Tailwind CSS and Google Fonts integration. - Setting up `index.tsx` as the React application entry point. - Configuring `tsconfig.json` for TypeScript compilation with Vite. - Establishing `vite.config.ts` for development server setup and environment variable handling. - Adding a standard `.gitignore` file. - Updating `README.md` with local run instructions and AI Studio links. - Defining `metadata.json` for application details. - Creating core type definitions in `types.ts`. - Populating `constants.ts` with initial CBT glossary terms. - Adding utility functions for audio processing in `services/audioUtils.ts`.
This commit is contained in:
1 parent
a6780ce1b2
commit
c78604de02
22 files changed
+2097
-8
No files matched your search
@@ -0,0 +1,250 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { SCENARIOS } from './constants';
|
||||
import { Scenario, SessionMode } 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';
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [activeScenario, setActiveScenario] = useState<Scenario | null>(null);
|
||||
const [sessionMode, setSessionMode] = useState<SessionMode>('text');
|
||||
const [selectedDifficulty, setSelectedDifficulty] = useState<string>('All');
|
||||
const [isPreparing, setIsPreparing] = useState(false);
|
||||
|
||||
// Custom Scenarios State
|
||||
const [customScenarios, setCustomScenarios] = useState<Scenario[]>([]);
|
||||
const [showGenerator, setShowGenerator] = useState(false);
|
||||
const [showReference, setShowReference] = useState(false);
|
||||
|
||||
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);
|
||||
setIsPreparing(false);
|
||||
}, 1400);
|
||||
};
|
||||
|
||||
const handleExitSession = () => {
|
||||
setActiveScenario(null);
|
||||
};
|
||||
|
||||
const handleScenarioGenerated = (newScenario: Scenario) => {
|
||||
setCustomScenarios(prev => [newScenario, ...prev]);
|
||||
setShowGenerator(false);
|
||||
};
|
||||
|
||||
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">
|
||||
<div className="absolute inset-0 overflow-hidden pointer-events-none opacity-20">
|
||||
<div className="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-teal-500 rounded-full blur-[120px] animate-pulse"></div>
|
||||
<div className="absolute bottom-[-10%] right-[-10%] w-[40%] h-[40%] bg-blue-500 rounded-full blur-[120px] animate-pulse" style={{ animationDelay: '1s' }}></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 flex flex-col items-center max-w-md text-center">
|
||||
<div className="w-24 h-24 bg-white/5 rounded-3xl border border-white/10 flex items-center justify-center mb-8 relative">
|
||||
<Brain size={48} className="text-teal-400" />
|
||||
<div className="absolute inset-0 border-2 border-teal-500/30 rounded-3xl animate-ping opacity-20"></div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-3 tracking-tight">Initializing Session</h2>
|
||||
<div className="flex items-center gap-2 mb-8">
|
||||
<span className="h-1.5 w-1.5 bg-teal-400 rounded-full animate-bounce" style={{ animationDelay: '0ms' }}></span>
|
||||
<span className="h-1.5 w-1.5 bg-teal-400 rounded-full animate-bounce" style={{ animationDelay: '150ms' }}></span>
|
||||
<span className="h-1.5 w-1.5 bg-teal-400 rounded-full animate-bounce" style={{ animationDelay: '300ms' }}></span>
|
||||
<p className="text-xs font-bold text-slate-400 uppercase tracking-widest ml-2">Calibrating AI Parameters</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full bg-white/5 h-1.5 rounded-full overflow-hidden mb-4 border border-white/5">
|
||||
<div className="h-full bg-teal-500 rounded-full w-0 animate-[loading_1.4s_ease-in-out_forwards]"></div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 opacity-60">
|
||||
<p className="text-[10px] uppercase font-black tracking-tighter flex items-center justify-center gap-2">
|
||||
<Zap size={10} className="text-teal-400" />
|
||||
Configuring {sessionMode === 'voice' ? 'Voice Link' : 'Neural Chat Bridge'}
|
||||
</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
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@keyframes loading {
|
||||
0% { width: 0%; }
|
||||
20% { width: 15%; }
|
||||
50% { width: 70%; }
|
||||
100% { width: 100%; }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (activeScenario) {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 flex flex-col items-center pt-8 px-4 pb-8 animate-in fade-in duration-700">
|
||||
{sessionMode === 'text' ? (
|
||||
<ChatSession scenario={activeScenario} onExit={handleExitSession} />
|
||||
) : (
|
||||
<div className="w-full max-w-4xl h-[80vh]">
|
||||
<VoiceSession scenario={activeScenario} onExit={handleExitSession} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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)} />
|
||||
)}
|
||||
|
||||
{/* Hero Section */}
|
||||
<div className="bg-white border-b border-slate-200">
|
||||
<div className="max-w-5xl mx-auto px-6 py-12">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6 mb-8">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="bg-teal-600 p-2 rounded-lg text-white shadow-sm shadow-teal-200">
|
||||
<Brain size={32} />
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<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.
|
||||
</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>
|
||||
</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 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} />
|
||||
<span className="hidden sm:inline">Difficulty</span>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
{difficulties.map((diff) => (
|
||||
<button
|
||||
key={diff}
|
||||
onClick={() => setSelectedDifficulty(diff)}
|
||||
className={`px-3 py-1.5 text-xs rounded-md transition-all whitespace-nowrap ${
|
||||
selectedDifficulty === diff
|
||||
? 'bg-teal-50 text-teal-700 font-semibold border border-teal-100'
|
||||
: 'text-slate-500 hover:bg-slate-50 border border-transparent'
|
||||
}`}
|
||||
>
|
||||
{diff}
|
||||
</button>
|
||||
))}
|
||||
</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>
|
||||
</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}
|
||||
/>
|
||||
))}
|
||||
|
||||
<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="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>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
Reference in new issue
Block a user