| 'use client'; | |
| import { Button } from './ui/button'; | |
| import { | |
| type Dispatch, | |
| type SetStateAction, | |
| useEffect, | |
| useRef, | |
| useState, | |
| } from 'react'; | |
| import { Textarea } from './ui/textarea'; | |
| import { deleteTrailingMessages } from '@/app/(chat)/actions'; | |
| import type { UseChatHelpers } from '@ai-sdk/react'; | |
| import type { ChatMessage } from '@/lib/types'; | |
| import { getTextFromMessage } from '@/lib/utils'; | |
| export type MessageEditorProps = { | |
| message: ChatMessage; | |
| setMode: Dispatch<SetStateAction<'view' | 'edit'>>; | |
| setMessages: UseChatHelpers<ChatMessage>['setMessages']; | |
| regenerate: UseChatHelpers<ChatMessage>['regenerate']; | |
| }; | |
| export function MessageEditor({ | |
| message, | |
| setMode, | |
| setMessages, | |
| regenerate, | |
| }: MessageEditorProps) { | |
| const [isSubmitting, setIsSubmitting] = useState<boolean>(false); | |
| const [draftContent, setDraftContent] = useState<string>( | |
| getTextFromMessage(message), | |
| ); | |
| const textareaRef = useRef<HTMLTextAreaElement>(null); | |
| useEffect(() => { | |
| if (textareaRef.current) { | |
| adjustHeight(); | |
| } | |
| }, []); | |
| const adjustHeight = () => { | |
| if (textareaRef.current) { | |
| textareaRef.current.style.height = 'auto'; | |
| textareaRef.current.style.height = `${textareaRef.current.scrollHeight + 2}px`; | |
| } | |
| }; | |
| const handleInput = (event: React.ChangeEvent<HTMLTextAreaElement>) => { | |
| setDraftContent(event.target.value); | |
| adjustHeight(); | |
| }; | |
| return ( | |
| <div className="flex flex-col gap-2 w-full"> | |
| <Textarea | |
| data-testid="message-editor" | |
| ref={textareaRef} | |
| className="bg-transparent outline-none overflow-hidden resize-none !text-base rounded-xl w-full" | |
| value={draftContent} | |
| onChange={handleInput} | |
| /> | |
| <div className="flex flex-row gap-2 justify-end"> | |
| <Button | |
| variant="outline" | |
| className="h-fit py-2 px-3" | |
| onClick={() => { | |
| setMode('view'); | |
| }} | |
| > | |
| Cancel | |
| </Button> | |
| <Button | |
| data-testid="message-editor-send-button" | |
| variant="default" | |
| className="h-fit py-2 px-3" | |
| disabled={isSubmitting} | |
| onClick={async () => { | |
| setIsSubmitting(true); | |
| await deleteTrailingMessages({ | |
| id: message.id, | |
| }); | |
| setMessages((messages) => { | |
| const index = messages.findIndex((m) => m.id === message.id); | |
| if (index !== -1) { | |
| const updatedMessage: ChatMessage = { | |
| ...message, | |
| parts: [{ type: 'text', text: draftContent }], | |
| }; | |
| return [...messages.slice(0, index), updatedMessage]; | |
| } | |
| return messages; | |
| }); | |
| setMode('view'); | |
| regenerate(); | |
| }} | |
| > | |
| {isSubmitting ? 'Sending...' : 'Send'} | |
| </Button> | |
| </div> | |
| </div> | |
| ); | |
| } | |