D.A.B.Tv0.0.17 Tutorial API DABT Tools ↗ GitHub ↗

Core: lifecycle, layout & runtime

The always-loaded core (lib/tui.sh, lib/tui_api.sh, lib/state.sh): starting the app, building the pane tree, tabs, runtime (factory) widgets, output, background process streaming, live updates, getters, logging and the event/result variables everything else reads. ← API index for the full module list and a task-oriented tour with examples.

Each function has its own page; the summary tables link to them, and the full entries follow each table. Functions return 0 unless their entry says otherwise. Getters print to stdout, so call them in $(...); the fork-free alternatives that set variables are noted on each entry.

Lifecycle

tui.start is the usual entry point. tui.init + tui.run are the manual form for apps that build their UI in code.

Function Summary
tui.start Runs a markup-driven app: validates the page, initializes the terminal, loads FILE and runs the main loop until the app quits.
tui.start_cached Like tui.start, but first records every page the app can reach into the page cache, so later page switches replay instead of parsing.
tui.init Takes over the terminal and prepares an empty root pane.
tui.run Runs the main loop (input, dispatch, render, ticks) until tui.stop is called, then restores the terminal.
tui.stop Asks the main loop to exit after the current iteration.
tui.cleanup Restores the terminal: mouse tracking and bracketed paste off, style reset, cursor shown, main screen, unread input drained, original stty settings back.
tui.require Sources an optional bundled library once per process.
tui.tick.add Registers FN to run once per main-loop iteration.
tui.tick.remove Unregisters a tick listener. Unknown names are ignored.
tui.on_resize Marks the terminal as resized; the main loop re-lays out on its next iteration.

tui.start

tui.start FILE

Runs a markup-driven app: validates the page, initializes the terminal, loads FILE and runs the main loop until the app quits.

Parameters

  • FILE: the first page (XML).

Returns: 1 when FILE is unreadable, fails validation or fails to load; otherwise returns after the app quits.

Notes

  • Validates FILE and its includes before the terminal is taken over. Errors are printed to stderr and abort the start, unless TUI_IGNORE_INVALID_XML=1 (set by dabt --ignore-invalid-xml). TUI_VALIDATE=0 skips validation.
  • Sets TUI_THEMES_DIR to the page’s themes/ folder (else the defaults’ themes/) when it is not already set.
  • The terminal is restored on every exit path: normal quit, INT/TERM, or a load failure.
  • Uses the page cache for later tui.goto calls but does not pre-warm it; see tui.start_cached.

Example

#!/usr/bin/env bash
TUI_APP_NAME=my_app                 # before sourcing: names the config folder and log file
source /path/to/lib/tui.sh
tui.start "$APP/config/home.xml"

See also: tui.start_cached, tui.init, tui.run

tui.start_cached

tui.start_cached FIRST_PAGE

Like tui.start, but first records every page the app can reach into the page cache, so later page switches replay instead of parsing.

Parameters

  • FIRST_PAGE: the page to open. Every *.xml next to it and every shipped default page (share/defaults/pages/) is warmed too.

Returns: as tui.start.

Notes

  • Files whose name starts with _ (fragments for <include>) are skipped.
  • All warmed pages are validated before the terminal is taken over, not only FIRST_PAGE.
  • Only pages that are missing from the on-disk cache or changed since are re-recorded, behind a progress banner. The cache lives in tui.cache.disk_dir.

See also: tui.cache.warm_with_spinner, tui.load_cached

tui.init

tui.init

Takes over the terminal and prepares an empty root pane.

Notes

  • Applies the saved config, switches to raw input (stty -echo -icanon -isig -ixon -iexten), enters the alternate screen, hides the cursor, and turns on mouse tracking and bracketed paste.
  • Because of -isig, ctrl+c and ctrl+z arrive as keys instead of signals.
  • Records the run in app.meta, starts enabled plugins and fires the init hook.
  • tui.start calls it. Call it yourself only when building the UI in code; pair it with tui.run.

See also: tui.cleanup

tui.run

tui.run

Runs the main loop (input, dispatch, render, ticks) until tui.stop is called, then restores the terminal.

Notes

  • Fires the ready hook once, before the first frame.
  • Installs INT/TERM traps (clean up and exit 1) and a WINCH trap for resizes.
  • Polls input every 0.05 s while tick listeners exist, else every 0.2 s (TUI_INPUT_POLL_TIMEOUT, TUI_INPUT_IDLE_TIMEOUT).
  • Restores the terminal before it returns, so code after tui.run prints to the normal screen.

Example

source lib/tui.sh
tui.init
tui.vsplit root top:1 bottom:3
tui.output bottom "hello"
tui.run

See also: tui.start

tui.stop

tui.stop

Asks the main loop to exit after the current iteration.

Notes

  • Fires the quit hook first.
  • Never asks for confirmation. Bind tui.action.quit instead to respect the confirm.quit setting.

See also: tui.run

tui.cleanup

tui.cleanup

Restores the terminal: mouse tracking and bracketed paste off, style reset, cursor shown, main screen, unread input drained, original stty settings back.

Notes

  • tui.run and the signal traps already call it. Call it yourself only when you called tui.init without tui.run.

See also: tui.init

tui.require

tui.require LIBRARY

Sources an optional bundled library once per process.

Parameters

  • LIBRARY: terminal_renderer, terminal_controls or tui_scan.

Returns: 1 and a message on stderr for an unknown name.

Notes

  • Callback files are re-sourced on every page visit. Use tui.require there instead of source so a large library is loaded only once.

Example

tui.require terminal_renderer
tui.output stats "$(table_string 'Name|Size' 'a.txt|1k')"

tui.tick.add

tui.tick.add FN

Registers FN to run once per main-loop iteration.

Parameters

  • FN: function name, called with no arguments.

Notes

  • Adding the same FN twice is a no-op.
  • Listeners survive page changes. Remove page-specific listeners with tui.tick.remove, or use tui.every, which is cleared on page change.
  • While any listener exists the loop polls input every 0.05 s instead of 0.2 s, so an idle app stays cheap only when nothing is registered.
  • Never assign _TUI_TICK_FN for this: it is a single slot, and overwriting it stops other tick users such as tui.exec.
  • A listener registered while a plugin loads is removed when that plugin is disabled.

See also: tui.every

tui.tick.remove

tui.tick.remove FN

Unregisters a tick listener. Unknown names are ignored.

Parameters

tui.on_resize

tui.on_resize

Marks the terminal as resized; the main loop re-lays out on its next iteration.

Notes

  • Installed as the WINCH handler by tui.run. You rarely call it yourself.
  • A drag-resize is coalesced: the loop waits until no resize arrived for about 40 ms, then lays out once and fires the resize hook.

Layout (panes)

Panes form a tree rooted at root. Splits divide a pane among children by integer weight; leaves hold widgets or output. Pane names are used in bash variable names, so use letters, digits and _ only.

Function Summary
tui.hsplit Splits PARENT into side-by-side child panes.
tui.vsplit Splits PARENT into stacked child panes, top to bottom.
tui.grid Lays NAME... out as a grid of cells inside PARENT, row by row.
tui.fixed Lays children out as fixed-size cells that flow left to right and wrap, like keys on a keyboard.
tui.pane_title Sets the title drawn in the pane’s top border.
tui.pane_border Sets the border style of a pane.
tui.pane_pad Sets blank columns (HPAD) and rows (VPAD) on each side inside the pane.
tui.pane_align Sets the default horizontal alignment for widgets in the pane.
tui.pane_valign Sets the default vertical anchor for widgets in the pane. Default: top.
tui.pane_minsize Sets the smallest size at which the pane shows its content. Below it, the pane shows a min space = WxH notice instead.
tui.pane_maxsize Caps the size a split gives the pane.
tui.pane_scroll Enables scrolling of the pane’s output and resets its scroll position to the top left.
tui.pane_strict_fit Turns the automatic content-fit check of a leaf pane on (default) or off.
tui.pane_size Stores the usable content size of a pane (inside border and padding) in variables, without a subshell.
tui.content_area Prints the content rectangle of a pane as ROW COL HEIGHT WIDTH (1-based, inside border and padding).
tui.pane.focus Makes PANE the keyboard pane: the target of scroll keys, drawn with a highlighted border.
tui.relayout Recomputes geometry and repaints in one synchronized frame. Use it after changing borders, padding, titles or splits while the app runs.
tui.clear_pane Blanks the pane’s content rectangle on screen.
tui.render Repaints every pane, widget and output now, as one buffered frame.
tui.redraw Same as tui.render.

tui.hsplit

tui.hsplit PARENT NAME[:WEIGHT]...

Splits PARENT into side-by-side child panes.

Parameters

  • PARENT: an existing pane (root at first).
  • NAME[:WEIGHT]: one per child, left to right. WEIGHT is a positive integer share of the width. Default: 1.

Notes

  • Pane names become part of bash variable names: use letters, digits and _ only.
  • Each child starts with a single border and no title. The last child absorbs rounding leftovers.

Example

tui.hsplit root nav main:3      # nav gets 1/4 of the width, main 3/4

See also: tui.vsplit, tui.grid, tui.fixed

tui.vsplit

tui.vsplit PARENT NAME[:WEIGHT]...

Splits PARENT into stacked child panes, top to bottom.

Parameters

  • PARENT: an existing pane.
  • NAME[:WEIGHT]: one per child. WEIGHT is a positive integer share of the height. Default: 1.

Notes

  • Same rules as tui.hsplit: identifier-safe names, single border by default.

Example

tui.vsplit main header:1 body:5 status:1

See also: tui.hsplit

tui.grid

tui.grid PARENT ROWS COLS FIT ROW_WEIGHTS COL_WEIGHTS NAME...

Lays NAME... out as a grid of cells inside PARENT, row by row.

Parameters

  • ROWS, COLS: grid size. Either may be "" and is then computed from the number of names; both empty gives a near-square grid.
  • FIT: what happens to cells with no name. pack (default when "") keeps them as empty panes; stretch lets the row’s other cells absorb them.
  • ROW_WEIGHTS, COL_WEIGHTS: space-separated weights, e.g. "1 2". "" = all 1.
  • NAME...: cell pane names, row-major.

Notes

  • All six leading parameters are positional; pass "" for the ones you skip.
  • Built from a tui.vsplit into rows named PARENT_row0, PARENT_row1, … and an tui.hsplit per row. Blank pack cells are named PARENT_rowR_cC_blank.
  • tui.get.split on PARENT prints v.

Example

tui.grid dash 2 3 pack "" "2 1 1" cpu mem disk net io load

See also: tui.factory.grid, tui.fixed

tui.fixed

tui.fixed PARENT CELL_W CELL_H CHILD[:SPAN[:nl]]...

Lays children out as fixed-size cells that flow left to right and wrap, like keys on a keyboard.

Parameters

  • CELL_W, CELL_H: size of one cell in columns and rows.
  • CHILD: pane name. SPAN makes it that many cells wide (default 1). A third field (any text, e.g. nl) starts a new row before this child.

Notes

  • Sizes never stretch with the window. Children that don’t fit get a 0×0 rectangle and are not drawn.
  • Children start with no border.
  • tui.get.split on PARENT prints f.

Example

tui.fixed keys 6 3 esc f1 f2 f3 tab:2:nl q w e

See also: tui.grid

tui.pane_title

tui.pane_title PANE TEXT

Sets the title drawn in the pane’s top border.

Notes

  • Not drawn when the pane has no border.
  • While the app runs, call tui.relayout PANE to show the change.

tui.pane_border

tui.pane_border PANE STYLE

Sets the border style of a pane.

Parameters

  • STYLE: single, double, heavy or none. Any other value draws single.

Notes

  • A parent pane draws its border only when it was set with this function and there is room for it; leaf panes draw a single border by default.
  • A border that doesn’t fit (leaf under 3×5 cells) is dropped automatically.
  • While the app runs, call tui.relayout to apply it; the border changes the content area.

See also: tui.get.border

tui.pane_pad

tui.pane_pad PANE HPAD VPAD

Sets blank columns (HPAD) and rows (VPAD) on each side inside the pane.

Parameters

  • HPAD, VPAD: cell counts. "" leaves that value unchanged.

Notes

  • On a parent pane: the gap between its frame and its children. On a leaf: shrinks the area for widgets and output.
  • Padding is clamped so at least one row and column remain.
  • While the app runs, call tui.relayout to apply it.

See also: tui.get.pad, tui.pad

tui.pane_align

tui.pane_align PANE left|center|right|fill

Sets the default horizontal alignment for widgets in the pane.

Notes

  • A widget’s own tui.align wins. Without either, widgets are left-aligned and buttons centered.
  • Affects widgets only, not text from tui.output.

tui.pane_valign

tui.pane_valign PANE top|middle|bottom

Sets the default vertical anchor for widgets in the pane. Default: top.

Notes

  • A widget’s ROW counts from the anchor: with bottom, row 0 is the last line and higher rows go up.
  • A widget’s own tui.valign wins.

tui.pane_minsize

tui.pane_minsize PANE [MIN_W] [MIN_H]

Sets the smallest size at which the pane shows its content. Below it, the pane shows a min space = WxH notice instead.

Parameters

  • MIN_W, MIN_H: columns and rows. Empty leaves that value unchanged.

Notes

  • Leaf panes also get an automatic minimum from their content unless tui.pane_strict_fit is false.

See also: tui.pane_maxsize

tui.pane_maxsize

tui.pane_maxsize PANE [MAX_W] [MAX_H]

Caps the size a split gives the pane.

Parameters

  • MAX_W, MAX_H: columns and rows. Empty leaves that value unchanged.

Notes

  • Space the cap takes away is passed to the pane’s last sibling. When the capped pane is itself the last child, that space stays empty.

See also: tui.pane_minsize

tui.pane_scroll

tui.pane_scroll PANE none|v|h|both

Enables scrolling of the pane’s output and resets its scroll position to the top left.

Notes

  • Scrolling applies to tui.output content: wheel, j/k, page keys and a draggable scrollbar.
  • Calling it again, even with the same mode, jumps back to the top.

See also: tui.get.scroll, tui.action.scroll

tui.pane_strict_fit

tui.pane_strict_fit PANE true|false

Turns the automatic content-fit check of a leaf pane on (default) or off.

Notes

  • With the check on, a pane whose widgets need more room than it has shows the min space notice. false falls back to the explicit tui.pane_minsize only; use it for panes where clipping is fine (tab headers do this).

tui.pane_size

tui.pane_size PANE

Stores the usable content size of a pane (inside border and padding) in variables, without a subshell.

Returns: 1 for an unknown pane.

Sets: TUI_PANE_ROWS, TUI_PANE_COLS.

Example

tui.pane_size status
printf -v rule '%*s' "$TUI_PANE_COLS" ''
tui.set_text status "${rule// /─}"      # a rule exactly as wide as the pane

See also: tui.get.dimensions

tui.content_area

tui.content_area PANE

Prints the content rectangle of a pane as ROW COL HEIGHT WIDTH (1-based, inside border and padding).

Notes

  • Does not check that PANE exists. Prefer tui.get.content_area, which returns 1 for an unknown pane.

tui.pane.focus

tui.pane.focus PANE

Makes PANE the keyboard pane: the target of scroll keys, drawn with a highlighted border.

Parameters

  • PANE: pane id, or "" to clear.

Notes

  • Focusing a widget also moves pane focus to the widget’s pane.
  • Only the two affected borders are redrawn.

See also: tui.get.pane_focus, tui.action.focus_pane

tui.relayout

tui.relayout [PANE]

Recomputes geometry and repaints in one synchronized frame. Use it after changing borders, padding, titles or splits while the app runs.

Parameters

  • PANE: a leaf pane to repaint alone, without clearing the screen. Omitted (or a parent pane): lay out and repaint everything.

Notes

  • Before tui.run it only recomputes geometry.

Example

tui.pane_border side double
tui.relayout

tui.clear_pane

tui.clear_pane PANE

Blanks the pane’s content rectangle on screen.

Notes

  • Writes to the terminal directly and does not change the pane’s content; the next repaint draws it again. Use tui.output_clear to empty the content.

tui.render

tui.render

Repaints every pane, widget and output now, as one buffered frame.

Notes

  • Expensive. Content functions (tui.output, tui.set_text, tui.update) already queue their own repaint, coalesced per frame; don’t call tui.render after them or from timers.
  • Does not recompute geometry; use tui.relayout after layout changes.

See also: tui.redraw

tui.redraw

tui.redraw

Same as tui.render.

Tabs

A tab group is a header pane with one button per tab and a content pane that the active tab’s action fills. In markup: <tabs>, see ../guide/grid-layouts-and-tabs.md.

Function Summary
tui.tabs.add Registers one tab before the group is built.
tui.tabs.compact Picks the header style of a tab group. Call it before tui.tabs.build.
tui.tabs.build Builds the header row for registered tabs and activates the default tab (or the first).
tui.tabs.activate Makes a tab active: focuses its header button and calls its action as ACTION TAB_ID.

