Show Cursor on-demand spend in dollars (#191) - #202
Conversation
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 30 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (17)
Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ceiling | cbcb8a2 | Commit Preview URL Branch Preview URL |
Aug 05 2026, 01:41 AM |
There was a problem hiding this comment.
Pull request overview
Adds support for displaying Cursor’s on-demand (“overdraft”) spend in dollar terms alongside its usage meter, plumbing a per-window currency amount from the Rust provider through the Tauri bridge into the React UI.
Changes:
- Extend usage-window data to optionally carry a currency-denominated
WindowAmount, and bridge it into the desktop UI. - Fix Cursor parsing to surface on-demand windows in more response shapes (including
overall) and to represent uncapped on-demand spend as a non-metering notice window. - Add UI rendering + tests for showing “$used of $limit” beneath a metered lane.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| rust/src/providers/cursor/api.rs | Cursor usage parsing now surfaces on-demand windows more reliably and attaches per-window dollar amounts. |
| rust/src/core/usage_snapshot.rs | Introduces WindowAmount and adds amount to NamedRateWindow for per-lane currency display. |
| rust/src/locale/en-US.ftl | Adds PanelAmountOf string for composing “$X of $Y”. |
| rust/src/locale/zh-CN.ftl | Adds PanelAmountOf translation for the same UI string. |
| rust/src/locale.rs | Registers the new PanelAmountOf locale key. |
| apps/desktop-tauri/src/types/bridge.ts | Adds WindowAmountBridge and wires it into extra window types for frontend consumption. |
| apps/desktop-tauri/src-tauri/src/commands/bridge.rs | Maps Rust WindowAmount into a camelCase bridge object with preformatted strings. |
| apps/desktop-tauri/src/components/MenuCard.tsx | Renders the per-lane currency amount beneath a metric row when present. |
| apps/desktop-tauri/src/components/MenuCard.test.tsx | Adds tests verifying amount rendering with and without limits. |
| apps/desktop-tauri/src/styles.css | Adds styling for the new per-lane amount line. |
| apps/desktop-tauri/src/i18n/keys.ts | Adds PanelAmountOf to the allowed locale keys list. |
| apps/desktop-tauri/src-tauri/src/tray_bridge.rs | Updates test fixtures to include the new amount field. |
| apps/desktop-tauri/src-tauri/src/taskbar_widget.rs | Updates test fixtures to include the new amount field. |
| apps/desktop-tauri/src-tauri/src/enforcement.rs | Updates test fixtures to include the new amount field. |
| apps/desktop-tauri/src-tauri/src/commands/tests.rs | Updates command-bridge test fixtures to include the new amount field. |
| apps/desktop-tauri/src-tauri/src/capacity_events.rs | Updates window snapshot test builder to include the new amount field and adds/adjusts tests. |
| apps/desktop-tauri/src-tauri/src/auto_refresh.rs | Updates snapshot test fixtures to include the new amount field. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
On-demand is the only Cursor lane that bills real money, but it could go missing entirely and, when it did appear, had nowhere to put the dollar figure the user actually cares about. - Add an optional `WindowAmount` to `NamedRateWindow`, plumbed through the Tauri bridge to `MetricRow`. A metered lane can now carry currency alongside its bar; `CostSnapshot` is one slot per provider and cannot describe individual windows. - Read `individual.on_demand` in the `overall` branch too. It was only consulted when a `plan` object was present, so accounts reporting `overall` lost the overdraft meter completely. - Surface uncapped on-demand spend. `usage_percent` needs a denominator, so on-demand enabled with real spend but no cap produced no meter and the spend was dropped. It now reports as an explicit non-metering window rather than inventing a percentage. - Label on-demand cost as "On-demand" instead of folding it into a generic "Monthly". Verified against a live Cursor account: the on-demand lane now reads "$0.00 of $1.00" against a $1 cap, which is the only real-money figure on that account. Plan usage is metered at internal rates and billed nothing, so it is deliberately left out of the cost slot.
b584b65 to
4f79477
Compare
- Treat an omitted `used` as zero in `on_demand_amount`, matching `usage_percent`. A cap reported without a `used` field dropped the whole amount instead of showing zero spend against a real limit. - Format the uncapped notice through `WindowAmount` rather than a hard-coded `$`, so it follows the same currency path as every other amount. - Give on-demand the cost slot ahead of `team.pooled`, matching the individual branch. On-demand is the billed lane; the pooled allowance is not, and letting it win contradicted the stated rule.
|
Thanks — all three were right, fixed in cbcb8a2:
798 rust / 473 tauri / 353 vitest, clippy clean. |
Closes #190. > it would be really helpful if we had an "estimator" that would estimate if we are using too much usage and would not last until the end of week Stacked on #202. Review the third commit. ## Why nobody could see this Ceiling already computed the answer. It just could not be reached: - The detail pace section is `display: none` in the **tray** surface, which is exactly where the reporter was looking. The two-bar expected/actual block is too tall for the tray, which is a fair call and the reason for a compact version rather than deleting the rule. - The warning that fires on a predicted shortfall was retired as experimental: pinned to `false` on every settings load, with its toggle removed from the UI and a test asserting it stayed gone. It was unreachable regardless of what a user stored. - Even when enabled, it was restricted to Claude and Codex. ## Visual `PaceVerdict`: a headline, its consequence, and one slim bar whose tick marks where usage should be by this point in the window. Same conclusion as the expected-vs-actual pair at roughly a third of the height. Shown in the tray; the taller breakdown stays for the window view. A pace that still runs out early never renders in the calm "slow" colour, since the shortfall is what matters, not that you were technically behind. ## Warning - Stop pinning the setting off on load; restore the toggle under Settings > Notifications. It stays **opt-in**. - Let any provider raise it. Providers that never report an account were skipped for want of a dedupe identity, and now fall back to the source label. - Name the window by real cadence. Warnings were labelled by slot, so a monthly quota would have announced itself as a `Session` limit once ungated. ## Verification 797 rust, 474 tauri, 362 vitest, clippy clean. New tests cover the verdict states, tick/fill placement, out-of-range clamping, ETA formatting, the settings round trip, and cadence naming. --------- Co-authored-by: tsouth89 <tsouth89@users.noreply.github.com>
Prepares **Ceiling 1.5.23**, carrying the two feature requests merged since v1.5.22 plus two cleanups found while verifying them. - **#202** — Show Cursor on-demand spend in dollars (closes #191) - **#203** — Add a pace verdict and re-expose predictive warnings (closes #190) ## Also in this PR ### Removed the Cursor "Promotional" lane The percentage was `plan.used - breakdown.included`, clamped to the bonus. The included lane is its own closed set — `plan.used == breakdown.included == plan.limit` on every real payload — so the subtraction was identically zero and the meter read **0% permanently**. Confirmed against a live account: `Promotional 0.00%`. The `PromoSignal` badge it fed took its expiry from the billing cycle end, while Cursor credits expire on their own schedule (the dashboard shows a 2027 date on my account). So it was telling users their bonus expired about a year early. Neither number is recoverable: `breakdown.bonus` is bonus *consumed*, and the grant it would be metered against is not in the payload. Removed rather than guessed at. ### Stopped mutating process environment in cost-scanner tests `unsafe { env::set_var(...) }` on `CODEX_HOME`, `CLAUDE_CONFIG_DIR`, and `GROK_HOME` is undefined behaviour while any other thread reads those variables, and nine modules read them constantly. Unrelated scans intermittently resolved the wrong home. The failure was also disguised: one panicking test poisoned the shared mutex, and the three tests that then failed on `.expect()` made a single fault look like four. `CostScanner` now takes an injected `ambient_home_override`, so no test touches process env. The mutex and its now-dead helper constructor are gone. Measured before/after: **~1 failing run in 5 → 12 consecutive clean runs.** ## Not included A sweep of ~36 providers turned up three more meters that may rest on inferred denominators (`commandcode`, `minimax`, `manus`). None could be verified without accounts on those services, and guessing at field semantics is what produced the bug this PR removes. Left alone deliberately; worth a follow-up issue for someone who can check them. ## Verification 796 rust / 474 tauri / 364 vitest, clippy clean, tsc clean. --------- Co-authored-by: tsouth89 <tsouth89@users.noreply.github.com>
…#205) Follow-up to #203 and #202, prepared as **1.5.24**. ## Why this exists #203 put a pace verdict in `MenuCard`. `<MenuCard` renders in exactly one place — `TrayPanel.tsx:466`. The main app window is `PopOutPanel`, which renders `ProviderDetailView`, a component that change never touched. So the pace answer, and the Cursor on-demand dollars from #202, never appeared in the window most people use. ## The marker Every weekly and monthly bar in the Overview and in a provider's detail view now shows where usage *should* be. One rule everywhere: **the marker is where the bar's edge should be right now.** Overspending fills the span between edge and marker with a striped band, so it reads as a distance rather than as a line you have to decode. Bars showing remaining capacity mirror the marker, and `ahead` is computed from usage rather than bar geometry so it means the same thing in both modes — pinned by a test asserting both modes agree on the same window. Derived from `elapsed / duration`, not from `PaceSnapshot`. That means no provider support is needed and it applies to every long window, rather than the single window pace is computed for. Windows under 12h are skipped: a 5-hour session is not spent evenly, so a marker there would sweep across the bar and mean nothing. ## Cursor on-demand `ProviderDetailView` filtered it out by name: ```ts .filter((metric) => !/promotional|on-demand/i.test(metric.label)) ``` So #202's plumbing was landing correctly and being discarded at the last step. Filter removed; the **On-demand** row now shows with its spend beside it. (`promotional` no longer exists at all after #204.) ## Two bugs only running the app could find 1. **`.usage-bar` collided with an existing component** at `styles.css:1831`, which sets `display: flex`. My bar became a flex item, collapsed to zero width, and vanished — while the absolutely-positioned marker kept rendering, leaving markers floating with no bars. Renamed to `.pace-overlay*`. 2. **Red on red was invisible.** On an exhausted bar the fill is already red, so a red marker and band disappeared into it. Both now use the text colour. Neither was reachable from unit tests or a static mock. ## Verification Run against live provider accounts and screenshotted, plus 801 rust / 375 vitest, clippy clean, tsc clean, and `local-check.ps1 -All -Version 1.5.24` green. One stale test updated: `ProviderDetailView.test.tsx` asserted the filtered lane was absent. Its fixture now carries an on-demand window with money and asserts it renders. --------- Co-authored-by: tsouth89 <tsouth89@users.noreply.github.com>
Closes #191.
Stacked on #201. Review the second commit.
What was wrong
On-demand is the only Cursor lane that bills real money, and it had three ways to go missing:
RateWindowis a percentage plus reset metadata, so a metered lane could not carry currency at all.CostSnapshotis one slot per provider and cannot describe individual windows.overallbranch.individual.on_demandwas only read when aplanobject was present, so accounts reportingoveralllost the overdraft meter entirely.Changes
WindowAmounttoNamedRateWindow, plumbed through the Tauri bridge toMetricRow. Any provider with a dollar-denominated lane gets this.individual.on_demandin theoverallbranch, with the same team fallback as the plan branch.On-demandrather than folding it into a genericMonthly.Deliberately not included
Cursor's
plan.breakdowncarries a much larger figure (~$254 on my account). An exported usage report shows every such event asIncludedorFreewith no charge, so that number is usage metered at internal rates, not money owed. Putting it in a cost card would tell users they owe hundreds of dollars when they owe nothing. Cursor is also mid-migration from dollar-denominated usage to tokens, so those cents fields are on their way out.Verification
Against a live Cursor account the on-demand lane now reads
$0.00 of $1.00against a $1 cap, which is the only real-money figure on that account.796 rust, 473 tauri, 353 vitest, clippy clean.