# Configuration Reference
`smux` reads a main TOML config file plus optional project definition files.
Default path:
```text
~/.config/smux/config.toml
```
If `XDG_CONFIG_HOME` is set, `smux` uses:
```text
$XDG_CONFIG_HOME/smux/config.toml
```
Project definitions live in:
```text
~/.config/smux/projects/*.toml
```
or, when `XDG_CONFIG_HOME` is set:
```text
$XDG_CONFIG_HOME/smux/projects/*.toml
```
`smux init` writes starter files with `#:schema` directives pointing at version-matched JSON Schema files for editor support.
`smux save-project` also writes a version-matched `#:schema` directive into exported project files.
`smux doctor` reports schema drift, and `smux doctor --fix` refreshes missing or stale schema directives in place.
Schema files live in this repo at:
```text
schemas/smux-config.schema.json
schemas/smux-project.schema.json
```
## Structure
The main config has two top-level sections:
- `settings`
- `templates`
Example:
```toml
#:schema https://raw.githubusercontent.com/Aietes/smux/vX.Y.Z/schemas/smux-config.schema.json
[settings]
default_template = "default"
icons = "auto"
[settings.icon_colors]
session = 75
directory = 108
template = 179
project = 81
[settings.picker.bindings]
reset = "ctrl-c"
sessions = "ctrl-s"
folders = "ctrl-f"
projects = "ctrl-p"
delete_session = "ctrl-x"
save_project = "ctrl-y"
[settings.picker.preview]
# sessions = "tmux capture-pane -p -t \"$SMUX_PREVIEW_SESSION\""
# folders = "eza --tree --level=2 --color=always --icons=always \"$SMUX_PREVIEW_PATH\""
# projects = "bat --style=plain --color=always --language=toml \"$SMUX_PREVIEW_FILE\""
[settings.folder_search]
# roots = ["~"]
# max_depth = 3
# include_hidden = false
[templates.default]
startup_window = "main"
windows = [{ name = "main" }]
[templates.rust]
startup_window = "editor"
startup_pane = 0
windows = [
{ name = "editor", pre_command = "source .venv/bin/activate", command = "nvim" },
{ name = "run", synchronize = true, layout = "main-horizontal", panes = [
{ command = "cargo run" },
{ layout = "right 40%", command = "cargo test", zoom = true },
] },
]
```
Example project file:
```toml
#:schema https://raw.githubusercontent.com/Aietes/smux/vX.Y.Z/schemas/smux-project.schema.json
path = "~/code/example"
session_name = "example"
template = "rust"
```
## `[settings]`
```toml
[settings]
default_template = "default"
icons = "auto"
```
Fields:
- `default_template`
- type: string
- optional
- used when neither `--template` nor a matching project provides a template
- `icons`
- type: `auto` | `always` | `never`
- default: `auto`
- controls whether picker icons are shown
### `[settings.icon_colors]`
```toml
[settings.icon_colors]
session = 75
directory = 108
template = 179
project = 81
```
Fields:
- `session`
- type: integer
- default: `75`
- `directory`
- type: integer
- default: `108`
- `template`
- type: integer
- default: `179`
- `project`
- type: integer
- default: `81`
These values are ANSI-256 color indexes used for picker icons.
### `[settings.picker.bindings]`
```toml
[settings.picker.bindings]
reset = "ctrl-c"
sessions = "ctrl-s"
folders = "ctrl-f"
projects = "ctrl-p"
delete_session = "ctrl-x"
save_project = "ctrl-y"
```
Fields:
- `reset`
- type: string
- default: `ctrl-c`
- resets the main picker to the full list
- `sessions`
- type: string
- default: `ctrl-s`
- filters the main picker to tmux sessions
- `folders`
- type: string
- default: `ctrl-f`
- filters the main picker to folders
- `projects`
- type: string
- default: `ctrl-p`
- filters the main picker to saved projects
- `delete_session`
- type: string
- default: `ctrl-x`
- closes the selected non-current tmux session or deletes the selected project file
- keeps the picker open
- `save_project`
- type: string
- default: `ctrl-y`
- saves the selected tmux session as a project and keeps the picker open
Rules:
- picker bindings must not be empty
- picker bindings must be unique within this block
- values are passed through as `fzf` key names
### `[settings.picker.preview]`
```toml
[settings.picker.preview]
sessions = "tmux capture-pane -p -t \"$SMUX_PREVIEW_SESSION\""
folders = "eza --tree --level=2 --color=always --icons=always \"$SMUX_PREVIEW_PATH\""
projects = "bat --style=plain --color=always --language=toml \"$SMUX_PREVIEW_FILE\""
```
Fields:
- `sessions`
- type: string
- optional
- shell command used for session previews
- receives `SMUX_PREVIEW_SESSION` and `SMUX_PREVIEW_KIND=session`
- `folders`
- type: string
- optional
- shell command used for folder previews
- receives `SMUX_PREVIEW_PATH` and `SMUX_PREVIEW_KIND=folder`
- `projects`
- type: string
- optional
- shell command used for project and broken-project previews
- receives `SMUX_PREVIEW_FILE` and `SMUX_PREVIEW_KIND=project` or `project-broken`
Defaults:
- sessions: built-in tmux window and pane summary
- folders: `eza` tree view when available, otherwise `ls -la`
- projects: `bat` with TOML syntax highlighting when available, otherwise `sed -n '1,200p'`
### `[settings.folder_search]`
```toml
[settings.folder_search]
roots = ["~"]
max_depth = 3
include_hidden = false
```
Fields:
- `roots`
- type: array of strings
- default: `["~"]`
- directory roots scanned by `smux select` in addition to zoxide
- `max_depth`
- type: integer
- default: `3`
- maximum directory depth below each root
- must be at most `16`
- `include_hidden`
- type: boolean
- default: `false`
- controls whether folder search descends into hidden directories
Folder search is bounded and skips common heavyweight directories such as `Library`, `node_modules`, and `target`.
## `[templates.<name>]`
Templates describe the tmux layout applied when `smux` creates a new session.
The recommended and documented format uses TOML 1.1 inline tables for `windows` and nested `panes`.
That means template definitions should normally be written as `windows = [{ ... }]` with nested `panes = [{ ... }]`, rather than verbose `[[templates...]]` arrays of tables.
Example:
```toml
[templates.rust]
startup_window = "editor"
startup_pane = 0
windows = [
{ name = "editor", cwd = "~/code/example", pre_command = "source .venv/bin/activate", command = "nvim" },
{ name = "run", synchronize = true, layout = "main-horizontal", panes = [
{ command = "cargo run" },
{ layout = "right 40%", command = "cargo test" },
] },
]
```
If you use `folke/persistence.nvim`, a practical editor command is:
```toml
{ name = "editor", command = "nvim -c 'lua require(\"persistence\").load({ last = true })'" }
```
That restores the last saved Neovim session when the window opens.
Template fields:
- `root`
- type: string
- optional
- currently accepted by the config schema
- `startup_window`
- type: string
- optional
- must match one of the template window names if set
- `startup_pane`
- type: integer
- optional
- default: `0`
- zero-based pane index within the startup window
- `windows`
- type: array of inline tables
- required
- must contain at least one window
### `windows = [{ ... }]`
Window fields:
- `name`
- type: string
- required
- `cwd`
- type: string
- optional
- `command`
- type: string
- optional
- `zoom`
- type: boolean
- default: `false`
- zooms that pane after the window has been created
- at most one pane per window may set `zoom = true`
- `pre_command`
- type: string
- optional
- runs in each pane of the window before the pane or window command
- `layout`
- type: string
- optional
- tmux window layout name passed to `tmux select-layout`
- examples: `tiled`, `main-horizontal`, `main-vertical`, `even-horizontal`, `even-vertical`
- applied after pane creation, so it may rearrange the final pane geometry
- `synchronize`
- type: boolean
- default: `false`
- enables tmux synchronized panes for that window
- `panes`
- type: array of inline tables
- optional
Rules:
- a window may define `command`
- a window may define `panes`
- a window may define neither
- a window may not define both `command` and `panes`
- if `panes` is present, it must not be empty
- `pre_command` runs as a separate command before the window or pane command
- `window.layout` is a tmux layout name and is passed through to tmux without additional validation
### `panes = [{ ... }]`
Pane fields:
- `layout`
- type: string
- optional
- format: `<position>` or `<position> <size>`
- supported positions: `right`, `left`, `bottom`, `top`
- controls how each new pane is created before any window-level tmux layout is applied
- `cwd`
- type: string
- optional
- `command`
- type: string
- optional
Examples:
- `layout = "right 40%"`
- `layout = "bottom 12"`
- `layout = "left"`
Interaction between pane and window layout:
- `pane.layout` controls split direction and optional size during pane creation
- `window.layout` is applied afterward with `tmux select-layout`
- if both are set, `window.layout` may rearrange the final pane geometry
- if you want tmux to normalize the final layout, set `window.layout`
- if you want to preserve the split sequence more closely, omit `window.layout`
## Recipes
These examples are meant to show practical combinations of pane `layout` and window `layout`.
### 2x2 grid
Use `tiled` when you want a simple 2x2-style workspace and do not care about preserving a specific split sequence.
```toml
[templates.grid]
startup_window = "grid"
windows = [
{ name = "grid", layout = "tiled", panes = [
{ command = "nvim" },
{ layout = "right", command = "cargo run" },
{ layout = "bottom", command = "cargo test" },
{ layout = "right", command = "git status -sb" },
] },
]
```
### One large top pane, two bottom panes
This is a good fit for `main-horizontal`.
```toml
[templates.dev]
startup_window = "run"
windows = [
{ name = "run", layout = "main-horizontal", panes = [
{ command = "nvim" },
{ layout = "bottom 30%", command = "cargo run" },
{ layout = "right", command = "cargo test" },
] },
]
```
This gives you one dominant pane at the top and two smaller panes below it.
### Sidebar on the left, work area on the right
```toml
[templates.sidebar]
startup_window = "main"
windows = [
{ name = "main", panes = [
{ command = "nvim" },
{ layout = "left 25%", command = "yazi" },
] },
]
```
### Vertical stack
```toml
[templates.stack]
startup_window = "stack"
windows = [
{ name = "stack", panes = [
{ command = "htop" },
{ layout = "bottom 40%", command = "cargo watch -x test" },
{ layout = "bottom 40%", command = "tail -f log/development.log" },
] },
]
```
## Project Definitions
Project definitions are stored as individual files in `~/.config/smux/projects/`.
The project name comes from the file name, for example:
```text
~/.config/smux/projects/myapp.toml
```
This project appears in `smux select` as `myapp`.
Project files can be written manually or exported from a live tmux session with:
```bash
smux save-project myapp
smux save-project myapp --stdout
```
Minimal project file:
```toml
path = "~/code/myapp"
template = "rust"
session_name = "myapp"
```
Fully defined project file:
```toml
path = "~/code/myapp"
session_name = "myapp"
startup_window = "editor"
windows = [
{ name = "editor", command = "nvim" },
{ name = "run", layout = "main-horizontal", panes = [
{ command = "cargo run" },
{ layout = "right 40%", command = "cargo test" },
] },
]
```
Project fields:
- `path`
- type: string
- required
- expanded and normalized before matching
- `session_name`
- type: string
- optional
- `template`
- type: string
- optional
- must refer to an existing template if set
- `root`
- type: string
- optional
- `startup_window`
- type: string
- optional
- `startup_pane`
- type: integer
- optional
- `windows`
- type: array of inline tables
- optional
Project behavior:
- a project may point at a template and use it as-is
- a project may define its own windows directly without using a template
- a project may use a template as a base and override it with project-specific session details
- when a project defines `windows`, they replace the template windows rather than merging window-by-window
- `smux save-project` exports concrete project files with inline `windows` instead of trying to infer template references
## Resolution Order
### Template resolution
When creating or connecting to a session, template resolution order is:
1. `--template`
2. matching project definition
3. `settings.default_template`
4. built-in fallback template
### Session name resolution
Session name resolution order is:
1. `--session-name`
2. matching project `session_name`
3. sanitized directory basename
## Validation Rules
`smux` validates the config when it is loaded.
Validation includes:
- `default_template` must exist if set
- each template must contain at least one window
- `startup_window` must refer to an existing template window
- `startup_pane` must be valid for the chosen startup window
- a window cannot define both `command` and `panes`
- a `panes` array cannot be empty
- project `template` references must exist
- project paths must be expandable and valid
## Picker Behavior
The unified picker combines:
- tmux sessions
- saved projects
- zoxide directories
- directories found by configured folder search
The template picker is separate and appears only when `--choose-template` is used.
Current behavior:
- prompt is shown at the top
- `Esc` cancels cleanly
- typing still does normal fuzzy search
- `Ctrl-C` resets the picker
- `Ctrl-S` limits the main picker to sessions and keeps fuzzy search active
- `Ctrl-P` limits the main picker to projects and keeps fuzzy search active
- `Ctrl-F` limits the main picker to folders and keeps fuzzy search active
- `Ctrl-X` closes the selected non-current tmux session or deletes the selected project file and keeps the picker open
- `Ctrl-Y` saves the selected tmux session as a project and keeps the picker open
- these keybinds are configurable through `[settings.picker.bindings]`
## Related Docs
- CLI overview: `README.md`
- design notes: `docs/design.md`
- distribution notes: `docs/distribution.md`