aboutsummaryrefslogtreecommitdiffstats

zj-pane-finder

A floating fuzzy finder for every pane in the session. Lists panes across all tabs, filters as you type, previews the highlighted one, and jumps to it.

It lists every pane in the session, including ones you cannot see: members of a stack hidden behind the visible one, and panes in other tabs. Selecting a hidden stack member swaps it into view; selecting a pane in another tab switches to that tab first. Both come free from the host's FocusPaneWithId, which resolves the owning tab through suppressed panes and hands hidden stack members to the fork's stack-list logic.

Suppressed plugin panes are the exception — zellij:link and friends are deliberately invisible infrastructure, and focusing one shows an empty pane.

Filtering matches pane metadata (tab, title, command, cwd) and, failing that, the visible contents of each pane — so you can find a pane by something you can see in it rather than only by what it is called. Content matches are marked ≡ in the list, and the preview scrolls to the matching line with the matched characters highlighted.

Why it lives here

It path-depends on this checkout's zellij-tile, so the plugin API and its protobuf assets always match the binary built beside it. Keeping the two in one repo means they cannot drift.

It is deliberately its own workspace root rather than a member of zellij's: a non-root member has its [profile] ignored, and opt-level = "z" + lto + strip are what keep the wasm small — which matters because the keybinding loads it with skip_plugin_cache, recompiling on every launch. Staying independent also means carrying this crate touches no upstream-tracked file.

Sizing

On a roomy terminal it opens as a centred float with a frame. Below COMPACT_BELOW_COLS x COMPACT_BELOW_ROWS (100x30) it takes the whole viewport and drops its frame instead.

The float's margins cost 16% of the columns and 28% of the rows, plus a border, plus this fork's pane gap — on an 80x24 terminal that leaves roughly 63x14 to work with rather than 80x24. Rows are the scarce resource in a list, so most of the results were going to decoration.

It re-evaluates on TabUpdate, so resizing the terminal switches modes live. Only applied when the decision actually flips, so moving or resizing the pane by hand is left alone.

Build and install

./install.sh

Builds for wasm32-wasip1, copies to ~/.config/zellij/plugins/, and hot-reloads it in every running session — zellij caches a plugin's compiled module in the server's memory for the life of the session, so overwriting the .wasm alone is not enough.

Note it does not build into ./target: this checkout's .cargo/config.toml sets a shared build.target-dir, so install.sh asks cargo metadata where the artifact actually landed.

Binding it

Something used this often wants a single chord rather than a mode dance. In a config with default_mode "locked", put it in a block shared by the modes you actually sit in:

shared_among "normal" "locked" {
    bind "Ctrl ;" {
        LaunchOrFocusPlugin "file:~/.config/zellij/plugins/pane-finder.wasm" {
            floating true
            move_to_focused_tab true
            skip_plugin_cache true
        };
        SwitchToMode "Locked"
    }
}

Ctrl ; is deliberately obscure: ; has no control-code meaning, readline does not bind it, and vim binds only bare ; (repeat f/t), never Ctrl ;. Plain ; keeps typing normally.

It does need the kitty keyboard protocol to be distinguishable from a plain ; — zellij enables it and kitty speaks it, but on a terminal without it the binding simply never fires. Ctrl \, Ctrl ] and Ctrl _ are real control codes and work anywhere, but they collide with SIGQUIT, vim's tag jump and readline's undo respectively.

Keys

type filter · Up/Down, Ctrl-p/Ctrl-n, Tab move · Enter jump Ctrl-w clear word · Ctrl-u clear · Esc/Ctrl-c close