Skip to content

feat(web): redesign usage insights - #7147

Open
maria-rcks wants to merge 1 commit into
agent/ui-workspace-refreshfrom
ui-refresh/usage
Open

feat(web): redesign usage insights#7147
maria-rcks wants to merge 1 commit into
agent/ui-workspace-refreshfrom
ui-refresh/usage

Conversation

@maria-rcks

@maria-rcks maria-rcks commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

What changed

  • Reworks Usage around provider summaries, API cost estimates, hourly and daily curves, clamped hover details, and a simpler totals breakdown.
  • Adds per-provider session counts while retaining source-fingerprint ownership so the same transcript directory is counted once across environments.
  • Uses the shared workspace header and segmented controls from feat(web): unify workspace navigation #7153.

Screenshots

Direct parent on the left; this PR on the right. Same viewport and copied application state.

BeforeAfter
Usage page before Usage page after
## Why

Usage was mixed with unrelated workspace, Pull Requests, terminal, and tool-call work. This layer keeps the presentation and its small shared aggregation change together for one review.

Validation

  • UsageProviderChart tests
  • usageMerge tests, including duplicate-source provider session ownership
  • Affected-package typechecks
  • Changed-file lint and formatting checks

Stack order

  1. feat(web): unify workspace navigation #7153 workspace and navigation
  2. feat(web): redesign usage insights #7147 Usage, this PR
  3. feat(web): refresh pull request details #7148 Pull Requests
  4. feat(web): improve terminal workspace controls #7149 terminal
  5. feat(web): attach composer state drawers #7150 composer
  6. fix(server): preserve tool lifecycle identity #7151 tool lifecycle projection
  7. feat(web): group live tool activity #7152 tool activity UI

Built with GPT-5.6-sol in the Codex harness.


Note

Medium Risk
Touches shared usage aggregation (mergeUsage) and presentation of cost/session totals; behavior is covered by tests but multi-environment deduplication logic is sensitive.

Overview
Redesigns the Usage page around shared workspace chrome (WorkspacePageHeader / WorkspacePageContainer), a top bar with breadcrumb + date range, and segmented ToggleGroup controls (new segmented toggle variant) for metric, time window, and breakdown on large screens; compact Select controls on small screens.

Provider sidebar now follows stable PROVIDER_ORDER, drops share bars, and shows per-provider session counts from extended mergeUsage (ProviderTotals.sessions from owned fingerprint distinctSessions). Headline cost copy shifts to plain API estimate (no asterisk footnote); totals strip loses per-metric detail lines.

Chart UX: hover tooltip follows the cursor with edge-aware placement (ResizeObserver); inline chart legend export is removed. Minor UI polish: breadcrumb separator className, table row hovers, simplified loading skeleton.

Reviewed by Cursor Bugbot for commit 095cd9e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Redesign usage insights page with segmented controls and per-provider session counts

  • Replaces inline button groups on the Usage page with segmented ToggleGroup controls for selecting metric (cost/tokens), period, and breakdown; adds a dynamic breadcrumb showing the current window label
  • Provider summary now shows session counts per provider using a stable PROVIDER_ORDER, removes the progress bar and legend, and simplifies the totals section
  • Chart tooltip in UsageProviderChart now follows the cursor with smart clamping and repositions on resize via ResizeObserver
  • mergeUsage now computes and exposes per-provider session counts in ProviderTotals, with new test coverage for sessions aggregation
  • UsageSkeleton and Metric are simplified to match the new layout, removing the detail prop and resolution-based skeleton variation

Macroscope summarized 095cd9e.

@coderabbitai

coderabbitai Bot commented Aug 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9fbffa22-735f-499e-a3de-7d64804fc1ec

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 12.6 KiB 15.1 KiB
Codex Thread snapshot wire 6.3 KiB 7.3 KiB
Codex Live turn WebSocket wire 6.3 KiB 7.8 KiB
Codex Live turn WebSocket decoded 51.8 KiB 66.4 KiB
Codex Live turn messages 16 21
Claude Total thread wire 12.6 KiB 15.1 KiB
Claude Thread snapshot wire 6.3 KiB 7.3 KiB
Claude Live turn WebSocket wire 6.3 KiB 7.8 KiB
Claude Live turn WebSocket decoded 52.7 KiB 66.4 KiB
Claude Live turn messages 16 21

