// Sources/UI/Sessions/SessionSwitcher.swift // T-2.6: Sheet UI for listing, selecting, and creating sessions. import SwiftUI struct SessionSwitcher: View { @ObservedObject var registry: SessionRegistry let credential: SidecarCredential let onSelect: (SessionInfo) -> Void @Environment(\.dismiss) private var dismiss @State private var showNewSessionAlert = false @State private var newSessionName = "" @State private var spawnError: String? = nil var body: some View { NavigationStack { Group { if registry.isLoading { ProgressView("Loading sessions…") .frame(maxWidth: .infinity, maxHeight: .infinity) } else { List(registry.sessions) { session in Button { onSelect(session) dismiss() } label: { SessionRow(session: session) } .foregroundStyle(.primary) } .overlay { if registry.sessions.isEmpty { ContentUnavailableView( "No Sessions", systemImage: "terminal", description: Text("Tap + to create a session.") ) } } } } .navigationTitle("Sessions") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Done") { dismiss() } } ToolbarItem(placement: .primaryAction) { Button { newSessionName = "" spawnError = nil showNewSessionAlert = true } label: { Image(systemName: "plus") } .accessibilityLabel("New Session") .accessibilityIdentifier("sessionswitcher.new") } } .alert("New Session", isPresented: $showNewSessionAlert) { TextField("Session name", text: $newSessionName) .autocorrectionDisabled() Button("Create") { let name = newSessionName.trimmingCharacters(in: .whitespaces) guard !name.isEmpty else { return } Task { do { try await registry.spawnSession(name: name, credential: credential) await registry.refresh(credential: credential) } catch { spawnError = error.localizedDescription } } } Button("Cancel", role: .cancel) {} } message: { Text("Enter a name for the new session.") } .safeAreaInset(edge: .bottom) { if let err = registry.error ?? spawnError { Text(err) .font(.caption) .foregroundStyle(.red) .padding(8) .frame(maxWidth: .infinity, alignment: .leading) .background(Color(uiColor: .secondarySystemBackground)) } } } .task { await registry.refresh(credential: credential) } } }