feat: Browser mod cards

This commit is contained in:
LynithDev 2024-06-23 22:02:00 +02:00
parent 7432f7cba9
commit 32741d0c3e
15 changed files with 392 additions and 50 deletions

View file

@ -46,6 +46,7 @@
"sass": "^1.77.6", "sass": "^1.77.6",
"unocss": "^0.61.0", "unocss": "^0.61.0",
"vite-plugin-solid": "^2.10.2", "vite-plugin-solid": "^2.10.2",
"vite-plugin-solid-svg": "^0.8.1",
"vite-tsconfig-paths": "^4.3.2" "vite-tsconfig-paths": "^4.3.2"
} }
} }

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"><path fill="currentColor" d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/></svg>

After

Width:  |  Height:  |  Size: 769 B

View file

@ -1,5 +1,5 @@
import { Index, type JSX, type ParentProps, children, createSignal, splitProps } from 'solid-js'; import { type Accessor, Index, type JSX, Match, type ParentProps, type ResolvedJSXElement, type Setter, Switch, children, createSignal, splitProps } from 'solid-js';
import { ChevronDownIcon } from '@untitled-theme/icons-solid'; import { ChevronDownIcon, ChevronUpIcon } from '@untitled-theme/icons-solid';
import Popup from '../overlay/Popup'; import Popup from '../overlay/Popup';
import Button from './Button'; import Button from './Button';
import styles from './Dropdown.module.scss'; import styles from './Dropdown.module.scss';
@ -8,10 +8,18 @@ export type DropdownProps = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'onChange'>
onChange?: (selected: number) => any; onChange?: (selected: number) => any;
text?: string; text?: string;
selected?: number; selected?: number;
dropdownClass?: string;
component?: (
props: {
visible: Accessor<boolean>;
setVisible: Setter<boolean>;
children: () => ResolvedJSXElement[];
}
) => JSX.Element;
}; };
function Dropdown(props: DropdownProps) { function Dropdown(props: DropdownProps) {
const [split, rest] = splitProps(props, ['children', 'class', 'text', 'onChange', 'selected']); const [split, rest] = splitProps(props, ['children', 'class', 'text', 'onChange', 'selected', 'component', 'dropdownClass']);
const [visible, setVisible] = createSignal(false); const [visible, setVisible] = createSignal(false);
// eslint-disable-next-line solid/reactivity -- todo // eslint-disable-next-line solid/reactivity -- todo
@ -29,27 +37,39 @@ function Dropdown(props: DropdownProps) {
split.onChange(selected()); split.onChange(selected());
} }
return ( const Component = () => (
<div ref={ref} class={`${styles.dropdown} ${split.class}`} {...rest}> split.component
<Button ? split.component({ visible, setVisible, children: items })
class="h-full w-full" : (
buttonStyle="secondary" <Button
onClick={() => setVisible(true)} class="h-full w-full"
iconRight={<ChevronDownIcon />} buttonStyle="secondary"
> onClick={() => setVisible(true)}
<div iconRight={<ChevronDownIcon />}
class="flex-1 flex flex-row items-center text-nowrap gap-1 h-full overflow-hidden"
> >
<span>{split.text}</span> <div
{items()[selected()]} class="flex-1 flex flex-row items-center text-nowrap gap-1 h-full overflow-hidden"
</div> >
</Button> <span>{split.text}</span>
{items()[selected()]}
</div>
</Button>
)
);
return (
<div ref={ref} class={`${styles.dropdown} ${split.class || ''}`} {...rest}>
<Component />
<Popup <Popup
mount={ref} mount={ref}
visible={visible} visible={visible}
setVisible={setVisible} setVisible={setVisible}
ref={ref => ref.classList.add('mt-1', 'w-full')} ref={(ref) => {
ref.classList.add('mt-1', 'w-full');
if (split.dropdownClass)
ref.classList.add(...split.dropdownClass.split(' '));
}}
> >
<div class="bg-secondary rounded-lg border border-gray-05 p-1 shadow-md shadow-black/30"> <div class="bg-secondary rounded-lg border border-gray-05 p-1 shadow-md shadow-black/30">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
@ -78,4 +98,38 @@ Dropdown.Row = function (props: ParentProps) {
); );
}; };
type MinimalDropdownProps = Omit<DropdownProps, 'component' | 'icon'> & {
icon?: JSX.Element;
};
Dropdown.Minimal = function (props: MinimalDropdownProps) {
const [split, rest] = splitProps(props, ['icon']);
return (
<Dropdown
class="w-auto!"
dropdownClass="w-max!"
component={props => (
<Button
onClick={() => props.setVisible(true)}
children={(
<Switch>
<Match when={split.icon}>
{split.icon}
</Match>
<Match when={props.visible() === true}>
<ChevronUpIcon />
</Match>
<Match when={props.visible() !== true}>
<ChevronDownIcon />
</Match>
</Switch>
)}
buttonStyle="iconSecondary"
/>
)}
{...rest}
/>
);
};
export default Dropdown; export default Dropdown;