Baseline: unavailable · PR result: 095cd9e · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 99.9 KiB
  • Claude decoded thread snapshot: 100.6 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@macroscopeapp macroscopeapp Bot 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.

Two consistency findings in the Usage refresh: a hand-rolled glass surface that bypasses the shared glass contract, and a call-site override of the shared workspace topbar height.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/usage/UsageProviderChart.tsx Outdated
Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated
Comment thread packages/shared/src/usageMerge.ts
@macroscopeapp

macroscopeapp Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a redesigned usage insights page with new UI patterns (segmented toggles, responsive controls), layout restructuring, and new per-provider session tracking. The scope represents a new feature rather than a minor tweak, and unresolved comments raise architectural consistency questions worth human evaluation.

You can customize Macroscope's approvability policy. Learn more.

@macroscopeapp macroscopeapp Bot 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.

UI Consistency

One finding: the new compact header menu duplicates an existing header control instead of routing through a shared primitive. The two items flagged on the previous run (header height override on WorkspacePageHeader, hand-rolled glass on the chart tooltip) are resolved on this head — the header now uses the primitive's geometry unchanged and the tooltip composes surface-glass.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated
@maria-rcks
maria-rcks force-pushed the ui-refresh/usage branch 2 times, most recently from 5e96339 to 2ec34dc Compare August 16, 2026 01:36
Comment thread apps/web/src/components/HeaderFilterMenu.tsx Outdated

@macroscopeapp macroscopeapp Bot 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.

One finding: the usage chart is now the only place provider colors appear, and this PR removes every static key for them.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/usage/UsagePage.tsx

@macroscopeapp macroscopeapp Bot 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.

UI consistency review: the earlier glass-surface and topbar-geometry issues are resolved (the tooltip now uses surface-glass, and the header goes through WorkspacePageHeader without overriding its height). Two remaining findings, both about new shared API surface introduced here.

Posted via Macroscope — UI Consistency

Comment on lines +32 to +34
<MenuTrigger
render={
<Button size="compact" variant="ghost-muted" aria-label={label} className="text-sm" />

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.

HeaderFilterMenu is the same control as CompactFilterMenu (apps/web/src/routes/_chat.pull-requests.tsx:1619): same Menu + MenuTrigger + MenuRadioGroup shape, same chevron glyph, and the optional icon / disabledReason props map one-to-one onto that component's option.Icon / option.unavailable while having no consumer on this page.

Leaving both means the same header filter control now exists twice with divergent trigger behavior: this one inherits the Button compact / ghost-muted contract (focus-visible ring, coarse-pointer hit target, muted icon color), while the pull-requests copy keeps a hand-rolled trigger class string with no focus ring. Consider migrating those three call sites to this component (Icon -> icon, unavailable -> disabledReason) and deleting the local copy, so the two prop generalizations have the consumer they were shaped for. If that migration is out of scope for this PR, dropping icon and disabledReason until something uses them keeps the new contract to what it actually serves.

Posted via Macroscope — UI Consistency

...props
}: {
selected: boolean;
density?: "default" | "compact";

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.

density="compact" (and the segmented-compact size it selects in toggle.tsx) has no call site — all three usages here use the default density. Adding a size to the shared toggleVariants contract that nothing renders means its geometry is never validated against a real surface. Suggest dropping density and the segmented-compact size until a second surface needs the smaller pill, and adding them back with that consumer.

Posted via Macroscope — UI Consistency

@maria-rcks
maria-rcks force-pushed the ui-refresh/usage branch 2 times, most recently from 4e4d9b3 to 8f2efda Compare August 16, 2026 01:51
Comment thread apps/web/src/components/HeaderFilterMenu.tsx Outdated

@macroscopeapp macroscopeapp Bot 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.

One consistency finding on the new SegmentedTabList primitive. Earlier findings from previous runs (glass tooltip, header geometry, provider color encoding, unused density size) look addressed; the HeaderFilterMenu / CompactFilterMenu duplication flagged previously is still open but not re-reported here.

Posted via Macroscope — UI Consistency

Comment on lines +6 to +17
function SegmentedTabList({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn(
"inline-flex w-fit items-center gap-0.5 rounded-lg bg-muted/45 p-0.5",
className,
)}
role="group"
{...props}
/>
);
}

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.

