支持系统深浅自适应同步

This commit is contained in:
cc
2026-02-21 23:23:40 +08:00
parent 5ab0466a87
commit acaac507b1
4 changed files with 39 additions and 15 deletions

View File

@@ -25,7 +25,7 @@ import NotificationWindow from './pages/NotificationWindow'
import AIChatPage from './pages/AIChatPage' import AIChatPage from './pages/AIChatPage'
import { useAppStore } from './stores/appStore' import { useAppStore } from './stores/appStore'
import { themes, useThemeStore, type ThemeId } from './stores/themeStore' import { themes, useThemeStore, type ThemeId, type ThemeMode } from './stores/themeStore'
import * as configService from './services/config' import * as configService from './services/config'
import { Download, X, Shield } from 'lucide-react' import { Download, X, Shield } from 'lucide-react'
import './App.scss' import './App.scss'
@@ -101,14 +101,27 @@ function App() {
// 应用主题 // 应用主题
useEffect(() => { useEffect(() => {
document.documentElement.setAttribute('data-theme', currentTheme) const mq = window.matchMedia('(prefers-color-scheme: dark)')
document.documentElement.setAttribute('data-mode', themeMode) const applyMode = (mode: ThemeMode, systemDark?: boolean) => {
const effectiveMode = mode === 'system' ? (systemDark ?? mq.matches ? 'dark' : 'light') : mode
// 更新窗口控件颜色以适配主题 document.documentElement.setAttribute('data-theme', currentTheme)
const symbolColor = themeMode === 'dark' ? '#ffffff' : '#1a1a1a' document.documentElement.setAttribute('data-mode', effectiveMode)
if (!isOnboardingWindow && !isNotificationWindow) { const symbolColor = effectiveMode === 'dark' ? '#ffffff' : '#1a1a1a'
window.electronAPI.window.setTitleBarOverlay({ symbolColor }) if (!isOnboardingWindow && !isNotificationWindow) {
window.electronAPI.window.setTitleBarOverlay({ symbolColor })
}
} }
applyMode(themeMode)
// 监听系统主题变化
const handler = (e: MediaQueryListEvent) => {
if (useThemeStore.getState().themeMode === 'system') {
applyMode('system', e.matches)
}
}
mq.addEventListener('change', handler)
return () => mq.removeEventListener('change', handler)
}, [currentTheme, themeMode, isOnboardingWindow, isNotificationWindow]) }, [currentTheme, themeMode, isOnboardingWindow, isNotificationWindow])
// 读取已保存的主题设置 // 读取已保存的主题设置
@@ -122,7 +135,7 @@ function App() {
if (savedThemeId && themes.some((theme) => theme.id === savedThemeId)) { if (savedThemeId && themes.some((theme) => theme.id === savedThemeId)) {
setTheme(savedThemeId as ThemeId) setTheme(savedThemeId as ThemeId)
} }
if (savedThemeMode === 'light' || savedThemeMode === 'dark') { if (savedThemeMode === 'light' || savedThemeMode === 'dark' || savedThemeMode === 'system') {
setThemeMode(savedThemeMode) setThemeMode(savedThemeMode)
} }
} catch (e) { } catch (e) {

View File

@@ -7,7 +7,7 @@ import { dialog } from '../services/ipc'
import * as configService from '../services/config' import * as configService from '../services/config'
import { import {
Eye, EyeOff, FolderSearch, FolderOpen, Search, Copy, Eye, EyeOff, FolderSearch, FolderOpen, Search, Copy,
RotateCcw, Trash2, Plug, Check, Sun, Moon, RotateCcw, Trash2, Plug, Check, Sun, Moon, Monitor,
Palette, Database, Download, HardDrive, Info, RefreshCw, ChevronDown, Mic, Palette, Database, Download, HardDrive, Info, RefreshCw, ChevronDown, Mic,
ShieldCheck, Fingerprint, Lock, KeyRound, Bell, Globe, BarChart2 ShieldCheck, Fingerprint, Lock, KeyRound, Bell, Globe, BarChart2
} from 'lucide-react' } from 'lucide-react'
@@ -55,6 +55,14 @@ function SettingsPage() {
const resetChatStore = useChatStore((state) => state.reset) const resetChatStore = useChatStore((state) => state.reset)
const { currentTheme, themeMode, setTheme, setThemeMode } = useThemeStore() const { currentTheme, themeMode, setTheme, setThemeMode } = useThemeStore()
const [systemDark, setSystemDark] = useState(() => window.matchMedia('(prefers-color-scheme: dark)').matches)
useEffect(() => {
const mq = window.matchMedia('(prefers-color-scheme: dark)')
const handler = (e: MediaQueryListEvent) => setSystemDark(e.matches)
mq.addEventListener('change', handler)
return () => mq.removeEventListener('change', handler)
}, [])
const effectiveMode = themeMode === 'system' ? (systemDark ? 'dark' : 'light') : themeMode
const clearAnalyticsStoreCache = useAnalyticsStore((state) => state.clearCache) const clearAnalyticsStoreCache = useAnalyticsStore((state) => state.clearCache)
const [activeTab, setActiveTab] = useState<SettingsTab>('appearance') const [activeTab, setActiveTab] = useState<SettingsTab>('appearance')
@@ -993,11 +1001,14 @@ function SettingsPage() {
<button className={`mode-btn ${themeMode === 'dark' ? 'active' : ''}`} onClick={() => setThemeMode('dark')}> <button className={`mode-btn ${themeMode === 'dark' ? 'active' : ''}`} onClick={() => setThemeMode('dark')}>
<Moon size={16} /> <Moon size={16} />
</button> </button>
<button className={`mode-btn ${themeMode === 'system' ? 'active' : ''}`} onClick={() => setThemeMode('system')}>
<Monitor size={16} />
</button>
</div> </div>
<div className="theme-grid"> <div className="theme-grid">
{themes.map((theme) => ( {themes.map((theme) => (
<div key={theme.id} className={`theme-card ${currentTheme === theme.id ? 'active' : ''}`} onClick={() => setTheme(theme.id)}> <div key={theme.id} className={`theme-card ${currentTheme === theme.id ? 'active' : ''}`} onClick={() => setTheme(theme.id)}>
<div className="theme-preview" style={{ background: themeMode === 'dark' ? 'linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%)' : `linear-gradient(135deg, ${theme.bgColor} 0%, ${theme.bgColor}dd 100%)` }}> <div className="theme-preview" style={{ background: effectiveMode === 'dark' ? 'linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 100%)' : `linear-gradient(135deg, ${theme.bgColor} 0%, ${theme.bgColor}dd 100%)` }}>
<div className="theme-accent" style={{ background: theme.primaryColor }} /> <div className="theme-accent" style={{ background: theme.primaryColor }} />
</div> </div>
<div className="theme-info"> <div className="theme-info">

View File

@@ -118,13 +118,13 @@ export async function setWxidConfig(wxid: string, configValue: WxidConfig): Prom
} }
// 获取主题 // 获取主题
export async function getTheme(): Promise<'light' | 'dark'> { export async function getTheme(): Promise<'light' | 'dark' | 'system'> {
const value = await config.get(CONFIG_KEYS.THEME) const value = await config.get(CONFIG_KEYS.THEME)
return (value as 'light' | 'dark') || 'light' return (value as 'light' | 'dark' | 'system') || 'light'
} }
// 设置主题 // 设置主题
export async function setTheme(theme: 'light' | 'dark'): Promise<void> { export async function setTheme(theme: 'light' | 'dark' | 'system'): Promise<void> {
await config.set(CONFIG_KEYS.THEME, theme) await config.set(CONFIG_KEYS.THEME, theme)
} }

View File

@@ -2,7 +2,7 @@ import { create } from 'zustand'
import { persist } from 'zustand/middleware' import { persist } from 'zustand/middleware'
export type ThemeId = 'cloud-dancer' | 'corundum-blue' | 'kiwi-green' | 'spicy-red' | 'teal-water' export type ThemeId = 'cloud-dancer' | 'corundum-blue' | 'kiwi-green' | 'spicy-red' | 'teal-water'
export type ThemeMode = 'light' | 'dark' export type ThemeMode = 'light' | 'dark' | 'system'
export interface ThemeInfo { export interface ThemeInfo {
id: ThemeId id: ThemeId