diff --git a/src/renderer/src/components/status-bar/StatusBar.tsx b/src/renderer/src/components/status-bar/StatusBar.tsx
index 2c9a2fb979c..b3fc9cb54f3 100644
--- a/src/renderer/src/components/status-bar/StatusBar.tsx
+++ b/src/renderer/src/components/status-bar/StatusBar.tsx
@@ -81,6 +81,7 @@ import {
} from '@/runtime/runtime-provider-accounts-client'
import { translate } from '@/i18n/i18n'
import {
+ getDisplayedUsagePercentage,
normalizeUsagePercentageDisplay,
type UsagePercentageDisplay
} from '../../../../shared/usage-percentage-display'
@@ -944,15 +945,21 @@ function ClaudeSwitcherMenu({
}
// ---------------------------------------------------------------------------
-// Mini progress bar (shows consumption / % used, grey)
+// Mini progress bar (follows the selected usage percentage meaning, grey)
// ---------------------------------------------------------------------------
-function MiniBar({ usedPct }: { usedPct: number }): React.JSX.Element {
+function MiniBar({
+ usedPct,
+ display
+}: {
+ usedPct: number
+ display: UsagePercentageDisplay
+}): React.JSX.Element {
return (
)
@@ -972,8 +979,6 @@ export function InlineUsageBars({
const display = normalizeUsagePercentageDisplay(
useAppStore((state) => state.usagePercentageDisplay)
)
- // Why: the preference changes copy, while bar fill stays consumption-based
- // so empty/green and full/red keep the meter semantics introduced in #8167.
const usageWindows = [
limits.session
? {
@@ -1008,9 +1013,10 @@ export function InlineUsageBars({
{usageWindows.map((window) => (
+ {/* Why: fill follows the selected percentage; color still signals consumption urgency. */}
@@ -1231,7 +1237,9 @@ export function ProviderSegment({
return (
- {p.session && !compact && }
+ {p.session && !compact && (
+
+ )}
{visibleWindows.map((window, index) => (
{index > 0 && ·}
diff --git a/src/renderer/src/components/status-bar/inline-usage-bars.test.tsx b/src/renderer/src/components/status-bar/inline-usage-bars.test.tsx
index 404caa344dc..00d27391e62 100644
--- a/src/renderer/src/components/status-bar/inline-usage-bars.test.tsx
+++ b/src/renderer/src/components/status-bar/inline-usage-bars.test.tsx
@@ -71,7 +71,7 @@ describe('InlineUsageBars', () => {
expect(markup).toContain('42% used Fable')
})
- it('shows remaining copy without reversing consumption meter fill', async () => {
+ it('shows remaining copy and remaining meter fill', async () => {
mocks.usagePercentageDisplay = 'remaining'
const { InlineUsageBars } = await import('./StatusBar')
@@ -82,6 +82,9 @@ describe('InlineUsageBars', () => {
expect(markup).toContain('68% left 5h')
expect(markup).toContain('84% left wk')
expect(markup).toContain('58% left Fable')
- expect(markup).toContain('width:32%')
+ expect(markup).toContain('width:68%')
+ expect(markup).toContain('width:84%')
+ expect(markup).toContain('width:58%')
+ expect(markup).not.toContain('width:32%')
})
})
diff --git a/src/renderer/src/components/status-bar/tooltip.test.ts b/src/renderer/src/components/status-bar/tooltip.test.ts
index 6daf47a8c1c..87e12d66048 100644
--- a/src/renderer/src/components/status-bar/tooltip.test.ts
+++ b/src/renderer/src/components/status-bar/tooltip.test.ts
@@ -514,26 +514,24 @@ describe('ProviderPanel reset rendering', () => {
expect(markup).not.toContain('140%')
})
- it.each(PROVIDER_IDS)(
- 'applies remaining copy to %s while retaining consumption bar direction',
- (providerId) => {
- const p = provider({
- provider: providerId,
- status: 'ok',
- session: {
- usedPercent: 25,
- windowMinutes: 300,
- resetsAt: null,
- resetDescription: null
- }
- })
+ it.each(PROVIDER_IDS)('applies remaining copy and meter fill to %s', (providerId) => {
+ const p = provider({
+ provider: providerId,
+ status: 'ok',
+ session: {
+ usedPercent: 25,
+ windowMinutes: 300,
+ resetsAt: null,
+ resetDescription: null
+ }
+ })
- const markup = renderToStaticMarkup(ProviderPanel({ p, usagePercentageDisplay: 'remaining' }))
+ const markup = renderToStaticMarkup(ProviderPanel({ p, usagePercentageDisplay: 'remaining' }))
- expect(markup).toContain('75% left')
- expect(markup).toContain('width:25%')
- }
- )
+ expect(markup).toContain('75% left')
+ expect(markup).toContain('width:75%')
+ expect(markup).not.toContain('width:25%')
+ })
})
describe('clampUsedPercent', () => {
diff --git a/src/renderer/src/components/status-bar/tooltip.tsx b/src/renderer/src/components/status-bar/tooltip.tsx
index 849dc7beda3..2a45500ddb8 100644
--- a/src/renderer/src/components/status-bar/tooltip.tsx
+++ b/src/renderer/src/components/status-bar/tooltip.tsx
@@ -13,6 +13,7 @@ import {
} from './usage-error-copy'
import {
clampUsedPercent,
+ getDisplayedUsagePercentage,
type UsagePercentageDisplay
} from '../../../../shared/usage-percentage-display'
import { formatUsagePercentageLabel } from './usage-percentage-label'
@@ -184,8 +185,8 @@ export function getWindowSections(
// `text-background` for primary text and `text-background/50` for secondary
// to stay readable inside the inverted tooltip container.
-// Why: color-coded by consumption so users can quickly gauge urgency.
-// Matches common harness usage meters (Claude/Codex): bars fill with % used.
+// Why: color always tracks % used so urgency reads correctly even when the meter
+// fills with % remaining (#8560) — low remaining still turns red, not green.
// Green = comfortable (<60% used), yellow = caution (60-80%), red = critical (≥80%).
export function barColor(usedPct: number): string {
if (usedPct < 60) {
@@ -278,18 +279,18 @@ export function ProviderPanel({
if (!w) {
return null
}
- // Why: preference changes the copy only; consumption-based bar direction
- // preserves the empty/green to full/red meter convention from #8167.
const usedPct = clampUsedPercent(w.usedPercent)
+ const displayedPct = getDisplayedUsagePercentage(usedPct, usagePercentageDisplay)
const resetLabel = w.resetsAt ? formatResetCountdown(w.resetsAt - Date.now()) : null
return (
{label}
+ {/* Why: fill follows the selected percentage; color still signals consumption urgency. */}