artisan-build / parfait
A framework-agnostic, component-based TUI layout and rendering system for PHP terminal applications
Requires
- php: ^8.2
- soloterm/grapheme: ^1.0
Requires (Dev)
- laravel/pint: ^1.18
- pestphp/pest: ^3.0
- phpstan/phpstan: ^2.0
This package is auto-updated.
Last update: 2026-06-03 19:41:15 UTC
README
A framework-agnostic, component-based TUI layout and rendering system for PHP terminal applications.
Why Parfait?
Building a terminal UI usually means hand-managing ANSI codes, cursor math, and Unicode width edge cases. Parfait gives you a declarative, component-based API instead โ you describe the UI as a tree of components, and Parfait renders it to a string.
- ๐งฉ Components โ
Panel,Modal,Accordion,FileTree,SelectList,NodeCanvas,Text, and more, each with a fluent API. - ๐ Layouts โ compose
Layout::columns()/Layout::rows()with ratio-based sizing. - ๐จ 256-color theming โ a semantic
Colorenum that emits correct ANSI codes. - ๐ Unicode-correct width โ emoji, CJK, combining marks, and ANSI escapes are measured accurately.
- ๐ช Portals โ dropdowns, modals, and toasts render onto higher layers instead of being clipped.
- ๐งช Effortless testing โ components are pure functions: state in โ
render()โ string out. No terminal required. - ๐ชถ Zero framework dependencies โ needs only PHP 8.2+ and
soloterm/grapheme.
Parfait layers cleanly on top of Laravel Prompts for input/render loops, but depends on neither Laravel nor Prompts โ you can drive it from any PHP terminal program.
Requirements
- PHP
^8.2 ext-mbstringandext-intl(for grapheme-aware width)
Installation
composer require artisan-build/parfait
Quick Start
Every component knows the space it has via a Container, and renders to a
string you print yourself:
use ArtisanBuild\Parfait\Components\Panel; use ArtisanBuild\Parfait\Components\Text; use ArtisanBuild\Parfait\Enums\Color; use ArtisanBuild\Parfait\Support\Container; $panel = Panel::make('welcome') ->bordered() ->title('Parfait') ->body(Text::make('greeting', 'Welcome to the TUI!')->fg(Color::Cyan)) ->setContainer(new Container(width: 40, height: 5)); echo $panel->render();
โญโโ Parfait โโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โWelcome to the TUI! โ
โ โ
โ โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
Composing a layout
Layout arranges child components and hands each one a correctly-sized
Container:
use ArtisanBuild\Parfait\Components\Panel; use ArtisanBuild\Parfait\Components\Text; use ArtisanBuild\Parfait\Layouts\Layout; use ArtisanBuild\Parfait\Support\Container; $ui = Layout::columns(1, 3) // sidebar : main = 1 : 3 ->add(Panel::make('sidebar')->sidebar()->title('Files')) ->add(Panel::make('main')->bordered()->body(Text::make('body', 'Editor'))) ->setContainer(new Container(width: 100, height: 30)); echo $ui->render();
Components
| Component | Purpose |
|---|---|
Text / Stack |
Inline styled text and vertical content composition |
Panel |
Bordered / sidebar / minimal containers with header, body, footer |
Modal |
Overlay dialogs with shadow rendering (via portals) |
Toast |
Transient notifications |
Accordion |
Expandable tree-style navigation |
SelectList |
Scrollable, selectable lists |
FileTree |
Filesystem browser with search and exclude patterns |
Input / TextArea |
Single- and multi-line text entry |
CommandLine |
Command-palette style input |
ProgressBar |
Determinate progress display |
StatusBar / AppHeader |
Chrome for the top and bottom of the screen |
NodeCanvas |
Node-graph visualization with automatic layout |
Each component lives in src/Components/ alongside a co-located *Renderer
where rendering is non-trivial โ see Architecture.
Architecture
Parfait uses a co-located renderer pattern:
Component.phpholds state and props (public properties, fluent setters).ComponentRenderer.phpturns that state into a string.
This keeps rendering logic isolated and makes components trivial to test โ
construct one, set its container, assert on the rendered string. The base
Component provides styling helpers (applyStyle, width/truncate utilities),
view-state storage, and the portal hook.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Your program (owns data, handles events) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Parfait (layout + components โ string) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Terminal โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
See DESIGN.md for the full design rationale.
Documentation
The docs/ directory covers each subsystem in depth:
Testing
Components are pure functions, so tests need no terminal or framework:
use ArtisanBuild\Parfait\Components\Text; use ArtisanBuild\Parfait\Enums\Color; it('styles text', function () { expect(Text::make('t', 'hi')->fg(Color::Cyan)->render()) ->toBe("\e[38;5;81mhi\e[0m"); });
Run the suite:
composer test # pest composer analyse # phpstan composer lint # pint (use lint:test to check without fixing)
Contributing
Contributions are welcome โ see CONTRIBUTING.md for the dev setup, coding standards, and quality gates.
License
Parfait is open-source software licensed under the MIT license.
