@@ -58,11 +58,6 @@ import { ChatViewWelcomePart } from './viewsWelcome/chatViewWelcomeController.js
58
58
59
59
const $ = dom . $ ;
60
60
61
- function revealLastElement ( list : WorkbenchObjectTree < any > ) {
62
- const newScrollTop = list . scrollHeight - list . renderHeight ;
63
- list . scrollTop = newScrollTop ;
64
- }
65
-
66
61
export interface IChatViewState {
67
62
inputValue ?: string ;
68
63
inputState ?: IChatInputState ;
@@ -146,6 +141,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
146
141
private tree ! : WorkbenchObjectTree < ChatTreeItem > ;
147
142
private renderer ! : ChatListItemRenderer ;
148
143
private readonly _codeBlockModelCollection : CodeBlockModelCollection ;
144
+ private lastItem : ChatTreeItem | undefined ;
149
145
150
146
private inputPart ! : ChatInputPart ;
151
147
private editorOptions ! : ChatEditorOptions ;
@@ -434,7 +430,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
434
430
scrollDownButton . setTitle ( localize ( 'scrollDownButtonLabel' , "Scroll down" ) ) ;
435
431
this . _register ( scrollDownButton . onDidClick ( ( ) => {
436
432
this . scrollLock = true ;
437
- revealLastElement ( this . tree ) ;
433
+ this . scrollToEnd ( ) ;
438
434
} ) ) ;
439
435
440
436
this . _register ( this . editorOptions . onDidChange ( ( ) => this . onDidStyleChange ( ) ) ) ;
@@ -443,7 +439,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
443
439
// Do initial render
444
440
if ( this . viewModel ) {
445
441
this . onDidChangeItems ( ) ;
446
- revealLastElement ( this . tree ) ;
442
+ this . scrollToEnd ( ) ;
447
443
}
448
444
449
445
this . contribs = ChatWidget . CONTRIBS . map ( contrib => {
@@ -459,6 +455,13 @@ export class ChatWidget extends Disposable implements IChatWidget {
459
455
this . _register ( ( this . chatWidgetService as ChatWidgetService ) . register ( this ) ) ;
460
456
}
461
457
458
+ private scrollToEnd ( ) {
459
+ if ( this . lastItem ) {
460
+ const offset = Math . max ( this . lastItem . currentRenderedHeight ?? 0 , 1e6 ) ;
461
+ this . tree . reveal ( this . lastItem , offset ) ;
462
+ }
463
+ }
464
+
462
465
getContrib < T extends IChatWidgetContrib > ( id : string ) : T | undefined {
463
466
return this . contribs . find ( c => c . id === id ) as T ;
464
467
}
@@ -545,12 +548,12 @@ export class ChatWidget extends Disposable implements IChatWidget {
545
548
this . layoutDynamicChatTreeItemMode ( ) ;
546
549
}
547
550
548
- const lastItem = treeItems [ treeItems . length - 1 ] ?. element ;
549
- if ( lastItem ) {
550
- ChatContextKeys . lastItemId . bindTo ( this . contextKeyService ) . set ( [ lastItem . id ] ) ;
551
+ this . lastItem = treeItems [ treeItems . length - 1 ] ?. element ;
552
+ if ( this . lastItem ) {
553
+ ChatContextKeys . lastItemId . bindTo ( this . contextKeyService ) . set ( [ this . lastItem . id ] ) ;
551
554
}
552
- if ( lastItem && isResponseVM ( lastItem ) && lastItem . isComplete ) {
553
- this . renderFollowups ( lastItem . replyFollowups , lastItem ) ;
555
+ if ( this . lastItem && isResponseVM ( this . lastItem ) && this . lastItem . isComplete ) {
556
+ this . renderFollowups ( this . lastItem . replyFollowups , this . lastItem ) ;
554
557
} else if ( ! treeItems . length && this . viewModel ) {
555
558
this . renderFollowups ( this . viewModel . model . sampleQuestions ) ;
556
559
} else {
@@ -738,10 +741,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
738
741
dom . scheduleAtNextAnimationFrame ( dom . getWindow ( this . listContainer ) , ( ) => {
739
742
// Can't set scrollTop during this event listener, the list might overwrite the change
740
743
741
- // TODO This doesn't necessarily work on the first try because of the dynamic heights of items and
742
- // the way ListView works. Twice is usually enough. But this would work better if this lived inside ListView somehow
743
- revealLastElement ( this . tree ) ;
744
- revealLastElement ( this . tree ) ;
744
+ this . scrollToEnd ( ) ;
745
745
} , 0 ) ;
746
746
}
747
747
}
@@ -868,7 +868,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
868
868
869
869
this . onDidChangeItems ( ) ;
870
870
if ( events . some ( e => e ?. kind === 'addRequest' ) && this . visible ) {
871
- revealLastElement ( this . tree ) ; // Now we know how big they actually are... how do we know that 2 rounds is enough
871
+ this . scrollToEnd ( ) ;
872
872
}
873
873
874
874
if ( this . chatEditingService . currentEditingSession && this . chatEditingService . currentEditingSession ?. chatSessionId === this . viewModel ?. sessionId ) {
@@ -903,7 +903,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
903
903
904
904
if ( this . tree && this . visible ) {
905
905
this . onDidChangeItems ( ) ;
906
- revealLastElement ( this . tree ) ;
906
+ this . scrollToEnd ( ) ;
907
907
}
908
908
909
909
this . updateChatInputContext ( ) ;
@@ -1138,7 +1138,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
1138
1138
const lastItem = this . viewModel ?. getItems ( ) . at ( - 1 ) ;
1139
1139
const lastResponseIsRendering = isResponseVM ( lastItem ) && lastItem . renderData ;
1140
1140
if ( lastElementVisible && ( ! lastResponseIsRendering || this . viewOptions . autoScroll ) ) {
1141
- revealLastElement ( this . tree ) ;
1141
+ this . scrollToEnd ( ) ;
1142
1142
}
1143
1143
1144
1144
this . listContainer . style . height = `${ listHeight } px` ;
@@ -1241,8 +1241,7 @@ export class ChatWidget extends Disposable implements IChatWidget {
1241
1241
) ;
1242
1242
1243
1243
if ( needsRerender || ! listHeight ) {
1244
- // TODO: figure out a better place to reveal the last element
1245
- revealLastElement ( this . tree ) ;
1244
+ this . scrollToEnd ( ) ;
1246
1245
}
1247
1246
}
1248
1247
0 commit comments