No description
  • Rust 83.8%
  • Python 11.8%
  • Nix 4.4%
Find a file
natsukium e99c7fff27
refactor(graphics): consume the shared felis-grid placeholder resolver
The placeholder cell decode (image id from fg + diacritic, tile run
inheritance) was a second copy of logic that also lived in
felis-render-wgpu, down to a duplicate fg_rgb24. felis-grid now owns it
as Grid::placeholder_cells (felis rev b70fcd7), so drop the local decode
and fold the shared resolver's cells into per-image bounding boxes. One
decoder, used by both the GPU renderer and this ANSI re-encoder.

Bump the felis pin to b70fcd7 for the new API.

Assisted-by: Claude Code Opus 4.8
2026-06-20 23:55:38 +09:00
docs docs: record the tree → config → graphics roadmap 2026-06-20 22:10:07 +09:00
scripts feat(graphics): place Kitty Unicode-placeholder images 2026-06-20 23:39:21 +09:00
src refactor(graphics): consume the shared felis-grid placeholder resolver 2026-06-20 23:55:38 +09:00
.gitignore build: add flake-parts dev environment 2026-06-20 20:56:37 +09:00
Cargo.lock refactor(graphics): consume the shared felis-grid placeholder resolver 2026-06-20 23:55:38 +09:00
Cargo.toml refactor(graphics): consume the shared felis-grid placeholder resolver 2026-06-20 23:55:38 +09:00
flake.lock build: add flake-parts dev environment 2026-06-20 20:56:37 +09:00
flake.nix build: add flake-parts dev environment 2026-06-20 20:56:37 +09:00
README.md feat: re-emit Kitty graphics clipped to panes 2026-06-20 22:28:55 +09:00

felis-tui

A felis-native terminal multiplexer and TUI client.

felis is a GPU terminal emulator with a daemon/client split. Its sessions already survive the window closing — the daemon is the persistence layer — and felis deliberately refuses tabs, splits, and panes, delegating layout to the window manager (felis principle 1).

That delegation assumes a first-class WM. On Windows, on macOS, and over a bare SSH login, you may not have one. felis-tui fills that gap: it attaches to the felis daemon from any host terminal and provides the persistence and the splits that felis itself won't — while losing as little of felis's fidelity as the host terminal allows.

Why not just use tmux?

A multiplexer running inside another terminal can only render what that host terminal supports — that part is unavoidable. The difference is where the fidelity is lost:

  • tmux/zellij/dvtm re-parse the PTY byte stream with their own, necessarily incomplete, VT parser. They lose information at the parse step and again at the render step.
  • felis-tui consumes felis's already-parsed, structured grid (ShadowGrid) over the felis protocol. There is no re-parse. The only fidelity loss is the final re-encode into the host terminal's capabilities — the theoretical minimum.

That is the whole bet, and it is recorded as a hard non-goal in docs/non-goals.md: felis-tui never re-parses a PTY byte stream. The day it wants to, it has become tmux and lost its reason to exist.

Status

Working multiplexer: each pane is its own felis session over its own connection, mirrored and composited into one host terminal. Panes live in a binary split tree (split the focused pane horizontally or vertically; close collapses the split). Keys are encoded through felis's own key_encode (full Kitty keyboard protocol), and colour is down-mapped to the host (true-colour passed through, else nearest xterm-256).

On a Kitty-graphics-capable host, images are re-emitted as Kitty graphics clipped to their pane — the thing tmux/zellij can't do, because felis-tui has the placement geometry as structured data rather than a re-parsed byte stream. On other hosts images are dropped client-side ("the client owns pixels"). Behaviour and config live in docs/roadmap.md.

cargo run            # first pane re-attaches an idle session, or spawns a shell

Prefix is Ctrl-A, then:

key action
c new pane (spawn a shell)
o focus the next pane
x close (detach) the focused pane — its shell keeps running
h split horizontally (side by side)
v split vertically (stacked)
d detach all and quit
Ctrl-A send a literal Ctrl-A to the focused pane

Detaching never kills a shell: the session lives in the daemon, so closing felis-tui (or a pane) parks it for the next attach.

Config

Optional, at ~/.config/felis-tui/config.toml (or $XDG_CONFIG_HOME/felis-tui/config.toml). The [keys] table overlays the defaults, so you only list what you change. Keys bind to a fixed action set — there is no scripting, by design (see docs/non-goals.md).

prefix = "C-a"          # the multiplexer prefix chord

[keys]                  # key -> action (overlays defaults)
h   = "split-horizontal"
v   = "split-vertical"
o   = "focus-next"
x   = "close"
d   = "detach"
"C-a" = "literal-prefix"

[theme]
separator = 8           # ANSI-256 index for pane separators

Actions: split-horizontal, split-vertical, focus-next, close, detach, literal-prefix. Key chords are a single character with an optional C- (Ctrl) or M- (Alt) prefix.

Not yet

  • Pixel-level cropping of images that overflow a pane (today an overflowing or natural-size placement is skipped rather than cropped), scrollback-anchored placements, animation, and placeholders on incapable hosts.
  • Query-based graphics detection (today it is env markers).
  • Interactive per-split resize and layout presets.
  • Scrollback viewport, copy mode, search.