Merge pull request #127 from M3gA-Mind/feat/system-tray-bug

fix(tauri): restore working tray controls in desktop shell
This commit is contained in:
Mega Mind
2026-03-31 22:17:57 +05:30
committed by GitHub
3 changed files with 80 additions and 17 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ serde_json = "1"
[dependencies]
# Tauri core and plugins
tauri = { version = "2.10", features = ["macos-private-api"] }
tauri = { version = "2.10", features = ["macos-private-api", "tray-icon"] }
tauri-plugin-deep-link = "2.0.0"
tauri-plugin-opener = "2"
serde_json = "1"
+71 -15
View File
@@ -3,7 +3,11 @@ compile_error!("src-tauri host is desktop-only. Non-desktop targets are not supp
mod core_process;
use tauri::{Manager, RunEvent};
use tauri::{
menu::{Menu, MenuItem},
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
AppHandle, Manager, RunEvent,
};
#[cfg(any(windows, target_os = "linux"))]
use tauri_plugin_deep_link::DeepLinkExt;
@@ -18,6 +22,65 @@ fn is_daemon_mode() -> bool {
std::env::args().any(|arg| arg == "daemon" || arg == "--daemon")
}
fn show_main_window(app: &AppHandle) {
if let Some(window) = app.get_webview_window("main") {
if let Err(err) = window.show() {
log::error!("[tray] failed to show main window: {err}");
}
if let Err(err) = window.unminimize() {
log::error!("[tray] failed to unminimize main window: {err}");
}
if let Err(err) = window.set_focus() {
log::error!("[tray] failed to focus main window: {err}");
}
} else {
log::error!("[tray] main window not found");
}
}
fn setup_tray(app: &AppHandle) -> tauri::Result<()> {
log::info!("[tray] setting up tray icon");
let show_item = MenuItem::with_id(app, "tray_show_window", "Open OpenHuman", true, None::<&str>)?;
let quit_item = MenuItem::with_id(app, "tray_quit", "Quit", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&show_item, &quit_item])?;
let icon = app
.default_window_icon()
.cloned()
.ok_or_else(|| tauri::Error::AssetNotFound("default window icon".to_string()))?;
TrayIconBuilder::with_id("openhuman-tray")
.icon(icon)
.menu(&menu)
.on_menu_event(|app, event| match event.id().as_ref() {
"tray_show_window" => {
log::info!("[tray] action=show_window source=menu");
show_main_window(app);
}
"tray_quit" => {
log::info!("[tray] action=quit source=menu");
app.exit(0);
}
_ => {}
})
.on_tray_icon_event(|tray, event| {
if let TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Up,
..
} = event
{
log::info!("[tray] action=show_window source=left_click");
show_main_window(tray.app_handle());
}
})
.build(app)?;
log::info!("[tray] tray icon ready");
Ok(())
}
pub fn run() {
let daemon_mode = is_daemon_mode();
@@ -59,30 +122,23 @@ pub fn run() {
if daemon_mode {
if let Some(window) = app.get_webview_window("main") {
let _ = window.hide();
log::info!("[tray] daemon_mode=true window_hidden_on_startup");
}
}
if let Err(err) = setup_tray(app.handle()) {
log::error!("[tray] failed to setup tray icon: {err}");
}
Ok(())
})
.invoke_handler(tauri::generate_handler![core_rpc_url])
.build({
let mut context = tauri::generate_context!();
if daemon_mode {
context.config_mut().app.windows.clear();
}
context
})
.build(tauri::generate_context!())
.expect("error while building tauri application")
.run(move |app_handle, event| match event {
#[cfg(target_os = "macos")]
RunEvent::Reopen { .. } => {
if !daemon_mode {
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.show();
let _ = window.unminimize();
let _ = window.set_focus();
}
}
show_main_window(app_handle);
}
RunEvent::Exit => {
if let Some(core) = app_handle.try_state::<core_process::CoreProcessHandle>() {
+8 -1
View File
@@ -10,7 +10,7 @@ Non-desktop targets fail at compile time (`compile_error!` in `lib.rs`).
```
app/src-tauri/src/
├── lib.rs # `run()`, tray, plugins, `generate_handler!`, core startup
├── lib.rs # `run()`, tray/menu actions, plugins, `generate_handler!`, core startup
├── main.rs # Binary entry
├── core_process.rs # CoreProcessHandle, spawn/monitor openhuman sidecar
├── core_rpc.rs # HTTP client to core JSON-RPC
@@ -37,6 +37,13 @@ React (invoke)
`CoreProcessHandle` in `core_process.rs` starts or waits for the sidecar; `commands/core_relay.rs` optionally ensures a **service-managed** core is running before relaying.
## Window and tray behavior
- The shell creates a tray icon at startup and wires actions to open the main window or quit.
- In daemon mode (`daemon` / `--daemon`), the main window is hidden on launch and can be reopened from tray actions.
- On macOS `RunEvent::Reopen` also restores and focuses the main window.
- Windows and Linux use the same tray actions (`Open OpenHuman`, `Quit`), with desktop-environment-specific tray rendering differences on some Linux setups.
## Bundled resources
`tauri.conf.json` bundles **`../../skills/skills`** and **`../../src/openhuman/agent/prompts`** so skills and prompt markdown ship with the app.