diff --git a/client/src/common/types.ts b/client/src/common/types.ts index e4f1b16df2..86590c3ffd 100644 --- a/client/src/common/types.ts +++ b/client/src/common/types.ts @@ -181,6 +181,7 @@ export type TAskProps = { export type TOptions = { editedMessageId?: string | null; editedText?: string | null; + resubmitFiles?: boolean; isRegenerate?: boolean; isContinued?: boolean; isEdited?: boolean; diff --git a/client/src/components/Chat/Input/Files/Table/fakeData.ts b/client/src/components/Chat/Input/Files/Table/fakeData.ts index c56cc3737f..46f01bd0df 100644 --- a/client/src/components/Chat/Input/Files/Table/fakeData.ts +++ b/client/src/components/Chat/Input/Files/Table/fakeData.ts @@ -17,6 +17,7 @@ export const files: TFile[] = [ updatedAt: '2024-01-23T18:25:48.153Z', usage: 0, user: '652ac880c4102a77fe54c5db', + embedded: false, }, { _id: '65b004abd70ce86b9146e861', @@ -34,6 +35,7 @@ export const files: TFile[] = [ usage: 0, user: '652ac880c4102a77fe54c5db', width: 1024, + embedded: false, }, { _id: '65b00495d70ce86b9146adc1', @@ -51,6 +53,7 @@ export const files: TFile[] = [ usage: 0, user: '652ac880c4102a77fe54c5db', width: 1024, + embedded: false, }, { _id: '65b00494d70ce86b9146ace6', @@ -68,5 +71,6 @@ export const files: TFile[] = [ usage: 0, user: '652ac880c4102a77fe54c5db', width: 1024, + embedded: false, }, ]; diff --git a/client/src/components/Chat/Messages/Content/Container.tsx b/client/src/components/Chat/Messages/Content/Container.tsx index b26c014c88..bfb2ee2938 100644 --- a/client/src/components/Chat/Messages/Content/Container.tsx +++ b/client/src/components/Chat/Messages/Content/Container.tsx @@ -1,6 +1,9 @@ -// Container Component -const Container = ({ children }: { children: React.ReactNode }) => ( +import { TMessage } from 'librechat-data-provider'; +import Files from './Files'; + +const Container = ({ children, message }: { children: React.ReactNode; message: TMessage }) => (
+ {message.isCreatedByUser && } {children}
); diff --git a/client/src/components/Chat/Messages/Content/ContentParts.tsx b/client/src/components/Chat/Messages/Content/ContentParts.tsx index 031795773f..9aff7db9fd 100644 --- a/client/src/components/Chat/Messages/Content/ContentParts.tsx +++ b/client/src/components/Chat/Messages/Content/ContentParts.tsx @@ -38,7 +38,7 @@ any) => { {!isSubmitting && unfinished && ( - + )} diff --git a/client/src/components/Chat/Messages/Content/EditMessage.tsx b/client/src/components/Chat/Messages/Content/EditMessage.tsx index 0c830e04fe..e17a17356d 100644 --- a/client/src/components/Chat/Messages/Content/EditMessage.tsx +++ b/client/src/components/Chat/Messages/Content/EditMessage.tsx @@ -3,10 +3,10 @@ import { EModelEndpoint } from 'librechat-data-provider'; import { useState, useRef, useEffect, useCallback } from 'react'; import { useUpdateMessageMutation } from 'librechat-data-provider/react-query'; import type { TEditProps } from '~/common'; -import Container from '~/components/Messages/Content/Container'; import { cn, removeFocusOutlines } from '~/utils'; import { useChatContext } from '~/Providers'; import { useLocalize } from '~/hooks'; +import Container from './Container'; const EditMessage = ({ text, @@ -39,11 +39,16 @@ const EditMessage = ({ const resubmitMessage = () => { if (message.isCreatedByUser) { - ask({ - text: editedText, - parentMessageId, - conversationId, - }); + ask( + { + text: editedText, + parentMessageId, + conversationId, + }, + { + resubmitFiles: true, + }, + ); setSiblingIdx((siblingIdx ?? 0) - 1); } else { @@ -105,7 +110,7 @@ const EditMessage = ({ ); return ( - + { diff --git a/client/src/components/Chat/Messages/Content/Files.tsx b/client/src/components/Chat/Messages/Content/Files.tsx new file mode 100644 index 0000000000..beff81b58b --- /dev/null +++ b/client/src/components/Chat/Messages/Content/Files.tsx @@ -0,0 +1,39 @@ +import { useMemo, memo } from 'react'; +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 imageFiles = useMemo(() => { + return message?.files?.filter((file) => file.type?.startsWith('image/')) || []; + }, [message?.files]); + + const otherFiles = useMemo(() => { + return message?.files?.filter((file) => !file.type?.startsWith('image/')) || []; + }, [message?.files]); + + return ( + <> + {otherFiles.length > 0 && + otherFiles.map((file) => )} + {imageFiles && + imageFiles.map((file) => ( + + ))} + + ); +}; + +export default memo(Files); diff --git a/client/src/components/Chat/Messages/Content/Image.tsx b/client/src/components/Chat/Messages/Content/Image.tsx index 4176815dec..18bafa49ea 100644 --- a/client/src/components/Chat/Messages/Content/Image.tsx +++ b/client/src/components/Chat/Messages/Content/Image.tsx @@ -1,18 +1,35 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useRef, useMemo } from 'react'; import { LazyLoadImage } from 'react-lazy-load-image-component'; import * as Dialog from '@radix-ui/react-dialog'; import DialogImage from './DialogImage'; import { cn } from '~/utils'; +const scaleImage = ({ + originalWidth, + originalHeight, + containerRef, +}: { + originalWidth: number; + originalHeight: number; + containerRef: React.RefObject; +}) => { + const containerWidth = containerRef.current?.offsetWidth ?? 0; + if (containerWidth === 0 || originalWidth === undefined || originalHeight === undefined) { + return { width: 'auto', height: 'auto' }; + } + const aspectRatio = originalWidth / originalHeight; + const scaledWidth = Math.min(containerWidth, originalWidth); + const scaledHeight = scaledWidth / aspectRatio; + return { width: `${scaledWidth}px`, height: `${scaledHeight}px` }; +}; + const Image = ({ imagePath, altText, height, width, placeholderDimensions, -}: // n, -// i, -{ +}: { imagePath: string; altText: string; height: number; @@ -21,69 +38,49 @@ const Image = ({ height: string; width: string; }; - // n: number; - // i: number; }) => { const [isLoaded, setIsLoaded] = useState(false); + const containerRef = useRef(null); + const handleImageLoad = () => setIsLoaded(true); - const [minDisplayTimeElapsed, setMinDisplayTimeElapsed] = useState(false); - useEffect(() => { - let timer: NodeJS.Timeout; - if (isLoaded) { - timer = setTimeout(() => setMinDisplayTimeElapsed(true), 150); - } - return () => clearTimeout(timer); - }, [isLoaded]); - // const makeSquare = n >= 3 && i < 2; - - let placeholderHeight = '288px'; - if (placeholderDimensions?.height && placeholderDimensions?.width) { - placeholderHeight = placeholderDimensions.height; - } else if (height > width) { - placeholderHeight = '900px'; - } else if (height === width) { - placeholderHeight = width + 'px'; - } + const { width: scaledWidth, height: scaledHeight } = useMemo( + () => + scaleImage({ + originalWidth: Number(placeholderDimensions?.width?.split('px')[0]) ?? width, + originalHeight: Number(placeholderDimensions?.height?.split('px')[0]) ?? height, + containerRef, + }), + [placeholderDimensions, height, width], + ); return ( -
+
- {isLoaded && minDisplayTimeElapsed && ( - - )} + {isLoaded && } ); }; diff --git a/client/src/components/Chat/Messages/Content/MessageContent.tsx b/client/src/components/Chat/Messages/Content/MessageContent.tsx index ea761c6569..5f75fc3b74 100644 --- a/client/src/components/Chat/Messages/Content/MessageContent.tsx +++ b/client/src/components/Chat/Messages/Content/MessageContent.tsx @@ -1,7 +1,6 @@ import { Fragment, Suspense } from 'react'; -import type { TResPlugin, TFile } from 'librechat-data-provider'; -import type { TMessageContentProps, TText, TDisplayProps } from '~/common'; -import FileContainer from '~/components/Chat/Input/Files/FileContainer'; +import type { TMessage, TResPlugin } from 'librechat-data-provider'; +import type { TMessageContentProps, TDisplayProps } from '~/common'; import Plugin from '~/components/Messages/Content/Plugin'; import Error from '~/components/Messages/Content/Error'; import { DelayedRender } from '~/components/ui'; @@ -9,11 +8,14 @@ import EditMessage from './EditMessage'; import Container from './Container'; import Markdown from './Markdown'; import { cn } from '~/utils'; -import Image from './Image'; -export const ErrorMessage = ({ text, className = '' }: TText) => { +export const ErrorMessage = ({ + text, + message, + className = '', +}: Pick) => { return ( - +
{ // Display Message Component const DisplayMessage = ({ text, isCreatedByUser, message, showCursor }: TDisplayProps) => { - const files: TFile[] = []; - const imageFiles = message?.files - ? message.files.filter((file) => { - if (file.type && file.type.startsWith('image/')) { - return true; - } - - files.push(file); - }) - : null; return ( - - {files.length > 0 && files.map((file) => )} - {imageFiles && - imageFiles.map((file) => ( - - ))} +
( - +export const UnfinishedMessage = ({ message }: { message: TMessage }) => ( + ); // Content Component @@ -86,7 +68,7 @@ const MessageContent = ({ ...props }: TMessageContentProps) => { if (error) { - return ; + return ; } else if (edit) { return ; } else { @@ -143,7 +125,7 @@ const MessageContent = ({ {!isSubmitting && unfinished && ( - + )} diff --git a/client/src/components/Chat/Messages/Content/Part.tsx b/client/src/components/Chat/Messages/Content/Part.tsx index e52ccfd082..e01e11d03b 100644 --- a/client/src/components/Chat/Messages/Content/Part.tsx +++ b/client/src/components/Chat/Messages/Content/Part.tsx @@ -53,11 +53,11 @@ export default function Part({ } if (part.type === ContentTypes.ERROR) { - return ; + return ; } else if (part.type === ContentTypes.TEXT) { // Access the value property return ( - +
+