diff --git a/src/renderer/components/TopBar.tsx b/src/renderer/components/TopBar.tsx index 5cbc70d..3447505 100644 --- a/src/renderer/components/TopBar.tsx +++ b/src/renderer/components/TopBar.tsx @@ -20,6 +20,7 @@ import CalendarMonthIcon from '@mui/icons-material/CalendarMonth'; import EmojiEventsIcon from '@mui/icons-material/EmojiEvents'; import PersonIcon from '@mui/icons-material/Person'; import SettingsIcon from '@mui/icons-material/Settings'; +import { useTheme } from '@mui/material/styles'; declare global { interface Window { electron: { @@ -48,6 +49,7 @@ export default function TopBar({ onRegister, username }: TopBarProps) { const navigate = useNavigate(); const tabsWrapperRef = useRef(null); const tabsRootRef = useRef(null); + const theme = useTheme(); const updateGradientVars = useCallback(() => { const root = tabsRootRef.current; @@ -71,7 +73,8 @@ export default function TopBar({ onRegister, username }: TopBarProps) { ); const path = location.pathname || ''; - const isAuthPage = path.startsWith('/login') || path.startsWith('/registration'); + const isAuthPage = + path.startsWith('/login') || path.startsWith('/registration'); const TAB_ROUTES: Array<{ value: number; @@ -198,26 +201,7 @@ export default function TopBar({ onRegister, username }: TopBarProps) { // Функция для получения количества монет - const tabBaseSx = { - color: 'white', - fontFamily: 'Benzin-Bold', - fontSize: '0.7em', - transition: 'all 0.3s ease', - '&:hover': { - color: 'rgb(170, 170, 170)', - }, - }; - - const activeTabSx = { - color: 'transparent', - WebkitTextFillColor: 'transparent', - backgroundImage: 'var(--tabs-grad)', - backgroundRepeat: 'no-repeat', - backgroundSize: 'var(--tabs-w) 100%', - backgroundPosition: 'calc(-1 * var(--active-x)) 0', - WebkitBackgroundClip: 'text', - backgroundClip: 'text', - }; + const tabBaseSx = [{ fontSize: '0.7em' }, theme.launcher.topbar.tabBase]; const logout = () => { localStorage.removeItem('launcher_config'); @@ -230,7 +214,7 @@ export default function TopBar({ onRegister, username }: TopBarProps) { return; } - const savedConfig = localStorage.getItem('launcher_config'); + const savedConfig = localStorage.getItem('launcher_config'); if (!savedConfig) return; let cfg: any = null; @@ -263,7 +247,8 @@ export default function TopBar({ onRegister, username }: TopBarProps) { }; window.addEventListener('skin-updated', handler as EventListener); - return () => window.removeEventListener('skin-updated', handler as EventListener); + return () => + window.removeEventListener('skin-updated', handler as EventListener); }, [loadSkin]); useEffect(() => { @@ -277,28 +262,24 @@ export default function TopBar({ onRegister, username }: TopBarProps) { return ( {/* Левая часть */} handleLaunchPage()} - sx={{ - width: '3em', - height: '3em', - borderRadius: '50%', - border: 'unset', - color: 'white', - minWidth: 'unset', - minHeight: 'unset', - transition: 'transform 0.3s ease', - '&:hover': { - transform: 'scale(1.2)', - ...(document.body.classList.contains('reduce-motion') ? { transform: 'none' } : null), + sx={[ + { + width: '3em', + height: '3em', + borderRadius: '50%', + minWidth: 'unset', + minHeight: 'unset', }, - }} + theme.launcher.topbar.backButton, + ]} > @@ -339,7 +316,7 @@ export default function TopBar({ onRegister, username }: TopBarProps) { // старый вариант sx={{ borderBottom: 1, - borderColor: 'transparent', + ...theme.launcher.topbar.tabsBox, // '& .MuiTabs-indicator': { // backgroundColor: 'rgba(255, 77, 77, 1)', // }, @@ -381,61 +358,40 @@ export default function TopBar({ onRegister, username }: TopBarProps) { scrollButtons={false} disableRipple={true} sx={{ - // один градиент на весь Tabs - '--tabs-grad': 'linear-gradient(90deg, #F27121 0%, #E940CD 50%, #8A2387 100%)', - - // активный текст показывает “срез” общего градиента - '& .MuiTab-root.Mui-selected': { - color: 'transparent', - backgroundImage: 'var(--tabs-grad)', - backgroundRepeat: 'no-repeat', - backgroundSize: 'var(--tabs-w) 100%', - backgroundPosition: 'calc(-1 * var(--active-x)) 0', - WebkitBackgroundClip: 'text', - backgroundClip: 'text', - }, - - // подчёркивание тоже из того же “единого” градиента - '& .MuiTabs-indicator': { - height: '2px', - backgroundImage: 'var(--tabs-grad)', - backgroundRepeat: 'no-repeat', - backgroundSize: 'var(--tabs-w) 100%', - backgroundPosition: 'calc(-1 * var(--active-x)) 0', - }, + ...theme.launcher.topbar.tabs, }} > @@ -507,6 +463,7 @@ export default function TopBar({ onRegister, username }: TopBarProps) { width: '3em', height: '3em', borderRadius: '50%', + ...theme.launcher.topbar.windowControlButton, }} > @@ -531,9 +488,12 @@ export default function TopBar({ onRegister, username }: TopBarProps) { width: '3em', height: '3em', borderRadius: '50%', + ...theme.launcher.topbar.windowControlButton, }} > - + @@ -581,10 +537,7 @@ export default function TopBar({ onRegister, username }: TopBarProps) { {username || 'Игрок'} @@ -603,22 +556,17 @@ export default function TopBar({ onRegister, username }: TopBarProps) { - + { handleAvatarMenuClose(); navigate('/profile'); }} - sx={{ - fontFamily: 'Benzin-Bold', - fontSize: '1.5vw', - gap: '0.5vw', - py: '0.7vw', - '&:hover': { - bgcolor: 'rgba(255,77,77,0.15)', - }, - }} + sx={[ + { fontSize: '1.5vw', gap: '0.5vw', py: '0.7vw' }, + theme.launcher.topbar.menuItem, + ]} > Профиль @@ -629,15 +577,10 @@ export default function TopBar({ onRegister, username }: TopBarProps) { handleAvatarMenuClose(); navigate('/dailyquests'); }} - sx={{ - fontFamily: 'Benzin-Bold', - fontSize: '1.5vw', - gap: '0.5vw', - py: '0.7vw', - '&:hover': { - bgcolor: 'rgba(255,77,77,0.15)', - }, - }} + sx={[ + { fontSize: '1.5vw', gap: '0.5vw', py: '0.7vw' }, + theme.launcher.topbar.menuItem, + ]} > Ежедневные задания @@ -648,15 +591,10 @@ export default function TopBar({ onRegister, username }: TopBarProps) { handleAvatarMenuClose(); navigate('/daily'); }} - sx={{ - fontFamily: 'Benzin-Bold', - fontSize: '1.5vw', - gap: '0.5vw', - py: '0.7vw', - '&:hover': { - bgcolor: 'rgba(255,77,77,0.15)', - }, - }} + sx={[ + { fontSize: '1.5vw', gap: '0.5vw', py: '0.7vw' }, + theme.launcher.topbar.menuItem, + ]} > Ежедневная награда @@ -666,20 +604,15 @@ export default function TopBar({ onRegister, username }: TopBarProps) { handleAvatarMenuClose(); navigate('/settings'); }} - sx={{ - fontFamily: 'Benzin-Bold', - fontSize: '1.5vw', - gap: '0.5vw', - py: '0.7vw', - '&:hover': { - bgcolor: 'rgba(255,77,77,0.15)', - }, - }} + sx={[ + { fontSize: '1.5vw', gap: '0.5vw', py: '0.7vw' }, + theme.launcher.topbar.menuItem, + ]} > Настройки - + {!isLoginPage && !isRegistrationPage && username && ( diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index 8864218..e18b0c2 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -1,9 +1,18 @@ import { createRoot } from 'react-dom/client'; import App from './App'; +import { ThemeProvider, CssBaseline } from '@mui/material'; +import { defaultTheme } from '../theme/themes'; // <-- поправь путь, если themes.ts лежит в другом месте + const container = document.getElementById('root') as HTMLElement; const root = createRoot(container); -root.render(); + +root.render( + + + + , +); // calling IPC exposed from preload script window.electron?.ipcRenderer.once('ipc-example', (arg) => { diff --git a/src/theme/themes.ts b/src/theme/themes.ts new file mode 100644 index 0000000..c81b645 --- /dev/null +++ b/src/theme/themes.ts @@ -0,0 +1,176 @@ +import { createTheme, SxProps, Theme } from '@mui/material/styles'; + +declare module '@mui/material/styles' { + interface Theme { + launcher: { + fonts: { + default: string; + }; + + gradients: { + accent: string; + tabs: string; + }; + + topbar: { + firstBox: SxProps; + backButton: SxProps; + tabsBox: { + borderColor: string; + }; + tabs: SxProps; + tabBase: SxProps; + tabActive: SxProps; + menuPaper: SxProps; + menuDivider: SxProps; + menuItem: SxProps; + menuUsername: SxProps; + logoutButton: SxProps; + windowControlIcon: { + color: string; + }; + windowControlButton: SxProps; + }; + }; + } + + interface ThemeOptions { + launcher?: Theme['launcher']; + } +} + +export const defaultTheme = createTheme({ + palette: { + mode: 'dark', + primary: { main: '#F27121' }, + secondary: { main: '#E940CD' }, + text: { + primary: '#FFFFFF', + secondary: 'rgba(255,255,255,0.7)', + }, + }, + + launcher: { + fonts: { + default: 'Benzin-Bold', + }, + + gradients: { + accent: '#F27121 0%, #E940CD 50%, #8A2387 100%', + tabs: 'linear-gradient(71deg, rgba(242,113,33,0.18) 0%, rgba(233,64,205,0.14) 70%, rgba(138,35,135,0.16) 100%)', + }, + + topbar: { + firstBox: (theme: Theme) => ({ + background: theme.launcher.gradients.tabs, + backdropFilter: 'blur(10px)', + boxShadow: '0 8px 30px rgba(0,0,0,0.35)', + }), + backButton: (theme: Theme) => ({ + color: theme.palette.text.primary, + transition: 'transform 0.3s ease', + '&:hover': { + transform: 'scale(1.2)', + }, + border: 'unset', + }), + tabsBox: { + borderColor: 'transparent', + }, + tabs: { + // один градиент на весь Tabs + '--tabs-grad': + 'linear-gradient(90deg, #F27121 0%, #E940CD 50%, #8A2387 100%)', + + // активный текст показывает “срез” общего градиента + '& .MuiTab-root.Mui-selected': { + color: 'transparent', + backgroundImage: 'var(--tabs-grad)', + backgroundRepeat: 'no-repeat', + backgroundSize: 'var(--tabs-w) 100%', + backgroundPosition: 'calc(-1 * var(--active-x)) 0', + WebkitBackgroundClip: 'text', + backgroundClip: 'text', + }, + + // подчёркивание тоже из того же “единого” градиента + '& .MuiTabs-indicator': { + height: '2px', + backgroundImage: 'var(--tabs-grad)', + backgroundRepeat: 'no-repeat', + backgroundSize: 'var(--tabs-w) 100%', + backgroundPosition: 'calc(-1 * var(--active-x)) 0', + }, + }, + tabBase: (theme: Theme) => ({ + color: 'white', + fontFamily: theme.launcher.fonts.default, + transition: 'all 0.3s ease', + '&:hover': { + color: 'rgb(170, 170, 170)', + }, + }), + tabActive: { + color: 'transparent', + WebkitTextFillColor: 'transparent', + backgroundImage: 'var(--tabs-grad)', + backgroundRepeat: 'no-repeat', + backgroundSize: 'var(--tabs-w) 100%', + backgroundPosition: 'calc(-1 * var(--active-x)) 0', + WebkitBackgroundClip: 'text', + backgroundClip: 'text', + }, + menuPaper: { + color: 'white', + bgcolor: 'rgba(0,0,0,0.82)', + backdropFilter: 'blur(10px)', + border: '1px solid rgba(233,64,205,0.25)', + boxShadow: '0 18px 40px rgba(0,0,0,0.55)', + }, + + menuDivider: { + borderColor: 'rgba(255,255,255,0.08)', + }, + + menuItem: (theme: Theme) => ({ + fontFamily: theme.launcher.fonts.default, + '&:hover': { + bgcolor: 'rgba(255,77,77,0.15)', + }, + }), + + menuUsername: (theme: Theme) => ({ + fontFamily: theme.launcher.fonts.default, + color: theme.palette.text.primary, + }), + + logoutButton: (theme: Theme) => ({ + fontFamily: theme.launcher.fonts.default, + borderRadius: '2.5vw', + background: + 'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)', + color: 'white', + border: 'none', + transition: 'transform 0.3s ease', + '&:hover': { + background: + 'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)', + transform: 'scale(1.01)', + boxShadow: '0 4px 15px rgba(242, 113, 33, 0.4)', + }, + boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)', + }), + + windowControlIcon: { + color: 'white', + }, + + windowControlButton: { + // тут только “визуал”, размеры оставим в TopBar + '&:hover': { + bgcolor: 'rgba(255,255,255,0.06)', + }, + }, + }, + }, +});