feat: 更新嵌入式文件系统逻辑,支持路径清理和静态资源请求处理,增强前端主题切换能力

This commit is contained in:
ryan
2026-03-11 13:18:32 +08:00
parent 6eea676f8f
commit ad090c9c04
26 changed files with 465 additions and 81 deletions
@@ -3,6 +3,8 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { type ReactNode, useState } from 'react';
import { ThemeProvider } from '@/components/providers/theme-provider';
interface AppProvidersProps {
children: ReactNode;
}
@@ -24,5 +26,9 @@ export function AppProviders({ children }: AppProvidersProps) {
}),
);
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
return (
<ThemeProvider>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</ThemeProvider>
);
}
@@ -0,0 +1,107 @@
'use client';
import {
createContext,
type ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import {
isThemeMode,
resolveTheme,
THEME_MEDIA_QUERY,
THEME_STORAGE_KEY,
type ResolvedTheme,
type ThemeMode,
} from '@/lib/theme/theme';
interface ThemeContextValue {
themeMode: ThemeMode;
resolvedTheme: ResolvedTheme;
setThemeMode: (mode: ThemeMode) => void;
}
const ThemeContext = createContext<ThemeContextValue | null>(null);
interface ThemeProviderProps {
children: ReactNode;
}
function getSystemPreference() {
return window.matchMedia(THEME_MEDIA_QUERY).matches;
}
function applyTheme(mode: ThemeMode) {
const resolvedTheme = resolveTheme(mode, getSystemPreference());
const root = document.documentElement;
root.dataset.themeMode = mode;
root.dataset.theme = resolvedTheme;
root.style.colorScheme = resolvedTheme;
window.localStorage.setItem(THEME_STORAGE_KEY, mode);
return resolvedTheme;
}
export function ThemeProvider({ children }: ThemeProviderProps) {
const [themeMode, setThemeModeState] = useState<ThemeMode>('system');
const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>('dark');
useEffect(() => {
const root = document.documentElement;
const domThemeMode = root.dataset.themeMode;
const nextThemeMode = isThemeMode(domThemeMode) ? domThemeMode : 'system';
const nextResolvedTheme = applyTheme(nextThemeMode);
setThemeModeState(nextThemeMode);
setResolvedTheme(nextResolvedTheme);
}, []);
useEffect(() => {
const mediaQuery = window.matchMedia(THEME_MEDIA_QUERY);
const handleChange = () => {
if (themeMode !== 'system') {
return;
}
setResolvedTheme(applyTheme('system'));
};
mediaQuery.addEventListener('change', handleChange);
return () => {
mediaQuery.removeEventListener('change', handleChange);
};
}, [themeMode]);
const setThemeMode = useCallback((mode: ThemeMode) => {
setThemeModeState(mode);
setResolvedTheme(applyTheme(mode));
}, []);
const value = useMemo(
() => ({
themeMode,
resolvedTheme,
setThemeMode,
}),
[resolvedTheme, setThemeMode, themeMode],
);
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}