2 Commits

8 changed files with 230 additions and 152 deletions

View File

@ -12,19 +12,52 @@
url('../../assets/fonts/benzin-bold.svg#benzin-bold') format('svg'); /* Chrome < 4, Legacy iOS */
}
:root {
--ui-scale: 1;
/* SETTINGS NO-BLUR */
.glass {
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
}
#root {
transform: scale(var(--ui-scale));
transform-origin: top left;
/* компенсация, чтобы после scale не появлялись пустые области/скроллы */
width: calc(100% / var(--ui-scale));
height: calc(100% / var(--ui-scale));
.glass-ui {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.glass--soft { backdrop-filter: blur(6px); }
.glass--hard { backdrop-filter: blur(20px); }
body.no-blur .glass,
body.no-blur .glass-ui {
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
/* SETTINGS NO-BLUR */
/* SETTINGS REDUCE-MOTION */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
body.reduce-motion *,
body.reduce-motion *::before,
body.reduce-motion *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important;
}
/* опционально: убрать ховер-скейлы (если ты их часто используешь) */
body.reduce-motion .no-motion-hover,
body.reduce-motion .no-motion-hover:hover {
transform: none !important;
}
/* SETTINGS REDUCE-MOTION */
body {
position: relative;
color: white;

View File

@ -108,6 +108,46 @@ const App = () => {
};
const AppLayout = () => {
useEffect(() => {
const applySettings = () => {
try {
const raw = localStorage.getItem('launcher_settings');
if (!raw) return;
const settings = JSON.parse(raw);
document.body.classList.toggle(
'reduce-motion',
Boolean(settings.reduceMotion),
);
document.body.classList.toggle(
'no-blur',
settings.blurEffects === false,
);
const ui = document.getElementById('app-ui');
if (ui && typeof settings.uiScale === 'number') {
const scale = settings.uiScale / 100;
ui.style.transform = `scale(${scale})`;
ui.style.transformOrigin = 'top left';
ui.style.width = `${100 / scale}%`;
ui.style.height = `${100 / scale}%`;
}
} catch (e) {
console.error('Failed to apply UI settings', e);
}
};
// применяем при загрузке
applySettings();
// применяем после сохранения настроек
window.addEventListener('settings-updated', applySettings);
return () => window.removeEventListener('settings-updated', applySettings);
}, []);
// Просто используйте window.open без useNavigate
const handleRegister = () => {
window.open('https://account.ely.by/register', '_blank');
@ -127,21 +167,44 @@ const AppLayout = () => {
}, []);
return (
<Box sx={{ width: '100vw', height: '100vh', position: 'relative', overflow: 'hidden' }}>
{/* ФОН — НЕ масштабируется */}
<Box sx={{ position: 'fixed', inset: 0, zIndex: 0, pointerEvents: 'none', }}>
<MinecraftBackground />
</Box>
{/* UI — масштабируется */}
<Box
id="app-scroll"
sx={{
position: 'relative',
zIndex: 1,
height: '100%',
width: '100%',
overflowY: 'hidden',
overflowX: 'hidden',
}}
>
<Box
id="app-ui"
sx={{
position: 'relative',
zIndex: 1,
height: '100vh',
width: '100vw',
position: 'relative',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: location.pathname === '/profile' || location.pathname.startsWith('/launch') || location.pathname === '/login' || location.pathname === '/registration'
justifyContent:
path === '/profile' ||
path.startsWith('/launch') ||
path === '/login' ||
path === '/registration'
? 'center'
: 'flex-start',
overflowX: 'hidden',
}}
>
<MinecraftBackground />
<TopBar onRegister={handleRegister} username={username || ''} />
<PageHeader />
<Notifier />
@ -226,6 +289,8 @@ const AppLayout = () => {
/>
</Routes>
</Box>
</Box>
</Box>
);
};

View File

@ -39,13 +39,12 @@ export const FullScreenLoader = ({
{fullScreen && (
<Fade in timeout={220} appear>
<Box
className="glass-ui"
sx={{
position: 'absolute',
inset: 0,
background:
'radial-gradient(circle at 15% 20%, rgba(242,113,33,0.15), transparent 60%), radial-gradient(circle at 85% 10%, rgba(233,64,205,0.12), transparent 55%), rgba(5,5,10,0.75)',
backdropFilter: 'blur(14px)',
WebkitBackdropFilter: 'blur(14px)',
}}
/>
</Fade>
@ -61,7 +60,9 @@ export const FullScreenLoader = ({
alignItems: 'center',
gap: 3,
// небольшой "подъём" при появлении
animation: 'popIn 260ms ease-out both',
animation: document.body.classList.contains('reduce-motion')
? 'none'
: 'popIn 260ms ease-out both',
'@keyframes popIn': {
from: { opacity: 0, transform: 'translateY(8px) scale(0.98)' },
to: { opacity: 1, transform: 'translateY(0) scale(1)' },
@ -76,7 +77,9 @@ export const FullScreenLoader = ({
position: 'relative',
overflow: 'hidden',
background: 'conic-gradient(#F27121, #E940CD, #8A2387, #F27121)',
animation: 'spin 1s linear infinite',
animation: document.body.classList.contains('reduce-motion')
? 'none'
: 'spin 1s linear infinite',
WebkitMask: 'radial-gradient(circle, transparent 55%, black 56%)',
mask: 'radial-gradient(circle, transparent 55%, black 56%)',
'@keyframes spin': {

View File

@ -251,8 +251,18 @@ export default function TopBar({ onRegister, username }: TopBarProps) {
window.removeEventListener('skin-updated', handler as EventListener);
}, [loadSkin]);
useEffect(() => {
const handler = () => {
requestAnimationFrame(updateGradientVars);
};
window.addEventListener('settings-updated', handler as EventListener);
return () => window.removeEventListener('settings-updated', handler as EventListener);
}, [updateGradientVars]);
return (
<Box
className={isAuthPage ? undefined : 'glass-ui'}
sx={[
{
display: 'flex',

View File

@ -153,11 +153,11 @@ export const News = () => {
sx={{
px: '7vw',
pb: '4vh',
maxHeight: '100vh',
overflowY: 'auto',
display: 'flex',
flexDirection: 'column',
gap: '2vh',
width: '85%',
}}
>
{/* Админский редактор */}
@ -353,7 +353,6 @@ export const News = () => {
border: '1px solid rgba(255, 255, 255, 0)',
boxShadow: '0 18px 45px rgba(0, 0, 0, 0.7)',
backdropFilter: 'blur(14px)',
width: '80vw',
// transition:
// 'transform 0.25s ease, box-shadow 0.25s.ease, border-color 0.25s ease',
'&:hover': {

View File

@ -54,7 +54,7 @@ const defaultSettings: SettingsState = {
walkingSpeed: 0.5,
notifications: true,
notificationPosition: 'top-right',
notificationPosition: 'bottom-center',
};
function safeParseSettings(raw: string | null): SettingsState | null {
@ -229,13 +229,10 @@ const Settings = () => {
};
useEffect(() => {
if (typeof document === 'undefined') return;
const scale = settings.uiScale / 100;
document.documentElement.style.setProperty('--ui-scale', String(scale));
// motion / blur классы — глобально на body
document.body.classList.toggle('reduce-motion', settings.reduceMotion);
document.body.classList.toggle('no-blur', !settings.blurEffects);
}, [settings.uiScale, settings.reduceMotion, settings.blurEffects]);
}, [settings.reduceMotion, settings.blurEffects]);
const SectionTitle = ({ children }: { children: string }) => (
<Typography
@ -255,6 +252,7 @@ const Settings = () => {
const Glass = ({ children }: { children: React.ReactNode }) => (
<Paper
className="glass glass--soft"
elevation={0}
sx={{
borderRadius: '1.2vw',
@ -263,7 +261,6 @@ const Settings = () => {
'radial-gradient(circle at 10% 10%, rgba(242,113,33,0.14), transparent 55%), radial-gradient(circle at 90% 20%, rgba(233,64,205,0.12), transparent 55%), rgba(10,10,20,0.86)',
border: '1px solid rgba(255,255,255,0.08)',
boxShadow: '0 1.2vw 3.2vw rgba(0,0,0,0.55)',
backdropFilter: 'blur(14px)',
color: 'white',
}}
>
@ -287,6 +284,10 @@ const Settings = () => {
},
} as const;
useEffect(() => {
document.body.classList.toggle('no-blur', !settings.blurEffects);
}, [settings.blurEffects]);
return (
<Box
sx={{

View File

@ -289,6 +289,7 @@ export const VersionsExplorer = () => {
flexDirection: 'column',
gap: '2vh',
height: '100%',
width: '85%',
}}
>
{loading ? (

View File

@ -1,34 +0,0 @@
import type { NotificationPosition } from '../components/Notifications/CustomNotification';
export type LauncherSettings = {
notificationPosition?: 'top-right' | 'top-center' | 'top-left' | 'bottom-right' | 'bottom-center' | 'bottom-left';
};
export function getLauncherSettings(): LauncherSettings {
try {
return JSON.parse(localStorage.getItem('launcher_settings') || '{}');
} catch {
return {};
}
}
export function getNotificationPosition(): NotificationPosition {
const { notificationPosition } = getLauncherSettings();
switch (notificationPosition) {
case 'top-right':
return { vertical: 'top', horizontal: 'right' };
case 'top-center':
return { vertical: 'top', horizontal: 'center' };
case 'top-left':
return { vertical: 'top', horizontal: 'left' };
case 'bottom-right':
return { vertical: 'bottom', horizontal: 'right' };
case 'bottom-center':
return { vertical: 'bottom', horizontal: 'center' };
case 'bottom-left':
return { vertical: 'bottom', horizontal: 'left' };
default:
return { vertical: 'bottom', horizontal: 'center' };
}
}