zombie-mermaid 5.2.0 → 5.4.0

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
@@ -28,12 +28,25 @@ Ultra-fast, fully themeable, zero DOM dependencies. A maintained fork of [`beaut
28
28
 
29
29
  ## Why This Fork Exists
30
30
 
31
- `beautiful-mermaid` is a genuinely good library — fast, beautiful, and works everywhere from rich UIs to plain terminals. But upstream development has stalled: dozens of [pull requests](https://github.com/lukilabs/beautiful-mermaid/pulls) sit open, some for over half a year, and nothing has merged in months — by most definitions, it's dead. I maintain `zombie-mermaid` as the fork that won't stay buried: pulling in upstream fixes, giving PRs stuck in the upstream queue a home, and actually shipping releases. Craft and Craft Agents aren't part of this project's process going forward; this is an independently maintained continuation.
31
+ `beautiful-mermaid` is a genuinely good library — fast, beautiful, and works everywhere from rich UIs to plain terminals. Checkable facts, as of 2026-10-13: the last push to upstream's `main` was 2026-05-06 and its latest release is v1.1.2 (2026-02-26); it has [43 open pull requests](https://github.com/lukilabs/beautiful-mermaid/pulls) (19 opened before 2026-04-13) and [50 open issues](https://github.com/lukilabs/beautiful-mermaid/issues) (live counts at those links; the figures above are a dated snapshot). I maintain `zombie-mermaid` as the fork that won't stay buried: pulling in upstream fixes, giving PRs stuck in the upstream queue a home, and actually shipping releases. Craft and Craft Agents aren't part of this project's process going forward; this is an independently maintained continuation.
32
32
 
33
- This isn't just a claim — see **[what this fork fixes](https://dfadler.github.io/zombie-mermaid/fork-fixes.html)** for an evidence-based before/after showcase: every bug listed there is rendered by the actual pre-fix and post-fix code, not described from memory.
33
+ This isn't just a claim — see **[what this fork fixes](https://dfadler.github.io/zombie-mermaid/fork-fixes.html)** for an evidence-based before/after showcase (38 fixes): every bug listed there is rendered by the actual pre-fix and post-fix code, not described from memory.
34
34
 
35
35
  It's still MIT-licensed, still built on the same foundation, and still credits the original authors — including the ASCII rendering engine's origins — in [Attribution](#attribution) below.
36
36
 
37
+ ### Migrating from `beautiful-mermaid`
38
+
39
+ Swap the package (`npm uninstall beautiful-mermaid && npm install zombie-mermaid`, Node.js 24+) and change the import path; `renderMermaidSVG`, `renderMermaidSVGAsync`, `renderMermaidASCII` and `parseMermaid` keep the same signatures. In browsers and bundlers, also register `elkjs` once (Node, Bun, the CLI and MCP do it for you):
40
+
41
+ ```ts
42
+ import ELK from 'elkjs/lib/elk.bundled.js'
43
+ import { registerElk, renderMermaidSVG } from 'zombie-mermaid'
44
+
45
+ registerElk(ELK) // once, before the first render
46
+ ```
47
+
48
+ Some fixes change existing output, so diff your stored diagrams first: see the [migration guide](docs/migrating-from-beautiful-mermaid.md) and the [`elkjs` guide](docs/guides/elkjs-optional-peer.md).
49
+
37
50
  ## The Problem With Default Mermaid Rendering
38
51
 
39
52
  Diagrams are essential for AI-assisted programming. When you're working with an AI coding assistant, being able to visualize data flows, state machines, and system architecture—directly in your terminal or chat interface—makes complex concepts instantly graspable.
@@ -210,6 +223,7 @@ zombie-mermaid --help # Show all options
210
223
  `--hyperlinks` (with `--ascii`) turns `click` hrefs into OSC 8 terminal
211
224
  hyperlinks — off by default, since terminal and pager support varies (see
212
225
  [ASCII terminal hyperlinks](docs/diagrams.md#ascii-terminal-hyperlinks)).
226
+ `--profile markdown` (with `--ascii`) emits plain ASCII characters with no color, for pasting into issues, commits and fenced blocks (the `profile` option in the library and the MCP `render_mermaid_ascii` tool).
213
227
 
214
228
  **Output rules.** `-o` is the destination for the run's _file_ output — SVG, HTML, or PNG when `--svg`/`--html`/`--png` is given, otherwise the ASCII rendering — and `-o -` sends it to stdout instead. A recognised extension (`.svg`, `.html`/`.htm`, `.txt`, `.png`) picks the format on its own, so the flag can be dropped; an extension that contradicts an explicit flag (`--svg -o out.txt`) is an error, while unrecognised ones are simply used as given. `--svg`/`--html`/`--png` with no `-o` writes `<input stem>.svg`/`.html`/`.png` beside the input (stdin input has no name to derive from, so it must pass `-o`). Only one of `--svg`/`--html`/`--png` can be set at a time — run the command twice for more than one. ASCII always prints to the terminal when one of them is also requested (`--ascii --svg -o out.svg`), and never carries ANSI colour codes when written to a file. **An existing output file is never overwritten unless you pass `--force`/`-f`.**
215
229
 
package/dist/cli.js CHANGED
@@ -1,28 +1,28 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire as V } from "node:module";
3
- import { extname as H, format as q, parse as D } from "node:path";
4
- import { THEMES as B, escapeXml as j, isDirection as K, isWideChar as J } from "@zombie-mermaid/core";
5
- import { access as Z, readFile as F, writeFile as Q } from "node:fs/promises";
6
- import { diagramColorsToAsciiTheme as ee, renderMermaidASCII as O } from "@zombie-mermaid/ascii-renderer";
2
+ import { createRequire as q } from "node:module";
3
+ import { extname as H, format as j, parse as F } from "node:path";
4
+ import { MermaidParseError as K, THEMES as O, escapeXml as J, isDirection as Z, isWideChar as Q } from "@zombie-mermaid/core";
5
+ import { access as ee, readFile as G, writeFile as te } from "node:fs/promises";
6
+ import { diagramColorsToAsciiTheme as ne, renderMermaidASCII as _ } from "@zombie-mermaid/ascii-renderer";
7
7
  import "@zombie-mermaid/mermaid-parser";
8
- import { registerElk as te, renderMermaidSVG as M, validateMermaid as ne } from "@zombie-mermaid/svg-renderer";
9
- import { createServer as re } from "node:http";
10
- import { StdioServerTransport as oe } from "@modelcontextprotocol/sdk/server/stdio.js";
11
- import { createMcpServer as ie } from "@zombie-mermaid/mcp";
12
- import ae from "elkjs/lib/elk.bundled.js";
13
- var se = 3e3;
14
- var G = {
8
+ import { registerElk as re, renderMermaidSVG as z, validateMermaid as oe } from "@zombie-mermaid/svg-renderer";
9
+ import { createServer as ie } from "node:http";
10
+ import { StdioServerTransport as ae } from "@modelcontextprotocol/sdk/server/stdio.js";
11
+ import { createMcpServer as se } from "@zombie-mermaid/mcp";
12
+ import de from "elkjs/lib/elk.bundled.js";
13
+ var ce = 3e3;
14
+ var W = {
15
15
  ".svg": "svg",
16
16
  ".txt": "ascii",
17
17
  ".html": "html",
18
18
  ".htm": "html",
19
19
  ".png": "png"
20
20
  };
21
- function de(e) {
21
+ function le(e) {
22
22
  if (!(e === void 0 || e === "-"))
23
- return G[H(e).toLowerCase()];
23
+ return W[H(e).toLowerCase()];
24
24
  }
25
- var w = {
25
+ var b = {
26
26
  svg: {
27
27
  flag: "--svg",
28
28
  ext: ".svg",
@@ -44,41 +44,41 @@ var w = {
44
44
  display: "PNG",
45
45
  label: "the PNG rasterization"
46
46
  }
47
- }, T = Object.keys(w);
48
- function ce(e, n) {
49
- const t = D(e);
50
- return q({
47
+ }, I = Object.keys(b);
48
+ function me(e, n) {
49
+ const t = F(e);
50
+ return j({
51
51
  dir: t.dir,
52
52
  name: t.name,
53
53
  ext: n
54
54
  });
55
55
  }
56
- function le(e) {
56
+ function ue(e) {
57
57
  const [n, ...t] = e;
58
58
  if (n === void 0) return { command: "help" };
59
59
  if (n === "--help" || n === "-h") return { command: "help" };
60
60
  if (n === "--version" || n === "-v") return { command: "version" };
61
61
  if (n === "themes") return { command: "themes" };
62
- if (n === "render") return pe(t);
62
+ if (n === "render") return he(t);
63
63
  if (n === "validate") {
64
- const r = t.find((a, c) => c > 0 || a.startsWith("-"));
64
+ const r = t.find((a, d) => d > 0 || a.startsWith("-"));
65
65
  if (r !== void 0) throw new Error(`Unexpected argument: ${r}`);
66
66
  return {
67
67
  command: "validate",
68
68
  input: t[0]
69
69
  };
70
70
  }
71
- if (n === "web") return fe(t);
71
+ if (n === "web") return ge(t);
72
72
  if (n === "mcp") return { command: "mcp" };
73
73
  throw new Error(`Unknown command: ${n}`);
74
74
  }
75
- function z(e, n, t) {
75
+ function $(e, n, t) {
76
76
  const r = e[n + 1];
77
77
  if (n + 1 >= e.length || r === void 0) throw new Error(`${t} requires a numeric value`);
78
78
  if (!/^\d+$/.test(r)) throw new Error(`${t} requires a non-negative integer, got: "${r}"`);
79
79
  return Number(r);
80
80
  }
81
- function me(e, n, t) {
81
+ function pe(e, n, t) {
82
82
  const r = e[n + 1];
83
83
  if (n + 1 >= e.length || r === void 0) throw new Error(`${t} requires a value ("auto" or a positive integer)`);
84
84
  if (r === "auto") return "auto";
@@ -86,15 +86,15 @@ function me(e, n, t) {
86
86
  if (!/^\d+$/.test(r) || a === 0 || !Number.isSafeInteger(a)) throw new Error(`${t} requires "auto" or a positive integer, got: "${r}"`);
87
87
  return a;
88
88
  }
89
- function ue(e, n, t) {
89
+ function fe(e, n, t) {
90
90
  const r = e[n + 1];
91
91
  if (n + 1 >= e.length || r === void 0) throw new Error(`${t} requires a value (one of TD, TB, BT, LR, RL)`);
92
92
  const a = r.toUpperCase();
93
- if (!K(a)) throw new Error(`${t} requires one of TD, TB, BT, LR, RL, got: "${r}"`);
93
+ if (!Z(a)) throw new Error(`${t} requires one of TD, TB, BT, LR, RL, got: "${r}"`);
94
94
  return a;
95
95
  }
96
- function pe(e) {
97
- let n, t = !1, r = !1, a = !1, c = !1, l = !1, m, g = !1, E, S, k, s, u = !1, p = !1, h, b, i = 0;
96
+ function he(e) {
97
+ let n, t = !1, r = !1, a = !1, d = !1, l = !1, m, g = !1, E, S, k, s, u = !1, p = !1, h, v, y, i = 0;
98
98
  for (; i < e.length; ) {
99
99
  const o = e[i];
100
100
  if (o === void 0) throw new Error(`parseArgs: index ${i} out of range while parsing arguments`);
@@ -105,7 +105,7 @@ function pe(e) {
105
105
  else if (o === "--resolve-colors")
106
106
  a = !0, i++;
107
107
  else if (o === "--html")
108
- c = !0, i++;
108
+ d = !0, i++;
109
109
  else if (o === "--png")
110
110
  l = !0, i++;
111
111
  else if (o === "-o" || o === "--output") {
@@ -117,72 +117,77 @@ function pe(e) {
117
117
  if (i + 1 >= e.length) throw new Error("--theme requires a theme name");
118
118
  E = e[i + 1], i += 2;
119
119
  } else if (o === "-x" || o === "--paddingX")
120
- S = z(e, i, o), i += 2;
120
+ S = $(e, i, o), i += 2;
121
121
  else if (o === "-y" || o === "--paddingY")
122
- k = z(e, i, o), i += 2;
122
+ k = $(e, i, o), i += 2;
123
123
  else if (o === "-p" || o === "--borderPadding")
124
- s = z(e, i, o), i += 2;
124
+ s = $(e, i, o), i += 2;
125
125
  else if (o === "--coords")
126
126
  u = !0, i++;
127
127
  else if (o === "--hyperlinks")
128
128
  p = !0, i++;
129
129
  else if (o === "-w" || o === "--max-width")
130
- h = me(e, i, o), i += 2;
131
- else if (o === "--direction")
132
- b = ue(e, i, o), i += 2;
130
+ h = pe(e, i, o), i += 2;
131
+ else if (o === "--profile") {
132
+ const c = e[i + 1];
133
+ if (c !== "markdown") throw new Error(`--profile requires one of: markdown, got: "${c ?? ""}"`);
134
+ v = c, i += 2;
135
+ } else if (o === "--direction")
136
+ y = fe(e, i, o), i += 2;
133
137
  else if (!o.startsWith("-") || o === "-") {
134
138
  if (o === "-") throw new Error("Unexpected argument: - (use -o - to write output to stdout; omit the input file to read stdin)");
135
139
  if (n !== void 0) throw new Error(`Unexpected argument: ${o} (input file already set to "${n}")`);
136
140
  n = o, i++;
137
141
  } else throw new Error(`Unknown flag: ${o}`);
138
142
  }
139
- const v = {
143
+ const w = {
140
144
  svg: r,
141
- html: c,
145
+ html: d,
142
146
  png: l
143
- }, y = T.filter((o) => v[o]);
144
- if (y.length > 1) {
145
- const [o, d] = y;
146
- throw new Error(`${w[o].flag} and ${w[d].flag} cannot both be set — they would write different content to the same output path. Run the command twice with different -o paths for each.`);
147
+ }, C = I.filter((o) => w[o]);
148
+ if (C.length > 1) {
149
+ const [o, c] = C;
150
+ throw new Error(`${b[o].flag} and ${b[c].flag} cannot both be set — they would write different content to the same output path. Run the command twice with different -o paths for each.`);
147
151
  }
148
- const f = de(m);
152
+ const f = le(m);
149
153
  if (f !== void 0 && f !== "ascii") {
150
- const o = T.find((d) => d !== f && v[d]);
154
+ const o = I.find((c) => c !== f && w[c]);
151
155
  if (o !== void 0) {
152
- const d = w[f], $ = w[o];
153
- throw new Error(`-o ${m} has ${d.article} ${d.ext} extension, but ${$.flag} output would be written to it. Use a ${$.ext} path (or -o - for stdout) for ${$.label}.`);
156
+ const c = b[f], P = b[o];
157
+ throw new Error(`-o ${m} has ${c.article} ${c.ext} extension, but ${P.flag} output would be written to it. Use a ${P.ext} path (or -o - for stdout) for ${P.label}.`);
154
158
  }
155
- f === "svg" ? r = !0 : f === "html" ? c = !0 : l = !0;
159
+ f === "svg" ? r = !0 : f === "html" ? d = !0 : l = !0;
156
160
  } else if (f === "ascii") {
157
- const o = T.find((d) => v[d]);
161
+ const o = I.find((c) => w[c]);
158
162
  if (o !== void 0) {
159
- const d = w[o];
160
- throw new Error(`-o ${m} has a .txt extension, but ${d.flag} output would be written to it. Use a ${d.ext} path (or -o - for stdout) instead.`);
163
+ const c = b[o];
164
+ throw new Error(`-o ${m} has a .txt extension, but ${c.flag} output would be written to it. Use a ${c.ext} path (or -o - for stdout) instead.`);
161
165
  }
162
166
  t = !0;
163
167
  }
164
- if (!t && !r && !c && !l) {
168
+ if (!t && !r && !d && !l) {
165
169
  if (m !== void 0 && m !== "-") {
166
- const o = Object.keys(G).join(", ");
170
+ const o = Object.keys(W).join(", ");
167
171
  throw new Error(`Cannot infer an output format from "${m}" — pass --ascii, --svg, --html, or --png, or use a recognised extension (${o})`);
168
172
  }
169
173
  throw new Error("Specify --ascii, --svg, --html, and/or --png (or -o <path> with a .svg/.txt/.html/.png extension)");
170
174
  }
171
- const C = {
175
+ const T = {
172
176
  svg: r,
173
- html: c,
177
+ html: d,
174
178
  png: l
175
- }, x = T.find((o) => C[o]);
179
+ }, x = I.find((o) => T[o]);
176
180
  if (m === "-" && t && x !== void 0) {
177
- const o = w[x].display;
181
+ const o = b[x].display;
178
182
  throw new Error(`-o - would send both ASCII and ${o} to stdout; drop --ascii, or write the ${o} to a file path`);
179
183
  }
180
184
  if (x !== void 0 && m === void 0) {
181
- const { flag: o, ext: d } = w[x];
185
+ const { flag: o, ext: c } = b[x];
182
186
  if (n === void 0) throw new Error(`${o} needs -o <path> (or -o - for stdout) when reading from stdin — there is no input file name to derive an output name from`);
183
- m = ce(n, d);
187
+ m = me(n, c);
184
188
  }
185
189
  if (h !== void 0 && !t) throw new Error("-w/--max-width requires --ascii");
190
+ if (v !== void 0 && !t) throw new Error("--profile requires --ascii");
186
191
  if (p && !t) throw new Error("--hyperlinks requires --ascii");
187
192
  if (a && !r) throw new Error("--resolve-colors requires --svg");
188
193
  return {
@@ -191,7 +196,7 @@ function pe(e) {
191
196
  ascii: t,
192
197
  svg: r,
193
198
  resolveColors: a,
194
- html: c,
199
+ html: d,
195
200
  png: l,
196
201
  output: m,
197
202
  force: g,
@@ -202,16 +207,17 @@ function pe(e) {
202
207
  coords: u,
203
208
  hyperlinks: p,
204
209
  maxWidth: h,
205
- direction: b
210
+ profile: v,
211
+ direction: y
206
212
  };
207
213
  }
208
- function fe(e) {
209
- let n = se, t = 0;
214
+ function ge(e) {
215
+ let n = ce, t = 0;
210
216
  for (; t < e.length; ) {
211
217
  const r = e[t];
212
218
  if (r === void 0) throw new Error(`parseArgs: index ${t} out of range while parsing arguments`);
213
219
  if (r === "--port")
214
- n = z(e, t, r), t += 2;
220
+ n = $(e, t, r), t += 2;
215
221
  else throw new Error(`Unknown flag: ${r}`);
216
222
  }
217
223
  return {
@@ -219,52 +225,51 @@ function fe(e) {
219
225
  port: n
220
226
  };
221
227
  }
222
- var he = /\p{Mn}|\p{Me}/u;
223
- function ge(e) {
224
- return he.test(e);
228
+ var ve = /\p{Mn}|\p{Me}/u;
229
+ function we(e) {
230
+ return ve.test(e);
225
231
  }
226
- var ve = new Intl.Segmenter(void 0, { granularity: "grapheme" }), we = "️";
227
- function be(e) {
232
+ var be = new Intl.Segmenter(void 0, { granularity: "grapheme" }), ye = "️";
233
+ function xe(e) {
228
234
  if (/^[\x20-\x7e]*$/.test(e)) return e.split("");
229
235
  const n = [];
230
- for (const { segment: t } of ve.segment(e)) n.push(t);
236
+ for (const { segment: t } of be.segment(e)) n.push(t);
231
237
  return n;
232
238
  }
233
- function ye(e) {
234
- if (e.includes(we)) return 2;
239
+ function Ee(e) {
240
+ if (e.includes(ye)) return 2;
235
241
  let n = !1, t = !1;
236
242
  for (const r of e)
237
- J(r) && (n = !0), ge(r) || (t = !0);
243
+ Q(r) && (n = !0), we(r) || (t = !0);
238
244
  return n ? 2 : t ? 1 : 0;
239
245
  }
240
- function xe(e) {
246
+ function Se(e) {
241
247
  let n = 0;
242
- for (const t of be(e)) n += ye(t);
248
+ for (const t of xe(e)) n += Ee(t);
243
249
  return n;
244
250
  }
245
- var Ee = /\x1b\]8;[^\x07\x1b]*(?:\x1b\\|\x07)/g;
246
- function Se(e) {
247
- return e.replace(Ee, "");
251
+ var ke = /\x1b\]8;[^\x07\x1b]*(?:\x1b\\|\x07)/g;
252
+ function Ce(e) {
253
+ return e.replace(ke, "");
248
254
  }
249
- var ke = /^Line (\d+): ([\s\S]*)$/;
250
- function Ce(e, n) {
251
- const t = ke.exec(e);
252
- if (t === null) return;
253
- const r = Number(t[1]), a = n.split(/\r?\n/)[r - 1];
254
- if (!(!Number.isSafeInteger(r) || r < 1 || a === void 0))
255
+ var Te = /^Line (\d+): ([\s\S]*)$/;
256
+ function Ie(e, n) {
257
+ if (!(e instanceof K) || e.line === void 0) return;
258
+ const { line: t, message: r } = e, a = Te.exec(r), d = n.split(/\r?\n/)[t - 1];
259
+ if (!(t < 1 || d === void 0))
255
260
  return {
256
- line: r,
257
- sourceLine: a,
258
- message: t[2] ?? ""
261
+ line: t,
262
+ sourceLine: d,
263
+ message: a?.[2] ?? r
259
264
  };
260
265
  }
261
- function W(e, n) {
266
+ function X(e, n) {
262
267
  if (!(e instanceof Error)) return e;
263
- const t = Ce(e.message, n);
268
+ const t = Ie(e, n);
264
269
  return t === void 0 ? e : /* @__PURE__ */ new Error(`${e.message}
265
270
  ${t.line} | ${t.sourceLine}`);
266
271
  }
267
- var Te = `// @ts-check
272
+ var Ae = `// @ts-check
268
273
  /// <reference lib="dom" />
269
274
  // ============================================================================
270
275
  // Client script for the CLI's self-contained HTML viewer (\`render --html\`).
@@ -556,7 +561,7 @@ var Te = `// @ts-check
556
561
  fit()
557
562
  stage.focus({ preventScroll: true })
558
563
  })()
