import CodexBarCoreimport Foundation enum PercentWindow: String, CaseIterable, Codable, Hashable, Sendable { case session case weekly case automatic} enum MenuBarLayoutToken: Codable, Hashable, Sendable { case icon case providerName case accountLabel case percent(window: PercentWindow) case usageBar case resetCountdown case resetAbsolute case runsOut case costToday case cost30d case separatorDot case space} enum MenuBarLayoutSemanticWindowResolver { static func windows( provider: UsageProvider, snapshot: UsageSnapshot?) -> (session: RateWindow?, weekly: RateWindow?) { guard let snapshot else { return (nil, nil) } let candidates = [ snapshot.primary, snapshot.secondary, snapshot.tertiary, ] + (snapshot.extraRateWindows ?? []).map(\.window) let usable = candidates.compactMap { window -> RateWindow? in guard let window, !window.isSyntheticPlaceholder else { return nil } return window } let session = usable.first { window in guard let minutes = window.windowMinutes else { return false } return (60...(12 * 60)).contains(minutes) } let cadenceWeekly = usable.first { $0.windowMinutes == 7 * 24 * 60 } let kimiWeekly = snapshot.primary.flatMap { $0.isSyntheticPlaceholder ? nil : $0 } let weekly = provider == .kimi ? kimiWeekly ?? cadenceWeekly : cadenceWeekly return (session, weekly) }} enum MenuBarLayoutCostResolver { static func todayCostUSD( snapshot: CostUsageTokenSnapshot?, now: Date, calendar: Calendar = .current) -> Double? { guard let snapshot else { return nil } return CostUsageTokenSnapshot.entry( in: snapshot.daily, forLocalDayContaining: now, calendar: calendar)?.costUSD }} struct MenuBarLayout: Codable, Hashable, Sendable { static let defaultLayout = MenuBarLayout(lines: [[.icon, .percent(window: .automatic)]]) let lines: [[MenuBarLayoutToken]] init(lines: [[MenuBarLayoutToken]]) { self.lines = Self.normalizedLines(lines) } private enum CodingKeys: String, CodingKey { case lines } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) try self.init(lines: container.decode([[MenuBarLayoutToken]].self, forKey: .lines)) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(self.lines, forKey: .lines) } private static func normalizedLines(_ lines: [[MenuBarLayoutToken]]) -> [[MenuBarLayoutToken]] { guard let firstContentLine = lines.firstIndex(where: { !$0.isEmpty }) else { return self.defaultLayout.lines } return Array(lines[firstContentLine...].prefix(2)) }} enum MenuBarLayoutPreset: String, CaseIterable, Identifiable, Sendable { case iconAndPercent case iconOnly case percentAndReset case compactStacked case custom var id: String { self.rawValue } var layout: MenuBarLayout? { switch self { case .iconAndPercent: MenuBarLayout(lines: [[.icon, .percent(window: .automatic)]]) case .iconOnly: MenuBarLayout(lines: [[.icon]]) case .percentAndReset: MenuBarLayout(lines: [[ .icon, .percent(window: .automatic), .separatorDot, .resetCountdown, ]]) case .compactStacked: MenuBarLayout(lines: [ [.percent(window: .session)], [.percent(window: .weekly)], ]) case .custom: nil } } static func matching(_ layout: MenuBarLayout) -> Self { allCases.first { $0.layout == layout } ?? .custom }} enum MenuBarLayoutSize: String, CaseIterable, Identifiable, Sendable { case small case regular var id: String { self.rawValue }} enum MenuBarLayoutGap: String, CaseIterable, Identifiable, Sendable { case tight case regular var id: String { self.rawValue }} struct MenuBarLayoutResolution: Equatable { struct LegacySettings: Equatable { let iconStyle: MenuBarIconStyle let displayMode: MenuBarDisplayMode let metricPreference: MenuBarMetricPreference let resetTimeDisplayStyle: ResetTimeDisplayStyle } let layout: MenuBarLayout let legacySettings: LegacySettings? var usesLegacyRendering: Bool { self.legacySettings != nil } static func stored(_ layout: MenuBarLayout) -> Self { Self(layout: layout, legacySettings: nil) } static func legacy( iconStyle: MenuBarIconStyle, displayMode: MenuBarDisplayMode, metricPreference: MenuBarMetricPreference, resetTimeDisplayStyle: ResetTimeDisplayStyle, provider: UsageProvider? = nil) -> Self { Self( layout: MenuBarLayout.migrated( iconStyle: iconStyle, displayMode: displayMode, metricPreference: metricPreference, resetTimeDisplayStyle: resetTimeDisplayStyle, provider: provider), legacySettings: LegacySettings( iconStyle: iconStyle, displayMode: displayMode, metricPreference: metricPreference, resetTimeDisplayStyle: resetTimeDisplayStyle)) }} extension MenuBarLayout { static func migrated( iconStyle: MenuBarIconStyle, displayMode: MenuBarDisplayMode, metricPreference: MenuBarMetricPreference, resetTimeDisplayStyle: ResetTimeDisplayStyle, provider: UsageProvider? = nil) -> MenuBarLayout { _ = iconStyle // Critters and bars keep rendering through their unchanged legacy path. let icon: MenuBarLayoutToken = .icon switch displayMode { case .percent: if metricPreference == .primaryAndSecondary { return MenuBarLayout(lines: [[ icon, .percent(window: Self.percentWindow(for: .primary, provider: provider)), .separatorDot, .percent(window: Self.percentWindow(for: .secondary, provider: provider)), ]]) } return MenuBarLayout(lines: [[ icon, .percent(window: Self.percentWindow(for: metricPreference, provider: provider)), ]]) case .pace: return MenuBarLayout(lines: [[icon, .runsOut]]) case .both: return MenuBarLayout(lines: [[ icon, .percent(window: Self.percentWindow(for: metricPreference, provider: provider)), .separatorDot, .runsOut, ]]) case .resetTime: let resetItem = resetTimeDisplayStyle == .absolute ? MenuBarLayoutToken.resetAbsolute : MenuBarLayoutToken.resetCountdown return MenuBarLayout(lines: [[icon, resetItem]]) } } private static func percentWindow( for preference: MenuBarMetricPreference, provider: UsageProvider?) -> PercentWindow { switch preference { case .primary: provider == .kimi ? .weekly : .session case .secondary: provider == .kimi ? .session : .weekly case .automatic, .primaryAndSecondary, .tertiary, .extraUsage, .average, .monthlyPlan: .automatic } }}
import CodexBarCore
import Foundation
enum PercentWindow: String, CaseIterable, Codable, Hashable, Sendable {
case session
case weekly
case automatic
}
enum MenuBarLayoutToken: Codable, Hashable, Sendable {
case icon
case providerName
case accountLabel
case percent(window: PercentWindow)
case usageBar
case resetCountdown
case resetAbsolute
case runsOut
case costToday
case cost30d
case separatorDot
case space
}
enum MenuBarLayoutSemanticWindowResolver {
static func windows(
provider: UsageProvider,
snapshot: UsageSnapshot?)
-> (session: RateWindow?, weekly: RateWindow?)
{
guard let snapshot else { return (nil, nil) }
let candidates = [
snapshot.primary,
snapshot.secondary,
snapshot.tertiary,
] + (snapshot.extraRateWindows ?? []).map(\.window)
let usable = candidates.compactMap { window -> RateWindow? in
guard let window, !window.isSyntheticPlaceholder else { return nil }
return window
}
let session = usable.first { window in
guard let minutes = window.windowMinutes else { return false }
return (60...(12 * 60)).contains(minutes)
}
let cadenceWeekly = usable.first { $0.windowMinutes == 7 * 24 * 60 }
let kimiWeekly = snapshot.primary.flatMap { $0.isSyntheticPlaceholder ? nil : $0 }
let weekly = provider == .kimi ? kimiWeekly ?? cadenceWeekly : cadenceWeekly
return (session, weekly)
}
}
enum MenuBarLayoutCostResolver {
static func todayCostUSD(
snapshot: CostUsageTokenSnapshot?,
now: Date,
calendar: Calendar = .current)
-> Double?
{
guard let snapshot else { return nil }
return CostUsageTokenSnapshot.entry(
in: snapshot.daily,
forLocalDayContaining: now,
calendar: calendar)?.costUSD
}
}
struct MenuBarLayout: Codable, Hashable, Sendable {
static let defaultLayout = MenuBarLayout(lines: [[.icon, .percent(window: .automatic)]])
let lines: [[MenuBarLayoutToken]]
init(lines: [[MenuBarLayoutToken]]) {
self.lines = Self.normalizedLines(lines)
}
private enum CodingKeys: String, CodingKey {
case lines
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
try self.init(lines: container.decode([[MenuBarLayoutToken]].self, forKey: .lines))
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.lines, forKey: .lines)
}
private static func normalizedLines(_ lines: [[MenuBarLayoutToken]]) -> [[MenuBarLayoutToken]] {
guard let firstContentLine = lines.firstIndex(where: { !$0.isEmpty }) else {
return self.defaultLayout.lines
}
return Array(lines[firstContentLine...].prefix(2))
}
}
enum MenuBarLayoutPreset: String, CaseIterable, Identifiable, Sendable {
case iconAndPercent
case iconOnly
case percentAndReset
case compactStacked
case custom
var id: String {
self.rawValue
}
var layout: MenuBarLayout? {
switch self {
case .iconAndPercent:
MenuBarLayout(lines: [[.icon, .percent(window: .automatic)]])
case .iconOnly:
MenuBarLayout(lines: [[.icon]])
case .percentAndReset:
MenuBarLayout(lines: [[
.icon,
.percent(window: .automatic),
.separatorDot,
.resetCountdown,
]])
case .compactStacked:
MenuBarLayout(lines: [
[.percent(window: .session)],
[.percent(window: .weekly)],
])
case .custom:
nil
}
}
static func matching(_ layout: MenuBarLayout) -> Self {
allCases.first { $0.layout == layout } ?? .custom
}
}
enum MenuBarLayoutSize: String, CaseIterable, Identifiable, Sendable {
case small
case regular
var id: String {
self.rawValue
}
}
enum MenuBarLayoutGap: String, CaseIterable, Identifiable, Sendable {
case tight
case regular
var id: String {
self.rawValue
}
}
struct MenuBarLayoutResolution: Equatable {
struct LegacySettings: Equatable {
let iconStyle: MenuBarIconStyle
let displayMode: MenuBarDisplayMode
let metricPreference: MenuBarMetricPreference
let resetTimeDisplayStyle: ResetTimeDisplayStyle
}
let layout: MenuBarLayout
let legacySettings: LegacySettings?
var usesLegacyRendering: Bool {
self.legacySettings != nil
}
static func stored(_ layout: MenuBarLayout) -> Self {
Self(layout: layout, legacySettings: nil)
}
static func legacy(
iconStyle: MenuBarIconStyle,
displayMode: MenuBarDisplayMode,
metricPreference: MenuBarMetricPreference,
resetTimeDisplayStyle: ResetTimeDisplayStyle,
provider: UsageProvider? = nil)
-> Self
{
Self(
layout: MenuBarLayout.migrated(
iconStyle: iconStyle,
displayMode: displayMode,
metricPreference: metricPreference,
resetTimeDisplayStyle: resetTimeDisplayStyle,
provider: provider),
legacySettings: LegacySettings(
iconStyle: iconStyle,
displayMode: displayMode,
metricPreference: metricPreference,
resetTimeDisplayStyle: resetTimeDisplayStyle))
}
}
extension MenuBarLayout {
static func migrated(
iconStyle: MenuBarIconStyle,
displayMode: MenuBarDisplayMode,
metricPreference: MenuBarMetricPreference,
resetTimeDisplayStyle: ResetTimeDisplayStyle,
provider: UsageProvider? = nil)
-> MenuBarLayout
{
_ = iconStyle // Critters and bars keep rendering through their unchanged legacy path.
let icon: MenuBarLayoutToken = .icon
switch displayMode {
case .percent:
if metricPreference == .primaryAndSecondary {
return MenuBarLayout(lines: [[
icon,
.percent(window: Self.percentWindow(for: .primary, provider: provider)),
.separatorDot,
.percent(window: Self.percentWindow(for: .secondary, provider: provider)),
]])
}
return MenuBarLayout(lines: [[
icon,
.percent(window: Self.percentWindow(for: metricPreference, provider: provider)),
]])
case .pace:
return MenuBarLayout(lines: [[icon, .runsOut]])
case .both:
return MenuBarLayout(lines: [[
icon,
.percent(window: Self.percentWindow(for: metricPreference, provider: provider)),
.separatorDot,
.runsOut,
]])
case .resetTime:
let resetItem = resetTimeDisplayStyle == .absolute
? MenuBarLayoutToken.resetAbsolute
: MenuBarLayoutToken.resetCountdown
return MenuBarLayout(lines: [[icon, resetItem]])
}
}
private static func percentWindow(
for preference: MenuBarMetricPreference,
provider: UsageProvider?)
-> PercentWindow
{
switch preference {
case .primary:
provider == .kimi ? .weekly : .session
case .secondary:
provider == .kimi ? .session : .weekly
case .automatic, .primaryAndSecondary, .tertiary, .extraUsage, .average, .monthlyPlan:
.automatic
}
}
}