🌿 feat: Fork Messages/Conversations (#2617)
* typedef for ImportBatchBuilder
* feat: first pass, fork conversations
* feat: fork - getMessagesUpToTargetLevel
* fix: additional tests and fix getAllMessagesUpToParent
* chore: arrow function return
* refactor: fork 3 options
* chore: remove unused genbuttons
* chore: remove unused hover buttons code
* feat: fork first pass
* wip: fork remember setting
* style: user icon
* chore: move clear chats to data tab
* WIP: fork UI options
* feat: data-provider fork types/services/vars and use generic MutationOptions
* refactor: use single param for fork option, use enum, fix mongo errors, use Date.now(), add records flag for testing, use endpoint from original convo and messages, pass originalConvo to finishConversation
* feat: add fork mutation hook and consolidate type imports
* refactor: use enum
* feat: first pass, fork mutation
* chore: add enum for target level fork option
* chore: add enum for target level fork option
* show toast when checking remember selection
* feat: splitAtTarget
* feat: split at target option
* feat: navigate to new fork, show toasts, set result query data
* feat: hover info for all fork options
* refactor: add Messages settings tab
* fix(Fork): remember text info
* ci: test for single message and is target edge case
* feat: additional tests for getAllMessagesUpToParent
* ci: additional tests and cycle detection for getMessagesUpToTargetLevel
* feat: circular dependency checks for getAllMessagesUpToParent
* fix: getMessagesUpToTargetLevel circular dep. check
* ci: more tests for getMessagesForConversation
* style: hover text for checkbox fork items
* refactor: add statefulness to conversation import
2024-05-05 11:48:20 -04:00
|
|
|
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<React.SetStateAction<string>>;
|
|
|
|
|
sideOffset?: number;
|
|
|
|
|
timeoutRef: React.MutableRefObject<NodeJS.Timeout | null>;
|
|
|
|
|
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<PopoverButtonProps> = ({
|
|
|
|
|
children,
|
|
|
|
|
setting,
|
|
|
|
|
onClick,
|
|
|
|
|
setActiveSetting,
|
|
|
|
|
sideOffset = 30,
|
|
|
|
|
timeoutRef,
|
|
|
|
|
hoverInfo,
|
|
|
|
|
hoverTitle,
|
|
|
|
|
hoverDescription,
|
|
|
|
|
}) => {
|
|
|
|
|
return (
|
|
|
|
|
<HoverCard openDelay={200}>
|
|
|
|
|
<Popover.Close
|
|
|
|
|
onClick={() => 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);
|
|
|
|
|
}}
|
2024-05-05 21:35:16 +02:00
|
|
|
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 "
|
🌿 feat: Fork Messages/Conversations (#2617)
* typedef for ImportBatchBuilder
* feat: first pass, fork conversations
* feat: fork - getMessagesUpToTargetLevel
* fix: additional tests and fix getAllMessagesUpToParent
* chore: arrow function return
* refactor: fork 3 options
* chore: remove unused genbuttons
* chore: remove unused hover buttons code
* feat: fork first pass
* wip: fork remember setting
* style: user icon
* chore: move clear chats to data tab
* WIP: fork UI options
* feat: data-provider fork types/services/vars and use generic MutationOptions
* refactor: use single param for fork option, use enum, fix mongo errors, use Date.now(), add records flag for testing, use endpoint from original convo and messages, pass originalConvo to finishConversation
* feat: add fork mutation hook and consolidate type imports
* refactor: use enum
* feat: first pass, fork mutation
* chore: add enum for target level fork option
* chore: add enum for target level fork option
* show toast when checking remember selection
* feat: splitAtTarget
* feat: split at target option
* feat: navigate to new fork, show toasts, set result query data
* feat: hover info for all fork options
* refactor: add Messages settings tab
* fix(Fork): remember text info
* ci: test for single message and is target edge case
* feat: additional tests for getAllMessagesUpToParent
* ci: additional tests and cycle detection for getMessagesUpToTargetLevel
* feat: circular dependency checks for getAllMessagesUpToParent
* fix: getMessagesUpToTargetLevel circular dep. check
* ci: more tests for getMessagesForConversation
* style: hover text for checkbox fork items
* refactor: add statefulness to conversation import
2024-05-05 11:48:20 -04:00
|
|
|
type="button"
|
|
|
|
|
>
|
|
|
|
|
{children}
|
|
|
|
|
</Popover.Close>
|
|
|
|
|
{(hoverInfo || hoverTitle || hoverDescription) && (
|
|
|
|
|
<HoverCardPortal>
|
|
|
|
|
<HoverCardContent
|
|
|
|
|
side="right"
|
|
|
|
|
className="z-[999] w-80 dark:bg-gray-700"
|
|
|
|
|
sideOffset={sideOffset}
|
|
|
|
|
>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<p className="flex flex-col gap-2 text-sm text-gray-600 dark:text-gray-300">
|
|
|
|
|
{hoverInfo && hoverInfo}
|
|
|
|
|
{hoverTitle && <span className="flex flex-wrap gap-1 font-bold">{hoverTitle}</span>}
|
|
|
|
|
{hoverDescription && hoverDescription}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</HoverCardContent>
|
|
|
|
|
</HoverCardPortal>
|
|
|
|
|
)}
|
|
|
|
|
</HoverCard>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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<NodeJS.Timeout | null>(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 (
|
|
|
|
|
<Popover.Root>
|
|
|
|
|
<Popover.Trigger asChild>
|
|
|
|
|
<button
|
|
|
|
|
className={cn(
|
|
|
|
|
'hover-button active rounded-md p-1 hover:bg-gray-200 hover:text-gray-700 dark:text-gray-400/70 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400 md:invisible md:group-hover:visible ',
|
|
|
|
|
'data-[state=open]:active data-[state=open]:bg-gray-200 data-[state=open]:text-gray-700 data-[state=open]:dark:bg-gray-700 data-[state=open]:dark:text-gray-200',
|
|
|
|
|
!isLast ? 'data-[state=open]:opacity-100 md:opacity-0 md:group-hover:opacity-100' : '',
|
|
|
|
|
)}
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
if (rememberGlobal) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
forkConvo.mutate({
|
|
|
|
|
messageId,
|
|
|
|
|
splitAtTarget,
|
|
|
|
|
conversationId,
|
|
|
|
|
option: forkSetting,
|
|
|
|
|
latestMessageId: latestMessage?.messageId,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
type="button"
|
2024-05-07 13:13:55 -04:00
|
|
|
title={localize('com_ui_fork')}
|
🌿 feat: Fork Messages/Conversations (#2617)
* typedef for ImportBatchBuilder
* feat: first pass, fork conversations
* feat: fork - getMessagesUpToTargetLevel
* fix: additional tests and fix getAllMessagesUpToParent
* chore: arrow function return
* refactor: fork 3 options
* chore: remove unused genbuttons
* chore: remove unused hover buttons code
* feat: fork first pass
* wip: fork remember setting
* style: user icon
* chore: move clear chats to data tab
* WIP: fork UI options
* feat: data-provider fork types/services/vars and use generic MutationOptions
* refactor: use single param for fork option, use enum, fix mongo errors, use Date.now(), add records flag for testing, use endpoint from original convo and messages, pass originalConvo to finishConversation
* feat: add fork mutation hook and consolidate type imports
* refactor: use enum
* feat: first pass, fork mutation
* chore: add enum for target level fork option
* chore: add enum for target level fork option
* show toast when checking remember selection
* feat: splitAtTarget
* feat: split at target option
* feat: navigate to new fork, show toasts, set result query data
* feat: hover info for all fork options
* refactor: add Messages settings tab
* fix(Fork): remember text info
* ci: test for single message and is target edge case
* feat: additional tests for getAllMessagesUpToParent
* ci: additional tests and cycle detection for getMessagesUpToTargetLevel
* feat: circular dependency checks for getAllMessagesUpToParent
* fix: getMessagesUpToTargetLevel circular dep. check
* ci: more tests for getMessagesForConversation
* style: hover text for checkbox fork items
* refactor: add statefulness to conversation import
2024-05-05 11:48:20 -04:00
|
|
|
>
|
|
|
|
|
<GitFork className="h-4 w-4 hover:text-gray-700 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400" />
|
|
|
|
|
</button>
|
|
|
|
|
</Popover.Trigger>
|
|
|
|
|
<Popover.Portal>
|
|
|
|
|
<div dir="ltr">
|
|
|
|
|
<Popover.Content
|
|
|
|
|
side="top"
|
|
|
|
|
role="menu"
|
2024-05-05 21:35:16 +02:00
|
|
|
className="bg-token-surface-primary flex min-h-[120px] min-w-[215px] flex-col gap-3 overflow-hidden rounded-lg bg-white p-2 px-3 shadow-lg dark:bg-gray-700"
|
🌿 feat: Fork Messages/Conversations (#2617)
* typedef for ImportBatchBuilder
* feat: first pass, fork conversations
* feat: fork - getMessagesUpToTargetLevel
* fix: additional tests and fix getAllMessagesUpToParent
* chore: arrow function return
* refactor: fork 3 options
* chore: remove unused genbuttons
* chore: remove unused hover buttons code
* feat: fork first pass
* wip: fork remember setting
* style: user icon
* chore: move clear chats to data tab
* WIP: fork UI options
* feat: data-provider fork types/services/vars and use generic MutationOptions
* refactor: use single param for fork option, use enum, fix mongo errors, use Date.now(), add records flag for testing, use endpoint from original convo and messages, pass originalConvo to finishConversation
* feat: add fork mutation hook and consolidate type imports
* refactor: use enum
* feat: first pass, fork mutation
* chore: add enum for target level fork option
* chore: add enum for target level fork option
* show toast when checking remember selection
* feat: splitAtTarget
* feat: split at target option
* feat: navigate to new fork, show toasts, set result query data
* feat: hover info for all fork options
* refactor: add Messages settings tab
* fix(Fork): remember text info
* ci: test for single message and is target edge case
* feat: additional tests for getAllMessagesUpToParent
* ci: additional tests and cycle detection for getMessagesUpToTargetLevel
* feat: circular dependency checks for getAllMessagesUpToParent
* fix: getMessagesUpToTargetLevel circular dep. check
* ci: more tests for getMessagesForConversation
* style: hover text for checkbox fork items
* refactor: add statefulness to conversation import
2024-05-05 11:48:20 -04:00
|
|
|
style={{ outline: 'none', pointerEvents: 'auto', boxSizing: 'border-box' }}
|
|
|
|
|
tabIndex={-1}
|
|
|
|
|
sideOffset={5}
|
|
|
|
|
align="center"
|
|
|
|
|
>
|
|
|
|
|
<div className="flex h-6 w-full items-center justify-center text-sm dark:text-gray-200">
|
|
|
|
|
{localize(activeSetting)}
|
|
|
|
|
<HoverCard openDelay={50}>
|
|
|
|
|
<HoverCardTrigger asChild>
|
2024-05-05 21:35:16 +02:00
|
|
|
<InfoIcon className="ml-auto flex h-4 w-4 gap-2 text-gray-500 dark:text-white/50" />
|
🌿 feat: Fork Messages/Conversations (#2617)
* typedef for ImportBatchBuilder
* feat: first pass, fork conversations
* feat: fork - getMessagesUpToTargetLevel
* fix: additional tests and fix getAllMessagesUpToParent
* chore: arrow function return
* refactor: fork 3 options
* chore: remove unused genbuttons
* chore: remove unused hover buttons code
* feat: fork first pass
* wip: fork remember setting
* style: user icon
* chore: move clear chats to data tab
* WIP: fork UI options
* feat: data-provider fork types/services/vars and use generic MutationOptions
* refactor: use single param for fork option, use enum, fix mongo errors, use Date.now(), add records flag for testing, use endpoint from original convo and messages, pass originalConvo to finishConversation
* feat: add fork mutation hook and consolidate type imports
* refactor: use enum
* feat: first pass, fork mutation
* chore: add enum for target level fork option
* chore: add enum for target level fork option
* show toast when checking remember selection
* feat: splitAtTarget
* feat: split at target option
* feat: navigate to new fork, show toasts, set result query data
* feat: hover info for all fork options
* refactor: add Messages settings tab
* fix(Fork): remember text info
* ci: test for single message and is target edge case
* feat: additional tests for getAllMessagesUpToParent
* ci: additional tests and cycle detection for getMessagesUpToTargetLevel
* feat: circular dependency checks for getAllMessagesUpToParent
* fix: getMessagesUpToTargetLevel circular dep. check
* ci: more tests for getMessagesForConversation
* style: hover text for checkbox fork items
* refactor: add statefulness to conversation import
2024-05-05 11:48:20 -04:00
|
|
|
</HoverCardTrigger>
|
|
|
|
|
<HoverCardPortal>
|
|
|
|
|
<HoverCardContent
|
|
|
|
|
side="right"
|
|
|
|
|
className="z-[999] w-80 dark:bg-gray-700"
|
|
|
|
|
sideOffset={19}
|
|
|
|
|
>
|
|
|
|
|
<div className="flex flex-col gap-2 space-y-2 text-sm text-gray-600 dark:text-gray-300">
|
|
|
|
|
<span>{localize('com_ui_fork_info_1')}</span>
|
|
|
|
|
<span>{localize('com_ui_fork_info_2')}</span>
|
|
|
|
|
<span>
|
|
|
|
|
{localize('com_ui_fork_info_3', localize('com_ui_fork_split_target'))}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</HoverCardContent>
|
|
|
|
|
</HoverCardPortal>
|
|
|
|
|
</HoverCard>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex h-full w-full items-center justify-center gap-1">
|
|
|
|
|
<PopoverButton
|
|
|
|
|
sideOffset={155}
|
|
|
|
|
setActiveSetting={setActiveSetting}
|
|
|
|
|
timeoutRef={timeoutRef}
|
|
|
|
|
onClick={onClick}
|
|
|
|
|
setting={ForkOptions.DIRECT_PATH}
|
|
|
|
|
hoverTitle={
|
|
|
|
|
<>
|
|
|
|
|
<GitCommit className="h-5 w-5 rotate-90" />
|
|
|
|
|
{localize(optionLabels[ForkOptions.DIRECT_PATH])}
|
|
|
|
|
</>
|
|
|
|
|
}
|
|
|
|
|
hoverDescription={localize('com_ui_fork_info_visible')}
|
|
|
|
|
>
|
|
|
|
|
<HoverCardTrigger asChild>
|
|
|
|
|
<GitCommit className="h-full w-full rotate-90 p-2" />
|
|
|
|
|
</HoverCardTrigger>
|
|
|
|
|
</PopoverButton>
|
|
|
|
|
<PopoverButton
|
|
|
|
|
sideOffset={90}
|
|
|
|
|
setActiveSetting={setActiveSetting}
|
|
|
|
|
timeoutRef={timeoutRef}
|
|
|
|
|
onClick={onClick}
|
|
|
|
|
setting={ForkOptions.INCLUDE_BRANCHES}
|
|
|
|
|
hoverTitle={
|
|
|
|
|
<>
|
|
|
|
|
<GitBranchPlus className="h-4 w-4 rotate-180" />
|
|
|
|
|
{localize(optionLabels[ForkOptions.INCLUDE_BRANCHES])}
|
|
|
|
|
</>
|
|
|
|
|
}
|
|
|
|
|
hoverDescription={localize('com_ui_fork_info_branches')}
|
|
|
|
|
>
|
|
|
|
|
<HoverCardTrigger asChild>
|
|
|
|
|
<GitBranchPlus className="h-full w-full rotate-180 p-2" />
|
|
|
|
|
</HoverCardTrigger>
|
|
|
|
|
</PopoverButton>
|
|
|
|
|
<PopoverButton
|
|
|
|
|
sideOffset={25}
|
|
|
|
|
setActiveSetting={setActiveSetting}
|
|
|
|
|
timeoutRef={timeoutRef}
|
|
|
|
|
onClick={onClick}
|
|
|
|
|
setting={ForkOptions.TARGET_LEVEL}
|
|
|
|
|
hoverTitle={
|
|
|
|
|
<>
|
|
|
|
|
<ListTree className="h-5 w-5" />
|
|
|
|
|
{`${localize(optionLabels[ForkOptions.TARGET_LEVEL])} (${localize(
|
|
|
|
|
'com_endpoint_default',
|
|
|
|
|
)})`}
|
|
|
|
|
</>
|
|
|
|
|
}
|
|
|
|
|
hoverDescription={localize('com_ui_fork_info_target')}
|
|
|
|
|
>
|
|
|
|
|
<HoverCardTrigger asChild>
|
|
|
|
|
<ListTree className="h-full w-full p-2" />
|
|
|
|
|
</HoverCardTrigger>
|
|
|
|
|
</PopoverButton>
|
|
|
|
|
</div>
|
|
|
|
|
<HoverCard openDelay={50}>
|
|
|
|
|
<HoverCardTrigger asChild>
|
|
|
|
|
<div className="flex h-6 w-full items-center justify-start text-sm dark:text-gray-300 dark:hover:text-gray-200">
|
|
|
|
|
<Checkbox
|
|
|
|
|
checked={splitAtTarget}
|
|
|
|
|
onCheckedChange={(checked: boolean) => setSplitAtTarget(checked)}
|
|
|
|
|
className="m-2 transition duration-300 ease-in-out"
|
|
|
|
|
/>
|
|
|
|
|
{localize('com_ui_fork_split_target')}
|
|
|
|
|
</div>
|
|
|
|
|
</HoverCardTrigger>
|
|
|
|
|
<OptionHover
|
|
|
|
|
side={ESide.Right}
|
|
|
|
|
description="com_ui_fork_info_start"
|
|
|
|
|
langCode={true}
|
|
|
|
|
sideOffset={20}
|
|
|
|
|
/>
|
|
|
|
|
</HoverCard>
|
|
|
|
|
<HoverCard openDelay={50}>
|
|
|
|
|
<HoverCardTrigger asChild>
|
|
|
|
|
<div className="flex h-6 w-full items-center justify-start text-sm dark:text-gray-300 dark:hover:text-gray-200">
|
|
|
|
|
<Checkbox
|
|
|
|
|
checked={remember}
|
|
|
|
|
onCheckedChange={(checked: boolean) => {
|
|
|
|
|
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')}
|
|
|
|
|
</div>
|
|
|
|
|
</HoverCardTrigger>
|
|
|
|
|
<OptionHover
|
|
|
|
|
side={ESide.Right}
|
|
|
|
|
description="com_ui_fork_info_remember"
|
|
|
|
|
langCode={true}
|
|
|
|
|
sideOffset={20}
|
|
|
|
|
/>
|
|
|
|
|
</HoverCard>
|
|
|
|
|
</Popover.Content>
|
|
|
|
|
</div>
|
|
|
|
|
</Popover.Portal>
|
|
|
|
|
</Popover.Root>
|
|
|
|
|
);
|
|
|
|
|
}
|