fix: mobile Enter inserts newline (closes #269) (#320)

Cherry-pick of mobile Enter newline fix from #315. On touch-primary devices (pointer:coarse), Enter inserts a newline. Desktop unchanged. 4 new tests, 746 total.
This commit is contained in:
nesquena-hermes
2026-04-12 12:41:12 -07:00
committed by GitHub
parent 9021e76708
commit 84ca4d617b
4 changed files with 47 additions and 3 deletions

View File

@@ -6,6 +6,13 @@
--- ---
## [v0.50.1] Mobile Enter key inserts newline (PR #315, fixes #269)
- **Enter inserts newline on mobile** (closes #269): On touch-primary devices (detected via `matchMedia('(pointer:coarse)')`), the Enter key now inserts a newline instead of sending. Users send via the Send button, which is always visible on mobile. Desktop behavior is unchanged — Enter sends, Shift+Enter inserts a newline.
- The `ctrl+enter` setting continues to work as before on all devices.
- Users who explicitly set send key to `enter` on mobile can override in Settings.
- 4 new tests in `tests/test_mobile_layout.py`; 746 tests total (up from 742)
## [v0.50.0] Composer-centric UI refresh + Hermes Control Center (PR #242) ## [v0.50.0] Composer-centric UI refresh + Hermes Control Center (PR #242)
Major UI overhaul by **[@aronprins](https://github.com/aronprins)** — the biggest single contribution to the project. Rebased and reviewed on `pr-242-review`. Major UI overhaul by **[@aronprins](https://github.com/aronprins)** — the biggest single contribution to the project. Rebased and reviewed on `pr-242-review`.

View File

@@ -343,9 +343,14 @@ $('msg').addEventListener('keydown',e=>{
if(e.key==='Escape'){e.preventDefault();hideCmdDropdown();return;} if(e.key==='Escape'){e.preventDefault();hideCmdDropdown();return;}
if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();selectCmdDropdownItem();return;} if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();selectCmdDropdownItem();return;}
} }
// Send key: respect user preference // Send key: respect user preference.
// On touch-primary devices (software keyboard), default to Enter = newline
// since there's no physical Shift key. Users send via the Send button.
// The 'ctrl+enter' setting also uses this behavior (Enter = newline).
// Users can override in Settings by explicitly choosing 'enter' mode.
if(e.key==='Enter'){ if(e.key==='Enter'){
if(window._sendKey==='ctrl+enter'){ const _mobileDefault=matchMedia('(pointer:coarse)').matches&&window._sendKey==='enter';
if(window._sendKey==='ctrl+enter'||_mobileDefault){
if(e.ctrlKey||e.metaKey){e.preventDefault();send();} if(e.ctrlKey||e.metaKey){e.preventDefault();send();}
} else { } else {
if(!e.shiftKey){e.preventDefault();send();} if(!e.shiftKey){e.preventDefault();send();}

View File

@@ -526,7 +526,7 @@
<div class="settings-section-title">System</div> <div class="settings-section-title">System</div>
<div class="settings-section-meta">Instance version and access controls.</div> <div class="settings-section-meta">Instance version and access controls.</div>
</div> </div>
<span class="settings-version-badge">v0.50.0</span> <span class="settings-version-badge">v0.50.1</span>
</div> </div>
<div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px"> <div class="settings-field" style="border-top:1px solid var(--border);padding-top:12px;margin-top:8px">
<label for="settingsPassword" data-i18n="settings_label_password">Access Password</label> <label for="settingsPassword" data-i18n="settings_label_password">Access Password</label>

View File

@@ -206,3 +206,35 @@ def test_mobile_profiles_button_is_last_in_nav():
profiles_pos = HTML.rfind('data-panel="profiles"') profiles_pos = HTML.rfind('data-panel="profiles"')
assert spaces_pos > 0 and profiles_pos > spaces_pos, \ assert spaces_pos > 0 and profiles_pos > spaces_pos, \
"Profiles button must appear after Spaces button in the mobile nav" "Profiles button must appear after Spaces button in the mobile nav"
# ── Mobile Enter key inserts newline (PR #315, fixes #269) ───────────────────
def test_mobile_enter_newline_condition_present():
"""boot.js keydown handler must detect touch-primary devices via pointer:coarse."""
boot_js = (REPO / "static" / "boot.js").read_text(encoding="utf-8")
assert "pointer:coarse" in boot_js, \
"boot.js must use pointer:coarse media query for mobile Enter detection"
def test_mobile_enter_newline_uses_match_media():
"""boot.js must call matchMedia for pointer detection, not a hardcoded flag."""
boot_js = (REPO / "static" / "boot.js").read_text(encoding="utf-8")
assert "matchMedia('(pointer:coarse)')" in boot_js or 'matchMedia("(pointer:coarse)")' in boot_js, \
"boot.js must use matchMedia('(pointer:coarse)') for mobile detection"
def test_mobile_enter_newline_only_overrides_enter_default():
"""Mobile newline override must only apply when _sendKey is the default 'enter'."""
boot_js = (REPO / "static" / "boot.js").read_text(encoding="utf-8")
# The _mobileDefault check must gate on _sendKey==='enter' so ctrl+enter users aren't affected
assert "_sendKey===" in boot_js and "'enter'" in boot_js, \
"Mobile newline fallback must check window._sendKey==='enter' to avoid overriding user preference"
def test_mobile_enter_does_not_affect_desktop_logic():
"""The mobile Enter override must not alter the existing else branch for desktop users."""
boot_js = (REPO / "static" / "boot.js").read_text(encoding="utf-8")
# The else branch (desktop, sends on Enter without Shift) must still be present
assert "if(!e.shiftKey){e.preventDefault();send();" in boot_js, \
"Desktop Enter-to-send logic (else branch) must still be present in boot.js"