Skip to content

Show Cursor on-demand spend in dollars (#191) - #202

Merged
tsouth89 merged 2 commits into
mainfrom
feat/cursor-on-demand-spend
Aug 5, 2026
Merged

Show Cursor on-demand spend in dollars (#191)#202
tsouth89 merged 2 commits into
mainfrom
feat/cursor-on-demand-spend

Conversation

@tsouth89

@tsouth89 tsouth89 commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Closes #191.

I have reached 100% of my plan and now just seeing this 100% and didn't find a way to view the overdraft meter (preferably not only in % but also in $).

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:

  1. No dollars anywhere to put. RateWindow is a percentage plus reset metadata, so a metered lane could not carry currency at all. CostSnapshot is one slot per provider and cannot describe individual windows.
  2. Dropped in the overall branch. individual.on_demand was only read when a plan object was present, so accounts reporting overall lost the overdraft meter entirely.
  3. Dropped when uncapped. Deriving a percentage needs a denominator. On-demand enabled with real spend but no cap produced no meter, and the spend went on the floor.

Changes

  • Add an optional WindowAmount to NamedRateWindow, plumbed through the Tauri bridge to MetricRow. Any provider with a dollar-denominated lane gets this.
  • Read individual.on_demand in the overall branch, with the same team fallback as the plan branch.
  • Report uncapped on-demand spend as an explicit non-metering window instead of inventing a percentage.
  • Label on-demand cost On-demand rather than folding it into a generic Monthly.

Deliberately not included

Cursor's plan.breakdown carries a much larger figure (~$254 on my account). An exported usage report shows every such event as Included or Free with 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.00 against a $1 cap, which is the only real-money figure on that account.

796 rust, 473 tauri, 353 vitest, clippy clean.

Copilot AI lite review requested due to automatic review settings August 5, 2026 01:16
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 30 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 68bd3fb6-e054-4cce-a882-f55370349232

📥 Commits

Reviewing files that changed from the base of the PR and between 5a2c796 and cbcb8a2.

📒 Files selected for processing (17)
  • apps/desktop-tauri/src-tauri/src/auto_refresh.rs
  • apps/desktop-tauri/src-tauri/src/capacity_events.rs
  • apps/desktop-tauri/src-tauri/src/commands/bridge.rs
  • apps/desktop-tauri/src-tauri/src/commands/tests.rs
  • apps/desktop-tauri/src-tauri/src/enforcement.rs
  • apps/desktop-tauri/src-tauri/src/taskbar_widget.rs
  • apps/desktop-tauri/src-tauri/src/tray_bridge.rs
  • apps/desktop-tauri/src/components/MenuCard.test.tsx
  • apps/desktop-tauri/src/components/MenuCard.tsx
  • apps/desktop-tauri/src/i18n/keys.ts
  • apps/desktop-tauri/src/styles.css
  • apps/desktop-tauri/src/types/bridge.ts
  • rust/src/core/usage_snapshot.rs
  • rust/src/locale.rs
  • rust/src/locale/en-US.ftl
  • rust/src/locale/zh-CN.ftl
  • rust/src/providers/cursor/api.rs

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

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Comment thread rust/src/providers/cursor/api.rs
Comment thread rust/src/providers/cursor/api.rs
Comment thread rust/src/providers/cursor/api.rs
@tsouth89
tsouth89 deleted the branch main August 5, 2026 01:28
@tsouth89 tsouth89 closed this Aug 5, 2026
@tsouth89 tsouth89 reopened this Aug 5, 2026
@tsouth89
tsouth89 changed the base branch from chore/release-profile-and-provider-fixes to main August 5, 2026 01:28
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.
@tsouth89
tsouth89 force-pushed the feat/cursor-on-demand-spend branch from b584b65 to 4f79477 Compare August 5, 2026 01:31
- 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.
@tsouth89

tsouth89 commented Aug 5, 2026

Copy link
Copy Markdown
Owner Author

Thanks — all three were right, fixed in cbcb8a2:

  1. on_demand_amount and a missing used — now unwrap_or(0), matching usage_percent. A cap reported without used was dropping the entire amount instead of showing zero spend against a real limit. Covered by test_cursor_on_demand_cap_renders_without_a_used_field.
  2. Hard-coded $ in the uncapped notice — now formatted through WindowAmount::format_used(), so it takes the same currency path as every other amount instead of assuming USD at the call site.
  3. Team cost ordering — this was a genuine contradiction of the rule the PR states. On-demand now takes the cost slot ahead of team.pooled, matching the individual branch, with pooled as the fallback. Covered by test_cursor_team_on_demand_outranks_pooled_for_cost.

798 rust / 473 tauri / 353 vitest, clippy clean.

@tsouth89
tsouth89 merged commit f31fd2f into main Aug 5, 2026
11 checks passed
@tsouth89
tsouth89 deleted the feat/cursor-on-demand-spend branch August 5, 2026 01:44
tsouth89 added a commit that referenced this pull request Aug 5, 2026
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>
@tsouth89 tsouth89 mentioned this pull request Aug 5, 2026
tsouth89 added a commit that referenced this pull request Aug 5, 2026
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>
tsouth89 added a commit that referenced this pull request Aug 5, 2026
…#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>
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.

[Feature]: Show (cursor) on-demand spendings ($) when reached 100%

2 participants