| import React from "react"; | |
| import { Button } from "@/components/ui/button"; | |
| import { X, Check, RefreshCcw, Pencil, ClipboardCopy } from "lucide-react"; | |
| import { AutosizeTextarea } from "@/components/ui/autosize-textarea"; | |
| import { toast } from "sonner"; | |
| import { MessageProps } from "../types"; | |
| import { DocumentBadgesScrollArea } from "./DocumentBadgesScrollArea"; | |
| interface HumanMessageComponentProps extends MessageProps { | |
| onEdit?: () => void; | |
| onRegenerate?: () => void; | |
| isEditing?: boolean; | |
| onSave?: (content: string) => void; | |
| onCancelEdit?: () => void; | |
| } | |
| export const HumanMessageComponent = React.memo(({ | |
| message, | |
| setPreviewDocument, | |
| onEdit, | |
| onRegenerate, | |
| isEditing, | |
| onSave, | |
| onCancelEdit | |
| }: HumanMessageComponentProps) => { | |
| const [editedContent, setEditedContent] = React.useState(String(message.content)); | |
| if (message.response_metadata?.documents?.length) { | |
| return ( | |
| <div className="flex flex-col gap-1 max-w-[70%] ml-auto items-end"> | |
| <DocumentBadgesScrollArea | |
| documents={message.response_metadata.documents} | |
| onPreview={(doc) => setPreviewDocument?.(doc)} | |
| onRemove={() => {}} | |
| removeable={false} | |
| maxHeight="200px" | |
| /> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className="flex flex-col gap-1 max-w-[70%] ml-auto items-end group"> | |
| {isEditing ? ( | |
| <div className="flex flex-col gap-2 w-full"> | |
| <AutosizeTextarea | |
| value={editedContent} | |
| onChange={(e) => setEditedContent(e.target.value)} | |
| className="bg-muted p-5 rounded-md" | |
| maxHeight={300} | |
| /> | |
| <div className="flex gap-2 justify-end"> | |
| <Button | |
| variant="ghost" | |
| size="sm" | |
| onClick={onCancelEdit} | |
| > | |
| <X className="h-4 w-4 mr-2" /> | |
| Cancel | |
| </Button> | |
| <Button | |
| size="sm" | |
| onClick={() => onSave?.(editedContent)} | |
| > | |
| <Check className="h-4 w-4 mr-2" /> | |
| Save | |
| </Button> | |
| </div> | |
| </div> | |
| ) : ( | |
| <> | |
| <div className="flex p-5 bg-muted rounded-md" style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}> | |
| {String(message.content)} | |
| </div> | |
| <div className="flex flex-row gap-1 opacity-0 group-hover:opacity-100"> | |
| <Button variant="ghost" size="icon" onClick={onRegenerate}> | |
| <RefreshCcw className="h-4 w-4" /> | |
| </Button> | |
| <Button variant="ghost" size="icon" onClick={onEdit}> | |
| <Pencil className="h-4 w-4" /> | |
| </Button> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| onClick={() => navigator.clipboard.writeText(String(message.content)).then(() => toast.success("Message copied to clipboard"))} | |
| > | |
| <ClipboardCopy className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| </> | |
| )} | |
| </div> | |
| ); | |
| }); | |
| HumanMessageComponent.displayName = "HumanMessageComponent"; |