Skip to content

fix(wardley): review follow-ups — pipeline links, theme, type safety, label sanitisation - #7641

Merged
knsv merged 5 commits into
mermaid-js:developfrom
tractorjuice:fix/wardley-review-followups
Apr 27, 2026
Merged

knsv merged 5 commits into
mermaid-js:developfrom
tractorjuice:fix/wardley-review-followups

Conversation

@tractorjuice

Copy link
Copy Markdown
Contributor

Summary

Post-merge follow-ups for the Wardley Maps diagram type (#7147). Two commits:

  1. Review feedback from @knsv (non-blocking at merge time):

    • Sanitize link labels through textSanitizer() for defense-in-depth
    • Add feat: prefix to the changeset description
    • Remove auto-generated docs files accidentally committed to the source tree
    • Document the handdrawn/rough (look: handDrawn) mode limitation in wardley.md
    • Add a required array to WardleyDiagramConfig JSON schema
  2. Real behaviour fixes:

    • Pipeline link resolution: links targeting components inside a pipeline now resolve correctly via a new resolveNodeId() that falls back to label-based matching when the synthetic-ID lookup misses.
    • Theme integration: add Wardley theme variables to all five theme files (base, default, dark, forest, neutral) so non-default themes render Wardley maps correctly. New styles.ts wires CSS rules to those variables; wardleyDiagram.ts uses the new styles function (previously empty).
    • Type safety: replace an incorrect Required<WardleyNode> cast with a narrowed WardleyNode & { x; y } type, and replace any[] with the proper d3.Selection type for text elements.

Test plan

  • pnpm --filter mermaid test src/diagrams/wardley/ passes
  • Manually verify dark, forest, and neutral themes render a Wardley map correctly in the browser
  • Manually verify a diagram with links targeting a pipeline child component renders the link

@changeset-bot

changeset-bot Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 288a6b3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes changesets to release 1 package
Name Type
mermaid Minor

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@netlify

netlify Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for mermaid-js ready!

Name Link
🔨 Latest commit 288a6b3
🔍 Latest deploy log https://app.netlify.com/projects/mermaid-js/deploys/69ec685dafbc8d00087da3f9
😎 Deploy Preview https://deploy-preview-7641--mermaid-js.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added the Type: Bug / Error Something isn't working or is incorrect label Apr 21, 2026
tractorjuice and others added 2 commits April 21, 2026 16:34
- 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>
…afety

- 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>
@tractorjuice
tractorjuice force-pushed the fix/wardley-review-followups branch from e4e659c to 32d36a7 Compare April 21, 2026 16:35
@pkg-pr-new

pkg-pr-new Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@mermaid-js/examples

npm i https://pkg.pr.new/@mermaid-js/examples@7641

mermaid

npm i https://pkg.pr.new/mermaid@7641

@mermaid-js/layout-elk

npm i https://pkg.pr.new/@mermaid-js/layout-elk@7641

@mermaid-js/layout-tidy-tree

npm i https://pkg.pr.new/@mermaid-js/layout-tidy-tree@7641

@mermaid-js/mermaid-zenuml

npm i https://pkg.pr.new/@mermaid-js/mermaid-zenuml@7641

@mermaid-js/parser

npm i https://pkg.pr.new/@mermaid-js/parser@7641

@mermaid-js/tiny

npm i https://pkg.pr.new/@mermaid-js/tiny@7641

commit: 288a6b3

@codecov

codecov Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 12.58741% with 125 lines in your changes missing coverage. Please review.
✅ Project coverage is 3.33%. Comparing base (2fe6e55) to head (288a6b3).
⚠️ Report is 36 commits behind head on develop.

Files with missing lines Patch % Lines
packages/mermaid/src/diagrams/wardley/styles.ts 2.94% 33 Missing ⚠️
packages/mermaid/src/themes/theme-base.js 0.00% 16 Missing ⚠️
packages/mermaid/src/themes/theme-dark.js 0.00% 16 Missing ⚠️
packages/mermaid/src/themes/theme-forest.js 0.00% 16 Missing ⚠️
packages/mermaid/src/themes/theme-neutral.js 0.00% 16 Missing ⚠️
...ges/mermaid/src/diagrams/wardley/wardleyBuilder.ts 0.00% 12 Missing ⚠️
packages/mermaid/src/diagrams/wardley/wardleyDb.ts 0.00% 11 Missing ⚠️
...ges/mermaid/src/diagrams/wardley/wardleyDiagram.ts 0.00% 2 Missing ⚠️
...es/mermaid/src/diagrams/wardley/wardleyRenderer.ts 33.33% 2 Missing ⚠️
...ages/mermaid/src/diagrams/wardley/wardleyParser.ts 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           develop   #7641      +/-   ##
==========================================
+ Coverage     3.31%   3.33%   +0.02%     
==========================================
  Files          539     541       +2     
  Lines        56688   56867     +179     
  Branches       824     838      +14     
==========================================
+ Hits          1880    1898      +18     
- Misses       54808   54969     +161     
Flag Coverage Δ
unit 3.33% <12.58%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/mermaid/src/themes/theme-default.js 95.70% <100.00%> (+0.16%) ⬆️
...ages/mermaid/src/diagrams/wardley/wardleyParser.ts 0.54% <0.00%> (ø)
...ges/mermaid/src/diagrams/wardley/wardleyDiagram.ts 10.00% <0.00%> (-1.12%) ⬇️
...es/mermaid/src/diagrams/wardley/wardleyRenderer.ts 0.11% <33.33%> (+0.11%) ⬆️
packages/mermaid/src/diagrams/wardley/wardleyDb.ts 0.00% <0.00%> (ø)
...ges/mermaid/src/diagrams/wardley/wardleyBuilder.ts 0.86% <0.00%> (-0.10%) ⬇️
packages/mermaid/src/themes/theme-base.js 2.21% <0.00%> (-0.10%) ⬇️
packages/mermaid/src/themes/theme-dark.js 1.99% <0.00%> (-0.09%) ⬇️
packages/mermaid/src/themes/theme-forest.js 2.20% <0.00%> (-0.10%) ⬇️
packages/mermaid/src/themes/theme-neutral.js 2.77% <0.00%> (-0.12%) ⬇️
... and 1 more

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@argos-ci

argos-ci Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) 👍 Changes approved 7 changed, 4 added Apr 25, 2026, 7:20 AM

