Skip to content

Follow new transcript turns until scrolled up - #615

Open
henrylee1003 wants to merge 3 commits into
sysprog21:mainfrom
henrylee1003:transcript-follow
Open

henrylee1003 wants to merge 3 commits into
sysprog21:mainfrom
henrylee1003:transcript-follow

Conversation

@henrylee1003

@henrylee1003 henrylee1003 commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Once the transcript is longer than the Transcript tab, new turns land below the visible area and the panel does not move, so the candidate has to scroll down after every turn. The panel now follows the newest turn, including one still growing. Scrolling up stops the follow, and scrolling back to the end resumes it. Only the panel scrolls, never the page.

The follow uses the caption bar's own checks, moved into web/scroll-follow.js so there is one copy; caption behavior does not change. selectTab calls the view's new reveal, so the tab opens at the end if it was following. The empty state before the first turn is not followed. While following, the panel turns off scroll anchoring, because Chrome's anchoring moved it when rows rewrapped across the 1024px breakpoint, and that read as the candidate scrolling up; paused, anchoring stays on and keeps the reread line in place. The rule is in the Captions section of docs/avatar-contract.md.

Tests: tests/browser/scroll-follow.test.js covers the shared checks, tests/browser/render.test.js covers the view with a panel laid out like #transcript-panel, and tests/browser/transcript-follow.test.js runs the interview page in Chromium at 1280x720 and 900x700. The browser test fails on main.

Run locally on Windows: the changed and nearby browser tests (486) pass, and eslint, prettier and the commit checks are clean. diagnostic-capture, floor-capture, replay-evidence, replay-fixture, replay-input and review-workbench fail on this machine, the same way on main. The Rust lanes were not run locally.

I also checked it in a live interview with the latest binary (codetrial 0.1.0 (b2c2bc9)) serving this branch's web/ through --web-dir. The video shows the panel keeping Jim's growing turn in view:

demo.mp4

Close #592


Summary by cubic

Makes the Transcript tab follow new turns until the candidate scrolls up, so the newest turn stays in view instead of landing below the visible area. The follow uses the same checks as the caption bar, now shared in web/scroll-follow.js; caption behavior does not change.

  • Scrolling up stops the follow, and scrolling back to the end resumes it; only the panel scrolls, never the page.
  • Opening the Transcript tab reveals the end if it was following, or the candidate's place otherwise.
  • The empty state before the first turn is not followed.
  • Disables overflow-anchor on #transcript-panel while following, so Chrome's rewrapping scroll anchoring is not read as the candidate scrolling.
  • Adds browser tests covering the follow at 1280x720 and 900x700, plus unit tests for the shared checks; the browser test fails on main.

Closes #592.

Written for commit 2f2354c. Summary will update on new commits.

View guided diff

cubic-dev-ai[bot]

This comment was marked as resolved.

Comment thread tests/browser/transcript-follow.test.js Resolved

@ColtenOuO ColtenOuO left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please squash your commits.

Comment thread web/render.js Resolved
Comment thread web/styles.css
/* The transcript follows by its own rule (web/render.js); a browser anchor
moving the panel when rows rewrap would read as the candidate scrolling. */
#transcript-panel {
overflow-anchor: none;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overflow-anchor: none stops Chrome's anchoring from being read as the candidate scrolling, but it also removes what keeps a paused reader's place. If rows rewrap across the 1024px breakpoint while the candidate is rereading, the line they were on moves, because nothing adjusts scrollTop any more. The anchoring only misleads the follow while following is true, so consider turning it off only then (for example, follow() toggles a class on the panel). Otherwise, say in the Captions section of docs/avatar-contract.md that a paused reader can lose their line on a resize.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in 0edbb47: anchoring is off only while following. One setFollowing sets the flag and toggles a following class on the panel, from the scroll listener as well as from the follow, so anchoring is back on as soon as the candidate scrolls up, before the next turn. The rule is now #transcript-panel.following { overflow-anchor: none; }, and the Captions section says a paused reader keeps their line. 2f2354c adds a browser step that pauses mid-transcript and resizes across the 1024px breakpoint: the reread row stays within 2px; with the old unconditional rule it moves 18px at 1280x720 and 36px at 900x700.

@jserv

jserv commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Some review comments on this pull request are not settled yet. Please refine the change per the review comments listed below, reply on each thread with what you changed or why you did not, and mark each thread resolved yourself once it is settled (Resolve conversation, at the bottom of the thread). The pull request stays blocked while review threads are open.

Not addressed at the current head:

Checked at bfc43f4.

The caption bar decides whether to follow new text with two checks: is
the bar resting at its end, and did the candidate move it, as opposed
to the browser clamping it into a smaller range. The Transcript tab
needs the same decision, so move both into web/scroll-follow.js with
the 2px zoom slack, and have captions.js import them. The bar keeps its
own seenTop, so caption behavior does not change.

Change-Id: I3e4c0590a72f3187be6fbaadab8b1fb2c1774540
Once the transcript was longer than the Transcript tab, new turns were
added below the visible area and the panel stayed where it was, so the
candidate had to scroll down after every turn to read what Jim said.
The panel now follows the newest turn, including a turn still growing,
with the caption bar's checks: scrolling up to reread stops the follow
and scrolling back to the end resumes it. A hidden panel has no range,
so the Problem tab leaves the follow alone; opening the Transcript tab
shows the end if it was following. Only the panel scrolls, never the
page. The empty state before the first turn is left alone, so its
heading stays in view and scrolling it does not stop the follow. While
following, the panel turns off the browser's scroll anchoring, whose
move when rows rewrap across the 1024px breakpoint read as the
candidate scrolling up; paused, that anchoring is what keeps the reread
line in place, so it stays on. The rule is added to the Captions
section of the avatar contract.

Change-Id: I0565f9bfeabc1984ae837d4022e9465a325abb9e
The unit tests fake the panel's layout. This drives the real interview
page in Chromium at 1280x720 and at 900x700, adding turns through the
page's own transcript by running the tests with no room joined. It
checks that the empty state keeps its heading in view, that new turns
keep the panel at its end without moving a page taller than the window,
that scrolling up stops the follow and scrolling back resumes it, that
a resize across the 1024px breakpoint keeps the follow and keeps the
line a paused reader is on in place, and that the tab opens at the end,
or where the candidate left it, after turns arrived behind the Problem
tab. On main the check that a new turn is shown fails. The avatar
contract lists the new check.

Close sysprog21#592

Change-Id: Iaf92d70006d3df321bb1b482db90f1da7a509be0
@henrylee1003

Copy link
Copy Markdown
Contributor Author

Please squash your commits.
Squashed into three commits at 2f2354c. The empty-state and scroll-anchoring commits are folded into the one they fixed. What stays apart: 0a5c34b moves the caption bar's two checks into web/scroll-follow.js with no change to caption behavior, 0edbb47 makes the Transcript tab follow with them, and 2f2354c is the browser check.

@henrylee1003

Copy link
Copy Markdown
Contributor Author

The gates (portable) failure is a network error in the Install commentflow step (curl: (56) Connection died); the gates themselves did not run. Could someone re-run the failed job?

Comment thread web/render.js
function follow() {
// panel.scrollTop, not scrollIntoView: below the 1024px breakpoint the
// page scrolls too, and the candidate's place in it is not ours to move.
if (following) panel.scrollTop = panel.scrollHeight;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

follow runs only from render and reveal, so a following panel that grows past its end without a new turn stays where it was. A window that gets shorter shrinks clientHeight, and one that crosses the 1024px breakpoint from narrow to wide rewraps the rows in the narrower sidebar; either way scrollTop does not change, no scroll event fires, and the newest lines sit below the panel until Jim's next turn. A ResizeObserver on the panel that calls follow() while following would keep the end in view, and the resize step in tests/browser/transcript-follow.test.js could assert atEnd before it adds the next turns, which it does not today.

Comment thread web/render.js
if (scrolledByCandidate(panel, seenTop)) setFollowing(atScrollEnd(panel));
seenTop = panel.scrollTop;
}
panel.addEventListener("scroll", trackCandidateScroll);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While following, a resize moves the end without a scroll event or a new turn. A shorter window, or rows rewrapping when it narrows, grows the range, but scrollTop stays where follow left it (anchoring is off), so the newest lines sit below the panel until the next turn renders. Keep up with the box while following:

  // A resize moves the end and fires no scroll event.
  const Observer = document.defaultView?.ResizeObserver;
  if (Observer)
    new Observer(() => {
      if (following && rows.length) follow();
    }).observe(panel);

Opening the tab also resizes the panel from nothing, so once this lands reveal may be redundant. Keep it only if the browser test needs the follow to happen synchronously.

// browser moving the panel then is not the candidate scrolling.
const across = width > 1024 ? 1000 : 1100;
await page.setViewportSize({ width: across, height });
await addTurns(page, 2);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds two turns before checking, and a new turn re-follows on its own, so "a resize keeps the follow" passes whether or not a resize ever moves the panel off its end. Check atEnd straight after setViewportSize, after the two frames scrollPanel already waits, and only then add turns. Going 1280 to 1000 is the direction that rewraps rows taller and exposes it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Transcript tab does not scroll to new turns

3 participants