ghostel.el - Terminal emulator powered by libghostty

Table of Contents

  1. Quick Start

1.1. Shell integration at a glance

1.2. Input modes at a glance

1. Quick Start

1.1. Shell integration at a glance

1.1. Shell integration at a glance

1.2. Input modes at a glance

1.2. Input modes at a glance

  1. Requirements

2. Requirements

  1. Installation

3.1. MELPA

3.2. use-package with :vc (Emacs 30+)

3.3. use-package with :load-path

3.4. Manual

3.5. Native module

3.6. Platform notes

3.6.1. Windows

3. Installation

3.1. MELPA

3.1. MELPA

3.2. use-package with :vc (Emacs 30+)

3.2. use-package with :vc (Emacs 30+)

3.3. use-package with :load-path

3.3. use-package with :load-path

3.4. Manual

3.4. Manual

3.5. Native module

3.5. Native module

3.6. Platform notes

3.6.1. Windows

3.6. Platform notes

3.6.1. Windows

3.6.1. Windows

  1. Building from source

4.1. Bundled terminfo

4. Building from source

4.1. Bundled terminfo

4.1. Bundled terminfo

  1. Shell integration

5. Shell integration

  1. Input modes

6.1. Mode-switch keybindings

6.2. Semi-char mode (default)

6.3. Char mode

6.4. Emacs mode

6.5. Copy mode

6.6. Mouse selection

6.7. Line mode

6.8. Scrollback search outside copy mode

6. Input modes

6.1. Mode-switch keybindings

6.1. Mode-switch keybindings

6.2. Semi-char mode (default)

6.2. Semi-char mode (default)

6.3. Char mode

6.3. Char mode

6.4. Emacs mode

6.4. Emacs mode

6.5. Copy mode

6.5. Copy mode

6.6. Mouse selection

6.6. Mouse selection

6.7. Line mode

6.7. Line mode

6.8. Scrollback search outside copy mode

6.8. Scrollback search outside copy mode

  1. Features

7.1. Terminal emulation

7.2. Process model

7.3. Bookmarks

7.4. Links and file detection

7.5. Clipboard

7.6. Input

7.7. Password prompt detection

7.8. Shell integration features

7.9. Rendering

7.10. Inline images (Kitty graphics protocol)

7.10.1. Limitations

7.11. Calling Elisp from the shell

7.12. Notifications and progress

7.13. Color palette

7. Features

7.1. Terminal emulation

7.1. Terminal emulation

7.2. Process model

7.2. Process model

7.3. Bookmarks

7.3. Bookmarks

7.4. Links and file detection

7.4. Links and file detection

7.5. Clipboard

7.5. Clipboard

7.6. Input

7.6. Input

7.7. Password prompt detection

7.7. Password prompt detection

7.8. Shell integration features

7.8. Shell integration features

7.9. Rendering

7.9. Rendering

7.10. Inline images (Kitty graphics protocol)

7.10.1. Limitations

7.10. Inline images (Kitty graphics protocol)

7.10.1. Limitations

7.10.1. Limitations

7.11. Calling Elisp from the shell

7.11. Calling Elisp from the shell

7.12. Notifications and progress

7.12. Notifications and progress

7.13. Color palette

7.13. Color palette

  1. TRAMP (Remote Terminals)

8.1. Remote shell integration

8.1.1. Option 1: Automatic injection (recommended for convenience)

8.1.2. Option 2: Manual setup (recommended for permanent remote hosts)

8.2. Remote xterm-ghostty terminfo

8.2.1. TRAMP-launched ghostel

8.2.2. Outbound ssh from a local ghostel buffer

8.2.3. Manual install (no auto-machinery)

8.2.4. Drop the Ghostty advertisement entirely

8. TRAMP (Remote Terminals)

8.1. Remote shell integration

8.1.1. Option 1: Automatic injection (recommended for convenience)

8.1.2. Option 2: Manual setup (recommended for permanent remote hosts)

8.1. Remote shell integration

8.1.1. Option 1: Automatic injection (recommended for convenience)

8.1.1. Option 1: Automatic injection (recommended for convenience)

8.1.2. Option 2: Manual setup (recommended for permanent remote hosts)

8.1.2. Option 2: Manual setup (recommended for permanent remote hosts)

8.2. Remote xterm-ghostty terminfo

8.2.1. TRAMP-launched ghostel

8.2.2. Outbound ssh from a local ghostel buffer

8.2.3. Manual install (no auto-machinery)

8.2.4. Drop the Ghostty advertisement entirely

8.2. Remote xterm-ghostty terminfo

8.2.1. TRAMP-launched ghostel

8.2.1. TRAMP-launched ghostel

8.2.2. Outbound ssh from a local ghostel buffer

8.2.2. Outbound ssh from a local ghostel buffer

8.2.3. Manual install (no auto-machinery)

8.2.3. Manual install (no auto-machinery)

8.2.4. Drop the Ghostty advertisement entirely

8.2.4. Drop the Ghostty advertisement entirely

  1. Configuration

9.1. Process and environment

9.2. Native module

9.3. TRAMP and remote

9.4. Rendering and performance

9.5. Images

9.6. Links, clipboard, and detection

9.7. Password prompts

9.8. Notifications and progress

9.9. Input and interaction

9.10. Line mode

9. Configuration

9.1. Process and environment

9.1. Process and environment

9.2. Native module

9.2. Native module

9.3. TRAMP and remote

9.3. TRAMP and remote

9.4. Rendering and performance

9.4. Rendering and performance

9.5. Images

9.5. Images

9.6. Links, clipboard, and detection

9.6. Links, clipboard, and detection

9.7. Password prompts

9.7. Password prompts

9.8. Notifications and progress

9.8. Notifications and progress

9.9. Input and interaction

9.9. Input and interaction

9.10. Line mode

9.10. Line mode

  1. Extensions

10.1. Evil-mode

10.2. Compilation mode

10.2.1. Live mode switching

10.2.2. Keybindings (ghostel-compile-view-mode, also active during a read-only run)

10.2.3. Make compile / recompile / project-compile use ghostel

10.2.4. Hooks for your own integrations

10.3. Eshell integration

10.4. Comint integration

10.5. Emacs Lisp input methods

10. Extensions

10.1. Evil-mode

10.1. Evil-mode

10.2. Compilation mode

10.2.1. Live mode switching

10.2.2. Keybindings (ghostel-compile-view-mode, also active during a read-only run)

10.2.3. Make compile / recompile / project-compile use ghostel

10.2.4. Hooks for your own integrations

10.2. Compilation mode

10.2.1. Live mode switching

10.2.1. Live mode switching

10.2.2. Keybindings (ghostel-compile-view-mode, also active during a read-only run)

10.2.2. Keybindings (ghostel-compile-view-mode, also active during a read-only run)

10.2.3. Make compile / recompile / project-compile use ghostel

10.2.3. Make compile / recompile / project-compile use ghostel

10.2.4. Hooks for your own integrations

10.2.4. Hooks for your own integrations

10.3. Eshell integration

10.3. Eshell integration

10.4. Comint integration

10.4. Comint integration

10.5. Emacs Lisp input methods

10.5. Emacs Lisp input methods

  1. Commands

11.1. Sending input from Lisp

11.2. Project integration

11. Commands

11.1. Sending input from Lisp

11.1. Sending input from Lisp

11.2. Project integration

11.2. Project integration

  1. Running tests

12. Running tests

  1. Performance

13.1. Native vs Emacs PTY

13.2. Burst absorption (cat a 10 MB file)

13.3. Typing latency

13. Performance

13.1. Native vs Emacs PTY

13.1. Native vs Emacs PTY

13.2. Burst absorption (cat a 10 MB file)

13.2. Burst absorption (cat a 10 MB file)

13.3. Typing latency

13.3. Typing latency

  1. Ghostel vs vterm and eat

14.1. Feature comparison

14.2. Key differences

14. Ghostel vs vterm and eat

14.1. Feature comparison

14.1. Feature comparison

14.2. Key differences

14.2. Key differences

  1. Architecture

15.1. PTY and process ownership

15.2. Renderer and buffer positions

15.2.1. Renderer-owned buffer position preservation

15.2.2. Avoid around-redraw semantic patching in Elisp

15. Architecture

15.1. PTY and process ownership

15.1. PTY and process ownership

15.2. Renderer and buffer positions

15.2.1. Renderer-owned buffer position preservation

15.2.2. Avoid around-redraw semantic patching in Elisp

15.2. Renderer and buffer positions

15.2.1. Renderer-owned buffer position preservation

15.2.1. Renderer-owned buffer position preservation

15.2.2. Avoid around-redraw semantic patching in Elisp

15.2.2. Avoid around-redraw semantic patching in Elisp

  1. Contributing

16. Contributing

  1. Changelog

17. Changelog

  1. License

18. License

  1. Indices

19.1. Command and function index

19.2. Concept index

19. Indices

19.1. Command and function index

19.1. Command and function index

19.2. Concept index

19.2. Concept index

MELPA

MELPA Stable

CI

Release

License: GPL-3.0-or-later

VT engine: libghostty-vt

Ghostel is an Emacs terminal emulator powered by libghostty-vt - the same VT

engine that drives the Ghostty terminal. A native dynamic module written in

Zig handles terminal state, rendering, and local PTY I/O; Elisp manages

keymaps, buffers, commands, and remote process integration.

libghostty-vt

Ghostty

Zig

Ghostel is inspired by emacs-libvterm and follows the same two-layer design,

but uses Ghostty's modern VT engine instead of libvterm. This brings the Kitty

keyboard and graphics protocols, rich underline styles, OSC 8 hyperlinks, OSC

4/10/11 color queries, and synchronized output (DEC 2026) - none of which

libvterm supports. See Ghostel vs vterm and eat for a detailed comparison.

emacs-libvterm

Ghostel vs vterm and eat

The native module is downloaded automatically on first use, so no toolchain is

required for the common case. Open a terminal with M-x ghostel.

Table of Contents

  1. Quick Start

1.1. Shell integration at a glance

1.2. Input modes at a glance

1. Quick Start

1.1. Shell integration at a glance

1.1. Shell integration at a glance

1.2. Input modes at a glance

1.2. Input modes at a glance

  1. Requirements

2. Requirements

  1. Installation

3.1. MELPA

3.2. use-package with :vc (Emacs 30+)

3.3. use-package with :load-path

3.4. Manual

3.5. Native module

3.6. Platform notes

3.6.1. Windows

3. Installation

3.1. MELPA

3.1. MELPA

3.2. use-package with :vc (Emacs 30+)

3.2. use-package with :vc (Emacs 30+)

3.3. use-package with :load-path

3.3. use-package with :load-path

3.4. Manual

3.4. Manual

3.5. Native module

3.5. Native module

3.6. Platform notes

3.6.1. Windows

3.6. Platform notes

3.6.1. Windows

3.6.1. Windows

  1. Building from source

4.1. Bundled terminfo

4. Building from source

4.1. Bundled terminfo

4.1. Bundled terminfo

  1. Shell integration

5. Shell integration

  1. Input modes

6.1. Mode-switch keybindings

6.2. Semi-char mode (default)

6.3. Char mode

6.4. Emacs mode

6.5. Copy mode

6.6. Mouse selection

6.7. Line mode

6.8. Scrollback search outside copy mode

6. Input modes

6.1. Mode-switch keybindings

6.1. Mode-switch keybindings

6.2. Semi-char mode (default)

6.2. Semi-char mode (default)

6.3. Char mode

6.3. Char mode

6.4. Emacs mode

6.4. Emacs mode

6.5. Copy mode

6.5. Copy mode

6.6. Mouse selection

6.6. Mouse selection

6.7. Line mode

6.7. Line mode

6.8. Scrollback search outside copy mode

6.8. Scrollback search outside copy mode

  1. Features

7.1. Terminal emulation

7.2. Process model

7.3. Bookmarks

7.4. Links and file detection

7.5. Clipboard

7.6. Input

7.7. Password prompt detection

7.8. Shell integration features

7.9. Rendering

7.10. Inline images (Kitty graphics protocol)

7.10.1. Limitations

7.11. Calling Elisp from the shell

7.12. Notifications and progress

7.13. Color palette

7. Features

7.1. Terminal emulation

7.1. Terminal emulation

7.2. Process model

7.2. Process model

7.3. Bookmarks

7.3. Bookmarks

7.4. Links and file detection

7.4. Links and file detection

7.5. Clipboard

7.5. Clipboard

7.6. Input

7.6. Input

7.7. Password prompt detection

7.7. Password prompt detection

7.8. Shell integration features

7.8. Shell integration features

7.9. Rendering

7.9. Rendering

7.10. Inline images (Kitty graphics protocol)

7.10.1. Limitations

7.10. Inline images (Kitty graphics protocol)

7.10.1. Limitations

7.10.1. Limitations

7.11. Calling Elisp from the shell

7.11. Calling Elisp from the shell

7.12. Notifications and progress

7.12. Notifications and progress

7.13. Color palette

7.13. Color palette

  1. TRAMP (Remote Terminals)

8.1. Remote shell integration

8.1.1. Option 1: Automatic injection (recommended for convenience)

8.1.2. Option 2: Manual setup (recommended for permanent remote hosts)

8.2. Remote xterm-ghostty terminfo

8.2.1. TRAMP-launched ghostel

8.2.2. Outbound ssh from a local ghostel buffer

8.2.3. Manual install (no auto-machinery)

8.2.4. Drop the Ghostty advertisement entirely

8. TRAMP (Remote Terminals)

8.1. Remote shell integration

8.1.1. Option 1: Automatic injection (recommended for convenience)

8.1.2. Option 2: Manual setup (recommended for permanent remote hosts)

8.1. Remote shell integration

8.1.1. Option 1: Automatic injection (recommended for convenience)

8.1.1. Option 1: Automatic injection (recommended for convenience)

8.1.2. Option 2: Manual setup (recommended for permanent remote hosts)

8.1.2. Option 2: Manual setup (recommended for permanent remote hosts)

8.2. Remote xterm-ghostty terminfo

8.2.1. TRAMP-launched ghostel

8.2.2. Outbound ssh from a local ghostel buffer

8.2.3. Manual install (no auto-machinery)

8.2.4. Drop the Ghostty advertisement entirely

8.2. Remote xterm-ghostty terminfo

8.2.1. TRAMP-launched ghostel

8.2.1. TRAMP-launched ghostel

8.2.2. Outbound ssh from a local ghostel buffer

8.2.2. Outbound ssh from a local ghostel buffer

8.2.3. Manual install (no auto-machinery)

8.2.3. Manual install (no auto-machinery)

8.2.4. Drop the Ghostty advertisement entirely

8.2.4. Drop the Ghostty advertisement entirely

  1. Configuration

9.1. Process and environment

9.2. Native module

9.3. TRAMP and remote

9.4. Rendering and performance

9.5. Images

9.6. Links, clipboard, and detection

9.7. Password prompts

9.8. Notifications and progress

9.9. Input and interaction

9.10. Line mode

9. Configuration

9.1. Process and environment

9.1. Process and environment

9.2. Native module

9.2. Native module

9.3. TRAMP and remote

9.3. TRAMP and remote

9.4. Rendering and performance

9.4. Rendering and performance

9.5. Images

9.5. Images

9.6. Links, clipboard, and detection

9.6. Links, clipboard, and detection

9.7. Password prompts

9.7. Password prompts

9.8. Notifications and progress

9.8. Notifications and progress

9.9. Input and interaction

9.9. Input and interaction

9.10. Line mode

9.10. Line mode

  1. Extensions

10.1. Evil-mode

10.2. Compilation mode

10.2.1. Live mode switching

10.2.2. Keybindings (ghostel-compile-view-mode, also active during a read-only run)

10.2.3. Make compile / recompile / project-compile use ghostel

10.2.4. Hooks for your own integrations

10.3. Eshell integration

10.4. Comint integration

10.5. Emacs Lisp input methods

10. Extensions

10.1. Evil-mode

10.1. Evil-mode

10.2. Compilation mode

10.2.1. Live mode switching

10.2.2. Keybindings (ghostel-compile-view-mode, also active during a read-only run)

10.2.3. Make compile / recompile / project-compile use ghostel

10.2.4. Hooks for your own integrations

10.2. Compilation mode

10.2.1. Live mode switching

10.2.1. Live mode switching

10.2.2. Keybindings (ghostel-compile-view-mode, also active during a read-only run)

10.2.2. Keybindings (ghostel-compile-view-mode, also active during a read-only run)

10.2.3. Make compile / recompile / project-compile use ghostel

10.2.3. Make compile / recompile / project-compile use ghostel

10.2.4. Hooks for your own integrations

10.2.4. Hooks for your own integrations

10.3. Eshell integration

10.3. Eshell integration

10.4. Comint integration

10.4. Comint integration

10.5. Emacs Lisp input methods

10.5. Emacs Lisp input methods

  1. Commands

11.1. Sending input from Lisp

11.2. Project integration

