Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreadman committed Dec 7, 2024
2 parents 0b40348 + 2e52d7f commit 8b5b1b6
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 105 deletions.
4 changes: 1 addition & 3 deletions src/vs/workbench/contrib/chat/browser/actions/chatActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,8 @@ MenuRegistry.appendMenuItem(MenuId.CommandCenter, {
when: ContextKeyExpr.and(
ContextKeyExpr.has('config.chat.commandCenter.enabled'),
ContextKeyExpr.or(
ChatContextKeys.Setup.installed,
ChatContextKeys.Setup.entitled,
ContextKeyExpr.has('config.chat.experimental.offerSetup'),
ChatContextKeys.Setup.installed,
ChatContextKeys.panelParticipantRegistered
)
),
Expand All @@ -553,7 +552,6 @@ registerAction2(class ToggleCopilotControl extends ToggleTitleBarConfigAction {
ContextKeyExpr.has('config.window.commandCenter'),
ContextKeyExpr.or(
ChatContextKeys.Setup.installed,
ChatContextKeys.Setup.entitled,
ContextKeyExpr.has('config.chat.experimental.offerSetup'),
ChatContextKeys.panelParticipantRegistered
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class ChatEditingService extends Disposable implements IChatEditingServic
void this._editingSessionFileLimitPromise;

const sessionIdToRestore = storageService.get(STORAGE_KEY_EDITING_SESSION, StorageScope.WORKSPACE);
if (isString(sessionIdToRestore)) {
if (isString(sessionIdToRestore) && this._chatService.getOrRestoreSession(sessionIdToRestore)) {
this._restoringEditingSession = this.startOrContinueEditingSession(sessionIdToRestore);
this._restoringEditingSession.finally(() => {
this._restoringEditingSession = undefined;
Expand Down
99 changes: 30 additions & 69 deletions src/vs/workbench/contrib/chat/browser/chatSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import './media/chatViewSetup.css';
import { $, addDisposableListener, EventType, getActiveElement, setVisibility } from '../../../../base/browser/dom.js';
import { $, 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';
Expand Down Expand Up @@ -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, defaultCheckboxStyles } from '../../../../platform/theme/browser/defaultStyles.js';
import { defaultButtonStyles } 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';
Expand All @@ -55,7 +55,6 @@ 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';
import { mainWindow } from '../../../../base/browser/window.js';

const defaultChat = {
Expand All @@ -65,7 +64,6 @@ 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 ?? [[]],
Expand All @@ -88,8 +86,6 @@ 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';
Expand Down Expand Up @@ -158,10 +154,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr
f1: true,
precondition: ContextKeyExpr.and(
ChatContextKeys.Setup.installed.negate(),
ContextKeyExpr.or(
ChatContextKeys.Setup.entitled,
ContextKeyExpr.has('config.chat.experimental.offerSetup')
)
ContextKeyExpr.has('config.chat.experimental.offerSetup')
),
menu: {
id: MenuId.ChatCommandCenter,
Expand All @@ -183,9 +176,9 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr
showCopilotView(viewsService, layoutService);
ensureSideBarChatViewSize(400, viewDescriptorService, layoutService);

if (startSetup === true && !ASK_FOR_PUBLIC_CODE_MATCHES) {
if (startSetup === true) {
const controller = that.controller.value;
controller.setup({ publicCodeSuggestions: true });
controller.setup();
}

configurationService.updateValue('chat.commandCenter.enabled', true);
Expand All @@ -205,10 +198,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr
category: CHAT_CATEGORY,
precondition: ContextKeyExpr.and(
ChatContextKeys.Setup.installed.negate(),
ContextKeyExpr.or(
ChatContextKeys.Setup.entitled,
ContextKeyExpr.has('config.chat.experimental.offerSetup')
)
ContextKeyExpr.has('config.chat.experimental.offerSetup')
),
menu: {
id: MenuId.ChatCommandCenter,
Expand Down Expand Up @@ -521,10 +511,10 @@ class ChatSetupRequests extends Disposable {
return this.resolveEntitlement(session, CancellationToken.None);
}

async signUpLimited(session: AuthenticationSession, options: { publicCodeSuggestions: boolean }): Promise<boolean> {
async signUpLimited(session: AuthenticationSession): Promise<boolean> {
const body = {
restricted_telemetry: 'disabled',
public_code_suggestions: options.publicCodeSuggestions ? 'enabled' : 'disabled'
public_code_suggestions: 'enabled'
};

const response = await this.request(defaultChat.entitlementSignupLimitedUrl, 'POST', body, session, CancellationToken.None);
Expand Down Expand Up @@ -637,7 +627,7 @@ class ChatSetupController extends Disposable {
this._onDidChange.fire();
}

async setup(options: { publicCodeSuggestions: boolean }): Promise<void> {
async setup(): 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),
Expand All @@ -649,14 +639,16 @@ class ChatSetupController extends Disposable {
location: ProgressLocation.Window,
command: TRIGGER_SETUP_COMMAND_ID,
title,
}, () => this.doSetup(options));
}, () => this.doSetup());
} finally {
badge.dispose();
}
}

private async doSetup(options: { publicCodeSuggestions: boolean }): Promise<void> {
private async doSetup(): Promise<void> {
this.context.suspend(); // reduces flicker

let focusChatInput = false;
try {
let session: AuthenticationSession | undefined;
let entitlement: ChatEntitlement | undefined;
Expand All @@ -680,20 +672,30 @@ class ChatSetupController extends Disposable {
}
}

const activeElement = getActiveElement();

// Install
this.setStep(ChatSetupStep.Installing);
await this.install(session, entitlement ?? this.context.state.entitlement, options);
await this.install(session, entitlement ?? this.context.state.entitlement);

const currentActiveElement = getActiveElement();
focusChatInput = activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body;
} finally {
this.setStep(ChatSetupStep.Initial);
this.context.resume();
}

if (focusChatInput) {
(await showCopilotView(this.viewsService, this.layoutService))?.focusInput();
}
}

private async signIn(): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> {
let session: AuthenticationSession | undefined;
let entitlement: ChatEntitlement | undefined;
try {
showCopilotView(this.viewsService, this.layoutService);

session = await this.authenticationService.createSession(defaultChat.providerId, defaultChat.providerScopes[0]);
entitlement = await this.requests.forceResolveEntitlement(session);
} catch (error) {
Expand All @@ -707,9 +709,8 @@ class ChatSetupController extends Disposable {
return { session, entitlement };
}

private async install(session: AuthenticationSession, entitlement: ChatEntitlement, options: { publicCodeSuggestions: boolean }): Promise<void> {
private async install(session: AuthenticationSession, entitlement: ChatEntitlement,): Promise<void> {
const signedIn = !!session;
const activeElement = getActiveElement();

let installResult: 'installed' | 'cancelled' | 'failedInstall' | undefined = undefined;
const wasInstalled = this.context.state.installed;
Expand All @@ -718,7 +719,7 @@ class ChatSetupController extends Disposable {
showCopilotView(this.viewsService, this.layoutService);

if (entitlement !== ChatEntitlement.Limited && entitlement !== ChatEntitlement.Pro && entitlement !== ChatEntitlement.Unavailable) {
didSignUp = await this.requests.signUpLimited(session, options);
didSignUp = await this.requests.signUpLimited(session);
}

await this.extensionsWorkbenchService.install(defaultChat.extensionId, {
Expand Down Expand Up @@ -747,11 +748,6 @@ class ChatSetupController extends Disposable {
}

this.telemetryService.publicLog2<InstallChatEvent, InstallChatClassification>('commandCenter.chatInstall', { installResult, signedIn });

const currentActiveElement = getActiveElement();
if (activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body) {
(await showCopilotView(this.viewsService, this.layoutService))?.focusInput();
}
}
}

Expand Down Expand Up @@ -802,18 +798,14 @@ class ChatSetupWelcomeContent extends Disposable {
}

// Limited SKU
const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl);
const limitedSkuHeader = localize({ key: 'limitedSkuHeader', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}) with 50 chat messages and 2000 code completions per month.", defaultChat.skusDocumentationUrl);
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);
const termsContainer = this.element.appendChild($('p'));
termsContainer.classList.add('terms-container');
termsContainer.classList.toggle('is-standalone', !ASK_FOR_PUBLIC_CODE_MATCHES);
termsContainer.appendChild(this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element);

// Setup Button
Expand All @@ -833,30 +825,13 @@ class ChatSetupWelcomeContent extends Disposable {
supportIcons: true,
...defaultButtonStyles
}));
this._register(button.onDidClick(() => this.controller.setup({ publicCodeSuggestions: ASK_FOR_PUBLIC_CODE_MATCHES ? publicCodeSuggestionsCheckbox.checked : true })));
this._register(button.onDidClick(() => this.controller.setup()));

// Update based on model state
this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(limitedSkuHeaderContainer, button, [publicCodeSuggestionsContainer], [publicCodeSuggestionsCheckbox])));
this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(limitedSkuHeaderContainer, button)));
}

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;
private update(limitedSkuHeaderContainer: HTMLElement, button: Button | ButtonWithDropdown): void {
let showLimitedSkuHeader: boolean;
let buttonLabel: string;

Expand All @@ -882,27 +857,15 @@ class ChatSetupWelcomeContent extends Disposable {
}

switch (this.controller.step) {
case ChatSetupStep.Initial:
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;
Expand All @@ -926,7 +889,6 @@ class ChatSetupContext extends Disposable {

private readonly canSignUpContextKey = ChatContextKeys.Setup.canSignUp.bindTo(this.contextKeyService);
private readonly signedOutContextKey = ChatContextKeys.Setup.signedOut.bindTo(this.contextKeyService);
private readonly entitledContextKey = ChatContextKeys.Setup.entitled.bindTo(this.contextKeyService);
private readonly limitedContextKey = ChatContextKeys.Setup.limited.bindTo(this.contextKeyService);
private readonly triggeredContext = ChatContextKeys.Setup.triggered.bindTo(this.contextKeyService);
private readonly installedContext = ChatContextKeys.Setup.installed.bindTo(this.contextKeyService);
Expand Down Expand Up @@ -1031,7 +993,6 @@ class ChatSetupContext extends Disposable {
changed = this.updateContextKey(this.signedOutContextKey, this._state.entitlement === ChatEntitlement.Unknown) || changed;
changed = this.updateContextKey(this.canSignUpContextKey, this._state.entitlement === ChatEntitlement.Available) || changed;
changed = this.updateContextKey(this.limitedContextKey, this._state.entitlement === ChatEntitlement.Limited) || changed;
changed = this.updateContextKey(this.entitledContextKey, this._state.entitlement === ChatEntitlement.Pro) || changed;
changed = this.updateContextKey(this.triggeredContext, !!this._state.triggered) || changed;
changed = this.updateContextKey(this.installedContext, !!this._state.installed) || changed;

Expand Down
19 changes: 2 additions & 17 deletions src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
background-color: var(--vscode-chat-requestBackground);
}

.terms-container.is-standalone {
.terms-container {
padding-top: 5px;
}

Expand All @@ -29,7 +29,7 @@
}

.chat-feature-container .codicon[class*='codicon-'] {
font-size: 20px;
font-size: 16px;
}

.codicon[class*='codicon-'] {
Expand All @@ -54,19 +54,4 @@
width: 100%;
padding: 4px 7px;
}

/** Checkboxes */
.checkbox-container {
display: flex;
padding-top: 15px;
}

.checkbox-label {
flex-basis: fit-content;
cursor: pointer;
}

.checkbox-label p {
display: inline;
}
}
1 change: 0 additions & 1 deletion src/vs/workbench/contrib/chat/common/chatContextKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export namespace ChatContextKeys {
canSignUp: new RawContextKey<boolean>('chatSetupCanSignUp', false, true), // True when user can sign up to be a chat limited user.

signedOut: new RawContextKey<boolean>('chatSetupSignedOut', false, true), // True when user is signed out.
entitled: new RawContextKey<boolean>('chatSetupEntitled', false, true), // True when user is a chat entitled user.
limited: new RawContextKey<boolean>('chatSetupLimited', false, true), // True when user is a chat limited user.

triggered: new RawContextKey<boolean>('chatSetupTriggered', false, true), // True when chat setup is triggered.
Expand Down
Loading

0 comments on commit 8b5b1b6

Please sign in to comment.