Skip to content

[0.64.1] Add preferred display currency and TRY support - #609

Draft
Finesssee wants to merge 4 commits into
mainfrom
codex/port-0.64.1-preferred-currency
Draft

Finesssee wants to merge 4 commits into
mainfrom
codex/port-0.64.1-preferred-currency

Conversation

@Finesssee

@Finesssee Finesssee commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Port the v0.64.1 preferred display currency behavior, including TRY. A persisted preference controls presentation in Usage & Spend, tray labels, FloatBar, provider cost views, Claude Swap, and share PNGs. Conversion uses cached live rates with bundled fallback rates; copied and saved JSON exports retain source values.

The CI follow-up corrects Rust formatting and the Tauri async command return type. It also preserves non-currency unit labels such as Credits when conversion is unavailable, as required by an existing regression test.

Validation:

  • cargo fmt --all --check and git diff --check: passed.
  • cargo clippy --workspace --all-targets -- -D warnings: passed on Windows.
  • cargo test --workspace currency -- --test-threads=4: 14 passed across backend and Tauri.
  • Focused Vitest checks for currency, usageSpendSharing, GeneralTab, and ClaudeSwapAccountsSection: 40 passed.
  • pnpm --dir apps/desktop-tauri build: locale check, TypeScript, and Vite build passed.
  • Independent thermo review of the two corrective changes: approved.
  • Merged the shared CI test fixes from Stabilize tray layout and Claude login tests in CI #607 into this branch; the full hosted check is running for the updated head.

This remains a draft pending a fresh Windows desktop build and native UI proof of the complete feature.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

CI and review follow-up

Fixed the rustfmt failure, then the next hosted failure in the Tauri command macro: async commands borrowing State must return Result. The command now returns Ok(CurrencyRatesSnapshot), preserving the successful invoke payload.

The focused frontend run also caught an existing assertion failure (4.25 Credits was rendered as 4.25 CREDITS). The formatter now preserves non-currency labels while normalizing currency codes.

Verification: workspace clippy with warnings denied passed; 14 Rust currency tests and 40 focused frontend tests passed; the frontend production build and formatting checks passed. The two corrective changes received an independent thermo review with no blocker. These checks do not substitute for the complete feature's pending native Windows UI proof, so the PR remains draft.

The branch also contains the merged #607 CI test corrections. The full CircleCI gate must pass on the updated head.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Thermo-nuclear code-quality review

Verdict: FINDINGS (blocking). Reviewed the full feature at head 0fdbe730c, not just the CI follow-up. The branch is behind main.

P1: The currency model is implemented twice, in Rust and in TypeScript

Each of these exists in both languages and has to be kept in sync by hand:

Concept Rust TypeScript
Supported codes rust/src/currency.rs SUPPORTED_CURRENCY_CODES lib/currency.ts SUPPORTED_CURRENCIES
Fallback rates (16 hand-typed numbers) currency.rs:11 currency.ts:6
Normalize preference normalize_preferred_currency normalizePreferredCurrency (:25)
USD-pivot conversion convert_amount convertCurrencyAmount (:32)
Rate sanitizing parse_exchange_rates (:48) and shell clean_rates (commands/currency.rs:103) mergeValidCurrencyRates (:87)

That's two fallback tables to update whenever a rate changes, and three copies of sanitizing.

Suggested restructure: Rust owns the model, and the bridge carries the result. get_currency_rates already returns merged, sanitized rates, so also return supportedCodes and the normalized preferredCode. The frontend then keeps only Intl formatting. FALLBACK_CURRENCY_RATES, SUPPORTED_CURRENCIES, normalizePreferredCurrency, mergeValidCurrencyRates and the TS conversion code all go away, and the shell's clean_rates becomes a call to one codexbar::currency::sanitize_rates that the parser also uses.

P1: The tray path skips the cache and reads from disk

convert_preferred_amount (commands/currency.rs:79) runs Settings::load() and reads currency-rates.json from disk on every call. It's called from format_cost_amount for every Monthly Plan provider on every tray update. Meanwhile CurrencyRateCache is managed Tauri state holding the same rates. That's two sources of truth for one value, and the UI and tray can disagree between a fetch and the next disk read. Have CurrencyRateCache own the merged rates and the preference, and let both the command and the tray read from it.

P2: A second symbol table next to the canonical one

bridge.rs:~130 adds a four-entry USD/EUR/GBP/TRY → symbol match. core::usage_snapshot::format_currency (usage_snapshot.rs:590) already has that table, without TRY. The result is inconsistent output: JPY shows as ¥1,234 in the UI (Intl) and 1234.00 JPY in the tray. Extend the canonical formatter and call it from both places.

P2: "AUTO" is a stringly sentinel checked everywhere

preferred_currency_code: String (settings.rs:87) is normalized at load, which is good, but then re-normalized and compared against "AUTO" at every consumer in both languages. A typed PreferredCurrency::{Auto, Code(CurrencyCode)}, serialized as today, turns each == "AUTO" branch into a match and makes an invalid code impossible to represent after load. default_preferred_currency_code is also defined twice (settings.rs:607, settings/raw.rs:613).

P3: A second settings subscription

CurrencyProvider listens to both the codexbar:settings-updated DOM event and the Tauri settings-changed event (CurrencyProvider.tsx:50), then refetches the snapshot. That's a new subscription path alongside useSettings. Reuse the canonical one, or lift the settings subscription into a single provider that both consume.

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.

1 participant