diff --git a/README.md b/README.md index b68ea508..676d6cd4 100644 --- a/README.md +++ b/README.md @@ -285,6 +285,7 @@ If an AI assistant is helping with install, reinstall, bootstrap, provider setup ### Settings and configuration - **Hermes Control Center** (sidebar launcher button) -- Conversation tab (export/import/clear), Preferences tab (model, send key, theme, language, all toggles), System tab (version, password) - Send key: Enter (default) or Ctrl/Cmd+Enter +- Send-key on touch devices: plain Enter inserts a newline on phones (iPhone/iPod, Android phones) and on tablets / iPadOS / touch-capable Macs that only expose a coarse pointer (no attached hardware keyboard), matching the software keyboard's return key. Devices that report a fine pointer (for example, a tablet with a hardware keyboard) keep the configured physical-keyboard send-key behavior. The configured shortcut and the Send button remain available for sending. - Show/hide CLI sessions toggle (enabled by default) - Token usage display toggle (off by default, also via `/usage` command) - Control Center always opens on the Conversation tab; resets on close diff --git a/static/boot.js b/static/boot.js index 2e5f2b00..8a5422a9 100644 --- a/static/boot.js +++ b/static/boot.js @@ -2392,9 +2392,29 @@ window._isImeEnter=_isImeEnter; function _hasFinePointerCoexisting(){ try{ return matchMedia('(any-pointer:fine)').matches; }catch(_){ return false; } } +// Detect phone software keyboards without undoing #3076's hardware-input +// guard for tablets. Some iOS Safari versions report (any-pointer:fine) on a +// plain iPhone, so phone UAs bypass that unreliable signal. Tablets and +// touch-capable desktop UAs still require a coarse pointer with no fine pointer. +function _isTouchOnlyDevice(){ + const ua=navigator.userAgent||''; + if(/iPhone|iPod/i.test(ua)) return true; + if(/Android.*Mobile/i.test(ua)) return true; + try{ + return matchMedia('(pointer:coarse)').matches&&!_hasFinePointerCoexisting(); + }catch(_){} + return false; +} function _isNumpadEnter(e){ return e.key==='Enter'&&(e.code==='NumpadEnter'||e.location===KeyboardEvent.DOM_KEY_LOCATION_NUMPAD); } +// Initialise _sendKey synchronously from the localStorage cache so the keydown +// handler below has the correct value before the async /api/settings call +// (line ~3231) resolves. Without this, on slow mobile networks the race window +// leaves _sendKey=undefined, _mobileDefault evaluates false, and plain Enter +// falls through to the `else { send() }` branch — sending the message instead +// of inserting a newline (issue: mobile Enter sends on fresh page load). +try{ window._sendKey=localStorage.getItem('hermes-pref-send_key')||'enter'; }catch(_){ window._sendKey='enter'; } $('msg').addEventListener('keydown',e=>{ // Autocomplete navigation when dropdown is open const dd=$('cmdDropdown'); @@ -2428,9 +2448,8 @@ $('msg').addEventListener('keydown',e=>{ if(e.key==='Enter'){ if(_isImeEnter(e)){return;} const isNumpadEnter=_isNumpadEnter(e); - const _mobileDefault=matchMedia('(pointer:coarse)').matches - &&!_hasFinePointerCoexisting() - &&window._sendKey==='enter'; + const _mobileDefault=_isTouchOnlyDevice() + &&(window._sendKey==='enter'||typeof window._sendKey==='undefined'); if(window._sendKey==='shift+enter'){ if(e.shiftKey){e.preventDefault();send();} } else if(window._sendKey==='ctrl+enter'||_mobileDefault){ diff --git a/static/index.html b/static/index.html index b95528e8..335868de 100644 --- a/static/index.html +++ b/static/index.html @@ -592,7 +592,7 @@ - +