diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3e8b51c..ec51c68 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -2,7 +2,6 @@ mod managers; use log::info; use managers::audio::AudioRecordingManager; -use managers::keybinding::KeyBindingManager; use managers::transcription::TranscriptionManager; use rdev::{simulate, EventType, Key, SimulateError}; use std::sync::{Arc, Mutex}; @@ -51,33 +50,6 @@ fn paste(text: String, app_handle: tauri::AppHandle) { clipboard.write_text(&clipboard_content).unwrap(); } -fn register_bindings(manager: &mut KeyBindingManager) { - manager.register( - "ctrl-meta".to_string(), - vec![Key::ControlRight, Key::MetaRight], - |ctx| { - info!("Ctrl+Meta pressed!"); - ctx.recording_manager.try_start_recording("ctrl-meta"); - None - }, - |ctx| { - info!("release being called from ctrl-meta"); - let ctx = ctx.clone(); - Some(tauri::async_runtime::spawn(async move { - if let Some(samples) = ctx.recording_manager.stop_recording("ctrl-meta") { - match ctx.transcription_manager.transcribe(samples) { - Ok(transcription) => { - println!("Transcription: {}", transcription); - paste(transcription, ctx.app_handle.clone()); - } - Err(err) => println!("Transcription error: {}", err), - } - } - })) - }, - ); -} - #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { env_logger::init(); @@ -94,8 +66,6 @@ pub fn run() { .plugin(tauri_plugin_macos_permissions::init()) .plugin(tauri_plugin_opener::init()) .setup(move |app| { - let app_handle = app.handle().clone(); - let vad_path = app.path().resolve( "resources/silero_vad_v4.onnx", tauri::path::BaseDirectory::Resource, @@ -119,54 +89,54 @@ pub fn run() { .expect("Failed to initialize transcription manager"), ); - let manager = Arc::new(Mutex::new(KeyBindingManager::new( - recording_manager.clone(), - transcription_manager.clone(), - // claude_client.clone(), - app_handle.clone(), - ))); - - { - let mut manager = manager.lock().unwrap(); - register_bindings(&mut manager); - } - - let manager_clone = manager.clone(); - tauri::async_runtime::spawn(async move { - rdev::listen(move |event| { - if let Ok(manager) = manager_clone.lock() { - manager.handle_event(&event); - } - }) - .unwrap(); - }); - #[cfg(desktop)] { use tauri_plugin_global_shortcut::{Code, Modifiers, ShortcutState}; + // Clone resources needed for the global shortcut handler + let rm_for_shortcut_handler = recording_manager.clone(); + let tm_for_shortcut_handler = transcription_manager.clone(); + app.handle().plugin( tauri_plugin_global_shortcut::Builder::new() - .with_shortcuts(["ctrl+d", "alt+space"])? - .with_handler(|app, shortcut, event| { - if event.state == ShortcutState::Pressed { - if shortcut.matches(Modifiers::CONTROL, Code::KeyD) { - let _ = app.emit("shortcut-event", "Ctrl+D triggered"); - println!("Ctrl+D triggered"); - } - if shortcut.matches(Modifiers::ALT, Code::Space) { - let _ = app.emit("shortcut-event", "Alt+Space triggered"); - println!("Alt+Space triggered"); - } - } - if event.state == ShortcutState::Released { - if shortcut.matches(Modifiers::CONTROL, Code::KeyD) { - let _ = app.emit("shortcut-event", "Ctrl+D released"); - println!("Ctrl+D released"); - } - if shortcut.matches(Modifiers::ALT, Code::Space) { - let _ = app.emit("shortcut-event", "Alt+Space released"); - println!("Alt+Space released"); + .with_shortcuts(["alt+space"])? // Register "alt+space" + .with_handler(move |app_handle_from_plugin, shortcut, event| { + // Clone Arcs for potential async tasks to move ownership + let recording_manager = rm_for_shortcut_handler.clone(); + let transcription_manager = tm_for_shortcut_handler.clone(); + let app_handle_for_async_tasks = app_handle_from_plugin.clone(); + + if shortcut.matches(Modifiers::ALT, Code::Space) { + if event.state == ShortcutState::Pressed { + info!("Alt+Space pressed! (Global Shortcut)"); + // Use the "alt-space" identifier for consistency + recording_manager.try_start_recording("alt-space"); + } else if event.state == ShortcutState::Released { + info!("Alt+Space released! (Global Shortcut)"); + tauri::async_runtime::spawn(async move { + // recording_manager, transcription_manager, and app_handle_for_async_tasks are moved into this async block + if let Some(samples) = + recording_manager.stop_recording("alt-space") + { + match transcription_manager.transcribe(samples) { + // Not .await, as transcribe is synchronous + Ok(transcription) => { + println!( + "Global Shortcut Transcription: {}", + transcription + ); + paste( + transcription, + app_handle_for_async_tasks, + ); + } + Err(err) => println!( + "Global Shortcut Transcription error: {}", + err + ), + } + } + }); } } }) diff --git a/src-tauri/src/managers/keybinding.rs b/src-tauri/src/managers/keybinding.rs deleted file mode 100644 index c42cf8a..0000000 --- a/src-tauri/src/managers/keybinding.rs +++ /dev/null @@ -1,130 +0,0 @@ -use super::audio::AudioRecordingManager; -use super::transcription::TranscriptionManager; -use rdev::{EventType, Key}; -use std::collections::HashSet; -use std::sync::{Arc, Mutex}; - -type KeySet = HashSet; - -#[derive(Clone)] -pub struct BindingContext { - pub recording_manager: Arc, - pub transcription_manager: Arc, - // pub anthropic: Arc, - pub app_handle: tauri::AppHandle, -} - -pub struct KeyBinding { - id: String, - keys: KeySet, - on_press: Box< - dyn Fn(&BindingContext) -> Option> + Send + 'static, - >, - on_release: Box< - dyn Fn(&BindingContext) -> Option> + Send + 'static, - >, -} - -impl KeyBinding { - fn new(id: String, keys: Vec, on_press: F, on_release: G) -> Self - where - F: Fn(&BindingContext) -> Option> + Send + 'static, - G: Fn(&BindingContext) -> Option> + Send + 'static, - { - Self { - id, - keys: keys.into_iter().collect(), - on_press: Box::new(on_press), - on_release: Box::new(on_release), - } - } -} - -pub struct KeyBindingManager { - bindings: Vec, - global_pressed: Arc>>, - active_bindings: Arc>>, - context: BindingContext, -} - -impl KeyBindingManager { - pub fn new( - recording_manager: Arc, - transcription_manager: Arc, - // anthropic: Arc, - app_handle: tauri::AppHandle, - ) -> Self { - Self { - bindings: Vec::new(), - global_pressed: Arc::new(Mutex::new(HashSet::new())), - active_bindings: Arc::new(Mutex::new(HashSet::new())), - context: BindingContext { - recording_manager, - transcription_manager, - // anthropic, - app_handle, - }, - } - } - - pub fn register(&mut self, id: String, keys: Vec, on_press: F, on_release: G) - where - F: Fn(&BindingContext) -> Option> + Send + 'static, - G: Fn(&BindingContext) -> Option> + Send + 'static, - { - self.bindings - .push(KeyBinding::new(id, keys, on_press, on_release)); - } - - pub fn handle_event(&self, event: &rdev::Event) { - if let EventType::KeyPress(key) | EventType::KeyRelease(key) = event.event_type { - let mut is_press = matches!(event.event_type, EventType::KeyPress(_)); - - // Update global pressed keys - let mut pressed = self.global_pressed.lock().unwrap(); - - // If we get a release event for a key that wasn't pressed, - // treat it as a press event instead - if !is_press && !pressed.contains(&key) { - is_press = true; - } - - if is_press { - pressed.insert(key); - } else { - pressed.remove(&key); - } - let pressed_snapshot = pressed.clone(); - drop(pressed); - - // Check binding states - let mut active = self.active_bindings.lock().unwrap(); - for binding in &self.bindings { - let is_active = active.contains(&binding.id); - let all_required_pressed = binding.keys.is_subset(&pressed_snapshot); - let any_required_key = binding.keys.contains(&key); - - // Only process if this event is for a key we care about - if any_required_key { - match (all_required_pressed, is_active) { - (true, false) => { - (binding.on_press)(&self.context); - active.insert(binding.id.clone()); - } - (false, true) => { - // Only trigger release if none of the required keys are pressed - let any_binding_key_pressed = - binding.keys.iter().any(|k| pressed_snapshot.contains(k)); - - if !any_binding_key_pressed { - (binding.on_release)(&self.context); - active.remove(&binding.id); - } - } - _ => {} - } - } - } - } - } -} diff --git a/src-tauri/src/managers/mod.rs b/src-tauri/src/managers/mod.rs index 8a31278..a61d9ac 100644 --- a/src-tauri/src/managers/mod.rs +++ b/src-tauri/src/managers/mod.rs @@ -1,3 +1,2 @@ pub mod audio; -pub mod keybinding; pub mod transcription; diff --git a/src/App.tsx b/src/App.tsx index 0db8fe7..6a41559 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,6 +20,10 @@ function App() { } setHasAccessibility(hasPermissions); }); + + register("ctrl+d", () => { + console.log("Ctrl+D pressed"); + }); }, []); return (