In-memory virtual terminal emulator
Feed an ANSI byte stream in, query a cell grid + cursor + mode state out — without ever spawning a real terminal. Built on a Paul-Williams VT500 state machine that mirrors charmbracelet/x/ansi/parser.
composer require sugarcraft/candy-vt
use SugarCraft\Vt\Terminal\Terminal;
$term = Terminal::create(cols: 80, rows: 24);
$term->feed("\x1b[1;31mHello\x1b[0m world");
$screen = $term->screen();
$screen->cell(0, 0)->grapheme; // 'H'
$screen->cell(0, 0)->sgr->bold; // true
$screen->cell(0, 0)->foreground(); // 16-color red
$term->feed("\x1b]2;Demo\x07");
$term->windowTitle(); // 'Demo'
$term->feed("\x1b[?1049h");
$term->mode()->altScreen; // true
feed() calls and CSI subparameters (: separator per VT500 spec).Cell::$combining so composed graphemes render correctly in a single column.Screen::scrollback(). Configure size with Terminal::withScrollbackSize(). Erased by CSI 3 J.view() through CandyVt and assert on cell-grid state instead of raw byte strings.feed() input and replay deterministically.candy-wish to run TUIs without a real PTY.| Class | Method | Description |
|---|---|---|
| Terminal | create(cols, rows) | Create a virtual terminal |
| Terminal | feed(bytes) | Feed ANSI bytes to terminal |
| Terminal | screen() | Get current cell grid |
| Terminal | cursor() | Get cursor position |
| Terminal | mode() | Get terminal mode state (includes autoWrap) |
| Terminal | windowTitle() | Get window title from OSC |
| Terminal | withScrollbackSize(n) | New terminal with scrollback buffer size N |
| Screen | cell(col, row) | Get cell at position |
| Screen | scrollback() | Get scrollback buffer (ring buffer of scrolled-off rows) |
| Cell | grapheme, sgr, combining, foreground(), background() | Cell content, style, and combining marks |
| Cell | withCombining(string) | New cell with additional combining marks appended |
| Mode | autoWrap, syncUpdate, withAutoWrap(bool) | DECAWM auto-wrap and synchronized-output (DEC 2026) state |
| Sgr | underlineStyle, withUnderlineStyle(UnderlineStyle) | SGR underline style (None/Single/Double/Curly/Dotted/Dashed) |
| UnderlineStyle | None / Single / Double / Curly / Dotted / Dashed | Enum — CSI 4:N map (4:0–4:5) |
VHS-recorded GIFs of every example shipped with the library. Regenerated automatically on every push that touches the source.