Handy/src-tauri/src/lib.rs
2025-05-06 11:19:23 -07:00

189 lines
6.9 KiB
Rust

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};
use std::{thread, time};
use tauri::{Emitter, 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();
}
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();
tauri::Builder::default()
.plugin(tauri_plugin_stronghold::Builder::new(|_pass| todo!()).build())
.plugin(tauri_plugin_upload::init())
// .plugin(tauri_plugin_global_shortcut::Builder::new().build())
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_autostart::init(
MacosLauncher::LaunchAgent,
Some(vec!["--auto-launch"]),
))
.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,
)?;
let whisper_path = app.path().resolve(
"resources/ggml-small.bin",
tauri::path::BaseDirectory::Resource,
)?;
let recording_manager = Arc::new(
AudioRecordingManager::new(&vad_path)
.expect("Failed to initialize recording manager"),
);
let transcription_manager = Arc::new(
TranscriptionManager::new(
whisper_path
.to_str()
.expect("Path contains invalid UTF-8 Chars"),
)
.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};
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");
}
}
})
.build(),
)?;
}
Ok(())
})
.on_window_event(|app, event| match event {
tauri::WindowEvent::CloseRequested { api, .. } => {
app.hide().unwrap();
api.prevent_close();
}
_ => {}
})
.invoke_handler(tauri::generate_handler![])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}