fix(ui): keep the prompt rail highlight inside the rail when the rail resizes
#2,324 opened on Aug 6, 2026
Repository metrics
- Stars
- (1 star)
- PR merge metrics
- (PR metrics pending)
Description
What happened
Once the prompt anchor rail is past its cap it becomes a scroller of its own, and PromptAnchorRail keeps the active tick inside that scroller by nudging rail.scrollTop in an effect (packages/ui/src/prompt-anchor-rail.tsx:182). That effect depends on [activeTurnId] only, and does one-shot arithmetic against the railBox/tickBox it measures at that moment.
The rail's own viewport, however, is driven by --maka-prompt-rail-scrollport and --maka-prompt-rail-dock, which a separate ResizeObserver effect (packages/ui/src/prompt-anchor-rail.tsx:148) publishes asynchronously. Any geometry change that lands after activeTurnId settles leaves the correction stale: the active tick — and the indicator anchored to it — is stranded outside the rail's visible box and never comes back, because nothing re-runs the correction until the active prompt changes again.
Observed in a live window: scroll a long transcript to the end, then shrink the window. rail.clientHeight goes 606 → 286, rail.scrollTop stays at 384, and the highlight is outside the rail's box — still there 2s later.
BEFORE {"railScrollTop":384,"railClientHeight":606,"centreDrift":0,"indicatorInsideRail":true}
AFTER SHRINK {"railScrollTop":384,"railClientHeight":286,"centreDrift":0,"indicatorInsideRail":false}
LATER (+2s) {"railScrollTop":384,"railClientHeight":286,"centreDrift":0,"indicatorInsideRail":false}
centreDrift: 0 throughout: the anchor tracking itself is correct. The tick and its highlight are perfectly together — just together in the wrong place.
How to reproduce
- Open a conversation long enough that the prompt rail overflows and scrolls internally (~60+ prompts).
- Scroll the transcript to the very end, so the last tick is active and the rail has scrolled itself to reach it.
- Shrink the window height (or otherwise change the composer dock's height, which feeds the rail's
max-height). - The highlight is now outside the rail's visible box and stays there. Scrolling back to another prompt and returning fixes it, because that changes
activeTurnIdand re-runs the correction.
Where it also shows up: a CI flake
The same defect makes apps/desktop/e2e/prompt-rail.spec.ts:401 (the highlight stays on the active tick while the rail scrolls itself) intermittently fail on CI — run 31082109494, e2e_shard 2/2:
Error: {"railScrollTop":282,"centreDrift":0,"indicatorInsideRail":false}
expect(reading.indicatorInsideRail).toBe(true)
Same signature as above: the correction ran (railScrollTop > 0) against geometry that had not finished settling, and nothing re-ran it. The test's fixed await page.waitForTimeout(600) at line 405 is what decides whether the read lands before or after the last geometry change — it is the exposure, not the cause. Reading the same page at 0/100/300/600/1500/3000ms locally gives six identical results, so waiting longer alone does not make the failing state converge.
Suggested fix
- Product: make the correction follow the geometry rather than only
activeTurnId— e.g. re-run the same arithmetic from aResizeObserveron the rail (and its content), or include the measured safe area in the effect's dependencies. Keep the deliberatescrollToparithmetic rather thanscrollIntoView; the comment atpackages/ui/src/prompt-anchor-rail.tsx:178explains why. - Test: replace the fixed
waitForTimeout(600)with a condition-based wait (expect.pollover the three readings). Do not relaxcentreDrift <= 1, and do not just raise the timeout. - Coverage: an e2e assertion that the highlight is still inside the rail after a window resize would lock the product fix.
Environment
- Maka commit:
f7e04f9da - OS: macOS (Darwin 25.5.0); the CI failure is Linux
- Surface: Desktop