Skip to content

Commit 09de762

Browse files
committed
Add mobile keyboard support
Focus textarea on touch/click to trigger mobile keyboard. Use clip-path to hide iOS caret.
1 parent e297e6c commit 09de762

File tree

1 file changed

+23
-10
lines changed

1 file changed

+23
-10
lines changed

lib/terminal.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -345,29 +345,42 @@ export class Terminal implements ITerminalCore {
345345
this.canvas.style.display = 'block';
346346
parent.appendChild(this.canvas);
347347

348-
// Create hidden textarea for clipboard operations (xterm.js pattern)
349-
// This textarea will be positioned under the mouse cursor during right-clicks
350-
// to enable the browser's native context menu with Copy/Paste options
348+
// Create hidden textarea for keyboard input (must be inside parent for event bubbling)
351349
this.textarea = document.createElement('textarea');
352350
this.textarea.setAttribute('autocorrect', 'off');
353351
this.textarea.setAttribute('autocapitalize', 'off');
354352
this.textarea.setAttribute('spellcheck', 'false');
355-
this.textarea.setAttribute('tabindex', '-1'); // Don't interfere with tab navigation
353+
this.textarea.setAttribute('tabindex', '0'); // Allow focus for mobile keyboard
356354
this.textarea.setAttribute('aria-label', 'Terminal input');
355+
// Use clip-path to completely hide the textarea and its caret
357356
this.textarea.style.position = 'absolute';
358357
this.textarea.style.left = '0';
359358
this.textarea.style.top = '0';
360-
this.textarea.style.width = '0';
361-
this.textarea.style.height = '0';
362-
this.textarea.style.zIndex = '-10';
359+
this.textarea.style.width = '1px';
360+
this.textarea.style.height = '1px';
361+
this.textarea.style.padding = '0';
362+
this.textarea.style.border = 'none';
363+
this.textarea.style.margin = '0';
363364
this.textarea.style.opacity = '0';
365+
this.textarea.style.clipPath = 'inset(50%)'; // Clip everything including caret
364366
this.textarea.style.overflow = 'hidden';
365-
this.textarea.style.pointerEvents = 'none'; // Don't interfere with mouse events normally
367+
this.textarea.style.whiteSpace = 'nowrap';
366368
this.textarea.style.resize = 'none';
367-
this.textarea.style.border = 'none';
368-
this.textarea.style.outline = 'none';
369369
parent.appendChild(this.textarea);
370370

371+
// Focus textarea on interaction - preventDefault before focus
372+
const textarea = this.textarea;
373+
// Desktop: mousedown
374+
this.canvas.addEventListener('mousedown', (ev) => {
375+
ev.preventDefault();
376+
textarea.focus();
377+
});
378+
// Mobile: touchend with preventDefault to suppress iOS caret
379+
this.canvas.addEventListener('touchend', (ev) => {
380+
ev.preventDefault();
381+
textarea.focus();
382+
});
383+
371384
// Create renderer
372385
this.renderer = new CanvasRenderer(this.canvas, {
373386
fontSize: this.options.fontSize,

0 commit comments

Comments
 (0)