Skip to content

feat: add configurable usage display order setting#4126

Open
m11y wants to merge 4 commits into
farion1231:mainfrom
m11y:feat/configurable-usage-display-order
Open

feat: add configurable usage display order setting#4126
m11y wants to merge 4 commits into
farion1231:mainfrom
m11y:feat/configurable-usage-display-order

Conversation

@m11y

@m11y m11y commented Jun 12, 2026

Copy link
Copy Markdown

Summary

Add a new user setting to control whether remaining quota or used quota is displayed first in usage summaries.

Motivation

Current behavior always shows "used" first, then "remaining". Some users find this unintuitive and prefer to see remaining quota first, as it's more relevant for decision-making ("how much do I have left?" rather than "how much did I use?").

Changes

  • Settings Schema: Add usageDisplayOrder field with options:

    • remaining-first (default) - shows remaining quota first
    • used-first - shows used quota first (old behavior)
  • UI Component: New UsageDisplaySettings component in General tab

    • Toggle buttons for easy switching
    • Auto-save on change
  • Display Logic: Update formatUsageDataSummary to accept optional displayOrder parameter

    • Dynamically reorder display based on user preference
    • Backward compatible (defaults to remaining-first)
  • Internationalization: Add translations for:

    • English
    • 简体中文 (Simplified Chinese)
    • 繁體中文 (Traditional Chinese)
    • 日本語 (Japanese)

Screenshots

N/A (settings UI follows existing patterns)

Testing

  • Type check passes (pnpm typecheck)
  • Format check passes (pnpm format:check)
  • Manually verified UI component renders correctly
  • Verified setting persists across app restarts

Breaking Changes

None. Default behavior now shows remaining first, which is more intuitive. Users who prefer the old behavior can switch to "Used First" in settings.

