|
|
|
@ -4,7 +4,7 @@
|
|
|
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
import './media/chatViewSetup.css';
|
|
|
|
|
import { $, getActiveElement, setVisibility } from '../../../../base/browser/dom.js';
|
|
|
|
|
import { $, addDisposableListener, EventType, getActiveElement, setVisibility } from '../../../../base/browser/dom.js';
|
|
|
|
|
import { Button, ButtonWithDropdown } from '../../../../base/browser/ui/button/button.js';
|
|
|
|
|
import { renderIcon } from '../../../../base/browser/ui/iconLabel/iconLabels.js';
|
|
|
|
|
import { IAction, toAction } from '../../../../base/common/actions.js';
|
|
|
|
@ -37,7 +37,7 @@ import { Registry } from '../../../../platform/registry/common/platform.js';
|
|
|
|
|
import { asText, IRequestService } from '../../../../platform/request/common/request.js';
|
|
|
|
|
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
|
|
|
|
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
|
|
|
|
|
import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js';
|
|
|
|
|
import { defaultButtonStyles, defaultCheckboxStyles } from '../../../../platform/theme/browser/defaultStyles.js';
|
|
|
|
|
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
|
|
|
|
|
import { IWorkbenchContribution } from '../../../common/contributions.js';
|
|
|
|
|
import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js';
|
|
|
|
@ -55,6 +55,7 @@ import { ChatViewId, EditsViewId, ensureSideBarChatViewSize, IChatWidget, showCh
|
|
|
|
|
import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID } from './chatViewPane.js';
|
|
|
|
|
import { ChatViewsWelcomeExtensions, IChatViewsWelcomeContributionRegistry } from './viewsWelcome/chatViewsWelcome.js';
|
|
|
|
|
import { IChatQuotasService } from './chatQuotasService.js';
|
|
|
|
|
import { Checkbox } from '../../../../base/browser/ui/toggle/toggle.js';
|
|
|
|
|
|
|
|
|
|
const defaultChat = {
|
|
|
|
|
extensionId: product.defaultChatAgent?.extensionId ?? '',
|
|
|
|
@ -63,12 +64,12 @@ const defaultChat = {
|
|
|
|
|
termsStatementUrl: product.defaultChatAgent?.termsStatementUrl ?? '',
|
|
|
|
|
privacyStatementUrl: product.defaultChatAgent?.privacyStatementUrl ?? '',
|
|
|
|
|
skusDocumentationUrl: product.defaultChatAgent?.skusDocumentationUrl ?? '',
|
|
|
|
|
publicCodeMatchesUrl: product.defaultChatAgent?.publicCodeMatchesUrl ?? '',
|
|
|
|
|
providerId: product.defaultChatAgent?.providerId ?? '',
|
|
|
|
|
providerName: product.defaultChatAgent?.providerName ?? '',
|
|
|
|
|
providerScopes: product.defaultChatAgent?.providerScopes ?? [[]],
|
|
|
|
|
entitlementUrl: product.defaultChatAgent?.entitlementUrl ?? '',
|
|
|
|
|
entitlementSignupLimitedUrl: product.defaultChatAgent?.entitlementSignupLimitedUrl ?? '',
|
|
|
|
|
managePlanUrl: product.defaultChatAgent?.managePlanUrl ?? '',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum ChatEntitlement {
|
|
|
|
@ -86,6 +87,8 @@ enum ChatEntitlement {
|
|
|
|
|
Pro
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ASK_FOR_PUBLIC_CODE_MATCHES = false; // TODO@bpasero revisit this
|
|
|
|
|
|
|
|
|
|
//#region Contribution
|
|
|
|
|
|
|
|
|
|
const TRIGGER_SETUP_COMMAND_ID = 'workbench.action.chat.triggerSetup';
|
|
|
|
@ -179,7 +182,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr
|
|
|
|
|
// Setup should be kicked off immediately
|
|
|
|
|
if (typeof startSetup === 'boolean' && startSetup) {
|
|
|
|
|
const controller = that.controller.value;
|
|
|
|
|
controller.setup();
|
|
|
|
|
controller.setup({ publicCodeSuggestions: true }); // TODO@sbatten pass in as argument
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
configurationService.updateValue('chat.commandCenter.enabled', true);
|
|
|
|
@ -506,10 +509,10 @@ class ChatSetupRequests extends Disposable {
|
|
|
|
|
return this.resolveEntitlement(session, CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async signUpLimited(session: AuthenticationSession): Promise<boolean> {
|
|
|
|
|
async signUpLimited(session: AuthenticationSession, options: { publicCodeSuggestions: boolean }): Promise<boolean> {
|
|
|
|
|
const body = {
|
|
|
|
|
restricted_telemetry: 'disabled',
|
|
|
|
|
public_code_suggestions: 'enabled'
|
|
|
|
|
public_code_suggestions: options.publicCodeSuggestions ? 'enabled' : 'disabled'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const response = await this.request(defaultChat.entitlementSignupLimitedUrl, 'POST', body, session, CancellationToken.None);
|
|
|
|
@ -621,7 +624,7 @@ class ChatSetupController extends Disposable {
|
|
|
|
|
this._onDidChange.fire();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async setup(): Promise<void> {
|
|
|
|
|
async setup(options: { publicCodeSuggestions: boolean }): Promise<void> {
|
|
|
|
|
const title = localize('setupChatProgress', "Getting Copilot ready...");
|
|
|
|
|
const badge = this.activityService.showViewContainerActivity(isCopilotEditsViewActive(this.viewsService) ? CHAT_EDITING_SIDEBAR_PANEL_ID : CHAT_SIDEBAR_PANEL_ID, {
|
|
|
|
|
badge: new ProgressBadge(() => title),
|
|
|
|
@ -633,13 +636,13 @@ class ChatSetupController extends Disposable {
|
|
|
|
|
location: ProgressLocation.Window,
|
|
|
|
|
command: TRIGGER_SETUP_COMMAND_ID,
|
|
|
|
|
title,
|
|
|
|
|
}, () => this.doSetup());
|
|
|
|
|
}, () => this.doSetup(options));
|
|
|
|
|
} finally {
|
|
|
|
|
badge.dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async doSetup(): Promise<void> {
|
|
|
|
|
private async doSetup(options: { publicCodeSuggestions: boolean }): Promise<void> {
|
|
|
|
|
this.context.suspend(); // reduces flicker
|
|
|
|
|
try {
|
|
|
|
|
let session: AuthenticationSession | undefined;
|
|
|
|
@ -666,7 +669,7 @@ class ChatSetupController extends Disposable {
|
|
|
|
|
|
|
|
|
|
// Install
|
|
|
|
|
this.setStep(ChatSetupStep.Installing);
|
|
|
|
|
await this.install(session, entitlement ?? this.context.state.entitlement);
|
|
|
|
|
await this.install(session, entitlement ?? this.context.state.entitlement, options);
|
|
|
|
|
} finally {
|
|
|
|
|
this.setStep(ChatSetupStep.Initial);
|
|
|
|
|
this.context.resume();
|
|
|
|
@ -691,7 +694,7 @@ class ChatSetupController extends Disposable {
|
|
|
|
|
return { session, entitlement };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async install(session: AuthenticationSession, entitlement: ChatEntitlement): Promise<void> {
|
|
|
|
|
private async install(session: AuthenticationSession, entitlement: ChatEntitlement, options: { publicCodeSuggestions: boolean }): Promise<void> {
|
|
|
|
|
const signedIn = !!session;
|
|
|
|
|
const activeElement = getActiveElement();
|
|
|
|
|
|
|
|
|
@ -702,7 +705,7 @@ class ChatSetupController extends Disposable {
|
|
|
|
|
showCopilotView(this.viewsService);
|
|
|
|
|
|
|
|
|
|
if (entitlement !== ChatEntitlement.Limited && entitlement !== ChatEntitlement.Pro && entitlement !== ChatEntitlement.Unavailable) {
|
|
|
|
|
didSignUp = await this.requests.signUpLimited(session);
|
|
|
|
|
didSignUp = await this.requests.signUpLimited(session, options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await this.extensionsWorkbenchService.install(defaultChat.extensionId, {
|
|
|
|
@ -789,6 +792,9 @@ class ChatSetupWelcomeContent extends Disposable {
|
|
|
|
|
const limitedSkuHeaderContainer = this.element.appendChild($('p'));
|
|
|
|
|
limitedSkuHeaderContainer.appendChild(this._register(markdown.render(new MarkdownString(limitedSkuHeader, { isTrusted: true, supportThemeIcons: true }))).element);
|
|
|
|
|
|
|
|
|
|
const publicCodeSuggestionsLabel = localize('detectionLabel', "Allow code suggestions that [match public code]({0})", defaultChat.publicCodeMatchesUrl);
|
|
|
|
|
const { container: publicCodeSuggestionsContainer, checkbox: publicCodeSuggestionsCheckbox } = this.createCheckBox(publicCodeSuggestionsLabel, true, markdown);
|
|
|
|
|
|
|
|
|
|
// Terms
|
|
|
|
|
const terms = localize({ key: 'termsLabel', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to our [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl);
|
|
|
|
|
this.element.appendChild($('p')).appendChild(this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element);
|
|
|
|
@ -810,13 +816,30 @@ class ChatSetupWelcomeContent extends Disposable {
|
|
|
|
|
supportIcons: true,
|
|
|
|
|
...defaultButtonStyles
|
|
|
|
|
}));
|
|
|
|
|
this._register(button.onDidClick(() => this.controller.setup()));
|
|
|
|
|
this._register(button.onDidClick(() => this.controller.setup({ publicCodeSuggestions: ASK_FOR_PUBLIC_CODE_MATCHES ? publicCodeSuggestionsCheckbox.checked : true })));
|
|
|
|
|
|
|
|
|
|
// Update based on model state
|
|
|
|
|
this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(limitedSkuHeaderContainer, button)));
|
|
|
|
|
this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(limitedSkuHeaderContainer, button, [publicCodeSuggestionsContainer], [publicCodeSuggestionsCheckbox])));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private update(limitedSkuHeaderContainer: HTMLElement, button: Button | ButtonWithDropdown): void {
|
|
|
|
|
private createCheckBox(label: string, checked: boolean, markdown: MarkdownRenderer): { container: HTMLElement; checkbox: Checkbox } {
|
|
|
|
|
const container = this.element.appendChild($('p.checkbox-container'));
|
|
|
|
|
const checkbox = this._register(new Checkbox(label, checked, defaultCheckboxStyles));
|
|
|
|
|
container.appendChild(checkbox.domNode);
|
|
|
|
|
|
|
|
|
|
const checkboxLabel = container.appendChild(this._register(markdown.render(new MarkdownString(label, { isTrusted: true, supportThemeIcons: true }), { inline: true, className: 'checkbox-label' })).element);
|
|
|
|
|
this._register(addDisposableListener(checkboxLabel, EventType.CLICK, e => {
|
|
|
|
|
if (checkbox?.enabled && (e.target as HTMLElement).tagName !== 'A') {
|
|
|
|
|
checkbox.checked = !checkbox.checked;
|
|
|
|
|
checkbox.focus();
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
return { container, checkbox };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private update(limitedSkuHeaderContainer: HTMLElement, button: Button | ButtonWithDropdown, limitedCheckboxContainers: HTMLElement[], limitedCheckboxes: Checkbox[]): void {
|
|
|
|
|
const showLimitedCheckboxes = ASK_FOR_PUBLIC_CODE_MATCHES ? this.context.state.entitlement !== ChatEntitlement.Limited && this.context.state.entitlement !== ChatEntitlement.Pro && this.context.state.entitlement !== ChatEntitlement.Unavailable : false;
|
|
|
|
|
let showLimitedSkuHeader: boolean;
|
|
|
|
|
let buttonLabel: string;
|
|
|
|
|
|
|
|
|
@ -840,17 +863,26 @@ class ChatSetupWelcomeContent extends Disposable {
|
|
|
|
|
|
|
|
|
|
switch (this.controller.step) {
|
|
|
|
|
case ChatSetupStep.Initial:
|
|
|
|
|
// do not override
|
|
|
|
|
for (const checkbox of limitedCheckboxes) {
|
|
|
|
|
checkbox.enable();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case ChatSetupStep.SigningIn:
|
|
|
|
|
for (const checkbox of limitedCheckboxes) {
|
|
|
|
|
checkbox.disable();
|
|
|
|
|
}
|
|
|
|
|
buttonLabel = localize('setupChatSignIn', "$(loading~spin) Signing in to {0}...", defaultChat.providerName);
|
|
|
|
|
break;
|
|
|
|
|
case ChatSetupStep.Installing:
|
|
|
|
|
for (const checkbox of limitedCheckboxes) {
|
|
|
|
|
checkbox.disable();
|
|
|
|
|
}
|
|
|
|
|
buttonLabel = localize('setupChatInstalling', "$(loading~spin) Getting Copilot Ready...");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setVisibility(showLimitedSkuHeader, limitedSkuHeaderContainer);
|
|
|
|
|
setVisibility(showLimitedCheckboxes, ...limitedCheckboxContainers);
|
|
|
|
|
|
|
|
|
|
button.label = buttonLabel;
|
|
|
|
|
button.enabled = this.controller.step === ChatSetupStep.Initial;
|
|
|
|
|