feat: add Radix UI components and implement sidebar functionality

- Added new Radix UI components: Dialog, Tooltip, Separator, and updated existing components.
- Introduced a Sidebar component with collapsible functionality and mobile responsiveness.
- Implemented a custom hook `useIsMobile` to manage mobile state.
- Updated package dependencies in package.json and yarn.lock for new components.
- Created utility components such as Button, Skeleton, and Input for consistent styling.

feat: add AppSidebar component with collapsible functionality and sidebar menu

- Introduced AppSidebar component for a customizable sidebar layout.
- Implemented collapsible sections using Radix UI's Collapsible component.
- Added sidebar menu items with icons and links for navigation.
- Created Sidebar UI components including SidebarHeader, SidebarFooter, and SidebarMenu.
- Integrated ThemePicker for theme selection within the sidebar.
- Updated sidebar styles and layout for better responsiveness.

chore: add @radix-ui/react-collapsible dependency

- Added @radix-ui/react-collapsible package to manage collapsible UI elements.
This commit is contained in:
Maximilian Liebmann 2025-06-16 13:01:20 +02:00
parent 21eff651e8
commit a6f74e0c22
16 changed files with 1520 additions and 207 deletions

View file

@ -27,20 +27,23 @@
"@fortawesome/react-fontawesome": "^0.2.2",
"@hookform/resolvers": "^5.0.1",
"@prisma/client": "^6.9.0",
"@radix-ui/react-collapsible": "^1.1.11",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-dropdown-menu": "^2.1.14",
"@radix-ui/react-hover-card": "^1.1.13",
"@radix-ui/react-label": "^2.1.6",
"@radix-ui/react-scroll-area": "^1.2.8",
"@radix-ui/react-select": "^2.2.4",
"@radix-ui/react-separator": "^1.1.6",
"@radix-ui/react-slot": "^1.2.2",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.7",
"@tanstack/react-query": "^5.80.7",
"bcryptjs": "^3.0.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.511.0",
"lucide-react": "^0.515.0",
"next": "15.4.0-canary.95",
"next-auth": "^5.0.0-beta.25",
"next-themes": "^0.4.6",

View file

@ -1,15 +1,13 @@
'use client';
import { RedirectButton } from '@/components/buttons/redirect-button';
import { ThemePicker } from '@/components/misc/theme-picker';
import { useGetApiUserMe } from '@/generated/api/user/user';
export default function Home() {
const { data, isLoading } = useGetApiUserMe();
return (
<div className='flex flex-col items-center justify-center h-screen'>
<div className='absolute top-4 right-4'>{<ThemePicker />}</div>
<div className='flex flex-col items-center justify-center h-full'>
<div>
<h1>
Hello{' '}

23
src/app/(main)/layout.tsx Normal file
View file

@ -0,0 +1,23 @@
import React from 'react';
import { cookies } from 'next/headers';
import { AppSidebar } from '@/components/custom-ui/app-sidebar';
import SidebarProviderWrapper from '@/components/wrappers/sidebar-provider';
import Header from '@/components/misc/header';
export default async function Layout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = await cookies();
const defaultOpen = cookieStore.get('sidebar_state')?.value === 'true';
return (
<>
<SidebarProviderWrapper defaultOpen={defaultOpen}>
<AppSidebar></AppSidebar>
<Header>{children}</Header>
</SidebarProviderWrapper>
</>
);
}

View file

@ -55,6 +55,8 @@
--card: var(--neutral-800);
--sidebar-width-icon: 32px;
/* ------------------- */
--foreground: oklch(0.13 0.028 261.692);
@ -95,17 +97,17 @@
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0.002 247.839);
--sidebar: var(--background);
--sidebar-foreground: oklch(0.13 0.028 261.692);
--sidebar-foreground: var(--text);
--sidebar-primary: oklch(0.21 0.034 264.665);
--sidebar-primary-foreground: oklch(0.985 0.002 247.839);
--sidebar-primary-foreground: var(--text);
--sidebar-accent: oklch(0.967 0.003 264.542);
--sidebar-accent-foreground: oklch(0.21 0.034 264.665);
--sidebar-accent-foreground: var(--text);
--sidebar-border: oklch(0.928 0.006 264.531);
@ -339,17 +341,17 @@
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.21 0.034 264.665);
--sidebar: var(--background);
--sidebar-foreground: oklch(0.985 0.002 247.839);
--sidebar-foreground: var(--text);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0.002 247.839);
--sidebar-primary-foreground: var(--text);
--sidebar-accent: oklch(0.278 0.033 256.848);
--sidebar-accent-foreground: oklch(0.985 0.002 247.839);
--sidebar-accent-foreground: var(--text);
--sidebar-border: oklch(1 0 0 / 10%);

View file

@ -0,0 +1,147 @@
'use client';
import React from 'react';
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupAction,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInput,
SidebarInset,
SidebarMenu,
SidebarMenuAction,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarRail,
SidebarSeparator,
SidebarTrigger,
useSidebar,
} from '@/components/ui/sidebar';
import { ChevronDown } from 'lucide-react';
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from '@/components/ui/collapsible';
import Logo from '@/components/misc/logo';
import Link from 'next/link';
import { ThemePicker } from '@/components/misc/theme-picker';
import {
Star,
CalendarDays,
User,
Users,
CalendarClock,
CalendarPlus,
} from 'lucide-react';
const items = [
{
title: 'Calendar',
url: '#',
icon: CalendarDays,
},
{
title: 'Friends',
url: '#',
icon: User,
},
{
title: 'Groups',
url: '#',
icon: Users,
},
{
title: 'Events',
url: '#',
icon: CalendarClock,
},
];
export function AppSidebar() {
return (
<>
<Sidebar collapsible='icon' variant='sidebar'>
<SidebarHeader className='overflow-hidden'>
<Logo
colorType='colored'
logoType='combo'
height={49.5}
className='group-data-[collapsible=icon]:hidden min-w-[203px]'
></Logo>
<Logo
colorType='colored'
logoType='submark'
height={49.5}
className='group-data-[collapsible=]:hidden group-data-[mobile=true]/mobile:hidden mb-[2.45]'
></Logo>
</SidebarHeader>
<SidebarContent className='grid grid-rows-[auto_1fr_auto]'>
<Collapsible defaultOpen className='group/collapsible'>
<SidebarGroup>
<SidebarGroupLabel asChild>
<CollapsibleTrigger>
<span className='flex items-center gap-2 text-xl font-label text-neutral-100'>
<Star className='size-8' />{' '}
<span className='group-data-[collapsible=icon]:hidden'>
Favorites
</span>
</span>
<ChevronDown className='ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180 group-data-[collapsible=icon]:hidden text-nowrap whitespace-nowrap' />
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarGroupContent />
</CollapsibleContent>
</SidebarGroup>
</Collapsible>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title} className='pt-2'>
<SidebarMenuButton asChild>
<Link href={item.url}>
<item.icon className='size-8' />
<span className='text-xl font-label group-data-[collapsible=icon]:hidden text-nowrap whitespace-nowrap'>
{item.title}
</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
<SidebarFooter>
<SidebarMenuItem className='pl-[8px]'>
<Link
href='/event/new'
className='flex items-center gap-2 text-xl font-label'
>
<CalendarPlus className='size-8' />
<span className='group-data-[collapsible=icon]:hidden text-nowrap whitespace-nowrap'>
New Event
</span>
</Link>
</SidebarMenuItem>
</SidebarFooter>
</SidebarContent>
</Sidebar>
</>
);
}

View file

@ -0,0 +1,23 @@
import { SidebarTrigger } from '@/components/ui/sidebar';
import { ThemePicker } from '@/components/misc/theme-picker';
export default function Header({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<div className='w-full grid grid-rows-[50px_1fr] h-screen'>
<header className='border-b-1 grid-cols-[1fr_3fr_1fr] grid items-center px-2'>
<span className='flex justify-start'>
<SidebarTrigger variant='outline_primary' size='icon' />
</span>
<span className='flex justify-center'>Search</span>
<span className='flex justify-end'>
<ThemePicker />
</span>
</header>
<main>{children}</main>
</div>
);
}

View file

@ -90,6 +90,7 @@ export default function Logo({
return (
<Image
unoptimized
src={logoVar}
alt={alt || defaultAltText}
className={className}

View file

@ -0,0 +1,33 @@
'use client';
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
function Collapsible({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
return <CollapsiblePrimitive.Root data-slot='collapsible' {...props} />;
}
function CollapsibleTrigger({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
return (
<CollapsiblePrimitive.CollapsibleTrigger
data-slot='collapsible-trigger'
{...props}
/>
);
}
function CollapsibleContent({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
return (
<CollapsiblePrimitive.CollapsibleContent
data-slot='collapsible-content'
{...props}
/>
);
}
export { Collapsible, CollapsibleTrigger, CollapsibleContent };

View file

@ -13,10 +13,13 @@ function Separator({
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
return (
<SeparatorPrimitive.Root
data-slot='separator-root'
data-slot='separator'
decorative={decorative}
orientation={orientation}
className={cn('shrink-0', className)}
className={cn(
'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
className,
)}
{...props}
/>
);

139
src/components/ui/sheet.tsx Normal file
View file

@ -0,0 +1,139 @@
"use client"
import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { XIcon } from "lucide-react"
import { cn } from "@/lib/utils"
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
return <SheetPrimitive.Root data-slot="sheet" {...props} />
}
function SheetTrigger({
...props
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
}
function SheetClose({
...props
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
}
function SheetPortal({
...props
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
}
function SheetOverlay({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
return (
<SheetPrimitive.Overlay
data-slot="sheet-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
className
)}
{...props}
/>
)
}
function SheetContent({
className,
children,
side = "right",
...props
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
side?: "top" | "right" | "bottom" | "left"
}) {
return (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
data-slot="sheet-content"
className={cn(
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
side === "right" &&
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
side === "left" &&
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
side === "top" &&
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
side === "bottom" &&
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
className
)}
{...props}
>
{children}
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<XIcon className="size-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
)
}
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="sheet-header"
className={cn("flex flex-col gap-1.5 p-4", className)}
{...props}
/>
)
}
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="sheet-footer"
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
{...props}
/>
)
}
function SheetTitle({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
return (
<SheetPrimitive.Title
data-slot="sheet-title"
className={cn("text-foreground font-semibold", className)}
{...props}
/>
)
}
function SheetDescription({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
return (
<SheetPrimitive.Description
data-slot="sheet-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
}
export {
Sheet,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
}

View file

@ -0,0 +1,725 @@
'use client';
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, VariantProps } from 'class-variance-authority';
import { PanelLeftIcon } from 'lucide-react';
import { useIsMobile } from '@/hooks/use-mobile';
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Separator } from '@/components/ui/separator';
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from '@/components/ui/sheet';
import { Skeleton } from '@/components/ui/skeleton';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@/components/ui/tooltip';
const SIDEBAR_COOKIE_NAME = 'sidebar_state';
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
const SIDEBAR_WIDTH = '16rem';
const SIDEBAR_WIDTH_MOBILE = '18rem';
const SIDEBAR_WIDTH_ICON = '4rem';
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
type SidebarContextProps = {
state: 'expanded' | 'collapsed';
open: boolean;
setOpen: (open: boolean) => void;
openMobile: boolean;
setOpenMobile: (open: boolean) => void;
isMobile: boolean;
toggleSidebar: () => void;
};
const SidebarContext = React.createContext<SidebarContextProps | null>(null);
function useSidebar() {
const context = React.useContext(SidebarContext);
if (!context) {
throw new Error('useSidebar must be used within a SidebarProvider.');
}
return context;
}
function SidebarProvider({
defaultOpen = true,
open: openProp,
onOpenChange: setOpenProp,
className,
style,
children,
...props
}: React.ComponentProps<'div'> & {
defaultOpen?: boolean;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}) {
const isMobile = useIsMobile();
const [openMobile, setOpenMobile] = React.useState(false);
// This is the internal state of the sidebar.
// We use openProp and setOpenProp for control from outside the component.
const [_open, _setOpen] = React.useState(defaultOpen);
const open = openProp ?? _open;
const setOpen = React.useCallback(
(value: boolean | ((value: boolean) => boolean)) => {
const openState = typeof value === 'function' ? value(open) : value;
if (setOpenProp) {
setOpenProp(openState);
} else {
_setOpen(openState);
}
// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
},
[setOpenProp, open],
);
// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
}, [isMobile, setOpen, setOpenMobile]);
// Adds a keyboard shortcut to toggle the sidebar.
React.useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (
event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
(event.metaKey || event.ctrlKey)
) {
event.preventDefault();
toggleSidebar();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [toggleSidebar]);
// We add a state so that we can do data-state="expanded" or "collapsed".
// This makes it easier to style the sidebar with Tailwind classes.
const state = open ? 'expanded' : 'collapsed';
const contextValue = React.useMemo<SidebarContextProps>(
() => ({
state,
open,
setOpen,
isMobile,
openMobile,
setOpenMobile,
toggleSidebar,
}),
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],
);
return (
<SidebarContext.Provider value={contextValue}>
<TooltipProvider delayDuration={0}>
<div
data-slot='sidebar-wrapper'
style={
{
'--sidebar-width': SIDEBAR_WIDTH,
'--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
...style,
} as React.CSSProperties
}
className={cn(
'group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full',
className,
)}
{...props}
>
{children}
</div>
</TooltipProvider>
</SidebarContext.Provider>
);
}
function Sidebar({
side = 'left',
variant = 'sidebar',
collapsible = 'offcanvas',
className,
children,
...props
}: React.ComponentProps<'div'> & {
side?: 'left' | 'right';
variant?: 'sidebar' | 'floating' | 'inset';
collapsible?: 'offcanvas' | 'icon' | 'none';
}) {
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
if (collapsible === 'none') {
return (
<div
data-slot='sidebar'
className={cn(
'bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col',
className,
)}
{...props}
>
{children}
</div>
);
}
if (isMobile) {
return (
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
<SheetContent
data-sidebar='sidebar'
data-slot='sidebar'
data-mobile='true'
className='bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden group/mobile'
style={
{
'--sidebar-width': SIDEBAR_WIDTH_MOBILE,
} as React.CSSProperties
}
side={side}
>
<SheetHeader className='sr-only'>
<SheetTitle>Sidebar</SheetTitle>
<SheetDescription>Displays the mobile sidebar.</SheetDescription>
</SheetHeader>
<div className='flex h-full w-full flex-col'>{children}</div>
</SheetContent>
</Sheet>
);
}
return (
<div
className='group peer text-sidebar-foreground hidden md:block'
data-state={state}
data-collapsible={state === 'collapsed' ? collapsible : ''}
data-variant={variant}
data-side={side}
data-slot='sidebar'
>
{/* This is what handles the sidebar gap on desktop */}
<div
data-slot='sidebar-gap'
className={cn(
'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',
'group-data-[collapsible=offcanvas]:w-0',
'group-data-[side=right]:rotate-180',
variant === 'floating' || variant === 'inset'
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'
: 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',
)}
/>
<div
data-slot='sidebar-container'
className={cn(
'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',
side === 'left'
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
// Adjust the padding for floating and inset variants.
variant === 'floating' || variant === 'inset'
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
: 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',
className,
)}
{...props}
>
<div
data-sidebar='sidebar'
data-slot='sidebar-inner'
className='bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm'
>
{children}
</div>
</div>
</div>
);
}
function SidebarTrigger({
className,
onClick,
...props
}: React.ComponentProps<typeof Button>) {
const { toggleSidebar } = useSidebar();
return (
<Button
data-sidebar='trigger'
data-slot='sidebar-trigger'
variant='muted'
size='icon'
className={cn('', className)}
onClick={(event) => {
onClick?.(event);
toggleSidebar();
}}
{...props}
>
<PanelLeftIcon />
<span className='sr-only'>Toggle Sidebar</span>
</Button>
);
}
function SidebarRail({ className, ...props }: React.ComponentProps<'button'>) {
const { toggleSidebar } = useSidebar();
return (
<button
data-sidebar='rail'
data-slot='sidebar-rail'
aria-label='Toggle Sidebar'
tabIndex={-1}
onClick={toggleSidebar}
title='Toggle Sidebar'
className={cn(
'hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex',
'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',
'[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
'hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full',
'[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
'[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
className,
)}
{...props}
/>
);
}
function SidebarInset({ className, ...props }: React.ComponentProps<'main'>) {
return (
<main
data-slot='sidebar-inset'
className={cn(
'bg-background relative flex w-full flex-1 flex-col',
'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',
className,
)}
{...props}
/>
);
}
function SidebarInput({
className,
...props
}: React.ComponentProps<typeof Input>) {
return (
<Input
data-slot='sidebar-input'
data-sidebar='input'
className={cn('bg-background h-8 w-full shadow-none', className)}
{...props}
/>
);
}
function SidebarHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot='sidebar-header'
data-sidebar='header'
className={cn('flex flex-col gap-2 p-2', className)}
{...props}
/>
);
}
function SidebarFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot='sidebar-footer'
data-sidebar='footer'
className={cn('flex flex-col gap-2 p-2', className)}
{...props}
/>
);
}
function SidebarSeparator({
className,
...props
}: React.ComponentProps<typeof Separator>) {
return (
<Separator
data-slot='sidebar-separator'
data-sidebar='separator'
className={cn('bg-sidebar-border mx-2 w-auto', className)}
{...props}
/>
);
}
function SidebarContent({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot='sidebar-content'
data-sidebar='content'
className={cn(
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
className,
)}
{...props}
/>
);
}
function SidebarGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot='sidebar-group'
data-sidebar='group'
className={cn('relative flex w-full min-w-0 flex-col p-2', className)}
{...props}
/>
);
}
function SidebarGroupLabel({
className,
asChild = false,
...props
}: React.ComponentProps<'div'> & { asChild?: boolean }) {
const Comp = asChild ? Slot : 'div';
return (
<Comp
data-slot='sidebar-group-label'
data-sidebar='group-label'
className={cn(
'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0 ml-[7.5px]',
className,
)}
{...props}
/>
);
}
function SidebarGroupAction({
className,
asChild = false,
...props
}: React.ComponentProps<'button'> & { asChild?: boolean }) {
const Comp = asChild ? Slot : 'button';
return (
<Comp
data-slot='sidebar-group-action'
data-sidebar='group-action'
className={cn(
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
// Increases the hit area of the button on mobile.
'after:absolute after:-inset-2 md:after:hidden',
'group-data-[collapsible=icon]:hidden',
className,
)}
{...props}
/>
);
}
function SidebarGroupContent({
className,
...props
}: React.ComponentProps<'div'>) {
return (
<div
data-slot='sidebar-group-content'
data-sidebar='group-content'
className={cn('w-full text-sm', className)}
{...props}
/>
);
}
function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {
return (
<ul
data-slot='sidebar-menu'
data-sidebar='menu'
className={cn('flex w-full min-w-0 flex-col gap-1', className)}
{...props}
/>
);
}
function SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {
return (
<li
data-slot='sidebar-menu-item'
data-sidebar='menu-item'
className={cn('group/menu-item relative', className)}
{...props}
/>
);
}
const sidebarMenuButtonVariants = cva(
'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! p-0 ml-[15.5px] [&>span:last-child]:truncate [&>svg]:shrink-0',
{
variants: {
variant: {
default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
outline:
'bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]',
},
size: {
default: 'h-8 text-sm',
sm: 'h-7 text-xs',
lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
);
function SidebarMenuButton({
asChild = false,
isActive = false,
variant = 'default',
size = 'default',
tooltip,
className,
...props
}: React.ComponentProps<'button'> & {
asChild?: boolean;
isActive?: boolean;
tooltip?: string | React.ComponentProps<typeof TooltipContent>;
} & VariantProps<typeof sidebarMenuButtonVariants>) {
const Comp = asChild ? Slot : 'button';
const { isMobile, state } = useSidebar();
const button = (
<Comp
data-slot='sidebar-menu-button'
data-sidebar='menu-button'
data-size={size}
data-active={isActive}
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
{...props}
/>
);
if (!tooltip) {
return button;
}
if (typeof tooltip === 'string') {
tooltip = {
children: tooltip,
};
}
return (
<Tooltip>
<TooltipTrigger asChild>{button}</TooltipTrigger>
<TooltipContent
side='right'
align='center'
hidden={state !== 'collapsed' || isMobile}
{...tooltip}
/>
</Tooltip>
);
}
function SidebarMenuAction({
className,
asChild = false,
showOnHover = false,
...props
}: React.ComponentProps<'button'> & {
asChild?: boolean;
showOnHover?: boolean;
}) {
const Comp = asChild ? Slot : 'button';
return (
<Comp
data-slot='sidebar-menu-action'
data-sidebar='menu-action'
className={cn(
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
// Increases the hit area of the button on mobile.
'after:absolute after:-inset-2 md:after:hidden',
'peer-data-[size=sm]/menu-button:top-1',
'peer-data-[size=default]/menu-button:top-1.5',
'peer-data-[size=lg]/menu-button:top-2.5',
'group-data-[collapsible=icon]:hidden',
showOnHover &&
'peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0',
className,
)}
{...props}
/>
);
}
function SidebarMenuBadge({
className,
...props
}: React.ComponentProps<'div'>) {
return (
<div
data-slot='sidebar-menu-badge'
data-sidebar='menu-badge'
className={cn(
'text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none',
'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
'peer-data-[size=sm]/menu-button:top-1',
'peer-data-[size=default]/menu-button:top-1.5',
'peer-data-[size=lg]/menu-button:top-2.5',
'group-data-[collapsible=icon]:hidden',
className,
)}
{...props}
/>
);
}
function SidebarMenuSkeleton({
className,
showIcon = false,
...props
}: React.ComponentProps<'div'> & {
showIcon?: boolean;
}) {
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%`;
}, []);
return (
<div
data-slot='sidebar-menu-skeleton'
data-sidebar='menu-skeleton'
className={cn('flex h-8 items-center gap-2 rounded-md px-2', className)}
{...props}
>
{showIcon && (
<Skeleton
className='size-4 rounded-md'
data-sidebar='menu-skeleton-icon'
/>
)}
<Skeleton
className='h-4 max-w-(--skeleton-width) flex-1'
data-sidebar='menu-skeleton-text'
style={
{
'--skeleton-width': width,
} as React.CSSProperties
}
/>
</div>
);
}
function SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {
return (
<ul
data-slot='sidebar-menu-sub'
data-sidebar='menu-sub'
className={cn(
'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5',
'group-data-[collapsible=icon]:hidden',
className,
)}
{...props}
/>
);
}
function SidebarMenuSubItem({
className,
...props
}: React.ComponentProps<'li'>) {
return (
<li
data-slot='sidebar-menu-sub-item'
data-sidebar='menu-sub-item'
className={cn('group/menu-sub-item relative', className)}
{...props}
/>
);
}
function SidebarMenuSubButton({
asChild = false,
size = 'md',
isActive = false,
className,
...props
}: React.ComponentProps<'a'> & {
asChild?: boolean;
size?: 'sm' | 'md';
isActive?: boolean;
}) {
const Comp = asChild ? Slot : 'a';
return (
<Comp
data-slot='sidebar-menu-sub-button'
data-sidebar='menu-sub-button'
data-size={size}
data-active={isActive}
className={cn(
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
size === 'sm' && 'text-xs',
size === 'md' && 'text-sm',
'group-data-[collapsible=icon]:hidden',
className,
)}
{...props}
/>
);
}
export {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupAction,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInput,
SidebarInset,
SidebarMenu,
SidebarMenuAction,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarRail,
SidebarSeparator,
SidebarTrigger,
useSidebar,
};

View file

@ -0,0 +1,13 @@
import { cn } from "@/lib/utils"
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="skeleton"
className={cn("bg-accent animate-pulse rounded-md", className)}
{...props}
/>
)
}
export { Skeleton }

View file

@ -0,0 +1,61 @@
"use client"
import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import { cn } from "@/lib/utils"
function TooltipProvider({
delayDuration = 0,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
return (
<TooltipPrimitive.Provider
data-slot="tooltip-provider"
delayDuration={delayDuration}
{...props}
/>
)
}
function Tooltip({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return (
<TooltipProvider>
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
</TooltipProvider>
)
}
function TooltipTrigger({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
}
function TooltipContent({
className,
sideOffset = 0,
children,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
className
)}
{...props}
>
{children}
<TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
)
}
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }

View file

@ -0,0 +1,23 @@
'use client';
import React from 'react';
import { SidebarProvider } from '../ui/sidebar';
export default function SidebarProviderWrapper({
defaultOpen,
children,
}: {
defaultOpen: boolean;
children: React.ReactNode;
}) {
const [open, setOpen] = React.useState(defaultOpen);
return (
<SidebarProvider
defaultOpen={defaultOpen}
open={open}
onOpenChange={setOpen}
>
{children}
</SidebarProvider>
);
}

19
src/hooks/use-mobile.ts Normal file
View file

@ -0,0 +1,19 @@
import * as React from "react"
const MOBILE_BREAKPOINT = 768
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
}
mql.addEventListener("change", onChange)
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
return () => mql.removeEventListener("change", onChange)
}, [])
return !!isMobile
}

482
yarn.lock
View file

@ -543,15 +543,15 @@ __metadata:
linkType: hard
"@gerrit0/mini-shiki@npm:^3.2.2":
version: 3.6.0
resolution: "@gerrit0/mini-shiki@npm:3.6.0"
version: 3.7.0
resolution: "@gerrit0/mini-shiki@npm:3.7.0"
dependencies:
"@shikijs/engine-oniguruma": "npm:^3.6.0"
"@shikijs/langs": "npm:^3.6.0"
"@shikijs/themes": "npm:^3.6.0"
"@shikijs/types": "npm:^3.6.0"
"@shikijs/engine-oniguruma": "npm:^3.7.0"
"@shikijs/langs": "npm:^3.7.0"
"@shikijs/themes": "npm:^3.7.0"
"@shikijs/types": "npm:^3.7.0"
"@shikijs/vscode-textmate": "npm:^10.0.2"
checksum: 10c0/347456c9da8a1fadd3c1f63097da459a5f930ef4bca6431cce913a379012c551e061d0a94ff7a0f307215b87f2418b7c198a55fba888fc97fb02ab36247adf6b
checksum: 10c0/eb3f4900d841338077d839ebbc7f8722b13876a586cff7abc73295e956683724dd3371a9f990900184a2d069461965951b2604d677991badf3474262e7811384
languageName: node
linkType: hard
@ -1299,6 +1299,32 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-collapsible@npm:^1.1.11":
version: 1.1.11
resolution: "@radix-ui/react-collapsible@npm:1.1.11"
dependencies:
"@radix-ui/primitive": "npm:1.1.2"
"@radix-ui/react-compose-refs": "npm:1.1.2"
"@radix-ui/react-context": "npm:1.1.2"
"@radix-ui/react-id": "npm:1.1.1"
"@radix-ui/react-presence": "npm:1.1.4"
"@radix-ui/react-primitive": "npm:2.1.3"
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
"@radix-ui/react-use-layout-effect": "npm:1.1.1"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/fa2de539ef06e2b2d18acebb12a34ce1534ca88bd484b7359aac05534d1e551fe83eaafbf60915c00161bb370f0dc9fc303903133510dea0a59fd018155b7db5
languageName: node
linkType: hard
"@radix-ui/react-collection@npm:1.1.7":
version: 1.1.7
resolution: "@radix-ui/react-collection@npm:1.1.7"
@ -1347,6 +1373,38 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-dialog@npm:^1.1.14":
version: 1.1.14
resolution: "@radix-ui/react-dialog@npm:1.1.14"
dependencies:
"@radix-ui/primitive": "npm:1.1.2"
"@radix-ui/react-compose-refs": "npm:1.1.2"
"@radix-ui/react-context": "npm:1.1.2"
"@radix-ui/react-dismissable-layer": "npm:1.1.10"
"@radix-ui/react-focus-guards": "npm:1.1.2"
"@radix-ui/react-focus-scope": "npm:1.1.7"
"@radix-ui/react-id": "npm:1.1.1"
"@radix-ui/react-portal": "npm:1.1.9"
"@radix-ui/react-presence": "npm:1.1.4"
"@radix-ui/react-primitive": "npm:2.1.3"
"@radix-ui/react-slot": "npm:1.2.3"
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
aria-hidden: "npm:^1.2.4"
react-remove-scroll: "npm:^2.6.3"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/ab7bc783510ed8fccfe91020b214f4a571d5a1d46d398faa33f4c151bc9f586c47483b307e72b67687b06694c194b3aa80dd1de728460fa765db9f3057690ba3
languageName: node
linkType: hard
"@radix-ui/react-direction@npm:1.1.1":
version: 1.1.1
resolution: "@radix-ui/react-direction@npm:1.1.1"
@ -1719,7 +1777,7 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-separator@npm:^1.1.6":
"@radix-ui/react-separator@npm:^1.1.7":
version: 1.1.7
resolution: "@radix-ui/react-separator@npm:1.1.7"
dependencies:
@ -1738,7 +1796,7 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-slot@npm:1.2.3, @radix-ui/react-slot@npm:^1.2.2":
"@radix-ui/react-slot@npm:1.2.3, @radix-ui/react-slot@npm:^1.2.3":
version: 1.2.3
resolution: "@radix-ui/react-slot@npm:1.2.3"
dependencies:
@ -1804,6 +1862,36 @@ __metadata:
languageName: node
linkType: hard
"@radix-ui/react-tooltip@npm:^1.2.7":
version: 1.2.7
resolution: "@radix-ui/react-tooltip@npm:1.2.7"
dependencies:
"@radix-ui/primitive": "npm:1.1.2"
"@radix-ui/react-compose-refs": "npm:1.1.2"
"@radix-ui/react-context": "npm:1.1.2"
"@radix-ui/react-dismissable-layer": "npm:1.1.10"
"@radix-ui/react-id": "npm:1.1.1"
"@radix-ui/react-popper": "npm:1.2.7"
"@radix-ui/react-portal": "npm:1.1.9"
"@radix-ui/react-presence": "npm:1.1.4"
"@radix-ui/react-primitive": "npm:2.1.3"
"@radix-ui/react-slot": "npm:1.2.3"
"@radix-ui/react-use-controllable-state": "npm:1.2.2"
"@radix-ui/react-visually-hidden": "npm:1.2.3"
peerDependencies:
"@types/react": "*"
"@types/react-dom": "*"
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
"@types/react":
optional: true
"@types/react-dom":
optional: true
checksum: 10c0/28798d576c6ffec4f11120cd563aa9d5ab9afb9a37dc18778176442756d026c8c46eec1ddc647b2b5914045495fcb89f82530106e91acb55776b7d6b1a10fb57
languageName: node
linkType: hard
"@radix-ui/react-use-callback-ref@npm:1.1.1":
version: 1.1.1
resolution: "@radix-ui/react-use-callback-ref@npm:1.1.1"
@ -1966,7 +2054,7 @@ __metadata:
languageName: node
linkType: hard
"@shikijs/engine-oniguruma@npm:^3.6.0":
"@shikijs/engine-oniguruma@npm:^3.7.0":
version: 3.7.0
resolution: "@shikijs/engine-oniguruma@npm:3.7.0"
dependencies:
@ -1976,7 +2064,7 @@ __metadata:
languageName: node
linkType: hard
"@shikijs/langs@npm:^3.6.0":
"@shikijs/langs@npm:^3.7.0":
version: 3.7.0
resolution: "@shikijs/langs@npm:3.7.0"
dependencies:
@ -1985,7 +2073,7 @@ __metadata:
languageName: node
linkType: hard
"@shikijs/themes@npm:^3.6.0":
"@shikijs/themes@npm:^3.7.0":
version: 3.7.0
resolution: "@shikijs/themes@npm:3.7.0"
dependencies:
@ -1994,7 +2082,7 @@ __metadata:
languageName: node
linkType: hard
"@shikijs/types@npm:3.7.0, @shikijs/types@npm:^3.6.0":
"@shikijs/types@npm:3.7.0, @shikijs/types@npm:^3.7.0":
version: 3.7.0
resolution: "@shikijs/types@npm:3.7.0"
dependencies:
@ -3094,11 +3182,11 @@ __metadata:
linkType: hard
"@types/node@npm:*":
version: 24.0.3
resolution: "@types/node@npm:24.0.3"
version: 24.0.4
resolution: "@types/node@npm:24.0.4"
dependencies:
undici-types: "npm:~7.8.0"
checksum: 10c0/9c3c4e87600d1cf11e291c2fd4bfd806a615455463c30a0ef6dc9c801b3423344d9b82b8084e3ccabce485a7421ebb61a66e9676181bd7d9aea4759998a120d5
checksum: 10c0/590e8cb0ec59fb9cd566402120e690d87ecbdf57f1ee2b8493266121ed33aa4b25949a0c6156b84a6ffb9250baaf1f80e9af142da542ed603e6ee73fc4d1115f
languageName: node
linkType: hard
@ -3190,104 +3278,104 @@ __metadata:
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
version: 8.34.1
resolution: "@typescript-eslint/eslint-plugin@npm:8.34.1"
version: 8.35.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.35.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.10.0"
"@typescript-eslint/scope-manager": "npm:8.34.1"
"@typescript-eslint/type-utils": "npm:8.34.1"
"@typescript-eslint/utils": "npm:8.34.1"
"@typescript-eslint/visitor-keys": "npm:8.34.1"
"@typescript-eslint/scope-manager": "npm:8.35.0"
"@typescript-eslint/type-utils": "npm:8.35.0"
"@typescript-eslint/utils": "npm:8.35.0"
"@typescript-eslint/visitor-keys": "npm:8.35.0"
graphemer: "npm:^1.4.0"
ignore: "npm:^7.0.0"
natural-compare: "npm:^1.4.0"
ts-api-utils: "npm:^2.1.0"
peerDependencies:
"@typescript-eslint/parser": ^8.34.1
"@typescript-eslint/parser": ^8.35.0
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.9.0"
checksum: 10c0/f1c9f25e4fe4b59622312dfa0ca1e80fa7945296ba5c04362a5fda084a17e23a6b98dac331f5a13bcb1ba34a2b598a3f5c41aa288f0c51fe60196e912954e56a
checksum: 10c0/27391f1b168a175fdc62370e5afe51317d4433115abbbff8ee0aea8ecd7bf6dd541a76f8e0cc94119750ae3146863204862640acb45394f0b92809e88d39f881
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
version: 8.34.1
resolution: "@typescript-eslint/parser@npm:8.34.1"
version: 8.35.0
resolution: "@typescript-eslint/parser@npm:8.35.0"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.34.1"
"@typescript-eslint/types": "npm:8.34.1"
"@typescript-eslint/typescript-estree": "npm:8.34.1"
"@typescript-eslint/visitor-keys": "npm:8.34.1"
"@typescript-eslint/scope-manager": "npm:8.35.0"
"@typescript-eslint/types": "npm:8.35.0"
"@typescript-eslint/typescript-estree": "npm:8.35.0"
"@typescript-eslint/visitor-keys": "npm:8.35.0"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.9.0"
checksum: 10c0/bf8070245d53ef6926ff6630bb72f245923f545304e2a61508fb944802a83fed8eab961d9010956d07999d51afdfbbec82aea9d6185295551a7c17c00d759183
checksum: 10c0/8f1cda98f8bee3d79266974e5e5c831a0ca473e928fb16f1dc1c85ee24f2cb9c0fcf3c1bcbbef9d6044cf063f6e59d3198b766a27000776830fe591043e11625
languageName: node
linkType: hard
"@typescript-eslint/project-service@npm:8.34.1":
version: 8.34.1
resolution: "@typescript-eslint/project-service@npm:8.34.1"
"@typescript-eslint/project-service@npm:8.35.0":
version: 8.35.0
resolution: "@typescript-eslint/project-service@npm:8.35.0"
dependencies:
"@typescript-eslint/tsconfig-utils": "npm:^8.34.1"
"@typescript-eslint/types": "npm:^8.34.1"
"@typescript-eslint/tsconfig-utils": "npm:^8.35.0"
"@typescript-eslint/types": "npm:^8.35.0"
debug: "npm:^4.3.4"
peerDependencies:
typescript: ">=4.8.4 <5.9.0"
checksum: 10c0/9333a890625f6777054db17a6b299281ae7502bb7615261d15b885a75b8cf65fc91591389c93b37ecd14b651d8e94851dac8718e5dcc8ed0600533535dae855c
checksum: 10c0/c2d6d44b6b2ff3ecabec8ade824163196799060ac457661eb94049487d770ce68d128b33a2f24090adf1ebcb66ff6c9a05fc6659349b9a0784a5a080ecf8ff81
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.34.1":
version: 8.34.1
resolution: "@typescript-eslint/scope-manager@npm:8.34.1"
"@typescript-eslint/scope-manager@npm:8.35.0":
version: 8.35.0
resolution: "@typescript-eslint/scope-manager@npm:8.35.0"
dependencies:
"@typescript-eslint/types": "npm:8.34.1"
"@typescript-eslint/visitor-keys": "npm:8.34.1"
checksum: 10c0/2af608fa3900f4726322e33bf4f3a376fdace3ac0f310cf7d9256bbc2905c3896138176a47dd195d2c2229f27fe43f5deb4bc7729db2eb18389926dedea78077
"@typescript-eslint/types": "npm:8.35.0"
"@typescript-eslint/visitor-keys": "npm:8.35.0"
checksum: 10c0/a27cf27a1852bb0d6ea08f475fcc79557f1977be96ef563d92127e8011e4065566441c32c40eb7a530111ffd3a8489919da7f8a2b7466a610cfc9c07670a9601
languageName: node
linkType: hard
"@typescript-eslint/tsconfig-utils@npm:8.34.1, @typescript-eslint/tsconfig-utils@npm:^8.34.1":
version: 8.34.1
resolution: "@typescript-eslint/tsconfig-utils@npm:8.34.1"
"@typescript-eslint/tsconfig-utils@npm:8.35.0, @typescript-eslint/tsconfig-utils@npm:^8.35.0":
version: 8.35.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.35.0"
peerDependencies:
typescript: ">=4.8.4 <5.9.0"
checksum: 10c0/8d1ead8b7c279b48e2ed96f083ec119a9aeea1ca9cdd40576ec271b996b9fd8cfa0ddb0aafbb4e14bc27fc62c69c5be66d39b1de68eab9ddd7f1861da267423d
checksum: 10c0/baa18e7137ba72f7d138f50d1168e8f334198a36499f954821e2369027e5b3d53ca93c354943e7782ba5caab604b050af10f353ccca34fbc0b23c48d6174832f
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.34.1":
version: 8.34.1
resolution: "@typescript-eslint/type-utils@npm:8.34.1"
"@typescript-eslint/type-utils@npm:8.35.0":
version: 8.35.0
resolution: "@typescript-eslint/type-utils@npm:8.35.0"
dependencies:
"@typescript-eslint/typescript-estree": "npm:8.34.1"
"@typescript-eslint/utils": "npm:8.34.1"
"@typescript-eslint/typescript-estree": "npm:8.35.0"
"@typescript-eslint/utils": "npm:8.35.0"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^2.1.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.9.0"
checksum: 10c0/502a2cdfe47f1f34206c747b5a70e0242dd99f570511db3dda9c5f999d9abadfbbb1dfa82a1fa437a1689d232715412e61c97d95f19c9314ba5ad23196b4096d
checksum: 10c0/9e23a332484a055eb73ba8918f95a981e0cec8fa623ba9ee0b57328af052628d630a415e32e0dbe95318574e62d4066f8aecc994728b3cedd906f36c616ec362
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.34.1, @typescript-eslint/types@npm:^8.34.1":
version: 8.34.1
resolution: "@typescript-eslint/types@npm:8.34.1"
checksum: 10c0/db1b3dce6a70b28ddb13c76fbb5983240d9395656df5f7cbd99bfd9905e39c0dab2132870f01dbc406b48739c437f7d344a879a824cedaba81b91a53110dc23a
"@typescript-eslint/types@npm:8.35.0, @typescript-eslint/types@npm:^8.35.0":
version: 8.35.0
resolution: "@typescript-eslint/types@npm:8.35.0"
checksum: 10c0/a2711a932680805e83252b5d7c55ac30437bdc4d40c444606cf6ccb6ba23a682da015ec03c64635e77bf733f84d9bb76810bf4f7177fd3a660db8a2c8a05e845
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.34.1":
version: 8.34.1
resolution: "@typescript-eslint/typescript-estree@npm:8.34.1"
"@typescript-eslint/typescript-estree@npm:8.35.0":
version: 8.35.0
resolution: "@typescript-eslint/typescript-estree@npm:8.35.0"
dependencies:
"@typescript-eslint/project-service": "npm:8.34.1"
"@typescript-eslint/tsconfig-utils": "npm:8.34.1"
"@typescript-eslint/types": "npm:8.34.1"
"@typescript-eslint/visitor-keys": "npm:8.34.1"
"@typescript-eslint/project-service": "npm:8.35.0"
"@typescript-eslint/tsconfig-utils": "npm:8.35.0"
"@typescript-eslint/types": "npm:8.35.0"
"@typescript-eslint/visitor-keys": "npm:8.35.0"
debug: "npm:^4.3.4"
fast-glob: "npm:^3.3.2"
is-glob: "npm:^4.0.3"
@ -3296,166 +3384,166 @@ __metadata:
ts-api-utils: "npm:^2.1.0"
peerDependencies:
typescript: ">=4.8.4 <5.9.0"
checksum: 10c0/4ee7249db91b9840361f34f80b7b6d646a3af159c7298d79a33d8a11c98792fd3a395343e5e17e0fa29529e8f0113bac8baadcef90d1e140bd736a48f0485042
checksum: 10c0/7e94f6a92efc5832289e8bfd0b61209aa501224c935359253c29aeef8e0b981b370ee2a43e2909991c3c3cf709fcccb6380474e0e9a863e8f89e2fbd213aed59
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.34.1":
version: 8.34.1
resolution: "@typescript-eslint/utils@npm:8.34.1"
"@typescript-eslint/utils@npm:8.35.0":
version: 8.35.0
resolution: "@typescript-eslint/utils@npm:8.35.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.7.0"
"@typescript-eslint/scope-manager": "npm:8.34.1"
"@typescript-eslint/types": "npm:8.34.1"
"@typescript-eslint/typescript-estree": "npm:8.34.1"
"@typescript-eslint/scope-manager": "npm:8.35.0"
"@typescript-eslint/types": "npm:8.35.0"
"@typescript-eslint/typescript-estree": "npm:8.35.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: ">=4.8.4 <5.9.0"
checksum: 10c0/e3085877f7940c02a37653e6bc52ac6cde115e755b1f788fe4331202f371b3421cc4d0878c7d3eb054e14e9b3a064496a707a73eac471cb2b73593b9e9d4b998
checksum: 10c0/e3317df7875305bee16edd573e4bfdafc099f26f9c284d8adb351333683aacd5b668320870653dff7ec7e0da1982bbf89dc06197bc193a3be65362f21452dbea
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.34.1":
version: 8.34.1
resolution: "@typescript-eslint/visitor-keys@npm:8.34.1"
"@typescript-eslint/visitor-keys@npm:8.35.0":
version: 8.35.0
resolution: "@typescript-eslint/visitor-keys@npm:8.35.0"
dependencies:
"@typescript-eslint/types": "npm:8.34.1"
"@typescript-eslint/types": "npm:8.35.0"
eslint-visitor-keys: "npm:^4.2.1"
checksum: 10c0/0e5a9b3d93905d16d3cf8cb5fb346dcc6f760482eb7d0ac209aefc09a32f78ef28a687634df6ad08e81fb3e1083e8805f34472de6bbc501c0105ad654d518f40
checksum: 10c0/df18ca9b6931cb58f5dc404fcc94f9e0cc1c22f3053c7013ab588bb8ccccd3d58a70c577c01267845d57fa124a8cf8371260d284dad97505c56b2abcf70a3dce
languageName: node
linkType: hard
"@unrs/resolver-binding-android-arm-eabi@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.9.1"
"@unrs/resolver-binding-android-arm-eabi@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.9.2"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@unrs/resolver-binding-android-arm64@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-android-arm64@npm:1.9.1"
"@unrs/resolver-binding-android-arm64@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-android-arm64@npm:1.9.2"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@unrs/resolver-binding-darwin-arm64@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.9.1"
"@unrs/resolver-binding-darwin-arm64@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.9.2"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@unrs/resolver-binding-darwin-x64@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-darwin-x64@npm:1.9.1"
"@unrs/resolver-binding-darwin-x64@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-darwin-x64@npm:1.9.2"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@unrs/resolver-binding-freebsd-x64@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-freebsd-x64@npm:1.9.1"
"@unrs/resolver-binding-freebsd-x64@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-freebsd-x64@npm:1.9.2"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.9.1"
"@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.9.2"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-arm-musleabihf@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-arm-musleabihf@npm:1.9.1"
"@unrs/resolver-binding-linux-arm-musleabihf@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-arm-musleabihf@npm:1.9.2"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-arm64-gnu@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-arm64-gnu@npm:1.9.1"
"@unrs/resolver-binding-linux-arm64-gnu@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-arm64-gnu@npm:1.9.2"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-arm64-musl@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-arm64-musl@npm:1.9.1"
"@unrs/resolver-binding-linux-arm64-musl@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-arm64-musl@npm:1.9.2"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-ppc64-gnu@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-ppc64-gnu@npm:1.9.1"
"@unrs/resolver-binding-linux-ppc64-gnu@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-ppc64-gnu@npm:1.9.2"
conditions: os=linux & cpu=ppc64 & libc=glibc
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-riscv64-gnu@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-riscv64-gnu@npm:1.9.1"
"@unrs/resolver-binding-linux-riscv64-gnu@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-riscv64-gnu@npm:1.9.2"
conditions: os=linux & cpu=riscv64 & libc=glibc
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-riscv64-musl@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-riscv64-musl@npm:1.9.1"
"@unrs/resolver-binding-linux-riscv64-musl@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-riscv64-musl@npm:1.9.2"
conditions: os=linux & cpu=riscv64 & libc=musl
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-s390x-gnu@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-s390x-gnu@npm:1.9.1"
"@unrs/resolver-binding-linux-s390x-gnu@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-s390x-gnu@npm:1.9.2"
conditions: os=linux & cpu=s390x & libc=glibc
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-x64-gnu@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-x64-gnu@npm:1.9.1"
"@unrs/resolver-binding-linux-x64-gnu@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-x64-gnu@npm:1.9.2"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@unrs/resolver-binding-linux-x64-musl@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-linux-x64-musl@npm:1.9.1"
"@unrs/resolver-binding-linux-x64-musl@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-linux-x64-musl@npm:1.9.2"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@unrs/resolver-binding-wasm32-wasi@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.9.1"
"@unrs/resolver-binding-wasm32-wasi@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.9.2"
dependencies:
"@napi-rs/wasm-runtime": "npm:^0.2.11"
conditions: cpu=wasm32
languageName: node
linkType: hard
"@unrs/resolver-binding-win32-arm64-msvc@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.9.1"
"@unrs/resolver-binding-win32-arm64-msvc@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.9.2"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@unrs/resolver-binding-win32-ia32-msvc@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.9.1"
"@unrs/resolver-binding-win32-ia32-msvc@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.9.2"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@unrs/resolver-binding-win32-x64-msvc@npm:1.9.1":
version: 1.9.1
resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.9.1"
"@unrs/resolver-binding-win32-x64-msvc@npm:1.9.2":
version: 1.9.2
resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.9.2"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
@ -3662,7 +3750,7 @@ __metadata:
languageName: node
linkType: hard
"array-includes@npm:^3.1.6, array-includes@npm:^3.1.8":
"array-includes@npm:^3.1.6, array-includes@npm:^3.1.8, array-includes@npm:^3.1.9":
version: 3.1.9
resolution: "array-includes@npm:3.1.9"
dependencies:
@ -3699,7 +3787,7 @@ __metadata:
languageName: node
linkType: hard
"array.prototype.findlastindex@npm:^1.2.5":
"array.prototype.findlastindex@npm:^1.2.6":
version: 1.2.6
resolution: "array.prototype.findlastindex@npm:1.2.6"
dependencies:
@ -3714,7 +3802,7 @@ __metadata:
languageName: node
linkType: hard
"array.prototype.flat@npm:^1.3.1, array.prototype.flat@npm:^1.3.2":
"array.prototype.flat@npm:^1.3.1, array.prototype.flat@npm:^1.3.3":
version: 1.3.3
resolution: "array.prototype.flat@npm:1.3.3"
dependencies:
@ -3964,9 +4052,9 @@ __metadata:
linkType: hard
"caniuse-lite@npm:^1.0.30001579":
version: 1.0.30001724
resolution: "caniuse-lite@npm:1.0.30001724"
checksum: 10c0/ed9ec0bcf619f0e7ef2d33aac74d2346d1faf52060dfded1fb9c32d87854de5c2988b3ba338c281034c88bf797d6b55468a804ce8396a7e16a48cb0d481d4bfe
version: 1.0.30001726
resolution: "caniuse-lite@npm:1.0.30001726"
checksum: 10c0/2c5f91da7fd9ebf8c6b432818b1498ea28aca8de22b30dafabe2a2a6da1e014f10e67e14f8e68e872a0867b6b4cd6001558dde04e3ab9770c9252ca5c8849d0e
languageName: node
linkType: hard
@ -4418,12 +4506,12 @@ __metadata:
linkType: hard
"enhanced-resolve@npm:^5.18.1":
version: 5.18.1
resolution: "enhanced-resolve@npm:5.18.1"
version: 5.18.2
resolution: "enhanced-resolve@npm:5.18.2"
dependencies:
graceful-fs: "npm:^4.2.4"
tapable: "npm:^2.2.0"
checksum: 10c0/4cffd9b125225184e2abed9fdf0ed3dbd2224c873b165d0838fd066cde32e0918626cba2f1f4bf6860762f13a7e2364fd89a82b99566be2873d813573ac71846
checksum: 10c0/2a45105daded694304b0298d1c0351a981842249a9867513d55e41321a4ccf37dfd35b0c1e9ceae290eab73654b09aa7a910d618ea6f9441e97c52bc424a2372
languageName: node
linkType: hard
@ -4792,7 +4880,7 @@ __metadata:
languageName: node
linkType: hard
"eslint-module-utils@npm:^2.12.0":
"eslint-module-utils@npm:^2.12.1":
version: 2.12.1
resolution: "eslint-module-utils@npm:2.12.1"
dependencies:
@ -4805,31 +4893,31 @@ __metadata:
linkType: hard
"eslint-plugin-import@npm:^2.31.0":
version: 2.31.0
resolution: "eslint-plugin-import@npm:2.31.0"
version: 2.32.0
resolution: "eslint-plugin-import@npm:2.32.0"
dependencies:
"@rtsao/scc": "npm:^1.1.0"
array-includes: "npm:^3.1.8"
array.prototype.findlastindex: "npm:^1.2.5"
array.prototype.flat: "npm:^1.3.2"
array.prototype.flatmap: "npm:^1.3.2"
array-includes: "npm:^3.1.9"
array.prototype.findlastindex: "npm:^1.2.6"
array.prototype.flat: "npm:^1.3.3"
array.prototype.flatmap: "npm:^1.3.3"
debug: "npm:^3.2.7"
doctrine: "npm:^2.1.0"
eslint-import-resolver-node: "npm:^0.3.9"
eslint-module-utils: "npm:^2.12.0"
eslint-module-utils: "npm:^2.12.1"
hasown: "npm:^2.0.2"
is-core-module: "npm:^2.15.1"
is-core-module: "npm:^2.16.1"
is-glob: "npm:^4.0.3"
minimatch: "npm:^3.1.2"
object.fromentries: "npm:^2.0.8"
object.groupby: "npm:^1.0.3"
object.values: "npm:^1.2.0"
object.values: "npm:^1.2.1"
semver: "npm:^6.3.1"
string.prototype.trimend: "npm:^1.0.8"
string.prototype.trimend: "npm:^1.0.9"
tsconfig-paths: "npm:^3.15.0"
peerDependencies:
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
checksum: 10c0/e21d116ddd1900e091ad120b3eb68c5dd5437fe2c930f1211781cd38b246f090a6b74d5f3800b8255a0ed29782591521ad44eb21c5534960a8f1fb4040fd913a
checksum: 10c0/bfb1b8fc8800398e62ddfefbf3638d185286edfed26dfe00875cc2846d954491b4f5112457831588b757fa789384e1ae585f812614c4797f0499fa234fd4a48b
languageName: node
linkType: hard
@ -5758,7 +5846,7 @@ __metadata:
languageName: node
linkType: hard
"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1, is-core-module@npm:^2.16.0":
"is-core-module@npm:^2.13.0, is-core-module@npm:^2.16.0, is-core-module@npm:^2.16.1":
version: 2.16.1
resolution: "is-core-module@npm:2.16.1"
dependencies:
@ -6454,12 +6542,12 @@ __metadata:
languageName: node
linkType: hard
"lucide-react@npm:^0.511.0":
version: 0.511.0
resolution: "lucide-react@npm:0.511.0"
"lucide-react@npm:^0.515.0":
version: 0.515.0
resolution: "lucide-react@npm:0.515.0"
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
checksum: 10c0/bf09dd73cf2233abea90506ad31a91739555d761062722acbe045cb73e274f035b196472de0971a8a8f0645b2b54e3f21b8c1980fe87c909ca93171a9c28428a
checksum: 10c0/00485e09ab3d0bbb34797b1f368c269e8708522b6e2f46fd84dd5bd99741546487be9a65a260f274e8049b81cc37687566e26132f5752352c8d9bc8e5d0b3dea
languageName: node
linkType: hard
@ -6549,15 +6637,18 @@ __metadata:
"@fortawesome/react-fontawesome": "npm:^0.2.2"
"@hookform/resolvers": "npm:^5.0.1"
"@prisma/client": "npm:^6.9.0"
"@radix-ui/react-collapsible": "npm:^1.1.11"
"@radix-ui/react-dialog": "npm:^1.1.14"
"@radix-ui/react-dropdown-menu": "npm:^2.1.14"
"@radix-ui/react-hover-card": "npm:^1.1.13"
"@radix-ui/react-label": "npm:^2.1.6"
"@radix-ui/react-scroll-area": "npm:^1.2.8"
"@radix-ui/react-select": "npm:^2.2.4"
"@radix-ui/react-separator": "npm:^1.1.6"
"@radix-ui/react-slot": "npm:^1.2.2"
"@radix-ui/react-separator": "npm:^1.1.7"
"@radix-ui/react-slot": "npm:^1.2.3"
"@radix-ui/react-switch": "npm:^1.2.4"
"@radix-ui/react-tabs": "npm:^1.1.11"
"@radix-ui/react-tooltip": "npm:^1.2.7"
"@tailwindcss/postcss": "npm:4.1.10"
"@tanstack/react-query": "npm:^5.80.7"
"@types/node": "npm:22.15.33"
@ -6572,7 +6663,7 @@ __metadata:
eslint: "npm:9.29.0"
eslint-config-next: "npm:15.3.4"
eslint-config-prettier: "npm:10.1.5"
lucide-react: "npm:^0.511.0"
lucide-react: "npm:^0.515.0"
next: "npm:15.4.0-canary.95"
next-auth: "npm:^5.0.0-beta.25"
next-themes: "npm:^0.4.6"
@ -6794,7 +6885,7 @@ __metadata:
languageName: node
linkType: hard
"napi-postinstall@npm:^0.2.2":
"napi-postinstall@npm:^0.2.4":
version: 0.2.4
resolution: "napi-postinstall@npm:0.2.4"
bin:
@ -7185,7 +7276,7 @@ __metadata:
languageName: node
linkType: hard
"object.values@npm:^1.1.6, object.values@npm:^1.2.0, object.values@npm:^1.2.1":
"object.values@npm:^1.1.6, object.values@npm:^1.2.1":
version: 1.2.1
resolution: "object.values@npm:1.2.1"
dependencies:
@ -7233,7 +7324,7 @@ __metadata:
languageName: node
linkType: hard
"openapi3-ts@npm:4.4.0, openapi3-ts@npm:^4.1.2, openapi3-ts@npm:^4.2.2":
"openapi3-ts@npm:4.4.0":
version: 4.4.0
resolution: "openapi3-ts@npm:4.4.0"
dependencies:
@ -7242,6 +7333,15 @@ __metadata:
languageName: node
linkType: hard
"openapi3-ts@npm:^4.1.2, openapi3-ts@npm:^4.2.2":
version: 4.5.0
resolution: "openapi3-ts@npm:4.5.0"
dependencies:
yaml: "npm:^2.8.0"
checksum: 10c0/97de2d24e9ceffb89e1388e137e4a6e17ee57a02dce0c938a5e98b1338ac72b31e8b2ce8dd28945ad43fae8bee2a145892cb548ba5ae60b0930f1b6b79b0747d
languageName: node
linkType: hard
"optionator@npm:^0.9.3":
version: 0.9.4
resolution: "optionator@npm:0.9.4"
@ -8569,7 +8669,7 @@ __metadata:
languageName: node
linkType: hard
"string.prototype.trimend@npm:^1.0.8, string.prototype.trimend@npm:^1.0.9":
"string.prototype.trimend@npm:^1.0.9":
version: 1.0.9
resolution: "string.prototype.trimend@npm:1.0.9"
dependencies:
@ -9171,29 +9271,29 @@ __metadata:
linkType: hard
"unrs-resolver@npm:^1.6.2":
version: 1.9.1
resolution: "unrs-resolver@npm:1.9.1"
version: 1.9.2
resolution: "unrs-resolver@npm:1.9.2"
dependencies:
"@unrs/resolver-binding-android-arm-eabi": "npm:1.9.1"
"@unrs/resolver-binding-android-arm64": "npm:1.9.1"
"@unrs/resolver-binding-darwin-arm64": "npm:1.9.1"
"@unrs/resolver-binding-darwin-x64": "npm:1.9.1"
"@unrs/resolver-binding-freebsd-x64": "npm:1.9.1"
"@unrs/resolver-binding-linux-arm-gnueabihf": "npm:1.9.1"
"@unrs/resolver-binding-linux-arm-musleabihf": "npm:1.9.1"
"@unrs/resolver-binding-linux-arm64-gnu": "npm:1.9.1"
"@unrs/resolver-binding-linux-arm64-musl": "npm:1.9.1"
"@unrs/resolver-binding-linux-ppc64-gnu": "npm:1.9.1"
"@unrs/resolver-binding-linux-riscv64-gnu": "npm:1.9.1"
"@unrs/resolver-binding-linux-riscv64-musl": "npm:1.9.1"
"@unrs/resolver-binding-linux-s390x-gnu": "npm:1.9.1"
"@unrs/resolver-binding-linux-x64-gnu": "npm:1.9.1"
"@unrs/resolver-binding-linux-x64-musl": "npm:1.9.1"
"@unrs/resolver-binding-wasm32-wasi": "npm:1.9.1"
"@unrs/resolver-binding-win32-arm64-msvc": "npm:1.9.1"
"@unrs/resolver-binding-win32-ia32-msvc": "npm:1.9.1"
"@unrs/resolver-binding-win32-x64-msvc": "npm:1.9.1"
napi-postinstall: "npm:^0.2.2"
"@unrs/resolver-binding-android-arm-eabi": "npm:1.9.2"
"@unrs/resolver-binding-android-arm64": "npm:1.9.2"
"@unrs/resolver-binding-darwin-arm64": "npm:1.9.2"
"@unrs/resolver-binding-darwin-x64": "npm:1.9.2"
"@unrs/resolver-binding-freebsd-x64": "npm:1.9.2"
"@unrs/resolver-binding-linux-arm-gnueabihf": "npm:1.9.2"
"@unrs/resolver-binding-linux-arm-musleabihf": "npm:1.9.2"
"@unrs/resolver-binding-linux-arm64-gnu": "npm:1.9.2"
"@unrs/resolver-binding-linux-arm64-musl": "npm:1.9.2"
"@unrs/resolver-binding-linux-ppc64-gnu": "npm:1.9.2"
"@unrs/resolver-binding-linux-riscv64-gnu": "npm:1.9.2"
"@unrs/resolver-binding-linux-riscv64-musl": "npm:1.9.2"
"@unrs/resolver-binding-linux-s390x-gnu": "npm:1.9.2"
"@unrs/resolver-binding-linux-x64-gnu": "npm:1.9.2"
"@unrs/resolver-binding-linux-x64-musl": "npm:1.9.2"
"@unrs/resolver-binding-wasm32-wasi": "npm:1.9.2"
"@unrs/resolver-binding-win32-arm64-msvc": "npm:1.9.2"
"@unrs/resolver-binding-win32-ia32-msvc": "npm:1.9.2"
"@unrs/resolver-binding-win32-x64-msvc": "npm:1.9.2"
napi-postinstall: "npm:^0.2.4"
dependenciesMeta:
"@unrs/resolver-binding-android-arm-eabi":
optional: true
@ -9233,7 +9333,7 @@ __metadata:
optional: true
"@unrs/resolver-binding-win32-x64-msvc":
optional: true
checksum: 10c0/fded9251b6c180c92c0510abe63e4fa9a5a4adcdcf3c9f7920507dc9f1ec756de5e71d1258f12bf4a32f7042e1fe142b6dc1003d8a6fb4d0bf1234226c879b01
checksum: 10c0/e3481cc19ea4b25f888e2412bbd80a729b13527a41b035e784b71d1a7d4e2109b58b174adce989085eb75c787435e80ffb385db2b1598288474f53beb01438c0
languageName: node
linkType: hard
@ -9518,7 +9618,7 @@ __metadata:
languageName: node
linkType: hard
"yaml@npm:^2.3.4, yaml@npm:^2.5.0, yaml@npm:^2.7.1":
"yaml@npm:^2.3.4, yaml@npm:^2.5.0, yaml@npm:^2.7.1, yaml@npm:^2.8.0":
version: 2.8.0
resolution: "yaml@npm:2.8.0"
bin: