Description
When using Sapling's internal pager (pager=internal:streampager, the default for sl log), any key that requires Shift does not work when running outside of tmux in Ghostty.app (and cmux.app which uses libghostty).
Observed failures:
G (Shift+g) to go to bottom of pager does nothing
- Typing
@ (Shift+2 on US layout) in search prompt (/) results in empty input
- Searching for any capital letter (e.g.
Foo) does not insert the capital
- All shifted punctuation / symbols affected
Inside tmux it works (tmux re-encodes keys as plain chars), and inside Terminal.app it works. Only Ghostty (and libghostty-based terms) outside tmux reproduces.
Repro steps (Ghostty, no tmux)
cd ~/code/github.com/facebook/sapling && sl log -l 5000 --pager=always
- Press lowercase
g -> goes to top (works)
- Press uppercase
G (Shift+g) -> should go to bottom, stays at top
- Press
/ to search
- Type
@ (Shift+2) -> prompt stays empty, should show @
- Type
ABC -> nothing inserted or only lowercase
Repro with hex injection (works inside tmux too, to avoid needing Ghostty)
Ghostty with modifyOtherKeys=2 enabled (which Sapling enables via termwiz ESC[>4;2m in set_raw_mode) encodes shifted keys as xterm modifyOtherKeys:
- Shift+G:
ESC[27;2;71~ hex 1b 5b 32 37 3b 32 3b 37 31 7e
- Shift+@:
ESC[27;2;64~ hex 1b 5b 32 37 3b 32 3b 36 34 7e
tmux new -d -s repro -x 200 -y 20
tmux send-keys -t repro "sl log -l 5000 --pager=always" C-m; sleep 2
tmux send-keys -t repro -l "g"; sleep 0.5 # top works
tmux send-keys -t repro -H '1b5b32373b323b37317e'; sleep 0.8 # Shift+G Ghostty
# expected bottom, actual stays at top -> bug
tmux send-keys -t repro -l "/"; sleep 0.3
tmux send-keys -t repro -H '1b5b32373b323b36347e'; sleep 0.6 # @ shift
# expected Search: @, actual empty
tmux send-keys -t repro "q"; tmux kill-session -t repro
Technical overview
Sapling's pager enables xterm modifyOtherKeys and Ghostty sends Shift+G as ESC[27;2;71~ which termwiz decodes as Char('G')+SHIFT. The pager's keymap expects Char('G')+NONE and prompt only accepts NONE, so the exact modifier check fails. tmux hides this by re-encoding to plain G.
Disclosure: Written by Meta Muse Spark 1.1
Description
When using Sapling's internal pager (
pager=internal:streampager, the default forsl log), any key that requires Shift does not work when running outside of tmux in Ghostty.app (and cmux.app which uses libghostty).Observed failures:
G(Shift+g) to go to bottom of pager does nothing@(Shift+2 on US layout) in search prompt (/) results in empty inputFoo) does not insert the capitalInside tmux it works (tmux re-encodes keys as plain chars), and inside Terminal.app it works. Only Ghostty (and libghostty-based terms) outside tmux reproduces.
Repro steps (Ghostty, no tmux)
cd ~/code/github.com/facebook/sapling && sl log -l 5000 --pager=alwaysg-> goes to top (works)G(Shift+g) -> should go to bottom, stays at top/to search@(Shift+2) -> prompt stays empty, should show @ABC-> nothing inserted or only lowercaseRepro with hex injection (works inside tmux too, to avoid needing Ghostty)
Ghostty with
modifyOtherKeys=2enabled (which Sapling enables via termwizESC[>4;2minset_raw_mode) encodes shifted keys as xterm modifyOtherKeys:ESC[27;2;71~hex1b 5b 32 37 3b 32 3b 37 31 7eESC[27;2;64~hex1b 5b 32 37 3b 32 3b 36 34 7eTechnical overview
Sapling's pager enables xterm modifyOtherKeys and Ghostty sends Shift+G as ESC[27;2;71~ which termwiz decodes as Char('G')+SHIFT. The pager's keymap expects Char('G')+NONE and prompt only accepts NONE, so the exact modifier check fails. tmux hides this by re-encoding to plain G.
Disclosure: Written by Meta Muse Spark 1.1