cmux-next sidebar: hover follows the pointer, row pills stop short of the edge, the row card stands off its row

Merged
TE
16d ago
+215-76

Takes back #22294: Leo 2026-10-20 asked that the hover follow the pointer at once, one row at a time; #22294's rule kept the old row hovered on the next row's fill, which felt trapped. The corner and gap rules from rest stay.

Leo, 2026-10-20: moving the pointer between sidebar workspace rows felt trapped.

Was

  • The old row's hover stayed lit while the pointer was already on the next row's fill, especially at its right end where the × sits.
  • The × and ⋯ buttons faded out.
  • Row pills ran to the sidebar's right edge.
  • A row's hover card opened against the row's top-right corner, so a pointer moving down to the next row ran into the card and kept it.

Now

  • Hover follows the pointer. Once a row is hovered, the pointer anywhere on another row's fill hands it the hover at once. Only the gap between rows keeps the previous row lit, so sweeps don't flicker (#20315). From rest, a row's rounded corners still don't light it. This replaces the 10-19 rule that held the old row until the pointer reached the next row's inner pill.
  • Hover-out is instant. The × and ⋯ buttons clear with the fill. The fill already used hoverOut, which is 0 by default.
  • Rows stop short of the edge. Every list row ends SidebarStyle.rowTrailingTrim (Metrics.space4) short of the right inset, Chrome-style. Headers get the same trim, so the trailing column stays aligned.
  • The hover card stands off its row. Sidebar cards (.besideRow, .beside) sit HoverCardEscapeGeometry.rowGap (10 pt) off their row. The strip between row and card keeps the card only while the pointer is heading into it (safe triangle), so moving down past the row lets it go. Tab cards still touch their tab (Lawrence 2026-10-19).

Tests

  • Commit 1 adds failing tests:
    • SidebarRowHoverFollowTests (hover follows the pointer; rows end short of the edge)
    • SidebarHoverCardEscapeTests.theCardStandsOffItsRow
    • SidebarHoverCardEscapeTests.movingDownPastTheRowsRightEndDismissesTheCard
  • Commit 2 is the fix. It also updates the older tests that encoded the previous behavior: SidebarRowHoverInsetTests, WorkspaceRowHoverMotionTests, HoverCardEscapeGeometryTests, HoverCardFlushTests.

Observed / Not run

Not built or run locally: Mac builds run on CI only. Local git on the build Mac was stuck in disk wait, so both commits were made through the GitHub API, and the local pre-push gate didn't run. CI is the first compile.

Changelog

Fixed: sidebar hover follows the pointer between rows, row highlights stop short of the edge, and a row's hover card no longer catches the pointer.

Agent session: cosmic-gray

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes sidebar hover in cmux-next so a pointer moving between workspace rows hands the hover to the row under it, instead of leaving the previous row lit and trapping the pointer.

  • Hover follows the pointer: once a row is hovered, any other row whose fill the pointer is on takes the hover at once; only the gap between rows keeps the previous row lit, so sweeps down or up the list don't flicker.
  • The × and ⋯ hover buttons clear at once instead of fading out, so they never trail behind the pointer.
  • Row pills end Metrics.space4 short of the sidebar's right edge, Chrome-style; headers get the same trim.
  • Sidebar hover cards now stand 10 pt off their row. The gap keeps the card only while the pointer is heading into it, so a pointer moving down past the row dismisses the card instead of running into it.