- Shell 98.6%
- Nix 0.9%
- Just 0.5%
Problem The tmux-mosaic nightly release workflow publishes tags/releases, but its status context appears under Nightly Release instead of the deploy namespace. It also only runs on schedule/manual dispatch, while vimdoc-language-server publishes a nightly release for each main-branch push. Solution Move the nightly release status under the deploy namespace and trigger it on main pushes: - workflow name becomes deploy - job name becomes Nightly, yielding deploy / Nightly - push to main now triggers the nightly release workflow - schedule and workflow_dispatch remain as catch-up/manual controls The existing release script behavior is unchanged; it still waits for quality and remains idempotent when the current nightly is already published. Verification - Parsed .forgejo/workflows/release_nightly.yaml with PyYAML. - Ran git diff --check. Reviewed-on: #155 |
||
|---|---|---|
| .forgejo | ||
| scripts | ||
| tests | ||
| .editorconfig | ||
| .gitignore | ||
| AGENTS.md | ||
| biome.json | ||
| flake.lock | ||
| flake.nix | ||
| justfile | ||
| LICENSE | ||
| mosaic.tmux | ||
| README.md | ||
tmux-mosaic
Pane tiling layouts for tmux
A tmux plugin that applies tiling layouts, tracks which panes belong to them, and lets native tmux pane operations either join or stay outside the layout.
Dependencies
Installation
TPM
TPM
- Add mosaic to your tmux config:
set -g @plugin 'barrettruth/tmux-mosaic'
- Install the plugin with TPM
Manual
Manual
- Clone the repo somewhere tmux can reach it:
git clone https://git.barrettruth.com/barrettruth/tmux-mosaic.git \
${XDG_DATA_HOME:-$HOME/.local/share}/tmux/plugins/tmux-mosaic
- Source the plugin from your tmux config:
run-shell ${XDG_DATA_HOME:-$HOME/.local/share}/tmux/plugins/tmux-mosaic/mosaic.tmux
Nix
Nix
- Add the flake input:
inputs.tmux-mosaic.url = "git+https://git.barrettruth.com/barrettruth/tmux-mosaic.git";
- Source the packaged plugin from your tmux wrapper config:
run-shell ${tmux-mosaic.packages.${system}.default}/share/tmux-plugins/mosaic/mosaic.tmux
Quick Start
Use the master-stack layout and set @mosaic-auto-apply to managed, which
keeps plain tmux splits outside the layout until you explicitly adopt them:
set-option -gwq @mosaic-layout master-stack
set-option -gwq @mosaic-auto-apply managed
Because those are global settings, new windows inherit the same default layout and apply mode. A fresh one-pane window still looks ordinary until you split it or add panes.
Set up some default mappings for adopting panes into the layout, creating owned panes, and working the current layout:
bind Enter run -b '#{E:@mosaic-exec} promote'
bind -r , run -b '#{E:@mosaic-exec} resize-master -5'
bind -r . run -b '#{E:@mosaic-exec} resize-master +5'
bind N run -b '#{E:@mosaic-exec} new-pane'
bind A run -b '#{E:@mosaic-exec} adopt'
bind T run -b '#{E:@mosaic-exec} toggle'
Sometimes another layout works better for your task. Change the current window layout to grid:
bind G set-option -wq @mosaic-layout grid
or reset back to the global default:
bind U set-option -wqu @mosaic-layout
The section below explains what managed changes and how the other apply modes
behave.
See [Pane Ownership Model](#Pane Ownership Model) for pane ownership, what
managed changes, and how the other apply modes work.
Layouts
Managed windows can take on a variety of layouts as specified below. The
new-pane command appends to the end of the layout's pane order; see
below for what that means visually for each layout. Of course, the ownership
and auto-apply rules above determine whether native tmux pane operations join
the layout automatically.
The following layouts are provided:
master-stack — one or more master panes plus equal-split stack
Behavior
This keeps the first @mosaic-nmaster panes in the master area and the rest in
an equal-split stack. @mosaic-orientation chooses whether the master area
sits on the left, right, top, or bottom. resize-master changes the size of
the whole master area, not individual master panes. If you kill inside the
master area, the next pane in tmux's pane order fills the gap on the next
relayout, and if you drag-resize the master/stack boundary, Mosaic syncs that
size back into @mosaic-mfact. If @mosaic-nmaster is at least the pane
count, all panes become masters and Mosaic falls back to an equal split in the
chosen axis.
Preview
Core actions
| Command | Behavior |
|---|---|
toggle |
Turn master-stack off on the current window. |
relayout |
Re-apply the current orientation and @mosaic-mfact. |
new-pane |
Create an owned pane and append it to the end of pane order; with a stack present, it lands at the stack end. |
promote |
Focused pane becomes the first master. On the first master, rotate the next pane forward. |
resize-master ±N |
Change the whole master-region size for the current window, clamped to 5–95. |
select-pane -t :.- (builtin) |
Focus the previous pane in stack order. |
select-pane -t :.+ (builtin) |
Focus the next pane in stack order. |
swap-pane -U (builtin) |
Move the current pane up the stack. |
swap-pane -D (builtin) |
Move the current pane down the stack. |
split-window (builtin) |
Add a pane and rebalance master plus stack. |
kill-pane (builtin) |
Remove a pane and rebalance; killing the master promotes the stack-top. |
resize-pane (builtin) |
Resize the master live, then sync the new size back into @mosaic-mfact. |
Relevant options
| Option | Scope | Default | Effect |
|---|---|---|---|
@mosaic-orientation |
window→global | left |
Chooses left, right, top, or bottom |
@mosaic-nmaster |
window→global | 1 |
Keeps the first N panes in the master area |
@mosaic-mfact |
window→global | 50 |
Stores the master size as a percent |
@mosaic-step |
global | 5 |
Used by resize-master when you call it without an explicit N |
Example config
set-option -gwq @mosaic-layout master-stack
set-option -gwq @mosaic-orientation right
set-option -gwq @mosaic-nmaster 2
bind Enter run -b '#{E:@mosaic-exec} promote'
bind -r , run -b '#{E:@mosaic-exec} resize-master -5'
bind -r . run -b '#{E:@mosaic-exec} resize-master +5'
bind T run -b '#{E:@mosaic-exec} toggle'
bind U set-option -wqu @mosaic-layout
centered-master — center master with side stacks
Behavior
This keeps @mosaic-nmaster panes in a center column and splits the remaining
panes into left and right stacks. If there is only one stack pane, it falls
back to master plus right stack; otherwise the master stays centered, and an odd
extra stack pane goes to the right. resize-master changes the width of the
whole center region, and drag-resizing that boundary syncs back into
@mosaic-mfact.
Preview
Core actions
| Command | Behavior |
|---|---|
toggle |
Turn centered-master off on the current window. |
relayout |
Re-apply the centered master column and side stacks with the current @mosaic-mfact. |
new-pane |
Create an owned pane and append it to the end of pane order; with side stacks present, it joins them instead of displacing current masters. |
promote |
Focused pane becomes the first master. On the first master, rotate the next master forward. |
resize-master ±N |
Change the whole center-region width for the current window, clamped to 5–95. |
select-pane -t :.- (builtin) |
Focus the previous pane in tmux pane order. |
select-pane -t :.+ (builtin) |
Focus the next pane in tmux pane order. |
swap-pane -U (builtin) |
Move the current pane earlier in tmux pane order. |
swap-pane -D (builtin) |
Move the current pane later in tmux pane order. |
split-window (builtin) |
Add a pane and rebalance the center column plus both side stacks. |
kill-pane (builtin) |
Remove a pane and rebalance the centered layout. |
resize-pane (builtin) |
Resize the center column live, then sync the new width back into @mosaic-mfact. |
Relevant options
| Option | Scope | Default | Effect |
|---|---|---|---|
@mosaic-nmaster |
window→global | 1 |
Keeps N panes in the center master column |
@mosaic-mfact |
window→global | 50 |
Stores the center-region width as a percent |
@mosaic-step |
global | 5 |
Used by resize-master when you call it without N |
Example config
bind C set-option -wq @mosaic-layout centered-master
bind Enter run -b '#{E:@mosaic-exec} promote'
bind -r , run -b '#{E:@mosaic-exec} resize-master -5'
bind -r . run -b '#{E:@mosaic-exec} resize-master +5'
bind T run -b '#{E:@mosaic-exec} toggle'
bind U set-option -wqu @mosaic-layout
three-column — master column plus two slave columns
Behavior
This is the plain left-master sibling of centered-master. It keeps
@mosaic-nmaster panes in a master column on the left and splits the remaining
panes into middle and right slave columns. If there is only one stack pane, it
falls back to master plus right stack; otherwise an odd extra stack pane goes to
the middle column. resize-master changes the width of the whole master region,
and drag-resizing that boundary syncs back into @mosaic-mfact.
Preview
Core actions
| Command | Behavior |
|---|---|
toggle |
Turn three-column off on the current window. |
relayout |
Re-apply the left master column and two slave columns with the current @mosaic-mfact. |
new-pane |
Create an owned pane and append it to the end of pane order; with slave columns present, it joins them instead of displacing current masters. |
promote |
Focused pane becomes the first master. On the first master, rotate the next pane forward. |
resize-master ±N |
Change the whole master-column width for the current window, clamped to 5–95. |
select-pane -t :.- (builtin) |
Focus the previous pane in tmux pane order. |
select-pane -t :.+ (builtin) |
Focus the next pane in tmux pane order. |
swap-pane -U (builtin) |
Move the current pane earlier in tmux pane order. |
swap-pane -D (builtin) |
Move the current pane later in tmux pane order. |
split-window (builtin) |
Add a pane and rebalance the master column plus both slave columns. |
kill-pane (builtin) |
Remove a pane and rebalance the three-column layout. |
resize-pane (builtin) |
Resize the master column live, then sync the new width back into @mosaic-mfact. |
Relevant options
| Option | Scope | Default | Effect |
|---|---|---|---|
@mosaic-nmaster |
window→global | 1 |
Keeps N panes in the left master column |
@mosaic-mfact |
window→global | 50 |
Stores the master-region width as a percent |
@mosaic-step |
global | 5 |
Used by resize-master when you call it without N |
Example config
bind 3 set-option -wq @mosaic-layout three-column
bind Enter run -b '#{E:@mosaic-exec} promote'
bind -r , run -b '#{E:@mosaic-exec} resize-master -5'
bind -r . run -b '#{E:@mosaic-exec} resize-master +5'
bind T run -b '#{E:@mosaic-exec} toggle'
bind U set-option -wqu @mosaic-layout
spiral — dwm-style Fibonacci spiral
Behavior
This follows the dwm fibonacci spiral layout. The first pane gets a master
region on the left sized by @mosaic-mfact, and the remaining panes recurse
through the leftover space in a clockwise spiral. promote bubbles the focused
pane into the master slot, resize-master changes the first split, and
drag-resizing that master boundary syncs back into @mosaic-mfact.
Preview
Core actions
| Command | Behavior |
|---|---|
toggle |
Turn spiral off on the current window. |
relayout |
Re-apply the current Fibonacci spiral with the current @mosaic-mfact. |
new-pane |
Create an owned pane and append it to the end of pane order so it becomes the newest leaf in the spiral pattern. |
promote |
Focused pane becomes the master pane. On the master pane, rotate the next pane forward. |
resize-master ±N |
Change the first split width for the current window, clamped to 5–95. |
select-pane -t :.- (builtin) |
Focus the previous pane in tmux pane order. |
select-pane -t :.+ (builtin) |
Focus the next pane in tmux pane order. |
swap-pane -U (builtin) |
Move the current pane earlier in tmux pane order. |
swap-pane -D (builtin) |
Move the current pane later in tmux pane order. |
split-window (builtin) |
Add a pane and rebalance the recursive spiral. |
kill-pane (builtin) |
Remove a pane and rebalance the recursive spiral. |
resize-pane (builtin) |
Resize the master pane live, then sync the new width back into @mosaic-mfact. |
Relevant options
| Option | Scope | Default | Effect |
|---|---|---|---|
@mosaic-mfact |
window→global | 50 |
Stores the master-split width as a percent |
@mosaic-step |
global | 5 |
Used by resize-master when you call it without N |
Example config
bind S set-option -wq @mosaic-layout spiral
bind Enter run -b '#{E:@mosaic-exec} promote'
bind -r , run -b '#{E:@mosaic-exec} resize-master -5'
bind -r . run -b '#{E:@mosaic-exec} resize-master +5'
bind T run -b '#{E:@mosaic-exec} toggle'
bind U set-option -wqu @mosaic-layout
dwindle — shrinking Fibonacci sibling of spiral
Behavior
This is the shrinking sibling of spiral, following the dwm fibonacci
dwindle layout. The first pane gets a master region on the left sized by
@mosaic-mfact, and the remaining panes recurse through the leftover space in
a steadily shrinking bottom-right pattern. promote bubbles the focused pane
into the master slot, resize-master changes the first split, and
drag-resizing that master boundary syncs back into @mosaic-mfact.
Preview
Core actions
| Command | Behavior |
|---|---|
toggle |
Turn dwindle off on the current window. |
relayout |
Re-apply the current shrinking Fibonacci layout with the current @mosaic-mfact. |
new-pane |
Create an owned pane and append it to the end of pane order so it becomes the newest leaf in the dwindle pattern. |
promote |
Focused pane becomes the master pane. On the master pane, rotate the next pane forward. |
resize-master ±N |
Change the first split width for the current window, clamped to 5–95. |
select-pane -t :.- (builtin) |
Focus the previous pane in tmux pane order. |
select-pane -t :.+ (builtin) |
Focus the next pane in tmux pane order. |
swap-pane -U (builtin) |
Move the current pane earlier in tmux pane order. |
swap-pane -D (builtin) |
Move the current pane later in tmux pane order. |
split-window (builtin) |
Add a pane and rebalance the recursive dwindle pattern. |
kill-pane (builtin) |
Remove a pane and rebalance the recursive dwindle pattern. |
resize-pane (builtin) |
Resize the master pane live, then sync the new width back into @mosaic-mfact. |
Relevant options
| Option | Scope | Default | Effect |
|---|---|---|---|
@mosaic-mfact |
window→global | 50 |
Stores the master-split width as a percent |
@mosaic-step |
global | 5 |
Used by resize-master when you call it without N |
Example config
bind D set-option -wq @mosaic-layout dwindle
bind Enter run -b '#{E:@mosaic-exec} promote'
bind -r , run -b '#{E:@mosaic-exec} resize-master -5'
bind -r . run -b '#{E:@mosaic-exec} resize-master +5'
bind T run -b '#{E:@mosaic-exec} toggle'
bind U set-option -wqu @mosaic-layout
even-vertical — equal-height panes in one column
Behavior
This keeps panes in a single top-to-bottom column with equal heights. While it
is active, splits and kills re-apply the same column layout. There is no
master pane, so promote and resize-master are not implemented.
Preview
Core actions
| Command | Behavior |
|---|---|
toggle |
Turn even-vertical off on the current window. |
relayout |
Re-apply the equal-height column. |
new-pane |
Create an owned pane and append it to the bottom of the column. |
select-pane -U (builtin) |
Focus the pane above. |
select-pane -D (builtin) |
Focus the pane below. |
swap-pane -U (builtin) |
Move the current pane toward the top of the column. |
swap-pane -D (builtin) |
Move the current pane toward the bottom of the column. |
split-window (builtin) |
Add a pane and rebalance the column. |
kill-pane (builtin) |
Remove a pane and rebalance the column. |
Example config
bind V set-option -wq @mosaic-layout even-vertical
bind T run -b '#{E:@mosaic-exec} toggle'
bind U set-option -wqu @mosaic-layout
even-horizontal — equal-width panes in one row
Behavior
This keeps panes in a single left-to-right row with equal widths. While it is
active, splits and kills re-apply the same row layout. There is no master
pane, so promote and resize-master are not implemented.
Preview
Core actions
| Command | Behavior |
|---|---|
toggle |
Turn even-horizontal off on the current window. |
relayout |
Re-apply the equal-width row. |
new-pane |
Create an owned pane and append it to the right end of the row. |
select-pane -L (builtin) |
Focus the pane on the left. |
select-pane -R (builtin) |
Focus the pane on the right. |
swap-pane -U (builtin) |
Move the current pane toward the left side of the row. |
swap-pane -D (builtin) |
Move the current pane toward the right side of the row. |
split-window (builtin) |
Add a pane and rebalance the row. |
kill-pane (builtin) |
Remove a pane and rebalance the row. |
Example config
bind H set-option -wq @mosaic-layout even-horizontal
bind T run -b '#{E:@mosaic-exec} toggle'
bind U set-option -wqu @mosaic-layout
grid — equal-size tiled grid
Behavior
This uses tmux's tiled layout and lets tmux choose the row and column shape
from the current pane count. With four panes, it becomes a 2x2 grid. There is
no master pane, so promote and resize-master are not implemented.
Preview
Core actions
| Command | Behavior |
|---|---|
toggle |
Turn grid off on the current window. |
relayout |
Re-apply tmux's tiled layout. |
new-pane |
Create an owned pane and append it to the end of pane order, then let tmux retile the grid. |
select-pane -L (builtin) |
Focus the pane on the left when one exists. |
select-pane -R (builtin) |
Focus the pane on the right when one exists. |
select-pane -U (builtin) |
Focus the pane above when one exists. |
select-pane -D (builtin) |
Focus the pane below when one exists. |
split-window (builtin) |
Add a pane and retile the grid. |
kill-pane (builtin) |
Remove a pane and retile the grid. |
Example config
bind G set-option -wq @mosaic-layout grid
bind T run -b '#{E:@mosaic-exec} toggle'
bind U set-option -wqu @mosaic-layout
monocle — keep the focused pane zoomed
Behavior
This keeps the focused pane zoomed on windows with more than one pane.
Splitting the zoomed pane keeps the new pane zoomed, and changing focus
re-zooms the new active pane because Mosaic relayouts on after-select-pane.
There is no master pane or master size, so promote and resize-master are
not implemented.
Preview
Core actions
| Command | Behavior |
|---|---|
toggle |
Turn monocle off on the current window. |
relayout |
Re-zoom the active pane. |
new-pane |
Create an owned pane, append it to the end of pane order, and keep the new pane zoomed. |
select-pane -t :.- (builtin) |
Show the previous pane and keep it zoomed. |
select-pane -t :.+ (builtin) |
Show the next pane and keep it zoomed. |
split-window (builtin) |
Add a pane and keep the new pane zoomed. |
kill-pane (builtin) |
Remove a pane; any remaining active pane stays zoomed. |
Example config
bind Z set-option -wq @mosaic-layout monocle
bind T run -b '#{E:@mosaic-exec} toggle'
bind U set-option -wqu @mosaic-layout
bind n select-pane -t :.+
bind p select-pane -t :.-
Explicit new-pane limits
Mosaic now tries to birth explicit new-pane directly into the active layout's
semantic tail before the final relayout. Most common cases stay local, but a
few layout states still need visible correction during creation:
master-stackleftandtop1 -> 2start in the final broad branch immediately.rightandbottom1 -> 2keep append-to-end pane order, so tmux can only start on the non-mirrored side before the final relayout.- The
nmaster > 1all-masters -> first-stack transition still reshapes the whole master block.
centered-master- The first side-stack birth is targeted directly.
- The default
2 -> 3transition and later odd side-stack parity changes still shift one existing pane into or across the side stacks before the centered layout settles.
three-column- The first side-column birth is targeted directly.
- The default
3 -> 4transition and later even parity changes still move one existing pane from the right column into the middle column.
even-horizontal/even-vertical- The new pane is born in the right row or column, then tmux re-equalizes the whole row or column.
grid2 -> 3still moves the old bottom full-width pane into the new top row.- Current pane counts
4,6,9,12, and more generallyk^2andk(k + 1)fork >= 2, still force a true global retile on the next pane. - Other tested counts now split the tail directly before the final retile.
spiral- The easy leaf-node phases and the first node-leaf phase now birth in the outer recursive tail directly.
- Later node-leaf phases still push the previous tail inward while the new pane stays on the outer branch first.
dwindle- Normal growth stays local to the recursive tail.
monoclenew-panealways ends with the new pane focused and zoomed.- If the window was manually unzoomed first, Mosaic reasserts zoom after creation.
- Small windows
- If the intended target pane is too small to split, Mosaic now falls back to a generic append path when some other pane in the window can still split.
- If no pane in the window can split, tmux still reports
no space for new pane.
Pane Ownership Model
Once a tmux window can contain both panes that should participate in a Mosaic layout and panes created or moved in outside it, Mosaic needs a way to tell which panes it owns before it can safely relayout, sync sizes, or recover.
@mosaic-layout picks a layout for a window. Mosaic separately tracks whether
the panes in that window belong to the current managed layout. When Mosaic
first manages a window, it stamps the current panes with a window-specific
generation. Panes created or moved in outside Mosaic can stay foreign until
you adopt them.
@mosaic-auto-apply
The main user-facing choice is @mosaic-auto-apply, which decides what native
tmux structural commands should do when they create or expose panes that may
not belong to the current layout.
| Value | Raw split-window |
When to use it |
|---|---|---|
full |
Default. Adopt the new pane before structural relayout, so raw tmux splits join Mosaic. | You want native tmux pane commands to behave like part of the active layout. |
managed |
Leave the new pane foreign, mark the window suspended, and skip structural auto-relayout and size sync until you recover. | You want temporary helper panes that Mosaic ignores until you explicitly adopt them. |
none |
Leave the new pane foreign and skip structural auto-apply. Explicit new-pane, adopt, and local @mosaic-layout changes still work. |
You want Mosaic only for explicit actions. |
Under the hood, that ownership tracking still leaves each window in one of
three states: managed when all panes belong to the current generation,
suspended when foreign panes are present and Mosaic has stopped structural
auto-apply, and unowned when no layout is active for the window.
Explicit actions and recovery
Those modes mainly affect what happens when you use tmux's built-in structural commands. Mosaic's explicit actions are the tools for adding owned panes on purpose or recovering a window after foreign panes appear.
| Action | What it does |
|---|---|
new-pane |
Creates an owned pane, preserves the current path, appends it to the end of the layout's pane order, and relayouts once. |
adopt |
Rotates the window generation, claims all current panes, and relayouts once. |
relayout |
Re-applies the current layout to the current window. |
In managed mode, a raw split-window suspends the window until you close the
foreign pane, run adopt, or set a new local @mosaic-layout.
new-pane, promote, and resize-master refuse suspended windows with
mosaic: window is suspended; adopt panes first.