SegmentedTabList reconstructs ToggleGroup (apps/web/src/components/ui/toggle-group.tsx), the primitive the other single-select segmented controls already compose (DiffPanel.tsx:752, PullRequestCodeTab.tsx:1098). A raw role="group" div with per-item pressed/onClick drops what the Base UI group owns: value/onValueChange single-selection semantics and the composite keyboard model (one tab stop, arrow keys to move between segments — here each segment is its own tab stop and arrows do nothing), plus the group's coarse-pointer hit-target normalisation (*:pointer-coarse:after:min-w-auto), so on touch each 24px-tall pill keeps a 44px ::after overlay that spills onto its neighbour and the content below.

Since the segmented size/variant you added live on the shared toggleVariants, ToggleGroupItem can already wear them; suggest keeping the track styling (rounded-lg bg-muted/45 p-0.5) as a ToggleGroup className and letting variant === "segmented" take the gap-0.5 branch rather than the joined-border branch, instead of shipping a second group primitive next to the existing one.

Posted via Macroscope — UI Consistency

@macroscopeapp macroscopeapp Bot 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.

One consistency finding on the new SegmentedTabList primitive. Earlier findings from previous runs (glass tooltip, header geometry, provider color encoding, unused density size) look addressed; the HeaderFilterMenu / CompactFilterMenu duplication flagged previously is still open but not re-reported here.

Posted via Macroscope — UI Consistency

Comment on lines +6 to +17
function SegmentedTabList({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn(
"inline-flex w-fit items-center gap-0.5 rounded-lg bg-muted/45 p-0.5",
className,
)}
role="group"
{...props}
/>
);
}

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.

SegmentedTabList reconstructs ToggleGroup (apps/web/src/components/ui/toggle-group.tsx), the primitive the other single-select segmented controls already compose (DiffPanel.tsx:752, PullRequestCodeTab.tsx:1098). A raw role="group" div with per-item pressed/onClick drops what the Base UI group owns: value/onValueChange single-selection semantics and the composite keyboard model (one tab stop, arrow keys to move between segments — here each segment is its own tab stop and arrows do nothing), plus the group's coarse-pointer hit-target normalisation (*:pointer-coarse:after:min-w-auto), so on touch each 24px-tall pill keeps a 44px ::after overlay that spills onto its neighbour and the content below.

Since the segmented size/variant you added live on the shared toggleVariants, ToggleGroupItem can already wear them; suggest keeping the track styling (rounded-lg bg-muted/45 p-0.5) as a ToggleGroup className and letting variant === "segmented" take the gap-0.5 branch rather than the joined-border branch, instead of shipping a second group primitive next to the existing one.

Posted via Macroscope — UI Consistency

@macroscopeapp macroscopeapp Bot 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.

One finding: the new segmented variant splits ownership of the segmented control's appearance between the primitive (items) and three identical call-site strings (track). Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ui/toggle-group.tsx Outdated

@macroscopeapp macroscopeapp Bot 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.

One finding: the new segmented variant splits ownership of the segmented control's appearance between the primitive (items) and three identical call-site strings (track). Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ui/toggle-group.tsx Outdated
@maria-rcks
maria-rcks force-pushed the ui-refresh/usage branch 2 times, most recently from 9ac145f to f399a1b Compare August 16, 2026 23:06

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f399a1b. Configure here.

Comment thread apps/web/src/components/usage/UsagePage.tsx

@macroscopeapp macroscopeapp Bot 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.

One finding on the new shared segmented toggle treatment: the pressed pill and its track resolve to the same token in the default dark theme, so the selected segment loses its background signal. Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ui/toggle.tsx Outdated
Comment on lines +32 to +33
segmented:
"border-transparent text-muted-foreground shadow-none transition-colors before:shadow-none hover:bg-accent/45 hover:text-foreground data-pressed:bg-accent data-pressed:text-foreground data-pressed:shadow-xs/5",

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.

