merge: maestro/tsk_O9q0_u78_NPo [tsk_O9q0_u78_NPo]

This commit is contained in:
maestro
2026-06-13 11:35:20 +08:00
6 changed files with 512 additions and 6 deletions
+63 -6
View File
@@ -589,12 +589,18 @@ function renderArchiveModal(t, runs, events) {
.join('');
const runRows = runs.length ? `<div class="gate-doc-label">执行历史(${runs.length} 次 run</div>
<dl class="result-kv">${runs.map((x) => kv(
`${RUN_KIND[x.kind] || esc(x.kind)} · ${RUN_ST[x.status] || esc(x.status)}`,
`${fmtFull(x.startedAt)}${x.endedAt ? fmtFull(x.endedAt) : '—'} · 时长 ${fmtDuration(x.startedAt, x.endedAt)}` +
(x.error ? `<br>错误:${esc(x.error)}` : '') +
(x.transcriptRef ? `<br><span class="t-id">转录 ${esc(x.transcriptRef)}</span>` : ''),
)).join('')}</dl>` : '';
<div class="run-list">${runs.map((x) => `
<div class="run-item">
<div class="run-head">
<span class="run-kind">${RUN_KIND[x.kind] || esc(x.kind)}</span>
<span class="run-st run-st-${esc(x.status)}">${RUN_ST[x.status] || esc(x.status)}</span>
<span class="run-time">${fmtFull(x.startedAt)}${x.endedAt ? fmtFull(x.endedAt) : ''} · ${fmtDuration(x.startedAt, x.endedAt)}</span>
<span class="spacer"></span>
${x.transcriptRef ? `<button class="btn btn-xs btn-ghost" data-action="run-transcript" data-id="${esc(x.id)}">▶ 查看转录</button>` : ''}
</div>
${x.error ? `<div class="run-err">错误:${esc(x.error)}</div>` : ''}
${x.transcriptRef ? `<div class="transcript" id="transcript-${esc(x.id)}" hidden></div>` : ''}
</div>`).join('')}</div>` : '';
const resultBlock = r ? `${reviewReportBlocks(r)}
<div class="gate-doc-label">执行结果</div>
@@ -651,6 +657,41 @@ function renderArchiveModal(t, runs, events) {
</div>`;
}
// 过长内容折叠为 <details>,短内容直接 <pre>
function foldable(text, truncated, threshold = 400) {
const body = esc(text) + (truncated ? esc('\n…(已截断)') : '');
if ((text || '').length <= threshold) return `<pre class="t-pre">${body}</pre>`;
return `<details class="t-fold"><summary>展开(${(text || '').length} 字符)</summary><pre class="t-pre">${body}</pre></details>`;
}
// 结构化转录消息流 → HTML(按 type 分类;文本/结论复用 mdToHtml,工具调用/结果折叠)
function renderTranscript(messages) {
if (!messages || !messages.length) return '<div class="transcript-empty">(无可展示的转录内容)</div>';
return `<div class="t-stream">${messages.map((m) => {
if (m.type === 'text') {
return `<div class="t-msg t-text"><span class="t-badge t-badge-a">assistant</span>
<div class="doc md">${mdToHtml(m.text || '')}</div>${m.truncated ? '<div class="t-trunc">…(已截断)</div>' : ''}</div>`;
}
if (m.type === 'result') {
return `<div class="t-msg t-result"><span class="t-badge t-badge-r">结论</span>
<div class="doc md">${mdToHtml(m.text || '')}</div>${m.truncated ? '<div class="t-trunc"></div>' : ''}</div>`;
}
if (m.type === 'fallback') {
return `<div class="t-msg t-fallback"><span class="t-badge t-badge-w">模型回退</span>
<span class="t-fallback-txt">${esc(m.from)}${esc(m.to)}</span></div>`;
}
if (m.type === 'tool_use') {
return `<div class="t-msg t-tool"><span class="t-badge t-badge-t">工具调用</span>
<span class="t-tool-name">${esc(m.name)}</span>${m.input ? foldable(m.input, m.truncated) : ''}</div>`;
}
if (m.type === 'tool_result') {
return `<div class="t-msg t-tool-result${m.isError ? ' is-err' : ''}">
<span class="t-badge t-badge-tr">工具结果${m.isError ? ' · 报错' : ''}</span>${foldable(m.text || '', m.truncated)}</div>`;
}
return '';
}).join('')}</div>`;
}
// ── 渲染:全局预览(全屏读方案 + 就地裁决) ──
function renderPreview() {
const root = $('#previewRoot');
@@ -1237,6 +1278,22 @@ document.addEventListener('click', (ev) => {
case 'archive-detail':
openArchiveDetail(id);
break;
case 'run-transcript': {
ev.stopPropagation();
const box = document.getElementById(`transcript-${id}`);
if (!box) break;
if (!box.hidden) { box.hidden = true; el.textContent = '▶ 查看转录'; break; }
box.hidden = false;
el.textContent = '▼ 收起转录';
if (!box.dataset.loaded) {
box.innerHTML = '<div class="transcript-loading">加载中…</div>';
api(`/api/runs/${id}/transcript`)
.then((data) => { box.dataset.loaded = '1'; box.innerHTML = renderTranscript(data.messages); })
.catch((e) => { box.innerHTML = `<div class="transcript-err">${esc(e.message)}</div>`; });
}
break;
}
case 'archive-modal-close':
$('#archiveModalRoot').hidden = true;
$('#archiveModalRoot').innerHTML = '';
+47
View File
@@ -842,3 +842,50 @@ li.ev-updated { --ev: var(--muted); }
.detail-confirm-msg {
font-size: 12px; color: var(--ink);
}
/* ── 归档详情:执行历史 + 转录回放 ── */
.run-list { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.run-item { border: 1px solid var(--line-soft); background: var(--panel); padding: 8px 10px; }
.run-head { display: flex; align-items: center; gap: 10px; font-size: 12px; flex-wrap: wrap; }
.run-head .spacer { flex: 1; }
.run-kind { color: var(--ink); font-weight: 600; }
.run-st { font-size: 11px; padding: 1px 6px; border: 1px solid var(--line); color: var(--muted); }
.run-st-succeeded { color: var(--green); border-color: var(--green-dim); }
.run-st-failed { color: var(--red); border-color: var(--red-dim); }
.run-st-started { color: var(--cyan); border-color: var(--cyan-dim); }
.run-time { color: var(--faint); font-size: 11px; letter-spacing: 0; }
.run-err { margin-top: 6px; font-size: 12px; color: var(--red); }
.transcript { margin-top: 8px; border-top: 1px dashed var(--line-soft); padding-top: 8px; }
.transcript-loading, .transcript-empty { color: var(--muted); font-size: 12px; }
.transcript-err { color: var(--red); font-size: 12px; }
.t-stream { display: flex; flex-direction: column; gap: 8px; }
.t-msg { font-size: 12.5px; border-left: 2px solid var(--line); padding: 2px 0 2px 10px; }
.t-msg .doc.md { margin-top: 2px; }
.t-text { border-left-color: var(--green-dim); }
.t-result { border-left-color: var(--green); }
.t-tool { border-left-color: var(--cyan-dim); }
.t-tool-result { border-left-color: var(--amber-dim); }
.t-tool-result.is-err { border-left-color: var(--red-dim); }
.t-fallback { border-left-color: var(--amber); }
.t-badge {
display: inline-block; font-size: 10px; letter-spacing: .08em;
padding: 1px 6px; margin-right: 8px; border: 1px solid var(--line); color: var(--muted);
}
.t-badge-a { color: var(--green); border-color: var(--green-dim); }
.t-badge-r { color: var(--green); border-color: var(--green); }
.t-badge-t { color: var(--cyan); border-color: var(--cyan-dim); }
.t-badge-tr { color: var(--amber); border-color: var(--amber-dim); }
.t-badge-w { color: var(--amber); border-color: var(--amber); }
.t-tool-name { color: var(--cyan); font-weight: 600; }
.t-fallback-txt { color: var(--amber); }
.t-trunc { color: var(--faint); font-size: 11px; margin-top: 2px; }
.t-pre {
margin: 4px 0 0; padding: 6px 8px; background: var(--bg-deep); border: 1px solid var(--line-soft);
font-size: 11.5px; white-space: pre-wrap; word-break: break-word; max-height: 320px; overflow-y: auto;
}
.t-fold { margin-top: 4px; }
.t-fold > summary { cursor: pointer; color: var(--muted); font-size: 11.5px; }
.t-fold > summary:hover { color: var(--green); }
.t-fold .t-pre { max-height: 480px; }