Layouts¶
Named tabs and split panes are created when a workspace is new. Reconnecting to
an open workspace keeps its running terminals. Select tab names through a
[[workspace]].tabs list, or through a matching [[rule]].tabs list for a
discovered project. See Configuration for file setup and lookup.
For one named tab without pane splits, start with the
small workspace example.
Herdr TUI and Herdr app layouts
Both integrations use these definitions to create local workspaces.
The walkthrough's proportions and focus behavior describe Herdr TUI.
The app splits evenly, ignores ratio, and has different focus behavior
during creation. See app layout differences.
Configured layouts cannot create remote spaces.
Pane layout walkthrough¶
The following complete native configuration creates an editor on the left,
with a server above logs on the right. Replace the workspace path with your
project directory. This example assumes nvim is installed, web/ contains
an npm project with a dev script, and development.log exists in the project
root; replace the commands to suit your project.
Edit your plugin config; pane definitions
are file-only. When adding this example to an existing
native config, keep its single top-level version = 1 line and use unique tab
and workspace names. Migrate legacy configs first.
version = 1
[[tab]]
name = "development"
[[tab.pane]]
name = "editor"
startup = "nvim"
[[tab.pane]]
name = "server"
split_from = "editor"
split = "right"
ratio = 0.35
path = "./web"
env = { NODE_ENV = "development" }
startup = "npm run dev"
[[tab.pane]]
name = "logs"
split_from = "server"
split = "down"
ratio = 0.3
startup = "tail -f development.log"
[[workspace]]
name = "my-project"
path = "~/projects/my-project"
tabs = ["development"]
The tabs list activates the layout; defining a tab alone does not create it.
Open the picker from the intended Herdr TUI session:
In the Herdr app, open the sesh mod instead.
Select my-project, which must not already be open. The new workspace opens on
Herdr's initial tab; select development to see the layout with the editor
focused. See Initial tab behavior for why it stays separate.
For optional validation before opening, follow the CLI setup
and run "$sesh_bin" config validate.
my-project workspace
Tabs: [Herdr initial tab (selected)] [development]
development tab — approximate proportions
┌──────────────────────────────────────┬────────────────────┐
│ editor (focused) │ server │
│ nvim │ npm run dev │
│ │ │
│ │ │
│ │ │
│ │ │
│ ├────────────────────┤
│ │ logs │
│ │ tail -f │
│ │ development.log │
└──────────────────────────────────────┴────────────────────┘
65% width 35% width
ratio is the new pane's share of the pane being split: the right column
gets 35% of the tab width, then logs take 30% of that column's height.
| Pane | Working directory | Pane-specific environment | Startup command |
|---|---|---|---|
editor |
~/projects/my-project |
None added | nvim |
server |
~/projects/my-project/web |
NODE_ENV=development |
npm run dev |
logs |
~/projects/my-project |
None added | tail -f development.log |
A pane inherits the tab directory, not the pane it splits. Here the tab
uses the workspace directory, so logs stay at the project root. Relative pane
paths resolve against the tab directory: a tab path = "frontend" would make
./web resolve to ~/projects/my-project/frontend/web.
[[tab]]¶
| Field | Runtime effect |
|---|---|
name |
Name referenced by a workspace or rule tabs list and used as the Herdr tab label. Must be non-empty and unique. |
path |
Optional tab working directory. Without it, the workspace path is used; relative paths resolve against it, ~/ expands, and absolute paths are used directly. |
startup |
Command run in the new tab. {} is replaced with that tab's working directory. Cannot be combined with [[tab.pane]] entries; set startup on each pane instead. |
pane |
Optional [[tab.pane]] layout. See below. |
[[tab.pane]]¶
Herdr TUI layouts require Herdr 0.8.2 or newer, as enforced by the plugin manifest.
Direct CLI use has no version preflight: check herdr --version. An older binary
may leave a partial workspace; upgrade, then follow recovery.
Pane fields¶
| Field | Runtime effect |
|---|---|
name |
Pane name referenced by later split_from values. Must be non-empty and unique within the tab. |
split_from |
Earlier pane to split. Required for every pane after the first; forward and self references are rejected. |
split |
"right" or "down". Required for every pane after the first. |
ratio |
Optional share of the split given to the new pane, from 0.1 to 0.9 in Herdr TUI. Without it, the TUI splits evenly. The app always splits evenly and ignores this field. |
path |
Optional working directory. Without it, the tab path is used; relative paths resolve against the tab path and ~/ is expanded. |
env |
Optional environment variables for the pane's shell. Names must match [A-Za-z_][A-Za-z0-9_]*; values cannot contain NUL bytes. Herdr TUI passes values as arguments; the app sends shell setup commands. Shell startup files can override them. |
startup |
Command run in the pane. {} is replaced with the pane's shell-quoted working directory. |
wait_for |
Optional readiness check: { match = "...", timeout_ms = 15000 }. Requires startup. See Waiting for a pane to become ready. |
focus |
Optional true to focus this pane once a new workspace's layout is built. See Choosing the focused pane. |
Do not put secrets in pane environment values
In Herdr TUI, values are passed as --env KEY=VALUE arguments. The Herdr
app sends octal-encoded setup commands through hc send. Octal encoding
is reversible. Values in either integration may be visible to local users
through process inspection, subject to OS permissions.
App setup commands may also enter shell history unless the shell ignores commands with a leading space. Error redaction does not prevent these exposures. Load secrets through shell credential tooling instead.
split_from copies neither path nor env; configure each pane separately.
The first pane is the root and cannot set split_from, split, or ratio.
Its path and env apply at tab creation (or workspace creation when reusing
the initial tab). In Herdr TUI, later panes split in declaration order without
taking focus.
Initial tab behavior¶
Herdr creates an initial tab. In Herdr TUI, reuse and focus follow this table
when tabs are configured. Picker selection uses the normal column, while
connect --no-focus creates the workspace in the background:
| Workspace startup | First tab's root pane | Reuse initial tab? | Active tab: normal / --no-focus |
|---|---|---|---|
| None | Workspace directory, no startup | Yes | First configured / first configured |
| None | Workspace directory, with startup | No | Initial / initial |
| Runs | Workspace directory, with or without startup | No | First configured / initial |
| Either | Another directory | No | Initial / initial |
A focus marker replaces the normal column's result when the target's configured directory matches the workspace directory.
Workspace startup includes rule/default fallbacks unless disable_startup = true.
The root uses the tab's path, overridden by the first pane's path. Later
panes/tabs do not affect reuse. Without configured tabs, the initial tab remains.
A reused tab receives the configured label and root env.
To reuse the initial tab, leave the first root at a shell in the workspace
directory, with no workspace startup; put commands in other panes. Any root
startup, even git status, keeps a separate tab because commands can change
directories. Herdr reports the active pane's directory as the workspace path;
reconnect by name or ID if a command or pane selection changes it.
Workspace startup runs in the initial pane, separately from tab/pane commands.
Its exports and directory changes do not carry into configured tabs; use their
path and pane env settings.
Startup commands and focus¶
Commands are sent separately, without eval or shell composition. Layouts do
not wait for workspace startup to finish. Use pane wait_for
for dependencies. Commands use the pane shell's syntax; {} uses POSIX shell
quoting, so avoid it with incompatible shells.
disable_startup = true suppresses only workspace startup, including rule/default
fallbacks. Tabs and panes still run their commands; omit a pane's startup to
leave it at a shell.
Choosing the focused pane¶
Set focus = true on one pane to focus it, and its tab, after the new
workspace's layout is built. The target can be in a later tab, but its resolved
configured directory must match the workspace directory. Markers on panes in
other directories are skipped, preserving the normal focus behavior and
path-based reconnects. Both the pane's path and an inherited tab path count.
For example, this selects a server pane that runs from the workspace directory:
[[tab.pane]]
name = "server"
split_from = "editor"
split = "right"
startup = "npm run dev"
focus = true
Without a marker, focus follows the table above. Each workspace or rule tabs
list may reach only one marked pane: config load rejects two markers in one
tab, two marked tabs in one list, or a marked tab listed twice. Several lists
can share the same marked tab.
connect --no-focus ignores the marker. It keeps your current focus in Herdr
TUI; the app restores the previously shown tab after creation. The marker
applies only when the layout completes; reconnecting to an open workspace
never reapplies the marker. Startup commands can still change a pane's
directory after creation; reconnect by name or ID if that happens.
In Herdr TUI, focus uses the pane.focus socket API through
HERDR_SOCKET_PATH, which Herdr sets for the picker, plugin actions, and its
panes. Run direct CLI commands from a Herdr TUI pane to use explicit focus.
In the Herdr app, focus uses the app's pane.focus command and does not require
HERDR_SOCKET_PATH. For direct CLI use, select the app backend with
HERDR_SESH_BACKEND=app; see backend selection.
If focus fails, the command reports a warning and keeps the completed layout. Connect still succeeds and records the workspace switch in history.
Waiting for a pane to become ready¶
To wait for a server before starting tests, add wait_for to the server pane.
Without it, commands run in declaration order without waiting for completion.
Add this tab to a native config and reference "development" in a workspace's
tabs list; replace any existing tab with that name:
[[tab]]
name = "development"
[[tab.pane]]
name = "server"
startup = "npm run dev"
wait_for = { match = "Local: http://localhost:3000", timeout_ms = 15000 }
[[tab.pane]]
name = "tests"
split_from = "server"
split = "right"
startup = "npm run test:integration"
After sending startup, herdr-sesh waits for matching output before creating later panes or tabs. Output printed before the wait starts is also searched.
| Field | Meaning |
|---|---|
match |
Literal text to find on a single output line. A line that wraps at the pane's edge still counts as one line. Required and non-empty. It must not appear in startup, because the pane echoes the typed command. ANSI styling is ignored. |
timeout_ms |
Optional wait limit in milliseconds, from 1 to 600000 (10 minutes). Defaults to 30000. |
Matches are checked against startup when loading and again after {} expands
to the final path. A match in the expanded command stops the layout before that
command runs, to avoid accepting echoed input as readiness.
Timeout, wait failure, or interruption stops later panes, commands, and tabs;
errors identify the workspace, tab, pane, and failed check. Running processes
are kept; use recovery.
wait_for is a one-time startup barrier, not health monitoring or automatic
restart. In Herdr TUI, it uses herdr pane wait-output, supported since
Herdr 0.8.2. The Herdr app instead polls the terminal's screen and recent
history through hc read TERMINAL --lines 1000.
Reconnecting and recovering a partial layout¶
Configuration is validated before workspace creation. If a Herdr operation fails during layout creation, the error identifies the workspace, tab, pane, and operation; the workspace and running processes are kept.
If creation failed, correct the reported error first. To apply edits or rebuild a partial layout, save your work, stop processes as needed, close the workspace, and select it again. Reconnecting to an open workspace never rearranges panes, reruns commands, or retries readiness checks.