tui.tabs.add

tui.tabs.add TAB_ID TEXT ACTION [DEFAULT]

Registers one tab before the group is built.

Parameters

  • TAB_ID: widget id of the tab’s header button.
  • TEXT: header caption.
  • ACTION: function called as ACTION TAB_ID whenever the tab is activated. It fills the content pane.
  • DEFAULT: true to activate this tab when the group is built. Any other value counts as not default.

See also: tui.tabs.build

tui.tabs.compact

tui.tabs.compact TABS_ID [true|false]

Picks the header style of a tab group. Call it before tui.tabs.build.

Parameters

  • TABS_ID: the group id passed to tui.tabs.build.
  • true (default): one-row, borderless headers styled with .tab_header_compact. false: framed headers (.tab_header), which need 3 rows.

tui.tabs.build

tui.tabs.build TABS_ID HEADER_PANE CONTENT_PANE TAB_ID...

Builds the header row for registered tabs and activates the default tab (or the first).

Parameters

  • TABS_ID: a name for the group.
  • HEADER_PANE: leaf pane that receives a one-row grid of header buttons.
  • CONTENT_PANE: the pane the tab actions fill.
  • TAB_ID...: tabs registered with tui.tabs.add, in display order.

Notes

  • Header cells are named HEADER_PANE_TAB_ID_cell and skip the content-fit check, so long captions clip instead of showing the size notice.
  • The active tab is shown with the header button’s focus style.
  • The framework does not clear CONTENT_PANE between tabs; each action replaces what it needs.

Example

tui.tabs.add tab_log  "Log"   show_log true
tui.tabs.add tab_conf "Config" show_conf
tui.tabs.compact main_tabs
tui.tabs.build main_tabs tab_bar tab_body tab_log tab_conf

See also: tui.tabs.activate

tui.tabs.activate

tui.tabs.activate TAB_ID

Makes a tab active: focuses its header button and calls its action as ACTION TAB_ID.

Notes

  • Does nothing for a tab that is not part of a built group.

Factory (runtime widgets)

Constructors for layouts whose shape is only known at runtime. Ids are generated and tracked per namespace, so one call tears everything down again.

Function Summary
tui.factory.label Creates a label with a generated id, tracked under namespace NS.
tui.factory.button Creates a button with a generated id, tracked under namespace NS. ACTION is called as ACTION ID.
tui.factory.input Creates a single-line input with a generated id, tracked under namespace NS.
tui.factory.checkbox Creates a checkbox with a generated id, tracked under namespace NS.
tui.factory.grid Builds a grid of COUNT cells with generated pane ids, for layouts whose size is only known at runtime.
tui.factory.clear Removes every widget and pane created under namespace NS and turns its grid parents back into empty leaf panes.

tui.factory.label

tui.factory.label NS PANE ROW TEXT

Creates a label with a generated id, tracked under namespace NS.

Sets: _TUI_FACTORY_LAST_ID to the new id (__f_NS_N).

Notes

  • The id is not printed: stdout is the screen. Read _TUI_FACTORY_LAST_ID right after the call.
  • _TUI_FACTORY_LAST_ID and _TUI_FACTORY_GRID_CELLS are the only underscore names app code may read.

See also: tui.label, tui.factory.clear

tui.factory.button

tui.factory.button NS PANE ROW TEXT [ACTION]

Creates a button with a generated id, tracked under namespace NS. ACTION is called as ACTION ID.

Sets: _TUI_FACTORY_LAST_ID.

Example

for f in *.log; do
    tui.factory.button files list_pane "$((row++))" "$f" open_file
done

See also: tui.button

tui.factory.input

tui.factory.input NS PANE ROW [PLACEHOLDER] [LABEL] [SUBMIT]

Creates a single-line input with a generated id, tracked under namespace NS.

Sets: _TUI_FACTORY_LAST_ID.

See also: tui.input

tui.factory.checkbox

tui.factory.checkbox NS PANE ROW LABEL [CHECKED] [ACTION]

Creates a checkbox with a generated id, tracked under namespace NS.

Sets: _TUI_FACTORY_LAST_ID.

See also: tui.checkbox

tui.factory.grid

tui.factory.grid NS PARENT COUNT [COLS] [FIT] [ROW_WEIGHTS] [COL_WEIGHTS]

Builds a grid of COUNT cells with generated pane ids, for layouts whose size is only known at runtime.

Parameters

  • COUNT: number of cells.
  • COLS: columns. Omitted: near-square.
  • FIT, ROW_WEIGHTS, COL_WEIGHTS: as in tui.grid.

Sets: _TUI_FACTORY_GRID_CELLS (array of cell ids, in order).

Example

tui.factory.clear tiles
tui.factory.grid tiles board "${#items[@]}" 4
for i in "${!items[@]}"; do
    tui.factory.button tiles "${_TUI_FACTORY_GRID_CELLS[$i]}" 0 "${items[$i]}" pick
done
tui.relayout

See also: tui.factory.clear

tui.factory.clear

tui.factory.clear NS

Removes every widget and pane created under namespace NS and turns its grid parents back into empty leaf panes.

Notes

  • Safe on a namespace that is empty or already cleared.
  • Does not repaint. Call tui.relayout after rebuilding.
  • Everything is also dropped on page change.

Content and output

Text content of leaf panes. Repaints are queued and coalesced, so several updates in one callback cost one frame.

Function Summary
tui.output Replaces the pane’s content with text, or with stdin when no TEXT is given.
tui.output_append Appends lines to the pane’s content, from arguments or stdin.
tui.output_clear Empties the pane’s content and repaints the pane immediately.
tui.set_text Replaces the pane’s content like tui.output, but skips the work when TEXT equals what it set last time.

