1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
|
# sl0ppij — an opinionated Zellij fork
> This is **not** upstream Zellij. It is a fork maintained at
> <https://git.sl0p.foo/sl0ppij.git>, tracking [zellij-org/zellij][upstream] with a
> small set of opinionated additions, mostly about how panes *look*.
>
> Upstream's README follows below, unchanged.
[upstream]: https://github.com/zellij-org/zellij
## What this fork adds
Everything lives in the existing `ui { pane_frames { ... } }` block, and every option
defaults to upstream behaviour — an unmodified config renders exactly as stock Zellij.
```kdl
ui {
pane_frames {
// Where the pane title sits in the frame. Upstream hardcodes it flush
// left for full frames. (Frameless mode already centred it.)
title_alignment "center" // "left" | "center" | "right" (default: left)
// Outer gap: real empty terminal between panes, WM style.
// Expressed in ROWS. A pane insets right/bottom always and left/top
// only at the screen edge, so two neighbours show exactly `gap`
// between them -- not double -- and the screen edges match.
gap 1 // default: 0
// Inner padding between a pane's frame and its content.
// CSS-style shorthand, but the 4-value order is UP DOWN LEFT RIGHT,
// deliberately NOT CSS's clockwise top-right-bottom-left:
// padding 1 all four sides
// padding 1 2 up/down = 1, left/right = 2
// padding 1 2 3 4 up=1 down=2 left=3 right=4
padding 1 // default: 0
// Show no pane title until something actually names the pane: either
// you rename it, or the running program sets a title. Upstream always
// shows a "Pane #N" placeholder. Tracked as a flag rather than by
// matching the string, so a program that genuinely titles itself
// "Pane #3" is not mistaken for an unnamed pane.
hide_default_pane_names true
// Desaturate the CONTENT of unfocused panes, 0-100 (default 0 = off).
// Colours are blended toward their own perceptual luminance, so an
// unfocused pane keeps its brightness but drains of colour. This is
// real colour transformation, not the SGR "faint" attribute.
//
// Cost: focus is per client, so clients can no longer share rendered
// output and each renders separately. Fine for a single user, worth
// knowing about in a multi-user session. Sixel/kitty IMAGES are not
// affected and stay in colour.
inactive_grayscale 85
// Columns per row. Terminal cells are ~2x taller than they are wide, so
// 1 row of gap needs ~2 columns to look like the same distance.
// Applies to `gap`, and to the 1-value form of `padding`. The 2- and
// 4-value padding forms are taken literally. Set to 1 to disable.
gap_aspect 2 // default: 2
// Collapse to a named swap layout when the terminal gets narrow, and
// restore the previous one when it grows back. 0 disables (default).
// See "Responsive layouts" below -- this one needs swap layouts in your
// layout file to do anything.
responsive_min_cols 90 // default: 0 (off)
responsive_layout "stacked" // default: "stacked"
}
}
```
### Responsive layouts
When the terminal is narrower than `responsive_min_cols`, the tab switches to the
swap layout named by `responsive_layout`; when it grows back, the layout that was
active before the collapse is restored. Splitting a wide window into thirds is great
until the window is 70 columns wide and each pane gets 23.
If no swap layout by that name exists, one is **synthesized**: every pane
flattened into a single stack, in the order they appear in the layout. That is
the normal case, because a custom layout file only picks up swap layouts from a
hand-written sibling `<layout>.swap.kdl` — so the collapse works out of the box
for a bare layout, and growing the terminal back restores the original
arrangement. It is logged once per tab so a name that does not match its swap
file is not mistaken for the fallback.
Expanding falls back too. A tab restores into whatever layout was active before
it collapsed, which for a bare layout is the implicit `BASE` — and `BASE` is
pinned to the pane count it was written with, so adding or closing a single pane
leaves nothing to expand into. When that happens the panes are spread into a
grid rather than left stacked.
Define the layout yourself when you want something other than a flat stack:
```kdl
layout {
pane split_direction="vertical" {
pane
pane
pane
}
swap_tiled_layout name="tiled" {
tab {
pane split_direction="vertical" {
pane
pane
pane
}
}
}
swap_tiled_layout name="stacked" {
tab min_panes=2 {
pane stacked=true {
children
}
}
}
}
```
(One node per line: zellij's KDL parser rejects the `{ pane; pane; }` one-liner form.)
Name-based rather than a new size-aware `LayoutConstraint` on purpose: that type
crosses both the plugin protobuf API and the client/server contract, and the
committed prost assets cannot be regenerated without `protoc`.
Two deliberate behaviours:
- **Hysteresis.** Collapse happens below `responsive_min_cols`, but restore only
above `responsive_min_cols + 10`. Without that dead band, dragging a window edge
that lands exactly on the threshold flips the layout on every motion event, and
every flip resizes every PTY in the tab.
- **Both directions are resolved from the panes as they are**, never replayed
from a remembered layout. Collapsing picks your named layout, or a synthesized
stack if that one is missing *or cannot currently apply*. Expanding picks the
tab's implicit `BASE` layout when it still applies — which reproduces your
original arrangement exactly — and otherwise a synthesized grid. Both chains
end in something with no constraints, so neither direction can fail to find a
target and leave the tab stuck.
- **The only state kept is one bit**: which side of the threshold you are on, and
it is flipped only after a layout has actually been applied. Every bug this
feature had came from remembering a layout *position* instead: positions stop
applying when the pane count changes, and a failed apply used to leave the tab
claiming a transition that never happened.
- **A manual arrangement does not suppress it.** Zellij's "damaged layout" flag
is also set whenever a relayout finds nothing applicable, so honouring it
disabled the feature in several situations that had nothing to do with intent.
It protected little in any case: expanding applies `BASE` or a grid, so a
manual arrangement is replaced by a collapse/expand round trip either way.
Note this affects **frame rendering only**. Panes still report their `Pane #N`
placeholder to the plugin API, so anything that lists panes — the session
manager, a pane finder — can still label and search them. Blanking it there too
left such plugins with rows they could neither identify nor fuzzy-match.
### Capping the stacked-pane picker
A stack reserves one row per member plus a padding row, so a deep stack spends
most of the screen listing itself. `stack_list_max_rows` caps it:
```kdl
ui {
pane_frames {
stack_list_max_rows 3 // default: 0 (show every member)
stack_list_layout "grid" // "rows" (default) | "grid"
}
}
```
In `grid` mode entries are packed several per row, so the cap becomes a **space
budget** rather than a count: eight panes in a wide terminal fit on one row, and
`+N more` never appears. Cell width comes from the widest label, so the column
count adapts to the titles rather than being configured; long titles simply
yield fewer columns, and one column is the floor.
`stack_list_style` controls how entries look:
| style | entry | active entry |
|---|---|---|
| `boxed` (default) | bracketed in vertical bars | prefixed with `> ` |
| `minimal` | no brackets, one space of padding | inverted, using the theme's `ribbon_selected` |
`minimal` reads better at a glance and costs 4 fewer columns per entry, so more
entries fit per row in `grid` mode. It uses `ribbon_selected` rather than
`text_selected` because themes give the former real contrast — it is what marks
a selected item elsewhere in zellij — while the latter's background is often a
hair off the terminal's own and would read as no highlight at all.
The list windows around the focused entry and reports the remainder as `+N more`
in the padding row it already reserves, so the indicator never costs a row or
steals one from a real entry. **Click that row to expand** the stack to its full
length; it becomes `- fewer`, which collapses it again. Entries keep their **real** position in the stack
rather than renumbering from 1, so the numbering still means something once the
window has scrolled. Ordinary focus movement slides the window, which is why
there is no separate binding for it.
Expansion is a click rather than hover on purpose: the row count feeds pane
geometry, so growing the list resizes the visible pane's PTY. Once per
deliberate click is fine — the same cost as any pane resize — whereas on hover
it would reflow a real terminal, and any full-screen program in it, on every
mouse move.
Nothing is unreachable while collapsed: the window slides as focus moves, so
the picker is a scrolling viewport rather than a truncation.
### Pane arrangements
A `children` placeholder normally inserts every pane as a **flat sibling**, which
is why a layout can only express "N equal columns", "N equal rows" or "one
stack" without hand-writing a variant per pane count (see the `max_panes=5` /
`max_panes=8` / `max_panes=12` ladders in a typical swap layout file). An
`arrangement` builds a tree for N panes instead:
```kdl
swap_tiled_layout name="grid" {
tab min_panes=3 {
pane arrangement="grid" {
children
}
}
}
swap_tiled_layout name="master-stack" {
tab min_panes=2 {
pane arrangement="master-stack" {
children
}
}
}
```
| arrangement | result |
|---|---|
| `grid` | roughly square: `ceil(sqrt(n))` columns, rows filled left to right (4 panes → 2x2, 5 → 3 then 2) |
| `master-stack` | one master pane taking 60% of the width, the rest stacked beside it |
Requires a `children` node (an arrangement has nothing to act on without one, so
omitting it is a config error rather than a silent no-op), and cannot be combined
with `stacked=true`.
It generates a `TiledPaneLayout` rather than geometry directly, so everything
downstream comes along for free: the minimum-size feasibility check, swap-layout
constraints, damage tracking, and the responsive collapse above. That last one
composes especially well — a grid is simply infeasible on a narrow terminal, so
it is skipped and the collapse takes over.
The arrangement has no protobuf representation (like `default_fg`/`default_bg`),
so plugins cannot set it and it does not survive a round trip through the plugin
API.
### Drag a pane title to rearrange it
Dragging a tiled pane by its title bar and dropping it on another pane swaps the
two, so panes can be rearranged with the mouse instead of only through MOVE mode.
The title bar EXCLUDING its corners is the move handle; the corners remain resize
handles, so two-axis resize from the top still works. Plain top-edge resize is gone,
which costs nothing in practice: every boundary is shared, so the boundary above a
pane is the bottom edge of the pane above it and can be dragged from there.
The reordering is previewed LIVE: the swap is actually applied as you drag, so the
real layout shows what you would get, and releasing simply keeps it. Dragging away
undoes the preview and restores the original arrangement. The pane being dragged
stays highlighted so it remains identifiable as the layout moves around it.
A drag that never leaves its own pane previews nothing, so a plain click still just
focuses and double-click-to-rename still works. Stacked panes are excluded — in a stack the
title row is how you focus a member.
### Double-click a pane title to rename it
Double-clicking the **top edge** of a pane's frame focuses that pane and drops
straight into rename mode — the same state the rename keybind produces.
Frame corners are excluded (they are resize handles), and a single click on the top
edge still starts the usual resize drag. Currently tiled panes only: on floating panes
the title is the drag handle for moving the pane, so that path is left alone for now.
Also in the fork, not configurable:
- Renaming a tab REPLACES the existing name on the first keystroke, the way a text
field with its contents selected behaves, instead of appending to it. Backspace
and delete count as editing rather than replacing, so they still work on the old
name.
- A compact scroll indicator (`↑42`) in place of upstream's `SCROLL: 42/1000`, shown
only when actually scrolled up rather than whenever scrollback exists.
- `sl0p.foo` branding in the default tab-bar plugin.
## Which changes apply live
`config.kdl` is watched, but only some things reach a running session:
| change | applies |
|---|---|
| `title_alignment`, `gap`, `padding`, `gap_aspect` | **live**, no restart |
| `responsive_min_cols`, `responsive_layout` | **live**, applies on the next resize |
| `status_bar_button_style` | **live**, on the next render |
| `rounded_corners`, and other stock options | **live** (upstream behaviour) |
| anything requiring a new binary | **new session** — a running server keeps its old inode |
## Building
Upstream's `[profile.release]` uses fat LTO and `codegen-units = 1`, which costs ~3
minutes for a one-line change. For iteration use the `fastdev` profile added here —
same `opt-level = 3`, no LTO, parallel codegen, incremental cache kept:
```bash
cargo build --profile fastdev -p zellij # ~3s incremental -> target/fastdev/zellij
cargo build --release -p zellij # ~3min -> ship this one
```
See `.fastfeedback/SPEED.md` for narrow test commands and measured timings.
The test suite is **green** (`cargo test -p zellij-server -p zellij-utils`), so any
failure is a real one. It was not always: the fork carried 35 failing snapshot tests
from its first commit until 5f2c28ab, because `aa51848d` changed the frame rendering
without refreshing the `insta` snapshots. If you change anything about how frames or
pane titles render, expect to regenerate snapshots — and read the diffs before
accepting them.
### Per-pane status bar (OSC 5577) — experimental
A program can put one row of its own text in its pane's bottom frame row:
```bash
printf '\033]5577;1;status;building 3/7 · 12s elapsed\033\\' # text
printf '\033]5577;1;buttons;approve:Approve;reject:Reject\033\\' # buttons
printf '\033]5577;1;clear\033\\' # remove both
```
```
╰ awaiting review ──────────────────────────[Approve]─[Reject]╯
```
### Workspace hints
A pane can carry an opaque `purpose` an agent can discover, so a project's layout
also says what each pane is *for*:
```kdl
pane name="server" purpose="dev-server" command="npm" { args "run" "dev"; }
```
Tabs can carry one too (`tab name="work" purpose="agent-work"`, reported by
`list-tabs --json`), and an agent can tag panes it spawns with
`zellij run --purpose agent:build -- …`.
It shows up in `zellij action list-panes --json`, survives resurrection, and can
also be declared in-band by the program in the pane
(`printf '\033]5577;1;purpose;agent:build\033\\'`) — though a layout-declared
purpose always wins, since in-band data is whatever the pane happens to be
showing. Zellij never interprets a purpose; the consuming agent owns the
vocabulary. See `sl0p/workspace-hints-handover.md`.
Buttons render as `[Approve]` by default, or `1:Approve` with
`ui { pane_frames { status_bar_button_style "indexed" } }` — useful when something
else binds those numbers to the same actions.
Full protocol reference, worked shell/Python consumers and the security model:
**[`sl0p/pane-status-bar.md`](sl0p/pane-status-bar.md)**.
Clicking a button writes a **fixed-format report** to the program's stdin:
```
OSC 5577 ; 1 ; click ; <id> ST
```
Button ids are restricted to `[A-Za-z0-9_-]` and labels are `%`-escaped on the
wire (`%3B` for `;`). Positions come from one `layout_status_bar` function that
both the renderer and the mouse hit-test read, so a button can never be drawn
somewhere a click does not resolve. Buttons are right-aligned and get first call
on the row: they are actionable, the text is informational and truncates.
Rendered **in the existing frame row**, not in reserved rows. Reserving a row
would shrink the pane's content area and so resize its PTY every time the bar
appeared or disappeared — firing SIGWINCH at the very program that asked for it.
The frame row is already a text surface and costs nothing.
It outranks the frame's other hints (resize help, mouse shortcuts), since those
are discoverable elsewhere and this was explicitly requested by the program. It
lives on the pane's `Grid` like the window title, so it survives re-renders,
resizes and detach/reattach — and it is serialised with the session, so a pane
resurrected from disk comes back showing what it showed before:
```kdl
pane focus=true contents_file="initial_contents_2" status_bar="building 3/7"
```
The text is sanitised again when that layout is parsed back, because a layout file
on disk is no more trustworthy than the escape sequence that produced it and may
have been hand-edited since. One sanitiser serves both paths.
The payload is versioned (`;1;`) and unknown versions or verbs are ignored, so
adding verbs later cannot be misinterpreted by an older build. The text is the
remaining params rejoined with `;`, so a single text field needs no escaping —
the same trick OSC 0/2 uses for titles. A structured payload will need a real
escaping rule.
**The text is untrusted.** It arrives in-band, so `cat` of a hostile file emits it
just as well as a cooperating program. Control characters are stripped (a raw ESC
would let it repaint anything; a newline would corrupt the row) and the length is
capped. Keep that in mind for the planned action buttons: a click must send a
fixed-format report, never bytes the payload chose, or viewing a file could inject
into a shell's stdin.
### `mouse_hover_help_text`
```kdl
mouse_hover_help_text false // default: true
```
Hovering a pane shows `Ctrl <MouseScroll> or <drag borders> to resize`. Upstream
governs that with `mouse_hover_effects`, which **also** controls the hover frame
highlight — so silencing the hint costs you the highlight too. This separates
them: the hint is a beginner reminder you outgrow, the highlight stays useful.
Config-file only. It has no protobuf slot, so it does not cross the client/server
IPC contract — the server reads it from the config file itself, and `merge()`
keeps the file value when the client sends `None`. A `--mouse-hover-help-text`
CLI flag would therefore be silently ignored.
### `close_if_focused` on `LaunchOrFocusPlugin`
Makes one chord both open and dismiss a plugin:
```kdl
bind "Ctrl ;" {
LaunchOrFocusPlugin "file:~/.config/zellij/plugins/pane-finder.wasm" {
floating true
close_if_focused true
};
}
```
Upstream's action focuses an already-running plugin, so the launch chord can only
ever open it. A plugin cannot fix this itself: keybindings are resolved into
actions in `route.rs` before a key can reach a pane, so the chord that launched a
plugin never arrives at it — not even via `intercept_key_presses`, which only
sees keys already destined for a pane.
Closes only when the plugin is the **focused pane in the client's active tab**.
Hidden behind a floating-layer toggle, or focused in another tab, it is revealed
or brought over instead — so the chord can never close whatever pane you happen
to be on.
Defaults to `false`, so existing bindings behave exactly as before. It is a
config-only option with no protobuf representation, and is dropped when an action
crosses the plugin API.
## Bundled plugin: `sl0p/zj-pane-finder`
A floating fuzzy finder for every pane in the session, filtering on pane
metadata **and** on what is visible inside each pane. See
`sl0p/zj-pane-finder/README.md`.
It lives in this repo because it path-depends on this checkout's `zellij-tile`,
so the plugin API and its protobuf assets always match the binary built beside
it — the two cannot drift. It is its own workspace root rather than a member,
so carrying it touches no upstream-tracked file. `./install.sh` builds,
installs and hot-reloads it.
## Known rough edges
- **`gap` and `padding` only apply with `pane_frame_style "full"`.** With `"titles"` or
`"none"` they are ignored: the cells around a pane's content there belong to the
inter-pane boundary renderer (`ui/boundaries.rs`), and insetting into that region
would leave cells nobody repaints (stale glyphs on resize) or paint over the
boundaries themselves. Rather than half-apply, a frameless pane is simply not inset.
Your config keeps its values; they just take effect if you switch back to full
frames.
- **Floating panes** get `padding` but no `gap`: they are positioned explicitly, so an
inter-pane gap does not apply.
- **Stacked panes** apply the gap uniformly; stack header spacing may look slightly off
at larger gaps.
- **Responsive layouts need a swap layout with a matching name.** `responsive_layout`
selects by name; if no swap layout has that name the tab silently stays as it is,
with nothing logged. It is also column-only — there is no `responsive_min_rows`.
- The fork's options are **not** exposed over the plugin protobuf API. Plugins have no
use for them and adding proto fields would mean regenerating the committed prost
assets in `zellij-utils/assets/prost/`.
## Version string
Fork builds report a `+sl0pN` build-metadata suffix, so `zellij --version` is
distinguishable from upstream:
```
$ zellij --version
zellij 0.45.0+sl0p1
```
Build metadata rather than a `-sl0pN` prerelease on purpose: cargo ignores build
metadata when matching version requirements, so the intra-workspace
`zellij-utils = "0.45.0"` dependencies still resolve. A prerelease suffix does not,
and would require editing every member manifest.
Bump the suffix per published fork build; reset to 1 when rebasing onto a new
upstream release. Note that a version bump changes `ZELLIJ_PLUGIN_ARTIFACT_DIR`, so
the bundled wasm plugins are recompiled once on first run.
## Staying current with upstream
```
origin ssh://git@git.sl0p.foo:39327/srv/git/sl0ppij.git (this fork; main = fork work)
upstream https://github.com/zellij-org/zellij.git (vanilla)
```
The local `main` branch is kept clean on `upstream/main` so fork commits can be rebased
onto new Zellij releases.
---
<h1 align="center">
<br>
<img src="https://raw.githubusercontent.com/zellij-org/zellij/main/assets/logo.png" alt="logo" width="200">
<br>
Zellij
<br>
<br>
</h1>
<p align="center">
<img src="https://raw.githubusercontent.com/zellij-org/zellij/main/assets/demo.gif" alt="demo">
</p>
<h4 align="center">
[<a href="https://zellij.dev/documentation/installation">Installation</a>]
[<a href="https://zellij.dev/screencasts/">Screencasts & Tutorials</a>]
[<a href="https://zellij.dev/documentation/configuration">Configuration</a>]
[<a href="https://zellij.dev/documentation/layouts">Layouts</a>]
[<a href="https://zellij.dev/documentation/faq">FAQ</a>]
</h4>
<p align="center">
<a href="https://discord.gg/CrUAFH3"><img alt="Discord Chat" src="https://img.shields.io/discord/771367133715628073?color=5865F2&label=discord&style=flat-square"></a>
<a href="https://matrix.to/#/#zellij_general:matrix.org"><img alt="Matrix Chat" src="https://img.shields.io/matrix/zellij_general:matrix.org?color=1d7e64&label=matrix%20chat&style=flat-square&logo=matrix"></a>
<a href="https://zellij.dev/documentation/"><img alt="Zellij documentation" src="https://img.shields.io/badge/zellij-documentation-fc0060?style=flat-square"></a>
</p>
<br>
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/bc5daac4-140a-4b83-8729-71c944ee1100">
<img src="https://github.com/user-attachments/assets/55156624-a71a-46b5-939e-f562e3b2dd7f" alt="Sponsored by ">
</picture>
<a href="https://www.gresearch.com/">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/d609936a-abf8-4406-8cfc-889f76a09d74">
<img src="https://github.com/user-attachments/assets/742ae902-fe9d-41c6-baf2-4bc143061da3" alt="gresearch logo">
</picture>
</a>
</p>
# What is this?
[Zellij](#origin-of-the-name) is a workspace aimed at developers, ops-oriented people and anyone who loves the terminal. Similar programs are sometimes called "Terminal Multiplexers".
Zellij is designed around the philosophy that one must not sacrifice simplicity for power, taking pride in its great experience out of the box as well as the advanced features it places at its users' fingertips.
Zellij is geared toward beginner and power users alike - allowing deep customizability, personal automation through [layouts](https://zellij.dev/documentation/layouts.html), true multiplayer collaboration, unique UX features such as floating and stacked panes, and a [plugin system](https://zellij.dev/documentation/plugins.html) allowing one to create plugins in any language that compiles to WebAssembly.
Zellij includes a built-in [web-client](https://zellij.dev/tutorials/web-client/), making a terminal optional.
You can get started by [installing](https://zellij.dev/documentation/installation.html) Zellij and checking out the [Screencasts & Tutorials](https://zellij.dev/screencasts/).
For more details about our future plans, read about upcoming features in our [roadmap](#roadmap).
## How do I install it?
The easiest way to install Zellij is through a [package for your OS](./docs/THIRD_PARTY_INSTALL.md).
If one is not available for your OS, you could download a prebuilt binary from the [latest release](https://github.com/zellij-org/zellij/releases/latest) and place it in your `$PATH`. If you'd like, we could [automatically choose one for you](#try-zellij-without-installing).
You can also install (compile) with `cargo`:
```
cargo install --locked zellij
```
#### Try Zellij without installing
bash/zsh:
```bash
bash <(curl -L https://zellij.dev/launch)
```
fish/xonsh:
```bash
bash -c 'bash <(curl -L https://zellij.dev/launch)'
```
#### Installing from `main`
Installing Zellij from the `main` branch is not recommended. This branch represents pre-release code, is constantly being worked on and may contain broken or unusable features. In addition, using it may corrupt the cache for future versions, forcing users to clear it before they can use the officially released version.
That being said - no-one will stop you from using it (and bug reports involving new features are greatly appreciated), but please consider using the latest release instead as detailed at the top of this section.
## How do I start a development environment?
* Clone the project
* In the project folder, for debug builds run: `cargo xtask run`
* To run all tests: `cargo xtask test`
For more build commands, see [CONTRIBUTING.md](CONTRIBUTING.md).
## Configuration
For configuring Zellij, please see the [Configuration Documentation](https://zellij.dev/documentation/configuration.html).
## About issues in this repository
Issues in this repository, whether open or closed, do not necessarily indicate a problem or a bug in the software. They only indicate that the reporter wanted to communicate their experiences or thoughts to the maintainers. The Zellij maintainers do their best to go over and reply to all issue reports, but unfortunately cannot promise these will always be dealt with or even read. Your understanding is appreciated.
## Roadmap
Presented here is the project roadmap, divided into three main sections.
These are issues that are either being actively worked on or are planned for the near future.
***If you'll click on the image, you'll be led to an SVG version of it on the website where you can directly click on every issue***
[](https://zellij.dev/roadmap)
## Origin of the Name
[From Wikipedia, the free encyclopedia](https://en.wikipedia.org/wiki/Zellij)
Zellij (Arabic: الزليج, romanized: zillīj; also spelled zillij or zellige) is a style of mosaic tilework made from individually hand-chiseled tile pieces. The pieces were typically of different colours and fitted together to form various patterns on the basis of tessellations, most notably elaborate Islamic geometric motifs such as radiating star patterns composed of various polygons. This form of Islamic art is one of the main characteristics of architecture in the western Islamic world. It is found in the architecture of Morocco, the architecture of Algeria, early Islamic sites in Tunisia, and in the historic monuments of al-Andalus (in the Iberian Peninsula).
## License
MIT
## Sponsored by
<a href="https://terminaltrove.com/"><img src="https://avatars.githubusercontent.com/u/121595180?s=200&v=4" width="80px"></a>
|