chore: remove debugging throughout codebase

This commit is contained in:
Maze Winther 2026-04-15 01:00:05 +02:00
parent dc0a8e7c96
commit 1fa5442f21
12 changed files with 947 additions and 1106 deletions

View File

@ -1,303 +1,302 @@
"use client";
import * as React from "react";
import { ContextMenu as ContextMenuPrimitive } from "radix-ui";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/utils/ui";
import { HugeiconsIcon } from "@hugeicons/react";
import {
Tick02Icon,
ArrowRightIcon,
CircleIcon,
} from "@hugeicons/core-free-icons";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function ContextMenu({
onOpenChange,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
source: "context-menu",
onOpenChange,
});
return (
<ContextMenuPrimitive.Root onOpenChange={handleOpenChange} {...props} />
);
}
const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
const ContextMenuGroup = ContextMenuPrimitive.Group;
const ContextMenuPortal = ContextMenuPrimitive.Portal;
const ContextMenuSub = ContextMenuPrimitive.Sub;
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
const contextMenuItemVariants = cva(
"relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-3 py-1.5 text-sm text-foreground/85 outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-3.5 [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"focus:bg-accent focus:text-accent-foreground [&_svg]:text-muted-foreground",
destructive:
"text-destructive focus:bg-destructive/10 focus:text-destructive [&_svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
},
);
const ContextMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
inset?: boolean;
variant?: VariantProps<typeof contextMenuItemVariants>["variant"];
icon?: React.ReactNode;
}
>(
(
{ className, inset, children, variant = "default", icon, ...props },
ref,
) => (
<ContextMenuPrimitive.SubTrigger
ref={ref}
className={cn(
contextMenuItemVariants({ variant }),
"data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
inset && "pl-8",
className,
)}
{...props}
>
{icon && (
<span className="size-4 shrink-0 text-muted-foreground">{icon}</span>
)}
{children}
<HugeiconsIcon
icon={ArrowRightIcon}
className="ml-auto text-muted-foreground/80"
/>
</ContextMenuPrimitive.SubTrigger>
),
);
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
const ContextMenuSubContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.SubContent
ref={ref}
className={cn(
"bg-popover text-popover-foreground z-50 min-w-48 overflow-hidden rounded-md border shadow-xl p-1",
className,
)}
{...props}
/>
));
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
const ContextMenuContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> & {
container?: HTMLElement | null;
}
>(({ className, container, ...props }, ref) => (
<ContextMenuPrimitive.Portal container={container ?? undefined}>
<ContextMenuPrimitive.Content
ref={ref}
className={cn(
"bg-popover text-popover-foreground z-50 min-w-48 overflow-hidden rounded-md border shadow-xl p-1",
className,
)}
{...props}
/>
</ContextMenuPrimitive.Portal>
));
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
const ContextMenuItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
inset?: boolean;
variant?: VariantProps<typeof contextMenuItemVariants>["variant"];
icon?: React.ReactNode;
textRight?: string;
}
>(
(
{
className,
inset,
variant = "default",
icon,
children,
textRight,
...props
},
ref,
) => {
const shouldInsetContent = inset || Boolean(icon);
return (
<ContextMenuPrimitive.Item
ref={ref}
className={cn(
contextMenuItemVariants({ variant }),
shouldInsetContent && "pl-8",
className,
)}
{...props}
>
{icon && (
<span className="absolute left-3 flex size-3.5 items-center justify-center text-muted-foreground [&_svg]:size-3.5 [&_svg]:shrink-0">
{icon}
</span>
)}
{children}
{textRight && (
<span className="ml-auto text-[0.60rem] tracking-widest text-muted-foreground/80 mb-0.5">
{textRight}
</span>
)}
</ContextMenuPrimitive.Item>
);
},
);
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
const ContextMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem> & {
variant?: VariantProps<typeof contextMenuItemVariants>["variant"];
icon?: React.ReactNode;
}
>(
(
{ className, children, checked, variant = "default", icon, ...props },
ref,
) => (
<ContextMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
contextMenuItemVariants({ variant }),
"pr-2 pl-8",
className,
)}
checked={checked}
{...props}
>
<span className="absolute left-3 flex size-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<HugeiconsIcon icon={Tick02Icon} className="size-4" />
</ContextMenuPrimitive.ItemIndicator>
</span>
{icon && (
<span className="size-4 shrink-0 text-muted-foreground">{icon}</span>
)}
{children}
</ContextMenuPrimitive.CheckboxItem>
),
);
ContextMenuCheckboxItem.displayName =
ContextMenuPrimitive.CheckboxItem.displayName;
const ContextMenuRadioItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem> & {
variant?: VariantProps<typeof contextMenuItemVariants>["variant"];
icon?: React.ReactNode;
}
>(({ className, children, variant = "default", icon, ...props }, ref) => (
<ContextMenuPrimitive.RadioItem
ref={ref}
className={cn(contextMenuItemVariants({ variant }), "pr-2 pl-8", className)}
{...props}
>
<span className="absolute left-2 flex size-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<HugeiconsIcon icon={CircleIcon} className="size-2 fill-current" />
</ContextMenuPrimitive.ItemIndicator>
</span>
{icon && (
<span className="size-4 shrink-0 text-muted-foreground">{icon}</span>
)}
{children}
</ContextMenuPrimitive.RadioItem>
));
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
const ContextMenuLabel = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
inset?: boolean;
icon?: React.ReactNode;
}
>(({ className, inset, icon, children, ...props }, ref) => (
<ContextMenuPrimitive.Label
ref={ref}
className={cn(
"flex items-center gap-2 px-3 py-1.5 text-sm font-semibold text-foreground",
inset && "pl-8",
className,
)}
{...props}
>
{icon && (
<span className="size-4 shrink-0 text-muted-foreground">{icon}</span>
)}
{children}
</ContextMenuPrimitive.Label>
));
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
const ContextMenuSeparator = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Separator
ref={ref}
className={cn("bg-border mx-1 my-1.5 h-px", className)}
{...props}
/>
));
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
const ContextMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground opacity-60",
className,
)}
{...props}
/>
);
};
ContextMenuShortcut.displayName = "ContextMenuShortcut";
export {
ContextMenu,
ContextMenuTrigger,
ContextMenuContent,
ContextMenuItem,
ContextMenuCheckboxItem,
ContextMenuRadioItem,
ContextMenuLabel,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuGroup,
ContextMenuPortal,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuRadioGroup,
};
"use client";
import * as React from "react";
import { ContextMenu as ContextMenuPrimitive } from "radix-ui";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/utils/ui";
import { HugeiconsIcon } from "@hugeicons/react";
import {
Tick02Icon,
ArrowRightIcon,
CircleIcon,
} from "@hugeicons/core-free-icons";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function ContextMenu({
onOpenChange,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
onOpenChange,
});
return (
<ContextMenuPrimitive.Root onOpenChange={handleOpenChange} {...props} />
);
}
const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
const ContextMenuGroup = ContextMenuPrimitive.Group;
const ContextMenuPortal = ContextMenuPrimitive.Portal;
const ContextMenuSub = ContextMenuPrimitive.Sub;
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
const contextMenuItemVariants = cva(
"relative flex cursor-pointer select-none items-center gap-2 rounded-sm px-3 py-1.5 text-sm text-foreground/85 outline-hidden data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-3.5 [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"focus:bg-accent focus:text-accent-foreground [&_svg]:text-muted-foreground",
destructive:
"text-destructive focus:bg-destructive/10 focus:text-destructive [&_svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
},
);
const ContextMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubTrigger> & {
inset?: boolean;
variant?: VariantProps<typeof contextMenuItemVariants>["variant"];
icon?: React.ReactNode;
}
>(
(
{ className, inset, children, variant = "default", icon, ...props },
ref,
) => (
<ContextMenuPrimitive.SubTrigger
ref={ref}
className={cn(
contextMenuItemVariants({ variant }),
"data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
inset && "pl-8",
className,
)}
{...props}
>
{icon && (
<span className="size-4 shrink-0 text-muted-foreground">{icon}</span>
)}
{children}
<HugeiconsIcon
icon={ArrowRightIcon}
className="ml-auto text-muted-foreground/80"
/>
</ContextMenuPrimitive.SubTrigger>
),
);
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
const ContextMenuSubContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.SubContent
ref={ref}
className={cn(
"bg-popover text-popover-foreground z-50 min-w-48 overflow-hidden rounded-md border shadow-xl p-1",
className,
)}
{...props}
/>
));
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
const ContextMenuContent = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Content> & {
container?: HTMLElement | null;
}
>(({ className, container, ...props }, ref) => (
<ContextMenuPrimitive.Portal container={container ?? undefined}>
<ContextMenuPrimitive.Content
ref={ref}
className={cn(
"bg-popover text-popover-foreground z-50 min-w-48 overflow-hidden rounded-md border shadow-xl p-1",
className,
)}
{...props}
/>
</ContextMenuPrimitive.Portal>
));
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
const ContextMenuItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Item> & {
inset?: boolean;
variant?: VariantProps<typeof contextMenuItemVariants>["variant"];
icon?: React.ReactNode;
textRight?: string;
}
>(
(
{
className,
inset,
variant = "default",
icon,
children,
textRight,
...props
},
ref,
) => {
const shouldInsetContent = inset || Boolean(icon);
return (
<ContextMenuPrimitive.Item
ref={ref}
className={cn(
contextMenuItemVariants({ variant }),
shouldInsetContent && "pl-8",
className,
)}
{...props}
>
{icon && (
<span className="absolute left-3 flex size-3.5 items-center justify-center text-muted-foreground [&_svg]:size-3.5 [&_svg]:shrink-0">
{icon}
</span>
)}
{children}
{textRight && (
<span className="ml-auto text-[0.60rem] tracking-widest text-muted-foreground/80 mb-0.5">
{textRight}
</span>
)}
</ContextMenuPrimitive.Item>
);
},
);
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
const ContextMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.CheckboxItem> & {
variant?: VariantProps<typeof contextMenuItemVariants>["variant"];
icon?: React.ReactNode;
}
>(
(
{ className, children, checked, variant = "default", icon, ...props },
ref,
) => (
<ContextMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
contextMenuItemVariants({ variant }),
"pr-2 pl-8",
className,
)}
checked={checked}
{...props}
>
<span className="absolute left-3 flex size-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<HugeiconsIcon icon={Tick02Icon} className="size-4" />
</ContextMenuPrimitive.ItemIndicator>
</span>
{icon && (
<span className="size-4 shrink-0 text-muted-foreground">{icon}</span>
)}
{children}
</ContextMenuPrimitive.CheckboxItem>
),
);
ContextMenuCheckboxItem.displayName =
ContextMenuPrimitive.CheckboxItem.displayName;
const ContextMenuRadioItem = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.RadioItem> & {
variant?: VariantProps<typeof contextMenuItemVariants>["variant"];
icon?: React.ReactNode;
}
>(({ className, children, variant = "default", icon, ...props }, ref) => (
<ContextMenuPrimitive.RadioItem
ref={ref}
className={cn(contextMenuItemVariants({ variant }), "pr-2 pl-8", className)}
{...props}
>
<span className="absolute left-2 flex size-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<HugeiconsIcon icon={CircleIcon} className="size-2 fill-current" />
</ContextMenuPrimitive.ItemIndicator>
</span>
{icon && (
<span className="size-4 shrink-0 text-muted-foreground">{icon}</span>
)}
{children}
</ContextMenuPrimitive.RadioItem>
));
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
const ContextMenuLabel = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Label> & {
inset?: boolean;
icon?: React.ReactNode;
}
>(({ className, inset, icon, children, ...props }, ref) => (
<ContextMenuPrimitive.Label
ref={ref}
className={cn(
"flex items-center gap-2 px-3 py-1.5 text-sm font-semibold text-foreground",
inset && "pl-8",
className,
)}
{...props}
>
{icon && (
<span className="size-4 shrink-0 text-muted-foreground">{icon}</span>
)}
{children}
</ContextMenuPrimitive.Label>
));
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
const ContextMenuSeparator = React.forwardRef<
React.ElementRef<typeof ContextMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof ContextMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<ContextMenuPrimitive.Separator
ref={ref}
className={cn("bg-border mx-1 my-1.5 h-px", className)}
{...props}
/>
));
ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
const ContextMenuShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground opacity-60",
className,
)}
{...props}
/>
);
};
ContextMenuShortcut.displayName = "ContextMenuShortcut";
export {
ContextMenu,
ContextMenuTrigger,
ContextMenuContent,
ContextMenuItem,
ContextMenuCheckboxItem,
ContextMenuRadioItem,
ContextMenuLabel,
ContextMenuSeparator,
ContextMenuShortcut,
ContextMenuGroup,
ContextMenuPortal,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuRadioGroup,
};

