215 lines · 9.6 KB · Swift
RawDownload
import CodexBarCoreimport SwiftUI @MainActorstruct DisplayPane: View {    private static let maxOverviewProviders = SettingsStore.mergedOverviewProviderLimit     @State private var isOverviewProviderPopoverPresented = false    @Bindable var settings: SettingsStore    @Bindable var store: UsageStore     var body: some View {        ScrollView(.vertical, showsIndicators: true) {            VStack(alignment: .leading, spacing: 16) {                SettingsSection(contentSpacing: 12) {                    Text("Menu bar")                        .font(.caption)                        .foregroundStyle(.secondary)                        .textCase(.uppercase)                    PreferenceToggleRow(                        title: "Merge Icons",                        subtitle: "Use a single menu bar icon with a provider switcher.",                        binding: self.$settings.mergeIcons)                    PreferenceToggleRow(                        title: "Switcher shows icons",                        subtitle: "Show provider icons in the switcher (otherwise show a weekly progress line).",                        binding: self.$settings.switcherShowsIcons)                        .disabled(!self.settings.mergeIcons)                        .opacity(self.settings.mergeIcons ? 1 : 0.5)                    PreferenceToggleRow(                        title: "Show most-used provider",                        subtitle: "Menu bar auto-shows the provider closest to its rate limit.",                        binding: self.$settings.menuBarShowsHighestUsage)                        .disabled(!self.settings.mergeIcons)                        .opacity(self.settings.mergeIcons ? 1 : 0.5)                    PreferenceToggleRow(                        title: "Menu bar shows percent",                        subtitle: "Replace critter bars with provider branding icons and a percentage.",                        binding: self.$settings.menuBarShowsBrandIconWithPercent)                    HStack(alignment: .top, spacing: 12) {                        VStack(alignment: .leading, spacing: 4) {                            Text("Display mode")                                .font(.body)                            Text("Choose what to show in the menu bar (Pace shows usage vs. expected).")                                .font(.footnote)                                .foregroundStyle(.tertiary)                        }                        Spacer()                        Picker("Display mode", selection: self.$settings.menuBarDisplayMode) {                            ForEach(MenuBarDisplayMode.allCases) { mode in                                Text(mode.label).tag(mode)                            }                        }                        .labelsHidden()                        .pickerStyle(.menu)                        .frame(maxWidth: 200)                    }                    .disabled(!self.settings.menuBarShowsBrandIconWithPercent)                    .opacity(self.settings.menuBarShowsBrandIconWithPercent ? 1 : 0.5)                }                 Divider()                 SettingsSection(contentSpacing: 12) {                    Text("Menu content")                        .font(.caption)                        .foregroundStyle(.secondary)                        .textCase(.uppercase)                    PreferenceToggleRow(                        title: "Show usage as used",                        subtitle: "Progress bars fill as you consume quota (instead of showing remaining).",                        binding: self.$settings.usageBarsShowUsed)                    PreferenceToggleRow(                        title: "Show reset time as clock",                        subtitle: "Display reset times as absolute clock values instead of countdowns.",                        binding: self.$settings.resetTimesShowAbsolute)                    PreferenceToggleRow(                        title: "Show credits + extra usage",                        subtitle: "Show Codex Credits and Claude Extra usage sections in the menu.",                        binding: self.$settings.showOptionalCreditsAndExtraUsage)                    PreferenceToggleRow(                        title: "Show all token accounts",                        subtitle: "Stack token accounts in the menu (otherwise show an account switcher bar).",                        binding: self.$settings.showAllTokenAccountsInMenu)                    self.overviewProviderSelector                }            }            .frame(maxWidth: .infinity, alignment: .leading)            .padding(.horizontal, 20)            .padding(.vertical, 12)            .onAppear {                self.reconcileOverviewSelection()            }            .onChange(of: self.settings.mergeIcons) { _, isEnabled in                guard isEnabled else {                    self.isOverviewProviderPopoverPresented = false                    return                }                self.reconcileOverviewSelection()            }            .onChange(of: self.activeProvidersInOrder) { _, _ in                if self.activeProvidersInOrder.isEmpty {                    self.isOverviewProviderPopoverPresented = false                }                self.reconcileOverviewSelection()            }        }    }     private var overviewProviderSelector: some View {        VStack(alignment: .leading, spacing: 6) {            HStack(alignment: .center, spacing: 12) {                Text("Overview tab providers")                    .font(.body)                Spacer(minLength: 0)                if self.showsOverviewConfigureButton {                    Button("Configure…") {                        self.isOverviewProviderPopoverPresented = true                    }                    .offset(y: 1)                    .popover(isPresented: self.$isOverviewProviderPopoverPresented, arrowEdge: .bottom) {                        self.overviewProviderPopover                    }                }            }             if !self.settings.mergeIcons {                Text("Enable Merge Icons to configure Overview tab providers.")                    .font(.footnote)                    .foregroundStyle(.tertiary)            } else if self.activeProvidersInOrder.isEmpty {                Text("No enabled providers available for Overview.")                    .font(.footnote)                    .foregroundStyle(.tertiary)            } else {                Text(self.overviewProviderSelectionSummary)                    .font(.footnote)                    .foregroundStyle(.tertiary)                    .lineLimit(2)                    .truncationMode(.tail)            }        }    }     private var overviewProviderPopover: some View {        VStack(alignment: .leading, spacing: 10) {            Text("Choose up to \(Self.maxOverviewProviders) providers")                .font(.headline)            Text("Overview rows always follow provider order.")                .font(.footnote)                .foregroundStyle(.tertiary)             ScrollView(.vertical, showsIndicators: true) {                VStack(alignment: .leading, spacing: 6) {                    ForEach(self.activeProvidersInOrder, id: \.self) { provider in                        Toggle(                            isOn: Binding(                                get: { self.overviewSelectedProviders.contains(provider) },                                set: { shouldSelect in                                    self.setOverviewProviderSelection(provider: provider, isSelected: shouldSelect)                                })) {                            Text(self.providerDisplayName(provider))                                .font(.body)                        }                        .toggleStyle(.checkbox)                        .disabled(                            !self.overviewSelectedProviders.contains(provider) &&                                self.overviewSelectedProviders.count >= Self.maxOverviewProviders)                    }                }            }            .frame(maxHeight: 220)        }        .padding(12)        .frame(width: 280)    }     private var activeProvidersInOrder: [UsageProvider] {        self.store.enabledProviders()    }     private var overviewSelectedProviders: [UsageProvider] {        self.settings.resolvedMergedOverviewProviders(            activeProviders: self.activeProvidersInOrder,            maxVisibleProviders: Self.maxOverviewProviders)    }     private var showsOverviewConfigureButton: Bool {        self.settings.mergeIcons && !self.activeProvidersInOrder.isEmpty    }     private var overviewProviderSelectionSummary: String {        let selectedNames = self.overviewSelectedProviders.map(self.providerDisplayName)        guard !selectedNames.isEmpty else { return "No providers selected" }        return selectedNames.joined(separator: ", ")    }     private func providerDisplayName(_ provider: UsageProvider) -> String {        ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName    }     private func setOverviewProviderSelection(provider: UsageProvider, isSelected: Bool) {        _ = self.settings.setMergedOverviewProviderSelection(            provider: provider,            isSelected: isSelected,            activeProviders: self.activeProvidersInOrder,            maxVisibleProviders: Self.maxOverviewProviders)    }     private func reconcileOverviewSelection() {        _ = self.settings.reconcileMergedOverviewSelectedProviders(            activeProviders: self.activeProvidersInOrder,            maxVisibleProviders: Self.maxOverviewProviders)    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t