973 lines · 36.9 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?             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     init(model: Model, width: CGFloat) {        self.model = model        self.width = width    }     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) { metric in                                VStack(alignment: .leading, spacing: 6) {                                    Text(metric.title)                                        .font(.body)                                        .fontWeight(.medium)                                    UsageProgressBar(                                        percent: metric.percent,                                        tint: self.model.progressColor,                                        accessibilityLabel: metric.percentStyle.accessibilityLabel)                                    HStack(alignment: .firstTextBaseline) {                                        Text(metric.percentLabel)                                            .font(.footnote)                                        Spacer()                                        if let reset = metric.resetText {                                            Text(reset)                                                .font(.footnote)                                                .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                                        }                                    }                                    if let detail = metric.detailText {                                        Text(detail)                                            .font(.footnote)                                            .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                                            .lineLimit(1)                                    }                                }                            }                        }                    }                    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))            }            if self.model.subtitleStyle == .error {                VStack(alignment: .leading, spacing: 4) {                    Text(self.model.subtitleText)                        .font(.footnote)                        .foregroundStyle(self.subtitleColor)                        .lineLimit(6)                        .multilineTextAlignment(.leading)                        .fixedSize(horizontal: false, vertical: true)                        .frame(maxWidth: .infinity, alignment: .leading)                    HStack(alignment: .firstTextBaseline) {                        if !self.model.subtitleText.isEmpty {                            CopyIconButton(copyText: self.model.subtitleText, isHighlighted: self.isHighlighted)                        }                        Spacer()                        if let plan = self.model.planText {                            Text(plan)                                .font(.footnote)                                .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                                .lineLimit(1)                        }                    }                }            } else {                HStack(alignment: .firstTextBaseline) {                    Text(self.model.subtitleText)                        .font(.footnote)                        .foregroundStyle(self.subtitleColor)                        .lineLimit(1)                        .multilineTextAlignment(.leading)                        .fixedSize(horizontal: false, vertical: true)                        .layoutPriority(1)                    Spacer()                    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))            }        }    }} 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) { metric in                    VStack(alignment: .leading, spacing: 6) {                        Text(metric.title)                            .font(.body)                            .fontWeight(.medium)                        UsageProgressBar(                            percent: metric.percent,                            tint: self.model.progressColor,                            accessibilityLabel: metric.percentStyle.accessibilityLabel)                        HStack(alignment: .firstTextBaseline) {                            Text(metric.percentLabel)                                .font(.footnote)                            Spacer()                            if let reset = metric.resetText {                                Text(reset)                                    .font(.footnote)                                    .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                            }                        }                        if let detail = metric.detailText {                            Text(detail)                                .font(.footnote)                                .foregroundStyle(MenuHighlightStyle.secondary(self.isHighlighted))                                .lineLimit(1)                        }                    }                }            }            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     init(        model: UsageMenuCardView.Model,        showBottomDivider: Bool,        topPadding: CGFloat,        bottomPadding: CGFloat,        width: CGFloat)    {        self.model = model        self.showBottomDivider = showBottomDivider        self.topPadding = topPadding        self.bottomPadding = bottomPadding        self.width = width    }     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)

Lines 1–500 of 973.