af690701bf
- RichDoc(marked+DOMPurify+highlight.js CDN)统一渲染 gate spec/复审报告/任务树 SPEC;- Transcript 组件:拆解评审「拆解轨迹」懒加载 planner run transcript;- 「审批闸」改名「需你处理」并并入 needs_attention(requeue/接管/取消 + 完整复审报告,按代码/安全分段折叠卡片、标裁决徽章);- 子任务显 scopeFiles 文件范围 + frozen「待拆解确认」标。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
66 lines
3.6 KiB
React
66 lines
3.6 KiB
React
// 共享 Markdown 渲染:marked 解析 + DOMPurify 净化 + highlight.js 代码高亮。
|
|
// 挂 window.MaestroRichDoc,供 GateSection / TaskTree 等在 vite app 与 standalone demo 两环境共用。
|
|
// 依赖 window.marked / window.DOMPurify(必需,CDN 全局)+ window.hljs(可选,缺则代码块不着色)。
|
|
function ensureRichDocCss() {
|
|
if (document.getElementById('maestro-richdoc-css')) return;
|
|
const s = document.createElement('style');
|
|
s.id = 'maestro-richdoc-css';
|
|
s.textContent = `
|
|
.m-richdoc { font-size: 12.8px; line-height: 1.7; color: var(--ink); word-break: break-word; }
|
|
.m-richdoc > :first-child { margin-top: 0; }
|
|
.m-richdoc > :last-child { margin-bottom: 0; }
|
|
.m-richdoc h1, .m-richdoc h2, .m-richdoc h3, .m-richdoc h4 { font-weight: 700; line-height: 1.3; margin: 16px 0 8px; letter-spacing: .01em; }
|
|
.m-richdoc h1 { font-size: 1.32em; color: var(--ink); border-bottom: 1px solid var(--line-soft); padding-bottom: 5px; }
|
|
.m-richdoc h2 { font-size: 1.16em; color: var(--green); }
|
|
.m-richdoc h3 { font-size: 1.05em; color: var(--cyan); }
|
|
.m-richdoc h4 { font-size: 1em; color: var(--muted); }
|
|
.m-richdoc p { margin: 7px 0; }
|
|
.m-richdoc ul, .m-richdoc ol { margin: 7px 0; padding-left: 22px; }
|
|
.m-richdoc li { margin: 3px 0; }
|
|
.m-richdoc li::marker { color: var(--green); }
|
|
.m-richdoc a { color: var(--cyan); text-decoration: none; border-bottom: 1px solid var(--cyan-dim); }
|
|
.m-richdoc a:hover { border-bottom-color: var(--cyan); }
|
|
.m-richdoc strong { color: var(--ink); font-weight: 700; }
|
|
.m-richdoc em { color: var(--muted); }
|
|
.m-richdoc code { font-family: var(--mono); font-size: .9em; color: var(--cyan); background: var(--panel-2); border-radius: 3px; padding: 1.5px 5px; }
|
|
.m-richdoc pre { background: var(--bg-deep); border: 1px solid var(--line-soft); border-radius: 5px; padding: 11px 13px; overflow-x: auto; margin: 9px 0; }
|
|
.m-richdoc pre code { display: block; font-family: var(--mono); font-size: 11.8px; line-height: 1.55; color: var(--ink); background: none; border: 0; padding: 0; }
|
|
.m-richdoc blockquote { margin: 9px 0; padding: 2px 13px; border-left: 3px solid var(--line); color: var(--muted); }
|
|
.m-richdoc table { border-collapse: collapse; margin: 9px 0; font-size: .95em; display: block; overflow-x: auto; }
|
|
.m-richdoc th, .m-richdoc td { border: 1px solid var(--line); padding: 5px 10px; text-align: left; }
|
|
.m-richdoc th { background: var(--panel-2); color: var(--ink); font-weight: 700; }
|
|
.m-richdoc hr { border: 0; border-top: 1px solid var(--line-soft); margin: 14px 0; }
|
|
.m-richdoc img { max-width: 100%; }
|
|
`;
|
|
document.head.appendChild(s);
|
|
}
|
|
|
|
function MaestroRichDoc({ md }) {
|
|
const ref = React.useRef(null);
|
|
ensureRichDocCss();
|
|
const ready = typeof window !== 'undefined' && !!window.marked && !!window.DOMPurify;
|
|
const html = React.useMemo(() => {
|
|
if (!ready || !md) return null;
|
|
try {
|
|
const raw = window.marked.parse(String(md), { gfm: true, breaks: false });
|
|
return window.DOMPurify.sanitize(raw);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}, [md, ready]);
|
|
React.useEffect(() => {
|
|
if (html && ref.current && window.hljs) {
|
|
ref.current.querySelectorAll('pre code').forEach((el) => {
|
|
try { window.hljs.highlightElement(el); } catch (e) { /* noop */ }
|
|
});
|
|
}
|
|
}, [html]);
|
|
// 兜底:库未就绪或解析失败 → 原文 pre-wrap,永不崩
|
|
if (html == null) {
|
|
return <pre style={{ margin: 0, whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontFamily: 'var(--mono)', fontSize: 12.5, lineHeight: 1.6, color: 'var(--ink)' }}>{md || ''}</pre>;
|
|
}
|
|
return <div ref={ref} className="m-richdoc" dangerouslySetInnerHTML={{ __html: html }} />;
|
|
}
|
|
|
|
window.MaestroRichDoc = MaestroRichDoc;
|