Skip to content

Commit 6b87182

Browse files
authored
chat: enhance final response rendering with pinning logic and repositioning (microsoft#293597)
1 parent 9bc20cc commit 6b87182

1 file changed

Lines changed: 19 additions & 2 deletions

File tree

src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,8 +1169,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
11691169
private renderChatContentDiff(partsToRender: ReadonlyArray<IChatRendererContent | null>, contentForThisTurn: ReadonlyArray<IChatRendererContent>, element: IChatResponseViewModel, elementIndex: number, templateData: IChatListItemTemplate): void {
11701170
const renderedParts = templateData.renderedParts ?? [];
11711171
templateData.renderedParts = renderedParts;
1172+
const lastMarkdownIndex = partsToRender.findLastIndex(part => part?.kind === 'markdownContent');
11721173
partsToRender.forEach((partToRender, contentIndex) => {
11731174
const alreadyRenderedPart = templateData.renderedParts?.[contentIndex];
1175+
const isFinalAnswerPart = partToRender?.kind === 'markdownContent' && contentIndex === lastMarkdownIndex && element.isComplete;
11741176

11751177
if (!partToRender) {
11761178
// null=no change
@@ -1188,7 +1190,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
11881190
}
11891191
renderedParts[contentIndex] = alreadyRenderedPart;
11901192
return;
1191-
} else if (alreadyRenderedPart instanceof ChatThinkingContentPart && this.shouldPinPart(partToRender, element)) {
1193+
} else if (alreadyRenderedPart instanceof ChatThinkingContentPart && this.shouldPinPart(partToRender, element) && !isFinalAnswerPart) {
11921194
// keep existing thinking part if we are pinning it (combining tool calls into it)
11931195
renderedParts[contentIndex] = alreadyRenderedPart;
11941196
return;
@@ -1219,7 +1221,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
12191221

12201222
// combine tool invocations into thinking part if needed. render the tool, but do not replace the working spinner with the new part's dom node since it is already inside the thinking part.
12211223
const lastThinking = this.getLastThinkingPart(renderedParts);
1222-
if (lastThinking && (partToRender.kind === 'toolInvocation' || partToRender.kind === 'toolInvocationSerialized' || partToRender.kind === 'markdownContent' || partToRender.kind === 'textEditGroup' || partToRender.kind === 'hook') && this.shouldPinPart(partToRender, element)) {
1224+
if (lastThinking && (partToRender.kind === 'toolInvocation' || partToRender.kind === 'toolInvocationSerialized' || partToRender.kind === 'markdownContent' || partToRender.kind === 'textEditGroup' || partToRender.kind === 'hook') && this.shouldPinPart(partToRender, element) && !isFinalAnswerPart) {
12231225
const newPart = this.renderChatContentPart(partToRender, templateData, context);
12241226
if (newPart) {
12251227
renderedParts[contentIndex] = newPart;
@@ -1379,9 +1381,17 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
13791381

13801382
private diff(renderedParts: ReadonlyArray<IChatContentPart>, contentToRender: ReadonlyArray<IChatRendererContent>, element: ChatTreeItem): ReadonlyArray<IChatRendererContent | null> {
13811383
const diff: (IChatRendererContent | null)[] = [];
1384+
const elementIsComplete = isResponseVM(element) && element.isComplete;
1385+
const lastMarkdownContentIndex = contentToRender.findLastIndex(part => part.kind === 'markdownContent');
13821386
for (let i = 0; i < contentToRender.length; i++) {
13831387
const content = contentToRender[i];
13841388
const renderedPart = renderedParts[i];
1389+
const isFinalAnswerPart = content.kind === 'markdownContent' && i === lastMarkdownContentIndex && elementIsComplete;
1390+
1391+
if (isFinalAnswerPart && this.isRenderedPartInsideThinking(renderedPart)) {
1392+
diff.push(content);
1393+
continue;
1394+
}
13851395

13861396
if (!renderedPart || !renderedPart.hasSameContent(content, contentToRender.slice(i + 1), element)) {
13871397
diff.push(content);
@@ -1394,6 +1404,13 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
13941404
return diff;
13951405
}
13961406

1407+
private isRenderedPartInsideThinking(renderedPart: IChatContentPart | undefined): boolean {
1408+
if (!renderedPart?.domNode) {
1409+
return false;
1410+
}
1411+
return !!dom.findParentWithClass(renderedPart.domNode, 'chat-thinking-box');
1412+
}
1413+
13971414
private hasCodeblockUri(part: IChatRendererContent): boolean {
13981415
if (part.kind !== 'markdownContent') {
13991416
return false;

0 commit comments

Comments
 (0)