From 6d9018e6b68074b1c28f19089d7c3e6ea92af5d3 Mon Sep 17 00:00:00 2001 From: cucadmuh <49571317+cucadmuh@users.noreply.github.com> Date: Wed, 15 Jul 2026 11:05:43 +0300 Subject: [PATCH] fix(tray): blank Mainstage after cold-start minimized to tray (#1303) --- CHANGELOG.md | 8 +++- public/startup-splash-reveal.js | 30 ++++++++++++++- src-tauri/src/lib.rs | 22 ----------- src-tauri/src/lib_commands/ui/mini.rs | 40 -------------------- src/features/home/pages/Home.tsx | 7 +++- src/lib/settings/readStartMinimizedToTray.ts | 2 +- 6 files changed, 43 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1d11e8f..33bb0def 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -163,6 +163,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Fixed +### Tray — blank Mainstage after cold start minimized to tray + +**By [@cucadmuh](https://github.com/cucadmuh), PR [#1303](https://github.com/Psychotoxical/psysonic/pull/1303)** + +* With **Start minimized to tray** enabled, the main content area could stay blank after opening from the tray (sidebar and queue rendered, Mainstage did not) until a manual tray hide/show cycle — `PAUSE_RENDERING_JS` on cold start froze Mainstage's `animate-fade-in` at `opacity: 0`. Cold-start tray now hides without pausing animations, `startup-splash-reveal.js` proactively hides when the tray flag is set, Mainstage drops the entrance fade, and showing the window resumes rendering. + ### Tray — release build compile after #1296 **By [@cucadmuh](https://github.com/cucadmuh), PR [#1298](https://github.com/Psychotoxical/psysonic/pull/1298)** @@ -173,7 +179,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 **By [@cucadmuh](https://github.com/cucadmuh), PR [#1296](https://github.com/Psychotoxical/psysonic/pull/1296)** -* With **Start minimized to tray** enabled, opening the main window from the tray could leave the left sidebar menu invisible until a full app restart (seen on Linux tiling WMs such as Hyprland). The sidebar no longer uses a slide-in entrance animation that starts at `opacity: 0`; cold-start tray hide restores the original pause-and-hide path from PR #1271, and tray show again resumes rendering before `show()` without an extra `unminimize()` that could pop the window on tiling WMs. +* With **Start minimized to tray** enabled, opening the main window from the tray could leave the left sidebar menu invisible until a full app restart (seen on Linux tiling WMs such as Hyprland). The sidebar no longer uses a slide-in entrance animation that starts at `opacity: 0`; tray show resumes rendering before `show()` without an extra `unminimize()` that could pop the window on tiling WMs. ### Playlist and radio custom covers blank diff --git a/public/startup-splash-reveal.js b/public/startup-splash-reveal.js index a4c95bc7..13e85b5a 100644 --- a/public/startup-splash-reveal.js +++ b/public/startup-splash-reveal.js @@ -1,5 +1,7 @@ /** * Show the native window after the inline startup splash has painted. + * When starting minimized to tray, hide the main window as early as possible + * (visible:false may still map briefly on some Linux WMs before this script). * __TAURI_INTERNALS__ may not exist yet when this script first runs. */ (function startupSplashReveal() { @@ -12,8 +14,22 @@ return true; } + function tryHideMainWindow() { + var internals = window.__TAURI_INTERNALS__; + if (!internals || typeof internals.invoke !== 'function') return false; + internals.invoke('plugin:window|hide', { label: 'main' }).catch(function () {}); + return true; + } + function reveal(attempt) { - if (window.__psyStartMinimizedToTray) return; + if (window.__psyStartMinimizedToTray) { + if (tryHideMainWindow()) return; + if (attempt >= MAX_ATTEMPTS) return; + window.setTimeout(function () { + reveal(attempt + 1); + }, 50); + return; + } if (tryShowMainWindow()) return; if (attempt >= MAX_ATTEMPTS) return; window.setTimeout(function () { @@ -21,6 +37,18 @@ }, 50); } + if (window.__psyStartMinimizedToTray) { + // Mark this synchronously, before React mounts. This deliberately does + // not set the CSS animation-pause attribute: entrance animations may + // still mount while the native window is hidden. + window.__psyHidden = true; + try { + sessionStorage.setItem('psy-startup-tray-handled', '1'); + } catch (_err) {} + reveal(0); + return; + } + requestAnimationFrame(function () { requestAnimationFrame(function () { reveal(0); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 58de8860..0cf26a50 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -906,28 +906,6 @@ pub fn run() { } } }) - .on_page_load(|webview, payload| { - if webview.label() != "main" { - return; - } - - match payload.event() { - tauri::webview::PageLoadEvent::Started => { - let app = webview.app_handle().clone(); - std::thread::spawn(move || { - std::thread::sleep(std::time::Duration::from_millis(48)); - if let Some(window) = app.get_webview_window("main") { - crate::lib_commands::ui::mini::eval_startup_main_window_visibility(&window); - } - }); - } - tauri::webview::PageLoadEvent::Finished => { - if let Some(window) = webview.app_handle().get_webview_window("main") { - crate::lib_commands::ui::mini::eval_startup_main_window_visibility(&window); - } - } - } - }) .invoke_handler(tauri::generate_handler![ greet, theme_import::import_theme_zip, diff --git a/src-tauri/src/lib_commands/ui/mini.rs b/src-tauri/src/lib_commands/ui/mini.rs index c40457ed..6cc1c063 100644 --- a/src-tauri/src/lib_commands/ui/mini.rs +++ b/src-tauri/src/lib_commands/ui/mini.rs @@ -169,46 +169,6 @@ document.documentElement.style.removeProperty('--psy-anim-speed'); })(); "#; -/// Show the main window after startup splash paint, or pause rendering and hide -/// when the user chose "start minimized to tray" (flag set in -/// `startup-splash-preflight.js`). -/// -/// The shell no longer uses entrance animations that start at `opacity: 0`, so -/// pausing CSS here is safe on WebKitGTK tiling WMs (sidebar fix in #1296). -pub(crate) fn eval_startup_main_window_visibility(window: &tauri::WebviewWindow) { - let js = format!( - "(function () {{ - try {{ - if (sessionStorage.getItem('psy-startup-tray-handled') === '1') return; - }} catch (e) {{}} - var deferToTray = !!window.__psyStartMinimizedToTray; - if (!deferToTray) {{ - try {{ - var raw = localStorage.getItem('psysonic-auth'); - if (raw) {{ - var state = JSON.parse(raw).state; - deferToTray = !!(state && state.startMinimizedToTray && state.showTrayIcon !== false); - }} - }} catch (e) {{}} - }} - var internals = window.__TAURI_INTERNALS__; - if (deferToTray) {{ - {pause} - try {{ sessionStorage.setItem('psy-startup-tray-handled', '1'); }} catch (e) {{}} - if (internals && typeof internals.invoke === 'function') {{ - internals.invoke('plugin:window|hide', {{ label: 'main' }}).catch(function () {{}}); - }} - return; - }} - if (internals && typeof internals.invoke === 'function') {{ - internals.invoke('plugin:window|show', {{ label: 'main' }}).catch(function () {{}}); - }} -}})();", - pause = PAUSE_RENDERING_JS.trim(), - ); - let _ = window.eval(&js); -} - /// Resume rendering and bring the main window to the foreground. pub(crate) fn restore_main_window(main: &tauri::WebviewWindow) -> Result<(), String> { main.eval(RESUME_RENDERING_JS).map_err(|e| e.to_string())?; diff --git a/src/features/home/pages/Home.tsx b/src/features/home/pages/Home.tsx index 001388fd..3ec8d3e1 100644 --- a/src/features/home/pages/Home.tsx +++ b/src/features/home/pages/Home.tsx @@ -345,7 +345,12 @@ export default function Home() { // sidebar) instead of leaving the user on nothing. const allSectionsHidden = homeSections.every(s => !s.visible); return ( -
+
{!loading && !perfFlags.disableMainstageHero && isVisible('hero') && }
diff --git a/src/lib/settings/readStartMinimizedToTray.ts b/src/lib/settings/readStartMinimizedToTray.ts index 09ba3197..30a90a31 100644 --- a/src/lib/settings/readStartMinimizedToTray.ts +++ b/src/lib/settings/readStartMinimizedToTray.ts @@ -1,6 +1,6 @@ const AUTH_STORAGE_KEY = 'psysonic-auth'; -/** Keep in sync with `public/startup-splash-preflight.js` and Rust `eval_startup_main_window_visibility`. */ +/** Keep in sync with `public/startup-splash-preflight.js` and `public/startup-splash-reveal.js`. */ export const STARTUP_TRAY_HANDLED_KEY = 'psy-startup-tray-handled'; /** Read persisted "start minimized to tray" before Zustand rehydrates. */