diff --git a/src/vs/workbench/api/browser/mainThreadChatSessions.ts b/src/vs/workbench/api/browser/mainThreadChatSessions.ts index fff57e96b30..edb808986f9 100644 --- a/src/vs/workbench/api/browser/mainThreadChatSessions.ts +++ b/src/vs/workbench/api/browser/mainThreadChatSessions.ts @@ -6,6 +6,7 @@ import { raceCancellationError } from '../../../base/common/async.js'; import { CancellationToken } from '../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../base/common/event.js'; +import { IMarkdownString, MarkdownString } from '../../../base/common/htmlContent.js'; import { Disposable, DisposableMap, DisposableStore, IDisposable } from '../../../base/common/lifecycle.js'; import { revive } from '../../../base/common/marshalling.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; @@ -87,7 +88,8 @@ export class MainThreadChatSessions extends Disposable implements MainThreadChat return sessions.map(session => ({ ...session, id: session.id, - iconPath: session.iconPath ? this._reviveIconPath(session.iconPath) : undefined + iconPath: session.iconPath ? this._reviveIconPath(session.iconPath) : undefined, + tooltip: session.tooltip ? this._reviveTooltip(session.tooltip) : undefined })); } catch (error) { this._logService.error('Error providing chat sessions:', error); @@ -307,6 +309,24 @@ export class MainThreadChatSessions extends Disposable implements MainThreadChat return undefined; } + private _reviveTooltip(tooltip: string | IMarkdownString | undefined): string | MarkdownString | undefined { + if (!tooltip) { + return undefined; + } + + // If it's already a string, return as-is + if (typeof tooltip === 'string') { + return tooltip; + } + + // If it's a serialized IMarkdownString, revive it to MarkdownString + if (typeof tooltip === 'object' && 'value' in tooltip) { + return MarkdownString.lift(tooltip); + } + + return undefined; + } + async $showChatSession(chatSessionType: string, sessionId: string, position: EditorGroupColumn | undefined): Promise { const sessionUri = ChatSessionUri.forSession(chatSessionType, sessionId); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 3a97d98f1eb..c3503baaaef 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1867,6 +1867,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ChatResponseTurn2: extHostTypes.ChatResponseTurn2, ChatToolInvocationPart: extHostTypes.ChatToolInvocationPart, ChatLocation: extHostTypes.ChatLocation, + ChatSessionStatus: extHostTypes.ChatSessionStatus, ChatRequestEditorData: extHostTypes.ChatRequestEditorData, ChatRequestNotebookData: extHostTypes.ChatRequestNotebookData, ChatReferenceBinaryData: extHostTypes.ChatReferenceBinaryData, diff --git a/src/vs/workbench/api/common/extHostChatSessions.ts b/src/vs/workbench/api/common/extHostChatSessions.ts index b56178412a2..2def43f4f60 100644 --- a/src/vs/workbench/api/common/extHostChatSessions.ts +++ b/src/vs/workbench/api/common/extHostChatSessions.ts @@ -11,7 +11,7 @@ import { MarshalledId } from '../../../base/common/marshallingIds.js'; import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { ILogService } from '../../../platform/log/common/log.js'; import { IChatAgentRequest, IChatAgentResult } from '../../contrib/chat/common/chatAgents.js'; -import { IChatSessionItem } from '../../contrib/chat/common/chatSessionsService.js'; +import { IChatSessionItem, ChatSessionStatus } from '../../contrib/chat/common/chatSessionsService.js'; import { ChatAgentLocation } from '../../contrib/chat/common/constants.js'; import { Proxied } from '../../services/extensions/common/proxyIdentifier.js'; import { ChatSessionDto, ExtHostChatSessionsShape, IChatAgentProgressShape, MainContext, MainThreadChatSessionsShape } from './extHost.protocol.js'; @@ -128,6 +128,22 @@ export class ExtHostChatSessions extends Disposable implements ExtHostChatSessio await this._proxy.$showChatSession(chatSessionType, sessionId, typeConvert.ViewColumn.from(options?.viewColumn)); } + private convertChatSessionStatus(status: vscode.ChatSessionStatus | undefined): ChatSessionStatus | undefined { + if (status === undefined) { + return undefined; + } + switch (status) { + case 0: // vscode.ChatSessionStatus.Failed + return ChatSessionStatus.Failed; + case 1: // vscode.ChatSessionStatus.Completed + return ChatSessionStatus.Completed; + case 2: // vscode.ChatSessionStatus.InProgress + return ChatSessionStatus.InProgress; + default: + return undefined; + } + } + async $provideChatSessionItems(handle: number, token: vscode.CancellationToken): Promise { const entry = this._chatSessionItemProviders.get(handle); if (!entry) { @@ -150,7 +166,10 @@ export class ExtHostChatSessions extends Disposable implements ExtHostChatSessio response.push({ id: sessionContent.id, label: sessionContent.label, - iconPath: sessionContent.iconPath + iconPath: sessionContent.iconPath, + description: sessionContent.description, + status: this.convertChatSessionStatus(sessionContent.status), + tooltip: typeConvert.MarkdownString.fromStrict(sessionContent.tooltip) }); } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 328e184142a..cce0d4b72c9 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4853,6 +4853,12 @@ export enum ChatLocation { Editor = 4, } +export enum ChatSessionStatus { + Failed = 0, + Completed = 1, + InProgress = 2 +} + export enum ChatResponseReferencePartStatusKind { Complete = 1, Partial = 2, diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions.ts b/src/vs/workbench/contrib/chat/browser/chatSessions.ts index 3e966a8c3dd..bf1fd0581f9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions.ts @@ -16,6 +16,7 @@ import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.j import { MarshalledId } from '../../../../base/common/marshallingIds.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; +import { isMarkdownString } from '../../../../base/common/htmlContent.js'; import * as nls from '../../../../nls.js'; import { getActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenuService, MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; @@ -48,9 +49,9 @@ import { IEditorGroup, IEditorGroupsService } from '../../../services/editor/com import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js'; +import { IChatSessionItem, IChatSessionItemProvider, IChatSessionsExtensionPoint, IChatSessionsService, ChatSessionStatus } from '../common/chatSessionsService.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; -import { IChatSessionItem, IChatSessionItemProvider, IChatSessionsExtensionPoint, IChatSessionsService } from '../common/chatSessionsService.js'; import { ChatSessionUri } from '../common/chatUri.js'; import { ChatAgentLocation, ChatConfiguration } from '../common/constants.js'; import { IChatWidget, IChatWidgetService } from './chat.js'; @@ -81,6 +82,7 @@ interface ILocalChatSessionItem extends IChatSessionItem { widget?: IChatWidget; sessionType: 'editor' | 'widget'; description?: string; + status?: ChatSessionStatus; } export class ChatSessionsView extends Disposable implements IWorkbenchContribution { @@ -548,14 +550,23 @@ class SessionsDataSource implements IAsyncDataSource { +class SessionsDelegate implements IListVirtualDelegate { static readonly ITEM_HEIGHT = 22; + static readonly ITEM_HEIGHT_WITH_DESCRIPTION = 38; // Slightly smaller for cleaner look - getHeight(element: IChatSessionItem): number { - return SessionsDelegate.ITEM_HEIGHT; + getHeight(element: ChatSessionItemWithProvider): number { + // Check if element has a non-empty description + const hasDescription = 'description' in element && + typeof element.description === 'string' && + element.description.trim().length > 0; + + // Only give taller height to non-local sessions with descriptions + const isLocalSession = element.provider.chatSessionType === 'local'; + + return hasDescription && !isLocalSession ? SessionsDelegate.ITEM_HEIGHT_WITH_DESCRIPTION : SessionsDelegate.ITEM_HEIGHT; } - getTemplateId(element: IChatSessionItem): string { + getTemplateId(element: ChatSessionItemWithProvider): string { return SessionsRenderer.TEMPLATE_ID; } } @@ -636,8 +647,11 @@ class SessionsRenderer extends Disposable implements ITreeRenderer .label-description { + margin-left: 0 !important; + margin-top: 0; + opacity: 0.6; + font-size: 0.9em; + color: var(--vscode-descriptionForeground); + white-space: normal !important; + word-wrap: break-word; + line-height: 1.3; display: block; } diff --git a/src/vs/workbench/contrib/chat/common/chatSessionsService.ts b/src/vs/workbench/contrib/chat/common/chatSessionsService.ts index d8582519b1d..a86d73815dd 100644 --- a/src/vs/workbench/contrib/chat/common/chatSessionsService.ts +++ b/src/vs/workbench/contrib/chat/common/chatSessionsService.ts @@ -12,6 +12,13 @@ import { ThemeIcon } from '../../../../base/common/themables.js'; import { IChatProgress } from './chatService.js'; import { IChatAgentRequest } from './chatAgents.js'; import { IRelaxedExtensionDescription } from '../../../../platform/extensions/common/extensions.js'; +import { IMarkdownString } from '../../../../base/common/htmlContent.js'; + +export const enum ChatSessionStatus { + Failed = 0, + Completed = 1, + InProgress = 2 +} export interface IChatSessionsExtensionPoint { readonly id: string; @@ -23,13 +30,15 @@ export interface IChatSessionsExtensionPoint { readonly when?: string; } export interface IChatSessionItem { - id: string; label: string; iconPath?: URI | { light: URI; dark: URI; } | ThemeIcon; + description?: string | IMarkdownString; + status?: ChatSessionStatus; + tooltip?: string | IMarkdownString; } export interface ChatSession extends IDisposable { diff --git a/src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts b/src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts index a1045ff47b1..fcb48e72b89 100644 --- a/src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.chatSessionsProvider.d.ts @@ -4,6 +4,26 @@ *--------------------------------------------------------------------------------------------*/ declare module 'vscode' { + /** + * Represents the status of a chat session. + */ + export enum ChatSessionStatus { + /** + * The chat session failed to complete. + */ + Failed = 0, + + /** + * The chat session completed successfully. + */ + Completed = 1, + + /** + * The chat session is currently in progress. + */ + InProgress = 2 + } + /** * Provides a list of information about chat sessions. */ @@ -46,6 +66,21 @@ declare module 'vscode' { * An icon for the participant shown in UI. */ iconPath?: IconPath; + + /** + * An optional description that provides additional context about the chat session. + */ + description?: string | MarkdownString; + + /** + * An optional status indicating the current state of the session. + */ + status?: ChatSessionStatus; + + /** + * The tooltip text when you hover over this item. + */ + tooltip?: string | MarkdownString; } export interface ChatSession {