zombie-mermaid 3.2.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 (47) 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 -435
  7. package/package.json +13 -11
  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 +108 -0
  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-parallel-lane-border-1230.test.ts +71 -0
  17. package/src/__tests__/ascii-reverse-direction-edges-629.test.ts +1 -1
  18. package/src/__tests__/ascii-stacked-frame-geometry-1285.test.ts +93 -0
  19. package/src/__tests__/ascii-subgraph-bbox.test.ts +1 -1
  20. package/src/__tests__/ascii-subgraph-later-edgeless-301.test.ts +1 -1
  21. package/src/__tests__/class-parser.test.ts +30 -10
  22. package/src/__tests__/class-styling.test.ts +7 -3
  23. package/src/__tests__/compound-flat-layout.test.ts +901 -0
  24. package/src/__tests__/edge-approach-direction.test.ts +1 -1
  25. package/src/__tests__/elk-layout-cache.test.ts +1 -1
  26. package/src/__tests__/er-integration.test.ts +7 -4
  27. package/src/__tests__/expanded-shapes.test.ts +1 -1
  28. package/src/__tests__/flowchart-back-edge-prereversal-1239.test.ts +212 -0
  29. package/src/__tests__/flowchart-syntax-gaps.test.ts +1 -1
  30. package/src/__tests__/interactivity-config.test.ts +1 -1
  31. package/src/__tests__/layout-engine.test.ts +1 -1
  32. package/src/__tests__/linkstyle.test.ts +1 -1
  33. package/src/__tests__/multiline-labels.test.ts +1 -1
  34. package/src/__tests__/parser.test.ts +28 -1
  35. package/src/__tests__/render-options-per-type.test.ts +1 -1
  36. package/src/__tests__/self-loop-edges.test.ts +1 -1
  37. package/src/__tests__/state-composite-order-1287.test.ts +132 -0
  38. package/src/__tests__/state-flow-order-1240.test.ts +113 -0
  39. package/src/__tests__/state-styling.test.ts +1 -1
  40. package/src/__tests__/styles.test.ts +20 -1
  41. package/src/__tests__/subgraph-title-edge-gap-1239.test.ts +238 -0
  42. package/src/__tests__/svg-csp-style-options.test.ts +2 -1
  43. package/src/__tests__/workspace-package-boundaries.test.ts +11 -25
  44. package/src/index.ts +1 -1
  45. package/dist/index.cjs.map +0 -1
  46. package/dist/index.js.map +0 -1
  47. package/src/parser.ts +0 -1308
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=F(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=I(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)||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),P(t,n.id)}function P(e,t){if(e.length>0){let n=e[e.length-1];n.nodeIds.includes(t)||n.nodeIds.push(t)}}function F(e){return e.startsWith(`~`)?`invisible`:e.includes(`.`)?`dotted`:e.startsWith(`=`)?`thick`:`solid`}function I(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,435 +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 S } from "@zombie-mermaid/svg-renderer";
5
- //#region src/parser.ts
6
- function C(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 w = "\"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 T(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 E = /^(?:[\w-]+@)?<?(?:-{2,}|={2,}|-\.+-?|~{3,})/;
20
- function D(e) {
21
- let t = [];
22
- for (let n of e) n.forEach((e, n) => {
23
- if (n === 0 && t.length > 0 && E.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 O(e) {
34
- let t = r(e.split("\n").map((e) => e.trim())), n = D(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) ? A(n) : k(n);
37
- return a.initConfig = t, a;
38
- }
39
- function k(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 = T(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: ${w}.`);
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
- U(n, i);
70
- continue;
71
- }
72
- if (G(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(C(t[2]))) : (r = a(C(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
- Y(n, i, s);
113
- }
114
- return i;
115
- }
116
- function A(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 (G(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
- j(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 : ""}`, M(t, n, {
179
- id: e,
180
- label: "",
181
- shape: "state-start"
182
- })) : r.has(e) || N(t, n, e), l === "[*]" ? (s++, l = `_end${s > 1 ? s : ""}`, M(t, n, {
183
- id: l,
184
- label: "",
185
- shape: "state-end"
186
- })) : r.has(l) || N(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) || N(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
- j(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) && N(t, [], e);
211
- return t;
212
- }
213
- function j(e, t, n, r) {
214
- let i = e.nodes.get(n);
215
- i && (i.label = r), M(e, t, {
216
- id: n,
217
- label: r,
218
- shape: "rounded"
219
- });
220
- }
221
- function M(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 N(e, t, n) {
228
- if (!e.nodes.has(n)) M(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 P = /^(<|o|x)?(-{2,}|={2,}|-\.+-|~{3,})(>|o|x)?(?:\|([^|]*)\|)?/, F = /* @__PURE__ */ new Set(["--", "=="]);
239
- function I(e) {
240
- if (e === "o") return "circle";
241
- if (e === "x") return "cross";
242
- }
243
- var L = /^(<|o|x)?(--|-\.|==)\s+(.+?)\s+(-{2,}[>ox]|={2,}[>ox]|\.+-[>ox]|-{3,}|={3,}|-\.+-)/, R = [
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
- ], z = /^([\w\p{L}]+(?:-[\w\p{L}]+)*)/u, B = /^([\w\p{L}-]+)(?=@\{)/u;
293
- function V(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 H(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 U(e, t) {
306
- n(e, t.interactions);
307
- }
308
- var W = /^([\w-]+)(?=@\{)/;
309
- function G(e, t) {
310
- let n = e.match(W);
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 ? (K(t, n[1], p(r.body)), !0) : !1;
314
- }
315
- function K(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 q = /^:::([\w][\w-]*)/, J = /^([\w\p{L}-]+):::([\w][\w-]*)/u;
319
- function Y(e, t, n) {
320
- let r = e.trim(), i = X(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(P), m = p?.[2], h = p?.[1], g = p?.[3];
328
- if (p && m !== void 0 && !(h === void 0 && g === void 0 && F.has(m))) {
329
- e = h !== void 0, l = I(h);
330
- let t = p[4]?.trim();
331
- c = t ? a(t) : void 0, r = r.slice(p[0].length).trim(), i = ee(m), s = g !== void 0, u = I(g);
332
- } else {
333
- let t = r.match(L);
334
- if (!t) break;
335
- e = !!t[1], l = I(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 = te(o, d), u = I(d.slice(-1)), s = d.endsWith(">") || u !== void 0;
340
- }
341
- let _ = X(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 X(e, t, n) {
359
- let r = Z(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 = Z(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 Z(e, t, n) {
374
- let r = null, i = e, o, s = i.match(J);
375
- s && (o = s[2], i = s[1] + i.slice(s[0].length));
376
- let c = i.match(B);
377
- if (c) {
378
- let e = c[1], o = f(i.slice(e.length));
379
- if (o) {
380
- let s = p(o.body);
381
- Q(t, n, {
382
- id: e,
383
- label: a(H(e, s)),
384
- shape: V(s)
385
- }), r = e, i = i.slice(e.length + o.length);
386
- }
387
- }
388
- if (r === null) for (let { regex: e, shape: o } of R) {
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
- Q(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(z);
403
- e && (r = e[1], t.nodes.has(r) || Q(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(q);
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 Q(e, t, n) {
418
- e.nodes.has(n.id) || e.nodes.set(n.id, n), $(t, n.id);
419
- }
420
- function $(e, t) {
421
- if (e.length > 0) {
422
- let n = e[e.length - 1];
423
- n.nodeIds.includes(t) || n.nodeIds.push(t);
424
- }
425
- }
426
- function ee(e) {
427
- return e.startsWith("~") ? "invisible" : e.includes(".") ? "dotted" : e.startsWith("=") ? "thick" : "solid";
428
- }
429
- function te(e, t) {
430
- return e.includes(".") || t.includes(".") ? "dotted" : e.startsWith("=") || t.startsWith("=") ? "thick" : "solid";
431
- }
432
- //#endregion
433
- export { e as DEFAULTS, t as THEMES, _ as createLayoutCache, i as fromShikiTheme, O as parseMermaid, v as renderMermaid, h as renderMermaidASCII, g as renderMermaidAscii, y as renderMermaidSVG, b as renderMermaidSVGAsync, x as renderMermaidSync, S as themeCssVariables };
434
-
435
- //# 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": "3.2.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",
@@ -84,17 +84,17 @@
84
84
  "README.md"
85
85
  ],
86
86
  "engines": {
87
- "node": ">=22"
87
+ "node": ">=24"
88
88
  },
89
89
  "dependencies": {
90
90
  "@modelcontextprotocol/sdk": "^1.30.0",
91
91
  "elkjs": "^0.11.0",
92
92
  "entities": "^7.0.1",
93
- "@zombie-mermaid/ascii-renderer": "3.2.0",
94
- "@zombie-mermaid/mcp": "3.2.0",
95
- "@zombie-mermaid/mermaid-parser": "3.2.0",
96
- "@zombie-mermaid/core": "3.2.0",
97
- "@zombie-mermaid/svg-renderer": "3.2.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",
@@ -105,7 +105,7 @@
105
105
  "@testing-library/react": "^16.3.3",
106
106
  "@testing-library/user-event": "^14.6.7",
107
107
  "@types/jsdom": "^30.0.0",
108
- "@types/node": "^22.0.0",
108
+ "@types/node": "^24.13.6",
109
109
  "@types/react": "^19.2.18",
110
110
  "@types/react-dom": "^19.2.7",
111
111
  "@vitest/coverage-v8": "^4.1.11",
@@ -124,7 +124,7 @@
124
124
  "shiki": "^3.19.0",
125
125
  "subset-font": "^2.7.0",
126
126
  "tsx": "^4.23.12",
127
- "typescript": "^5.9.3",
127
+ "typescript": "^6.0.3",
128
128
  "typescript-eslint": "^8.68.0",
129
129
  "unplugin-dts": "^1.0.3",
130
130
  "vite": "^8.2.2",
@@ -159,9 +159,10 @@
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
+ "docs:scripts": "tsx scripts/generate-development-scripts-doc.ts && prettier --write docs/development-scripts.md",
165
166
  "badge:bundle-size": "tsx scripts/generate-bundle-badge.ts",
166
167
  "visual-diff": "tsx scripts/visual-diff.ts",
167
168
  "form-diff": "tsx scripts/form-diff.ts",
@@ -179,6 +180,7 @@
179
180
  "dashboard": "scripts/tsx-css.sh packages/site/dashboard.ts",
180
181
  "dashboard:data": "tsx scripts/generate-dashboard-data.ts",
181
182
  "upstream:pr-candidates": "tsx scripts/list-upstream-pr-rescue-candidates.ts",
182
- "audit:responsive": "tsx scripts/audit-responsive.ts"
183
+ "audit:responsive": "tsx scripts/audit-responsive.ts",
184
+ "layout:oracle": "tsx scripts/layout-oracle.ts"
183
185
  }
184
186
  }
@@ -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 {