From 5f7df33469014adcb3686b5f850183469a02d577 Mon Sep 17 00:00:00 2001 From: XYenon Date: Wed, 24 Dec 2025 00:02:59 +0800 Subject: [PATCH 1/3] feat: add auto theme mode (follow system preference) - Add 'auto' to Theme type - Implement cycleTheme (light -> dark -> auto) - Add autoTheme icon (sun with half-filled center) - Listen to system theme changes in auto mode Also includes some Prettier formatting fixes. --- src/components/layout/MainLayout.tsx | 125 ++++++++++++++++++++------- src/stores/useThemeStore.ts | 67 +++++++------- src/types/common.ts | 2 +- 3 files changed, 134 insertions(+), 60 deletions(-) diff --git a/src/components/layout/MainLayout.tsx b/src/components/layout/MainLayout.tsx index 0481d97..28f2b01 100644 --- a/src/components/layout/MainLayout.tsx +++ b/src/components/layout/MainLayout.tsx @@ -1,4 +1,12 @@ -import { ReactNode, SVGProps, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; +import { + ReactNode, + SVGProps, + useCallback, + useEffect, + useLayoutEffect, + useRef, + useState, +} from 'react'; import { NavLink, Outlet } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/Button'; @@ -14,10 +22,16 @@ import { IconScrollText, IconSettings, IconShield, - IconSlidersHorizontal + IconSlidersHorizontal, } from '@/components/ui/icons'; import { INLINE_LOGO_JPEG } from '@/assets/logoInline'; -import { useAuthStore, useConfigStore, useLanguageStore, useNotificationStore, useThemeStore } from '@/stores'; +import { + useAuthStore, + useConfigStore, + useLanguageStore, + useNotificationStore, + useThemeStore, +} from '@/stores'; import { configApi, versionApi } from '@/services/api'; const sidebarIcons: Record = { @@ -30,7 +44,7 @@ const sidebarIcons: Record = { usage: , config: , logs: , - system: + system: , }; // Header action icons - smaller size for header buttons @@ -44,7 +58,7 @@ const headerIconProps: SVGProps = { strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': 'true', - focusable: 'false' + focusable: 'false', }; const headerIcons = { @@ -97,19 +111,38 @@ const headerIcons = { ), - moon: ( - - - - ), - logout: ( - - - - - - ) - }; + moon: ( + + + + ), + autoTheme: ( + + + + + + + + + + + + + + + + + + ), + logout: ( + + + + + + ), +}; const parseVersionSegments = (version?: string | null) => { if (!version) return null; @@ -153,7 +186,7 @@ export function MainLayout() { const updateConfigValue = useConfigStore((state) => state.updateConfigValue); const theme = useThemeStore((state) => state.theme); - const toggleTheme = useThemeStore((state) => state.toggleTheme); + const cycleTheme = useThemeStore((state) => state.cycleTheme); const toggleLanguage = useLanguageStore((state) => state.toggleLanguage); const [sidebarOpen, setSidebarOpen] = useState(false); @@ -187,7 +220,9 @@ export function MainLayout() { updateHeaderHeight(); const resizeObserver = - typeof ResizeObserver !== 'undefined' && headerRef.current ? new ResizeObserver(updateHeaderHeight) : null; + typeof ResizeObserver !== 'undefined' && headerRef.current + ? new ResizeObserver(updateHeaderHeight) + : null; if (resizeObserver && headerRef.current) { resizeObserver.observe(headerRef.current); } @@ -320,8 +355,10 @@ export function MainLayout() { { path: '/oauth', label: t('nav.oauth', { defaultValue: 'OAuth' }), icon: sidebarIcons.oauth }, { path: '/usage', label: t('nav.usage_stats'), icon: sidebarIcons.usage }, { path: '/config', label: t('nav.config_management'), icon: sidebarIcons.config }, - ...(config?.loggingToFile ? [{ path: '/logs', label: t('nav.logs'), icon: sidebarIcons.logs }] : []), - { path: '/system', label: t('nav.system_info'), icon: sidebarIcons.system } + ...(config?.loggingToFile + ? [{ path: '/logs', label: t('nav.logs'), icon: sidebarIcons.logs }] + : []), + { path: '/system', label: t('nav.system_info'), icon: sidebarIcons.system }, ]; const handleRefreshAll = async () => { @@ -370,7 +407,11 @@ export function MainLayout() { @@ -400,20 +441,40 @@ export function MainLayout() {
- - - -