Files
2026-09-28 18:28:32 +05:30

40 lines
1.4 KiB
Swift

/// Decides when Starting ends. Frames and progress before NAVIGATED come from the agent's
/// about:blank bootstrap and never count; `afterHello` is a backstop if NAVIGATED never arrives.
public struct ContentReadiness: Sendable {
public static let afterNavigated: Duration = .seconds(15)
public static let afterHello: Duration = .seconds(25)
public private(set) var isReady = false
private var helloAt: ContinuousClock.Instant?
private var navigatedAt: ContinuousClock.Instant?
public init() {}
public mutating func hello(at now: ContinuousClock.Instant) {
if helloAt == nil { helloAt = now }
}
public mutating func navigated(at now: ContinuousClock.Instant) {
if navigatedAt == nil { navigatedAt = now }
}
public mutating func frame(isUniform: Bool) {
if navigatedAt != nil, !isUniform { isReady = true }
}
public mutating func progress(_ percent: Int) {
if navigatedAt != nil, percent >= 100 { isReady = true }
}
public mutating func tick(now: ContinuousClock.Instant) {
if let deadline = nextDeadline, now >= deadline { isReady = true }
}
public var nextDeadline: ContinuousClock.Instant? {
guard !isReady else { return nil }
return [navigatedAt.map { $0 + Self.afterNavigated }, helloAt.map { $0 + Self.afterHello }]
.compactMap { $0 }
.min()
}
}