44 *--------------------------------------------------------------------------------------------*/
55
66import * as dom from '../../../../../base/browser/dom.js' ;
7+ import { StandardKeyboardEvent } from '../../../../../base/browser/keyboardEvent.js' ;
78import { StandardMouseEvent } from '../../../../../base/browser/mouseEvent.js' ;
89import { Button } from '../../../../../base/browser/ui/button/button.js' ;
910import { getDefaultHoverDelegate } from '../../../../../base/browser/ui/hover/hoverDelegateFactory.js' ;
1011import { Codicon } from '../../../../../base/common/codicons.js' ;
12+ import { KeyCode } from '../../../../../base/common/keyCodes.js' ;
1113import { Disposable , DisposableStore } from '../../../../../base/common/lifecycle.js' ;
1214import { Schemas } from '../../../../../base/common/network.js' ;
1315import { basename , dirname } from '../../../../../base/common/resources.js' ;
@@ -17,6 +19,7 @@ import { IModelService } from '../../../../../editor/common/services/model.js';
1719import { localize } from '../../../../../nls.js' ;
1820import { getFlatContextMenuActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js' ;
1921import { IMenuService , MenuId } from '../../../../../platform/actions/common/actions.js' ;
22+ import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js' ;
2023import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js' ;
2124import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js' ;
2225import { FileKind , IFileService } from '../../../../../platform/files/common/files.js' ;
@@ -25,6 +28,8 @@ import { ILabelService } from '../../../../../platform/label/common/label.js';
2528import { ResourceLabels } from '../../../../browser/labels.js' ;
2629import { ResourceContextKey } from '../../../../common/contextkeys.js' ;
2730import { IChatRequestImplicitVariableEntry } from '../../common/chatVariableEntries.js' ;
31+ import { IChatWidgetService } from '../chat.js' ;
32+ import { ChatAttachmentModel } from '../chatAttachmentModel.js' ;
2833
2934export class ImplicitContextAttachmentWidget extends Disposable {
3035 public readonly domNode : HTMLElement ;
@@ -34,6 +39,7 @@ export class ImplicitContextAttachmentWidget extends Disposable {
3439 constructor (
3540 private readonly attachment : IChatRequestImplicitVariableEntry ,
3641 private readonly resourceLabels : ResourceLabels ,
42+ private readonly attachmentModel : ChatAttachmentModel ,
3743 @IContextKeyService private readonly contextKeyService : IContextKeyService ,
3844 @IContextMenuService private readonly contextMenuService : IContextMenuService ,
3945 @ILabelService private readonly labelService : ILabelService ,
@@ -42,6 +48,8 @@ export class ImplicitContextAttachmentWidget extends Disposable {
4248 @ILanguageService private readonly languageService : ILanguageService ,
4349 @IModelService private readonly modelService : IModelService ,
4450 @IHoverService private readonly hoverService : IHoverService ,
51+ @IChatWidgetService private readonly chatWidgetService : IChatWidgetService ,
52+ @IConfigurationService private readonly configService : IConfigurationService
4553 ) {
4654 super ( ) ;
4755
@@ -80,17 +88,56 @@ export class ImplicitContextAttachmentWidget extends Disposable {
8088 this . domNode . ariaLabel = ariaLabel ;
8189 this . domNode . tabIndex = 0 ;
8290
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 ) : '' ;
8493 const hintElement = dom . append ( this . domNode , dom . $ ( 'span.chat-implicit-hint' , undefined , hintLabel ) ) ;
8594 this . _register ( this . hoverService . setupManagedHover ( getDefaultHoverDelegate ( 'element' ) , hintElement , title ) ) ;
8695
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+ }
94141
95142 // Context menu
96143 const scopedContextKeyService = this . renderDisposables . add ( this . contextKeyService . createScoped ( this . domNode ) ) ;
@@ -112,4 +159,13 @@ export class ImplicitContextAttachmentWidget extends Disposable {
112159 } ) ;
113160 } ) ) ;
114161 }
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+ }
115171}
0 commit comments