zombie-mermaid 4.0.0 → 4.1.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.
Files changed (40) hide show
  1. package/README.md +8 -5
  2. package/dist/cli.js +1 -0
  3. package/dist/index.cjs +1 -3
  4. package/dist/index.d.cts +2 -6
  5. package/dist/index.d.ts +2 -6
  6. package/dist/index.js +5 -439
  7. package/package.json +7 -7
  8. package/src/__tests__/ascii-draw-bundles.test.ts +4 -2
  9. package/src/__tests__/ascii-edge-bundling-routing.test.ts +1 -1
  10. package/src/__tests__/ascii-edge-cross-style-overlap.test.ts +1 -1
  11. package/src/__tests__/ascii-grid-deep-chain.test.ts +1 -1
  12. package/src/__tests__/ascii-nested-subgraph-sibling-order-444.test.ts +1 -1
  13. package/src/__tests__/ascii-nonmember-beside-frame-1283.test.ts +1 -1
  14. package/src/__tests__/ascii-package-boundary.test.ts +4 -6
  15. package/src/__tests__/ascii-parallel-edges-329.test.ts +1 -1
  16. package/src/__tests__/ascii-reverse-direction-edges-629.test.ts +1 -1
  17. package/src/__tests__/ascii-subgraph-bbox.test.ts +1 -1
  18. package/src/__tests__/ascii-subgraph-later-edgeless-301.test.ts +1 -1
  19. package/src/__tests__/class-styling.test.ts +1 -1
  20. package/src/__tests__/edge-approach-direction.test.ts +1 -1
  21. package/src/__tests__/elk-layout-cache.test.ts +1 -1
  22. package/src/__tests__/expanded-shapes.test.ts +1 -1
  23. package/src/__tests__/flowchart-back-edge-prereversal-1239.test.ts +31 -1
  24. package/src/__tests__/flowchart-syntax-gaps.test.ts +1 -1
  25. package/src/__tests__/interactivity-config.test.ts +1 -1
  26. package/src/__tests__/layout-engine.test.ts +1 -1
  27. package/src/__tests__/linkstyle.test.ts +1 -1
  28. package/src/__tests__/multiline-labels.test.ts +1 -1
  29. package/src/__tests__/parser.test.ts +1 -1
  30. package/src/__tests__/render-options-per-type.test.ts +1 -1
  31. package/src/__tests__/self-loop-edges.test.ts +1 -1
  32. package/src/__tests__/state-styling.test.ts +1 -1
  33. package/src/__tests__/styles.test.ts +20 -1
  34. package/src/__tests__/subgraph-title-edge-gap-1239.test.ts +21 -0
  35. package/src/__tests__/svg-csp-style-options.test.ts +2 -1
  36. package/src/__tests__/workspace-package-boundaries.test.ts +11 -25
  37. package/src/index.ts +1 -1
  38. package/dist/index.cjs.map +0 -1
  39. package/dist/index.js.map +0 -1
  40. package/src/parser.ts +0 -1324
package/README.md CHANGED
@@ -48,11 +48,14 @@ Diagrams are essential for AI-assisted programming. When you're working with an
48
48
 
49
49
  The **Bundle Size** badge above tracks the gzipped size of `zombie-mermaid`'s
50
50
  main entry point (`dist/index.js`) together with the `@zombie-mermaid/*`
