Add filesystem logs for theme resolution flow

This commit is contained in:
Lawrence Chen 2026-02-23 00:14:10 -08:00
parent 963bb03e99
commit 8f68ddb947
3 changed files with 81 additions and 13 deletions

View file

@ -166,10 +166,33 @@ class GhosttyApp {
private(set) var config: ghostty_config_t?
private(set) var defaultBackgroundColor: NSColor = .windowBackgroundColor
private(set) var defaultBackgroundOpacity: Double = 1.0
private static func resolveBackgroundLogURL(
environment: [String: String] = ProcessInfo.processInfo.environment
) -> URL {
if let explicitPath = environment["CMUX_DEBUG_BG_LOG"],
!explicitPath.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return URL(fileURLWithPath: explicitPath)
}
if let debugLogPath = environment["CMUX_DEBUG_LOG"],
!debugLogPath.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
let baseURL = URL(fileURLWithPath: debugLogPath)
let extensionSeparatorIndex = baseURL.lastPathComponent.lastIndex(of: ".")
let stem = extensionSeparatorIndex.map { String(baseURL.lastPathComponent[..<$0]) } ?? baseURL.lastPathComponent
let bgName = "\(stem)-bg.log"
return baseURL.deletingLastPathComponent().appendingPathComponent(bgName)
}
return URL(fileURLWithPath: "/tmp/cmux-bg.log")
}
let backgroundLogEnabled = {
if ProcessInfo.processInfo.environment["CMUX_DEBUG_BG"] == "1" {
return true
}
if ProcessInfo.processInfo.environment["CMUX_DEBUG_LOG"] != nil {
return true
}
if ProcessInfo.processInfo.environment["GHOSTTYTABS_DEBUG_BG"] == "1" {
return true
}
@ -178,7 +201,7 @@ class GhosttyApp {
}
return UserDefaults.standard.bool(forKey: "GhosttyTabsDebugBG")
}()
private let backgroundLogURL = URL(fileURLWithPath: "/tmp/cmux-bg.log")
private let backgroundLogURL = GhosttyApp.resolveBackgroundLogURL()
private var appObservers: [NSObjectProtocol] = []
// Scroll lag tracking

View file

