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:
Yaojia Wang
2026-07-05 01:04:42 +02:00
parent 5098643355
commit cc4d3129cc
22 changed files with 1752 additions and 106 deletions

View File

@@ -1,89 +0,0 @@
//
// SpikeTerminalScreen.swift T-iOS-2 Day-1 spike W4/T-iOS-15
//
// SwiftTerm TerminalView /IME smoke plan §7 T-iOS-2
// - / first-responder
// - IME keydown
// - inputAccessoryView Esc/Ctrl-C SwiftTerm iOS
// TerminalAccessory esc/ctrl/tab iOSAccessoryView.swift
// spike key-bar public/keybar.ts T-iOS-11
// - SwiftTerm
//
// 线typed bytes send() feed()
// CR CRLF SessionEngine 线 W2/W3
//
// 2026-07-04 DEFERRED() PROGRESS_LOG
// App target xcodebuild
import SwiftTerm
import SwiftUI
/// Day-1 spike SwiftUI WebTermApp WebTermApp.swift
/// T-iOS-1/T-iOS-15 App target Swift 6 strict
/// concurrency SwiftTerm TerminalView
struct SpikeTerminalScreen: View {
var body: some View {
LocalEchoTerminal()
.ignoresSafeArea(.container, edges: .bottom)
.navigationTitle("SwiftTerm spike")
}
}
/// SwiftTerm UIKit `TerminalView` UIViewRepresentable
/// T-iOS-11 TerminalScreen SessionEngine
private struct LocalEchoTerminal: UIViewRepresentable {
func makeCoordinator() -> LocalEchoCoordinator {
LocalEchoCoordinator()
}
func makeUIView(context: Context) -> TerminalView {
let view = TerminalView(frame: .zero)
view.terminalDelegate = context.coordinator
view.feed(text: SpikeGreeting.text)
return view
}
func updateUIView(_ uiView: TerminalView, context: Context) {
//
}
}
private enum SpikeGreeting {
/// CRLFPTY LF
static let text = "SwiftTerm local-echo spike (T-iOS-2)\r\n"
+ "Typed bytes are echoed locally; CR -> CRLF.\r\n"
+ "真机清单: 键盘弹出 / 中文 IME / accessory esc·ctrl / 文本选择\r\n\r\n> "
}
/// delegateSwiftTerm "宿"
/// Enter CR0x0D LF
@MainActor
private final class LocalEchoCoordinator: NSObject, @preconcurrency TerminalViewDelegate {
private static let carriageReturn: UInt8 = 0x0D
private static let lineFeed: UInt8 = 0x0A
func send(source: TerminalView, data: ArraySlice<UInt8>) {
var echoed: [UInt8] = []
echoed.reserveCapacity(data.count + 1)
for byte in data {
echoed.append(byte)
if byte == Self.carriageReturn {
echoed.append(Self.lineFeed)
}
}
source.feed(byteArray: echoed[...])
}
func sizeChanged(source: TerminalView, newCols: Int, newRows: Int) {}
func setTerminalTitle(source: TerminalView, title: String) {}
func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {}
func scrolled(source: TerminalView, position: Double) {}
func requestOpenLink(source: TerminalView, link: String, params: [String: String]) {}
func clipboardCopy(source: TerminalView, content: Data) {}
func rangeChanged(source: TerminalView, startY: Int, endY: Int) {}
// bell / iTermContent SwiftTerm
}
#Preview {
SpikeTerminalScreen()
}