preserve conversation trace scroll
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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\)/);
|
||||
|
||||
Reference in New Issue
Block a user