Coalesce Ghostty background notifications to latest value
This commit is contained in:
parent
790f3c8287
commit
afba0fb459
2 changed files with 122 additions and 16 deletions
|
|
@ -124,6 +124,48 @@ enum TerminalOpenURLTarget: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
/// Coalesces Ghostty background notifications so consumers only observe
|
||||
/// the latest runtime background for a burst of updates.
|
||||
final class GhosttyDefaultBackgroundNotificationDispatcher {
|
||||
private let coalescer: NotificationBurstCoalescer
|
||||
private let postNotification: ([AnyHashable: Any]) -> Void
|
||||
private var pendingUserInfo: [AnyHashable: Any]?
|
||||
|
||||
init(
|
||||
delay: TimeInterval = 1.0 / 30.0,
|
||||
postNotification: @escaping ([AnyHashable: Any]) -> Void = { userInfo in
|
||||
NotificationCenter.default.post(
|
||||
name: .ghosttyDefaultBackgroundDidChange,
|
||||
object: nil,
|
||||
userInfo: userInfo
|
||||
)
|
||||
}
|
||||
) {
|
||||
coalescer = NotificationBurstCoalescer(delay: delay)
|
||||
self.postNotification = postNotification
|
||||
}
|
||||
|
||||
func signal(backgroundColor: NSColor, opacity: Double) {
|
||||
let signalOnMain = { [self] in
|
||||
pendingUserInfo = [
|
||||
GhosttyNotificationKey.backgroundColor: backgroundColor,
|
||||
GhosttyNotificationKey.backgroundOpacity: opacity
|
||||
]
|
||||
coalescer.signal { [self] in
|
||||
guard let userInfo = pendingUserInfo else { return }
|
||||
pendingUserInfo = nil
|
||||
postNotification(userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if Thread.isMainThread {
|
||||
signalOnMain()
|
||||
} else {
|
||||
DispatchQueue.main.async(execute: signalOnMain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resolveTerminalOpenURLTarget(_ rawValue: String) -> TerminalOpenURLTarget? {
|
||||
let trimmed = rawValue.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty else { return nil }
|
||||
|
|
@ -203,6 +245,7 @@ class GhosttyApp {
|
|||
}()
|
||||
private let backgroundLogURL = GhosttyApp.resolveBackgroundLogURL()
|
||||
private var appObservers: [NSObjectProtocol] = []
|
||||
private let defaultBackgroundNotificationDispatcher = GhosttyDefaultBackgroundNotificationDispatcher()
|
||||
|
||||
// Scroll lag tracking
|
||||
private(set) var isScrolling = false
|
||||
|
|
@ -631,22 +674,10 @@ class GhosttyApp {
|
|||
}
|
||||
|
||||
private func notifyDefaultBackgroundDidChange() {
|
||||
let userInfo: [AnyHashable: Any] = [
|
||||
GhosttyNotificationKey.backgroundColor: defaultBackgroundColor,
|
||||
GhosttyNotificationKey.backgroundOpacity: defaultBackgroundOpacity
|
||||
]
|
||||
let post = {
|
||||
NotificationCenter.default.post(
|
||||
name: .ghosttyDefaultBackgroundDidChange,
|
||||
object: nil,
|
||||
userInfo: userInfo
|
||||
)
|
||||
}
|
||||
if Thread.isMainThread {
|
||||
post()
|
||||
} else {
|
||||
DispatchQueue.main.async(execute: post)
|
||||
}
|
||||
defaultBackgroundNotificationDispatcher.signal(
|
||||
backgroundColor: defaultBackgroundColor,
|
||||
opacity: defaultBackgroundOpacity
|
||||
)
|
||||
}
|
||||
|
||||
private func performOnMain<T>(_ work: @MainActor () -> T) -> T {
|
||||
|
|
|
|||
|
|
@ -340,6 +340,81 @@ final class NotificationBurstCoalescerTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
final class GhosttyDefaultBackgroundNotificationDispatcherTests: XCTestCase {
|
||||
func testSignalCoalescesBurstToLatestBackground() {
|
||||
guard let dark = NSColor(hex: "#272822"),
|
||||
let light = NSColor(hex: "#FDF6E3") else {
|
||||
XCTFail("Expected valid test colors")
|
||||
return
|
||||
}
|
||||
|
||||
let expectation = expectation(description: "coalesced notification")
|
||||
expectation.expectedFulfillmentCount = 1
|
||||
var postedUserInfos: [[AnyHashable: Any]] = []
|
||||
|
||||
let dispatcher = GhosttyDefaultBackgroundNotificationDispatcher(delay: 0.01) { userInfo in
|
||||
postedUserInfos.append(userInfo)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
dispatcher.signal(backgroundColor: dark, opacity: 0.95)
|
||||
dispatcher.signal(backgroundColor: light, opacity: 0.75)
|
||||
}
|
||||
|
||||
wait(for: [expectation], timeout: 1.0)
|
||||
XCTAssertEqual(postedUserInfos.count, 1)
|
||||
XCTAssertEqual(
|
||||
(postedUserInfos[0][GhosttyNotificationKey.backgroundColor] as? NSColor)?.hexString(),
|
||||
"#FDF6E3"
|
||||
)
|
||||
XCTAssertEqual(
|
||||
postedOpacity(from: postedUserInfos[0][GhosttyNotificationKey.backgroundOpacity]),
|
||||
0.75,
|
||||
accuracy: 0.0001
|
||||
)
|
||||
}
|
||||
|
||||
func testSignalAcrossSeparateBurstsPostsMultipleNotifications() {
|
||||
guard let dark = NSColor(hex: "#272822"),
|
||||
let light = NSColor(hex: "#FDF6E3") else {
|
||||
XCTFail("Expected valid test colors")
|
||||
return
|
||||
}
|
||||
|
||||
let expectation = expectation(description: "two notifications")
|
||||
expectation.expectedFulfillmentCount = 2
|
||||
var postedHexes: [String] = []
|
||||
|
||||
let dispatcher = GhosttyDefaultBackgroundNotificationDispatcher(delay: 0.01) { userInfo in
|
||||
let hex = (userInfo[GhosttyNotificationKey.backgroundColor] as? NSColor)?.hexString() ?? "nil"
|
||||
postedHexes.append(hex)
|
||||
expectation.fulfill()
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
dispatcher.signal(backgroundColor: dark, opacity: 1.0)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
||||
dispatcher.signal(backgroundColor: light, opacity: 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
wait(for: [expectation], timeout: 1.0)
|
||||
XCTAssertEqual(postedHexes, ["#272822", "#FDF6E3"])
|
||||
}
|
||||
|
||||
private func postedOpacity(from value: Any?) -> Double {
|
||||
if let value = value as? Double {
|
||||
return value
|
||||
}
|
||||
if let value = value as? NSNumber {
|
||||
return value.doubleValue
|
||||
}
|
||||
XCTFail("Expected background opacity payload")
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
final class RecentlyClosedBrowserStackTests: XCTestCase {
|
||||
func testPopReturnsEntriesInLIFOOrder() {
|
||||
var stack = RecentlyClosedBrowserStack(capacity: 20)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue