ezpn 0.10.0

Dead simple terminal pane splitting — ezpn 2 3 gives you a 2x3 grid of shells
<p align="center">
  <img src="../assets/hero.png" width="720" alt="ezpn 데모">
</p>

<h1 align="center">ezpn</h1>

<p align="center">
  <strong>터미널 패널, 즉시.</strong><br>
  설정 없이 세션 영속성과 tmux 호환 키를 제공하는 터미널 멀티플렉서.
</p>

<p align="center">
  <a href="https://crates.io/crates/ezpn"><img src="https://img.shields.io/crates/v/ezpn?style=flat-square&color=orange" alt="crates.io"></a>
  <a href="../LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT License"></a>
  <a href="https://github.com/subinium/ezpn/actions"><img src="https://img.shields.io/github/actions/workflow/status/subinium/ezpn/ci.yml?style=flat-square&label=CI" alt="CI"></a>
  <a href="https://github.com/subinium/ezpn/actions/workflows/gitleaks.yml"><img src="https://img.shields.io/github/actions/workflow/status/subinium/ezpn/gitleaks.yml?style=flat-square&label=gitleaks" alt="gitleaks"></a>
  <a href="https://github.com/subinium/ezpn/actions/workflows/supply-chain.yml"><img src="https://img.shields.io/github/actions/workflow/status/subinium/ezpn/supply-chain.yml?style=flat-square&label=audit" alt="audit"></a>
  <img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux-lightgrey?style=flat-square" alt="Platform">
</p>

<p align="center">
  <a href="../README.md">English</a> | <b>한국어</b> | <a href="README.ja.md">日本語</a> | <a href="README.zh.md">中文</a> | <a href="README.es.md">Español</a> | <a href="README.fr.md">Français</a>
</p>

---

## 왜 ezpn?

```bash
$ ezpn                # 터미널을 즉시 분할
$ ezpn 2 3            # 2x3 셸 그리드
$ ezpn -l dev         # 프리셋 레이아웃
```

설정 파일도, 셋업도, 러닝 커브도 없습니다. 세션은 백그라운드에서 유지 — `Ctrl+B d`로 분리, `ezpn a`로 복귀.

**프로젝트에서**, `.ezpn.toml`을 레포에 넣고 `ezpn`을 실행하면 모두가 같은 워크스페이스를 사용합니다:

```toml
[session]
name = "myproject"           # 세션 이름 고정 (충돌 시 myproject-1, -2... 로 처리)

[workspace]
layout = "7:3/1:1"
persist_scrollback = true    # 스크롤백이 분리/재연결 후에도 유지됨

[[pane]]
name = "editor"
command = "nvim ."

[[pane]]
name = "server"
command = "npm run dev"
restart = "on_failure"
env = { NODE_ENV = "${env:NODE_ENV}", DB_URL = "${file:.env.local}" }

[[pane]]
name = "tests"
command = "npm test -- --watch"

[[pane]]
name = "logs"
command = "tail -f logs/app.log"
```

```bash
$ ezpn         # .ezpn.toml을 읽고 전부 시작
$ ezpn doctor  # 실행 전에 환경 변수 보간과 시크릿 참조를 검증
```

tmuxinator도 없고. YAML도 없고. 레포에 TOML 파일 하나면 끝.

## 설치

```bash
cargo install ezpn
```

