mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-02 00:28:51 +01:00
refactor: non-assistant message content, parts
This commit is contained in:
parent
2c2dd57af5
commit
d66a35887d
10 changed files with 363 additions and 126 deletions
|
|
@ -1,12 +1,12 @@
|
|||
import { TMessage } from 'librechat-data-provider';
|
||||
import Files from './Files';
|
||||
|
||||
const Container = ({ children, message }: { children: React.ReactNode; message: TMessage }) => (
|
||||
const Container = ({ children, message }: { children: React.ReactNode; message?: TMessage }) => (
|
||||
<div
|
||||
className="text-message flex min-h-[20px] flex-col items-start gap-3 overflow-x-auto [.text-message+&]:mt-5"
|
||||
dir="auto"
|
||||
>
|
||||
{message.isCreatedByUser && <Files message={message} />}
|
||||
{message?.isCreatedByUser === true && <Files message={message} />}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,51 +1,34 @@
|
|||
import { Suspense } from 'react';
|
||||
import { memo } from 'react';
|
||||
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||
import { UnfinishedMessage } from './MessageContent';
|
||||
import { DelayedRender } from '~/components/ui';
|
||||
import Part from './Part';
|
||||
|
||||
const ContentParts = ({
|
||||
error,
|
||||
unfinished,
|
||||
isSubmitting,
|
||||
isLast,
|
||||
content,
|
||||
...props
|
||||
}: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
any) => {
|
||||
if (error) {
|
||||
// return <ErrorMessage text={text} />;
|
||||
} else {
|
||||
const { message } = props;
|
||||
const { messageId } = message;
|
||||
type ContentPartsProps = {
|
||||
content: Array<TMessageContentParts | undefined>;
|
||||
messageId: string;
|
||||
isCreatedByUser: boolean;
|
||||
isLast: boolean;
|
||||
isSubmitting: boolean;
|
||||
};
|
||||
|
||||
const ContentParts = memo(
|
||||
({ content, messageId, isCreatedByUser, isLast, isSubmitting }: ContentPartsProps) => {
|
||||
return (
|
||||
<>
|
||||
{content
|
||||
.filter((part: TMessageContentParts | undefined) => part)
|
||||
.map((part: TMessageContentParts | undefined, idx: number) => {
|
||||
const showCursor = idx === content.length - 1 && isLast;
|
||||
return (
|
||||
<Part
|
||||
key={`display-${messageId}-${idx}`}
|
||||
showCursor={showCursor === true && isSubmitting}
|
||||
isSubmitting={isSubmitting}
|
||||
part={part}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{/* Temporarily remove this */}
|
||||
{/* {!isSubmitting && unfinished && (
|
||||
<Suspense>
|
||||
<DelayedRender delay={250}>
|
||||
<UnfinishedMessage message={message} key={`unfinished-${messageId}`} />
|
||||
</DelayedRender>
|
||||
</Suspense>
|
||||
)} */}
|
||||
.filter((part) => part)
|
||||
.map((part, idx) => (
|
||||
<Part
|
||||
key={`display-${messageId}-${idx}`}
|
||||
part={part}
|
||||
isSubmitting={isSubmitting}
|
||||
showCursor={idx === content.length - 1 && isLast}
|
||||
messageId={messageId}
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export default ContentParts;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { TFile, TMessage } from 'librechat-data-provider';
|
|||
import FileContainer from '~/components/Chat/Input/Files/FileContainer';
|
||||
import Image from './Image';
|
||||
|
||||
const Files = ({ message }: { message: TMessage }) => {
|
||||
const Files = ({ message }: { message?: TMessage }) => {
|
||||
const imageFiles = useMemo(() => {
|
||||
return message?.files?.filter((file) => file.type?.startsWith('image/')) || [];
|
||||
}, [message?.files]);
|
||||
|
|
@ -20,7 +20,7 @@ const Files = ({ message }: { message: TMessage }) => {
|
|||
imageFiles.map((file) => (
|
||||
<Image
|
||||
key={file.file_id}
|
||||
imagePath={file?.preview ?? file.filepath ?? ''}
|
||||
imagePath={file.preview ?? file.filepath ?? ''}
|
||||
height={file.height ?? 1920}
|
||||
width={file.width ?? 1080}
|
||||
altText={file.filename ?? 'Uploaded Image'}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ export const ErrorMessage = ({
|
|||
text,
|
||||
message,
|
||||
className = '',
|
||||
}: Pick<TDisplayProps, 'text' | 'className' | 'message'>) => {
|
||||
}: Pick<TDisplayProps, 'text' | 'className'> & {
|
||||
message?: TMessage;
|
||||
}) => {
|
||||
const localize = useLocalize();
|
||||
if (text === 'Error connecting to server, try refreshing the page.') {
|
||||
console.log('error message', message);
|
||||
|
|
|
|||
|
|
@ -4,81 +4,43 @@ import {
|
|||
imageGenTools,
|
||||
isImageVisionTool,
|
||||
} from 'librechat-data-provider';
|
||||
import { useMemo } from 'react';
|
||||
import type { TMessageContentParts, TMessage } from 'librechat-data-provider';
|
||||
import type { TDisplayProps } from '~/common';
|
||||
import { memo } from 'react';
|
||||
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||
import { ErrorMessage } from './MessageContent';
|
||||
import { useChatContext } from '~/Providers';
|
||||
import RetrievalCall from './RetrievalCall';
|
||||
import CodeAnalyze from './CodeAnalyze';
|
||||
import Container from './Container';
|
||||
import ToolCall from './ToolCall';
|
||||
import Markdown from './Markdown';
|
||||
import ImageGen from './ImageGen';
|
||||
import { cn } from '~/utils';
|
||||
import Text from './Parts/Text';
|
||||
import Image from './Image';
|
||||
|
||||
// Display Message Component
|
||||
const DisplayMessage = ({ text, isCreatedByUser = false, message, showCursor }: TDisplayProps) => {
|
||||
const { isSubmitting, latestMessage } = useChatContext();
|
||||
const showCursorState = useMemo(
|
||||
() => showCursor === true && isSubmitting,
|
||||
[showCursor, isSubmitting],
|
||||
);
|
||||
const isLatestMessage = useMemo(
|
||||
() => message.messageId === latestMessage?.messageId,
|
||||
[message.messageId, latestMessage?.messageId],
|
||||
);
|
||||
|
||||
// Note: for testing purposes
|
||||
// isSubmitting && isLatestMessage && logger.log('message_stream', { text, isCreatedByUser, isSubmitting, showCursorState });
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
isSubmitting ? 'submitting' : '',
|
||||
showCursorState && !!text.length ? 'result-streaming' : '',
|
||||
'markdown prose message-content dark:prose-invert light w-full break-words',
|
||||
isCreatedByUser ? 'whitespace-pre-wrap dark:text-gray-20' : 'dark:text-gray-70',
|
||||
)}
|
||||
>
|
||||
{!isCreatedByUser ? (
|
||||
<Markdown content={text} showCursor={showCursorState} isLatestMessage={isLatestMessage} />
|
||||
) : (
|
||||
<>{text}</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Part({
|
||||
part,
|
||||
showCursor,
|
||||
isSubmitting,
|
||||
message,
|
||||
}: {
|
||||
part: TMessageContentParts | undefined;
|
||||
type PartProps = {
|
||||
part?: TMessageContentParts;
|
||||
isSubmitting: boolean;
|
||||
showCursor: boolean;
|
||||
message: TMessage;
|
||||
}) {
|
||||
messageId: string;
|
||||
isCreatedByUser: boolean;
|
||||
};
|
||||
|
||||
const Part = memo(({ part, isSubmitting, showCursor, messageId, isCreatedByUser }: PartProps) => {
|
||||
if (!part) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (part.type === ContentTypes.ERROR) {
|
||||
return <ErrorMessage message={message} text={part[ContentTypes.TEXT].value} className="my-2" />;
|
||||
return <ErrorMessage text={part[ContentTypes.TEXT].value} className="my-2" />;
|
||||
} else if (part.type === ContentTypes.TEXT) {
|
||||
const text = typeof part.text === 'string' ? part.text : part.text.value;
|
||||
if (typeof text !== 'string') {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Container message={message}>
|
||||
<DisplayMessage
|
||||
<Container>
|
||||
<Text
|
||||
text={text}
|
||||
isCreatedByUser={message.isCreatedByUser}
|
||||
message={message}
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
messageId={messageId}
|
||||
showCursor={showCursor}
|
||||
/>
|
||||
</Container>
|
||||
|
|
@ -132,11 +94,11 @@ export default function Part({
|
|||
if (isImageVisionTool(toolCall)) {
|
||||
if (isSubmitting && showCursor) {
|
||||
return (
|
||||
<Container message={message}>
|
||||
<DisplayMessage
|
||||
<Container>
|
||||
<Text
|
||||
text={''}
|
||||
isCreatedByUser={message.isCreatedByUser}
|
||||
message={message}
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
messageId={messageId}
|
||||
showCursor={showCursor}
|
||||
/>
|
||||
</Container>
|
||||
|
|
@ -174,4 +136,6 @@ export default function Part({
|
|||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
export default Part;
|
||||
|
|
|
|||
39
client/src/components/Chat/Messages/Content/Parts/Text.tsx
Normal file
39
client/src/components/Chat/Messages/Content/Parts/Text.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { memo, useMemo } from 'react';
|
||||
import { useChatContext } from '~/Providers';
|
||||
import Markdown from '~/components/Chat/Messages/Content/Markdown';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
type TextPartProps = {
|
||||
text: string;
|
||||
isCreatedByUser: boolean;
|
||||
messageId: string;
|
||||
showCursor: boolean;
|
||||
};
|
||||
|
||||
const TextPart = memo(({ text, isCreatedByUser, messageId, showCursor }: TextPartProps) => {
|
||||
const { isSubmitting, latestMessage } = useChatContext();
|
||||
const showCursorState = useMemo(() => showCursor && isSubmitting, [showCursor, isSubmitting]);
|
||||
const isLatestMessage = useMemo(
|
||||
() => messageId === latestMessage?.messageId,
|
||||
[messageId, latestMessage?.messageId],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
isSubmitting ? 'submitting' : '',
|
||||
showCursorState && !!text.length ? 'result-streaming' : '',
|
||||
'markdown prose message-content dark:prose-invert light w-full break-words',
|
||||
isCreatedByUser ? 'whitespace-pre-wrap dark:text-gray-20' : 'dark:text-gray-70',
|
||||
)}
|
||||
>
|
||||
{!isCreatedByUser ? (
|
||||
<Markdown content={text} showCursor={showCursorState} isLatestMessage={isLatestMessage} />
|
||||
) : (
|
||||
<>{text}</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default TextPart;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import Icon from '~/components/Chat/Messages/MessageIcon';
|
||||
import { useMessageHelpers, useLocalize } from '~/hooks';
|
||||
|
|
@ -17,7 +18,6 @@ export default function Message(props: TMessageProps) {
|
|||
props;
|
||||
|
||||
const {
|
||||
ask,
|
||||
edit,
|
||||
index,
|
||||
agent,
|
||||
|
|
@ -33,7 +33,7 @@ export default function Message(props: TMessageProps) {
|
|||
regenerateMessage,
|
||||
} = useMessageHelpers(props);
|
||||
const fontSize = useRecoilValue(store.fontSize);
|
||||
const { content, children, messageId = null, isCreatedByUser, error, unfinished } = message ?? {};
|
||||
const { children, messageId = null, isCreatedByUser } = message ?? {};
|
||||
|
||||
if (!message) {
|
||||
return null;
|
||||
|
|
@ -82,24 +82,11 @@ export default function Message(props: TMessageProps) {
|
|||
<div className="flex-col gap-1 md:gap-3">
|
||||
<div className="flex max-w-full flex-grow flex-col gap-0">
|
||||
<ContentParts
|
||||
ask={ask}
|
||||
edit={edit}
|
||||
content={message.content as Array<TMessageContentParts | undefined>}
|
||||
messageId={message.messageId}
|
||||
isCreatedByUser={message.isCreatedByUser}
|
||||
isLast={isLast}
|
||||
content={content ?? []}
|
||||
message={message}
|
||||
messageId={messageId}
|
||||
enterEdit={enterEdit}
|
||||
error={!!(error ?? false)}
|
||||
isSubmitting={isSubmitting}
|
||||
unfinished={unfinished ?? false}
|
||||
isCreatedByUser={isCreatedByUser ?? true}
|
||||
siblingIdx={siblingIdx ?? 0}
|
||||
setSiblingIdx={
|
||||
setSiblingIdx ??
|
||||
(() => {
|
||||
return;
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import Message from './Message';
|
||||
import MessageContent from '~/components/Messages/MessageContent';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import MessageParts from './MessageParts';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import Message from './Message';
|
||||
import store from '~/store';
|
||||
|
||||
export default function MultiMessage({
|
||||
|
|
@ -30,22 +34,22 @@ export default function MultiMessage({
|
|||
}, [messagesTree?.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (messagesTree?.length && siblingIdx >= messagesTree?.length) {
|
||||
if (messagesTree?.length && siblingIdx >= messagesTree.length) {
|
||||
setSiblingIdx(0);
|
||||
}
|
||||
}, [siblingIdx, messagesTree?.length, setSiblingIdx]);
|
||||
|
||||
if (!(messagesTree && messagesTree?.length)) {
|
||||
if (!(messagesTree && messagesTree.length)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const message = messagesTree[messagesTree.length - siblingIdx - 1];
|
||||
const message = messagesTree[messagesTree.length - siblingIdx - 1] as TMessage | undefined;
|
||||
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (message.content) {
|
||||
if (isAssistantsEndpoint(message.endpoint) && message.content) {
|
||||
return (
|
||||
<MessageParts
|
||||
key={message.messageId}
|
||||
|
|
@ -57,6 +61,18 @@ export default function MultiMessage({
|
|||
setSiblingIdx={setSiblingIdxRev}
|
||||
/>
|
||||
);
|
||||
} else if (message.content) {
|
||||
return (
|
||||
<MessageContent
|
||||
key={message.messageId}
|
||||
message={message}
|
||||
currentEditId={currentEditId}
|
||||
setCurrentEditId={setCurrentEditId}
|
||||
siblingIdx={messagesTree.length - siblingIdx - 1}
|
||||
siblingCount={messagesTree.length}
|
||||
setSiblingIdx={setSiblingIdxRev}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
164
client/src/components/Messages/ContentRender.tsx
Normal file
164
client/src/components/Messages/ContentRender.tsx
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
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 Icon 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,
|
||||
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],
|
||||
);
|
||||
|
||||
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">
|
||||
<Icon message={msg} conversation={conversation} assistant={assistant} />
|
||||
</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
|
||||
content={msg.content as Array<TMessageContentParts | undefined>}
|
||||
messageId={msg.messageId}
|
||||
isCreatedByUser={msg.isCreatedByUser}
|
||||
isLast={isLast}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
</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;
|
||||
82
client/src/components/Messages/MessageContent.tsx
Normal file
82
client/src/components/Messages/MessageContent.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import React from 'react';
|
||||
import { useMessageProcess } from '~/hooks';
|
||||
import type { TMessageProps } from '~/common';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import MultiMessage from '~/components/Chat/Messages/MultiMessage';
|
||||
import ContentRender from './ContentRender';
|
||||
|
||||
const MessageContainer = React.memo(
|
||||
({
|
||||
handleScroll,
|
||||
children,
|
||||
}: {
|
||||
handleScroll: (event?: unknown) => void;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className="text-token-text-primary w-full border-0 bg-transparent dark:border-0 dark:bg-transparent"
|
||||
onWheel={handleScroll}
|
||||
onTouchMove={handleScroll}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default function MessageContent(props: TMessageProps) {
|
||||
const {
|
||||
showSibling,
|
||||
conversation,
|
||||
handleScroll,
|
||||
siblingMessage,
|
||||
latestMultiMessage,
|
||||
isSubmittingFamily,
|
||||
} = useMessageProcess({ message: props.message });
|
||||
const { message, currentEditId, setCurrentEditId } = props;
|
||||
|
||||
if (!message || typeof message !== 'object') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { children, messageId = null } = message;
|
||||
|
||||
return (
|
||||
<>
|
||||
<MessageContainer handleScroll={handleScroll}>
|
||||
{showSibling ? (
|
||||
<div className="m-auto my-2 flex justify-center p-4 py-2 md:gap-6">
|
||||
<div className="flex w-full flex-row flex-wrap justify-between gap-1 md:max-w-5xl md:flex-nowrap md:gap-2 lg:max-w-5xl xl:max-w-6xl">
|
||||
<ContentRender
|
||||
{...props}
|
||||
message={message}
|
||||
isSubmittingFamily={isSubmittingFamily}
|
||||
isCard
|
||||
/>
|
||||
<ContentRender
|
||||
{...props}
|
||||
isMultiMessage
|
||||
isCard
|
||||
message={siblingMessage ?? latestMultiMessage ?? undefined}
|
||||
isSubmittingFamily={isSubmittingFamily}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="m-auto justify-center p-4 py-2 md:gap-6 ">
|
||||
<ContentRender {...props} />
|
||||
</div>
|
||||
)}
|
||||
</MessageContainer>
|
||||
<MultiMessage
|
||||
key={messageId}
|
||||
messageId={messageId}
|
||||
conversation={conversation}
|
||||
messagesTree={children ?? []}
|
||||
currentEditId={currentEditId}
|
||||
setCurrentEditId={setCurrentEditId}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue