import CodexBarCore import Foundation #if canImport(Glibc) import Glibc #elseif canImport(Musl) import Musl #elseif canImport(Darwin) import Darwin #endif struct CLICardMetric: Sendable, Equatable { let label: String let remainingPercent: Double let resetText: String? let resetAt: Date? let detailText: String? init( label: String, remainingPercent: Double, resetText: String?, resetAt: Date? = nil, detailText: String? = nil) { self.label = label self.remainingPercent = remainingPercent self.resetText = resetText self.resetAt = resetAt self.detailText = detailText } } struct CLICardModel: Sendable, Equatable { let provider: UsageProvider let title: String let sourceLabel: String let planBadge: String? let accountLine: String? let infoLines: [String] let metrics: [CLICardMetric] let extraLines: [String] let statusLine: String? } struct CLICardFailure: Sendable, Equatable { let provider: UsageProvider let accountLabel: String? let message: String } struct CLICardBuildInput: Sendable { let provider: UsageProvider let snapshot: UsageSnapshot let credits: CreditsSnapshot? let source: String let status: ProviderStatusPayload? let notes: [String] let useColor: Bool let resetStyle: ResetTimeDisplayStyle let weeklyWorkDays: Int? let now: Date } enum CLICardsRenderer { static let minCardWidth = 38 static let maxCardWidth = 42 static let cardGap = 2 static func terminalColumnCount() -> Int { if let value = terminalColumnCountFromTTY(), value > 0 { return value } if let columns = ProcessInfo.processInfo.environment["COLUMNS"], let value = Int(columns.trimmingCharacters(in: .whitespacesAndNewlines)), value > 0 { return value } return 80 } private static func terminalColumnCountFromTTY(fileDescriptor: Int32 = STDOUT_FILENO) -> Int? { guard isatty(fileDescriptor) == 1 else { return nil } var windowSize = winsize(ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0) guard ioctl(fileDescriptor, UInt(TIOCGWINSZ), &windowSize) == 0 else { return nil } let columns = Int(windowSize.ws_col) return columns > 0 ? columns : nil } static func columnCount(terminalWidth: Int, minCardWidth: Int = Self.minCardWidth) -> Int { let usable = max(minCardWidth, terminalWidth) return max(1, (usable + Self.cardGap) / (minCardWidth + Self.cardGap)) } static func cardWidth(terminalWidth: Int, columns: Int) -> Int { let totalGaps = (columns - 1) * Self.cardGap let availableWidth = max(1, (terminalWidth - totalGaps) / columns) return min(Self.maxCardWidth, availableWidth) } static func makeCard(_ input: CLICardBuildInput) -> CLICardModel { let provider = input.provider let snapshot = input.snapshot let displayName = ProviderDescriptorRegistry.descriptor(for: provider).metadata.displayName let context = RenderContext( header: displayName, status: input.status, useColor: input.useColor, resetStyle: input.resetStyle, weeklyWorkDays: input.weeklyWorkDays, notes: input.notes) let infoLines = CLIRenderer.collectCardInfoLines( provider: provider, snapshot: snapshot, credits: input.credits, notes: input.notes, useColor: input.useColor, now: input.now) let metrics = CLIRenderer.collectCardMetrics( provider: provider, snapshot: snapshot, resetStyle: input.resetStyle, now: input.now) let extraLines = CLIRenderer.collectCardExtraLines( provider: provider, snapshot: snapshot, credits: input.credits, context: context, now: input.now) let statusLine: String? if let status = input.status { let line = "Status: \(status.indicator.label)\(status.descriptionSuffix)" statusLine = CLIRenderer.colorizeStatusLine(line, indicator: status.indicator, useColor: input.useColor) } else { statusLine = nil } return CLICardModel( provider: provider, title: displayName, sourceLabel: Self.normalizedSourceLabel(input.source), planBadge: CLIRenderer.planBadgeText(provider: provider, snapshot: snapshot), accountLine: snapshot.accountEmail(for: provider), infoLines: infoLines, metrics: metrics, extraLines: extraLines, statusLine: statusLine) } static func render( cards: [CLICardModel], failures: [CLICardFailure], terminalWidth: Int, useColor: Bool, enhanced: Bool = false) -> String { guard !cards.isEmpty else { return self.renderFailuresOnly(failures, useColor: useColor) } let columns = Self.columnCount(terminalWidth: terminalWidth) let width = Self.cardWidth(terminalWidth: terminalWidth, columns: columns) var chunks: [String] = [] for rowStart in stride(from: 0, to: cards.count, by: columns) { let rowCards = Array(cards[rowStart.. String in if lineIndex < lines.count - 1 { return lines[lineIndex] } if lineIndex == rowHeight - 1, let bottom = lines.last { return bottom } return Self.emptyCardLine(width: width, useColor: useColor, enhanced: enhanced) } chunks.append(parts.joined(separator: String(repeating: " ", count: Self.cardGap))) } if rowStart + columns < cards.count { chunks.append("") } } if !failures.isEmpty { if !chunks.isEmpty { chunks.append("") } chunks.append(Self.renderFailureFooter(failures: failures, useColor: useColor)) } return chunks.joined(separator: "\n") } static func renderCard(_ card: CLICardModel, width: Int, useColor: Bool, enhanced: Bool = false) -> [String] { let innerWidth = max(12, width - 4) var lines: [String] = [] lines.append(Self.boxLine(kind: .top, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) lines.append(Self.headerLine(card: card, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) if let account = card.accountLine?.trimmingCharacters(in: .whitespacesAndNewlines), !account.isEmpty { let accountText = "@ \(account)" lines.append(Self.contentLine( accountText, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced, style: .subtle)) } lines.append(Self.separatorLine(innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) for infoLine in card.infoLines { lines.append(Self.detailLine( infoLine, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) } if !card.metrics.isEmpty, !card.infoLines.isEmpty { lines.append(Self.contentLine("", innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) } for (index, metric) in card.metrics.enumerated() { if index > 0 { lines.append(Self.contentLine("", innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) } lines.append(Self.metricLabelLine( metric: metric, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) lines.append(Self.metricBarLine( metric: metric, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) if let resetText = metric.resetText { lines.append(Self.contentLine( resetText, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced, style: .subtle)) } if let detailText = metric.detailText { lines.append(Self.contentLine( detailText, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced, style: .subtle)) } } for extraLine in card.extraLines { lines.append(Self.detailLine(extraLine, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) } if let statusLine = card.statusLine { lines.append(Self.contentLine(statusLine, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) } lines.append(Self.boxLine(kind: .bottom, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced)) return lines } private enum BoxLineKind { case top case bottom } private enum ContentStyle: Equatable { case normal case subtle case border } private static func boxLine(kind: BoxLineKind, innerWidth: Int, useColor: Bool, enhanced: Bool) -> String { let chars = switch kind { case .top: ("╭", "╮") case .bottom: ("╰", "╯") } let line = chars.0 + String(repeating: "─", count: innerWidth + 2) + chars.1 return Self.styleBorder(line, useColor: useColor, enhanced: enhanced) } private static func headerLine(card: CLICardModel, innerWidth: Int, useColor: Bool, enhanced: Bool) -> String { let title: String let badge: String if useColor, enhanced { title = CLIRenderer.colorizeEnhancedAccentBold(card.title) badge = CLIRenderer.colorizeEnhancedBadge(card.sourceLabel) } else if useColor { title = CLIRenderer.colorizeAccentBold(card.title) badge = CLIRenderer.colorizeCardBadge(card.sourceLabel) } else { title = card.title badge = "[\(card.sourceLabel)]" } let left = "\(title) \(badge)" let leftVisible = Self.visibleLength(left) let rawPlanText = card.planBadge.map { "PLAN \($0)" } ?? "" let maxPlanWidth = max(0, innerWidth - leftVisible - 1) let planText = maxPlanWidth >= 8 ? Self.truncatePlain(rawPlanText, width: maxPlanWidth) : "" let planVisible = Self.visibleLength(planText) let gap = max(1, innerWidth - leftVisible - planVisible) let plan = Self.planPill(text: planText, useColor: useColor, enhanced: enhanced) let content = left + String(repeating: " ", count: gap) + plan return Self.sideBorder(content, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced) } private static func planPill(text: String, useColor: Bool, enhanced: Bool) -> String { guard !text.isEmpty else { return "" } let pieces = text.split(separator: " ", maxSplits: 1).map(String.init) guard pieces.count == 2 else { return useColor ? CLIRenderer.colorizeCardPlanBox(text) : text } if useColor, enhanced { return CLIRenderer.colorizeEnhancedPlanLabel(pieces[0]) + " " + CLIRenderer.colorizeEnhancedPlanValue(pieces[1]) } if useColor { return CLIRenderer.colorizeCardPlanBox(pieces[0]) + " " + CLIRenderer.colorizeWarning(pieces[1]) } return text } private static func separatorLine(innerWidth: Int, useColor: Bool, enhanced: Bool) -> String { self.sideBorder( String(repeating: "─", count: innerWidth), innerWidth: innerWidth, useColor: useColor, enhanced: enhanced, contentStyle: .border) } private static func metricLabelLine( metric: CLICardMetric, innerWidth: Int, useColor: Bool, enhanced: Bool) -> String { let percentText = UsageFormatter.usageLine( remaining: metric.remainingPercent, used: 100 - metric.remainingPercent, showUsed: false) let coloredPercent: String = if useColor, enhanced { CLIRenderer.colorizeEnhancedRemainingPercent(percentText, remainingPercent: metric.remainingPercent) } else { CLIRenderer.colorizeCardPercent( percentText, remainingPercent: metric.remainingPercent, useColor: useColor) } let label: String = if useColor, enhanced { CLIRenderer.colorizeEnhancedReadable(metric.label) } else if useColor { CLIRenderer.colorizeReadable(metric.label) } else { metric.label } let gap = max(1, innerWidth - Self.visibleLength(label) - Self.visibleLength(coloredPercent)) let content = label + String(repeating: " ", count: gap) + coloredPercent return Self.sideBorder(content, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced) } private static func metricBarLine( metric: CLICardMetric, innerWidth: Int, useColor: Bool, enhanced: Bool) -> String { let barWidth = max(4, innerWidth - 4) let bar: String = if useColor, enhanced { CLIRenderer.gradientRemainingTrackBar(remainingPercent: metric.remainingPercent, width: barWidth) } else { CLIRenderer.cardBlockBar( remainingPercent: metric.remainingPercent, width: barWidth, useColor: useColor) } return Self.sideBorder("[ \(bar) ]", innerWidth: innerWidth, useColor: useColor, enhanced: enhanced) } private static func detailLine(_ content: String, innerWidth: Int, useColor: Bool, enhanced: Bool) -> String { let normalized = Self.normalizeGlyphs(content) let plain = TextParsing.stripANSICodes(normalized) let parts = plain.split(separator: ":", maxSplits: 1).map(String.init) guard parts.count == 2 else { return Self.contentLine(normalized, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced) } let rawLabel = parts[0].trimmingCharacters(in: .whitespacesAndNewlines) + ":" let rawValue = parts[1].trimmingCharacters(in: .whitespacesAndNewlines) let label: String = if useColor, enhanced { CLIRenderer.colorizeEnhancedReadable(rawLabel) } else if useColor { CLIRenderer.colorizeReadable(rawLabel) } else { rawLabel } let value: String = if useColor, enhanced { CLIRenderer.colorizeEnhancedGood(rawValue) } else if useColor { CLIRenderer.colorizeAccent(rawValue) } else { rawValue } let gap = max(1, innerWidth - Self.visibleLength(label) - Self.visibleLength(value)) let line = label + String(repeating: " ", count: gap) + value return Self.sideBorder(line, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced) } private static func contentLine( _ content: String, innerWidth: Int, useColor: Bool, enhanced: Bool, style: ContentStyle = .normal) -> String { let normalized = Self.normalizeGlyphs(content) let stripped = TextParsing.stripANSICodes(normalized) let clipped = stripped.count <= innerWidth ? normalized : (innerWidth <= 1 ? String(stripped.prefix(innerWidth)) : String(stripped.prefix(innerWidth - 1)) + "…") let display: String = if style == .subtle, useColor, enhanced { CLIRenderer.colorizeEnhancedSubtle(TextParsing.stripANSICodes(clipped)) } else if style == .subtle, useColor { CLIRenderer.colorizeSubtle(TextParsing.stripANSICodes(clipped)) } else { clipped } return Self.sideBorder(display, innerWidth: innerWidth, useColor: useColor, enhanced: enhanced) } private static func sideBorder( _ content: String, innerWidth: Int, useColor: Bool, enhanced: Bool, contentStyle: ContentStyle = .normal) -> String { let fitted = Self.fitContent(content, width: innerWidth) let padding = max(0, innerWidth - Self.visibleLength(fitted)) let padded = fitted + String(repeating: " ", count: padding) let visible = "│ \(padded) │" guard useColor else { return visible } let left = Self.styleBorder("│ ", useColor: useColor, enhanced: enhanced) let right = Self.styleBorder(" │", useColor: useColor, enhanced: enhanced) let styledContent: String = if contentStyle == .border { Self.styleBorder(padded, useColor: useColor, enhanced: enhanced) } else { padded } return left + styledContent + right } private static func styleBorder(_ text: String, useColor: Bool, enhanced: Bool) -> String { guard useColor else { return text } if enhanced { return CLIRenderer.colorizeEnhancedBorder(text) } return CLIRenderer.colorizeCardBorder(text) } private static func emptyCardLine(width: Int, useColor: Bool, enhanced: Bool) -> String { let innerWidth = max(12, width - 4) return Self.sideBorder("", innerWidth: innerWidth, useColor: useColor, enhanced: enhanced) } private static func visibleLength(_ text: String) -> Int { TextParsing.stripANSICodes(self.normalizeGlyphs(text)).count } private static func truncatePlain(_ text: String, width: Int) -> String { guard width > 0 else { return "" } guard text.count > width else { return text } if width <= 1 { return String(text.prefix(width)) } return String(text.prefix(width - 1)) + "…" } private static func fitContent(_ text: String, width: Int) -> String { guard self.visibleLength(text) > width else { return text } return self.truncatePlain(TextParsing.stripANSICodes(text), width: width) } private static func normalizeGlyphs(_ text: String) -> String { text .replacingOccurrences(of: "👤", with: "@") .replacingOccurrences(of: "⏳ Resets in ", with: "Reset in ") .replacingOccurrences(of: "⏳ Resets ", with: "Reset ") .replacingOccurrences(of: "⏳ ", with: "Reset ") } static func renderFailureFooter(failures: [CLICardFailure], useColor: Bool) -> String { var lines = ["Failed providers:"] for failure in failures { let name = ProviderDescriptorRegistry.descriptor(for: failure.provider).metadata.displayName if let account = failure.accountLabel, !account.isEmpty { lines.append(" - \(name) (\(account)): \(failure.message)") } else { lines.append(" - \(name): \(failure.message)") } } let text = lines.joined(separator: "\n") guard useColor else { return text } return CLIRenderer.colorizeError(text) } static func renderFailuresOnly(_ failures: [CLICardFailure], useColor: Bool) -> String { guard !failures.isEmpty else { return "" } return self.renderFailureFooter(failures: failures, useColor: useColor) } private static func normalizedSourceLabel(_ source: String) -> String { let trimmed = source.trimmingCharacters(in: .whitespacesAndNewlines) if trimmed.isEmpty { return "auto" } if trimmed.contains("oauth") { return "oauth" } if trimmed.contains("web") || trimmed.contains("openai-web") { return "web" } if trimmed.contains("api") { return "api" } if trimmed.contains("cli") { return "cli" } return trimmed } }