@@ -2603,7 +2603,14 @@ impl App { |
| 2603 | 2603 | }; |
| 2604 | 2604 | |
| 2605 | 2605 | match std::process::Command::new(&app_cmd).arg(&path).spawn() { |
| 2606 | | - Ok(_) => self.status_bar.set_status_message(format!("Opened with {}", app_cmd)), |
| 2606 | + Ok(_) => { |
| 2607 | + self.status_bar.set_status_message(format!("Opened with {}", app_cmd)); |
| 2608 | + // Add to recently used files |
| 2609 | + let mime_type = guess_mime_type(&path); |
| 2610 | + if let Err(e) = self.recents.add_entry(&path, &mime_type) { |
| 2611 | + tracing::warn!("Failed to add to recents: {}", e); |
| 2612 | + } |
| 2613 | + } |
| 2607 | 2614 | Err(e) => self.status_bar.set_status_message(format!("Failed to open with {}: {}", app_cmd, e)), |
| 2608 | 2615 | } |
| 2609 | 2616 | } |
@@ -2611,7 +2618,14 @@ impl App { |
| 2611 | 2618 | /// Open file with the system's default application (xdg-open). |
| 2612 | 2619 | fn open_file_with_default(&mut self, path: &PathBuf) { |
| 2613 | 2620 | match std::process::Command::new("xdg-open").arg(path).spawn() { |
| 2614 | | - Ok(_) => self.status_bar.set_status_message(format!("Opened {}", path.file_name().unwrap_or_default().to_string_lossy())), |
| 2621 | + Ok(_) => { |
| 2622 | + self.status_bar.set_status_message(format!("Opened {}", path.file_name().unwrap_or_default().to_string_lossy())); |
| 2623 | + // Add to recently used files |
| 2624 | + let mime_type = guess_mime_type(path); |
| 2625 | + if let Err(e) = self.recents.add_entry(path, &mime_type) { |
| 2626 | + tracing::warn!("Failed to add to recents: {}", e); |
| 2627 | + } |
| 2628 | + } |
| 2615 | 2629 | Err(e) => self.status_bar.set_status_message(format!("Failed to open: {}", e)), |
| 2616 | 2630 | } |
| 2617 | 2631 | } |
@@ -2628,7 +2642,14 @@ impl App { |
| 2628 | 2642 | } |
| 2629 | 2643 | |
| 2630 | 2644 | match std::process::Command::new(app_name).arg(&path).spawn() { |
| 2631 | | - Ok(_) => self.status_bar.set_status_message(format!("Opened with {}", app_name)), |
| 2645 | + Ok(_) => { |
| 2646 | + self.status_bar.set_status_message(format!("Opened with {}", app_name)); |
| 2647 | + // Add to recently used files |
| 2648 | + let mime_type = guess_mime_type(&path); |
| 2649 | + if let Err(e) = self.recents.add_entry(&path, &mime_type) { |
| 2650 | + tracing::warn!("Failed to add to recents: {}", e); |
| 2651 | + } |
| 2652 | + } |
| 2632 | 2653 | Err(e) => self.status_bar.set_status_message(format!("Failed to open with {}: {}", app_name, e)), |
| 2633 | 2654 | } |
| 2634 | 2655 | } |
@@ -3579,3 +3600,63 @@ impl Drop for App { |
| 3579 | 3600 | let _ = self.window.connection().inner().free_gc(self.gc); |
| 3580 | 3601 | } |
| 3581 | 3602 | } |
| 3603 | + |
| 3604 | +/// Guess MIME type from file extension. |
| 3605 | +fn guess_mime_type(path: &std::path::Path) -> String { |
| 3606 | + let ext = path.extension() |
| 3607 | + .and_then(|e| e.to_str()) |
| 3608 | + .map(|e| e.to_lowercase()) |
| 3609 | + .unwrap_or_default(); |
| 3610 | + |
| 3611 | + match ext.as_str() { |
| 3612 | + // Images |
| 3613 | + "png" => "image/png", |
| 3614 | + "jpg" | "jpeg" => "image/jpeg", |
| 3615 | + "gif" => "image/gif", |
| 3616 | + "webp" => "image/webp", |
| 3617 | + "svg" => "image/svg+xml", |
| 3618 | + "bmp" => "image/bmp", |
| 3619 | + "ico" => "image/x-icon", |
| 3620 | + // Documents |
| 3621 | + "pdf" => "application/pdf", |
| 3622 | + "txt" => "text/plain", |
| 3623 | + "md" => "text/markdown", |
| 3624 | + "html" | "htm" => "text/html", |
| 3625 | + "css" => "text/css", |
| 3626 | + "js" => "text/javascript", |
| 3627 | + "json" => "application/json", |
| 3628 | + "xml" => "application/xml", |
| 3629 | + // Code |
| 3630 | + "rs" => "text/x-rust", |
| 3631 | + "py" => "text/x-python", |
| 3632 | + "c" | "h" => "text/x-c", |
| 3633 | + "cpp" | "hpp" | "cc" => "text/x-c++", |
| 3634 | + "java" => "text/x-java", |
| 3635 | + "go" => "text/x-go", |
| 3636 | + "lua" => "text/x-lua", |
| 3637 | + "sh" | "bash" => "application/x-shellscript", |
| 3638 | + "toml" => "application/toml", |
| 3639 | + "yaml" | "yml" => "application/x-yaml", |
| 3640 | + // Archives |
| 3641 | + "zip" => "application/zip", |
| 3642 | + "tar" => "application/x-tar", |
| 3643 | + "gz" | "gzip" => "application/gzip", |
| 3644 | + "xz" => "application/x-xz", |
| 3645 | + "7z" => "application/x-7z-compressed", |
| 3646 | + "rar" => "application/vnd.rar", |
| 3647 | + // Audio |
| 3648 | + "mp3" => "audio/mpeg", |
| 3649 | + "wav" => "audio/wav", |
| 3650 | + "flac" => "audio/flac", |
| 3651 | + "ogg" => "audio/ogg", |
| 3652 | + "m4a" => "audio/mp4", |
| 3653 | + // Video |
| 3654 | + "mp4" => "video/mp4", |
| 3655 | + "mkv" => "video/x-matroska", |
| 3656 | + "avi" => "video/x-msvideo", |
| 3657 | + "webm" => "video/webm", |
| 3658 | + "mov" => "video/quicktime", |
| 3659 | + // Fallback |
| 3660 | + _ => "application/octet-stream", |
| 3661 | + }.to_string() |
| 3662 | +} |