@knsv-bot knsv-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sisyphus-bot]

Thanks for the thorough follow-up @tractorjuice — this is exactly the kind of post-merge cleanup we love to see. Every review item from #7147 is addressed, and you've quietly folded in a legitimate pipeline-link bug fix and a nice type-safety pass alongside it.

What's working well

  • 🎉 Theme integration is done right. styles.ts:38-87 pulls everything through theme variables, and the parallel wardley blocks in theme-base.js:266, theme-dark.js:327, theme-default.js:333, theme-forest.js:305, theme-neutral.js:339 keep the shape consistent across all five themes.
  • 🎉 Pipeline link fix is surgical. WardleyBuilder.resolveNodeId (wardleyBuilder.ts:175) plus the call sites in wardleyParser.ts:186 cleanly handle the synthetic-ID case for pipeline components without leaking into other code paths. The doc comment explains the why — thank you.
  • 🎉 Type safety upgrade. Replacing any[] with d3.Selection<SVGTextElement, ...>[] in wardleyRenderer.ts:892 and dropping the as SVGTextElement cast in favour of ! is a small but meaningful improvement.
  • 🎉 Label sanitisation in wardleyDb.ts:60 closes the last gap — now every user-provided text field goes through textSanitizer consistently.
  • 🎉 Handdrawn limitation documented in packages/mermaid/src/docs/syntax/wardley.md:444-447. That's precisely the kind of explicit acknowledgement we asked for.

