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
This commit is contained in:
Connor Peet
2026-03-06 15:24:05 -08:00
committed by GitHub
parent 57479c0e8a
commit 19b032d2ff
4 changed files with 51 additions and 8 deletions

View File

@@ -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);

View File

@@ -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
}],
});
}

View File

@@ -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<string>('chat.editRequests') === 'input';
this.inputPart?.setEditing(!!this.viewModel?.editing && isInput, isEditingSentRequest);

View File

@@ -23,7 +23,8 @@ export namespace ChatContextKeys {
export const enum EditingRequestType {
Sent = 's',
QueueOrSteer = 'qs',
Queue = 'q',
Steer = 'st',
}
export const editingRequestType = new RawContextKey<EditingRequestType | undefined>('chatEditingSentRequest', undefined, { type: 'string', description: localize('chatEditingSentRequest', "The type of the current editing request.") });