fix style pack prefs sync consistency

This commit is contained in:
cooper committed 2026-05-13 22:22:38 +08:00
1 parent ae2c7e4e74
commit c77e235d96
2 files changed
+97 -9

No files matched your search

+18 -5
View File
@@ -28,6 +28,7 @@ use crate::types::{
builtin_style_pack_id, default_active_style_pack_id, ChineseScriptPreference, ComboBinding,
CorrectionRule, CredentialsStatus, DictationSession, DictionaryEntry, HotkeyCapability,
HotkeyStatus, OutputLanguagePreference, PolishMode, ShortcutBinding, StylePack,
StylePackKind,
StylePackRuntimeDiagnostics, StyleSystemPrompts, UpdateChannel, UserPreferences,
VocabPresetStore, WindowsImeStatus,
};
@@ -157,8 +158,10 @@ pub fn set_settings(
coord: CoordinatorState<'_>,
app: AppHandle,
tray_microphones: State<'_, TrayMicrophoneMenuState>,
prefs: UserPreferences,
mut prefs: UserPreferences,
) -> Result<(), String> {
let packs = coord.style_packs().list().map_err(|e| e.to_string())?;
sync_style_pack_preferences(&mut prefs, &packs);
// 广播给所有 webview。issue #205:QaPanel 跑在独立 webview,
// 没有 HotkeySettingsContext,必须靠事件感知录音键变化,否则面板可见时
// 用户改键会让浮窗里的 "{recordHotkey}" 文案一直停留在旧值。
@@ -1257,6 +1260,7 @@ pub fn list_style_packs(coord: CoordinatorState<'_>) -> Result<Vec<StylePack>, S
#[tauri::command]
pub fn save_style_pack(
coord: CoordinatorState<'_>,
app: AppHandle,
style_pack: StylePack,
) -> Result<StylePack, String> {
log::info!(
@@ -1265,10 +1269,15 @@ pub fn save_style_pack(
style_pack.kind,
style_pack.base_mode
);
coord
let saved = coord
.style_packs()
.upsert(style_pack)
.map_err(|e| e.to_string())
.map_err(|e| e.to_string())?;
if saved.kind == StylePackKind::Builtin {
let prefs = coord.prefs().get();
let _ = sync_style_pack_prefs_and_persist(&*coord, &app, prefs)?;
}
Ok(saved)
}
#[tauri::command]
@@ -1324,13 +1333,17 @@ pub fn set_style_pack_enabled(
#[tauri::command]
pub fn reset_builtin_style_pack(
coord: CoordinatorState<'_>,
app: AppHandle,
id: String,
) -> Result<StylePack, String> {
log::info!("[style-pack] command reset_builtin requested id={id}");
coord
let saved = coord
.style_packs()
.reset_builtin(&id)
.map_err(|e| e.to_string())
.map_err(|e| e.to_string())?;
let prefs = coord.prefs().get();
let _ = sync_style_pack_prefs_and_persist(&*coord, &app, prefs)?;
Ok(saved)
}
#[tauri::command]
+79 -4
View File
@@ -24,8 +24,9 @@ use uuid::Uuid;
use crate::types::{
builtin_style_pack_for_mode, builtin_style_pack_id, builtin_style_packs,
default_active_style_pack_id, CorrectionRule, DictationSession, DictionaryEntry, PolishMode,
StylePack, StylePackExample, StylePackKind, UserPreferences, VocabPresetStore,
default_active_style_pack_id, CorrectionRule, CustomStylePrompts, DictationSession,
DictionaryEntry, PolishMode, StylePack, StylePackExample, StylePackKind, UserPreferences,
VocabPresetStore,
BUILTIN_STYLE_PACK_LIGHT_ID,
};
@@ -1407,6 +1408,10 @@ pub fn sync_style_pack_preferences(prefs: &mut UserPreferences, packs: &[StylePa
changed = true;
}
if sync_builtin_style_prompt_preferences(prefs, packs) {
changed = true;
}
if changed {
log::info!(
"[style-pack] sync_prefs active:{}->{} default_mode:{:?}->{:?} enabled_modes:{:?}->{:?}",
@@ -1422,6 +1427,44 @@ pub fn sync_style_pack_preferences(prefs: &mut UserPreferences, packs: &[StylePa
changed
}
fn sync_builtin_style_prompt_preferences(prefs: &mut UserPreferences, packs: &[StylePack]) -> bool {
let mut changed = false;
let mut saw_builtin = false;
for mode in [
PolishMode::Raw,
PolishMode::Light,
PolishMode::Structured,
PolishMode::Formal,
] {
let Some(pack) = packs
.iter()
.find(|pack| pack.kind == StylePackKind::Builtin && pack.base_mode == mode)
else {
continue;
};
saw_builtin = true;
let next_prompt = pack.prompt.clone();
let current_prompt = prefs.style_system_prompts.for_mode(mode);
if current_prompt == next_prompt {
continue;
}
match mode {
PolishMode::Raw => prefs.style_system_prompts.raw = next_prompt,
PolishMode::Light => prefs.style_system_prompts.light = next_prompt,
PolishMode::Structured => prefs.style_system_prompts.structured = next_prompt,
PolishMode::Formal => prefs.style_system_prompts.formal = next_prompt,
}
changed = true;
}
if saw_builtin && prefs.custom_style_prompts != CustomStylePrompts::default() {
prefs.custom_style_prompts = CustomStylePrompts::default();
changed = true;
}
changed
}
pub fn enabled_modes_from_style_packs(packs: &[StylePack]) -> Vec<PolishMode> {
let mut modes = Vec::new();
for mode in [
@@ -2013,10 +2056,10 @@ impl CredentialsVault {
#[cfg(test)]
mod tests {
use super::{
chunk_json_payload, list_vocab_presets, save_vocab_presets,
chunk_json_payload, list_vocab_presets, save_vocab_presets, sync_style_pack_preferences,
validate_correction_rule_syntax, KEYRING_CHUNK_MAX_UTF16_UNITS,
};
use crate::types::{VocabPreset, VocabPresetStore};
use crate::types::{builtin_style_packs, CustomStylePrompts, VocabPreset, VocabPresetStore};
use std::fs;
use std::path::PathBuf;
@@ -2075,4 +2118,36 @@ mod tests {
assert!(validate_correction_rule_syntax("{num}到{num}粒", "{num}例").is_err());
assert!(validate_correction_rule_syntax("几粒", "{num}例").is_err());
}
#[test]
fn sync_style_pack_preferences_uses_builtin_store_prompts_as_source_of_truth() {
let mut prefs = crate::types::UserPreferences {
style_system_prompts: crate::types::StyleSystemPrompts {
raw: "stale raw".into(),
light: "stale light".into(),
structured: "stale structured".into(),
formal: "stale formal".into(),
},
custom_style_prompts: CustomStylePrompts {
raw: String::new(),
light: "legacy extra instruction".into(),
structured: String::new(),
formal: String::new(),
},
..Default::default()
};
let mut packs = builtin_style_packs();
let light = packs
.iter_mut()
.find(|pack| pack.id == "builtin.light")
.expect("builtin light pack");
light.prompt = "fresh light prompt from store".into();
assert!(sync_style_pack_preferences(&mut prefs, &packs));
assert_eq!(prefs.style_system_prompts.raw, packs[0].prompt);
assert_eq!(prefs.style_system_prompts.light, "fresh light prompt from store");
assert_eq!(prefs.style_system_prompts.structured, packs[2].prompt);
assert_eq!(prefs.style_system_prompts.formal, packs[3].prompt);
assert_eq!(prefs.custom_style_prompts, CustomStylePrompts::default());
}
}