Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
376d075
Add configurable split action buttons
lawrencecchen Apr 22, 2026
f9178cd
Merge remote-tracking branch 'origin/main' into feat-configurable-spl…
lawrencecchen Apr 22, 2026
b1d43c0
Support template SVG action icons
lawrencecchen Apr 22, 2026
d3bb771
Address configurable split button review feedback
lawrencecchen Apr 22, 2026
5585df3
Handle malformed SVG icon data
lawrencecchen Apr 22, 2026
9d56ab5
Cache split action button images
lawrencecchen Apr 22, 2026
44a49b0
Bound split action icon image cache
lawrencecchen Apr 22, 2026
e6c8f3e
Gate trailing new-tab action on split buttons
lawrencecchen Apr 22, 2026
bc29763
Guard duplicate split action buttons
lawrencecchen Apr 22, 2026
88360f9
Show pressed state for image split buttons
lawrencecchen Apr 22, 2026
59c1d04
Use brightness-only split button press state
lawrencecchen Apr 22, 2026
f29aeb1
Sync split button hover state on window focus changes
lawrencecchen Apr 23, 2026
fc54dcc
Support emoji scale for split buttons
lawrencecchen Apr 23, 2026
cecc39e
feat: gate tab shortcut hints by host focus
lawrencecchen Apr 25, 2026
427e48a
fix: standardize shortcut hint animation
lawrencecchen Apr 25, 2026
6cb1b73
Separate pane background from tab chrome
lawrencecchen Apr 26, 2026
11c7313
Preserve translucent split button chrome
lawrencecchen Apr 26, 2026
275f026
Skip split button backdrop for translucent chrome
lawrencecchen Apr 26, 2026
0b2be95
Restore translucent split button backdrop
lawrencecchen Apr 26, 2026
1d2db14
Use opaque split button backdrop
lawrencecchen Apr 26, 2026
b8a8e39
Expose split button backdrop styles
lawrencecchen Apr 27, 2026
85e1e38
Add split button backdrop effects
lawrencecchen Apr 27, 2026
36fba6e
Add configurable split button backdrop ramp
lawrencecchen Apr 27, 2026
9bc6b5c
Tune split button backdrop fade defaults
lawrencecchen Apr 27, 2026
fb2ebfa
Reduce split button backdrop intensity
lawrencecchen Apr 27, 2026
3bfa3de
Add split button content occlusion control
lawrencecchen Apr 27, 2026
3ac045a
Merge commit '427e48a' into feat-pane-background-color
lawrencecchen Apr 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ final class SplitViewController {
/// updateNSView is called to toggle isHidden on the AppKit containers.
var isInteractive: Bool = true

/// When false, pane tab shortcut hints stay hidden even if this pane is
/// selected. The host app owns this because keyboard focus can move outside
/// Bonsplit while the selected pane remains unchanged.
var tabShortcutHintsEnabled: Bool = true

/// Handler for file/URL drops from external apps (e.g. Finder).
/// Receives the dropped URLs and the pane ID where the drop occurred.
@ObservationIgnored var onFileDrop: ((_ urls: [URL], _ paneId: PaneID) -> Bool)?
Expand Down
85 changes: 78 additions & 7 deletions Sources/Bonsplit/Internal/Styling/TabBarColors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,33 @@ enum TabBarColors {
return NSColor(bonsplitHex: value)
}

private static func paneBackgroundColor(
for appearance: BonsplitConfiguration.Appearance
) -> NSColor? {
guard let value = appearance.chromeColors.paneBackgroundHex else {
return chromeBackgroundColor(for: appearance)
}
return NSColor(bonsplitHex: value)

@cubic-dev-ai cubic-dev-ai Bot Apr 26, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Invalid paneBackgroundHex values skip fallback to backgroundHex and can produce an unexpected pane color.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/Bonsplit/Internal/Styling/TabBarColors.swift, line 26:

<comment>Invalid `paneBackgroundHex` values skip fallback to `backgroundHex` and can produce an unexpected pane color.</comment>

<file context>
@@ -17,6 +17,15 @@ enum TabBarColors {
+        guard let value = appearance.chromeColors.paneBackgroundHex else {
+            return chromeBackgroundColor(for: appearance)
+        }
+        return NSColor(bonsplitHex: value)
+    }
+
</file context>
Suggested change
return NSColor(bonsplitHex: value)
return NSColor(bonsplitHex: value) ?? chromeBackgroundColor(for: appearance)
Fix with Cubic

}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private static func tabBarBackgroundColor(
for appearance: BonsplitConfiguration.Appearance
) -> NSColor? {
guard let value = appearance.chromeColors.tabBarBackgroundHex else {
return chromeBackgroundColor(for: appearance)
}
return NSColor(bonsplitHex: value)
}

private static func splitButtonBackdropColor(
for appearance: BonsplitConfiguration.Appearance
) -> NSColor? {
guard let value = appearance.chromeColors.splitButtonBackdropHex else {
return tabBarBackgroundColor(for: appearance)
}
return NSColor(bonsplitHex: value)
}

private static func chromeBorderColor(
for appearance: BonsplitConfiguration.Appearance
) -> NSColor? {
Expand All @@ -31,11 +58,29 @@ enum TabBarColors {
chromeBackgroundColor(for: appearance) ?? fallbackColor
}

private static func precompositedPaneBackground(
for appearance: BonsplitConfiguration.Appearance,
focused: Bool
) -> NSColor {
let chrome = nsColorPaneBackground(for: appearance)
let windowBackground = NSColor.windowBackgroundColor
guard let foreground = chrome.usingColorSpace(.sRGB),
let background = windowBackground.usingColorSpace(.sRGB) else {
return chrome.withAlphaComponent(1.0)
}
let alpha = focused ? foreground.alphaComponent : foreground.alphaComponent * 0.95
let oneMinusAlpha = 1.0 - alpha
let red = foreground.redComponent * alpha + background.redComponent * oneMinusAlpha
let green = foreground.greenComponent * alpha + background.greenComponent * oneMinusAlpha
let blue = foreground.blueComponent * alpha + background.blueComponent * oneMinusAlpha
return NSColor(red: red, green: green, blue: blue, alpha: 1.0)
}

private static func effectiveTextColor(
for appearance: BonsplitConfiguration.Appearance,
secondary: Bool
) -> NSColor {
guard let custom = chromeBackgroundColor(for: appearance) else {
guard let custom = tabBarBackgroundColor(for: appearance) else {
return secondary ? .secondaryLabelColor : .labelColor
}

Expand All @@ -49,11 +94,11 @@ enum TabBarColors {
}

static func paneBackground(for appearance: BonsplitConfiguration.Appearance) -> Color {
Color(nsColor: effectiveBackgroundColor(for: appearance, fallback: .textBackgroundColor))
Color(nsColor: paneBackgroundColor(for: appearance) ?? .textBackgroundColor)
}

static func nsColorPaneBackground(for appearance: BonsplitConfiguration.Appearance) -> NSColor {
effectiveBackgroundColor(for: appearance, fallback: .textBackgroundColor)
paneBackgroundColor(for: appearance) ?? .textBackgroundColor
}

// MARK: - Tab Bar Background
Expand All @@ -63,7 +108,33 @@ enum TabBarColors {
}

static func barBackground(for appearance: BonsplitConfiguration.Appearance) -> Color {
Color(nsColor: effectiveBackgroundColor(for: appearance, fallback: .windowBackgroundColor))
Color(nsColor: nsColorBarBackground(for: appearance))
}

static func nsColorBarBackground(for appearance: BonsplitConfiguration.Appearance) -> NSColor {
tabBarBackgroundColor(for: appearance)
?? effectiveBackgroundColor(for: appearance, fallback: .windowBackgroundColor)
}

static func nsColorChromeBackground(for appearance: BonsplitConfiguration.Appearance) -> NSColor {
effectiveBackgroundColor(for: appearance, fallback: .windowBackgroundColor)
}

static func nsColorSplitButtonBackdropSurface(for appearance: BonsplitConfiguration.Appearance) -> NSColor {
splitButtonBackdropColor(for: appearance) ?? nsColorBarBackground(for: appearance)
}

static func nsColorSplitButtonBackdrop(
for appearance: BonsplitConfiguration.Appearance,
focused: Bool = true
) -> NSColor {
precompositedPaneBackground(for: appearance, focused: focused)
}

static func shouldPaintSplitButtonBackdrop(for appearance: BonsplitConfiguration.Appearance) -> Bool {
splitButtonBackdropColor(for: appearance) != nil
|| tabBarBackgroundColor(for: appearance) != nil
|| chromeBackgroundColor(for: appearance) != nil
}

static var barMaterial: Material {
Expand All @@ -77,7 +148,7 @@ enum TabBarColors {
}

static func activeTabBackground(for appearance: BonsplitConfiguration.Appearance) -> Color {
guard let custom = chromeBackgroundColor(for: appearance) else {
guard let custom = tabBarBackgroundColor(for: appearance) else {
return activeTabBackground
}
let adjusted = custom.isBonsplitLightColor
Expand All @@ -91,7 +162,7 @@ enum TabBarColors {
}

static func hoveredTabBackground(for appearance: BonsplitConfiguration.Appearance) -> Color {
guard let custom = chromeBackgroundColor(for: appearance) else {
guard let custom = tabBarBackgroundColor(for: appearance) else {
return hoveredTabBackground
}
let adjusted = custom.isBonsplitLightColor
Expand Down Expand Up @@ -156,7 +227,7 @@ enum TabBarColors {
return explicit
}

guard let custom = chromeBackgroundColor(for: appearance) else {
guard let custom = tabBarBackgroundColor(for: appearance) else {
return .separatorColor
}
let alpha: CGFloat = custom.isBonsplitLightColor ? 0.26 : 0.36
Expand Down
Loading
Loading