From b6aa570fcc255935c52ce39459bb32976729b52a Mon Sep 17 00:00:00 2001 From: t Date: Wed, 16 Sep 2026 17:09:48 +0800 Subject: [PATCH] fix(ui): remove glass chrome appearance --- apps/desktop/src/main/rpc.ts | 4 +- apps/desktop/src/main/window.test.ts | 39 ++---- apps/desktop/src/main/window.ts | 50 +------ apps/desktop/src/renderer/src/App.tsx | 10 +- .../src/features/changes/ChangesView.tsx | 6 +- .../src/features/composer/Composer.tsx | 6 +- .../renderer/src/features/composer/DESIGN.md | 6 +- .../src/features/composer/FilePopup.tsx | 2 +- .../src/features/composer/ModelSelector.tsx | 2 +- .../src/features/diffs/DiffViewer.tsx | 2 +- .../renderer/src/features/focus/FocusPill.tsx | 2 +- .../src/features/github/HubWorkspace.tsx | 10 +- .../src/features/palette/CommandPalette.tsx | 2 +- .../providers/AgentConfigSettings.tsx | 2 +- .../features/search/ContentSearchOverlay.tsx | 2 +- .../features/session/ChildSessionActivity.tsx | 2 +- .../src/features/session/SessionView.test.tsx | 1 - .../src/features/session/SessionView.tsx | 4 +- .../settings/AdvancedSettings.test.tsx | 1 - .../settings/AppearanceSettings.test.tsx | 24 +--- .../features/settings/AppearanceSettings.tsx | 29 +--- .../settings/NotificationsSettings.test.tsx | 1 - .../settings/PermissionsSettings.test.tsx | 1 - .../features/settings/PermissionsSettings.tsx | 2 +- .../src/features/settings/SettingsSearch.tsx | 2 +- .../settings/SettingsWorkspace.test.tsx | 1 - .../features/settings/SettingsWorkspace.tsx | 6 +- .../settings/useEngineSettings.test.ts | 8 +- .../renderer/src/features/split/drag-split.ts | 2 +- .../src/features/terminal/TerminalDock.tsx | 2 +- .../src/features/transcript/MessageRail.tsx | 2 +- .../src/features/welcome/WelcomePanel.tsx | 2 +- .../src/renderer/src/lib/theme-persistence.ts | 8 +- .../src/renderer/src/shell/ContextMenu.tsx | 2 +- .../src/renderer/src/shell/Sidebar.tsx | 22 +-- .../src/renderer/src/shell/Titlebar.tsx | 6 +- .../desktop/src/renderer/src/styles/index.css | 4 +- packages/contracts/src/contracts.test.ts | 10 +- packages/contracts/src/settings.ts | 4 - packages/engine/src/settings.test.ts | 8 +- packages/ui/package.json | 2 +- packages/ui/src/color.ts | 2 +- packages/ui/src/components/Dialog.tsx | 2 +- packages/ui/src/components/Popover.tsx | 2 +- packages/ui/src/glass.css | 131 ------------------ packages/ui/src/surface-effects.css | 62 +++++++++ packages/ui/src/theme-provider.test.tsx | 65 +++------ packages/ui/src/theme-provider.tsx | 42 ++---- packages/ui/src/themes.test.ts | 2 - packages/ui/src/themes.ts | 51 +------ packages/ui/src/tokens.css | 40 +----- packages/ui/src/wallpaper-css.test.ts | 14 +- packages/ui/src/wallpaper.css | 43 ++---- 53 files changed, 226 insertions(+), 531 deletions(-) delete mode 100644 packages/ui/src/glass.css create mode 100644 packages/ui/src/surface-effects.css diff --git a/apps/desktop/src/main/rpc.ts b/apps/desktop/src/main/rpc.ts index 5c888103..d40685ac 100644 --- a/apps/desktop/src/main/rpc.ts +++ b/apps/desktop/src/main/rpc.ts @@ -1167,9 +1167,7 @@ export function registerRpc(contents: WebContents, options: RegisterRpcOptions = return { done: true } }) - // Live theme change: repaint native chrome (Windows overlay symbols + the OS - // light/dark hint). Window material is fixed at creation, so a glass <-> opaque - // switch only takes full effect on the next launch. + // Live theme change: repaint Windows overlay symbols and the OS light/dark hint. r.register('theme.apply', (params) => { const win = BrowserWindow.fromWebContents(contents) if (!win) return { applied: false } diff --git a/apps/desktop/src/main/window.test.ts b/apps/desktop/src/main/window.test.ts index e905c254..3e8d2ee1 100644 --- a/apps/desktop/src/main/window.test.ts +++ b/apps/desktop/src/main/window.test.ts @@ -18,30 +18,15 @@ describe('theme registries agree across packages', () => { }) describe('themeWindowChrome', () => { - it('requests platform translucency for glass themes', () => { - expect(themeWindowChrome(themes.obsidian, 'win32')).toMatchObject({ - backgroundColor: '#00000000', - backgroundMaterial: 'acrylic', - }) - expect(themeWindowChrome(themes.obsidian, 'darwin')).toMatchObject({ - vibrancy: 'under-window', - visualEffectState: 'active', - }) - expect(themeWindowChrome(themes.obsidian, 'linux')).toMatchObject({ transparent: true }) - }) - - it('gives opaque themes a solid background and no translucency', () => { - for (const platform of ['win32', 'darwin', 'linux'] as const) { - const chrome = themeWindowChrome(themes.graphite, platform) - expect(chrome.backgroundMaterial).toBeUndefined() - expect(chrome.vibrancy).toBeUndefined() - expect(chrome.transparent).toBeUndefined() + it('gives every theme a solid background', () => { + for (const theme of Object.values(themes)) { + const chrome = themeWindowChrome(theme) expect(chrome.backgroundColor).toMatch(/^#[0-9a-f]{6}$/) } }) it('never flashes black for light themes', () => { - const chrome = themeWindowChrome(themes.porcelain, 'win32') + const chrome = themeWindowChrome(themes.porcelain) const red = parseInt(chrome.backgroundColor.slice(1, 3), 16) expect(red).toBeGreaterThan(0xd0) // Overlay symbols must be dark on a light backdrop. @@ -49,17 +34,15 @@ describe('themeWindowChrome', () => { }) it('derives the overlay symbol color from the theme foreground', () => { - expect(themeWindowChrome(themes.obsidian, 'win32').symbolColor).toMatch(/^#[0-9a-f]{6}$/) - expect(themeWindowChrome(themes.obsidian, 'win32').symbolColor).not.toBe( - themeWindowChrome(themes.porcelain, 'win32').symbolColor, + expect(themeWindowChrome(themes.obsidian).symbolColor).toMatch(/^#[0-9a-f]{6}$/) + expect(themeWindowChrome(themes.obsidian).symbolColor).not.toBe( + themeWindowChrome(themes.porcelain).symbolColor, ) }) - it('differs between a glass theme and an opaque theme', () => { - const glass = themeWindowChrome(themes.nocturne, 'win32') - const opaque = themeWindowChrome(themes.verdant, 'win32') - expect(glass.backgroundColor).not.toBe(opaque.backgroundColor) - expect(glass.backgroundMaterial).toBe('acrylic') - expect(opaque.backgroundMaterial).toBeUndefined() + it('uses each theme background rather than a shared transparent value', () => { + expect(themeWindowChrome(themes.nocturne).backgroundColor).not.toBe( + themeWindowChrome(themes.verdant).backgroundColor, + ) }) }) diff --git a/apps/desktop/src/main/window.ts b/apps/desktop/src/main/window.ts index a7bf4f17..bac945f4 100644 --- a/apps/desktop/src/main/window.ts +++ b/apps/desktop/src/main/window.ts @@ -7,15 +7,9 @@ import { appDisplayName } from './dev-instance' import { getSettingsStore } from './store' /** - * Platform chrome strategy (PLAN §8): - * - Windows: hidden frame + native titleBarOverlay (snap/max/min preserved), - * backgroundMaterial 'acrylic' when the active theme opts into glass - * - macOS: hiddenInset traffic lights + vibrancy 'under-window' (glass only) - * - Linux: hidden frame; custom controls ship in the renderer titlebar; - * transparent window for glass themes (compositor blur not guaranteed) - * - * Opaque themes get a solid `backgroundColor` from their own `bg` token, so a - * light theme never flashes black before the renderer paints. + * The native window always uses a solid `backgroundColor` from the active + * theme's `bg` token, so no desktop content bleeds through and a light theme + * never flashes black before the renderer paints. * * Window bounds persist across launches via the settings store. */ @@ -31,32 +25,12 @@ export const PACKAGED_CONTENT_SECURITY_POLICY = export interface ThemeWindowChrome { backgroundColor: string symbolColor: string - transparent?: true - backgroundMaterial?: 'acrylic' - vibrancy?: 'under-window' - visualEffectState?: 'active' } -/** - * Native window options derived from a theme. Glass themes request the - * platform's translucency and a fully transparent backdrop; opaque themes get - * the theme's own background so the first frame matches the UI. - */ -export function themeWindowChrome( - theme: Theme, - platform: NodeJS.Platform = process.platform, -): ThemeWindowChrome { +/** Native window colors derived from a theme. */ +export function themeWindowChrome(theme: Theme): ThemeWindowChrome { const symbolColor = oklchToHex(theme.colors.fg) ?? FALLBACK_SYMBOL - if (!theme.glass) { - return { backgroundColor: oklchToHex(theme.colors.bg) ?? FALLBACK_BG, symbolColor } - } - const translucent = - platform === 'win32' - ? { backgroundMaterial: 'acrylic' as const } - : platform === 'darwin' - ? { vibrancy: 'under-window' as const, visualEffectState: 'active' as const } - : { transparent: true as const } - return { backgroundColor: '#00000000', symbolColor, ...translucent } + return { backgroundColor: oklchToHex(theme.colors.bg) ?? FALLBACK_BG, symbolColor } } /** The theme the window should paint, per persisted settings. */ @@ -66,8 +40,7 @@ export function persistedTheme(): Theme { /** * Repaints native chrome for a live theme change: the Windows overlay symbol - * color and the OS-level light/dark hint. Window material cannot change after - * creation, so glass transitions land on the next launch. + * color and the OS-level light/dark hint. */ export function applyThemeToWindow(win: BrowserWindow, theme: Theme): void { const chrome = themeWindowChrome(theme) @@ -97,15 +70,6 @@ export function createMainWindow(): BrowserWindow { process.platform === 'win32' ? { color: '#00000000', symbolColor: chrome.symbolColor, height: 38 } : false, - // Glass: the desktop shows through the shell chrome. Windows gets DWM - // acrylic; macOS gets native vibrancy; Linux composites its own blur via - // CSS backdrop-filter inside a transparent window. Opaque themes skip all - // three and render on the solid backgroundColor above. - ...(chrome.backgroundMaterial ? { backgroundMaterial: chrome.backgroundMaterial } : {}), - ...(chrome.vibrancy - ? { vibrancy: chrome.vibrancy, visualEffectState: chrome.visualEffectState } - : {}), - ...(chrome.transparent ? { transparent: true } : {}), webPreferences: { preload: join(import.meta.dirname, '../preload/index.cjs'), sandbox: true, diff --git a/apps/desktop/src/renderer/src/App.tsx b/apps/desktop/src/renderer/src/App.tsx index 3c8385da..800c2030 100644 --- a/apps/desktop/src/renderer/src/App.tsx +++ b/apps/desktop/src/renderer/src/App.tsx @@ -742,7 +742,7 @@ function Shell() { if (settingsOpen || hubOpen) { return ( -
+
+
ARI / @@ -800,7 +800,7 @@ function Shell() { } return ( -
+
{sidebarOpen ? ( -