4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
6
import * as dom from '../../../../../base/browser/dom.js' ;
7
+ import { StandardKeyboardEvent } from '../../../../../base/browser/keyboardEvent.js' ;
7
8
import { StandardMouseEvent } from '../../../../../base/browser/mouseEvent.js' ;
8
9
import { Button } from '../../../../../base/browser/ui/button/button.js' ;
9
10
import { getDefaultHoverDelegate } from '../../../../../base/browser/ui/hover/hoverDelegateFactory.js' ;
10
11
import { Codicon } from '../../../../../base/common/codicons.js' ;
12
+ import { KeyCode } from '../../../../../base/common/keyCodes.js' ;
11
13
import { Disposable , DisposableStore } from '../../../../../base/common/lifecycle.js' ;
12
14
import { Schemas } from '../../../../../base/common/network.js' ;
13
15
import { basename , dirname } from '../../../../../base/common/resources.js' ;
@@ -17,6 +19,7 @@ import { IModelService } from '../../../../../editor/common/services/model.js';
17
19
import { localize } from '../../../../../nls.js' ;
18
20
import { getFlatContextMenuActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js' ;
19
21
import { IMenuService , MenuId } from '../../../../../platform/actions/common/actions.js' ;
22
+ import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js' ;
20
23
import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js' ;
21
24
import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js' ;
22
25
import { FileKind , IFileService } from '../../../../../platform/files/common/files.js' ;
@@ -25,6 +28,8 @@ import { ILabelService } from '../../../../../platform/label/common/label.js';
25
28
import { ResourceLabels } from '../../../../browser/labels.js' ;
26
29
import { ResourceContextKey } from '../../../../common/contextkeys.js' ;
27
30
import { IChatRequestImplicitVariableEntry } from '../../common/chatVariableEntries.js' ;
31
+ import { IChatWidgetService } from '../chat.js' ;
32
+ import { ChatAttachmentModel } from '../chatAttachmentModel.js' ;
28
33
29
34
export class ImplicitContextAttachmentWidget extends Disposable {
30
35
public readonly domNode : HTMLElement ;
@@ -34,6 +39,7 @@ export class ImplicitContextAttachmentWidget extends Disposable {
34
39
constructor (
35
40
private readonly attachment : IChatRequestImplicitVariableEntry ,
36
41
private readonly resourceLabels : ResourceLabels ,
42
+ private readonly attachmentModel : ChatAttachmentModel ,
37
43
@IContextKeyService private readonly contextKeyService : IContextKeyService ,
38
44
@IContextMenuService private readonly contextMenuService : IContextMenuService ,
39
45
@ILabelService private readonly labelService : ILabelService ,
@@ -42,6 +48,8 @@ export class ImplicitContextAttachmentWidget extends Disposable {
42
48
@ILanguageService private readonly languageService : ILanguageService ,
43
49
@IModelService private readonly modelService : IModelService ,
44
50
@IHoverService private readonly hoverService : IHoverService ,
51
+ @IChatWidgetService private readonly chatWidgetService : IChatWidgetService ,
52
+ @IConfigurationService private readonly configService : IConfigurationService
45
53
) {
46
54
super ( ) ;
47
55
@@ -80,17 +88,56 @@ export class ImplicitContextAttachmentWidget extends Disposable {
80
88
this . domNode . ariaLabel = ariaLabel ;
81
89
this . domNode . tabIndex = 0 ;
82
90
83
- const hintLabel = localize ( 'hint.label.current' , "Current {0}" , attachmentTypeName ) ;
91
+ const isSuggestedEnabled = this . configService . getValue ( 'chat.implicitContext.suggestedContext' ) ;
92
+ const hintLabel = ! this . attachment . isSelection && ! isSuggestedEnabled ? localize ( 'hint.label.current' , "Current {0}" , attachmentTypeName ) : '' ;
84
93
const hintElement = dom . append ( this . domNode , dom . $ ( 'span.chat-implicit-hint' , undefined , hintLabel ) ) ;
85
94
this . _register ( this . hoverService . setupManagedHover ( getDefaultHoverDelegate ( 'element' ) , hintElement , title ) ) ;
86
95
87
- const buttonMsg = this . attachment . enabled ? localize ( 'disable' , "Disable current {0} context" , attachmentTypeName ) : localize ( 'enable' , "Enable current {0} context" , attachmentTypeName ) ;
88
- const toggleButton = this . renderDisposables . add ( new Button ( this . domNode , { supportIcons : true , title : buttonMsg } ) ) ;
89
- toggleButton . icon = this . attachment . enabled ? Codicon . eye : Codicon . eyeClosed ;
90
- this . renderDisposables . add ( toggleButton . onDidClick ( ( e ) => {
91
- e . stopPropagation ( ) ; // prevent it from triggering the click handler on the parent immediately after rerendering
92
- this . attachment . enabled = ! this . attachment . enabled ;
93
- } ) ) ;
96
+
97
+ if ( isSuggestedEnabled ) {
98
+ if ( ! this . attachment . isSelection ) {
99
+ const buttonMsg = this . attachment . enabled ? localize ( 'disable' , "Disable current {0} context" , attachmentTypeName ) : localize ( 'enable' , "Enable current {0} context" , attachmentTypeName ) ;
100
+ const toggleButton = this . renderDisposables . add ( new Button ( this . domNode , { supportIcons : true , title : buttonMsg } ) ) ;
101
+ toggleButton . icon = this . attachment . enabled ? Codicon . x : Codicon . plus ;
102
+ this . renderDisposables . add ( toggleButton . onDidClick ( ( e ) => {
103
+ e . stopPropagation ( ) ;
104
+ e . preventDefault ( ) ;
105
+ if ( ! this . attachment . enabled ) {
106
+ this . convertToRegularAttachment ( ) ;
107
+ }
108
+ this . attachment . enabled = false ;
109
+ } ) ) ;
110
+ }
111
+
112
+ if ( ! this . attachment . enabled && this . attachment . isSelection ) {
113
+ this . domNode . classList . remove ( 'disabled' ) ;
114
+ }
115
+
116
+ this . renderDisposables . add ( dom . addDisposableListener ( this . domNode , dom . EventType . CLICK , e => {
117
+ if ( ! this . attachment . enabled && ! this . attachment . isSelection ) {
118
+ this . convertToRegularAttachment ( ) ;
119
+ }
120
+ } ) ) ;
121
+
122
+ this . renderDisposables . add ( dom . addDisposableListener ( this . domNode , dom . EventType . KEY_DOWN , e => {
123
+ const event = new StandardKeyboardEvent ( e ) ;
124
+ if ( event . equals ( KeyCode . Enter ) || event . equals ( KeyCode . Space ) ) {
125
+ if ( ! this . attachment . enabled && ! this . attachment . isSelection ) {
126
+ e . preventDefault ( ) ;
127
+ e . stopPropagation ( ) ;
128
+ this . convertToRegularAttachment ( ) ;
129
+ }
130
+ }
131
+ } ) ) ;
132
+ } else {
133
+ const buttonMsg = this . attachment . enabled ? localize ( 'disable' , "Disable current {0} context" , attachmentTypeName ) : localize ( 'enable' , "Enable current {0} context" , attachmentTypeName ) ;
134
+ const toggleButton = this . renderDisposables . add ( new Button ( this . domNode , { supportIcons : true , title : buttonMsg } ) ) ;
135
+ toggleButton . icon = this . attachment . enabled ? Codicon . eye : Codicon . eyeClosed ;
136
+ this . renderDisposables . add ( toggleButton . onDidClick ( ( e ) => {
137
+ e . stopPropagation ( ) ; // prevent it from triggering the click handler on the parent immediately after rerendering
138
+ this . attachment . enabled = ! this . attachment . enabled ;
139
+ } ) ) ;
140
+ }
94
141
95
142
// Context menu
96
143
const scopedContextKeyService = this . renderDisposables . add ( this . contextKeyService . createScoped ( this . domNode ) ) ;
@@ -112,4 +159,13 @@ export class ImplicitContextAttachmentWidget extends Disposable {
112
159
} ) ;
113
160
} ) ) ;
114
161
}
162
+
163
+ private convertToRegularAttachment ( ) : void {
164
+ if ( ! this . attachment . value ) {
165
+ return ;
166
+ }
167
+ const file = URI . isUri ( this . attachment . value ) ? this . attachment . value : this . attachment . value . uri ;
168
+ this . attachmentModel . addFile ( file ) ;
169
+ this . chatWidgetService . lastFocusedWidget ?. focusInput ( ) ;
170
+ }
115
171
}
0 commit comments