mirror of
https://github.com/coder/coder.git
synced 2026-10-31 11:02:50 +08:00
> 🤖 This PR was written by Coder Agents on behalf of Jake Howell. Fixes coder/coder#25692. Closes coder/coder#25692. Supersedes coder/coder#28250. ## Problem `AgentLogs` virtualizes rows with a **fixed** `itemSize` (20px), so react-window sets the total scroll height to `itemCount * 20`. Log entries that contain newlines render taller than 20px, so the estimate falls short: tall rows overlap the rows below them and the true bottom sits past where `scrollToItem(..., "end")` can reach. The last lines become unreachable. ## Fix Switch to `VariableSizeList` and measure each row with a `ResizeObserver`, feeding the real height back via `resetAfterIndex`. The total scroll height now tracks the real content, so the bottom is always reachable. `AgentRow` and `TaskPage` only change the ref type; their existing autoscroll now lands correctly. ## Before / After Dogfooded on a local instance with a workspace whose agent logs include multi-line entries (241 entries, every 30th a 6-line block, plus a final bottom-marker line). Same workspace, same scroll interaction, `main` vs this branch: <!-- linear:table-colwidths:266,266,266 --> | | Before (`main`, `FixedSizeList`) | After (this PR, `VariableSizeList`) | | -- | -- | -- | | **A multi-line entry** | Painted into a 20px slot, so it collides with the rows above and below (text on top of text). | Rendered at full height in its own space, neighbours no longer overlap. | | |  |  | | **Scrolled to the bottom** | Total height is short, so normal scrolling stops early: the final marker line is clipped and unreachable. | Height tracks real content: the final marker line is fully visible and reachable. | | |  |  | ## Validation The new `AgentLogs.test.tsx` renders rows measured taller (40px) than the estimate and asserts the scroll area is sized from measured heights. Run against the `main` implementation it fails, against this branch it passes: ``` # main (FixedSizeList): scroll area = 5 rows * 20px - Expected 200 + Received 100 → FAIL # this branch (VariableSizeList): scroll area = 5 rows * measured 40px ✓ AgentLogs.test.tsx (1 test) → PASS ``` Also green: `tsc`, `biome check`, and the AgentRow (25) + TaskPage (46) + AgentLogs (3) Storybook interaction tests. <details><summary>Dogfood details and decision log</summary> **Dogfood setup** (SHA `1228dcfe8d`, `./scripts/develop.sh`): template `lognl2` (a `coder_agent` on a `null_resource` so the agent authenticates against a real build), workspace `lognl-demo2`. Posted 241 log entries via `PATCH /api/v2/workspaceagents/me/logs`, every 30th a 6-line `\n` entry plus a bottom marker; confirmed the 8 multi-line entries persisted with embedded newlines. On the fixed build, at maximum scroll the DevTools console reported `scrollTop 5204`, `scrollHeight 5460`, `clientHeight 256`, `remaining 0`, marker clipped `0px`; height is self-consistent (`innerHeight 5428` + 16px top + 16px bottom padding = `5460`). **Decision log** * coder/coder#28250 treated this as an autoscroll problem. Wrong: the height calculation is the bug, so no scroll target can reach a bottom placed out of range. Reverted. * Only embedded newlines make a row exceed 20px; long lines do not wrap (they overflow horizontally). A fixed constant can't cover a variable line count, so measuring is the robust fix. * `resetAfterIndex` can't run from a row's first-mount measurement effect because the list ref is assigned after child rows commit; the lowest changed index is recorded and flushed from a parent layout effect. </details> Evidence images are hosted on the `devex-407-evidence` branch of the author's fork (`jakehwll/coder`) to keep this repo's diff clean.