use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use tauri::AppHandle; use tauri_plugin_store::StoreExt; const QUICK_ACTIONS_STORE_KEY: &str = "quick_actions"; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct QuickAction { pub id: String, pub name: String, pub prompt: String, pub icon: String, pub is_default: bool, pub created_at: DateTime, pub updated_at: DateTime, } fn get_default_quick_actions() -> Vec { let now = Utc::now(); vec![ QuickAction { id: "default-review-pr".to_string(), name: "Review PR".to_string(), prompt: "Please review this pull request and provide feedback on code quality, potential issues, and suggestions for improvement.".to_string(), icon: "git-pull-request".to_string(), is_default: true, created_at: now, updated_at: now, }, QuickAction { id: "default-run-tests".to_string(), name: "Run Tests".to_string(), prompt: "Please run the test suite for this project and report any failures or issues.".to_string(), icon: "play".to_string(), is_default: true, created_at: now, updated_at: now, }, QuickAction { id: "default-explain-file".to_string(), name: "Explain File".to_string(), prompt: "Please explain what this file does, its purpose, and how it fits into the overall project structure.".to_string(), icon: "file-text".to_string(), is_default: true, created_at: now, updated_at: now, }, QuickAction { id: "default-fix-error".to_string(), name: "Fix Error".to_string(), prompt: "I'm getting an error. Can you help me identify the cause and fix it?".to_string(), icon: "alert-circle".to_string(), is_default: true, created_at: now, updated_at: now, }, QuickAction { id: "default-write-tests".to_string(), name: "Write Tests".to_string(), prompt: "Please write comprehensive unit tests for the current code with good coverage.".to_string(), icon: "check-square".to_string(), is_default: true, created_at: now, updated_at: now, }, QuickAction { id: "default-refactor".to_string(), name: "Refactor".to_string(), prompt: "Please refactor this code to improve readability, maintainability, and performance.".to_string(), icon: "refresh-cw".to_string(), is_default: true, created_at: now, updated_at: now, }, ] } fn load_all_quick_actions(app: &AppHandle) -> Result, String> { let store = app .store("hikari-quick-actions.json") .map_err(|e| e.to_string())?; match store.get(QUICK_ACTIONS_STORE_KEY) { Some(value) => { let mut actions: Vec = serde_json::from_value(value.clone()).map_err(|e| e.to_string())?; let defaults = get_default_quick_actions(); for default in defaults { if !actions.iter().any(|a| a.id == default.id) { actions.push(default); } } Ok(actions) } None => Ok(get_default_quick_actions()), } } fn save_all_quick_actions(app: &AppHandle, actions: &[QuickAction]) -> Result<(), String> { let store = app .store("hikari-quick-actions.json") .map_err(|e| e.to_string())?; let value = serde_json::to_value(actions).map_err(|e| e.to_string())?; store.set(QUICK_ACTIONS_STORE_KEY, value); store.save().map_err(|e| e.to_string())?; Ok(()) } #[tauri::command] pub async fn list_quick_actions(app: AppHandle) -> Result, String> { let mut actions = load_all_quick_actions(&app)?; actions.sort_by(|a, b| { let default_cmp = b.is_default.cmp(&a.is_default); if default_cmp == std::cmp::Ordering::Equal { a.name.cmp(&b.name) } else { default_cmp } }); Ok(actions) } #[tauri::command] pub async fn save_quick_action(app: AppHandle, action: QuickAction) -> Result<(), String> { let mut actions = load_all_quick_actions(&app)?; if let Some(existing) = actions.iter_mut().find(|a| a.id == action.id) { let mut updated = action; updated.is_default = existing.is_default; *existing = updated; } else { actions.push(action); } save_all_quick_actions(&app, &actions) } #[tauri::command] pub async fn delete_quick_action(app: AppHandle, action_id: String) -> Result<(), String> { let mut actions = load_all_quick_actions(&app)?; if actions .iter() .any(|a| a.id == action_id && a.is_default) { return Err("Cannot delete default quick actions".to_string()); } actions.retain(|a| a.id != action_id); save_all_quick_actions(&app, &actions) } #[tauri::command] pub async fn reset_default_quick_actions(app: AppHandle) -> Result<(), String> { let mut actions = load_all_quick_actions(&app)?; actions.retain(|a| !a.is_default); actions.extend(get_default_quick_actions()); save_all_quick_actions(&app, &actions) } #[cfg(test)] mod tests { use super::*; #[test] fn test_default_quick_actions_exist() { let defaults = get_default_quick_actions(); assert!(!defaults.is_empty()); assert!(defaults.iter().all(|a| a.is_default)); } #[test] fn test_default_quick_actions_have_required_fields() { let defaults = get_default_quick_actions(); for action in defaults { assert!(!action.id.is_empty()); assert!(!action.name.is_empty()); assert!(!action.prompt.is_empty()); assert!(!action.icon.is_empty()); } } }