From dc83bdb3fb6c423b8c7ba1a65e307fed13c131a4 Mon Sep 17 00:00:00 2001 From: Dmytro Kondakov Date: Tue, 22 Apr 2025 22:04:26 +0200 Subject: [PATCH] feat(screenshot): implement area screenshot functionality and integrate with shortcut management --- frontend/src-tauri/Cargo.lock | 5 ++- frontend/src-tauri/Cargo.toml | 1 + frontend/src-tauri/src/lib.rs | 5 +-- frontend/src-tauri/src/screenshot.rs | 53 ++++++++++++++++++++++++++++ frontend/src-tauri/src/shortcut.rs | 11 ++++-- 5 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 frontend/src-tauri/src/screenshot.rs diff --git a/frontend/src-tauri/Cargo.lock b/frontend/src-tauri/Cargo.lock index 7d384f8..e777b10 100644 --- a/frontend/src-tauri/Cargo.lock +++ b/frontend/src-tauri/Cargo.lock @@ -1,12 +1,13 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Haystack" version = "0.1.0" dependencies = [ "base64 0.21.7", + "chrono", "cocoa", "notify", "serde", @@ -536,8 +537,10 @@ checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link", ] diff --git a/frontend/src-tauri/Cargo.toml b/frontend/src-tauri/Cargo.toml index 893e2ec..359848d 100644 --- a/frontend/src-tauri/Cargo.toml +++ b/frontend/src-tauri/Cargo.toml @@ -28,6 +28,7 @@ base64 = "0.21.7" tokio = { version = "1.36.0", features = ["full"] } tauri-plugin-store = "2.0.0-beta.12" tauri-plugin-http = "2.0.0-beta.12" +chrono = "0.4" [target."cfg(target_os = \"macos\")".dependencies] cocoa = "0.26" diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index 37f3f80..b977eec 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -1,7 +1,8 @@ mod commands; -mod shortcut; +pub mod screenshot; +pub mod shortcut; mod state; -mod utils; +pub mod utils; mod window; use state::new_shared_watcher_state; diff --git a/frontend/src-tauri/src/screenshot.rs b/frontend/src-tauri/src/screenshot.rs new file mode 100644 index 0000000..8d58e68 --- /dev/null +++ b/frontend/src-tauri/src/screenshot.rs @@ -0,0 +1,53 @@ +use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; +use std::fs; +use std::process::Command; +use tauri::{AppHandle, Runtime}; + +/// Takes a screenshot of a selected area and returns the image data as base64 +pub fn take_area_screenshot(app: &AppHandle) -> Result { + // Create a temporary file path + let temp_dir = std::env::temp_dir(); + let timestamp = chrono::Local::now().format("%Y%m%d_%H%M%S"); + let temp_file = temp_dir.join(format!("haystack_screenshot_{}.png", timestamp)); + + // Use screencapture command with -i flag for interactive selection + let output = Command::new("screencapture") + .arg("-i") // interactive selection + .arg("-x") // don't play sound + .arg("-o") // don't show cursor + .arg("-r") // don't add shadow + .arg(temp_file.to_str().unwrap()) + .output() + .map_err(|e| format!("Failed to execute screencapture: {}", e))?; + + if !output.status.success() { + return Err(format!( + "screencapture failed: {}", + String::from_utf8_lossy(&output.stderr) + )); + } + + // Read the captured image + let contents = + fs::read(&temp_file).map_err(|e| format!("Failed to read screenshot file: {}", e))?; + + // Convert to base64 + let base64_string = BASE64.encode(&contents); + + // Clean up the temporary file + if let Err(e) = fs::remove_file(&temp_file) { + println!("Warning: Failed to remove temporary screenshot file: {}", e); + } + + // Log the base64 string (truncated for readability) + if base64_string.len() > 100 { + println!( + "Screenshot base64 (truncated): {}...", + &base64_string[..100] + ); + } else { + println!("Screenshot base64: {}", base64_string); + } + + Ok(base64_string) +} diff --git a/frontend/src-tauri/src/shortcut.rs b/frontend/src-tauri/src/shortcut.rs index 2808fc7..1875dc0 100644 --- a/frontend/src-tauri/src/shortcut.rs +++ b/frontend/src-tauri/src/shortcut.rs @@ -1,3 +1,4 @@ +use crate::screenshot::take_area_screenshot; use tauri::App; use tauri::AppHandle; use tauri::Emitter; @@ -200,6 +201,9 @@ fn register_shortcut_upon_start( // TODO: Implement screenshot functionality println!("Screenshot shortcut pressed"); } + if let Err(e) = take_area_screenshot(app) { + println!("Failed to take screenshot: {}", e); + } } }) .build(), @@ -268,11 +272,12 @@ pub fn change_screenshot_shortcut( fn register_screenshot_shortcut(app: &AppHandle, shortcut: Shortcut) { app.global_shortcut() - .on_shortcut(shortcut, move |_app, scut, event| { + .on_shortcut(shortcut, move |app, scut, event| { if scut == &shortcut { if let ShortcutState::Pressed = event.state() { - // TODO: Implement screenshot functionality - println!("Screenshot shortcut pressed"); + if let Err(e) = take_area_screenshot(app) { + println!("Failed to take screenshot: {}", e); + } } } })