From 94011f377e09f4fc392e7dc841ffb0c0ef1c0b7f Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Sat, 16 May 2026 11:32:22 +0200 Subject: [PATCH 1/2] feat(ui): add splash screen to mask cold-start delay Main window now starts `visible: false`; a small transparent secondary window (`splashscreen`, 360x240, always-on-top, off taskbar) shows the WaveFlow logo + indeterminate progress bar while the Rust setup chain runs and the React bundle parses. main.tsx shows the main window after the first React frame, then closes the splash so the desktop is never visible between the two. Particularly noticeable on the very first launch after install, when Windows SmartScreen / Defender scans every freshly-extracted DLL. --- docs/features/ui.md | 6 ++ public/splash.html | 165 ++++++++++++++++++++++++++++++++++++++ src-tauri/tauri.conf.json | 19 ++++- src/main.tsx | 35 ++++++++ 4 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 public/splash.html diff --git a/docs/features/ui.md b/docs/features/ui.md index a8b29eb..7e47188 100644 --- a/docs/features/ui.md +++ b/docs/features/ui.md @@ -50,6 +50,12 @@ Two entry points in the [`PlayerBar`](../../src/components/player/PlayerBar.tsx) - **Interactive seek bar** — slim white bar at the bottom, click/drag to scrub. Same `pointer capture` + local `dragMs` pattern as the main `ProgressBar`. Thumb + timestamps fade in on hover so the idle widget stays minimal. - **Capabilities** — the mini-player's window label is added to [`capabilities/default.json`](../../src-tauri/capabilities/default.json) so it inherits every command the main window has access to (no duplicated capability file, no per-window permission pruning). +## Splash screen + +To hide the cold-start delay (Windows SmartScreen / Defender scanning every freshly-extracted DLL on the very first launch after install, plus the `setup()` chain in [`lib.rs`](../../src-tauri/src/lib.rs) — opening `app.db` + running migrations, creating the default profile, cold-initialising cpal/WASAPI), the main window is created with `"visible": false` and a small secondary window (`label: "splashscreen"`, 360×240, transparent, decorations off, always-on-top, off the taskbar) shows a WaveFlow logo + indeterminate progress bar while the backend boots and the React bundle parses. + +The static HTML lives in [`public/splash.html`](../../public/splash.html) (no JS, inline SVG logo, single CSS animation) so it paints the instant the WebView2 process spawns. [`main.tsx`](../../src/main.tsx) runs the takeover after the first React frame: show the main window first, then close the splash so the desktop is never visible between the two. The mini-player webview branches out via `?mini=1` and skips the dance. + ## System tray Quick playback controls (Play/Pause, Previous, Next, Quitter). Close-to-tray is the default close behaviour — the `WindowEvent::CloseRequested` handler hides the window unless the tray "Quitter" item armed `QuitGate`. Tray ID is `waveflow`. diff --git a/public/splash.html b/public/splash.html new file mode 100644 index 0000000..a10ab79 --- /dev/null +++ b/public/splash.html @@ -0,0 +1,165 @@ + + + + + + WaveFlow + + + +
+ +
WaveFlow
+ +
+ + diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index d464a17..ce57794 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -12,12 +12,29 @@ "app": { "windows": [ { + "label": "main", "title": "WaveFlow", "width": 1440, "height": 900, "minWidth": 1000, "minHeight": 650, - "center": true + "center": true, + "visible": false + }, + { + "label": "splashscreen", + "url": "splash.html", + "title": "WaveFlow", + "width": 360, + "height": 240, + "center": true, + "resizable": false, + "decorations": false, + "transparent": true, + "alwaysOnTop": true, + "skipTaskbar": true, + "focus": false, + "shadow": false } ], "security": { diff --git a/src/main.tsx b/src/main.tsx index 760b51c..1b243ef 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,5 +1,6 @@ import React from "react"; import ReactDOM from "react-dom/client"; +import { getCurrentWindow, Window as TauriWindow } from "@tauri-apps/api/window"; import App from "./App"; import { MiniPlayerApp } from "./MiniPlayerApp"; import "./app.css"; @@ -10,6 +11,39 @@ import { i18nReady } from "./i18n"; // a stripped-down provider tree (no LibraryContext / sidebar / etc). const isMini = new URLSearchParams(window.location.search).get("mini") === "1"; +// The main window is created with `visible: false` in tauri.conf.json so +// the user never sees a white WebView while Rust setup + React mount run. +// A `splashscreen` window is created in its place (small, transparent, +// always-on-top) to give visual feedback during the cold-start delay +// — especially on the very first launch after install, when Windows +// SmartScreen / Defender scans every freshly-extracted DLL. +// +// We reveal the main window after the first frame is painted, then +// close the splash. Order matters: show main BEFORE closing splash so +// there's never a moment where the desktop is visible between the two. +// The mini-player is its own window opened explicitly with visible: +// true, so skip the dance there. +function revealMainWindow() { + if (isMini) return; + requestAnimationFrame(() => { + requestAnimationFrame(() => { + void (async () => { + try { + await getCurrentWindow().show(); + } catch (err) { + console.error("[main] window.show failed", err); + } + try { + const splash = await TauriWindow.getByLabel("splashscreen"); + if (splash) await splash.close(); + } catch (err) { + console.error("[main] splash close failed", err); + } + })(); + }); + }); +} + i18nReady .catch((err) => { console.error("[i18n] initialization failed", err); @@ -20,4 +54,5 @@ i18nReady {isMini ? : } , ); + revealMainWindow(); }); From 4246a30748ce7c19533ac6090ccb347d6a5f2bae Mon Sep 17 00:00:00 2001 From: InstaZDLL Date: Sat, 16 May 2026 11:38:48 +0200 Subject: [PATCH 2/2] fix(ui): address coderabbit review on splash screen - switch splash lang from fr to en (paints before i18n is initialised, English is the neutral fallback for screen readers in any locale) - drop redundant -webkit-app-region: drag on .card (splash is ephemeral and non-focusable, nothing to drag) --- public/splash.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/public/splash.html b/public/splash.html index a10ab79..81df78e 100644 --- a/public/splash.html +++ b/public/splash.html @@ -1,5 +1,5 @@ - + @@ -38,7 +38,6 @@ #121212; border-radius: 14px; box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55); - -webkit-app-region: drag; } .logo { width: 96px; @@ -93,7 +92,7 @@ -
+