โ† All libraries

SugarBits

๐Ÿง SugarBits

Fifteen pre-built TUI components

port of bubbles components widgets forms input

TextInput, TextArea, ItemList, Table, Tree, Viewport, FilePicker, Progress, Spinner, Cursor, Help, Key, Paginator, Stopwatch, Timer. Everything you'd hand-roll on day one of a TUI project, ready to drop into your Model.

Install

composer require sugarcraft/sugar-bits

Quickstart

use SugarCraft\Bits\TextInput\TextInput;
use SugarCraft\Bits\Spinner\Spinner;
use SugarCraft\Core\{Cmd, Model, Msg, Program};
use SugarCraft\Core\Msg\KeyMsg;
use SugarCraft\Core\KeyType;

final class Search implements Model {
    public function __construct(
        public readonly TextInput $ti,
        public readonly Spinner $sp,
    ) {}

    public function init(): ?\Closure { return $this->sp->tick(); }

    public function update(Msg $msg): array {
        if ($msg instanceof KeyMsg && $msg->type === KeyType::Enter) {
            return [$this, Cmd::quit()];
        }
        [$ti, $cmd]  = $this->ti->update($msg);
        [$sp, $cmd2] = $this->sp->update($msg);
        return [new self($ti, $sp), Cmd::batch($cmd, $cmd2)];
    }

    public function view(): string {
        return $this->sp->view().' '.$this->ti->view();
    }
}

What's in the box

Spinner12 styles โ€” line, dot, miniDot, points, pulse, globe, meter, jump, moon, monkey, hamburger, ellipsis.
ProgressStatic Progress + spring-physics AnimatedProgress driven by HoneyBounce.
TextInput / TextAreaSingle + multi-line. Autocomplete, validators, ValidateOn timing control, keystroke restrict pattern filter, line numbers, soft-wrap, dynamic-height growth (mirrors upstream bubbles #910).
ItemListSelectable, scrollable, filterable. Status messages, custom delegates.
TableSelectable data table with Column DTOs, header highlight, vertical nav, per-cell styleFunc(row, col) => Style (mirrors bubbles #246), multi-column sort with withSort() / thenSortBy() / clearSort(), filtering with withFilterable() / withFilter() / withFilterPredicate() (default: case-insensitive substring match on all visible columns), and pagination with withPageSize() / getPaginator() / withPage() / nextPage() / prevPage() / pageFirst() / pageLast() โ€” integrates with sort and filter.
TreeInteractive tree view โ€” cursor nav, expand / collapse, viewport scroll, g / G jump (mirrors bubbles #233).
ViewportScrollable text region. Mouse wheel, scrollbar, keyboard nav.
FilePickerDirectory browser with icons, size, sort modes.
Paginator + Help + KeyDot / arabic page indicator, key-help footer, KeyMap binding DSL.

Source & demos

Try the quickstart โ†’

API

ClassMethodDescription
TextInputnew()Create a new text input instance
TextInputupdate(Msg)Handle input events
TextInputwithValidateOn(ValidateOn)Set validation timing โ€” None / Blur / Change / Submit
TextInputwithRestrict(string $pattern)PCRE regex filter โ€” only matching keystrokes are accepted
ValidateOnenumCases: None / Blur / Change / Submit โ€” controls when validation fires
TextAreanew()Create a multi-line text area
Spinnernew(Style)Create spinner with given style
Spinnertick()Return tick command for animation
Tablenew(headers, rows)Create data table
TablewithSort(string $column, SortDirection $dir = Asc)Set primary sort column and direction โ€” clears prior sort chain
TablethenSortBy(string $column, SortDirection $dir = Asc)Add secondary (or further) sort tiebreaker
TableclearSort()Remove all sort criteria, restoring insertion order
TablegetSortState(): SortStateReturn current sort criteria (readonly)
TablewithFilterable(bool)Enable or disable the filter feature
TablewithFilter(string $query)Set filter query string โ€” default: case-insensitive substring match on all visible columns
TablewithFilterPredicate(?Closure(list<string>): bool)Custom filter callable โ€” null restores the default
TablegetFilterable(): boolReturn whether filtering is enabled
TablegetFilter(): stringReturn the current filter query string
TablegetFilterPredicate(): ?ClosureReturn the current custom filter predicate
SortDirectionenumCases: Asc / Desc โ€” use toggle() to flip direction
SortStateDTOImmutable list of (column index, SortDirection) pairs
TablewithPageSize(int $size)Set rows per page โ€” 0 disables pagination, โ‰ฅ1 enables it
TablewithPage(int $page)Navigate to a zero-based page (clamps to valid range)
TablenextPage()Advance one page โ€” returns new Table
TableprevPage()Retreat one page โ€” returns new Table
TablepageFirst()Jump to the first page โ€” returns new Table
TablepageLast()Jump to the last page โ€” returns new Table
TablegetPageSize(): intReturn rows per page (0 = pagination disabled)
TablegetCurrentPage(): intReturn the current zero-based page
TablegetTotalPages(): intReturn total page count (1 when pagination is disabled)
TablegetPaginator(): PaginatorReturn a Paginator wired to the table's current page state
Treenew(root)Create tree view
Viewportnew(content)Create scrollable viewport
Progressnew(ratio)Create progress bar
FilePickernew(directory)Create file picker
ItemListnew(items)Create selectable item list

Demos.

VHS-recorded GIFs of every example shipped with the library. Regenerated automatically on every push that touches the source.

Spinner gallery

Spinner gallery

All 12 spinner styles ticking in parallel.
TextInput

TextInput

Single-line input with autocomplete + placeholder.
TextArea

TextArea

Multi-line editor with line numbers + soft-wrap.
ItemList

ItemList

Filterable scrollable list with status messages.
Table

Table

Selectable data table with column delegates + per-cell styleFunc.
Tree

Tree

Interactive tree โ€” cursor, expand / collapse, scroll.
Viewport

Viewport

Scrollable text region with mouse wheel + scrollbar.
FilePicker

FilePicker

Directory browser with icons + size + sort.
Progress

Progress

Spring-physics-animated progress bar.
Cursor

Cursor

Animated text cursor with BlinkMsg tick.
Help

Help

Short / full key-help footer from a KeyMap.
Paginator

Paginator

Dot + arabic page indicator.
Stopwatch

Stopwatch

Elapsed-time counter.
Timer

Timer

Countdown timer.