路由嵌套修改

This commit is contained in:
fengxiangdebaozi committed 2026-05-14 19:25:39 +08:00
1 parent 8ecc32da6d
commit d6ec0afbd6
16 files changed
+836 -759

No files matched your search

+19 -17
View File
@@ -31,23 +31,25 @@ function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
); );
} }
function BreadcrumbLink({ const BreadcrumbLink = React.forwardRef<
asChild, HTMLAnchorElement,
className, React.ComponentProps<"a"> & {
...props asChild?: boolean;
}: React.ComponentProps<"a"> & { }
asChild?: boolean; >(({ asChild, className, ...props }, ref) => {
}) { const Comp = asChild ? Slot : "a";
const Comp = asChild ? Slot : "a";
return (
return ( <Comp
<Comp ref={ref}
data-slot="breadcrumb-link" data-slot="breadcrumb-link"
className={cn("hover:text-foreground transition-colors", className)} className={cn("hover:text-foreground transition-colors", className)}
{...props} {...props}
/> />
); );
} });
BreadcrumbLink.displayName = "BreadcrumbLink";
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) { function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
return ( return (
+27 -27
View File
@@ -1,6 +1,6 @@
import * as React from "react"; import * as React from "react";
import { Slot } from "@radix-ui/react-slot"; import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority"; import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@@ -37,27 +37,27 @@ const buttonVariants = cva(
}, },
); );
function Button({ const Button = React.forwardRef<
className, HTMLButtonElement,
variant = "default", React.ComponentProps<"button"> &
size = "default", VariantProps<typeof buttonVariants> & {
asChild = false, asChild?: boolean;
...props }
}: React.ComponentProps<"button"> & >(({ className, variant = "default", size = "default", asChild = false, ...props }, ref) => {
VariantProps<typeof buttonVariants> & { const Comp = asChild ? Slot : "button";
asChild?: boolean;
}) { return (
const Comp = asChild ? Slot : "button"; <Comp
ref={ref}
return ( data-slot="button"
<Comp {...(variant !== undefined && { "data-variant": variant })}
data-slot="button" {...(size !== undefined && { "data-size": size })}
{...(variant !== undefined && { "data-variant": variant })} className={cn(buttonVariants({ variant, size, className }))}
{...(size !== undefined && { "data-size": size })} {...props}
className={cn(buttonVariants({ variant, size, className }))} />
{...props} );
/> });
);
} Button.displayName = "Button";
export { Button, buttonVariants }; export { Button, buttonVariants };
+36 -26
View File
@@ -1,6 +1,7 @@
"use client"; "use client";
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; import * as React from "react";
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@@ -10,28 +11,37 @@ function Collapsible({
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />; return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
} }
function CollapsibleTrigger({ const CollapsibleTrigger = React.forwardRef<
className, React.ElementRef<typeof CollapsiblePrimitive.CollapsibleTrigger>,
...props React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.CollapsibleTrigger>
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) { >(({ className, ...props }, ref) => {
return ( return (
<CollapsiblePrimitive.CollapsibleTrigger <CollapsiblePrimitive.CollapsibleTrigger
data-slot="collapsible-trigger" ref={ref}
className={cn("cursor-pointer", className)} data-slot="collapsible-trigger"
{...props} className={cn("cursor-pointer", className)}
/> {...props}
); />
} );
});
function CollapsibleContent({
...props CollapsibleTrigger.displayName =
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) { CollapsiblePrimitive.CollapsibleTrigger.displayName;
return (
<CollapsiblePrimitive.CollapsibleContent const CollapsibleContent = React.forwardRef<
data-slot="collapsible-content" React.ElementRef<typeof CollapsiblePrimitive.CollapsibleContent>,
{...props} React.ComponentPropsWithoutRef<typeof CollapsiblePrimitive.CollapsibleContent>
/> >(({ ...props }, ref) => {
); return (
} <CollapsiblePrimitive.CollapsibleContent
ref={ref}
data-slot="collapsible-content"
{...props}
/>
);
});
CollapsibleContent.displayName =
CollapsiblePrimitive.CollapsibleContent.displayName;
export { Collapsible, CollapsibleTrigger, CollapsibleContent }; export { Collapsible, CollapsibleTrigger, CollapsibleContent };
+74 -58
View File
@@ -12,11 +12,20 @@ function Dialog({
return <DialogPrimitive.Root data-slot="dialog" {...props} />; return <DialogPrimitive.Root data-slot="dialog" {...props} />;
} }
function DialogTrigger({ const DialogTrigger = React.forwardRef<
...props React.ElementRef<typeof DialogPrimitive.Trigger>,
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) { React.ComponentPropsWithoutRef<typeof DialogPrimitive.Trigger>
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />; >(({ ...props }, ref) => {
} return (
<DialogPrimitive.Trigger
ref={ref}
data-slot="dialog-trigger"
{...props}
/>
);
});
DialogTrigger.displayName = DialogPrimitive.Trigger.displayName;
function DialogPortal({ function DialogPortal({
...props ...props
@@ -24,61 +33,68 @@ function DialogPortal({
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />; return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
} }
function DialogClose({ const DialogClose = React.forwardRef<
...props React.ElementRef<typeof DialogPrimitive.Close>,
}: React.ComponentProps<typeof DialogPrimitive.Close>) { React.ComponentPropsWithoutRef<typeof DialogPrimitive.Close>
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />; >(({ ...props }, ref) => {
} return <DialogPrimitive.Close ref={ref} data-slot="dialog-close" {...props} />;
});
DialogClose.displayName = DialogPrimitive.Close.displayName;
function DialogOverlay({ const DialogOverlay = React.forwardRef<
className, React.ElementRef<typeof DialogPrimitive.Overlay>,
...props React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) { >(({ className, ...props }, ref) => {
return ( return (
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
data-slot="dialog-overlay" ref={ref}
className={cn( data-slot="dialog-overlay"
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", className={cn(
className, "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
)} className,
{...props} )}
/> {...props}
); />
} );
});
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
function DialogContent({ const DialogContent = React.forwardRef<
className, React.ElementRef<typeof DialogPrimitive.Content>,
children, React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
showCloseButton = true, showCloseButton?: boolean;
...props }
}: React.ComponentProps<typeof DialogPrimitive.Content> & { >(({ className, children, showCloseButton = true, ...props }, ref) => {
showCloseButton?: boolean; return (
}) { <DialogPortal data-slot="dialog-portal">
return ( <DialogOverlay />
<DialogPortal data-slot="dialog-portal"> <DialogPrimitive.Content
<DialogOverlay /> ref={ref}
<DialogPrimitive.Content data-slot="dialog-content"
data-slot="dialog-content" className={cn(
className={cn( "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg", className,
className, )}
)} {...props}
{...props} >
> {children}
{children} {showCloseButton && (
{showCloseButton && ( <DialogPrimitive.Close
<DialogPrimitive.Close data-slot="dialog-close"
data-slot="dialog-close" className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" >
> <XIcon />
<XIcon /> <span className="sr-only">Close</span>
<span className="sr-only">Close</span> </DialogPrimitive.Close>
</DialogPrimitive.Close> )}
)} </DialogPrimitive.Content>
</DialogPrimitive.Content> </DialogPortal>
</DialogPortal> );
); });
}
DialogContent.displayName = DialogPrimitive.Content.displayName;
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
+102 -89
View File
@@ -20,36 +20,42 @@ function DropdownMenuPortal({
); );
} }
function DropdownMenuTrigger({ const DropdownMenuTrigger = React.forwardRef<
...props React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) { React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>
return ( >(({ ...props }, ref) => {
<DropdownMenuPrimitive.Trigger return (
data-slot="dropdown-menu-trigger" <DropdownMenuPrimitive.Trigger
{...props} ref={ref}
/> data-slot="dropdown-menu-trigger"
); {...props}
} />
);
});
DropdownMenuTrigger.displayName = DropdownMenuPrimitive.Trigger.displayName;
function DropdownMenuContent({ const DropdownMenuContent = React.forwardRef<
className, React.ElementRef<typeof DropdownMenuPrimitive.Content>,
sideOffset = 4, React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
...props >(({ className, sideOffset = 4, ...props }, ref) => {
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) { return (
return ( <DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Portal> <DropdownMenuPrimitive.Content
<DropdownMenuPrimitive.Content ref={ref}
data-slot="dropdown-menu-content" data-slot="dropdown-menu-content"
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md", "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
className, className,
)} )}
{...props} {...props}
/> />
</DropdownMenuPrimitive.Portal> </DropdownMenuPrimitive.Portal>
); );
} });
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
function DropdownMenuGroup({ function DropdownMenuGroup({
...props ...props
@@ -59,28 +65,29 @@ function DropdownMenuGroup({
); );
} }
function DropdownMenuItem({ const DropdownMenuItem = React.forwardRef<
className, React.ElementRef<typeof DropdownMenuPrimitive.Item>,
inset, React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
variant = "default", inset?: boolean;
...props variant?: "default" | "destructive";
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & { }
inset?: boolean; >(({ className, inset, variant = "default", ...props }, ref) => {
variant?: "default" | "destructive"; return (
}) { <DropdownMenuPrimitive.Item
return ( ref={ref}
<DropdownMenuPrimitive.Item data-slot="dropdown-menu-item"
data-slot="dropdown-menu-item" data-inset={inset}
data-inset={inset} data-variant={variant}
data-variant={variant} className={cn(
className={cn( "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className,
className, )}
)} {...props}
{...props} />
/> );
); });
}
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
function DropdownMenuCheckboxItem({ function DropdownMenuCheckboxItem({
className, className,
@@ -198,45 +205,51 @@ function DropdownMenuSub({
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />; return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
} }
function DropdownMenuSubTrigger({ const DropdownMenuSubTrigger = React.forwardRef<
className, React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
inset, React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
children, inset?: boolean;
...props }
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { >(({ className, inset, children, ...props }, ref) => {
inset?: boolean; return (
}) { <DropdownMenuPrimitive.SubTrigger
return ( ref={ref}
<DropdownMenuPrimitive.SubTrigger data-slot="dropdown-menu-sub-trigger"
data-slot="dropdown-menu-sub-trigger" data-inset={inset}
data-inset={inset} className={cn(
className={cn( "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className,
className, )}
)} {...props}
{...props} >
> {children}
{children} <ChevronRightIcon className="ml-auto size-4" />
<ChevronRightIcon className="ml-auto size-4" /> </DropdownMenuPrimitive.SubTrigger>
</DropdownMenuPrimitive.SubTrigger> );
); });
}
DropdownMenuSubTrigger.displayName =
DropdownMenuPrimitive.SubTrigger.displayName;
function DropdownMenuSubContent({ const DropdownMenuSubContent = React.forwardRef<
className, React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
...props React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) { >(({ className, ...props }, ref) => {
return ( return (
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content" ref={ref}
className={cn( data-slot="dropdown-menu-sub-content"
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg", className={cn(
className, "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
)} className,
{...props} )}
/> {...props}
); />
} );
});
DropdownMenuSubContent.displayName =
DropdownMenuPrimitive.SubContent.displayName;
export { export {
DropdownMenu, DropdownMenu,
+37 -29
View File
@@ -11,34 +11,42 @@ function HoverCard({
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />; return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
} }
function HoverCardTrigger({ const HoverCardTrigger = React.forwardRef<
...props React.ElementRef<typeof HoverCardPrimitive.Trigger>,
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) { React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Trigger>
return ( >(({ ...props }, ref) => {
<HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} /> return (
); <HoverCardPrimitive.Trigger
} ref={ref}
data-slot="hover-card-trigger"
function HoverCardContent({ {...props}
className, />
align = "center", );
sideOffset = 4, });
...props
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) { HoverCardTrigger.displayName = HoverCardPrimitive.Trigger.displayName;
return (
<HoverCardPrimitive.Portal data-slot="hover-card-portal"> const HoverCardContent = React.forwardRef<
<HoverCardPrimitive.Content React.ElementRef<typeof HoverCardPrimitive.Content>,
data-slot="hover-card-content" React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
align={align} >(({ className, align = "center", sideOffset = 4, ...props }, ref) => {
sideOffset={sideOffset} return (
className={cn( <HoverCardPrimitive.Portal data-slot="hover-card-portal">
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden", <HoverCardPrimitive.Content
className, ref={ref}
)} data-slot="hover-card-content"
{...props} align={align}
/> sideOffset={sideOffset}
</HoverCardPrimitive.Portal> className={cn(
); "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
} className,
)}
{...props}
/>
</HoverCardPrimitive.Portal>
);
});
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
export { HoverCard, HoverCardTrigger, HoverCardContent }; export { HoverCard, HoverCardTrigger, HoverCardContent };
+90 -87
View File
@@ -24,68 +24,69 @@ function SelectValue({
return <SelectPrimitive.Value data-slot="select-value" {...props} />; return <SelectPrimitive.Value data-slot="select-value" {...props} />;
} }
function SelectTrigger({ const SelectTrigger = React.forwardRef<
className, React.ElementRef<typeof SelectPrimitive.Trigger>,
size = "default", React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
children, size?: "sm" | "default";
...props }
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & { >(({ className, size = "default", children, ...props }, ref) => {
size?: "sm" | "default"; return (
}) { <SelectPrimitive.Trigger
return ( ref={ref}
<SelectPrimitive.Trigger data-slot="select-trigger"
data-slot="select-trigger" data-size={size}
data-size={size} className={cn(
className={cn( "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit cursor-pointer items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit cursor-pointer items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className,
className, )}
)} {...props}
{...props} >
> {children}
{children} <SelectPrimitive.Icon asChild>
<SelectPrimitive.Icon asChild> <ChevronDownIcon className="size-4 opacity-50" />
<ChevronDownIcon className="size-4 opacity-50" /> </SelectPrimitive.Icon>
</SelectPrimitive.Icon> </SelectPrimitive.Trigger>
</SelectPrimitive.Trigger> );
); });
}
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
function SelectContent({ const SelectContent = React.forwardRef<
className, React.ElementRef<typeof SelectPrimitive.Content>,
children, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
position = "item-aligned", >(({ className, children, position = "item-aligned", align = "center", ...props }, ref) => {
align = "center", return (
...props <SelectPrimitive.Portal>
}: React.ComponentProps<typeof SelectPrimitive.Content>) { <SelectPrimitive.Content
return ( ref={ref}
<SelectPrimitive.Portal> data-slot="select-content"
<SelectPrimitive.Content className={cn(
data-slot="select-content" "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
className={cn( position === "popper" &&
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md", "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
position === "popper" && className,
"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}
position={position} {...props}
align={align} >
{...props} <SelectScrollUpButton />
> <SelectPrimitive.Viewport
<SelectScrollUpButton /> className={cn(
<SelectPrimitive.Viewport "p-1",
className={cn( position === "popper" &&
"p-1", "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1",
position === "popper" && )}
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1", >
)} {children}
> </SelectPrimitive.Viewport>
{children} <SelectScrollDownButton />
</SelectPrimitive.Viewport> </SelectPrimitive.Content>
<SelectScrollDownButton /> </SelectPrimitive.Portal>
</SelectPrimitive.Content> );
</SelectPrimitive.Portal> });
);
} SelectContent.displayName = SelectPrimitive.Content.displayName;
function SelectLabel({ function SelectLabel({
className, className,
@@ -100,32 +101,34 @@ function SelectLabel({
); );
} }
function SelectItem({ const SelectItem = React.forwardRef<
className, React.ElementRef<typeof SelectPrimitive.Item>,
children, React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
...props >(({ className, children, ...props }, ref) => {
}: React.ComponentProps<typeof SelectPrimitive.Item>) { return (
return ( <SelectPrimitive.Item
<SelectPrimitive.Item ref={ref}
data-slot="select-item" data-slot="select-item"
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none 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", "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-pointer items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none 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} {...props}
> >
<span <span
data-slot="select-item-indicator" data-slot="select-item-indicator"
className="absolute right-2 flex size-3.5 items-center justify-center" className="absolute right-2 flex size-3.5 items-center justify-center"
> >
<SelectPrimitive.ItemIndicator> <SelectPrimitive.ItemIndicator>
<CheckIcon className="size-4" /> <CheckIcon className="size-4" />
</SelectPrimitive.ItemIndicator> </SelectPrimitive.ItemIndicator>
</span> </span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item> </SelectPrimitive.Item>
); );
} });
SelectItem.displayName = SelectPrimitive.Item.displayName;
function SelectSeparator({ function SelectSeparator({
className, className,
+71 -61
View File
@@ -10,17 +10,23 @@ function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
return <SheetPrimitive.Root data-slot="sheet" {...props} />; return <SheetPrimitive.Root data-slot="sheet" {...props} />;
} }
function SheetTrigger({ const SheetTrigger = React.forwardRef<
...props React.ElementRef<typeof SheetPrimitive.Trigger>,
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) { React.ComponentPropsWithoutRef<typeof SheetPrimitive.Trigger>
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />; >(({ ...props }, ref) => {
} return <SheetPrimitive.Trigger ref={ref} data-slot="sheet-trigger" {...props} />;
});
SheetTrigger.displayName = SheetPrimitive.Trigger.displayName;
function SheetClose({ const SheetClose = React.forwardRef<
...props React.ElementRef<typeof SheetPrimitive.Close>,
}: React.ComponentProps<typeof SheetPrimitive.Close>) { React.ComponentPropsWithoutRef<typeof SheetPrimitive.Close>
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />; >(({ ...props }, ref) => {
} return <SheetPrimitive.Close ref={ref} data-slot="sheet-close" {...props} />;
});
SheetClose.displayName = SheetPrimitive.Close.displayName;
function SheetPortal({ function SheetPortal({
...props ...props
@@ -28,58 +34,62 @@ function SheetPortal({
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />; return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
} }
function SheetOverlay({ const SheetOverlay = React.forwardRef<
className, React.ElementRef<typeof SheetPrimitive.Overlay>,
...props React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) { >(({ className, ...props }, ref) => {
return ( return (
<SheetPrimitive.Overlay <SheetPrimitive.Overlay
data-slot="sheet-overlay" ref={ref}
className={cn( data-slot="sheet-overlay"
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", className={cn(
className, "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
)} className,
{...props} )}
/> {...props}
); />
} );
});
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
function SheetContent({ const SheetContent = React.forwardRef<
className, React.ElementRef<typeof SheetPrimitive.Content>,
children, React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content> & {
side = "right", side?: "top" | "right" | "bottom" | "left";
...props }
}: React.ComponentProps<typeof SheetPrimitive.Content> & { >(({ className, children, side = "right", ...props }, ref) => {
side?: "top" | "right" | "bottom" | "left"; return (
}) { <SheetPortal>
return ( <SheetOverlay />
<SheetPortal> <SheetPrimitive.Content
<SheetOverlay /> ref={ref}
<SheetPrimitive.Content data-slot="sheet-content"
data-slot="sheet-content" className={cn(
className={cn( "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500", side === "right" &&
side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm", side === "left" &&
side === "left" && "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm", side === "top" &&
side === "top" && "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b", side === "bottom" &&
side === "bottom" && "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t", className,
className, )}
)} {...props}
{...props} >
> {children}
{children} <SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none"> <XIcon className="size-4" />
<XIcon className="size-4" /> <span className="sr-only">Close</span>
<span className="sr-only">Close</span> </SheetPrimitive.Close>
</SheetPrimitive.Close> </SheetPrimitive.Content>
</SheetPrimitive.Content> </SheetPortal>
</SheetPortal> );
); });
}
SheetContent.displayName = SheetPrimitive.Content.displayName;
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
+169 -153
View File
@@ -397,49 +397,53 @@ function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
); );
} }
function SidebarGroupLabel({ const SidebarGroupLabel = React.forwardRef<
className, HTMLDivElement,
asChild = false, React.ComponentProps<"div"> & { asChild?: boolean }
...props >(({ className, asChild = false, ...props }, ref) => {
}: React.ComponentProps<"div"> & { asChild?: boolean }) { const Comp = asChild ? Slot : "div";
const Comp = asChild ? Slot : "div";
return (
<Comp
ref={ref}
data-slot="sidebar-group-label"
data-sidebar="group-label"
className={cn(
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
className,
)}
{...props}
/>
);
});
SidebarGroupLabel.displayName = "SidebarGroupLabel";
return ( const SidebarGroupAction = React.forwardRef<
<Comp HTMLButtonElement,
data-slot="sidebar-group-label" React.ComponentProps<"button"> & { asChild?: boolean }
data-sidebar="group-label" >(({ className, asChild = false, ...props }, ref) => {
className={cn( const Comp = asChild ? Slot : "button";
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", return (
className, <Comp
)} ref={ref}
{...props} data-slot="sidebar-group-action"
/> data-sidebar="group-action"
); className={cn(
} "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
function SidebarGroupAction({ "after:absolute after:-inset-2 md:after:hidden",
className, "group-data-[collapsible=icon]:hidden",
asChild = false, className,
...props )}
}: React.ComponentProps<"button"> & { asChild?: boolean }) { {...props}
const Comp = asChild ? Slot : "button"; />
);
return ( });
<Comp
data-slot="sidebar-group-action" SidebarGroupAction.displayName = "SidebarGroupAction";
data-sidebar="group-action"
className={cn(
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:absolute after:-inset-2 md:after:hidden",
"group-data-[collapsible=icon]:hidden",
className,
)}
{...props}
/>
);
}
function SidebarGroupContent({ function SidebarGroupContent({
className, className,
@@ -499,87 +503,99 @@ const sidebarMenuButtonVariants = cva(
}, },
); );
function SidebarMenuButton({ const SidebarMenuButton = React.forwardRef<
asChild = false, HTMLButtonElement,
isActive = false, React.ComponentProps<"button"> & {
variant = "default", asChild?: boolean;
size = "default", isActive?: boolean;
tooltip, tooltip?: string | React.ComponentProps<typeof TooltipContent>;
className, } & VariantProps<typeof sidebarMenuButtonVariants>
...props >(
}: React.ComponentProps<"button"> & { (
asChild?: boolean; {
isActive?: boolean; asChild = false,
tooltip?: string | React.ComponentProps<typeof TooltipContent>; isActive = false,
} & VariantProps<typeof sidebarMenuButtonVariants>) { variant = "default",
const Comp = asChild ? Slot : "button"; size = "default",
const { isMobile, state } = useSidebar(); tooltip,
className,
...props
},
ref,
) => {
const Comp = asChild ? Slot : "button";
const { isMobile, state } = useSidebar();
const button = (
<Comp
ref={ref}
data-slot="sidebar-menu-button"
data-sidebar="menu-button"
data-size={size}
data-active={isActive}
className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
{...props}
/>
);
if (!tooltip) {
return button;
}
if (typeof tooltip === "string") {
tooltip = {
children: tooltip,
};
}
return (
<Tooltip>
<TooltipTrigger asChild>{button}</TooltipTrigger>
<TooltipContent
side="right"
align="center"
hidden={state !== "collapsed" || isMobile}
{...tooltip}
/>
</Tooltip>
);
},
);
SidebarMenuButton.displayName = "SidebarMenuButton";
const button = ( const SidebarMenuAction = React.forwardRef<
<Comp HTMLButtonElement,
data-slot="sidebar-menu-button" React.ComponentProps<"button"> & {
data-sidebar="menu-button" asChild?: boolean;
data-size={size} showOnHover?: boolean;
data-active={isActive} }
className={cn(sidebarMenuButtonVariants({ variant, size }), className)} >(({ className, asChild = false, showOnHover = false, ...props }, ref) => {
{...props} const Comp = asChild ? Slot : "button";
/>
); return (
<Comp
if (!tooltip) { ref={ref}
return button; data-slot="sidebar-menu-action"
} data-sidebar="menu-action"
className={cn(
if (typeof tooltip === "string") { "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
tooltip = { // Increases the hit area of the button on mobile.
children: tooltip, "after:absolute after:-inset-2 md:after:hidden",
}; "peer-data-[size=sm]/menu-button:top-1",
} "peer-data-[size=default]/menu-button:top-1.5",
"peer-data-[size=lg]/menu-button:top-2.5",
return ( "group-data-[collapsible=icon]:hidden",
<Tooltip> showOnHover &&
<TooltipTrigger asChild>{button}</TooltipTrigger> "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
<TooltipContent className,
side="right" )}
align="center" {...props}
hidden={state !== "collapsed" || isMobile} />
{...tooltip} );
/> });
</Tooltip>
); SidebarMenuAction.displayName = "SidebarMenuAction";
}
function SidebarMenuAction({
className,
asChild = false,
showOnHover = false,
...props
}: React.ComponentProps<"button"> & {
asChild?: boolean;
showOnHover?: boolean;
}) {
const Comp = asChild ? Slot : "button";
return (
<Comp
data-slot="sidebar-menu-action"
data-sidebar="menu-action"
className={cn(
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:absolute after:-inset-2 md:after:hidden",
"peer-data-[size=sm]/menu-button:top-1",
"peer-data-[size=default]/menu-button:top-1.5",
"peer-data-[size=lg]/menu-button:top-2.5",
"group-data-[collapsible=icon]:hidden",
showOnHover &&
"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
className,
)}
{...props}
/>
);
}
function SidebarMenuBadge({ function SidebarMenuBadge({
className, className,
@@ -670,37 +686,37 @@ function SidebarMenuSubItem({
); );
} }
function SidebarMenuSubButton({ const SidebarMenuSubButton = React.forwardRef<
asChild = false, HTMLAnchorElement,
size = "md", React.ComponentProps<"a"> & {
isActive = false, asChild?: boolean;
className, size?: "sm" | "md";
...props isActive?: boolean;
}: React.ComponentProps<"a"> & { }
asChild?: boolean; >(({ asChild = false, size = "md", isActive = false, className, ...props }, ref) => {
size?: "sm" | "md"; const Comp = asChild ? Slot : "a";
isActive?: boolean;
}) { return (
const Comp = asChild ? Slot : "a"; <Comp
ref={ref}
return ( data-slot="sidebar-menu-sub-button"
<Comp data-sidebar="menu-sub-button"
data-slot="sidebar-menu-sub-button" data-size={size}
data-sidebar="menu-sub-button" data-active={isActive}
data-size={size} className={cn(
data-active={isActive} "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
className={cn( "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0", size === "sm" && "text-xs",
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", size === "md" && "text-sm",
size === "sm" && "text-xs", "group-data-[collapsible=icon]:hidden",
size === "md" && "text-sm", className,
"group-data-[collapsible=icon]:hidden", )}
className, {...props}
)} />
{...props} );
/> });
);
} SidebarMenuSubButton.displayName = "SidebarMenuSubButton";
export { export {
Sidebar, Sidebar,
+53 -45
View File
@@ -40,52 +40,60 @@ const tabsListVariants = cva(
}, },
); );
function TabsList({ const TabsList = React.forwardRef<
className, React.ElementRef<typeof TabsPrimitive.List>,
variant = "default", React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> &
...props VariantProps<typeof tabsListVariants>
}: React.ComponentProps<typeof TabsPrimitive.List> & >(({ className, variant = "default", ...props }, ref) => {
VariantProps<typeof tabsListVariants>) { return (
return ( <TabsPrimitive.List
<TabsPrimitive.List ref={ref}
data-slot="tabs-list" data-slot="tabs-list"
data-variant={variant} data-variant={variant}
className={cn(tabsListVariants({ variant }), className)} className={cn(tabsListVariants({ variant }), className)}
{...props} {...props}
/> />
); );
} });
TabsList.displayName = TabsPrimitive.List.displayName;
function TabsTrigger({ const TabsTrigger = React.forwardRef<
className, React.ElementRef<typeof TabsPrimitive.Trigger>,
...props React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) { >(({ className, ...props }, ref) => {
return ( return (
<TabsPrimitive.Trigger <TabsPrimitive.Trigger
data-slot="tabs-trigger" ref={ref}
className={cn( data-slot="tabs-trigger"
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className={cn(
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 data-[state=active]:text-foreground", "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
"after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100", "data-[state=active]:bg-background dark:data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 data-[state=active]:text-foreground",
className, "after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
)} className,
{...props} )}
/> {...props}
); />
} );
});
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
function TabsContent({ const TabsContent = React.forwardRef<
className, React.ElementRef<typeof TabsPrimitive.Content>,
...props React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
}: React.ComponentProps<typeof TabsPrimitive.Content>) { >(({ className, ...props }, ref) => {
return ( return (
<TabsPrimitive.Content <TabsPrimitive.Content
data-slot="tabs-content" ref={ref}
className={cn("flex-1 outline-none", className)} data-slot="tabs-content"
{...props} className={cn("flex-1 outline-none", className)}
/> {...props}
); />
} );
});
TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }; export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
+38 -28
View File
@@ -28,33 +28,43 @@ function Tooltip({
); );
} }
function TooltipTrigger({ const TooltipTrigger = React.forwardRef<
...props React.ElementRef<typeof TooltipPrimitive.Trigger>,
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) { React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Trigger>
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />; >(({ ...props }, ref) => {
} return (
<TooltipPrimitive.Trigger
function TooltipContent({ ref={ref}
className, data-slot="tooltip-trigger"
sideOffset, {...props}
children, />
...props );
}: React.ComponentProps<typeof TooltipPrimitive.Content>) { });
return (
<TooltipPrimitive.Portal> TooltipTrigger.displayName = TooltipPrimitive.Trigger.displayName;
<TooltipPrimitive.Content
data-slot="tooltip-content" const TooltipContent = React.forwardRef<
sideOffset={sideOffset ?? 4} React.ElementRef<typeof TooltipPrimitive.Content>,
className={cn( React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
"animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-foreground text-background dark:text-foreground z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md border px-3 py-1.5 text-xs text-balance shadow-xs dark:border-white/18 dark:bg-[#050504]", >(({ className, sideOffset, children, ...props }, ref) => {
className, return (
)} <TooltipPrimitive.Portal>
{...props} <TooltipPrimitive.Content
> ref={ref}
{children} data-slot="tooltip-content"
</TooltipPrimitive.Content> sideOffset={sideOffset ?? 4}
</TooltipPrimitive.Portal> className={cn(
); "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-foreground text-background dark:text-foreground z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md border px-3 py-1.5 text-xs text-balance shadow-xs dark:border-white/18 dark:bg-[#050504]",
} className,
)}
{...props}
>
{children}
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
);
});
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
@@ -173,91 +173,89 @@ export function RecentChatList() {
<div className="flex w-full flex-col gap-1"> <div className="flex w-full flex-col gap-1">
{visibleThreads.map((thread) => { {visibleThreads.map((thread) => {
const isActive = pathOfThread(thread) === pathname; const isActive = pathOfThread(thread) === pathname;
return ( return (
<SidebarMenuItem <SidebarMenuItem
key={thread.thread_id} key={thread.thread_id}
className="group/side-menu-item" className="group/side-menu-item"
> >
<SidebarMenuButton isActive={isActive} asChild> <SidebarMenuButton isActive={isActive} asChild>
<div> <Link
<Link className="text-muted-foreground block w-full whitespace-nowrap group-hover/side-menu-item:overflow-hidden"
className="text-muted-foreground block w-full whitespace-nowrap group-hover/side-menu-item:overflow-hidden" href={pathOfThread(thread)}
href={pathOfThread(thread)} >
> {titleOfThread(thread)}
{titleOfThread(thread)} </Link>
</Link> </SidebarMenuButton>
{env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY !== "true" && ( {env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY !== "true" && (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<SidebarMenuAction <SidebarMenuAction
showOnHover showOnHover
className="bg-background/50 hover:bg-background" className="bg-background/50 hover:bg-background"
> >
<MoreHorizontal /> <MoreHorizontal />
<span className="sr-only">{t.common.more}</span> <span className="sr-only">{t.common.more}</span>
</SidebarMenuAction> </SidebarMenuAction>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent <DropdownMenuContent
className="w-48 rounded-lg" className="w-48 rounded-lg"
side={"right"} side={"right"}
align={"start"} align={"start"}
> >
<DropdownMenuItem <DropdownMenuItem
onSelect={() => onSelect={() =>
handleRenameClick( handleRenameClick(
thread.thread_id, thread.thread_id,
titleOfThread(thread), titleOfThread(thread),
) )
} }
> >
<Pencil className="text-muted-foreground" /> <Pencil className="text-muted-foreground" />
<span>{t.common.rename}</span> <span>{t.common.rename}</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onSelect={() => handleShare(thread)} onSelect={() => handleShare(thread)}
> >
<Share2 className="text-muted-foreground" /> <Share2 className="text-muted-foreground" />
<span>{t.common.share}</span> <span>{t.common.share}</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuSub> <DropdownMenuSub>
<DropdownMenuSubTrigger> <DropdownMenuSubTrigger>
<Download className="text-muted-foreground" /> <Download className="text-muted-foreground" />
<span>{t.common.export}</span> <span>{t.common.export}</span>
</DropdownMenuSubTrigger> </DropdownMenuSubTrigger>
<DropdownMenuSubContent> <DropdownMenuSubContent>
<DropdownMenuItem <DropdownMenuItem
onSelect={() => onSelect={() =>
handleExport(thread, "markdown") handleExport(thread, "markdown")
} }
> >
<FileText className="text-muted-foreground" /> <FileText className="text-muted-foreground" />
<span>{t.common.exportAsMarkdown}</span> <span>{t.common.exportAsMarkdown}</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onSelect={() => onSelect={() =>
handleExport(thread, "json") handleExport(thread, "json")
} }
> >
<FileJson className="text-muted-foreground" /> <FileJson className="text-muted-foreground" />
<span>{t.common.exportAsJSON}</span> <span>{t.common.exportAsJSON}</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuSubContent> </DropdownMenuSubContent>
</DropdownMenuSub> </DropdownMenuSub>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem <DropdownMenuItem
onSelect={() => handleDelete(thread.thread_id)} onSelect={() => handleDelete(thread.thread_id)}
> >
<Trash2 className="text-muted-foreground" /> <Trash2 className="text-muted-foreground" />
<span>{t.common.delete}</span> <span>{t.common.delete}</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
)} )}
</div> </SidebarMenuItem>
</SidebarMenuButton> );
</SidebarMenuItem> })}
);
})}
</div> </div>
</SidebarMenu> </SidebarMenu>
</SidebarGroupContent> </SidebarGroupContent>
+31 -38
View File
@@ -1,4 +1,4 @@
"use client"; "use client";
import { import {
ArrowLeftIcon, ArrowLeftIcon,
@@ -17,13 +17,6 @@ import {
} from "@/components/ai-elements/prompt-input"; } from "@/components/ai-elements/prompt-input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { ArtifactsProvider } from "@/components/workspace/artifacts"; import { ArtifactsProvider } from "@/components/workspace/artifacts";
import { import {
@@ -120,7 +113,7 @@ export default function NewAgentPage() {
model_name: chatModelInput || undefined, model_name: chatModelInput || undefined,
}, },
onToolEnd() { onToolEnd() {
// setup_agent never actually executes in this flow — it gets // setup_agent never actually executes in this flow 鈥?it gets
// intercepted by SetupAgentApprovalMiddleware and the form is filled // intercepted by SetupAgentApprovalMiddleware and the form is filled
// from the approval ToolMessage payload (handleApproveSetupAgent). // from the approval ToolMessage payload (handleApproveSetupAgent).
// No agent record is fetched/refreshed during chat. // No agent record is fetched/refreshed during chat.
@@ -156,7 +149,7 @@ export default function NewAgentPage() {
if (!name) { if (!name) {
// Names are display-only and may collide, so a timestamped placeholder // Names are display-only and may collide, so a timestamped placeholder
// is safe to send to the backend just to satisfy the required-field // is safe to send to the backend just to satisfy the required-field
// validation. Intentionally NOT written back into the visible input — // validation. Intentionally NOT written back into the visible input 鈥?
// the user wants the name box to stay blank while they chat, and we // the user wants the name box to stay blank while they chat, and we
// refill it later (with the model's proposed name) on setup_agent // refill it later (with the model's proposed name) on setup_agent
// approval. // approval.
@@ -245,7 +238,7 @@ export default function NewAgentPage() {
// Chat is purely a discussion channel. The agent record is only // Chat is purely a discussion channel. The agent record is only
// created when the user explicitly clicks Save (top-right). If the // created when the user explicitly clicks Save (top-right). If the
// model proposes a config via setup_agent, our approval handler // model proposes a config via setup_agent, our approval handler
// prefills the form locally — it does NOT persist anything. // prefills the form locally 鈥?it does NOT persist anything.
await ensureThreadExists(); await ensureThreadExists();
await sendMessage(threadId, { text: trimmed, files: [] }); await sendMessage(threadId, { text: trimmed, files: [] });
}, },
@@ -255,7 +248,7 @@ export default function NewAgentPage() {
const handleApproveSetupAgent = useCallback( const handleApproveSetupAgent = useCallback(
async (payload: SetupAgentApprovalPayload) => { async (payload: SetupAgentApprovalPayload) => {
// Approving the model's setup_agent proposal **does not** persist // Approving the model's setup_agent proposal **does not** persist
// anything — it only mirrors the proposal into the form so the user // anything 鈥?it only mirrors the proposal into the form so the user
// can review it. Creation/update happens later when the user clicks // can review it. Creation/update happens later when the user clicks
// the top-right Save button. // the top-right Save button.
const proposedName = payload.name?.trim() ?? ""; const proposedName = payload.name?.trim() ?? "";
@@ -338,22 +331,23 @@ export default function NewAgentPage() {
<span className="text-muted-foreground text-xs"> <span className="text-muted-foreground text-xs">
当前对话模型 当前对话模型
</span> </span>
<Select <select
value={chatModelInput} value={chatModelInput}
onValueChange={setChatModelInput} onChange={(event) =>
setChatModelInput(event.currentTarget.value)
}
disabled={isModelsLoading || thread.isLoading} disabled={isModelsLoading || thread.isLoading}
className="border-input bg-background h-7 min-w-[10rem] rounded-md border px-2 text-xs shadow-xs outline-none"
> >
<SelectTrigger className="h-7 w-auto min-w-[10rem] gap-2 px-2 text-xs"> <option value="" disabled>
<SelectValue placeholder="选择模型" /> 选择模型
</SelectTrigger> </option>
<SelectContent> {models.map((model) => (
{models.map((model) => ( <option key={model.name} value={model.name}>
<SelectItem key={model.name} value={model.name}> {model.display_name || model.name}
{model.display_name || model.name} </option>
</SelectItem> ))}
))} </select>
</SelectContent>
</Select>
</div> </div>
<PromptInput <PromptInput
onSubmit={({ text }) => void handleChatSubmit(text)} onSubmit={({ text }) => void handleChatSubmit(text)}
@@ -405,22 +399,21 @@ export default function NewAgentPage() {
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">模型</label> <label className="text-sm font-medium">模型</label>
<Select <select
value={modelInput} value={modelInput}
onValueChange={setModelInput} onChange={(event) => setModelInput(event.currentTarget.value)}
disabled={isModelsLoading} disabled={isModelsLoading}
className="border-input bg-background h-9 w-full rounded-md border px-3 text-sm shadow-xs outline-none"
> >
<SelectTrigger className="w-full"> <option value="" disabled>
<SelectValue placeholder="选择模型" /> 选择模型
</SelectTrigger> </option>
<SelectContent> {models.map((model) => (
{models.map((model) => ( <option key={model.name} value={model.name}>
<SelectItem key={model.name} value={model.name}> {model.display_name || model.name}
{model.display_name || model.name} </option>
</SelectItem> ))}
))} </select>
</SelectContent>
</Select>
<p className="text-muted-foreground text-xs"> <p className="text-muted-foreground text-xs">
该专家被使用时调用的模型。 该专家被使用时调用的模型。
</p> </p>
+1 -1
View File
@@ -8,7 +8,7 @@ import StrategyRoutes from "./StrategyRoutes";
import Header from "@/strategy-components/Header"; import Header from "@/strategy-components/Header";
// 设为 true 时隐藏侧边栏(开发中,暂未完成) // 设为 true 时隐藏侧边栏(开发中,暂未完成)
const SIDEBAR_ENABLED = false; const SIDEBAR_ENABLED = true;
function PageLayout() { function PageLayout() {
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
+4 -14
View File
@@ -1,14 +1,12 @@
import { Navigate, Route, Routes } from "react-router-dom"; import { Navigate, Route, Routes } from "react-router-dom";
import type { ReactNode } from "react";
import { Toaster } from "sonner"; import { Toaster } from "sonner";
import { getStoredAuth } from "@/core/auth"; import { getStoredAuth } from "@/core/auth";
import { RouteBaseProvider } from "@/core/route-base"; import { RouteBaseProvider } from "@/core/route-base";
import { SubtasksProvider } from "@/core/tasks/context"; import { SidebarProvider } from "@/components/ui/sidebar";
import { ArtifactsProvider } from "@/components/workspace/artifacts";
import { PromptInputProvider } from "@/components/ai-elements/prompt-input";
import GeneralQA from "@/strategy-components/components/qa/GeneralQA"; import GeneralQA from "@/strategy-components/components/qa/GeneralQA";
import { ChatRuntime } from "./WorkspaceRoutes";
import AgentsPage from "./AgentsPage"; import AgentsPage from "./AgentsPage";
import ChatsPage from "./ChatsPage"; import ChatsPage from "./ChatsPage";
import ChatPage from "./ChatPage"; import ChatPage from "./ChatPage";
@@ -18,19 +16,10 @@ import ScheduledTasksPage from "./ScheduledTasksPage";
import ScheduledTaskRunDetailPage from "./ScheduledTaskRunDetailPage"; import ScheduledTaskRunDetailPage from "./ScheduledTaskRunDetailPage";
import RecommendedQuestionsPage from "./RecommendedQuestionsPage"; import RecommendedQuestionsPage from "./RecommendedQuestionsPage";
function ChatRuntime({ children }: { children: ReactNode }) {
return (
<SubtasksProvider>
<ArtifactsProvider>
<PromptInputProvider>{children}</PromptInputProvider>
</ArtifactsProvider>
</SubtasksProvider>
);
}
export default function StrategyRoutes() { export default function StrategyRoutes() {
return ( return (
<RouteBaseProvider value="/page/strategy"> <RouteBaseProvider value="/page/strategy">
<SidebarProvider>
<div className="h-full w-full overflow-hidden"> <div className="h-full w-full overflow-hidden">
<Routes> <Routes>
<Route index element={<Navigate to="qa/general" replace />} /> <Route index element={<Navigate to="qa/general" replace />} />
@@ -89,6 +78,7 @@ export default function StrategyRoutes() {
/> />
</Routes> </Routes>
</div> </div>
</SidebarProvider>
<Toaster position="top-center" /> <Toaster position="top-center" />
</RouteBaseProvider> </RouteBaseProvider>
); );
+1 -1
View File
@@ -61,7 +61,7 @@ export function WorkspaceLayout({
); );
} }
function ChatRuntime({ children }: { children: ReactNode }) { export function ChatRuntime({ children }: { children: ReactNode }) {
return ( return (
<SubtasksProvider> <SubtasksProvider>
<ArtifactsProvider> <ArtifactsProvider>