# Round-trip Mermaid text

Use Mermaid text as an editable view of a live Grafloria diagram: export the current model, edit the visible Mermaid body, then load it back while retaining model data that Mermaid cannot express, including positions.

## When to use this

Use the instance methods after mounting a canvas. [`DiagramInstance`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-renderer-instance-diagraminstance) is the shared handle for the rendered model. `exportText()` returns Mermaid-compatible text, and `loadText()` parses that text and reconciles it into the same instance.

## Use the framework bindings

Each binding exposes the same instance operations after its component mounts. Keep the instance in the binding’s initialization callback or hook; do not recreate it in a render function.

### JavaScript

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

const stage = document.getElementById('stage');
const source = document.getElementById('source');
const instance = render({
  nodes: [
    { id: 'start', label: 'Start', position: { x: 80, y: 60 }, size: { width: 140, height: 60 } },
    { id: 'work', label: 'Work', position: { x: 320, y: 60 }, size: { width: 140, height: 60 } },
    { id: 'done', label: 'Done', position: { x: 560, y: 60 }, size: { width: 140, height: 60 } },
  ],
  edges: [
    { id: 'e1', source: 'start', target: 'work' },
    { id: 'e2', source: 'work', target: 'done' },
  ],
}, stage);

instance.renderNow();
source.value = instance.exportText();
document.getElementById('load').addEventListener('click', () => {
  instance.loadText(source.value);
  instance.renderNow();
});
```

```html
<div id="stage" style="height:400px"></div>
<textarea id="source" style="width:100%;height:220px"></textarea>
<button id="load">Load</button>
```

The canvas shows **Start → Work → Done**, and the textarea contains the exported text. Edit the textarea and choose **Load** to apply the text to the same mounted instance.

### Angular

```ts
import { AfterViewInit, Component, viewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { DiagramCanvasComponent } from '@grafloria/angular';

@Component({
  standalone: true,
  imports: [DiagramCanvasComponent, FormsModule],
  template: `
    <div style="height:400px"><grafloria-diagram-canvas [nodes]="nodes" [edges]="edges" /></div>
    <textarea [(ngModel)]="text" style="width:100%;height:220px"></textarea>
    <button (click)="exportText()">Export</button><button (click)="loadText()">Load</button>
  `,
})
export class MermaidTextComponent implements AfterViewInit {
  canvas = viewChild.required(DiagramCanvasComponent);
  text = '';
  nodes = [
    { id: 'start', position: { x: 80, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Start' } },
    { id: 'work', position: { x: 320, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Work' } },
    { id: 'done', position: { x: 560, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Done' } },
  ];
  edges = [{ id: 'e1', source: 'start', target: 'work' }, { id: 'e2', source: 'work', target: 'done' }];
  exportText(): void { this.text = this.canvas().exportText(); }
  loadText(): void { this.canvas().loadText(this.text); }
  ngAfterViewInit(): void { this.exportText(); }
}
```

The canvas renders **Start → Work → Done**. **Export** fills the textarea; edit it and choose **Load** to reconcile the edit into the mounted canvas.

### Qwik

```tsx
import { component$, $, noSerialize, useSignal, type NoSerialize } from '@builder.io/qwik';
import { GrafloriaFlow, type DiagramInstance, type NodeSpec, type EdgeSpec } from '@grafloria/qwik';

const nodes: NodeSpec[] = [
  { id: 'start', position: { x: 80, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Start' } },
  { id: 'work', position: { x: 320, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Work' } },
  { id: 'done', position: { x: 560, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Done' } },
];
const edges: EdgeSpec[] = [{ id: 'e1', source: 'start', target: 'work' }, { id: 'e2', source: 'work', target: 'done' }];

export default component$(() => {
  const instance = useSignal<NoSerialize<DiagramInstance>>();
  const text = useSignal('');
  return <div style={{ height: '400px' }}><GrafloriaFlow defaultNodes={nodes} defaultEdges={edges}
    onInit$={$((api: DiagramInstance) => { instance.value = noSerialize(api); text.value = api.exportText(); })} />
    <button onClick$={() => { if (instance.value) text.value = instance.value.exportText(); }}>Export</button>
    <button onClick$={() => { instance.value?.loadText(text.value); }}>Load</button>
    <textarea value={text.value} onInput$={(_, element) => { text.value = element.value; }} />
  </div>;
});
```

The mounted `GrafloriaFlow` shows the three nodes. **Load** applies edited text; **Export** reads the current model back into the textarea.

### React

```tsx
import { useRef, useState } from 'react';
import { GrafloriaFlow, type DiagramInstance, type NodeSpec, type EdgeSpec } from '@grafloria/react';

const nodes: NodeSpec[] = [
  { id: 'start', position: { x: 80, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Start' } },
  { id: 'work', position: { x: 320, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Work' } },
  { id: 'done', position: { x: 560, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Done' } },
];
const edges: EdgeSpec[] = [{ id: 'e1', source: 'start', target: 'work' }, { id: 'e2', source: 'work', target: 'done' }];

export default function MermaidTextDemo() {
  const instance = useRef<DiagramInstance | null>(null);
  const [text, setText] = useState('');
  return <div style={{ height: 400 }}><GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} style={{ height: '100%' }}
    onInit={(api) => { instance.current = api; setText(api.exportText()); }} />
    <button onClick={() => { if (instance.current) setText(instance.current.exportText()); }}>Export</button>
    <button onClick={() => { instance.current?.loadText(text); }}>Load</button>
    <textarea value={text} onChange={(event) => setText(event.target.value)} style={{ width: '100%', height: 220 }} />
  </div>;
}
```

The component renders **Start → Work → Done** and keeps the live instance in a ref. Editing the controlled textarea and selecting **Load** changes the mounted diagram.

### Vue

```vue
<script setup lang="ts">
import { ref } from 'vue';
import { GrafloriaFlow, type DiagramInstance, type NodeSpec, type EdgeSpec } from '@grafloria/vue';
const text = ref('');
let instance: DiagramInstance | null = null;
const nodes: NodeSpec[] = [
  { id: 'start', position: { x: 80, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Start' } },
  { id: 'work', position: { x: 320, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Work' } },
  { id: 'done', position: { x: 560, y: 60 }, size: { width: 140, height: 60 }, data: { label: 'Done' } },
];
const edges: EdgeSpec[] = [{ id: 'e1', source: 'start', target: 'work' }, { id: 'e2', source: 'work', target: 'done' }];
function onInit(api: DiagramInstance) { instance = api; text.value = api.exportText(); }
function exportText() { if (instance) text.value = instance.exportText(); }
function loadText() { instance?.loadText(text.value); }
</script>
<template><div style="height:400px"><GrafloriaFlow :default-nodes="nodes" :default-edges="edges" @init="onInit" style="height:100%" />
  <button @click="exportText">Export</button><button @click="loadText">Load</button><textarea v-model="text" style="width:100%;height:220px" /></div></template>
```

The mounted component shows **Start → Work → Done**. `v-model` supplies edited Mermaid text to `loadText()`; `exportText()` reads it back from the same instance.

## Inspect text before mounting

Use [`importDiagramText`](https://atloria.dev/p/grafloria-h7YM7amryF/developer/grafloria-engine-serialization) when you need to parse text before deciding how to mount it. It returns an `ImportTextResult`. The live canvas path remains `loadText()` because it reconciles the result into a rendered instance.

## See it running

Open the [Mermaid text demo](https://grafloria.com/demos/misc/mermaid-text.html) to edit Mermaid text and apply it to the canvas. Its [source](https://github.com/grafloria/grafloria/blob/ef2bcc55237d4d1f1643b6fea68bd996aa37a9e9/demos/misc/mermaid-text.html) follows the same flow.

## Related

- [How Grafloria works](https://atloria.dev/p/grafloria-h7YM7amryF/developer/how-grafloria-works)
- [The model and document](https://atloria.dev/p/grafloria-h7YM7amryF/developer/model-and-document)
- [Style a diagram](https://atloria.dev/p/grafloria-h7YM7amryF/developer/style-a-diagram)