11. Commands

11.1. Sending input from Lisp

11.1. Sending input from Lisp

11.2. Project integration

11.2. Project integration

  1. Running tests

12. Running tests

  1. Performance

13.1. Native vs Emacs PTY

13.2. Burst absorption (cat a 10 MB file)

13.3. Typing latency

13. Performance

13.1. Native vs Emacs PTY

13.1. Native vs Emacs PTY

13.2. Burst absorption (cat a 10 MB file)

13.2. Burst absorption (cat a 10 MB file)

13.3. Typing latency

13.3. Typing latency

  1. Ghostel vs vterm and eat

14.1. Feature comparison

14.2. Key differences

14. Ghostel vs vterm and eat

14.1. Feature comparison

14.1. Feature comparison

14.2. Key differences

14.2. Key differences

  1. Architecture

15.1. PTY and process ownership

15.2. Renderer and buffer positions

15.2.1. Renderer-owned buffer position preservation

15.2.2. Avoid around-redraw semantic patching in Elisp

15. Architecture

15.1. PTY and process ownership

15.1. PTY and process ownership

15.2. Renderer and buffer positions

15.2.1. Renderer-owned buffer position preservation

15.2.2. Avoid around-redraw semantic patching in Elisp

15.2. Renderer and buffer positions

15.2.1. Renderer-owned buffer position preservation

15.2.1. Renderer-owned buffer position preservation

15.2.2. Avoid around-redraw semantic patching in Elisp

15.2.2. Avoid around-redraw semantic patching in Elisp

  1. Contributing

16. Contributing

  1. Changelog

17. Changelog

  1. License

18. License

  1. Indices

19.1. Command and function index

19.2. Concept index

19. Indices

19.1. Command and function index

19.1. Command and function index

19.2. Concept index

19.2. Concept index

1. Quick Start

If you are an evil user you can install the evil-ghostel extension:

evil-ghostel

1.1. Shell integration at a glance

Directory tracking and prompt navigation are automatically on by default for

local bash, zsh, or fish sessions. See shell integration for TRAMP support and

more.

shell integration

To call Emacs functions from your shell you have to add them to the

ghostel-eval-cmds whitelist and then add something like this to your bashrc:

1.2. Input modes at a glance

Ghostel offers five eat.el-style input modes.

input modes

The default is semi-char mode, which forwards almost all keys to the terminal

besides a few exceptions (e.g. M-x, C-c).

In char mode, all keys go to the terminal. Press M-RET to exit.

In line mode Ghostel behaves like M-x shell: the buffer is a normal Emacs

buffer and no key is sent to the terminal. Only after you finish typing a line

and press RET is the whole line sent to the terminal at once.

emacs mode and copy mode make the buffer temporarily a normal Emacs buffer

that you can use to navigate, look around, and copy text. The difference between

the two is that copy mode freezes the terminal, so if you have continuous

output nothing "scrolls away" while you try to select something. emacs mode is

live, so new output keeps coming in while you scroll and select.

Those read-only modes have ghostel-readonly-fast-exit enabled by default (it defaults to t),

which automatically exits them on most keys that you expect to be sent to the

terminal. This makes for seamless transitions: say you have some output running

and see something you want to copy - you press C-c C-t to enter copy mode,

navigate like in a normal Emacs buffer, and select your text. When you copy

something or type any character you are automatically back in your normal ghostel

terminal session. Some actions also activate copy mode automatically, like

selecting with the mouse, navigating to hyperlinks (C-c C-p), or activating the

mark.

2. Requirements

Emacs 28.1+ with dynamic module support

macOS, Linux, FreeBSD, or native Windows

The native module is automatically downloaded on first use. Pre-built

binaries are available for:

aarch64-macos (Apple Silicon)

x86_64-macos (Intel Mac)

x86_64-linux

aarch64-linux

x86_64-freebsd

x86_64-windows

aarch64-windows

If you prefer to build from source or need a different platform, you will also

need Zig 0.15.2 - see Building from source.

Zig

Building from source

3. Installation

3.1. MELPA

3.2. use-package with :vc (Emacs 30+)

:lisp-dir "lisp" is only required on Emacs < 31.1.

3.3. use-package with :load-path

3.4. Manual

Then M-x ghostel to open a terminal.

3.5. Native module

When the native module is missing, Ghostel offers to download a pre-built

binary or compile from source. This is controlled by

ghostel-module-auto-install (default ask). You can also trigger these

manually:

M-x ghostel-download-module - download the minimum supported pre-built

binary.

C-u M-x ghostel-download-module - choose a specific release tag (leave blank

for the latest).

M-x ghostel-module-compile - build from source via zig build.

By default the module is read from and written to the package directory. If

your package manager rebuilds or reinstalls the tree while Emacs has the module

loaded, point ghostel-module-directory at a stable location outside the

package tree (for example ~/.config/emacs/ghostel/).

3.6. Platform notes

3.6.1. Windows

Ghostel supports native Windows Emacs builds with dynamic module support. The

pre-built release modules target the common x86_64 and aarch64 native Emacs

architectures; the module DLL must match the architecture of Emacs, not merely

the architecture of Windows itself. Unusual custom Emacs builds, including

static-CRT builds, are best-effort.

Local terminals use Windows ConPTY. Ghostel first looks for support files from

Microsoft's redistributable console runtime next to the native module

(conpty.dll and the matching OpenConsole.exe helpers). When present, those

files provide newer ConPTY behavior than older inbox Windows versions and can

improve both interactivity and terminal-protocol correctness. If they are not

installed, Ghostel falls back to the inbox Windows ConPTY API.

Downloading a pre-built Ghostel module also downloads the matching support files

from the same Ghostel release. When compiling the module locally, Ghostel tries

to install the latest available support files; failure to install them is

non-fatal because the inbox ConPTY fallback remains available.

TRAMP remote terminals from Windows are limited to POSIX remotes. They use

TRAMP/ssh rather than local ConPTY, so the PTY lives on the remote host. Dynamic

window resizing for Windows-to-TRAMP terminals is not currently supported.

4. Building from source

Building is only needed if you do not want the pre-built binaries. Ghostel

vendors a generated vendor/emacs-module.h, so normal builds do not require

local Emacs headers.

To override the vendored Emacs header, set EMACS_INCLUDE_DIR to a directory

containing emacs-module.h, or set EMACS_BIN_DIR to an Emacs bin/ directory

Ghostel then looks for ../include and ../share/emacs/include.

To build against a local ghostty checkout, temporarily point the dependency at

your local path:

The module and ghostel-module.version sidecar are installed under Zig's

install prefix. Use --prefix . when building into a checkout that Ghostel

should load directly, or pass another prefix matching ghostel-module-directory.

On Windows, local builds normally use Zig's MinGW target (for example

-Dtarget=x86_64-windows-gnu) so they match common native Emacs builds and do

not require a Windows SDK.

When installed from MELPA, M-x ghostel-module-compile builds the native module

from source using zig build --prefix <ghostel-module-directory>; the Zig package

manager fetches the ghostty dependency automatically.

4.1. Bundled terminfo

The compiled xterm-ghostty terminfo entry ships pre-built in etc/terminfo/

and is identical to what tic would produce locally - no build step needed, and

the file format is portable across BSD and ncurses systems. Maintainers

regenerate it via make regen-terminfo after bumping libghostty.

5. Shell integration

Shell integration (directory tracking via OSC 7, prompt navigation via OSC 133,

title tracking via OSC 2, and ghostel_cmd for calling Elisp from the shell) is

automatic for bash, zsh, fish, and nushell. No changes to your shell

configuration files are needed.

calling Elisp from the shell

For nushell, OSC 7/133/2 come from nushell's own $env.config.shell_integration

(on by default), so ghostel's ghostel.nu only adds ghostel_cmd and the

outbound ssh terminfo wrapper. Leave shell_integration.osc7 and .osc133

enabled or directory tracking and prompt navigation will stop working.

This is controlled by ghostel-shell-integration (default t). Set it to nil

to disable auto-injection and source the scripts manually instead:

macOS's built-in /bin/bash is version 3.2, which Apple patched to ignore the

ENV variable auto-injection relies on - so automatic integration does not

