Skip to content

fix(macos): shrink and scroll tile tabs without a system scroller - #254

Merged
akonwi merged 1 commit into
mainfrom
macos/tile-tab-overflow
Oct 22, 2026
Merged

akonwi merged 1 commit into
mainfrom
macos/tile-tab-overflow

Conversation

@akonwi

@akonwi akonwi commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Problem

When a tile had more tabs than fit:

  • With legacy scrollers ("Show scroll bars: Always", or "Automatically" with a mouse connected), a grey system scrollbar ran under its tabs. .scrollIndicators(.hidden) is only a hint on macOS: SwiftUI's HostingScrollView still got a 17 pt horizontal scroller and squeezed the 34 pt strip to 16 pt. .never removes it. I confirmed this by inspecting the AppKit view tree and capturing windows in both scroller modes.
  • In both modes, the selected tab was clipped, "+" scrolled out of view, nothing showed that tabs were hidden, and a vertical mouse wheel couldn't scroll the strip.

Change (option B from the Paper exploration, page "Tile tab overflow")

  • Shrinking: tabs keep their natural width while they fit. Then the widest shrink first, down to 88 pt, and the selected tab keeps at least 160 pt (TileTabWidths, laid out by TileTabRow).
  • Scrolling past that: no scroller, and fades on the edges that hide tabs. The selected tab scrolls into view clear of the fades when it's selected or added, when the strip changes size, and on appear. Scrolling by hand stops that until another tab is selected.
  • "+N" menu: styled like the session tabs' count. It lists the hidden panes in tab order with their icons or favicons. Choosing the selected tab after scrolling it away brings it back into view.
  • "+" button: follows the last tab while everything fits, and stays beside "+N" while the strip scrolls.
  • Split/zoom group: keeps its slot while hidden, so hovering doesn't reflow shrunk tabs.
  • Tooltips: tabs show their full title.
  • HorizontalWheelScrolling: turns a vertical mouse wheel into horizontal scrolling over the tile strips, the session top tabs, and the composer and annotation strips. It only acts when that strip is the view under the pointer, so panels over it keep their wheel. Trackpads pass through unchanged.
  • Other .hidden scroll indicators → .never: composer attachment strips, AnnotationStrip (which had no modifier at all), the mention panel, and the bounded diagnostics in the feedback view.
  • Shared code: the session tabs' overflow model, count label, and edge mask are now shared (TabStripOverflow<ID>).

Tests

TileTabStripTests covers:

  • the water-fill widths and the reveal offsets
  • which tabs count as hidden
  • the mouse-wheel conversion, and a panel over a strip keeping its wheel
  • rendered workspaces checking exact geometry:
    • tabs that fit, with "+" right after the last one
    • tabs shrunk to fill 465 pt, with the action slot held when focus moves
    • 10 tabs with a forced legacy scroller: no scroller, full 34 pt height, the viewport sized for the "+6" count, "+" pinned, the selected tab revealed on selection, wheel scrolling, and no snap-back after scrolling by hand

The scroller check fails if the strip reverts to .hidden, and the snap-back check fails without the follow guard.

Full macOS suite: 971 tests pass. SessionLauncherTests.renderLauncherForReview failed once on its focus-dependent check (#234) and passed on rerun.

A tile's tab strip used `.scrollIndicators(.hidden)`, which is only a hint
on macOS: with legacy scrollers ("Show scroll bars: Always", or a mouse
connected) AppKit still drew a 17 pt scroller under the tabs and squeezed
them into the remaining 16 pt. The strip also clipped the selected tab,
scrolled the add button away, and showed no sign of hidden tabs.

Tile tabs now share the strip: the widest shrink first, down to 88 pt,
while the selected tab keeps at least 160 pt. Past that, the strip
scrolls with no scroller, fades the edges that hide tabs, keeps the
selected tab in view (until the user scrolls by hand), and shows a "+N"
menu of the hidden panes with their icons. The add button follows the
last tab and stays beside the count while the tabs scroll. The split and
zoom actions keep their slot while hidden, so hovering doesn't reflow the
tabs, and tabs show their full title as a tooltip.

A vertical mouse wheel now scrolls the tile strips, the session top tabs,
and the composer and annotation strips across. Other strips and panels
that meant to hide their scrollers use `.never`. The session tabs'
overflow model, count, and edge mask are shared with tile strips.
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