Skip to content

feat(desktop,web): window translucency + background modes (#58)#59

Merged
oorabona merged 15 commits into
mainfrom
feat/desktop-translucency
Jun 10, 2026
Merged

feat(desktop,web): window translucency + background modes (#58)#59
oorabona merged 15 commits into
mainfrom
feat/desktop-translucency

Conversation

@oorabona

@oorabona oorabona commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Closes #58

What

Three explicit background modes per scope — Image / Solid theme / Transparent — replacing the implicit "wallpaper set or not" model, plus native window effects on desktop (mica/blur/acrylic on Windows, vibrancy on macOS).

  • The desktop (Tauri) window is now created transparent: true on all OS; CSS paints opacity in non-transparent modes, so the mode switches instantly per the active pane's scope (same cascade + cross-fade mechanics as the window-wide wallpaper).
  • With mode Transparent, the OS desktop shows through and the four region opacity sliders act as glass.
  • Effect picker in the Wallpaper settings (desktop only, filtered by detected OS + Windows build; auto resolves to mica on Win11, blur on Win10, under-window vibrancy on macOS; hidden on Linux where every effect resolves to none). Unknown/invalid values degrade to none.
  • The native effect follows what is displayed: useWindowEffects derives the desired effect from the displayed background state exposed by useActiveWallpaper (resolved, cached, or fallback) — the effect can never outlive the painted background (pane closed, hung cascade fetch, cached scope, etc.). Native IPC is latest-wins, serialized, with reconciliation after stale completions.
  • Browsers: Transparent renders as the solid theme background; the mode selector stays available (per-scope server-side setting honored by desktop clients) with a "(desktop only)" hint.
  • Backward compatible: default mode image + empty wallpaper reproduces today's two states exactly — no profile migration.

Desktop asset-loading fixes (surfaced by the real-build smoke test, pre-existing on main)

  • Wallpapers (runtime layer AND settings thumbnails) now use hubBaseUrl()-prefixed URLs — relative /public/... URLs resolved against tauri://localhost and 404'd in the desktop app.
  • All /public/* assets (fonts, sounds, wallpapers; disk and SEA in-memory serving) now carry Cross-Origin-Resource-Policy: cross-origin — helmet's blanket same-origin made WebKit block font/image embeds from the tauri://localhost origin even with valid CORS. API routes keep same-origin; no other helmet setting changed.

Spec: docs/plans/desktop-translucency.md (19 BDD scenarios, hardened). Brief: docs/briefs/desktop-translucency.md.

Verification already run

  • Full suite green: 2540+ tests (incl. 29 tauri-config invariants, cascade/allowlist/TOML coverage, rendering decisions, effect lifecycle incl. cached/fallback/pane-close clears + out-of-order-IPC latest-wins — spec scenarios S1–S19 covered by named tests).
  • pnpm install --frozen-lockfile clean (new deps: @tauri-apps/plugin-os + cargo tauri-plugin-os).
  • cargo clippy --all-targets -- -D warnings clean; hub/web/shared typecheck clean.
  • Targeted biome clean on every touched file (NB: main is already lint-red on 4 pre-existing files — tracked separately, untouched here).
  • Orthogonal pre-push review (codex, multiple rounds until clean on final HEAD): caught the missing core:window:allow-set-effects capability, a setHeaders typing break, the stale-effect lifecycle class (fixed structurally), and the OS capability narrowing (os:allow-platform/os:allow-version instead of os:default). Copilot out of credits for the whole cycle — documented degraded coverage (codex + senior architectural review).
  • Linux smoke (real build, via WSLg): transparent mode shows the desktop through ✅; fonts + custom wallpapers ✅ after the asset-loading fixes (final re-test pending below).

Notes for review / before merge

  • macOS: effects are best-effort and untested on real hardware. Also, transparent: true has a known ~8× GPU power cost on macOS even for static content ([bug] macOS: transparent:true causes continuous full-window recomposite (~8x GPU power) even when the page is fully static tauri-apps/tauri#15471, open upstream) — accepted deliberately (macOS is not a tested distribution target yet); the mitigation path (macOS-only restart-gated opt-out) is designed in the brief.
  • Linux: plain alpha requires a running compositor; no native effects (documented in docs/SPEC.md).
  • Linux smoke (WSLg): transparent ✅, drag/resize ✅; fonts + wallpaper re-test after asset fixes: in progress
  • Manual smoke before merge (real builds): Windows 10 + 11 — desktop visible through Transparent mode, titlebar drag, window shadow, resize edges, effects applied (mica/blur), no stale effect after closing all panes.

oorabona added 4 commits June 10, 2026 03:02
Introduce per-scope background mode (image/solid/transparent) and window
effect (none/auto/mica/blur/acrylic) to terminal profiles. Resolve through
the 4-layer config cascade (defaults → TOML → host → channel) and persist
via the settings API. Web client renders the modes with transparent
backgrounds on Tauri; browsers fall back to the theme. Foundation for
native desktop window effects and solid/transparent rendering modes.
Apply native window effects (mica/blur/acrylic on Windows, vibrancy on macOS) when the active pane's background mode is "transparent". Platform detection via @tauri-apps/plugin-os, resolved at startup and cached; unknown effects degrade to none. Per-OS + Windows-build effect picker in Wallpaper settings, with desktop-only indicator for browsers. Out-of-order async IPC handled latest-wins; no effect calls while config cascade unresolved; clearEffects only when an effect was applied. macOS effects untested on real hardware; acrylic flagged as laggy on Win10.
…window

Without core:window:allow-set-effects and os:default in the default
capability, the webview's setEffects and platform/version IPC calls are
denied at runtime, leaving native window effects inert on desktop.
@oorabona

Copy link
Copy Markdown
Collaborator Author

Review coverage note: GitHub Copilot is out of credits (review request pending, never fulfilled; both orthogonal-gate attempts returned unable_to_assess). Coverage for this PR = codex xhigh orthogonal gate (1 refusal fixed in 78c0a9e, then clean on final HEAD) + senior architectural review (clean, all 16 spec scenarios mutation-verified). Remaining before merge: the manual smoke checklist in the PR body (Windows 10/11 + Linux real builds).

oorabona added 5 commits June 10, 2026 04:23
Wallpaper background URLs now use the hubBaseUrl() prefix in the Tauri
desktop context (same pattern as the existing @font-face injection).
Relative /public/wallpapers/ URLs resolve against tauri://localhost and
were 404ing; browsers unaffected (hubBaseUrl() returns "").

All /public/* asset responses (fonts, sounds, wallpapers) now carry
Cross-Origin-Resource-Policy: cross-origin instead of helmet's
same-origin default. Desktop webview (tauri://localhost) embeds these
cross-origin from http://localhost:<port>; CORP same-origin was
blocking valid CORS loads in WebKit. API routes and app shell keep
same-origin; no other helmet setting changed.
…aders type

Structural parameter type accepts both SetHeadersResponse and
ServerResponse; tsc --noEmit is green again (vitest and the esbuild
SEA bundle never typecheck, which is how the mismatch slipped through).
When the last pane closes, no channel is active, or the profile
permanently fails to resolve, the window effect is now cleared instead
of persisting indefinitely. Effect IPC calls are serialized to wait for
in-flight operations before reconciling. Fallback wallpaper detection
now exposes a flag to correctly determine active-scope state in App.vue.
Window-effect picker is hidden on Linux where effects resolve to no-op.
Tauri OS capability narrowed to only the two calls in use
(allow-platform, allow-version).
The window effect (blur, mica, vibrancy) now derives from the displayed
background state rather than scope/profile resolution gates. This ensures
the native effect always matches what the UI paints: transparent backgrounds
apply effects; solid, cached, fallback, or uninitialized backgrounds clear
them. Eliminates the prior disagreement where effects could outlive
displayed backgrounds in scope changes or fetch failures. Wallpaper picker
previews in the desktop app now render via hubBaseUrl().
oorabona added 6 commits June 10, 2026 11:43
The settings cascade load, theme save, and host-override writes used
relative /api URLs, which the Tauri webview resolves against
tauri://localhost instead of the hub — the settings panel never loaded
layer values on desktop (selection state, modes, sliders all read
defaults) and theme/override writes failed. All four call sites now use
hubBaseUrl() like their sibling requests; browsers are unaffected.
… None tile

With explicit background modes, the None tile was a second path to the
same visual result as Solid (a leftover from the wallpaper-or-nothing
model). The grid now only selects which image to use; removing the
wallpaper is expressed by switching the mode to Solid. Deleting the
currently-selected wallpaper file still clears the setting internally.
Add per-boot random asset token (crypto.randomBytes) distributed via
authenticated GET /api/assets/token. Desktop webview embeds /public/*
resources (fonts, wallpapers, sounds) cross-origin only when request
carries valid ?asset_token= URL parameter (constant-time verify).

Rationale: prevent drive-by fingerprinting via localhost:4100 — an
unconditional CORP: cross-origin header would let arbitrary websites
embed user's local assets. Token proof is in the URL (webview sends no
Origin header), authenticated GET ensures only paired clients get the
token, and constant-time compare protects against timing attacks.

All /public URL builders (useWallpaper, @font-face, useBellSound,
WallpaperCategory) updated to thread the token. GET /api/fonts and
GET /api/wallpapers now require auth + return signed URLs.

Docs: PROTOCOL.md and decisions.md updated to signed-asset model.
Hub and desktop build jobs only ran on release tags, so cross-platform
build breakage (Windows hub SEA, Tauri bundles) could only surface at
release time. A workflow_dispatch run on any branch now exercises them
for pre-merge validation; artifact retention stays at 1 day.
GET /public/{fonts,sounds,wallpapers} now returns 403
ASSET_TOKEN_REQUIRED unless the URL carries a valid ?asset_token=.
Previously the token only steered the Cross-Origin-Resource-Policy
header, so any local process or DNS-rebinding page could read user
assets by guessing filenames. Every legitimate consumer already signs
its URLs, so enforcement costs nothing for clients.
Wallpaper and thumbnail URLs read the asset token from a plain module
variable, so a URL built before the token was set (pairing path: invalid
token at boot, then pair, then token) never rebuilt — the wallpaper
stayed blocked (403 / cross-origin) until a manual profile change.
Auto-auth hid this because the token was set before the first render.

Each reactive consumer now reads assetTokenReady so Vue re-runs the URL
build once the token lands; fonts and bell sounds are imperative calls
already ordered after initAssetToken.
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.

Desktop window translucency (Tauri transparent) + image/solid/transparent background modes

1 participant