work with it. Use a newer bash (e.g. Homebrew's) via ghostel-shell, or source

ghostel.bash manually from ~/.bashrc as shown above.

Remote (TRAMP / outbound ssh) shell integration has its own setup; see TRAMP

(Remote Terminals).

[TRAMP

(Remote Terminals)](#tramp-remote-terminals)

6. Input modes

Ghostel offers five eat.el-style input modes. You enter a ghostel buffer in

semi-char mode; switch modes with the key bindings below and watch

mode-line-process for the current mode indicator.

6.1. Mode-switch keybindings

Available from every mode except char mode:

6.2. Semi-char mode (default)

Most keys are sent to the terminal. Keys in ghostel-keymap-exceptions

(default: C-c, C-x, C-u, C-h, M-x, M-:, C-\) pass through to Emacs.

6.3. Char mode

Entered with C-c M-d. All keys (including ghostel-keymap-exceptions) are

sent to the terminal. Useful for TUI apps that want to bind C-x, M-x, C-h,

etc. themselves. M-RET (or C-M-m) is the sole escape hatch.

6.4. Emacs mode

Entered with C-c C-e. The terminal keeps running, the buffer is read-only,

and standard Emacs bindings fall through to the global map. isearch-forward,

occur, M-x, C-SPC + M-w, arrow keys, wheel scroll - all work unmodified. The

terminal keeps producing output and the buffer keeps growing, but your point

stays where you navigated it (the delayed-redraw path preserves point in Emacs

mode).

Typed keys do not reach the shell - Emacs mode is a "look but don't touch"

view. Self-insert, RET, TAB, DEL fall through to the read-only buffer and

trigger text-read-only, so a stray keystroke cannot accidentally land at the

prompt. Switch to semi-char mode (C-c C-j) when you want to type to the shell.

C-y is the exception: it pastes via bracketed paste as a deliberate action and

snaps point back to the live cursor.

C-c C-e toggles Emacs mode off again (returning to the mode you came from), and

C-c C-t switches to copy mode to freeze the output.

Use this for searching through scrollback while a build is running, filtering

streaming logs with M-x occur, marking and copying across the visible history,

or running any buffer-based command over the terminal's output without having to

freeze it.

6.5. Copy mode

Entered with C-c C-t. The terminal is frozen - no live output updates the

buffer until you exit. Use this when you want to select text precisely without

the terminal scrolling underneath your cursor. The aggressive copy-mode keymap

exits on self-insert, so typing a letter sends it to the terminal and returns to

semi-char mode (controlled by ghostel-readonly-fast-exit).

C-c C-t toggles copy mode off again, and C-c C-e switches to Emacs mode -

read-only but live, so output resumes - without going through semi-char.

Soft-wrapped newlines are automatically stripped from copied text.

6.6. Mouse selection

Click-and-drag inside a ghostel buffer creates a region. On release,

ghostel-mouse-drag-or-set-region switches input mode so streaming terminal

output cannot clobber the selection - the target is picked by

ghostel-mouse-drag-input-mode (default copy):

copy - enter copy mode. Redraws pause; the selection is stable regardless of

where it sits.

emacs - enter Emacs mode. The terminal keeps streaming and the buffer becomes

read-only; selections wholly in scrollback survive, selections over rows the

live program rewrites can still be lost.

nil - stay in semi-char. Same selection-survival guarantees as emacs, but

M-w is forwarded to the shell so it cannot copy the region - pick this only if

you copy via primary selection or the GUI menu.

A single click inside a window that is already selected sets point and then

switches input mode per ghostel-mouse-drag-input-mode, the same as a drag. A

click that focuses a previously-unselected window only gives that window focus:

point lands at the terminal's input cursor (not the click) and the input mode is

unchanged. A click meant just to focus a window therefore never freezes the buffer. (Set

ghostel-mouse-drag-input-mode to nil to turn the click feature off, so a click sets

point like in any Emacs buffer.) When ghostel is in a terminal-input mode and a

TUI has DEC mouse-tracking enabled (1000/1002/1003 - htop, lazygit, etc.), the

click is forwarded to the program and none of the above applies. Copy, Emacs,

and line modes keep normal Emacs mouse behavior even if terminal mouse tracking

is active.

Double- and triple-click select the word and line under the cursor respectively,

and protect that region the same way a drag does (from any window, focused or not).

The same protection exists for keyboard selections: when a command activates

the mark in semi-char mode - C-SPC (set-mark-command), an expand-region

variant, C-x h, anything that turns the region on - ghostel switches to the

input mode picked by ghostel-mark-activation-input-mode (copy by default,

emacs, or nil to stay in semi-char). This hooks mark activation rather than

any particular key, so custom bindings like set-mark-or-expand trigger it

too. Note that on a TTY Ctrl+Space is indistinguishable from C-@ (NUL) and

is forwarded to the terminal instead; in char mode Ctrl+Space always reaches

the terminal as NUL (GUI and TTY alike).

The same applies when a command merely moves point off the live input point

without selecting anything. isearch (via isearch-mode-end-hook) and

minibuffer commands like consult-line (via minibuffer-exit-hook) switch to

the mode picked by ghostel-point-leave-input-mode (copy by default,

emacs, or nil to disable), so the position is frozen and the content is

navigable instead of the next redraw yanking point back to the prompt.

Other jump packages have no built-in hook, so wire them up with the command

ghostel-maybe-leave-input. It is a no-op unless, in semi-char mode, point has

moved off the live cursor, in which case it applies ghostel-point-leave-input-mode.

It ignores its arguments, so it serves as both a hook function and :after advice:

6.7. Line mode

Entered with C-c C-l. Line mode buffers the user's input locally in Emacs -

no keystrokes are forwarded to the shell while composing. Full Emacs editing

(M-b, M-DEL, C-y yank, transpose-words, etc.) works on the input region.

Pressing RET sends the whole line to the shell in one write; bash receives it

atomically, echoes and executes it.

The terminal stays live: output keeps streaming and the buffer keeps

re-rendering while you compose. A snapshot/restore step in the delayed-redraw

path captures the in-progress input before each redraw and re-inserts it at the

new prompt-end afterwards, so async output or a fresh prompt arriving mid-edit

does not clobber what you typed. After RET, line mode stays active - the next

prompt is found on the following redraw cycle and the input marker moves there.

Line mode uses the terminal cursor as the input-area boundary, so REPLs without

shell integration (python3, irb, sqlite3, …) work too. When OSC 133 prompt

markers are present on the cursor's row, the prompt prefix is recognised and the

input boundary lands right after it. As a fallback without OSC 133, the prompt

prefix is matched against ghostel-prompt-regexp.

Line mode and fullscreen TUIs (vim, less, htop, …) cannot share the same

keystroke stream - the TUI needs every key forwarded raw, while line mode

buffers them locally. Ghostel handles this transparently: when an alt-screen

TUI starts, line mode drops to semi-char so the TUI gets its keys, with a brief

message that it will resume on exit. When the TUI exits, line mode resumes at

the new prompt.

Pressing C-c C-l on the alt screen does the right thing for what is running.

Over a raw TUI it arms that same auto-resume, so line mode activates when the

TUI exits; an explicit mode switch (C-c C-j, ghostel-char-mode, etc.) cancels

the arming. At an inner shell prompt - a tmux=/=screen session whose OSC 133

markers reach Ghostel via passthrough - C-c C-l enters line mode at that prompt

directly. When those markers do not pass through, C-u C-c C-l forces entry

anyway.

TAB completes the input via ghostel-line-mode-completion-at-point-functions

(comint filename/command completion by default), and optionally layers bash

programmable completion on top via ghostel-line-mode-use-bash-completion.

6.8. Scrollback search outside copy mode

The full scrollback is always rendered into the buffer as styled text, so

isearch, consult-line, occur, M-x flush-lines, C-x h to select all, and any

other buffer-based command work across the full history in any mode that has a

read-only buffer (Emacs or copy).

7. Features

7.1. Terminal emulation

Full VT terminal emulation via libghostty-vt.

256-color and RGB (24-bit true color) support.

TERM=xterm-ghostty with bundled terminfo - apps that consult terminfo for

capabilities (Claude Code, neovim, tmux, modern TUIs) discover synchronized

output (DEC 2026), the Kitty keyboard protocol, true color, colored

underlines, focus reporting, etc., and use their fast paths. Synchronized

output in particular eliminates the choppy partial-redraw effect when Claude

Code repaints over a large scrollback. OSC 52 (clipboard) is supported but

intentionally not advertised in the bundled terminfo (see Clipboard).

Override via ghostel-term.

Clipboard

OSC 4 / 10 / 11 color queries - TUI programs can query the current palette,

foreground, and background colors, so tools like duf, btop, delta, and

anything else using termenv auto-detect the right light/dark theme from the

Emacs face colors.

OSC 9 / OSC 777 - desktop notifications and ConEmu progress reports (see

Notifications and Progress).

Notifications and Progress

Text attributes: bold, italic, faint, underline (single/double/curly/dotted/dashed, with color), strikethrough, inverse.

Cursor styles: block, bar, underline, hollow block - each steady or blinking.

Alternate screen buffer (for TUI apps like htop, vim, etc.).

Scrollback buffer (configurable, default 5 MB / ~5,000 lines, materialized

into the Emacs buffer so isearch / consult-line work over history).

7.2. Process model

Local ghostel buffers use a native PTY path by default

(ghostel-use-native-pty). The native reader consumes PTY output on a

background thread, updates libghostty-vt asynchronously, and notifies Emacs

through an event pipe when callbacks or redraws are needed. This keeps large

log streams and full-screen TUI redraws from running through Emacs process

filters byte-for-byte.

Remote TRAMP buffers still use Emacs process machinery so TRAMP can spawn the

shell on the remote host and apply its file handlers. The rendering and input

APIs are shared by both paths.

7.3. Bookmarks

Ghostel buffers work with Emacs's built-in bookmarks. bookmark-set (C-x r

m) records the terminal's working directory and buffer name; bookmark-jump

(C-x r b) reopens it — reusing a live ghostel buffer of that name, or starting

a fresh shell in the bookmarked directory when none exists. Bookmarks capture

the directory and name only (not scrollback or session contents), matching

vterm.

ghostel-bookmark-check-dir (default t) controls directory restoration: a

fresh buffer starts its shell in the bookmarked directory, and a reused buffer

that has since moved elsewhere gets a cd typed into its shell. Set it to nil

to leave the directory alone.

7.4. Links and file detection

OSC 8 hyperlinks - clickable URLs emitted by terminal programs (click or

RET to open).

Plain-text URL detection - automatically linkifies http:// and https://

URLs even without OSC 8 (toggle with ghostel-enable-url-detection).

File path detection - patterns like /path/to/file.el:42 become clickable,

opening the file at the given line (toggle with ghostel-enable-file-detection;

tune the path pattern with ghostel-file-detection-path-regex).

7.5. Clipboard

OSC 52 clipboard - terminal programs can set the Emacs kill ring and system

clipboard (opt-in via ghostel-enable-osc52, useful for remote SSH sessions).

The bundled xterm-ghostty terminfo intentionally does not advertise the

Ms capability, so apps do not auto-discover it. This avoids silent clipboard

drops when ghostel-enable-osc52 is at its default nil. If you enable OSC 52

and want apps (neovim, tmux) to auto-detect it, install upstream Ghostty's

terminfo on the same path or override TERMINFO.

Bracketed paste - yank from the kill ring sends text as a bracketed paste so

shells handle it correctly.

7.6. Input

Full keyboard input with the Ghostty key encoder (respects terminal modes,

Kitty keyboard protocol).

Mouse tracking (press, release, drag) via the SGR mouse protocol - TUI apps

receive full mouse input.

Focus events gated by DEC mode 1004.

Drag-and-drop (file paths and text).

7.7. Password prompt detection

When sudo, ssh, gpg, passwd, etc. ask for a password, ghostel pops up

read-passwd and sends the answer through the PTY - keystrokes never flow through

Emacs's normal key pipeline, so the password does not land in view-lossage,

the recent-keys ring, or any keyboard-macro recording. This is controlled by

ghostel-detect-password-prompts (default t).

Detection has two layers. The primary signal mirrors libghostty's heuristic: the

slave tty is in canonical mode with echo off, read via a small tcgetattr Zig

binding. This catches local programs that flip !ECHO (sudo, ssh's own prompt,

gpg, …). A cursor-row regex fallback (ghostel-password-prompt-regex,

defaulting to comint-password-prompt-regexp) covers cases the tty signal cannot

see, but runs only when the foreground shell is on a remote host

(ghostel--remote-shell-p, derived from the TRAMP default-directory ghostel

keeps in sync via OSC 7). Gating it on remote-only avoids false positives from

local raw-mode TUIs like vim or less, and structural anchoring keeps shell-typed

lines such as $ echo Password: from triggering. See ghostel-debug-start /

ghostel-debug-password-events-show for diagnostics.

The mode line shows = 🔒Password= while a prompt is open. Wrong-password retries are detected automatically (the cursor moves to the new

prompt row). The wire copy of the

password is clear-string'd immediately after sending, so it does not linger in the

heap.

Detection is extensible via ghostel-password-prompt-functions - a chain of

(ROW) -> string-or-nil sources tried in order. The default reads with

read-passwd; users prepend their own (auth-source / KeePass / pass / etc.) and

the default acts as the fallback. The defcustom docstring includes a

TRAMP-aware auth-source-pick-first-password example.

7.8. Shell integration features

Automatic injection for bash, zsh, and fish - no shell RC edits needed.

OSC 7 - directory tracking (default-directory follows the shell's cwd,

TRAMP-aware for remote hosts).

OSC 133 - semantic prompt markers, enabling prompt-to-prompt navigation with

C-c M-n / C-c M-p.

OSC 2 - title tracking (the buffer is renamed from the terminal title; see

ghostel-buffer-name-function).

OSC 52;e - call whitelisted Emacs functions from shell scripts (see Calling

Elisp from the Shell).

[Calling

Elisp from the Shell](#calling-elisp-from-the-shell)

OSC 52 - clipboard support (opt-in, for remote sessions).

INSIDE_EMACS and EMACS_GHOSTEL_PATH environment variables.

7.9. Rendering

Incremental redraw - unchanged rows are skipped.

Timer-based batched updates with adaptive frame rate.

Immediate redraw for interactive typing echo - PTY output arriving shortly

after a keystroke bypasses the timer, eliminating 16-33ms of latency per

keypress.

Asynchronous local PTY output - local PTY output is parsed by the native

reader and Emacs is notified only when callbacks or redraws are needed.

Cursor position updates even without cell changes.

Theme-aware color palette (syncs with the Emacs theme via ghostel-sync-theme).

7.10. Inline images (Kitty graphics protocol)

Ghostel renders inline images using the Kitty graphics protocol via libghostty.

It supports both placement modes used by real-world tools:

Kitty graphics protocol

Traditional placements - timg, kitty +kitten icat, and any tool that emits

direct kitty graphics commands.

Unicode-placeholder placements (U+10EEEE) - used by yazi and other modern

image previewers to anchor images to the buffer's text grid.

Pixel data is rendered through Emacs's built-in image support: PNG payloads are

decoded by a vendored stbimage, and raw RGB/RGBA/Gray/GrayAlpha transmissions

are converted to PPM in the native module - no external ImageMagick dependency.

XTWINOPS size queries (CSI 14 / 16 / 18 t) are answered so apps can detect

graphics support and pick image dimensions; without that, timg falls back to

half-block rendering even when TERM_PROGRAM=ghostty.

Cell pixel sizes are reported as physical pixels via ghostel-cell-pixel-scale

(default auto, derived from display DPI). On most displays this approximates

standalone Ghostty's output; for pixel-perfect parity (especially on Linux

Wayland with fractional scaling or non-standard DPI), set an explicit number.

7.10.1. Limitations

Alpha is dropped, not composited. All formats - raw RGBA, GrayAlpha, and

PNG - go through an RGBA→PPM conversion that strips the alpha channel (PNGs are

decoded to RGBA by libghostty's PNG hook at transmit time, then follow the same

path). Transparent pixels render as whatever the underlying color value

happens to be (most decoders emit black). Acceptable for thumbnails and

screenshots; not ideal for icons with semi-transparent edges.

Source-rect cropping is not supported. Atlas-style placements that specify a

sub-region of the source image (x, y, w, h in the kitty protocol) are

refused with an explicit error rather than silently mis-rendering. Full-image

placements - what timg, yazi, and kitty +kitten icat use - are unaffected.

Multiple simultaneous virtual placements share rendering. Unicode-placeholder

placements that coexist in the same buffer are rendered as a single image; the

most recent transmission wins. yazi's preview pane uses one image at a time,

so this has not been a problem in practice.

Non-direct mediums are off by default for safety. Only the inline (base64)

medium is enabled; file / temp-file / shared-memory mediums are opt-in via

ghostel-kitty-graphics-mediums. See its docstring for the

privilege-escalation reasoning.

7.11. Calling Elisp from the shell

Shell scripts running inside ghostel can call whitelisted Elisp functions via

the ghostel_cmd helper (provided by the shell integration scripts):

This uses an OSC 52 escape sequence with a reserved kind byte

(\e]52;e;<payload>\e\\) - a ghostel-private extension. Only functions listed