tui.output

tui.output PANE [TEXT...]
some_command | tui.output PANE

Replaces the pane’s content with text, or with stdin when no TEXT is given.

Parameters

  • TEXT...: joined with spaces, then split on newlines. ANSI escapes are kept.

Notes

  • The repaint is queued and coalesced with other changes in the same frame.
  • PANE must be a valid bash identifier (letters, digits, _): the content is stored in a variable named after it.
  • Reading stdin (cmd | tui.output) costs a pipeline fork; for text updated in a loop use tui.set_text.
  • Don’t mix tui.output and tui.set_text on the same pane: tui.set_text skips text it thinks is already shown.

Example

tui.output log "line one"
tui.output log "$(ls -la)"
df -h | tui.output disk

See also: tui.output_append, tui.pane_scroll

tui.output_append

tui.output_append PANE [TEXT...]
some_command | tui.output_append PANE

Appends lines to the pane’s content, from arguments or stdin.

Notes

  • Content is kept in memory without a limit. For a long-running log, trim it yourself or use tui.exec.

See also: tui.output

tui.output_clear

tui.output_clear PANE

Empties the pane’s content and repaints the pane immediately.

tui.set_text

tui.set_text PANE TEXT

Replaces the pane’s content like tui.output, but skips the work when TEXT equals what it set last time.

Notes

  • Fork-free. Use it for anything updated from timers or tick functions.
  • The cache is per pane and cleared on page change. After changing the pane through another function, the next tui.set_text with the old text is skipped; don’t mix them on one pane.

Example

refresh() { tui.sys.cpu; tui.set_text cpu "CPU ${TUI_SYS_CPU}%"; }
tui.every 1 refresh

Process streaming (tui.exec)

Function Summary
tui.exec Runs a shell command in a pseudo-terminal and streams its output into a pane while the UI stays responsive.
tui.exec.cancel_pane Kills and dismisses every tui.exec instance targeting PANE, running or finished, and empties the pane.

tui.exec

tui.exec CMD OUT_PANE [CTL_PANE]

Runs a shell command in a pseudo-terminal and streams its output into a pane while the UI stays responsive.

Parameters

  • CMD: command line, run by script -q -e -c CMD.
  • OUT_PANE: pane that shows the output.
  • CTL_PANE: optional pane that gets Cancel, Save Output, View Command, Retry and Back buttons plus an input box that sends a line to the process’s stdin.

Returns: 1 when CMD or OUT_PANE is empty.

Notes

  • Several instances may run at once, also into the same pane. Each gets an instance id (e1, e2, …); later instances in a shared pane print a ── [eN] started ── separator.
  • Needs the util-linux script. BSD/macOS script takes different options.
  • Running instances are killed on page change and on exit.
  • Save Output writes exec_output_<id>_<timestamp>.log into the current directory.
  • To restart cleanly in a pane (one process at a time), call tui.exec.cancel_pane first.

Example

tui.exec "ping -c 5 example.com" out ctl

tui.exec.cancel_pane

tui.exec.cancel_pane PANE

Kills and dismisses every tui.exec instance targeting PANE, running or finished, and empties the pane.

Example

on_visit_shell() {
    tui.exec.cancel_pane term      # one shell per visit, not one more per visit
    tui.exec "bash -i" term
}

Live updates

Timers, clocks, watches and the system samplers share one tick listener, cost no fork per update, and are cleared on page change and exit.

Function Summary
tui.every Calls FN repeatedly on a timer from the main loop.
tui.after Calls FN ID once, SEC seconds from now.
tui.every.cancel Removes a timer created by tui.every, tui.after, tui.clock or tui.monitor. Unknown ids are ignored.
tui.every.pause Stops a timer from firing until tui.every.resume is called.
tui.every.resume Resumes a paused timer. It fires on the next loop iteration, then at its normal interval.
tui.every.clear Removes every timer, including clocks and monitors. Watches from tui.watch keep running.
tui.every.list Prints one line per timer.
tui.clock Shows a live clock in a pane, updated on every whole second.
tui.watch Runs a shell command every SEC seconds in the background and shows its latest output in a pane, like watch.
tui.watch.stop Stops a watch and kills its background process. Unknown ids are ignored.
tui.watch.now Restarts a watch so its command runs again right away.
tui.monitor Shows a ready-made system dashboard in a pane: CPU, memory and swap gauges, a CPU history sparkline, memory in MB, load average and uptime.
tui.sys.cpu Samples CPU usage since the previous call, fork-free.
tui.sys.mem Samples memory and swap usage from /proc/meminfo, fork-free.
tui.sys.load Reads the load averages from /proc/loadavg.
tui.sys.uptime Reads the system uptime from /proc/uptime.
tui.hist.push Appends a value to a named rolling series, dropping the oldest beyond MAX.
tui.hist.get Prints a series, oldest first.

tui.every

tui.every SEC FN [ID]

Calls FN repeatedly on a timer from the main loop.

Parameters

  • SEC: interval in seconds; decimals allowed (0.25). Values below 0.05 are raised to 0.05.
  • FN: function, called as FN ID.
  • ID: timer id for tui.every.cancel and friends. Default: FN.

Notes

  • The first call happens on the next loop iteration, not after SEC. Use tui.after for a delayed start.
  • Registering an existing ID again replaces that timer and resumes it if paused. Pass distinct IDs to run one function on two timers.
  • Timers are removed on page change. Register them again from the page’s on_visit.
  • After a terminal resize every timer runs on the next iteration, so size-dependent output re-fits at once.
  • FN runs inside the main loop: keep it fork-free (tui.set_text, tui.sys.*). Slow work belongs in tui.watch.
  • Timers registered while a plugin loads are removed when that plugin is disabled.

Example

