diff --git a/client/src/components/Conversations/Fork.tsx b/client/src/components/Conversations/Fork.tsx index 5aeb62aab4..381134f87c 100644 --- a/client/src/components/Conversations/Fork.tsx +++ b/client/src/components/Conversations/Fork.tsx @@ -1,21 +1,13 @@ 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 * 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 { TranslationKeys, 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'; @@ -24,11 +16,11 @@ interface PopoverButtonProps { setting: ForkOptions; onClick: (setting: ForkOptions) => void; setActiveSetting: React.Dispatch>; - sideOffset?: number; timeoutRef: React.MutableRefObject; hoverInfo?: React.ReactNode | string; hoverTitle?: React.ReactNode | string; hoverDescription?: React.ReactNode | string; + label: string; } const optionLabels: Record = { @@ -38,57 +30,83 @@ const optionLabels: Record = { [ForkOptions.DEFAULT]: 'com_ui_fork_from_message', }; +const chevronDown = ( + + + +); + const PopoverButton: React.FC = ({ children, setting, onClick, setActiveSetting, - sideOffset = 30, timeoutRef, hoverInfo, hoverTitle, hoverDescription, + label, }) => { + const localize = useLocalize(); return ( - - onClick(setting)} - onMouseEnter={() => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - timeoutRef.current = null; + +
+ 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} + } - 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 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 !== '')) && ( - - + /> + + + {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}

-
-
- )} - + + )} +
+
); }; @@ -114,6 +132,9 @@ export default function Fork({ 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); @@ -157,12 +178,12 @@ export default function Fork({ }; return ( - - + <> + - - -
- -
- {localize(activeSetting )} - - - - - - + +
+ {localize(activeSetting)} + +
+ -
- {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(checked)} - className="m-2 transition duration-300 ease-in-out" - /> - {localize('com_ui_fork_split_target')} -
-
- -
- - -
- + {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" + > + { + onChange={(event) => { + const checked = event.target.checked; + console.log('checked', checked); if (checked) { showToast({ message: localize('com_ui_fork_remember_checked'), status: 'info', }); } - setRemember(checked); + return setRemember(checked); }} - className="m-2 transition duration-300 ease-in-out" + 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_remember')} +
- - - -
-
-
-
+ } + /> + + + {localize('com_ui_fork_more_info_remember', { + 0: localize('com_ui_fork_remember'), + })} + + {chevronDown} + + + +
+

{localize('com_ui_fork_info_remember')}

+
+
+ + + ); } diff --git a/client/src/components/Nav/SettingsTabs/Chat/ForkSettings.tsx b/client/src/components/Nav/SettingsTabs/Chat/ForkSettings.tsx index a44b3d63c6..a01fb6ab51 100644 --- a/client/src/components/Nav/SettingsTabs/Chat/ForkSettings.tsx +++ b/client/src/components/Nav/SettingsTabs/Chat/ForkSettings.tsx @@ -44,6 +44,7 @@ export const ForkSettings = () => { options={forkOptions} sizeClasses="w-[200px]" testId="fork-setting-dropdown" + className="z-[50]" /> diff --git a/client/src/components/ui/SplitText.tsx b/client/src/components/ui/SplitText.tsx index 962d413007..8a37def906 100644 --- a/client/src/components/ui/SplitText.tsx +++ b/client/src/components/ui/SplitText.tsx @@ -90,33 +90,37 @@ const SplitText: React.FC = ({ }, [inView, text, onLineCountChange]); return ( -

- {words.map((word, wordIndex) => ( - - {word.map((letter, letterIndex) => { - const index = - words.slice(0, wordIndex).reduce((acc, w) => acc + w.length, 0) + letterIndex; + <> + {text} +

+ return ( + + {letter} + + ); + })} + {wordIndex < words.length - 1 && ( +   + )} + + ))} +

+ ); }; diff --git a/client/src/locales/en/translation.json b/client/src/locales/en/translation.json index d8e572eb2d..b22cfe5128 100644 --- a/client/src/locales/en/translation.json +++ b/client/src/locales/en/translation.json @@ -864,5 +864,10 @@ "com_ui_yes": "Yes", "com_ui_zoom": "Zoom", "com_user_message": "You", - "com_warning_resubmit_unsupported": "Resubmitting the AI message is not supported for this endpoint." -} \ No newline at end of file + "com_warning_resubmit_unsupported": "Resubmitting the AI message is not supported for this endpoint.", + "com_ui_fork_more_details_about": "View additional information and details about the \"{{0}}\" fork option", + "com_ui_fork_more_info_options": "View detailed explanation of all fork options and their behaviors", + "com_ui_fork_info_button_label": "View information about forking conversations", + "com_ui_fork_more_info_split_target": "View explanation of how the \"{{0}}\" option affects which messages are included in your fork", + "com_ui_fork_more_info_remember": "View explanation of how the \"{{0}}\" option saves your preferences for future forks" +}