Panel-based terminal forms for PHP
A dependency-light PHP engine for building panel-based terminal forms: interactive, keyboard-driven questionnaires 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 runs headless from a JSON payload.
It powers the Vortex project installer, but knows nothing about Vortex: the engine is generic, the project-specific questions and handlers live in the consumer, and applying the collected answers is the consumer's job, not the TUI's.
Features
- 🧭 Panel TUI - a full-screen, scrollable, keyboard-driven form: panels hold fields, sub-panels drill in to any depth, with a contextual key-hint footer and a
?help overlay - 🧩 Widgets -
text,number,date,textarea,password,select,multiselect,suggest,search,multisearch,filepicker,multifilepicker,confirm,toggle,pause - 🏗️ Builder-driven - panels and fields are declared in PHP with a fluent builder; the common cases need no code
- 🤖 Interactive or headless - drive the panel TUI by keyboard, or collect answers non-interactively from a JSON payload and environment variables (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, 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('My form')
->panel('general', 'General', fn(PanelBuilder $p) => $p->text('name', 'Your name')->required());
$tui = new Tui($form, ['App\\Handler']);
// Interactive panel TUI on a terminal, headless otherwise.
$answers = $tui->run();
// Or call a mode directly:
echo $tui->collect('{"name":"Ada"}')->toJson(); // headless: JSON + environment
$answers = $tui->interact(); // interactive panel TUI
It also exposes schema(), agentHelp() and validate(), and - when you want
finer control - the internals via config(), engine() and registry(). See
Installation to get started and the
playground for complete, runnable examples.