@ -352,9 +352,20 @@ final class Workspace: Identifiable, ObservableObject {
}
func applyGhosttyChrome(backgroundColor: NSColor) {
let currentChromeColors = bonsplitController.configuration.appearance.chromeColors
let nextChromeColors = Self.resolvedChromeColors(from: backgroundColor)
if bonsplitController.configuration.appearance.chromeColors.backgroundHex == nextChromeColors.backgroundHex &&
bonsplitController.configuration.appearance.chromeColors.borderHex == nextChromeColors.borderHex {
let isNoOp = currentChromeColors.backgroundHex == nextChromeColors.backgroundHex &&
currentChromeColors.borderHex == nextChromeColors.borderHex
if GhosttyApp.shared.backgroundLogEnabled {
let currentBackgroundHex = currentChromeColors.backgroundHex ?? "nil"
let nextBackgroundHex = nextChromeColors.backgroundHex ?? "nil"
GhosttyApp.shared.logBackground(
"theme apply workspace=\(id.uuidString) currentBg=\(currentBackgroundHex) nextBg=\(nextBackgroundHex) currentBorder=\(currentChromeColors.borderHex ?? "nil") nextBorder=\(nextChromeColors.borderHex ?? "nil") noop=\(isNoOp)"
)
}
if isNoOp {
return
}
bonsplitController.configuration.appearance.chromeColors = nextChromeColors

View file

@ -9,7 +9,7 @@ struct WorkspaceContentView: View {
let isWorkspaceVisible: Bool
let isWorkspaceInputActive: Bool
let workspacePortalPriority: Int
@State private var config = WorkspaceContentView.resolveGhosttyAppearanceConfig()
@State private var config = WorkspaceContentView.resolveGhosttyAppearanceConfig(reason: "stateInit")
@Environment(\.colorScheme) private var colorScheme
@EnvironmentObject var notificationStore: TerminalNotificationStore
@ -87,7 +87,7 @@ struct WorkspaceContentView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onAppear {
syncBonsplitNotificationBadges()
refreshGhosttyAppearanceConfig()
refreshGhosttyAppearanceConfig(reason: "onAppear")
}
.onChange(of: notificationStore.notifications) { _, _ in
syncBonsplitNotificationBadges()
@ -96,17 +96,20 @@ struct WorkspaceContentView: View {
syncBonsplitNotificationBadges()
}
.onReceive(NotificationCenter.default.publisher(for: .ghosttyConfigDidReload)) { _ in
refreshGhosttyAppearanceConfig()
refreshGhosttyAppearanceConfig(reason: "ghosttyConfigDidReload")
}
.onChange(of: colorScheme) { _, _ in
.onChange(of: colorScheme) { oldValue, newValue in
// Keep split overlay color/opacity in sync with light/dark theme transitions.
refreshGhosttyAppearanceConfig()
refreshGhosttyAppearanceConfig(reason: "colorSchemeChanged:\(oldValue)->\(newValue)")
}
.onReceive(NotificationCenter.default.publisher(for: .ghosttyDefaultBackgroundDidChange)) { notification in
if let backgroundColor = notification.userInfo?[GhosttyNotificationKey.backgroundColor] as? NSColor {
refreshGhosttyAppearanceConfig(backgroundOverride: backgroundColor)
refreshGhosttyAppearanceConfig(
reason: "ghosttyDefaultBackgroundDidChange:withPayload",
backgroundOverride: backgroundColor
)
} else {
refreshGhosttyAppearanceConfig()
refreshGhosttyAppearanceConfig(reason: "ghosttyDefaultBackgroundDidChange:withoutPayload")
}
}
}
@ -142,20 +145,51 @@ struct WorkspaceContentView: View {
}
static func resolveGhosttyAppearanceConfig(
reason: String = "unspecified",
backgroundOverride: NSColor? = nil,
loadConfig: () -> GhosttyConfig = GhosttyConfig.load,
defaultBackground: () -> NSColor = { GhosttyApp.shared.defaultBackgroundColor }
) -> GhosttyConfig {
var next = loadConfig()
next.backgroundColor = backgroundOverride ?? defaultBackground()
let loadedBackgroundHex = next.backgroundColor.hexString()
let defaultBackgroundHex: String
let resolvedBackground: NSColor
if let backgroundOverride {
resolvedBackground = backgroundOverride
defaultBackgroundHex = "skipped"
} else {
let fallback = defaultBackground()
resolvedBackground = fallback
defaultBackgroundHex = fallback.hexString()
}
next.backgroundColor = resolvedBackground
if GhosttyApp.shared.backgroundLogEnabled {
GhosttyApp.shared.logBackground(
"theme resolve reason=\(reason) loadedBg=\(loadedBackgroundHex) overrideBg=\(backgroundOverride?.hexString() ?? "nil") defaultBg=\(defaultBackgroundHex) finalBg=\(next.backgroundColor.hexString()) theme=\(next.theme ?? "nil")"
)
}
return next
}
private func refreshGhosttyAppearanceConfig(backgroundOverride: NSColor? = nil) {
let next = Self.resolveGhosttyAppearanceConfig(backgroundOverride: backgroundOverride)
private func refreshGhosttyAppearanceConfig(reason: String, backgroundOverride: NSColor? = nil) {
let previousBackgroundHex = config.backgroundColor.hexString()
let next = Self.resolveGhosttyAppearanceConfig(
reason: reason,
backgroundOverride: backgroundOverride
)
logTheme(
"theme refresh workspace=\(workspace.id.uuidString) reason=\(reason) previousBg=\(previousBackgroundHex) nextBg=\(next.backgroundColor.hexString()) overrideBg=\(backgroundOverride?.hexString() ?? "nil")"
)
config = next
workspace.applyGhosttyChrome(from: next)
}
private func logTheme(_ message: String) {
guard GhosttyApp.shared.backgroundLogEnabled else { return }
GhosttyApp.shared.logBackground(message)
}
}
extension WorkspaceContentView {