Files
clarity-helper/services/sessionStorage.ts
T
jpremill 1feea40684 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
2026-09-29 23:35:05 -04:00

133 lines
3.8 KiB
TypeScript

/**
* Session Storage & Export Service
*
* Manages persistent storage of completed CBT roleplay sessions,
* enabling history review, markdown report exports, and JSON backups.
*/
import { SavedSession, Scenario, SessionMode, Message, AnalysisResult } from '../types';
const SESSIONS_STORAGE_KEY = 'cognitive-clarity-saved-sessions';
/**
* Retrieves all saved sessions from LocalStorage.
*/
export function getSavedSessions(): SavedSession[] {
try {
const raw = localStorage.getItem(SESSIONS_STORAGE_KEY);
return raw ? JSON.parse(raw) : [];
} catch (e) {
console.error("Failed to load saved sessions", e);
return [];
}
}
/**
* Saves a completed session to LocalStorage.
*/
export function saveSession(
scenario: Scenario,
mode: SessionMode,
messages: Message[],
finalReport?: AnalysisResult
): SavedSession {
const newSession: SavedSession = {
id: `session-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`,
scenarioId: scenario.id,
scenarioTitle: scenario.title,
category: scenario.category,
difficulty: scenario.difficulty,
timestamp: new Date().toISOString(),
mode,
messages,
finalReport
};
try {
const existing = getSavedSessions();
const updated = [newSession, ...existing].slice(0, 50); // Keep last 50 sessions
localStorage.setItem(SESSIONS_STORAGE_KEY, JSON.stringify(updated));
} catch (e) {
console.error("Failed to save session", e);
}
return newSession;
}
/**
* Deletes a session by ID.
*/
export function deleteSession(sessionId: string): void {
try {
const existing = getSavedSessions();
const filtered = existing.filter(s => s.id !== sessionId);
localStorage.setItem(SESSIONS_STORAGE_KEY, JSON.stringify(filtered));
} catch (e) {
console.error("Failed to delete session", e);
}
}
/**
* Generates an Apple HIG-styled Markdown summary of a CBT session report.
*/
export function formatSessionAsMarkdown(
scenarioTitle: string,
analysis: AnalysisResult,
messages?: Message[]
): string {
const dateStr = new Date().toLocaleDateString(undefined, {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
let md = `# Cognitive Clarity — Clinical CBT Session Report\n\n`;
md += `**Scenario:** ${scenarioTitle}\n`;
md += `**Date:** ${dateStr}\n`;
md += `**Clinical Evaluation:** ${analysis.analysis.isAdaptive ? '✅ Adaptive Growth Observed' : '⚠️ Clinical Patterns / Distortions Detected'}\n`;
md += `**Mastery Rating:** ${analysis.rating.score} / 5\n\n`;
md += `---\n\n`;
md += `## 1. Clinical Analysis\n\n`;
if (analysis.analysis.distortions.length > 0) {
md += `**Cognitive Distortions Identified:**\n`;
analysis.analysis.distortions.forEach(d => {
md += `- ${d}\n`;
});
md += `\n`;
}
md += `**Behavioral Evidence:**\n> "${analysis.analysis.evidence}"\n\n`;
md += `**Clinical Justification:**\n${analysis.rating.reason}\n\n`;
md += `## 2. Cognitive Reframing & Tactical Steps\n\n`;
md += `**Master Reframe:**\n> "${analysis.suggestions.alternativeThought}"\n\n`;
md += `**Homework & Behavioral Step:**\n${analysis.suggestions.behavioralStep}\n\n`;
if (messages && messages.length > 0) {
md += `---\n\n## 3. Session Transcript\n\n`;
messages.forEach(m => {
const sender = m.role === 'user' ? 'You' : 'AI Partner';
md += `**[${sender}]**: ${m.text}\n\n`;
});
}
return md;
}
/**
* Triggers a client-side file download of text content.
*/
export function downloadFile(content: string, fileName: string, contentType: string): void {
const blob = new Blob([content], { type: contentType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}