diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index d38d6f3..0ba008f 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,4 +1,5 @@ mod managers; +mod shortcut; use log::info; use managers::audio::AudioRecordingManager; @@ -9,47 +10,7 @@ use std::{thread, time}; use tauri::tray::TrayIconBuilder; use tauri::{AppHandle, Manager}; use tauri_plugin_autostart::MacosLauncher; -use tauri_plugin_clipboard_manager::ClipboardExt; - -fn try_send_event(event: &EventType) { - if let Err(SimulateError) = simulate(event) { - println!("We could not send {:?}", event); - } -} - -fn send(event: EventType) { - try_send_event(&event); - thread::sleep(time::Duration::from_millis(60)); -} - -fn send_paste() { - // Determine the modifier key based on the OS - #[cfg(target_os = "macos")] - let modifier_key = Key::MetaLeft; // Command key on macOS - #[cfg(not(target_os = "macos"))] - let modifier_key = Key::ControlLeft; // Control key on other systems - - // Press both keys - send(EventType::KeyPress(modifier_key)); - send(EventType::KeyPress(Key::KeyV)); - - // Release both keys - send(EventType::KeyRelease(Key::KeyV)); - send(EventType::KeyRelease(modifier_key)); -} - -fn paste(text: String, app_handle: tauri::AppHandle) { - let clipboard = app_handle.clipboard(); - - // get the current clipboard content - let clipboard_content = clipboard.read_text().unwrap_or_default(); - - clipboard.write_text(&text).unwrap(); - send_paste(); - - // restore the clipboard - clipboard.write_text(&clipboard_content).unwrap(); -} +use tauri_plugin_global_shortcut::{Code, Modifiers, Shortcut, ShortcutEvent, ShortcutState}; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { @@ -67,7 +28,7 @@ pub fn run() { .plugin(tauri_plugin_macos_permissions::init()) .plugin(tauri_plugin_opener::init()) .setup(move |app| { - let tray = TrayIconBuilder::new().build(app)?; + let _tray = TrayIconBuilder::new().build(app)?; let vad_path = app.path().resolve( "resources/silero_vad_v4.onnx", @@ -96,60 +57,7 @@ pub fn run() { app.manage(recording_manager.clone()); app.manage(transcription_manager.clone()); - #[cfg(desktop)] - { - use tauri_plugin_global_shortcut::{Code, Modifiers, ShortcutState}; - - app.handle().plugin( - tauri_plugin_global_shortcut::Builder::new() - .with_shortcuts(["alt+space", "ctrl+d"])? // Register "alt+space" - .with_handler(move |app_handle_from_plugin, shortcut, event| { - // Retrieve managers from state - let recording_manager_state = - app_handle_from_plugin.state::>(); - let transcription_manager_state = - app_handle_from_plugin.state::>(); - 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_state.try_start_recording("alt-space"); - } else if event.state == ShortcutState::Released { - info!("Alt+Space released! (Global Shortcut)"); - // Clone Arcs for the async block - let rm_clone = recording_manager_state.inner().clone(); - let tm_clone = transcription_manager_state.inner().clone(); - - tauri::async_runtime::spawn(async move { - if let Some(samples) = rm_clone.stop_recording("alt-space") - { - match tm_clone.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 - ), - } - } - }); - } - } - }) - .build(), - )?; - } + shortcut::enable_shortcut(app); Ok(()) }) diff --git a/src-tauri/src/shortcut.rs b/src-tauri/src/shortcut.rs new file mode 100644 index 0000000..60a00be --- /dev/null +++ b/src-tauri/src/shortcut.rs @@ -0,0 +1,113 @@ +use std::sync::Arc; +use std::thread; +use std::time; + +use rdev::{simulate, EventType, Key, SimulateError}; +use tauri::App; +use tauri::AppHandle; +use tauri::Manager; +use tauri_plugin_clipboard_manager::ClipboardExt; +use tauri_plugin_global_shortcut::GlobalShortcutExt; +use tauri_plugin_global_shortcut::{Shortcut, ShortcutState}; +use tauri_plugin_store::{JsonValue, StoreExt}; + +use crate::managers::audio::AudioRecordingManager; +use crate::managers::transcription::TranscriptionManager; + +fn try_send_event(event: &EventType) { + if let Err(SimulateError) = simulate(event) { + println!("We could not send {:?}", event); + } +} + +fn send(event: EventType) { + try_send_event(&event); + thread::sleep(time::Duration::from_millis(60)); +} + +fn send_paste() { + // Determine the modifier key based on the OS + #[cfg(target_os = "macos")] + let modifier_key = Key::MetaLeft; // Command key on macOS + #[cfg(not(target_os = "macos"))] + let modifier_key = Key::ControlLeft; // Control key on other systems + + // Press both keys + send(EventType::KeyPress(modifier_key)); + send(EventType::KeyPress(Key::KeyV)); + + // Release both keys + send(EventType::KeyRelease(Key::KeyV)); + send(EventType::KeyRelease(modifier_key)); +} + +fn paste(text: String, app_handle: AppHandle) { + let clipboard = app_handle.clipboard(); + + // get the current clipboard content + let clipboard_content = clipboard.read_text().unwrap_or_default(); + + clipboard.write_text(&text).unwrap(); + send_paste(); + + // restore the clipboard + clipboard.write_text(&clipboard_content).unwrap(); +} + +// const HANDY_TAURI_STORE: &str = "handy_tauri_store"; + +// let mut shortcut_map: HashMap = HashMap::new(); + +fn transcribe_pressed(app: &AppHandle) { + let rm = app.state::>(); + rm.try_start_recording("transcribe"); +} + +fn transcribe_released(app: &AppHandle) { + let ah = app.clone(); + let rm = Arc::clone(&app.state::>()); + let tm = Arc::clone(&app.state::>()); + + tauri::async_runtime::spawn(async move { + if let Some(samples) = rm.stop_recording("transcribe") { + match tm.transcribe(samples) { + // Not .await, as transcribe is synchronous + Ok(transcription) => { + println!("Global Shortcut Transcription: {}", transcription); + paste(transcription, ah); + } + Err(err) => println!("Global Shortcut Transcription error: {}", err), + } + } + }); +} + +pub fn enable_shortcut(app: &App) { + _register_shortcut_upon_start( + app, + "alt+space" + .parse::() + .expect("Failed to parse shortcut"), + ); +} + +fn _register_shortcut_upon_start(app: &App, shortcut: Shortcut) { + // Initialize global shortcut and set its handler + app.handle() + .plugin( + tauri_plugin_global_shortcut::Builder::new() + .with_handler(move |handler_app, scut, event| { + if scut == &shortcut { + println!("Global Shortcut pressed! {}", scut.into_string()); + if event.state == ShortcutState::Pressed { + transcribe_pressed(handler_app); + } else if event.state == ShortcutState::Released { + transcribe_released(handler_app); + } + } + }) + .build(), + ) + .unwrap(); + app.global_shortcut().register(shortcut).unwrap(); // Register global shortcut +} diff --git a/src/components/settings/KeyboardShortcuts.tsx b/src/components/settings/KeyboardShortcuts.tsx index 3960d48..4cf807c 100644 --- a/src/components/settings/KeyboardShortcuts.tsx +++ b/src/components/settings/KeyboardShortcuts.tsx @@ -37,9 +37,6 @@ export const KeyboardShortcuts: React.FC = () => { {key} - {index < shortcut.keys.length - 1 && ( - + - )} ))}