From 9ba40f8204f1b4cb092f9585b19ab3f26d0a588c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 12 Dec 2025 15:16:32 +0100 Subject: [PATCH] Agent sessions: special rendering of PR links (fix #281643) (#283037) --- .../api/common/extHostChatSessions.ts | 1 + .../agentSessions/agentSessionsModel.ts | 2 + .../agentSessions/agentSessionsViewer.ts | 58 ++++++++++++------- .../media/agentsessionsviewer.css | 29 ++++++++-- .../chat/common/chatSessionsService.ts | 1 + .../vscode.proposed.chatSessionsProvider.d.ts | 5 ++ 6 files changed, 72 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/api/common/extHostChatSessions.ts b/src/vs/workbench/api/common/extHostChatSessions.ts index 5cb108be803..1ee8852ffc4 100644 --- a/src/vs/workbench/api/common/extHostChatSessions.ts +++ b/src/vs/workbench/api/common/extHostChatSessions.ts @@ -181,6 +181,7 @@ export class ExtHostChatSessions extends Disposable implements ExtHostChatSessio resource: sessionContent.resource, label: sessionContent.label, description: sessionContent.description ? typeConvert.MarkdownString.from(sessionContent.description) : undefined, + badge: sessionContent.badge ? typeConvert.MarkdownString.from(sessionContent.badge) : undefined, status: this.convertChatSessionStatus(sessionContent.status), tooltip: typeConvert.MarkdownString.fromStrict(sessionContent.tooltip), timing: { diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsModel.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsModel.ts index 2c0ddee1c9a..639dd6bac8a 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsModel.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsModel.ts @@ -47,6 +47,7 @@ interface IAgentSessionData { readonly label: string; readonly description?: string | IMarkdownString; + readonly badge?: string | IMarkdownString; readonly icon: ThemeIcon; readonly timing: { @@ -346,6 +347,7 @@ export class AgentSessionsModel extends Disposable implements IAgentSessionsMode label: session.label, description: session.description, icon, + badge: session.badge, tooltip: session.tooltip, status, archived: session.archived, diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts index cc5d42676e7..5a7d098284a 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts @@ -39,7 +39,7 @@ import { ChatContextKeys } from '../../common/chatContextKeys.js'; import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; import { Event } from '../../../../../base/common/event.js'; import { renderAsPlaintext } from '../../../../../base/browser/markdownRenderer.js'; -import { MarkdownString } from '../../../../../base/common/htmlContent.js'; +import { MarkdownString, IMarkdownString } from '../../../../../base/common/htmlContent.js'; interface IAgentSessionItemTemplate { readonly element: HTMLElement; @@ -57,6 +57,7 @@ interface IAgentSessionItemTemplate { readonly diffAddedSpan: HTMLSpanElement; readonly diffRemovedSpan: HTMLSpanElement; + readonly badge: HTMLElement; readonly description: HTMLElement; readonly status: HTMLElement; @@ -106,6 +107,7 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer, template: IAgentSessionItemTemplate): void { + const badge = session.element.badge; + template.badge.classList.toggle('has-badge', !!badge); + + if (badge) { + this.renderMarkdownOrText(badge, template.badge, template.elementDisposable); + } + } + + private renderMarkdownOrText(content: string | IMarkdownString, container: HTMLElement, disposables: DisposableStore): void { + if (typeof content === 'string') { + container.textContent = content; + } else { + disposables.add(this.markdownRendererService.render(content, { + sanitizerConfig: { + replaceWithPlaintext: true, + allowedTags: { + override: allowedChatMarkdownHtmlTags, + }, + allowedLinkSchemes: { augment: [this.productService.urlProtocol] } + }, + }, container)); + } + } + private renderDiff(session: ITreeNode, template: IAgentSessionItemTemplate): boolean { const diff = getAgentChangesSummary(session.element.changes); if (!diff) { @@ -229,21 +261,7 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer, template: IAgentSessionItemTemplate): void { const description = session.element.description; if (description) { - - // Support description as string - if (typeof description === 'string') { - template.description.textContent = description; - } else { - template.elementDisposable.add(this.markdownRendererService.render(description, { - sanitizerConfig: { - replaceWithPlaintext: true, - allowedTags: { - override: allowedChatMarkdownHtmlTags, - }, - allowedLinkSchemes: { augment: [this.productService.urlProtocol] } - }, - }, template.description)); - } + this.renderMarkdownOrText(description, template.description, template.elementDisposable); } // Fallback to state label diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/media/agentsessionsviewer.css b/src/vs/workbench/contrib/chat/browser/agentSessions/media/agentsessionsviewer.css index c87f0390d75..b6d7f4a9280 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/media/agentsessionsviewer.css +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/media/agentsessionsviewer.css @@ -32,9 +32,15 @@ color: unset; } } + + .agent-session-badge { + background-color: unset; + outline: 1px solid var(--vscode-agentSessionSelectedBadge-border); + } } - .monaco-list:not(:focus) .monaco-list-row.selected .agent-session-details-row .agent-session-diff-container { + .monaco-list:not(:focus) .monaco-list-row.selected .agent-session-details-row .agent-session-diff-container, + .monaco-list:not(:focus) .monaco-list-row.selected .agent-session-details-row .agent-session-badge { outline: 1px solid var(--vscode-agentSessionSelectedUnfocusedBadge-border); } @@ -62,9 +68,7 @@ .agent-session-item { display: flex; flex-direction: row; - padding: 8px 12px - /* to offset from possible scrollbar */ - 8px 8px; + padding: 8px 12px /* to offset from possible scrollbar */ 8px 8px; &.archived { color: var(--vscode-descriptionForeground); @@ -123,6 +127,7 @@ } .agent-session-details-row { + gap: 4px; font-size: 12px; color: var(--vscode-descriptionForeground); @@ -167,6 +172,22 @@ color: var(--vscode-chat-linesRemovedForeground); } } + + .agent-session-badge { + background-color: var(--vscode-toolbar-hoverBackground); + font-weight: 500; + padding: 0 4px; + font-variant-numeric: tabular-nums; + border-radius: 5px; + + &:not(.has-badge) { + display: none; + } + + .codicon { + font-size: 14px; + } + } } .agent-session-title, diff --git a/src/vs/workbench/contrib/chat/common/chatSessionsService.ts b/src/vs/workbench/contrib/chat/common/chatSessionsService.ts index 3168345d0e8..2b171d247f4 100644 --- a/src/vs/workbench/contrib/chat/common/chatSessionsService.ts +++ b/src/vs/workbench/contrib/chat/common/chatSessionsService.ts @@ -66,6 +66,7 @@ export interface IChatSessionItem { resource: URI; label: string; iconPath?: ThemeIcon; + badge?: string | IMarkdownString; description?: string | IMarkdownString; status?: ChatSessionStatus; tooltip?: string | IMarkdownString; diff --git a/src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts b/src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts index bd4e624430f..772fc387b98 100644 --- a/src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts @@ -95,6 +95,11 @@ declare module 'vscode' { */ description?: string | MarkdownString; + /** + * An optional badge that provides additional context about the chat session. + */ + badge?: string | MarkdownString; + /** * An optional status indicating the current state of the session. */