Rework #6

Merged
monoid merged 38 commits from dev into main 2024-04-17 01:45:37 +09:00
14 changed files with 913 additions and 69 deletions
Showing only changes of commit 62ec80565e - Show all commits

View File

@ -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",

View File

@ -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;
}

View File

@ -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

View 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>
);
}

View 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>
}

View 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 }

View 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 }

View 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 }

View 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());
};
}

View File

@ -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;

View File

@ -0,0 +1,6 @@
export default function Gallery() {
return (<div>
a
</div>
);
}

View File

@ -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[],

View 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;
}

View File

@ -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'}