Skip to content

Releases

v0.8.3

Choose a tag to compare

@csheaff csheaff released this 27 Oct 06:20
0b47b71

tmux-control 0.8.3

Speed

  • A terminal that keeps receiving output is redrawn at most 30 times a
    second, and all such terminals are redrawn together. Emacs pays for each
    redraw of a frame, so drawing every chunk of a fast stream kept it busy:
    four terminals each printing 200 lines a second held Emacs at 75–100% of
    a core on a Mac, and now take 35–43%. The first output after a quiet
    spell is drawn at once, and so is everything for half a second after you
    type into the terminal, so typing feels the same. Set
    tmux-control-redisplay-interval to change the pace, or to nil to draw
    every chunk as it comes.
  • A pane whose program stopped at its last column no longer repaints from
    tmux three times over after a resize or a visit. Over SSH each repaint
    cost a round trip.

Window buffers

  • tmux-control-window-buffers takes all: every window of the session
    gets its buffer as soon as tmux-control learns of it, and the buffer keeps
    up with its window whether or not it is shown.
  • tmux-control-window-buffer-names set to tab names window buffers as
    their tabs read, such as "*tmux dev:work 1:bash*", and renames them when
    tmux renames or moves a window. The default, id, keeps
    "*tmux dev:work @5*".
  • A window's buffer takes the size of the pane that becomes active. Closing
    a split's other pane left the buffer at the split's height, blank above
    its last few rows.

Sizes and tiled views

  • A window made shorter keeps following its terminal's output. Output drawn
    before Emacs next drew the window could leave it behind, falling further
    back with each line until something repainted it.
  • A new connection asks tmux for its size once its window has settled. A
    package that changes the window right after connecting, as Roost does when
    it docks its task panel, had tmux rewrap the pane at two widths, and the
    pane showed a long line from its second row.
  • Tiled panes keep their screens in view through a resize. They showed a
    row or more of history above the screen until the next repaint.
  • A tiled view beside a full-height window, with a band such as a menu
    across the frame's bottom, is sized by its columns. tmux was asked for
    one row.
  • Panes stacked in a tiled view fit their rows when the mode line is taller
    than a line of text, as modus themes and doom-modeline make it. They came
    out a few pixels short and scrolled their screen part way out of view.
  • The rightmost pane of a tiled view shows its last column rather than a
    $.

Dropped connections

  • When a connection drops, its screen stays in view with the note on the
    line under it. The window used to scroll down to the note, past blank
    rows. Notes no longer move point.
  • A click on a dead terminal says in the echo area that C-c C-r
    reconnects. It asked whether to reconnect for each press and each
    release. Typing still asks.

For packages

  • tmux-control-window-buffer-functions is called with each new window
    buffer once it knows its connection and window. Making a buffer doesn't
    display it, so this is where a package can, for example, add it to a
    perspective.

Tests and docs

  • The VT oracle (make test-vt-oracle) measures tab stops and grapheme
    cells as tmux does, no longer echoes query replies onto the screen it
    compares, and has 35 new cases, among them grapheme clusters and tmux's
    own quirks.
  • The README and guide show the current look, and the README says to
    require 0.8.0 for tmux-control-switch-session-functions.

v0.8.2

Choose a tag to compare

@csheaff csheaff released this 18 Oct 21:04
4315eac

tmux-control 0.8.2

  • Clicks keep working after a terminal misses a mouse release. A press in a
    program that takes the mouse made Eat send every later click in Emacs to
    the terminal if the release never reached it: while tmux wasn't replying,
    when the window showed another buffer by the release, or when a question
    read it. Now the press is let go of once its release is handled, and a
    press outside any terminal goes to its own binding.
  • A split view's pane starts at its screen's top when a window
    configuration brings it back, as perspective.el's persp-switch does. It
    showed a line or more of scrollback until the pane's next output.
  • Two docstrings start with a whole sentence, as checkdoc asks.

v0.8.1

Choose a tag to compare

