#if os(tvOS)
import SwiftUI
import PhysarumEngine
/// Scene root for tvOS: owns the model (`AppState`) and the playback brain
/// (`AmbientController`), and suppresses the idle/Aerial screensaver so the ambient
/// visual keeps running.
struct TVRootView: View {
@State private var appState: AppState
@State private var controller: AmbientController
// Advertises `_physarum._tcp` and accepts a phone editor-remote (T2a). Disconnect → autonomous T1.
@State private var listener: AmbientListener?
@State private var errorPresenter = ErrorPresenter()
init() {
let state = AppState(slotStore: AppState.makeProductionSlotStore())
_appState = State(initialValue: state)
_controller = State(initialValue: AmbientController(appState: state))
}
var body: some View {
// NB: only the MetalView ignores the safe area (full-bleed render); the HUD/Hint
// overlays inside TVPlayerView must respect the tvOS title-safe area so they don't
// land in the overscan band on TVs that crop (e.g. an Ambilight set).
TVPlayerView(appState: appState, controller: controller)
.onAppear {
UIApplication.shared.isIdleTimerDisabled = true
appState.migrateLegacyCustomsIfNeeded() // once: carry pre-slot customs into slots
listener?.stop() // idempotent: a re-fired onAppear must not leak a 2nd listener
let listener = AmbientListener(controller: controller)
listener.start()
self.listener = listener
}
.onDisappear {
listener?.stop()
listener = nil
}
.environment(errorPresenter)
.errorAlert(errorPresenter)
}
}
#endif