# Text and canvas

Grafloria keeps Mermaid-compatible text readable while a lossless sidecar carries the canvas document: semantic node types describe what each entity means, and the sidecar preserves geometry that Mermaid does not express.

## How the parts fit together

```mermaid
flowchart LR
  text["Mermaid-compatible body"] --> parse["Text parser"]
  sidecar["%%grafloria:document sidecar"] --> model["Live diagram model"]
  parse --> model
  model --> canvas["Rendered canvas"]
  model --> export["Text + sidecar"]
```

The Mermaid body carries structure, labels, and diagram direction. Importing it creates typed entities such as `flowchart:process` and `flowchart:decision`; the renderer can therefore choose the corresponding glyphs and layout behavior. The text parser exposes this result through [`importDiagramText`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-engine-serialization#importdiagramtext), whose `diagram` is a live [`DiagramModel`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-engine-models-diagrammodel#diagrammodel).

Grafloria's sidecar is a `%%grafloria:document` comment. Mermaid-compatible renderers ignore it, while Grafloria reads the serialized document from it. Positions, sizes, styles, ports, groups, and viewport data therefore survive an export/import round trip even though they are not part of ordinary Mermaid syntax.

## Put Mermaid text into a live canvas

Use [`render`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-element-core#render) to mount a real canvas, then use the returned [`DiagramInstance`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-renderer-instance-diagraminstance#diagraminstance) to load text into that mounted diagram. `loadText()` reconciles the parsed result into the existing instance; it does not discard the renderer, listeners, plugins, or selection.

```ts
import { render } from '@grafloria/element';

const host = document.createElement('div');
host.id = 'canvas';
host.style.width = '800px';
host.style.height = '400px';
document.body.append(host);

const instance = render(
  {
    nodes: [
      {
        id: 'start',
        label: 'Loading',
        position: { x: 60, y: 80 },
        size: { width: 150, height: 60 },
      },
    ],
    edges: [],
  },
  host,
);

const mermaid = `flowchart
start[Start] --> gate{Gate}
gate --> ship[Ship]`;

const loaded = instance.loadText(mermaid);
if (loaded.unsupported) {
  throw new Error(`Unsupported diagram type: ${loaded.unsupported}`);
}

const firstNode = loaded.diagram.getNodes()[0];
if (!firstNode) {
  throw new Error('The text contains no nodes');
}
instance.renderNow();

const exported = instance.exportText();
const status = document.getElementById('status');
if (status) {
  status.textContent = `${firstNode.type}; sidecar: ${exported.includes('%%grafloria:document')}`;
}
```

With a host such as `<div id="canvas" style="height: 400px"></div>`, the canvas shows `Start`, `Gate`, and `Ship` connected from left to right. The status reports `flowchart:process` for the first node and `sidecar: true`. The initial `Loading` node is reconciled away because the Mermaid text defines the current node set.

## Export and re-import

Call `instance.exportText()` after a user arranges the canvas. Its visible body remains Mermaid-compatible; by default, the output also contains the document sidecar and a body hash. Feeding that string to `loadText()` restores the document in the same live instance, including the canvas positions and other data that the body cannot represent.

When the body hash still matches, the default import preference uses the sidecar. If a person edits the Mermaid body, the returned result sets `bodyEdited` to `true`; the edited structure and labels take effect, while the sidecar remains the base for information the text cannot express. Check `sidecarInvalid` when a sidecar comment exists but its JSON is malformed. For a diagram type Grafloria recognises but does not support, `unsupported` identifies the type instead of silently treating it as another diagram.

The lower-level [`importDiagramText`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-engine-serialization#importdiagramtext) function is useful when parsing must happen before a canvas exists—for example, to inspect `source`, `bodyEdited`, or `unsupported`. Use the instance methods when the result belongs in a mounted canvas.

## What the text does not own

The Mermaid body is the human-readable semantic layer: entities, relationships, labels, shapes, and direction. The sidecar is the fidelity layer: exact model state needed to reproduce the canvas. A pure Mermaid file without a sidecar remains importable, but its layout and other non-Mermaid details follow the lossy text boundary rather than a previous canvas arrangement.

For a saved document whose full model is the source of truth, use the JSON document path and [`fromDocument`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-element-core#fromdocument) rather than projecting the model back into plain node and edge specs. For a text file meant to remain readable in Git and renderable by Mermaid, use the text path described here.

## Related

- [Round-trip Mermaid text](https://atloria.dev/p/grafloria-h7YM7amryF/developer/round-trip-mermaid-text)
- [Model and document](https://atloria.dev/p/grafloria-h7YM7amryF/developer/model-and-document)
- [Auto-layout a diagram](https://atloria.dev/p/grafloria-h7YM7amryF/developer/auto-layout-a-diagram)
- [Save and restore diagrams](https://atloria.dev/p/grafloria-h7YM7amryF/developer/save-and-restore-diagrams)
