Switch to "colorSchemes" from "palette" API in mui theme
"colorSchemes" is the preferred api starting from mui v6
This commit is contained in:
@@ -41,6 +41,7 @@
|
|||||||
"@mui/icons-material": "6.3.0",
|
"@mui/icons-material": "6.3.0",
|
||||||
"@mui/material": "6.3.0",
|
"@mui/material": "6.3.0",
|
||||||
"@mui/system": "6.3.0",
|
"@mui/system": "6.3.0",
|
||||||
|
"@mui/utils": "^6.4.0",
|
||||||
"@mui/x-date-pickers": "7.23.3",
|
"@mui/x-date-pickers": "7.23.3",
|
||||||
"apollo-upload-client": "18.0.1",
|
"apollo-upload-client": "18.0.1",
|
||||||
"csstype": "3.1.3",
|
"csstype": "3.1.3",
|
||||||
|
|||||||
@@ -1034,10 +1034,7 @@
|
|||||||
"invalid_json": "Invalid json",
|
"invalid_json": "Invalid json",
|
||||||
"invalid_name": "The theme name must be unique and has a limit of 16 characters"
|
"invalid_name": "The theme name must be unique and has a limit of 16 characters"
|
||||||
},
|
},
|
||||||
"info": {
|
"description": "See the official <0>MUI documentation</0> for how to customize the theme.\nThe palette API is not supported, you have to use the new <1>color schemes API</1>.\n\nYou can use theme creators like <2>MUI Theme Creator</2>, however, you have to adjust the created object according to the new color schemes API.\n\nIn case no background is defined, a background will be calculated based on the primary color",
|
||||||
"creating_theme": "Create a custom theme with the <2>MUI Theme Creator</2>, copy everything after \"themeOptions\" from \"{\" to \"}\" and paste it into the \"theme\" text field.",
|
|
||||||
"theme_mode_lock": "In case \"mode\" or \"type\" is defined in the \"palette\" object, the theme will be locked into this mode and will not change regardless of the selected apps theme mode"
|
|
||||||
},
|
|
||||||
"theme_name": "Name"
|
"theme_name": "Name"
|
||||||
},
|
},
|
||||||
"failure": "Could not create theme",
|
"failure": "Could not create theme",
|
||||||
|
|||||||
15
src/lib/mui/MuiStyles.types.d.ts
vendored
Normal file
15
src/lib/mui/MuiStyles.types.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import '@mui/material/styles';
|
||||||
|
|
||||||
|
declare module '@mui/material/styles' {
|
||||||
|
interface CssThemeVariables {
|
||||||
|
enabled: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,9 @@ export class Storage {
|
|||||||
return this.parseValue(this.getItem(key), defaultValue);
|
return this.parseValue(this.getItem(key), defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
setItem(key: string, value: unknown, emitEvent: boolean = true): void {
|
setItem(key: string, value: unknown, emitEvent: boolean = true, stringify: boolean = true): void {
|
||||||
|
const currentValue = this.getItem(key);
|
||||||
|
|
||||||
const fireEvent = (valueToStore: string | undefined) => {
|
const fireEvent = (valueToStore: string | undefined) => {
|
||||||
if (!emitEvent) {
|
if (!emitEvent) {
|
||||||
return;
|
return;
|
||||||
@@ -35,7 +37,7 @@ export class Storage {
|
|||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new StorageEvent('storage', {
|
new StorageEvent('storage', {
|
||||||
key,
|
key,
|
||||||
oldValue: this.getItem(key),
|
oldValue: currentValue,
|
||||||
newValue: valueToStore,
|
newValue: valueToStore,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -47,10 +49,10 @@ export class Storage {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueToStore = JSON.stringify(value);
|
const valueToStore = stringify ? JSON.stringify(value) : value;
|
||||||
|
|
||||||
this.storage.setItem(key, valueToStore);
|
this.storage.setItem(key, valueToStore as string);
|
||||||
fireEvent(valueToStore);
|
fireEvent(valueToStore as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Direction, StyledEngineProvider, ThemeProvider } from '@mui/material/styles';
|
import { Direction, StyledEngineProvider, ThemeProvider, useColorScheme } from '@mui/material/styles';
|
||||||
import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { BrowserRouter as Router } from 'react-router-dom';
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
import { QueryParamProvider } from 'use-query-params';
|
import { QueryParamProvider } from 'use-query-params';
|
||||||
@@ -58,6 +58,9 @@ export const AppContext: React.FC<Props> = ({ children }) => {
|
|||||||
const [themeMode, setThemeMode] = useLocalStorage<ThemeMode>('themeMode', ThemeMode.SYSTEM);
|
const [themeMode, setThemeMode] = useLocalStorage<ThemeMode>('themeMode', ThemeMode.SYSTEM);
|
||||||
const [pureBlackMode, setPureBlackMode] = useLocalStorage<boolean>('pureBlackMode', false);
|
const [pureBlackMode, setPureBlackMode] = useLocalStorage<boolean>('pureBlackMode', false);
|
||||||
|
|
||||||
|
const { mode } = useColorScheme();
|
||||||
|
const actualThemeMode = mode ?? themeMode ?? 'dark';
|
||||||
|
|
||||||
const darkThemeContext = useMemo(
|
const darkThemeContext = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
appTheme,
|
appTheme,
|
||||||
@@ -71,8 +74,14 @@ export const AppContext: React.FC<Props> = ({ children }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const theme = useMemo(
|
const theme = useMemo(
|
||||||
() => createAndSetTheme(themeMode, getTheme(appTheme, customThemes), pureBlackMode, currentDirection),
|
() =>
|
||||||
[themeMode, currentDirection, systemThemeMode, pureBlackMode, appTheme, customThemes],
|
createAndSetTheme(
|
||||||
|
actualThemeMode as ThemeMode,
|
||||||
|
getTheme(appTheme, customThemes),
|
||||||
|
pureBlackMode,
|
||||||
|
currentDirection,
|
||||||
|
),
|
||||||
|
[actualThemeMode, currentDirection, systemThemeMode, pureBlackMode, appTheme, customThemes],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export class MediaQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static getThemeMode(): Exclude<ThemeMode, 'system'> {
|
static getThemeMode(): Exclude<ThemeMode, 'system'> {
|
||||||
const themeMode = AppStorage.local.getItemParsed<ThemeMode>('themeMode', ThemeMode.SYSTEM);
|
const themeMode = AppStorage.local.getItem('mui-mode') as ThemeMode;
|
||||||
|
|
||||||
const isSystemMode = themeMode === ThemeMode.SYSTEM;
|
const isSystemMode = themeMode === ThemeMode.SYSTEM;
|
||||||
if (isSystemMode) {
|
if (isSystemMode) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import MenuItem from '@mui/material/MenuItem';
|
|||||||
import ListSubheader from '@mui/material/ListSubheader';
|
import ListSubheader from '@mui/material/ListSubheader';
|
||||||
import Switch from '@mui/material/Switch';
|
import Switch from '@mui/material/Switch';
|
||||||
import Link from '@mui/material/Link';
|
import Link from '@mui/material/Link';
|
||||||
|
import { useColorScheme } from '@mui/material/styles';
|
||||||
import { NavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
|
import { NavBarContext } from '@/modules/navigation-bar/contexts/NavbarContext.tsx';
|
||||||
import { ThemeMode, ThemeModeContext } from '@/modules/theme/contexts/ThemeModeContext.tsx';
|
import { ThemeMode, ThemeModeContext } from '@/modules/theme/contexts/ThemeModeContext.tsx';
|
||||||
import { Select } from '@/modules/core/components/inputs/Select.tsx';
|
import { Select } from '@/modules/core/components/inputs/Select.tsx';
|
||||||
@@ -23,7 +24,6 @@ import { NumberSetting } from '@/modules/core/components/settings/NumberSetting.
|
|||||||
import { useLocalStorage } from '@/modules/core/hooks/useStorage.tsx';
|
import { useLocalStorage } from '@/modules/core/hooks/useStorage.tsx';
|
||||||
import { I18nResourceCode, i18nResources } from '@/i18n';
|
import { I18nResourceCode, i18nResources } from '@/i18n';
|
||||||
import { langCodeToName } from '@/modules/core/utils/Languages.ts';
|
import { langCodeToName } from '@/modules/core/utils/Languages.ts';
|
||||||
import { getTheme } from '@/modules/theme/services/AppThemes.ts';
|
|
||||||
import { ThemeList } from '@/modules/theme/components/ThemeList.tsx';
|
import { ThemeList } from '@/modules/theme/components/ThemeList.tsx';
|
||||||
import {
|
import {
|
||||||
createUpdateMetadataServerSettings,
|
createUpdateMetadataServerSettings,
|
||||||
@@ -35,10 +35,13 @@ import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts'
|
|||||||
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
import { makeToast } from '@/modules/core/utils/Toast.ts';
|
||||||
import { MetadataThemeSettings } from '@/modules/theme/AppTheme.types.ts';
|
import { MetadataThemeSettings } from '@/modules/theme/AppTheme.types.ts';
|
||||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||||
|
import { AppStorage } from '@/lib/storage/AppStorage.ts';
|
||||||
|
|
||||||
export const Appearance = () => {
|
export const Appearance = () => {
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const { themeMode, setThemeMode, pureBlackMode, setPureBlackMode, appTheme } = useContext(ThemeModeContext);
|
const { themeMode, setThemeMode, pureBlackMode, setPureBlackMode } = useContext(ThemeModeContext);
|
||||||
|
const { mode, setMode } = useColorScheme();
|
||||||
|
const actualThemeMode = (mode ?? themeMode) as ThemeMode;
|
||||||
|
|
||||||
const { setTitle, setAction } = useContext(NavBarContext);
|
const { setTitle, setAction } = useContext(NavBarContext);
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
@@ -59,8 +62,7 @@ export const Appearance = () => {
|
|||||||
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
|
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const isDarkMode =
|
const isDarkMode = MediaQuery.getThemeMode() === ThemeMode.DARK;
|
||||||
getTheme(appTheme).muiTheme.palette?.mode === 'dark' || MediaQuery.getThemeMode() === ThemeMode.DARK;
|
|
||||||
|
|
||||||
const DEFAULT_ITEM_WIDTH = 300;
|
const DEFAULT_ITEM_WIDTH = 300;
|
||||||
const [itemWidth, setItemWidth] = useLocalStorage<number>('ItemWidth', DEFAULT_ITEM_WIDTH);
|
const [itemWidth, setItemWidth] = useLocalStorage<number>('ItemWidth', DEFAULT_ITEM_WIDTH);
|
||||||
@@ -89,7 +91,17 @@ export const Appearance = () => {
|
|||||||
>
|
>
|
||||||
<ListItem>
|
<ListItem>
|
||||||
<ListItemText primary={t('settings.appearance.theme.mode')} />
|
<ListItemText primary={t('settings.appearance.theme.mode')} />
|
||||||
<Select<ThemeMode> value={themeMode} onChange={(e) => setThemeMode(e.target.value as ThemeMode)}>
|
<Select<ThemeMode>
|
||||||
|
value={actualThemeMode}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newMode = e.target.value as 'system' | 'light' | 'dark';
|
||||||
|
|
||||||
|
setThemeMode(newMode as ThemeMode);
|
||||||
|
setMode(newMode);
|
||||||
|
// in case a non "colorSchemes" mui theme is active, "setMode" does not update the mode ("mui-mode") value
|
||||||
|
AppStorage.local.setItem('mui-mode', newMode, true, false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<MenuItem key={ThemeMode.SYSTEM} value={ThemeMode.SYSTEM}>
|
<MenuItem key={ThemeMode.SYSTEM} value={ThemeMode.SYSTEM}>
|
||||||
System
|
System
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@@ -6,16 +6,32 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ThemeOptions } from '@mui/material/styles';
|
import { CssVarsThemeOptions } from '@mui/material/styles';
|
||||||
import { t as translate } from 'i18next';
|
import { t as translate } from 'i18next';
|
||||||
|
|
||||||
export type TBaseTheme = { isCustom: boolean; getName: () => string; muiTheme: ThemeOptions };
|
export type TBaseTheme = {
|
||||||
|
isCustom: boolean;
|
||||||
|
getName: () => string;
|
||||||
|
muiTheme: Omit<CssVarsThemeOptions, 'activeMode'>;
|
||||||
|
};
|
||||||
|
|
||||||
export const themes = {
|
export const themes = {
|
||||||
default: {
|
default: {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('global.label.default'),
|
getName: () => translate('global.label.default'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#5b74ef',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#efd65b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#5b74ef',
|
main: '#5b74ef',
|
||||||
@@ -26,10 +42,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
lavender: {
|
lavender: {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.lavender'),
|
getName: () => translate('settings.appearance.theme.themes.lavender'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#5c58dc',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#d8dc58',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#5c58dc',
|
main: '#5c58dc',
|
||||||
@@ -40,10 +70,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
dune: {
|
dune: {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.dune'),
|
getName: () => translate('settings.appearance.theme.themes.dune'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#897869',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#697a89',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#897869',
|
main: '#897869',
|
||||||
@@ -54,10 +98,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
rosegold: {
|
rosegold: {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.rosegold'),
|
getName: () => translate('settings.appearance.theme.themes.rosegold'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#E9A7A1',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#A1E3E9',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#E9A7A1',
|
main: '#E9A7A1',
|
||||||
@@ -68,10 +126,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
'forest dew': {
|
'forest dew': {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.forest_dew'),
|
getName: () => translate('settings.appearance.theme.themes.forest_dew'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#53a584',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#a55374',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#53a584',
|
main: '#53a584',
|
||||||
@@ -82,10 +154,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
'montain sunset': {
|
'montain sunset': {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.mountain_sunset'),
|
getName: () => translate('settings.appearance.theme.themes.mountain_sunset'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#c55a77',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#5ac5a8',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#c55a77',
|
main: '#c55a77',
|
||||||
@@ -96,10 +182,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
crimson: {
|
crimson: {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.crimson'),
|
getName: () => translate('settings.appearance.theme.themes.crimson'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#DC143C',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#14DCB4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#DC143C',
|
main: '#DC143C',
|
||||||
@@ -110,10 +210,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
'minty miracles': {
|
'minty miracles': {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.minty_miracles'),
|
getName: () => translate('settings.appearance.theme.themes.minty_miracles'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#5CE6A1',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#E65CA1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#5CE6A1',
|
main: '#5CE6A1',
|
||||||
@@ -124,10 +238,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
'orange juice': {
|
'orange juice': {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.orange_juice'),
|
getName: () => translate('settings.appearance.theme.themes.orange_juice'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#ffb546',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#4690ff',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#ffb546',
|
main: '#ffb546',
|
||||||
@@ -138,10 +266,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
'bright pink': {
|
'bright pink': {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.bright_pink'),
|
getName: () => translate('settings.appearance.theme.themes.bright_pink'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#FF007F',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#00FF80',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#FF007F',
|
main: '#FF007F',
|
||||||
@@ -152,10 +294,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
veronica: {
|
veronica: {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.veronica'),
|
getName: () => translate('settings.appearance.theme.themes.veronica'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#A020F0',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#70F020',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#A020F0',
|
main: '#A020F0',
|
||||||
@@ -166,10 +322,24 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
'tree frog green': {
|
'tree frog green': {
|
||||||
isCustom: false,
|
isCustom: false,
|
||||||
getName: () => translate('settings.appearance.theme.themes.tree_frog_green'),
|
getName: () => translate('settings.appearance.theme.themes.tree_frog_green'),
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#8ace31',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#7531CE',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dark: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#8ace31',
|
main: '#8ace31',
|
||||||
@@ -180,4 +350,6 @@ export const themes = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const satisfies Record<string, TBaseTheme>;
|
} as const satisfies Record<string, TBaseTheme>;
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ const baseCustomTheme: AppTheme = {
|
|||||||
isCustom: true,
|
isCustom: true,
|
||||||
getName: () => '',
|
getName: () => '',
|
||||||
muiTheme: {
|
muiTheme: {
|
||||||
|
colorSchemes: {
|
||||||
|
light: {
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#1976d2',
|
main: '#1976d2',
|
||||||
@@ -46,6 +48,18 @@ const baseCustomTheme: AppTheme = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dark: {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#1976d2',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#9c27b0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
type DialogMode = 'create' | 'edit';
|
type DialogMode = 'create' | 'edit';
|
||||||
@@ -115,10 +129,23 @@ export const ThemeCreationDialog = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!error && (
|
{!error && (
|
||||||
<Stack sx={{ gap: 2 }}>
|
<Stack sx={{ gap: 2, whiteSpace: 'pre-line' }}>
|
||||||
<Typography>
|
<Typography>
|
||||||
<Trans i18nKey="settings.appearance.theme.create.dialog.info.creating_theme">
|
<Trans i18nKey="settings.appearance.theme.create.dialog.description">
|
||||||
Create a custom theme with the{' '}
|
<Link
|
||||||
|
href="https://mui.com/material-ui/customization/how-to-customize/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
MUI documentation
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="https://mui.com/material-ui/customization/palette/#color-schemes"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
MUI documentation
|
||||||
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="https://zenoo.github.io/mui-theme-creator/"
|
href="https://zenoo.github.io/mui-theme-creator/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@@ -130,7 +157,6 @@ export const ThemeCreationDialog = ({
|
|||||||
" and paste it into the "theme" text field.
|
" and paste it into the "theme" text field.
|
||||||
</Trans>
|
</Trans>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography>{t('settings.appearance.theme.create.dialog.info.theme_mode_lock')}</Typography>
|
|
||||||
<TextField
|
<TextField
|
||||||
disabled={mode === 'edit'}
|
disabled={mode === 'edit'}
|
||||||
label={t('settings.appearance.theme.create.dialog.theme_name')}
|
label={t('settings.appearance.theme.create.dialog.theme_name')}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export const ThemeList = () => {
|
|||||||
{allThemes.map((theme) => (
|
{allThemes.map((theme) => (
|
||||||
<ThemePreview
|
<ThemePreview
|
||||||
key={theme.id}
|
key={theme.id}
|
||||||
theme={theme}
|
appTheme={theme}
|
||||||
onDelete={() => {
|
onDelete={() => {
|
||||||
makeToast(t('settings.appearance.theme.delete.action', { theme: theme.getName() }), 'info');
|
makeToast(t('settings.appearance.theme.delete.action', { theme: theme.getName() }), 'info');
|
||||||
updateCustomThemes(
|
updateCustomThemes(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useContext, useMemo } from 'react';
|
import { useContext, useMemo } from 'react';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import { styled, ThemeProvider } from '@mui/material/styles';
|
import { styled, ThemeProvider, useTheme } from '@mui/material/styles';
|
||||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||||
import Card from '@mui/material/Card';
|
import Card from '@mui/material/Card';
|
||||||
import CardActionArea from '@mui/material/CardActionArea';
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
@@ -32,17 +32,21 @@ const ThemePreviewBadge = styled(Box)(() => ({
|
|||||||
height: '20px',
|
height: '20px',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const ThemePreview = ({ theme, onDelete }: { theme: AppTheme; onDelete: () => void }) => {
|
export const ThemePreview = ({ appTheme, onDelete }: { appTheme: AppTheme; onDelete: () => void }) => {
|
||||||
const { getName } = theme;
|
const { getName } = appTheme;
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { themeMode, setAppTheme, appTheme, pureBlackMode } = useContext(ThemeModeContext);
|
const theme = useTheme();
|
||||||
|
const { themeMode, setAppTheme, appTheme: activeAppTheme, pureBlackMode } = useContext(ThemeModeContext);
|
||||||
|
|
||||||
const popupState = usePopupState({ variant: 'popover', popupId: `theme-edit-dialog-${theme.id}` });
|
const popupState = usePopupState({ variant: 'popover', popupId: `theme-edit-dialog-${appTheme.id}` });
|
||||||
|
|
||||||
const isSelected = theme.id === appTheme;
|
const isSelected = appTheme.id === activeAppTheme;
|
||||||
|
|
||||||
const muiTheme = useMemo(() => createTheme(themeMode, theme, pureBlackMode), [theme, themeMode, pureBlackMode]);
|
const muiTheme = useMemo(
|
||||||
|
() => createTheme(themeMode, appTheme, pureBlackMode),
|
||||||
|
[appTheme, themeMode, pureBlackMode],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -71,12 +75,15 @@ export const ThemePreview = ({ theme, onDelete }: { theme: AppTheme; onDelete: (
|
|||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
border: '4px solid',
|
border: '4px solid',
|
||||||
borderColor: isSelected
|
borderColor: isSelected ? muiTheme.palette.primary.light : muiTheme.palette.grey['100'],
|
||||||
? muiTheme.palette.primary.light
|
|
||||||
: muiTheme.palette.background.paper,
|
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
borderRadius: 1,
|
borderRadius: 1,
|
||||||
|
...theme.applyStyles('dark', {
|
||||||
|
borderColor: isSelected
|
||||||
|
? muiTheme.palette.primary.light
|
||||||
|
: muiTheme.palette.grey['900'],
|
||||||
|
}),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<CardActionArea
|
<CardActionArea
|
||||||
@@ -85,15 +92,15 @@ export const ThemePreview = ({ theme, onDelete }: { theme: AppTheme; onDelete: (
|
|||||||
backgroundColor: 'background.default',
|
backgroundColor: 'background.default',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const needToLoadFonts = hasMissingFonts(theme.muiTheme);
|
const needToLoadFonts = hasMissingFonts(appTheme.muiTheme);
|
||||||
if (!needToLoadFonts) {
|
if (!needToLoadFonts) {
|
||||||
setAppTheme(theme.id);
|
setAppTheme(appTheme.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeToast(t('settings.appearance.theme.select.fonts.loading'), 'info');
|
makeToast(t('settings.appearance.theme.select.fonts.loading'), 'info');
|
||||||
loadThemeFonts(theme.muiTheme)
|
loadThemeFonts(appTheme.muiTheme)
|
||||||
.then(() => setAppTheme(theme.id))
|
.then(() => setAppTheme(appTheme.id))
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
makeToast(
|
makeToast(
|
||||||
t('settings.appearance.theme.select.fonts.error'),
|
t('settings.appearance.theme.select.fonts.error'),
|
||||||
@@ -136,7 +143,7 @@ export const ThemePreview = ({ theme, onDelete }: { theme: AppTheme; onDelete: (
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!isSelected && theme.isCustom && (
|
{!isSelected && appTheme.isCustom && (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -152,7 +159,7 @@ export const ThemePreview = ({ theme, onDelete }: { theme: AppTheme; onDelete: (
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
{theme.isCustom && (
|
{appTheme.isCustom && (
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -194,7 +201,7 @@ export const ThemePreview = ({ theme, onDelete }: { theme: AppTheme; onDelete: (
|
|||||||
<Stack
|
<Stack
|
||||||
sx={{
|
sx={{
|
||||||
height: '25%',
|
height: '25%',
|
||||||
backgroundColor: muiTheme.palette.background.paper,
|
backgroundColor: 'background.paper',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
gap: 2,
|
gap: 2,
|
||||||
@@ -226,7 +233,7 @@ export const ThemePreview = ({ theme, onDelete }: { theme: AppTheme; onDelete: (
|
|||||||
<TypographyMaxLines sx={{ maxWidth: '100%' }}>{getName()}</TypographyMaxLines>
|
<TypographyMaxLines sx={{ maxWidth: '100%' }}>{getName()}</TypographyMaxLines>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Stack>
|
</Stack>
|
||||||
<ThemeCreationDialog bindDialogProps={bindDialog(popupState)} mode="edit" appTheme={theme} />
|
<ThemeCreationDialog bindDialogProps={bindDialog(popupState)} mode="edit" appTheme={appTheme} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ThemeOptions } from '@mui/material/styles';
|
import { CssVarsThemeOptions } from '@mui/material/styles';
|
||||||
import WebFont from 'webfontloader';
|
import WebFont from 'webfontloader';
|
||||||
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
||||||
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
|
import { ControlledPromise } from '@/lib/ControlledPromise.ts';
|
||||||
@@ -66,12 +66,12 @@ const getFontsFromTheme = (obj: Record<string, any>, fonts: string[] = []): stri
|
|||||||
|
|
||||||
const loadedFonts: string[] = [];
|
const loadedFonts: string[] = [];
|
||||||
|
|
||||||
export const hasMissingFonts = (theme: ThemeOptions): boolean => {
|
export const hasMissingFonts = (theme: CssVarsThemeOptions): boolean => {
|
||||||
const themeFonts = getFontsFromTheme(theme.typography ?? {});
|
const themeFonts = getFontsFromTheme(theme.typography ?? {});
|
||||||
return !!themeFonts.length && themeFonts.some((font) => !loadedFonts.includes(font));
|
return !!themeFonts.length && themeFonts.some((font) => !loadedFonts.includes(font));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loadThemeFonts = async (theme: ThemeOptions): Promise<void> => {
|
export const loadThemeFonts = async (theme: CssVarsThemeOptions): Promise<void> => {
|
||||||
const themeFonts = getFontsFromTheme(theme.typography ?? {});
|
const themeFonts = getFontsFromTheme(theme.typography ?? {});
|
||||||
const missingThemeFonts = themeFonts.filter((font) => !loadedFonts.includes(font));
|
const missingThemeFonts = themeFonts.filter((font) => !loadedFonts.includes(font));
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,16 @@ import {
|
|||||||
darken,
|
darken,
|
||||||
Direction,
|
Direction,
|
||||||
lighten,
|
lighten,
|
||||||
Palette,
|
|
||||||
responsiveFontSizes,
|
responsiveFontSizes,
|
||||||
Theme,
|
Theme,
|
||||||
|
TypeBackground,
|
||||||
useTheme,
|
useTheme,
|
||||||
} from '@mui/material/styles';
|
} from '@mui/material/styles';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies,no-restricted-imports
|
||||||
|
import { deepmerge } from '@mui/utils';
|
||||||
|
// eslint-disable-next-line no-restricted-imports
|
||||||
|
import { PaletteBackgroundChannel } from '@mui/material/styles/createThemeWithVars';
|
||||||
import { ThemeMode } from '@/modules/theme/contexts/ThemeModeContext.tsx';
|
import { ThemeMode } from '@/modules/theme/contexts/ThemeModeContext.tsx';
|
||||||
import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx';
|
import { MediaQuery } from '@/modules/core/utils/MediaQuery.tsx';
|
||||||
import { AppTheme, loadThemeFonts } from '@/modules/theme/services/AppThemes.ts';
|
import { AppTheme, loadThemeFonts } from '@/modules/theme/services/AppThemes.ts';
|
||||||
@@ -25,6 +29,56 @@ import { applyStyles } from '@/modules/core/utils/ApplyStyles.ts';
|
|||||||
|
|
||||||
const SCROLLBAR_SIZE = 14;
|
const SCROLLBAR_SIZE = 14;
|
||||||
|
|
||||||
|
declare module '@mui/material/styles' {
|
||||||
|
interface CssThemeVariables {
|
||||||
|
enabled: true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBackgroundColor = (
|
||||||
|
type: 'light' | 'dark',
|
||||||
|
appTheme: AppTheme,
|
||||||
|
theme: Theme,
|
||||||
|
setPureBlackMode: boolean = false,
|
||||||
|
): (Partial<TypeBackground> & Partial<PaletteBackgroundChannel>) | undefined => {
|
||||||
|
if (setPureBlackMode) {
|
||||||
|
return {
|
||||||
|
paper: '#111',
|
||||||
|
default: '#000',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'light' && !!theme.colorSchemes.light) {
|
||||||
|
if (
|
||||||
|
typeof appTheme.muiTheme.colorSchemes?.light === 'object' &&
|
||||||
|
appTheme.muiTheme.colorSchemes.light.palette?.background
|
||||||
|
) {
|
||||||
|
return appTheme.muiTheme.colorSchemes.light.palette.background;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
paper: lighten(theme.colorSchemes.light.palette.primary.dark, 0.8),
|
||||||
|
default: lighten(theme.colorSchemes.light.palette.primary.dark, 0.9),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'dark' && !!theme.colorSchemes.dark) {
|
||||||
|
if (
|
||||||
|
typeof appTheme.muiTheme.colorSchemes?.dark === 'object' &&
|
||||||
|
appTheme.muiTheme.colorSchemes.dark.palette?.background
|
||||||
|
) {
|
||||||
|
return appTheme.muiTheme.colorSchemes.dark.palette.background;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
paper: darken(theme.colorSchemes.dark.palette.primary.dark, 0.8),
|
||||||
|
default: darken(theme.colorSchemes.dark.palette.primary.dark, 0.9),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
export const createTheme = (
|
export const createTheme = (
|
||||||
themeMode: ThemeMode,
|
themeMode: ThemeMode,
|
||||||
appTheme: AppTheme,
|
appTheme: AppTheme,
|
||||||
@@ -33,50 +87,32 @@ export const createTheme = (
|
|||||||
) => {
|
) => {
|
||||||
const systemMode = MediaQuery.getSystemThemeMode();
|
const systemMode = MediaQuery.getSystemThemeMode();
|
||||||
|
|
||||||
const appThemeType = (appTheme.muiTheme.palette as any)?.type ?? appTheme.muiTheme.palette?.mode;
|
const mode = themeMode === ThemeMode.SYSTEM ? systemMode : themeMode;
|
||||||
const isStaticThemeMode = !!appThemeType;
|
|
||||||
const appThemeMode = appThemeType === 'dark' ? ThemeMode.DARK : ThemeMode.LIGHT;
|
|
||||||
const staticThemeMode = isStaticThemeMode ? appThemeMode : undefined;
|
|
||||||
|
|
||||||
const mode = staticThemeMode ?? (themeMode === ThemeMode.SYSTEM ? systemMode : themeMode);
|
|
||||||
const isDarkMode = mode === ThemeMode.DARK;
|
const isDarkMode = mode === ThemeMode.DARK;
|
||||||
const setPureBlackMode = isDarkMode && pureBlackMode;
|
const setPureBlackMode = isDarkMode && pureBlackMode;
|
||||||
|
|
||||||
const baseTheme = createMuiTheme({
|
const themeForColors = createMuiTheme({ ...appTheme.muiTheme, defaultColorScheme: mode });
|
||||||
|
|
||||||
|
const suwayomiTheme = createMuiTheme(
|
||||||
|
deepmerge(appTheme.muiTheme, {
|
||||||
|
defaultColorScheme: mode,
|
||||||
direction,
|
direction,
|
||||||
...appTheme.muiTheme,
|
colorSchemes: {
|
||||||
|
light: appTheme.muiTheme.colorSchemes?.light
|
||||||
|
? {
|
||||||
palette: {
|
palette: {
|
||||||
mode,
|
background: getBackgroundColor('light', appTheme, themeForColors),
|
||||||
...(appTheme.muiTheme.palette ?? {}),
|
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
: undefined,
|
||||||
const backgroundTrueBlack: Palette['background'] = {
|
dark: appTheme.muiTheme.colorSchemes?.dark
|
||||||
paper: '#111',
|
? {
|
||||||
default: '#000',
|
|
||||||
};
|
|
||||||
const backgroundDark: Palette['background'] = {
|
|
||||||
paper: darken(baseTheme.palette.primary.dark, 0.75),
|
|
||||||
default: darken(baseTheme.palette.primary.dark, 0.85),
|
|
||||||
};
|
|
||||||
const backgroundLight: Palette['background'] = {
|
|
||||||
paper: lighten(baseTheme.palette.primary.dark, 0.8),
|
|
||||||
default: lighten(baseTheme.palette.primary.dark, 0.9),
|
|
||||||
};
|
|
||||||
const backgroundThemeMode = isDarkMode ? backgroundDark : backgroundLight;
|
|
||||||
const automaticBackground = setPureBlackMode ? backgroundTrueBlack : backgroundThemeMode;
|
|
||||||
const appThemeBackground = appTheme.muiTheme.palette?.background;
|
|
||||||
|
|
||||||
const requiresAutomaticBackground = setPureBlackMode || !appThemeBackground;
|
|
||||||
const background = requiresAutomaticBackground ? automaticBackground : appThemeBackground;
|
|
||||||
|
|
||||||
const colorTheme = createMuiTheme(baseTheme, {
|
|
||||||
palette: {
|
palette: {
|
||||||
background,
|
background: getBackgroundColor('dark', appTheme, themeForColors, setPureBlackMode),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
});
|
|
||||||
|
|
||||||
const suwayomiTheme = createMuiTheme(colorTheme, {
|
|
||||||
components: {
|
components: {
|
||||||
...appTheme.muiTheme.components,
|
...appTheme.muiTheme.components,
|
||||||
MuiUseMediaQuery: {
|
MuiUseMediaQuery: {
|
||||||
@@ -98,16 +134,19 @@ export const createTheme = (
|
|||||||
'*::-webkit-scrollbar'
|
'*::-webkit-scrollbar'
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
'*::-webkit-scrollbar-thumb': applyStyles(CSS.supports('-webkit-touch-callout', 'none'), {
|
'*::-webkit-scrollbar-thumb': applyStyles(
|
||||||
|
CSS.supports('-webkit-touch-callout', 'none'),
|
||||||
|
{
|
||||||
border: '4px solid rgba(0, 0, 0, 0)',
|
border: '4px solid rgba(0, 0, 0, 0)',
|
||||||
backgroundClip: 'padding-box',
|
backgroundClip: 'padding-box',
|
||||||
borderRadius: '9999px',
|
borderRadius: '9999px',
|
||||||
backgroundColor: `${colorTheme.palette.primary[isDarkMode ? 'dark' : 'light']}`,
|
backgroundColor: `${themeForColors.palette.primary[isDarkMode ? 'dark' : 'light']}`,
|
||||||
// @ts-ignore - '*::-webkit-scrollbar-thumb' is a valid key
|
// @ts-ignore - '*::-webkit-scrollbar-thumb' is a valid key
|
||||||
...appTheme.muiTheme.components?.MuiCssBaseline?.styleOverrides?.[
|
...appTheme.muiTheme.components?.MuiCssBaseline?.styleOverrides?.[
|
||||||
'*::-webkit-scrollbar-thumb'
|
'*::-webkit-scrollbar-thumb'
|
||||||
],
|
],
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
'*::-webkit-scrollbar-thumb:hover': applyStyles(
|
'*::-webkit-scrollbar-thumb:hover': applyStyles(
|
||||||
CSS.supports('-webkit-touch-callout', 'none'),
|
CSS.supports('-webkit-touch-callout', 'none'),
|
||||||
{
|
{
|
||||||
@@ -130,7 +169,7 @@ export const createTheme = (
|
|||||||
border: 4px solid rgba(0, 0, 0, 0);
|
border: 4px solid rgba(0, 0, 0, 0);
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
background-color: ${colorTheme.palette.primary[isDarkMode ? 'dark' : 'light']};
|
background-color: ${themeForColors.palette.primary[isDarkMode ? 'dark' : 'light']};
|
||||||
}
|
}
|
||||||
*::-webkit-scrollbar-thumb:hover {
|
*::-webkit-scrollbar-thumb:hover {
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
@@ -141,7 +180,8 @@ export const createTheme = (
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return responsiveFontSizes(suwayomiTheme);
|
return responsiveFontSizes(suwayomiTheme);
|
||||||
};
|
};
|
||||||
|
|||||||
17
yarn.lock
17
yarn.lock
@@ -2409,6 +2409,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.20.tgz#16d8c9178b42b62ba95bbedbda8f343feb373d1e"
|
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.20.tgz#16d8c9178b42b62ba95bbedbda8f343feb373d1e"
|
||||||
integrity sha512-straFHD7L8v05l/N5vcWk+y7eL9JF0C2mtph/y4BPm3gn2Eh61dDwDB65pa8DLss3WJfDXYC7Kx5yjP0EmXpgw==
|
integrity sha512-straFHD7L8v05l/N5vcWk+y7eL9JF0C2mtph/y4BPm3gn2Eh61dDwDB65pa8DLss3WJfDXYC7Kx5yjP0EmXpgw==
|
||||||
|
|
||||||
|
"@mui/types@^7.2.21":
|
||||||
|
version "7.2.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.21.tgz#63f50874eda8e4a021a69aaa8ba9597369befda2"
|
||||||
|
integrity sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==
|
||||||
|
|
||||||
"@mui/utils@^5.16.6 || ^6.0.0", "@mui/utils@^6.3.0":
|
"@mui/utils@^5.16.6 || ^6.0.0", "@mui/utils@^6.3.0":
|
||||||
version "6.3.0"
|
version "6.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.3.0.tgz#76c80cb663c7c1bdf782de50a5c3f6ff8bf38e21"
|
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.3.0.tgz#76c80cb663c7c1bdf782de50a5c3f6ff8bf38e21"
|
||||||
@@ -2421,6 +2426,18 @@
|
|||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
react-is "^19.0.0"
|
react-is "^19.0.0"
|
||||||
|
|
||||||
|
"@mui/utils@^6.4.0":
|
||||||
|
version "6.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-6.4.0.tgz#817d8135794b8741ad0267b90dc656361f1c0030"
|
||||||
|
integrity sha512-woOTATWNsTNR3YBh2Ixkj3l5RaxSiGoC9G8gOpYoFw1mZM77LWJeuMHFax7iIW4ahK0Cr35TF9DKtrafJmOmNQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.26.0"
|
||||||
|
"@mui/types" "^7.2.21"
|
||||||
|
"@types/prop-types" "^15.7.14"
|
||||||
|
clsx "^2.1.1"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
react-is "^19.0.0"
|
||||||
|
|
||||||
"@mui/x-date-pickers@7.23.3":
|
"@mui/x-date-pickers@7.23.3":
|
||||||
version "7.23.3"
|
version "7.23.3"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-7.23.3.tgz#0c2b3db6a99e6e38aeb0980bfdf98468f415dded"
|
resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-7.23.3.tgz#0c2b3db6a99e6e38aeb0980bfdf98468f415dded"
|
||||||
|
|||||||
Reference in New Issue
Block a user