Skip to content

CmuxNext: sidebar rows tile so hover moves row to row with no flicker - #20315

Merged
teamleaderleo merged 8 commits into
feat-cmux-nextfrom
op-next-theme/sidebar-row-hover-gap
Oct 13, 2026
Merged

teamleaderleo merged 8 commits into
feat-cmux-nextfrom
op-next-theme/sidebar-row-hover-gap

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Leo reported four sidebar workspace-row papercuts. This PR fixes all four:

  • Rows tile for hit testing. Rows sat rowSpacing (2 pt) apart, and that spacing hit nothing. Moving the pointer down the list dropped the hover in every gap, so it flickered row to row. SidebarLayout.row(at:) now splits the spacing between two consecutive rows, half to each, so their hit areas meet. The hover moves from one row to the next on the same pointer step, and the fills cross-fade (MotionFade.hover). The pill stays inset inside the row.
    • Clicks, the context menu and middle-click use the same hit test, so the 2 pt slivers now hit the nearer row.
    • Section gaps, a group's bottom padding and the drag drop gap are wider than the spacing and still hit nothing. Double-clicking empty space still makes a workspace there.
  • The hover card waits for a dwell. The workspace card used to show at once on every row the pointer passed. It now waits about 1 s, like a native tooltip, via the new tunable motion.hoverCard.dwell (default 1 s, with the other Motion hover tunables).
    • A sweep shows no card, and leaving a row cancels its pending card.
    • Everything else about the card is unchanged: once a card shows, moving to another row slides it over, its fade uses the existing tokens, and every row still gets a card.
  • One gutter. SidebarStyle.horizontalInset now sets four distances: the pill's inset from the list edges, the text's inset from the pill's leading edge, the trailing marks' inset from its trailing edge, and the band divider's inset. These match on selected and unselected rows.
    • Before, the text sat 8 pt in while the trailing marks sat 6 pt in. The title now starts at the inset, with label cells sitting back by their cell padding (SidebarStyle.labelCellPadding), and it runs to the trailing inset.
    • Section, folder and region header names start where row titles do.
    • The divider above the list is inset like the pills, so its ends line up with theirs. This changes the divider: it was full width.
  • Count badge. I kept the chip rather than switching to plain muted text. Next to the status glyph, a bare digit reads as part of the title or the glyph, and the fill marks it as a count.
    • It's now a quiet small-radius rect (Metrics.chipCornerRadius, not a capsule) on the theme's badge fill, with no hairline.
    • Its height matches the status glyph through one new CmuxNextDesign token, Metrics.statusMarkSize.
    • Its digits are tabular system digits instead of the monospaced face, optically centered by cap height on the pixel grid.

The row's X and options controls are unchanged. They show with the row's hover fill, as in Edge and Codex.

Overlap with other lanes:

Testing

I added three behavior suites in CmuxNextAppTests that drive a real SidebarListView or SidebarView in a window:

  • SidebarRowHoverGapTests:
    • A 0.25 pt probe finds no dead band between consecutive rows.
    • A 1 pt pointer sweep hovers exactly one row at every step, and at each change both fills animate.
  • SidebarHoverCardDwellTests:
    • A sweep across 5 rows at 1 pt every 2 ms, ending outside the sidebar, shows zero cards (checked again after 1.2 s).
    • A 1.2 s rest on one row shows exactly that row's card, and no card shows at 300 ms.
  • SidebarRowGutterTests:
    • On selected and unselected rows, the leading inset equals the trailing inset and equals the pill inset.
    • The pill's edges match the pinned section header's leading edge and the band divider's leading and trailing edges.
    • The badge height equals the status glyph's, and its digits are tabular and not in a fixed-pitch face.

Fleet runs, each cmux-ci run --class light --script scripts/ci/run-swift-testing-suites.sh --arg Packages/macOS/CmuxNext:

  • Red on 7b2a4804111c (--red): both hover suites fail.
    • Rows: a 2.25 pt dead band, and 0 rows hovered in the gaps.
    • Cards: the sweep showed 5 cards, and the dwell test showed one before 300 ms.
  • Red on aa4c0416d921 (--red): SidebarRowGutterTests fails.
    • Text 8 pt in vs marks 6 pt in.
    • Pill 6 to 234 vs divider 0 to 240.
    • Badge 14 pt vs glyph 10 pt, in a fixed-pitch font.
  • Green on 171d134c43d6: both hover suites pass.
  • Green on the head 26961ff1d96d: all three suites pass.

Every run also fails CmuxNextDaemonTests.DaemonCapabilityExportTests/exportIsFresh, including the test-only red commits. This PR doesn't touch it, so it's a base red.

Guards on the head (merge base 5f5eefbd72fc):

  • check-concurrency.sh, check-motion.sh, check-theme-scope.sh and check-system-blue.py pass.
  • crash_ratchet.py passes.
  • lint-ios-package-conventions.sh passes.
  • check-app-size.sh --base passes: CmuxNextApp is untouched at 113798 lines.
  • check-no-godfiles.sh --base passes, with notes only for entries that were already over budget on the base and that this change didn't grow. WorkspaceRowView.swift stays at 399 lines.

Localization: the only new strings are the Debug Settings tunable's title and help text, which are English-only like every other tunable. No user-facing strings changed.

