Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions Sources/CodexBar/MenuCardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,13 @@ extension UsageMenuCardView.Model {
if input.provider == .antigravity {
return Self.antigravityMetrics(input: input, snapshot: snapshot)
}
if input.provider == .minimax {
if let minimaxUsage = snapshot.minimaxUsage {
if let services = minimaxUsage.services, !services.isEmpty {
return Self.minimaxMetrics(services: services, input: input)
}
}
}
var metrics: [Metric] = []
let percentStyle: PercentStyle = input.usageBarsShowUsed ? .used : .left
let zaiUsage = input.provider == .zai ? snapshot.zaiUsage : nil
Expand Down Expand Up @@ -1095,6 +1102,42 @@ extension UsageMenuCardView.Model {
percentStyle: percentStyle),
]
}

private static func minimaxMetrics(services: [MiniMaxServiceUsage], input: Input) -> [Metric] {
let percentStyle: PercentStyle = input.usageBarsShowUsed ? .used : .left
var metrics: [Metric] = []

for (index, service) in services.enumerated() {
let id = "minimax-service-\(index)"
let title = service.displayName
let used = service.limit - service.usage // usage is remaining, calculate used
let remaining = service.usage // service.usage is remaining quota
let percent = service.percent // This is used percent

// Adjust display based on usageBarsShowUsed setting
let displayValue: Int = input.usageBarsShowUsed ? used : remaining
let detailText = "\(displayValue)/\(service.limit)"

// Adjust percentage display - BOTH for progress bar AND text
let displayPercent = input.usageBarsShowUsed ? percent : (100 - percent)
let detailLeftText = service.windowType
let detailRightText = String(format: "%.0f%%", displayPercent)

metrics.append(Metric(
id: id,
title: title,
percent: Self.clamped(displayPercent), // Use displayPercent for progress bar too
percentStyle: percentStyle,
resetText: service.resetDescription,
detailText: detailText,
detailLeftText: detailLeftText,
detailRightText: detailRightText,
pacePercent: nil,
paceOnTop: true))
}

return metrics
}

