refactor(cli): consolidate quota reset formatting and restore conservative defaults

This commit is contained in:
Spencer
2026-02-20 22:04:27 +00:00
parent aa68a97482
commit 7abb4f5770
5 changed files with 61 additions and 43 deletions
@@ -85,13 +85,7 @@ export const QuotaStatsInfo: React.FC<QuotaStatsInfoProps> = ({
: `${displayPercentage.toFixed(0)}%`}
</Text>
{displayPercentage !== 0 && <Text> usage remaining</Text>}
{displayResetTime &&
`, ${(function (t) {
const formatted = formatResetTime(t);
return formatted === 'Resetting...' || formatted === '< 1m'
? formatted
: `resets in ${formatted}`;
})(displayResetTime)}`}
{displayResetTime && `, ${formatResetTime(displayResetTime)}`}
</Text>
)}
{showDetails && (
@@ -278,7 +278,13 @@ const ModelUsageTable: React.FC<{
</Text>
</Box>
<Box width={tokenColumnWidth} justifyContent="flex-end">
<Text color={theme.text.secondary}>{row.cachedTokens}</Text>
<Text
color={
row.isActive ? theme.text.primary : theme.text.secondary
}
>
{row.cachedTokens}
</Text>
</Box>
<Box width={tokenColumnWidth} justifyContent="flex-end">
<Text
@@ -294,19 +300,19 @@ const ModelUsageTable: React.FC<{
{showQuotaColumn && (
<Box flexGrow={1} justifyContent="flex-end">
{row.bucket && row.bucket.remainingFraction != null && (
<Text color={theme.text.secondary}>
<Text
color={
row.isActive ? theme.text.primary : theme.text.secondary
}
>
{(row.bucket.remainingFraction * 100).toFixed(1)}%
{row.bucket.resetTime && (
<Text color={theme.text.secondary}>
{' '}
(
{(function (t) {
const formatted = formatResetTime(t);
return formatted === 'Resetting...' ||
formatted === '< 1m'
? formatted
: `Resets in ${formatted}`;
})(row.bucket.resetTime)}
{formatResetTime(row.bucket.resetTime, {
capitalize: true,
})}
)
</Text>
)}
+2 -2
View File
@@ -16,8 +16,8 @@ export const USER_AGREEMENT_RATE_MEDIUM = 45;
export const CACHE_EFFICIENCY_HIGH = 40;
export const CACHE_EFFICIENCY_MEDIUM = 15;
export const QUOTA_THRESHOLD_HIGH = 50;
export const QUOTA_THRESHOLD_MEDIUM = 20;
export const QUOTA_THRESHOLD_HIGH = 20;
export const QUOTA_THRESHOLD_MEDIUM = 5;
// --- Color Logic ---
export const getStatusColor = (
+35 -13
View File
@@ -97,17 +97,24 @@ export function stripReferenceContent(text: string): string {
return text.replace(pattern, '').trim();
}
const UNIX_SECONDS_THRESHOLD = 10_000_000_000;
export const formatResetTime = (resetTime: string): string => {
export const formatResetTime = (
resetTime: string,
options: { capitalize?: boolean; includePrefix?: boolean } = {},
): string => {
const { capitalize = false, includePrefix = true } = options;
let date = new Date(resetTime);
// If invalid, try parsing as a number (unix timestamp)
if (isNaN(date.getTime())) {
const timestamp = parseInt(resetTime, 10);
if (!isNaN(timestamp)) {
// Heuristic: If the timestamp is less than 10^12, it is likely in seconds
// (as 10^12 ms is year 2001, while 10^12 s is far in the future).
date = new Date(timestamp < 10000000000 ? timestamp * 1000 : timestamp);
// Heuristic: If the timestamp is less than 10^10, it is likely in seconds
// (as 10^10 ms is year 1970, while 10^10 s is year 2286).
date = new Date(
timestamp < UNIX_SECONDS_THRESHOLD ? timestamp * 1000 : timestamp,
);
}
}
@@ -123,19 +130,34 @@ export const formatResetTime = (resetTime: string): string => {
const minutes = totalMinutes % 60;
const fmt = (val: number, unit: 'hour' | 'minute') =>
new Intl.NumberFormat('en', {
style: 'unit',
unit,
unitDisplay: 'narrow',
}).format(val);
new Intl.RelativeTimeFormat('en', {
style: 'narrow',
numeric: 'always',
})
.formatToParts(val, unit)
.filter(
(p) =>
p.type !== 'literal' || (p.value !== 'in ' && p.value !== ' ago'),
)
.map((p) => p.value)
.join('')
.trim();
let timeStr = '';
if (hours > 0 && minutes > 0) {
return `${fmt(hours, 'hour')} ${fmt(minutes, 'minute')}`;
timeStr = `${fmt(hours, 'hour')} ${fmt(minutes, 'minute')}`;
} else if (hours > 0) {
return fmt(hours, 'hour');
timeStr = fmt(hours, 'hour');
} else if (minutes > 0) {
return fmt(minutes, 'minute');
timeStr = fmt(minutes, 'minute');
} else {
return '< 1m';
timeStr = '< 1m';
}
if (timeStr === '< 1m' || !includePrefix) {
return timeStr;
}
const prefix = capitalize ? 'Resets in ' : 'resets in ';
return `${prefix}${timeStr}`;
};
+8 -12
View File
@@ -68,7 +68,10 @@ import { ideContextStore } from '../ide/ideContext.js';
import { WriteTodosTool } from '../tools/write-todos.js';
import type { FileSystemService } from '../services/fileSystemService.js';
import { StandardFileSystemService } from '../services/fileSystemService.js';
import { logRipgrepFallback, logFlashFallback } from '../telemetry/loggers.js';
import { logRipgrepFallback, logFlashFallback ,
logApprovalModeSwitch,
logApprovalModeDuration,
} from '../telemetry/loggers.js';
import {
RipgrepFallbackEvent,
FlashFallbackEvent,
@@ -103,9 +106,7 @@ import type { EventEmitter } from 'node:events';
import { PolicyEngine } from '../policy/policy-engine.js';
import { ApprovalMode, type PolicyEngineConfig } from '../policy/types.js';
import { HookSystem } from '../hooks/index.js';
import type { UserTierId } from '../code_assist/types.js';
import type { RetrieveUserQuotaResponse } from '../code_assist/types.js';
import type { AdminControlsSettings } from '../code_assist/types.js';
import type { UserTierId , RetrieveUserQuotaResponse , AdminControlsSettings } from '../code_assist/types.js';
import type { HierarchicalMemory } from './memory.js';
import { getCodeAssistServer } from '../code_assist/codeAssist.js';
import type { Experiments } from '../code_assist/experiments/experiments.js';
@@ -119,10 +120,6 @@ import { debugLogger } from '../utils/debugLogger.js';
import { SkillManager, type SkillDefinition } from '../skills/skillManager.js';
import { startupProfiler } from '../telemetry/startupProfiler.js';
import type { AgentDefinition } from '../agents/types.js';
import {
logApprovalModeSwitch,
logApprovalModeDuration,
} from '../telemetry/loggers.js';
import { fetchAdminControls } from '../code_assist/admin/admin_controls.js';
import { isSubpath } from '../utils/paths.js';
import { UserHintService } from './userHintService.js';
@@ -1335,12 +1332,11 @@ export class Config {
const flashQuota = this.modelQuotas.get(flashModel);
if (proQuota || flashQuota) {
// For reset time, take the one that is nearest in the future (soonest reset)
// For reset time, take the one that is furthest in the future (most conservative)
const resetTime = [proQuota?.resetTime, flashQuota?.resetTime]
.filter((t): t is string => !!t)
.map((t) => new Date(t))
.sort((a, b) => a.getTime() - b.getTime())[0]
?.toISOString();
.sort()
.reverse()[0];
return {
remaining: (proQuota?.remaining ?? 0) + (flashQuota?.remaining ?? 0),