5
5
6
6
import './media/chatStatus.css' ;
7
7
import { safeIntl } from '../../../../base/common/date.js' ;
8
- import { Disposable , DisposableStore , MutableDisposable } from '../../../../base/common/lifecycle.js' ;
8
+ import { Disposable , DisposableStore , MutableDisposable , toDisposable } from '../../../../base/common/lifecycle.js' ;
9
9
import { language , OS } from '../../../../base/common/platform.js' ;
10
10
import { localize } from '../../../../nls.js' ;
11
11
import { ContextKeyExpr , IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js' ;
@@ -17,16 +17,15 @@ import { IChatQuotasService } from '../common/chatQuotasService.js';
17
17
import { quotaToButtonMessage , OPEN_CHAT_QUOTA_EXCEEDED_DIALOG , CHAT_SETUP_ACTION_LABEL , TOGGLE_CHAT_ACTION_ID , CHAT_OPEN_ACTION_ID } from './actions/chatActions.js' ;
18
18
import { $ , addDisposableListener , append , EventHelper , EventLike , EventType } from '../../../../base/browser/dom.js' ;
19
19
import { IChatEntitlementsService } from '../common/chatEntitlementsService.js' ;
20
- import { CancellationToken } from '../../../../base/common/cancellation.js' ;
20
+ import { CancellationTokenSource } from '../../../../base/common/cancellation.js' ;
21
21
import { KeybindingLabel } from '../../../../base/browser/ui/keybindingLabel/keybindingLabel.js' ;
22
22
import { defaultCheckboxStyles , defaultKeybindingLabelStyles } from '../../../../platform/theme/browser/defaultStyles.js' ;
23
23
import { Checkbox } from '../../../../base/browser/ui/toggle/toggle.js' ;
24
24
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js' ;
25
25
import { Command } from '../../../../editor/common/languages.js' ;
26
26
import { ICommandService } from '../../../../platform/commands/common/commands.js' ;
27
27
import { Lazy } from '../../../../base/common/lazy.js' ;
28
- import { contrastBorder , registerColor , transparent } from '../../../../platform/theme/common/colorRegistry.js' ;
29
- import { ACTIVITY_BAR_BADGE_BACKGROUND } from '../../../common/theme.js' ;
28
+ import { contrastBorder , inputValidationErrorBorder , inputValidationInfoBorder , inputValidationWarningBorder , registerColor , transparent } from '../../../../platform/theme/common/colorRegistry.js' ;
30
29
import { IHoverService } from '../../../../platform/hover/browser/hover.js' ;
31
30
import { coalesce } from '../../../../base/common/arrays.js' ;
32
31
import { CTX_INLINE_CHAT_POSSIBLE } from '../../inlineChat/common/inlineChat.js' ;
@@ -38,9 +37,11 @@ import { Gesture, EventType as TouchEventType } from '../../../../base/browser/t
38
37
import { IEditorService } from '../../../services/editor/common/editorService.js' ;
39
38
import { IProductService } from '../../../../platform/product/common/productService.js' ;
40
39
40
+ //#region --- colors
41
+
41
42
const gaugeBackground = registerColor ( 'gauge.background' , {
42
- dark : ACTIVITY_BAR_BADGE_BACKGROUND ,
43
- light : ACTIVITY_BAR_BADGE_BACKGROUND ,
43
+ dark : inputValidationInfoBorder ,
44
+ light : inputValidationInfoBorder ,
44
45
hcDark : contrastBorder ,
45
46
hcLight : contrastBorder
46
47
} , localize ( 'gaugeBackground' , "Gauge background color." ) ) ;
@@ -59,6 +60,36 @@ registerColor('gauge.border', {
59
60
hcLight : contrastBorder
60
61
} , localize ( 'gaugeBorder' , "Gauge border color." ) ) ;
61
62
63
+ const gaugeWarningBackground = registerColor ( 'gauge.warningBackground' , {
64
+ dark : inputValidationWarningBorder ,
65
+ light : inputValidationWarningBorder ,
66
+ hcDark : contrastBorder ,
67
+ hcLight : contrastBorder
68
+ } , localize ( 'gaugeWarningBackground' , "Gauge warning background color." ) ) ;
69
+
70
+ registerColor ( 'gauge.warningForeground' , {
71
+ dark : transparent ( gaugeWarningBackground , 0.3 ) ,
72
+ light : transparent ( gaugeWarningBackground , 0.3 ) ,
73
+ hcDark : Color . white ,
74
+ hcLight : Color . white
75
+ } , localize ( 'gaugeWarningForeground' , "Gauge warning foreground color." ) ) ;
76
+
77
+ const gaugeErrorBackground = registerColor ( 'gauge.errorBackground' , {
78
+ dark : inputValidationErrorBorder ,
79
+ light : inputValidationErrorBorder ,
80
+ hcDark : contrastBorder ,
81
+ hcLight : contrastBorder
82
+ } , localize ( 'gaugeErrorBackground' , "Gauge error background color." ) ) ;
83
+
84
+ registerColor ( 'gauge.errorForeground' , {
85
+ dark : transparent ( gaugeErrorBackground , 0.3 ) ,
86
+ light : transparent ( gaugeErrorBackground , 0.3 ) ,
87
+ hcDark : Color . white ,
88
+ hcLight : Color . white
89
+ } , localize ( 'gaugeErrorForeground' , "Gauge error foreground color." ) ) ;
90
+
91
+ //#endregion
92
+
62
93
export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribution {
63
94
64
95
static readonly ID = 'chat.statusBarEntry' ;
@@ -170,30 +201,43 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu
170
201
const container = $ ( 'div.chat-status-bar-entry-tooltip' ) ;
171
202
172
203
// Quota Indicator
173
- const { chatTotal, chatRemaining, completionsTotal, completionsRemaining, quotaResetDate } = this . chatQuotasService . quotas ;
204
+ {
205
+ const { chatTotal, chatRemaining, completionsTotal, completionsRemaining, quotaResetDate } = this . chatQuotasService . quotas ;
174
206
175
- container . appendChild ( $ ( 'div' , undefined , localize ( 'limitTitle' , "You are using Copilot Free" ) ) ) ;
176
- container . appendChild ( $ ( 'hr' ) ) ;
207
+ container . appendChild ( $ ( 'div.header' , undefined , localize ( 'usageTitle' , "Copilot Free Usage" ) ) ) ;
177
208
178
- const chatQuotaIndicator = this . createQuotaIndicator ( container , chatTotal , chatRemaining , localize ( 'chatsLabel' , "Chats messages remaining " ) ) ;
179
- const completionsQuotaIndicator = this . createQuotaIndicator ( container , completionsTotal , completionsRemaining , localize ( 'completionsLabel' , "Code completions remaining " ) ) ;
209
+ const chatQuotaIndicator = this . createQuotaIndicator ( container , chatTotal , chatRemaining , localize ( 'chatsLabel' , "Chats messages" ) ) ;
210
+ const completionsQuotaIndicator = this . createQuotaIndicator ( container , completionsTotal , completionsRemaining , localize ( 'completionsLabel' , "Code completions" ) ) ;
180
211
181
- this . chatEntitlementsService . resolve ( CancellationToken . None ) . then ( ( ) => {
182
- const { chatTotal, chatRemaining, completionsTotal, completionsRemaining } = this . chatQuotasService . quotas ;
212
+ container . appendChild ( $ ( 'div.description' , undefined , localize ( 'limitQuota' , "Limits will reset on {0}." , this . dateFormatter . value . format ( quotaResetDate ) ) ) ) ;
183
213
184
- chatQuotaIndicator ( chatTotal , chatRemaining ) ;
185
- completionsQuotaIndicator ( completionsTotal , completionsRemaining ) ;
186
- } ) ;
214
+ const cts = new CancellationTokenSource ( ) ;
215
+ disposables . add ( toDisposable ( ( ) => cts . dispose ( true ) ) ) ;
216
+ this . chatEntitlementsService . resolve ( cts . token ) . then ( ( ) => {
217
+ if ( cts . token . isCancellationRequested ) {
218
+ return ;
219
+ }
187
220
188
- container . appendChild ( $ ( 'div.description' , undefined , localize ( 'limitQuota' , "Limits will reset on {0}." , this . dateFormatter . value . format ( quotaResetDate ) ) ) ) ;
221
+ const { chatTotal, chatRemaining, completionsTotal, completionsRemaining } = this . chatQuotasService . quotas ;
222
+
223
+ chatQuotaIndicator ( chatTotal , chatRemaining ) ;
224
+ completionsQuotaIndicator ( completionsTotal , completionsRemaining ) ;
225
+ } ) ;
226
+ }
189
227
190
228
// Settings
191
- container . appendChild ( $ ( 'hr' ) ) ;
192
- this . createSettings ( container , disposables ) ;
229
+ {
230
+ container . appendChild ( $ ( 'hr' ) ) ;
231
+ container . appendChild ( $ ( 'div.header' , undefined , localize ( 'settingsTitle' , "Settings" ) ) ) ;
232
+ this . createSettings ( container , disposables ) ;
233
+ }
193
234
194
235
// Shortcuts
195
- container . appendChild ( $ ( 'hr' ) ) ;
196
- this . createShortcuts ( container , disposables ) ;
236
+ {
237
+ container . appendChild ( $ ( 'hr' ) ) ;
238
+ container . appendChild ( $ ( 'div.header' , undefined , localize ( 'keybindingsTitle' , "Keybindings" ) ) ) ;
239
+ this . createShortcuts ( container , disposables ) ;
240
+ }
197
241
198
242
return container ;
199
243
} ;
@@ -205,17 +249,18 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu
205
249
tooltip = ( ) => {
206
250
const container = $ ( 'div.chat-status-bar-entry-tooltip' ) ;
207
251
208
- if ( this . contextKeyService . getContextKeyValue < boolean > ( ChatContextKeys . Setup . pro . key ) === true ) {
209
- container . appendChild ( $ ( 'div' , undefined , localize ( 'proTitle' , "You are using Copilot Pro" ) ) ) ;
210
- container . appendChild ( $ ( 'hr' ) ) ;
211
- }
212
-
213
252
// Settings
214
- this . createSettings ( container , disposables ) ;
253
+ {
254
+ container . appendChild ( $ ( 'div.header' , undefined , localize ( 'settingsTitle' , "Settings" ) ) ) ;
255
+ this . createSettings ( container , disposables ) ;
256
+ }
215
257
216
258
// Shortcuts
217
- container . appendChild ( $ ( 'hr' ) ) ;
218
- this . createShortcuts ( container , disposables ) ;
259
+ {
260
+ container . appendChild ( $ ( 'hr' ) ) ;
261
+ container . appendChild ( $ ( 'div.header' , undefined , localize ( 'keybindingsTitle' , "Keybindings" ) ) ) ;
262
+ this . createShortcuts ( container , disposables ) ;
263
+ }
219
264
220
265
return container ;
221
266
} ;
@@ -237,7 +282,7 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu
237
282
const quotaText = $ ( 'span' ) ;
238
283
const quotaBit = $ ( 'div.quota-bit' ) ;
239
284
240
- container . appendChild ( $ ( 'div.quota-indicator' , undefined ,
285
+ const quotaIndicator = container . appendChild ( $ ( 'div.quota-indicator' , undefined ,
241
286
$ ( 'div.quota-label' , undefined ,
242
287
$ ( 'span' , undefined , label ) ,
243
288
quotaText
@@ -248,10 +293,23 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu
248
293
) ) ;
249
294
250
295
const update = ( total : number | undefined , remaining : number | undefined ) => {
296
+ quotaIndicator . classList . remove ( 'error' ) ;
297
+ quotaIndicator . classList . remove ( 'warning' ) ;
298
+
251
299
if ( typeof total === 'number' && typeof remaining === 'number' ) {
252
- // TODO@bpasero : enable this label when we can track this better
253
- //quotaText.textContent = localize('quotaDisplay', "{0} / {1}", remaining, total);
254
- quotaBit . style . width = `${ ( remaining / total ) * 100 } %` ;
300
+ let usedPercentage = Math . round ( ( ( total - remaining ) / total ) * 100 ) ;
301
+ if ( total !== remaining && usedPercentage === 0 ) {
302
+ usedPercentage = 1 ; // indicate minimal usage as 1%
303
+ }
304
+
305
+ quotaText . textContent = localize ( 'quotaDisplay' , "{0}%" , usedPercentage ) ;
306
+ quotaBit . style . width = `${ usedPercentage } %` ;
307
+
308
+ if ( usedPercentage >= 90 ) {
309
+ quotaIndicator . classList . add ( 'error' ) ;
310
+ } else if ( usedPercentage >= 75 ) {
311
+ quotaIndicator . classList . add ( 'warning' ) ;
312
+ }
255
313
}
256
314
} ;
257
315
@@ -328,7 +386,7 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu
328
386
return settings ;
329
387
}
330
388
331
- private createSetting ( container : HTMLElement , label : string , setting : { key : string ; override : string | undefined } , disposables : DisposableStore ) : Checkbox {
389
+ private createSetting ( container : HTMLElement , label : string , setting : { key : string ; override : string | undefined } , disposables : DisposableStore ) : void {
332
390
const readSetting = ( ) => Boolean ( this . configurationService . getValue < boolean > ( setting . key , { overrideIdentifier : setting . override } ) ) ;
333
391
const writeSetting = ( checkbox : Checkbox ) => this . configurationService . updateValue ( setting . key , checkbox . checked , { overrideIdentifier : setting . override } ) ;
334
392
@@ -358,8 +416,6 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu
358
416
settingCheckbox . checked = readSetting ( ) ;
359
417
}
360
418
} ) ) ;
361
-
362
- return settingCheckbox ;
363
419
}
364
420
365
421
override dispose ( ) : void {
0 commit comments