in ghostel-eval-cmds are allowed.

Default whitelisted commands: find-file, find-file-other-window, dired,

dired-other-window, message.

Add your own with:

Example shell aliases (add to your .bashrc / .zshrc):

7.12. Notifications and progress

Ghostel recognises two notification protocols used by terminal programs:

OSC 9 (iTerm2 form): ESC ] 9 ; BODY ST - body only.

OSC 777 (rxvt notify): ESC ] 777 ; notify ; TITLE ; BODY ST - title + body.

Both route to ghostel-notification-function with (TITLE BODY). The default

handler, ghostel-default-notify, uses the alert package when installed - it picks

a sensible backend per platform (osascript on macOS, libnotify on Linux, Growl,

terminal-notifier, etc.) and is configurable via alert-default-style. Install

it from MELPA with M-x package-install RET alert RET.

alert

When alert is not available, ghostel falls back to message, which only appears

in the echo area. Set ghostel-notification-function to nil to silence

notifications entirely, or to your own function to route them elsewhere.

A custom handler receives the title and body and can route them anywhere:

ConEmu's OSC 9;4 progress protocol is also recognised: build tools, AI agents

like Claude Code, and other long-running commands emit it to report completion

percentage. Ghostel dispatches these to ghostel-progress-function with (STATE

PROGRESS) where STATE is one of remove, set, error, indeterminate, pause and

