Skip to content

Commit 14e8d65

Browse files
authored
chat: show ask-questions header as title, full question text below with multi-line options (#296902)
1 parent 3d37407 commit 14e8d65

File tree

3 files changed

+83
-43
lines changed

3 files changed

+83
-43
lines changed

src/vs/workbench/contrib/chat/browser/widget/chatContentParts/chatQuestionCarouselPart.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -489,11 +489,10 @@ export class ChatQuestionCarouselPart extends Disposable implements IChatContent
489489
// Render question header row with title and close button
490490
const headerRow = dom.$('.chat-question-header-row');
491491

492-
// Render question message with title styling (no progress prefix)
493-
// Fall back to question.title if message is not provided
494-
const questionText = question.message ?? question.title;
495-
if (questionText) {
492+
// Render question title (short header) in the header bar as plain text
493+
if (question.title) {
496494
const title = dom.$('.chat-question-title');
495+
const questionText = question.title;
497496
const messageContent = this.getQuestionText(questionText);
498497

499498
title.setAttribute('aria-label', messageContent);
@@ -529,6 +528,18 @@ export class ChatQuestionCarouselPart extends Disposable implements IChatContent
529528

530529
this._questionContainer.appendChild(headerRow);
531530

531+
// Render full question text below the header row (supports multi-line and markdown)
532+
if (question.message) {
533+
const messageEl = dom.$('.chat-question-message');
534+
if (isMarkdownString(question.message)) {
535+
const renderedMessage = questionRenderStore.add(this._markdownRendererService.render(MarkdownString.lift(question.message)));
536+
messageEl.appendChild(renderedMessage.element);
537+
} else {
538+
messageEl.textContent = this.getQuestionText(question.message);
539+
}
540+
this._questionContainer.appendChild(messageEl);
541+
}
542+
532543
const isSingleQuestion = this.carousel.questions.length === 1;
533544
// Update step indicator in footer
534545
if (this._stepIndicator) {

src/vs/workbench/contrib/chat/browser/widget/chatContentParts/media/chatQuestionCarousel.css

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -43,38 +43,44 @@
4343

4444
.chat-question-header-row {
4545
display: flex;
46-
justify-content: space-between;
47-
align-items: center;
48-
gap: 8px;
49-
min-width: 0;
50-
padding-bottom: 5px;
51-
margin-left: -16px;
52-
margin-right: -16px;
53-
padding-left: 16px;
54-
padding-right: 16px;
55-
border-bottom: 1px solid var(--vscode-chat-requestBorder);
46+
flex-direction: column;
47+
background: var(--vscode-chat-requestBackground);
48+
padding: 0 16px 10px 16px;
49+
overflow: hidden;
5650

5751
.chat-question-title {
5852
flex: 1;
5953
min-width: 0;
60-
word-wrap: break-word;
61-
overflow-wrap: break-word;
62-
font-weight: 500;
63-
font-size: var(--vscode-chat-font-size-body-s);
64-
margin: 0;
54+
padding-top: 4px;
55+
padding-bottom: 4px;
56+
margin-left: -16px;
57+
margin-right: -16px;
58+
padding-left: 16px;
59+
padding-right: 16px;
60+
border-bottom: 1px solid var(--vscode-chat-requestBorder);
61+
62+
.chat-question-title {
63+
flex: 1;
64+
min-width: 0;
65+
word-wrap: break-word;
66+
overflow-wrap: break-word;
67+
font-weight: 500;
68+
font-size: var(--vscode-chat-font-size-body-s);
69+
margin: 0;
6570

66-
.rendered-markdown {
67-
a {
68-
color: var(--vscode-textLink-foreground);
69-
}
71+
.rendered-markdown {
72+
a {
73+
color: var(--vscode-textLink-foreground);
74+
}
7075

71-
a:hover,
72-
a:active {
73-
color: var(--vscode-textLink-activeForeground);
74-
}
76+
a:hover,
77+
a:active {
78+
color: var(--vscode-textLink-activeForeground);
79+
}
7580

76-
p {
77-
margin: 0;
81+
p {
82+
margin: 0;
83+
}
7884
}
7985
}
8086

@@ -105,6 +111,29 @@
105111
background: var(--vscode-toolbar-hoverBackground) !important;
106112
}
107113
}
114+
115+
.chat-question-message {
116+
padding-top: 8px;
117+
font-size: var(--vscode-chat-font-size-body-s);
118+
word-wrap: break-word;
119+
overflow-wrap: break-word;
120+
line-height: 1.4;
121+
122+
.rendered-markdown {
123+
a {
124+
color: var(--vscode-textLink-foreground);
125+
}
126+
127+
a:hover,
128+
a:active {
129+
color: var(--vscode-textLink-activeForeground);
130+
}
131+
132+
p {
133+
margin: 0;
134+
}
135+
}
136+
}
108137
}
109138
}
110139

@@ -142,7 +171,7 @@
142171

143172
.chat-question-list-item {
144173
display: flex;
145-
align-items: center;
174+
align-items: flex-start;
146175
gap: 8px;
147176
padding: 3px 8px;
148177
cursor: pointer;
@@ -167,9 +196,9 @@
167196
.chat-question-list-label {
168197
font-size: var(--vscode-chat-font-size-body-s);
169198
flex: 1;
170-
overflow: hidden;
171-
text-overflow: ellipsis;
172-
white-space: nowrap;
199+
word-wrap: break-word;
200+
overflow-wrap: break-word;
201+
padding-top: 2px;
173202
}
174203

175204
.chat-question-list-label-title {

src/vs/workbench/contrib/chat/test/browser/widget/chatContentParts/chatQuestionCarouselPart.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,15 @@ suite('ChatQuestionCarouselPart', () => {
9999

100100
const title = widget.domNode.querySelector('.chat-question-title');
101101
assert.ok(title, 'title element should exist');
102-
assert.ok(title?.querySelector('.rendered-markdown'), 'markdown content should be rendered');
103-
assert.strictEqual(title?.textContent?.includes('**details**'), false, 'markdown syntax should not be shown as raw text');
104-
const link = title?.querySelector('a') as HTMLAnchorElement | null;
102+
const messageEl = widget.domNode.querySelector('.chat-question-message');
103+
assert.ok(messageEl, 'message element should exist');
104+
assert.ok(messageEl?.querySelector('.rendered-markdown'), 'markdown content should be rendered');
105+
assert.strictEqual(messageEl?.textContent?.includes('**details**'), false, 'markdown syntax should not be shown as raw text');
106+
const link = messageEl?.querySelector('a') as HTMLAnchorElement | null;
105107
assert.ok(link, 'markdown link should render as anchor');
106108
});
107109

108-
test('renders markdown in plain string question message', () => {
110+
test('renders plain string question message as text', () => {
109111
const carousel = createMockCarousel([
110112
{
111113
id: 'q1',
@@ -116,12 +118,10 @@ suite('ChatQuestionCarouselPart', () => {
116118
]);
117119
createWidget(carousel);
118120

119-
const title = widget.domNode.querySelector('.chat-question-title');
120-
assert.ok(title, 'title element should exist');
121-
assert.ok(title?.querySelector('.rendered-markdown'), 'markdown content should be rendered for plain string messages');
122-
assert.strictEqual(title?.textContent?.includes('**details**'), false, 'markdown syntax should not be shown as raw text');
123-
const link = title?.querySelector('a') as HTMLAnchorElement | null;
124-
assert.ok(link, 'markdown link should render as anchor');
121+
const messageEl = widget.domNode.querySelector('.chat-question-message');
122+
assert.ok(messageEl, 'message element should exist');
123+
assert.ok(messageEl?.textContent?.includes('details'), 'plain text content should be rendered');
124+
assert.strictEqual(messageEl?.querySelector('.rendered-markdown'), null, 'plain string message should not use markdown renderer');
125125
});
126126

127127
test('renders progress indicator correctly', () => {

0 commit comments

Comments
 (0)