feat(ios): W4 app wiring — DI graph, event fan-out, lifecycle, privacy shade
T-iOS-15: AppEnvironment production DI (real Keychain/probe/transports), EventFanOut (engine.events → TerminalVM + GateVM + activity bridge), scenePhase lifecycle (.background→close, suspend→rebuild+reopen reclaims size), privacy shade on != .active, spike screen deleted, cold-start routing Walkthrough proxies: hosted live-server smoke over the production DI graph (probe→store→ attach→echo→close) + real-Keychain kSecAttrAccessible assertion (closes T-iOS-7 deferral) Deviation closed: TerminalViewModel.lastSentDims (valid-only) + alive-engine .active → notifyForegrounded(dims:) (orchestrator fix on behalf of T-iOS-11 owner) Env finding: repo under ~/Documents → TCC blocks sim-spawned node; SimServerHarness dual-mode (self-bootstrap / WEBTERM_SERVER_URL via simctl launchctl setenv) Verified: 214 pkg + 76 app + 10 integration tests green; verify agent 8/8 PASS
This commit is contained in:
@@ -43,12 +43,25 @@ final class TerminalViewModel {
|
||||
case exited(code: Int, reason: String?)
|
||||
}
|
||||
|
||||
/// Terminal geometry snapshot (Equatable for test assertions; the frozen
|
||||
/// engine API takes a tuple, so `asTuple` bridges).
|
||||
struct TerminalDims: Equatable, Sendable {
|
||||
let cols: Int
|
||||
let rows: Int
|
||||
var asTuple: (cols: Int, rows: Int) { (cols, rows) }
|
||||
}
|
||||
|
||||
/// Actionable copy for `.failed(.replayTooLarge)` (plan §3.2 / §3.2.1
|
||||
/// coupling warning): reconnecting would deterministically fail forever,
|
||||
/// so the user must change a knob, not wait.
|
||||
static let replayTooLargeMessage =
|
||||
"服务器 scrollback 超过客户端上限,请调低 SCROLLBACK_BYTES 或调高客户端上限"
|
||||
|
||||
/// Last VALID dims forwarded to the engine (SwiftTerm `sizeChanged`).
|
||||
/// Read by the wiring layer for `notifyForegrounded(dims:)` — the frozen
|
||||
/// §3.2 signature needs real cols/rows and this is their single source.
|
||||
private(set) var lastSentDims: TerminalDims?
|
||||
|
||||
private(set) var banner: ConnectionBanner = .none
|
||||
private(set) var phase: TerminalPhase = .live
|
||||
/// Server-adopted session id (ALWAYS the server-issued one — persisting it
|
||||
@@ -182,8 +195,14 @@ final class TerminalViewModel {
|
||||
}
|
||||
|
||||
/// Terminal geometry changed (SwiftTerm `sizeChanged`). Always forwarded —
|
||||
/// the engine validates bounds and owns terminal-state dropping.
|
||||
/// the engine validates bounds and owns terminal-state dropping. Valid
|
||||
/// dims are remembered in `lastSentDims` so the T-iOS-15 wiring can feed
|
||||
/// `engine.notifyForegrounded(dims:)` on scenePhase reactivation (closes
|
||||
/// the W4 documented deviation; invalid dims never overwrite a good pair).
|
||||
func sendResize(cols: Int, rows: Int) {
|
||||
if Validation.isValidResize(cols: cols, rows: rows) {
|
||||
lastSentDims = TerminalDims(cols: cols, rows: rows)
|
||||
}
|
||||
enqueueSend(.resize(cols: cols, rows: rows))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user