import { Paper, ThemeProvider } from '@mui/material' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { Outlet, useNavigate } from 'react-router' import { BaseErrorBoundary } from '@/components/base' import { LayoutSidebar } from '@/components/layout/layout-sidebar' import { NoticeManager } from '@/components/layout/notice-manager' import { ServiceMigrationDialog } from '@/components/layout/service-migration-dialog' import { SysproxyPrivilegeDialog } from '@/components/layout/sysproxy-privilege-dialog' import { WindowControls, WindowResizeHandles, } from '@/components/layout/window-controller' import { useI18n } from '@/hooks/use-i18n' import { useVerge } from '@/hooks/use-verge' import { useWindowDecorations } from '@/hooks/use-window' import { useEventBus } from '@/services/bus' import { handleNoticeMessage } from '@/services/notice-handlers' import { useThemeMode } from '@/services/states' import getSystem from '@/utils/get-system' import { useCustomTheme, useLoadingOverlay, usePendingFailures, } from './_layout/hooks' import 'dayjs/locale/ru' import 'dayjs/locale/zh-cn' dayjs.extend(relativeTime) const OS = getSystem() const Layout = () => { const mode = useThemeMode() const isDark = mode !== 'light' const { t } = useTranslation() const { theme } = useCustomTheme() const { verge } = useVerge() const { language } = verge ?? {} const navCollapsed = verge?.collapse_navbar ?? false const { switchLanguage } = useI18n() const navigate = useNavigate() const themeReady = useMemo(() => Boolean(theme), [theme]) const windowControlsRef = useRef(null) const { decorated } = useWindowDecorations() const customTitlebar = useMemo( () => decorated === false ? (
) : null, [decorated], ) useLoadingOverlay(themeReady) const handleNotice = useCallback( (payload: [string, string]) => { const [status, msg] = payload try { handleNoticeMessage(status, msg, t, navigate) } catch (error) { console.error('[通知处理] 失败:', error) } }, [t, navigate], ) useEventBus(handleNotice) usePendingFailures() useEffect(() => { if (language) { dayjs.locale(language === 'zh' ? 'zh-cn' : language) switchLanguage(language) } }, [language, switchLanguage]) if (!themeReady) { return (
) } return ( {/* 左侧底部窗口控制按钮 */}
{ if ( OS === 'windows' && !['input', 'textarea'].includes( e.currentTarget.tagName.toLowerCase(), ) && !e.currentTarget.isContentEditable ) { e.preventDefault() } }} sx={[ ({ palette }) => ({ bgcolor: palette.background.paper }), OS === 'linux' ? { borderRadius: '8px', width: '100vw', height: '100vh', } : {}, ]} > {decorated === false && } {/* Custom titlebar - rendered only when decorated is false, memoized for performance */} {customTitlebar}
) } export default Layout