diff --git a/package.json b/package.json index 0b8fd19b267..fd59fa39ab5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.96.0", - "distro": "259941ab128814d2a40a076eecd74b25d4a37a5b", + "distro": "c883c91dadf5f063b26c86ffe01851acee3747c6", "author": { "name": "Microsoft Corporation" }, diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index 6a0e09444c7..ac7c5529b27 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -310,8 +310,10 @@ export interface IDefaultChatAgent { readonly termsStatementUrl: string; readonly privacyStatementUrl: string; readonly skusDocumentationUrl: string; + readonly publicCodeMatchesUrl: string; readonly manageSettingsUrl: string; readonly managePlanUrl: string; + readonly upgradePlanUrl: string; readonly providerId: string; readonly providerName: string; readonly providerScopes: string[][]; diff --git a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts index 390b8425973..6739927fc0d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts @@ -133,7 +133,7 @@ export class ChatQuotasService extends Disposable implements IChatQuotasService buttons: [ { label: localize('managePlan', "Upgrade to Copilot Pro"), - run: () => { openerService.open(URI.parse(product.defaultChatAgent?.managePlanUrl ?? '')); } + run: () => { openerService.open(URI.parse(product.defaultChatAgent?.upgradePlanUrl ?? '')); } }, ], custom: { diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 05018fc3b30..ae7f9456840 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -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 { + async signUpLimited(session: AuthenticationSession, options: { publicCodeSuggestions: boolean }): Promise { 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 { + async setup(options: { publicCodeSuggestions: boolean }): Promise { 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 { + private async doSetup(options: { publicCodeSuggestions: boolean }): Promise { 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 { + private async install(session: AuthenticationSession, entitlement: ChatEntitlement, options: { publicCodeSuggestions: boolean }): Promise { 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; diff --git a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css index 52a1e9f7209..f4b166fead1 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css @@ -50,4 +50,19 @@ width: 100%; padding: 4px 7px; } + + /** Checkboxes */ + .checkbox-container { + display: flex; + padding-top: 20px; + } + + .checkbox-label { + flex-basis: fit-content; + cursor: pointer; + } + + .checkbox-label p { + display: inline; + } }