Skip to content

Fix #3076: don't force mobile send-on-Enter when a real keyboard is co-present - #3130

Merged
1 commit merged into
nesquena:masterfrom
Sanjays2402:fix/3076-android-tablet-external-keyboard
May 29, 2026
Merged

1 commit merged into
nesquena:masterfrom
Sanjays2402:fix/3076-android-tablet-external-keyboard

Conversation

@Sanjays2402

Copy link
Copy Markdown
Contributor

Thinking Path

Issue #3076 reports that Shift+Enter and Ctrl+Enter don't work on an Android tablet with an external keyboard. Traced to static/boot.js:1142:

const _mobileDefault = matchMedia('(pointer:coarse)').matches
                    && window._sendKey === 'enter'
                    && _isVirtualKeyboardLikelyOpen();

The problem: a touch-primary device (pointer:coarse) can also have a physical keyboard attached (Android tablet + Bluetooth keyboard, detachable Surface, iPad + Magic Keyboard). When that's the case, the visual-viewport heuristic in _isVirtualKeyboardLikelyOpen() often returns true (toolbar inset, on-screen IME hint chip, etc.) even though the user is typing on hardware keys, and _mobileDefault flips Enter to newline. Combined with the ctrlKey || metaKey gate, the user cannot submit at all.

What Changed

  • static/boot.js:
    • Add _hasFinePointerCoexisting() helper — matchMedia('(any-pointer:fine)'). This is the strongest browser signal for "there is a real keyboard/trackpad/stylus in the picture alongside touch".
    • Short-circuit the _mobileDefault path when any fine pointer is co-present. Pure-touch phones/tablets are unaffected.

Why It Matters

Closes #3076. Hardware-keyboard users on touch devices regain desktop submission semantics. Pure-touch users keep newline-on-Enter.

Verification

  • Lint clean.
  • (any-pointer:fine) is supported across all evergreen browsers (Chrome, Firefox, Safari, Edge — Level 4 Media Queries, shipped since ~2017).
  • Wrapped in a try/catch so unknown-query environments fall back to the prior behavior.

Risks / Follow-ups

A touch-only device that ships with a "fine" pointer (some smart-TV setups expose a remote as fine pointer) would lose the mobile default. The risk is small — those devices typically aren't running hermes-webui interactively — and the failure mode there (desktop Enter semantics on a TV) is recoverable via the explicit Settings choice that already exists.

Model Used

claude-opus-4.7 via GitHub Copilot.

Closes #3076.

…ard is attached

A touch-primary device (`matchMedia('(pointer:coarse)')` is true) can
still have a physical keyboard available — Android tablet + Bluetooth
keyboard, detachable Surface, iPad + Magic Keyboard. The existing
`_mobileDefault` gate flipped Enter to newline on every such device the
moment the visual-viewport heuristic *thought* the soft keyboard was
open, which it often did when the on-screen IME hadn't actually come up
because the user is typing on the hardware keys. Result: Shift+Enter and
Ctrl+Enter never sent and the user could not submit at all.

Add `_hasFinePointerCoexisting()` (`(any-pointer:fine)`) and short-
circuit the mobile-default path when ANY fine pointer is present. That
flag is true whenever a real mouse/trackpad/stylus is paired, which is
the strongest browser signal we have for 'there is a hardware input rig
in the picture too'. Pure-touch phones/tablets are unaffected.
@nesquena-hermes

Copy link
Copy Markdown
Collaborator

LGTM. The fix maps directly to the reporter's suggestion in #3076 ("Disable or refine the _mobileDefault auto-detection for devices with (pointer: fine) or when a physical keyboard is detected"). Reading the original gate at static/boot.js:1142 and the new helper:

Code reference

Pre-PR gate (static/boot.js:1142):

const _mobileDefault=matchMedia('(pointer:coarse)').matches
                  &&window._sendKey==='enter'
                  &&_isVirtualKeyboardLikelyOpen();

The bug: (pointer:coarse) is true on any touch-primary device, whether or not a physical keyboard is also attached. Android tablet + Bluetooth keyboard, Surface in tablet mode, iPad + Magic Keyboard all satisfy it. Combined with _isVirtualKeyboardLikelyOpen() (which uses a 120px viewport-shrink heuristic that browsers also trip on for IME hint chips, address-bar toolbars, gesture-bar insets), the mobile override fires when the user is typing on real keys.

The post-PR gate:

const _mobileDefault=matchMedia('(pointer:coarse)').matches
  &&!_hasFinePointerCoexisting()
  &&window._sendKey==='enter'
  &&_isVirtualKeyboardLikelyOpen();

_hasFinePointerCoexisting() uses matchMedia('(any-pointer:fine)'). The any-pointer family checks across all available pointing devices on the system, so a coarse touchscreen + a fine Bluetooth mouse/keyboard/trackpad will satisfy any-pointer:fine, while a pure-touch phone won't.

Browser support is fine

(any-pointer: fine) is Level 4 Media Queries, shipped in all evergreen browsers since 2017 (Chrome 41, Firefox 64, Safari 9, Edge 12). The PR wraps it in a try/catch defensively. The try/catch is overcautious — matchMedia itself doesn't throw on unknown queries (returns MediaQueryList with matches:false) — but it's harmless.

Edge cases I traced

  • Pure-touch Android phone, software keyboard open: (pointer:coarse) true, (any-pointer:fine) false → mobile default still kicks in ✓ (no regression)
  • iPad with Magic Keyboard: (pointer:coarse) true, (any-pointer:fine) true (trackpad on the keyboard exposes fine pointer) → desktop semantics restored ✓
  • Android tablet + Bluetooth keyboard (no trackpad): depends on the browser's keyboard heuristics; Chrome on Android typically reports (any-pointer:fine) true when a HID keyboard is paired, even without a mouse, because the keyboard itself counts as a fine input device. Worth a sanity check on the reporter's exact setup, but matches the documented Media Queries spec.
  • Smart-TV with remote-as-pointer: some setups expose the remote as a fine pointer (the PR body acknowledges this). For TV setups the failure mode is "desktop Enter sends instead of newline" — recoverable via the Settings send_key choice. Acceptable.

One small note

The PR's risk section says "A touch-only device that ships with a 'fine' pointer (some smart-TV setups expose a remote as fine pointer) would lose the mobile default." Worth pinning the explicit setting in Settings as the documented escape hatch — window._sendKey='ctrl+enter' from boot.js:1525 honors user preference and bypasses _mobileDefault entirely. That's already in the file; no code change needed, but worth a note in the WebUI settings copy if regressions are reported.

Test plan suggestion

The PR's verification is just lint + spec-support check. The actual regression has to be tested on the reporter's hardware (Android tablet + Bluetooth keyboard), since the bug is in the interaction between pointer:coarse + any-pointer:fine + browser-specific viewport heuristics. Worth asking @ankerlandiq (the reporter) to confirm Shift+Enter and Ctrl+Enter both work after this lands. If the issue's WebView version doesn't expose any-pointer:fine when a Bluetooth keyboard is paired (Chrome on Android has had bugs here in the past), the fix would silently no-op on the exact device the reporter has.

Minimal, targeted, correctly diagnosed. Ship it after a single end-to-end confirmation on the reporter's setup.

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.

[Bug] Shift+Enter and Ctrl+Enter broken on Android tablet with external keyboard

2 participants