💄 style: fix theme issue in desktop (#8380)

* 💄 style: fix theme issue

* fix

* fix
This commit is contained in:
Arvin Xu
2025-07-10 15:47:53 +08:00
committed by GitHub
parent b7ca447946
commit c7ae78bfb7
5 changed files with 77 additions and 12 deletions
+47 -1
View File
@@ -1,15 +1,27 @@
import { ElectronAppState, ThemeMode } from '@lobechat/electron-client-ipc';
import { app, shell, systemPreferences } from 'electron';
import { app, nativeTheme, shell, systemPreferences } from 'electron';
import { macOS } from 'electron-is';
import { readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import process from 'node:process';
import { DB_SCHEMA_HASH_FILENAME, LOCAL_DATABASE_DIR, userDataDir } from '@/const/dir';
import { createLogger } from '@/utils/logger';
import { ControllerModule, ipcClientEvent, ipcServerEvent } from './index';
const logger = createLogger('controllers:SystemCtr');
export default class SystemController extends ControllerModule {
private systemThemeListenerInitialized = false;
/**
* Initialize system theme listener when app is ready
*/
afterAppReady() {
this.initializeSystemThemeListener();
}
/**
* Handles the 'getDesktopAppState' IPC request.
* Gathers essential application and system information.
@@ -26,6 +38,7 @@ export default class SystemController extends ControllerModule {
isMac: platform === 'darwin',
isWindows: platform === 'win32',
platform: platform as 'darwin' | 'win32' | 'linux',
systemAppearance: nativeTheme.shouldUseDarkColors ? 'dark' : 'light',
userPath: {
// User Paths (ensure keys match UserPathData / DesktopAppState interface)
desktop: app.getPath('desktop'),
@@ -100,4 +113,37 @@ export default class SystemController extends ControllerModule {
private get DB_SCHEMA_HASH_PATH() {
return join(this.app.appStoragePath, DB_SCHEMA_HASH_FILENAME);
}
/**
* Initialize system theme listener to monitor OS theme changes
*/
private initializeSystemThemeListener() {
if (this.systemThemeListenerInitialized) {
logger.debug('System theme listener already initialized');
return;
}
logger.info('Initializing system theme listener');
// Get initial system theme
const initialDarkMode = nativeTheme.shouldUseDarkColors;
const initialSystemTheme: ThemeMode = initialDarkMode ? 'dark' : 'light';
logger.info(`Initial system theme: ${initialSystemTheme}`);
// Listen for system theme changes
nativeTheme.on('updated', () => {
const isDarkMode = nativeTheme.shouldUseDarkColors;
const systemTheme: ThemeMode = isDarkMode ? 'dark' : 'light';
logger.info(`System theme changed to: ${systemTheme}`);
// Broadcast system theme change to all renderer processes
this.app.browserManager.broadcastToAllWindows('systemThemeChanged', {
themeMode: systemTheme,
});
});
this.systemThemeListenerInitialized = true;
logger.info('System theme listener initialized successfully');
}
}
@@ -1,3 +1,5 @@
import { ThemeAppearance } from 'antd-style';
import { ElectronAppState, ThemeMode } from '../types';
export interface SystemDispatchEvents {
@@ -16,5 +18,6 @@ export interface SystemDispatchEvents {
}
export interface SystemBroadcastEvents {
systemThemeChanged: (data: { themeMode: ThemeAppearance }) => void;
themeChanged: (data: { themeMode: ThemeMode }) => void;
}
@@ -3,7 +3,8 @@ export interface ElectronAppState {
isLinux?: boolean;
isMac?: boolean;
isWindows?: boolean;
platform?: 'darwin' | 'win32' | 'linux'; // , etc.
platform?: 'darwin' | 'win32' | 'linux';
systemAppearance?: string;
userPath?: UserPathData;
}
@@ -3,19 +3,32 @@ import { useTheme } from 'antd-style';
import { rgba } from 'polished';
import { useEffect } from 'react';
import { useElectronStore } from '@/store/electron';
import { useGlobalStore } from '@/store/global';
export const useWatchThemeUpdate = () => {
const [systemAppearance, updateElectronAppState] = useElectronStore((s) => [
s.appState.systemAppearance,
s.updateElectronAppState,
]);
const switchThemeMode = useGlobalStore((s) => s.switchThemeMode);
const token = useTheme();
const theme = useTheme();
useWatchBroadcast('themeChanged', ({ themeMode }) => {
switchThemeMode(themeMode, { skipBroadcast: true });
});
useWatchBroadcast('systemThemeChanged', ({ themeMode }) => {
updateElectronAppState({ systemAppearance: themeMode });
});
useEffect(() => {
document.documentElement.style.background = 'none';
document.body.style.background = rgba(token.colorBgLayout, 0.66);
}, [token]);
// https://x.com/alanblogsooo/status/1939208908993896684
const isNotSameTheme = !systemAppearance ? true : theme.appearance !== systemAppearance;
document.body.style.background = rgba(theme.colorBgLayout, isNotSameTheme ? 0.95 : 0.66);
}, [theme, systemAppearance]);
};
+9 -7
View File
@@ -6,18 +6,15 @@ import { useOnlyFetchOnceSWR } from '@/libs/swr';
// Import for type usage
import { electronSystemService } from '@/services/electron/system';
import { globalAgentContextManager } from '@/utils/client/GlobalAgentContextManager';
import { merge } from '@/utils/merge';
import { ElectronStore } from '../store';
// Import the new service
// ======== State ======== //
// Note: Actual state is defined in initialState.ts and ElectronState interface
// ======== Action Interface ======== //
export interface ElectronAppAction {
updateElectronAppState: (state: ElectronAppState) => void;
/**
* Initializes the basic Electron application state, including system info and special paths.
* Should be called once when the application starts.
@@ -32,7 +29,12 @@ export const createElectronAppSlice: StateCreator<
[['zustand/devtools', never]],
[],
ElectronAppAction
> = (set) => ({
> = (set, get) => ({
updateElectronAppState: (state: ElectronAppState) => {
const prevState = get().appState;
set({ appState: merge(prevState, state) });
},
useInitElectronAppState: () =>
useOnlyFetchOnceSWR<ElectronAppState>(
'initElectronAppState',