fix: handle terminal keyboard input on Android tablet (#1133)
* fix: handle terminal keyboard input on Android with desktop UA - Detect Android via navigator.userAgentData.platform when UA is spoofed by Chrome's "Desktop site" mode, so the <input> overlay (useTextInput) is used instead of <textarea>. - Handle Ctrl+letter and Alt+letter combos in the hidden input overlay's keydown handler, so terminal control sequences (^C, ^D, ^S, M-x, etc.) work through the touch input path. - Skip global keyboard shortcuts (sidebar toggle, etc.) when focus is inside the terminal viewport, preventing shortcut steal. * fix: preserve terminal keyboard shortcuts --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
d5b3590901
commit
b65edc3436
@@ -128,7 +128,10 @@ const TerminalViewport = React.forwardRef<TerminalController, TerminalViewportPr
|
||||
inputHandlerRef.current = onInput;
|
||||
resizeHandlerRef.current = onResize;
|
||||
|
||||
const isAndroid = typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent);
|
||||
const isAndroid = typeof navigator !== 'undefined' && (
|
||||
/Android/i.test(navigator.userAgent) ||
|
||||
(navigator as { userAgentData?: { platform: string } }).userAgentData?.platform === 'Android'
|
||||
);
|
||||
// Touch devices need a dedicated editable surface so special keys like
|
||||
// Backspace and arrows are captured reliably without relying on Ghostty's
|
||||
// internal mobile text handling.
|
||||
@@ -1409,6 +1412,25 @@ const TerminalViewport = React.forwardRef<TerminalController, TerminalViewportPr
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.ctrlKey && !event.metaKey && !event.altKey && event.key.length === 1) {
|
||||
const upper = event.key.toUpperCase();
|
||||
if (upper >= 'A' && upper <= 'Z') {
|
||||
event.preventDefault();
|
||||
(event.nativeEvent as KeyboardEvent | undefined)?.stopImmediatePropagation();
|
||||
inputHandlerRef.current(String.fromCharCode(upper.charCodeAt(0) - 64));
|
||||
clearEditableValue(event.currentTarget as HTMLElement);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.altKey && !event.ctrlKey && !event.metaKey && event.key.length === 1) {
|
||||
event.preventDefault();
|
||||
(event.nativeEvent as KeyboardEvent | undefined)?.stopImmediatePropagation();
|
||||
inputHandlerRef.current('\x1b' + event.key);
|
||||
clearEditableValue(event.currentTarget as HTMLElement);
|
||||
return;
|
||||
}
|
||||
|
||||
const specialKeySequences: Record<string, string> = {
|
||||
ArrowUp: '\u001b[A',
|
||||
ArrowDown: '\u001b[B',
|
||||
|
||||
@@ -64,7 +64,6 @@ export const useKeyboardShortcuts = () => {
|
||||
target.getAttribute('data-terminal-hidden-input') === 'true'
|
||||
);
|
||||
};
|
||||
|
||||
const handleTerminalShortcutCapture = (e: KeyboardEvent) => {
|
||||
if (!isTerminalEventTarget(e.target)) {
|
||||
return;
|
||||
@@ -94,6 +93,10 @@ export const useKeyboardShortcuts = () => {
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (isTerminalEventTarget(e.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventMatchesShortcut(e, combo('open_command_palette'))) {
|
||||
e.preventDefault();
|
||||
toggleCommandPalette();
|
||||
|
||||
Reference in New Issue
Block a user