Sync theme updates in same frame on theme switch

This commit is contained in:
Lawrence Chen 2026-02-23 01:11:27 -08:00
parent bf17e4a56d
commit 1b954f1d68
3 changed files with 21 additions and 8 deletions

View file

@ -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

View file

@ -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)
})

View file

@ -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)
}