Skip to content

Commit d41280b

Browse files
authored
Chat welcome experiment: Add suggested prompts and input centering (microsoft#252891)
Update chat widget welcome view with suggested prompts for startup experiments
1 parent db3240b commit d41280b

File tree

3 files changed

+132
-10
lines changed

3 files changed

+132
-10
lines changed

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

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import { buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHo
4040
import { asCssVariable } from '../../../../platform/theme/common/colorUtils.js';
4141
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
4242
import { checkModeOption } from '../common/chat.js';
43-
import { IChatAgentCommand, IChatAgentData, IChatAgentService, IChatWelcomeMessageContent } from '../common/chatAgents.js';
43+
import { IChatAgentCommand, IChatAgentData, IChatAgentService } from '../common/chatAgents.js';
4444
import { ChatContextKeys } from '../common/chatContextKeys.js';
4545
import { applyingChatEditsFailedContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, inChatEditingSessionContextKey, ModifiedFileEntryState } from '../common/chatEditingService.js';
4646
import { ChatPauseState, IChatModel, IChatResponseModel } from '../common/chatModel.js';
@@ -65,12 +65,13 @@ import { ChatEditorOptions } from './chatOptions.js';
6565
import './media/chat.css';
6666
import './media/chatAgentHover.css';
6767
import './media/chatViewWelcome.css';
68-
import { ChatViewWelcomePart } from './viewsWelcome/chatViewWelcomeController.js';
68+
import { ChatViewWelcomePart, IChatSuggestedPrompts, IChatViewWelcomeContent } from './viewsWelcome/chatViewWelcomeController.js';
6969
import { MicrotaskDelay } from '../../../../base/common/symbols.js';
7070
import { IChatRequestVariableEntry, ChatRequestVariableSet as ChatRequestVariableSet, isPromptFileVariableEntry, toPromptFileVariableEntry, PromptFileVariableKind } from '../common/chatVariableEntries.js';
7171
import { PromptsConfig } from '../common/promptSyntax/config/config.js';
7272
import { CancellationToken } from '../../../../base/common/cancellation.js';
7373
import { ComputeAutomaticInstructions } from '../common/promptSyntax/computeAutomaticInstructions.js';
74+
import { startupExpContext, StartupExperimentGroup } from '../../../services/coreExperimentation/common/coreExperimentationService.js';
7475

7576
const $ = dom.$;
7677

@@ -711,22 +712,43 @@ export class ChatWidget extends Disposable implements IChatWidget {
711712
}
712713

713714
private renderWelcomeViewContentIfNeeded() {
715+
716+
// reset the input in welcome view if it was rendered in experimental mode
717+
if (this.container.classList.contains('experimental-welcome-view')) {
718+
this.container.classList.remove('experimental-welcome-view');
719+
const renderFollowups = this.viewOptions.renderFollowups ?? false;
720+
const renderStyle = this.viewOptions.renderStyle;
721+
this.createInput(this.container, { renderFollowups, renderStyle });
722+
}
723+
714724
if (this.viewOptions.renderStyle === 'compact' || this.viewOptions.renderStyle === 'minimal') {
715725
return;
716726
}
717727

718728
const numItems = this.viewModel?.getItems().length ?? 0;
719729
if (!numItems) {
720-
const welcomeContent = this.getWelcomeViewContent();
721730
dom.clearNode(this.welcomeMessageContainer);
722-
const tips = this.input.currentModeKind === ChatModeKind.Ask
723-
? 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 })
724-
: new MarkdownString(localize('chatWidget.tips.withoutParticipants', "{0} or type {1} to attach context", '$(attach)', '#'), { supportThemeIcons: true });
725731
const defaultAgent = this.chatAgentService.getDefaultAgent(this.location, this.input.currentModeKind);
726732
const additionalMessage = defaultAgent?.metadata.additionalWelcomeMessage;
733+
734+
const startupExpValue = startupExpContext.getValue(this.contextKeyService);
735+
let welcomeContent: IChatViewWelcomeContent;
736+
if (startupExpValue === StartupExperimentGroup.MaximizedChat
737+
|| startupExpValue === StartupExperimentGroup.SplitEmptyEditorChat
738+
|| startupExpValue === StartupExperimentGroup.SplitWelcomeChat) {
739+
welcomeContent = this.getExpWelcomeViewContent();
740+
this.container.classList.add('experimental-welcome-view');
741+
}
742+
else {
743+
const tips = this.input.currentModeKind === ChatModeKind.Ask
744+
? 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 })
745+
: new MarkdownString(localize('chatWidget.tips.withoutParticipants', "{0} or type {1} to attach context", '$(attach)', '#'), { supportThemeIcons: true });
746+
welcomeContent = this.getWelcomeViewContent();
747+
welcomeContent.tips = tips;
748+
}
727749
this.welcomePart.value = this.instantiationService.createInstance(
728750
ChatViewWelcomePart,
729-
{ ...welcomeContent, tips, additionalMessage },
751+
{ ...welcomeContent, additionalMessage },
730752
{
731753
location: this.location,
732754
isWidgetAgentWelcomeViewContent: this.input?.currentModeKind === ChatModeKind.Agent
@@ -741,7 +763,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
741763
}
742764
}
743765

