fix(auto): markdown rendering for LLM answer and thinking blocks

This commit is contained in:
PM-pinou
2026-07-24 11:29:02 +08:00
parent d7f9db098c
commit fa13ebcf72
+43 -5
View File
@@ -502,6 +502,44 @@ function escapeHtml(str) {
return String(str).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function renderMarkdown(text) {
if (!text) return '';
// Escape HTML first
var html = escapeHtml(text);
// Code blocks (```...```) — must be before inline code
html = html.replace(/```(\w*)\n?([\s\S]*?)```/g, '<pre style="background:#1a1a2e;color:#e0e0e0;padding:0.5rem;border-radius:4px;font-size:0.78rem;overflow:auto;margin:0.4rem 0;"><code>$2</code></pre>');
// Inline code
html = html.replace(/`([^`]+)`/g, '<code style="background:#1a1a2e;color:#e0e0e0;padding:0.1rem 0.3rem;border-radius:3px;font-size:0.78rem;">$1</code>');
// Bold (**text** or __text__)
html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
html = html.replace(/__([^_]+)__/g, '<strong>$1</strong>');
// Italic (*text* or _text_)
html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
html = html.replace(/(?<!_)_{1}(?!_)([^_]+)_{1}(?!_)/g, '<em>$1</em>');
// Links [text](url)
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" style="color:#4361ee;">$1</a>');
// Headers (# ## ###)
html = html.replace(/^### (.+)$/gm, '<h4 style="margin:0.5rem 0 0.3rem;font-size:0.95rem;">$1</h4>');
html = html.replace(/^## (.+)$/gm, '<h3 style="margin:0.5rem 0 0.3rem;font-size:1rem;">$1</h3>');
html = html.replace(/^# (.+)$/gm, '<h2 style="margin:0.5rem 0 0.3rem;font-size:1.1rem;">$1</h2>');
// Blockquotes
html = html.replace(/^&gt;\s?(.+)$/gm, '<blockquote style="border-left:3px solid #4361ee;padding:0.3rem 0.6rem;margin:0.3rem 0;background:#f0f4ff;border-radius:0 4px 4px 0;">$1</blockquote>');
// Unordered lists
html = html.replace(/^[\s]*[-*+]\s+(.+)$/gm, '<li style="margin:0.15rem 0;">$1</li>');
html = html.replace(/(<li[\s\S]*?<\/li>)\n(?!<li)/g, function(m) { return '<ul style="padding-left:1.5rem;margin:0.3rem 0;">' + m + '</ul>'; });
// Ordered lists
html = html.replace(/^\d+\.\s+(.+)$/gm, '<li style="margin:0.15rem 0;">$1</li>');
// Horizontal rules
html = html.replace(/^---$/gm, '<hr style="border:none;border-top:1px solid #ddd;margin:0.5rem 0;">');
// Paragraphs (double newlines → paragraphs)
html = html.replace(/\n\n/g, '</p><p style="margin:0.4rem 0;">');
// Line breaks (single newlines → br)
html = html.replace(/\n/g, '<br>');
// Wrap in paragraph if not already wrapped
if (!html.startsWith('<')) html = '<p style="margin:0.4rem 0;">' + html + '</p>';
return html;
}
function prettyJson(obj) {
try { return JSON.stringify(obj, null, 2); }
catch(e) { return String(obj); }
@@ -596,18 +634,18 @@ function renderTimeline(entries) {
if (entry.type === 'thinking') {
// 思考条目 — 浅蓝背景文本块
const div = document.createElement('div');
div.style.cssText = 'background:#eef2ff;padding:0.6rem;border-radius:6px;font-size:0.78rem;line-height:1.6;color:#333;max-height:200px;overflow:auto;margin-bottom:0.3rem;white-space:pre-wrap;border:1px solid #d0d8f0;';
div.style.cssText = 'background:#eef2ff;padding:0.6rem;border-radius:6px;font-size:0.78rem;line-height:1.6;color:#333;max-height:300px;overflow:auto;margin-bottom:0.3rem;border:1px solid #d0d8f0;';
div.innerHTML =
'<span style="font-weight:600;color:#4361ee;font-size:0.75rem;display:block;margin-bottom:0.3rem;">💭 思考</span>' +
escapeHtml(entry.text);
renderMarkdown(entry.text);
container.appendChild(div);
} else if (entry.type === 'answer') {
// 回答条目 — 绿色背景
// 回答条目 — 绿色背景,支持 Markdown
const div = document.createElement('div');
div.style.cssText = 'background:#e8f5e9;padding:0.6rem;border-radius:6px;font-size:0.78rem;line-height:1.6;color:#333;max-height:300px;overflow:auto;margin-bottom:0.3rem;white-space:pre-wrap;border:1px solid #c8e6c9;';
div.style.cssText = 'background:#e8f5e9;padding:0.6rem 0.8rem;border-radius:6px;font-size:0.82rem;line-height:1.7;color:#333;max-height:400px;overflow:auto;margin-bottom:0.3rem;border:1px solid #c8e6c9;';
div.innerHTML =
'<span style="font-weight:600;color:#2e7d32;font-size:0.75rem;display:block;margin-bottom:0.3rem;">✅ 回答</span>' +
escapeHtml(entry.text);
renderMarkdown(entry.text);
container.appendChild(div);
} else if (entry.type === 'tool') {
const card = document.createElement('div');