use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher}; use std::fs; use std::path::PathBuf; use std::sync::mpsc::channel; use std::sync::Arc; use std::sync::Mutex; use tauri::AppHandle; use tauri::Emitter; use tauri::{WebviewUrl, WebviewWindowBuilder}; struct WatcherState { watcher: Option, } impl WatcherState { fn new() -> Self { Self { watcher: None } } } // Handle PNG file processing fn process_png_file(path: &PathBuf, app: AppHandle) -> Result<(), String> { println!("Processing PNG file: {}", path.display()); // Read the file let contents = fs::read(path).map_err(|e| format!("Failed to read file: {}", e))?; // Convert to base64 let base64_string = BASE64.encode(&contents); println!("Generated base64 string of length: {}", base64_string.len()); // Emit the base64 to frontend app.emit("png-processed", base64_string) .map_err(|e| format!("Failed to emit event: {}", e))?; println!("Successfully processed file: {}", path.display()); Ok(()) } #[tauri::command] async fn handle_selected_folder( path: String, state: tauri::State<'_, Arc>>, app: AppHandle, ) -> Result { let path_buf = PathBuf::from(&path); if !path_buf.exists() || !path_buf.is_dir() { return Err("Invalid directory path".to_string()); } // Stop existing watcher if any let mut state = state .lock() .map_err(|_| "Failed to lock state".to_string())?; state.watcher = None; // Create a channel to receive file system events let (tx, rx) = channel(); // Create a new watcher let mut watcher = RecommendedWatcher::new(tx, Config::default()) .map_err(|e| format!("Failed to create watcher: {}", e))?; // Start watching the directory watcher .watch(path_buf.as_ref(), RecursiveMode::Recursive) .map_err(|e| format!("Failed to watch directory: {}", e))?; // Store the watcher in state state.watcher = Some(watcher); let path_clone = path.clone(); let app_clone = app.clone(); tokio::spawn(async move { println!("Starting to watch directory: {}", path_clone); for res in rx { match res { Ok(event) => { println!("Received event: {:?}", event); match event.kind { notify::EventKind::Create(_) | notify::EventKind::Modify(_) => { for path in event.paths { println!("Processing path: {}", path.display()); if let Some(extension) = path.extension() { if extension.to_string_lossy().to_lowercase() == "png" { if let Err(e) = process_png_file(&path, app_clone.clone()) { eprintln!("Error processing PNG file: {}", e); } } } } } _ => {} } } Err(e) => eprintln!("Watch error: {:?}", e), } } }); Ok(format!("Now watching directory: {}", path)) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { let watcher_state = Arc::new(Mutex::new(WatcherState::new())); tauri::Builder::default() .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_opener::init()) .manage(watcher_state) .invoke_handler(tauri::generate_handler![handle_selected_folder]) .setup(|app| { let win_builder = WebviewWindowBuilder::new(app, "main", WebviewUrl::default()) .inner_size(480.0, 360.0) .resizable(true); // set transparent title bar only when building for macOS #[cfg(target_os = "macos")] let win_builder = win_builder.title_bar_style(TitleBarStyle::Transparent); let window = win_builder.build().unwrap(); // set background color only when building for macOS #[cfg(target_os = "macos")] { use cocoa::appkit::{NSColor, NSWindow}; use cocoa::base::{id, nil}; let ns_window = window.ns_window().unwrap() as id; unsafe { let bg_color = NSColor::colorWithRed_green_blue_alpha_( nil, 245.0 / 255.0, 245.0 / 255.0, 245.0 / 255.0, 1.0, ); ns_window.setBackgroundColor_(bg_color); } } Ok(()) }) .run(tauri::generate_context!()) .expect("error while running tauri application"); }