All notable changes to this project will be documented in this file.
- Breaking:
ghostel_cmd(shell helper) now emitsOSC 52;einstead ofOSC 51;E. Libghostty parses OSC 51 into an action the standard handler ignores, so the elisp-eval extension never reached ghostel's callback through normal action dispatch — the old code worked around this with a post-write byte scanner. The extension now rides on OSC 52 with a reservedkindbyte (e), which libghostty dispatches directly to ghostel's handler. Scripts that hand-roll the legacy\e]51;E…\e\\sequence will silently no-op; update to\e]52;e;…\e\\or call the bundledghostel_cmdhelper. The post-write byte scanner is gone; the OSC 52 path is parsed by libghostty and now reassembles payloads split across read boundaries (slow producers, SSH). - Rendering now tracks scroll position and row eviction via libghostty's pin/page primitives instead of the previous heuristic. The old method had corner cases that triggered unnecessary full rebuilds; the new path keeps incremental updates correct across scrollback growth and eviction.
ghostel-next-hyperlink/ghostel-previous-hyperlinknow dedupe by OSC 8id=, so a single logical URL split across multiple cell runs (e.g. a URL wrapped across rows inside an ASCII box) is visited once instead of once per chunk. As a side effect, two adjacent cells pointing to different OSC 8 hyperlinks no longer merge into a single style run that clobbered the second link's text properties. Fixes #125.M-<punct>andM-<digit>keys (e.g.M-.,M-/,M-;,M-1) no longer drop silently when libghostty's encoder returns no output. The raw fallback inghostel--raw-key-sequencepreviously only covered meta + lowercase a–z; it now emits ESC + char for the full printable ASCII range, matching legacy alt encoding (zsh/readlineM-.→ insert-last-word, etc.).- Scrollback eviction no longer drops rows incorrectly when the active pin lands at the top of the screen — found via property-based testing. Related viewport rendering paths were hardened against over/underflow.
- Property-based renderer testing via Hypothesis
(
test/hypothesis/), with CI replay of captured failure cases as regression tests. - Renderer hardening: debug assertions on
rows_in_buffer, over/underflow guards, consolidated renderer tests, CSI 3J same-count refill regression test, fix for the debug finalizer setup. - Emacs function registration refactored to be declarative with
the function body next to its metadata, and
link_idfolded intoCellPropsso OSC 8 metadata lives in one place. EMACSFLAGSnow forwarded into the Hypothesis driver and small development utilities added undertools/.
ghostel-comint-mode(andglobal-ghostel-comint-mode) replacesansi-color-process-outputwith a full libghostty VT parser as a comint preoutput filter. Handles truecolor / 256-color SGR, italic / faint / strikethrough / overline, curly / dotted / dashed / double underline (including colon-separator forms), underline colour, OSC 8 hyperlinks (reusingghostel-link-mapso clicks dispatch by URI scheme), OSC 7 working-directory reports, and silently consumes DCS / APC / SS3 without leaking bytes. Unstyled runs inherit the comint buffer's default face; inverse is emitted as:inverse-video t. Whenfont-lock-modeis on, the filter swapsfaceforfont-lock-faceto survive font-lock's unfontify pass. Ref #278.ghostel-glyph-scale-floorbuffer-local defcustom (number, 0.0–1.0, default 0.0) clamps the computed glyph scale from below. At 0.0 the existing strict-grid behaviour is unchanged; at 1.0 CJK and other fallback glyphs render at natural font size at the cost of slightly taller rows. Being buffer-local, different ghostel buffers can use different settings. Closes #298.- Semi-char mode now forwards
M-<digit>,M-<punct>, andM-<uppercase>(plusM-SPC) to the terminal. Previously theM-loop only covered?a..?z, soM-.,M-1,M-/,M-;, etc. fell through to Emacs global commands instead of reaching the shell. CSI/SS3 escape-sequence prefixes (M-[,M-O) are still left to TTY input decoding. Fixes #314.
ghostel--filter-soft-wrapswas O(n²) due to one-character-at- a-time string concatenation, causing multi-second freezes when copying large selections (e.g. full scrollback) withM-win copy mode. Replaced with a chunk-collection approach.- Skip rows-only resize while a minibuffer is active. Fish (and
other shells with
fish_handle_reflowon) repaints its prompt on every SIGWINCH; aconsult-buffer/M-xcycle grew then shrank the body of the window showing the ghostel buffer, producing two prompt repaints in quick succession. The deferral hits the no-change branch after the minibuffer closes. Col changes and non-minibuffer vertical resizes still propagate so$LINESstays accurate. Fixes #268. - CRLF normalization is now skipped on the alternate screen. Apps
like tmux, vim, and less emit VT-correct sequences where a bare
\nmeans LF; normalizing them to\r\ncorrupted their layout. Detection covers all three alt-screen entry modes (DECSET 47 / 1047 / 1049). - Default-styled text is no longer invisible on the Linux
framebuffer TTY.
ghostel--face-hex-coloronly recognised the literal"unspecified"string as unset, so on a TTY frame where the default face reports"unspecified-fg"/"unspecified-bg"it fell through to a"#000000"fallback for both attributes, collapsing fg and bg to black-on-black. All three sentinel strings are now recognised; the last-resort fallback splits into white for:foregroundand black for:background. Fixes #297.
- Removed
ghostel-readonly-recenter— no longer needed now that scrollback is materialized into the buffer. Fixes #310.
- Switched the Zig module from libghostty-vt's C API to the Zig
API. OSC actions are now routed through a custom
GhostelHandlerthat wraps libghostty's standard terminal handler and overrides the OSC arms (semantic_prompt, color_operation, report_pwd, clipboard_contents, show_desktop_notification, progress_report). This deletes ~395 lines of parallel byte-scanning code inmodule.zigin favour of reading ghostty's parsed Command values directly. Only OSC 51 still uses a bespoke scan since it is ghostel's elisp-eval extension. - Style-to-face translation extracted into
src/style_face.zigand shared between the renderer and theghostel-comintstream filter so they stay in sync. - Debug builds use
DebugAllocatorfor corruption and leak detection; allocator is deinit'd on atexit and the false-positive leak check was tightened. Allocators are now injected via Zig's best-practice pattern: terminal storesallocat init,Renderer/RowContentacceptallocparameters (unmanaged pattern), andmodule.zighas a single top-levelallocbinding instead of scatteredc_allocatorreferences. - Zig refactors:
Terminalrenamed toGhostelTermto avoid confusion with ghostty'sTerminaltype;Rendererstoresgt.Terminal; theforce_fullargument is replaced by setting the render state dirty; debug-mode userptr freeing is generalized beyondGhostelTerm; Zig log output also goes to stderr.
- Multi-terminal navigation commands:
ghostel-next,ghostel-previous,ghostel-list-buffers, plus project-scoped variantsghostel-project-next,ghostel-project-previous, andghostel-project-list-buffersfor cycling and picking among ghostel buffers. Project membership is configurable via the newghostel-project-buffer-scopedefcustom.
- Font metrics are now cached during redraw, avoiding repeated font-attribute lookups per frame.
- Tests run in parallel via per-file Make targets, with stamps
under
.build/tests/. Recommended invocation:make -j$(nproc) all(or-j$(sysctl -n hw.ncpu)on macOS). Wall-clock improvements on a warm cache:test12.5s → 6.6s,test-native11.2s → 7.8s,all10.4s → 8.4s. NewEMACSFLAGSvariable lets callers inject load paths. - The monolithic
ghostel-test.elwas split into 16 per-topicghostel-*-test.elfiles plus a sharedghostel-test-helpers.el. Native-only tests carry:tags '(native); the runner selects via(tag native)/(not (tag native))instead of a hand-maintained whitelist. - A review pass over the split fixed ~30 tests that were passing for the wrong reasons (tautological assertions, mocks of the function under test, references to symbols that no longer exist).
ghostel-query-before-killingdefcustom controls whether Emacs asks for confirmation before killing a live ghostel buffer or exiting Emacs while one is running. Defaults toauto: quiet at the shell prompt, queries while a command is running (via OSC 133 C/D markers). Set totfor always-on confirmation,nilto restore the previous never-query behavior. Closes #288.ghostel-macos-login-shelldefcustom (defaultton Darwin) wraps the shell via/usr/bin/login -flp $USERso~/.zprofileand~/.bash_profileare sourced, matching Terminal.app and Ghostty. Skipped for TRAMP, non-Darwin, andghostel-exec.ghostel-shellnow also accepts a list form (program + args) so users can pass extra flags like'("/bin/zsh" "--login")on any platform. Fixes #285.ghostel-prompt-regexpdefcustom provides a prompt-detection fallback forghostel-input-start-pointandghostel-beginning-of-input-or-linewhen OSC 133 shell integration isn't available (raw zsh, Python REPL, sqlite3, ssh into unprovisioned hosts). Default recognizes$ # % > >>>and themed prompts (λ ❯ ➜ →). OSC 133 still wins when present.- Public cursor-state API for integrations:
ghostel-input-start-point,ghostel-cursor-point, andghostel-point-on-cursor-row-p. Pure state reads — safe to call from any input mode.
- Bash's OSC 7 cwd report now uses the real kernel hostname via
${var@P}on\H(bash 4.4+) instead of$HOSTNAME. Toolbox and container runtimes export$HOSTNAMEwith a value that disagrees withgethostname(2), so Emacs's(system-name)saw a mismatch and TRAMP fired on everycd. Falls back to$HOSTNAMEon bash <4.4. Fixes #276. - OSC 7 is now emitted after any user-supplied
PROMPT_COMMAND/precmd_functionsentries, so competing emitters (e.g. Fedora's/etc/profile.d/vte.sh) can no longer overwrite ghostel's cwd report and cause buffers to be misclassified as remote. - Auto-composition is now disabled on TTY frames to fix the
off-by-one column drift caused by emoji + VS-16 grapheme
clusters (e.g. 🗂️) — Emacs's
char-widthreports 1 while VS-16-honoring terminals paint 2 columns, desyncing the TTY screen cache. GUI frames are untouched. Fixes #274. - Glyphs that occupy a narrow cell but have an empty cell after them are now promoted to wide on render, so emoji and other ambiguous-width sequences land in the right column even when upstream width tables disagree with the terminal.
- Internal Zig refactor of
adjustGlyphsandputTextProperty; no user-visible change beyond the width promotion above. - Removed dead debug entry points (
fnDebugState,fnDebugFeed) and unused render state fields.
ghostel-bold-colordefcustom mirrors Ghostty 1.2.0'sbold-color: bold text with palette colors 0-7 is mapped to the 8-15 bright slot when set tobright(the default), or rendered in a fixed face/color when set to a color spec. Existing terminals pick up the change live.
S-<insert>is bound toghostel-yank, matching the conventional alternative-paste binding in browsers, terminal emulators, and file managers. In the same pass, semi-char and read-only maps layer<remap> <yank>so any key Emacs binds to yank globally —s-von macOS plus user rebinds — routes throughghostel-yankwithout per-key entries.C-ystays explicitly bound so user rebinds of the global yank key cannot break ghostel paste; line mode keeps Emacs's regular yank so paste lands in the input region. Closes #263.ghostel-readonly-RET-or-exit-and-sendnow honoursghostel-readonly-fast-exitwhen point is on a hyperlink: read-only mode exits first (capturing the URL beforehand, since exit moves point andfile:///fileref:links switch buffers), then the link is opened. Previously RET on a hyperlink left the buffer in copy mode.
- Exiting read-only modes (copy, Emacs) no longer flickers.
The viewport snap-back to the live terminal cursor used to
schedule an async redraw; in the gap, Emacs would observe
point at
point-maxand recenter, only for the timer to anchor the window back a few ms later. The redraw is now synchronous, so the window is anchored to the active viewport before redisplay runs. Fixes #269. - Upgrading the elisp package ahead of the native module no
longer requires two restarts. build.zig writes a
ghostel-module.versionsidecar next to the binary, andghostel--load-moduleconsults it beforemodule-loadand refuses to map a stale.so— so a fresh install loads in the same Emacs process. The interactive version check also runs unconditionally on existing installs without a sidecar, soM-x ghostelsurfaces the install prompt instead of only warning. Fixes #256.
ghostel-mouse-drag-input-modedefcustom: a bare left click in semi-char mode no longer freezes the buffer into copy mode on press — it only focuses the window and sets point, matching standard Emacs behavior. A drag switches input mode on release so streaming output cannot clobber the selection; the target mode iscopy(default; freezes redraws, selection is rock-solid),emacs(terminal keeps streaming, buffer becomes read-only), ornil(stay in semi-char; scrollback selections still survive, selections over live-redrawn rows can be lost). Closes #257.ghostel-module-directorydefcustom selects where the compiled native module lives. Defaults to the package directory (current behavior). Set it to a path outside the package manager's tree (e.g. elpaca'srepos/dir, which gets deleted on rebuild) to keep the artifact stable across rebuilds.M-x ghostel-module-compilebuilds in the resource root and moves the artifact into the configured directory.ghostel-password-prompt-debouncedefcustom (default 0.2s). Ghostel's canonical+!echo heuristic is checked on every redraw (sub-100ms), so short-lived termios flips that ghostty's 200ms poller silently misses became user-visibleread-passwdpopups in ghostel. The rising edge is now debounced: the source chain only runs if the heuristic still reports password mode at the deadline. Sub-debounce flickers leave nothing behind except a brief mode-line indicator flash, matching ghostty's transient lock-icon UX.
ghostel-download-moduleandM-x ghostel-module-compilenow write to a sibling temp file andrename-fileit into place. Renaming swaps the directory entry to a fresh inode, so any Emacs still holding the previous file mmap'd retains a valid mapping — updating the native module is safe under a running Emacs and no longer risks crashes on the next code-resolution. Fixes #247.- Native module internals: function-registration table reworked
for readability and alphabetical ordering; error/logging
helpers unified; ergonomic variadic
Env.f("fn", .{…})replaces the per-aritycallNfamily; remaining legacy interop usage removed. No user-visible API changes, but the Elisp/native pairing changes — the bundledghostel-module-versionis bumped andghostel--minimum-module-versionis now0.25.0. UseM-x ghostel-download-moduleafter updating ghostel.el.
- An open
read-passwdminibuffer that ghostel opened auto-cancels on the falling edge of the heuristic. When the foreground program exits (e.g. the user kills sudo withC-c C-csending Ctrl+C), the prompt now disappears with it instead of waiting for the user to dismiss it. Three gates ensure ghostel only aborts its own minibuffer (active-flag, minibuffer depth, and the minibuffer-buffer identity captured at open time), so unrelated concurrent minibuffers are never closed.
- OSC 133 imenu integration: each shell prompt with OSC 133
A/B/Cmarkers becomes an imenu entry of the form<cwd> <command>, with the target landing on the prompt prefix's start. Composes withconsult-imenu,imenu-list, evil's]m/[m, etc. The cwd is captured at command-start (OSC 133C) and tracked chronologically, so prompts issued before acdkeep the correct directory in their imenu label. Selecting an entry leaves line and copy modes untouched; only semi-char and char modes switch to Emacs mode so point can move. ghostel-readonly-fake-cursor: shows a thin hint cursor at the live terminal-cursor position when point has moved away in copy or Emacs mode, so the next-output spot stays visible while the user reads scrollback. Facesghostel-fake-cursor(hollow) andghostel-fake-cursor-box(solid) are user-customisable; the hollow box uses:line-width (-1 . -1)to stay inside the character cell and not reflow the line. Updates are driven bypre-redisplay-functionsinstalled buffer-locally on read-only entry — nopost-command-hookoverhead.- Click-to-focus in semi-char mode no longer drops the press when
no app is tracking the mouse. Left mouse-1 falls back to
mouse-drag-region/mouse-set-point/mouse-set-regionwhen no DEC mouse-tracking mode (1000/1002/1003) is active, so click-set-point and drag-to-select work in any input mode. (Refined further in 0.25.0 byghostel-mouse-drag-input-mode.) ghostel-detect-password-prompts— defcustom (default t) gating the entire password-prompt detector.ghostel-compilebinds it to nil buffer-locally because compile buffers run withstty -echoto avoid double-echoing the command, which puts the pty into the exactcanonical+!echostate the libghostty heuristic matches — leaving detection on would pop aread-passwdminibuffer at the start of every compile.M-x ghostel-debug-password-events-show— view the last 32 password-prompt rising edges with the detection arm that fired (zigorregex), the cursor row text, and the buffer's remote-shell state. Capture is enabled byM-x ghostel-debug-start; the events buffer survivesghostel-debug-stop.
ghostel-password-prompt-regexnow defaults tocomint-password-prompt-regexp(the same regexM-x shell,M-x term, and eshell use). The previous default ([Pp]ass\(?:word\|phrase\)[^:]*:[ \t]*\') was too permissive and caused the false-positive class fixed below. Users who want to extend the regex should prefer customizingcomint-password-prompt-regexpitself (BEFORE loading ghostel) so other Emacs facilities benefit from the same change.ghostel--password-prompt-detected-pnow returns nil or a symbol (zigorregex) identifying which arm fired. Truthiness is unchanged for callers that treat the return value as a boolean.- Wide-char / emoji rendering is now handled in the native module
rather than by an elisp pixel-compensation pass. The renderer
reports glyph adjustments directly, so emoji-heavy output no
longer trips the
string-widthvs. pixel-width divergence that used to overflow the window. Eliminates theghostel--has-wide-chars/ghostel--compensate-wide-charsround-trip on every redraw. - Native module switched to strict Zig error handling: bare
catch {}swallow sites in color, PWD-update, and render-state paths now log to*Messages*(or signal) with the unifiedghostel: [function] failed: [error]pattern. Cursor tracking no longer goes through accessor methods that clobbered render state; the renderer publishes cursor position as buffer-local variables matching what was rendered, which removes a class of subtle bugs.
- Spurious
read-passwdminibuffer prompts when the terminal cursor row happened to end inPassword:/passphrase:— typingecho Password:at a local shell prompt would pop a password prompt because the regex fallback ran unconditionally whenever the libghostty heuristic returned nil, and the regex itself was anchored only at end-of-line. The fallback regex now defaults tocomint-password-prompt-regexp(structurally anchored at start-of-line or after curated trigger words), and the fallback only runs whenghostel--remote-shell-pindicates a remote shell — local raw-mode TUIs (vim, less, htop) don't risk false positives from coincidental cursor-row content. Fixes #244. consult-line,consult-imenu, and othergoto-charjumps in line mode no longer snap back to the live cursor. The minibuffer that consult opens resizes the ghostel window twice (open + close); each resize forced a redraw whose anchored-window predicate ignoredwindow-point, so the closing resize re-anchored the window and yanked point back to the live cursor. The predicate now also checkswindow-pointagainst the anchor.- Several evil-ghostel operator and motion bugs in multi-line TUI
input and single-line shell prompts:
dd/ccon a non-cursor row of a multi-line input (pi, ipython, prompt_toolkit) now deletes the line at point instead of the last line;cwlands at the start of the deleted range, not at column 0;dwon a single trailing space treats it as content in single-line ranges;0/^skip the shell prompt prefix on prompt rows and fall through to the original motion on scrollback / output rows;^/$/0followed byipreserves the navigated column across scrollback; column navigation in normal state survives idle redraws on the cursor's line;evil-replace's paste count matches its delete count when trailing whitespace is stripped. Underneath: cursor delta math now subtracts scrollback before comparing buffer to viewport rows, and a newevil-ghostel--shadow-cursormodels the pending terminal cursor between PTY-bound key emits and their echo back through the redraw. Fixes #218. <mouse-2>in semi-char or copy mode no longer drops the click when no app is tracking the mouse. Middle-click now feedsgui-get-primary-selectiontoghostel--paste-text(bracketed paste at the live prompt) when no tracking mode is on, matching the standard X primary-selection paste on Linux. In copy / Emacs mode withghostel-readonly-fast-exiton, the click exits to the prior input mode first so the paste lands at the prompt; with fast-exit off it pastes in place, mirroringghostel-yank/C-y.- RET in copy mode no longer dies in
text-read-onlyafterghostel-mouse-press-or-copy-modeflipped the buffer into copy mode on a focus click. RET now exits read-only mode (whenghostel-readonly-fast-exitis on) and forwards a CR via the encoder; hyperlinks under point still callghostel-open-link-at-pointas before.C-c C-lwas renamed toC-c M-lso the parent map'sghostel-line-modebinding is no longer shadowed. Fixes #251.
- Eat-style input modes. Five modes replace the old semi-char /
copy duality:
semi-char(terminal-focused default),char(send-everything, including most Emacs prefixes),emacs(live but read-only — buffer keeps streaming whileisearch,occur,M-x, mark +M-wall work over the same vocabulary that reads any Emacs buffer),copy(frozen, point-based navigation), andline(shell-style local edit, send whole lines on RET, with filename / env / executable / pcomplete /bash-completion-backed TAB completion, history ring onM-p/M-n,C-c C-cinterrupt, andC-dEOF on empty input). Mode switches live in a slim base map so they work from every live mode:C-c C-jsemi-char,C-c M-dchar,C-c C-eemacs,C-c C-tcopy,C-c C-lline,M-RETescape from char mode. Prompt navigation (C-c M-n/C-c M-p) auto-enters Emacs mode so the terminal keeps running while the user jumps between prompts. Line mode discovers the prompt via the terminal cursor (newghostel--cursor-row-char-offsetZig binding that walks the cursor row's cells so wide / box-drawing glyphs map correctly), refined by OSC 133 markers when present, so it works in REPLs and shells without integration loaded. README gains an "Input modes" section andevil-ghostelnow gates on semi-char specifically. Closes #40. ghostel-compileis now read-only by default, mirroringM-x compile:greruns,n/pwalk errors, RET jumps,C-c C-cjumps,C-c C-kinterrupts.C-u M-x ghostel-compileopens a writable ghostel terminal (the previous default); useful forhtop,less,read -p, test prompts. Two new commands flip a buffer between states mid-run without restarting:ghostel-compile-switch-to-interactive(C-c C-j) andghostel-compile-switch-to-readonly(C-c C-e).mode-line-processshows:run/:run/iso the current state is visible at a glance.ghostel-recompileandM-x revert-bufferboth preserve the launch mode of the source buffer;next-error/M-g n/M-g pwork mid-run in either variant.ghostel-compile-global-modeadvice now routescompilation-start CMD tto a writable ghostel terminal — callers asking for the comint variant still get a real TTY, just rendered by ghostel.- Password prompt detection. When
sudo,ssh,gpg,passwd, etc. ask for a password ghostel pops upread-passwdand sends the answer through the PTY — the keystrokes never flow through Emacs's normal key pipeline, so the password does not land inview-lossage, the recent-keys ring, or any keyboard-macro recording. Detection mirrors libghostty's heuristic (the slave tty is in canonical mode with echo off) via a tinytcgetattrZig binding, with a regex fallback on the cursor row when the local tty's echo state can't be observed (remote ssh, programs that don't toggle echo). Mode-line shows🔒Passwordwhile a prompt is open, the wire copy of the password isclear-string'd immediately after the send, and wrong-password retries auto-detect via cursor movement. Customizeghostel-password-prompt-functions— a chain of(ROW) -> string-or-nilsources tried in order — to plug inauth-source(or Keepass / pass / etc); the defcustom docstring includes a TRAMP-awareauth-source-pick-first-passwordexample. PR #241. ghostel-debug-ghostel, a wrapper aroundM-x ghostelthat installs self-removing advice onghostel--spawn-ptyandghostel--start-processto capture program / args / geometry / stty-flags / extra-env / process-environment, the wrapper command list passed tomake-process(caught viacl-letf*onmake-processso TRAMP's file handler can't rewrite it before capture), per-phase spawn timings, the first ~16 KB of PTY output as a(timestamp . chunk)event log, and the first ~64 sends.ghostel-debug-inforenders the capture on a single chronological timeline (sends and PTY output interleaved) and grows a TRAMP section withtramp-version,tramp-terminal-type, the resolvedtramp-direct-async-process, multi-hop length, and the local-vs-toplevel TERM divergence diagnostic that surfaces whentramp-local-environment-variable-pwould silently strip the pushed TERM.C-u M-x ghostel-debug-infoon a remote buffer additionally runs a single-round-trip remote probe (infocmp,xterm-ghostty/xterm-256colorterminfo,~/.local/share/ghostel/terminfopaths,/bin/shidentity, remotebash --version,$INPUTRC, and the first 80 lines of~/.inputrc). All capture lives inghostel-debug.el— plainM-x ghostelsessions are unaffected.- Local "Key encoding (legacy mode)" section in
ghostel-debug-infothat probes a fresh terminal for the chords commonly cited in inputrc reports (C-Backspace,M-f,C-M-f,C-M-v, …) and shows the resulting bytes, mirroringghostel--send-encoded's encoder + raw fallback so the output matches what a real keystroke produces. repeat-modesupport for prompt and link navigation:C-c C-n C-n C-ncycles hyperlinks,C-c M-n M-n M-ncycles prompts (baren/pwork after releasing the modifier). Internally, eight keymaps inghostel.elare converted fromlet/define-keyblocks todefvar-keymap;compat 30.1.0.1is added toPackage-Requiressodefvar-keymapis available on Emacs 28.
C-M-<letter>chords (C-M-f,C-M-v, …) are now bound inghostel-mode-mapand routed through libghostty's encoder so readline.inputrcrules like"\e\C-f": dump-functionsactually fire. Without the binding, Emacs'sforward-sexp/scroll-down-commandran instead of reaching the shell. Fixes #239.- Remote spawns now apply
stty saneas a baseline (matching vterm) instead of the prior per-spawn flag list that omittedechoon the remote path withoutghostel-tramp-shell-integration. Any upstream that left the PTY with echo cleared (TRAMP env stripping, custom remote/etc/bashrc, old bash readline) no longer produces silent input on the remote shell. All five spawn paths share a singleghostel--default-sttyconstant. Fixes #224 (third iteration). ghostel--set-buffer-facenow caches the last(FG . BG)pair and short-circuits theface-remapround-trip when the colors haven't changed. The render path called this on every dirty redraw, andface-remap-remove-relative/face-remap-add-relativeeach callforce-mode-line-updateinternally — measured at ~960 FMLU per 5s with two visible buffers running spinners, dropping to ~50 after the cache. The visible symptom was the minibuffer flickering andC-g/ RET taking several attempts to land while output was streaming.
ghostel-next-prompt/ghostel-previous-promptnow land correctly with the realistic two-propertyghostel-prompt/ghostel-inputrow layout.ghostel--prompt-input-startusedskip-charsheuristics that jumped past the last char + newline for any input ending in a single-char arg (e.g.echo b) and landed on the last word for multi-word commands; previous-prompt failed to recognize point inside aghostel-inputregion or at the start of input as "still on the current prompt" and snapped back to the same prompt instead of the prior one.C-gin semi-char and char input modes now reachesghostel-send-C-grather than the raw^Glambda installed byghostel--define-terminal-keys. The terminal-key loop ran on the per-mode child maps and shadowed the parent'sC-gbinding, re-introducing #200 —deactivate-markand thequit-flagclear were skipped. The loop now skips?g, and char mode binds it explicitly.ghostel-readonly-copy(M-w/C-w) and the copy-mode branch ofghostel-xterm-pastenow honourghostel-readonly-fast-exit nilinstead of unconditionally exiting copy/Emacs mode on those keys.ghostel-readonly-copydeactivates the mark likekill-ring-savedoes, soM-w/C-wno longer leaves the region highlighted whenghostel-readonly-fast-exitis nil. Closes #238.- RET on a
ghostel--detect-urls-linkified cell no longer hijacks the keystroke away from the PTY in the default terminal mode. RET is dropped fromghostel-link-map(text-property keymaps outrank evenemulation-mode-map-alists) and bound onghostel-copy-mode-mapinstead, so click is still a real click in any mode but RET routes to the local map. Link detection on the cursor's own row is now skipped regardless of OSC 133 state, so REPLs and shells without integration loaded (Gemini CLI, raw bash, …) no longer linkify the typed command. - In evil normal state, output that grew scrollback no longer leaves point above the new prompt. The around-redraw advice used to restore point by buffer position; it now tracks the buffer line where the previous redraw placed point at the terminal cursor and lets the renderer's new placement stand when the user has not navigated. Closes #228.
ghostel-debug-infono longer raisesvoid-variableon Emacs 28/29.tramp-direct-async-processwas added in Emacs 30.1's bundled TRAMP; the variable read is guarded withboundpand a forwarddefvarquietsbyte-compile-error-on-warnin CI.
-
Loading
ghostel.elno longer prompts to download or compile the native module under any circumstances. Previouslyghostel.elconsultedghostel-module-auto-installat load time and could open an interactiveread-char-choiceprompt — this hung Emacs 31user-lisp/auto-byte-compile and similar harnesses where a user(setq ghostel-module-auto-install nil)had not yet been evaluated. Module installation now happens only on an explicit user action:M-x ghostel,M-x ghostel-download-module, orM-x ghostel-module-compile. When the module is missing at load time the package issues adisplay-warninginstead. Closes #231. -
No-echo on remote shells launched from a TRAMP
default-directory(ghostel-tramp-shell-integration nil). The previous fix reboundtramp-terminal-type, which only takes effect on the generictramp-handle-make-processpath; the ssh-method path (tramp-sh-handle-make-process) ignores it. In addition, when the local default-toplevelprocess-environmentalready hasTERM=xterm-ghostty(e.g. Emacs launched from ghostty),tramp-local-environment-variable-pstrips ghostel's pushed TERM as "ambient", and the remote shell inherits TERM=dumb from TRAMP's connection shell — disabling readline/ZLE/fish line editing.The per-spawn
/bin/sh -cwrapper now sets TERM itself on the remote, after probing forxterm-ghosttyterminfo viainfocmp. Single path covers auto-integration (TERMINFO pushed), manual install (system or~/.terminfo), and the bare case (fall back toxterm-256colorso echo works). The bogus local TERMINFO path is no longer pushed to the remote. Closes #224 again.
- Manual remote-integration setups can now drop the bundled
xterm-ghosttyterminfo at~/.local/share/ghostel/terminfo/alongside the shell scripts (scp -r etc/terminfo/{x,78}from the local package). TRAMP-spawned remote shells detect it and prepend the directory toTERMINFO_DIRSautomatically — notic, no touching~/.terminfo. README "Option 2: Manual setup" updated with the one-shot install recipe.
ghostel-pre-spawn-hook, run insideghostel--spawn-ptyjust beforemake-processwithprocess-environmentdynamically bound to the about-to-be-spawned env. Hook functions cansetenvto inject entries the child inherits. Intended for integrations like with-editor — with awith-editor-setup-environmentexposed upstream, users can wire Magit'sEDITORplumbing into ghostel buffers via(add-hook 'ghostel-pre-spawn-hook #'with-editor-setup-environment). Fires for bothghostel/ghostel-projectandghostel-execspawns;ghostel-compilehas its ownmake-processand is not covered.evil-ghostel-escapecontrols how ESC is routed in evil insert state:auto(default) inspects DECSET 1049 to send ESC to the terminal in alt-screen apps (vim, less, htop, …) and otherwise fall back toevil-normal-state; explicitterminalandevilvalues force one or the other. A toggle command with numeric prefix support is also bound. The terminal-bound ESC snaps to the live viewport like every other typed key; the evil-bound fallback lands onevil-force-normal-statewhen the user's<escape>binding is missing or a chord prefix (#215).make bench-e2e(and a--e2eflag onrun-bench.sh) measures whole-pipeline throughput by installing each backend's production filter+sentinel on acatsubprocess and waiting for full quiescence, so the wall clock reflects what users actually feel (including ghostel'sdelayed-redrawlink detection / anchoring, vterm's regex split + decode loop, and eat's deferred queue). Composes with--quick,--size,--iterations, and the backend-skip flags.
- The Zig native module has been broadly refactored ("Zigify
everything"): libghostty calls are wrapped to be return-value
oriented rather than out-pointer oriented, errors propagate via
Zig's
try/catchand error unions instead of C error codes, optional absence is distinguished from real errors, and per-error logging/handling is consistent across the module (#217). This is internal — no user-visible API changes — but bumps the minimum required native module version, so update both ghostel.el and the prebuilt.so/.dylibtogether. - Render code is reorganised around a single property-run
abstraction (prompt cells, input cells, and ordinary content all
flow through the same path), and uses a new internal
FixedArrayListto cut per-row allocation overhead. Adds an Emacs↔Zig logging/debugging layer used during the refactor and available for future native-module work.
- Launching
M-x ghostelfrom a TRAMPdefault-directory(e.g. afterfind-file /ssh:host:) now produces a usable remote shell. Previously TRAMP'smake-process' handler reset TERM totramp-terminal-type' (default"dumb"'), which caused bash/readline, zsh/ZLE, and fish to disable interactive line editing on the remote: typed characters didn't echo, although Enter still submitted the line. Ghostel now rebindstramp-terminal-type' for its remote spawns toxterm-256color' (orxterm-ghostty' when `ghostel-tramp-shell-integration' has pushed the bundled terminfo), restoring echo and line editing (#224). - Shell integration survives prompt themes and rcfile assignments
that overwrite
PROMPT/PS1/fish_promptafter ghostel sourced its bootstrap. The OSC 133 A/B markers are now (re)installed every prompt cycle — modeled on ghostty's own zsh/bash/fish integrations — so powerlevel10k, agnoster, Pure, oh-my-zsh / prezto add-zle-hook chains, bashPROMPT_COMMANDreassignments, and fish themes loaded viaconf.d/no longer strip the prompt-range markers. Without those markers, the file-path link detector linkified user-typed cells and the link keymap's RET binding shadowed the normal terminal RET in tty Emacs — pressing RET on a typedcd some/pathopened the link instead of executing the command (#199). - New terminals no longer briefly flash with libghostty's default colors before ghostel applies the Emacs theme. A regression test guards against the flicker reappearing (#219).
ghostel-keymap-exceptionsnow also excludes special keys (<return>,<tab>,<f1>, …, including theirS-/C-/M-/C-S-/M-S-/C-M-variants). The special-keys binding loop inghostel-mode-mapwas missing the exceptions check that theC-<letter>andM-<letter>loops had, so users could not exclude e.g.C-<return>orC-M-<down>(#210).ghostel-download-moduleno longer segfaults when the module is already loaded. Callingmodule-loadon a path whose shared library is mapped into the running Emacs makes dyld/ld.so return the existing handle and resolveemacs_module_initviadlsymon the stale image. Ghostel now skips the secondmodule-loadwhen the module is alreadyfeaturep'd and tells the user to restart Emacs to pick up the new version (#78).
ghostel-spinner-progress, a built-in handler forghostel-progress-functionthat animatesmode-line-processvia spinner.el during indeterminate progress (e.g. while Claude Code is working) and shows percentage text for determinate states. spinner.el is a soft dependency: when it is on theload-pathat ghostel load time,ghostel-progress-functiondefaults to this handler; otherwise the existingghostel-default-progresstext indicator is used. Newghostel-spinner-typedefcustom (defaultprogress-bar) selects the spinner style.
- Resize redraw work now scales with the resized axis. Column changes still trigger a full scrollback rebuild (cell wrapping depends on width), but row-only changes only re-render the visible area and defer until after the next redraw, eliminating noticeable lag when only the height changes (24f6653).
- Claude Code's progress reports now update
mode-line-processin ghostel buffers. Claude Code gates OSC 9;4 progress emission onTERM_PROGRAM_VERSIONparsing as semver>= 1.2.0; ghostel advertisedTERM_PROGRAM=ghosttywithout the version, so the gate failed and progress was silently dropped. Ghostel now also exportsTERM_PROGRAM_VERSIONmatching the vendored libghostty pin, satisfying Claude Code's check and any other consumer that applies the same probe. - Plain-text URL/file detection no longer linkifies the cell the
user is typing into. In tty Emacs,
RETon a linkified cell resolved toghostel-open-link-at-point(text-property keymap overridesghostel-mode-map), so pressing return at a path the shell echoed — e.g.cd src/main.rs— opened the file instead of running the command. The renderer now marks OSC 133 B..C cells asghostel-inputandghostel--detect-urlsskips the prompt prefix unconditionally and the active input line. The bundled bash/zsh/fish integrations are updated to emit 133;A and 133;B from inside the prompt itself rather than back-to-back before prompt expansion, so libghostty sees a non-empty PROMPT range (c145c5e, closes #199). - OSC 51;E callbacks dispatch synchronously from the process
filter rather than waiting for the next redraw timer tick.
Callers like
b4 prep --edit-coverwrite the OSC and continue, cleaning up their tempdir before deferred elisp couldfind-filethe path; deferred dispatch producedSetting current directory: No such file or directory. Matches vterm's behavior. OSC 51;A (directory tracking) is left deferred since it has no such race (3f8846c, fixes #209).
- Style-run break detection during render uses a cheap
CellStyleKeyrather than a fullCellStylecomparison, cutting per-cell overhead on large dirty regions (81f1258). - libghostty can now be built with optimization settings independent from the ghostel module itself, so debug ghostel builds no longer drag libghostty into debug mode (5baea2d).
- Kitty graphics protocol support — render images inline in the
ghostel buffer for both traditional non-virtual placements
(timg, kitty +kitten icat, applications using direct kitty
graphics) and unicode-placeholder placements (yazi, modern image
previewers). All decoding, storage, and protocol parsing flow
through libghostty's kitty graphics C API; ghostel queries the
placement iterator each redraw and applies image overlays in
Emacs. Non-PNG pixel data (RGBA / RGB / GrayAlpha / Gray) is
converted to PPM (P6) for Emacs's built-in image renderer; PNGs
go through libghostty's PNG-decode hook backed by vendored
stb_image. Per-row slicing (
:ascent 'centerplus aline-heightclamp on the trailing newline) keeps image rows flush even when the placeholder line'sline-pixel-heightis pulled aboveframe-char-heightby a fallback font or nerd-font icon on the same line (57ef5a7). ghostel-cell-pixel-scalecontrols the physical:logical pixel ratio reported to apps that probe XTWINOPS CSI 14/16/18 t. Apps like timg and yazi expect cell dimensions in physical pixels (what standalone Ghostty advertises via the OS window server's backing scale factor), but Emacs only exposes logical pixels — reporting them unscaled makes apps either fall back to half-blocks (timg) or fill many more cells than expected with upscaled, blocky output (yazi). Theautodefault derives a float scale from display DPI (display-pixel-width/display-mm-widthcompared to the 96 DPI reference); a numeric override is available for pixel-perfect parity with standalone Ghostty (57ef5a7).- File detection recognises tilde-prefixed paths
(
~/file.el:42) —~is added to the leading character class and leading anchor ofghostel-file-detection-path-regex(abae518).
OPT_SIZE(XTWINOPS CSI 14/16/18 t) is now answered by ghostel, alongside the existingOPT_DEVICE_ATTRIBUTESreply. Image- rendering tools probe these queries to detect kitty graphics support and pick image dimensions; without a response timg fell back to half-block rendering even whenTERM_PROGRAM=ghostty. Cell pixel dimensions are stored on the Terminal struct and updated on every resize, andghostel--set-sizeis seeded once betweenghostel--newand the process spawn so the very first output (e.g. timg's transmit-and-place) reports authoritative values rather than zero (57ef5a7).
ghostel-execuses the universal 80×24 default when BUFFER is not displayed in any window, instead of sizing the PTY from(selected-window). The selected window had nothing to do with where the agent buffer would eventually be shown — programs ending up in a different window had to rely on SIGWINCH to recover, and TUIs that latch initial dimensions at startup rendered against the wrong size. Matches eat's behaviour; the displayed-buffer path is unchanged (a8bf9ae).
0.19.0 — 2026-04-29
ghostel-debug-keypressarms a one-shot capture of the next keystroke in the current ghostel buffer and renders a paste-ready diagnostic (raw event, resolved keymap binding, every byte sent, active DEC mode flags, coalesce-buffer state) into*ghostel-debug-keypress*— enough to distinguish kitty CSI-u from legacy encoding without any new native binding (07b8438).ghostel-debug-infogains anEnvironmentblock (env vars passed to the spawned shell —TERM,COLORTERM,TERMINFO,TERM_PROGRAM,INSIDE_EMACS,ghostel-environmentoverrides, and pass-throughLANG/LC_*), aSize syncblock (Emacs window body rows vsghostel--term-rowsvs Emacs's recordedold-body-pixel-height, with explicit verdicts for in-sync / chrome-absorbed-but-unreconciled / pending-redisplay), and aRenderingblock (default face, resolved font with fallback/remap detection,line-spacingbroken into buffer-local / default-value / frame-parameter,face-remapping-alist). The buffer is now read-only (special-mode,qto quit), module-file lookup works in the dev /package-vclayout, and customized defcustoms are auto-detected by comparing againststandard-value(0b19011, d717732, ea594fc, e8eb2f8).- FreeBSD release artifact built via Zig cross-compile from the
Linux CI runner, using the same matrix-driven
-Dtarget=...pattern asaarch64-linux-gnu(81abb18). list-buffers-directoryis set to the buffer'sdefault-directoryinghostel-modeandghostel-compile-view-mode, and is updated by OSC 7 directory tracking — sobuffer-menu/ibuffer(and any consumer that reads the variable) can categorise ghostel terminals by working directory. Mirrorsshell-mode's convention (75fe69f).tui-partialbenchmark renders the static screen once, then updates only the bottom row per iteration — the workload that status bars and prompt redraws actually produce. Exposes that ghostel's per-row dirty-bit branch is 8–13× faster than full mode at 24×80 and 40×120 (6a4a069).
- Native rendering rewritten. Each pass parks the libghostty
terminal at
max_offset - 1, which lets us track scrollback correctly across the libghostty cap by detecting eviction from the parked offset, identify scrollback-clear (CSI 3Jet al.) reliably via theoffset+len==totalsnap-back signal without per-byte VT scanning, use libghostty dirty flags directly to identify stale lines (fixes the case where promoted scrollback rows could carry outdated content), and evict old scrollback rows from the Emacs buffer in lockstep with libghostty's ring-buffer wrap. OSC 8 hyperlink handling switches from storing URI strings on every run to a lazyhelp-echolookup viaghostel--native-uri-at. The Emacs buffer now always carries a trailing newline so line-math is uniform across the codebase (8e3135f). - PTY size is now captured against the buffer's window via
window-screen-lines, not the selected window viawindow-body-height. When a theme remaps the buffer's default face — e.g.nano-light/nano-darkbumping it ~7% — the two metrics disagree and the previous capture spawned the PTY too tall, then issued a startup SIGWINCH that some TUIs (Claude Code's/tuifullscreen) mishandle. Updates every spawn / commit / reconcile site (ghostel--init-buffer,ghostel-exec,ghostel--commit-cropped-size,ghostel-compile). Closes #192 (23cdc7c, ce966eb). - Terminal size is reconciled via
window-buffer-change-functions(event-driven) instead of a 50 ms idle timer. When a ghostel buffer migrates to a window of a different size (popup dismissed,+popup/raise, etc.) no Emacs-visible window-size-change event fires — only the buffer-to-window mapping changed — so the existingadjust-window-size-functionmachinery did not run. The hook covers the buffer's whole lifetime cleanly (387c275, b9c12ca). - README's manual
~/.bashrc/~/.zshrc/~/.config/fish/config.fishsource gate now uses a prefix match (${INSIDE_EMACS%%,*}, fish-anchored regex) so TRAMP-rewrittenINSIDE_EMACS=ghostel,tramp:VERmatches. The remote-host gate adds aTERM=xterm-ghosttyfallback because plainsshcannot propagateINSIDE_EMACSwithoutAcceptEnvconfigured server-side. Also updates the source-comment header inetc/shell/ghostel.{bash,zsh,fish}and adds a TRAMP canary test ontramp-inside-emacs(d3ecac0). package-lint,checkdoc, anddocquotes(a regex check for back/front-quoted non-symbols, widened from melpazoid's[A-Z]+to[A-Z_]+so identifiers likeINSIDE_EMACSaren't skipped) now run in CI as a single Emacs 29.4 lint job;evil-ghostel.elis byte-compiled and linted alongsidelisp/(5d73106, 5321d1b).
- Coalesced single-byte input is drained before every direct PTY write from Zig (key encoder, mouse encoder, OSC 4/10/11 replies, focus events, VT write-back). Encoded keystrokes could otherwise overtake preceding self-insert bytes (0b37dae).
C-gin a ghostel buffer now also deactivates the active region, matching the side effect users expect fromkeyboard-quit(theinhibit-quitbinding routesC-gthrough the keymap toghostel-send-C-g, which previously only sent SIGINT). Closes #200 (14b4e85, 7631ea9).- IME preedit anchor stays stable during redraw — the cursor row no longer drifts while typing into an active preedit window (90f1f71).
- Evil state transitions no longer wipe + rebuild the buffer once
any scrollback exists. A
deferinfnCursorPosition/fnDebugState/fnDebugFeedwas scoped to an innerif (term.getScrollbar()) |sb| { … }block, so the viewport-restore fired before theSCROLL_BOTTOMcall below it. Each call left libghostty parked at the bottom (offset+len==total), which the next redraw mistook for a scrollback-clear signal. Hoisted the defer to function scope. Folded in: a heap-fallbackdefer ... .free(buf)infnUriAtwas scoped to the inner alloc block and freed beforemakeStringread through the alias — hoisted, plus aSUCCESScheck onghostty_grid_ref_hyperlink_uriso error returns no longer stringify uninitialised data (8e3135f).
0.18.1 — 2026-04-25
ghostel-plain-link-detection-delayuser option (default 0.1s) controls how long ghostel waits after a redraw before scanning for plain-text URLs and file paths. Set to 0 to restore the previous synchronous behavior (671d3ee).
- Plain-text link detection is now deferred off the redraw path and coalesced via a single timer, so bursts of redraws collapse into one scan instead of running detection on every dirty redraw. Native OSC-8 hyperlink spans continue to be handled inside the renderer. The process sentinel cancels the pending detection timer so it cannot fire against a buffer that is about to be killed (671d3ee).
- Scrollback rotation detection now snapshots the first scrollback
row directly (
std.mem.eqlover allterm.colscells) instead of hashing the first 16 cells with FNV-1a. Removes a small collision probability and the arbitrary 16-cell sample that could miss rotation when two rows shared the same opening cells; the cached-read optimisation that skips the end-of-redraw round trip is preserved (4b1a0ba).
0.18.0 — 2026-04-24
- Repository layout reorganized. Elisp sources now live under
lisp/(theghostelpackage) andextensions/(independentevil-ghostelpackage); vendored headers moved frominclude/tovendor/; the bundled compiled terminfo moved fromterminfo/toetc/terminfo/; shell-integration assets restructured intoetc/shell/ghostel.{bash, fish,zsh}(user-sourced rc files) andetc/shell/bootstrap/(env- hook shims for local auto-injection) (266e3e9). - Users who source ghostel's shell rc files manually from their own
shell configuration must update the path:
etc/ghostel.{bash,zsh, fish}→etc/shell/ghostel.{bash,zsh,fish}. evil-ghostelis now published as a separate MELPA package. Users who relied on installingghostelalone and getting evil integration for free must now installevil-ghostelseparately. In return,package-vc-install ghostelno longer pullsevilin as a transitive dependency of the single-repo scan.- Removed the
ghostel-evilcompatibility shim that was deprecated in 0.13.0. Replace any(require 'ghostel-evil)with(require 'evil-ghostel)and anyghostel-evil-modecalls withevil-ghostel-mode.
ghostel-environmentuser option (mirrorsvterm-environment): list ofKEY=VALUEstrings prepended toprocess-environmentbefore spawning the shell. Honors.dir-locals.elviahack-dir-local-variables, propagates to TRAMP remote shells, and applies to both shell spawns andghostel-compilespawns. User entries take precedence over ghostel's ownTERM/INSIDE_EMACS. Closes #176 (87c99e5).ghostel-defaultface (inheritsdefault) as the per-buffer customization point for terminal foreground/background, allowing e.g. a dark terminal inside a light Emacs without resorting todefadvice. Closes #178 (7c3fa5b).
ghostelandghostel-projectnow explicitly return the buffer they create or switch to, so callers can use the buffer programmatically without relying onpop-to-bufferside effects. Closes #185 (fdfb68f).- ANSI color faces now inherit from
ansi-color-*instead ofterm-color-*. Themes (notably modus) deliberately remapterm-color-black/term-color-whiteto bright palette entries to keep them distinct fromterm.el's buffer face — that accommodation made e.g. htop's status bar render gray-on-green and unreadable.ansi-color-*is the canonical ANSI face family since Emacs 28.1 and themes customize it to the proper palette. Closes #175 (a27f2fa).
- Scrollback no longer leaves stale rows after
CSI 3J(clear-scrollback) followed by enough new output to restore the same scrollback depth. A unifiedrebuild_pendingflag now tracks all scrollback-validity signals (resize, CSI 3J, rotation hash mismatch); the surgical-trim fallback that misbehaved on reflow is replaced with a single full-erase path. Closes #160 (f5524ef). - A ghostel buffer that received output while hidden no longer
shows a stale pre-hide screen on re-show. A per-window snap list
populated via
window-buffer-change-functionsforces the next redraw to anchor to the latest output. Closes #177 (63e008f). - The first ghostel buffer in a session now respects
display-buffer-alist. Fixes #179 (d33052d). ghostelandghostel-projectreuse an existing terminal buffer even afterghostel--set-title-defaulthas renamed it. Buffers now carry a stickyghostel--buffer-identityset at creation time, and lookup matches on identity rather than current buffer name. Fixes #168 (465030e).- Bind
[xterm-paste]to a ghostel-aware handler so clipboard pastes delivered by the host terminal (TTY Emacs with bracketed paste) reach the inferior shell instead of being inserted into the renderer-owned buffer and wiped on the next redraw. Fixes #172 (5546b97). - Meta-modified keys (
M-x,M-DEL, …) now reach the terminal in TTY Emacs. TTY Emacs deliversM-<key>as an ESC prefix that consumes the meta modifier before the binding fires; the dispatch path now detects theesc-maplookup viathis-command-keys-vectorand re-injects meta. Follow-up to 43220db; fixes #48 (c42451e). - Fish auto-inject now installs
xterm-ghosttyterminfo on remote hosts via thesshwrapper (parity with bash/zsh), and no longer leaks fish's internal vendor-confxdg_data_dirs(with/fishappended) intoXDG_DATA_DIRSfor every spawned subprocess. The vendor-conf shim now chains toetc/ghostel.fishinstead of carrying a drifting inline copy (d9fd009). package-vc-installon Emacs 30.x no longer fails byte-compilingtest/,bench/, andextensions/. A.elpaignorescopes recompilation to the package's lisp directory viabyte-compile-ignore-files. Emacs 31 fixed this upstream (573acd97); the.elpaignorecovers the un-backported case (bcba725).
0.17.0 — 2026-04-21
evil-ghostel-initial-statedefcustom controls the initial evil state in ghostel buffers (defaultinsert). Replaces a hard-codedevil-set-initial-statecall that fired on every ghostel buffer creation and silently clobbered user overrides.:setre-applies the value on change, and thesetq-before-requirepath is honoured on load (5fcbb19).
- Replaced
ghostel-enable-title-tracking(boolean) withghostel-set-title-function. The new option holds the function invoked on OSC 2 title changes; set to nil to disable title tracking, or to a custom function to fully override the rename behaviour (5bd67f1).
marknow survives native redraws. The full-redraw path (eraseBuffer) previously snapped every marker topoint-min, and the partial-redraw path drifted markers asymmetrically by insertion-type — soC-SPC-set marks or normal-state region commands lost their anchor on every frame (4816ece).- Evil visual selections no longer stretch to a multi-row phantom
region in a buffer that is streaming output. The
around-redrawadvice now saves and restoresevil-visual-beginning/evil-visual-endwhile in visual state, in addition topoint(606ec4d). - Removed the
evil-ghostelnormal-state-entry hook that corrupted point after operator commands —yy,v..y, andv..<escape>could discard the motion and land point on the TUI cursor row. Evil's own operator/visual machinery places point correctly without the extra snap (b955dbb).
0.16.3 — 2026-04-20
- Block cursor no longer drifts up a row when a TUI parks it on an
empty last row via absolute positioning (CUP). The
window-pointclamp from 0.16.1 is broadened via a newghostel--cursor-on-empty-row-pnative predicate so the clamp fires on both pending-wrap and empty-trailing-row conditions. Closes #157 (d4fdc8e). - The bundled
sshwrapper inghostel.bash/ghostel.zshno longer fails with a parse error when the user hasalias ssh=…set before sourcing the integration. Usesfunction ssh { … }form to sidestep alias expansion. Fixes #155 (44aaf67).
0.16.2 — 2026-04-20
- Bundled
xterm-ghosttyterminfo underterminfo/(both Linux and macOS hashed-dir layouts). Terminal sessions now setTERM=xterm-ghostty+TERMINFO=<bundled>+TERM_PROGRAM=ghosttyso TUI apps that consult terminfo see ghostel's real capabilities — most notably DEC 2026 (Sync), which Claude Code needs to avoid cascading unsynchronised redraws onM-xwith large scrollback. TRAMP pushes terminfo to a remote temp dir over the existing connection; outboundsshfrom a local buffer is shadowed with a wrapper that installs terminfo on the remote viaticon first use (cached per-host under$XDG_CACHE_HOME/ghostel/, invalidated on libghostty bumps). New options:ghostel-term,ghostel-ssh-install-terminfo,M-x ghostel-ssh-clear-terminfo-cache(2c92f68).
- Minibuffer activation (M-x, vertico, consult) no longer repaints the shell prompt or forces full TUI redraws. Shrinks caused by the minibuffer stealing window space are treated as viewport crops instead of real resizes, suppressing the spurious SIGWINCH. Apps on the alternate screen (vim, htop, less, Claude Code) still receive SIGWINCH because they own the full viewport; selecting the ghostel window while the minibuffer is open commits the cropped size (3e8d9c7).
ghostel-compileheader and early output no longer wrap at the wrong column when the compile buffer lands in a smaller window than the selected one. The VT is now reconciled to the output window's dimensions before rendering the header and before spawning the process (dcbbf1d).M-x kill-compilationnow finds and terminates a liveghostel-compilerun.compilation-locsis declared buffer-locally during the run socompilation-buffer-internal-precognises the buffer (dcbbf1d).
0.16.1 — 2026-04-20
- Block cursor no longer draws on top of the last character while the
user is typing at a shell prompt. The
window-pointclamp introduced in 0.16.0 is narrowed to fire only when libghostty reports the cursor in pending-wrap state, exposed via a newghostel--cursor-pending-wrap-pnative function. Fixes #146 (ad8536e).
0.16.0 — 2026-04-19
- Desktop notifications via OSC 9 (iTerm2) and OSC 777 (rxvt
notify), plus ConEmu OSC 9;4 progress reports. Notifications route throughghostel-notification-function(default usesnotifications-notifywith amessagefallback, dispatched viarun-at-timeso a slow DBus broker can't stall the VT parser); progress routes throughghostel-progress-function(default shows[42%]/[...]/[err]/[paused]in the mode line). OSC 9;9 CWD reports are handled the same way as OSC 7. Closes #141 (4f7b1cd). ghostel-compile-global-mode: opt-in global minor mode that advisescompilation-startso every caller (compile,recompile,project-compile, ...) automatically runs in a ghostel buffer. Falls through to the stock implementation forgrep-mode, comint, andcontinue=non-nil; excluded set is customisable viaghostel-compile-global-mode-excluded-modes(e7164ec).ghostel-send-stringandghostel-send-keypublic API for external packages (agent integrations, custom keymaps) to drive a ghostel buffer without reaching intoghostel--internals. The old internalghostel--send-keyis kept as an obsolete alias; the raw-byte primitive is nowghostel--send-string(5453c22).<XF86Paste>and<XF86Copy>media keys are now bound toghostel-yankandkill-ring-save. Previously they fell through to the global commands and got overpainted by the next redraw (65932e6).
ghostel-compileno longer types its command into an interactive shell. Each invocation spawnsshell-file-name -c COMMANDdirectly viamake-processthrough a PTY owned by the ghostel renderer. Multi-line scripts with embedded newlines now pass through verbatim (the old type-into-shell path interpreted each newline as RET), exit status comes from the process sentinel, and shell integration is no longer required. The banner is written to the VT before spawn so it appears live; interactive programs likehtop,less, andreadprompts keep working because the buffer stays inghostel-modeduring the run (e7164ec).ghostel-recompilenow re-runs into the current buffer when it holds a localghostel-compile--command, so pressinggin a*compilation*buffer produced byghostel-compile-global-modereuses the buffer and window instead of opening a second one (e7164ec).ghostel-compileopens its buffer in a non-selected window, matchingM-x compileexactly. Respectsdisplay-buffer-alist, keeps focus on the caller, andquit-windowdisposes of the window the way users expect. Closes #122 (9846c64).evil-ghostelpoint now tracks the terminal cursor inevil-emacs-state, not justinsert-state— emacs-state is evil's vanilla-Emacs escape hatch and should behave like a normal terminal (f05e0db).- Large TUI redraws (Claude Code, post-resize frames) now stream in a
single filter call.
process-adaptive-read-bufferingis disabled andread-process-output-maxraised to at least 1 MB for ghostel PTYs; pre-Emacs 31 this collapses a 570 KB post-resize frame from ~9 filter calls to 1 — a ~15-second cascading repaint becomes instant. Mirrors what vterm does for the same reason. Fixes #85 (bcf2f0c).
- Child programs that enable focus reporting (Claude Code, btop, vim)
now see focus-out when the user selects a different window inside
Emacs, not only when the whole frame blurs. Adds hooks on
window-selection-change-functionsandwindow-buffer-change-functionsin addition to frame focus. Closes #140 (ddaefbc). - Process sentinel no longer removes the focus-reporting hook globally on exit, which had broken focus reports for every other live ghostel buffer (ddaefbc).
- TUI cursor no longer disappears on the last viewport row when it
lands in pending-wrap state. Clamps
window-pointback by one whenptequalspoint-maxso Emacs redisplay stops shiftingwindow-startup by a row to "make it visible." Closes #138 (17fc791). - Viewport no longer snaps to the prompt when the minibuffer opens (and the ghostel window shrinks) in a scrolled-up TUI. During a resize-triggered redraw, windows that were auto-following before the resize are treated as still anchored rather than as a user scroll. Closes #127 (aa4912d).
- Per-cell face properties (colours from SGR sequences) now survive
when
font-lock-modeis force-enabled in a ghostel buffer — e.g. Doom Emacs setsfont-lock-defaultsglobally, which reactivates font-lock afterghostel-mode's(font-lock-mode -1). A buffer-localfont-lock-unfontify-region-functionneutralises the unfontify pass in bothghostel-modeandghostel-compile-view-mode(28f5071). evil-ghostel: entering normal state in a buffer with any scrollback no longer snaps point to row N of the scrollback region instead of row N of the visible viewport — the row offset now accounts for scrollback line count (69d4b0d).
0.15.0 — 2026-04-17
ghostel-compileandghostel-recompile:M-x compile-style workflow backed by a real PTY, so commands that need a terminal (colour output, progress bars, curses tools) work normally. Finished buffers supportnext-errornavigation and sharecompile-command/compile-historywithM-x compile;grecompiles in the original directory,C-u gprompts to edit the command (5280db2, d72751e).ghostel-eshell-visual-command-mode: overrideseshell-exec-visualso TUI programs invoked from eshell (vim, htop, less, top) run in a dedicated ghostel buffer instead of the defaultterm-modefallback. Addsghostel-execas the public primitive for running an arbitrary program in a ghostel buffer and aneshell/ghostelbuiltin (8df9fc7).ghostel-next-hyperlink/ghostel-previous-hyperlinknavigate OSC 8 hyperlinks, auto-detected URLs, and file:line references viaC-c C-n/C-c C-p; prompt navigation moves toC-c M-n/C-c M-p(895e55b).ghostel-debug-infocommand collects Emacs version, system info, native module version (with mismatch warning), terminal state, and settings into*ghostel-debug*for pasting into bug reports. Resize and redraw events are now logged whenghostel-debug-startis active (b5d7b4d).ghostel-ignore-cursor-changeoption ignores terminal requests that change cursor shape or visibility; useful when editor-owned cursor behaviour should take precedence (c901c02).M-ywith no preceding yank now opens acompleting-readbrowser over the kill ring (works with consult/vertico) instead of signalling an error (e1e1896).
C-gis now sent to the terminal instead of triggeringkeyboard-quit; in copy mode it still exits copy mode (057fb1f).- Linkified file paths in terminal output now also match bare relative
paths (e.g.
src/foo.rs:43:4), paths wrapped in punctuation (Python tracebacks, backticks, brackets), and an optional:columnafter the line number. Configurable viaghostel-file-detection-regex. Closes #107 (ed17efb). - Module auto-download now works on systems that report
amd64/arm64insystem-configuration(27dcec0). - OSC dispatch rewritten to scan each PTY write once instead of five
times. A single
OscIteratoryields(code, payload, terminator)and onedispatchPostWriteOscshandles codes 7/51/52/133 in document order. Engine micro-benchmarks improve ~20–28% on bulk input (819098f, 1729f24). - CRLF normalisation is now zero-allocation and zero-copy. The old
path allocated up to 131 KB of scratch (with heap fallback and a
silent-truncation failure mode) and walked the input twice; the new
path streams raw segments into libghostty's VT parser and emits
\r\ninline at each bare\n. State is persisted across calls so a CRLF pair split between two writes isn't double-normalised (42092e7, 1729f24). - Module loader unified into a single helper. Load-time and interactive-command paths no longer diverge in guard checks, directory resolution, or failure mode (bbe1c41).
evil-ghostelnow included inmake checkdoc(0a9faa1).
- Top line no longer renders clipped after a terminal redraw when
pixel-scroll-precision-modehad left a partial pixel offset. Closes #105 (bfb6e7c). - Scroll position preserved across window resizes (M-x, vertico
open/close, window splits). A pre-redraw classifier tags windows as
auto-follow vs. user-scrolled via multi-line content keys that
survive scrollback eviction, full-redraw erase, and viewport
rewrite — so scrolling up to read history and pressing
M-xno longer yanks the view back to the prompt. Also eliminates a 1-row per-keystroke flicker seen in Claude Code's TUI. Closes #115 (2efecf2). - Backspace now works in terminal mode (
emacs -nw). The event arrives as integer 127 and is now normalised to"backspace"at the Emacs-event boundary before key-name dispatch. Fixes #114 (c5b38d5). - Typing or pasting while point is in scrollback (after mouse wheel, M-v, pixel-scroll) now jumps the viewport to the terminal prompt as intended. Fixes #113 (31bdc9c).
- Wheel events on an unselected ghostel window no longer hang Emacs.
The scroll intercept was running in the selected window's buffer
instead of the event window's, so the re-dispatched event hit the
intercept again — infinite loop, recoverable only via
C-g. Fixes #119 (305eacd). ghostel-compileno longer leaves ~24 blank rows between the output and the footer on short commands. Trailing blank grid rows from the VT render are trimmed on finalise. Fixes #111 (60ab84f).- OSC iterator no longer cannibalises the next OSC's bytes when a
preceding OSC is missing its terminator — a new
\e]introducer now ends the current payload (1729f24).
0.14.0 — 2026-04-13
C-c C-lbinding in copy mode (156a714).
- Decouple module downloads from package version (36a1ad5).
- Disable XON/XOFF flow control so
C-qandC-sreach the shell (a8a3034). - Speed up test suite with early-return polling and parallel execution (2d3bda7).
- Wheel events now fall through to third-party scroll packages
(ultra-scroll,
pixel-scroll-precision-mode,mwheel) when terminal mouse tracking is inactive. Fixes #97 (3b6c980).
- Dead scroll commands (156a714).
- Blank first page of scrollback after initial output burst (f01de74).
- Keystrokes are now visible from the first character in bash sessions
(previously invisible on old bash, notably macOS
/bin/bash3.2). Fixes #101 (51705bd).
0.13.0 — 2026-04-13
- VT log callback (a3d043a).
- Build with Zig and vendored Emacs header (4ca5770).
- Use env vars for Emacs header override (cdcfa76).
- Replace ghostty git submodule with Zig URL dependency (b32308c).
- Use
_get_multifor render state queries (a3d043a). - Remove
zig build checkstep and clean up stale references (f19409e).
- musl cross-compilation; release builds now pass
-Dcpu=baseline(3e0776d).
0.12.2 — 2026-04-12
- Rename
ghostel-eviltoevil-ghostel(1c37fef).
- Blank screen after idle when buffer gets out of sync (0f60388).
0.12.1 — 2026-04-12
- Cursor lands on the correct character for box-drawing and other glyphs where Emacs' width calculation disagrees with the terminal (seen on CJK/pgtk). Fixes #86 (fcb8d3b).
0.12.0 — 2026-04-12
ghostel-enable-title-trackingdefcustom (0102ad9).
- Defer buffer erasure on resize to eliminate blank flash (5966043).
- Redraw synchronously on resize and anchor
window-start(6728ffc).
- Stale horizontal scroll after window resize (cc48ae3).
0.11.0 — 2026-04-11
- Materialize libghostty scrollback into the Emacs buffer (vterm parity) (34645e2).
- Detect cap rotation via first-row hash to keep scrollback fresh (9ed2a76).
- Always insert trailing newline in
insertScrollbackRange(d3acea1). - Trim trailing blank cells when rendering rows (b3e86b5).
- Update benchmark numbers after trailing-whitespace trim (1a31d37).
- OSC 51;E eval no longer crashes the process filter when the executed command switches buffers, signals an error, or deselects the ghostel window. Fixes #82 (20cce42).
0.10.1 — 2026-04-11
- Configurable TRAMP method for OSC 7 directory tracking (1159a5b).
- Pass
-Dcpu=baselinefor native x86_64 builds (11df11b). - Harden Claude review workflow for fork PRs (f16d7b8).
0.10.0 — 2026-04-11
evil-modeintegration: normal-mode navigation works in terminal buffers with the cursor kept in sync on state transitions. Closes #52 (21d8439).- OSC 4/10/11 color query responses (c57f281, fixes #75).
- SIGWINCH delivery tests for PTY resize (500d978).
- Use per-process property for window resize handler (dc102eb).
- Track
.elcfiles as proper Make targets (144c9ba). - Use
executable-findto locate bash in SIGWINCH tests (ae06f8e).
- Remote zsh temp directory leak during session startup (519f063).
- ncurses apps (htop, etc.) now redraw at the correct size after a window resize instead of being frozen at their start-up dimensions. Fixes #67 (83d90f7).
- SIGWINCH baseline tests on Linux by using bash explicitly (e5582d5).
wrong-number-of-argumentsinghostel-evil--around-delete(79a6b86).
0.9.0 — 2026-04-09
- TRAMP integration for remote shell spawning and directory tracking (512a4db).
- Scroll wheel inside TUI apps with mouse tracking (htop, less, etc.) is now forwarded to the application; it still scrolls the viewport when mouse tracking is off. Fixes #60 (a46c784).
ghostel-send-next-keynow works with prefix keys (C-x,C-h) and Meta-modified keys (M-x). Fixes #62 (f9e7fc0).claude-code-reviewworkflow write permissions (63f5550).- Wide-char pixel overflow compensation for emoji (4c191c3).
- Cursor visibility preserved in copy mode during redraws (5d7be51).
0.8.0 — 2026-04-08
ghostel-projectfunction (560776f).ghostel-scroll-on-inputto jump to bottom on typing (cabb939).ghostel--cursor-positionto query terminal cursor location (e3852d8).ghostel-copy-mode-recenter(C-l) for copy mode (6075b64).- Full scrollback copy mode and copy-all command (d07f509).
ghostel-copy-mode-auto-load-scrollbackoption (fc7fc94).
- Bump minimum Emacs version for CI test to 28.2 (cd3031d).
- Preserve manual ghostel buffer renames (c6eb801).
- Rework how ghostel buffers are created (cd7c043).
- Ignore byte-compiled elisp files (cfb0112).
- Move
ghostel--suppress-interfering-modescall insideghostel-mode(59b6928). - Display lint errors when checking locally (628ecae).
- Terminal buffers now respect
display-buffer-alistrules (e.g.(derived-mode . ghostel-mode)). Fixes #56 (85b3e5f). - Preserve column position when scrolling in copy mode (0e7f904).
0.7.1 — 2026-04-06
- Prebuilt binaries for x86_64-macos and aarch64-linux (in addition to the existing x86_64-linux and aarch64-macos). Closes #43 (d04afa6).
- MELPA installation instructions and source build notes (c1d0daf).
- Address MELPA review feedback (416cf7a).
- Build on musl-based distros (Alpine Linux) (204164f).
- Scrollback defaults treated as bytes and not lines (21abb3d).
- Module download URL when installed from MELPA (cb74461).
- Mouse scroll when
pixel-scroll-precision-modeis enabled (067af25). ghostel-test-package-versionfailure with stale.elc(beb72d5).
0.7.0 — 2026-04-05
- Use
grid_refAPI for hyperlink detection instead of HTML formatter (23aa22a). - Optimize release binaries: strip symbols and enable dead-code elimination (f6f3ba3).
0.6.0 — 2026-04-05
- Change ghostty submodule from ssh to https (607beae).
C-tand other control keys not being sent to the terminal (d4ac858).
0.5 — 2026-04-05
- Bind
s-v(Cmd-V) toghostel-yankon macOS (4e43d38).
- Improve typing responsiveness with immediate redraw and input coalescing (a30b53a).
- Clicking ghostel buffer not switching window focus (d030cbb).
struct_timespecopaque type error on some Linux systems (5e1660b).- Dim/faint text (SGR 2) is now rendered by dimming the foreground
colour (previously used
:weight light, which most monospace fonts ignore). Closes #27 (a644834). - Backspace not working in fish shell (36433fd).
0.4 — 2026-04-04
- Claude Code GitHub Actions workflows (23b7caa).
- Prompt to install native module when
ghostelcommand is called (57c6352).
- Set default terminal fg/bg from Emacs theme colors (e66a57d).
0.3 — 2026-04-04
- Module version check to detect stale native modules (be5c399).
- Shrink terminal when tall glyphs push content off-screen (d913076).
- Compensate for wide-char pixel overflow by hiding trailing spaces (e87d820).
- Skip wide-character spacer cells to fix emoji line overflow (40b23e1).
- Skip wide-char compensation when no wide characters are present (691a752).
- Revert overflow detection — keep only viewport pinning (d335346).
- Fix melpazoid warning (ff6dc1b).
ghostel--pin-window-start(caused emoji clipping) (f029fbf).
- Drag-and-drop by extracting drop data from correct event position (794b5c8).
0.2 — 2026-04-02
- Automatic theme color synchronization (eb545fa).
- ERT test for
ghostel-sync-theme(9668724). - Performance benchmark suite comparing ghostel, vterm, and eat (a184e34).
- Emacs built-in
termadded to benchmark suite; README performance section (2c86fb5). - Ghostel vs. vterm comparison section in README (3c71314).
- OSC 51 elisp eval from shell (b3094b7).
- Table of contents in README (ece4a52).
ghostel-full-redrawoption and forcewindow-startpin (1f299df).- Multi-version byte-compile job; warnings treated as errors (97258ef).
- Makefile (835d878).
- Migrate test suite from custom framework to ERT (bb986a2).
- Build with
ReleaseFastfor production performance (7393e64). - Replace manual lint CI with melpazoid (ede8f76).
- Remove
Package-Requiresfrom secondary file (c47662c). - Fix melpazoid lint warnings (9e0f076).
- Filter libghostty info log spam from benchmark output (ae0e9b9).
- Overhaul README: installation, features, configuration (1649771).
- Exit copy mode on normal key press (2ee9d3b).
- Show cursor in copy-mode even when terminal app hid it (c78b290).
- Suppress
hl-line-modein terminal buffer to prevent prompt flicker (a9a07f1). - Byte-compile warnings treated as errors in CI (56ef155).
- Bottom lines cut off when TUI apps fill the screen (f276f2d).
extractStringsilently dropping data >= 64KB (1f88bed).- Missing
errdeferformouse_encoderinTerminal.init(04ca152). - Heap fallback for HTML formatter buffer in
scanHyperlinks(08e2649). ghostel-dirfalling back todefault-directoryinstart-process(6d45a0d).- Missing double-quote escaping in zsh
ghostel_cmd(d398fec). - Copy-mode
M->landing at bottom-right and exit not scrolling back (4a9eb59). ghostel-clearandghostel-clear-scrollback(6ac0ba2).
0.1 — 2026-03-31
Initial tagged release.
- Initial skeleton: Emacs terminal module powered by libghostty-vt (d0e0ee3).
- Styled rendering with colors, bold, italic, underline, etc. (150e9e2).
- Key encoding via
GhosttyKeyEncoder(a8ad51b). - Scrollback, cursor style, and resize improvements (f23290e).
- Mouse input, paste, copy mode, directory tracking (de8d2c7).
- Test suite (61 tests) (6be06f7).
- Incremental redraw using
DIRTY_PARTIAL(c8024ed). - Focus event support gated by DEC mode 1004 (3855df9).
- ANSI 16-color palette customization (b42321f).
- Use face inheritance for ANSI color palette (e75f897).
INSIDE_EMACS=ghostelin shell environment (a496249).ghostel-kill-buffer-on-exitoption (6d31a99).- Shell integration scripts for bash, zsh, and fish (eddc7d8).
- Auto-inject shell integration without requiring .bashrc changes (593af8e).
- Clear scrollback and clear screen commands (f8c9a80).
ghostel-send-next-keyescape hatch (aa9207b).ghostel-yankandghostel-yank-popfor kill-ring cycling (6eaa60b).ghostel-exit-functionshook (0b5de44).- OSC 52 clipboard support (a7eb78a).
- OSC 8 hyperlink support with click-to-open (58f92e3).
- OSC 133 semantic prompt markers for prompt navigation (052c3d7).
- URL auto-detection and
file://link support (b0d143c). - Detect file:line references and open them in Emacs on click (454b794).
- Separate defcustom for file:line detection (457977b).
- Bracketed paste conditional on terminal mode 2004 (e209445).
- Cache frequently-used Emacs symbols as global refs (7103a56).
- Synchronized output support and debounced resize (0a2eb85).
- Keyboard scrolling in copy mode (83b7f44).
M-</M->to jump to top/bottom of scrollback in copy mode (ea4353f).C-ein copy mode to stop at last non-whitespace character (c2328dc).- Preserve column position during
C-n/C-pin copy mode (7a04588). - Strip trailing whitespace from copied text in copy mode (7d0de0f).
- Filter soft-wrapped newlines in copy mode (f384746).
ghostel-module-compilecommand (d75d9d1).- Cross-platform build support and module auto-download (a734e8e).
- Rework module installation with interactive choice and defcustom (f95fd8a).
- Full native build in CI and add release workflow (20bf1f6).
- GitHub Actions CI with linting and tests (3a190e3).
- Improve code quality, adaptive redraw, and CI coverage (ebe6f8b).
- GPL3 license and expanded commentary section (1d676df).
- README with build instructions, features, and configuration (c43bf6a).