Skip to main content
A PHP library · MIT

A dependency-light PHP engine for interactive, keyboard-driven terminal forms. Declare the questions with a fluent builder; the engine renders a scrollable, themeable TUI - or collects the answers headlessly from JSON and environment variables.

composer require drevops/tui
quick-start

Declare a form in PHP

Describe the questions with a fluent builder - text, choices, toggles and more - add a handler class where a question needs real behaviour, and call run(). Interactive on a terminal, non-interactive otherwise.

form.php
use DrevOps\Tui\Builder\Form;
use DrevOps\Tui\Builder\PanelBuilder;
use DrevOps\Tui\Tui;

$form = Form::create('New project')
  ->panel('setup', 'Setup', function (PanelBuilder $p): void {
    $p->text('name', 'Project name')->required();
    $p->select('type', 'Project type')->options([
      'app' => 'Application',
      'library' => 'Library',
    ]);
    $p->select('services', 'Services')->multiple()->default(['redis'])->options([
      'redis' => 'Redis',
      'solr' => 'Solr',
      'clamav' => 'ClamAV',
    ]);
    $p->confirm('ci', 'Set up CI?')->default(TRUE);
  });

$tui = new Tui($form, ['App\\Handler']);

// Interactive on a terminal, non-interactive otherwise.
$answers = $tui->run();
INTERACTIVE

On a terminal

A scrollable, keyboard-driven TUI; fields group into sections that drill in to any depth, with a contextual key-hint footer and a ? help overlay.

UNATTENDED

Everywhere else

Supply the answers up front as a JSON payload and environment variables so it runs without prompting. Emits a JSON schema for agents.

features

Built for keyboard-driven forms

The engine stays generic. Your questions and handlers live in the consumer - it collects, you apply.

01Full-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.

02Inline editing

A field's editor opens in place on the panel row, with its own view and keys; opt a field out to a full screen with ->standalone().

03Widget library

Text, numbers, dates, single and multiple choice, fuzzy search, file browsing, reordering and gates.

04Builder-driven

The form is declared in PHP with a fluent builder; the common cases need no extra code.

05Interactive or unattended

Answer by keyboard, or supply the answers up front as a JSON payload and environment variables so it runs without prompting. Emits a JSON schema for agents.

06Derived values

Compute one field from others with str2name transforms; chains settle to a fixpoint.

07Conditional fields

Show or hide fields with when rules; a fix-up pass reconciles dependent answers.

08Themes

The whole visual representation - colours, glyphs, layout - is a theme class; ships dark and light.

09Key bindings

Remap navigation, edit, accept and cancel keys per widget type; ships a vim-style preset.

widgets

One layer, every field type

Text, numbers, dates, single and multiple choice, fuzzy search, file browsing, reordering and gates.

Animated demo of the Calendar widget

Calendar

A month calendar returning a normalized ISO YYYY-MM-DD; arrows move by day and week.

Animated demo of the Confirm widget

Confirm

Yes/No toggle; arrows or Space switch, y/n set the choice directly, Enter accepts.

Animated demo of the File picker widget

File picker

Browse the filesystem for a path, or several with ->multiple(); -> enters a directory, <- returns to its parent.

Animated demo of the Number widget

Number

Integer entry accepted as an int, with optional min, max and step.

Animated demo of the Password widget

Password

Text rendered as a mask everywhere; the accepted value stays plain for the consumer, and can be made revealable.

Animated demo of the Pause widget

Pause

An acknowledgement gate; Enter or Space accepts. Unattended runs auto-acknowledge it.

Animated demo of the Reorder widget

Reorder

Rank a list by moving items; Space picks an item up, arrows carry it, Enter accepts.

Animated demo of the Search widget

Search

Single choice with a visible filter line; typing fuzzy-matches and ranks the labels.

Animated demo of the Select widget

Select

Single choice from a list; arrows move, Enter accepts, long lists page around the cursor.

Animated demo of the Suggest widget

Suggest

Free text with autocomplete over a fixed option set; suggestions fuzzy-matched and ranked.

Animated demo of the Text widget

Text

Single-line input with a movable caret; type to insert, arrows move, Backspace deletes.

Animated demo of the Textarea widget

Textarea

Multi-line input; Enter inserts a newline, Tab accepts, with an external-editor handoff.

Animated demo of the Toggle widget

Toggle

An inline switch between two labelled values; arrows or Space flip, first letter sets it directly.