zombie-mermaid 5.11.2 → 5.12.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 +2 -1
- package/dist/cli.js +106 -51
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Ultra-fast, fully themeable, zero DOM dependencies. A maintained fork of [`beaut
|
|
|
30
30
|
|
|
31
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 (
|
|
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 (55 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. Who maintains it, how releases are published, and what is claimed: [provenance and continuity](docs/provenance.md).
|
|
36
36
|
|
|
@@ -214,6 +214,7 @@ zombie-mermaid render diagram.mmd --ascii -o out.txt # Write the ASCII render
|
|
|
214
214
|
zombie-mermaid render diagram.mmd --html # Self-contained pan/zoom HTML viewer
|
|
215
215
|
zombie-mermaid render diagram.mmd --png # Rasterize to diagram.png
|
|
216
216
|
cat diagram.mmd | zombie-mermaid render --ascii # Read from stdin
|
|
217
|
+
zombie-mermaid --check README.md docs/*.md # Validate every mermaid block in Markdown; exit 1 with file:line diagnostics
|
|
217
218
|
zombie-mermaid themes # List built-in theme names
|
|
218
219
|
zombie-mermaid --help # Show all options
|
|
219
220
|
```
|
package/dist/cli.js
CHANGED
|
@@ -45,7 +45,7 @@ var T = {
|
|
|
45
45
|
label: "the PNG rasterization"
|
|
46
46
|
}
|
|
47
47
|
}, E = Object.keys(T);
|
|
48
|
-
function
|
|
48
|
+
function ee(e, t) {
|
|
49
49
|
let i = r(e);
|
|
50
50
|
return n({
|
|
51
51
|
dir: i.dir,
|
|
@@ -53,12 +53,21 @@ function D(e, t) {
|
|
|
53
53
|
ext: t
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
-
function
|
|
56
|
+
function D(e) {
|
|
57
57
|
let [t, ...n] = e;
|
|
58
58
|
if (t === void 0 || t === "--help" || t === "-h") return { command: "help" };
|
|
59
59
|
if (t === "--version" || t === "-v") return { command: "version" };
|
|
60
|
+
if (t === "--check") {
|
|
61
|
+
if (n.length === 0) throw Error("--check requires at least one Markdown file");
|
|
62
|
+
let e = n.find((e) => e.startsWith("-"));
|
|
63
|
+
if (e !== void 0) throw Error(`Unknown flag: ${e}`);
|
|
64
|
+
return {
|
|
65
|
+
command: "check",
|
|
66
|
+
files: n
|
|
67
|
+
};
|
|
68
|
+
}
|
|
60
69
|
if (t === "themes") return { command: "themes" };
|
|
61
|
-
if (t === "render") return
|
|
70
|
+
if (t === "render") return j(n);
|
|
62
71
|
if (t === "validate") {
|
|
63
72
|
let e = n.find((e, t) => t > 0 || e.startsWith("-"));
|
|
64
73
|
if (e !== void 0) throw Error(`Unexpected argument: ${e}`);
|
|
@@ -67,17 +76,17 @@ function O(e) {
|
|
|
67
76
|
input: n[0]
|
|
68
77
|
};
|
|
69
78
|
}
|
|
70
|
-
if (t === "web") return
|
|
79
|
+
if (t === "web") return M(n);
|
|
71
80
|
if (t === "mcp") return { command: "mcp" };
|
|
72
81
|
throw Error(`Unknown command: ${t}`);
|
|
73
82
|
}
|
|
74
|
-
function
|
|
83
|
+
function O(e, t, n) {
|
|
75
84
|
let r = e[t + 1];
|
|
76
85
|
if (t + 1 >= e.length || r === void 0) throw Error(`${n} requires a numeric value`);
|
|
77
86
|
if (!/^\d+$/.test(r)) throw Error(`${n} requires a non-negative integer, got: "${r}"`);
|
|
78
87
|
return Number(r);
|
|
79
88
|
}
|
|
80
|
-
function
|
|
89
|
+
function k(e, t, n) {
|
|
81
90
|
let r = e[t + 1];
|
|
82
91
|
if (t + 1 >= e.length || r === void 0) throw Error(`${n} requires a value ("auto" or a positive integer)`);
|
|
83
92
|
if (r === "auto") return "auto";
|
|
@@ -85,14 +94,14 @@ function A(e, t, n) {
|
|
|
85
94
|
if (!/^\d+$/.test(r) || i === 0 || !Number.isSafeInteger(i)) throw Error(`${n} requires "auto" or a positive integer, got: "${r}"`);
|
|
86
95
|
return i;
|
|
87
96
|
}
|
|
88
|
-
function
|
|
97
|
+
function A(e, t, n) {
|
|
89
98
|
let r = e[t + 1];
|
|
90
99
|
if (t + 1 >= e.length || r === void 0) throw Error(`${n} requires a value (one of TD, TB, BT, LR, RL)`);
|
|
91
100
|
let i = r.toUpperCase();
|
|
92
101
|
if (!s(i)) throw Error(`${n} requires one of TD, TB, BT, LR, RL, got: "${r}"`);
|
|
93
102
|
return i;
|
|
94
103
|
}
|
|
95
|
-
function
|
|
104
|
+
function j(e) {
|
|
96
105
|
let t, n = !1, r = !1, i = !1, a = !1, o = !1, s, c = !1, l, u, d, f, p = !1, m = !1, h, g, _, v, y = 0;
|
|
97
106
|
for (; y < e.length;) {
|
|
98
107
|
let b = e[y];
|
|
@@ -109,12 +118,12 @@ function M(e) {
|
|
|
109
118
|
else if (b === "--theme") {
|
|
110
119
|
if (y + 1 >= e.length) throw Error("--theme requires a theme name");
|
|
111
120
|
l = e[y + 1], y += 2;
|
|
112
|
-
} else if (b === "-x" || b === "--paddingX") u =
|
|
113
|
-
else if (b === "-y" || b === "--paddingY") d =
|
|
114
|
-
else if (b === "-p" || b === "--borderPadding") f =
|
|
121
|
+
} else if (b === "-x" || b === "--paddingX") u = O(e, y, b), y += 2;
|
|
122
|
+
else if (b === "-y" || b === "--paddingY") d = O(e, y, b), y += 2;
|
|
123
|
+
else if (b === "-p" || b === "--borderPadding") f = O(e, y, b), y += 2;
|
|
115
124
|
else if (b === "--coords") p = !0, y++;
|
|
116
125
|
else if (b === "--hyperlinks") m = !0, y++;
|
|
117
|
-
else if (b === "-w" || b === "--max-width") h =
|
|
126
|
+
else if (b === "-w" || b === "--max-width") h = k(e, y, b), y += 2;
|
|
118
127
|
else if (b === "--profile") {
|
|
119
128
|
let t = e[y + 1];
|
|
120
129
|
if (t !== "markdown") throw Error(`--profile requires one of: markdown, got: "${t ?? ""}"`);
|
|
@@ -123,7 +132,7 @@ function M(e) {
|
|
|
123
132
|
let t = b.indexOf("="), n = t === -1 ? e[y + 1] : b.slice(t + 1);
|
|
124
133
|
if (n !== "mermaid" && n !== "mermaid-wide") throw Error(`--layout requires one of: mermaid, mermaid-wide, got: "${n ?? ""}"`);
|
|
125
134
|
_ = n, y += t === -1 ? 2 : 1;
|
|
126
|
-
} else if (b === "--direction") v =
|
|
135
|
+
} else if (b === "--direction") v = A(e, y, b), y += 2;
|
|
127
136
|
else if (!b.startsWith("-") || b === "-") {
|
|
128
137
|
if (b === "-") throw Error("Unexpected argument: - (use -o - to write output to stdout; omit the input file to read stdin)");
|
|
129
138
|
if (t !== void 0) throw Error(`Unexpected argument: ${b} (input file already set to "${t}")`);
|
|
@@ -162,19 +171,19 @@ function M(e) {
|
|
|
162
171
|
}
|
|
163
172
|
throw Error("Specify --ascii, --svg, --html, and/or --png (or -o <path> with a .svg/.txt/.html/.png extension)");
|
|
164
173
|
}
|
|
165
|
-
let
|
|
174
|
+
let D = {
|
|
166
175
|
svg: r,
|
|
167
176
|
html: a,
|
|
168
177
|
png: o
|
|
169
|
-
},
|
|
170
|
-
if (s === "-" && n &&
|
|
171
|
-
let e = T[
|
|
178
|
+
}, j = E.find((e) => D[e]);
|
|
179
|
+
if (s === "-" && n && j !== void 0) {
|
|
180
|
+
let e = T[j].display;
|
|
172
181
|
throw Error(`-o - would send both ASCII and ${e} to stdout; drop --ascii, or write the ${e} to a file path`);
|
|
173
182
|
}
|
|
174
|
-
if (
|
|
175
|
-
let { flag: e, ext: n } = T[
|
|
183
|
+
if (j !== void 0 && s === void 0) {
|
|
184
|
+
let { flag: e, ext: n } = T[j];
|
|
176
185
|
if (t === void 0) throw Error(`${e} needs -o <path> (or -o - for stdout) when reading from stdin — there is no input file name to derive an output name from`);
|
|
177
|
-
s =
|
|
186
|
+
s = ee(t, n);
|
|
178
187
|
}
|
|
179
188
|
if (h !== void 0 && !n) throw Error("-w/--max-width requires --ascii");
|
|
180
189
|
if (g !== void 0 && !n) throw Error("--profile requires --ascii");
|
|
@@ -203,12 +212,12 @@ function M(e) {
|
|
|
203
212
|
direction: v
|
|
204
213
|
};
|
|
205
214
|
}
|
|
206
|
-
function
|
|
215
|
+
function M(e) {
|
|
207
216
|
let t = S, n = 0;
|
|
208
217
|
for (; n < e.length;) {
|
|
209
218
|
let r = e[n];
|
|
210
219
|
if (r === void 0) throw Error(`parseArgs: index ${n} out of range while parsing arguments`);
|
|
211
|
-
if (r === "--port") t =
|
|
220
|
+
if (r === "--port") t = O(e, n, r), n += 2;
|
|
212
221
|
else throw Error(`Unknown flag: ${r}`);
|
|
213
222
|
}
|
|
214
223
|
return {
|
|
@@ -456,8 +465,47 @@ async function Y() {
|
|
|
456
465
|
});
|
|
457
466
|
}
|
|
458
467
|
//#endregion
|
|
468
|
+
//#region src/cli/check.ts
|
|
469
|
+
var he = /^ {0,3}(`{3,}|~{3,})\s*mermaid\b[^`]*$/i, ge = /^ {0,3}(`+|~+)\s*$/;
|
|
470
|
+
function _e(e, t) {
|
|
471
|
+
let n = ge.exec(e)?.[1];
|
|
472
|
+
return !!n && n[0] === t[0] && n.length >= t.length;
|
|
473
|
+
}
|
|
474
|
+
function ve(e) {
|
|
475
|
+
let t = e.split(/\r?\n/), n = [];
|
|
476
|
+
for (let e = 0; e < t.length; e++) {
|
|
477
|
+
let r = he.exec(t[e]);
|
|
478
|
+
if (!r) continue;
|
|
479
|
+
let i = r[1], a = [], o = e + 1;
|
|
480
|
+
for (; o < t.length && !_e(t[o], i);) a.push(t[o++]);
|
|
481
|
+
n.push({
|
|
482
|
+
startLine: e + 2,
|
|
483
|
+
source: a.join("\n")
|
|
484
|
+
}), e = o;
|
|
485
|
+
}
|
|
486
|
+
return n;
|
|
487
|
+
}
|
|
488
|
+
async function ye(e) {
|
|
489
|
+
let t = 0, n = 0;
|
|
490
|
+
for (let r of e.files) {
|
|
491
|
+
let e = await u(r, "utf-8");
|
|
492
|
+
for (let i of ve(e)) {
|
|
493
|
+
n++;
|
|
494
|
+
try {
|
|
495
|
+
g(i.source);
|
|
496
|
+
} catch (e) {
|
|
497
|
+
t++;
|
|
498
|
+
let n = N(e, i.source), a = i.startLine + (n ? n.line - 1 : 0), o = n ? n.message : e instanceof Error ? e.message : String(e);
|
|
499
|
+
console.error(`${r}:${a}: ${o}`), n && console.error(` ${a} | ${n.sourceLine}`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (t > 0) throw Error(`${t} of ${n} Mermaid block(s) invalid`);
|
|
504
|
+
console.log(`${n} Mermaid block(s) valid`);
|
|
505
|
+
}
|
|
506
|
+
//#endregion
|
|
459
507
|
//#region src/cli/validate.ts
|
|
460
|
-
async function
|
|
508
|
+
async function be(e, t) {
|
|
461
509
|
let n;
|
|
462
510
|
if (e.input !== void 0) n = await u(e.input, "utf-8");
|
|
463
511
|
else if (t !== void 0) n = t;
|
|
@@ -476,12 +524,12 @@ async function he(e, t) {
|
|
|
476
524
|
}
|
|
477
525
|
//#endregion
|
|
478
526
|
//#region src/cli/web.ts
|
|
479
|
-
var
|
|
480
|
-
function
|
|
527
|
+
var xe = 1e6;
|
|
528
|
+
function Se(e) {
|
|
481
529
|
return new Promise((t, n) => {
|
|
482
530
|
let r = [], i = 0;
|
|
483
531
|
e.on("data", (t) => {
|
|
484
|
-
if (i += t.length, i >
|
|
532
|
+
if (i += t.length, i > xe) {
|
|
485
533
|
n(/* @__PURE__ */ Error("Request body too large")), e.destroy();
|
|
486
534
|
return;
|
|
487
535
|
}
|
|
@@ -492,7 +540,7 @@ function _e(e) {
|
|
|
492
540
|
function X(e) {
|
|
493
541
|
return typeof e == "object" && !!e;
|
|
494
542
|
}
|
|
495
|
-
function
|
|
543
|
+
function Z(e) {
|
|
496
544
|
let t;
|
|
497
545
|
try {
|
|
498
546
|
t = JSON.parse(e);
|
|
@@ -507,80 +555,80 @@ function ve(e) {
|
|
|
507
555
|
format: t.format === "svg" ? "svg" : "ascii"
|
|
508
556
|
};
|
|
509
557
|
}
|
|
510
|
-
function
|
|
558
|
+
function Q(e, t, n) {
|
|
511
559
|
let r = JSON.stringify(n);
|
|
512
560
|
e.writeHead(t, {
|
|
513
561
|
"Content-Type": "application/json; charset=utf-8",
|
|
514
562
|
"Content-Length": Buffer.byteLength(r)
|
|
515
563
|
}), e.end(r);
|
|
516
564
|
}
|
|
517
|
-
async function
|
|
565
|
+
async function Ce(e, t) {
|
|
518
566
|
try {
|
|
519
|
-
let { source: n, format: r } =
|
|
520
|
-
|
|
567
|
+
let { source: n, format: r } = Z(await Se(e));
|
|
568
|
+
Q(t, 200, {
|
|
521
569
|
output: r === "svg" ? h(n) : p(n),
|
|
522
570
|
format: r
|
|
523
571
|
});
|
|
524
572
|
} catch (e) {
|
|
525
|
-
|
|
573
|
+
Q(t, 400, { error: e instanceof Error ? e.message : String(e) });
|
|
526
574
|
}
|
|
527
575
|
}
|
|
528
|
-
var
|
|
529
|
-
function
|
|
576
|
+
var we = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<title>zombie-mermaid</title>\n<style>\n body { font-family: system-ui, sans-serif; margin: 2rem; max-width: 900px; }\n h1 { font-size: 1.25rem; }\n textarea { width: 100%; height: 200px; font-family: monospace; font-size: 14px; box-sizing: border-box; }\n pre { white-space: pre; overflow: auto; background: #111; color: #eee; padding: 1rem; border-radius: 4px; }\n .row { display: flex; gap: 1rem; align-items: center; margin: 0.75rem 0; }\n #error { color: #c00; white-space: pre-wrap; }\n #output svg { max-width: 100%; }\n</style>\n</head>\n<body>\n<h1>zombie-mermaid</h1>\n<textarea id=\"source\" placeholder=\"graph LR\n A --> B\">graph LR\n A --> B --> C</textarea>\n<div class=\"row\">\n <label><input type=\"radio\" name=\"format\" value=\"ascii\" checked /> ASCII</label>\n <label><input type=\"radio\" name=\"format\" value=\"svg\" /> SVG</label>\n <button id=\"render\" type=\"button\">Render</button>\n</div>\n<div id=\"error\"></div>\n<div id=\"output\"></div>\n<script>\n const sourceEl = document.getElementById('source')\n const outputEl = document.getElementById('output')\n const errorEl = document.getElementById('error')\n\n async function render() {\n errorEl.textContent = ''\n outputEl.innerHTML = ''\n const formatInput = document.querySelector('input[name=\"format\"]:checked')\n const format = formatInput ? formatInput.value : 'ascii'\n try {\n const res = await fetch('/render', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ source: sourceEl.value, format }),\n })\n const data = await res.json()\n if (!res.ok) {\n errorEl.textContent = data.error || 'Render failed'\n return\n }\n if (format === 'svg') {\n outputEl.innerHTML = data.output\n } else {\n const pre = document.createElement('pre')\n pre.textContent = data.output\n outputEl.appendChild(pre)\n }\n } catch (err) {\n errorEl.textContent = String(err)\n }\n }\n\n document.getElementById('render').addEventListener('click', render)\n<\/script>\n</body>\n</html>\n";
|
|
577
|
+
function Te() {
|
|
530
578
|
return v((e, t) => {
|
|
531
579
|
let n = (e.url ?? "/").split("?")[0];
|
|
532
580
|
if (e.method === "GET" && (n === "/" || n === "")) {
|
|
533
|
-
t.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), t.end(
|
|
581
|
+
t.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), t.end(we);
|
|
534
582
|
return;
|
|
535
583
|
}
|
|
536
584
|
if (e.method === "POST" && n === "/render") {
|
|
537
|
-
|
|
585
|
+
Ce(e, t);
|
|
538
586
|
return;
|
|
539
587
|
}
|
|
540
588
|
t.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" }), t.end("Not found");
|
|
541
589
|
});
|
|
542
590
|
}
|
|
543
|
-
function
|
|
591
|
+
function Ee(e) {
|
|
544
592
|
return new Promise((t, n) => {
|
|
545
|
-
let r =
|
|
593
|
+
let r = Te();
|
|
546
594
|
r.once("error", n), r.listen(e, "127.0.0.1", () => {
|
|
547
595
|
r.removeListener("error", n), t(r);
|
|
548
596
|
});
|
|
549
597
|
});
|
|
550
598
|
}
|
|
551
|
-
async function
|
|
552
|
-
let n = await
|
|
599
|
+
async function De(e, t = console.log) {
|
|
600
|
+
let n = await Ee(e.port), r = n.address();
|
|
553
601
|
return t(`zombie-mermaid web running at http://localhost:${X(r) && typeof r.port == "number" ? r.port : e.port}`), n;
|
|
554
602
|
}
|
|
555
603
|
//#endregion
|
|
556
604
|
//#region src/cli/mcp.ts
|
|
557
|
-
async function
|
|
605
|
+
async function Oe(e = (e) => console.error(e)) {
|
|
558
606
|
let t = b(), n = new y();
|
|
559
607
|
return await t.connect(n), e("zombie-mermaid MCP server running on stdio (experimental — feedback welcome: https://github.com/dfadler/zombie-mermaid/issues)"), t;
|
|
560
608
|
}
|
|
561
609
|
//#endregion
|
|
562
610
|
//#region src/package-info.ts
|
|
563
|
-
function
|
|
611
|
+
function ke() {
|
|
564
612
|
let t = e(import.meta.url)("../package.json");
|
|
565
613
|
return typeof t == "object" && t && "version" in t && typeof t.version == "string" ? t.version : "unknown";
|
|
566
614
|
}
|
|
567
615
|
//#endregion
|
|
568
616
|
//#region src/cli.ts
|
|
569
617
|
m(x);
|
|
570
|
-
var
|
|
618
|
+
var Ae = ke();
|
|
571
619
|
async function $() {
|
|
572
620
|
let e = process.argv.slice(2), t;
|
|
573
621
|
try {
|
|
574
|
-
t =
|
|
622
|
+
t = D(e);
|
|
575
623
|
} catch (e) {
|
|
576
624
|
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`), process.exit(1);
|
|
577
625
|
}
|
|
578
626
|
switch (t.command) {
|
|
579
627
|
case "help":
|
|
580
|
-
|
|
628
|
+
je();
|
|
581
629
|
break;
|
|
582
630
|
case "version":
|
|
583
|
-
console.log(`zombie-mermaid ${
|
|
631
|
+
console.log(`zombie-mermaid ${Ae}`);
|
|
584
632
|
break;
|
|
585
633
|
case "themes":
|
|
586
634
|
console.log("Available themes:\n");
|
|
@@ -595,27 +643,34 @@ async function $() {
|
|
|
595
643
|
break;
|
|
596
644
|
case "validate":
|
|
597
645
|
try {
|
|
598
|
-
await
|
|
646
|
+
await be(t);
|
|
647
|
+
} catch (e) {
|
|
648
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`), process.exit(1);
|
|
649
|
+
}
|
|
650
|
+
break;
|
|
651
|
+
case "check":
|
|
652
|
+
try {
|
|
653
|
+
await ye(t);
|
|
599
654
|
} catch (e) {
|
|
600
655
|
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`), process.exit(1);
|
|
601
656
|
}
|
|
602
657
|
break;
|
|
603
658
|
case "web":
|
|
604
659
|
try {
|
|
605
|
-
await
|
|
660
|
+
await De(t);
|
|
606
661
|
} catch (e) {
|
|
607
662
|
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`), process.exit(1);
|
|
608
663
|
}
|
|
609
664
|
break;
|
|
610
665
|
case "mcp": try {
|
|
611
|
-
await
|
|
666
|
+
await Oe();
|
|
612
667
|
} catch (e) {
|
|
613
668
|
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`), process.exit(1);
|
|
614
669
|
}
|
|
615
670
|
}
|
|
616
671
|
}
|
|
617
|
-
function
|
|
618
|
-
console.log("zombie-mermaid — render Mermaid diagrams from the command line\n\nUsage:\n zombie-mermaid render <file> --ascii Render to ASCII in terminal\n zombie-mermaid render <file> --svg Render to <file stem>.svg\n zombie-mermaid render <file> --svg -o <out.svg> Render to a named SVG file\n zombie-mermaid render <file> -o out.svg Format inferred from the extension\n zombie-mermaid render <file> --svg -o - Write SVG to stdout\n zombie-mermaid render <file> --ascii --svg -o <out.svg> Both (ASCII to terminal)\n zombie-mermaid render <file> --html Self-contained pan/zoom HTML viewer\n zombie-mermaid render <file> --png Rasterize to <file stem>.png\n cat file.mmd | zombie-mermaid render --ascii Read from stdin\n zombie-mermaid validate [file] Parse only; exit 1 with line diagnostics if invalid\n zombie-mermaid themes List available themes\n zombie-mermaid web [--port <n>] Start a local web UI (default port: 3000)\n zombie-mermaid mcp Start an MCP server on stdio (render tools)\n zombie-mermaid --help Show this help\n zombie-mermaid --version Show version\n\nOptions:\n --ascii Print ASCII/Unicode diagram to terminal, or to -o <file.txt>\n when it is the only format\n --svg Render SVG to -o <path> (default: <input stem>.svg;\n stdin input must give -o)\n --resolve-colors Replace CSS var()/color-mix() in the SVG with computed\n sRGB values, for rasterizers and other non-browser SVG\n consumers (resvg, librsvg, Inkscape) that don't\n evaluate them. The default output stays a live function\n of its CSS variables. Requires --svg.\n --html Render a self-contained HTML pan/zoom viewer to -o\n <path> (default: <input stem>.html). Embeds the SVG;\n no server, no network, opens from disk. Cannot be\n combined with --svg/--png (different content, same\n slot).\n --png Rasterize to PNG at -o <path> (default: <input\n stem>.png), via the optional @resvg/resvg-js native\n dependency — install it with `pnpm add\n @resvg/resvg-js` if it's missing. Always resolves\n CSS var()/color-mix() first (as --resolve-colors\n does for --svg), since a rasterizer can't evaluate\n them. 1:1 with the SVG's own pixel size, no scaling.\n Cannot be combined with --svg/--html.\n -o, --output <path> Output file for the SVG/HTML/PNG (or for ASCII when\n none of those is given). '-' writes to stdout. A\n .svg, .html, .txt, or .png extension selects the\n format when no --svg/--html/--png/--ascii flag is\n given; one that contradicts the flags is an error.\n Existing files are never overwritten without --force.\n -f, --force Overwrite an existing output file\n --theme <name> Apply a built-in theme (see 'themes' command)\n --direction <dir> Override the diagram's layout direction: TD, TB, BT, LR, or RL\n (flowchart, state, and ER diagrams; nested subgraph\n directions still apply on top of it)\n -x, --paddingX <n> Horizontal spacing between nodes (ASCII, default: 5)\n -y, --paddingY <n> Vertical spacing between nodes (ASCII, default: 5; LR/RL sibling gap is n-1, min 1)\n -p, --borderPadding <n> Padding inside node boxes (ASCII, default: 1)\n --coords Overlay row/column index rulers (ASCII, debug layout)\n --hyperlinks Emit OSC 8 terminal hyperlinks for `click` hrefs\n (ASCII). Off by default: terminal support varies —\n iTerm2, WezTerm, kitty, Windows Terminal, and\n VTE-based terminals render them as clickable links;\n others may show the URL or nothing. Pagers need\n OSC 8 support too (`less -R`, recent versions).\n --profile <name> ASCII preset: 'markdown' = plain ASCII characters, no\n color, for pasting into issues/commits/fenced blocks\n (explicit flags like --coords still win). Requires\n --ascii.\n -w, --max-width <n|auto> Fit ASCII output within this many columns\n ('auto' detects the terminal width): applies compact\n spacing automatically when needed, and warns on\n stderr if it still doesn't fit (no label wrapping or\n direction-flip — see issue #335).\n --layout mermaid Opt in (flowcharts, --ascii): draw mermaid's own ELK layout\n instead of the grid renderer. Subgraphs, some shapes,\n other diagram types, layout failures and drawings that\n cannot fit 120 columns (or -w <n>, used as the cap)\n fall back to the default renderer; stderr says which\n one ran. --layout mermaid-wide drops the cap and always\n draws the (possibly wide) layout-faithful text. Merged\n parallel edges hide edge multiplicity. Depends on\n mermaid 12.1.0 + elkjs 0.9.3: install the optional peer\n with `pnpm add elkjs-0.9.3@npm:elkjs@0.9.3`. -x/-y/-p\n and --coords do not apply to the layout-faithful drawing.\n --port <n> Port for the 'web' command (default: 3000)\n -h, --help Show help\n -v, --version Show version");
|
|
672
|
+
function je() {
|
|
673
|
+
console.log("zombie-mermaid — render Mermaid diagrams from the command line\n\nUsage:\n zombie-mermaid render <file> --ascii Render to ASCII in terminal\n zombie-mermaid render <file> --svg Render to <file stem>.svg\n zombie-mermaid render <file> --svg -o <out.svg> Render to a named SVG file\n zombie-mermaid render <file> -o out.svg Format inferred from the extension\n zombie-mermaid render <file> --svg -o - Write SVG to stdout\n zombie-mermaid render <file> --ascii --svg -o <out.svg> Both (ASCII to terminal)\n zombie-mermaid render <file> --html Self-contained pan/zoom HTML viewer\n zombie-mermaid render <file> --png Rasterize to <file stem>.png\n cat file.mmd | zombie-mermaid render --ascii Read from stdin\n zombie-mermaid validate [file] Parse only; exit 1 with line diagnostics if invalid\n zombie-mermaid --check <file.md>... Validate every mermaid fenced block in Markdown; exit 1 with file:line diagnostics\n zombie-mermaid themes List available themes\n zombie-mermaid web [--port <n>] Start a local web UI (default port: 3000)\n zombie-mermaid mcp Start an MCP server on stdio (render tools)\n zombie-mermaid --help Show this help\n zombie-mermaid --version Show version\n\nOptions:\n --ascii Print ASCII/Unicode diagram to terminal, or to -o <file.txt>\n when it is the only format\n --svg Render SVG to -o <path> (default: <input stem>.svg;\n stdin input must give -o)\n --resolve-colors Replace CSS var()/color-mix() in the SVG with computed\n sRGB values, for rasterizers and other non-browser SVG\n consumers (resvg, librsvg, Inkscape) that don't\n evaluate them. The default output stays a live function\n of its CSS variables. Requires --svg.\n --html Render a self-contained HTML pan/zoom viewer to -o\n <path> (default: <input stem>.html). Embeds the SVG;\n no server, no network, opens from disk. Cannot be\n combined with --svg/--png (different content, same\n slot).\n --png Rasterize to PNG at -o <path> (default: <input\n stem>.png), via the optional @resvg/resvg-js native\n dependency — install it with `pnpm add\n @resvg/resvg-js` if it's missing. Always resolves\n CSS var()/color-mix() first (as --resolve-colors\n does for --svg), since a rasterizer can't evaluate\n them. 1:1 with the SVG's own pixel size, no scaling.\n Cannot be combined with --svg/--html.\n -o, --output <path> Output file for the SVG/HTML/PNG (or for ASCII when\n none of those is given). '-' writes to stdout. A\n .svg, .html, .txt, or .png extension selects the\n format when no --svg/--html/--png/--ascii flag is\n given; one that contradicts the flags is an error.\n Existing files are never overwritten without --force.\n -f, --force Overwrite an existing output file\n --theme <name> Apply a built-in theme (see 'themes' command)\n --direction <dir> Override the diagram's layout direction: TD, TB, BT, LR, or RL\n (flowchart, state, and ER diagrams; nested subgraph\n directions still apply on top of it)\n -x, --paddingX <n> Horizontal spacing between nodes (ASCII, default: 5)\n -y, --paddingY <n> Vertical spacing between nodes (ASCII, default: 5; LR/RL sibling gap is n-1, min 1)\n -p, --borderPadding <n> Padding inside node boxes (ASCII, default: 1)\n --coords Overlay row/column index rulers (ASCII, debug layout)\n --hyperlinks Emit OSC 8 terminal hyperlinks for `click` hrefs\n (ASCII). Off by default: terminal support varies —\n iTerm2, WezTerm, kitty, Windows Terminal, and\n VTE-based terminals render them as clickable links;\n others may show the URL or nothing. Pagers need\n OSC 8 support too (`less -R`, recent versions).\n --profile <name> ASCII preset: 'markdown' = plain ASCII characters, no\n color, for pasting into issues/commits/fenced blocks\n (explicit flags like --coords still win). Requires\n --ascii.\n -w, --max-width <n|auto> Fit ASCII output within this many columns\n ('auto' detects the terminal width): applies compact\n spacing automatically when needed, and warns on\n stderr if it still doesn't fit (no label wrapping or\n direction-flip — see issue #335).\n --layout mermaid Opt in (flowcharts, --ascii): draw mermaid's own ELK layout\n instead of the grid renderer. Subgraphs, some shapes,\n other diagram types, layout failures and drawings that\n cannot fit 120 columns (or -w <n>, used as the cap)\n fall back to the default renderer; stderr says which\n one ran. --layout mermaid-wide drops the cap and always\n draws the (possibly wide) layout-faithful text. Merged\n parallel edges hide edge multiplicity. Depends on\n mermaid 12.1.0 + elkjs 0.9.3: install the optional peer\n with `pnpm add elkjs-0.9.3@npm:elkjs@0.9.3`. -x/-y/-p\n and --coords do not apply to the layout-faithful drawing.\n --port <n> Port for the 'web' command (default: 3000)\n -h, --help Show help\n -v, --version Show version");
|
|
619
674
|
}
|
|
620
675
|
$();
|
|
621
676
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zombie-mermaid",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"mcpName": "io.github.dfadler/zombie-mermaid",
|
|
6
6
|
"description": "Render Mermaid diagrams as beautiful SVGs or ASCII art. Ultra-fast, fully themeable, zero DOM dependencies. Maintained fork of beautiful-mermaid.",
|
|
@@ -115,11 +115,11 @@
|
|
|
115
115
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
116
116
|
"elkjs": "^0.12.0",
|
|
117
117
|
"entities": "^7.0.1",
|
|
118
|
-
"@zombie-mermaid/ascii-renderer": "5.
|
|
119
|
-
"@zombie-mermaid/core": "5.
|
|
120
|
-
"@zombie-mermaid/
|
|
121
|
-
"@zombie-mermaid/
|
|
122
|
-
"@zombie-mermaid/
|
|
118
|
+
"@zombie-mermaid/ascii-renderer": "5.12.0",
|
|
119
|
+
"@zombie-mermaid/core": "5.12.0",
|
|
120
|
+
"@zombie-mermaid/mcp": "5.12.0",
|
|
121
|
+
"@zombie-mermaid/mermaid-parser": "5.12.0",
|
|
122
|
+
"@zombie-mermaid/svg-renderer": "5.12.0"
|
|
123
123
|
},
|
|
124
124
|
"devDependencies": {
|
|
125
125
|
"@arethetypeswrong/cli": "0.18.5",
|
|
@@ -213,6 +213,7 @@
|
|
|
213
213
|
"generate:logo-lockup": "tsx scripts/generate-logo-lockup.ts",
|
|
214
214
|
"api:report": "tsx scripts/api-report.ts",
|
|
215
215
|
"mutation": "stryker run",
|
|
216
|
+
"docs:llms": "tsx scripts/generate-llms-full.ts",
|
|
216
217
|
"docs:scripts": "tsx scripts/generate-development-scripts-doc.ts && oxfmt docs/development-scripts.md",
|
|
217
218
|
"badge:bundle-size": "tsx scripts/generate-bundle-badge.ts",
|
|
218
219
|
"visual-diff": "tsx scripts/visual-diff.ts",
|