View File

@ -1,145 +1,148 @@
"use client";
import * as React from "react";
import { Dialog as DialogPrimitive } from "radix-ui";
import { X } from "lucide-react";
import { cn } from "@/utils/ui";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function Dialog({
open,
onOpenChange,
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
source: "dialog",
open,
onOpenChange,
});
return (
<DialogPrimitive.Root open={open} onOpenChange={handleOpenChange} {...props} />
);
}
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-250 backdrop-blur-sm bg-black/10",
className,
)}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"bg-popover fixed top-[50%] left-[50%] z-250 grid w-[calc(100%-2rem)] max-w-lg translate-x-[-50%] translate-y-[-50%] rounded-lg border shadow-lg duration-200",
className,
)}
onCloseAutoFocus={(e) => {
e.stopPropagation();
e.preventDefault();
}}
{...props}
>
{children}
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-6 right-6 cursor-pointer opacity-70 hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<X className="size-5 text-muted-foreground" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn("flex flex-col space-y-2 text-left border-b p-6", className)}
{...props}
/>
);
DialogHeader.displayName = "DialogHeader";
const DialogBody = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("p-6 flex flex-col gap-4", className)} {...props} />
);
DialogBody.displayName = "DialogBody";
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex gap-3 flex-col-reverse sm:flex-row sm:justify-end p-6 py-5 border-t",
className,
)}
{...props}
/>
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg leading-none font-semibold tracking-tight",
className,
)}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogBody,
DialogFooter,
DialogTitle,
DialogDescription,
};
"use client";
import * as React from "react";
import { Dialog as DialogPrimitive } from "radix-ui";
import { X } from "lucide-react";
import { cn } from "@/utils/ui";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function Dialog({
open,
onOpenChange,
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
open,
onOpenChange,
});
return (
<DialogPrimitive.Root
open={open}
onOpenChange={handleOpenChange}
{...props}
/>
);
}
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-250 backdrop-blur-sm bg-black/10",
className,
)}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"bg-popover fixed top-[50%] left-[50%] z-250 grid w-[calc(100%-2rem)] max-w-lg translate-x-[-50%] translate-y-[-50%] rounded-lg border shadow-lg duration-200",
className,
)}
onCloseAutoFocus={(e) => {
e.stopPropagation();
e.preventDefault();
}}
{...props}
>
{children}
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-6 right-6 cursor-pointer opacity-70 hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<X className="size-5 text-muted-foreground" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn("flex flex-col space-y-2 text-left border-b p-6", className)}
{...props}
/>
);
DialogHeader.displayName = "DialogHeader";
const DialogBody = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("p-6 flex flex-col gap-4", className)} {...props} />
);
DialogBody.displayName = "DialogBody";
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex gap-3 flex-col-reverse sm:flex-row sm:justify-end p-6 py-5 border-t",
className,
)}
{...props}
/>
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg leading-none font-semibold tracking-tight",
className,
)}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogBody,
DialogFooter,
DialogTitle,
DialogDescription,
};

View File

@ -13,7 +13,6 @@ function DropdownMenu({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
source: "dropdown-menu",
open,
onOpenChange,
});
@ -105,12 +104,12 @@ const DropdownMenuContent = React.forwardRef<
e.preventDefault();
}}
className={cn(
"group/menu bg-popover text-popover-foreground z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg",
className,
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
"group/menu bg-popover text-popover-foreground z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg",
className,
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;

View File

@ -1,52 +1,51 @@
"use client";
import * as React from "react";
import { Popover as PopoverPrimitive } from "radix-ui";
import { cn } from "@/utils/ui";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function Popover({
open,
onOpenChange,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
source: "popover",
open,
onOpenChange,
});
return (
<PopoverPrimitive.Root
open={open}
onOpenChange={handleOpenChange}
{...props}
/>
);
}
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverAnchor = PopoverPrimitive.Anchor;
const PopoverClose = PopoverPrimitive.Close;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground z-50 w-72 rounded-md border p-4 shadow-[0_0_10px_rgba(0,0,0,0.15)] outline-hidden",
className,
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverClose };
"use client";
import * as React from "react";
import { Popover as PopoverPrimitive } from "radix-ui";
import { cn } from "@/utils/ui";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function Popover({
open,
onOpenChange,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
open,
onOpenChange,
});
return (
<PopoverPrimitive.Root
open={open}
onOpenChange={handleOpenChange}
{...props}
/>
);
}
const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverAnchor = PopoverPrimitive.Anchor;
const PopoverClose = PopoverPrimitive.Close;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground z-50 w-72 rounded-md border p-4 shadow-[0_0_10px_rgba(0,0,0,0.15)] outline-hidden",
className,
)}
{...props}
/>
</PopoverPrimitive.Portal>
));
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverClose };

