diff --git a/src/vs/editor/common/services/textResourceConfigurationService.ts b/src/vs/editor/common/services/textResourceConfigurationService.ts index 04420d7f5e6..85ef41882c4 100644 --- a/src/vs/editor/common/services/textResourceConfigurationService.ts +++ b/src/vs/editor/common/services/textResourceConfigurationService.ts @@ -110,7 +110,7 @@ export class TextResourceConfigurationService extends Disposable implements ITex return true; } if (overrideIdentifier) { - //TODO@bpasero workaround for https://github.com/microsoft/vscode/issues/240410 + //TODO@sandy081 workaround for https://github.com/microsoft/vscode/issues/240410 return configurationChangeEvent.affectedKeys.has(`[${overrideIdentifier}]`); } return false; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 501318fcc3d..1ce8c2e12d0 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -752,31 +752,28 @@ export class CopilotTitleBarMenuRendering extends Disposable implements IWorkben }); const chatExtensionInstalled = chatEntitlementService.sentiment === ChatSentiment.Installed; - const chatHidden = chatEntitlementService.sentiment === ChatSentiment.Disabled; const { chatQuotaExceeded, completionsQuotaExceeded } = chatEntitlementService.quotas; const signedOut = chatEntitlementService.entitlement === ChatEntitlement.Unknown; - const setupFromDialog = configurationService.getValue('chat.setupFromDialog'); let primaryActionId = TOGGLE_CHAT_ACTION_ID; let primaryActionTitle = localize('toggleChat', "Toggle Chat"); let primaryActionIcon = Codicon.copilot; - if (!chatExtensionInstalled && (!setupFromDialog || chatHidden)) { - primaryActionId = CHAT_SETUP_ACTION_ID; - primaryActionTitle = localize('triggerChatSetup', "Use AI Features with Copilot for free..."); - } else if (chatExtensionInstalled && signedOut) { - primaryActionId = setupFromDialog ? CHAT_SETUP_ACTION_ID : TOGGLE_CHAT_ACTION_ID; - primaryActionTitle = localize('signInToChatSetup', "Sign in to use Copilot..."); - primaryActionIcon = Codicon.copilotNotConnected; - } else if (chatExtensionInstalled && (chatQuotaExceeded || completionsQuotaExceeded)) { - primaryActionId = OPEN_CHAT_QUOTA_EXCEEDED_DIALOG; - if (chatQuotaExceeded && !completionsQuotaExceeded) { - primaryActionTitle = localize('chatQuotaExceededButton', "Monthly chat messages limit reached. Click for details."); - } else if (completionsQuotaExceeded && !chatQuotaExceeded) { - primaryActionTitle = localize('completionsQuotaExceededButton', "Monthly code completions limit reached. Click for details."); - } else { - primaryActionTitle = localize('chatAndCompletionsQuotaExceededButton', "Copilot Free plan limit reached. Click for details."); + if (chatExtensionInstalled) { + if (signedOut) { + primaryActionId = CHAT_SETUP_ACTION_ID; + primaryActionTitle = localize('signInToChatSetup', "Sign in to use Copilot..."); + primaryActionIcon = Codicon.copilotNotConnected; + } else if (chatQuotaExceeded || completionsQuotaExceeded) { + primaryActionId = OPEN_CHAT_QUOTA_EXCEEDED_DIALOG; + if (chatQuotaExceeded && !completionsQuotaExceeded) { + primaryActionTitle = localize('chatQuotaExceededButton', "Monthly chat messages limit reached. Click for details."); + } else if (completionsQuotaExceeded && !chatQuotaExceeded) { + primaryActionTitle = localize('completionsQuotaExceededButton', "Monthly code completions limit reached. Click for details."); + } else { + primaryActionTitle = localize('chatAndCompletionsQuotaExceededButton', "Copilot Free plan limit reached. Click for details."); + } + primaryActionIcon = Codicon.copilotWarning; } - primaryActionIcon = Codicon.copilotWarning; } return instantiationService.createInstance(DropdownWithPrimaryActionViewItem, instantiationService.createInstance(MenuItemAction, { id: primaryActionId, @@ -786,8 +783,7 @@ export class CopilotTitleBarMenuRendering extends Disposable implements IWorkben }, Event.any( chatEntitlementService.onDidChangeSentiment, chatEntitlementService.onDidChangeQuotaExceeded, - chatEntitlementService.onDidChangeEntitlement, - Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('chat.setupFromDialog')) + chatEntitlementService.onDidChangeEntitlement )); // Reduces flicker a bit on reload/restart diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts b/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts index b7a0a6613f8..54311675fff 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts @@ -11,9 +11,8 @@ import { ExtensionIdentifier } from '../../../../../platform/extensions/common/e import { IExtensionManagementService, InstallOperation } from '../../../../../platform/extensionManagement/common/extensionManagement.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; import { IDefaultChatAgent } from '../../../../../base/common/product.js'; -import { IViewDescriptorService } from '../../../../common/views.js'; import { IWorkbenchLayoutService } from '../../../../services/layout/browser/layoutService.js'; -import { ensureSideBarChatViewSize, showCopilotView } from '../chat.js'; +import { showCopilotView } from '../chat.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; import { IStatusbarService } from '../../../../services/statusbar/browser/statusbar.js'; @@ -30,7 +29,6 @@ export class ChatGettingStartedContribution extends Disposable implements IWorkb @IViewsService private readonly viewsService: IViewsService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IStorageService private readonly storageService: IStorageService, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStatusbarService private readonly statusbarService: IStatusbarService, @@ -74,10 +72,6 @@ export class ChatGettingStartedContribution extends Disposable implements IWorkb // Open Copilot view showCopilotView(this.viewsService, this.layoutService); - const setupFromDialog = this.configurationService.getValue('chat.setupFromDialog'); - if (!setupFromDialog) { - ensureSideBarChatViewSize(this.viewDescriptorService, this.layoutService, this.viewsService); - } // Only do this once this.storageService.store(ChatGettingStartedContribution.hideWelcomeView, true, StorageScope.APPLICATION, StorageTarget.MACHINE); diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 0a37a907953..f387456dcab 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -18,7 +18,6 @@ import { IContextKeyService } from '../../../../platform/contextkey/common/conte import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import product from '../../../../platform/product/common/product.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { PromptsConfig } from '../../../../platform/prompts/common/config.js'; import { DEFAULT_SOURCE_FOLDER as PROMPT_FILES_DEFAULT_SOURCE_FOLDER, PROMPT_FILE_EXTENSION } from '../../../../platform/prompts/common/constants.js'; @@ -198,12 +197,6 @@ configurationRegistry.registerConfiguration({ description: nls.localize('chat.renderRelatedFiles', "Controls whether related files should be rendered in the chat input."), default: false }, - 'chat.setupFromDialog': { // TODO@bpasero remove this eventually - type: 'boolean', - description: nls.localize('chat.setupFromChat', "Controls whether Copilot setup starts from a dialog or from the welcome view."), - default: product.quality !== 'stable', - tags: ['experimental', 'onExp'] - }, 'chat.focusWindowOnConfirmation': { type: 'boolean', description: nls.localize('chat.focusWindowOnConfirmation', "Controls whether the Copilot window should be focused when a confirmation is needed."), diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 31a1caa19c2..3205aaf7a37 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -11,8 +11,7 @@ import { Selection } from '../../../../editor/common/core/selection.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js'; -import { IWorkbenchLayoutService, Parts } from '../../../services/layout/browser/layoutService.js'; +import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { IChatAgentCommand, IChatAgentData } from '../common/chatAgents.js'; import { IChatResponseModel } from '../common/chatModel.js'; @@ -59,27 +58,6 @@ export function showCopilotView(viewsService: IViewsService, layoutService: IWor return showChatView(viewsService); } -export function ensureSideBarChatViewSize(viewDescriptorService: IViewDescriptorService, layoutService: IWorkbenchLayoutService, viewsService: IViewsService): void { - const location = viewDescriptorService.getViewLocationById(ChatViewId); - if (location === ViewContainerLocation.Panel) { - return; // panel is typically very wide - } - - const viewPart = location === ViewContainerLocation.Sidebar ? Parts.SIDEBAR_PART : Parts.AUXILIARYBAR_PART; - const partSize = layoutService.getSize(viewPart); - - let adjustedChatWidth: number | undefined; - if (partSize.width < 400 && layoutService.mainContainerDimension.width > 1200) { - adjustedChatWidth = 400; // up to 400px if window bounds permit - } else if (partSize.width < 300) { - adjustedChatWidth = 300; // at minimum 300px - } - - if (typeof adjustedChatWidth === 'number') { - layoutService.setSize(viewPart, { width: adjustedChatWidth, height: partSize.height }); - } -} - export const IQuickChatService = createDecorator('quickChatService'); export interface IQuickChatService { readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index b2f52a1f20c..fc1321cf32a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -3,11 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, getActiveElement, setVisibility } from '../../../../base/browser/dom.js'; -import { ButtonWithDropdown } from '../../../../base/browser/ui/button/button.js'; +import { $ } from '../../../../base/browser/dom.js'; import { Dialog } from '../../../../base/browser/ui/dialog/dialog.js'; -import { renderIcon } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; -import { mainWindow } from '../../../../base/browser/window.js'; import { toAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../../base/common/actions.js'; import { timeout } from '../../../../base/common/async.js'; import { Codicon } from '../../../../base/common/codicons.js'; @@ -44,7 +41,6 @@ import { IProgressService, ProgressLocation } from '../../../../platform/progres import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { ITelemetryService, TelemetryLevel } from '../../../../platform/telemetry/common/telemetry.js'; -import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { IWorkspaceTrustRequestService } from '../../../../platform/workspace/common/workspaceTrust.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js'; @@ -66,10 +62,9 @@ import { IChatProgress, IChatService } from '../common/chatService.js'; import { ChatAgentLocation, ChatConfiguration, ChatMode, validateChatMode } from '../common/constants.js'; import { ILanguageModelsService } from '../common/languageModels.js'; import { CHAT_CATEGORY, CHAT_OPEN_ACTION_ID, CHAT_SETUP_ACTION_ID } from './actions/chatActions.js'; -import { ChatViewId, ensureSideBarChatViewSize, IChatWidgetService, showCopilotView } from './chat.js'; +import { ChatViewId, IChatWidgetService, showCopilotView } from './chat.js'; import { CHAT_SIDEBAR_PANEL_ID } from './chatViewPane.js'; import './media/chatSetup.css'; -import { ChatViewsWelcomeExtensions, IChatViewsWelcomeContributionRegistry } from './viewsWelcome/chatViewsWelcome.js'; const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', @@ -436,13 +431,13 @@ class ChatSetup { try { switch (setupStrategy) { case ChatSetupStrategy.SetupWithEnterpriseProvider: - success = await this.controller.value.setupWithProvider({ setupFromDialog: true, useEnterpriseProvider: true }); + success = await this.controller.value.setupWithProvider({ useEnterpriseProvider: true }); break; case ChatSetupStrategy.SetupWithoutEnterpriseProvider: - success = await this.controller.value.setupWithProvider({ setupFromDialog: true, useEnterpriseProvider: false }); + success = await this.controller.value.setupWithProvider({ useEnterpriseProvider: false }); break; case ChatSetupStrategy.DefaultSetup: - success = await this.controller.value.setup({ setupFromDialog: true }); + success = await this.controller.value.setup(); break; } } catch (error) { @@ -507,7 +502,7 @@ class ChatSetup { } private createDialog(disposables: DisposableStore): HTMLElement { - const element = $('.chat-setup-view'); + const element = $('.chat-setup-dialog'); const markdown = this.instantiationService.createInstance(MarkdownRenderer, {}); @@ -539,7 +534,6 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr @ICommandService private readonly commandService: ICommandService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IChatEntitlementService chatEntitlementService: ChatEntitlementService, - @IConfigurationService private readonly configurationService: IConfigurationService, @ILogService private readonly logService: ILogService, ) { super(); @@ -553,7 +547,6 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr const controller = new Lazy(() => this._register(this.instantiationService.createInstance(ChatSetupController, context, requests))); this.registerSetupAgents(context, controller); - this.registerChatWelcome(context, controller); this.registerActions(context, requests, controller); this.registerUrlLinkHandler(); } @@ -562,7 +555,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr const registration = markAsSingleton(new MutableDisposable()); // prevents flicker on window reload const updateRegistration = () => { - const disabled = context.state.hidden || !this.configurationService.getValue('chat.setupFromDialog'); + const disabled = context.state.hidden; if (!disabled && !registration.value) { const { agent: panelAgent, disposable: panelDisposable } = SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Panel, ChatMode.Ask, context, controller); registration.value = combinedDisposable( @@ -587,19 +580,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr } }; - this._register(Event.runAndSubscribe(Event.any( - context.onDidChange, - Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('chat.setupFromDialog')) - ), () => updateRegistration())); - } - - private registerChatWelcome(context: ChatEntitlementContext, controller: Lazy): void { - Registry.as(ChatViewsWelcomeExtensions.ChatViewsWelcomeRegistry).register({ - title: localize('welcomeChat', "Welcome to Copilot"), - when: ChatContextKeys.SetupViewCondition, - icon: Codicon.copilotLarge, - content: disposables => disposables.add(this.instantiationService.createInstance(ChatSetupWelcomeContent, controller.value, context)).element, - }); + this._register(Event.runAndSubscribe(context.onDidChange, () => updateRegistration())); } private registerActions(context: ChatEntitlementContext, requests: ChatEntitlementRequests, controller: Lazy): void { @@ -625,10 +606,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr order: 1, when: ContextKeyExpr.and( chatSetupTriggerContext, - ContextKeyExpr.or( - ChatContextKeys.Setup.fromDialog.negate(), // reduce noise when using the skeleton-view approach - ChatContextKeys.Setup.hidden // but enforce it if copilot is hidden - ) + ChatContextKeys.Setup.hidden ) } }); @@ -636,7 +614,6 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr override async run(accessor: ServicesAccessor, mode: ChatMode): Promise { const viewsService = accessor.get(IViewsService); - const viewDescriptorService = accessor.get(IViewDescriptorService); const configurationService = accessor.get(IConfigurationService); const layoutService = accessor.get(IWorkbenchLayoutService); const statusbarService = accessor.get(IStatusbarService); @@ -653,27 +630,20 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr chatWidget?.input.setChatMode(mode); } - const setupFromDialog = configurationService.getValue('chat.setupFromDialog'); - if (!setupFromDialog) { - ensureSideBarChatViewSize(viewDescriptorService, layoutService, viewsService); - } - statusbarService.updateEntryVisibility('chat.statusBarEntry', true); configurationService.updateValue('chat.commandCenter.enabled', true); - if (setupFromDialog) { - const setup = ChatSetup.getInstance(instantiationService, context, controller); - const result = await setup.run(); - if (result === false && !lifecycleService.willShutdown) { - const { confirmed } = await dialogService.confirm({ - type: Severity.Error, - message: localize('setupErrorDialog', "Copilot setup failed. Would you like to try again?"), - primaryButton: localize('retry', "Retry"), - }); + const setup = ChatSetup.getInstance(instantiationService, context, controller); + const result = await setup.run(); + if (result === false && !lifecycleService.willShutdown) { + const { confirmed } = await dialogService.confirm({ + type: Severity.Error, + message: localize('setupErrorDialog', "Copilot setup failed. Would you like to try again?"), + primaryButton: localize('retry', "Retry"), + }); - if (confirmed) { - commandService.executeCommand(CHAT_SETUP_ACTION_ID); - } + if (confirmed) { + commandService.executeCommand(CHAT_SETUP_ACTION_ID); } } } @@ -816,13 +786,11 @@ type InstallChatClassification = { installResult: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the extension was installed successfully, cancelled or failed to install.' }; installDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The duration it took to install the extension.' }; signUpErrorCode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The error code in case of an error signing up.' }; - setupFromDialog: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the setup was triggered from the dialog or not.' }; }; type InstallChatEvent = { installResult: 'installed' | 'alreadyInstalled' | 'cancelled' | 'failedInstall' | 'failedNotSignedIn' | 'failedSignUp' | 'failedNotTrusted' | 'failedNoSession'; installDuration: number; signUpErrorCode: number | undefined; - setupFromDialog: boolean; }; enum ChatSetupStep { @@ -844,15 +812,12 @@ class ChatSetupController extends Disposable { private readonly requests: ChatEntitlementRequests, @ITelemetryService private readonly telemetryService: ITelemetryService, @IAuthenticationService private readonly authenticationService: IAuthenticationService, - @IViewsService private readonly viewsService: IViewsService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IProductService private readonly productService: IProductService, @ILogService private readonly logService: ILogService, @IProgressService private readonly progressService: IProgressService, - @IChatAgentService private readonly chatAgentService: IChatAgentService, @IActivityService private readonly activityService: IActivityService, @ICommandService private readonly commandService: ICommandService, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IWorkspaceTrustRequestService private readonly workspaceTrustRequestService: IWorkspaceTrustRequestService, @IDialogService private readonly dialogService: IDialogService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -877,7 +842,7 @@ class ChatSetupController extends Disposable { this._onDidChange.fire(); } - async setup(options?: { forceSignIn?: boolean; setupFromDialog?: boolean }): Promise { + async setup(options?: { forceSignIn?: boolean }): Promise { const watch = new StopWatch(false); const title = localize('setupChatProgress', "Getting Copilot ready..."); const badge = this.activityService.showViewContainerActivity(CHAT_SIDEBAR_PANEL_ID, { @@ -895,10 +860,9 @@ class ChatSetupController extends Disposable { } } - private async doSetup(options: { forceSignIn?: boolean; setupFromDialog?: boolean }, watch: StopWatch): Promise { + private async doSetup(options: { forceSignIn?: boolean }, watch: StopWatch): Promise { this.context.suspend(); // reduces flicker - let focusChatInput = false; let success = false; try { const providerId = ChatEntitlementRequests.providerId(this.configurationService); @@ -908,9 +872,9 @@ class ChatSetupController extends Disposable { // Entitlement Unknown or `forceSignIn`: we need to sign-in user if (this.context.state.entitlement === ChatEntitlement.Unknown || options.forceSignIn) { this.setStep(ChatSetupStep.SigningIn); - const result = await this.signIn(providerId, options); + const result = await this.signIn(providerId); if (!result.session) { - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNotSignedIn', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog: Boolean(options.setupFromDialog) }); + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNotSignedIn', installDuration: watch.elapsed(), signUpErrorCode: undefined }); return false; } @@ -922,38 +886,25 @@ class ChatSetupController extends Disposable { message: localize('copilotWorkspaceTrust', "Copilot is currently only supported in trusted workspaces.") }); if (!trusted) { - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNotTrusted', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog: Boolean(options.setupFromDialog) }); + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNotTrusted', installDuration: watch.elapsed(), signUpErrorCode: undefined }); return false; } - const activeElement = getActiveElement(); - // Install this.setStep(ChatSetupStep.Installing); - success = await this.install(session, entitlement ?? this.context.state.entitlement, providerId, options, watch); - - const currentActiveElement = getActiveElement(); - focusChatInput = activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body; + success = await this.install(session, entitlement ?? this.context.state.entitlement, providerId, watch); } finally { this.setStep(ChatSetupStep.Initial); this.context.resume(); } - if (focusChatInput && !options.setupFromDialog) { - (await showCopilotView(this.viewsService, this.layoutService))?.focusInput(); - } - return success; } - private async signIn(providerId: string, options?: { setupFromDialog?: boolean }): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> { + private async signIn(providerId: string): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> { let session: AuthenticationSession | undefined; let entitlements; try { - if (!options?.setupFromDialog) { - showCopilotView(this.viewsService, this.layoutService); - } - ({ session, entitlements } = await this.requests.signIn()); } catch (e) { this.logService.error(`[chat setup] signIn: error ${e}`); @@ -968,21 +919,18 @@ class ChatSetupController extends Disposable { }); if (confirmed) { - return this.signIn(providerId, options); + return this.signIn(providerId); } } return { session, entitlement: entitlements?.entitlement }; } - private async install(session: AuthenticationSession | undefined, entitlement: ChatEntitlement, providerId: string, options: { setupFromDialog?: boolean }, watch: StopWatch): Promise { + private async install(session: AuthenticationSession | undefined, entitlement: ChatEntitlement, providerId: string, watch: StopWatch): Promise { const wasInstalled = this.context.state.installed; let signUpResult: boolean | { errorCode: number } | undefined = undefined; try { - if (!options?.setupFromDialog) { - showCopilotView(this.viewsService, this.layoutService); - } if ( entitlement !== ChatEntitlement.Limited && // User is not signed up to Copilot Free @@ -997,7 +945,7 @@ class ChatSetupController extends Disposable { } if (!session) { - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNoSession', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog: Boolean(options.setupFromDialog) }); + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNoSession', installDuration: watch.elapsed(), signUpErrorCode: undefined }); return false; // unexpected } } @@ -1005,30 +953,23 @@ class ChatSetupController extends Disposable { signUpResult = await this.requests.signUpLimited(session); if (typeof signUpResult !== 'boolean' /* error */) { - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedSignUp', installDuration: watch.elapsed(), signUpErrorCode: signUpResult.errorCode, setupFromDialog: Boolean(options.setupFromDialog) }); + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedSignUp', installDuration: watch.elapsed(), signUpErrorCode: signUpResult.errorCode }); } } await this.doInstall(); } catch (error) { this.logService.error(`[chat setup] install: error ${error}`); - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: isCancellationError(error) ? 'cancelled' : 'failedInstall', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog: Boolean(options.setupFromDialog) }); + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: isCancellationError(error) ? 'cancelled' : 'failedInstall', installDuration: watch.elapsed(), signUpErrorCode: undefined }); return false; } - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: wasInstalled ? 'alreadyInstalled' : 'installed', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog: Boolean(options.setupFromDialog) }); + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: wasInstalled ? 'alreadyInstalled' : 'installed', installDuration: watch.elapsed(), signUpErrorCode: undefined }); if (wasInstalled && signUpResult === true) { refreshTokens(this.commandService); } - if (!options?.setupFromDialog) { - await Promise.race([ - timeout(5000), // helps prevent flicker with sign-in welcome view - Event.toPromise(this.chatAgentService.onDidChangeAgents) // https://github.com/microsoft/vscode-copilot/issues/9274 - ]); - } - return true; } @@ -1065,7 +1006,7 @@ class ChatSetupController extends Disposable { } } - async setupWithProvider(options: { useEnterpriseProvider: boolean; setupFromDialog?: boolean }): Promise { + async setupWithProvider(options: { useEnterpriseProvider: boolean }): Promise { const registry = Registry.as(ConfigurationExtensions.Configuration); registry.registerConfiguration({ 'id': 'copilot.setup', @@ -1184,129 +1125,6 @@ class ChatSetupController extends Disposable { //#endregion -//#region Setup View Welcome - -class ChatSetupWelcomeContent extends Disposable { - - readonly element = $('.chat-setup-view'); - - constructor( - private readonly controller: ChatSetupController, - private readonly context: ChatEntitlementContext, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - ) { - super(); - - this.create(); - } - - private create(): void { - const markdown = this.instantiationService.createInstance(MarkdownRenderer, {}); - - // Header - { - const header = localize({ key: 'header', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer.", this.context.state.installed ? `command:${defaultChat.walkthroughCommand}` : defaultChat.documentationUrl); - this.element.appendChild($('p', undefined, this._register(markdown.render(new MarkdownString(header, { isTrusted: true }))).element)); - - this.element.appendChild( - $('div.chat-features-container', undefined, - $('div', undefined, - $('div.chat-feature-container', undefined, - renderIcon(Codicon.code), - $('span', undefined, localize('featureChat', "Code faster with Completions")) - ), - $('div.chat-feature-container', undefined, - renderIcon(Codicon.editSession), - $('span', undefined, localize('featureEdits', "Build features with Copilot Edits")) - ), - $('div.chat-feature-container', undefined, - renderIcon(Codicon.commentDiscussion), - $('span', undefined, localize('featureExplore', "Explore your codebase with Chat")) - ) - ) - ) - ); - } - - // Limited SKU - const free = localize({ key: 'free', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); - const freeContainer = this.element.appendChild($('p', undefined, this._register(markdown.render(new MarkdownString(free, { isTrusted: true, supportThemeIcons: true }))).element)); - - // Setup Button - const buttonContainer = this.element.appendChild($('p')); - buttonContainer.classList.add('button-container'); - const button = this._register(new ButtonWithDropdown(buttonContainer, { - actions: [ - toAction({ id: 'chatSetup.setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => this.controller.setupWithProvider({ useEnterpriseProvider: false }) }), - toAction({ id: 'chatSetup.setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => this.controller.setupWithProvider({ useEnterpriseProvider: true }) }) - ], - addPrimaryActionToDropdown: false, - contextMenuProvider: this.contextMenuService, - supportIcons: true, - ...defaultButtonStyles - })); - this._register(button.onDidClick(() => this.controller.setup())); - - // Terms - const terms = localize({ key: 'terms', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to the [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); - this.element.appendChild($('p', undefined, this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element)); - - // SKU Settings - const settings = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot Free and Pro may show [public code]({0}) suggestions and we may use your data for product improvement. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); - const settingsContainer = this.element.appendChild($('p', undefined, this._register(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element)); - - // Update based on model state - this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(freeContainer, settingsContainer, button))); - } - - private update(freeContainer: HTMLElement, settingsContainer: HTMLElement, button: ButtonWithDropdown): void { - const showSettings = this.telemetryService.telemetryLevel !== TelemetryLevel.NONE; - let showFree: boolean; - let buttonLabel: string; - - switch (this.context.state.entitlement) { - case ChatEntitlement.Unknown: - showFree = true; - buttonLabel = this.context.state.registered ? localize('signUp', "Sign in to use Copilot") : localize('signUpFree', "Sign in to use Copilot for free"); - break; - case ChatEntitlement.Unresolved: - showFree = true; - buttonLabel = this.context.state.registered ? localize('startUp', "Use Copilot") : localize('startUpLimited', "Use Copilot for free"); - break; - case ChatEntitlement.Available: - case ChatEntitlement.Limited: - showFree = true; - buttonLabel = localize('startUpLimited', "Use Copilot for free"); - break; - case ChatEntitlement.Pro: - case ChatEntitlement.Unavailable: - showFree = false; - buttonLabel = localize('startUp', "Use Copilot"); - break; - } - - switch (this.controller.step) { - case ChatSetupStep.SigningIn: - buttonLabel = localize('setupChatSignIn', "$(loading~spin) Signing in to {0}...", ChatEntitlementRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId ? defaultChat.enterpriseProviderName : defaultChat.providerName); - break; - case ChatSetupStep.Installing: - buttonLabel = localize('setupChatInstalling', "$(loading~spin) Getting Copilot Ready..."); - break; - } - - setVisibility(showFree, freeContainer); - setVisibility(showSettings, settingsContainer); - - button.label = buttonLabel; - button.enabled = this.controller.step === ChatSetupStep.Initial; - } -} - -//#endregion - function refreshTokens(commandService: ICommandService): void { // ugly, but we need to signal to the extension that entitlements changed commandService.executeCommand(defaultChat.completionsRefreshTokenCommand); diff --git a/src/vs/workbench/contrib/chat/browser/chatStatus.ts b/src/vs/workbench/contrib/chat/browser/chatStatus.ts index 7a017875fd5..1028bcc90e7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatStatus.ts +++ b/src/vs/workbench/contrib/chat/browser/chatStatus.ts @@ -129,11 +129,6 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu if (!hidden) { this.entry ||= this.statusbarService.addEntry(this.getEntryProps(), 'chat.statusBarEntry', StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.RIGHT }); - - // TODO@bpasero: remove this eventually - const completionsStatusId = `${defaultChat.extensionId}.status`; - this.statusbarService.updateEntryVisibility(completionsStatusId, false); - this.statusbarService.overrideEntry(completionsStatusId, { name: localize('codeCompletionsStatus', "Copilot Code Completions"), text: localize('codeCompletionsStatusText', "$(copilot) Completions") }); } else { this.entry?.dispose(); this.entry = undefined; diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index bed396a8b7a..63e8a5dc98d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -26,7 +26,6 @@ import { SIDE_BAR_FOREGROUND } from '../../../common/theme.js'; import { IViewDescriptorService } from '../../../common/views.js'; import { IChatViewTitleActionContext } from '../common/chatActions.js'; import { IChatAgentService } from '../common/chatAgents.js'; -import { ChatContextKeys } from '../common/chatContextKeys.js'; import { ChatModelInitState, IChatModel } from '../common/chatModel.js'; import { CHAT_PROVIDER_ID } from '../common/chatParticipantContribTypes.js'; import { IChatService } from '../common/chatService.js'; @@ -124,12 +123,6 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { this._onDidChangeViewWelcomeState.fire(); })); - - this._register(this.contextKeyService.onDidChangeContext(e => { - if (e.affectsSome(ChatContextKeys.SetupViewKeys)) { - this._onDidChangeViewWelcomeState.fire(); - } - })); } override getActionsContext(): IChatViewTitleActionContext | undefined { @@ -161,11 +154,10 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { } override shouldShowWelcome(): boolean { - const showSetup = this.contextKeyService.contextMatchesRules(ChatContextKeys.SetupViewCondition); const noPersistedSessions = !this.chatService.hasSessions(); const hasCoreAgent = this.chatAgentService.getAgents().some(agent => agent.isCore && agent.locations.includes(this.chatOptions.location)); - const shouldShow = !hasCoreAgent && (this.didUnregisterProvider || !this._widget?.viewModel && noPersistedSessions || this.defaultParticipantRegistrationFailed || showSetup); - this.logService.trace(`ChatViewPane#shouldShowWelcome(${this.chatOptions.location}) = ${shouldShow}: hasCoreAgent=${hasCoreAgent} didUnregister=${this.didUnregisterProvider} || noViewModel=${!this._widget?.viewModel} && noPersistedSessions=${noPersistedSessions} || defaultParticipantRegistrationFailed=${this.defaultParticipantRegistrationFailed} || showSetup=${showSetup}`); + const shouldShow = !hasCoreAgent && (this.didUnregisterProvider || !this._widget?.viewModel && noPersistedSessions || this.defaultParticipantRegistrationFailed); + this.logService.trace(`ChatViewPane#shouldShowWelcome(${this.chatOptions.location}) = ${shouldShow}: hasCoreAgent=${hasCoreAgent} didUnregister=${this.didUnregisterProvider} || noViewModel=${!this._widget?.viewModel} && noPersistedSessions=${noPersistedSessions} || defaultParticipantRegistrationFailed=${this.defaultParticipantRegistrationFailed}`); return !!shouldShow; } diff --git a/src/vs/workbench/contrib/chat/browser/media/chatSetup.css b/src/vs/workbench/contrib/chat/browser/media/chatSetup.css index b042970d0d8..3d1e37d5e2f 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatSetup.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatSetup.css @@ -3,25 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.chat-welcome-view .chat-setup-view { - - text-align: center; - - .chat-features-container { - display: flex; - justify-content: center; - text-align: initial; - border-radius: 2px; - border: 1px solid var(--vscode-chat-requestBorder); - background-color: var(--vscode-chat-requestBackground); - } -} - -.dialog-message-body .chat-setup-view { +.chat-setup-dialog { p { margin-top: 0; margin-bottom: 0; + width: 100%; } p.setup-legal { @@ -35,14 +22,6 @@ color: var(--vscode-descriptionForeground); margin-top: 1em; } -} - -.dialog-message-body .chat-setup-view, -.chat-welcome-view .chat-setup-view { - - p { - width: 100%; - } .chat-feature-container { display: flex; diff --git a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css deleted file mode 100644 index 785aeaa2fa7..00000000000 --- a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css +++ /dev/null @@ -1,51 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.chat-welcome-view .chat-setup-view { - text-align: center; - - p { - width: 100%; - } - - .chat-features-container { - display: flex; - justify-content: center; - text-align: initial; - border-radius: 2px; - border: 1px solid var(--vscode-chat-requestBorder); - background-color: var(--vscode-chat-requestBackground); - } - - .chat-feature-container { - display: flex; - align-items: center; - gap: 6px; - padding: 5px 10px 5px 10px; - } - - .chat-feature-container .codicon[class*='codicon-'] { - font-size: 16px; - } - - .codicon[class*='codicon-'] { - font-size: 13px; - line-height: 1.4em; - vertical-align: bottom; - } - - .button-container { - padding-top: 20px; - } - - /** Dropdown Button */ - .monaco-button-dropdown { - width: 100%; - } - - .monaco-button-dropdown .monaco-text-button { - width: 100%; - } -} diff --git a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts index 113bb9ffa68..3d665e130d6 100644 --- a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts +++ b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts @@ -51,8 +51,7 @@ export namespace ChatContextKeys { export const Setup = { hidden: new RawContextKey('chatSetupHidden', false, true), // True when chat setup is explicitly hidden. - installed: new RawContextKey('chatSetupInstalled', false, true), // True when the chat extension is installed. - fromDialog: ContextKeyExpr.has('config.chat.setupFromDialog'), + installed: new RawContextKey('chatSetupInstalled', false, true) // True when the chat extension is installed. }; export const Entitlement = { @@ -62,24 +61,6 @@ export namespace ChatContextKeys { pro: new RawContextKey('chatPlanPro', false, true) // True when user is a chat pro user. }; - export const SetupViewKeys = new Set([ChatContextKeys.Setup.hidden.key, ChatContextKeys.Setup.installed.key, ChatContextKeys.Entitlement.signedOut.key, ChatContextKeys.Entitlement.canSignUp.key, ...Setup.fromDialog.keys()]); - export const SetupViewCondition = ContextKeyExpr.and( - Setup.fromDialog.negate(), - ContextKeyExpr.or( - ContextKeyExpr.and( - ChatContextKeys.Setup.hidden.negate(), - ChatContextKeys.Setup.installed.negate() - ), - ContextKeyExpr.and( - ChatContextKeys.Entitlement.canSignUp, - ChatContextKeys.Setup.installed - ), - ContextKeyExpr.and( - ChatContextKeys.Entitlement.signedOut, - ChatContextKeys.Setup.installed - ) - ))!; - export const chatQuotaExceeded = new RawContextKey('chatQuotaExceeded', false, true); export const completionsQuotaExceeded = new RawContextKey('completionsQuotaExceeded', false, true); diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index b6e29d43d5b..b556ef2eaea 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -470,15 +470,10 @@ export class ChatService extends Disposable implements IChatService { this.trace('initializeSession', `Initialize session ${model.sessionId}`); model.startInitialize(); - const activation = this.activateDefaultAgent(model.initialLocation); - if (this.configurationService.getValue('chat.setupFromDialog')) { - // Activate the default extension provided agent but do not wait - // for it to be ready so that the session can be used immediately - // without having to wait for the agent to be ready. - activation.catch(e => this.logService.error(e)); - } else { - await activation; - } + // Activate the default extension provided agent but do not wait + // for it to be ready so that the session can be used immediately + // without having to wait for the agent to be ready. + this.activateDefaultAgent(model.initialLocation).catch(e => this.logService.error(e)); model.initialize(); } catch (err) {