From cf85805b340a9a9d0d10efd0d42a4de5dcf3239a Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Wed, 30 Jul 2025 16:14:59 -0700 Subject: [PATCH 1/2] Chat sessions empty message --- .../contrib/chat/browser/chatSessions.ts | 106 +++++++++++++++++- .../chat/browser/media/chatSessions.css | 19 ++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions.ts b/src/vs/workbench/contrib/chat/browser/chatSessions.ts index 08196c0d117..72166f2ab9c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions.ts @@ -34,7 +34,7 @@ import { FuzzyScore } from '../../../../base/common/filters.js'; import { ResourceLabels, IResourceLabel } from '../../../browser/labels.js'; import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; -import { append, $, getActiveWindow } from '../../../../base/browser/dom.js'; +import { append, $, getActiveWindow, clearNode } from '../../../../base/browser/dom.js'; import { URI } from '../../../../base/common/uri.js'; import { IEditorGroupsService, IEditorGroup } from '../../../services/editor/common/editorGroupsService.js'; import { GroupModelChangeKind } from '../../../common/editor.js'; @@ -673,6 +673,7 @@ class SessionsViewPane extends ViewPane { private treeContainer?: HTMLElement; private dataSource?: SessionsDataSource; private labels?: ResourceLabels; + private messageElement?: HTMLElement; constructor( private readonly provider: IChatSessionItemProvider, @@ -690,6 +691,7 @@ class SessionsViewPane extends ViewPane { @IViewsService private readonly viewsService: IViewsService, @ILogService private readonly logService: ILogService, @IProgressService private readonly progressService: IProgressService, + @IChatSessionsService private readonly chatSessionsService: IChatSessionsService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); @@ -713,6 +715,73 @@ class SessionsViewPane extends ViewPane { } } + private async getProviderDisplayName(): Promise { + // For local provider, return default name + if (this.provider.chatSessionType === 'local') { + return 'Local'; + } + + // For other providers, try to get a friendly name from the extension contributions + try { + const contributions = await this.chatSessionsService.getChatSessionContributions(); + const contribution = contributions.find(c => c.type === this.provider.chatSessionType); + if (contribution) { + return contribution.displayName; + } + } catch (error) { + // Fall back to the provider type if we can't get the display name + } + return this.provider.chatSessionType; + } + + private showEmptyMessage(): void { + if (!this.messageElement) { + return; + } + + // Only show message for non-local providers + if (this.provider.chatSessionType === 'local') { + this.hideMessage(); + return; + } + + this.getProviderDisplayName().then(providerName => { + if (!this.messageElement) { + return; + } + + const messageText = nls.localize('chatSessions.noResults', "No sessions found from {0}", providerName); + + // Clear the message element using DOM utility + clearNode(this.messageElement); + + const messageContainer = append(this.messageElement, $('.no-sessions-message')); + + append(messageContainer, $('.codicon.codicon-info')); + const textElement = append(messageContainer, $('span')); + textElement.textContent = messageText; + + // Show the message element + this.messageElement.style.display = 'block'; + + // Hide the tree + if (this.treeContainer) { + this.treeContainer.style.display = 'none'; + } + }); + } + + private hideMessage(): void { + if (this.messageElement) { + this.messageElement.style.display = 'none'; + } + + // Show the tree + if (this.treeContainer) { + this.treeContainer.style.display = 'block'; + } + } + /** * Refreshes the tree data with progress indication. * Shows a progress indicator while the tree updates its children from the provider. @@ -730,6 +799,21 @@ class SessionsViewPane extends ViewPane { }, async () => { await this.tree!.updateChildren(this.provider); + + // Check if we have any items and show/hide message accordingly + try { + const items = await this.provider.provideChatSessionItems(CancellationToken.None); + if (items.length === 0) { + this.showEmptyMessage(); + } else { + this.hideMessage(); + } + } catch (error) { + // On error, also show the empty message for non-local providers + if (this.provider.chatSessionType !== 'local') { + this.showEmptyMessage(); + } + } } ); } catch (error) { @@ -755,6 +839,21 @@ class SessionsViewPane extends ViewPane { }, async () => { await this.tree!.setInput(this.provider); + + // Check if we have any items and show/hide message accordingly + try { + const items = await this.provider.provideChatSessionItems(CancellationToken.None); + if (items.length === 0) { + this.showEmptyMessage(); + } else { + this.hideMessage(); + } + } catch (error) { + // On error, also show the empty message for non-local providers + if (this.provider.chatSessionType !== 'local') { + this.showEmptyMessage(); + } + } } ); } catch (error) { @@ -766,6 +865,10 @@ class SessionsViewPane extends ViewPane { protected override renderBody(container: HTMLElement): void { super.renderBody(container); + // Create message element for empty state + this.messageElement = append(container, $('.chat-sessions-message')); + this.messageElement.style.display = 'none'; + this.treeContainer = append(container, $('.chat-sessions-tree.show-file-icons')); this.treeContainer.classList.add('file-icon-themable-tree'); @@ -807,6 +910,7 @@ class SessionsViewPane extends ViewPane { // Handle double-click and keyboard selection to open editors this._register(this.tree.onDidOpen(async e => { const element = e.element as IChatSessionItem & { provider: IChatSessionItemProvider }; + if (element && this.isLocalChatSessionItem(element)) { if (element.sessionType === 'editor' && element.editor && element.group) { // Open the chat editor diff --git a/src/vs/workbench/contrib/chat/browser/media/chatSessions.css b/src/vs/workbench/contrib/chat/browser/media/chatSessions.css index b057f0e9e66..358d8d158fc 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatSessions.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatSessions.css @@ -21,3 +21,22 @@ margin-right: 4px; margin-left: 4px; } + +/* Style for empty state message */ +.chat-sessions-message { + padding: 20px; + text-align: center; + color: var(--vscode-descriptionForeground); +} + +.chat-sessions-message .no-sessions-message { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + font-style: italic; +} + +.chat-sessions-message .no-sessions-message .codicon { + opacity: 0.7; +} From 4c63eb1adcd4e8faee900280c3670fa97d859828 Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Wed, 30 Jul 2025 19:22:05 -0700 Subject: [PATCH 2/2] Simplified implementation --- .../contrib/chat/browser/chatSessions.ts | 115 ++++++++---------- 1 file changed, 51 insertions(+), 64 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions.ts b/src/vs/workbench/contrib/chat/browser/chatSessions.ts index 72166f2ab9c..0a6e831a882 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions.ts @@ -715,23 +715,13 @@ class SessionsViewPane extends ViewPane { } } - private async getProviderDisplayName(): Promise { - // For local provider, return default name - if (this.provider.chatSessionType === 'local') { - return 'Local'; + private getProviderDisplayName(): string { + const contributions = this.chatSessionsService.getChatSessionContributions(); + const contribution = contributions.find(c => c.type === this.provider.chatSessionType); + if (contribution) { + return contribution.displayName; } - - // For other providers, try to get a friendly name from the extension contributions - try { - const contributions = await this.chatSessionsService.getChatSessionContributions(); - const contribution = contributions.find(c => c.type === this.provider.chatSessionType); - if (contribution) { - return contribution.displayName; - } - } catch (error) { - // Fall back to the provider type if we can't get the display name - } - return this.provider.chatSessionType; + return ''; } private showEmptyMessage(): void { @@ -745,30 +735,29 @@ class SessionsViewPane extends ViewPane { return; } - this.getProviderDisplayName().then(providerName => { - if (!this.messageElement) { - return; - } + const providerName = this.getProviderDisplayName(); + if (!providerName) { + return; + } - const messageText = nls.localize('chatSessions.noResults', "No sessions found from {0}", providerName); + const messageText = nls.localize('chatSessions.noResults', "No sessions found from {0}", providerName); - // Clear the message element using DOM utility - clearNode(this.messageElement); + // Clear the message element using DOM utility + clearNode(this.messageElement); - const messageContainer = append(this.messageElement, $('.no-sessions-message')); + const messageContainer = append(this.messageElement, $('.no-sessions-message')); - append(messageContainer, $('.codicon.codicon-info')); - const textElement = append(messageContainer, $('span')); - textElement.textContent = messageText; + append(messageContainer, $('.codicon.codicon-info')); + const textElement = append(messageContainer, $('span')); + textElement.textContent = messageText; - // Show the message element - this.messageElement.style.display = 'block'; + // Show the message element + this.messageElement.style.display = 'block'; - // Hide the tree - if (this.treeContainer) { - this.treeContainer.style.display = 'none'; - } - }); + // Hide the tree + if (this.treeContainer) { + this.treeContainer.style.display = 'none'; + } } private hideMessage(): void { @@ -782,6 +771,28 @@ class SessionsViewPane extends ViewPane { } } + /** + * Updates the empty state message based on current tree data. + * Uses the tree's existing data to avoid redundant provider calls. + */ + private updateEmptyStateMessage(): void { + try { + // Check if the tree has the provider node and get its children count + if (this.tree?.hasNode(this.provider)) { + const providerNode = this.tree.getNode(this.provider); + const childCount = providerNode.children?.length || 0; + + if (childCount === 0) { + this.showEmptyMessage(); + } else { + this.hideMessage(); + } + } + } catch (error) { + this.logService.error('Error checking tree data for empty state:', error); + } + } + /** * Refreshes the tree data with progress indication. * Shows a progress indicator while the tree updates its children from the provider. @@ -799,23 +810,11 @@ class SessionsViewPane extends ViewPane { }, async () => { await this.tree!.updateChildren(this.provider); - - // Check if we have any items and show/hide message accordingly - try { - const items = await this.provider.provideChatSessionItems(CancellationToken.None); - if (items.length === 0) { - this.showEmptyMessage(); - } else { - this.hideMessage(); - } - } catch (error) { - // On error, also show the empty message for non-local providers - if (this.provider.chatSessionType !== 'local') { - this.showEmptyMessage(); - } - } } ); + + // Check for empty state after refresh using tree data + this.updateEmptyStateMessage(); } catch (error) { // Log error but don't throw to avoid breaking the UI this.logService.error('Error refreshing chat sessions tree:', error); @@ -839,23 +838,11 @@ class SessionsViewPane extends ViewPane { }, async () => { await this.tree!.setInput(this.provider); - - // Check if we have any items and show/hide message accordingly - try { - const items = await this.provider.provideChatSessionItems(CancellationToken.None); - if (items.length === 0) { - this.showEmptyMessage(); - } else { - this.hideMessage(); - } - } catch (error) { - // On error, also show the empty message for non-local providers - if (this.provider.chatSessionType !== 'local') { - this.showEmptyMessage(); - } - } } ); + + // Check for empty state after loading using tree data + this.updateEmptyStateMessage(); } catch (error) { // Log error but don't throw to avoid breaking the UI this.logService.error('Error loading chat sessions data:', error);