Skip to content
Open
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
37 changes: 36 additions & 1 deletion Sources/CodexBar/CodexbarApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct CodexBarApp: App {
@State private var store: UsageStore
@State private var managedCodexAccountCoordinator: ManagedCodexAccountCoordinator
@State private var codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator
@State private var spendDashboardController: SpendDashboardController
private let preferencesSelection: PreferencesSelection
private let account: AccountInfo

Expand Down Expand Up @@ -57,6 +58,9 @@ struct CodexBarApp: App {
let browserDetection = BrowserDetection(cacheTTL: BrowserDetection.defaultCacheTTL)
let account = fetcher.loadAccountInfo()
let store = UsageStore(fetcher: fetcher, browserDetection: browserDetection, settings: settings)
let spendDashboardController = SpendDashboardController(requestBuilder: { mode in
await SpendDashboardSource.makeRequest(settings: settings, store: store, mode: mode)
})
let codexAccountPromotionCoordinator = CodexAccountPromotionCoordinator(
settingsStore: settings,
usageStore: store,
Expand All @@ -66,6 +70,7 @@ struct CodexBarApp: App {
_store = State(wrappedValue: store)
_managedCodexAccountCoordinator = State(wrappedValue: managedCodexAccountCoordinator)
_codexAccountPromotionCoordinator = State(wrappedValue: codexAccountPromotionCoordinator)
_spendDashboardController = State(wrappedValue: spendDashboardController)
self.account = account
CodexBarLog.setLogLevel(settings.debugLogLevel)
self.appDelegate.configure(.init(
Expand All @@ -74,7 +79,8 @@ struct CodexBarApp: App {
account: account,
selection: preferencesSelection,
managedCodexAccountCoordinator: managedCodexAccountCoordinator,
codexAccountPromotionCoordinator: codexAccountPromotionCoordinator))
codexAccountPromotionCoordinator: codexAccountPromotionCoordinator,
spendDashboardController: spendDashboardController))
}

@SceneBuilder
Expand All @@ -91,6 +97,7 @@ struct CodexBarApp: App {
PreferencesView(
settings: self.settings,
store: self.store,
spendDashboardController: self.spendDashboardController,
updater: self.appDelegate.updaterController,
selection: self.preferencesSelection,
managedCodexAccountCoordinator: self.managedCodexAccountCoordinator,
Expand Down Expand Up @@ -353,6 +360,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
let selection: PreferencesSelection
let managedCodexAccountCoordinator: ManagedCodexAccountCoordinator
let codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator
let spendDashboardController: SpendDashboardController?
}

let updaterController: UpdaterProviding = makeUpdaterController()
Expand All @@ -369,6 +377,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
private var preferencesSelection: PreferencesSelection?
private var managedCodexAccountCoordinator: ManagedCodexAccountCoordinator?
private var codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator?
private var spendDashboardController: SpendDashboardController?
private var spendDashboardWarmupTask: Task<Void, Never>?
private var hasInstalledLimitResetObservers = false
#if DEBUG
private var debugMemoryPressureObserver: NSObjectProtocol?
Expand All @@ -384,6 +394,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
self.preferencesSelection = dependencies.selection
self.managedCodexAccountCoordinator = dependencies.managedCodexAccountCoordinator
self.codexAccountPromotionCoordinator = dependencies.codexAccountPromotionCoordinator
self.spendDashboardController = dependencies.spendDashboardController
}

func applicationWillFinishLaunching(_ notification: Notification) {
Expand All @@ -396,6 +407,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
self.installDebugMemoryPressureObserverIfNeeded()
#endif
self.ensureStatusController()
self.scheduleSpendDashboardWarmup()
Task { @MainActor [weak self] in
await Task.yield()
guard let settings = self?.settings else { return }
Expand Down Expand Up @@ -424,6 +436,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
}

func applicationWillTerminate(_ notification: Notification) {
self.spendDashboardWarmupTask?.cancel()
self.spendDashboardWarmupTask = nil
self.spendDashboardController?.stop()
self.memoryPressureMonitor.stop()
#if DEBUG
self.removeDebugMemoryPressureObserver()
Expand All @@ -434,6 +449,26 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
self.terminateActiveProcessesForAppShutdown()
}

private func scheduleSpendDashboardWarmup() {
guard self.spendDashboardWarmupTask == nil,
let controller = self.spendDashboardController,
let settings = self.settings,
let store = self.store
else {
return
}
self.spendDashboardWarmupTask = Task(priority: .utility) { @MainActor [weak self] in
defer { self?.spendDashboardWarmupTask = nil }
do {
try await Task.sleep(for: .seconds(5))
} catch {
return
}
guard !Task.isCancelled, settings.costUsageEnabled else { return }
controller.update(configuration: SpendDashboardSource.configuration(settings: settings, store: store))
}
}

func runProviderLoginFlow(_ provider: UsageProvider) async {
self.ensureStatusController()
guard let statusController else { return }
Expand Down
11 changes: 11 additions & 0 deletions Sources/CodexBar/Localization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,17 @@ func L(_ key: String, language: String) -> String {
return codexBarLocalizedString(key, bundle: bundle, resourceBundle: resourceBundle)
}

/// Force-English variant of `L`. The token-usage dashboard mixes many inline numeric/metric
/// labels that were laid out for English; rendering them in CJK breaks the row widths, so the
/// whole panel pins to English regardless of system language.
func LEn(_ key: String) -> String {
L(key, language: "en")
}

func LEn(_ key: String, _ arguments: CVarArg...) -> String {
String(format: LEn(key), arguments: arguments)
}

func codexBarLocalizedLocale() -> Locale {
let language = resolvedAppLanguage()
guard !language.isEmpty else { return .current }
Expand Down
Loading
Loading