diff --git a/CHANGELOG.md b/CHANGELOG.md
index db5fb20..dc78bd1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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)
Major UI overhaul by **[@aronprins](https://github.com/aronprins)** — the biggest single contribution to the project. Rebased and reviewed on `pr-242-review`.
diff --git a/static/boot.js b/static/boot.js
index c3ed207..fed5361 100644
--- a/static/boot.js
+++ b/static/boot.js
@@ -343,9 +343,14 @@ $('msg').addEventListener('keydown',e=>{
if(e.key==='Escape'){e.preventDefault();hideCmdDropdown();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(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();}
} else {
if(!e.shiftKey){e.preventDefault();send();}
diff --git a/static/index.html b/static/index.html
index 24978d6..1db8121 100644
--- a/static/index.html
+++ b/static/index.html
@@ -526,7 +526,7 @@
System
Instance version and access controls.
- v0.50.0
+ v0.50.1
diff --git a/tests/test_mobile_layout.py b/tests/test_mobile_layout.py
index 7dda17a..9cce81c 100644
--- a/tests/test_mobile_layout.py
+++ b/tests/test_mobile_layout.py
@@ -206,3 +206,35 @@ def test_mobile_profiles_button_is_last_in_nav():
profiles_pos = HTML.rfind('data-panel="profiles"')
assert spaces_pos > 0 and profiles_pos > spaces_pos, \
"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"