Skip to content
Closed
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
4 changes: 3 additions & 1 deletion Sources/CodexBar/CostHistoryChartMenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,11 @@ struct CostHistoryChartMenuView: View {
}

if let total = self.totalCostUSD {
Text("Total (30d): \(UsageFormatter.usdString(total))")
Text("Est. total (30d): \(UsageFormatter.usdString(total))")
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
.truncationMode(.head)
}
}
.padding(.horizontal, 16)
Expand Down
6 changes: 3 additions & 3 deletions Sources/CodexBar/MenuCardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ struct UsageMenuCardView: View {
}
if let tokenUsage = self.model.tokenUsage {
VStack(alignment: .leading, spacing: 6) {
Text("Cost")
Text("cost_header_estimated")
.font(.body)
.fontWeight(.medium)
Text(tokenUsage.sessionLine)
Expand Down Expand Up @@ -598,7 +598,7 @@ struct UsageMenuCardCostSectionView: View {
VStack(alignment: .leading, spacing: 10) {
if let tokenUsage = self.model.tokenUsage {
VStack(alignment: .leading, spacing: 6) {
Text("Cost")
Text("cost_header_estimated")
.font(.body)
.fontWeight(.medium)
Text(tokenUsage.sessionLine)
Expand Down Expand Up @@ -1556,7 +1556,7 @@ extension UsageMenuCardView.Model {
return TokenUsageSection(
sessionLine: sessionLine,
monthLine: monthLine,
hintLine: nil,
hintLine: String(localized: "cost_estimate_hint"),
errorLine: err,
errorCopyText: (error?.isEmpty ?? true) ? nil : error)
}
Expand Down
4 changes: 4 additions & 0 deletions Sources/CodexBar/Resources/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -622,3 +622,7 @@
"refresh_5min" = "5 min";
"refresh_15min" = "15 min";
"refresh_30min" = "30 min";

/* Cost estimation */
"cost_header_estimated" = "Cost (estimated)";
"cost_estimate_hint" = "Estimated from local logs · may differ from your bill";
4 changes: 4 additions & 0 deletions Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -602,3 +602,7 @@
"refresh_5min" = "5 min";
"refresh_15min" = "15 min";
"refresh_30min" = "30 min";

/* Cost estimation */
"cost_header_estimated" = "Custo (estimado)";
"cost_estimate_hint" = "Estimado a partir de logs locais · pode diferir da sua fatura";
4 changes: 4 additions & 0 deletions Sources/CodexBar/Resources/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -632,3 +632,7 @@

/* Additional keys */
"not_found" = "未找到";

/* Cost estimation */
"cost_header_estimated" = "费用(估算)";
"cost_estimate_hint" = "根据本地日志估算 · 可能与账单不同";
4 changes: 2 additions & 2 deletions Sources/CodexBarCLI/CLICostCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ extension CodexBarCLI {
useColor: Bool) -> String
{
let name = ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName
let header = Self.costHeaderLine("\(name) Cost (local)", useColor: useColor)
let header = Self.costHeaderLine("\(name) Cost (API-rate estimate)", useColor: useColor)

let todayCost = snapshot.sessionCostUSD.map { UsageFormatter.usdString($0) } ?? "—"
let todayTokens = snapshot.sessionTokens.map { UsageFormatter.tokenCountString($0) }
Expand All @@ -89,7 +89,7 @@ extension CodexBarCLI {
let monthTokens = snapshot.last30DaysTokens.map { UsageFormatter.tokenCountString($0) }
let monthLine = monthTokens.map { "Last 30 days: \(monthCost) · \($0) tokens" } ?? "Last 30 days: \(monthCost)"

return [header, todayLine, monthLine].joined(separator: "\n")
return [header, todayLine, monthLine, UsageFormatter.costEstimateHint].joined(separator: "\n")
}

private static func costHeaderLine(_ header: String, useColor: Bool) -> String {
Expand Down
2 changes: 2 additions & 0 deletions Sources/CodexBarCore/UsageFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public enum UsageFormatter {
value.formatted(.currency(code: "USD").locale(Locale(identifier: "en_US")))
}

public static let costEstimateHint = "Estimated from local logs · may differ from your bill"

/// Formats a currency value with the specified currency code.
/// Uses FormatStyle with explicit en_US locale to ensure consistent formatting
/// regardless of the user's system locale (e.g., pt-BR users see $54.72 not US$ 54,72).
Expand Down
10 changes: 9 additions & 1 deletion Tests/CodexBarTests/CLICostTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ struct CLICostTests {
.replacingOccurrences(of: "\u{00A0}", with: " ")
.replacingOccurrences(of: "$ ", with: "$")

#expect(output.contains("Claude Cost (local)"))
#expect(output.contains("Claude Cost (API-rate estimate)"))
#expect(output.contains("Today: $1.25 · 1.2K tokens"))
#expect(output.contains("Last 30 days: $9.99 · 9K tokens"))
#expect(output.contains(UsageFormatter.costEstimateHint))
}

@Test
Expand Down Expand Up @@ -138,4 +139,11 @@ struct CLICostTests {
#expect(json.contains("\"cost\":0"))
#expect(json.contains("\"totalTokens\":140"))
}

@Test
func `cost estimate hint is stable string`() {
let hint = UsageFormatter.costEstimateHint
#expect(!hint.isEmpty)
#expect(hint.contains("Estimated"))
}
}