diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 3ed5bd2..8f4fc69 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4180,6 +4180,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", + "image", "jni", "libc", "log", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5ac4de4..ede8d32 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -13,7 +13,7 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2", features = [] } [dependencies] -tauri = { version = "2", features = [] } +tauri = { version = "2", features = ["tray-icon", "image-png"] } tauri-plugin-dialog = "2" tauri-plugin-opener = "2" tauri-plugin-shell = "2" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 5e067ad..2bf139b 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -15,6 +15,7 @@ "notification:allow-request-permission", "notification:allow-notify", "clipboard-manager:default", - "clipboard-manager:allow-read-image" + "clipboard-manager:allow-read-image", + "core:tray:default" ] } diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index d6f6fae..6800346 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -70,6 +70,9 @@ pub struct HikariConfig { #[serde(default = "default_font_size")] pub font_size: u32, + + #[serde(default)] + pub minimize_to_tray: bool, } impl Default for HikariConfig { @@ -89,6 +92,7 @@ impl Default for HikariConfig { update_checks_enabled: true, character_panel_width: None, font_size: 14, + minimize_to_tray: false, } } } @@ -140,6 +144,7 @@ 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); } #[test] @@ -159,6 +164,7 @@ mod tests { update_checks_enabled: true, character_panel_width: Some(400), font_size: 16, + minimize_to_tray: true, }; let json = serde_json::to_string(&config).unwrap(); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f635d4d..8957ffa 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -5,6 +5,7 @@ mod config; mod notifications; mod stats; mod temp_manager; +mod tray; mod types; mod vbs_notification; mod windows_toast; @@ -15,7 +16,9 @@ use bridge_manager::create_shared_bridge_manager; use commands::load_saved_achievements; use commands::*; use notifications::*; +use tauri::Manager; use temp_manager::create_shared_temp_manager; +use tray::{setup_tray, should_minimize_to_tray}; use vbs_notification::*; use windows_toast::*; use wsl_notifications::*; @@ -47,6 +50,27 @@ pub fn run() { } } + // Set up system tray + if let Err(e) = setup_tray(app.handle()) { + eprintln!("Failed to set up system tray: {}", e); + } + + // Handle window close event for minimize to tray + 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(); + } + } + } + } + }); + Ok(()) }) .invoke_handler(tauri::generate_handler![ diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs new file mode 100644 index 0000000..9f87b0b --- /dev/null +++ b/src-tauri/src/tray.rs @@ -0,0 +1,68 @@ +use tauri::{ + menu::{Menu, MenuItem}, + tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}, + 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>)?; + + let menu = Menu::with_items(app, &[&show_item, &quit_item])?; + + let _tray = TrayIconBuilder::with_id("main") + .icon(app.default_window_icon().unwrap().clone()) + .menu(&menu) + .tooltip("Hikari - Claude Code Assistant") + .on_menu_event(|app, event| match event.id.as_ref() { + "show" => { + if let Some(window) = app.get_webview_window("main") { + let _ = window.show(); + let _ = window.unminimize(); + let _ = window.set_focus(); + } + } + "quit" => { + app.exit(0); + } + _ => {} + }) + .on_tray_icon_event(|tray, event| { + if let TrayIconEvent::Click { + button: MouseButton::Left, + button_state: MouseButtonState::Up, + .. + } = event + { + let app = tray.app_handle(); + if let Some(window) = app.get_webview_window("main") { + let _ = window.show(); + let _ = window.unminimize(); + let _ = window.set_focus(); + } + } + }) + .build(app)?; + + 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-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b529bcc..368ea33 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -22,6 +22,12 @@ ], "security": { "csp": null + }, + "trayIcon": { + "id": "main", + "iconPath": "icons/32x32.png", + "iconAsTemplate": false, + "tooltip": "Hikari - Claude Code Assistant" } }, "bundle": { diff --git a/src/lib/components/ConfigSidebar.svelte b/src/lib/components/ConfigSidebar.svelte index 8981690..22ce8c1 100644 --- a/src/lib/components/ConfigSidebar.svelte +++ b/src/lib/components/ConfigSidebar.svelte @@ -23,6 +23,7 @@ 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, @@ -477,6 +478,21 @@

+ +
+ +

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

+
+