build ok,

This commit is contained in:
louiscklaw
2025-04-14 09:26:24 +08:00
commit 6c931c1fe8
770 changed files with 63959 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
'use client';
import * as React from 'react';
import Box from '@mui/material/Box';
import GlobalStyles from '@mui/material/GlobalStyles';
import { useSettings } from '@/hooks/use-settings';
import { layoutConfig } from '../config';
import { MainNav } from './main-nav';
export interface HorizontalLayoutProps {
children?: React.ReactNode;
}
export function HorizontalLayout({ children }: HorizontalLayoutProps): React.JSX.Element {
const { settings } = useSettings();
return (
<React.Fragment>
<GlobalStyles
styles={{ body: { '--MainNav-zIndex': 1000, '--MobileNav-width': '320px', '--MobileNav-zIndex': 1100 } }}
/>
<Box
sx={{
bgcolor: 'var(--mui-palette-background-default)',
display: 'flex',
flexDirection: 'column',
position: 'relative',
minHeight: '100%',
}}
>
<MainNav color={settings.navColor} items={layoutConfig.navItems} />
<Box
component="main"
sx={{
'--Content-margin': '0 auto',
'--Content-maxWidth': 'var(--maxWidth-xl)',
'--Content-paddingX': '24px',
'--Content-paddingY': { xs: '24px', lg: '64px' },
'--Content-padding': 'var(--Content-paddingY) var(--Content-paddingX)',
'--Content-width': '100%',
display: 'flex',
flex: '1 1 auto',
flexDirection: 'column',
}}
>
{children}
</Box>
</Box>
</React.Fragment>
);
}

View File

