Files
coder/site
Jake Howell d12cc2da21 fix(site): measure virtualized log row heights (#28251)
> 🤖 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. |
| | ![Before: multi-line entry overlapping neighbouring
rows](https://raw.githubusercontent.com/jakehwll/coder/devex-407-evidence/evidence/old-multiline.png)
| ![After: multi-line entry at full height, no
overlap](https://raw.githubusercontent.com/jakehwll/coder/devex-407-evidence/evidence/new-multiline.png)
|
| **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. |
| | ![Before: bottom marker clipped and
unreachable](https://raw.githubusercontent.com/jakehwll/coder/devex-407-evidence/evidence/old-bottom.png)
| ![After: bottom marker fully
visible](https://raw.githubusercontent.com/jakehwll/coder/devex-407-evidence/evidence/new-bottom.png)
|

## 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.
2026-08-24 23:25:57 +07:00
..
…
…
…
…