This commit is contained in:
90
frontend/src/theme/SteamTheme.ts
Normal file
90
frontend/src/theme/SteamTheme.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
import type { ThemeOptions } from '@mui/material/styles';
|
||||
|
||||
export type ThemeType = 'steam' | 'mocha' | 'latte' | 'nord' | 'tokyo';
|
||||
|
||||
const commonSettings: ThemeOptions = {
|
||||
typography: {
|
||||
fontFamily: '"Motiva Sans", "Roboto", "Helvetica", "Arial", sans-serif',
|
||||
h6: { fontWeight: 700, letterSpacing: '0.5px' },
|
||||
button: { textTransform: 'none', fontWeight: 600 },
|
||||
},
|
||||
shape: { borderRadius: 4 },
|
||||
components: {
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: { borderRadius: 2, boxShadow: 'none', '&:hover': { boxShadow: 'none' } },
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: { backgroundImage: 'none' },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const themes: Record<ThemeType, ThemeOptions> = {
|
||||
steam: {
|
||||
...commonSettings,
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
primary: { main: '#66c0f4', dark: '#1a9fff' },
|
||||
secondary: { main: '#4c6b22', dark: '#3d541b' },
|
||||
background: { default: '#171a21', paper: '#1b2838' },
|
||||
text: { primary: '#ffffff', secondary: '#8f98a0' },
|
||||
success: { main: '#a3cf06' },
|
||||
error: { main: '#ff4c4c' },
|
||||
},
|
||||
},
|
||||
mocha: {
|
||||
...commonSettings,
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
primary: { main: '#89b4fa', dark: '#74c7ec' },
|
||||
secondary: { main: '#a6e3a1', dark: '#94e2d5' },
|
||||
background: { default: '#11111b', paper: '#1e1e2e' },
|
||||
text: { primary: '#cdd6f4', secondary: '#9399b2' },
|
||||
success: { main: '#a6e3a1' },
|
||||
error: { main: '#f38ba8' },
|
||||
},
|
||||
},
|
||||
latte: {
|
||||
...commonSettings,
|
||||
palette: {
|
||||
mode: 'light',
|
||||
primary: { main: '#1e66f5', dark: '#179299' },
|
||||
secondary: { main: '#40a02b', dark: '#40a02b' },
|
||||
background: { default: '#eff1f5', paper: '#e6e9ef' },
|
||||
text: { primary: '#4c4f69', secondary: '#6c6f85' },
|
||||
success: { main: '#40a02b' },
|
||||
error: { main: '#d20f39' },
|
||||
},
|
||||
},
|
||||
nord: {
|
||||
...commonSettings,
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
primary: { main: '#88c0d0', dark: '#81a1c1' },
|
||||
secondary: { main: '#a3be8c', dark: '#8fbcbb' },
|
||||
background: { default: '#2e3440', paper: '#3b4252' },
|
||||
text: { primary: '#eceff4', secondary: '#d8dee9' },
|
||||
success: { main: '#a3be8c' },
|
||||
error: { main: '#bf616a' },
|
||||
},
|
||||
},
|
||||
tokyo: {
|
||||
...commonSettings,
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
primary: { main: '#7aa2f7', dark: '#3d59a1' },
|
||||
secondary: { main: '#9ece6a', dark: '#73daca' },
|
||||
background: { default: '#1a1b26', paper: '#24283b' },
|
||||
text: { primary: '#c0caf5', secondary: '#9aa5ce' },
|
||||
success: { main: '#9ece6a' },
|
||||
error: { main: '#f7768e' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const getTheme = (type: ThemeType) => createTheme(themes[type]);
|
||||
52
frontend/src/theme/ThemeContext.tsx
Normal file
52
frontend/src/theme/ThemeContext.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import React, { createContext, useContext, useState, useEffect, useMemo } from 'react';
|
||||
import { ThemeProvider, CssBaseline } from '@mui/material';
|
||||
import { getTheme } from './SteamTheme';
|
||||
import type { ThemeType } from './SteamTheme';
|
||||
|
||||
interface ThemeContextType {
|
||||
currentTheme: ThemeType;
|
||||
setTheme: (theme: ThemeType) => void;
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
||||
|
||||
export const AppThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [currentTheme, setCurrentTheme] = useState<ThemeType>('steam');
|
||||
|
||||
useEffect(() => {
|
||||
// Load theme from store on startup
|
||||
const loadTheme = async () => {
|
||||
const api = (window as any).electronAPI;
|
||||
if (api?.getServerConfig) {
|
||||
const config = await api.getServerConfig();
|
||||
if (config?.theme) setCurrentTheme(config.theme);
|
||||
}
|
||||
};
|
||||
loadTheme();
|
||||
}, []);
|
||||
|
||||
const setTheme = async (theme: ThemeType) => {
|
||||
setCurrentTheme(theme);
|
||||
const api = (window as any).electronAPI;
|
||||
if (api?.updateServerConfig) {
|
||||
await api.updateServerConfig({ theme });
|
||||
}
|
||||
};
|
||||
|
||||
const theme = useMemo(() => getTheme(currentTheme), [currentTheme]);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ currentTheme, setTheme }}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAppTheme = () => {
|
||||
const context = useContext(ThemeContext);
|
||||
if (!context) throw new Error('useAppTheme must be used within AppThemeProvider');
|
||||
return context;
|
||||
};
|
||||
Reference in New Issue
Block a user