View File

@ -1,221 +1,224 @@
"use client";
import * as React from "react";
import { Select as SelectPrimitive } from "radix-ui";
import { Check } from "lucide-react";
import { ArrowUpIcon, ArrowDownIcon } from "@hugeicons/core-free-icons";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/utils/ui";
import { HugeiconsIcon } from "@hugeicons/react";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function Select({
open,
onOpenChange,
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
source: "select",
open,
onOpenChange,
});
return (
<SelectPrimitive.Root open={open} onOpenChange={handleOpenChange} {...props} />
);
}
const SelectGroup = SelectPrimitive.Group;
const SelectValue = SelectPrimitive.Value;
const selectItemVariants = cva(
"relative flex cursor-pointer select-none items-center gap-1.5 rounded-sm px-2 py-1 text-sm text-foreground/85 outline-hidden data-[highlighted]:bg-popover-hover data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
{
variants: {
variant: {
default: "",
destructive:
"text-destructive data-[highlighted]:bg-destructive/5 data-[highlighted]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
},
);
const selectTriggerVariants = cva(
"border-border ring-offset-background placeholder:text-muted-foreground flex h-7 w-auto cursor-pointer items-center justify-between gap-1 rounded-md border px-2.5 text-sm whitespace-nowrap transition-none focus:border-primary focus:ring-0 focus:ring-primary/10 focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
{
variants: {
variant: {
default: "bg-accent",
outline: "bg-background hover:bg-accent/50",
},
size: {
default: "",
sm: "rounded-sm",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> &
VariantProps<typeof selectTriggerVariants> & {
icon?: React.ReactNode;
}
>(({ className, children, icon, variant, size, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(selectTriggerVariants({ variant, size }), className)}
{...props}
>
<div className="flex items-center gap-1.5">
{icon && (
<span className="text-muted-foreground [&_svg]:size-3.5 shrink-0">
{icon}
</span>
)}
{children}
</div>
<SelectPrimitive.Icon asChild>
<HugeiconsIcon icon={ArrowDownIcon} className="size-4" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
)}
{...props}
>
<HugeiconsIcon icon={ArrowUpIcon} className="size-4" />
</SelectPrimitive.ScrollUpButton>
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
)}
{...props}
>
<HugeiconsIcon icon={ArrowDownIcon} className="size-4" />
</SelectPrimitive.ScrollDownButton>
));
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName;
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"bg-popover text-popover-foreground z-50 max-h-(--radix-select-content-available-height) min-w-32 overflow-hidden rounded-md border p-1 shadow-lg",
className,
)}
position={position}
onCloseAutoFocus={(e) => {
e.preventDefault();
e.stopPropagation();
}}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
position === "popper" &&
"h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)",
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn(
"px-2 pb-1 pt-0.5 text-[11px] font-bold uppercase tracking-wider text-muted-foreground",
className,
)}
{...props}
/>
));
SelectLabel.displayName = SelectPrimitive.Label.displayName;
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> & {
variant?: VariantProps<typeof selectItemVariants>["variant"];
}
>(({ className, children, variant = "default", ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(selectItemVariants({ variant }), "pl-6 pr-2", className)}
{...props}
>
<span className="absolute left-1.5 flex size-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="size-3.5" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("bg-border mx-1 my-1 h-px", className)}
{...props}
/>
));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
};
"use client";
import * as React from "react";
import { Select as SelectPrimitive } from "radix-ui";
import { Check } from "lucide-react";
import { ArrowUpIcon, ArrowDownIcon } from "@hugeicons/core-free-icons";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/utils/ui";
import { HugeiconsIcon } from "@hugeicons/react";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function Select({
open,
onOpenChange,
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
open,
onOpenChange,
});
return (
<SelectPrimitive.Root
open={open}
onOpenChange={handleOpenChange}
{...props}
/>
);
}
const SelectGroup = SelectPrimitive.Group;
const SelectValue = SelectPrimitive.Value;
const selectItemVariants = cva(
"relative flex cursor-pointer select-none items-center gap-1.5 rounded-sm px-2 py-1 text-sm text-foreground/85 outline-hidden data-[highlighted]:bg-popover-hover data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
{
variants: {
variant: {
default: "",
destructive:
"text-destructive data-[highlighted]:bg-destructive/5 data-[highlighted]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
},
);
const selectTriggerVariants = cva(
"border-border ring-offset-background placeholder:text-muted-foreground flex h-7 w-auto cursor-pointer items-center justify-between gap-1 rounded-md border px-2.5 text-sm whitespace-nowrap transition-none focus:border-primary focus:ring-0 focus:ring-primary/10 focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
{
variants: {
variant: {
default: "bg-accent",
outline: "bg-background hover:bg-accent/50",
},
size: {
default: "",
sm: "rounded-sm",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> &
VariantProps<typeof selectTriggerVariants> & {
icon?: React.ReactNode;
}
>(({ className, children, icon, variant, size, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(selectTriggerVariants({ variant, size }), className)}
{...props}
>
<div className="flex items-center gap-1.5">
{icon && (
<span className="text-muted-foreground [&_svg]:size-3.5 shrink-0">
{icon}
</span>
)}
{children}
</div>
<SelectPrimitive.Icon asChild>
<HugeiconsIcon icon={ArrowDownIcon} className="size-4" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
)}
{...props}
>
<HugeiconsIcon icon={ArrowUpIcon} className="size-4" />
</SelectPrimitive.ScrollUpButton>
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
)}
{...props}
>
<HugeiconsIcon icon={ArrowDownIcon} className="size-4" />
</SelectPrimitive.ScrollDownButton>
));
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName;
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"bg-popover text-popover-foreground z-50 max-h-(--radix-select-content-available-height) min-w-32 overflow-hidden rounded-md border p-1 shadow-lg",
className,
)}
position={position}
onCloseAutoFocus={(e) => {
e.preventDefault();
e.stopPropagation();
}}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
position === "popper" &&
"h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)",
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn(
"px-2 pb-1 pt-0.5 text-[11px] font-bold uppercase tracking-wider text-muted-foreground",
className,
)}
{...props}
/>
));
SelectLabel.displayName = SelectPrimitive.Label.displayName;
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> & {
variant?: VariantProps<typeof selectItemVariants>["variant"];
}
>(({ className, children, variant = "default", ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(selectItemVariants({ variant }), "pl-6 pr-2", className)}
{...props}
>
<span className="absolute left-1.5 flex size-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="size-3.5" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("bg-border mx-1 my-1 h-px", className)}
{...props}
/>
));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
};

