merge(notifications): Merged notification work

This commit is contained in:
LynithDev 2024-05-10 19:52:27 +02:00
parent 3c0c402410
commit 8e5d7e77fc
20 changed files with 533 additions and 46 deletions

View file

@ -20,6 +20,7 @@
"dependencies": {
"@fontsource/poppins": "^5.0.14",
"@iconify-json/logos": "^1.1.42",
"@solid-primitives/refs": "^1.0.8",
"@solidjs/router": "^0.13.3",
"@tauri-apps/api": "^2.0.0-beta",
"@tauri-apps/plugin-shell": "github:tauri-apps/tauri-plugin-shell#v2",

View file

@ -44,8 +44,8 @@
"hiddenTitle": true,
"width": 1280,
"height": 802,
"minWidth": 600,
"minHeight": 400,
"minWidth": 1020,
"minHeight": 600,
"resizable": true,
"center": true,
"decorations": false,

View file

@ -0,0 +1,46 @@
import { invoke } from '@tauri-apps/api/core';
import type { UnlistenFn } from '@tauri-apps/api/event';
import { listen } from '@tauri-apps/api/event';
export enum NotificationType {
Info = 'Info',
Alert = 'Alert',
Success = 'Success',
Download = 'Download',
DownloadSuccess = 'DownloadSuccess',
DownloadError = 'DownloadError',
Refresh = 'Refresh',
}
export async function addNotification(notification: MakeOptional<Core.Notification, 'created_at'>): Promise<void> {
await invoke('plugin:onelauncher|notifications_add', {
notification: {
created_at: Math.floor(Date.now() / 1000),
...notification,
},
});
}
export async function removeNotification(index: number): Promise<void> {
await invoke('plugin:onelauncher|notifications_remove_by_index', { index });
}
export async function clearNotifications(): Promise<void> {
await invoke('plugin:onelauncher|notifications_clear');
}
export async function getNotifications(): Promise<Core.Notification[]> {
return await invoke('plugin:onelauncher|notifications_get');
}
export async function on<
T extends Core.NotificationEvents,
>(
event: T,
callback: () => void,
): Promise<UnlistenFn> {
return await listen<string>(`notifications_event`, (e) => {
if (e.payload.toLowerCase() === event)
callback();
});
};

1
desktop/src/constants.ts Normal file
View file

@ -0,0 +1 @@
export const WEBSITE = 'https://polyfrost.org/';

11
desktop/src/types/core.d.ts vendored Normal file
View file

@ -0,0 +1,11 @@
declare namespace Core {
export interface Notification {
created_at: number;
title: string;
message: string;
notification_type: NotificationType;
progress?: number;
}
export type NotificationEvents = 'added' | 'removed' | 'cleared';
}

View file

@ -1,3 +1,4 @@
declare namespace globalThis {
export type WithIndex<T> = T & { index: number }
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
}

View file

@ -2,8 +2,9 @@ import type { ParentProps } from 'solid-js';
import { Transition } from 'solid-transition-group';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid';
import environment from '../utils/environment';
import WindowFrame from './components/native/WindowFrame';
import WindowFrame from './components/WindowFrame';
import Navbar from './components/Navbar';
import ErrorBoundary from './components/ErrorBoundary';
function App(props: ParentProps) {
if (!environment.isDev()) {
@ -14,21 +15,21 @@ function App(props: ParentProps) {
return (
<main class="flex flex-col bg-primary w-full min-h-screen overflow-hidden h-screen max-h-screen text-white">
<WindowFrame />
<div class="flex flex-col px-8">
<Navbar />
</div>
{/* This was a pain to do */}
<div class="relative h-full w-full overflow-hidden">
<div class="absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden">
<OverlayScrollbarsComponent class="absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden overflow-y-auto px-8">
<AnimatedRoutes>
{props.children}
</AnimatedRoutes>
<div class="pb-8" />
</OverlayScrollbarsComponent>
<ErrorBoundary>
<OverlayScrollbarsComponent class="absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden overflow-y-auto px-8">
<AnimatedRoutes>
{props.children}
</AnimatedRoutes>
<div class="pb-8" />
</OverlayScrollbarsComponent>
</ErrorBoundary>
</div>
</div>

View file

@ -0,0 +1,21 @@
import type { ParentProps } from 'solid-js';
import { ErrorBoundary as Capturer } from 'solid-js';
// TODO: Uncomment code
function ErrorBoundary(props: ParentProps) {
return (
<>{props.children}</>
// <Capturer
// fallback={err => (
// <div class="p-2 bg-danger/20 rounded-md border-danger border">
// <h2>An unexpected error has occurred</h2>
// <p>{err.toString()}</p>
// </div>
// )}
// >
// {props.children}
// </Capturer>
);
}
export default ErrorBoundary;

View file

@ -1,11 +1,15 @@
import { A } from '@solidjs/router';
import { createSignal } from 'solid-js';
import { Bell01Icon, Cloud01Icon, Settings01Icon, TerminalBrowserIcon } from '@untitled-theme/icons-solid';
import { open } from '@tauri-apps/plugin-shell';
import useAccount from '../hooks/useAccount';
import PolyfrostFull from './logos/PolyfrostFull';
import AccountPopup from './overlay/AccountPopup';
import PlayerHead from './game/PlayerHead';
import Button from './base/Button';
import NotificationPopup from './overlay/notifications/NotificationPopup';
import Popup from './overlay/Popup';
import { WEBSITE } from '~constants';
interface NavbarLinkProps {
path: string;
@ -28,38 +32,54 @@ function NavbarLink(props: NavbarLinkProps) {
function Navbar() {
const [profileMenuOpen, setProfileMenuOpen] = createSignal(false);
const [notificationMenuOpen, setNotificationMenuOpen] = createSignal(false);
const account = useAccount();
let profileButton!: HTMLButtonElement;
let notificationButton!: HTMLButtonElement;
return (
<div class="flex flex-row justify-center items-center min-h-[60px] h-15 mx-2">
<div class="flex flex-row *:flex-1 items-center min-h-[60px] h-15 mx-2">
<div>
<PolyfrostFull />
<div onClick={() => open(WEBSITE)} class="flex items-center justify-center active:scale-90 transition-transform w-min">
<PolyfrostFull />
</div>
</div>
<div class="flex flex-1 flex-row items-center justify-center gap-x-10 py-1">
<div class="flex flex-row items-center justify-center gap-x-10 py-1">
<NavbarLink path="/" label="Home" />
<NavbarLink path="/browser" label="Browser" />
<NavbarLink path="/updates" label="Updates" />
</div>
<div class="flex flex-row justify-end items-center gap-x-2 relative">
<Button styleType="icon" class="w-8 h-8">
<TerminalBrowserIcon class="w-7 h-7" />
<Button buttonStyle="icon">
<TerminalBrowserIcon />
</Button>
<Button styleType="icon" class="w-8 h-8">
<Cloud01Icon class="w-7 h-7" />
<Button buttonStyle="icon">
<Cloud01Icon />
</Button>
<Button styleType="icon" class="w-8 h-8">
<Bell01Icon class="w-7 h-7" />
{/* Notification Manager Button */}
<Button
buttonStyle="icon"
ref={notificationButton}
class="[&>div]:absolute relative"
onClick={() => setNotificationMenuOpen(!notificationMenuOpen())}
>
<Bell01Icon />
</Button>
<Button styleType="icon" class="w-8 h-8">
<Settings01Icon class="w-7 h-7" />
{/* Launcher Settings Button */}
<Button buttonStyle="icon">
<Settings01Icon />
</Button>
<button ref={profileButton} onClick={() => setProfileMenuOpen(!profileMenuOpen())}>
{/* Account Menu Button */}
<button
ref={profileButton}
onClick={() => setProfileMenuOpen(!profileMenuOpen())}
>
<PlayerHead class="w-[30px] h-[30px] rounded-md hover:opacity-70" uuid={account.uuid} />
</button>
</div>
@ -67,8 +87,18 @@ function Navbar() {
<AccountPopup
visible={profileMenuOpen}
setVisible={setProfileMenuOpen}
mount={profileButton}
class="right-0 top-full mt-2"
ref={(el) => {
el.style.left = `${profileButton.getBoundingClientRect().right - el.getBoundingClientRect().width}px`;
el.style.top = `${profileButton.getBoundingClientRect().bottom}px`;
}}
class="mt-2"
/>
<NotificationPopup
visible={notificationMenuOpen}
setVisible={setNotificationMenuOpen}
ref={el => Popup.setPos(notificationButton, el)}
class="mt-2"
/>
</div>
);

View file

@ -0,0 +1,48 @@
import { type ParentProps, createSignal, onCleanup, onMount } from 'solid-js';
type TimeAgoProps = ParentProps & {
timestamp: number;
};
function TimeAgo(props: TimeAgoProps) {
const [time, setTime] = createSignal('');
const [intervalId, setIntervalId] = createSignal<NodeJS.Timeout | undefined>(undefined);
onMount(() => {
const formatter = new Intl.RelativeTimeFormat(navigator.language, { numeric: 'auto' });
const units = {
year: 24 * 60 * 60 * 1000 * 365,
month: 24 * 60 * 60 * 1000 * 365 / 12,
day: 24 * 60 * 60 * 1000,
hour: 60 * 60 * 1000,
minute: 60 * 1000,
second: 1000,
};
const getRelativeTime = (timestamp: number): string => {
const elapsed = timestamp - Date.now();
for (const [unit, ms] of Object.entries(units)) {
if (Math.abs(elapsed) > ms || unit === 'second')
return formatter.format(Math.round(elapsed / ms), unit as Intl.RelativeTimeFormatUnit);
}
return 'now';
};
const loop = () => {
setTime(getRelativeTime(props.timestamp));
};
loop();
setIntervalId(setInterval(loop, 1000));
});
onCleanup(() => {
clearInterval(intervalId()!);
});
return <span>{time()}</span>;
}
export default TimeAgo;

View file

@ -2,8 +2,8 @@ import { Window } from '@tauri-apps/api/window';
import { MinusIcon, XCloseIcon } from '@untitled-theme/icons-solid';
import type { JSX } from 'solid-js';
import { createSignal, onMount } from 'solid-js';
import Modal from '../overlay/Modal';
import Button from '../base/Button';
import Modal from './overlay/Modal';
import Button from './base/Button';
interface TitlebarButtonProps {
icon: (any: any) => JSX.Element;
@ -50,8 +50,8 @@ function WindowFrame() {
visible={isModalVisible}
setVisible={setModalVisible}
buttons={[
<Button styleType="secondary" onClick={() => setModalVisible(false)}>No</Button>,
<Button styleType="danger" onClick={() => quit()}>Yes</Button>,
<Button buttonStyle="secondary" onClick={() => setModalVisible(false)}>No</Button>,
<Button buttonStyle="danger" onClick={() => quit()}>Yes</Button>,
]}
/>
</div>

View file

@ -21,7 +21,14 @@
}
&__icon {
@apply p-1.5 hover:bg-gray-0.05 active:bg-gray-0.10 aspect-square box-border;
@apply p-1.5 hover:bg-gray-0.05 active:bg-gray-0.10 aspect-square box-border w-8 h-8;
&.large {
@apply w-9 h-9;
& > * {
@apply p-0.5;
}
}
& > * {
@apply w-full h-full stroke-fg-primary;
@ -43,4 +50,4 @@
.icon > * {
@apply w-4;
}
}

View file

@ -5,15 +5,16 @@ import styles from './Button.module.scss';
type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & ParentProps & {
iconLeft?: JSX.Element;
iconRight?: JSX.Element;
styleType?: 'primary' | 'secondary' | 'danger' | 'icon' | 'ghost';
buttonStyle?: 'primary' | 'secondary' | 'danger' | 'icon' | 'ghost';
large?: boolean;
};
function Button(props: ButtonProps) {
const [buttonProps, rest] = splitProps(props, ['iconLeft', 'iconRight', 'styleType', 'class']);
const merged = mergeProps({ styleType: 'primary' }, buttonProps);
const [buttonProps, rest] = splitProps(props, ['iconLeft', 'iconRight', 'buttonStyle', 'class', 'large']);
const merged = mergeProps({ buttonStyle: 'primary', large: false }, buttonProps);
return (
<button class={`${styles.button} ${styles[`button__${merged.styleType}`]} ${buttonProps.class || ''}`} {...rest}>
<button class={`${styles.button} ${styles[`button__${merged.buttonStyle}`]} ${merged.large ? styles.large : ''} ${buttonProps.class || ''}`} {...rest}>
{merged.iconLeft && <span class={styles.icon}>{merged.iconLeft}</span>}
{props.children}
{merged.iconRight && <span class={styles.icon}>{merged.iconRight}</span>}

View file

@ -23,7 +23,7 @@ function AccountComponent(props: AccountComponentProps) {
</div>
</div>
{props.loggedIn && (
<Button styleType="icon" class="w-8 h-8">
<Button buttonStyle="icon">
<LogOut01Icon class=" stroke-danger" />
</Button>
)}
@ -50,11 +50,11 @@ function AccountPopup(props: Popup.PopupProps) {
<div class="flex flex-row justify-between">
<div>
<Button styleType="ghost" iconLeft={<PlusIcon />}>Add Account</Button>
<Button buttonStyle="ghost" iconLeft={<PlusIcon />}>Add Account</Button>
</div>
<div class="flex flex-row">
<Button styleType="icon" class="w-9 h-9">
<Settings01Icon class="p-0.5 stroke-fg-primary" />
<Button buttonStyle="icon" large>
<Settings01Icon class="stroke-fg-primary" />
</Button>
</div>
</div>

View file

@ -1,3 +1,4 @@
import { mergeRefs } from '@solid-primitives/refs';
import {
Show,
createEffect,
@ -11,13 +12,13 @@ import { Portal } from 'solid-js/web';
// eslint-disable-next-line ts/no-namespace
declare namespace Popup {
interface PopupProps extends ParentProps {
type PopupProps = Omit<Parameters<typeof Portal>[0], 'children'> & ParentProps & {
visible: Accessor<boolean>;
setVisible: Setter<boolean>;
class?: string;
style?: JSX.CSSProperties | string;
mount?: Node;
}
};
}
function Popup(props: Popup.PopupProps) {
@ -53,8 +54,14 @@ function Popup(props: Popup.PopupProps) {
return (
<Show when={localVisible()}>
<Portal mount={props.mount || document.body}>
<div ref={popupRef} style={props.style || ''} class={`absolute z-[1000] animate-duration-150 ${animate()} ${props.class || ''}`}>
<Portal
mount={props.mount || document.body}
ref={mergeRefs((el) => {
el.classList.add('absolute', 'z-[1000]');
return el;
}, props.ref)}
>
<div ref={popupRef} style={props.style || ''} class={`${animate()} ${props.class || ''}`}>
{props.children}
</div>
</Portal>
@ -62,4 +69,14 @@ function Popup(props: Popup.PopupProps) {
);
}
Popup.setPos = (parent: HTMLElement, ref: (SVGGElement | HTMLDivElement)) => {
const parentRect = parent.getBoundingClientRect();
const top = parentRect.bottom;
const right = document.body.clientWidth - parentRect.right;
ref.style.top = `${top}px`;
ref.style.right = `${right}px`;
};
export default Popup;

View file

@ -0,0 +1,102 @@
import { type JSX, Show } from 'solid-js';
import { AlertTriangleIcon, CheckCircleIcon, FolderCheckIcon, FolderDownloadIcon, FolderXIcon, InfoCircleIcon, RefreshCcw02Icon } from '@untitled-theme/icons-solid';
import TimeAgo from '../../TimeAgo';
import { NotificationType } from '../../../../bridge/notifications';
interface NotificationComponentProps {
overlay: boolean;
data: Core.Notification;
};
function IconFromNotificationType(type: NotificationType): (props: JSX.HTMLAttributes<HTMLDivElement>) => JSX.Element {
switch (type) {
case NotificationType.Alert:
return AlertTriangleIcon as any;
case NotificationType.Download:
return FolderDownloadIcon as any;
case NotificationType.DownloadSuccess:
return FolderCheckIcon as any;
case NotificationType.DownloadError:
return FolderXIcon as any;
case NotificationType.Refresh:
return RefreshCcw02Icon as any;
case NotificationType.Success:
return CheckCircleIcon as any;
case NotificationType.Info:
default:
return InfoCircleIcon as any;
}
}
function ColorFromNotificationType(type: NotificationType): string {
switch (type) {
case NotificationType.Info:
case NotificationType.Refresh:
return 'text-brand';
case NotificationType.Alert:
case NotificationType.DownloadError:
return 'text-danger';
case NotificationType.DownloadSuccess:
case NotificationType.Success:
return 'text-success';
case NotificationType.Download:
default:
return 'text-fg-primary';
}
}
function NotificationOverlayComponent(props: NotificationComponentProps) {
return (
<div>
<span>{props.data.title}</span>
</div>
);
}
function NotificationPopupComponent(props: NotificationComponentProps) {
return (
<div class="p-2 flex flex-col gap-y-1">
<div class="min-h-10 grid place-items-center grid-cols-[24px_1fr_auto] gap-3">
{IconFromNotificationType(props.data.notification_type)({
class: `w-6 h-6 ${ColorFromNotificationType(props.data.notification_type)}`,
})}
<div class="flex flex-col w-full">
<span class={`font-medium ${ColorFromNotificationType(props.data.notification_type)}`}>{props.data.title}</span>
<span class="text-sm text-white/60">{props.data.message}</span>
</div>
<div class="flex flex-row justify-end items-center gap-1">
<span class="text-sm text-white/40">
<TimeAgo timestamp={props.data.created_at * 1000} />
</span>
<span class="w-1.5 h-1.5 rounded-full bg-brand" />
</div>
</div>
<Show when={props.data.progress}>
<div class="rounded-full overflow-hidden h-1.5 bg-brand-disabled w-full">
<div
class="rounded-full h-full min-w-0 max-w-full bg-brand transition-[width]"
style={{
width: `${Math.floor(props.data.progress! * 100)}%`,
}}
/>
</div>
</Show>
</div>
);
}
function NotificationComponent(props: NotificationComponentProps) {
return (
<>
{(props.overlay ? NotificationOverlayComponent : NotificationPopupComponent)(props)}
</>
);
}
export default NotificationComponent;

View file

@ -0,0 +1,91 @@
import { For, Match, Switch, createSignal, onMount } from 'solid-js';
import { Settings01Icon, Trash01Icon } from '@untitled-theme/icons-solid';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid';
import Popup from '../Popup';
import Button from '../../base/Button';
import * as manager from '../../../../bridge/notifications';
import NotificationComponent from './NotificationComponent';
function NotificationPopup(props: Popup.PopupProps) {
const [notifications, setNotifications] = createSignal<Core.Notification[]>([]);
let inner!: HTMLDivElement;
let parent!: HTMLDivElement;
function updateSize() {
if (inner && parent) {
const rect = inner.getBoundingClientRect();
parent.style.height = `${rect.height}px`;
}
}
async function refetchNotifications() {
const notis = await manager.getNotifications();
setNotifications(notis);
updateSize();
console.log(notifications());
}
onMount(() => {
document.addEventListener('keypress', (e) => {
if (e.key === 'n') {
manager.addNotification({
title: 'Test Notification',
message: 'This is a test notification',
notification_type: manager.NotificationType.Download,
progress: 0.39,
});
}
});
refetchNotifications();
manager.on('added', refetchNotifications);
manager.on('removed', refetchNotifications);
manager.on('cleared', refetchNotifications);
});
return (
<Popup {...props}>
<div class="bg-secondary rounded-xl border border-gray-0.10 w-96 p-2 shadow-lg shadow-black/50">
<div class="overflow-hidden transition-[height]" ref={parent}>
<div class="flex flex-col justify-start items-stretch text-start gap-2" ref={inner}>
<p class="text-2lg px-2 pt-1">Notifications</p>
<Switch>
<Match when={notifications().length !== 0}>
<OverlayScrollbarsComponent class="max-h-[min(500px,60vh)] overflow-auto">
<div class="flex flex-col-reverse justify-center items-stretch ">
<For each={notifications()}>
{noti => (
<div class="flex flex-col w-full">
<NotificationComponent data={noti} overlay={false} />
<span class="bg-gray-0.05 h-px w-full" />
</div>
)}
</For>
</div>
</OverlayScrollbarsComponent>
</Match>
<Match when={notifications().length === 0}>
<span class="px-2">You have no notifications</span>
<span class="bg-gray-0.05 h-px" />
</Match>
</Switch>
<div class="flex flex-row justify-between items-end">
<Button onClick={() => manager.clearNotifications()} buttonStyle="ghost" iconLeft={<Trash01Icon />}>
Clear Notifications
</Button>
<Button buttonStyle="icon" large>
<Settings01Icon />
</Button>
</div>
</div>
</div>
</div>
</Popup>
);
}
export default NotificationPopup;

View file

@ -47,7 +47,7 @@ function Banner() {
<div class="flex w-full flex-row justify-between items-end">
<div class="flex flex-row items-center gap-x-4">
<Button iconLeft={<PlayIcon />}>Launch 1.19.2</Button>
<Button styleType="icon" class="w-8 h-8">
<Button buttonStyle="icon" class="w-8 h-8">
<DotsVerticalIcon />
</Button>
</div>
@ -159,7 +159,7 @@ function ClusterCard(props: Core.ClusterWithManifest) {
{props.instance.mods && `• ${props.mods} mods`} */}
</p>
</div>
<Button onClick={e => openContextMenu(e)} styleType="icon" class="w-8 h-8">
<Button onClick={e => openContextMenu(e)} buttonStyle="icon" class="w-8 h-8">
<DotsVerticalIcon />
</Button>
</div>
@ -218,7 +218,7 @@ function HomePage() {
</div>
<div class="flex flex-row gap-x-4">
<Button
styleType="primary"
buttonStyle="primary"
iconLeft={<PlusIcon class="!w-5 stroke-[2.2]" />}
children="New Cluster"
onClick={() => newInstance()}

View file

@ -30,9 +30,12 @@ export default defineConfig({
'sm': '0.75rem',
'md': '0.875rem',
'lg': '1rem',
'2lg': '1.125rem',
'xl': '1.25rem',
'xxl': '1.5rem',
'2xl': '2rem',
'3xl': '2.5rem',
'4xl': '3rem',
},
colors: {

106
pnpm-lock.yaml generated
View file

@ -35,6 +35,9 @@ importers:
'@iconify-json/logos':
specifier: ^1.1.42
version: 1.1.42
'@solid-primitives/refs':
specifier: ^1.0.8
version: 1.0.8(solid-js@1.8.17)
'@solidjs/router':
specifier: ^0.13.3
version: 0.13.3(solid-js@1.8.17)
@ -90,6 +93,9 @@ importers:
sass:
specifier: ^1.77.0
version: 1.77.0
ts-node:
specifier: ^10.9.2
version: 10.9.2(@types/node@20.12.11)(typescript@5.4.5)
typescript:
specifier: ^5.4.5
version: 5.4.5
@ -330,6 +336,10 @@ packages:
resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==}
engines: {node: '>=6.9.0'}
'@cspotcode/source-map-support@0.8.1':
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
'@es-joy/jsdoccomment@0.42.0':
resolution: {integrity: sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==}
engines: {node: '>=16'}
@ -578,6 +588,9 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
'@jsdevtools/ez-spawn@3.0.4':
resolution: {integrity: sha512-f5DRIOZf7wxogefH03RjMPMdBF7ADTWUMoOs9kaJo06EfwF+aFhMZMDZxHg/Xe12hptN9xoZjGso2fdjapBRIA==}
engines: {node: '>=10'}
@ -851,6 +864,18 @@ packages:
peerDependencies:
solid-js: ^1.5
'@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
'@tsconfig/node12@1.0.11':
resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
'@tsconfig/node14@1.0.3':
resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
'@tsconfig/node16@1.0.4':
resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
'@types/aria-query@5.0.4':
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
@ -1178,6 +1203,9 @@ packages:
resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==}
engines: {node: '>=14'}
arg@4.1.3:
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
@ -1356,6 +1384,9 @@ packages:
core-js-compat@3.37.0:
resolution: {integrity: sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==}
create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@ -1440,6 +1471,10 @@ packages:
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
diff@4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
@ -2190,6 +2225,9 @@ packages:
magic-string@0.30.10:
resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
mdast-util-from-markdown@0.8.5:
resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==}
@ -2805,6 +2843,20 @@ packages:
peerDependencies:
typescript: '>=4.2.0'
ts-node@10.9.2:
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
hasBin: true
peerDependencies:
'@swc/core': '>=1.2.50'
'@swc/wasm': '>=1.2.50'
'@types/node': '*'
typescript: '>=2.7'
peerDependenciesMeta:
'@swc/core':
optional: true
'@swc/wasm':
optional: true
tsconfck@3.0.3:
resolution: {integrity: sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==}
engines: {node: ^18 || >=20}
@ -2892,6 +2944,9 @@ packages:
uuid@9.0.1:
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
v8-compile-cache-lib@3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
validate-html-nesting@1.2.2:
resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==}
@ -3073,6 +3128,10 @@ packages:
engines: {node: '>= 14'}
hasBin: true
yn@3.1.1:
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
engines: {node: '>=6'}
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
@ -3393,6 +3452,10 @@ snapshots:
'@babel/helper-validator-identifier': 7.24.5
to-fast-properties: 2.0.0
'@cspotcode/source-map-support@0.8.1':
dependencies:
'@jridgewell/trace-mapping': 0.3.9
'@es-joy/jsdoccomment@0.42.0':
dependencies:
comment-parser: 1.4.1
@ -3587,6 +3650,11 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
'@jridgewell/trace-mapping@0.3.9':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
'@jsdevtools/ez-spawn@3.0.4':
dependencies:
call-me-maybe: 1.0.2
@ -3815,6 +3883,14 @@ snapshots:
dependencies:
solid-js: 1.8.17
'@tsconfig/node10@1.0.11': {}
'@tsconfig/node12@1.0.11': {}
'@tsconfig/node14@1.0.3': {}
'@tsconfig/node16@1.0.4': {}
'@types/aria-query@5.0.4': {}
'@types/babel__core@7.20.5':
@ -4302,6 +4378,8 @@ snapshots:
are-docs-informative@0.0.2: {}
arg@4.1.3: {}
argparse@2.0.1: {}
aria-query@5.1.3:
@ -4503,6 +4581,8 @@ snapshots:
dependencies:
browserslist: 4.23.0
create-require@1.1.1: {}
cross-spawn@7.0.3:
dependencies:
path-key: 3.1.1
@ -4588,6 +4668,8 @@ snapshots:
diff-sequences@29.6.3: {}
diff@4.0.2: {}
dir-glob@3.0.1:
dependencies:
path-type: 4.0.0
@ -5447,6 +5529,8 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
make-error@1.3.6: {}
mdast-util-from-markdown@0.8.5:
dependencies:
'@types/mdast': 3.0.15
@ -6058,6 +6142,24 @@ snapshots:
dependencies:
typescript: 5.4.5
ts-node@10.9.2(@types/node@20.12.11)(typescript@5.4.5):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
'@types/node': 20.12.11
acorn: 8.11.3
acorn-walk: 8.3.2
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
typescript: 5.4.5
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
tsconfck@3.0.3(typescript@5.4.5):
optionalDependencies:
typescript: 5.4.5
@ -6144,6 +6246,8 @@ snapshots:
uuid@9.0.1: {}
v8-compile-cache-lib@3.0.1: {}
validate-html-nesting@1.2.2: {}
validate-npm-package-license@3.0.4:
@ -6329,6 +6433,8 @@ snapshots:
yaml@2.4.2: {}
yn@3.1.1: {}
yocto-queue@0.1.0: {}
yocto-queue@1.0.0: {}