Agent sessions: special rendering of PR links (fix #281643) (#283037)

This commit is contained in:
Benjamin Pasero
2025-12-12 15:16:32 +01:00
committed by GitHub
parent fddfa4d646
commit 9ba40f8204
6 changed files with 72 additions and 24 deletions
@@ -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: {
@@ -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,
@@ -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<IAgentSes
h('span.agent-session-diff-added@addedSpan'),
h('span.agent-session-diff-removed@removedSpan')
]),
h('div.agent-session-badge@badge'),
h('div.agent-session-description@description'),
h('div.agent-session-status@status')
])
@@ -130,6 +132,7 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer<IAgentSes
diffFilesSpan: elements.filesSpan,
diffAddedSpan: elements.addedSpan,
diffRemovedSpan: elements.removedSpan,
badge: elements.badge,
description: elements.description,
status: elements.status,
contextKeyService,
@@ -145,6 +148,7 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer<IAgentSes
template.diffFilesSpan.textContent = '';
template.diffAddedSpan.textContent = '';
template.diffRemovedSpan.textContent = '';
template.badge.textContent = '';
template.description.textContent = '';
// Archived
@@ -164,17 +168,20 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer<IAgentSes
template.titleToolbar.context = session.element;
// Diff information
let hasDiff = false;
const { changes: diff } = session.element;
if (!isSessionInProgressStatus(session.element.status) && diff && hasValidDiff(diff)) {
if (this.renderDiff(session, template)) {
template.diffContainer.classList.add('has-diff');
hasDiff = true;
}
}
template.diffContainer.classList.toggle('has-diff', hasDiff);
// Description otherwise
else {
template.diffContainer.classList.remove('has-diff');
// Badge
this.renderBadge(session, template);
// Description (unless diff is shown)
if (!hasDiff) {
this.renderDescription(session, template);
}
@@ -185,6 +192,31 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer<IAgentSes
this.renderHover(session, template);
}
private renderBadge(session: ITreeNode<IAgentSession, FuzzyScore>, 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<IAgentSession, FuzzyScore>, template: IAgentSessionItemTemplate): boolean {
const diff = getAgentChangesSummary(session.element.changes);
if (!diff) {
@@ -229,21 +261,7 @@ export class AgentSessionRenderer implements ICompressibleTreeRenderer<IAgentSes
private renderDescription(session: ITreeNode<IAgentSession, FuzzyScore>, 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
@@ -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,
@@ -66,6 +66,7 @@ export interface IChatSessionItem {
resource: URI;
label: string;
iconPath?: ThemeIcon;
badge?: string | IMarkdownString;
description?: string | IMarkdownString;
status?: ChatSessionStatus;
tooltip?: string | IMarkdownString;
@@ -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.
*/