mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
Add theme selector / update defaults
This commit is contained in:
+16
-14
@@ -1,12 +1,12 @@
|
||||
import { ReactNode, useEffect } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { MantineProvider } from '@mantine/core';
|
||||
import { useLocalStorage } from '@mantine/hooks';
|
||||
import { ModalsProvider } from '@mantine/modals';
|
||||
import { NotificationsProvider } from '@mantine/notifications';
|
||||
import isElectron from 'is-electron';
|
||||
import { BrowserRouter, HashRouter } from 'react-router-dom';
|
||||
import { initSimpleImg } from 'react-simple-img';
|
||||
import { BaseContextModal } from '@/renderer/components';
|
||||
import { useTheme } from '@/renderer/hooks';
|
||||
import { useDefaultSettings } from './features/settings';
|
||||
import { AppRouter } from './router/app-router';
|
||||
import './styles/global.scss';
|
||||
@@ -23,27 +23,29 @@ const SelectRouter = ({ children }: { children: ReactNode }) => {
|
||||
};
|
||||
|
||||
export const App = () => {
|
||||
const [theme] = useLocalStorage({
|
||||
defaultValue: 'dark',
|
||||
key: 'theme',
|
||||
});
|
||||
|
||||
useDefaultSettings();
|
||||
|
||||
useEffect(() => {
|
||||
document.body.setAttribute('data-theme', theme);
|
||||
}, [theme]);
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<MantineProvider
|
||||
withGlobalStyles
|
||||
withNormalizeCSS
|
||||
theme={{
|
||||
colorScheme: 'dark',
|
||||
colorScheme: theme,
|
||||
defaultRadius: 'xs',
|
||||
dir: 'ltr',
|
||||
focusRing: 'never',
|
||||
fontFamily: 'Sora, sans-serif',
|
||||
focusRing: 'auto',
|
||||
focusRingStyles: {
|
||||
inputStyles: () => ({
|
||||
border: `1px solid var(--primary-color)`,
|
||||
}),
|
||||
resetStyles: () => ({ outline: 'none' }),
|
||||
styles: () => ({
|
||||
outline: `1px solid var(--primary-color)`,
|
||||
outlineOffset: '-1px',
|
||||
}),
|
||||
},
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontSizes: {
|
||||
lg: 16,
|
||||
md: 14,
|
||||
|
||||
@@ -31,7 +31,7 @@ const StyledButton = styled(MantineButton)<StyledButtonProps>`
|
||||
return '';
|
||||
}
|
||||
}};
|
||||
background-color: ${(props) => {
|
||||
background: ${(props) => {
|
||||
switch (props.variant) {
|
||||
case 'default':
|
||||
return 'var(--btn-default-bg)';
|
||||
@@ -43,6 +43,7 @@ const StyledButton = styled(MantineButton)<StyledButtonProps>`
|
||||
return '';
|
||||
}
|
||||
}};
|
||||
border: none;
|
||||
|
||||
&:disabled {
|
||||
color: ${(props) => {
|
||||
@@ -57,7 +58,7 @@ const StyledButton = styled(MantineButton)<StyledButtonProps>`
|
||||
return '';
|
||||
}
|
||||
}};
|
||||
background-color: ${(props) => {
|
||||
background: ${(props) => {
|
||||
switch (props.variant) {
|
||||
case 'default':
|
||||
return 'var(--btn-default-bg)';
|
||||
@@ -70,7 +71,7 @@ const StyledButton = styled(MantineButton)<StyledButtonProps>`
|
||||
}
|
||||
}};
|
||||
|
||||
opacity: 0.4;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -86,7 +87,7 @@ const StyledButton = styled(MantineButton)<StyledButtonProps>`
|
||||
return '';
|
||||
}
|
||||
}};
|
||||
background-color: ${(props) => {
|
||||
background: ${(props) => {
|
||||
switch (props.variant) {
|
||||
case 'default':
|
||||
return 'var(--btn-default-bg-hover)';
|
||||
@@ -103,14 +104,6 @@ const StyledButton = styled(MantineButton)<StyledButtonProps>`
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
border: 1px var(--primary-color) solid;
|
||||
}
|
||||
|
||||
&.mantine-Button-root &:focus-visible {
|
||||
outline: --var-primary;
|
||||
}
|
||||
`;
|
||||
|
||||
export const _Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
|
||||
@@ -31,6 +31,10 @@ const StyledMenuItem = styled(MantineMenu.Item)<MenuItemProps>`
|
||||
font-family: var(--label-font-family);
|
||||
background-color: var(--dropdown-menu-bg);
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--dropdown-menu-bg-hover);
|
||||
}
|
||||
@@ -40,18 +44,23 @@ const StyledMenuItem = styled(MantineMenu.Item)<MenuItemProps>`
|
||||
}
|
||||
|
||||
& .mantine-Menu-itemLabel {
|
||||
color: ${({ $isActive }) => $isActive && 'var(--primary-color)'};
|
||||
color: ${({ $isActive }) =>
|
||||
$isActive ? 'var(--primary-color)' : 'var(--dropdown-menu-fg)'};
|
||||
font-weight: 500;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
& .mantine-Menu-itemRightSection {
|
||||
margin-left: 2rem !important;
|
||||
color: ${({ $isActive }) =>
|
||||
$isActive ? 'var(--primary-color)' : 'var(--dropdown-menu-fg)'};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledMenuDropdown = styled(MantineMenu.Dropdown)`
|
||||
background: var(--dropdown-menu-bg);
|
||||
border: var(--dropdown-menu-border);
|
||||
border-radius: var(--dropdown-menu-border-radius);
|
||||
`;
|
||||
|
||||
const StyledMenuDivider = styled(MantineMenu.Divider)`
|
||||
@@ -74,9 +83,7 @@ const pMenuItem = ({ $isActive, children, ...props }: MenuItemProps) => {
|
||||
return (
|
||||
<StyledMenuItem
|
||||
$isActive={$isActive}
|
||||
rightSection={
|
||||
$isActive && <RiArrowLeftLine color="var(--primary-color)" />
|
||||
}
|
||||
rightSection={$isActive && <RiArrowLeftLine />}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -36,21 +36,11 @@ interface FileInputProps extends MantineFileInputProps {
|
||||
}
|
||||
|
||||
const StyledTextInput = styled(MantineTextInput)<TextInputProps>`
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
& .mantine-TextInput-wrapper {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
& .mantine-TextInput-input {
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
color: var(--input-fg);
|
||||
background: var(--input-bg);
|
||||
|
||||
@@ -72,26 +62,16 @@ const StyledTextInput = styled(MantineTextInput)<TextInputProps>`
|
||||
}
|
||||
|
||||
& .mantine-TextInput-disabled {
|
||||
cursor: default;
|
||||
opacity: 0.6;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledNumberInput = styled(MantineNumberInput)<NumberInputProps>`
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
& .mantine-NumberInput-wrapper {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
& .mantine-NumberInput-input {
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
color: var(--input-fg);
|
||||
background: var(--input-bg);
|
||||
|
||||
@@ -113,18 +93,11 @@ const StyledNumberInput = styled(MantineNumberInput)<NumberInputProps>`
|
||||
}
|
||||
|
||||
& .mantine-NumberInput-disabled {
|
||||
cursor: default;
|
||||
opacity: 0.6;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledPasswordInput = styled(MantinePasswordInput)<PasswordInputProps>`
|
||||
& .mantine-PasswordInput-input {
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
& .mantine-PasswordInput-required {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
@@ -134,18 +107,11 @@ const StyledPasswordInput = styled(MantinePasswordInput)<PasswordInputProps>`
|
||||
}
|
||||
|
||||
& .mantine-PasswordInput-disabled {
|
||||
cursor: default;
|
||||
opacity: 0.6;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledFileInput = styled(MantineFileInput)<FileInputProps>`
|
||||
& .mantine-FileInput-input {
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
& .mantine-FileInput-required {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
@@ -155,7 +121,7 @@ const StyledFileInput = styled(MantineFileInput)<FileInputProps>`
|
||||
}
|
||||
|
||||
& .mantine-PasswordInput-disabled {
|
||||
cursor: default;
|
||||
opacity: 0.6;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -10,18 +10,20 @@ type SegmentedControlProps = MantineSegmentedControlProps;
|
||||
const StyledSegmentedControl = styled(
|
||||
MantineSegmentedControl
|
||||
)<MantineSegmentedControlProps>`
|
||||
&:focus,
|
||||
&:focus-within,
|
||||
&:active {
|
||||
outline: 1px var(--primary-color) solid;
|
||||
}
|
||||
|
||||
& .mantine-SegmentedControl-label {
|
||||
color: var(--input-fg);
|
||||
font-family: var(--label-font-family);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
background-color: var(--input-bg);
|
||||
|
||||
& .mantine-SegmentedControl-disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
& .mantine-SegmentedControl-active {
|
||||
color: var(--input-active-fg);
|
||||
background-color: var(--input-active-bg);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -29,5 +31,13 @@ export const SegmentedControl = forwardRef<
|
||||
HTMLDivElement,
|
||||
SegmentedControlProps
|
||||
>(({ ...props }: SegmentedControlProps, ref) => {
|
||||
return <StyledSegmentedControl ref={ref} {...props} />;
|
||||
return (
|
||||
<StyledSegmentedControl
|
||||
ref={ref}
|
||||
styles={{}}
|
||||
transitionDuration={250}
|
||||
transitionTimingFunction="linear"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -11,7 +11,12 @@ interface SelectProps extends MantineSelectProps {
|
||||
|
||||
const StyledSelect = styled(MantineSelect)`
|
||||
& [data-selected='true'] {
|
||||
background: black;
|
||||
background: var(--input-select-bg);
|
||||
}
|
||||
|
||||
& .mantine-Select-disabled {
|
||||
background: var(--input-select-bg);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
& .mantine-Select-itemsWrapper {
|
||||
@@ -29,10 +34,17 @@ export const Select = ({ width, maxWidth, ...props }: SelectProps) => {
|
||||
dropdown: {
|
||||
background: 'var(--dropdown-menu-bg)',
|
||||
},
|
||||
input: {
|
||||
background: 'var(--input-bg)',
|
||||
color: 'var(--input-fg)',
|
||||
},
|
||||
item: {
|
||||
'&:hover': {
|
||||
background: 'var(--dropdown-menu-bg-hover)',
|
||||
},
|
||||
'&[data-hovered]': {
|
||||
background: 'var(--dropdown-menu-bg-hover)',
|
||||
},
|
||||
'&[data-selected="true"]': {
|
||||
'&:hover': {
|
||||
background: 'var(--dropdown-menu-bg-hover)',
|
||||
@@ -40,6 +52,7 @@ export const Select = ({ width, maxWidth, ...props }: SelectProps) => {
|
||||
background: 'none',
|
||||
color: 'var(--primary-color)',
|
||||
},
|
||||
color: 'var(--dropdown-menu-fg)',
|
||||
padding: '.3rem',
|
||||
},
|
||||
itemsWrapper: {
|
||||
|
||||
@@ -15,6 +15,8 @@ const StyledSlider = styled(MantineSlider)`
|
||||
& .mantine-Slider-thumb {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
background: var(--slider-thumb-bg);
|
||||
border: none;
|
||||
}
|
||||
|
||||
& .mantine-Slider-label {
|
||||
|
||||
@@ -8,12 +8,23 @@ type TabsProps = MantineTabsProps;
|
||||
|
||||
const StyledTabs = styled(MantineTabs)`
|
||||
button[data-active] {
|
||||
color: var(--main-fg);
|
||||
border-color: var(--primary-color);
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
color: var(--main-fg);
|
||||
opacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--btn-subtle-bg-hover);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const Tabs = ({ children, ...props }: TabsProps) => {
|
||||
|
||||
@@ -64,5 +64,5 @@ _Text.defaultProps = {
|
||||
font: undefined,
|
||||
overflow: 'visible',
|
||||
to: '',
|
||||
weight: 500,
|
||||
weight: 400,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Center, Skeleton } from '@mantine/core';
|
||||
import { RiAlbumFill } from 'react-icons/ri';
|
||||
import { generatePath, useNavigate } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { SimpleImg, initSimpleImg } from 'react-simple-img';
|
||||
import { SimpleImg } from 'react-simple-img';
|
||||
import { ListChildComponentProps } from 'react-window';
|
||||
import styled from 'styled-components';
|
||||
import { Text } from '@/renderer/components/text';
|
||||
@@ -17,8 +17,6 @@ import {
|
||||
Play,
|
||||
} from '@/renderer/types';
|
||||
|
||||
initSimpleImg({ threshold: 0.5 }, true);
|
||||
|
||||
const CardWrapper = styled.div<{
|
||||
itemGap: number;
|
||||
itemHeight: number;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Center, Skeleton } from '@mantine/core';
|
||||
import { RiAlbumFill } from 'react-icons/ri';
|
||||
import { generatePath } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { SimpleImg, initSimpleImg } from 'react-simple-img';
|
||||
import { SimpleImg } from 'react-simple-img';
|
||||
import { ListChildComponentProps } from 'react-window';
|
||||
import styled from 'styled-components';
|
||||
import { Text } from '@/renderer/components/text';
|
||||
@@ -16,8 +16,6 @@ import {
|
||||
Play,
|
||||
} from '@/renderer/types';
|
||||
|
||||
initSimpleImg({ threshold: 0.5 }, true);
|
||||
|
||||
const CardWrapper = styled.div<{
|
||||
itemGap: number;
|
||||
itemHeight: number;
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import { Select, Stack } from '@mantine/core';
|
||||
import { Divider, Stack } from '@mantine/core';
|
||||
import { Switch, Select } from '@/renderer/components';
|
||||
import { SettingsOptions } from '@/renderer/features/settings/components/settings-option';
|
||||
import { useSettingsStore } from '@/renderer/store/settings.store';
|
||||
import { AppTheme } from '@/renderer/themes/types';
|
||||
|
||||
const THEME_OPTIONS = [
|
||||
{ label: 'Default Dark', value: AppTheme.DEFAULT_DARK },
|
||||
{ label: 'Default Light', value: AppTheme.DEFAULT_LIGHT },
|
||||
];
|
||||
|
||||
export const GeneralTab = () => {
|
||||
const settings = useSettingsStore((state) => state.general);
|
||||
const update = useSettingsStore((state) => state.setSettings);
|
||||
|
||||
const options = [
|
||||
{
|
||||
control: <Select disabled data={[]} />,
|
||||
@@ -9,12 +20,7 @@ export const GeneralTab = () => {
|
||||
isHidden: false,
|
||||
title: 'Language',
|
||||
},
|
||||
{
|
||||
control: <Select disabled data={[]} />,
|
||||
description: 'Sets the default theme',
|
||||
isHidden: false,
|
||||
title: 'Theme',
|
||||
},
|
||||
|
||||
{
|
||||
control: <Select disabled data={[]} />,
|
||||
description: 'Sets the default font',
|
||||
@@ -31,11 +37,95 @@ export const GeneralTab = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const themeOptions = [
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
defaultChecked={settings.followSystemTheme}
|
||||
onChange={(e) => {
|
||||
update({
|
||||
general: {
|
||||
...settings,
|
||||
followSystemTheme: e.currentTarget.checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: 'Follows the system-defined light or dark preference',
|
||||
isHidden: false,
|
||||
title: 'Use system theme',
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Select
|
||||
data={THEME_OPTIONS}
|
||||
defaultValue={settings.theme}
|
||||
onChange={(e) => {
|
||||
update({
|
||||
general: {
|
||||
...settings,
|
||||
theme: e as AppTheme,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: 'Sets the default theme',
|
||||
isHidden: settings.followSystemTheme,
|
||||
title: 'Theme',
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Select
|
||||
data={THEME_OPTIONS}
|
||||
defaultValue={settings.themeDark}
|
||||
onChange={(e) => {
|
||||
update({
|
||||
general: {
|
||||
...settings,
|
||||
themeDark: e as AppTheme,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: 'Sets the dark theme',
|
||||
isHidden: !settings.followSystemTheme,
|
||||
title: 'Theme (dark)',
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Select
|
||||
data={THEME_OPTIONS}
|
||||
defaultValue={settings.themeLight}
|
||||
onChange={(e) => {
|
||||
update({
|
||||
general: {
|
||||
...settings,
|
||||
themeLight: e as AppTheme,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: 'Sets the light theme',
|
||||
isHidden: !settings.followSystemTheme,
|
||||
title: 'Theme (light)',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack mt="1rem" spacing="xl">
|
||||
{options.map((option) => (
|
||||
<SettingsOptions key={`general-${option.title}`} {...option} />
|
||||
))}
|
||||
<Divider />
|
||||
{themeOptions
|
||||
.filter((o) => !o.isHidden)
|
||||
.map((option) => (
|
||||
<SettingsOptions key={`general-${option.title}`} {...option} />
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './use-theme';
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSettingsStore } from '@/renderer/store/settings.store';
|
||||
|
||||
export const useTheme = () => {
|
||||
const getCurrentTheme = () =>
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const [isDarkTheme, setIsDarkTheme] = useState(getCurrentTheme());
|
||||
const { followSystemTheme, theme, themeDark, themeLight } = useSettingsStore(
|
||||
(state) => state.general
|
||||
);
|
||||
|
||||
const mqListener = (e: any) => {
|
||||
setIsDarkTheme(e.matches);
|
||||
};
|
||||
|
||||
const getTheme = () => {
|
||||
if (followSystemTheme) {
|
||||
return isDarkTheme ? themeDark : themeLight;
|
||||
}
|
||||
|
||||
return theme;
|
||||
};
|
||||
|
||||
const appTheme = getTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const darkThemeMq = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
darkThemeMq.addListener(mqListener);
|
||||
return () => darkThemeMq.removeListener(mqListener);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.setAttribute('data-theme', appTheme);
|
||||
}, [appTheme]);
|
||||
|
||||
return isDarkTheme ? 'dark' : 'light';
|
||||
};
|
||||
@@ -50,19 +50,21 @@ const SidebarContainer = styled.div`
|
||||
position: relative;
|
||||
grid-area: sidebar;
|
||||
background: var(--sidebar-bg);
|
||||
border-right: var(--sidebar-border);
|
||||
`;
|
||||
|
||||
const RightSidebarContainer = styled(motion.div)`
|
||||
position: relative;
|
||||
grid-area: right-sidebar;
|
||||
background: var(--sidebar-bg);
|
||||
border-left: var(--sidebar-border);
|
||||
`;
|
||||
|
||||
const PlayerbarContainer = styled.footer`
|
||||
z-index: 50;
|
||||
grid-area: player;
|
||||
background: var(--playerbar-bg);
|
||||
filter: drop-shadow(0 -3px 4mm rgba(0, 0, 0, 50%));
|
||||
filter: drop-shadow(0 -3px 1px rgba(0, 0, 0, 10%));
|
||||
`;
|
||||
|
||||
const ResizeHandle = styled.div<{
|
||||
|
||||
@@ -4,6 +4,7 @@ import merge from 'lodash/merge';
|
||||
import create from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import { AppTheme } from '@/renderer/themes/types';
|
||||
import {
|
||||
CrossfadeStyle,
|
||||
Play,
|
||||
@@ -12,6 +13,12 @@ import {
|
||||
} from '@/renderer/types';
|
||||
|
||||
export interface SettingsState {
|
||||
general: {
|
||||
followSystemTheme: boolean;
|
||||
theme: AppTheme;
|
||||
themeDark: AppTheme;
|
||||
themeLight: AppTheme;
|
||||
};
|
||||
player: {
|
||||
audioDeviceId?: string | null;
|
||||
crossfadeDuration: number;
|
||||
@@ -42,6 +49,12 @@ export const useSettingsStore = create<SettingsSlice>()(
|
||||
persist(
|
||||
devtools(
|
||||
immer((set, get) => ({
|
||||
general: {
|
||||
followSystemTheme: false,
|
||||
theme: AppTheme.DEFAULT_DARK,
|
||||
themeDark: AppTheme.DEFAULT_DARK,
|
||||
themeLight: AppTheme.DEFAULT_LIGHT,
|
||||
},
|
||||
player: {
|
||||
audioDeviceId: undefined,
|
||||
crossfadeDuration: 5,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
body[data-theme='dark'] {
|
||||
body[data-theme='defaultDark'] {
|
||||
}
|
||||
|
||||
@@ -6,39 +6,46 @@
|
||||
--icon-color: rgb(255, 255, 255);
|
||||
|
||||
--primary-color: rgb(58, 129, 237);
|
||||
--secondary-color: rgb(199, 100, 141);
|
||||
--secondary-color: rgb(255, 120, 120);
|
||||
--success-color: green;
|
||||
--warning-color: orange;
|
||||
--danger-color: rgb(204, 30, 54);
|
||||
--danger-color: rgb(204, 50, 50);
|
||||
--generic-border-color: rgba(50, 50, 50, 0.7);
|
||||
|
||||
--main-bg: rgb(22, 22, 22);
|
||||
--main-fg: rgb(193, 194, 187);
|
||||
--main-bg: rgb(18, 18, 18);
|
||||
--main-fg: rgb(240, 240, 240);
|
||||
--main-fg-secondary: rgb(150, 150, 150);
|
||||
|
||||
--titlebar-fg: rgb(255, 255, 255);
|
||||
--titlebar-bg: rgb(15, 15, 15);
|
||||
--titlebar-bg: rgb(13, 14, 17);
|
||||
|
||||
--sidebar-bg: rgb(15, 15, 15);
|
||||
--sidebar-bg: radial-gradient(
|
||||
circle,
|
||||
rgba(0, 0, 0, 1) 38%,
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
--sidebar-btn-color: rgb(179, 179, 179);
|
||||
--sidebar-btn-color-hover: #dddddd;
|
||||
--sidebar-handle-bg: #4d4d4d;
|
||||
--sidebar-border: 1px rgba(150, 150, 150, 0.1) solid;
|
||||
|
||||
--playerbar-bg: linear-gradient(
|
||||
rgb(20, 20, 20) 0%,
|
||||
rgb(30, 30, 30) 50%,
|
||||
rgb(20, 20, 20) 100%
|
||||
);
|
||||
--playerbar-text-primary-color: #e7e7e7;
|
||||
--playerbar-text-secondary-color: #a8a8a8;
|
||||
--playerbar-btn-color: #c5c5c5;
|
||||
--playerbar-btn-color-hover: #ffffff;
|
||||
--playerbar-btn-main-fg: rgb(0, 0, 0);
|
||||
--playerbar-btn-main-fg-hover: rgb(0, 0, 0);
|
||||
--playerbar-btn-main-bg: rgb(230, 230, 230);
|
||||
--playerbar-btn-main-bg-hover: rgb(255, 255, 255);
|
||||
--playerbar-btn-fg: rgba(200, 200, 200, 0.8);
|
||||
--playerbar-btn-fg-hover: rgba(255, 255, 255, 1);
|
||||
--playerbar-btn-bg: #c5c5c5;
|
||||
--playerbar-btn-bg-hover: transparent;
|
||||
--playerbar-border-top: 1px rgba(50, 50, 50, 0.7) solid;
|
||||
--playerbar-slider-track-bg: #3c3f43;
|
||||
--playerbar-slider-track-progress-bg: #cccccc;
|
||||
|
||||
--grid-card-bg: rgb(24, 24, 24);
|
||||
|
||||
--tooltip-bg: #ffffff;
|
||||
--tooltip-fg: #000000;
|
||||
|
||||
@@ -51,7 +58,7 @@
|
||||
--btn-primary-fg-hover: #ffffff;
|
||||
|
||||
--btn-default-bg: rgb(41, 41, 41);
|
||||
--btn-default-bg-hover: color.scale(var(--btn-default-bg), $lightness: 15%);
|
||||
--btn-default-bg-hover: rgb(51, 51, 51);
|
||||
--btn-default-fg: rgb(193, 193, 193);
|
||||
--btn-default-fg-hover: rgb(193, 193, 193);
|
||||
|
||||
@@ -63,23 +70,40 @@
|
||||
--input-bg: rgb(37, 38, 43);
|
||||
--input-fg: rgb(193, 193, 193);
|
||||
--input-placeholder-fg: rgb(119, 126, 139);
|
||||
--input-active-fg: rgb(193, 193, 193);
|
||||
--input-active-bg: rgba(255, 255, 255, 0.1);
|
||||
|
||||
--dropdown-menu-bg: rgb(24, 24, 24);
|
||||
--dropdown-menu-fg: rgb(193, 194, 197);
|
||||
--dropdown-menu-bg-hover: rgb(37, 38, 43);
|
||||
--dropdown-menu-border: 1px rgba(50, 50, 50, 0.7) solid;
|
||||
--dropdown-menu-border-radius: 4px;
|
||||
|
||||
--switch-track-bg: rgb(50, 50, 50);
|
||||
--switch-track-enabled-bg: var(--primary-color);
|
||||
--switch-thumb-bg: rgb(255, 255, 255);
|
||||
|
||||
--slider-track-bg: rgb(50, 50, 50);
|
||||
--slider-thumb-bg: rgb(50, 50, 50);
|
||||
|
||||
--toast-title-fg: rgb(255, 255, 255);
|
||||
--toast-description-fg: rgb(193, 194, 197);
|
||||
--toast-bg: rgb(16, 16, 16);
|
||||
|
||||
--modal-bg: rgb(24, 24, 24);
|
||||
|
||||
--paper-bg: rgb(30, 30, 30);
|
||||
|
||||
--placeholder-bg: rgba(53, 53, 53, 0.5);
|
||||
--placeholder-fg: rgba(126, 126, 126);
|
||||
|
||||
--card-default-bg: rgb(24, 24, 24);
|
||||
--card-default-bg-hover: rgb(40, 40, 40);
|
||||
--card-default-radius: 10px;
|
||||
--card-poster-bg: transparent;
|
||||
--card-poster-bg-hover: transparent;
|
||||
--card-poster-radius: 5px;
|
||||
|
||||
.ag-theme-alpine-dark {
|
||||
--ag-font-family: poppins;
|
||||
--ag-font-size: 12px;
|
||||
@@ -92,8 +116,8 @@
|
||||
|
||||
--ag-foreground-color: rgb(179, 179, 179);
|
||||
|
||||
--ag-background-color: rgb(24, 24, 25);
|
||||
--ag-odd-row-background-color: rgb(24, 24, 25);
|
||||
--ag-background-color: var(--main-bg);
|
||||
--ag-odd-row-background-color: rgb(25, 25, 25);
|
||||
--ag-row-hover-color: rgba(100, 100, 100, 0.2);
|
||||
--ag-selected-row-background-color: rgba(100, 100, 100, 0.4);
|
||||
}
|
||||
@@ -106,4 +130,8 @@
|
||||
.current-song {
|
||||
background: rgba(0, 50, 150, 0.2) !important;
|
||||
}
|
||||
|
||||
.mantine-Modal-modal {
|
||||
background: var(--modal-bg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
body[data-theme='defaultLight'] {
|
||||
--primary-color: rgb(117, 172, 255);
|
||||
--icon-color: #ffffff;
|
||||
|
||||
--main-bg: rgb(255, 255, 255);
|
||||
--main-fg: rgb(25, 25, 25);
|
||||
--main-fg-secondary: rgb(80, 80, 80);
|
||||
|
||||
--titlebar-fg: rgb(25, 25, 25);
|
||||
--titlebar-bg: rgb(227, 229, 232);
|
||||
|
||||
--sidebar-bg: radial-gradient(255, 255, 255);
|
||||
--sidebar-btn-color: rgb(0, 0, 0);
|
||||
--sidebar-btn-color-hover: rgb(0, 0, 0);
|
||||
--sidebar-handle-bg: #4d4d4d;
|
||||
|
||||
--playerbar-bg: linear-gradient(
|
||||
rgb(220, 220, 220) 0%,
|
||||
rgb(240, 240, 240) 50%,
|
||||
rgb(220, 220, 220) 100%
|
||||
);
|
||||
--playerbar-btn-main-fg: rgb(0, 0, 0);
|
||||
--playerbar-btn-main-fg-hover: rgb(0, 0, 0);
|
||||
--playerbar-btn-main-bg: transparent;
|
||||
--playerbar-btn-main-bg-hover: transparent;
|
||||
--playerbar-btn-fg: #000;
|
||||
--playerbar-btn-fg-hover: #000;
|
||||
--playerbar-btn-bg: transparent;
|
||||
--playerbar-btn-bg-hover: transparent;
|
||||
--playerbar-border-top: 1px rgba(50, 50, 50, 0.7) solid;
|
||||
--playerbar-slider-track-bg: rgba(50, 50, 50, 0.2);
|
||||
--playerbar-slider-track-progress-bg: rgb(50, 50, 50);
|
||||
|
||||
--tooltip-bg: rgb(255, 255, 255);
|
||||
--tooltip-fg: rgb(0, 0, 0);
|
||||
|
||||
--scrollbar-track-bg: rgba(0, 0, 0, 0.2);
|
||||
--scrollbar-thumb-bg: #5a5a5a;
|
||||
|
||||
--btn-primary-bg: var(--primary-color);
|
||||
--btn-primary-bg-hover: rgb(47, 122, 237);
|
||||
--btn-primary-fg: #ffffff;
|
||||
--btn-primary-fg-hover: #ffffff;
|
||||
|
||||
--btn-default-bg: rgb(180, 180, 180);
|
||||
--btn-default-bg-hover: rgb(150, 150, 150);
|
||||
--btn-default-fg: rgb(80, 80, 80);
|
||||
--btn-default-fg-hover: rgb(0, 0, 0);
|
||||
|
||||
--btn-subtle-bg: transparent;
|
||||
--btn-subtle-bg-hover: rgba(100, 100, 100, 0.1);
|
||||
--btn-subtle-fg: rgb(60, 60, 60);
|
||||
--btn-subtle-fg-hover: rgb(50, 50, 50);
|
||||
|
||||
--input-bg: rgb(235, 237, 239);
|
||||
--input-fg: rgb(0, 0, 0);
|
||||
--input-placeholder-fg: rgb(119, 126, 139);
|
||||
--input-active-fg: rgb(193, 193, 193);
|
||||
--input-active-bg: rgba(37, 38, 43, 0.3);
|
||||
|
||||
--dropdown-menu-bg: rgb(255, 255, 255);
|
||||
--dropdown-menu-fg: rgb(0, 0, 0);
|
||||
--dropdown-menu-bg-hover: rgba(0, 0, 0, 0.1);
|
||||
--dropdown-menu-border: 1px rgba(150, 150, 150, 0.7) solid;
|
||||
--dropdown-menu-border-radius: 4px;
|
||||
|
||||
--switch-track-bg: rgb(114, 118, 125);
|
||||
--switch-track-enabled-bg: var(--primary-color);
|
||||
--switch-thumb-bg: rgb(255, 255, 255);
|
||||
|
||||
--slider-track-bg: rgba(50, 50, 50, 0.1);
|
||||
--slider-thumb-bg: rgb(100, 100, 100);
|
||||
|
||||
--toast-title-fg: rgb(255, 255, 255);
|
||||
--toast-description-fg: rgb(193, 194, 197);
|
||||
--toast-bg: rgb(16, 16, 16);
|
||||
|
||||
--modal-bg: rgb(255, 255, 255);
|
||||
|
||||
--paper-bg: rgb(230, 230, 230);
|
||||
|
||||
--placeholder-bg: rgba(53, 53, 53, 0.5);
|
||||
--placeholder-fg: rgba(126, 126, 126);
|
||||
|
||||
--card-default-bg: rgba(235, 235, 235, 0.5);
|
||||
--card-default-bg-hover: rgba(200, 200, 200, 0.8);
|
||||
--card-default-radius: 10px;
|
||||
--card-poster-bg: transparent;
|
||||
--card-poster-bg-hover: transparent;
|
||||
--card-poster-radius: 5px;
|
||||
|
||||
.ag-theme-alpine-dark {
|
||||
--ag-font-family: poppins;
|
||||
--ag-font-size: 12px;
|
||||
|
||||
--ag-borders: none;
|
||||
--ag-border-color: rgb(50, 50, 50);
|
||||
|
||||
--ag-header-background-color: var(--main-bg);
|
||||
--ag-header-foreground-color: var(--main-fg);
|
||||
|
||||
--ag-foreground-color: rgb(0, 0, 0);
|
||||
|
||||
--ag-background-color: var(--main-bg);
|
||||
--ag-odd-row-background-color: rgb(230, 230, 230);
|
||||
--ag-row-hover-color: rgba(100, 100, 100, 0.2);
|
||||
--ag-selected-row-background-color: rgba(100, 100, 100, 0.4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum AppTheme {
|
||||
DEFAULT_DARK = 'defaultDark',
|
||||
DEFAULT_LIGHT = 'defaultLight',
|
||||
}
|
||||
Reference in New Issue
Block a user