Layouts
A layout is a named set of tabs that are opened together. Each tab can change into a directory, run a specific command instead of your shell, carry a name and a color, and optionally be split into a tree of panes. Layouts let you reproduce a working set of tabs — for example an editor, a chat/agent tool, and a couple of monitoring panes — with a single action instead of opening and arranging them by hand every time.
Configuration
Layouts are configured under the top-level layouts entry, a map of layout name to its
list of tabs. An optional top-level default_layout names a layout to open
automatically at startup instead of the usual single default tab.
layouts:
work:
tabs:
- title: "editor"
color: "#d75f00"
directory: "~/proj/foo"
command: "nvim"
arguments: ["."]
profile: "dark-big"
- title: "claude"
color: "#00af5f"
directory: "~/proj/foo"
command: "claude"
- title: "servers"
split:
orientation: vertical
panes:
- { command: "npm run dev", directory: "~/proj/foo", ratio: 0.6 }
- split:
orientation: horizontal
panes:
- { command: "htop" }
- { command: "journalctl -f" }
dev:
tabs:
- title: "shell"
directory: "~/proj/bar"
default_layout: work
Tab options
Each entry under tabs describes either a single-pane tab (leaf fields) or a split
tab (a split node). A tab uses leaf fields or split, not both.
| Option | Description |
|---|---|
title |
Optional. Seeds the tab's name. |
color |
Optional. Hex in '#RRGGBB' form (e.g. '#d75f00'); sets the tab's user color. |
directory |
Optional working directory for the tab. Falls back to the profile's configured directory when omitted. |
command |
Optional. Replaces the shell — the program to run in the pane. May be a full command line (see Command lines below). When omitted, the effective profile's shell is used. |
arguments |
Optional list of extra arguments, appended after any arguments already given in command. |
profile |
Optional per-tab profile override (see Per-tab profile below). |
split |
Optional. Turns the tab into a split node instead of a single pane; see Split panes. |
Command lines
command may be written as a full command line. It is kept as written and read when the pane starts, the
way a profile's shell is read: when it names a program to run — a file at that path, or a name found on
PATH — it runs as it is, spaces and all; otherwise its first word is the program and the rest are its
arguments. So both of these are equivalent:
How the words are read depends on the platform:
- On Linux, macOS and FreeBSD, as a POSIX shell reads them: whitespace separates words; double quotes
(
"...", allowing\"and\\) or single quotes ('...', literal) keep spaces inside one word; a backslash outside quotes escapes the next character. - On Windows, as
CreateProcessreads a command line: the program runs up to the first space at which what precedes it names a program — soC:\Program Files\Git\bin\bash.exe --loginrunsbash.exe— or up to its closing double quote, and a backslash separates directories. What follows the program is handed to it as written, for it to split itself:cmd.exe /s /c ""C:\Tools x\app.exe" --flag"reachescmd.exeunchanged. - A program path with spaces in it that names no file is split like any other command line; quote it to
keep it whole — e.g.
command: '"/opt/My App/bin/foo" --flag'. - In the Flatpak build, where a pane runs on the host, Contour cannot see the host's files: a
commandholding whitespace is always read as a command line there, so quote a program path with spaces in it. - Any entries in a separate
arguments:list are passed after the words ofcommand. ${NAME}insidecommandorargumentsexpands to the environment variableNAME. To pass a literal${...}through to the program (e.g. for template tooling), escape it as$${...}.
SaveLayout (below) writes command: back as the pane was started with it, and arguments: after it —
escaping any literal ${ — so everything round-trips.
Split panes
A split node replaces the leaf fields (command, directory, ...) with:
| Option | Description |
|---|---|
orientation |
vertical or horizontal (case-insensitive). vertical arranges panes side by side; horizontal stacks them. |
panes |
A list of child panes. Each child is either a leaf (with command, directory, arguments, profile, ratio) or another split node, nested recursively. |
ratio |
Optional, set on a child pane/split. The fraction of the parent split's space (between 0 and 1) that child occupies; children without a ratio share the remaining space equally. In the example above, ratio: 0.6 gives the first pane 60% and leaves 40% for its unspecified sibling. |
Splits can be nested arbitrarily deep, as shown in the servers tab of the example
above: a vertical split whose second pane is itself a horizontal split.
Per-tab profile
A tab (or an individual pane) may set profile to use a different profile than the
window's own profile for that tab/pane. The override is terminal-level only — it
affects things like the shell, fonts, colors, and scrollback of that pane. Window-level
settings (window size, decorations, and similar) always come from the profile that
launched the window itself.
Opening a layout
There are four ways to open a layout:
At startup
Set default_layout to a layout name; it opens automatically when Contour starts,
replacing the usual single default tab.
From the command line
Pass --layout NAME to open a layout in the newly launched window:
From a keybinding
Bind the LaunchLayout action to a key, passing the layout's name:
Unlike the startup and CLI cases, LaunchLayout appends the layout's tabs to the
current window rather than opening a new one.
From the command palette
Open the command palette (Ctrl+Shift+P by default) and pick the Launch Layout: name
entry — one is listed for every saved layout, so no keybinding is required. Picking one
appends that layout's tabs to the current window, exactly like the LaunchLayout action.
Saving the current window as a layout
The SaveLayout action captures the active window's current tabs and panes — their
directories, the command each pane was started with, titles, colors, and split
structure — and writes them out as a named layout.
SaveLayout never modifies your hand-written configuration file. Instead it writes a
machine-managed layouts.yml file next to your main configuration file, in the same
directory. Layouts from layouts.yml are merged with any layouts defined inline in
your main configuration; if a name exists in both, the layouts.yml version wins, since
it reflects the most recently saved state.
You don't need a keybinding to save: open the command palette (Ctrl+Shift+P) and pick
Save Layout. Because saving needs a name, that entry opens a small prompt for you to
type one in, then saves exactly as the SaveLayout action does. A key bound to
SaveLayout with no name opens the same prompt.