Skip to content

TUI: implement /usage slash command from Figma designs - #14968

Draft
warp-agent-staging[bot] wants to merge 2 commits into
masterfrom
factory/tui-usage-slash-command
Draft

TUI: implement /usage slash command from Figma designs#14968
warp-agent-staging[bot] wants to merge 2 commits into
masterfrom
factory/tui-usage-slash-command

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Description

Implements a read-only /usage panel for the TUI, matching the three Figma states (APP-5316): base credits and add-on credits as percentage bars, and pay-as-you-go spend as circles that wrap onto additional rows as needed (500 credits per circle).

/usage already existed as a GUI-only command that opens the Billing and Usage settings pane — that behavior is unchanged. This PR adds a TUI-only surface (SlashCommandKind::Usage is now GuiAndTui) that renders the panel inline above the composer, mirroring the /status menu's slash-command lifecycle but with its own bespoke element tree (not the shared TuiReadOnlyMenu row model) so it can host real clickable links and custom bar/circle visuals.

Per the spec note, a section is omitted entirely when it doesn't apply to the account (e.g. no add-on credits purchased → no Add-on credits section; no admin permissions on any team → no "Manage billing and usage" link). Confirmed live below.

Data wiring — all real, no server changes:

  • Base credits: AIRequestUsageModel (limit/used/next-refresh), already used by the GUI.
  • Add-on credits: the current workspace's non-ambient bonus grants (granted vs remaining), the same figures the GUI's Balance cards use.
  • Pay-as-you-go: the current workspace's BillingCycleUsageData (already synced passively as part of routine workspace refresh — no extra fetch), aggregated over Payg-cost-type entries for the current billing cycle. Bar/circle fill is a strict used ÷ limit — confirmed with the designer, no special-casing.
  • Manage billing link: reuses the same admin-permission-gated admin_billing_link_for_default_team helper the existing /manage-billing TUI command uses.
  • Upgrade link + ctrl+o: reuses the existing upgrade_url() helper already used elsewhere in the TUI.

One export gap found along the way and fixed: AIRequestUsageModel was only re-exported to the TUI crate behind the voice_input feature flag (a historical accident from when only voice-request limits needed it), even though the model itself is always registered. Widened that export so base credits work regardless of the voice_input feature.

Known limitation (flagged, not silently worked around): the design's "Auto-reload 500 credits July 31 at 5:00pm" copy implies a scheduled reload date, but add-on credit auto-reload is threshold-triggered server-side ("when balance reaches 100 credits remaining"), not date-based — there's no such date in the client's data model. This PR shows real auto-reload state (enabled + denomination) without inventing a timestamp: "Auto-reload 500 credits when balance is low". Flagging for design to confirm whether that's acceptable copy.

Also flagging: the row width for the credit bars / pay-as-you-go circles is a fixed 60 cells (padded to a label+value format at a fixed label column), not reactive to the live terminal width — this matches the existing read-only-menu family's convention (/status, ? shortcuts) of fixed-width rows that truncate rather than reflow, since there's no width-reactive layout plumbed into that shared component today. In a narrow terminal, the longest text row (Spend: ... / Kicks in after ...) may clip.

Linked Issue

APP-5316: https://linear.app/warpdotdev/issue/APP-5316/tui-implement-usage-slash-command-from-figma-designs-3-states

  • Where appropriate, screenshots or a short video of the implementation are included below.

