diff --git a/src-tauri/capabilities/log-app-window.json b/src-tauri/capabilities/log-app-window.json index 9f95d1bb9..97dbf952d 100644 --- a/src-tauri/capabilities/log-app-window.json +++ b/src-tauri/capabilities/log-app-window.json @@ -3,10 +3,13 @@ "identifier": "logs-app-window", "description": "enables permissions for the logs app window", "windows": ["logs-app-window"], + "platforms": ["linux", "macOS", "windows"], "permissions": [ "core:default", "core:window:allow-start-dragging", "core:window:allow-set-theme", + "core:window:allow-get-all-windows", + "core:event:allow-listen", "log:default", "core:webview:allow-create-webview-window", "core:window:allow-set-focus" diff --git a/src-tauri/capabilities/logs-window.json b/src-tauri/capabilities/logs-window.json index ef56e6f75..34b1e033a 100644 --- a/src-tauri/capabilities/logs-window.json +++ b/src-tauri/capabilities/logs-window.json @@ -3,10 +3,13 @@ "identifier": "logs-window", "description": "enables permissions for the logs window", "windows": ["logs-window-local-api-server"], + "platforms": ["linux", "macOS", "windows"], "permissions": [ "core:default", "core:window:allow-start-dragging", "core:window:allow-set-theme", + "core:window:allow-get-all-windows", + "core:event:allow-listen", "log:default", "core:webview:allow-create-webview-window", "core:window:allow-set-focus" diff --git a/src-tauri/capabilities/system-monitor-window.json b/src-tauri/capabilities/system-monitor-window.json index 68a75e9fb..75a26fd87 100644 --- a/src-tauri/capabilities/system-monitor-window.json +++ b/src-tauri/capabilities/system-monitor-window.json @@ -8,6 +8,8 @@ "core:default", "core:window:allow-start-dragging", "core:window:allow-set-theme", + "core:window:allow-get-all-windows", + "core:event:allow-listen", "log:default", "core:webview:allow-create-webview-window", "core:window:allow-set-focus", @@ -15,6 +17,18 @@ "hardware:allow-get-system-usage", "llamacpp:allow-get-devices", "llamacpp:allow-read-gguf-metadata", - "deep-link:allow-get-current" + "deep-link:allow-get-current", + { + "identifier": "http:default", + "allow": [ + { + "url": "https://*:*" + }, + { + "url": "http://*:*" + } + ], + "deny": [] + } ] } diff --git a/src-tauri/src/core/setup.rs b/src-tauri/src/core/setup.rs index 6564d3609..7ba8f2f74 100644 --- a/src-tauri/src/core/setup.rs +++ b/src-tauri/src/core/setup.rs @@ -7,7 +7,7 @@ use std::{ }; use tar::Archive; use tauri::{ - App, Emitter, Manager, Runtime, Wry + App, Emitter, Manager, Runtime, Wry, WindowEvent }; #[cfg(desktop)] @@ -270,3 +270,32 @@ pub fn setup_tray(app: &App) -> tauri::Result { }) .build(app) } + +pub fn setup_theme_listener(app: &App) -> tauri::Result<()> { + // Setup theme listener for main window + if let Some(window) = app.get_webview_window("main") { + setup_window_theme_listener(app.handle().clone(), window); + } + + Ok(()) +} + +fn setup_window_theme_listener( + app_handle: tauri::AppHandle, + window: tauri::WebviewWindow, +) { + let window_label = window.label().to_string(); + let app_handle_clone = app_handle.clone(); + + window.on_window_event(move |event| { + if let WindowEvent::ThemeChanged(theme) = event { + let theme_str = match theme { + tauri::Theme::Light => "light", + tauri::Theme::Dark => "dark", + _ => "auto", + }; + log::info!("System theme changed to: {} for window: {}", theme_str, window_label); + let _ = app_handle_clone.emit("theme-changed", theme_str); + } + }); +} diff --git a/src-tauri/src/core/system/commands.rs b/src-tauri/src/core/system/commands.rs index 938e6f8bf..e01c36854 100644 --- a/src-tauri/src/core/system/commands.rs +++ b/src-tauri/src/core/system/commands.rs @@ -117,3 +117,108 @@ pub fn is_library_available(library: &str) -> bool { } } } + +// Check if the system supports blur/acrylic effects +// - Windows: Checks build version (17134+ for acrylic support) +// - Linux: Checks for KWin (KDE) or compositor with blur support +// - macOS: Always supported +#[tauri::command] +pub fn supports_blur_effects() -> bool { + #[cfg(target_os = "windows")] + { + // Windows 10 build 17134 (1803) and later support acrylic effects + // Windows 11 (build 22000+) has better support + use std::process::Command; + + if let Ok(output) = Command::new("cmd") + .args(&["/C", "ver"]) + .output() + { + if let Ok(version_str) = String::from_utf8(output.stdout) { + // Parse Windows version from output like "Microsoft Windows [Version 10.0.22631.4602]" + if let Some(version_part) = version_str.split("Version ").nth(1) { + if let Some(build_str) = version_part.split('.').nth(2) { + if let Ok(build) = build_str.split(']').next().unwrap_or("0").trim().parse::() { + // Windows 10 build 17134+ or Windows 11 build 22000+ support blur + let supports_blur = build >= 17134; + if supports_blur { + log::info!("✅ Windows build {} detected - Blur/Acrylic effects SUPPORTED", build); + } else { + log::warn!("❌ Windows build {} detected - Blur/Acrylic effects NOT SUPPORTED (requires build 17134+)", build); + } + return supports_blur; + } + } + } + } + } + + // If we can't detect version, assume it doesn't support blur for safety + log::warn!("❌ Could not detect Windows version - Assuming NO blur support for safety"); + false + } + + #[cfg(target_os = "linux")] + { + use std::process::Command; + + // Check for KDE Plasma with KWin (best blur support) + if let Ok(output) = Command::new("kwin_x11").arg("--version").output() { + if output.status.success() { + log::info!("✅ KDE/KWin detected - Blur effects SUPPORTED"); + return true; + } + } + + // Check for Wayland KWin + if let Ok(output) = Command::new("kwin_wayland").arg("--version").output() { + if output.status.success() { + log::info!("✅ KDE/KWin Wayland detected - Blur effects SUPPORTED"); + return true; + } + } + + // Check for GNOME with blur extensions (less reliable) + if std::env::var("XDG_CURRENT_DESKTOP").unwrap_or_default().contains("GNOME") { + log::info!("🔍 GNOME detected - Blur support depends on extensions"); + // GNOME might have blur through extensions, allow it + return true; + } + + // Check for Compiz (older but has blur) + if let Ok(_) = Command::new("compiz").arg("--version").output() { + log::info!("✅ Compiz compositor detected - Blur effects SUPPORTED"); + return true; + } + + // Check for Picom with blur (common X11 compositor) + if let Ok(output) = Command::new("picom").arg("--version").output() { + if output.status.success() { + log::info!("✅ Picom compositor detected - Blur effects SUPPORTED"); + return true; + } + } + + // Check environment variable for compositor + if let Ok(compositor) = std::env::var("COMPOSITOR") { + log::info!("🔍 Compositor detected: {} - Assuming blur support", compositor); + return true; + } + + log::warn!("❌ No known blur-capable compositor detected on Linux"); + false + } + + #[cfg(target_os = "macos")] + { + // macOS always supports blur/vibrancy effects + log::info!("✅ macOS detected - Blur/Vibrancy effects SUPPORTED"); + true + } + + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] + { + log::warn!("❌ Unknown platform - Assuming NO blur support"); + false + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 628f22b08..85916f6cf 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -78,6 +78,7 @@ pub fn run() { core::system::commands::factory_reset, core::system::commands::read_logs, core::system::commands::is_library_available, + core::system::commands::supports_blur_effects, // Server commands core::server::commands::start_server, core::server::commands::stop_server, @@ -193,6 +194,7 @@ pub fn run() { } setup_mcp(app); + setup::setup_theme_listener(app)?; Ok(()) }) .build(tauri::generate_context!()) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b0df3fc2f..fb1b1950b 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -40,7 +40,7 @@ } ], "security": { - "capabilities": ["default"], + "capabilities": ["default", "logs-app-window", "logs-window", "system-monitor-window"], "csp": { "default-src": "'self' customprotocol: asset: http://localhost:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*", "connect-src": "ipc: http://ipc.localhost http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:* https: http:", diff --git a/web-app/index.html b/web-app/index.html index dd2e76ee6..55625d33c 100644 --- a/web-app/index.html +++ b/web-app/index.html @@ -17,7 +17,7 @@ Jan diff --git a/web-app/src/containers/ColorPickerAppBgColor.tsx b/web-app/src/containers/ColorPickerAppBgColor.tsx index 72e098aa4..36566d03b 100644 --- a/web-app/src/containers/ColorPickerAppBgColor.tsx +++ b/web-app/src/containers/ColorPickerAppBgColor.tsx @@ -1,4 +1,4 @@ -import { useAppearance, isDefaultColor } from '@/hooks/useAppearance' +import { useAppearance, isDefaultColor, useBlurSupport } from '@/hooks/useAppearance' import { cn } from '@/lib/utils' import { RgbaColor, RgbaColorPicker } from 'react-colorful' import { IconColorPicker } from '@tabler/icons-react' @@ -14,6 +14,12 @@ export function ColorPickerAppBgColor() { const { appBgColor, setAppBgColor } = useAppearance() const { isDark } = useTheme() const { t } = useTranslation() + const showAlphaSlider = useBlurSupport() + + // Helper to get alpha value based on blur support + const getAlpha = (defaultAlpha: number) => { + return showAlphaSlider ? defaultAlpha : 1 + } const predefineAppBgColor: RgbaColor[] = [ isDark @@ -21,38 +27,38 @@ export function ColorPickerAppBgColor() { r: 25, g: 25, b: 25, - a: IS_WINDOWS || IS_LINUX || !IS_TAURI ? 1 : 0.4, + a: getAlpha(0.4), } : { r: 255, g: 255, b: 255, - a: IS_WINDOWS || IS_LINUX || !IS_TAURI ? 1 : 0.4, + a: getAlpha(0.4), }, { r: 70, g: 79, b: 229, - a: IS_WINDOWS || IS_LINUX || !IS_TAURI ? 1 : 0.5, + a: getAlpha(0.5), }, { r: 238, g: 130, b: 238, - a: IS_WINDOWS || IS_LINUX || !IS_TAURI ? 1 : 0.5, + a: getAlpha(0.5), }, { r: 255, g: 99, b: 71, - a: IS_WINDOWS || IS_LINUX || !IS_TAURI ? 1 : 0.5, + a: getAlpha(0.5), }, { r: 255, g: 165, b: 0, - a: IS_WINDOWS || IS_LINUX || !IS_TAURI ? 1 : 0.5, + a: getAlpha(0.5), }, ] @@ -61,9 +67,9 @@ export function ColorPickerAppBgColor() { {predefineAppBgColor.map((item, i) => { const isSelected = (item.r === appBgColor.r && - item.g === appBgColor.g && - item.b === appBgColor.b && - item.a === appBgColor.a) || + item.g === appBgColor.g && + item.b === appBgColor.b && + item.a === appBgColor.a) || (isDefaultColor(appBgColor) && isDefaultColor(item)) return (
{ return ( <> -
+