@@ -19,6 +19,7 @@ import {
1919 USER_AGREEMENT_RATE_MEDIUM ,
2020} from '../utils/displayUtils.js' ;
2121import { computeSessionStats } from '../utils/computeStats.js' ;
22+ import type { RetrieveUserQuotaResponse } from '@google/gemini-cli-core' ;
2223
2324// A more flexible and powerful StatRow component
2425interface StatRowProps {
@@ -69,15 +70,41 @@ const Section: React.FC<SectionProps> = ({ title, children }) => (
6970 </ Box >
7071) ;
7172
73+ const formatResetTime = ( resetTime : string ) : string => {
74+ const diff = new Date ( resetTime ) . getTime ( ) - Date . now ( ) ;
75+ if ( diff <= 0 ) return '' ;
76+
77+ const totalMinutes = Math . ceil ( diff / ( 1000 * 60 ) ) ;
78+ const hours = Math . floor ( totalMinutes / 60 ) ;
79+ const minutes = totalMinutes % 60 ;
80+
81+ const fmt = ( val : number , unit : 'hour' | 'minute' ) =>
82+ new Intl . NumberFormat ( 'en' , {
83+ style : 'unit' ,
84+ unit,
85+ unitDisplay : 'narrow' ,
86+ } ) . format ( val ) ;
87+
88+ if ( hours > 0 && minutes > 0 ) {
89+ return `(Resets in ${ fmt ( hours , 'hour' ) } ${ fmt ( minutes , 'minute' ) } )` ;
90+ } else if ( hours > 0 ) {
91+ return `(Resets in ${ fmt ( hours , 'hour' ) } )` ;
92+ }
93+
94+ return `(Resets in ${ fmt ( minutes , 'minute' ) } )` ;
95+ } ;
96+
7297const ModelUsageTable : React . FC < {
7398 models : Record < string , ModelMetrics > ;
7499 totalCachedTokens : number ;
75100 cacheEfficiency : number ;
76- } > = ( { models, totalCachedTokens, cacheEfficiency } ) => {
101+ quotas ?: RetrieveUserQuotaResponse ;
102+ } > = ( { models, totalCachedTokens, cacheEfficiency, quotas } ) => {
77103 const nameWidth = 25 ;
78104 const requestsWidth = 8 ;
79105 const inputTokensWidth = 15 ;
80106 const outputTokensWidth = 15 ;
107+ const usageLimitWidth = quotas ? 30 : 0 ;
81108
82109 return (
83110 < Box flexDirection = "column" marginTop = { 1 } >
@@ -103,6 +130,13 @@ const ModelUsageTable: React.FC<{
103130 Output Tokens
104131 </ Text >
105132 </ Box >
133+ { quotas && (
134+ < Box width = { usageLimitWidth } justifyContent = "flex-end" >
135+ < Text bold color = { theme . text . primary } >
136+ Usage limit remaining
137+ </ Text >
138+ </ Box >
139+ ) }
106140 </ Box >
107141 { /* Divider */ }
108142 < Box
@@ -112,44 +146,73 @@ const ModelUsageTable: React.FC<{
112146 borderLeft = { false }
113147 borderRight = { false }
114148 borderColor = { theme . border . default }
115- width = { nameWidth + requestsWidth + inputTokensWidth + outputTokensWidth }
149+ width = {
150+ nameWidth +
151+ requestsWidth +
152+ inputTokensWidth +
153+ outputTokensWidth +
154+ usageLimitWidth
155+ }
116156 > </ Box >
117157
118158 { /* Rows */ }
119- { Object . entries ( models ) . map ( ( [ name , modelMetrics ] ) => (
120- < Box key = { name } >
121- < Box width = { nameWidth } >
122- < Text color = { theme . text . primary } > { name . replace ( '-001' , '' ) } </ Text >
123- </ Box >
124- < Box width = { requestsWidth } justifyContent = "flex-end" >
125- < Text color = { theme . text . primary } >
126- { modelMetrics . api . totalRequests }
127- </ Text >
128- </ Box >
129- < Box width = { inputTokensWidth } justifyContent = "flex-end" >
130- < Text color = { theme . status . warning } >
131- { modelMetrics . tokens . prompt . toLocaleString ( ) }
132- </ Text >
133- </ Box >
134- < Box width = { outputTokensWidth } justifyContent = "flex-end" >
135- < Text color = { theme . status . warning } >
136- { modelMetrics . tokens . candidates . toLocaleString ( ) }
137- </ Text >
159+ { Object . entries ( models ) . map ( ( [ name , modelMetrics ] ) => {
160+ const modelName = name . replace ( '-001' , '' ) ;
161+ const bucket = quotas ?. buckets ?. find ( ( b ) => b . modelId === modelName ) ;
162+
163+ return (
164+ < Box key = { name } >
165+ < Box width = { nameWidth } >
166+ < Text color = { theme . text . primary } > { modelName } </ Text >
167+ </ Box >
168+ < Box width = { requestsWidth } justifyContent = "flex-end" >
169+ < Text color = { theme . text . primary } >
170+ { modelMetrics . api . totalRequests }
171+ </ Text >
172+ </ Box >
173+ < Box width = { inputTokensWidth } justifyContent = "flex-end" >
174+ < Text color = { theme . status . warning } >
175+ { modelMetrics . tokens . prompt . toLocaleString ( ) }
176+ </ Text >
177+ </ Box >
178+ < Box width = { outputTokensWidth } justifyContent = "flex-end" >
179+ < Text color = { theme . status . warning } >
180+ { modelMetrics . tokens . candidates . toLocaleString ( ) }
181+ </ Text >
182+ </ Box >
183+ < Box width = { usageLimitWidth } justifyContent = "flex-end" >
184+ { bucket &&
185+ bucket . remainingFraction != null &&
186+ bucket . resetTime && (
187+ < Text color = { theme . text . secondary } >
188+ { ( bucket . remainingFraction * 100 ) . toFixed ( 1 ) } %{ ' ' }
189+ { formatResetTime ( bucket . resetTime ) }
190+ </ Text >
191+ ) }
192+ </ Box >
138193 </ Box >
139- </ Box >
140- ) ) }
194+ ) ;
195+ } ) }
141196 { cacheEfficiency > 0 && (
142197 < Box flexDirection = "column" marginTop = { 1 } >
143198 < Text color = { theme . text . primary } >
144199 < Text color = { theme . status . success } > Savings Highlight:</ Text > { ' ' }
145200 { totalCachedTokens . toLocaleString ( ) } ({ cacheEfficiency . toFixed ( 1 ) }
146201 %) of input tokens were served from the cache, reducing costs.
147202 </ Text >
148- < Box height = { 1 } />
203+ </ Box >
204+ ) }
205+ { models && (
206+ < >
207+ < Box marginTop = { 1 } marginBottom = { 2 } >
208+ < Text color = { theme . text . primary } >
209+ { `Usage limits span all sessions and reset daily.\n/auth to upgrade or switch to API key.` }
210+ </ Text >
211+ </ Box >
149212 < Text color = { theme . text . secondary } >
150213 » Tip: For a full token breakdown, run `/stats model`.
151214 </ Text >
152- </ Box >
215+ </ >
153216 ) }
154217 </ Box >
155218 ) ;
@@ -158,11 +221,13 @@ const ModelUsageTable: React.FC<{
158221interface StatsDisplayProps {
159222 duration : string ;
160223 title ?: string ;
224+ quotas ?: RetrieveUserQuotaResponse ;
161225}
162226
163227export const StatsDisplay : React . FC < StatsDisplayProps > = ( {
164228 duration,
165229 title,
230+ quotas,
166231} ) => {
167232 const { stats } = useSessionStats ( ) ;
168233 const { metrics } = stats ;
@@ -276,6 +341,7 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
276341 models = { models }
277342 totalCachedTokens = { computed . totalCachedTokens }
278343 cacheEfficiency = { computed . cacheEfficiency }
344+ quotas = { quotas }
279345 />
280346 ) }
281347 </ Box >
0 commit comments