mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-17 04:55:51 +00:00
💄 style: fix theme issue in desktop (#8380)
* 💄 style: fix theme issue
* fix
* fix
This commit is contained in:
@@ -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]);
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user