1433 lines · 53.5 KB · Swift
RawDownload
import AppKitimport CodexBarCoreimport SwiftUI /// SwiftUI card used inside the NSMenu to mirror Apple's rich menu panels.struct UsageMenuCardView: View {    struct Model {        enum PercentStyle: String {            case left            case used             var labelSuffix: String {                switch self {                case .left: "left"                case .used: "used"                }            }             var accessibilityLabel: String {                switch self {                case .left: "Usage remaining"                case .used: "Usage used"                }            }        }         struct Metric: Identifiable {            let id: String            let title: String            let percent: Double            let percentStyle: PercentStyle            let statusText: String?            let resetText: String?            let detailText: String?            let detailLeftText: String?            let detailRightText: String?            let pacePercent: Double?            let paceOnTop: Bool             init(                id: String,                title: String,                percent: Double,                percentStyle: PercentStyle,                statusText: String? = nil,                resetText: String?,                detailText: String?,                detailLeftText: String?,                detailRightText: String?,                pacePercent: Double?,                paceOnTop: Bool)            {                self.id = id                self.title = title                self.percent = percent                self.percentStyle = percentStyle                self.statusText = statusText                self.resetText = resetText                self.detailText = detailText                self.detailLeftText = detailLeftText                self.detailRightText = detailRightText                self.pacePercent = pacePercent                self.paceOnTop = paceOnTop            }             var percentLabel: String {                String(format: "%.0f%% %@", self.percent, self.percentStyle.labelSuffix)            }        }         enum SubtitleStyle {            case info            case loading            case error        }         struct TokenUsageSection {            let sessionLine: String            let monthLine: String            let hintLine: String?            let errorLine: String?            let errorCopyText: String?        }         struct ProviderCostSection {            let title: String            let percentUsed: Double            let spendLine: String        }         let provider: UsageProvider        let providerName: String        let email: String        let subtitleText: String        let subtitleStyle: SubtitleStyle        let planText: String?        let metrics: [Metric]        let usageNotes: [String]        let creditsText: String?        let creditsRemaining: Double?        let creditsHintText: String?        let creditsHintCopyText: String?        let providerCost: ProviderCostSection?        let tokenUsage: TokenUsageSection?        let placeholder: String?        let progressColor: Color    }     let model: Model    let width: CGFloat    @Environment(\.menuItemHighlighted) private var isHighlighted     static func popupMetricTitle(provider: UsageProvider, metric: Model.Metric) -> String {        if provider == .openrouter, metric.id == "primary" {            return "API key limit"        }        return metric.title    }     var body: some View {        VStack(alignment: .leading, spacing: 6) {            UsageMenuCardHeaderView(model: self.model)             if self.hasDetails {                Divider()            }             if self.model.metrics.isEmpty {                if !self.model.usageNotes.isEmpty {                    UsageNotesContent(notes: self.model.usageNotes)                } else if let placeholder = self.model.placeholder {                    Text(placeholder)                        .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                        .font(.subheadline)                }            } else {                let hasUsage = !self.model.metrics.isEmpty || !self.model.usageNotes.isEmpty                let hasCredits = self.model.creditsText != nil                let hasProviderCost = self.model.providerCost != nil                let hasCost = self.model.tokenUsage != nil || hasProviderCost                 VStack(alignment: .leading, spacing: 12) {                    if hasUsage {                        VStack(alignment: .leading, spacing: 12) {                            ForEach(self.model.metrics, id: \.id) { metric in                                MetricRow(                                    metric: metric,                                    title: Self.popupMetricTitle(provider: self.model.provider, metric: metric),                                    progressColor: self.model.progressColor)                            }                            if !self.model.usageNotes.isEmpty {                                UsageNotesContent(notes: self.model.usageNotes)                            }                        }                    }                    if hasUsage, hasCredits || hasCost {                        Divider()                    }                    if let credits = self.model.creditsText {                        CreditsBarContent(                            creditsText: credits,                            creditsRemaining: self.model.creditsRemaining,                            hintText: self.model.creditsHintText,                            hintCopyText: self.model.creditsHintCopyText,                            progressColor: self.model.progressColor)                    }                    if hasCredits, hasCost {                        Divider()                    }                    if let providerCost = self.model.providerCost {                        ProviderCostContent(                            section: providerCost,                            progressColor: self.model.progressColor)                    }                    if hasProviderCost, self.model.tokenUsage != nil {                        Divider()                    }                    if let tokenUsage = self.model.tokenUsage {                        VStack(alignment: .leading, spacing: 6) {                            Text("Cost")                                .font(.body)                                .fontWeight(.medium)                            Text(tokenUsage.sessionLine)                                .font(.footnote)                            Text(tokenUsage.monthLine)                                .font(.footnote)                            if let hint = tokenUsage.hintLine, !hint.isEmpty {                                Text(hint)                                    .font(.footnote)                                    .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                                    .lineLimit(4)                                    .fixedSize(horizontal: false, vertical: true)                            }                            if let error = tokenUsage.errorLine, !error.isEmpty {                                Text(error)                                    .font(.footnote)                                    .foregroundStyle(MenuHighlightStyle.error(self.isHighlighted))                                    .lineLimit(4)                                    .fixedSize(horizontal: false, vertical: true)                                    .overlay {                                        ClickToCopyOverlay(copyText: tokenUsage.errorCopyText ?? error)                                    }                            }                        }                    }                }                .padding(.bottom, self.model.creditsText == nil ? 6 : 0)            }        }        .padding(.horizontal, 16)        .padding(.top, 2)        .padding(.bottom, 2)        .frame(width: self.width, alignment: .leading)    }     private var hasDetails: Bool {        !self.model.metrics.isEmpty || !self.model.usageNotes.isEmpty || self.model.placeholder != nil ||            self.model.tokenUsage != nil ||            self.model.providerCost != nil    }} private struct UsageMenuCardHeaderView: View {    let model: UsageMenuCardView.Model    @Environment(\.menuItemHighlighted) private var isHighlighted     var body: some View {        VStack(alignment: .leading, spacing: 3) {            HStack(alignment: .firstTextBaseline) {                Text(self.model.providerName)                    .font(.headline)                    .fontWeight(.semibold)                Spacer()                Text(self.model.email)                    .font(.subheadline)                    .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))            }            let subtitleAlignment: VerticalAlignment = self.model.subtitleStyle == .error ? .top : .firstTextBaseline            HStack(alignment: subtitleAlignment) {                Text(self.model.subtitleText)                    .font(.footnote)                    .foregroundStyle(self.subtitleColor)                    .lineLimit(self.model.subtitleStyle == .error ? 4 : 1)                    .multilineTextAlignment(.leading)                    .fixedSize(horizontal: false, vertical: true)                    .layoutPriority(1)                    .padding(.bottom, self.model.subtitleStyle == .error ? 4 : 0)                Spacer()                if self.model.subtitleStyle == .error, !self.model.subtitleText.isEmpty {                    CopyIconButton(copyText: self.model.subtitleText, isHighlighted: self.isHighlighted)                }                if let plan = self.model.planText {                    Text(plan)                        .font(.footnote)                        .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                        .lineLimit(1)                }            }        }    }     private var subtitleColor: Color {        switch self.model.subtitleStyle {        case .info: MenuHighlightStyle.secondary(self.isHighlighted)        case .loading: MenuHighlightStyle.secondary(self.isHighlighted)        case .error: MenuHighlightStyle.error(self.isHighlighted)        }    }} private struct CopyIconButtonStyle: ButtonStyle {    let isHighlighted: Bool     func makeBody(configuration: Configuration) -> some View {        configuration.label            .padding(4)            .background {                RoundedRectangle(cornerRadius: 4, style: .continuous)                    .fill(MenuHighlightStyle.secondary(self.isHighlighted).opacity(configuration.isPressed ? 0.18 : 0))            }            .scaleEffect(configuration.isPressed ? 0.94 : 1)            .animation(.easeOut(duration: 0.12), value: configuration.isPressed)    }} private struct CopyIconButton: View {    let copyText: String    let isHighlighted: Bool     @State private var didCopy = false    @State private var resetTask: Task<Void, Never>?     var body: some View {        Button {            self.copyToPasteboard()            withAnimation(.easeOut(duration: 0.12)) {                self.didCopy = true            }            self.resetTask?.cancel()            self.resetTask = Task { @MainActor in                try? await Task.sleep(for: .seconds(0.9))                withAnimation(.easeOut(duration: 0.2)) {                    self.didCopy = false                }            }        } label: {            Image(systemName: self.didCopy ? "checkmark" : "doc.on.doc")                .font(.caption2.weight(.semibold))                .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                .frame(width: 18, height: 18)        }        .buttonStyle(CopyIconButtonStyle(isHighlighted: self.isHighlighted))        .accessibilityLabel(self.didCopy ? "Copied" : "Copy error")    }     private func copyToPasteboard() {        let pb = NSPasteboard.general        pb.clearContents()        pb.setString(self.copyText, forType: .string)    }} private struct ProviderCostContent: View {    let section: UsageMenuCardView.Model.ProviderCostSection    let progressColor: Color    @Environment(\.menuItemHighlighted) private var isHighlighted     var body: some View {        VStack(alignment: .leading, spacing: 6) {            Text(self.section.title)                .font(.body)                .fontWeight(.medium)            UsageProgressBar(                percent: self.section.percentUsed,                tint: self.progressColor,                accessibilityLabel: "Extra usage spent")            HStack(alignment: .firstTextBaseline) {                Text(self.section.spendLine)                    .font(.footnote)                Spacer()                Text(String(format: "%.0f%% used", min(100, max(0, self.section.percentUsed))))                    .font(.footnote)                    .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))            }        }    }} private struct MetricRow: View {    let metric: UsageMenuCardView.Model.Metric    let title: String    let progressColor: Color    @Environment(\.menuItemHighlighted) private var isHighlighted     var body: some View {        VStack(alignment: .leading, spacing: 6) {            Text(self.title)                .font(.body)                .fontWeight(.medium)            if let statusText = self.metric.statusText {                Text(statusText)                    .font(.footnote)                    .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                    .lineLimit(1)            } else {                UsageProgressBar(                    percent: self.metric.percent,                    tint: self.progressColor,                    accessibilityLabel: self.metric.percentStyle.accessibilityLabel,                    pacePercent: self.metric.pacePercent,                    paceOnTop: self.metric.paceOnTop)                VStack(alignment: .leading, spacing: 2) {                    HStack(alignment: .firstTextBaseline) {                        Text(self.metric.percentLabel)                            .font(.footnote)                            .lineLimit(1)                        Spacer()                        if let rightLabel = self.metric.resetText {                            Text(rightLabel)                                .font(.footnote)                                .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                                .lineLimit(1)                        }                    }                    if self.metric.detailLeftText != nil || self.metric.detailRightText != nil {                        HStack(alignment: .firstTextBaseline) {                            if let detailLeft = self.metric.detailLeftText {                                Text(detailLeft)                                    .font(.footnote)                                    .foregroundStyle(MenuHighlightStyle.primary(self.isHighlighted))                                    .lineLimit(1)                            }                            Spacer()                            if let detailRight = self.metric.detailRightText {                                Text(detailRight)                                    .font(.footnote)                                    .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                                    .lineLimit(1)                            }                        }                    }                }                .frame(maxWidth: .infinity, alignment: .leading)                if let detail = self.metric.detailText {                    Text(detail)                        .font(.footnote)                        .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                        .lineLimit(1)                }            }        }        .frame(maxWidth: .infinity, alignment: .leading)    }} private struct UsageNotesContent: View {    let notes: [String]    @Environment(\.menuItemHighlighted) private var isHighlighted     var body: some View {        VStack(alignment: .leading, spacing: 4) {            ForEach(Array(self.notes.enumerated()), id: \.offset) { _, note in                Text(note)                    .font(.footnote)                    .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                    .lineLimit(2)                    .fixedSize(horizontal: false, vertical: true)            }        }        .frame(maxWidth: .infinity, alignment: .leading)    }} struct UsageMenuCardHeaderSectionView: View {    let model: UsageMenuCardView.Model    let showDivider: Bool    let width: CGFloat     var body: some View {        VStack(alignment: .leading, spacing: 6) {            UsageMenuCardHeaderView(model: self.model)             if self.showDivider {                Divider()            }        }        .padding(.horizontal, 16)        .padding(.top, 2)        .padding(.bottom, self.model.subtitleStyle == .error ? 2 : 0)        .frame(width: self.width, alignment: .leading)    }} struct UsageMenuCardUsageSectionView: View {    let model: UsageMenuCardView.Model    let showBottomDivider: Bool    let bottomPadding: CGFloat    let width: CGFloat    @Environment(\.menuItemHighlighted) private var isHighlighted     var body: some View {        VStack(alignment: .leading, spacing: 12) {            if self.model.metrics.isEmpty {                if !self.model.usageNotes.isEmpty {                    UsageNotesContent(notes: self.model.usageNotes)                } else if let placeholder = self.model.placeholder {                    Text(placeholder)                        .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                        .font(.subheadline)                }            } else {                ForEach(self.model.metrics, id: \.id) { metric in                    MetricRow(                        metric: metric,                        title: UsageMenuCardView.popupMetricTitle(provider: self.model.provider, metric: metric),                        progressColor: self.model.progressColor)                }                if !self.model.usageNotes.isEmpty {                    UsageNotesContent(notes: self.model.usageNotes)                }            }            if self.showBottomDivider {                Divider()            }        }        .padding(.horizontal, 16)        .padding(.top, 10)        .padding(.bottom, self.bottomPadding)        .frame(width: self.width, alignment: .leading)    }} struct UsageMenuCardCreditsSectionView: View {    let model: UsageMenuCardView.Model    let showBottomDivider: Bool    let topPadding: CGFloat    let bottomPadding: CGFloat    let width: CGFloat     var body: some View {

Lines 1–500 of 1433.