| 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"; |