You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tailchat/client/web/src/components/Markdown/render.tsx

87 lines
2.6 KiB
TypeScript

import { makeAbsoluteUrl } from '@/utils/url-helper';
import React, { useCallback, useMemo } from 'react';
import { isValidStr, parseUrlStr, useTranslation } from 'tailchat-shared';
import { Loadable } from '../Loadable';
import { Image } from 'tailchat-design';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import './render.less';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const ReactMarkdown = Loadable(() => import('react-markdown'));
export const Markdown: React.FC<{
raw: string;
baseUrl?: string;
}> = React.memo(({ raw, baseUrl }) => {
const { t } = useTranslation();
const transformUrl = useCallback(
(url: string) => {
url = parseUrlStr(url);
if (!isValidStr(baseUrl)) {
return url;
}
return new URL(url, makeAbsoluteUrl(baseUrl)).href;
},
[baseUrl]
);
/**
* Markdown自定义渲染组件
*/
const components = useMemo<
React.ComponentProps<typeof ReactMarkdown>['components']
>(
() => ({
img: (props) => (
<Image
style={props.style}
width={props.width}
height={props.height}
src={props.src}
preview={true}
/>
),
svg: () => <div>not support svg</div>,
iframe: (props) => {
let src = props.src;
if (!src) {
return <div />;
}
if (!src.startsWith('http')) {
return <div>only support http source</div>;
}
if (src && src.includes('?')) {
src += '&autoplay=0'; // make sure media autoplay is false
}
return <iframe {...props} src={src} />;
},
style: () => <div>{t('不支持自定义样式')}</div>,
meta: () => <div>{t('不支持自定义Meta')}</div>,
}),
[]
);
// [md]<iframe src="//player.bilibili.com/player.html?isOutside=true&aid=113350126076732&bvid=BV1ZpyHYkEQ3&cid=26409569922&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>[/md]
// <iframe src="//player.bilibili.com/player.html?isOutside=true&aid=113350126076732&bvid=BV1ZpyHYkEQ3&cid=26409569922&p=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>
return (
<ReactMarkdown
className="tailchat-markdown"
transformImageUri={(src) => transformUrl(src)}
transformLinkUri={(href) => transformUrl(href)}
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
linkTarget="_blank"
skipHtml={true}
components={components}
>
{raw}
</ReactMarkdown>
);
});
Markdown.displayName = 'Markdown';