@@ -33,6 +33,9 @@ interface MockClipboardEvent {
3333interface MockHTMLElement {
3434 addEventListener : ( event : string , handler : ( e : any ) => void ) => void ;
3535 removeEventListener : ( event : string , handler : ( e : any ) => void ) => void ;
36+ childNodes : Node [ ] ;
37+ removeChild : ( node : Node ) => Node ;
38+ appendChild : ( node : Node ) => Node ;
3639}
3740
3841// Helper to create mock keyboard event
@@ -125,6 +128,19 @@ function createMockContainer(): MockHTMLElement & {
125128 handler ( event ) ;
126129 }
127130 } ,
131+ // Mock childNodes and removeChild for text node cleanup test
132+ childNodes : [ ] as Node [ ] ,
133+ removeChild ( node : Node ) {
134+ const index = this . childNodes . indexOf ( node ) ;
135+ if ( index >= 0 ) {
136+ this . childNodes . splice ( index , 1 ) ;
137+ }
138+ return node ;
139+ } ,
140+ appendChild ( node : Node ) {
141+ this . childNodes . push ( node ) ;
142+ return node ;
143+ }
128144 } ;
129145}
130146
@@ -353,6 +369,36 @@ describe('InputHandler', () => {
353369 container . dispatchEvent ( createCompositionEvent ( 'compositionend' , 'a' ) ) ;
354370 expect ( dataReceived ) . toEqual ( [ 'a' ] ) ;
355371 } ) ;
372+
373+ test ( 'cleans up text nodes in container after composition' , ( ) => {
374+ const handler = new InputHandler (
375+ ghostty ,
376+ container as any ,
377+ ( data ) => dataReceived . push ( data ) ,
378+ ( ) => {
379+ bellCalled = true ;
380+ }
381+ ) ;
382+
383+ // Simulate browser inserting text node during composition
384+ const textNode = { nodeType : 3 , textContent : '你好' } as Node ;
385+ container . appendChild ( textNode ) ;
386+
387+ // Also add a non-text node (e.g. canvas) to ensure it's not removed
388+ const elementNode = { nodeType : 1 , nodeName : 'CANVAS' } as Node ;
389+ container . appendChild ( elementNode ) ;
390+
391+ expect ( container . childNodes . length ) . toBe ( 2 ) ;
392+
393+ // End composition
394+ const endEvent = createCompositionEvent ( 'compositionend' , '你好' ) ;
395+ container . dispatchEvent ( endEvent ) ;
396+
397+ // Should have removed the text node but kept the element node
398+ expect ( container . childNodes . length ) . toBe ( 1 ) ;
399+ expect ( container . childNodes [ 0 ] ) . toBe ( elementNode ) ;
400+ expect ( dataReceived ) . toEqual ( [ '你好' ] ) ;
401+ } ) ;
356402 } ) ;
357403
358404 describe ( 'Control Characters' , ( ) => {
0 commit comments