diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 3c196acf221..cc30b06e5d7 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -586,7 +586,7 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel setAgent(agent: IChatAgentData, slashCommand?: IChatAgentCommand) { this._agent = agent; this._slashCommand = slashCommand; - this._agentOrSlashCommandDetected = !agent.isDefault; + this._agentOrSlashCommandDetected = !agent.isDefault || !!slashCommand; this._onDidChange.fire(); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index c947e3ed856..9060badbfad 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { $, Dimension, getActiveElement, getTotalHeight, h, reset, trackFocus } from '../../../../base/browser/dom.js'; +import { renderFormattedText } from '../../../../base/browser/formattedTextRenderer.js'; import { IActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; @@ -48,7 +49,8 @@ import { ChatWidget, IChatViewState, IChatWidgetLocationOptions } from '../../ch import { chatRequestBackground } from '../../chat/common/chatColors.js'; import { ChatContextKeys } from '../../chat/common/chatContextKeys.js'; import { IChatModel } from '../../chat/common/chatModel.js'; -import { ChatAgentVoteDirection, IChatService } from '../../chat/common/chatService.js'; +import { chatSubcommandLeader } from '../../chat/common/chatParserTypes.js'; +import { ChatAgentVoteDirection, IChatSendRequestOptions, IChatService } from '../../chat/common/chatService.js'; import { isResponseVM } from '../../chat/common/chatViewModel.js'; import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONSE_FOCUSED, inlineChatBackground, inlineChatForeground } from '../common/inlineChat.js'; import { HunkInformation, Session } from './inlineChatSession.js'; @@ -88,6 +90,7 @@ export class InlineChatWidget { h('div.status@status', [ h('div.label.info.hidden@infoLabel'), h('div.actions.hidden@toolbar1'), + h('div.rerun@rerun'), h('div.label.status.hidden@statusLabel'), h('div.actions.secondary.hidden@toolbar2'), ]), @@ -198,6 +201,8 @@ export class InlineChatWidget { ctxResponseSupportIssues.reset(); })); + const detectedAgentCmdStore = viewModelStore.add(new DisposableStore()); + viewModelStore.add(viewModel.onDidChange(() => { this._requestInProgress.set(viewModel.requestInProgress, undefined); @@ -211,6 +216,37 @@ export class InlineChatWidget { ctxResponseErrorFiltered.set((!!(isResponseVM(last) && last.errorDetails?.responseIsFiltered))); ctxResponseSupportIssues.set(isResponseVM(last) && (last.agent?.metadata.supportIssueReporting ?? false)); + if (isResponseVM(last) && last.agentOrSlashCommandDetected && last.slashCommand) { + this._elements.rerun.innerText = last.slashCommand.name; + this._elements.rerun.title = last.slashCommand.description; + + const msg = localize('usedAgentSlashCommand', "``{0}`` [[(rerun without)]]", `${chatSubcommandLeader}${last.slashCommand.name}`); + + reset(this._elements.rerun, renderFormattedText(msg, { + className: 'agentOrSlashCommandDetected', + inline: true, + renderCodeSegments: true, + actionHandler: { + disposables: detectedAgentCmdStore, + callback: (content) => { + const request = this._chatService.getSession(last.sessionId)?.getRequests().find(candidate => candidate.id === last.requestId); + if (request) { + const options: IChatSendRequestOptions = { + noCommandDetection: true, + attempt: request.attempt + 1, + location: location.location, + userSelectedModelId: this.chatWidget.input.currentLanguageModel + }; + this._chatService.resendRequest(request, options); + } + }, + } + })); + + } else { + reset(this._elements.rerun); + } + this._onDidChangeHeight.fire(); })); this._onDidChangeHeight.fire(); @@ -479,6 +515,7 @@ export class InlineChatWidget { this._chatWidget.saveState(); reset(this._elements.statusLabel); + reset(this._elements.rerun); this._elements.statusLabel.classList.toggle('hidden', true); this._elements.toolbar1.classList.add('hidden'); this._elements.toolbar2.classList.add('hidden'); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts index 07734037b8d..267dd8dd858 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts @@ -106,7 +106,7 @@ export class InlineChatZoneWidget extends ZoneWidget { revealFn ??= this._createZoneAndScrollRestoreFn(this.position); const height = this._computeHeight(); this._relayout(height.linesValue); - revealFn(); + revealFn?.(); revealFn = undefined; } })); diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index 9cb1eccb91d..e2c67b0c97d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -141,7 +141,6 @@ .monaco-workbench .inline-chat > .status { display: flex; - justify-content: space-between; align-items: center; padding-right: 16px; } @@ -195,6 +194,24 @@ line-height: 18px; } +.monaco-workbench .inline-chat .status .rerun { + display: inline-flex; +} + +.monaco-workbench .inline-chat .status .rerun:not(:empty) { + padding-top: 8px; + padding-left: 4px; +} + +.monaco-workbench .inline-chat .status .rerun .agentOrSlashCommandDetected A { + cursor: pointer; + color: var(--vscode-textLink-foreground); +} + +.monaco-workbench .inline-chat .interactive-item-container.interactive-response .detail-container .detail .agentOrSlashCommandDetected, +.monaco-workbench .inline-chat .interactive-item-container.interactive-response .detail-container .chat-animated-ellipsis { + display: none; +} .monaco-workbench .inline-chat .status .actions, .monaco-workbench .inline-chat-diff-overlay { @@ -238,6 +255,7 @@ } .monaco-workbench .inline-chat .status .actions.secondary { + margin-left: auto; display: none; }