private static func antigravityMetric(
id: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct MiniMaxProviderImplementation: ProviderImplementation {

@MainActor
func settingsSnapshot(context: ProviderSettingsSnapshotContext) -> ProviderSettingsSnapshotContribution? {
.minimax(context.settings.minimaxSettingsSnapshot(tokenOverride: context.tokenOverride))
.minimax(context.settings.minimaxSettingsSnapshot())
}

@MainActor
Expand Down Expand Up @@ -65,7 +65,7 @@ struct MiniMaxProviderImplementation: ProviderImplementation {
source: context.settings.minimaxCookieSource,
keychainDisabled: context.settings.debugDisableKeychainAccess,
auto: "Automatic imports browser cookies and local storage tokens.",
manual: "Paste a Cookie header or cURL capture from the Coding Plan page.",
manual: "Paste a Cookie header or cURL capture from the Token Plan page.",
off: "MiniMax cookies are disabled.")
}

Expand Down Expand Up @@ -122,7 +122,7 @@ struct MiniMaxProviderImplementation: ProviderImplementation {
actions: [
ProviderSettingsActionDescriptor(
id: "minimax-open-dashboard",
title: "Open Coding Plan",
title: "Open Token Plan",
style: .link,
isVisible: nil,
perform: {
Expand All @@ -141,7 +141,7 @@ struct MiniMaxProviderImplementation: ProviderImplementation {
actions: [
ProviderSettingsActionDescriptor(
id: "minimax-open-dashboard-cookie",
title: "Open Coding Plan",
title: "Open Token Plan",
style: .link,
isVisible: nil,
perform: {
Expand Down
82 changes: 34 additions & 48 deletions Sources/CodexBar/Providers/MiniMax/MiniMaxSettingsStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@ extension SettingsStore {
}
}

var minimaxAPIToken: String {
get { self.configSnapshot.providerConfig(for: .minimax)?.sanitizedAPIKey ?? "" }
set {
self.updateProviderConfig(provider: .minimax) { entry in
entry.apiKey = self.normalizedConfigValue(newValue)
}
self.logSecretUpdate(provider: .minimax, field: "apiKey", value: newValue)
}
}

var minimaxCookieSource: ProviderCookieSource {
get { self.resolvedCookieSource(provider: .minimax, fallback: .auto) }
set {
Expand All @@ -44,53 +34,49 @@ extension SettingsStore {
}
}

func ensureMiniMaxCookieLoaded() {}

func ensureMiniMaxAPITokenLoaded() {}

func minimaxAuthMode(
environment: [String: String] = ProcessInfo.processInfo.environment) -> MiniMaxAuthMode
{
let apiToken = MiniMaxAPISettingsReader.apiToken(environment: environment) ?? self.minimaxAPIToken
let cookieHeader = MiniMaxSettingsReader.cookieHeader(environment: environment) ?? self.minimaxCookieHeader
return MiniMaxAuthMode.resolve(apiToken: apiToken, cookieHeader: cookieHeader)
var minimaxAPIToken: String {
get { self.configSnapshot.providerConfig(for: .minimax)?.sanitizedAPIKey ?? "" }
set {
self.updateProviderConfig(provider: .minimax) { entry in
entry.apiKey = self.normalizedConfigValue(newValue)
}
let hasToken = !newValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
if hasToken,
let metadata = ProviderDescriptorRegistry.metadata[.minimax],
!self.isProviderEnabled(provider: .minimax, metadata: metadata)
{
self.setProviderEnabled(provider: .minimax, metadata: metadata, enabled: true)
}
self.logSecretUpdate(provider: .minimax, field: "apiKey", value: newValue)
}
}
}

extension SettingsStore {
func minimaxSettingsSnapshot(tokenOverride: TokenAccountOverride?) -> ProviderSettingsSnapshot
.MiniMaxProviderSettings {
func minimaxSettingsSnapshot() -> ProviderSettingsSnapshot.MiniMaxProviderSettings {
ProviderSettingsSnapshot.MiniMaxProviderSettings(
cookieSource: self.minimaxSnapshotCookieSource(tokenOverride: tokenOverride),
manualCookieHeader: self.minimaxSnapshotCookieHeader(tokenOverride: tokenOverride),
cookieSource: self.minimaxCookieSource,
manualCookieHeader: self.minimaxCookieHeader,
apiRegion: self.minimaxAPIRegion)
Comment on lines +56 to 60
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve token-account overrides in MiniMax settings snapshot

UsageStore.refreshTokenAccounts relies on ProviderRegistry.makeSettingsSnapshot(..., tokenOverride:) to fetch each account with its own credentials, but this snapshot now always returns minimaxCookieSource and minimaxCookieHeader from global settings. Because MiniMax token accounts use cookie-header injection (not env injection), per-account refreshes will all use the same cookie (and may stay on .auto), so account switching and multi-account menu snapshots can no longer show distinct account usage.

Useful? React with 👍 / 👎.

}
}

private func minimaxSnapshotCookieHeader(tokenOverride: TokenAccountOverride?) -> String {
let fallback = self.minimaxCookieHeader
guard let support = TokenAccountSupportCatalog.support(for: .minimax),
case .cookieHeader = support.injection
else {
return fallback
}
guard let account = ProviderTokenAccountSelection.selectedAccount(
provider: .minimax,
settings: self,
override: tokenOverride)
else {
return fallback
}
return TokenAccountSupportCatalog.normalizedCookieHeader(account.token, support: support)
extension SettingsStore {
func ensureMiniMaxCookieLoaded() {
// Cookie loading handled by MiniMaxCookieImporter
}
}

private func minimaxSnapshotCookieSource(tokenOverride: TokenAccountOverride?) -> ProviderCookieSource {
let fallback = self.minimaxCookieSource
guard let support = TokenAccountSupportCatalog.support(for: .minimax),
support.requiresManualCookieSource
else {
return fallback
}
if self.tokenAccounts(for: .minimax).isEmpty { return fallback }
return .manual
extension SettingsStore {
func ensureMiniMaxAPITokenLoaded() {
// Token loading handled by MiniMaxAPITokenStore
}
}

extension SettingsStore {
func minimaxAuthMode() -> MiniMaxAuthMode {
MiniMaxAuthMode.resolve(
apiToken: self.minimaxAPIToken,
cookieHeader: self.minimaxCookieHeader)
}
}
3 changes: 3 additions & 0 deletions Sources/CodexBar/StatusItemController+Actions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ extension StatusItemController {
if provider == .alibaba {
return self.settings.alibabaCodingPlanAPIRegion.dashboardURL
}
if provider == .minimax {
return self.settings.minimaxAPIRegion.dashboardURL
}

let meta = self.store.metadata(for: provider)
let urlString: String? = if provider == .claude, self.store.isClaudeSubscription() {
Expand Down
7 changes: 7 additions & 0 deletions Sources/CodexBarCore/Providers/MiniMax/MiniMaxAPIRegion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,11 @@ public enum MiniMaxAPIRegion: String, CaseIterable, Sendable {
public var apiRemainsURL: URL {
URL(string: self.apiBaseURLString)!.appendingPathComponent(Self.remainsPath)
}

public var dashboardURL: URL {
var components = URLComponents(string: self.baseURLString)!
components.path = "/" + Self.codingPlanPath
components.query = Self.codingPlanQuery
return components.url!
}
}
Loading