import Foundation import SwiftUI protocol CodexAmbientLoginRunning: Sendable { func run(timeout: TimeInterval) async -> CodexLoginRunner.Result } struct DefaultCodexAmbientLoginRunner: CodexAmbientLoginRunning { func run(timeout: TimeInterval) async -> CodexLoginRunner.Result { await CodexLoginRunner.run(timeout: timeout) } } struct CodexAccountsSectionNotice: Equatable { enum Tone: Equatable { case secondary case warning } let text: String let tone: Tone } struct CodexAccountsSectionState: Equatable { let visibleAccounts: [CodexVisibleAccount] let activeVisibleAccountID: String? let liveVisibleAccountID: String? let hasUnreadableManagedAccountStore: Bool let isAuthenticatingManagedAccount: Bool let authenticatingManagedAccountID: UUID? let isRemovingManagedAccount: Bool let isAuthenticatingLiveAccount: Bool let isPromotingSystemAccount: Bool let notice: CodexAccountsSectionNotice? var showsActivePicker: Bool { self.visibleAccounts.count > 1 } var singleVisibleAccount: CodexVisibleAccount? { self.visibleAccounts.count == 1 ? self.visibleAccounts.first : nil } var systemVisibleAccount: CodexVisibleAccount? { guard let liveVisibleAccountID else { return nil } return self.visibleAccounts.first { $0.id == liveVisibleAccountID } } var showsSystemPicker: Bool { self.visibleAccounts.count > 1 || (self.liveVisibleAccountID == nil && !self.visibleAccounts.isEmpty) } var systemDisplayName: String { self.systemVisibleAccount?.displayName ?? L("No system account") } var canAddAccount: Bool { !self.hasUnreadableManagedAccountStore && !self.isAuthenticatingManagedAccount && !self.isRemovingManagedAccount && !self.isAuthenticatingLiveAccount && !self.isPromotingSystemAccount } var addAccountTitle: String { if self.isAuthenticatingManagedAccount, self.authenticatingManagedAccountID == nil { return L("Adding Account…") } return L("Add Account") } func showsLiveBadge(for account: CodexVisibleAccount) -> Bool { account.isLive } var isSystemSelectionDisabled: Bool { self.hasUnreadableManagedAccountStore || self.isAuthenticatingManagedAccount || self.isRemovingManagedAccount || self.isAuthenticatingLiveAccount || self.isPromotingSystemAccount } func canPromoteToSystem(_ account: CodexVisibleAccount) -> Bool { guard self.isSystemSelectionDisabled == false else { return false } guard account.id != self.liveVisibleAccountID else { return false } return account.storedAccountID != nil } func canReauthenticate(_ account: CodexVisibleAccount) -> Bool { guard account.canReauthenticate else { return false } guard self.isAuthenticatingManagedAccount == false else { return false } guard self.isRemovingManagedAccount == false else { return false } guard self.isAuthenticatingLiveAccount == false else { return false } guard self.isPromotingSystemAccount == false else { return false } if account.storedAccountID != nil { return self.hasUnreadableManagedAccountStore == false } return true } func canRemove(_ account: CodexVisibleAccount) -> Bool { guard account.canRemove else { return false } guard self.isAuthenticatingManagedAccount == false else { return false } guard self.isRemovingManagedAccount == false else { return false } guard self.isAuthenticatingLiveAccount == false else { return false } guard self.isPromotingSystemAccount == false else { return false } return self.hasUnreadableManagedAccountStore == false } func reauthenticateTitle(for account: CodexVisibleAccount) -> String { if let accountID = account.storedAccountID, self.isAuthenticatingManagedAccount, self.authenticatingManagedAccountID == accountID { return L("Re-authenticating…") } if account.storedAccountID == nil, self.isAuthenticatingLiveAccount { return L("Re-authenticating…") } return L("Re-auth") } } @MainActor struct CodexAccountsSectionView: View { let state: CodexAccountsSectionState let setActiveVisibleAccount: (String) -> Void let reauthenticateAccount: (CodexVisibleAccount) -> Void let removeAccount: (CodexVisibleAccount) -> Void let requestSystemVisibleAccount: (String) -> Void let addAccount: () -> Void var body: some View { Section { if let selection = self.activeSelectionBinding { VStack(alignment: .leading, spacing: 6) { HStack(alignment: .firstTextBaseline, spacing: 10) { Text(L("Active")) .font(.subheadline.weight(.semibold)) .frame(width: ProviderSettingsMetrics.pickerLabelWidth, alignment: .leading) Picker("", selection: selection) { ForEach(self.state.visibleAccounts) { account in Text(account.displayName).tag(account.id) } } .labelsHidden() .pickerStyle(.menu) .controlSize(.small) Spacer(minLength: 0) } Text(L("Choose which Codex account CodexBar should follow.")) .font(.footnote) .foregroundStyle(.secondary) self.systemRow(selection: self.systemSelectionBinding) } .disabled( self.state.isAuthenticatingManagedAccount || self.state.isRemovingManagedAccount || self.state.isAuthenticatingLiveAccount || self.state.isPromotingSystemAccount) } else if let account = self.state.singleVisibleAccount { VStack(alignment: .leading, spacing: 6) { HStack(alignment: .firstTextBaseline, spacing: 10) { Text(L("Account")) .font(.subheadline.weight(.semibold)) .frame(width: ProviderSettingsMetrics.pickerLabelWidth, alignment: .leading) Text(account.displayName) .font(.subheadline) Spacer(minLength: 0) } self.systemRow(selection: nil) } } if self.state.visibleAccounts.isEmpty { Text(L("No Codex accounts detected yet.")) .font(.footnote) .foregroundStyle(.secondary) } else { VStack(alignment: .leading, spacing: 10) { ForEach(self.state.visibleAccounts) { account in CodexAccountsSectionRowView( account: account, showsSystemBadge: self.state.showsLiveBadge(for: account), reauthenticateTitle: self.state.reauthenticateTitle(for: account), canReauthenticate: self.state.canReauthenticate(account), canRemove: self.state.canRemove(account), onReauthenticate: { self.reauthenticateAccount(account) }, onRemove: { self.removeAccount(account) }) } } } if let notice = self.state.notice { Text(notice.text) .font(.footnote) .foregroundStyle(notice.tone == .warning ? .red : .secondary) .fixedSize(horizontal: false, vertical: true) } Button(self.state.addAccountTitle) { self.addAccount() } .buttonStyle(.bordered) .controlSize(.small) .disabled(self.state.canAddAccount == false) } header: { Text(L("Accounts")) } } private var activeSelectionBinding: Binding? { guard self.state.showsActivePicker else { return nil } let fallbackID = self.state.activeVisibleAccountID ?? self.state.visibleAccounts.first?.id guard let fallbackID else { return nil } return Binding( get: { self.state.activeVisibleAccountID ?? fallbackID }, set: { self.setActiveVisibleAccount($0) }) } private var systemSelectionBinding: Binding? { guard self.state.showsSystemPicker else { return nil } guard let liveVisibleAccountID = self.state.liveVisibleAccountID else { return nil } return Binding( get: { self.state.liveVisibleAccountID ?? liveVisibleAccountID }, set: { self.requestSystemVisibleAccount($0) }) } @ViewBuilder private func systemRow(selection: Binding?) -> some View { HStack(alignment: .firstTextBaseline, spacing: 10) { Text(L("System")) .font(.subheadline.weight(.semibold)) .frame(width: ProviderSettingsMetrics.pickerLabelWidth, alignment: .leading) if let selection { Picker("", selection: selection) { ForEach(self.state.visibleAccounts) { account in Text(account.displayName) .tag(account.id) } } .labelsHidden() .pickerStyle(.menu) .controlSize(.small) .disabled(self.state.isSystemSelectionDisabled) } else if self.state.showsSystemPicker { Menu { ForEach(self.state.visibleAccounts) { account in Button(account.displayName) { self.requestSystemVisibleAccount(account.id) } .disabled(self.state.canPromoteToSystem(account) == false) } } label: { Text(self.state.systemDisplayName) .font(.subheadline) .foregroundStyle(.secondary) } .disabled(self.state.isSystemSelectionDisabled) } else { Text(self.state.systemDisplayName) .font(.subheadline) .foregroundStyle(self.state.systemVisibleAccount == nil ? .secondary : .primary) } Spacer(minLength: 0) } Text(L("The default Codex account on this Mac.")) .font(.footnote) .foregroundStyle(.secondary) } } private struct CodexAccountsSectionRowView: View { let account: CodexVisibleAccount let showsSystemBadge: Bool let reauthenticateTitle: String let canReauthenticate: Bool let canRemove: Bool let onReauthenticate: () -> Void let onRemove: () -> Void var body: some View { HStack(alignment: .center, spacing: 12) { VStack(alignment: .leading, spacing: 2) { HStack(alignment: .firstTextBaseline, spacing: 6) { Text(self.account.displayName) .font(.subheadline.weight(.semibold)) if self.showsSystemBadge { Text(L("(System)")) .font(.caption.weight(.semibold)) .foregroundStyle(.secondary) } } if let health = self.account.authenticationHealthLabel { Text(health) .font(.caption) .foregroundStyle(.orange) } } Spacer(minLength: 8) if self.account.canReauthenticate { Button(self.reauthenticateTitle) { self.onReauthenticate() } .buttonStyle(.bordered) .controlSize(.small) .disabled(self.canReauthenticate == false) } if self.account.canRemove { Button(L("Remove")) { self.onRemove() } .buttonStyle(.bordered) .controlSize(.small) .disabled(self.canRemove == false) } } } }