Skip to content

feat(tabs): replace segmented control with custom profile tabs - #19

Merged
darvid merged 2 commits into
mainfrom
devin/1791207172-profile-tab-strip
Oct 5, 2026
Merged

darvid merged 2 commits into
mainfrom
devin/1791207172-profile-tab-strip

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Replaces the stock NSSegmentedControl in TabStripView with a custom row of profile tabs (option B from the tab-strip investigation). The public TabStripView contract (update(windows:selectedWindowID:), onSelectWindow, onReorderWindow, onMove, …) is unchanged; two callbacks are added.

Built and tested only via macOS CI (the dev box is Linux); not yet exercised interactively on a Mac.

Layout, left to right:

[browser icon menu] [tab][tab][tab]…[ghost profile tabs]   [+N ▾] [+]
  • TabButton (custom-drawn NSView): selected tab = profile-color tint + ring + semibold label; hover/pressed fills; close × on hover (reserved space, so no relayout on hover); middle-click closes. Selection fires on mouse-down; a >4pt drag switches to reorder, where the tab follows the pointer (lifted) and neighbours animate into new slots (NSAnimationContext, 0.16s).
  • TabStripLayout (pure, unit-tested): clamps tabs to 96–180pt, water-fills shrink (widest shrink first), and once tabs no longer fit at 96pt moves the rest into an overflow +N menu, always keeping the selected tab visible. Leftover width is filled with dashed ghost tabs for picker-visible profiles that have no open window (click → open that profile).
  • Duplicate labels: TabStripLayout.labels(for:) → ["Personal", "Work", "Window 3", "Personal 2"].
  • Browser icon is now a menu button (Settings / Rescan / Quit — same items as the existing right-click menu, which still works on the strip and tabs).
  • + button pops a menu of ProfileChoice.pickerChoices for this browser.
  • Inactive dimming: ContainerController.update calls tabStrip.setBrowserActive(frontmost pid == selected pid); tints desaturate and labels fade.
  • Scroll over the strip cycles tabs (one step per trackpad gesture / wheel notch).
  • Accessibility: row is .tabGroup; window tabs are .radioButton with selected value, "Window i of n" help, and a "Close Window" custom action; ghosts are buttons labelled "Open …".

New plumbing:

tabStrip.onCloseWindow  -> AccessibilityController.close(window)   // presses kAXCloseButtonAttribute
tabStrip.onOpenProfile  -> BrowserLauncher.openWindow(browser:profile:)
//   chromium: --user-data-dir=… [--profile-directory=id] --new-window
//   firefox:  [-P name] -new-window about:newtab

BrowserLauncher.launch now shares a private run(browser:arguments:) helper; its arguments are unchanged.

Things worth checking on a Mac (none exercised yet):

  • Hover/close affordance in the non-activating panel while another app is frontmost (tracking areas use .activeAlways).
  • Firefox -new-window about:newtab from the command line, and Chromium --new-window against an already-running instance.
  • Drag-reorder feel with mixed tab widths.

Link to Devin session: https://app.devin.ai/sessions/3feca0145cfc45de90b3030ab28f7a89
Open in Devin Desktop: https://app.devin.ai/desktop/session/3feca0145cfc45de90b3030ab28f7a89?variant=devin
Requested by: @darvid

Co-Authored-By: David Gidwani <david.gidwani@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

Co-Authored-By: David Gidwani <david.gidwani@gmail.com>
@darvid
darvid merged commit 4dc1ba8 into main Oct 5, 2026
4 checks passed
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