The footer
The footer is the status area under the input box. It is data, not code:
[tui.footer] in the config file picks a preset, a glyph set and rows of
items. Every key below takes effect at the next startup. A bad value warns
once on the startup line and keeps its default; nothing here blocks startup.
radian footer export <preset> prints one built-in preset's TOML exactly.
Pasting that output as [tui.footer] and editing it customizes from that
preset. An unknown preset name prints the four names on stderr and exits 2.
[tui.footer]#
| Key | Type | Default | Values | What it does |
|---|---|---|---|---|
preset |
string | deck |
deck, full, pi, classic |
Which built-in rows draw when rows is empty. |
separator |
string | " · " |
any string | The glyphs between two items. A row may override it. |
separator_style |
style | footer_separator |
a theme role, one of dim, muted, accent, warn, error, ok, text, or a fixed color |
The color of the separator. A bad value warns and keeps footer_separator. |
glyphs |
string | unicode |
unicode, nerd, ascii, emoji |
Which icon set items draw. nerd needs a Nerd Font; emoji draws through the system emoji font. An unknown value warns and keeps unicode. |
context_line |
string | off |
off, percentage |
How the gap between a row's left and right groups draws. percentage fills it with a ─ rule: the used share of the context window in the budget role, the rest in footer_separator. With no known budget the gap stays spaces. An unknown value warns and keeps off. |
transparent |
boolean | false |
true, false |
true clears every footer cell background, so the terminal's own background shows. |
items |
table | none | [tui.footer.items.<id>] tables |
Per-item defaults. A row that names the item by bare id takes them; an inline table in the row wins. |
rows |
array | none | [[tui.footer.rows]] tables |
Your own rows. Empty means the preset's rows draw. |
One structural mistake anywhere in [tui.footer] (an unknown key, a wrong
type, a row with an unknown key, an item that is neither a string nor a
table, a threshold without an integer at and a string style) drops the
whole block with a warning, and the deck preset draws.
Rows#
| Field | Type | Default | Values | What it does |
|---|---|---|---|---|
left |
array | empty | item ids or inline item tables | Items before the gap. |
right |
array | empty | item ids or inline item tables | Items after the gap. |
separator |
string | the footer's separator |
any string | The glyphs between this row's items. |
position |
string | footer |
footer, above_input, below_input |
Where the row draws: the status area, one line above the input box, or one line below it. An unknown value warns and draws at footer. |
A row with no data renders empty and takes no space. [tui.widgets]
is the old spelling of position: above_input and below_input id lists
fold into footer rows, with one deprecation warning.
Items#
An item is a bare id ("model") or an inline table
({ item = "cwd", format = "{repo}" }). A bare id takes
[tui.footer.items.<id>]; an inline table is used as written, except that
[tui.footer.items.<id>] still supplies the item's own parameters. A table
without an item key is a structural mistake and drops the whole
[tui.footer] block.
| Field | Type | Default | What it does |
|---|---|---|---|
item |
string | none | The item id. A built-in id draws its component; an id with a command parameter draws that command's value; any other id draws a contributor's published text once one exists, else its own id text with one warning. |
icon / symbol |
string | the glyph set's icon | Both names set the same field. Drawn before the text. "" draws no icon. |
symbol_style |
style | the item's text style | The icon's own style, when it differs from the text's. A bad value warns and follows the text. |
format |
string | the item's own default | {placeholder} names from the item's section below, plus [text](style) spans with an inline style. An unknown placeholder or a bad inline style warns and shows the format as written. |
style |
style | the item's own role | A theme role, one of dim, muted, accent, warn, error, ok, text, or a fixed color. A bad value warns and keeps the role. A style the data itself sets (a thinking level, a stale mark) wins over it. |
priority |
integer | the item's own number | Higher survives a narrow terminal longer. |
thresholds |
array | the item's own list | { at = 70, style = "warn" } entries in file order. Only context, cost, pending and queued have a number to compare, so only they ever match one. |
disabled |
boolean | false |
true hides the item everywhere it is named. A disabled command item never runs. |
command |
string | none | Turns the id into a command item. Write it in [tui.footer.items.<id>]: only that table starts the command. An inline command (or period) in a row is not read: the load warns tui.footer: item "<id>" sets `command` (or `period`) inline; inline `command`/`period` is not read, move it to `[tui.footer.items.<id>]` and the id follows the unknown-id rule unless the items table sets command for it. A command under a built-in id never runs: the load warns tui.footer.items.<id>.command: "<id>" is a built-in item; its `command` is ignored and the built-in keeps the draw. |
period |
integer seconds | 60 |
How often a command item runs. 0 runs once at startup. Any other value clamps silently into 15–3600. A non-number warns and keeps 60. |
width, fill, empty |
string | 10, ━, ─ |
The context item's bar. A non-number width reads as 10. |
keys |
string | all keys | The status item's filter: comma-separated status.* key names, trimmed. |
A style value is a theme role name (every name themes.md lists), one of
dim, muted, accent, warn, error, ok, text, or a fixed color:
one of black, red, green, yellow, blue, purple, magenta,
cyan, white, bright_black, bright_red, bright_green,
bright_yellow, bright_blue, bright_purple, bright_magenta,
bright_cyan, bright_white, #rrggbb (exactly six hex digits), or a
0–255 index; plus bold, italic, dimmed, underline. The first
color is the foreground, a second the background; a third is an error. An
empty string is the default style.
Thresholds#
thresholds compare one number per item. Only four items have one:
context compares the used percent rounded to a whole number (0–100),
cost compares whole dollars, pending and queued compare their count.
Every other item ignores its thresholds. The default list on context and
cost is { at = 70, style = "warn" } then { at = 90, style = "error" }
(warn is the budget_over role, error the tool_result_error role);
every other item defaults to an empty list. When several entries match, the
last matching entry in list order wins. A style the data itself sets wins
over every threshold.
Items#
Each section names the item's placeholders, its default format, its icon
in each glyph set (unicode / nerd / ascii / emoji), its default
role, its priority, and when it hides. none as a role means the terminal
default. An item with no data hides and its row takes no space.
brand#
Placeholders: brand. Default format: {brand}. Icons: none (the mark is
the value). Role: notice_label under unicode and emoji, or whenever
tui.brand is set; text otherwise. Priority: 92. Never hides. The value
is tui.brand when set (at most 4 display columns; wider is a config
error), else the set's mark: R (unicode), (nerd), rad
(ascii), ⚡ (emoji).
host#
Placeholders: host. Default format: {host}. Icons: @ / /
host / 💻 . Role: notice_body. Priority: 78. The machine's
hostname, read once at startup. Hides when the hostname is unknown.
model#
Placeholders: display_name, id, provider. Default format:
{display_name}. Icons: none. Role: model. Priority: 90. Never hides:
with no model {display_name} is no model and {id} and {provider}
are empty. {display_name} is the display name when set, else the model
id.
reasoning#
Placeholders: level. Default format: {level}. Icons: * / /
* / 🧠 . Role: none (the level picks its own: off draws in
reasoning_off, minimal in reasoning_minimal, low in
reasoning_low, medium in reasoning_medium, high in
reasoning_high, xhigh in reasoning_xhigh, max in reasoning_max;
any other value draws in reasoning_medium). A configured style never
overrides it. Priority: 60. Hides unless a thinking level is set. Levels
are off, minimal, low, medium, high, xhigh, max.
tier#
Placeholders: tier. Default format: {tier}. Icons: none. Role: tier.
Priority: 75. Hides unless the session has a tier.
cwd#
Placeholders: path, path_home, basename, repo, worktree. Default
format: {path_home}. Icons: none. Role: none. Priority: 80. Hides when
the working directory is unknown. {path} is the full path; {path_home}
shortens a $HOME prefix to ~; {basename} is the last path component;
{repo} is the repository root's name and falls back to {basename}
outside a repository; {worktree} is empty outside a worktree.
git_branch#
Placeholders: branch, dirty, modified, untracked. Default format:
{branch}. Icons: none / / none / none. Role: none (the contribution's
role; dim while stale). Priority: 70. Hides with no branch data.
{modified} and {untracked} are the change counts, empty when unknown;
{dirty} is empty on a clean tree, else ? plus the total count.
worktree#
Placeholders: worktree. Default format: worktree {worktree}. Icons:
none. Role: none (the contribution's role; dim while stale). Priority: 70.
Shows only inside a worktree.
status#
Placeholders: status. Default format: {status}. Icons: none. Role: none
(normal text; dim while stale). Priority: 45. Joins every status.*
contribution with spaces. Hides with no text. Parameters: keys filters to
the named keys: keys = "build, lint" shows only status.build and
status.lint. Without keys every key shows.
session#
Placeholders: id. Default format: {id}. Icons: # / none / # /
# . Role: none. Priority: 65. Never hides. The value is the session id.
context#
Placeholders: bar, percent, used, budget, auto. Default format:
{used}/{budget}. Icons: none / / [ / none. Role: budget, with the
default thresholds above. Priority: 85. Never hides: unknown counts show
-. {bar} is width cells (default 10; a non-number reads as 10),
fill (default ━) for the used part, empty (default ─) for the rest.
{percent} has one decimal (41.3). {auto} is ~ while automatic
compaction is on ( auto under ascii), else empty.
usage#
Placeholders: value. Default format: {value}. Icons: none. Role: none
(normal text; dim while stale). Priority: 50. Shows only while the current
provider reports a usage snapshot: each window's remaining share, each
window's reset countdown, then the plan word. Hides for a provider with no
usage source, or when the stored snapshot belongs to another provider.
/usage prints the same snapshot in full.
tokens#
Placeholders: in, out, cache, cache_write, in_arrow,
out_arrow. Default format under unicode and emoji:
↑{in} ↓{out} R{cache}; under nerd and ascii: ^{in} v{out} R{cache}.
{in_arrow} is ↑ (unicode, emoji) or ^ (nerd, ascii);
{out_arrow} is ↓ or v. Icons: none. Role: usage. Priority: 50.
Never hides: a count no provider reported shows -, never a fabricated
zero. {cache} is the cache-read total; {cache_write} is the
cache-write total and stays out of the default format. Counts render
compact: raw below 1000, K below a million, M above.
speed#
Placeholders: speed. Default format: {speed}. Icons: » / /
spd / 🚀 . Role: usage. Priority: 55. The last finished
generation's tokens per second (42.1 tok/s). Hides until one finishes.
elapsed#
Placeholders: elapsed. Default format: {elapsed}. Icons: none / /
t / ⏱ . Role: truncation. Priority: 35. Live while a generation
runs, the last one when idle. Hides with no reading, and stays empty after
a resume. Renders 16.2s, past a minute 1m02s, past an hour 1h02m.
cost#
Placeholders: usd. Default format: {usd}. Icons: none. Role: none, with
the default thresholds above. Priority: 40. The session's accumulated fee
($14.510, three decimals so a sub-cent fee never reads as free). A total
with a missing rate carries ≥; a total re-priced from old records carries
~. Hides until some call bills and the catalog has a price: never a
fabricated number.
pending#
Placeholders: count. Default format: {count} pending. Icons: none.
Role: turn_running. Priority: 70. Shows only while a request waits.
queued#
Placeholders: count. Default format: {count} queued. Icons: none. Role:
turn_running. Priority: 70. Shows only while a queued input (/queue,
or a steered Enter) waits for its turn.
loop#
Placeholders: count, every, next. Default format varies with the
state: loop 3/20 while iterating, loop every 5m · 3/20 with an --every
wait between turns, loop · next in 3m while the wait runs, loop on while
armed with no prompt yet, loop paused while paused. Icons: none. Role:
turn_running. Priority: 70. Shows only while the loop is active.
{count} is iterations/max_iterations.
jobs#
Placeholders: count, age. Default format: {count} · {age}. Icons:
none. Role: none. Priority: 70. This session's running background jobs
with the oldest one's age (2 jobs · 18m, 1 job · 5s). Shows only while
jobs run. The age renders 5s under a minute, 18m under an hour,
2h14m above.
mcp#
Placeholders: mcp. Default format: {mcp}. Icons: none. Role: mcp.
Priority: 55. Always shows: the configured and connected servers and
tools.
scroll#
Placeholders: position, mode. Default format: {position} {mode}.
Icons: none. Role: none. Priority: 20. Never hides. {position} is all
at the bottom with nothing to scroll, else scroll/max (for example
12/48). {mode} is follow or paused.
Command items#
Any id no built-in claims becomes a command item when its
[tui.footer.items.<id>] table sets command. Only that table starts the
command: a command (or period) written inline in a row is not read. The
load warns once per such item
tui.footer: item "<id>" sets `command` (or `period`) inline; inline `command`/`period` is not read, move it to `[tui.footer.items.<id>]`,
and the id follows the unknown-id rule unless the items table sets command
for it (an inline table still wins over the items table for every other
field). A command under a built-in id never runs: the load warns
tui.footer.items.<id>.command: "<id>" is a built-in item; its `command` is ignored
and the built-in keeps the draw. Placeholders: value.
Default format: {value}. Icons: none, unless the output or the item sets
one. Priority: 50 unless the item sets another.
command is a command line, not a shell: whitespace separates words, single
and double quotes group them, a backslash escapes the next character (inside
double quotes too). An empty command, an empty executable name, an
unterminated quote or a trailing backslash warns and never runs. period
sets the refresh period (see the items table). The command runs as you,
unsandboxed, on that period, with stdin as one JSON object of display-safe
facts (model, cwd, branch, context_percent, tokens with input,
output and cache_read, cost_usd; each null when unknown, never a
fabricated zero). Only PATH, RADIAN_SESSION_ID and RADIAN_CWD pass
through from the environment; everything else is cleared.
Its stdout's first line is the value, trimmed and cut at 256 bytes on a
UTF-8 boundary (at most 64 KiB is read, so a flooding command cannot grow
memory). A line that does not start with { is plain text. A line that
starts with { must be a JSON object with a string text and optional
string style and symbol: {"text": "ok", "style": "warn", "symbol": "! "}.
style names one of normal (also text), dim (also dimmed,
muted), warn (also warning), ok (also success), error (also
err), accent; absent or null means normal. A JSON symbol draws
verbatim before the text, so include its own trailing space; a configured
symbol on the item wins over it.
One run may take 5 seconds; a timeout, a spawn failure, a non-zero exit, an empty line or a bad line keeps the last value and draws it stale (dim). Until the first good value the item hides.
Narrow terminals, numbers, unknown text#
A row that is too wide drops items until it fits: the whole right group
first, then left, lowest priority first, ties broken toward the group's
right end. Counts render compact: raw below 1000 (999), K below a
million (9.5K, 15K), M above (15M, 1962M); one decimal below ten
of the unit, whole numbers above. Money renders $14.510. Percents render
with one decimal (41.3%).
An unknown item id, placeholder or style shows as written with one warning
each and never blocks startup — except an id a contributor publishes, which
draws that text normally. An unknown scalar parameter on a built-in item is
kept and ignored silently; a non-scalar one (an array or a table) drops the
whole [tui.footer] block with a warning.
Presets#
Preset deck#
Three rows: the provider usage line (dropped when it has no snapshot, so it never leaves a blank line), the identity row, then the numbers row. This is the default.
# The `deck` preset (the default): the current provider's usage
# line (dropped when it has no snapshot, so it never leaves a blank line), the identity row,
# then the numbers row.
#
# A preset file uses the same keys a user writes for `[tui.footer]`: `radian footer export
# deck` prints this file exactly, and using it as the config's `[tui.footer]` section gives
# this footer.
[tui.footer]
preset = "deck"
separator = " · "
[[tui.footer.rows]]
left = ["usage"]
[[tui.footer.rows]]
left = [
"model",
{ item = "cwd", format = "{repo}" },
{ item = "git_branch", format = "{branch}{dirty}" },
"worktree",
]
right = [
{ item = "context", symbol = "", format = "{bar} {percent}%" },
"reasoning",
"queued",
"loop",
]
[[tui.footer.rows]]
separator = " "
right = [
"tokens",
"cost",
{ item = "context", symbol = "", format = "{percent}%/{budget}" },
]
Preset full#
Two rows: the identity row with host and session, then the numbers row with speed, elapsed and background jobs.
# The `full` preset: the identity row and the numbers row.
#
# `full` includes `jobs` (this session's running background jobs and the
# oldest one's age; empty while none runs, so it takes no space); the other presets do not.
# A preset file uses the same keys a user writes for `[tui.footer]`: `radian footer export
# full` prints this file exactly, and using it as the config's `[tui.footer]` section gives
# this footer.
[tui.footer]
preset = "full"
separator = " · "
[[tui.footer.rows]]
left = [
"brand",
"host",
"model",
"reasoning",
{ item = "cwd", format = "{repo}" },
{ item = "git_branch", format = "{branch}{dirty}" },
"session",
]
[[tui.footer.rows]]
left = [
{ item = "tokens", format = "{in_arrow}{in} {out_arrow}{out} {cache}" },
"speed",
"cost",
{ item = "context", format = "{percent}%/{budget}{auto}" },
"elapsed",
"jobs",
]
Preset pi#
Two rows: model, directory, branch, the context bar and thinking on top; tokens and cost below.
# The `pi` preset (the earlier two-row preset): the model/cwd/branch row with the
# context bar and reasoning, then the tokens/cost row.
#
# A preset file uses the same keys a user writes for `[tui.footer]`: `radian footer export
# pi` prints this file exactly, and using it as the config's `[tui.footer]` section gives
# this footer.
[tui.footer]
preset = "pi"
separator = " · "
[[tui.footer.rows]]
left = [
"model",
"cwd",
"git_branch",
{ item = "context", symbol = "", format = "{bar} {percent}%" },
]
right = ["reasoning"]
[[tui.footer.rows]]
left = ["tokens", "cost"]
Preset classic#
No rows: the renderer draws the one status line it always drew.
# The `classic` preset: today's one status line, byte-for-byte. It is drawn by the
# renderer, so it carries no rows.
#
# A preset file uses the same keys a user writes for `[tui.footer]`: `radian footer export
# classic` prints this file exactly, and using it as the config's `[tui.footer]` section
# gives this footer.
[tui.footer]
preset = "classic"
separator = " · "
Worked examples#
A two-row footer from scratch. Name two items per side, turn the gap into a context gauge, and drop the separator's own styling to plain dim:
[tui.footer]
preset = "deck"
glyphs = "unicode"
context_line = "percentage"
[[tui.footer.rows]]
left = ["model", { item = "cwd", format = "{repo}" }]
right = ["reasoning"]
[[tui.footer.rows]]
left = ["tokens", "cost"]
right = [{ item = "context", format = "{percent}%/{budget}" }]
separator = " "
A command item that shows the current ticket. The id is your own, so no
built-in claims it; period = 60 refreshes it every minute; {value} is
its only placeholder:
[tui.footer.items.ticket]
command = "~/bin/ticket"
period = 60
[[tui.footer.rows]]
left = ["model", "ticket"]
When ~/bin/ticket prints {"text": "T-123", "style": "accent"},
the item shows T-123 in the accent role. When it exits non-zero, the last
ticket stays and draws dim.