Use geist icons and group history by ranges (#471)

This commit is contained in:
Jeremy 2024-10-31 15:39:07 +05:30 committed by GitHub
parent dce1314084
commit 543267516e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 306 additions and 107 deletions

View file

@ -1,10 +1,9 @@
'use client'; 'use client';
import { Plus } from 'lucide-react';
import Link from 'next/link'; import Link from 'next/link';
import { type User } from 'next-auth'; import { type User } from 'next-auth';
import { VercelIcon } from '@/components/custom/icons'; import { PlusIcon, VercelIcon } from '@/components/custom/icons';
import { SidebarHistory } from '@/components/custom/sidebar-history'; import { SidebarHistory } from '@/components/custom/sidebar-history';
import { SidebarUserNav } from '@/components/custom/sidebar-user-nav'; import { SidebarUserNav } from '@/components/custom/sidebar-user-nav';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@ -23,9 +22,6 @@ import {
SidebarGroupContent, SidebarGroupContent,
SidebarHeader, SidebarHeader,
SidebarMenu, SidebarMenu,
SidebarMenuAction,
SidebarMenuButton,
SidebarMenuItem,
useSidebar, useSidebar,
} from '@/components/ui/sidebar'; } from '@/components/ui/sidebar';
import { BetterTooltip } from '@/components/ui/tooltip'; import { BetterTooltip } from '@/components/ui/tooltip';
@ -37,31 +33,33 @@ export function AppSidebar({ user }: { user: User | undefined }) {
<Sidebar className="group-data-[side=left]:border-r-0"> <Sidebar className="group-data-[side=left]:border-r-0">
<SidebarHeader> <SidebarHeader>
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> <div className="flex flex-row justify-between items-center">
<SidebarMenuButton asChild> <Link
<Link href="/" onClick={() => setOpenMobile(false)}> href="/"
<span className="text-lg font-semibold font-mono tracking-tighter"> onClick={() => setOpenMobile(false)}
Chatbot className="flex flex-row gap-3 items-center"
</span> >
<span className="text-lg font-semibold px-2">Chatbot</span>
</Link> </Link>
</SidebarMenuButton> <BetterTooltip content="New Chat" align="start">
<BetterTooltip content="New Chat"> <Button variant="ghost" className="p-2 h-fit">
<SidebarMenuAction asChild>
<Link href="/" onClick={() => setOpenMobile(false)}> <Link href="/" onClick={() => setOpenMobile(false)}>
<Plus /> <PlusIcon />
</Link> </Link>
</SidebarMenuAction> </Button>
</BetterTooltip> </BetterTooltip>
</SidebarMenuItem> </div>
</SidebarMenu> </SidebarMenu>
</SidebarHeader> </SidebarHeader>
<SidebarContent> <SidebarContent>
<SidebarGroup>
<SidebarHistory user={user} /> <SidebarHistory user={user} />
</SidebarGroup>
</SidebarContent> </SidebarContent>
<SidebarFooter className="gap-0"> <SidebarFooter className="gap-0">
<SidebarGroup> <SidebarGroup>
<SidebarGroupContent> <SidebarGroupContent>
<Card className="p-4 flex flex-col gap-4 relative rounded-md border-none shadow-none hover:shadow transition-shadow"> <Card className="p-4 flex flex-col gap-4 relative rounded-lg shadow-none border-none">
<a <a
href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot&env=AUTH_SECRET,OPENAI_API_KEY&envDescription=Learn%20more%20about%20how%20to%20get%20the%20API%20Keys%20for%20the%20application&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot%2Fblob%2Fmain%2F.env.example&demo-title=AI%20Chatbot&demo-description=An%20Open-Source%20AI%20Chatbot%20Template%20Built%20With%20Next.js%20and%20the%20AI%20SDK%20by%20Vercel.&demo-url=https%3A%2F%2Fchat.vercel.ai&stores=[{%22type%22:%22postgres%22},{%22type%22:%22blob%22}]" href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot&env=AUTH_SECRET,OPENAI_API_KEY&envDescription=Learn%20more%20about%20how%20to%20get%20the%20API%20Keys%20for%20the%20application&envLink=https%3A%2F%2Fgithub.com%2Fvercel%2Fai-chatbot%2Fblob%2Fmain%2F.env.example&demo-title=AI%20Chatbot&demo-description=An%20Open-Source%20AI%20Chatbot%20Template%20Built%20With%20Next.js%20and%20the%20AI%20SDK%20by%20Vercel.&demo-url=https%3A%2F%2Fchat.vercel.ai&stores=[{%22type%22:%22postgres%22},{%22type%22:%22blob%22}]"
className="absolute inset-0 rounded-lg outline-none focus-visible:ring-2 focus-visible:ring-sidebar-ring" className="absolute inset-0 rounded-lg outline-none focus-visible:ring-2 focus-visible:ring-sidebar-ring"

View file

@ -1,4 +1,3 @@
import { Plus } from 'lucide-react';
import Link from 'next/link'; import Link from 'next/link';
import { ModelSelector } from '@/components/custom/model-selector'; import { ModelSelector } from '@/components/custom/model-selector';
@ -6,9 +5,11 @@ import { SidebarToggle } from '@/components/custom/sidebar-toggle';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { BetterTooltip } from '@/components/ui/tooltip'; import { BetterTooltip } from '@/components/ui/tooltip';
import { PlusIcon } from './icons';
export function ChatHeader({ selectedModelId }: { selectedModelId: string }) { export function ChatHeader({ selectedModelId }: { selectedModelId: string }) {
return ( return (
<header className="flex h-16 sticky top-0 bg-background md:h-12 items-center px-2 md:px-2 z-10"> <header className="flex h-16 sticky top-0 bg-background md:h-12 items-center px-2 md:px-2 gap-2">
<SidebarToggle /> <SidebarToggle />
<BetterTooltip content="New Chat"> <BetterTooltip content="New Chat">
<Button <Button
@ -17,7 +18,7 @@ export function ChatHeader({ selectedModelId }: { selectedModelId: string }) {
asChild asChild
> >
<Link href="/"> <Link href="/">
<Plus /> <PlusIcon />
<span className="md:sr-only">New Chat</span> <span className="md:sr-only">New Chat</span>
</Link> </Link>
</Button> </Button>

View file

@ -5,7 +5,7 @@ export const BotIcon = () => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width="16" width="16"
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -25,7 +25,7 @@ export const UserIcon = () => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width="16" width="16"
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -44,7 +44,7 @@ export const AttachmentIcon = () => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width="16" width="16"
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -63,7 +63,7 @@ export const VercelIcon = ({ size = 17 }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -82,7 +82,7 @@ export const GitIcon = () => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width="16" width="16"
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<g clipPath="url(#clip0_872_3147)"> <g clipPath="url(#clip0_872_3147)">
<path <path
@ -108,7 +108,7 @@ export const BoxIcon = ({ size = 16 }: { size: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -127,7 +127,7 @@ export const HomeIcon = ({ size = 16 }: { size: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -146,7 +146,7 @@ export const GPSIcon = ({ size = 16 }: { size: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
d="M1 6L15 1L10 15L7.65955 8.91482C7.55797 8.65073 7.34927 8.44203 7.08518 8.34045L1 6Z" d="M1 6L15 1L10 15L7.65955 8.91482C7.55797 8.65073 7.34927 8.44203 7.08518 8.34045L1 6Z"
@ -167,7 +167,7 @@ export const InvoiceIcon = ({ size = 16 }: { size: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -186,7 +186,7 @@ export const LogoOpenAI = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
d="M14.9449 6.54871C15.3128 5.45919 15.1861 4.26567 14.5978 3.27464C13.7131 1.75461 11.9345 0.972595 10.1974 1.3406C9.42464 0.481584 8.3144 -0.00692594 7.15045 7.42132e-05C5.37487 -0.00392587 3.79946 1.1241 3.2532 2.79113C2.11256 3.02164 1.12799 3.72615 0.551837 4.72468C-0.339497 6.24071 -0.1363 8.15175 1.05451 9.45178C0.686626 10.5413 0.813308 11.7348 1.40162 12.7258C2.28637 14.2459 4.06498 15.0279 5.80204 14.6599C6.5743 15.5189 7.68504 16.0074 8.849 15.9999C10.6256 16.0044 12.2015 14.8754 12.7478 13.2069C13.8884 12.9764 14.873 12.2718 15.4491 11.2733C16.3394 9.75728 16.1357 7.84774 14.9454 6.54771L14.9449 6.54871ZM8.85001 14.9544C8.13907 14.9554 7.45043 14.7099 6.90468 14.2604C6.92951 14.2474 6.97259 14.2239 7.00046 14.2069L10.2293 12.3668C10.3945 12.2743 10.4959 12.1008 10.4949 11.9133V7.42173L11.8595 8.19925C11.8742 8.20625 11.8838 8.22025 11.8858 8.23625V11.9558C11.8838 13.6099 10.5263 14.9509 8.85001 14.9544ZM2.32133 12.2028C1.9651 11.5958 1.8369 10.8843 1.95902 10.1938C1.98284 10.2078 2.02489 10.2333 2.05479 10.2503L5.28366 12.0903C5.44733 12.1848 5.65003 12.1848 5.81421 12.0903L9.75604 9.84429V11.3993C9.75705 11.4153 9.74945 11.4308 9.73678 11.4408L6.47295 13.3004C5.01915 14.1264 3.1625 13.6354 2.32184 12.2028H2.32133ZM1.47155 5.24819C1.82626 4.64017 2.38619 4.17516 3.05305 3.93366C3.05305 3.96116 3.05152 4.00966 3.05152 4.04366V7.72424C3.05051 7.91124 3.15186 8.08475 3.31654 8.17725L7.25838 10.4228L5.89376 11.2003C5.88008 11.2093 5.86285 11.2108 5.84765 11.2043L2.58331 9.34327C1.13255 8.51426 0.63494 6.68272 1.47104 5.24869L1.47155 5.24819ZM12.6834 7.82274L8.74157 5.57669L10.1062 4.79968C10.1199 4.79068 10.1371 4.78918 10.1523 4.79568L13.4166 6.65522C14.8699 7.48373 15.3681 9.31827 14.5284 10.7523C14.1732 11.3593 13.6138 11.8243 12.9474 12.0663V8.27575C12.9489 8.08875 12.8481 7.91574 12.6839 7.82274H12.6834ZM14.0414 5.8057C14.0176 5.7912 13.9756 5.7662 13.9457 5.7492L10.7168 3.90916C10.5531 3.81466 10.3504 3.81466 10.1863 3.90916L6.24442 6.15521V4.60017C6.2434 4.58417 6.251 4.56867 6.26367 4.55867L9.52751 2.70063C10.9813 1.87311 12.84 2.36563 13.6781 3.80066C14.0323 4.40667 14.1605 5.11618 14.0404 5.8057H14.0414ZM5.50257 8.57726L4.13744 7.79974C4.12275 7.79274 4.11312 7.77874 4.11109 7.76274V4.04316C4.11211 2.38713 5.47368 1.0451 7.15197 1.0461C7.86189 1.0461 8.54902 1.2921 9.09476 1.74011C9.06993 1.75311 9.02737 1.77661 8.99899 1.79361L5.77012 3.63365C5.60493 3.72615 5.50358 3.89916 5.50459 4.08666L5.50257 8.57626V8.57726ZM6.24391 7.00022L7.99972 5.9997L9.75553 6.99972V9.00027L7.99972 10.0003L6.24391 9.00027V7.00022Z" d="M14.9449 6.54871C15.3128 5.45919 15.1861 4.26567 14.5978 3.27464C13.7131 1.75461 11.9345 0.972595 10.1974 1.3406C9.42464 0.481584 8.3144 -0.00692594 7.15045 7.42132e-05C5.37487 -0.00392587 3.79946 1.1241 3.2532 2.79113C2.11256 3.02164 1.12799 3.72615 0.551837 4.72468C-0.339497 6.24071 -0.1363 8.15175 1.05451 9.45178C0.686626 10.5413 0.813308 11.7348 1.40162 12.7258C2.28637 14.2459 4.06498 15.0279 5.80204 14.6599C6.5743 15.5189 7.68504 16.0074 8.849 15.9999C10.6256 16.0044 12.2015 14.8754 12.7478 13.2069C13.8884 12.9764 14.873 12.2718 15.4491 11.2733C16.3394 9.75728 16.1357 7.84774 14.9454 6.54771L14.9449 6.54871ZM8.85001 14.9544C8.13907 14.9554 7.45043 14.7099 6.90468 14.2604C6.92951 14.2474 6.97259 14.2239 7.00046 14.2069L10.2293 12.3668C10.3945 12.2743 10.4959 12.1008 10.4949 11.9133V7.42173L11.8595 8.19925C11.8742 8.20625 11.8838 8.22025 11.8858 8.23625V11.9558C11.8838 13.6099 10.5263 14.9509 8.85001 14.9544ZM2.32133 12.2028C1.9651 11.5958 1.8369 10.8843 1.95902 10.1938C1.98284 10.2078 2.02489 10.2333 2.05479 10.2503L5.28366 12.0903C5.44733 12.1848 5.65003 12.1848 5.81421 12.0903L9.75604 9.84429V11.3993C9.75705 11.4153 9.74945 11.4308 9.73678 11.4408L6.47295 13.3004C5.01915 14.1264 3.1625 13.6354 2.32184 12.2028H2.32133ZM1.47155 5.24819C1.82626 4.64017 2.38619 4.17516 3.05305 3.93366C3.05305 3.96116 3.05152 4.00966 3.05152 4.04366V7.72424C3.05051 7.91124 3.15186 8.08475 3.31654 8.17725L7.25838 10.4228L5.89376 11.2003C5.88008 11.2093 5.86285 11.2108 5.84765 11.2043L2.58331 9.34327C1.13255 8.51426 0.63494 6.68272 1.47104 5.24869L1.47155 5.24819ZM12.6834 7.82274L8.74157 5.57669L10.1062 4.79968C10.1199 4.79068 10.1371 4.78918 10.1523 4.79568L13.4166 6.65522C14.8699 7.48373 15.3681 9.31827 14.5284 10.7523C14.1732 11.3593 13.6138 11.8243 12.9474 12.0663V8.27575C12.9489 8.08875 12.8481 7.91574 12.6839 7.82274H12.6834ZM14.0414 5.8057C14.0176 5.7912 13.9756 5.7662 13.9457 5.7492L10.7168 3.90916C10.5531 3.81466 10.3504 3.81466 10.1863 3.90916L6.24442 6.15521V4.60017C6.2434 4.58417 6.251 4.56867 6.26367 4.55867L9.52751 2.70063C10.9813 1.87311 12.84 2.36563 13.6781 3.80066C14.0323 4.40667 14.1605 5.11618 14.0404 5.8057H14.0414ZM5.50257 8.57726L4.13744 7.79974C4.12275 7.79274 4.11312 7.77874 4.11109 7.76274V4.04316C4.11211 2.38713 5.47368 1.0451 7.15197 1.0461C7.86189 1.0461 8.54902 1.2921 9.09476 1.74011C9.06993 1.75311 9.02737 1.77661 8.99899 1.79361L5.77012 3.63365C5.60493 3.72615 5.50358 3.89916 5.50459 4.08666L5.50257 8.57626V8.57726ZM6.24391 7.00022L7.99972 5.9997L9.75553 6.99972V9.00027L7.99972 10.0003L6.24391 9.00027V7.00022Z"
@ -204,7 +204,7 @@ export const LogoGoogle = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
d="M8.15991 6.54543V9.64362H12.4654C12.2763 10.64 11.709 11.4837 10.8581 12.0509L13.4544 14.0655C14.9671 12.6692 15.8399 10.6182 15.8399 8.18188C15.8399 7.61461 15.789 7.06911 15.6944 6.54552L8.15991 6.54543Z" d="M8.15991 6.54543V9.64362H12.4654C12.2763 10.64 11.709 11.4837 10.8581 12.0509L13.4544 14.0655C14.9671 12.6692 15.8399 10.6182 15.8399 8.18188C15.8399 7.61461 15.789 7.06911 15.6944 6.54552L8.15991 6.54543Z"
@ -234,7 +234,7 @@ export const LogoAnthropic = () => {
x="0px" x="0px"
y="0px" y="0px"
viewBox="0 0 92.2 65" viewBox="0 0 92.2 65"
style={{ color: "currentcolor", fill: "currentcolor" }} style={{ color: 'currentcolor', fill: 'currentcolor' }}
width="18px" width="18px"
height="18px" height="18px"
> >
@ -253,7 +253,7 @@ export const RouteIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -272,7 +272,7 @@ export const FileIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -291,7 +291,7 @@ export const LoaderIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<g clipPath="url(#clip0_2393_1490)"> <g clipPath="url(#clip0_2393_1490)">
<path d="M8 0V4" stroke="currentColor" strokeWidth="1.5"></path> <path d="M8 0V4" stroke="currentColor" strokeWidth="1.5"></path>
@ -367,7 +367,7 @@ export const UploadIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -386,7 +386,7 @@ export const MenuIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -405,7 +405,7 @@ export const PencilEditIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -424,7 +424,7 @@ export const CheckedSquare = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -443,7 +443,7 @@ export const UncheckedSquare = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<rect <rect
x="1" x="1"
@ -465,7 +465,7 @@ export const MoreIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -484,7 +484,7 @@ export const TrashIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -503,7 +503,7 @@ export const InfoIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -522,7 +522,7 @@ export const ArrowUpIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -540,7 +540,7 @@ export const StopIcon = ({ size = 16 }: { size?: number }) => {
height={size} height={size}
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -559,7 +559,7 @@ export const PaperclipIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
className="-rotate-45" className="-rotate-45"
> >
<path <path
@ -579,7 +579,7 @@ export const MoreHorizontalIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -598,7 +598,7 @@ export const MessageIcon = ({ size = 16 }: { size?: number }) => {
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -616,7 +616,7 @@ export const CrossIcon = ({ size = 16 }: { size?: number }) => (
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -633,7 +633,7 @@ export const UndoIcon = ({ size = 16 }: { size?: number }) => (
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -650,7 +650,7 @@ export const RedoIcon = ({ size = 16 }: { size?: number }) => (
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -667,7 +667,7 @@ export const DeltaIcon = ({ size = 16 }: { size?: number }) => (
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -684,7 +684,7 @@ export const PenIcon = ({ size = 16 }: { size?: number }) => (
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -701,7 +701,7 @@ export const SummarizeIcon = ({ size = 16 }: { size?: number }) => (
strokeLinejoin="round" strokeLinejoin="round"
viewBox="0 0 16 16" viewBox="0 0 16 16"
width={size} width={size}
style={{ color: "currentcolor" }} style={{ color: 'currentcolor' }}
> >
<path <path
fillRule="evenodd" fillRule="evenodd"
@ -711,3 +711,37 @@ export const SummarizeIcon = ({ size = 16 }: { size?: number }) => (
></path> ></path>
</svg> </svg>
); );
export const SidebarLeftIcon = ({ size = 16 }: { size?: number }) => (
<svg
height={size}
strokeLinejoin="round"
viewBox="0 0 16 16"
width={size}
style={{ color: 'currentcolor' }}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.245 2.5H14.5V12.5C14.5 13.0523 14.0523 13.5 13.5 13.5H6.245V2.5ZM4.995 2.5H1.5V12.5C1.5 13.0523 1.94772 13.5 2.5 13.5H4.995V2.5ZM0 1H1.5H14.5H16V2.5V12.5C16 13.8807 14.8807 15 13.5 15H2.5C1.11929 15 0 13.8807 0 12.5V2.5V1Z"
fill="currentColor"
></path>
</svg>
);
export const PlusIcon = ({ size = 16 }: { size?: number }) => (
<svg
height={size}
strokeLinejoin="round"
viewBox="0 0 16 16"
width={size}
style={{ color: 'currentcolor' }}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.75 1.75V1H7.25V1.75V6.75H2.25H1.5V8.25H2.25H7.25V13.25V14H8.75V13.25V8.25H13.75H14.5V6.75H13.75H8.75V1.75Z"
fill="currentColor"
></path>
</svg>
);

View file

@ -34,7 +34,7 @@ export function ModelSelector({
<DropdownMenuTrigger <DropdownMenuTrigger
asChild asChild
className={cn( className={cn(
'w-fit data-[state=open]:bg-accent data-[state=open]:text-accent-foreground md:h-8 [&>svg]:!size-5 md:[&>svg]:!size-4', 'px-2 w-fit data-[state=open]:bg-accent data-[state=open]:text-accent-foreground md:h-8 [&>svg]:!size-5 md:[&>svg]:!size-4',
className className
)} )}
> >

View file

@ -1,5 +1,6 @@
'use client'; 'use client';
import { isToday, isYesterday, subMonths, subWeeks } from 'date-fns';
import Link from 'next/link'; import Link from 'next/link';
import { useParams, usePathname, useRouter } from 'next/navigation'; import { useParams, usePathname, useRouter } from 'next/navigation';
import { type User } from 'next-auth'; import { type User } from 'next-auth';
@ -41,6 +42,54 @@ import {
import { Chat } from '@/db/schema'; import { Chat } from '@/db/schema';
import { fetcher, getTitleFromChat } from '@/lib/utils'; import { fetcher, getTitleFromChat } from '@/lib/utils';
type GroupedChats = {
today: Chat[];
yesterday: Chat[];
lastWeek: Chat[];
lastMonth: Chat[];
older: Chat[];
};
const ChatItem = ({
chat,
isActive,
onDelete,
setOpenMobile,
}: {
chat: Chat;
isActive: boolean;
onDelete: (chatId: string) => void;
setOpenMobile: (open: boolean) => void;
}) => (
<SidebarMenuItem>
<SidebarMenuButton asChild isActive={isActive}>
<Link href={`/chat/${chat.id}`} onClick={() => setOpenMobile(false)}>
<span>{getTitleFromChat(chat)}</span>
</Link>
</SidebarMenuButton>
<DropdownMenu modal={true}>
<DropdownMenuTrigger asChild>
<SidebarMenuAction
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground mr-0.5"
showOnHover={!isActive}
>
<MoreHorizontalIcon />
<span className="sr-only">More</span>
</SidebarMenuAction>
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="end">
<DropdownMenuItem
className="cursor-pointer text-destructive focus:bg-destructive/15 focus:text-destructive dark:text-red-500"
onSelect={() => onDelete(chat.id)}
>
<TrashIcon />
<span>Delete</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
);
export function SidebarHistory({ user }: { user: User | undefined }) { export function SidebarHistory({ user }: { user: User | undefined }) {
const { setOpenMobile } = useSidebar(); const { setOpenMobile } = useSidebar();
const { id } = useParams(); const { id } = useParams();
@ -87,7 +136,9 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
if (!user) { if (!user) {
return ( return (
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel>History</SidebarGroupLabel> <div className="px-2 py-1 text-xs text-sidebar-foreground/50">
Today
</div>
<SidebarGroupContent> <SidebarGroupContent>
<div className="text-zinc-500 h-dvh w-full flex flex-row justify-center items-center text-sm gap-2"> <div className="text-zinc-500 h-dvh w-full flex flex-row justify-center items-center text-sm gap-2">
<InfoIcon /> <InfoIcon />
@ -101,7 +152,9 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
if (isLoading) { if (isLoading) {
return ( return (
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel>History</SidebarGroupLabel> <div className="px-2 py-1 text-xs text-sidebar-foreground/50">
Today
</div>
<SidebarGroupContent> <SidebarGroupContent>
<div className="flex flex-col"> <div className="flex flex-col">
{[44, 32, 28, 64, 52].map((item) => ( {[44, 32, 28, 64, 52].map((item) => (
@ -128,13 +181,15 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
if (history?.length === 0) { if (history?.length === 0) {
return ( return (
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel>History</SidebarGroupLabel> <div className="px-2 py-1 text-xs text-sidebar-foreground/50">
Today
</div>
<SidebarGroupContent> <SidebarGroupContent>
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> <SidebarMenuItem>
<SidebarMenuButton> <SidebarMenuButton>
<InfoIcon /> <InfoIcon />
<span>No chats found</span> <span>No previous chats</span>
</SidebarMenuButton> </SidebarMenuButton>
</SidebarMenuItem> </SidebarMenuItem>
</SidebarMenu> </SidebarMenu>
@ -143,48 +198,152 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
); );
} }
const groupChatsByDate = (chats: Chat[]): GroupedChats => {
const now = new Date();
const oneWeekAgo = subWeeks(now, 1);
const oneMonthAgo = subMonths(now, 1);
return chats.reduce(
(groups, chat) => {
const chatDate = new Date(chat.createdAt);
if (isToday(chatDate)) {
groups.today.push(chat);
} else if (isYesterday(chatDate)) {
groups.yesterday.push(chat);
} else if (chatDate > oneWeekAgo) {
groups.lastWeek.push(chat);
} else if (chatDate > oneMonthAgo) {
groups.lastMonth.push(chat);
} else {
groups.older.push(chat);
}
return groups;
},
{
today: [],
yesterday: [],
lastWeek: [],
lastMonth: [],
older: [],
} as GroupedChats
);
};
return ( return (
<> <>
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel>History</SidebarGroupLabel>
<SidebarGroupContent> <SidebarGroupContent>
<SidebarMenu> <SidebarMenu>
{history && {history &&
history.map((chat) => ( (() => {
<SidebarMenuItem key={chat.id}> const groupedChats = groupChatsByDate(history);
<SidebarMenuButton asChild isActive={chat.id === id}>
<Link return (
href={`/chat/${chat.id}`} <>
onClick={() => setOpenMobile(false)} {groupedChats.today.length > 0 && (
> <>
<span>{getTitleFromChat(chat)}</span> <div className="px-2 py-1 text-xs text-sidebar-foreground/50">
</Link> Today
</SidebarMenuButton> </div>
<DropdownMenu modal={true}> {groupedChats.today.map((chat) => (
<DropdownMenuTrigger asChild> <ChatItem
<SidebarMenuAction key={chat.id}
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground" chat={chat}
showOnHover={chat.id !== id} isActive={chat.id === id}
> onDelete={(chatId) => {
<MoreHorizontalIcon /> setDeleteId(chatId);
<span className="sr-only">More</span>
</SidebarMenuAction>
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="end">
<DropdownMenuItem
className="text-destructive focus:bg-destructive/15 focus:text-destructive"
onSelect={() => {
setDeleteId(chat.id);
setShowDeleteDialog(true); setShowDeleteDialog(true);
}} }}
> setOpenMobile={setOpenMobile}
<TrashIcon /> />
<span>Delete</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
))} ))}
</>
)}
{groupedChats.yesterday.length > 0 && (
<>
<div className="px-2 py-1 text-xs text-sidebar-foreground/50 mt-6">
Yesterday
</div>
{groupedChats.yesterday.map((chat) => (
<ChatItem
key={chat.id}
chat={chat}
isActive={chat.id === id}
onDelete={(chatId) => {
setDeleteId(chatId);
setShowDeleteDialog(true);
}}
setOpenMobile={setOpenMobile}
/>
))}
</>
)}
{groupedChats.lastWeek.length > 0 && (
<>
<div className="px-2 py-1 text-xs text-sidebar-foreground/50 mt-6">
Last 7 days
</div>
{groupedChats.lastWeek.map((chat) => (
<ChatItem
key={chat.id}
chat={chat}
isActive={chat.id === id}
onDelete={(chatId) => {
setDeleteId(chatId);
setShowDeleteDialog(true);
}}
setOpenMobile={setOpenMobile}
/>
))}
</>
)}
{groupedChats.lastMonth.length > 0 && (
<>
<div className="px-2 py-1 text-xs text-sidebar-foreground/50 mt-6">
Last 30 days
</div>
{groupedChats.lastMonth.map((chat) => (
<ChatItem
key={chat.id}
chat={chat}
isActive={chat.id === id}
onDelete={(chatId) => {
setDeleteId(chatId);
setShowDeleteDialog(true);
}}
setOpenMobile={setOpenMobile}
/>
))}
</>
)}
{groupedChats.older.length > 0 && (
<>
<div className="px-2 py-1 text-xs text-sidebar-foreground/50 mt-6">
Older
</div>
{groupedChats.older.map((chat) => (
<ChatItem
key={chat.id}
chat={chat}
isActive={chat.id === id}
onDelete={(chatId) => {
setDeleteId(chatId);
setShowDeleteDialog(true);
}}
setOpenMobile={setOpenMobile}
/>
))}
</>
)}
</>
);
})()}
</SidebarMenu> </SidebarMenu>
</SidebarGroupContent> </SidebarGroupContent>
</SidebarGroup> </SidebarGroup>

View file

@ -1,20 +1,26 @@
import { ComponentProps } from 'react'; import { ComponentProps } from 'react';
import { SidebarTrigger } from '@/components/ui/sidebar'; import { SidebarTrigger, useSidebar } from '@/components/ui/sidebar';
import { BetterTooltip } from '@/components/ui/tooltip'; import { BetterTooltip } from '@/components/ui/tooltip';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { SidebarLeftIcon } from './icons';
import { Button } from '../ui/button';
export function SidebarToggle({ export function SidebarToggle({
className, className,
}: ComponentProps<typeof SidebarTrigger>) { }: ComponentProps<typeof SidebarTrigger>) {
const { toggleSidebar } = useSidebar();
return ( return (
<BetterTooltip content="Toggle Sidebar" align="start"> <BetterTooltip content="Toggle Sidebar" align="start">
<SidebarTrigger <Button
className={cn( onClick={toggleSidebar}
'size-10 md:size-8 [&>svg]:!size-5 md:[&>svg]:!size-4', variant="ghost"
className className="md:px-2 md:h-fit"
)} >
/> <SidebarLeftIcon />
</Button>
</BetterTooltip> </BetterTooltip>
); );
} }

View file

@ -20,6 +20,7 @@ import {
export function SidebarUserNav({ user }: { user: User }) { export function SidebarUserNav({ user }: { user: User }) {
const { setTheme, theme } = useTheme(); const { setTheme, theme } = useTheme();
return ( return (
<SidebarMenu> <SidebarMenu>
<SidebarMenuItem> <SidebarMenuItem>
@ -42,6 +43,7 @@ export function SidebarUserNav({ user }: { user: User }) {
className="w-[--radix-popper-anchor-width]" className="w-[--radix-popper-anchor-width]"
> >
<DropdownMenuItem <DropdownMenuItem
className="cursor-pointer"
onSelect={() => setTheme(theme === 'dark' ? 'light' : 'dark')} onSelect={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
> >
{`Toggle ${theme === 'light' ? 'dark' : 'light'} mode`} {`Toggle ${theme === 'light' ? 'dark' : 'light'} mode`}
@ -49,7 +51,7 @@ export function SidebarUserNav({ user }: { user: User }) {
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<button <button
className="w-full " className="w-full cursor-pointer"
onClick={() => { onClick={() => {
signOut({ signOut({
redirectTo: '/', redirectTo: '/',

View file

@ -73,6 +73,5 @@ const config: Config = {
}, },
}, },
plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')], plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')],
safelist: ['w-32', 'w-44', 'w-52'],
}; };
export default config; export default config;