@@ -263,6 +263,7 @@ export class TextComponent implements AfterViewInit, OnDestroy {
263263 private presenceActiveEditor$ : Subject < boolean > = new Subject < boolean > ( ) ;
264264 private onPresenceDocReceive = ( _presenceId : string , _range : Range | null ) : void => { } ;
265265 private onPresenceChannelReceive = ( _presenceId : string , _presenceData : PresenceData | null ) : void => { } ;
266+ private isShiftDown = false ;
266267
267268 constructor (
268269 private readonly destroyRef : DestroyRef ,
@@ -525,6 +526,24 @@ export class TextComponent implements AfterViewInit, OnDestroy {
525526 . subscribe ( ( ) => {
526527 this . updateLocalCursor ( ) ;
527528 } ) ;
529+
530+ fromEvent < KeyboardEvent > ( document , 'keydown' )
531+ . pipe ( quietTakeUntilDestroyed ( this . destroyRef ) )
532+ . subscribe ( event => {
533+ this . isShiftDown = event . shiftKey ;
534+ } ) ;
535+
536+ fromEvent < KeyboardEvent > ( document , 'keyup' )
537+ . pipe ( quietTakeUntilDestroyed ( this . destroyRef ) )
538+ . subscribe ( event => {
539+ // Call 'update()' when shift key is released, as update is disabled while shift is down
540+ // to prevent incorrect cursor position updates while selecting text.
541+ if ( this . isShiftDown && ! event . shiftKey ) {
542+ this . update ( ) ;
543+ }
544+
545+ this . isShiftDown = event . shiftKey ;
546+ } ) ;
528547 }
529548
530549 ngOnDestroy ( ) : void {
@@ -827,7 +846,13 @@ export class TextComponent implements AfterViewInit, OnDestroy {
827846 }
828847
829848 async onSelectionChanged ( range : Range | null ) : Promise < void > {
830- this . update ( ) ;
849+ // During selection expansion (keyboard or mouse), avoid calling update()
850+ // which can cause incorrect cursor position updates.
851+ // Update will be called once the shift key is released.
852+ if ( ! this . isShiftDown ) {
853+ this . update ( ) ;
854+ }
855+
831856 this . submitLocalPresenceDoc ( range ) ;
832857 }
833858
0 commit comments