import React, { useMemo } from 'react' import { ChatCompletionRole, ContentType, ThreadMessage } from '@janhq/core' import { useAtomValue } from 'jotai' import 'katex/dist/katex.min.css' import { twMerge } from 'tailwind-merge' import LogoMark from '@/containers/Brand/Logo/Mark' import { displayDate } from '@/utils/datetime' import EditChatInput from '../EditChatInput' import MessageToolbar from '../MessageToolbar' import DocMessage from './DocMessage' import ImageMessage from './ImageMessage' import { MarkdownTextMessage } from './MarkdownTextMessage' import ThinkingBlock from './ThinkingBlock' import { activeAssistantAtom } from '@/helpers/atoms/Assistant.atom' import { editMessageAtom, tokenSpeedAtom, } from '@/helpers/atoms/ChatMessage.atom' import { selectedModelAtom } from '@/helpers/atoms/Model.atom' import { chatWidthAtom } from '@/helpers/atoms/Setting.atom' const MessageContainer: React.FC< ThreadMessage & { isCurrentMessage: boolean; index: number } > = (props) => { const isUser = props.role === ChatCompletionRole.User const isSystem = props.role === ChatCompletionRole.System const editMessage = useAtomValue(editMessageAtom) const activeAssistant = useAtomValue(activeAssistantAtom) const tokenSpeed = useAtomValue(tokenSpeedAtom) const chatWidth = useAtomValue(chatWidthAtom) const selectedModel = useAtomValue(selectedModelAtom) const text = useMemo( () => props.content.find((e) => e.type === ContentType.Text)?.text?.value ?? '', [props.content] ) const { reasoningSegment, textSegment } = useMemo(() => { const isThinking = text.includes('') && !text.includes('') if (isThinking) return { reasoningSegment: text, textSegment: '' } const match = text.match(/([\s\S]*?)<\/think>/) if (match?.index === undefined) return { reasoningSegment: undefined, textSegment: text } const splitIndex = match.index + match[0].length return { reasoningSegment: text.slice(0, splitIndex), textSegment: text.slice(splitIndex), } }, [text]) const image = useMemo( () => props.content.find((e) => e.type === ContentType.Image)?.image_url?.url, [props.content] ) const attachedFile = useMemo(() => 'attachments' in props, [props]) return (
{!isUser && !isSystem && }
{!isUser && ( <> {props.metadata && 'model' in props.metadata ? (props.metadata?.model as string) : props.isCurrentMessage ? selectedModel?.name : (activeAssistant?.assistant_name ?? props.role)} )}

{props.created_at && displayDate(props.created_at ?? Date.now() / 1000)}

<> {image && } {attachedFile && ( )} {editMessage === props.id ? (
) : (
{reasoningSegment && ( )}
)}
) } export default React.memo(MessageContainer)