Terminal user interfaces for PHP
A dependency-light PHP engine for building terminal user interfaces: interactive, keyboard-driven forms that collect answers and hand them to your code. Describe the questions in PHP with a fluent builder, add a handler class wherever a question needs real behaviour, and the engine renders a scrollable, themeable TUI - or collects the answers non-interactively from a JSON payload.
The engine knows nothing about the application it serves: it stays generic, the application-specific questions and handlers live in the consumer, and applying the collected answers is the consumer's job, not the TUI's.
Features
- 🧭 Full-screen TUI - a scrollable, keyboard-driven form: fields group into sections that drill in to any depth, with a contextual key-hint footer and a
?help overlay - 🧩 Widgets -
confirm,date,filepicker,multifilepicker,multisearch,multiselect,number,password,pause,search,select,suggest,text,textarea,toggle - 🏗️ Builder-driven - the form is declared in PHP with a fluent builder; the common cases need no code
- 🎛️ Interactive or unattended - answer the form by keyboard, or supply the answers up front as a JSON payload and environment variables so it runs without prompting (and emit a JSON schema for agents and forms)
- 🔗 Derived values - compute one field from others with str2name transforms; chains settle to a fixpoint
- 🔀 Conditional fields - show or hide fields with
whenrules; a fix-up pass reconciles dependent answers - 🔍 Discovery - detect sensible defaults from the target directory (
.envkeys, JSON paths, path existence, directory scans) - ⚙️ Declared behaviour - validation, transforms and dynamic defaults as closures on the field; per-field handler classes remain as a fallback
- 📦 Self-describing answers - each answer carries a snapshot of its question and its provenance; summaries need no form config
- 🎨 Themes - the whole visual representation (colours, glyphs, layout) is a theme class; ships with dark and light
- ⌨️ Key bindings - remap navigation, edit, accept and cancel keys per widget type; ships a vim-style preset, and a bad binding fails loudly at build time
- ✨ Unicode and ASCII - glyphs follow the terminal locale and colour honours
NO_COLOR; both can be forced on the form
Quick start
Declare a form with the fluent Form builder - a panel of fields, each one a
widget - then drive it through the Tui facade, the one class that
wires the engine, resolver, schema tools and TUI for you:
use DrevOps\Tui\Builder\Form;
use DrevOps\Tui\Builder\PanelBuilder;
use DrevOps\Tui\Tui;
$form = Form::create('Quick start')
->panel('order', 'New order', function (PanelBuilder $p): void {
// A required single-line text field.
$p->text('name', 'Order name')->required();
// A single choice, starting on "Banana".
$p->select('fruit', 'Fruit')->default('banana')->options([
'apple' => 'Apple',
'banana' => 'Banana',
'cherry' => 'Cherry',
]);
// A multi-select, with one option pre-checked.
$p->multiSelect('veg', 'Vegetables')->default(['carrot'])->options([
'carrot' => 'Carrot',
'tomato' => 'Tomato',
'spinach' => 'Spinach',
]);
// An integer bounded to a sensible quantity.
$p->number('quantity', 'Quantity')->min(1)->max(99)->default(6);
// A yes/no gate.
$p->confirm('organic', 'Organic only?')->default(FALSE);
});
$tui = new Tui($form);
// Interactive panel TUI on a terminal, headless otherwise.
$answers = $tui->run();
// Or drive a mode directly:
echo $tui->collect('{"name":"Weekly box"}')->toJson(); // headless: JSON + environment
$answers = $tui->interact(); // interactive panel TUI
Run it on a terminal and the panel opens on the form's fields, ready to fill:
run() picks the mode automatically - the interactive panel TUI on a terminal,
headless otherwise. The facade also exposes schema(), agentHelp() and
validate(), and - for finer control - the internals via form(), engine()
and registry().
This is the runnable playground/0-quickstart/
example. See Installation to get started and the
playground for more complete examples.