From b82b3b2f20ebe427d086ecef4c85b0c9dd86768a Mon Sep 17 00:00:00 2001 From: Dylan <40187776+Anaphylaxis@users.noreply.github.com> Date: Sat, 6 Sep 2025 17:07:33 -0400 Subject: [PATCH] Added "Start Hidden" (#105) * Added "Start Hidden" * revert visible option in tauri conf --------- Co-authored-by: CJ Pais --- src-tauri/src/lib.rs | 20 ++++++++++++++ src-tauri/src/settings.rs | 7 +++++ src-tauri/src/shortcut.rs | 18 ++++++++++++ src/components/settings/AdvancedSettings.tsx | 2 ++ src/components/settings/StartHidden.tsx | 29 ++++++++++++++++++++ src/components/settings/index.ts | 1 + src/lib/types.ts | 1 + src/stores/settingsStore.ts | 4 +++ 8 files changed, 82 insertions(+) create mode 100644 src/components/settings/StartHidden.tsx diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 6ad6b06..e61d10d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -91,6 +91,14 @@ pub fn run() { )) .manage(Mutex::new(ShortcutToggleStates::default())) .setup(move |app| { + // Apply macOS Accessory policy early if starting hidden + #[cfg(target_os = "macos")] + { + let settings = settings::get_settings(&app.handle()); + if settings.start_hidden { + let _ = app.set_activation_policy(tauri::ActivationPolicy::Accessory); + } + } // Get the current theme to set the appropriate initial icon let initial_theme = tray::get_current_theme(&app.handle()); @@ -134,6 +142,17 @@ pub fn run() { // Enable autostart let _ = autostart_manager.enable(); + // Window is configured to start hidden to avoid flicker. + // If user didn't choose Start Hidden, show it now. + let settings = settings::get_settings(&app.handle()); + if settings.start_hidden { + if let Some(main_window) = app.get_webview_window("main") { + let _ = main_window.hide(); + } + } else { + show_main_window(&app.handle()); + } + let recording_manager = Arc::new( AudioRecordingManager::new(app).expect("Failed to initialize recording manager"), ); @@ -185,6 +204,7 @@ pub fn run() { shortcut::reset_binding, shortcut::change_ptt_setting, shortcut::change_audio_feedback_setting, + shortcut::change_start_hidden_setting, shortcut::change_translate_to_english_setting, shortcut::change_selected_language_setting, shortcut::change_overlay_position_setting, diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 2d8b010..30991bb 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -69,6 +69,8 @@ pub struct AppSettings { pub bindings: HashMap, pub push_to_talk: bool, pub audio_feedback: bool, + #[serde(default = "default_start_hidden")] + pub start_hidden: bool, #[serde(default = "default_model")] pub selected_model: String, #[serde(default = "default_always_on_microphone")] @@ -105,6 +107,10 @@ fn default_translate_to_english() -> bool { false } +fn default_start_hidden() -> bool { + false +} + fn default_selected_language() -> String { "auto".to_string() } @@ -149,6 +155,7 @@ pub fn get_default_settings() -> AppSettings { bindings, push_to_talk: true, audio_feedback: false, + start_hidden: default_start_hidden(), selected_model: "".to_string(), always_on_microphone: false, selected_microphone: None, diff --git a/src-tauri/src/shortcut.rs b/src-tauri/src/shortcut.rs index 9ccb2ad..f51db0f 100644 --- a/src-tauri/src/shortcut.rs +++ b/src-tauri/src/shortcut.rs @@ -173,6 +173,24 @@ pub fn change_debug_mode_setting(app: AppHandle, enabled: bool) -> Result<(), St Ok(()) } +#[tauri::command] +pub fn change_start_hidden_setting(app: AppHandle, enabled: bool) -> Result<(), String> { + let mut settings = settings::get_settings(&app); + settings.start_hidden = enabled; + settings::write_settings(&app, settings); + + // Notify frontend + let _ = app.emit( + "settings-changed", + serde_json::json!({ + "setting": "start_hidden", + "value": enabled + }), + ); + + Ok(()) +} + #[tauri::command] pub fn update_custom_words(app: AppHandle, words: Vec) -> Result<(), String> { let mut settings = settings::get_settings(&app); diff --git a/src/components/settings/AdvancedSettings.tsx b/src/components/settings/AdvancedSettings.tsx index 2ca6c81..8795e7e 100644 --- a/src/components/settings/AdvancedSettings.tsx +++ b/src/components/settings/AdvancedSettings.tsx @@ -5,11 +5,13 @@ import { ModelUnloadTimeoutSetting } from "./ModelUnloadTimeout"; import { CustomWords } from "./CustomWords"; import { AlwaysOnMicrophone } from "./AlwaysOnMicrophone"; import { SettingsGroup } from "../ui/SettingsGroup"; +import { StartHidden } from "./StartHidden"; export const AdvancedSettings: React.FC = () => { return (
+ diff --git a/src/components/settings/StartHidden.tsx b/src/components/settings/StartHidden.tsx new file mode 100644 index 0000000..186b425 --- /dev/null +++ b/src/components/settings/StartHidden.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { ToggleSwitch } from "../ui/ToggleSwitch"; +import { useSettings } from "../../hooks/useSettings"; + +interface StartHiddenProps { + descriptionMode?: "inline" | "tooltip"; + grouped?: boolean; +} + +export const StartHidden: React.FC = React.memo(({ + descriptionMode = "tooltip", + grouped = false, +}) => { + const { getSetting, updateSetting, isUpdating } = useSettings(); + + const startHidden = getSetting("start_hidden") ?? false; + + return ( + updateSetting("start_hidden", enabled)} + isUpdating={isUpdating("start_hidden")} + label="Start Hidden" + description="Launch to system tray without opening the window." + descriptionMode={descriptionMode} + grouped={grouped} + /> + ); +}); diff --git a/src/components/settings/index.ts b/src/components/settings/index.ts index 19c7f69..81803fe 100644 --- a/src/components/settings/index.ts +++ b/src/components/settings/index.ts @@ -17,3 +17,4 @@ export { TranslateToEnglish } from "./TranslateToEnglish"; export { CustomWords } from "./CustomWords"; export { AppDataDirectory } from "./AppDataDirectory"; export { ModelUnloadTimeoutSetting } from "./ModelUnloadTimeout"; +export { StartHidden } from "./StartHidden"; diff --git a/src/lib/types.ts b/src/lib/types.ts index 8bffb3a..3f99dcf 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -38,6 +38,7 @@ export const SettingsSchema = z.object({ bindings: ShortcutBindingsMapSchema, push_to_talk: z.boolean(), audio_feedback: z.boolean(), + start_hidden: z.boolean().optional().default(false), selected_model: z.string(), always_on_microphone: z.boolean(), selected_microphone: z.string().nullable().optional(), diff --git a/src/stores/settingsStore.ts b/src/stores/settingsStore.ts index f89e2e8..da5c806 100644 --- a/src/stores/settingsStore.ts +++ b/src/stores/settingsStore.ts @@ -33,6 +33,7 @@ interface SettingsStore { const DEFAULT_SETTINGS: Partial = { always_on_microphone: false, audio_feedback: true, + start_hidden: false, push_to_talk: false, selected_microphone: "Default", selected_output_device: "Default", @@ -162,6 +163,9 @@ export const useSettingsStore = create()( case "audio_feedback": await invoke("change_audio_feedback_setting", { enabled: value }); break; + case "start_hidden": + await invoke("change_start_hidden_setting", { enabled: value }); + break; case "push_to_talk": await invoke("change_ptt_setting", { enabled: value }); break;