diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index 82de33b1..21f40272 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -843,7 +843,6 @@ struct ContentView: View { @State private var titlebarThemeGeneration: UInt64 = 0 @State private var sidebarDraggedTabId: UUID? @State private var titlebarTextUpdateCoalescer = NotificationBurstCoalescer(delay: 1.0 / 30.0) - @State private var titlebarThemeUpdateCoalescer = NotificationBurstCoalescer(delay: 1.0 / 30.0) @State private var sidebarResizerCursorReleaseWorkItem: DispatchWorkItem? @State private var sidebarResizerPointerMonitor: Any? @State private var isResizerBandActive = false @@ -1261,10 +1260,15 @@ struct ContentView: View { } } - private func scheduleTitlebarThemeRefresh() { - titlebarThemeUpdateCoalescer.signal { + private func scheduleTitlebarThemeRefresh(reason: String) { + withTransaction(Transaction(animation: nil)) { titlebarThemeGeneration &+= 1 } + if GhosttyApp.shared.backgroundLogEnabled { + GhosttyApp.shared.logBackground( + "titlebar theme refresh reason=\(reason) generation=\(titlebarThemeGeneration)" + ) + } } private var focusedDirectory: String? { @@ -1406,11 +1410,16 @@ struct ContentView: View { }) view = AnyView(view.onReceive(NotificationCenter.default.publisher(for: Notification.Name("ghosttyConfigDidReload"))) { _ in - scheduleTitlebarThemeRefresh() + scheduleTitlebarThemeRefresh(reason: "ghosttyConfigDidReload") }) - view = AnyView(view.onReceive(NotificationCenter.default.publisher(for: Notification.Name("ghosttyDefaultBackgroundDidChange"))) { _ in - scheduleTitlebarThemeRefresh() + view = AnyView(view.onReceive(NotificationCenter.default.publisher(for: Notification.Name("ghosttyDefaultBackgroundDidChange"))) { notification in + let payloadHex = (notification.userInfo?[GhosttyNotificationKey.backgroundColor] as? NSColor)?.hexString() ?? "nil" + let eventId = (notification.userInfo?[GhosttyNotificationKey.backgroundEventId] as? NSNumber)?.uint64Value + let source = (notification.userInfo?[GhosttyNotificationKey.backgroundSource] as? String) ?? "nil" + scheduleTitlebarThemeRefresh( + reason: "ghosttyDefaultBackgroundDidChange:event=\(eventId.map(String.init) ?? "nil"):source=\(source):payload=\(payloadHex)" + ) }) view = AnyView(view.onReceive(NotificationCenter.default.publisher(for: .ghosttyDidBecomeFirstResponderSurface)) { notification in diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index 2b80a7f3..f0dd19e6 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -283,7 +283,9 @@ class GhosttyApp { private var defaultBackgroundUpdateScope: GhosttyDefaultBackgroundUpdateScope = .unscoped private var defaultBackgroundScopeSource: String = "initialize" private lazy var defaultBackgroundNotificationDispatcher: GhosttyDefaultBackgroundNotificationDispatcher = - GhosttyDefaultBackgroundNotificationDispatcher(logEvent: { [weak self] message in + // Theme chrome should track terminal theme changes in the same frame. + // Keep coalescing semantics, but flush in the next main turn instead of waiting ~1 frame. + GhosttyDefaultBackgroundNotificationDispatcher(delay: 0, logEvent: { [weak self] message in guard let self, self.backgroundLogEnabled else { return } self.logBackground(message) }) diff --git a/Sources/WorkspaceContentView.swift b/Sources/WorkspaceContentView.swift index 3c33f1ab..24743fe4 100644 --- a/Sources/WorkspaceContentView.swift +++ b/Sources/WorkspaceContentView.swift @@ -183,7 +183,9 @@ struct WorkspaceContentView: View { logTheme( "theme refresh workspace=\(workspace.id.uuidString) reason=\(reason) previousBg=\(previousBackgroundHex) nextBg=\(next.backgroundColor.hexString()) overrideBg=\(backgroundOverride?.hexString() ?? "nil")" ) - config = next + withTransaction(Transaction(animation: nil)) { + config = next + } workspace.applyGhosttyChrome(from: next) }