Fixed login bug again

This commit is contained in:
Alois 2026-04-02 22:17:49 +02:00
commit 0d214de5ed
21 changed files with 1142 additions and 1147 deletions

View file

@ -77,7 +77,6 @@ function Chat() {
}
const rootRoute = createRootRoute({
component: RootShell,
});
@ -99,7 +98,7 @@ const chatRoute = createRoute({
component: Chat,
validateSearch: z.object({
id: z.number().optional(),
})
}),
});
const callRoute = createRoute({

View file

@ -1,9 +1,7 @@
import { useEffect, useState, type ReactNode } from "react";
import { useNavigate } from "@tanstack/react-router";
import { type ReactNode } from "react";
import Sidebar from "@/components/sidebar";
import Navbar from "@/components/navbar";
import { useStorage } from "@tensamin/storage/context";
import { motion } from "framer-motion";
@ -13,40 +11,6 @@ import { motion } from "framer-motion";
* @returns unknown.
*/
export default function Layout(props: { children: ReactNode }) {
const [loggedIn, setLoggedIn] = useState<boolean | null>(null);
const { load } = useStorage();
const navigate = useNavigate();
useEffect(() => {
let active = true;
void load("user_id").then((userId) => {
if (!active) {
return;
}
if (userId !== 0) {
setLoggedIn(true);
return;
}
setLoggedIn(false);
void navigate({
to: "/login",
});
});
return () => {
active = false;
};
}, [load, navigate]);
if (loggedIn !== true) {
return null;
}
return (
<motion.div
className="w-full h-full flex bg-sidebar"

View file

@ -8,6 +8,9 @@ import LegalWrapper from "@/features/legal/screen";
import { Toaster } from "@tensamin/ui/cmp/sonner";
import { TooltipProvider } from "@tensamin/ui/cmp/tooltip";
import { useStorage } from "@tensamin/storage/context";
import { useLocation, useNavigate } from "@tanstack/react-router";
/**
* Executes Layout.
* @param props Parameter props.
@ -45,11 +48,52 @@ export default function Layout(props: { children: ReactNode }) {
<Toaster />
<TooltipProvider>
<Storage>
<LegalWrapper>
<Crypto>{props.children}</Crypto>
</LegalWrapper>
<LoginWrapper>
<LegalWrapper>
<Crypto>{props.children}</Crypto>
</LegalWrapper>
</LoginWrapper>
</Storage>
</TooltipProvider>
</>
);
}
function LoginWrapper({ children }: { children: ReactNode }) {
const [loggedIn, setLoggedIn] = useState<boolean | null>(null);
const { load } = useStorage();
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
let active = true;
load("user_id").then((userId) => {
if (!active) {
return;
}
if (userId !== 0) {
setLoggedIn(true);
return;
}
setLoggedIn(false);
navigate({
to: "/login",
});
});
return () => {
active = false;
};
}, [load, navigate, location.pathname]);
if (loggedIn !== true && location.pathname !== "/login") {
return null;
}
return children;
}

View file

@ -6,7 +6,7 @@ export const context = createContext<contextType | undefined>(undefined);
export default function Provider(props: { children: React.ReactNode }) {
const navigate = useNavigate();
const [state, setState] = useState<"closed" | "connecting" | "open">(
"closed",
);
@ -33,14 +33,14 @@ export default function Provider(props: { children: React.ReactNode }) {
// Utils
function joinCall(userId: number, callId?: string) {
// get/generate e2ee secret
// go into convs and find call id
// generate shared secret and decrypt enc call secret
// go into convs and find call id
// generate shared secret and decrypt enc call secret
// check if user is already in call
// connect to call
connect("", "");
// navigate to call page
navigate({ to: "/call", search: { userId: userId, callId: callId } });
}

View file

@ -22,7 +22,9 @@ export default function Provider(props: { children: React.ReactNode }) {
const { load } = useStorage();
const { send } = useSocket();
const [liveMessagesState, setLiveMessagesState] = React.useState<LiveMessage[]>([]);
const [liveMessagesState, setLiveMessagesState] = React.useState<
LiveMessage[]
>([]);
const [currentSharedSecret, setCurrentSharedSecret] = React.useState("");
const locationSearch = useRouterState({

View file

@ -26,7 +26,7 @@ export function log(
blue: "\x1b[34m",
};
const resetCode = "\x1b[0m";
console.log(`${colorCodes[color]}[${logger}]${resetCode}`, ...args);
console.log(`${colorCodes[color]}${logger}${resetCode}`, ...args);
}
const size = 20;

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,5 @@
import { describe, it, expect } from "bun:test";
import {
createTransportClient,
type SchemaMap,
} from "./core";
import { createTransportClient, type SchemaMap } from "./core";
import {
decodeCommunicationMessage,
encodeCommunicationMessage,

View file

@ -199,7 +199,7 @@ export function createTransportClient<T extends SchemaMap>(
*/
const handleIncomingMessage = (message: TypedMessage) => {
if (message.type !== "pong") {
log(2, "Socket", "blue", "Received:", message.type, message.data, {
log(2, "ttp", "blue", "Received:", message.type, message.data, {
id: message.id,
});
}
@ -233,7 +233,7 @@ export function createTransportClient<T extends SchemaMap>(
log(
0,
"Socket",
"ttp",
"red",
`Response validation failed for "${message.type}"`,
result.error,
@ -254,7 +254,7 @@ export function createTransportClient<T extends SchemaMap>(
if (!result.success) {
log(
0,
"Socket",
"ttp",
"red",
`Push-event validation failed for "${message.type}"`,
result.error,
@ -282,7 +282,7 @@ export function createTransportClient<T extends SchemaMap>(
const handleRecoverableDecodeFailure = (
error: RecoverableMessageDecodeError,
) => {
log(1, "Socket", "yellow", "Recoverable message decode failure", {
log(1, "ttp", "yellow", "Recoverable message decode failure", {
id: error.messageId,
type: error.messageType,
error: error.message,
@ -359,13 +359,7 @@ export function createTransportClient<T extends SchemaMap>(
shouldDiscardFrames,
);
} catch (error) {
log(
0,
"Socket",
"red",
"Incoming transport stream failed",
error,
);
log(0, "ttp", "red", "Incoming transport stream failed", error);
handleConnectionFailure(connection, error);
}
})();
@ -387,7 +381,7 @@ export function createTransportClient<T extends SchemaMap>(
);
}
} catch (error) {
log(0, "Socket", "red", "Incoming stream accept loop failed", error);
log(0, "ttp", "red", "Incoming stream accept loop failed", error);
handleConnectionFailure(connection, error);
} finally {
connection.streamReader?.releaseLock();
@ -456,11 +450,11 @@ export function createTransportClient<T extends SchemaMap>(
return;
}
log(1, "Socket", "green", "Connected");
log(1, "ttp", "green", "Connected");
setReadyState(READY_STATE.OPEN);
startIncomingLoop(connection);
} catch (error) {
log(0, "Socket", "red", "WebTransport connection failed", error);
log(0, "ttp", "red", "WebTransport connection failed", error);
handleConnectionFailure(connection, error);
throw error;
}
@ -494,7 +488,7 @@ export function createTransportClient<T extends SchemaMap>(
try {
await writeCloseFrame(connection.transport);
} catch (error) {
log(1, "Socket", "yellow", "Failed to send close sentinel", error);
log(1, "ttp", "yellow", "Failed to send close sentinel", error);
}
try {
@ -544,7 +538,7 @@ export function createTransportClient<T extends SchemaMap>(
if (!result.success) {
log(
0,
"Socket",
"ttp",
"red",
`Request validation failed for "${type}"`,
result.error,
@ -569,7 +563,7 @@ export function createTransportClient<T extends SchemaMap>(
}
if (type !== "ping") {
log(2, "Socket", "purple", "Sent:", type, payload, { id: options.id });
log(2, "ttp", "purple", "Sent:", type, payload, { id: options.id });
}
const expectsResponse = !options?.noResponse;
@ -704,10 +698,7 @@ function logBinaryMessage(
return;
}
console.log(
`[Socket] ${direction} binary message (${payload.byteLength} bytes)`,
payload,
);
log(3, "ttp", "yellow", `${direction} ${payload.byteLength} bytes:`, payload);
}
/**

View file

@ -1,14 +1,14 @@
import * as React from "react"
import { Avatar as AvatarPrimitive } from "radix-ui"
import * as React from "react";
import { Avatar as AvatarPrimitive } from "radix-ui";
import { cn } from "../lib/utils"
import { cn } from "../lib/utils";
function Avatar({
className,
size = "default",
...props
}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
size?: "default" | "sm" | "lg"
size?: "default" | "sm" | "lg";
}) {
return (
<AvatarPrimitive.Root
@ -16,11 +16,11 @@ function Avatar({
data-size={size}
className={cn(
"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
className
className,
)}
{...props}
/>
)
);
}
function AvatarImage({
@ -32,11 +32,11 @@ function AvatarImage({
data-slot="avatar-image"
className={cn(
"aspect-square size-full rounded-full object-cover",
className
className,
)}
{...props}
/>
)
);
}
function AvatarFallback({
@ -48,11 +48,11 @@ function AvatarFallback({
data-slot="avatar-fallback"
className={cn(
"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
className
className,
)}
{...props}
/>
)
);
}
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
@ -64,11 +64,11 @@ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
className
className,
)}
{...props}
/>
)
);
}
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
@ -77,11 +77,11 @@ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
data-slot="avatar-group"
className={cn(
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
className
className,
)}
{...props}
/>
)
);
}
function AvatarGroupCount({
@ -93,11 +93,11 @@ function AvatarGroupCount({
data-slot="avatar-group-count"
className={cn(
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
className
className,
)}
{...props}
/>
)
);
}
export {
@ -107,4 +107,4 @@ export {
AvatarGroup,
AvatarGroupCount,
AvatarBadge,
}
};

View file

@ -1,8 +1,8 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { Slot } from "radix-ui";
import { cn } from "../lib/utils"
import { cn } from "../lib/utils";
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
@ -38,8 +38,8 @@ const buttonVariants = cva(
variant: "default",
size: "default",
},
}
)
},
);
function Button({
className,
@ -49,9 +49,9 @@ function Button({
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
asChild?: boolean;
}) {
const Comp = asChild ? Slot.Root : "button"
const Comp = asChild ? Slot.Root : "button";
return (
<Comp
@ -61,7 +61,7 @@ function Button({
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
);
}
export { Button, buttonVariants }
export { Button, buttonVariants };

View file

@ -1,6 +1,6 @@
import * as React from "react"
import * as React from "react";
import { cn } from "../lib/utils"
import { cn } from "../lib/utils";
function Card({
className,
@ -13,11 +13,11 @@ function Card({
data-size={size}
className={cn(
"group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
className
className,
)}
{...props}
/>
)
);
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
@ -26,11 +26,11 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-header"
className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
className
className,
)}
{...props}
/>
)
);
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
@ -39,11 +39,11 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-title"
className={cn(
"text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
className
className,
)}
{...props}
/>
)
);
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
@ -53,7 +53,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
)
);
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
@ -62,11 +62,11 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
className,
)}
{...props}
/>
)
);
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
@ -76,7 +76,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
{...props}
/>
)
);
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
@ -85,11 +85,11 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-footer"
className={cn(
"flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3",
className
className,
)}
{...props}
/>
)
);
}
export {
@ -100,4 +100,4 @@ export {
CardAction,
CardDescription,
CardContent,
}
};

View file

@ -1,10 +1,10 @@
"use client"
"use client";
import * as React from "react"
import { Checkbox as CheckboxPrimitive } from "radix-ui"
import * as React from "react";
import { Checkbox as CheckboxPrimitive } from "radix-ui";
import { cn } from "../lib/utils"
import { CheckIcon } from "lucide-react"
import { cn } from "../lib/utils";
import { CheckIcon } from "lucide-react";
function Checkbox({
className,
@ -15,7 +15,7 @@ function Checkbox({
data-slot="checkbox"
className={cn(
"peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
className
className,
)}
{...props}
>
@ -23,11 +23,10 @@ function Checkbox({
data-slot="checkbox-indicator"
className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
>
<CheckIcon
/>
<CheckIcon />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
)
);
}
export { Checkbox }
export { Checkbox };

View file

@ -1,13 +1,13 @@
import * as React from "react"
import { ContextMenu as ContextMenuPrimitive } from "radix-ui"
import * as React from "react";
import { ContextMenu as ContextMenuPrimitive } from "radix-ui";
import { cn } from "../lib/utils"
import { ChevronRightIcon, CheckIcon } from "lucide-react"
import { cn } from "../lib/utils";
import { ChevronRightIcon, CheckIcon } from "lucide-react";
function ContextMenu({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />;
}
function ContextMenuTrigger({
@ -20,7 +20,7 @@ function ContextMenuTrigger({
className={cn("select-none", className)}
{...props}
/>
)
);
}
function ContextMenuGroup({
@ -28,7 +28,7 @@ function ContextMenuGroup({
}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
return (
<ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
)
);
}
function ContextMenuPortal({
@ -36,13 +36,13 @@ function ContextMenuPortal({
}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
return (
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
)
);
}
function ContextMenuSub({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />;
}
function ContextMenuRadioGroup({
@ -53,24 +53,27 @@ function ContextMenuRadioGroup({
data-slot="context-menu-radio-group"
{...props}
/>
)
);
}
function ContextMenuContent({
className,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Content> & {
side?: "top" | "right" | "bottom" | "left"
side?: "top" | "right" | "bottom" | "left";
}) {
return (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content
data-slot="context-menu-content"
className={cn("z-50 max-h-(--radix-context-menu-content-available-height) min-w-36 origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!", className )}
className={cn(
"z-50 max-h-(--radix-context-menu-content-available-height) min-w-36 origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
className,
)}
{...props}
/>
</ContextMenuPrimitive.Portal>
)
);
}
function ContextMenuItem({
@ -79,8 +82,8 @@ function ContextMenuItem({
variant = "default",
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
inset?: boolean
variant?: "default" | "destructive"
inset?: boolean;
variant?: "default" | "destructive";
}) {
return (
<ContextMenuPrimitive.Item
@ -89,11 +92,11 @@ function ContextMenuItem({
data-variant={variant}
className={cn(
"group/context-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive",
className
className,
)}
{...props}
/>
)
);
}
function ContextMenuSubTrigger({
@ -102,7 +105,7 @@ function ContextMenuSubTrigger({
children,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
inset?: boolean
inset?: boolean;
}) {
return (
<ContextMenuPrimitive.SubTrigger
@ -110,14 +113,14 @@ function ContextMenuSubTrigger({
data-inset={inset}
className={cn(
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto" />
</ContextMenuPrimitive.SubTrigger>
)
);
}
function ContextMenuSubContent({
@ -127,10 +130,13 @@ function ContextMenuSubContent({
return (
<ContextMenuPrimitive.SubContent
data-slot="context-menu-sub-content"
className={cn("z-50 min-w-32 origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-lg border p-1 text-popover-foreground shadow-lg duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!", className )}
className={cn(
"z-50 min-w-32 origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-lg border p-1 text-popover-foreground shadow-lg duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
className,
)}
{...props}
/>
)
);
}
function ContextMenuCheckboxItem({
@ -140,7 +146,7 @@ function ContextMenuCheckboxItem({
inset,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem> & {
inset?: boolean
inset?: boolean;
}) {
return (
<ContextMenuPrimitive.CheckboxItem
@ -148,20 +154,19 @@ function ContextMenuCheckboxItem({
data-inset={inset}
className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
checked={checked}
{...props}
>
<span className="pointer-events-none absolute right-2">
<ContextMenuPrimitive.ItemIndicator>
<CheckIcon
/>
<CheckIcon />
</ContextMenuPrimitive.ItemIndicator>
</span>
{children}
</ContextMenuPrimitive.CheckboxItem>
)
);
}
function ContextMenuRadioItem({
@ -170,7 +175,7 @@ function ContextMenuRadioItem({
inset,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem> & {
inset?: boolean
inset?: boolean;
}) {
return (
<ContextMenuPrimitive.RadioItem
@ -178,19 +183,18 @@ function ContextMenuRadioItem({
data-inset={inset}
className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
>
<span className="pointer-events-none absolute right-2">
<ContextMenuPrimitive.ItemIndicator>
<CheckIcon
/>
<CheckIcon />
</ContextMenuPrimitive.ItemIndicator>
</span>
{children}
</ContextMenuPrimitive.RadioItem>
)
);
}
function ContextMenuLabel({
@ -198,7 +202,7 @@ function ContextMenuLabel({
inset,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
inset?: boolean
inset?: boolean;
}) {
return (
<ContextMenuPrimitive.Label
@ -206,11 +210,11 @@ function ContextMenuLabel({
data-inset={inset}
className={cn(
"px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
className
className,
)}
{...props}
/>
)
);
}
function ContextMenuSeparator({
@ -223,7 +227,7 @@ function ContextMenuSeparator({
className={cn("-mx-1 my-1 h-px bg-border", className)}
{...props}
/>
)
);
}
function ContextMenuShortcut({
@ -235,11 +239,11 @@ function ContextMenuShortcut({
data-slot="context-menu-shortcut"
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground group-focus/context-menu-item:text-accent-foreground",
className
className,
)}
{...props}
/>
)
);
}
export {
@ -258,4 +262,4 @@ export {
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuRadioGroup,
}
};

View file

@ -1,34 +1,34 @@
"use client"
"use client";
import * as React from "react"
import { Dialog as DialogPrimitive } from "radix-ui"
import * as React from "react";
import { Dialog as DialogPrimitive } from "radix-ui";
import { cn } from "../lib/utils"
import { Button } from "./button"
import { XIcon } from "lucide-react"
import { cn } from "../lib/utils";
import { Button } from "./button";
import { XIcon } from "lucide-react";
function Dialog({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
}
function DialogTrigger({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}
function DialogPortal({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
}
function DialogClose({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}
function DialogOverlay({
@ -40,11 +40,11 @@ function DialogOverlay({
data-slot="dialog-overlay"
className={cn(
"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
className
className,
)}
{...props}
/>
)
);
}
function DialogContent({
@ -53,7 +53,7 @@ function DialogContent({
showCloseButton = true,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean
showCloseButton?: boolean;
}) {
return (
<DialogPortal>
@ -62,7 +62,7 @@ function DialogContent({
data-slot="dialog-content"
className={cn(
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className
className,
)}
{...props}
>
@ -74,15 +74,14 @@ function DialogContent({
className="absolute top-2 right-2"
size="icon-sm"
>
<XIcon
/>
<XIcon />
<span className="sr-only">Close</span>
</Button>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
)
);
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
@ -92,7 +91,7 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
className={cn("flex flex-col gap-2", className)}
{...props}
/>
)
);
}
function DialogFooter({
@ -101,14 +100,14 @@ function DialogFooter({
children,
...props
}: React.ComponentProps<"div"> & {
showCloseButton?: boolean
showCloseButton?: boolean;
}) {
return (
<div
data-slot="dialog-footer"
className={cn(
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
className
className,
)}
{...props}
>
@ -119,7 +118,7 @@ function DialogFooter({
</DialogPrimitive.Close>
)}
</div>
)
);
}
function DialogTitle({
@ -129,13 +128,10 @@ function DialogTitle({
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn(
"text-base leading-none font-medium",
className
)}
className={cn("text-base leading-none font-medium", className)}
{...props}
/>
)
);
}
function DialogDescription({
@ -147,11 +143,11 @@ function DialogDescription({
data-slot="dialog-description"
className={cn(
"text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
className
className,
)}
{...props}
/>
)
);
}
export {
@ -165,4 +161,4 @@ export {
DialogPortal,
DialogTitle,
DialogTrigger,
}
};

View file

@ -1,6 +1,6 @@
import * as React from "react"
import * as React from "react";
import { cn } from "../lib/utils"
import { cn } from "../lib/utils";
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
@ -9,11 +9,11 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
data-slot="input"
className={cn(
"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
className
className,
)}
{...props}
/>
)
);
}
export { Input }
export { Input };

View file

@ -1,9 +1,9 @@
"use client"
"use client";
import * as React from "react"
import { Label as LabelPrimitive } from "radix-ui"
import * as React from "react";
import { Label as LabelPrimitive } from "radix-ui";
import { cn } from "../lib/utils"
import { cn } from "../lib/utils";
function Label({
className,
@ -14,11 +14,11 @@ function Label({
data-slot="label"
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
className,
)}
{...props}
/>
)
);
}
export { Label }
export { Label };

View file

@ -1,13 +1,13 @@
import * as React from "react"
import { Select as SelectPrimitive } from "radix-ui"
import * as React from "react";
import { Select as SelectPrimitive } from "radix-ui";
import { cn } from "../lib/utils"
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
import { cn } from "../lib/utils";
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react";
function Select({
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
return <SelectPrimitive.Root data-slot="select" {...props} />
return <SelectPrimitive.Root data-slot="select" {...props} />;
}
function SelectGroup({
@ -20,13 +20,13 @@ function SelectGroup({
className={cn("scroll-my-1 p-1", className)}
{...props}
/>
)
);
}
function SelectValue({
...props
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
return <SelectPrimitive.Value data-slot="select-value" {...props} />
return <SelectPrimitive.Value data-slot="select-value" {...props} />;
}
function SelectTrigger({
@ -35,7 +35,7 @@ function SelectTrigger({
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: "sm" | "default"
size?: "sm" | "default";
}) {
return (
<SelectPrimitive.Trigger
@ -43,7 +43,7 @@ function SelectTrigger({
data-size={size}
className={cn(
"flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
>
@ -52,7 +52,7 @@ function SelectTrigger({
<ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
)
);
}
function SelectContent({
@ -67,7 +67,12 @@ function SelectContent({
<SelectPrimitive.Content
data-slot="select-content"
data-align-trigger={position === "item-aligned"}
className={cn("z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
className={cn(
"z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className,
)}
position={position}
align={align}
{...props}
@ -77,7 +82,7 @@ function SelectContent({
data-position={position}
className={cn(
"data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)",
position === "popper" && ""
position === "popper" && "",
)}
>
{children}
@ -85,7 +90,7 @@ function SelectContent({
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
)
);
}
function SelectLabel({
@ -98,7 +103,7 @@ function SelectLabel({
className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)}
{...props}
/>
)
);
}
function SelectItem({
@ -111,7 +116,7 @@ function SelectItem({
data-slot="select-item"
className={cn(
"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className
className,
)}
{...props}
>
@ -122,7 +127,7 @@ function SelectItem({
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
)
);
}
function SelectSeparator({
@ -135,7 +140,7 @@ function SelectSeparator({
className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
{...props}
/>
)
);
}
function SelectScrollUpButton({
@ -147,14 +152,13 @@ function SelectScrollUpButton({
data-slot="select-scroll-up-button"
className={cn(
"z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
>
<ChevronUpIcon
/>
<ChevronUpIcon />
</SelectPrimitive.ScrollUpButton>
)
);
}
function SelectScrollDownButton({
@ -166,14 +170,13 @@ function SelectScrollDownButton({
data-slot="select-scroll-down-button"
className={cn(
"z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
className
className,
)}
{...props}
>
<ChevronDownIcon
/>
<ChevronDownIcon />
</SelectPrimitive.ScrollDownButton>
)
);
}
export {
@ -187,4 +190,4 @@ export {
SelectSeparator,
SelectTrigger,
SelectValue,
}
};

View file

@ -1,4 +1,4 @@
import { cn } from "../lib/utils"
import { cn } from "../lib/utils";
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return (
@ -7,7 +7,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
className={cn("animate-pulse rounded-md bg-muted", className)}
{...props}
/>
)
);
}
export { Skeleton }
export { Skeleton };

View file

@ -1,32 +1,28 @@
"use client"
"use client";
import { useTheme } from "next-themes"
import { Toaster as Sonner, type ToasterProps } from "sonner"
import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react"
import { useTheme } from "next-themes";
import { Toaster as Sonner, type ToasterProps } from "sonner";
import {
CircleCheckIcon,
InfoIcon,
TriangleAlertIcon,
OctagonXIcon,
Loader2Icon,
} from "lucide-react";
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme()
const { theme = "system" } = useTheme();
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
icons={{
success: (
<CircleCheckIcon className="size-4" />
),
info: (
<InfoIcon className="size-4" />
),
warning: (
<TriangleAlertIcon className="size-4" />
),
error: (
<OctagonXIcon className="size-4" />
),
loading: (
<Loader2Icon className="size-4 animate-spin" />
),
success: <CircleCheckIcon className="size-4" />,
info: <InfoIcon className="size-4" />,
warning: <TriangleAlertIcon className="size-4" />,
error: <OctagonXIcon className="size-4" />,
loading: <Loader2Icon className="size-4 animate-spin" />,
}}
style={
{
@ -43,7 +39,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
}}
{...props}
/>
)
}
);
};
export { Toaster }
export { Toaster };

View file

@ -1,7 +1,7 @@
import * as React from "react"
import { Tooltip as TooltipPrimitive } from "radix-ui"
import * as React from "react";
import { Tooltip as TooltipPrimitive } from "radix-ui";
import { cn } from "../lib/utils"
import { cn } from "../lib/utils";
function TooltipProvider({
delayDuration = 0,
@ -13,19 +13,19 @@ function TooltipProvider({
delayDuration={delayDuration}
{...props}
/>
)
);
}
function Tooltip({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
}
function TooltipTrigger({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}
function TooltipContent({
@ -41,7 +41,7 @@ function TooltipContent({
sideOffset={sideOffset}
className={cn(
"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 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 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className
className,
)}
{...props}
>
@ -49,7 +49,7 @@ function TooltipContent({
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
)
);
}
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };