Rework #6
					 14 changed files with 913 additions and 69 deletions
				
			
		| 
						 | 
				
			
			@ -12,12 +12,17 @@
 | 
			
		|||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@radix-ui/react-icons": "^1.3.0",
 | 
			
		||||
    "@radix-ui/react-slot": "^1.0.2",
 | 
			
		||||
    "@radix-ui/react-tooltip": "^1.0.7",
 | 
			
		||||
    "class-variance-authority": "^0.7.0",
 | 
			
		||||
    "clsx": "^2.1.0",
 | 
			
		||||
    "react": "^18.2.0",
 | 
			
		||||
    "react-dom": "^18.2.0",
 | 
			
		||||
    "react-resizable-panels": "^2.0.16",
 | 
			
		||||
    "swr": "^2.2.5",
 | 
			
		||||
    "tailwind-merge": "^2.2.2",
 | 
			
		||||
    "tailwindcss-animate": "^1.0.7"
 | 
			
		||||
    "tailwindcss-animate": "^1.0.7",
 | 
			
		||||
    "wouter": "^3.1.0"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@types/node": ">=20.0.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100..900&display=swap');
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    font-family: "Noto Sans KR", sans-serif;
 | 
			
		||||
    font-optical-sizing: auto;
 | 
			
		||||
    min-height: 100vh;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#root {
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    min-height: 100vh;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,59 +1,44 @@
 | 
			
		|||
import { Route, Switch, Redirect } from "wouter";
 | 
			
		||||
 | 
			
		||||
import './App.css'
 | 
			
		||||
// import React, { createContext, useEffect, useRef, useState } from "react";
 | 
			
		||||
// import ReactDom from "react-dom";
 | 
			
		||||
// import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
 | 
			
		||||
 | 
			
		||||
// import {
 | 
			
		||||
// 	DifferencePage,
 | 
			
		||||
// 	DocumentAbout,
 | 
			
		||||
// 	Gallery,
 | 
			
		||||
// 	LoginPage,
 | 
			
		||||
// 	NotFoundPage,
 | 
			
		||||
// 	ProfilePage,
 | 
			
		||||
// 	ReaderPage,
 | 
			
		||||
// 	SettingPage,
 | 
			
		||||
// 	TagsPage,
 | 
			
		||||
// 	// DifferencePage,
 | 
			
		||||
// 	// DocumentAbout,
 | 
			
		||||
// 	// Gallery,
 | 
			
		||||
// 	// LoginPage,
 | 
			
		||||
// 	// NotFoundPage,
 | 
			
		||||
// 	// ProfilePage,
 | 
			
		||||
// 	// ReaderPage,
 | 
			
		||||
// 	// SettingPage,
 | 
			
		||||
// 	// TagsPage,
 | 
			
		||||
// } from "./page/mod";
 | 
			
		||||
// import { getInitialValue, UserContext } from "./state";
 | 
			
		||||
 | 
			
		||||
import { TooltipProvider } from "./components/ui/tooltip";
 | 
			
		||||
 | 
			
		||||
import Gallery from "./page/galleryPage";
 | 
			
		||||
import Layout from "./components/layout/layout";
 | 
			
		||||
import NotFoundPage from "./page/404";
 | 
			
		||||
 | 
			
		||||
const App = () => {
 | 
			
		||||
	// const [user, setUser] = useState("");
 | 
			
		||||
	// const [userPermission, setUserPermission] = useState<string[]>([]);
 | 
			
		||||
	// (async () => {
 | 
			
		||||
	// 	const { username, permission } = await getInitialValue();
 | 
			
		||||
	// 	if (username !== user) {
 | 
			
		||||
	// 		setUser(username);
 | 
			
		||||
	// 		setUserPermission(permission);
 | 
			
		||||
	// 	}
 | 
			
		||||
	// })();
 | 
			
		||||
	// useEffect(()=>{});
 | 
			
		||||
	return ( <h1 className="text-3xl font-bold underline">
 | 
			
		||||
	Hello world!
 | 
			
		||||
  </h1>
 | 
			
		||||
		// <UserContext.Provider
 | 
			
		||||
		// 	value={{
 | 
			
		||||
		// 		username: user,
 | 
			
		||||
		// 		setUsername: setUser,
 | 
			
		||||
		// 		permission: userPermission,
 | 
			
		||||
		// 		setPermission: setUserPermission,
 | 
			
		||||
		// 	}}
 | 
			
		||||
		// >
 | 
			
		||||
		// 		<BrowserRouter>
 | 
			
		||||
		// 			<Routes>
 | 
			
		||||
		// 				<Route path="/" element={<Navigate replace to="/search?" />} />
 | 
			
		||||
		// 				<Route path="/search" element={<Gallery />} />
 | 
			
		||||
		// 				<Route path="/doc/:id" element={<DocumentAbout />}></Route>
 | 
			
		||||
		// 				<Route path="/doc/:id/reader" element={<ReaderPage />}></Route>
 | 
			
		||||
		// 				<Route path="/login" element={<LoginPage></LoginPage>} />
 | 
			
		||||
		// 				<Route path="/profile" element={<ProfilePage />}></Route>
 | 
			
		||||
		// 				<Route path="/difference" element={<DifferencePage />}></Route>
 | 
			
		||||
		// 				<Route path="/setting" element={<SettingPage />}></Route>
 | 
			
		||||
		// 				<Route path="/tags" element={<TagsPage />}></Route>
 | 
			
		||||
		// 				<Route path="*" element={<NotFoundPage />} />
 | 
			
		||||
		// 			</Routes>
 | 
			
		||||
		// 		</BrowserRouter>
 | 
			
		||||
		// </UserContext.Provider>
 | 
			
		||||
	);
 | 
			
		||||
	return (
 | 
			
		||||
		<TooltipProvider>
 | 
			
		||||
 | 
			
		||||
			<Layout>
 | 
			
		||||
				<Switch>
 | 
			
		||||
					<Route path="/" component={() => <Redirect replace to="/search?" />} />
 | 
			
		||||
					<Route path="/search" component={Gallery} />
 | 
			
		||||
					{/* <Route path="/doc/:id" component={<DocumentAbout />}></Route>
 | 
			
		||||
				<Route path="/doc/:id/reader" component={<ReaderPage />}></Route>
 | 
			
		||||
				<Route path="/login" component={<LoginPage></LoginPage>} />
 | 
			
		||||
				<Route path="/profile" component={<ProfilePage />}></Route>
 | 
			
		||||
				<Route path="/difference" component={<DifferencePage />}></Route>
 | 
			
		||||
				<Route path="/setting" component={<SettingPage />}></Route>
 | 
			
		||||
			<Route path="/tags" component={<TagsPage />}></Route>*/}
 | 
			
		||||
					<Route component={NotFoundPage} />
 | 
			
		||||
				</Switch>
 | 
			
		||||
			</Layout>
 | 
			
		||||
		</TooltipProvider>);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default App
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										50
									
								
								packages/client/src/components/layout/layout.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								packages/client/src/components/layout/layout.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
import { useLayoutEffect, useState } from "react";
 | 
			
		||||
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "../ui/resizable";
 | 
			
		||||
import { NavList } from "./nav";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface LayoutProps {
 | 
			
		||||
    children?: React.ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default function Layout({ children }: LayoutProps) {
 | 
			
		||||
    const MIN_SIZE_IN_PIXELS = 70;
 | 
			
		||||
    const [minSize, setMinSize] = useState(MIN_SIZE_IN_PIXELS);
 | 
			
		||||
 | 
			
		||||
    useLayoutEffect(() => {
 | 
			
		||||
        const panelGroup = document.querySelector('[data-panel-group-id="main"]');
 | 
			
		||||
        const resizeHandles = document.querySelectorAll(
 | 
			
		||||
            "[data-panel-resize-handle-id]"
 | 
			
		||||
        );
 | 
			
		||||
        if (!panelGroup || !resizeHandles) return;
 | 
			
		||||
        console.log(panelGroup, resizeHandles);
 | 
			
		||||
        const observer = new ResizeObserver(() => {
 | 
			
		||||
            let width = panelGroup?.clientWidth;
 | 
			
		||||
            if (!width) return;
 | 
			
		||||
            width -= [...resizeHandles].reduce((acc, resizeHandle) => acc + resizeHandle.clientWidth, 0);
 | 
			
		||||
            // Minimum size in pixels is a percentage of the PanelGroup's height,
 | 
			
		||||
            // less the (fixed) height of the resize handles.
 | 
			
		||||
            setMinSize((MIN_SIZE_IN_PIXELS / width) * 100);
 | 
			
		||||
        });
 | 
			
		||||
        observer.observe(panelGroup);
 | 
			
		||||
        for (const resizeHandle of resizeHandles) {
 | 
			
		||||
            observer.observe(resizeHandle);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return () => {
 | 
			
		||||
            observer.disconnect();
 | 
			
		||||
        };
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <ResizablePanelGroup direction="horizontal" id="main">
 | 
			
		||||
            <ResizablePanel minSize={minSize} collapsible maxSize={minSize}>
 | 
			
		||||
                <NavList />
 | 
			
		||||
            </ResizablePanel>
 | 
			
		||||
            <ResizableHandle withHandle />
 | 
			
		||||
            <ResizablePanel>
 | 
			
		||||
                {children}
 | 
			
		||||
            </ResizablePanel>
 | 
			
		||||
        </ResizablePanelGroup>
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								packages/client/src/components/layout/nav.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								packages/client/src/components/layout/nav.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"
 | 
			
		||||
import { Link } from "wouter"
 | 
			
		||||
import { MagnifyingGlassIcon, GearIcon, ActivityLogIcon, ArchiveIcon, PersonIcon } from "@radix-ui/react-icons"
 | 
			
		||||
import { buttonVariants } from "../ui/button"
 | 
			
		||||
 | 
			
		||||
interface NavItemProps {
 | 
			
		||||
    icon: React.ReactNode;
 | 
			
		||||
    to: string;
 | 
			
		||||
    name: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function NavItem({
 | 
			
		||||
    icon,
 | 
			
		||||
    to,
 | 
			
		||||
    name
 | 
			
		||||
}: NavItemProps) {
 | 
			
		||||
    return <Tooltip>
 | 
			
		||||
        <TooltipTrigger asChild>
 | 
			
		||||
            <Link
 | 
			
		||||
                href={to}
 | 
			
		||||
                className={buttonVariants({ variant: "ghost" })}
 | 
			
		||||
            >
 | 
			
		||||
                {icon}
 | 
			
		||||
                <span className="sr-only">{name}</span>
 | 
			
		||||
            </Link>
 | 
			
		||||
        </TooltipTrigger>
 | 
			
		||||
        <TooltipContent side="right">{name}</TooltipContent>
 | 
			
		||||
    </Tooltip>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function NavList() {
 | 
			
		||||
    return <aside className="h-screen flex flex-col">
 | 
			
		||||
        <nav className="flex flex-col items-center gap-4 px-2 sm:py-5 flex-1">
 | 
			
		||||
            <NavItem icon={<MagnifyingGlassIcon className="h-5 w-5" />} to="/search" name="Search" />
 | 
			
		||||
            <NavItem icon={<ActivityLogIcon className="h-5 w-5" />} to="/tags" name="Tags" />
 | 
			
		||||
            <NavItem icon={<ArchiveIcon className="h-5 w-5" />} to="/difference" name="Difference" />
 | 
			
		||||
        </nav>
 | 
			
		||||
        <nav className="mt-auto flex flex-col items-center gap-4 px-2 sm:py-5 flex-grow-0">
 | 
			
		||||
            <NavItem icon={<PersonIcon className="h-5 w-5" />} to="/profile" name="Profiles" />
 | 
			
		||||
            <NavItem icon={<GearIcon className="h-5 w-5" />} to="/setting" name="Settings" />
 | 
			
		||||
        </nav>
 | 
			
		||||
    </aside>
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								packages/client/src/components/ui/button.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								packages/client/src/components/ui/button.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
import * as React from "react"
 | 
			
		||||
import { Slot } from "@radix-ui/react-slot"
 | 
			
		||||
import { cva, type VariantProps } from "class-variance-authority"
 | 
			
		||||
 | 
			
		||||
import { cn } from "@/lib/utils"
 | 
			
		||||
 | 
			
		||||
const buttonVariants = cva(
 | 
			
		||||
  "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
 | 
			
		||||
  {
 | 
			
		||||
    variants: {
 | 
			
		||||
      variant: {
 | 
			
		||||
        default:
 | 
			
		||||
          "bg-primary text-primary-foreground shadow hover:bg-primary/90",
 | 
			
		||||
        destructive:
 | 
			
		||||
          "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
 | 
			
		||||
        outline:
 | 
			
		||||
          "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
 | 
			
		||||
        secondary:
 | 
			
		||||
          "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
 | 
			
		||||
        ghost: "hover:bg-accent hover:text-accent-foreground",
 | 
			
		||||
        link: "text-primary underline-offset-4 hover:underline",
 | 
			
		||||
      },
 | 
			
		||||
      size: {
 | 
			
		||||
        default: "h-9 px-4 py-2",
 | 
			
		||||
        sm: "h-8 rounded-md px-3 text-xs",
 | 
			
		||||
        lg: "h-10 rounded-md px-8",
 | 
			
		||||
        icon: "h-9 w-9",
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    defaultVariants: {
 | 
			
		||||
      variant: "default",
 | 
			
		||||
      size: "default",
 | 
			
		||||
    },
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
export interface ButtonProps
 | 
			
		||||
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
 | 
			
		||||
    VariantProps<typeof buttonVariants> {
 | 
			
		||||
  asChild?: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
 | 
			
		||||
  ({ className, variant, size, asChild = false, ...props }, ref) => {
 | 
			
		||||
    const Comp = asChild ? Slot : "button"
 | 
			
		||||
    return (
 | 
			
		||||
      <Comp
 | 
			
		||||
        className={cn(buttonVariants({ variant, size, className }))}
 | 
			
		||||
        ref={ref}
 | 
			
		||||
        {...props}
 | 
			
		||||
      />
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
Button.displayName = "Button"
 | 
			
		||||
 | 
			
		||||
export { Button, buttonVariants }
 | 
			
		||||
							
								
								
									
										43
									
								
								packages/client/src/components/ui/resizable.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								packages/client/src/components/ui/resizable.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
import { DragHandleDots2Icon } from "@radix-ui/react-icons"
 | 
			
		||||
import * as ResizablePrimitive from "react-resizable-panels"
 | 
			
		||||
 | 
			
		||||
import { cn } from "@/lib/utils"
 | 
			
		||||
 | 
			
		||||
const ResizablePanelGroup = ({
 | 
			
		||||
  className,
 | 
			
		||||
  ...props
 | 
			
		||||
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
 | 
			
		||||
  <ResizablePrimitive.PanelGroup
 | 
			
		||||
    className={cn(
 | 
			
		||||
      "flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
 | 
			
		||||
      className
 | 
			
		||||
    )}
 | 
			
		||||
    {...props}
 | 
			
		||||
  />
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const ResizablePanel = ResizablePrimitive.Panel
 | 
			
		||||
 | 
			
		||||
const ResizableHandle = ({
 | 
			
		||||
  withHandle,
 | 
			
		||||
  className,
 | 
			
		||||
  ...props
 | 
			
		||||
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
 | 
			
		||||
  withHandle?: boolean
 | 
			
		||||
}) => (
 | 
			
		||||
  <ResizablePrimitive.PanelResizeHandle
 | 
			
		||||
    className={cn(
 | 
			
		||||
      "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
 | 
			
		||||
      className
 | 
			
		||||
    )}
 | 
			
		||||
    {...props}
 | 
			
		||||
  >
 | 
			
		||||
    {withHandle && (
 | 
			
		||||
      <div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
 | 
			
		||||
        <DragHandleDots2Icon className="h-2.5 w-2.5" />
 | 
			
		||||
      </div>
 | 
			
		||||
    )}
 | 
			
		||||
  </ResizablePrimitive.PanelResizeHandle>
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
 | 
			
		||||
							
								
								
									
										28
									
								
								packages/client/src/components/ui/tooltip.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								packages/client/src/components/ui/tooltip.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
import * as React from "react"
 | 
			
		||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
 | 
			
		||||
 | 
			
		||||
import { cn } from "@/lib/utils"
 | 
			
		||||
 | 
			
		||||
const TooltipProvider = TooltipPrimitive.Provider
 | 
			
		||||
 | 
			
		||||
const Tooltip = TooltipPrimitive.Root
 | 
			
		||||
 | 
			
		||||
const TooltipTrigger = TooltipPrimitive.Trigger
 | 
			
		||||
 | 
			
		||||
const TooltipContent = React.forwardRef<
 | 
			
		||||
  React.ElementRef<typeof TooltipPrimitive.Content>,
 | 
			
		||||
  React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
 | 
			
		||||
>(({ className, sideOffset = 4, ...props }, ref) => (
 | 
			
		||||
  <TooltipPrimitive.Content
 | 
			
		||||
    ref={ref}
 | 
			
		||||
    sideOffset={sideOffset}
 | 
			
		||||
    className={cn(
 | 
			
		||||
      "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs 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",
 | 
			
		||||
      className
 | 
			
		||||
    )}
 | 
			
		||||
    {...props}
 | 
			
		||||
  />
 | 
			
		||||
))
 | 
			
		||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName
 | 
			
		||||
 | 
			
		||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
 | 
			
		||||
							
								
								
									
										70
									
								
								packages/client/src/lib/atom.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								packages/client/src/lib/atom.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
import { useEffect, useReducer, useState } from "react";
 | 
			
		||||
 | 
			
		||||
interface AtomState<T> {
 | 
			
		||||
    value: T;
 | 
			
		||||
    listeners: Set<() => void>;
 | 
			
		||||
}
 | 
			
		||||
interface Atom<T> {
 | 
			
		||||
    key: string;
 | 
			
		||||
    default: T;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const atomStateMap = new WeakMap<Atom<unknown>, AtomState<unknown>>();
 | 
			
		||||
 | 
			
		||||
export function atom<T>(key: string, defaultVal: T): Atom<T> {
 | 
			
		||||
    return { key, default: defaultVal };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getAtomState<T>(atom: Atom<T>): AtomState<T> {
 | 
			
		||||
    let atomState = atomStateMap.get(atom);
 | 
			
		||||
    if (!atomState) {
 | 
			
		||||
        atomState = {
 | 
			
		||||
            value: atom.default,
 | 
			
		||||
            listeners: new Set(),
 | 
			
		||||
        };
 | 
			
		||||
        atomStateMap.set(atom, atomState);
 | 
			
		||||
    }
 | 
			
		||||
    return atomState as AtomState<T>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useAtom<T>(atom: Atom<T>): [T, (val: T) => void] {
 | 
			
		||||
    const state = getAtomState(atom);
 | 
			
		||||
    const [, setState] = useState(state.value);
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const listener = () => setState(state.value);
 | 
			
		||||
        state.listeners.add(listener);
 | 
			
		||||
        return () => {
 | 
			
		||||
            state.listeners.delete(listener);
 | 
			
		||||
        };
 | 
			
		||||
    }, [state]);
 | 
			
		||||
    return [
 | 
			
		||||
        state.value as T,
 | 
			
		||||
        (val: T) => {
 | 
			
		||||
            state.value = val;
 | 
			
		||||
            // biome-ignore lint/complexity/noForEach: forEach is used to call each listener
 | 
			
		||||
            state.listeners.forEach((listener) => listener());
 | 
			
		||||
        },
 | 
			
		||||
    ];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useAtomValue<T>(atom: Atom<T>): T {
 | 
			
		||||
    const state = getAtomState(atom);
 | 
			
		||||
    const update = useReducer((x) => x + 1, 0)[1];
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const listener = () => update();
 | 
			
		||||
        state.listeners.add(listener);
 | 
			
		||||
        return () => {
 | 
			
		||||
            state.listeners.delete(listener);
 | 
			
		||||
        };
 | 
			
		||||
    }, [state, update]);
 | 
			
		||||
    return state.value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function setAtomValue<T>(atom: Atom<T>): (val: T) => void {
 | 
			
		||||
    const state = getAtomState(atom);
 | 
			
		||||
    return (val: T) => {
 | 
			
		||||
        state.value = val;
 | 
			
		||||
        // biome-ignore lint/complexity/noForEach: forEach is used to call each listener
 | 
			
		||||
        state.listeners.forEach((listener) => listener());
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,15 +1,9 @@
 | 
			
		|||
import { Typography } from "@mui/material";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { CommonMenuList, Headline } from "../component/mod";
 | 
			
		||||
import { PagePad } from "../component/pagepad";
 | 
			
		||||
 | 
			
		||||
export const NotFoundPage = () => {
 | 
			
		||||
	const menu = CommonMenuList();
 | 
			
		||||
	return (
 | 
			
		||||
		<Headline menu={menu}>
 | 
			
		||||
			<PagePad>
 | 
			
		||||
				<Typography variant="h2">404 Not Found</Typography>
 | 
			
		||||
			</PagePad>
 | 
			
		||||
		</Headline>
 | 
			
		||||
	return (<div className="flex items-center justify-center flex-col box-border h-screen space-y-2">
 | 
			
		||||
				<h2 className="text-6xl">404 Not Found</h2>
 | 
			
		||||
				<p>찾을 수 없음</p>
 | 
			
		||||
			</div>
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default NotFoundPage;
 | 
			
		||||
							
								
								
									
										6
									
								
								packages/client/src/page/galleryPage.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								packages/client/src/page/galleryPage.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
export default function Gallery() {
 | 
			
		||||
    return (<div>
 | 
			
		||||
        a
 | 
			
		||||
    </div>
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import React, { createContext, useRef, useState } from "react";
 | 
			
		||||
export const BackLinkContext = createContext({ backLink: "", setBackLink: (s: string) => {} });
 | 
			
		||||
 | 
			
		||||
export const UserContext = createContext({
 | 
			
		||||
	username: "",
 | 
			
		||||
	permission: [] as string[],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										109
									
								
								packages/client/src/state/user.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								packages/client/src/state/user.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
import { atom, useAtomValue, setAtomValue } from "../lib/atom";
 | 
			
		||||
 | 
			
		||||
type LoginLocalStorage = {
 | 
			
		||||
	username: string;
 | 
			
		||||
	permission: string[];
 | 
			
		||||
	accessExpired: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let localObj: LoginLocalStorage | null = null;
 | 
			
		||||
function getUserSessions() {
 | 
			
		||||
    if (localObj === null) {
 | 
			
		||||
		const storagestr = localStorage.getItem("UserLoginContext") as string | null;
 | 
			
		||||
		const storage = storagestr !== null ? (JSON.parse(storagestr) as LoginLocalStorage | null) : null;
 | 
			
		||||
		localObj = storage;
 | 
			
		||||
	}
 | 
			
		||||
    if (localObj !== null && localObj.accessExpired > Math.floor(Date.now() / 1000)) {
 | 
			
		||||
        return {
 | 
			
		||||
            username: localObj.username,
 | 
			
		||||
            permission: localObj.permission,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function refresh() {
 | 
			
		||||
    const res = await fetch("/user/refresh", {
 | 
			
		||||
        method: "POST",
 | 
			
		||||
    });
 | 
			
		||||
    if (res.status !== 200) throw new Error("Maybe Network Error");
 | 
			
		||||
    const r = (await res.json()) as LoginLocalStorage & { refresh: boolean };
 | 
			
		||||
    if (r.refresh) {
 | 
			
		||||
        localObj = {
 | 
			
		||||
            ...r
 | 
			
		||||
        };
 | 
			
		||||
    } else {
 | 
			
		||||
        localObj = {
 | 
			
		||||
            accessExpired: 0,
 | 
			
		||||
            username: "",
 | 
			
		||||
            permission: r.permission,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    localStorage.setItem("UserLoginContext", JSON.stringify(localObj));
 | 
			
		||||
    return {
 | 
			
		||||
        username: r.username,
 | 
			
		||||
        permission: r.permission,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const doLogout = async () => {
 | 
			
		||||
	const req = await fetch("/user/logout", {
 | 
			
		||||
		method: "POST",
 | 
			
		||||
	});
 | 
			
		||||
    const setVal = setAtomValue(userLoginStateAtom);
 | 
			
		||||
	try {
 | 
			
		||||
		const res = await req.json();
 | 
			
		||||
		localObj = {
 | 
			
		||||
			accessExpired: 0,
 | 
			
		||||
			username: "",
 | 
			
		||||
			permission: res.permission,
 | 
			
		||||
		};
 | 
			
		||||
		window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj));
 | 
			
		||||
        setVal(localObj);
 | 
			
		||||
		return {
 | 
			
		||||
			username: localObj.username,
 | 
			
		||||
			permission: localObj.permission,
 | 
			
		||||
		};
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		console.error(`Server Error ${error}`);
 | 
			
		||||
		return {
 | 
			
		||||
			username: "",
 | 
			
		||||
			permission: [],
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
export const doLogin = async (userLoginInfo: {
 | 
			
		||||
	username: string;
 | 
			
		||||
	password: string;
 | 
			
		||||
}): Promise<string | LoginLocalStorage> => {
 | 
			
		||||
	const res = await fetch("/user/login", {
 | 
			
		||||
		method: "POST",
 | 
			
		||||
		body: JSON.stringify(userLoginInfo),
 | 
			
		||||
		headers: { "content-type": "application/json" },
 | 
			
		||||
	});
 | 
			
		||||
	const b = await res.json();
 | 
			
		||||
	if (res.status !== 200) {
 | 
			
		||||
		return b.detail as string;
 | 
			
		||||
	}
 | 
			
		||||
    const setVal = setAtomValue(userLoginStateAtom);
 | 
			
		||||
	localObj = b;
 | 
			
		||||
    setVal(b);
 | 
			
		||||
	window.localStorage.setItem("UserLoginContext", JSON.stringify(localObj));
 | 
			
		||||
	return b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export async function getInitialValue() {
 | 
			
		||||
    const user = getUserSessions();
 | 
			
		||||
    if (user) {
 | 
			
		||||
        return user;
 | 
			
		||||
    }
 | 
			
		||||
    return refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const userLoginStateAtom = atom("userLoginState", getUserSessions());
 | 
			
		||||
 | 
			
		||||
export function useLogin() {
 | 
			
		||||
    const val = useAtomValue(userLoginStateAtom);
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										451
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										451
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -17,6 +17,12 @@ importers:
 | 
			
		|||
      '@radix-ui/react-icons':
 | 
			
		||||
        specifier: ^1.3.0
 | 
			
		||||
        version: 1.3.0(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-slot':
 | 
			
		||||
        specifier: ^1.0.2
 | 
			
		||||
        version: 1.0.2(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-tooltip':
 | 
			
		||||
        specifier: ^1.0.7
 | 
			
		||||
        version: 1.0.7(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      class-variance-authority:
 | 
			
		||||
        specifier: ^0.7.0
 | 
			
		||||
        version: 0.7.0
 | 
			
		||||
| 
						 | 
				
			
			@ -29,12 +35,21 @@ importers:
 | 
			
		|||
      react-dom:
 | 
			
		||||
        specifier: ^18.2.0
 | 
			
		||||
        version: 18.2.0(react@18.2.0)
 | 
			
		||||
      react-resizable-panels:
 | 
			
		||||
        specifier: ^2.0.16
 | 
			
		||||
        version: 2.0.16(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      swr:
 | 
			
		||||
        specifier: ^2.2.5
 | 
			
		||||
        version: 2.2.5(react@18.2.0)
 | 
			
		||||
      tailwind-merge:
 | 
			
		||||
        specifier: ^2.2.2
 | 
			
		||||
        version: 2.2.2
 | 
			
		||||
      tailwindcss-animate:
 | 
			
		||||
        specifier: ^1.0.7
 | 
			
		||||
        version: 1.0.7(tailwindcss@3.4.3)
 | 
			
		||||
      wouter:
 | 
			
		||||
        specifier: ^3.1.0
 | 
			
		||||
        version: 3.1.0(react@18.2.0)
 | 
			
		||||
    devDependencies:
 | 
			
		||||
      '@types/node':
 | 
			
		||||
        specifier: '>=20.0.0'
 | 
			
		||||
| 
						 | 
				
			
			@ -817,6 +832,34 @@ packages:
 | 
			
		|||
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /@floating-ui/core@1.6.0:
 | 
			
		||||
    resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@floating-ui/utils': 0.2.1
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@floating-ui/dom@1.6.3:
 | 
			
		||||
    resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@floating-ui/core': 1.6.0
 | 
			
		||||
      '@floating-ui/utils': 0.2.1
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@floating-ui/react-dom@2.0.8(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      react: '>=16.8.0'
 | 
			
		||||
      react-dom: '>=16.8.0'
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@floating-ui/dom': 1.6.3
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@floating-ui/utils@0.2.1:
 | 
			
		||||
    resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@humanwhocodes/config-array@0.11.14:
 | 
			
		||||
    resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
 | 
			
		||||
    engines: {node: '>=10.10.0'}
 | 
			
		||||
| 
						 | 
				
			
			@ -911,6 +954,86 @@ packages:
 | 
			
		|||
    requiresBuild: true
 | 
			
		||||
    optional: true
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/primitive@1.0.1:
 | 
			
		||||
    resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      '@types/react-dom': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@types/react-dom':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      '@types/react-dom': 18.2.22
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-context@1.0.1(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      '@types/react-dom': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@types/react-dom':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/primitive': 1.0.1
 | 
			
		||||
      '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      '@types/react-dom': 18.2.22
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-icons@1.3.0(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
| 
						 | 
				
			
			@ -919,6 +1042,277 @@ packages:
 | 
			
		|||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-id@1.0.1(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      '@types/react-dom': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@types/react-dom':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-context': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/rect': 1.0.1
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      '@types/react-dom': 18.2.22
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      '@types/react-dom': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@types/react-dom':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      '@types/react-dom': 18.2.22
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      '@types/react-dom': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@types/react-dom':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      '@types/react-dom': 18.2.22
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      '@types/react-dom': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@types/react-dom':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-slot': 1.0.2(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      '@types/react-dom': 18.2.22
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-slot@1.0.2(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-tooltip@1.0.7(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      '@types/react-dom': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@types/react-dom':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/primitive': 1.0.1
 | 
			
		||||
      '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-context': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-id': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-slot': 1.0.2(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      '@types/react-dom': 18.2.22
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/rect': 1.0.1
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-use-size@1.0.1(@types/react@18.2.71)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.71)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      '@types/react': '*'
 | 
			
		||||
      '@types/react-dom': '*'
 | 
			
		||||
      react: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
      react-dom: ^16.8 || ^17.0 || ^18.0
 | 
			
		||||
    peerDependenciesMeta:
 | 
			
		||||
      '@types/react':
 | 
			
		||||
        optional: true
 | 
			
		||||
      '@types/react-dom':
 | 
			
		||||
        optional: true
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
      '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.22)(@types/react@18.2.71)(react-dom@18.2.0)(react@18.2.0)
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
      '@types/react-dom': 18.2.22
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@radix-ui/rect@1.0.1:
 | 
			
		||||
    resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@babel/runtime': 7.24.1
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /@rollup/rollup-android-arm-eabi@4.13.0:
 | 
			
		||||
    resolution: {integrity: sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==}
 | 
			
		||||
    cpu: [arm]
 | 
			
		||||
| 
						 | 
				
			
			@ -1382,7 +1776,6 @@ packages:
 | 
			
		|||
 | 
			
		||||
  /@types/prop-types@15.7.12:
 | 
			
		||||
    resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /@types/qs@6.9.14:
 | 
			
		||||
    resolution: {integrity: sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==}
 | 
			
		||||
| 
						 | 
				
			
			@ -1396,7 +1789,6 @@ packages:
 | 
			
		|||
    resolution: {integrity: sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
      '@types/react': 18.2.71
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /@types/react@18.2.71:
 | 
			
		||||
    resolution: {integrity: sha512-PxEsB9OjmQeYGffoWnYAd/r5FiJuUw2niFQHPc2v2idwh8wGPkkYzOHuinNJJY6NZqfoTCiOIizDOz38gYNsyw==}
 | 
			
		||||
| 
						 | 
				
			
			@ -1404,7 +1796,6 @@ packages:
 | 
			
		|||
      '@types/prop-types': 15.7.12
 | 
			
		||||
      '@types/scheduler': 0.23.0
 | 
			
		||||
      csstype: 3.1.3
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /@types/responselike@1.0.3:
 | 
			
		||||
    resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
 | 
			
		||||
| 
						 | 
				
			
			@ -1414,7 +1805,6 @@ packages:
 | 
			
		|||
 | 
			
		||||
  /@types/scheduler@0.23.0:
 | 
			
		||||
    resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /@types/semver@7.5.8:
 | 
			
		||||
    resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
 | 
			
		||||
| 
						 | 
				
			
			@ -1933,6 +2323,10 @@ packages:
 | 
			
		|||
    engines: {node: '>=6'}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /client-only@0.0.1:
 | 
			
		||||
    resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /clone-response@1.0.3:
 | 
			
		||||
    resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
| 
						 | 
				
			
			@ -2079,7 +2473,6 @@ packages:
 | 
			
		|||
 | 
			
		||||
  /csstype@3.1.3:
 | 
			
		||||
    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /data-uri-to-buffer@4.0.1:
 | 
			
		||||
    resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
 | 
			
		||||
| 
						 | 
				
			
			@ -3432,6 +3825,10 @@ packages:
 | 
			
		|||
    resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
 | 
			
		||||
    engines: {node: '>=16 || 14 >=14.17'}
 | 
			
		||||
 | 
			
		||||
  /mitt@3.0.1:
 | 
			
		||||
    resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /mkdirp-classic@0.5.3:
 | 
			
		||||
    resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3934,6 +4331,16 @@ packages:
 | 
			
		|||
      scheduler: 0.23.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /react-resizable-panels@2.0.16(react-dom@18.2.0)(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-UrnxmTZaTnbCl/xIOX38ig35RicqGfLuqt2x5fytpNlQvCRuxyXZwIBEhmF+pmrEGxfajyXFBoCplNxLvhF0CQ==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      react: ^16.14.0 || ^17.0.0 || ^18.0.0
 | 
			
		||||
      react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0
 | 
			
		||||
    dependencies:
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      react-dom: 18.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /react@18.2.0:
 | 
			
		||||
    resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
 | 
			
		||||
    engines: {node: '>=0.10.0'}
 | 
			
		||||
| 
						 | 
				
			
			@ -3989,6 +4396,11 @@ packages:
 | 
			
		|||
    resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /regexparam@3.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q==}
 | 
			
		||||
    engines: {node: '>=8'}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /resolve-alpn@1.2.1:
 | 
			
		||||
    resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
 | 
			
		||||
    dev: true
 | 
			
		||||
| 
						 | 
				
			
			@ -4384,6 +4796,16 @@ packages:
 | 
			
		|||
    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
 | 
			
		||||
    engines: {node: '>= 0.4'}
 | 
			
		||||
 | 
			
		||||
  /swr@2.2.5(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      react: ^16.11.0 || ^17.0.0 || ^18.0.0
 | 
			
		||||
    dependencies:
 | 
			
		||||
      client-only: 0.0.1
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      use-sync-external-store: 1.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /tailwind-merge@2.2.2:
 | 
			
		||||
    resolution: {integrity: sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==}
 | 
			
		||||
    dependencies:
 | 
			
		||||
| 
						 | 
				
			
			@ -4612,6 +5034,14 @@ packages:
 | 
			
		|||
      punycode: 2.3.1
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /use-sync-external-store@1.2.0(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      react: ^16.8.0 || ^17.0.0 || ^18.0.0
 | 
			
		||||
    dependencies:
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /util-deprecate@1.0.2:
 | 
			
		||||
    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4681,6 +5111,17 @@ packages:
 | 
			
		|||
    dependencies:
 | 
			
		||||
      isexe: 2.0.0
 | 
			
		||||
 | 
			
		||||
  /wouter@3.1.0(react@18.2.0):
 | 
			
		||||
    resolution: {integrity: sha512-hou3w+12BMTBckdWdyJp/z7+kKcbdLDWfz6omSyrO6bbx4irNuQQyLDQkfSGXXJCxmglea3c8On9XFUkBSU8+Q==}
 | 
			
		||||
    peerDependencies:
 | 
			
		||||
      react: '>=16.8.0'
 | 
			
		||||
    dependencies:
 | 
			
		||||
      mitt: 3.0.1
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
      regexparam: 3.0.0
 | 
			
		||||
      use-sync-external-store: 1.2.0(react@18.2.0)
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /wrap-ansi@7.0.0:
 | 
			
		||||
    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
 | 
			
		||||
    engines: {node: '>=10'}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue