Skip to content

fix(mobile): take-control tap leaks to xterm on iOS, opens keyboard#90

Merged
attson merged 1 commit into
mainfrom
fix/mobile-take-control-tap
May 31, 2026
Merged

fix(mobile): take-control tap leaks to xterm on iOS, opens keyboard#90
attson merged 1 commit into
mainfrom
fix/mobile-take-control-tap

Conversation

@attson
Copy link
Copy Markdown
Owner

@attson attson commented May 31, 2026

Repro

On iOS, when the desktop is the active driver and the phone is viewing, the mobile view shows a "远端正在控制 / 接管控制" overlay. Tapping "接管控制" doesn't take control — the on-screen keyboard pops up instead and the overlay stays visible.

Root cause

The take-control button is inside `.viewer-overlay`, a sibling of `.term`. xterm hosts a hidden `<textarea>` (the helper textarea for IME) inside `.term`. On a fast (LAN) relay the sub-100ms roundtrip from `claim_driver` to the broadcast driver-change can land between iOS's `touchstart` and the synthesized click event:

  1. touchstart on the button
  2. `claimDriver()` fires from a non-click path, or the click handler fires
  3. Relay broadcasts driver-change → `isDriver=true` → overlay is removed from DOM
  4. iOS synthesizes the click at the touch location, which is now on `.term` / `.xterm` / the helper textarea — focusing it and opening the keyboard

Even when the click does hit the button correctly, the same shape of redirection can mis-fire if the underlying element changes mid-touch.

Fix

Set `.term { pointer-events: none }` whenever `!isDriver`, so the only interactive surface under the touch is the overlay itself. The button can fire its click reliably; the xterm textarea is never reachable while the overlay is up.

Defense-in-depth on the button:

  • `type="button"` (was implicit-but-correct; pinned explicitly so future `` wraps don't accidentally make it submit)
  • `@click.stop` so the click event can never bubble to a delegated handler on a parent

Test plan

  • new vitest case asserts `.term` gets the `inert` class while `!isDriver` and loses it when driver
  • existing `shows viewer overlay when not driver; take-control calls claimDriver` test still passes
  • `npm test` — 73 files / 615 tests green
  • `npx vue-tsc --noEmit` — clean
  • manual: on iOS Safari / Capacitor build, with desktop driving and phone viewing, tap "接管控制" — the overlay should disappear and no keyboard pops up

…rd popup)

On iOS the take-control button is rendered inside .viewer-overlay, a
sibling of .term. Between touchstart and the synthesized click event a
sub-100ms relay roundtrip can flip isDriver and remove the overlay; iOS
then redirects the resulting click to whatever sits under the touch
point — xterm — focusing its hidden helper textarea and popping up the
on-screen keyboard. The take-control click effectively never fires.

Make .term pointer-events: none while !isDriver so a tap can only ever
land on the overlay button. Also pin button type="button" + @click.stop
as defense in depth.
@attson attson merged commit 3fea50f into main May 31, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant