diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts index 6130d14dffb33..39a5873424bb9 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsViewer.ts @@ -41,6 +41,7 @@ import { renderAsPlaintext } from '../../../../../base/browser/markdownRenderer. import { MarkdownString, IMarkdownString } from '../../../../../base/common/htmlContent.js'; import { AgentSessionHoverWidget } from './agentSessionHoverWidget.js'; import { AgentSessionsGrouping } from './agentSessionsFilter.js'; +import { AgentSessionProviders } from './agentSessions.js'; export type AgentSessionListItem = IAgentSession | IAgentSessionSection; @@ -194,8 +195,20 @@ export class AgentSessionRenderer extends Disposable implements ICompressibleTre } template.diffContainer.classList.toggle('has-diff', hasDiff); - // TODO@lszomoru - Only show the "View All Changes" action if the changes are in an array. We have to revisit this - ChatContextKeys.hasAgentSessionChanges.bindTo(template.contextKeyService).set(Array.isArray(diff) && diff.length > 0); + let hasAgentSessionChanges = false; + if ( + session.element.providerType === AgentSessionProviders.Background || + session.element.providerType === AgentSessionProviders.Cloud + ) { + // Background and Cloud agents provide the list of changes directly, + // so we have to use the list of changes to determine whether to show + // the "View All Changes" action + hasAgentSessionChanges = Array.isArray(diff) && diff.length > 0; + } else { + hasAgentSessionChanges = hasDiff; + } + + ChatContextKeys.hasAgentSessionChanges.bindTo(template.contextKeyService).set(hasAgentSessionChanges); // Badge let hasBadge = false; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts index b344ca9aea7c9..de32e7a32af44 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts @@ -36,6 +36,7 @@ import { ChatAgentLocation, ChatConfiguration, ChatModeKind } from '../../common import { CHAT_CATEGORY } from '../actions/chatActions.js'; import { ChatTreeItem, IChatWidget, IChatWidgetService } from '../chat.js'; import { IAgentSession, isAgentSession } from '../agentSessions/agentSessionsModel.js'; +import { AgentSessionProviders } from '../agentSessions/agentSessions.js'; export abstract class EditingSessionAction extends Action2 { @@ -366,6 +367,7 @@ export class ViewAllSessionChangesAction extends Action2 { override async run(accessor: ServicesAccessor, sessionOrSessionResource?: URI | IAgentSession): Promise { const agentSessionsService = accessor.get(IAgentSessionsService); const commandService = accessor.get(ICommandService); + const chatEditingService = accessor.get(IChatEditingService); if (!URI.isUri(sessionOrSessionResource) && !isAgentSession(sessionOrSessionResource)) { return; @@ -377,16 +379,25 @@ export class ViewAllSessionChangesAction extends Action2 { const session = agentSessionsService.getSession(sessionResource); const changes = session?.changes; - if (!(changes instanceof Array)) { + + if (!session || !changes) { return; } - const resources = changes.map(d => ({ - originalUri: d.originalUri, - modifiedUri: d.modifiedUri - })); + if ( + session.providerType === AgentSessionProviders.Background || + session.providerType === AgentSessionProviders.Cloud + ) { + if (!Array.isArray(changes) || changes.length === 0) { + return; + } + + // Use agent session changes + const resources = changes.map(d => ({ + originalUri: d.originalUri, + modifiedUri: d.modifiedUri + })); - if (resources.length > 0) { await commandService.executeCommand('_workbench.openMultiDiffEditor', { multiDiffSourceUri: sessionResource.with({ scheme: sessionResource.scheme + '-worktree-changes' }), title: localize('chatEditing.allChanges.title', 'All Session Changes'), @@ -394,7 +405,13 @@ export class ViewAllSessionChangesAction extends Action2 { }); session?.setRead(true); + return; } + + // Use edit session changes + const editingSession = chatEditingService.getEditingSession(sessionResource); + await editingSession?.show(); + session?.setRead(true); } } registerAction2(ViewAllSessionChangesAction); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts index f0e0eddb75000..dcc111a18780e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts @@ -410,17 +410,30 @@ export class AcceptAllEditsAction extends ChatEditingEditorAction { abstract class MultiDiffAcceptDiscardAction extends Action2 { - constructor(readonly accept: boolean) { + constructor(private readonly _options: { readonly location: 'title' | 'content'; readonly accept: boolean }) { super({ - id: accept ? 'chatEditing.multidiff.acceptAllFiles' : 'chatEditing.multidiff.discardAllFiles', - title: accept ? localize('accept4', 'Keep All Edits') : localize('discard4', 'Undo All Edits'), - icon: accept ? Codicon.check : Codicon.discard, - menu: { - when: ContextKeyExpr.equals('resourceScheme', CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME), - id: MenuId.EditorTitle, - order: accept ? 0 : 1, - group: 'navigation', - }, + id: _options.location === 'title' + ? _options.accept ? 'chatEditing.multidiff.title.acceptAllFiles' : 'chatEditing.multidiff.title.discardAllFiles' + : _options.accept ? 'chatEditing.multidiff.content.acceptAllFiles' : 'chatEditing.multidiff.content.discardAllFiles', + title: _options.location === 'title' + ? _options.accept ? localize('accept4', 'Keep All Edits') : localize('discard4', 'Undo All Edits') + : _options.accept ? localize('accept5', 'Keep') : localize('discard5', 'Undo'), + icon: _options.location === 'title' + ? _options.accept ? Codicon.check : Codicon.discard + : undefined, + menu: _options.location === 'title' + ? { + when: ContextKeyExpr.equals('resourceScheme', CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME), + id: MenuId.EditorTitle, + order: _options.accept ? -100 : -99, + group: 'navigation', + } + : { + when: ContextKeyExpr.equals('resourceScheme', CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME), + id: MenuId.MultiDiffEditorContent, + order: _options.accept ? -100 : -99, + group: 'navigation' + } }); } @@ -445,7 +458,7 @@ abstract class MultiDiffAcceptDiscardAction extends Action2 { const { chatSessionResource } = parseChatMultiDiffUri(editor.resource); const session = chatEditingService.getEditingSession(chatSessionResource); if (session) { - if (this.accept) { + if (this._options.accept) { await session.accept(); } else { await session.reject(); @@ -603,8 +616,10 @@ export function registerChatEditorActions() { registerAction2(ToggleDiffAction); registerAction2(ToggleAccessibleDiffViewAction); - registerAction2(class extends MultiDiffAcceptDiscardAction { constructor() { super(true); } }); - registerAction2(class extends MultiDiffAcceptDiscardAction { constructor() { super(false); } }); + registerAction2(class extends MultiDiffAcceptDiscardAction { constructor() { super({ location: 'title', accept: true }); } }); + registerAction2(class extends MultiDiffAcceptDiscardAction { constructor() { super({ location: 'title', accept: false }); } }); + registerAction2(class extends MultiDiffAcceptDiscardAction { constructor() { super({ location: 'content', accept: true }); } }); + registerAction2(class extends MultiDiffAcceptDiscardAction { constructor() { super({ location: 'content', accept: false }); } }); registerAction2(ExplainMultiDiffAction); MenuRegistry.appendMenuItem(MenuId.ChatEditingEditorContent, { diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts index de55084d5825b..d202b99918b0d 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts @@ -784,14 +784,12 @@ export class ChatWidget extends Disposable implements IChatWidget { * Updates the DOM visibility of welcome view and chat list immediately */ private updateChatViewVisibility(): void { - if (!this.viewModel) { - return; + if (this.viewModel) { + const numItems = this.viewModel.getItems().length; + dom.setVisibility(numItems === 0, this.welcomeMessageContainer); + dom.setVisibility(numItems !== 0, this.listContainer); } - const numItems = this.viewModel.getItems().length; - dom.setVisibility(numItems === 0, this.welcomeMessageContainer); - dom.setVisibility(numItems !== 0, this.listContainer); - this._onDidChangeEmptyState.fire(); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletions.ts index 9cacb07ddfd39..a38f9781833bc 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/editor/chatInputCompletions.ts @@ -1243,7 +1243,6 @@ class ToolCompletions extends Disposable { documentation, insertText: withLeader + ' ', kind: CompletionItemKind.Tool, - sortText: 'z', }); } diff --git a/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts index 4481014f752c1..1c67d25dd54ca 100644 --- a/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/widgetHosts/viewPane/chatViewPane.ts @@ -461,9 +461,9 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { // Sessions control: stacked if (this.sessionsViewerOrientation === AgentSessionsViewerOrientation.Stacked) { newSessionsContainerVisible = - !!this.chatEntitlementService.sentiment.installed && // chat is installed (otherwise make room for terms and welcome) - (!this._widget || this._widget?.isEmpty()) && // chat widget empty - !this.welcomeController?.isShowingWelcome.get(); // welcome not showing + !!this.chatEntitlementService.sentiment.installed && // chat is installed (otherwise make room for terms and welcome) + (!this._widget || (this._widget.isEmpty() && !!this._widget.viewModel)) && // chat widget empty (but not when model is loading) + !this.welcomeController?.isShowingWelcome.get(); // welcome not showing } // Sessions control: sidebar