feat: Add character names to scenarios and UI

Introduces `characterName` to the `Scenario` type, enabling AI personas to be explicitly named. This change also updates the `ScenarioCard` component to display the character name and adds foundational support for potential future scenario deletion functionality. The README has been updated with a new AI Studio link.
This commit is contained in:
John Remillard committed 2026-03-04 11:43:39 -05:00
1 parent e92c489db5
commit a4946ed04d
18 files changed
+3585 -320

No files matched your search

+39 -10
View File
@@ -1,8 +1,9 @@
import React, { useState } from 'react';
import { GoogleGenAI, Type } from "@google/genai";
import { Scenario } from '../types';
import { CBT_GLOSSARY } from '../constants';
import { Sparkles, X, Loader2, AlertCircle, Info, Target, UserCircle2, BrainCircuit, Check } from 'lucide-react';
import { Sparkles, X, Loader2, AlertCircle, Info, Target, UserCircle2, BrainCircuit, Check, BarChart } from 'lucide-react';
interface ScenarioGeneratorProps {
onGenerate: (scenario: Scenario) => void;
@@ -12,6 +13,7 @@ interface ScenarioGeneratorProps {
const ScenarioGenerator: React.FC<ScenarioGeneratorProps> = ({ onGenerate, onCancel }) => {
const [prompt, setPrompt] = useState('');
const [selectedConcepts, setSelectedConcepts] = useState<string[]>([]);
const [selectedDifficulty, setSelectedDifficulty] = useState<Scenario['difficulty']>('Intermediate');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -35,6 +37,7 @@ const ScenarioGenerator: React.FC<ScenarioGeneratorProps> = ({ onGenerate, onCan
properties: {
id: { type: Type.STRING },
title: { type: Type.STRING },
characterName: { type: Type.STRING },
description: { type: Type.STRING },
category: {
type: Type.STRING,
@@ -51,7 +54,7 @@ const ScenarioGenerator: React.FC<ScenarioGeneratorProps> = ({ onGenerate, onCan
items: { type: Type.STRING }
}
},
required: ['id', 'title', 'description', 'category', 'difficulty', 'systemInstruction', 'initialMessage', 'cbtFocus']
required: ['id', 'title', 'characterName', 'description', 'category', 'difficulty', 'systemInstruction', 'initialMessage', 'cbtFocus']
};
const selectedContext = selectedConcepts.length > 0
@@ -69,17 +72,18 @@ const ScenarioGenerator: React.FC<ScenarioGeneratorProps> = ({ onGenerate, onCan
CORE INSTRUCTIONS:
1. CLINICAL FOCUS: ${selectedContext}
2. CATEGORIZATION: You MUST assign the scenario to exactly one of the following categories based on its primary context:
2. CATEGORIZATION: Based on the user's input, you MUST suggest the most appropriate category for the scenario:
- 'Social Anxiety': Situations involving social performance, fear of judgment, or networking.
- 'Workplace': Scenarios involving colleagues, supervisors, career growth, or professional conflict.
- 'Relationships': Dynamics with family, friends, or romantic partners; boundary setting.
- 'Self-Talk': Purely internal cognitive restructuring, rumination, or self-compassion work.
3. DIFFICULTY CALIBRATION: Assign a level based on cognitive load:
- 'Beginner': Low stakes, high support, clear-cut distortions.
- 'Intermediate': Ambiguous stakes, moderate social friction, subtle distortions.
- 'Advanced': High emotional triggers, persistent clinical resistance from AI character, deep-seated patterns.
4. INTERNAL LOGIC: The 'systemInstruction' must include a detailed "INTERNAL CBT LOGIC" section. This tells the AI how to react to the user's progress or cognitive lapses.
5. CONSISTENCY: Ensure 'cbtFocus' includes the selected concepts.
3. DIFFICULTY CALIBRATION: The user has explicitly requested a '${selectedDifficulty}' level.
Generate the scenario content and the AI character's behavior to match this complexity:
- 'Beginner': Low stakes, high support, clear-cut distortions, AI character is cooperative.
- 'Intermediate': Ambiguous stakes, moderate social friction, subtle distortions, AI character has specific needs/points.
- 'Advanced': High emotional triggers, persistent clinical resistance from AI character, complex deep-seated cognitive patterns.
4. INTERNAL LOGIC: The 'systemInstruction' for the character MUST include a detailed "INTERNAL CBT LOGIC" section. This instructs the AI character how to react to the user's progress or cognitive lapses.
5. CHARACTER VOICE: Define a realistic voice profile for the character, including their likely emotional state (frustrated, busy, anxious, etc.).
`
},
contents: [
@@ -89,6 +93,8 @@ const ScenarioGenerator: React.FC<ScenarioGeneratorProps> = ({ onGenerate, onCan
if (result.text) {
const scenario = JSON.parse(result.text) as Scenario;
// Enforce the user-selected difficulty in the final object
scenario.difficulty = selectedDifficulty;
scenario.id = `custom-${Date.now()}-${scenario.id}`;
onGenerate(scenario);
} else {
@@ -162,6 +168,29 @@ const ScenarioGenerator: React.FC<ScenarioGeneratorProps> = ({ onGenerate, onCan
</div>
</div>
{/* Difficulty Selection */}
<div className="space-y-3">
<label className="block text-[10px] font-black text-slate-400 uppercase tracking-widest">
Communication Difficulty
</label>
<div className="grid grid-cols-3 gap-2">
{(['Beginner', 'Intermediate', 'Advanced'] as const).map((level) => (
<button
key={level}
onClick={() => setSelectedDifficulty(level)}
className={`flex items-center justify-center gap-2 py-2.5 rounded-xl border-2 transition-all text-[11px] font-bold ${
selectedDifficulty === level
? 'bg-teal-50 border-teal-600 text-teal-700 shadow-sm shadow-teal-50'
: 'bg-white border-slate-100 text-slate-400 hover:border-slate-200'
}`}
>
<BarChart size={14} className={selectedDifficulty === level ? 'text-teal-600' : 'text-slate-300'} />
{level}
</button>
))}
</div>
</div>
{/* Clinical Targets (Selection) */}
<div className="space-y-3">
<label className="block text-[10px] font-black text-slate-400 uppercase tracking-widest">
@@ -229,4 +258,4 @@ const ScenarioGenerator: React.FC<ScenarioGeneratorProps> = ({ onGenerate, onCan
);
};
export default ScenarioGenerator;
export default ScenarioGenerator;