Prompt rail: its own layout box, unmoved by the composer, tool expands or the sidebar

Closed
TE
5d ago
+378-35

Leo (2026-11-01), on the transcript's left prompt rail: "the left side scroll rail needs to not really get affected by most other layout things it shouldn't really shift much at all"

Design spec: leo-workspace design/session-rail.md (updated alongside: "Its own layout box").

What changed

The rail's position and height now depend only on the transcript's view and the prompt count.

  • Vertical box (App.tsx VirtualTranscript railBox, ThreadMinimap.tsx): the rail's top and height read the scroller's height and the bottom stack's reach at rest (its smallest at that view height), instead of the stack's current reach and the followed view's lift. A composer growing a line, a tray or bar opening in the stack, or a tool expanding at the bottom of a followed chat leaves the rail where it is. A new view height (a window resize) starts over. The band (visibleTurns) still reads the real visible view.
  • Mask lane (conversation.css): the rail sits in the scroller, under its edge mask. A third mask layer keeps the gutter on the rail's side (--cv-inset, where rows never reach) in full ink, the way the scrollbar's lane already is, so the bottom ticks don't fade out under a grown composer or as a fade comes and goes.
  • Inset (model.ts railPlacement): the inset is the widest spacing step (--cmux-space-4) whenever the gutter has room for it, instead of 1% of the pane width. Toggling the sidebar (900 → 1150 px) used to move the rail 3 px; now it stays put. It shrinks toward --cmux-space-2 only once the gutter runs short, so at 800 px and below nothing changes.
  • Gallery agent-pane.thread-minimap-shift: the rail is the entry's anchor (play.ts anchorMovePx 0), through opening a chat, the composer growing four lines, a bottom tool expand and a scroll, at 520/900/1400 px. Once the gallery shift gate (#30250) lands, its box sampling scores the same anchor.

Test

bun test test/thread-rail-layout-box.test.tsx: fails on the base 976be72e5e0e (3 fail: the rail's top/height follow the stack, the inset changes with the pane width, no mask lane) and passes on the head (3 pass). The narrow and side suites take the new 16 px wide inset (they asserted 10 px at a 1040 px pane).

Observed / Not run

Ran: the rail suites under test/thread-minimap-*.test.ts*, test/thread-rail-*.test.ts and test/transcript-follow.test.ts (all pass), scripts/web-lint.sh, prepush-gate.py.
Not run yet: the gallery stage for the new entry (runs in this PR's gallery health), a DEV build check.

Risk

Normal. Contained to the rail's geometry and one mask layer on the scroller. If the composer at rest is taller than it was when the view height was last set (a tray open at load, then closed), the rail grows once into the freed room.

Changelog

Changed: The prompt rail no longer moves or resizes when the composer grows, a tool expands, or the sidebar is toggled.

šŸ¤– Generated with Claude Code

Agent session: agent session