Things to consider

  • 🟡 [important] Missing unit test for resolveNodeId. The fallback-to-label-match logic is new behaviour and the only non-trivial code added in this PR. A small spec — either a new wardleyBuilder.spec.ts or an addition to wardleyParser.spec.ts — covering (a) the exact-ID match, (b) the label-fallback match (the pipeline Parent_Child case), and (c) the "returns input unchanged when nothing matches" case would lock this in against regressions.

  • 🟡 [important] Hardcoded colour defaults in theme files. A few values are hex/rgba literals rather than derivations from existing theme variables: gridColor: 'rgba(100, 100, 100, 0.2)' (theme-base.js, theme-default.js, theme-forest.js, theme-neutral.js), gridColor: 'rgba(200, 200, 200, 0.15)' (theme-dark.js), and evolutionStroke: '#dc3545' / '#ff6b6b'. Users can override via themeVariables.wardley.*, but it would be cleaner if the defaults tracked the theme's palette — e.g., lineColor with an alpha for the grid, or an existing accent colour for evolutionStroke. Happy to defer if you have a specific reason these were picked.

  • 🟢 [nit] resolveNodeId does an O(n) scan on fallback (wardleyBuilder.ts:184-188). Not a concern at realistic map sizes, but a secondary labelToId map would make it O(1) if you wanted. Only worth doing if it's trivial.

  • 💡 [suggestion] Nothing structural — the only tiny thing is that the textElements type annotation in wardleyRenderer.ts:892 is fairly long. A local type alias (type WardleyText = d3.Selection<SVGTextElement, unknown, Element | null, unknown>) could keep the signature tidy if you end up reusing it.

Security

No XSS or injection concerns introduced by this PR. The textSanitizer addition strengthens the sanitisation boundary, the styles template literals follow the same pattern every other diagram's styles.ts uses, and no new innerHTML / foreignObject / event-handler sinks are introduced.

Summary

Tally: 🔴 0 · 🟡 2 · 🟢 1 · 💡 1 · 🎉 5

Nothing blocking. A small unit test for resolveNodeId would be really nice to have before merge; the hardcoded colour defaults are worth a second thought but I'm happy to defer. Let's get this across the line.

- 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>
@tractorjuice

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review!

Pushed f70916710 addressing the feedback:

  • resolveNodeId unit test — new wardleyBuilder.spec.ts with the three cases you suggested (exact-id, label-fallback, unknown-input passthrough) plus an id-wins-over-label case for disambiguation.
  • Theme-derived gridColor — switched all 5 themes from hardcoded rgba(…) to this.gridColor, which is already defined by each theme for gantt/other grid-drawing diagrams. Grid lines now track the palette consistently.
  • Type alias — extracted type WardleyText = d3.Selection<SVGTextElement, unknown, Element | null, unknown> at the top of wardleyRenderer.ts and applied it to the textElements declaration.

On evolutionStroke staying hardcoded — deferring this one: the red is a semantic Wardley/OWM convention for evolution arrows (Simon Wardley's original maps and onlinewardleymaps.com both use red regardless of background), so it doesn't really want to track a palette accent. I kept #dc3545 for light themes and #ff6b6b for dark (better contrast on dark backgrounds), and users can still override via themeVariables.wardley.evolutionStroke. Happy to revisit if you'd rather tie it to a theme variable after all.

On the O(n) scan nit — left as is; a labelToId map would need to stay in sync with addNode deletions and the extra state isn't worth it at realistic map sizes.

@knsv-bot knsv-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sisyphus-bot]

Thanks for turning the review feedback around quickly — this is a well-scoped follow-up that mixes real bug fixes with the hygiene items from the original review. Nice work threading all of it into a focused PR.

What's working well

  • 🎉 The resolveNodeId unit tests in wardleyBuilder.spec.ts are clean and deliberately cover the subtle "exact id wins over label match" case — that's the one that would have bitten someone later when a user picked a component name that collided with a synthetic pipeline id.
  • 🎉 Theme integration is applied consistently across all five themes with the same overrideable variables and sensible fallbacks pointing at shared theme tokens.
  • 🎉 styles.ts mirrors the radar diagram pattern idiomatically (cleanAndMerge over themeVariables.wardley, subfield-destructured argument) — good consistency with the existing codebase conventions.

Things to address

🟡 [important] — Missing E2E visual test for theme integration

packages/mermaid/src/diagrams/wardley/styles.ts and the five theme files introduce substantive new visual behaviour, but cypress/integration/rendering/wardley/wardley.spec.js has zero theme coverage today. Per CLAUDE.md, renderer/style changes need E2E visual regression tests using imgSnapshotTest(). A single test that renders a Wardley diagram under theme: dark (ideally forest and neutral too) would lock this in.

