From 978dd2c0232f53b42402dd302298b21a8a652235 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Mon, 30 Mar 2026 17:02:53 -0700 Subject: [PATCH] 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 --- CLAUDE.md | 8 ++++ Sources/cmuxApp.swift | 103 ++++++++++++++++++++++++++++++++++-------- vendor/bonsplit | 2 +- 3 files changed, 92 insertions(+), 21 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index fe961481..0c2d8156 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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`). diff --git a/Sources/cmuxApp.swift b/Sources/cmuxApp.swift index 8ba0d97d..8dffff53 100644 --- a/Sources/cmuxApp.swift +++ b/Sources/cmuxApp.swift @@ -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 { diff --git a/vendor/bonsplit b/vendor/bonsplit index 447ac42b..aff9f707 160000 --- a/vendor/bonsplit +++ b/vendor/bonsplit @@ -1 +1 @@ -Subproject commit 447ac42b45256bdf333659d2dbe955afcaa87f6b +Subproject commit aff9f7073125d412fed07bce1adab0e78f907b14