@@ -6768,6 +6768,47 @@ final class Workspace: Identifiable, ObservableObject {
67686768 return themedColor. hexString ( includeAlpha: includeAlpha)
67696769 }
67706770
6771+ /// Returns a clearly-perceptible divider hex derived from the chrome background hex.
6772+ /// Dark backgrounds are lightened ~28% toward white; light backgrounds are darkened ~20% toward
6773+ /// black. These factors are meaningfully stronger than bonsplit's built-in weak fallback (0.16/0.12
6774+ /// tone at reduced alpha), ensuring the 1pt split divider is visible in both dark and light themes.
6775+ /// The result is always an opaque #RRGGBB string (no alpha), matching hexString(includeAlpha:false).
6776+ static func bonsplitDividerHex( fromChromeHex chromeHex: String ) -> String {
6777+ // Parse #RRGGBB or #RRGGBBAA produced by hexString(includeAlpha:)
6778+ let stripped = chromeHex. hasPrefix ( " # " ) ? String ( chromeHex. dropFirst ( ) ) : chromeHex
6779+ guard stripped. count >= 6 ,
6780+ let rByte = UInt8 ( stripped. prefix ( 2 ) , radix: 16 ) ,
6781+ let gByte = UInt8 ( stripped. dropFirst ( 2 ) . prefix ( 2 ) , radix: 16 ) ,
6782+ let bByte = UInt8 ( stripped. dropFirst ( 4 ) . prefix ( 2 ) , radix: 16 )
6783+ else {
6784+ return chromeHex // fallback: return unchanged on parse failure
6785+ }
6786+
6787+ var r = CGFloat ( rByte) / 255.0
6788+ var g = CGFloat ( gByte) / 255.0
6789+ var b = CGFloat ( bByte) / 255.0
6790+
6791+ // Perceptual luminance check (sRGB coefficients, no gamma expansion needed for light/dark)
6792+ let luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
6793+
6794+ if luminance < 0.5 {
6795+ // Dark background: lighten 28% toward white
6796+ r += ( 1.0 - r) * 0.28
6797+ g += ( 1.0 - g) * 0.28
6798+ b += ( 1.0 - b) * 0.28
6799+ } else {
6800+ // Light background: darken 20% toward black
6801+ r *= 0.80
6802+ g *= 0.80
6803+ b *= 0.80
6804+ }
6805+
6806+ let rOut = min ( 255 , max ( 0 , Int ( ( r * 255 ) . rounded ( ) ) ) )
6807+ let gOut = min ( 255 , max ( 0 , Int ( ( g * 255 ) . rounded ( ) ) ) )
6808+ let bOut = min ( 255 , max ( 0 , Int ( ( b * 255 ) . rounded ( ) ) ) )
6809+ return String ( format: " #%02X%02X%02X " , rOut, gOut, bOut)
6810+ }
6811+
67716812 nonisolated static func resolvedChromeColors(
67726813 from backgroundColor: NSColor
67736814 ) -> BonsplitConfiguration . Appearance . ChromeColors {
@@ -6778,14 +6819,16 @@ final class Workspace: Identifiable, ObservableObject {
67786819 from backgroundColor: NSColor ,
67796820 backgroundOpacity: Double
67806821 ) -> BonsplitConfiguration . Appearance {
6781- BonsplitConfiguration . Appearance (
6822+ let chromeHex = Self . bonsplitChromeHex (
6823+ backgroundColor: backgroundColor,
6824+ backgroundOpacity: backgroundOpacity
6825+ )
6826+ return BonsplitConfiguration . Appearance (
67826827 splitButtonTooltips: Self . currentSplitButtonTooltips ( ) ,
67836828 enableAnimations: false ,
67846829 chromeColors: . init(
6785- backgroundHex: Self . bonsplitChromeHex (
6786- backgroundColor: backgroundColor,
6787- backgroundOpacity: backgroundOpacity
6788- )
6830+ backgroundHex: chromeHex,
6831+ borderHex: Self . bonsplitDividerHex ( fromChromeHex: chromeHex)
67896832 )
67906833 )
67916834 }
@@ -6817,6 +6860,7 @@ final class Workspace: Identifiable, ObservableObject {
68176860 return
68186861 }
68196862 bonsplitController. configuration. appearance. chromeColors. backgroundHex = nextHex
6863+ bonsplitController. configuration. appearance. chromeColors. borderHex = Self . bonsplitDividerHex ( fromChromeHex: nextHex)
68206864 if GhosttyApp . shared. backgroundLogEnabled {
68216865 GhosttyApp . shared. logBackground (
68226866 " theme applied workspace= \( id. uuidString) reason= \( reason) resultingBg= \( bonsplitController. configuration. appearance. chromeColors. backgroundHex ?? " nil " ) "
0 commit comments