diff --git a/mac/Sources/CodeBurnMenubar/AppStore.swift b/mac/Sources/CodeBurnMenubar/AppStore.swift index c2c6412..f38f1de 100644 --- a/mac/Sources/CodeBurnMenubar/AppStore.swift +++ b/mac/Sources/CodeBurnMenubar/AppStore.swift @@ -85,9 +85,11 @@ final class AppStore { return total >= activeDailyBudget } - /// The active daily-budget threshold formatted for display (currency or tokens). + /// The active daily-budget threshold formatted for display (tokens, or USD). + /// The cost budget is defined in USD (matching the "$" presets and field), so + /// it is not run through the display-currency conversion here. var dailyBudgetLabel: String { - isTokenMetric ? "\(activeDailyBudget.asCompactTokens()) tokens" : activeDailyBudget.asCurrency() + isTokenMetric ? "\(activeDailyBudget.asCompactTokens()) tokens" : activeDailyBudget.asUSD() } var isLoading: Bool { loadingCountsByKey.values.contains { $0 > 0 } } @@ -1268,6 +1270,12 @@ private let thousandsFormatter: NumberFormatter = { if n >= 1_000 { return String(format: "%.0fK", n / 1_000) } return String(format: "%.0f", n) } + + /// Formats a raw USD amount with a "$" and grouping, without applying the + /// display-currency rate. Used for the USD-denominated daily budget. + func asUSD() -> String { + "$" + (groupedDecimalFormatter.string(from: NSNumber(value: self)) ?? "\(Int(self))") + } } extension Int { diff --git a/mac/Sources/CodeBurnMenubar/Views/SettingsView.swift b/mac/Sources/CodeBurnMenubar/Views/SettingsView.swift index 3f9733a..3197d66 100644 --- a/mac/Sources/CodeBurnMenubar/Views/SettingsView.swift +++ b/mac/Sources/CodeBurnMenubar/Views/SettingsView.swift @@ -57,6 +57,17 @@ private struct GeneralSettingsTab: View { v == v.rounded() ? String(Int(v)) : String(v) } + // Help text under the budget picker. When "Custom…" is selected but no amount + // has been entered, the budget is effectively 0 (off); call that out so the + // alert does not look armed when it isn't. + private var alertHelpText: String { + let customEmpty = store.isTokenMetric + ? (tokenCustom && store.dailyTokenBudget == 0) + : (costCustom && store.dailyBudget == 0) + if customEmpty { return "Enter an amount above, or the alert stays off." } + return "Flame icon turns yellow when today's \(store.isTokenMetric ? "tokens" : "cost") pass the daily budget." + } + var body: some View { Form { Section("Display") { @@ -162,7 +173,7 @@ private struct GeneralSettingsTab: View { } } } - Text("Flame icon turns yellow when today's \(store.isTokenMetric ? "tokens" : "cost") pass the daily budget.") + Text(alertHelpText) .font(.system(size: 11)) .foregroundStyle(.secondary) }