51
- workspace packages it imports (third-party dependencies such as ELK.js are not
52
- counted), and updates automatically with every release — the same
53
- badge/CI-tracked pattern already backing the coverage and CI status badges.
54
- The standalone renderer packages under [Packages](#packages) get the same
55
- measurement for their own dependency closure. Need ASCII rendering only? `import { renderMermaidASCII }
51
+ workspace packages it imports, **plus the SVG renderer's third-party
52
+ dependencies** ([ELK.js](https://github.com/kieler/elkjs) and `entities`,
53
+ which make up most of the SVG renderer's real weight), and updates
54
+ automatically with every release — the same badge/CI-tracked pattern already
55
+ backing the coverage and CI status badges. The standalone renderer packages
56
+ under [Packages](#packages) get the same measurement for their own dependency
57
+ closure: the SVG badge is labelled "incl. deps" because it counts ELK.js and
58
+ `entities`, while the ASCII renderer has no third-party dependencies to count. Need ASCII rendering only? `import { renderMermaidASCII }
56
59
  from 'zombie-mermaid/ascii'` skips [ELK.js](https://github.com/kieler/elkjs),
57
60
  the layout engine the SVG renderer depends on, for a substantially smaller
58
61
  bundle.
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@ import { extname as V, format as H, parse as G } from "node:path";
4
4
  import { THEMES as B, escapeXml as q, isDirection as j, isWideChar as K } from "@zombie-mermaid/core";
5
5
  import { access as J, readFile as Z, writeFile as Q } from "node:fs/promises";
6
6
  import { diagramColorsToAsciiTheme as ee, renderMermaidASCII as O } from "@zombie-mermaid/ascii-renderer";
7
+ import "@zombie-mermaid/mermaid-parser";
7
8
  import { renderMermaidSVG as z } from "@zombie-mermaid/svg-renderer";
8
9
  import { createServer as te } from "node:http";
9
10
  import { StdioServerTransport as ne } from "@modelcontextprotocol/sdk/server/stdio.js";
package/dist/index.cjs CHANGED
@@ -1,3 +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");function i(e){let t=e.trim();return t.length>=2&&(t[0]===`"`&&t[t.length-1]===`"`||t[0]===`'`&&t[t.length-1]===`'`)?t.slice(1,-1):t}var a=`"graph <dir>"/"flowchart <dir>" (dir: TD, TB, LR, BT, RL), "stateDiagram-v2", "sequenceDiagram", "classDiagram", "erDiagram", "xychart-beta", "C4Context"/"C4Container"/"C4Component"/"C4Dynamic"/"C4Deployment", "architecture-beta"`;function o(e){let t=e.trim().toLowerCase();if(/^sequence/.test(t))return`sequenceDiagram`;if(/^class/.test(t))return`classDiagram`;if(/^er/.test(t))return`erDiagram`;if(/^xychart/.test(t))return`xychart-beta`;if(/^state/.test(t))return`stateDiagram-v2`}var s=/^(?:[\w-]+@)?<?(?:-{2,}|={2,}|-\.+-?|~{3,})/;function c(e){let t=[];for(let n of e)n.forEach((e,n)=>{if(n===0&&t.length>0&&s.test(e.text)){let n=t[t.length-1];t[t.length-1]={text:`${n.text} ${e.text}`,line:n.line}}else t.push(e)});return t}function l(t){let n=(0,e.extractInitConfig)(t.split(`
2
- `).map(e=>e.trim())),r=c((0,e.splitStatementsByLine)(t));if(r.length===0)throw Error(`Empty mermaid diagram`);let i=r[0],a=/^stateDiagram(-v2)?\s*$/i.test(i.text)?d(r):u(r);return a.initConfig=n,a}function u(t){let n=t[0];if(n===void 0)throw Error(`parseFlowchart called with no lines`);let r=n.text,s=r.match(/^(?:graph|flowchart)\s+(TD|TB|LR|BT|RL)\s*$/i);if(!s){let e=r.match(/^(?:graph|flowchart)\b\s*(.*)$/i);if(e){let t=e[1].trim();throw Error(t.length>0?`Line ${n.line}: Invalid direction "${t}" in header "${r}". Expected one of: TD, TB, LR, BT, RL.`:`Line ${n.line}: Missing direction in header "${r}". Expected e.g. "graph TD" or "flowchart LR" — one of: TD, TB, LR, BT, RL.`)}let t=o(r),i=t&&t.toLowerCase()!==r.trim().toLowerCase()?` Did you mean "${t}"?`:``;throw Error(`Line ${n.line}: Invalid mermaid header: "${r}".${i} Supported headers: ${a}.`)}let c={direction:(0,e.toDirection)(s[1]),nodes:new Map,edges:[],subgraphs:[],classDefs:new Map,classAssignments:new Map,nodeStyles:new Map,linkStyles:new Map,interactions:new Map},l=[];for(let n=1;n<t.length;n++){let r=t[n].text;if((0,e.tryApplyClassDef)(r,c)||(0,e.tryApplyClassAssignment)(r,c)||(0,e.tryApplyStyleStatement)(r,c))continue;if(/^click\s+/i.test(r)){w(r,c);continue}if(E(r,c))continue;let a=r.match(/^linkStyle\s+(default|[\d,\s]+)\s+(.+)$/);if(a){let t=a[1].trim(),n=(0,e.parseStyleProps)(a[2]);if(t==="default")c.linkStyles.set(`default`,{...c.linkStyles.get(`default`),...n});else{let e=t.split(`,`).map(e=>parseInt(e.trim(),10));for(let t of e)isNaN(t)||c.linkStyles.set(t,{...c.linkStyles.get(t),...n})}continue}let o=r.match(/^direction\s+(TD|TB|LR|BT|RL)\s*$/i);if(o&&l.length>0){l[l.length-1].direction=(0,e.toDirection)(o[1]);continue}let s=r.match(/^subgraph\s+(.+)$/);if(s){let t=s[1].trim(),n=t.match(/^([^\s[]+)\s*\[(.+)\]$/),r,a;n?(r=n[1],a=(0,e.normalizeBrTags)(i(n[2]))):(a=(0,e.normalizeBrTags)(i(t)),r=a.replace(/\s+/g,`_`).replace(/[^\p{L}\p{N}_-]/gu,``));let o={id:r,label:a,nodeIds:[],children:[]};l.push(o);continue}if(r===`end`){let e=l.pop();e&&(l.length>0?l[l.length-1].children.push(e):c.subgraphs.push(e));continue}A(r,c,l)}return c}function d(t){let n={direction:`TD`,nodes:new Map,edges:[],subgraphs:[],classDefs:new Map,classAssignments:new Map,nodeStyles:new Map,linkStyles:new Map,interactions:new Map},r=[],i=new Set,a=0,o=0;for(let s=1;s<t.length;s++){let c=t[s].text;if((0,e.tryApplyClassDef)(c,n)||(0,e.tryApplyClassAssignment)(c,n)||(0,e.tryApplyStyleStatement)(c,n))continue;let l=c.match(/^direction\s+(TD|TB|LR|BT|RL)\s*$/i);if(l){r.length>0?r[r.length-1].direction=(0,e.toDirection)(l[1]):n.direction=(0,e.toDirection)(l[1]);continue}let u=c.match(/^linkStyle\s+(default|[\d,\s]+)\s+(.+)$/);if(u){let t=u[1].trim(),r=(0,e.parseStyleProps)(u[2]);if(t==="default")n.linkStyles.set(`default`,{...n.linkStyles.get(`default`),...r});else{let e=t.split(`,`).map(e=>parseInt(e.trim(),10));for(let t of e)isNaN(t)||n.linkStyles.set(t,{...n.linkStyles.get(t),...r})}continue}if(E(c,n))continue;let d=c.match(/^state\s+(?:"([^"]+)"\s+as\s+)?([\w\p{L}]+)\s*\{$/u);if(d){let e=d[1]??d[2],t=d[2],a={id:t,label:e,nodeIds:[],children:[]};r.push(a),i.add(t),n.nodes.delete(t);continue}if(c===`}`){let e=r.pop();e&&(r.length>0?r[r.length-1].children.push(e):n.subgraphs.push(e));continue}let h=c.match(/^state\s+"([^"]+)"\s+as\s+([\w\p{L}]+)\s*$/u);if(h){let t=(0,e.normalizeBrTags)(h[1]),i=h[2];f(n,r,i,t);continue}let g=c.match(/^(\[\*\]|[\w\p{L}-]+)(?::::([\w][\w-]*))?\s*(?:([\w-]+)@)?-->\s*(\[\*\]|[\w\p{L}-]+)(?::::([\w][\w-]*))?(?:\s*:\s*(.+))?$/u);if(g){let t=g[1],s=g[2],c=g[3],l=g[4],u=g[5],d=g[6]?.trim(),f=d?(0,e.normalizeBrTags)(d):void 0;t===`[*]`?(a++,t=`_start${a>1?a:``}`,p(n,r,{id:t,label:``,shape:`state-start`})):i.has(t)||m(n,r,t),l===`[*]`?(o++,l=`_end${o>1?o:``}`,p(n,r,{id:l,label:``,shape:`state-end`})):i.has(l)||m(n,r,l),s!==void 0&&n.classAssignments.set(t,s),u!==void 0&&n.classAssignments.set(l,u),n.edges.push({source:t,target:l,label:f,style:`solid`,hasArrowStart:!1,hasArrowEnd:!0,...c===void 0?{}:{id:c}});continue}let _=c.match(/^([\w\p{L}-]+):::([\w][\w-]*)\s*$/u);if(_){let e=_[1];i.has(e)||m(n,r,e),n.classAssignments.set(e,_[2]);continue}let v=c.match(/^([\w\p{L}-]+)\s*:\s*(.+)$/u);if(v){let t=v[1];f(n,r,t,(0,e.normalizeBrTags)(v[2].trim()));continue}}for(let e of n.classAssignments.keys())!n.nodes.has(e)&&!i.has(e)&&m(n,[],e);return n}function f(e,t,n,r){let i=e.nodes.get(n);i&&(i.label=r),p(e,t,{id:n,label:r,shape:`rounded`})}function p(e,t,n){if(e.nodes.has(n.id)||e.nodes.set(n.id,n),t.length>0){let e=t[t.length-1];e.nodeIds.includes(n.id)||e.nodeIds.push(n.id)}}function m(e,t,n){if(!e.nodes.has(n))p(e,t,{id:n,label:n,shape:`rounded`});else if(t.length>0){let e=t[t.length-1];e.nodeIds.includes(n)||e.nodeIds.push(n)}}var h=/^(<|o|x)?(-{2,}|={2,}|-\.+-|~{3,})(>|o|x)?(?:\|([^|]*)\|)?/,g=new Set([`--`,`==`]);function _(e){if(e===`o`)return`circle`;if(e===`x`)return`cross`}var v=/^(<|o|x)?(--|-\.|==)\s+(.+?)\s+(-{2,}[>ox]|={2,}[>ox]|\.+-[>ox]|-{3,}|={3,}|-\.+-)/,y=[{regex:/^([\w\p{L}-]+)\(\(\(((?:"[^"]*"|(?!\)\)\)).)+)\)\)\)/u,shape:`doublecircle`},{regex:/^([\w\p{L}-]+)\(\[((?:"[^"]*"|(?!\]\)).)+)\]\)/u,shape:`stadium`},{regex:/^([\w\p{L}-]+)\(\(((?:"[^"]*"|(?!\)\)).)+)\)\)/u,shape:`circle`},{regex:/^([\w\p{L}-]+)\[\[((?:"[^"]*"|(?!\]\]).)+)\]\]/u,shape:`subroutine`},{regex:/^([\w\p{L}-]+)\[\(((?:"[^"]*"|(?!\)\]).)+)\)\]/u,shape:`cylinder`},{regex:/^([\w\p{L}-]+)\[\/((?:"[^"]*"|(?![\\/]\]).)+)([\\/])\]/u,shape:e=>e===`\\`?`trapezoid`:`parallelogram`},{regex:/^([\w\p{L}-]+)\[\\((?:"[^"]*"|(?![\\/]\]).)+)([\\/])\]/u,shape:e=>e===`/`?`trapezoid-alt`:`parallelogram-alt`},{regex:/^([\w\p{L}-]+)>((?:"[^"]*"|(?!\]).)+)\]/u,shape:`asymmetric`},{regex:/^([\w\p{L}-]+)\{\{((?:"[^"]*"|(?!\}\}).)+)\}\}/u,shape:`hexagon`},{regex:/^([\w\p{L}-]+)\[((?:"[^"]*"|(?!\]).)+)\]/u,shape:`rectangle`},{regex:/^([\w\p{L}-]+)\(((?:"[^"]*"|(?!\)).)+)\)/u,shape:`rounded`},{regex:/^([\w\p{L}-]+)\{((?:"[^"]*"|(?!\}).)+)\}/u,shape:`diamond`}],b=/^([\w\p{L}]+(?:-[\w\p{L}]+)*)/u,x=/^([\w\p{L}-]+)(?=@\{)/u;function S(e){if(e.shape)return(0,t.resolveShapeName)(e.shape)??`rectangle`;if(e.icon!==void 0||e.img!==void 0)switch(e.form?.toLowerCase()){case`circle`:return`circle`;case`rounded`:return`rounded`;default:return`rectangle`}return`rectangle`}function C(e,t){return t.label!==void 0&&t.label.length>0?t.label:t.icon?t.icon:t.img?t.img:e}function w(t,n){(0,e.applyClickStatement)(t,n.interactions)}var T=/^([\w-]+)(?=@\{)/;function E(e,n){let r=e.match(T);if(!r||!n.edges.some(e=>e.id===r[1]))return!1;let i=(0,t.matchExpandedBlock)(e.slice(r[1].length));return i?(D(n,r[1],(0,t.parseExpandedMeta)(i.body)),!0):!1}function D(e,t,n){for(let r of e.edges)r.id===t&&(n.animate!==void 0&&(r.animate=n.animate.toLowerCase()!==`false`),n.animation!==void 0&&(r.animate=!0))}var O=/^:::([\w][\w-]*)/,k=/^([\w\p{L}-]+):::([\w][\w-]*)/u;function A(t,n,r){let i=t.trim(),a=j(i,n,r);if(!a||a.ids.length===0)return;i=a.remaining.trim();let o=a.ids;for(;i.length>0;){let t,a,s,c,l,u,d,f=i.match(/^([\w-]+)@(?=[-=<~ox])/);f&&(d=f[1],i=i.slice(f[0].length));let p=i.match(h),m=p?.[2],y=p?.[1],b=p?.[3];if(p&&m!==void 0&&!(y===void 0&&b===void 0&&g.has(m))){t=y!==void 0,l=_(y);let n=p[4]?.trim();c=n?(0,e.normalizeBrTags)(n):void 0,i=i.slice(p[0].length).trim(),a=I(m),s=b!==void 0,u=_(b)}else{let n=i.match(v);if(!n)break;t=!!n[1],l=_(n[1]);let r=n[3].trim();c=r?(0,e.normalizeBrTags)(r):void 0;let o=n[2],d=n[4];i=i.slice(n[0].length).trim(),a=L(o,d),u=_(d.slice(-1)),s=d.endsWith(`>`)||u!==void 0}let x=j(i,n,r);if(!x||x.ids.length===0)break;i=x.remaining.trim();for(let e of o)for(let r of x.ids)n.edges.push({source:e,target:r,label:c,style:a,hasArrowStart:t,hasArrowEnd:s,...l===void 0?{}:{startMarker:l},...u===void 0?{}:{endMarker:u},...d===void 0?{}:{id:d}});o=x.ids}}function j(e,t,n){let r=M(e,t,n);if(!r)return null;let i=[r.id],a=r.remaining.trim();for(;a.startsWith(`&`);){a=a.slice(1).trim();let e=M(a,t,n);if(!e)break;i.push(e.id),a=e.remaining.trim()}return{ids:i,remaining:a}}function M(n,r,i){let a=null,o=n,s,c=o.match(k);c&&(s=c[2],o=c[1]+o.slice(c[0].length));let l=o.match(x);if(l){let n=l[1],s=(0,t.matchExpandedBlock)(o.slice(n.length));if(s){let c=(0,t.parseExpandedMeta)(s.body);N(r,i,{id:n,label:(0,e.normalizeBrTags)(C(n,c)),shape:S(c)}),a=n,o=o.slice(n.length+s.length)}}if(a===null)for(let{regex:t,shape:n}of y){let s=o.match(t);if(s){a=s[1];let t=(0,e.normalizeBrTags)(s[2]),c=typeof n==`function`?n(s[3]??``):n;N(r,i,{id:a,label:t,shape:c}),o=o.slice(s[0].length);break}}if(a===null){let e=o.match(b);e&&(a=e[1],r.nodes.has(a)?P(r,i,a)||F(i,a):N(r,i,{id:a,label:a,shape:`rectangle`}),o=o.slice(e[0].length))}if(a===null)return null;s&&r.classAssignments.set(a,s);let u=o.match(O);return u&&(r.classAssignments.set(a,u[1]),o=o.slice(u[0].length)),{id:a,remaining:o}}function N(e,t,n){e.nodes.has(n.id)||e.nodes.set(n.id,n),F(t,n.id)}function P(e,t,n){let r=e=>e.nodeIds.includes(n)||e.children.some(r);return e.subgraphs.some(r)||t.some(r)}function F(e,t){if(e.length>0){let n=e[e.length-1];n.nodeIds.includes(t)||n.nodeIds.push(t)}}function I(e){return e.startsWith(`~`)?`invisible`:e.includes(`.`)?`dotted`:e.startsWith(`=`)?`thick`:`solid`}function L(e,t){return e.includes(`.`)||t.includes(`.`)?`dotted`:e.startsWith(`=`)||t.startsWith(`=`)?`thick`:`solid`}Object.defineProperty(exports,"DEFAULTS",{enumerable:!0,get:function(){return e.DEFAULTS}}),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}}),exports.parseMermaid=l,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}});
3
- //# sourceMappingURL=index.cjs.map
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,"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,"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}});
package/dist/index.d.cts CHANGED
@@ -11,6 +11,7 @@ import { fromShikiTheme } from '@zombie-mermaid/core';
11
11
  import { LayoutCache } from '@zombie-mermaid/svg-renderer';
12
12
  import { MermaidGraph } from '@zombie-mermaid/core';
13
13
  import { NodeInteraction } from '@zombie-mermaid/core';
14
+ import { parseMermaid } from '@zombie-mermaid/mermaid-parser';
14
15
  import { PositionedGraph } from '@zombie-mermaid/core';
15
16
  import { renderMermaid } from '@zombie-mermaid/svg-renderer';
16
17
  import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer';
@@ -51,12 +52,7 @@ export { MermaidGraph }
51
52
 
52
53
  export { NodeInteraction }
53
54
 
54
- /**
55
- * Parse Mermaid text into a logical graph structure.
56
- * Auto-detects diagram type (flowchart or state diagram).
57
- * Throws on invalid/unsupported input.
58
- */
59
- export declare function parseMermaid(text: string): MermaidGraph;
55
+ export { parseMermaid }
60
56
 
61
57
  export { PositionedGraph }
62
58
 
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ import { fromShikiTheme } from '@zombie-mermaid/core';
11
11
  import { LayoutCache } from '@zombie-mermaid/svg-renderer';
12
12
  import { MermaidGraph } from '@zombie-mermaid/core';
13
13
  import { NodeInteraction } from '@zombie-mermaid/core';
14
+ import { parseMermaid } from '@zombie-mermaid/mermaid-parser';
14
15
  import { PositionedGraph } from '@zombie-mermaid/core';
15
16
  import { renderMermaid } from '@zombie-mermaid/svg-renderer';
16
17
  import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer';
@@ -51,12 +52,7 @@ export { MermaidGraph }
51
52
 
52
53
  export { NodeInteraction }
53
54
 
54
- /**
55
- * Parse Mermaid text into a logical graph structure.
56
- * Auto-detects diagram type (flowchart or state diagram).
57
- * Throws on invalid/unsupported input.
58
- */
59
- export declare function parseMermaid(text: string): MermaidGraph;
55
+ export { parseMermaid }
60
56
 
61
57
  export { PositionedGraph }
62
58
 
package/dist/index.js CHANGED
@@ -1,439 +1,5 @@
1
- import { DEFAULTS as e, THEMES as t, applyClickStatement as n, extractInitConfig as r, fromShikiTheme as i, normalizeBrTags as a, parseStyleProps as o, splitStatementsByLine as s, toDirection as c, tryApplyClassAssignment as l, tryApplyClassDef as u, tryApplyStyleStatement as d } from "@zombie-mermaid/core";
2
- import { matchExpandedBlock as f, parseExpandedMeta as p, resolveShapeName as m } from "@zombie-mermaid/mermaid-parser";
3
- import { renderMermaidASCII as h, renderMermaidAscii as g } from "@zombie-mermaid/ascii-renderer";
4
- import { createLayoutCache as _, renderMermaid as v, renderMermaidSVG as y, renderMermaidSVGAsync as b, renderMermaidSync as x, themeCssVariables as ee } from "@zombie-mermaid/svg-renderer";
5
- //#region src/parser.ts
6
- function S(e) {
7
- let t = e.trim();
8
- return t.length >= 2 && (t[0] === "\"" && t[t.length - 1] === "\"" || t[0] === "'" && t[t.length - 1] === "'") ? t.slice(1, -1) : t;
9
- }
10
- var C = "\"graph <dir>\"/\"flowchart <dir>\" (dir: TD, TB, LR, BT, RL), \"stateDiagram-v2\", \"sequenceDiagram\", \"classDiagram\", \"erDiagram\", \"xychart-beta\", \"C4Context\"/\"C4Container\"/\"C4Component\"/\"C4Dynamic\"/\"C4Deployment\", \"architecture-beta\"";
11
- function w(e) {
12
- let t = e.trim().toLowerCase();
13
- if (/^sequence/.test(t)) return "sequenceDiagram";
14
- if (/^class/.test(t)) return "classDiagram";
15
- if (/^er/.test(t)) return "erDiagram";
16
- if (/^xychart/.test(t)) return "xychart-beta";
17
- if (/^state/.test(t)) return "stateDiagram-v2";
18
- }
19
- var T = /^(?:[\w-]+@)?<?(?:-{2,}|={2,}|-\.+-?|~{3,})/;
20
- function E(e) {
21
- let t = [];
22
- for (let n of e) n.forEach((e, n) => {
23
- if (n === 0 && t.length > 0 && T.test(e.text)) {
24
- let n = t[t.length - 1];
25
- t[t.length - 1] = {
26
- text: `${n.text} ${e.text}`,
27
- line: n.line
28
- };
29
- } else t.push(e);
30
- });
31
- return t;
32
- }
33
- function D(e) {
34
- let t = r(e.split("\n").map((e) => e.trim())), n = E(s(e));
35
- if (n.length === 0) throw Error("Empty mermaid diagram");
36
- let i = n[0], a = /^stateDiagram(-v2)?\s*$/i.test(i.text) ? k(n) : O(n);
37
- return a.initConfig = t, a;
38
- }
39
- function O(e) {
40
- let t = e[0];
41
- if (t === void 0)
42
- /* v8 ignore next */
43
- throw Error("parseFlowchart called with no lines");
44
- let n = t.text, r = n.match(/^(?:graph|flowchart)\s+(TD|TB|LR|BT|RL)\s*$/i);
45
- if (!r) {
46
- let e = n.match(/^(?:graph|flowchart)\b\s*(.*)$/i);
47
- if (e) {
48
- let r = e[1].trim();
49
- throw Error(r.length > 0 ? `Line ${t.line}: Invalid direction "${r}" in header "${n}". Expected one of: TD, TB, LR, BT, RL.` : `Line ${t.line}: Missing direction in header "${n}". Expected e.g. "graph TD" or "flowchart LR" — one of: TD, TB, LR, BT, RL.`);
50
- }
51
- let r = w(n), i = r && r.toLowerCase() !== n.trim().toLowerCase() ? ` Did you mean "${r}"?` : "";
52
- throw Error(`Line ${t.line}: Invalid mermaid header: "${n}".${i} Supported headers: ${C}.`);
53
- }
54
- let i = {
55
- direction: c(r[1]),
56
- nodes: /* @__PURE__ */ new Map(),
57
- edges: [],
58
- subgraphs: [],
59
- classDefs: /* @__PURE__ */ new Map(),
60
- classAssignments: /* @__PURE__ */ new Map(),
61
- nodeStyles: /* @__PURE__ */ new Map(),
62
- linkStyles: /* @__PURE__ */ new Map(),
63
- interactions: /* @__PURE__ */ new Map()
64
- }, s = [];
65
- for (let t = 1; t < e.length; t++) {
66
- let n = e[t].text;
67
- if (u(n, i) || l(n, i) || d(n, i)) continue;
68
- if (/^click\s+/i.test(n)) {
69
- H(n, i);
70
- continue;
71
- }
72
- if (W(n, i)) continue;
73
- let r = n.match(/^linkStyle\s+(default|[\d,\s]+)\s+(.+)$/);
74
- if (r) {
75
- let e = r[1].trim(), t = o(r[2]);
76
- if (e === "default") i.linkStyles.set("default", {
77
- ...i.linkStyles.get("default"),
78
- ...t
79
- });
80
- else {
81
- let n = e.split(",").map((e) => parseInt(e.trim(), 10));
82
- for (let e of n) isNaN(e) || i.linkStyles.set(e, {
83
- ...i.linkStyles.get(e),
84
- ...t
85
- });
86
- }
87
- continue;
88
- }
89
- let f = n.match(/^direction\s+(TD|TB|LR|BT|RL)\s*$/i);
90
- if (f && s.length > 0) {
91
- s[s.length - 1].direction = c(f[1]);
92
- continue;
93
- }
94
- let p = n.match(/^subgraph\s+(.+)$/);
95
- if (p) {
96
- let e = p[1].trim(), t = e.match(/^([^\s[]+)\s*\[(.+)\]$/), n, r;
97
- t ? (n = t[1], r = a(S(t[2]))) : (r = a(S(e)), n = r.replace(/\s+/g, "_").replace(/[^\p{L}\p{N}_-]/gu, ""));
98
- let i = {
99
- id: n,
100
- label: r,
101
- nodeIds: [],
102
- children: []
103
- };
104
- s.push(i);
105
- continue;
106
- }
107
- if (n === "end") {
108
- let e = s.pop();
109
- e && (s.length > 0 ? s[s.length - 1].children.push(e) : i.subgraphs.push(e));
110
- continue;
111
- }
112
- J(n, i, s);
113
- }
114
- return i;
115
- }
116
- function k(e) {
117
- let t = {
118
- direction: "TD",
119
- nodes: /* @__PURE__ */ new Map(),
120
- edges: [],
121
- subgraphs: [],
122
- classDefs: /* @__PURE__ */ new Map(),
123
- classAssignments: /* @__PURE__ */ new Map(),
124
- nodeStyles: /* @__PURE__ */ new Map(),
125
- linkStyles: /* @__PURE__ */ new Map(),
126
- interactions: /* @__PURE__ */ new Map()
127
- }, n = [], r = /* @__PURE__ */ new Set(), i = 0, s = 0;
128
- for (let f = 1; f < e.length; f++) {
129
- let p = e[f].text;
130
- if (u(p, t) || l(p, t) || d(p, t)) continue;
131
- let m = p.match(/^direction\s+(TD|TB|LR|BT|RL)\s*$/i);
132
- if (m) {
133
- n.length > 0 ? n[n.length - 1].direction = c(m[1]) : t.direction = c(m[1]);
134
- continue;
135
- }
136
- let h = p.match(/^linkStyle\s+(default|[\d,\s]+)\s+(.+)$/);
137
- if (h) {
138
- let e = h[1].trim(), n = o(h[2]);
139
- if (e === "default") t.linkStyles.set("default", {
140
- ...t.linkStyles.get("default"),
141
- ...n
142
- });
143
- else {
144
- let r = e.split(",").map((e) => parseInt(e.trim(), 10));
145
- for (let e of r) isNaN(e) || t.linkStyles.set(e, {
146
- ...t.linkStyles.get(e),
147
- ...n
148
- });
149
- }
150
- continue;
151
- }
152
- if (W(p, t)) continue;
153
- let g = p.match(/^state\s+(?:"([^"]+)"\s+as\s+)?([\w\p{L}]+)\s*\{$/u);
154
- if (g) {
155
- let e = g[1] ?? g[2], i = g[2], a = {
156
- id: i,
157
- label: e,
158
- nodeIds: [],
159
- children: []
160
- };
161
- n.push(a), r.add(i), t.nodes.delete(i);
162
- continue;
163
- }
164
- if (p === "}") {
165
- let e = n.pop();
166
- e && (n.length > 0 ? n[n.length - 1].children.push(e) : t.subgraphs.push(e));
167
- continue;
168
- }
169
- let _ = p.match(/^state\s+"([^"]+)"\s+as\s+([\w\p{L}]+)\s*$/u);
170
- if (_) {
171
- let e = a(_[1]), r = _[2];
172
- A(t, n, r, e);
173
- continue;
174
- }
175
- let v = p.match(/^(\[\*\]|[\w\p{L}-]+)(?::::([\w][\w-]*))?\s*(?:([\w-]+)@)?-->\s*(\[\*\]|[\w\p{L}-]+)(?::::([\w][\w-]*))?(?:\s*:\s*(.+))?$/u);
176
- if (v) {
177
- let e = v[1], o = v[2], c = v[3], l = v[4], u = v[5], d = v[6]?.trim(), f = d ? a(d) : void 0;
178
- e === "[*]" ? (i++, e = `_start${i > 1 ? i : ""}`, j(t, n, {
179
- id: e,
180
- label: "",
181
- shape: "state-start"
182
- })) : r.has(e) || M(t, n, e), l === "[*]" ? (s++, l = `_end${s > 1 ? s : ""}`, j(t, n, {
183
- id: l,
184
- label: "",
185
- shape: "state-end"
186
- })) : r.has(l) || M(t, n, l), o !== void 0 && t.classAssignments.set(e, o), u !== void 0 && t.classAssignments.set(l, u), t.edges.push({
187
- source: e,
188
- target: l,
189
- label: f,
190
- style: "solid",
191
- hasArrowStart: !1,
192
- hasArrowEnd: !0,
193
- ...c === void 0 ? {} : { id: c }
194
- });
195
- continue;
196
- }
197
- let y = p.match(/^([\w\p{L}-]+):::([\w][\w-]*)\s*$/u);
198
- if (y) {
199
- let e = y[1];
200
- r.has(e) || M(t, n, e), t.classAssignments.set(e, y[2]);
201
- continue;
202
- }
203
- let b = p.match(/^([\w\p{L}-]+)\s*:\s*(.+)$/u);
204
- if (b) {
205
- let e = b[1];
206
- A(t, n, e, a(b[2].trim()));
207
- continue;
208
- }
209
- }
210
- for (let e of t.classAssignments.keys()) !t.nodes.has(e) && !r.has(e) && M(t, [], e);
211
- return t;
212
- }
213
- function A(e, t, n, r) {
214
- let i = e.nodes.get(n);
215
- i && (i.label = r), j(e, t, {
216
- id: n,
217
- label: r,
218
- shape: "rounded"
219
- });
220
- }
221
- function j(e, t, n) {
222
- if (e.nodes.has(n.id) || e.nodes.set(n.id, n), t.length > 0) {
223
- let e = t[t.length - 1];
224
- e.nodeIds.includes(n.id) || e.nodeIds.push(n.id);
225
- }
226
- }
227
- function M(e, t, n) {
228
- if (!e.nodes.has(n)) j(e, t, {
229
- id: n,
230
- label: n,
231
- shape: "rounded"
232
- });
233
- else if (t.length > 0) {
234
- let e = t[t.length - 1];
235
- e.nodeIds.includes(n) || e.nodeIds.push(n);
236
- }
237
- }
238
- var N = /^(<|o|x)?(-{2,}|={2,}|-\.+-|~{3,})(>|o|x)?(?:\|([^|]*)\|)?/, P = /* @__PURE__ */ new Set(["--", "=="]);
239
- function F(e) {
240
- if (e === "o") return "circle";
241
- if (e === "x") return "cross";
242
- }
243
- var I = /^(<|o|x)?(--|-\.|==)\s+(.+?)\s+(-{2,}[>ox]|={2,}[>ox]|\.+-[>ox]|-{3,}|={3,}|-\.+-)/, L = [
244
- {
245
- regex: /^([\w\p{L}-]+)\(\(\(((?:"[^"]*"|(?!\)\)\)).)+)\)\)\)/u,
246
- shape: "doublecircle"
247
- },
248
- {
249
- regex: /^([\w\p{L}-]+)\(\[((?:"[^"]*"|(?!\]\)).)+)\]\)/u,
250
- shape: "stadium"
251
- },
252
- {
253
- regex: /^([\w\p{L}-]+)\(\(((?:"[^"]*"|(?!\)\)).)+)\)\)/u,
254
- shape: "circle"
255
- },
256
- {
257
- regex: /^([\w\p{L}-]+)\[\[((?:"[^"]*"|(?!\]\]).)+)\]\]/u,
258
- shape: "subroutine"
259
- },
260
- {
261
- regex: /^([\w\p{L}-]+)\[\(((?:"[^"]*"|(?!\)\]).)+)\)\]/u,
262
- shape: "cylinder"
263
- },
264
- {
265
- regex: /^([\w\p{L}-]+)\[\/((?:"[^"]*"|(?![\\/]\]).)+)([\\/])\]/u,
266
- shape: (e) => e === "\\" ? "trapezoid" : "parallelogram"
267
- },
268
- {
269
- regex: /^([\w\p{L}-]+)\[\\((?:"[^"]*"|(?![\\/]\]).)+)([\\/])\]/u,
270
- shape: (e) => e === "/" ? "trapezoid-alt" : "parallelogram-alt"
271
- },
272
- {
273
- regex: /^([\w\p{L}-]+)>((?:"[^"]*"|(?!\]).)+)\]/u,
274
- shape: "asymmetric"
275
- },
276
- {
277
- regex: /^([\w\p{L}-]+)\{\{((?:"[^"]*"|(?!\}\}).)+)\}\}/u,
278
- shape: "hexagon"
279
- },
280
- {
281
- regex: /^([\w\p{L}-]+)\[((?:"[^"]*"|(?!\]).)+)\]/u,
282
- shape: "rectangle"
283
- },
284
- {
285
- regex: /^([\w\p{L}-]+)\(((?:"[^"]*"|(?!\)).)+)\)/u,
286
- shape: "rounded"
287
- },
288
- {
289
- regex: /^([\w\p{L}-]+)\{((?:"[^"]*"|(?!\}).)+)\}/u,
290
- shape: "diamond"
291
- }
292
- ], R = /^([\w\p{L}]+(?:-[\w\p{L}]+)*)/u, z = /^([\w\p{L}-]+)(?=@\{)/u;
293
- function B(e) {
294
- if (e.shape) return m(e.shape) ?? "rectangle";
295
- if (e.icon !== void 0 || e.img !== void 0) switch (e.form?.toLowerCase()) {
296
- case "circle": return "circle";
297
- case "rounded": return "rounded";
298
- default: return "rectangle";
299
- }
300
- return "rectangle";
301
- }
302
- function V(e, t) {
303
- return t.label !== void 0 && t.label.length > 0 ? t.label : t.icon ? t.icon : t.img ? t.img : e;
304
- }
305
- function H(e, t) {
306
- n(e, t.interactions);
307
- }
308
- var U = /^([\w-]+)(?=@\{)/;
309
- function W(e, t) {
310
- let n = e.match(U);
311
- if (!n || !t.edges.some((e) => e.id === n[1])) return !1;
312
- let r = f(e.slice(n[1].length));
313
- return r ? (G(t, n[1], p(r.body)), !0) : !1;
314
- }
315
- function G(e, t, n) {
316
- for (let r of e.edges) r.id === t && (n.animate !== void 0 && (r.animate = n.animate.toLowerCase() !== "false"), n.animation !== void 0 && (r.animate = !0));
317
- }
318
- var K = /^:::([\w][\w-]*)/, q = /^([\w\p{L}-]+):::([\w][\w-]*)/u;
319
- function J(e, t, n) {
320
- let r = e.trim(), i = Y(r, t, n);
321
- if (!i || i.ids.length === 0) return;
322
- r = i.remaining.trim();
323
- let o = i.ids;
324
- for (; r.length > 0;) {
325
- let e, i, s, c, l, u, d, f = r.match(/^([\w-]+)@(?=[-=<~ox])/);
326
- f && (d = f[1], r = r.slice(f[0].length));
327
- let p = r.match(N), m = p?.[2], h = p?.[1], g = p?.[3];
328
- if (p && m !== void 0 && !(h === void 0 && g === void 0 && P.has(m))) {
329
- e = h !== void 0, l = F(h);
330
- let t = p[4]?.trim();
331
- c = t ? a(t) : void 0, r = r.slice(p[0].length).trim(), i = te(m), s = g !== void 0, u = F(g);
332
- } else {
333
- let t = r.match(I);
334
- if (!t) break;
335
- e = !!t[1], l = F(t[1]);
336
- let n = t[3].trim();
337
- c = n ? a(n) : void 0;
338
- let o = t[2], d = t[4];
339
- r = r.slice(t[0].length).trim(), i = ne(o, d), u = F(d.slice(-1)), s = d.endsWith(">") || u !== void 0;
340
- }
341
- let _ = Y(r, t, n);
342
- if (!_ || _.ids.length === 0) break;
343
- r = _.remaining.trim();
344
- for (let n of o) for (let r of _.ids) t.edges.push({
345
- source: n,
346
- target: r,
347
- label: c,
348
- style: i,
349
- hasArrowStart: e,
350
- hasArrowEnd: s,
351
- ...l === void 0 ? {} : { startMarker: l },
352
- ...u === void 0 ? {} : { endMarker: u },
353
- ...d === void 0 ? {} : { id: d }
354
- });
355
- o = _.ids;
356
- }
357
- }
358
- function Y(e, t, n) {
359
- let r = X(e, t, n);
360
- if (!r) return null;
361
- let i = [r.id], a = r.remaining.trim();
362
- for (; a.startsWith("&");) {
363
- a = a.slice(1).trim();
364
- let e = X(a, t, n);
365
- if (!e) break;
366
- i.push(e.id), a = e.remaining.trim();
367
- }
368
- return {
369
- ids: i,
370
- remaining: a
371
- };
372
- }
373
- function X(e, t, n) {
374
- let r = null, i = e, o, s = i.match(q);
375
- s && (o = s[2], i = s[1] + i.slice(s[0].length));
376
- let c = i.match(z);
377
- if (c) {
378
- let e = c[1], o = f(i.slice(e.length));
379
- if (o) {
380
- let s = p(o.body);
381
- Z(t, n, {
382
- id: e,
383
- label: a(V(e, s)),
384
- shape: B(s)
385
- }), r = e, i = i.slice(e.length + o.length);
386
- }
387
- }
388
- if (r === null) for (let { regex: e, shape: o } of L) {
389
- let s = i.match(e);
390
- if (s) {
391
- r = s[1];
392
- let e = a(s[2]), c = typeof o == "function" ? o(s[3] ?? "") : o;
393
- Z(t, n, {
394
- id: r,
395
- label: e,
396
- shape: c
397
- }), i = i.slice(s[0].length);
398
- break;
399
- }
400
- }
401
- if (r === null) {
402
- let e = i.match(R);
403
- e && (r = e[1], t.nodes.has(r) ? Q(t, n, r) || $(n, r) : Z(t, n, {
404
- id: r,
405
- label: r,
406
- shape: "rectangle"
407
- }), i = i.slice(e[0].length));
408
- }
409
- if (r === null) return null;
410
- o && t.classAssignments.set(r, o);
411
- let l = i.match(K);
412
- return l && (t.classAssignments.set(r, l[1]), i = i.slice(l[0].length)), {
413
- id: r,
414
- remaining: i
415
- };
416
- }
417
- function Z(e, t, n) {
418
- e.nodes.has(n.id) || e.nodes.set(n.id, n), $(t, n.id);
419
- }
420
- function Q(e, t, n) {
421
- let r = (e) => e.nodeIds.includes(n) || e.children.some(r);
422
- return e.subgraphs.some(r) || t.some(r);
423
- }
424
- function $(e, t) {
425
- if (e.length > 0) {
426
- let n = e[e.length - 1];
427
- n.nodeIds.includes(t) || n.nodeIds.push(t);
428
- }
429
- }
430
- function te(e) {
431
- return e.startsWith("~") ? "invisible" : e.includes(".") ? "dotted" : e.startsWith("=") ? "thick" : "solid";
432
- }
433
- function ne(e, t) {
434
- return e.includes(".") || t.includes(".") ? "dotted" : e.startsWith("=") || t.startsWith("=") ? "thick" : "solid";
435
- }
436
- //#endregion
437
- export { e as DEFAULTS, t as THEMES, _ as createLayoutCache, i as fromShikiTheme, D as parseMermaid, v as renderMermaid, h as renderMermaidASCII, g as renderMermaidAscii, y as renderMermaidSVG, b as renderMermaidSVGAsync, x as renderMermaidSync, ee as themeCssVariables };
438
-
439
- //# sourceMappingURL=index.js.map
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 { createLayoutCache as o, renderMermaid as s, renderMermaidSVG as c, renderMermaidSVGAsync as l, renderMermaidSync as u, themeCssVariables as d } from "@zombie-mermaid/svg-renderer";
5
+ export { e as DEFAULTS, t as THEMES, o as createLayoutCache, n as fromShikiTheme, r as parseMermaid, s as renderMermaid, i as renderMermaidASCII, a as renderMermaidAscii, c as renderMermaidSVG, l as renderMermaidSVGAsync, u as renderMermaidSync, d as themeCssVariables };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zombie-mermaid",
3
- "version": "4.0.0",
3
+ "version": "4.1.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",
@@ -90,11 +90,11 @@
90
90
  "@modelcontextprotocol/sdk": "^1.30.0",
91
91
  "elkjs": "^0.11.0",
92
92
  "entities": "^7.0.1",
93
- "@zombie-mermaid/ascii-renderer": "4.0.0",
94
- "@zombie-mermaid/mcp": "4.0.0",
95
- "@zombie-mermaid/svg-renderer": "4.0.0",
96
- "@zombie-mermaid/mermaid-parser": "4.0.0",
97
- "@zombie-mermaid/core": "4.0.0"
93
+ "@zombie-mermaid/ascii-renderer": "4.1.0",
94
+ "@zombie-mermaid/core": "4.1.0",
95
+ "@zombie-mermaid/mcp": "4.1.0",
96
+ "@zombie-mermaid/mermaid-parser": "4.1.0",
97
+ "@zombie-mermaid/svg-renderer": "4.1.0"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@changesets/changelog-github": "^1.0.0",
@@ -159,7 +159,7 @@
159
159
  "bench:mcp": "tsx scripts/bench-mcp.ts",
160
160
  "check:bundle-size": "tsx scripts/check-bundle-size.ts",
161
161
  "generate:hero": "tsx scripts/generate-hero.ts",
162
- "build:mono-font": "tsx scripts/build-mono-font-subset.ts && prettier --write demo/components/generated/mono-font-subset.ts demo/styles.css packages/core/src/generated/mono-font-subset.ts",
162
+ "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",
163
163
  "generate:primitives-classes": "tsx scripts/generate-primitives-classes.ts && prettier --write demo/components/generated/primitives-classes.ts",
164
164
  "generate:logo-lockup": "tsx scripts/generate-logo-lockup.ts",
165
165
  "docs:scripts": "tsx scripts/generate-development-scripts-doc.ts && prettier --write docs/development-scripts.md",
@@ -15,7 +15,7 @@
15
15
  * with that input.
16
16
  */
17
17
  import { describe, it, expect } from 'vitest'
18
- import { parseMermaid } from '../parser.ts'
18
+ import { parseMermaid } from '@zombie-mermaid/mermaid-parser'
19
19
  import { convertToAsciiGraph } from '../../packages/ascii-renderer/src/converter.ts'
20
20
  import { createMapping } from '../../packages/ascii-renderer/src/grid.ts'
21
21
  import type {
@@ -97,7 +97,9 @@ describe('draw-bundles: rendered fan-in/fan-out diagrams', () => {
97
97
 
98
98
  it('draws a fan-out bundle with one arrowhead per target (unicode)', () => {
99
99
  const out = renderMermaidASCII(FAN_OUT, { useAscii: false })
100
- expect(out).toContain('├────────────┐')
100
+ // The source sits centred over its two targets, so the shared trunk
101
+ // leaves its bottom border and splits at a ┴ directly beneath it.
102
+ expect(out).toMatch(/┌─+┴─+┐/)
101
103
  expect(out.match(/▼/g)).toHaveLength(2)
102
104
  })
103
105
 
@@ -15,7 +15,7 @@
15
15
  * code with the same direction-confusion risk the TD side had.
16
16
  */
17
17
  import { describe, it, expect } from 'vitest'
18
- import { parseMermaid } from '../parser.ts'
18
+ import { parseMermaid } from '@zombie-mermaid/mermaid-parser'
19
19
  import { convertToAsciiGraph } from '../../packages/ascii-renderer/src/converter.ts'
20
20
  import { createMapping } from '../../packages/ascii-renderer/src/grid.ts'
21
21
  import {
@@ -17,7 +17,7 @@
17
17
  * do with subgraphs — this repro has none).
18
18
  */
19
19
  import { describe, it, expect } from 'vitest'
20
- import { parseMermaid } from '../parser.ts'
20
+ import { parseMermaid } from '@zombie-mermaid/mermaid-parser'
21
21
  import { convertToAsciiGraph } from '../../packages/ascii-renderer/src/converter.ts'
22
22
  import { createMapping } from '../../packages/ascii-renderer/src/grid.ts'
23
23
  import {
@@ -10,7 +10,7 @@
10
10
  * so it grows with the chain instead of capping at a fixed depth.
11
11
  */
12
12
  import { describe, it, expect } from 'vitest'
13
- import { parseMermaid } from '../parser.ts'
13
+ import { parseMermaid } from '@zombie-mermaid/mermaid-parser'
14
14
  import { convertToAsciiGraph } from '../../packages/ascii-renderer/src/converter.ts'
15
15
  import { createMapping } from '../../packages/ascii-renderer/src/grid.ts'
16
16
  import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer'
@@ -34,7 +34,7 @@
34
34
  * diverge from declaration order.
35
35
  */
36
36
  import { describe, it, expect } from 'vitest'
37
- import { parseMermaid } from '../parser.ts'
37
+ import { parseMermaid } from '@zombie-mermaid/mermaid-parser'
38
38
  import { convertToAsciiGraph } from '../../packages/ascii-renderer/src/converter.ts'
39
39
  import { createMapping } from '../../packages/ascii-renderer/src/grid.ts'
40
40
  import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer'
@@ -8,7 +8,7 @@
8
8
  * the moved node now moves to the same column.
9
9
  */
10
10
  import { describe, it, expect } from 'vitest'
11
- import { parseMermaid } from '../parser.ts'
11
+ import { parseMermaid } from '@zombie-mermaid/mermaid-parser'
12
12
  import { convertToAsciiGraph } from '../../packages/ascii-renderer/src/converter.ts'
13
13
  import { createMapping } from '../../packages/ascii-renderer/src/grid.ts'
14
14
  import type {