559
- `, Ae = `
564
+ `, Me = `
560
565
  :root {
561
566
  color-scheme: light dark;
562
567
  --bg: #f4f4f5;
@@ -663,16 +668,16 @@ var Te = `// @ts-check
663
668
  #diagram { position: static; transform: none !important; }
664
669
  }
665
670
  `.trim();
666
- function Ie(e) {
671
+ function ze(e) {
667
672
  const n = e.title?.trim() || "Mermaid diagram";
668
673
  return `<!doctype html>
669
674
  <html lang="en">
670
675
  <head>
671
676
  <meta charset="utf-8" />
672
677
  <meta name="viewport" content="width=device-width, initial-scale=1" />
673
- <title>${j(n)}</title>
678
+ <title>${J(n)}</title>
674
679
  <style>
675
- ${Ae}
680
+ ${Me}
676
681
  </style>
677
682
  </head>
678
683
  <body>
@@ -690,13 +695,13 @@ ${e.svg}
690
695
  <button id="theme" type="button" aria-pressed="false" aria-label="Switch to dark mode (T)">Dark</button>
691
696
  </div>
692
697
  <script>
693
- ${Te}
698
+ ${Ae}
694
699
  <\/script>
695
700
  </body>
696
701
  </html>
697
702
  `;
698
703
  }
699
- async function Me(e) {
704
+ async function $e(e) {
700
705
  let n;
701
706
  try {
702
707
  n = await import("@resvg/resvg-js");
@@ -707,73 +712,73 @@ ${r}`);
707
712
  }
708
713
  return (await n.renderAsync(e)).asPng();
709
714
  }
710
- var ze = 80, P = 1, L = 1, R = 0, $e = /\x1b\[[0-9;]*m/g;
711
- function _(e) {
715
+ var Pe = 80, L = 1, R = 1, B = 0, Le = /\x1b\[[0-9;]*m/g;
716
+ function N(e) {
712
717
  return e.split(`
713
- `).reduce((n, t) => Math.max(n, xe(Se(t.replace($e, "")))), 0);
718
+ `).reduce((n, t) => Math.max(n, Se(Ce(t.replace(Le, "")))), 0);
714
719
  }
715
- function Pe(e) {
716
- return e !== "auto" ? e : process.stdout.columns ?? ze;
720
+ function Re(e) {
721
+ return e !== "auto" ? e : process.stdout.columns ?? Pe;
717
722
  }
718
- async function Le(e, n, t, r) {
719
- const a = n ?? process.stdout, c = r ?? process.stderr;
723
+ async function Be(e, n, t, r) {
724
+ const a = n ?? process.stdout, d = r ?? process.stderr;
720
725
  let l;
721
- if (e.input !== void 0) l = await F(e.input, "utf-8");
726
+ if (e.input !== void 0) l = await G(e.input, "utf-8");
722
727
  else if (t !== void 0) l = t;
723
728
  else {
724
729
  if (process.stdin.isTTY) throw new Error("No input file specified and stdin is a terminal. Pipe a diagram or pass a file path.");
725
- l = await Y();
730
+ l = await U();
726
731
  }
727
732
  if (l.trim().length === 0) throw new Error("Empty input — provide a Mermaid diagram via file or stdin");
728
733
  try {
729
- await Re(e, l, a, c);
734
+ await Oe(e, l, a, d);
730
735
  } catch (m) {
731
- throw W(m, l);
736
+ throw X(m, l);
732
737
  }
733
738
  }
734
- async function Re(e, n, t, r) {
739
+ async function Oe(e, n, t, r) {
735
740
  let a;
736
- if (e.theme !== void 0 && (a = B[e.theme], a === void 0)) {
737
- const s = Object.keys(B).join(", ");
741
+ if (e.theme !== void 0 && (a = O[e.theme], a === void 0)) {
742
+ const s = Object.keys(O).join(", ");
738
743
  throw new Error(`Unknown theme: "${e.theme}". Available themes: ${s}`);
739
744
  }
740
- const c = e.svg && e.output === "-", l = e.html && e.output === "-", m = e.png && e.output === "-", g = e.ascii && !e.svg && !e.html && !e.png && e.output !== void 0 && e.output !== "-" ? e.output : void 0, E = e.svg && !c ? e.output : void 0, S = e.html && !l ? e.output : void 0, k = e.png && !m ? e.output : void 0;
745
+ const d = e.svg && e.output === "-", l = e.html && e.output === "-", m = e.png && e.output === "-", g = e.ascii && !e.svg && !e.html && !e.png && e.output !== void 0 && e.output !== "-" ? e.output : void 0, E = e.svg && !d ? e.output : void 0, S = e.html && !l ? e.output : void 0, k = e.png && !m ? e.output : void 0;
741
746
  for (const s of [
742
747
  g,
743
748
  E,
744
749
  S,
745
750
  k
746
- ]) s !== void 0 && await Be(s, e.force);
751
+ ]) s !== void 0 && await _e(s, e.force);
747
752
  if (e.ascii) {
748
753
  const s = a && g === void 0 ? {
749
754
  colorMode: "auto",
750
- theme: ee(a)
755
+ theme: ne(a)
751
756
  } : { colorMode: "none" };
752
- e.paddingX !== void 0 && (s.paddingX = e.paddingX), e.paddingY !== void 0 && (s.paddingY = e.paddingY), e.borderPadding !== void 0 && (s.boxBorderPadding = e.borderPadding), e.coords && (s.showCoords = !0), e.direction !== void 0 && (s.direction = e.direction), e.hyperlinks && g === void 0 && (s.hyperlinks = !0);
753
- let u = O(n, s);
757
+ e.paddingX !== void 0 && (s.paddingX = e.paddingX), e.paddingY !== void 0 && (s.paddingY = e.paddingY), e.borderPadding !== void 0 && (s.boxBorderPadding = e.borderPadding), e.coords && (s.showCoords = !0), e.profile !== void 0 && (s.profile = e.profile), e.direction !== void 0 && (s.direction = e.direction), e.hyperlinks && g === void 0 && (s.hyperlinks = !0);
758
+ let u = _(n, s);
754
759
  if (e.maxWidth !== void 0) {
755
- const p = Pe(e.maxWidth);
756
- let h = _(u), b = !1;
760
+ const p = Re(e.maxWidth);
761
+ let h = N(u), v = !1;
757
762
  if (h > p) {
758
- const i = s.paddingX ?? 5, v = s.paddingY ?? 5, y = s.boxBorderPadding ?? 1, f = Math.min(i, P), C = Math.min(v, L), x = Math.min(y, R);
759
- if (f < i || C < v || x < y) {
760
- const o = O(n, {
763
+ const y = s.paddingX ?? 5, i = s.paddingY ?? 5, w = s.boxBorderPadding ?? 1, C = Math.min(y, L), f = Math.min(i, R), T = Math.min(w, B);
764
+ if (C < y || f < i || T < w) {
765
+ const x = _(n, {
761
766
  ...s,
762
- paddingX: f,
763
- paddingY: C,
764
- boxBorderPadding: x
765
- }), d = _(o);
766
- d < h && (u = o, h = d, b = !0);
767
+ paddingX: C,
768
+ paddingY: f,
769
+ boxBorderPadding: T
770
+ }), o = N(x);
771
+ o < h && (u = x, h = o, v = !0);
767
772
  }
768
773
  }
769
774
  if (h > p) {
770
- const i = e.maxWidth === "auto" ? "detected terminal width" : "--max-width", v = b ? `Compact spacing (-x ${P} -y ${L} -p ${R}) was already applied automatically. ` : "", y = b ? "Try a narrower direction (LR vs TD), shorter labels, or widen your terminal." : "Try -x/-y/-p for tighter spacing, a narrower direction (LR vs TD), or widen your terminal.";
771
- r.write(`Warning: ASCII output is ${h} columns wide, exceeding ${i} of ${p}. ${v}zombie-mermaid does not reflow diagram structure to fit (no label wrapping or direction-flip cascade — see https://github.com/dfadler/zombie-mermaid/issues/335). ${y}
775
+ const y = e.maxWidth === "auto" ? "detected terminal width" : "--max-width", i = v ? `Compact spacing (-x ${L} -y ${R} -p ${B}) was already applied automatically. ` : "", w = v ? "Try a narrower direction (LR vs TD), shorter labels, or widen your terminal." : "Try -x/-y/-p for tighter spacing, a narrower direction (LR vs TD), or widen your terminal.";
776
+ r.write(`Warning: ASCII output is ${h} columns wide, exceeding ${y} of ${p}. ${i}zombie-mermaid does not reflow diagram structure to fit (no label wrapping or direction-flip cascade — see https://github.com/dfadler/zombie-mermaid/issues/335). ${w}
772
777
  `);
773
- } else b && r.write(`Note: ASCII output exceeded ${p} columns at the requested spacing; applied compact spacing automatically (-x ${P} -y ${L} -p ${R}) to fit.
778
+ } else v && r.write(`Note: ASCII output exceeded ${p} columns at the requested spacing; applied compact spacing automatically (-x ${L} -y ${R} -p ${B}) to fit.
774
779
  `);
775
780
  }
776
- await I(A(g === void 0, g), u + `
781
+ await M(A(g === void 0, g), u + `
777
782
  `, t, e.force);
778
783
  }
779
784
  if (e.svg) {
@@ -782,15 +787,15 @@ async function Re(e, n, t, r) {
782
787
  resolveColors: e.resolveColors
783
788
  };
784
789
  e.direction !== void 0 && (s.direction = e.direction);
785
- const u = M(n, s);
786
- await I(A(c, E), u, t, e.force);
790
+ const u = z(n, s);
791
+ await M(A(d, E), u, t, e.force);
787
792
  }
788
793
  if (e.html) {
789
- const s = M(n, a ?? {}), u = e.input ? D(e.input).name : void 0, p = Ie({
794
+ const s = z(n, a ?? {}), u = e.input ? F(e.input).name : void 0, p = ze({
790
795
  svg: s,
791
796
  title: u
792
797
  });
793
- await I(A(l, S), p, t, e.force);
798
+ await M(A(l, S), p, t, e.force);
794
799
  }
795
800
  if (e.png) {
796
801
  const s = {
@@ -798,8 +803,8 @@ async function Re(e, n, t, r) {
798
803
  resolveColors: !0
799
804
  };
800
805
  e.direction !== void 0 && (s.direction = e.direction);
801
- const u = M(n, s), p = await Me(u);
802
- await I(A(m, k), p, t, e.force);
806
+ const u = z(n, s), p = await $e(u);
807
+ await M(A(m, k), p, t, e.force);
803
808
  }
804
809
  }
805
810
  function A(e, n) {
@@ -808,86 +813,86 @@ function A(e, n) {
808
813
  path: n
809
814
  } : { kind: "skip" };
810
815
  }
811
- async function I(e, n, t, r) {
816
+ async function M(e, n, t, r) {
812
817
  switch (e.kind) {
813
818
  case "stdout":
814
819
  t.write(n);
815
820
  break;
816
821
  case "file":
817
- await Oe(e.path, n, r);
822
+ await Ne(e.path, n, r);
818
823
  }
819
824
  }
820
- function X(e) {
825
+ function Y(e) {
821
826
  return /* @__PURE__ */ new Error(`Refusing to overwrite existing file "${e}" — pass --force to replace it`);
822
827
  }
823
- async function Be(e, n) {
828
+ async function _e(e, n) {
824
829
  if (n) return;
825
830
  let t = !0;
826
831
  try {
827
- await Z(e);
832
+ await ee(e);
828
833
  } catch {
829
834
  t = !1;
830
835
  }
831
- if (t) throw X(e);
836
+ if (t) throw Y(e);
832
837
  }
833
- async function Oe(e, n, t) {
838
+ async function Ne(e, n, t) {
834
839
  try {
835
- await Q(e, n, {
840
+ await te(e, n, {
836
841
  encoding: "utf-8",
837
842
  flag: t ? "w" : "wx"
838
843
  });
839
844
  } catch (r) {
840
- throw !t && r instanceof Error && "code" in r && r.code === "EEXIST" ? X(e) : r;
845
+ throw !t && r instanceof Error && "code" in r && r.code === "EEXIST" ? Y(e) : r;
841
846
  }
842
847
  }
843
- async function Y() {
848
+ async function U() {
844
849
  const e = [], n = process.stdin;
845
850
  return n.setEncoding("utf-8"), new Promise((t, r) => {
846
851
  n.on("data", (a) => e.push(a)), n.on("end", () => t(e.join(""))), n.on("error", r);
847
852
  });
848
853
  }
849
- async function _e(e, n) {
854
+ async function De(e, n) {
850
855
  let t;
851
- if (e.input !== void 0) t = await F(e.input, "utf-8");
856
+ if (e.input !== void 0) t = await G(e.input, "utf-8");
852
857
  else if (n !== void 0) t = n;
853
858
  else {
854
859
  if (process.stdin.isTTY) throw new Error("No input file specified and stdin is a terminal. Pipe a diagram or pass a file path.");
855
- t = await Y();
860
+ t = await U();
856
861
  }
857
862
  const r = t;
858
863
  if (r.trim().length === 0) throw new Error("Empty input — provide a Mermaid diagram via file or stdin");
859
864
  try {
860
- ne(r);
865
+ oe(r);
861
866
  } catch (a) {
862
- throw W(a, r);
867
+ throw X(a, r);
863
868
  }
864
869
  console.log("valid");
865
870
  }
866
- var Ne = 1e6;
867
- function De(e) {
871
+ var Fe = 1e6;
872
+ function Ge(e) {
868
873
  return new Promise((n, t) => {
869
874
  const r = [];
870
875
  let a = 0;
871
- e.on("data", (c) => {
872
- if (a += c.length, a > Ne) {
876
+ e.on("data", (d) => {
877
+ if (a += d.length, a > Fe) {
873
878
  t(/* @__PURE__ */ new Error("Request body too large")), e.destroy();
874
879
  return;
875
880
  }
876
- r.push(c);
881
+ r.push(d);
877
882
  }), e.on("end", () => n(Buffer.concat(r).toString("utf-8"))), e.on("error", t);
878
883
  });
879
884
  }
880
- function U(e) {
885
+ function V(e) {
881
886
  return typeof e == "object" && e !== null;
882
887
  }
883
- function Fe(e) {
888
+ function We(e) {
884
889
  let n;
885
890
  try {
886
891
  n = JSON.parse(e);
887
892
  } catch {
888
893
  throw new Error("Invalid JSON in request body");
889
894
  }
890
- if (!U(n)) throw new Error("Request body must be a JSON object");
895
+ if (!V(n)) throw new Error("Request body must be a JSON object");
891
896
  const t = n.source;
892
897
  if (typeof t != "string" || t.trim().length === 0) throw new Error('Provide Mermaid source as a non-empty "source" string');
893
898
  return {
@@ -895,25 +900,25 @@ function Fe(e) {
895
900
  format: n.format === "svg" ? "svg" : "ascii"
896
901
  };
897
902
  }
898
- function N(e, n, t) {
903
+ function D(e, n, t) {
899
904
  const r = JSON.stringify(t);
900
905
  e.writeHead(n, {
901
906
  "Content-Type": "application/json; charset=utf-8",
902
907
  "Content-Length": Buffer.byteLength(r)
903
908
  }), e.end(r);
904
909
  }
905
- async function Ge(e, n) {
910
+ async function Xe(e, n) {
906
911
  try {
907
- const { source: t, format: r } = Fe(await De(e));
908
- N(n, 200, {
909
- output: r === "svg" ? M(t) : O(t),
912
+ const { source: t, format: r } = We(await Ge(e));
913
+ D(n, 200, {
914
+ output: r === "svg" ? z(t) : _(t),
910
915
  format: r
911
916
  });
912
917
  } catch (t) {
913
- N(n, 400, { error: t instanceof Error ? t.message : String(t) });
918
+ D(n, 400, { error: t instanceof Error ? t.message : String(t) });
914
919
  }
915
920
  }
916
- var We = `<!doctype html>
921
+ var Ye = `<!doctype html>
917
922
  <html lang="en">
918
923
  <head>
919
924
  <meta charset="utf-8" />
@@ -979,92 +984,92 @@ var We = `<!doctype html>
979
984
  </body>
980
985
  </html>
981
986
  `;
982
- function Xe() {
983
- return re((e, n) => {
987
+ function Ue() {
988
+ return ie((e, n) => {
984
989
  const t = (e.url ?? "/").split("?")[0];
985
990
  if (e.method === "GET" && (t === "/" || t === "")) {
986
- n.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), n.end(We);
991
+ n.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), n.end(Ye);
987
992
  return;
988
993
  }
989
994
  if (e.method === "POST" && t === "/render") {
990
- Ge(e, n);
995
+ Xe(e, n);
991
996
  return;
992
997
  }
993
998
  n.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" }), n.end("Not found");
994
999
  });
995
1000
  }
996
- function Ye(e) {
1001
+ function Ve(e) {
997
1002
  return new Promise((n, t) => {
998
- const r = Xe();
1003
+ const r = Ue();
999
1004
  r.once("error", t), r.listen(e, "127.0.0.1", () => {
1000
1005
  r.removeListener("error", t), n(r);
1001
1006
  });
1002
1007
  });
1003
1008
  }
1004
- async function Ue(e, n = console.log) {
1005
- const t = await Ye(e.port), r = t.address();
1006
- return n(`zombie-mermaid web running at http://localhost:${U(r) && typeof r.port == "number" ? r.port : e.port}`), t;
1009
+ async function qe(e, n = console.log) {
1010
+ const t = await Ve(e.port), r = t.address();
1011
+ return n(`zombie-mermaid web running at http://localhost:${V(r) && typeof r.port == "number" ? r.port : e.port}`), t;
1007
1012
  }
1008
- async function Ve(e = (n) => console.error(n)) {
1009
- const n = ie(), t = new oe();
1013
+ async function He(e = (n) => console.error(n)) {
1014
+ const n = se(), t = new ae();
1010
1015
  return await n.connect(t), e("zombie-mermaid MCP server running on stdio (experimental — feedback welcome: https://github.com/dfadler/zombie-mermaid/issues)"), n;
1011
1016
  }
1012
- function He() {
1013
- const e = V(import.meta.url)("../package.json");
1017
+ function je() {
1018
+ const e = q(import.meta.url)("../package.json");
1014
1019
  return typeof e == "object" && e !== null && "version" in e && typeof e.version == "string" ? e.version : "unknown";
1015
1020
  }
1016
- te(ae);
1017
- var qe = He();
1018
- async function je() {
1021
+ re(de);
1022
+ var Ke = je();
1023
+ async function Je() {
1019
1024
  const e = process.argv.slice(2);
1020
1025
  let n;
1021
1026
  try {
1022
- n = le(e);
1027
+ n = ue(e);
1023
1028
  } catch (t) {
1024
1029
  console.error(`Error: ${t instanceof Error ? t.message : String(t)}`), process.exit(1);
1025
1030
  }
1026
1031
  switch (n.command) {
1027
1032
  case "help":
1028
- Ke();
1033
+ Ze();
1029
1034
  break;
1030
1035
  case "version":
1031
- console.log(`zombie-mermaid ${qe}`);
1036
+ console.log(`zombie-mermaid ${Ke}`);
1032
1037
  break;
1033
1038
  case "themes":
1034
1039
  console.log(`Available themes:
1035
1040
  `);
1036
- for (const t of Object.keys(B)) console.log(` ${t}`);
1041
+ for (const t of Object.keys(O)) console.log(` ${t}`);
1037
1042
  break;
1038
1043
  case "render":
1039
1044
  try {
1040
- await Le(n);
1045
+ await Be(n);
1041
1046
  } catch (t) {
1042
1047
  console.error(`Error: ${t instanceof Error ? t.message : String(t)}`), process.exit(1);
1043
1048
  }
1044
1049
  break;
1045
1050
  case "validate":
1046
1051
  try {
1047
- await _e(n);
1052
+ await De(n);
1048
1053
  } catch (t) {
1049
1054
  console.error(`Error: ${t instanceof Error ? t.message : String(t)}`), process.exit(1);
1050
1055
  }
1051
1056
  break;
1052
1057
  case "web":
1053
1058
  try {
1054
- await Ue(n);
1059
+ await qe(n);
1055
1060
  } catch (t) {
1056
1061
  console.error(`Error: ${t instanceof Error ? t.message : String(t)}`), process.exit(1);
1057
1062
  }
1058
1063
  break;
1059
1064
  case "mcp":
1060
1065
  try {
1061
- await Ve();
1066
+ await He();
1062
1067
  } catch (t) {
1063
1068
  console.error(`Error: ${t instanceof Error ? t.message : String(t)}`), process.exit(1);
1064
1069
  }
1065
1070
  }
1066
1071
  }
1067
- function Ke() {
1072
+ function Ze() {
1068
1073
  console.log(`
1069
1074
  zombie-mermaid — render Mermaid diagrams from the command line
1070
1075
 
@@ -1129,6 +1134,10 @@ Options:
1129
1134
  VTE-based terminals render them as clickable links;
1130
1135
  others may show the URL or nothing. Pagers need
1131
1136
  OSC 8 support too (\`less -R\`, recent versions).
1137
+ --profile <name> ASCII preset: 'markdown' = plain ASCII characters, no
1138
+ color, for pasting into issues/commits/fenced blocks
1139
+ (explicit flags like --coords still win). Requires
1140
+ --ascii.
1132
1141
  -w, --max-width <n|auto> Fit ASCII output within this many columns
1133
1142
  ('auto' detects the terminal width): applies compact
1134
1143
  spacing automatically when needed, and warns on
@@ -1139,7 +1148,7 @@ Options:
1139
1148
  -v, --version Show version
1140
1149
  `.trim());
1141
1150
  }
1142
- je();
1151
+ Je();
1143
1152
  export {
1144
- je as main
1153
+ Je as main
1145
1154
  };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@zombie-mermaid/core"),t=require("@zombie-mermaid/mermaid-parser"),n=require("@zombie-mermaid/ascii-renderer"),r=require("@zombie-mermaid/svg-renderer");Object.defineProperty(exports,"DEFAULTS",{enumerable:!0,get:function(){return e.DEFAULTS}}),Object.defineProperty(exports,"ElkNotRegisteredError",{enumerable:!0,get:function(){return r.ElkNotRegisteredError}}),Object.defineProperty(exports,"THEMES",{enumerable:!0,get:function(){return e.THEMES}}),Object.defineProperty(exports,"createLayoutCache",{enumerable:!0,get:function(){return r.createLayoutCache}}),Object.defineProperty(exports,"fromShikiTheme",{enumerable:!0,get:function(){return e.fromShikiTheme}}),Object.defineProperty(exports,"parseMermaid",{enumerable:!0,get:function(){return t.parseMermaid}}),Object.defineProperty(exports,"registerElk",{enumerable:!0,get:function(){return r.registerElk}}),Object.defineProperty(exports,"renderMermaid",{enumerable:!0,get:function(){return r.renderMermaid}}),Object.defineProperty(exports,"renderMermaidASCII",{enumerable:!0,get:function(){return n.renderMermaidASCII}}),Object.defineProperty(exports,"renderMermaidAscii",{enumerable:!0,get:function(){return n.renderMermaidAscii}}),Object.defineProperty(exports,"renderMermaidSVG",{enumerable:!0,get:function(){return r.renderMermaidSVG}}),Object.defineProperty(exports,"renderMermaidSVGAsync",{enumerable:!0,get:function(){return r.renderMermaidSVGAsync}}),Object.defineProperty(exports,"renderMermaidSync",{enumerable:!0,get:function(){return r.renderMermaidSync}}),Object.defineProperty(exports,"themeCssVariables",{enumerable:!0,get:function(){return r.themeCssVariables}}),Object.defineProperty(exports,"validateMermaid",{enumerable:!0,get:function(){return r.validateMermaid}});
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@zombie-mermaid/core"),t=require("@zombie-mermaid/mermaid-parser"),n=require("@zombie-mermaid/ascii-renderer"),r=require("@zombie-mermaid/svg-renderer");Object.defineProperty(exports,"DEFAULTS",{enumerable:!0,get:function(){return e.DEFAULTS}}),Object.defineProperty(exports,"DEFAULT_RENDER_LIMITS",{enumerable:!0,get:function(){return e.DEFAULT_RENDER_LIMITS}}),Object.defineProperty(exports,"ElkNotRegisteredError",{enumerable:!0,get:function(){return r.ElkNotRegisteredError}}),Object.defineProperty(exports,"LimitExceededError",{enumerable:!0,get:function(){return e.LimitExceededError}}),Object.defineProperty(exports,"THEMES",{enumerable:!0,get:function(){return e.THEMES}}),Object.defineProperty(exports,"createLayoutCache",{enumerable:!0,get:function(){return r.createLayoutCache}}),Object.defineProperty(exports,"fromShikiTheme",{enumerable:!0,get:function(){return e.fromShikiTheme}}),Object.defineProperty(exports,"parseMermaid",{enumerable:!0,get:function(){return t.parseMermaid}}),Object.defineProperty(exports,"registerElk",{enumerable:!0,get:function(){return r.registerElk}}),Object.defineProperty(exports,"renderMermaid",{enumerable:!0,get:function(){return r.renderMermaid}}),Object.defineProperty(exports,"renderMermaidASCII",{enumerable:!0,get:function(){return n.renderMermaidASCII}}),Object.defineProperty(exports,"renderMermaidAscii",{enumerable:!0,get:function(){return n.renderMermaidAscii}}),Object.defineProperty(exports,"renderMermaidSVG",{enumerable:!0,get:function(){return r.renderMermaidSVG}}),Object.defineProperty(exports,"renderMermaidSVGAsync",{enumerable:!0,get:function(){return r.renderMermaidSVGAsync}}),Object.defineProperty(exports,"renderMermaidSync",{enumerable:!0,get:function(){return r.renderMermaidSync}}),Object.defineProperty(exports,"themeCssVariables",{enumerable:!0,get:function(){return r.themeCssVariables}}),Object.defineProperty(exports,"validateMermaid",{enumerable:!0,get:function(){return r.validateMermaid}});
package/dist/index.d.cts CHANGED
@@ -2,6 +2,7 @@ import { AsciiRenderOptions } from '@zombie-mermaid/ascii-renderer';
2
2
  import { ClassRenderOptions } from '@zombie-mermaid/core';
3
3
  import { CommonRenderOptions } from '@zombie-mermaid/core';
4
4
  import { createLayoutCache } from '@zombie-mermaid/svg-renderer';
5
+ import { DEFAULT_RENDER_LIMITS } from '@zombie-mermaid/core';
5
6
  import { DEFAULTS } from '@zombie-mermaid/core';
6
7
  import { DiagramColors } from '@zombie-mermaid/core';
7
8
  import { Direction } from '@zombie-mermaid/core';
@@ -11,11 +12,13 @@ import { ErRenderOptions } from '@zombie-mermaid/core';
11
12
  import { FlowchartRenderOptions } from '@zombie-mermaid/core';
12
13
  import { fromShikiTheme } from '@zombie-mermaid/core';
13
14
  import { LayoutCache } from '@zombie-mermaid/svg-renderer';
15
+ import { LimitExceededError } from '@zombie-mermaid/core';
14
16
  import { MermaidGraph } from '@zombie-mermaid/core';
15
17
  import { NodeInteraction } from '@zombie-mermaid/core';
16
18
  import { parseMermaid } from '@zombie-mermaid/mermaid-parser';
17
19
  import { PositionedGraph } from '@zombie-mermaid/core';
18
20
  import { registerElk } from '@zombie-mermaid/svg-renderer';
21
+ import { RenderLimits } from '@zombie-mermaid/core';
19
22
  import { renderMermaid } from '@zombie-mermaid/svg-renderer';
20
23
  import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer';
21
24
  import { renderMermaidAscii } from '@zombie-mermaid/ascii-renderer';
@@ -38,6 +41,8 @@ export { CommonRenderOptions }
38
41
 
39
42
  export { createLayoutCache }
40
43
 
44
+ export { DEFAULT_RENDER_LIMITS }
45
+
41
46
  export { DEFAULTS }
42
47
 
43
48
  export { DiagramColors }
@@ -56,6 +61,8 @@ export { fromShikiTheme }
56
61
 
57
62
  export { LayoutCache }
58
63
 
64
+ export { LimitExceededError }
65
+
59
66
  export { MermaidGraph }
60
67
 
61
68
  export { NodeInteraction }
@@ -66,6 +73,8 @@ export { PositionedGraph }
66
73
 
67
74
  export { registerElk }
68
75
 
76
+ export { RenderLimits }
77
+
69
78
  export { renderMermaid }
70
79
 
71
80
  export { renderMermaidASCII }
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { AsciiRenderOptions } from '@zombie-mermaid/ascii-renderer';
2
2
  import { ClassRenderOptions } from '@zombie-mermaid/core';
3
3
  import { CommonRenderOptions } from '@zombie-mermaid/core';
4
4
  import { createLayoutCache } from '@zombie-mermaid/svg-renderer';
5
+ import { DEFAULT_RENDER_LIMITS } from '@zombie-mermaid/core';
5
6
  import { DEFAULTS } from '@zombie-mermaid/core';
6
7
  import { DiagramColors } from '@zombie-mermaid/core';
7
8
  import { Direction } from '@zombie-mermaid/core';
@@ -11,11 +12,13 @@ import { ErRenderOptions } from '@zombie-mermaid/core';
11
12
  import { FlowchartRenderOptions } from '@zombie-mermaid/core';
12
13
  import { fromShikiTheme } from '@zombie-mermaid/core';
13
14
  import { LayoutCache } from '@zombie-mermaid/svg-renderer';
15
+ import { LimitExceededError } from '@zombie-mermaid/core';
14
16
  import { MermaidGraph } from '@zombie-mermaid/core';
15
17
  import { NodeInteraction } from '@zombie-mermaid/core';
16
18
  import { parseMermaid } from '@zombie-mermaid/mermaid-parser';
17
19
  import { PositionedGraph } from '@zombie-mermaid/core';
18
20
  import { registerElk } from '@zombie-mermaid/svg-renderer';
21
+ import { RenderLimits } from '@zombie-mermaid/core';
19
22
  import { renderMermaid } from '@zombie-mermaid/svg-renderer';
20
23
  import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer';
21
24
  import { renderMermaidAscii } from '@zombie-mermaid/ascii-renderer';
@@ -38,6 +41,8 @@ export { CommonRenderOptions }
38
41
 
39
42
  export { createLayoutCache }
40
43
 
44
+ export { DEFAULT_RENDER_LIMITS }
45
+
41
46
  export { DEFAULTS }
42
47
 
43
48
  export { DiagramColors }
@@ -56,6 +61,8 @@ export { fromShikiTheme }
56
61
 
57
62
  export { LayoutCache }
58
63
 
64
+ export { LimitExceededError }
65
+
59
66
  export { MermaidGraph }
60
67
 
61
68
  export { NodeInteraction }
@@ -66,6 +73,8 @@ export { PositionedGraph }
66
73
 
67
74
  export { registerElk }
68
75
 
76
+ export { RenderLimits }
77
+
69
78
  export { renderMermaid }
70
79
 
71
80
  export { renderMermaidASCII }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { DEFAULTS as e, THEMES as t, fromShikiTheme as n } from "@zombie-mermaid/core";
2
- import { parseMermaid as r } from "@zombie-mermaid/mermaid-parser";
3
- import { renderMermaidASCII as i, renderMermaidAscii as a } from "@zombie-mermaid/ascii-renderer";
4
- import { ElkNotRegisteredError as o, createLayoutCache as s, registerElk as c, renderMermaid as l, renderMermaidSVG as u, renderMermaidSVGAsync as d, renderMermaidSync as f, themeCssVariables as p, validateMermaid as m } from "@zombie-mermaid/svg-renderer";
5
- export { e as DEFAULTS, o as ElkNotRegisteredError, t as THEMES, s as createLayoutCache, n as fromShikiTheme, r as parseMermaid, c as registerElk, l as renderMermaid, i as renderMermaidASCII, a as renderMermaidAscii, u as renderMermaidSVG, d as renderMermaidSVGAsync, f as renderMermaidSync, p as themeCssVariables, m as validateMermaid };
1
+ import { DEFAULTS as e, DEFAULT_RENDER_LIMITS as t, LimitExceededError as n, THEMES as r, fromShikiTheme as i } from "@zombie-mermaid/core";
2
+ import { parseMermaid as a } from "@zombie-mermaid/mermaid-parser";
3
+ import { renderMermaidASCII as o, renderMermaidAscii as s } from "@zombie-mermaid/ascii-renderer";
4
+ import { ElkNotRegisteredError as c, createLayoutCache as l, registerElk as u, renderMermaid as d, renderMermaidSVG as f, renderMermaidSVGAsync as p, renderMermaidSync as m, themeCssVariables as h, validateMermaid as g } from "@zombie-mermaid/svg-renderer";
5
+ export { e as DEFAULTS, t as DEFAULT_RENDER_LIMITS, c as ElkNotRegisteredError, n as LimitExceededError, r as THEMES, l as createLayoutCache, i as fromShikiTheme, a as parseMermaid, u as registerElk, d as renderMermaid, o as renderMermaidASCII, s as renderMermaidAscii, f as renderMermaidSVG, p as renderMermaidSVGAsync, m as renderMermaidSync, h as themeCssVariables, g as validateMermaid };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zombie-mermaid",
3
- "version": "5.2.0",
3
+ "version": "5.4.0",
4
4
  "license": "MIT",
5
5
  "description": "Render Mermaid diagrams as beautiful SVGs or ASCII art. Ultra-fast, fully themeable, zero DOM dependencies. Maintained fork of beautiful-mermaid.",
6
6
  "type": "module",
@@ -88,20 +88,24 @@
88
88
  },
89
89
  "dependencies": {
90
90
  "@modelcontextprotocol/sdk": "^1.30.0",
91
- "elkjs": "^0.11.0",
91
+ "elkjs": "^0.12.0",
92
92
  "entities": "^7.0.1",
93
- "@zombie-mermaid/ascii-renderer": "5.2.0",
94
- "@zombie-mermaid/core": "5.2.0",
95
- "@zombie-mermaid/mcp": "5.2.0",
96
- "@zombie-mermaid/svg-renderer": "5.2.0",
97
- "@zombie-mermaid/mermaid-parser": "5.2.0"
93
+ "@zombie-mermaid/ascii-renderer": "5.4.0",
94
+ "@zombie-mermaid/core": "5.4.0",
95
+ "@zombie-mermaid/mcp": "5.4.0",
96
+ "@zombie-mermaid/mermaid-parser": "5.4.0",
97
+ "@zombie-mermaid/svg-renderer": "5.4.0"
98
98
  },
99
99
  "devDependencies": {
100
+ "@arethetypeswrong/cli": "0.18.5",
100
101
  "@axe-core/playwright": "^4.13.0",
101
102
  "@changesets/changelog-github": "^1.0.0",
102
103
  "@changesets/cli": "^3.0.1",
104
+ "@fast-check/vitest": "0.5.0",
103
105
  "@microsoft/api-extractor": "^7.59.0",
104
106
  "@playwright/test": "1.62.1",
107
+ "@stryker-mutator/core": "10.0.0",
108
+ "@stryker-mutator/vitest-runner": "10.0.0",
105
109
  "@testing-library/jest-dom": "^7.0.1",
106
110
  "@testing-library/react": "^16.3.3",
107
111
  "@testing-library/user-event": "^14.6.7",
@@ -114,12 +118,14 @@
114
118
  "esbuild": "^0.28.2",
115
119
  "eslint": "^10.9.1",
116
120
  "eslint-config-prettier": "^10.1.8",
121
+ "fast-check": "4.10.2",
117
122
  "fontkit": "^2.0.4",
118
123
  "jsdom": "^30.0.1",
119
124
  "lightningcss": "^1.33.0",
120
125
  "marked": "^18.0.11",
121
126
  "mermaid": "12.1.0",
122
127
  "prettier": "^3.9.6",
128
+ "publint": "0.3.25",
123
129
  "react": "^19.2.8",
124
130
  "react-dom": "^19.2.8",
125
131
  "shiki": "^3.19.0",
@@ -137,6 +143,7 @@
137
143
  },
138
144
  "scripts": {
139
145
  "test": "vitest run -c config/vitest.config.ts",
146
+ "test:shuffle": "vitest run -c config/vitest.config.ts --sequence.shuffle.files --sequence.shuffle.tests",
140
147
  "test:watch": "vitest -c config/vitest.config.ts",
141
148
  "test:coverage": "vitest run -c config/vitest.config.ts --coverage",
142
149
  "test:visual": "playwright test",
@@ -166,11 +173,15 @@
166
173
  "check:changesets": "tsx scripts/check-changesets.ts",
167
174
  "check:docs": "tsx scripts/check-docs.ts",
168
175
  "smoke:packed": "node scripts/smoke-test-packed.mjs",
176
+ "check:pack-dist": "node scripts/check-pack-dist.mjs",
177
+ "check:package-types": "scripts/check-package-types.sh",
169
178
  "check:bundle-size": "tsx scripts/check-bundle-size.ts",
170
179
  "generate:hero": "tsx scripts/generate-hero.ts",
171
180
  "build:mono-font": "tsx scripts/build-mono-font-subset.ts && prettier --write demo/components/generated/mono-font-subset.ts demo/styles.css packages/svg-renderer/src/generated/mono-font-subset.ts",
172
181
  "generate:primitives-classes": "tsx scripts/generate-primitives-classes.ts && prettier --write demo/components/generated/primitives-classes.ts",
173
182
  "generate:logo-lockup": "tsx scripts/generate-logo-lockup.ts",
183
+ "api:report": "tsx scripts/api-report.ts",
184
+ "mutation": "stryker run",
174
185
  "docs:scripts": "tsx scripts/generate-development-scripts-doc.ts && prettier --write docs/development-scripts.md",
175
186
  "badge:bundle-size": "tsx scripts/generate-bundle-badge.ts",
176
187
  "visual-diff": "tsx scripts/visual-diff.ts",