Add hover background to split action buttons (#2271)

* Add hover background to split action buttons

Split buttons (terminal, browser, split right/down) now show a subtle
rounded-rect background highlight on hover. Matches standard macOS
toolbar button behavior.

* Prevent fade overlays from bleeding into bottom separator

* Render bottom separator above fade overlays to prevent bleed

* Exclude drop zone from scroll fade threshold

* Revert button hover, fix fade threshold with 32pt buffer

* Fix right fade threshold: subtract drop zone, 4pt tolerance

* Add leading padding to split buttons, fix fade threshold

* Rework tab bar: full-width scroll with floating split buttons

* Use ultraThinMaterial blur for floating split buttons

* Make split buttons group full height

* Full-height split button blur background

* Inset split buttons from bottom separator

* Fix blur overlapping separator

* Use matching tab bar bg for split buttons, clear separator

* Use regularMaterial blur for split buttons

* Try thickMaterial for split buttons

* Fade gradient + solid barFill for floating split buttons

* Use extended right fade as split buttons backdrop

* Add 5 debug styles for split button background

* Add Split Button Style debug window to Debug menu

* Clean up: no-bg floating buttons, alphabetical debug menu

- Split buttons float with no background (tabBarBackground covers the
  area, scroll padding prevents tabs from appearing behind buttons)
- Default splitButtonsWidth to 120 so first render has correct padding
- Remove split button style debug window and debug styles
- Alphabetize Debug Windows menu entries, remove dividers

* Revert to HStack sibling layout, add debug menu docs to CLAUDE.md

- Split buttons are HStack siblings of the ScrollView, not overlays.
  Single .background() on parent, no compositing mismatch.
- Alphabetize Debug Windows menu, remove dividers.
- Document Debug menu in CLAUDE.md.

* Add Split Button Layout debug window with 5 switchable approaches

* Fix fade gradient color to match tab bar background

* Add fade color debug window with 6 color options

* Use mask for scroll fades, fixes color mismatch

* Hide scroll fades in minimal mode unless hovering

* Remove split buttons from layout when hidden in minimal mode

* Always show fades, overlay buttons to prevent scroll jump

* Add hover-only mask fade behind split buttons

* Reduce button mask area to 90pt

* Animate button mask smoothly

* Fade entire button group together via opacity

* Add blur behind buttons with fade mask

* Use theme barBackground for button backdrop

* Blur + theme tint for button backdrop

* More tint (0.85), less blur

* Tint 0.2, clear bottom border

* Use terminal bg color, add scroll trailing padding for buttons

* Less blur, paneBackground at 0.75 opacity

* paneBackground at 0.9 opacity

* 0.97 opacity for button backdrop

* Test: fully opaque paneBackground

* Test: solid red backdrop

* Gradient + solid paneBackground backdrop, no mask

* Force opaque paneBackground for button backdrop

* Use barBackground for button backdrop

* Use terminal bg (paneBackground forced opaque)

* Pre-composite backdrop color for exact match

* Add 6 switchable backdrop styles in debug window

* Mask-based button area hiding, no backdrop color needed

---------

Co-authored-by: Lawrence Chen <lawrencecchen@users.noreply.github.com>
This commit is contained in:
Lawrence Chen 2026-03-30 17:02:53 -07:00 committed by GitHub
parent d015ace094
commit 978dd2c023
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 92 additions and 21 deletions

View file

@ -141,6 +141,14 @@ When adding a regression test for a bug fix, use a two-commit structure so CI pr
This makes it visible in the GitHub PR UI (Commits tab, check statuses) that the test genuinely fails without the fix. This makes it visible in the GitHub PR UI (Commits tab, check statuses) that the test genuinely fails without the fix.
## Debug menu
The app has a **Debug** menu in the macOS menu bar (only in DEBUG builds). Use it for visual iteration:
- **Debug > Debug Windows** contains panels for tuning layout, colors, and behavior. Entries are alphabetical with no dividers.
- To add a debug toggle or visual option: create an `NSWindowController` subclass with a `shared` singleton, add it to the "Debug Windows" menu in `Sources/cmuxApp.swift`, and add a SwiftUI view with `@AppStorage` bindings for live changes.
- When the user says "debug menu" or "debug window", they mean this menu, not `defaults write`.
## Pitfalls ## Pitfalls
- **Custom UTTypes** for drag-and-drop must be declared in `Resources/Info.plist` under `UTExportedTypeDeclarations` (e.g. `com.splittabbar.tabtransfer`, `com.cmux.sidebar-tab-reorder`). - **Custom UTTypes** for drag-and-drop must be declared in `Resources/Info.plist` under `UTExportedTypeDeclarations` (e.g. `com.splittabbar.tabtransfer`, `com.cmux.sidebar-tab-reorder`).

View file

@ -474,14 +474,12 @@ struct cmuxApp: App {
Divider() Divider()
Menu("Debug Windows") { Menu("Debug Windows") {
Button("Debug Window Controls") { Button("Background Debug") {
DebugWindowControlsWindowController.shared.show() BackgroundDebugWindowController.shared.show()
} }
Button("Browser Import Hint Debug…") { Button("Browser Import Hint Debug…") {
BrowserImportHintDebugWindowController.shared.show() BrowserImportHintDebugWindowController.shared.show()
} }
Button( Button(
String( String(
localized: "debug.menu.browserProfilePopoverDebug", localized: "debug.menu.browserProfilePopoverDebug",
@ -490,26 +488,21 @@ struct cmuxApp: App {
) { ) {
BrowserProfilePopoverDebugWindowController.shared.show() BrowserProfilePopoverDebugWindowController.shared.show()
} }
Button("Debug Window Controls…") {
Button("Settings/About Titlebar Debug…") { DebugWindowControlsWindowController.shared.show()
SettingsAboutTitlebarDebugWindowController.shared.show()
} }
Divider()
Button("Sidebar Debug…") {
SidebarDebugWindowController.shared.show()
}
Button("Background Debug…") {
BackgroundDebugWindowController.shared.show()
}
Button("Menu Bar Extra Debug…") { Button("Menu Bar Extra Debug…") {
MenuBarExtraDebugWindowController.shared.show() MenuBarExtraDebugWindowController.shared.show()
} }
Button("Settings/About Titlebar Debug…") {
Divider() SettingsAboutTitlebarDebugWindowController.shared.show()
}
Button("Sidebar Debug…") {
SidebarDebugWindowController.shared.show()
}
Button("Split Button Layout Debug…") {
SplitButtonLayoutDebugWindowController.shared.show()
}
Button("Open All Debug Windows") { Button("Open All Debug Windows") {
openAllDebugWindows() openAllDebugWindows()
} }
@ -3337,6 +3330,76 @@ private struct MenuBarExtraDebugView: View {
} }
} }
// MARK: - Split Button Layout Debug Window
private final class SplitButtonLayoutDebugWindowController: NSWindowController, NSWindowDelegate {
static let shared = SplitButtonLayoutDebugWindowController()
private init() {
let window = NSPanel(
contentRect: NSRect(x: 0, y: 0, width: 320, height: 240),
styleMask: [.titled, .closable, .utilityWindow],
backing: .buffered,
defer: false
)
window.title = "Split Button Layout"
window.titleVisibility = .visible
window.titlebarAppearsTransparent = false
window.isMovableByWindowBackground = true
window.isReleasedWhenClosed = false
window.identifier = NSUserInterfaceItemIdentifier("cmux.splitButtonLayoutDebug")
window.center()
window.contentView = NSHostingView(rootView: SplitButtonLayoutDebugView())
AppDelegate.shared?.applyWindowDecorations(to: window)
super.init(window: window)
window.delegate = self
}
@available(*, unavailable)
required init?(coder: NSCoder) { fatalError() }
func show() {
window?.center()
window?.makeKeyAndOrderFront(nil)
}
}
private struct SplitButtonLayoutDebugView: View {
@AppStorage("debugFadeColorStyle") private var backdropStyle = 0
private let options: [(Int, String)] = [
(0, "Pre-composited paneBackground"),
(1, "Raw paneBackground (opaque)"),
(2, "barBackground (tab chrome)"),
(3, "windowBackgroundColor"),
(4, "controlBackgroundColor"),
(5, "Pre-composited barBackground"),
]
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text("Button Backdrop Color")
.font(.headline)
ForEach(options, id: \.0) { id, label in
HStack {
Image(systemName: backdropStyle == id ? "checkmark.circle.fill" : "circle")
.foregroundColor(backdropStyle == id ? .accentColor : .secondary)
Text(label)
}
.contentShape(Rectangle())
.onTapGesture { backdropStyle = id }
}
Text("Changes apply live.")
.font(.caption)
.foregroundColor(.secondary)
}
.padding(16)
.frame(maxWidth: .infinity, alignment: .topLeading)
}
}
// MARK: - Background Debug Window // MARK: - Background Debug Window
private final class BackgroundDebugWindowController: NSWindowController, NSWindowDelegate { private final class BackgroundDebugWindowController: NSWindowController, NSWindowDelegate {

2
vendor/bonsplit vendored

@ -1 +1 @@
Subproject commit 447ac42b45256bdf333659d2dbe955afcaa87f6b Subproject commit aff9f7073125d412fed07bce1adab0e78f907b14