ui: address PR comments and rename 'usage-limit' to 'quota'

This commit is contained in:
Keith Guerin
2026-02-28 23:55:24 -08:00
parent ea965bc149
commit 94272f3f24
7 changed files with 47 additions and 55 deletions
+3 -3
View File
@@ -19,7 +19,7 @@ describe('deriveItemsFromLegacySettings', () => {
'git-branch', 'git-branch',
'sandbox-status', 'sandbox-status',
'model-name', 'model-name',
'usage-limit', 'quota',
]); ]);
}); });
@@ -39,14 +39,14 @@ describe('deriveItemsFromLegacySettings', () => {
expect(items).not.toContain('sandbox-status'); expect(items).not.toContain('sandbox-status');
}); });
it('removes model-name, context-remaining, and usage-limit when hideModelInfo is true', () => { it('removes model-name, context-remaining, and quota when hideModelInfo is true', () => {
const settings = createMockSettings({ const settings = createMockSettings({
ui: { footer: { hideModelInfo: true, hideContextPercentage: true } }, ui: { footer: { hideModelInfo: true, hideContextPercentage: true } },
}).merged; }).merged;
const items = deriveItemsFromLegacySettings(settings); const items = deriveItemsFromLegacySettings(settings);
expect(items).not.toContain('model-name'); expect(items).not.toContain('model-name');
expect(items).not.toContain('context-remaining'); expect(items).not.toContain('context-remaining');
expect(items).not.toContain('usage-limit'); expect(items).not.toContain('quota');
}); });
it('includes context-remaining when hideContextPercentage is false', () => { it('includes context-remaining when hideContextPercentage is false', () => {
+4 -4
View File
@@ -33,7 +33,7 @@ export const ALL_ITEMS = [
description: 'Percentage of context window remaining', description: 'Percentage of context window remaining',
}, },
{ {
id: 'usage-limit', id: 'quota',
header: '/stats', header: '/stats',
description: 'Remaining usage on daily limit (not shown when unavailable)', description: 'Remaining usage on daily limit (not shown when unavailable)',
}, },
@@ -67,7 +67,7 @@ export const DEFAULT_ORDER = [
'sandbox-status', 'sandbox-status',
'model-name', 'model-name',
'context-remaining', 'context-remaining',
'usage-limit', 'quota',
'memory-usage', 'memory-usage',
'session-id', 'session-id',
'code-changes', 'code-changes',
@@ -82,7 +82,7 @@ export function deriveItemsFromLegacySettings(
'git-branch', 'git-branch',
'sandbox-status', 'sandbox-status',
'model-name', 'model-name',
'usage-limit', 'quota',
]; ];
const items = [...defaults]; const items = [...defaults];
@@ -96,7 +96,7 @@ export function deriveItemsFromLegacySettings(
if (settings.ui.footer.hideModelInfo) { if (settings.ui.footer.hideModelInfo) {
remove(items, 'model-name'); remove(items, 'model-name');
remove(items, 'context-remaining'); remove(items, 'context-remaining');
remove(items, 'usage-limit'); remove(items, 'quota');
} }
if ( if (
!settings.ui.footer.hideContextPercentage && !settings.ui.footer.hideContextPercentage &&
+20 -27
View File
@@ -17,10 +17,7 @@ import { ConsoleSummaryDisplay } from './ConsoleSummaryDisplay.js';
import process from 'node:process'; import process from 'node:process';
import { MemoryUsageDisplay } from './MemoryUsageDisplay.js'; import { MemoryUsageDisplay } from './MemoryUsageDisplay.js';
import { ContextUsageDisplay } from './ContextUsageDisplay.js'; import { ContextUsageDisplay } from './ContextUsageDisplay.js';
import { import { QuotaDisplay } from './QuotaDisplay.js';
QUOTA_THRESHOLD_HIGH,
QUOTA_THRESHOLD_MEDIUM,
} from '../utils/displayUtils.js';
import { DebugProfiler } from './DebugProfiler.js'; import { DebugProfiler } from './DebugProfiler.js';
import { useUIState } from '../contexts/UIStateContext.js'; import { useUIState } from '../contexts/UIStateContext.js';
import { useConfig } from '../contexts/ConfigContext.js'; import { useConfig } from '../contexts/ConfigContext.js';
@@ -312,24 +309,22 @@ export const Footer: React.FC = () => {
); );
break; break;
} }
case 'usage-limit': { case 'quota': {
if (quotaStats?.remaining !== undefined && quotaStats.limit) { if (quotaStats?.remaining !== undefined && quotaStats.limit) {
const percentage = (quotaStats.remaining / quotaStats.limit) * 100;
let color = itemColor;
if (percentage < QUOTA_THRESHOLD_MEDIUM) {
color = theme.status.error;
} else if (percentage < QUOTA_THRESHOLD_HIGH) {
color = theme.status.warning;
}
const text =
quotaStats.remaining === 0
? 'limit reached'
: `daily ${percentage.toFixed(0)}%`;
addCol( addCol(
id, id,
header, header,
() => <Text color={color}>{text}</Text>, () => (
text.length, <QuotaDisplay
remaining={quotaStats.remaining}
limit={quotaStats.limit}
resetTime={quotaStats.resetTime}
terse={true}
forceShow={true}
lowercase={true}
/>
),
10, // "daily 100%" is 10 chars, but terse is "100%" (4 chars)
); );
} }
break; break;
@@ -375,18 +370,16 @@ export const Footer: React.FC = () => {
for (const m of Object.values(uiState.sessionStats.metrics.models)) for (const m of Object.values(uiState.sessionStats.metrics.models))
total += m.tokens.total; total += m.tokens.total;
if (total > 0) { if (total > 0) {
const formatted = const formatter = new Intl.NumberFormat('en-US', {
new Intl.NumberFormat('en-US', { notation: 'compact',
notation: 'compact', maximumFractionDigits: 1,
maximumFractionDigits: 1, });
}) const formatted = formatter.format(total).toLowerCase();
.format(total)
.toLowerCase() + ' tokens';
addCol( addCol(
id, id,
header, header,
() => <Text color={itemColor}>{formatted}</Text>, () => <Text color={itemColor}>{formatted} tokens</Text>,
formatted.length, formatted.length + 7,
); );
} }
break; break;
@@ -285,9 +285,7 @@ export const FooterConfigDialog: React.FC<FooterConfigDialogProps> = ({
'context-remaining': ( 'context-remaining': (
<Text color={getColor('context-remaining', itemColor)}>85% left</Text> <Text color={getColor('context-remaining', itemColor)}>85% left</Text>
), ),
'usage-limit': ( quota: <Text color={getColor('quota', itemColor)}>97%</Text>,
<Text color={getColor('usage-limit', itemColor)}>daily 97%</Text>
),
'memory-usage': ( 'memory-usage': (
<Text color={getColor('memory-usage', itemColor)}>260 MB</Text> <Text color={getColor('memory-usage', itemColor)}>260 MB</Text>
), ),
+16 -15
View File
@@ -18,6 +18,8 @@ interface QuotaDisplayProps {
limit: number | undefined; limit: number | undefined;
resetTime?: string; resetTime?: string;
terse?: boolean; terse?: boolean;
forceShow?: boolean;
lowercase?: boolean;
} }
export const QuotaDisplay: React.FC<QuotaDisplayProps> = ({ export const QuotaDisplay: React.FC<QuotaDisplayProps> = ({
@@ -25,6 +27,8 @@ export const QuotaDisplay: React.FC<QuotaDisplayProps> = ({
limit, limit,
resetTime, resetTime,
terse = false, terse = false,
forceShow = false,
lowercase = false,
}) => { }) => {
if (remaining === undefined || limit === undefined || limit === 0) { if (remaining === undefined || limit === undefined || limit === 0) {
return null; return null;
@@ -32,7 +36,7 @@ export const QuotaDisplay: React.FC<QuotaDisplayProps> = ({
const percentage = (remaining / limit) * 100; const percentage = (remaining / limit) * 100;
if (percentage > QUOTA_THRESHOLD_HIGH) { if (!forceShow && percentage > QUOTA_THRESHOLD_HIGH) {
return null; return null;
} }
@@ -45,20 +49,17 @@ export const QuotaDisplay: React.FC<QuotaDisplayProps> = ({
!terse && resetTime ? `, ${formatResetTime(resetTime)}` : ''; !terse && resetTime ? `, ${formatResetTime(resetTime)}` : '';
if (remaining === 0) { if (remaining === 0) {
return ( let text = terse
<Text color={color}> ? 'Limit reached'
{terse : `/stats Limit reached${resetInfo}${!terse && '. /auth to continue.'}`;
? 'Limit reached' if (lowercase) text = text.toLowerCase();
: `/stats Limit reached${resetInfo}${!terse && '. /auth to continue.'}`} return <Text color={color}>{text}</Text>;
</Text>
);
} }
return ( let text = terse
<Text color={color}> ? `${percentage.toFixed(0)}%`
{terse : `/stats ${percentage.toFixed(0)}% usage remaining${resetInfo}`;
? `${percentage.toFixed(0)}%` if (lowercase) text = text.toLowerCase();
: `/stats ${percentage.toFixed(0)}% usage remaining${resetInfo}`}
</Text> return <Text color={color}>{text}</Text>;
);
}; };
@@ -8,7 +8,7 @@ exports[`<Footer /> > displays "Limit reached" message when remaining is 0 1`] =
exports[`<Footer /> > displays the usage indicator when usage is low 1`] = ` exports[`<Footer /> > displays the usage indicator when usage is low 1`] = `
" workspace (/directory) sandbox /model /stats " workspace (/directory) sandbox /model /stats
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro daily 15% ~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 15%
" "
`; `;
@@ -40,6 +40,6 @@ exports[`<Footer /> > footer configuration filtering (golden snapshots) > render
exports[`<Footer /> > hides the usage indicator when usage is not near limit 1`] = ` exports[`<Footer /> > hides the usage indicator when usage is not near limit 1`] = `
" workspace (/directory) sandbox /model /stats " workspace (/directory) sandbox /model /stats
~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro daily 85% ~/project/foo/bar/and/some/more/directories/to/make/it/long no sandbox gemini-pro 85%
" "
`; `;
@@ -26,7 +26,7 @@ exports[`<FooterConfigDialog /> > renders correctly with default settings 1`] =
│ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │ │ ┌────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ Preview: │ │ │ │ Preview: │ │
│ │ workspace (/directory) branch sandbox /model /stats │ │ │ │ workspace (/directory) branch sandbox /model /stats │ │
│ │ ~/project/path main docker gemini-2.5-pro daily 97% │ │ │ │ ~/project/path main docker gemini-2.5-pro 97% │ │
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │ │ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
│ │ │ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