fix(webui): stabilize fast history scrolling

This commit is contained in:
Xubin Ren
2026-08-27 10:45:40 +08:00
parent 25e20a1458
commit 6a3f53a917
3 changed files with 144 additions and 10 deletions
+58
View File
@@ -1481,6 +1481,64 @@ describe("ThreadViewport", () => {
expect(screen.getAllByText("message 299").length).toBeGreaterThan(0);
});
it("keeps the first visible history item fixed while deferred rows materialize", () => {
const resizeObserver = stubResizeObserver();
try {
const { container } = render(
<ThreadViewport
messages={makeLongMessages(300)}
isStreaming={false}
composer={<div />}
/>,
);
const scroller = getScroller(container);
let scrollHeight = 2_400;
Object.defineProperties(scroller, {
scrollHeight: { configurable: true, get: () => scrollHeight },
clientHeight: { configurable: true, value: 600 },
scrollTop: { configurable: true, writable: true, value: 80 },
getBoundingClientRect: {
configurable: true,
value: () => DOMRect.fromRect({ y: 0, width: 800, height: 600 }),
},
});
const anchor = screen.getByText("message 140")
.closest<HTMLElement>("[data-thread-display-unit]");
expect(anchor).not.toBeNull();
let anchorDocumentTop = 200;
Object.defineProperty(anchor, "getBoundingClientRect", {
configurable: true,
value: () => DOMRect.fromRect({
y: anchorDocumentTop - scroller.scrollTop,
width: 800,
height: 40,
}),
});
act(() => {
dispatchUserScroll(scroller);
});
anchorDocumentTop += 180;
scrollHeight += 180;
const content = screen.getByTestId("thread-message-region").firstElementChild;
const observer = resizeObserver.observers.find((candidate) =>
content ? candidate.elements.includes(content) : false,
);
expect(observer).toBeDefined();
act(() => {
observer?.callback([], observer as unknown as ResizeObserver);
});
expect(scroller.scrollTop).toBe(260);
expect(anchor.getBoundingClientRect().top).toBe(120);
} finally {
resizeObserver.restore();
}
});
it("automatically requests older transcript pages near the top", () => {
const onLoadOlder = vi.fn();