import AppIntentsimport CodexBarCoreimport SwiftUIimport WidgetKit enum ProviderChoice: String, AppEnum { case codex case claude case gemini case alibaba case alibabatokenplan case antigravity case cursor case zai case copilot case devin case minimax case kilo case opencode case opencodego case mistral case kimi static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Provider") static let caseDisplayRepresentations: [ProviderChoice: DisplayRepresentation] = [ .codex: DisplayRepresentation(title: "Codex"), .claude: DisplayRepresentation(title: "Claude"), .gemini: DisplayRepresentation(title: "Gemini"), .alibaba: DisplayRepresentation(title: "Alibaba"), .alibabatokenplan: DisplayRepresentation(title: "Alibaba Token Plan"), .antigravity: DisplayRepresentation(title: "Antigravity"), .cursor: DisplayRepresentation(title: "Cursor"), .zai: DisplayRepresentation(title: "z.ai"), .copilot: DisplayRepresentation(title: "Copilot"), .devin: DisplayRepresentation(title: "Devin"), .minimax: DisplayRepresentation(title: "MiniMax"), .kilo: DisplayRepresentation(title: "Kilo"), .opencode: DisplayRepresentation(title: "OpenCode"), .opencodego: DisplayRepresentation(title: "OpenCode Go"), .mistral: DisplayRepresentation(title: "Mistral"), .kimi: DisplayRepresentation(title: "Kimi"), ] var provider: UsageProvider { switch self { case .codex: .codex case .claude: .claude case .gemini: .gemini case .alibaba: .alibaba case .alibabatokenplan: .alibabatokenplan case .antigravity: .antigravity case .cursor: .cursor case .zai: .zai case .copilot: .copilot case .devin: .devin case .minimax: .minimax case .kilo: .kilo case .opencode: .opencode case .opencodego: .opencodego case .mistral: .mistral case .kimi: .kimi } } // swiftlint:disable:next cyclomatic_complexity init?(provider: UsageProvider) { switch provider { case .codex: self = .codex case .openai: return nil // OpenAI not yet supported in widgets case .azureopenai: return nil // Azure OpenAI not yet supported in widgets case .claude: self = .claude case .clinepass: return nil // ClinePass not yet supported in widgets case .gemini: self = .gemini case .alibaba: self = .alibaba case .alibabatokenplan: self = .alibabatokenplan case .antigravity: self = .antigravity case .cursor: self = .cursor case .opencode: self = .opencode case .opencodego: self = .opencodego case .zai: self = .zai case .factory: return nil // Factory not yet supported in widgets case .copilot: self = .copilot case .devin: self = .devin case .minimax: self = .minimax case .manus: return nil // Manus not yet supported in widgets case .vertexai: return nil // Vertex AI not yet supported in widgets case .kilo: self = .kilo case .kiro: return nil // Kiro not yet supported in widgets case .augment: return nil // Augment not yet supported in widgets case .jetbrains: return nil // JetBrains not yet supported in widgets case .kimi: self = .kimi case .moonshot: return nil // Moonshot not yet supported in widgets case .amp: return nil // Amp not yet supported in widgets case .t3chat: return nil // T3 Chat not yet supported in widgets case .ollama: return nil // Ollama not yet supported in widgets case .synthetic: return nil // Synthetic not yet supported in widgets case .openrouter: return nil // OpenRouter not yet supported in widgets case .clawrouter: return nil // ClawRouter not yet supported in widgets case .sub2api: return nil // sub2api not yet supported in widgets case .wayfinder: return nil // Wayfinder not yet supported in widgets case .elevenlabs: return nil // ElevenLabs not yet supported in widgets case .warp: return nil // Warp not yet supported in widgets case .windsurf: return nil // Windsurf not yet supported in widgets case .perplexity: return nil // Perplexity not yet supported in widgets case .mimo: return nil // Xiaomi MiMo not yet supported in widgets case .doubao: return nil // Doubao not yet supported in widgets case .sakana: return nil // Sakana AI not yet supported in widgets case .abacus: return nil // Abacus AI not yet supported in widgets case .mistral: self = .mistral case .deepseek: return nil // DeepSeek not yet supported in widgets case .deepinfra: return nil // DeepInfra not yet supported in widgets case .codebuff: return nil // Codebuff not yet supported in widgets case .crof: return nil // Crof not yet supported in widgets case .venice: return nil // Venice not yet supported in widgets case .commandcode: return nil // CommandCode not yet supported in widgets case .qoder: return nil // Qoder not yet supported in widgets case .stepfun: return nil // StepFun not yet supported in widgets case .bedrock: return nil // Bedrock not yet supported in widgets case .grok: return nil // Grok not yet supported in widgets case .groq: return nil // Groq not yet supported in widgets case .llmproxy: return nil // LLM Proxy not yet supported in widgets case .litellm: return nil // LiteLLM not yet supported in widgets case .deepgram: return nil // Deepgram not yet supported in widgets case .poe: return nil // Poe not yet supported in widgets case .chutes: return nil // Chutes not yet supported in widgets case .longcat: return nil // LongCat not yet supported in widgets case .zed: return nil // Zed not yet supported in widgets case .neuralwatt: return nil // Neuralwatt not yet supported in widgets case .zenmux: return nil // ZenMux not yet supported in widgets case .aiand: return nil // ai& not yet supported in widgets } }} enum CompactMetric: String, AppEnum { case credits case todayCost case last30DaysCost static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Metric") static let caseDisplayRepresentations: [CompactMetric: DisplayRepresentation] = [ .credits: DisplayRepresentation(title: "Credits left"), .todayCost: DisplayRepresentation(title: "Today cost"), .last30DaysCost: DisplayRepresentation(title: "30d cost"), ]} struct ProviderSelectionIntent: AppIntent, WidgetConfigurationIntent { static let title: LocalizedStringResource = "Provider" static let description = IntentDescription("Select the provider to display in the widget.") @Parameter(title: "Provider", default: .codex) var provider: ProviderChoice init() { self.provider = .codex }} struct SwitchWidgetProviderIntent: AppIntent { static let title: LocalizedStringResource = "Switch Provider" static let description = IntentDescription("Switch the provider shown in the widget.") @Parameter(title: "Provider") var provider: ProviderChoice init() {} init(provider: ProviderChoice) { self.provider = provider } func perform() async throws -> some IntentResult { WidgetSelectionStore.saveSelectedProvider(self.provider.provider) WidgetCenter.shared.reloadAllTimelines() return .result() }} struct CompactMetricSelectionIntent: AppIntent, WidgetConfigurationIntent { static let title: LocalizedStringResource = "Provider + Metric" static let description = IntentDescription("Select the provider and metric to display.") @Parameter(title: "Provider", default: .codex) var provider: ProviderChoice @Parameter(title: "Metric", default: .credits) var metric: CompactMetric init() { self.provider = .codex self.metric = .credits }} struct CodexBarWidgetEntry: TimelineEntry { let date: Date let provider: UsageProvider let snapshot: WidgetSnapshot} struct CodexBarCompactEntry: TimelineEntry { let date: Date let provider: UsageProvider let metric: CompactMetric let snapshot: WidgetSnapshot} struct CodexBarSwitcherEntry: TimelineEntry { let date: Date let provider: UsageProvider let availableProviders: [UsageProvider] let snapshot: WidgetSnapshot} struct CodexBarTimelineProvider: AppIntentTimelineProvider { func placeholder(in context: Context) -> CodexBarWidgetEntry { CodexBarWidgetEntry( date: Date(), provider: .codex, snapshot: WidgetPreviewData.snapshot()) } func snapshot(for configuration: ProviderSelectionIntent, in context: Context) async -> CodexBarWidgetEntry { let provider = configuration.provider.provider return CodexBarWidgetEntry( date: Date(), provider: provider, snapshot: WidgetSnapshotStore.load() ?? WidgetPreviewData.snapshot()) } func timeline( for configuration: ProviderSelectionIntent, in context: Context) async -> Timeline<CodexBarWidgetEntry> { let provider = configuration.provider.provider let snapshot = WidgetSnapshotStore.load() ?? WidgetPreviewData.emptySnapshot() let now = Date() let entry = CodexBarWidgetEntry(date: now, provider: provider, snapshot: snapshot) let refresh = BurnDownRefreshSchedule.nextRefresh(snapshot: snapshot, provider: provider, now: now) return Timeline(entries: [entry], policy: .after(refresh)) }} struct CodexBarSwitcherTimelineProvider: TimelineProvider { func placeholder(in context: Context) -> CodexBarSwitcherEntry { let snapshot = WidgetPreviewData.snapshot() let providers = self.availableProviders(from: snapshot) return CodexBarSwitcherEntry( date: Date(), provider: providers.first ?? .codex, availableProviders: providers, snapshot: snapshot) } func getSnapshot(in context: Context, completion: @escaping (CodexBarSwitcherEntry) -> Void) { completion(self.makeEntry()) } func getTimeline(in context: Context, completion: @escaping (Timeline<CodexBarSwitcherEntry>) -> Void) { let entry = self.makeEntry() let refresh = BurnDownRefreshSchedule.nextRefresh( snapshot: entry.snapshot, provider: entry.provider, now: entry.date) completion(Timeline(entries: [entry], policy: .after(refresh))) } private func makeEntry() -> CodexBarSwitcherEntry { let snapshot = WidgetSnapshotStore.load() ?? WidgetPreviewData.emptySnapshot() let providers = self.availableProviders(from: snapshot) let stored = WidgetSelectionStore.loadSelectedProvider() let selected = providers.first { $0 == stored } ?? providers.first ?? .codex if selected != stored { WidgetSelectionStore.saveSelectedProvider(selected) } return CodexBarSwitcherEntry( date: Date(), provider: selected, availableProviders: providers, snapshot: snapshot) } private func availableProviders(from snapshot: WidgetSnapshot) -> [UsageProvider] { Self.supportedProviders(from: snapshot) } static func supportedProviders(from snapshot: WidgetSnapshot) -> [UsageProvider] { let enabled = snapshot.enabledProviders let providers = enabled.isEmpty ? snapshot.entries.map(\.provider) : enabled let supported = providers.filter { ProviderChoice(provider: $0) != nil } return supported.isEmpty ? [.codex] : supported }} struct CodexBarCompactTimelineProvider: AppIntentTimelineProvider { func placeholder(in context: Context) -> CodexBarCompactEntry { CodexBarCompactEntry( date: Date(), provider: .codex, metric: .credits, snapshot: WidgetPreviewData.snapshot()) } func snapshot(for configuration: CompactMetricSelectionIntent, in context: Context) async -> CodexBarCompactEntry { let provider = configuration.provider.provider let metric = configuration.metric return CodexBarCompactEntry( date: Date(), provider: provider, metric: metric, snapshot: WidgetSnapshotStore.load() ?? WidgetPreviewData.snapshot()) } func timeline( for configuration: CompactMetricSelectionIntent, in context: Context) async -> Timeline<CodexBarCompactEntry> { let provider = configuration.provider.provider let metric = configuration.metric let snapshot = WidgetSnapshotStore.load() ?? WidgetPreviewData.emptySnapshot() let entry = CodexBarCompactEntry( date: Date(), provider: provider, metric: metric, snapshot: snapshot) let refresh = Date().addingTimeInterval(30 * 60) return Timeline(entries: [entry], policy: .after(refresh)) }} enum WidgetPreviewData { static func emptySnapshot() -> WidgetSnapshot { WidgetSnapshot(entries: [], enabledProviders: [], generatedAt: Date()) } static func snapshot() -> WidgetSnapshot { let primary = RateWindow(usedPercent: 35, windowMinutes: 300, resetsAt: nil, resetDescription: "Resets in 4h") let secondary = RateWindow( usedPercent: 60, windowMinutes: 10080, resetsAt: nil, resetDescription: "Resets in 3d") let entry = WidgetSnapshot.ProviderEntry( provider: .codex, updatedAt: Date(), primary: primary, secondary: secondary, tertiary: nil, creditsRemaining: 1243.4, codeReviewRemainingPercent: 78, tokenUsage: WidgetSnapshot.TokenUsageSummary( sessionCostUSD: 12.4, sessionTokens: 420_000, last30DaysCostUSD: 923.8, last30DaysTokens: 12_400_000), dailyUsage: [ WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-01", totalTokens: 120_000, costUSD: 15.2), WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-02", totalTokens: 80000, costUSD: 10.1), WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-03", totalTokens: 140_000, costUSD: 17.9), WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-04", totalTokens: 90000, costUSD: 11.4), WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-05", totalTokens: 160_000, costUSD: 19.8), WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-06", totalTokens: 70000, costUSD: 8.9), WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-07", totalTokens: 110_000, costUSD: 13.7), ]) return WidgetSnapshot(entries: [entry], generatedAt: Date()) }}
import AppIntents
import CodexBarCore
import SwiftUI
import WidgetKit
enum ProviderChoice: String, AppEnum {
case codex
case claude
case gemini
case alibaba
case alibabatokenplan
case antigravity
case cursor
case zai
case copilot
case devin
case minimax
case kilo
case opencode
case opencodego
case mistral
case kimi
static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Provider")
static let caseDisplayRepresentations: [ProviderChoice: DisplayRepresentation] = [
.codex: DisplayRepresentation(title: "Codex"),
.claude: DisplayRepresentation(title: "Claude"),
.gemini: DisplayRepresentation(title: "Gemini"),
.alibaba: DisplayRepresentation(title: "Alibaba"),
.alibabatokenplan: DisplayRepresentation(title: "Alibaba Token Plan"),
.antigravity: DisplayRepresentation(title: "Antigravity"),
.cursor: DisplayRepresentation(title: "Cursor"),
.zai: DisplayRepresentation(title: "z.ai"),
.copilot: DisplayRepresentation(title: "Copilot"),
.devin: DisplayRepresentation(title: "Devin"),
.minimax: DisplayRepresentation(title: "MiniMax"),
.kilo: DisplayRepresentation(title: "Kilo"),
.opencode: DisplayRepresentation(title: "OpenCode"),
.opencodego: DisplayRepresentation(title: "OpenCode Go"),
.mistral: DisplayRepresentation(title: "Mistral"),
.kimi: DisplayRepresentation(title: "Kimi"),
]
var provider: UsageProvider {
switch self {
case .codex: .codex
case .claude: .claude
case .gemini: .gemini
case .alibaba: .alibaba
case .alibabatokenplan: .alibabatokenplan
case .antigravity: .antigravity
case .cursor: .cursor
case .zai: .zai
case .copilot: .copilot
case .devin: .devin
case .minimax: .minimax
case .kilo: .kilo
case .opencode: .opencode
case .opencodego: .opencodego
case .mistral: .mistral
case .kimi: .kimi
}
}
// swiftlint:disable:next cyclomatic_complexity
init?(provider: UsageProvider) {
switch provider {
case .codex: self = .codex
case .openai: return nil // OpenAI not yet supported in widgets
case .azureopenai: return nil // Azure OpenAI not yet supported in widgets
case .claude: self = .claude
case .clinepass: return nil // ClinePass not yet supported in widgets
case .gemini: self = .gemini
case .alibaba: self = .alibaba
case .alibabatokenplan: self = .alibabatokenplan
case .antigravity: self = .antigravity
case .cursor: self = .cursor
case .opencode: self = .opencode
case .opencodego: self = .opencodego
case .zai: self = .zai
case .factory: return nil // Factory not yet supported in widgets
case .copilot: self = .copilot
case .devin: self = .devin
case .minimax: self = .minimax
case .manus: return nil // Manus not yet supported in widgets
case .vertexai: return nil // Vertex AI not yet supported in widgets
case .kilo: self = .kilo
case .kiro: return nil // Kiro not yet supported in widgets
case .augment: return nil // Augment not yet supported in widgets
case .jetbrains: return nil // JetBrains not yet supported in widgets
case .kimi: self = .kimi
case .moonshot: return nil // Moonshot not yet supported in widgets
case .amp: return nil // Amp not yet supported in widgets
case .t3chat: return nil // T3 Chat not yet supported in widgets
case .ollama: return nil // Ollama not yet supported in widgets
case .synthetic: return nil // Synthetic not yet supported in widgets
case .openrouter: return nil // OpenRouter not yet supported in widgets
case .clawrouter: return nil // ClawRouter not yet supported in widgets
case .sub2api: return nil // sub2api not yet supported in widgets
case .wayfinder: return nil // Wayfinder not yet supported in widgets
case .elevenlabs: return nil // ElevenLabs not yet supported in widgets
case .warp: return nil // Warp not yet supported in widgets
case .windsurf: return nil // Windsurf not yet supported in widgets
case .perplexity: return nil // Perplexity not yet supported in widgets
case .mimo: return nil // Xiaomi MiMo not yet supported in widgets
case .doubao: return nil // Doubao not yet supported in widgets
case .sakana: return nil // Sakana AI not yet supported in widgets
case .abacus: return nil // Abacus AI not yet supported in widgets
case .mistral: self = .mistral
case .deepseek: return nil // DeepSeek not yet supported in widgets
case .deepinfra: return nil // DeepInfra not yet supported in widgets
case .codebuff: return nil // Codebuff not yet supported in widgets
case .crof: return nil // Crof not yet supported in widgets
case .venice: return nil // Venice not yet supported in widgets
case .commandcode: return nil // CommandCode not yet supported in widgets
case .qoder: return nil // Qoder not yet supported in widgets
case .stepfun: return nil // StepFun not yet supported in widgets
case .bedrock: return nil // Bedrock not yet supported in widgets
case .grok: return nil // Grok not yet supported in widgets
case .groq: return nil // Groq not yet supported in widgets
case .llmproxy: return nil // LLM Proxy not yet supported in widgets
case .litellm: return nil // LiteLLM not yet supported in widgets
case .deepgram: return nil // Deepgram not yet supported in widgets
case .poe: return nil // Poe not yet supported in widgets
case .chutes: return nil // Chutes not yet supported in widgets
case .longcat: return nil // LongCat not yet supported in widgets
case .zed: return nil // Zed not yet supported in widgets
case .neuralwatt: return nil // Neuralwatt not yet supported in widgets
case .zenmux: return nil // ZenMux not yet supported in widgets
case .aiand: return nil // ai& not yet supported in widgets
}
}
}
enum CompactMetric: String, AppEnum {
case credits
case todayCost
case last30DaysCost
static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Metric")
static let caseDisplayRepresentations: [CompactMetric: DisplayRepresentation] = [
.credits: DisplayRepresentation(title: "Credits left"),
.todayCost: DisplayRepresentation(title: "Today cost"),
.last30DaysCost: DisplayRepresentation(title: "30d cost"),
]
}
struct ProviderSelectionIntent: AppIntent, WidgetConfigurationIntent {
static let title: LocalizedStringResource = "Provider"
static let description = IntentDescription("Select the provider to display in the widget.")
@Parameter(title: "Provider", default: .codex)
var provider: ProviderChoice
init() {
self.provider = .codex
}
}
struct SwitchWidgetProviderIntent: AppIntent {
static let title: LocalizedStringResource = "Switch Provider"
static let description = IntentDescription("Switch the provider shown in the widget.")
@Parameter(title: "Provider")
var provider: ProviderChoice
init() {}
init(provider: ProviderChoice) {
self.provider = provider
}
func perform() async throws -> some IntentResult {
WidgetSelectionStore.saveSelectedProvider(self.provider.provider)
WidgetCenter.shared.reloadAllTimelines()
return .result()
}
}
struct CompactMetricSelectionIntent: AppIntent, WidgetConfigurationIntent {
static let title: LocalizedStringResource = "Provider + Metric"
static let description = IntentDescription("Select the provider and metric to display.")
@Parameter(title: "Provider", default: .codex)
var provider: ProviderChoice
@Parameter(title: "Metric", default: .credits)
var metric: CompactMetric
init() {
self.provider = .codex
self.metric = .credits
}
}
struct CodexBarWidgetEntry: TimelineEntry {
let date: Date
let provider: UsageProvider
let snapshot: WidgetSnapshot
}
struct CodexBarCompactEntry: TimelineEntry {
let date: Date
let provider: UsageProvider
let metric: CompactMetric
let snapshot: WidgetSnapshot
}
struct CodexBarSwitcherEntry: TimelineEntry {
let date: Date
let provider: UsageProvider
let availableProviders: [UsageProvider]
let snapshot: WidgetSnapshot
}
struct CodexBarTimelineProvider: AppIntentTimelineProvider {
func placeholder(in context: Context) -> CodexBarWidgetEntry {
CodexBarWidgetEntry(
date: Date(),
provider: .codex,
snapshot: WidgetPreviewData.snapshot())
}
func snapshot(for configuration: ProviderSelectionIntent, in context: Context) async -> CodexBarWidgetEntry {
let provider = configuration.provider.provider
return CodexBarWidgetEntry(
date: Date(),
provider: provider,
snapshot: WidgetSnapshotStore.load() ?? WidgetPreviewData.snapshot())
}
func timeline(
for configuration: ProviderSelectionIntent,
in context: Context) async -> Timeline<CodexBarWidgetEntry>
{
let provider = configuration.provider.provider
let snapshot = WidgetSnapshotStore.load() ?? WidgetPreviewData.emptySnapshot()
let now = Date()
let entry = CodexBarWidgetEntry(date: now, provider: provider, snapshot: snapshot)
let refresh = BurnDownRefreshSchedule.nextRefresh(snapshot: snapshot, provider: provider, now: now)
return Timeline(entries: [entry], policy: .after(refresh))
}
}
struct CodexBarSwitcherTimelineProvider: TimelineProvider {
func placeholder(in context: Context) -> CodexBarSwitcherEntry {
let snapshot = WidgetPreviewData.snapshot()
let providers = self.availableProviders(from: snapshot)
return CodexBarSwitcherEntry(
date: Date(),
provider: providers.first ?? .codex,
availableProviders: providers,
snapshot: snapshot)
}
func getSnapshot(in context: Context, completion: @escaping (CodexBarSwitcherEntry) -> Void) {
completion(self.makeEntry())
}
func getTimeline(in context: Context, completion: @escaping (Timeline<CodexBarSwitcherEntry>) -> Void) {
let entry = self.makeEntry()
let refresh = BurnDownRefreshSchedule.nextRefresh(
snapshot: entry.snapshot,
provider: entry.provider,
now: entry.date)
completion(Timeline(entries: [entry], policy: .after(refresh)))
}
private func makeEntry() -> CodexBarSwitcherEntry {
let snapshot = WidgetSnapshotStore.load() ?? WidgetPreviewData.emptySnapshot()
let providers = self.availableProviders(from: snapshot)
let stored = WidgetSelectionStore.loadSelectedProvider()
let selected = providers.first { $0 == stored } ?? providers.first ?? .codex
if selected != stored {
WidgetSelectionStore.saveSelectedProvider(selected)
}
return CodexBarSwitcherEntry(
date: Date(),
provider: selected,
availableProviders: providers,
snapshot: snapshot)
}
private func availableProviders(from snapshot: WidgetSnapshot) -> [UsageProvider] {
Self.supportedProviders(from: snapshot)
}
static func supportedProviders(from snapshot: WidgetSnapshot) -> [UsageProvider] {
let enabled = snapshot.enabledProviders
let providers = enabled.isEmpty ? snapshot.entries.map(\.provider) : enabled
let supported = providers.filter { ProviderChoice(provider: $0) != nil }
return supported.isEmpty ? [.codex] : supported
}
}
struct CodexBarCompactTimelineProvider: AppIntentTimelineProvider {
func placeholder(in context: Context) -> CodexBarCompactEntry {
CodexBarCompactEntry(
date: Date(),
provider: .codex,
metric: .credits,
snapshot: WidgetPreviewData.snapshot())
}
func snapshot(for configuration: CompactMetricSelectionIntent, in context: Context) async -> CodexBarCompactEntry {
let provider = configuration.provider.provider
let metric = configuration.metric
return CodexBarCompactEntry(
date: Date(),
provider: provider,
metric: metric,
snapshot: WidgetSnapshotStore.load() ?? WidgetPreviewData.snapshot())
}
func timeline(
for configuration: CompactMetricSelectionIntent,
in context: Context) async -> Timeline<CodexBarCompactEntry>
{
let provider = configuration.provider.provider
let metric = configuration.metric
let snapshot = WidgetSnapshotStore.load() ?? WidgetPreviewData.emptySnapshot()
let entry = CodexBarCompactEntry(
date: Date(),
provider: provider,
metric: metric,
snapshot: snapshot)
let refresh = Date().addingTimeInterval(30 * 60)
return Timeline(entries: [entry], policy: .after(refresh))
}
}
enum WidgetPreviewData {
static func emptySnapshot() -> WidgetSnapshot {
WidgetSnapshot(entries: [], enabledProviders: [], generatedAt: Date())
}
static func snapshot() -> WidgetSnapshot {
let primary = RateWindow(usedPercent: 35, windowMinutes: 300, resetsAt: nil, resetDescription: "Resets in 4h")
let secondary = RateWindow(
usedPercent: 60,
windowMinutes: 10080,
resetsAt: nil,
resetDescription: "Resets in 3d")
let entry = WidgetSnapshot.ProviderEntry(
provider: .codex,
updatedAt: Date(),
primary: primary,
secondary: secondary,
tertiary: nil,
creditsRemaining: 1243.4,
codeReviewRemainingPercent: 78,
tokenUsage: WidgetSnapshot.TokenUsageSummary(
sessionCostUSD: 12.4,
sessionTokens: 420_000,
last30DaysCostUSD: 923.8,
last30DaysTokens: 12_400_000),
dailyUsage: [
WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-01", totalTokens: 120_000, costUSD: 15.2),
WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-02", totalTokens: 80000, costUSD: 10.1),
WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-03", totalTokens: 140_000, costUSD: 17.9),
WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-04", totalTokens: 90000, costUSD: 11.4),
WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-05", totalTokens: 160_000, costUSD: 19.8),
WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-06", totalTokens: 70000, costUSD: 8.9),
WidgetSnapshot.DailyUsagePoint(dayKey: "2025-12-07", totalTokens: 110_000, costUSD: 13.7),
])
return WidgetSnapshot(entries: [entry], generatedAt: Date())
}
}