@ai-gui/plugin-mermaid 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,16 +17,35 @@ import { AIRenderer } from "@ai-gui/react"
17
17
  <AIRenderer plugins={[mermaid({ theme: "default" })]} />
18
18
  ```
19
19
 
20
- The model emits, e.g.:
20
+ The model can emit flowcharts, UML-style class/sequence/state diagrams, ER diagrams, journeys, Gantt charts, mind maps, timelines, Git graphs, and other Mermaid-supported diagram types.
21
+
22
+ Flowchart:
21
23
 
22
24
  ```mermaid
23
25
  graph TD; A-->B; A-->C;
24
26
  ```
25
27
 
28
+ UML class diagram:
29
+
30
+ ```mermaid
31
+ classDiagram
32
+ class Renderer { +push(chunk) +reset() }
33
+ Renderer --> AST : produces
34
+ ```
35
+
36
+ Sequence diagram:
37
+
38
+ ```mermaid
39
+ sequenceDiagram
40
+ LLM->>AIGUI: streamed tokens
41
+ AIGUI-->>App: AST patches
42
+ ```
43
+
26
44
  ## Options
27
45
 
28
46
  - `theme?: string` — Mermaid theme. Mermaid has process-global configuration, so the theme of the first render wins across plugin instances; later instances share that initialization.
47
+ - `maxSourceBytes?: number` — maximum UTF-8 source size, default 64 KiB.
29
48
 
30
- Renders are queued across instances because Mermaid mutates global state while rendering. This also guarantees unique diagram IDs for concurrent renders.
49
+ Renders use Mermaid `securityLevel: "strict"`, are complete-gated, and are queued across instances because Mermaid mutates global state while rendering. Errors use a generic non-reflective fallback and diagram IDs remain unique across concurrent renders.
31
50
 
32
51
  See the [root README](../../README.md) for the full plugin list.
package/dist/index.cjs CHANGED
@@ -14,39 +14,59 @@ function enqueue(work) {
14
14
  function escapeHtml(s) {
15
15
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
16
16
  }
17
- function errorHtml(message) {
17
+ function errorHtml() {
18
18
  return {
19
19
  kind: "html",
20
- html: `<pre data-aigui-mermaid-error>${escapeHtml(message)}</pre>`
20
+ html: `<pre data-aigui-mermaid-error>${escapeHtml("Diagram could not be rendered.")}</pre>`
21
21
  };
22
22
  }
23
+ function mermaidPromptSpec() {
24
+ return [
25
+ "Diagrams (fenced): ```mermaid <Mermaid diagram syntax>```.",
26
+ "Supported examples include flowchart, sequenceDiagram, classDiagram (UML), stateDiagram-v2, erDiagram, journey, gantt, pie, mindmap, timeline, and gitGraph.",
27
+ "Use concise labels and valid Mermaid syntax. Never emit HTML, scripts, click handlers, URLs, initialization directives, remote resources, or credentials."
28
+ ].join("\n");
29
+ }
23
30
  function mermaid(opts = {}) {
24
31
  const theme = opts.theme ?? "default";
25
- const render = (node) => enqueue(async () => {
26
- try {
27
- const m = await loadMermaid();
28
- if (initializedTheme === void 0) {
29
- m.initialize({
30
- startOnLoad: false,
31
- theme
32
- });
33
- initializedTheme = theme;
32
+ const maxSourceBytes = opts.maxSourceBytes ?? 64 * 1024;
33
+ if (!Number.isSafeInteger(maxSourceBytes) || maxSourceBytes <= 0) throw new TypeError("maxSourceBytes must be a positive safe integer");
34
+ const outputs = new WeakMap();
35
+ const render = (node) => {
36
+ const cached = outputs.get(node);
37
+ if (cached) return cached;
38
+ const output = enqueue(async () => {
39
+ try {
40
+ if (new TextEncoder().encode(node.content ?? "").byteLength > maxSourceBytes) return errorHtml();
41
+ const m = await loadMermaid();
42
+ if (initializedTheme === void 0) {
43
+ m.initialize({
44
+ startOnLoad: false,
45
+ theme,
46
+ securityLevel: "strict"
47
+ });
48
+ initializedTheme = theme;
49
+ }
50
+ const id = `aigui-mermaid-${nextId++}`;
51
+ const { svg } = await m.render(id, node.content ?? "");
52
+ return {
53
+ kind: "html",
54
+ html: svg
55
+ };
56
+ } catch {
57
+ return errorHtml();
34
58
  }
35
- const id = `aigui-mermaid-${nextId++}`;
36
- const { svg } = await m.render(id, node.content ?? "");
37
- return {
38
- kind: "html",
39
- html: svg
40
- };
41
- } catch (e) {
42
- return errorHtml(String(e?.message ?? e));
43
- }
44
- });
59
+ });
60
+ outputs.set(node, output);
61
+ return output;
62
+ };
45
63
  return {
46
64
  name: "mermaid",
47
- nodeRenderers: { mermaid: render }
65
+ nodeRenderers: { mermaid: render },
66
+ promptSpec: mermaidPromptSpec()
48
67
  };
49
68
  }
50
69
 
51
70
  //#endregion
52
- exports.mermaid = mermaid
71
+ exports.mermaid = mermaid
72
+ exports.mermaidPromptSpec = mermaidPromptSpec
package/dist/index.d.cts CHANGED
@@ -3,8 +3,10 @@ import { AIGuiPlugin } from "@ai-gui/core";
3
3
  //#region src/index.d.ts
4
4
  interface MermaidOptions {
5
5
  theme?: string;
6
+ maxSourceBytes?: number;
6
7
  }
8
+ declare function mermaidPromptSpec(): string;
7
9
  declare function mermaid(opts?: MermaidOptions): AIGuiPlugin;
8
10
 
9
11
  //#endregion
10
- export { MermaidOptions, mermaid };
12
+ export { MermaidOptions, mermaid, mermaidPromptSpec };
package/dist/index.d.ts CHANGED
@@ -3,8 +3,10 @@ import { AIGuiPlugin } from "@ai-gui/core";
3
3
  //#region src/index.d.ts
4
4
  interface MermaidOptions {
5
5
  theme?: string;
6
+ maxSourceBytes?: number;
6
7
  }
8
+ declare function mermaidPromptSpec(): string;
7
9
  declare function mermaid(opts?: MermaidOptions): AIGuiPlugin;
8
10
 
9
11
  //#endregion
10
- export { MermaidOptions, mermaid };
12
+ export { MermaidOptions, mermaid, mermaidPromptSpec };
package/dist/index.js CHANGED
@@ -12,39 +12,58 @@ function enqueue(work) {
12
12
  function escapeHtml(s) {
13
13
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
14
14
  }
15
- function errorHtml(message) {
15
+ function errorHtml() {
16
16
  return {
17
17
  kind: "html",
18
- html: `<pre data-aigui-mermaid-error>${escapeHtml(message)}</pre>`
18
+ html: `<pre data-aigui-mermaid-error>${escapeHtml("Diagram could not be rendered.")}</pre>`
19
19
  };
20
20
  }
21
+ function mermaidPromptSpec() {
22
+ return [
23
+ "Diagrams (fenced): ```mermaid <Mermaid diagram syntax>```.",
24
+ "Supported examples include flowchart, sequenceDiagram, classDiagram (UML), stateDiagram-v2, erDiagram, journey, gantt, pie, mindmap, timeline, and gitGraph.",
25
+ "Use concise labels and valid Mermaid syntax. Never emit HTML, scripts, click handlers, URLs, initialization directives, remote resources, or credentials."
26
+ ].join("\n");
27
+ }
21
28
  function mermaid(opts = {}) {
22
29
  const theme = opts.theme ?? "default";
23
- const render = (node) => enqueue(async () => {
24
- try {
25
- const m = await loadMermaid();
26
- if (initializedTheme === void 0) {
27
- m.initialize({
28
- startOnLoad: false,
29
- theme
30
- });
31
- initializedTheme = theme;
30
+ const maxSourceBytes = opts.maxSourceBytes ?? 64 * 1024;
31
+ if (!Number.isSafeInteger(maxSourceBytes) || maxSourceBytes <= 0) throw new TypeError("maxSourceBytes must be a positive safe integer");
32
+ const outputs = new WeakMap();
33
+ const render = (node) => {
34
+ const cached = outputs.get(node);
35
+ if (cached) return cached;
36
+ const output = enqueue(async () => {
37
+ try {
38
+ if (new TextEncoder().encode(node.content ?? "").byteLength > maxSourceBytes) return errorHtml();
39
+ const m = await loadMermaid();
40
+ if (initializedTheme === void 0) {
41
+ m.initialize({
42
+ startOnLoad: false,
43
+ theme,
44
+ securityLevel: "strict"
45
+ });
46
+ initializedTheme = theme;
47
+ }
48
+ const id = `aigui-mermaid-${nextId++}`;
49
+ const { svg } = await m.render(id, node.content ?? "");
50
+ return {
51
+ kind: "html",
52
+ html: svg
53
+ };
54
+ } catch {
55
+ return errorHtml();
32
56
  }
33
- const id = `aigui-mermaid-${nextId++}`;
34
- const { svg } = await m.render(id, node.content ?? "");
35
- return {
36
- kind: "html",
37
- html: svg
38
- };
39
- } catch (e) {
40
- return errorHtml(String(e?.message ?? e));
41
- }
42
- });
57
+ });
58
+ outputs.set(node, output);
59
+ return output;
60
+ };
43
61
  return {
44
62
  name: "mermaid",
45
- nodeRenderers: { mermaid: render }
63
+ nodeRenderers: { mermaid: render },
64
+ promptSpec: mermaidPromptSpec()
46
65
  };
47
66
  }
48
67
 
49
68
  //#endregion
50
- export { mermaid };
69
+ export { mermaid, mermaidPromptSpec };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-gui/plugin-mermaid",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Mermaid diagram plugin for AIGUI.",
5
5
  "keywords": [
6
6
  "llm",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "mermaid": "^11.4.0",
55
- "@ai-gui/core": "0.3.0"
55
+ "@ai-gui/core": "0.4.1"
56
56
  },
57
57
  "scripts": {
58
58
  "build": "tsdown",