또는 [최신 릴리스](https://github.com/subinium/ezpn/releases/latest)에서 미리 빌드된 바이너리를 받으세요 — `ezpn-x86_64-unknown-linux-gnu.tar.gz`, `ezpn-x86_64-apple-darwin.tar.gz`, `ezpn-aarch64-apple-darwin.tar.gz`.

<details>
<summary>소스에서 빌드</summary>

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

</details>

## 빠른 시작

```bash
ezpn                  # 2패널 (또는 .ezpn.toml 로드)
ezpn 2 3              # 2x3 그리드
ezpn -l dev           # 레이아웃 프리셋 (dev, monitor, quad, stack, trio...)
ezpn -e 'cmd1' -e 'cmd2'   # 패널별 명령어
```

### 세션

```bash
Ctrl+B d               # 분리 (세션은 계속 실행)
ezpn a                 # 가장 최근 세션에 재연결
ezpn a myproject       # 이름으로 재연결
ezpn ls                # 활성 세션 목록
ezpn kill myproject    # 세션 종료
ezpn --new             # $PWD에 기존 세션이 있어도 새 세션을 강제로 생성
```

세션 이름은 기본적으로 `basename($PWD)`를 따릅니다. 충돌은 결정론적으로 해결됩니다 — `repo` → `repo-1` → `repo-2` (스캔 중 죽은 소켓은 정리됨). `.ezpn.toml`의 `[session].name = "..."`로 이름을 고정할 수 있습니다.

### 

```bash
Ctrl+B c               # 새 탭
Ctrl+B n / p           # 다음 / 이전 탭
Ctrl+B 0-9             # 번호로 탭 이동
```

모든 tmux 키가 동작합니다 — `Ctrl+B %`로 분할, `Ctrl+B x`로 닫기, `Ctrl+B [`로 복사 모드.

## 주요 기능

| | |
|---|---|
| **제로 설정** | 바로 사용 가능. rc 파일 불필요. |
| **레이아웃 프리셋** | `dev`, `ide`, `monitor`, `quad`, `stack`, `main`, `trio` |
| **세션 유지** | tmux처럼 분리/연결. 백그라운드 데몬이 프로세스 유지. 콜드 어태치 50ms 미만. |
| **스크롤백 저장** | 옵션 `persist_scrollback`으로 분리/재연결 후에도 스크롤백 유지 (v3 스냅샷에서 gzip+bincode). |
| **** | tmux 스타일 윈도우. 탭 바와 마우스 클릭 전환 지원. |
| **마우스 우선** | 클릭으로 포커스, 드래그로 크기 조절, 스크롤로 히스토리, 드래그로 선택 & 복사. |
| **복사 모드** | Vi 키, 비주얼 선택, 표시 폭 기반 증분 검색, OSC 52 클립보드. |
| **커맨드 팔레트** | `Ctrl+B :` tmux 호환 명령어. |
| **브로드캐스트 모드** | 모든 패널에 동시 입력. |
| **프로젝트 설정** | `.ezpn.toml` — 레이아웃, 명령어, 환경변수, 자동 재시작. |
| **환경 변수 보간** | 패널 env에서 `${HOME}`, `${env:VAR}`, `${file:.env.local}`, `${secret:keychain:KEY}` 사용. |
| **테마** | TOML 팔레트 + 4개 빌트인 (`tokyo-night`, `gruvbox-dark`, `solarized-dark`/`-light`). |
| **핫 리로드** | `Ctrl+B r`로 분리 없이 `~/.config/ezpn/config.toml` 재로드. |
| **보더리스 모드** | `ezpn -b none`으로 화면 공간 극대화. |
| **Kitty 키보드** | `Shift+Enter`, `Ctrl+Arrow`, Alt+Char (CSI u / RFC 3665) — 수정 키 정상 동작. |
| **CJK/유니코드** | 한국어, 중국어, 일본어, 이모지 정확한 폭 계산. |
| **크래시 격리** | 패닉이 발생한 패널 하나가 데몬을 죽이지 못함 (시그널 안전한 SIGTERM/SIGCHLD 처리). |

## 레이아웃 프리셋

```bash
ezpn -l dev       # 7:3 — 메인 + 사이드
ezpn -l ide       # 7:3/1:1 — 에디터 + 사이드바 + 하단 2개
ezpn -l monitor   # 1:1:1 — 3열 균등
ezpn -l quad      # 2x2 그리드
ezpn -l stack     # 1/1/1 — 3행 쌓기
ezpn -l main      # 6:4/1 — 상단 넓은 쌍 + 하단 전체
ezpn -l trio      # 1/1:1 — 상단 전체 + 하단 2개
```

커스텀 비율: `ezpn -l '7:3/5:5'`

## 프로젝트 설정

프로젝트 루트에 `.ezpn.toml`을 넣고 `ezpn`을 실행하세요. 끝.

**패널별 옵션:** `command`, `cwd`, `name`, `env`, `restart` (`never`/`on_failure`/`always`), `shell`

```bash
ezpn init              # .ezpn.toml 템플릿 생성
ezpn from Procfile     # Procfile에서 가져오기
ezpn doctor            # 설정 + 환경 변수 보간 검증, 참조 누락 시 비-0 종료
```

### 환경 변수 보간

패널 env 값은 네 가지 참조 형식을 지원합니다:

```toml
[[pane]]
command = "npm run dev"
env = {
  HOME       = "${HOME}",                    # 프로세스 env
  NODE_ENV   = "${env:NODE_ENV}",            # 명시적 env
  DB_URL     = "${file:.env.local}",         # dotenv 스타일 파일 조회
  GH_TOKEN   = "${secret:keychain:GH_TOKEN}",# macOS Keychain (Linux: secret-tool)
}
```

`.ezpn.toml` 옆의 `.env.local`은 자동으로 머지되어 `[env]`를 덮어씁니다. OS 키체인을 사용할 수 없을 때 `${secret:keychain:KEY}`는 경고와 함께 `${env:KEY}`로 폴백합니다. 순환을 잡기 위해 재귀 깊이는 8로 제한됩니다.

### 테마

```toml
# .ezpn.toml 또는 ~/.config/ezpn/config.toml
theme = "tokyo-night"   # default | tokyo-night | gruvbox-dark | solarized-dark | solarized-light
```

사용자 테마는 `~/.config/ezpn/themes/<name>.toml`에서 로드됩니다. ezpn은 `$COLORTERM` / `$TERM`을 자동 감지하며 트루컬러가 지원되지 않을 때 256색 또는 16색으로 다운그레이드합니다.

<details>
<summary>글로벌 설정 (~/.config/ezpn/config.toml)</summary>

```toml
border = rounded            # single | rounded | heavy | double | none
shell = /bin/zsh
scrollback = 10000
status_bar = true
tab_bar = true
prefix = b                  # 프리픽스 키 (Ctrl+<key>)
theme = default             # default | tokyo-night | gruvbox-dark | solarized-dark | solarized-light
persist_scrollback = false  # 자동 스냅샷에 스크롤백 저장 (기본 비활성)
```

설정 패널 변경(`Ctrl+B Shift+,`)은 원자적으로 저장됩니다. 디스크에서 다시 불러오려면 `Ctrl+B r`.

</details>

## 키 바인딩

**직접 단축키:**

|| 동작 |
|---|---|
| `Ctrl+D` | 좌우 분할 |
| `Ctrl+E` | 상하 분할 |
| `Ctrl+N` | 다음 패널 |
| `F2` | 크기 균등화 |

**프리픽스 모드** (`Ctrl+B` 후):

|| 동작 |
|---|---|
| `%` / `"` | 좌우 / 상하 분할 |
| `o` / Arrow | 패널 이동 |
| `x` | 패널 닫기 |
| `z` | 줌 토글 |
| `R` | 크기 조절 모드 |
| `[` | 복사 모드 |
| `B` | 브로드캐스트 |
| `:` | 커맨드 팔레트 |
| `r` | 설정 재로드 |
| `d` | 디태치 |
| `?` | 도움말 |

<details>
<summary>전체 키 바인딩 참조</summary>

**탭:**

|| 동작 |
|---|---|
| `Ctrl+B c` | 새 탭 |
| `Ctrl+B n` / `p` | 다음 / 이전 탭 |
| `Ctrl+B 0-9` | 번호로 탭 이동 |
| `Ctrl+B ,` | 탭 이름 변경 |
| `Ctrl+B &` | 탭 닫기 |

**패널:**

|| 동작 |
|---|---|
| `Ctrl+B {` / `}` | 이전 / 다음 패널과 교환 |
| `Ctrl+B E` / `Space` | 크기 균등화 |
| `Ctrl+B s` | 상태 바 토글 |
| `Ctrl+B q` | 패널 번호 + 빠른 이동 |

**복사 모드** (`Ctrl+B [`):

|| 동작 |
|---|---|
| `h` `j` `k` `l` | 커서 이동 |
| `w` / `b` | 다음 / 이전 단어 |
| `0` / `$` / `^` | 줄 시작 / 끝 / 첫 문자 |
| `g` / `G` | 스크롤백 맨 위 / 맨 아래 |
| `Ctrl+U` / `Ctrl+D` | 반 페이지 위 / 아래 |
| `v` | 문자 선택 |
| `V` | 줄 선택 |
| `y` / `Enter` | 복사 후 종료 |
| `/` / `?` | 앞으로 / 뒤로 검색 |
| `n` / `N` | 다음 / 이전 일치 |
| `q` / `Esc` | 종료 |

**마우스:**

| 동작 | 효과 |
|---|---|
| 패널 클릭 | 포커스 |
| 더블클릭 | 줌 토글 |
| 탭 클릭 | 탭 전환 |
| `[x]` 클릭 | 패널 닫기 |
| 보더 드래그 | 크기 조절 |
| 텍스트 드래그 | 선택 + 복사 |
| 스크롤 휠 | 스크롤백 히스토리 |

**macOS 참고:** Alt+Arrow 방향 이동은 Option을 Meta로 설정해야 합니다 (iTerm2: Preferences > Profiles > Keys > `Esc+`).

</details>

<details>
<summary>커맨드 팔레트 명령어</summary>

`Ctrl+B :` 명령어 프롬프트. tmux 별칭 모두 지원.

```
split / split-window         좌우 분할
split -v                     상하 분할
new-tab / new-window         새 탭
next-tab / prev-tab          탭 전환
close-pane / kill-pane       패널 닫기
close-tab / kill-window      탭 닫기
rename-tab <name>            탭 이름 변경
layout <spec>                레이아웃 변경
equalize / even              크기 균등화
zoom                         줌 토글
broadcast                    브로드캐스트 토글
```

</details>

## ezpn vs. tmux vs. Zellij

| | tmux | Zellij | **ezpn** |
|---|---|---|---|
| 설정 | `.tmux.conf` 필요 | KDL 설정 | **제로 설정** |
| 첫 사용 | 빈 화면 | 튜토리얼 모드 | **`ezpn`** |
| 세션 | `tmux a` | `zellij a` | **`ezpn a`** |
| 프로젝트 설정 | tmuxinator (gem) || **`.ezpn.toml` 내장** |
| 브로드캐스트 | `:setw synchronize-panes` || **`Ctrl+B B`** |
| 자동 재시작 ||| **`restart = "always"`** |
| Kitty 키보드 | 미지원 | 지원 | **지원** |
| 플러그인 || WASM ||
| 생태계 | 거대 (30년) | 성장중 | 신규 |

**ezpn** — 설정 없이 바로 쓰는 터미널 분할.
**tmux** — 깊은 스크립팅과 플러그인 생태계가 필요할 때.
**Zellij** — 모던 UI와 WASM 플러그인을 원할 때.

## CLI 레퍼런스

```
ezpn [ROWS COLS]         그리드 레이아웃으로 시작
ezpn -l <PRESET>         레이아웃 프리셋으로 시작
ezpn -e <CMD> [-e ...]   패널별 명령어
ezpn -S <NAME>           이름 지정 세션
ezpn -b <STYLE>          보더 스타일 (single/rounded/heavy/double/none)
ezpn --new               새 세션 강제 (기존 세션 자동 연결 건너뛰기)
ezpn a [NAME]            세션 연결
ezpn ls                  세션 목록
ezpn kill [NAME]         세션 종료
ezpn rename OLD NEW      세션 이름 변경
ezpn init                .ezpn.toml 템플릿 생성
ezpn from <FILE>         Procfile에서 가져오기
ezpn doctor              .ezpn.toml + 환경 변수 보간 검증
```

## 라이선스

[MIT](../LICENSE)