mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00

* chore: typing * chore: typing * fix: enhance message scrolling logic to handle empty messages tree and ref checks * fix: optimize message selection logic with useCallback for better performance * chore: typing * refactor: optimize icon rendering * refactor: further optimize chat props * fix: remove unnecessary console log in useQueryParams cleanup * refactor: add queryClient to reset message data on new conversation initiation * refactor: update data-testid attributes for consistency and improve code readability * refactor: integrate queryClient to reset message data on new conversation initiation
192 lines
6.5 KiB
TypeScript
192 lines
6.5 KiB
TypeScript
import { useRecoilValue } from 'recoil';
|
|
import { useCallback, useMemo, memo } from 'react';
|
|
import type { TMessage, TMessageContentParts } from 'librechat-data-provider';
|
|
import type { TMessageProps } from '~/common';
|
|
import ContentParts from '~/components/Chat/Messages/Content/ContentParts';
|
|
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 SubRow from '~/components/Chat/Messages/SubRow';
|
|
import { useMessageActions } from '~/hooks';
|
|
import { cn, logger } from '~/utils';
|
|
import store from '~/store';
|
|
|
|
type ContentRenderProps = {
|
|
message?: TMessage;
|
|
isCard?: boolean;
|
|
isMultiMessage?: boolean;
|
|
isSubmittingFamily?: boolean;
|
|
} & Pick<
|
|
TMessageProps,
|
|
'currentEditId' | 'setCurrentEditId' | 'siblingIdx' | 'setSiblingIdx' | 'siblingCount'
|
|
>;
|
|
|
|
const ContentRender = memo(
|
|
({
|
|
isCard,
|
|
siblingIdx,
|
|
siblingCount,
|
|
message: msg,
|
|
setSiblingIdx,
|
|
currentEditId,
|
|
isMultiMessage,
|
|
setCurrentEditId,
|
|
isSubmittingFamily,
|
|
}: ContentRenderProps) => {
|
|
const {
|
|
// ask,
|
|
edit,
|
|
index,
|
|
agent,
|
|
assistant,
|
|
enterEdit,
|
|
conversation,
|
|
messageLabel,
|
|
isSubmitting,
|
|
latestMessage,
|
|
handleContinue,
|
|
copyToClipboard,
|
|
setLatestMessage,
|
|
regenerateMessage,
|
|
} = useMessageActions({
|
|
message: msg,
|
|
currentEditId,
|
|
isMultiMessage,
|
|
setCurrentEditId,
|
|
});
|
|
|
|
const fontSize = useRecoilValue(store.fontSize);
|
|
const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]);
|
|
// const { isCreatedByUser, error, unfinished } = msg ?? {};
|
|
const isLast = useMemo(
|
|
() =>
|
|
!(msg?.children?.length ?? 0) && (msg?.depth === latestMessage?.depth || msg?.depth === -1),
|
|
[msg?.children, msg?.depth, latestMessage?.depth],
|
|
);
|
|
|
|
const iconData = useMemo(
|
|
() =>
|
|
({
|
|
endpoint: conversation?.endpoint,
|
|
model: conversation?.model ?? msg?.model,
|
|
iconURL: conversation?.iconURL ?? msg?.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?.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;
|
|
|
|
return (
|
|
<div
|
|
aria-label={`message-${msg.depth}-${msg.messageId}`}
|
|
className={cn(
|
|
'final-completion group mx-auto flex flex-1 gap-3',
|
|
isCard === true
|
|
? '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'
|
|
: 'md:max-w-3xl md:px-5 lg:max-w-[40rem] lg:px-1 xl:max-w-[48rem] xl:px-5',
|
|
isLatestCard === true ? 'bg-surface-secondary' : '',
|
|
showCardRender ? 'cursor-pointer transition-colors duration-300' : '',
|
|
'focus:outline-none focus:ring-2 focus:ring-border-xheavy',
|
|
)}
|
|
onClick={clickHandler}
|
|
onKeyDown={(e) => {
|
|
if ((e.key === 'Enter' || e.key === ' ') && clickHandler) {
|
|
clickHandler();
|
|
}
|
|
}}
|
|
role={showCardRender ? 'button' : undefined}
|
|
tabIndex={showCardRender ? 0 : undefined}
|
|
>
|
|
{isLatestCard === true && (
|
|
<div className="absolute right-0 top-0 m-2 h-3 w-3 rounded-full bg-text-primary" />
|
|
)}
|
|
<div className="relative flex flex-shrink-0 flex-col items-end">
|
|
<div>
|
|
<div className="pt-0.5">
|
|
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
|
<MessageIcon iconData={iconData} assistant={assistant} agent={agent} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div
|
|
className={cn(
|
|
'relative flex w-11/12 flex-col',
|
|
msg.isCreatedByUser === true ? '' : 'agent-turn',
|
|
)}
|
|
>
|
|
<h2 className={cn('select-none font-semibold', fontSize)}>{messageLabel}</h2>
|
|
<div className="flex-col gap-1 md:gap-3">
|
|
<div className="flex max-w-full flex-grow flex-col gap-0">
|
|
<ContentParts
|
|
edit={edit}
|
|
isLast={isLast}
|
|
enterEdit={enterEdit}
|
|
siblingIdx={siblingIdx}
|
|
messageId={msg.messageId}
|
|
isSubmitting={isSubmitting}
|
|
setSiblingIdx={setSiblingIdx}
|
|
attachments={msg.attachments}
|
|
isCreatedByUser={msg.isCreatedByUser}
|
|
conversationId={conversation?.conversationId}
|
|
content={msg.content as Array<TMessageContentParts | undefined>}
|
|
/>
|
|
</div>
|
|
</div>
|
|
{!(msg.children?.length ?? 0) && (isSubmittingFamily === true || isSubmitting) ? (
|
|
<PlaceholderRow isCard={isCard} />
|
|
) : (
|
|
<SubRow classes="text-xs">
|
|
<SiblingSwitch
|
|
siblingIdx={siblingIdx}
|
|
siblingCount={siblingCount}
|
|
setSiblingIdx={setSiblingIdx}
|
|
/>
|
|
<HoverButtons
|
|
index={index}
|
|
isEditing={edit}
|
|
message={msg}
|
|
enterEdit={enterEdit}
|
|
isSubmitting={isSubmitting}
|
|
conversation={conversation ?? null}
|
|
regenerate={handleRegenerateMessage}
|
|
copyToClipboard={copyToClipboard}
|
|
handleContinue={handleContinue}
|
|
latestMessage={latestMessage}
|
|
isLast={isLast}
|
|
/>
|
|
</SubRow>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
);
|
|
|
|
export default ContentRender;
|