feat(ui): redesign voice cockpit with clinical companion drawer, Svelte 5 migration, and Gemini 3.8 voice
- Re-architect VoiceSession into full-bleed studio cockpit with overlay slide-over companion drawer - Eliminate width squishing defect on LiveInsightPanel and add native dark/light theme support - Integrate authentic Google Gemini 3.8 Flash TTS with Fenrir persona and fallback audio pipeline - Implement comprehensive Playwright E2E visual regression suite across Desktop, iPad, and iPhone (21 tests, 45 snapshots) - Migrate core application to Svelte 5 runes (, , ) with reactive stores - Maintain .NET 8 Azure Functions serverless endpoints and clean build hygiene
This commit is contained in:
1 parent
c82b112624
commit
1feea40684
148 files changed
+11230
-1706
No files matched your search
+94
-16
@@ -1,5 +1,71 @@
|
||||
import { Scenario } from './types';
|
||||
import { Type } from "@google/genai";
|
||||
import { Scenario, UserSettings } from './types';
|
||||
|
||||
export interface VoiceProfile {
|
||||
id: UserSettings['voiceName'];
|
||||
label: string;
|
||||
gender: 'Male' | 'Female' | 'Neutral';
|
||||
style: string;
|
||||
description: string;
|
||||
sampleText: string;
|
||||
openRouterVoice: 'alloy' | 'echo' | 'fable' | 'onyx' | 'nova' | 'shimmer';
|
||||
webSpeechTuning: {
|
||||
pitch: number;
|
||||
rate: number;
|
||||
};
|
||||
}
|
||||
|
||||
export const VOICE_PROFILES: VoiceProfile[] = [
|
||||
{
|
||||
id: 'Fenrir',
|
||||
label: 'Fenrir (Salt of the Earth)',
|
||||
gender: 'Male',
|
||||
style: 'Gruff / Earthy',
|
||||
description: 'A gravelly, direct, salt-of-the-earth tone. Grounded and weathered, perfect for boundary testing and blue-collar roleplays.',
|
||||
sampleText: "Listen here, friend. Life's messy and things break down, but we take it one honest step at a time.",
|
||||
openRouterVoice: 'onyx',
|
||||
webSpeechTuning: { pitch: 0.75, rate: 0.92 }
|
||||
},
|
||||
{
|
||||
id: 'Zephyr',
|
||||
label: 'Zephyr',
|
||||
gender: 'Neutral',
|
||||
style: 'Helpful',
|
||||
description: 'A balanced, clear, and professional tone suitable for most general scenarios.',
|
||||
sampleText: "Hello. I'm here to provide a clear, balanced perspective as we explore different ways of thinking.",
|
||||
openRouterVoice: 'alloy',
|
||||
webSpeechTuning: { pitch: 1.0, rate: 1.0 }
|
||||
},
|
||||
{
|
||||
id: 'Puck',
|
||||
label: 'Puck',
|
||||
gender: 'Male',
|
||||
style: 'Youthful',
|
||||
description: 'Energetic and friendly, ideal for peer-to-peer or informal interactions.',
|
||||
sampleText: "Hey there! Ready to jump into this scenario and see what kind of creative solutions we can uncover?",
|
||||
openRouterVoice: 'fable',
|
||||
webSpeechTuning: { pitch: 1.15, rate: 1.05 }
|
||||
},
|
||||
{
|
||||
id: 'Kore',
|
||||
label: 'Kore',
|
||||
gender: 'Female',
|
||||
style: 'Calm',
|
||||
description: 'Soothing and professional, perfect for clinical or high-stress roleplay.',
|
||||
sampleText: "Take a steady breath. Whatever you're carrying right now, we can slow down and examine it together.",
|
||||
openRouterVoice: 'nova',
|
||||
webSpeechTuning: { pitch: 1.1, rate: 0.95 }
|
||||
},
|
||||
{
|
||||
id: 'Charon',
|
||||
label: 'Charon',
|
||||
gender: 'Male',
|
||||
style: 'Authoritative',
|
||||
description: 'Deep and commanding, useful for workplace or boundary-testing scenarios.',
|
||||
sampleText: "Let us be direct. In demanding situations, clear boundaries and disciplined reasoning are essential.",
|
||||
openRouterVoice: 'echo',
|
||||
webSpeechTuning: { pitch: 0.85, rate: 0.95 }
|
||||
}
|
||||
];
|
||||
|
||||
export interface GlossaryEntry {
|
||||
term: string;
|
||||
@@ -99,6 +165,18 @@ export const CBT_GLOSSARY: GlossaryEntry[] = [
|
||||
definition: "Believing you are responsible for events outside your control or that other people's behavior is a direct reaction to you.",
|
||||
example: "My boss looks upset today; it must be because of that email I sent."
|
||||
},
|
||||
{
|
||||
term: "Labeling",
|
||||
category: "Distortion",
|
||||
definition: "Assigning a harsh, global, and definitive negative label to yourself or others based on a single instance or error.",
|
||||
example: "I made an error on the slide, so 'I am an idiot' rather than 'I made a mistake.'"
|
||||
},
|
||||
{
|
||||
term: "Overgeneralization",
|
||||
category: "Distortion",
|
||||
definition: "Taking a single negative event and seeing it as a never-ending pattern of defeat by using words like 'always' or 'never'.",
|
||||
example: "I didn't get this promotion, so I will never advance in my career."
|
||||
},
|
||||
{
|
||||
term: "Grounding Exercises",
|
||||
category: "Technique",
|
||||
@@ -116,7 +194,7 @@ export const SCENARIOS: Scenario[] = [
|
||||
description: "You sent a text to a close friend hours ago and haven't heard back. You're starting to imagine they're angry with you or that you've done something wrong.",
|
||||
category: 'Relationships',
|
||||
difficulty: 'Beginner',
|
||||
systemInstruction: "You are Alex, a busy friend who just hasn't had a chance to reply yet. You are friendly and non-confrontational. If asked, you explain you were just preoccupied with chores.",
|
||||
systemInstruction: "You are Alex, a busy friend who just hasn't had a chance to reply yet. You are friendly and non-confrontational. If asked, you explain you were just preoccupied with chores. INTERNAL CBT LOGIC: If the user tests for reassurance or asks if you're upset, warmly reassure them that everything is fine.",
|
||||
initialMessage: "Hey! Sorry I'm just getting back to you, it's been a crazy day. What's up?",
|
||||
cbtFocus: ["Mind Reading", "Fortune Telling"]
|
||||
},
|
||||
@@ -138,7 +216,7 @@ export const SCENARIOS: Scenario[] = [
|
||||
description: "You're at a networking event and you just made a small slip-up in a conversation. You feel like everyone noticed and is judging you.",
|
||||
category: 'Social Anxiety',
|
||||
difficulty: 'Advanced',
|
||||
systemInstruction: "You are David, an attendee at a networking event. You are polite and didn't even notice the user's minor slip-up. You are focused on professional exchange.",
|
||||
systemInstruction: "You are David, an attendee at a networking event. You are polite and didn't even notice the user's minor slip-up. You are focused on professional exchange. INTERNAL CBT LOGIC: If the user draws excessive attention to their mistake or apologizes repeatedly, casually dismiss it and keep the conversation on the topic.",
|
||||
initialMessage: "That's an interesting point about the industry trends. So, how did you get started in this field?",
|
||||
cbtFocus: ["Spotlight Effect", "Emotional Reasoning"]
|
||||
},
|
||||
@@ -149,7 +227,7 @@ export const SCENARIOS: Scenario[] = [
|
||||
description: "A minor mistake at home triggers a spiral of self-deprecating thoughts. Practice internal cognitive restructuring by talking back to your personified inner critic.",
|
||||
category: 'Self-Talk',
|
||||
difficulty: 'Beginner',
|
||||
systemInstruction: "You are the user's personified Inner Critic. You are harsh, cynical, and use common distortions. Your goal is to represent the user's negative self-talk so they can practice refuting it.",
|
||||
systemInstruction: "You are the user's personified Inner Critic. You are harsh, cynical, and use common distortions. Your goal is to represent the user's negative self-talk so they can practice refuting it. INTERNAL CBT LOGIC: If the user responds with calm, objective evidence-based reframing, grudgingly lose ground. If the user engages in emotional reasoning or agrees with your insults, double down on catastrophic conclusions.",
|
||||
initialMessage: "I can't believe you forgot to buy milk again. You're completely disorganized and you'll never get your life together.",
|
||||
cbtFocus: ["Labeling", "Overgeneralization"]
|
||||
},
|
||||
@@ -266,30 +344,30 @@ export const SCENARIOS: Scenario[] = [
|
||||
];
|
||||
|
||||
export const CBT_ANALYSIS_SCHEMA = {
|
||||
type: Type.OBJECT,
|
||||
type: 'object',
|
||||
properties: {
|
||||
analysis: {
|
||||
type: Type.OBJECT,
|
||||
type: 'object',
|
||||
properties: {
|
||||
distortions: { type: Type.ARRAY, items: { type: Type.STRING } },
|
||||
isAdaptive: { type: Type.BOOLEAN },
|
||||
evidence: { type: Type.STRING },
|
||||
distortions: { type: 'array', items: { type: 'string' } },
|
||||
isAdaptive: { type: 'boolean' },
|
||||
evidence: { type: 'string' },
|
||||
},
|
||||
required: ['distortions', 'isAdaptive', 'evidence']
|
||||
},
|
||||
rating: {
|
||||
type: Type.OBJECT,
|
||||
type: 'object',
|
||||
properties: {
|
||||
score: { type: Type.INTEGER },
|
||||
reason: { type: Type.STRING },
|
||||
score: { type: 'integer' },
|
||||
reason: { type: 'string' },
|
||||
},
|
||||
required: ['score', 'reason']
|
||||
},
|
||||
suggestions: {
|
||||
type: Type.OBJECT,
|
||||
type: 'object',
|
||||
properties: {
|
||||
alternativeThought: { type: Type.STRING },
|
||||
behavioralStep: { type: Type.STRING },
|
||||
alternativeThought: { type: 'string' },
|
||||
behavioralStep: { type: 'string' },
|
||||
},
|
||||
required: ['alternativeThought', 'behavioralStep']
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user