# How Grafloria works

Grafloria separates diagram data from diagram behavior: a headless model stores the document, an engine applies behavior, and a framework binding mounts the result.

```mermaid
flowchart TB
  S["Node and edge specs"] --> B["Framework binding"]
  B --> I["DiagramInstance"]
  I --> M["DiagramModel\ndata"]
  I --> E["DiagramEngine\nbehavior"]
  M --> R["Renderer\npixels and export"]
  E --> R
  E --> H["Command history"]
```

## One model, every binding

The engine is headless, so the same document model drives JavaScript, React, Vue, Angular, and Qwik. Each binding converts its component or element props into model objects; the renderer then turns those objects into the visible diagram. Learn the model and the instance once, then apply the same ideas through the binding for your framework.

The [React quick start](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/react-quick-start) covers mounting the flow and choosing its state-ownership pattern; this page adds the path from binding props to model objects and rendered pixels. Give the canvas a resolved height so it has room to paint.

```tsx
import { GrafloriaFlow } from '@grafloria/react';
import { LIGHT_THEME } from '@grafloria/renderer';
import type { DiagramInstance, EdgeSpec, NodeSpec } from '@grafloria/renderer';
import type { ReactElement } from 'react';

const nodes: NodeSpec[] = [
  { id: 'extract', position: { x: 0, y: 0 }, label: 'Extract' },
  { id: 'transform', position: { x: 240, y: 0 }, label: 'Transform' },
];

const edges: EdgeSpec[] = [
  { id: 'extract-transform', source: 'extract', target: 'transform' },
];

export function PipelineDiagram(): ReactElement {
  const onInit = (instance: DiagramInstance): void => {
    instance.fitView(24);
  };

  return (
    <div style={{ height: '320px', width: '640px' }}>
      <GrafloriaFlow
        defaultNodes={nodes}
        defaultEdges={edges}
        theme={LIGHT_THEME}
        fitView
        onInit={onInit}
      />
    </div>
  );
}
```

This mounts two labelled nodes and a link, then frames them in the canvas. The `onInit` callback receives the same instance that the other bindings expose through their ready or init mechanism. The Vue, Angular, Qwik, and JavaScript surfaces change how you mount and bind values; they still hand the same kinds of specs to the same model and renderer layers.

## The instance is the facade

[`DiagramInstance`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-renderer-instance-diagraminstance#diagraminstance) is the first object to use after mounting. It covers the rendered surface: reconcile nodes, edges, and groups with `setNodes()`, `setEdges()`, and `setGroups()`; subscribe with `on()`; frame content with `fitView()`; repaint with `render()` or `renderNow()`; and export with `export()` or the synchronous SVG and PDF methods.

The instance exposes the two layers below it:

- [`DiagramModel`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-models-diagrammodel#diagrammodel) owns data: node, link, group, and stroke collections, plus document queries and serialization-related state.
- [`DiagramEngine`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-engine#diagramengine) owns behavior: interaction, validation, layout, routing, and command execution.

Use the instance for specs and pixels, the model for data queries, and the engine for behavior. For example, `instance.getModel()` returns the document model and `instance.getEngine()` returns the behavior engine. `setNodes()` and `setEdges()` reconcile by id: existing ids keep their live objects and listeners, new ids are created, and missing ids are removed. If externally edited data reuses ids and must replace every live object, clear the relevant collection before applying it.

`undo()` is not a renderer-instance method. Call `instance.getEngine().undo()`. Angular's canvas component mirrors the operation, but React and Vue reach it through the instance.

## The document is the API

The document contains nodes, links, groups, and viewport state. Specs are the input representation; live models are the objects the engine and renderer operate on. This separation lets a document round-trip through framework bindings, persistence, export, and collaboration without making the framework component the source of truth.

Mermaid-compatible text is a second, human-writable representation. `instance.exportText()` produces text with the lossless Grafloria sidecar by default, and `instance.loadText(text)` parses and reconciles it into the mounted canvas. [`importDiagramText`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-serialization#importdiagramtext) also parses diagram text when you need the engine's import result directly. Pure Mermaid text is a best-effort boundary; the sidecar preserves positions and styling that Mermaid alone cannot express.

Groups are part of the document rather than a property of a node or edge. A [`GroupModel`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-models-groupmodel#groupmodel) stores membership and the zone's geometry, so pass groups as well as nodes and edges when you need containment to survive a round trip.

## Ports make connections legal

Nodes receive deterministic default ports for ordinary flowcharts. When a diagram declares ports, [`NodeModel`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-models-nodemodel#nodemodel) exposes them through `getPorts()` and related queries, while each [`PortModel`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-models-portmodel#portmodel) carries connection intent.

A port can declare whether it accepts input, output, or both; its side and position; allowed data types; incoming and outgoing caps; whether it can start or end a link; and whether self-links or duplicate links are allowed. The engine combines that port configuration with connection anatomy and any registered validator. During a connection gesture it highlights valid targets and refuses connections that violate the resulting rules. `getAvailablePorts()` and `canConnectTo()` let code inspect the same constraints before presenting an action.

## One command history for user edits

User gestures become command objects on one shared history. Dragging, connecting, deleting, pasting, and grouping therefore use the same keyboard undo and redo behavior. The framework binding re-emits the changed models after history changes, so application state follows the engine's history rather than maintaining a second stack.

Keep the intent distinction clear:

- Build, load, import, or synchronize a document with direct model mutations such as `diagram.addNode(node)` and `diagram.addLink(link)`. Setup is not a user edit, so it does not enter history.
- Change a diagram on the user's behalf through the engine's command manager. [`CommandManager`](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/grafloria-engine-commands-classes-a-r#commandmanager) executes commands asynchronously, and `undo()` and `redo()` operate on the same stack as gestures.

The command manager can batch several commands into one history entry and can refuse a command before it enters history. Use the engine's command path for toolbar actions and automated edits that the user must be able to undo.

## Choose the layer

Start with the framework component and its props. Capture the instance in the binding's init or ready callback when you need rendering, events, export, or reconciliation. Reach into the model for document queries, and into the engine for layout, validation, interaction configuration, or history. This keeps the application on the public facade until it needs a specific lower-layer capability.

Continue with [the model and documents](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/model-and-documents), [the instance and lifecycle](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/instance-and-lifecycle), [ports and connection rules](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/ports-and-connection-rules), or [commands and shared history](https://bench-grafloria-56.atloria.app/p/bench-grafloria-56-62cg8vR5rF/developer/commands-and-shared-history).
