✨ feat(tab-groups): add tab groups with color-coded inline grouping#594
✨ feat(tab-groups): add tab groups with color-coded inline grouping#594kud wants to merge 2 commits into
Conversation
|
I get some compiler errors: The idea of tab groups in general is a good and thank you for taking this on! A few comments:
|
|
🔧 All addressed in 19ffef9:
|
|
I still get compiler errors and warnings. |
|
@gnachman Oh yeah sorry, it's still in draft! |
|
Ah, this is just what I was looking for. Hopefully this comes to fruition! |
|
@abrambailey Working on it, I've just got a hard time to build the app on my different computers. I'm working on the DX for now to help the |
|
@kud , |
|
@namp10010 Yes :] |
|
@kud , I created a small PR to your branch to fix a compile issue on my machine (M1 Pro). it compiles and runs now. hope that helps |
|
Thank you! @namp10010 |
Groups tabs in the tab bar under a named, color-coded header. Users can create, rename, collapse/expand, reorder, and persist groups across restarts. - Right-click any tab → "Add to New Group…" opens a sheet with name field and 9 color swatches plus an eyedropper for custom colors - Group header tab renders with color dot, chevron, and member count - Single click toggles collapse/expand; double click opens the edit sheet (rename + recolor in one place) - Member tabs render a left-edge color accent bar matching the group color - Dragging a group header auto-collapses it first to prevent self-nesting - "Move to Group" / "Remove from Group" in the context menu - Groups and collapse state persist via window arrangements - Pre-ship bug fixes: NSColorPanel dangling-target crash, stash integrity on removeTab, multi-group collapse completions, selectNearestTabOutside bounds, timer invalidation in dealloc, retain cycle in deleteTabGroup, and intra-group reorder no longer ejects tabs
gnachman
left a comment
There was a problem hiding this comment.
Missing Tab Style and Orientation Coverage
Tahoe style is completely unhandled. PSMTahoeTabStyle.swift has its own independent drawTabCell(_:highlightAmount:) and desiredWidth(ofTabCell:). The PR only modifies PSMYosemiteTabStyle.m. On Tahoe:
- Group headers render as normal tabs (with close button, interior text, etc.) instead of the colored pill
closeButtonRect(forTabCell:)doesn't returnNSZeroRectfor group headersdesiredWidth(ofTabCell:)doesn't account for group header sizing- Member tabs get no accent bar
The Yosemite-derived styles (Minimal, Dark, DarkHighContrast, LightHighContrast) all inherit drawTabCell:highlightAmount: and desiredWidthOfTabCell: without overriding them, so those are covered.
Vertical orientation is not handled. The group decoration methods assume horizontal layout:
drawGroupMemberSidebar:draws a full-width horizontal strip at the cell's bottom edge. In vertical orientation a left-edge vertical strip would make more sense.drawGroupHeaderDecorations:uses margins and text centering tuned for horizontal cells (short and wide). Vertical tab cells are tall and narrow — the pill and text will likely look wrong.- The group header width calculation in
desiredWidthOfTabCell:is only meaningful for horizontal layout; vertical tabs use height.
Light mode has hardcoded dark-background assumptions. The default neon style hardcodes [NSColor whiteColor] for group header text. The active-state border (colorWithWhite:1.0 alpha:0.4) and hover overlay (colorWithWhite:0.0 alpha:0.25) also don't adapt. The flat style does luminance-adaptive text, which is good — the neon path should do the same. The PSMGroupPillButton multi-select pill has the same problem (white text/borders hardcoded).
Memory Management (MRC)
PSMTabBarControl.m is MRC ([super dealloc], explicit retain/release throughout). The new code has leaks:
- Missing releases in
-dealloc:_fadingInCells,_collapsingCells,_collapseCompletions,_multiSelectedTabViewItems, and_groupSelectionButtonare allocated but never released. PSMGroupPillButton -updateTrackingAreas: The old_trackingAreais removed from the view but not released before reassignment.PSMGroupPillButtonhas no-dealloc:_trackingAreais never released on teardown.-groupGradientForColor:leaks:[[NSGradient alloc] initWithColors:...]is returned without autorelease.
Architectural Fragility
The PR adds 16+ isKindOfClass:[PTYTab class] guards scattered across PseudoTerminal.m (in windowWillResize:, tabViewDidChangeNumberOfTabViewItems:, fitTabsToWindow, allSessions, refreshTerminal:, tabs, sizeOfLargestTabWithExclusion:, minWidth, buildSessionSubmenu:, and every delegate method). Every existing and future code path that iterates tabViewItems must remember this check or it will crash on group header items.
A centralizing approach would reduce risk — e.g., a -[PseudoTerminal realTabViewItems] filtered accessor, or a category on NSTabViewItem that provides -isGroupHeader.
NSColorPanel Dangling Target
iTermCreateTabGroupViewController sets itself as the shared NSColorPanel's target/action but only cleans up via dismissColorPanel() called from button actions. If the sheet is dismissed externally (parent window closes, escape key), deinit doesn't clean up and the color panel retains a dangling target. Should add:
deinit {
dismissColorPanel()
}Naming / Rendering Inconsistencies
drawGroupMemberSidebar:draws a horizontal top/bottom strip, not a sidebar. The PR description says "left-edge accent bar." Either the rendering or the name should change.updateAnimatedcalls[self update:NO]— theNOargument means no animation. The name suggests the opposite.
Dead Code
NSCodingoniTermTabGroup:encode(with:)andinit?(coder:)are implemented, but arrangements use thetoDictionary/init?(dictionary:)path. The NSCoding conformance appears unused._cellSlideOffsetproperty onPSMTabBarCell: declared, synthesized, initialized, never read.
|
Hey all. What's needed to help move this PR forward? I've been looking for exactly this feature and am encouraged to see so much progress already made on it. |
|
Hi, and thanks for putting this together. There's a lot of good work here — the sheet UI, the color picker with the eyedropper, the gradient drawing, and the overall feature scope are all well-conceived. Before either of us invests more time in revisions, I'd like to step back and talk about the underlying design, because I think we need to change the foundation before the detail-level feedback is worth addressing. I pulled the branch down and tried it, and hit a bunch of issues:
Aside from that, there is a real architectural issue: the group header is a real NSTabViewItem inserted into NSTabView.tabViewItems, with identifier = iTermTabGroup. That creates a bunch of issues:
It would be simpler to keep NSTabView.tabViewItems the same, with only PTYTabs. Group headers can be synthetic cells rendered by PSMTabBarControl, interleaved between real tab cells based on a model the tab bar queries through a delegate callback along the lines of -orderedGroupsForTabBarControl: and -groupForTab:. iTermTabGroup holds [uniqueId] (no NSTabViewItem, which can create a retain cycle). The expanded/collapsed state of a group is reflected by the cells that PSMTabBarControl sees internally, while NSTabBarControl does not need to know about them. Under that model, a lot complexity disappears: the isKindOfClass: guards, stashedTabViewItems, enforceContiguity, reconcileGroupsAfterReorder, moveGroupAfterLastGroup, autoManageGroupCollapseForTab, the groupCollapseManaging reentrancy guard, the animation state machine with markNextInsertionsAsAnimated:, and the expand-encode-recollapse in A few things carry forward pretty much as-is: iTermCreateTabGroupViewController, the gradient drawing in drawGroupHeaderDecorations:, the sidebar drawing for members, the menu wiring, and the basic shape of iTermTabGroup (name / color / memberTabIDs / isCollapsed). Two smaller points I'd separate out, independent of the core:
How would you feel about taking another pass along these lines? Happy to discuss the delegate shape, the collapse-as-render-filter approach, or anything else before you start. I want to make sure we land something we're both comfortable maintaining — the feature itself is worth having, I just want the foundation to be right. I think it would be less painful to do as several smaller PRs, since this is a pretty big project. |

🎟️ Ticket
Ticket: N/A
📄 Description
Adds tab groups to the tab bar. Users can organise tabs into named, color-coded groups that collapse and expand inline — similar to how Firefox handles tab groups.
What was added:
Key files:
sources/iTermTabGroup.swiftsources/PseudoTerminal+TabGroups.swiftsources/iTermCreateTabGroupViewController.swiftThirdParty/PSMTabBarControl/source/PSMTabBarCell.h/.mThirdParty/PSMTabBarControl/source/PSMTabBarControl.h/.mThirdParty/PSMTabBarControl/source/PSMYosemiteTabStyle.msources/PseudoTerminal.h/.m✅ How to Validate
make run🛠️ Developer Checklist