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);
|
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);
|
const followRef = useRef(true);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
if (!working) {
|
||||||
|
followRef.current = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const viewport = scrollRef.current;
|
const viewport = scrollRef.current;
|
||||||
if (viewport && followRef.current) viewport.scrollTop = viewport.scrollHeight;
|
if (viewport && followRef.current) viewport.scrollTop = viewport.scrollHeight;
|
||||||
}, [rowCount, tail]);
|
}, [working, rowCount, tail]);
|
||||||
|
|
||||||
if (!working || rows.length === 0) return null;
|
if (!working || rows.length === 0) return null;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user