mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
fix: prevent double character input on Android Shift+key
On Android tablets, pressing Shift+A produces "AA" because the input event listener re-sends characters that xterm already processed via its keydown handler. Track keydown timestamps and skip input events that fire within 50ms of a handled keydown. Only affects touch devices (listener gated by isTouchDevice()).
This commit is contained in:
@@ -122,21 +122,45 @@ Object.assign(CodemanApp.prototype, {
|
|||||||
// On Android Chrome, typing symbols (e.g., "/" from Gboard's symbol keyboard)
|
// On Android Chrome, typing symbols (e.g., "/" from Gboard's symbol keyboard)
|
||||||
// sends keyCode 229 + input event WITHOUT compositionstart/end wrapping.
|
// sends keyCode 229 + input event WITHOUT compositionstart/end wrapping.
|
||||||
// The custom key handler above returns false for keyCode 229, telling xterm
|
// The custom key handler above returns false for keyCode 229, telling xterm
|
||||||
// to ignore the keydown. This listener catches those orphaned input events.
|
// to ignore the keydown. xterm.js expects the character to arrive via
|
||||||
|
// composition events, but since there's no composition, the character is lost.
|
||||||
|
// This listener catches those orphaned input events and forwards them to onData.
|
||||||
{
|
{
|
||||||
const xtermTextarea = container.querySelector('.xterm-helper-textarea');
|
const xtermTextarea = container.querySelector('.xterm-helper-textarea');
|
||||||
if (xtermTextarea && MobileDetection.isTouchDevice()) {
|
if (xtermTextarea && MobileDetection.isTouchDevice()) {
|
||||||
let composing = false;
|
let composing = false;
|
||||||
|
let lastKeydownHandled = 0;
|
||||||
xtermTextarea.addEventListener('compositionstart', () => { composing = true; });
|
xtermTextarea.addEventListener('compositionstart', () => { composing = true; });
|
||||||
xtermTextarea.addEventListener('compositionend', () => { composing = false; });
|
xtermTextarea.addEventListener('compositionend', () => { composing = false; });
|
||||||
|
// Track when xterm handles a keydown normally (non-229 keyCode).
|
||||||
|
// If xterm processed the keydown, it will emit onData itself --
|
||||||
|
// the input event handler below must NOT re-send the character.
|
||||||
|
xtermTextarea.addEventListener('keydown', (e) => {
|
||||||
|
if (!e.isComposing && e.keyCode !== 229) {
|
||||||
|
lastKeydownHandled = Date.now();
|
||||||
|
}
|
||||||
|
});
|
||||||
xtermTextarea.addEventListener('input', (e) => {
|
xtermTextarea.addEventListener('input', (e) => {
|
||||||
|
// Only handle insertText events outside of composition -- these are
|
||||||
|
// the ones xterm.js misses on Android virtual keyboards.
|
||||||
if (composing || e.isComposing) return;
|
if (composing || e.isComposing) return;
|
||||||
if (e.inputType !== 'insertText' || !e.data) return;
|
if (e.inputType !== 'insertText' || !e.data) return;
|
||||||
|
// If xterm just handled a keydown (within 50ms), it already sent the
|
||||||
|
// char via onData. Skip to avoid double-send (e.g., Shift+A => AA).
|
||||||
|
if (Date.now() - lastKeydownHandled < 50) return;
|
||||||
|
// xterm.js may have already processed this via its own input handler.
|
||||||
|
// Check if the textarea was cleared by xterm (value is empty or just
|
||||||
|
// whitespace) -- if so, xterm handled it and we should not double-send.
|
||||||
|
// Use a microtask to check after xterm's own handlers have run.
|
||||||
const data = e.data;
|
const data = e.data;
|
||||||
Promise.resolve().then(() => {
|
Promise.resolve().then(() => {
|
||||||
|
// If xterm cleared the textarea, it processed the input -- skip.
|
||||||
const val = xtermTextarea.value;
|
const val = xtermTextarea.value;
|
||||||
if (!val || val.trim() === '') return;
|
if (!val || val.trim() === '') return;
|
||||||
|
// xterm didn't process it -- forward to terminal as if typed.
|
||||||
|
// Emit via onData path by writing to terminal's input handler.
|
||||||
this.terminal._core.coreService.triggerDataEvent(data, true);
|
this.terminal._core.coreService.triggerDataEvent(data, true);
|
||||||
|
// Clear the textarea to prevent xterm from processing it later.
|
||||||
xtermTextarea.value = '';
|
xtermTextarea.value = '';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user