diff --git a/src/main/ipc.ts b/src/main/ipc.ts
index 47e0b25386..0ae09d8b21 100644
--- a/src/main/ipc.ts
+++ b/src/main/ipc.ts
@@ -5,7 +5,7 @@ import { isMac, isWin } from '@main/constant'
import { getBinaryPath, isBinaryExists, runInstallScript } from '@main/utils/process'
import { IpcChannel } from '@shared/IpcChannel'
import { Shortcut, ThemeMode } from '@types'
-import { BrowserWindow, ipcMain, session, shell } from 'electron'
+import { BrowserWindow, ipcMain, nativeTheme, session, shell } from 'electron'
import log from 'electron-log'
import { titleBarOverlayDark, titleBarOverlayLight } from './config'
@@ -119,23 +119,26 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
})
// theme
- ipcMain.handle(IpcChannel.App_SetTheme, (event, theme: ThemeMode) => {
- if (theme === configManager.getTheme()) return
+ ipcMain.handle(IpcChannel.App_SetTheme, (_, theme: ThemeMode) => {
+ const notifyThemeChange = () => {
+ const windows = BrowserWindow.getAllWindows()
+ windows.forEach((win) =>
+ win.webContents.send(IpcChannel.ThemeChange, nativeTheme.shouldUseDarkColors ? ThemeMode.dark : ThemeMode.light)
+ )
+ }
- configManager.setTheme(theme)
-
- // should sync theme change to all windows
- const senderWindowId = event.sender.id
- const windows = BrowserWindow.getAllWindows()
- // 向其他窗口广播主题变化
- windows.forEach((win) => {
- if (win.webContents.id !== senderWindowId) {
- win.webContents.send(IpcChannel.ThemeChange, theme)
- }
- })
+ if (theme === ThemeMode.auto) {
+ nativeTheme.themeSource = 'system'
+ nativeTheme.on('updated', notifyThemeChange)
+ } else {
+ nativeTheme.themeSource = theme
+ nativeTheme.removeAllListeners('updated')
+ }
mainWindow?.setTitleBarOverlay &&
- mainWindow.setTitleBarOverlay(theme === 'dark' ? titleBarOverlayDark : titleBarOverlayLight)
+ mainWindow.setTitleBarOverlay(nativeTheme.shouldUseDarkColors ? titleBarOverlayDark : titleBarOverlayLight)
+ configManager.setTheme(theme)
+ notifyThemeChange()
})
// custom css
diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts
index 10fed67d3d..923fbd6bb5 100644
--- a/src/preload/index.d.ts
+++ b/src/preload/index.d.ts
@@ -28,7 +28,8 @@ declare global {
setTray: (isActive: boolean) => void
setTrayOnClose: (isActive: boolean) => void
restartTray: () => void
- setTheme: (theme: 'light' | 'dark') => void
+ setTheme: (theme: 'light' | 'dark' | 'auto') => void
+ getTheme: () => Promise<'light' | 'dark' | 'auto'>
setCustomCss: (css: string) => void
setAutoUpdate: (isActive: boolean) => void
reload: () => void
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 251706e694..1671f72cf2 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -18,7 +18,7 @@ const api = {
setTray: (isActive: boolean) => ipcRenderer.invoke(IpcChannel.App_SetTray, isActive),
setTrayOnClose: (isActive: boolean) => ipcRenderer.invoke(IpcChannel.App_SetTrayOnClose, isActive),
restartTray: () => ipcRenderer.invoke(IpcChannel.App_RestartTray),
- setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke(IpcChannel.App_SetTheme, theme),
+ setTheme: (theme: 'light' | 'dark' | 'auto') => ipcRenderer.invoke(IpcChannel.App_SetTheme, theme),
setCustomCss: (css: string) => ipcRenderer.invoke(IpcChannel.App_SetCustomCss, css),
setAutoUpdate: (isActive: boolean) => ipcRenderer.invoke(IpcChannel.App_SetAutoUpdate, isActive),
openWebsite: (url: string) => ipcRenderer.invoke(IpcChannel.Open_Website, url),
diff --git a/src/renderer/src/components/app/Sidebar.tsx b/src/renderer/src/components/app/Sidebar.tsx
index d63c7b9992..c1fef66656 100644
--- a/src/renderer/src/components/app/Sidebar.tsx
+++ b/src/renderer/src/components/app/Sidebar.tsx
@@ -19,6 +19,7 @@ import {
MessageSquareQuote,
Moon,
Palette,
+ RefreshCcw,
Settings,
Sparkle,
Sun
@@ -98,7 +99,13 @@ const Sidebar: FC = () => {
mouseEnterDelay={0.8}
placement="right">
toggleTheme()}>
- {theme === 'dark' ? : }
+ {settingTheme === 'dark' ? (
+
+ ) : settingTheme === 'light' ? (
+
+ ) : (
+
+ )}
diff --git a/src/renderer/src/context/ThemeProvider.tsx b/src/renderer/src/context/ThemeProvider.tsx
index dd31b0aed0..b2eafc21f8 100644
--- a/src/renderer/src/context/ThemeProvider.tsx
+++ b/src/renderer/src/context/ThemeProvider.tsx
@@ -11,8 +11,8 @@ interface ThemeContextType {
}
const ThemeContext = createContext({
- theme: ThemeMode.light,
- settingTheme: ThemeMode.light,
+ theme: ThemeMode.auto,
+ settingTheme: ThemeMode.auto,
toggleTheme: () => {}
})
@@ -22,43 +22,37 @@ interface ThemeProviderProps extends PropsWithChildren {
export const ThemeProvider: React.FC = ({ children, defaultTheme }) => {
const { theme, setTheme } = useSettings()
- const [_theme, _setTheme] = useState(theme)
+ const [effectiveTheme, setEffectiveTheme] = useState(theme)
const toggleTheme = () => {
- setTheme(theme === ThemeMode.dark ? ThemeMode.light : ThemeMode.dark)
+ // 主题顺序是light, dark, auto, 所以需要先判断当前主题,然后取下一个主题
+ const nextTheme =
+ theme === ThemeMode.light ? ThemeMode.dark : theme === ThemeMode.dark ? ThemeMode.auto : ThemeMode.light
+ setTheme(nextTheme)
}
- useEffect((): any => {
- if (theme === ThemeMode.auto || defaultTheme === ThemeMode.auto) {
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
- _setTheme(mediaQuery.matches ? ThemeMode.dark : ThemeMode.light)
- const handleChange = (e: MediaQueryListEvent) => _setTheme(e.matches ? ThemeMode.dark : ThemeMode.light)
- mediaQuery.addEventListener('change', handleChange)
- return () => mediaQuery.removeEventListener('change', handleChange)
- } else {
- _setTheme(theme)
- }
+ useEffect(() => {
+ window.api?.setTheme(defaultTheme || theme)
}, [defaultTheme, theme])
useEffect(() => {
- document.body.setAttribute('theme-mode', _theme)
- // 移除迷你窗口的条件判断,让所有窗口都能设置主题
- window.api?.setTheme(_theme === ThemeMode.dark ? 'dark' : 'light')
- }, [_theme])
+ document.body.setAttribute('theme-mode', effectiveTheme)
+ }, [effectiveTheme])
useEffect(() => {
document.body.setAttribute('os', isMac ? 'mac' : 'windows')
-
- // listen theme change from main process from other windows
- const themeChangeListenerRemover = window.electron.ipcRenderer.on(IpcChannel.ThemeChange, (_, newTheme) => {
- setTheme(newTheme)
- })
+ const themeChangeListenerRemover = window.electron.ipcRenderer.on(
+ IpcChannel.ThemeChange,
+ (_, realTheam: ThemeMode) => {
+ setEffectiveTheme(realTheam)
+ }
+ )
return () => {
themeChangeListenerRemover()
}
})
- return {children}
+ return {children}
}
export const useTheme = () => use(ThemeContext)