fix: apply keybar modifiers to physical keyboard input

On iPad with a hardware keyboard, keybar modifiers were cleared without
being applied if the textarea wasn't focused. Now keybar Ctrl/Shift
intercepts document-level keydown events, applies the modifiers to the
pressed key, sends the correct sequence, and then clears the modifiers.
This commit is contained in:
GitHub Copilot
2026-01-29 23:00:55 +00:00
parent 902b545aa5
commit 3bf68117a8
2 changed files with 73 additions and 10 deletions
File diff suppressed because one or more lines are too long
+66 -3
View File
@@ -748,7 +748,7 @@ class WebTerminal {
} }
}); });
// Clear keybar modifiers when physical keyboard input occurs outside the textarea. // Apply keybar modifiers to physical keyboard input even when the textarea isn't focused.
document.addEventListener("keydown", (event) => { document.addEventListener("keydown", (event) => {
if (!this.ctrlActive && !this.shiftActive) { if (!this.ctrlActive && !this.shiftActive) {
return; return;
@@ -756,10 +756,73 @@ class WebTerminal {
if (event.target === this.mobileInput) { if (event.target === this.mobileInput) {
return; return;
} }
if (["Shift", "Control", "Alt", "Meta"].includes(event.key)) {
return; const useCtrl = this.ctrlActive;
const useShift = this.shiftActive;
let handled = false;
if (event.key.length === 1 && !event.altKey && !event.metaKey) {
let toSend = event.key;
if (useShift) {
toSend = toSend.toUpperCase();
} }
if (useCtrl) {
const code = toSend.toUpperCase().charCodeAt(0);
if (code >= 65 && code <= 90) {
toSend = String.fromCharCode(code - 64);
}
}
event.preventDefault();
this.send(["stdin", toSend]);
handled = true;
} else {
let seq: string | null = null;
switch (event.key) {
case "Escape":
seq = "\x1b";
break;
case "ArrowUp":
case "ArrowDown":
case "ArrowRight":
case "ArrowLeft": {
const dir =
event.key === "ArrowUp"
? "A"
: event.key === "ArrowDown"
? "B"
: event.key === "ArrowRight"
? "C"
: "D";
if (useCtrl && useShift) {
seq = `\x1b[1;6${dir}`;
} else if (useCtrl) {
seq = `\x1b[1;5${dir}`;
} else if (useShift) {
seq = `\x1b[1;2${dir}`;
} else {
seq = `\x1b[${dir}`;
}
break;
}
case "Tab":
if (useShift) {
seq = "\x1b[Z";
} else {
seq = "\t";
}
break;
}
if (seq) {
event.preventDefault();
this.send(["stdin", seq]);
handled = true;
}
}
if (handled) {
this.deactivateModifiers(); this.deactivateModifiers();
}
}); });
// Focus textarea on touch/click to show mobile keyboard // Focus textarea on touch/click to show mobile keyboard