import CodexBarCoreimport Foundation enum MenuBarDisplayText { static func percentText(window: RateWindow?, showUsed: Bool) -> String? { guard let window else { return nil } let percent = showUsed ? window.usedPercent : window.remainingPercent let clamped = min(100, max(0, percent)) return String(format: "%.0f%%", clamped) } static func paceText(pace: UsagePace?) -> String? { guard let pace else { return nil } let deltaValue = Int(abs(pace.deltaPercent).rounded()) let sign = pace.deltaPercent >= 0 ? "+" : "-" return "\(sign)\(deltaValue)%" } static func displayText( mode: MenuBarDisplayMode, percentWindow: RateWindow?, pace: UsagePace? = nil, showUsed: Bool, resetTimeDisplayStyle: ResetTimeDisplayStyle = .countdown, now: Date = .init()) -> String? { switch mode { case .percent: return self.percentText(window: percentWindow, showUsed: showUsed) case .pace: // Pace can be temporarily unavailable near a reset or when a provider omits window metadata. // Keep the selected quota visible instead of collapsing the status item to an icon-only state. return self.paceText(pace: pace) ?? self.percentText(window: percentWindow, showUsed: showUsed) case .both: guard let percent = percentText(window: percentWindow, showUsed: showUsed) else { return nil } // Fall back to percent-only when pace is unavailable (e.g. Copilot) guard let paceText = Self.paceText(pace: pace) else { return percent } return "\(percent) · \(paceText)" case .resetTime: guard let percentWindow else { return nil } if let resetsAt = percentWindow.resetsAt { let description = switch resetTimeDisplayStyle { case .countdown: UsageFormatter.resetCountdownDescription(from: resetsAt, now: now) case .absolute: UsageFormatter.resetDescription(from: resetsAt, now: now) } return "↻ \(description)" } if let resetDescription = self.resetMetadataText(percentWindow.resetDescription) { return "↻ \(resetDescription)" } return self.percentText(window: percentWindow, showUsed: showUsed) } } private static func resetMetadataText(_ description: String?) -> String? { guard let description else { return nil } let trimmed = description.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return nil } // RateWindow.resetDescription predates provider-specific detail fields and is also used for // request/token summaries. Only trust phrases that explicitly describe reset timing. let normalized = trimmed.lowercased() let resetPrefixes = [ "reset ", "resets ", "in ", "today ", "today,", "tomorrow ", "tomorrow,", "next ", "expire ", "expires ", "refill ", "refills ", ] let exactResetDescriptions = ["today", "tomorrow", "expired", "now", "soon"] return exactResetDescriptions.contains(normalized) || resetPrefixes.contains(where: normalized.hasPrefix) ? trimmed : nil }}
import CodexBarCore
import Foundation
enum MenuBarDisplayText {
static func percentText(window: RateWindow?, showUsed: Bool) -> String? {
guard let window else { return nil }
let percent = showUsed ? window.usedPercent : window.remainingPercent
let clamped = min(100, max(0, percent))
return String(format: "%.0f%%", clamped)
}
static func paceText(pace: UsagePace?) -> String? {
guard let pace else { return nil }
let deltaValue = Int(abs(pace.deltaPercent).rounded())
let sign = pace.deltaPercent >= 0 ? "+" : "-"
return "\(sign)\(deltaValue)%"
}
static func displayText(
mode: MenuBarDisplayMode,
percentWindow: RateWindow?,
pace: UsagePace? = nil,
showUsed: Bool,
resetTimeDisplayStyle: ResetTimeDisplayStyle = .countdown,
now: Date = .init()) -> String?
{
switch mode {
case .percent:
return self.percentText(window: percentWindow, showUsed: showUsed)
case .pace:
// Pace can be temporarily unavailable near a reset or when a provider omits window metadata.
// Keep the selected quota visible instead of collapsing the status item to an icon-only state.
return self.paceText(pace: pace)
?? self.percentText(window: percentWindow, showUsed: showUsed)
case .both:
guard let percent = percentText(window: percentWindow, showUsed: showUsed) else { return nil }
// Fall back to percent-only when pace is unavailable (e.g. Copilot)
guard let paceText = Self.paceText(pace: pace) else { return percent }
return "\(percent) · \(paceText)"
case .resetTime:
guard let percentWindow else { return nil }
if let resetsAt = percentWindow.resetsAt {
let description = switch resetTimeDisplayStyle {
case .countdown:
UsageFormatter.resetCountdownDescription(from: resetsAt, now: now)
case .absolute:
UsageFormatter.resetDescription(from: resetsAt, now: now)
}
return "↻ \(description)"
}
if let resetDescription = self.resetMetadataText(percentWindow.resetDescription) {
return "↻ \(resetDescription)"
}
return self.percentText(window: percentWindow, showUsed: showUsed)
}
}
private static func resetMetadataText(_ description: String?) -> String? {
guard let description else { return nil }
let trimmed = description.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return nil }
// RateWindow.resetDescription predates provider-specific detail fields and is also used for
// request/token summaries. Only trust phrases that explicitly describe reset timing.
let normalized = trimmed.lowercased()
let resetPrefixes = [
"reset ", "resets ", "in ", "today ", "today,", "tomorrow ", "tomorrow,", "next ",
"expire ", "expires ", "refill ", "refills ",
]
let exactResetDescriptions = ["today", "tomorrow", "expired", "now", "soon"]
return exactResetDescriptions.contains(normalized) || resetPrefixes.contains(where: normalized.hasPrefix)
? trimmed
: nil
}
}