#279 and #280 auto-merge cleanly, but the merged result was red: neither
branch could see the other, and CI cannot see either, because the only test
covering #279 lives in test/mobile/** which test:ci excludes.
Two problems, both in #279's test:
1. The in-terminal case tapped the terminal's top-left corner, i.e. an inert
transcript row, and asserted focus was retained. That is precisely the
gesture #280 redefines, so #280 turned it red. Aim it at the PROMPT row
instead: the one in-terminal tap whose outcome neither PR claims, so it
still proves the #terminalContainer exemption without asserting the
toggle's behaviour.
2. The "a real control is exempt" case was VACUOUS. It picked the first
button measuring >8px, which is .welcome-ralph-link inside the welcome
overlay hideWelcome() had already hidden: the rect still measures, but
elementFromPoint at that point returns .xterm-screen, so the case tapped
the TERMINAL and passed for the wrong reason. It only surfaced because
#280 changed what a terminal tap does. Require the sampled point to
actually resolve to the button, and fail loudly when no control is
usable rather than silently asserting nothing.
Mutation-checked: removing the install, the #terminalContainer exemption,
the control exemption or the `if (moved) return` scroll guard each turns
the test red on its own. The control exemption had no coverage before.
Also fold the duplicated tap slop into one constant: initTerminal's
TAP_THRESHOLD now reads MOBILE_KEYBOARD_DISMISS_TAP_SLOP instead of
re-declaring 8, since a drift between them is exactly the bug the second
#279 commit fixed. And restore the comment the slop constant was inserted
into the middle of, which left "Regions where a tap must NOT dismiss"
sitting above the slop rather than the selector it documents.
test/mobile/keyboard.test.ts: 5 failed | 47 passed (52). Master is
5 failed | 46 passed (51) — the same five pre-existing failures.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>