diff --git a/package-lock.json b/package-lock.json
index 913fe23..29b0121 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,7 +32,7 @@
"@vitest/coverage-v8": "^3.2.4",
"@vitest/ui": "^3.2.4",
"typescript": "^5.8.3",
- "vite": "^7.0.5",
+ "vite": "^7.0.6",
"vite-plugin-dts": "~4.5.4",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4"
@@ -3468,9 +3468,9 @@
"dev": true
},
"node_modules/picomatch": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
- "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4506,15 +4506,15 @@
}
},
"node_modules/vite": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz",
- "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz",
+ "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.6",
- "picomatch": "^4.0.2",
+ "picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rollup": "^4.40.0",
"tinyglobby": "^0.2.14"
diff --git a/package.json b/package.json
index 5eb9e92..64a791a 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"@vitest/coverage-v8": "^3.2.4",
"@vitest/ui": "^3.2.4",
"typescript": "^5.8.3",
- "vite": "^7.0.5",
+ "vite": "^7.0.6",
"vite-plugin-dts": "~4.5.4",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4"
diff --git a/src/pages/settings/components/Theming/ThemePlaylists/ThemePlaylistModal.tsx b/src/pages/settings/components/Theming/ThemePlaylists/ThemePlaylistModal.tsx
index 57d7796..01bb063 100644
--- a/src/pages/settings/components/Theming/ThemePlaylists/ThemePlaylistModal.tsx
+++ b/src/pages/settings/components/Theming/ThemePlaylists/ThemePlaylistModal.tsx
@@ -1,4 +1,4 @@
-import { Button, Group, Modal, SimpleGrid, Text, TextInput } from '@mantine/core';
+import { Button, Group, Modal, Text, TextInput } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react';
import { modalClasses, textInputClasses } from 'styles';
@@ -87,7 +87,7 @@ export function ThemePlaylistModal({
/>
) : (
<>
-
+
{selectedPlaylist?.name !== 'Time of Day' &&
allThemes?.map((miniTheme: Theme) => {
const newThemeList = themeList;
@@ -102,7 +102,7 @@ export function ThemePlaylistModal({
/>
);
})}
-
+
+
+ {
+ setSelectedTheme({
+ ...selectedTheme,
+ name: `${selectedTheme.name} copy`,
+ });
+ setIsNew(false);
+ }}
+ >
+ Confirm
+
+
{
- closePicker();
- setDesignerTheme({
- ...designerTheme,
- [colorId]: selectedTheme[colorId as keyof Theme],
+ setDuplicate(false);
+ setSelectedTheme({
+ id: undefined,
+ name: 'Default',
+ backgroundImage: 'none',
+ backgroundColor: '#EEEEEE',
+ searchColor: '#00000033',
+ primaryColor: '#222222',
+ accentColor: '#5c5c5c',
+ borderColor: '#5c5c5c',
+ randomEnabled: 1,
+ textShadowsEnabled: 0,
+ appShadowsEnabled: 0,
});
}}
>
-
+ Back
)}
- {backgroundPicker && (
-
-
-
+ ) : (
+ <>
+
+
+
+
+ {
+ setColorId('primaryColor');
+ setColorValue(selectedTheme.primaryColor);
+ handleSwatchClick();
+ }}
+ color={selectedTheme.primaryColor}
+ bd={`1px solid ${selectedTheme.primaryColor}`}
+ pos={'absolute'}
+ top={'6.25rem'}
+ left={'1.5rem'}
+ />
+
+
+ {
+ setColorId('accentColor');
+ setColorValue(selectedTheme.accentColor);
+ handleSwatchClick();
+ }}
+ color={selectedTheme.accentColor}
+ bd={`1px solid ${selectedTheme.primaryColor}`}
+ pos={'absolute'}
+ bottom={'6rem'}
+ left={'11rem'}
+ />
+
+
+ {
+ setColorId('backgroundColor');
+ setColorValue(selectedTheme.backgroundColor);
+ handleSwatchClick();
+ }}
+ color={selectedTheme.backgroundColor}
+ bd={`1px solid ${selectedTheme.primaryColor}`}
+ pos={'absolute'}
+ bottom={'10rem'}
+ right={'3.5rem'}
+ />
+
+
+ {
+ setColorId('borderColor');
+ setColorValue(selectedTheme.borderColor);
+ handleSwatchClick();
+ }}
+ color={selectedTheme.borderColor}
+ bd={`1px solid ${selectedTheme.primaryColor}`}
+ pos={'absolute'}
+ top={'4.5rem'}
+ right={'15%'}
+ />
+
+
+ {
+ setColorId('searchColor');
+ setColorValue(selectedTheme.searchColor);
+ handleSwatchClick();
+ }}
+ color={selectedTheme.searchColor}
+ bd={`1px solid ${selectedTheme.primaryColor}`}
+ pos={'absolute'}
+ top={'3.25rem'}
+ left={'6.5rem'}
+ />
+
+
+ {
+ closePicker();
+ openBackgroundPicker();
+ setDesignerTheme(selectedTheme);
+ }}
+ pos={'absolute'}
+ right={0}
+ >
+
+
+
+
+
+ {pickerOpen && (
+
+
+ Change{' '}
+ {
+ BackgroundProperties[
+ colorId as keyof typeof BackgroundProperties
+ ]
+ }
+
+ {colorId === 'backgroundColor' && (
+ {
+ setColorValue(e);
+ setDesignerTheme({
+ ...designerTheme,
+ backgroundColor: e,
+ });
}}
+ format={'hex'}
+ w={'90%'}
+ mb={'1.5rem'}
+ />
+ )}
+ {colorId !== 'backgroundColor' && (
+ {
+ setColorValue(e);
+ setDesignerTheme({
+ ...designerTheme,
+ [colorId]: e,
+ });
+ }}
+ format={'hexa'}
+ w={'90%'}
+ />
+ )}
+ setColorValue(e.target.value)}
+ />
+
+ {
- setQuery('');
+ switch (colorId) {
+ case 'primaryColor':
+ setSelectedTheme({
+ ...selectedTheme,
+ primaryColor: colorValue,
+ });
+ break;
+ case 'accentColor':
+ setSelectedTheme({
+ ...selectedTheme,
+ accentColor: colorValue,
+ });
+ break;
+ case 'backgroundColor':
+ setSelectedTheme({
+ ...selectedTheme,
+ backgroundColor: colorValue,
+ });
+ break;
+ case 'borderColor':
+ setSelectedTheme({
+ ...selectedTheme,
+ borderColor: colorValue,
+ });
+ break;
+ case 'searchColor':
+ setSelectedTheme({
+ ...selectedTheme,
+ searchColor: colorValue,
+ });
+ break;
+ }
+ closePicker();
+ }}
+ >
+
+
+ {
+ closePicker();
+ setDesignerTheme({
+ ...designerTheme,
+ [colorId]: selectedTheme[colorId as keyof Theme],
+ });
+ }}
+ >
+
+
+
+
+ )}
+ {backgroundPicker && (
+
+
+ {
+ setQuery('');
+ }}
+ />
+ }
+ onChange={(event) => {
+ setQuery(event.currentTarget.value);
+ setHovered(-1);
+ }}
+ onKeyDown={(event) => {
+ if (event.key === 'ArrowDown') {
+ event.preventDefault();
+ setHovered((current) => {
+ const nextIndex =
+ current + 1 >= filtered.length ? current : current + 1;
+ viewportRef.current
+ ?.querySelectorAll('[data-list-item]')
+ ?.[nextIndex]?.scrollIntoView({ block: 'nearest' });
+ return nextIndex;
+ });
+ }
+
+ if (event.key === 'ArrowUp') {
+ event.preventDefault();
+ setHovered((current) => {
+ const nextIndex = current - 1 < 0 ? current : current - 1;
+ viewportRef.current
+ ?.querySelectorAll('[data-list-item]')
+ ?.[nextIndex]?.scrollIntoView({ block: 'nearest' });
+ return nextIndex;
+ });
+ }
}}
/>
- }
- onChange={(event) => {
- setQuery(event.currentTarget.value);
- setHovered(-1);
- }}
- onKeyDown={(event) => {
- if (event.key === 'ArrowDown') {
- event.preventDefault();
- setHovered((current) => {
- const nextIndex =
- current + 1 >= filtered.length ? current : current + 1;
- viewportRef.current
- ?.querySelectorAll('[data-list-item]')
- ?.[nextIndex]?.scrollIntoView({ block: 'nearest' });
- return nextIndex;
- });
- }
-
- if (event.key === 'ArrowUp') {
- event.preventDefault();
- setHovered((current) => {
- const nextIndex = current - 1 < 0 ? current : current - 1;
- viewportRef.current
- ?.querySelectorAll('[data-list-item]')
- ?.[nextIndex]?.scrollIntoView({ block: 'nearest' });
- return nextIndex;
- });
- }
- }}
- />
-
-
-
- {items}
-
-
-
- {
- setSelectedTheme({
- ...selectedTheme,
- backgroundImage: designerTheme.backgroundImage,
- });
- closeBackgroundPicker();
- }}
- >
-
-
- {
- closeBackgroundPicker();
- setDesignerTheme({
- ...designerTheme,
- backgroundImage: selectedTheme.backgroundImage,
- });
- }}
- >
-
-
-
-
- )}
- {!pickerOpen && !backgroundPicker && (
-
-
- {
- setSelectedTheme({
- ...selectedTheme,
- textShadowsEnabled: e.target.checked ? 1 : 0,
- });
- }}
- styles={{
- root: { width: '50%' },
- body: {
- width: '100%',
- display: 'flex',
- justifyContent: 'space-between',
- },
- }}
- />
- {
- setSelectedTheme({
- ...selectedTheme,
- appShadowsEnabled: e.target.checked ? 1 : 0,
- });
- }}
- styles={{
- root: { width: '50%' },
- body: {
- width: '100%',
- display: 'flex',
- justifyContent: 'space-between',
- },
- }}
- />
-
- {JSON.stringify(defaultTheme) !== JSON.stringify(selectedTheme) && (
- <>
- Changes:
+
-
- {getObjectDiff(defaultTheme, designerTheme) !== null &&
- Object.keys(getObjectDiff(defaultTheme, designerTheme)).map(
- (key: any) => {
- return (
-
-
- {
- BackgroundProperties[
- key as keyof typeof BackgroundProperties
- ]
- }
-
- {key === 'name' && (
-
- {defaultTheme[key as keyof Theme]}
- →
- {designerTheme[key as keyof Theme]}
-
- )}
- {key === 'backgroundImage' && (
-
-
- →
-
-
- )}
- {key.includes('Color') && (
-
-
- →
-
-
- )}
- {key.includes('Shadow') && (
-
- {defaultTheme[key as keyof Theme] === 0 ? (
- Off
- ) : (
- On
- )}
- →
- {designerTheme[key as keyof Theme] === 0 ? (
- Off
- ) : (
- On
- )}
-
- )}
-
- );
- },
- )}
+
+ {items}
- >
+
+ {
+ setSelectedTheme({
+ ...selectedTheme,
+ backgroundImage: designerTheme.backgroundImage,
+ });
+ closeBackgroundPicker();
+ }}
+ >
+
+
+ {
+ closeBackgroundPicker();
+ setDesignerTheme({
+ ...designerTheme,
+ backgroundImage: selectedTheme.backgroundImage,
+ });
+ }}
+ >
+
+
+
+
+ )}
+ {!pickerOpen && !backgroundPicker && (
+
+
+ {
+ setSelectedTheme({
+ ...selectedTheme,
+ textShadowsEnabled: e.target.checked ? 1 : 0,
+ });
+ }}
+ styles={{
+ root: { width: '50%' },
+ body: {
+ width: '100%',
+ display: 'flex',
+ justifyContent: 'space-between',
+ },
+ }}
+ />
+ {
+ setSelectedTheme({
+ ...selectedTheme,
+ appShadowsEnabled: e.target.checked ? 1 : 0,
+ });
+ }}
+ styles={{
+ root: { width: '50%' },
+ body: {
+ width: '100%',
+ display: 'flex',
+ justifyContent: 'space-between',
+ },
+ }}
+ />
+
+ {JSON.stringify(defaultTheme) !== JSON.stringify(selectedTheme) && (
+ <>
+ Changes:
+
+
+ {getObjectDiff(defaultTheme, designerTheme) !== null &&
+ Object.keys(
+ getObjectDiff(defaultTheme, designerTheme),
+ ).map((key: any) => {
+ return (
+
+
+ {
+ BackgroundProperties[
+ key as keyof typeof BackgroundProperties
+ ]
+ }
+
+ {key === 'name' && (
+
+ {defaultTheme[key as keyof Theme]}
+ →
+ {designerTheme[key as keyof Theme]}
+
+ )}
+ {key === 'backgroundImage' && (
+
+
+ →
+
+
+ )}
+ {key.includes('Color') && (
+
+
+ →
+
+
+ )}
+ {key.includes('Shadow') && (
+
+ {defaultTheme[key as keyof Theme] === 0 ? (
+ Off
+ ) : (
+ On
+ )}
+ →
+ {designerTheme[key as keyof Theme] === 0 ? (
+ Off
+ ) : (
+ On
+ )}
+
+ )}
+
+ );
+ })}
+
+
+ >
+ )}
+ {
+ setSelectedTheme({ ...defaultTheme });
+ }}
+ mih={'36px'}
+ >
+ Reset Changes
+
+
)}
- {
- setSelectedTheme({ ...defaultTheme });
- }}
- mih={'36px'}
- >
- Reset Changes
-
- )}
-
-
-
-
- Save
-
- {
- closeBackgroundPicker();
- handleClose();
- }}
- >
- Cancel
-
-
+
+
+
+ Save
+
+ {
+ closeBackgroundPicker();
+ handleClose();
+ setDuplicate(false);
+ }}
+ >
+ Cancel
+
+
+ >
+ )}
diff --git a/src/pages/settings/components/Theming/Themes/Themes.tsx b/src/pages/settings/components/Theming/Themes/Themes.tsx
index cbde821..4ae62cd 100644
--- a/src/pages/settings/components/Theming/Themes/Themes.tsx
+++ b/src/pages/settings/components/Theming/Themes/Themes.tsx
@@ -44,7 +44,7 @@ export function Themes() {
primaryColor: '#222222',
accentColor: '#5c5c5c',
borderColor: '#5c5c5c',
- randomEnabled: 0,
+ randomEnabled: 1,
textShadowsEnabled: 0,
appShadowsEnabled: 0,
});
@@ -52,6 +52,7 @@ export function Themes() {
const [searchValue, setSearchValue] = useState('');
const [debouncedSearch] = useDebouncedValue(searchValue, 500);
const [isLoading, setIsLoading] = useState(false);
+ const [isNew, setIsNew] = useState(false);
const navigate = useNavigate();
const {
currentTheme,
@@ -70,6 +71,7 @@ export function Themes() {
const handleClose = () => {
closeThemeModal();
closePicker();
+ setIsNew(false);
setSelectedTheme({
id: undefined,
name: 'default',
@@ -79,12 +81,17 @@ export function Themes() {
primaryColor: '#222222',
accentColor: '#5c5c5c',
borderColor: '#5c5c5c',
- randomEnabled: 0,
+ randomEnabled: 1,
textShadowsEnabled: 0,
appShadowsEnabled: 0,
});
};
+ const handleNewTheme = () => {
+ setIsNew(true);
+ openThemeModal();
+ };
+
const handleSaveTheme = () => {
mutateAsync({
method: selectedTheme?.id === undefined ? 'POST' : 'PATCH',
@@ -147,6 +154,9 @@ export function Themes() {
pickerOpen={pickerOpen}
closePicker={closePicker}
handleSaveTheme={handleSaveTheme}
+ isNew={isNew}
+ setIsNew={setIsNew}
+ allThemes={allThemes}
/>