// ── 报告渲染:把 markdown(## 章节 + - 列表 + **粗体** + `代码`)渲染成可折叠卡片 ── function mdInline(text) { const nodes = []; const re = /(\*\*[^*]+\*\*|`[^`]+`)/g; let last = 0; let m; let key = 0; while ((m = re.exec(text))) { if (m.index > last) nodes.push(text.slice(last, m.index)); const tok = m[0]; if (tok.startsWith('**')) nodes.push({tok.slice(2, -2)}); else nodes.push({tok.slice(1, -1)}); last = m.index + tok.length; } if (last < text.length) nodes.push(text.slice(last)); return nodes; } function mdBody(body) { const out = []; let key = 0; for (const raw of body.split('\n')) { const t = raw.trim(); if (!t) { out.push(
); continue; } if (/^[-*]\s/.test(t)) { out.push(
·{mdInline(t.replace(/^[-*]\s/, ''))}
); } else if (/^#{1,4}\s/.test(t)) { out.push(
{mdInline(t.replace(/^#{1,4}\s/, ''))}
); } else { out.push(
{mdInline(t)}
); } } return out; } function mdSections(md) { if (!md) return []; const out = []; const lines = md.split('\n'); let cur = null; for (const line of lines) { const h = line.match(/^##\s+(.+)$/); if (h) { if (cur) out.push(cur); cur = { title: h[1].trim(), body: '' }; } else if (cur) { cur.body += (cur.body ? '\n' : '') + line; } else { if (line.trim()) { cur = { title: '', body: line }; } } // 首章节前的前言 } if (cur) out.push(cur); return out; } function ReportSectionCard({ title, body, t, defaultOpen }) { const [open, setOpen] = React.useState(defaultOpen); return (
setOpen((o) => !o)} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '9px 12px', cursor: 'pointer', background: 'var(--panel-2)', userSelect: 'none' }}> {title || '说明'}
{open ?
{mdBody(body)}
: null}
); } function ReportCards({ md, t }) { const secs = mdSections(md); if (!secs.length) return null; return
{secs.map((s, i) => )}
; } // 单文件 diff 渲染(+/- 行着色) function DiffBody({ diff }) { return (
      {diff.split('\n').map((ln, i) => {
        const c = (ln.startsWith('+') && !ln.startsWith('+++')) ? 'var(--green)'
          : (ln.startsWith('-') && !ln.startsWith('---')) ? 'var(--red)'
          : ln.startsWith('@@') ? 'var(--cyan)'
          : (ln.startsWith('diff --git') || ln.startsWith('index ') || ln.startsWith('+++') || ln.startsWith('---')) ? 'var(--faint)'
          : 'var(--muted)';
        return 
{ln || ' '}
; })}
); } // 改动文件列表:点击文件展开其 diff(按需向 daemon 取) function FileDiffs({ taskId, t }) { const [files, setFiles] = React.useState(null); const [openPath, setOpenPath] = React.useState(null); React.useEffect(() => { let alive = true; fetch('/api/tasks/' + taskId + '/diff').then((r) => r.json()).then((d) => { if (alive) setFiles(d.files || []); }).catch(() => { if (alive) setFiles([]); }); return () => { alive = false; }; }, [taskId]); if (!files) return
… diff
; if (!files.length) return null; return (
{files.map((f) => (
setOpenPath((p) => (p === f.path ? null : f.path))} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '7px 11px', cursor: 'pointer', background: 'var(--panel-2)' }}> {f.path} {t.clickToView || ''}
{openPath === f.path ? : null}
))}
); } // 全屏预览:读完整方案 + 就地裁决 function GatePreview({ item, t, onDecide, onClose }) { const { ComplexityBadge, StatusChip, Button, Textarea } = window.MaestroDesignSystem_a6a290; const [rejecting, setRejecting] = React.useState(false); const [reason, setReason] = React.useState(''); React.useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [onClose]); if (!item) return null; // 拆解子任务序号映射(依赖用 #序号 引用) const subIdx = {}; (item.subtasks || []).forEach((s, i) => { subIdx[s.id] = i + 1; }); const rv = item.review; const vBadge = (label, v) => { const ok = v === 'approve', bad = v === 'reject'; return ( {label} {ok ? '✓ ' + t.vApprove : bad ? '✗ ' + t.vReject : t.vNone} ); }; const secLabel = (txt) =>
{txt}
; const reportBox = (txt) =>
{txt}
; return (
{t.gates[item.gate]} {item.title} {item.meta}
{item.gate === 'exec' && rv ? (
{vBadge(t.lblCodeReview, rv.verdict)} {vBadge(t.lblSecurity, rv.securityVerdict)}
) : null} {item.docLabel ?
{item.docLabel}
: null} {/^##\s/m.test(item.doc || '') ? :
{item.doc}
} {item.gate === 'exec' && rv && rv.securitySummary ? ( {secLabel(t.lblSecurity)} ) : null} {item.gate === 'exec' && rv && (rv.commits.length || rv.diffSummary) ? ( {secLabel(t.lblChanges + (rv.branch ? ' · ' + rv.branch : ''))}
{rv.commits.map((c) =>
· {c}
)} {rv.diffSummary ?
{rv.diffSummary}
: null}
) : null} {item.gate === 'plan' && item.subtasks && item.subtasks.length ? (
{t.gateSubtasks} · {item.subtasks.length}
{item.subtasks.map((s, i) => { const deps = (s.deps || []).map((d) => (subIdx[d] ? '#' + subIdx[d] : null)).filter(Boolean); return (
{i + 1} P{s.priority} {s.title} {StatusChip ? : null}
{t.dependsOn}: {deps.length ? {deps.join('、')} : t.noDeps}
); })}
) : null}
{rejecting ? (