View file

@ -33,7 +33,7 @@ function TextField(props: TextFieldProps) {
} }
return ( return (
<label for={id} class={`${styles.textfield} ${isValid() ? {} : styles.invalid}`}> <label for={id} class={`${styles.textfield} ${isValid() ? '' : styles.invalid}`}>
{fieldProps.iconLeft && <span class={styles.icon}>{fieldProps.iconLeft}</span>} {fieldProps.iconLeft && <span class={styles.icon}>{fieldProps.iconLeft}</span>}
<input <input
id={id} id={id}

View file

@ -18,7 +18,7 @@ import ClusterCover from '~ui/components/game/ClusterCover';
import type { Cluster } from '~bindings'; import type { Cluster } from '~bindings';
import useCommand from '~ui/hooks/useCommand'; import useCommand from '~ui/hooks/useCommand';
import { bridge } from '~imports'; import { bridge } from '~imports';
import { upperFirst } from '~utils/string'; import { upperFirst } from '~utils/primitives';
// TODO: Replace this into it's own component // TODO: Replace this into it's own component
// possibly with the core ui lirbary with autogenerated and optimized svgs pls // possibly with the core ui lirbary with autogenerated and optimized svgs pls

View file

@ -0,0 +1,23 @@
import { useSearchParams } from '@solidjs/router';
import ScrollableContainer from '~ui/components/ScrollableContainer';
function BrowserCategory() {
const [params] = useSearchParams<{
category: string;
}>();
return (
<div>
<h1>
Category =
{' '}
{params.category}
</h1>
<ScrollableContainer>
<span>test</span>
</ScrollableContainer>
</div>
);
}
export default BrowserCategory;

View file

@ -1,7 +1,10 @@
import { SearchMdIcon } from '@untitled-theme/icons-solid'; import { Download01Icon, FileCode01Icon, FilterLinesIcon, HeartIcon, SearchMdIcon } from '@untitled-theme/icons-solid';
import { For, createSignal } from 'solid-js'; import { type Accessor, For, type Setter, createEffect, createSignal } from 'solid-js';
import Button from '~ui/components/base/Button';
import Dropdown from '~ui/components/base/Dropdown'; import Dropdown from '~ui/components/base/Dropdown';
import TextField from '~ui/components/base/TextField'; import TextField from '~ui/components/base/TextField';
import ScrollableContainer from '~ui/components/ScrollableContainer';
import { abbreviateNumber } from '~utils/primitives';
import createSortable from '~utils/sorting'; import createSortable from '~utils/sorting';
interface CardProps { interface CardProps {
@ -35,21 +38,146 @@ function BrowserMain() {
'New': (a, b) => b.date_added - a.date_added, 'New': (a, b) => b.date_added - a.date_added,
}); });
function onSortChange(selected: number) {
sortable.setKey(selected);
}
return ( return (
<div class="flex flex-col flex-grow-1"> <div class="flex flex-col flex-1 h-full gap-2 relative">
<div class="flex flex-row gap-2 h-8"> <div class="flex flex-row flex-1 justify-between sticky top-0 z-10 bg-primary">
<TextField iconLeft={<SearchMdIcon />} /> <div class="flex flex-row gap-2 h-8">
<Dropdown onChange={onSortChange} text="Sort By: ">
<For each={Object.keys(sortable.sortables)}> <TextField
{sortable => ( iconLeft={<SearchMdIcon />}
<Dropdown.Row>{sortable}</Dropdown.Row> placeholder="Search for content"
)} />
</For>
</Dropdown> <Dropdown.Minimal
onChange={sortable.setKey}
icon={<FilterLinesIcon />}
>
<For each={Object.keys(sortable.sortables)}>
{sortable => (
<Dropdown.Row>{sortable}</Dropdown.Row>
)}
</For>
</Dropdown.Minimal>
</div>
<div class="flex flex-row gap-2 justify-end">
<Button
iconLeft={<Download01Icon />}
children="From URL"
/>
<Button
iconLeft={<FileCode01Icon />}
children="From File"
buttonStyle="secondary"
/>
</div>
</div>
<div class="flex flex-col h-full flex-1 gap-4 py-2">
{/* eslint-disable-next-line ts/no-use-before-define -- blh */}
<For each={_modRowData}>
{row => (
<>
<ModsRow {...row} />
<ModsRow {...row} />
<ModsRow {...row} />
</>
)}
</For>
</div>
</div>
);
}
const _modRowData: ModsRowProps[] = [
{
header: 'Highly Endorsed',
category: 'endorsed',
mods: [
{
name: 'EvergreenHUD',
description: 'Improves your heads up display.',
author: 'Polyfrost',
icon_url: 'https://cdn.modrinth.com/data/1yIQcc2b/icon.png',
page_url: 'https://modrinth.com/mod/evergreenhud',
provider: Provider.Modrinth,
downloads: 281700,
ratings: 220,
},
],
},
];
interface ModsRowProps {
header: string;
category: string;
mods: Mod[];
};
interface Mod {
name: string;
description: string;
author: string;
icon_url: string;
provider: Provider;
page_url: string;
downloads: number;
ratings: number;
}
function ModsRow(props: ModsRowProps) {
return (
<div class="flex flex-col flex-1 gap-3">
<div class="flex flex-row flex-1 justify-between">
<h4>{props.header}</h4>
<a class="text-fg-secondary hover:text-fg-secondary-hover active:text-fg-secondary-pressed" href={`/browser/category?category=${props.category}`}>See all</a>
</div>
<div class="flex flex-row gap-2">
<For each={props.mods}>
{mod => <ModCard {...mod} />}
</For>
</div>
</div>
);
}
function ModCard(props: Mod) {
return (
<div class="flex flex-col overflow-hidden rounded-lg bg-component-bg max-w-53 min-w-53 w-full max-h-68 min-h-68 h-full">
<div class="relative h-28 overflow-hidden flex justify-center items-center">
<img class="absolute filter-blur-xl z-0 max-w-none w-7/6" src={props.icon_url} alt={`Icon for ${props.name}`} />
<img class="relative w-2/5 aspect-ratio-square z-1 rounded-2xl" src={props.icon_url} alt={`Icon for ${props.name}`} />
</div>
<div class="flex flex-col flex-1 p-3 gap-4">
<div class="flex flex-col gap-2">
<h5 class="font-medium text-fg-primary">{props.name}</h5>
<p class="text-fg-secondary text-xs">
By
{' '}
<span class="text-fg-primary">{props.author}</span>
{' '}
on
{' '}
{Provider[props.provider]}
</p>
</div>
<p class="text-fg-secondary text-sm flex-1">{props.description}</p>
<div class="flex flex-row gap-4 text-xs">
<div class="flex flex-row items-center gap-2">
<Download01Icon class="w-4 h-4" />
{abbreviateNumber(props.downloads)}
</div>
<div class="flex flex-row items-center gap-2">
<HeartIcon class="w-4 h-4" />
{abbreviateNumber(props.ratings)}
</div>
</div>
</div> </div>
</div> </div>
); );

View file

@ -1,11 +1,13 @@
import { Route } from '@solidjs/router'; import { Route } from '@solidjs/router';
import type { ParentProps } from 'solid-js'; import type { ParentProps } from 'solid-js';
import BrowserMain from './BrowserMain'; import BrowserMain from './BrowserMain';
import BrowserCategory from './BrowserCategory';
function BrowserRoutes() { function BrowserRoutes() {
return ( return (
<> <>
<Route path="/" component={BrowserMain} /> <Route path="/" component={BrowserMain} />
<Route path="/category" component={BrowserCategory} />
</> </>
); );
} }

View file

@ -5,7 +5,7 @@ import Button from '../../../components/base/Button';
import ScrollableContainer from '~ui/components/ScrollableContainer'; import ScrollableContainer from '~ui/components/ScrollableContainer';
import Sidebar from '~ui/components/Sidebar'; import Sidebar from '~ui/components/Sidebar';
import type { Cluster } from '~bindings'; import type { Cluster } from '~bindings';
import { upperFirst } from '~utils/string'; import { upperFirst } from '~utils/primitives';
import useClusterContext from '~ui/hooks/useCluster'; import useClusterContext from '~ui/hooks/useCluster';
function ClusterOverview() { function ClusterOverview() {

View file

@ -6,6 +6,7 @@ import Toggle from '~ui/components/base/Toggle';
import Sidebar from '~ui/components/Sidebar'; import Sidebar from '~ui/components/Sidebar';
import useSettingsContext from '~ui/hooks/useSettings'; import useSettingsContext from '~ui/hooks/useSettings';
import Button from '~ui/components/base/Button'; import Button from '~ui/components/base/Button';
import DiscordIcon from '~assets/logos/discord.svg?component-solid';
function SettingsGeneral() { function SettingsGeneral() {
const settings = useSettingsContext(); const settings = useSettingsContext();
@ -18,7 +19,7 @@ function SettingsGeneral() {
<SettingsRow <SettingsRow
title="Discord RPC" title="Discord RPC"
description="Enable Discord Rich Presence." description="Enable Discord Rich Presence."
icon={<XIcon />} icon={<DiscordIcon class="w-6" />}
> >
<Toggle <Toggle
defaultChecked={!(settings.disable_discord ?? false)} defaultChecked={!(settings.disable_discord ?? false)}

View file

@ -0,0 +1,22 @@
export function upperFirst(object: any): string {
const str = object.toString();
return str.charAt(0).toUpperCase() + str.slice(1);
}
export function abbreviateNumber(n: number) {
if (n < 1e3)
return n;
if (n >= 1e3 && n < 1e6)
return `${+(n / 1e3).toFixed(1)}K`;
if (n >= 1e6 && n < 1e9)
return `${+(n / 1e6).toFixed(1)}M`;
if (n >= 1e9 && n < 1e12)
return `${+(n / 1e9).toFixed(1)}B`;
if (n >= 1e12)
return `${+(n / 1e12).toFixed(1)}T`;
};
export default {
upperFirst,
abbreviateNumber,
};

View file

@ -1,8 +0,0 @@
export function upperFirst(object: any): string {
const str = object.toString();
return str.charAt(0).toUpperCase() + str.slice(1);
}
export default {
upperFirst,
};

View file

@ -14,7 +14,11 @@
}, },
"resolveJsonModule": true, "resolveJsonModule": true,
"types": ["vitest/globals", "@testing-library/jest-dom"], "types": [
"vitest/globals",
"@testing-library/jest-dom",
"vite-plugin-solid-svg/types"
],
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"strict": true, "strict": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,

View file

@ -5,6 +5,7 @@ import type { Plugin, UserConfig } from 'vite';
import { defineConfig, loadEnv } from 'vite'; import { defineConfig, loadEnv } from 'vite';
import solid from 'vite-plugin-solid'; import solid from 'vite-plugin-solid';
import solidSvg from 'vite-plugin-solid-svg';
import paths from 'vite-tsconfig-paths'; import paths from 'vite-tsconfig-paths';
import unocss from 'unocss/vite'; import unocss from 'unocss/vite';
@ -15,6 +16,9 @@ export default defineConfig(async ({ mode }) => {
plugins: [ plugins: [
unocss(), unocss(),
solid(), solid(),
solidSvg({
defaultAsComponent: false,
}),
paths(), paths(),
devtools(), devtools(),
], ],

116
pnpm-lock.yaml generated
View file

@ -162,6 +162,9 @@ importers:
vite-plugin-solid: vite-plugin-solid:
specifier: ^2.10.2 specifier: ^2.10.2
version: 2.10.2(@testing-library/jest-dom@6.4.6(vitest@1.6.0(@types/node@20.14.3)(@vitest/ui@1.6.0(vitest@1.6.0))(jsdom@24.1.0)(sass@1.77.6)))(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)) version: 2.10.2(@testing-library/jest-dom@6.4.6(vitest@1.6.0(@types/node@20.14.3)(@vitest/ui@1.6.0(vitest@1.6.0))(jsdom@24.1.0)(sass@1.77.6)))(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6))
vite-plugin-solid-svg:
specifier: ^0.8.1
version: 0.8.1(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6))
vite-tsconfig-paths: vite-tsconfig-paths:
specifier: ^4.3.2 specifier: ^4.3.2
version: 4.3.2(typescript@5.4.5)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)) version: 4.3.2(typescript@5.4.5)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6))
@ -1116,6 +1119,10 @@ packages:
peerDependencies: peerDependencies:
solid-js: ^1.5 solid-js: ^1.5
'@trysound/sax@0.2.0':
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
engines: {node: '>=10.13.0'}
'@tsconfig/node10@1.0.11': '@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@ -1588,6 +1595,10 @@ packages:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'}
comment-parser@1.4.1: comment-parser@1.4.1:
resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@ -1615,10 +1626,21 @@ packages:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
css-select@5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
css-tree@2.2.1:
resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
css-tree@2.3.1: css-tree@2.3.1:
resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
css-what@6.1.0:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
css.escape@1.5.1: css.escape@1.5.1:
resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
@ -1627,6 +1649,10 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
hasBin: true hasBin: true
csso@5.0.5:
resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
cssstyle@4.0.1: cssstyle@4.0.1:
resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -1710,6 +1736,19 @@ packages:
dom-accessibility-api@0.6.3: dom-accessibility-api@0.6.3:
resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
domelementtype@2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
domutils@3.1.0:
resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
dotenv@16.4.5: dotenv@16.4.5:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -2409,6 +2448,9 @@ packages:
mdast-util-to-string@2.0.0: mdast-util-to-string@2.0.0:
resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==}
mdn-data@2.0.28:
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
mdn-data@2.0.30: mdn-data@2.0.30:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
@ -2962,6 +3004,11 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
svgo@3.3.2:
resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==}
engines: {node: '>=14.0.0'}
hasBin: true
symbol-tree@3.2.4: symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
@ -3159,6 +3206,12 @@ packages:
engines: {node: ^18.0.0 || >=20.0.0} engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true hasBin: true
vite-plugin-solid-svg@0.8.1:
resolution: {integrity: sha512-ROGC2ae1eYUCMd+zfJtsbUtuZwsb6DZS0+Sy5/ZXDokOunGi0Ez/cL7OPdsixN3I0/rNYd/3hilo3kpRMAS+IA==}
peerDependencies:
solid-js: ^1
vite: '>=4'
vite-plugin-solid@2.10.2: vite-plugin-solid@2.10.2:
resolution: {integrity: sha512-AOEtwMe2baBSXMXdo+BUwECC8IFHcKS6WQV/1NEd+Q7vHPap5fmIhLcAzr+DUJ04/KHx/1UBU0l1/GWP+rMAPQ==} resolution: {integrity: sha512-AOEtwMe2baBSXMXdo+BUwECC8IFHcKS6WQV/1NEd+Q7vHPap5fmIhLcAzr+DUJ04/KHx/1UBU0l1/GWP+rMAPQ==}
peerDependencies: peerDependencies:
@ -4276,6 +4329,8 @@ snapshots:
dependencies: dependencies:
solid-js: 1.8.17 solid-js: 1.8.17
'@trysound/sax@0.2.0': {}
'@tsconfig/node10@1.0.11': {} '@tsconfig/node10@1.0.11': {}
'@tsconfig/node12@1.0.11': {} '@tsconfig/node12@1.0.11': {}
@ -4752,8 +4807,7 @@ snapshots:
binary-extensions@2.3.0: {} binary-extensions@2.3.0: {}
boolbase@1.0.0: boolbase@1.0.0: {}
optional: true
brace-expansion@1.1.11: brace-expansion@1.1.11:
dependencies: dependencies:
@ -4902,6 +4956,8 @@ snapshots:
dependencies: dependencies:
delayed-stream: 1.0.0 delayed-stream: 1.0.0
commander@7.2.0: {}
comment-parser@1.4.1: {} comment-parser@1.4.1: {}
concat-map@0.0.1: {} concat-map@0.0.1: {}
@ -4924,16 +4980,35 @@ snapshots:
shebang-command: 2.0.0 shebang-command: 2.0.0
which: 2.0.2 which: 2.0.2
css-select@5.1.0:
dependencies:
boolbase: 1.0.0
css-what: 6.1.0
domhandler: 5.0.3
domutils: 3.1.0
nth-check: 2.1.1
css-tree@2.2.1:
dependencies:
mdn-data: 2.0.28
source-map-js: 1.2.0
css-tree@2.3.1: css-tree@2.3.1:
dependencies: dependencies:
mdn-data: 2.0.30 mdn-data: 2.0.30
source-map-js: 1.2.0 source-map-js: 1.2.0
css-what@6.1.0: {}
css.escape@1.5.1: {} css.escape@1.5.1: {}
cssesc@3.0.0: cssesc@3.0.0:
optional: true optional: true
csso@5.0.5:
dependencies:
css-tree: 2.2.1
cssstyle@4.0.1: cssstyle@4.0.1:
dependencies: dependencies:
rrweb-cssom: 0.6.0 rrweb-cssom: 0.6.0
@ -4989,6 +5064,24 @@ snapshots:
dom-accessibility-api@0.6.3: {} dom-accessibility-api@0.6.3: {}
dom-serializer@2.0.0:
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
entities: 4.5.0
domelementtype@2.3.0: {}
domhandler@5.0.3:
dependencies:
domelementtype: 2.3.0
domutils@3.1.0:
dependencies:
dom-serializer: 2.0.0
domelementtype: 2.3.0
domhandler: 5.0.3
dotenv@16.4.5: {} dotenv@16.4.5: {}
duplexer@0.1.2: {} duplexer@0.1.2: {}
@ -5803,6 +5896,8 @@ snapshots:
mdast-util-to-string@2.0.0: {} mdast-util-to-string@2.0.0: {}
mdn-data@2.0.28: {}
mdn-data@2.0.30: {} mdn-data@2.0.30: {}
merge-anything@5.1.7: merge-anything@5.1.7:
@ -5915,7 +6010,6 @@ snapshots:
nth-check@2.1.1: nth-check@2.1.1:
dependencies: dependencies:
boolbase: 1.0.0 boolbase: 1.0.0
optional: true
nwsapi@2.2.10: {} nwsapi@2.2.10: {}
@ -6340,6 +6434,16 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {} supports-preserve-symlinks-flag@1.0.0: {}
svgo@3.3.2:
dependencies:
'@trysound/sax': 0.2.0
commander: 7.2.0
css-select: 5.1.0
css-tree: 2.3.1
css-what: 6.1.0
csso: 5.0.5
picocolors: 1.0.1
symbol-tree@3.2.4: {} symbol-tree@3.2.4: {}
synckit@0.6.2: synckit@0.6.2:
@ -6548,6 +6652,12 @@ snapshots:
- supports-color - supports-color
- terser - terser
vite-plugin-solid-svg@0.8.1(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)):
dependencies:
solid-js: 1.8.17
svgo: 3.3.2
vite: 5.3.1(@types/node@20.14.3)(sass@1.77.6)
vite-plugin-solid@2.10.2(@testing-library/jest-dom@6.4.6(vitest@1.6.0(@types/node@20.14.3)(@vitest/ui@1.6.0(vitest@1.6.0))(jsdom@24.1.0)(sass@1.77.6)))(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)): vite-plugin-solid@2.10.2(@testing-library/jest-dom@6.4.6(vitest@1.6.0(@types/node@20.14.3)(@vitest/ui@1.6.0(vitest@1.6.0))(jsdom@24.1.0)(sass@1.77.6)))(solid-js@1.8.17)(vite@5.3.1(@types/node@20.14.3)(sass@1.77.6)):
dependencies: dependencies:
'@babel/core': 7.24.4 '@babel/core': 7.24.4