refresh_stats() {
    tui.sys.cpu; tui.sys.mem
    tui.set_text stats "CPU ${TUI_SYS_CPU}%  MEM ${TUI_SYS_MEM_PCT}%"
}
tui.every 2 refresh_stats

See also: tui.after, tui.tick.add, tui.watch

tui.after

tui.after SEC FN [ID]

Calls FN ID once, SEC seconds from now.

Parameters

Notes

  • The timer is removed before FN runs, so FN may schedule itself again with the same ID.
  • Cancelled by page change like every timer.

Example

tui.notify "Saved" success
tui.after 3 clear_status

tui.every.cancel

tui.every.cancel ID

Removes a timer created by tui.every, tui.after, tui.clock or tui.monitor. Unknown ids are ignored.

tui.every.pause

tui.every.pause ID

Stops a timer from firing until tui.every.resume is called.

tui.every.resume

tui.every.resume ID

Resumes a paused timer. It fires on the next loop iteration, then at its normal interval.

tui.every.clear

tui.every.clear

Removes every timer, including clocks and monitors. Watches from tui.watch keep running.

tui.every.list

tui.every.list

Prints one line per timer.

Output: ID<TAB>INTERVALms<TAB>FN, e.g. refresh_stats 2000ms refresh_stats.

tui.clock

tui.clock PANE [FORMAT] [FONT] [ID]

Shows a live clock in a pane, updated on every whole second.

Parameters

  • FORMAT: strftime format. Default: %H:%M:%S.
  • FONT: a banner font (block5, seg3, box3, blk3, half2) for big digits, styled with the pane’s colors. Empty: plain text.
  • ID: timer id. Default: clock_PANE.

Notes

  • Stops itself when PANE no longer exists.
  • Loads terminal_renderer.sh on first use of a font.

Example

tui.clock header_clock "%a %H:%M" seg3

tui.watch

tui.watch PANE CMD [SEC] [ID]

Runs a shell command every SEC seconds in the background and shows its latest output in a pane, like watch.

Parameters

  • CMD: shell code, evaluated with eval in a background subshell.
  • SEC: interval. Default: 2. Minimum 0.05.
  • ID: watch id. Default: watch_PANE.

Returns: 1 when PANE or CMD is empty.

Notes

  • The UI never waits for CMD. Only the newest complete output is shown; stderr is discarded.
  • Starting a watch with an existing ID restarts it.
  • Stopped on page change, on exit, and when PANE no longer exists.

Example

tui.watch disk "df -h /" 5

See also: tui.watch.stop, tui.watch.now

tui.watch.stop

tui.watch.stop ID

Stops a watch and kills its background process. Unknown ids are ignored.

tui.watch.now

tui.watch.now ID

Restarts a watch so its command runs again right away.

Returns: 1 for an unknown id.

Example

on_refresh() { tui.watch.now watch_disk; }

tui.monitor

tui.monitor PANE [SEC] [ID]

Shows a ready-made system dashboard in a pane: CPU, memory and swap gauges, a CPU history sparkline, memory in MB, load average and uptime.

Parameters

  • SEC: refresh interval. Default: 1.
  • ID: timer id. Default: monitor_PANE.

Notes

  • Linux only: reads /proc.
  • Gauges size themselves to the pane width.

tui.sys.cpu

tui.sys.cpu

Samples CPU usage since the previous call, fork-free.

Sets: TUI_SYS_CPU (integer percent).

Returns: 1 when /proc/stat can’t be read.

Notes

  • The first call measures since boot. Call it once to prime, then read it on a timer.
  • Linux only.

tui.sys.mem

tui.sys.mem

Samples memory and swap usage from /proc/meminfo, fork-free.

Sets: TUI_SYS_MEM_PCT, TUI_SYS_MEM_USED_MB, TUI_SYS_MEM_TOTAL_MB, TUI_SYS_SWAP_PCT.

Notes

  • “Used” is total minus available, so file cache does not count as used. Linux only.

tui.sys.load

tui.sys.load

Reads the load averages from /proc/loadavg.

Sets: TUI_SYS_LOAD1, TUI_SYS_LOAD5, TUI_SYS_LOAD15 (decimal strings, e.g. 0.42).

tui.sys.uptime

tui.sys.uptime

Reads the system uptime from /proc/uptime.

Sets: TUI_SYS_UPTIME (seconds), TUI_SYS_UPTIME_STR (2d 03h 14m).

tui.hist.push

tui.hist.push NAME VALUE [MAX]

Appends a value to a named rolling series, dropping the oldest beyond MAX.

Parameters

  • VALUE: one word; values are stored space-separated.
  • MAX: series length. Default: 60.

Example

tui.sys.cpu; tui.hist.push cpu "$TUI_SYS_CPU" 120

See also: tui.hist.get

tui.hist.get

tui.hist.get NAME

Prints a series, oldest first.

Output: values separated by spaces, no trailing newline. Charts such as linechart expect commas: v=$(tui.hist.get cpu); linechart "cpu:${v// /,}".

Getters

Read-only. Pane getters return 1 for an unknown pane.

