use tauri::{AppHandle, State}; use tauri_plugin_store::StoreExt; use crate::config::{ClaudeStartOptions, HikariConfig}; use crate::stats::UsageStats; use crate::bridge_manager::SharedBridgeManager; use crate::achievements::{load_achievements, get_achievement_info, AchievementUnlockedEvent}; const CONFIG_STORE_KEY: &str = "config"; #[tauri::command] pub async fn start_claude( bridge_manager: State<'_, SharedBridgeManager>, conversation_id: String, options: ClaudeStartOptions, ) -> Result<(), String> { let mut manager = bridge_manager.lock(); manager.start_claude(&conversation_id, options) } #[tauri::command] pub async fn stop_claude( bridge_manager: State<'_, SharedBridgeManager>, conversation_id: String, ) -> Result<(), String> { let mut manager = bridge_manager.lock(); manager.stop_claude(&conversation_id) } #[tauri::command] pub async fn interrupt_claude( bridge_manager: State<'_, SharedBridgeManager>, conversation_id: String, ) -> Result<(), String> { let mut manager = bridge_manager.lock(); manager.interrupt_claude(&conversation_id) } #[tauri::command] pub async fn send_prompt( bridge_manager: State<'_, SharedBridgeManager>, conversation_id: String, message: String, ) -> Result<(), String> { let mut manager = bridge_manager.lock(); manager.send_prompt(&conversation_id, message) } #[tauri::command] pub async fn is_claude_running( bridge_manager: State<'_, SharedBridgeManager>, conversation_id: String, ) -> Result { let manager = bridge_manager.lock(); Ok(manager.is_claude_running(&conversation_id)) } #[tauri::command] pub async fn get_working_directory( bridge_manager: State<'_, SharedBridgeManager>, conversation_id: String, ) -> Result { let manager = bridge_manager.lock(); manager.get_working_directory(&conversation_id) } #[tauri::command] pub async fn select_wsl_directory() -> Result { Ok("/home".to_string()) } #[tauri::command] pub async fn get_config(app: AppHandle) -> Result { let store = app .store("hikari-config.json") .map_err(|e| e.to_string())?; match store.get(CONFIG_STORE_KEY) { Some(value) => { serde_json::from_value(value.clone()).map_err(|e| e.to_string()) } None => Ok(HikariConfig::default()), } } #[tauri::command] pub async fn save_config(app: AppHandle, config: HikariConfig) -> Result<(), String> { let store = app .store("hikari-config.json") .map_err(|e| e.to_string())?; let value = serde_json::to_value(&config).map_err(|e| e.to_string())?; store.set(CONFIG_STORE_KEY, value); store.save().map_err(|e| e.to_string())?; Ok(()) } #[tauri::command] pub async fn get_usage_stats( bridge_manager: State<'_, SharedBridgeManager>, conversation_id: String, ) -> Result { let manager = bridge_manager.lock(); manager.get_usage_stats(&conversation_id) } #[tauri::command] pub async fn validate_directory(path: String, current_dir: Option) -> Result { use std::path::Path; let path = Path::new(&path); // Expand ~ to home directory let expanded_path = if path.starts_with("~") { if let Some(home) = std::env::var_os("HOME") { let home_path = Path::new(&home); if path == Path::new("~") { home_path.to_path_buf() } else { home_path.join(path.strip_prefix("~").unwrap()) } } else { return Err("Could not determine home directory".to_string()); } } else if path.is_relative() { // Handle relative paths (., .., or any relative path) by resolving against current_dir if let Some(ref cwd) = current_dir { Path::new(cwd).join(path) } else { path.to_path_buf() } } else { path.to_path_buf() }; // Check if the path exists and is a directory if !expanded_path.exists() { return Err(format!("Directory does not exist: {}", expanded_path.display())); } if !expanded_path.is_dir() { return Err(format!("Path is not a directory: {}", expanded_path.display())); } // Return the canonicalized (absolute) path expanded_path .canonicalize() .map(|p| p.to_string_lossy().to_string()) .map_err(|e| format!("Failed to resolve path: {}", e)) } #[tauri::command] pub async fn load_saved_achievements(app: AppHandle) -> Result, String> { use chrono::Utc; // Load achievements from persistent store let progress = load_achievements(&app).await; // Create events for all previously unlocked achievements let mut events = Vec::new(); for achievement_id in &progress.unlocked { let mut info = get_achievement_info(achievement_id); info.unlocked_at = Some(Utc::now()); // We don't store timestamps, so just use now events.push(AchievementUnlockedEvent { achievement: info, }); } Ok(events) }