import { useRecoilValue } from 'recoil'; import { useCallback, useMemo, memo } from 'react'; import type { TMessage } from 'librechat-data-provider'; import type { TMessageProps } from '~/common'; import MessageContent from '~/components/Chat/Messages/Content/MessageContent'; import PlaceholderRow from '~/components/Chat/Messages/ui/PlaceholderRow'; import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch'; import HoverButtons from '~/components/Chat/Messages/HoverButtons'; import MessageIcon from '~/components/Chat/Messages/MessageIcon'; import { Plugin } from '~/components/Messages/Content'; import SubRow from '~/components/Chat/Messages/SubRow'; import { MessageContext } from '~/Providers'; import { useMessageActions } from '~/hooks'; import { cn, logger } from '~/utils'; import store from '~/store'; type MessageRenderProps = { message?: TMessage; isCard?: boolean; isMultiMessage?: boolean; isSubmittingFamily?: boolean; } & Pick< TMessageProps, 'currentEditId' | 'setCurrentEditId' | 'siblingIdx' | 'setSiblingIdx' | 'siblingCount' >; const MessageRender = memo( ({ isCard, siblingIdx, siblingCount, message: msg, setSiblingIdx, currentEditId, isMultiMessage, setCurrentEditId, isSubmittingFamily, }: MessageRenderProps) => { const { ask, edit, index, assistant, enterEdit, conversation, messageLabel, isSubmitting, latestMessage, handleContinue, copyToClipboard, setLatestMessage, regenerateMessage, } = useMessageActions({ message: msg, currentEditId, isMultiMessage, setCurrentEditId, }); const fontSize = useRecoilValue(store.fontSize); const maximizeChatSpace = useRecoilValue(store.maximizeChatSpace); const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]); const { isCreatedByUser, error, unfinished } = msg ?? {}; const hasNoChildren = !(msg?.children?.length ?? 0); const isLast = useMemo( () => hasNoChildren && (msg?.depth === latestMessage?.depth || msg?.depth === -1), [hasNoChildren, msg?.depth, latestMessage?.depth], ); const iconData = useMemo( () => ({ endpoint: msg?.endpoint ?? conversation?.endpoint, model: msg?.model ?? conversation?.model, iconURL: msg?.iconURL ?? conversation?.iconURL, modelLabel: conversation?.chatGptLabel ?? conversation?.modelLabel, isCreatedByUser: msg?.isCreatedByUser, } as TMessage & { modelLabel?: string }), [ conversation?.chatGptLabel, conversation?.modelLabel, conversation?.endpoint, conversation?.iconURL, conversation?.model, msg?.model, msg?.iconURL, msg?.endpoint, msg?.isCreatedByUser, ], ); if (!msg) { return null; } const isLatestMessage = msg.messageId === latestMessage?.messageId; const showCardRender = isLast && !(isSubmittingFamily === true) && isCard === true; const isLatestCard = isCard === true && !(isSubmittingFamily === true) && isLatestMessage; const clickHandler = showCardRender && !isLatestMessage ? () => { logger.log(`Message Card click: Setting ${msg.messageId} as latest message`); logger.dir(msg); setLatestMessage(msg); } : undefined; // Style classes const baseClasses = 'final-completion group mx-auto flex flex-1 gap-3 transition-all duration-300 transform-gpu'; let layoutClasses = ''; if (isCard ?? false) { layoutClasses = 'relative w-full gap-1 rounded-lg border border-border-medium bg-surface-primary-alt p-2 md:w-1/2 md:gap-3 md:p-4'; } else if (maximizeChatSpace) { layoutClasses = 'md:max-w-full md:px-5'; } else { layoutClasses = 'md:max-w-3xl md:px-5 lg:max-w-[40rem] lg:px-1 xl:max-w-[48rem] xl:px-5'; } const latestCardClasses = isLatestCard ? 'bg-surface-secondary' : ''; const showRenderClasses = showCardRender ? 'cursor-pointer transition-colors duration-300' : ''; return (