ezpn 0.3.0

Dead simple terminal pane splitting — ezpn 2 3 gives you a 2x3 grid of shells
ezpn-0.3.0 is not a library.

ezpn

Split your terminal in one command. Click, drag, done.

License Crate Platform

English | 한국어 | 日本語 | 中文 | Español | Français

Install

cargo install ezpn

Or build from source:

git clone https://github.com/subinium/ezpn
cd ezpn
cargo install --path .

Usage

ezpn                # 2 panes side by side (or load .ezpn.toml)
ezpn 4              # 4 horizontal panes
ezpn 3 -d v         # 3 vertical panes
ezpn 2 3            # 2x3 grid

# Layout presets
ezpn -l dev          # 70/30 split
ezpn -l ide          # editor + sidebar + 2 bottom panes
ezpn -l monitor      # 3 equal columns
ezpn -l quad         # 2x2 grid
ezpn -l stack        # 3 stacked rows
ezpn -l main         # wide top pair + full bottom
ezpn -l trio         # full top + 2 bottom

# Custom ratios
ezpn -l '7:3/1:1'
ezpn -l '1:1:1' -e 'cargo watch -x test' -e 'npm run dev' -e 'tail -f app.log'

# Project config
ezpn init            # generate .ezpn.toml template
ezpn from Procfile   # import from Procfile
ezpn                 # auto-loads .ezpn.toml or Procfile

# Restore session
ezpn --restore .ezpn-session.json

Commands passed with -e/--exec run via $SHELL -l -c, so pipes, redirects, and shell syntax work as expected.

Project Config (.ezpn.toml)

Place .ezpn.toml in your project root. Run ezpn init to generate a template.

[workspace]
layout = "ide"    # or "7:3", "1:1:1", "dev", "monitor", etc.

[[pane]]
name = "server"
command = "npm run dev"
cwd = "./frontend"
restart = "on_failure"    # never | on_failure | always

[pane.env]
NODE_ENV = "development"
PORT = "3000"

[[pane]]
name = "tests"
command = "cargo watch -x test"
restart = "always"

[[pane]]
name = "logs"
command = "tail -f /var/log/app.log"
shell = "/bin/bash"

Supported fields per pane:

  • command — shell command to run (default: interactive shell)
  • cwd — working directory (relative to .ezpn.toml)
  • name — custom title bar label
  • env — environment variables table
  • restartnever (default), on_failure, or always
  • shell — per-pane shell override

Also auto-detects Procfile when no .ezpn.toml exists.

Controls

Mouse — the primary way to interact:

Click pane Focus
Double-click pane Zoom toggle
Click [x] Close pane
Drag border Resize
Scroll wheel Scroll through output (scrollback)

Keyboard (direct shortcuts):

Ctrl+D Split left | right
Ctrl+E Split top / bottom
F2 Equalize all sizes
Ctrl+N Next pane
Ctrl+G Settings (j/k/Enter to navigate)
Ctrl+W Quit

tmux-compatible prefix keys (Ctrl+B then...):

