mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-10 01:09:43 +02:00
Merge pull request #143 from TeigenZhang/pr/cjk-input-loss
fix(mobile): CJK input loss — IME state machine, focus routing, and Android InputConnection recovery
This commit is contained in:
@@ -316,6 +316,25 @@ Object.assign(CodemanApp.prototype, {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Focus router ──
|
||||
// While the CJK field is visible, EVERY terminal.focus() call must land on
|
||||
// the CJK field instead. Focusing xterm's hidden textarea in CJK mode sends
|
||||
// the IME's output into a black hole: the keyboard composes normally, but
|
||||
// onData is gated by cjkActive, so nothing reaches the field OR the PTY.
|
||||
// Session select / SSE-reconnect restore paths call terminal.focus() and
|
||||
// were silently stealing focus after every app switch on mobile (the
|
||||
// intermittent "Chinese input goes nowhere" bug). One chokepoint here
|
||||
// covers all ~15 call sites plus any future ones.
|
||||
const _xtermFocus = this.terminal.focus.bind(this.terminal);
|
||||
this.terminal.focus = () => {
|
||||
const cjkEl = document.getElementById('cjkInput');
|
||||
if (cjkEl?.classList.contains('cjk-input-visible')) {
|
||||
cjkEl.focus();
|
||||
} else {
|
||||
_xtermFocus();
|
||||
}
|
||||
};
|
||||
|
||||
// On mobile Safari, delay initial fit() to allow layout to settle
|
||||
// This prevents 0-column terminals caused by fit() running before container is sized
|
||||
const isMobileSafari =
|
||||
@@ -638,7 +657,28 @@ Object.assign(CodemanApp.prototype, {
|
||||
// is on, because cjkActive stays true the whole time the field is visible.
|
||||
const isMouseReport = /^\x1b\[<\d+;\d+;\d+[Mm]$/.test(data);
|
||||
// CJK input has focus — block xterm from sending keystrokes to PTY
|
||||
if (!isMouseReport && (window.cjkActive || document.activeElement?.id === 'cjkInput')) return;
|
||||
if (!isMouseReport && (window.cjkActive || document.activeElement?.id === 'cjkInput')) {
|
||||
// Self-heal: if the CJK field is visible but focus drifted to xterm's
|
||||
// hidden textarea (e.g. something called terminal.focus()), everything
|
||||
// typed lands HERE and is swallowed — keyboard shows the IME composing
|
||||
// while both the CJK field and the terminal stay empty. Route focus
|
||||
// back so the very next keystroke lands in the CJK field again.
|
||||
// Only GENUINE typed input qualifies: onData also fires for xterm's
|
||||
// self-generated query replies (DA/DSR/CPR/OSC during Ink redraws),
|
||||
// which arrive no matter what has focus — so require focus to be on
|
||||
// xterm's own textarea and bail on query replies, or this would steal
|
||||
// focus from the rename/search/settings inputs while output streams.
|
||||
const cjkEl = document.getElementById('cjkInput');
|
||||
if (
|
||||
cjkEl?.classList.contains('cjk-input-visible') &&
|
||||
document.activeElement === this.terminal.textarea &&
|
||||
!window.CodemanTerminalInput?.shouldSuppressTerminalQueryResponse(data)
|
||||
) {
|
||||
_crashDiag.log('CJK regain-focus (onData swallowed input)');
|
||||
cjkEl.focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.activeSessionId) {
|
||||
// Filter terminal query replies generated by xterm.js itself.
|
||||
// Forwarding them through the WebSocket injects DA/DSR/CPR replies
|
||||
@@ -1633,7 +1673,11 @@ Object.assign(CodemanApp.prototype, {
|
||||
// CJK textarea already provides visual feedback — bypass local echo
|
||||
// buffering so each composed word reaches the PTY immediately.
|
||||
_handleCjkInput(text) {
|
||||
if (!this.activeSessionId) return;
|
||||
if (!this.activeSessionId) {
|
||||
_crashDiag.log(`CJK send DROP no-session len=${text.length}`);
|
||||
return;
|
||||
}
|
||||
_crashDiag.log(`CJK send→${this.activeSessionId.slice(0, 8)} len=${text.length}`);
|
||||
this._sendInputAsync(this.activeSessionId, text);
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user