Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
name: render-tui-diagrams
name: render-phptui-diagrams
description: Use when regenerating the TUI architecture diagrams under docs/architecture/ after editing a .puml source, or when adding a new data-flow (sequence) diagram. Renders the PlantUML sources to SVG and scaffolds new data-flow diagrams traced from src/. Triggers on "render diagrams", "regenerate architecture diagrams", "update the architecture diagrams", "add a data-flow diagram for <flow>".
---

Expand Down Expand Up @@ -61,7 +61,7 @@ The light render stays the single source of truth; the dark variant is derived f

```plantuml
@startuml
' drevops/tui - data flow for <flow>.
' drevops/phptui - data flow for <flow>.
' Traced from src/<entry class and method>.
' Regenerate every SVG with: plantuml -tsvg docs/architecture/*.puml
!theme plain
Expand Down
16 changes: 8 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ TUI or headlessly from a JSON payload and environment variables.
This 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
- **`DrevOps\PhpTui\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
- **`DrevOps\PhpTui\Builder\Form`** - the fluent builder for declaring a form's
panels and fields.

A consumer declares a form with `Form::create(...)->panel(...)` and drives it
Expand All @@ -30,12 +30,12 @@ through the `Tui` facade.
The facade also exposes **primitives** - standalone, theme-drawn elements that
collect no answer and never run inside the panel:

- `Tui::progress()` (`DrevOps\Tui\Primitive\Progress`) wraps a slow callback
- `Tui::progress()` (`DrevOps\PhpTui\Primitive\Progress`) wraps a slow callback
with a spinner (unknown length) or a determinate bar (known total) for work
that runs around the form.
- `Tui::output()` (`DrevOps\Tui\Primitive\Output`) draws the static chrome
- `Tui::output()` (`DrevOps\PhpTui\Primitive\Output`) draws the static chrome
around a form: boxes and cards, aligned tables, the five status lines of
`DrevOps\Tui\Primitive\Status`, definition lists, wrapped text, rules and a
`DrevOps\PhpTui\Primitive\Status`, definition lists, wrapped text, rules and a
banner.

Every piece a primitive draws routes through a `render*()` method on the theme
Expand All @@ -53,8 +53,8 @@ it was given, so it declares its edges and never draws them.

### Namespace Structure

- Source code: `DrevOps\Tui\`
- Tests: `DrevOps\Tui\Tests\`
- Source code: `DrevOps\PhpTui\`
- Tests: `DrevOps\PhpTui\Tests\`
- Autoloading: PSR-4 via Composer

## Commands
Expand Down Expand Up @@ -203,7 +203,7 @@ Key workflows:
## Documentation

Architecture diagrams and a narrative walkthrough live in `docs/architecture/`.
After a structural change, update them with the `render-tui-diagrams` skill.
After a structural change, update them with the `render-phptui-diagrams` skill.

### Terminal SVG assets

Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Contributing

Thanks for your interest in improving `drevops/tui`. Bug reports, feature ideas and pull requests are all welcome.
Thanks for your interest in improving `drevops/phptui`. Bug reports, feature ideas and pull requests are all welcome.

- Report a bug or propose a change: open an [issue](https://github.com/drevops/tui/issues).
- Submit a change: send a [pull request](https://github.com/drevops/tui/pulls) from a branch off `main`.
- Report a bug or propose a change: open an [issue](https://github.com/drevops/phptui/issues).
- Submit a change: send a [pull request](https://github.com/drevops/phptui/pulls) from a branch off `main`.

The full guide - local setup, the quality stack, tests, coding conventions and how the documentation and SVG assets are built - is on the documentation site:

Expand Down
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="logo-light.svg">
<img width="300" src="logo-dark.svg" alt="TUI logo">
<img width="300" src="logo-dark.svg" alt="PHPTUI logo">
</picture>
</p>

<h1 align="center">Terminal user interfaces for PHP</h1>

<div align="center">

[![GitHub Issues](https://img.shields.io/github/issues/drevops/tui.svg)](https://github.com/drevops/tui/issues)
[![GitHub Pull Requests](https://img.shields.io/github/issues-pr/drevops/tui.svg)](https://github.com/drevops/tui/pulls)
[![Test PHP](https://github.com/drevops/tui/actions/workflows/test-php.yml/badge.svg)](https://github.com/drevops/tui/actions/workflows/test-php.yml)
[![codecov](https://codecov.io/gh/drevops/tui/graph/badge.svg?token=7WEB1IXBYT)](https://codecov.io/gh/drevops/tui)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/drevops/tui)
![LICENSE](https://img.shields.io/github/license/drevops/tui)
[![GitHub Issues](https://img.shields.io/github/issues/drevops/phptui.svg)](https://github.com/drevops/phptui/issues)
[![GitHub Pull Requests](https://img.shields.io/github/issues-pr/drevops/phptui.svg)](https://github.com/drevops/phptui/pulls)
[![Test PHP](https://github.com/drevops/phptui/actions/workflows/test-php.yml/badge.svg)](https://github.com/drevops/phptui/actions/workflows/test-php.yml)
[![codecov](https://codecov.io/gh/drevops/phptui/graph/badge.svg?token=7WEB1IXBYT)](https://codecov.io/gh/drevops/phptui)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/drevops/phptui)
![LICENSE](https://img.shields.io/github/license/drevops/phptui)
![Renovate](https://img.shields.io/badge/renovate-enabled-green?logo=renovatebot)

</div>
Expand All @@ -28,7 +28,7 @@
</picture>
</p>

`drevops/tui` is a PHP library for panel-based terminal forms: keyboard-driven questionnaires that collect a set of answers and hand them back to your code as typed values.
`drevops/phptui` is a PHP library for panel-based terminal forms: keyboard-driven questionnaires that collect a set of answers and hand them back to your code as typed values.

- **Declarative form model.** A form is declared with a fluent builder (`Form` / `PanelBuilder` / `FieldBuilder`): panels of typed fields, each with its own options, conditions, derivation rules and behavior.
- **Two collection modes, one declaration.** The same form runs as a full-screen interactive TUI on a terminal, or resolves non-interactively from a JSON payload, per-field environment variables, discovery rules and defaults.
Expand Down Expand Up @@ -83,7 +83,7 @@ Every feature has a reference page and a runnable, self-contained example in [`p
| ⚡ Inline editing | A field's editor opens in place on the panel row; `->standalone()` opts a field out to full-screen | [panels](https://phptui.dev/panels#inline-editing) | [`04-inline-editing`](playground/04-inline-editing.php) |
| 🧩 Fields | 15 field types: text, template, number, rating, calendar, textarea, password, select, reorder, suggest, search, file picker, confirm, toggle, pause - plus markup and progress blocks, which collect nothing | [fields](https://phptui.dev/fields) | [`02-fields-*`](playground) |
| 🏗️ Builder-driven | The form is declared in PHP with a fluent builder; the common cases need no code | [configuration](https://phptui.dev/configuration) | [`01-quickstart`](playground/01-quickstart.php) |
| 🎛️ Interactive or unattended | `run()` picks the mode: keyboard on a terminal, otherwise JSON payload + `TUI_<ID>` environment variables | [headless collection](https://phptui.dev/headless-collection) | [`08-headless-*`](playground) |
| 🎛️ Interactive or unattended | `run()` picks the mode: keyboard on a terminal, otherwise JSON payload + `PHPTUI_<ID>` environment variables | [headless collection](https://phptui.dev/headless-collection) | [`08-headless-*`](playground) |
| 🔗 Derived values | Fields computed from other answers via `{{field}}` templates and str2name transforms, settling to a fixpoint | [configuration](https://phptui.dev/configuration#derived-values) | [`05-form-logic-*`](playground) |
| 🔀 Conditional fields | `->when()` conditions (eq/ne/in/contains, composable with all/any/not) drive visibility; form-level fix-ups reconcile answers, and an opt-in indent steps each field in from the answer that reveals it | [configuration](https://phptui.dev/configuration#conditional-fields) | [`05-form-logic-*`](playground) |
| ⚙️ Declared behavior | `->required()` rejects an empty value with a label-derived or declared message; dynamic defaults, validation and transforms as field closures, or as per-field handler classes resolved by naming convention | [field behavior](https://phptui.dev/field-behaviour) | [`06-field-behaviour-*`](playground) |
Expand All @@ -102,17 +102,17 @@ Every feature has a reference page and a runnable, self-contained example in [`p
## Installation

```bash
composer require drevops/tui
composer require drevops/phptui
```

## Quick start

Declare a form with the `Form` builder, then drive it through the `Tui` facade - the one class that wires up collection, the input resolver, the schema tools and the interactive screen for you:

```php
use DrevOps\Tui\Builder\Form;
use DrevOps\Tui\Builder\PanelBuilder;
use DrevOps\Tui\Tui;
use DrevOps\PhpTui\Builder\Form;
use DrevOps\PhpTui\Builder\PanelBuilder;
use DrevOps\PhpTui\Tui;

$form = Form::create('Quick start')
->panel('order', 'New order', function (PanelBuilder $p): void {
Expand Down
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "drevops/tui",
"name": "drevops/phptui",
"description": "Provides terminal UI form-building functionality.",
"license": "GPL-2.0-or-later",
"type": "library",
Expand All @@ -13,8 +13,8 @@
],
"homepage": "https://phptui.dev/",
"support": {
"issues": "https://github.com/drevops/tui/issues",
"source": "https://github.com/drevops/tui"
"issues": "https://github.com/drevops/phptui/issues",
"source": "https://github.com/drevops/phptui"
},
"require": {
"php": ">=8.3",
Expand All @@ -39,12 +39,12 @@
"prefer-stable": true,
"autoload": {
"psr-4": {
"DrevOps\\Tui\\": "src/"
"DrevOps\\PhpTui\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"DrevOps\\Tui\\Tests\\": "tests/phpunit"
"DrevOps\\PhpTui\\Tests\\": "tests/phpunit"
}
},
"config": {
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# How the TUI works

This is a walkthrough of the `drevops/tui` library - what you assemble to build a form, and what happens when it runs. The diagrams are rendered from the PlantUML sources in this directory by the [`render-tui-diagrams`](../../.claude/skills/render-tui-diagrams/SKILL.md) skill; everything below is derived from `src/`, so if the prose and the code disagree, the code wins.
This is a walkthrough of the `drevops/phptui` library - what you assemble to build a form, and what happens when it runs. The diagrams are rendered from the PlantUML sources in this directory by the [`render-phptui-diagrams`](../../.claude/skills/render-phptui-diagrams/SKILL.md) skill; everything below is derived from `src/`, so if the prose and the code disagree, the code wins.

The model the whole library is built on - four levels, seventeen capabilities, one canonical tree - is written out on the [specification](https://phptui.dev/specification) page. This walkthrough is the same thing seen from the outside: which class does which part, and in what order.

Expand Down Expand Up @@ -88,4 +88,4 @@ The diagrams are PlantUML (`.puml`) rendered to a light `.svg`, each with a dark
plantuml -tsvg docs/architecture/*.puml
node docs/util/derive-dark-diagram.js docs/architecture/*.svg

The [`render-tui-diagrams`](../../.claude/skills/render-tui-diagrams/SKILL.md) skill covers rendering, adding a new data-flow diagram, and keeping this walkthrough current.
The [`render-phptui-diagrams`](../../.claude/skills/render-phptui-diagrams/SKILL.md) skill covers rendering, adding a new data-flow diagram, and keeping this walkthrough current.
2 changes: 1 addition & 1 deletion docs/architecture/architecture-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/architecture/architecture.puml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@startuml
' drevops/tui - component architecture.
' drevops/phptui - component architecture.
' Packages group the src/ classes by what they do; arrows show the main
' dependencies.
' Regenerate every SVG with: plantuml -tsvg docs/architecture/*.puml
Expand All @@ -8,7 +8,7 @@ skinparam backgroundColor white
skinparam defaultFontName Helvetica
skinparam shadowing false
skinparam componentStyle rectangle
title drevops/tui - component architecture
title drevops/phptui - component architecture

package "Declaring" #F0F4C3 {
[Tui]
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/architecture.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/architecture/dataflow-collect-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/architecture/dataflow-collect.puml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@startuml
' drevops/tui - data flow for headless collection.
' drevops/phptui - data flow for headless collection.
' Traced from src/Tui.php (collect) and src/Screen/Collector.php
' (answers -> fetched -> settle -> resolveAll -> transformSupplied -> stabilize
' -> refusal).
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/dataflow-collect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/architecture/dataflow-tui-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/architecture/dataflow-tui.puml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@startuml
' drevops/tui - data flow for the interactive screen session.
' drevops/phptui - data flow for the interactive screen session.
' Traced from src/Screen/ScreenController.php (run -> paint/handle), with
' src/Screen/ScreenRenderer.php drawing outward from the Screen, src/Screen/
' KeyRouter.php sending each key inward, src/Input/ resolving a key press to a
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/dataflow-tui.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 11 additions & 11 deletions docs/content/ai-agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A form built with this library is self-describing: it hands an AI agent (or any
`agentHelp()` returns a JSON Schema (draft 2020-12) of the answers - the object an agent supplies, keyed by question id. Each property carries its `type` and allowed values (a `select`'s `enum`, a number's `minimum`/`maximum`), its `title` and `description`, its `default`, and the `env` variable that sets it. What it deliberately doesn't name is CLI flags: the flags an agent ultimately calls are yours to define. You retrieve the schema and fold it into your own help - an "AI agents" section of `--help`, a dedicated flag, a generated README:

```php
use DrevOps\Tui\Tui;
use DrevOps\PhpTui\Tui;

// The library hands back the schema; the consumer decides where it goes.
echo (new Tui($form))->agentHelp();
Expand All @@ -29,43 +29,43 @@ The schema the produce-order form emits:
"name": {
"type": "string",
"title": "Order name",
"env": "TUI_NAME"
"env": "PHPTUI_NAME"
},
"fruit": {
"type": "string",
"enum": ["apple", "banana", "cherry"],
"title": "Fruit",
"default": "banana",
"env": "TUI_FRUIT"
"env": "PHPTUI_FRUIT"
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 99,
"title": "Quantity",
"default": 6,
"env": "TUI_QUANTITY"
"env": "PHPTUI_QUANTITY"
},
"organic": {
"type": "boolean",
"title": "Organic only?",
"default": false,
"env": "TUI_ORGANIC"
"env": "PHPTUI_ORGANIC"
},
"certifier": {
"type": "string",
"title": "Certifier",
"x-asked-when": {"field": "organic", "eq": true},
"x-required-when-asked": true,
"env": "TUI_CERTIFIER"
"env": "PHPTUI_CERTIFIER"
}
},
"required": ["name"],
"x-precedence": ["provided", "environment", "discovered", "derived", "default"]
}
```

Each `env` is the variable that sets its answer - the uppercased id under the active prefix, which defaults to `TUI_` and changes with `->envPrefix('MYAPP_')` on the form or `new Tui($form, env_prefix: 'MYAPP_')`. A field that names its own variable advertises that name instead, and any further names it answers to appear beside it in `x-env-aliases` - see [naming the variables](/headless-collection#naming-the-variables). The root `x-precedence` is the resolution order described below.
Each `env` is the variable that sets its answer - the uppercased id under the active prefix, which defaults to `PHPTUI_` and changes with `->envPrefix('MYAPP_')` on the form or `new Tui($form, env_prefix: 'MYAPP_')`. A field that names its own variable advertises that name instead, and any further names it answers to appear beside it in `x-env-aliases` - see [naming the variables](/headless-collection#naming-the-variables). The root `x-precedence` is the resolution order described below.

### Required, and required when asked

Expand All @@ -84,7 +84,7 @@ A field's other two [guidance texts](/field-behaviour#guidance-texts) travel bes
"description": "The crop this basket was picked from.",
"x-help": "Type a few letters to filter.",
"x-placeholder": "E.g. Golden Beetroot",
"env": "TUI_CROP"
"env": "PHPTUI_CROP"
}
```

Expand All @@ -110,7 +110,7 @@ Dynamic defaults - the `fn (Context $c): mixed` closures from [field behavior](/
"options_dynamic": false,
"default": "",
"required": true,
"env": "TUI_NAME",
"env": "PHPTUI_NAME",
"env_aliases": [],
"min": null,
"max": null,
Expand Down Expand Up @@ -176,13 +176,13 @@ $tui->validate(['name' => 'Weekly Box', 'organic' => true]);
The schema's `x-precedence` spells out how every field resolves - the first source that provides a value wins:

1. **Provided** - an explicit value you pass in, however your interface accepts one. Highest precedence.
2. **Environment** - the per-question variable named in `env` (e.g. `TUI_NAME`), which is the uppercased id under the active prefix unless the field named its own, followed by each name in `x-env-aliases` in declaration order; the first one that is set wins.
2. **Environment** - the per-question variable named in `env` (e.g. `PHPTUI_NAME`), which is the uppercased id under the active prefix unless the field named its own, followed by each name in `x-env-aliases` in declaration order; the first one that is set wins.
3. **Discovered** - a value detected from the target directory (see [Discovery](/field-behaviour#discovery)).
4. **Derived** - a value computed from other fields.
5. **Default** - the declared default.

## Runnable example

[`playground/08-headless-agent-cli.php`](https://github.com/drevops/tui/tree/main/playground/08-headless-agent-cli.php) folds `agentHelp()` into a consumer tool's help, and `agentHelp()`, `schema()` and `validate()` each have a script of their own beside it in [`playground/08-headless-*`](https://github.com/drevops/tui/tree/main/playground).
[`playground/08-headless-agent-cli.php`](https://github.com/drevops/phptui/tree/main/playground/08-headless-agent-cli.php) folds `agentHelp()` into a consumer tool's help, and `agentHelp()`, `schema()` and `validate()` each have a script of their own beside it in [`playground/08-headless-*`](https://github.com/drevops/phptui/tree/main/playground).

See also [Headless collection](/headless-collection) for driving the same form from CI and [self-describing answers](/headless-collection#self-describing-answers) for what comes back.
2 changes: 1 addition & 1 deletion docs/content/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl';

# How the TUI works

A walkthrough of `drevops/tui`: what you assemble to build a form, and what happens when it runs. The diagrams are rendered from the PlantUML sources in [`docs/architecture/`](https://github.com/drevops/tui/tree/main/docs/architecture); everything below is derived from `src/`, so if the prose and the code ever disagree, the code wins.
A walkthrough of `drevops/phptui`: what you assemble to build a form, and what happens when it runs. The diagrams are rendered from the PlantUML sources in [`docs/architecture/`](https://github.com/drevops/phptui/tree/main/docs/architecture); everything below is derived from `src/`, so if the prose and the code ever disagree, the code wins.

The model underneath it - four levels, seventeen capabilities, one canonical tree - is written out on the [specification](/specification). This page is the same thing seen from the outside: which class does which part, and in what order.

Expand Down
Loading
Loading