Skip to content

Quick start

Install

tui-builder needs Go 1.25 or later.

go install github.com/jsdrews/tui-builder/cmd/tui-builder@latest
go install github.com/jsdrews/tui-builder/cmd/wrangl@latest

Or build from a clone, which also gives you the examples:

git clone https://github.com/jsdrews/tui-builder.git
cd tui-builder
task build        # → bin/tui-builder, bin/wrangl, bin/example-launcher
task examples     # browse and run every example

A first config

Save this as hello.yaml:

app:
  title: Cities
  theme: nord

tui:
  components:
    cities:
      type: table
      filterable: true
      columns:
        - {title: City,   sortable: true}
        - {title: Region, sortable: true}
        - {title: Pop,    sortable: true, sort: si, align: right}
      rows:
        - [London,    Europe, "9M"]
        - [Tokyo,     Asia,   "37M"]
        - [Reykjavík, Europe, "130K"]
  screen:
    layout:
      component: cities

Run it:

tui-builder hello.yaml

Try / to filter (region:europe scopes to a column), ] to sort, ? for every key, and q to quit. The mouse works too: click to focus and select, scroll with the wheel.

Live data

Replace the static rows with a source. Components bind to sources by name, and each column plucks its value from the item with a dot-path. Save this as users.yaml:

app:
  title: Users

data:
  sources:
    users:
      type: http
      url: https://jsonplaceholder.typicode.com/users
      refresh: 1m

tui:
  components:
    users:
      type: table
      source: users
      filterable: true
      columns:
        - {title: Name,    value: name,         sortable: true}
        - {title: City,    value: address.city, sortable: true}
        - {title: Company, value: company.name}
        - {title: Website, value: website}
  screen:
    layout:
      component: users

refresh: 1m polls; cursor, filter and sort survive each refresh, and r refetches now.

The same config works without the TUI. wrangl runs only the data: block:

wrangl --list users.yaml                         # every source in the config
wrangl users.yaml users | jq '.[0].address'      # one source's data

Reshape it

Add a pipeline entry that reads users and keeps only the .biz websites, then point the table's source: at it:

data:
  sources:
    users: {type: http, url: "https://jsonplaceholder.typicode.com/users"}
    biz_users:
      type: filter
      from: users
      where: "hasSuffix(website, '.biz')"

Add an action

Actions are the write side. Declare what to run and what it needs, then bind it on a screen and say which row feeds it:

actions:
  open_site:
    run: [open, "https://${inputs.site}"]   # xdg-open on Linux
    inputs:
      site: {required: true}

tui:
  screen:
    layout:
      component: users
    actions:
      - key: enter
        action: open_site
        from: users
        bind: {site: "${selection.Website}"}

enter on a row runs it; a opens the action menu listing every action on the screen. Output and results land in the output console (o).

Next

  • The Guide walks through each part of a config.
  • The Reference lists every field.
  • Examples has a runnable config for each feature.