Remove CSD titlebar buttons, fix tray toggle and quit
- Replace GTK header bar with buttonless version to work around upstream bug where CSD buttons become non-interactive after hide()/show() (tauri-apps/tauri#11856, tauri-apps/tao#1046) - Tray menu now toggles between "Hide Claude" / "Show Claude" - Left-click tray icon toggles window visibility - Fix quit from tray (use std::process::exit to bypass keep-alive) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c024717934
commit
6b2c8ffa08
3 changed files with 62 additions and 21 deletions
1
src-tauri/Cargo.lock
generated
1
src-tauri/Cargo.lock
generated
|
|
@ -451,6 +451,7 @@ dependencies = [
|
||||||
name = "claude-app"
|
name = "claude-app"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"gtk",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
url = "2"
|
url = "2"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
gtk = { version = "0.18", features = ["v3_24"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["custom-protocol"]
|
default = ["custom-protocol"]
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
|
|
||||||
|
|
@ -97,11 +97,10 @@ pub fn run() {
|
||||||
.plugin(tauri_plugin_opener::init())
|
.plugin(tauri_plugin_opener::init())
|
||||||
.invoke_handler(tauri::generate_handler![send_notification])
|
.invoke_handler(tauri::generate_handler![send_notification])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
// -- Create main window navigating directly to claude.ai --
|
// NOTE: Decorations disabled due to upstream bug where CSD titlebar
|
||||||
//
|
// buttons become non-interactive after hide()/show() on Linux.
|
||||||
// on_navigation and on_new_window are builder methods, so they must
|
// Tracked at: https://github.com/tauri-apps/tauri/issues/11856
|
||||||
// be chained before .build(). The close-to-tray handler is set on
|
// https://github.com/tauri-apps/tao/issues/1046
|
||||||
// the built window via on_window_event.
|
|
||||||
let webview_window = WebviewWindowBuilder::new(
|
let webview_window = WebviewWindowBuilder::new(
|
||||||
app,
|
app,
|
||||||
"main",
|
"main",
|
||||||
|
|
@ -110,6 +109,7 @@ pub fn run() {
|
||||||
.title("Claude")
|
.title("Claude")
|
||||||
.inner_size(1200.0, 800.0)
|
.inner_size(1200.0, 800.0)
|
||||||
.min_inner_size(400.0, 300.0)
|
.min_inner_size(400.0, 300.0)
|
||||||
|
.resizable(true)
|
||||||
// Standard WebKit user agent so claude.ai serves the full experience
|
// Standard WebKit user agent so claude.ai serves the full experience
|
||||||
.user_agent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15")
|
.user_agent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15")
|
||||||
.initialization_script(NOTIFICATION_BRIDGE_JS)
|
.initialization_script(NOTIFICATION_BRIDGE_JS)
|
||||||
|
|
@ -124,38 +124,71 @@ pub fn run() {
|
||||||
})
|
})
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
// -- Close-to-tray: hide window instead of quitting --
|
// Replace the default GTK header bar with one that has no buttons.
|
||||||
let win_clone = webview_window.clone();
|
// The standard CSD buttons become non-interactive after hide()/show()
|
||||||
webview_window.on_window_event(move |event| {
|
// due to upstream bug: https://github.com/tauri-apps/tauri/issues/11856
|
||||||
if let WindowEvent::CloseRequested { api, .. } = event {
|
#[cfg(target_os = "linux")]
|
||||||
api.prevent_close();
|
{
|
||||||
let _ = win_clone.hide();
|
use gtk::prelude::*;
|
||||||
|
if let Ok(gtk_win) = webview_window.gtk_window() {
|
||||||
|
let header = gtk::HeaderBar::new();
|
||||||
|
header.set_show_close_button(false);
|
||||||
|
header.set_title(Some("Claude"));
|
||||||
|
header.show();
|
||||||
|
gtk_win.set_titlebar(Some(&header));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// -- System tray --
|
// -- System tray --
|
||||||
let show_item =
|
let toggle_item =
|
||||||
MenuItem::with_id(app, "show", "Show Claude", true, None::<&str>)?;
|
MenuItem::with_id(app, "toggle", "Hide Claude", true, None::<&str>)?;
|
||||||
let quit_item =
|
let quit_item =
|
||||||
MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
|
MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
|
||||||
let menu = Menu::with_items(app, &[&show_item, &quit_item])?;
|
let menu = Menu::with_items(app, &[&toggle_item, &quit_item])?;
|
||||||
|
|
||||||
let tray_icon = Image::from_bytes(include_bytes!("../icons/32x32.png"))?;
|
let tray_icon = Image::from_bytes(include_bytes!("../icons/32x32.png"))?;
|
||||||
|
|
||||||
|
fn show_window(win: &tauri::WebviewWindow, toggle: &MenuItem<tauri::Wry>) {
|
||||||
|
let _ = win.show();
|
||||||
|
let _ = win.set_focus();
|
||||||
|
let _ = toggle.set_text("Hide Claude");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide_window(win: &tauri::WebviewWindow, toggle: &MenuItem<tauri::Wry>) {
|
||||||
|
let _ = win.hide();
|
||||||
|
let _ = toggle.set_text("Show Claude");
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Close-to-tray: hide window instead of quitting --
|
||||||
|
let win_for_close = webview_window.clone();
|
||||||
|
let toggle_for_close = toggle_item.clone();
|
||||||
|
webview_window.on_window_event(move |event| {
|
||||||
|
if let WindowEvent::CloseRequested { api, .. } = event {
|
||||||
|
api.prevent_close();
|
||||||
|
hide_window(&win_for_close, &toggle_for_close);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let toggle_for_menu = toggle_item.clone();
|
||||||
|
let toggle_for_tray = toggle_item.clone();
|
||||||
let tray_handle = app.handle().clone();
|
let tray_handle = app.handle().clone();
|
||||||
TrayIconBuilder::new()
|
TrayIconBuilder::new()
|
||||||
.icon(tray_icon)
|
.icon(tray_icon)
|
||||||
.tooltip("Claude")
|
.tooltip("Claude")
|
||||||
.menu(&menu)
|
.menu(&menu)
|
||||||
.on_menu_event(move |app_handle, event| match event.id().as_ref() {
|
.on_menu_event(move |app_handle, event| match event.id().as_ref() {
|
||||||
"show" => {
|
"toggle" => {
|
||||||
if let Some(win) = app_handle.get_webview_window("main") {
|
if let Some(win) = app_handle.get_webview_window("main") {
|
||||||
let _ = win.show();
|
let visible = win.is_visible().unwrap_or(false);
|
||||||
let _ = win.set_focus();
|
if visible {
|
||||||
|
hide_window(&win, &toggle_for_menu);
|
||||||
|
} else {
|
||||||
|
show_window(&win, &toggle_for_menu);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"quit" => {
|
"quit" => {
|
||||||
app_handle.exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
})
|
})
|
||||||
|
|
@ -166,8 +199,12 @@ pub fn run() {
|
||||||
} = event
|
} = event
|
||||||
{
|
{
|
||||||
if let Some(win) = tray_handle.get_webview_window("main") {
|
if let Some(win) = tray_handle.get_webview_window("main") {
|
||||||
let _ = win.show();
|
let visible = win.is_visible().unwrap_or(false);
|
||||||
let _ = win.set_focus();
|
if visible {
|
||||||
|
hide_window(&win, &toggle_for_tray);
|
||||||
|
} else {
|
||||||
|
show_window(&win, &toggle_for_tray);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue