diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b35e25a8..5dba74d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,7 +104,7 @@ importers: specifiers: '@iconify/iconify': ^2.0.2 '@iconify/react': ^3.0.0-alpha.1 - '@loadable/component': ^5.15.0 + '@loadable/component': ^5.15.2 '@testing-library/jest-dom': ^5.14.1 '@testing-library/react': ^12.0.0 '@testing-library/react-hooks': ^7.0.1 @@ -2423,7 +2423,7 @@ packages: dev: true /@types/loadable__component/5.13.4: - resolution: {integrity: sha1-pGRrJAaxKD76wanZSFgkqQWzPUo=, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/@types/loadable__component/download/@types/loadable__component-5.13.4.tgz} + resolution: {integrity: sha512-YhoCCxyuvP2XeZNbHbi8Wb9EMaUJuA2VGHxJffcQYrJKIKSkymJrhbzsf9y4zpTmr5pExAAEh5hbF628PAZ8Dg==} dependencies: '@types/react': 17.0.38 dev: true @@ -4845,6 +4845,12 @@ packages: value-equal: registry.nlark.com/value-equal/1.0.1 dev: false + /hoist-non-react-statics/3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + dependencies: + react-is: 16.13.1 + dev: false + /hosted-git-info/2.8.9: resolution: {integrity: sha1-3/wL+aIcAiCQkPKqaUKeFBTa8/k=, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.9.tgz} dev: true diff --git a/shared/model/plugin.ts b/shared/model/plugin.ts index a88edb2a..da71c1e1 100644 --- a/shared/model/plugin.ts +++ b/shared/model/plugin.ts @@ -47,6 +47,12 @@ export interface PluginManifest { * 是否需要重启才能应用插件 */ requireRestart: boolean; + + /** + * 文档的链接 + * 如果是markdown则解析, 如果是html则使用iframe + */ + documentUrl?: string; } /** diff --git a/web/package.json b/web/package.json index 63d9954d..72715858 100644 --- a/web/package.json +++ b/web/package.json @@ -25,7 +25,7 @@ "dependencies": { "@iconify/iconify": "^2.0.2", "@iconify/react": "^3.0.0-alpha.1", - "@loadable/component": "^5.15.0", + "@loadable/component": "^5.15.2", "antd": "^4.18.2", "axios": "^0.21.1", "clsx": "^1.1.1", diff --git a/web/src/components/Markdown.tsx b/web/src/components/Markdown.tsx index c9329b3c..03ab6971 100644 --- a/web/src/components/Markdown.tsx +++ b/web/src/components/Markdown.tsx @@ -1,7 +1,10 @@ import React, { useCallback } from 'react'; import { isValidStr } from 'tailchat-shared'; +import { Loadable } from './Loadable'; -const ReactMarkdown = React.lazy(() => import('react-markdown')); +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +const ReactMarkdown = Loadable(() => import('react-markdown')); export const Markdown: React.FC<{ raw: string; @@ -20,6 +23,7 @@ export const Markdown: React.FC<{ return ( transformUrl(src)} transformLinkUri={(href) => transformUrl(href)} > diff --git a/web/src/plugin/PluginStore/DocumentView/DocumentMarkdownRender.tsx b/web/src/plugin/PluginStore/DocumentView/DocumentMarkdownRender.tsx new file mode 100644 index 00000000..d264fedc --- /dev/null +++ b/web/src/plugin/PluginStore/DocumentView/DocumentMarkdownRender.tsx @@ -0,0 +1,30 @@ +import { LoadingSpinner } from '@/components/LoadingSpinner'; +import { Markdown } from '@/components/Markdown'; +import { Problem } from '@/components/Problem'; +import React from 'react'; +import { useAsync } from 'tailchat-shared'; + +export const DocumentMarkdownRender: React.FC<{ url: string }> = React.memo( + ({ url }) => { + const { loading, value, error } = useAsync(async () => { + const data = await fetch(url); + if (data.status >= 400) { + throw new Error('请求异常'); + } + const raw = data.text(); + + return raw; + }, [url]); + + if (loading) { + return ; + } + + if (error) { + return ; + } + + return ; + } +); +DocumentMarkdownRender.displayName = 'DocumentMarkdownRender'; diff --git a/web/src/plugin/PluginStore/DocumentView/index.tsx b/web/src/plugin/PluginStore/DocumentView/index.tsx new file mode 100644 index 00000000..309c06a7 --- /dev/null +++ b/web/src/plugin/PluginStore/DocumentView/index.tsx @@ -0,0 +1,24 @@ +import { Problem } from '@/components/Problem'; +import React from 'react'; +import { isValidStr, t } from 'tailchat-shared'; +import { DocumentMarkdownRender } from './DocumentMarkdownRender'; + +interface DocumentViewProps { + documentUrl?: string; +} +export const DocumentView: React.FC = React.memo((props) => { + const { documentUrl } = props; + + if (!isValidStr(documentUrl)) { + return ; + } + + if (documentUrl.endsWith('.md')) { + return ; + } else if (documentUrl.endsWith('.html')) { + return