diff --git a/openless-all/app/src-tauri/src/lib.rs b/openless-all/app/src-tauri/src/lib.rs
index 33bb590d..e5096788 100644
--- a/openless-all/app/src-tauri/src/lib.rs
+++ b/openless-all/app/src-tauri/src/lib.rs
@@ -204,8 +204,47 @@ pub fn run() {
let suppress_show = !force_show && coordinator.prefs().get().start_minimized;
if suppress_show {
log::info!("[main] start_minimized=true → 跳过初始 show,等用户点托盘");
- } else if let Err(e) = main.show() {
- log::warn!("[main] initial show failed: {e}");
+ } else {
+ #[cfg(target_os = "linux")]
+ {
+ // Workaround for Linux Wayland WebKitGTK compositing:
+ // `visible:false` → `show()` can leave the webview surface
+ // without a valid input region. The ±1px nudge forces
+ // GTK size-allocate → input surface reattach.
+ // Ref: tauri#9394, cc-switch linux_fix.rs
+ let main_clone = main.clone();
+ let _ = main_clone.set_focus();
+ tauri::async_runtime::spawn(async move {
+ tokio::time::sleep(std::time::Duration::from_millis(200)).await;
+ let _ = main_clone.set_focus();
+ if let Ok(orig) = main_clone.inner_size() {
+ let bumped = tauri::PhysicalSize::new(
+ orig.width.saturating_add(1),
+ orig.height,
+ );
+ let _ = main_clone.set_size(bumped);
+ tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+ let _ = main_clone.set_size(orig);
+ log::info!("[main] Linux nudge: focus + surface reactivation done");
+ // Reconcile: compositor may have coalesced the two
+ // set_size calls, leaving the window at width+1.
+ tokio::time::sleep(std::time::Duration::from_millis(500)).await;
+ if let Ok(after) = main_clone.inner_size() {
+ // Only correct the ±1px nudge artifact — if the
+ // compositor or user resized the window significantly
+ // during this window, don't clobber that change.
+ let dw = if after.width > orig.width { after.width - orig.width } else { orig.width - after.width };
+ let dh = if after.height > orig.height { after.height - orig.height } else { orig.height - after.height };
+ if dw <= 1 && dh <= 1 && (dw > 0 || dh > 0) {
+ let _ = main_clone.set_size(orig);
+ }
+ }
+ }
+ });
+ }
+ if let Err(e) = main.show() {
+ log::warn!("[main] initial show failed: {e}");
+ }
}
}
diff --git a/openless-all/app/src-tauri/src/main.rs b/openless-all/app/src-tauri/src/main.rs
index cb1e6ae9..dc101365 100644
--- a/openless-all/app/src-tauri/src/main.rs
+++ b/openless-all/app/src-tauri/src/main.rs
@@ -2,5 +2,20 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
+ // Work around WebKitGTK compositing bugs on Linux Wayland:
+ // - WEBKIT_DISABLE_COMPOSITING_MODE=1 fixes "whole window unresponsive
+ // to clicks until maximize/restore" (tauri#9394)
+ // - WEBKIT_DISABLE_DMABUF_RENDERER=1 fixes white/black screen on some
+ // GPU/driver combos (e.g. Nvidia + Debian)
+ #[cfg(target_os = "linux")]
+ {
+ if std::env::var("WEBKIT_DISABLE_DMABUF_RENDERER").is_err() {
+ std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
+ }
+ if std::env::var("WEBKIT_DISABLE_COMPOSITING_MODE").is_err() {
+ std::env::set_var("WEBKIT_DISABLE_COMPOSITING_MODE", "1");
+ }
+ }
+
openless_lib::run();
}
diff --git a/openless-all/app/src-tauri/tauri.linux.conf.json b/openless-all/app/src-tauri/tauri.linux.conf.json
index 4016955d..c77074e2 100644
--- a/openless-all/app/src-tauri/tauri.linux.conf.json
+++ b/openless-all/app/src-tauri/tauri.linux.conf.json
@@ -10,8 +10,8 @@
"minWidth": 980,
"minHeight": 640,
"resizable": true,
- "decorations": true,
- "transparent": false,
+ "decorations": false,
+ "transparent": true,
"shadow": true,
"visible": false,
"acceptFirstMouse": true
diff --git a/openless-all/app/src/components/WindowChrome.tsx b/openless-all/app/src/components/WindowChrome.tsx
index fdd3d76f..0e669373 100644
--- a/openless-all/app/src/components/WindowChrome.tsx
+++ b/openless-all/app/src/components/WindowChrome.tsx
@@ -1,4 +1,4 @@
-import { type CSSProperties, type ReactNode } from 'react';
+import { type CSSProperties, type ReactNode, useCallback, useEffect, useRef, useState } from 'react';
export type OS = 'mac' | 'win' | 'linux';
@@ -16,6 +16,7 @@ export function detectOS(): OS {
const MAC_TITLEBAR_HEIGHT = 28;
const MAC_SYSTEM_CONTROLS_RESERVED_WIDTH = 76;
+const LINUX_TITLEBAR_HEIGHT = 36;
const WIN_CONSOLE_RADIUS = 10;
interface WindowChromeProps {
@@ -30,17 +31,15 @@ export function WindowChrome({
children,
height = 800,
}: WindowChromeProps) {
- // Windows 下交还原生外壳(decorations:true):外层不画圆角 / 边框 / 阴影 / 标题栏,
- // 避免与原生窗口的角和关闭按钮重叠。内层卡片保留 10px 圆角,跟整体设计对齐。
+ // Windows: decorations:true 时外层不画圆角/边框/阴影/标题栏,避免与原生窗口重叠。
+ // Linux: decorations:false 时外层画 14px 圆角 + 自定义标题栏。
const shellRadius = os === 'mac' ? 0 : os === 'win' ? 0 : 14;
const consoleRadius = os === 'mac' ? 20 : os === 'win' ? WIN_CONSOLE_RADIUS : 14;
- const titlebarHeight = os === 'mac' ? MAC_TITLEBAR_HEIGHT : 0;
+ const titlebarHeight = os === 'mac' ? MAC_TITLEBAR_HEIGHT : os === 'linux' ? LINUX_TITLEBAR_HEIGHT : 0;
- // 两个平台用同一份半透明玻璃 background + backdropFilter,让 sidebar 透明地坐在
- // 磨砂底板上时有可见的玻璃感。
- // Windows: Tauri transparent:true + lib.rs apply_mica 提供 Win11 Mica 透出来;
- // macOS: NSVisualEffectView 提供材质。
- // alpha 0.92:和原生 Win11 caption(lib.rs 设为 rgb(245,245,247))色差最小,玻璃感仍可见。
+ // 三个平台共用半透明玻璃 background + backdropFilter。
+ // macOS: NSVisualEffectView 提供材质;Windows: Tauri apply_mica 提供 Mica;
+ // Linux: decorations:false 后 CSS 磨砂玻璃自成背景。
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%),
@@ -83,9 +82,156 @@ export function WindowChrome({
}}
/>
)}
+ {os === 'linux' &&