1017 lines · 37.8 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, Sendable {            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 resetText: String?            let detailText: String?            let detailLeftText: String?            let detailRightText: String?            let pacePercent: Double?            let paceOnTop: Bool             var percentLabel: String {                String(format: "%.0f%% %@", self.percent, self.percentStyle.labelSuffix)            }        }         enum SubtitleStyle {            case info            case loading            case error        }         struct TokenUsageSection: Sendable {            let sessionLine: String            let monthLine: String            let hintLine: String?            let errorLine: String?            let errorCopyText: String?        }         struct ProviderCostSection: Sendable {            let title: String            let percentUsed: Double            let spendLine: String        }         let providerName: String        let email: String        let subtitleText: String        let subtitleStyle: SubtitleStyle        let planText: String?        let metrics: [Metric]        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     var body: some View {        VStack(alignment: .leading, spacing: 6) {            UsageMenuCardHeaderView(model: self.model)             if self.hasDetails {                Divider()            }             if self.model.metrics.isEmpty {                if let placeholder = self.model.placeholder {                    Text(placeholder)                        .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                        .font(.subheadline)                }            } else {                let hasUsage = !self.model.metrics.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,                                    progressColor: self.model.progressColor)                            }                        }                    }                    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.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 progressColor: Color    @Environment(\.menuItemHighlighted) private var isHighlighted     var body: some View {        VStack(alignment: .leading, spacing: 6) {            Text(self.metric.title)                .font(.body)                .fontWeight(.medium)            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)                        }                    }                }            }            if let detail = self.metric.detailText {                Text(detail)                    .font(.footnote)                    .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                    .lineLimit(1)            }        }        .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 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,                        progressColor: self.model.progressColor)                }            }            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 {        if let credits = self.model.creditsText {            VStack(alignment: .leading, spacing: 6) {                CreditsBarContent(                    creditsText: credits,                    creditsRemaining: self.model.creditsRemaining,                    hintText: self.model.creditsHintText,                    hintCopyText: self.model.creditsHintCopyText,                    progressColor: self.model.progressColor)                if self.showBottomDivider {                    Divider()                }            }            .padding(.horizontal, 16)            .padding(.top, self.topPadding)            .padding(.bottom, self.bottomPadding)            .frame(width: self.width, alignment: .leading)        }    }} private struct CreditsBarContent: View {    private static let fullScaleTokens: Double = 1000     let creditsText: String    let creditsRemaining: Double?    let hintText: String?    let hintCopyText: String?    let progressColor: Color    @Environment(\.menuItemHighlighted) private var isHighlighted     private var percentLeft: Double? {        guard let creditsRemaining else { return nil }        let percent = (creditsRemaining / Self.fullScaleTokens) * 100        return min(100, max(0, percent))    }     private var scaleText: String {        let scale = UsageFormatter.tokenCountString(Int(Self.fullScaleTokens))        return "\(scale) tokens"    }     var body: some View {        VStack(alignment: .leading, spacing: 6) {            Text("Credits")                .font(.body)                .fontWeight(.medium)            if let percentLeft {                UsageProgressBar(                    percent: percentLeft,                    tint: self.progressColor,                    accessibilityLabel: "Credits remaining")                HStack(alignment: .firstTextBaseline) {                    Text(self.creditsText)                        .font(.caption)                    Spacer()                    Text(self.scaleText)                        .font(.caption)                        .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                }            } else {                Text(self.creditsText)                    .font(.caption)            }            if let hintText, !hintText.isEmpty {                Text(hintText)                    .font(.footnote)                    .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                    .lineLimit(4)                    .fixedSize(horizontal: false, vertical: true)                    .overlay {                        ClickToCopyOverlay(copyText: self.hintCopyText ?? hintText)                    }            }        }    }}

Lines 1–500 of 1017.