@csheaff csheaff released this 13 Oct 15:14
dc4ac35

tmux-control 0.8.1

  • After C-c C-r reconnects a view in place, tmux is sized to the window
    with its header line, so the view's top row stays in sight. tmux kept one
    row more than the window showed until the window next changed.
  • A connect's quiet period for background activity is no longer cut short by
    the resize it makes, so a slow first repaint doesn't flag windows.

v0.8.0

Choose a tag to compare

@csheaff csheaff released this 13 Oct 14:48
997daf6

tmux-control 0.8.0

Names

  • The mode line names the host and tmux window, such as "dev › build", in
    place of the buffer name, and the terminal's mode reads "tmux". Set
    tmux-control-mode-line-window-name to nil to keep the buffer name.
  • Buffers are named "*tmux HOST:SESSION*", "local" for the local server,
    with the socket in parentheses when it isn't the default, and a window's,
    pane's or scrollback's suffix after that, such as "*tmux dev:work @2*".
    Update anything in your configuration that matches the old names.

Keys and links

  • Shift+Return reaches the program as tmux's own S-Enter on tmux 3.5 and
    later, so it starts a new line in Claude Code's prompt rather than sending
    it. So does Option+Return, as M-Enter, in a GUI frame's semi-char mode.
    Older servers get the bytes as before.
  • A lone ESC stays whole: the M-RET override binding is gone.
  • C-c C-u opens a URL shown on the screen. Links that programs mark with
    OSC 8 open with a click or C-c C-u in the live view, and with a click or
    RET in scrollback.
  • C-c C-o moves to the next pane's window in a tiled view.

Split windows

  • A split window keeps its tabs: the header line sits over the top-left pane,
    and tmux lays the panes out one row shorter to make room. Each pane keeps
    the mode line other windows have, naming its host, window and pane.
  • When a split closes, the view goes back to one pane, with its tabs.
  • A new tiled view keeps you on the pane you were looking at, tmux's active
    one, so C-c | focuses the new pane, as tmux does.
  • Switching sessions replaces all of a split session's panes, and returning
    to it splits it again.

What you see

  • Panes render as tmux does where Eat's emulation departs from it, checked
    against tmux by a differential test (make test-vt-oracle).
    tmux-control-correct-emulation turns the corrections off.
  • A normal screen repaints from tmux after any change in size, so it stays on
    tmux's screen as a pane gets shorter, taller, narrower or wider, and long
    lines wrap where tmux wraps them. A full-screen program repaints itself.
  • A repainted screen starts on a line of its own, which fixes a garbled first
    row after returning to a split session.
  • No-break spaces show as spaces.

Speed

  • Keystrokes are answered sooner, panes that keep scrolling stay fast, and a
    settled window is left alone when output arrives.
  • Idle collection (tmux-control-idle-gc-mode) checks from idle timers
    rather than 20 times a second, and terminal buffers keep no undo.
  • A window showing a window buffer resizes tmux when it changes size.

Diagnostics

  • tmux-control-pause-log-mode notes each time Emacs stops responding for
    tmux-control-pause-log-threshold seconds, with the command, tmux output
    or timer that took most of it, or else that redisplay or another program's
    output most likely did, and the time spent collecting garbage.
    tmux-control-diagnostics lists the latest notes.

For packages

  • tmux-control-switch-session-functions lets a package show a session its
    own way when you switch to it from a view, as Roost does to open the task.
    tmux ending a connection is said once.

v0.7.2

Choose a tag to compare

@csheaff csheaff released this 04 Oct 22:18
6d594d1

tmux-control 0.7.2

  • unload-feature now leaves Emacs as it was: tmux-control-unload-function
    removes the advice on Eat and on the pager command, the global resize hooks,
    the Evil initial state, and tmux-control-idle-gc-mode's hooks and timer.
    Close tmux-control buffers before unloading.
  • The two window-size-change-functions hooks that follow resizes of
    scrollback pagers and tiled views are added by the first tmux-control buffer
    rather than when the package loads.

