diff --git a/web/static/js/chat.js b/web/static/js/chat.js
index 3ce9b9df..0b9819e3 100644
--- a/web/static/js/chat.js
+++ b/web/static/js/chat.js
@@ -139,51 +139,43 @@ function addMessage(role, content, mcpExecutionIds = null, progressId = null) {
// 解析 Markdown 或 HTML 格式
let formattedContent;
+ const defaultSanitizeConfig = {
+ ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 's', 'code', 'pre', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'a', 'img', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'hr'],
+ ALLOWED_ATTR: ['href', 'title', 'alt', 'src', 'class'],
+ ALLOW_DATA_ATTR: false,
+ };
- // 先使用 DOMPurify 清理(如果可用),这样可以处理已经是 HTML 的内容
- if (typeof DOMPurify !== 'undefined') {
- // 配置 DOMPurify 允许的标签和属性
- const sanitizeConfig = {
- // 允许基本的 Markdown 格式化标签
- ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 's', 'code', 'pre', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'a', 'img', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'hr'],
- ALLOWED_ATTR: ['href', 'title', 'alt', 'src', 'class'],
- ALLOW_DATA_ATTR: false,
- };
-
- // 如果内容看起来已经是 HTML(包含 HTML 标签),直接清理
- // 否则先用 marked.js 解析 Markdown,再清理
- if (typeof marked !== 'undefined' && !/<[a-z][\s\S]*>/i.test(content)) {
- // 内容不包含 HTML 标签,可能是 Markdown,使用 marked.js 解析
- try {
- marked.setOptions({
- breaks: true,
- gfm: true,
- });
- let parsedContent = marked.parse(content);
- formattedContent = DOMPurify.sanitize(parsedContent, sanitizeConfig);
- } catch (e) {
- console.error('Markdown 解析失败:', e);
- // 降级处理:直接清理原始内容
- formattedContent = DOMPurify.sanitize(content, sanitizeConfig);
- }
- } else {
- // 内容包含 HTML 标签或 marked.js 不可用,直接清理
- formattedContent = DOMPurify.sanitize(content, sanitizeConfig);
+ const parseMarkdown = (raw) => {
+ if (typeof marked === 'undefined') {
+ return null;
}
- } else if (typeof marked !== 'undefined') {
- // 没有 DOMPurify,但有 marked.js
try {
marked.setOptions({
breaks: true,
gfm: true,
});
- formattedContent = marked.parse(content);
+ return marked.parse(raw);
} catch (e) {
console.error('Markdown 解析失败:', e);
+ return null;
+ }
+ };
+
+ if (typeof DOMPurify !== 'undefined') {
+ let parsedContent = parseMarkdown(content);
+ if (!parsedContent) {
+ // 如果 Markdown 解析失败或 marked 不可用,则退回原始内容
+ parsedContent = content;
+ }
+ formattedContent = DOMPurify.sanitize(parsedContent, defaultSanitizeConfig);
+ } else if (typeof marked !== 'undefined') {
+ const parsedContent = parseMarkdown(content);
+ if (parsedContent) {
+ formattedContent = parsedContent;
+ } else {
formattedContent = escapeHtml(content).replace(/\n/g, '
');
}
} else {
- // 都没有,简单转义
formattedContent = escapeHtml(content).replace(/\n/g, '
');
}