fix(frontend): reset live log follow state on resume
This commit is contained in:
@@ -83,3 +83,41 @@ test("preserves manual live-log scroll position away from the bottom", () => {
|
||||
|
||||
expect(log.scrollTop).toBe(100);
|
||||
});
|
||||
|
||||
test("resets bottom-follow when the live log resumes after becoming inactive", () => {
|
||||
useSessionStore.setState({ transcript: [{ role: "assistant", text: "First" }] });
|
||||
const { rerender } = render(<CentralStatus working />);
|
||||
const log = screen.getByRole("log", { name: "Live model activity" });
|
||||
setScrollGeometry(log, { scrollHeight: 400, clientHeight: 100, scrollTop: 100 });
|
||||
fireEvent.scroll(log);
|
||||
|
||||
rerender(<CentralStatus working={false} />);
|
||||
expect(screen.queryByRole("log", { name: "Live model activity" })).not.toBeInTheDocument();
|
||||
|
||||
const scrollProperties = ["scrollHeight", "clientHeight", "scrollTop"] as const;
|
||||
const originalDescriptors = scrollProperties.map((property) =>
|
||||
Object.getOwnPropertyDescriptor(HTMLElement.prototype, property),
|
||||
);
|
||||
Object.defineProperties(HTMLElement.prototype, {
|
||||
scrollHeight: { configurable: true, value: 400 },
|
||||
clientHeight: { configurable: true, value: 100 },
|
||||
scrollTop: { configurable: true, writable: true, value: 0 },
|
||||
});
|
||||
|
||||
try {
|
||||
rerender(<CentralStatus working />);
|
||||
const resumedLog = screen.getByRole("log", { name: "Live model activity" });
|
||||
expect(resumedLog.scrollTop).toBe(400);
|
||||
|
||||
Object.defineProperty(resumedLog, "scrollHeight", { configurable: true, value: 500 });
|
||||
act(() => useSessionStore.getState().applyEvent({ type: "text_delta", text: " second" }));
|
||||
|
||||
expect(resumedLog.scrollTop).toBe(500);
|
||||
} finally {
|
||||
scrollProperties.forEach((property, index) => {
|
||||
const descriptor = originalDescriptors[index];
|
||||
if (descriptor) Object.defineProperty(HTMLElement.prototype, property, descriptor);
|
||||
else Reflect.deleteProperty(HTMLElement.prototype, property);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,9 +21,13 @@ export function CentralStatus({ working }: { working: boolean }) {
|
||||
const followRef = useRef(true);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!working) {
|
||||
followRef.current = true;
|
||||
return;
|
||||
}
|
||||
const viewport = scrollRef.current;
|
||||
if (viewport && followRef.current) viewport.scrollTop = viewport.scrollHeight;
|
||||
}, [rowCount, tail]);
|
||||
}, [working, rowCount, tail]);
|
||||
|
||||
if (!working || rows.length === 0) return null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user