72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
'use client';
|
|
|
|
import React, {
|
|
createContext,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
} from 'react';
|
|
import { ThemeProvider, createTheme, CssBaseline } from '@mui/material';
|
|
import getDesignTokens from '../config/theme';
|
|
|
|
type Mode = 'light' | 'dark';
|
|
|
|
interface ColorModeContextValue {
|
|
mode: Mode;
|
|
toggleColorMode: () => void;
|
|
setColorMode: (mode: Mode) => void;
|
|
}
|
|
|
|
const STORAGE_KEY = 'patchpilot:color-mode';
|
|
|
|
const ColorModeContext = createContext<ColorModeContextValue>({
|
|
mode: 'light',
|
|
toggleColorMode: () => {},
|
|
setColorMode: () => {},
|
|
});
|
|
|
|
export const useColorMode = () => useContext(ColorModeContext);
|
|
|
|
function detectInitialMode(): Mode {
|
|
if (typeof window === 'undefined') return 'light';
|
|
const stored = window.localStorage.getItem(STORAGE_KEY);
|
|
if (stored === 'light' || stored === 'dark') return stored;
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
}
|
|
|
|
export const ColorModeProvider = ({ children }: { children: React.ReactNode }) => {
|
|
const [mode, setMode] = useState<Mode>('light');
|
|
const [hydrated, setHydrated] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setMode(detectInitialMode());
|
|
setHydrated(true);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!hydrated) return;
|
|
window.localStorage.setItem(STORAGE_KEY, mode);
|
|
}, [mode, hydrated]);
|
|
|
|
const value = useMemo<ColorModeContextValue>(
|
|
() => ({
|
|
mode,
|
|
toggleColorMode: () => setMode((p) => (p === 'light' ? 'dark' : 'light')),
|
|
setColorMode: (next) => setMode(next),
|
|
}),
|
|
[mode]
|
|
);
|
|
|
|
const theme = useMemo(() => createTheme(getDesignTokens(mode)), [mode]);
|
|
|
|
return (
|
|
<ColorModeContext.Provider value={value}>
|
|
<ThemeProvider theme={theme}>
|
|
<CssBaseline />
|
|
{children}
|
|
</ThemeProvider>
|
|
</ColorModeContext.Provider>
|
|
);
|
|
};
|