diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a9c590..576d35c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,15 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and _Nothing yet._ -[Unreleased]: https://github.com/noahbclarkson/codebase_viewer/compare/v0.1.1...HEAD +[Unreleased]: https://github.com/noahbclarkson/codebase_viewer/compare/v0.1.2...HEAD + +--- + +## [0.1.2] - 2025-06-04 + +### Added +- Copy generated reports directly to the clipboard via a new button. +- `arboard` dependency for cross-platform clipboard access. --- @@ -36,6 +44,7 @@ _Nothing yet._ - Explicit revision pin for **cross** install. [0.1.1]: https://github.com/noahbclarkson/codebase_viewer/releases/tag/v0.1.1 +[0.1.2]: https://github.com/noahbclarkson/codebase_viewer/releases/tag/v0.1.2 --- diff --git a/Cargo.lock b/Cargo.lock index 70e0c64..dc26458 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -794,9 +794,10 @@ dependencies = [ [[package]] name = "codebase_viewer" -version = "0.1.1" +version = "0.1.2" dependencies = [ "anyhow", + "arboard", "chrono", "crossbeam-channel", "dark-light", diff --git a/Cargo.toml b/Cargo.toml index 2b0da91..069de71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "codebase_viewer" -version = "0.1.1" +version = "0.1.2" edition = "2021" authors = ["Noah Clarkson "] description = "A cross-platform desktop tool written in Rust to scan, explore, and document codebases using egui." @@ -38,6 +38,7 @@ image = { version = "0.25", default-features = false, features = ["png", "jpeg", regex = "1.11" egui-phosphor = "0.9" egui_material_icons = "0.3.0" +arboard = "3.5" [profile.release] codegen-units = 1 diff --git a/src/app.rs b/src/app.rs index cc250a6..cb8d630 100644 --- a/src/app.rs +++ b/src/app.rs @@ -11,6 +11,7 @@ use crate::{ task::{ScanMessage, TaskMessage}, // Use items from task module ui, // Use the ui module's drawing functions }; +use arboard::Clipboard; use crossbeam_channel::{Receiver, Sender}; use egui::{style::Visuals, CentralPanel, Context, Key, Modifiers, SidePanel}; use rfd::MessageDialogResult; @@ -146,6 +147,8 @@ pub(crate) enum AppAction { LoadSelection, /// Generate a report with the specified options. GenerateReport(ReportOptions), + /// Generate a report and copy the output to the clipboard. + CopyReport(ReportOptions), /// Start scanning a new directory path. StartScan(PathBuf), /// Request cancellation of the currently running scan. @@ -245,6 +248,7 @@ impl CodebaseApp { AppAction::SaveSelection => self.perform_save_selection(), AppAction::LoadSelection => self.perform_load_selection(), AppAction::GenerateReport(opts) => self.perform_generate_report(opts), + AppAction::CopyReport(opts) => self.perform_copy_report(opts), AppAction::StartScan(path) => self.perform_start_scan(path), AppAction::CancelScan => self.perform_cancel_scan(), AppAction::FocusSearchBox => self.perform_focus_search_box(), @@ -647,6 +651,56 @@ impl CodebaseApp { } } + /// Generates a report and copies the content to the clipboard. + fn perform_copy_report(&mut self, options: ReportOptions) { + if self.is_scanning || self.is_generating_report { + log::warn!("Cannot copy report: Another background task is running."); + self.status_message = "Busy with another task (scan/report).".to_string(); + return; + } + if self.root_path.is_none() || self.root_id.is_none() { + self.status_message = "No directory open to generate report from.".to_string(); + log::warn!("Copy report attempted with no directory open."); + return; + } + + match report::generate_report(self, &options) { + Ok(content) => match Clipboard::new() { + Ok(mut clipboard) => { + if let Err(e) = clipboard.set_text(content) { + log::error!("Failed to copy report to clipboard: {}", e); + self.status_message = format!("Error copying report: {}", e); + rfd::MessageDialog::new() + .set_level(rfd::MessageLevel::Error) + .set_title("Copy Report Failed") + .set_description(format!("Could not copy report:\n{}", e)) + .show(); + } else { + self.status_message = "Report copied to clipboard.".to_string(); + } + } + Err(e) => { + log::error!("Failed to access clipboard: {}", e); + self.status_message = format!("Clipboard error: {}", e); + rfd::MessageDialog::new() + .set_level(rfd::MessageLevel::Error) + .set_title("Clipboard Error") + .set_description(format!("Could not access clipboard:\n{}", e)) + .show(); + } + }, + Err(e) => { + log::error!("Failed to generate report for clipboard: {}", e); + self.status_message = format!("Error generating report: {}", e); + rfd::MessageDialog::new() + .set_level(rfd::MessageLevel::Error) + .set_title("Report Generation Failed") + .set_description(format!("Could not generate report:\n{}", e)) + .show(); + } + } + } + /// Sets the focus flag for the search box. fn perform_focus_search_box(&mut self) { self.focus_search_box = true; diff --git a/src/ui/dialogs.rs b/src/ui/dialogs.rs index f598dac..7e2e698 100644 --- a/src/ui/dialogs.rs +++ b/src/ui/dialogs.rs @@ -202,6 +202,7 @@ pub fn draw_report_options_window(app: &mut CodebaseApp, ctx: &Context) { } let mut generate_clicked = false; + let mut copy_clicked = false; let mut cancel_clicked = false; let mut is_open = true; @@ -266,6 +267,16 @@ pub fn draw_report_options_window(app: &mut CodebaseApp, ctx: &Context) { { generate_clicked = true; } + if ui + .add_enabled( + !app.is_scanning && !app.is_generating_report, + Button::new("Copy to Clipboard"), + ) + .on_hover_text("Copy the report to the clipboard") + .clicked() + { + copy_clicked = true; + } if app.is_scanning || app.is_generating_report { ui.label(RichText::new("Busy...").color(Color32::RED).small()); } @@ -290,6 +301,15 @@ pub fn draw_report_options_window(app: &mut CodebaseApp, ctx: &Context) { app.show_report_options_window = false; // Close the dialog } } + + // Handle Copy button click + if copy_clicked { + if let Some(opts) = app.report_options_draft.take() { + app.last_report_options = opts.clone(); + app.queue_action(AppAction::CopyReport(opts)); + app.show_report_options_window = false; + } + } } /// Draws the About window (modal).