From 477c8568a43de9e860f62b0e62d405bfad3ee186 Mon Sep 17 00:00:00 2001 From: Trim Date: Sat, 18 Jul 2026 21:14:02 -0500 Subject: [PATCH] Make Cursor extra usage optional --- Sources/CodexBar/MenuCardView.swift | 7 +- .../Cursor/CursorProviderImplementation.swift | 5 +- .../Resources/ar.lproj/Localizable.strings | 2 +- .../Resources/ca.lproj/Localizable.strings | 2 +- .../Resources/de.lproj/Localizable.strings | 2 +- .../Resources/en.lproj/Localizable.strings | 2 +- .../Resources/es.lproj/Localizable.strings | 2 +- .../Resources/fa.lproj/Localizable.strings | 2 +- .../Resources/fr.lproj/Localizable.strings | 2 +- .../Resources/gl.lproj/Localizable.strings | 2 +- .../Resources/id.lproj/Localizable.strings | 2 +- .../Resources/it.lproj/Localizable.strings | 2 +- .../Resources/ja.lproj/Localizable.strings | 2 +- .../Resources/ko.lproj/Localizable.strings | 2 +- .../Resources/nl.lproj/Localizable.strings | 2 +- .../Resources/pl.lproj/Localizable.strings | 2 +- .../Resources/pt-BR.lproj/Localizable.strings | 2 +- .../Resources/ru.lproj/Localizable.strings | 2 +- .../Resources/sv.lproj/Localizable.strings | 2 +- .../Resources/th.lproj/Localizable.strings | 2 +- .../Resources/tr.lproj/Localizable.strings | 2 +- .../Resources/uk.lproj/Localizable.strings | 2 +- .../Resources/vi.lproj/Localizable.strings | 2 +- .../zh-Hans.lproj/Localizable.strings | 2 +- .../zh-Hant.lproj/Localizable.strings | 2 +- .../CodexBar/StatusItemController+Menu.swift | 12 +-- .../Cursor/CursorProviderDescriptor.swift | 4 +- .../CursorMenuCardModelTests.swift | 13 ++- .../CursorProviderImplementationTests.swift | 81 +++++++++++++++++++ .../MenuCardOptionalUsageModelTests.swift | 32 ++++++++ .../StatusMenuNativeSectionSpacingTests.swift | 64 ++++++++++++++- 31 files changed, 220 insertions(+), 44 deletions(-) create mode 100644 Tests/CodexBarTests/CursorProviderImplementationTests.swift diff --git a/Sources/CodexBar/MenuCardView.swift b/Sources/CodexBar/MenuCardView.swift index f0b01e1cef..ff5b0bafec 100644 --- a/Sources/CodexBar/MenuCardView.swift +++ b/Sources/CodexBar/MenuCardView.swift @@ -906,9 +906,9 @@ extension UsageMenuCardView.Model { let openAIAPIUsage = input.snapshot?.openAIAPIUsage let inlineUsageDashboard = Self.inlineUsageDashboard(input: input) let usageNotes = Self.usageNotes(input: input) - let rawCreditsText: String? = if input.provider == .openrouter { - nil - } else if input.codexProjection != nil, !input.showOptionalCreditsAndExtraUsage { + let rawCreditsText: String? = if input.provider == .openrouter || + !input.showOptionalCreditsAndExtraUsage + { nil } else { Self.creditsLine( @@ -925,6 +925,7 @@ extension UsageMenuCardView.Model { input.snapshot?.identity?.loginMethod == "Admin API" let isRequiredOpenCodeZenBalance = Self.isRequiredOpenCodeZenBalance(input.snapshot) let hidesOptionalProviderCost = ((input.provider == .claude && !isClaudeAdminAPI) || + input.provider == .cursor || input.provider == .factory || input.provider == .devin || (input.provider == .opencodego && !isRequiredOpenCodeZenBalance)) && diff --git a/Sources/CodexBar/Providers/Cursor/CursorProviderImplementation.swift b/Sources/CodexBar/Providers/Cursor/CursorProviderImplementation.swift index 8286432b10..bc5d83cd25 100644 --- a/Sources/CodexBar/Providers/Cursor/CursorProviderImplementation.swift +++ b/Sources/CodexBar/Providers/Cursor/CursorProviderImplementation.swift @@ -85,7 +85,10 @@ struct CursorProviderImplementation: ProviderImplementation { @MainActor func appendUsageMenuEntries(context: ProviderMenuUsageContext, entries: inout [ProviderMenuEntry]) { - guard let cost = context.snapshot?.providerCost, cost.currencyCode != "Quota" else { return } + guard context.settings.showOptionalCreditsAndExtraUsage, + let cost = context.snapshot?.providerCost, + cost.currencyCode != "Quota" + else { return } let used = UsageFormatter.currencyString(cost.used, currencyCode: cost.currencyCode) if cost.limit > 0 { let limitStr = UsageFormatter.currencyString(cost.limit, currencyCode: cost.currencyCode) diff --git a/Sources/CodexBar/Resources/ar.lproj/Localizable.strings b/Sources/CodexBar/Resources/ar.lproj/Localizable.strings index be1b7a75dc..e953ae7003 100644 --- a/Sources/CodexBar/Resources/ar.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ar.lproj/Localizable.strings @@ -590,7 +590,7 @@ "show_provider_changelog_links_title" = "اعرض روابط سجل التغييرات لمزود الخدمة"; "show_provider_changelog_links_subtitle" = "يضيف روابط ملاحظات الإصدار لمزودي الخدمة المدعومين بدعم CLI إلى القائمة."; "show_credits_extra_usage_title" = "اعتمادات العرض + الاستخدام الإضافي"; -"show_credits_extra_usage_subtitle" = "عرض أقسام Codex الاعتمادات و Claude الاستخدام الإضافي في القائمة."; +"show_credits_extra_usage_subtitle" = "عرض الأرصدة والاستخدام الإضافي لموفري الخدمات المدعومين."; "multi_account_layout_title" = "تخطيط الحسابات المتعددة"; "multi_account_layout_subtitle" = "اختر تبديل الحسابات المجزأة أو بطاقات الحساب المكدسة."; "multi_account_layout_segmented" = "مقسم"; diff --git a/Sources/CodexBar/Resources/ca.lproj/Localizable.strings b/Sources/CodexBar/Resources/ca.lproj/Localizable.strings index 55985b0af6..756867d493 100644 --- a/Sources/CodexBar/Resources/ca.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ca.lproj/Localizable.strings @@ -568,7 +568,7 @@ "show_provider_changelog_links_title" = "Mostreu els enllaços al registre de canvis del proveïdor"; "show_provider_changelog_links_subtitle" = "Afegiu al menú enllaços a les notes de versió dels proveïdors compatibles basats en CLI."; "show_credits_extra_usage_title" = "Mostreu crèdits + ús addicional"; -"show_credits_extra_usage_subtitle" = "Mostreu les seccions de Crèdits de Codex i Ús addicional de Claude al menú."; +"show_credits_extra_usage_subtitle" = "Mostra els crèdits i l’ús addicional dels proveïdors compatibles."; "multi_account_layout_title" = "Disposició multicompte"; "multi_account_layout_subtitle" = "Trieu el canvi de compte segmentat o targetes de compte apilades."; "multi_account_layout_segmented" = "Segmentat"; diff --git a/Sources/CodexBar/Resources/de.lproj/Localizable.strings b/Sources/CodexBar/Resources/de.lproj/Localizable.strings index 37d2b62165..48720beaf3 100644 --- a/Sources/CodexBar/Resources/de.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/de.lproj/Localizable.strings @@ -582,7 +582,7 @@ "show_provider_changelog_links_title" = "Links zum Änderungsprotokoll des Anbieters anzeigen"; "show_provider_changelog_links_subtitle" = "Fügt dem Menü Versionshinweise-Links für unterstützte CLI-gestützte Anbieter hinzu."; "show_credits_extra_usage_title" = "Credits + zusätzliche Nutzung anzeigen"; -"show_credits_extra_usage_subtitle" = "Zeigen Sie die Nutzungsabschnitte \"Codex Credits\" und \"Claude Extra\" im Menü an."; +"show_credits_extra_usage_subtitle" = "Zeigt Guthaben und zusätzliche Nutzung für unterstützte Anbieter."; "multi_account_layout_title" = "Layout für mehrere Konten"; "multi_account_layout_subtitle" = "Wählen Sie segmentierte Kontoumschaltung oder gestapelte Kontokarten."; "multi_account_layout_segmented" = "Segmentiert"; diff --git a/Sources/CodexBar/Resources/en.lproj/Localizable.strings b/Sources/CodexBar/Resources/en.lproj/Localizable.strings index 5cc6b2defb..03f8eb40a7 100644 --- a/Sources/CodexBar/Resources/en.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/en.lproj/Localizable.strings @@ -590,7 +590,7 @@ "show_provider_changelog_links_title" = "Show provider changelog links"; "show_provider_changelog_links_subtitle" = "Adds release-notes links for supported CLI-backed providers to the menu."; "show_credits_extra_usage_title" = "Show credits & extra usage"; -"show_credits_extra_usage_subtitle" = "Show Codex Credits and Claude Extra usage sections in the menu."; +"show_credits_extra_usage_subtitle" = "Show credits and extra usage for supported providers."; "multi_account_layout_title" = "Multi-account layout"; "multi_account_layout_subtitle" = "Choose segmented account switching or stacked account cards."; "multi_account_layout_segmented" = "Segmented"; diff --git a/Sources/CodexBar/Resources/es.lproj/Localizable.strings b/Sources/CodexBar/Resources/es.lproj/Localizable.strings index 90159b2bc2..8e4826c2ea 100644 --- a/Sources/CodexBar/Resources/es.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/es.lproj/Localizable.strings @@ -576,7 +576,7 @@ "show_provider_changelog_links_title" = "Mostrar enlaces al registro de cambios del proveedor"; "show_provider_changelog_links_subtitle" = "Añade al menú enlaces a las notas de versión de los proveedores compatibles basados en CLI."; "show_credits_extra_usage_title" = "Mostrar créditos + uso adicional"; -"show_credits_extra_usage_subtitle" = "Mostrar las secciones de Créditos de Codex y Uso adicional de Claude en el menú."; +"show_credits_extra_usage_subtitle" = "Muestra créditos y uso adicional para los proveedores compatibles."; "multi_account_layout_title" = "Diseño multicuenta"; "multi_account_layout_subtitle" = "Elige cambio de cuenta segmentado o tarjetas de cuenta apiladas."; "multi_account_layout_segmented" = "Segmentado"; diff --git a/Sources/CodexBar/Resources/fa.lproj/Localizable.strings b/Sources/CodexBar/Resources/fa.lproj/Localizable.strings index 789090531a..50da83ef54 100644 --- a/Sources/CodexBar/Resources/fa.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/fa.lproj/Localizable.strings @@ -590,7 +590,7 @@ "show_provider_changelog_links_title" = "لینک های تغییرات ارائه دهنده را نمایش دهید"; "show_provider_changelog_links_subtitle" = "لینک های یادداشت های انتشار برای ارائه دهندگان پشتیبانی شده با پشتیبانی CLI به منو اضافه می شود."; "show_credits_extra_usage_title" = "نمایش اعتبارها + استفاده اضافی"; -"show_credits_extra_usage_subtitle" = "بخش های Codex اعتبارها و Claude استفاده اضافی را در منو نمایش دهید."; +"show_credits_extra_usage_subtitle" = "اعتبارها و استفادهٔ اضافی را برای ارائه‌دهندگان پشتیبانی‌شده نمایش دهید."; "multi_account_layout_title" = "چیدمان چندحسابی"; "multi_account_layout_subtitle" = "کارت های حساب سوئیچینگ تقسیم شده یا کارت های حساب انباشته را انتخاب کنید."; "multi_account_layout_segmented" = "بخش بندی شده"; diff --git a/Sources/CodexBar/Resources/fr.lproj/Localizable.strings b/Sources/CodexBar/Resources/fr.lproj/Localizable.strings index fafb4abed0..c1e80d848a 100644 --- a/Sources/CodexBar/Resources/fr.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/fr.lproj/Localizable.strings @@ -584,7 +584,7 @@ "show_provider_changelog_links_title" = "Afficher les liens du journal des modifications du fournisseur"; "show_provider_changelog_links_subtitle" = "Ajoute au menu des liens de notes de version pour les fournisseurs pris en charge par CLI."; "show_credits_extra_usage_title" = "Afficher les crédits + utilisation supplémentaire"; -"show_credits_extra_usage_subtitle" = "Afficher les sections d'utilisation des crédits Codex et de Claude Extra dans le menu."; +"show_credits_extra_usage_subtitle" = "Afficher les crédits et l’utilisation supplémentaire pour les fournisseurs compatibles."; "multi_account_layout_title" = "Disposition multi-comptes"; "multi_account_layout_subtitle" = "Choisissez un changement de compte segmenté ou des cartes de compte empilées."; "multi_account_layout_segmented" = "Segmenté"; diff --git a/Sources/CodexBar/Resources/gl.lproj/Localizable.strings b/Sources/CodexBar/Resources/gl.lproj/Localizable.strings index 82cc28af19..328e7ba582 100644 --- a/Sources/CodexBar/Resources/gl.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/gl.lproj/Localizable.strings @@ -563,7 +563,7 @@ "show_provider_changelog_links_title" = "Amosar as ligazóns ao rexistro de cambios do provedor"; "show_provider_changelog_links_subtitle" = "Engade ao menú enlaces ás notas de versión dos provedores compatibles baseados en CLI."; "show_credits_extra_usage_title" = "Amosar créditos + uso adicional"; -"show_credits_extra_usage_subtitle" = "Amosa as seccións de Créditos de Codex e Uso adicional de Claude no menú."; +"show_credits_extra_usage_subtitle" = "Amosa créditos e uso adicional para os provedores compatibles."; "multi_account_layout_title" = "Disposición multiconta"; "multi_account_layout_subtitle" = "Escolle o cambio de conta segmentado ou tarxetas de conta apiladas."; "multi_account_layout_segmented" = "Segmentado"; diff --git a/Sources/CodexBar/Resources/id.lproj/Localizable.strings b/Sources/CodexBar/Resources/id.lproj/Localizable.strings index abebb2f911..c02dc62f45 100644 --- a/Sources/CodexBar/Resources/id.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/id.lproj/Localizable.strings @@ -592,7 +592,7 @@ "show_provider_changelog_links_title" = "Tampilkan tautan changelog penyedia"; "show_provider_changelog_links_subtitle" = "Menambahkan tautan catatan rilis untuk penyedia berbasis CLI yang didukung ke menu."; "show_credits_extra_usage_title" = "Tampilkan kredit + penggunaan ekstra"; -"show_credits_extra_usage_subtitle" = "Tampilkan bagian Kredit Codex dan penggunaan Ekstra Claude di menu."; +"show_credits_extra_usage_subtitle" = "Tampilkan kredit dan penggunaan tambahan untuk penyedia yang didukung."; "multi_account_layout_title" = "Tata letak multi-akun"; "multi_account_layout_subtitle" = "Pilih pengalihan akun tersegmentasi atau kartu akun bertumpuk."; "multi_account_layout_segmented" = "Tersegmentasi"; diff --git a/Sources/CodexBar/Resources/it.lproj/Localizable.strings b/Sources/CodexBar/Resources/it.lproj/Localizable.strings index 402383c328..bda20dee7e 100644 --- a/Sources/CodexBar/Resources/it.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/it.lproj/Localizable.strings @@ -592,7 +592,7 @@ "show_provider_changelog_links_title" = "Mostra link changelog provider"; "show_provider_changelog_links_subtitle" = "Aggiunge link alle note di rilascio per i provider CLI supportati."; "show_credits_extra_usage_title" = "Mostra crediti + uso extra"; -"show_credits_extra_usage_subtitle" = "Mostra nel menu le sezioni Crediti Codex e Uso extra Claude."; +"show_credits_extra_usage_subtitle" = "Mostra crediti e utilizzo extra per i provider supportati."; "multi_account_layout_title" = "Layout multi-account"; "multi_account_layout_subtitle" = "Scegli tra selezione segmentata o schede account impilate."; "multi_account_layout_segmented" = "Segmentato"; diff --git a/Sources/CodexBar/Resources/ja.lproj/Localizable.strings b/Sources/CodexBar/Resources/ja.lproj/Localizable.strings index 51f5987788..adac3e43a0 100644 --- a/Sources/CodexBar/Resources/ja.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ja.lproj/Localizable.strings @@ -581,7 +581,7 @@ "show_provider_changelog_links_title" = "プロバイダの変更履歴リンクを表示"; "show_provider_changelog_links_subtitle" = "対応する CLI ベースのプロバイダのリリースノートへのリンクをメニューに追加します。"; "show_credits_extra_usage_title" = "クレジットと追加使用量を表示"; -"show_credits_extra_usage_subtitle" = "Codex クレジットと Claude 追加使用量のセクションをメニューに表示します。"; +"show_credits_extra_usage_subtitle" = "対応プロバイダーのクレジットと追加使用量を表示します。"; "multi_account_layout_title" = "複数アカウントのレイアウト"; "multi_account_layout_subtitle" = "セグメント式のアカウント切替か、積み重ね式のアカウントカードを選択します。"; "multi_account_layout_segmented" = "セグメント"; diff --git a/Sources/CodexBar/Resources/ko.lproj/Localizable.strings b/Sources/CodexBar/Resources/ko.lproj/Localizable.strings index 57d7245382..e628ce6a00 100644 --- a/Sources/CodexBar/Resources/ko.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ko.lproj/Localizable.strings @@ -573,7 +573,7 @@ "show_provider_changelog_links_title" = "공급자 변경 로그 링크 표시"; "show_provider_changelog_links_subtitle" = "지원되는 CLI 기반 공급자의 릴리스 노트 링크를 메뉴에 추가합니다."; "show_credits_extra_usage_title" = "크레딧 + 추가 사용량 표시"; -"show_credits_extra_usage_subtitle" = "메뉴에 Codex 크레딧 및 Claude 추가 사용량 섹션을 표시합니다."; +"show_credits_extra_usage_subtitle" = "지원되는 제공업체의 크레딧 및 추가 사용량을 표시합니다."; "multi_account_layout_title" = "다중 계정 레이아웃"; "multi_account_layout_subtitle" = "분할된 계정 전환 또는 쌓인 계정 카드를 선택하세요."; "multi_account_layout_segmented" = "분할"; diff --git a/Sources/CodexBar/Resources/nl.lproj/Localizable.strings b/Sources/CodexBar/Resources/nl.lproj/Localizable.strings index fbf7cbf4df..8ce341c96c 100644 --- a/Sources/CodexBar/Resources/nl.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/nl.lproj/Localizable.strings @@ -584,7 +584,7 @@ "show_provider_changelog_links_title" = "Toon provider changelog-links"; "show_provider_changelog_links_subtitle" = "Voegt koppelingen naar release-opmerkingen voor ondersteunde CLI-ondersteunde providers toe aan het menu."; "show_credits_extra_usage_title" = "Toon credits + extra gebruik"; -"show_credits_extra_usage_subtitle" = "Toon Codex Credits en Claude Extra gebruikssecties in het menu."; +"show_credits_extra_usage_subtitle" = "Toon tegoeden en extra gebruik voor ondersteunde providers."; "multi_account_layout_title" = "Indeling voor meerdere accounts"; "multi_account_layout_subtitle" = "Kies voor gesegmenteerd wisselen tussen accounts of gestapelde accountkaarten."; "multi_account_layout_segmented" = "Gesegmenteerd"; diff --git a/Sources/CodexBar/Resources/pl.lproj/Localizable.strings b/Sources/CodexBar/Resources/pl.lproj/Localizable.strings index 92351c1788..75fcb25099 100644 --- a/Sources/CodexBar/Resources/pl.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/pl.lproj/Localizable.strings @@ -592,7 +592,7 @@ "show_provider_changelog_links_title" = "Pokaż linki do changelogów dostawców"; "show_provider_changelog_links_subtitle" = "Dodaje linki do informacji o wydaniach dla obsługiwanych dostawców CLI w menu."; "show_credits_extra_usage_title" = "Pokaż kredyty + dodatkowe użycie"; -"show_credits_extra_usage_subtitle" = "Pokazuje sekcje Codex Credits i Claude Extra usage w menu."; +"show_credits_extra_usage_subtitle" = "Pokazuje kredyty i dodatkowe użycie dla obsługiwanych dostawców."; "multi_account_layout_title" = "Układ wielu kont"; "multi_account_layout_subtitle" = "Wybierz przełączanie segmentowe kont lub ułożone karty kont."; "multi_account_layout_segmented" = "Segmentowy"; diff --git a/Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings b/Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings index 9fc56f647d..cbdabf07f6 100644 --- a/Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/pt-BR.lproj/Localizable.strings @@ -581,7 +581,7 @@ "show_provider_changelog_links_title" = "Mostrar links de changelog dos provedores"; "show_provider_changelog_links_subtitle" = "Adiciona links de notas de versão para provedores baseados em CLI compatíveis no menu."; "show_credits_extra_usage_title" = "Mostrar créditos + uso extra"; -"show_credits_extra_usage_subtitle" = "Mostra as seções de créditos do Codex e uso extra do Claude no menu."; +"show_credits_extra_usage_subtitle" = "Mostra créditos e uso extra para provedores compatíveis."; "multi_account_layout_title" = "Layout de múltiplas contas"; "multi_account_layout_subtitle" = "Escolha alternância segmentada de contas ou cartões de contas empilhados."; "multi_account_layout_segmented" = "Segmentado"; diff --git a/Sources/CodexBar/Resources/ru.lproj/Localizable.strings b/Sources/CodexBar/Resources/ru.lproj/Localizable.strings index debe92c848..9d8c4ec31a 100644 --- a/Sources/CodexBar/Resources/ru.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/ru.lproj/Localizable.strings @@ -585,7 +585,7 @@ "show_provider_changelog_links_title" = "Показывать ссылки на журналы изменений провайдеров"; "show_provider_changelog_links_subtitle" = "Добавляет в меню ссылки на заметки к релизам для поддерживаемых CLI-провайдеров."; "show_credits_extra_usage_title" = "Показывать кредиты и доп. использование"; -"show_credits_extra_usage_subtitle" = "Показывать в меню разделы кредитов Codex и дополнительного использования Claude."; +"show_credits_extra_usage_subtitle" = "Показывать кредиты и дополнительное использование для поддерживаемых провайдеров."; "multi_account_layout_title" = "Макет нескольких аккаунтов"; "multi_account_layout_subtitle" = "Выберите сегментированное переключение аккаунтов или сгруппированные карты аккаунтов."; "multi_account_layout_segmented" = "Сегментированный"; diff --git a/Sources/CodexBar/Resources/sv.lproj/Localizable.strings b/Sources/CodexBar/Resources/sv.lproj/Localizable.strings index 18e1d3c828..4ed8d62a6c 100644 --- a/Sources/CodexBar/Resources/sv.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/sv.lproj/Localizable.strings @@ -583,7 +583,7 @@ "show_provider_changelog_links_title" = "Visa länkar till leverantörers ändringsloggar"; "show_provider_changelog_links_subtitle" = "Lägger till länkar till utgåvekommentarer för stödda CLI-baserade leverantörer i menyn."; "show_credits_extra_usage_title" = "Visa krediter och extra användning"; -"show_credits_extra_usage_subtitle" = "Visa avsnitt för Codex-krediter och Claude Extra-användning i menyn."; +"show_credits_extra_usage_subtitle" = "Visa krediter och extra användning för leverantörer som stöds."; "multi_account_layout_title" = "Layout för flera konton"; "multi_account_layout_subtitle" = "Välj segmenterad kontoväxling eller staplade kontokort."; "multi_account_layout_segmented" = "Segmenterad"; diff --git a/Sources/CodexBar/Resources/th.lproj/Localizable.strings b/Sources/CodexBar/Resources/th.lproj/Localizable.strings index 686d42e6ef..caa98cbc50 100644 --- a/Sources/CodexBar/Resources/th.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/th.lproj/Localizable.strings @@ -590,7 +590,7 @@ "show_provider_changelog_links_title" = "แสดงลิงก์บันทึกการเปลี่ยนแปลงของผู้ให้บริการ"; "show_provider_changelog_links_subtitle" = "เพิ่มลิงก์บันทึกประจํารุ่นสําหรับผู้ให้บริการที่ได้รับการสนับสนุน CLI ในเมนู"; "show_credits_extra_usage_title" = "แสดงเครดิต + การใช้งานเพิ่มเติม"; -"show_credits_extra_usage_subtitle" = "แสดงส่วนเครดิต Codex และการใช้งานเพิ่มเติม Claude ในเมนู"; +"show_credits_extra_usage_subtitle" = "แสดงเครดิตและการใช้งานเพิ่มเติมสำหรับผู้ให้บริการที่รองรับ"; "multi_account_layout_title" = "รูปแบบหลายบัญชี"; "multi_account_layout_subtitle" = "เลือกการสลับบัญชีแบบแบ่งกลุ่มหรือบัตรบัญชีแบบเรียงซ้อน"; "multi_account_layout_segmented" = "แบ่งกลุ่ม"; diff --git a/Sources/CodexBar/Resources/tr.lproj/Localizable.strings b/Sources/CodexBar/Resources/tr.lproj/Localizable.strings index b8eb70a926..d9bb25ea5c 100644 --- a/Sources/CodexBar/Resources/tr.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/tr.lproj/Localizable.strings @@ -590,7 +590,7 @@ "show_provider_changelog_links_title" = "Sağlayıcı değişiklik günlüğü bağlantılarını göster"; "show_provider_changelog_links_subtitle" = "Desteklenen CLI destekli sağlayıcılar için menüye sürüm notları bağlantıları ekler."; "show_credits_extra_usage_title" = "Krediler + ekstra kullanımı göster"; -"show_credits_extra_usage_subtitle" = "Menüde Codex Kredileri ve Claude Ekstra kullanım bölümlerini göster."; +"show_credits_extra_usage_subtitle" = "Desteklenen sağlayıcılar için kredileri ve ek kullanımı göster."; "multi_account_layout_title" = "Çoklu hesap düzeni"; "multi_account_layout_subtitle" = "Bölümlü hesap değiştirme veya yığınlı hesap kartları seçin."; "multi_account_layout_segmented" = "Bölümlü"; diff --git a/Sources/CodexBar/Resources/uk.lproj/Localizable.strings b/Sources/CodexBar/Resources/uk.lproj/Localizable.strings index 359780b371..29667cfa5e 100644 --- a/Sources/CodexBar/Resources/uk.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/uk.lproj/Localizable.strings @@ -584,7 +584,7 @@ "show_provider_changelog_links_title" = "Показати посилання журналу змін провайдера"; "show_provider_changelog_links_subtitle" = "Додає в меню посилання на примітки до випуску для підтримуваних постачальників, що підтримують CLI."; "show_credits_extra_usage_title" = "Показати кредити + додаткове використання"; -"show_credits_extra_usage_subtitle" = "Показати в меню розділи використання кредитів Codex і Claude Extra."; +"show_credits_extra_usage_subtitle" = "Показувати кредити та додаткове використання для підтримуваних постачальників."; "multi_account_layout_title" = "Макет кількох облікових записів"; "multi_account_layout_subtitle" = "Виберіть сегментоване перемикання облікових записів або складені картки облікових записів."; "multi_account_layout_segmented" = "Сегментований"; diff --git a/Sources/CodexBar/Resources/vi.lproj/Localizable.strings b/Sources/CodexBar/Resources/vi.lproj/Localizable.strings index 14dbaabac6..096ed7b54e 100644 --- a/Sources/CodexBar/Resources/vi.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/vi.lproj/Localizable.strings @@ -580,7 +580,7 @@ "show_provider_changelog_links_title" = "Hiển thị Nhà cung cấp liên kết nhật ký thay đổi"; "show_provider_changelog_links_subtitle" = "Thêm liên kết ghi chú phát hành cho các nhà cung cấp được hỗ trợ CLI vào menu."; "show_credits_extra_usage_title" = "Hiển thị tín dụng + phần Mức sử dụng"; -"show_credits_extra_usage_subtitle" = "Hiển thị tín dụng Codex và Claude Các phần Mức sử dụng bổ sung trong menu."; +"show_credits_extra_usage_subtitle" = "Hiển thị tín dụng và mức sử dụng bổ sung cho các nhà cung cấp được hỗ trợ."; "multi_account_layout_title" = "Bố cục nhiều tài khoản"; "multi_account_layout_subtitle" = "Chọn thẻ tài khoản chuyển đổi phân đoạn hoặc thẻ tài khoản xếp chồng."; "multi_account_layout_segmented" = "Được phân đoạn"; diff --git a/Sources/CodexBar/Resources/zh-Hans.lproj/Localizable.strings b/Sources/CodexBar/Resources/zh-Hans.lproj/Localizable.strings index ae94fdb4b8..6a765a997d 100644 --- a/Sources/CodexBar/Resources/zh-Hans.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/zh-Hans.lproj/Localizable.strings @@ -560,7 +560,7 @@ "show_provider_changelog_links_title" = "显示提供商变更日志链接"; "show_provider_changelog_links_subtitle" = "在菜单中为支持的 CLI 提供商添加发布说明链接。"; "show_credits_extra_usage_title" = "显示额度 + 额外用量"; -"show_credits_extra_usage_subtitle" = "在菜单中显示 Codex 额度和 Claude 额外用量部分。"; +"show_credits_extra_usage_subtitle" = "显示受支持提供商的额度和额外用量。"; "multi_account_layout_title" = "多账户布局"; "multi_account_layout_subtitle" = "选择分段账户切换或堆叠账户卡片。"; "multi_account_layout_segmented" = "分段"; diff --git a/Sources/CodexBar/Resources/zh-Hant.lproj/Localizable.strings b/Sources/CodexBar/Resources/zh-Hant.lproj/Localizable.strings index c5a328b8c9..8f847514ac 100644 --- a/Sources/CodexBar/Resources/zh-Hant.lproj/Localizable.strings +++ b/Sources/CodexBar/Resources/zh-Hant.lproj/Localizable.strings @@ -583,7 +583,7 @@ "show_provider_changelog_links_title" = "顯示提供者版本資訊連結"; "show_provider_changelog_links_subtitle" = "在選單中為支援的 CLI 提供者新增發行說明連結。"; "show_credits_extra_usage_title" = "顯示額度 + 額外使用量"; -"show_credits_extra_usage_subtitle" = "在選單中顯示 Codex 額度和 Claude 額外使用量部分。"; +"show_credits_extra_usage_subtitle" = "顯示支援的供應商的額度和額外用量。"; "multi_account_layout_title" = "多帳號版面配置"; "multi_account_layout_subtitle" = "選擇分段帳號切換或堆疊帳號卡片。"; "multi_account_layout_segmented" = "分段"; diff --git a/Sources/CodexBar/StatusItemController+Menu.swift b/Sources/CodexBar/StatusItemController+Menu.swift index f5d0eaa43c..728b996856 100644 --- a/Sources/CodexBar/StatusItemController+Menu.swift +++ b/Sources/CodexBar/StatusItemController+Menu.swift @@ -1310,9 +1310,7 @@ extension StatusItemController { } if hasCredits { - if hasExtraUsage || hasCost { - addSectionSeparator() - } + addSectionSeparator() let creditsView = UsageMenuCardCreditsSectionView( model: model, showBottomDivider: false, @@ -1332,9 +1330,7 @@ extension StatusItemController { } } if hasExtraUsage { - if hasCredits { - addSectionSeparator() - } + addSectionSeparator() let extraUsageSubmenu = self.makeOpenAIAPIUsageSubmenu(provider: provider, width: width) let extraUsageView = UsageMenuCardExtraUsageSectionView( model: model, @@ -1350,9 +1346,7 @@ extension StatusItemController { submenu: extraUsageSubmenu)) } if hasCost { - if hasCredits || hasExtraUsage { - addSectionSeparator() - } + addSectionSeparator() let costSubmenu = webItems.hasCostHistory ? self .makeCostHistorySubmenu(provider: provider, width: width) : nil menu.addItem(self.makeCostMenuCardItem( diff --git a/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift b/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift index 244ae107be..59dec03b17 100644 --- a/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift +++ b/Sources/CodexBarCore/Providers/Cursor/CursorProviderDescriptor.swift @@ -13,8 +13,8 @@ public enum CursorProviderDescriptor { weeklyLabel: "Auto", opusLabel: "API", supportsOpus: true, - supportsCredits: true, - creditsHint: "On-demand usage beyond included plan limits.", + supportsCredits: false, + creditsHint: "", toggleTitle: "Show Cursor usage", cliName: "cursor", defaultEnabled: false, diff --git a/Tests/CodexBarTests/CursorMenuCardModelTests.swift b/Tests/CodexBarTests/CursorMenuCardModelTests.swift index bb0c4b8293..92152d2eb2 100644 --- a/Tests/CodexBarTests/CursorMenuCardModelTests.swift +++ b/Tests/CodexBarTests/CursorMenuCardModelTests.swift @@ -9,7 +9,12 @@ struct CursorMenuCardModelTests { let now = Date(timeIntervalSince1970: 0) let metadata = try #require(ProviderDefaults.metadata[.cursor]) - func makeModel(personalUsed: Double?) -> UsageMenuCardView.Model { + #expect(metadata.supportsCredits == false) + + func makeModel( + personalUsed: Double?, + showOptionalUsage: Bool = true) -> UsageMenuCardView.Model + { let snapshot = UsageSnapshot( primary: nil, secondary: nil, @@ -39,7 +44,7 @@ struct CursorMenuCardModelTests { usageBarsShowUsed: false, resetTimeDisplayStyle: .countdown, tokenCostUsageEnabled: false, - showOptionalCreditsAndExtraUsage: true, + showOptionalCreditsAndExtraUsage: showOptionalUsage, hidePersonalInfo: false, now: now)) } @@ -47,10 +52,14 @@ struct CursorMenuCardModelTests { let personal = makeModel(personalUsed: 44.71) let absent = makeModel(personalUsed: nil) let zero = makeModel(personalUsed: 0) + let hidden = makeModel(personalUsed: 44.71, showOptionalUsage: false) + #expect(personal.creditsText == nil) + #expect(personal.providerCost?.title == "Extra usage") #expect(personal.providerCost?.personalSpendLine == "Your spend: $44.71") #expect(absent.providerCost?.personalSpendLine == nil) #expect(zero.providerCost?.personalSpendLine == nil) + #expect(hidden.providerCost == nil) #expect(personal.heightFingerprint(section: "card") != absent.heightFingerprint(section: "card")) #expect(!personal.hasCompatibleTrackedLayout(with: absent)) #expect(!absent.hasCompatibleTrackedLayout(with: personal)) diff --git a/Tests/CodexBarTests/CursorProviderImplementationTests.swift b/Tests/CodexBarTests/CursorProviderImplementationTests.swift new file mode 100644 index 0000000000..3fd3dd1bea --- /dev/null +++ b/Tests/CodexBarTests/CursorProviderImplementationTests.swift @@ -0,0 +1,81 @@ +import CodexBarCore +import Foundation +import Testing +@testable import CodexBar + +@MainActor +struct CursorProviderImplementationTests { + @Test + func `on demand usage respects optional usage setting`() throws { + let snapshot = UsageSnapshot( + primary: nil, + secondary: nil, + providerCost: ProviderCostSnapshot( + used: 12, + limit: 60, + currencyCode: "USD", + period: "Monthly", + updatedAt: Date(timeIntervalSince1970: 0)), + updatedAt: Date(timeIntervalSince1970: 0)) + + var hiddenEntries: [ProviderMenuEntry] = [] + let hiddenContext = try Self.context(snapshot: snapshot, showOptionalUsage: false) + CursorProviderImplementation().appendUsageMenuEntries( + context: hiddenContext, + entries: &hiddenEntries) + #expect(hiddenEntries.isEmpty) + + var visibleEntries: [ProviderMenuEntry] = [] + let visibleContext = try Self.context(snapshot: snapshot, showOptionalUsage: true) + CursorProviderImplementation().appendUsageMenuEntries( + context: visibleContext, + entries: &visibleEntries) + + guard case let .text(title, style) = try #require(visibleEntries.first) else { + Issue.record("Expected Cursor on-demand usage menu text") + return + } + #expect(title == "On-demand: $12.00 / $60.00") + #expect(style == .primary) + } + + private static func context( + snapshot: UsageSnapshot, + showOptionalUsage: Bool) throws -> ProviderMenuUsageContext + { + let suite = "CursorProviderImplementationTests-\(UUID().uuidString)" + let defaults = try #require(UserDefaults(suiteName: suite)) + defaults.removePersistentDomain(forName: suite) + let settings = SettingsStore( + userDefaults: defaults, + configStore: testConfigStore(suiteName: suite), + zaiTokenStore: NoopZaiTokenStore(), + syntheticTokenStore: NoopSyntheticTokenStore(), + codexCookieStore: InMemoryCookieHeaderStore(), + claudeCookieStore: InMemoryCookieHeaderStore(), + cursorCookieStore: InMemoryCookieHeaderStore(), + opencodeCookieStore: InMemoryCookieHeaderStore(), + factoryCookieStore: InMemoryCookieHeaderStore(), + minimaxCookieStore: InMemoryMiniMaxCookieStore(), + minimaxAPITokenStore: InMemoryMiniMaxAPITokenStore(), + kimiTokenStore: InMemoryKimiTokenStore(), + augmentCookieStore: InMemoryCookieHeaderStore(), + ampCookieStore: InMemoryCookieHeaderStore(), + copilotTokenStore: InMemoryCopilotTokenStore(), + tokenAccountStore: InMemoryTokenAccountStore()) + settings.showOptionalCreditsAndExtraUsage = showOptionalUsage + let store = UsageStore( + fetcher: UsageFetcher(environment: [:]), + browserDetection: BrowserDetection(cacheTTL: 0), + settings: settings, + startupBehavior: .testing, + environmentBase: [:]) + + return ProviderMenuUsageContext( + provider: .cursor, + store: store, + settings: settings, + metadata: CursorProviderDescriptor.descriptor.metadata, + snapshot: snapshot) + } +} diff --git a/Tests/CodexBarTests/MenuCardOptionalUsageModelTests.swift b/Tests/CodexBarTests/MenuCardOptionalUsageModelTests.swift index 7784c571d8..57d5b2ac26 100644 --- a/Tests/CodexBarTests/MenuCardOptionalUsageModelTests.swift +++ b/Tests/CodexBarTests/MenuCardOptionalUsageModelTests.swift @@ -4,6 +4,38 @@ import Testing @testable import CodexBar struct MenuCardOptionalUsageModelTests { + @Test + func `hides supported provider credits when disabled`() throws { + let now = Date() + let metadata = try #require(ProviderDefaults.metadata[.augment]) + let credits = CreditsSnapshot(remaining: 12, events: [], updatedAt: now) + + func makeModel(showOptionalUsage: Bool) -> UsageMenuCardView.Model { + UsageMenuCardView.Model.make(.init( + provider: .augment, + metadata: metadata, + snapshot: nil, + credits: credits, + creditsError: nil, + dashboard: nil, + dashboardError: nil, + tokenSnapshot: nil, + tokenError: nil, + account: AccountInfo(email: nil, plan: nil), + isRefreshing: false, + lastError: nil, + usageBarsShowUsed: false, + resetTimeDisplayStyle: .countdown, + tokenCostUsageEnabled: false, + showOptionalCreditsAndExtraUsage: showOptionalUsage, + hidePersonalInfo: false, + now: now)) + } + + #expect(makeModel(showOptionalUsage: true).creditsText != nil) + #expect(makeModel(showOptionalUsage: false).creditsText == nil) + } + @Test func `hides codex credits when disabled`() throws { let now = Date() diff --git a/Tests/CodexBarTests/StatusMenuNativeSectionSpacingTests.swift b/Tests/CodexBarTests/StatusMenuNativeSectionSpacingTests.swift index c604c07dd6..ed689cca25 100644 --- a/Tests/CodexBarTests/StatusMenuNativeSectionSpacingTests.swift +++ b/Tests/CodexBarTests/StatusMenuNativeSectionSpacingTests.swift @@ -6,6 +6,62 @@ import Testing @MainActor @Suite(.serialized) struct StatusMenuNativeSectionSpacingTests { + @Test + func `standalone cost section has one separator above it`() throws { + let previousRendering = StatusItemController.menuCardRenderingEnabled + StatusItemController.menuCardRenderingEnabled = true + defer { StatusItemController.menuCardRenderingEnabled = previousRendering } + + let settings = self.makeSettings() + settings.statusChecksEnabled = false + settings.refreshFrequency = .manual + settings.mergeIcons = true + settings.selectedMenuProvider = .cursor + settings.costUsageEnabled = true + settings.costSummaryDisplayStyle = .both + self.enableOnly(.cursor, settings: settings) + + let fetcher = UsageFetcher() + let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings) + store._setTokenSnapshotForTesting(CostUsageTokenSnapshot( + sessionTokens: 123, + sessionCostUSD: 0.12, + last30DaysTokens: 123, + last30DaysCostUSD: 1.23, + daily: [ + CostUsageDailyReport.Entry( + date: "2025-12-23", + inputTokens: nil, + outputTokens: nil, + totalTokens: 123, + costUSD: 1.23, + modelsUsed: nil, + modelBreakdowns: nil), + ], + updatedAt: Date()), provider: .cursor) + + let controller = StatusItemController( + store: store, + settings: settings, + account: fetcher.loadAccountInfo(), + updater: DisabledUpdaterController(), + preferencesSelection: PreferencesSelection(), + statusBar: .system) + defer { controller.releaseStatusItemsForTesting() } + + let menu = controller.makeMenu(for: .cursor) + controller.menuWillOpen(menu) + let costIndex = try #require(menu.items.firstIndex { + ($0.representedObject as? String) == "menuCardCost" + }) + + #expect(costIndex > 0) + #expect(menu.items[costIndex - 1].isSeparatorItem) + #expect(!zip(menu.items, menu.items.dropFirst()).contains { first, second in + first.isSeparatorItem && second.isSeparatorItem + }) + } + @Test func `buy credits stays available without an error only credits section`() { let previousRendering = StatusItemController.menuCardRenderingEnabled @@ -18,7 +74,7 @@ struct StatusMenuNativeSectionSpacingTests { settings.mergeIcons = true settings.selectedMenuProvider = .codex settings.showOptionalCreditsAndExtraUsage = true - self.enableOnlyCodex(settings) + self.enableOnly(.codex, settings: settings) let fetcher = UsageFetcher() let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings) @@ -79,7 +135,7 @@ struct StatusMenuNativeSectionSpacingTests { settings.costUsageEnabled = true settings.costSummaryDisplayStyle = .both settings.providerStorageFootprintsEnabled = true - self.enableOnlyCodex(settings) + self.enableOnly(.codex, settings: settings) let fetcher = UsageFetcher() let store = UsageStore(fetcher: fetcher, browserDetection: BrowserDetection(cacheTTL: 0), settings: settings) @@ -245,10 +301,10 @@ struct StatusMenuNativeSectionSpacingTests { return settings } - private func enableOnlyCodex(_ settings: SettingsStore) { + private func enableOnly(_ enabledProvider: UsageProvider, settings: SettingsStore) { for provider in UsageProvider.allCases { guard let metadata = ProviderRegistry.shared.metadata[provider] else { continue } - settings.setProviderEnabled(provider: provider, metadata: metadata, enabled: provider == .codex) + settings.setProviderEnabled(provider: provider, metadata: metadata, enabled: provider == enabledProvider) } }