From 61214aa8fb00552ae62b6cdcd7f9864ef8d2e195 Mon Sep 17 00:00:00 2001 From: Leslie Leung Date: Mon, 4 May 2026 22:02:21 +0800 Subject: [PATCH] Fix hotkey binding replacement validation --- .../app/src-tauri/src/combo_hotkey.rs | 25 ++++++++++++++++++- openless-all/app/src-tauri/src/commands.rs | 20 +++++++++++---- openless-all/app/src-tauri/src/qa_hotkey.rs | 1 - 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/openless-all/app/src-tauri/src/combo_hotkey.rs b/openless-all/app/src-tauri/src/combo_hotkey.rs index dd8d7144..68244825 100644 --- a/openless-all/app/src-tauri/src/combo_hotkey.rs +++ b/openless-all/app/src-tauri/src/combo_hotkey.rs @@ -97,7 +97,6 @@ impl ComboHotkeyMonitor { return Ok(()); } } - current.take(); let runtime = GlobalHotkeyRuntime::shared() .map_err(|e| ComboHotkeyError::ManagerInitFailed(e.to_string()))?; let (registered, rx) = runtime @@ -202,4 +201,28 @@ mod tests { Err(ComboHotkeyError::UnsupportedKey(_)) )); } + + #[test] + fn bare_shift_is_rejected_for_combo_hotkey() { + let binding = ShortcutBinding { + primary: "Shift".into(), + modifiers: vec![], + }; + assert!(matches!( + validate_binding(&binding), + Err(ComboHotkeyError::UnsupportedKey(_)) + )); + } + + #[test] + fn legacy_modifier_only_is_rejected_for_combo_hotkey() { + let binding = ShortcutBinding { + primary: "RightOption".into(), + modifiers: vec![], + }; + assert!(matches!( + validate_binding(&binding), + Err(ComboHotkeyError::UnsupportedKey(_)) + )); + } } diff --git a/openless-all/app/src-tauri/src/commands.rs b/openless-all/app/src-tauri/src/commands.rs index 89d0cebe..a91a203e 100644 --- a/openless-all/app/src-tauri/src/commands.rs +++ b/openless-all/app/src-tauri/src/commands.rs @@ -13,8 +13,7 @@ use crate::persistence::{CredentialAccount, CredentialsSnapshot, CredentialsVaul use crate::polish::{LLMError, OpenAICompatibleConfig, OpenAICompatibleLLMProvider}; use crate::types::{ ComboBinding, CredentialsStatus, DictationSession, DictionaryEntry, HotkeyCapability, - HotkeyStatus, PolishMode, ShortcutBinding, UserPreferences, VocabPresetStore, - WindowsImeStatus, + HotkeyStatus, PolishMode, ShortcutBinding, UserPreferences, VocabPresetStore, WindowsImeStatus, }; type CoordinatorState<'a> = State<'a, Arc>; @@ -707,10 +706,11 @@ fn reject_modifier_only_action_shortcut(binding: &ShortcutBinding) -> Result<(), #[tauri::command] pub fn validate_combo_hotkey(binding: ComboBinding) -> Result<(), String> { - validate_shortcut_binding(ShortcutBinding { + crate::combo_hotkey::validate_binding(&ShortcutBinding { primary: binding.primary, modifiers: binding.modifiers, }) + .map_err(|e| e.to_string()) } /// 设置自定义录音组合键并热更新 monitor。 @@ -721,7 +721,7 @@ pub fn set_combo_hotkey(coord: CoordinatorState<'_>, binding: ComboBinding) -> R primary: binding.primary.clone(), modifiers: binding.modifiers.clone(), }; - crate::shortcut_binding::validate_binding(&shortcut).map_err(|e| e.to_string())?; + crate::combo_hotkey::validate_binding(&shortcut).map_err(|e| e.to_string())?; prefs.custom_combo_hotkey = Some(binding); prefs.dictation_hotkey = shortcut; prefs.hotkey.trigger = crate::types::HotkeyTrigger::Custom; @@ -743,7 +743,7 @@ mod tests { SettingsWriter, }; use crate::types::{ - HotkeyBinding, HotkeyMode, HotkeyTrigger, ShortcutBinding, UserPreferences, + ComboBinding, HotkeyBinding, HotkeyMode, HotkeyTrigger, ShortcutBinding, UserPreferences, }; use std::io::{Read, Write}; use std::net::TcpListener; @@ -835,6 +835,16 @@ mod tests { assert_eq!(*writer.combo_refreshes.lock().unwrap(), 1); } + #[test] + fn validate_combo_hotkey_rejects_bare_shift() { + let result = super::validate_combo_hotkey(ComboBinding { + primary: "Shift".into(), + modifiers: vec![], + }); + + assert!(result.is_err()); + } + #[tokio::test] async fn fetch_provider_models_omits_authorization_when_api_key_is_empty() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); diff --git a/openless-all/app/src-tauri/src/qa_hotkey.rs b/openless-all/app/src-tauri/src/qa_hotkey.rs index 70e17c84..798e2f0c 100644 --- a/openless-all/app/src-tauri/src/qa_hotkey.rs +++ b/openless-all/app/src-tauri/src/qa_hotkey.rs @@ -103,7 +103,6 @@ impl QaHotkeyMonitor { return Ok(()); } } - current.take(); let runtime = GlobalHotkeyRuntime::shared() .map_err(|e| QaHotkeyError::ManagerInitFailed(e.to_string()))?; let (registered, rx) = runtime