Atelier

State Diagrams

A state diagram is a set of named states and the labeled transitions between them, plus the initial and final pseudo-states.

Order lifecycleIn reviewDraftApprovedRejectedShippedDonesubmitapproverejectshipdeliveramendrevise

Overview

The model lives in Atelier\Diagram\State\: StateDiagram, State (id, label defaulting to the id), and Transition (from, to, optional label). All model classes are immutable.

Reach for it to show a lifecycle or workflow where a subject moves between discrete states: an order moving from draft to fulfilled, a document through review, a connection through its handshake. Use a flowchart instead when you are modeling process steps and decisions rather than the states of a single subject.

Format

State diagrams round-trip through a stateDiagram-v2 subset:

stateDiagram-v2
    direction LR
    state "In review" as Review
    [*] --> Draft
    Draft --> Review : submit
    Review --> Approved : approve
    Approved --> [*]

State ids match [A-Za-z_][A-Za-z0-9_]*. Composite state { } blocks, forks, joins, notes, and concurrency are rejected, never skipped. Full grammar: Mermaid support.

Builder API

StateDiagramBuilder (or Diagram::state(), which returns it) is the one way to build the model:

use Atelier\Diagram\Diagram;
use Atelier\Diagram\Model\Direction;

$diagram = Diagram::state()
    ->direction(Direction::LeftToRight)
    ->title('Order lifecycle')
    ->state('Review', 'In review')              // id + display label
    ->initial('Draft')                          // [*] --> Draft
    ->transition('Draft', 'Review', 'submit')
    ->transition('Review', 'Review', 'amend')   // self-transition
    ->transition('Review', 'Draft', 'reject')
    ->transition('Review', 'Approved', 'approve')
    ->final('Approved')                         // Approved --> [*]
    ->build();

Diagram::of($diagram)->saveSvg('order.svg');
  • state($id, $label) - declares a state. Re-declaring an existing id with a label overrides it; without a label it is a no-op.
  • transition($from, $to, $label) - adds a transition and auto-declares unknown endpoints with the id as label (Mermaid behavior). Declaration order is the layout tie-breaker.
  • initial($to) / final($from) - add transitions from/to the pseudo-states StateDiagram::INITIAL / StateDiagram::FINAL. These ids cannot be declared as regular states; transitions cannot start at FINAL or end at INITIAL.
  • direction($direction) - Direction::TopToBottom (default) or Direction::LeftToRight.
  • title($text) - sets a title (not part of the Mermaid grammar; dropped on serialization).
  • build() - throws InvalidDiagramException when no state was declared.

Options

Setting Default Effect
direction(Direction) TopToBottom ranks states as rows or as columns
title(string) none heading above the diagram
Theme::minNodeWidth / minNodeHeight null floor for state boxes, so a row of states reads evenly
  • Determinism: layout is source-order based and deterministic; the same model always yields the same SVG.

Layout\State\StateLayoutEngine ranks states by longest path from the initial/source states (breaking cycles by ignoring back edges), runs a few barycenter sweeps to reduce crossings, then places ranks as rows or columns. States render as rounded rectangles, the initial pseudo-state as a small filled circle, the final one as a double circle. Edges are straight between adjacent ranks, bowed for back edges and long spans, small arcs for self-loops, with labels over a background halo.

Order lifecycleIn reviewDraftApprovedRejectedShippedDonesubmitapproverejectshipdeliveramendrevise

direction(Direction::LeftToRight) on the same states and transitions. Direction is a layout decision, so the graph is unchanged and only its shape moves.

Themes

State diagrams use nodeFillColor and nodeStrokeColor for the state boxes, nodeStrokeColor for edges and the pseudo-states, textColor for labels, and mutedTextColor for edge-label halos. They do not use accentColors (every state is styled uniformly), so the palette size does not matter here.

All presets apply (Theme::default(), dark(), blueprint(), mono(), neutral()), and a Scene\BackgroundPattern (as in blueprint()) sits behind the states. See Theming.

Parse

Header keyword: stateDiagram-v2 (or stateDiagram).

$diagram = Diagram::fromMermaid($source);        // throws ParseException
$result  = Diagram::tryFromMermaid($source);      // non-throwing ParseResult

toMermaid() / toMarkdown() serialize a built model back; titles are dropped because the grammar has no place for them. Full grammar and round-trip rules: Mermaid support.

Debug

On a rejected source, inspect ParseResult::getDiagnostic() (a ParserDiagnostic with a message, a stable code, and a source span) or catch ParseException (getLineNumber(), getSourceLine()). Render a code frame with ParserDiagnosticFormatter:

$result = Diagram::tryFromMermaid($source);
if ($result->isFailure()) {
    echo \Atelier\Diagram\Parser\Support\ParserDiagnosticFormatter::format($result->getDiagnostic());
}

See parser diagnostics.