Skip to main content

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