Function Summary
tui.get.dimensions Prints the size of the terminal or of a pane.
tui.get.terminal Prints the terminal size as ROWS COLS.
tui.get.position Prints the top-left corner of a pane as ROW COL (1-based). Returns 1 for an unknown pane.
tui.get.rect Prints the outer rectangle of a pane as ROW COL HEIGHT WIDTH. Returns 1 for an unknown pane.
tui.get.content_area Prints the usable rectangle inside border and padding as ROW COL HEIGHT WIDTH. Returns 1 for an unknown pane.
tui.get.border Prints the border actually drawn: single, double, heavy or none. Returns 1 for an unknown pane.
tui.get.title Prints the pane title (empty when none). Returns 1 for an unknown pane.
tui.get.pad Prints the pane padding as HPAD VPAD. Returns 1 for an unknown pane.
tui.get.split Prints how a pane is split: h, v, f (fixed) or empty for a leaf. Returns 1 for an unknown pane.
tui.get.children Prints the child pane ids on one line, space-separated (empty for a leaf). Returns 1 for an unknown pane.
tui.get.parent Prints the id of the pane that contains PANE. Returns 1 for root or an unknown pane.
tui.get.panes Prints every pane id, one per line; with leaves, only panes without children.
tui.get.lines Prints the number of output lines a pane holds (0 for none or an unknown pane).
tui.get.scroll Prints the scroll state as MODE V_OFFSET H_OFFSET LINES MAX_WIDTH, e.g. v 12 0 240 80. Returns 1 for an unknown pane.
tui.get.style Prints one resolved style field of a pane or widget.
tui.get.widgets Prints widget ids in draw order, one per line: all of them, or only those in PANE.
tui.get.type Prints the widget type: label, button, input, checkbox, password, textarea, list, table, select or progress. Returns 1 for an unknown id.
tui.get.pane Prints the pane a widget lives in. Returns 1 for an unknown id.
tui.get.action Prints the action function of a widget (empty when none or unknown).
tui.get.checked Prints nothing; returns 0 when ID is a checked checkbox, else 1.
tui.get.focused Prints the id of the focused widget (empty when none).
tui.get.hovered Prints the id of the widget (default) or pane under the mouse pointer (empty when none).
tui.get.pane_focus Prints the keyboard pane: the scroll target with the highlighted border (empty when none).
tui.get.page Prints the absolute path of the current page file (empty before a page is loaded).
tui.get.pages Prints the pages linked by a <button page="…"> so far, sorted, one per line as PAGE<TAB>TITLE.
tui.get.event Prints the current input event on one line: type=… key=… x=… y=… pane=… widget=… button=….
tui.get.classes Prints every key the loaded theme defines, sorted, one per line.
tui.get.class.style Prints one field of a theme class, without a pane or widget.

tui.get.dimensions

tui.get.dimensions [-r|--rows] [-c|--columns] [--content] [PANE]

Prints the size of the terminal or of a pane.

Parameters

  • -r, --rows / -c, --columns: print only that number.
  • --content: the pane’s usable area inside border and padding.
  • PANE: omitted = the terminal.

Output: ROWS COLS, or one number with -r/-c.

Returns: 1 for an unknown pane.

Example

read -r rows cols <<<"$(tui.get.dimensions --content chart)"
cols=$(tui.get.dimensions -c)          # terminal width

See also: tui.pane_size (same numbers without a subshell)

tui.get.terminal

tui.get.terminal

Prints the terminal size as ROWS COLS.

tui.get.position

tui.get.position PANE

Prints the top-left corner of a pane as ROW COL (1-based). Returns 1 for an unknown pane.

tui.get.rect

tui.get.rect PANE

Prints the outer rectangle of a pane as ROW COL HEIGHT WIDTH. Returns 1 for an unknown pane.

tui.get.content_area

tui.get.content_area PANE

Prints the usable rectangle inside border and padding as ROW COL HEIGHT WIDTH. Returns 1 for an unknown pane.

tui.get.border

tui.get.border PANE

Prints the border actually drawn: single, double, heavy or none. Returns 1 for an unknown pane.

Notes

  • This is the effective style: a border that does not fit, or an unset border on a parent pane, prints none.

tui.get.title

tui.get.title PANE

Prints the pane title (empty when none). Returns 1 for an unknown pane.

tui.get.pad

tui.get.pad PANE

Prints the pane padding as HPAD VPAD. Returns 1 for an unknown pane.

tui.get.split

tui.get.split PANE

Prints how a pane is split: h, v, f (fixed) or empty for a leaf. Returns 1 for an unknown pane.

Notes

  • A pane built with tui.grid prints v: a grid is a vertical split into rows.

tui.get.children

tui.get.children PANE

Prints the child pane ids on one line, space-separated (empty for a leaf). Returns 1 for an unknown pane.

tui.get.parent

tui.get.parent PANE

Prints the id of the pane that contains PANE. Returns 1 for root or an unknown pane.

tui.get.panes

tui.get.panes [leaves]

Prints every pane id, one per line; with leaves, only panes without children.

tui.get.lines

tui.get.lines PANE

Prints the number of output lines a pane holds (0 for none or an unknown pane).

tui.get.scroll

tui.get.scroll PANE

Prints the scroll state as MODE V_OFFSET H_OFFSET LINES MAX_WIDTH, e.g. v 12 0 240 80. Returns 1 for an unknown pane.

tui.get.style

tui.get.style ID FIELD [STATE]

Prints one resolved style field of a pane or widget.

Parameters

  • FIELD: fg, bg or mods.
  • STATE: normal (default), focus, border, title, hover, checked, unchecked. A state with no rules falls back to normal.

Returns: 2 and a message on stderr for any other FIELD.

See also: tui.style.sgr (fork-free), tui.get.class.style

tui.get.widgets

tui.get.widgets [PANE]

Prints widget ids in draw order, one per line: all of them, or only those in PANE.

tui.get.type

tui.get.type ID

Prints the widget type: label, button, input, checkbox, password, textarea, list, table, select or progress. Returns 1 for an unknown id.

tui.get.pane

tui.get.pane ID

Prints the pane a widget lives in. Returns 1 for an unknown id.

tui.get.action

tui.get.action ID

Prints the action function of a widget (empty when none or unknown).

tui.get.checked

tui.get.checked ID

Prints nothing; returns 0 when ID is a checked checkbox, else 1.

Notes

  • Use it in a condition: if tui.get.checked chk_a; then .... tui.get prints the value as 0/1.

