diff --git a/src-tauri/src/actions.rs b/src-tauri/src/actions.rs index 6dfa52d3..a26f060a 100644 --- a/src-tauri/src/actions.rs +++ b/src-tauri/src/actions.rs @@ -25,16 +25,11 @@ pub trait ShortcutAction: Send + Sync { } // Transcribe Action -struct TranscribeAction; - -async fn maybe_post_process_transcription( - settings: &AppSettings, - transcription: &str, -) -> Option { - if !settings.post_process_enabled { - return None; - } +struct TranscribeAction { + post_process: bool, +} +async fn post_process_transcription(settings: &AppSettings, transcription: &str) -> Option { let provider = match settings.active_post_process_provider().cloned() { Some(provider) => provider, None => { @@ -305,6 +300,7 @@ impl ShortcutAction for TranscribeAction { play_feedback_sound(app, SoundType::Stop); let binding_id = binding_id.to_string(); // Clone binding_id for the async task + let post_process = self.post_process; tauri::async_runtime::spawn(async move { let binding_id = binding_id.clone(); // Clone for the inner async task @@ -343,11 +339,14 @@ impl ShortcutAction for TranscribeAction { final_text = converted_text; } - // Then apply regular post-processing if enabled + // Then apply LLM post-processing if this is the post-process hotkey // Uses final_text which may already have Chinese conversion applied - if let Some(processed_text) = - maybe_post_process_transcription(&settings, &final_text).await - { + let processed = if post_process { + post_process_transcription(&settings, &final_text).await + } else { + None + }; + if let Some(processed_text) = processed { post_processed_text = Some(processed_text.clone()); final_text = processed_text; @@ -474,7 +473,13 @@ pub static ACTION_MAP: Lazy>> = Lazy::ne let mut map = HashMap::new(); map.insert( "transcribe".to_string(), - Arc::new(TranscribeAction) as Arc, + Arc::new(TranscribeAction { + post_process: false, + }) as Arc, + ); + map.insert( + "transcribe_with_post_process".to_string(), + Arc::new(TranscribeAction { post_process: true }) as Arc, ); map.insert( "cancel".to_string(), diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 2bbb5598..21f9903f 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -562,6 +562,17 @@ pub fn get_default_settings() -> AppSettings { current_binding: default_shortcut.to_string(), }, ); + bindings.insert( + "transcribe_with_post_process".to_string(), + ShortcutBinding { + id: "transcribe_with_post_process".to_string(), + name: "Transcribe with Post-Processing".to_string(), + description: "Converts your speech into text and applies AI post-processing." + .to_string(), + default_binding: String::new(), + current_binding: String::new(), + }, + ); bindings.insert( "cancel".to_string(), ShortcutBinding { diff --git a/src-tauri/src/shortcut/handy_keys.rs b/src-tauri/src/shortcut/handy_keys.rs index 963351bb..1cd0ce5c 100644 --- a/src-tauri/src/shortcut/handy_keys.rs +++ b/src-tauri/src/shortcut/handy_keys.rs @@ -433,6 +433,10 @@ pub fn init_shortcuts(app: &AppHandle) -> Result<(), String> { if id == "cancel" { continue; } + // Skip post-processing shortcut when the feature is disabled + if id == "transcribe_with_post_process" && !user_settings.post_process_enabled { + continue; + } let binding = user_settings .bindings diff --git a/src-tauri/src/shortcut/mod.rs b/src-tauri/src/shortcut/mod.rs index da0e1609..464b5645 100644 --- a/src-tauri/src/shortcut/mod.rs +++ b/src-tauri/src/shortcut/mod.rs @@ -110,17 +110,30 @@ pub fn change_binding( ) -> Result { let mut settings = settings::get_settings(&app); - // Get the binding to modify + // Get the binding to modify, or create it from defaults if it doesn't exist let binding_to_modify = match settings.bindings.get(&id) { Some(binding) => binding.clone(), None => { - let error_msg = format!("Binding with id '{}' not found", id); - warn!("change_binding error: {}", error_msg); - return Ok(BindingResponse { - success: false, - binding: None, - error: Some(error_msg), - }); + // Try to get the default binding for this id + let default_settings = settings::get_default_settings(); + match default_settings.bindings.get(&id) { + Some(default_binding) => { + warn!( + "Binding '{}' not found in settings, creating from defaults", + id + ); + default_binding.clone() + } + None => { + let error_msg = format!("Binding with id '{}' not found in defaults", id); + warn!("change_binding error: {}", error_msg); + return Ok(BindingResponse { + success: false, + binding: None, + error: Some(error_msg), + }); + } + } } }; @@ -374,6 +387,11 @@ fn register_all_shortcuts_for_implementation( continue; } + // Skip post-processing shortcut when the feature is disabled + if id == "transcribe_with_post_process" && !current_settings.post_process_enabled { + continue; + } + let mut binding = current_settings .bindings .get(id) @@ -680,7 +698,24 @@ pub fn change_clipboard_handling_setting(app: AppHandle, handling: String) -> Re pub fn change_post_process_enabled_setting(app: AppHandle, enabled: bool) -> Result<(), String> { let mut settings = settings::get_settings(&app); settings.post_process_enabled = enabled; - settings::write_settings(&app, settings); + settings::write_settings(&app, settings.clone()); + + // Register or unregister the post-processing shortcut + if let Some(binding) = settings + .bindings + .get("transcribe_with_post_process") + .cloned() + { + if enabled { + // Only register if the user has actually set a binding + if !binding.current_binding.is_empty() { + let _ = register_shortcut(&app, binding); + } + } else { + let _ = unregister_shortcut(&app, binding); + } + } + Ok(()) } diff --git a/src-tauri/src/shortcut/tauri_impl.rs b/src-tauri/src/shortcut/tauri_impl.rs index d8f00184..28816f45 100644 --- a/src-tauri/src/shortcut/tauri_impl.rs +++ b/src-tauri/src/shortcut/tauri_impl.rs @@ -21,6 +21,10 @@ pub fn init_shortcuts(app: &AppHandle) { if id == "cancel" { continue; // Skip cancel shortcut, it will be registered dynamically } + // Skip post-processing shortcut when the feature is disabled + if id == "transcribe_with_post_process" && !user_settings.post_process_enabled { + continue; + } let binding = user_settings .bindings .get(&id) diff --git a/src/components/settings/GlobalShortcutInput.tsx b/src/components/settings/GlobalShortcutInput.tsx index 4dfd3c4c..80d6b9ef 100644 --- a/src/components/settings/GlobalShortcutInput.tsx +++ b/src/components/settings/GlobalShortcutInput.tsx @@ -299,10 +299,16 @@ export const GlobalShortcutInput: React.FC = ({ ) : (
startRecording(shortcutId)} > - {formatKeyCombination(binding.current_binding, osType)} + {binding.current_binding + ? formatKeyCombination(binding.current_binding, osType) + : t("settings.general.shortcut.notSet")}
)} = ({ ) : (
- {formatKeyCombination(binding.current_binding, osType)} + {binding.current_binding + ? formatKeyCombination(binding.current_binding, osType) + : t("settings.general.shortcut.notSet")}
)} { postProcessModelOptions, } = useSettings(); - const enabled = settings?.post_process_enabled || false; - // Settings are guaranteed to have providers after migration const providers = settings?.post_process_providers || []; @@ -191,7 +188,6 @@ export const usePostProcessProviderState = (): PostProcessProviderState => { // No automatic fetching - user must click refresh button return { - enabled, providerOptions, selectedProviderId, selectedProvider, diff --git a/src/components/settings/post-processing/PostProcessingSettings.tsx b/src/components/settings/post-processing/PostProcessingSettings.tsx index 2776931b..2dfbbf26 100644 --- a/src/components/settings/post-processing/PostProcessingSettings.tsx +++ b/src/components/settings/post-processing/PostProcessingSettings.tsx @@ -19,28 +19,13 @@ import { BaseUrlField } from "../PostProcessingSettingsApi/BaseUrlField"; import { ApiKeyField } from "../PostProcessingSettingsApi/ApiKeyField"; import { ModelSelect } from "../PostProcessingSettingsApi/ModelSelect"; import { usePostProcessProviderState } from "../PostProcessingSettingsApi/usePostProcessProviderState"; +import { ShortcutInput } from "../ShortcutInput"; import { useSettings } from "../../../hooks/useSettings"; -const DisabledNotice: React.FC<{ children: React.ReactNode }> = ({ - children, -}) => ( -
-