% Split left | right
" Split top / bottom
o Next pane
Arrow Navigate directionally
x Close pane
z Zoom / unzoom pane (full screen)
B Broadcast mode (type in all panes)
R Resize mode (arrow/hjkl to resize, q to exit)
q Show pane numbers, press 1-9 to jump
{ } Swap pane with prev / next
E Equalize
[ Scroll mode (j/k/g/G/PgUp/PgDn, q to exit)
s Toggle status bar
? Help overlay
d Quit (with confirmation if panes are live)

Alt+Arrow navigates between panes directionally. This requires your terminal to send Option as Meta:

  • iTerm2: Preferences → Profiles → Keys → Left Option Key → Esc+
  • Terminal.app: Settings → Profiles → Keyboard → Use Option as Meta Key

Features

Layout presets — Named presets for common workflows:

ezpn -l dev       # 7:3 split — main + side
ezpn -l ide       # 7:3/1:1 — editor + sidebar + 2 bottom
ezpn -l monitor   # 1:1:1 — 3 equal columns
ezpn -l quad      # 2x2 grid

Presets also work in .ezpn.toml: layout = "ide".

Flexible layouts — Start with a grid, use ratio layouts with --layout, split individual panes, and drag to resize. Auto-equalizes on split. Press F2 to reset sizes.

╭────────┬────╮       ╭────────┬────╮
│        │ 2  │       │        │ 2  │
│   1    ├────┤  ──>  │   1    ├──┬─┤
│        │ 3  │       │        │3 │4│
╰────────┴────╯       ╰────────┴──┴─╯

Per-pane commands — Launch each pane with a different command:

ezpn --layout '1/1:1' -e 'htop' -e 'npm run dev' -e 'tail -f app.log'

Broadcast modeCtrl+B B sends your keystrokes to all panes simultaneously. Status bar shows BROADCAST. Press Ctrl+B B again to stop.

Mouse wheel scrollback — Scroll through terminal output with the mouse wheel. Shows [SCROLL] indicator in the title bar. New output snaps back to live view.

Auto-restart — Panes with restart = "on_failure" or restart = "always" in .ezpn.toml automatically respawn when they exit. Includes backoff to prevent restart loops.

Title bar — Each pane shows its number, custom name, and running command. [━] [┃] [×] buttons for split/close.

Zoom modeCtrl+B z or double-click expands any pane to full terminal. Press again to restore.

Keyboard resizeCtrl+B R enters resize mode. Use arrow keys or h/j/k/l to grow/shrink.

Pane swapCtrl+B { and Ctrl+B } swap the active pane with the previous/next.

Quick jumpCtrl+B q overlays pane numbers. Press 1-9 to jump.

Scroll modeCtrl+B [ enters scroll mode. Navigate with j/k, g/G, PgUp/PgDn, Ctrl+U/Ctrl+D.

Settings panelCtrl+G opens a dark modal. Navigate with j/k, apply with Enter, quick-select borders with 1-4.

Dead pane recovery — When a process exits, the pane dims and shows "Process exited". Press Enter to respawn, or × to close.

Config file — Reads ~/.config/ezpn/config.toml for defaults (border style, status bar, shell, scrollback). CLI flags override.

Border styles--border flag or change in settings:

single           rounded (default) heavy            double
┌──────┬──────┐  ╭──────┬──────╮  ┏━━━━━━┳━━━━━━┓  ╔══════╦══════╗
│      │      │  │      │      │  ┃      ┃      ┃  ║      ║      ║
└──────┴──────┘  ╰──────┴──────╯  ┗━━━━━━┻━━━━━━┛  ╚══════╩══════╝

Procfile importezpn from Procfile generates .ezpn.toml from a Procfile. Also auto-detects Procfile when no config exists.

IPC + automation — Control a live instance from another terminal:

ezpn-ctl list
ezpn-ctl split horizontal
ezpn-ctl exec 1 'cargo test'
ezpn-ctl save .ezpn-session.json
ezpn-ctl load .ezpn-session.json

Workspace snapshots — Save layout ratios, active pane, commands, shell path, and UI settings. Restore them later with ezpn --restore.

Nesting prevention — Running ezpn inside an ezpn pane is blocked via $EZPN.

Options

Flag Values Default
-l layout spec or preset (ide, dev, 7:3/1:1)
-e shell command (repeatable) interactive $SHELL
-r snapshot file path
-d h, v h
-b single, rounded, heavy, double rounded
-s shell path $SHELL
-V show version

Layout Presets

Name Layout Panes Use Case
dev 7:3 2 Main editor + side terminal
ide 7:3/1:1 4 Editor + sidebar + 2 bottom
monitor 1:1:1 3 Dashboards, logs, monitoring
quad 2x2 4 Equal grid
stack 1/1/1 3 Stacked rows
main 6:4/1 3 Wide top pair + full bottom
trio 1/1:1 3 Full top + 2 bottom

ezpn-ctl

ezpn-ctl talks to a running ezpn instance over a Unix socket using JSON messages.

ezpn-ctl list
ezpn-ctl --pid 12345 focus 2
ezpn-ctl --json list

Commands:

  • split horizontal [pane]
  • split vertical [pane]
  • close <pane>
  • focus <pane>
  • equalize
  • layout <spec>
  • exec <pane> <command>
  • save <path>
  • load <path>

How it works

Each pane owns a PTY pair (portable-pty) running either an interactive shell or a shell command. Output is parsed by a per-pane VT100 emulator (vt100) with configurable scrollback. The layout is a binary split tree where each node is either a leaf (pane) or a split with a direction and ratio. Rendering caches border geometry and redraws only dirty panes unless the layout chrome changes.

src/
├── main.rs          Event loop, prefix key state machine, pane lifecycle
├── layout.rs        Binary split tree + named presets
├── pane.rs          PTY + VT100 emulation + scrollback + mouse forwarding
├── render.rs        Dirty render path + border cache + title bar buttons
├── settings.rs      Dark modal with vim navigation
├── project.rs       .ezpn.toml parsing + Procfile import
├── ipc.rs           JSON IPC protocol + Unix socket listener
├── workspace.rs     Snapshot save/load and validation
├── config.rs        Config file loading (~/.config/ezpn/config.toml)
├── tab.rs           Tab manager (prepared)
└── bin/ezpn-ctl.rs  External control client

vs. tmux / Zellij

tmux Zellij ezpn
Config .tmux.conf KDL files .ezpn.toml / CLI flags
Split Ctrl+B % Mode switch Ctrl+D / click
Resize :resize-pane Resize mode Drag / Ctrl+B R
Select Ctrl+B arrow Click Click
Project setup tmuxinator .ezpn.toml / Procfile
Broadcast :setw synchronize-panes Ctrl+B B
Auto-restart restart = "always"
Scrollback Ctrl+B [ Scroll mode Mouse wheel / Ctrl+B [
Detach Yes Yes No

Use ezpn when you want split terminals with zero config. Use tmux/Zellij when you need detach/reattach session persistence.

License

MIT