import React, { useState, useEffect } from 'react';
import type { TMessage } from 'librechat-data-provider';
import { useRecoilValue } from 'recoil';
import ReactMarkdown from 'react-markdown';
import type { PluggableList } from 'unified';
import rehypeKatex from 'rehype-katex';
import rehypeHighlight from 'rehype-highlight';
import remarkMath from 'remark-math';
import supersub from 'remark-supersub';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import CodeBlock from './CodeBlock';
import { langSubset, validateIframe } from '~/utils';
import store from '~/store';
type TCodeProps = {
inline: boolean;
className: string;
children: React.ReactNode;
};
type TContentProps = {
content: string;
message: TMessage;
showCursor?: boolean;
};
const code = React.memo(({ inline, className, children }: TCodeProps) => {
const match = /language-(\w+)/.exec(className || '');
const lang = match && match[1];
if (inline) {
return {children};
} else {
return
{children}
; }); const Markdown = React.memo(({ content, message, showCursor }: TContentProps) => { const [cursor, setCursor] = useState('█'); const isSubmitting = useRecoilValue(store.isSubmitting); const latestMessage = useRecoilValue(store.latestMessage); const isInitializing = content === '█'; const isLatestMessage = message?.messageId === latestMessage?.messageId; const currentContent = content?.replace('z-index: 1;', '') ?? ''; const isValidIFrame = validateIframe(currentContent); useEffect(() => { let timer1: NodeJS.Timeout, timer2: NodeJS.Timeout; if (!showCursor) { setCursor('ㅤ'); return; } if (isSubmitting && isLatestMessage) { timer1 = setInterval(() => { setCursor('ㅤ'); timer2 = setTimeout(() => { setCursor('█'); }, 200); }, 1000); } else { setCursor('ㅤ'); } // This is the cleanup function that React will run when the component unmounts return () => { clearInterval(timer1); clearTimeout(timer2); }; }, [isSubmitting, isLatestMessage, showCursor]); const rehypePlugins: PluggableList = [ [rehypeKatex, { output: 'mathml' }], [ rehypeHighlight, { detect: true, ignoreMissing: true, subset: langSubset, }, ], [rehypeRaw], ]; if ((!isInitializing || !isLatestMessage) && !isValidIFrame) { rehypePlugins.pop(); } return (