From 19b032d2ff68faf05f34599076d30b97d534d940 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 6 Mar 2026 15:24:05 -0800 Subject: [PATCH] chat: toggle queue/steer keybindings based on context (#299885) * chat: toggle queue/steer keybindings based on context - Splits EditingRequestType.QueueOrSteer into Queue and Steer to track which type of pending message is being edited - Keybindings now respect the chat.requestQueuing.defaultAction setting: when steer is default, Enter=Steer and Alt+Enter=Queue; when queue is default, the bindings swap - When editing a queued or steer message, Enter always submits with the same type, regardless of the config setting. This ensures pressing Enter to save an edit keeps the message in its original queue category - Updates chatWidget to set the specific editing type based on the pending message's kind - Simplifies keybinding logic with effectiveDefault conditions that account for both config and editing context Fixes #297454 (Commit message generated by Copilot) * pr comments --- .../browser/actions/chatExecuteActions.ts | 3 +- .../chat/browser/actions/chatQueueActions.ts | 47 +++++++++++++++++-- .../contrib/chat/browser/widget/chatWidget.ts | 6 ++- .../chat/common/actions/chatContextKeys.ts | 3 +- 4 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts index b1d898bbcb1..907a1145b99 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts @@ -744,7 +744,8 @@ export class ChatEditingSessionSubmitAction extends SubmitAction { constructor() { const notInProgressOrEditing = ContextKeyExpr.and( ContextKeyExpr.or(whenNotInProgress, ChatContextKeys.editingRequestType.isEqualTo(ChatContextKeys.EditingRequestType.Sent)), - ChatContextKeys.editingRequestType.notEqualsTo(ChatContextKeys.EditingRequestType.QueueOrSteer) + ChatContextKeys.editingRequestType.notEqualsTo(ChatContextKeys.EditingRequestType.Queue), + ChatContextKeys.editingRequestType.notEqualsTo(ChatContextKeys.EditingRequestType.Steer) ); const menuCondition = ChatContextKeys.chatModeKind.notEqualsTo(ChatModeKind.Ask); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatQueueActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatQueueActions.ts index 0ad56d59669..3eac2679afa 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatQueueActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatQueueActions.ts @@ -13,15 +13,34 @@ import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contex import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { ChatContextKeys } from '../../common/actions/chatContextKeys.js'; import { ChatRequestQueueKind, IChatService } from '../../common/chatService/chatService.js'; +import { ChatConfiguration } from '../../common/constants.js'; import { isRequestVM } from '../../common/model/chatViewModel.js'; import { IChatWidgetService } from '../chat.js'; import { CHAT_CATEGORY } from './chatActions.js'; +const editingQueue = ChatContextKeys.editingRequestType.isEqualTo(ChatContextKeys.EditingRequestType.Queue); +const editingSteer = ChatContextKeys.editingRequestType.isEqualTo(ChatContextKeys.EditingRequestType.Steer); +const editingQueueOrSteer = ContextKeyExpr.or(editingQueue, editingSteer)!; + const queuingActionsPresent = ContextKeyExpr.and( - ContextKeyExpr.or(ChatContextKeys.requestInProgress, ChatContextKeys.editingRequestType.isEqualTo(ChatContextKeys.EditingRequestType.QueueOrSteer)), + ContextKeyExpr.or(ChatContextKeys.requestInProgress, editingQueueOrSteer), ChatContextKeys.editingRequestType.notEqualsTo(ChatContextKeys.EditingRequestType.Sent), ); +const steerIsDefault = ContextKeyExpr.equals(`config.${ChatConfiguration.RequestQueueingDefaultAction}`, 'steer'); +const queueIsDefault = steerIsDefault.negate(); + +// The effective default respects the editing context: when editing a queued/steer +// message, the default matches that message type regardless of the config setting. +const effectiveDefaultIsQueue = ContextKeyExpr.or( + ContextKeyExpr.and(queueIsDefault, editingQueueOrSteer.negate()), + editingQueue +); +const effectiveDefaultIsSteer = ContextKeyExpr.or( + ContextKeyExpr.and(steerIsDefault, editingQueueOrSteer.negate()), + editingSteer +); + export interface IChatRemovePendingRequestContext { sessionResource: URI; pendingRequestId: string; @@ -52,14 +71,23 @@ export class ChatQueueMessageAction extends Action2 { queuingActionsPresent, ChatContextKeys.inputHasText, ), - keybinding: { + keybinding: [{ when: ContextKeyExpr.and( ChatContextKeys.inChatInput, queuingActionsPresent, + effectiveDefaultIsSteer, ), primary: KeyMod.Alt | KeyCode.Enter, weight: KeybindingWeight.EditorContrib + 1 - }, + }, { + when: ContextKeyExpr.and( + ChatContextKeys.inChatInput, + queuingActionsPresent, + effectiveDefaultIsQueue, + ), + primary: KeyCode.Enter, + weight: KeybindingWeight.EditorContrib + 1 + }], }); } @@ -94,14 +122,23 @@ export class ChatSteerWithMessageAction extends Action2 { queuingActionsPresent, ChatContextKeys.inputHasText, ), - keybinding: { + keybinding: [{ when: ContextKeyExpr.and( ChatContextKeys.inChatInput, queuingActionsPresent, + effectiveDefaultIsSteer, ), primary: KeyCode.Enter, weight: KeybindingWeight.EditorContrib + 1 - }, + }, { + when: ContextKeyExpr.and( + ChatContextKeys.inChatInput, + queuingActionsPresent, + effectiveDefaultIsQueue, + ), + primary: KeyMod.Alt | KeyCode.Enter, + weight: KeybindingWeight.EditorContrib + 1 + }], }); } diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts index a26b7b4efb6..81716d9c338 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatWidget.ts @@ -1542,7 +1542,11 @@ export class ChatWidget extends Disposable implements IChatWidget { ChatContextKeys.currentlyEditing.bindTo(item.contextKeyService).set(true); } - const isEditingSentRequest = currentElement.pendingKind === undefined ? ChatContextKeys.EditingRequestType.Sent : ChatContextKeys.EditingRequestType.QueueOrSteer; + const isEditingSentRequest = currentElement.pendingKind === undefined + ? ChatContextKeys.EditingRequestType.Sent + : currentElement.pendingKind === ChatRequestQueueKind.Queued + ? ChatContextKeys.EditingRequestType.Queue + : ChatContextKeys.EditingRequestType.Steer; const isInput = this.configurationService.getValue('chat.editRequests') === 'input'; this.inputPart?.setEditing(!!this.viewModel?.editing && isInput, isEditingSentRequest); diff --git a/src/vs/workbench/contrib/chat/common/actions/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/actions/chatContextKeys.ts index 33f7a4a9f7a..7b5018b7f44 100644 --- a/src/vs/workbench/contrib/chat/common/actions/chatContextKeys.ts +++ b/src/vs/workbench/contrib/chat/common/actions/chatContextKeys.ts @@ -23,7 +23,8 @@ export namespace ChatContextKeys { export const enum EditingRequestType { Sent = 's', - QueueOrSteer = 'qs', + Queue = 'q', + Steer = 'st', } export const editingRequestType = new RawContextKey('chatEditingSentRequest', undefined, { type: 'string', description: localize('chatEditingSentRequest', "The type of the current editing request.") });