diff --git a/Sources/UI/Terminal/MainTerminalView.swift b/Sources/UI/Terminal/MainTerminalView.swift index 1001e48..806b3ec 100644 --- a/Sources/UI/Terminal/MainTerminalView.swift +++ b/Sources/UI/Terminal/MainTerminalView.swift @@ -48,8 +48,22 @@ struct MainTerminalView: View { .padding(.vertical, 4) .background(Color(uiColor: .secondarySystemBackground)) } - .task { await bootstrap() } + .task { await initialBootstrap() } .task { await registry.refresh(credential: credential) } // T-2.6 + .onChange(of: activeSessionId) { _, newId in + guard let newId else { return } + // Avoid reconnect storm if id already matches the current connection. + if connection?.id == newId { return } + Task { + if let oldConn = connection { await oldConn.suspend() } + cancellables.removeAll() + connection = nil + currentPiState = nil + sessionName = "" + terminalVC.feed(data: Data("\u{1B}[H\u{1B}[2J".utf8)) + await bootstrap(sessionId: newId) + } + } .sheet(isPresented: $showSwitcher) { // T-2.6 SessionSwitcher(registry: registry, credential: credential) { session in activeSessionId = session.id @@ -63,12 +77,23 @@ struct MainTerminalView: View { // MARK: - Bootstrap - private func bootstrap() async { + private func initialBootstrap() async { statusText = "Looking for sessions…" do { let sessionId = try await resolveSession() - statusText = "Connecting to \(sessionId)…" - let conn = SessionConnection(id: sessionId, credential: credential) + await bootstrap(sessionId: sessionId) + } catch { + statusText = "Error: \(error.localizedDescription)" + } + } + + private func bootstrap(sessionId: String) async { + // Keep activeSessionId in sync. The .onChange handler guards against + // re-entry via `connection?.id == newId` check (connection is still nil here + // on first call, but we set it below before any further state change). + activeSessionId = sessionId + statusText = "Connecting to \(sessionId)…" + let conn = SessionConnection(id: sessionId, credential: credential) // Wire live ANSI stream → terminal conn.stream @@ -161,9 +186,6 @@ struct MainTerminalView: View { ? ResumeCursor().lastSeq(for: sessionId) : nil await conn.resume(from: lastSeq) - } catch { - statusText = "Error: \(error.localizedDescription)" - } } // MARK: - Session resolution