PROGRESS is an integer 0-100 or nil.

Two built-in handlers are available:

ghostel-default-progress - plain text in mode-line-process: [42%], [...],

[err 73%], [paused 25%], or cleared on remove. Zero dependencies.

ghostel-spinner-progress - animates mode-line-process via spinner.el during

indeterminate (e.g. while Claude Code is working) and falls back to the same

text indicator for the other states.

spinner.el

ghostel-progress-function defaults to ghostel-spinner-progress when spinner.el

is on the load-path at ghostel load time, otherwise to ghostel-default-progress.

Pin a specific handler explicitly:

7.13. Color palette

The 16 ANSI colors are defined as Emacs faces inheriting from term-color-*:

Themes that customize term-color-* faces automatically apply. Customize

individual faces with M-x customize-face.

Default text inherits from the ghostel-default face, which inherits from

default. Customize it to give ghostel terminals different default colors,

font, or size than the rest of Emacs (e.g. a dark terminal inside a light Emacs):

Bold text coloring follows ghostel-bold-color (nil = same color as normal text,

bright = use the bright ANSI variant, or a fixed #RRGGBB string), matching

Ghostty 1.2.0's bold-color configuration.

8. TRAMP (Remote Terminals)

When default-directory is a TRAMP path (e.g. /ssh:host:/home/user/), M-x

ghostel spawns a shell on the remote host via TRAMP's process machinery. The

ghostel-tramp-shells variable controls which shell to use per TRAMP method:

Each entry is (METHOD SHELL [FALLBACK [ARG...]]). SHELL can be a path like

"/bin/bash" or the symbol login-shell to auto-detect the remot