{children}

-
-); - const PostProcessingSettingsApiComponent: React.FC = () => { const { t } = useTranslation(); const state = usePostProcessProviderState(); - if (!state.enabled) { - return ( - - {t("settings.postProcessing.disabledNotice")} - - ); - } - return ( <> { const [draftName, setDraftName] = useState(""); const [draftText, setDraftText] = useState(""); - const enabled = getSetting("post_process_enabled") || false; const prompts = getSetting("post_process_prompts") || []; const selectedPromptId = getSetting("post_process_selected_prompt_id") || ""; const selectedPrompt = @@ -257,14 +241,6 @@ const PostProcessingSettingsPromptsComponent: React.FC = () => { setDraftText(""); }; - if (!enabled) { - return ( - - {t("settings.postProcessing.disabledNotice")} - - ); - } - const hasPrompts = prompts.length > 0; const isDirty = !!selectedPrompt && @@ -452,6 +428,14 @@ export const PostProcessingSettings: React.FC = () => { return (
+ + + + diff --git a/src/i18n/locales/ar/translation.json b/src/i18n/locales/ar/translation.json index 399d7b3f..697e96b2 100644 --- a/src/i18n/locales/ar/translation.json +++ b/src/i18n/locales/ar/translation.json @@ -241,7 +241,6 @@ }, "postProcessing": { "title": "معالجة لاحقة", - "disabledNotice": "المعالجة اللاحقة معطلة حالياً. قم بتمكينها في إعدادات تصحيح الأخطاء للتكوين.", "api": { "title": "API (متوافق مع OpenAI)", "provider": { diff --git a/src/i18n/locales/cs/translation.json b/src/i18n/locales/cs/translation.json index ddfccc0d..f31b72c8 100644 --- a/src/i18n/locales/cs/translation.json +++ b/src/i18n/locales/cs/translation.json @@ -221,7 +221,6 @@ }, "postProcessing": { "title": "Následné zpracování", - "disabledNotice": "Následné zpracování je nyní vypnuto. Pro konfiguraci jej zapněte v nastavení Ladění.", "api": { "title": "API (kompatibilní s OpenAI)", "provider": { diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json index 1c2be180..bb86826c 100644 --- a/src/i18n/locales/de/translation.json +++ b/src/i18n/locales/de/translation.json @@ -237,7 +237,9 @@ }, "postProcessing": { "title": "Nachbearbeitung", - "disabledNotice": "Die Nachbearbeitung ist derzeit deaktiviert. Aktiviere sie in den Debug-Einstellungen, um sie zu konfigurieren.", + "hotkey": { + "title": "Tastenkürzel" + }, "api": { "title": "API (OpenAI-kompatibel)", "provider": { diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index b2a16a3e..8e7785fe 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -110,6 +110,7 @@ "loading": "Loading shortcuts...", "none": "No shortcuts configured", "notFound": "Shortcut not found", + "notSet": "Not set", "pressKeys": "Press keys...", "bindings": { "transcribe": { @@ -119,6 +120,10 @@ "cancel": { "name": "Cancel Shortcut", "description": "The keyboard shortcut to cancel the current recording." + }, + "transcribe_with_post_process": { + "name": "Post-Processing Hotkey", + "description": "Optional: A dedicated hotkey that always applies AI post-processing to your transcription." } }, "errors": { @@ -241,7 +246,9 @@ }, "postProcessing": { "title": "Post Process", - "disabledNotice": "Post processing is currently disabled. Enable it in Debug settings to configure.", + "hotkey": { + "title": "Hotkey" + }, "api": { "title": "API (OpenAI Compatible)", "provider": { diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 4c03ec37..57ecbb80 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -237,7 +237,9 @@ }, "postProcessing": { "title": "Post Proceso", - "disabledNotice": "El post procesamiento está actualmente deshabilitado. Habilítalo en la configuración de Depuración para configurarlo.", + "hotkey": { + "title": "Tecla de acceso rápido" + }, "api": { "title": "API (Compatible con OpenAI)", "provider": { diff --git a/src/i18n/locales/fr/translation.json b/src/i18n/locales/fr/translation.json index dce93f31..9290cacb 100644 --- a/src/i18n/locales/fr/translation.json +++ b/src/i18n/locales/fr/translation.json @@ -238,7 +238,9 @@ }, "postProcessing": { "title": "Post-traitement", - "disabledNotice": "Le post-traitement est actuellement désactivé. Activez-le dans les paramètres de débogage pour le configurer.", + "hotkey": { + "title": "Raccourci clavier" + }, "api": { "title": "API (Compatible OpenAI)", "provider": { diff --git a/src/i18n/locales/it/translation.json b/src/i18n/locales/it/translation.json index 3d43bd33..1a362d82 100644 --- a/src/i18n/locales/it/translation.json +++ b/src/i18n/locales/it/translation.json @@ -237,7 +237,9 @@ }, "postProcessing": { "title": "Post-Elaborazione", - "disabledNotice": "La post-elaborazione è attualmente disattivata. Abilitala nelle opzioni di debug per configurarla.", + "hotkey": { + "title": "Tasto di scelta rapida" + }, "api": { "title": "API (Compatibile con OpenAI)", "provider": { diff --git a/src/i18n/locales/ja/translation.json b/src/i18n/locales/ja/translation.json index 545e85ed..dc57fc85 100644 --- a/src/i18n/locales/ja/translation.json +++ b/src/i18n/locales/ja/translation.json @@ -237,7 +237,9 @@ }, "postProcessing": { "title": "後処理", - "disabledNotice": "後処理は現在無効です。設定するにはデバッグ設定で有効にしてください。", + "hotkey": { + "title": "ホットキー" + }, "api": { "title": "API(OpenAI互換)", "provider": { diff --git a/src/i18n/locales/ko/translation.json b/src/i18n/locales/ko/translation.json index f7a2b927..80555708 100644 --- a/src/i18n/locales/ko/translation.json +++ b/src/i18n/locales/ko/translation.json @@ -241,7 +241,6 @@ }, "postProcessing": { "title": "후처리", - "disabledNotice": "후처리가 현재 비활성화되어 있습니다. 설정하려면 디버그 설정에서 활성화하세요.", "api": { "title": "API (OpenAI 호환)", "provider": { diff --git a/src/i18n/locales/pl/translation.json b/src/i18n/locales/pl/translation.json index 5a7ce175..e0300efa 100644 --- a/src/i18n/locales/pl/translation.json +++ b/src/i18n/locales/pl/translation.json @@ -237,7 +237,9 @@ }, "postProcessing": { "title": "Postprocess", - "disabledNotice": "Postprocess jest obecnie wyłączony. Włącz go w ustawieniach Debug, aby skonfigurować.", + "hotkey": { + "title": "Skrót klawiszowy" + }, "api": { "title": "API (zgodne z OpenAI)", "provider": { diff --git a/src/i18n/locales/pt/translation.json b/src/i18n/locales/pt/translation.json index ced57d1f..2d518b26 100644 --- a/src/i18n/locales/pt/translation.json +++ b/src/i18n/locales/pt/translation.json @@ -241,7 +241,9 @@ }, "postProcessing": { "title": "Pós-Processamento", - "disabledNotice": "O pós-processamento está atualmente desabilitado. Habilite-o nas configurações de Depuração para configurar.", + "hotkey": { + "title": "Tecla de atalho" + }, "api": { "title": "API (Compatível com OpenAI)", "provider": { diff --git a/src/i18n/locales/ru/translation.json b/src/i18n/locales/ru/translation.json index 664020fe..2bcfe46c 100644 --- a/src/i18n/locales/ru/translation.json +++ b/src/i18n/locales/ru/translation.json @@ -237,7 +237,9 @@ }, "postProcessing": { "title": "Постобработка", - "disabledNotice": "Постобработка в настоящее время отключена. Включите его в настройках отладки для настройки.", + "hotkey": { + "title": "Горячая клавиша" + }, "api": { "title": "API (совместимый с OpenAI)", "provider": { diff --git a/src/i18n/locales/tr/translation.json b/src/i18n/locales/tr/translation.json index d165d0fd..4f20c7c6 100644 --- a/src/i18n/locales/tr/translation.json +++ b/src/i18n/locales/tr/translation.json @@ -241,7 +241,6 @@ }, "postProcessing": { "title": "Son İşlem", - "disabledNotice": "Son işlem şu anda devre dışı. Yapılandırmak için Hata Ayıklama ayarlarında etkinleştirin.", "api": { "title": "API (OpenAI Uyumlu)", "provider": { diff --git a/src/i18n/locales/uk/translation.json b/src/i18n/locales/uk/translation.json index 599cdea5..6ef8d47c 100644 --- a/src/i18n/locales/uk/translation.json +++ b/src/i18n/locales/uk/translation.json @@ -237,7 +237,9 @@ }, "postProcessing": { "title": "Постобробка", - "disabledNotice": "Постобробка наразі вимкнена. Увімкніть її в розділі Дебаг.", + "hotkey": { + "title": "Гаряча клавіша" + }, "api": { "title": "API (сумісний з OpenAI)", "provider": { diff --git a/src/i18n/locales/vi/translation.json b/src/i18n/locales/vi/translation.json index bea9688e..891dd04d 100644 --- a/src/i18n/locales/vi/translation.json +++ b/src/i18n/locales/vi/translation.json @@ -238,7 +238,9 @@ }, "postProcessing": { "title": "Xử lý sau", - "disabledNotice": "Xử lý sau hiện đang bị tắt. Bật nó trong cài đặt Gỡ lỗi để cấu hình.", + "hotkey": { + "title": "Phím tắt" + }, "api": { "title": "API (Tương thích OpenAI)", "provider": { diff --git a/src/i18n/locales/zh/translation.json b/src/i18n/locales/zh/translation.json index e77621c9..75907c27 100644 --- a/src/i18n/locales/zh/translation.json +++ b/src/i18n/locales/zh/translation.json @@ -237,7 +237,9 @@ }, "postProcessing": { "title": "后处理", - "disabledNotice": "后处理当前已禁用。请在调试设置中启用以进行配置。", + "hotkey": { + "title": "快捷键" + }, "api": { "title": "API(兼容 OpenAI)", "provider": {