@@ -0,0 +1,516 @@
'use client';
import * as React from 'react';
import RouterLink from 'next/link';
import { usePathname } from 'next/navigation';
import Avatar from '@mui/material/Avatar';
import Badge from '@mui/material/Badge';
import Box from '@mui/material/Box';
import Chip from '@mui/material/Chip';
import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton';
import Stack from '@mui/material/Stack';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import { ArrowSquareOut as ArrowSquareOutIcon } from '@phosphor-icons/react/dist/ssr/ArrowSquareOut';
import { Bell as BellIcon } from '@phosphor-icons/react/dist/ssr/Bell';
import { CaretDown as CaretDownIcon } from '@phosphor-icons/react/dist/ssr/CaretDown';
import { CaretRight as CaretRightIcon } from '@phosphor-icons/react/dist/ssr/CaretRight';
import { List as ListIcon } from '@phosphor-icons/react/dist/ssr/List';
import { MagnifyingGlass as MagnifyingGlassIcon } from '@phosphor-icons/react/dist/ssr/MagnifyingGlass';
import { Users as UsersIcon } from '@phosphor-icons/react/dist/ssr/Users';
import { useTranslation } from 'next-i18next';
import type { NavItemConfig } from '@/types/nav';
import type { NavColor } from '@/types/settings';
import type { User } from '@/types/user';
import { paths } from '@/paths';
import { isNavItemActive } from '@/lib/is-nav-item-active';
import { useDialog } from '@/hooks/use-dialog';
import { usePopover } from '@/hooks/use-popover';
import { useSettings } from '@/hooks/use-settings';
import { Dropdown } from '@/components/core/dropdown/dropdown';
import { DropdownPopover } from '@/components/core/dropdown/dropdown-popover';
import { DropdownTrigger } from '@/components/core/dropdown/dropdown-trigger';
import { Logo } from '@/components/core/logo';
import { SearchDialog } from '@/components/dashboard/layout/search-dialog';
import type { ColorScheme } from '@/styles/theme/types';
import { ContactsPopover } from '../contacts-popover';
import { languageFlags, LanguagePopover } from '../language-popover';
import type { Language } from '../language-popover';
import { MobileNav } from '../mobile-nav';
import { icons } from '../nav-icons';
import { NotificationsPopover } from '../notifications-popover';
import { UserPopover } from '../user-popover/user-popover';
import { WorkspacesSwitch } from '../workspaces-switch';
import { navColorStyles } from './styles';
const logoColors = {
dark: { blend_in: 'light', discrete: 'light', evident: 'light' },
light: { blend_in: 'dark', discrete: 'dark', evident: 'light' },
} as Record<ColorScheme, Record<NavColor, 'dark' | 'light'>>;
export interface MainNavProps {
color?: NavColor;
items?: NavItemConfig[];
}
export function MainNav({ color = 'evident', items = [] }: MainNavProps): React.JSX.Element {
const pathname = usePathname();
const [openNav, setOpenNav] = React.useState<boolean>(false);
const {
settings: { colorScheme = 'light' },
} = useSettings();
const styles = navColorStyles[colorScheme][color];
const logoColor = logoColors[colorScheme][color];
return (
<React.Fragment>
<Box
component="header"
sx={{
...styles,
bgcolor: 'var(--MainNav-background)',
border: 'var(--MainNav-border)',
color: 'var(--MainNav-color)',
left: 0,
position: 'sticky',
top: 0,
zIndex: 'var(--MainNav-zIndex)',
}}
>
<Box
sx={{
display: 'flex',
flex: '1 1 auto',
minHeight: 'var(--MainNav-height, 72px)',
px: { xs: 2, sm: 3 },
py: 1,
}}
>
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', flex: '1 1 auto' }}>
<IconButton
onClick={(): void => {
setOpenNav(true);
}}
sx={{ display: { md: 'none' } }}
>
<ListIcon color="var(--NavItem-icon-color)" />
</IconButton>
<Box component={RouterLink} href={paths.home} sx={{ display: { xs: 'none', md: 'inline-block' } }}>
<Logo color={logoColor} height={32} width={122} />
</Box>
<Box sx={{ display: { xs: 'none', md: 'block' } }}>
<WorkspacesSwitch />
</Box>
</Stack>
<Stack
direction="row"
spacing={2}
sx={{ alignItems: 'center', flex: '1 1 auto', justifyContent: 'flex-end' }}
>
<SearchButton />
<NotificationsButton />
<ContactsButton />
<Divider
flexItem
orientation="vertical"
sx={{ borderColor: 'var(--MainNav-divider)', display: { xs: 'none', md: 'block' } }}
/>
<LanguageSwitch />
<UserButton />
</Stack>
</Box>
<Box
component="nav"
sx={{
borderTop: '1px solid var(--MainNav-divider)',
display: { xs: 'none', md: 'block' },
minHeight: '56px',
overflowX: 'auto',
}}
>
{renderNavGroups({ items, pathname })}
</Box>
</Box>
<MobileNav
items={items}
onClose={() => {
setOpenNav(false);
}}
open={openNav}
/>
</React.Fragment>
);
}
function SearchButton(): React.JSX.Element {
const dialog = useDialog();
return (
<React.Fragment>
<Tooltip title="Search">
<IconButton onClick={dialog.handleOpen} sx={{ display: { xs: 'none', md: 'inline-flex' } }}>
<MagnifyingGlassIcon color="var(--NavItem-icon-color)" />
</IconButton>
</Tooltip>
<SearchDialog onClose={dialog.handleClose} open={dialog.open} />
</React.Fragment>
);
}
function NotificationsButton(): React.JSX.Element {
const popover = usePopover<HTMLButtonElement>();
return (
<React.Fragment>
<Tooltip title="Notifications">
<Badge
color="error"
sx={{ '& .MuiBadge-dot': { borderRadius: '50%', height: '10px', right: '6px', top: '6px', width: '10px' } }}
variant="dot"
>
<IconButton onClick={popover.handleOpen} ref={popover.anchorRef}>
<BellIcon color="var(--NavItem-icon-color)" />
</IconButton>
</Badge>
</Tooltip>
<NotificationsPopover anchorEl={popover.anchorRef.current} onClose={popover.handleClose} open={popover.open} />
</React.Fragment>
);
}
function ContactsButton(): React.JSX.Element {
const popover = usePopover<HTMLButtonElement>();
return (
<React.Fragment>
<Tooltip title="Contacts">
<IconButton onClick={popover.handleOpen} ref={popover.anchorRef}>
<UsersIcon color="var(--NavItem-icon-color)" />
</IconButton>
</Tooltip>
<ContactsPopover anchorEl={popover.anchorRef.current} onClose={popover.handleClose} open={popover.open} />
</React.Fragment>
);
}
function LanguageSwitch(): React.JSX.Element {
const { i18n } = useTranslation();
const popover = usePopover<HTMLButtonElement>();
const language = (i18n.language || 'en') as Language;
const flag = languageFlags[language];
return (
<React.Fragment>
<Tooltip title="Language">
<IconButton
onClick={popover.handleOpen}
ref={popover.anchorRef}
sx={{ display: { xs: 'none', md: 'inline-flex' } }}
>
<Box sx={{ height: '24px', width: '24px' }}>
<Box alt={language} component="img" src={flag} sx={{ height: 'auto', width: '100%' }} />
</Box>
</IconButton>
</Tooltip>
<LanguagePopover anchorEl={popover.anchorRef.current} onClose={popover.handleClose} open={popover.open} />
</React.Fragment>
);
}
const user = {
id: 'USR-000',
name: 'Sofia Rivers',
avatar: '/assets/avatar.png',
email: 'sofia@devias.io',
} satisfies User;
function UserButton(): React.JSX.Element {
const popover = usePopover<HTMLButtonElement>();
return (
<React.Fragment>
<Box
component="button"
onClick={popover.handleOpen}
ref={popover.anchorRef}
sx={{ border: 'none', background: 'transparent', cursor: 'pointer', p: 0 }}
>
<Badge
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
color="success"
sx={{
'& .MuiBadge-dot': {
border: '2px solid var(--MainNav-background)',
borderRadius: '50%',
bottom: '6px',
height: '12px',
right: '6px',
width: '12px',
},
}}
variant="dot"
>
<Avatar src={user.avatar} />
</Badge>
</Box>
<UserPopover anchorEl={popover.anchorRef.current} onClose={popover.handleClose} open={popover.open} />
</React.Fragment>
);
}
function renderNavGroups({ items = [], pathname }: { items?: NavItemConfig[]; pathname: string }): React.JSX.Element {
const children = items.reduce((acc: React.ReactNode[], curr: NavItemConfig): React.ReactNode[] => {
acc.push(
<Box component="li" key={curr.key} sx={{ flex: '0 0 auto' }}>
{renderNavItems({ pathname, items: curr.items })}
</Box>
);
return acc;
}, []);
return (
<Stack component="ul" direction="row" spacing={2} sx={{ listStyle: 'none', m: 0, p: '8px 12px' }}>
{children}
</Stack>
);
}
function renderNavItems({ items = [], pathname }: { items?: NavItemConfig[]; pathname: string }): React.JSX.Element {
const children = items.reduce((acc: React.ReactNode[], curr: NavItemConfig): React.ReactNode[] => {
const { key, ...item } = curr;
acc.push(<NavItem key={key} pathname={pathname} {...item} />);
return acc;
}, []);
return (
<Stack component="ul" direction="row" spacing={2} sx={{ listStyle: 'none', m: 0, p: 0 }}>
{children}
</Stack>
);
}
interface NavItemProps extends NavItemConfig {
pathname: string;
}
function NavItem({
disabled,
external,
items,
href,
icon,
label,
matcher,
pathname,
title,
}: NavItemProps): React.JSX.Element {
const active = isNavItemActive({ disabled, external, href, matcher, pathname });
const Icon = icon ? icons[icon] : null;
const isBranch = Boolean(items);
const element = (
<Box component="li" sx={{ userSelect: 'none' }}>
<Box
{...(isBranch
? { role: 'button' }
: {
...(href
? {
component: external ? 'a' : RouterLink,
href,
target: external ? '_blank' : undefined,
rel: external ? 'noreferrer' : undefined,
}
: { role: 'button' }),
})}
sx={{
alignItems: 'center',
borderRadius: 1,
color: 'var(--NavItem-color)',
cursor: 'pointer',
display: 'flex',
flex: '0 0 auto',
gap: 1,
p: '6px 16px',
textDecoration: 'none',
whiteSpace: 'nowrap',
...(disabled && {
bgcolor: 'var(--NavItem-disabled-background)',
color: 'var(--NavItem-disabled-color)',
cursor: 'not-allowed',
}),
...(active && { bgcolor: 'var(--NavItem-active-background)', color: 'var(--NavItem-active-color)' }),
'&:hover': {
...(!disabled &&
!active && { bgcolor: 'var(--NavItem-hover-background)', color: 'var(--NavItem-hover-color)' }),
},
}}
tabIndex={0}
>
{Icon ? (
<Box sx={{ alignItems: 'center', display: 'flex', justifyContent: 'center', flex: '0 0 auto' }}>
<Icon
fill={active ? 'var(--NavItem-icon-active-color)' : 'var(--NavItem-icon-color)'}
fontSize="var(--icon-fontSize-md)"
weight={active ? 'fill' : undefined}
/>
</Box>
) : null}
<Box sx={{ flex: '1 1 auto' }}>
<Typography
component="span"
sx={{ color: 'inherit', fontSize: '0.875rem', fontWeight: 500, lineHeight: '28px' }}
>
{title}
</Typography>
</Box>
{label ? <Chip color="primary" label={label} size="small" /> : null}
{external ? (
<Box sx={{ alignItems: 'center', display: 'flex', flex: '0 0 auto' }}>
<ArrowSquareOutIcon color="var(--NavItem-icon-color)" fontSize="var(--icon-fontSize-sm)" />
</Box>
) : null}
{isBranch ? (
<Box sx={{ alignItems: 'center', display: 'flex', flex: '0 0 auto' }}>
<CaretDownIcon fontSize="var(--icon-fontSize-sm)" />
</Box>
) : null}
</Box>
</Box>
);
if (items) {
return (
<Dropdown>
<DropdownTrigger>{element}</DropdownTrigger>
<DropdownPopover
PaperProps={{ sx: { minWidth: '200px', p: 1 } }}
anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }}
>
{renderDropdownItems({ pathname, items })}
</DropdownPopover>
</Dropdown>
);
}
return element;
}
function renderDropdownItems({
items = [],
pathname,
}: {
items?: NavItemConfig[];
pathname: string;
}): React.JSX.Element {
const children = items.reduce((acc: React.ReactNode[], curr: NavItemConfig): React.ReactNode[] => {
const { key, ...item } = curr;
acc.push(<DropdownItem key={key} pathname={pathname} {...item} />);
return acc;
}, []);
return (
<Stack component="ul" spacing={1} sx={{ listStyle: 'none', m: 0, p: 0 }}>
{children}
</Stack>
);
}
interface DropdownItemProps extends NavItemConfig {
pathname: string;
}
function DropdownItem({
disabled,
external,
items,
href,
matcher,
pathname,
title,
}: DropdownItemProps): React.JSX.Element {
const active = isNavItemActive({ disabled, external, href, matcher, pathname });
const isBranch = Boolean(items);
const element = (
<Box component="li" sx={{ userSelect: 'none' }}>
<Box
{...(isBranch
? { role: 'button' }
: {
...(href
? {
component: external ? 'a' : RouterLink,
href,
target: external ? '_blank' : undefined,
rel: external ? 'noreferrer' : undefined,
}
: { role: 'button' }),
})}
sx={{
alignItems: 'center',
borderRadius: 1,
color: 'var(--NavItem-color)',
cursor: 'pointer',
display: 'flex',
flex: '0 0 auto',
p: '6px 16px',
textDecoration: 'none',
whiteSpace: 'nowrap',
...(disabled && {
bgcolor: 'var(--mui-palette-action-disabledBackground)',
color: 'var(--mui-action-disabled)',
cursor: 'not-allowed',
}),
...(active && { bgcolor: 'var(--mui-palette-action-selected)', color: 'var(--mui-palette-action-active)' }),
'&:hover': {
...(!disabled &&
!active && { bgcolor: 'var(--mui-palette-action-hover)', color: 'var(--mui-palette-action-color)' }),
},
}}
tabIndex={0}
>
<Box sx={{ flex: '1 1 auto' }}>
<Typography
component="span"
sx={{ color: 'inherit', fontSize: '0.875rem', fontWeight: 500, lineHeight: '28px' }}
>
{title}
</Typography>
</Box>
{isBranch ? (
<Box sx={{ flex: '0 0 auto' }}>
<CaretRightIcon fontSize="var(--icon-fontSize-sm)" />
</Box>
) : null}
</Box>
</Box>
);
if (items) {
return (
<Dropdown>
<DropdownTrigger>{element}</DropdownTrigger>
<DropdownPopover
PaperProps={{ sx: { minWidth: '200px', p: 1 } }}
anchorOrigin={{ horizontal: 'right', vertical: 'top' }}
>
{renderDropdownItems({ pathname, items })}
</DropdownPopover>
</Dropdown>
);
}
return element;
}

