fix blur on macos

This commit is contained in:
Boof2015
2026-07-10 03:24:43 -04:00
parent 0ee95fe480
commit 4a9b325532
3 changed files with 43 additions and 5 deletions
+25 -4
View File
@@ -23,7 +23,10 @@ import { RESIZE_DIRECTIONS, type ResizeDirection } from '../types/windowResize'
import type { DialogOptions, DialogResult } from '../types/dialog'
import { normalizeProfile } from '../shared/profileState'
import { resolveNativeThemeSource } from '../shared/themeState'
import { resolveWindowCapabilities } from '../shared/windowCapabilities'
import {
resolveMacWindowBlurMaterial,
resolveWindowCapabilities,
} from '../shared/windowCapabilities'
import {
clampDraggedMainWindowBounds,
clampRestoredWindowBounds,
@@ -495,6 +498,7 @@ function applyNativeThemeSnapshot(snapshot: ThemeLibrarySnapshot): void {
? snapshot.themes[snapshot.activeThemeId] ?? null
: null
nativeTheme.themeSource = resolveNativeThemeSource(activeTheme)
refreshMacBlurVibrancy()
}
async function syncNativeThemeAppearance(): Promise<void> {
@@ -841,8 +845,9 @@ function getWindowBackgroundSnapshot(): WindowBackgroundSnapshot {
// intact. On Windows, blurred and clear windows must be created `transparent`
// (blurred composites accent-policy acrylic behind the alpha pixels), which is
// mutually exclusive with the thick frame — those windows fall back to the JS
// move/resize controllers. On macOS, blurred uses vibrancy and keeps the
// native frame semantics.
// move/resize controllers. On macOS, blurred uses theme-aware native vibrancy
// (HUD for dark themes, Content for light themes) and keeps the native frame
// semantics.
function getFramelessWindowOptions(
background: WindowBackgroundState = SOLID_WINDOW_BACKGROUND,
): BrowserWindowConstructorOptions {
@@ -861,7 +866,7 @@ function getFramelessWindowOptions(
: {}),
...(process.platform === 'darwin' && background.mode === 'blurred'
? {
vibrancy: 'under-window' as const,
vibrancy: resolveMacWindowBlurMaterial(nativeTheme.shouldUseDarkColors),
visualEffectState: 'active' as const,
}
: {}),
@@ -886,6 +891,22 @@ function getBackgroundCapableWindows(): BrowserWindow[] {
return windows
}
function refreshMacBlurVibrancy(): void {
if (process.platform !== 'darwin' || getEffectiveWindowBackground().mode !== 'blurred') {
return
}
const material = resolveMacWindowBlurMaterial(nativeTheme.shouldUseDarkColors)
for (const window of getBackgroundCapableWindows()) {
try {
window.setVibrancy(material)
} catch {
// A material refresh is cosmetic; keep the current backdrop if AppKit
// rejects a live update during a window transition.
}
}
}
function broadcastWindowBackgroundChanged(snapshot: WindowBackgroundSnapshot): void {
for (const window of getBackgroundCapableWindows()) {
if (!window.webContents.isDestroyed()) {
+6
View File
@@ -1,5 +1,11 @@
import type { WindowCapabilities, WindowDisplayServer } from '../types/windowCapabilities'
export type MacWindowBlurMaterial = 'hud' | 'content'
export function resolveMacWindowBlurMaterial(useDarkColors: boolean): MacWindowBlurMaterial {
return useDarkColors ? 'hud' : 'content'
}
interface WindowCapabilityResolutionOptions {
platform: string
argv?: readonly string[]
+12 -1
View File
@@ -28,7 +28,10 @@ import {
import {
createDefaultProfile,
} from '../src/shared/profileState'
import { resolveWindowCapabilities } from '../src/shared/windowCapabilities'
import {
resolveMacWindowBlurMaterial,
resolveWindowCapabilities,
} from '../src/shared/windowCapabilities'
import {
clampDraggedMainWindowBounds,
clampRestoredWindowBounds,
@@ -3225,6 +3228,11 @@ test('resolveWindowCapabilities gates blurred backgrounds on the Windows 11 22H2
assert.equal(resolveForBuild('not-a-version'), false)
})
test('resolveMacWindowBlurMaterial selects neutral theme-aware macOS materials', () => {
assert.equal(resolveMacWindowBlurMaterial(true), 'hud')
assert.equal(resolveMacWindowBlurMaterial(false), 'content')
})
test('Wayland window controls use native drag regions and omit unsupported reposition/geometry paths', async () => {
const toolbarSource = await readFile(join(process.cwd(), 'src', 'renderer', 'components', 'Toolbar.tsx'), 'utf8')
const appSource = await readFile(join(process.cwd(), 'src', 'renderer', 'App.tsx'), 'utf8')
@@ -3249,6 +3257,9 @@ test('main and detached windows keep frameless Prism chrome while enabling snap-
const nowPlayingSource = await readFile(join(process.cwd(), 'src', 'renderer', 'components', 'NowPlayingConfigWindow.tsx'), 'utf8')
assert.match(mainSource, /function getFramelessWindowOptions\([\s\S]*?return \{[\s\S]*?frame: false,[\s\S]*?transparent: background\.mode === 'clear' \|\| transparentOnWindows,[\s\S]*?roundedCorners: false,[\s\S]*?hasShadow: false,[\s\S]*?thickFrame: background\.mode === 'solid',[\s\S]*?backgroundMaterial: 'none'[\s\S]*?resizable: true,[\s\S]*?maximizable: true,[\s\S]*?fullscreenable: true,[\s\S]*?skipTaskbar: false,[\s\S]*?\}/)
assert.match(mainSource, /process\.platform === 'darwin' && background\.mode === 'blurred'[\s\S]*?vibrancy: resolveMacWindowBlurMaterial\(nativeTheme\.shouldUseDarkColors\),[\s\S]*?visualEffectState: 'active'/)
assert.match(mainSource, /function applyNativeThemeSnapshot\([\s\S]*?nativeTheme\.themeSource = resolveNativeThemeSource\(activeTheme\)[\s\S]*?refreshMacBlurVibrancy\(\)/)
assert.match(mainSource, /function refreshMacBlurVibrancy\(\): void \{[\s\S]*?process\.platform !== 'darwin'[\s\S]*?getEffectiveWindowBackground\(\)\.mode !== 'blurred'[\s\S]*?resolveMacWindowBlurMaterial\(nativeTheme\.shouldUseDarkColors\)[\s\S]*?getBackgroundCapableWindows\(\)[\s\S]*?window\.setVibrancy\(material\)/)
assert.match(mainSource, /function createMainWindow\(restoreBounds\?: WindowBounds\): void \{[\s\S]*?\.\.\.getFramelessWindowOptions\(background\),/)
assert.match(mainSource, /function createScopePopoutWindow\(kind: ScopeKind, rawBounds\?: WindowBounds\): BrowserWindow \| null \{[\s\S]*?\.\.\.getFramelessWindowOptions\(background\),/)
assert.match(mainSource, /function createNowPlayingConfigWindow\(\): BrowserWindow \{[\s\S]*?\.\.\.getFramelessWindowOptions\(\),/)