use config::Config; // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ use enigo::{ Direction::{Press, Release}, Enigo, Key, Keyboard, }; use rdev::{listen, Event, EventType}; use std::sync::Mutex; use std::thread; use tauri::{ipc::Channel, Manager, State}; mod config; struct AppState { config: Config, } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_opener::init()) .invoke_handler(tauri::generate_handler![ get_config_path, update_config, get_default, get_theme, get_bind, start_listener, paste_text ]) .setup(|app| { let config = config::Config::load_or_create(get_config_path()).unwrap(); let theme = &config.theme; // Validate and set the theme let themes = vec!["grey", "night", "day", "catppuccin_mocha"]; if themes.contains(&theme.as_str()) { println!("Setting theme to: {}", theme); app.manage(Mutex::new(AppState { config: config })); } else { println!( "{}", format!("Theme {} is invalid! Valid themes are: {:?}", theme, themes) ); } Ok(()) }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } // Commands // Config commands // gross but i tried returning a struct and everything froze up #[tauri::command] fn get_config_path() -> std::path::PathBuf { let config_dir = dirs::config_dir().expect("Config directory not found"); config_dir.join("copybot") } // update the app state with new config #[tauri::command] fn update_config(state: State<'_, Mutex>) { let mut state = state.lock().unwrap(); state.config = config::Config::load_or_create(get_config_path()).unwrap(); } #[tauri::command] fn get_default(state: State<'_, Mutex>) -> Result { let default = state.lock().unwrap().config.default; Ok(default) } #[tauri::command] fn get_theme(state: State<'_, Mutex>) -> Result { let theme = state.lock().unwrap().config.theme.clone(); Ok(theme) } #[tauri::command] fn get_bind(state: State<'_, Mutex>) -> Result { let bind = state.lock().unwrap().config.bind.clone(); Ok(bind) } // Input events #[tauri::command] fn start_listener(bind: String, channel: Channel) { thread::spawn(move || { println!("Started listening for bind: {:?}", bind); listen(move |event| match event.event_type { EventType::KeyPress(key) | EventType::KeyRelease(key) => { if bind == serde_json::to_string(&key).unwrap().replace("\"", "") { channel.send(event).unwrap() }; } _ => (), }) }); } #[tauri::command] fn paste_text(state: State<'_, Mutex>, text: String) { println!("{}", text); let mut enigo = Enigo::new(&enigo::Settings::default()).unwrap(); if state.lock().unwrap().config.shift_enter_newline { for c in text.chars() { if c == '\n' { // Press Shift+Enter combination enigo.key(Key::Shift, Press).unwrap(); enigo.key(Key::Return, Press).unwrap(); enigo.key(Key::Return, Release).unwrap(); enigo.key(Key::Shift, Release).unwrap(); } else { // Type regular characters normally enigo.text(&c.to_string()).unwrap(); } } } else { enigo.text(text.as_str()).unwrap(); } }