View File

@@ -0,0 +1,130 @@
import type { NavColor } from '@/types/settings';
import type { ColorScheme } from '@/styles/theme/types';
export const navColorStyles = {
dark: {
blend_in: {
'--MainNav-background': 'var(--mui-palette-background-default)',
'--MainNav-color': 'var(--mui-palette-common-white)',
'--MainNav-border': '1px solid var(--mui-palette-neutral-700)',
'--MainNav-divider': 'var(--mui-palette-neutral-700)',
'--NavItem-color': 'var(--mui-palette-neutral-300)',
'--NavItem-hover-background': 'var(--mui-palette-action-hover)',
'--NavItem-active-background': 'var(--mui-palette-primary-main)',
'--NavItem-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-disabled-color': 'var(--mui-palette-neutral-500)',
'--NavItem-icon-color': 'var(--mui-palette-neutral-400)',
'--NavItem-icon-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-icon-disabled-color': 'var(--mui-palette-neutral-600)',
'--NavItem-expand-color': 'var(--mui-palette-neutral-400)',
'--Workspaces-background': 'var(--mui-palette-neutral-950)',
'--Workspaces-border-color': 'var(--mui-palette-neutral-700)',
'--Workspaces-title-color': 'var(--mui-palette-neutral-400)',
'--Workspaces-name-color': 'var(--mui-palette-neutral-300)',
'--Workspaces-expand-color': 'var(--mui-palette-neutral-400)',
},
discrete: {
'--MainNav-background': 'var(--mui-palette-neutral-900)',
'--MainNav-color': 'var(--mui-palette-common-white)',
'--MainNav-border': '1px solid var(--mui-palette-neutral-700)',
'--MainNav-divider': 'var(--mui-palette-neutral-700)',
'--NavItem-color': 'var(--mui-palette-neutral-300)',
'--NavItem-hover-background': 'var(--mui-palette-action-hover)',
'--NavItem-active-background': 'var(--mui-palette-primary-main)',
'--NavItem-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-disabled-color': 'var(--mui-palette-neutral-500)',
'--NavItem-icon-color': 'var(--mui-palette-neutral-400)',
'--NavItem-icon-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-icon-disabled-color': 'var(--mui-palette-neutral-600)',
'--NavItem-expand-color': 'var(--mui-palette-neutral-400)',
'--Workspaces-background': 'var(--mui-palette-neutral-950)',
'--Workspaces-border-color': 'var(--mui-palette-neutral-700)',
'--Workspaces-title-color': 'var(--mui-palette-neutral-400)',
'--Workspaces-name-color': 'var(--mui-palette-neutral-300)',
'--Workspaces-expand-color': 'var(--mui-palette-neutral-400)',
},
evident: {
'--MainNav-background': 'var(--mui-palette-neutral-800)',
'--MainNav-color': 'var(--mui-palette-common-white)',
'--MainNav-border': 'none',
'--MainNav-divider': 'var(--mui-palette-neutral-700)',
'--NavItem-color': 'var(--mui-palette-neutral-300)',
'--NavItem-hover-background': 'rgba(255, 255, 255, 0.04)',
'--NavItem-active-background': 'var(--mui-palette-primary-main)',
'--NavItem-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-disabled-color': 'var(--mui-palette-neutral-500)',
'--NavItem-icon-color': 'var(--mui-palette-neutral-400)',
'--NavItem-icon-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-icon-disabled-color': 'var(--mui-palette-neutral-600)',
'--NavItem-expand-color': 'var(--mui-palette-neutral-400)',
'--Workspaces-background': 'var(--mui-palette-neutral-950)',
'--Workspaces-border-color': 'var(--mui-palette-neutral-700)',
'--Workspaces-title-color': 'var(--mui-palette-neutral-400)',
'--Workspaces-name-color': 'var(--mui-palette-neutral-300)',
'--Workspaces-expand-color': 'var(--mui-palette-neutral-400)',
},
},
light: {
blend_in: {
'--MainNav-background': 'var(--mui-palette-background-default)',
'--MainNav-color': 'var(--mui-palette-text-primary)',
'--MainNav-border': '1px solid var(--mui-palette-divider)',
'--MainNav-divider': 'var(--mui-palette-divider)',
'--NavItem-color': 'var(--mui-palette-neutral-600)',
'--NavItem-hover-background': 'var(--mui-palette-action-hover)',
'--NavItem-active-background': 'var(--mui-palette-primary-main)',
'--NavItem-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-disabled-color': 'var(--mui-palette-neutral-500)',
'--NavItem-icon-color': 'var(--mui-palette-neutral-500)',
'--NavItem-icon-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-icon-disabled-color': 'var(--mui-palette-neutral-500)',
'--NavItem-expand-color': 'var(--mui-palette-neutral-500)',
'--Workspaces-background': 'var(--mui-palette-neutral-100)',
'--Workspaces-border-color': 'var(--mui-palette-divider)',
'--Workspaces-title-color': 'var(--mui-palette-neutra-400)',
'--Workspaces-name-color': 'var(--mui-palette-neutral-900)',
'--Workspaces-expand-color': 'var(--mui-palette-neutral-400)',
},
discrete: {
'--MainNav-background': 'var(--mui-palette-neutral-50)',
'--MainNav-color': 'var(--mui-palette-text-primary)',
'--MainNav-border': '1px solid var(--mui-palette-divider)',
'--MainNav-divider': 'var(--mui-palette-divider)',
'--NavGroup-title-color': 'var(--mui-palette-neutral-600)',
'--NavItem-color': 'var(--mui-palette-neutral-600)',
'--NavItem-hover-background': 'var(--mui-palette-action-hover)',
'--NavItem-active-background': 'var(--mui-palette-primary-main)',
'--NavItem-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-disabled-color': 'var(--mui-palette-neutral-500)',
'--NavItem-icon-color': 'var(--mui-palette-neutral-500)',
'--NavItem-icon-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-icon-disabled-color': 'var(--mui-palette-neutral-500)',
'--NavItem-expand-color': 'var(--mui-palette-neutral-500)',
'--Workspaces-background': 'var(--mui-palette-neutral-100)',
'--Workspaces-border-color': 'var(--mui-palette-divider)',
'--Workspaces-title-color': 'var(--mui-palette-neutra-400)',
'--Workspaces-name-color': 'var(--mui-palette-neutral-900)',
'--Workspaces-expand-color': 'var(--mui-palette-neutral-400)',
},
evident: {
'--MainNav-background': 'var(--mui-palette-neutral-950)',
'--MainNav-color': 'var(--mui-palette-common-white)',
'--MainNav-border': 'none',
'--MainNav-divider': 'var(--mui-palette-neutral-700)',
'--NavItem-color': 'var(--mui-palette-neutral-300)',
'--NavItem-hover-background': 'rgba(255, 255, 255, 0.04)',
'--NavItem-active-background': 'var(--mui-palette-primary-main)',
'--NavItem-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-disabled-color': 'var(--mui-palette-neutral-500)',
'--NavItem-icon-color': 'var(--mui-palette-neutral-400)',
'--NavItem-icon-active-color': 'var(--mui-palette-primary-contrastText)',
'--NavItem-icon-disabled-color': 'var(--mui-palette-neutral-600)',
'--NavItem-expand-color': 'var(--mui-palette-neutral-400)',
'--Workspaces-background': 'var(--mui-palette-neutral-950)',
'--Workspaces-border-color': 'var(--mui-palette-neutral-700)',
'--Workspaces-title-color': 'var(--mui-palette-neutral-400)',
'--Workspaces-name-color': 'var(--mui-palette-neutral-300)',
'--Workspaces-expand-color': 'var(--mui-palette-neutral-400)',
},
},
} satisfies Record<ColorScheme, Record<NavColor, Record<string, string>>>;