diff --git a/controller/misc.go b/controller/misc.go
index 38e98fe9c..c6c54b6d1 100644
--- a/controller/misc.go
+++ b/controller/misc.go
@@ -108,6 +108,8 @@ func GetStatus(c *gin.Context) {
"passkey_user_verification": passkeySetting.UserVerification,
"passkey_attachment": passkeySetting.AttachmentPreference,
"setup": constant.Setup,
+ "user_agreement_enabled": common.OptionMap["UserAgreement"] != "",
+ "privacy_policy_enabled": common.OptionMap["PrivacyPolicy"] != "",
}
// 根据启用状态注入可选内容
diff --git a/web/src/components/auth/RegisterForm.jsx b/web/src/components/auth/RegisterForm.jsx
index 6c76b20d3..5bf9b670a 100644
--- a/web/src/components/auth/RegisterForm.jsx
+++ b/web/src/components/auth/RegisterForm.jsx
@@ -110,27 +110,9 @@ const RegisterForm = () => {
setTurnstileSiteKey(status.turnstile_site_key);
}
- // 检查用户协议和隐私政策是否已设置
- const checkTermsAvailability = async () => {
- try {
- const [userAgreementRes, privacyPolicyRes] = await Promise.all([
- API.get('/api/user-agreement'),
- API.get('/api/privacy-policy')
- ]);
-
- if (userAgreementRes.data.success && userAgreementRes.data.data) {
- setHasUserAgreement(true);
- }
-
- if (privacyPolicyRes.data.success && privacyPolicyRes.data.data) {
- setHasPrivacyPolicy(true);
- }
- } catch (error) {
- console.error('检查用户协议和隐私政策失败:', error);
- }
- };
-
- checkTermsAvailability();
+ // 从 status 获取用户协议和隐私政策的启用状态
+ setHasUserAgreement(status.user_agreement_enabled || false);
+ setHasPrivacyPolicy(status.privacy_policy_enabled || false);
}, [status]);
useEffect(() => {
diff --git a/web/src/components/common/DocumentRenderer/index.jsx b/web/src/components/common/DocumentRenderer/index.jsx
new file mode 100644
index 000000000..383afc11d
--- /dev/null
+++ b/web/src/components/common/DocumentRenderer/index.jsx
@@ -0,0 +1,243 @@
+/*
+Copyright (C) 2025 QuantumNous
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+
+For commercial licensing, please contact support@quantumnous.com
+*/
+
+import React, { useEffect, useState } from 'react';
+import { API, showError } from '../../../helpers';
+import { Empty, Card, Spin, Typography } from '@douyinfe/semi-ui';
+const { Title } = Typography;
+import {
+ IllustrationConstruction,
+ IllustrationConstructionDark,
+} from '@douyinfe/semi-illustrations';
+import { useTranslation } from 'react-i18next';
+import MarkdownRenderer from '../markdown/MarkdownRenderer';
+
+// 检查是否为 URL
+const isUrl = (content) => {
+ try {
+ new URL(content.trim());
+ return true;
+ } catch {
+ return false;
+ }
+};
+
+// 检查是否为 HTML 内容
+const isHtmlContent = (content) => {
+ if (!content || typeof content !== 'string') return false;
+
+ // 检查是否包含HTML标签
+ const htmlTagRegex = /<\/?[a-z][\s\S]*>/i;
+ return htmlTagRegex.test(content);
+};
+
+// 安全地渲染HTML内容
+const sanitizeHtml = (html) => {
+ // 创建一个临时元素来解析HTML
+ const tempDiv = document.createElement('div');
+ tempDiv.innerHTML = html;
+
+ // 提取样式
+ const styles = Array.from(tempDiv.querySelectorAll('style'))
+ .map(style => style.innerHTML)
+ .join('\n');
+
+ // 提取body内容,如果没有body标签则使用全部内容
+ const bodyContent = tempDiv.querySelector('body');
+ const content = bodyContent ? bodyContent.innerHTML : html;
+
+ return { content, styles };
+};
+
+/**
+ * 通用文档渲染组件
+ * @param {string} apiEndpoint - API 接口地址
+ * @param {string} title - 文档标题
+ * @param {string} cacheKey - 本地存储缓存键
+ * @param {string} emptyMessage - 空内容时的提示消息
+ */
+const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => {
+ const { t } = useTranslation();
+ const [content, setContent] = useState('');
+ const [loading, setLoading] = useState(true);
+ const [htmlStyles, setHtmlStyles] = useState('');
+ const [processedHtmlContent, setProcessedHtmlContent] = useState('');
+
+ const loadContent = async () => {
+ // 先从缓存中获取
+ const cachedContent = localStorage.getItem(cacheKey) || '';
+ if (cachedContent) {
+ setContent(cachedContent);
+ processContent(cachedContent);
+ setLoading(false);
+ }
+
+ try {
+ const res = await API.get(apiEndpoint);
+ const { success, message, data } = res.data;
+ if (success && data) {
+ setContent(data);
+ processContent(data);
+ localStorage.setItem(cacheKey, data);
+ } else {
+ if (!cachedContent) {
+ showError(message || emptyMessage);
+ setContent('');
+ }
+ }
+ } catch (error) {
+ if (!cachedContent) {
+ showError(emptyMessage);
+ setContent('');
+ }
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const processContent = (rawContent) => {
+ if (isHtmlContent(rawContent)) {
+ const { content: htmlContent, styles } = sanitizeHtml(rawContent);
+ setProcessedHtmlContent(htmlContent);
+ setHtmlStyles(styles);
+ } else {
+ setProcessedHtmlContent('');
+ setHtmlStyles('');
+ }
+ };
+
+ useEffect(() => {
+ loadContent();
+ }, []);
+
+ // 处理HTML样式注入
+ useEffect(() => {
+ const styleId = `document-renderer-styles-${cacheKey}`;
+
+ if (htmlStyles) {
+ let styleEl = document.getElementById(styleId);
+ if (!styleEl) {
+ styleEl = document.createElement('style');
+ styleEl.id = styleId;
+ styleEl.type = 'text/css';
+ document.head.appendChild(styleEl);
+ }
+ styleEl.innerHTML = htmlStyles;
+ } else {
+ const el = document.getElementById(styleId);
+ if (el) el.remove();
+ }
+
+ return () => {
+ const el = document.getElementById(styleId);
+ if (el) el.remove();
+ };
+ }, [htmlStyles, cacheKey]);
+
+ // 显示加载状态
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ // 如果没有内容,显示空状态
+ if (!content || content.trim() === '') {
+ return (
+
+ }
+ darkModeImage={}
+ className='p-8'
+ />
+
+ );
+ }
+
+ // 如果是 URL,显示链接卡片
+ if (isUrl(content)) {
+ return (
+
+ );
+ }
+
+ // 如果是 HTML 内容,直接渲染
+ if (isHtmlContent(content)) {
+ const { content: htmlContent, styles } = sanitizeHtml(content);
+
+ // 设置样式(如果有的话)
+ useEffect(() => {
+ if (styles && styles !== htmlStyles) {
+ setHtmlStyles(styles);
+ }
+ }, [content, styles, htmlStyles]);
+
+ return (
+
+ );
+ }
+
+ // 其他内容统一使用 Markdown 渲染器
+ return (
+
+ );
+};
+
+export default DocumentRenderer;
\ No newline at end of file
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index 7b0f47932..48d5a3ff2 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -2276,5 +2276,8 @@
"和": " and ",
"请先阅读并同意用户协议和隐私政策": "Please read and agree to the user agreement and privacy policy first",
"填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "After filling in the user agreement content, users will be required to check that they have read the user agreement when registering",
- "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "After filling in the privacy policy content, users will be required to check that they have read the privacy policy when registering"
+ "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "After filling in the privacy policy content, users will be required to check that they have read the privacy policy when registering",
+ "管理员设置了外部链接,点击下方按钮访问": "Administrator has set an external link, click the button below to access",
+ "访问用户协议": "Access User Agreement",
+ "访问隐私政策": "Access Privacy Policy"
}
diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json
index ee2a3c297..ac70a2bde 100644
--- a/web/src/i18n/locales/fr.json
+++ b/web/src/i18n/locales/fr.json
@@ -2270,5 +2270,8 @@
"和": " et ",
"请先阅读并同意用户协议和隐私政策": "Veuillez d'abord lire et accepter l'accord utilisateur et la politique de confidentialité",
"填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "Après avoir rempli le contenu de l'accord utilisateur, les utilisateurs devront cocher qu'ils ont lu l'accord utilisateur lors de l'inscription",
- "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "Après avoir rempli le contenu de la politique de confidentialité, les utilisateurs devront cocher qu'ils ont lu la politique de confidentialité lors de l'inscription"
+ "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "Après avoir rempli le contenu de la politique de confidentialité, les utilisateurs devront cocher qu'ils ont lu la politique de confidentialité lors de l'inscription",
+ "管理员设置了外部链接,点击下方按钮访问": "L'administrateur a défini un lien externe, cliquez sur le bouton ci-dessous pour accéder",
+ "访问用户协议": "Accéder à l'accord utilisateur",
+ "访问隐私政策": "Accéder à la politique de confidentialité"
}
diff --git a/web/src/i18n/locales/zh.json b/web/src/i18n/locales/zh.json
index 661cbea92..8db3e215f 100644
--- a/web/src/i18n/locales/zh.json
+++ b/web/src/i18n/locales/zh.json
@@ -130,5 +130,8 @@
"和": "和",
"请先阅读并同意用户协议和隐私政策": "请先阅读并同意用户协议和隐私政策",
"填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议",
- "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策"
+ "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策",
+ "管理员设置了外部链接,点击下方按钮访问": "管理员设置了外部链接,点击下方按钮访问",
+ "访问用户协议": "访问用户协议",
+ "访问隐私政策": "访问隐私政策"
}
diff --git a/web/src/pages/PrivacyPolicy/index.jsx b/web/src/pages/PrivacyPolicy/index.jsx
index 373b8a282..026290b18 100644
--- a/web/src/pages/PrivacyPolicy/index.jsx
+++ b/web/src/pages/PrivacyPolicy/index.jsx
@@ -17,233 +17,21 @@ along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
-import React, { useEffect, useState } from 'react';
-import { API, showError } from '../../helpers';
-import { Empty } from '@douyinfe/semi-ui';
-import {
- IllustrationConstruction,
- IllustrationConstructionDark,
-} from '@douyinfe/semi-illustrations';
+import React from 'react';
import { useTranslation } from 'react-i18next';
-import MarkdownRenderer from '../../components/common/markdown/MarkdownRenderer';
-import { getContentType } from '../../utils/contentDetector';
+import DocumentRenderer from '../../components/common/DocumentRenderer';
const PrivacyPolicy = () => {
const { t } = useTranslation();
- const [privacyPolicy, setPrivacyPolicy] = useState('');
- const [privacyPolicyLoaded, setPrivacyPolicyLoaded] = useState(false);
- const [contentType, setContentType] = useState('empty');
- const [htmlBody, setHtmlBody] = useState('');
- const [htmlStyles, setHtmlStyles] = useState('');
- const [htmlLinks, setHtmlLinks] = useState([]);
- // Height of the top navigation/header in pixels. Adjust if your header is a different height.
- const HEADER_HEIGHT = 64;
- const displayPrivacyPolicy = async () => {
- // 先从缓存中获取
- const cachedContent = localStorage.getItem('privacy_policy') || '';
- if (cachedContent) {
- setPrivacyPolicy(cachedContent);
- const ct = getContentType(cachedContent);
- setContentType(ct);
- if (ct === 'html') {
- // parse cached HTML to extract body and inline styles
- try {
- const parser = new DOMParser();
- const doc = parser.parseFromString(cachedContent, 'text/html');
- setHtmlBody(doc.body ? doc.body.innerHTML : cachedContent);
- const styles = Array.from(doc.querySelectorAll('style'))
- .map((s) => s.innerHTML)
- .join('\n');
- setHtmlStyles(styles);
- const links = Array.from(doc.querySelectorAll('link[rel="stylesheet"]'))
- .map((l) => l.getAttribute('href') || l.href)
- .filter(Boolean);
- setHtmlLinks(links);
- } catch (e) {
- setHtmlBody(cachedContent);
- setHtmlStyles('');
- setHtmlLinks([]);
- }
- }
- }
-
- try {
- const res = await API.get('/api/privacy-policy');
- const { success, message, data } = res.data;
- if (success && data) {
- // 直接使用原始数据,不进行任何预处理
- setPrivacyPolicy(data);
- const ct = getContentType(data);
- setContentType(ct);
- // 如果是完整 HTML 文档,解析 body 内容并提取内联样式放到 head
- if (ct === 'html') {
- try {
- const parser = new DOMParser();
- const doc = parser.parseFromString(data, 'text/html');
- setHtmlBody(doc.body ? doc.body.innerHTML : data);
- const styles = Array.from(doc.querySelectorAll('style'))
- .map((s) => s.innerHTML)
- .join('\n');
- setHtmlStyles(styles);
- const links = Array.from(doc.querySelectorAll('link[rel="stylesheet"]'))
- .map((l) => l.getAttribute('href') || l.href)
- .filter(Boolean);
- setHtmlLinks(links);
- } catch (e) {
- setHtmlBody(data);
- setHtmlStyles('');
- setHtmlLinks([]);
- }
- } else {
- setHtmlBody('');
- setHtmlStyles('');
- setHtmlLinks([]);
- }
- localStorage.setItem('privacy_policy', data);
- } else {
- if (!cachedContent) {
- showError(message || t('加载隐私政策内容失败...'));
- setPrivacyPolicy('');
- setContentType('empty');
- }
- }
- } catch (error) {
- if (!cachedContent) {
- showError(t('加载隐私政策内容失败...'));
- setPrivacyPolicy('');
- setContentType('empty');
- }
- }
- setPrivacyPolicyLoaded(true);
- };
-
- useEffect(() => {
- displayPrivacyPolicy();
- }, []);
-
- // inject inline styles for parsed HTML content and cleanup on unmount or styles change
- useEffect(() => {
- const styleId = 'privacy-policy-inline-styles';
- const createdLinkIds = [];
-
- if (htmlStyles) {
- let styleEl = document.getElementById(styleId);
- if (!styleEl) {
- styleEl = document.createElement('style');
- styleEl.id = styleId;
- styleEl.type = 'text/css';
- document.head.appendChild(styleEl);
- }
- styleEl.innerHTML = htmlStyles;
- } else {
- const el = document.getElementById(styleId);
- if (el) el.remove();
- }
-
- if (htmlLinks && htmlLinks.length) {
- htmlLinks.forEach((href, idx) => {
- try {
- const existing = document.querySelector(`link[rel="stylesheet"][href="${href}"]`);
- if (existing) return;
- const linkId = `${styleId}-link-${idx}`;
- const linkEl = document.createElement('link');
- linkEl.id = linkId;
- linkEl.rel = 'stylesheet';
- linkEl.href = href;
- document.head.appendChild(linkEl);
- createdLinkIds.push(linkId);
- } catch (e) {
- // ignore
- }
- });
- }
-
- return () => {
- const el = document.getElementById(styleId);
- if (el) el.remove();
- createdLinkIds.forEach((id) => {
- const l = document.getElementById(id);
- if (l) l.remove();
- });
- };
- }, [htmlStyles]);
-
- const renderContent = () => {
- if (!privacyPolicyLoaded) {
- return (
-
-
-
- );
- }
-
- if (contentType === 'empty' || !privacyPolicy) {
- return (
-
-
- }
- darkModeImage={
-
- }
- description={t('管理员未设置隐私政策内容')}
- />
-
- );
- }
-
- if (contentType === 'url') {
- return (
-
- );
- }
-
- if (contentType === 'html') {
- return (
-
- );
- }
-
- // markdown 或 text 内容
- return (
-
-
-
- );
- };
-
- return <>{renderContent()}>;
+ return (
+
+ );
};
export default PrivacyPolicy;
\ No newline at end of file
diff --git a/web/src/pages/UserAgreement/index.jsx b/web/src/pages/UserAgreement/index.jsx
index a703895c2..965a0dd54 100644
--- a/web/src/pages/UserAgreement/index.jsx
+++ b/web/src/pages/UserAgreement/index.jsx
@@ -17,236 +17,21 @@ along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
-import React, { useEffect, useState } from 'react';
-import { API, showError } from '../../helpers';
-import { Empty } from '@douyinfe/semi-ui';
-import {
- IllustrationConstruction,
- IllustrationConstructionDark,
-} from '@douyinfe/semi-illustrations';
+import React from 'react';
import { useTranslation } from 'react-i18next';
-import MarkdownRenderer from '../../components/common/markdown/MarkdownRenderer';
-import { getContentType } from '../../utils/contentDetector';
+import DocumentRenderer from '../../components/common/DocumentRenderer';
const UserAgreement = () => {
const { t } = useTranslation();
- const [userAgreement, setUserAgreement] = useState('');
- const [userAgreementLoaded, setUserAgreementLoaded] = useState(false);
- const [contentType, setContentType] = useState('empty');
- const [htmlBody, setHtmlBody] = useState('');
- const [htmlStyles, setHtmlStyles] = useState('');
- const [htmlLinks, setHtmlLinks] = useState([]);
- // Height of the top navigation/header in pixels. Adjust if your header is a different height.
- const HEADER_HEIGHT = 64;
- const displayUserAgreement = async () => {
- // 先从缓存中获取
- const cachedContent = localStorage.getItem('user_agreement') || '';
- if (cachedContent) {
- setUserAgreement(cachedContent);
- const ct = getContentType(cachedContent);
- setContentType(ct);
- if (ct === 'html') {
- try {
- const parser = new DOMParser();
- const doc = parser.parseFromString(cachedContent, 'text/html');
- setHtmlBody(doc.body ? doc.body.innerHTML : cachedContent);
- const styles = Array.from(doc.querySelectorAll('style'))
- .map((s) => s.innerHTML)
- .join('\n');
- setHtmlStyles(styles);
- const links = Array.from(doc.querySelectorAll('link[rel="stylesheet"]'))
- .map((l) => l.getAttribute('href') || l.href)
- .filter(Boolean);
- setHtmlLinks(links);
- } catch (e) {
- setHtmlBody(cachedContent);
- setHtmlStyles('');
- setHtmlLinks([]);
- }
- }
- }
-
- try {
- const res = await API.get('/api/user-agreement');
- const { success, message, data } = res.data;
- if (success && data) {
- // 直接使用原始数据,不进行任何预处理
- setUserAgreement(data);
- const ct = getContentType(data);
- setContentType(ct);
- if (ct === 'html') {
- try {
- const parser = new DOMParser();
- const doc = parser.parseFromString(data, 'text/html');
- setHtmlBody(doc.body ? doc.body.innerHTML : data);
- const styles = Array.from(doc.querySelectorAll('style'))
- .map((s) => s.innerHTML)
- .join('\n');
- setHtmlStyles(styles);
- const links = Array.from(doc.querySelectorAll('link[rel="stylesheet"]'))
- .map((l) => l.getAttribute('href') || l.href)
- .filter(Boolean);
- setHtmlLinks(links);
- } catch (e) {
- setHtmlBody(data);
- setHtmlStyles('');
- setHtmlLinks([]);
- }
- } else {
- setHtmlBody('');
- setHtmlStyles('');
- setHtmlLinks([]);
- }
- localStorage.setItem('user_agreement', data);
- } else {
- if (!cachedContent) {
- showError(message || t('加载用户协议内容失败...'));
- setUserAgreement('');
- setContentType('empty');
- }
- }
- } catch (error) {
- if (!cachedContent) {
- showError(t('加载用户协议内容失败...'));
- setUserAgreement('');
- setContentType('empty');
- }
- }
- setUserAgreementLoaded(true);
- };
-
- useEffect(() => {
- displayUserAgreement();
- }, []);
-
- // inject inline styles for parsed HTML content and cleanup on unmount or styles change
- useEffect(() => {
- // if there's nothing to inject, remove any existing injected elements
- const styleId = 'user-agreement-inline-styles';
- const createdLinkIds = [];
-
- // handle style tags
- if (htmlStyles) {
- let styleEl = document.getElementById(styleId);
- if (!styleEl) {
- styleEl = document.createElement('style');
- styleEl.id = styleId;
- styleEl.type = 'text/css';
- document.head.appendChild(styleEl);
- }
- styleEl.innerHTML = htmlStyles;
- } else {
- const el = document.getElementById(styleId);
- if (el) el.remove();
- }
-
- // handle external stylesheet links
- if (htmlLinks && htmlLinks.length) {
- htmlLinks.forEach((href, idx) => {
- try {
- // avoid duplicate injection if a link with same href already exists
- const existing = document.querySelector(`link[rel="stylesheet"][href="${href}"]`);
- if (existing) return;
- const linkId = `${styleId}-link-${idx}`;
- const linkEl = document.createElement('link');
- linkEl.id = linkId;
- linkEl.rel = 'stylesheet';
- linkEl.href = href;
- document.head.appendChild(linkEl);
- createdLinkIds.push(linkId);
- } catch (e) {
- // ignore malformed hrefs
- }
- });
- }
-
- return () => {
- const el = document.getElementById(styleId);
- if (el) el.remove();
- // remove only the links we created
- createdLinkIds.forEach((id) => {
- const l = document.getElementById(id);
- if (l) l.remove();
- });
- };
- }, [htmlStyles]);
-
- const renderContent = () => {
- if (!userAgreementLoaded) {
- return (
-
-
-
- );
- }
-
- if (contentType === 'empty' || !userAgreement) {
- return (
-
-
- }
- darkModeImage={
-
- }
- description={t('管理员未设置用户协议内容')}
- />
-
- );
- }
-
- if (contentType === 'url') {
- return (
-
- );
- }
-
- if (contentType === 'html') {
- return (
-
- );
- }
-
- // markdown 或 text 内容
- return (
-
-
-
- );
- };
-
- return <>{renderContent()}>;
+ return (
+
+ );
};
export default UserAgreement;
\ No newline at end of file
diff --git a/web/src/utils/contentDetector.js b/web/src/utils/contentDetector.js
deleted file mode 100644
index 5438a83f8..000000000
--- a/web/src/utils/contentDetector.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * 检测内容类型并返回相应的渲染信息
- */
-
-// 检查是否为 URL
-export const isUrl = (content) => {
- try {
- new URL(content);
- return true;
- } catch {
- return false;
- }
-};
-
-// 检查是否为 HTML 内容
-export const isHtmlContent = (content) => {
- if (!content || typeof content !== 'string') return false;
-
- // 检查是否包含HTML标签
- const htmlTagRegex = /<\/?[a-z][\s\S]*>/i;
- return htmlTagRegex.test(content);
-};
-
-// 检查是否为 Markdown 内容
-export const isMarkdownContent = (content) => {
- if (!content || typeof content !== 'string') return false;
-
- // 如果已经是HTML,则不是原始Markdown
- if (isHtmlContent(content)) return false;
-
- // 检查Markdown特征
- const markdownFeatures = [
- /^#{1,6}\s+.+$/m, // 标题
- /^\*\s+.+$/m, // 无序列表
- /^\d+\.\s+.+$/m, // 有序列表
- /\*\*.+\*\*/, // 粗体
- /\*.+\*/, // 斜体
- /\[.+\]\(.+\)/, // 链接
- /^>.+$/m, // 引用
- /^```[\s\S]*?```$/m, // 代码块
- /`[^`]+`/, // 行内代码
- /^\|.+\|$/m, // 表格
- /^---+$/m, // 分割线
- ];
-
- return markdownFeatures.some(regex => regex.test(content));
-};
-
-// 获取内容类型
-export const getContentType = (content) => {
- if (!content) return 'empty';
-
- const trimmedContent = content.trim();
-
- if (isUrl(trimmedContent)) return 'url';
- if (isHtmlContent(trimmedContent)) return 'html';
- if (isMarkdownContent(trimmedContent)) return 'markdown';
-
- // 默认当作纯文本处理
- return 'text';
-};
\ No newline at end of file