a very good jj gui
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add Tauri commands and Debug menu

+106 -15
+106 -15
apps/desktop/src-tauri/src/lib.rs
··· 4 4 5 5 use repo::diff; 6 6 use repo::jj::JjRepo; 7 - use repo::log::Revision; 7 + use repo::log::{Revision, RevsetResult}; 8 8 use repo::status::WorkingCopyStatus; 9 9 use serde::Serialize; 10 10 use std::path::{Path, PathBuf}; 11 11 use std::sync::Arc; 12 12 use storage::{AppLayout, Project, Storage, get_storage}; 13 13 use tauri::Manager; 14 + use tauri::menu::{MenuBuilder, SubmenuBuilder}; 14 15 use watcher::{WatcherManager, get_watcher_manager}; 15 16 16 17 #[derive(Serialize)] ··· 26 27 } 27 28 28 29 #[tauri::command] 29 - async fn get_revisions(repo_path: String, limit: usize, revset: Option<String>, preset: Option<String>) -> Result<Vec<Revision>, String> { 30 + async fn get_revisions( 31 + repo_path: String, 32 + limit: usize, 33 + revset: Option<String>, 34 + preset: Option<String>, 35 + ) -> Result<Vec<Revision>, String> { 30 36 let path = Path::new(&repo_path); 31 - repo::log::fetch_log(path, limit, revset.as_deref(), preset.as_deref()).map_err(|e| format!("Failed to fetch log: {}", e)) 37 + repo::log::fetch_log(path, limit, revset.as_deref(), preset.as_deref()) 38 + .map_err(|e| format!("Failed to fetch log: {}", e)) 32 39 } 33 40 34 41 #[tauri::command] ··· 76 83 77 84 let parent_tree = { 78 85 let parents = commit.parents(); 79 - let parent = parents.into_iter().next().ok_or_else(|| "Commit has no parent".to_string())?; 80 - parent.map_err(|e| format!("Failed to get parent: {}", e))?.tree().map_err(|e| format!("Failed to get parent tree: {}", e))? 86 + let parent = parents 87 + .into_iter() 88 + .next() 89 + .ok_or_else(|| "Commit has no parent".to_string())?; 90 + parent 91 + .map_err(|e| format!("Failed to get parent: {}", e))? 92 + .tree() 93 + .map_err(|e| format!("Failed to get parent tree: {}", e))? 81 94 }; 82 95 83 - let commit_tree = commit.tree().map_err(|e| format!("Failed to get commit tree: {}", e))?; 96 + let commit_tree = commit 97 + .tree() 98 + .map_err(|e| format!("Failed to get commit tree: {}", e))?; 84 99 85 100 let matcher = EverythingMatcher; 86 101 let mut diff_iter = parent_tree.diff_stream(&commit_tree, &matcher); ··· 93 108 let path = entry.path; 94 109 let path_str = path.as_internal_file_string(); 95 110 96 - let diff_values = entry.values.map_err(|e| format!("Failed to get diff values: {}", e))?; 111 + let diff_values = entry 112 + .values 113 + .map_err(|e| format!("Failed to get diff values: {}", e))?; 97 114 let before = diff_values.before.removes().next().and_then(|v| v.as_ref()); 98 115 let after = diff_values.after.adds().next().and_then(|v| v.as_ref()); 99 116 100 117 match (before, after) { 101 - (Some(TreeValue::File { .. }), Some(TreeValue::File { .. })) | 102 - (None, Some(TreeValue::File { .. })) | 103 - (Some(TreeValue::File { .. }), None) => { 118 + (Some(TreeValue::File { .. }), Some(TreeValue::File { .. })) 119 + | (None, Some(TreeValue::File { .. })) 120 + | (Some(TreeValue::File { .. }), None) => { 104 121 let old_content = jj_repo 105 122 .get_parent_file_content(&commit, path_str) 106 123 .unwrap_or_default(); ··· 126 143 } 127 144 128 145 #[tauri::command] 129 - async fn get_revision_changes(repo_path: String, change_id: String) -> Result<Vec<ChangedFile>, String> { 146 + async fn get_revision_changes( 147 + repo_path: String, 148 + change_id: String, 149 + ) -> Result<Vec<ChangedFile>, String> { 130 150 use jj_lib::backend::TreeValue; 131 151 use jj_lib::matchers::EverythingMatcher; 132 152 ··· 139 159 140 160 let parent_tree = { 141 161 let parents = commit.parents(); 142 - let parent = parents.into_iter().next().ok_or_else(|| "Commit has no parent".to_string())?; 143 - parent.map_err(|e| format!("Failed to get parent: {}", e))?.tree().map_err(|e| format!("Failed to get parent tree: {}", e))? 162 + let parent = parents 163 + .into_iter() 164 + .next() 165 + .ok_or_else(|| "Commit has no parent".to_string())?; 166 + parent 167 + .map_err(|e| format!("Failed to get parent: {}", e))? 168 + .tree() 169 + .map_err(|e| format!("Failed to get parent tree: {}", e))? 144 170 }; 145 171 146 - let commit_tree = commit.tree().map_err(|e| format!("Failed to get commit tree: {}", e))?; 172 + let commit_tree = commit 173 + .tree() 174 + .map_err(|e| format!("Failed to get commit tree: {}", e))?; 147 175 148 176 let matcher = EverythingMatcher; 149 177 let mut diff_iter = parent_tree.diff_stream(&commit_tree, &matcher); ··· 156 184 let path = entry.path; 157 185 let path_str = path.as_internal_file_string(); 158 186 159 - let diff_values = entry.values.map_err(|e| format!("Failed to get diff values: {}", e))?; 187 + let diff_values = entry 188 + .values 189 + .map_err(|e| format!("Failed to get diff values: {}", e))?; 160 190 let before = diff_values.before.removes().next().and_then(|v| v.as_ref()); 161 191 let after = diff_values.after.adds().next().and_then(|v| v.as_ref()); 162 192 ··· 262 292 .map_err(|e| format!("Failed to edit revision: {}", e)) 263 293 } 264 294 295 + #[tauri::command] 296 + async fn jj_abandon(repo_path: String, change_id: String) -> Result<(), String> { 297 + let path = Path::new(&repo_path); 298 + let mut jj_repo = JjRepo::open(path).map_err(|e| format!("Failed to open repo: {}", e))?; 299 + jj_repo 300 + .abandon_revision(&change_id) 301 + .map_err(|e| format!("Failed to abandon revision: {}", e)) 302 + } 303 + 304 + /// Get recency data for commits by walking the operation log. 305 + /// Returns a map of commit_id (hex) -> timestamp_millis (when it was last the working copy). 306 + #[tauri::command] 307 + async fn get_commit_recency( 308 + repo_path: String, 309 + limit: usize, 310 + ) -> Result<std::collections::HashMap<String, i64>, String> { 311 + let path = Path::new(&repo_path); 312 + let jj_repo = JjRepo::open(path).map_err(|e| format!("Failed to open repo: {}", e))?; 313 + jj_repo 314 + .get_commit_recency(limit) 315 + .map_err(|e| format!("Failed to get commit recency: {}", e)) 316 + } 317 + 318 + /// Resolve a revset expression and return matching change IDs. 319 + /// Uses jj-lib's full revset parser. 320 + #[tauri::command] 321 + async fn resolve_revset(repo_path: String, revset: String) -> Result<RevsetResult, String> { 322 + let path = Path::new(&repo_path); 323 + repo::log::resolve_revset(path, &revset).map_err(|e| format!("Failed to resolve revset: {}", e)) 324 + } 325 + 265 326 #[cfg_attr(mobile, tauri::mobile_entry_point)] 266 327 pub fn run() { 267 328 tauri::Builder::default() ··· 284 345 285 346 app.handle().manage(WatcherManager::new()); 286 347 348 + // Add Debug menu for development 349 + #[cfg(debug_assertions)] 350 + { 351 + let debug_menu = SubmenuBuilder::new(app, "Debug") 352 + .text("reload", "Reload") 353 + .build()?; 354 + 355 + let menu = MenuBuilder::new(app) 356 + .copy() 357 + .paste() 358 + .undo() 359 + .redo() 360 + .separator() 361 + .item(&debug_menu) 362 + .build()?; 363 + 364 + app.set_menu(menu)?; 365 + 366 + app.on_menu_event(|app_handle, event| { 367 + if event.id().0.as_str() == "reload" { 368 + if let Some(window) = app_handle.get_webview_window("main") { 369 + let _ = window.eval("window.location.reload()"); 370 + } 371 + } 372 + }); 373 + } 374 + 287 375 Ok(()) 288 376 }) 289 377 .invoke_handler(tauri::generate_handler![ ··· 293 381 get_file_diff, 294 382 get_revision_diff, 295 383 get_revision_changes, 384 + get_commit_recency, 385 + resolve_revset, 296 386 get_projects, 297 387 upsert_project, 298 388 find_project_by_path, ··· 303 393 unwatch_repository, 304 394 jj_new, 305 395 jj_edit, 396 + jj_abandon, 306 397 ]) 307 398 .run(tauri::generate_context!()) 308 399 .expect("error while running tauri application");