fix(hub): render AI coach summary and chat as Markdown
Reuse shared ai_review_render.js so summaries and coach replies display formatted lists and bold text instead of raw syntax. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2958,17 +2958,32 @@
|
||||
return `${n > 0 ? "+" : "-"}${abs}U`;
|
||||
}
|
||||
|
||||
function renderAiMarkdown(text) {
|
||||
const escLocal = (s) =>
|
||||
String(s || "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
return escLocal(text)
|
||||
function renderHubMarkdown(text) {
|
||||
const raw = String(text || "");
|
||||
if (typeof window !== "undefined" && window.AiReviewRender && window.AiReviewRender.renderMarkdown) {
|
||||
return window.AiReviewRender.renderMarkdown(raw);
|
||||
}
|
||||
return esc(raw)
|
||||
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
|
||||
.replace(/\n/g, "<br>");
|
||||
}
|
||||
|
||||
function renderAiMarkdown(text) {
|
||||
return renderHubMarkdown(text);
|
||||
}
|
||||
|
||||
function setAiSummaryMarkdown(body, contentMd) {
|
||||
if (!body) return;
|
||||
body.classList.add("ai-result-md");
|
||||
body.innerHTML = renderHubMarkdown(contentMd);
|
||||
}
|
||||
|
||||
function setAiSummaryPlaceholder(body, html) {
|
||||
if (!body) return;
|
||||
body.classList.remove("ai-result-md");
|
||||
body.innerHTML = html;
|
||||
}
|
||||
|
||||
function renderAiSummaryStats(snapshot) {
|
||||
const el = document.getElementById("ai-summary-stats");
|
||||
if (!el) return;
|
||||
@@ -2994,10 +3009,13 @@
|
||||
const label = isUser ? "主人" : "AI教练";
|
||||
const rowCls = isUser ? "ai-msg-row-user" : "ai-msg-row-coach";
|
||||
const bubbleCls = isUser ? "ai-bubble-user" : "ai-bubble-assistant";
|
||||
const isThinking = extraClass && String(extraClass).includes("ai-bubble-thinking");
|
||||
const bubbleInner = isUser || isThinking ? esc(content || "") : renderHubMarkdown(content || "");
|
||||
const mdCls = !isUser && !isThinking ? " ai-result-md" : "";
|
||||
return (
|
||||
`<div class="ai-msg-row ${rowCls}">` +
|
||||
`<span class="ai-msg-role">${label}</span>` +
|
||||
`<div class="ai-bubble ${bubbleCls}${extraClass ? " " + extraClass : ""}">${esc(content || "")}</div>` +
|
||||
`<div class="ai-bubble ${bubbleCls}${mdCls}${extraClass ? " " + extraClass : ""}">${bubbleInner}</div>` +
|
||||
`</div>`
|
||||
);
|
||||
}
|
||||
@@ -3057,7 +3075,7 @@
|
||||
const j = await r.json();
|
||||
const latest = j.latest;
|
||||
if (latest && latest.content_md) {
|
||||
if (body) body.innerHTML = renderAiMarkdown(latest.content_md);
|
||||
if (body) setAiSummaryMarkdown(body, latest.content_md);
|
||||
renderAiSummaryStats(latest.stats_snapshot);
|
||||
const sm = document.getElementById("ai-summary-meta");
|
||||
if (sm && latest.generated_at) {
|
||||
@@ -3065,7 +3083,7 @@
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (body) body.innerHTML = `<p class="ai-placeholder">${esc(String(e))}</p>`;
|
||||
if (body) setAiSummaryPlaceholder(body, `<p class="ai-placeholder">${esc(String(e))}</p>`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3087,7 +3105,7 @@
|
||||
const btn = document.getElementById("btn-ai-summary");
|
||||
const body = document.getElementById("ai-summary-body");
|
||||
if (btn) btn.disabled = true;
|
||||
if (body) body.innerHTML = '<p class="ai-placeholder">正在聚合四户数据并生成总结…</p>';
|
||||
if (body) setAiSummaryPlaceholder(body, '<p class="ai-placeholder">正在聚合四户数据并生成总结…</p>');
|
||||
try {
|
||||
const r = await apiFetch("/api/ai/summary/generate", {
|
||||
method: "POST",
|
||||
@@ -3099,14 +3117,14 @@
|
||||
if (!j.ok && j.detail) throw new Error(j.detail);
|
||||
const sum = j.summary;
|
||||
if (sum && sum.content_md && body) {
|
||||
body.innerHTML = renderAiMarkdown(sum.content_md);
|
||||
setAiSummaryMarkdown(body, sum.content_md);
|
||||
renderAiSummaryStats(sum.stats_snapshot);
|
||||
}
|
||||
showToast(j.cached ? "已是最新上下文,返回缓存总结" : "今日总结已生成");
|
||||
await loadAiSummary();
|
||||
} catch (e) {
|
||||
showToast(String(e), true);
|
||||
if (body) body.innerHTML = `<p class="ai-placeholder">${esc(String(e))}</p>`;
|
||||
if (body) setAiSummaryPlaceholder(body, `<p class="ai-placeholder">${esc(String(e))}</p>`);
|
||||
} finally {
|
||||
aiSummaryLoading = false;
|
||||
if (btn) btn.disabled = false;
|
||||
|
||||
Reference in New Issue
Block a user