SugarCraft is the PHP port of the Charmbracelet TUI ecosystem — sixteen libraries and four reference apps. Elm-architecture runtime, declarative styling, animation, charts, forms, SSH-served apps, telemetry, file managers, an AI chat shell. Composer-installable, PHP 8.1+, fibers, async on ReactPHP.
Click an icon to jump to its detail page. Every package is published independently, ships its own examples and PHPUnit suite, and shares a single styling vocabulary so they compose without glue code.
Persistent render loops, mouse + keyboard, alt-screen, async work without blocking the UI, multi-tenant SSH-served TUIs, charts and sparklines, animated spinners, multi-page forms, Markdown rendered straight to ANSI. The boring parts of CLI tooling — argv parsing, exit codes — pair perfectly with whatever Console framework you already use.
Each library has its own composer package, README, examples, and PHPUnit suite. Pull just the ones you need — or grab the umbrella metapackage and get the lot.
Elm-architecture runtime — Model / Msg / Cmd / Program. Cursed cell-diff renderer for SSH bandwidth.
Declarative styling + layout. Style, Border, Table, List, Tree, Layout::join/Place, multi-layer Canvas.
Damped spring physics + Newtonian projectile sim. The animation engine behind every smooth scroll in the stack.
Mouse-zone tracker. Wrap rendered chunks, get back bounding boxes — clickable regions without coordinate math.
Fourteen components: TextInput, TextArea, ItemList, Table, Viewport, FilePicker, Progress, Spinner, Cursor, Help, Key, Paginator, Stopwatch, Timer.
Sparkline, Bar, Line, Heatmap, Scatter, TimeSeries, Streamline, Waveline, OHLC, plus Sixel / Kitty / iTerm2 image rendering.
Form library — Note, Input, Confirm, Select, MultiSelect, Text, FilePicker, multi-page Groups, six themes.
Composer-installable CLI of thirteen subcommands — choose, confirm, file, filter, format, input, join, log, pager, spin, style, table, write.
Markdown → ANSI renderer. Word-wrap, OSC 8 hyperlinks, syntax highlighting, eight themes, custom themes via JSON.
CLI presentation helpers — StatusLine, Banner, Section, Stage, HelpText. Library-only, no Symfony Console requirement.
Code → SVG screenshot. Traffic-light window controls, drop shadow, gutter line numbers, ANSI SGR parsing. No GD / Imagick.
Markdown CLI viewer. Renders to stdout or opens a fullscreen pager. Eight themes, custom JSON themes, OSC 8 toggle.
ANSI escape-sequence inspector. Labels SGR / CSI / OSC / DCS / APC / SS3 — feed it stdin, get a human-readable annotation back.
SSH server middleware framework. Logger, Auth, RateLimit, BubbleTea — leans on OpenSSH ForceCommand for the wire protocol.
SSH endpoint launcher. YAML / JSON directory of `ssh user@host` shortcuts, interactive picker, pcntl_exec into the chosen ssh.
Telemetry primitives. Counter / Gauge / Histogram, four backends (InMemory, JSON, StatsD, Prometheus textfile), CandyWish session middleware.
Real apps that exercise the libraries end-to-end — install via Composer, runnable as a CLI binary. Each one has a detail page below with controls, architecture, and demo recordings.
composer create-project candycore/candy-mold my-app — pour your model into the mold and you've got a working app.
Tetris clone — SRS rules, 7-bag RNG, ghost piece, NES scoring, level-driven gravity. 41 tests / 1535 assertions.
Dual-pane file manager. Tab-swap focus, multi-select, sort cycling, hidden-file toggle, delete-with-confirm.
AI coding-assistant chat shell. Pluggable Backend interface — wire to Anthropic / OpenAI / Ollama via a wrapper script.
Three-pane git TUI — status / branches / log, single-key stage / unstage. Shells out to git for every mutation so your hooks + signing config keep working.
SQLite browser TUI — list tables, peek at row data, run ad-hoc queries. SQLite-only at v1; multi-driver is a one-class follow-up.
Privacy-first coding-time tracker — JSONL on disk, SugarCharts-driven dashboard. No cloud, no MongoDB, bring-your-own-storage stays local.
Minesweeper — customisable board, recursive flood-fill, win/lose detection. Pure-state Board; first-click safety; deterministic-RNG injectable for tests.
ASCII GIF viewer — ext-gd decode, downsample to a cell grid, render as ANSI 24-bit blocks or a luminance-ramp ASCII rendering.
Flappy-Bird in your terminal — bird motion is a HoneyBounce projectile, pipes scroll left at a fixed cell rate, collision is per-cell. PRNG-injectable for deterministic tests.
SugarCraft is the same layered architecture as the Charm stack, translated package-by-package. The deps map cleanly so you can grab as much or as little as you need.
ReactPHP event loop under the hood. Cmds run as fibers; the render thread never blocks on I/O.
Every Model, Style, Border, Theme is a readonly DTO. with*() returns a new instance — no hidden mutations.
Pure update functions and snapshot ANSI tests. No tmp dirs, no mocks for the renderer — just assert against the frame.
Every package speaks Sprinkles\Style. Mix CandyShine output into a SugarBits Viewport, wrap that with a CandySprinkles Border — they all compose.
CandyWish leans on ForceCommand. sshd handles auth, ciphers, fail2ban, audit logs — you get a clean middleware programming model.
Pure-PHP base flow. ext-pcntl, ext-gd, ext-ssh2 are optional for niche features.
VHS-recorded GIFs of every interactive demo, regenerated on every push that touches the corresponding source. Sixty-plus demos in total — these are the headliners; click into any library page for its full set.
composer require to a working counter in ten lines.
Install the umbrella, drop the snippet below into a file,
php counter.php. ↑ ↓ to count, q to quit.
composer require candycore/candycore
use CandyCore\Core\{Cmd, KeyType, Model, Msg, Program};
use CandyCore\Core\Msg\KeyMsg;
final class Counter implements Model {
public function __construct(public readonly int $n = 0) {}
public function init(): ?\Closure { return null; }
public function update(Msg $msg): array {
if ($msg instanceof KeyMsg && $msg->rune === 'q') return [$this, Cmd::quit()];
return [match (true) {
$msg instanceof KeyMsg && $msg->type === KeyType::Up => new self($this->n + 1),
$msg instanceof KeyMsg && $msg->type === KeyType::Down => new self($this->n - 1),
default => $this,
}, null];
}
public function view(): string { return "n = {$this->n}\n↑ ↓ to count, q to quit\n"; }
}
(new Program(new Counter()))->run();
php counter.php
composer create-project candycore/candy-mold my-app
scaffolds a runnable counter app with PHPUnit wired up — see
CandyMold.
Three flavours of "build a TUI in PHP" — pick the row that matches your project's needs. (Full reasoning on the comparison page.)
| Capability | SugarCraft | Symfony Console | readline / raw |
|---|---|---|---|
| Elm-architecture runtime (Model / Msg / Cmd) | ✓ | — | — |
| Async event loop (fibers + ReactPHP) | ✓ | partial | — |
| Declarative styling (CSS-like flexbox / borders) | ✓ | manual | — |
| Composable form library | ✓ (SugarPrompt) | — | — |
| Mouse + key bindings | ✓ | — | — |
| SSH-served apps (multi-tenant) | ✓ (CandyWish) | — | — |
| Charts / sparklines / heatmaps | ✓ (SugarCharts) | — | — |
| One-shot CLI prompts (gum-style) | ✓ (CandyShell) | primitive | — |
| Markdown → ANSI rendering | ✓ (CandyShine) | — | — |
| Code → SVG screenshots | ✓ (CandyFreeze) | — | — |
| Telemetry primitives (StatsD / Prom) | ✓ (CandyMetrics) | — | — |