|
import { Menu, Transition } from '@headlessui/react'; |
|
import { Fragment, useState } from 'react'; |
|
import { useRecoilValue } from 'recoil'; |
|
import SearchBar from './SearchBar'; |
|
import Settings from './Settings'; |
|
import { Download } from 'lucide-react'; |
|
import NavLink from './NavLink'; |
|
import ExportModel from './ExportConversation/ExportModel'; |
|
import ClearConvos from './ClearConvos'; |
|
import Logout from './Logout'; |
|
import { useAuthContext } from '~/hooks/AuthContext'; |
|
import { cn } from '~/utils/'; |
|
|
|
import store from '~/store'; |
|
import { LinkIcon, DotsIcon, GearIcon, TrashIcon } from '~/components'; |
|
import { localize } from '~/localization/Translation'; |
|
|
|
export default function NavLinks({ clearSearch, isSearchEnabled }) { |
|
const [showExports, setShowExports] = useState(false); |
|
const [showClearConvos, setShowClearConvos] = useState(false); |
|
const [showSettings, setShowSettings] = useState(false); |
|
const { user } = useAuthContext(); |
|
const lang = useRecoilValue(store.lang); |
|
|
|
const conversation = useRecoilValue(store.conversation) || {}; |
|
|
|
const exportable = |
|
conversation?.conversationId && |
|
conversation?.conversationId !== 'new' && |
|
conversation?.conversationId !== 'search'; |
|
|
|
const clickHandler = () => { |
|
if (exportable) { |
|
setShowExports(true); |
|
} |
|
}; |
|
|
|
return ( |
|
<> |
|
<Menu as="div" className="group relative"> |
|
{({ open }) => ( |
|
<> |
|
<Menu.Button |
|
className={cn( |
|
'group-ui-open:bg-gray-800 flex w-full items-center gap-2.5 rounded-md px-3 py-3 text-sm transition-colors duration-200 hover:bg-gray-800', |
|
open ? 'bg-gray-800' : '', |
|
)} |
|
> |
|
<div className="-ml-0.5 h-5 w-5 flex-shrink-0"> |
|
<div className="relative flex"> |
|
<img |
|
className="rounded-sm" |
|
src={ |
|
user?.avatar || |
|
`https://api.dicebear.com/6.x/initials/svg?seed=${ |
|
user?.name || 'User' |
|
}&fontFamily=Verdana&fontSize=36` |
|
} |
|
alt="" |
|
/> |
|
</div> |
|
</div> |
|
<div className="grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-white"> |
|
{user?.name || localize(lang, 'com_nav_user')} |
|
</div> |
|
<DotsIcon /> |
|
</Menu.Button> |
|
|
|
<Transition |
|
as={Fragment} |
|
enter="transition ease-out duration-100" |
|
enterFrom="transform opacity-0 scale-95" |
|
enterTo="transform opacity-100 scale-100" |
|
leave="transition ease-in duration-75" |
|
leaveFrom="transform opacity-100 scale-100" |
|
leaveTo="transform opacity-0 scale-95" |
|
> |
|
<Menu.Items className="absolute bottom-full left-0 z-20 mb-2 w-full translate-y-0 overflow-hidden rounded-md bg-[#050509] py-1.5 opacity-100 outline-none"> |
|
{isSearchEnabled && ( |
|
<Menu.Item> |
|
<SearchBar clearSearch={clearSearch} /> |
|
</Menu.Item> |
|
)} |
|
<Menu.Item as="div"> |
|
<NavLink |
|
className={cn( |
|
'flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700', |
|
exportable ? 'cursor-pointer text-white' : 'cursor-not-allowed text-white/50', |
|
)} |
|
svg={() => <Download size={16} />} |
|
text={localize(lang, 'com_nav_export_conversation')} |
|
clickHandler={clickHandler} |
|
/> |
|
</Menu.Item> |
|
<div className="my-1.5 h-px bg-white/20" role="none" /> |
|
<Menu.Item as="div"> |
|
<NavLink |
|
className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700" |
|
svg={() => <TrashIcon />} |
|
text={localize(lang, 'com_nav_clear_conversation')} |
|
clickHandler={() => setShowClearConvos(true)} |
|
/> |
|
</Menu.Item> |
|
<Menu.Item as="div"> |
|
<NavLink |
|
className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700" |
|
svg={() => <LinkIcon />} |
|
text={localize(lang, 'com_nav_help_faq')} |
|
clickHandler={() => window.open('https://docs.librechat.ai/', '_blank')} |
|
/> |
|
</Menu.Item> |
|
<Menu.Item as="div"> |
|
<NavLink |
|
className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700" |
|
svg={() => <GearIcon />} |
|
text={localize(lang, 'com_nav_settings')} |
|
clickHandler={() => setShowSettings(true)} |
|
/> |
|
</Menu.Item> |
|
<div className="my-1.5 h-px bg-white/20" role="none" /> |
|
<Menu.Item as="div"> |
|
<Logout /> |
|
</Menu.Item> |
|
</Menu.Items> |
|
</Transition> |
|
</> |
|
)} |
|
</Menu> |
|
{showExports && <ExportModel open={showExports} onOpenChange={setShowExports} />} |
|
{showClearConvos && <ClearConvos open={showClearConvos} onOpenChange={setShowClearConvos} />} |
|
{showSettings && <Settings open={showSettings} onOpenChange={setShowSettings} />} |
|
</> |
|
); |
|
} |
|
|