fix(auto): markdown rendering for LLM answer and thinking blocks
This commit is contained in:
@@ -502,6 +502,44 @@ function escapeHtml(str) {
|
|||||||
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(/^>\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) {
|
function prettyJson(obj) {
|
||||||
try { return JSON.stringify(obj, null, 2); }
|
try { return JSON.stringify(obj, null, 2); }
|
||||||
catch(e) { return String(obj); }
|
catch(e) { return String(obj); }
|
||||||
@@ -596,18 +634,18 @@ function renderTimeline(entries) {
|
|||||||
if (entry.type === 'thinking') {
|
if (entry.type === 'thinking') {
|
||||||
// 思考条目 — 浅蓝背景文本块
|
// 思考条目 — 浅蓝背景文本块
|
||||||
const div = document.createElement('div');
|
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 =
|
div.innerHTML =
|
||||||
'<span style="font-weight:600;color:#4361ee;font-size:0.75rem;display:block;margin-bottom:0.3rem;">💭 思考</span>' +
|
'<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);
|
container.appendChild(div);
|
||||||
} else if (entry.type === 'answer') {
|
} else if (entry.type === 'answer') {
|
||||||
// 回答条目 — 绿色背景
|
// 回答条目 — 绿色背景,支持 Markdown
|
||||||
const div = document.createElement('div');
|
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 =
|
div.innerHTML =
|
||||||
'<span style="font-weight:600;color:#2e7d32;font-size:0.75rem;display:block;margin-bottom:0.3rem;">✅ 回答</span>' +
|
'<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);
|
container.appendChild(div);
|
||||||
} else if (entry.type === 'tool') {
|
} else if (entry.type === 'tool') {
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
|
|||||||
Reference in New Issue
Block a user