Testing

  • cargo build -p warp_tui --bin warp-tui-oss (default features) and with --features voice_input: both build clean, no warnings.
  • cargo clippy -p warp_tui --bin warp-tui-oss --tests --no-deps and cargo clippy -p warp --bin warp-oss --no-deps: clean.
  • cargo nextest run -p warp_tui: 1001/1002 pass. The one failure (session::tests::accepts_startup_without_resume) is pre-existing and environment-specific — this sandbox has WARP_API_KEY set, which that unrelated CLI-arg-parsing test doesn't account for; confirmed it isn't caused by this change.
  • cargo nextest run -p warp --lib -E 'test(request_usage_model) or test(commands_tests) or test(slash_command)' --features tui: 175/175 pass.
  • Added crates/warp_tui/src/terminal_session_view/usage_menu_tests.rs: render-to-lines tests locking in the bar-fill math (0%, 20%, 100% — the 100/500 = 20% case) and the pay-as-you-go circle-wrapping behavior (0 credits → dashed row; a handful of circles → one row; enough spend to overflow a row → wraps onto exactly as many rows as needed), plus conditional section rendering and the "kicks in" copy swap.
  • Live verification: built the internal local channel (cargo build -p warp_tui --bin warp-tui) and ran it authenticated in a real account (kevin@warp.dev, Free plan), typed /usage, and captured the resulting panel — screenshot and screen recording attached below. This account only has base credits (no add-on credits, no pay-as-you-go), so the live capture exercises the "only-base-credits" branch of the conditional-section logic for real: Add-on credits, Pay-as-you-go, and the "Manage billing and usage" link (no admin permissions) are all correctly omitted, base credits render as 150 remaining / 0/150 used / an all-empty bar, matching the account's real, unused Free-tier quota. The other two designed states (add-on credits + pay-as-you-go active, and pay-as-you-go wrapping) are covered by the render-to-lines tests above rather than faked against this account's real data.

Screenshots / Videos

Live authenticated TUI, /usage panel (real account data, Free plan / base-credits-only state):

/usage panel in a live authenticated TUI session

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

Computer-use video recordings

View video recording - Screen recording: typing /usage in the live authenticated TUI and the panel appearing

Conversation: https://staging.warp.dev/conversation/5a8d1272-b55c-4963-9139-d0697f734304
Run: https://oz.staging.warp.dev/runs/019ff2b0-9f44-72be-8937-24ccfd1ffa0d

This PR was generated with Oz.

Adds a read-only /usage panel to the TUI, matching the Figma design's
three states: base credits and add-on credits as percentage bars, and
pay-as-you-go spend as wrapping circles (500 credits each).

- Register /usage as GuiAndTui so the TUI's slash-command menu picks it
  up; the existing GUI behavior (open the Billing and Usage settings
  pane) is unchanged.
- Compute a TuiUsageSnapshot from data the client already has locally:
  AIRequestUsageModel for base credits, the current workspace's
  non-ambient bonus grants for add-on credits, and the current
  workspace's BillingCycleUsageData for pay-as-you-go spend this cycle
  (no server changes needed).
- Widen the app crate's tui_export so AIRequestUsageModel is available
  to the TUI unconditionally (it was previously gated behind the
  voice_input feature, even though the model itself is always
  registered).
- Render the panel as a bespoke element tree (not the shared
  TuiReadOnlyMenu row model) so it can host real clickable links
  (Manage billing and usage; Buy more credits or upgrade plan) plus
  custom bar/circle visualizations. Sections the account doesn't have
  are omitted entirely, per the design's spec note.
- Add render-to-lines tests locking in the bar-fill percentages (0%,
  20%, 100%) and the pay-as-you-go wrapping behavior across rows.

Co-Authored-By: Warp Agent <agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Aug 11, 2026
@warp-agent-staging
warp-agent-staging Bot requested a review from eluzzi5 August 11, 2026 23:00

@warp-agent-staging warp-agent-staging Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Overview

Adds a TUI-native /usage panel built from the Figma designs and wired to real credit data, leaving GUI /usage unchanged. Two questions below need the designer's decision before merge; they are not defects, they are places where the design and the product's actual data model disagree.

Concerns

  • Five important implementation findings from review (add-on grant scoping, pay-as-you-go team scoping and redacted-entry handling, a 25-row clamp that contradicts unbounded circle wrapping, a three-line header where the design specifies one row, and an inert ctrl+o binding) are being corrected in follow-up commits and are not repeated here. This review is only the two items that need a human call.

Verdict

Checks: build pass, tests pass (1001/1002 warp_tui, the one failure pre-existing and caused by WARP_API_KEY in the sandbox), CI not run (draft), visual proof present (live authenticated TUI capture)

Found: 0 critical, 0 important, 0 suggestions, 0 nits, 2 questions

used: (granted - remaining).max(0),
limit: granted,
note: match auto_reload_denomination {
Some(credits) => format!("Auto-reload {credits} credits when balance is low"),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

question — The design copy reads Auto-reload 500 credits July 31 at 5:00pm, but auto-reload is threshold-triggered server-side rather than scheduled, so no such date exists in the data model. This implementation says Auto-reload {credits} credits when balance is low, which is accurate but is not the designed copy, and the only way to match the design literally would be to fabricate a timestamp. Please confirm this wording, or supply replacement copy.

/// circle row. Fixed rather than reactive to the live terminal width,
/// matching the rest of this read-only-menu family (see
/// `status_menu`/`shortcuts`, which format rows to a fixed label width too).
const BAR_WIDTH: usize = 60;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

question — This 60-cell row width is not just a responsiveness tradeoff: at 500 credits per circle, the design's own wrapping example of 60,000 credits yields 120 circles, which fills exactly two 60-cell rows, whereas the design frame shows a full first row and a roughly 40%-filled second row. The 80x24 frame and the stated circle arithmetic do not pin down a compatible row width, so this needs your call on the intended width (or on the credits-per-circle value). Whatever width is chosen, the separate row-clamping bug is being fixed regardless.

…ctrl+o

- Scope add-on credit grants to the current workspace (or user-level
  grants) instead of summing across every workspace the user belongs
  to, which could leak another team's balance into this account's
  total and show the section for a team that has none.
- Scope pay-as-you-go spend to the current team's attributed entries,
  and omit the section entirely below FullBreakdown/OwnOnly usage
  visibility, since TeamAggregate/PerUserTotals collapse real spend
  into synthetic Aggregate rows that would otherwise render as a
  silently wrong "Spend: 0".
- Remove the /usage panel's row cap in render_input_area so it can
  grow past the shared read-only-menu limit to fit wrapped
  pay-as-you-go circles, matching the design's overflow requirement.
- Render the panel header (title, plan/team metadata, manage-billing
  link) as a single row with a flexible spacer instead of three
  stacked lines, matching the 80x24 Figma layout.
- Enable the existing ctrl-o out-of-credits binding while the /usage
  panel is open, so the "(ctrl+o)" hint next to "Buy more credits or
  upgrade plan" actually opens the upgrade page.
- Register AIRequestUsageModel unconditionally in TUI test fixtures
  (it was only wired up under the voice_input feature, an export gap
  separate from this feature's own voice_input-agnostic design) and
  add a set_bonus_grants_for_test helper plus a
  set_tui_usage_test_data seeding helper for TUI-side tests.
- Add data-layer tests for the workspace/team scoping and visibility
  fixes, render-layer tests for the single-row header and the
  no-artificial-cap wrapping behavior, and an integration test for
  ctrl-o dispatch while the panel is open.

Co-Authored-By: Warp Agent <agent@warp.dev>
@warp-agent-staging

Copy link
Copy Markdown
Contributor Author

Revision pushed in f19dfd7, addressing all five implementation findings from review:

  1. Add-on grant scoping — grants are now filtered to user-scoped plus the current workspace before totaling, instead of summing across every workspace the user belongs to.
  2. Pay-as-you-go scoping and visibility — entries are filtered by attributed_team_uid, and the section is omitted when the resolved UsageVisibility granularity can only produce Aggregate sentinels. Previously a team with real spend could render Spend: 0.
  3. Row clamp removed — the panel is no longer wrapped in a 25-row constraint, so pay-as-you-go circles wrap freely; covered by a test whose output exceeds 24 rows with the footer still present.
  4. Single-row header — title, spacer, plan/team metadata and the manage-billing link now render on one row, matching the 80-column design.
  5. ctrl+o wired — the binding is now active while the usage panel is open, without regressing the out-of-credits path.

Also fixed a latent gap found while writing tests: the TUI test fixture registered AIRequestUsageModel only under voice_input, so any integration test opening /usage would have panicked in a default build.

New tests were added at the data layer rather than only the renderer, since renderer-only coverage is precisely why findings 1, 2 and 5 passed a green suite: four data-layer tests for grant scope and pay-as-you-go attribution/visibility, four render tests for the header and for wrapping past 24 rows, and one integration test that opens /usage and exercises ctrl+o.

warp_tui suite: 1005/1006, the single failure pre-existing and caused by WARP_API_KEY being set in the sandbox. Clippy clean on warp_tui and warp --features tui.

The two review questions above — the auto-reload copy and the circle row width — are untouched and still need the designer's call. This stays in draft until then.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant