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 } 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 { useLocalize, useNavigateToConvo } from '~/hooks'; import { useForkConvoMutation } from '~/data-provider'; import { useToastContext } from '~/Providers'; 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 | string; hoverTitle?: React.ReactNode | string; hoverDescription?: React.ReactNode | string; } 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 text-gray-700 transition duration-300 ease-in-out hover:bg-gray-200 hover:text-gray-900 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-400 dark:hover:bg-gray-600 dark:hover:text-gray-100 " type="button" > {children} {((hoverInfo != null && hoverInfo !== '') || (hoverTitle != null && hoverTitle !== '') || (hoverDescription != null && hoverDescription !== '')) && (

{hoverInfo != null && hoverInfo !== '' && hoverInfo} {hoverTitle != null && hoverTitle !== '' && ( {hoverTitle} )} {hoverDescription != null && hoverDescription !== '' && hoverDescription}

)}
); }; export default function Fork({ isLast = false, messageId, conversationId: _convoId, forkingSupported = false, latestMessageId, }: { isLast?: boolean; messageId: string; conversationId: string | null; forkingSupported?: boolean; latestMessageId?: string; }) { const localize = useLocalize(); const { showToast } = useToastContext(); const [remember, setRemember] = useState(false); const { navigateToConvo } = useNavigateToConvo(); 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.rememberDefaultFork); const forkConvo = useForkConvoMutation({ onSuccess: (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', }); }, }); const conversationId = _convoId ?? ''; if (!forkingSupported || !conversationId || !messageId) { return null; } const onClick = (option: string) => { if (remember) { setRememberGlobal(true); setForkSetting(option); } forkConvo.mutate({ messageId, conversationId, option, splitAtTarget, latestMessageId, }); }; 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')}
); }