/* eslint-disable react-hooks/exhaustive-deps */ import { useGetConversationByIdQuery } from 'librechat-data-provider'; import { useEffect } from 'react'; import { useSetRecoilState, useRecoilState } from 'recoil'; import copy from 'copy-to-clipboard'; import { SubRow, Plugin, MessageContent } from './Content'; // eslint-disable-next-line import/no-cycle import MultiMessage from './MultiMessage'; import HoverButtons from './HoverButtons'; import SiblingSwitch from './SiblingSwitch'; import { Icon } from '~/components/Endpoints'; import { useMessageHandler, useConversation } from '~/hooks'; import type { TMessageProps } from '~/common'; import { cn } from '~/utils'; import store from '~/store'; export default function Message({ conversation, message, scrollToBottom, currentEditId, setCurrentEditId, siblingIdx, siblingCount, setSiblingIdx, }: TMessageProps) { const setLatestMessage = useSetRecoilState(store.latestMessage); const [abortScroll, setAbortScroll] = useRecoilState(store.abortScroll); const { isSubmitting, ask, regenerate, handleContinue } = useMessageHandler(); const { switchToConversation } = useConversation(); const { text, children, messageId = null, searchResult, isCreatedByUser, error, unfinished, } = message ?? {}; const isLast = !children?.length; const edit = messageId == currentEditId; const getConversationQuery = useGetConversationByIdQuery(message?.conversationId ?? '', { enabled: false, }); const blinker = message?.submitting && isSubmitting; // debugging // useEffect(() => { // console.log('isSubmitting:', isSubmitting); // console.log('unfinished:', unfinished); // }, [isSubmitting, unfinished]); useEffect(() => { if (blinker && scrollToBottom && !abortScroll) { scrollToBottom(); } }, [isSubmitting, blinker, text, scrollToBottom]); useEffect(() => { if (!message) { return; } else if (isLast) { setLatestMessage({ ...message }); } }, [isLast, message]); if (!message) { return null; } const enterEdit = (cancel?: boolean) => setCurrentEditId && setCurrentEditId(cancel ? -1 : messageId); const handleScroll = () => { if (blinker) { setAbortScroll(true); } else { setAbortScroll(false); } }; const commonClasses = 'w-full border-b text-gray-800 group border-black/10 dark:border-gray-900/50 dark:text-gray-100'; const uniqueClasses = isCreatedByUser ? 'bg-white dark:bg-gray-800 dark:text-gray-20' : 'bg-gray-50 dark:bg-gray-1000 dark:text-gray-70'; const props = { className: cn(commonClasses, uniqueClasses), titleclass: '', }; const icon = Icon({ ...conversation, ...message, model: message?.model ?? conversation?.model, size: 36, }); if (message?.bg && searchResult) { props.className = message?.bg?.split('hover')[0]; props.titleclass = message?.bg?.split(props.className)[1] + ' cursor-pointer'; } const regenerateMessage = () => { if (!isSubmitting && !isCreatedByUser) { regenerate(message); } }; const copyToClipboard = (setIsCopied: React.Dispatch>) => { setIsCopied(true); copy(text ?? ''); setTimeout(() => { setIsCopied(false); }, 3000); }; const clickSearchResult = async () => { if (!searchResult) { return; } if (!message) { return; } getConversationQuery.refetch({ queryKey: [message?.conversationId] }).then((response) => { console.log('getConversationQuery response.data:', response.data); if (response.data) { switchToConversation(response.data); } }); }; return ( <>
{typeof icon === 'string' && /[^\\x00-\\x7F]+/.test(icon as string) ? ( {icon} ) : ( icon )}
{searchResult && ( {`${message?.title} | ${message?.sender}`} )}
{/* Legacy Plugins */} {message?.plugin && } { return; }) } />
regenerateMessage()} handleContinue={handleContinue} copyToClipboard={copyToClipboard} />
); }