diff --git a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift index 094ed7e..4a5536e 100644 --- a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift +++ b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift @@ -145,9 +145,18 @@ final class RemoteStore: ObservableObject { private var seenSeq: Set = [] private var reconnectAttempts = 0 - /// Offline UI-preview mode (set `NUCLEIC_DEMO=1`): seeds mock state so the surfaces render - /// without a paired Mac. No effect on real use. - private let demoMode = ProcessInfo.processInfo.environment["NUCLEIC_DEMO"] == "1" + /// Offline demo mode: seeds mock state and simulates the agent locally so every surface + /// renders — and the core loops (send, approve, start chat, to-dos) actually respond — + /// without a paired Mac. Reachable in two ways: the `NUCLEIC_DEMO=1` env var (dev / + /// screenshots, forced on at launch) and a persisted in-app toggle + /// (`enterDemo()` / `exitDemo()`) so an App Store reviewer with no Mac can exercise the app + /// (App Review Guideline 2.1). `@Published` so the UI can show a DEMO indicator and the + /// "Leave demo" affordance. + private static let demoModeKey = "nucleic.demoMode" + @Published private(set) var demoMode = ProcessInfo.processInfo.environment["NUCLEIC_DEMO"] == "1" + || UserDefaults.standard.bool(forKey: RemoteStore.demoModeKey) + /// In-flight simulated-run tasks (canned streaming), cancelled on `exitDemo()`. + private var demoTasks: [Task] = [] var isPaired: Bool { demoMode || IdentityStore.loadPairedHost() != nil } var deviceFingerprint: String { identity.fingerprint }