Skip to content

fix(menubar): make provider strip reachable and mouse-wheel scrollable#334

Merged
iamtoruk merged 1 commit into
getagentseal:mainfrom
vaibhavarora14:fix/provider-strip-scroll
May 16, 2026
Merged

fix(menubar): make provider strip reachable and mouse-wheel scrollable#334
iamtoruk merged 1 commit into
getagentseal:mainfrom
vaibhavarora14:fix/provider-strip-scroll

Conversation

@vaibhavarora14
Copy link
Copy Markdown
Contributor

@vaibhavarora14 vaibhavarora14 commented May 15, 2026

Summary

  • Add overflow-aware left/right chevron nav buttons to AgentTabStrip so off-screen providers are reachable when chips don't all fit in the popover.
  • Translate vertical-only mouse-wheel scroll events into horizontal deltas while the cursor is over the strip so standard mice can scroll it.

Why

The strip already lays out provider chips inside a horizontal ScrollView, which on macOS:

  1. Does not scroll from click-drag, so when chips overflow the viewport at the default 360pt popover width (~7+ providers), the off-screen chips are completely unreachable.
  2. Ignores vertical-only NSEvents. Standard mouse wheels emit hasPreciseScrollingDeltas == false with a non-zero deltaY and zero deltaX, so a horizontal ScrollView silently drops the event. Trackpads, which produce precise deltas with native horizontal components, already worked.

Either bug alone is annoying; together they make some providers unselectable on a mouse-only setup.

Implementation notes

  • Wrap the strip in ScrollViewReader. Add left/right chevron buttons that programmatically scroll to the next/previous visible provider via proxy.scrollTo(target.id, anchor: .center) and switch the selected provider via the existing store.switchTo(provider:). Buttons are only rendered when stripContentWidth > stripViewportWidth - 30, and they disable at the strip's ends.
  • Install an NSEvent.addLocalMonitorForEvents(matching: .scrollWheel) in .onAppear (removed in .onDisappear). Track strip hover via .onHover into a @MainActor singleton AgentTabStripScrollState so the monitor closure can read the latest hover state without capturing stale SwiftUI @State.
  • In the monitor, gate the rewrite on:
    • cursor is currently over the strip
    • !event.hasPreciseScrollingDeltas (mouse wheel, not trackpad)
    • |deltaX| < 0.001 and |deltaY| > 0 (vertical-only delta)
      When all hold, copy the CGEvent and transpose scrollWheelEventDeltaAxis1 / PointDeltaAxis1 / FixedPtDeltaAxis1 onto axis 2, then return the rewritten event so the underlying NSScrollView receives a real horizontal delta. All other events (trackpad, vertical scroll outside the strip, etc.) are passed through untouched, so vertical scrolling of the main popover content is unaffected.

Test plan

  • Run the menubar app with 7+ detected providers at the default popover width. The provider strip shows left/right chevrons.
  • Click the chevrons. Hidden providers become visible and selectable; chevrons disable at the strip ends.
  • With a standard mouse wheel, hover the strip and scroll. The strip scrolls horizontally.
  • With a trackpad, two-finger horizontal swipe over the strip still scrolls it.
  • With a standard mouse wheel, hover the main popover content (outside the strip) and scroll. Vertical scrolling of the main content still works as before.
  • Open the popover on a configuration where providers fit without overflow. Chevrons do not appear.

Fixes #333. Related to #332.

Made with Cursor

Two related bugs in the macOS menubar `AgentTabStrip`:

1. With more detected providers than fit at the default 360pt popover
   width (~7+), the off-screen provider chips were unreachable. SwiftUI's
   horizontal `ScrollView` does not scroll from click-drag, and there
   was no other affordance to reveal the hidden tabs.

2. Independent mouse wheels could not scroll the horizontal strip.
   Standard wheels emit only vertical `deltaY` with
   `hasPreciseScrollingDeltas == false`, and a horizontal SwiftUI
   `ScrollView` ignores vertical-only deltas. Trackpads (which emit
   horizontal deltas natively) already worked.

Fix:

- Wrap the strip in `ScrollViewReader` and add overflow-aware
  left/right chevron buttons that programmatically scroll to the
  next/previous visible provider via `proxy.scrollTo(_, anchor: .center)`.
  Buttons only appear when `stripContentWidth > stripViewportWidth - 30`
  and disable at the ends.
- Install an `NSEvent.addLocalMonitorForEvents(matching: .scrollWheel)`
  in `.onAppear` (removed in `.onDisappear`). When the cursor is over
  the strip and the event is non-precise (`!hasPreciseScrollingDeltas`)
  with `deltaX≈0` and `deltaY≠0`, copy the `CGEvent` and transpose
  `scrollWheelEventDeltaAxis1` / `PointDeltaAxis1` / `FixedPtDeltaAxis1`
  onto axis 2 so the underlying NSScrollView receives a real horizontal
  delta.
- Track strip hover via a `@MainActor` singleton
  `AgentTabStripScrollState` so the local-monitor closure can read the
  latest hover status without capturing stale SwiftUI state.

Trackpad events are passed through untouched, so vertical scrolling
elsewhere in the popover is unaffected.

Co-authored-by: Cursor <cursoragent@cursor.com>
@iamtoruk iamtoruk merged commit 95884c0 into getagentseal:main May 16, 2026
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.

Provider strip in macOS menubar can't be scrolled with a mouse wheel and hides overflowing providers

2 participants