Random updates

This commit is contained in:
Sean Edmunds
2025-08-01 15:25:27 -05:00
parent 70c28f54b8
commit 07ce9da818
11 changed files with 186 additions and 40 deletions

28
package-lock.json generated
View File

@@ -13,9 +13,9 @@
"@mantine/form": "^8.2.2",
"@mantine/hooks": "^8.2.2",
"@mantine/notifications": "^8.2.2",
"@tanstack/react-query": "^5.84.0",
"@tanstack/react-query-devtools": "^5.84.0",
"@tanstack/react-query-persist-client": "^5.84.0",
"@tanstack/react-query": "^5.84.1",
"@tanstack/react-query-devtools": "^5.84.1",
"@tanstack/react-query-persist-client": "^5.84.1",
"idb-keyval": "^6.2.2",
"jotai": "^2.12.5",
"react": "^19.1.1",
@@ -1662,9 +1662,9 @@
}
},
"node_modules/@tanstack/react-query": {
"version": "5.84.0",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.84.0.tgz",
"integrity": "sha512-iPycFGLq5lltDE16Jf13Nx7SOvtfoopfOH/+Ahbdd+z4QqOfYu/SOkY86AVYVcKjneuqPxTm8e85lSGhwe0cog==",
"version": "5.84.1",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.84.1.tgz",
"integrity": "sha512-zo7EUygcWJMQfFNWDSG7CBhy8irje/XY0RDVKKV4IQJAysb+ZJkkJPcnQi+KboyGUgT+SQebRFoTqLuTtfoDLw==",
"license": "MIT",
"dependencies": {
"@tanstack/query-core": "5.83.1"
@@ -1678,9 +1678,9 @@
}
},
"node_modules/@tanstack/react-query-devtools": {
"version": "5.84.0",
"resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.84.0.tgz",
"integrity": "sha512-7SwgDOm1Waz9wKOwR8WPaN4yo6zq1UBQxGLWy/SJWZOoo2bUVRVlGPvFicuOUwQWv5VDzzNYq4GvTQnX4T3xtw==",
"version": "5.84.1",
"resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.84.1.tgz",
"integrity": "sha512-nle+OQ9B3Z3EG2R3ixvaNcJ6OeqGwmAc5iMDW6Vj+emLZkWRrN3BDsrzZQu414n34lpxplnC7z1jmKuU/scHCQ==",
"license": "MIT",
"dependencies": {
"@tanstack/query-devtools": "5.84.0"
@@ -1690,14 +1690,14 @@
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"@tanstack/react-query": "^5.84.0",
"@tanstack/react-query": "^5.84.1",
"react": "^18 || ^19"
}
},
"node_modules/@tanstack/react-query-persist-client": {
"version": "5.84.0",
"resolved": "https://registry.npmjs.org/@tanstack/react-query-persist-client/-/react-query-persist-client-5.84.0.tgz",
"integrity": "sha512-vpe2KnCkbcZmbk4SXObKxJL9BKP6IlZxBOi5Ua7oZFa8LKoECBHR+oK0YoEhtQKVaBaRbSc7G0YsuUZWct7vRQ==",
"version": "5.84.1",
"resolved": "https://registry.npmjs.org/@tanstack/react-query-persist-client/-/react-query-persist-client-5.84.1.tgz",
"integrity": "sha512-Qpn2kg7eI+dI4AHeXmI0FYn1JVwluSESA3iJ+KJv9zZiluPhiFmzFT++j9QYwu/LHUwp4PmjqSPHkuBLiYkTRQ==",
"license": "MIT",
"dependencies": {
"@tanstack/query-persist-client-core": "5.83.1"
@@ -1707,7 +1707,7 @@
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"@tanstack/react-query": "^5.84.0",
"@tanstack/react-query": "^5.84.1",
"react": "^18 || ^19"
}
},

View File

@@ -15,9 +15,9 @@
"@mantine/form": "^8.2.2",
"@mantine/hooks": "^8.2.2",
"@mantine/notifications": "^8.2.2",
"@tanstack/react-query": "^5.84.0",
"@tanstack/react-query-devtools": "^5.84.0",
"@tanstack/react-query-persist-client": "^5.84.0",
"@tanstack/react-query": "^5.84.1",
"@tanstack/react-query-devtools": "^5.84.1",
"@tanstack/react-query-persist-client": "^5.84.1",
"idb-keyval": "^6.2.2",
"jotai": "^2.12.5",
"react": "^19.1.1",

View File

@@ -130,7 +130,9 @@ export function useMutationAPI() {
endpoint === 'api/bookmarkCategoryOrder' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/bookmarkCategories'] });
endpoint === 'api/applicationOrder' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/applicationOrder'] });
queryClient.refetchQueries({ queryKey: ['GET', 'api/applications'] });
endpoint === 'api/applications' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/enabledApplications'] });
endpoint === 'api/bookmarks' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/themes'] });
endpoint === 'api/providers' &&
@@ -145,7 +147,9 @@ export function useMutationAPI() {
endpoint === 'api/bookmarkCategoryOrder' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/bookmarkCategories'] });
endpoint === 'api/applicationOrder' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/applicationOrder'] });
queryClient.refetchQueries({ queryKey: ['GET', 'api/applications'] });
endpoint === 'api/applications' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/enabledApplications'] });
endpoint === 'api/bookmarks' &&
queryClient.refetchQueries({ queryKey: ['GET', 'api/themes'] });
endpoint === 'api/providers' &&

View File

@@ -1,6 +1,7 @@
import { Button, Group, Modal, Text, TextInput } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react';
import { FaTag } from 'react-icons/fa';
import { modalClasses, textInputClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types';
import { ThemePreviewMiddleware } from '../../Themes';
@@ -55,6 +56,14 @@ export function ThemePlaylistModal({
<Modal.Overlay />
<Modal.Content>
<Modal.Header style={{ justifyContent: 'center' }}>
{selectedPlaylist?.name !== 'Time of Day' && (
<Group pos={'absolute'} justify='center' left={'.5rem'}>
<FaTag color={'var(--mantine-color-background-0)'} size={'2.5rem'} />
<Text size='1.25rem' pos={'absolute'} c={'var(--mantine-color-primary-0)'}>
{themeList.length}
</Text>
</Group>
)}
<Group pos={'absolute'}>
<Text c={'var(--mantine-color-background-0)'}>Name:</Text>
<TextInput

View File

@@ -18,12 +18,14 @@ export function ThemeCard({
themePlaylists,
setSelectedTheme,
openThemeModal,
enabled = true,
}: {
cardTheme: Theme;
allThemes: Theme[];
themePlaylists: ThemePlaylist[];
setSelectedTheme: (theme: Theme) => void;
openThemeModal: () => void;
enabled?: boolean;
}) {
const { hovered, ref: hoverRef } = useHover();
const { ref, width } = useElementSize();
@@ -107,7 +109,7 @@ export function ThemeCard({
handleOpen={handleOpen}
themePlaylists={themePlaylists}
openModal={openModal}
hovered={hovered}
hovered={hovered && enabled}
hoverPreview={hoverPreview}
setHoverPreview={setHoverPreview}
transition={transition}
@@ -145,7 +147,7 @@ export function ThemeCard({
setCurrentThemePlaylist('All');
}}
>
{hovered && currentTheme !== 'random' && (
{hovered && enabled && currentTheme !== 'random' && (
<Overlay
style={{
opacity: !hoverPreview || transition ? 0.5 : 0,

View File

@@ -1,9 +1,10 @@
import { Group, Modal, Text, TextInput } from '@mantine/core';
import { ActionIcon, Card, Group, Modal, Text, TextInput } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { useCommonHooks, useMutationAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
import { modalClasses, textInputClasses } from 'styles';
import type { Theme } from 'types';
import type { Theme, ThemePlaylist } from 'types';
import { NewThemeStarter } from './components/NewThemeStarter';
import { ThemeEditor } from './components/ThemeEditor';
@@ -16,6 +17,7 @@ export function ThemeModal({
setIsNew,
allThemes,
closeThemeModal,
themePlaylists,
}: {
opened: boolean;
selectedTheme: Theme;
@@ -25,6 +27,7 @@ export function ThemeModal({
setIsNew: (isNew: boolean) => void;
allThemes: Theme[];
closeThemeModal: () => void;
themePlaylists: ThemePlaylist[];
}) {
const themesEndpoint = 'api/themes';
const { isMobile } = useCommonHooks();
@@ -36,11 +39,22 @@ export function ThemeModal({
const [pickerOpen, { open: openPicker, close: closePicker }] = useDisclosure(false);
const [duplicate, setDuplicate] = useState(false);
const [hidden, setHidden] = useState(false);
const playlists =
themePlaylists.length > 0
? themePlaylists.filter((playlist: ThemePlaylist) => {
if (playlist.themeList.includes(`${selectedTheme.id}`)) {
return playlist;
}
})
: [];
const handleClose = () => {
closeThemeModal();
closePicker();
setIsNew(false);
setHidden(false);
setSelectedTheme({
id: undefined,
name: 'default',
@@ -87,7 +101,7 @@ export function ThemeModal({
keepMounted={true}
>
<Modal.Overlay />
<Modal.Content>
<Modal.Content style={{ overflow: 'visible' }}>
<Modal.Header style={{ justifyContent: 'center' }}>
{!isNew && (
<Group pos={'absolute'}>
@@ -116,6 +130,87 @@ export function ThemeModal({
<Modal.CloseButton />
</Modal.Header>
<Modal.Body>
<Group
gap={0}
pos={'absolute'}
h={'90%'}
w={'100%'}
bg={'var(--mantine-color-background-0)'}
style={{ zIndex: -1 }}
/>
{playlists.length > 0 && !isMobile && (
<Card
pos={'absolute'}
top={0}
style={{
color: 'var(--mantine-color-primary-0)',
backgroundColor:
'color-mix(in srgb, var(--mantine-color-accent-0), transparent 80%)',
backdropFilter: 'blur(15px)',
borderRadius: '8px',
right: hidden ? '-10rem' : 0,
transition: 'all .2s ease',
overflow: 'visible',
zIndex: -2,
}}
>
<Card.Section
h={'60px'}
px={'2rem'}
bg={'var(--mantine-color-accent-0)'}
style={{
alignContent: 'center',
borderTopRightRadius: '4px',
borderTopLeftRadius: '4px',
}}
>
<Text
c={'var(--mantine-color-background-0)'}
fz={'1.5rem'}
w={'100%'}
style={{ textAlign: 'center' }}
>
Playlists:
</Text>
<ActionIcon
pos={'absolute'}
right={-32}
top={16}
p={0}
m={0}
onClick={() => setHidden((hidden) => !hidden)}
style={{
borderRadius: '50%',
backgroundColor: 'var(--mantine-color-accent-0)',
color: 'var(--mantine-color-background-0)',
}}
>
{hidden ? <FaChevronLeft /> : <FaChevronRight />}
</ActionIcon>
</Card.Section>
<Card.Section>
{playlists.map((playlist: ThemePlaylist) => (
<Text
key={playlist.id}
px={'1rem'}
py={'0.25rem'}
style={{
fontSize: '18px',
fontWeight: 600,
backgroundColor:
'color-mix(in srgb, var(--mantine-color-background-0), transparent 0%)',
borderRadius: '24px',
margin: '0.5rem',
textAlign: 'center',
color: 'var(--mantine-color-primary-0)',
}}
>
{playlist.name}
</Text>
))}
</Card.Section>
</Card>
)}
{isNew ? (
<NewThemeStarter
allThemes={allThemes}

View File

@@ -1,6 +1,6 @@
import { Box, Button, Divider, Group, Overlay, Stack, Text } from '@mantine/core';
import { useLongPress } from '@mantine/hooks';
import { useState } from 'react';
import { useHover, useLongPress } from '@mantine/hooks';
import { useMemo, useState } from 'react';
import { FaArrowRight } from 'react-icons/fa';
import { textClasses } from 'styles';
import type { Theme } from 'types';
@@ -27,10 +27,14 @@ export function NewThemeStarter({
const handlers = useLongPress(() => {
setPressed(true);
});
const randomTheme = {
...allThemes[Math.floor(Math.random() * allThemes.length)],
randomEnabled: 1,
};
const { hovered: newHover, ref: newRef } = useHover();
const { hovered: copyHover, ref: copyRef } = useHover();
const randomTheme = useMemo(() => {
return {
...allThemes[Math.floor(Math.random() * allThemes.length)],
randomEnabled: 1,
};
}, []);
return (
<Stack align='center' p={'2rem'}>
{!duplicate ? (
@@ -40,12 +44,17 @@ export function NewThemeStarter({
</Text>
<Group>
<Stack
ref={newRef}
justify='center'
align='center'
bg={'var(--mantine-color-primary-0)'}
bg={
newHover
? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-primary-0)'
}
px={'1rem'}
py={'3rem'}
style={{ borderRadius: '8px' }}
style={{ borderRadius: '8px', transition: 'all .2s ease-in-out' }}
>
<ThemeCard
cardTheme={{
@@ -65,6 +74,7 @@ export function NewThemeStarter({
themePlaylists={[]}
setSelectedTheme={setSelectedTheme}
openThemeModal={() => {}}
enabled={false}
/>
<Button
pos={'absolute'}
@@ -99,13 +109,21 @@ export function NewThemeStarter({
m={'2rem'}
/>
<Stack
ref={copyRef}
justify='center'
align='center'
pos={'relative'}
bg={'var(--mantine-color-primary-0)'}
bg={
copyHover
? 'var(--mantine-color-accent-0)'
: 'var(--mantine-color-primary-0)'
}
px={'1rem'}
py={'3rem'}
style={{ borderRadius: '8px' }}
style={{
borderRadius: '8px',
transition: 'all .2s ease-in-out',
}}
>
<ThemeCard
cardTheme={randomTheme}
@@ -113,6 +131,7 @@ export function NewThemeStarter({
themePlaylists={[]}
setSelectedTheme={setSelectedTheme}
openThemeModal={() => {}}
enabled={false}
/>
<Overlay
bg={'transparent'}

View File

@@ -157,6 +157,7 @@ export function ThemeEditor({
pos={'absolute'}
top={'11.5rem'}
left={'8.5rem'}
style={{ cursor: 'pointer' }}
/>
</Tooltip>
<Tooltip
@@ -175,6 +176,7 @@ export function ThemeEditor({
pos={'absolute'}
bottom={'6rem'}
left={'11rem'}
style={{ cursor: 'pointer' }}
/>
</Tooltip>
<Tooltip
@@ -193,6 +195,7 @@ export function ThemeEditor({
pos={'absolute'}
bottom={'4rem'}
right={'3.5rem'}
style={{ cursor: 'pointer' }}
/>
</Tooltip>
<Tooltip
@@ -211,6 +214,7 @@ export function ThemeEditor({
pos={'absolute'}
top={'4.5rem'}
right={'15%'}
style={{ cursor: 'pointer' }}
/>
</Tooltip>
<Tooltip
@@ -229,6 +233,7 @@ export function ThemeEditor({
pos={'absolute'}
top={'3.25rem'}
left={'6.5rem'}
style={{ cursor: 'pointer' }}
/>
</Tooltip>
<Tooltip
@@ -280,7 +285,7 @@ export function ThemeEditor({
Change{' '}
{BackgroundProperties[colorId as keyof typeof BackgroundProperties]}
</Text>
{(colorId === 'backgroundColor' || colorId === 'searchColor') && (
{colorId === 'searchColor' && (
<ColorPicker
value={colorValue}
onChange={(e) => {
@@ -294,7 +299,7 @@ export function ThemeEditor({
w={'90%'}
/>
)}
{colorId !== 'backgroundColor' && colorId !== 'searchColor' && (
{colorId !== 'searchColor' && (
<ColorPicker
value={colorValue}
onChange={(e) => {
@@ -320,7 +325,7 @@ export function ThemeEditor({
<Button
c={'white'}
onClick={() => {
const rc = randomColor();
const rc = randomColor(colorId === 'searchColor');
setColorValue(rc);
setDesignerTheme({
...selectedTheme,
@@ -604,7 +609,7 @@ export function ThemeEditor({
setSelectedTheme({
...selectedTheme,
backgroundColor: randomColor(),
searchColor: randomColor(),
searchColor: randomColor(true),
primaryColor: randomColor(),
accentColor: randomColor(),
borderColor: randomColor(),

View File

@@ -28,6 +28,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
'--mantine-color-background-0': selectedTheme?.backgroundColor,
'--mantine-color-border-0': selectedTheme?.borderColor,
'--mantine-color-search-0': selectedTheme?.searchColor,
transition: 'all 0.2s ease',
}}
align='center'
gap={0}
@@ -67,6 +68,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
textShadow: selectedTheme.textShadowsEnabled
? '3px 3px 1px color-mix(in srgb, var(--mantine-color-primary-0), transparent 50%)'
: undefined,
transition: 'all 0.2s ease',
}}
>
Hello World!
@@ -79,6 +81,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
boxShadow: selectedTheme.textShadowsEnabled
? '1px 1px 1px color-mix(in srgb, var(--mantine-color-accent-0), transparent 50%)'
: undefined,
transition: 'all 0.2s ease',
}}
>
Applications
@@ -103,6 +106,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr 1fr',
transition: 'all 0.2s ease',
}}
w={'100%'}
display={'grid'}
@@ -147,6 +151,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
boxShadow: selectedTheme.textShadowsEnabled
? '1px 1px 1px color-mix(in srgb, var(--mantine-color-accent-0), transparent 50%)'
: undefined,
transition: 'all 0.2s ease',
}}
>
Bookmarks
@@ -182,6 +187,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
textTransform: 'uppercase',
marginTop: '1.25rem',
marginBottom: '1.25rem',
transition: 'all 0.2s ease',
}}
>
Example Category
@@ -192,6 +198,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
fontSize: '.875rem',
lineHeight: 2,
color: selectedTheme?.primaryColor,
transition: 'all 0.2s ease',
}}
component='a'
>
@@ -203,6 +210,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
fontSize: '.875rem',
lineHeight: 2,
color: selectedTheme?.primaryColor,
transition: 'all 0.2s ease',
}}
component='a'
>
@@ -228,6 +236,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
textTransform: 'uppercase',
marginTop: '1.25rem',
marginBottom: '1.25rem',
transition: 'all 0.2s ease',
}}
>
Example Category
@@ -238,6 +247,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
fontSize: '.875rem',
lineHeight: 2,
color: selectedTheme?.primaryColor,
transition: 'all 0.2s ease',
}}
component='a'
>
@@ -249,6 +259,7 @@ export function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
fontSize: '.875rem',
lineHeight: 2,
color: selectedTheme?.primaryColor,
transition: 'all 0.2s ease',
}}
component='a'
>

View File

@@ -127,6 +127,7 @@ export function Themes() {
setIsNew={setIsNew}
allThemes={allThemes}
closeThemeModal={closeThemeModal}
themePlaylists={themePlaylists}
/>
<BackgroundModal
backgroundModalOpened={backgroundModalOpened}

View File

@@ -2,7 +2,7 @@
&.root {
padding-top: 1.5rem;
color: var(--mantine-color-primary-0);
background-color: transparent;
background-color: color-mix(in srgb, var(--mantine-color-background-0), transparent 50%);
backdrop-filter: blur(5px) brightness(1.25);
}
}
@@ -10,7 +10,7 @@
.settingsItemHover {
&.root {
color: var(--mantine-color-primary-0);
background-color: transparent;
background-color: color-mix(in srgb, var(--mantine-color-background-0), transparent 50%);
backdrop-filter: blur(5px) brightness(1.25);
cursor: pointer;
&:hover {