228 lines · 7.3 KB · Swift
RawDownload
import AppIntentsimport CodexBarCoreimport WidgetKit enum BurnProviderChoice: String, AppEnum {    case codex    case claude     static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Provider")     static let caseDisplayRepresentations: [BurnProviderChoice: DisplayRepresentation] = [        .codex: DisplayRepresentation(title: "Codex"),        .claude: DisplayRepresentation(title: "Claude"),    ]     var provider: UsageProvider {        switch self {        case .codex: .codex        case .claude: .claude        }    }} enum BurnWindowChoice: String, AppEnum {    case session    case weekly     static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Usage window")     static let caseDisplayRepresentations: [BurnWindowChoice: DisplayRepresentation] = [        .session: DisplayRepresentation(title: "Session (5-hour)"),        .weekly: DisplayRepresentation(title: "Weekly (7-day)"),    ]} struct BurnDownSelectionIntent: AppIntent, WidgetConfigurationIntent {    static let title: LocalizedStringResource = "Burn Down"    static let description = IntentDescription("Select the provider and usage window to display.")     @Parameter(title: "Provider", default: .codex)    var provider: BurnProviderChoice     @Parameter(title: "Usage window", default: .session)    var window: BurnWindowChoice     init() {        self.provider = .codex        self.window = .session    }} struct BurnProviderSelectionIntent: AppIntent, WidgetConfigurationIntent {    static let title: LocalizedStringResource = "Burn Down Provider"    static let description = IntentDescription("Select the provider to display.")     @Parameter(title: "Provider", default: .codex)    var provider: BurnProviderChoice     init() {        self.provider = .codex    }} struct BurnDownEntry: TimelineEntry {    let date: Date    let provider: UsageProvider    let window: BurnWindowChoice    let snapshot: WidgetSnapshot} struct CombinedBurnDownEntry: TimelineEntry {    let date: Date    let provider: UsageProvider    let snapshot: WidgetSnapshot} struct BurnDownState {    private static let sessionWindowMinutes = 5 * 60    private static let weeklyWindowMinutes = 7 * 24 * 60     let entry: WidgetSnapshot.ProviderEntry    let selection: BurnWindowChoice    let now: Date     init?(        snapshot: WidgetSnapshot,        provider: UsageProvider,        selection: BurnWindowChoice,        now: Date = Date())    {        guard let entry = snapshot.entries.first(where: { $0.provider == provider }) else { return nil }        self.entry = entry        self.selection = selection        self.now = now    }     var secondaryGloballyCapsPrimary: Bool {        switch self.entry.provider {        case .codex, .claude: true        default: false        }    }     var secondaryExhausted: Bool {        guard self.secondaryGloballyCapsPrimary, let secondary = self.secondaryWindow else { return false }        guard secondary.remainingPercent <= 0 else { return false }        return secondary.resetsAt.map { $0 > self.now } ?? true    }     var primaryWindow: RateWindow? {        guard let primary = self.window(minutes: Self.sessionWindowMinutes) else { return nil }        guard self.secondaryExhausted, primary.remainingPercent > 0 else { return primary }        return RateWindow(            usedPercent: 100,            windowMinutes: primary.windowMinutes,            resetsAt: primary.resetsAt,            resetDescription: primary.resetDescription,            nextRegenPercent: primary.nextRegenPercent)    }     var secondaryWindow: RateWindow? {        self.window(minutes: Self.weeklyWindowMinutes)    }     var selectedWindow: RateWindow? {        switch self.selection {        case .session: self.primaryWindow        case .weekly: self.secondaryWindow        }    }     var blankPrimaryChart: Bool {        self.selection == .session            && self.secondaryExhausted            && self.window(minutes: Self.sessionWindowMinutes) != nil    }     var selectedResetOverride: Date? {        self.blankPrimaryChart ? self.secondaryWindow?.resetsAt : nil    }     private func window(minutes: Int) -> RateWindow? {        [self.entry.primary, self.entry.secondary]            .compactMap(\.self)            .first { $0.windowMinutes == minutes }    }} enum BurnDownRefreshSchedule {    private static let maximumInterval: TimeInterval = 30 * 60     static func nextRefresh(        snapshot: WidgetSnapshot,        provider: UsageProvider,        now: Date = Date()) -> Date    {        let fallback = now.addingTimeInterval(self.maximumInterval)        guard let entry = snapshot.entries.first(where: { $0.provider == provider }) else { return fallback }        let nextReset = [entry.primary?.resetsAt, entry.secondary?.resetsAt]            .compactMap(\.self)            .filter { $0 > now }            .min()?            .addingTimeInterval(1)        return min(fallback, nextReset ?? fallback)    }} struct BurnDownTimelineProvider: AppIntentTimelineProvider {    func placeholder(in context: Context) -> BurnDownEntry {        BurnDownEntry(            date: Date(),            provider: .codex,            window: .session,            snapshot: WidgetPreviewData.snapshot())    }     func snapshot(for configuration: BurnDownSelectionIntent, in context: Context) async -> BurnDownEntry {        BurnDownEntry(            date: Date(),            provider: configuration.provider.provider,            window: configuration.window,            snapshot: WidgetSnapshotStore.load() ?? WidgetPreviewData.snapshot())    }     func timeline(        for configuration: BurnDownSelectionIntent,        in context: Context) async -> Timeline<BurnDownEntry>    {        let entry = BurnDownEntry(            date: Date(),            provider: configuration.provider.provider,            window: configuration.window,            snapshot: WidgetSnapshotStore.load() ?? WidgetPreviewData.emptySnapshot())        let refresh = BurnDownRefreshSchedule.nextRefresh(snapshot: entry.snapshot, provider: entry.provider)        return Timeline(entries: [entry], policy: .after(refresh))    }} struct CombinedBurnDownTimelineProvider: AppIntentTimelineProvider {    func placeholder(in context: Context) -> CombinedBurnDownEntry {        CombinedBurnDownEntry(            date: Date(),            provider: .codex,            snapshot: WidgetPreviewData.snapshot())    }     func snapshot(        for configuration: BurnProviderSelectionIntent,        in context: Context) async -> CombinedBurnDownEntry    {        CombinedBurnDownEntry(            date: Date(),            provider: configuration.provider.provider,            snapshot: WidgetSnapshotStore.load() ?? WidgetPreviewData.snapshot())    }     func timeline(        for configuration: BurnProviderSelectionIntent,        in context: Context) async -> Timeline<CombinedBurnDownEntry>    {        let entry = CombinedBurnDownEntry(            date: Date(),            provider: configuration.provider.provider,            snapshot: WidgetSnapshotStore.load() ?? WidgetPreviewData.emptySnapshot())        let refresh = BurnDownRefreshSchedule.nextRefresh(snapshot: entry.snapshot, provider: entry.provider)        return Timeline(entries: [entry], policy: .after(refresh))    }}