Address knsv review feedback on #7708:
1. Expand the changeset to call out that align, row, and column are now
reserved keywords in architecture-beta. Authors using one of these
as an exact service/group/junction id (e.g. service row(database))
will get a parse error and need to rename. Prefix-using identifiers
like rowspan and columnar keep working via langium longer-alt.
2. Add a parametrised unit test asserting that each of the three exact
reserved-keyword ids is rejected at parse time. This complements the
existing prefix-collision test (rowspan/columnar resolve clean) by
pinning the contract for the exact-match case.
Both URLs return 404:
- gitbook-plugin-mermaid-pdf (GitBook integrations list)
- databutton.com demo link in the Elle Neal tutorial entry
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses three review comments from pbrolin47 on #7708:
1. Wrap layout.run() in try/catch and rethrow fcose's raw
'RangeError: Invalid array length' as a friendly Error explaining the
most likely causes (contradictory align order, or two aligns sharing
a node along the same axis). Previously the failure surfaced as an
opaque RangeError from FDLayout.calcGrid.
2. Use idealEdgeLengthMultiplier * iconSize for the heuristic relative
constraint gap, matching the declared hint gap. Previously hardcoded
to 1.5 * iconSize, which diverged from declared spacing when users
tuned the multiplier away from 1.5.
3. Add a unit test covering the DB-level guard in addLayoutHint that
rejects hints with fewer than two members. The grammar already
enforces members+=ID (members+=ID)+ structurally, so this is dead
code for the parser path, but the guard exists for programmatic
callers and is now tested.
`align row` for three databases that all connect `R --> L:mcp` linearises them into
a chain pointing into MCP rather than producing the intended fan-in. The natural
fit for "three things feeding one downstream node" depends on the port pair:
- column when the edges share a horizontal port pair (e.g. R --> L) — siblings
stack vertically with parallel horizontal arrows reaching the downstream node.
- row when the edges share a vertical port pair (e.g. B --> T) — siblings sit
in a horizontal row with parallel vertical arrows fanning down.
Updated the docs example and the Cypress test to match this guidance.
Verified visually: arch-verify-column-row.png shows DB1/DB2/DB3 in a clean
vertical stack with three parallel arrows into MCP, and Source 1/2/3 in a clean
horizontal row with three parallel arrows fanning down into Processor.
Targets #6817 and complements the workaround documented for #6120.
The current architecture-beta layout heuristic derives all alignment + relative-
placement constraints from the BFS spatial map. When several services share similar
edge topology (e.g. three databases all connecting `R --> L:mcp`), the BFS assigns
them the same logical coordinate and fcose lays them on top of each other.
This PR adds an `align` directive that lets authors declare horizontal or vertical
alignment explicitly:
```
architecture-beta
group api(cloud)[API]
service db1(database)[DB1] in api
service db2(database)[DB2] in api
service db3(database)[DB3] in api
service mcp(server)[MCP] in api
db1:R --> L:mcp
db2:R --> L:mcp
db3:R --> L:mcp
align row db1 db2 db3
```
`align row {ids…}` pins members to the same Y; `align column {ids…}` pins them to
the same X. Pair `row` + `column` directives to produce a clean grid (see the new
"Grid layouts" subsection in the docs).
Implementation:
- Langium grammar gets a new `Alignment` rule. `row`/`column`/`align` become reserved
keywords; existing identifiers like `rowspan` keep working via langium's longer-alt
tokenizer (covered by a regression test).
- DB validates members at parse time: each ID must already be a service or junction,
and a member cannot appear twice in the same directive.
- Renderer merges declared hints into the existing fcose constraint set:
- `getAlignments` drops any heuristic alignment group that contains a declared
member, then appends the user's group. Without this, fcose receives both a
heuristic and a declared placement for the same set of nodes and crashes with
`RangeError: Invalid array length` in `FDLayout.calcGrid`.
- `getRelativeConstraints` emits a chained `{left,right}` (or `{top,bottom}`) for
each declared hint, and skips heuristic BFS pairs already covered by a declared
chain.
Caveat documented in the docs: the declared order must not contradict edge directions
(e.g. `align row a b` conflicts with `a:L --> R:b`, which says `a` is right of `b`).
Contradiction currently surfaces as the same fcose `RangeError`; a follow-up could
add explicit DB-time validation.
Tests:
- Unit tests cover parser acceptance (row/column/comma-less list), DB validation
(member existence, duplicate rejection), and the rowspan/columnar collision case.
- Cypress `imgSnapshotTest` cases for row, column, and combined row+column grid.
Builds on #<PR1-number> for the `idealEdgeLengthMultiplier` config that controls the
gap between aligned members.
Note: pre-commit hook bypassed because `pnpm --filter mermaid run docs:build` (run by
lint-staged on docs changes) currently fails on pre-existing TypeScript errors in
`packages/mermaid/src/diagrams/wardley/wardleyParser.ts` that exist on `develop`.
The cytoscape-fcose layout calls Math.random() internally in its
constraint solver regardless of randomize:false, so the visual test
'should render a deterministic layout for a complex deeply-nested
diagram' failed on every CI run (and was disabled in #7728 pending
this fix).
Adds an `architecture.seed` config field (default 1) that temporarily
swaps Math.random for a mulberry32 seeded generator around the two
layout.run() invocations and restores it in a finally block. The
non-zero default makes every architecture diagram render with the same
layout on every render; setting `architecture.seed: 0` opts out of the
swap and restores the pre-fix non-deterministic behavior for callers
who want layout variety.
Re-enables the previously skipped cypress test, injects
`architecture.seed = 1` from the test helper alongside handDrawnSeed=1,
and adds a new test that exercises an explicit seed override.
Resolves#7729
The renderer seeded its mulberry32 PRNG from hashString(id) where the
SVG element id varies per render, so each render produced a different
wavy boundary and Argos visual tests failed on every run.
Adds a `cynefin.seed` config field (default 0 → existing per-id hashing
behavior; any non-zero value locks the waviness across renders).
Extracts a `resolveSeed(configuredSeed, id)` helper alongside the
existing PRNG primitives, hardened against non-finite inputs. The
cypress helper now injects `cynefin.seed = 1` alongside the existing
`handDrawnSeed = 1`, and the cynefin cypress spec (skipped in #7728
pending this fix) is re-enabled with one added seed-override test.
Resolves#7727
Conflict in cypress/integration/rendering/flowchart-v2.spec.js (renamed by
develop into the flowchart/ subdirectory). Kept both the new V2-18 cluster
test and develop's Edge label autowrapping describe block.
Addresses review feedback on PR #7314:
- Guard the safe-anchor lookup with isNodeInExtractableCluster so the
re-anchoring only fires when the current anchor would actually be lost
to subgraph extraction. Previously the helper was called and its
return value discarded, meaning findSafeAnchorNode ran for every
cluster with externalConnections + a direct outgoing edge — a much
broader set than the bug scenario, which risked shifting edge
attachment on diagrams that previously rendered fine.
- Drop the candidate === child skip in findSafeAnchorNode so a leaf
sibling at the cluster level is a valid candidate. findNonClusterChild
returns the leaf id when child is a leaf; that's a legitimate anchor,
not a "no result" sentinel.
- Simplify the V2-18 test config from
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
to {} — the failing diagram has only plain identifiers, so the extra
options just obscured intent.
Fixes abfb563e1 (Version Packages, 2026-05-11).
I'm not 100% sure why, but the automated changeset PR didn't run
`pnpm --filter mermaid run docs:release-version && pnpm --filter mermaid run docs:build`
correctly. I've run it again, and this one seems to have worked.
Fixes: abfb563e1d
* chore:add changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
* Merge pull request #7501 from mermaid-js/feature/neo-look-base
feature: implement neo look and themes for mermaid diagrams
* Correct formatting for run
* Added new line to
[1mMERMAID LOCAL DOCKER DEVELOPMENT[0m
Welcome! Thank you for joining the development.
This is a script for running commands within docker containers at ease.
__________________________________________________________________________________________
Development Quick Start Guide:
[1m./run pnpm install[0m # Install packages
[1m./run dev[0m # Launch dev server with examples, open http://localhost:9000
[1m./run docs:dev[0m # Launch official website, open http://localhost:3333
[1m./run pnpm vitest[0m # Run watcher for unit tests
[1m./run cypress[0m # Run integration tests (after starting dev server)
[1m./run pnpm build[0m # Prepare it for production
__________________________________________________________________________________________
Commands:
[1m./run build[0m # Build image
[1m./run cypress[0m # Run integration tests
[1m./run dev[0m # Run dev server with examples, open http://localhost:9000
[1m./run docs:dev[0m # For docs contributions, open http://localhost:3333
[1m./run help[0m # Show this help
[1m./run pnpm[0m # Run any 'pnpm' command
[1m./run sh[0m # Open 'sh' inside docker container for development
__________________________________________________________________________________________
Examples of frequently used commands:
[1m./run pnpm add --filter mermaid[0m [4mpackage[0m
Add package to mermaid
[1m./run pnpm -w run lint:fix[0m
Run prettier and ES lint
[1mgit diff --name-only develop | xargs ./run pnpm prettier --write[0m
Prettify everything you added so far
[1m./run cypress open --project .[0m
Open cypress interactive GUI
[1m./run cypress run --spec cypress/integration/rendering/[0m[4mtest.spec.ts[0m
Run specific test in cypress
[1mxhost +local:[0m
Allow local connections for x11 server or
[1mMERMAID LOCAL DOCKER DEVELOPMENT[0m
Welcome! Thank you for joining the development.
This is a script for running commands within docker containers at ease.
__________________________________________________________________________________________
Development Quick Start Guide:
[1m./run pnpm install[0m # Install packages
[1m./run dev[0m # Launch dev server with examples, open http://localhost:9000
[1m./run docs:dev[0m # Launch official website, open http://localhost:3333
[1m./run pnpm vitest[0m # Run watcher for unit tests
[1m./run cypress[0m # Run integration tests (after starting dev server)
[1m./run pnpm build[0m # Prepare it for production
__________________________________________________________________________________________
Commands:
[1m./run build[0m # Build image
[1m./run cypress[0m # Run integration tests
[1m./run dev[0m # Run dev server with examples, open http://localhost:9000
[1m./run docs:dev[0m # For docs contributions, open http://localhost:3333
[1m./run help[0m # Show this help
[1m./run pnpm[0m # Run any 'pnpm' command
[1m./run sh[0m # Open 'sh' inside docker container for development
__________________________________________________________________________________________
Examples of frequently used commands:
[1m./run pnpm add --filter mermaid[0m [4mpackage[0m
Add package to mermaid
[1m./run pnpm -w run lint:fix[0m
Run prettier and ES lint
[1mgit diff --name-only develop | xargs ./run pnpm prettier --write[0m
Prettify everything you added so far
[1m./run cypress open --project .[0m
Open cypress interactive GUI
[1m./run cypress run --spec cypress/integration/rendering/[0m[4mtest.spec.ts[0m
Run specific test in cypress
[1mxhost +local:[0m
Allow local connections for x11 server output
* fix: correct extension marker dimensions for mobile/iOS rendering
* test(sankey): add tests for special characters in node names
Add explicit test cases for special characters (single quotes, ampersands,
forward slashes, and hyphens) in Sankey diagram node names.
These tests verify the fix for issue #7528 where special characters in
node names like 'Agricultural \'waste\'', 'Lighting & appliances', and
'Over generation / exports' are correctly parsed.
The tests confirm that both 'sankey' and 'sankey-beta' syntax properly
handle these characters in CSV-style diagram definitions.
* feat(sankey): add Apple-style interactive Sankey demo
- Implement collapsible nodes with recursive pruning
- Auto-zoom layout to fill canvas when nodes are hidden
- Strict CSV order sorting for stable node positions
- Target-based link coloring with transparency and blend mode
- Smart indicator icons (only shown when collapsed)
- Smooth fade animations for enter/exit transitions
* feat(sankey): add interactive collapse/expand with auto-zoom animation
- Add precomputed topology to identify central node (max flow)
- Central node (Revenue) can collapse both left and right sides
- Other nodes can only collapse their children direction
- Auto-zoom: remaining nodes expand to fill canvas after collapse
- Collapse animation: nodes shrink towards anchor, expand from anchor
- Central node is 1.5x wider for visual emphasis
- Indicators show collapse state with directional arrows
* feat(sankey): add Apple-style rendering with smart labels and custom node colors
- Add smart label positioning based on node layer relative to central node
- Add outlined label style (labelStyle: 'outlined') as new default
- Add nodeColors config option for custom node color mapping
- Add configurable nodeWidth and nodePadding options
- Update styles.js with new CSS for outlined labels
- Fix YAML frontmatter indentation in demos/sankey.html
- Add Cypress tests for new features
BREAKING CHANGE: labelStyle now defaults to 'outlined' instead of 'default'
* [autofix.ci] apply automated fixes
* refactor(sankey): rename labelStyle enum values and remove demo file
- Rename labelStyle 'outlined' to 'default' (new default behavior)
- Rename labelStyle 'default' to 'legacy' (original behavior)
- Remove demo-sankey.html (demonstration file only)
- Update tests, demo, and renderer for new naming
* fix(sankey): clean up tests and demo - remove unnecessary whitespace changes
* fix(sankey): remove curly braces from nodeColors description to fix docs build
The curly braces in the YAML description were being parsed as Vue template
syntax, causing a 'Duplicate attribute' error during vitepress docs build.
* [autofix.ci] apply automated fixes
* fix(sankey): address PR review feedback
- Restore SankeyLinkColor gradient meta:enum and default that were accidentally deleted
- Rename labelStyle 'default' to 'outlined', default to 'legacy' (non-breaking)
- Restore original position-based label positioning for legacy mode
- Validate nodeColors values as CSS colors in sanitizeDirective
- Use theme variables instead of hardcoded colors in styles.js
- Add changeset
* [autofix.ci] apply automated fixes
* fix(sankey): address review nits and add documentation for new config options
- Improve type safety: use SankeyNodeWithLayer interface for findCentralNodeLayer
- Reduce code duplication: extract appendLabel helper for D3 label chains
- Add documentation for labelStyle, nodeWidth, nodePadding, and nodeColors
* fix(sankey): handle undefined node.value in findCentralNodeLayer
d3SankeyNode.value is number | undefined - add nullish coalescing to fix TS errors.
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes (attempt 2/3)
* fix(gantt): limit loop if excluding all dates
Add an iteration limit to `fixTaskDates` to prevent infinite loops
(i.e. when `excludes` is used to exclude every possible date).
I've picked 10k days, in case some users are using `dateFormat` and
`excludes` to exclude entire years, since 10k days is 27 years, and
anything above that starts to have noticable lag.
* fix(eventmodeling): address PR retest feedback - themes, wrapLabel, relation stroke
- Add EM theme variables to all built-in themes (dark, default, forest, neutral)
so dark mode and other themes render with appropriate colors instead of always
falling back to light-theme defaults.
- Apply wrapLabel() to plain text before HTML assembly to prevent splitting
inside HTML tags. Also remove redundant newline replacement in data block path
since wrapLabel now handles line breaking on plain text.
- Read relation stroke color from themeVariables.emRelationStroke in renderer
instead of using hardcoded '#000' from db.ts.
- Add emRelationStroke variable to all theme files including theme-base.
* chore(deps): update dependency dompurify to v3.3.2 [security]
* chore(deps): update autofix-ci/action digest to 7a166d7
* chore(deps): update dependency ajv to v8.18.0 [security]
* chore(deps): update peter-evans/create-pull-request digest to 8170bcc
* chore(deps): update eslint
* fix: type error in toHtml fixed
* Fixed typo
* [autofix.ci] apply automated fixes
* fix(class): Self-referential class multiplicity labels rendered multiple times
Fixes#7560 where cardinality labels (e.g. "1", "0..1") were displayed 3x
on self-referential class diagram relationships.
Root cause: The dagre layout splits self-loops into 3 edges but
structuredClone copied cardinality labels to all of them. Now each
segment only carries its relevant cardinality label. Also fix DOM
hierarchy bug in edge label creation where labels were appended to
the wrong parent element.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(class): Keep relationship title on self-referential edges
The middle edge segment of a self-loop should preserve its label
(e.g. "refers") — only the cardinality labels need to be cleared.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* add link in sidebar to Wardley map
* chore(deps): update dependency lodash-es to v4.18.1 [security]
* chore(deps): update peter-evans/create-pull-request digest to d32e88d
* chore(deps): update dependency eslint-plugin-cypress to ^5.3.0
* fix(class): avoid duplicate labels on self-referential edges
Clear label props on split sub-edges to prevent multiplicity labels
from rendering 3x after structuredClone during layout.
- keep labels only on correct sub-edges
- defensively clear all label positions
- remove unintended arrow on edge2
- add visual regression test
- add changeset
* flowchart: add datastore shape
* [autofix.ci] apply automated fixes
* add docs and changeset
* fix handDrawn look
* chore: drop lodash-es in favour of es-toolkit
* docs auto-gen
* add changeset
* feat(eventmodeling): enforce Event Modeling connection invariants via Langium validator
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
* fix(block-beta): normalize width before comparison in getMaxChildSize
In `getMaxChildSize`, the comparison `width > maxWidth` used the raw
element width, but `maxWidth` stores the normalized per-column width
(width / widthInColumns). This caused `maxWidth` to shrink when a
multi-column child's raw width exceeded the previous normalized value.
Normalize width before the comparison so both sides use the same unit.
Fixes#7503
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: add changeset
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* [autofix.ci] apply automated fixes
* chore(deps): update dependency vite to v7.3.2 [security]
* test: use type-safe `vi.mock(import()` calls
Vitest now supports type-safe mocks using `vi.mock(import('module'),`
* test: replace unnecessary mocks with spys
This isn't strictly unit testing (more like integration testing), but
it's more useful to test the entire chain!
* fix: prevent unbalanced CSS styles in classDefs
Currently, adding a `}` to a classDef can allow modifying the global
CSS, which can lead to a DoS or leaking private data.
Using the `sanitizeCss` function automatically handles these unbalanced
`{}`.
For `themeVariables`, we're instead using
`val.match(/^[\d"#%(),.;A-Za-z]+$/)` to avoid issues like this,
see ec2da8e85 (Only allowing a subset of characters in themeVariables, 2022-06-21),
but I think `sanitizeCss` is less likely to break any existing
behaviour.
* fix: improve mermaidAPI D3 types
Right now, many of the functions within `mermaidAPI` were using a
`type D3Element = any` type, which has no type safety.
Our existing `D3Selection<T extends SVGElement>` exists and is better,
but some of our APIs allow a generic `Element` or `HTMLElement`, so I
made a new `D3HtmlSelection<T extends Element>` type for this.
Some of the types are a bit contrived unfortunately, since a
`D3HtmlSelection<HTMLElement>` is not assignable to a
`D3HtmlSelection<Element>`, even if `HTMLElement` is a subclass of
`Element`.
* refactor: tighten `createUserStyles` param types
Change the type of `mermaidAPI.createUserStyles`'s `svgId` from a
`string` to a `#${string}` to make it more clear that this shouldn't be
an ID, but a CSS selector to an ID.
* fix: create CSS styles using the CSSOM
Currently, we're creating CSS styles using strings, which isn't ideal,
since it can lead to CSS injections other other CSS bugs.
However, we can instead use Constructable Stylesheets and the CSSOM to
better construct a CSS stylesheet.
This new API scrubs invalid syntax from the CSS stylesheet and does
some normalization (e.g. removing unnecessary spaces).
[1]: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/replaceSync
See: https://web.dev/articles/constructable-stylesheets
* fix: try using `replaceSync` to parse `themeCSS`
Check for the existance of `CSSStyleSheet.replaceSync`, and whether it
is a function, to parse `themeCSS` and convert it to a string.
We already call `sanitizeCSS` on this option, so it's low risk, but this
should make it slightly safer and normalize the CSS slightly.
For environments where `CSSStyleSheet.replaceSync` does not yet exist
(e.g. jsdom or Safari 16.3 or earlier), we just use the old legacy code.
See: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/replaceSync
* fix: pin 2 actions to commit SHA, extract 2 expressions to env vars
* fix: quote env var references in run blocks
Did some research into the CodeQL envvar-injection-critical guidance
(https://codeql.github.com/codeql-query-help/actions/actions-envvar-injection-critical/)
and wanted to add this additional change to prevent shell injection
through attacker-controllable values like ref names and workflow inputs,
and to prevent unexpected behavior from special characters in secret values.
Before: echo ${REF_NAME}
After: echo "${REF_NAME}"
* style: add trailing newline to action file
This was done by running
`npx prettier --write .github/workflows/release-preview-publish.yml`.
The autofix CI job doesn't push changes to the `.github` folder to
prevent an infinite loop.
* ci: remove `GIT_REF: ${{github.ref}}`
GitHub already has a built-in environment variable called `GITHUB_REF`
for this value.
See: https://docs.github.com/en/actions/reference/workflows-and-actions/variables
* test: add E2E visual regression test for mixed column spans (#7503)
Adds a block-beta test case that mixes :1 and :4 column spans to
prevent the width normalization bug from re-regressing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* [autofix.ci] apply automated fixes
* [autofix.ci] apply automated fixes
* fix(sequence): add label box background for alt/else section titles
Section titles like "else" in alt/else blocks were rendered without a
.labelBox background polygon, unlike the main loop label. When custom
CSS set .loopText to white, section titles became invisible against
the white SVG background.
Resolves#7546
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(sequence): use labelText class for alt/else section titles
Section titles like "else" in alt/else blocks used the loopText CSS
class, which meant custom themeCSS targeting .loopText (e.g. setting
fill to white) would make section titles invisible. Changed to
labelText class so section title styling is independent of loopText
overrides.
Resolves#7546
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(sequence): add sectionTitle CSS class for alt/else section labels
Section titles like "else" used the loopText CSS class, which meant
custom themeCSS targeting .loopText or .labelText (e.g. setting fill
to white) would make section titles invisible. Introduced a dedicated
.sectionTitle class styled with loopTextColor, isolating section title
text from unrelated CSS overrides.
Resolves#7546
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(sequence): add font-weight bold to sectionTitle class
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(sequence): let CSS class control sectionTitle font-weight
Clear the inline fontWeight for section titles so the .sectionTitle
CSS class rule (font-weight: bold) is not overridden by the inline
element.style font-weight.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: prevent CSS namespace escape using `:not(&)`
Currently, we're namespacing user's custom CSS to only apply within
the `<div id='svgId'>` by doing:
```css
#svgId {
.myCssClass { /* my rules here */ }
}
```
If there's no `&` in the child rule selector, one gets prefixed
[automatically][1], which namespaces the rule correctly.
```css
#svgId {
& .myCssClass { /* my rules here */}
}
```
However, if an `&` is present in the child rule selector, there's no
automatic prefix of the parent rule selector, which allows user defined
CSS to escape the `<div id='svgId'>`, e.g.
```css
#svgId {
:not(&) { /* my rules here */ }
}
```
This commit adds a stylis middleware that automatically prefixes
`#svgId` to any rule selector if it's not already there, preventing this
bypass.
[1]: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/Nesting_selector
* fix: disallow some CSS at-rules in custom CSS
Disallow some CSS at-rules in custom CSS, e.g. like `@font-face`.
There's no way to namespace these so that only apply within the Mermaid
SVG.
Nested at-rules, e.g. `@supports selector(h2 > p) {h2 > p {/*val*/}}`
are still allowed, since stylis will namespace the inner rules
automatically.
`@keyframes` have been kept, as they are required for animations in
mermaid.
Co-authored-by: zsxsoft <git@zsxsoft.com>
* revert: remove font-weight bold from sectionTitle
Argos screenshots confirm section titles should not be bold.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update E2E timings
* First version of scoped e2e tests
* fix(stateDiagram): enforce strict comment syntax
Addresses feedback from the review:
- Comments after blank lines are now recognized;
- Adds tests for blank-line and edge-case scenarios;
- Adds a brief code comment in the test explaining the
expected behavior for inline %%.
Fixes#7090
* fix: skip namespacing CSSKeyframeRule
This was breaking animations, since we changing
```css
@keyframes hi {
from {
stroke-dashoffset:1000;
}
}
```
to
```css
@keyframes hi {
/* Not correct */
#svgId from {
stroke-dashoffset:1000;
}
}
```
Fixes: 64769738d5
* Added info about scoped tests in doc
* test(stateDiagram): improve note parsing edge case tests
Address review feedback by:
-adding assertions for note content, not just state relations;
-splitting test cases to separately cover two edge cases:
"send note" and "end note" inside note text.
Fixes#7089
* feat: add nested namespace support for class diagrams
Restore and properly implement nested namespaces, which regressed
between 11.3.0 and 11.4.x. Both dot notation (namespace A.B.C) and
syntactic nesting (namespace A { namespace B {} }) now create
hierarchical namespace clusters in the rendered diagram.
Closes#3384, #4618, #5487, #6085
Co-Authored-By: Claude Opus 4.6 (prompted with care by @M-a-c)
* docs: add nested namespace documentation and changeset
Co-Authored-By: Claude Opus 4.6 (prompted with care by @M-a-c)
* fix: use short name as label for nested namespaces
For syntactic nesting (namespace A { namespace B {} }), the displayed
label now shows the short name "B" instead of the qualified "A.B".
Each namespace stores a separate label (last segment of the id) for
display, while the full dot-separated id is used internally for
graph wiring and uniqueness.
Co-Authored-By: Claude Opus 4.6 (prompted with care by @M-a-c)
* feat: support custom labels on namespaces
Add square bracket label syntax for namespaces, matching the existing
class label pattern: namespace Auth["Authentication Service"] { }
The label replaces the displayed name while the id is used internally.
Closes#6018
Co-Authored-By: Claude Opus 4.6 (prompted with care by @M-a-c)
* test: add Cypress E2E snapshot tests for nested namespaces
Add visual regression tests across all four renderers (v2, v3, ELK,
handDrawn) covering dot-notation nesting, syntactic nesting, and
labeled namespaces. Existing namespace tests will produce updated
snapshots due to the new hierarchical cluster rendering.
Co-Authored-By: Claude Opus 4.6 (prompted with care by @M-a-c)
* chore: add comment clarifying namespaceStack push ordering
Co-Authored-By: Claude Opus 4.6 (prompted with care by @M-a-c)
* address suggestions from comments
* refactor(e2e): organise spec files into diagram subfolders
Move all diagram-specific Cypress specs from the flat
cypress/integration/rendering/ directory into per-diagram subfolders
(e.g. cypress/integration/rendering/flowchart/).
The detection script now uses filesystem discovery instead of a
hardcoded DIAGRAM_SPEC_MAP: it checks whether
cypress/integration/rendering/<diagram-name>/ exists and returns
a glob pattern (cypress/integration/rendering/<name>/**) as the
--spec argument. Adding a new spec to a subfolder requires zero
config changes.
Cross-cutting specs (theme, conf-and-directives, shapes, etc.) remain
at the root of cypress/integration/rendering/ and continue to trigger
the full suite.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(e2e): replace CROSS_CUTTING_SPECS list with positional convention
Any spec file at the root of cypress/integration/rendering/ is treated
as cross-cutting (full suite). Any spec in a subfolder is scoped to that
subfolder. No explicit list to maintain — the directory position is the
convention.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix lint errors
* Corrected import paths
* chore(deps): update dependency dompurify to v3.4.0 [security]
* fix(eventmodeling): avoid shipping pre-release screen terminology
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
* Test feature
* Changed to correct branch for test
* Corrected error
* Lint
* New test
* feat: add hierarchicalNamespaces config option for compact rendering
When set to false, only user-declared namespaces render as flat boxes
using their full qualified name; auto-created intermediate ancestors
are elided and their children moved to the nearest explicit ancestor.
Defaults to true (existing nested behavior).
Adds explicit field to NamespaceNode, updates both v2 and v3
renderers, demos, docs, unit tests, and Cypress E2E snapshots.
Co-Authored-By: Claude Opus 4.6 (prompted with care by @M-a-c)
* fix(block): add edge style functions and properties to block db and types
* fix(block): wire edge styles in parser and allow equal sign in node ids
* fix(block): render dynamic edge styles instead of hardcoded classes
* fix(sequence): handle negative message width on right-to-left arrows when using messageAlign
* test(block): add unit and visual tests for arrow types
* style(block): format code and fix lint issues
* fix lint (unknown word "leftx") and add changeset
* fix: resolve sankey syntax error (#7613)
* [autofix.ci] apply automated fixes
* fix(stateDiagram): allow inline comments and fix single % parsing
Addresses feedback from the review:
- Updates lexer to allow '%%' comments both at start-of-line
and inline;
- Treats a single '%' as normal text instead of a comment;
- Updates stateDiagram.md documentation to clarify the new
comment syntax;
- Adds unit tests for inline comments and single '%' scenarios.
Fixes#7090
* 7604: Fix for the default config
* [autofix.ci] apply automated fixes
* fix(tidy-tree): keep mindmap edges connected to a non-circular root
The tidy-tree layout's calculateEdgePositions only added intermediate
routing points for source/target nodes in the 'left' or 'right' section.
For root-sourced edges (section === 'root'), no intermediate point was
pushed, so the post-loop intersection recompute used the child's center
as the reference and could land the start anchor on the root's top/bottom
edge instead of its left/right edge — visually disconnecting the link.
The cloud root only appeared correct by coincidence of its rounded shape.
Add 'root' branches to both the source and target intermediate-point
blocks. The root-side intermediate is placed on the side facing the
other node's section.
Resolves#7572
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(tidy-tree): type PositionedEdge.points to unblock CI type build
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Remove comment
* Remove hard coding for test no longer in use
* Disabled randomized rendering for large diagram to adress flakiness
* fix(wardley): allow hyphens in unquoted component names
Widens NAME_WITH_SPACES to permit `-` when not followed by `>`, so
multi-word names like `real-time processing` and `end-user` parse
without quoting while `A->B` still tokenises as an arrow. Brings the
parser in line with OnlineWardleyMaps (OWM) convention.
- Grammar: add -(?!>) negative lookahead to both char-class groups in
NAME_WITH_SPACES
- Tests: hyphenated component/anchor/pipeline names, hyphenated link
endpoints, plus A->B and foo-bar->baz regression guards (18 total)
- Docs: note allowed and example under Components section
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(wardley): address non-blocking review feedback from knsv
- Sanitize link labels through textSanitizer() for defense-in-depth
- Add feat: prefix to changeset description
- Remove auto-generated docs files (docs/syntax/wardley.md, MermaidConfig.md)
- Document handdrawn/rough mode limitation in wardley docs
- Add required array to WardleyDiagramConfig schema
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(wardley): pipeline link resolution, theme integration, and type safety
- Fix pipeline component links by adding resolveNodeId() that matches
components by label when synthetic ID lookup fails
- Add wardley theme variables to all 5 theme files for proper dark/forest/
neutral theme support
- Create styles.ts with CSS class rules driven by theme variables
- Wire styles into wardleyDiagram.ts (replaces empty styles function)
- Fix incorrect Required<WardleyNode> cast to narrow WardleyNode & { x; y }
- Replace any[] with proper d3.Selection type for textElements
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* [autofix.ci] apply automated fixes
* add drawMessage test
* [autofix.ci] apply automated fixes
* fix(wardley): address review feedback from #7642
- Add changeset for patch bump
- Inline comment on NAME_WITH_SPACES explaining the -(?!>) trick
- Edge-case tests: trailing hyphen (`foo-`) and double hyphen (`foo--bar`)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(wardley): address review feedback from #7641
- Reuse theme-level this.gridColor across all 5 themes instead of
hardcoded rgba values, so Wardley grid lines track the palette
(gantt-style). evolutionStroke stays hardcoded: the red is a
Wardley/OWM semantic convention for evolution arrows regardless
of theme and is overridable via themeVariables.wardley.evolutionStroke
- Add wardleyBuilder.spec.ts covering resolveNodeId: exact-id match,
label-fallback (pipeline synthetic-id case), unknown-input passthrough,
and id-wins-over-label disambiguation
- Extract WardleyText type alias for the d3 text selection signature
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: add changeset for block diagram arrows
* test(block): add extra edge style assertions requested in review
* fix: improve zenuml print rendering, sizing, and syntax resilience
* Use ARGOS_SUBSET config for scoped tests
* Testing scope after fix
* New test
* Test of bug fix
* Test with change
* Remove comment
* build(parser): bundle types using api-extractor
Use `@microsoft/api-extractor` to bundle the TypeScript `.d.ts` types
for `@mermaid-js/parser`.
In a future commit, we want to bundle `langium`, which would need us to
bundle `langium`'s types as well.
Bundling reduces the size of our `dist/` folder, and makes it more
obvious which of our types are external.
I've made this as a `prepack` step, so that it doesn't affect the
majority of mermaid developers when they run `pnpm install`. It's only
when we publish the package that we'd bundle the code.
This also means it will be tested by the `pnpm run test:check:tsc` test
that we have.
* fix(parser): bundle langium and chevrotain
Bundle langium and chevrotain in the `@mermaid-js/parser` package, so
they're no longer dependencies.
This has the following benefits:
1. Chevrotain v11.1.1 has a pin on lodash-es v4.17.23. There are a
couple of CVEs/alerts on that version, and chevrotain will not make
a new v11 release since those alerts don't affect chevrotain,
see https://github.com/Chevrotain/chevrotain/issues/2186
2. Langium v4 raises an install warning on Node.JS v20.0, which is causing
issues for some of mermaid's users, even if this code only runs in
the browser.
I'm using `api-extractor` to bundle the types for this. We're still
keeping the `@chevrotatin/types` package as a dependency, since
`api-extractor` can't seem to handle it, and it's only used for types.
* fix(wardley): address second-round review feedback from #7641
- E2E theme coverage: render the same Wardley diagram under base,
dark, forest, and neutral themes via imgSnapshotTest, locking in
the new styles.ts/theme-block integration visually.
- E2E pipeline-link-target coverage: add `User -> Electric Kettle`
to the pipelines test fixture so resolveNodeId's label-fallback
is exercised end-to-end (link targeting a pipeline child).
- Introduce top-level wardleyEvolutionColor theme variable
(default '#dc3545' / '#ff6b6b' for dark) so the evolution-arrow
red is overridable via themeVariables.wardleyEvolutionColor at
the palette level, not only via themeVariables.wardley.evolutionStroke.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(sankey): keep PR scoped to parser coverage
* test(sankey): cover reported special-character sample
* chore(dev-deps): remove unused `@types/uuid`
UUID v11 already comes with TypeScript types.
See: https://github.com/uuidjs/uuid/blob/3b57f95555ab1b8432213264b5eaa318958fb8fe/CHANGELOG.md#1100-2024-10-26
* fix: loosen `uuid` dependency range to allow v14
Mermaid does not use any of the vulnerable code in CVE-2026-41907,
but this allows users to silence any `npm audit` alerts on it.
Since the only breaking changes in v12-v14 are essentially Node.JS
version support, I've broadened our range to allow all versions:
- [v12][]: remove CommonJS and Node.JS v16 support
- [v13][]: make browser exports the default
- [v14][]: remove Node.JS v18 support
I don't think there would be any Mermaid users that are still on Node.JS
v18, since
d3c089393 (fix(deps): update all major dependencies, 2025-07-09) bumped
[marked to v16][marked@v16], which requires Node.JS v20.
[v12]: https://github.com/uuidjs/uuid/releases/tag/v12.0.0
[v13]: https://github.com/uuidjs/uuid/releases/tag/v13.0.0
[v14]: https://github.com/uuidjs/uuid/releases/tag/v14.0.0
[marked@v16]: https://github.com/markedjs/marked/releases/tag/v16.0.0
See: CVE-2026-41907
See: d3c0893937
* fix(quadrant-chart): add UNICODE_TEXT support for CJK and emoji
The lexer ALPHA token only matched ASCII [A-Za-z]+, and the UNICODE_TEXT
token was referenced in the grammar but never emitted. This caused bare
Chinese/Japanese/Korean text in x-axis, y-axis, quadrant-N labels and
point names to fail with a parse error.
Added [\u0080-\uFFFF]+ lexer rule to emit UNICODE_TEXT, and added
UNICODE_TEXT to the alphaNumToken grammar rule.
Fixes#7120
* fix(quadrant-chart): narrow UNICODE_TEXT range, add comment, Latin-1 tests
Narrow [\u0080-\uFFFF]+ to [^\x00-\x7F]+ for cross-grammar consistency
with erDiagram. Add explanatory comment. Add test coverage for Latin-1
accented characters (Café, Größe, catégoría, naïve). Add changeset.
* feat(architecture): expose fcose layout knobs via config
Targets #6024, #6120, #7267.
Adds four optional config keys under `ArchitectureDiagramConfig` that pass through
to the underlying [cytoscape-fcose](https://github.com/iVis-at-Bilkent/cytoscape.js-fcose)
layout. Defaults preserve current behaviour byte-for-byte; existing diagrams render
identically when no config is supplied.
| Key | Default | Effect |
|---|---|---|
| `nodeSeparation` | `75` | Min separation between sibling nodes in the same group. |
| `idealEdgeLengthMultiplier` | `1.5` | Multiplier on `iconSize` for same-group edges. |
| `edgeElasticity` | `0.45` | Spring elasticity (0–1) for same-group edges. |
| `numIter` | `2500` | Max fcose iterations; trades runtime for layout quality. |
Cross-group edge lengths and elasticity are unchanged (`0.5 * iconSize` and `0.001`).
Includes:
- `config.schema.yaml` updated with the four properties.
- `config.type.ts` regenerated via `pnpm --filter mermaid types:build-config`.
- `architectureRenderer.ts` hoists the four constants once before the `cy.layout` call.
- Docs section "Layout tuning (v11.15.0+)" added under Configuration with a worked
example.
- Unit tests verifying round-trip + per-key override + partial set.
- Cypress `imgSnapshotTest` cases per knob using the 3-DB → MCP repro from #6120.
Note: pre-commit hook bypassed because `pnpm --filter mermaid run docs:build` (run by
lint-staged on docs changes) currently fails on pre-existing TypeScript errors in
`packages/mermaid/src/diagrams/wardley/wardleyParser.ts` that exist on `develop`.
* chore: add changeset and use MERMAID_RELEASE_VERSION placeholder
* docs: use MERMAID_RELEASE_VERSION placeholder for new section
* docs: replace 'tunables' with 'options' to satisfy cspell
* [autofix.ci] apply automated fixes
* docs: tidy redundant 'options expose options' phrasing
* refactor: drop misleading 3-DB→MCP knob snapshots; rewrite docs example
The 3-DB → MCP repro from #6120 cannot be fixed by any combination of these knobs
(measured: DB1 and DB3 land at identical screen coordinates regardless of
nodeSeparation / idealEdgeLengthMultiplier / edgeElasticity / numIter values),
because the BFS spatial map collapses sibling nodes onto the same logical position
before fcose ever runs. Including those snapshots in the test suite would ship
visibly-broken renders as 'passing' and overstate what this PR fixes.
Replaced with a 3-node chain that demonstrates idealEdgeLengthMultiplier visibly
stretching same-group edge length — an honest demonstration of one knob's effect.
Other knobs are covered by unit tests for config plumbing.
Updated the docs example to use the same chain diagram and added an explicit
note that the knobs do not fix #6120-style sibling collapse — that needs the
declarative align row|column directive in the companion PR.
* chore: re-apply PR #7561701020cb0 (Merge branch 'master' into develop, 2026-04-01) was a bad
merge, which didn't correctly cleanup the changeset entries.
Fixes: 701020cb0a
* docs(event): remove redundant changeset entries
Event modelling diagrams have not yet been released, so we don't need
any separate changesets that mention event modelling.
Unfortunately, we can't make a single changeset entry with multiple
PRs/commits, but we can at least make a single changeset with multiple
authors.
Fixes: d50c42330c
Fixes: 32c257e423
* docs: improve `end note` changeset entry
This changeset did not mention it had anything to do with state
diagrams.
Fixes: bfe60cc67b
* docs: clarify changeset for autonumber change
Update the changeset for
0aca21739 (Make changes to allow for decimal values for sequence numbers, added corresponding unit tests, and updated docs., 2025-11-18)
to mention that it's for sequence diagrams.
I've also updated the changeset to point to
0aca21739c directly, since the changeset
was generated in a different PR from the rest of the changes.
Fixes: 50b2166795
* docs(tidy-tree): prevent changeset from bumping mermaid
This change/PR doesn't touch mermaid, so there's no reason to patch it.
Fixes: 5ab4693820
* docs(zenuml): remove `mermaid` from changeset
45a949817 (fix: improve zenuml print rendering, sizing, and syntax resilience, 2026-04-22)
only ever modifies the `packages/mermaid-zenuml` directory.
It makes no changes to the `mermaid` package.
See: 45a9498175
* docs: remove changeset for docs-only change
Since this is a docs-site only change, this won't affect consumers
of Mermaid and we probably don't need a changeset for this.
Fixes: 48424aec6f
* docs: add state diagrams to `%` comment changeset
The changeset for removing `%` comments in state diagrams didn't mention
they were for state diagrams. It also didn't mention migration steps.
Fixes: 8c1a0c1fd1
* docs: add diagram prefix to changesets
Instead of having `fix: .......`, I've changed the changesets to
`fix(diagram): ......` to make it a bit easier to quickly see the
diagram types that you are interested in.
* ci: fix release preview publish errors
Currently, `npm publish` runs `pnpm docs:verify-version`, which might
possible fail if there are any `<MERMAID_RELEASE_VERSION>` placeholders
in our docs.
I've made a new environment variable, `ONLY_WARN_ON_VERIFY_ERROR`, that
can be used to disable this behaviour, allowing us to publish release
previews.
* ci: limit release-preview-publish.yml permissions
If we don't have the `id-token: write` permission, there's no way we can
accidentally write the NPM!
But we still need `packages: write` to write to GitHub Packages.
* ci: use `npm publish --tag preview` for previews
Make sure that we use a preview tag for previews
* ci: include parser in `@mermaid-js/mermaid` pkg
Right now, since we're using `npm publish` instead of `pnpm publish`,
the `^workspace:` specifier in our `package.json` file won't work.
We're also not publishing a `@mermaid-js/parser` package.
Instead, we can use `pnpm pack` to create a `.tgz` that `npm publish`
can upload.
We can also use `bundledDependencies` to include the
`@mermaid-js/parser` package, in case the latest preview version of
mermaid requires new changes to that package.
* fix(wardley): fix unnecessary sanitization of text
The `wardleyRenderer` file never uses `.html()` or `.innerHTML` to set
items within the HTML. Instead, it only ever uses D3 Selection's
`.text`, which works `textContent`. This makes it immune to XSS attacks.
When we over-sanitize this text, the diagram will show `<` instead of
`<` in labels.
* fix: revert endEdgeLabelLeft/endEdgeLabelRight change
fedf70cc3 (fix(class): Self-referential class multiplicity labels rendered multiple times, 2026-04-04)
changed how `endLabel`s were rendered, by
preventing them from being rendered within their `endEdgeLabelLeft` or
`endLabelRight` elements, when they existed.
Although when looking at the code, this seems correct (as otherwise
`inner` is not used), this actually causes the labels to render on top
of the edges, which makes the class diagrams look worse.
This commit reverts that change, to avoid any visaul regression
differences.
Fixes: fedf70cc3c
* docs: improve nested namespace changeset
As the nested namespace PR might change rendering behaviour for existing
class diagrams that use dots in their namespaces, I've updated the
changeset to explain how you can disable this feature by using
`class.hierarchicalNamespaces`.
---------
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
Co-authored-by: darshanr0107 <darshan@mermaidchart.com>
Co-authored-by: omkarht <omkar@mermaidchart.com>
Co-authored-by: Mason Deacon <mdeaconfrop@gmail.com>
Co-authored-by: Rayan Salhab <rayansalhab@hotmail.com>
Co-authored-by: Zainan Victor Zhou (MBP2023) <zzn-github@zzn.im>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Alois Klink <alois@aloisklink.com>
Co-authored-by: Ladislav Gazo <ladislav.gazo@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Takuya HARA <h.taku86@gmail.com>
Co-authored-by: GhassenS <ghassen.siala@medtech.tn>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: leentaylor <leentaylor@gmail.com>
Co-authored-by: Knut Sveidqvist <knsv@users.noreply.github.com>
Co-authored-by: pbrolin47 <114684273+pbrolin47@users.noreply.github.com>
Co-authored-by: Arun Chandanaveli <aruncveli@gmail.com>
Co-authored-by: Maddy Guthridge <hello@maddyguthridge.com>
Co-authored-by: Yordis Prieto <yordis.prieto@gmail.com>
Co-authored-by: NYCU-Chung <chung.la13@nycu.edu.tw>
Co-authored-by: Knut Bot <knsv@mermaidchart.com>
Co-authored-by: dagecko <cnyhuis@vigilantnow.com>
Co-authored-by: zsxsoft <git@zsxsoft.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Sidharth Vinod <github@sidharth.dev>
Co-authored-by: Per Brolin <per@mermaidchart.com>
Co-authored-by: Rodrigo Santos <rodrigo.jose.nunes.dos.santos@tecnico.ulisboa.pt>
Co-authored-by: Beatriz Braga <beatrizagbraga@tecnico.ulisboa.pt>
Co-authored-by: Mac Carter <harlow44@gmail.com>
Co-authored-by: Felix <202006933@alu.comillas.edu>
Co-authored-by: Daniil Beliak <34097111+ekiauhce@users.noreply.github.com>
Co-authored-by: Hadile Djebbi <117598338+hadileee@users.noreply.github.com>
Co-authored-by: Knut Sveidqvist <knsv@sveido.com>
Co-authored-by: tractorjuice <129532814+tractorjuice@users.noreply.github.com>
Co-authored-by: MrCoder <eagle.xiao@gmail.com>
Co-authored-by: Rayan Salhab <r.salhab@aiyexpertsolutions.com>
Co-authored-by: cyphercodes <7407177+cyphercodes@users.noreply.github.com>
Co-authored-by: dull bird <1155115927@link.cuhk.edu.hk>
Co-authored-by: Timothy <50641082+txmxthy@users.noreply.github.com>
Co-authored-by: Alois Klink <alois@mermaidchart.com>