@@ -398,21 +398,18 @@ export class SelectionManager {
398398 // ==========================================================================
399399
400400 /**
401- * Attach mouse event listeners to canvas
401+ * Attach mouse event listeners to textarea (which overlays the canvas)
402+ * The textarea is on top to receive touch events for mobile keyboard support
402403 */
403404 private attachEventListeners ( ) : void {
404405 const canvas = this . renderer . getCanvas ( ) ;
406+ const target = this . textarea ; // Events go to textarea since it's on top
405407
406408 // Mouse down - start selection or clear existing
407- canvas . addEventListener ( 'mousedown' , ( e : MouseEvent ) => {
409+ target . addEventListener ( 'mousedown' , ( e : MouseEvent ) => {
408410 if ( e . button === 0 ) {
409411 // Left click only
410-
411- // CRITICAL: Focus the terminal so it can receive keyboard input
412- // The canvas doesn't have tabindex, but the parent container does
413- if ( canvas . parentElement ) {
414- canvas . parentElement . focus ( ) ;
415- }
412+ // Textarea is already focused by the click, which triggers mobile keyboard
416413
417414 const cell = this . pixelToCell ( e . offsetX , e . offsetY ) ;
418415
@@ -430,8 +427,8 @@ export class SelectionManager {
430427 }
431428 } ) ;
432429
433- // Mouse move on canvas - update selection
434- canvas . addEventListener ( 'mousemove' , ( e : MouseEvent ) => {
430+ // Mouse move on textarea - update selection
431+ target . addEventListener ( 'mousemove' , ( e : MouseEvent ) => {
435432 if ( this . isSelecting ) {
436433 // Mark current selection rows as dirty before updating
437434 this . markCurrentSelectionDirty ( ) ;
@@ -442,15 +439,15 @@ export class SelectionManager {
442439 this . requestRender ( ) ;
443440
444441 // Check if near edges for auto-scroll
445- this . updateAutoScroll ( e . offsetY , canvas . clientHeight ) ;
442+ this . updateAutoScroll ( e . offsetY , target . clientHeight ) ;
446443 }
447444 } ) ;
448445
449- // Mouse leave - check for auto-scroll when leaving canvas during drag
450- canvas . addEventListener ( 'mouseleave' , ( e : MouseEvent ) => {
446+ // Mouse leave - check for auto-scroll when leaving during drag
447+ target . addEventListener ( 'mouseleave' , ( e : MouseEvent ) => {
451448 if ( this . isSelecting ) {
452449 // Determine scroll direction based on where mouse left
453- const rect = canvas . getBoundingClientRect ( ) ;
450+ const rect = target . getBoundingClientRect ( ) ;
454451 if ( e . clientY < rect . top ) {
455452 this . startAutoScroll ( - 1 ) ; // Scroll up
456453 } else if ( e . clientY > rect . bottom ) {
@@ -459,8 +456,8 @@ export class SelectionManager {
459456 }
460457 } ) ;
461458
462- // Mouse enter - stop auto-scroll when mouse returns to canvas
463- canvas . addEventListener ( 'mouseenter' , ( ) => {
459+ // Mouse enter - stop auto-scroll when mouse returns
460+ target . addEventListener ( 'mouseenter' , ( ) => {
464461 if ( this . isSelecting ) {
465462 this . stopAutoScroll ( ) ;
466463 }
@@ -533,7 +530,7 @@ export class SelectionManager {
533530 document . addEventListener ( 'mouseup' , this . boundMouseUpHandler ) ;
534531
535532 // Double-click - select word
536- canvas . addEventListener ( 'dblclick' , ( e : MouseEvent ) => {
533+ target . addEventListener ( 'dblclick' , ( e : MouseEvent ) => {
537534 const cell = this . pixelToCell ( e . offsetX , e . offsetY ) ;
538535 const word = this . getWordAtCell ( cell . col , cell . row ) ;
539536
@@ -583,17 +580,18 @@ export class SelectionManager {
583580 // Focus the textarea so the context menu appears on it
584581 this . textarea . focus ( ) ;
585582
586- // After a short delay, restore the textarea to its hidden state
583+ // After a short delay, restore the textarea to its normal overlay state
587584 // This allows the context menu to appear first
588585 setTimeout ( ( ) => {
589586 // Listen for when the context menu closes (user clicks away or selects an option)
590587 const resetTextarea = ( ) => {
591- this . textarea . style . pointerEvents = 'none' ;
592- this . textarea . style . zIndex = '-10' ;
593- this . textarea . style . width = '0' ;
594- this . textarea . style . height = '0' ;
588+ // Restore textarea to full overlay state for mobile keyboard support
589+ this . textarea . style . position = 'absolute' ;
595590 this . textarea . style . left = '0' ;
596591 this . textarea . style . top = '0' ;
592+ this . textarea . style . width = '100%' ;
593+ this . textarea . style . height = '100%' ;
594+ this . textarea . style . zIndex = '1' ;
597595 this . textarea . value = '' ;
598596
599597 // Remove the one-time listeners
@@ -611,7 +609,7 @@ export class SelectionManager {
611609 // Don't prevent default - let browser show the context menu on the textarea
612610 } ;
613611
614- canvas . addEventListener ( 'contextmenu' , this . boundContextMenuHandler ) ;
612+ target . addEventListener ( 'contextmenu' , this . boundContextMenuHandler ) ;
615613
616614 // Click outside canvas - clear selection
617615 // This allows users to deselect by clicking anywhere outside the terminal
0 commit comments