Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as dom from '../../../../../base/browser/dom.js';
7
import { Button } from '../../../../../base/browser/ui/button/button.js';
8
import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../../../base/common/actions.js';
9
import { Codicon } from '../../../../../base/common/codicons.js';
10
import { Emitter } from '../../../../../base/common/event.js';
11
import { MarkdownString } from '../../../../../base/common/htmlContent.js';
12
import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js';
13
import { ThemeIcon } from '../../../../../base/common/themables.js';
14
import { assertType } from '../../../../../base/common/types.js';
15
import { MarkdownRenderer } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js';
16
import { localize } from '../../../../../nls.js';
17
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
18
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
19
import { defaultButtonStyles } from '../../../../../platform/theme/browser/defaultStyles.js';
20
import { asCssVariable, textLinkForeground } from '../../../../../platform/theme/common/colorRegistry.js';
21
import { ChatEntitlement, IChatEntitlementService } from '../../common/chatEntitlementService.js';
22
import { IChatErrorDetailsPart, IChatRendererContent, IChatResponseViewModel } from '../../common/chatViewModel.js';
23
import { IChatWidgetService } from '../chat.js';
24
import { IChatContentPart } from './chatContentParts.js';
25
26
const $ = dom.$;
27
28
/**
29
* Once the sign up button is clicked, and the retry button has been shown, it should be shown every time.
30
*/
31
let shouldShowRetryButton = false;
32
33
/**
34
* Once the 'retry' button is clicked, the wait warning should be shown every time.
35
*/
36
let shouldShowWaitWarning = false;
37
38
export class ChatQuotaExceededPart extends Disposable implements IChatContentPart {
39
public readonly domNode: HTMLElement;
40
41
private readonly _onDidChangeHeight = this._register(new Emitter<void>());
42
public readonly onDidChangeHeight = this._onDidChangeHeight.event;
43
44
constructor(
45
element: IChatResponseViewModel,
46
private readonly content: IChatErrorDetailsPart,
47
renderer: MarkdownRenderer,
48
@IChatWidgetService chatWidgetService: IChatWidgetService,
49
@ICommandService commandService: ICommandService,
50
@ITelemetryService telemetryService: ITelemetryService,
51
@IChatEntitlementService chatEntitlementService: IChatEntitlementService
52
) {
53
super();
54
55
const errorDetails = element.errorDetails;
56
assertType(!!errorDetails, 'errorDetails');
57
58
this.domNode = $('.chat-quota-error-widget');
59
const icon = dom.append(this.domNode, $('span'));
60
icon.classList.add(...ThemeIcon.asClassNameArray(Codicon.warning));
61
62
const messageContainer = dom.append(this.domNode, $('.chat-quota-error-message'));
63
const markdownContent = renderer.render(new MarkdownString(errorDetails.message));
64
dom.append(messageContainer, markdownContent.element);
65
66
let button1Label = '';
67
switch (chatEntitlementService.entitlement) {
68
case ChatEntitlement.Pro:
69
case ChatEntitlement.ProPlus:
70
button1Label = localize('enableAdditionalUsage', "Manage paid premium requests");
71
break;
72
case ChatEntitlement.Free:
73
button1Label = localize('upgradeToCopilotPro', "Upgrade to GitHub Copilot Pro");
74
break;
75
default:
76
button1Label = '';
77
}
78
79
let hasAddedWaitWarning = false;
80
const addWaitWarningIfNeeded = () => {
81
if (!shouldShowWaitWarning || hasAddedWaitWarning) {
82
return;
83
}
84
85
hasAddedWaitWarning = true;
86
dom.append(messageContainer, $('.chat-quota-wait-warning', undefined, localize('waitWarning', "Changes may take a few minutes to take effect.")));
87
};
88
89
let hasAddedRetryButton = false;
90
const addRetryButtonIfNeeded = () => {
91
if (!shouldShowRetryButton || hasAddedRetryButton) {
92
return;
93
}
94
95
hasAddedRetryButton = true;
96
const button2 = this._register(new Button(messageContainer, {
97
buttonBackground: undefined,
98
buttonForeground: asCssVariable(textLinkForeground)
99
}));
100
button2.element.classList.add('chat-quota-error-secondary-button');
101
button2.label = localize('clickToContinue', "Click to retry.");
102
this._onDidChangeHeight.fire();
103
this._register(button2.onDidClick(() => {
104
const widget = chatWidgetService.getWidgetBySessionId(element.sessionId);
105
if (!widget) {
106
return;
107
}
108
109
widget.rerunLastRequest();
110
111
shouldShowWaitWarning = true;
112
addWaitWarningIfNeeded();
113
}));
114
};
115
116
if (button1Label) {
117
const button1 = this._register(new Button(messageContainer, { ...defaultButtonStyles, supportIcons: true }));
118
button1.label = button1Label;
119
button1.element.classList.add('chat-quota-error-button');
120
this._register(button1.onDidClick(async () => {
121
const commandId = chatEntitlementService.entitlement === ChatEntitlement.Free ? 'workbench.action.chat.upgradePlan' : 'workbench.action.chat.manageOverages';
122
telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: commandId, from: 'chat-response' });
123
await commandService.executeCommand(commandId);
124
125
shouldShowRetryButton = true;
126
addRetryButtonIfNeeded();
127
}));
128
}
129
130
addRetryButtonIfNeeded();
131
addWaitWarningIfNeeded();
132
}
133
134
hasSameContent(other: IChatRendererContent): boolean {
135
return other.kind === this.content.kind && !!other.errorDetails.isQuotaExceeded;
136
}
137
138
addDisposable(disposable: IDisposable): void {
139
this._register(disposable);
140
}
141
}
142
143