mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
fix(status-bar): sync usage meters with display mode (#8582)
* fix(status-bar): sync usage meters with display mode * docs(status-bar): correct barColor comment now that fill follows display mode --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
This commit is contained in:
co-authored by
Brennan Benson
parent
e7f785f0ac
commit
0690be5656
@@ -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 (
|
||||
<div className="w-[48px] h-[6px] rounded-full bg-muted overflow-hidden flex-shrink-0">
|
||||
<div
|
||||
className="h-full rounded-full transition-all duration-300 bg-muted-foreground/40"
|
||||
style={{ width: `${clampUsedPercent(usedPct)}%` }}
|
||||
style={{ width: `${getDisplayedUsagePercentage(usedPct, display)}%` }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@@ -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) => (
|
||||
<div key={window.key} className="flex min-w-0 items-center gap-1">
|
||||
<div className="h-[4px] min-w-0 flex-1 overflow-hidden rounded-full bg-muted">
|
||||
{/* Why: fill follows the selected percentage; color still signals consumption urgency. */}
|
||||
<div
|
||||
className={`h-full rounded-full ${barColor(window.used)}`}
|
||||
style={{ width: `${window.used}%` }}
|
||||
style={{ width: `${getDisplayedUsagePercentage(window.used, display)}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="shrink-0 text-[10px] tabular-nums text-muted-foreground">
|
||||
@@ -1231,7 +1237,9 @@ export function ProviderSegment({
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<ProviderIcon provider={provider} />
|
||||
{p.session && !compact && <MiniBar usedPct={clampUsedPercent(p.session.usedPercent)} />}
|
||||
{p.session && !compact && (
|
||||
<MiniBar usedPct={clampUsedPercent(p.session.usedPercent)} display={display} />
|
||||
)}
|
||||
{visibleWindows.map((window, index) => (
|
||||
<React.Fragment key={window.key}>
|
||||
{index > 0 && <span className="text-muted-foreground">·</span>}
|
||||
|
||||
@@ -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%')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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 (
|
||||
<div className="space-y-1">
|
||||
<div className={`font-medium ${textClass}`}>{label}</div>
|
||||
<div className={`h-[6px] w-full overflow-hidden rounded-full ${emptyBarClass}`}>
|
||||
{/* Why: fill follows the selected percentage; color still signals consumption urgency. */}
|
||||
<div
|
||||
className={`h-full rounded-full ${barColor(usedPct)} transition-all duration-300`}
|
||||
style={{ width: `${usedPct}%` }}
|
||||
style={{ width: `${displayedPct}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className={`flex justify-between ${mutedClass}`}>
|
||||
|
||||
Reference in New Issue
Block a user