Proof: captures don't gate this change. What's visible: the hover moves row to row with no off frame; no hover card appears during a sweep; row text and trailing marks sit the same distance in from the pill; and the count badge is a glyph-height quiet rect.

Changelog

Fixed: Moving the pointer down the sidebar no longer flickers the hover highlight between rows.

🤖 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 four sidebar workspace-row papercuts: hover no longer flickers between rows, the workspace hover card waits for a native-tooltip dwell, row content and marks share one gutter, and the count badge matches the status glyph.

Behavior changes

  • Rows' hit areas now tile: the spacing between two rows is split half to each, so hovering moves row to row on the same pointer step and clicks in the gaps hit the nearer row. Section gaps, group padding, and the drop gap still hit nothing.
  • The workspace hover card waits about 1 s (new tunable motion.hoverCard.dwell) before showing; sweeping the list shows no cards, and leaving a row cancels its pending card.

Visual changes

  • SidebarStyle.horizontalInset now sets the pill, title, trailing marks, and band divider insets to one gutter; the divider is now inset (it was full width), and a title without marks runs to the trailing inset.
  • The count badge is now a quiet small-radius rect as tall as the status glyph (Metrics.statusMarkSize), with tabular system digits instead of a taller monospaced chip.

Three new test suites cover the hit-area tiling, hover-card dwell, and gutter/badge metrics.

Written for commit 55f860c. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 6 commits October 13, 2026 15:10
…bead)

Two behavior tests on SidebarListView: consecutive workspace rows' hit
areas meet with no dead band, and a 1 pt pointer sweep down the list
hovers exactly one row at every step, the fills cross-fading as it moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The card waits for a native-tooltip dwell: a 1 pt / 2 ms sweep across
five rows shows none, and a 1.2 s rest on one row shows exactly its card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (no bead)

Hit testing gives the spacing between two rows to the rows on either side
(half each), so consecutive rows' hit areas meet and the pointer is never
between two rows: the hover moves from one row to the next in one step,
and the two fills cross-fade (MotionFade.hover). The pill stays inset in
the row. Section and group gaps and the drop gap still hit nothing.

The workspace hover card waits for a native-tooltip dwell
(motion.hoverCard.dwell, 1 s) instead of showing at once, so a sweep down
the list shows no card and leaving a row cancels its pending card.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bead)

Workspace rows, selected or not, keep their text and trailing marks the
same inset from the pill's edges as the pill is from the sidebar's; the
pill lines up with the section header and the band divider; the count
badge is as tall as the status glyph, with tabular digits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…adge (no bead)

SidebarStyle.horizontalInset now sets the pill's inset from the sidebar's
edges and the content's inset from the pill's, leading and trailing,
selected or not: the title's text starts at it (label cells sit back by
their cell padding) and the trailing marks end at it, so they no longer
crowd the right edge. Section and region header names start where row
titles do, and the band divider takes the same inset so its ends line up
with the pills.

The count badge stays a quiet small-radius rect on the theme's badge fill
(no hairline), now as tall as the status glyph through one CmuxNextDesign
token (Metrics.statusMarkSize), with tabular system digits optically
centered instead of a monospaced face.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The title draws at its frame's edge, so it ends at the shared inset like
the trailing marks do; a row without marks no longer stops its text 2 pt
short of the pill's trailing inset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: ad6d2cfb-eff5-48c0-ad53-e6f5b92ace29

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Not verified: macOS jobs did not run: compile and app tests were skipped on 55f860ccaa; ci-status is green without them.

CI passes on 55f860ccaa (run 38484425843 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

…well (no bead)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cursor

cursor Bot commented Oct 13, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@teamleaderleo
teamleaderleo merged commit 1625040 into feat-cmux-next Oct 13, 2026
93 of 94 checks passed
@teamleaderleo
teamleaderleo deleted the op-next-theme/sidebar-row-hover-gap branch October 13, 2026 23:49
teamleaderleo added a commit that referenced this pull request Oct 18, 2026
#21543 made hover entry immediate (fadesNextFill = !isHovered) and rows clear
with the hoverOut token, instant by default. SidebarRowHoverGapTests from
#20315 still expected both fills to fade, so it failed on feat-cmux-next. The
test now expects the new row's fill to show at once and the old row to animate
only when hoverOut has a duration.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 20, 2026
The gap between rows, a row's rounded corners and the margin beside it no
longer hand the hover (and the "..." and close buttons, and the hover card)
to a new row. The row hovered keeps it across the gap, so a sweep never
flickers (#20315). Clicks still split the gap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 20, 2026
…t the gap or corners (#22294)

* test(cmux-next): sidebar rows take hover only inside their rounded pill

Leo 2026-10-19: moving up-left out of a row, the gap and the rounded corner
of the row above took the hover and showed its controls in his way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cmux-next): sidebar rows take hover only inside their rounded pill

The gap between rows, a row's rounded corners and the margin beside it no
longer hand the hover (and the "..." and close buttons, and the hover card)
to a new row. The row hovered keeps it across the gap, so a sweep never
flickers (#20315). Clicks still split the gap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(next): PR autofix at 6f74de6

- Packages/macOS/CmuxNext/ci-target-graph.json
- schemas/defaults/cmux-next.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39358292621

* chore(next): PR autofix at b45db77

- Packages/macOS/CmuxNext/ci-target-graph.json

Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39361446273

* fix(cmux-next): a sidebar click acts on the hovered row; pill geometry tests (review of #22294)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant