# refactor\(web\): ChatView's timeline follow and anchoring lives in useTimelineFollow · PR \#22701 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/22701)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: refactor/chatview-timeline-follow

Target branch: refactor/chatview-right-panel

Head commit: c61cf64dae5de0bba59018c60d73e15b1061b846

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/22701)

[Commits](https://git.cafe/pingdotgg/t3code/pull/22701/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/22701/diffs?format=markdown)

## Description

---

The timeline's follow-the-end and new-turn anchoring logic took about 550 lines in the middle of ChatView: the scroll-to-end pill state, about a dozen refs, the scroll, wheel, touch and keyboard listeners, page-scroll keys, and the anchor callbacks. Both send paths also repeated the same nine statements to arm the anchor.

`chat/useTimelineFollow.ts` now holds all of it. The send paths call `anchorNewTurn(threadKey, messageId)` instead. The anchor state itself stays in ChatView, because the timeline entries are derived from it.

The behavior is the same. Lint needed `legendListRef` and `setTimelineAnchor` added to effect deps; both are stable, so the effects run just as often as before.

| | previous layer | this layer |
|---|---|---|
| ChatView.tsx lines | 8,477 | 7,927 |

Renders were counted with the `rc-init.js` fiber counter on a dev server seeded from real data (median of 3 runs; base [6a6c84f](https://github.com/pingdotgg/t3code/commit/6a6c84f990) vs the stack top before its rebase onto current main):

| Interaction | main: commits / renders (ChatView) | stack top: commits / renders (ChatView) |
|---|---|---|
| Type 20 chars in the composer | 22 / 678 (1) | 22 / 678 (1) |
| Switch threads | 14 / 779 (5) | 14 / 777 (5) |
| Open + close the terminal drawer | 15 / 494 (3) | 15 / 497 (3) |
| Open + close the right panel | 21 / 723 (4) | 21 / 726 (4) |
| Drag the right panel's resize handle | 13 / 139 (1) | 1 / 1 (0) |

The +3 renders are the new `ChatTerminalDrawers` and `ChatRightPanel` wrappers. The drag row is the [right panel layer](https://github.com/pingdotgg/t3code/pull/22700): the drag's final width commit used to re-render ChatView and the whole chat column, and now re-renders only `ChatRightPanel`.

Tests: `vp test run src/reactCompilerCoverage.test.ts` for ChatView and every new file (0 bailouts), `tsc-rs --noEmit` for apps/web, and oxlint on the changed files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

