Skip to content

Commit 533e889

Browse files
Copilotosortega
andauthored
Add description and status properties to ChatSessionItem interface (microsoft#261292)
* Initial plan * Add description and status properties to ChatSessionItem interface Co-authored-by: osortega <48293249+osortega@users.noreply.github.com> * Update extHostChatSessions to pass through new description and status properties Co-authored-by: osortega <48293249+osortega@users.noreply.github.com> * Two line * Support for tooltip * API update --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: osortega <48293249+osortega@users.noreply.github.com> Co-authored-by: Osvaldo Ortega <osortega@microsoft.com>
1 parent 6959b99 commit 533e889

File tree

8 files changed

+190
-12
lines changed

8 files changed

+190
-12
lines changed

src/vs/workbench/api/browser/mainThreadChatSessions.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { raceCancellationError } from '../../../base/common/async.js';
77
import { CancellationToken } from '../../../base/common/cancellation.js';
88
import { Emitter, Event } from '../../../base/common/event.js';
9+
import { IMarkdownString, MarkdownString } from '../../../base/common/htmlContent.js';
910
import { Disposable, DisposableMap, DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
1011
import { revive } from '../../../base/common/marshalling.js';
1112
import { URI, UriComponents } from '../../../base/common/uri.js';
@@ -87,7 +88,8 @@ export class MainThreadChatSessions extends Disposable implements MainThreadChat
8788
return sessions.map(session => ({
8889
...session,
8990
id: session.id,
90-
iconPath: session.iconPath ? this._reviveIconPath(session.iconPath) : undefined
91+
iconPath: session.iconPath ? this._reviveIconPath(session.iconPath) : undefined,
92+
tooltip: session.tooltip ? this._reviveTooltip(session.tooltip) : undefined
9193
}));
9294
} catch (error) {
9395
this._logService.error('Error providing chat sessions:', error);
@@ -307,6 +309,24 @@ export class MainThreadChatSessions extends Disposable implements MainThreadChat
307309
return undefined;
308310
}
309311

312+
private _reviveTooltip(tooltip: string | IMarkdownString | undefined): string | MarkdownString | undefined {
313+
if (!tooltip) {
314+
return undefined;
315+
}
316+
317+
// If it's already a string, return as-is
318+
if (typeof tooltip === 'string') {
319+
return tooltip;
320+
}
321+
322+
// If it's a serialized IMarkdownString, revive it to MarkdownString
323+
if (typeof tooltip === 'object' && 'value' in tooltip) {
324+
return MarkdownString.lift(tooltip);
325+
}
326+
327+
return undefined;
328+
}
329+
310330
async $showChatSession(chatSessionType: string, sessionId: string, position: EditorGroupColumn | undefined): Promise<void> {
311331
const sessionUri = ChatSessionUri.forSession(chatSessionType, sessionId);
312332

src/vs/workbench/api/common/extHost.api.impl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
18671867
ChatResponseTurn2: extHostTypes.ChatResponseTurn2,
18681868
ChatToolInvocationPart: extHostTypes.ChatToolInvocationPart,
18691869
ChatLocation: extHostTypes.ChatLocation,
1870+
ChatSessionStatus: extHostTypes.ChatSessionStatus,
18701871
ChatRequestEditorData: extHostTypes.ChatRequestEditorData,
18711872
ChatRequestNotebookData: extHostTypes.ChatRequestNotebookData,
18721873
ChatReferenceBinaryData: extHostTypes.ChatReferenceBinaryData,

src/vs/workbench/api/common/extHostChatSessions.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { MarshalledId } from '../../../base/common/marshallingIds.js';
1111
import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
1212
import { ILogService } from '../../../platform/log/common/log.js';
1313
import { IChatAgentRequest, IChatAgentResult } from '../../contrib/chat/common/chatAgents.js';
14-
import { IChatSessionItem } from '../../contrib/chat/common/chatSessionsService.js';
14+
import { IChatSessionItem, ChatSessionStatus } from '../../contrib/chat/common/chatSessionsService.js';
1515
import { ChatAgentLocation } from '../../contrib/chat/common/constants.js';
1616
import { Proxied } from '../../services/extensions/common/proxyIdentifier.js';
1717
import { ChatSessionDto, ExtHostChatSessionsShape, IChatAgentProgressShape, MainContext, MainThreadChatSessionsShape } from './extHost.protocol.js';
@@ -128,6 +128,22 @@ export class ExtHostChatSessions extends Disposable implements ExtHostChatSessio
128128
await this._proxy.$showChatSession(chatSessionType, sessionId, typeConvert.ViewColumn.from(options?.viewColumn));
129129
}
130130

131+
private convertChatSessionStatus(status: vscode.ChatSessionStatus | undefined): ChatSessionStatus | undefined {
132+
if (status === undefined) {
133+
return undefined;
134+
}
135+
switch (status) {
136+
case 0: // vscode.ChatSessionStatus.Failed
137+
return ChatSessionStatus.Failed;
138+
case 1: // vscode.ChatSessionStatus.Completed
139+
return ChatSessionStatus.Completed;
140+
case 2: // vscode.ChatSessionStatus.InProgress
141+
return ChatSessionStatus.InProgress;
142+
default:
143+
return undefined;
144+
}
145+
}
146+
131147
async $provideChatSessionItems(handle: number, token: vscode.CancellationToken): Promise<IChatSessionItem[]> {
132148
const entry = this._chatSessionItemProviders.get(handle);
133149
if (!entry) {
@@ -150,7 +166,10 @@ export class ExtHostChatSessions extends Disposable implements ExtHostChatSessio
150166
response.push({
151167
id: sessionContent.id,
152168
label: sessionContent.label,
153-
iconPath: sessionContent.iconPath
169+
iconPath: sessionContent.iconPath,
170+
description: sessionContent.description,
171+
status: this.convertChatSessionStatus(sessionContent.status),
172+
tooltip: typeConvert.MarkdownString.fromStrict(sessionContent.tooltip)
154173
});
155174
}
156175
}

src/vs/workbench/api/common/extHostTypes.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4853,6 +4853,12 @@ export enum ChatLocation {
48534853
Editor = 4,
48544854
}
48554855

4856+
export enum ChatSessionStatus {
4857+
Failed = 0,
4858+
Completed = 1,
4859+
InProgress = 2
4860+
}
4861+
48564862
export enum ChatResponseReferencePartStatusKind {
48574863
Complete = 1,
48584864
Partial = 2,

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

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.j
1616
import { MarshalledId } from '../../../../base/common/marshallingIds.js';
1717
import { ThemeIcon } from '../../../../base/common/themables.js';
1818
import { URI } from '../../../../base/common/uri.js';
19+
import { isMarkdownString } from '../../../../base/common/htmlContent.js';
1920
import * as nls from '../../../../nls.js';
2021
import { getActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
2122
import { IMenuService, MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js';
@@ -48,9 +49,9 @@ import { IEditorGroup, IEditorGroupsService } from '../../../services/editor/com
4849
import { IEditorService } from '../../../services/editor/common/editorService.js';
4950
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
5051
import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js';
52+
import { IChatSessionItem, IChatSessionItemProvider, IChatSessionsExtensionPoint, IChatSessionsService, ChatSessionStatus } from '../common/chatSessionsService.js';
5153
import { IViewsService } from '../../../services/views/common/viewsService.js';
5254
import { ChatContextKeys } from '../common/chatContextKeys.js';
53-
import { IChatSessionItem, IChatSessionItemProvider, IChatSessionsExtensionPoint, IChatSessionsService } from '../common/chatSessionsService.js';
5455
import { ChatSessionUri } from '../common/chatUri.js';
5556
import { ChatAgentLocation, ChatConfiguration } from '../common/constants.js';
5657
import { IChatWidget, IChatWidgetService } from './chat.js';
@@ -81,6 +82,7 @@ interface ILocalChatSessionItem extends IChatSessionItem {
8182
widget?: IChatWidget;
8283
sessionType: 'editor' | 'widget';
8384
description?: string;
85+
status?: ChatSessionStatus;
8486
}
8587

8688
export class ChatSessionsView extends Disposable implements IWorkbenchContribution {
@@ -548,14 +550,23 @@ class SessionsDataSource implements IAsyncDataSource<IChatSessionItemProvider, C
548550
}
549551

550552
// Tree delegate for session items
551-
class SessionsDelegate implements IListVirtualDelegate<IChatSessionItem> {
553+
class SessionsDelegate implements IListVirtualDelegate<ChatSessionItemWithProvider> {
552554
static readonly ITEM_HEIGHT = 22;
555+
static readonly ITEM_HEIGHT_WITH_DESCRIPTION = 38; // Slightly smaller for cleaner look
553556

554-
getHeight(element: IChatSessionItem): number {
555-
return SessionsDelegate.ITEM_HEIGHT;
557+
getHeight(element: ChatSessionItemWithProvider): number {
558+
// Check if element has a non-empty description
559+
const hasDescription = 'description' in element &&
560+
typeof element.description === 'string' &&
561+
element.description.trim().length > 0;
562+
563+
// Only give taller height to non-local sessions with descriptions
564+
const isLocalSession = element.provider.chatSessionType === 'local';
565+
566+
return hasDescription && !isLocalSession ? SessionsDelegate.ITEM_HEIGHT_WITH_DESCRIPTION : SessionsDelegate.ITEM_HEIGHT;
556567
}
557568

558-
getTemplateId(element: IChatSessionItem): string {
569+
getTemplateId(element: ChatSessionItemWithProvider): string {
559570
return SessionsRenderer.TEMPLATE_ID;
560571
}
561572
}
@@ -636,8 +647,11 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
636647

637648
renderTemplate(container: HTMLElement): ISessionTemplateData {
638649
const element = append(container, $('.chat-session-item'));
639-
const resourceLabel = this.labels.create(element, { supportHighlights: true });
640-
const actionsContainer = append(resourceLabel.element, $('.actions'));
650+
651+
// Create a container that holds both the label and actions
652+
const contentContainer = append(element, $('.session-content'));
653+
const resourceLabel = this.labels.create(contentContainer, { supportHighlights: true });
654+
const actionsContainer = append(contentContainer, $('.actions'));
641655
const actionBar = new ActionBar(actionsContainer);
642656
const elementDisposable = new DisposableStore();
643657

@@ -656,6 +670,13 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
656670
// Clear previous element disposables
657671
templateData.elementDisposable.clear();
658672

673+
// Add CSS class for local sessions
674+
if (sessionWithProvider.provider.chatSessionType === 'local') {
675+
templateData.container.classList.add('local-session');
676+
} else {
677+
templateData.container.classList.remove('local-session');
678+
}
679+
659680
// Handle different icon types
660681
let iconResource: URI | undefined;
661682
let iconTheme: ThemeIcon | undefined;
@@ -688,7 +709,14 @@ class SessionsRenderer extends Disposable implements ITreeRenderer<IChatSessionI
688709
resource: iconResource
689710
}, {
690711
fileKind: undefined,
691-
icon: iconTheme || iconUri
712+
icon: iconTheme || iconUri,
713+
title: 'tooltip' in session && session.tooltip ?
714+
(typeof session.tooltip === 'string' ? session.tooltip :
715+
isMarkdownString(session.tooltip) ? {
716+
markdown: session.tooltip,
717+
markdownNotSupportedFallback: session.tooltip.value
718+
} : undefined) :
719+
undefined
692720
});
693721

694722
// Create context overlay for this specific session item

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,22 @@
4141
opacity: 0.7;
4242
}
4343

44+
/* Position session content and actions inline */
45+
.chat-sessions-tree .chat-session-item .session-content {
46+
display: flex;
47+
justify-content: space-between;
48+
align-items: center;
49+
width: 100%;
50+
min-height: 22px;
51+
}
52+
53+
.chat-sessions-tree .chat-session-item .actions {
54+
margin-left: 8px;
55+
display: flex;
56+
align-items: center;
57+
flex-shrink: 0;
58+
}
59+
4460
/* Hide action bar actions by default */
4561
.chat-sessions-tree .chat-session-item .actions .action-label {
4662
display: none;
@@ -50,5 +66,49 @@
5066
.chat-sessions-tree .chat-session-item:hover .actions .action-label,
5167
.chat-sessions-tree .monaco-list-row.focused .chat-session-item .actions .action-label,
5268
.chat-sessions-tree .monaco-list-row.selected .chat-session-item .actions .action-label {
69+
display: flex;
70+
align-items: center;
71+
}
72+
73+
/* For items with descriptions, keep the structure but adjust alignment */
74+
.chat-sessions-tree .chat-session-item:not(.local-session) .session-content {
75+
align-items: flex-start;
76+
padding-top: 2px;
77+
padding-bottom: 2px;
78+
}
79+
80+
/* Ensure resource label takes up available space */
81+
.chat-sessions-tree .chat-session-item .monaco-icon-label {
82+
flex: 1;
83+
min-width: 0; /* Allow text to truncate */
84+
}
85+
86+
/* Multi-line description support */
87+
.chat-sessions-tree .chat-session-item:not(.local-session) .monaco-icon-label-container {
88+
flex-direction: column !important;
89+
align-items: flex-start !important;
90+
gap: 1px;
91+
}
92+
93+
.chat-sessions-tree .chat-session-item:not(.local-session) .monaco-icon-name-container {
94+
display: flex;
95+
align-items: center;
96+
width: 100%;
97+
}
98+
99+
.chat-sessions-tree .chat-session-item:not(.local-session) .monaco-icon-description-container {
100+
width: 100%;
101+
padding-left: 0;
102+
}
103+
104+
.chat-sessions-tree .chat-session-item:not(.local-session) .monaco-icon-description-container > .label-description {
105+
margin-left: 0 !important;
106+
margin-top: 0;
107+
opacity: 0.6;
108+
font-size: 0.9em;
109+
color: var(--vscode-descriptionForeground);
110+
white-space: normal !important;
111+
word-wrap: break-word;
112+
line-height: 1.3;
53113
display: block;
54114
}

src/vs/workbench/contrib/chat/common/chatSessionsService.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ import { ThemeIcon } from '../../../../base/common/themables.js';
1212
import { IChatProgress } from './chatService.js';
1313
import { IChatAgentRequest } from './chatAgents.js';
1414
import { IRelaxedExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
15+
import { IMarkdownString } from '../../../../base/common/htmlContent.js';
16+
17+
export const enum ChatSessionStatus {
18+
Failed = 0,
19+
Completed = 1,
20+
InProgress = 2
21+
}
1522

1623
export interface IChatSessionsExtensionPoint {
1724
readonly id: string;
@@ -23,13 +30,15 @@ export interface IChatSessionsExtensionPoint {
2330
readonly when?: string;
2431
}
2532
export interface IChatSessionItem {
26-
2733
id: string;
2834
label: string;
2935
iconPath?: URI | {
3036
light: URI;
3137
dark: URI;
3238
} | ThemeIcon;
39+
description?: string | IMarkdownString;
40+
status?: ChatSessionStatus;
41+
tooltip?: string | IMarkdownString;
3342
}
3443

3544
export interface ChatSession extends IDisposable {

src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
declare module 'vscode' {
7+
/**
8+
* Represents the status of a chat session.
9+
*/
10+
export enum ChatSessionStatus {
11+
/**
12+
* The chat session failed to complete.
13+
*/
14+
Failed = 0,
15+
16+
/**
17+
* The chat session completed successfully.
18+
*/
19+
Completed = 1,
20+
21+
/**
22+
* The chat session is currently in progress.
23+
*/
24+
InProgress = 2
25+
}
26+
727
/**
828
* Provides a list of information about chat sessions.
929
*/
@@ -46,6 +66,21 @@ declare module 'vscode' {
4666
* An icon for the participant shown in UI.
4767
*/
4868
iconPath?: IconPath;
69+
70+
/**
71+
* An optional description that provides additional context about the chat session.
72+
*/
73+
description?: string | MarkdownString;
74+
75+
/**
76+
* An optional status indicating the current state of the session.
77+
*/
78+
status?: ChatSessionStatus;
79+
80+
/**
81+
* The tooltip text when you hover over this item.
82+
*/
83+
tooltip?: string | MarkdownString;
4984
}
5085

5186
export interface ChatSession {

0 commit comments

Comments
 (0)