44 *--------------------------------------------------------------------------------------------*/
55
66import { Codicon } from '../../../../../base/common/codicons.js' ;
7+ import { KeyCode , KeyMod } from '../../../../../base/common/keyCodes.js' ;
78import { URI } from '../../../../../base/common/uri.js' ;
8- import { localize2 } from '../../../../../nls.js' ;
9+ import { localize , localize2 } from '../../../../../nls.js' ;
910import { Action2 , MenuId , MenuRegistry , registerAction2 } from '../../../../../platform/actions/common/actions.js' ;
1011import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js' ;
12+ import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js' ;
1113import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js' ;
1214import { ChatContextKeys } from '../../common/actions/chatContextKeys.js' ;
1315import { ChatRequestQueueKind , IChatService } from '../../common/chatService/chatService.js' ;
1416import { ChatConfiguration } from '../../common/constants.js' ;
17+ import { isRequestVM } from '../../common/model/chatViewModel.js' ;
1518import { IChatWidgetService } from '../chat.js' ;
1619import { CHAT_CATEGORY } from './chatActions.js' ;
1720
@@ -38,6 +41,7 @@ export class ChatQueueMessageAction extends Action2 {
3841 super ( {
3942 id : ChatQueueMessageAction . ID ,
4043 title : localize2 ( 'chat.queueMessage' , "Add to Queue" ) ,
44+ tooltip : localize ( 'chat.queueMessage.tooltip' , "Queue this message to send after the current request completes" ) ,
4145 icon : Codicon . add ,
4246 f1 : false ,
4347 category : CHAT_CATEGORY ,
@@ -46,6 +50,15 @@ export class ChatQueueMessageAction extends Action2 {
4650 ChatContextKeys . requestInProgress ,
4751 ChatContextKeys . inputHasText
4852 ) ,
53+ keybinding : {
54+ when : ContextKeyExpr . and (
55+ ChatContextKeys . inChatInput ,
56+ ChatContextKeys . requestInProgress ,
57+ queueingEnabledCondition
58+ ) ,
59+ primary : KeyCode . Enter ,
60+ weight : KeybindingWeight . EditorContrib + 1
61+ } ,
4962 menu : [ {
5063 id : MenuId . ChatExecuteQueue ,
5164 group : 'navigation' ,
@@ -77,6 +90,7 @@ export class ChatSteerWithMessageAction extends Action2 {
7790 super ( {
7891 id : ChatSteerWithMessageAction . ID ,
7992 title : localize2 ( 'chat.steerWithMessage' , "Steer with Message" ) ,
93+ tooltip : localize ( 'chat.steerWithMessage.tooltip' , "Send this message at the next opportunity, signaling the current request to yield" ) ,
8094 icon : Codicon . arrowRight ,
8195 f1 : false ,
8296 category : CHAT_CATEGORY ,
@@ -85,6 +99,15 @@ export class ChatSteerWithMessageAction extends Action2 {
8599 ChatContextKeys . requestInProgress ,
86100 ChatContextKeys . inputHasText
87101 ) ,
102+ keybinding : {
103+ when : ContextKeyExpr . and (
104+ ChatContextKeys . inChatInput ,
105+ ChatContextKeys . requestInProgress ,
106+ queueingEnabledCondition
107+ ) ,
108+ primary : KeyMod . Alt | KeyCode . Enter ,
109+ weight : KeybindingWeight . EditorContrib + 1
110+ } ,
88111 menu : [ {
89112 id : MenuId . ChatExecuteQueue ,
90113 group : 'navigation' ,
@@ -119,24 +142,140 @@ export class ChatRemovePendingRequestAction extends Action2 {
119142 icon : Codicon . close ,
120143 f1 : false ,
121144 category : CHAT_CATEGORY ,
145+ menu : [ {
146+ id : MenuId . ChatMessageTitle ,
147+ group : 'navigation' ,
148+ order : 4 ,
149+ when : ContextKeyExpr . and (
150+ queueingEnabledCondition ,
151+ ChatContextKeys . isRequest ,
152+ ChatContextKeys . isPendingRequest
153+ )
154+ } ]
155+ } ) ;
156+ }
157+
158+ override run ( accessor : ServicesAccessor , ...args : unknown [ ] ) : void {
159+ const chatService = accessor . get ( IChatService ) ;
160+ const [ context ] = args ;
161+
162+ // Support both toolbar context (IChatRequestViewModel) and command context (IChatRemovePendingRequestContext)
163+ if ( isRequestVM ( context ) && context . pendingKind ) {
164+ chatService . removePendingRequest ( context . sessionResource , context . id ) ;
165+ return ;
166+ }
167+
168+ if ( isRemovePendingRequestContext ( context ) ) {
169+ chatService . removePendingRequest ( context . sessionResource , context . pendingRequestId ) ;
170+ return ;
171+ }
172+ }
173+ }
174+
175+ export class ChatSendPendingImmediatelyAction extends Action2 {
176+ static readonly ID = 'workbench.action.chat.sendPendingImmediately' ;
177+
178+ constructor ( ) {
179+ super ( {
180+ id : ChatSendPendingImmediatelyAction . ID ,
181+ title : localize2 ( 'chat.sendPendingImmediately' , "Send Immediately" ) ,
182+ icon : Codicon . arrowUp ,
183+ f1 : false ,
184+ category : CHAT_CATEGORY ,
185+ menu : [ {
186+ id : MenuId . ChatMessageTitle ,
187+ group : 'navigation' ,
188+ order : 3 ,
189+ when : ContextKeyExpr . and (
190+ queueingEnabledCondition ,
191+ ChatContextKeys . isRequest ,
192+ ChatContextKeys . isPendingRequest
193+ )
194+ } ]
122195 } ) ;
123196 }
124197
125198 override run ( accessor : ServicesAccessor , ...args : unknown [ ] ) : void {
126199 const chatService = accessor . get ( IChatService ) ;
200+ const widgetService = accessor . get ( IChatWidgetService ) ;
127201 const [ context ] = args ;
128- if ( ! isRemovePendingRequestContext ( context ) ) {
202+
203+ if ( ! isRequestVM ( context ) || ! context . pendingKind ) {
129204 return ;
130205 }
131206
132- chatService . removePendingRequest ( context . sessionResource , context . pendingRequestId ) ;
207+ const widget = widgetService . getWidgetBySessionResource ( context . sessionResource ) ;
208+ const model = widget ?. viewModel ?. model ;
209+ if ( ! model ) {
210+ return ;
211+ }
212+
213+ const pendingRequests = model . getPendingRequests ( ) ;
214+ const targetIndex = pendingRequests . findIndex ( r => r . request . id === context . id ) ;
215+ if ( targetIndex === - 1 ) {
216+ return ;
217+ }
218+
219+ // Keep the target item's kind (queued vs steering)
220+ const targetRequest = pendingRequests [ targetIndex ] ;
221+
222+ // Reorder: move target to front, keep others in their relative order
223+ const reordered = [
224+ { requestId : targetRequest . request . id , kind : targetRequest . kind } ,
225+ ...pendingRequests . filter ( ( _ , i ) => i !== targetIndex ) . map ( r => ( { requestId : r . request . id , kind : r . kind } ) )
226+ ] ;
227+
228+ chatService . setPendingRequests ( context . sessionResource , reordered ) ;
229+ chatService . cancelCurrentRequestForSession ( context . sessionResource ) ;
230+ chatService . processPendingRequests ( context . sessionResource ) ;
231+ }
232+ }
233+
234+ export class ChatRemoveAllPendingRequestsAction extends Action2 {
235+ static readonly ID = 'workbench.action.chat.removeAllPendingRequests' ;
236+
237+ constructor ( ) {
238+ super ( {
239+ id : ChatRemoveAllPendingRequestsAction . ID ,
240+ title : localize2 ( 'chat.removeAllPendingRequests' , "Remove All Queued" ) ,
241+ icon : Codicon . clearAll ,
242+ f1 : false ,
243+ category : CHAT_CATEGORY ,
244+ menu : [ {
245+ id : MenuId . ChatContext ,
246+ group : 'navigation' ,
247+ order : 3 ,
248+ when : ContextKeyExpr . and (
249+ queueingEnabledCondition ,
250+ ChatContextKeys . hasPendingRequests
251+ )
252+ } ]
253+ } ) ;
254+ }
255+
256+ override run ( accessor : ServicesAccessor , ...args : unknown [ ] ) : void {
257+ const chatService = accessor . get ( IChatService ) ;
258+ const widgetService = accessor . get ( IChatWidgetService ) ;
259+ const [ context ] = args ;
260+
261+ const widget = ( isRequestVM ( context ) && widgetService . getWidgetBySessionResource ( context . sessionResource ) ) || widgetService . lastFocusedWidget ;
262+ const model = widget ?. viewModel ?. model ;
263+ if ( ! model ) {
264+ return ;
265+ }
266+
267+ for ( const pendingRequest of [ ...model . getPendingRequests ( ) ] ) {
268+ chatService . removePendingRequest ( model . sessionResource , pendingRequest . request . id ) ;
269+ }
133270 }
134271}
135272
136273export function registerChatQueueActions ( ) : void {
137274 registerAction2 ( ChatQueueMessageAction ) ;
138275 registerAction2 ( ChatSteerWithMessageAction ) ;
139276 registerAction2 ( ChatRemovePendingRequestAction ) ;
277+ registerAction2 ( ChatSendPendingImmediatelyAction ) ;
278+ registerAction2 ( ChatRemoveAllPendingRequestsAction ) ;
140279
141280 // Register the queue submenu as a split button dropdown in the execute toolbar
142281 // This shows "Add to Queue" / "Steer with Message" when a request is in progress and input has text
@@ -150,7 +289,7 @@ export function registerChatQueueActions(): void {
150289 ChatContextKeys . inputHasText
151290 ) ,
152291 group : 'navigation' ,
153- order : 3 ,
292+ order : 4 ,
154293 isSplitButton : { togglePrimaryAction : true }
155294 } ) ;
156295}
0 commit comments