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:
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::{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user