@@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom';
7
7
import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer' ;
8
8
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent' ;
9
9
import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems' ;
10
+ import { DropdownMenuActionViewItem , IDropdownMenuActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem' ;
10
11
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory' ;
11
12
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list' ;
12
13
import { ITreeNode , ITreeRenderer } from 'vs/base/browser/ui/tree/tree' ;
@@ -26,18 +27,20 @@ import { ThemeIcon } from 'vs/base/common/themables';
26
27
import { URI } from 'vs/base/common/uri' ;
27
28
import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer' ;
28
29
import { localize } from 'vs/nls' ;
29
- import { IMenuEntryActionViewItemOptions , MenuEntryActionViewItem , createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem' ;
30
+ import { IMenuEntryActionViewItemOptions , createActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem' ;
30
31
import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar' ;
31
32
import { MenuId , MenuItemAction } from 'vs/platform/actions/common/actions' ;
32
33
import { ICommandService } from 'vs/platform/commands/common/commands' ;
33
34
import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
34
35
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey' ;
36
+ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView' ;
35
37
import { IHoverService } from 'vs/platform/hover/browser/hover' ;
36
38
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' ;
37
39
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection' ;
38
40
import { ILogService } from 'vs/platform/log/common/log' ;
39
41
import { ColorScheme } from 'vs/platform/theme/common/theme' ;
40
42
import { IThemeService } from 'vs/platform/theme/common/themeService' ;
43
+ import { MarkUnhelpfulActionId } from 'vs/workbench/contrib/chat/browser/actions/chatTitleActions' ;
41
44
import { ChatTreeItem , GeneratingPhrase , IChatCodeBlockInfo , IChatFileTreeInfo , IChatListItemRendererOptions } from 'vs/workbench/contrib/chat/browser/chat' ;
42
45
import { ChatAgentHover , getChatAgentHoverOptions } from 'vs/workbench/contrib/chat/browser/chatAgentHover' ;
43
46
import { ChatAttachmentsContentPart } from 'vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart' ;
@@ -61,9 +64,10 @@ import { ChatAgentLocation, IChatAgentMetadata } from 'vs/workbench/contrib/chat
61
64
import { CONTEXT_CHAT_RESPONSE_SUPPORT_ISSUE_REPORTING , CONTEXT_REQUEST , CONTEXT_RESPONSE , CONTEXT_RESPONSE_DETECTED_AGENT_COMMAND , CONTEXT_RESPONSE_ERROR , CONTEXT_RESPONSE_FILTERED , CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys' ;
62
65
import { IChatRequestVariableEntry , IChatTextEditGroup } from 'vs/workbench/contrib/chat/common/chatModel' ;
63
66
import { chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes' ;
64
- import { ChatAgentVoteDirection , IChatConfirmation , IChatContentReference , IChatFollowup , IChatTask , IChatTreeData } from 'vs/workbench/contrib/chat/common/chatService' ;
67
+ import { ChatAgentVoteDirection , ChatAgentVoteDownReason , IChatConfirmation , IChatContentReference , IChatFollowup , IChatTask , IChatTreeData } from 'vs/workbench/contrib/chat/common/chatService' ;
65
68
import { IChatCodeCitations , IChatReferences , IChatRendererContent , IChatRequestViewModel , IChatResponseViewModel , IChatWelcomeMessageViewModel , isRequestVM , isResponseVM , isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel' ;
66
69
import { getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter' ;
70
+ import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/common/issue' ;
67
71
import { annotateSpecialMarkdownContent } from '../common/annotations' ;
68
72
import { CodeBlockModelCollection } from '../common/codeBlockModelCollection' ;
69
73
@@ -276,26 +280,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
276
280
277
281
const contextKeyService = templateDisposables . add ( this . contextKeyService . createScoped ( rowContainer ) ) ;
278
282
const scopedInstantiationService = templateDisposables . add ( this . instantiationService . createChild ( new ServiceCollection ( [ IContextKeyService , contextKeyService ] ) ) ) ;
279
- let titleToolbar : MenuWorkbenchToolBar | undefined ;
280
- if ( this . rendererOptions . noHeader ) {
281
- header . classList . add ( 'hidden' ) ;
282
- } else {
283
- titleToolbar = templateDisposables . add ( scopedInstantiationService . createInstance ( MenuWorkbenchToolBar , toolbarParent ?? header , MenuId . ChatMessageTitle , {
284
- menuOptions : {
285
- shouldForwardArgs : true
286
- } ,
287
- toolbarOptions : {
288
- shouldInlineSubmenu : submenu => submenu . actions . length <= 1
289
- } ,
290
- actionViewItemProvider : ( action : IAction , options : IActionViewItemOptions ) => {
291
- if ( action instanceof MenuItemAction && ( action . item . id === 'workbench.action.chat.voteDown' || action . item . id === 'workbench.action.chat.voteUp' ) ) {
292
- return scopedInstantiationService . createInstance ( ChatVoteButton , action , options as IMenuEntryActionViewItemOptions ) ;
293
- }
294
- return createActionViewItem ( scopedInstantiationService , action , options ) ;
295
- }
296
- } ) ) ;
297
- }
298
-
299
283
const agentHover = templateDisposables . add ( this . instantiationService . createInstance ( ChatAgentHover ) ) ;
300
284
const hoverContent = ( ) => {
301
285
if ( isResponseVM ( template . currentElement ) && template . currentElement . agent && ! template . currentElement . agent . isDefault ) {
@@ -318,7 +302,29 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
318
302
this . hoverService . hideHover ( ) ;
319
303
}
320
304
} ) ) ;
321
- const template : IChatListItemTemplate = { avatarContainer, username, detail, value, rowContainer, elementDisposables, titleToolbar, templateDisposables, contextKeyService, instantiationService : scopedInstantiationService , agentHover } ;
305
+ const template : IChatListItemTemplate = { avatarContainer, username, detail, value, rowContainer, elementDisposables, templateDisposables, contextKeyService, instantiationService : scopedInstantiationService , agentHover } ;
306
+
307
+ if ( this . rendererOptions . noHeader ) {
308
+ header . classList . add ( 'hidden' ) ;
309
+ } else {
310
+ // Have to create the template first because actionViewItemProvider depends on the template and runs immediately
311
+ ( template as any ) . titleToolbar = templateDisposables . add ( scopedInstantiationService . createInstance ( MenuWorkbenchToolBar , toolbarParent ?? header , MenuId . ChatMessageTitle , {
312
+ menuOptions : {
313
+ shouldForwardArgs : true
314
+ } ,
315
+ toolbarOptions : {
316
+ shouldInlineSubmenu : submenu => submenu . actions . length <= 1
317
+ } ,
318
+ actionViewItemProvider : ( action : IAction , options : IActionViewItemOptions ) => {
319
+ const currentElement = template . currentElement ;
320
+ if ( action instanceof MenuItemAction && action . item . id === MarkUnhelpfulActionId && isResponseVM ( currentElement ) ) {
321
+ return scopedInstantiationService . createInstance ( ChatVoteDownButton , action , options as IMenuEntryActionViewItemOptions ) ;
322
+ }
323
+ return createActionViewItem ( scopedInstantiationService , action , options ) ;
324
+ }
325
+ } ) ) ;
326
+ }
327
+
322
328
return template ;
323
329
}
324
330
@@ -949,9 +955,88 @@ export class ChatListDelegate implements IListVirtualDelegate<ChatTreeItem> {
949
955
}
950
956
}
951
957
952
- class ChatVoteButton extends MenuEntryActionViewItem {
958
+ const voteDownDetailLabels : Record < ChatAgentVoteDownReason , string > = {
959
+ [ ChatAgentVoteDownReason . IncorrectCode ] : localize ( 'incorrectCode' , "Suggested incorrect code" ) ,
960
+ [ ChatAgentVoteDownReason . DidNotFollowInstructions ] : localize ( 'didNotFollowInstructions' , "Didn't follow instructions" ) ,
961
+ [ ChatAgentVoteDownReason . MissingContext ] : localize ( 'missingContext' , "Missing context" ) ,
962
+ [ ChatAgentVoteDownReason . OffensiveOrUnsafe ] : localize ( 'offensiveOrUnsafe' , "Offensive or unsafe" ) ,
963
+ [ ChatAgentVoteDownReason . PoorlyWrittenOrFormatted ] : localize ( 'poorlyWrittenOrFormatted' , "Poorly written or formatted" ) ,
964
+ [ ChatAgentVoteDownReason . RefusedAValidRequest ] : localize ( 'refusedAValidRequest' , "Refused a valid request" ) ,
965
+ [ ChatAgentVoteDownReason . IncompleteCode ] : localize ( 'incompleteCode' , "Incomplete code" ) ,
966
+ [ ChatAgentVoteDownReason . WillReportIssue ] : localize ( 'reportIssue' , "Report an issue" ) ,
967
+ [ ChatAgentVoteDownReason . Other ] : localize ( 'other' , "Other" ) ,
968
+ } ;
969
+
970
+ export class ChatVoteDownButton extends DropdownMenuActionViewItem {
971
+ constructor (
972
+ action : IAction ,
973
+ options : IDropdownMenuActionViewItemOptions | undefined ,
974
+ @ICommandService private readonly commandService : ICommandService ,
975
+ @IWorkbenchIssueService private readonly issueService : IWorkbenchIssueService ,
976
+ @ILogService private readonly logService : ILogService ,
977
+ @IContextMenuService contextMenuService : IContextMenuService ,
978
+ ) {
979
+ super ( action ,
980
+ { getActions : ( ) => this . getActions ( ) , } ,
981
+ contextMenuService ,
982
+ {
983
+ ...options ,
984
+ classNames : ThemeIcon . asClassNameArray ( Codicon . thumbsdown ) ,
985
+ } ) ;
986
+ }
987
+
988
+ getActions ( ) : readonly IAction [ ] {
989
+ return [
990
+ this . getVoteDownDetailAction ( ChatAgentVoteDownReason . IncorrectCode ) ,
991
+ this . getVoteDownDetailAction ( ChatAgentVoteDownReason . DidNotFollowInstructions ) ,
992
+ this . getVoteDownDetailAction ( ChatAgentVoteDownReason . IncompleteCode ) ,
993
+ this . getVoteDownDetailAction ( ChatAgentVoteDownReason . MissingContext ) ,
994
+ this . getVoteDownDetailAction ( ChatAgentVoteDownReason . PoorlyWrittenOrFormatted ) ,
995
+ this . getVoteDownDetailAction ( ChatAgentVoteDownReason . RefusedAValidRequest ) ,
996
+ this . getVoteDownDetailAction ( ChatAgentVoteDownReason . OffensiveOrUnsafe ) ,
997
+ this . getVoteDownDetailAction ( ChatAgentVoteDownReason . Other ) ,
998
+ {
999
+ id : 'reportIssue' ,
1000
+ label : voteDownDetailLabels [ ChatAgentVoteDownReason . WillReportIssue ] ,
1001
+ tooltip : '' ,
1002
+ enabled : true ,
1003
+ class : undefined ,
1004
+ run : async ( context : IChatResponseViewModel ) => {
1005
+ if ( ! isResponseVM ( context ) ) {
1006
+ this . logService . error ( 'ChatVoteDownButton#run: invalid context' ) ;
1007
+ return ;
1008
+ }
1009
+
1010
+ await this . commandService . executeCommand ( MarkUnhelpfulActionId , context , ChatAgentVoteDownReason . WillReportIssue ) ;
1011
+ await this . issueService . openReporter ( { extensionId : context . agent ?. extensionId . value } ) ;
1012
+ }
1013
+ }
1014
+ ] ;
1015
+ }
1016
+
953
1017
override render ( container : HTMLElement ) : void {
954
1018
super . render ( container ) ;
955
- container . classList . toggle ( 'checked' , this . action . checked ) ;
1019
+
1020
+ this . element ?. classList . toggle ( 'checked' , this . action . checked ) ;
1021
+ }
1022
+
1023
+ private getVoteDownDetailAction ( reason : ChatAgentVoteDownReason ) : IAction {
1024
+ const label = voteDownDetailLabels [ reason ] ;
1025
+ return {
1026
+ id : MarkUnhelpfulActionId ,
1027
+ label,
1028
+ tooltip : '' ,
1029
+ enabled : true ,
1030
+ checked : ( this . _context as IChatResponseViewModel ) . voteDownReason === reason ,
1031
+ class : undefined ,
1032
+ run : async ( context : IChatResponseViewModel ) => {
1033
+ if ( ! isResponseVM ( context ) ) {
1034
+ this . logService . error ( 'ChatVoteDownButton#getVoteDownDetailAction: invalid context' ) ;
1035
+ return ;
1036
+ }
1037
+
1038
+ await this . commandService . executeCommand ( MarkUnhelpfulActionId , context , reason ) ;
1039
+ }
1040
+ } ;
956
1041
}
957
1042
}
0 commit comments