fix(window): refine cross-platform window frames and eliminate Windows shadow bleed

- Set shadow: false on Windows main and Properties window builders to prevent rectangular DWM shadow bleed behind rounded corners.
- Refine active and inactive window frame tokens across themes for subtle, non-glaring boundaries.
- Apply a 0.5px hairline window border rule specifically on macOS to match native Retina AppKit window strokes.
- Synchronize document platform dataset attribute on app boot across main and Properties windows.
- Update and extend window configuration and platform synchronization test suites.
This commit is contained in:
NimBold
2026-09-05 20:02:31 +03:30
parent bbe5445933
commit 1ddfaec338
8 changed files with 96 additions and 30 deletions
+7 -5
View File
@@ -455,12 +455,14 @@ pub async fn open_download_properties_window(
// A hidden WebView2 must not request focus during construction. The
// native reveal path focuses it after the window is visible.
.focused(false);
// Native elevation follows the window shape on macOS. On Windows, Tauri
// gives an undecorated window the system contour and Windows 11 corners as
// well as its shadow. Linux does not implement this API, so its guaranteed
// boundary remains the renderer's theme-aware contour and non-transparent frame.
#[cfg(any(target_os = "windows", target_os = "macos"))]
// Native elevation follows the window shape on macOS. On Windows, Tao enables
// an undecorated shadow that leaves an opaque native frame outside the rounded
// renderer surface at the corners, so shadow is disabled. Linux does not implement
// this API, so its boundary remains the renderer's theme-aware contour.
#[cfg(target_os = "macos")]
let builder = builder.transparent(true).shadow(true);
#[cfg(target_os = "windows")]
let builder = builder.transparent(true).shadow(false);
#[cfg(target_os = "linux")]
let builder = builder.transparent(false).shadow(false);
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
+1 -1
View File
@@ -10,7 +10,7 @@
"minHeight": 640,
"transparent": true,
"decorations": false,
"shadow": true
"shadow": false
}
]
},
+1 -1
View File
@@ -1222,7 +1222,7 @@ function App() {
}, [autoAddClipboardLinks, coreReady, showKeychainModal]);
return (
<div data-window-active={isWindowActive ? 'true' : 'false'} className={`app-shell flex h-screen w-screen overflow-hidden text-text-primary ${
<div data-window-active={isWindowActive ? 'true' : 'false'} className={`app-shell app-shell--style-${windowControlStyle} flex h-screen w-screen overflow-hidden text-text-primary ${
isSidebarOnRight ? 'app-shell--sidebar-right' : 'app-shell--sidebar-left'
} ${
hasWindowChrome ? 'app-shell--window-chrome' : ''
+14 -10
View File
@@ -27,8 +27,8 @@
/* Keep this token alpha-free because some consumers apply their own /alpha. */
--surface-overlay: 0 0% 100%;
--shadow-color: 220 10% 20% / 0.1;
--window-frame-active: 220 12% 30% / 0.60;
--window-frame-inactive: 220 10% 30% / 0.18;
--window-frame-active: 220 12% 30% / 0.22;
--window-frame-inactive: 220 10% 30% / 0.08;
--sidebar-shell-bg: 0 0% 92%;
--sidebar-panel-bg: 0 0% 96%;
--workspace-bg: 0 0% 98%;
@@ -75,8 +75,8 @@
--properties-header-surface: hsl(var(--bg-modal));
--surface-overlay: 0 0% 100%;
--shadow-color: 220 10% 20% / 0.1;
--window-frame-active: 220 12% 30% / 0.60;
--window-frame-inactive: 220 10% 30% / 0.18;
--window-frame-active: 220 12% 30% / 0.22;
--window-frame-inactive: 220 10% 30% / 0.08;
--sidebar-shell-bg: 0 0% 92%;
--sidebar-panel-bg: 0 0% 96%;
--workspace-bg: 0 0% 98%;
@@ -107,8 +107,8 @@
--bg-input: 0 0% 16%;
--properties-header-surface: hsl(0 0% 10%);
--shadow-color: 0 0% 0% / 0.30;
--window-frame-active: 0 0% 100% / 0.35;
--window-frame-inactive: 0 0% 100% / 0.14;
--window-frame-active: 0 0% 100% / 0.14;
--window-frame-inactive: 0 0% 100% / 0.06;
--status-completed: 136 62% 48%;
--status-paused: 0 0% 56%;
--status-downloading: 211 100% 56%;
@@ -156,8 +156,8 @@
--bg-input: 231 15% 20%;
--properties-header-surface: hsl(231 15% 17%);
--shadow-color: 231 20% 8% / 0.35;
--window-frame-active: 228 14% 84% / 0.45;
--window-frame-inactive: 228 14% 84% / 0.18;
--window-frame-active: 228 14% 84% / 0.18;
--window-frame-inactive: 228 14% 84% / 0.08;
--status-completed: 135 94% 65%;
--status-paused: 65 92% 76%;
--status-downloading: 191 97% 77%;
@@ -205,8 +205,8 @@
--bg-input: 220 16% 24%;
--properties-header-surface: hsl(220 16% 19%);
--shadow-color: 220 25% 10% / 0.34;
--window-frame-active: 218 27% 88% / 0.45;
--window-frame-inactive: 218 27% 88% / 0.18;
--window-frame-active: 218 27% 88% / 0.18;
--window-frame-inactive: 218 27% 88% / 0.08;
--status-completed: 92 28% 65%;
--status-paused: 40 71% 73%;
--status-downloading: 193 43% 67%;
@@ -2369,6 +2369,10 @@ html[data-list-density="relaxed"] {
box-shadow: none;
}
html[data-platform="macos"] :is(.app-shell, .properties-window-shell) {
border-width: 0.5px;
}
@media (forced-colors: active) {
.app-shell,
.properties-window-shell {
+1
View File
@@ -14,6 +14,7 @@ import { error as logError, warn as logWarn, initLogger } from "./utils/logger";
import { getCurrentWindow } from '@tauri-apps/api/window';
import { invokeCommand as invoke } from './ipc';
import { useWindowFocusState } from './utils/windowFocus';
import './utils/platform';
const isPropertiesWindow = getCurrentWindow().label.startsWith('properties-');
+30 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { shouldUseCustomWindowControls } from './platform';
import { shouldUseCustomWindowControls, syncPlatformDataset } from './platform';
describe('shouldUseCustomWindowControls', () => {
it('keeps custom controls present while Windows/Linux detection is unresolved', () => {
@@ -20,3 +20,32 @@ describe('shouldUseCustomWindowControls', () => {
expect(shouldUseCustomWindowControls('unknown', 'Mozilla/5.0 (Linux; Android 14; Mobile)')).toBe(false);
});
});
describe('syncPlatformDataset', () => {
it('synchronizes known desktop platforms to document dataset', () => {
const mockDocument = { documentElement: { dataset: {} as Record<string, string | undefined> } };
syncPlatformDataset('macos', mockDocument);
expect(mockDocument.documentElement.dataset.platform).toBe('macos');
syncPlatformDataset('windows', mockDocument);
expect(mockDocument.documentElement.dataset.platform).toBe('windows');
syncPlatformDataset('linux', mockDocument);
expect(mockDocument.documentElement.dataset.platform).toBe('linux');
});
it('ignores unknown or unsupported platforms', () => {
const mockDocument = { documentElement: { dataset: { platform: 'macos' } } };
syncPlatformDataset('unknown', mockDocument);
expect(mockDocument.documentElement.dataset.platform).toBe('macos');
syncPlatformDataset('android', mockDocument);
expect(mockDocument.documentElement.dataset.platform).toBe('macos');
});
it('safely handles missing document in headless environments', () => {
expect(() => syncPlatformDataset('macos', undefined)).not.toThrow();
});
});
+27
View File
@@ -22,12 +22,39 @@ export const shouldUseCustomWindowControls = (os: string, userAgent: string): bo
let cached: PlatformInfo | null = null;
let pending: Promise<PlatformInfo> | null = null;
type TargetDocument = {
documentElement: {
dataset: Record<string, string | undefined>;
};
};
export const syncPlatformDataset = (
os: string,
targetDocument: TargetDocument | undefined = typeof document !== 'undefined' ? document : undefined,
): void => {
if (!targetDocument) return;
if (os === 'macos' || os === 'windows' || os === 'linux') {
targetDocument.documentElement.dataset.platform = os;
}
};
if (typeof document !== 'undefined' && typeof navigator !== 'undefined') {
if (/Macintosh|Mac OS X/i.test(navigator.userAgent)) {
syncPlatformDataset('macos');
} else if (/Windows/i.test(navigator.userAgent)) {
syncPlatformDataset('windows');
} else if (/Linux/i.test(navigator.userAgent)) {
syncPlatformDataset('linux');
}
}
export const getPlatformInfo = (): Promise<PlatformInfo> => {
if (cached) return Promise.resolve(cached);
if (!pending) {
pending = invoke('get_platform_info')
.then(info => {
cached = info;
syncPlatformDataset(info.os);
return info;
})
.finally(() => {
+15 -12
View File
@@ -24,22 +24,25 @@ describe('main window configuration', () => {
}
});
it('uses native elevation where Tauri supports undecorated window shadows', () => {
for (const [platform, config] of [
['macOS', macosConfiguration],
['Windows', windowsConfiguration],
] as const) {
expect(config.app.windows[0], platform).toMatchObject({
transparent: true,
decorations: false,
shadow: true,
});
}
it('uses native elevation on macOS where undecorated window shadows conform to the surface', () => {
expect(macosConfiguration.app.windows[0], 'macOS').toMatchObject({
transparent: true,
decorations: false,
shadow: true,
});
});
it('keeps Windows transparent without native shadow bleed outside rounded corners', () => {
expect(windowsConfiguration.app.windows[0], 'Windows').toMatchObject({
transparent: true,
decorations: false,
shadow: false,
});
});
it('keeps Linux on the renderer contour without claiming native shadow support', () => {
const mainWindow = linuxConfiguration.app.windows[0];
expect(mainWindow).toMatchObject({
expect(mainWindow, 'Linux').toMatchObject({
transparent: false,
decorations: false,
shadow: false,