@@ -12,7 +12,9 @@ import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js';
12
12
import * as aria from '../../../../base/browser/ui/aria/aria.js' ;
13
13
import { Button } from '../../../../base/browser/ui/button/button.js' ;
14
14
import { IHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate.js' ;
15
+ import { getBaseLayerHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate2.js' ;
15
16
import { createInstantHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js' ;
17
+ import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js' ;
16
18
import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js' ;
17
19
import { ProgressBar } from '../../../../base/browser/ui/progressbar/progressbar.js' ;
18
20
import { IAction } from '../../../../base/common/actions.js' ;
@@ -84,7 +86,7 @@ import { ILanguageModelChatMetadata, ILanguageModelsService } from '../common/la
84
86
import { CancelAction , ChatModelPickerActionId , ChatSubmitSecondaryAgentAction , IChatExecuteActionContext , ChatSubmitAction } from './actions/chatExecuteActions.js' ;
85
87
import { ImplicitContextAttachmentWidget } from './attachments/implicitContextAttachment.js' ;
86
88
import { IChatWidget } from './chat.js' ;
87
- import { ChatAttachmentModel } from './chatAttachmentModel.js' ;
89
+ import { ChatAttachmentModel , EditsAttachmentModel } from './chatAttachmentModel.js' ;
88
90
import { IDisposableReference } from './chatContentParts/chatCollections.js' ;
89
91
import { CollapsibleListPool , IChatCollapsibleListItem } from './chatContentParts/chatReferencesContentPart.js' ;
90
92
import { ChatDragAndDrop , EditsDragAndDrop } from './chatDragAndDrop.js' ;
@@ -223,6 +225,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
223
225
224
226
private readonly _chatEditsActionsDisposables = this . _register ( new DisposableStore ( ) ) ;
225
227
private readonly _chatEditsDisposables = this . _register ( new DisposableStore ( ) ) ;
228
+ private readonly _chatEditsFileLimitHover = this . _register ( new MutableDisposable < IDisposable > ( ) ) ;
226
229
private _chatEditsProgress : ProgressBar | undefined ;
227
230
private _chatEditsListPool : CollapsibleListPool ;
228
231
private _chatEditList : IDisposableReference < WorkbenchList < IChatCollapsibleListItem > > | undefined ;
@@ -281,7 +284,14 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
281
284
) {
282
285
super ( ) ;
283
286
284
- this . _attachmentModel = this . _register ( new ChatAttachmentModel ( ) ) ;
287
+ if ( this . location === ChatAgentLocation . EditingSession ) {
288
+ this . _attachmentModel = this . _register ( this . instantiationService . createInstance ( EditsAttachmentModel ) ) ;
289
+ this . dnd = this . _register ( this . instantiationService . createInstance ( EditsDragAndDrop , this . attachmentModel , styles ) ) ;
290
+ } else {
291
+ this . _attachmentModel = this . _register ( this . instantiationService . createInstance ( ChatAttachmentModel ) ) ;
292
+ this . dnd = this . _register ( this . instantiationService . createInstance ( ChatDragAndDrop , this . attachmentModel , styles ) ) ;
293
+ }
294
+
285
295
this . getInputState = ( ) : IChatInputState => {
286
296
return {
287
297
...getContribsInputState ( ) ,
@@ -304,7 +314,6 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
304
314
} ) ) ;
305
315
306
316
this . _chatEditsListPool = this . _register ( this . instantiationService . createInstance ( CollapsibleListPool , this . _onDidChangeVisibility . event , MenuId . ChatEditingWidgetModifiedFilesToolbar ) ) ;
307
- this . dnd = this . _register ( this . instantiationService . createInstance ( this . location === ChatAgentLocation . EditingSession ? EditsDragAndDrop : ChatDragAndDrop , this . attachmentModel , styles ) ) ;
308
317
309
318
this . _hasFileAttachmentContextKey = ChatContextKeys . hasFileAttachments . bindTo ( contextKeyService ) ;
310
319
}
@@ -1044,6 +1053,19 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
1044
1053
} ) ;
1045
1054
}
1046
1055
}
1056
+ const excludedEntries : IChatCollapsibleListItem [ ] = [ ] ;
1057
+ for ( const excludedAttachment of ( this . attachmentModel as EditsAttachmentModel ) . excludedFileAttachments ) {
1058
+ if ( excludedAttachment . isFile && URI . isUri ( excludedAttachment . value ) && ! seenEntries . has ( excludedAttachment . value ) ) {
1059
+ excludedEntries . push ( {
1060
+ reference : excludedAttachment . value ,
1061
+ state : WorkingSetEntryState . Attached ,
1062
+ kind : 'reference' ,
1063
+ excluded : true ,
1064
+ title : localize ( 'chatEditingSession.excludedFile' , 'The Working Set file limit has ben reached. {0} is excluded from the Woking Set. Remove other files to make space for {0}.' , basename ( excludedAttachment . value . path ) )
1065
+ } ) ;
1066
+ seenEntries . add ( excludedAttachment . value ) ;
1067
+ }
1068
+ }
1047
1069
entries . sort ( ( a , b ) => {
1048
1070
if ( a . kind === 'reference' && b . kind === 'reference' ) {
1049
1071
if ( a . state === b . state || a . state === undefined || b . state === undefined ) {
@@ -1055,14 +1077,18 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
1055
1077
} ) ;
1056
1078
let remainingFileEntriesBudget = this . chatEditingService . editingSessionFileLimit ;
1057
1079
const overviewRegion = innerContainer . querySelector ( '.chat-editing-session-overview' ) as HTMLElement ?? dom . append ( innerContainer , $ ( '.chat-editing-session-overview' ) ) ;
1058
- const overviewText = overviewRegion . querySelector ( 'span' ) ?? dom . append ( overviewRegion , $ ( 'span' ) ) ;
1059
- overviewText . textContent = localize ( 'chatEditingSession.workingSet' , 'Working Set' ) ;
1080
+ const overviewTitle = overviewRegion . querySelector ( '.working-set-title' ) as HTMLElement ?? dom . append ( overviewRegion , $ ( '.working-set-title' ) ) ;
1081
+ const overviewWorkingSet = overviewTitle . querySelector ( 'span' ) ?? dom . append ( overviewTitle , $ ( 'span' ) ) ;
1082
+ const overviewFileCount = overviewTitle . querySelector ( 'span.working-set-count' ) ?? dom . append ( overviewTitle , $ ( 'span.working-set-count' ) ) ;
1083
+
1084
+ overviewWorkingSet . textContent = localize ( 'chatEditingSession.workingSet' , 'Working Set' ) ;
1060
1085
1061
1086
// Record the number of entries that the user wanted to add to the working set
1062
- this . _attemptedWorkingSetEntriesCount = entries . length ;
1087
+ this . _attemptedWorkingSetEntriesCount = entries . length + excludedEntries . length ;
1063
1088
1089
+ overviewFileCount . textContent = '' ;
1064
1090
if ( entries . length === 1 ) {
1065
- overviewText . textContent + = ' ' + localize ( 'chatEditingSession.oneFile' , '(1 file)' ) ;
1091
+ overviewFileCount . textContent = ' ' + localize ( 'chatEditingSession.oneFile' , '(1 file)' ) ;
1066
1092
} else if ( entries . length >= remainingFileEntriesBudget ) {
1067
1093
// The user tried to attach too many files, we have to drop anything after the limit
1068
1094
const entriesToPreserve : IChatCollapsibleListItem [ ] = [ ] ;
@@ -1099,7 +1125,28 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
1099
1125
entries = [ ...entriesToPreserve , ...newEntriesThatFit , ...suggestedFilesThatFit ] ;
1100
1126
}
1101
1127
if ( entries . length > 1 ) {
1102
- overviewText . textContent += ' ' + localize ( 'chatEditingSession.manyFiles' , '({0} files)' , entries . length ) ;
1128
+ overviewFileCount . textContent = ' ' + localize ( 'chatEditingSession.manyFiles' , '({0} files)' , entries . length ) ;
1129
+ }
1130
+
1131
+ if ( excludedEntries . length > 0 ) {
1132
+ overviewFileCount . textContent = ' ' + localize ( 'chatEditingSession.excludedFiles' , '({0} files, {1} excluded)' , entries . length , excludedEntries . length ) ;
1133
+ }
1134
+
1135
+ const fileLimitReached = remainingFileEntriesBudget <= 0 ;
1136
+ overviewFileCount . classList . toggle ( 'file-limit-reached' , fileLimitReached ) ;
1137
+ if ( fileLimitReached ) {
1138
+ let title = localize ( 'chatEditingSession.fileLimitReached' , 'You have reached the maximum number of files that can be added to the working set.' ) ;
1139
+ title += excludedEntries . length === 1 ? ' ' + localize ( 'chatEditingSession.excludedOneFile' , '1 file is excluded from the Working Set.' ) : '' ;
1140
+ title += excludedEntries . length > 1 ? ' ' + localize ( 'chatEditingSession.excludedSomeFiles' , '{0} files are excluded from the Working Set.' , excludedEntries . length ) : '' ;
1141
+
1142
+ this . _chatEditsFileLimitHover . value = getBaseLayerHoverDelegate ( ) . setupDelayedHover ( overviewFileCount as HTMLElement ,
1143
+ {
1144
+ content : title ,
1145
+ appearance : { showPointer : true , compact : true } ,
1146
+ position : { hoverPosition : HoverPosition . ABOVE }
1147
+ } ) ;
1148
+ } else {
1149
+ this . _chatEditsFileLimitHover . clear ( ) ;
1103
1150
}
1104
1151
1105
1152
// Clear out the previous actions (if any)
@@ -1166,12 +1213,13 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
1166
1213
}
1167
1214
1168
1215
const maxItemsShown = 6 ;
1169
- const itemsShown = Math . min ( entries . length , maxItemsShown ) ;
1216
+ const itemsShown = Math . min ( entries . length + excludedEntries . length , maxItemsShown ) ;
1170
1217
const height = itemsShown * 22 ;
1171
1218
const list = this . _chatEditList . object ;
1172
1219
list . layout ( height ) ;
1173
1220
list . getHTMLElement ( ) . style . height = `${ height } px` ;
1174
1221
list . splice ( 0 , list . length , entries ) ;
1222
+ list . splice ( entries . length , 0 , excludedEntries ) ;
1175
1223
this . _combinedChatEditWorkingSetEntries = coalesce ( entries . map ( ( e ) => e . kind === 'reference' && URI . isUri ( e . reference ) ? e . reference : undefined ) ) ;
1176
1224
1177
1225
const addFilesElement = innerContainer . querySelector ( '.chat-editing-session-toolbar-actions' ) as HTMLElement ?? dom . append ( innerContainer , $ ( '.chat-editing-session-toolbar-actions' ) ) ;
@@ -1183,7 +1231,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
1183
1231
// Disable the button if the entries that are not suggested exceed the budget
1184
1232
button . enabled = remainingFileEntriesBudget > 0 ;
1185
1233
button . label = localize ( 'chatAddFiles' , '{0} Add Files...' , '$(add)' ) ;
1186
- button . setTitle ( button . enabled ? localize ( 'addFiles.label' , 'Add files to your working set' ) : localize ( 'addFilesDisabled.label ' , 'You have reached the maximum number of files that can be added to the working set.' ) ) ;
1234
+ button . setTitle ( button . enabled ? localize ( 'addFiles.label' , 'Add files to your working set' ) : localize ( 'chatEditingSession.fileLimitReached ' , 'You have reached the maximum number of files that can be added to the working set.' ) ) ;
1187
1235
this . _chatEditsActionsDisposables . add ( button . onDidClick ( ( ) => {
1188
1236
this . commandService . executeCommand ( 'workbench.action.chat.editing.attachFiles' , { widget : chatWidget } ) ;
1189
1237
} ) ) ;
0 commit comments