diff --git a/Sources/CodexBar/Providers/Shared/ProviderImplementationManifest.swift b/Sources/CodexBar/Providers/Shared/ProviderImplementationManifest.swift new file mode 100644 index 0000000000..f0f858b721 --- /dev/null +++ b/Sources/CodexBar/Providers/Shared/ProviderImplementationManifest.swift @@ -0,0 +1,75 @@ +import CodexBarCore +import Foundation + +/// Closed first-party bootstrap list. Runtime/plugin registrations remain owned by +/// `ProviderImplementationRegistry.register(_:)`. +enum ProviderImplementationManifest { + static let makeImplementations: [@Sendable () -> any ProviderImplementation] = [ + { CodexProviderImplementation() }, + { OpenAIAPIProviderImplementation() }, + { AzureOpenAIProviderImplementation() }, + { ClaudeProviderImplementation() }, + { ClinePassProviderImplementation() }, + { CursorProviderImplementation() }, + { OpenCodeProviderImplementation() }, + { OpenCodeGoProviderImplementation() }, + { AlibabaCodingPlanProviderImplementation() }, + { AlibabaTokenPlanProviderImplementation() }, + { QwenCloudProviderImplementation() }, + { FactoryProviderImplementation() }, + { GeminiProviderImplementation() }, + { AntigravityProviderImplementation() }, + { CopilotProviderImplementation() }, + { DevinProviderImplementation() }, + { ZaiProviderImplementation() }, + { MiniMaxProviderImplementation() }, + { ManusProviderImplementation() }, + { KimiProviderImplementation() }, + { KiloProviderImplementation() }, + { KiroProviderImplementation() }, + { VertexAIProviderImplementation() }, + { AugmentProviderImplementation() }, + { JetBrainsProviderImplementation() }, + { MoonshotProviderImplementation() }, + { AmpProviderImplementation() }, + { T3ChatProviderImplementation() }, + { OllamaProviderImplementation() }, + { SyntheticProviderImplementation() }, + { OpenRouterProviderImplementation() }, + { ElevenLabsProviderImplementation() }, + { WarpProviderImplementation() }, + { WindsurfProviderImplementation() }, + { ZedProviderImplementation() }, + { PerplexityProviderImplementation() }, + { MiMoProviderImplementation() }, + { DoubaoProviderImplementation() }, + { SakanaProviderImplementation() }, + { AbacusProviderImplementation() }, + { MistralProviderImplementation() }, + { DeepSeekProviderImplementation() }, + { DeepInfraProviderImplementation() }, + { CodebuffProviderImplementation() }, + { CrofProviderImplementation() }, + { VeniceProviderImplementation() }, + { CommandCodeProviderImplementation() }, + { QoderProviderImplementation() }, + { StepFunProviderImplementation() }, + { BedrockProviderImplementation() }, + { GrokProviderImplementation() }, + { GroqProviderImplementation() }, + { LLMProxyProviderImplementation() }, + { LiteLLMProviderImplementation() }, + { DeepgramProviderImplementation() }, + { PoeProviderImplementation() }, + { ChutesProviderImplementation() }, + { NeuralWattProviderImplementation() }, + { ClawRouterProviderImplementation() }, + { LongCatProviderImplementation() }, + { Sub2APIProviderImplementation() }, + { WayfinderProviderImplementation() }, + { ZenMuxProviderImplementation() }, + { AiAndProviderImplementation() }, + { ZoomMateProviderImplementation() }, + { XAIProviderImplementation() }, + ] +} diff --git a/Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift b/Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift index c811590992..7805ca8b16 100644 --- a/Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift +++ b/Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift @@ -10,81 +10,9 @@ enum ProviderImplementationRegistry { private static let lock = NSLock() private static let store = Store() - // swiftlint:disable:next cyclomatic_complexity - private static func makeImplementation(for provider: UsageProvider) -> (any ProviderImplementation) { - switch provider { - case .codex: CodexProviderImplementation() - case .openai: OpenAIAPIProviderImplementation() - case .azureopenai: AzureOpenAIProviderImplementation() - case .claude: ClaudeProviderImplementation() - case .clinepass: ClinePassProviderImplementation() - case .cursor: CursorProviderImplementation() - case .opencode: OpenCodeProviderImplementation() - case .opencodego: OpenCodeGoProviderImplementation() - case .alibaba: AlibabaCodingPlanProviderImplementation() - case .alibabatokenplan: AlibabaTokenPlanProviderImplementation() - case .qwencloud: QwenCloudProviderImplementation() - case .factory: FactoryProviderImplementation() - case .gemini: GeminiProviderImplementation() - case .antigravity: AntigravityProviderImplementation() - case .copilot: CopilotProviderImplementation() - case .devin: DevinProviderImplementation() - case .zai: ZaiProviderImplementation() - case .minimax: MiniMaxProviderImplementation() - case .manus: ManusProviderImplementation() - case .kimi: KimiProviderImplementation() - case .kilo: KiloProviderImplementation() - case .kiro: KiroProviderImplementation() - case .vertexai: VertexAIProviderImplementation() - case .augment: AugmentProviderImplementation() - case .jetbrains: JetBrainsProviderImplementation() - case .moonshot: MoonshotProviderImplementation() - case .amp: AmpProviderImplementation() - case .t3chat: T3ChatProviderImplementation() - case .ollama: OllamaProviderImplementation() - case .synthetic: SyntheticProviderImplementation() - case .openrouter: OpenRouterProviderImplementation() - case .elevenlabs: ElevenLabsProviderImplementation() - case .warp: WarpProviderImplementation() - case .windsurf: WindsurfProviderImplementation() - case .zed: ZedProviderImplementation() - case .perplexity: PerplexityProviderImplementation() - case .mimo: MiMoProviderImplementation() - case .doubao: DoubaoProviderImplementation() - case .sakana: SakanaProviderImplementation() - case .abacus: AbacusProviderImplementation() - case .mistral: MistralProviderImplementation() - case .deepseek: DeepSeekProviderImplementation() - case .deepinfra: DeepInfraProviderImplementation() - case .codebuff: CodebuffProviderImplementation() - case .crof: CrofProviderImplementation() - case .venice: VeniceProviderImplementation() - case .commandcode: CommandCodeProviderImplementation() - case .qoder: QoderProviderImplementation() - case .stepfun: StepFunProviderImplementation() - case .bedrock: BedrockProviderImplementation() - case .grok: GrokProviderImplementation() - case .groq: GroqProviderImplementation() - case .llmproxy: LLMProxyProviderImplementation() - case .litellm: LiteLLMProviderImplementation() - case .deepgram: DeepgramProviderImplementation() - case .poe: PoeProviderImplementation() - case .chutes: ChutesProviderImplementation() - case .neuralwatt: NeuralWattProviderImplementation() - case .clawrouter: ClawRouterProviderImplementation() - case .longcat: LongCatProviderImplementation() - case .sub2api: Sub2APIProviderImplementation() - case .wayfinder: WayfinderProviderImplementation() - case .zenmux: ZenMuxProviderImplementation() - case .aiand: AiAndProviderImplementation() - case .zoommate: ZoomMateProviderImplementation() - case .xai: XAIProviderImplementation() - } - } - private static let bootstrap: Void = { - for provider in UsageProvider.allCases { - _ = ProviderImplementationRegistry.register(makeImplementation(for: provider)) + for makeImplementation in ProviderImplementationManifest.makeImplementations { + _ = ProviderImplementationRegistry.register(makeImplementation()) } }() @@ -112,7 +40,9 @@ enum ProviderImplementationRegistry { static func implementation(for id: UsageProvider) -> (any ProviderImplementation)? { self.ensureBootstrapped() - if let found = self.store.byID[id] { return found } + if let found = self.store.byID[id] { + return found + } return self.all.first(where: { $0.id == id }) } } diff --git a/Sources/CodexBar/UsageStore.swift b/Sources/CodexBar/UsageStore.swift index c119c1ed9b..d471a8606f 100644 --- a/Sources/CodexBar/UsageStore.swift +++ b/Sources/CodexBar/UsageStore.swift @@ -1139,12 +1139,7 @@ extension UsageStore { configToken: nil, hasEnvToken: deepSeekHasEnvToken, hasTokenAccount: deepSeekHasTokenAccount) - case .clinepass, .gemini, .antigravity, .opencode, .opencodego, .alibabatokenplan, .qwencloud, .factory, - .copilot, .devin, .vertexai, .kilo, .kiro, .kimi, .moonshot, .jetbrains, .perplexity, - .mimo, .doubao, .sakana, .abacus, .mistral, .deepinfra, .codebuff, .crof, .windsurf, - .venice, .manus, .commandcode, .qoder, .stepfun, .bedrock, .grok, .groq, .t3chat, .llmproxy, - .litellm, .zed, .deepgram, .poe, .chutes, .neuralwatt, .clawrouter, .longcat, .wayfinder, - .sub2api, .zenmux, .aiand, .zoommate, .xai: + default: return unimplementedDebugLogMessages[provider] ?? "Debug log not yet implemented" } } diff --git a/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift b/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift index 40ed686a5e..e8904b9067 100644 --- a/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift +++ b/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift @@ -1,5 +1,5 @@ // Generated by Scripts/regenerate-codex-parser-hash.sh. Do not edit by hand. enum CodexParserHash { - static let value = "dcab8c067490a89e" + static let value = "a72389ecaa16bc9a" } diff --git a/Sources/CodexBarCore/Logging/LogCategories.swift b/Sources/CodexBarCore/Logging/LogCategories.swift index 9d482635fb..c59e5d7877 100644 --- a/Sources/CodexBarCore/Logging/LogCategories.swift +++ b/Sources/CodexBarCore/Logging/LogCategories.swift @@ -1,100 +1,105 @@ public enum LogCategories { - public static let abacusCookie = "abacus-cookie" - public static let abacusUsage = "abacus-usage" + public static func provider(_ provider: UsageProvider, scope: String? = nil) -> String { + let base = provider == .opencodego ? "opencode-go" : provider.rawValue + return scope.map { "\(base)-\($0)" } ?? base + } + + public static let abacusCookie = Self.provider(.abacus, scope: "cookie") + public static let abacusUsage = Self.provider(.abacus, scope: "usage") public static let adaptiveRefresh = "adaptive-refresh" - public static let amp = "amp" - public static let antigravity = "antigravity" + public static let amp = Self.provider(.amp) + public static let antigravity = Self.provider(.antigravity) public static let app = "app" public static let auggieCLI = "auggie-cli" - public static let augment = "augment" - public static let augmentKeepalive = "augment-keepalive" - public static let bedrockUsage = "bedrock-usage" + public static let augment = Self.provider(.augment) + public static let augmentKeepalive = Self.provider(.augment, scope: "keepalive") + public static let bedrockUsage = Self.provider(.bedrock, scope: "usage") public static let browserCookieGate = "browser-cookie-gate" - public static let claudeCLI = "claude-cli" - public static let claudeProbe = "claude-probe" - public static let claudeUsage = "claude-usage" - public static let codexRPC = "codex-rpc" - public static let commandcodeCookie = "commandcode-cookie" - public static let commandcodeUsage = "commandcode-usage" + public static let claudeCLI = Self.provider(.claude, scope: "cli") + public static let claudeProbe = Self.provider(.claude, scope: "probe") + public static let claudeUsage = Self.provider(.claude, scope: "usage") + public static let codexRPC = Self.provider(.codex, scope: "rpc") + public static let commandcodeCookie = Self.provider(.commandcode, scope: "cookie") + public static let commandcodeUsage = Self.provider(.commandcode, scope: "usage") public static let configMigration = "config-migration" public static let configStore = "config-store" public static let confetti = "confetti" public static let cookieCache = "cookie-cache" public static let cookieHeaderStore = "cookie-header-store" - public static let copilotTokenStore = "copilot-token-store" + public static let copilotTokenStore = Self.provider(.copilot, scope: "token-store") public static let creditsPurchase = "creditsPurchase" - public static let cursorLogin = "cursor-login" - public static let deepSeekSettings = "deepseek-settings" - public static let deepSeekUsage = "deepseek-usage" - public static let deepgramUsage = "deepgram-usage" - public static let devin = "devin" - public static let doubaoUsage = "doubao-usage" - public static let elevenLabsUsage = "elevenlabs-usage" - public static let geminiProbe = "gemini-probe" - public static let grok = "grok" + public static let cursorLogin = Self.provider(.cursor, scope: "login") + public static let deepSeekSettings = Self.provider(.deepseek, scope: "settings") + public static let deepSeekUsage = Self.provider(.deepseek, scope: "usage") + public static let deepgramUsage = Self.provider(.deepgram, scope: "usage") + public static let devin = Self.provider(.devin) + public static let doubaoUsage = Self.provider(.doubao, scope: "usage") + public static let elevenLabsUsage = Self.provider(.elevenlabs, scope: "usage") + public static let geminiProbe = Self.provider(.gemini, scope: "probe") + public static let grok = Self.provider(.grok) public static let hooks = "hooks" public static let keychainCache = "keychain-cache" public static let keychainMigration = "keychain-migration" public static let keychainPreflight = "keychain-preflight" public static let keychainPrompt = "keychain-prompt" - public static let kimiAPI = "kimi-api" - public static let kimiCookie = "kimi-cookie" - public static let kimiTokenStore = "kimi-token-store" - public static let kimiWeb = "kimi-web" - public static let kiro = "kiro" - public static let longcatAPI = "longcat-api" - public static let longcatCookie = "longcat-cookie" - public static let longcatWeb = "longcat-web" + public static let kimiAPI = Self.provider(.kimi, scope: "api") + public static let kimiCookie = Self.provider(.kimi, scope: "cookie") + public static let kimiTokenStore = Self.provider(.kimi, scope: "token-store") + public static let kimiWeb = Self.provider(.kimi, scope: "web") + public static let kiro = Self.provider(.kiro) + public static let longcatAPI = Self.provider(.longcat, scope: "api") + public static let longcatCookie = Self.provider(.longcat, scope: "cookie") + public static let longcatWeb = Self.provider(.longcat, scope: "web") public static let launchAtLogin = "launch-at-login" public static let login = "login" public static let logging = "logging" - public static let manusAPI = "manus-api" - public static let manusCookie = "manus-cookie" - public static let manusWeb = "manus-web" + public static let manusAPI = Self.provider(.manus, scope: "api") + public static let manusCookie = Self.provider(.manus, scope: "cookie") + public static let manusWeb = Self.provider(.manus, scope: "web") public static let memoryPressure = "memory-pressure" - public static let minimaxAPITokenStore = "minimax-api-token-store" - public static let minimaxCookie = "minimax-cookie" - public static let minimaxCookieStore = "minimax-cookie-store" - public static let minimaxUsage = "minimax-usage" - public static let minimaxWeb = "minimax-web" - public static let mimoCookie = "mimo-cookie" - public static let moonshotUsage = "moonshot-usage" - public static let neuralWattUsage = "neuralwatt-usage" + public static let minimaxAPITokenStore = Self.provider(.minimax, scope: "api-token-store") + public static let minimaxCookie = Self.provider(.minimax, scope: "cookie") + public static let minimaxCookieStore = Self.provider(.minimax, scope: "cookie-store") + public static let minimaxUsage = Self.provider(.minimax, scope: "usage") + public static let minimaxWeb = Self.provider(.minimax, scope: "web") + public static let mimoCookie = Self.provider(.mimo, scope: "cookie") + public static let moonshotUsage = Self.provider(.moonshot, scope: "usage") + public static let neuralWattUsage = Self.provider(.neuralwatt, scope: "usage") public static let notifications = "notifications" - public static let openAIWeb = "openai-web" - public static let openAIWebview = "openai-webview" - public static let ollama = "ollama" - public static let opencodeUsage = "opencode-usage" - public static let opencodeGoUsage = "opencode-go-usage" - public static let openRouterUsage = "openrouter-usage" - public static let perplexityAPI = "perplexity-api" - public static let perplexityCookie = "perplexity-cookie" - public static let perplexityWeb = "perplexity-web" - public static let poeUsage = "poe-usage" + public static let openAIWeb = Self.provider(.openai, scope: "web") + public static let openAIWebview = Self.provider(.openai, scope: "webview") + public static let ollama = Self.provider(.ollama) + public static let opencodeUsage = Self.provider(.opencode, scope: "usage") + public static let opencodeGoUsage = Self.provider(.opencodego, scope: "usage") + public static let openRouterUsage = Self.provider(.openrouter, scope: "usage") + public static let perplexityAPI = Self.provider(.perplexity, scope: "api") + public static let perplexityCookie = Self.provider(.perplexity, scope: "cookie") + public static let perplexityWeb = Self.provider(.perplexity, scope: "web") + public static let poeUsage = Self.provider(.poe, scope: "usage") public static let providerDetection = "provider-detection" public static let providers = "providers" - public static let qoderCookie = "qoder-cookie" - public static let qoderUsage = "qoder-usage" + public static let qoderCookie = Self.provider(.qoder, scope: "cookie") + public static let qoderUsage = Self.provider(.qoder, scope: "usage") public static let quotaWarningNotifications = "quotaWarningNotifications" public static let sessionQuota = "sessionQuota" public static let sessionQuotaNotifications = "sessionQuotaNotifications" public static let settings = "settings" public static let subprocess = "subprocess" - public static let syntheticTokenStore = "synthetic-token-store" - public static let syntheticUsage = "synthetic-usage" - public static let t3chat = "t3chat" + public static let syntheticTokenStore = Self.provider(.synthetic, scope: "token-store") + public static let syntheticUsage = Self.provider(.synthetic, scope: "usage") + public static let t3chat = Self.provider(.t3chat) public static let terminal = "terminal" public static let tokenAccounts = "token-accounts" public static let tokenCost = "token-cost" public static let ttyRunner = "tty-runner" - public static let veniceUsage = "venice-usage" - public static let vertexAIFetcher = "vertexai-fetcher" - public static let warpUsage = "warp-usage" - public static let zed = "zed" + public static let veniceUsage = Self.provider(.venice, scope: "usage") + public static let vertexAIFetcher = Self.provider(.vertexai, scope: "fetcher") + public static let warpUsage = Self.provider(.warp, scope: "usage") + public static let zed = Self.provider(.zed) public static let webkitTeardown = "webkit-teardown" - public static let zaiSettings = "zai-settings" - public static let zaiTokenStore = "zai-token-store" - public static let zaiUsage = "zai-usage" - public static let stepfunUsage = "stepfun-usage" - public static let zoommate = "zoommate" + public static let zaiSettings = Self.provider(.zai, scope: "settings") + public static let zaiTokenStore = Self.provider(.zai, scope: "token-store") + public static let zaiUsage = Self.provider(.zai, scope: "usage") + public static let stepfunUsage = Self.provider(.stepfun, scope: "usage") + public static let zoommate = Self.provider(.zoommate) } diff --git a/Sources/CodexBarCore/Providers/Abacus/AbacusProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Abacus/AbacusProviderDescriptor.swift index ebc1c6125e..e5237bd552 100644 --- a/Sources/CodexBarCore/Providers/Abacus/AbacusProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Abacus/AbacusProviderDescriptor.swift @@ -13,6 +13,7 @@ public enum AbacusProviderDescriptor { metadata: ProviderMetadata( id: .abacus, displayName: "Abacus AI", + shortDisplayName: "Abacus", sessionLabel: "Credits", weeklyLabel: "Weekly", opusLabel: nil, @@ -22,6 +23,7 @@ public enum AbacusProviderDescriptor { toggleTitle: "Show Abacus AI usage", cliName: "abacusai", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder, @@ -29,7 +31,7 @@ public enum AbacusProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .abacus, + iconStyle: .init(provider: .abacus), iconResourceName: "ProviderIcon-abacus", color: ProviderColor(red: 56 / 255, green: 189 / 255, blue: 248 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/AiAnd/AiAndProviderDescriptor.swift b/Sources/CodexBarCore/Providers/AiAnd/AiAndProviderDescriptor.swift index 97ad028afe..96bc05c77d 100644 --- a/Sources/CodexBarCore/Providers/AiAnd/AiAndProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/AiAnd/AiAndProviderDescriptor.swift @@ -18,10 +18,11 @@ public enum AiAndProviderDescriptor { toggleTitle: "Show ai& usage", cliName: "aiand", defaultEnabled: false, + widgetSelectable: false, dashboardURL: "https://console.aiand.com", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .aiand, + iconStyle: .init(provider: .aiand), iconResourceName: "ProviderIcon-aiand", color: ProviderColor(red: 226 / 255, green: 92 / 255, blue: 43 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanProviderDescriptor.swift index 9aefdd5f22..484dff6885 100644 --- a/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Alibaba/AlibabaCodingPlanProviderDescriptor.swift @@ -43,7 +43,7 @@ public enum AlibabaCodingPlanProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.aliyun.com"), branding: ProviderBranding( - iconStyle: .alibaba, + iconStyle: .init(provider: .alibaba), iconResourceName: "ProviderIcon-alibaba", color: ProviderColor(red: 1.0, green: 106 / 255, blue: 0), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanProviderDescriptor.swift index 3340e2e42a..a9d8960b5d 100644 --- a/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Alibaba/AlibabaTokenPlanProviderDescriptor.swift @@ -35,6 +35,7 @@ public enum AlibabaTokenPlanProviderDescriptor { metadata: ProviderMetadata( id: .alibabatokenplan, displayName: "Alibaba Token Plan", + shortDisplayName: "Token Plan", sessionLabel: "Credits", weeklyLabel: "Usage", opusLabel: nil, @@ -51,7 +52,7 @@ public enum AlibabaTokenPlanProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.aliyun.com"), branding: ProviderBranding( - iconStyle: .alibaba, + iconStyle: .init(provider: .alibaba), iconResourceName: "ProviderIcon-alibaba", color: ProviderColor(red: 1.0, green: 106 / 255, blue: 0), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Amp/AmpProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Amp/AmpProviderDescriptor.swift index c1877e40f0..ec1347f881 100644 --- a/Sources/CodexBarCore/Providers/Amp/AmpProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Amp/AmpProviderDescriptor.swift @@ -18,13 +18,14 @@ public enum AmpProviderDescriptor { toggleTitle: "Show Amp usage", cliName: "amp", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder, dashboardURL: "https://ampcode.com/settings/usage", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .amp, + iconStyle: .init(provider: .amp), iconResourceName: "ProviderIcon-amp", color: ProviderColor(red: 220 / 255, green: 38 / 255, blue: 38 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Antigravity/AntigravityProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Antigravity/AntigravityProviderDescriptor.swift index db210b6069..790e74809e 100644 --- a/Sources/CodexBarCore/Providers/Antigravity/AntigravityProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Antigravity/AntigravityProviderDescriptor.swift @@ -9,6 +9,7 @@ public enum AntigravityProviderDescriptor { metadata: ProviderMetadata( id: .antigravity, displayName: "Antigravity", + shortDisplayName: "Anti", sessionLabel: "Gemini Models", weeklyLabel: "Claude and GPT", opusLabel: nil, @@ -25,7 +26,7 @@ public enum AntigravityProviderDescriptor { statusLinkURL: "https://www.google.com/appsstatus/dashboard/products/npdyhgECDJ6tB66MxXyo/history", statusWorkspaceProductID: "npdyhgECDJ6tB66MxXyo"), branding: ProviderBranding( - iconStyle: .antigravity, + iconStyle: .init(provider: .antigravity), iconResourceName: "ProviderIcon-antigravity", color: ProviderColor(red: 96 / 255, green: 186 / 255, blue: 126 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Augment/AugmentProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Augment/AugmentProviderDescriptor.swift index 8f23d48a85..a96fe2ec68 100644 --- a/Sources/CodexBarCore/Providers/Augment/AugmentProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Augment/AugmentProviderDescriptor.swift @@ -42,13 +42,14 @@ public enum AugmentProviderDescriptor { toggleTitle: "Show Augment usage", cliName: "augment", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: browserOrder, dashboardURL: "https://app.augmentcode.com/account/subscription", statusPageURL: "https://status.augmentcode.com"), branding: ProviderBranding( - iconStyle: .augment, + iconStyle: .init(provider: .augment), iconResourceName: "ProviderIcon-augment", color: ProviderColor(red: 99 / 255, green: 102 / 255, blue: 241 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAIProviderDescriptor.swift b/Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAIProviderDescriptor.swift index 9f375d12af..80ba0b204a 100644 --- a/Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAIProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/AzureOpenAI/AzureOpenAIProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum AzureOpenAIProviderDescriptor { toggleTitle: "Show Azure OpenAI status", cliName: "azure-openai", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum AzureOpenAIProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://azure.status.microsoft/en-us/status"), branding: ProviderBranding( - iconStyle: .openai, + iconStyle: .init(provider: .openai), iconResourceName: "ProviderIcon-codex", color: ProviderColor(red: 0, green: 120 / 255, blue: 212 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Bedrock/BedrockProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Bedrock/BedrockProviderDescriptor.swift index 701a729968..ebfcd76404 100644 --- a/Sources/CodexBarCore/Providers/Bedrock/BedrockProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Bedrock/BedrockProviderDescriptor.swift @@ -9,6 +9,7 @@ public enum BedrockProviderDescriptor { metadata: ProviderMetadata( id: .bedrock, displayName: "AWS Bedrock", + shortDisplayName: "Bedrock", sessionLabel: "Budget", weeklyLabel: "Cost", opusLabel: nil, @@ -18,13 +19,14 @@ public enum BedrockProviderDescriptor { toggleTitle: "Show AWS Bedrock usage", cliName: "bedrock", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: "https://console.aws.amazon.com/bedrock", statusPageURL: nil, statusLinkURL: "https://health.aws.amazon.com/health/status"), branding: ProviderBranding( - iconStyle: .bedrock, + iconStyle: .init(provider: .bedrock), iconResourceName: "ProviderIcon-bedrock", color: ProviderColor(red: 1, green: 0.6, blue: 0), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Chutes/ChutesProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Chutes/ChutesProviderDescriptor.swift index 5bb80a9f24..05077562ad 100644 --- a/Sources/CodexBarCore/Providers/Chutes/ChutesProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Chutes/ChutesProviderDescriptor.swift @@ -18,13 +18,14 @@ public enum ChutesProviderDescriptor { toggleTitle: "Show Chutes usage", cliName: "chutes", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, dashboardURL: "https://chutes.ai", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .chutes, + iconStyle: .init(provider: .chutes), iconResourceName: "ProviderIcon-chutes", color: ProviderColor(red: 49 / 255, green: 132 / 255, blue: 255 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift index e18e0892f6..563fa9125a 100644 --- a/Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Claude/ClaudeProviderDescriptor.swift @@ -26,7 +26,7 @@ public enum ClaudeProviderDescriptor { changelogURL: "https://github.com/anthropics/claude-code/releases", statusPageURL: "https://status.claude.com/"), branding: ProviderBranding( - iconStyle: .claude, + iconStyle: .init(provider: .claude), iconResourceName: "ProviderIcon-claude", color: ProviderColor(red: 204 / 255, green: 124 / 255, blue: 94 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/ClawRouter/ClawRouterProviderDescriptor.swift b/Sources/CodexBarCore/Providers/ClawRouter/ClawRouterProviderDescriptor.swift index 4019f25ddf..080fe910ec 100644 --- a/Sources/CodexBarCore/Providers/ClawRouter/ClawRouterProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/ClawRouter/ClawRouterProviderDescriptor.swift @@ -18,10 +18,11 @@ public enum ClawRouterProviderDescriptor { toggleTitle: "Show ClawRouter usage", cliName: "clawrouter", defaultEnabled: false, + widgetSelectable: false, dashboardURL: "https://clawrouter.openclaw.ai/dashboard/access", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .clawrouter, + iconStyle: .init(provider: .clawrouter), iconResourceName: "ProviderIcon-clawrouter", color: ProviderColor(red: 89 / 255, green: 110 / 255, blue: 246 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/ClinePass/ClinePassProviderDescriptor.swift b/Sources/CodexBarCore/Providers/ClinePass/ClinePassProviderDescriptor.swift index 033279e0cb..d5fae7852c 100644 --- a/Sources/CodexBarCore/Providers/ClinePass/ClinePassProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/ClinePass/ClinePassProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum ClinePassProviderDescriptor { toggleTitle: "Show ClinePass usage", cliName: "clinepass", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum ClinePassProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .clinepass, + iconStyle: .init(provider: .clinepass), iconResourceName: "ProviderIcon-clinepass", color: ProviderColor(red: 0.38, green: 0.64, blue: 0.98), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Codebuff/CodebuffProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Codebuff/CodebuffProviderDescriptor.swift index 5f75610f9b..2f0f0b745f 100644 --- a/Sources/CodexBarCore/Providers/Codebuff/CodebuffProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Codebuff/CodebuffProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum CodebuffProviderDescriptor { toggleTitle: "Show Codebuff usage", cliName: "codebuff", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum CodebuffProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .codebuff, + iconStyle: .init(provider: .codebuff), iconResourceName: "ProviderIcon-codebuff", color: ProviderColor(red: 68 / 255, green: 255 / 255, blue: 0 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Codex/CodexProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Codex/CodexProviderDescriptor.swift index e6fa71b4e7..d4de65f6ba 100644 --- a/Sources/CodexBarCore/Providers/Codex/CodexProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Codex/CodexProviderDescriptor.swift @@ -26,7 +26,7 @@ public enum CodexProviderDescriptor { changelogURL: "https://github.com/openai/codex/releases", statusPageURL: "https://status.openai.com/"), branding: ProviderBranding( - iconStyle: .codex, + iconStyle: .init(provider: .codex), iconResourceName: "ProviderIcon-codex", color: ProviderColor(red: 73 / 255, green: 163 / 255, blue: 176 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderDescriptor.swift b/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderDescriptor.swift index 6fd32c6024..3cb7eec5dc 100644 --- a/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/CommandCode/CommandCodeProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum CommandCodeProviderDescriptor { toggleTitle: "Show Command Code usage", cliName: "commandcode", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder, @@ -26,7 +27,7 @@ public enum CommandCodeProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .commandcode, + iconStyle: .init(provider: .commandcode), iconResourceName: "ProviderIcon-commandcode", color: ProviderColor(hex: 0xA04DFD), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift index 13db4ed67c..0652242b6e 100644 --- a/Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Copilot/CopilotProviderDescriptor.swift @@ -24,7 +24,7 @@ public enum CopilotProviderDescriptor { dashboardURL: "https://github.com/settings/copilot", statusPageURL: "https://www.githubstatus.com/"), branding: ProviderBranding( - iconStyle: .copilot, + iconStyle: .init(provider: .copilot), iconResourceName: "ProviderIcon-copilot", color: ProviderColor(red: 168 / 255, green: 85 / 255, blue: 247 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Crof/CrofProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Crof/CrofProviderDescriptor.swift index ace227fe9d..5ca3be1de0 100644 --- a/Sources/CodexBarCore/Providers/Crof/CrofProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Crof/CrofProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum CrofProviderDescriptor { toggleTitle: "Show Crof usage", cliName: "crof", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum CrofProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .crof, + iconStyle: .init(provider: .crof), iconResourceName: "ProviderIcon-crof", color: ProviderColor(red: 0.18, green: 0.67, blue: 0.58), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift index 26114ce4d1..7907b76131 100644 --- a/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift @@ -26,7 +26,7 @@ public enum CursorProviderDescriptor { statusPageURL: "https://status.cursor.com", statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .cursor, + iconStyle: .init(provider: .cursor), iconResourceName: "ProviderIcon-cursor", color: ProviderColor(red: 0 / 255, green: 191 / 255, blue: 165 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/DeepInfra/DeepInfraProviderDescriptor.swift b/Sources/CodexBarCore/Providers/DeepInfra/DeepInfraProviderDescriptor.swift index 678a84bb2b..61fb45f7ad 100644 --- a/Sources/CodexBarCore/Providers/DeepInfra/DeepInfraProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/DeepInfra/DeepInfraProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum DeepInfraProviderDescriptor { toggleTitle: "Show DeepInfra usage", cliName: "deepinfra", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum DeepInfraProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.deepinfra.com"), branding: ProviderBranding( - iconStyle: .deepinfra, + iconStyle: .init(provider: .deepinfra), iconResourceName: "ProviderIcon-deepinfra", color: ProviderColor(red: 42 / 255, green: 50 / 255, blue: 117 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift b/Sources/CodexBarCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift index 4b5b3e0281..36064c4aa0 100644 --- a/Sources/CodexBarCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/DeepSeek/DeepSeekProviderDescriptor.swift @@ -55,6 +55,7 @@ public enum DeepSeekProviderDescriptor { toggleTitle: "Show DeepSeek usage", cliName: "deepseek", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -62,7 +63,7 @@ public enum DeepSeekProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.deepseek.com"), branding: ProviderBranding( - iconStyle: .deepseek, + iconStyle: .init(provider: .deepseek), iconResourceName: "ProviderIcon-deepseek", color: ProviderColor(red: 0.32, green: 0.49, blue: 0.94), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Deepgram/DeepgramProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Deepgram/DeepgramProviderDescriptor.swift index e99941971d..db881e48e1 100644 --- a/Sources/CodexBarCore/Providers/Deepgram/DeepgramProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Deepgram/DeepgramProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum DeepgramProviderDescriptor { toggleTitle: "Show Deepgram usage", cliName: "deepgram", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum DeepgramProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.deepgram.com"), branding: ProviderBranding( - iconStyle: .deepgram, + iconStyle: .init(provider: .deepgram), iconResourceName: "ProviderIcon-deepgram", color: ProviderColor( red: 100 / 255, diff --git a/Sources/CodexBarCore/Providers/Devin/DevinProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Devin/DevinProviderDescriptor.swift index 80ca592199..9ca539afd5 100644 --- a/Sources/CodexBarCore/Providers/Devin/DevinProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Devin/DevinProviderDescriptor.swift @@ -25,7 +25,7 @@ public enum DevinProviderDescriptor { subscriptionDashboardURL: "https://app.devin.ai/settings/usage", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .devin, + iconStyle: .init(provider: .devin), iconResourceName: "ProviderIcon-devin", color: ProviderColor(red: 70 / 255, green: 180 / 255, blue: 130 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Doubao/DoubaoProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Doubao/DoubaoProviderDescriptor.swift index d0dbbc01a7..8236512645 100644 --- a/Sources/CodexBarCore/Providers/Doubao/DoubaoProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Doubao/DoubaoProviderDescriptor.swift @@ -27,13 +27,14 @@ public enum DoubaoProviderDescriptor { toggleTitle: "Show Doubao usage", cliName: "doubao", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, dashboardURL: "https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement?LLM=%7B%7D&advancedActiveKey=subscribe", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .doubao, + iconStyle: .init(provider: .doubao), iconResourceName: "ProviderIcon-doubao", color: ProviderColor(red: 51 / 255, green: 112 / 255, blue: 255 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/ElevenLabs/ElevenLabsProviderDescriptor.swift b/Sources/CodexBarCore/Providers/ElevenLabs/ElevenLabsProviderDescriptor.swift index bddc58c71c..3087edfa41 100644 --- a/Sources/CodexBarCore/Providers/ElevenLabs/ElevenLabsProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/ElevenLabs/ElevenLabsProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum ElevenLabsProviderDescriptor { toggleTitle: "Show ElevenLabs usage", cliName: "elevenlabs", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -26,7 +27,7 @@ public enum ElevenLabsProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.elevenlabs.io"), branding: ProviderBranding( - iconStyle: .elevenlabs, + iconStyle: .init(provider: .elevenlabs), iconResourceName: "ProviderIcon-elevenlabs", color: ProviderColor(red: 0.92, green: 0.92, blue: 0.90), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Factory/FactoryProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Factory/FactoryProviderDescriptor.swift index 88bbccd65c..1eae19e46e 100644 --- a/Sources/CodexBarCore/Providers/Factory/FactoryProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Factory/FactoryProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum FactoryProviderDescriptor { toggleTitle: "Show Droid usage", cliName: "factory", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder, @@ -25,7 +26,7 @@ public enum FactoryProviderDescriptor { statusPageURL: "https://status.factory.ai", statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .factory, + iconStyle: .init(provider: .factory), iconResourceName: "ProviderIcon-factory", color: ProviderColor(red: 255 / 255, green: 107 / 255, blue: 53 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Gemini/GeminiProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Gemini/GeminiProviderDescriptor.swift index 4cb101eb3f..493c2d3691 100644 --- a/Sources/CodexBarCore/Providers/Gemini/GeminiProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Gemini/GeminiProviderDescriptor.swift @@ -26,7 +26,7 @@ public enum GeminiProviderDescriptor { statusLinkURL: "https://www.google.com/appsstatus/dashboard/products/npdyhgECDJ6tB66MxXyo/history", statusWorkspaceProductID: "npdyhgECDJ6tB66MxXyo"), branding: ProviderBranding( - iconStyle: .gemini, + iconStyle: .init(provider: .gemini), iconResourceName: "ProviderIcon-gemini", color: ProviderColor(red: 171 / 255, green: 135 / 255, blue: 234 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift index 28df122789..d3ec2d714e 100644 --- a/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Grok/GrokProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum GrokProviderDescriptor { toggleTitle: "Show Grok usage", cliName: "grok", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.grokCookieImportOrder, @@ -26,7 +27,7 @@ public enum GrokProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.x.ai"), branding: ProviderBranding( - iconStyle: .grok, + iconStyle: .init(provider: .grok), iconResourceName: "ProviderIcon-grok", color: ProviderColor(red: 16 / 255, green: 163 / 255, blue: 127 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Groq/GroqProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Groq/GroqProviderDescriptor.swift index fbc6dace91..8a53f58d13 100644 --- a/Sources/CodexBarCore/Providers/Groq/GroqProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Groq/GroqProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum GroqProviderDescriptor { toggleTitle: "Show Groq usage", cliName: "groqcloud", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum GroqProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.groq.com"), branding: ProviderBranding( - iconStyle: .groq, + iconStyle: .init(provider: .groq), iconResourceName: "ProviderIcon-groq", color: ProviderColor(red: 245 / 255, green: 104 / 255, blue: 68 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderDescriptor.swift b/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderDescriptor.swift index ef7a39c12c..19ec4ebe4c 100644 --- a/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/JetBrains/JetBrainsProviderDescriptor.swift @@ -9,6 +9,7 @@ public enum JetBrainsProviderDescriptor { metadata: ProviderMetadata( id: .jetbrains, displayName: "JetBrains AI", + shortDisplayName: "JetBrains", sessionLabel: "Current", weeklyLabel: "Refill", opusLabel: nil, @@ -18,12 +19,13 @@ public enum JetBrainsProviderDescriptor { toggleTitle: "Show JetBrains AI usage", cliName: "jetbrains", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: nil, statusPageURL: nil), branding: ProviderBranding( - iconStyle: .jetbrains, + iconStyle: .init(provider: .jetbrains), iconResourceName: "ProviderIcon-jetbrains", color: ProviderColor(red: 255 / 255, green: 51 / 255, blue: 153 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Kilo/KiloProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Kilo/KiloProviderDescriptor.swift index ab8392a26b..293632b3dd 100644 --- a/Sources/CodexBarCore/Providers/Kilo/KiloProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Kilo/KiloProviderDescriptor.swift @@ -24,7 +24,7 @@ public enum KiloProviderDescriptor { dashboardURL: "https://app.kilo.ai/usage", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .kilo, + iconStyle: .init(provider: .kilo), iconResourceName: "ProviderIcon-kilo", color: ProviderColor(red: 242 / 255, green: 112 / 255, blue: 39 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Kimi/KimiProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Kimi/KimiProviderDescriptor.swift index c2c82758b7..731623ee07 100644 --- a/Sources/CodexBarCore/Providers/Kimi/KimiProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Kimi/KimiProviderDescriptor.swift @@ -26,7 +26,7 @@ public enum KimiProviderDescriptor { dashboardURL: "https://www.kimi.com/code/console", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .kimi, + iconStyle: .init(provider: .kimi), iconResourceName: "ProviderIcon-kimi", color: ProviderColor(red: 254 / 255, green: 96 / 255, blue: 60 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Kiro/KiroProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Kiro/KiroProviderDescriptor.swift index 6e0304a4f3..9d2053fc72 100644 --- a/Sources/CodexBarCore/Providers/Kiro/KiroProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Kiro/KiroProviderDescriptor.swift @@ -18,13 +18,14 @@ public enum KiroProviderDescriptor { toggleTitle: "Show Kiro usage", cliName: "kiro", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: "https://app.kiro.dev/account/usage", statusPageURL: nil, statusLinkURL: "https://health.aws.amazon.com/health/status"), branding: ProviderBranding( - iconStyle: .kiro, + iconStyle: .init(provider: .kiro), iconResourceName: "ProviderIcon-kiro", color: ProviderColor(red: 255 / 255, green: 153 / 255, blue: 0 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/LLMProxy/LLMProxyProviderDescriptor.swift b/Sources/CodexBarCore/Providers/LLMProxy/LLMProxyProviderDescriptor.swift index 4ed16573fc..48107edc7a 100644 --- a/Sources/CodexBarCore/Providers/LLMProxy/LLMProxyProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/LLMProxy/LLMProxyProviderDescriptor.swift @@ -18,13 +18,14 @@ public enum LLMProxyProviderDescriptor { toggleTitle: "Show LLM Proxy usage", cliName: "llmproxy", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, dashboardURL: nil, statusPageURL: nil), branding: ProviderBranding( - iconStyle: .llmproxy, + iconStyle: .init(provider: .llmproxy), iconResourceName: "ProviderIcon-llmproxy", color: ProviderColor(red: 36 / 255, green: 180 / 255, blue: 126 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/LiteLLM/LiteLLMProviderDescriptor.swift b/Sources/CodexBarCore/Providers/LiteLLM/LiteLLMProviderDescriptor.swift index b3261ce03c..bdc15c755f 100644 --- a/Sources/CodexBarCore/Providers/LiteLLM/LiteLLMProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/LiteLLM/LiteLLMProviderDescriptor.swift @@ -18,12 +18,13 @@ public enum LiteLLMProviderDescriptor { toggleTitle: "Show LiteLLM usage", cliName: "litellm", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: nil, statusPageURL: nil), branding: ProviderBranding( - iconStyle: .litellm, + iconStyle: .init(provider: .litellm), iconResourceName: "ProviderIcon-litellm", color: ProviderColor(red: 76 / 255, green: 137 / 255, blue: 240 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/LongCat/LongCatProviderDescriptor.swift b/Sources/CodexBarCore/Providers/LongCat/LongCatProviderDescriptor.swift index 83daf5d220..6e92cec79f 100644 --- a/Sources/CodexBarCore/Providers/LongCat/LongCatProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/LongCat/LongCatProviderDescriptor.swift @@ -18,13 +18,14 @@ public enum LongCatProviderDescriptor { toggleTitle: "Show LongCat usage", cliName: "longcat", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.longcatCookieImportOrder, dashboardURL: "https://longcat.chat/platform/", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .longcat, + iconStyle: .init(provider: .longcat), iconResourceName: "ProviderIcon-longcat", color: ProviderColor(red: 255 / 255, green: 209 / 255, blue: 0 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Manus/ManusProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Manus/ManusProviderDescriptor.swift index 514c5057a3..a27707ae6f 100644 --- a/Sources/CodexBarCore/Providers/Manus/ManusProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Manus/ManusProviderDescriptor.swift @@ -18,13 +18,14 @@ public enum ManusProviderDescriptor { toggleTitle: "Show Manus usage", cliName: "manus", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder, dashboardURL: "https://manus.im", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .manus, + iconStyle: .init(provider: .manus), iconResourceName: "ProviderIcon-manus", color: ProviderColor(red: 52 / 255, green: 50 / 255, blue: 45 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/MiMo/MiMoProviderDescriptor.swift b/Sources/CodexBarCore/Providers/MiMo/MiMoProviderDescriptor.swift index febeb04896..0b8aac38a2 100644 --- a/Sources/CodexBarCore/Providers/MiMo/MiMoProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/MiMo/MiMoProviderDescriptor.swift @@ -9,6 +9,7 @@ public enum MiMoProviderDescriptor { metadata: ProviderMetadata( id: .mimo, displayName: "Xiaomi MiMo", + shortDisplayName: "MiMo", sessionLabel: "Credits", weeklyLabel: "Window", opusLabel: nil, @@ -18,13 +19,14 @@ public enum MiMoProviderDescriptor { toggleTitle: "Show Xiaomi MiMo token plan & balance", cliName: "mimo", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.mimoCookieImportOrder, dashboardURL: "https://platform.xiaomimimo.com/#/console/balance", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .mimo, + iconStyle: .init(provider: .mimo), iconResourceName: "ProviderIcon-mimo", color: ProviderColor(red: 1.0, green: 105 / 255, blue: 0), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift index 4d02782100..757f200e4a 100644 --- a/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/MiniMax/MiniMaxProviderDescriptor.swift @@ -24,7 +24,7 @@ public enum MiniMaxProviderDescriptor { dashboardURL: "https://platform.minimax.io/user-center/payment/coding-plan?cycle_type=3", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .minimax, + iconStyle: .init(provider: .minimax), iconResourceName: "ProviderIcon-minimax", color: ProviderColor(red: 254 / 255, green: 96 / 255, blue: 60 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Mistral/MistralProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Mistral/MistralProviderDescriptor.swift index c97c4797aa..11e00abb72 100644 --- a/Sources/CodexBarCore/Providers/Mistral/MistralProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Mistral/MistralProviderDescriptor.swift @@ -25,7 +25,7 @@ public enum MistralProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.mistral.ai"), branding: ProviderBranding( - iconStyle: .mistral, + iconStyle: .init(provider: .mistral), iconResourceName: "ProviderIcon-mistral", color: ProviderColor(red: 255 / 255, green: 80 / 255, blue: 15 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderDescriptor.swift index fc18f20391..8ab1d194e9 100644 --- a/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Moonshot/MoonshotProviderDescriptor.swift @@ -9,6 +9,7 @@ public enum MoonshotProviderDescriptor { metadata: ProviderMetadata( id: .moonshot, displayName: "Moonshot / Kimi API", + shortDisplayName: "Moonshot", sessionLabel: "Balance", weeklyLabel: "Balance", opusLabel: nil, @@ -18,13 +19,14 @@ public enum MoonshotProviderDescriptor { toggleTitle: "Show Moonshot / Kimi API balance", cliName: "moonshot", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, dashboardURL: "https://platform.moonshot.ai/console/account", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .kimi, + iconStyle: .init(provider: .kimi), iconResourceName: "ProviderIcon-kimi", color: ProviderColor(red: 32 / 255, green: 93 / 255, blue: 235 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/NeuralWatt/NeuralWattProviderDescriptor.swift b/Sources/CodexBarCore/Providers/NeuralWatt/NeuralWattProviderDescriptor.swift index e02c047045..e712bde838 100644 --- a/Sources/CodexBarCore/Providers/NeuralWatt/NeuralWattProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/NeuralWatt/NeuralWattProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum NeuralWattProviderDescriptor { toggleTitle: "Show Neuralwatt usage", cliName: "neuralwatt", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -27,7 +28,7 @@ public enum NeuralWattProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .neuralwatt, + iconStyle: .init(provider: .neuralwatt), iconResourceName: "ProviderIcon-neuralwatt", color: ProviderColor(red: 0.22, green: 0.85, blue: 0.55), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Ollama/OllamaProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Ollama/OllamaProviderDescriptor.swift index 3d3520bc94..f18b5122b7 100644 --- a/Sources/CodexBarCore/Providers/Ollama/OllamaProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Ollama/OllamaProviderDescriptor.swift @@ -18,13 +18,14 @@ public enum OllamaProviderDescriptor { toggleTitle: "Show Ollama usage", cliName: "ollama", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder, dashboardURL: "https://ollama.com/settings", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .ollama, + iconStyle: .init(provider: .ollama), iconResourceName: "ProviderIcon-ollama", color: ProviderColor(red: 136 / 255, green: 136 / 255, blue: 136 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/OpenAI/OpenAIAPIProviderDescriptor.swift b/Sources/CodexBarCore/Providers/OpenAI/OpenAIAPIProviderDescriptor.swift index ebe68f562a..90e8d36410 100644 --- a/Sources/CodexBarCore/Providers/OpenAI/OpenAIAPIProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/OpenAI/OpenAIAPIProviderDescriptor.swift @@ -18,12 +18,13 @@ public enum OpenAIAPIProviderDescriptor { toggleTitle: "Show OpenAI usage", cliName: "openai", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: "https://platform.openai.com/usage", statusPageURL: "https://status.openai.com"), branding: ProviderBranding( - iconStyle: .openai, + iconStyle: .init(provider: .openai), iconResourceName: "ProviderIcon-codex", color: ProviderColor(red: 0.06, green: 0.51, blue: 0.43), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderDescriptor.swift b/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderDescriptor.swift index b7b27c5005..f2c3f7154a 100644 --- a/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/OpenCode/OpenCodeProviderDescriptor.swift @@ -24,7 +24,7 @@ public enum OpenCodeProviderDescriptor { dashboardURL: "https://opencode.ai/auth", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .opencode, + iconStyle: .init(provider: .opencode), iconResourceName: "ProviderIcon-opencode", color: ProviderColor(red: 59 / 255, green: 130 / 255, blue: 246 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderDescriptor.swift b/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderDescriptor.swift index 15e9b68588..d1e60ee838 100644 --- a/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/OpenCodeGo/OpenCodeGoProviderDescriptor.swift @@ -24,7 +24,7 @@ public enum OpenCodeGoProviderDescriptor { dashboardURL: "https://opencode.ai/auth", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .opencodego, + iconStyle: .init(provider: .opencodego), iconResourceName: "ProviderIcon-opencodego", color: ProviderColor(red: 59 / 255, green: 130 / 255, blue: 246 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift b/Sources/CodexBarCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift index 271241e342..167cf893bc 100644 --- a/Sources/CodexBarCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/OpenRouter/OpenRouterProviderDescriptor.swift @@ -18,13 +18,14 @@ public enum OpenRouterProviderDescriptor { toggleTitle: "Show OpenRouter usage", cliName: "openrouter", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: "https://openrouter.ai/settings/credits", statusPageURL: nil, statusLinkURL: "https://status.openrouter.ai"), branding: ProviderBranding( - iconStyle: .openrouter, + iconStyle: .init(provider: .openrouter), iconResourceName: "ProviderIcon-openrouter", color: ProviderColor(red: 100 / 255, green: 103 / 255, blue: 242 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderDescriptor.swift index a6364ed72a..acc96e9ea2 100644 --- a/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Perplexity/PerplexityProviderDescriptor.swift @@ -9,6 +9,7 @@ public enum PerplexityProviderDescriptor { metadata: ProviderMetadata( id: .perplexity, displayName: "Perplexity", + shortDisplayName: "Pplx", sessionLabel: "Credits", weeklyLabel: "Bonus credits", opusLabel: "Purchased", @@ -18,6 +19,7 @@ public enum PerplexityProviderDescriptor { toggleTitle: "Show Perplexity usage", cliName: "perplexity", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +27,7 @@ public enum PerplexityProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.perplexity.com/"), branding: ProviderBranding( - iconStyle: .perplexity, + iconStyle: .init(provider: .perplexity), iconResourceName: "ProviderIcon-perplexity", color: ProviderColor(red: 32 / 255, green: 178 / 255, blue: 170 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Poe/PoeProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Poe/PoeProviderDescriptor.swift index 6ee9836004..16b50a0f01 100644 --- a/Sources/CodexBarCore/Providers/Poe/PoeProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Poe/PoeProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum PoeProviderDescriptor { toggleTitle: "Show Poe usage", cliName: "poe", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum PoeProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .poe, + iconStyle: .init(provider: .poe), iconResourceName: "ProviderIcon-poe", color: ProviderColor(red: 93 / 255, green: 92 / 255, blue: 222 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/ProviderDescriptor.swift b/Sources/CodexBarCore/Providers/ProviderDescriptor.swift index ab474b4627..67720cd4db 100644 --- a/Sources/CodexBarCore/Providers/ProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/ProviderDescriptor.swift @@ -145,79 +145,8 @@ public enum ProviderDescriptorRegistry { private static let lock = NSLock() private static let store = Store() - private static let descriptorsByID: [UsageProvider: ProviderDescriptor] = [ - .codex: CodexProviderDescriptor.descriptor, - .openai: OpenAIAPIProviderDescriptor.descriptor, - .azureopenai: AzureOpenAIProviderDescriptor.descriptor, - .claude: ClaudeProviderDescriptor.descriptor, - .clinepass: ClinePassProviderDescriptor.descriptor, - .cursor: CursorProviderDescriptor.descriptor, - .opencode: OpenCodeProviderDescriptor.descriptor, - .opencodego: OpenCodeGoProviderDescriptor.descriptor, - .alibaba: AlibabaCodingPlanProviderDescriptor.descriptor, - .alibabatokenplan: AlibabaTokenPlanProviderDescriptor.descriptor, - .qwencloud: QwenCloudProviderDescriptor.descriptor, - .factory: FactoryProviderDescriptor.descriptor, - .gemini: GeminiProviderDescriptor.descriptor, - .antigravity: AntigravityProviderDescriptor.descriptor, - .copilot: CopilotProviderDescriptor.descriptor, - .devin: DevinProviderDescriptor.descriptor, - .zai: ZaiProviderDescriptor.descriptor, - .minimax: MiniMaxProviderDescriptor.descriptor, - .manus: ManusProviderDescriptor.descriptor, - .kimi: KimiProviderDescriptor.descriptor, - .kilo: KiloProviderDescriptor.descriptor, - .kiro: KiroProviderDescriptor.descriptor, - .vertexai: VertexAIProviderDescriptor.descriptor, - .augment: AugmentProviderDescriptor.descriptor, - .jetbrains: JetBrainsProviderDescriptor.descriptor, - .moonshot: MoonshotProviderDescriptor.descriptor, - .amp: AmpProviderDescriptor.descriptor, - .t3chat: T3ChatProviderDescriptor.descriptor, - .ollama: OllamaProviderDescriptor.descriptor, - .synthetic: SyntheticProviderDescriptor.descriptor, - .openrouter: OpenRouterProviderDescriptor.descriptor, - .elevenlabs: ElevenLabsProviderDescriptor.descriptor, - .warp: WarpProviderDescriptor.descriptor, - .windsurf: WindsurfProviderDescriptor.descriptor, - .zed: ZedProviderDescriptor.descriptor, - .perplexity: PerplexityProviderDescriptor.descriptor, - .mimo: MiMoProviderDescriptor.descriptor, - .doubao: DoubaoProviderDescriptor.descriptor, - .sakana: SakanaProviderDescriptor.descriptor, - .abacus: AbacusProviderDescriptor.descriptor, - .mistral: MistralProviderDescriptor.descriptor, - .deepseek: DeepSeekProviderDescriptor.descriptor, - .deepinfra: DeepInfraProviderDescriptor.descriptor, - .codebuff: CodebuffProviderDescriptor.descriptor, - .crof: CrofProviderDescriptor.descriptor, - .venice: VeniceProviderDescriptor.descriptor, - .commandcode: CommandCodeProviderDescriptor.descriptor, - .qoder: QoderProviderDescriptor.descriptor, - .stepfun: StepFunProviderDescriptor.descriptor, - .bedrock: BedrockProviderDescriptor.descriptor, - .grok: GrokProviderDescriptor.descriptor, - .groq: GroqProviderDescriptor.descriptor, - .llmproxy: LLMProxyProviderDescriptor.descriptor, - .litellm: LiteLLMProviderDescriptor.descriptor, - .deepgram: DeepgramProviderDescriptor.descriptor, - .poe: PoeProviderDescriptor.descriptor, - .chutes: ChutesProviderDescriptor.descriptor, - .neuralwatt: NeuralWattProviderDescriptor.descriptor, - .clawrouter: ClawRouterProviderDescriptor.descriptor, - .longcat: LongCatProviderDescriptor.descriptor, - .sub2api: Sub2APIProviderDescriptor.descriptor, - .wayfinder: WayfinderProviderDescriptor.descriptor, - .zenmux: ZenMuxProviderDescriptor.descriptor, - .aiand: AiAndProviderDescriptor.descriptor, - .zoommate: ZoomMateProviderDescriptor.descriptor, - .xai: XAIProviderDescriptor.descriptor, - ] private static let bootstrap: Void = { - for provider in UsageProvider.allCases { - guard let descriptor = descriptorsByID[provider] else { - preconditionFailure("Missing ProviderDescriptor for \(provider.rawValue)") - } + for descriptor in ProviderManifest.allDescriptors { _ = ProviderDescriptorRegistry.register(descriptor) } }() @@ -250,8 +179,12 @@ public enum ProviderDescriptorRegistry { public static func descriptor(for id: UsageProvider) -> ProviderDescriptor { self.ensureBootstrapped() - if let found = self.store.byID[id] { return found } - if let found = self.all.first(where: { $0.id == id }) { return found } + if let found = self.store.byID[id] { + return found + } + if let found = self.all.first(where: { $0.id == id }) { + return found + } fatalError("Missing ProviderDescriptor for \(id.rawValue)") } diff --git a/Sources/CodexBarCore/Providers/ProviderManifest.swift b/Sources/CodexBarCore/Providers/ProviderManifest.swift new file mode 100644 index 0000000000..d7771cacd8 --- /dev/null +++ b/Sources/CodexBarCore/Providers/ProviderManifest.swift @@ -0,0 +1,74 @@ +import Foundation + +/// Closed first-party bootstrap list. Runtime/plugin registrations remain owned by +/// `ProviderDescriptorRegistry.register(_:)`. +public enum ProviderManifest { + public static let allDescriptors: [ProviderDescriptor] = [ + CodexProviderDescriptor.descriptor, + OpenAIAPIProviderDescriptor.descriptor, + AzureOpenAIProviderDescriptor.descriptor, + ClaudeProviderDescriptor.descriptor, + ClinePassProviderDescriptor.descriptor, + CursorProviderDescriptor.descriptor, + OpenCodeProviderDescriptor.descriptor, + OpenCodeGoProviderDescriptor.descriptor, + AlibabaCodingPlanProviderDescriptor.descriptor, + AlibabaTokenPlanProviderDescriptor.descriptor, + QwenCloudProviderDescriptor.descriptor, + FactoryProviderDescriptor.descriptor, + GeminiProviderDescriptor.descriptor, + AntigravityProviderDescriptor.descriptor, + CopilotProviderDescriptor.descriptor, + DevinProviderDescriptor.descriptor, + ZaiProviderDescriptor.descriptor, + MiniMaxProviderDescriptor.descriptor, + ManusProviderDescriptor.descriptor, + KimiProviderDescriptor.descriptor, + KiloProviderDescriptor.descriptor, + KiroProviderDescriptor.descriptor, + VertexAIProviderDescriptor.descriptor, + AugmentProviderDescriptor.descriptor, + JetBrainsProviderDescriptor.descriptor, + MoonshotProviderDescriptor.descriptor, + AmpProviderDescriptor.descriptor, + T3ChatProviderDescriptor.descriptor, + OllamaProviderDescriptor.descriptor, + SyntheticProviderDescriptor.descriptor, + OpenRouterProviderDescriptor.descriptor, + ElevenLabsProviderDescriptor.descriptor, + WarpProviderDescriptor.descriptor, + WindsurfProviderDescriptor.descriptor, + ZedProviderDescriptor.descriptor, + PerplexityProviderDescriptor.descriptor, + MiMoProviderDescriptor.descriptor, + DoubaoProviderDescriptor.descriptor, + SakanaProviderDescriptor.descriptor, + AbacusProviderDescriptor.descriptor, + MistralProviderDescriptor.descriptor, + DeepSeekProviderDescriptor.descriptor, + DeepInfraProviderDescriptor.descriptor, + CodebuffProviderDescriptor.descriptor, + CrofProviderDescriptor.descriptor, + VeniceProviderDescriptor.descriptor, + CommandCodeProviderDescriptor.descriptor, + QoderProviderDescriptor.descriptor, + StepFunProviderDescriptor.descriptor, + BedrockProviderDescriptor.descriptor, + GrokProviderDescriptor.descriptor, + GroqProviderDescriptor.descriptor, + LLMProxyProviderDescriptor.descriptor, + LiteLLMProviderDescriptor.descriptor, + DeepgramProviderDescriptor.descriptor, + PoeProviderDescriptor.descriptor, + ChutesProviderDescriptor.descriptor, + NeuralWattProviderDescriptor.descriptor, + ClawRouterProviderDescriptor.descriptor, + LongCatProviderDescriptor.descriptor, + Sub2APIProviderDescriptor.descriptor, + WayfinderProviderDescriptor.descriptor, + ZenMuxProviderDescriptor.descriptor, + AiAndProviderDescriptor.descriptor, + ZoomMateProviderDescriptor.descriptor, + XAIProviderDescriptor.descriptor, + ] +} diff --git a/Sources/CodexBarCore/Providers/Providers.swift b/Sources/CodexBarCore/Providers/Providers.swift index 7033a5ff29..e482d6da23 100644 --- a/Sources/CodexBarCore/Providers/Providers.swift +++ b/Sources/CodexBarCore/Providers/Providers.swift @@ -73,77 +73,47 @@ public enum UsageProvider: String, CaseIterable, Sendable, Codable { // swiftformat:enable sortDeclarations -public enum IconStyle: String, Sendable, CaseIterable { - case codex - case openai - case claude - case clinepass - case zai - case minimax - case manus - case gemini - case antigravity - case cursor - case opencode - case opencodego - case alibaba - case qwencloud - case factory - case copilot - case devin - case kimi - case kilo - case kiro - case vertexai - case augment - case jetbrains - case moonshot - case amp - case t3chat - case ollama - case synthetic - case warp - case openrouter - case elevenlabs - case windsurf - case zed - case perplexity - case mimo - case doubao - case sakana - case abacus - case mistral - case deepseek - case deepinfra - case codebuff - case crof - case venice - case commandcode - case qoder - case stepfun - case bedrock - case grok - case groq - case llmproxy - case litellm - case deepgram - case poe - case chutes - case neuralwatt - case clawrouter - case longcat - case sub2api - case wayfinder - case zenmux - case aiand - case zoommate - case xai - case combined +public struct IconStyle: RawRepresentable, Hashable, Sendable, CaseIterable, CustomStringConvertible { + public let rawValue: String + + public var description: String { + self.rawValue + } + + public init(rawValue: String) { + self.rawValue = rawValue + } + + public init(provider: UsageProvider) { + self.init(rawValue: provider.rawValue) + } + + public static var allCases: [IconStyle] { + UsageProvider.allCases.map(Self.init(provider:)) + [.combined] + } + + // Named styles below carry renderer behavior or preserve source-compatible call sites. + public static let codex = Self(provider: .codex) + public static let claude = Self(provider: .claude) + public static let gemini = Self(provider: .gemini) + public static let antigravity = Self(provider: .antigravity) + public static let cursor = Self(provider: .cursor) + public static let factory = Self(provider: .factory) + public static let copilot = Self(provider: .copilot) + public static let commandcode = Self(provider: .commandcode) + public static let kimi = Self(provider: .kimi) + public static let mimo = Self(provider: .mimo) + public static let mistral = Self(provider: .mistral) + public static let qoder = Self(provider: .qoder) + public static let warp = Self(provider: .warp) + public static let perplexity = Self(provider: .perplexity) + public static let combined = Self(rawValue: "combined") } public struct ProviderMetadata: Sendable { public let id: UsageProvider public let displayName: String + public let shortDisplayName: String public let sessionLabel: String public let weeklyLabel: String public let opusLabel: String? @@ -153,6 +123,7 @@ public struct ProviderMetadata: Sendable { public let toggleTitle: String public let cliName: String public let defaultEnabled: Bool + public let widgetSelectable: Bool public let isPrimaryProvider: Bool public let usesAccountFallback: Bool public let browserCookieOrder: BrowserCookieImportOrder? @@ -172,6 +143,7 @@ public struct ProviderMetadata: Sendable { public init( id: UsageProvider, displayName: String, + shortDisplayName: String? = nil, sessionLabel: String, weeklyLabel: String, opusLabel: String?, @@ -181,6 +153,7 @@ public struct ProviderMetadata: Sendable { toggleTitle: String, cliName: String, defaultEnabled: Bool, + widgetSelectable: Bool = true, isPrimaryProvider: Bool = false, usesAccountFallback: Bool = false, browserCookieOrder: BrowserCookieImportOrder? = nil, @@ -194,6 +167,7 @@ public struct ProviderMetadata: Sendable { { self.id = id self.displayName = displayName + self.shortDisplayName = shortDisplayName ?? displayName self.sessionLabel = sessionLabel self.weeklyLabel = weeklyLabel self.opusLabel = opusLabel @@ -203,6 +177,7 @@ public struct ProviderMetadata: Sendable { self.toggleTitle = toggleTitle self.cliName = cliName self.defaultEnabled = defaultEnabled + self.widgetSelectable = widgetSelectable self.isPrimaryProvider = isPrimaryProvider self.usesAccountFallback = usesAccountFallback self.browserCookieOrder = browserCookieOrder diff --git a/Sources/CodexBarCore/Providers/Qoder/QoderProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Qoder/QoderProviderDescriptor.swift index 8829eab84c..b10d4ba9a9 100644 --- a/Sources/CodexBarCore/Providers/Qoder/QoderProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Qoder/QoderProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum QoderProviderDescriptor { toggleTitle: "Show Qoder usage", cliName: "qoder", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.qoderCookieImportOrder, @@ -25,7 +26,7 @@ public enum QoderProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .qoder, + iconStyle: .init(provider: .qoder), iconResourceName: "ProviderIcon-qoder", color: ProviderColor(red: 16 / 255, green: 185 / 255, blue: 129 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderDescriptor.swift b/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderDescriptor.swift index ffd88045b4..1809b6bc6c 100644 --- a/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/QwenCloud/QwenCloudProviderDescriptor.swift @@ -35,7 +35,7 @@ public enum QwenCloudProviderDescriptor { statusPageURL: nil, statusLinkURL: "https://status.alibabacloud.com"), branding: ProviderBranding( - iconStyle: .qwencloud, + iconStyle: .init(provider: .qwencloud), iconResourceName: "ProviderIcon-qwencloud", color: ProviderColor(hex: 0x615CED), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Sakana/SakanaProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Sakana/SakanaProviderDescriptor.swift index 3212b2ea04..cf10815287 100644 --- a/Sources/CodexBarCore/Providers/Sakana/SakanaProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Sakana/SakanaProviderDescriptor.swift @@ -9,6 +9,7 @@ public enum SakanaProviderDescriptor { metadata: ProviderMetadata( id: .sakana, displayName: "Sakana AI", + shortDisplayName: "Sakana", sessionLabel: "5-hour", weeklyLabel: "Weekly", opusLabel: nil, @@ -18,13 +19,14 @@ public enum SakanaProviderDescriptor { toggleTitle: "Show Sakana AI usage", cliName: "sakana", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, dashboardURL: "https://console.sakana.ai/billing", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .sakana, + iconStyle: .init(provider: .sakana), iconResourceName: "ProviderIcon-sakana", color: ProviderColor(red: 0.16, green: 0.46, blue: 0.86), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift b/Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift index bf0fe602c7..49445ad916 100644 --- a/Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/StepFun/StepFunProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum StepFunProviderDescriptor { toggleTitle: "Show StepFun usage", cliName: "stepfun", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum StepFunProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .stepfun, + iconStyle: .init(provider: .stepfun), iconResourceName: "ProviderIcon-stepfun", color: ProviderColor(red: 0.13, green: 0.59, blue: 0.95), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Sub2API/Sub2APIProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Sub2API/Sub2APIProviderDescriptor.swift index 14d0f970a6..f6192d8808 100644 --- a/Sources/CodexBarCore/Providers/Sub2API/Sub2APIProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Sub2API/Sub2APIProviderDescriptor.swift @@ -19,10 +19,11 @@ public enum Sub2APIProviderDescriptor { toggleTitle: "Show sub2api usage", cliName: "sub2api", defaultEnabled: false, + widgetSelectable: false, dashboardURL: nil, statusPageURL: nil), branding: ProviderBranding( - iconStyle: .sub2api, + iconStyle: .init(provider: .sub2api), iconResourceName: "ProviderIcon-sub2api", color: ProviderColor(red: 45 / 255, green: 198 / 255, blue: 216 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Synthetic/SyntheticProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Synthetic/SyntheticProviderDescriptor.swift index f5881ec3a1..e54bb978ae 100644 --- a/Sources/CodexBarCore/Providers/Synthetic/SyntheticProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Synthetic/SyntheticProviderDescriptor.swift @@ -18,12 +18,13 @@ public enum SyntheticProviderDescriptor { toggleTitle: "Show Synthetic usage", cliName: "synthetic", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: nil, statusPageURL: nil), branding: ProviderBranding( - iconStyle: .synthetic, + iconStyle: .init(provider: .synthetic), iconResourceName: "ProviderIcon-synthetic", color: ProviderColor(red: 20 / 255, green: 20 / 255, blue: 20 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderDescriptor.swift b/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderDescriptor.swift index 2ec8762aa6..ec91856a0b 100644 --- a/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/T3Chat/T3ChatProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum T3ChatProviderDescriptor { toggleTitle: "Show T3 Chat usage", cliName: "t3chat", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.defaultImportOrder, @@ -25,7 +26,7 @@ public enum T3ChatProviderDescriptor { subscriptionDashboardURL: "https://t3.chat/settings/subscription", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .t3chat, + iconStyle: .init(provider: .t3chat), iconResourceName: "ProviderIcon-t3chat", color: ProviderColor(red: 245 / 255, green: 102 / 255, blue: 71 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Venice/VeniceProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Venice/VeniceProviderDescriptor.swift index 08383155f2..c9cf985689 100644 --- a/Sources/CodexBarCore/Providers/Venice/VeniceProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Venice/VeniceProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum VeniceProviderDescriptor { toggleTitle: "Show Venice usage", cliName: "venice", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, @@ -25,7 +26,7 @@ public enum VeniceProviderDescriptor { statusPageURL: nil, statusLinkURL: nil), branding: ProviderBranding( - iconStyle: .venice, + iconStyle: .init(provider: .venice), iconResourceName: "ProviderIcon-venice", color: ProviderColor(red: 0.2, green: 0.6, blue: 1.0), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/VertexAI/VertexAIProviderDescriptor.swift b/Sources/CodexBarCore/Providers/VertexAI/VertexAIProviderDescriptor.swift index 9b4e66efaf..f213550602 100644 --- a/Sources/CodexBarCore/Providers/VertexAI/VertexAIProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/VertexAI/VertexAIProviderDescriptor.swift @@ -9,6 +9,7 @@ public enum VertexAIProviderDescriptor { metadata: ProviderMetadata( id: .vertexai, displayName: "Vertex AI", + shortDisplayName: "Vertex", sessionLabel: "Requests", weeklyLabel: "Tokens", opusLabel: nil, @@ -18,13 +19,14 @@ public enum VertexAIProviderDescriptor { toggleTitle: "Show Vertex AI usage", cliName: "vertexai", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: "https://console.cloud.google.com/vertex-ai", statusPageURL: nil, statusLinkURL: "https://status.cloud.google.com"), branding: ProviderBranding( - iconStyle: .vertexai, + iconStyle: .init(provider: .vertexai), iconResourceName: "ProviderIcon-vertexai", color: ProviderColor(red: 66 / 255, green: 133 / 255, blue: 244 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Warp/WarpProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Warp/WarpProviderDescriptor.swift index 924b981a11..969eb89575 100644 --- a/Sources/CodexBarCore/Providers/Warp/WarpProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Warp/WarpProviderDescriptor.swift @@ -18,13 +18,14 @@ public enum WarpProviderDescriptor { toggleTitle: "Show Warp usage", cliName: "warp", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: nil, dashboardURL: "https://docs.warp.dev/reference/cli/api-keys", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .warp, + iconStyle: .init(provider: .warp), iconResourceName: "ProviderIcon-warp", color: ProviderColor(red: 147 / 255, green: 139 / 255, blue: 180 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Wayfinder/WayfinderProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Wayfinder/WayfinderProviderDescriptor.swift index f7322abdc9..5e01b5fc7e 100644 --- a/Sources/CodexBarCore/Providers/Wayfinder/WayfinderProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Wayfinder/WayfinderProviderDescriptor.swift @@ -18,10 +18,11 @@ public enum WayfinderProviderDescriptor { toggleTitle: "Show Wayfinder usage", cliName: "wayfinder", defaultEnabled: false, + widgetSelectable: false, dashboardURL: WayfinderSettingsReader.dashboardURL(environment: [:]).absoluteString, statusPageURL: nil), branding: ProviderBranding( - iconStyle: .wayfinder, + iconStyle: .init(provider: .wayfinder), iconResourceName: "ProviderIcon-wayfinder", color: ProviderColor(red: 16 / 255, green: 163 / 255, blue: 127 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderDescriptor.swift index 050426377d..3db0266188 100644 --- a/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Windsurf/WindsurfProviderDescriptor.swift @@ -18,12 +18,13 @@ public enum WindsurfProviderDescriptor { toggleTitle: "Show Windsurf usage", cliName: "windsurf", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: "https://windsurf.com/subscription/usage", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .windsurf, + iconStyle: .init(provider: .windsurf), iconResourceName: "ProviderIcon-windsurf", color: ProviderColor(red: 52 / 255, green: 232 / 255, blue: 187 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/XAI/XAIProviderDescriptor.swift b/Sources/CodexBarCore/Providers/XAI/XAIProviderDescriptor.swift index 72abbcac28..70fc58fac6 100644 --- a/Sources/CodexBarCore/Providers/XAI/XAIProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/XAI/XAIProviderDescriptor.swift @@ -18,11 +18,12 @@ public enum XAIProviderDescriptor { toggleTitle: "Show xAI usage", cliName: "xai", defaultEnabled: false, + widgetSelectable: false, dashboardURL: "https://console.x.ai", statusPageURL: nil, statusLinkURL: "https://status.x.ai"), branding: ProviderBranding( - iconStyle: .xai, + iconStyle: .init(provider: .xai), iconResourceName: "ProviderIcon-xai", color: ProviderColor(red: 142 / 255, green: 142 / 255, blue: 147 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Zai/ZaiProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Zai/ZaiProviderDescriptor.swift index 13995d248e..7829ecfaff 100644 --- a/Sources/CodexBarCore/Providers/Zai/ZaiProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Zai/ZaiProviderDescriptor.swift @@ -23,7 +23,7 @@ public enum ZaiProviderDescriptor { dashboardURL: ZaiAPIRegion.global.dashboardURL.absoluteString, statusPageURL: nil), branding: ProviderBranding( - iconStyle: .zai, + iconStyle: .init(provider: .zai), iconResourceName: "ProviderIcon-zai", color: ProviderColor(red: 232 / 255, green: 90 / 255, blue: 106 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/Zed/ZedProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Zed/ZedProviderDescriptor.swift index 30a2403787..3c8654d0e4 100644 --- a/Sources/CodexBarCore/Providers/Zed/ZedProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Zed/ZedProviderDescriptor.swift @@ -18,12 +18,13 @@ public enum ZedProviderDescriptor { toggleTitle: "Show Zed usage", cliName: "zed", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, dashboardURL: nil, statusPageURL: nil), branding: ProviderBranding( - iconStyle: .zed, + iconStyle: .init(provider: .zed), iconResourceName: "ProviderIcon-zed", color: ProviderColor(red: 8 / 255, green: 78 / 255, blue: 255 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/ZenMux/ZenMuxProviderDescriptor.swift b/Sources/CodexBarCore/Providers/ZenMux/ZenMuxProviderDescriptor.swift index 3781fc7585..ac58b10718 100644 --- a/Sources/CodexBarCore/Providers/ZenMux/ZenMuxProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/ZenMux/ZenMuxProviderDescriptor.swift @@ -18,10 +18,11 @@ public enum ZenMuxProviderDescriptor { toggleTitle: "Show ZenMux usage", cliName: "zenmux", defaultEnabled: false, + widgetSelectable: false, dashboardURL: "https://zenmux.ai/platform/management", statusPageURL: nil), branding: ProviderBranding( - iconStyle: .zenmux, + iconStyle: .init(provider: .zenmux), iconResourceName: "ProviderIcon-zenmux", color: ProviderColor(red: 108 / 255, green: 92 / 255, blue: 231 / 255), confettiPalette: [ diff --git a/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderDescriptor.swift b/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderDescriptor.swift index 409dc9feac..fe77244db2 100644 --- a/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/ZoomMate/ZoomMateProviderDescriptor.swift @@ -18,6 +18,7 @@ public enum ZoomMateProviderDescriptor { toggleTitle: "Show ZoomMate usage", cliName: "zoommate", defaultEnabled: false, + widgetSelectable: false, isPrimaryProvider: false, usesAccountFallback: false, browserCookieOrder: ProviderBrowserCookieDefaults.chromeOnlyImportOrder, @@ -34,7 +35,7 @@ public enum ZoomMateProviderDescriptor { "Zoom Website", ]), branding: ProviderBranding( - iconStyle: .zoommate, + iconStyle: .init(provider: .zoommate), iconResourceName: "ProviderIcon-zoommate", // Zoom Brand Center "Visual identity > Color", retrieved 2026-07-18: // https://brand.zoom.com/document/1#/visual-identity/color diff --git a/Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift b/Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift index f69842ca5a..853f54dd88 100644 --- a/Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift +++ b/Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift @@ -1728,13 +1728,7 @@ enum CostUsageScanner { now: now, options: filtered, checkCancellation: checkCancellation) - case .openai, .azureopenai, .clinepass, .zai, .gemini, .antigravity, .cursor, .opencode, .opencodego, .alibaba, - .alibabatokenplan, .qwencloud, .factory, - .copilot, .devin, .minimax, .manus, .kilo, .kiro, .kimi, .moonshot, .augment, .jetbrains, .amp, - .ollama, .t3chat, .synthetic, .openrouter, .elevenlabs, .warp, .perplexity, .mimo, .doubao, .sakana, - .abacus, .mistral, .deepseek, .deepinfra, .codebuff, .crof, .windsurf, .zed, .venice, .commandcode, - .qoder, .stepfun, .bedrock, .grok, .groq, .llmproxy, .litellm, .deepgram, .poe, .chutes, .neuralwatt, - .clawrouter, .longcat, .sub2api, .wayfinder, .zenmux, .aiand, .zoommate, .xai: + default: return emptyReport } } diff --git a/Sources/CodexBarWidget/CodexBarWidgetProvider.swift b/Sources/CodexBarWidget/CodexBarWidgetProvider.swift index 86acf1caea..adefda08e3 100644 --- a/Sources/CodexBarWidget/CodexBarWidgetProvider.swift +++ b/Sources/CodexBarWidget/CodexBarWidgetProvider.swift @@ -24,118 +24,19 @@ enum ProviderChoice: String, AppEnum { static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Provider") - static let caseDisplayRepresentations: [ProviderChoice: DisplayRepresentation] = [ - .codex: DisplayRepresentation(title: "Codex"), - .claude: DisplayRepresentation(title: "Claude"), - .gemini: DisplayRepresentation(title: "Gemini"), - .alibaba: DisplayRepresentation(title: "Alibaba"), - .alibabatokenplan: DisplayRepresentation(title: "Alibaba Token Plan"), - .qwencloud: DisplayRepresentation(title: "Qwen Cloud"), - .antigravity: DisplayRepresentation(title: "Antigravity"), - .cursor: DisplayRepresentation(title: "Cursor"), - .zai: DisplayRepresentation(title: "z.ai"), - .copilot: DisplayRepresentation(title: "Copilot"), - .devin: DisplayRepresentation(title: "Devin"), - .minimax: DisplayRepresentation(title: "MiniMax"), - .kilo: DisplayRepresentation(title: "Kilo"), - .opencode: DisplayRepresentation(title: "OpenCode"), - .opencodego: DisplayRepresentation(title: "OpenCode Go"), - .mistral: DisplayRepresentation(title: "Mistral"), - .kimi: DisplayRepresentation(title: "Kimi"), - ] + static let caseDisplayRepresentations: [ProviderChoice: DisplayRepresentation] = Dictionary( + uniqueKeysWithValues: Self.allCases.map { choice in + let name = ProviderDescriptorRegistry.descriptor(for: choice.provider).metadata.displayName + return (choice, DisplayRepresentation(title: "\(name)")) + }) var provider: UsageProvider { - switch self { - case .codex: .codex - case .claude: .claude - case .gemini: .gemini - case .alibaba: .alibaba - case .alibabatokenplan: .alibabatokenplan - case .qwencloud: .qwencloud - case .antigravity: .antigravity - case .cursor: .cursor - case .zai: .zai - case .copilot: .copilot - case .devin: .devin - case .minimax: .minimax - case .kilo: .kilo - case .opencode: .opencode - case .opencodego: .opencodego - case .mistral: .mistral - case .kimi: .kimi - } + UsageProvider(rawValue: self.rawValue)! } - // swiftlint:disable:next cyclomatic_complexity init?(provider: UsageProvider) { - switch provider { - case .codex: self = .codex - case .openai: return nil // OpenAI not yet supported in widgets - case .azureopenai: return nil // Azure OpenAI not yet supported in widgets - case .claude: self = .claude - case .clinepass: return nil // ClinePass not yet supported in widgets - case .gemini: self = .gemini - case .alibaba: self = .alibaba - case .alibabatokenplan: self = .alibabatokenplan - case .qwencloud: self = .qwencloud - case .antigravity: self = .antigravity - case .cursor: self = .cursor - case .opencode: self = .opencode - case .opencodego: self = .opencodego - case .zai: self = .zai - case .factory: return nil // Factory not yet supported in widgets - case .copilot: self = .copilot - case .devin: self = .devin - case .minimax: self = .minimax - case .manus: return nil // Manus not yet supported in widgets - case .vertexai: return nil // Vertex AI not yet supported in widgets - case .kilo: self = .kilo - case .kiro: return nil // Kiro not yet supported in widgets - case .augment: return nil // Augment not yet supported in widgets - case .jetbrains: return nil // JetBrains not yet supported in widgets - case .kimi: self = .kimi - case .moonshot: return nil // Moonshot not yet supported in widgets - case .amp: return nil // Amp not yet supported in widgets - case .t3chat: return nil // T3 Chat not yet supported in widgets - case .zoommate: return nil // ZoomMate not yet supported in widgets - case .ollama: return nil // Ollama not yet supported in widgets - case .synthetic: return nil // Synthetic not yet supported in widgets - case .openrouter: return nil // OpenRouter not yet supported in widgets - case .clawrouter: return nil // ClawRouter not yet supported in widgets - case .sub2api: return nil // sub2api not yet supported in widgets - case .wayfinder: return nil // Wayfinder not yet supported in widgets - case .elevenlabs: return nil // ElevenLabs not yet supported in widgets - case .warp: return nil // Warp not yet supported in widgets - case .windsurf: return nil // Windsurf not yet supported in widgets - case .perplexity: return nil // Perplexity not yet supported in widgets - case .mimo: return nil // Xiaomi MiMo not yet supported in widgets - case .doubao: return nil // Doubao not yet supported in widgets - case .sakana: return nil // Sakana AI not yet supported in widgets - case .abacus: return nil // Abacus AI not yet supported in widgets - case .mistral: self = .mistral - case .deepseek: return nil // DeepSeek not yet supported in widgets - case .deepinfra: return nil // DeepInfra not yet supported in widgets - case .codebuff: return nil // Codebuff not yet supported in widgets - case .crof: return nil // Crof not yet supported in widgets - case .venice: return nil // Venice not yet supported in widgets - case .commandcode: return nil // CommandCode not yet supported in widgets - case .qoder: return nil // Qoder not yet supported in widgets - case .stepfun: return nil // StepFun not yet supported in widgets - case .bedrock: return nil // Bedrock not yet supported in widgets - case .grok: return nil // Grok not yet supported in widgets - case .groq: return nil // Groq not yet supported in widgets - case .llmproxy: return nil // LLM Proxy not yet supported in widgets - case .litellm: return nil // LiteLLM not yet supported in widgets - case .deepgram: return nil // Deepgram not yet supported in widgets - case .poe: return nil // Poe not yet supported in widgets - case .chutes: return nil // Chutes not yet supported in widgets - case .longcat: return nil // LongCat not yet supported in widgets - case .zed: return nil // Zed not yet supported in widgets - case .neuralwatt: return nil // Neuralwatt not yet supported in widgets - case .zenmux: return nil // ZenMux not yet supported in widgets - case .aiand: return nil // ai& not yet supported in widgets - case .xai: return nil // xAI not yet supported in widgets - } + guard ProviderDescriptorRegistry.descriptor(for: provider).metadata.widgetSelectable else { return nil } + self.init(rawValue: provider.rawValue) } } diff --git a/Sources/CodexBarWidget/CodexBarWidgetViews.swift b/Sources/CodexBarWidget/CodexBarWidgetViews.swift index 6cfaee31ee..0e70f874a1 100644 --- a/Sources/CodexBarWidget/CodexBarWidgetViews.swift +++ b/Sources/CodexBarWidget/CodexBarWidgetViews.swift @@ -298,74 +298,7 @@ private struct ProviderSwitchChip: View { } private var shortLabel: String { - switch self.provider { - case .codex: "Codex" - case .openai: "OpenAI" - case .azureopenai: "Azure OpenAI" - case .claude: "Claude" - case .clinepass: "ClinePass" - case .gemini: "Gemini" - case .antigravity: "Anti" - case .cursor: "Cursor" - case .opencode: "OpenCode" - case .opencodego: "OpenCode Go" - case .alibaba: "Alibaba" - case .alibabatokenplan: "Token Plan" - case .qwencloud: "Qwen Cloud" - case .zai: "z.ai" - case .factory: "Droid" - case .copilot: "Copilot" - case .devin: "Devin" - case .minimax: "MiniMax" - case .manus: "Manus" - case .vertexai: "Vertex" - case .kilo: "Kilo" - case .kiro: "Kiro" - case .augment: "Augment" - case .jetbrains: "JetBrains" - case .kimi: "Kimi" - case .moonshot: "Moonshot" - case .amp: "Amp" - case .t3chat: "T3 Chat" - case .zoommate: "ZoomMate" - case .ollama: "Ollama" - case .synthetic: "Synthetic" - case .openrouter: "OpenRouter" - case .clawrouter: "ClawRouter" - case .sub2api: "sub2api" - case .wayfinder: "Wayfinder" - case .elevenlabs: "ElevenLabs" - case .warp: "Warp" - case .windsurf: "Windsurf" - case .perplexity: "Pplx" - case .mimo: "MiMo" - case .doubao: "Doubao" - case .sakana: "Sakana" - case .abacus: "Abacus" - case .mistral: "Mistral" - case .deepseek: "DeepSeek" - case .deepinfra: "DeepInfra" - case .codebuff: "Codebuff" - case .crof: "Crof" - case .venice: "Venice" - case .commandcode: "Command Code" - case .qoder: "Qoder" - case .stepfun: "StepFun" - case .bedrock: "Bedrock" - case .grok: "Grok" - case .groq: "Groq" - case .llmproxy: "LLM Proxy" - case .litellm: "LiteLLM" - case .deepgram: "Deepgram" - case .poe: "Poe" - case .chutes: "Chutes" - case .longcat: "LongCat" - case .zed: "Zed" - case .neuralwatt: "Neuralwatt" - case .zenmux: "ZenMux" - case .aiand: "ai&" - case .xai: "xAI" - } + ProviderDefaults.metadata[self.provider]?.shortDisplayName ?? self.provider.rawValue.capitalized } } @@ -640,12 +573,16 @@ struct WidgetUsageRow: Identifiable, Equatable { } static func smallWidgetRowLimit(for entry: WidgetSnapshot.ProviderEntry) -> Int? { - if entry.provider == .kimi { return 3 } + if entry.provider == .kimi { + return 3 + } return self.antigravityQuotaSummaryRowLimit(for: entry, limit: 2) } static func mediumWidgetRowLimit(for entry: WidgetSnapshot.ProviderEntry) -> Int? { - if entry.provider == .kimi { return 3 } + if entry.provider == .kimi { + return 3 + } return self.antigravityQuotaSummaryRowLimit(for: entry, limit: 3) } @@ -951,7 +888,9 @@ private struct UsageHistoryChart: View { var body: some View { let isCostMode = UsageHistoryChartMode.isCostMode(self.points) let values = self.points.map { point -> Double in - if isCostMode { return point.costUSD ?? 0 } + if isCostMode { + return point.costUSD ?? 0 + } return Double(point.totalTokens ?? 0) } let scale = UsageChartScale(values: values) diff --git a/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift b/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift new file mode 100644 index 0000000000..e5d6c41409 --- /dev/null +++ b/Tests/CodexBarTests/ProviderArchitectureGatekeeperTests.swift @@ -0,0 +1,90 @@ +import AppKit +import Foundation +import Testing +@testable import CodexBar +@testable import CodexBarCore +@testable import CodexBarWidget + +@MainActor +struct ProviderArchitectureGatekeeperTests { + @Test + func `every provider has descriptor and implementation manifest entries`() { + let expected = Set(UsageProvider.allCases) + let descriptors = Set(ProviderDescriptorRegistry.all.map(\.id)) + let implementations = Set(ProviderImplementationRegistry.all.map(\.id)) + let missingDescriptors = expected.subtracting(descriptors).map(\.rawValue).sorted() + let missingImplementations = expected.subtracting(implementations).map(\.rawValue).sorted() + + #expect( + missingDescriptors.isEmpty, + "Missing descriptor manifest entries: \(missingDescriptors.joined(separator: ", "))") + #expect( + missingImplementations.isEmpty, + "Missing implementation manifest entries: \(missingImplementations.joined(separator: ", "))") + } + + @Test + func `every provider descriptor has a loadable SVG resource`() throws { + let resources = try Self.repoRoot() + .appending(path: "Sources/CodexBar/Resources", directoryHint: .isDirectory) + + for descriptor in ProviderDescriptorRegistry.all { + let resourceName = descriptor.branding.iconResourceName + let url = resources.appending(path: "\(resourceName).svg") + #expect( + FileManager.default.fileExists(atPath: url.path(percentEncoded: false)), + "Missing SVG for \(descriptor.id.rawValue): \(resourceName).svg") + #expect(NSImage(contentsOf: url) != nil, "Could not load \(resourceName).svg as NSImage") + } + } + + @Test + func `widget provider choices match selectable descriptor metadata`() { + let selectable = Set(ProviderDescriptorRegistry.all.filter(\.metadata.widgetSelectable).map(\.id)) + let choices = Set(ProviderChoice.allCases.map(\.provider)) + let missing = selectable.subtracting(choices).map(\.rawValue).sorted() + let unexpected = choices.subtracting(selectable).map(\.rawValue).sorted() + + #expect( + missing.isEmpty, + "Missing ProviderChoice cases for widget-selectable providers: \(missing.joined(separator: ", "))") + #expect( + unexpected.isEmpty, + "ProviderChoice cases marked non-selectable in descriptor metadata: \(unexpected.joined(separator: ", "))") + } + + @Test + func `widget short labels preserve compact provider names`() { + let overrides: [UsageProvider: String] = [ + .antigravity: "Anti", + .alibabatokenplan: "Token Plan", + .vertexai: "Vertex", + .perplexity: "Pplx", + .mimo: "MiMo", + .sakana: "Sakana", + .abacus: "Abacus", + .bedrock: "Bedrock", + .jetbrains: "JetBrains", + .moonshot: "Moonshot", + ] + for descriptor in ProviderDescriptorRegistry.all { + let expected = overrides[descriptor.id] ?? descriptor.metadata.displayName + #expect( + descriptor.metadata.shortDisplayName == expected, + "Unexpected widget short label for \(descriptor.id.rawValue).") + } + } + + private static func repoRoot() throws -> URL { + var directory = URL(filePath: #filePath).deletingLastPathComponent() + for _ in 0..<12 { + if FileManager.default.fileExists( + atPath: directory.appending(path: "Package.swift").path(percentEncoded: false)) + { + return directory + } + directory.deleteLastPathComponent() + } + throw CocoaError(.fileNoSuchFile) + } +} diff --git a/Tests/CodexBarTests/ProviderIconResourcesTests.swift b/Tests/CodexBarTests/ProviderIconResourcesTests.swift index 064e4c33a9..ab758512d9 100644 --- a/Tests/CodexBarTests/ProviderIconResourcesTests.swift +++ b/Tests/CodexBarTests/ProviderIconResourcesTests.swift @@ -6,57 +6,6 @@ import Testing @MainActor struct ProviderIconResourcesTests { - @Test - func `provider icon SV gs exist`() throws { - let root = try Self.repoRoot() - let resources = root.appending(path: "Sources/CodexBar/Resources", directoryHint: .isDirectory) - - let slugs = [ - "codex", - "claude", - "clinepass", - "zai", - "minimax", - "cursor", - "opencode", - "opencodego", - "alibaba", - "gemini", - "antigravity", - "factory", - "copilot", - "devin", - "crof", - "commandcode", - "t3chat", - "kimi", - "longcat", - "bedrock", - "elevenlabs", - "groq", - "llmproxy", - "litellm", - "deepgram", - "ollama", - "clawrouter", - "sub2api", - "wayfinder", - "zenmux", - "aiand", - "zoommate", - "xai", - ] - for slug in slugs { - let url = resources.appending(path: "ProviderIcon-\(slug).svg") - #expect( - FileManager.default.fileExists(atPath: url.path(percentEncoded: false)), - "Missing SVG for \(slug)") - - let image = NSImage(contentsOf: url) - #expect(image != nil, "Could not load SVG as NSImage for \(slug)") - } - } - @Test func `groq and grok provider icons are distinct`() throws { let root = try Self.repoRoot() diff --git a/Tests/CodexBarTests/ProviderRegistryTests.swift b/Tests/CodexBarTests/ProviderRegistryTests.swift index e92d5a5941..bf92eb3e92 100644 --- a/Tests/CodexBarTests/ProviderRegistryTests.swift +++ b/Tests/CodexBarTests/ProviderRegistryTests.swift @@ -33,6 +33,34 @@ struct ProviderRegistryTests { #expect(ids == secondPass, "ProviderImplementationRegistry order changed between reads.") } + @Test + func `icon styles derive from provider identifiers while preserving shared styles`() { + #expect(IconStyle.allCases == UsageProvider.allCases.map(IconStyle.init(provider:)) + [.combined]) + + let sharedStyles: [UsageProvider: UsageProvider] = [ + .azureopenai: .openai, + .alibabatokenplan: .alibaba, + .moonshot: .kimi, + ] + for descriptor in ProviderDescriptorRegistry.all { + let expectedProvider = sharedStyles[descriptor.id] ?? descriptor.id + #expect( + descriptor.branding.iconStyle == IconStyle(provider: expectedProvider), + "Unexpected icon style for \(descriptor.id.rawValue).") + } + } + + @Test + func `provider log categories derive byte identical names`() { + #expect(LogCategories.provider(.codex) == "codex") + #expect(LogCategories.provider(.deepseek, scope: "usage") == "deepseek-usage") + #expect(LogCategories.provider(.opencodego, scope: "usage") == "opencode-go-usage") + #expect(LogCategories.codexRPC == "codex-rpc") + #expect(LogCategories.openAIWebview == "openai-webview") + #expect(LogCategories.minimaxAPITokenStore == "minimax-api-token-store") + #expect(LogCategories.neuralWattUsage == "neuralwatt-usage") + } + @Test func `minimax sorts after zai in registry`() { let ids = ProviderDescriptorRegistry.all.map(\.id) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 544092eb93..008d92e63f 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -107,7 +107,7 @@ CodexBar/ ## Common Tasks ### Add a New Provider -See the canonical [provider authoring guide](provider.md#adding-a-new-provider-current-flow) for the complete flow. +See the canonical [provider authoring guide](provider.md#adding-a-new-provider) for the complete flow. 1. Add the provider identity to `Sources/CodexBarCore/Providers/Providers.swift`. 2. Add the descriptor and the fetcher, parser, settings-reader, or status-probe pieces the provider needs under diff --git a/docs/provider.md b/docs/provider.md index 6fd968d36f..c6b28b3e8b 100644 --- a/docs/provider.md +++ b/docs/provider.md @@ -39,8 +39,9 @@ Common building blocks already exist: - OpenAI dashboard web scrape: `OpenAIDashboardFetcher` (WKWebView + JS) - cost usage: local log scanner (Codex + Claude) -Provider behavior is descriptor-driven. Two explicit, exhaustive registries form the bootstrap boundary: -`ProviderDescriptorRegistry` owns core descriptors and `ProviderImplementationRegistry` owns app implementations. +Provider behavior is descriptor-driven. Two flat first-party manifests form the closed bootstrap boundary: +`ProviderManifest` lists core descriptors and `ProviderImplementationManifest` lists app implementations. The registries +retain thread-safe `register(_:)` methods for future dynamic providers. ## Provider descriptor (source of truth) @@ -120,7 +121,7 @@ public enum ExampleProviderDescriptor { dashboardURL: nil, statusPageURL: nil), branding: ProviderBranding( - iconStyle: .codex, + iconStyle: .init(provider: .example), iconResourceName: "ProviderIcon-example", color: ProviderColor(red: 0.2, green: 0.6, blue: 0.8), confettiPalette: [ @@ -176,35 +177,26 @@ boundary: An integration can be restored when missing operator or authorization evidence becomes available. -## Adding a new provider (current flow) - -Checklist: -- Add `UsageProvider` case in `Sources/CodexBarCore/Providers/Providers.swift`. -- Create `Sources/CodexBarCore/Providers//`: - - `Descriptor.swift`: define `ProviderDescriptor` + fetch pipeline. - - `Strategies.swift`: implement one or more `ProviderFetchStrategy`. - - `Probe.swift` / `Fetcher.swift`: concrete fetcher logic. - - `Models.swift`: snapshot structs. - - `Parser.swift` (if needed). -- Define a cached `public static let descriptor` and `static func makeDescriptor() -> ProviderDescriptor`. -- Add the descriptor to `ProviderDescriptorRegistry.descriptorsByID`. -- Add `Sources/CodexBar/Providers//ProviderImplementation.swift`: - - `ProviderImplementation` only for settings/login UI hooks. -- Add an exhaustive case to `ProviderImplementationRegistry.makeImplementation(for:)`. -- Add icons + colors in descriptor: - - `iconName` must match `ProviderIcon-` asset. - - Color used in menu cards + switcher. - - Provide a curated `confettiPalette` with 2–3 colors for reset celebrations. -- If CLI-specific behavior is needed: - - add `cliName`, `cliAliases`, `sourceModes`, `versionProvider` in descriptor. - - strategies decide which `--source` modes apply. -- Tests: - - `UsageSnapshot` mapping unit tests - - strategy availability + fallback tests - - CLI provider parsing (aliases + --source validation) -- Docs: - - add provider section in `docs/providers.md` with data source + auth notes - - update `docs/provider.md` if the pipeline model changes +## Adding a new provider + +The mandatory registration checklist is intentionally short: + +1. Create `Sources/CodexBarCore/Providers//` with the descriptor and fetch strategies. +2. Create `Sources/CodexBar/Providers//` with the app implementation and optional settings extension. +3. Add one stable case to `UsageProvider` in `Sources/CodexBarCore/Providers/Providers.swift`. +4. Add one `ProviderIcon-.svg` resource under `Sources/CodexBar/Resources` and reference it from branding. +5. Add one descriptor line to `ProviderManifest.allDescriptors`. +6. Add one implementation factory line to `ProviderImplementationManifest.makeImplementations`. +7. Add one case to the WidgetKit `ProviderChoice` `AppEnum`. New descriptors are widget-selectable by default; set + `widgetSelectable: false` in the provider's descriptor only when the provider genuinely cannot appear in widgets. + +Everything else is derived from the descriptor: icon-style identity, log-category construction, display and compact +labels, default enablement, fetch/CLI metadata, icon validation, and widget display representations. The provider +architecture gatekeeper test reports missing descriptor, implementation, icon, or widget registrations by provider ID. + +Provider-specific behavior still deserves focused tests—for example snapshot mapping, strategy availability/fallback, +CLI aliases/source validation, and parser fixtures. Add a section to `docs/providers.md` when users need data-source or +authentication guidance. ## UI notes (Providers settings) Current: checkboxes per provider.