@plteam/chat-ui
Version:
CUI Kit is a free and open-source library for creating AI assistant chat interfaces, built with React, Material UI, and TypeScript
90 lines (89 loc) • 5.19 kB
JavaScript
import * as React from 'react';
import MessageContainer from './MessageContainer';
import { styled } from '@mui/material/styles';
import MessageMarkdownBlock from './markdown/MessageMarkdownBlock';
import { messageActionsClasses } from './messageActionsClasses';
import MessageActionsUser from './actions/MessageActionsUser';
import MessageUserEditor from './editor/MessageUserEditor';
import MessagePagination from './MessagePagination';
import { MessageStateEnum } from './hooks/useMessagesMode';
import Stack from '@mui/material/Stack';
import { useThreadContext } from '../thread/ThreadContext';
import { useObserverValue } from '../hooks/useObserverValue';
import { useChatContext } from '../core/ChatGlobalContext';
import { useChatSlots } from '../core/ChatSlotsContext';
import { motion } from '../../utils/materialDesign/motion';
import { useElementRefState } from '../hooks/useElementRef';
import useHover from '../hooks/useHover';
import { useTablet } from '../../ui/Responsive';
import clsx from 'clsx';
import MessageAttachments from './attachments/MessageAttachments';
import { chatClassNames } from '../core/chatClassNames';
import attachmentsStore from '../../models/AttachmentsStore';
const { actionsClassName, paginationClassName, hoverMessageClassName, } = messageActionsClasses;
const StackStyled = styled(Stack)(({ theme }) => ({
[`& .${actionsClassName}`]: {
opacity: 0,
transition: theme.transitions.create('opacity', { duration: motion.duration.short3 }),
},
[`&.${hoverMessageClassName}`]: {
[`& .${actionsClassName}`]: {
opacity: 1,
},
},
[`:has(.${paginationClassName})`]: {
marginBottom: theme.spacing(1.5),
}
}));
const MessageUser = ({ message, thread, isFirst, elevation }) => {
const { element, setElement } = useElementRefState();
const isTablet = useTablet();
const isTyping = useObserverValue(thread?.isTyping);
const { messageMode, apiRef } = useThreadContext();
const { onAssistantMessageTypingFinish, enableBranches, disableFileAttachmentsEditing } = useChatContext();
const { slots, slotProps } = useChatSlots();
const { itemsAll, deletedIds } = message.attachments;
const mode = messageMode.values[message.id];
const isHover = useHover(element);
const onClickEdit = () => {
messageMode.edit(message.id);
};
const onClickApplyEdit = async (newText) => {
messageMode.view(message.id);
let content = newText;
if (itemsAll.value.length) {
const attachments = itemsAll.value?.filter((i) => !deletedIds.value.includes(i.id)) || [];
attachmentsStore.items = attachments;
content = [{
type: 'text',
text: newText,
}, ...attachments.map((a) => a.contentData)];
}
const newMessage = await apiRef.current?.onEditMessage(content, message);
if (newMessage) {
message.attachments.deletedIds.value = [];
// Branch changes in the message sending hook before editing
// apiRef.current?.handleChangeBranch(newMessage);
onAssistantMessageTypingFinish?.({ message: message.data, thread: thread.data });
}
};
const onClickCancelEdit = () => {
messageMode.view(message.id);
message.attachments.deletedIds.value = [];
};
const onDeleteAttachment = (id) => {
message.attachments.deletedIds.value = [...deletedIds.value, id];
};
if (mode === MessageStateEnum.EDIT) {
return (React.createElement(Stack, { width: "80%", gap: 1, mx: 1.5, alignItems: "flex-end" },
React.createElement(MessageAttachments, { message: message, onDeleteAttachment: disableFileAttachmentsEditing ? undefined : onDeleteAttachment }),
React.createElement(MessageUserEditor, { message: message, onClickApply: onClickApplyEdit, onClickCancel: onClickCancelEdit })));
}
return (React.createElement(StackStyled, { ref: setElement, gap: 1, alignItems: 'flex-end', position: 'relative', mx: 1.5, className: clsx({ [hoverMessageClassName]: isHover || isTablet }, chatClassNames.messageUserRoot), width: message.text ? '80%' : 'auto', maxWidth: "80%" },
React.createElement(MessageAttachments, { message: message }),
message.text ? (React.createElement(MessageContainer, { className: chatClassNames.messageUser, elevation: elevation, sx: { maxWidth: '100%', backgroundColor: (theme) => theme.palette.grey[200] } },
React.createElement(MessageMarkdownBlock, { text: message.text, rootComponent: slots.markdownMessageRoot, rootComponentProps: slotProps.markdownMessageRoot, inProgress: false }))) : null,
((isFirst || message.parentId) && !!enableBranches) ? (React.createElement(MessageActionsUser, { className: actionsClassName, disabled: isTyping, onClickEdit: onClickEdit })) : null,
enableBranches ? (React.createElement(MessagePagination, { disabled: isTyping, message: message, classes: messageActionsClasses })) : null));
};
export default MessageUser;