fix(tutor): the log stopped following a reply sent from answer mode
Submitting from answer mode brings back the earlier messages, the composer and the nav in one layout. That reset the log's scrollTop to 0, and the scroll event it fired read as the learner scrolling up — undoing the follow send() had just armed, so the reply landed out of view, 1,812px below. A move made while the log's content or box changed size is now taken for layout, not for the learner. Scrolling up during a reply still lets go. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -206,8 +206,9 @@ export function TutorTab() {
|
|||||||
const log = useRef<HTMLDivElement>(null);
|
const log = useRef<HTMLDivElement>(null);
|
||||||
/** The log follows new text while this is set; see the follow effect. */
|
/** The log follows new text while this is set; see the follow effect. */
|
||||||
const stick = useRef(true);
|
const stick = useRef(true);
|
||||||
/** Where the log was last scrolled to; see onLogScroll. */
|
/** Where the log was last scrolled to, and how tall it was; see onLogScroll. */
|
||||||
const lastTop = useRef(0);
|
const lastTop = useRef(0);
|
||||||
|
const lastHeights = useRef({ content: 0, box: 0 });
|
||||||
const menuButton = useRef<HTMLButtonElement>(null);
|
const menuButton = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
/* ── the keyboard's field, and answer mode ── */
|
/* ── the keyboard's field, and answer mode ── */
|
||||||
@@ -716,15 +717,24 @@ export function TutorTab() {
|
|||||||
stream has added more than STICK_PX below it — judged by distance
|
stream has added more than STICK_PX below it — judged by distance
|
||||||
alone, that read as the learner scrolling away, and the log stopped
|
alone, that read as the learner scrolling away, and the log stopped
|
||||||
following mid-reply. Growing content never moves scrollTop up; a
|
following mid-reply. Growing content never moves scrollTop up; a
|
||||||
finger does. */
|
finger does.
|
||||||
|
|
||||||
|
And a finger does not change the log's height. Leaving answer mode
|
||||||
|
brings back the earlier messages, the composer and the nav in one
|
||||||
|
layout, which reset scrollTop to 0 — an upward jump that read as
|
||||||
|
scrolling away, just as the answer was sent. A move made while the
|
||||||
|
content or the box changed size is layout, not the learner. */
|
||||||
|
|
||||||
const onLogScroll = useCallback(() => {
|
const onLogScroll = useCallback(() => {
|
||||||
const el = log.current;
|
const el = log.current;
|
||||||
if (!el || !active) return;
|
if (!el || !active) return;
|
||||||
const top = el.scrollTop;
|
const top = el.scrollTop;
|
||||||
|
const resized =
|
||||||
|
el.scrollHeight !== lastHeights.current.content || el.clientHeight !== lastHeights.current.box;
|
||||||
if (el.scrollHeight - top - el.clientHeight < STICK_PX) stick.current = true;
|
if (el.scrollHeight - top - el.clientHeight < STICK_PX) stick.current = true;
|
||||||
else if (top < lastTop.current - 1) stick.current = false;
|
else if (top < lastTop.current - 1 && !resized) stick.current = false;
|
||||||
lastTop.current = top;
|
lastTop.current = top;
|
||||||
|
lastHeights.current = { content: el.scrollHeight, box: el.clientHeight };
|
||||||
}, [active]);
|
}, [active]);
|
||||||
|
|
||||||
const follow = useCallback(() => {
|
const follow = useCallback(() => {
|
||||||
@@ -732,6 +742,7 @@ export function TutorTab() {
|
|||||||
if (!el || !stick.current) return;
|
if (!el || !stick.current) return;
|
||||||
el.scrollTop = el.scrollHeight;
|
el.scrollTop = el.scrollHeight;
|
||||||
lastTop.current = el.scrollTop;
|
lastTop.current = el.scrollTop;
|
||||||
|
lastHeights.current = { content: el.scrollHeight, box: el.clientHeight };
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user