candycore/candy-mold

Skeleton repo for bootstrapping a SugarCraft TUI app — `composer create-project sugarcraft/candy-mold my-app`. Ships a runnable counter Model wired through Program, plus a docs-as-code walkthrough that maps every file to the concept it demonstrates.

Maintainers

Package info

github.com/sugarcraft/candy-mold

Documentation

Type:project

pkg:composer/candycore/candy-mold

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v0.2.0 2026-05-07 01:29 UTC

This package is not auto-updated.

Last update: 2026-05-07 16:12:34 UTC


README

candy-mold

CandyMold

CI codecov Packagist Version License PHP

demo

Skeleton repo for bootstrapping a SugarCraft TUI app. Pour your model into the mold and you've got a working app.

composer create-project sugarcraft/candy-mold my-app
cd my-app
./bin/start

and you'll see a working counter. Replace src/Counter.php with your own Model, keep editing.

What you get

my-app/
├── composer.json     # requires candy-core + candy-sprinkles
├── phpunit.xml
├── bin/start         # entry point — runs Program(new Counter())
├── src/
│   └── Counter.php   # demo Model with up/down/quit, styled border
└── tests/
    └── CounterTest.php

bin/start is just three meaningful lines: load the autoloader, instantiate your Model, hand it to Program::run(). The Program harness owns the event loop, render tick, signal handling, raw-mode setup, and alt-screen lifecycle — you only write Models.

Anatomy of a SugarCraft Model

A Model is three pure methods:

public function init(): ?\Closure;            // optional startup Cmd (timers, fetch...)
public function update(Msg $msg): array;       // [nextModel, ?Cmd]
public function view(): string;                // current frame

The shape is borrowed verbatim from Bubble Tea / The Elm Architecture. State lives on the value object, transitions are pure functions, side effects (timers, HTTP, file I/O) get scheduled as Cmds rather than executed inline.

update() always returns a new Model rather than mutating $this. That's why the demo declares public readonly int $n — the only way to "change" the count is to construct a fresh Counter with the new value.

Common next steps

Want to… Reach for…
Add a text input sugarcraft/sugar-bitsTextInput
Show a spinner while loading sugarcraft/sugar-bitsSpinner
Render Markdown help text sugarcraft/candy-shineRenderer
Tail a log into a scrollable pane sugarcraft/sugar-bitsViewport
Build a multi-page wizard sugarcraft/sugar-promptGroup
Plot a sparkline sugarcraft/sugar-chartsSparkline
Make it ssh-accessible sugarcraft/candy-wish

Add the dep, import its classes, return them from view(). They're all pure renderers on the same Style-based vocabulary.

Testing

composer install
vendor/bin/phpunit

The included tests/CounterTest.php shows how to test update() deterministically by constructing Msg objects directly. No event loop, no terminal, no mocking — just call methods and assert the returned tuple.

License

MIT.