Fix custom dictation hotkey fallbacks
This commit is contained in:
3 files changed
+82
-15
No files matched your search
@@ -642,9 +642,7 @@ pub fn set_dictation_hotkey(
|
||||
binding: ShortcutBinding,
|
||||
) -> Result<(), String> {
|
||||
crate::shortcut_binding::validate_binding(&binding).map_err(|e| e.to_string())?;
|
||||
if binding.modifiers.is_empty() && binding.primary.eq_ignore_ascii_case("shift") {
|
||||
return Err("Shift 单键目前只能用于翻译快捷键".into());
|
||||
}
|
||||
reject_bare_shift_dictation_shortcut(&binding)?;
|
||||
let mut prefs = coord.prefs().get();
|
||||
prefs.dictation_hotkey = binding;
|
||||
coord.prefs().set(prefs).map_err(|e| e.to_string())?;
|
||||
@@ -706,11 +704,12 @@ fn reject_modifier_only_action_shortcut(binding: &ShortcutBinding) -> Result<(),
|
||||
|
||||
#[tauri::command]
|
||||
pub fn validate_combo_hotkey(binding: ComboBinding) -> Result<(), String> {
|
||||
crate::combo_hotkey::validate_binding(&ShortcutBinding {
|
||||
let shortcut = ShortcutBinding {
|
||||
primary: binding.primary,
|
||||
modifiers: binding.modifiers,
|
||||
})
|
||||
.map_err(|e| e.to_string())
|
||||
};
|
||||
reject_bare_shift_dictation_shortcut(&shortcut)?;
|
||||
crate::combo_hotkey::validate_binding(&shortcut).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// 设置自定义录音组合键并热更新 monitor。
|
||||
@@ -721,6 +720,7 @@ pub fn set_combo_hotkey(coord: CoordinatorState<'_>, binding: ComboBinding) -> R
|
||||
primary: binding.primary.clone(),
|
||||
modifiers: binding.modifiers.clone(),
|
||||
};
|
||||
reject_bare_shift_dictation_shortcut(&shortcut)?;
|
||||
crate::combo_hotkey::validate_binding(&shortcut).map_err(|e| e.to_string())?;
|
||||
prefs.custom_combo_hotkey = Some(binding);
|
||||
prefs.dictation_hotkey = shortcut;
|
||||
@@ -731,6 +731,13 @@ pub fn set_combo_hotkey(coord: CoordinatorState<'_>, binding: ComboBinding) -> R
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reject_bare_shift_dictation_shortcut(binding: &ShortcutBinding) -> Result<(), String> {
|
||||
if binding.modifiers.is_empty() && binding.primary.eq_ignore_ascii_case("shift") {
|
||||
return Err("Shift 单键目前只能用于翻译快捷键".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ─────────────────────────── unused but exported (silences dead_code) ───────────────────────────
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -845,6 +852,19 @@ mod tests {
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn combo_hotkey_bare_shift_rejection_matches_dictation_setter() {
|
||||
let binding = ShortcutBinding {
|
||||
primary: "Shift".into(),
|
||||
modifiers: vec![],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
super::reject_bare_shift_dictation_shortcut(&binding),
|
||||
Err("Shift 单键目前只能用于翻译快捷键".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_provider_models_omits_authorization_when_api_key_is_empty() {
|
||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
|
||||
@@ -441,7 +441,7 @@ impl PreferencesStore {
|
||||
ensure_dir(&dir)?;
|
||||
let path = dir.join(PREFERENCES_FILE);
|
||||
let prefs = if path.exists() {
|
||||
read_or_default::<UserPreferences>(&path).unwrap_or_default()
|
||||
read_or_default::<UserPreferences>(&path)?
|
||||
} else {
|
||||
UserPreferences::default()
|
||||
};
|
||||
|
||||
@@ -192,9 +192,11 @@ impl<'de> Deserialize<'de> for UserPreferences {
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let wire = UserPreferencesWire::deserialize(deserializer)?;
|
||||
let dictation_hotkey = wire.dictation_hotkey.unwrap_or_else(|| {
|
||||
default_dictation_hotkey_from_legacy(&wire.hotkey, &wire.custom_combo_hotkey)
|
||||
});
|
||||
let dictation_hotkey = match wire.dictation_hotkey {
|
||||
Some(binding) => binding,
|
||||
None => default_dictation_hotkey_from_legacy(&wire.hotkey, &wire.custom_combo_hotkey)
|
||||
.map_err(serde::de::Error::custom)?,
|
||||
};
|
||||
Ok(Self {
|
||||
hotkey: wire.hotkey,
|
||||
dictation_hotkey,
|
||||
@@ -261,16 +263,21 @@ fn default_app_shortcut_modifiers() -> Vec<String> {
|
||||
fn default_dictation_hotkey_from_legacy(
|
||||
hotkey: &HotkeyBinding,
|
||||
custom_combo_hotkey: &Option<ComboBinding>,
|
||||
) -> ShortcutBinding {
|
||||
) -> Result<ShortcutBinding, String> {
|
||||
if hotkey.trigger == HotkeyTrigger::Custom {
|
||||
if let Some(combo) = custom_combo_hotkey {
|
||||
return ShortcutBinding {
|
||||
return Ok(ShortcutBinding {
|
||||
primary: combo.primary.clone(),
|
||||
modifiers: combo.modifiers.clone(),
|
||||
};
|
||||
});
|
||||
}
|
||||
return Err(
|
||||
"hotkey.trigger is custom but dictationHotkey/customComboHotkey is missing".into(),
|
||||
);
|
||||
}
|
||||
crate::shortcut_binding::binding_from_legacy_trigger(hotkey.trigger)
|
||||
Ok(crate::shortcut_binding::binding_from_legacy_trigger(
|
||||
hotkey.trigger,
|
||||
))
|
||||
}
|
||||
|
||||
fn default_working_languages() -> Vec<String> {
|
||||
@@ -284,7 +291,8 @@ impl Default for UserPreferences {
|
||||
dictation_hotkey: default_dictation_hotkey_from_legacy(
|
||||
&HotkeyBinding::default(),
|
||||
&None,
|
||||
),
|
||||
)
|
||||
.expect("default legacy hotkey is not custom"),
|
||||
default_mode: PolishMode::Light,
|
||||
enabled_modes: vec![
|
||||
PolishMode::Raw,
|
||||
@@ -759,4 +767,43 @@ mod tests {
|
||||
|
||||
assert!(prefs.allow_non_tsf_insertion_fallback);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_custom_hotkey_without_custom_binding_is_rejected() {
|
||||
let result = serde_json::from_str::<UserPreferences>(
|
||||
r#"{
|
||||
"hotkey": { "trigger": "custom", "mode": "toggle" }
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_custom_hotkey_uses_custom_combo_binding() {
|
||||
let prefs: UserPreferences = serde_json::from_str(
|
||||
r#"{
|
||||
"hotkey": { "trigger": "custom", "mode": "toggle" },
|
||||
"customComboHotkey": { "primary": "D", "modifiers": ["cmd", "shift"] }
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(prefs.dictation_hotkey.primary, "D");
|
||||
assert_eq!(prefs.dictation_hotkey.modifiers, vec!["cmd", "shift"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_hotkey_with_dictation_hotkey_preserves_dictation_binding() {
|
||||
let prefs: UserPreferences = serde_json::from_str(
|
||||
r#"{
|
||||
"hotkey": { "trigger": "custom", "mode": "toggle" },
|
||||
"dictationHotkey": { "primary": "Space", "modifiers": ["ctrl"] }
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(prefs.dictation_hotkey.primary, "Space");
|
||||
assert_eq!(prefs.dictation_hotkey.modifiers, vec!["ctrl"]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user