Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion src-tauri/crates/app-window/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ dispatch2 = { workspace = true }
# the frontend passes when toggling vibrancy off (so we can paint a static
# bitmap behind external content like Stripe Checkout).
base64 = { workspace = true }
# Windows-specific DWM rounded-corner attribute (`set_rounded_corners`).
# Windows-specific DWM rounded corners + acrylic backdrop + OS version
# detection (Win10 vs Win11 chrome policy in `windows_corner.rs`).
[target.'cfg(windows)'.dependencies]
windows = { version = "0.61", features = ["Win32_Foundation", "Win32_Graphics_Dwm"] }
windows-version = "0.1"
window-vibrancy = "0.6"
17 changes: 17 additions & 0 deletions src-tauri/crates/app-window/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,3 +228,20 @@ pub async fn remove_window_background(app: AppHandle) -> Result<(), String> {

Ok(())
}

/// Whether the main window has a translucent native backdrop (Windows 11
/// acrylic). The frontend mirrors this as `<html data-windows-chrome>` so
/// CSS can relax its opaque fail-safe background. Always `false` on
/// Windows 10 (acrylic disabled — drag lag) and non-Windows hosts (macOS
/// vibrancy uses its own `data-host-desktop="macos"` CSS path).
#[tauri::command]
pub fn main_window_chrome_is_acrylic() -> bool {
#[cfg(windows)]
{
super::windows_corner::current_policy().acrylic
}
#[cfg(not(windows))]
{
false
}
}
25 changes: 22 additions & 3 deletions src-tauri/crates/app-window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,16 +221,30 @@ fn is_main_thread() -> bool {
}
}

/// Host-native window chrome so the OS frame matches frontend corner radii.
/// Host-native chrome for the frameless, transparent main window.
///
/// - **Windows 11+:** `DWMWCP_ROUND` via DWM (pairs with `--border-radius-window` in the web layer).
/// - **Windows 11+:** DWM rounded corners + translucent acrylic backdrop.
/// - **Windows 10:** opaque background, no acrylic (drag lag), no DWM shadow
/// (renders as a 1px border artifact on transparent frameless windows).
/// - **macOS:** Applied separately through [`apply_macos_window_material`].
/// - **Linux / others:** No-op.
pub fn apply_host_desktop_window_chrome(
#[cfg_attr(not(windows), allow(unused_variables))] window: &tauri::WebviewWindow,
) {
#[cfg(windows)]
windows_corner::apply_dwm_rounded_corner_preference(window);
windows_corner::apply_frameless_window_chrome(window);
}

/// Rounded corners only, for decorated secondary windows (e.g. browser).
/// Decorated windows keep their native frame, shadow, and opaque backdrop.
///
/// - **Windows 11+:** `DWMWCP_ROUND` via DWM.
/// - **Windows 10 / macOS / Linux:** No-op.
pub fn apply_host_desktop_decorated_window_corners(
#[cfg_attr(not(windows), allow(unused_variables))] window: &tauri::WebviewWindow,
) {
#[cfg(windows)]
windows_corner::apply_rounded_corners(window);
}

