@@ -414,7 +414,16 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
414414 this . _register ( addDisposableListener ( this . textarea ! , 'keyup' , ( ev : KeyboardEvent ) => this . _keyUp ( ev ) , true ) ) ;
415415 this . _register ( addDisposableListener ( this . textarea ! , 'keydown' , ( ev : KeyboardEvent ) => this . _keyDown ( ev ) , true ) ) ;
416416 this . _register ( addDisposableListener ( this . textarea ! , 'keypress' , ( ev : KeyboardEvent ) => this . _keyPress ( ev ) , true ) ) ;
417- this . _register ( addDisposableListener ( this . textarea ! , 'compositionstart' , ( ) => this . _compositionHelper ! . compositionstart ( ) ) ) ;
417+ this . _register ( addDisposableListener ( this . textarea ! , 'compositionstart' , ( ) => {
418+ // Ensure the textarea is synced to the latest cursor location before composition begins. This
419+ // is to workaround a problem where highly dynamic TUIs like agentic CLIs reprint agressively
420+ // would cause the IME to appear in the wrong position. The theory is that when the IME is
421+ // triggered during a partial render the textarea position becomes locked and will not move
422+ // until it is hidden and a custom move occurs.
423+ this . _syncTextArea ( ) ;
424+ this . _compositionHelper ! . compositionstart ( ) ;
425+ this . _compositionHelper ! . updateCompositionElements ( ) ;
426+ } ) ) ;
418427 this . _register ( addDisposableListener ( this . textarea ! , 'compositionupdate' , ( e : CompositionEvent ) => this . _compositionHelper ! . compositionupdate ( e ) ) ) ;
419428 this . _register ( addDisposableListener ( this . textarea ! , 'compositionend' , ( ) => this . _compositionHelper ! . compositionend ( ) ) ) ;
420429 this . _register ( addDisposableListener ( this . textarea ! , 'input' , ( ev : InputEvent ) => this . _inputEvent ( ev ) , true ) ) ;
0 commit comments