View File

@ -1,157 +1,160 @@
"use client";
import * as React from "react";
import { Dialog as SheetPrimitive } from "radix-ui";
import { cva, type VariantProps } from "class-variance-authority";
import { X } from "lucide-react";
import { cn } from "@/utils/ui";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function Sheet({
open,
onOpenChange,
...props
}: React.ComponentProps<typeof SheetPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
source: "sheet",
open,
onOpenChange,
});
return (
<SheetPrimitive.Root open={open} onOpenChange={handleOpenChange} {...props} />
);
}
const SheetTrigger = SheetPrimitive.Trigger;
const SheetClose = SheetPrimitive.Close;
const SheetPortal = SheetPrimitive.Portal;
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-200 bg-black/50",
className,
)}
{...props}
ref={ref}
/>
));
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva(
"fixed z-250 gap-4 bg-background p-6 shadow-lg transition ease data-[state=closed]:duration-250 data-[state=open]:duration-250 data-[state=open]:animate-in data-[state=closed]:animate-out",
{
variants: {
side: {
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
},
);
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
onOpenAutoFocus={(e) => {
e.preventDefault();
e.stopPropagation();
}}
{...props}
>
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 cursor-pointer rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<X className="size-5" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
{children}
</SheetPrimitive.Content>
</SheetPortal>
));
SheetContent.displayName = SheetPrimitive.Content.displayName;
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className,
)}
{...props}
/>
);
SheetHeader.displayName = "SheetHeader";
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-4",
className,
)}
{...props}
/>
);
SheetFooter.displayName = "SheetFooter";
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn("text-foreground text-lg font-semibold", className)}
{...props}
/>
));
SheetTitle.displayName = SheetPrimitive.Title.displayName;
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
));
SheetDescription.displayName = SheetPrimitive.Description.displayName;
export {
Sheet,
SheetPortal,
SheetOverlay,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
};
"use client";
import * as React from "react";
import { Dialog as SheetPrimitive } from "radix-ui";
import { cva, type VariantProps } from "class-variance-authority";
import { X } from "lucide-react";
import { cn } from "@/utils/ui";
import { useOverlayOpenChange } from "./use-overlay-open-change";
function Sheet({
open,
onOpenChange,
...props
}: React.ComponentProps<typeof SheetPrimitive.Root>) {
const handleOpenChange = useOverlayOpenChange({
open,
onOpenChange,
});
return (
<SheetPrimitive.Root
open={open}
onOpenChange={handleOpenChange}
{...props}
/>
);
}
const SheetTrigger = SheetPrimitive.Trigger;
const SheetClose = SheetPrimitive.Close;
const SheetPortal = SheetPrimitive.Portal;
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-200 bg-black/50",
className,
)}
{...props}
ref={ref}
/>
));
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva(
"fixed z-250 gap-4 bg-background p-6 shadow-lg transition ease data-[state=closed]:duration-250 data-[state=open]:duration-250 data-[state=open]:animate-in data-[state=closed]:animate-out",
{
variants: {
side: {
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
},
);
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
onOpenAutoFocus={(e) => {
e.preventDefault();
e.stopPropagation();
}}
{...props}
>
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 cursor-pointer rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<X className="size-5" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
{children}
</SheetPrimitive.Content>
</SheetPortal>
));
SheetContent.displayName = SheetPrimitive.Content.displayName;
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className,
)}
{...props}
/>
);
SheetHeader.displayName = "SheetHeader";
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-4",
className,
)}
{...props}
/>
);
SheetFooter.displayName = "SheetFooter";
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn("text-foreground text-lg font-semibold", className)}
{...props}
/>
));
SheetTitle.displayName = SheetPrimitive.Title.displayName;
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
));
SheetDescription.displayName = SheetPrimitive.Description.displayName;
export {
Sheet,
SheetPortal,
SheetOverlay,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
};

