diff --git a/web-app/src/containers/ColorPickerAppBgColor.tsx b/web-app/src/containers/ColorPickerAppBgColor.tsx index 36566d03b..c60b34f13 100644 --- a/web-app/src/containers/ColorPickerAppBgColor.tsx +++ b/web-app/src/containers/ColorPickerAppBgColor.tsx @@ -1,4 +1,4 @@ -import { useAppearance, isDefaultColor, useBlurSupport } from '@/hooks/useAppearance' +import { useAppearance, useBlurSupport } from '@/hooks/useAppearance' import { cn } from '@/lib/utils' import { RgbaColor, RgbaColorPicker } from 'react-colorful' import { IconColorPicker } from '@tabler/icons-react' @@ -62,6 +62,15 @@ export function ColorPickerAppBgColor() { }, ] + // Check if a color is the default color (considering both dark and light themes) + const isColorDefault = (color: RgbaColor): boolean => { + const isDarkDefault = color.r === 25 && color.g === 25 && color.b === 25 + const isLightDefault = color.r === 255 && color.g === 255 && color.b === 255 + // Accept both 0.4 and 1 as valid default alpha values (handles blur detection timing) + const hasDefaultAlpha = Math.abs(color.a - 0.4) < 0.01 || Math.abs(color.a - 1) < 0.01 + return (isDarkDefault || isLightDefault) && hasDefaultAlpha + } + return (
{predefineAppBgColor.map((item, i) => { @@ -69,13 +78,13 @@ export function ColorPickerAppBgColor() { (item.r === appBgColor.r && item.g === appBgColor.g && item.b === appBgColor.b && - item.a === appBgColor.a) || - (isDefaultColor(appBgColor) && isDefaultColor(item)) + Math.abs(item.a - appBgColor.a) < 0.01) || + (isColorDefault(appBgColor) && isColorDefault(item)) return (
{ diff --git a/web-app/src/hooks/useAppearance.ts b/web-app/src/hooks/useAppearance.ts index 874c8ae84..1d422d90f 100644 --- a/web-app/src/hooks/useAppearance.ts +++ b/web-app/src/hooks/useAppearance.ts @@ -109,10 +109,14 @@ const isColorEqual = (color1: RgbaColor, color2: RgbaColor): boolean => { // Helper function to check if color is default (not customized) export const isDefaultColor = (color: RgbaColor): boolean => { - return ( - isColorEqual(color, defaultAppBgColor) || - isColorEqual(color, defaultLightAppBgColor) - ) + // Check if RGB matches default (ignore alpha since it changes based on blur support) + const isDarkDefault = color.r === 25 && color.g === 25 && color.b === 25 + const isLightDefault = color.r === 255 && color.g === 255 && color.b === 255 + + // Consider it default if RGB matches and alpha is either 0.4 or 1 (common values) + const hasDefaultAlpha = Math.abs(color.a - 0.4) < 0.01 || Math.abs(color.a - 1) < 0.01 + + return (isDarkDefault || isLightDefault) && hasDefaultAlpha } export const isDefaultColorMainView = (color: RgbaColor): boolean => { @@ -213,8 +217,11 @@ export const useAppearance = create()( defaultFontSize ) - // Reset app background color - const defaultBg = isDark ? defaultAppBgColor : defaultLightAppBgColor + // Reset app background color with correct alpha based on blur support + const currentAlpha = blurEffectsSupported && IS_TAURI ? 0.4 : 1 + const defaultBg = isDark + ? { r: 25, g: 25, b: 25, a: currentAlpha } + : { r: 255, g: 255, b: 255, a: currentAlpha } const culoriRgbBg = rgb({ mode: 'rgb', r: defaultBg.r / 255, @@ -351,12 +358,11 @@ export const useAppearance = create()( // If color is being set to default, use theme-appropriate default let finalColor = color if (isDefaultColor(color)) { - finalColor = isDark ? defaultAppBgColor : defaultLightAppBgColor - } - - // Force alpha to 1 if blur effects are not supported - if (!blurEffectsSupported && (IS_WINDOWS || IS_LINUX || !IS_TAURI)) { - finalColor = { ...finalColor, a: 1 } + // Use current blur support state to determine alpha + const currentAlpha = blurEffectsSupported && IS_TAURI ? 0.4 : 1 + finalColor = isDark + ? { r: 25, g: 25, b: 25, a: currentAlpha } + : { r: 255, g: 255, b: 255, a: currentAlpha } } // Convert RGBA to a format culori can work with @@ -629,11 +635,9 @@ export const useAppearance = create()( // Get the current theme state const { isDark } = useTheme.getState() - // If stored color is default, use theme-appropriate default - let finalColor = state.appBgColor - if (isDefaultColor(state.appBgColor)) { - finalColor = isDark ? defaultAppBgColor : defaultLightAppBgColor - } + // Just use the stored color as-is during rehydration + // The AppearanceProvider will handle alpha normalization after blur detection + const finalColor = state.appBgColor let finalColorMainView = state.appMainViewBgColor if (isDefaultColorMainView(state.appMainViewBgColor)) { diff --git a/web-app/src/providers/AppearanceProvider.tsx b/web-app/src/providers/AppearanceProvider.tsx index 70c9a881f..290c42231 100644 --- a/web-app/src/providers/AppearanceProvider.tsx +++ b/web-app/src/providers/AppearanceProvider.tsx @@ -31,6 +31,28 @@ export function AppearanceProvider() { const { isDark } = useTheme() const showAlphaSlider = useBlurSupport() + // Force re-apply appearance on mount to fix theme desync issues on Windows + // This ensures that when navigating to routes (like logs), the theme is properly applied + useEffect(() => { + const { + setAppBgColor, + setAppMainViewBgColor, + appBgColor, + appMainViewBgColor, + } = useAppearance.getState() + + // Re-trigger setters to ensure CSS variables are applied with correct theme + setAppBgColor(appBgColor) + setAppMainViewBgColor(appMainViewBgColor) + }, []) // Run once on mount + + // Update colors when blur support changes (important for Windows/Linux) + useEffect(() => { + const { setAppBgColor, appBgColor } = useAppearance.getState() + // Re-apply color to update alpha based on blur support + setAppBgColor(appBgColor) + }, [showAlphaSlider]) + // Apply appearance settings on mount and when they change useEffect(() => { // Apply font size @@ -197,6 +219,10 @@ export function AppearanceProvider() { setAppDestructiveBgColor, } = useAppearance.getState() + // Force re-apply all colors when theme changes to ensure correct dark/light defaults + // This is especially important on Windows where the theme might not be properly + // synchronized when navigating to different routes (e.g., logs page) + // If using default background color, update it when theme changes if (isDefaultColor(appBgColor)) { // This will trigger the appropriate updates for both background and text color