UNPKG

@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

93 lines (92 loc) 5.84 kB
import * as React from 'react'; import Stack from '@mui/material/Stack'; import Box from '@mui/material/Box'; import { styled } from '@mui/material/styles'; import MessageReasoningFull from './MessageReasoningFull'; import { useObserverValue } from '../../hooks/useObserverValue'; import { StreamResponseState } from '../../../models'; import { Collapse, Fade } from '@mui/material'; import { useReasoningParse } from './useReasoningParse'; import ReasoningTextSmooth from './ReasoningTextSmooth'; import MessageReasoningTitle from './MessageReasoningTitle'; import { useThreadContext } from '../../thread/ThreadContext'; import SimpleScrollbar from '../../../ui/SimpleScrollbar'; import { ReasoningViewType } from '../../../models/MessageReasoningModel'; import { chatClassNames } from '../../core/chatClassNames'; const LineBoxStyled = styled(Box)(({ theme }) => ({ height: '100%', width: 4, minWidth: 4, backgroundColor: theme.palette.grey[300], borderRadius: 999, transition: theme.transitions.create('opacity', { duration: '.5s' }), })); var ViewType; (function (ViewType) { ViewType[ViewType["SHORT"] = 0] = "SHORT"; ViewType[ViewType["FULL"] = 1] = "FULL"; })(ViewType || (ViewType = {})); const transitionDuration = 200; const MessageReasoning = ({ message, thread, isLatest }) => { const [viewType, setViewType] = React.useState(ViewType.SHORT); const [fullCollapseSize, setFullCollapseSize] = React.useState(0); const { apiRef } = useThreadContext(); const statusText = useObserverValue(thread.streamStatus) ?? ''; const shortRef = React.useRef(null); const reasoning = useObserverValue(message.reasoningManager.text) ?? ''; const inProgress = statusText === StreamResponseState.THINKING && !!isLatest && !!reasoning; const [isExpanding, setIsExpanding] = React.useState(false); const { reasoningType, description } = useReasoningParse(reasoning, message, inProgress); const handleExpandedChange = () => { if (!isExpanding) { setFullCollapseSize(shortRef.current?.clientHeight ?? 0); } const newValue = !isExpanding; setIsExpanding(newValue); if (newValue) { setViewType(ViewType.FULL); } else { setTimeout(() => setViewType(ViewType.SHORT), transitionDuration); } setTimeout(() => apiRef.current?.updateScrollButtonState(), transitionDuration); // setTimeout(() => setViewType(newValue ? ViewType.FULL : ViewType.SHORT),transitionDuration); }; const isFull = viewType === ViewType.FULL; const isShort = viewType === ViewType.SHORT; if (!reasoning) return null; return (React.createElement(Stack, { gap: 1.5, className: chatClassNames.messageReasoningRoot }, React.createElement(MessageReasoningTitle, { message: message, inProgress: inProgress, isExpanding: isExpanding, handleExpandedChange: handleExpandedChange }), React.createElement(Stack, { direction: "row", alignItems: "center" }, React.createElement(LineBoxStyled, { sx: { opacity: reasoning ? 1 : 0 } }), (reasoningType === ReasoningViewType.STREAM || reasoningType === ReasoningViewType.HEADERS_STREAM) && (React.createElement(Collapse, { in: isExpanding, timeout: transitionDuration, sx: { flex: 1 } }, React.createElement(Box, { ml: 2, flex: 1 }, React.createElement(SimpleScrollbar, { style: { maxHeight: 300 } }, React.createElement(MessageReasoningFull, { text: reasoning, isProgress: inProgress }))))), reasoningType === ReasoningViewType.SHORT_BLOCKS && (React.createElement(React.Fragment, null, React.createElement(Collapse, { in: isExpanding, timeout: transitionDuration, collapsedSize: fullCollapseSize }, React.createElement(Fade, { in: isExpanding, timeout: transitionDuration }, React.createElement(Box, { display: isFull ? undefined : 'none', ml: 2 }, React.createElement(MessageReasoningFull, { text: reasoning, isProgress: inProgress })))), React.createElement(Fade, { in: isShort, timeout: transitionDuration }, React.createElement(Box // Animation replays after switching from display: none // https://www.w3.org/TR/css-animations-1/#animations // If an element has a display of none, updating display to a value other than none will start all animations applied to the element by the animation-name property, // as well as all animations applied to descendants with display other than none. , { // Animation replays after switching from display: none // https://www.w3.org/TR/css-animations-1/#animations // If an element has a display of none, updating display to a value other than none will start all animations applied to the element by the animation-name property, // as well as all animations applied to descendants with display other than none. ref: shortRef, height: isFull ? 0 : 'auto', overflow: "hidden", width: isFull ? 0 : undefined, visibility: isFull ? 'hidden' : 'visible', ml: 2, onClick: handleExpandedChange }, React.createElement(ReasoningTextSmooth, { text: description })))))))); }; export default React.memo(MessageReasoning, (prevProps, nextProps) => { return prevProps.message.id === nextProps.message.id && prevProps.thread.id === nextProps.thread.id && prevProps.isLatest === nextProps.isLatest; }); ;