View File

@ -1,78 +1,55 @@
import { useCallback, useEffect, useId, useRef } from "react";
import { useKeybindingsStore } from "@/stores/keybindings-store";
export function useOverlayOpenChange({
source,
open,
onOpenChange,
}: {
source: string;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}) {
const { openOverlay, closeOverlay } = useKeybindingsStore();
const isTrackedRef = useRef(false);
const isControlled = typeof open === "boolean";
const overlayId = useId();
useEffect(() => {
if (!isControlled) return;
if (open && !isTrackedRef.current) {
openOverlay(overlayId, source);
isTrackedRef.current = true;
return;
}
if (!open && isTrackedRef.current) {
closeOverlay(overlayId, source);
isTrackedRef.current = false;
}
}, [closeOverlay, isControlled, open, openOverlay, overlayId, source]);
useEffect(() => {
return () => {
if (!isTrackedRef.current) return;
// #region agent log
fetch(
"http://127.0.0.1:7245/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Debug-Session-Id": "3997d9",
},
body: JSON.stringify({
sessionId: "3997d9",
runId: "post-fix",
hypothesisId: "H2",
location: "use-overlay-open-change.ts:cleanup",
message: "Overlay closed during unmount cleanup",
data: { source, overlayId },
timestamp: Date.now(),
}),
},
).catch(() => {});
// #endregion
closeOverlay(overlayId, source);
isTrackedRef.current = false;
};
}, [closeOverlay, overlayId, source]);
return useCallback(
(nextOpen: boolean) => {
if (!isControlled) {
if (nextOpen && !isTrackedRef.current) {
openOverlay(overlayId, source);
isTrackedRef.current = true;
} else if (!nextOpen && isTrackedRef.current) {
closeOverlay(overlayId, source);
isTrackedRef.current = false;
}
}
onOpenChange?.(nextOpen);
},
[closeOverlay, isControlled, onOpenChange, openOverlay, overlayId, source],
);
}
import { useCallback, useEffect, useId, useRef } from "react";
import { useKeybindingsStore } from "@/stores/keybindings-store";
export function useOverlayOpenChange({
open,
onOpenChange,
}: {
open?: boolean;
onOpenChange?: (open: boolean) => void;
}) {
const { openOverlay, closeOverlay } = useKeybindingsStore();
const isTrackedRef = useRef(false);
const isControlled = typeof open === "boolean";
const overlayId = useId();
useEffect(() => {
if (!isControlled) return;
if (open && !isTrackedRef.current) {
openOverlay(overlayId);
isTrackedRef.current = true;
return;
}
if (!open && isTrackedRef.current) {
closeOverlay(overlayId);
isTrackedRef.current = false;
}
}, [closeOverlay, isControlled, open, openOverlay, overlayId]);
useEffect(() => {
return () => {
if (!isTrackedRef.current) return;
closeOverlay(overlayId);
isTrackedRef.current = false;
};
}, [closeOverlay, overlayId]);
return useCallback(
(nextOpen: boolean) => {
if (!isControlled) {
if (nextOpen && !isTrackedRef.current) {
openOverlay(overlayId);
isTrackedRef.current = true;
} else if (!nextOpen && isTrackedRef.current) {
closeOverlay(overlayId);
isTrackedRef.current = false;
}
}
onOpenChange?.(nextOpen);
},
[closeOverlay, isControlled, onOpenChange, openOverlay, overlayId],
);
}

