import { type PropsWithChildren, useMemo } from 'react'
import CssBaseline from '@mui/material/CssBaseline'
import { alpha, ThemeProvider, createTheme } from '@mui/material/styles'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ThemeControllerProvider, useThemeController } from '@/app/providers/theme-controller'
import { SseProvider } from './SseProvider'
function AppThemeInner({ children }: PropsWithChildren) {
const controller = useThemeController()
const isDark = controller.resolvedMode === 'dark'
const theme = useMemo(
() =>
createTheme({
palette: (() => {
const common = { mode: controller.resolvedMode }
const text = isDark
? { primary: '#F2F2F2', secondary: 'rgba(242,242,242,0.72)', disabled: 'rgba(242,242,242,0.48)' }
: { primary: '#1F1B16', secondary: 'rgba(31,27,22,0.72)', disabled: 'rgba(31,27,22,0.48)' }
const chip = isDark ? { default: '#0E1510', paper: '#121B14' } : { default: '#F6FAF6', paper: '#FFFFFF' }
switch (controller.scheme) {
case 'forest':
return {
...common,
primary: { main: isDark ? '#8FBC8F' : '#2E8B57' },
secondary: { main: isDark ? '#CD853F' : '#8B4513' },
info: { main: isDark ? '#4682B4' : '#1E90FF' },
success: { main: isDark ? '#90EE90' : '#32CD32' },
warning: { main: isDark ? '#FFD700' : '#FFA500' },
error: { main: isDark ? '#F08080' : '#CD5C5C' },
divider: isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)',
text,
chip,
background: isDark
? { default: '#0F1720', paper: '#1A242E' }
: { default: '#F8F6F3', paper: '#FFFFFF' },
}
case 'ocean':
return {
...common,
primary: { main: isDark ? '#5F9EA0' : '#20B2AA' },
secondary: { main: isDark ? '#7B68EE' : '#6A5ACD' },
info: { main: isDark ? '#87CEEB' : '#00BFFF' },
success: { main: isDark ? '#98FB98' : '#00FA9A' },
warning: { main: isDark ? '#FFE4B5' : '#FFDAB9' },
error: { main: isDark ? '#FF6347' : '#FF4500' },
divider: isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)',
text,
chip,
background: isDark
? { default: '#0A1A2A', paper: '#0F1D35' }
: { default: '#F0F8FF', paper: '#FFFFFF' },
}
case 'berry':
return {
...common,
primary: { main: isDark ? '#9370DB' : '#8A2BE2' },
secondary: { main: isDark ? '#FF69B4' : '#FF1493' },
info: { main: isDark ? '#00CED1' : '#00BFFF' },
success: { main: isDark ? '#00FF7F' : '#7CFC00' },
warning: { main: isDark ? '#FFD700' : '#FFA500' },
error: { main: isDark ? '#FF4500' : '#FF6347' },
divider: isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)',
text,
chip,
background: isDark
? { default: '#1A0A1A', paper: '#250E25' }
: { default: '#FFF0F5', paper: '#FFFFFF' },
}
case 'craft':
default:
return {
...common,
primary: { main: isDark ? '#90A4AE' : '#546E7A' },
secondary: { main: isDark ? '#78909C' : '#78909C' },
info: { main: isDark ? '#7986CB' : '#3F51B5' },
success: { main: isDark ? '#66BB6A' : '#43A047' },
warning: { main: isDark ? '#FFB74D' : '#F57C00' },
error: { main: isDark ? '#EF5350' : '#D32F2F' },
divider: isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)',
text,
chip,
background: isDark
? { default: '#121212', paper: '#1E1E1E' }
: { default: '#F5F5F5', paper: '#FFFFFF' },
}
}
})(),
shape: { borderRadius: 12 },
typography: {
fontFamily: '"Outfit", "Segoe UI", system-ui, sans-serif',
h1: { fontWeight: 700, letterSpacing: '-1px', lineHeight: 1.1, textWrap: 'balance' },
h2: { fontWeight: 700, letterSpacing: '-0.75px', lineHeight: 1.15, textWrap: 'balance' },
h3: { fontWeight: 700, letterSpacing: '-0.5px', lineHeight: 1.2, textWrap: 'balance' },
h4: { fontWeight: 700, letterSpacing: '-0.5px', textWrap: 'balance' },
h5: { fontWeight: 600, letterSpacing: '-0.25px', textWrap: 'balance' },
h6: { fontWeight: 600, textWrap: 'balance' },
subtitle1: { fontWeight: 600 },
subtitle2: { fontWeight: 500 },
body1: { fontSize: '0.875rem', lineHeight: 1.6 },
body2: { fontSize: '0.75rem', lineHeight: 1.5 },
button: { textTransform: 'none', fontWeight: 600 },
},
components: {
MuiButton: {
styleOverrides: {
root: {
textTransform: 'none',
borderRadius: 12,
fontWeight: 600,
transition: 'all 0.2s ease-in-out',
'&:focus-visible': {
outline: '2px solid currentColor',
outlineOffset: 2,
},
},
contained: {
boxShadow: '0 4px 14px 0 rgba(0,0,0,0.12)',
'&:hover': {
boxShadow: '0 6px 20px 0 rgba(0,0,0,0.18)',
transform: 'translateY(-2px)',
},
'&:active': {
boxShadow: '0 2px 8px 0 rgba(0,0,0,0.12)',
transform: 'translateY(0) scale(0.98)',
},
},
outlined: {
border: '1px solid',
'&:hover': {
boxShadow: '0 2px 8px 0 rgba(0,0,0,0.08)',
},
'&:active': {
boxShadow: 'none',
transform: 'scale(0.98)',
},
},
text: {
'&:hover': {
backgroundColor: 'action.hover',
},
'&:active': {
backgroundColor: 'action.selected',
},
},
},
},
MuiIconButton: {
styleOverrides: {
root: {
transition: 'all 0.2s ease-in-out',
'&:hover': {
backgroundColor: 'action.hover',
transform: 'scale(1.08)',
},
'&:active': {
backgroundColor: 'action.selected',
transform: 'scale(0.95)',
},
'&:focus-visible': {
outline: '2px solid currentColor',
outlineOffset: 2,
},
},
},
},
MuiCard: {
styleOverrides: {
root: {
'&:focus-visible': {
outline: '2px solid currentColor',
outlineOffset: 2,
},
},
},
},
MuiLink: {
styleOverrides: {
root: {
'&:focus-visible': {
outline: '2px solid currentColor',
outlineOffset: 2,
borderRadius: 2,
},
},
},
},
MuiInputBase: {
styleOverrides: {
root: {
'&.Mui-focused': {
'& .MuiOutlinedInput-notchedOutline': {
borderWidth: 2,
},
},
},
},
},
MuiAlert: {
styleOverrides: {
root: {
borderRadius: 12,
border: '1px solid',
boxShadow: 'none',
fontWeight: 500,
alignItems: 'center',
padding: '8px 12px',
'& .MuiAlert-icon': {
padding: 0,
marginRight: 12,
display: 'flex',
alignItems: 'center',
},
'& .MuiAlert-message': {
padding: 0,
},
'& .MuiAlert-action': {
padding: 0,
marginRight: 0,
marginLeft: 8,
},
},
colorSuccess: ({ theme }) => {
const isDark = theme.palette.mode === 'dark'
const p = theme.palette.success
return {
bgcolor: isDark ? alpha(p.light, 0.08) : alpha(p.main, 0.08),
borderColor: isDark ? alpha(p.light, 0.2) : alpha(p.main, 0.2),
color: isDark ? p.light : p.dark,
'& .MuiAlert-icon': { color: isDark ? p.light : p.dark },
'&.MuiAlert-outlined': {
bgcolor: 'transparent',
borderColor: isDark ? alpha(p.light, 0.3) : alpha(p.main, 0.3),
color: isDark ? p.light : p.dark,
'& .MuiAlert-icon': { color: isDark ? p.light : p.dark },
},
'&.MuiAlert-filled': {
bgcolor: isDark ? alpha(p.light, 0.15) : p.dark,
borderColor: 'transparent',
color: isDark ? alpha(p.light, 0.9) : '#FFFFFF',
},
}
},
colorError: ({ theme }) => {
const isDark = theme.palette.mode === 'dark'
const p = theme.palette.error
return {
bgcolor: isDark ? alpha(p.light, 0.08) : alpha(p.main, 0.08),
borderColor: isDark ? alpha(p.light, 0.2) : alpha(p.main, 0.2),
color: isDark ? p.light : p.dark,
'& .MuiAlert-icon': { color: isDark ? p.light : p.dark },
'&.MuiAlert-outlined': {
bgcolor: 'transparent',
borderColor: isDark ? alpha(p.light, 0.3) : alpha(p.main, 0.3),
color: isDark ? p.light : p.dark,
'& .MuiAlert-icon': { color: isDark ? p.light : p.dark },
},
'&.MuiAlert-filled': {
bgcolor: isDark ? alpha(p.light, 0.15) : p.dark,
borderColor: 'transparent',
color: isDark ? alpha(p.light, 0.9) : '#FFFFFF',
},
}
},
colorWarning: ({ theme }) => {
const isDark = theme.palette.mode === 'dark'
const p = theme.palette.warning
return {
bgcolor: isDark ? alpha(p.light, 0.08) : alpha(p.main, 0.08),
borderColor: isDark ? alpha(p.light, 0.2) : alpha(p.main, 0.2),
color: isDark ? p.light : p.dark,
'& .MuiAlert-icon': { color: isDark ? p.light : p.dark },
'&.MuiAlert-outlined': {
bgcolor: 'transparent',
borderColor: isDark ? alpha(p.light, 0.3) : alpha(p.main, 0.3),
color: isDark ? p.light : p.dark,
'& .MuiAlert-icon': { color: isDark ? p.light : p.dark },
},
'&.MuiAlert-filled': {
bgcolor: isDark ? alpha(p.light, 0.15) : p.dark,
borderColor: 'transparent',
color: isDark ? alpha(p.light, 0.9) : '#FFFFFF',
},
}
},
colorInfo: ({ theme }) => {
const isDark = theme.palette.mode === 'dark'
const p = theme.palette.info
return {
bgcolor: isDark ? alpha(p.light, 0.08) : alpha(p.main, 0.08),
borderColor: isDark ? alpha(p.light, 0.2) : alpha(p.main, 0.2),
color: isDark ? p.light : p.dark,
'& .MuiAlert-icon': { color: isDark ? p.light : p.dark },
'&.MuiAlert-outlined': {
bgcolor: 'transparent',
borderColor: isDark ? alpha(p.light, 0.3) : alpha(p.main, 0.3),
color: isDark ? p.light : p.dark,
'& .MuiAlert-icon': { color: isDark ? p.light : p.dark },
},
'&.MuiAlert-filled': {
bgcolor: isDark ? alpha(p.light, 0.15) : p.dark,
borderColor: 'transparent',
color: isDark ? alpha(p.light, 0.9) : '#FFFFFF',
},
}
},
},
},
MuiSnackbarContent: {
styleOverrides: {
root: {
borderRadius: 12,
border: '1px solid',
borderColor: isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.06)',
bgcolor: isDark ? '#1E1E1E' : '#FFFFFF',
boxShadow: '0 2px 12px rgba(0,0,0,0.06)',
color: isDark ? '#F2F2F2' : '#1F1B16',
fontWeight: 500,
},
},
},
},
}),
[controller.resolvedMode, controller.scheme],
)
return (
{children}
)
}
export function AppProviders({ children }: PropsWithChildren) {
const queryClient = useMemo(
() =>
new QueryClient({
defaultOptions: {
queries: {
staleTime: 30_000,
retry: 1,
refetchOnWindowFocus: false,
},
},
}),
[],
)
return (
{children}
)
}