The pressed pill and the group track resolve to the same token in the default dark theme: index.css sets --muted and --accent both to --alpha(var(--color-white) / 4%) under @variant dark (:root), so the track bg-muted/45 (added in toggle-group.tsx) computes to ~1.8% white while data-pressed:bg-accent computes to 4% white — roughly #0e0e0e vs #141414 over neutral-950, and data-pressed:shadow-xs/5 adds nothing on a dark surface. hover:bg-accent/45 computes to exactly the track color, so a hovered unselected segment ends up looking like the selected one (both get text-foreground). All three new controls (UsagePage.tsx metric, period, breakdown) then communicate selection by text color alone in dark mode.

Suggest lifting the pressed/hover states with the token the primitive already uses for pressed state (bg-input/64 in the base class and dark:data-pressed:bg-input in outline), which stays above muted in both themes; data-pressed:bg-background plus a dark-only lift would work too if a white pill is wanted in light mode.

Suggested change
segmented:
"border-transparent text-muted-foreground shadow-none transition-colors before:shadow-none hover:bg-accent/45 hover:text-foreground data-pressed:bg-accent data-pressed:text-foreground data-pressed:shadow-xs/5",
segmented:
"border-transparent text-muted-foreground shadow-none transition-colors before:shadow-none hover:bg-input/32 hover:text-foreground data-pressed:bg-input/64 data-pressed:text-foreground data-pressed:shadow-xs/5",

Posted via Macroscope — UI Consistency

@maria-rcks
maria-rcks force-pushed the ui-refresh/usage branch 2 times, most recently from 672a618 to d586e6d Compare August 17, 2026 19:35

@macroscopeapp macroscopeapp Bot 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.

Two consistency findings on the new Usage header and loading state. Details inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated
Comment thread apps/web/src/components/usage/UsagePage.tsx Outdated

@macroscopeapp macroscopeapp Bot 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.

Reviewed the usage refresh against the shared component system. Two ownership/consistency findings on the new shared pieces; the earlier tooltip-glass, breadcrumb-separator and segmented-track points are addressed in this revision.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/ui/toggle-group.tsx
Comment on lines +56 to +61
/** Keeps an icon glyph on the content edge while its larger hit target extends outward. */
export function WorkspacePageHeaderEdgeControl({
className,
...props
}: ComponentPropsWithoutRef<"div">) {
return <div className={cn("-me-[7px] flex shrink-0", className)} {...props} />;

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.

This adds a shared export whose only consumer is UsagePage, and it makes the same header action align differently per page: the Pull Requests header renders the identical ghost refresh button flush inside the header padding with a size-4 glyph (_chat.pull-requests.tsx:1905-1912), and the Settings header places its trailing action flush too (settings.tsx:78-80). The offset is also tied to one call site's geometry rather than a durable contract — -me-[7px] optically centers a 28px button around a 14px glyph, which is size="icon-sm" only at sm and up (size-8 sm:size-7), so below sm the button is 32px and the glyph lands 2px inside the edge this helper exists to touch.

Suggest either dropping the wrapper and rendering the button flush like the other workspace headers, or moving the optical inset into WorkspacePageHeader itself so every workspace header's trailing icon action inherits it instead of one page opting in.

Posted via Macroscope — UI Consistency

@macroscopeapp macroscopeapp Bot 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.

One finding: the chart keeps several non-obvious rendering invariants whose rationale was dropped in this pass. Everything else in scope — the segmented variant/size coupling, the shared header and breadcrumb separator, the glass tooltip surface, the skeleton geometry, and the table row hover treatment — now lines up with the shared primitives and the prevailing repo patterns.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/usage/UsageProviderChart.tsx

@macroscopeapp macroscopeapp Bot 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.

One finding: the loading skeleton no longer matches the new provider-row geometry, so the rows shift horizontally when data lands. Everything else in the diff (segmented ToggleGroup/Toggle variant now owning both track and pill geometry, WorkspaceBreadcrumbSeparator reuse, WorkspacePageHeader/WorkspacePageContainer adoption, surface-glass tooltip, chart-skeleton gutter alignment) reads consistent with the shared component system.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/usage/UsagePage.tsx
sheehanmunim added a commit to sheehanmunim/mtcode that referenced this pull request Aug 17, 2026
Adopt Maria's workspace header, segmented controls, per-provider session
counts, and chart tooltip UX while keeping multi-provider coverage,
pricing-unavailable honesty, and Theo's rate-limits strip.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant