diff --git a/client/src/components/Chat/Input/ChatForm.tsx b/client/src/components/Chat/Input/ChatForm.tsx index 3c7ba42457..69a62b7846 100644 --- a/client/src/components/Chat/Input/ChatForm.tsx +++ b/client/src/components/Chat/Input/ChatForm.tsx @@ -1,17 +1,30 @@ import { useRecoilState } from 'recoil'; -import type { ChangeEvent } from 'react'; +import { memo, useCallback, useRef } from 'react'; +import TextareaAutosize from 'react-textarea-autosize'; +import { useForm } from 'react-hook-form'; +import { + supportsFiles, + mergeFileConfig, + fileConfig as defaultFileConfig, +} from 'librechat-data-provider'; +import { useRequiresKey, useTextarea } from '~/hooks'; +import { useGetFileConfig } from '~/data-provider'; +import { cn, removeFocusOutlines } from '~/utils'; import { useChatContext } from '~/Providers'; -import { useRequiresKey } from '~/hooks'; import AttachFile from './Files/AttachFile'; import StopButton from './StopButton'; import SendButton from './SendButton'; import FileRow from './Files/FileRow'; -import Textarea from './Textarea'; import store from '~/store'; -export default function ChatForm({ index = 0 }) { - const [text, setText] = useRecoilState(store.textByIndex(index)); +const ChatForm = ({ index = 0 }) => { + const submitButtonRef = useRef(null); + const textAreaRef = useRef(null); const [showStopButton, setShowStopButton] = useRecoilState(store.showStopButtonByIndex(index)); + const { requiresKey } = useRequiresKey(); + + const { handlePaste, handleKeyUp, handleKeyDown, handleCompositionStart, handleCompositionEnd } = + useTextarea({ textAreaRef, submitButtonRef, disabled: !!requiresKey }); const { ask, @@ -24,21 +37,34 @@ export default function ChatForm({ index = 0 }) { setFilesLoading, } = useChatContext(); - const submitMessage = () => { - ask({ text }); - setText(''); - }; + const methods = useForm<{ text: string }>({ + defaultValues: { text: '' }, + }); + + const submitMessage = useCallback( + (data?: { text: string }) => { + if (!data) { + return console.warn('No data provided to submitMessage'); + } + ask({ text: data.text }); + methods.reset(); + textAreaRef.current?.setRangeText('', 0, data.text.length, 'end'); + }, + [ask, methods], + ); - const { requiresKey } = useRequiresKey(); const { endpoint: _endpoint, endpointType } = conversation ?? { endpoint: null }; const endpoint = endpointType ?? _endpoint; + const { data: fileConfig = defaultFileConfig } = useGetFileConfig({ + select: (data) => mergeFileConfig(data), + }); + + const endpointFileConfig = fileConfig.endpoints[endpoint ?? '']; + return (
{ - e.preventDefault(); - submitMessage(); - }} + onSubmit={methods.handleSubmit((data) => submitMessage(data))} className="stretch mx-2 flex flex-row gap-3 last:mb-2 md:mx-4 md:last:mb-6 lg:mx-auto lg:max-w-2xl xl:max-w-3xl" >
@@ -55,14 +81,36 @@ export default function ChatForm({ index = 0 }) { )} /> {endpoint && ( -