From c52ea9490b2738c757665f61e09294fbd6dc5ad9 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Fri, 1 Mar 2024 12:46:15 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20feat:=20Improved=20Textarea=20Fu?= =?UTF-8?q?nctionality=20(#1942)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: paste plain text from apps with rich paste data, improved edit message textarea, improved height resizing for long text * feat(EditMessage): autofocus * chore: retain user text color when entering edit mode --- client/src/components/Chat/Input/Textarea.tsx | 6 +- .../Chat/Messages/Content/EditMessage.tsx | 61 ++++++++++++++----- client/src/hooks/Input/useTextarea.ts | 60 ++++++++++++------ 3 files changed, 91 insertions(+), 36 deletions(-) diff --git a/client/src/components/Chat/Input/Textarea.tsx b/client/src/components/Chat/Input/Textarea.tsx index e642fb92e..c2d3e5d5c 100644 --- a/client/src/components/Chat/Input/Textarea.tsx +++ b/client/src/components/Chat/Input/Textarea.tsx @@ -21,7 +21,7 @@ export default function Textarea({ select: (data) => mergeFileConfig(data), }); const { - inputRef, + textAreaRef, handlePaste, handleKeyUp, handleKeyDown, @@ -31,7 +31,7 @@ export default function Textarea({ const endpointFileConfig = fileConfig.endpoints[endpoint ?? '']; return ( ); diff --git a/client/src/components/Chat/Messages/Content/EditMessage.tsx b/client/src/components/Chat/Messages/Content/EditMessage.tsx index e888e94c8..c7c776c04 100644 --- a/client/src/components/Chat/Messages/Content/EditMessage.tsx +++ b/client/src/components/Chat/Messages/Content/EditMessage.tsx @@ -1,9 +1,11 @@ -import { useRef } from 'react'; +import { useState, useRef, useEffect } from 'react'; +import TextareaAutosize from 'react-textarea-autosize'; import { EModelEndpoint } from 'librechat-data-provider'; import { useUpdateMessageMutation } from 'librechat-data-provider/react-query'; -import Container from '~/components/Messages/Content/Container'; -import { useChatContext } from '~/Providers'; 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'; const EditMessage = ({ @@ -17,18 +19,28 @@ const EditMessage = ({ }: TEditProps) => { const { getMessages, setMessages, conversation } = useChatContext(); - const textEditor = useRef(null); + const [editedText, setEditedText] = useState(text ?? ''); + const textAreaRef = useRef(null); + const { conversationId, parentMessageId, messageId } = message; const { endpoint: _endpoint, endpointType } = conversation ?? { endpoint: null }; const endpoint = endpointType ?? _endpoint; const updateMessageMutation = useUpdateMessageMutation(conversationId ?? ''); const localize = useLocalize(); + useEffect(() => { + const textArea = textAreaRef.current; + if (textArea) { + const length = textArea.value.length; + textArea.focus(); + textArea.setSelectionRange(length, length); + } + }, []); + const resubmitMessage = () => { - const text = textEditor?.current?.innerText ?? ''; if (message.isCreatedByUser) { ask({ - text, + text: editedText, parentMessageId, conversationId, }); @@ -44,7 +56,7 @@ const EditMessage = ({ ask( { ...parentMessage }, { - editedText: text, + editedText, editedMessageId: messageId, isRegenerate: true, isEdited: true, @@ -62,19 +74,18 @@ const EditMessage = ({ if (!messages) { return; } - const text = textEditor?.current?.innerText ?? ''; updateMessageMutation.mutate({ conversationId: conversationId ?? '', model: conversation?.model ?? 'gpt-3.5-turbo', + text: editedText, messageId, - text, }); setMessages( messages.map((msg) => msg.messageId === messageId ? { ...msg, - text, + text: editedText, isEdited: true, } : msg, @@ -85,15 +96,33 @@ const EditMessage = ({ return ( -
{ + setEditedText(e.target.value); + }} data-testid="message-text-editor" - className="markdown prose dark:prose-invert light w-full whitespace-pre-wrap break-words border-none focus:outline-none" + className={cn( + 'markdown prose dark:prose-invert light whitespace-pre-wrap break-words dark:text-gray-20', + 'm-0 w-full resize-none border-0 bg-transparent p-0', + removeFocusOutlines, + )} + onPaste={(e) => { + const pastedData = e.clipboardData.getData('text/plain'); + const textArea = textAreaRef.current; + if (!textArea) { + return; + } + const start = textArea.selectionStart; + const end = textArea.selectionEnd; + const newValue = + textArea.value.substring(0, start) + pastedData + textArea.value.substring(end); + setEditedText(newValue); + }} contentEditable={true} - ref={textEditor} + value={editedText} suppressContentEditableWarning={true} - > - {text} -
+ />