@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
61 lines (60 loc) • 3.69 kB
JavaScript
import * as React from 'react';
import { styled } from '@mui/material/styles';
import MessageContainer from './MessageContainer';
import MessageActionsAssistant from './actions/MessageActionsAssistant';
import { clsx } from 'clsx';
import { messageActionsClasses } from './messageActionsClasses';
import { useObserverValue } from '../hooks/useObserverValue';
import { useChatSlots } from '../core/ChatSlotsContext';
import MessageReasoning from './reasoning/MessageReasoning';
import { useChatContext } from '../core/ChatGlobalContext';
import { useInternalMessageTransformer } from '../adapter/AdapterContext';
import Stack from '@mui/material/Stack';
import AssistantTextBlock from './AssistantTextBlock';
import { chatClassNames } from '../core/chatClassNames';
import { usePhotoswipeInitialization } from './hooks/usePhotoswipeInitialization';
import { useResolvedSpeed } from '../core/useResolvedSpeed';
import { triggerSmoothCheck } from './markdown/smooth/useSmoothManager';
const { actionsClassName, } = messageActionsClasses;
const MessageContainerStyled = styled(MessageContainer)(() => ({
width: '100%',
flexDirection: 'column',
}));
const MessageAssistant = ({ message, enableAssistantActions, thread, elevation }) => {
// const { element, setElement } = useElementRefState();
// const isHover = useHover(element);
const texts = useObserverValue(message.texts) ?? [];
const typing = useObserverValue(message.typing);
const { slots, slotProps } = useChatSlots();
const { enableReasoning } = useChatContext();
const { typing: typingSpeed, stagger } = useResolvedSpeed();
const [isTypedOnce, setIsTypedOnce] = React.useState(false);
React.useEffect(() => {
if (typing && !isTypedOnce) {
setIsTypedOnce(true);
}
;
}, [typing]);
// The action buttons + footer mount only once typing finishes, outside the streaming
// text flow, so nothing else triggers the smoother for them. Kick a check here so their
// pending -> animating fade actually runs. The manager's rerun guard makes this safe even
// if the final text batch is still animating.
React.useEffect(() => {
if (!typing && enableAssistantActions && isTypedOnce) {
triggerSmoothCheck(typingSpeed, stagger);
}
}, [typing, enableAssistantActions, isTypedOnce]);
const getInternalMessage = useInternalMessageTransformer();
const containerId = message.photoswipeContainerId;
usePhotoswipeInitialization(containerId, typing);
return (React.createElement(MessageContainerStyled
// ref={setElement}
, {
// ref={setElement}
gap: 1, className: clsx(chatClassNames.messageAssistantRoot), elevation: elevation },
(enableReasoning) ? (React.createElement(MessageReasoning, { message: message })) : null,
React.createElement(Stack, { id: containerId, gap: 1 }, texts.map((text, index) => (React.createElement(AssistantTextBlock, { key: text.modelId, message: message, messageText: text, showStatus: index === texts.length - 1, inProgress: !!typing })))),
(!typing && enableAssistantActions) ? (React.createElement(MessageActionsAssistant, { message: message, thread: thread, className: actionsClassName, isTypedOnce: isTypedOnce })) : null,
(!typing && !!enableAssistantActions) && (React.createElement(slots.messageAssistantFooter, { ...slotProps.messageAssistantFooter, message: getInternalMessage(message), className: clsx({ [chatClassNames.markdownSmoothedPending]: isTypedOnce }, slotProps.messageAssistantFooter?.className) }))));
};
export default MessageAssistant;