# refactor\(web\): timeline work group and live activity rows live in their own file · PR \#22675 · pingdotgg/t3code

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

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

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: refactor/messages-timeline-work-group-rows

Target branch: refactor/messages-timeline-work-entry-rows

Head commit: 46d59b6f6d681ffa224ea455145fb9e0ee7356f7

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

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

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

## Description

---

Part of the MessagesTimeline split. This layer moves the work group section, its toggle and header, the live work-entry, thinking and working rows, and the working timer into `TimelineWorkGroupRows.tsx`. The timer formatting helpers go to `TimelineWorkGroupRows.logic.ts`.

| File | Before | After |
|---|---:|---:|
| MessagesTimeline.tsx | 4,279 | 3,653 |
| TimelineWorkGroupRows.tsx | – | 640 |

### 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)

