generated from nhcarrigan/template
02cc8bd0d5
- Add Rust backend for managing quick actions with persistent storage - Create QuickActionsPanel component with edit/delete functionality - Add quickActions store for frontend state management - Move Actions and Snippets buttons to input controls row - Include 6 default quick actions: Review PR, Run Tests, Explain File, Fix Error, Write Tests, and Refactor - Support custom quick action creation and management Closes #15
192 lines
6.0 KiB
Rust
192 lines
6.0 KiB
Rust
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<Utc>,
|
|
pub updated_at: DateTime<Utc>,
|
|
}
|
|
|
|
fn get_default_quick_actions() -> Vec<QuickAction> {
|
|
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<Vec<QuickAction>, 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<QuickAction> =
|
|
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<Vec<QuickAction>, 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());
|
|
}
|
|
}
|
|
}
|