preserve conversation trace scroll

This commit is contained in:
Codex
2026-05-25 01:38:00 +00:00
parent 78e66c4a01
commit f2bbdf198a
3 changed files with 29 additions and 4 deletions
+5 -4
View File
@@ -109,10 +109,11 @@ session identifiers, the visible detail must include a short redacted
且非空的 assistant response 前,Workbench 不得暗示已有真实 assistant reply。
Code Agent trace 在消息卡片内使用独立滚动容器展示。SSE、轮询或“回放 trace”
更新只能刷新内容,不得重置用户已展开/折叠状态,也不得把
`.message-trace-events`内部滚动位置跳回顶部;渲染前后必须
`messageId/traceId` 保存并恢复 trace list`scrollTop/scrollLeft`。完整 trace
可通过复制/下载获取,页面内默认保持“显示全部 + 内部滚动”的单一模式。
更新只能刷新内容,不得重置用户已展开/折叠状态,也不得把 `#conversation-list`
`.message-trace-events` 的滚动位置跳回顶部;渲染前后必须保存并恢复外层
conversation list 和按 `messageId/traceId` 记录的 trace list
`scrollTop/scrollLeft`。完整 trace 可通过复制/下载获取,页面内默认保持“显示全部
+ 内部滚动”的单一模式。
## Lightweight Checks
+18
View File
@@ -180,6 +180,7 @@ const state = {
chatMessages: [],
traceStreams: new Map(),
traceDetailsOpen: new Map(),
conversationScrollPosition: { top: 0, left: 0 },
traceScrollPositions: new Map(),
canceledTraces: new Set(),
currentRequest: null,
@@ -792,6 +793,7 @@ function initCommandBar() {
for (const close of state.traceStreams.values()) close();
state.traceStreams.clear();
state.traceDetailsOpen.clear();
state.conversationScrollPosition = { top: 0, left: 0 };
state.traceScrollPositions.clear();
state.chatMessages = [];
state.conversationId = null;
@@ -2006,6 +2008,7 @@ function workbenchApiSurfaceStatus(live, coreProbes = [live.healthLive, live.res
}
function renderConversation() {
captureConversationScrollPosition();
captureTraceScrollPositions();
const introMessages = [
{
@@ -2017,9 +2020,24 @@ function renderConversation() {
codeAgentStatusMessage(state.codeAgentAvailability)
];
replaceChildren(el.conversationList, ...[...introMessages, ...state.chatMessages].map(messageCard));
restoreConversationScrollPosition();
restoreTraceScrollPositions();
}
function captureConversationScrollPosition() {
state.conversationScrollPosition = {
top: el.conversationList.scrollTop,
left: el.conversationList.scrollLeft
};
}
function restoreConversationScrollPosition() {
const position = state.conversationScrollPosition;
if (!position) return;
el.conversationList.scrollTop = Math.min(position.top, Math.max(0, el.conversationList.scrollHeight - el.conversationList.clientHeight));
el.conversationList.scrollLeft = Math.min(position.left, Math.max(0, el.conversationList.scrollWidth - el.conversationList.clientWidth));
}
function captureTraceScrollPositions(root = el.conversationList) {
for (const list of root.querySelectorAll(".message-trace-events[data-trace-ui-key]")) {
rememberTraceScrollPosition(list.dataset.traceUiKey, list);
+6
View File
@@ -809,14 +809,20 @@ 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, /conversationScrollPosition:\s*\{\s*top:\s*0,\s*left:\s*0\s*\}/);
assert.match(app, /traceScrollPositions:\s*new Map\(\)/);
assert.match(app, /state\.traceDetailsOpen\.clear\(\)/);
assert.match(app, /state\.conversationScrollPosition\s*=\s*\{\s*top:\s*0,\s*left:\s*0\s*\}/);
assert.match(app, /state\.traceScrollPositions\.clear\(\)/);
assert.match(app, /function messageTraceUiKey/);
assert.match(app, /function defaultTraceDetailsOpen/);
assert.match(app, /function captureConversationScrollPosition/);
assert.match(app, /function restoreConversationScrollPosition/);
assert.match(app, /function captureTraceScrollPositions/);
assert.match(app, /function restoreTraceScrollPositions/);
assert.match(app, /function rememberTraceScrollPosition/);
assert.match(functionBody(app, "renderConversation"), /captureConversationScrollPosition\(\)/);
assert.match(functionBody(app, "renderConversation"), /restoreConversationScrollPosition\(\)/);
assert.match(functionBody(app, "renderConversation"), /captureTraceScrollPositions\(\)/);
assert.match(functionBody(app, "renderConversation"), /restoreTraceScrollPositions\(\)/);
assert.match(functionBody(app, "messageTracePanel"), /state\.traceDetailsOpen\.get\(traceUiKey\)/);