Files
steipete--codexbar/Sources/CodexBar/MenuCardView+MiniMax.swift
T
2026-07-13 12:22:33 +08:00

143 lines
5.5 KiB
Swift

import CodexBarCore
import Foundation
extension UsageMenuCardView.Model {
static func minimaxMetrics(services: [MiniMaxServiceUsage], input: Input) -> [Metric] {
let percentStyle: PercentStyle = .used
let displayNameCounts = Dictionary(grouping: services.map(\.displayName), by: { $0 }).mapValues(\.count)
return services.enumerated().map { index, service in
let used = service.usage
let displayPercent = min(100, max(0, service.percent))
let usageLabel = if service.isUnlimited {
nil as String?
} else {
String(
format: L("minimax_usage_amount_format"),
used.formatted(),
service.limit.formatted())
}
let localizedName = Self.localizedMiniMaxServiceName(service.displayName)
let title = if (displayNameCounts[service.displayName] ?? 0) > 1 {
"\(localizedName) · \(Self.displayWindowBadge(for: service.windowType))"
} else {
localizedName
}
return Metric(
id: "minimax-service-\(index)",
title: title,
percent: displayPercent,
percentStyle: percentStyle,
statusText: service.isUnlimited ? "∞ Unlimited" : nil,
resetText: Self.localizedMiniMaxResetDescription(service.resetDescription),
detailText: nil,
detailLeftText: usageLabel,
detailRightText: nil,
pacePercent: nil,
paceOnTop: true,
warningMarkerPercents: service.isUnlimited
? []
: Self.miniMaxWarningMarkerPercents(service: service, input: input),
workdayMarkerPercents: service.isUnlimited
? []
: Self.miniMaxWorkdayMarkerPercents(service: service, input: input),
cardStyle: false)
}
}
private static func miniMaxWarningMarkerPercents(service: MiniMaxServiceUsage, input: Input) -> [Double] {
switch self.miniMaxQuotaWarningWindow(for: service) {
case .session:
warningMarkerPercents(
thresholds: input.quotaWarningThresholds[.session],
showUsed: true)
case .weekly:
warningMarkerPercents(
thresholds: input.quotaWarningThresholds[.weekly],
showUsed: true)
}
}
private static func miniMaxWorkdayMarkerPercents(service: MiniMaxServiceUsage, input: Input) -> [Double] {
guard self.miniMaxQuotaWarningWindow(for: service) == .weekly else { return [] }
return workDayMarkerPercents(
workDays: input.workDaysPerWeek,
windowMinutes: self.miniMaxWindowMinutes(for: service.windowType))
}
private static func miniMaxQuotaWarningWindow(for service: MiniMaxServiceUsage) -> QuotaWarningWindow {
service.windowType.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() == "weekly" ? .weekly : .session
}
private static func miniMaxWindowMinutes(for windowType: String) -> Int? {
let normalized = windowType.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
if normalized == "weekly" {
return 7 * 24 * 60
}
if normalized == "today" || normalized == "daily" {
return 24 * 60
}
if normalized == "5h" {
return 5 * 60
}
let pieces = normalized.split(separator: " ")
guard pieces.count >= 2, let value = Int(pieces[0]) else { return nil }
switch pieces[1] {
case "hour", "hours", "hr", "hrs":
return value * 60
case "minute", "minutes", "min", "mins":
return value
default:
return nil
}
}
private static func displayWindowBadge(for windowType: String) -> String {
let trimmed = windowType.trimmingCharacters(in: .whitespacesAndNewlines)
let normalized = trimmed.lowercased()
if normalized == "weekly" {
return L("Weekly")
}
if normalized == "5 hours" || normalized == "5 hour" || normalized == "5h" {
return "5h"
}
if normalized == "today" {
return L("Today")
}
if normalized == "daily" {
return L("Daily")
}
return trimmed.isEmpty ? windowType : trimmed
}
private static func localizedMiniMaxResetDescription(_ text: String) -> String {
let prefix = "Resets in "
guard text.hasPrefix(prefix) else { return text }
let rest = String(text.dropFirst(prefix.count))
return L("Resets in %@", rest)
}
private static func localizedMiniMaxServiceName(_ raw: String) -> String {
switch raw {
case "Text Generation", "text_generation":
L("minimax_service_text_generation")
case "Text to Speech", "text_to_speech":
L("minimax_service_text_to_speech")
case "Music Generation", "music_generation":
L("minimax_service_music_generation")
case "Image Generation", "image_generation":
L("minimax_service_image_generation")
case "lyrics_generation":
L("minimax_service_lyrics_generation")
case "coding-plan-vlm":
L("minimax_service_coding_plan_vlm")
case "coding-plan-search":
L("minimax_service_coding_plan_search")
default:
raw
}
}
}