mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-08 10:44:50 +00:00
The home run-control card and the menu-bar status card render the agent and system-proxy status rows straight from snapshot replacements, so fast operations (e.g. proxy mode switches) flash transient states like "applying" before settling. Add a presentation-layer debounce shared by both surfaces: - TransientDelayGate: per-stream gate where stable values display immediately, transient values (starting/applying/repairing) display after 400 ms and are dropped when superseded, and displayed transients are held for at least 400 ms. - StatusDisplayModel: app-level shared model deriving agent and system-proxy payloads (row + detail + hint) from workflow snapshots through two independent gates; domain facts keep their cadence. - Wire the model through the composition root into RuntimeControlCard and StatusCardView; toggles, approval actions, the difference view and target/mode rows keep reading the raw snapshot.
29 lines
897 B
Swift
29 lines
897 B
Swift
import Foundation
|
|
|
|
/// 顺序虚拟 sleep 记录器:调用前先 `armNextCall()` 预约一对门闩,下一次
|
|
/// sleep 记录延迟、报到后挂起在预约门闩上;测试据此按调用序推进虚拟时钟,
|
|
/// 不轮询、不真实等待。
|
|
@MainActor
|
|
final class SleepGateRecorder {
|
|
private(set) var delays: [TimeInterval] = []
|
|
private var armedEntered: AsyncGate?
|
|
private var armedRelease: AsyncGate?
|
|
|
|
func sleep(_ delay: TimeInterval) async {
|
|
delays.append(delay)
|
|
let entered = armedEntered ?? AsyncGate()
|
|
let release = armedRelease ?? AsyncGate()
|
|
armedEntered = nil
|
|
armedRelease = nil
|
|
entered.release()
|
|
await release.wait()
|
|
}
|
|
|
|
func armNextCall() -> (entered: AsyncGate, release: AsyncGate) {
|
|
let pair = (entered: AsyncGate(), release: AsyncGate())
|
|
armedEntered = pair.entered
|
|
armedRelease = pair.release
|
|
return pair
|
|
}
|
|
}
|