Files
Qiu Yuzhou c708cd8d19 feat(ui): debounce transient status rows via shared display gate
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.
2026-10-24 02:22:39 +08:00

41 lines
1.5 KiB
Swift

import SwiftUI
/// 首页分区:代理模式切换、运行控制、终端代理环境变量命令。
/// 模式与两个开关(agent/系统代理)走代理控制
/// 工作流的整体 snapshot;运行控制卡的状态行走 statusDisplay 的瞬态门控
/// 呈现;命令复制是 UI 副作用。三种模式(规则/全局/直连)
/// 与规则子选项共用同一快照。
struct HomeView: View {
@ObservedObject var workflow: CatalogWorkflow
@ObservedObject var control: ProxyControlWorkflow
let statusDisplay: StatusDisplayModel
let browser: HomeTargetBrowser
let activation: ActivationFeedbackState
let showServers: () -> Void
let clipboard: any TextClipboard
let errors: ErrorAlertPresenter
var body: some View {
ScrollView {
HStack(alignment: .top, spacing: 20) {
VStack(spacing: 16) {
HomeTargetCard(
workflow: workflow, browser: browser, activation: activation,
activeTargetID: control.snapshot.activeTarget?.id, showServers: showServers)
}
.frame(maxWidth: .infinity, alignment: .leading)
VStack(spacing: 16) {
RuntimeControlCard(control: control, statusDisplay: statusDisplay)
ModeCard(control: control)
TerminalProxyEnvironmentCard(control: control, clipboard: clipboard, errors: errors)
}
.frame(width: 400)
}
.padding(.leading, 28)
.padding(.trailing, 32)
.padding(.top, 24)
.padding(.bottom, 36)
}
}
}