diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9874cd5..ced325e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -82,7 +82,10 @@ pub fn run() { } _ => {} }) - .invoke_handler(tauri::generate_handler![shortcut::set_binding]) + .invoke_handler(tauri::generate_handler![ + shortcut::change_binding, + shortcut::reset_binding + ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 4e3dd63..b9280c8 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -1,7 +1,9 @@ -use serde::Deserialize; -use serde::Serialize; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, sync::Arc}; +use tauri::{App, AppHandle, Runtime}; +use tauri_plugin_store::{Store, StoreExt}; -#[derive(Serialize, Deserialize, Debug, Clone)] // Clone is useful +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct ShortcutBinding { pub id: String, pub name: String, @@ -10,28 +12,106 @@ pub struct ShortcutBinding { pub current_binding: String, } -#[derive(Serialize, Deserialize, Debug)] +/* still handy for composing the initial JSON in the store ------------- */ +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct AppSettings { - pub bindings: Vec, + pub bindings: HashMap, } +pub const SETTINGS_STORE_PATH: &str = "settings_store.json"; + pub fn get_default_settings() -> AppSettings { - AppSettings { - bindings: vec![ - ShortcutBinding { - id: "transcribe".to_string(), - name: "Transcribe".to_string(), - description: "Converts your speech into text.".to_string(), - default_binding: "alt+space".to_string(), - current_binding: "alt+space".to_string(), - }, - ShortcutBinding { - id: "test".to_string(), - name: "Test".to_string(), - description: "This is a test binding.".to_string(), - default_binding: "ctrl+d".to_string(), - current_binding: "ctrl+d".to_string(), - }, - ], - } + let mut bindings = HashMap::new(); + bindings.insert( + "transcribe".to_string(), + ShortcutBinding { + id: "transcribe".to_string(), + name: "Transcribe".to_string(), + description: "Converts your speech into text.".to_string(), + default_binding: "alt+space".to_string(), + current_binding: "alt+space".to_string(), + }, + ); + bindings.insert( + "test".to_string(), + ShortcutBinding { + id: "test".to_string(), + name: "Test".to_string(), + description: "This is a test binding.".to_string(), + default_binding: "ctrl+d".to_string(), + current_binding: "ctrl+d".to_string(), + }, + ); + + AppSettings { bindings } +} + +pub fn load_or_create_app_settings(app: &App) -> AppSettings { + // Initialize store + let store = app + .store(SETTINGS_STORE_PATH) + .expect("Failed to initialize store"); + + let settings = if let Some(settings_value) = store.get("settings") { + // Parse the entire settings object + match serde_json::from_value::(settings_value) { + Ok(settings) => { + println!("Found existing settings: {:?}", settings); + + settings + } + Err(e) => { + println!("Failed to parse settings: {}", e); + // Fall back to default settings if parsing fails + let default_settings = get_default_settings(); + + // Store the default settings + store.set("settings", serde_json::to_value(&default_settings).unwrap()); + + default_settings + } + } + } else { + // Create default settings + let default_settings = get_default_settings(); + + // Store the settings + store.set("settings", serde_json::to_value(&default_settings).unwrap()); + + default_settings + }; + + settings +} + +pub fn get_settings(app: &AppHandle) -> AppSettings { + let store = app + .store(SETTINGS_STORE_PATH) + .expect("Failed to initialize store"); + + let settings = serde_json::from_value::(store.get("settings").unwrap()).unwrap(); + + settings +} + +pub fn write_settings(app: &AppHandle, settings: AppSettings) { + let store = app + .store(SETTINGS_STORE_PATH) + .expect("Failed to initialize store"); + + store.set("settings", serde_json::to_value(&settings).unwrap()); +} + +pub fn get_bindings(app: &AppHandle) -> HashMap { + let settings = get_settings(app); + + settings.bindings +} + +pub fn get_stored_binding(app: &AppHandle, id: &str) -> ShortcutBinding { + let bindings = get_bindings(app); + + let binding = bindings.get(id).unwrap().clone(); + + binding } diff --git a/src-tauri/src/shortcut.rs b/src-tauri/src/shortcut.rs index 81c8b1b..c9680c7 100644 --- a/src-tauri/src/shortcut.rs +++ b/src-tauri/src/shortcut.rs @@ -3,18 +3,14 @@ use std::sync::Arc; use tauri::App; use tauri::AppHandle; use tauri::Manager; -use tauri::Runtime; use tauri_plugin_global_shortcut::GlobalShortcutExt; use tauri_plugin_global_shortcut::{Shortcut, ShortcutState}; -use tauri_plugin_store::StoreExt; use crate::managers::audio::AudioRecordingManager; use crate::managers::transcription::TranscriptionManager; use crate::settings; -use crate::settings::AppSettings; use crate::settings::ShortcutBinding; use crate::utils; -use crate::AppState; fn transcribe_pressed(app: &AppHandle) { let rm = app.state::>(); @@ -41,142 +37,68 @@ fn transcribe_released(app: &AppHandle) { } pub fn init_shortcuts(app: &App) { - // Initialize store - let kb_store = app - .store("settings_store.json") - .expect("Failed to initialize store"); - - // Get or create settings - let settings = if let Some(settings_value) = kb_store.get("settings") { - // Parse the entire settings object - match serde_json::from_value::(settings_value) { - Ok(settings) => { - println!("Found existing settings: {:?}", settings); - - settings - } - Err(e) => { - println!("Failed to parse settings: {}", e); - // Fall back to default settings if parsing fails - let default_settings = settings::get_default_settings(); - - // Store the default settings - kb_store.set("settings", serde_json::to_value(&default_settings).unwrap()); - - default_settings - } - } - } else { - // Create default settings - let default_settings = settings::get_default_settings(); - - // Store the settings - kb_store.set("settings", serde_json::to_value(&default_settings).unwrap()); - - default_settings - }; + let settings = settings::load_or_create_app_settings(app); // Register shortcuts with the bindings from settings - _register_shortcuts(app, settings.bindings); + for (_id, binding) in settings.bindings { + _register_shortcut(app.handle(), binding); + } } #[tauri::command] -pub fn set_binding(app: AppHandle, id: String, binding: String) { - let app_state = app.state::(); - let mut active_bindings = app_state.active_bindings.lock().unwrap(); - let old_binding_str = active_bindings.get(&id).cloned(); // Clone to avoid borrowing issues +pub fn change_binding(app: AppHandle, id: String, binding: String) -> ShortcutBinding { + let mut settings = settings::get_settings(&app); - match binding.parse::() { - Ok(shortcut) => { - println!("Shortcut '{}' is valid", shortcut); - // unregister the existing binding - if let Some(old_binding_str) = old_binding_str { - match old_binding_str.parse::() { - Ok(old_binding) => { - app.global_shortcut() - .unregister(old_binding) - .expect("Failed to unregister shortcut"); - } - Err(e) => { - eprintln!("Error parsing old shortcut '{}': {:?}", old_binding_str, e); - } - } - } else { - println!("No existing shortcut to unregister"); - } + // Get the binding to modify + let binding_to_modify = settings.bindings.get(&id).unwrap().clone(); - // register the new binding - app.global_shortcut() - .on_shortcut(shortcut, 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); - } - } - }) - .expect("couldnt register shortcut"); + // Unregister the existing binding + _unregister_shortcut(&app, binding_to_modify.clone()); - // TODO error handling? - active_bindings.insert(id, binding); - } - Err(e) => { - eprintln!("Error parsing shortcut '{}': {:?}", binding, e); - } - } + // Create an updated binding + let mut updated_binding = binding_to_modify; + updated_binding.current_binding = binding; + + // Register the new binding + _register_shortcut(&app, updated_binding.clone()); + + // Update the binding in the settings + settings.bindings.insert(id, updated_binding.clone()); + + // Save the settings + settings::write_settings(&app, settings); + + // Return the updated binding + updated_binding } -fn _register_shortcuts(app: &App, bindings: Vec) { - // get bindings from state - let app_state = app.state::(); - let mut active_bindings = app_state.active_bindings.lock().unwrap(); +#[tauri::command] +pub fn reset_binding(app: AppHandle, id: String) -> ShortcutBinding { + let binding = settings::get_stored_binding(&app, &id); + return change_binding(app, id, binding.default_binding); +} - // iterate through bindings - for binding in bindings { - // Parse the shortcut, handling errors gracefully - match binding.current_binding.parse::() { - Ok(shortcut) => { - // Try to register the shortcut - match app.global_shortcut().on_shortcut( - shortcut, - 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); - } - } - }, - ) { - Ok(_) => { - // Additional actions on successful registration - println!( - "Successfully registered shortcut: {}", - binding.current_binding - ); +fn _register_shortcut(app: &AppHandle, binding: ShortcutBinding) { + let shortcut = binding.current_binding.parse::().unwrap(); - active_bindings.insert(binding.id, binding.current_binding); - } - Err(err) => { - // Log registration error - eprintln!( - "Failed to register shortcut {}: {}", - binding.current_binding, err - ); - } + app.global_shortcut() + .on_shortcut(shortcut, 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); } } - Err(err) => { - // Log parsing error - eprintln!( - "Failed to parse shortcut {}: {}", - binding.current_binding, err - ); - } - } - } + }) + .expect("couldnt register shortcut"); +} + +fn _unregister_shortcut(app: &AppHandle, binding: ShortcutBinding) { + let shortcut = binding.current_binding.parse::().unwrap(); + + app.global_shortcut() + .unregister(shortcut) + .expect("couldnt unregister shortcut"); } diff --git a/src/components/settings/KeyboardShortcuts.tsx b/src/components/settings/KeyboardShortcuts.tsx index ce0f341..db242af 100644 --- a/src/components/settings/KeyboardShortcuts.tsx +++ b/src/components/settings/KeyboardShortcuts.tsx @@ -1,9 +1,15 @@ import React, { useEffect } from "react"; import { load } from "@tauri-apps/plugin-store"; -import { SettingsSchema, ShortcutBinding } from "../../lib/types"; +import { + SettingsSchema, + ShortcutBinding, + ShortcutBindingSchema, + ShortcutBindingsMap, +} from "../../lib/types"; +import { invoke } from "@tauri-apps/api/core"; export const KeyboardShortcuts: React.FC = () => { - const [bindings, setBindings] = React.useState([]); + const [bindings, setBindings] = React.useState({}); useEffect(() => { load("settings_store.json", { autoSave: false }).then((r) => { @@ -14,13 +20,22 @@ export const KeyboardShortcuts: React.FC = () => { setBindings(settings.bindings); }); }); + + // setTimeout(() => { + // console.log("invoked set binding"); + // invoke("change_binding", { id: "test", binding: "alt+d" }).then((b) => { + // const newBinding = ShortcutBindingSchema.parse(b); + // console.log(bindings); + // setBindings((prev) => ({ ...prev, [newBinding.id]: newBinding })); + // }); + // }, 1000); }, []); return (
- {bindings.map((binding) => ( + {Object.entries(bindings).map(([id, binding]) => (
@@ -30,13 +45,20 @@ export const KeyboardShortcuts: React.FC = () => {

{binding.description}

- - {/* */} -
- {binding.current_binding} -
- {/*
*/} -
+
+ {binding.current_binding} +
+
))} diff --git a/src/lib/types.ts b/src/lib/types.ts index 0fcf4ad..3ea56e3 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -8,9 +8,15 @@ export const ShortcutBindingSchema = z.object({ current_binding: z.string(), }); +export const ShortcutBindingsMapSchema = z.record( + z.string(), + ShortcutBindingSchema +); + export const SettingsSchema = z.object({ - bindings: z.array(ShortcutBindingSchema), + bindings: ShortcutBindingsMapSchema, }); export type ShortcutBinding = z.infer; +export type ShortcutBindingsMap = z.infer; export type Settings = z.infer;