# refactor\(web\): the timeline minimap lives in its own file · PR \#22662 · pingdotgg/t3code

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

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

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: refactor/messages-timeline-minimap

Target branch: main

Head commit: 60e318155ef80a03a4d28cb4857a994e755d829f

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

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

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

## Description

---

MessagesTimeline.tsx is ~5,800 lines. It holds the list, scroll machinery and every row renderer, which makes it hard to read and review. This stack splits it into focused files without changing behavior.

This layer moves the timeline minimap (`TimelineMinimap`, its navigation button and the pointer-release helper) into `TimelineMinimap.tsx`.

| File | Before | After |
|---|---:|---:|
| MessagesTimeline.tsx | 5,772 | 5,441 |
| TimelineMinimap.tsx | – | 335 |

### Measurements

Dev app on a seeded copy of real data, median of 3, React renders counted by the commit-root counter (only fibers React actually rendered). "Before" is main at 6a6c84f990; "after" is the top of this stack. Nested update peak comes from a probe in react-dom's `getRootForUpdatedFiber`.

| Interaction | Before: commits / renders / timeline renders / nested peak | After |
|---|---|---|
| Scroll an 8.5k-item thread up 40 × 150px | 43 / 1,656 / 70 / 0 | 44 / 1,670 / 70 / 0 |
| Load earlier turns, then scroll up 60 × 300px | 107 / 5,316 / 356 / 0 | 104 / 5,275 / 356 / 0 |
| Agent streams a 20-line reply (561-item thread) | 63 / 3,092 / 164 / 1 | 55 / 2,728 / 139 / 0 |

The extractions keep every component, context, and hook boundary unchanged, so render counts match within run-to-run noise. Streaming numbers vary with reply length. The win is readability: each row family is now a file you can read on its own, and the React Compiler caches each one independently. The legend-list wiring is unchanged. That covers `getItemType` with its length buckets (#21269), `TimelineRowCtx` inside `renderItem` with `extraData` carrying `sharedState` (#21123), and scroll restore. No update loops appeared while loading earlier turns.

### Tests

- `vp test run` MessagesTimeline logic, active-tools, and browser tests, plus `timelineScrollAnchoring`
- `reactCompilerCoverage` for every touched and new file (0 bailouts)
- `tsc-rs --noEmit` for apps/web, `vp lint` on changed files

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

