diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index f8363c3..a7a7827 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -30,6 +30,7 @@ }, "core:window:allow-set-size", "core:window:allow-set-always-on-top", - "core:window:allow-inner-size" + "core:window:allow-inner-size", + "core:window:allow-hide" ] } diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 1be5ccd..69c418c 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1154,6 +1154,19 @@ pub async fn log_discord_rpc( Ok(()) } +#[tauri::command] +pub async fn close_application(app_handle: AppHandle) -> Result<(), String> { + // Get the main window + if let Some(window) = app_handle.get_webview_window("main") { + // Hide the window first for a smoother close + let _ = window.hide(); + } + + // Exit the application + app_handle.exit(0); + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index b8fe217..f66086b 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -71,9 +71,6 @@ pub struct HikariConfig { #[serde(default = "default_font_size")] pub font_size: u32, - #[serde(default)] - pub minimize_to_tray: bool, - #[serde(default)] pub streamer_mode: bool, @@ -134,7 +131,6 @@ impl Default for HikariConfig { update_checks_enabled: true, character_panel_width: None, font_size: 14, - minimize_to_tray: false, streamer_mode: false, streamer_hide_paths: false, compact_mode: false, @@ -242,7 +238,6 @@ mod tests { assert!(config.update_checks_enabled); assert!(config.character_panel_width.is_none()); assert_eq!(config.font_size, 14); - assert!(!config.minimize_to_tray); assert!(!config.streamer_mode); assert!(!config.streamer_hide_paths); assert!(!config.compact_mode); @@ -275,7 +270,6 @@ mod tests { update_checks_enabled: true, character_panel_width: Some(400), font_size: 16, - minimize_to_tray: true, streamer_mode: false, streamer_hide_paths: false, compact_mode: false, diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3fe20e9..d9a0a9d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -33,11 +33,11 @@ use quick_actions::*; use sessions::*; use snippets::*; use std::sync::Arc; -use tauri::Manager; +use tauri::{Emitter, Manager}; use temp_manager::create_shared_temp_manager; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; -use tray::{setup_tray, should_minimize_to_tray}; +use tray::setup_tray; use vbs_notification::*; use windows_toast::*; use wsl_notifications::*; @@ -89,17 +89,18 @@ pub fn run() { eprintln!("Failed to set up system tray: {}", e); } - // Handle window close event for minimize to tray + // Handle window close event for minimize to tray and close confirmation let main_window = app.get_webview_window("main").unwrap(); main_window.on_window_event({ let app_handle = app.handle().clone(); move |event| { if let tauri::WindowEvent::CloseRequested { api, .. } = event { - if should_minimize_to_tray(&app_handle) { - api.prevent_close(); - if let Some(window) = app_handle.get_webview_window("main") { - let _ = window.hide(); - } + // Always prevent default close - let frontend handle it + api.prevent_close(); + + // Emit event to frontend to show confirmation modal + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.emit("window-close-requested", ()); } } } @@ -194,6 +195,7 @@ pub fn run() { update_discord_rpc, stop_discord_rpc, log_discord_rpc, + close_application, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs index 9f87b0b..4532ac7 100644 --- a/src-tauri/src/tray.rs +++ b/src-tauri/src/tray.rs @@ -4,8 +4,6 @@ use tauri::{ AppHandle, Manager, }; -use crate::config::HikariConfig; - pub fn setup_tray(app: &AppHandle) -> tauri::Result<()> { let show_item = MenuItem::with_id(app, "show", "Show Hikari", true, None::<&str>)?; let quit_item = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?; @@ -48,21 +46,3 @@ pub fn setup_tray(app: &AppHandle) -> tauri::Result<()> { Ok(()) } - -pub fn should_minimize_to_tray(app: &AppHandle) -> bool { - let config_path = app - .path() - .app_config_dir() - .ok() - .map(|p| p.join("hikari-config.json")); - - if let Some(path) = config_path { - if let Ok(content) = std::fs::read_to_string(&path) { - if let Ok(config) = serde_json::from_str::(&content) { - return config.minimize_to_tray; - } - } - } - - false -} diff --git a/src/lib/components/CloseAppConfirmModal.svelte b/src/lib/components/CloseAppConfirmModal.svelte new file mode 100644 index 0000000..756f3b0 --- /dev/null +++ b/src/lib/components/CloseAppConfirmModal.svelte @@ -0,0 +1,116 @@ + + + + +{#if isOpen} +
e.key === " " && onCancel()} + > +
e.stopPropagation()} + onkeydown={(e) => e.stopPropagation()} + role="dialog" + aria-labelledby="confirm-title" + aria-describedby="confirm-message" + tabindex="-1" + > +
+
+
+ + + +
+
+

+ Close Hikari Desktop? +

+

+ {#if hasActiveConversation} + You have an active conversation with Claude. Are you sure you want to close the + application? Your conversation history will be saved, but any in-progress tasks will + be interrupted. + {:else} + Are you sure you want to close the application? + {/if} +

+
+
+ +
+ + + +
+
+
+
+{/if} + + diff --git a/src/lib/components/ConfigSidebar.svelte b/src/lib/components/ConfigSidebar.svelte index cd0f20e..2f3c94b 100644 --- a/src/lib/components/ConfigSidebar.svelte +++ b/src/lib/components/ConfigSidebar.svelte @@ -26,7 +26,6 @@ notifications_enabled: true, notification_volume: 0.7, always_on_top: false, - minimize_to_tray: false, update_checks_enabled: true, character_panel_width: null, font_size: 14, @@ -728,21 +727,6 @@

- -
- -

- Hide to tray instead of closing when you click the X button -

-
-