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
@@ -1,14 +1,42 @@
|
||||
|
||||
import React from 'react';
|
||||
import { AnalysisResult } from '../types';
|
||||
import { ClipboardCheck, X, CheckCircle2, AlertTriangle, Lightbulb, TrendingUp } from 'lucide-react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { AnalysisResult, Message } from '../types';
|
||||
import { ClipboardCheck, X, CheckCircle2, AlertTriangle, Lightbulb, TrendingUp, Download, Copy, Check, FileText } from 'lucide-react';
|
||||
import { Button } from './ui/button';
|
||||
import { formatSessionAsMarkdown, downloadFile } from '../services/sessionStorage';
|
||||
|
||||
interface SessionReportProps {
|
||||
analysis: AnalysisResult;
|
||||
scenarioTitle?: string;
|
||||
messages?: Message[];
|
||||
onExit: () => void;
|
||||
}
|
||||
|
||||
const SessionReport: React.FC<SessionReportProps> = ({ analysis, onExit }) => {
|
||||
const SessionReport: React.FC<SessionReportProps> = ({
|
||||
analysis,
|
||||
scenarioTitle = "CBT Roleplay Session",
|
||||
messages = [],
|
||||
onExit
|
||||
}) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopyReframe = () => {
|
||||
navigator.clipboard.writeText(analysis.suggestions.alternativeThought);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
const handleExportMarkdown = () => {
|
||||
const md = formatSessionAsMarkdown(scenarioTitle, analysis, messages);
|
||||
const fileName = `cbt-report-${scenarioTitle.toLowerCase().replace(/\s+/g, '-')}-${Date.now()}.md`;
|
||||
downloadFile(md, fileName, 'text/markdown');
|
||||
};
|
||||
|
||||
const handleExportJSON = () => {
|
||||
const json = JSON.stringify({ scenarioTitle, analysis, messages, exportedAt: new Date().toISOString() }, null, 2);
|
||||
const fileName = `cbt-report-${scenarioTitle.toLowerCase().replace(/\s+/g, '-')}-${Date.now()}.json`;
|
||||
downloadFile(json, fileName, 'application/json');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full bg-[#FDFDFF] rounded-3xl border border-slate-200 overflow-hidden shadow-2xl animate-in zoom-in-95 duration-500 w-full max-w-4xl mx-auto">
|
||||
<div className="bg-teal-600 p-6 flex justify-between items-center text-white">
|
||||
@@ -16,12 +44,36 @@ const SessionReport: React.FC<SessionReportProps> = ({ analysis, onExit }) => {
|
||||
<ClipboardCheck size={28} />
|
||||
<div>
|
||||
<h2 className="text-xl font-bold leading-none">Session Summary Report</h2>
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-teal-100 opacity-80">Clinical Analysis Complete</span>
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-teal-100 opacity-80">
|
||||
{scenarioTitle} • Clinical Evaluation Complete
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button onClick={onExit} className="bg-white/10 hover:bg-white/20 p-2 rounded-xl border border-white/10 transition-colors">
|
||||
<X size={20} />
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleExportMarkdown}
|
||||
className="text-white hover:bg-white/20 rounded-xl text-xs flex items-center gap-1.5"
|
||||
title="Download Markdown Report"
|
||||
>
|
||||
<FileText size={16} />
|
||||
<span className="hidden sm:inline">Export MD</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleExportJSON}
|
||||
className="text-white hover:bg-white/20 rounded-xl text-xs flex items-center gap-1.5"
|
||||
title="Download JSON Report"
|
||||
>
|
||||
<Download size={16} />
|
||||
<span className="hidden sm:inline">JSON</span>
|
||||
</Button>
|
||||
<button onClick={onExit} className="bg-white/10 hover:bg-white/20 p-2 rounded-xl border border-white/10 transition-colors ml-1">
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-grow overflow-y-auto p-8 flex flex-col gap-6">
|
||||
@@ -55,10 +107,21 @@ const SessionReport: React.FC<SessionReportProps> = ({ analysis, onExit }) => {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="bg-white p-6 rounded-2xl border border-teal-100 shadow-sm">
|
||||
<div className="flex items-center gap-2 text-teal-600 mb-3">
|
||||
<Lightbulb size={20} />
|
||||
<span className="text-[10px] font-black uppercase tracking-widest">Master Reframe</span>
|
||||
<div className="bg-white p-6 rounded-2xl border border-teal-100 shadow-sm relative group">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2 text-teal-600">
|
||||
<Lightbulb size={20} />
|
||||
<span className="text-[10px] font-black uppercase tracking-widest">Master Reframe</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleCopyReframe}
|
||||
className="h-7 px-2 text-[10px] text-slate-400 hover:text-teal-700 flex items-center gap-1"
|
||||
>
|
||||
{copied ? <Check size={12} className="text-teal-600" /> : <Copy size={12} />}
|
||||
{copied ? 'Copied' : 'Copy'}
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-base text-slate-800 italic font-medium leading-relaxed">"{analysis.suggestions.alternativeThought}"</p>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user