fix trace scroll preservation
This commit is contained in:
@@ -108,6 +108,12 @@ session identifiers, the visible detail must include a short redacted
|
||||
[Code Agent readiness](code-agent-chat-readiness.md) 合同返回 completed
|
||||
且非空的 assistant response 前,Workbench 不得暗示已有真实 assistant reply。
|
||||
|
||||
Code Agent trace 在消息卡片内使用独立滚动容器展示。SSE、轮询或“回放 trace”
|
||||
更新只能刷新内容,不得重置用户已展开/折叠状态,也不得把
|
||||
`.message-trace-events` 的内部滚动位置跳回顶部;渲染前后必须按
|
||||
`messageId/traceId` 保存并恢复 trace list 的 `scrollTop/scrollLeft`。完整 trace
|
||||
可通过复制/下载获取,页面内默认保持“显示全部 + 内部滚动”的单一模式。
|
||||
|
||||
## Lightweight Checks
|
||||
|
||||
Use the existing lightweight checks unless a task explicitly authorizes a
|
||||
|
||||
@@ -180,6 +180,7 @@ const state = {
|
||||
chatMessages: [],
|
||||
traceStreams: new Map(),
|
||||
traceDetailsOpen: new Map(),
|
||||
traceScrollPositions: new Map(),
|
||||
canceledTraces: new Set(),
|
||||
currentRequest: null,
|
||||
sessionStatus: null,
|
||||
@@ -791,6 +792,7 @@ function initCommandBar() {
|
||||
for (const close of state.traceStreams.values()) close();
|
||||
state.traceStreams.clear();
|
||||
state.traceDetailsOpen.clear();
|
||||
state.traceScrollPositions.clear();
|
||||
state.chatMessages = [];
|
||||
state.conversationId = null;
|
||||
state.sessionId = null;
|
||||
@@ -2004,6 +2006,7 @@ function workbenchApiSurfaceStatus(live, coreProbes = [live.healthLive, live.res
|
||||
}
|
||||
|
||||
function renderConversation() {
|
||||
captureTraceScrollPositions();
|
||||
const introMessages = [
|
||||
{
|
||||
role: "system",
|
||||
@@ -2014,6 +2017,30 @@ function renderConversation() {
|
||||
codeAgentStatusMessage(state.codeAgentAvailability)
|
||||
];
|
||||
replaceChildren(el.conversationList, ...[...introMessages, ...state.chatMessages].map(messageCard));
|
||||
restoreTraceScrollPositions();
|
||||
}
|
||||
|
||||
function captureTraceScrollPositions(root = el.conversationList) {
|
||||
for (const list of root.querySelectorAll(".message-trace-events[data-trace-ui-key]")) {
|
||||
rememberTraceScrollPosition(list.dataset.traceUiKey, list);
|
||||
}
|
||||
}
|
||||
|
||||
function rememberTraceScrollPosition(traceUiKey, list) {
|
||||
if (!traceUiKey || !list) return;
|
||||
state.traceScrollPositions.set(traceUiKey, {
|
||||
top: list.scrollTop,
|
||||
left: list.scrollLeft
|
||||
});
|
||||
}
|
||||
|
||||
function restoreTraceScrollPositions(root = el.conversationList) {
|
||||
for (const list of root.querySelectorAll(".message-trace-events[data-trace-ui-key]")) {
|
||||
const position = state.traceScrollPositions.get(list.dataset.traceUiKey);
|
||||
if (!position) continue;
|
||||
list.scrollTop = Math.min(position.top, Math.max(0, list.scrollHeight - list.clientHeight));
|
||||
list.scrollLeft = Math.min(position.left, Math.max(0, list.scrollWidth - list.clientWidth));
|
||||
}
|
||||
}
|
||||
|
||||
function renderHardwareStatus(m3Status) {
|
||||
@@ -4066,6 +4093,10 @@ function messageTracePanel(message) {
|
||||
summary.textContent = runnerTraceHeadline(message, trace);
|
||||
const list = document.createElement("ol");
|
||||
list.className = "message-trace-events";
|
||||
if (traceUiKey) {
|
||||
list.dataset.traceUiKey = traceUiKey;
|
||||
list.addEventListener("scroll", () => rememberTraceScrollPosition(traceUiKey, list), { passive: true });
|
||||
}
|
||||
const events = Array.isArray(trace?.events) ? trace.events : [];
|
||||
const rows = traceDisplayRows(trace, events);
|
||||
if (events.length === 0) {
|
||||
|
||||
@@ -809,12 +809,21 @@ assert.match(app, /function messageTraceToolbar/);
|
||||
assert.match(app, /function messageTraceCountText/);
|
||||
assert.match(app, /显示全部\s+\$\{displayTotal\}\s+\/\s+原始\s+\$\{rawTotal\}/);
|
||||
assert.match(app, /traceDetailsOpen:\s*new Map\(\)/);
|
||||
assert.match(app, /traceScrollPositions:\s*new Map\(\)/);
|
||||
assert.match(app, /state\.traceDetailsOpen\.clear\(\)/);
|
||||
assert.match(app, /state\.traceScrollPositions\.clear\(\)/);
|
||||
assert.match(app, /function messageTraceUiKey/);
|
||||
assert.match(app, /function defaultTraceDetailsOpen/);
|
||||
assert.match(app, /function captureTraceScrollPositions/);
|
||||
assert.match(app, /function restoreTraceScrollPositions/);
|
||||
assert.match(app, /function rememberTraceScrollPosition/);
|
||||
assert.match(functionBody(app, "renderConversation"), /captureTraceScrollPositions\(\)/);
|
||||
assert.match(functionBody(app, "renderConversation"), /restoreTraceScrollPositions\(\)/);
|
||||
assert.match(functionBody(app, "messageTracePanel"), /state\.traceDetailsOpen\.get\(traceUiKey\)/);
|
||||
assert.match(functionBody(app, "messageTracePanel"), /defaultTraceDetailsOpen\(message\)/);
|
||||
assert.match(functionBody(app, "messageTracePanel"), /state\.traceDetailsOpen\.set\(traceUiKey,\s*details\.open\)/);
|
||||
assert.match(functionBody(app, "messageTracePanel"), /list\.dataset\.traceUiKey\s*=\s*traceUiKey/);
|
||||
assert.match(functionBody(app, "messageTracePanel"), /rememberTraceScrollPosition\(traceUiKey,\s*list\)/);
|
||||
assert.match(functionBody(app, "messageTracePanel"), /list\.dataset\.traceMode\s*=\s*"all"/);
|
||||
assert.match(functionBody(app, "messageTracePanel"), /renderTraceEventList\(list,\s*rows\)/);
|
||||
assert.doesNotMatch(app, /CODE_AGENT_TRACE_PREVIEW_LIMIT|tracePreviewRows|收起为最近|展开全部|data-trace-mode="tail"/);
|
||||
|
||||
Reference in New Issue
Block a user