@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
JavaScript
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;
});
;