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.
## 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
- **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()
Menu("Debug Windows") {
Button("Debug Window Controls") {
DebugWindowControlsWindowController.shared.show()
Button("Background Debug") {
BackgroundDebugWindowController.shared.show()
}
Button("Browser Import Hint Debug…") {
BrowserImportHintDebugWindowController.shared.show()
}
Button(
String(
localized: "debug.menu.browserProfilePopoverDebug",
@ -490,26 +488,21 @@ struct cmuxApp: App {
) {
BrowserProfilePopoverDebugWindowController.shared.show()
}
Button("Settings/About Titlebar Debug…") {
SettingsAboutTitlebarDebugWindowController.shared.show()
Button("Debug Window Controls…") {
DebugWindowControlsWindowController.shared.show()
}
Divider()
Button("Sidebar Debug…") {
SidebarDebugWindowController.shared.show()
}
Button("Background Debug…") {
BackgroundDebugWindowController.shared.show()
}
Button("Menu Bar Extra Debug…") {
MenuBarExtraDebugWindowController.shared.show()
}
Divider()
Button("Settings/About Titlebar Debug…") {
SettingsAboutTitlebarDebugWindowController.shared.show()
}
Button("Sidebar Debug…") {
SidebarDebugWindowController.shared.show()
}
Button("Split Button Layout Debug…") {
SplitButtonLayoutDebugWindowController.shared.show()
}
Button("Open All Debug Windows") {
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
private final class BackgroundDebugWindowController: NSWindowController, NSWindowDelegate {

2
vendor/bonsplit vendored

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