Skip to content

Commit d0ad23a

Browse files
authored
chat - alternate dialog tweaks and settings (microsoft#251974)
1 parent bdc1dd9 commit d0ad23a

File tree

5 files changed

+85
-274
lines changed

5 files changed

+85
-274
lines changed

src/vs/workbench/contrib/chat/browser/chat.contribution.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ import { ChatRelatedFilesContribution } from './contrib/chatInputRelatedFilesCon
106106
import { LanguageModelToolsService } from './languageModelToolsService.js';
107107
import { ChatViewsWelcomeHandler } from './viewsWelcome/chatViewsWelcomeHandler.js';
108108
import { registerAction2 } from '../../../../platform/actions/common/actions.js';
109-
import product from '../../../../platform/product/common/product.js';
110109
import { ChatModeService, IChatModeService } from '../common/chatModes.js';
111110
import { ChatResponseResourceFileSystemProvider } from '../common/chatResponseResourceFileSystemProvider.js';
112111
import { runSaveToPromptAction, SAVE_TO_PROMPT_SLASH_COMMAND_NAME } from './promptSyntax/saveToPromptAction.js';
@@ -468,16 +467,17 @@ configurationRegistry.registerConfiguration({
468467
],
469468
},
470469
'chat.setup.signInWithAlternateProvider': { // TODO@bpasero remove me eventually
471-
type: 'boolean',
470+
type: 'string',
471+
enum: ['off', 'monochrome', 'colorful', 'first'],
472472
description: nls.localize('chat.signInWithAlternateProvider', "Enable alternative sign-in provider."),
473-
default: false,
473+
default: 'off',
474474
tags: ['onExp', 'experimental'],
475475
},
476476
'chat.setup.signInDialogVariant': { // TODO@bpasero remove me eventually
477477
type: 'string',
478-
enum: ['default', 'modern', 'brand-gh', 'brand-vsc', 'style-glow', 'alt-first', 'input-email', 'account-create'],
478+
enum: ['default', 'brand-gh', 'brand-vsc', 'style-glow', 'account-create'],
479479
description: nls.localize('chat.signInDialogVariant', "Control variations of the sign-in dialog."),
480-
default: product.quality !== 'stable' ? 'modern' : 'default',
480+
default: 'default',
481481
tags: ['onExp', 'experimental']
482482
},
483483
'chat.setup.continueLaterIndicator': { // TODO@bpasero remove me eventually

src/vs/workbench/contrib/chat/browser/chatSetup.ts

Lines changed: 67 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
import './media/chatSetup.css';
77
import { $ } from '../../../../base/browser/dom.js';
8-
import { Dialog, DialogContentsAlignment, IDialogInputOptions } from '../../../../base/browser/ui/dialog/dialog.js';
9-
import { toAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../../base/common/actions.js';
8+
import { Dialog, DialogContentsAlignment } from '../../../../base/browser/ui/dialog/dialog.js';
9+
import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../../base/common/actions.js';
1010
import { timeout } from '../../../../base/common/async.js';
1111
import { CancellationToken } from '../../../../base/common/cancellation.js';
1212
import { Codicon } from '../../../../base/common/codicons.js';
@@ -70,7 +70,6 @@ import { CHAT_SIDEBAR_PANEL_ID } from './chatViewPane.js';
7070
import { coalesce } from '../../../../base/common/arrays.js';
7171
import { ThemeIcon } from '../../../../base/common/themables.js';
7272
import { IButton } from '../../../../base/browser/ui/button/button.js';
73-
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
7473
import { ChatMode2 } from '../common/chatModes.js';
7574
import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';
7675

@@ -593,7 +592,7 @@ class ChatSetup {
593592
let instance = ChatSetup.instance;
594593
if (!instance) {
595594
instance = ChatSetup.instance = instantiationService.invokeFunction(accessor => {
596-
return new ChatSetup(context, controller, instantiationService, accessor.get(ITelemetryService), accessor.get(IWorkbenchLayoutService), accessor.get(IKeybindingService), accessor.get(IChatEntitlementService) as ChatEntitlementService, accessor.get(ILogService), accessor.get(IConfigurationService), accessor.get(IViewsService), accessor.get(IProductService), accessor.get(IOpenerService), accessor.get(IContextMenuService));
595+
return new ChatSetup(context, controller, instantiationService, accessor.get(ITelemetryService), accessor.get(IWorkbenchLayoutService), accessor.get(IKeybindingService), accessor.get(IChatEntitlementService) as ChatEntitlementService, accessor.get(ILogService), accessor.get(IConfigurationService), accessor.get(IViewsService), accessor.get(IProductService), accessor.get(IOpenerService));
597596
});
598597
}
599598

@@ -617,7 +616,6 @@ class ChatSetup {
617616
@IViewsService private readonly viewsService: IViewsService,
618617
@IProductService private readonly productService: IProductService,
619618
@IOpenerService private readonly openerService: IOpenerService,
620-
@IContextMenuService private readonly contextMenuService: IContextMenuService,
621619
) { }
622620

623621
skipDialog(): void {
@@ -693,13 +691,9 @@ class ChatSetup {
693691
}
694692

695693
private async showDialog(): Promise<ChatSetupStrategy> {
696-
let dialogVariant = this.configurationService.getValue<'default' | 'modern' | 'brand-gh' | 'brand-vsc' | 'style-glow' | 'alt-first' | 'input-email' | 'account-create' | unknown>('chat.setup.signInDialogVariant');
697-
if (this.context.state.entitlement !== ChatEntitlement.Unknown && (dialogVariant === 'input-email' || dialogVariant === 'account-create')) {
698-
dialogVariant = this.productService.quality !== 'stable' ? 'modern' : 'default'; // fallback to modern/default for users that are signed in already
699-
}
700-
701-
if (dialogVariant === 'default') {
702-
return this.showLegacyDialog();
694+
let dialogVariant = this.configurationService.getValue<'default' | 'brand-gh' | 'brand-vsc' | 'style-glow' | 'account-create' | unknown>('chat.setup.signInDialogVariant');
695+
if (this.context.state.entitlement !== ChatEntitlement.Unknown && dialogVariant === 'account-create') {
696+
dialogVariant = 'default'; // fallback to modern/default for users that are signed in already
703697
}
704698

705699
const disposables = new DisposableStore();
@@ -727,14 +721,12 @@ class ChatSetup {
727721
type: 'none',
728722
extraClasses: coalesce([
729723
'chat-setup-dialog',
730-
dialogVariant === 'style-glow' ? 'chat-setup-glow' : undefined,
731-
dialogVariant === 'input-email' ? 'chat-setup-input-email' : undefined
724+
dialogVariant === 'style-glow' ? 'chat-setup-glow' : undefined
732725
]),
733726
detail: ' ', // workaround allowing us to render the message in large
734727
icon,
735728
alignment: DialogContentsAlignment.Vertical,
736729
cancelId: buttons.length - 1,
737-
inputs: this.getInputs(dialogVariant),
738730
disableCloseButton: true,
739731
renderFooter: this.telemetryService.telemetryLevel !== TelemetryLevel.NONE ? footer => footer.appendChild(this.createDialogFooter(disposables)) : undefined,
740732
buttonOptions: buttons.map(button => button[2])
@@ -747,106 +739,76 @@ class ChatSetup {
747739
return buttons[button]?.[1] ?? ChatSetupStrategy.Canceled;
748740
}
749741

750-
private getInputs(variant: 'input-email' | unknown): IDialogInputOptions[] | undefined {
751-
if (variant !== 'input-email') {
752-
return undefined;
753-
}
754-
755-
return [{ placeholder: localize('emailOrUserIdPlaceholder', "Enter your email or {0} username", defaultChat.providerName) }];
756-
}
757-
758-
private getButtons(variant: 'modern' | 'alt-first' | 'input-email' | 'account-create' | unknown): Array<[string, ChatSetupStrategy, { styleButton?: (button: IButton) => void } | undefined]> {
742+
private getButtons(variant: unknown): Array<[string, ChatSetupStrategy, { styleButton?: (button: IButton) => void } | undefined]> {
759743
let buttons: Array<[string, ChatSetupStrategy, { styleButton?: (button: IButton) => void } | undefined]>;
760744

761745
if (this.context.state.entitlement === ChatEntitlement.Unknown) {
762-
const supportAlternateProvider = this.configurationService.getValue('chat.setup.signInWithAlternateProvider') === true && defaultChat.alternativeProviderId;
746+
let alternateProvider: 'off' | 'monochrome' | 'colorful' | 'first' = 'off';
747+
const alternateProviderSetting: unknown = this.configurationService.getValue('chat.setup.signInWithAlternateProvider');
748+
if (alternateProviderSetting === true) {
749+
alternateProvider = 'colorful';
750+
} else if (alternateProviderSetting === 'monochrome' || alternateProviderSetting === 'colorful' || alternateProviderSetting === 'first') {
751+
alternateProvider = alternateProviderSetting;
752+
}
763753

764-
switch (variant) {
765-
case 'input-email':
766-
buttons = coalesce([
767-
[localize('continueWithEmailOrUserId', "Continue"), ChatSetupStrategy.SetupWithoutEnterpriseProvider, undefined],
768-
[localize('createAccount', "Create a New Account"), ChatSetupStrategy.SetupWithAccountCreate, {
769-
styleButton: button => {
770-
button.element.classList.add('link-button');
754+
const enableAlternateProvider = alternateProvider !== 'off' && defaultChat.alternativeProviderId;
771755

772-
const separator = button.element.parentElement?.appendChild($('.buttons-separator'));
773-
separator?.appendChild($('.buttons-separator-left'));
774-
separator?.appendChild($('.buttons-separator-center', undefined, localize('or', "Or")));
775-
separator?.appendChild($('.buttons-separator-right'));
776-
}
777-
}],
778-
supportAlternateProvider ? [localize('continueWith', "Continue with {0}", defaultChat.alternativeProviderName), ChatSetupStrategy.SetupWithAlternateProvider, {
779-
styleButton: button => {
780-
button.element.classList.add('continue-button', 'alternate');
756+
if (ChatEntitlementRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId) {
757+
buttons = coalesce([
758+
[localize('continueWith', "Continue with {0}", defaultChat.enterpriseProviderName), ChatSetupStrategy.SetupWithEnterpriseProvider, {
759+
styleButton: button => {
760+
button.element.classList.add('continue-button', 'default');
761+
}
762+
}],
763+
enableAlternateProvider ? [localize('continueWith', "Continue with {0}", defaultChat.alternativeProviderName), ChatSetupStrategy.SetupWithAlternateProvider, {
764+
styleButton: button => {
765+
button.element.classList.add('continue-button', 'alternate', alternateProvider);
766+
}
767+
}] : undefined,
768+
[variant !== 'account-create' ? localize('signInWithProvider', "Sign in with a {0} account", defaultChat.providerName) : localize('continueWithProvider', "Continue with {0}", defaultChat.providerName), ChatSetupStrategy.SetupWithoutEnterpriseProvider, {
769+
styleButton: button => {
770+
if (variant !== 'account-create') {
771+
button.element.classList.add('link-button');
772+
} else {
773+
button.element.classList.add('continue-button', 'default');
781774
}
782-
}] : undefined,
783-
[localize('continueWith', "Continue with {0}", defaultChat.enterpriseProviderName), ChatSetupStrategy.SetupWithEnterpriseProvider, {
784-
styleButton: button => {
775+
}
776+
}]
777+
]);
778+
} else {
779+
buttons = coalesce([
780+
[localize('continueWith', "Continue with {0}", defaultChat.providerName), ChatSetupStrategy.SetupWithoutEnterpriseProvider, {
781+
styleButton: button => {
782+
button.element.classList.add('continue-button', 'default');
783+
}
784+
}],
785+
enableAlternateProvider ? [localize('continueWith', "Continue with {0}", defaultChat.alternativeProviderName), ChatSetupStrategy.SetupWithAlternateProvider, {
786+
styleButton: button => {
787+
button.element.classList.add('continue-button', 'alternate', alternateProvider);
788+
}
789+
}] : undefined,
790+
[variant !== 'account-create' ? localize('signInWithProvider', "Sign in with a {0} account", defaultChat.enterpriseProviderName) : localize('continueWithProvider', "Continue with {0}", defaultChat.enterpriseProviderName), ChatSetupStrategy.SetupWithEnterpriseProvider, {
791+
styleButton: button => {
792+
if (variant !== 'account-create') {
793+
button.element.classList.add('link-button');
794+
} else {
785795
button.element.classList.add('continue-button', 'default');
786796
}
787-
}]
788-
]);
789-
break;
790-
default:
791-
if (ChatEntitlementRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId) {
792-
buttons = coalesce([
793-
[localize('continueWith', "Continue with {0}", defaultChat.enterpriseProviderName), ChatSetupStrategy.SetupWithEnterpriseProvider, {
794-
styleButton: button => {
795-
button.element.classList.add('continue-button', 'default');
796-
}
797-
}],
798-
supportAlternateProvider ? [localize('continueWith', "Continue with {0}", defaultChat.alternativeProviderName), ChatSetupStrategy.SetupWithAlternateProvider, {
799-
styleButton: button => {
800-
button.element.classList.add('continue-button', 'alternate');
801-
}
802-
}] : undefined,
803-
[variant !== 'account-create' ? localize('signInWithProvider', "Sign in with a {0} account", defaultChat.providerName) : localize('continueWithProvider', "Continue with {0}", defaultChat.providerName), ChatSetupStrategy.SetupWithoutEnterpriseProvider, {
804-
styleButton: button => {
805-
if (variant !== 'account-create') {
806-
button.element.classList.add('link-button');
807-
} else {
808-
button.element.classList.add('continue-button', 'default');
809-
}
810-
}
811-
}]
812-
]);
813-
} else {
814-
buttons = coalesce([
815-
[localize('continueWith', "Continue with {0}", defaultChat.providerName), ChatSetupStrategy.SetupWithoutEnterpriseProvider, {
816-
styleButton: button => {
817-
button.element.classList.add('continue-button', 'default');
818-
}
819-
}],
820-
supportAlternateProvider ? [localize('continueWith', "Continue with {0}", defaultChat.alternativeProviderName), ChatSetupStrategy.SetupWithAlternateProvider, {
821-
styleButton: button => {
822-
button.element.classList.add('continue-button', 'alternate');
823-
}
824-
}] : undefined,
825-
[variant !== 'account-create' ? localize('signInWithProvider', "Sign in with a {0} account", defaultChat.enterpriseProviderName) : localize('continueWithProvider', "Continue with {0}", defaultChat.enterpriseProviderName), ChatSetupStrategy.SetupWithEnterpriseProvider, {
826-
styleButton: button => {
827-
if (variant !== 'account-create') {
828-
button.element.classList.add('link-button');
829-
} else {
830-
button.element.classList.add('continue-button', 'default');
831-
}
832-
}
833-
}]
834-
]);
835-
}
797+
}
798+
}]
799+
]);
800+
}
836801

837-
if (supportAlternateProvider && variant === 'alt-first') {
838-
[buttons[0], buttons[1]] = [buttons[1], buttons[0]];
839-
}
802+
if (enableAlternateProvider && alternateProvider === 'first') {
803+
[buttons[0], buttons[1]] = [buttons[1], buttons[0]];
804+
}
840805

841-
if (variant === 'account-create') {
842-
buttons.push([localize('createAccount', "Create a New Account"), ChatSetupStrategy.SetupWithAccountCreate, {
843-
styleButton: button => {
844-
button.element.classList.add('link-button');
845-
}
846-
}]);
806+
if (variant === 'account-create') {
807+
buttons.push([localize('createAccount', "Create a New Account"), ChatSetupStrategy.SetupWithAccountCreate, {
808+
styleButton: button => {
809+
button.element.classList.add('link-button');
847810
}
848-
849-
break;
811+
}]);
850812
}
851813
} else {
852814
buttons = [[localize('setupCopilotButton', "Set up Copilot"), ChatSetupStrategy.DefaultSetup, undefined]];
@@ -857,7 +819,7 @@ class ChatSetup {
857819
return buttons;
858820
}
859821

860-
private getDialogTitle(variant: 'default' | 'brand-gh' | 'brand-vsc' | 'style-glow' | 'alt-first' | unknown): string {
822+
private getDialogTitle(variant: unknown): string {
861823
if (this.context.state.entitlement === ChatEntitlement.Unknown) {
862824
switch (variant) {
863825
case 'brand-gh':
@@ -890,81 +852,6 @@ class ChatSetup {
890852

891853
return element;
892854
}
893-
894-
private async showLegacyDialog(): Promise<ChatSetupStrategy> {
895-
const disposables = new DisposableStore();
896-
897-
let result: ChatSetupStrategy | undefined = undefined;
898-
899-
const buttons = [this.getLegacyPrimaryButton(), localize('maybeLater', "Maybe Later")];
900-
901-
const dialog = disposables.add(new Dialog(
902-
this.layoutService.activeContainer,
903-
this.getLegacyDialogTitle(),
904-
buttons,
905-
createWorkbenchDialogOptions({
906-
type: 'none',
907-
icon: Codicon.copilotLarge,
908-
cancelId: buttons.length - 1,
909-
renderBody: body => body.appendChild(this.createLegacyDialog(disposables)),
910-
primaryButtonDropdown: {
911-
contextMenuProvider: this.contextMenuService,
912-
addPrimaryActionToDropdown: false,
913-
actions: [
914-
toAction({ id: 'setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => result = ChatSetupStrategy.SetupWithoutEnterpriseProvider }),
915-
toAction({ id: 'setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => result = ChatSetupStrategy.SetupWithEnterpriseProvider }),
916-
]
917-
}
918-
}, this.keybindingService, this.layoutService)
919-
));
920-
921-
const { button } = await dialog.show();
922-
disposables.dispose();
923-
924-
return button === 0 ? result ?? ChatSetupStrategy.DefaultSetup : ChatSetupStrategy.Canceled;
925-
}
926-
927-
private getLegacyPrimaryButton(): string {
928-
if (this.context.state.entitlement === ChatEntitlement.Unknown) {
929-
if (ChatEntitlementRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId) {
930-
return localize('setupWithProviderShort', "Sign in with {0}", defaultChat.enterpriseProviderName);
931-
}
932-
933-
return localize('signInButton', "Sign in");
934-
}
935-
936-
return localize('useCopilotButton', "Use Copilot");
937-
}
938-
939-
private getLegacyDialogTitle(): string {
940-
if (this.context.state.entitlement === ChatEntitlement.Unknown) {
941-
return this.context.state.registered ? localize('signUp', "Sign in to use Copilot") : localize('signUpFree', "Sign in to use Copilot for free");
942-
}
943-
944-
if (isProUser(this.context.state.entitlement)) {
945-
return localize('copilotProTitle', "Start using Copilot Pro");
946-
}
947-
948-
return this.context.state.registered ? localize('copilotTitle', "Start using Copilot") : localize('copilotFreeTitle', "Start using Copilot for free");
949-
}
950-
951-
private createLegacyDialog(disposables: DisposableStore): HTMLElement {
952-
const element = $('.chat-setup-dialog-legacy');
953-
954-
const markdown = this.instantiationService.createInstance(MarkdownRenderer, {});
955-
956-
// Header
957-
const header = localize({ key: 'headerDialog', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer. Write code faster with completions, fix bugs and build new features across multiple files, and learn about your codebase through chat.", defaultChat.documentationUrl);
958-
element.appendChild($('p.setup-header', undefined, disposables.add(markdown.render(new MarkdownString(header, { isTrusted: true }))).element));
959-
960-
// SKU Settings
961-
if (this.telemetryService.telemetryLevel !== TelemetryLevel.NONE) {
962-
const settings = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "{0} Copilot Free, Pro and Pro+ may show [public code]({1}) suggestions and we may use your data for product improvement. You can change these [settings]({2}) at any time.", defaultChat.providerName, defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl);
963-
element.appendChild($('p.setup-settings', undefined, disposables.add(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element));
964-
}
965-
966-
return element;
967-
}
968855
}
969856

970857
export class ChatSetupContribution extends Disposable implements IWorkbenchContribution {

0 commit comments

Comments
 (0)