4ac4c062e0
Co-authored-by: Cursor <cursoragent@cursor.com>
182 lines
6.2 KiB
JavaScript
182 lines
6.2 KiB
JavaScript
/**
|
||
* AI 日复盘 / 周复盘:Markdown 子集渲染 + 五节大标题图标兜底
|
||
*/
|
||
(function (global) {
|
||
"use strict";
|
||
|
||
var SECTION_FIXES = [
|
||
{ re: /^\*\*1\.\s*(?!📊)总体盈亏结构\*\*/m, rep: "**1. 📊 总体盈亏结构**" },
|
||
{ re: /^\*\*2\.\s*(?!🧠)心态与执行\*\*/m, rep: "**2. 🧠 心态与执行**" },
|
||
{ re: /^\*\*3\.\s*(?!🏷️)行为标签\*\*/m, rep: "**3. 🏷️ 行为标签**" },
|
||
{ re: /^\*\*4\.\s*(?!✅)改进建议\*\*/m, rep: "**4. ✅ 改进建议**" },
|
||
{ re: /^\*\*5\.\s*(?!📈)图表(?:分析)?\*\*/m, rep: "**5. 📈 图表分析**" },
|
||
{ re: /^1\.\s*(?!📊)总体盈亏结构/m, rep: "**1. 📊 总体盈亏结构**" },
|
||
{ re: /^2\.\s*(?!🧠)心态与执行/m, rep: "**2. 🧠 心态与执行**" },
|
||
{ re: /^3\.\s*(?!🏷️)行为标签/m, rep: "**3. 🏷️ 行为标签**" },
|
||
{ re: /^4\.\s*(?!✅)改进建议/m, rep: "**4. ✅ 改进建议**" },
|
||
{ re: /^5\.\s*(?!📈)图表/m, rep: "**5. 📈 图表分析**" },
|
||
];
|
||
|
||
function escapeHtml(s) {
|
||
return String(s || "")
|
||
.replace(/&/g, "&")
|
||
.replace(/</g, "<")
|
||
.replace(/>/g, ">")
|
||
.replace(/"/g, """);
|
||
}
|
||
|
||
function parseInline(raw) {
|
||
var s = escapeHtml(raw);
|
||
s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
||
s = s.replace(/`([^`]+)`/g, "<code>$1</code>");
|
||
return s;
|
||
}
|
||
|
||
function enhanceReviewHeadings(text) {
|
||
var out = String(text || "");
|
||
SECTION_FIXES.forEach(function (item) {
|
||
out = out.replace(item.re, item.rep);
|
||
});
|
||
if (/^【系统说明/m.test(out) && !/^ℹ️/m.test(out)) {
|
||
out = out.replace(/^【系统说明/gm, "ℹ️ 【系统说明");
|
||
}
|
||
if (/^原始记录:/m.test(out) && !/^📎/m.test(out)) {
|
||
out = out.replace(/^原始记录:/gm, "📎 **原始记录**");
|
||
}
|
||
return out;
|
||
}
|
||
|
||
function renderMarkdown(text) {
|
||
var src = enhanceReviewHeadings(text);
|
||
var lines = src.replace(/\r\n/g, "\n").split("\n");
|
||
var html = [];
|
||
var inUl = false;
|
||
var inOl = false;
|
||
|
||
function closeLists() {
|
||
if (inUl) {
|
||
html.push("</ul>");
|
||
inUl = false;
|
||
}
|
||
if (inOl) {
|
||
html.push("</ol>");
|
||
inOl = false;
|
||
}
|
||
}
|
||
|
||
lines.forEach(function (line) {
|
||
var trimmed = line.trim();
|
||
if (!trimmed) {
|
||
closeLists();
|
||
return;
|
||
}
|
||
var hm = trimmed.match(/^(#{1,3})\s+(.+)$/);
|
||
if (hm) {
|
||
closeLists();
|
||
var level = hm[1].length + 1;
|
||
if (level > 4) level = 4;
|
||
html.push("<h" + level + ">" + parseInline(hm[2]) + "</h" + level + ">");
|
||
return;
|
||
}
|
||
var ulm = trimmed.match(/^[-*]\s+(.+)$/);
|
||
if (ulm) {
|
||
if (!inUl) {
|
||
closeLists();
|
||
html.push("<ul>");
|
||
inUl = true;
|
||
}
|
||
html.push("<li>" + parseInline(ulm[1]) + "</li>");
|
||
return;
|
||
}
|
||
var olm = trimmed.match(/^\d+\.\s+(.+)$/);
|
||
if (olm && !/^\*\*/.test(trimmed)) {
|
||
if (!inOl) {
|
||
closeLists();
|
||
html.push("<ol>");
|
||
inOl = true;
|
||
}
|
||
html.push("<li>" + parseInline(olm[1]) + "</li>");
|
||
return;
|
||
}
|
||
closeLists();
|
||
if (/^📎\s*\*\*原始记录\*\*/.test(trimmed) || /^原始记录:/.test(trimmed)) {
|
||
html.push('<div class="md-raw-block-title">' + parseInline(trimmed) + "</div>");
|
||
return;
|
||
}
|
||
html.push("<p>" + parseInline(trimmed) + "</p>");
|
||
});
|
||
closeLists();
|
||
return html.join("\n");
|
||
}
|
||
|
||
var _genBusy = false;
|
||
|
||
function setGenerating(opts) {
|
||
opts = opts || {};
|
||
_genBusy = true;
|
||
var wrap = document.getElementById(opts.wrapId);
|
||
var el = document.getElementById(opts.elId);
|
||
var btn = opts.btnId ? document.getElementById(opts.btnId) : null;
|
||
if (wrap) wrap.style.display = "block";
|
||
if (el) {
|
||
el.classList.remove("ai-result-md");
|
||
el.classList.add("is-loading");
|
||
el.innerHTML = "";
|
||
el.innerText = opts.message || "生成复盘中,请稍候…";
|
||
}
|
||
if (btn) {
|
||
btn.disabled = true;
|
||
if (!btn.dataset.aiOrigText) btn.dataset.aiOrigText = btn.textContent;
|
||
btn.textContent = opts.btnLabel || "生成中…";
|
||
}
|
||
if (wrap && wrap.scrollIntoView) {
|
||
try {
|
||
wrap.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||
} catch (e) { /* ignore */ }
|
||
}
|
||
}
|
||
|
||
function clearGenerating(btnId) {
|
||
_genBusy = false;
|
||
var btn = btnId ? document.getElementById(btnId) : null;
|
||
if (btn) {
|
||
btn.disabled = false;
|
||
if (btn.dataset.aiOrigText) {
|
||
btn.textContent = btn.dataset.aiOrigText;
|
||
delete btn.dataset.aiOrigText;
|
||
}
|
||
}
|
||
}
|
||
|
||
function isGenerating() {
|
||
return _genBusy;
|
||
}
|
||
|
||
function setElementMarkdown(el, rawText) {
|
||
if (!el) return;
|
||
var raw = String(rawText || "");
|
||
el.dataset.markdownRaw = raw;
|
||
el.classList.remove("is-loading");
|
||
el.classList.add("ai-result-md");
|
||
el.innerHTML = renderMarkdown(raw);
|
||
}
|
||
|
||
function getElementMarkdown(el) {
|
||
if (!el) return "";
|
||
if (el.dataset && el.dataset.markdownRaw != null) {
|
||
return el.dataset.markdownRaw;
|
||
}
|
||
return el.innerText || "";
|
||
}
|
||
|
||
global.AiReviewRender = {
|
||
enhanceReviewHeadings: enhanceReviewHeadings,
|
||
renderMarkdown: renderMarkdown,
|
||
setElementMarkdown: setElementMarkdown,
|
||
getElementMarkdown: getElementMarkdown,
|
||
setGenerating: setGenerating,
|
||
clearGenerating: clearGenerating,
|
||
isGenerating: isGenerating,
|
||
};
|
||
})(typeof window !== "undefined" ? window : this);
|