Skip to content

Commit 73fbb0c

Browse files
trying new empty state! (microsoft#253689)
* exp welcome view * use exp setting * use exp setting * positioned disclaimers under title to avoid overlap * Updated chat placeholder text * some fixes * fix hygiene * make sure to add additional message * swap exp is active * add new setting --------- Co-authored-by: Elijah King <[email protected]>
1 parent 4bd42b3 commit 73fbb0c

File tree

6 files changed

+72
-18
lines changed

6 files changed

+72
-18
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,12 @@ configurationRegistry.registerConfiguration({
262262
default: 'inline',
263263
tags: ['experimental', 'onExp'],
264264
},
265+
'chat.emptyChatState.enabled': {
266+
type: 'boolean',
267+
default: true,
268+
description: nls.localize('chat.emptyChatState', "Shows a modified empty chat state with hints in the input placeholder text."),
269+
tags: ['experimental', 'onExp'],
270+
},
265271
[mcpEnabledSection]: {
266272
type: 'boolean',
267273
description: nls.localize('chat.mcp.enabled', "Enables integration with Model Context Protocol servers to provide additional tools and functionality."),

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

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -751,22 +751,27 @@ export class ChatWidget extends Disposable implements IChatWidget {
751751
const configuration = this.configurationService.inspect('workbench.secondarySideBar.defaultVisibility');
752752
const expIsActive = configuration.defaultValue !== 'hidden';
753753

754+
const expEmptyState = this.configurationService.getValue<boolean>('chat.emptyChatState.enabled');
755+
754756
const chatSetupTriggerContext = ContextKeyExpr.or(
755757
ChatContextKeys.Setup.installed.negate(),
756758
ChatContextKeys.Entitlement.canSignUp
757759
);
758760

759761
let welcomeContent: IChatViewWelcomeContent;
762+
const defaultAgent = this.chatAgentService.getDefaultAgent(this.location, this.input.currentModeKind);
763+
const additionalMessage = defaultAgent?.metadata.additionalWelcomeMessage;
760764
if ((startupExpValue === StartupExperimentGroup.MaximizedChat
761765
|| startupExpValue === StartupExperimentGroup.SplitEmptyEditorChat
762766
|| startupExpValue === StartupExperimentGroup.SplitWelcomeChat
763767
|| expIsActive) && this.contextKeyService.contextMatchesRules(chatSetupTriggerContext)) {
764768
welcomeContent = this.getExpWelcomeViewContent();
765769
this.container.classList.add('experimental-welcome-view');
766770
}
771+
else if (expEmptyState) {
772+
welcomeContent = this.getWelcomeViewContent(additionalMessage, expEmptyState);
773+
}
767774
else {
768-
const defaultAgent = this.chatAgentService.getDefaultAgent(this.location, this.input.currentModeKind);
769-
const additionalMessage = defaultAgent?.metadata.additionalWelcomeMessage;
770775
const tips = this.input.currentModeKind === ChatModeKind.Ask
771776
? new MarkdownString(localize('chatWidget.tips', "{0} or type {1} to attach context\n\n{2} to chat with extensions\n\nType {3} to use commands", '$(attach)', '#', '$(mention)', '/'), { supportThemeIcons: true })
772777
: new MarkdownString(localize('chatWidget.tips.withoutParticipants', "{0} or type {1} to attach context", '$(attach)', '#'), { supportThemeIcons: true });
@@ -790,27 +795,37 @@ export class ChatWidget extends Disposable implements IChatWidget {
790795
}
791796
}
792797

793-
private getWelcomeViewContent(additionalMessage: string | IMarkdownString | undefined): IChatViewWelcomeContent {
794-
const baseMessage = localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use.");
798+
private getWelcomeViewContent(additionalMessage: string | IMarkdownString | undefined, expEmptyState?: boolean): IChatViewWelcomeContent {
799+
const disclaimerMessage = expEmptyState
800+
? localize('chatDisclaimer', "AI responses may be inaccurate.")
801+
: localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use.");
802+
const icon = expEmptyState ? Codicon.chatSparkle : Codicon.copilotLarge;
803+
795804
if (this.input.currentModeKind === ChatModeKind.Ask) {
796805
return {
797-
title: localize('chatDescription', "Ask Copilot"),
798-
message: new MarkdownString(baseMessage),
799-
icon: Codicon.copilotLarge,
806+
title: localize('chatDescription', "Ask about your code."),
807+
message: new MarkdownString(disclaimerMessage),
808+
icon,
800809
additionalMessage,
801810
};
802811
} else if (this.input.currentModeKind === ChatModeKind.Edit) {
812+
const editsHelpMessage = localize('editsHelp', "Start your editing session by defining a set of files that you want to work with. Then ask Copilot for the changes you want to make.");
813+
const message = expEmptyState ? disclaimerMessage : `${editsHelpMessage}\n\n${disclaimerMessage}`;
814+
803815
return {
804-
title: localize('editsTitle', "Edit with Copilot"),
805-
message: new MarkdownString(localize('editsMessage', "Start your editing session by defining a set of files that you want to work with. Then ask Copilot for the changes you want to make.") + `\n\n${baseMessage}`),
806-
icon: Codicon.copilotLarge,
816+
title: localize('editsTitle', "Edit in context."),
817+
message: new MarkdownString(message),
818+
icon,
807819
additionalMessage
808820
};
809821
} else {
822+
const agentHelpMessage = localize('agentMessage', "Ask Copilot to edit your files in [agent mode]({0}). Copilot will automatically use multiple requests to pick files to edit, run terminal commands, and iterate on errors.", 'https://aka.ms/vscode-copilot-agent');
823+
const message = expEmptyState ? disclaimerMessage : `${agentHelpMessage}\n\n${disclaimerMessage}`;
824+
810825
return {
811-
title: localize('editsTitle', "Edit with Copilot"),
812-
message: new MarkdownString(localize('agentMessage', "Ask Copilot to edit your files in [agent mode]({0}). Copilot will automatically use multiple requests to pick files to edit, run terminal commands, and iterate on errors.", 'https://aka.ms/vscode-copilot-agent') + `\n\n${baseMessage}`),
813-
icon: Codicon.copilotLarge,
826+
title: localize('agentTitle', "Build with agent mode."),
827+
message: new MarkdownString(message),
828+
icon,
814829
additionalMessage
815830
};
816831
}

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ import { ICodeEditorService } from '../../../../../editor/browser/services/codeE
1111
import { Range } from '../../../../../editor/common/core/range.js';
1212
import { IDecorationOptions } from '../../../../../editor/common/editorCommon.js';
1313
import { TrackedRangeStickiness } from '../../../../../editor/common/model.js';
14+
import { localize } from '../../../../../nls.js';
15+
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
1416
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
1517
import { inputPlaceholderForeground } from '../../../../../platform/theme/common/colorRegistry.js';
1618
import { IThemeService } from '../../../../../platform/theme/common/themeService.js';
1719
import { IChatAgentCommand, IChatAgentData, IChatAgentService } from '../../common/chatAgents.js';
1820
import { chatSlashCommandBackground, chatSlashCommandForeground } from '../../common/chatColors.js';
1921
import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, ChatRequestSlashPromptPart, ChatRequestTextPart, ChatRequestToolPart, ChatRequestToolSetPart, IParsedChatRequestPart, chatAgentLeader, chatSubcommandLeader } from '../../common/chatParserTypes.js';
2022
import { ChatRequestParser } from '../../common/chatRequestParser.js';
23+
import { ChatModeKind } from '../../common/constants.js';
2124
import { IChatWidget } from '../chat.js';
2225
import { ChatWidget } from '../chatWidget.js';
2326
import { dynamicVariableDecorationType } from './chatDynamicVariables.js';
@@ -44,6 +47,7 @@ class InputEditorDecorations extends Disposable {
4447
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
4548
@IThemeService private readonly themeService: IThemeService,
4649
@IChatAgentService private readonly chatAgentService: IChatAgentService,
50+
@IConfigurationService private readonly configurationService: IConfigurationService
4751
) {
4852
super();
4953

@@ -126,7 +130,16 @@ class InputEditorDecorations extends Disposable {
126130
}
127131

128132
if (!inputValue) {
129-
const description = this.widget.input.currentModeObs.get().description.get();
133+
const mode = this.widget.input.currentModeObs.get();
134+
let description = mode.description.get();
135+
if (this.configurationService.getValue<boolean>('chat.emptyChatState.enabled')) {
136+
if (mode.kind === ChatModeKind.Ask) {
137+
description += ` ${localize('askPlaceholderHint', "# context, @ extensions, / commands")}`;
138+
} else if (mode.kind === ChatModeKind.Edit || mode.kind === ChatModeKind.Agent) {
139+
description += ` ${localize('editPlaceholderHint', "# context")}`;
140+
}
141+
}
142+
130143
const decoration: IDecorationOptions[] = [
131144
{
132145
range: {

src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,22 @@ div.chat-welcome-view {
8080
}
8181
}
8282

83+
& > .chat-welcome-view-message.experimental-empty-state {
84+
position: relative;
85+
text-align: center;
86+
max-width: 100%;
87+
margin: 0 auto;
88+
color: var(--vscode-input-placeholderForeground);
89+
90+
a {
91+
color: var(--vscode-textLink-foreground);
92+
}
93+
p{
94+
margin-top: 8px;
95+
margin-bottom: 8px;
96+
}
97+
}
98+
8399
.monaco-button {
84100
display: inline-block;
85101
width: initial;

src/vs/workbench/contrib/chat/browser/viewsWelcome/chatViewWelcomeController.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ITelemetryService } from '../../../../../platform/telemetry/common/tele
2020
import { defaultButtonStyles } from '../../../../../platform/theme/browser/defaultStyles.js';
2121
import { ChatAgentLocation } from '../../common/constants.js';
2222
import { IChatWidgetService } from '../chat.js';
23+
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
2324
import { chatViewsWelcomeRegistry, IChatViewsWelcomeDescriptor } from './chatViewsWelcome.js';
2425

2526
const $ = dom.$;
@@ -142,6 +143,7 @@ export class ChatViewWelcomePart extends Disposable {
142143
@ILogService private logService: ILogService,
143144
@IChatWidgetService private chatWidgetService: IChatWidgetService,
144145
@ITelemetryService private telemetryService: ITelemetryService,
146+
@IConfigurationService private configurationService: IConfigurationService,
145147
) {
146148
super();
147149
this.element = dom.$('.chat-welcome-view');
@@ -160,13 +162,15 @@ export class ChatViewWelcomePart extends Disposable {
160162
title.textContent = content.title;
161163

162164
// Preview indicator
163-
if (typeof content.message !== 'function' && options?.isWidgetAgentWelcomeViewContent) {
165+
const expEmptyState = this.configurationService.getValue<boolean>('chat.emptyChatState.enabled');
166+
if (typeof content.message !== 'function' && options?.isWidgetAgentWelcomeViewContent && !expEmptyState) {
164167
const container = dom.append(this.element, $('.chat-welcome-view-indicator-container'));
165168
dom.append(container, $('.chat-welcome-view-subtitle', undefined, localize('agentModeSubtitle', "Agent Mode")));
166169
}
167170

168171
// Message
169172
const message = dom.append(this.element, content.isExperimental ? $('.chat-welcome-experimental-view-message') : $('.chat-welcome-view-message'));
173+
message.classList.toggle('experimental-empty-state', expEmptyState);
170174
if (typeof content.message === 'function') {
171175
dom.append(message, content.message(this._register(new DisposableStore())));
172176
} else {

src/vs/workbench/contrib/chat/common/chatModes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,9 @@ export class BuiltinChatMode implements IChatMode {
333333
}
334334

335335
export namespace ChatMode {
336-
export const Ask = new BuiltinChatMode(ChatModeKind.Ask, 'Ask', localize('chatDescription', "Ask Copilot"));
337-
export const Edit = new BuiltinChatMode(ChatModeKind.Edit, 'Edit', localize('editsDescription', "Edit files in your workspace"));
338-
export const Agent = new BuiltinChatMode(ChatModeKind.Agent, 'Agent', localize('agentDescription', "Edit files in your workspace in agent mode"));
336+
export const Ask = new BuiltinChatMode(ChatModeKind.Ask, 'Ask', localize('chatDescription', "Ask a question."));
337+
export const Edit = new BuiltinChatMode(ChatModeKind.Edit, 'Edit', localize('editsDescription', "Edit files."));
338+
export const Agent = new BuiltinChatMode(ChatModeKind.Agent, 'Agent', localize('agentDescription', "Build autonomously."));
339339
}
340340

341341
export function isBuiltinChatMode(mode: IChatMode): boolean {

0 commit comments

Comments
 (0)