fix(linux): use solid surfaces for windows

Disable Linux transparent windows for main, capsule, and QA so WebKitGTK compositing no longer controls frosted-glass visuals.

Remove the no-compositing frontend fallback command and route Linux shell surfaces through opaque surface styling while preserving the existing compositing environment workaround.
This commit is contained in:
H-Chris233 committed 2026-06-06 14:08:00 +08:00
1 parent 5ca3a80cd6
commit b59c920195
10 files changed
+34 -94

No files matched your search

@@ -3564,12 +3564,6 @@ pub async fn github_device_flow_poll(
Ok(GithubDevicePollResult::Error { message: msg })
}
#[tauri::command]
pub fn is_no_compositing_mode() -> bool {
// Linux WebKitGTK: WEBKIT_DISABLE_COMPOSITING_MODE=1 时 backdrop-filter 不可用
std::env::var("WEBKIT_DISABLE_COMPOSITING_MODE").as_deref() == Ok("1")
}
#[cfg(test)]
mod tests {
use super::{
-1
View File
@@ -471,7 +471,6 @@ pub fn run() {
#[cfg(target_os = "windows")]
commands::sherpa_onnx_asr_reveal_model_dir,
commands::export_error_log,
commands::is_no_compositing_mode,
restart_app,
])
.build(tauri::generate_context!())
@@ -11,7 +11,7 @@
"minHeight": 640,
"resizable": true,
"decorations": false,
"transparent": true,
"transparent": false,
"shadow": true,
"visible": false,
"acceptFirstMouse": true
@@ -23,7 +23,7 @@
"width": 220,
"height": 110,
"decorations": false,
"transparent": true,
"transparent": false,
"shadow": false,
"alwaysOnTop": true,
"skipTaskbar": true,
@@ -40,7 +40,7 @@
"width": 380,
"height": 440,
"decorations": false,
"transparent": true,
"transparent": false,
"shadow": true,
"alwaysOnTop": true,
"skipTaskbar": true,
-14
View File
@@ -17,7 +17,6 @@ import {
windowMouseHotkeyCode,
} from './lib/windowHotkeyFallback';
import { QaPanel } from './pages/QaPanel';
import { invoke } from '@tauri-apps/api/core';
import { HotkeySettingsProvider } from './state/HotkeySettingsContext';
interface AppProps {
@@ -169,18 +168,6 @@ export function App({ isCapsule, isQa, forcedOs }: AppProps) {
};
}, [os]);
// Linux: 检测 WEBKIT_DISABLE_COMPOSITING_MODE → 触发磨砂 fallback(#570, #553)
useEffect(() => {
if (!isTauri) return;
invoke<boolean>('is_no_compositing_mode').then((val) => {
if (val) {
document.documentElement.dataset.olNoCompositing = 'true';
}
}).catch((err) => {
console.warn('[startup] is_no_compositing_mode failed', err);
});
}, []);
return (
<HotkeySettingsProvider>
{gate === 'onboarding' ? <Onboarding onComplete={() => setGate('ready')} /> : <FloatingShell os={os} />}
@@ -188,4 +175,3 @@ export function App({ isCapsule, isQa, forcedOs }: AppProps) {
</HotkeySettingsProvider>
);
}
+1 -1
View File
@@ -247,7 +247,7 @@ function Pill({ os, state, level, insertedChars, message, onCancel, onConfirm }:
const shadowAlpha = 0.20 + ambient * 0.10;
return (
// 假毛玻璃:半透明白底 + .ol-frost 噪点纹理 + 内描边高光 + 柔和阴影。
// 非 Linux 走假毛玻璃;Linux 禁用透明窗口后由 .ol-frost 平台规则退成不透明面。
// 不写 backdrop-filter —— webview 模糊不了透明窗口背后的桌面(Tauri 上游限制)。
<div
className="ol-frost"
@@ -319,18 +319,16 @@ function FloatingShellBody({ os, initialTab, initialSettings }: { os: OS; initia
</div>
</aside>
{/* Main content — v1.3.1-8 用户希望"sidebar / main panel / caption 三处玻璃统一"。
从 var(--ol-surface) 不透明白底 改成半透明白 + backdrop-filter,跟 sidebar
一起坐在 WindowChrome 的磨砂底板上,整体一块连续玻璃。 */}
{/* Main content — Linux 禁用透明窗口后使用不透明面;其他平台保留玻璃层。 */}
<div style={{ flex: 1, minWidth: 0, padding: '4px 8px 6px 0', display: 'flex' }}>
<main
className="ol-console-main"
style={{
flex: 1, minWidth: 0,
overflow: 'hidden',
background: 'rgba(255, 255, 255, 0.62)',
backdropFilter: 'blur(18px) saturate(170%)',
WebkitBackdropFilter: 'blur(18px) saturate(170%)',
background: os === 'linux' ? 'var(--ol-surface)' : 'rgba(255, 255, 255, 0.62)',
backdropFilter: os === 'linux' ? 'none' : 'blur(18px) saturate(170%)',
WebkitBackdropFilter: os === 'linux' ? 'none' : 'blur(18px) saturate(170%)',
borderRadius: 'var(--ol-window-console-radius)',
border: '0.5px solid rgba(0,0,0,0.06)',
boxShadow: '0 1px 0 rgba(255,255,255,0.8) inset, 0 8px 24px -12px rgba(15,17,22,0.10), 0 2px 6px -2px rgba(15,17,22,0.06)',
@@ -629,4 +627,3 @@ function HotkeyModeMigrationPrompt({ onLater, onOpenSettings }: { onLater: () =>
</div>
);
}
@@ -37,17 +37,15 @@ export function WindowChrome({
const consoleRadius = os === 'mac' ? 20 : os === 'win' ? WIN_CONSOLE_RADIUS : 14;
const titlebarHeight = os === 'mac' ? MAC_TITLEBAR_HEIGHT : os === 'linux' ? LINUX_TITLEBAR_HEIGHT : 0;
// 三个平台共用半透明玻璃 background + backdropFilter。
// macOS / Windows 共用半透明玻璃 background + backdropFilter。
// macOS: NSVisualEffectView 提供材质;Windows: Tauri apply_mica 提供 Mica;
// Linux: decorations:false 后 CSS 磨砂玻璃自成背景。
//
// 注意:三层渐变的参数与 global.css 中 [data-ol-no-compositing] .ol-winchrome
// 的回退 background 同步(只 opacity 从 0.92 提到 0.96)。改这里时请同步更新 CSS。
// Linux: 透明窗口 / WebKitGTK 合成层不稳定,走不透明 surface。
const background = `
radial-gradient(120% 80% at 0% 0%, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 60%),
radial-gradient(100% 70% at 100% 100%, rgba(37,99,235,0.07) 0%, rgba(37,99,235,0) 55%),
linear-gradient(180deg, rgba(245,245,247,0.92) 0%, rgba(232,232,236,0.92) 100%)
`;
const useSolidSurface = os === 'linux';
return (
<div
@@ -65,9 +63,9 @@ export function WindowChrome({
display: 'flex',
flexDirection: 'column',
border: os === 'win' ? 'none' : os === 'mac' ? 'none' : '0.5px solid rgba(0,0,0,.10)',
background,
backdropFilter: 'blur(var(--ol-glass-blur-strong)) saturate(190%)',
WebkitBackdropFilter: 'blur(var(--ol-glass-blur-strong)) saturate(190%)',
background: useSolidSurface ? 'var(--ol-surface)' : background,
backdropFilter: useSolidSurface ? 'none' : 'blur(var(--ol-glass-blur-strong)) saturate(190%)',
WebkitBackdropFilter: useSolidSurface ? 'none' : 'blur(var(--ol-glass-blur-strong)) saturate(190%)',
animation: os === 'win' ? undefined : 'ol-window-enter 0.42s var(--ol-motion-spring) both',
transition: 'box-shadow 0.28s var(--ol-motion-soft), border-color 0.28s var(--ol-motion-soft), backdrop-filter 0.28s var(--ol-motion-soft)',
willChange: 'opacity, transform, filter',
@@ -160,9 +158,9 @@ function LinuxTitlebar() {
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 6px 0 14px',
background: 'rgba(245,245,247,0.85)',
backdropFilter: 'blur(12px) saturate(180%)',
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
background: 'var(--ol-surface)',
backdropFilter: 'none',
WebkitBackdropFilter: 'none',
borderBottom: '0.5px solid rgba(0,0,0,0.08)',
color: 'var(--ol-ink-3)',
fontSize: 13,
+4 -1
View File
@@ -1,6 +1,7 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { App } from "./App";
import { detectOS } from "./components/WindowChrome";
import i18n from "./i18n"; // 副作用:触发 i18next init
import "./styles/tokens.css";
import "./styles/global.css";
@@ -12,13 +13,15 @@ const windowKind = params.get("window");
const isCapsule = windowKind === "capsule";
const isQa = windowKind === "qa";
const osQuery = params.get("os") as OS | null;
const os = osQuery ?? detectOS();
document.documentElement.dataset.olPlatform = os;
const root = ReactDOM.createRoot(document.getElementById("root")!);
const renderApp = () => {
root.render(
<React.StrictMode>
<App isCapsule={isCapsule} isQa={isQa} forcedOs={osQuery} />
<App isCapsule={isCapsule} isQa={isQa} forcedOs={os} />
</React.StrictMode>,
);
};
+2 -3
View File
@@ -611,9 +611,8 @@ function truncate(text: string, max: number): string {
// ── 样式 ──────────────────────────────────────────────────────────────
// 假毛玻璃外壳:玻璃质感(体渐变 + 高光扫面 + 噪点颗粒)全部由 .ol-frost 提供;
// 这里只管布局 + 内描边高光 + 柔和阴影。webview 模糊不了透明窗口背后的桌面
// (Tauri 上游限制),所以不写 background / backdrop-filter。
// 非 Linux 走 .ol-frost 假毛玻璃;Linux 禁用透明窗口后退成不透明面。
// 这里只管布局 + 内描边高光 + 柔和阴影,所以不写 background / backdrop-filter。
const shellStyle: CSSProperties = {
width: '100%',
height: '100vh',
+11 -47
View File
@@ -7,6 +7,12 @@ html, body, #root {
overflow: hidden;
}
html[data-ol-platform="linux"],
html[data-ol-platform="linux"] body,
html[data-ol-platform="linux"] #root {
background: var(--ol-surface);
}
body {
background: transparent;
user-select: none;
@@ -133,7 +139,7 @@ a { color: inherit; text-decoration: none; }
是 Tauri 已知的上游限制(issue #2827 / #10064 / #6876)。所以这里**不做
真实高斯模糊**:宿主元素保留半透明白底,这层 ::before 叠一张细颗粒噪点纹理
(磨砂质感来源);内描边高光与柔和阴影由宿主自带的 box-shadow 提供。
纯静态 CSS,零运行时 / 动画开销,macOS / Windows / Linux 像素一致。
纯静态 CSS,零运行时 / 动画开销。Linux 主动禁用透明窗口后走纯不透明面。
宿主元素自带 border-radius 即可,::before 噪点层 inherit 圆角;宿主不要再设
backdrop-filter(死代码)。 */
.ol-frost {
@@ -164,52 +170,10 @@ a { color: inherit; text-decoration: none; }
pointer-events: none;
}
/* ── Linux no-compositing fallback ──────────────────────────────────────
WEBKIT_DISABLE_COMPOSITING_MODE=1 时 backdrop-filter 不可用,用噪点纹理
替代磨砂效果。data-ol-no-compositing 由 App.tsx 在初始化时设置。
!important 是因为三处目标的 backdrop-filter / background 均为 inline style
(WindowChrome.tsx / FloatingShell.tsx 的 style={...}),CSS class 必须
用 !important 才能覆盖。 */
[data-ol-no-compositing] .ol-winchrome {
position: relative;
isolation: isolate;
/* !important 覆盖 WindowChrome.tsx inline backdropFilter */
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
/* 提高透明梯度不透明度,补偿缺少模糊的视觉深度
注意:此三层的渐变参数与 WindowChrome.tsx 的 inline background 同步,
改动那边时请同步更新这里。 */
background:
radial-gradient(120% 80% at 0% 0%, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 60%),
radial-gradient(100% 70% at 100% 100%, rgba(37,99,235,0.07) 0%, rgba(37,99,235,0) 55%),
linear-gradient(180deg, rgba(245,245,247,0.96) 0%, rgba(232,232,236,0.96) 100%) !important;
html[data-ol-platform="linux"] .ol-frost {
background: var(--ol-surface);
}
[data-ol-no-compositing] .ol-winchrome::after {
content: '';
position: absolute;
inset: 0;
z-index: -1;
border-radius: inherit;
/* 灰阶噪点颗粒,低 opacity 模拟磨砂质感 */
background-image: var(--ol-frost-grain);
background-size: 100px 100px;
opacity: 0.20;
pointer-events: none;
}
[data-ol-no-compositing] .ol-console-main {
/* !important 覆盖 FloatingShell.tsx inline backdropFilter */
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
background: rgba(255, 255, 255, 0.80) !important;
}
[data-ol-no-compositing] .ol-linux-titlebar {
/* !important 覆盖 WindowChrome.tsx LinuxTitlebar inline backdropFilter */
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
background: rgba(245, 245, 247, 0.95) !important;
html[data-ol-platform="linux"] .ol-frost::before {
content: none;
}