tabby/ee/tabby-ui/app/playground/components/chat-sessions.tsx

128 lines
4.2 KiB
TypeScript

'use client'
import React from 'react'
import { useStore } from '@/lib/hooks/use-store'
import {
clearChats,
deleteChat,
setActiveChatId
} from '@/lib/stores/chat-actions'
import { useChatStore } from '@/lib/stores/chat-store'
import { cn, nanoid } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { IconPlus, IconTrash } from '@/components/ui/icons'
import { Separator } from '@/components/ui/separator'
import {
Tooltip,
TooltipContent,
TooltipTrigger
} from '@/components/ui/tooltip'
import { ListSkeleton } from '@/components/skeleton'
import { ClearChatsButton } from './clear-chats-button'
import { EditChatTitleDialog } from './edit-chat-title-dialog'
interface ChatSessionsProps {
className?: string
}
export const ChatSessions = ({ className }: ChatSessionsProps) => {
const _hasHydrated = useStore(useChatStore, state => state._hasHydrated)
const chats = useStore(useChatStore, state => state.chats)
const activeChatId = useStore(useChatStore, state => state.activeChatId)
const onDeleteClick = (
e: React.MouseEvent<HTMLButtonElement>,
chatId: string
) => {
deleteChat(chatId)
}
const onNewChatClick = (e: React.MouseEvent<HTMLButtonElement>) => {
setActiveChatId(nanoid())
}
const handleClearChats = () => {
clearChats()
}
return (
<>
<div className={cn(className)}>
<div className="fixed inset-y-0 left-0 flex w-[279px] flex-col gap-2 overflow-hidden px-3 pt-16">
<div className="shrink-0 pb-0 pt-2">
<Button
className="h-12 w-full justify-start"
variant="ghost"
onClick={onNewChatClick}
>
<IconPlus />
<span className="ml-2">New Chat</span>
</Button>
</div>
<Separator />
<div className="flex flex-1 flex-col gap-2 overflow-y-auto">
{!_hasHydrated ? (
<ListSkeleton />
) : (
<>
{chats?.map(chat => {
const isActive = activeChatId === chat.id
return (
<div
key={chat.id}
onClick={e => setActiveChatId(chat.id)}
className={cn(
'flex cursor-pointer items-center justify-between gap-3 rounded-lg px-3 py-2 text-zinc-900 transition-all hover:bg-accent hover:text-zinc-900 dark:text-zinc-50 hover:dark:bg-zinc-900 dark:hover:text-zinc-50',
isActive && '!bg-zinc-200 dark:!bg-zinc-800'
)}
>
<span className="truncate leading-8">
{chat.title || '(Untitled)'}
</span>
{isActive && (
<div
className="flex items-center"
onClick={e => e.stopPropagation()}
>
<EditChatTitleDialog
initialValue={chat.title}
chatId={chat.id}
/>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={e => onDeleteClick(e, chat.id)}
>
<IconTrash />
<span className="sr-only">Delete</span>
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
Delete
</TooltipContent>
</Tooltip>
</div>
)}
</div>
)
})}
</>
)}
</div>
<Separator />
<div className="shrink-0 pb-2">
<ClearChatsButton
disabled={chats?.length === 0}
onClear={handleClearChats}
/>
</div>
</div>
</div>
</>
)
}