Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Alt+<printable key> sends the bare key with no ESC prefix (readline/emacs/tmux meta bindings lost)

Open Beginner friendly
#203 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript
Domain
frontend

Research direction

Start in lib/input-handler.ts, where isPrintableCharacter rejects Alt-modified keys and handleKeyDown passes them on to the key encoder. Check whether KeyEncoderOption.ALT_ESC_PREFIX is ever set. Done when a dispatched Alt+b produces the string "\u001bb", Alt+ArrowLeft and Alt+Backspace still encode as before, and the change works on Linux without breaking the macOS path referenced in #109.

Written by the indexing model from the issue text.

Description

Summary

Alt+<printable key> sends the bare key with no ESC prefix. Terminal applications rely on the meta prefix (readline Alt-b/Alt-f/Alt-d, emacs M-x, tmux/vim meta mappings), so all of them silently lose their Alt bindings. This is the Linux/Windows case of what #109 describes for macOS: isPrintableCharacter() correctly refuses Alt-modified keys, but they then reach the Ghostty key encoder without KeyEncoderOption.ALT_ESC_PREFIX ever being set, so the encoder emits just the character.

Seen on [email protected] (Chromium 14x on Linux). The same code path is unchanged on main (lib/input-handler.ts: isPrintableCharacter returns false for Alt, nothing sets ALT_ESC_PREFIX). (Shift+Tab from #109 is fixed on main but not in the published 0.4.0 -- see #137.)

Minimal reproduction

<div id="t"></div>
<script type="module">
  import { init, Terminal } from 'ghostty-web';
  await init();
  const term = new Terminal();
  term.open(document.getElementById('t'));
  term.onData((d) => console.log(JSON.stringify(d)));
  term.focus();
  // Press Alt+B, or dispatch it:
  term.element.dispatchEvent(new KeyboardEvent('keydown', { key: 'b', code: 'KeyB', altKey: true, bubbles: true, cancelable: true }));
</script>

Actual: "b". Expected (xterm.js, every native terminal, Ghostty itself with macos-option-as-alt/Linux defaults): "\u001bb".

Alt+ArrowLeft ("\u001b[1;3D") and Alt+Backspace ("\u001b\u007f") are fine, so the encoder path itself works; only the printable-key case loses the prefix.

Suggested fix

Either set KeyEncoderOption.ALT_ESC_PREFIX on the encoder (it is already a defined option) and let Alt+printable go through it, or handle altKey && !ctrlKey && !metaKey && event.key.length === 1 in handleKeyDown by emitting '\x1b' + event.key (on macOS, derive the letter from event.code as proposed in #109, since event.key is the composed character there).

Found while evaluating ghostty-web as a PTY renderer for https://github.com/erisera-code/clawser; the host currently works around it with attachCustomKeyEventHandler.

Dominant language
TypeScript
Stars
2.9k
Forks
182
PR merge metrics
No merged PRs in 30d

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from coder/ghostty-web

All issues in coder/ghostty-web

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.