View File

@ -528,12 +528,10 @@ export class ProjectManager {
}
async prepareExit(): Promise<void> {
console.log("prepareExit", this.active);
if (!this.active) return;
try {
const didUpdateThumbnail = await this.updateThumbnailFromTimeline();
console.log("didUpdateThumbnail", didUpdateThumbnail);
if (didUpdateThumbnail) {
await this.editor.save.flush();
}

View File

@ -21,68 +21,10 @@ export function useKeybindingsListener() {
useEffect(() => {
const eventOptions: AddEventListenerOptions = { capture: true };
// #region agent log
fetch("http://127.0.0.1:7245/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Debug-Session-Id": "3997d9",
},
body: JSON.stringify({
sessionId: "3997d9",
runId: "initial",
hypothesisId: "H1",
location: "use-keybindings.ts:effect",
message: "Keybindings listener mounted",
data: {
overlayDepth,
isLoadingProject,
isRecording,
keybindingCount: Object.keys(keybindings).length,
},
timestamp: Date.now(),
}),
}).catch(() => {});
// #endregion
const handleKeyDown = (ev: KeyboardEvent) => {
const normalizedKey = (ev.key ?? "").toLowerCase();
const shouldLogKey =
ev.code === "Space" ||
ev.code.startsWith("Key") ||
["escape", "delete", "backspace", "enter"].includes(normalizedKey);
if (overlayDepth > 0 || isLoadingProject || isRecording) {
if (shouldLogKey) {
// #region agent log
fetch(
"http://127.0.0.1:7245/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Debug-Session-Id": "3997d9",
},
body: JSON.stringify({
sessionId: "3997d9",
runId: "initial",
hypothesisId: "H2",
location: "use-keybindings.ts:blocked",
message: "Shortcut blocked by runtime gate",
data: {
key: ev.key,
code: ev.code,
overlayDepth,
isLoadingProject,
isRecording,
targetTag:
ev.target instanceof HTMLElement ? ev.target.tagName : null,
},
timestamp: Date.now(),
}),
},
).catch(() => {});
// #endregion
}
return;
}
@ -93,43 +35,6 @@ export function useKeybindingsListener() {
isTypableDOMElement({ element: activeElement });
const boundAction = binding ? keybindings[binding] : undefined;
if (shouldLogKey || binding || boundAction) {
// #region agent log
fetch(
"http://127.0.0.1:7245/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Debug-Session-Id": "3997d9",
},
body: JSON.stringify({
sessionId: "3997d9",
runId: "initial",
hypothesisId: !binding ? "H3" : isTextInput ? "H5" : "H4",
location: "use-keybindings.ts:keydown",
message: "Shortcut keydown observed",
data: {
key: ev.key,
code: ev.code,
binding,
boundAction: boundAction ?? null,
isTextInput,
keybindingCount: Object.keys(keybindings).length,
activeTag:
activeElement instanceof HTMLElement
? activeElement.tagName
: null,
targetTag:
ev.target instanceof HTMLElement ? ev.target.tagName : null,
},
timestamp: Date.now(),
}),
},
).catch(() => {});
// #endregion
}
if (normalizedKey === "escape" && isTextInput) {
activeElement.blur();
return;

View File

@ -57,29 +57,6 @@ export const invokeAction: InvokeActionFunc = <A extends TAction>(
args?: TArgOfAction<A>,
trigger?: TInvocationTrigger,
) => {
if (trigger === "keypress") {
// #region agent log
fetch("http://127.0.0.1:7245/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Debug-Session-Id": "3997d9",
},
body: JSON.stringify({
sessionId: "3997d9",
runId: "initial",
hypothesisId: "H4",
location: "actions/registry.ts:invokeAction",
message: "Action invoked from keypress",
data: {
action,
handlerCount: boundActions[action]?.length ?? 0,
},
timestamp: Date.now(),
}),
}).catch(() => {});
// #endregion
}
boundActions[action]?.forEach((handler) => {
handler(args, trigger);
});

View File

@ -52,7 +52,6 @@ class WasmCompositor {
string,
{ source: CanvasImageSource; width: number; height: number }
>();
private _debugLogged = false;
ensureInitialized({ width, height }: { width: number; height: number }) {
if (!this.canvas) {
@ -121,20 +120,6 @@ class WasmCompositor {
}
render(frame: FrameDescriptor) {
if (!this._debugLogged) {
this._debugLogged = true;
const firstLayer = frame.items.find((item) => item.type === "layer");
console.log(
"[compositor] first frame — canvas size:",
JSON.stringify({ width: frame.width, height: frame.height }),
"| first layer transform:",
firstLayer && firstLayer.type === "layer"
? JSON.stringify(firstLayer.transform)
: "none",
"| webgpu available:",
typeof navigator !== "undefined" && "gpu" in navigator,
);
}
renderFrame(frame);
}
}

View File

@ -13,18 +13,11 @@ export function initializeGpuRenderer(): Promise<void> {
initPromise = initializeGpu()
.then(() => {
gpuAvailable = true;
// #region agent log
fetch('http://127.0.0.1:7408/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'b140a6'},body:JSON.stringify({sessionId:'b140a6',location:'gpu-renderer.ts',message:'GPU init SUCCESS',data:{userAgent:navigator.userAgent},timestamp:Date.now()})}).catch(()=>{});
// #endregion
})
.catch((error: unknown) => {
gpuAvailable = false;
const message =
error instanceof Error ? error.message : String(error);
const message = error instanceof Error ? error.message : String(error);
console.warn(`GPU renderer unavailable: ${message}`);
// #region agent log
fetch('http://127.0.0.1:7408/ingest/669b22f8-172b-4e65-aa3f-1c702ede83f7',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'b140a6'},body:JSON.stringify({sessionId:'b140a6',location:'gpu-renderer.ts',message:'GPU init FAILED',data:{error:message,userAgent:navigator.userAgent,hasGpu:!!navigator.gpu},timestamp:Date.now()})}).catch(()=>{});
// #endregion
});
}
return initPromise;