diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 318d17925..b18cf90d4 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -657,6 +657,8 @@ "autoDJ_timing_description": "the number of songs remaining in the queue before auto DJ is triggered", "accentColor_description": "sets the accent color for the application", "accentColor": "accent color", + "useThemeAccentColor": "use theme accent color", + "useThemeAccentColor_description": "use the primary color defined in the selected theme instead of the custom accent color", "albumBackground_description": "adds a background image for album pages containing the album art", "albumBackground": "album background image", "albumBackgroundBlur_description": "adjusts the amount of blur applied to the album background image", diff --git a/src/renderer/components/item-list/item-table-list/item-table-list.module.css b/src/renderer/components/item-list/item-table-list/item-table-list.module.css index d530baa69..5e323aa33 100644 --- a/src/renderer/components/item-list/item-table-list/item-table-list.module.css +++ b/src/renderer/components/item-list/item-table-list/item-table-list.module.css @@ -187,19 +187,10 @@ pointer-events: none; background: linear-gradient( to bottom, - rgb(0 0 0 / 50%) 0%, - rgb(0 0 0 / 5%) 50%, + darken(var(--theme-colors-background), 10%) 0%, + darken(var(--theme-colors-background), 5%) 50%, transparent 100% ); - - @mixin light { - background: linear-gradient( - to bottom, - var(--theme-colors-background) 0%, - alpha(var(--theme-colors-background), 0.05) 50%, - transparent 100% - ); - } } .item-table-left-scroll-shadow { @@ -210,18 +201,21 @@ z-index: 1; width: 8px; pointer-events: none; - background: linear-gradient( - to right, - rgb(0 0 0 / 50%) 0%, - rgb(0 0 0 / 5%) 50%, - transparent 100% - ); + + @mixin dark { + background: linear-gradient( + to right, + darken(var(--theme-colors-background), 10%) 0%, + darken(var(--theme-colors-background), 5%) 50%, + transparent 100% + ); + } @mixin light { background: linear-gradient( to right, - var(--theme-colors-background) 0%, - alpha(var(--theme-colors-background), 0.05) 50%, + darken(var(--theme-colors-background), 5%) 0%, + darken(var(--theme-colors-background), 3%) 50%, transparent 100% ); } @@ -235,18 +229,21 @@ z-index: 1; width: 8px; pointer-events: none; - background: linear-gradient( - to left, - rgb(0 0 0 / 50%) 0%, - rgb(0 0 0 / 5%) 50%, - transparent 100% - ); + + @mixin dark { + background: linear-gradient( + to left, + darken(var(--theme-colors-background), 10%) 0%, + darken(var(--theme-colors-background), 5%) 50%, + transparent 100% + ); + } @mixin light { background: linear-gradient( to left, - var(--theme-colors-background) 0%, - alpha(var(--theme-colors-background), 0.05) 50%, + darken(var(--theme-colors-background), 5%) 0%, + darken(var(--theme-colors-background), 3%) 50%, transparent 100% ); } @@ -260,18 +257,21 @@ z-index: 1; height: 8px; pointer-events: none; - background: linear-gradient( - to bottom, - rgb(0 0 0 / 50%) 0%, - rgb(0 0 0 / 5%) 50%, - transparent 100% - ); + + @mixin dark { + background: linear-gradient( + to bottom, + darken(var(--theme-colors-background), 10%) 0%, + darken(var(--theme-colors-background), 5%) 50%, + transparent 100% + ); + } @mixin light { background: linear-gradient( to bottom, - var(--theme-colors-background) 0%, - alpha(var(--theme-colors-background), 0.05) 50%, + darken(var(--theme-colors-background), 5%) 0%, + darken(var(--theme-colors-background), 3%) 50%, transparent 100% ); } diff --git a/src/renderer/features/player/components/playerbar-waveform.tsx b/src/renderer/features/player/components/playerbar-waveform.tsx index 56eb4d507..572cd0932 100644 --- a/src/renderer/features/player/components/playerbar-waveform.tsx +++ b/src/renderer/features/player/components/playerbar-waveform.tsx @@ -14,9 +14,8 @@ import { usePlaybackSettings, usePlayerSong, usePlayerTimestamp, - usePrimaryColor, } from '/@/renderer/store'; -import { useColorScheme } from '/@/renderer/themes/use-app-theme'; +import { useAppThemeColors, useColorScheme } from '/@/renderer/themes/use-app-theme'; import { Spinner } from '/@/shared/components/spinner/spinner'; import { Text } from '/@/shared/components/text/text'; @@ -39,7 +38,8 @@ export const PlayerbarWaveform = () => { const streamUrl = useSongUrl(currentSong, true, transcode); - const primaryColor = usePrimaryColor(); + const { color } = useAppThemeColors(); + const primaryColor = (color['--theme-colors-primary'] as string) || 'rgb(53, 116, 252)'; const colorScheme = useColorScheme(); diff --git a/src/renderer/features/settings/components/advanced/styles-settings.tsx b/src/renderer/features/settings/components/advanced/styles-settings.tsx index 2ab627167..b4b543a8e 100644 --- a/src/renderer/features/settings/components/advanced/styles-settings.tsx +++ b/src/renderer/features/settings/components/advanced/styles-settings.tsx @@ -1,4 +1,3 @@ -import { closeAllModals, openModal } from '@mantine/modals'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -7,7 +6,6 @@ import { useCssSettings, useSettingsStoreActions } from '/@/renderer/store'; import { sanitizeCss } from '/@/renderer/utils/sanitize'; import { Button } from '/@/shared/components/button/button'; import { Code } from '/@/shared/components/code/code'; -import { ConfirmModal } from '/@/shared/components/modal/modal'; import { Switch } from '/@/shared/components/switch/switch'; import { Text } from '/@/shared/components/text/text'; import { Textarea } from '/@/shared/components/textarea/textarea'; @@ -30,16 +28,6 @@ export const StylesSettings = () => { }); }; - const handleResetToDefault = () => { - setSettings({ - css: { - content, - enabled: true, - }, - }); - closeAllModals(); - }; - useEffect(() => { if (content !== css) { setCss(content); @@ -47,19 +35,6 @@ export const StylesSettings = () => { // eslint-disable-next-line react-hooks/exhaustive-deps -- Reason: This is to only fire if an external source updates the stores css.content }, [content]); - const openConfirmModal = () => { - openModal({ - children: ( - - - {t('setting.customCssNotice', { postProcess: 'sentenceCase' })} - - - ), - title: t('setting.customCssEnable', { postProcess: 'sentenceCase' }), - }); - }; - return ( <> { { - if (!e.currentTarget.checked) { - setSettings({ - css: { - content, - enabled: false, - }, - }); - } else { - openConfirmModal(); - } + setSettings({ + css: { + content, + enabled: e.currentTarget.checked, + }, + }); }} /> } @@ -84,6 +55,7 @@ export const StylesSettings = () => { context: 'description', postProcess: 'sentenceCase', })} + note={t('setting.customCssNotice', { postProcess: 'sentenceCase' })} title={t('setting.customCssEnable', { postProcess: 'sentenceCase' })} /> {enabled && ( diff --git a/src/renderer/features/settings/components/general/theme-settings.tsx b/src/renderer/features/settings/components/general/theme-settings.tsx index 4df0978c2..c71ad467e 100644 --- a/src/renderer/features/settings/components/general/theme-settings.tsx +++ b/src/renderer/features/settings/components/general/theme-settings.tsx @@ -1,6 +1,8 @@ import isElectron from 'is-electron'; +import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; +import i18n from '/@/i18n/i18n'; import { StylesSettings } from '/@/renderer/features/settings/components/advanced/styles-settings'; import { SettingOption, @@ -9,19 +11,83 @@ import { import { useGeneralSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store'; import { THEME_DATA, useSetColorScheme } from '/@/renderer/themes/use-app-theme'; import { ColorInput } from '/@/shared/components/color-input/color-input'; +import { Group } from '/@/shared/components/group/group'; import { Select } from '/@/shared/components/select/select'; import { Stack } from '/@/shared/components/stack/stack'; import { Switch } from '/@/shared/components/switch/switch'; +import { getAppTheme } from '/@/shared/themes/app-theme'; import { AppTheme } from '/@/shared/themes/app-theme-types'; const localSettings = isElectron() ? window.api.localSettings : null; +const getThemeSwatchColors = (theme: AppTheme) => { + const themeConfig = getAppTheme(theme); + return { + background: themeConfig.colors?.background || 'rgb(0, 0, 0)', + foreground: themeConfig.colors?.foreground || 'rgb(255, 255, 255)', + surface: themeConfig.colors?.surface || themeConfig.colors?.background || 'rgb(0, 0, 0)', + }; +}; + +const getGroupedThemeData = () => { + const darkThemes = THEME_DATA.filter((theme) => theme.type === 'dark').sort((a, b) => + a.label.localeCompare(b.label), + ); + const lightThemes = THEME_DATA.filter((theme) => theme.type === 'light').sort((a, b) => + a.label.localeCompare(b.label), + ); + + return [ + { + group: i18n.t('setting.themeDark', { postProcess: 'sentenceCase' }), + items: darkThemes, + }, + { + group: i18n.t('setting.themeLight', { postProcess: 'sentenceCase' }), + items: lightThemes, + }, + ]; +}; + +const ColorSwatch = ({ color }: { color: string }) => { + return ( +
+ ); +}; + +const renderThemeOption = ({ option }: { option: { label: string; value: string } }) => { + const themeValue = option.value as AppTheme; + const colors = getThemeSwatchColors(themeValue); + + return ( + + + + + + + {option.label} + + ); +}; + export const ThemeSettings = () => { const { t } = useTranslation(); const settings = useGeneralSettings(); const { setSettings } = useSettingsStoreActions(); const { setColorScheme } = useSetColorScheme(); + const groupedThemeData = useMemo(() => getGroupedThemeData(), []); + const themeOptions: SettingOption[] = [ { control: ( @@ -57,7 +123,7 @@ export const ThemeSettings = () => { { control: ( { setSettings({ @@ -99,6 +166,7 @@ export const ThemeSettings = () => { }, }); }} + renderOption={renderThemeOption} /> ), description: t('setting.themeDark', { @@ -111,7 +179,7 @@ export const ThemeSettings = () => { { control: (