The test plan line "Manually verify dark, forest, and neutral themes render a Wardley map correctly" is still unchecked — would be great to convert that into a Cypress test rather than a manual step.

🟡 [important] — No E2E test for the pipeline-link fix

wardleyParser.ts:186 changes real rendering behaviour, but the existing test #3 ("should render Pipelines") uses pipeline children as link sources (Campfire Kettle -> Kettle), not as targets. The bug you're fixing is links targeting a pipeline child, where the synthetic Parent_Child id lookup previously missed.

A one-line addition to that test fixture — something like User -> Electric Kettle where Electric Kettle lives inside a pipeline — would exercise the newly-fixed path end-to-end. The unit tests cover resolveNodeId in isolation, but the integration through addLink + renderer isn't visually verified.

💡 [suggestion] — Hardcoded evolutionStroke fallback colours

In theme-base.js, theme-default.js, theme-forest.js, theme-neutral.js the evolutionStroke fallback is #dc3545, and in theme-dark.js it's #ff6b6b. Every other Wardley fallback in the same blocks points at a shared theme token (this.lineColor, this.primaryTextColor, this.background), so this one stands out.

If the design intent is "always red to signal evolution/movement", fine — but pointing at something like this.errorBkgColor (or introducing a dedicated wardleyEvolutionColor top-level variable) would integrate cleanly with user-customised palettes. Non-blocking for a -beta diagram type.

Security

No XSS or injection issues identified. textSanitizer is correctly applied to link labels in wardleyDb.ts; styles.ts only interpolates theme-config values (not user-controlled parser output); the rendering pipeline continues to terminate at DOMPurify. The <foreignObject> / <script> / event-handler surface is unchanged by this PR.


Overall: the behavioural fixes and type cleanup all look correct; the main ask is converting the two manual verification steps into Cypress tests so we don't regress on themes or pipeline targets. Happy to help with those if useful.

- 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>
@tractorjuice

Copy link
Copy Markdown
Contributor Author

