|
| 1 | +# 移动端终端自定义虚拟按键会重新唤起系统软键盘 |
| 2 | + |
| 3 | +## 标题 |
| 4 | + |
| 5 | +`fix(web): 移动端终端自定义虚拟按键不应重新唤起系统软键盘` |
| 6 | + |
| 7 | +## 问题描述 |
| 8 | + |
| 9 | +在 `coder-studio` 的移动端 terminal 场景下,用户先让终端进入可输入状态,再手动收起系统软键盘,随后点击终端底部我们自己实现的虚拟按键行,例如 `Ctrl`、`Shift`、`Esc`、`Tab`、方向键或 `Enter`,系统软键盘会再次被唤起。 |
| 10 | + |
| 11 | +这排按键的设计目标是补足移动端终端缺失的控制键,不应该在用户已经手动收起系统软键盘后,又把键盘重新拉起。 |
| 12 | + |
| 13 | +## 复现步骤 |
| 14 | + |
| 15 | +1. 在手机或平板上打开 `coder-studio` 的 terminal。 |
| 16 | +2. 点击 terminal,让系统软键盘弹出。 |
| 17 | +3. 手动收起系统软键盘。 |
| 18 | +4. 点击 terminal 底部自定义虚拟按键行中的任意键,例如 `Ctrl`、`Shift`、`Esc` 或方向键。 |
| 19 | +5. 观察系统软键盘重新弹出。 |
| 20 | + |
| 21 | +## 预期行为 |
| 22 | + |
| 23 | +- 当系统软键盘已经打开时,点击自定义虚拟按键不应强制把它收掉。 |
| 24 | +- 当系统软键盘已经被用户手动收起时,点击自定义虚拟按键也不应再次把它唤起。 |
| 25 | +- 自定义虚拟按键应只向终端发送控制输入,而不改变当前系统软键盘的可见状态。 |
| 26 | + |
| 27 | +## 实际行为 |
| 28 | + |
| 29 | +用户手动收起系统软键盘后,点击自定义虚拟按键仍会导致系统软键盘再次弹出。 |
| 30 | + |
| 31 | +## 已确认事实 |
| 32 | + |
| 33 | +- 该问题涉及的是 terminal 内部我们自己实现的移动端虚拟按键行,不是系统软键盘本身。 |
| 34 | +- 自定义虚拟按键的数据链路是: |
| 35 | + - `MobileTerminalInputBar` |
| 36 | + - `handleSoftKeyPress` |
| 37 | + - `handleInput` |
| 38 | + - `wsClient.sendTerminalInput` |
| 39 | +- 自定义虚拟按键并不是通过 `xterm.onData(...)` 触发输入;它们不会先走 xterm 的浏览器键盘事件采集路径。 |
| 40 | +- `xterm` 在浏览器里依赖隐藏的 `textarea`(`.xterm-helper-textarea`)承接键盘、IME 与移动端输入。 |
| 41 | +- `xterm` 自身维护这个隐藏 `textarea` 的焦点状态,并通过 `focus()` / `blur()` 管理终端输入上下文。 |
| 42 | +- 在移动端浏览器里,用户“收起软键盘”不一定等价于输入元素真正 `blur`;隐藏 `textarea` 可能仍保持焦点。 |
| 43 | +- 当前自定义虚拟按键在 `pointerdown` 上使用了 `preventDefault()`,因此按钮本身不会接管焦点。 |
| 44 | +- 当按钮不接管焦点,而 `xterm` 的隐藏 `textarea` 仍处于焦点上下文时,浏览器可能在下一次触摸交互时重新恢复系统软键盘。 |
| 45 | + |
| 46 | +## 当前判断 |
| 47 | + |
| 48 | +问题更像是: |
| 49 | + |
| 50 | +- `xterm` 的隐藏 `textarea` 在用户收起软键盘后仍保持焦点或可恢复的输入上下文 |
| 51 | +- 自定义虚拟按键由于 `preventDefault()` 没有改变当前焦点归属 |
| 52 | +- 浏览器在后续触摸时将该焦点中的文本输入上下文重新激活,于是系统软键盘再次弹出 |
| 53 | + |
| 54 | +也就是说,根因不在“自定义虚拟按键错误地走进了 xterm 的 `onData`”,而在 `xterm` 隐藏输入层的焦点生命周期与移动端浏览器软键盘策略之间的耦合。 |
| 55 | + |
| 56 | +## 已排除方向 |
| 57 | + |
| 58 | +- 不是自定义虚拟按键通过 `xterm.onData(...)` 间接触发的输入问题。 |
| 59 | +- 不是 PTY / WebSocket 数据发送链路导致系统软键盘弹出。 |
| 60 | +- 直接对 `document.activeElement` 做全局 `blur()` 虽然能验证问题与焦点有关,但方案过粗,可能引入键盘闪烁、误伤其他输入元素等副作用,不适合作为最终修复。 |
| 61 | + |
| 62 | +## 后续排查方向 |
| 63 | + |
| 64 | +- 在真实移动端浏览器上记录以下事件时序: |
| 65 | + - 用户手动收起软键盘前后 |
| 66 | + - 点击自定义虚拟按键前后 |
| 67 | + - `document.activeElement` |
| 68 | + - `xterm.textarea` 的 `focus` / `blur` |
| 69 | + - `visualViewport.height` 变化 |
| 70 | +- 确认“软键盘收起但 `xterm.textarea` 仍保持焦点”是否稳定可复现,以及在不同浏览器上的差异。 |
| 71 | +- 评估是否需要在移动端引入更精确的终端输入状态机,例如区分: |
| 72 | + - 系统键盘打开 |
| 73 | + - 系统键盘已被用户收起 |
| 74 | + - 仅使用自定义虚拟按键输入 |
| 75 | +- 如果需要修复,优先考虑: |
| 76 | + - 仅围绕 `xterm.textarea` 做精确的 `focus` / `blur` 管理 |
| 77 | + - 避免对全局 `activeElement` 做无条件处理 |
| 78 | + - 保证“键盘开着时不强制收掉,键盘关着时不重新拉起” |
0 commit comments