SugarCraft — sweet to build, fun to use

Terminal UIs that taste like candy.

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.

16libraries
10reference apps
68VHS demos
PHP 8.1+fibers / ReactPHP
MITlicensed

The whole Charm stack — in PHP.

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.

CandyCore CandySprinkles HoneyBounce CandyZone SugarBits SugarCharts SugarPrompt CandyShell CandyShine CandyKit CandyFreeze SugarGlow SugarSpark CandyWish SugarWishlist CandyMetrics CandyMold CandyTetris SuperCandy SugarCrush SugarStash CandyQuery SugarTick CandyMines CandyFlip HoneyFlap

Built for the things Symfony Console can't do.

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.

Sixteen libraries, one ecosystem.

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.

CandyCore

Elm-architecture runtime — Model / Msg / Cmd / Program. Cursed cell-diff renderer for SSH bandwidth.

CandySprinkles

Declarative styling + layout. Style, Border, Table, List, Tree, Layout::join/Place, multi-layer Canvas.

HoneyBounce

Damped spring physics + Newtonian projectile sim. The animation engine behind every smooth scroll in the stack.

CandyZone

Mouse-zone tracker. Wrap rendered chunks, get back bounding boxes — clickable regions without coordinate math.

SugarBits

Fourteen components: TextInput, TextArea, ItemList, Table, Viewport, FilePicker, Progress, Spinner, Cursor, Help, Key, Paginator, Stopwatch, Timer.

SugarCharts

Sparkline, Bar, Line, Heatmap, Scatter, TimeSeries, Streamline, Waveline, OHLC, plus Sixel / Kitty / iTerm2 image rendering.

SugarPrompt

Form library — Note, Input, Confirm, Select, MultiSelect, Text, FilePicker, multi-page Groups, six themes.

CandyShell

Composer-installable CLI of thirteen subcommands — choose, confirm, file, filter, format, input, join, log, pager, spin, style, table, write.

CandyShine

Markdown → ANSI renderer. Word-wrap, OSC 8 hyperlinks, syntax highlighting, eight themes, custom themes via JSON.

CandyKit

CLI presentation helpers — StatusLine, Banner, Section, Stage, HelpText. Library-only, no Symfony Console requirement.

CandyFreeze

Code → SVG screenshot. Traffic-light window controls, drop shadow, gutter line numbers, ANSI SGR parsing. No GD / Imagick.

SugarGlow

Markdown CLI viewer. Renders to stdout or opens a fullscreen pager. Eight themes, custom JSON themes, OSC 8 toggle.

SugarSpark

ANSI escape-sequence inspector. Labels SGR / CSI / OSC / DCS / APC / SS3 — feed it stdin, get a human-readable annotation back.

CandyWish

SSH server middleware framework. Logger, Auth, RateLimit, BubbleTea — leans on OpenSSH ForceCommand for the wire protocol.

SugarWishlist

SSH endpoint launcher. YAML / JSON directory of `ssh user@host` shortcuts, interactive picker, pcntl_exec into the chosen ssh.

CandyMetrics

Telemetry primitives. Counter / Gauge / Histogram, four backends (InMemory, JSON, StatsD, Prometheus textfile), CandyWish session middleware.

Apps built on the stack.

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.

CandyMold

starter skeleton

composer create-project candycore/candy-mold my-app — pour your model into the mold and you've got a working app.

CandyTetris

Tetris clone — SRS rules, 7-bag RNG, ghost piece, NES scoring, level-driven gravity. 41 tests / 1535 assertions.

SuperCandy

Dual-pane file manager. Tab-swap focus, multi-select, sort cycling, hidden-file toggle, delete-with-confirm.

SugarCrush

AI coding-assistant chat shell. Pluggable Backend interface — wire to Anthropic / OpenAI / Ollama via a wrapper script.

SugarStash

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.

CandyQuery

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.

SugarTick

Privacy-first coding-time tracker — JSONL on disk, SugarCharts-driven dashboard. No cloud, no MongoDB, bring-your-own-storage stays local.

CandyMines

Minesweeper — customisable board, recursive flood-fill, win/lose detection. Pure-state Board; first-click safety; deterministic-RNG injectable for tests.

CandyFlip

ASCII GIF viewer — ext-gd decode, downsample to a cell grid, render as ANSI 24-bit blocks or a luminance-ramp ASCII rendering.

HoneyFlap

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.

Five layers, no surprises.

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.

1. Foundation
CandyCore::Util — Ansi, Color, ColorProfile, Width, Tty. Depended on by everything below.
2. Render + physics
CandySprinkles (lipgloss), HoneyBounce (harmonica). Pure rendering / pure math — no I/O.
3. Runtime
CandyCore (bubbletea) — Model / Msg / Cmd / Program. Hooks Phase 1 + 2 onto a ReactPHP loop.
4. Components
SugarBits (bubbles), SugarCharts (ntcharts), SugarPrompt (huh), CandyZone (bubblezone). Composable widgets that drop into your Model.
5. Apps + tooling
CandyShell (gum), CandyShine (glamour), CandyKit (fang), CandyFreeze (freeze), SugarGlow (glow), SugarSpark (sequin), CandyWish (wish), SugarWishlist (wishlist), CandyMetrics (promwish), and the reference apps — CandyMold, CandyTetris, SuperCandy, SugarCrush.

Async-first

ReactPHP event loop under the hood. Cmds run as fibers; the render thread never blocks on I/O.

📐

Immutable values

Every Model, Style, Border, Theme is a readonly DTO. with*() returns a new instance — no hidden mutations.

🧪

Testable by design

Pure update functions and snapshot ANSI tests. No tmp dirs, no mocks for the renderer — just assert against the frame.

🎨

One styling vocabulary

Every package speaks Sprinkles\Style. Mix CandyShine output into a SugarBits Viewport, wrap that with a CandySprinkles Border — they all compose.

🔐

SSH the easy way

CandyWish leans on ForceCommand. sshd handles auth, ciphers, fail2ban, audit logs — you get a clean middleware programming model.

📦

No native extensions

Pure-PHP base flow. ext-pcntl, ext-gd, ext-ssh2 are optional for niche features.

See them run.

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.

From 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.

1. Install

composer require candycore/candycore

2. Write the model

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();

3. Run it

php counter.php

Want a project skeleton instead?

composer create-project candycore/candy-mold my-app scaffolds a runnable counter app with PHPUnit wired up — see CandyMold.

Why pick SugarCraft?

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)