From aeda5f827de8118d843bd72a0f3dc9e02b096ab0 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:22:58 -0800 Subject: [PATCH] Adopt custom blue accent across active UI states --- Sources/ContentView.swift | 48 +++++++++++++------ Sources/GhosttyTerminalView.swift | 4 +- Sources/NotificationsPage.swift | 4 +- Sources/Panels/BrowserPanelView.swift | 8 ++-- Sources/Update/UpdateTitlebarAccessory.swift | 6 +-- Sources/Update/UpdateViewModel.swift | 4 +- cmuxTests/CmuxWebViewKeyEquivalentTests.swift | 12 ++--- 7 files changed, 53 insertions(+), 33 deletions(-) diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 752ffa29..36fca195 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -38,25 +38,45 @@ func sidebarActiveForegroundNSColor( return baseColor.withAlphaComponent(clampedOpacity) } -func sidebarSelectedWorkspaceBackgroundNSColor(for colorScheme: ColorScheme) -> NSColor { +func cmuxAccentNSColor(for colorScheme: ColorScheme) -> NSColor { switch colorScheme { case .dark: return NSColor( - srgbRed: 63.0 / 255.0, - green: 142.0 / 255.0, - blue: 252.0 / 255.0, + srgbRed: 0, + green: 145.0 / 255.0, + blue: 1.0, alpha: 1.0 ) default: return NSColor( - srgbRed: 62.0 / 255.0, - green: 133.0 / 255.0, - blue: 252.0 / 255.0, + srgbRed: 0, + green: 136.0 / 255.0, + blue: 1.0, alpha: 1.0 ) } } +func cmuxAccentNSColor(for appAppearance: NSAppearance?) -> NSColor { + let bestMatch = appAppearance?.bestMatch(from: [.darkAqua, .aqua]) + let scheme: ColorScheme = (bestMatch == .darkAqua) ? .dark : .light + return cmuxAccentNSColor(for: scheme) +} + +func cmuxAccentNSColor() -> NSColor { + NSColor(name: nil) { appearance in + cmuxAccentNSColor(for: appearance) + } +} + +func cmuxAccentColor() -> Color { + Color(nsColor: cmuxAccentNSColor()) +} + +func sidebarSelectedWorkspaceBackgroundNSColor(for colorScheme: ColorScheme) -> NSColor { + cmuxAccentNSColor(for: colorScheme) +} + func sidebarSelectedWorkspaceForegroundNSColor(opacity: CGFloat) -> NSColor { let clampedOpacity = max(0, min(opacity, 1)) return NSColor.white.withAlphaComponent(clampedOpacity) @@ -2583,7 +2603,7 @@ struct ContentView: View { let isSelected = index == selectedIndex let isHovered = commandPaletteHoveredResultIndex == index let rowBackground: Color = isSelected - ? Color.accentColor.opacity(0.12) + ? cmuxAccentColor().opacity(0.12) : (isHovered ? Color.primary.opacity(0.08) : .clear) Button { @@ -5903,7 +5923,7 @@ private struct SidebarEmptyArea: View { .overlay(alignment: .top) { if shouldShowTopDropIndicator { Rectangle() - .fill(Color.accentColor) + .fill(cmuxAccentColor()) .frame(height: 2) .padding(.horizontal, 8) .offset(y: -(rowSpacing / 2)) @@ -6010,7 +6030,7 @@ private struct TabItemView: View { } private var activeUnreadBadgeFillColor: Color { - usesInvertedActiveForeground ? Color.white.opacity(0.25) : Color.accentColor + usesInvertedActiveForeground ? Color.white.opacity(0.25) : cmuxAccentColor() } private var activeProgressTrackColor: Color { @@ -6018,7 +6038,7 @@ private struct TabItemView: View { } private var activeProgressFillColor: Color { - usesInvertedActiveForeground ? Color.white.opacity(0.8) : Color.accentColor + usesInvertedActiveForeground ? Color.white.opacity(0.8) : cmuxAccentColor() } private var shortcutHintEmphasis: Double { @@ -6289,7 +6309,7 @@ private struct TabItemView: View { .overlay(alignment: .top) { if showsCenteredTopDropIndicator { Rectangle() - .fill(Color.accentColor) + .fill(cmuxAccentColor()) .frame(height: 2) .padding(.horizontal, 8) .offset(y: index == 0 ? 0 : -(rowSpacing / 2)) @@ -6492,7 +6512,7 @@ private struct TabItemView: View { switch activeTabIndicatorStyle { case .leftRail: if isActive { return Color(nsColor: sidebarSelectedWorkspaceBackgroundNSColor(for: colorScheme)) } - if isMultiSelected { return Color.accentColor.opacity(0.25) } + if isMultiSelected { return cmuxAccentColor().opacity(0.25) } return Color.clear case .solidFill: if isActive { return Color(nsColor: sidebarSelectedWorkspaceBackgroundNSColor(for: colorScheme)) } @@ -6500,7 +6520,7 @@ private struct TabItemView: View { if isMultiSelected { return custom.opacity(0.35) } return custom.opacity(0.7) } - if isMultiSelected { return Color.accentColor.opacity(0.25) } + if isMultiSelected { return cmuxAccentColor().opacity(0.25) } return Color.clear } } diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index ecb5b7dc..89229133 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -3619,8 +3619,8 @@ final class GhosttySurfaceScrollView: NSView { inactiveOverlayView.isHidden = true addSubview(inactiveOverlayView) dropZoneOverlayView.wantsLayer = true - dropZoneOverlayView.layer?.backgroundColor = NSColor.controlAccentColor.withAlphaComponent(0.25).cgColor - dropZoneOverlayView.layer?.borderColor = NSColor.controlAccentColor.cgColor + dropZoneOverlayView.layer?.backgroundColor = cmuxAccentNSColor().withAlphaComponent(0.25).cgColor + dropZoneOverlayView.layer?.borderColor = cmuxAccentNSColor().cgColor dropZoneOverlayView.layer?.borderWidth = 2 dropZoneOverlayView.layer?.cornerRadius = 8 dropZoneOverlayView.isHidden = true diff --git a/Sources/NotificationsPage.swift b/Sources/NotificationsPage.swift index f04841ed..53cc8737 100644 --- a/Sources/NotificationsPage.swift +++ b/Sources/NotificationsPage.swift @@ -182,11 +182,11 @@ private struct NotificationRow: View { Button(action: onOpen) { HStack(alignment: .top, spacing: 12) { Circle() - .fill(notification.isRead ? Color.clear : Color.accentColor) + .fill(notification.isRead ? Color.clear : cmuxAccentColor()) .frame(width: 8, height: 8) .overlay( Circle() - .stroke(Color.accentColor.opacity(notification.isRead ? 0.2 : 1), lineWidth: 1) + .stroke(cmuxAccentColor().opacity(notification.isRead ? 0.2 : 1), lineWidth: 1) ) .padding(.top, 6) diff --git a/Sources/Panels/BrowserPanelView.swift b/Sources/Panels/BrowserPanelView.swift index 82069f74..b3ff4844 100644 --- a/Sources/Panels/BrowserPanelView.swift +++ b/Sources/Panels/BrowserPanelView.swift @@ -71,7 +71,7 @@ enum BrowserDevToolsIconColorOption: String, CaseIterable, Identifiable { // Matches Bonsplit tab icon tint for active tabs. return Color(nsColor: .labelColor) case .accent: - return .accentColor + return cmuxAccentColor() case .tertiary: return Color(nsColor: .tertiaryLabelColor) } @@ -288,8 +288,8 @@ struct BrowserPanelView: View { } .overlay { RoundedRectangle(cornerRadius: FocusFlashPattern.ringCornerRadius) - .stroke(Color.accentColor.opacity(focusFlashOpacity), lineWidth: 3) - .shadow(color: Color.accentColor.opacity(focusFlashOpacity * 0.35), radius: 10) + .stroke(cmuxAccentColor().opacity(focusFlashOpacity), lineWidth: 3) + .shadow(color: cmuxAccentColor().opacity(focusFlashOpacity * 0.35), radius: 10) .padding(FocusFlashPattern.ringInset) .allowsHitTesting(false) } @@ -676,7 +676,7 @@ struct BrowserPanelView: View { ) .overlay( RoundedRectangle(cornerRadius: omnibarPillCornerRadius, style: .continuous) - .stroke(addressBarFocused ? Color.accentColor : Color.clear, lineWidth: 1) + .stroke(addressBarFocused ? cmuxAccentColor() : Color.clear, lineWidth: 1) ) .accessibilityElement(children: .contain) .background { diff --git a/Sources/Update/UpdateTitlebarAccessory.swift b/Sources/Update/UpdateTitlebarAccessory.swift index ff73c91a..84ac40d3 100644 --- a/Sources/Update/UpdateTitlebarAccessory.swift +++ b/Sources/Update/UpdateTitlebarAccessory.swift @@ -333,7 +333,7 @@ struct TitlebarControlsView: View { .foregroundColor(.white) .frame(width: config.badgeSize, height: config.badgeSize) .background( - Circle().fill(Color.accentColor) + Circle().fill(cmuxAccentColor()) ) .offset(x: config.badgeOffset.width, y: config.badgeOffset.height) } @@ -905,11 +905,11 @@ private struct NotificationPopoverRow: View { Button(action: onOpen) { HStack(alignment: .top, spacing: 10) { Circle() - .fill(notification.isRead ? Color.clear : Color.accentColor) + .fill(notification.isRead ? Color.clear : cmuxAccentColor()) .frame(width: 8, height: 8) .overlay( Circle() - .stroke(Color.accentColor.opacity(notification.isRead ? 0.2 : 1), lineWidth: 1) + .stroke(cmuxAccentColor().opacity(notification.isRead ? 0.2 : 1), lineWidth: 1) ) .padding(.top, 6) diff --git a/Sources/Update/UpdateViewModel.swift b/Sources/Update/UpdateViewModel.swift index 8aa275af..dd8a6697 100644 --- a/Sources/Update/UpdateViewModel.swift +++ b/Sources/Update/UpdateViewModel.swift @@ -132,7 +132,7 @@ class UpdateViewModel: ObservableObject { case .checking: return .secondary case .updateAvailable: - return .accentColor + return cmuxAccentColor() case .downloading, .extracting, .installing: return .secondary case .notFound: @@ -147,7 +147,7 @@ class UpdateViewModel: ObservableObject { case .permissionRequest: return Color(nsColor: NSColor.systemBlue.blended(withFraction: 0.3, of: .black) ?? .systemBlue) case .updateAvailable: - return .accentColor + return cmuxAccentColor() case .notFound: return Color(nsColor: NSColor.systemBlue.blended(withFraction: 0.5, of: .black) ?? .systemBlue) case .error: diff --git a/cmuxTests/CmuxWebViewKeyEquivalentTests.swift b/cmuxTests/CmuxWebViewKeyEquivalentTests.swift index 7f742558..132f2374 100644 --- a/cmuxTests/CmuxWebViewKeyEquivalentTests.swift +++ b/cmuxTests/CmuxWebViewKeyEquivalentTests.swift @@ -850,9 +850,9 @@ final class SidebarSelectedWorkspaceColorTests: XCTestCase { return } - XCTAssertEqual(color.redComponent, 62.0 / 255.0, accuracy: 0.001) - XCTAssertEqual(color.greenComponent, 133.0 / 255.0, accuracy: 0.001) - XCTAssertEqual(color.blueComponent, 252.0 / 255.0, accuracy: 0.001) + XCTAssertEqual(color.redComponent, 0, accuracy: 0.001) + XCTAssertEqual(color.greenComponent, 136.0 / 255.0, accuracy: 0.001) + XCTAssertEqual(color.blueComponent, 1.0, accuracy: 0.001) XCTAssertEqual(color.alphaComponent, 1.0, accuracy: 0.001) } @@ -862,9 +862,9 @@ final class SidebarSelectedWorkspaceColorTests: XCTestCase { return } - XCTAssertEqual(color.redComponent, 63.0 / 255.0, accuracy: 0.001) - XCTAssertEqual(color.greenComponent, 142.0 / 255.0, accuracy: 0.001) - XCTAssertEqual(color.blueComponent, 252.0 / 255.0, accuracy: 0.001) + XCTAssertEqual(color.redComponent, 0, accuracy: 0.001) + XCTAssertEqual(color.greenComponent, 145.0 / 255.0, accuracy: 0.001) + XCTAssertEqual(color.blueComponent, 1.0, accuracy: 0.001) XCTAssertEqual(color.alphaComponent, 1.0, accuracy: 0.001) }