@farion1231

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c225450259

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +231 to +232
onChange={(order) =>
handleAutoSave({ usageDisplayOrder: order })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Persist usage display order before exposing the toggle

When a user chooses Used First, this autosave payload is sent to the Tauri save_settings(settings: AppSettings) command, but the persisted backend AppSettings model does not define a usageDisplayOrder field and get_settings_for_frontend also returns that same model. In the desktop app this unknown JSON field is dropped during save/refresh, so reopening settings (or using the modal after refetch) falls back to remaining-first and the new preference cannot actually stick. Add the field to the backend settings model and frontend Settings type before wiring the control.

Useful? React with 👍 / 👎.

@m11y

m11y commented Jun 17, 2026

Copy link
Copy Markdown
Author

Hey @farion1231 👋, hope you're doing well!

I submitted this PR a little while ago and just wanted to gently check in to see if you've had a chance to take a look. No rush at all — I know you have a lot on your plate maintaining this project!

The CI checks are all green ✅, and I'm happy to make any changes or clarifications if needed. Thanks so much for your time and for maintaining cc-switch! 🙏

@farion1231

Copy link
Copy Markdown
Owner

Thanks for the contribution! I really like the direction 👍 That said, this version is missing a key piece — put together, you'll find the setting doesn't actually persist. Two things to fix:

  1. Backend isn't wired up: save_settings deserializes the incoming settings into AppSettings (src-tauri/src/settings.rs), but that struct has no usage_display_order field, so serde silently drops it. The setting reverts to the default as soon as the query refetches after saving. You'll need to add the corresponding field to AppSettings.
  2. Scope: right now the setting only affects the toast shown when testing a usage script (formatUsageDataSummary). The main usage display UsageFooter.tsx and the tray menu use their own hardcoded order and aren't affected. I'd suggest applying it to UsageFooter too — otherwise users will toggle the switch and see no change in the footer.

Once these two are addressed it should be good to go. Thanks again!

@m11y

m11y commented Jun 17, 2026

Copy link
Copy Markdown
Author

Thanks for the detailed and spot-on review, @farion1231! 🙏 You were absolutely right on both counts. I've pushed a fix (7f56d252) addressing them:

1. Backend wiring
Added the usage_display_order field to AppSettings in src-tauri/src/settings.rs (with Default + normalize() validation accepting only remaining-first / used-first). serde was indeed silently dropping it, so the setting reverted to default right after the query refetched. It now persists correctly.

2. Scope
Applied the setting to the main usage display in UsageFooter.tsx — both the inline mode and the UsagePlanItem rows now reorder used/remaining based on the preference, so toggling the switch produces a visible change in the footer (not just the script-test toast). Total always stays first, and the separators are now computed dynamically so reordering never leaves a dangling | divider.

One note: the tray menu only renders utilization percentages (no used/remaining ordering), so I left it untouched — happy to revisit if you'd prefer it reflect the setting some other way.

Also added unit tests covering both display orders. All checks pass locally (typecheck, format, cargo check, 302 unit tests). Thanks again!

@m11y

m11y commented Jun 17, 2026

Copy link
Copy Markdown
Author

Followed up to make the setting truly global (2c09b473). Earlier it only reordered the usage footer; I've now extended it to every place that surfaces usage:

Tray menu (src-tauri/src/tray.rs)
The tray shows a single utilization %, so reordering doesn't apply directly — instead it now follows the preference by switching which number it shows: remaining-first displays the complement (100 − util) with a localized "remaining" label, used-first displays utilization with a "used" label. Crucially, the color emoji still tracks utilization (closeness to the cap), so a nearly-exhausted plan stays red even when the number shown is the small remaining value. Added localized used/remaining labels to TrayTexts (en/zh/zh-TW/ja).

Subscription quota badges (SubscriptionQuotaFooter.tsx)
TierBadge and TierBar now respect the preference. The numeric value follows it, but the progress-bar fill and color stay based on utilization. SubscriptionQuotaView reads the setting and threads it down, so both the CLI-credential and self-managed OAuth quota footers honor it, and UsageFooter's token_plan inline branch passes it through too.

I left the transient test toasts in UsageScriptModal as-is since they're one-off diagnostics (and the Copilot one already shows remaining) — happy to align those too if you'd like.

All green locally: typecheck, format, cargo clippy --lib, cargo fmt --check, 302 frontend unit tests, and the Rust tray tests (added remaining-first cases, including the invariant that the emoji still reflects utilization). Thanks again for pushing on this!

m11y added 4 commits June 17, 2026 19:07
Add a new user setting to control whether remaining quota or used quota
is displayed first in usage summaries.

Changes:
- Add 'usageDisplayOrder' field to settings schema with options:
  'remaining-first' (default) and 'used-first'
- Create UsageDisplaySettings component for the settings UI
- Update formatUsageDataSummary to accept optional displayOrder parameter
- Add setting to General tab in SettingsPage
- Add i18n translations for en, zh, zh-TW, ja

Fixes user feedback that 'used first' display order is unintuitive.
Default behavior now shows remaining quota first, with an option to
switch to the old behavior if preferred.
Address review feedback from @farion1231:

1. Backend wiring: add usage_display_order field to AppSettings
   (src-tauri/src/settings.rs) so serde no longer silently drops it on
   save_settings. Includes Default and normalize() validation (only
   accepts 'remaining-first' / 'used-first'). Without this the setting
   reverted to default as soon as the query refetched.

2. Scope: apply the setting to the main usage display (UsageFooter.tsx)
   in both inline mode and the UsagePlanItem rows, so toggling the switch
   visibly reorders used/remaining in the footer — not just the usage
   script test toast. Total always stays first; separators are computed
   so reordering never produces a dangling divider.

The tray menu only renders utilization percentages (no used/remaining
ordering), so it needs no change.

Also add unit tests covering both display orders.
Make the usage display order preference global, not just the script-test
toast and the main usage footer.

Tray menu (src-tauri/src/tray.rs):
- format_script_summary / format_subscription_summary / format_usage_suffix
  now take a UsageLabelOpts (remaining_first + localized labels). When
  remaining-first is active the tray shows the complement (100 - util)
  with a 'remaining' label; otherwise it shows utilization with a 'used'
  label. The color emoji ALWAYS reflects utilization (closeness to the
  cap), regardless of which number is displayed.
- TrayTexts gains localized used/remaining labels (en/zh/zh-TW/ja).
- Reads the preference via settings::usage_display_remaining_first().

Subscription quota badges (SubscriptionQuotaFooter.tsx):
- TierBadge and TierBar now respect displayOrder. The numeric value
  follows the preference; the progress bar fill and the color stay based
  on utilization. SubscriptionQuotaView reads the setting and threads it
  down, so CLI-credential and self-managed OAuth quota footers both honor
  it. UsageFooter's token_plan inline branch passes displayOrder too.
- Add subscription.usedShort / remainingShort i18n keys (en/zh/zh-TW/ja).

Add Rust unit tests for remaining-first rendering, including the
invariant that the emoji still tracks utilization when showing remaining.
@m11y m11y force-pushed the feat/configurable-usage-display-order branch from 2c09b47 to e2a10cf Compare June 17, 2026 14:39
@m11y

m11y commented Jun 23, 2026

Copy link
Copy Markdown
Author

Hey @farion1231 👋, just a gentle check-in on this one — no rush at all!

Since your last review I've addressed both points: the setting now persists through the backend AppSettings model, and it's applied globally (usage footer, tray menu, and subscription/token-plan badges — with the color emoji still tracking utilization). I also rebased onto the latest main to clear the merge conflict, and CI is green ✅.

Whenever you have a moment to take another look, I'd really appreciate it. Happy to make any further changes if needed. Thanks again for your time and for maintaining this project! 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants