import { useState, useRef } from 'react'; import { useRecoilState } from 'recoil'; import { GitFork, InfoIcon } from 'lucide-react'; import * as Popover from '@radix-ui/react-popover'; import { ForkOptions, TMessage } from 'librechat-data-provider'; import { GitCommit, GitBranchPlus, ListTree } from 'lucide-react'; import { Checkbox, HoverCard, HoverCardTrigger, HoverCardPortal, HoverCardContent, } from '~/components/ui'; import OptionHover from '~/components/SidePanel/Parameters/OptionHover'; import { useToastContext, useChatContext } from '~/Providers'; import { useLocalize, useNavigateToConvo } from '~/hooks'; import { useForkConvoMutation } from '~/data-provider'; import { ESide } from '~/common'; import { cn } from '~/utils'; import store from '~/store'; interface PopoverButtonProps { children: React.ReactNode; setting: string; onClick: (setting: string) => void; setActiveSetting: React.Dispatch>; sideOffset?: number; timeoutRef: React.MutableRefObject; hoverInfo?: React.ReactNode; hoverTitle?: React.ReactNode; hoverDescription?: React.ReactNode; } const optionLabels = { [ForkOptions.DIRECT_PATH]: 'com_ui_fork_visible', [ForkOptions.INCLUDE_BRANCHES]: 'com_ui_fork_branches', [ForkOptions.TARGET_LEVEL]: 'com_ui_fork_all_target', default: 'com_ui_fork_from_message', }; const PopoverButton: React.FC = ({ children, setting, onClick, setActiveSetting, sideOffset = 30, timeoutRef, hoverInfo, hoverTitle, hoverDescription, }) => { return ( onClick(setting)} onMouseEnter={() => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } setActiveSetting(optionLabels[setting]); }} onMouseLeave={() => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } timeoutRef.current = setTimeout(() => { setActiveSetting(optionLabels.default); }, 175); }} className="mx-1 max-w-14 flex-1 rounded-lg border-2 bg-white transition duration-300 ease-in-out hover:bg-black dark:border-gray-400 dark:bg-gray-700/95 dark:text-gray-400 hover:dark:border-gray-200 hover:dark:text-gray-200" type="button" > {children} {(hoverInfo || hoverTitle || hoverDescription) && (

{hoverInfo && hoverInfo} {hoverTitle && {hoverTitle}} {hoverDescription && hoverDescription}

)}
); }; export default function Fork({ isLast, messageId, conversationId, forkingSupported, latestMessage, }: { isLast?: boolean; messageId: string; conversationId: string | null; forkingSupported?: boolean; latestMessage: TMessage | null; }) { const localize = useLocalize(); const { index } = useChatContext(); const { showToast } = useToastContext(); const [remember, setRemember] = useState(false); const { navigateToConvo } = useNavigateToConvo(index); const timeoutRef = useRef(null); const [forkSetting, setForkSetting] = useRecoilState(store.forkSetting); const [activeSetting, setActiveSetting] = useState(optionLabels.default); const [splitAtTarget, setSplitAtTarget] = useRecoilState(store.splitAtTarget); const [rememberGlobal, setRememberGlobal] = useRecoilState(store.rememberForkOption); const forkConvo = useForkConvoMutation({ onSuccess: (data) => { if (data) { navigateToConvo(data.conversation); showToast({ message: localize('com_ui_fork_success'), status: 'success', }); } }, onMutate: () => { showToast({ message: localize('com_ui_fork_processing'), status: 'info', }); }, onError: () => { showToast({ message: localize('com_ui_fork_error'), status: 'error', }); }, }); if (!forkingSupported || !conversationId || !messageId) { return null; } const onClick = (option: string) => { if (remember) { setRememberGlobal(true); setForkSetting(option); } forkConvo.mutate({ messageId, conversationId, option, splitAtTarget, latestMessageId: latestMessage?.messageId, }); }; return (
{localize(activeSetting)}
{localize('com_ui_fork_info_1')} {localize('com_ui_fork_info_2')} {localize('com_ui_fork_info_3', localize('com_ui_fork_split_target'))}
{localize(optionLabels[ForkOptions.DIRECT_PATH])} } hoverDescription={localize('com_ui_fork_info_visible')} > {localize(optionLabels[ForkOptions.INCLUDE_BRANCHES])} } hoverDescription={localize('com_ui_fork_info_branches')} > {`${localize(optionLabels[ForkOptions.TARGET_LEVEL])} (${localize( 'com_endpoint_default', )})`} } hoverDescription={localize('com_ui_fork_info_target')} >
setSplitAtTarget(checked)} className="m-2 transition duration-300 ease-in-out" /> {localize('com_ui_fork_split_target')}
{ if (checked) { showToast({ message: localize('com_ui_fork_remember_checked'), status: 'info', }); } setRemember(checked); }} className="m-2 transition duration-300 ease-in-out" /> {localize('com_ui_fork_remember')}
); }