Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1913,7 +1913,7 @@ def _strip_leaked_bracketed_paste_wrappers(text: str) -> str:
def _preserve_ctrl_enter_newline() -> bool:
"""Detect environments where Ctrl+Enter must produce a newline, not submit.

Native Windows, WSL, SSH sessions, and Windows Terminal all send Ctrl+Enter
Native macOS, Windows, WSL, SSH sessions, and Windows Terminal all send Ctrl+Enter
as bare LF (c-j). On those terminals c-j must NOT be bound to submit;
binding it to submit makes Ctrl+Enter (intended as 'newline like Alt+Enter')
submit instead. Local POSIX TTYs that deliver Enter as LF (docker exec,
Expand All @@ -1922,7 +1922,7 @@ def _preserve_ctrl_enter_newline() -> bool:

See issue #22379.
"""
if sys.platform == "win32":
if sys.platform in {"darwin", "win32"}:
return True
if any(os.environ.get(v) for v in ("SSH_CONNECTION", "SSH_CLIENT", "SSH_TTY")):
return True
Expand All @@ -1948,7 +1948,7 @@ def _bind_prompt_submit_keys(kb, handler) -> None:
some thin PTYs (docker exec, certain SSH flavors) deliver Enter as LF
instead of CR — without this, Enter appears dead on those terminals.

Exception: on Windows, WSL, SSH sessions, and Windows Terminal,
Exception: on macOS, Windows, WSL, SSH sessions, and Windows Terminal,
c-j is the wire encoding of Ctrl+Enter (a distinct keystroke from
plain Enter / c-m). We leave c-j unbound there so the c-j newline
handler registered separately can fire — giving the user an
Expand Down Expand Up @@ -5187,7 +5187,7 @@ def show_help(self):
)

_cprint(f"\n {_DIM}Tip: Just type your message to chat with Hermes!{_RST}")
_cprint(f" {_DIM}Multi-line: Alt+Enter for a new line{_RST}")
_cprint(f" {_DIM}Multi-line: Ctrl+J or Alt+Enter for a new line{_RST}")
_cprint(f" {_DIM}Draft editor: Ctrl+G (Alt+G in VSCode/Cursor){_RST}")
if _is_termux_environment():
_cprint(f" {_DIM}Attach image: /image {_termux_example_image_path()} or start your prompt with a local image path{_RST}\n")
Expand Down Expand Up @@ -11421,7 +11421,7 @@ def handle_alt_enter(event):

Works on mac/Linux/WSL. On Windows Terminal this keystroke is
intercepted at the terminal layer (toggles fullscreen) and never
reaches here — Windows users get newline via Ctrl+Enter instead
reaches here — Windows and macOS users get newline via Ctrl+J/Ctrl+Enter instead
(bound below as c-j, since WT delivers Ctrl+Enter as LF).
"""
event.current_buffer.insert_text('\n')
Expand Down
12 changes: 11 additions & 1 deletion tests/cli/test_cli_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def test_lf_enter_binds_to_submit_handler_posix(self):

On a bare local POSIX TTY (no SSH/WSL/WT) we keep c-j → submit so
Enter works on thin PTYs (docker exec, certain ssh configurations).
On Windows, WSL, SSH sessions, and Windows Terminal we leave c-j
On macOS, Windows, WSL, SSH sessions, and Windows Terminal we leave c-j
unbound here so it can be used as the Ctrl+Enter newline keystroke
without conflicting with submit. See issue #22379.
"""
Expand Down Expand Up @@ -203,6 +203,16 @@ def submit_handler(event):
assert bindings[("c-m",)] is submit_handler
assert ("c-j",) not in bindings

# macOS: only enter submits; c-j is free for the newline binding
# added separately in the prompt setup.
with _patch.object(_sys, "platform", "darwin"), \
_patch.dict(_os.environ, {}, clear=True):
kb = KeyBindings()
_bind_prompt_submit_keys(kb, submit_handler)
bindings = {tuple(key.value for key in binding.keys): binding.handler for binding in kb.bindings}
assert bindings[("c-m",)] is submit_handler
assert ("c-j",) not in bindings

# Windows: only enter submits; c-j is free for the newline binding
# added separately in the prompt setup.
with _patch.object(_sys, "platform", "win32"):
Expand Down
7 changes: 7 additions & 0 deletions tests/cli/test_ctrl_enter_newline.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ def test_native_windows_preserves_newline():
assert cli_mod._preserve_ctrl_enter_newline() is True


def test_native_macos_preserves_newline():
import cli as cli_mod
with patch.object(sys, "platform", "darwin"):
with patch.dict(os.environ, {}, clear=True):
assert cli_mod._preserve_ctrl_enter_newline() is True


def test_ssh_session_preserves_newline_on_linux():
import cli as cli_mod
with patch.object(sys, "platform", "linux"):
Expand Down