744-
private getWelcomeViewContent(): IChatWelcomeMessageContent {
766+
private getWelcomeViewContent(): IChatViewWelcomeContent {
745767
const baseMessage = localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use.");
746768
if (this.input.currentModeKind === ChatModeKind.Ask) {
747769
return {
@@ -764,6 +786,35 @@ export class ChatWidget extends Disposable implements IChatWidget {
764786
}
765787
}
766788

789+
private getExpWelcomeViewContent(): IChatViewWelcomeContent {
790+
const baseMessage = localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use.");
791+
const welcomeContent = {
792+
title: 'Get Started with VS Code',
793+
message: new MarkdownString(baseMessage),
794+
icon: Codicon.copilotLarge,
795+
suggestedPrompts: this.getExpSuggestedPrompts(),
796+
inputPart: this.inputPart.element,
797+
};
798+
return welcomeContent;
799+
}
800+
801+
private getExpSuggestedPrompts(): IChatSuggestedPrompts[] {
802+
803+
return [
804+
{
805+
icon: Codicon.vscode,
806+
label: localize('chatWidget.suggestedPrompts.gettingStarted', "Ask @vscode"),
807+
prompt: '@vscode Help me get started with VS Code?',
808+
},
809+
{
810+
icon: Codicon.newFolder,
811+
label: localize('chatWidget.suggestedPrompts.newProject', "Create a #new Project"),
812+
prompt: '#new Create a new project for me',
813+
}
814+
];
815+
}
816+
817+
767818
private async renderChatEditingSessionState() {
768819
if (!this.input) {
769820
return;

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,45 @@ div.chat-welcome-view {
9797
}
9898
}
9999
}
100+
101+
& > .chat-welcome-view-suggested-prompts {
102+
display: flex;
103+
flex-wrap: wrap;
104+
gap: 10px;
105+
justify-content: center;
106+
margin-top: 15px;
107+
108+
> .chat-welcome-view-suggested-prompt {
109+
display: flex;
110+
align-items: center;
111+
padding: 0 4px;
112+
border-radius: 8px;
113+
background-color: var(--vscode-editorWidget-background);
114+
cursor: pointer;
115+
border: 1px solid var(--vscode-chat-requestBorder, var(--vscode-input-background, transparent));
116+
border-radius: 4px;
117+
gap: 2px;
118+
max-width: 100%;
119+
width: fit-content;
120+
121+
> .chat-welcome-view-suggested-prompt-icon {
122+
display: flex;
123+
align-items: center;
124+
margin-right: 8px;
125+
font-size: 4px;
126+
color: var(--vscode-icon-foreground) !important;
127+
align-items: center;
128+
}
129+
130+
> .chat-welcome-view-suggested-prompt-label {
131+
font-size: 14px;
132+
color: var(--vscode-editorWidget-foreground);
133+
}
134+
}
135+
136+
> .chat-welcome-view-suggested-prompt:hover {
137+
background-color: var(--vscode-list-hoverBackground);
138+
border-color: var(--vscode-focusBorder);
139+
}
140+
}
100141
}

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com
1717
import { ILogService } from '../../../../../platform/log/common/log.js';
1818
import { IOpenerService } from '../../../../../platform/opener/common/opener.js';
1919
import { defaultButtonStyles } from '../../../../../platform/theme/browser/defaultStyles.js';
20-
import { IChatAgentService } from '../../common/chatAgents.js';
2120
import { ChatAgentLocation } from '../../common/constants.js';
2221
import { chatViewsWelcomeRegistry, IChatViewsWelcomeDescriptor } from './chatViewsWelcome.js';
22+
import { IChatWidgetService } from '../chat.js';
2323

2424
const $ = dom.$;
2525

@@ -113,6 +113,14 @@ export interface IChatViewWelcomeContent {
113113
message: IMarkdownString | ((disposables: DisposableStore) => HTMLElement);
114114
additionalMessage?: string | IMarkdownString;
115115
tips?: IMarkdownString;
116+
inputPart?: HTMLElement;
117+
suggestedPrompts?: IChatSuggestedPrompts[];
118+
}
119+
120+
export interface IChatSuggestedPrompts {
121+
icon?: ThemeIcon;
122+
label: string;
123+
prompt: string;
116124
}
117125

118126
export interface IChatViewWelcomeRenderOptions {
@@ -130,7 +138,7 @@ export class ChatViewWelcomePart extends Disposable {
130138
@IOpenerService private openerService: IOpenerService,
131139
@IInstantiationService private instantiationService: IInstantiationService,
132140
@ILogService private logService: ILogService,
133-
@IChatAgentService chatAgentService: IChatAgentService,
141+
@IChatWidgetService private chatWidgetService: IChatWidgetService,
134142
) {
135143
super();
136144
this.element = dom.$('.chat-welcome-view');
@@ -174,6 +182,28 @@ export class ChatViewWelcomePart extends Disposable {
174182
dom.append(message, additionalMessageResult.element);
175183
}
176184

185+
if (content.inputPart) {
186+
dom.append(this.element, content.inputPart);
187+
}
188+
189+
if (content.suggestedPrompts && content.suggestedPrompts.length) {
190+
191+
// create a tile with icon and label for each suggested promot
192+
const suggestedPromptsContainer = dom.append(this.element, $('.chat-welcome-view-suggested-prompts'));
193+
for (const prompt of content.suggestedPrompts) {
194+
const promptElement = dom.append(suggestedPromptsContainer, $('.chat-welcome-view-suggested-prompt'));
195+
if (prompt.icon) {
196+
const iconElement = dom.append(promptElement, $('.chat-welcome-view-suggested-prompt-icon'));
197+
iconElement.appendChild(renderIcon(prompt.icon));
198+
}
199+
const labelElement = dom.append(promptElement, $('.chat-welcome-view-suggested-prompt-label'));
200+
labelElement.textContent = prompt.label;
201+
this._register(dom.addDisposableListener(promptElement, dom.EventType.CLICK, () => {
202+
this.chatWidgetService.lastFocusedWidget?.setInput(prompt.prompt);
203+
}));
204+
}
205+
}
206+
177207
// Tips
178208
if (content.tips) {
179209
const tips = dom.append(this.element, $('.chat-welcome-view-tips'));

0 commit comments

Comments
 (0)