# 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 ```bash ./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: ```kdl 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