Summary
XTermParser._sequence_to_key_events only applies the alt modifier when the resolved key name is a single character (if len(name) == 1 and alt). For a Meta-prefixed named key — e.g. Alt/Option+Enter, which a terminal sends as ESC + CR (\x1b\r) — the resolved name is enter (length 5), so the alt is silently dropped. The event arrives as plain enter, indistinguishable from a real Enter.
alt+j works; alt+enter, alt+tab, alt+backspace, etc. do not.
Impact
Apps can't bind alt+enter — the common "insert newline vs. submit" split in chat/agent CLIs — on terminals that deliver Meta as an ESC prefix rather than via the Kitty keyboard protocol (e.g. xterm.js-based terminals like Termius, macOS "Option as Meta").
Textual version
8.2.8 (latest on PyPI). The same guard is present on main.
Reproduction
from textual._xterm_parser import XTermParser
p = XTermParser(False)
keys = [t.key for t in p.feed("\x1b\r") if hasattr(t, "key")]
keys += [t.key for t in p.feed("") if hasattr(t, "key")] # EOF flush
print(keys) # ['enter'] -- expected ['alt+enter']
Location
src/textual/_xterm_parser.py, in _sequence_to_key_events:
if len(name) == 1 and alt: # <-- drops alt for named keys
if name.isupper():
name = f"shift+{name.lower()}"
name = f"alt+{name}"
yield events.Key(name, sequence)
Proposed fix
Apply the alt prefix regardless of name length; keep the shift/upper handling scoped to single characters:
if alt:
if len(name) == 1 and name.isupper():
name = f"shift+{name.lower()}"
name = f"alt+{name}"
yield events.Key(name, sequence)
With this, \x1b\r yields alt+enter, and alt+j / plain enter / plain j are unchanged.
Summary
XTermParser._sequence_to_key_eventsonly applies thealtmodifier when the resolved key name is a single character (if len(name) == 1 and alt). For a Meta-prefixed named key — e.g.Alt/Option+Enter, which a terminal sends asESC+CR(\x1b\r) — the resolved name isenter(length 5), so thealtis silently dropped. The event arrives as plainenter, indistinguishable from a real Enter.alt+jworks;alt+enter,alt+tab,alt+backspace, etc. do not.Impact
Apps can't bind
alt+enter— the common "insert newline vs. submit" split in chat/agent CLIs — on terminals that deliver Meta as an ESC prefix rather than via the Kitty keyboard protocol (e.g. xterm.js-based terminals like Termius, macOS "Option as Meta").Textual version
8.2.8(latest on PyPI). The same guard is present onmain.Reproduction
Location
src/textual/_xterm_parser.py, in_sequence_to_key_events:Proposed fix
Apply the
altprefix regardless of name length; keep the shift/upper handling scoped to single characters:With this,
\x1b\ryieldsalt+enter, andalt+j/ plainenter/ plainjare unchanged.