phux docs

Status bar and sidebar

The status bar is the one row phux keeps for tabs, the session name, and the clock. The sidebar is the strip down one side that lists your agents and sessions. Both are built from your configuration file, and both change on phux config reload except where noted.

[status]
position = "bottom"
right = [
  { kind = "cwd", truncate = 30 },
  { kind = "time", format = " %H:%M " },
]

[sidebar]
width = 28
position = "right"

The status bar

[status] has three slots, left, center, and right, each a list of widgets drawn in order. The shipped bar has the tabs and a + button on the left, nothing in the center, and the session name, clock, and a + session button on the right.

A widget is a table with a kind and that kind’s options. A bare string is shorthand for a widget with no options:

[status]
left = ["windows"]
center = [{ kind = "text", value = "prod" }]
right = ["session-name", { kind = "time", format = "%a %H:%M" }]

Setting a slot replaces the shipped list. To keep it and add to the end, use -append:

[status]
right-append = [{ kind = "exec", command = "uptime | cut -d, -f1", interval = "30s" }]

Set all three slots to [] and the bar disappears, giving its row back to the panes. position puts the bar on the "top" (the default) or "bottom" row.

Widget kinds

KindShows
windowsOne clickable tab per tab, with agent and zoom markers
session-nameThe current session’s name
timeThe clock, in strftime format
cwdThe focused pane’s working directory
exitThe focused pane’s last exit code (needs shell integration)
execThe first line a command prints, rerun on an interval
textFixed text
spacerBlank space that pushes its neighbours apart
buttonA clickable label that runs an action
switchA clickable chip that opens the agents list
help-hintsClickable hints for the main keys

Status-bar widgets lists every option of every kind with its default.

Styles and colours

Every widget takes a style table. fg and bg are colours; bold, dim, italic, underline, and reverse are true or false:

[status]
center = [{ kind = "text", value = " prod ", style = { fg = "black", bg = "red", bold = true } }]

A colour is a name ("red"), hex ("#f87171"), an ANSI palette index ("9"), or theme.<slot> to follow a theme slot. The shipped bar uses theme slots throughout, so a theme recolours it with the rest of the chrome:

[status]
left = [
  { kind = "windows", format = " {index} {name} ", active = { fg = "theme.accent", bg = "theme.selection_bg", bold = true }, inactive = { fg = "theme.dim" }, index = { fg = "theme.border" } },
]

The windows widget styles its parts separately: active for the current tab, inactive for the rest, and index for the number alone. format is the template for each tab ({index} and {name}), and separator goes between tabs. Every cell of a tab, padding included, is a click target.

Changing shape with the terminal

Every widget takes min-cols and max-cols. They compare against the width of the whole bar, and a widget outside its range takes no room at all, so the rest get its columns:

[status]
right = [
  { kind = "session-name", min-cols = 65 },
  { kind = "time", format = " %H:%M ", min-cols = 65 },
  { kind = "switch", max-cols = 64 },
]

This is what the shipped bar does: on a terminal 64 columns wide or narrower, the session name and clock give way to an agents chip that opens the agents list.

Buttons

A button runs one action that takes no arguments when you click it:

[status]
right-append = [{ kind = "button", label = "settings", action = "settings" }]

The shipped + after the tabs runs new-window, and + session runs new-session. Any action from Actions that needs no arguments works.

Plugins can add widgets too. They append to the end of their slot, after yours.

The sidebar

The sidebar shows two panels. Agents lists every agent with its state, under a row of filter chips: all, this (this session’s), and need (blocked, asking, or finished and not yet looked at). Sessions lists your sessions with the current one’s tabs nested under it, and a line per machine when you use more than one. Click an agent to go to its tab; click a session to switch to it.

Press C-a b to hide or show it. That choice lasts until you detach, across session switches. C-a a steps the Agents filter, and phux remembers the filter across attaches.

[sidebar]
enabled = true        # shown when you attach
width = 20            # columns; 0 adapts to a quarter of the terminal, 28 to 40
position = "left"     # or "right"
agents-share = 50     # percent of the height for Agents; 0 drops the panel
hosts = true          # group sessions by machine

Agents and Sessions split the height at a fixed line, so gaining or losing an agent never moves the Sessions panel. Plugins can add fixed sections between them.

[sidebar] settings apply when you attach. phux config reload does not move or resize a sidebar that is already showing; detach and run phux attach again.

Narrow terminals

The sidebar steps aside when the terminal is too narrow to afford it. Below its width plus min-pane-cols (40 columns by default; see Breakpoints), phux gives the panes the whole width and C-a b rings the bell instead of toggling. On a terminal that narrow, C-a A (or the status bar’s agents chip) opens the agents list full-screen.

Machines

With hosts = true, the Sessions panel lists the sessions of every machine you use, each under its own line. phux refreshes the other machines every hosts-refresh-secs seconds (default 10, at least 2) by running its own phux ls --all --json. hosts-provider replaces that command with any argv that prints the same JSON. hosts = false lists only the server you’re attached to.

Breakpoints

[chrome] sets the sizes at which phux’s layout adapts to a small terminal:

[chrome]
compact-cols = 64     # at or below: popups fill the width instead of floating
compact-rows = 18     # at or below: popups fill the height
min-pane-cols = 40    # the sidebar needs this many columns left for panes

Raise compact-cols for full-width pickers on a roomier terminal; lower min-pane-cols to keep the sidebar on a narrower one. 0 turns a threshold off.

Notifications

phux notifies you when an agent becomes blocked or asks for you, and when an agent that was working finishes:

[notifications]
mode = "terminal"           # "system", "bell", or "off"
terminal-protocol = "auto"  # or "osc9", "osc777", "osc99"
blocked = true              # an agent needs you
done = true                 # an agent finished
suppress-focused = true     # quiet for the pane you're looking at
sound = false
ModeDelivers
terminalA notification escape sequence to your terminal, so it reaches your desktop over SSH too
systemThe desktop notifier of the machine the client runs on (osascript on macOS, notify-send elsewhere)
bellThe terminal bell
offNothing

“Finished” fires once per run, and looking at the pane before it goes out cancels it. A burst of notifications within a second arrives as one.

terminal-protocol = "auto" picks OSC 99 for kitty, OSC 777 for Ghostty, WezTerm, and foot, and OSC 9 for the rest. Over SSH your terminal may not be identifiable; set the protocol yourself if notifications don’t appear. Inside tmux, phux wraps the sequence for passthrough, which needs set -g allow-passthrough on in tmux.

Debugging

  • phux config check reports an unknown widget kind or option with a did-you-mean, and a min-cols above max-cols, as status.right[0] and so on.
  • phux config show prints the [status] and [sidebar] in effect.
  • A bar that fails to build shows the error in its place when you attach, and phux config reload refuses the file.

If a [sidebar] change seems ignored, detach and attach again. If the sidebar won’t show at all, the terminal may be narrower than its width plus min-pane-cols.