diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs
index 1ac2b29..2322010 100644
--- a/desktop/src-tauri/src/lib.rs
+++ b/desktop/src-tauri/src/lib.rs
@@ -63,6 +63,7 @@ pub fn run() {
.setup(|app| {
use tauri::Manager;
log_environment(app);
+ paint_window_before_the_webview_does(app);
// The on-device store lives in the platform app-data dir (e.g. Linux
// ~/.local/share/com.fabledsword.thoughtsync/thoughtsync.db), created on
// first launch. This is what makes the app work with no server or login.
@@ -143,6 +144,43 @@ pub fn run() {
.expect("error while running the ThoughtSync desktop app");
}
+/// Match the window's own background to the theme the UI is about to render in.
+///
+/// There is a gap between the window appearing and the webview painting its first
+/// frame, and in it the platform's default background shows through — white. On a
+/// dark-mode desktop that is the harshest thing the app does, and forcing WebKit's
+/// software rendering (see `harden_linux_webkit_rendering`) makes the gap wider,
+/// not narrower.
+///
+/// Done here rather than as `app.windows[].backgroundColor` in tauri.conf.json
+/// because that config takes ONE static colour, and picking either one would fix
+/// half of users while introducing the same flash for the other half. Reading the
+/// live theme is the only version that is never a regression.
+///
+/// Best-effort throughout: a window that won't tell us its theme, or won't take a
+/// colour, is a cosmetic loss and must never stop the app from opening.
+fn paint_window_before_the_webview_does(app: &tauri::App) {
+ use tauri::Manager;
+ let Some(window) = app.get_webview_window("main") else {
+ return;
+ };
+ // Unknown theme reads as light, matching the platform default we'd get anyway.
+ let dark = matches!(window.theme(), Ok(tauri::Theme::Dark));
+ // The two values style.css actually paints: neutral-950 and neutral-50.
+ let color = if dark {
+ tauri::window::Color(10, 10, 10, 255)
+ } else {
+ tauri::window::Color(250, 250, 250, 255)
+ };
+ match window.set_background_color(Some(color)) {
+ Ok(()) => log::info!(
+ "window background set for the {} theme",
+ if dark { "dark" } else { "light" }
+ ),
+ Err(e) => log::warn!("could not set the window background: {e}"),
+ }
+}
+
/// Expire old trash at startup, on an unlinked device only (see `local::retention`).
///
/// At startup rather than on a timer: a desktop app isn't a server, and a sweep the
diff --git a/frontend/src/components/AppShell.vue b/frontend/src/components/AppShell.vue
index f183573..3a304af 100644
--- a/frontend/src/components/AppShell.vue
+++ b/frontend/src/components/AppShell.vue
@@ -260,9 +260,15 @@ async function signOut() {
>
New
- {{
- session.user?.display_name
- }}
+
+ {{ session.user?.display_name }}
-
+
+
-