1
- import { useRecoilState } from 'recoil' ;
2
- import TextareaAutosize from 'react-textarea-autosize' ;
1
+ import { useRecoilState , useRecoilValue } from 'recoil' ;
3
2
import { EModelEndpoint } from 'librechat-data-provider' ;
4
- import { useState , useRef , useEffect , useCallback } from 'react' ;
3
+ import { useRef , useEffect , useCallback } from 'react' ;
4
+ import { useForm } from 'react-hook-form' ;
5
5
import { useUpdateMessageMutation } from 'librechat-data-provider/react-query' ;
6
6
import type { TEditProps } from '~/common' ;
7
7
import { useChatContext , useAddedChatContext } from '~/Providers' ;
8
+ import { TextareaAutosize } from '~/components/ui' ;
8
9
import { cn , removeFocusRings } from '~/utils' ;
9
10
import { useLocalize } from '~/hooks' ;
10
11
import Container from './Container' ;
@@ -25,7 +26,6 @@ const EditMessage = ({
25
26
store . latestMessageFamily ( addedIndex ) ,
26
27
) ;
27
28
28
- const [ editedText , setEditedText ] = useState < string > ( text ?? '' ) ;
29
29
const textAreaRef = useRef < HTMLTextAreaElement | null > ( null ) ;
30
30
31
31
const { conversationId, parentMessageId, messageId } = message ;
@@ -34,6 +34,15 @@ const EditMessage = ({
34
34
const updateMessageMutation = useUpdateMessageMutation ( conversationId ?? '' ) ;
35
35
const localize = useLocalize ( ) ;
36
36
37
+ const chatDirection = useRecoilValue ( store . chatDirection ) . toLowerCase ( ) ;
38
+ const isRTL = chatDirection === 'rtl' ;
39
+
40
+ const { register, handleSubmit, setValue } = useForm ( {
41
+ defaultValues : {
42
+ text : text ?? '' ,
43
+ } ,
44
+ } ) ;
45
+
37
46
useEffect ( ( ) => {
38
47
const textArea = textAreaRef . current ;
39
48
if ( textArea ) {
@@ -43,11 +52,11 @@ const EditMessage = ({
43
52
}
44
53
} , [ ] ) ;
45
54
46
- const resubmitMessage = ( ) => {
55
+ const resubmitMessage = ( data : { text : string } ) => {
47
56
if ( message . isCreatedByUser ) {
48
57
ask (
49
58
{
50
- text : editedText ,
59
+ text : data . text ,
51
60
parentMessageId,
52
61
conversationId,
53
62
} ,
@@ -67,7 +76,7 @@ const EditMessage = ({
67
76
ask (
68
77
{ ...parentMessage } ,
69
78
{
70
- editedText,
79
+ editedText : data . text ,
71
80
editedMessageId : messageId ,
72
81
isRegenerate : true ,
73
82
isEdited : true ,
@@ -80,32 +89,32 @@ const EditMessage = ({
80
89
enterEdit ( true ) ;
81
90
} ;
82
91
83
- const updateMessage = ( ) => {
92
+ const updateMessage = ( data : { text : string } ) => {
84
93
const messages = getMessages ( ) ;
85
94
if ( ! messages ) {
86
95
return ;
87
96
}
88
97
updateMessageMutation . mutate ( {
89
98
conversationId : conversationId ?? '' ,
90
99
model : conversation ?. model ?? 'gpt-3.5-turbo' ,
91
- text : editedText ,
100
+ text : data . text ,
92
101
messageId,
93
102
} ) ;
94
103
95
104
if ( message . messageId === latestMultiMessage ?. messageId ) {
96
- setLatestMultiMessage ( { ...latestMultiMessage , text : editedText } ) ;
105
+ setLatestMultiMessage ( { ...latestMultiMessage , text : data . text } ) ;
97
106
}
98
107
99
- const isInMessages = messages ? .some ( ( message ) => message ? .messageId === messageId ) ;
108
+ const isInMessages = messages . some ( ( message ) => message . messageId === messageId ) ;
100
109
if ( ! isInMessages ) {
101
- message . text = editedText ;
110
+ message . text = data . text ;
102
111
} else {
103
112
setMessages (
104
113
messages . map ( ( msg ) =>
105
114
msg . messageId === messageId
106
115
? {
107
116
...msg ,
108
- text : editedText ,
117
+ text : data . text ,
109
118
isEdited : true ,
110
119
}
111
120
: msg ,
@@ -126,43 +135,33 @@ const EditMessage = ({
126
135
[ enterEdit ] ,
127
136
) ;
128
137
138
+ const { ref, ...registerProps } = register ( 'text' , {
139
+ required : true ,
140
+ onChange : ( e ) => {
141
+ setValue ( 'text' , e . target . value , { shouldValidate : true } ) ;
142
+ } ,
143
+ } ) ;
144
+
129
145
return (
130
146
< Container message = { message } >
131
- < div className = "bg-token-main-surface-primary relative flex w-full flex-grow flex-col overflow-hidden rounded-2xl border dark: border-gray-600 dark: text-white [&:has(textarea:focus)]:border-gray-300 [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)] dark:[&:has(textarea:focus)]:border-gray-500 " >
147
+ < div className = "bg-token-main-surface-primary relative flex w-full flex-grow flex-col overflow-hidden rounded-2xl border border-border-medium text-text-primary [&:has(textarea:focus)]:border-border-heavy [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)]" >
132
148
< TextareaAutosize
133
- ref = { textAreaRef }
134
- onChange = { ( e ) => {
135
- setEditedText ( e . target . value ) ;
149
+ { ...registerProps }
150
+ ref = { ( e ) => {
151
+ ref ( e ) ;
152
+ textAreaRef . current = e ;
136
153
} }
137
154
onKeyDown = { handleKeyDown }
138
155
data-testid = "message-text-editor"
139
156
className = { cn (
140
- 'markdown prose dark:prose-invert light whitespace-pre-wrap break-words' ,
141
- 'pl-3 md:pl-4' ,
157
+ 'markdown prose dark:prose-invert light whitespace-pre-wrap break-words pl-3 md:pl-4' ,
142
158
'm-0 w-full resize-none border-0 bg-transparent py-[10px]' ,
143
- 'placeholder-black/50 focus:ring-0 focus-visible:ring-0 dark:bg-transparent dark:placeholder-white/50 md:py-3.5 ' ,
144
- 'pr-3 md:pr-4 ',
145
- 'max-h-[65vh] md:max-h-[75vh]' ,
159
+ 'placeholder-text-secondary focus:ring-0 focus-visible:ring-0 md:py-3.5' ,
160
+ isRTL ? 'text-right' : 'text-left ',
161
+ 'max-h-[65vh] pr-3 md:max-h-[75vh] md:pr-4 ' ,
146
162
removeFocusRings ,
147
163
) }
148
- onPaste = { ( e ) => {
149
- e . preventDefault ( ) ;
150
-
151
- const pastedData = e . clipboardData . getData ( 'text/plain' ) ;
152
- const textArea = textAreaRef . current ;
153
- if ( ! textArea ) {
154
- return ;
155
- }
156
- const start = textArea . selectionStart ;
157
- const end = textArea . selectionEnd ;
158
- const newValue =
159
- textArea . value . substring ( 0 , start ) + pastedData + textArea . value . substring ( end ) ;
160
- setEditedText ( newValue ) ;
161
- } }
162
- contentEditable = { true }
163
- value = { editedText }
164
- suppressContentEditableWarning = { true }
165
- dir = "auto"
164
+ dir = { isRTL ? 'rtl' : 'ltr' }
166
165
/>
167
166
</ div >
168
167
< div className = "mt-2 flex w-full justify-center text-center" >
@@ -171,14 +170,14 @@ const EditMessage = ({
171
170
disabled = {
172
171
isSubmitting || ( endpoint === EModelEndpoint . google && ! message . isCreatedByUser )
173
172
}
174
- onClick = { resubmitMessage }
173
+ onClick = { handleSubmit ( resubmitMessage ) }
175
174
>
176
175
{ localize ( 'com_ui_save_submit' ) }
177
176
</ button >
178
177
< button
179
178
className = "btn btn-secondary relative mr-2"
180
179
disabled = { isSubmitting }
181
- onClick = { updateMessage }
180
+ onClick = { handleSubmit ( updateMessage ) }
182
181
>
183
182
{ localize ( 'com_ui_save' ) }
184
183
</ button >
0 commit comments