UNPKG

@atomazing-org/design-system

Version:

A library providing a set of useful utils, MUI style extensions, and components to build your application.

1 lines 61.6 kB
{"version":3,"sources":["../src/components/DialogBtn.ts","../src/components/ErrorBoundary.tsx","../src/components/Loading.tsx","../src/components/PathName.ts","../src/constants/darkModeOptions.tsx","../src/constants/defaultColorPalette.ts","../src/context/ThemeContext.tsx","../src/context/ThemeProviderWrapper.tsx","../src/styles/commonComponents.ts","../src/styles/createTheme.ts","../src/styles/themeConfig.ts","../src/styles/typography.ts","../src/styles/GlobalStyles.tsx","../src/utils/colorUtils.ts","../src/utils/displayGreeting.ts","../src/utils/getDayIdentifier.ts","../src/utils/getSystemInfo.ts","../src/utils/isDarkMode.ts","../src/utils/timeAgo.ts","../src/utils/useResponsiveDisplay.ts","../src/utils/useSystemTheme.ts","../src/styles/keyframes.ts"],"sourcesContent":["import styled from \"@emotion/styled\";\nimport { Button } from \"@mui/material\";\n\nexport const DialogBtn = styled(Button)`\n padding: 10px 16px;\n border-radius: 16px;\n font-size: 16px;\n margin: 8px;\n`;\n","/* eslint-disable @typescript-eslint/class-methods-use-this -- only for ErrorBoundary */\nimport React from \"react\";\nimport styled from \"@emotion/styled\";\nimport ErrorOutlineRounded from \"@mui/icons-material/ErrorOutlineRounded\";\nimport { Box } from \"@mui/material\";\n\nimport type { ErrorInfo } from \"react\";\n\ninterface ErrorBoundaryProps {\n children: React.ReactNode;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error?: Error;\n}\n\n/**\n * ErrorBoundary component that catches and displays errors.\n */\nexport class ErrorBoundary extends React.Component<\n ErrorBoundaryProps,\n ErrorBoundaryState\n> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = {\n hasError: false,\n };\n }\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return {\n hasError: true,\n error,\n };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n // eslint-disable-next-line no-console -- Allow console output for error reporting\n console.error(\"Error:\", error);\n // eslint-disable-next-line no-console -- Allow console output for error reporting\n console.error(\"Error Info:\", errorInfo);\n }\n\n render() {\n const { state, props } = this;\n if (state.hasError) {\n return (\n <Container>\n <ErrorHeader>\n <Box>Something went wrong.&nbsp;</Box>\n </ErrorHeader>\n <h3>\n <Box style={{ color: \"#ff3131\", display: \"inline-block\" }}>\n <ErrorOutlineRounded\n sx={{ verticalAlign: \"middle\", mb: \"4px\" }}\n />{\" \"}\n ERROR:\n </Box>{\" \"}\n <Box translate=\"no\">\n [{state.error?.name}] {state.error?.message}\n </Box>\n <Box style={{ color: \"#ff3131\", display: \"inline-block\" }}>\n <ErrorOutlineRounded\n sx={{ verticalAlign: \"middle\", mb: \"4px\" }}\n />{\" \"}\n Stack:\n </Box>{\" \"}\n <Box translate=\"no\">[{state.error?.stack}]</Box>\n </h3>\n </Container>\n );\n }\n\n return props.children;\n }\n}\n\nconst Container = styled.div`\n margin: 0 8vw;\n @media (max-width: 768px) {\n margin: 0;\n }\n`;\n\nconst ErrorHeader = styled.h1`\n margin-top: 32px;\n margin-bottom: 32px;\n font-size: 36px;\n color: #ff3131;\n text-align: center;\n display: flex;\n align-items: center;\n justify-content: center;\n @media (max-width: 768px) {\n text-align: left;\n justify-content: left;\n font-size: 30px;\n margin-top: 0;\n margin-bottom: 0;\n }\n`;\n","import { useEffect, useState } from \"react\";\nimport styled from \"@emotion/styled\";\nimport { Box, CircularProgress } from \"@mui/material\";\n\nexport const Loading = () => {\n const [showLoading, setShowLoading] = useState<boolean>(false);\n\n useEffect(() => {\n const timer = setTimeout(() => {\n setShowLoading(true);\n }, 100); // Show the loading spinner after 100 milliseconds\n\n return () => clearTimeout(timer);\n }, []);\n\n return (\n <Container aria-live=\"polite\" role=\"status\">\n {showLoading && (\n <>\n <CircularProgress aria-label=\"loading\" size={80} thickness={4} />\n <h3 style={{ opacity: 0.8 }}>Loading Page...</h3>\n </>\n )}\n </Container>\n );\n};\n\nconst Container = styled(Box)`\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n justify-content: center;\n align-items: center;\n flex-direction: column;\n text-align: center;\n gap: 8px;\n`;\n","import styled from \"@emotion/styled\";\n\nexport const PathName = styled.code`\n background: #000000c8;\n color: white;\n padding: 4px 6px;\n border-radius: 8px;\n`;\n","import BrightnessAutoRoundedIcon from \"@mui/icons-material/BrightnessAutoRounded\";\nimport DarkModeRoundedIcon from \"@mui/icons-material/DarkModeRounded\";\nimport LightModeRoundedIcon from \"@mui/icons-material/LightModeRounded\";\nimport PersonalVideoRoundedIcon from \"@mui/icons-material/PersonalVideoRounded\";\n\nimport type { OptionItem } from \"../models\";\n\nconst OPTION_ICON_SIZE = 32;\n\nexport const darkModeOptions: OptionItem[] = [\n {\n label: \"Auto\",\n value: \"auto\",\n icon: (\n <BrightnessAutoRoundedIcon\n color=\"inherit\"\n sx={{ fontSize: OPTION_ICON_SIZE }}\n />\n ),\n },\n {\n label: \"System\",\n value: \"system\",\n icon: (\n <PersonalVideoRoundedIcon\n color=\"inherit\"\n sx={{ fontSize: OPTION_ICON_SIZE }}\n />\n ),\n },\n {\n label: \"Light\",\n value: \"light\",\n icon: (\n <LightModeRoundedIcon\n color=\"inherit\"\n sx={{ fontSize: OPTION_ICON_SIZE }}\n />\n ),\n },\n {\n label: \"Dark\",\n value: \"dark\",\n icon: (\n <DarkModeRoundedIcon\n color=\"inherit\"\n sx={{ fontSize: OPTION_ICON_SIZE }}\n />\n ),\n },\n];\n","import type { ColorPaletteType } from \"../models\";\n\nexport const defaultColorPalette: ColorPaletteType = {\n fontDark: \"#101727\",\n fontLight: \"#f0f0f0\",\n // Generic tokens\n brand: \"#9FA9EA\",\n accent: \"#F3503A\",\n muted: \"#64748B\",\n // MUI-like defaults\n success: \"#2E7D32\",\n info: \"#0288D1\",\n warning: \"#ED6C02\",\n error: \"#D32F2F\",\n neutral: \"#64748B\",\n};\n","import { createContext, useContext } from \"react\";\n\nimport type { ThemeContextProps } from \"../models\";\n\nexport const ThemeContext = createContext<ThemeContextProps | undefined>(\n undefined,\n);\n\nexport const useThemeSettings = (): ThemeContextProps => {\n const context = useContext(ThemeContext);\n if (!context)\n throw new Error(\n \"useThemeSettings must be used within ThemeProviderWrapper\",\n );\n return context;\n};\n","import { useMemo, useState, useEffect } from \"react\";\nimport { ThemeProvider as EmotionThemeProvider } from \"@emotion/react\";\nimport { ThemeProvider as MuiThemeProvider } from \"@mui/material/styles\";\n\nimport {\n createCustomTheme,\n GlobalStyles,\n setColorPaletteOverride,\n getColorPalette,\n} from \"../styles\";\nimport { isDarkMode, useSystemTheme } from \"../utils\";\n\nimport { ThemeContext } from \"./ThemeContext\";\n\nimport type { ThemeOptions } from \"@mui/material\";\nimport type { ColorPaletteType } from \"../models\";\nimport type { FC, PropsWithChildren } from \"react\";\n\ntype ThemeProviderWrapperProps = PropsWithChildren<{\n /** Optional font stack to apply across the app. */\n fontFamily?: string;\n /**\n * Optional dynamic list of themes.\n * Takes precedence over static defaults when provided.\n */\n themes?: {\n name: string;\n primaryColor: string;\n secondaryColor?: string;\n background?: {\n light?: { default?: string; paper?: string };\n dark?: { default?: string; paper?: string };\n };\n }[];\n /** Optional color palette override (e.g., fontLight/fontDark/accent colors). */\n colorPaletteOverride?: Partial<ColorPaletteType>;\n /**\n * Optional MUI theme overrides to customize design system styles.\n * Allows external consumers to override any part of the theme (components, palette, typography, etc.).\n * Applied after the design system theme, so it takes precedence.\n */\n themeOverrides?: ThemeOptions;\n}>;\n\nexport const ThemeProviderWrapper: FC<ThemeProviderWrapperProps> = ({\n children,\n fontFamily,\n themes: themesInput,\n colorPaletteOverride,\n themeOverrides,\n}) => {\n const systemTheme = useSystemTheme();\n\n // Apply palette overrides when provided (no-op otherwise)\n useEffect(() => {\n setColorPaletteOverride(colorPaletteOverride);\n }, [colorPaletteOverride]);\n\n // SSR-safe initial settings\n const [theme, setTheme] = useState<string>(\"system\");\n const [darkMode, setDarkMode] = useState<\n \"light\" | \"dark\" | \"system\" | \"auto\"\n >(\"auto\");\n\n // Hydrate from localStorage on client\n useEffect(() => {\n if (globalThis.window === undefined) return;\n try {\n const storedRaw = globalThis.localStorage.getItem(\"appSettings\");\n if (storedRaw) {\n const stored = JSON.parse(storedRaw);\n if (stored.theme) setTheme(stored.theme);\n if (stored.darkMode) setDarkMode(stored.darkMode);\n }\n } catch {\n /* empty */\n }\n }, []);\n\n // Persist settings\n useEffect(() => {\n if (globalThis.window === undefined) return;\n try {\n globalThis.localStorage.setItem(\n \"appSettings\",\n JSON.stringify({ theme, darkMode }),\n );\n } catch {\n /* empty */\n }\n }, [theme, darkMode]);\n\n const themesSource = useMemo(() => {\n if (themesInput && themesInput.length > 0) {\n return themesInput;\n }\n // Fallback: single default theme based on palette brand\n const defaultPrimary = getColorPalette().brand;\n return [\n {\n name: \"Default\",\n primaryColor: defaultPrimary,\n },\n ];\n }, [themesInput]);\n\n const selectedTheme = useMemo(() => {\n if (theme === \"system\" || systemTheme === \"unknown\") {\n return themesSource[0];\n }\n return themesSource.find((t) => t.name === theme) || themesSource[0];\n }, [systemTheme, theme, themesSource]);\n\n const mode = useMemo(\n () => (isDarkMode(darkMode, systemTheme) ? \"dark\" : \"light\"),\n [darkMode, systemTheme],\n );\n\n const muiTheme = useMemo(() => {\n const bg = selectedTheme.background?.[mode];\n return createCustomTheme(\n selectedTheme.primaryColor,\n mode,\n selectedTheme.secondaryColor,\n bg,\n themeOverrides,\n );\n }, [selectedTheme, mode, themeOverrides]);\n\n const emotionTheme = useMemo(() => ({ darkMode: mode === \"dark\" }), [mode]);\n\n return (\n <ThemeContext.Provider value={{ theme, darkMode, setTheme, setDarkMode }}>\n <MuiThemeProvider theme={muiTheme}>\n <EmotionThemeProvider theme={emotionTheme}>\n <GlobalStyles fontFamily={fontFamily} />\n {children}\n </EmotionThemeProvider>\n </MuiThemeProvider>\n </ThemeContext.Provider>\n );\n};\n","import type { Theme } from \"@mui/material\";\n\n/**\n * Common component style overrides and default props shared across the design system.\n * This object should be spread into the `components` field of the MUI theme.\n */\nexport const commonComponentProps: Theme[\"components\"] = {\n MuiTooltip: {\n defaultProps: {\n disableInteractive: true,\n },\n styleOverrides: {\n tooltip: ({ theme }) => ({\n backdropFilter: \"blur(6px)\",\n WebkitBackdropFilter: \"blur(6px)\",\n padding: \"8px 16px\",\n borderRadius: theme.shape.borderRadius,\n fontSize: \"12px\",\n }),\n },\n },\n\n MuiButton: {\n styleOverrides: {\n root: ({ theme }) => ({\n padding: \"12px 24px\",\n borderRadius: theme.shape.borderRadius,\n }),\n contained: {\n boxShadow: \"none\",\n },\n },\n },\n\n MuiSkeleton: {\n styleOverrides: {\n root: ({ theme }) => ({\n borderRadius: theme.shape.borderRadius,\n }),\n },\n },\n\n MuiSelect: {\n styleOverrides: {\n root: ({ theme }) => ({\n borderRadius: theme.shape.borderRadius,\n }),\n select: {\n display: \"flex\",\n justifyContent: \"flex-start\",\n alignItems: \"center\",\n gap: \"4px\",\n },\n },\n },\n\n MuiDialog: {\n defaultProps: {\n slotProps: {\n paper: {\n style: {\n padding: \"12px\",\n borderRadius: 24, // оставить явно, если это критично\n minWidth: \"400px\",\n },\n },\n },\n },\n styleOverrides: {\n root: {\n \"& .MuiDialog-container\": {\n backdropFilter: \"blur(4px)\",\n },\n },\n },\n },\n\n MuiAvatar: {\n styleOverrides: {\n root: {\n fontWeight: 500,\n },\n },\n },\n\n MuiAlert: {\n styleOverrides: {\n root: ({ theme }) => ({\n borderRadius: theme.shape.borderRadius,\n }),\n },\n },\n\n MuiTextField: {\n defaultProps: {\n variant: \"outlined\", // по умолчанию, если нужно\n },\n styleOverrides: {\n root: ({ theme }) => ({\n \"& .MuiInputBase-root\": {\n borderRadius: theme.shape.borderRadius,\n },\n }),\n },\n },\n\n MuiOutlinedInput: {\n styleOverrides: {\n root: ({ theme }) => ({\n color: theme.palette.primary.main,\n \"& fieldset\": {\n borderColor: theme.palette.primary.main,\n },\n \"&:hover fieldset\": {\n borderColor: theme.palette.primary.dark,\n },\n \"&.Mui-focused fieldset\": {\n borderColor: theme.palette.primary.main,\n },\n }),\n },\n },\n\n MuiInputLabel: {\n styleOverrides: {\n root: ({ theme }) => ({\n color: theme.palette.primary.main,\n \"&.Mui-focused\": {\n color: theme.palette.primary.main,\n },\n }),\n },\n },\n MuiFormHelperText: {\n styleOverrides: {\n root: ({ theme }) => ({\n color: theme.palette.error.main,\n }),\n },\n },\n\n MuiPaper: {\n styleOverrides: {\n root: ({ theme }) => ({\n borderRadius: theme.shape.borderRadius,\n }),\n elevation8: ({ theme }) => ({\n borderRadius: theme.shape.borderRadius,\n }),\n },\n },\n\n MuiMenuItem: {\n styleOverrides: {\n root: ({ theme }) => ({\n borderRadius: theme.shape.borderRadius,\n }),\n },\n },\n\n MuiBottomNavigationAction: {\n styleOverrides: {\n root: ({ theme }) => ({\n borderRadius: theme.shape.borderRadius,\n padding: \"12px\",\n margin: 0,\n maxHeight: \"none\",\n }),\n },\n },\n\n MuiDialogContent: {\n styleOverrides: {\n root: {\n padding: 0,\n },\n },\n },\n\n MuiSlider: {\n styleOverrides: {\n valueLabel: ({ theme }) => ({\n borderRadius: theme.shape.borderRadius,\n padding: \"6px 14px\",\n \"&::before, &::after\": {\n display: \"none\",\n },\n }),\n },\n },\n\n MuiCircularProgress: {\n styleOverrides: {\n circle: {\n strokeLinecap: \"round\",\n },\n },\n },\n\n MuiTab: {\n styleOverrides: {\n root: ({ theme }) => ({\n borderRadius: theme.shape.borderRadius,\n }),\n },\n },\n\n MuiAccordion: {\n styleOverrides: {\n root: {\n \"&::before\": {\n display: \"none\",\n },\n },\n },\n },\n};\n","import { createTheme } from \"@mui/material\";\n\nimport { commonComponentProps } from \"./commonComponents\";\nimport { getColorPalette } from \"./themeConfig\";\nimport { muiTypography, typographyVariants } from \"./typography\";\n\nimport type { PaletteMode, Theme, ThemeOptions } from \"@mui/material\";\n\n\nexport const createCustomTheme = (\n primaryColor: string,\n mode: PaletteMode = \"light\",\n secondaryColor?: string,\n background?: { default?: string; paper?: string },\n themeOverrides?: ThemeOptions,\n): Theme => {\n const isDark = mode === \"dark\";\n\n // 1) БАЗА MUI по выбранному режиму — даёт корректные text, action, grey и т.д.\n const base = createTheme({\n palette: { mode },\n });\n\n // 2) ТОЧЕЧНЫЕ ОВЕРРАЙДЫ поверх базы\n const designSystemTheme: ThemeOptions = {\n palette: {\n primary: { ...base.palette.primary, main: primaryColor },\n // Brand follows active theme primary\n brand: base.palette.augmentColor({ color: { main: primaryColor } }),\n neutral: base.palette.augmentColor({\n color: { main: getColorPalette().neutral },\n }),\n accent: base.palette.augmentColor({\n color: { main: getColorPalette().accent },\n }),\n muted: base.palette.augmentColor({\n color: { main: getColorPalette().muted },\n }),\n ...(secondaryColor\n ? { secondary: { ...base.palette.secondary, main: secondaryColor } }\n : {}),\n error: { ...base.palette.error, main: getColorPalette().error },\n warning: { ...base.palette.warning, main: getColorPalette().warning },\n success: { ...base.palette.success, main: getColorPalette().success },\n info: { ...base.palette.info, main: getColorPalette().info },\n\n background: (() => {\n const baseBg = isDark\n ? { default: \"#1C1C1E\", paper: \"#2C2C2E\" }\n : { default: \"#F2F2F7\", paper: \"#FFFFFF\" };\n return { ...base.palette.background, ...baseBg, ...background };\n })(),\n\n divider: isDark ? \"rgba(255,255,255,0.12)\" : \"rgba(0,0,0,0.12)\",\n },\n\n // Остальные ваши настройки — без изменений\n components: {\n ...commonComponentProps,\n MuiTypography: muiTypography,\n },\n typography: {\n ...typographyVariants,\n // Let application control font via CSS variable; default to Mulish stack\n fontFamily:\n 'var(--app-font-family, \"Mulish\", system-ui, -apple-system, \"Segoe UI\", Roboto, Arial, sans-serif)',\n } as any,\n shape: { borderRadius: 24 },\n };\n\n // 3) ПРИМЕНЕНИЕ ПЕРЕОПРЕДЕЛЕНИЙ ОТ ВНЕШНИХ ПОТРЕБИТЕЛЕЙ\n // Переопределения применяются поверх дизайн-системы, позволяя полностью перезаписать любые стили\n return createTheme(base, designSystemTheme, themeOverrides ?? {});\n};\n\n/**\n * A predefined list of named themes based on the `themeConfig` definition.\n */\n// No static theme list export — themes are provided dynamically via ThemeProviderWrapper.\n","import { defaultColorPalette } from \"../constants\";\n\nimport type { ColorPaletteType } from \"../models\";\n\nlet currentColorPalette: ColorPaletteType = { ...defaultColorPalette };\n\n/** Returns the active color palette, allowing app-level overrides. */\nexport const getColorPalette = (): ColorPaletteType => currentColorPalette;\n\n/**\n * Overrides the active color palette with app-provided values.\n * Pass `undefined` or empty to reset to defaults.\n */\nexport const setColorPaletteOverride = (\n override?: Partial<ColorPaletteType>,\n): void => {\n currentColorPalette = { ...defaultColorPalette, ...override };\n};\n\n/** Backward-compatible live view of the active palette. */\nexport const ColorPalette: Readonly<ColorPaletteType> = {\n get fontDark() {\n return currentColorPalette.fontDark;\n },\n get fontLight() {\n return currentColorPalette.fontLight;\n },\n get brand() {\n return currentColorPalette.brand;\n },\n get accent() {\n return currentColorPalette.accent;\n },\n get muted() {\n return currentColorPalette.muted;\n },\n get success() {\n return currentColorPalette.success;\n },\n get info() {\n return currentColorPalette.info;\n },\n get warning() {\n return currentColorPalette.warning;\n },\n get error() {\n return currentColorPalette.error;\n },\n get neutral() {\n return currentColorPalette.neutral;\n },\n};\n","import type {\n Components,\n Theme,\n TypographyVariantsOptions,\n} from \"@mui/material\";\n\n/**\n * Mapping of custom typography variants to corresponding HTML elements.\n */\nexport const muiTypography: Components<Theme>[\"MuiTypography\"] = {\n defaultProps: {\n variantMapping: {\n // TEXT REGULAR\n text_xl_regular: \"p\",\n text_lg_regular: \"p\",\n text_md_regular: \"p\",\n text_sm_regular: \"p\",\n text_xs_regular: \"p\",\n text_2xs_regular: \"p\",\n\n // TEXT BOLD\n text_xl_bold: \"p\",\n text_lg_bold: \"p\",\n text_md_bold: \"p\",\n text_sm_bold: \"p\",\n text_xs_bold: \"p\",\n text_2xs_bold: \"p\",\n\n // TEXT SEMIBOLD\n text_xl_semibold: \"p\",\n text_lg_semibold: \"p\",\n text_md_semibold: \"p\",\n text_sm_semibold: \"p\",\n text_xs_semibold: \"p\",\n text_2xs_semibold: \"p\",\n\n // TEXT THIN\n text_xl_thin: \"p\",\n text_lg_thin: \"p\",\n text_md_thin: \"p\",\n text_sm_thin: \"p\",\n text_xs_thin: \"p\",\n text_2xs_thin: \"p\",\n\n // HEADER REGULAR\n header_2xl_regular: \"h1\",\n header_xl_regular: \"h2\",\n header_lg_regular: \"h3\",\n header_md_regular: \"h4\",\n header_sm_regular: \"h5\",\n header_xs_regular: \"h6\",\n\n // DISPLAY BOLD\n header_2xl_bold: \"h1\",\n header_xl_bold: \"h2\",\n header_lg_bold: \"h3\",\n header_md_bold: \"h4\",\n header_sm_bold: \"h5\",\n header_xs_bold: \"h6\",\n\n // HEADER SEMIBOLD\n header_2xl_semibold: \"h1\",\n header_xl_semibold: \"h2\",\n header_lg_semibold: \"h3\",\n header_md_semibold: \"h4\",\n header_sm_semibold: \"h5\",\n header_xs_semibold: \"h6\",\n },\n },\n};\n\n/**\n * Custom typography variant definitions with adjusted display sizes.\n */\nexport const typographyVariants: TypographyVariantsOptions = {\n text_xl_regular: { font: \"400 20px/30px inherit inherit\" },\n text_lg_regular: { font: \"400 18px/28px inherit inherit\" },\n text_md_regular: { font: \"400 16px/24px inherit inherit\" },\n text_sm_regular: { font: \"400 14px/20px inherit inherit\" },\n text_xs_regular: { font: \"400 12px/18px inherit inherit\" },\n text_2xs_regular: { font: \"400 10px/14px inherit inherit\" },\n\n text_xl_bold: { font: \"700 20px/30px inherit inherit\" },\n text_lg_bold: { font: \"700 18px/28px inherit inherit\" },\n text_md_bold: { font: \"700 16px/24px inherit inherit\" },\n text_sm_bold: { font: \"700 14px/20px inherit inherit\" },\n text_xs_bold: { font: \"700 12px/18px inherit inherit\" },\n text_2xs_bold: { font: \"700 10px/14px inherit inherit\" },\n\n text_xl_semibold: { font: \"600 20px/30px inherit inherit\" },\n text_lg_semibold: { font: \"600 18px/28px inherit inherit\" },\n text_md_semibold: { font: \"600 16px/24px inherit inherit\" },\n text_sm_semibold: { font: \"600 14px/20px inherit inherit\" },\n text_xs_semibold: { font: \"600 12px/18px inherit inherit\" },\n text_2xs_semibold: { font: \"600 10px/14px inherit inherit\" },\n\n text_xl_thin: { font: \"100 20px/30px inherit inherit\" },\n text_lg_thin: { font: \"100 18px/28px inherit inherit\" },\n text_md_thin: { font: \"100 16px/24px inherit inherit\" },\n text_sm_thin: { font: \"100 14px/20px inherit inherit\" },\n text_xs_thin: { font: \"100 12px/18px inherit inherit\" },\n text_2xs_thin: { font: \"100 10px/14px inherit inherit\" },\n\n header_2xl_regular: { font: \"400 34px/42px inherit inherit\" },\n header_xl_regular: { font: \"400 32px/40px inherit inherit\" },\n header_lg_regular: { font: \"400 28px/36px inherit inherit\" },\n header_md_regular: { font: \"400 24px/32px inherit inherit\" },\n header_sm_regular: { font: \"400 20px/28px inherit inherit\" },\n header_xs_regular: { font: \"400 18px/26px inherit inherit\" },\n\n header_2xl_bold: { font: \"700 34px/42px inherit inherit\" },\n header_xl_bold: { font: \"700 32px/40px inherit inherit\" },\n header_lg_bold: { font: \"700 28px/36px inherit inherit\" },\n header_md_bold: { font: \"700 24px/32px inherit inherit\" },\n header_sm_bold: { font: \"700 20px/28px inherit inherit\" },\n header_xs_bold: { font: \"700 18px/26px inherit inherit\" },\n\n // HEADER SEMIBOLD\n header_2xl_semibold: { font: \"600 34px/42px inherit inherit\" },\n header_xl_semibold: { font: \"600 32px/40px inherit inherit\" },\n header_lg_semibold: { font: \"600 28px/36px inherit inherit\" },\n header_md_semibold: { font: \"600 24px/32px inherit inherit\" },\n header_sm_semibold: { font: \"600 20px/28px inherit inherit\" },\n header_xs_semibold: { font: \"600 18px/26px inherit inherit\" },\n};\n","import { useMemo } from \"react\";\nimport { Global, css } from \"@emotion/react\";\nimport { useTheme } from \"@mui/material/styles\";\n\nimport { getFontColor } from \"../utils\";\n\nimport type { FC } from \"react\";\n\n/**\n * Injects global styles into the document using Emotion.\n * These styles include font setup, base HTML styles, custom scrollbars,\n * selection styling, and some accessibility tweaks.\n *\n * Uses the MUI theme to dynamically adjust colors for light/dark mode.\n */\ninterface GlobalStylesProps {\n /** Optional font stack to apply across the app. */\n fontFamily?: string;\n}\n\nexport const GlobalStyles: FC<GlobalStylesProps> = ({ fontFamily }) => {\n const theme = useTheme();\n const isDarkMode = theme.palette.mode === \"dark\";\n\n const primaryColor = theme.palette.primary.main;\n const backgroundDefault = theme.palette.background.default;\n const backgroundPaper = theme.palette.background.paper;\n\n const primaryFontColor = useMemo(\n () => getFontColor(primaryColor),\n [primaryColor],\n );\n\n return (\n <Global\n styles={css`\n /* Allow application to control font via CSS var or prop */\n :root {\n ${fontFamily ? `--app-font-family: ${fontFamily};` : \"\"}\n }\n * {\n font-family:\n var(\n --app-font-family,\n \"Mulish\",\n system-ui,\n -apple-system,\n \"Segoe UI\",\n Roboto,\n Arial\n ),\n sans-serif !important;\n -webkit-tap-highlight-color: transparent;\n &::selection {\n background-color: ${`${primaryColor}e1`};\n color: ${primaryFontColor};\n }\n }\n\n html,\n body,\n #root {\n height: 100%;\n margin: 0;\n }\n\n :root {\n height: 100%;\n /* default fallback font; apps can override via --app-font-family */\n font-family: var(\n --app-font-family,\n \"Mulish\",\n system-ui,\n -apple-system,\n \"Segoe UI\",\n Roboto,\n Arial,\n sans-serif\n )\n sans-serif;\n line-height: 1.5;\n font-weight: 400;\n color-scheme: ${isDarkMode ? \"dark\" : \"light\"};\n font-synthesis: none;\n text-rendering: optimizeLegibility;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n -webkit-text-size-adjust: 100%;\n }\n\n body {\n margin: 0;\n height: 100%;\n overflow: auto;\n touch-action: manipulation;\n background: ${backgroundDefault};\n background-attachment: fixed;\n background-size: cover;\n transition: 0.3s background;\n /* Firefox */\n scrollbar-color: ${primaryColor} ${backgroundDefault};\n scrollbar-width: thin;\n\n ::-webkit-scrollbar {\n width: 8px;\n background-color: ${backgroundDefault};\n }\n ::-webkit-scrollbar-thumb {\n background-color: ${primaryColor};\n border-radius: 64px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: ${`${primaryColor}d8`};\n }\n ::-webkit-scrollbar-track {\n border-radius: 64px;\n background-color: ${backgroundDefault};\n }\n }\n\n a {\n text-decoration: none;\n color: inherit;\n }\n\n img {\n user-select: none;\n }\n\n input[type=\"file\"]::-webkit-file-upload-button {\n display: none;\n }\n\n input[type=\"datetime-local\"]:placeholder-shown {\n color: transparent !important;\n }\n\n pre {\n padding: 16px;\n border-radius: 18px;\n overflow-x: auto;\n }\n\n .MuiDialogContent-root,\n .MuiDrawer-paper,\n .customScrollbar,\n textarea {\n /* Firefox */\n scrollbar-color: ${primaryColor} ${backgroundPaper};\n scrollbar-width: thin;\n ::-webkit-scrollbar {\n width: 8px;\n background-color: ${backgroundPaper};\n }\n ::-webkit-scrollbar-thumb {\n background-color: ${primaryColor};\n border-radius: 64px;\n }\n ::-webkit-scrollbar-thumb:hover {\n background-color: ${`${primaryColor}d8`};\n }\n ::-webkit-scrollbar-track {\n border-radius: 64px;\n background-color: ${backgroundPaper};\n }\n }\n\n /* react-spring-bottom-sheet styles */\n div[role=\"dialog\"] {\n border-radius: 42px 42px 0 0;\n z-index: 9999999;\n }\n `}\n />\n );\n};\n","import { getColorPalette } from \"../styles/themeConfig\";\n\n/**\n * Validates whether a given string is a valid 3- or 6-digit hex color code (e.g., \"#fff\" or \"#ffffff\").\n *\n * @param value - The string to check.\n * @returns `true` if the string is a valid hex color; otherwise, `false`.\n */\nexport const isHexColor = (value: string): boolean =>\n /^#([\\dA-Fa-f]{3}|[\\dA-Fa-f]{6})$/.test(value);\n\n/**\n * Determines the ideal font color (white or black) for contrast against a given background color.\n *\n * Uses luminance calculation (YIQ) to ensure accessibility and visual clarity.\n *\n * @param backgroundColor - A valid hex color (e.g., \"#ffffff\").\n * @returns A hex color string (`fontLight` or `fontDark`) suitable for overlay text.\n */\nexport const getFontColor = (backgroundColor: string): string => {\n if (!isHexColor(backgroundColor)) {\n // eslint-disable-next-line no-console -- Allow\n console.error(\"Invalid hex color provided:\", backgroundColor);\n return getColorPalette().fontDark;\n }\n\n const hex = backgroundColor.slice(1);\n\n const fullHex =\n hex.length === 3\n ? hex\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : hex;\n\n const r = Number.parseInt(fullHex.slice(0, 2), 16);\n const g = Number.parseInt(fullHex.slice(2, 4), 16);\n const b = Number.parseInt(fullHex.slice(4, 6), 16);\n\n const brightness = Math.round((r * 299 + g * 587 + b * 114) / 1000);\n\n const palette = getColorPalette();\n return brightness > 128 ? palette.fontDark : palette.fontLight;\n};\n\n/**\n * Determines whether the ideal font color for a background is light (i.e., white).\n *\n * @param color - The background color in hex.\n * @returns `true` if white text is recommended; otherwise, `false`.\n */\nexport const isFontLight = (color: string): boolean =>\n getFontColor(color) === getColorPalette().fontLight;\n","/**\n * Returns a greeting based on the current time.\n * @returns {string} The appropriate greeting.\n */\nexport const displayGreeting = (): string => {\n const currentHour = new Date().getHours();\n if (currentHour >= 5 && currentHour < 12) return \"Good morning\";\n if (currentHour > 12 && currentHour < 18) return \"Good afternoon\";\n return \"Good evening\";\n};\n","/**\n * Function to extract year, month, and day from a Date object\n */\nexport const getDayIdentifier = (date: Date) => {\n const year = date.getFullYear();\n const month = String(date.getMonth() + 1).padStart(2, \"0\"); // Months are zero-based in JavaScript\n const day = String(date.getDate()).padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n};\n","/**\n * A list of supported operating systems.\n */\nexport type OperatingSystem =\n | \"Windows\"\n | \"macOS\"\n | \"Linux\"\n | \"iOS\"\n | \"Android\"\n | \"Unknown\";\n\n/**\n * A list of supported browsers.\n */\nexport type Browser = \"Chrome\" | \"Firefox\" | \"Safari\" | \"Edge\" | \"Unknown\";\n\n/**\n * Detects the user's operating system based on the user agent string.\n * Safe for SSR: falls back to \"Unknown\" on server.\n */\nexport const getOperatingSystem = (): OperatingSystem => {\n const ua = (\n typeof navigator === \"undefined\" ? \"\" : navigator.userAgent\n ).toLowerCase();\n\n if (ua.includes(\"windows nt\")) return \"Windows\";\n if (ua.includes(\"iphone\") || ua.includes(\"ipad\") || ua.includes(\"ipod\"))\n return \"iOS\";\n if (ua.includes(\"mac\")) return \"macOS\";\n if (ua.includes(\"android\")) return \"Android\";\n if (ua.includes(\"linux\")) return \"Linux\";\n\n return \"Unknown\";\n};\n\n/**\n * Detects the user's browser based on the user agent string.\n * Safe for SSR: falls back to \"Unknown\" on server.\n */\nexport const getBrowser = (): Browser => {\n const ua = (\n typeof navigator === \"undefined\" ? \"\" : navigator.userAgent\n ).toLowerCase();\n\n // Order matters: Edge must come before Chrome\n if (ua.includes(\"edg\")) return \"Edge\";\n if (ua.includes(\"chrome\")) return \"Chrome\";\n if (ua.includes(\"firefox\")) return \"Firefox\";\n if (ua.includes(\"safari\")) return \"Safari\";\n\n return \"Unknown\";\n};\n\n/**\n * Basic information about the user's system (OS and browser).\n * Safe for SSR: resolves to Unknown values on server.\n */\nexport const systemInfo = {\n os:\n typeof navigator === \"undefined\"\n ? (\"Unknown\" as OperatingSystem)\n : getOperatingSystem(),\n browser:\n typeof navigator === \"undefined\" ? (\"Unknown\" as Browser) : getBrowser(),\n};\n","import type { AppSettings, SystemTheme } from \"../models\";\n\n/**\n * Determines whether dark mode should be enabled based on user settings and system conditions.\n *\n * @param darkMode - User preference: 'light' | 'dark' | 'system' | 'auto'.\n * @param systemTheme - Detected OS-level theme: 'light' | 'dark'.\n * @param backgroundColor - The background color to assess contrast in 'auto' mode.\n * @returns True if dark mode should be used.\n */\nexport const isDarkMode = (\n darkMode: AppSettings[\"darkMode\"],\n systemTheme: SystemTheme,\n): boolean => {\n switch (darkMode) {\n case \"light\": {\n return false;\n }\n case \"dark\": {\n return true;\n }\n case \"system\": {\n return systemTheme === \"dark\";\n }\n default: {\n return false;\n }\n }\n};\n","/**\n * Converts a given date to a human-readable relative time string.\n *\n * @param {Date} date - The date to be converted.\n * @param lang\n * @returns {string} A string representing the relative time using `Intl` format (e.g., \"2 days ago\").\n */\nexport const timeAgo = (date: Date, lang?: string): string => {\n const locale =\n lang ?? (typeof navigator === \"undefined\" ? \"en-US\" : navigator.language);\n // Get the current date and time\n const now = new Date();\n date = new Date(date);\n // Calculate the time difference in seconds\n const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);\n\n // Create an Intl.RelativeTimeFormat instance with the user's language\n const rtf = new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n\n // Determine the appropriate unit and format the result\n if (diffInSeconds < 60) {\n return rtf.format(-diffInSeconds, \"second\");\n }\n if (diffInSeconds < 3600) {\n const minutes = Math.floor(diffInSeconds / 60);\n return rtf.format(-minutes, \"minute\");\n }\n if (diffInSeconds < 86_400) {\n const hours = Math.floor(diffInSeconds / 3600);\n return rtf.format(-hours, \"hour\");\n }\n const days = Math.floor(diffInSeconds / 86_400);\n return rtf.format(-days, \"day\");\n};\n\nexport const timeAgoFromStart = (date: Date, lang?: string): string => {\n const locale =\n lang ?? (typeof navigator === \"undefined\" ? \"en-US\" : navigator.language);\n const now = new Date();\n date = new Date(date);\n const difference = (date.getTime() - now.getTime()) / 1000;\n const differenceHours = Math.floor(difference / (60 * 60));\n const differenceMinutes = Math.floor(\n (difference - 60 * 60 * differenceHours) / 60,\n );\n const diffInSeconds = Math.floor(\n difference - 60 * 60 * differenceHours - 60 * differenceMinutes,\n );\n\n const rtf = new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n\n if (differenceMinutes === 0 && diffInSeconds < 60) {\n return rtf.format(diffInSeconds, \"second\");\n }\n if (differenceHours === 0 && differenceMinutes < 60) {\n return rtf.format(differenceMinutes, \"minute\");\n }\n if (differenceHours < 24) {\n const hours = `${new Intl.RelativeTimeFormat(locale, {\n numeric: \"auto\",\n }).format(differenceHours, \"hour\")}`;\n const minutes = ` ${new Intl.RelativeTimeFormat(locale, {\n localeMatcher: \"lookup\",\n numeric: \"always\",\n style: \"long\",\n }).format(differenceMinutes, \"minute\")}`.replace(/^\\D+/, \"\");\n return `${hours} ${minutes}`;\n }\n\n const days = Math.floor(diffInSeconds / 86_400);\n return rtf.format(days, \"day\");\n};\n","import { useEffect, useState } from \"react\";\n\n/**\n * A custom React hook to determine if the current device is a smaller device\n * based on the screen width.\n * @param [breakpoint=768] - The breakpoint in pixels at which a device is considered \"smaller\".\n * @returns {boolean} - A boolean value indicating whether the current device is a smaller device.\n */\nexport const useResponsiveDisplay = (breakpoint = 768): boolean => {\n const [isSmallerDevice, setIsSmallerDevice] = useState<boolean>(false);\n\n useEffect(() => {\n const checkScreenSize = () => {\n setIsSmallerDevice(window.innerWidth < breakpoint);\n };\n checkScreenSize();\n const handleResize = () => checkScreenSize();\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [breakpoint]);\n\n return isSmallerDevice;\n};\n","import { useState, useEffect } from \"react\";\n\nimport type { SystemTheme } from \"../models\";\n\n/**\n * A React hook to detect the system theme preference.\n * @returns The current system theme ('light', 'dark', or 'unknown').\n */\nexport const useSystemTheme = (): SystemTheme => {\n const [theme, setTheme] = useState<SystemTheme>(\"unknown\");\n useEffect(() => {\n const mediaQueryListener = (e: MediaQueryListEvent) => {\n setTheme(e.matches ? \"dark\" : \"light\");\n };\n\n const prefersDarkScheme = globalThis.matchMedia(\n \"(prefers-color-scheme: dark)\",\n );\n setTheme(prefersDarkScheme.matches ? \"dark\" : \"light\");\n\n // Listen for changes in system theme\n prefersDarkScheme.addEventListener(\"change\", mediaQueryListener);\n\n return () => {\n prefersDarkScheme.removeEventListener(\"change\", mediaQueryListener);\n };\n }, []);\n\n return theme;\n};\n","import { keyframes } from \"@emotion/react\";\n\n/**\n * Fade in from the left with slight movement on the X-axis.\n */\nexport const fadeInLeft = keyframes`\n from {\n opacity: 0;\n transform: translateX(-40px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n`;\n\n/**\n * Simple fade in animation (opacity only).\n */\nexport const fadeIn = keyframes`\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n`;\n\n/**\n * Slide in from the left side of the screen.\n */\nexport const slideIn = keyframes`\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(0);\n }\n`;\n\n/**\n * Slide in from the bottom of the screen.\n */\nexport const slideInBottom = keyframes`\n from {\n transform: translateY(100%);\n }\n to {\n transform: translateY(0);\n }\n`;\n\n/**\n * Scale from 0 to full size.\n */\nexport const scale = keyframes`\n from {\n transform: scale(0);\n }\n to {\n transform: scale(1);\n }\n`;\n\n/**\n * Creates a pulsating animation using scale and box-shadow.\n * Simulates a glowing effect.\n *\n * @param clr - The base color for the shadow in hex format.\n * @param shadowBlur - The maximum spread of the shadow during the pulse (default: 12).\n * @returns Emotion keyframes animation.\n */\nexport const pulseAnimation = (clr: string, shadowBlur = 12) => keyframes`\n 0% {\n transform: scale(0.95);\n box-shadow: 0 0 0 0 ${clr}b2;\n }\n 70% {\n transform: scale(1);\n box-shadow: 0 0 0 ${shadowBlur}px ${clr}00;\n }\n 100% {\n transform: scale(0.95);\n box-shadow: 0 0 0 0 ${clr}00;\n }\n`;\n\n/**\n * Creates a glowing pulse animation using drop-shadow.\n * Used in progress or highlight elements.\n *\n * @param clr - The glow color in hex.\n * @returns Emotion keyframes animation.\n */\nexport const progressPulse = (clr: string) => keyframes`\n 0% {\n filter: none;\n }\n 50% {\n filter: drop-shadow(0 0 10px ${clr}78);\n }\n 100% {\n filter: none;\n }\n`;\n\n/**\n * A bounce-scale animation used during logout transition.\n */\nexport const logoutAnimation = keyframes`\n 0% {\n transform: scale(1);\n opacity: 1;\n }\n 50% {\n transform: scale(0.9) translateX(-2px);\n opacity: 0.7;\n }\n 100% {\n transform: scale(1);\n opacity: 1;\n }\n`;\n\n/**\n * Subtle bounce animation used for install app prompts.\n */\nexport const installAppAnimation = keyframes`\n 0% {\n transform: translateY(0);\n }\n 30% {\n transform: translateY(-5px);\n }\n 50% {\n transform: translateY(2px);\n }\n 70% {\n transform: translateY(-2px);\n }\n 100% {\n transform: translateY(0);\n }\n`;\n"],"mappings":"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