import React, { useState, useRef } from 'react'; import { useRecoilState } from 'recoil'; import * as Ariakit from '@ariakit/react'; import { VisuallyHidden } from '@ariakit/react'; import { GitFork, InfoIcon } from 'lucide-react'; import { ForkOptions } from 'librechat-data-provider'; import { GitCommit, GitBranchPlus, ListTree } from 'lucide-react'; import { TranslationKeys, useLocalize, useNavigateToConvo } from '~/hooks'; import { useForkConvoMutation } from '~/data-provider'; import { useToastContext } from '~/Providers'; import { cn } from '~/utils'; import store from '~/store'; interface PopoverButtonProps { children: React.ReactNode; setting: ForkOptions; onClick: (setting: ForkOptions) => void; setActiveSetting: React.Dispatch>; timeoutRef: React.MutableRefObject; hoverInfo?: React.ReactNode | string; hoverTitle?: React.ReactNode | string; hoverDescription?: React.ReactNode | string; label: string; } const optionLabels: Record = { [ForkOptions.DIRECT_PATH]: 'com_ui_fork_visible', [ForkOptions.INCLUDE_BRANCHES]: 'com_ui_fork_branches', [ForkOptions.TARGET_LEVEL]: 'com_ui_fork_all_target', [ForkOptions.DEFAULT]: 'com_ui_fork_from_message', }; const chevronDown = ( ); const PopoverButton: React.FC = ({ children, setting, onClick, setActiveSetting, timeoutRef, hoverInfo, hoverTitle, hoverDescription, label, }) => { const localize = useLocalize(); 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[ForkOptions.DEFAULT]); }, 175); }} className="mx-1 max-w-14 flex-1 rounded-lg border-2 border-border-medium bg-surface-secondary text-text-secondary transition duration-300 ease-in-out hover:border-border-xheavy hover:bg-surface-hover hover:text-text-primary" aria-label={label} > {children} {label} } /> {localize('com_ui_fork_more_details_about', { 0: label })} {chevronDown} {((hoverInfo != null && hoverInfo !== '') || (hoverTitle != null && hoverTitle !== '') || (hoverDescription != null && hoverDescription !== '')) && (

{hoverInfo && hoverInfo} {hoverTitle && {hoverTitle}} {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 popoverStore = Ariakit.usePopoverStore({ placement: 'top', }); 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 ( <> { if (rememberGlobal) { e.preventDefault(); forkConvo.mutate({ messageId, splitAtTarget, conversationId, option: forkSetting, latestMessageId, }); } else { popoverStore.toggle(); } }} type="button" aria-label={localize('com_ui_fork')} > } />
{localize(activeSetting)}
} /> {localize('com_ui_fork_more_info_options')} {chevronDown}
{localize('com_ui_fork_info_1')} {localize('com_ui_fork_info_2')} {localize('com_ui_fork_info_3', { 0: 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(event.target.checked)} className="m-2 h-4 w-4 rounded-sm border border-primary ring-offset-background transition duration-300 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground" aria-label={localize('com_ui_fork_split_target')} />
} /> {localize('com_ui_fork_more_info_split_target', { 0: localize('com_ui_fork_split_target'), })} {chevronDown}

{localize('com_ui_fork_info_start')}

setRemember((prev) => !prev)} className="flex h-6 w-full select-none items-center justify-start rounded-md text-sm text-text-secondary hover:text-text-primary" > { const checked = event.target.checked; console.log('checked', checked); if (checked) { showToast({ message: localize('com_ui_fork_remember_checked'), status: 'info', }); } return setRemember(checked); }} className="m-2 h-4 w-4 rounded-sm border border-primary ring-offset-background transition duration-300 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground" aria-label={localize('com_ui_fork_remember')} />
} /> {localize('com_ui_fork_more_info_remember', { 0: localize('com_ui_fork_remember'), })} {chevronDown}

{localize('com_ui_fork_info_remember')}

); }