Installation
Install the package with Composer:
composer require drevops/tui
The package is a library consumed programmatically - it has no CLI entry point of its own. The public surface is:
DrevOps\Tui\Tui- the facade: collect a form's answers, headlessly or through the interactive panel TUI.DrevOps\Tui\Builder\Form- the fluent builder for declaring a form's panels and fields.
Usage
Declare a form with the fluent Form builder, then drive it with the Tui
facade - 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->select('veg', 'Vegetables')->multiple()->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() picks the mode automatically: it drives the interactive panel TUI on a
terminal and collects headlessly otherwise (or whenever prompts are supplied).
The facade also exposes schema(), agentHelp() and validate(), and - when
you want finer control - the internals via form(), engine() and
registry().
Aborting with Ctrl-C
On a terminal, pressing Ctrl-C part-way through the interactive session aborts
it: the panel TUI restores the terminal and clears the screen, and the facade
raises a DrevOps\Tui\InterruptException instead of returning the partial
answers - so an abort is never mistaken for a completed submission. Catch it to
leave cleanly, the way a conventional SIGINT would (exit code 130):
use DrevOps\Tui\InterruptException;
try {
$answers = $tui->run();
}
catch (InterruptException) {
// The user pressed Ctrl-C: leave without printing a result.
exit(130);
}
echo $answers->toSummary();
Only the interactive session raises this. Headless collection - collect(), or
run() when prompts are supplied or the input is piped - never interacts, so it
never aborts this way.
Next steps
- Widgets - the field types and their options.
- Configuration - form structure, derived values and conditional fields.
- Headless collection - driving the form from JSON and environment variables.
- Playground - complete, runnable examples.