artisan-build/parfait

A framework-agnostic, component-based TUI layout and rendering system for PHP terminal applications

Maintainers

Package info

github.com/artisan-build/parfait

pkg:composer/artisan-build/parfait

Statistics

Installs: 1

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-06-03 19:15 UTC

This package is auto-updated.

Last update: 2026-06-03 19:41:15 UTC


README

Parfait

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 Color enum 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-mbstring and ext-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.php holds state and props (public properties, fluent setters).
  • ComponentRenderer.php turns 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:

  1. Introduction
  2. Components
  3. Layouts
  4. Styles
  5. Navigation
  6. View State
  7. Events
  8. Testing
  9. Portals

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.