mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-13 15:35:20 +01:00
add chat dialog (#235152)
* wip: quota limits hit * polish copy * split into 3 actions
This commit is contained in:
@@ -37,6 +37,7 @@ export interface IDialogOptions {
|
||||
readonly icon?: ThemeIcon;
|
||||
readonly buttonDetails?: string[];
|
||||
readonly disableCloseAction?: boolean;
|
||||
readonly closeOnLinkClick?: boolean;
|
||||
readonly disableDefaultAction?: boolean;
|
||||
readonly buttonStyles: IButtonStyles;
|
||||
readonly checkboxStyles: ICheckboxStyles;
|
||||
@@ -203,6 +204,28 @@ export class Dialog extends Disposable {
|
||||
return new Promise<IDialogResult>((resolve) => {
|
||||
clearNode(this.buttonsContainer);
|
||||
|
||||
const close = () => {
|
||||
resolve({
|
||||
button: this.options.cancelId || 0,
|
||||
checkboxChecked: this.checkbox ? this.checkbox.checked : undefined
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
||||
if (this.options.closeOnLinkClick) {
|
||||
for (const el of this.messageContainer.getElementsByTagName('a')) {
|
||||
this._register(addDisposableListener(el, EventType.CLICK, () => {
|
||||
setTimeout(close); // HACK to ensure the link action is triggered before the dialog is closed
|
||||
}));
|
||||
this._register(addDisposableListener(el, EventType.KEY_DOWN, (e: KeyboardEvent) => {
|
||||
const evt = new StandardKeyboardEvent(e);
|
||||
if (evt.equals(KeyCode.Enter)) {
|
||||
setTimeout(close); // HACK to ensure the link action is triggered before the dialog is closed
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const buttonBar = this.buttonBar = this._register(new ButtonBar(this.buttonsContainer));
|
||||
const buttonMap = this.rearrangeButtons(this.buttons, this.options.cancelId);
|
||||
|
||||
@@ -337,10 +360,7 @@ export class Dialog extends Disposable {
|
||||
const evt = new StandardKeyboardEvent(e);
|
||||
|
||||
if (!this.options.disableCloseAction && evt.equals(KeyCode.Escape)) {
|
||||
resolve({
|
||||
button: this.options.cancelId || 0,
|
||||
checkboxChecked: this.checkbox ? this.checkbox.checked : undefined
|
||||
});
|
||||
close();
|
||||
}
|
||||
}, true));
|
||||
|
||||
@@ -450,6 +470,8 @@ export class Dialog extends Disposable {
|
||||
|
||||
let color;
|
||||
switch (this.options.type) {
|
||||
case 'none':
|
||||
break;
|
||||
case 'error':
|
||||
color = style.errorIconForeground;
|
||||
break;
|
||||
|
||||
@@ -278,6 +278,7 @@ export interface ICustomDialogOptions {
|
||||
readonly classes?: string[];
|
||||
readonly icon?: ThemeIcon;
|
||||
readonly disableCloseAction?: boolean;
|
||||
readonly closeOnLinkClick?: boolean;
|
||||
}
|
||||
|
||||
export interface ICustomDialogMarkdown {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common
|
||||
import { MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js';
|
||||
import { defaultButtonStyles, defaultCheckboxStyles, defaultDialogStyles, defaultInputBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js';
|
||||
import { ResultKind } from '../../../../platform/keybinding/common/keybindingResolver.js';
|
||||
import { CancellationToken } from '../../../../base/common/cancellation.js';
|
||||
|
||||
export class BrowserDialogHandler extends AbstractDialogHandler {
|
||||
|
||||
@@ -105,7 +106,7 @@ export class BrowserDialogHandler extends AbstractDialogHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private async doShow(type: Severity | DialogType | undefined, message: string, buttons?: string[], detail?: string, cancelId?: number, checkbox?: ICheckbox, inputs?: IInputElement[], customOptions?: ICustomDialogOptions): Promise<IDialogResult> {
|
||||
private async doShow(type: Severity | DialogType | undefined, message: string, buttons?: string[], detail?: string, cancelId?: number, checkbox?: ICheckbox, inputs?: IInputElement[], customOptions?: ICustomDialogOptions, cancellationToken?: CancellationToken): Promise<IDialogResult> {
|
||||
const dialogDisposables = new DisposableStore();
|
||||
|
||||
const renderBody = customOptions ? (parent: HTMLElement) => {
|
||||
@@ -137,6 +138,7 @@ export class BrowserDialogHandler extends AbstractDialogHandler {
|
||||
renderBody,
|
||||
icon: customOptions?.icon,
|
||||
disableCloseAction: customOptions?.disableCloseAction,
|
||||
closeOnLinkClick: customOptions?.closeOnLinkClick,
|
||||
buttonDetails: customOptions?.buttonDetails,
|
||||
checkboxLabel: checkbox?.label,
|
||||
checkboxChecked: checkbox?.checked,
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IRequestContext } from '../../../../base/parts/request/common/request.j
|
||||
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
|
||||
import { MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js';
|
||||
import { localize, localize2 } from '../../../../nls.js';
|
||||
import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
|
||||
import { Action2, IAction2Options, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
|
||||
import { ICommandService } from '../../../../platform/commands/common/commands.js';
|
||||
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
|
||||
import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
|
||||
@@ -67,6 +67,7 @@ const defaultChat = {
|
||||
providerScopes: product.defaultChatAgent?.providerScopes ?? [[]],
|
||||
entitlementUrl: product.defaultChatAgent?.entitlementUrl ?? '',
|
||||
entitlementSignupLimitedUrl: product.defaultChatAgent?.entitlementSignupLimitedUrl ?? '',
|
||||
managePlanUrl: product.defaultChatAgent?.managePlanUrl ?? '',
|
||||
};
|
||||
|
||||
enum ChatEntitlement {
|
||||
@@ -239,8 +240,84 @@ class ChatSetupContribution extends Disposable implements IWorkbenchContribution
|
||||
}
|
||||
}
|
||||
|
||||
const outOfFreeChatResponses = localize('out of free chat responses', "You've run out of free chat responses, but free code completions are still available as part of the Copilot Free plan.");
|
||||
const outOfCompletions = localize('out of completions', "You've run out of free code completions, but free chat responses are still available as part of the Copilot Free plan.");
|
||||
const outOfLimits = localize('out of limits', "You've reached the limits of the Copilot Free plan.");
|
||||
const limitReset = localize('limit reset', "Your limits will reset on {0}.", 'January 13, 2025 at 3:35 PM');
|
||||
const upgradeToPro = localize('upgradeToPro', "Here's what you can expect when upgrading to Copilot Pro:\n- Unlimited code completions\n- Unlimited chat interactions\n- 30 day free trial");
|
||||
|
||||
abstract class AbstractShowLimitReachedDialogAction extends Action2 {
|
||||
|
||||
constructor(private readonly message: string, desc: Readonly<IAction2Options>) {
|
||||
super(desc);
|
||||
}
|
||||
|
||||
override async run(accessor: ServicesAccessor, ...args: any[]) {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
await accessor.get(IDialogService).prompt({
|
||||
type: 'none',
|
||||
message: localize('limit reached', "Copilot Free"),
|
||||
cancelButton: {
|
||||
label: localize('dismiss', "Dismiss"),
|
||||
run: () => { /* noop */ }
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
label: localize('managePlan', "Upgrade to Copilot Pro"),
|
||||
run: () => commandService.executeCommand('workbench.action.chat.managePlan')
|
||||
},
|
||||
],
|
||||
custom: {
|
||||
closeOnLinkClick: true,
|
||||
icon: Codicon.copilot,
|
||||
markdownDetails: [
|
||||
{ markdown: new MarkdownString(`${this.message} ${limitReset}`, true) },
|
||||
{ markdown: new MarkdownString(upgradeToPro, true) }
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ShowOutOfFreeChatResponsesDialogAction extends AbstractShowLimitReachedDialogAction {
|
||||
constructor() {
|
||||
super(outOfFreeChatResponses, {
|
||||
id: 'workbench.action.chat.showOutOfFreeChatResponsesDialog',
|
||||
title: localize2('showLimitReachedDialog', "Show Out of Free Chat Responses Dialog"),
|
||||
// f1: true,
|
||||
category: CHAT_CATEGORY
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ShowOutOfCompletionsDialogAction extends AbstractShowLimitReachedDialogAction {
|
||||
constructor() {
|
||||
super(outOfCompletions, {
|
||||
id: 'workbench.action.chat.showOutOfCompletions',
|
||||
title: localize2('showOutOfCompletions', "Show Out of Completions Dialog"),
|
||||
// f1: true,
|
||||
category: CHAT_CATEGORY
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ShowOutOfLimitsDialogAction extends AbstractShowLimitReachedDialogAction {
|
||||
constructor() {
|
||||
super(outOfLimits, {
|
||||
id: 'workbench.action.chat.showOutOfLimits',
|
||||
title: localize2('showOutOfLimits', "Show Out of Limits Dialog"),
|
||||
// f1: true,
|
||||
category: CHAT_CATEGORY
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
registerAction2(ChatSetupTriggerAction);
|
||||
registerAction2(ChatSetupHideAction);
|
||||
registerAction2(ShowOutOfFreeChatResponsesDialogAction);
|
||||
registerAction2(ShowOutOfCompletionsDialogAction);
|
||||
registerAction2(ShowOutOfLimitsDialogAction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user