/// Apply the native macOS AbuttedSidebar material underneath the transparent webview.
Expand Down Expand Up @@ -307,6 +321,11 @@ pub fn recreate_main_window(app: &AppHandle) -> Result<(), String> {

apply_host_desktop_window_chrome(&window);

// The main window starts hidden (visible:false in the platform config)
// so chrome can be applied before first paint; show it now that the
// opaque background + shadow policy are in place.
let _ = window.show();

let _ = window.set_focus();

println!("✅ [Window] Main window recreated");
Expand Down
32 changes: 31 additions & 1 deletion src-tauri/crates/app-window/src/tests/windows_corner_tests.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
//! Sanity checks for DWM corner preference enum values (Win32 docs).
//! Windows chrome policy tests + DWM corner preference enum sanity checks.

use windows::Win32::Graphics::Dwm::{DWMWCP_DONOTROUND, DWMWCP_ROUND, DWMWCP_ROUNDSMALL};

use super::policy_for_build;

#[test]
fn dwm_corner_preference_enum_matches_win32_docs() {
assert_eq!(DWMWCP_ROUND.0, 2);
assert_eq!(DWMWCP_ROUNDSMALL.0, 3);
assert_eq!(DWMWCP_DONOTROUND.0, 1);
}

#[test]
fn win10_builds_get_conservative_chrome() {
// 19045 = Win10 22H2 final build.
let policy = policy_for_build(19045);
assert!(!policy.acrylic);
assert!(!policy.rounded_corners);
assert!(!policy.shadow);
}

#[test]
fn win11_builds_get_full_chrome() {
// 22000 = first Win11 build; 26100 = Win11 24H2.
for build in [22000, 26100] {
let policy = policy_for_build(build);
assert!(policy.acrylic);
assert!(policy.rounded_corners);
assert!(policy.shadow);
}
}

#[test]
fn failed_version_lookup_falls_back_to_win10_chrome() {
let policy = policy_for_build(0);
assert!(!policy.acrylic);
assert!(!policy.rounded_corners);
assert!(!policy.shadow);
}
89 changes: 85 additions & 4 deletions src-tauri/crates/app-window/src/windows_corner.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
//! Windows 11+ DWM window corner preference for decorated windows.
//! Windows native chrome: version-aware acrylic, corner, and shadow policy.
//!
//! Sets `DWMWCP_ROUND` (8 px at 100 % DPI) so the native frame matches the
//! frontend's `--border-radius-window: 8px` on Windows (see `windowChromeRadius.ts`).
//! Win10's DWM cannot round frameless windows, draws the window shadow as a
//! visible 1px border around transparent windows, and recomposits acrylic on
//! every frame while dragging (visible lag). Win11 (build 22000+) supports
//! all three natively. The policy is decided once from the OS build number
//! and applied from Rust only — the frontend reads the resulting policy via
//! the `main_window_chrome_is_acrylic` command and mirrors it as
//! `<html data-windows-chrome="acrylic">` so CSS can relax its opaque
//! fail-safe background (see `src/index.scss`).

use std::ffi::c_void;

Expand All @@ -11,7 +17,82 @@ use windows::Win32::Graphics::Dwm::{
DwmSetWindowAttribute, DWMWA_WINDOW_CORNER_PREFERENCE, DWMWCP_ROUND,
};

pub(super) fn apply_dwm_rounded_corner_preference(window: &WebviewWindow) {
/// Native chrome capabilities for a given Windows build.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) struct WindowsChromePolicy {
/// Translucent acrylic backdrop behind the frameless main window.
pub acrylic: bool,
/// DWM rounded corners (`DWMWCP_ROUND`, 8 px at 100 % DPI — pairs with
/// the frontend's `--border-radius-window`, see `windowChromeRadius.ts`).
pub rounded_corners: bool,
/// DWM window shadow. On Win10 it renders as a 1px border artifact
/// around transparent frameless windows, so it is disabled there.
pub shadow: bool,
}

const WINDOWS_11_FIRST_BUILD: u32 = 22000;

/// Pure build-number → policy mapping. Build 0 (version lookup failed)
/// deliberately falls into the conservative Win10 branch.
const fn policy_for_build(build: u32) -> WindowsChromePolicy {
let win11 = build >= WINDOWS_11_FIRST_BUILD;
WindowsChromePolicy {
acrylic: win11,
rounded_corners: win11,
shadow: win11,
}
}

/// Policy for the Windows version this process is running on.
pub(super) fn current_policy() -> WindowsChromePolicy {
policy_for_build(windows_version::OsVersion::current().build)
}

/// Rounded corners only — for decorated secondary windows (e.g. browser).
/// Decorated windows keep their native frame, shadow, and opaque backdrop,
/// so the acrylic/shadow parts of the policy do not apply to them.
pub(super) fn apply_rounded_corners(window: &WebviewWindow) {
if current_policy().rounded_corners {
set_dwm_rounded_corners(window);
}
}

/// Full chrome for the frameless, transparent main window.
///
/// **Win11+:** DWM rounded corners + translucent acrylic backdrop.
/// **Win10:** no acrylic (drag lag), no DWM shadow (1px border artifact),
/// opaque native background so `transparent: true` does not punch a hole
/// through to the desktop.
pub(super) fn apply_frameless_window_chrome(window: &WebviewWindow) {
let policy = current_policy();

if policy.rounded_corners {
set_dwm_rounded_corners(window);
}

if policy.acrylic {
// Semi-transparent dark tint over the system backdrop; an alpha of
// 255 would make the acrylic fully opaque and thus invisible.
if let Err(err) = window_vibrancy::apply_acrylic(window, Some((13, 13, 13, 125))) {
warn!(
target: "app_lib::window",
"apply_acrylic failed (non-fatal, continuing without acrylic): {}",
err
);
}
} else {
// Clear any acrylic left over from a previous run/config layer, and
// paint an opaque native background as the pre-CSS fallback.
let _ = window_vibrancy::clear_acrylic(window);
let _ = window.set_background_color(Some(tauri::window::Color(13, 13, 13, 255)));
}

if !policy.shadow {
let _ = window.set_shadow(false);
}
}

fn set_dwm_rounded_corners(window: &WebviewWindow) {
let hwnd = match window.hwnd() {
Ok(handle) => handle,
Err(err) => {
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/crates/browser/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub async fn open_browser_window(
set_traffic_light_position(&window, TRAFFIC_LIGHT_X, TRAFFIC_LIGHT_Y);
}

app_window::apply_host_desktop_window_chrome(&window);
app_window::apply_host_desktop_decorated_window_corners(&window);

#[cfg(all(not(target_os = "macos"), not(windows)))]
let _ = window;
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/commands/handler_list.inc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ infrastructure::dev_bundled_auth::debug_import_bundled_org2_cloud_auth,
app_window::commands::set_window_vibrancy,
app_window::commands::set_main_webview_zoom,
app_window::commands::remove_window_background,
app_window::commands::main_window_chrome_is_acrylic,
// Optional sidecar commands - lazy install after first paint
crate::setup::sidecar_setup::sidecar_list_status,
crate::setup::sidecar_setup::sidecar_install,
Expand Down
44 changes: 38 additions & 6 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,12 +439,10 @@ pub fn run() {

{
use tauri::Manager;

if let Some(main_window) = app.handle().get_webview_window("main") {
#[cfg(not(target_os = "macos"))]
{
let _ = main_window.show();
let _ = main_window.set_focus();
}
// Apply chrome while the window is still hidden.
app_window::apply_host_desktop_window_chrome(&main_window);

#[cfg(target_os = "macos")]
{
Expand All @@ -455,9 +453,43 @@ pub fn run() {
app_window::TRAFFIC_LIGHT_Y,
);
app_window::apply_macos_window_material(&main_window);
let _ = main_window.show();
let _ = main_window.set_focus();
}
}

app_window::apply_host_desktop_window_chrome(&main_window);
// On Windows the main window starts hidden (visible:false in the
// platform config). With transparent:true, set_background_color
// is a visual no-op — WebView2 composites directly over the
// transparent surface, so showing the window before the webview
// has painted exposes DWM/WebView2 edge artifacts (thin black
// lines around the border on Win10). We defer show() until the
// frontend emits "orgii:main-window-ready", which fires once
// the splash HTML has loaded and painted.
#[cfg(not(target_os = "macos"))]
{
let show_handle = app.handle().clone();
app.handle().listen(
"orgii:main-window-ready",
move |_| {
if let Some(w) = show_handle.get_webview_window("main") {
let _ = w.show();
let _ = w.set_focus();
}
},
);

// Safety fallback: if the frontend event never arrives
// (bundle crash, IPC failure), show after 3 s so the user
// is never stranded on a hidden window.
let timeout_handle = app.handle().clone();
tauri::async_runtime::spawn(async move {
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
if let Some(w) = timeout_handle.get_webview_window("main") {
let _ = w.show();
let _ = w.set_focus();
}
});
}
}

Expand Down
4 changes: 1 addition & 3 deletions src-tauri/tauri.windows.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
"hiddenTitle": true,
"backgroundColor": "#00000000",
"transparent": true,
"windowEffects": {
"effects": ["acrylic"]
}
"visible": false
}
]
},
Expand Down
26 changes: 26 additions & 0 deletions src/config/windowChromeRadius.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,29 @@ export function applyHostDesktopWindowChromeRadius(): void {
document.body.style.setProperty("--radius-page", pageVal);
}
}

/**
* Mirrors the Rust-side Windows chrome policy as
* `<html data-windows-chrome="acrylic">` so index.scss can relax the opaque
* Windows fail-safe background when a translucent native backdrop (Win11
* acrylic) actually exists behind the webview. Until this resolves — and on
* Windows 10, where acrylic is disabled — the attribute stays absent and the
* opaque fallback applies. Requires initializeTauriAPIs() to have completed.
*/
export async function applyWindowsNativeChromeAttribute(): Promise<void> {
if (typeof document === "undefined") {
return;
}
if (resolveHostDesktop() !== HOST_DESKTOP.WINDOWS) {
return;
}
try {
const { invokeTauri } = await import("@src/util/platform/tauri/init");
const acrylic = await invokeTauri<boolean>("main_window_chrome_is_acrylic");
if (acrylic) {
document.documentElement.dataset.windowsChrome = "acrylic";
}
} catch {
// Keep the opaque fail-safe background if the policy can't be read.
}
}
25 changes: 24 additions & 1 deletion src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -536,12 +536,35 @@ html[data-host-desktop="macos"] .sidebar-base {
background: transparent;
}

// Windows fail-safe: opaque until the Rust chrome policy confirms a
// translucent native backdrop exists. Win10 never gets one (acrylic causes
// drag lag; DWM shadow draws a 1px border on transparent frameless windows),
// and even on Win11 the window must not be see-through before the policy
// query resolves. See applyWindowsNativeChromeAttribute() in
// windowChromeRadius.ts.
html[data-host-desktop="windows"] body,
html[data-host-desktop="windows"] #root {
background: transparent;
background: var(--color-bg-2);
}

html[data-host-desktop="windows"] .sidebar-base {
background: var(--color-bg-2);
}

// Win11 acrylic confirmed: let the native backdrop show through, tinted per
// theme (same opacity variable the top bar and sidebar chrome use). Must
// include <html> itself — the global rule above paints it opaque.
html[data-windows-chrome="acrylic"],
html[data-windows-chrome="acrylic"] body,
html[data-windows-chrome="acrylic"] #root {
background: color-mix(
in srgb,
var(--color-bg-2) var(--windows-native-chrome-opacity, 30%),
transparent
);
}

html[data-windows-chrome="acrylic"] .sidebar-base {
background: transparent;
}

Expand Down
Loading
Loading