mirror of
https://github.com/run-llama/ai-chatbot.git
synced 2026-07-01 21:14:02 -04:00
fix: update sidebar structure to match shadcn spec (#591)
This commit is contained in:
+2
-2
@@ -50,10 +50,10 @@
|
||||
--chart-5: 27 87% 67%;
|
||||
--radius: 0.5rem;
|
||||
--sidebar-background: 0 0% 98%;
|
||||
--sidebar-foreground: 240 10% 3.9%;
|
||||
--sidebar-foreground: 240 5.3% 26.1%;
|
||||
--sidebar-primary: 240 5.9% 10%;
|
||||
--sidebar-primary-foreground: 0 0% 98%;
|
||||
--sidebar-accent: 240 5.9% 94%;
|
||||
--sidebar-accent: 240 4.8% 95.9%;
|
||||
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||
--sidebar-border: 220 13% 91%;
|
||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
|
||||
+20
-27
@@ -17,8 +17,8 @@ import {
|
||||
SidebarMenu,
|
||||
useSidebar,
|
||||
} from '@/components/ui/sidebar';
|
||||
import { BetterTooltip } from '@/components/ui/tooltip';
|
||||
import Link from 'next/link';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
|
||||
|
||||
export function AppSidebar({ user }: { user: User | undefined }) {
|
||||
const router = useRouter();
|
||||
@@ -40,37 +40,30 @@ export function AppSidebar({ user }: { user: User | undefined }) {
|
||||
Chatbot
|
||||
</span>
|
||||
</Link>
|
||||
<BetterTooltip content="New Chat" align="start">
|
||||
<Button
|
||||
variant="ghost"
|
||||
type="button"
|
||||
className="p-2 h-fit"
|
||||
onClick={() => {
|
||||
setOpenMobile(false);
|
||||
router.push('/');
|
||||
router.refresh();
|
||||
}}
|
||||
>
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
</BetterTooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
type="button"
|
||||
className="p-2 h-fit"
|
||||
onClick={() => {
|
||||
setOpenMobile(false);
|
||||
router.push('/');
|
||||
router.refresh();
|
||||
}}
|
||||
>
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent align="end">New Chat</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</SidebarMenu>
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<SidebarGroup className="-mx-2">
|
||||
<SidebarHistory user={user} />
|
||||
</SidebarGroup>
|
||||
<SidebarHistory user={user} />
|
||||
</SidebarContent>
|
||||
<SidebarFooter className="gap-0 -mx-2">
|
||||
{user && (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupContent>
|
||||
<SidebarUserNav user={user} />
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
)}
|
||||
</SidebarFooter>
|
||||
<SidebarFooter>{user && <SidebarUserNav user={user} />}</SidebarFooter>
|
||||
</Sidebar>
|
||||
);
|
||||
}
|
||||
|
||||
+17
-14
@@ -7,10 +7,10 @@ import { useWindowSize } from 'usehooks-ts';
|
||||
import { ModelSelector } from '@/components/model-selector';
|
||||
import { SidebarToggle } from '@/components/sidebar-toggle';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { BetterTooltip } from '@/components/ui/tooltip';
|
||||
import { PlusIcon, VercelIcon } from './icons';
|
||||
import { useSidebar } from './ui/sidebar';
|
||||
import { memo } from 'react';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
|
||||
|
||||
function PureChatHeader({ selectedModelId }: { selectedModelId: string }) {
|
||||
const router = useRouter();
|
||||
@@ -22,19 +22,22 @@ function PureChatHeader({ selectedModelId }: { selectedModelId: string }) {
|
||||
<header className="flex sticky top-0 bg-background py-1.5 items-center px-2 md:px-2 gap-2">
|
||||
<SidebarToggle />
|
||||
{(!open || windowWidth < 768) && (
|
||||
<BetterTooltip content="New Chat">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="order-2 md:order-1 md:px-2 px-2 md:h-fit ml-auto md:ml-0"
|
||||
onClick={() => {
|
||||
router.push('/');
|
||||
router.refresh();
|
||||
}}
|
||||
>
|
||||
<PlusIcon />
|
||||
<span className="md:sr-only">New Chat</span>
|
||||
</Button>
|
||||
</BetterTooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="order-2 md:order-1 md:px-2 px-2 md:h-fit ml-auto md:ml-0"
|
||||
onClick={() => {
|
||||
router.push('/');
|
||||
router.refresh();
|
||||
}}
|
||||
>
|
||||
<PlusIcon />
|
||||
<span className="md:sr-only">New Chat</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>New Chat</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
<ModelSelector
|
||||
selectedModelId={selectedModelId}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import type { ComponentProps } from 'react';
|
||||
|
||||
import { type SidebarTrigger, useSidebar } from '@/components/ui/sidebar';
|
||||
import { BetterTooltip } from '@/components/ui/tooltip';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
|
||||
import { SidebarLeftIcon } from './icons';
|
||||
import { Button } from './ui/button';
|
||||
@@ -12,14 +16,17 @@ export function SidebarToggle({
|
||||
const { toggleSidebar } = useSidebar();
|
||||
|
||||
return (
|
||||
<BetterTooltip content="Toggle Sidebar" align="start">
|
||||
<Button
|
||||
onClick={toggleSidebar}
|
||||
variant="outline"
|
||||
className="md:px-2 md:h-fit"
|
||||
>
|
||||
<SidebarLeftIcon size={16} />
|
||||
</Button>
|
||||
</BetterTooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={toggleSidebar}
|
||||
variant="outline"
|
||||
className="md:px-2 md:h-fit"
|
||||
>
|
||||
<SidebarLeftIcon size={16} />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent align="start">Toggle Sidebar</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex items-center gap-2 justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -30,10 +30,10 @@ const CardHeader = React.forwardRef<
|
||||
CardHeader.displayName = 'CardHeader';
|
||||
|
||||
const CardTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h3
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'text-2xl font-semibold leading-none tracking-tight',
|
||||
@@ -45,10 +45,10 @@ const CardTitle = React.forwardRef<
|
||||
CardTitle.displayName = 'CardTitle';
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<p
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
|
||||
@@ -27,14 +27,14 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
|
||||
'flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="ml-auto h-4 w-4" />
|
||||
<ChevronRight className="ml-auto" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
));
|
||||
DropdownMenuSubTrigger.displayName =
|
||||
@@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-md 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] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md 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',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -83,7 +83,7 @@ const DropdownMenuItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex gap-2 cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className,
|
||||
)}
|
||||
|
||||
@@ -2,16 +2,13 @@ import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { type VariantProps, cva } from 'class-variance-authority';
|
||||
import { VariantProps, cva } from 'class-variance-authority';
|
||||
import { PanelLeft } from 'lucide-react';
|
||||
|
||||
import { useIsMobile } from '@/hooks/use-mobile';
|
||||
@@ -10,12 +10,7 @@ import { cn } from '@/lib/utils';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetTitle,
|
||||
} from '@/components/ui/sheet';
|
||||
import { Sheet, SheetContent } from '@/components/ui/sheet';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import {
|
||||
Tooltip,
|
||||
@@ -157,7 +152,6 @@ const SidebarProvider = React.forwardRef<
|
||||
'group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar',
|
||||
className,
|
||||
)}
|
||||
data-state={state}
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
@@ -209,10 +203,6 @@ const Sidebar = React.forwardRef<
|
||||
if (isMobile) {
|
||||
return (
|
||||
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
|
||||
<SheetTitle className="sr-only">Sidebar</SheetTitle>
|
||||
<SheetDescription className="sr-only">
|
||||
Mobile sidebar
|
||||
</SheetDescription>
|
||||
<SheetContent
|
||||
data-sidebar="sidebar"
|
||||
data-mobile="true"
|
||||
@@ -280,7 +270,7 @@ Sidebar.displayName = 'Sidebar';
|
||||
const SidebarTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof Button>,
|
||||
React.ComponentProps<typeof Button>
|
||||
>(({ onClick, ...props }, ref) => {
|
||||
>(({ className, onClick, ...props }, ref) => {
|
||||
const { toggleSidebar } = useSidebar();
|
||||
|
||||
return (
|
||||
@@ -289,13 +279,14 @@ const SidebarTrigger = React.forwardRef<
|
||||
data-sidebar="trigger"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={cn('h-7 w-7', className)}
|
||||
onClick={(event) => {
|
||||
onClick?.(event);
|
||||
toggleSidebar();
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<PanelLeft className="size-5" />
|
||||
<PanelLeft />
|
||||
<span className="sr-only">Toggle Sidebar</span>
|
||||
</Button>
|
||||
);
|
||||
@@ -681,12 +672,12 @@ const SidebarMenuSkeleton = React.forwardRef<
|
||||
>
|
||||
{showIcon && (
|
||||
<Skeleton
|
||||
className="size-4 rounded-md bg-sidebar-accent-foreground/10"
|
||||
className="size-4 rounded-md"
|
||||
data-sidebar="menu-skeleton-icon"
|
||||
/>
|
||||
)}
|
||||
<Skeleton
|
||||
className="h-4 flex-1 max-w-[--skeleton-width] bg-sidebar-accent-foreground/10"
|
||||
className="h-4 flex-1 max-w-[--skeleton-width]"
|
||||
data-sidebar="menu-skeleton-text"
|
||||
style={
|
||||
{
|
||||
|
||||
+15
-17
@@ -2,23 +2,21 @@ import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface TextareaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
|
||||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
const Textarea = React.forwardRef<
|
||||
HTMLTextAreaElement,
|
||||
React.ComponentProps<'textarea'>
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
Textarea.displayName = 'Textarea';
|
||||
|
||||
export { Textarea };
|
||||
|
||||
@@ -28,22 +28,3 @@ const TooltipContent = React.forwardRef<
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
||||
|
||||
export const BetterTooltip = ({
|
||||
content,
|
||||
children,
|
||||
align = 'center',
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<typeof Tooltip> & {
|
||||
content: JSX.Element | string;
|
||||
align?: 'center' | 'end' | 'start';
|
||||
}) => {
|
||||
return (
|
||||
<TooltipProvider delayDuration={0}>
|
||||
<Tooltip {...props}>
|
||||
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
||||
<TooltipContent align={align}>{content}</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-2
@@ -21,13 +21,13 @@
|
||||
"@ai-sdk/openai": "1.0.6",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.2",
|
||||
"@radix-ui/react-dialog": "^1.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@radix-ui/react-tooltip": "^1.1.3",
|
||||
"@radix-ui/react-visually-hidden": "^1.1.0",
|
||||
"@vercel/analytics": "^1.3.1",
|
||||
"@vercel/blob": "^0.24.1",
|
||||
|
||||
Generated
+2
-2
@@ -18,7 +18,7 @@ importers:
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021)
|
||||
'@radix-ui/react-dropdown-menu':
|
||||
specifier: ^2.1.1
|
||||
specifier: ^2.1.2
|
||||
version: 2.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021)
|
||||
'@radix-ui/react-icons':
|
||||
specifier: ^1.3.0
|
||||
@@ -36,7 +36,7 @@ importers:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(@types/react@18.3.12)(react@19.0.0-rc-45804af1-20241021)
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: ^1.1.2
|
||||
specifier: ^1.1.3
|
||||
version: 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021)
|
||||
'@radix-ui/react-visually-hidden':
|
||||
specifier: ^1.1.0
|
||||
|
||||
Reference in New Issue
Block a user