claude-desktop/src-tauri/src/session.rs
Eddy f101661016 Phase 5: Session-Verwaltung + permanente Konversationen
- session.rs: Neues Modul mit 7 Tauri-Commands (CRUD, Resume, aktive Session)
- db.rs: Sessions-Tabelle + CRUD-Methoden (bleiben bis User sie löscht)
- claude.rs: Session-ID und Token/Kosten automatisch in DB speichern
- SessionList.svelte: Sidebar mit Session-Liste, Erstellen, Fortsetzen, Löschen
- +page.svelte: 4-Panel Layout (Sessions | Chat | Aktivität | Agents)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 19:11:17 +02:00

155 lines
4.4 KiB
Rust

// Claude Desktop — Session-Verwaltung
// Sessions bleiben permanent gespeichert bis der User sie löscht
use std::sync::{Arc, Mutex};
use tauri::{AppHandle, Manager};
use crate::db::{self, Session};
// ============ Tauri Commands ============
/// Neue Session erstellen
#[tauri::command]
pub async fn create_session(
app: AppHandle,
title: String,
working_dir: Option<String>,
) -> Result<Session, String> {
let session = Session {
id: uuid::Uuid::new_v4().to_string(),
claude_session_id: None,
title,
working_dir,
message_count: 0,
token_input: 0,
token_output: 0,
cost_usd: 0.0,
status: "active".to_string(),
created_at: chrono::Local::now().to_rfc3339(),
updated_at: chrono::Local::now().to_rfc3339(),
last_message: None,
};
let state = app.state::<Arc<Mutex<db::Database>>>();
let db = state.lock().unwrap();
db.create_session(&session).map_err(|e| e.to_string())?;
// Als aktive Session speichern
db.set_setting("active_session_id", &session.id).map_err(|e| e.to_string())?;
println!("📝 Neue Session: {} ({})", session.title, session.id);
Ok(session)
}
/// Session aktualisieren (nach Nachrichten, Token-Update, etc.)
#[tauri::command]
pub async fn update_session(
app: AppHandle,
session: Session,
) -> Result<(), String> {
let state = app.state::<Arc<Mutex<db::Database>>>();
let db = state.lock().unwrap();
db.update_session(&session).map_err(|e| e.to_string())
}
/// Alle Sessions laden
#[tauri::command]
pub async fn list_sessions(
app: AppHandle,
limit: Option<usize>,
) -> Result<Vec<Session>, String> {
let limit = limit.unwrap_or(50);
let state = app.state::<Arc<Mutex<db::Database>>>();
let db = state.lock().unwrap();
db.load_sessions(limit).map_err(|e| e.to_string())
}
/// Session nach ID laden
#[tauri::command]
pub async fn get_session(
app: AppHandle,
id: String,
) -> Result<Option<Session>, String> {
let state = app.state::<Arc<Mutex<db::Database>>>();
let db = state.lock().unwrap();
db.get_session(&id).map_err(|e| e.to_string())
}
/// Session löschen
#[tauri::command]
pub async fn delete_session(
app: AppHandle,
id: String,
) -> Result<(), String> {
let state = app.state::<Arc<Mutex<db::Database>>>();
let db = state.lock().unwrap();
db.delete_session(&id).map_err(|e| e.to_string())?;
// Falls es die aktive Session war, Setting löschen
if let Ok(Some(active_id)) = db.get_setting("active_session_id") {
if active_id == id {
let _ = db.set_setting("active_session_id", "");
}
}
println!("🗑️ Session gelöscht: {}", id);
Ok(())
}
/// Session fortsetzen — setzt die aktive Session und gibt die claude_session_id zurück
#[tauri::command]
pub async fn resume_session(
app: AppHandle,
id: String,
) -> Result<Session, String> {
let state = app.state::<Arc<Mutex<db::Database>>>();
let db = state.lock().unwrap();
let session = db.get_session(&id)
.map_err(|e| e.to_string())?
.ok_or_else(|| format!("Session {} nicht gefunden", id))?;
// Als aktive Session setzen
db.set_setting("active_session_id", &session.id).map_err(|e| e.to_string())?;
println!("▶️ Session fortgesetzt: {} (claude: {:?})", session.title, session.claude_session_id);
Ok(session)
}
/// Aktive Session holen (nach App-Start)
#[tauri::command]
pub async fn get_active_session(
app: AppHandle,
) -> Result<Option<Session>, String> {
let state = app.state::<Arc<Mutex<db::Database>>>();
let db = state.lock().unwrap();
if let Ok(Some(id)) = db.get_setting("active_session_id") {
if !id.is_empty() {
return db.get_session(&id).map_err(|e| e.to_string());
}
}
Ok(None)
}
/// Claude Session-ID speichern (kommt von der Bridge nach erstem Request)
#[tauri::command]
pub async fn set_claude_session_id(
app: AppHandle,
session_id: String,
claude_session_id: String,
) -> Result<(), String> {
let state = app.state::<Arc<Mutex<db::Database>>>();
let db = state.lock().unwrap();
if let Ok(Some(mut session)) = db.get_session(&session_id) {
session.claude_session_id = Some(claude_session_id.clone());
db.update_session(&session).map_err(|e| e.to_string())?;
println!("🔗 Claude Session-ID gesetzt: {}{}", session_id, claude_session_id);
}
Ok(())
}