v0.7.1

Choose a tag to compare

@csheaff csheaff released this 04 Oct 19:20
62fac72

tmux-control 0.7.1

tmux-control-kill-pane moves from C-c x to C-c C-x. Emacs reserves C-c
followed by a letter for users' own bindings, and MELPA's checks reject
packages that bind those keys. To keep the old key, add it to your
configuration:

(with-eval-after-load 'tmux-control
  (keymap-set tmux-control-mode-map "C-c x" #'tmux-control-kill-pane))

v0.7.0

Choose a tag to compare

@csheaff csheaff released this 04 Oct 18:51
0824b6a

tmux-control 0.7.0

This release adds a public API for packages built on tmux-control, so they no
longer need its internal functions and variables. It also collects the
terminal, recovery and modal-editing fixes made since 0.6.0.

Public API

  • tmux-control-connect-or-switch, tmux-control-send-command and
    tmux-control-query connect to a session and talk to tmux over its
    connection.
  • tmux-control-tiled-p, tmux-control-buffer-host,
    tmux-control-buffer-socket-name, tmux-control-buffer-session,
    tmux-control-active-pane and tmux-control-window-id describe the current
    buffer.
  • tmux-control-send-command and tmux-control-query accept exactly one tmux
    command, starting with the bare command name. They refuse blank and
    comment-only lines, variable assignments, NUL bytes, more than one line, and
    an unquoted ;, { or }, any of which would detach the client or
    misalign later replies.
  • tmux-control-window-id also works in tiled pane buffers and in session
    buffers without tmux-control-window-buffers.
  • The internal names these functions wrap still work.

See "For package authors" in the README for the full list.

Connections and terminal fidelity

  • Add named connection bookmarks using native Emacs bookmarks and a copyable
    tmux-control-diagnostics report with asynchronous live server details.
  • Separate connection identity by host, socket, and session; controller renames
    no longer break reuse or render-buffer cleanup.
  • Retain visited tiled pane history through window switches, resizing,
    repaints, and untile/retile; cached panes keep streaming while hidden.
  • Restore tiling on reconnect while preserving neighboring code windows/focus.
  • Detect incomplete command replies, refuse additional input on an overdue
    connection, and report unacknowledged input without replaying it on recovery.
  • Refresh pane cursor and terminal modes during in-band repaint/flow-control
    recovery; ignore stale seeds from older connections or superseded requests.
  • Preserve canonically composable combining accents that Eat 0.9.4 omits.
    Complex graphemes without a single-cell composition remain an Eat limitation.

Scrollback and the live buffer

  • Scrolling back shows a clickable ↑ 120 lines indicator; clicking it
    returns that window to live output.
  • tmux-control-clear-scrollback (C-c M-o) clears both the Emacs copy of a
    pane's history and tmux's, for example after a full-screen program repaints
    at a new size and leaves its old frame in scrollback.
  • The live terminal buffer is read-only, so an editing command can no longer
    change the text Eat uses as its model of the pane. Typing still reaches the
    pane, and input methods keep working.
  • tmux-control-adopt-window-size clears stale sizing warnings and takes
    sizing back from another attached client.
  • Every header line redraws when the window list changes.

Modal editing and GUI frames

  • Full-screen programs no longer lose rows or columns in GUI frames:
    alternate-screen scrolling, tmux's separator column in horizontal tiles, and
    stale horizontal scrolling are handled.
  • In raw mode, keys go to the pane before modal-editing maps; entering raw mode
    clears xah-fly-keys' command map, and Option-Return returns to semi-char
    mode.
  • Evil users: buffers start in tmux-control-evil-state, insert by
    default, so keys reach the pane instead of acting as Evil commands.
  • The README shows how to send ESC to the pane under a modal package.

Installation

  • The README shows use-package :vc (Emacs 30) and package-vc-install
    (Emacs 29) alongside straight.el.
  • .elpaignore keeps the test helpers out of package installs, which had
    printed load errors during package-vc installation.

v0.6.0

Choose a tag to compare

@csheaff csheaff released this 07 Sep 22:06
fa20cee

tmux-control 0.6.0

This release improves scrollback loading and live-history retention, reduces
temporary output-decoding allocations, and adds optional idle-time garbage
collection for tmux-control views.

Scrollback

  • History loads in smaller, 500-row extensions and begins loading three
    screens before the loaded top. Both settings are configurable.
  • Overlap matching normalizes only the bounded seam between history chunks.
  • Live buffers retain up to 1,048,576 characters by default, reducing the
    chance that incoming output discards text being read. Customize
    tmux-control-live-scrollback-size, or set it to nil to inherit Eat's
    setting. The limit applies to new or reconnected buffers; more retained
    text uses more memory per pane.
  • Output decoding copies literal runs together and reuses octal replacement
    strings to reduce temporary allocation.

Optional idle collection

Enable M-x tmux-control-idle-gc-mode to request garbage collection during
quiet periods in live views and scrollback pagers. Run it again to disable.
The mode is off by default and leaves Emacs's automatic GC settings intact.
Use M-x tmux-control-idle-gc-status to see its requested collections.

The defaults require one second without a completed command, ten million
new cons allocations since the last collection, a selected tmux-control
view, and no pending input or active minibuffer.

In a controlled five-minute comparison, maximum software response fell
from 242 ms to 12 ms, while total GC time increased from 230 ms to 452 ms.
Input deliberately resumed during collection still saw 62–71 ms responses.
These exploratory measurements do not establish display FPS or physical
trackpad latency. Try the mode with your normal workload before enabling
it permanently. Lower decoder allocation alone did not demonstrate fewer
long scrolling pauses.

Other fixes since 0.5.0

  • Handle normalized control-mode delimiters.
  • Preserve carriage returns when decoding UTF-8 output streams.
  • Anchor tiled pane windows to Eat's live display start.

Developer tools

New allocation benchmarks, controlled GUI scroll traces, and aggregate
analysis tools make input delays and reading-position drift measurable.
See the measurement guide for reproduction steps and
limitations, and the user guide for configuration.

v0.5.0

Choose a tag to compare

@csheaff csheaff released this 25 Jul 19:30
daad16c

A month and 26 commits since v0.4.0, with one new opt-in feature and three render-correctness fixes.

New

Pane-directory tracking (#119). find-file and dired in a tmux-control buffer already default to the pane's own directory, on the pane's host. The opt-in tmux-control-pane-directory-mode goes further and makes default-directory itself follow the pane, so compile, grep, consult-ripgrep and project commands run there too:

(add-hook 'tmux-control-mode-hook #'tmux-control-pane-directory-mode)

Tracking is session-wide — toggling it in any of a session's buffers applies to all of them, each following its own pane (#121).

Render correctness

Three distinct bugs, all surfaced by running the chaos soak against a remote session over SSH rather than a local socket:

  • The repaint painted the wrong buffer (#124). tmux-control--seed-screen drives its seed with tagged commands, and tagged replies are dispatched in the controller — so a repaint requested from a per-window buffer (the default view) painted that window's pane into the connect buffer, and left it there. Reached both by tmux-control-clear-and-repaint — the command the README prescribes for a drifted view — and by the opt-in auto-heal.
  • Drift was never repaired on arrival (#127). A window's buffer keeps streaming while you look at another window, where Eat can accumulate a row of divergence from the pane's true screen. Nothing repainted a buffer that was merely switched to, so the drift survived every switch. Arriving at a window now verifies its screen against tmux and repaints only on a real difference.
  • A seed could paint a stale capture (#126). Output arriving after tmux ran capture-pane, but before the paint, was rendered and then erased by the paint's screen clear. Cursor-only verification cannot catch it, because the cursor still agrees.
  • Tiled panes: the view anchor counted from the buffer's end, so a single appended [tmux-control] note scrolled a pane permanently past the top of its own screen (#122).
  • Tiling entry points fixed for per-window render buffers (#115); seeds now replay pane terminal modes (alternate screen, mouse) into Eat; the alternate-screen off warning is throttled across reconnects.

Diagnostics

  • A failed tmux command reports tmux's own words — tmux: can't find window: @9 — instead of the internal reply kind (#123). Command arguments are deliberately not echoed, since keystrokes ride send-keys.
  • tmux-control-scrollback-mode is guarded against being invoked in a live buffer (#120).

Verification

233 ERT tests green, clean byte-compile, and a parallel A/B soak against a remote tmux 3.6b over SSH — six seeds × 60 randomized operations, v0.4.0-era main and this release running simultaneously against separate sessions on the same host: 9 render failures → 2, with no seed worse.

v0.4.0

Choose a tag to compare

@csheaff csheaff released this 28 Jun 00:59
b9ec441

v0.4.0

Everything since v0.2.0. The headline is a redesigned header line, a full
security-hardening pass, and connection resilience (auto-reconnect, SSH
keepalives, flock self-heal).

Security hardening

  • Command-injection defused in session/window names and the SSH host — all
    attacker-influenced values are now quoted/validated before reaching the shell
    or control-mode targets (#97, #75, #76, #90).
  • Mode-line / header-line injection closed: % in pane-derived titles and
    fields is escaped so a pane can't forge mode-line constructs (#98).
  • Reply-stream validation: pane ids from tmux are validated and the input
    accumulator is capped, so a hostile/runaway stream can't blow up Emacs (#99).
  • Clipboard writes (OSC 52) ignored by default — a pane can no longer set
    your system clipboard unless you opt in via tmux-control-allow-clipboard-write
    (marked :risky) (#100).
  • Supply-chain/CI: added SECURITY.md, Dependabot, and OpenSSF Scorecard (#95).

Redesigned header line ("here" / "elsewhere")

  • The header now splits "here" (left: a persistent host:session › windows
    label, so you always know which server/session you're looking at) from
    "elsewhere" (right corner: other connected sessions with unseen output,
    named and grouped by server — click to jump) (#88, #86, #87).
  • Cross-session activity is disambiguated by host, so two hosts' default
    sessions no longer collide into an indistinguishable marker (#85).
  • Per-tab hover highlight; local connections are named local:session (#87).

Connection resilience

  • Opt-in in-place auto-reconnect (tmux-control-auto-reconnect, default off)
    re-attaches a dropped session without losing your buffers (#104).
  • Hardened SSH: keepalives and a connect timeout on every invocation, so a
    dead network surfaces fast instead of hanging (#101).
  • Flock self-heal: the tiled grid re-lays-out when a session joins or dies (#105).
  • Warn once when the controlled tmux has alternate-screen disabled (#103).

Rendering & tiling

  • In-band tiling — the tiled view is built over the control connection
    instead of blocking SSH round-trips (#69, #83).
  • Opt-in auto-heal for Eat render drift (tmux-control-auto-heal-drift,
    default off): clears and repaints a desynced pane (#81).
  • Optional per-pane mode-line hide in tiled view for a gapless grid (#107).
  • Skip resize round-trips when the requested size is unchanged (#82).

Navigation & UX

  • Keyboard window jumps and a real scrollback-pager exit (l) (#93).
  • In-place live-history scroll (wheel-up) is now the default (#94).
  • Guards against acting on a session killed out from under the corner/switcher
    (#90, #91), and a quiet-window so the controller doesn't flag its own activity (#92).

Protocol correctness (bug-hunt batch)

  • Seed/focus fixes, scrollback-seam dedup (no double lines on a scrolling pane),
    timer-lifecycle hygiene, and a literal-TAB-in-pane-title fix (#72, #74, #77, #78).

CI & docs

  • Eat caching with a Codeberg-resilient, SHA-verified fallback fetch; fixed
    setup-emacs rate-limiting (#108, #106).
  • Documented why tmux-control renders through Eat, plus a Troubleshooting
    section (#71).