diff --git a/src-tauri/src/properties_window.rs b/src-tauri/src/properties_window.rs index 38bc44e..0b44a37 100644 --- a/src-tauri/src/properties_window.rs +++ b/src-tauri/src/properties_window.rs @@ -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"))] diff --git a/src-tauri/tauri.windows.conf.json b/src-tauri/tauri.windows.conf.json index ee28d6f..ccfdc66 100644 --- a/src-tauri/tauri.windows.conf.json +++ b/src-tauri/tauri.windows.conf.json @@ -10,7 +10,7 @@ "minHeight": 640, "transparent": true, "decorations": false, - "shadow": true + "shadow": false } ] }, diff --git a/src/App.tsx b/src/App.tsx index 47850e8..edcf200 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1222,7 +1222,7 @@ function App() { }, [autoAddClipboardLinks, coreReady, showKeychainModal]); return ( -
{ 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 } }; + + 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(); + }); +}); diff --git a/src/utils/platform.ts b/src/utils/platform.ts index d073515..026a2bf 100644 --- a/src/utils/platform.ts +++ b/src/utils/platform.ts @@ -22,12 +22,39 @@ export const shouldUseCustomWindowControls = (os: string, userAgent: string): bo let cached: PlatformInfo | null = null; let pending: Promise | null = null; +type TargetDocument = { + documentElement: { + dataset: Record; + }; +}; + +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 => { if (cached) return Promise.resolve(cached); if (!pending) { pending = invoke('get_platform_info') .then(info => { cached = info; + syncPlatformDataset(info.os); return info; }) .finally(() => { diff --git a/src/utils/windowConfiguration.test.ts b/src/utils/windowConfiguration.test.ts index 9ba36c9..7e850ad 100644 --- a/src/utils/windowConfiguration.test.ts +++ b/src/utils/windowConfiguration.test.ts @@ -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,