Round-two review tackled in 288a6b3a4:

  • E2E theme coverage — added a parameterised it() rendering the same Wardley diagram under base, dark, forest, and neutral themes via imgSnapshotTest. Locks in the styles.ts / theme-block integration visually so any regression on a non-default theme will show up in argos.
  • Pipeline-link-target E2E — added User -> Electric Kettle to the pipelines test fixture so resolveNodeId's label-fallback is exercised end-to-end (link targeting a pipeline child, which is the path the unit-tests-only previously left uncovered).
  • wardleyEvolutionColor theme variable — introduced a top-level this.wardleyEvolutionColor in all 5 themes (default #dc3545 light / #ff6b6b dark). The wardley block's evolutionStroke now defaults to it, so users can override at the palette level via themeVariables.wardleyEvolutionColor without reaching into themeVariables.wardley.evolutionStroke. Keeps the Wardley-red default but integrates cleanly with custom palettes.

Both manual verification steps from the original test plan are now Cypress tests.

@knsv-bot knsv-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[sisyphus-bot]

Round 3 — quick one. Both rounds of feedback are now addressed cleanly and Argos has approved the visual diffs. From a re-read of the current diff (288a6b3) against the prior ask list, every item lands.

Round-2 asks → all addressed

  • 🎉 E2E theme coverage — cypress/integration/rendering/wardley/wardley.spec.js:142-167 is exactly the parameterised pattern requested. One snapshot per theme (base, dark, forest, neutral), shared fixture, drops into Argos cleanly. The "7 changed, 4 added — Changes approved" from Argos confirms maintainer sign-off on the visuals.
  • 🎉 Pipeline-link-target E2E — adding component User [0.95, 0.50] and User -> Electric Kettle to test #3 (wardley.spec.js:75-78) is the right surgery: User is top-level, Electric Kettle lives inside the Kettle pipeline as the synthetic Kettle_Electric Kettle, so the link target now exercises the resolveNodeId label fallback through the parser. Unit + E2E coverage now mirror each other.
  • 🎉 wardleyEvolutionColor as palette-level variable — clean implementation across all five themes (#dc3545 for light, #ff6b6b for dark), with the wardley block defaulting evolutionStroke to it. Users can now override at the palette level via themeVariables.wardleyEvolutionColor or per-property via themeVariables.wardley.evolutionStroke. Exactly the pattern asked for.

Other quality calls re-verified

  • 🎉 styles.ts mirrors radar/styles.ts nearly line-for-line in shape — same getThemeVariables() + getConfigAPI() + cleanAndMerge pattern, same destructured signature. Idiomatic with the existing codebase.
  • 🎉 wardleyBuilder.spec.ts covers the subtle "exact id wins over label match" disambiguation case — that's the one that would have bitten someone later if a user named a top-level component the same as a pipeline child's label.
  • 🎉 The 6-field required array on WardleyDiagramConfig is the right scope — only the layout-essential fields (width, height, padding, nodeRadius, labelFontSize, axisFontSize), leaving nodeLabelOffset and showGrid correctly optional. Defaults already present in the schema mean this can't break existing user configs.

Security

No new XSS or injection surface. textSanitizer continues to wrap user-provided link labels in wardleyDb.ts:60; styles.ts only interpolates already-validated theme values into CSS template literals (no parser-derived strings); the rendering pipeline still terminates at DOMPurify.

Tiny nit (deferrable)

  • 🟢 [nit] The new theme tests use the description string should render under the ${theme} theme while the surrounding tests use a numeric prefix ('1: should render Onset Map', '3: should render Pipelines', etc.). Not worth re-pushing for, but if you ever touch this file again, threading a number through (6a/6b/6c/6d or similar) would keep the convention consistent.

Summary

Severity Count
🔴 blocking 0
🟡 important 0
🟢 nit 1
💡 suggestion 0
🎉 praise 6

Thanks for the patience across two rounds @tractorjuice — this is in good shape now. The pipeline-link bug fix alone justified the follow-up, and the theme integration + type cleanup were genuinely valuable additions on top. Approving.

@knsv
knsv added this pull request to the merge queue Apr 27, 2026
@knsv

knsv commented Apr 27, 2026

Copy link
Copy Markdown
Collaborator

@tractorjuice Merging this! Thanks for the contribution

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Apr 27, 2026
@knsv
knsv added this pull request to the merge queue Apr 27, 2026
@knsv
knsv removed this pull request from the merge queue due to a manual request Apr 27, 2026
@knsv
knsv merged commit 193accf into mermaid-js:develop Apr 27, 2026
24 checks passed
ashishjain0512 added a commit that referenced this pull request May 11, 2026
* 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 ec2da8e (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: 6476973

* 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 Chevrotain/chevrotain#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
d3c0893 (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: d3c0893

* 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 #7561

701020c (Merge branch 'master' into develop, 2026-04-01) was a bad
merge, which didn't correctly cleanup the changeset entries.

Fixes: 701020c

* 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: d50c423
Fixes: 32c257e

* docs: improve `end note` changeset entry

This changeset did not mention it had anything to do with state
diagrams.

Fixes: bfe60cc

* docs: clarify changeset for autonumber change

Update the changeset for
0aca217 (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
0aca217 directly, since the changeset
was generated in a different PR from the rest of the changes.

Fixes: 50b2166

* docs(tidy-tree): prevent changeset from bumping mermaid

This change/PR doesn't touch mermaid, so there's no reason to patch it.

Fixes: 5ab4693

* docs(zenuml): remove `mermaid` from changeset

45a9498 (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: 45a9498

* 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: 48424ae

* 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: 8c1a0c1

* 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 `&lt;` instead of
`<` in labels.

* fix: revert endEdgeLabelLeft/endEdgeLabelRight change

fedf70c (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: fedf70c

* 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>
@aloisklink

Copy link
Copy Markdown
Member

Hi @tractorjuice,

Just to let you know, we reverted and undid your textSanitizer() change in #7726. It broke some of the labels in wardley maps.

image

The rest of your changes in this PR have gone out in today's Mermaid v11.15.0 release, see https://github.com/mermaid-js/mermaid/releases/tag/mermaid%4011.15.0! Thanks for your contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Bug / Error Something isn't working or is incorrect

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants