📝 feat: Improved Textarea Functionality (#1942)

* 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
This commit is contained in:
Danny Avila 2024-03-01 12:46:15 -05:00 committed by GitHub
parent de0cee3f56
commit c52ea9490b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 91 additions and 36 deletions

View file

@ -1,7 +1,8 @@
import debounce from 'lodash/debounce';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useCallback } from 'react';
import { EModelEndpoint } from 'librechat-data-provider';
import type { TEndpointOption } from 'librechat-data-provider';
import type { SetterOrUpdater } from 'recoil';
import type { KeyboardEvent } from 'react';
import { useAssistantsMapContext } from '~/Providers/AssistantsMapContext';
import useGetSender from '~/hooks/Conversations/useGetSender';
@ -25,12 +26,20 @@ const getAssistantName = ({
}
};
export default function useTextarea({ setText, submitMessage, disabled = false }) {
export default function useTextarea({
setText,
submitMessage,
disabled = false,
}: {
setText: SetterOrUpdater<string>;
submitMessage: () => void;
disabled?: boolean;
}) {
const assistantMap = useAssistantsMapContext();
const { conversation, isSubmitting, latestMessage, setShowBingToneSetting, setFilesLoading } =
useChatContext();
const isComposing = useRef(false);
const inputRef = useRef<HTMLTextAreaElement | null>(null);
const textAreaRef = useRef<HTMLTextAreaElement | null>(null);
const { handleFiles } = useFileHandling();
const getSender = useGetSender();
const localize = useLocalize();
@ -54,7 +63,7 @@ export default function useTextarea({ setText, submitMessage, disabled = false }
}
if (conversationId !== 'search') {
inputRef.current?.focus();
textAreaRef.current?.focus();
}
// setShowBingToneSetting is a recoil setter, so it doesn't need to be in the dependency array
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -62,14 +71,14 @@ export default function useTextarea({ setText, submitMessage, disabled = false }
useEffect(() => {
const timeoutId = setTimeout(() => {
inputRef.current?.focus();
textAreaRef.current?.focus();
}, 100);
return () => clearTimeout(timeoutId);
}, [isSubmitting]);
useEffect(() => {
if (inputRef.current?.value) {
if (textAreaRef.current?.value) {
return;
}
@ -91,15 +100,15 @@ export default function useTextarea({ setText, submitMessage, disabled = false }
const placeholder = getPlaceholderText();
if (inputRef.current?.getAttribute('placeholder') === placeholder) {
if (textAreaRef.current?.getAttribute('placeholder') === placeholder) {
return;
}
const setPlaceholder = () => {
const placeholder = getPlaceholderText();
if (inputRef.current?.getAttribute('placeholder') !== placeholder) {
inputRef.current?.setAttribute('placeholder', placeholder);
if (textAreaRef.current?.getAttribute('placeholder') !== placeholder) {
textAreaRef.current?.setAttribute('placeholder', placeholder);
}
};
@ -147,16 +156,33 @@ export default function useTextarea({ setText, submitMessage, disabled = false }
isComposing.current = false;
};
const handlePaste = (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
if (e.clipboardData && e.clipboardData.files.length > 0) {
e.preventDefault();
setFilesLoading(true);
handleFiles(e.clipboardData.files);
}
};
const handlePaste = useCallback(
(e: React.ClipboardEvent<HTMLTextAreaElement>) => {
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);
setText(newValue);
if (e.clipboardData && e.clipboardData.files.length > 0) {
e.preventDefault();
setFilesLoading(true);
handleFiles(e.clipboardData.files);
}
},
[handleFiles, setFilesLoading, setText],
);
return {
inputRef,
textAreaRef,
handleKeyDown,
handleKeyUp,
handlePaste,