From d5dd866ac647892dbccd47bb8e374c9a93d752a9 Mon Sep 17 00:00:00 2001 From: Sergey Date: Wed, 30 Sep 2026 21:54:07 +0300 Subject: [PATCH] fix(linux): hide native window menu bar and add frameless window resize borders --- src-tauri/capabilities/default.json | 1 + src-tauri/src/app_menu.rs | 44 +++++++++----- src/App.tsx | 3 +- src/components/WindowResizeBorders.tsx | 80 ++++++++++++++++++++++++++ src/components/index.ts | 1 + 5 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 src/components/WindowResizeBorders.tsx diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index e8ccdf97..3c6d73b9 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -9,6 +9,7 @@ "core:window:allow-close", "core:window:allow-minimize", "core:window:allow-start-dragging", + "core:window:allow-start-resize-dragging", "core:window:allow-toggle-maximize", "opener:default", "dialog:default", diff --git a/src-tauri/src/app_menu.rs b/src-tauri/src/app_menu.rs index 70404dfe..0c4affd3 100644 --- a/src-tauri/src/app_menu.rs +++ b/src-tauri/src/app_menu.rs @@ -1,20 +1,23 @@ -//! Native application menu management. - -use tauri::{ - menu::{AboutMetadata, CheckMenuItem, Menu, MenuItem, PredefinedMenuItem, Submenu}, - AppHandle, Emitter, Runtime, -}; +#[cfg(target_os = "macos")] +use tauri::menu::{AboutMetadata, CheckMenuItem, Menu, MenuItem, PredefinedMenuItem, Submenu}; +use tauri::{AppHandle, Emitter, Runtime}; +#[cfg(target_os = "macos")] +use crate::types::AppSettings; #[cfg(target_os = "macos")] pub(crate) use crate::types::DockDisplayMode; use crate::{ auth::{load_app_settings, save_app_settings}, - types::{AppSettings, TrayDisplayMode}, + types::TrayDisplayMode, }; +#[cfg(target_os = "macos")] const TRAY_ICON_AND_SESSION_ID: &str = "tray-display-icon-and-session"; +#[cfg(target_os = "macos")] const TRAY_ACTIVE_USAGE_TEXT_ID: &str = "tray-display-active-usage-text"; +#[cfg(target_os = "macos")] const TRAY_HIDDEN_ID: &str = "tray-display-hidden"; +#[cfg(target_os = "macos")] const DESKTOP_REOPEN_SETTINGS_ID: &str = "desktop-reopen-settings"; #[cfg(target_os = "macos")] pub(crate) const DOCK_SHOW_IN_DOCK_ID: &str = "dock-display-show-in-dock"; @@ -23,22 +26,34 @@ pub(crate) const DOCK_MENU_BAR_ONLY_ID: &str = "dock-display-menu-bar-only"; pub fn setup(app: &AppHandle) -> tauri::Result<()> { #[cfg(target_os = "macos")] - apply_saved_dock_display_mode(app); - refresh(app)?; - app.on_menu_event(handle_menu_event); + { + apply_saved_dock_display_mode(app); + refresh(app)?; + app.on_menu_event(handle_menu_event); + } + #[cfg(not(target_os = "macos"))] + { + // On non-macOS platforms, Codex Switcher uses a frameless custom UI (decorations: false). + // Settings are managed in the tray menu and in-app UI, so no native window menu is attached. + refresh(app)?; + } Ok(()) } pub fn refresh(app: &AppHandle) -> tauri::Result<()> { - let settings = load_app_settings().unwrap_or_default(); - let menu = build_menu(app, &settings)?; - app.set_menu(menu)?; + #[cfg(target_os = "macos")] + { + let settings = load_app_settings().unwrap_or_default(); + let menu = build_menu(app, &settings)?; + app.set_menu(menu)?; + } if let Err(error) = app.emit("app-settings-changed", ()) { eprintln!("Failed to notify settings changes: {error}"); } Ok(()) } +#[cfg(target_os = "macos")] fn handle_menu_event(app: &AppHandle, event: tauri::menu::MenuEvent) { let item_id = event.id(); @@ -61,6 +76,7 @@ fn handle_menu_event(app: &AppHandle, event: tauri::menu::MenuEvent) { } } +#[cfg(target_os = "macos")] fn tray_display_mode_for_item(item_id: &str) -> Option { Some(match item_id { TRAY_ICON_AND_SESSION_ID => TrayDisplayMode::IconAndSession, @@ -70,6 +86,7 @@ fn tray_display_mode_for_item(item_id: &str) -> Option { }) } +#[cfg(target_os = "macos")] pub(crate) fn update_tray_display_mode(app: &AppHandle, mode: TrayDisplayMode) { if let Err(error) = set_tray_display_mode(app, mode) { eprintln!("Failed to update tray display mode: {error}"); @@ -186,6 +203,7 @@ fn apply_dock_display_mode(app: &AppHandle, mode: DockDisplayMode } } +#[cfg(target_os = "macos")] fn build_menu(app: &AppHandle, settings: &AppSettings) -> tauri::Result> { let pkg_info = app.package_info(); let config = app.config(); diff --git a/src/App.tsx b/src/App.tsx index 9e836c6c..0dacc7b0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,7 +6,7 @@ import { SettingsModal } from "./components/SettingsModal"; import { finishForceClose, type DesktopReopenPreference } from "./lib/desktopReopen"; import type { CodexClosePreference } from "./lib/codexClosePreference"; import { useForceCloseCodexProcesses } from "./hooks/useForceCloseCodexProcesses"; -import { AccountCard, AddAccountModal, UpdateChecker } from "./components"; +import { AccountCard, AddAccountModal, UpdateChecker, WindowResizeBorders } from "./components"; import type { AccountWithUsage, CodexProcessInfo, DockDisplayMode, UsageInfo } from "./types"; import { exportFullBackupFile, @@ -1349,6 +1349,7 @@ function App() { return (
+
= ({ + isMaximized = false, +}) => { + if (!isTauriRuntime() || isMacOs || isMaximized) { + return null; + } + + const handleResizeStart = + (direction: ResizeDirection) => (event: React.MouseEvent) => { + if (event.button !== 0) return; + event.preventDefault(); + event.stopPropagation(); + void getTauriWindow()?.startResizeDragging(direction); + }; + + return ( +