diff --git a/web/.ministarrc.js b/web/.ministarrc.js index ee2f50c8..927cffaa 100644 --- a/web/.ministarrc.js +++ b/web/.ministarrc.js @@ -18,6 +18,14 @@ module.exports = { ), dest: path.resolve(__dirname, `./dist/plugins/${pluginName}/assets/`), }, + { + src: path.resolve( + __dirname, + `./plugins/${pluginName}`, + './docs/**/*' + ), + dest: path.resolve(__dirname, `./dist/plugins/${pluginName}/docs/`), + }, { src: path.resolve( __dirname, diff --git a/web/plugins/com.msgbyte.miaolang/README.md b/web/plugins/com.msgbyte.miaolang/README.md new file mode 100644 index 00000000..83ce9788 --- /dev/null +++ b/web/plugins/com.msgbyte.miaolang/README.md @@ -0,0 +1,11 @@ +## 喵语翻译 + +允许用户将自然语言转换为加密后的喵语 + +在任意聊天款的右侧添加喵语翻译 + +![](./docs/send.png) + +在此输入的任意内容都会被转换为加密后的喵语言,只有安装了相同插件的用户才能翻译 + +![](./docs/output.png) diff --git a/web/plugins/com.msgbyte.miaolang/docs/output.png b/web/plugins/com.msgbyte.miaolang/docs/output.png new file mode 100644 index 00000000..beb16e14 Binary files /dev/null and b/web/plugins/com.msgbyte.miaolang/docs/output.png differ diff --git a/web/plugins/com.msgbyte.miaolang/docs/send.png b/web/plugins/com.msgbyte.miaolang/docs/send.png new file mode 100644 index 00000000..a1eec82a Binary files /dev/null and b/web/plugins/com.msgbyte.miaolang/docs/send.png differ diff --git a/web/plugins/com.msgbyte.miaolang/manifest.json b/web/plugins/com.msgbyte.miaolang/manifest.json index 9c9d9b2c..9d2e1f48 100644 --- a/web/plugins/com.msgbyte.miaolang/manifest.json +++ b/web/plugins/com.msgbyte.miaolang/manifest.json @@ -5,5 +5,6 @@ "version": "0.0.0", "author": "msgbyte", "description": "为聊天提供喵语言对话功能", + "documentUrl": "/plugins/com.msgbyte.miaolang/README.md", "requireRestart": true } diff --git a/web/registry.json b/web/registry.json index ec91653b..92a13bd2 100644 --- a/web/registry.json +++ b/web/registry.json @@ -6,6 +6,7 @@ "version": "0.0.0", "author": "msgbyte", "description": "为聊天提供喵语言对话功能", + "documentUrl": "/plugins/com.msgbyte.miaolang/README.md", "requireRestart": true }, { diff --git a/web/src/components/ErrorBoundary.tsx b/web/src/components/ErrorBoundary.tsx index 8a4ad8a0..8e2bb424 100644 --- a/web/src/components/ErrorBoundary.tsx +++ b/web/src/components/ErrorBoundary.tsx @@ -1,3 +1,56 @@ -import { Alert } from 'antd'; +import React from 'react'; +import { t } from 'tailchat-shared'; +import { Problem } from './Problem'; -export const ErrorBoundary = Alert.ErrorBoundary; +interface ErrorBoundaryProps { + message?: React.ReactNode; + description?: string; +} + +export class ErrorBoundary extends React.Component< + ErrorBoundaryProps, + { + error?: Error | null; + info: { + componentStack?: string; + }; + } +> { + state = { + error: undefined, + info: { + componentStack: '', + }, + }; + + componentDidCatch(error: Error | null, info: any) { + this.setState({ error, info }); + } + + render() { + const { message, description, children } = this.props; + const { error, info } = this.state; + const componentStack = + info && info.componentStack ? info.componentStack : null; + const errorMessage = + typeof message === 'undefined' ? (error || '').toString() : message; + const errorDescription = + typeof description === 'undefined' ? componentStack : description; + if (error) { + return ( +
+ +

{t('页面出现了一些问题')}

+

{errorMessage}

+ + } + /> +
+ ); + } + + return children; + } +} diff --git a/web/src/components/Markdown.less b/web/src/components/Markdown.less index 19849981..a93469bf 100644 --- a/web/src/components/Markdown.less +++ b/web/src/components/Markdown.less @@ -15,4 +15,8 @@ code { @apply px-2 py-1 bg-black bg-opacity-20 rounded; } + + p { + @apply my-2; + } } diff --git a/web/src/components/Markdown.tsx b/web/src/components/Markdown.tsx index 192d63f3..d0a6b8f6 100644 --- a/web/src/components/Markdown.tsx +++ b/web/src/components/Markdown.tsx @@ -1,3 +1,4 @@ +import { markAbsoluteUrl } from '@/utils/url-helper'; import React, { useCallback } from 'react'; import { isValidStr } from 'tailchat-shared'; import { Loadable } from './Loadable'; @@ -17,7 +18,7 @@ export const Markdown: React.FC<{ return url; } - return new URL(url, baseUrl).href; + return new URL(url, markAbsoluteUrl(baseUrl)).href; }, [baseUrl] ); diff --git a/web/src/components/Modal.tsx b/web/src/components/Modal.tsx index fadc3ae2..198fe736 100644 --- a/web/src/components/Modal.tsx +++ b/web/src/components/Modal.tsx @@ -11,9 +11,10 @@ import { Icon } from '@/components/Icon'; import { CSSTransition } from 'react-transition-group'; import clsx from 'clsx'; import { useIsMobile } from '@/hooks/useIsMobile'; +import { stopPropagation } from '@/utils/dom-helper'; +import { ErrorBoundary } from './ErrorBoundary'; import './Modal.less'; -import { stopPropagation } from '@/utils/dom-helper'; const transitionEndListener = (node: HTMLElement, done: () => void) => node.addEventListener('transitionend', done, false); @@ -101,7 +102,8 @@ export const Modal: React.FC = React.memo((props) => { onClick={closeModal} /> )} - {props.children} + + {props.children} diff --git a/web/src/plugin/PluginStore/DocumentView/DocumentMarkdownRender.tsx b/web/src/plugin/PluginStore/DocumentView/DocumentMarkdownRender.tsx index d264fedc..0a36b661 100644 --- a/web/src/plugin/PluginStore/DocumentView/DocumentMarkdownRender.tsx +++ b/web/src/plugin/PluginStore/DocumentView/DocumentMarkdownRender.tsx @@ -24,7 +24,7 @@ export const DocumentMarkdownRender: React.FC<{ url: string }> = React.memo( return ; } - return ; + return ; } ); DocumentMarkdownRender.displayName = 'DocumentMarkdownRender'; diff --git a/web/src/utils/__tests__/url-helper.spec.ts b/web/src/utils/__tests__/url-helper.spec.ts new file mode 100644 index 00000000..a3765703 --- /dev/null +++ b/web/src/utils/__tests__/url-helper.spec.ts @@ -0,0 +1,14 @@ +import { markAbsoluteUrl } from '../url-helper'; + +describe('markAbsoluteUrl', () => { + test.each([ + ['bar', 'https://www.example.com/foo/bar'], + ['./bar', 'https://www.example.com/foo/bar'], + ['../bar', 'https://www.example.com/bar'], + ['/bar', 'https://www.example.com/bar'], + ['https://www.baidu.com', 'https://www.baidu.com/'], + ['https://www.baidu.com/search', 'https://www.baidu.com/search'], + ])('%s', (input, output) => { + expect(markAbsoluteUrl(input)).toBe(output); + }); +}); diff --git a/web/src/utils/url-helper.ts b/web/src/utils/url-helper.ts new file mode 100644 index 00000000..9de60d10 --- /dev/null +++ b/web/src/utils/url-helper.ts @@ -0,0 +1,8 @@ +/** + * 根据输入url返回绝对url + * @param relativeUrl 相对或绝对url + * @returns 绝对url + */ +export function markAbsoluteUrl(relativeUrl: string): string { + return new URL(relativeUrl, location.href).href; +} diff --git a/web/test/setup.js b/web/test/setup.js index 81851bfd..e2bde2e1 100644 --- a/web/test/setup.js +++ b/web/test/setup.js @@ -19,3 +19,7 @@ console.error = (...args) => { originalError.call(console, ...args); }; + +// Mock location +delete window.location; +window.location = new URL('https://www.example.com/foo/index');