Skip to content

fix(session-window): stop the startup flash in detached windows - #1236

Merged
Harry19081 merged 2 commits into
developfrom
dev/fix-detached-window-startup-flash
Sep 3, 2026
Merged

fix(session-window): stop the startup flash in detached windows#1236
Harry19081 merged 2 commits into
developfrom
dev/fix-detached-window-startup-flash

Conversation

@Lando801

@Lando801 Lando801 commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Problem

Opening a detached session window showed three states in a row: the window
appeared transparent, flashed an opaque plate, then settled back to its
vibrancy. Two independent layers painted that plate and neither of them was
the app's own surface.

On macOS the window is built transparent with an NSVisualEffectView behind
its webview and settles on that material (html[data-host-desktop="macos"] in
src/index.scss paints only a 15% tint). Two things covered it during boot:

  1. apply_window_background_color toggled _setDrawsBackground: on the
    WKWebView but never pinned underPageBackgroundColor. WebKit derives that
    colour from the document, and before the first document has painted there
    is nothing to derive from, so it falls back to white — for the entire
    cold boot of that window's webview, since each Tauri window re-parses and
    re-executes the bundle.
  2. public/index.html's splash plate, html, body, #root { background: var(--splash-bg) }. On a light theme that resolves to #ffffff, and
    secondary windows suppress the splash mark, so it was a bare white
    rectangle over the vibrancy.

On Windows and Linux the secondary window is opaque (transparent(true) in
open_session_window is macOS-only), and its startup backdrop was a hardcoded
#0d0d0d. On a light theme that opened the window dark, then the splash plate
repainted it white, then the app painted — two flashes, neither the app's
colour.

Separately, the window was built visible(true), so the frames between
build() and the post-build chrome reached the screen as the leading
transparent state.

Solution

