diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index d1afbd4af69..3ad329eff37 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -18,6 +18,7 @@ import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_IN_CHAT_INPUT, CONTEXT_IN_CHAT_SESSION, CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_ERROR, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE, CONTEXT_VOTE_UP_ENABLED } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatService, ChatAgentVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { isRequestVM, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { MENU_INLINE_CHAT_WIDGET_SECONDARY } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellEditType, CellKind, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; @@ -33,12 +34,17 @@ export function registerChatTitleActions() { category: CHAT_CATEGORY, icon: Codicon.thumbsup, toggled: CONTEXT_RESPONSE_VOTE.isEqualTo('up'), - menu: { + menu: [{ id: MenuId.ChatMessageTitle, group: 'navigation', order: 1, when: ContextKeyExpr.and(CONTEXT_RESPONSE, CONTEXT_VOTE_UP_ENABLED, CONTEXT_RESPONSE_ERROR.negate()) - } + }, { + id: MENU_INLINE_CHAT_WIDGET_SECONDARY, + group: 'navigation', + order: 1, + when: ContextKeyExpr.and(CONTEXT_RESPONSE, CONTEXT_VOTE_UP_ENABLED, CONTEXT_RESPONSE_ERROR.negate()) + }] }); } @@ -73,12 +79,17 @@ export function registerChatTitleActions() { category: CHAT_CATEGORY, icon: Codicon.thumbsdown, toggled: CONTEXT_RESPONSE_VOTE.isEqualTo('down'), - menu: { + menu: [{ id: MenuId.ChatMessageTitle, group: 'navigation', order: 2, when: ContextKeyExpr.and(CONTEXT_RESPONSE, CONTEXT_RESPONSE_ERROR.negate()) - } + }, { + id: MENU_INLINE_CHAT_WIDGET_SECONDARY, + group: 'navigation', + order: 2, + when: ContextKeyExpr.and(CONTEXT_RESPONSE, CONTEXT_RESPONSE_ERROR.negate()) + }] }); } @@ -112,12 +123,17 @@ export function registerChatTitleActions() { f1: false, category: CHAT_CATEGORY, icon: Codicon.report, - menu: { + menu: [{ id: MenuId.ChatMessageTitle, group: 'navigation', order: 3, when: ContextKeyExpr.and(CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_RESPONSE) - } + }, { + id: MENU_INLINE_CHAT_WIDGET_SECONDARY, + group: 'navigation', + order: 3, + when: ContextKeyExpr.and(CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_RESPONSE) + }] }); } diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 15e2584f498..bffc94d794e 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -43,7 +43,7 @@ import { isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IVoiceChatService, VoiceChatInProgress as GlobalVoiceChatInProgress } from 'vs/workbench/contrib/chat/common/voiceChatService'; import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { CTX_INLINE_CHAT_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_FOCUSED, MENU_INLINE_CHAT_WIDGET_SECONDARY } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { HasSpeechProvider, ISpeechService, KeywordRecognitionStatus, SpeechToTextInProgress, SpeechToTextStatus, TextToSpeechStatus, TextToSpeechInProgress as GlobalTextToSpeechInProgress } from 'vs/workbench/contrib/speech/common/speechService'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -877,7 +877,7 @@ export class ReadChatResponseAloud extends Action2 { title: localize2('workbench.action.chat.readChatResponseAloud', "Read Aloud"), icon: Codicon.unmute, precondition: CanVoiceChat, - menu: { + menu: [{ id: MenuId.ChatMessageTitle, when: ContextKeyExpr.and( CanVoiceChat, @@ -886,7 +886,16 @@ export class ReadChatResponseAloud extends Action2 { CONTEXT_RESPONSE_FILTERED.negate() // and not when response is filtered ), group: 'navigation' - } + }, { + id: MENU_INLINE_CHAT_WIDGET_SECONDARY, + when: ContextKeyExpr.and( + CanVoiceChat, + CONTEXT_RESPONSE, // only for responses + ScopedChatSynthesisInProgress.negate(), // but not when already in progress + CONTEXT_RESPONSE_FILTERED.negate() // and not when response is filtered + ), + group: 'navigation' + }] }); } @@ -932,6 +941,12 @@ export class StopReadAloud extends Action2 { when: ScopedChatSynthesisInProgress, group: 'navigation', order: -1 + }, + { + id: MENU_INLINE_CHAT_WIDGET_SECONDARY, + when: ScopedChatSynthesisInProgress, + group: 'navigation', + order: -1 } ] }); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts index dce09a65403..5e70e522748 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -107,8 +107,6 @@ registerAction2(InlineChatActions.ViewInChatAction); registerAction2(InlineChatActions.ToggleDiffForChange); registerAction2(InlineChatActions.AcceptChanges); -registerAction2(InlineChatActions.ReportIssueAction); - const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(InlineChatNotebookContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index a9b72ce72ed..8119cdd08fa 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -11,7 +11,7 @@ import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/em import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_EDIT_MODE, EditMode, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, MENU_INLINE_CHAT_CONTENT_STATUS, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, MENU_INLINE_CHAT_ZONE, CTX_INLINE_CHAT_SUPPORT_REPORT_ISSUE, ACTION_REPORT_ISSUE } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_EDIT_MODE, EditMode, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, MENU_INLINE_CHAT_CONTENT_STATUS, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, MENU_INLINE_CHAT_ZONE } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { localize, localize2 } from 'vs/nls'; import { Action2, IAction2Options } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -516,28 +516,3 @@ export class ToggleDiffForChange extends AbstractInlineChatAction { ctrl.toggleDiff(hunkInfo); } } - -export class ReportIssueAction extends AbstractInlineChatAction { - constructor() { - super({ - id: ACTION_REPORT_ISSUE, - title: localize2('reportIssue', 'Report Issue'), - icon: Codicon.report, - menu: [{ - id: MENU_INLINE_CHAT_WIDGET_STATUS, - group: '0_main', - order: 6, - when: ContextKeyExpr.and( - CTX_INLINE_CHAT_VISIBLE, - CTX_INLINE_CHAT_SUPPORT_REPORT_ISSUE, - CTX_INLINE_CHAT_RESPONSE_TYPE.notEqualsTo(InlineChatResponseType.None), - CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.negate() - ) - }] - }); - } - - override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController): void { - ctrl.reportIssue(); - } -} diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index bf21a01d17f..29b4dee9719 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -42,13 +42,14 @@ import { InlineChatContentWidget } from 'vs/workbench/contrib/inlineChat/browser import { HunkInformation, Session, StashedSession } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { InlineChatError } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl'; import { EditModeStrategy, HunkAction, IEditObserver, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; -import { CTX_INLINE_CHAT_EDITING, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_SUPPORT_REPORT_ISSUE, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_EDITING, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { IInlineChatSavingService } from './inlineChatSavingService'; import { IInlineChatSessionService } from './inlineChatSessionService'; import { InlineChatZoneWidget } from './inlineChatZoneWidget'; +import { CONTEXT_RESPONSE, CONTEXT_RESPONSE_ERROR } from 'vs/workbench/contrib/chat/common/chatContextKeys'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -113,7 +114,8 @@ export class InlineChatController implements IEditorContribution { private readonly _ctxResponseType: IContextKey; private readonly _ctxUserDidEdit: IContextKey; private readonly _ctxRequestInProgress: IContextKey; - private readonly _ctxSupportsReportIssue: IContextKey; + + private readonly _ctxResponse: IContextKey; private readonly _messages = this._store.add(new Emitter()); protected readonly _onDidEnterState = this._store.add(new Emitter()); @@ -154,7 +156,9 @@ export class InlineChatController implements IEditorContribution { this._ctxUserDidEdit = CTX_INLINE_CHAT_USER_DID_EDIT.bindTo(contextKeyService); this._ctxResponseType = CTX_INLINE_CHAT_RESPONSE_TYPE.bindTo(contextKeyService); this._ctxRequestInProgress = CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.bindTo(contextKeyService); - this._ctxSupportsReportIssue = CTX_INLINE_CHAT_SUPPORT_REPORT_ISSUE.bindTo(contextKeyService); + + this._ctxResponse = CONTEXT_RESPONSE.bindTo(contextKeyService); + CONTEXT_RESPONSE_ERROR.bindTo(contextKeyService); this._ui = new Lazy(() => { @@ -487,6 +491,8 @@ export class InlineChatController implements IEditorContribution { const diff = await this._editorWorkerService.computeDiff(this._session.textModel0.uri, this._session.textModelN.uri, { computeMoves: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, ignoreTrimWhitespace: false }, 'advanced'); this._session.wholeRange.fixup(diff?.changes ?? []); await this._session.hunkData.recompute(editState, diff); + + this._updateCtxResponseType(); } options.position = await this._strategy.renderChanges(); @@ -575,8 +581,6 @@ export class InlineChatController implements IEditorContribution { assertType(request); assertType(request.response); - this._ctxSupportsReportIssue.set(request.response.agent?.metadata.supportIssueReporting ?? false); - this._showWidget(this._session.headless, false); this._ui.value.zone.widget.selectAll(false); this._ui.value.zone.widget.updateInfo(''); @@ -928,6 +932,7 @@ export class InlineChatController implements IEditorContribution { } } this._ctxResponseType.set(responseType); + this._ctxResponse.set(responseType !== InlineChatResponseType.None); } private _createChatTextEditGroupState(): IChatTextEditGroupState { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 7a572e77396..c44f6e6ee7e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -21,7 +21,6 @@ import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, IValidEditOpera import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel'; -import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Progress } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/common/editor'; @@ -592,9 +591,6 @@ export class LiveStrategy extends EditModeStrategy { if (widgetData) { this._zone.updatePositionAndHeight(widgetData.position); - const remainingHunks = this._session.hunkData.pending; - this._updateSummaryMessage(remainingHunks, this._session.hunkData.size); - const mode = this._configService.getValue<'on' | 'off' | 'auto'>(InlineChatConfigKeys.AccessibleDiffView); if (mode === 'on' || mode === 'auto' && this._accessibilityService.isScreenReaderOptimized()) { @@ -625,30 +621,6 @@ export class LiveStrategy extends EditModeStrategy { return renderHunks()?.position; } - private _updateSummaryMessage(remaining: number, total: number) { - - const needsReview = this._configService.getValue(InlineChatConfigKeys.AcceptedOrDiscardBeforeSave); - let message: string; - if (total === 0) { - message = localize('change.0', "Nothing changed."); - } else if (remaining === 1) { - message = needsReview - ? localize('review.1', "$(info) Accept or Discard change") - : localize('change.1', "1 change"); - } else { - message = needsReview - ? localize('review.N', "$(info) Accept or Discard {0} changes", remaining) - : localize('change.N', "{0} changes", total); - } - - let title: string | undefined; - if (needsReview) { - title = localize('review', "Review (accept or discard) all changes before continuing"); - } - - this._zone.widget.updateStatus(message, { title }); - } - hasFocus(): boolean { return this._zone.widget.hasFocus(); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 4204a0b8146..aceeaf1b635 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -7,7 +7,7 @@ import { Dimension, getActiveElement, getTotalHeight, h, reset, trackFocus } fro import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { Emitter, Event } from 'vs/base/common/event'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; -import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ISettableObservable, constObservable, derived, observableValue } from 'vs/base/common/observable'; import 'vs/css!./media/inlineChat'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -41,11 +41,13 @@ import { chatRequestBackground } from 'vs/workbench/contrib/chat/common/chatColo import { Selection } from 'vs/editor/common/core/selection'; import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; import { isNonEmptyArray, tail } from 'vs/base/common/arrays'; -import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +import { ChatAgentVoteDirection, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IHoverService } from 'vs/platform/hover/browser/hover'; import { IChatWidgetViewOptions } from 'vs/workbench/contrib/chat/browser/chat'; +import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING, CONTEXT_RESPONSE, CONTEXT_RESPONSE_ERROR, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; export interface InlineChatWidgetViewState { @@ -61,6 +63,8 @@ export interface IInlineChatWidgetConstructionOptions { */ statusMenuId: MenuId | { menu: MenuId; options: IWorkbenchButtonBarOptions }; + secondaryMenuId?: MenuId; + /** * The options for the chat widget */ @@ -87,8 +91,9 @@ export class InlineChatWidget { h('div.accessibleViewer@accessibleViewer'), h('div.status@status', [ h('div.label.info.hidden@infoLabel'), - h('div.actions.button-style.hidden@toolbar2'), + h('div.actions.button-style.hidden@toolbar1'), h('div.label.status.hidden@statusLabel'), + h('div.actions.button-style.hidden@toolbar2'), ]), ] ); @@ -176,13 +181,43 @@ export class InlineChatWidget { this._chatWidget.setVisible(true); this._store.add(this._chatWidget); + const ctxResponse = CONTEXT_RESPONSE.bindTo(this.scopedContextKeyService); + const ctxResponseVote = CONTEXT_RESPONSE_VOTE.bindTo(this.scopedContextKeyService); + const ctxResponseSupportIssues = CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING.bindTo(this.scopedContextKeyService); + const ctxResponseError = CONTEXT_RESPONSE_ERROR.bindTo(this.scopedContextKeyService); + const ctxResponseErrorFiltered = CONTEXT_RESPONSE_FILTERED.bindTo(this.scopedContextKeyService); + const viewModelStore = this._store.add(new DisposableStore()); this._store.add(this._chatWidget.onDidChangeViewModel(() => { viewModelStore.clear(); + const viewModel = this._chatWidget.viewModel; - if (viewModel) { - viewModelStore.add(viewModel.onDidChange(() => this._onDidChangeHeight.fire())); + if (!viewModel) { + return; } + + viewModelStore.add(toDisposable(() => { + toolbar2.context = undefined; + ctxResponse.reset(); + ctxResponseVote.reset(); + ctxResponseError.reset(); + ctxResponseErrorFiltered.reset(); + ctxResponseSupportIssues.reset(); + })); + + viewModelStore.add(viewModel.onDidChange(() => { + + const last = viewModel.getItems().at(-1); + toolbar2.context = last; + + ctxResponse.set(isResponseVM(last)); + ctxResponseVote.set(isResponseVM(last) ? last.vote === ChatAgentVoteDirection.Down ? 'down' : last.vote === ChatAgentVoteDirection.Up ? 'up' : '' : ''); + ctxResponseError.set(isResponseVM(last) && last.errorDetails !== undefined); + ctxResponseErrorFiltered.set((!!(isResponseVM(last) && last.errorDetails?.responseIsFiltered))); + ctxResponseSupportIssues.set(isResponseVM(last) && (last.agent?.metadata.supportIssueReporting ?? false)); + + this._onDidChangeHeight.fire(); + })); this._onDidChangeHeight.fire(); })); @@ -204,7 +239,7 @@ export class InlineChatWidget { // BUTTON bar const statusMenuOptions = options.statusMenuId instanceof MenuId ? undefined : options.statusMenuId.options; - const statusButtonBar = scopedInstaService.createInstance(MenuWorkbenchButtonBar, this._elements.toolbar2, statusMenuId, { + const statusButtonBar = scopedInstaService.createInstance(MenuWorkbenchButtonBar, this._elements.toolbar1, statusMenuId, { toolbarOptions: { primaryGroup: '0_main' }, telemetrySource: options.chatWidgetViewOptions?.menus?.telemetrySource, menuOptions: { renderShortTitle: true }, @@ -213,6 +248,14 @@ export class InlineChatWidget { this._store.add(statusButtonBar.onDidChange(() => this._onDidChangeHeight.fire())); this._store.add(statusButtonBar); + // secondary toolbar + const toolbar2 = scopedInstaService.createInstance(MenuWorkbenchToolBar, this._elements.toolbar2, options.secondaryMenuId ?? MenuId.for(''), { + telemetrySource: options.chatWidgetViewOptions?.menus?.telemetrySource, + menuOptions: { renderShortTitle: true, shouldForwardArgs: true }, + }); + this._store.add(toolbar2.onDidChangeMenuItems(() => this._onDidChangeHeight.fire())); + this._store.add(toolbar2); + this._store.add(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(AccessibilityVerbositySettingId.InlineChat)) { @@ -361,6 +404,7 @@ export class InlineChatWidget { } toggleStatus(show: boolean) { + this._elements.toolbar1.classList.toggle('hidden', !show); this._elements.toolbar2.classList.toggle('hidden', !show); this._elements.status.classList.toggle('hidden', !show); this._elements.infoLabel.classList.toggle('hidden', !show); @@ -369,6 +413,7 @@ export class InlineChatWidget { updateToolbar(show: boolean) { this._elements.root.classList.toggle('toolbar', show); + this._elements.toolbar1.classList.toggle('hidden', !show); this._elements.toolbar2.classList.toggle('hidden', !show); this._elements.status.classList.toggle('actions', show); this._elements.infoLabel.classList.toggle('hidden', show); @@ -490,6 +535,7 @@ export class InlineChatWidget { reset(this._elements.statusLabel); this._elements.statusLabel.classList.toggle('hidden', true); + this._elements.toolbar1.classList.add('hidden'); this._elements.toolbar2.classList.add('hidden'); this.updateInfo(''); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts index 4c0c898b5e5..1ce7b490450 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts @@ -14,7 +14,7 @@ import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/browser/zoneWidget'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ACTION_REGENERATE_RESPONSE, ACTION_REPORT_ISSUE, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, EditMode, InlineChatConfigKeys, MENU_INLINE_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { ACTION_REGENERATE_RESPONSE, ACTION_REPORT_ISSUE, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, EditMode, InlineChatConfigKeys, MENU_INLINE_CHAT_WIDGET_SECONDARY, MENU_INLINE_CHAT_WIDGET_STATUS } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { EditorBasedInlineChatWidget } from './inlineChatWidget'; import { isEqual } from 'vs/base/common/resources'; import { StableEditorBottomScrollState } from 'vs/editor/browser/stableEditorScroll'; @@ -62,6 +62,7 @@ export class InlineChatZoneWidget extends ZoneWidget { } } }, + secondaryMenuId: MENU_INLINE_CHAT_WIDGET_SECONDARY, chatWidgetViewOptions: { menus: { executeToolbar: MenuId.ChatExecute, diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index 4381be9c9e8..f209158f8fe 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -60,6 +60,7 @@ .monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list .interactive-item-container.minimal > .header { top: 5px; right: 10px; + display: none; } @@ -91,6 +92,7 @@ .monaco-workbench .inline-chat .status .label.status { margin-left: auto; + padding: 0 6px; } .monaco-workbench .inline-chat .status .label.hidden, diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 9587b823c3c..d656e6c3f25 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -113,8 +113,6 @@ export const CTX_INLINE_CHAT_EDIT_MODE = new RawContextKey('config.inl export const CTX_INLINE_CHAT_REQUEST_IN_PROGRESS = new RawContextKey('inlineChatRequestInProgress', false, localize('inlineChatRequestInProgress', "Whether an inline chat request is currently in progress")); export const CTX_INLINE_CHAT_RESPONSE_TYPE = new RawContextKey('inlineChatResponseType', InlineChatResponseType.None, localize('inlineChatResponseTypes', "What type was the responses have been receieved, nothing yet, just messages, or messaged and local edits")); -export const CTX_INLINE_CHAT_SUPPORT_REPORT_ISSUE = new RawContextKey('inlineChatSupportReportIssue', false, localize('inlineChatSupportReportIssue', "Whether inline chat supports reporting issues")); - // --- (selected) action identifier export const ACTION_ACCEPT_CHANGES = 'inlineChat.acceptChanges'; @@ -127,6 +125,7 @@ export const ACTION_REPORT_ISSUE = 'inlineChat.reportIssue'; export const MENU_INLINE_CHAT_CONTENT_STATUS = MenuId.for('inlineChat.content.status'); export const MENU_INLINE_CHAT_WIDGET_STATUS = MenuId.for('inlineChatWidget.status'); +export const MENU_INLINE_CHAT_WIDGET_SECONDARY = MenuId.for('inlineChatWidget.secondary'); export const MENU_INLINE_CHAT_ZONE = MenuId.for('inlineChatWidget.changesZone'); // --- colors