You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
22 KiB
546 lines
22 KiB
# `tui`
|
|
|
|
The `tui` global is for simple terminal interactivity: asking the user
|
|
questions (**prompts**), producing colored, styled output (**`tui.styled`**),
|
|
and printing ready-made **message blocks** (`tui.success`, `tui.outlineError`,
|
|
…). Prompts render an interactive one-line UI on the terminal — a yes/no
|
|
question, a text field, a filterable option list, a calendar — and return the
|
|
answer as a plain Lua value. It is always available as the global `tui`.
|
|
|
|
```lua
|
|
local name = tui.promptText("Your name?")
|
|
local lang = tui.promptSelect("Favorite language?", "lua", { options = { "lua", "rust", "c" } })
|
|
if tui.confirm("Save results?", true) then
|
|
print(tui.styled("<info>saved</info> for <options=bold>" .. tui.escape(name) .. "</>"))
|
|
end
|
|
```
|
|
|
|
## Prompts
|
|
|
|
All prompts share one shape:
|
|
|
|
```lua
|
|
tui.promptXxx(text, default, opts)
|
|
```
|
|
|
|
- `text` — the question shown to the user (required string).
|
|
- `default` — the prompt's default value; `nil` means none. (Exceptions:
|
|
`promptSelect` matches the default against the options *by value*;
|
|
`promptSecret` has no default parameter at all.)
|
|
- `opts` — an optional table of extended options, listed per prompt below.
|
|
Every prompt accepts `help` (a hint line shown below the prompt). Unknown
|
|
keys are an error, so typos fail loudly.
|
|
|
|
Shared behavior:
|
|
|
|
- **Esc returns `nil`.** Declining a prompt is a normal, checkable outcome —
|
|
this holds for `tui.confirm` too, since `false` ("answered no") is distinct
|
|
from `nil` ("didn't answer").
|
|
- **Ctrl-C raises an error** (`tui.<fn>: interrupted`), stopping the script
|
|
unless trapped with `pcall`/`utils.try`.
|
|
- **A terminal is required.** Prompting with stdin/stdout redirected raises
|
|
`tui.<fn>: not a terminal`.
|
|
- **Prompts are async.** Like `os.sleep` or `http`, a prompt suspends only the
|
|
calling coroutine (see [Concurrency](concurrency.md)). Prompts from
|
|
concurrent `task.join` branches are serialized — one owns the terminal at a
|
|
time — but `print`/`log` output from sibling coroutines can still interleave
|
|
visually with a live prompt.
|
|
- **Validation options re-prompt inline.** Constraint options like `min`,
|
|
`maxLen` or `minSelected` are enforced by the prompt UI itself (an inline
|
|
message, then the user tries again) — they do not raise Lua errors.
|
|
Malformed *arguments* (a default that is not among the options, `min > max`,
|
|
a bad date string) raise immediately, before the terminal is touched.
|
|
|
|
### `tui.confirm(text, default, opts)` → `boolean | nil`
|
|
|
|
Yes/no question, answered with `y`/`n`.
|
|
|
|
```lua
|
|
local ok = tui.confirm("Deploy to production?", false, { help = "y/N" })
|
|
if ok == nil then return end -- Esc: user backed out
|
|
```
|
|
|
|
- `default` — boolean returned when the user just presses Enter; with `nil`
|
|
the user must type an answer.
|
|
- opts: `help`, `placeholder`.
|
|
|
|
### `tui.promptText(text, default, opts)` → `string | nil`
|
|
|
|
One-line text input.
|
|
|
|
```lua
|
|
local host = tui.promptText("Host?", "localhost", {
|
|
placeholder = "hostname or IP",
|
|
suggestions = { "localhost", "db.internal", "cache.internal" },
|
|
})
|
|
```
|
|
|
|
- `default` — pre-filled **editable** text (the user edits it in place).
|
|
- opts:
|
|
- `placeholder` — dim hint shown while the input is empty.
|
|
- `default` — string returned when the input is submitted empty (shown as a
|
|
hint; distinct from the pre-filled positional default, and both can be
|
|
used together).
|
|
- `minLen`, `maxLen` — length bounds (characters), enforced inline.
|
|
- `suggestions` — array of strings offered as autocompletion
|
|
(case-insensitive substring match; Tab completes the highlighted one).
|
|
- `pageSize` — how many suggestions are visible at once.
|
|
|
|
### `tui.promptLongText(text, default, opts)` → `string | nil`
|
|
|
|
Multi-line input via an external editor: opens `$VISUAL`/`$EDITOR` (falling
|
|
back to a platform default) on a temp file and returns its contents on save.
|
|
|
|
```lua
|
|
local body = tui.promptLongText("Release notes", "## Changes\n\n", { extension = ".md" })
|
|
```
|
|
|
|
- `default` — text the editor buffer starts with.
|
|
- opts:
|
|
- `extension` — temp-file extension (drives editor syntax highlighting);
|
|
a missing leading dot is added, default `.txt`.
|
|
- `editor` — editor command to use instead of `$VISUAL`/`$EDITOR`.
|
|
|
|
### `tui.promptSelect(text, default, opts)` → `string | string[] | nil`
|
|
|
|
Pick from a list. The third argument is **required** — it carries the options.
|
|
The list is filterable by typing (fuzzy match); arrows or `vimMode` keys move
|
|
the cursor.
|
|
|
|
```lua
|
|
local color = tui.promptSelect("Color?", "green", { options = { "red", "green", "blue" } })
|
|
|
|
local picks = tui.promptSelect("Toppings?", { "cheese" }, {
|
|
options = { "cheese", "ham", "mushrooms", "olives" },
|
|
multiple = true,
|
|
minSelected = 1,
|
|
})
|
|
```
|
|
|
|
- `default` — matched against the options **by value**; a value that is not
|
|
among the options raises an error (a stale default is a script bug). Single
|
|
mode: a string (the cursor starts there). Multiple mode: a string or an
|
|
array of strings (pre-checked).
|
|
- opts:
|
|
- `options` — non-empty array of strings (**required**).
|
|
- `multiple` — `true` switches to checkbox-style multi-select (Space
|
|
toggles, Enter submits) and the result becomes an array of the chosen
|
|
strings (possibly empty).
|
|
- `pageSize` — visible rows (default 7).
|
|
- `vimMode` — `hjkl` navigation.
|
|
- `filter` — set `false` to disable the type-to-filter input.
|
|
- multiple-only: `minSelected` / `maxSelected` (selection-count bounds,
|
|
enforced inline), `allSelected` (start with everything checked).
|
|
|
|
### `tui.promptSecret(text, opts)` → `string | nil`
|
|
|
|
Secret input. Deliberately has **no default parameter**.
|
|
|
|
```lua
|
|
local secret = tui.promptSecret("API token", { confirm = true, minLen = 8 })
|
|
```
|
|
|
|
- opts:
|
|
- `confirm` — ask twice and require both entries to match (default `false`).
|
|
- `display` — `"masked"` (default, `*` per character), `"hidden"` (no echo
|
|
at all), or `"full"` (plain text).
|
|
- `toggle` — allow Ctrl-R to toggle revealing the input.
|
|
- `minLen` — minimum length, enforced inline.
|
|
|
|
### `tui.promptNumber(text, default, opts)` → `number | nil`
|
|
### `tui.promptInt(text, default, opts)` → `integer | nil`
|
|
|
|
Numeric input; the entry is parsed and re-prompted until it is a valid number
|
|
(`promptNumber`) or whole number (`promptInt`).
|
|
|
|
```lua
|
|
local ratio = tui.promptNumber("Ratio?", 0.5, { min = 0, max = 1 })
|
|
local port = tui.promptInt("Port?", 8080, { min = 1, max = 65535 })
|
|
```
|
|
|
|
- `default` — returned when the input is submitted empty. `promptInt`
|
|
rejects a fractional default (`1.5`) at call time.
|
|
- opts: `placeholder`, `min`, `max` (bounds enforced inline; `min > max` is a
|
|
call-time error).
|
|
|
|
### `tui.promptDate(text, default, opts)` → `string | nil`
|
|
|
|
Date picker: an interactive calendar navigated with the arrow keys. Dates
|
|
cross the API as `"YYYY-MM-DD"` strings in both directions.
|
|
|
|
```lua
|
|
local day = tui.promptDate("Start date?", "2026-07-06", {
|
|
min = "2026-01-01",
|
|
max = "2026-12-31",
|
|
weekStart = "monday",
|
|
})
|
|
```
|
|
|
|
- `default` — the initially selected date, `"YYYY-MM-DD"`.
|
|
- opts:
|
|
- `min`, `max` — selectable range, `"YYYY-MM-DD"`.
|
|
- `weekStart` — first day of the calendar week: a weekday name like
|
|
`"monday"` or `"sun"` (default Sunday).
|
|
|
|
## `tui.styled(markup)` → `string`
|
|
|
|
Renders text with HTML-like style tags into a string with ANSI escape
|
|
sequences, ready to `print`. The tag syntax follows
|
|
[Symfony console formatting](https://symfony.com/doc/current/console/coloring.html).
|
|
|
|
```lua
|
|
print(tui.styled("<error> FAIL </error> expected <info>42</info>, see " ..
|
|
"<href=https://example.com/docs>the docs</> for <fg=#8888ff;options=bold>details</>"))
|
|
```
|
|
|
|
### Built-in styles
|
|
|
|
| Tag | Rendering |
|
|
|-----|-----------|
|
|
| `<info>` | bold green text |
|
|
| `<error>` | white on red |
|
|
| `<b>` | **bold** |
|
|
| `<i>` | *italic* |
|
|
| `<u>` | underscore |
|
|
| `<s>` | ~~strikethrough~~ |
|
|
|
|
(Symfony's `<comment>` and `<question>` are deliberately not built in — add
|
|
them with `tui.addPreset` if you want them.)
|
|
|
|
### Inline styles
|
|
|
|
`<fg=COLOR>`, `<bg=COLOR>` and `<options=...>` can be combined in one tag,
|
|
separated by `;`:
|
|
|
|
```lua
|
|
tui.styled("<fg=green>ok</> <bg=yellow;options=bold>careful</> <fg=#c0392b>brick red</>")
|
|
```
|
|
|
|
- Colors: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`,
|
|
`white`, `gray`, their `bright-*` variants, `default` (the terminal's own
|
|
color — as an *inner* tag it resets an inherited color), and hex
|
|
`#rrggbb` / `#rgb` (truecolor).
|
|
- Options (comma-separated): `bold`, `italic`, `underscore`, `blink`,
|
|
`reverse`, `conceal`, `strikethrough`.
|
|
- The syntax is strict: no spaces around `=` or `;`.
|
|
|
|
### Shorthand
|
|
|
|
Bare tokens work without the `fg=`/`options=` keys: a color names the
|
|
foreground, `on-<color>` the background, and option names apply directly.
|
|
Shorthand mixes freely with `key=value` parts and built-in names:
|
|
|
|
```lua
|
|
tui.styled("<red;on-white;bold>alarm</> <#f0a;u>pink underline</> <info;b>bold green</>")
|
|
```
|
|
|
|
### Custom presets — `tui.addPreset(name, style)`
|
|
|
|
Register your own tag name for any style spec. The spec uses the same syntax
|
|
as tags (attributes, shorthand, or another preset/built-in name); the name
|
|
must be identifier-like (start with a letter; letters, digits, `-`, `_`).
|
|
|
|
```lua
|
|
tui.addPreset("keyword", "fg=white;bg=magenta")
|
|
tui.addPreset("em", "i;bold")
|
|
print(tui.styled("the <keyword>local</keyword> keyword is <em>important</em>"))
|
|
tui.note("see the <keyword>docs</keyword>") -- NOT styled: block text is plain
|
|
tui.block("styled block", { style = "keyword" }) -- but block styles resolve presets
|
|
```
|
|
|
|
Re-registering a name overwrites it, and a preset may shadow a built-in name
|
|
or shorthand token (e.g. `addPreset("info", "fg=blue")` restyles `<info>`).
|
|
|
|
### Closing tags and nesting
|
|
|
|
`</>` closes the innermost open style; named forms (`</info>`,
|
|
`</fg=green>`) work too and do the same thing. Styles nest — an inner tag
|
|
overrides only what it sets and inherits the rest:
|
|
|
|
```lua
|
|
tui.styled("<error>fail in <options=bold>step 2</> of job</error>")
|
|
-- "step 2" is bold white-on-red; the rest of the sentence plain white-on-red
|
|
```
|
|
|
|
### Hyperlinks
|
|
|
|
`<href=URL>` emits an [OSC-8 hyperlink](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda);
|
|
supporting terminals show clickable text, others show just the text:
|
|
|
|
```lua
|
|
tui.styled("see <href=https://example.com>the manual</>")
|
|
```
|
|
|
|
### Action tags: cursor movement and clearing
|
|
|
|
Besides styles, markup can move the cursor and clear parts of the display —
|
|
handy for single-line redraws without string-formatting escape codes by hand:
|
|
|
|
```lua
|
|
for i = 1, 100 do
|
|
tui.raw(tui.styled(("<clear=line>progress <info>%d%%</info>"):format(i)))
|
|
-- ... work ...
|
|
end
|
|
tui.raw("\n")
|
|
```
|
|
|
|
| Tag | Effect |
|
|
|-----|--------|
|
|
| `<up>` `<down>` `<left>` `<right>` | move the cursor by one cell |
|
|
| `<up=3>` … | move by *n* cells |
|
|
| `<row=3;col=2>` | absolute move, 1-based; each axis works alone too |
|
|
| `<start>` | column 1 of the current line |
|
|
| `<home>` | top-left corner |
|
|
| `<clear=line>` | clear the whole line, cursor to column 1 |
|
|
| `<clear=start>` | clear to the start of the line, cursor to column 1 |
|
|
| `<clear=end>` | clear to the end of the line, cursor stays put |
|
|
| `<clear=screen>` | clear the whole screen, cursor home |
|
|
| `<clear=up>` | clear above the cursor, cursor home |
|
|
| `<clear=down>` | clear below the cursor, cursor stays put |
|
|
|
|
Unlike style tags, action tags emit their escape sequence *in place* — they
|
|
don't enclose text and have no closing form (`</up>` is literal text). Ops
|
|
combine in one tag, applied left to right: `<up;clear=line>` moves up one
|
|
line and blanks it. Whole and to-start clears also return the cursor (column
|
|
1 or home, matching `tui.clearLine`/`tui.clearScreen`), so the redraw starts
|
|
from a known spot; the `end`/`down` variants leave it in place.
|
|
|
|
Like all escape output, action tags are dropped when colors are off (pipes,
|
|
`NO_COLOR`) — piped output never contains control bytes. A custom preset may
|
|
shadow an action name (`addPreset("up", …)` turns `<up>` into a style tag).
|
|
|
|
Stateful operations — the alternate screen, cursor visibility, window title —
|
|
are deliberately **not** tags; they need cleanup tracking and live in the
|
|
[terminal control functions](#terminal-control) below.
|
|
|
|
### Escaping and invalid tags
|
|
|
|
A backslash escapes a literal bracket: `"\\<info>"` prints `<info>`; `"\\\\"`
|
|
prints a single backslash. Anything that does not parse as a tag —
|
|
`<unknown>`, `<Info>` (tags are case-sensitive), a lone `<`, a bad color — is
|
|
passed through as literal text, never an error. For dynamic text, don't
|
|
escape by hand — use [`tui.escape`](#tuiescapetext--string).
|
|
|
|
### Color detection
|
|
|
|
When output is piped or `NO_COLOR` is set, `tui.styled` returns the plain
|
|
text: tags are still consumed, but no escape codes (colors or hyperlinks) are
|
|
emitted. Detection follows the usual conventions (`NO_COLOR`, `CLICOLOR`,
|
|
`CLICOLOR_FORCE`, tty check on stdout).
|
|
|
|
## `tui.escape(text)` → `string`
|
|
|
|
Backslash-escapes the markup metacharacters (`<`, `>`, `\`) so text from
|
|
outside the program — user input, file contents, API responses — can be
|
|
embedded in a `tui.styled` format string and come out exactly as it went in,
|
|
styled only by the enclosing tags:
|
|
|
|
```lua
|
|
local city = tui.promptText("City?")
|
|
print(tui.styled(("looking up <b>%s</b>…"):format(tui.escape(city))))
|
|
```
|
|
|
|
Without escaping, a user who types `<error>` gets it rendered as a red block,
|
|
and stray brackets can eat the format string's own tags. Only `tui.styled`
|
|
markup needs this; block messages (`tui.warning` & co.) are plain text and
|
|
never interpret tags.
|
|
|
|
## Message blocks
|
|
|
|
Ready-made status messages in the style of Symfony's console: a labeled,
|
|
word-wrapped block, padded to the terminal width (capped at 120 columns) and
|
|
painted with a background color. They **print directly to stdout**, surrounded
|
|
by blank lines. When output is piped (or `NO_COLOR` is set) they degrade to
|
|
plain aligned text.
|
|
|
|
```lua
|
|
tui.success("Deployment finished")
|
|
tui.warning({ "Disk usage above 80%", "Consider pruning old artifacts" })
|
|
tui.error("Build failed")
|
|
```
|
|
|
|
| Function | Label | Style |
|
|
|---|---|---|
|
|
| `tui.success(message, opts)` | `[OK]` | black on green |
|
|
| `tui.error(message, opts)` | `[ERROR]` | white on red |
|
|
| `tui.warning(message, opts)` | `[WARNING]` | black on yellow |
|
|
| `tui.caution(message, opts)` | `[CAUTION]` | white on red, `!` prefix |
|
|
| `tui.info(message, opts)` | `[INFO]` | green text |
|
|
| `tui.note(message, opts)` | `[NOTE]` | yellow text, `!` prefix |
|
|
| `tui.comment(message, opts)` | — | gray text |
|
|
| `tui.block(message, opts)` | — | generic: everything from opts |
|
|
|
|
- `message` — a string or an array of strings (rendered into one block,
|
|
separated by a blank line). Treated as **plain text**: markup tags are not
|
|
interpreted here (compose with `tui.styled` on your own lines instead), and
|
|
lines wrap by word to fit the width.
|
|
- `opts` (each overrides the preset's default):
|
|
- `label` — the `[LABEL]` text; continuation lines align under it.
|
|
- `style` — same syntax as a `styled` tag body: a preset or built-in name
|
|
(`"error"`, anything from `tui.addPreset`), a spec (`"fg=black;bg=cyan"`),
|
|
or shorthand (`"black;on-cyan"`). An invalid style is an error.
|
|
- `prefix` — string prepended to every line (default `" "`).
|
|
- `padding` — blank styled line above and below the text (only applies when
|
|
colors are on; without a background it would just be empty lines).
|
|
|
|
### Outline blocks
|
|
|
|
The same messages as a bordered box — colored **borders** instead of colored
|
|
backgrounds, which stays readable on any terminal color scheme:
|
|
|
|
```
|
|
┌─ Success ──────────────────────────┐
|
|
│ │
|
|
│ All tests passed │
|
|
│ │
|
|
└────────────────────────────────────┘
|
|
```
|
|
|
|
```lua
|
|
tui.outlineSuccess("All tests passed")
|
|
tui.outlineError("Build failed", { title = "Compile error" })
|
|
tui.outlineBlock("Anything", { title = "Stats", style = "fg=magenta" })
|
|
```
|
|
|
|
| Function | Fallback title | Border style |
|
|
|---|---|---|
|
|
| `tui.outlineSuccess` | Success | green |
|
|
| `tui.outlineError` | Error | red |
|
|
| `tui.outlineWarning` | Warning | yellow |
|
|
| `tui.outlineNote` | Note | blue |
|
|
| `tui.outlineInfo` | Info | green |
|
|
| `tui.outlineCaution` | Caution | red |
|
|
| `tui.outlineBlock` | — | — (generic) |
|
|
|
|
- `opts`: `title` (shown in the top border; overrides the preset's fallback),
|
|
`style` (border color, tag-body syntax), `padding` (blank line above/below
|
|
the content **inside** the box, default `true`).
|
|
|
|
## Low-level output
|
|
|
|
### `tui.raw(...)`
|
|
|
|
Writes its arguments to stdout with **no newline and no separator** (each is
|
|
converted with `tostring`), then flushes. This is the building block for
|
|
progress-style output that redraws one line:
|
|
|
|
```lua
|
|
for i = 1, 100 do
|
|
tui.raw("\rprocessing ", i, "/100")
|
|
-- ... work ...
|
|
end
|
|
tui.raw("\n")
|
|
```
|
|
|
|
### `tui.screenInfo()` → `table`
|
|
|
|
Facts about the terminal, for scripts that render their own UI (progress
|
|
bars, right-aligned text, …):
|
|
|
|
| Field | Meaning |
|
|
|---|---|
|
|
| `width`, `height` | terminal size in cells; `nil` when there is no terminal to measure |
|
|
| `tty` | whether stdout is a terminal (`false` when piped) |
|
|
| `colors` | whether styled output will actually emit colors (same detection as `tui.styled`) |
|
|
|
|
## Terminal control
|
|
|
|
Direct terminal manipulation for scripts that render their own UI: cursor
|
|
movement, clearing, the alternate screen, scroll regions, the window title
|
|
and the clipboard. These are the standard xterm-style escape sequences,
|
|
wrapped as functions.
|
|
|
|
```lua
|
|
tui.altScreen(true)
|
|
tui.clearScreen()
|
|
tui.showCursor(false)
|
|
tui.moveTo(2, 4)
|
|
tui.raw(tui.styled("<info>fullscreen!</info>"))
|
|
os.sleep(2)
|
|
-- no explicit restore needed: the runtime cleans up on exit
|
|
```
|
|
|
|
Shared behavior:
|
|
|
|
- **No-ops without a terminal.** When stdout is not a tty, every function
|
|
validates its arguments and then does nothing — piped output never
|
|
contains control bytes. This is a plain tty check (unlike color
|
|
detection): `NO_COLOR` turns off colors, not cursor control.
|
|
- **Bad arguments always raise**, tty or not — typos fail loudly in pipes
|
|
and CI too.
|
|
- **Automatic cleanup.** The runtime tracks the alternate screen, a hidden
|
|
cursor, a changed cursor style, a scroll region and a pushed window title,
|
|
and restores all of them when the script ends — on normal exit, on error
|
|
(the message prints on the normal screen), and on a crash. Scripts should
|
|
still restore what they change for mid-script tidiness, but cannot wreck
|
|
the terminal by forgetting.
|
|
|
|
### Cursor
|
|
|
|
- **`tui.moveTo(row, col)`** — absolute move, 1-based (row 1, column 1 is
|
|
the top-left corner). Passing `nil` for one axis keeps it:
|
|
`tui.moveTo(5)` jumps to row 5, `tui.moveTo(nil, 1)` to column 1.
|
|
- **`tui.move(dx, dy)`** — relative move: positive `dx` right, positive `dy`
|
|
down; negative values go the other way, `nil` counts as 0. The terminal
|
|
clamps at the screen edges.
|
|
- **`tui.saveCursor()` / `tui.restoreCursor()`** — save and restore the
|
|
cursor position (one slot; a save overwrites the previous one).
|
|
- **`tui.cursorPos()` → `row, col`** — ask the terminal where the cursor is
|
|
(1-based). Returns `nil` when there is no terminal or it doesn't answer.
|
|
Like the prompts this is async and serialized against them, so a
|
|
concurrent prompt can't eat the terminal's reply.
|
|
- **`tui.showCursor(visible)`** — hide (`false`) or show (`true` or no
|
|
argument) the cursor. A hidden cursor is re-shown on exit.
|
|
- **`tui.cursorStyle(style, opts)`** — set the cursor shape: `"block"`,
|
|
`"underline"` or `"bar"`, with `{ blink = true }` for the blinking
|
|
variant. No arguments reset to the terminal default (also done on exit).
|
|
|
|
### Clearing
|
|
|
|
- **`tui.clearLine(mode)`** / **`tui.clearScreen(mode)`** — erase the
|
|
current line / the screen. `mode` selects what: `0` or `nil` = whole,
|
|
`-1` = up to the cursor, `1` = from the cursor to the end. Whole and
|
|
to-start clears also return the cursor to a known spot (column 1 / home);
|
|
to-end clears leave it in place.
|
|
|
|
### Alternate screen
|
|
|
|
- **`tui.altScreen(on)`** — switch to (`true`) or back from (`false`) the
|
|
alternate screen buffer, the full-screen-app mode where the normal
|
|
scrollback stays untouched. Switching in an already-active direction is
|
|
ignored, and the runtime always switches back on exit.
|
|
|
|
### Scrolling
|
|
|
|
- **`tui.scrollRegion(top, bottom)`** — restrict scrolling to the given
|
|
rows (1-based, inclusive; `top < bottom`) — the basis for fixed
|
|
headers/footers. Setting a region moves the cursor home. With no arguments
|
|
the region resets to the full screen (also done on exit).
|
|
- **`tui.scroll(n)`** — scroll the scroll region: positive `n` scrolls the
|
|
content up by `n` lines, negative down, `0` does nothing. The cursor does
|
|
not move.
|
|
|
|
### Terminal integration
|
|
|
|
- **`tui.setTitle(text)`** — set the terminal window title. The first call
|
|
saves the current title on the terminal's title stack, and the runtime
|
|
restores it on exit. Control characters in the title are an error.
|
|
- **`tui.setClipboard(text)`** — copy `text` into the system clipboard via
|
|
the terminal (OSC 52). Write-only: reading the clipboard is disabled by
|
|
most terminals for good reasons. Works over SSH in supporting terminals;
|
|
some terminals require enabling clipboard access in their settings.
|
|
- **`tui.reset()`** — soft-reset the terminal (DECSTR): un-sticks stuck
|
|
attributes, a garbled charset, a forgotten scroll region — without
|
|
clearing the screen. The heavy-handed fixer for "my output looks insane".
|
|
|
|
## Notes
|
|
|
|
- **Always loaded.** No `require`; `tui` is a global in every script
|
|
(`require "tui"` also works and returns the same table).
|
|
- **Prompts need a terminal.** In pipes, cron, CI etc. every prompt raises
|
|
`not a terminal`; `tui.styled` and the blocks still work and degrade to
|
|
plain text, and the terminal-control functions become no-ops.
|
|
- **Progress bars** can be built from `tui.raw` + `tui.screenInfo` + the
|
|
`<clear=line>` action tag; a ready-made widget may come later.
|
|
- **Key events** (raw keyboard input, mouse tracking) are not exposed yet;
|
|
prompts are the supported way to read input.
|
|
|