ds-markdown
Version:
> 🚀 React Markdown 打字动画组件,提供现代聊天界面效果
122 lines • 5.92 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
/* eslint-disable @typescript-eslint/no-explicit-any */
import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef } from 'react';
import gfmPlugin from 'remark-gfm';
import classNames from 'classnames';
import { __DEV__, katexId } from '../constant.js';
import { MarkdownTyperCMD } from 'react-markdown-typer';
import { DEFAULT_ANSWER_TYPE, DEFAULT_PLUGINS, DEFAULT_THEME, MarkdownThemeProvider, useMarkdownThemeContext } from '../context/MarkdownThemeProvider.js';
import { MarkdownProvider } from '../context/MarkdownProvider.js';
import { useConfig } from '../context/ConfigProvider/index.js';
import { replaceMathBracket } from '../utils/remarkMathBracket.js';
import CodeComponent from '../components/CodeComponent/index.js';
const MarkdownCMDInner = forwardRef(({ answerType = 'answer', timerType = 'requestAnimationFrame', ...rest }, ref) => {
var _a, _b;
const { state: themeState } = useMarkdownThemeContext();
const cmdRef = useRef(null);
// 从 context 中获取主题配置
const currentTheme = themeState.theme;
useImperativeHandle(ref, () => ({
push: cmdRef.current.push,
clear: cmdRef.current.clear,
triggerWholeEnd: cmdRef.current.triggerWholeEnd,
stop: cmdRef.current.stop,
resume: cmdRef.current.resume,
start: cmdRef.current.start,
restart: cmdRef.current.restart,
}));
const { katexConfig } = useConfig();
// 从 context 中获取主题配置
const currentMath = themeState.math;
const currentPlugins = themeState.plugins;
const mathSplitSymbol = (_a = currentMath === null || currentMath === void 0 ? void 0 : currentMath.splitSymbol) !== null && _a !== void 0 ? _a : 'dollar';
const finalReplaceMathBracket = (_b = currentMath === null || currentMath === void 0 ? void 0 : currentMath.replaceMathBracket) !== null && _b !== void 0 ? _b : replaceMathBracket;
const { remarkPlugins, rehypePlugins, hasKatexPlugin, components } = useMemo(() => {
let hasKatexPlugin = false;
const components = {};
const remarkPlugins = [gfmPlugin];
const rehypePlugins = [];
if (!currentPlugins) {
return {
remarkPlugins,
rehypePlugins,
};
}
currentPlugins.forEach((plugin) => {
if (plugin.id === katexId) {
hasKatexPlugin = true;
remarkPlugins.push(plugin.remarkPlugin);
rehypePlugins.push([plugin.rehypePlugin, katexConfig]);
}
else {
if (plugin.rehypePlugin) {
rehypePlugins.push(plugin.rehypePlugin);
}
if (plugin.remarkPlugin) {
remarkPlugins.push(plugin.remarkPlugin);
}
}
if (plugin.components) {
Object.assign(components, plugin.components);
}
});
return {
remarkPlugins,
rehypePlugins,
hasKatexPlugin,
components,
};
}, [currentPlugins, katexConfig]);
const customConvertMarkdownString = useCallback((markdownString) => {
/** 如果存在数学公式插件,并且数学公式分隔符为括号,则替换成 $ 符号 */
if (hasKatexPlugin && mathSplitSymbol === 'bracket') {
return finalReplaceMathBracket(markdownString);
}
return markdownString;
}, [finalReplaceMathBracket, hasKatexPlugin, mathSplitSymbol]);
return (_jsx("div", { className: classNames({
'ds-markdown': true,
apple: true,
'ds-markdown-dark': currentTheme === 'dark',
}), children: _jsx("div", { className: `ds-markdown-${answerType}`, children: _jsx(MarkdownTyperCMD, { ref: cmdRef, timerType: timerType, customConvertMarkdownString: customConvertMarkdownString, ...rest, reactMarkdownProps: {
remarkPlugins,
rehypePlugins,
components: {
code: CodeComponent,
table: ({ children, ...props }) => {
return (_jsx("div", { className: "markdown-table-wrapper", children: _jsx("table", { className: "ds-markdown-table", children: children }) }));
},
...components,
},
} }) }) }));
});
if (__DEV__) {
MarkdownCMDInner.displayName = 'MarkdownCMD';
}
const MarkdownCMD = forwardRef((props, ref) => {
const { children = '', answerType = 'answer', isInnerRender, ...reset } = props;
if (__DEV__) {
if (!['thinking', 'answer'].includes(answerType)) {
throw new Error('The answerType of MarkdownCMD component must be thinking or answer');
}
if (typeof children !== 'string') {
throw new Error('The children of MarkdownCMD component must be a string');
}
}
const contextValue = useMemo(() => ({ ...reset, answerType }), [reset, answerType]);
// 分离主题相关的 props
const themeProps = useMemo(() => ({
theme: props.theme || DEFAULT_THEME,
math: props.math,
codeBlock: props.codeBlock,
plugins: props.plugins || DEFAULT_PLUGINS,
answerType: props.answerType || DEFAULT_ANSWER_TYPE,
}), [props.theme, props.math, props.codeBlock, props.plugins, props.answerType]);
if (isInnerRender) {
// 内部渲染,外层已经 context 传递了 props,这里不再重复传递
return _jsx(MarkdownCMDInner, { ...props, ref: ref });
}
return (_jsx(MarkdownProvider, { value: contextValue, children: _jsx(MarkdownThemeProvider, { value: themeProps, children: _jsx(MarkdownCMDInner, { ...props, ref: ref }) }) }));
});
export default MarkdownCMD;
//# sourceMappingURL=index.js.map