The pre-paint surface should be whatever the window settles on, so there is no
seam to see.

  • set_webview_background_recursive (was set_draws_background_recursive) now
    pins underPageBackgroundColor alongside _setDrawsBackground:, so the
    webview paints the intended colour instead of its white default, and the
    setting survives the navigation that re-derives it. Public API from macOS 12
    and the bundle targets 10.15, so it is probed with respondsToSelector:.
    It also returns whether a WKWebView was actually found, and both callers warn
    when it was not — the previous version failed silently, which is exactly the
    case that is indistinguishable from a working fix.

  • open_session_window no longer calls apply_window_background_color (right
    for the main window, which settles opaque; wrong here). It mounts the
    vibrancy material and clears the builder backdrop, so the webview composites
    onto the material from its first frame. public/index.html skips the splash
    plate for macOS secondary windows to match, scoped to
    [data-host-desktop="macos"][data-orgii-secondary-window] so opaque hosts
    and the main window keep it.

  • New startup_backdrop.rs resolves the opaque backdrop from general.theme
    and matches --splash-bg exactly (#ffffff / #141414), mirroring
    normalizeGlobalThemePreference + LEGACY_THEME_ALIASES from
    globalThemes.ts and falling back to the OS theme for system, absent, and
    unrecognised values.

  • The window is built hidden and shown once its chrome is applied. The show()
    stays synchronous with creation, not deferred to the frontend's first
    paint — deferring it would make the click that opens the window feel dead.

  • Updated the Team Inbox test harness to preserve the real Git-remotes module exports while overriding only getGitRemotes; this prevents the PR import graph from exposing a stale full-module mock during full-suite collection.

Potential risks

  • The macOS half is diagnosed by elimination, not yet observed working. An
    earlier iteration of this change (the CSS plate plus dropping
    apply_window_background_color) shipped to a local dev build and the flash
    persisted, which is what identified underPageBackgroundColor as the
    remaining white source: the NSWindow was clearColor, the vibrancy is dark
    on a dark system appearance, and the page was proven transparent. The
    underPageBackgroundColor fix itself has not been run — see Verification.

  • underPageBackgroundColor is macOS 12+. On 10.15/11 the respondsToSelector:
    probe skips it and those hosts keep the old behaviour, including the white
    base. No crash, no regression, but no fix there either.

  • If apply_macos_window_material ever fails, the boot window is now
    see-through to the desktop where it previously showed a plate. The same
    failure already shows through after first paint, since the macOS rule paints
    only a 15% tint, so this widens an existing failure mode rather than adding
    one.

  • app_window now depends on the settings crate: one JSONC read per detached
    window open (a user gesture, not a hot path). read_settings() creates the
    settings file with {} when absent, so a window open can now create it. The
    dependency is acyclic — settings pulls only app_paths + tauri.

  • A non-baseline skin overrides --splash-bg from
    localStorage["orgii_skin_surface"], derived by deriveSkinTokens in TS.
    Nothing derives those tokens outside the bundle, so a skinned app gets
    ORGII's base surface as its backdrop. Light/dark polarity is still correct,
    which is what removes the flash; matching the exact skin surface would mean
    duplicating the skin registry in Rust.

  • The Windows/Linux backdrop change is reasoned from the shared code path and
    covered by unit tests, but has not been run on either platform.

  • The main window is untouched. apply_window_background_color still uses a
    fixed #0d0d0d for recreate_main_window, and tauri.conf.json's
    backgroundColor is static, so a light-theme macOS cold start still opens on
    a dark frame. Same defect class, different window, left for its own change.

  • The CI repair changes only a Vitest mock boundary; production code and runtime behavior are unaffected.

Verification

  • GitHub Actions CI run 33753170543 → frontend, Rust clippy/workspace tests, and cargo audit passed

  • ../../../node_modules/.bin/vitest run --config config/vitest.config.ts src/modules/MainApp/TeamInbox/__tests__/AssignedWorkItemDetail.test.ts --reporter=verbose9/9 passed, 0 failed

  • ../../../node_modules/.bin/eslint src/modules/MainApp/TeamInbox/__tests__/AssignedWorkItemDetail.test.ts --max-warnings 0 --report-unused-disable-directives → clean

  • ../../../node_modules/.bin/tsgo --noEmit --pretty false → clean

  • node scripts/quality/check-test-placement.mjs → consistent across 457 directories

Ran:

  • cargo test -p app_window — 6 passed (3 pre-existing + 3 new theme-resolution
    tests covering current ids, every legacy alias, and the follow-the-OS
    fallbacks).
  • cargo check -p app_window — clean.
  • cargo fmt -p app_window -- --check — clean.
  • npx vitest run src/app/root/__tests__/secondaryWindowStartupSurface.test.ts
    — 8 passed. Mutation-checked: removing the CSS override fails 4 of the 8, so
    the guard is not vacuous.
  • pnpm typecheck:fast (tsgo) — clean.
  • pnpm run check:test-placement — clean across 458 directories.
  • prettier --check, oxlint, eslint --max-warnings 0 on the changed TS —
    clean.
  • Cascade reproduced in a real engine, not asserted from source: fetched the
    served index.html, dropped the bundle scripts, set
    data-host-desktop="macos" + data-orgii-secondary-window, and read computed
    styles. --splash-bg resolves to #ffffff while html, body, #root and
    #splash are all rgba(0, 0, 0, 0) — the override wins and the page paints
    nothing.

Did not run:

  • The app. The local tauri dev stack was stopped before the
    underPageBackgroundColor change was built, so the macOS fix has never been
    exercised at runtime. The prior iteration was confirmed live (dev binary
    rebuilt before the retest) and did not fix the flash; this one is untested.
    Needs a run on macOS with a light app theme, opening a detached session
    window, before it can be called fixed.
  • Any Windows or Linux run.
  • The e2e suite.
  • The pre-commit hook. This branch was built with git commit-tree from a
    temp index because the checkout holds a concurrent session's unrelated work,
    so no hook ran and there is no Pre-commit hook ran. trailer. The checks it
    would have run (typecheck, oxlint, eslint, prettier) were run manually and
    are listed above.

No screenshots: the change is a sub-second pre-paint transient, and since the
fix is not yet confirmed working there is no correct-state frame to capture.

🤖 Generated with Claude Code

Lando801 and others added 2 commits September 3, 2026 18:36
A detached session window opened transparent, flashed an opaque plate,
then settled back to its vibrancy. Two separate layers painted that
plate and neither of them was the app's own surface.

macOS: apply_window_background_color enabled WKWebView background
drawing without pinning underPageBackgroundColor, so the webview fell
back to its white default for the whole bundle boot, and index.html's
--splash-bg plate (#ffffff on a light theme) painted over the vibrancy
on top of that. The window now mounts its material, clears the
backdrop, and pins the webview's own background; index.html skips the
plate for macOS secondary windows, which settle on vibrancy anyway.

Windows and Linux: the opaque startup backdrop was a hardcoded
#0d0d0d, so a light-theme window opened dark, then white, then the
app. It now follows general.theme and matches --splash-bg exactly.

The window is also built hidden and shown once its chrome is applied,
so the pre-chrome frames never reach the screen. The show stays
synchronous with creation rather than deferred to first paint.
@Harry19081
Harry19081 merged commit d3c6785 into develop Sep 3, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants