import SwiftUI /// T-iOS-15 · Privacy shade (security-critical, plan §7 T-iOS-15). /// /// iOS writes the app-switcher snapshot to DISK the moment the scene reaches /// `.background` — but the switcher is usually ENTERED at `.inactive`. The /// shade therefore covers whenever `scenePhase != .active` (the exact rule; /// `.inactive`-only would let the `.background` snapshot capture terminal /// content — API keys, tokens, source — into the on-disk switcher image). /// Restored on `.active`. /// /// Pure mapping split from the view so the rule is unit-testable for all /// three phases (PrivacyShadeTests). enum PrivacyShadePolicy { static func isShadeVisible(for scenePhase: ScenePhase) -> Bool { scenePhase != .active } } /// Opaque cover rendered ABOVE the whole navigation tree (RootView ZStack) — /// deliberately no animation: the snapshot moment must never race a fade. /// /// Scope note (documented): sheets present in a separate presentation layer a /// root overlay cannot cover — but no terminal bytes are ever rendered in a /// sheet (pairing / plan-gate only), so the root-level shade covers every /// surface that shows PTY content. struct PrivacyShadeView: View { var body: some View { ZStack { Color(.systemBackground) .ignoresSafeArea() VStack(spacing: ShadeMetrics.spacing) { Image(systemName: "terminal.fill") .font(.largeTitle) .foregroundStyle(.secondary) Text("WebTerm") .font(.headline) .foregroundStyle(.secondary) } } .accessibilityHidden(true) } private enum ShadeMetrics { static let spacing: CGFloat = 12 } }