666 lines · 24.5 KB · Swift
RawDownload
import CodexBarCoreimport SwiftUIimport WidgetKit struct CodexBarUsageWidgetView: View {    @Environment(\.widgetFamily) private var family    let entry: CodexBarWidgetEntry     var body: some View {        let providerEntry = self.entry.snapshot.entries.first { $0.provider == self.entry.provider }        ZStack {            Color.black.opacity(0.02)            if let providerEntry {                self.content(providerEntry: providerEntry)            } else {                self.emptyState            }        }        .containerBackground(.fill.tertiary, for: .widget)    }     @ViewBuilder    private func content(providerEntry: WidgetSnapshot.ProviderEntry) -> some View {        switch self.family {        case .systemSmall:            SmallUsageView(entry: providerEntry)        case .systemMedium:            MediumUsageView(entry: providerEntry)        default:            LargeUsageView(entry: providerEntry)        }    }     private var emptyState: some View {        VStack(alignment: .leading, spacing: 6) {            Text("Open CodexBar")                .font(.body)                .fontWeight(.semibold)            Text("Usage data will appear once the app refreshes.")                .font(.caption)                .foregroundStyle(.secondary)        }        .padding(12)    }} struct CodexBarHistoryWidgetView: View {    @Environment(\.widgetFamily) private var family    let entry: CodexBarWidgetEntry     var body: some View {        let providerEntry = self.entry.snapshot.entries.first { $0.provider == self.entry.provider }        ZStack {            Color.black.opacity(0.02)            if let providerEntry {                HistoryView(entry: providerEntry, isLarge: self.family == .systemLarge)            } else {                self.emptyState            }        }        .containerBackground(.fill.tertiary, for: .widget)    }     private var emptyState: some View {        VStack(alignment: .leading, spacing: 6) {            Text("Open CodexBar")                .font(.body)                .fontWeight(.semibold)            Text("Usage history will appear after a refresh.")                .font(.caption)                .foregroundStyle(.secondary)        }        .padding(12)    }} struct CodexBarCompactWidgetView: View {    let entry: CodexBarCompactEntry     var body: some View {        let providerEntry = self.entry.snapshot.entries.first { $0.provider == self.entry.provider }        ZStack {            Color.black.opacity(0.02)            if let providerEntry {                CompactMetricView(entry: providerEntry, metric: self.entry.metric)            } else {                self.emptyState            }        }        .containerBackground(.fill.tertiary, for: .widget)    }     private var emptyState: some View {        VStack(alignment: .leading, spacing: 6) {            Text("Open CodexBar")                .font(.body)                .fontWeight(.semibold)            Text("Usage data will appear once the app refreshes.")                .font(.caption)                .foregroundStyle(.secondary)        }        .padding(12)    }} struct CodexBarSwitcherWidgetView: View {    @Environment(\.widgetFamily) private var family    let entry: CodexBarSwitcherEntry     var body: some View {        let providerEntry = self.entry.snapshot.entries.first { $0.provider == self.entry.provider }        ZStack {            Color.black.opacity(0.02)            VStack(alignment: .leading, spacing: 10) {                ProviderSwitcherRow(                    providers: self.entry.availableProviders,                    selected: self.entry.provider,                    updatedAt: providerEntry?.updatedAt ?? Date(),                    compact: self.family == .systemSmall,                    showsTimestamp: self.family != .systemSmall)                if let providerEntry {                    self.content(providerEntry: providerEntry)                } else {                    self.emptyState                }            }            .padding(12)        }        .containerBackground(.fill.tertiary, for: .widget)    }     @ViewBuilder    private func content(providerEntry: WidgetSnapshot.ProviderEntry) -> some View {        switch self.family {        case .systemSmall:            SwitcherSmallUsageView(entry: providerEntry)        case .systemMedium:            SwitcherMediumUsageView(entry: providerEntry)        default:            SwitcherLargeUsageView(entry: providerEntry)        }    }     private var emptyState: some View {        VStack(alignment: .leading, spacing: 6) {            Text("Open CodexBar")                .font(.caption)                .foregroundStyle(.secondary)            Text("Usage data appears after a refresh.")                .font(.caption2)                .foregroundStyle(.secondary)        }    }} private struct CompactMetricView: View {    let entry: WidgetSnapshot.ProviderEntry    let metric: CompactMetric     var body: some View {        let display = self.display        VStack(alignment: .leading, spacing: 8) {            HeaderView(provider: self.entry.provider, updatedAt: self.entry.updatedAt)            VStack(alignment: .leading, spacing: 2) {                Text(display.value)                    .font(.title2)                    .fontWeight(.semibold)                Text(display.label)                    .font(.caption2)                    .foregroundStyle(.secondary)                if let detail = display.detail {                    Text(detail)                        .font(.caption2)                        .foregroundStyle(.secondary)                }            }        }        .padding(12)    }     private var display: (value: String, label: String, detail: String?) {        switch self.metric {        case .credits:            let value = self.entry.creditsRemaining.map(WidgetFormat.credits) ?? "—"            return (value, "Credits left", nil)        case .todayCost:            let value = self.entry.tokenUsage?.sessionCostUSD.map(WidgetFormat.usd) ?? "—"            let detail = self.entry.tokenUsage?.sessionTokens.map(WidgetFormat.tokenCount)            return (value, "Today cost", detail)        case .last30DaysCost:            let value = self.entry.tokenUsage?.last30DaysCostUSD.map(WidgetFormat.usd) ?? "—"            let detail = self.entry.tokenUsage?.last30DaysTokens.map(WidgetFormat.tokenCount)            return (value, "30d cost", detail)        }    }} private struct ProviderSwitcherRow: View {    let providers: [UsageProvider]    let selected: UsageProvider    let updatedAt: Date    let compact: Bool    let showsTimestamp: Bool     var body: some View {        HStack(spacing: self.compact ? 4 : 6) {            ForEach(self.providers, id: \.self) { provider in                ProviderSwitchChip(                    provider: provider,                    selected: provider == self.selected,                    compact: self.compact)            }            if self.showsTimestamp {                Spacer(minLength: 6)                Text(WidgetFormat.relativeDate(self.updatedAt))                    .font(.caption2)                    .foregroundStyle(.secondary)            }        }    }} private struct ProviderSwitchChip: View {    let provider: UsageProvider    let selected: Bool    let compact: Bool     var body: some View {        let label = self.compact ? self.shortLabel : self.longLabel        let background = self.selected            ? WidgetColors.color(for: self.provider).opacity(0.2)            : Color.primary.opacity(0.08)         if let choice = ProviderChoice(provider: self.provider) {            Button(intent: SwitchWidgetProviderIntent(provider: choice)) {                Text(label)                    .font(self.compact ? .caption2.weight(.semibold) : .caption.weight(.semibold))                    .foregroundStyle(self.selected ? Color.primary : Color.secondary)                    .padding(.horizontal, self.compact ? 6 : 8)                    .padding(.vertical, self.compact ? 3 : 4)                    .background(Capsule().fill(background))            }            .buttonStyle(.plain)        } else {            Text(label)                .font(self.compact ? .caption2.weight(.semibold) : .caption.weight(.semibold))                .foregroundStyle(self.selected ? Color.primary : Color.secondary)                .padding(.horizontal, self.compact ? 6 : 8)                .padding(.vertical, self.compact ? 3 : 4)                .background(Capsule().fill(background))        }    }     private var longLabel: String {        ProviderDefaults.metadata[self.provider]?.displayName ?? self.provider.rawValue.capitalized    }     private var shortLabel: String {        switch self.provider {        case .codex: "Codex"        case .claude: "Claude"        case .gemini: "Gemini"        case .antigravity: "Anti"        case .cursor: "Cursor"        case .opencode: "OpenCode"        case .zai: "z.ai"        case .factory: "Droid"        case .copilot: "Copilot"        case .minimax: "MiniMax"        case .vertexai: "Vertex"        case .kiro: "Kiro"        case .augment: "Augment"        case .jetbrains: "JetBrains"        case .kimi: "Kimi"        case .kimik2: "Kimi K2"        case .amp: "Amp"        case .ollama: "Ollama"        case .synthetic: "Synthetic"        case .openrouter: "OpenRouter"        case .warp: "Warp"        }    }} private struct SwitcherSmallUsageView: View {    let entry: WidgetSnapshot.ProviderEntry     var body: some View {        VStack(alignment: .leading, spacing: 8) {            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.sessionLabel ?? "Session",                percentLeft: self.entry.primary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.weeklyLabel ?? "Weekly",                percentLeft: self.entry.secondary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            if let codeReview = entry.codeReviewRemainingPercent {                UsageBarRow(                    title: "Code review",                    percentLeft: codeReview,                    color: WidgetColors.color(for: self.entry.provider))            }        }    }} private struct SwitcherMediumUsageView: View {    let entry: WidgetSnapshot.ProviderEntry     var body: some View {        VStack(alignment: .leading, spacing: 10) {            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.sessionLabel ?? "Session",                percentLeft: self.entry.primary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.weeklyLabel ?? "Weekly",                percentLeft: self.entry.secondary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            if let credits = entry.creditsRemaining {                ValueLine(title: "Credits", value: WidgetFormat.credits(credits))            }            if let token = entry.tokenUsage {                ValueLine(                    title: "Today",                    value: WidgetFormat.costAndTokens(cost: token.sessionCostUSD, tokens: token.sessionTokens))            }        }    }} private struct SwitcherLargeUsageView: View {    let entry: WidgetSnapshot.ProviderEntry     var body: some View {        VStack(alignment: .leading, spacing: 12) {            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.sessionLabel ?? "Session",                percentLeft: self.entry.primary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.weeklyLabel ?? "Weekly",                percentLeft: self.entry.secondary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            if let codeReview = entry.codeReviewRemainingPercent {                UsageBarRow(                    title: "Code review",                    percentLeft: codeReview,                    color: WidgetColors.color(for: self.entry.provider))            }            if let credits = entry.creditsRemaining {                ValueLine(title: "Credits", value: WidgetFormat.credits(credits))            }            if let token = entry.tokenUsage {                VStack(alignment: .leading, spacing: 4) {                    ValueLine(                        title: "Today",                        value: WidgetFormat.costAndTokens(cost: token.sessionCostUSD, tokens: token.sessionTokens))                    ValueLine(                        title: "30d",                        value: WidgetFormat.costAndTokens(                            cost: token.last30DaysCostUSD,                            tokens: token.last30DaysTokens))                }            }            UsageHistoryChart(points: self.entry.dailyUsage, color: WidgetColors.color(for: self.entry.provider))                .frame(height: 50)        }    }} private struct SmallUsageView: View {    let entry: WidgetSnapshot.ProviderEntry     var body: some View {        VStack(alignment: .leading, spacing: 8) {            HeaderView(provider: self.entry.provider, updatedAt: self.entry.updatedAt)            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.sessionLabel ?? "Session",                percentLeft: self.entry.primary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.weeklyLabel ?? "Weekly",                percentLeft: self.entry.secondary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            if let codeReview = entry.codeReviewRemainingPercent {                UsageBarRow(                    title: "Code review",                    percentLeft: codeReview,                    color: WidgetColors.color(for: self.entry.provider))            }        }        .padding(12)    }} private struct MediumUsageView: View {    let entry: WidgetSnapshot.ProviderEntry     var body: some View {        VStack(alignment: .leading, spacing: 10) {            HeaderView(provider: self.entry.provider, updatedAt: self.entry.updatedAt)            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.sessionLabel ?? "Session",                percentLeft: self.entry.primary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.weeklyLabel ?? "Weekly",                percentLeft: self.entry.secondary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            if let credits = entry.creditsRemaining {                ValueLine(title: "Credits", value: WidgetFormat.credits(credits))            }            if let token = entry.tokenUsage {                ValueLine(                    title: "Today",                    value: WidgetFormat.costAndTokens(cost: token.sessionCostUSD, tokens: token.sessionTokens))            }        }        .padding(12)    }} private struct LargeUsageView: View {    let entry: WidgetSnapshot.ProviderEntry     var body: some View {        VStack(alignment: .leading, spacing: 12) {            HeaderView(provider: self.entry.provider, updatedAt: self.entry.updatedAt)            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.sessionLabel ?? "Session",                percentLeft: self.entry.primary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            UsageBarRow(                title: ProviderDefaults.metadata[self.entry.provider]?.weeklyLabel ?? "Weekly",                percentLeft: self.entry.secondary?.remainingPercent,                color: WidgetColors.color(for: self.entry.provider))            if let codeReview = entry.codeReviewRemainingPercent {                UsageBarRow(                    title: "Code review",                    percentLeft: codeReview,                    color: WidgetColors.color(for: self.entry.provider))            }            if let credits = entry.creditsRemaining {                ValueLine(title: "Credits", value: WidgetFormat.credits(credits))            }            if let token = entry.tokenUsage {                VStack(alignment: .leading, spacing: 4) {                    ValueLine(                        title: "Today",                        value: WidgetFormat.costAndTokens(cost: token.sessionCostUSD, tokens: token.sessionTokens))                    ValueLine(                        title: "30d",                        value: WidgetFormat.costAndTokens(                            cost: token.last30DaysCostUSD,                            tokens: token.last30DaysTokens))                }            }            UsageHistoryChart(points: self.entry.dailyUsage, color: WidgetColors.color(for: self.entry.provider))                .frame(height: 50)        }        .padding(12)    }} private struct HistoryView: View {    let entry: WidgetSnapshot.ProviderEntry    let isLarge: Bool     var body: some View {        VStack(alignment: .leading, spacing: 12) {            HeaderView(provider: self.entry.provider, updatedAt: self.entry.updatedAt)            UsageHistoryChart(points: self.entry.dailyUsage, color: WidgetColors.color(for: self.entry.provider))                .frame(height: self.isLarge ? 90 : 60)            if let token = entry.tokenUsage {                ValueLine(                    title: "Today",                    value: WidgetFormat.costAndTokens(cost: token.sessionCostUSD, tokens: token.sessionTokens))                ValueLine(                    title: "30d",                    value: WidgetFormat.costAndTokens(cost: token.last30DaysCostUSD, tokens: token.last30DaysTokens))            }        }        .padding(12)    }} private struct HeaderView: View {    let provider: UsageProvider    let updatedAt: Date     var body: some View {        HStack(alignment: .firstTextBaseline) {            Text(ProviderDefaults.metadata[self.provider]?.displayName ?? self.provider.rawValue.capitalized)                .font(.body)                .fontWeight(.semibold)            Spacer()            Text(WidgetFormat.relativeDate(self.updatedAt))                .font(.caption2)

Lines 1–500 of 666.