tui.get.focused

tui.get.focused

Prints the id of the focused widget (empty when none).

tui.get.hovered

tui.get.hovered [widget|pane]

Prints the id of the widget (default) or pane under the mouse pointer (empty when none).

tui.get.pane_focus

tui.get.pane_focus

Prints the keyboard pane: the scroll target with the highlighted border (empty when none).

tui.get.page

tui.get.page

Prints the absolute path of the current page file (empty before a page is loaded).

tui.get.pages

tui.get.pages

Prints the pages linked by a <button page="…"> so far, sorted, one per line as PAGE<TAB>TITLE.

Notes

  • PAGE is the path as written in the page attribute; TITLE is the button caption.
  • The command palette uses this list to offer page commands.

tui.get.event

tui.get.event

Prints the current input event on one line: type=… key=… x=… y=… pane=… widget=… button=….

Notes

  • Meant for debugging. In handlers read the TUI_EVENT_* variables directly (see the module intro).

tui.get.classes

tui.get.classes

Prints every key the loaded theme defines, sorted, one per line.

Notes

  • Pseudo-states appear as CLASS_focus, CLASS_hover, …
  • Fork-free alternative: tui.class.names.

tui.get.class.style

tui.get.class.style CLASS FIELD [STATE]

Prints one field of a theme class, without a pane or widget.

Parameters

  • CLASS: class name without the dot.
  • FIELD: fg, bg or mods.
  • STATE: normal (default) or a pseudo-state such as focus; falls back to the plain class.

Returns: 2 for an invalid FIELD.

Example

accent=$(tui.get.class.style brand fg)

See also: tui.class.style (fork-free)

Logging and perf

Function Summary
tui.log Appends a line to the app’s log file. stdout is the screen, so logs go to a file.
tui.log.debug Same as tui.log MSG debug. See tui.log.
tui.log.info Same as tui.log MSG info. See tui.log.
tui.log.warn Same as tui.log MSG warn. See tui.log.
tui.log.error Same as tui.log MSG error. See tui.log.
tui.log.file Prints the path tui.log writes to today.
tui.perf.mean_render_ms Prints the mean frame render time in milliseconds over the last SECONDS.

tui.log

tui.log MSG [LEVEL]

Appends a line to the app’s log file. stdout is the screen, so logs go to a file.

Parameters

  • MSG: the message.
  • LEVEL: free text shown in brackets. Default: info.

Output: nothing on screen. The file gets [HH:MM:SS] [LEVEL] MSG.

Notes

  • The file is ~/.config/DABT/apps/<TUI_APP_NAME>/logs/<yyyy-mm-dd>_<TUI_APP_NAME>.log (dabt when no app name is set). Set TUI_LOG_DIR before sourcing tui.sh to use another folder. tui.log.file prints the current path.
  • One file per day; an app running past midnight continues in the next day’s file.
  • Fork-free once the folder exists. A log folder that can’t be created or written is ignored silently; logging never fails the caller.
  • Logs are never rotated or deleted.

Example

tui.log "export started: $file"
tui.log.warn "config missing, using defaults"

See also: tui.log.debug, tui.log.info, tui.log.warn, tui.log.error

tui.log.debug

tui.log.debug MSG

Same as tui.log MSG debug. See tui.log.

tui.log.info

tui.log.info MSG

Same as tui.log MSG info. See tui.log.

tui.log.warn

tui.log.warn MSG

Same as tui.log MSG warn. See tui.log.

tui.log.error

tui.log.error MSG

Same as tui.log MSG error. See tui.log.

tui.log.file

tui.log.file

Prints the path tui.log writes to today.

Output: e.g. /home/me/.config/DABT/apps/my_app/logs/2026-09-24_my_app.log.

Notes

  • The file may not exist yet: it is created by the first log line.

Example

tui.view "Log" "$(tail -n 200 "$(tui.log.file)" 2>/dev/null)"

tui.perf.mean_render_ms

tui.perf.mean_render_ms SECONDS

Prints the mean frame render time in milliseconds over the last SECONDS.

Output: an integer, or nothing when tracking is off or no frame fell in the window.

Notes

  • Tracking is off by default. Set _TUI_PERF_TRACKING=1 to turn it on (used by the debug page and tools/debug/).

Event and result variables

Variable Set by Meaning
TUI_EVENT_TYPE dispatcher key, mouse (wheel and drag included), paste.
TUI_EVENT_KEY dispatcher Normalised key name (ctrl+q, wheel:down).
TUI_EVENT_X, TUI_EVENT_Y mouse Pointer column / row (1-based).
TUI_EVENT_PANE, TUI_EVENT_WIDGET dispatcher Pane / widget under the pointer or focused.
TUI_EVENT_BUTTON, TUI_EVENT_RAWBTN mouse Button name / raw SGR button code.
TUI_EVENT_COUNT dispatcher Number of identical scroll events merged into this dispatch.
TUI_EVENT_PASTE paste Bracketed-paste text.
TUI_PANE_ROWS, TUI_PANE_COLS tui.pane_size Pane content size.
TUI_SGR, TUI_FG, TUI_BG, TUI_MODS tui.class.sgr, tui.style.sgr Resolved style.
TUI_CLASSES tui.class.names Sorted theme keys.
TUI_SYS_* tui.sys.* System samples.
_TUI_FACTORY_LAST_ID, _TUI_FACTORY_GRID_CELLS tui.factory.* Last generated ids (public by documented exception).

Settings read when tui.sh is sourced: TUI_APP_NAME (config folder and log name, default dabt), TUI_HOME, TUI_LOG_DIR, TUI_CONFIG_FILE, TUI_VALIDATE=0 (skip page validation), TUI_IGNORE_INVALID_XML=1 (start despite validation errors).