Compare commits

..

242 Commits

Author SHA1 Message Date
Sidharth Vinod
96778f7789 Merge pull request #6880 from mermaid-js/changeset-release/master
Version Packages
2025-08-19 13:33:48 +05:30
github-actions[bot]
d4c058bd56 Version Packages 2025-08-19 08:02:22 +00:00
Sidharth Vinod
b638a0a9c1 temp: Remove peerDeps from examples 2025-08-19 13:29:58 +05:30
Sidharth Vinod
fd9aa36c77 chore: Update peerDependencies for examples 2025-08-19 13:22:32 +05:30
Sidharth Vinod
46a9f1b31e temp: Disable cspell check as it's blocking release 2025-08-19 13:11:21 +05:30
Sidharth Vinod
83c6224cc0 Merge pull request #6878 from mermaid-js/develop
Pre Release
2025-08-19 13:06:56 +05:30
Sidharth Vinod
d8161b1923 fix: move fourcube to contributor 2025-08-19 13:05:44 +05:30
Sidharth Vinod
8223141af9 chore: add fourcube to cspell 2025-08-19 13:04:47 +05:30
Sidharth Vinod
99f98a6876 Merge pull request #6877 from mermaid-js/update-timings
Update E2E Timings
2025-08-19 13:01:41 +05:30
github-actions[bot]
ef28f548df chore: update E2E timings 2025-08-19 04:12:32 +00:00
Sidharth Vinod
e448c53b53 Merge pull request #6876 from mermaid-js/develop
Pre Release
2025-08-18 19:47:51 +05:30
Sidharth Vinod
cad144734d chore: ignore redmine.org in linkchecker 2025-08-18 17:55:32 +05:30
Sidharth Vinod
5e57f22e23 Merge pull request #6875 from mermaid-js/sidv/fixDev
chore: just something to let autofix run
2025-08-18 17:43:25 +05:30
Sidharth Vinod
fc2c32603d fix: Config handling
Co-authored-by: Saurabh Gore <saurabh@mermaidchart.com>
2025-08-18 17:26:01 +05:30
autofix-ci[bot]
8c7da5af56 [autofix.ci] apply automated fixes 2025-08-18 11:52:01 +00:00
Sidharth Vinod
4b89af3aca chore: just something to let autofix run 2025-08-18 17:16:32 +05:30
Sidharth Vinod
2aa8330279 Merge commit from fork
fix: Sanitize icons and icon labels
2025-08-18 17:00:40 +05:30
Sidharth Vinod
803e2e14be Merge remote-tracking branch 'upstream/develop' into advisory-fix-1
* upstream/develop: (68 commits)
  fix: sanitize addHtmlLabel in createLabel
  docs(integrations): update Tiki to Tiki Wiki CMS Groupware community in list cms/ecm to avoid confusion
  updated lock file
  #6856 Exposing elk configuration forceNodeModelOrder and considerModelOrder to the mermaid configuration
  chore: Modify changeset
  Update .changeset/strong-laws-confess.md
  fix: fallback to raw text instead of rendering empty boxes when `htmlLabels: false`
  chore: Modify changeset
  [autofix.ci] apply automated fixes
  test: Add E2E test for unsupported markdown
  Create strong-laws-confess.md
  fix: Remove data loss when unsupported markdown is encountered
  Apply suggestion from @sidharthv96
  Add changeset and integration test
  chore: update E2E timings
  Make elk not force model order, but strongly consider it instead
  chore(deps): update peter-evans/create-pull-request digest to 1310d7d
  added changeset
  Fix border style for hand drawn shapes
  test: Verify label is sanitized
  ...
2025-08-18 16:59:44 +05:30
Sidharth Vinod
685516a85e Merge commit from fork
fix: Sanitize KATEX blocks
2025-08-18 16:57:11 +05:30
Sidharth Vinod
880f7454a3 fix: sanitize addHtmlLabel in createLabel
Co-authored-by: Chris Grieger <chris@scolp.de>
2025-08-18 16:53:20 +05:30
Sidharth Vinod
1f46c9e9bb Merge pull request #6861 from Baraka24/docs/6339_update-tiki-in-integrations-community-list
docs(integrations): update Tiki to Tiki Wiki CMS Groupware community in list cms/ecm to avoid confusion
2025-08-16 18:46:37 +00:00
Baraka Kinywa
ec7099dc27 docs(integrations): update Tiki to Tiki Wiki CMS Groupware community in list cms/ecm to avoid confusion 2025-08-16 10:20:05 +02:00
Sidharth Vinod
e6ee145edf Merge pull request #6857 from mermaid-js/elk-exposing-considerModelOrder-to-config
Exposing elk configuration forceNodeModelOrder and considerMode…
2025-08-14 11:18:58 +00:00
Knut Sveidqvist
5cc264feb7 updated lock file 2025-08-14 13:05:40 +02:00
Alois Klink
690cc73259 Merge pull request #6855 from mermaid-js/sidv/showRawData
fix: fallback to raw text instead of rendering *Unsupported markdown*
2025-08-14 11:01:58 +00:00
Knut Sveidqvist
b9ef683fb6 #6856 Exposing elk configuration forceNodeModelOrder and considerModelOrder to the mermaid configuration 2025-08-14 12:56:20 +02:00
Sidharth Vinod
6a6289f2aa chore: Modify changeset 2025-08-14 16:18:45 +05:30
Sidharth Vinod
accb4c6369 Update .changeset/strong-laws-confess.md
Co-authored-by: Alois Klink <alois@aloisklink.com>
2025-08-14 03:47:30 -07:00
Sidharth Vinod
52be254ad3 fix: fallback to raw text instead of rendering empty boxes when htmlLabels: false 2025-08-14 16:17:04 +05:30
Sidharth Vinod
9c071a9064 chore: Modify changeset
Co-authored-by: Alois Klink <alois@mermaidchart.com>
2025-08-14 15:47:29 +05:30
Knut Sveidqvist
91d7229f1b Merge pull request #6849 from anderium/bug/6647_fix-crossing-regression-while-keeping-preferred-node-order
Make elk not force node model order, but strongly consider it instead
2025-08-14 10:17:25 +00:00
autofix-ci[bot]
60feec465b [autofix.ci] apply automated fixes 2025-08-14 09:26:22 +00:00
Sidharth Vinod
d1ae687d1e Merge branch 'sidv/showRawData' of https://github.com/mermaid-js/mermaid into sidv/showRawData
* 'sidv/showRawData' of https://github.com/mermaid-js/mermaid:
  Create strong-laws-confess.md
2025-08-14 14:48:46 +05:30
Sidharth Vinod
7af6723ac0 test: Add E2E test for unsupported markdown 2025-08-14 14:48:37 +05:30
Sidharth Vinod
da90f6760b Create strong-laws-confess.md 2025-08-14 02:16:38 -07:00
Sidharth Vinod
d74013c642 fix: Remove data loss when unsupported markdown is encountered 2025-08-14 13:05:44 +05:30
Sidharth Vinod
ce996346f8 Merge pull request #6821 from kriss-u/chore/4119_cspell_issue_eslint
chore: run cspell on lint staged for markdown files
2025-08-14 11:46:13 +05:30
Sidharth Vinod
29edfa7f56 Apply suggestion from @sidharthv96 2025-08-13 10:03:26 -07:00
Sidharth Vinod
14a4ab81c9 Merge pull request #6850 from mermaid-js/update-timings
Update E2E Timings
2025-08-13 22:15:22 +05:30
Sidharth Vinod
13d72262d9 Merge pull request #6847 from mermaid-js/rajat-ht/feat-editor-selection-ab-test
Feat: Add editor selection A/B test
2025-08-13 21:55:18 +05:30
rajat-ht
62dee0bad4 chore: update UI for variant A 2025-08-13 21:51:21 +05:30
rajat-ht
9e81e1146a chore: spell check 2025-08-13 19:34:48 +05:30
anderium
2260948b7b Add changeset and integration test 2025-08-13 14:39:09 +02:00
rajat-ht
186429ae32 chore: Update UI design 2025-08-13 16:28:18 +05:30
github-actions[bot]
657a9ef785 chore: update E2E timings 2025-08-13 04:15:39 +00:00
anderium
4f24489d81 Make elk not force model order, but strongly consider it instead
This improves the ordering in models with crossings that can be resolved by reordering nodes. It keeps the node order to still avoid #6647 which cause this regression to be introduced.

[See this example on the ELK Editor.](https://rtsys.informatik.uni-kiel.de/elklive/elkgraph.html?compressedContent=IYGw5g9gTglgLgCwLYC4AEJgE8CmUcAmAUAPQloDGUEAzjTAHZgCyjMSMAXsHDBAwDoAZtAo4AchAI5mUnCADyUaVHRwoAVxxEK-eitnTFyvAJrqeOMFnTiFAEQCiAZQD6AQXH3Xj+wHEXIlJyABUEGBo0CLQAI2ACNAB3BBwGNA16JjQRKDFJaUN5JRU1TRwBIIY5NAYABjQAbwxgGPk0ACJa9rQAXyIq6Rr3RubWkA7gbr6BnBqARhHMMY65qf7qhgAmRZa29s21mZqADR3l9uPDjYAhM72Yq8GGa+GmpfuBVd71p+vbt9243aMQEB2+R2epwB5wAWmsiIQwLM6mgALQAPiGaARBCRNXqGPmQURyIWhK2xNxyOG5OulLxzzRmOe7hxDNutLpbORLyZNWux25-I5zMFQq2fIYYuCaGuGjgaEQ0RoCAgGhACVaaAA1jgAA4KxCzGjAJDlGUASQV+GgKkicAgaAAzAAaNAAFjdczd2xgQkoehgBjkxhUZgscCsNjQdnEjjQAAppEJgOq4ABKSrVABu9Whe1zj1m2YW+aBJaLaGz2zLHWrlezTru5adDfdzbr7vhJKrBMxJaFub51cHfarTtHw-dRCAA)
2025-08-13 01:47:25 +02:00
rajat-ht
2a514fa69e Feat: Add editor selection A/B test 2025-08-12 18:18:03 +05:30
Sidharth Vinod
bf3ca9d1ef Merge pull request #6332 from ajuckel/ajuckel/issue-6171-equals-in-sequence-labels
fix: Allow equals sign in sequenceDiagram labels
2025-08-11 14:36:42 +00:00
Shubham P
e53c17a012 Merge pull request #6841 from mermaid-js/renovate/peter-evans-create-pull-request-digest
chore(deps): update peter-evans/create-pull-request digest to 1310d7d
2025-08-11 07:24:30 +00:00
renovate[bot]
bb2d6973ba chore(deps): update peter-evans/create-pull-request digest to 1310d7d 2025-08-11 02:12:30 +00:00
Sidharth Vinod
aeaf626bb5 fix: Sanitize check in unit test 2025-08-08 18:59:47 +05:30
Sidharth Vinod
9322771b5c Merge pull request #6839 from aloisklink/refactor/remove-unnecessary-check-in-DOMPurify-hooks
refactor: remove unnecessary `Element` check since DOMPurify 3.2.2
2025-08-08 13:09:49 +00:00
Sidharth Vinod
2fe3063bf5 Merge pull request #6838 from saurabhg772244/saurabh/fix-node-border-style-for-hand-drawn-shapes
Saurabh/fix-node-border-style-for-hand-drawn-shapes
2025-08-08 13:02:38 +00:00
Shubham P
4e55a45b1b Merge branch 'develop' into saurabh/fix-node-border-style-for-hand-drawn-shapes 2025-08-08 16:57:55 +05:30
Knut Sveidqvist
3d319824a6 Merge pull request #6683 from mermaid-js/6576-state-diagram-label-position
6576: State diagram edge label position
2025-08-08 10:26:18 +00:00
Knut Sveidqvist
aa5d443a46 Merge pull request #6693 from mermaid-js/6649-gantt-chart-dateformat
6649: gantt chart date format issue
2025-08-08 10:09:59 +00:00
Knut Sveidqvist
356da0b4d7 Merge branch 'develop' into 6576-state-diagram-label-position 2025-08-08 12:01:34 +02:00
Knut Sveidqvist
22530a8bdf Merge pull request #6653 from mermaid-js/4459-remove-beta-suffix
#4459: Remove beta suffix
2025-08-08 09:55:06 +00:00
Knut Sveidqvist
e6574ef40c Merge pull request #6651 from mermaid-js/6584-piechart-zero-negative-values
6584: prevent pie chart crash on zero or negative values
2025-08-08 09:52:16 +00:00
Knut Sveidqvist
c4eb526162 Merge pull request #6734 from mermaid-js/6730-gantt-excludes-datetime-format
6730: Fix excluded dates ignored in YYYY-MM-DD HH:mm:ss date format in gantt diagram
2025-08-08 09:33:56 +00:00
saurabhg772244
4d62d59632 added changeset 2025-08-08 14:19:03 +05:30
saurabhg772244
5af489d8dd Fix border style for hand drawn shapes 2025-08-08 14:17:09 +05:30
Sidharth Vinod
096fbe933e test: Verify label is sanitized
Co-authored-by: Chris Grieger <chris@scolp.de>
2025-08-08 12:55:18 +05:30
Sidharth Vinod
e539909e87 fix: Label in addHtmlSpan 2025-08-08 12:54:26 +05:30
Knut Sveidqvist
074701e316 Merge pull request #6833 from mermaid-js/6611-block-diagram-arrow-direction
6611: Block diagram shows incorrect arrow direction
2025-08-08 07:24:02 +00:00
Sidharth Vinod
cfc76ef1cb fix: sanitize HTML for spans 2025-08-07 22:49:39 +05:30
Sidharth Vinod
4b31361506 Merge pull request #6733 from mermaid-js/fix-connection-gaps-in-flowchart-shapes
Fix: Connection gaps in flowchart shapes
2025-08-07 14:36:34 +00:00
Sidharth Vinod
20a18971ea chore: Add CVE ID 2025-08-07 13:39:01 +05:30
Sidharth Vinod
e1e36dfcb3 chore: Add CVE ID 2025-08-07 13:37:54 +05:30
darshanr0107
165ffefad5 chore: remove unnecessary test case
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-07 12:09:49 +05:30
darshanr0107
9258b2933b add changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-06 20:05:35 +05:30
darshanr0107
f5445b266e fix: incorrect arrow direction in block direction
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-06 20:01:28 +05:30
Alois Klink
7a1530d911 refactor: remove unnecessary Element check
[DOMPurify v3.2.2][1] narrowed down the types of these hooks so that
these are known to have the `Element` type, which means checking the
type of these at runtime is unnecessary.

[1]: https://github.com/cure53/DOMPurify/releases/tag/3.2.2

See: fe3cffbb67
See: 4cdfd1ffef
2025-08-06 22:30:09 +08:00
Shubham P
d93d9a521d Merge pull request #6820 from kriss-u/fix/6815_escape_namespace_class_diagram
fix: add classLiteral to the language for class diagram namespace
2025-08-06 08:12:54 +00:00
Sidharth Vinod
e32e80ea7f chore: Add @fourcube in changeset 2025-08-05 10:17:21 -07:00
Krishna Upadhyay
c99bce6bab fix: add classLiteral to the language for class diagram namespace 2025-08-05 12:15:44 -05:00
Sidharth Vinod
3256807d25 chore: Add @fourcube in changeset 2025-08-05 22:44:58 +05:30
Sidharth Vinod
0133f1c0c5 docs: Add changeset 2025-08-05 22:42:13 +05:30
Sidharth Vinod
12e01bdb5c docs: Add changeset 2025-08-05 22:41:10 +05:30
Sidharth Vinod
a776b4f0ab Merge branch 'advisory-fix-1' of https://github.com/mermaid-js/mermaid-ghsa-7rqq-prvp-x9jh into advisory-fix-1
* 'advisory-fix-1' of https://github.com/mermaid-js/mermaid-ghsa-7rqq-prvp-x9jh:
  fix: Sanitize Katex
2025-08-05 22:37:06 +05:30
Sidharth Vinod
8d79bc9b19 test: check katex sanitization 2025-08-05 22:36:28 +05:30
Sidharth Vinod
ecf7ab4355 fix: Sanitize getIconSVG 2025-08-05 22:33:05 +05:30
Sidharth Vinod
c61a431e2d fix: Sanitize iconText 2025-08-05 22:32:38 +05:30
Sidharth Vinod
526b35c602 test: Architecture XSS 2025-08-05 22:30:45 +05:30
Sidharth Vinod
8090580c67 test: Handle missing bindFunctions 2025-08-05 22:27:21 +05:30
Sidharth Vinod
9d685178d2 fix: Sanitize Katex 2025-08-05 20:44:03 +05:30
omkarht
b451c66d7c chore: added test case for updated shapes
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-05 19:43:34 +05:30
omkarht
1dd11705d9 fix: updated changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-05 17:40:18 +05:30
Krishna Upadhyay
17142ef8d7 chore: run cspell on lint staged for markdown files [skip ci] 2025-08-04 20:53:19 -05:00
omkarht
29de40478f Merge branch 'develop' into fix-connection-gaps-in-flowchart-shapes 2025-08-04 17:06:03 +05:30
Shubham P
4ab98c2ec7 Merge pull request #6813 from mermaid-js/renovate/peter-evans-create-pull-request-digest
chore(deps): update peter-evans/create-pull-request digest to 07cbaeb
2025-08-04 07:36:23 +00:00
renovate[bot]
aeb51e56e2 chore(deps): update peter-evans/create-pull-request digest to 07cbaeb 2025-08-04 03:11:32 +00:00
Shubham P
ddcd8a5e73 Merge pull request #6803 from mermaid-js/6774-update-db-class-architecture-diagram
6774: update architecture diagram to reflect new class-based DB structure
2025-07-30 13:37:32 +00:00
Shubham P
e464d080ef Merge pull request #6804 from mermaid-js/6691-update-packet-diagram-class-based-db
6691: update packet diagram to use new class-based DB approach
2025-07-30 13:36:43 +00:00
Sidharth Vinod
1a9b94ca2d Merge pull request #6688 from zarhasan/patch-1
Update integrations-community.md
2025-07-30 13:32:11 +00:00
Shubham P
e4b33a1d99 Merge pull request #6796 from HashanCP/bug/6795_fix-flowchart-elk-detector
make flowchart elk detector regex match less greedy
2025-07-30 09:41:37 +00:00
omkarht
e27a9da61d Update .changeset/vast-buses-see.md
Co-authored-by: Sidharth Vinod <github@sidharth.dev>
2025-07-30 13:18:44 +05:30
omkarht
03cf10003f fix: modified changeset to patch
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-30 13:16:49 +05:30
omkarht
8e31fdb611 Merge branch 'develop' into 6774-update-db-class-architecture-diagram 2025-07-29 17:01:12 +05:30
omkarht
5dd748148f fix: refactored code
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-29 16:59:02 +05:30
omkarht
895f9d43ff 6691: update packet diagram to use new class-based DB approach
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-29 16:55:04 +05:30
shubham-mermaid
fb890a2be8 Merge branch 'develop' into bug/6795_fix-flowchart-elk-detector 2025-07-29 16:09:29 +05:30
shubham-mermaid
5986189a52 Merge pull request #6801 from mermaid-js/sidv/fixID
fix: Casing of ID in requirement diagram
2025-07-29 10:16:51 +00:00
omkarht
1988dfc956 Merge branch 'develop' into 6774-update-db-class-architecture-diagram 2025-07-29 15:41:46 +05:30
omkarht
e48b0ba61d chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-29 15:41:21 +05:30
omkarht
1a4b8662cf 6774: update architecture diagram to reflect new class-based DB structure
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-29 15:21:34 +05:30
shubham-mermaid
6083463c8e Merge pull request #6799 from mermaid-js/renovate/patch-eslint
chore(deps): update eslint (patch)
2025-07-29 09:49:28 +00:00
shubham-mermaid
1a14e331ea Merge pull request #6739 from kriss-u/bug/6681_td_tb_behavior
fix: trim the direction string in flowchart
2025-07-29 09:43:32 +00:00
shubham-mermaid
ebb6680eba Merge pull request #6792 from mermaid-js/prashants/mermaid-diagrams-flowchart-mindmap-mermaidAPI
chore: Mermaid diagrams flowchart mindmap mermaid api
2025-07-29 09:42:41 +00:00
shubham-mermaid
6d1d46f88a Merge branch 'develop' into bug/6795_fix-flowchart-elk-detector 2025-07-29 15:11:56 +05:30
shubham-mermaid
435790f931 Merge pull request #6798 from ZenUml/fix/zenuml-rendering
Fix: zenuml rendering
2025-07-29 09:41:00 +00:00
shubham-mermaid
ffe9c1090e Merge branch 'develop' into sidv/fixID 2025-07-29 15:09:56 +05:30
renovate[bot]
a476e99d4c chore(deps): update eslint 2025-07-29 08:41:36 +00:00
Alois Klink
0cc0b63e52 Merge pull request #6702 from qraqras/bug/6633_remove-unexpected-spaces-in-block-diagram
Fix: Remove unexpected spaces in Block Diagram
2025-07-29 08:29:22 +00:00
Prashant-7718
694844050a removed changeset 2025-07-29 13:34:18 +05:30
autofix-ci[bot]
1be1620000 [autofix.ci] apply automated fixes 2025-07-29 07:45:04 +00:00
shubham-mermaid
c36cd05c45 Create light-flowers-judge.md 2025-07-29 13:10:10 +05:30
Alois Klink
b7a591b8d3 docs(block): add clarifying comment for overflow
This line is a bit confusing to me, so I thought I should clarify it!

See: 939da082b2
2025-07-29 15:45:53 +09:00
Alois Klink
8bb29fc879 docs(block): add changeset for overflow bug
Add a changeset that explains that overflowing blocks no longer
create an empty line in block diagrams or cause bugs in later lines.

Technically, this is a breaking change since it can change the layout of
block diagrams, but this diagram is still in `-beta` and they all will
still render correctly.
2025-07-29 15:40:36 +09:00
Alois Klink
e073c80019 test(e2e): test block column overflowing
Add a test-case for 939da082b (Fix 6633, 2025-06-28), that checks
whether overflowing blocks render correctly.
2025-07-29 15:27:10 +09:00
Sidharth Vinod
01aaef39b4 docs: Add changeset 2025-07-29 11:31:28 +05:30
Sidharth Vinod
3d640fc620 fix: Casing of ID to match https://www.britannica.com/dictionary/ID 2025-07-29 11:30:11 +05:30
MrCoder
724197c910 chore: add missing newline at end of README.md 2025-07-27 21:25:46 +10:00
MrCoder
6180c5f2ff chore: add TypeScript declaration for ZenUml module 2025-07-27 21:12:08 +10:00
MrCoder
a9f7a94ae3 fix: remove TypeScript error suppression for ZenUml instantiation 2025-07-27 21:08:00 +10:00
MrCoder
3ffe9618ae chore: added changeset. 2025-07-27 21:05:49 +10:00
MrCoder
da539c1fa1 chore: Upgrade @zenuml/core to version 3.35.2 2025-07-27 20:55:58 +10:00
Hashan Chandika
5e8aa2dccf add unit tests for coverage #6795 2025-07-27 15:21:46 +05:30
Hashan Chandika
ac04172cf8 add tests for #6795 2025-07-27 14:53:28 +05:30
Hashan Chandika
cf5b4b89a8 make regex match less greedy 2025-07-27 13:51:26 +05:30
MrCoder
1c269e0432 Convert mermaid-zenuml README from symlink to regular file
Changed packages/mermaid-zenuml/README.md from a symbolic link pointing to ../mermaid/src/docs/syntax/zenuml.md to a standalone README file with comprehensive documentation for the @mermaid-js/mermaid-zenuml package.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-25 07:52:32 +10:00
Prashant-7718
999b836508 updated changeset 2025-07-24 14:46:34 +05:30
Prashant-7718
326e4e3693 added mermaid files for documentation 2025-07-24 14:16:18 +05:30
autofix-ci[bot]
9c92da487f [autofix.ci] apply automated fixes 2025-07-24 08:39:03 +00:00
Prashant-7718
10752f1357 added mermaid files 2025-07-24 14:03:50 +05:30
autofix-ci[bot]
1a80854242 [autofix.ci] apply automated fixes 2025-07-24 07:30:35 +00:00
Prashant-7718
fc9c600a31 Merge branch 'develop' of https://github.com/mermaid-js/mermaid into prashants/mermaid-diagrams-flowchart-mindmap-mermaidAPI 2025-07-24 12:52:47 +05:30
Prashant-7718
da8ce0b93e chore: removed unwanted files 2025-07-24 12:49:08 +05:30
Prashant-7718
00a79353fc changeset added 2025-07-24 12:40:39 +05:30
shubham-mermaid
1ceeca1ef1 Merge pull request #6782 from mermaid-js/renovate/peter-evans-create-pull-request-digest
chore(deps): update peter-evans/create-pull-request digest to 2e50522
2025-07-24 06:21:23 +00:00
shubham-mermaid
94890390ef Merge pull request #6783 from mermaid-js/renovate/patch-eslint
chore(deps): update eslint (patch)
2025-07-24 06:20:37 +00:00
Prashant-7718
adfeb093cb chore: mermaid diagrams for flowchart, mindmap and mermaidAPI files 2025-07-23 18:55:25 +05:30
renovate[bot]
366d217928 chore(deps): update eslint 2025-07-23 09:15:14 +00:00
renovate[bot]
b94f1336ab chore(deps): update peter-evans/create-pull-request digest to 2e50522 2025-07-23 09:14:38 +00:00
shubham-mermaid
020c6d66e0 Merge pull request #6744 from SpecularAura/feature/6617_per-link-style
6617: Support Per-Link Curve Styling in Flowcharts
2025-07-23 09:00:42 +00:00
Sidharth Vinod
cfc2551bdc Merge pull request #6778 from lcnittl/docs/NA_update-mime-type-iana-status
Update MIME type IANA status
2025-07-21 00:46:51 +05:30
autofix-ci[bot]
000308c8f5 [autofix.ci] apply automated fixes 2025-07-18 21:44:56 +00:00
SpecularAura
6039a8b930 Merge remote-tracking branch 'origin/feature/6617_per-link-style' into feature/6617_per-link-style 2025-07-18 21:38:04 +00:00
SpecularAura
cd282f2245 Added Documentation for the per link styling in syntax/flowchat 2025-07-18 21:19:06 +00:00
autofix-ci[bot]
a27d90fe9c [autofix.ci] apply automated fixes 2025-07-18 13:05:27 +00:00
Christian Knittl-Frank
64bf34b9ab Update MIME type IANA status
The mermaid media type has been recognized by IANA on 2023-09-18
2025-07-18 13:54:16 +01:00
Sidharth Vinod
bcd3e33243 Merge pull request #6776 from mermaid-js/update-blog
DOCS: update blog page
2025-07-18 13:52:59 +05:30
Alois Klink
9faf2f9fb2 Merge pull request #6717 from mermaid-js/6633-Stacking-blocks-renders-unexpected-space
6633: Log warning for blocks exceeding column width
2025-07-18 07:31:17 +00:00
darshanr0107
f683b03645 Merge branch '6633-Stacking-blocks-renders-unexpected-space' of https://github.com/mermaid-js/mermaid into 6633-Stacking-blocks-renders-unexpected-space 2025-07-18 12:50:36 +05:30
darshanr0107
9cef40d164 update changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-18 12:50:26 +05:30
Steph
b2754bc553 update blog page 2025-07-17 14:44:38 -07:00
SpecularAura
04612e078a Removed directive syntax from the documentation 2025-07-17 21:12:51 +00:00
SpecularAura
af585bdcc7 Added a functioning visual test for per link curve styling 2025-07-17 21:09:22 +00:00
autofix-ci[bot]
37bfa2aa75 [autofix.ci] apply automated fixes 2025-07-17 14:46:30 +00:00
darshanr0107
54640ce476 resolved PR comments
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-17 20:11:21 +05:30
shubham-mermaid
47b4c56b2b Merge pull request #6772 from hustcc/patch-2
chore: update to add mcp-mermaid
2025-07-17 07:28:14 +00:00
Sidharth Vinod
6b1b0bf151 chore: Add symlink 2025-07-17 12:45:46 +05:30
Sidharth Vinod
7ba332ad4a docs: Move changelog content 2025-07-17 12:45:03 +05:30
Sidharth Vinod
b65a73f432 Merge pull request #6697 from mermaid-js/rajat-ht/feat-add-start-pro-trial
feat: Add start pro trial button.
2025-07-17 11:57:18 +05:30
autofix-ci[bot]
412d2a09d3 [autofix.ci] apply automated fixes 2025-07-17 01:41:47 +00:00
hustcc
7886fed8b2 chore: update to add mcp-mermaid 2025-07-17 09:36:16 +08:00
Sidharth Vinod
af3d5b6528 docs: Add README for examples 2025-07-16 20:11:38 +05:30
Sidharth Vinod
404286a90d Merge branch 'master' into develop
* master:
  Version Packages
2025-07-16 19:53:10 +05:30
Sidharth Vinod
827a9af790 chore: Add repo to examples package 2025-07-16 19:52:52 +05:30
darshanr0107
a14cd0e2a1 fix: update test case
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-16 18:47:45 +05:30
darshanr0107
0823e08a54 Merge branch 'develop' into 6584-piechart-zero-negative-values 2025-07-16 18:05:06 +05:30
Ashish Jain
95733b6295 Merge pull request #6762 from mermaid-js/changeset-release/master
Version Packages
2025-07-16 14:05:42 +02:00
darshanr0107
5cafe241d0 Merge branch 'develop' into 6649-gantt-chart-dateformat 2025-07-16 17:29:52 +05:30
darshanr0107
c847817a54 Merge branch 'develop' into 6730-gantt-excludes-datetime-format 2025-07-16 17:29:37 +05:30
darshanr0107
8b86d617e7 Merge branch 'develop' into 6633-Stacking-blocks-renders-unexpected-space 2025-07-16 17:29:09 +05:30
darshanr0107
3ab0961bdc fix: PR comment and updated test case
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-16 17:27:08 +05:30
darshanr0107
cc2112c7aa fix: updated test case
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-16 15:25:16 +05:30
omkarht
0892870b5d Merge branch 'develop' into fix-connection-gaps-in-flowchart-shapes 2025-07-16 14:55:22 +05:30
omkarht
500f90a105 Merge branch 'fix-connection-gaps-in-flowchart-shapes' of https://github.com/mermaid-js/mermaid into fix-connection-gaps-in-flowchart-shapes 2025-07-16 14:54:09 +05:30
omkarht
14983158a2 fix: updated hexagon shape
on-behalf-of: @Mermaid-Chart hello@mermaidchart.com
2025-07-16 14:52:53 +05:30
darshanr0107
3fea9e8759 fix: update changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-16 13:40:40 +05:30
darshanr0107
e5ea2ed0b1 Merge branch 'develop' of https://github.com/mermaid-js/mermaid into 4459-remove-beta-suffix 2025-07-16 13:38:28 +05:30
darshanr0107
34e7f9704b fix: update changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-16 13:20:47 +05:30
darshanr0107
ac976245ad fix: fix review comments
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-16 13:11:44 +05:30
darshanr0107
1176d30668 fix: update changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-16 12:38:35 +05:30
Sidharth Vinod
6728852b7a Merge branch 'develop' into fix-connection-gaps-in-flowchart-shapes 2025-07-15 20:37:20 +05:30
Sidharth Vinod
15e7c890ed Merge branch 'develop' into 6576-state-diagram-label-position 2025-07-15 20:21:40 +05:30
omkarht
0451e343ef Merge branch 'develop' into fix-connection-gaps-in-flowchart-shapes 2025-07-14 16:53:02 +05:30
omkarht
a2dbc8e4b3 updated hexagon shape 2025-07-14 16:51:34 +05:30
autofix-ci[bot]
003d1c7a70 [autofix.ci] apply automated fixes 2025-07-12 20:03:58 +00:00
SpecularAura
daf8d8d3be Added Changeset for Per Link Styling of edges 2025-07-12 19:41:01 +00:00
SpecularAura
d7a55b422b Added tests for Per Link Curve Styling 2025-07-12 19:08:42 +00:00
Krishna Upadhyay
98bf9b4cb4 test: add tests for directions in flowchart 2025-07-10 22:17:30 -05:00
Krishna Upadhyay
e9ce8cf4da fix: trim the direction string in flowchart 2025-07-10 22:01:50 -05:00
SpecularAura
b3a12237c0 Added per link styling
Edited flowDB.ts which already had interpolate for styling links
individually. Added the ability to modify this parameter using the
newer @ syntax using the curve property.
2025-07-10 12:55:09 +00:00
darshanr0107
aea16eaf7e Merge branch 'develop' into 6584-piechart-zero-negative-values 2025-07-10 16:36:41 +05:30
darshanr0107
bb7cd70034 Merge branch 'develop' into 6730-gantt-excludes-datetime-format 2025-07-10 16:35:54 +05:30
darshanr0107
35a92efcdc resolve PR comments 2025-07-10 15:49:53 +05:30
darshanr0107
fce7cabb71 add changeset 2025-07-09 20:24:36 +05:30
darshanr0107
38e6dc497a Merge branch 'develop' into 6730-gantt-excludes-datetime-format 2025-07-09 20:06:07 +05:30
darshanr0107
5f0c53c8a7 fix excluded dates ignored in YYYY-MM-DD HH:mm:ss date format 2025-07-09 20:02:18 +05:30
omkarht
f9da4433ff Merge branch 'develop' into fix-connection-gaps-in-flowchart-shapes 2025-07-09 19:07:25 +05:30
omkarht
fc07f0d8ab added changeset 2025-07-09 19:06:57 +05:30
omkarht
ce7a487dfc Fix: Fixed Connection gaps in flowchart shapes 2025-07-09 18:37:52 +05:30
darshanr0107
8c0d12027d Merge branch 'develop' into 6584-piechart-zero-negative-values 2025-07-09 12:22:59 +05:30
rajat-ht
ea72740d1f Merge branch 'master' of https://github.com/mermaid-js/mermaid into rajat-ht/feat-add-start-pro-trial 2025-07-07 17:08:17 +05:30
darshanr0107
71b04f93b0 added changeset 2025-07-04 18:37:32 +05:30
darshanr0107
27185f62e4 throw error when block width is greater than column width 2025-07-04 18:26:39 +05:30
rajat-ht
427dc38857 chore: revert 'Open Editor' link in header 2025-07-03 19:21:24 +05:30
rajat-ht
ec21042c53 chore: minor UI updates and added search params to URL 2025-07-03 19:00:33 +05:30
rajat-ht
4a86b68e01 chore: update UI as per suggestion 2025-07-02 18:51:06 +05:30
darshanr0107
1414380181 Merge branch 'develop' into 6584-piechart-zero-negative-values 2025-07-01 12:34:50 +05:30
darshanr0107
6686ee9253 Merge branch 'develop' into 4459-remove-beta-suffix 2025-07-01 12:34:33 +05:30
darshanr0107
7f109c7b94 Merge branch 'develop' into 6576-state-diagram-label-position 2025-07-01 11:15:46 +05:30
darshanr0107
b9c3375be3 Merge branch '6576-state-diagram-label-position' of https://github.com/mermaid-js/mermaid into 6576-state-diagram-label-position 2025-06-30 20:20:48 +05:30
darshanr0107
4254bdd473 added test cases for edge label positions 2025-06-30 20:15:32 +05:30
darshanr0107
4012cbf013 Update changeset
Co-authored-by: Sidharth Vinod <github@sidharth.dev>
2025-06-30 18:32:31 +05:30
qraqras
939da082b2 Fix 6633 2025-06-28 08:18:19 +00:00
rajat-ht
8693be56ee feat: Add start pro trial button. 2025-06-27 17:26:54 +05:30
darshanr0107
814b68b4a9 added changeset 2025-06-27 15:16:20 +05:30
darshanr0107
bfa0eefa32 fix gantt chart date format issue 2025-06-27 15:01:45 +05:30
autofix-ci[bot]
e70be4f155 [autofix.ci] apply automated fixes 2025-06-25 11:00:55 +00:00
Khizar Hasan
6621f6ddb2 Update integrations-community.md
Added 'WP Documentation' in integrations.
2025-06-25 16:25:47 +05:30
darshanr0107
579c22cf5d refactor code 2025-06-24 17:08:42 +05:30
darshanr0107
33e08daf17 added changeset 2025-06-24 15:10:36 +05:30
darshanr0107
24257de8a6 fix state diagram edge label position 2025-06-24 14:50:20 +05:30
darshanr0107
42ac1848dd Merge branch 'develop' into 6584-piechart-zero-negative-values 2025-06-23 19:52:23 +05:30
darshanr0107
3920ad442d resolved PR comments 2025-06-23 15:08:11 +05:30
darshanr0107
53d27b771d Merge branch 'develop' into 4459-remove-beta-suffix 2025-06-17 15:57:40 +05:30
darshanr0107
2c0931da46 added changeset 2025-06-17 15:53:25 +05:30
darshanr0107
8224a81ab6 fix xychart test cases 2025-06-17 13:52:04 +05:30
darshanr0107
98442294ed resolved PR comments 2025-06-17 13:44:12 +05:30
darshanr0107
a3d164fde8 remove beta suffix for block diagram 2025-06-17 12:43:26 +05:30
darshanr0107
9dc987b28b remove beta support for sankey diagram 2025-06-17 12:12:40 +05:30
darshanr0107
72c0d9df26 added test case for preventing zero% values from rendering 2025-06-16 19:38:58 +05:30
darshanr0107
cfbce54638 prevent zero% slices from rendering in pie chart 2025-06-16 19:11:29 +05:30
darshanr0107
6979aa1013 Fix failing test cases and update error messages for negative and zero inputs 2025-06-16 14:53:09 +05:30
darshanr0107
ea60525988 remove beta suffix from xyChart 2025-06-13 19:33:43 +05:30
darshanr0107
9da6fb39ae added changeset 2025-06-12 19:18:36 +05:30
darshanr0107
83b9a17277 fix: prevent pie chart crash on zero or negative values 2025-06-12 18:13:11 +05:30
Anthony Juckel
6cc192680a Added changeset 2025-03-03 15:10:37 -06:00
Anthony Juckel
3dd6107e76 fix: Allow equals sign in sequenceDiagram labels 2025-03-03 15:08:34 -06:00
129 changed files with 7079 additions and 2723 deletions

View File

@@ -2,6 +2,7 @@
Ashish Jain
cpettitt
Dong Cai
fourcube
knsv
Knut Sveidqvist
Nikolay Rozhkov

1
.github/lychee.toml vendored
View File

@@ -59,6 +59,7 @@ exclude = [
"https://huehive.co",
"https://foswiki.org",
"https://www.gnu.org",
"https://redmine.org",
"https://mermaid-preview.com"
]

View File

@@ -58,7 +58,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT
- name: Commit and create pull request
uses: peter-evans/create-pull-request@889dce9eaba7900ce30494f5e1ac7220b27e5c81
uses: peter-evans/create-pull-request@1310d7dab503600742045e6fd4b84dda64352858
with:
add-paths: |
cypress/timings.json

File diff suppressed because it is too large Load Diff

1
CHANGELOG.md Symbolic link
View File

@@ -0,0 +1 @@
./packages/mermaid/CHANGELOG.md

View File

@@ -14,7 +14,7 @@ interface CodeObject {
mermaid: CypressMermaidConfig;
}
const utf8ToB64 = (str: string): string => {
export const utf8ToB64 = (str: string): string => {
return Buffer.from(decodeURIComponent(encodeURIComponent(str))).toString('base64');
};
@@ -22,7 +22,7 @@ const batchId: string =
'mermaid-batch-' +
(Cypress.env('useAppli')
? Date.now().toString()
: Cypress.env('CYPRESS_COMMIT') || Date.now().toString());
: (Cypress.env('CYPRESS_COMMIT') ?? Date.now().toString()));
export const mermaidUrl = (
graphStr: string | string[],
@@ -61,9 +61,7 @@ export const imgSnapshotTest = (
sequence: {
...(_options.sequence ?? {}),
actorFontFamily: 'courier',
noteFontFamily: _options.sequence?.noteFontFamily
? _options.sequence.noteFontFamily
: 'courier',
noteFontFamily: _options.sequence?.noteFontFamily ?? 'courier',
messageFontFamily: 'courier',
},
};

View File

@@ -1,4 +1,4 @@
import { mermaidUrl } from '../../helpers/util.ts';
import { imgSnapshotTest, mermaidUrl, utf8ToB64 } from '../../helpers/util.ts';
describe('XSS', () => {
it('should handle xss in tags', () => {
const str =
@@ -141,4 +141,37 @@ describe('XSS', () => {
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
it('should sanitize icon labels in architecture diagrams', () => {
const str = JSON.stringify({
code: `architecture-beta
group api(cloud)[API]
service db "<img src=x onerror=\\"xssAttack()\\">" [Database] in api`,
});
imgSnapshotTest(utf8ToB64(str), {}, true);
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
it('should sanitize katex blocks', () => {
const str = JSON.stringify({
code: `sequenceDiagram
participant A as Alice<img src="x" onerror="xssAttack()">$$\\text{Alice}$$
A->>John: Hello John, how are you?`,
});
imgSnapshotTest(utf8ToB64(str), {}, true);
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
it('should sanitize labels', () => {
const str = JSON.stringify({
code: `erDiagram
"<img src=x onerror=xssAttack()>" ||--|| ENTITY2 : "<img src=x onerror=xssAttack()>"
`,
});
imgSnapshotTest(utf8ToB64(str), {}, true);
cy.wait(1000);
cy.get('#the-malware').should('not.exist');
});
});

View File

@@ -16,7 +16,7 @@ describe('Block diagram', () => {
it('BL2: should handle columns statement in sub-blocks', () => {
imgSnapshotTest(
`block-beta
`block
id1["Hello"]
block
columns 3
@@ -32,7 +32,7 @@ describe('Block diagram', () => {
it('BL3: should align block widths and handle columns statement in sub-blocks', () => {
imgSnapshotTest(
`block-beta
`block
block
columns 1
id1
@@ -48,7 +48,7 @@ describe('Block diagram', () => {
it('BL4: should align block widths and handle columns statements in deeper sub-blocks then 1 level', () => {
imgSnapshotTest(
`block-beta
`block
columns 1
block
columns 1
@@ -68,7 +68,7 @@ describe('Block diagram', () => {
it('BL5: should align block widths and handle columns statements in deeper sub-blocks then 1 level (alt)', () => {
imgSnapshotTest(
`block-beta
`block
columns 1
block
id1
@@ -87,7 +87,7 @@ describe('Block diagram', () => {
it('BL6: should handle block arrows and spece statements', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
space:3
ida idb idc
@@ -106,7 +106,7 @@ describe('Block diagram', () => {
it('BL7: should handle different types of edges', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
A space:5
A --o B
@@ -119,7 +119,7 @@ describe('Block diagram', () => {
it('BL8: should handle sub-blocks without columns statements', () => {
imgSnapshotTest(
`block-beta
`block
columns 2
C A B
block
@@ -133,7 +133,7 @@ describe('Block diagram', () => {
it('BL9: should handle edges from blocks in sub blocks to other blocks', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
B space
block
@@ -147,7 +147,7 @@ describe('Block diagram', () => {
it('BL10: should handle edges from composite blocks', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
B space
block BL
@@ -161,7 +161,7 @@ describe('Block diagram', () => {
it('BL11: should handle edges to composite blocks', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
B space
block BL
@@ -175,7 +175,7 @@ describe('Block diagram', () => {
it('BL12: edges should handle labels', () => {
imgSnapshotTest(
`block-beta
`block
A
space
A -- "apa" --> E
@@ -186,7 +186,7 @@ describe('Block diagram', () => {
it('BL13: should handle block arrows in different directions', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
space blockArrowId1<["down"]>(down) space
blockArrowId2<["right"]>(right) blockArrowId3<["Sync"]>(x, y) blockArrowId4<["left"]>(left)
@@ -199,7 +199,7 @@ describe('Block diagram', () => {
it('BL14: should style statements and class statements', () => {
imgSnapshotTest(
`block-beta
`block
A
B
classDef blue fill:#66f,stroke:#333,stroke-width:2px;
@@ -212,7 +212,7 @@ describe('Block diagram', () => {
it('BL15: width alignment - D and E should share available space', () => {
imgSnapshotTest(
`block-beta
`block
block
D
E
@@ -225,7 +225,7 @@ describe('Block diagram', () => {
it('BL16: width alignment - C should be as wide as the composite block', () => {
imgSnapshotTest(
`block-beta
`block
block
A("This is the text")
B
@@ -238,7 +238,7 @@ describe('Block diagram', () => {
it('BL17: width alignment - blocks should be equal in width', () => {
imgSnapshotTest(
`block-beta
`block
A("This is the text")
B
C
@@ -249,7 +249,7 @@ describe('Block diagram', () => {
it('BL18: block types 1 - square, rounded and circle', () => {
imgSnapshotTest(
`block-beta
`block
A["square"]
B("rounded")
C(("circle"))
@@ -260,7 +260,7 @@ describe('Block diagram', () => {
it('BL19: block types 2 - odd, diamond and hexagon', () => {
imgSnapshotTest(
`block-beta
`block
A>"rect_left_inv_arrow"]
B{"diamond"}
C{{"hexagon"}}
@@ -271,7 +271,7 @@ describe('Block diagram', () => {
it('BL20: block types 3 - stadium', () => {
imgSnapshotTest(
`block-beta
`block
A(["stadium"])
`,
{}
@@ -280,7 +280,7 @@ describe('Block diagram', () => {
it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
imgSnapshotTest(
`block-beta
`block
A[/"lean right"/]
B[\"lean left"\]
C[/"trapezoid"\]
@@ -292,7 +292,7 @@ describe('Block diagram', () => {
it('BL22: block types 1 - square, rounded and circle', () => {
imgSnapshotTest(
`block-beta
`block
A["square"]
B("rounded")
C(("circle"))
@@ -303,7 +303,7 @@ describe('Block diagram', () => {
it('BL23: sizing - it should be possible to make a block wider', () => {
imgSnapshotTest(
`block-beta
`block
A("rounded"):2
B:2
C
@@ -314,7 +314,7 @@ describe('Block diagram', () => {
it('BL24: sizing - it should be possible to make a composite block wider', () => {
imgSnapshotTest(
`block-beta
`block
block:2
A
end
@@ -326,7 +326,7 @@ describe('Block diagram', () => {
it('BL25: block in the middle with space on each side', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
space
middle["In the middle"]
@@ -337,7 +337,7 @@ describe('Block diagram', () => {
});
it('BL26: space and an edge', () => {
imgSnapshotTest(
`block-beta
`block
columns 5
A space B
A --x B
@@ -347,7 +347,7 @@ describe('Block diagram', () => {
});
it('BL27: block sizes for regular blocks', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
a["A wide one"] b:2 c:2 d
`,
@@ -356,7 +356,7 @@ describe('Block diagram', () => {
});
it('BL28: composite block with a set width - f should use the available space', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
a:3
block:e:3
@@ -370,7 +370,7 @@ describe('Block diagram', () => {
it('BL29: composite block with a set width - f and g should split the available space', () => {
imgSnapshotTest(
`block-beta
`block
columns 3
a:3
block:e:3
@@ -384,4 +384,28 @@ describe('Block diagram', () => {
{}
);
});
it('BL30: block should overflow if too wide for columns', () => {
imgSnapshotTest(
`block-beta
columns 2
fit:2
overflow:3
short:1
also_overflow:2
`,
{}
);
});
it('BL31: edge without arrow syntax should render with no arrowheads', () => {
imgSnapshotTest(
`block-beta
a
b
a --- b
`,
{}
);
});
});

View File

@@ -495,4 +495,34 @@ describe('Class diagram', () => {
cy.get('a').should('have.attr', 'target', '_blank').should('have.attr', 'rel', 'noopener');
});
});
describe('Include char sequence "graph" in text (#6795)', () => {
it('has a label with char sequence "graph"', () => {
imgSnapshotTest(
`
classDiagram
class Person {
+String name
-Int id
#double age
+Text demographicProfile
}
`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});
});
it('should handle backticks for namespace and class names', () => {
imgSnapshotTest(
`
classDiagram
namespace \`A::B\` {
class \`IPC::Sender\`
}
RenderProcessHost --|> \`IPC::Sender\`
`,
{}
);
});
});

View File

@@ -354,4 +354,19 @@ ORDER ||--|{ LINE-ITEM : contains
{ logLevel: 1 }
);
});
describe('Include char sequence "graph" in text (#6795)', () => {
it('has a label with char sequence "graph"', () => {
imgSnapshotTest(
`
erDiagram
p[Photograph] {
varchar(12) jobId
date dateCreated
}
`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});
});
});

View File

@@ -1053,6 +1053,21 @@ flowchart LR
});
});
});
it('6647-elk: should keep node order when using elk layout unless it would add crossings', () => {
imgSnapshotTest(
`---
config:
layout: elk
---
flowchart TB
a --> a1 & a2 & a3 & a4
b --> b1 & b2
b2 --> b3
b1 --> b4
`
);
});
});
describe('Title and arrow styling #4813', () => {

View File

@@ -1113,4 +1113,77 @@ end
);
});
});
describe('Flowchart Node Shape Rendering', () => {
it('should render a stadium-shaped node', () => {
imgSnapshotTest(
`flowchart TB
A(["Start"]) --> n1["Untitled Node"]
A --> n2["Untitled Node"]
`,
{}
);
});
it('should render a diamond-shaped node using shape config', () => {
imgSnapshotTest(
`flowchart BT
n2["Untitled Node"] --> n1["Diamond"]
n1@{ shape: diam}
`,
{}
);
});
it('should render a rounded rectangle and a normal rectangle', () => {
imgSnapshotTest(
`flowchart BT
n2["Untitled Node"] --> n1["Rounded Rectangle"]
n3["Untitled Node"] --> n1
n1@{ shape: rounded}
n3@{ shape: rect}
`,
{}
);
});
});
it('6617: Per Link Curve Styling using edge Ids', () => {
imgSnapshotTest(
`flowchart TD
A e1@-->B e5@--> E
E e7@--> D
B e3@-->D
A e2@-->C e4@-->D
C e6@--> F
F e8@--> D
e1@{ curve: natural }
e2@{ curve: stepAfter }
e3@{ curve: monotoneY }
e4@{ curve: bumpY }
e5@{ curve: linear }
e6@{ curve: catmullRom }
e7@{ curve: cardinal }
`
);
});
describe('when rendering unsuported markdown', () => {
const graph = `flowchart TB
mermaid{"What is\nyourmermaid version?"} --> v10["<11"] --"\`<**1**1\`"--> fine["No bug"]
mermaid --> v11[">= v11"] -- ">= v11" --> broken["Affected by https://github.com/mermaid-js/mermaid/issues/5824"]
subgraph subgraph1["\`How to fix **fix**\`"]
broken --> B["B"]
end
githost["Github, Gitlab, BitBucket, etc."]
githost2["\`Github, Gitlab, BitBucket, etc.\`"]
a["1."]
b["- x"]
`;
it('should render raw strings', () => {
imgSnapshotTest(graph);
});
it('should render raw strings with htmlLabels: false', () => {
imgSnapshotTest(graph, { htmlLabels: false });
});
});
});

View File

@@ -565,6 +565,18 @@ describe('Gantt diagram', () => {
);
});
it('should render only the day when using dateFormat D', () => {
imgSnapshotTest(
`
gantt
title Test
dateFormat D
A :a, 1, 1d
`,
{}
);
});
// TODO: fix it
//
// This test is skipped deliberately
@@ -647,6 +659,49 @@ describe('Gantt diagram', () => {
);
});
it('should render a gantt diagram excluding a specific date in YYYY-MM-DD HH:mm:ss format', () => {
imgSnapshotTest(
`
gantt
dateFormat YYYY-MM-DD HH:mm:ss
excludes 2025-07-07
section Section
A task :a1, 2025-07-04 20:30:30, 2025-07-08 10:30:30
Another task:after a1, 20h
`,
{}
);
});
it('should render a gantt diagram excluding saturday and sunday in YYYY-MM-DD HH:mm:ss format', () => {
imgSnapshotTest(
`
gantt
dateFormat YYYY-MM-DD HH:mm:ss
excludes weekends
weekend saturday
section Section
A task :a1, 2025-07-04 20:30:30, 2025-07-08 10:30:30
Another task:after a1, 20h
`,
{}
);
});
it('should render a gantt diagram excluding friday and saturday in YYYY-MM-DD HH:mm:ss format', () => {
imgSnapshotTest(
`
gantt
dateFormat YYYY-MM-DD HH:mm:ss
excludes weekends
weekend friday
section Section
A task :a1, 2025-07-04 20:30:30, 2025-07-08 10:30:30
Another task:after a1, 20h
`,
{}
);
});
it("should render when there's a semicolon in the title", () => {
imgSnapshotTest(
`

View File

@@ -246,5 +246,22 @@ Word!\`]
);
});
});
describe('Include char sequence "graph" in text (#6795)', () => {
it('has a label with char sequence "graph"', () => {
imgSnapshotTest(
`
mindmap
root
Photograph
Waterfall
Landscape
Geography
Mountains
Rocks
`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});
});
/* The end */
});

View File

@@ -82,4 +82,13 @@ describe('pie chart', () => {
`
);
});
it('should render pie slices only for non-zero values but shows all legends', () => {
imgSnapshotTest(
` pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 1
`
);
});
});

View File

@@ -15,7 +15,7 @@ describe('Sankey Diagram', () => {
describe('when given a linkColor', function () {
this.beforeAll(() => {
cy.wrap(
`sankey-beta
`sankey
a,b,10
`
).as('graph');
@@ -62,7 +62,7 @@ describe('Sankey Diagram', () => {
this.beforeAll(() => {
cy.wrap(
`
sankey-beta
sankey
a,b,8
b,c,8

View File

@@ -602,6 +602,231 @@ State1 --> [*]
--
55
}
`,
{}
);
});
it('should render edge labels correctly', () => {
imgSnapshotTest(
`---
title: On The Way To Something Something DarkSide
config:
look: default
theme: default
---
stateDiagram-v2
state State1_____________
{
c0
}
state State2_____________
{
c1
}
state State3_____________
{
c7
}
state State4_____________
{
c2
}
state State5_____________
{
c3
}
state State6_____________
{
c4
}
state State7_____________
{
c5
}
state State8_____________
{
c6
}
[*] --> State1_____________
State1_____________ --> State2_____________ : Transition1_____
State2_____________ --> State4_____________ : Transition2_____
State2_____________ --> State3_____________ : Transition3_____
State3_____________ --> State2_____________
State4_____________ --> State2_____________ : Transition5_____
State4_____________ --> State5_____________ : Transition6_____
State5_____________ --> State6_____________ : Transition7_____
State6_____________ --> State4_____________ : Transition8_____
State2_____________ --> State7_____________ : Transition4_____
State4_____________ --> State7_____________ : Transition4_____
State5_____________ --> State7_____________ : Transition4_____
State6_____________ --> State7_____________ : Transition4_____
State7_____________ --> State1_____________ : Transition9_____
State5_____________ --> State8_____________ : Transition10____
State8_____________ --> State5_____________ : Transition11____
`,
{}
);
});
it('should render edge labels correctly with multiple transitions', () => {
imgSnapshotTest(
`---
title: Multiple Transitions
config:
look: default
theme: default
---
stateDiagram-v2
state State1_____________
{
c0
}
state State2_____________
{
c1
}
state State3_____________
{
c7
}
state State4_____________
{
c2
}
state State5_____________
{
c3
}
state State6_____________
{
c4
}
state State7_____________
{
c5
}
state State8_____________
{
c6
}
state State9_____________
{
c9
}
[*] --> State1_____________
State1_____________ --> State2_____________ : Transition1_____
State2_____________ --> State4_____________ : Transition2_____
State2_____________ --> State3_____________ : Transition3_____
State3_____________ --> State2_____________
State4_____________ --> State2_____________ : Transition5_____
State4_____________ --> State5_____________ : Transition6_____
State5_____________ --> State6_____________ : Transition7_____
State6_____________ --> State4_____________ : Transition8_____
State2_____________ --> State7_____________ : Transition4_____
State4_____________ --> State7_____________ : Transition4_____
State5_____________ --> State7_____________ : Transition4_____
State6_____________ --> State7_____________ : Transition4_____
State7_____________ --> State1_____________ : Transition9_____
State5_____________ --> State8_____________ : Transition10____
State8_____________ --> State5_____________ : Transition11____
State9_____________ --> State8_____________ : Transition12____
`,
{}
);
});
it('should render edge labels correctly with multiple states', () => {
imgSnapshotTest(
`---
title: Multiple States
config:
look: default
theme: default
---
stateDiagram-v2
state State1_____________
{
c0
}
state State2_____________
{
c1
}
state State3_____________
{
c7
}
state State4_____________
{
c2
}
state State5_____________
{
c3
}
state State6_____________
{
c4
}
state State7_____________
{
c5
}
state State8_____________
{
c6
}
state State9_____________
{
c9
}
state State10_____________
{
c10
}
[*] --> State1_____________
State1_____________ --> State2_____________ : Transition1_____
State2_____________ --> State3_____________ : Transition2_____
State3_____________ --> State4_____________ : Transition3_____
State4_____________ --> State5_____________ : Transition4_____
State5_____________ --> State6_____________ : Transition5_____
State6_____________ --> State7_____________ : Transition6_____
State7_____________ --> State8_____________ : Transition7_____
State8_____________ --> State9_____________ : Transition8_____
State9_____________ --> State10_____________ : Transition9_____
`,
{}
);

View File

@@ -1,7 +1,7 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
describe('XY Chart', () => {
it('should render the simplest possible chart', () => {
it('should render the simplest possible xy-beta chart', () => {
imgSnapshotTest(
`
xychart-beta
@@ -10,10 +10,19 @@ describe('XY Chart', () => {
{}
);
});
it('should render the simplest possible xy chart', () => {
imgSnapshotTest(
`
xychart
line [10, 30, 20]
`,
{}
);
});
it('Should render a complete chart', () => {
imgSnapshotTest(
`
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -26,7 +35,7 @@ describe('XY Chart', () => {
it('Should render a chart without title', () => {
imgSnapshotTest(
`
xychart-beta
xychart
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
@@ -38,7 +47,7 @@ describe('XY Chart', () => {
it('y-axis title not required', () => {
imgSnapshotTest(
`
xychart-beta
xychart
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis 4000 --> 11000
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
@@ -50,7 +59,7 @@ describe('XY Chart', () => {
it('Should render a chart without y-axis with different range', () => {
imgSnapshotTest(
`
xychart-beta
xychart
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
bar [5000, 6000, 7500, 8200, 9500, 10500, 14000, 3200, 9200, 9900, 3400, 6000]
line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800]
@@ -61,7 +70,7 @@ describe('XY Chart', () => {
it('x axis title not required', () => {
imgSnapshotTest(
`
xychart-beta
xychart
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
bar [5000, 6000, 7500, 8200, 9500, 10500, 14000, 3200, 9200, 9900, 3400, 6000]
line [2000, 7000, 6500, 9200, 9500, 7500, 11000, 10200, 3200, 8500, 7000, 8800]
@@ -72,7 +81,7 @@ describe('XY Chart', () => {
it('Multiple plots can be rendered', () => {
imgSnapshotTest(
`
xychart-beta
xychart
line [23, 46, 77, 34]
line [45, 32, 33, 12]
bar [87, 54, 99, 85]
@@ -86,7 +95,7 @@ describe('XY Chart', () => {
it('Decimals and negative numbers are supported', () => {
imgSnapshotTest(
`
xychart-beta
xychart
y-axis -2.4 --> 3.5
line [+1.3, .6, 2.4, -.34]
`,
@@ -104,7 +113,7 @@ describe('XY Chart', () => {
height: 20
plotReservedSpacePercent: 100
---
xychart-beta
xychart
line [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
`,
{}
@@ -130,7 +139,7 @@ describe('XY Chart', () => {
showTick: false
showAxisLine: false
---
xychart-beta
xychart
bar [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
`,
{}
@@ -140,7 +149,7 @@ describe('XY Chart', () => {
imgSnapshotTest(
`
%%{init: {"xyChart": {"width": 1000, "height": 600, "titlePadding": 5, "titleFontSize": 10, "xAxis": {"labelFontSize": "20", "labelPadding": 10, "titleFontSize": 30, "titlePadding": 20, "tickLength": 10, "tickWidth": 5}, "yAxis": {"labelFontSize": "20", "labelPadding": 10, "titleFontSize": 30, "titlePadding": 20, "tickLength": 10, "tickWidth": 5}, "plotBorderWidth": 5, "chartOrientation": "horizontal", "plotReservedSpacePercent": 60 }}}%%
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -181,7 +190,7 @@ describe('XY Chart', () => {
plotReservedSpacePercent: 60
showDataLabel: true
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -202,7 +211,7 @@ describe('XY Chart', () => {
yAxis:
showTitle: false
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -223,7 +232,7 @@ describe('XY Chart', () => {
yAxis:
showLabel: false
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -244,7 +253,7 @@ describe('XY Chart', () => {
yAxis:
showTick: false
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -265,7 +274,7 @@ describe('XY Chart', () => {
yAxis:
showAxisLine: false
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -294,7 +303,7 @@ describe('XY Chart', () => {
xAxisLineColor: "#87ceeb"
plotColorPalette: "#008000, #faba63"
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -307,7 +316,7 @@ describe('XY Chart', () => {
it('should use the correct distances between data points', () => {
imgSnapshotTest(
`
xychart-beta
xychart
x-axis 0 --> 2
line [0, 1, 0, 1]
bar [1, 0, 1, 0]
@@ -325,7 +334,7 @@ describe('XY Chart', () => {
xyChart:
showDataLabel: true
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -344,7 +353,7 @@ describe('XY Chart', () => {
showDataLabel: true
chartOrientation: horizontal
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -357,7 +366,7 @@ describe('XY Chart', () => {
it('should render vertical bar chart without labels by default', () => {
imgSnapshotTest(
`
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -375,7 +384,7 @@ describe('XY Chart', () => {
xyChart:
chartOrientation: horizontal
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -393,7 +402,7 @@ describe('XY Chart', () => {
xyChart:
showDataLabel: true
---
xychart-beta
xychart
title "Multiple Bar Plots"
x-axis Categories [A, B, C]
y-axis "Values" 0 --> 100
@@ -412,7 +421,7 @@ describe('XY Chart', () => {
showDataLabel: true
chartOrientation: horizontal
---
xychart-beta
xychart
title "Multiple Bar Plots"
x-axis Categories [A, B, C]
y-axis "Values" 0 --> 100
@@ -430,7 +439,7 @@ describe('XY Chart', () => {
xyChart:
showDataLabel: true
---
xychart-beta
xychart
title "Single Bar Chart"
x-axis Categories [A]
y-axis "Value" 0 --> 100
@@ -449,7 +458,7 @@ describe('XY Chart', () => {
showDataLabel: true
chartOrientation: horizontal
---
xychart-beta
xychart
title "Single Bar Chart"
x-axis Categories [A]
y-axis "Value" 0 --> 100
@@ -467,7 +476,7 @@ describe('XY Chart', () => {
xyChart:
showDataLabel: true
---
xychart-beta
xychart
title "Decimal and Negative Values"
x-axis Categories [A, B, C]
y-axis -10 --> 10
@@ -486,7 +495,7 @@ describe('XY Chart', () => {
showDataLabel: true
chartOrientation: horizontal
---
xychart-beta
xychart
title "Decimal and Negative Values"
x-axis Categories [A, B, C]
y-axis -10 --> 10
@@ -504,7 +513,7 @@ describe('XY Chart', () => {
xyChart:
showDataLabel: true
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan,b,c]
y-axis "Revenue (in $)" 4000 --> 12000
@@ -561,7 +570,7 @@ describe('XY Chart', () => {
showDataLabel: true
chartOrientation: horizontal
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan,b,c]
y-axis "Revenue (in $)" 4000 --> 12000
@@ -615,7 +624,7 @@ describe('XY Chart', () => {
xyChart:
showDataLabel: true
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s]
y-axis "Revenue (in $)" 4000 --> 12000
@@ -672,7 +681,7 @@ describe('XY Chart', () => {
showDataLabel: true
chartOrientation: horizontal
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s]
y-axis "Revenue (in $)" 4000 --> 12000
@@ -726,7 +735,7 @@ describe('XY Chart', () => {
xyChart:
showDataLabel: true
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan]
y-axis "Revenue (in $)" 3000 --> 12000
@@ -783,7 +792,7 @@ describe('XY Chart', () => {
showDataLabel: true
chartOrientation: horizontal
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan]
y-axis "Revenue (in $)" 3000 --> 12000

View File

@@ -0,0 +1,35 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<style>
div.mermaid {
font-family: 'Courier New', Courier, monospace !important;
}
</style>
</head>
<body>
<h1>Pie chart demos</h1>
<pre class="mermaid">
pie title Default text position: Animal adoption
accTitle: simple pie char demo
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
"dogs" : -60.67
"rats" : 40.12
</pre>
<hr />
<script type="module">
import mermaid from '/mermaid.esm.mjs';
mermaid.initialize({
theme: 'forest',
logLevel: 3,
securityLevel: 'loose',
});
</script>
</body>
</html>

View File

@@ -131,6 +131,22 @@
<body>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
elk:
mergeEdges: false
forceNodeModelOrder: false
considerModelOrder: NONE
---
flowchart TB
a --> a1 & a2 & a3 & a4
b --> b1 & b2
b2 --> b3
b1 --> b4</pre
>
<pre id="diagram4" class="mermaid">
treemap
"Section 1"
"Leaf 1.1": 12

View File

@@ -41,10 +41,6 @@ graph TB
const { svg } = await mermaid.render('d22', value);
console.log(svg);
el.innerHTML = svg;
// mermaid.test1('first_slow', 1200).then((r) => console.info(r));
// mermaid.test1('second_fast', 200).then((r) => console.info(r));
// mermaid.test1('third_fast', 200).then((r) => console.info(r));
// mermaid.test1('forth_slow', 1200).then((r) => console.info(r));
</script>
</body>
</html>

View File

@@ -182,7 +182,7 @@ const contentLoadedApi = async function () {
for (let i = 0; i < numCodes; i++) {
const { svg, bindFunctions } = await mermaid.render('newid' + i, graphObj.code[i], divs[i]);
div.innerHTML = svg;
bindFunctions(div);
bindFunctions?.(div);
}
} else {
const div = document.createElement('div');
@@ -194,7 +194,7 @@ const contentLoadedApi = async function () {
const { svg, bindFunctions } = await mermaid.render('newid', graphObj.code, div);
div.innerHTML = svg;
console.log(div.innerHTML);
bindFunctions(div);
bindFunctions?.(div);
}
}
};

View File

@@ -2,219 +2,219 @@
"durations": [
{
"spec": "cypress/integration/other/configuration.spec.js",
"duration": 5672
"duration": 6297
},
{
"spec": "cypress/integration/other/external-diagrams.spec.js",
"duration": 1990
"duration": 2187
},
{
"spec": "cypress/integration/other/ghsa.spec.js",
"duration": 3186
"duration": 3509
},
{
"spec": "cypress/integration/other/iife.spec.js",
"duration": 1948
"duration": 2218
},
{
"spec": "cypress/integration/other/interaction.spec.js",
"duration": 11938
"duration": 12104
},
{
"spec": "cypress/integration/other/rerender.spec.js",
"duration": 1932
"duration": 2151
},
{
"spec": "cypress/integration/other/xss.spec.js",
"duration": 27237
"duration": 33064
},
{
"spec": "cypress/integration/rendering/appli.spec.js",
"duration": 3170
"duration": 3488
},
{
"spec": "cypress/integration/rendering/architecture.spec.ts",
"duration": 104
"duration": 106
},
{
"spec": "cypress/integration/rendering/block.spec.js",
"duration": 17390
"duration": 18317
},
{
"spec": "cypress/integration/rendering/c4.spec.js",
"duration": 5296
"duration": 5592
},
{
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
"duration": 39004
"duration": 39358
},
{
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
"duration": 37653
"duration": 37160
},
{
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
"duration": 23278
"duration": 23660
},
{
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
"duration": 36645
"duration": 36866
},
{
"spec": "cypress/integration/rendering/classDiagram.spec.js",
"duration": 15418
"duration": 17334
},
{
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
"duration": 9684
"duration": 9871
},
{
"spec": "cypress/integration/rendering/current.spec.js",
"duration": 2570
"duration": 2833
},
{
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
"duration": 84687
"duration": 85321
},
{
"spec": "cypress/integration/rendering/erDiagram.spec.js",
"duration": 14819
"duration": 15673
},
{
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
"duration": 3371
"duration": 3724
},
{
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
"duration": 39925
"duration": 41178
},
{
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
"duration": 34694
"duration": 29966
},
{
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
"duration": 7137
"duration": 7689
},
{
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
"duration": 24740
"duration": 24709
},
{
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
"duration": 42077
"duration": 45565
},
{
"spec": "cypress/integration/rendering/flowchart.spec.js",
"duration": 30642
"duration": 31144
},
{
"spec": "cypress/integration/rendering/gantt.spec.js",
"duration": 18085
"duration": 20808
},
{
"spec": "cypress/integration/rendering/gitGraph.spec.js",
"duration": 50107
"duration": 49985
},
{
"spec": "cypress/integration/rendering/iconShape.spec.ts",
"duration": 276279
"duration": 273272
},
{
"spec": "cypress/integration/rendering/imageShape.spec.ts",
"duration": 56505
"duration": 55880
},
{
"spec": "cypress/integration/rendering/info.spec.ts",
"duration": 3036
"duration": 3271
},
{
"spec": "cypress/integration/rendering/journey.spec.js",
"duration": 6889
"duration": 7293
},
{
"spec": "cypress/integration/rendering/kanban.spec.ts",
"duration": 7353
"duration": 7861
},
{
"spec": "cypress/integration/rendering/katex.spec.js",
"duration": 3580
"duration": 3922
},
{
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
"duration": 2508
"duration": 2726
},
{
"spec": "cypress/integration/rendering/mindmap.spec.ts",
"duration": 10939
"duration": 11670
},
{
"spec": "cypress/integration/rendering/newShapes.spec.ts",
"duration": 149102
"duration": 146020
},
{
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
"duration": 113987
"duration": 114244
},
{
"spec": "cypress/integration/rendering/packet.spec.ts",
"duration": 4060
"duration": 5036
},
{
"spec": "cypress/integration/rendering/pie.spec.ts",
"duration": 5715
"duration": 6545
},
{
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
"duration": 8945
"duration": 9097
},
{
"spec": "cypress/integration/rendering/radar.spec.js",
"duration": 5337
"duration": 5676
},
{
"spec": "cypress/integration/rendering/requirement.spec.js",
"duration": 2643
"duration": 2795
},
{
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
"duration": 52072
"duration": 51660
},
{
"spec": "cypress/integration/rendering/sankey.spec.ts",
"duration": 6692
"duration": 6957
},
{
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
"duration": 35721
"duration": 36026
},
{
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
"duration": 26030
"duration": 29551
},
{
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
"duration": 16333
"duration": 17364
},
{
"spec": "cypress/integration/rendering/theme.spec.js",
"duration": 29287
"duration": 30209
},
{
"spec": "cypress/integration/rendering/timeline.spec.ts",
"duration": 8491
"duration": 8699
},
{
"spec": "cypress/integration/rendering/treemap.spec.ts",
"duration": 12291
"duration": 12168
},
{
"spec": "cypress/integration/rendering/xyChart.spec.js",
"duration": 20651
"duration": 21453
},
{
"spec": "cypress/integration/rendering/zenuml.spec.js",
"duration": 3218
"duration": 3577
}
]
}

View File

@@ -10,7 +10,7 @@
<body>
<h1>Block diagram demos</h1>
<pre id="diagram" class="mermaid">
block-beta
block
columns 1
db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -26,7 +26,7 @@ columns 1
style B fill:#f9F,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
A1["square"]
B1("rounded")
C1(("circle"))
@@ -36,7 +36,7 @@ block-beta
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
A1(["stadium"])
A2[["subroutine"]]
B1[("cylinder")]
@@ -48,7 +48,7 @@ block-beta
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
block:e:4
columns 2
f
@@ -57,7 +57,7 @@ block-beta
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
block:e:4
columns 2
f
@@ -67,7 +67,7 @@ block-beta
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
columns 3
a:3
block:e:3
@@ -80,7 +80,7 @@ block-beta
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
columns 4
a b c d
block:e:4
@@ -97,19 +97,19 @@ flowchart LR
X-- "a label" -->z
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
columns 5
A space B
A --x B
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
columns 3
a["A wide one"] b:2 c:2 d
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
columns 3
a b c
e:3
@@ -117,7 +117,7 @@ columns 3
</pre>
<pre id="diagram" class="mermaid">
block-beta
block
A1:3
A2:1

View File

@@ -20,12 +20,14 @@
width: 800
nodeAlignment: left
---
sankey-beta
Revenue,Expenses,10
Revenue,Profit,10
Expenses,Manufacturing,5
Expenses,Tax,3
Expenses,Research,2
sankey
a,b,8
b,c,8
c,d,8
d,e,8
x,c,4
c,y,4
</pre>
<h2>Energy flow</h2>
@@ -40,7 +42,7 @@
linkColor: gradient
nodeAlignment: justify
---
sankey-beta
sankey
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597

View File

@@ -16,7 +16,7 @@
<body>
<h1>XY Charts demos</h1>
<pre class="mermaid">
xychart-beta
xychart
title "Sales Revenue (in $)"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -26,7 +26,7 @@
<hr />
<h1>XY Charts horizontal</h1>
<pre class="mermaid">
xychart-beta horizontal
xychart horizontal
title "Basic xychart"
x-axis "this is x axis" [category1, "category 2", category3, category4]
y-axis yaxisText 10 --> 150
@@ -36,7 +36,7 @@
<hr />
<h1>XY Charts only lines and bar</h1>
<pre class="mermaid">
xychart-beta
xychart
line [23, 46, 77, 34]
line [45, 32, 33, 12]
line [87, 54, 99, 85]
@@ -48,13 +48,13 @@
<hr />
<h1>XY Charts with +ve and -ve numbers</h1>
<pre class="mermaid">
xychart-beta
xychart
line [+1.3, .6, 2.4, -.34]
</pre>
<h1>XY Charts Bar with multiple category</h1>
<pre class="mermaid">
xychart-beta
xychart
title "Basic xychart with many categories"
x-axis "this is x axis" [category1, "category 2", category3, category4, category5, category6, category7]
y-axis yaxisText 10 --> 150
@@ -63,7 +63,7 @@
<h1>XY Charts line with multiple category</h1>
<pre class="mermaid">
xychart-beta
xychart
title "Line chart with many category"
x-axis "this is x axis" [category1, "category 2", category3, category4, category5, category6, category7]
y-axis yaxisText 10 --> 150
@@ -72,7 +72,7 @@
<h1>XY Charts category with large text</h1>
<pre class="mermaid">
xychart-beta
xychart
title "Basic xychart with many categories with category overlap"
x-axis "this is x axis" [category1, "Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", category3, category4, category5, category6, category7]
y-axis yaxisText 10 --> 150
@@ -89,7 +89,7 @@ config:
height: 20
plotReservedSpacePercent: 100
---
xychart-beta
xychart
line [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
</pre>
@@ -103,7 +103,7 @@ config:
height: 20
plotReservedSpacePercent: 100
---
xychart-beta
xychart
bar [5000, 9000, 7500, 6200, 9500, 5500, 11000, 8200, 9200, 9500, 7000, 8800]
</pre>
@@ -136,7 +136,7 @@ config:
chartOrientation: horizontal
plotReservedSpacePercent: 60
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -162,7 +162,7 @@ config:
xAxisLineColor: "#87ceeb"
plotColorPalette: "#008000, #faba63"
---
xychart-beta
xychart
title "Sales Revenue"
x-axis Months [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000

View File

@@ -12,4 +12,4 @@
> `const` **configKeys**: `Set`<`string`>
Defined in: [packages/mermaid/src/defaultConfig.ts:290](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L290)
Defined in: [packages/mermaid/src/defaultConfig.ts:292](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L292)

View File

@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/config.type.ts:58](https://github.com/mermaid-
> `optional` **altFontFamily**: `string`
Defined in: [packages/mermaid/src/config.type.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L122)
Defined in: [packages/mermaid/src/config.type.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L132)
---
@@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/config.type.ts:122](https://github.com/mermaid
> `optional` **architecture**: `ArchitectureDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
Defined in: [packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
---
@@ -34,7 +34,7 @@ Defined in: [packages/mermaid/src/config.type.ts:194](https://github.com/mermaid
> `optional` **arrowMarkerAbsolute**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:141](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L141)
Defined in: [packages/mermaid/src/config.type.ts:151](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L151)
Controls whether or arrow markers in html code are absolute paths or anchors.
This matters if you are using base tag settings.
@@ -45,7 +45,7 @@ This matters if you are using base tag settings.
> `optional` **block**: `BlockDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
Defined in: [packages/mermaid/src/config.type.ts:211](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L211)
---
@@ -53,7 +53,7 @@ Defined in: [packages/mermaid/src/config.type.ts:201](https://github.com/mermaid
> `optional` **c4**: `C4DiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
Defined in: [packages/mermaid/src/config.type.ts:208](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L208)
---
@@ -61,7 +61,7 @@ Defined in: [packages/mermaid/src/config.type.ts:198](https://github.com/mermaid
> `optional` **class**: `ClassDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187)
Defined in: [packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
---
@@ -69,7 +69,7 @@ Defined in: [packages/mermaid/src/config.type.ts:187](https://github.com/mermaid
> `optional` **darkMode**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L113)
Defined in: [packages/mermaid/src/config.type.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L123)
---
@@ -77,7 +77,7 @@ Defined in: [packages/mermaid/src/config.type.ts:113](https://github.com/mermaid
> `optional` **deterministicIds**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
Defined in: [packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
This option controls if the generated ids of nodes in the SVG are
generated randomly or based on a seed.
@@ -93,7 +93,7 @@ should not change unless content is changed.
> `optional` **deterministicIDSeed**: `string`
Defined in: [packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181)
Defined in: [packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
This option is the optional seed for deterministic ids.
If set to `undefined` but deterministicIds is `true`, a simple number iterator is used.
@@ -105,7 +105,7 @@ You can set this attribute to base the seed on a static string.
> `optional` **dompurifyConfig**: `Config`
Defined in: [packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
Defined in: [packages/mermaid/src/config.type.ts:213](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L213)
---
@@ -115,12 +115,24 @@ Defined in: [packages/mermaid/src/config.type.ts:203](https://github.com/mermaid
Defined in: [packages/mermaid/src/config.type.ts:91](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L91)
#### considerModelOrder?
> `optional` **considerModelOrder**: `"NONE"` | `"NODES_AND_EDGES"` | `"PREFER_EDGES"` | `"PREFER_NODES"`
Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting.
#### cycleBreakingStrategy?
> `optional` **cycleBreakingStrategy**: `"GREEDY"` | `"DEPTH_FIRST"` | `"INTERACTIVE"` | `"MODEL_ORDER"` | `"GREEDY_MODEL_ORDER"`
This strategy decides how to find cycles in the graph and deciding which edges need adjustment to break loops.
#### forceNodeModelOrder?
> `optional` **forceNodeModelOrder**: `boolean`
The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES.
#### mergeEdges?
> `optional` **mergeEdges**: `boolean`
@@ -139,7 +151,7 @@ Elk specific option affecting how nodes are placed.
> `optional` **er**: `ErDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189)
Defined in: [packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
---
@@ -147,7 +159,7 @@ Defined in: [packages/mermaid/src/config.type.ts:189](https://github.com/mermaid
> `optional` **flowchart**: `FlowchartDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182)
Defined in: [packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
---
@@ -155,7 +167,7 @@ Defined in: [packages/mermaid/src/config.type.ts:182](https://github.com/mermaid
> `optional` **fontFamily**: `string`
Defined in: [packages/mermaid/src/config.type.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L121)
Defined in: [packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
Specifies the font to be used in the rendered diagrams.
Can be any possible CSS `font-family`.
@@ -167,7 +179,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
> `optional` **fontSize**: `number`
Defined in: [packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
Defined in: [packages/mermaid/src/config.type.ts:215](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L215)
---
@@ -175,7 +187,7 @@ Defined in: [packages/mermaid/src/config.type.ts:205](https://github.com/mermaid
> `optional` **forceLegacyMathML**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L163)
Defined in: [packages/mermaid/src/config.type.ts:173](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L173)
This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS
fonts and browser's MathML implementation, this option is recommended if consistent rendering is important.
@@ -187,7 +199,7 @@ If set to true, ignores legacyMathML.
> `optional` **gantt**: `GanttDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
Defined in: [packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
---
@@ -195,7 +207,7 @@ Defined in: [packages/mermaid/src/config.type.ts:184](https://github.com/mermaid
> `optional` **gitGraph**: `GitGraphDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
Defined in: [packages/mermaid/src/config.type.ts:207](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L207)
---
@@ -213,7 +225,7 @@ Defines the seed to be used when using handDrawn look. This is important for the
> `optional` **htmlLabels**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L114)
Defined in: [packages/mermaid/src/config.type.ts:124](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L124)
---
@@ -221,7 +233,7 @@ Defined in: [packages/mermaid/src/config.type.ts:114](https://github.com/mermaid
> `optional` **journey**: `JourneyDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
Defined in: [packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
---
@@ -229,7 +241,7 @@ Defined in: [packages/mermaid/src/config.type.ts:185](https://github.com/mermaid
> `optional` **kanban**: `KanbanDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
Defined in: [packages/mermaid/src/config.type.ts:206](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L206)
---
@@ -247,7 +259,7 @@ Defines which layout algorithm to use for rendering the diagram.
> `optional` **legacyMathML**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:156](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L156)
Defined in: [packages/mermaid/src/config.type.ts:166](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L166)
This option specifies if Mermaid can expect the dependent to include KaTeX stylesheets for browsers
without their own MathML implementation. If this option is disabled and MathML is not supported, the math
@@ -260,7 +272,7 @@ fall back to legacy rendering for KaTeX.
> `optional` **logLevel**: `0` | `2` | `1` | `"trace"` | `"debug"` | `"info"` | `"warn"` | `"error"` | `"fatal"` | `3` | `4` | `5`
Defined in: [packages/mermaid/src/config.type.ts:127](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L127)
Defined in: [packages/mermaid/src/config.type.ts:137](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L137)
This option decides the amount of logging to be used by mermaid.
@@ -280,7 +292,7 @@ Defines which main look to use for the diagram.
> `optional` **markdownAutoWrap**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:206](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L206)
Defined in: [packages/mermaid/src/config.type.ts:216](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L216)
---
@@ -308,7 +320,7 @@ The maximum allowed size of the users text diagram
> `optional` **mindmap**: `MindmapDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
Defined in: [packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
---
@@ -316,7 +328,7 @@ Defined in: [packages/mermaid/src/config.type.ts:195](https://github.com/mermaid
> `optional` **packet**: `PacketDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
Defined in: [packages/mermaid/src/config.type.ts:210](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L210)
---
@@ -324,7 +336,7 @@ Defined in: [packages/mermaid/src/config.type.ts:200](https://github.com/mermaid
> `optional` **pie**: `PieDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
Defined in: [packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
---
@@ -332,7 +344,7 @@ Defined in: [packages/mermaid/src/config.type.ts:190](https://github.com/mermaid
> `optional` **quadrantChart**: `QuadrantChartConfig`
Defined in: [packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
Defined in: [packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
---
@@ -340,7 +352,7 @@ Defined in: [packages/mermaid/src/config.type.ts:191](https://github.com/mermaid
> `optional` **radar**: `RadarDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
Defined in: [packages/mermaid/src/config.type.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L212)
---
@@ -348,7 +360,7 @@ Defined in: [packages/mermaid/src/config.type.ts:202](https://github.com/mermaid
> `optional` **requirement**: `RequirementDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
Defined in: [packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
---
@@ -356,7 +368,7 @@ Defined in: [packages/mermaid/src/config.type.ts:193](https://github.com/mermaid
> `optional` **sankey**: `SankeyDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
Defined in: [packages/mermaid/src/config.type.ts:209](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L209)
---
@@ -364,7 +376,7 @@ Defined in: [packages/mermaid/src/config.type.ts:199](https://github.com/mermaid
> `optional` **secure**: `string`\[]
Defined in: [packages/mermaid/src/config.type.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L148)
Defined in: [packages/mermaid/src/config.type.ts:158](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L158)
This option controls which `currentConfig` keys are considered secure and
can only be changed via call to `mermaid.initialize`.
@@ -376,7 +388,7 @@ This prevents malicious graph directives from overriding a site's default securi
> `optional` **securityLevel**: `"strict"` | `"loose"` | `"antiscript"` | `"sandbox"`
Defined in: [packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
Defined in: [packages/mermaid/src/config.type.ts:141](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L141)
Level of trust for parsed diagram
@@ -386,7 +398,7 @@ Level of trust for parsed diagram
> `optional` **sequence**: `SequenceDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
Defined in: [packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
---
@@ -394,7 +406,7 @@ Defined in: [packages/mermaid/src/config.type.ts:183](https://github.com/mermaid
> `optional` **startOnLoad**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:135](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L135)
Defined in: [packages/mermaid/src/config.type.ts:145](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L145)
Dictates whether mermaid starts on Page load
@@ -404,7 +416,7 @@ Dictates whether mermaid starts on Page load
> `optional` **state**: `StateDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188)
Defined in: [packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
---
@@ -412,7 +424,7 @@ Defined in: [packages/mermaid/src/config.type.ts:188](https://github.com/mermaid
> `optional` **suppressErrorRendering**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L212)
Defined in: [packages/mermaid/src/config.type.ts:222](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L222)
Suppresses inserting 'Syntax error' diagram in the DOM.
This is useful when you want to control how to handle syntax errors in your application.
@@ -450,7 +462,7 @@ Defined in: [packages/mermaid/src/config.type.ts:65](https://github.com/mermaid-
> `optional` **timeline**: `TimelineDiagramConfig`
Defined in: [packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186)
Defined in: [packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
---
@@ -458,7 +470,7 @@ Defined in: [packages/mermaid/src/config.type.ts:186](https://github.com/mermaid
> `optional` **wrap**: `boolean`
Defined in: [packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
Defined in: [packages/mermaid/src/config.type.ts:214](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L214)
---
@@ -466,4 +478,4 @@ Defined in: [packages/mermaid/src/config.type.ts:204](https://github.com/mermaid
> `optional` **xyChart**: `XYChartConfig`
Defined in: [packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
Defined in: [packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)

View File

@@ -10,7 +10,7 @@
# Interface: ParseOptions
Defined in: [packages/mermaid/src/types.ts:59](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L59)
Defined in: [packages/mermaid/src/types.ts:72](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L72)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:59](https://github.com/mermaid-js/mer
> `optional` **suppressErrors**: `boolean`
Defined in: [packages/mermaid/src/types.ts:64](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L64)
Defined in: [packages/mermaid/src/types.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L77)
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
The `parseError` function will not be called.

View File

@@ -10,7 +10,7 @@
# Interface: ParseResult
Defined in: [packages/mermaid/src/types.ts:67](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L67)
Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L80)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:67](https://github.com/mermaid-js/mer
> **config**: [`MermaidConfig`](MermaidConfig.md)
Defined in: [packages/mermaid/src/types.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L75)
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
The config passed as YAML frontmatter or directives
@@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives
> **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:71](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L71)
Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
The diagram type, e.g. 'flowchart', 'sequence', etc.

View File

@@ -10,7 +10,7 @@
# Interface: RenderResult
Defined in: [packages/mermaid/src/types.ts:85](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L85)
Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:85](https://github.com/mermaid-js/mer
> `optional` **bindFunctions**: (`element`) => `void`
Defined in: [packages/mermaid/src/types.ts:103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L103)
Defined in: [packages/mermaid/src/types.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L116)
Bind function to be called after the svg has been inserted into the DOM.
This is necessary for adding event listeners to the elements in the svg.
@@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
> **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:93](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L93)
Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106)
The diagram type, e.g. 'flowchart', 'sequence', etc.
@@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
> **svg**: `string`
Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102)
The svg code for the rendered graph.

View File

@@ -0,0 +1,189 @@
---
references:
- "File: /packages/mermaid/src/diagrams/flowchart/flowDiagram.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowDb.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowDetector.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/styles.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/types.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowChartShapes.js"
- "File: /packages/mermaid/src/diagrams/flowchart/parser/flowParser.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/elk/detector.ts"
generationTime: 2025-07-23T10:31:53.266Z
---
flowchart TD
%% Entry Points and Detection
Input["User Input Text"] --> Detection{Detection Phase}
Detection --> flowDetector["flowDetector.ts<br/>detector(txt, config)"]
Detection --> flowDetectorV2["flowDetector-v2.ts<br/>detector(txt, config)"]
Detection --> elkDetector["elk/detector.ts<br/>detector(txt, config)"]
flowDetector --> |"Checks /^\s*graph/"| DetectLegacy{Legacy Flowchart?}
flowDetectorV2 --> |"Checks /^\s*flowchart/"| DetectNew{New Flowchart?}
elkDetector --> |"Checks /^\s*flowchart-elk/"| DetectElk{ELK Layout?}
DetectLegacy --> |Yes| LoadDiagram
DetectNew --> |Yes| LoadDiagram
DetectElk --> |Yes| LoadDiagram
%% Loading Phase
LoadDiagram["loader() function"] --> flowDiagram["flowDiagram.ts<br/>diagram object"]
flowDiagram --> DiagramStructure{Diagram Components}
DiagramStructure --> Parser["parser: flowParser"]
DiagramStructure --> Database["db: new FlowDB()"]
DiagramStructure --> Renderer["renderer: flowRenderer-v3-unified"]
DiagramStructure --> Styles["styles: flowStyles"]
DiagramStructure --> Init["init: (cnf: MermaidConfig)"]
%% Parser Phase
Parser --> flowParser["parser/flowParser.ts<br/>newParser.parse(src)"]
flowParser --> |"Preprocesses src"| RemoveWhitespace["Remove trailing whitespace<br/>src.replace(/}\s*\n/g, '}\n')"]
RemoveWhitespace --> flowJison["parser/flow.jison<br/>flowJisonParser.parse(newSrc)"]
flowJison --> ParseGraph["Parse Graph Structure"]
ParseGraph --> ParseVertices["Parse Vertices"]
ParseGraph --> ParseEdges["Parse Edges"]
ParseGraph --> ParseSubgraphs["Parse Subgraphs"]
ParseGraph --> ParseClasses["Parse Classes"]
ParseGraph --> ParseStyles["Parse Styles"]
%% Database Phase - FlowDB Class
Database --> FlowDBClass["flowDb.ts<br/>FlowDB class"]
FlowDBClass --> DBInit["constructor()<br/>- Initialize counters<br/>- Bind methods<br/>- Setup toolTips<br/>- Call clear()"]
DBInit --> DBMethods{FlowDB Methods}
DBMethods --> addVertex["addVertex(id, textObj, type, style,<br/>classes, dir, props, metadata)"]
DBMethods --> addLink["addLink(_start[], _end[], linkData)"]
DBMethods --> addSingleLink["addSingleLink(_start, _end, type, id)"]
DBMethods --> setDirection["setDirection(dir)"]
DBMethods --> addSubGraph["addSubGraph(nodes[], id, title)"]
DBMethods --> addClass["addClass(id, style)"]
DBMethods --> setClass["setClass(ids, className)"]
DBMethods --> setTooltip["setTooltip(ids, tooltip)"]
DBMethods --> setClickEvent["setClickEvent(id, functionName, args)"]
DBMethods --> setClickFun["setClickFun(id, functionName, args)"]
%% Vertex Processing
addVertex --> VertexProcess{Vertex Processing}
VertexProcess --> CreateVertex["Create FlowVertex object<br/>- id, labelType, domId<br/>- styles[], classes[]"]
VertexProcess --> SanitizeText["sanitizeText(textObj.text)"]
VertexProcess --> ParseMetadata["Parse YAML metadata<br/>yaml.load(yamlData)"]
VertexProcess --> SetVertexProps["Set vertex properties<br/>- shape, label, icon, form<br/>- pos, img, constraint, w, h"]
%% Edge Processing
addSingleLink --> EdgeProcess{Edge Processing}
EdgeProcess --> CreateEdge["Create FlowEdge object<br/>- start, end, type, text<br/>- labelType, classes[]"]
EdgeProcess --> ProcessLinkText["Process link text<br/>- sanitizeText()<br/>- strip quotes"]
EdgeProcess --> SetEdgeProps["Set edge properties<br/>- type, stroke, length"]
EdgeProcess --> GenerateEdgeId["Generate edge ID<br/>getEdgeId(start, end, counter)"]
EdgeProcess --> ValidateEdgeLimit["Validate edge limit<br/>maxEdges check"]
%% Data Collection
DBMethods --> GetData["getData()"]
GetData --> CollectNodes["Collect nodes[] from vertices"]
GetData --> CollectEdges["Collect edges[] from edges"]
GetData --> ProcessSubGraphs["Process subgraphs<br/>- parentDB Map<br/>- subGraphDB Map"]
GetData --> AddNodeFromVertex["addNodeFromVertex()<br/>for each vertex"]
GetData --> ProcessEdgeTypes["destructEdgeType()<br/>arrowTypeStart, arrowTypeEnd"]
%% Node Creation
AddNodeFromVertex --> NodeCreation{Node Creation}
NodeCreation --> FindExistingNode["findNode(nodes, vertex.id)"]
NodeCreation --> CreateBaseNode["Create base node<br/>- id, label, parentId<br/>- cssStyles, cssClasses<br/>- shape, domId, tooltip"]
NodeCreation --> GetCompiledStyles["getCompiledStyles(classDefs)"]
NodeCreation --> GetTypeFromVertex["getTypeFromVertex(vertex)"]
%% Rendering Phase
Renderer --> flowRendererV3["flowRenderer-v3-unified.ts<br/>draw(text, id, version, diag)"]
flowRendererV3 --> RenderInit["Initialize rendering<br/>- getConfig()<br/>- handle securityLevel<br/>- getDiagramElement()"]
RenderInit --> GetLayoutData["diag.db.getData()<br/>as LayoutData"]
GetLayoutData --> SetupLayoutData["Setup layout data<br/>- type, layoutAlgorithm<br/>- direction, spacing<br/>- markers, diagramId"]
SetupLayoutData --> CallRender["render(data4Layout, svg)"]
CallRender --> SetupViewPort["setupViewPortForSVG(svg, padding)"]
SetupViewPort --> ProcessLinks["Process vertex links<br/>- create anchor elements<br/>- handle click events"]
%% Shape Rendering
CallRender --> ShapeSystem["flowChartShapes.js<br/>Shape Functions"]
ShapeSystem --> ShapeFunctions{Shape Functions}
ShapeFunctions --> question["question(parent, bbox, node)"]
ShapeFunctions --> hexagon["hexagon(parent, bbox, node)"]
ShapeFunctions --> rect_left_inv_arrow["rect_left_inv_arrow(parent, bbox, node)"]
ShapeFunctions --> lean_right["lean_right(parent, bbox, node)"]
ShapeFunctions --> lean_left["lean_left(parent, bbox, node)"]
ShapeFunctions --> insertPolygonShape["insertPolygonShape(parent, w, h, points)"]
ShapeFunctions --> intersectPolygon["intersectPolygon(node, points, point)"]
ShapeFunctions --> intersectRect["intersectRect(node, point)"]
%% Styling System
Styles --> stylesTS["styles.ts<br/>getStyles(options)"]
stylesTS --> StyleOptions["FlowChartStyleOptions<br/>- arrowheadColor, border2<br/>- clusterBkg, mainBkg<br/>- fontFamily, textColor"]
StyleOptions --> GenerateCSS["Generate CSS styles<br/>- .label, .cluster-label<br/>- .node, .edgePath<br/>- .flowchart-link, .edgeLabel"]
GenerateCSS --> GetIconStyles["getIconStyles()"]
%% Type System
Parser --> TypeSystem["types.ts<br/>Type Definitions"]
TypeSystem --> FlowVertex["FlowVertex interface"]
TypeSystem --> FlowEdge["FlowEdge interface"]
TypeSystem --> FlowClass["FlowClass interface"]
TypeSystem --> FlowSubGraph["FlowSubGraph interface"]
TypeSystem --> FlowVertexTypeParam["FlowVertexTypeParam<br/>Shape types"]
%% Utility Functions
DBMethods --> UtilityFunctions{Utility Functions}
UtilityFunctions --> lookUpDomId["lookUpDomId(id)"]
UtilityFunctions --> getClasses["getClasses()"]
UtilityFunctions --> getDirection["getDirection()"]
UtilityFunctions --> getVertices["getVertices()"]
UtilityFunctions --> getEdges["getEdges()"]
UtilityFunctions --> getSubGraphs["getSubGraphs()"]
UtilityFunctions --> clear["clear()"]
UtilityFunctions --> defaultConfig["defaultConfig()"]
%% Event Handling
ProcessLinks --> EventHandling{Event Handling}
EventHandling --> setupToolTips["setupToolTips(element)"]
EventHandling --> bindFunctions["bindFunctions(element)"]
EventHandling --> runFunc["utils.runFunc(functionName, args)"]
%% Common Database Functions
DBMethods --> CommonDB["commonDb.js functions"]
CommonDB --> setAccTitle["setAccTitle()"]
CommonDB --> getAccTitle["getAccTitle()"]
CommonDB --> setAccDescription["setAccDescription()"]
CommonDB --> getAccDescription["getAccDescription()"]
CommonDB --> setDiagramTitle["setDiagramTitle()"]
CommonDB --> getDiagramTitle["getDiagramTitle()"]
CommonDB --> commonClear["clear()"]
%% Final Output
ProcessLinks --> FinalSVG["Final SVG Output"]
%% Layout Algorithm Selection
SetupLayoutData --> LayoutAlgorithm{Layout Algorithm}
LayoutAlgorithm --> Dagre["dagre<br/>(default)"]
LayoutAlgorithm --> DagreWrapper["dagre-wrapper<br/>(v2 renderer)"]
LayoutAlgorithm --> ELK["elk<br/>(external package)"]
%% Testing Components
FlowDBClass --> TestFiles["Test Files"]
TestFiles --> flowDbSpec["flowDb.spec.ts"]
TestFiles --> flowChartShapesSpec["flowChartShapes.spec.js"]
TestFiles --> ParserTests["parser/*.spec.js files<br/>- flow-text.spec.js<br/>- flow-edges.spec.js<br/>- flow-style.spec.js<br/>- subgraph.spec.js"]
%% Configuration
Init --> ConfigSetup["Configuration Setup"]
ConfigSetup --> FlowchartConfig["cnf.flowchart config"]
ConfigSetup --> ArrowMarkers["arrowMarkerAbsolute"]
ConfigSetup --> LayoutConfig["layout config"]
ConfigSetup --> SetConfig["setConfig() calls"]

View File

@@ -0,0 +1,307 @@
---
references:
- "File: /packages/mermaid/src/mermaidAPI.ts"
- "File: /packages/mermaid/src/mermaid.ts"
generationTime: 2025-01-28T16:30:00.000Z
---
sequenceDiagram
participant User as User/Browser
participant Mermaid as mermaid.ts
participant Queue as executionQueue
participant API as mermaidAPI.ts
participant Config as configApi
participant Preprocessor as preprocessDiagram
participant DiagramAPI as diagram-api
participant Diagram as Diagram.fromText
participant Renderer as diagram.renderer
participant Styles as Styling System
participant DOM as DOM/SVG
Note over User, DOM: Mermaid Complete API Flow
%% Initialization Phase
User->>+Mermaid: mermaid.initialize(config)
Mermaid->>+API: mermaidAPI.initialize(config)
API->>API: assignWithDepth({}, userOptions)
API->>API: handle legacy fontFamily config
API->>Config: saveConfigFromInitialize(options)
alt Theme Configuration Available
API->>API: check if theme in theme object
API->>API: set themeVariables from theme
else Default Theme
API->>API: use default theme variables
end
API->>Config: setSiteConfig(options) or getSiteConfig()
API->>API: setLogLevel(config.logLevel)
API->>DiagramAPI: addDiagrams()
API-->>-Mermaid: initialization complete
Mermaid-->>-User: ready to render
%% Content Loaded Event
User->>DOM: document.load event
DOM->>+Mermaid: contentLoaded()
opt startOnLoad is true
Mermaid->>Config: getConfig()
Config-->>Mermaid: { startOnLoad: true }
Mermaid->>Mermaid: run()
end
Mermaid-->>-DOM: event handling complete
%% Main Run Function
User->>+Mermaid: mermaid.run(options)
Mermaid->>Mermaid: runThrowsErrors(options)
Mermaid->>Config: getConfig()
Config-->>Mermaid: configuration object
alt nodes provided
Mermaid->>Mermaid: use provided nodes
else querySelector provided
Mermaid->>DOM: document.querySelectorAll(querySelector)
DOM-->>Mermaid: nodesToProcess
end
Mermaid->>Mermaid: new InitIDGenerator(deterministicIds, seed)
loop For each diagram element
Mermaid->>DOM: check element.getAttribute('data-processed')
opt not processed
Mermaid->>DOM: element.setAttribute('data-processed', 'true')
Mermaid->>Mermaid: generate unique id
Mermaid->>DOM: get element.innerHTML
Mermaid->>Mermaid: entityDecode and clean text
Mermaid->>Mermaid: detectInit(txt)
Mermaid->>Queue: render(id, txt, element)
end
end
Mermaid-->>-User: processing initiated
%% Render Function (Queued)
activate Queue
Queue->>+API: mermaidAPI.render(id, text, container)
API->>DiagramAPI: addDiagrams()
API->>+Preprocessor: processAndSetConfigs(text)
Preprocessor->>Preprocessor: preprocessDiagram(text)
Preprocessor->>Config: reset()
Preprocessor->>Config: addDirective(processed.config)
Preprocessor-->>-API: { code, config }
API->>Config: getConfig()
Config-->>API: current configuration
opt text length > maxTextSize
API->>API: text = MAX_TEXTLENGTH_EXCEEDED_MSG
end
API->>API: setup id selectors and element IDs
API->>API: determine security level (sandbox/loose)
%% DOM Setup
alt svgContainingElement provided
alt isSandboxed
API->>DOM: sandboxedIframe(select(svgContainingElement), iFrameID)
API->>DOM: select iframe contentDocument body
else
API->>DOM: select(svgContainingElement)
end
else no container
API->>API: removeExistingElements(document, id, divId, iFrameId)
alt isSandboxed
API->>DOM: sandboxedIframe(select('body'), iFrameID)
else
API->>DOM: select('body')
end
end
API->>DOM: appendDivSvgG(root, id, enclosingDivID, fontFamily, XMLNS_XLINK_STD)
%% Diagram Creation
API->>+Diagram: Diagram.fromText(text, { title: processed.title })
Diagram->>DiagramAPI: detect diagram type
Diagram->>DiagramAPI: load appropriate diagram
Diagram-->>-API: diagram instance
opt parsing error occurred
API->>+Diagram: Diagram.fromText('error')
Diagram-->>-API: error diagram
API->>API: store parseEncounteredException
end
%% Style Generation
API->>DOM: get svg element and firstChild
API->>Renderer: diag.renderer.getClasses(text, diag)
Renderer-->>API: diagramClassDefs
API->>+Styles: createUserStyles(config, diagramType, diagramClassDefs, idSelector)
Styles->>Styles: createCssStyles(config, classDefs)
opt config.themeCSS defined
Styles->>Styles: append themeCSS
end
opt fontFamily configured
Styles->>Styles: add CSS variables for fonts
end
opt classDefs exist
loop For each styleClassDef
opt has styles
Styles->>Styles: cssImportantStyles for each CSS element
end
opt has textStyles
Styles->>Styles: cssImportantStyles for tspan elements
end
end
end
Styles->>Styles: getStyles(graphType, userCSSstyles, themeVariables)
Styles->>Styles: serialize(compile(svgId{allStyles}), stringify)
Styles-->>-API: compiled CSS rules
API->>DOM: create style element
API->>DOM: style.innerHTML = rules
API->>DOM: svg.insertBefore(style, firstChild)
%% Diagram Rendering
API->>+Renderer: diag.renderer.draw(text, id, version, diag)
Renderer->>Renderer: diagram-specific rendering logic
Renderer->>DOM: create SVG elements
Renderer->>DOM: apply positioning and styling
Renderer-->>-API: rendered diagram
opt rendering error
alt suppressErrorRendering
API->>API: removeTempElements()
API->>Mermaid: throw error
else
API->>Renderer: errorRenderer.draw(text, id, version)
end
end
%% Accessibility and Cleanup
API->>DOM: select svg element
API->>Diagram: diag.db.getAccTitle()
API->>Diagram: diag.db.getAccDescription()
API->>API: addA11yInfo(diagramType, svgNode, a11yTitle, a11yDescr)
API->>DOM: set xmlns for foreignobject elements
API->>DOM: get innerHTML from enclosing div
API->>+API: cleanUpSvgCode(svgCode, isSandboxed, arrowMarkerAbsolute)
opt not useArrowMarkerUrls and not sandboxed
API->>API: replace marker-end URLs with anchors
end
API->>API: decodeEntities(svgCode)
API->>API: replace <br> with <br/>
API-->>-API: cleaned SVG code
alt isSandboxed
API->>+API: putIntoIFrame(svgCode, svgEl)
API->>API: calculate iframe height
API->>API: toBase64 encode SVG content
API->>API: create iframe with sandbox attributes
API-->>-API: iframe HTML
else not loose security
API->>API: DOMPurify.sanitize(svgCode, options)
end
API->>API: attachFunctions()
API->>API: removeTempElements()
opt parseEncounteredException
API->>Mermaid: throw parseEncounteredException
end
API-->>-Queue: { diagramType, svg: svgCode, bindFunctions }
%% Return to Web Integration
activate Mermaid
Queue-->>Mermaid: render result
Mermaid->>DOM: element.innerHTML = svg
opt postRenderCallback
Mermaid->>User: postRenderCallback(id)
end
opt bindFunctions exist
Mermaid->>DOM: bindFunctions(element)
end
deactivate Mermaid
%% Parse Function Flow
User->>+Mermaid: mermaid.parse(text, parseOptions)
activate Queue
Queue->>+API: mermaidAPI.parse(text, parseOptions)
API->>DiagramAPI: addDiagrams()
API->>Preprocessor: processAndSetConfigs(text)
Preprocessor-->>API: { code, config }
API->>Diagram: getDiagramFromText(code)
Diagram-->>API: diagram instance
API-->>-Queue: { diagramType: diagram.type, config }
Queue-->>-Mermaid: parse result
Mermaid-->>-User: ParseResult or false
%% External Diagram Registration
User->>+Mermaid: registerExternalDiagrams(diagrams, options)
Mermaid->>DiagramAPI: addDiagrams()
Mermaid->>DiagramAPI: registerLazyLoadedDiagrams(...diagrams)
opt lazyLoad is false
Mermaid->>DiagramAPI: loadRegisteredDiagrams()
end
Mermaid-->>-User: registration complete
%% Error Handling
Note over Mermaid, API: Error Handling Throughout
alt Error occurs
API->>Mermaid: throw error
Mermaid->>+Mermaid: handleError(error, errors, parseError)
Mermaid->>Mermaid: log.warn(error)
alt isDetailedError
Mermaid->>User: parseError(error.str, error.hash)
else
Mermaid->>User: parseError(error)
end
opt not suppressErrors
Mermaid->>User: throw error
end
Mermaid-->>-User: error handled
end
%% Configuration Details
Note over Config: Configuration Functions
Note right of Config: Functions:<br/>- reset()<br/>- getConfig()<br/>- setConfig()<br/>- getSiteConfig()<br/>- updateSiteConfig()<br/>- saveConfigFromInitialize()
Note over Styles: CSS Generation
Note right of Styles: Features:<br/>- createCssStyles()<br/>- createUserStyles()<br/>- cssImportantStyles()<br/>- Theme integration<br/>- Class definitions
Note over API: Security Levels
Note right of API: Modes:<br/>- sandbox: iframe isolation<br/>- loose: minimal sanitization<br/>- default: DOMPurify sanitization
Note over API: Helper Functions
Note right of API: Utilities:<br/>- cleanUpSvgCode()<br/>- putIntoIFrame()<br/>- appendDivSvgG()<br/>- removeExistingElements()

View File

@@ -0,0 +1,180 @@
---
references:
- "File: /packages/mermaid/src/diagrams/mindmap/mindmap-definition.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/mindmapDb.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/detector.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/styles.ts"
- "File: /packages/mermaid/src/diagrams/mindmap/svgDraw.ts"
generationTime: 2025-01-28T16:00:00.000Z
---
sequenceDiagram
participant User as User Input Text
participant Detector as detector.ts
participant Loader as DiagramLoader
participant Definition as mindmap-definition.ts
participant Parser as parser/mindmap.jison
participant DB as MindmapDB
participant Renderer as mindmapRenderer.ts
participant Cytoscape as cytoscape.js
participant SVGDraw as svgDraw.ts
participant Styles as styles.ts
participant Output as Final SVG
Note over User, Output: Mindmap Implementation Flow
%% Detection Phase
User->>Detector: /^\s*mindmap/ text input
activate Detector
Detector->>Detector: detector(txt) validates pattern
Detector->>Loader: loader() function called
deactivate Detector
activate Loader
Loader->>Definition: import mindmap-definition.js
deactivate Loader
%% Core Structure Setup
activate Definition
Definition->>DB: get db() → new MindmapDB()
Definition->>Renderer: setup renderer
Definition->>Parser: setup parser
Definition->>Styles: setup styles
deactivate Definition
%% Database Initialization
activate DB
Note over DB: MindmapDB Constructor
DB->>DB: initialize nodes array
DB->>DB: setup nodeType constants
DB->>DB: bind methods
DB->>DB: clear() state
%% Parsing Phase
activate Parser
User->>Parser: mindmap syntax text
loop For each node in hierarchy
Parser->>DB: addNode(level, id, descr, type)
activate DB
DB->>DB: sanitizeText(id, descr)
DB->>DB: getType(startStr, endStr)
Note right of DB: Shape Detection:<br/>[ → RECT<br/>( → ROUNDED_RECT<br/>(( → CIRCLE<br/>)) → BANG<br/>{{ → HEXAGON
DB->>DB: getParent(level)
DB->>DB: create MindmapNode
DB->>DB: add to hierarchy
deactivate DB
end
opt Icon/Class Decoration
Parser->>DB: decorateNode(decoration)
DB->>DB: set icon/class properties
end
deactivate Parser
%% Data Preparation
Renderer->>DB: getData() for layout
activate DB
DB->>DB: collect all nodes
DB->>DB: build parent-child relationships
DB-->>Renderer: return node hierarchy
deactivate DB
%% Rendering Pipeline
activate Renderer
Note over Renderer: Rendering Phase
Renderer->>Cytoscape: initialize cytoscape
activate Cytoscape
loop For each node in mindmap
Renderer->>Cytoscape: addNodes(mindmap, cy, conf, level)
Cytoscape->>Cytoscape: create node data
Cytoscape->>Cytoscape: set position (x, y)
end
loop For parent-child relationships
Renderer->>Cytoscape: add edges
Cytoscape->>Cytoscape: create edge data
end
Renderer->>Cytoscape: configure cose-bilkent layout
Cytoscape->>Cytoscape: calculate optimal positions
Cytoscape-->>Renderer: return positioned graph
deactivate Cytoscape
%% SVG Generation
Renderer->>SVGDraw: drawNodes(db, svg, mindmap, section, conf)
activate SVGDraw
loop For each node recursively
SVGDraw->>SVGDraw: select shape function
alt Default Shape
SVGDraw->>SVGDraw: defaultBkg() - rounded rectangle
else Rectangle Shape
SVGDraw->>SVGDraw: rectBkg() - sharp corners
else Circle Shape
SVGDraw->>SVGDraw: circleBkg() - perfect circle
else Cloud Shape
SVGDraw->>SVGDraw: cloudBkg() - organic curves
else Bang Shape
SVGDraw->>SVGDraw: bangBkg() - explosion style
else Hexagon Shape
SVGDraw->>SVGDraw: hexagonBkg() - six sides
end
SVGDraw->>SVGDraw: create SVG elements
SVGDraw->>SVGDraw: add text labels
SVGDraw->>SVGDraw: position node
opt Node has children
SVGDraw->>SVGDraw: drawNodes() recursive call
end
end
deactivate SVGDraw
%% Edge Rendering
Renderer->>Renderer: drawEdges(edgesEl, cy)
loop For each edge
Renderer->>Renderer: extract edge bounds
Renderer->>Renderer: draw SVG path
end
%% Styling Application
Renderer->>Styles: getStyles(options)
activate Styles
Styles->>Styles: genSections(options)
loop For THEME_COLOR_LIMIT sections
Styles->>Styles: generate color scale
Styles->>Styles: create CSS rules
Note right of Styles: .section-X fills<br/>.edge-depth-X widths<br/>.node-icon-X colors
end
Styles->>Styles: apply theme integration
Styles-->>Renderer: return compiled CSS
deactivate Styles
%% Final Assembly
Renderer->>Output: selectSvgElement()
Renderer->>Output: setupGraphViewbox()
Renderer->>Output: apply styles
Renderer->>Output: add interactive elements
deactivate Renderer
activate Output
Note over Output: Final Mindmap Features
Output->>Output: responsive layout
Output->>Output: accessibility attributes
Output->>Output: hover effects
Output->>Output: click handling
Output-->>User: rendered mindmap
deactivate Output
%% Configuration Details
Note over DB, Styles: Configuration Options
Note right of DB: Padding Calculations:<br/>Base padding from config<br/>RECT: ×2 padding<br/>ROUNDED_RECT: ×2 padding<br/>HEXAGON: ×2 padding
Note right of Styles: Section Management:<br/>MAX_SECTIONS = 12<br/>Dynamic color generation<br/>Git theme integration
Note right of Renderer: Layout Parameters:<br/>Cytoscape configuration<br/>coseBilkent settings<br/>Node spacing rules

View File

@@ -73,7 +73,7 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [Obsidian](https://help.obsidian.md/Editing+and+formatting/Advanced+formatting+syntax#Diagram) ✅
- [Outline](https://docs.getoutline.com/s/guide/doc/diagrams-KQiKoT4wzK) ✅
- [Redmine](https://redmine.org)
- [Mermaid Macro](https://www.redmine.org/plugins/redmine_mermaid_macro)
- [Mermaid Macro](https://redmine.org/plugins/redmine_mermaid_macro)
- [Markdown for mermaid plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin)
- [redmine-mermaid](https://github.com/styz/redmine_mermaid)
- Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive)
@@ -84,6 +84,7 @@ To add an integration to this list, see the [Integrations - create page](./integ
LLM integrations to create mermaid diagrams using AI from text descriptions.
- [HueHive - Create mermaid diagrams with text](https://huehive.co/tools/diagrams)
- [MCP Server Mermaid](https://github.com/hustcc/mcp-mermaid) - Generate mermaid diagram and chart with AI MCP dynamically.
### CRM/ERP
@@ -103,6 +104,7 @@ Blogging frameworks and platforms
- [Mermaid](https://nextra.site/docs/guide/mermaid)
- [WordPress](https://wordpress.org)
- [MerPRess](https://wordpress.org/plugins/merpress/)
- [WP Documentation](https://wordpress.org/themes/wp-documentation/)
### CMS/ECM
@@ -115,7 +117,7 @@ Content Management Systems/Enterprise Content Management
- [Grav CMS](https://getgrav.org/)
- [Mermaid Diagrams Plugin](https://github.com/DanielFlaum/grav-plugin-mermaid-diagrams)
- [GitLab Markdown Adapter](https://github.com/Goutte/grav-plugin-gitlab-markdown-adapter)
- [Tiki](https://tiki.org)
- [Tiki Wiki CMS Groupware](https://tiki.org)
- [Tracker Entity Relationship Diagram](https://doc.tiki.org/Tracker-Entity-Relationship-Diagram)
- [VitePress](https://vitepress.vuejs.org/)
- [Plugin for Mermaid.js](https://emersonbottero.github.io/vitepress-plugin-mermaid/)

View File

@@ -16,9 +16,7 @@ Applications that support Mermaid files [SHOULD](https://datatracker.ietf.org/do
### MIME Type
The recommended [MIME type](https://www.iana.org/assignments/media-types/media-types.xhtml) for Mermaid media is `text/vnd.mermaid`.
Currently pending [IANA](https://www.iana.org/) recognition.
The recommended [MIME type](https://www.iana.org/assignments/media-types/media-types.xhtml) for Mermaid media is [`text/vnd.mermaid`](https://www.iana.org/assignments/media-types/application/vnd.mermaid).
## Showcase

View File

@@ -6,6 +6,66 @@
# Blog
## [Mermaid introduces the Visual Editor for Entity Relationship diagrams](https://docs.mermaidchart.com/blog/posts/mermaid-introduces-the-visual-editor-for-entity-relationship-diagrams)
7/15/2025 • 7 mins
Mermaid just introduced a Visual Editor for Entity Relationship diagrams, letting anyone map database structures through a simple point-and-click interface instead of code. This no-code ER builder now sits alongside Mermaids editors for flowcharts, sequence, and class diagrams, enabling teams to craft and share polished data models for apps, AI, and business processes.
## [Mermaid supports Treemap Diagrams now!!!](https://docs.mermaidchart.com/blog/posts/mermaid-have-treemap-diagrams-now)
7/3/2025 • 4 mins
Mermaid has introduced Treemap diagrams, currently in beta, enhancing hierarchical data visualization. Treemap diagrams use nested rectangles to represent data relationships, focusing on size and proportions. They offer various applications, including budget visualization and market analysis. With simple syntax and customization options, users can effectively present complex data hierarchies.
## [AI Diagram Generators and Data Visualization: Best Practices](https://docs.mermaidchart.com/blog/posts/ai-diagram-generators-and-data-visualization-best-practices)
7/2/2025 • 6 mins
AI diagram generators transform complex data into clear, interactive visuals enabling faster analysis, better decisions, and stronger collaboration across teams. By combining automation with manual refinement, these tools empower anyone to communicate insights effectively, regardless of technical skill level.
## [How to Choose the Best AI Diagram Generator for Your Needs (2025)](https://docs.mermaidchart.com/blog/posts/how-to-choose-the-best-ai-diagram-generator-for-your-needs-2025)
6/26/2025 • 14 mins
AI diagram generators are transforming how developers visualize and communicate complex systems, reducing hours of manual work into minutes. With tools like Mermaid AI, users benefit from both code-based and visual editing, enabling seamless collaboration and precision. Whether youre diagramming workflows, software architecture, or data relationships, the right AI tool can significantly boost productivity and streamline communication.
## [5 Time-Saving Tips for Using Mermaids AI Diagram Generator Effectively](https://docs.mermaidchart.com/blog/posts/5-time-saving-tips-for-using-mermaids-ai-diagram-generator-effectively)
6/11/2025 • 10 mins
See how developers can save time and boost productivity using Mermaid Charts AI diagram generator. Learn five practical tips that help turn plain language into powerful, professional diagrams.
## [Enhancing Team Collaboration with AI-Powered Diagrams](https://docs.mermaidchart.com/blog/posts/enhancing-team-collaboration-with-ai-powered-diagrams)
5/27/2025 • 6 mins
Software teams move fast, but old-school diagramming tools cant keep up. Mermaid Chart replaces static slides and whiteboards with real-time, AI-generated visuals that evolve with your code and ideas. Just describe a process in plain English, and watch it come to life.
## [What is an AI Diagram Generator? Benefits and Use Cases](https://docs.mermaidchart.com/blog/posts/what-is-an-ai-diagram-generator-benefits-and-use-cases)
5/22/2025 • 6 mins
Discover how AI diagram generators like Mermaid Chart transform developer workflows. Instantly turn text into flowcharts, ERDs, and system diagrams, no manual drag-and-drop needed. Learn how it works, key benefits, and real-world use cases.
## [How to Use Mermaid Chart as an AI Diagram Generator for Developers](https://docs.mermaidchart.com/blog/posts/how-to-use-mermaid-chart-as-an-ai-diagram-generator)
5/21/2025 • 9 mins
Would an AI diagram generator make your life easier? We think it would!
## [Mermaid Chart VS Code Plugin: Create and Edit Mermaid.js Diagrams in Visual Studio Code](https://docs.mermaidchart.com/blog/posts/mermaid-chart-vs-code-plugin-create-and-edit-mermaid-js-diagrams-in-visual-studio-code)
3/21/2025 • 5 mins
The Mermaid Chart VS Code Plugin is a powerful developer diagramming tool that brings Mermaid.js diagramming directly into your Visual Studio Code environment. Whether youre visualizing software architecture, documenting API flows, fixing bad documentation, or managing flowcharts and sequence diagrams, this plugin integrates seamlessly into your workflow. Key Features of the Mermaid Chart VS Code \[…]
## [Mermaid Chart: The Evolution of Mermaid](https://docs.mermaidchart.com/blog/posts/mermaid-chart-the-evolution-of-mermaid)
1/30/2025 • 3 mins
Mermaid revolutionized diagramming with its simple, markdown-style syntax, empowering millions of developers worldwide. Now, Mermaid Chart takes it further with AI-powered visuals, a GUI for seamless editing, real-time collaboration, and advanced design tools. Experience the next generation of diagramming—faster, smarter, and built for modern teams. Try Mermaid Chart today!
## [GUI for editing Mermaid Class Diagrams](https://docs.mermaidchart.com/blog/posts/gui-for-editing-mermaid-class-diagrams)
1/17/2025 • 5 mins

View File

@@ -9,7 +9,7 @@
## Introduction to Block Diagrams
```mermaid-example
block-beta
block
columns 1
db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -26,7 +26,7 @@ columns 1
```
```mermaid
block-beta
block
columns 1
db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -80,12 +80,12 @@ At its core, a block diagram consists of blocks representing different entities
To create a simple block diagram with three blocks labeled 'a', 'b', and 'c', the syntax is as follows:
```mermaid-example
block-beta
block
a b c
```
```mermaid
block-beta
block
a b c
```
@@ -101,13 +101,13 @@ While simple block diagrams are linear and straightforward, more complex systems
In scenarios where you need to distribute blocks across multiple columns, you can specify the number of columns and arrange the blocks accordingly. Here's how to create a block diagram with three columns and four blocks, where the fourth block appears in a second row:
```mermaid-example
block-beta
block
columns 3
a b c d
```
```mermaid
block-beta
block
columns 3
a b c d
```
@@ -130,13 +130,13 @@ In more complex diagrams, you may need blocks that span multiple columns to emph
To create a block diagram where one block spans across two columns, you can specify the desired width for each block:
```mermaid-example
block-beta
block
columns 3
a["A label"] b:2 c:2 d
```
```mermaid
block-beta
block
columns 3
a["A label"] b:2 c:2 d
```
@@ -153,7 +153,7 @@ Composite blocks, or blocks within blocks, are an advanced feature in Mermaid's
Creating a composite block involves defining a parent block and then nesting other blocks within it. Here's how to define a composite block with nested elements:
```mermaid-example
block-beta
block
block
D
end
@@ -161,7 +161,7 @@ block-beta
```
```mermaid
block-beta
block
block
D
end
@@ -180,7 +180,7 @@ Mermaid also allows for dynamic adjustment of column widths based on the content
In diagrams with varying block sizes, Mermaid automatically adjusts the column widths to fit the largest block in each column. Here's an example:
```mermaid-example
block-beta
block
columns 3
a:3
block:group1:2
@@ -195,7 +195,7 @@ block-beta
```
```mermaid
block-beta
block
columns 3
a:3
block:group1:2
@@ -215,7 +215,7 @@ This example demonstrates how Mermaid dynamically adjusts the width of the colum
In scenarios where you need to stack blocks horizontally, you can use column width to accomplish the task. Blocks can be arranged vertically by putting them in a single column. Here is how you can create a block diagram in which 4 blocks are stacked on top of each other:
```mermaid-example
block-beta
block
block
columns 1
a["A label"] b c d
@@ -223,7 +223,7 @@ block-beta
```
```mermaid
block-beta
block
block
columns 1
a["A label"] b c d
@@ -247,12 +247,12 @@ Mermaid supports a range of block shapes to suit different diagramming needs, fr
To create a block with round edges, which can be used to represent a softer or more flexible component:
```mermaid-example
block-beta
block
id1("This is the text in the box")
```
```mermaid
block-beta
block
id1("This is the text in the box")
```
@@ -261,12 +261,12 @@ block-beta
A stadium-shaped block, resembling an elongated circle, can be used for components that are process-oriented:
```mermaid-example
block-beta
block
id1(["This is the text in the box"])
```
```mermaid
block-beta
block
id1(["This is the text in the box"])
```
@@ -275,12 +275,12 @@ block-beta
For representing subroutines or contained processes, a block with double vertical lines is useful:
```mermaid-example
block-beta
block
id1[["This is the text in the box"]]
```
```mermaid
block-beta
block
id1[["This is the text in the box"]]
```
@@ -289,12 +289,12 @@ block-beta
The cylindrical shape is ideal for representing databases or storage components:
```mermaid-example
block-beta
block
id1[("Database")]
```
```mermaid
block-beta
block
id1[("Database")]
```
@@ -303,12 +303,12 @@ block-beta
A circle can be used for centralized or pivotal components:
```mermaid-example
block-beta
block
id1(("This is the text in the circle"))
```
```mermaid
block-beta
block
id1(("This is the text in the circle"))
```
@@ -319,36 +319,36 @@ For decision points, use a rhombus, and for unique or specialized processes, asy
**Asymmetric**
```mermaid-example
block-beta
block
id1>"This is the text in the box"]
```
```mermaid
block-beta
block
id1>"This is the text in the box"]
```
**Rhombus**
```mermaid-example
block-beta
block
id1{"This is the text in the box"}
```
```mermaid
block-beta
block
id1{"This is the text in the box"}
```
**Hexagon**
```mermaid-example
block-beta
block
id1{{"This is the text in the box"}}
```
```mermaid
block-beta
block
id1{{"This is the text in the box"}}
```
@@ -357,7 +357,7 @@ block-beta
Parallelogram and trapezoid shapes are perfect for inputs/outputs and transitional processes:
```mermaid-example
block-beta
block
id1[/"This is the text in the box"/]
id2[\"This is the text in the box"\]
A[/"Christmas"\]
@@ -365,7 +365,7 @@ block-beta
```
```mermaid
block-beta
block
id1[/"This is the text in the box"/]
id2[\"This is the text in the box"\]
A[/"Christmas"\]
@@ -377,12 +377,12 @@ block-beta
For highlighting critical or high-priority components, a double circle can be effective:
```mermaid-example
block-beta
block
id1((("This is the text in the circle")))
```
```mermaid
block-beta
block
id1((("This is the text in the circle")))
```
@@ -395,7 +395,7 @@ Mermaid also offers unique shapes like block arrows and space blocks for directi
Block arrows can visually indicate direction or flow within a process:
```mermaid-example
block-beta
block
blockArrowId<["Label"]>(right)
blockArrowId2<["Label"]>(left)
blockArrowId3<["Label"]>(up)
@@ -406,7 +406,7 @@ block-beta
```
```mermaid
block-beta
block
blockArrowId<["Label"]>(right)
blockArrowId2<["Label"]>(left)
blockArrowId3<["Label"]>(up)
@@ -421,14 +421,14 @@ block-beta
Space blocks can be used to create intentional empty spaces in the diagram, which is useful for layout and readability:
```mermaid-example
block-beta
block
columns 3
a space b
c d e
```
```mermaid
block-beta
block
columns 3
a space b
c d e
@@ -437,12 +437,12 @@ block-beta
or
```mermaid-example
block-beta
block
ida space:3 idb idc
```
```mermaid
block-beta
block
ida space:3 idb idc
```
@@ -467,13 +467,13 @@ The most fundamental aspect of connecting blocks is the use of arrows or links.
A simple link with an arrow can be created to show direction or flow from one block to another:
```mermaid-example
block-beta
block
A space B
A-->B
```
```mermaid
block-beta
block
A space B
A-->B
```
@@ -490,13 +490,13 @@ Example - Text with Links
To add text to a link, the syntax includes the text within the link definition:
```mermaid-example
block-beta
block
A space:2 B
A-- "X" -->B
```
```mermaid
block-beta
block
A space:2 B
A-- "X" -->B
```
@@ -506,7 +506,7 @@ This example show how to add descriptive text to the links, enhancing the inform
Example - Edges and Styles:
```mermaid-example
block-beta
block
columns 1
db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -523,7 +523,7 @@ columns 1
```
```mermaid
block-beta
block
columns 1
db(("DB"))
blockArrowId6<["&nbsp;&nbsp;&nbsp;"]>(down)
@@ -552,7 +552,7 @@ Mermaid enables detailed styling of individual blocks, allowing you to apply var
To apply custom styles to a block, you can use the `style` keyword followed by the block identifier and the desired CSS properties:
```mermaid-example
block-beta
block
id1 space id2
id1("Start")-->id2("Stop")
style id1 fill:#636,stroke:#333,stroke-width:4px
@@ -560,7 +560,7 @@ block-beta
```
```mermaid
block-beta
block
id1 space id2
id1("Start")-->id2("Stop")
style id1 fill:#636,stroke:#333,stroke-width:4px
@@ -574,7 +574,7 @@ Mermaid enables applying styling to classes, which could make styling easier if
#### Example - Styling a Single Class
```mermaid-example
block-beta
block
A space B
A-->B
classDef blue fill:#6e6ce6,stroke:#333,stroke-width:4px;
@@ -583,7 +583,7 @@ block-beta
```
```mermaid
block-beta
block
A space B
A-->B
classDef blue fill:#6e6ce6,stroke:#333,stroke-width:4px;
@@ -608,7 +608,7 @@ Combining the elements of structure, linking, and styling, we can create compreh
Illustrating a simple software system architecture with interconnected components:
```mermaid-example
block-beta
block
columns 3
Frontend blockArrowId6<[" "]>(right) Backend
space:2 down<[" "]>(down)
@@ -621,7 +621,7 @@ block-beta
```
```mermaid
block-beta
block
columns 3
Frontend blockArrowId6<[" "]>(right) Backend
space:2 down<[" "]>(down)
@@ -640,7 +640,7 @@ This example shows a basic architecture with a frontend, backend, and database.
Representing a business process flow with decision points and multiple stages:
```mermaid-example
block-beta
block
columns 3
Start(("Start")) space:2
down<[" "]>(down) space:2
@@ -653,7 +653,7 @@ block-beta
```
```mermaid
block-beta
block
columns 3
Start(("Start")) space:2
down<[" "]>(down) space:2
@@ -682,7 +682,7 @@ Understanding and avoiding common syntax errors is key to a smooth experience wi
A common mistake is incorrect linking syntax, which can lead to unexpected results or broken diagrams:
```
block-beta
block
A - B
```
@@ -690,13 +690,13 @@ block-beta
Ensure that links between blocks are correctly specified with arrows (--> or ---) to define the direction and type of connection. Also remember that one of the fundamentals for block diagram is to give the author full control of where the boxes are positioned so in the example you need to add a space between the boxes:
```mermaid-example
block-beta
block
A space B
A --> B
```
```mermaid
block-beta
block
A space B
A --> B
```
@@ -706,13 +706,13 @@ block-beta
Applying styles in the wrong context or with incorrect syntax can lead to blocks not being styled as intended:
```mermaid-example
block-beta
block
A
style A fill#969;
```
```mermaid
block-beta
block
A
style A fill#969;
```
@@ -721,14 +721,14 @@ Applying styles in the wrong context or with incorrect syntax can lead to blocks
Correct the syntax by ensuring proper separation of style properties with commas and using the correct CSS property format:
```mermaid-example
block-beta
block
A
style A fill:#969,stroke:#333;
```
```mermaid
block-beta
block
A
style A fill:#969,stroke:#333;

View File

@@ -1795,15 +1795,54 @@ It is possible to style the type of curve used for lines between items, if the d
Available curve styles include `basis`, `bumpX`, `bumpY`, `cardinal`, `catmullRom`, `linear`, `monotoneX`, `monotoneY`,
`natural`, `step`, `stepAfter`, and `stepBefore`.
For a full list of available curves, including an explanation of custom curves, refer to
the [Shapes](https://d3js.org/d3-shape/curve) documentation in the [d3-shape](https://github.com/d3/d3-shape/) project.
Line styling can be achieved in two ways:
1. Change the curve style of all the lines
2. Change the curve style of a particular line
#### Diagram level curve style
In this example, a left-to-right graph uses the `stepBefore` curve style:
```
%%{ init: { 'flowchart': { 'curve': 'stepBefore' } } }%%
---
config:
flowchart:
curve: stepBefore
---
graph LR
```
For a full list of available curves, including an explanation of custom curves, refer to
the [Shapes](https://d3js.org/d3-shape/curve) documentation in the [d3-shape](https://github.com/d3/d3-shape/) project.
#### Edge level curve style using Edge IDs (v11.10.0+)
You can assign IDs to [edges](#attaching-an-id-to-edges). After assigning an ID you can modify the line style by modifying the edge's `curve` property using the following syntax:
```mermaid-example
flowchart LR
A e1@==> B
A e2@--> C
e1@{ curve: linear }
e2@{ curve: natural }
```
```mermaid
flowchart LR
A e1@==> B
A e2@--> C
e1@{ curve: linear }
e2@{ curve: natural }
```
```info
Any edge curve style modified at the edge level overrides the diagram level style.
```
```info
If the same edge is modified multiple times the last modification will be rendered.
```
### Styling a node

View File

@@ -37,6 +37,11 @@ Drawing a pie chart is really simple in mermaid.
- Followed by `:` colon as separator
- Followed by `positive numeric value` (supported up to two decimal places)
**Note:**
> Pie chart values must be **positive numbers greater than zero**.
> **Negative values are not allowed** and will result in an error.
\[pie] \[showData] (OPTIONAL)
\[title] \[titlevalue] (OPTIONAL)
"\[datakey1]" : \[dataValue1]

View File

@@ -23,7 +23,7 @@ config:
sankey:
showValues: false
---
sankey-beta
sankey
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
@@ -101,7 +101,7 @@ config:
sankey:
showValues: false
---
sankey-beta
sankey
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
@@ -175,7 +175,7 @@ Wind,Electricity grid,289.366
## Syntax
The idea behind syntax is that a user types `sankey-beta` keyword first, then pastes raw CSV below and get the result.
The idea behind syntax is that a user types `sankey` keyword first, then pastes raw CSV below and get the result.
It implements CSV standard as [described here](https://www.ietf.org/rfc/rfc4180.txt) with subtle **differences**:
@@ -187,7 +187,7 @@ It implements CSV standard as [described here](https://www.ietf.org/rfc/rfc4180.
It is implied that 3 columns inside CSV should represent `source`, `target` and `value` accordingly:
```mermaid-example
sankey-beta
sankey
%% source,target,value
Electricity grid,Over generation / exports,104.453
@@ -196,7 +196,7 @@ Electricity grid,H2 conversion,27.14
```
```mermaid
sankey-beta
sankey
%% source,target,value
Electricity grid,Over generation / exports,104.453
@@ -209,7 +209,7 @@ Electricity grid,H2 conversion,27.14
CSV does not support empty lines without comma delimiters by default. But you can add them if needed:
```mermaid-example
sankey-beta
sankey
Bio-conversion,Losses,26.862
@@ -219,7 +219,7 @@ Bio-conversion,Gas,81.144
```
```mermaid
sankey-beta
sankey
Bio-conversion,Losses,26.862
@@ -233,14 +233,14 @@ Bio-conversion,Gas,81.144
If you need to have a comma, wrap it in double quotes:
```mermaid-example
sankey-beta
sankey
Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672
```
```mermaid
sankey-beta
sankey
Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672
@@ -251,14 +251,14 @@ Pumped heat,"Heating and cooling, commercial",70.672
If you need to have double quote, put a pair of them inside quoted string:
```mermaid-example
sankey-beta
sankey
Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672
```
```mermaid
sankey-beta
sankey
Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672

View File

@@ -13,7 +13,7 @@
## Example
```mermaid-example
xychart-beta
xychart
title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -22,7 +22,7 @@ xychart-beta
```
```mermaid
xychart-beta
xychart
title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -40,7 +40,7 @@ xychart-beta
The chart can be drawn horizontal or vertical, default value is vertical.
```
xychart-beta horizontal
xychart horizontal
...
```
@@ -51,7 +51,7 @@ The title is a short description of the chart and it will always render on top o
#### Example
```
xychart-beta
xychart
title "This is a simple example"
...
```
@@ -98,10 +98,10 @@ A bar chart offers the capability to graphically depict bars.
#### Simplest example
The only two things required are the chart name (`xychart-beta`) and one data set. So you will be able to draw a chart with a simple config like
The only two things required are the chart name (`xychart`) and one data set. So you will be able to draw a chart with a simple config like
```
xychart-beta
xychart
line [+1.3, .6, 2.4, -.34]
```
@@ -176,7 +176,7 @@ config:
xyChart:
titleColor: "#ff0000"
---
xychart-beta
xychart
title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
@@ -195,7 +195,7 @@ config:
xyChart:
titleColor: "#ff0000"
---
xychart-beta
xychart
title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000

View File

@@ -67,7 +67,7 @@
"@argos-ci/cypress": "^5.0.2",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.27.12",
"@cspell/eslint-plugin": "^8.19.3",
"@cspell/eslint-plugin": "^8.19.4",
"@cypress/code-coverage": "^3.12.49",
"@eslint/js": "^9.26.0",
"@rollup/plugin-typescript": "^12.1.2",
@@ -94,17 +94,17 @@
"cypress-split": "^1.24.14",
"esbuild": "^0.25.0",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-cypress": "^4.3.0",
"eslint-plugin-html": "^8.1.2",
"eslint-plugin-jest": "^28.11.0",
"eslint-plugin-jsdoc": "^50.6.9",
"eslint-plugin-html": "^8.1.3",
"eslint-plugin-jest": "^28.14.0",
"eslint-plugin-jsdoc": "^50.8.0",
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-lodash": "^8.0.0",
"eslint-plugin-markdown": "^5.1.0",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-tsdoc": "^0.4.0",
"eslint-plugin-unicorn": "^59.0.0",
"eslint-plugin-unicorn": "^59.0.1",
"express": "^5.1.0",
"globals": "^16.0.0",
"globby": "^14.0.2",
@@ -126,7 +126,7 @@
"tslib": "^2.8.1",
"tsx": "^4.7.3",
"typescript": "~5.7.3",
"typescript-eslint": "^8.32.0",
"typescript-eslint": "^8.38.0",
"vite": "^7.0.3",
"vite-plugin-istanbul": "^7.0.0",
"vitest": "^3.0.6"

View File

@@ -0,0 +1,41 @@
# @mermaid-js/examples
The `@mermaid-js/examples` package contains a collection of examples used by tools like [mermaid.live](https://mermaid.live) to help users get started with new diagrams.
You can duplicate an existing diagram example file, e.g., `packages/examples/src/examples/flowchart.ts`, and modify it with details specific to your diagram.
Then, import the example in the `packages/examples/src/index.ts` file and add it to the `examples` array.
Each diagram should have at least one example, which should be marked as the default. It's a good idea to add more examples to showcase different features of the diagram.
## Usage
```bash
pnpm add @mermaid-js/examples
```
A sample usage of the package in mermaid.live, to get the default example for every diagram type:
```ts
import { diagramData } from '@mermaid-js/examples';
type DiagramDefinition = (typeof diagramData)[number];
const isValidDiagram = (diagram: DiagramDefinition): diagram is Required<DiagramDefinition> => {
return Boolean(diagram.name && diagram.examples && diagram.examples.length > 0);
};
export const getSampleDiagrams = () => {
const diagrams = diagramData
.filter((d) => isValidDiagram(d))
.map(({ examples, ...rest }) => ({
...rest,
example: examples?.filter(({ isDefault }) => isDefault)[0],
}));
const examples: Record<string, string> = {};
for (const diagram of diagrams) {
examples[diagram.name.replace(/ (Diagram|Chart|Graph)/, '')] = diagram.example.code;
}
return examples;
};
```

View File

@@ -16,6 +16,10 @@
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"
},
"scripts": {
"clean": "rimraf dist"
},
@@ -23,9 +27,6 @@
"devDependencies": {
"mermaid": "workspace:*"
},
"peerDependencies": {
"mermaid": "workspace:~"
},
"publishConfig": {
"access": "public"
}

View File

@@ -1,5 +1,16 @@
# @mermaid-js/layout-elk
## 0.1.9
### Patch Changes
- [#6857](https://github.com/mermaid-js/mermaid/pull/6857) [`b9ef683`](https://github.com/mermaid-js/mermaid/commit/b9ef683fb67b8959abc455d6cc5266c37ba435f6) Thanks [@knsv](https://github.com/knsv)! - feat: Exposing elk configuration forceNodeModelOrder and considerModelOrder to the mermaid configuration
- [#6849](https://github.com/mermaid-js/mermaid/pull/6849) [`2260948`](https://github.com/mermaid-js/mermaid/commit/2260948b7bda08f00616c2ce678bed1da69eb96c) Thanks [@anderium](https://github.com/anderium)! - Make elk not force node model order, but strongly consider it instead
- Updated dependencies [[`b9ef683`](https://github.com/mermaid-js/mermaid/commit/b9ef683fb67b8959abc455d6cc5266c37ba435f6), [`2c0931d`](https://github.com/mermaid-js/mermaid/commit/2c0931da46794b49d2523211e25f782900c34e94), [`33e08da`](https://github.com/mermaid-js/mermaid/commit/33e08daf175125295a06b1b80279437004a4e865), [`814b68b`](https://github.com/mermaid-js/mermaid/commit/814b68b4a94813f7c6b3d7fb4559532a7bab2652), [`fce7cab`](https://github.com/mermaid-js/mermaid/commit/fce7cabb71d68a20a66246fe23d066512126a412), [`fc07f0d`](https://github.com/mermaid-js/mermaid/commit/fc07f0d8abca49e4f887d7457b7b94fb07d1e3da), [`12e01bd`](https://github.com/mermaid-js/mermaid/commit/12e01bdb5cacf3569133979a5a4f1d8973e9aec1), [`01aaef3`](https://github.com/mermaid-js/mermaid/commit/01aaef39b4a1ec8bc5a0c6bfa3a20b712d67f4dc), [`daf8d8d`](https://github.com/mermaid-js/mermaid/commit/daf8d8d3befcd600618a629977b76463b38d0ad9), [`c36cd05`](https://github.com/mermaid-js/mermaid/commit/c36cd05c45ac3090181152b4dae41f8d7b569bd6), [`8bb29fc`](https://github.com/mermaid-js/mermaid/commit/8bb29fc879329ad109898e4025b4f4eba2ab0649), [`71b04f9`](https://github.com/mermaid-js/mermaid/commit/71b04f93b07f876df2b30656ef36036c1d0e4e4f), [`c99bce6`](https://github.com/mermaid-js/mermaid/commit/c99bce6bab4c7ce0b81b66d44f44853ce4aeb1c3), [`6cc1926`](https://github.com/mermaid-js/mermaid/commit/6cc192680a2531cab28f87a8061a53b786e010f3), [`9da6fb3`](https://github.com/mermaid-js/mermaid/commit/9da6fb39ae278401771943ac85d6d1b875f78cf1), [`e48b0ba`](https://github.com/mermaid-js/mermaid/commit/e48b0ba61dab7f95aa02da603b5b7d383b894932), [`4d62d59`](https://github.com/mermaid-js/mermaid/commit/4d62d5963238400270e9314c6e4d506f48147074), [`e9ce8cf`](https://github.com/mermaid-js/mermaid/commit/e9ce8cf4da9062d85098042044822100889bb0dd), [`9258b29`](https://github.com/mermaid-js/mermaid/commit/9258b2933bbe1ef41087345ffea3731673671c49), [`da90f67`](https://github.com/mermaid-js/mermaid/commit/da90f6760b6efb0da998bcb63b75eecc29e06c08), [`0133f1c`](https://github.com/mermaid-js/mermaid/commit/0133f1c0c5cff4fc4c8e0b99e9cf0b3d49dcbe71), [`895f9d4`](https://github.com/mermaid-js/mermaid/commit/895f9d43ff98ca05ebfba530789f677f31a011ff)]:
- mermaid@11.10.0
## 0.1.8
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@mermaid-js/layout-elk",
"version": "0.1.8",
"version": "0.1.9",
"description": "ELK layout engine for mermaid",
"module": "dist/mermaid-layout-elk.core.mjs",
"types": "dist/layouts.d.ts",

View File

@@ -766,7 +766,10 @@ export const render = async (
id: 'root',
layoutOptions: {
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
'elk.layered.crossingMinimization.forceNodeModelOrder': true,
'elk.layered.crossingMinimization.forceNodeModelOrder':
data4Layout.config.elk?.forceNodeModelOrder,
'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder,
'elk.algorithm': algorithm,
'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy,
'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges,

View File

@@ -1,5 +1,14 @@
# @mermaid-js/mermaid-zenuml
## 0.2.2
### Patch Changes
- [#6798](https://github.com/mermaid-js/mermaid/pull/6798) [`3ffe961`](https://github.com/mermaid-js/mermaid/commit/3ffe9618aebc9ac96de6e3c826481f542f18c2a9) Thanks [@MrCoder](https://github.com/MrCoder)! - Fixed a critical bug that the ZenUML diagram is not rendered.
- Updated dependencies [[`b9ef683`](https://github.com/mermaid-js/mermaid/commit/b9ef683fb67b8959abc455d6cc5266c37ba435f6), [`2c0931d`](https://github.com/mermaid-js/mermaid/commit/2c0931da46794b49d2523211e25f782900c34e94), [`33e08da`](https://github.com/mermaid-js/mermaid/commit/33e08daf175125295a06b1b80279437004a4e865), [`814b68b`](https://github.com/mermaid-js/mermaid/commit/814b68b4a94813f7c6b3d7fb4559532a7bab2652), [`fce7cab`](https://github.com/mermaid-js/mermaid/commit/fce7cabb71d68a20a66246fe23d066512126a412), [`fc07f0d`](https://github.com/mermaid-js/mermaid/commit/fc07f0d8abca49e4f887d7457b7b94fb07d1e3da), [`12e01bd`](https://github.com/mermaid-js/mermaid/commit/12e01bdb5cacf3569133979a5a4f1d8973e9aec1), [`01aaef3`](https://github.com/mermaid-js/mermaid/commit/01aaef39b4a1ec8bc5a0c6bfa3a20b712d67f4dc), [`daf8d8d`](https://github.com/mermaid-js/mermaid/commit/daf8d8d3befcd600618a629977b76463b38d0ad9), [`c36cd05`](https://github.com/mermaid-js/mermaid/commit/c36cd05c45ac3090181152b4dae41f8d7b569bd6), [`8bb29fc`](https://github.com/mermaid-js/mermaid/commit/8bb29fc879329ad109898e4025b4f4eba2ab0649), [`71b04f9`](https://github.com/mermaid-js/mermaid/commit/71b04f93b07f876df2b30656ef36036c1d0e4e4f), [`c99bce6`](https://github.com/mermaid-js/mermaid/commit/c99bce6bab4c7ce0b81b66d44f44853ce4aeb1c3), [`6cc1926`](https://github.com/mermaid-js/mermaid/commit/6cc192680a2531cab28f87a8061a53b786e010f3), [`9da6fb3`](https://github.com/mermaid-js/mermaid/commit/9da6fb39ae278401771943ac85d6d1b875f78cf1), [`e48b0ba`](https://github.com/mermaid-js/mermaid/commit/e48b0ba61dab7f95aa02da603b5b7d383b894932), [`4d62d59`](https://github.com/mermaid-js/mermaid/commit/4d62d5963238400270e9314c6e4d506f48147074), [`e9ce8cf`](https://github.com/mermaid-js/mermaid/commit/e9ce8cf4da9062d85098042044822100889bb0dd), [`9258b29`](https://github.com/mermaid-js/mermaid/commit/9258b2933bbe1ef41087345ffea3731673671c49), [`da90f67`](https://github.com/mermaid-js/mermaid/commit/da90f6760b6efb0da998bcb63b75eecc29e06c08), [`0133f1c`](https://github.com/mermaid-js/mermaid/commit/0133f1c0c5cff4fc4c8e0b99e9cf0b3d49dcbe71), [`895f9d4`](https://github.com/mermaid-js/mermaid/commit/895f9d43ff98ca05ebfba530789f677f31a011ff)]:
- mermaid@11.10.0
## 0.2.1
### Patch Changes

View File

@@ -1 +0,0 @@
../mermaid/src/docs/syntax/zenuml.md

View File

@@ -0,0 +1,384 @@
# @mermaid-js/mermaid-zenuml
MermaidJS plugin for ZenUML integration - A powerful sequence diagram rendering engine.
> A Sequence diagram is an interaction diagram that shows how processes operate with one another and in what order.
Mermaid can render sequence diagrams with [ZenUML](https://zenuml.com). Note that ZenUML uses a different
syntax than the original Sequence Diagram in mermaid.
```mermaid
zenuml
BookLibService.Borrow(id) {
User = Session.GetUser()
if(User.isActive) {
try {
BookRepository.Update(id, onLoan, User)
receipt = new Receipt(id, dueDate)
} catch (BookNotFoundException) {
ErrorService.onException(BookNotFoundException)
} finally {
Connection.close()
}
}
return receipt
}
```
## Installation
### With bundlers
```sh
npm install @mermaid-js/mermaid-zenuml
```
```ts
import mermaid from 'mermaid';
import zenuml from '@mermaid-js/mermaid-zenuml';
await mermaid.registerExternalDiagrams([zenuml]);
```
### With CDN
```html
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
import zenuml from 'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-zenuml@0.2.0/dist/mermaid-zenuml.core.mjs';
await mermaid.registerExternalDiagrams([zenuml]);
</script>
```
> [!NOTE]
> ZenUML uses experimental lazy loading & async rendering features which could change in the future.
## Basic Usage
Once the plugin is registered, you can create ZenUML diagrams using the `zenuml` syntax:
```mermaid
zenuml
Controller.Get(id) {
Service.Get(id) {
item = Repository.Get(id)
if(item) {
return item
} else {
return null
}
}
return result
}
```
## ZenUML Syntax Reference
### Participants
The participants can be defined implicitly as in the first example on this page. The participants or actors are
rendered in order of appearance in the diagram source text. Sometimes you might want to show the participants in a
different order than how they appear in the first message. It is possible to specify the actor's order of
appearance by doing the following:
```mermaid
zenuml
title Declare participant (optional)
Bob
Alice
Alice->Bob: Hi Bob
Bob->Alice: Hi Alice
```
### Annotators
If you specifically want to use symbols instead of just rectangles with text you can do so by using the annotator syntax to declare participants as per below.
```mermaid
zenuml
title Annotators
@Actor Alice
@Database Bob
Alice->Bob: Hi Bob
Bob->Alice: Hi Alice
```
Available annotators include:
- `@Actor` - Human figure
- `@Database` - Database symbol
- `@Boundary` - Boundary symbol
- `@Control` - Control symbol
- `@Entity` - Entity symbol
- `@Queue` - Queue symbol
### Aliases
The participants can have a convenient identifier and a descriptive label.
```mermaid
zenuml
title Aliases
A as Alice
J as John
A->J: Hello John, how are you?
J->A: Great!
```
## Messages
Messages can be one of:
1. Sync message
2. Async message
3. Creation message
4. Reply message
### Sync message
You can think of a sync (blocking) method in a programming language.
```mermaid
zenuml
title Sync message
A.SyncMessage
A.SyncMessage(with, parameters) {
B.nestedSyncMessage()
}
```
### Async message
You can think of an async (non-blocking) method in a programming language. Fire an event and forget about it.
```mermaid
zenuml
title Async message
Alice->Bob: How are you?
```
### Creation message
We use `new` keyword to create an object.
```mermaid
zenuml
new A1
new A2(with, parameters)
```
### Reply message
There are three ways to express a reply message:
```mermaid
zenuml
// 1. assign a variable from a sync message.
a = A.SyncMessage()
// 1.1. optionally give the variable a type
SomeType a = A.SyncMessage()
// 2. use return keyword
A.SyncMessage() {
return result
}
// 3. use @return or @reply annotator on an async message
@return
A->B: result
```
The third way `@return` is rarely used, but it is useful when you want to return to one level up.
```mermaid
zenuml
title Reply message
Client->A.method() {
B.method() {
if(condition) {
return x1
// return early
@return
A->Client: x11
}
}
return x2
}
```
## Advanced Features
### Nesting
Sync messages and Creation messages are naturally nestable with `{}`.
```mermaid
zenuml
A.method() {
B.nested_sync_method()
B->C: nested async message
}
```
### Comments
It is possible to add comments to a sequence diagram with `// comment` syntax.
Comments will be rendered above the messages or fragments. Comments on other places
are ignored. Markdown is supported.
```mermaid
zenuml
// a comment on a participant will not be rendered
BookService
// a comment on a message.
// **Markdown** is supported.
BookService.getBook()
```
### Loops
It is possible to express loops in a ZenUML diagram. This is done by any of the
following notations:
1. while
2. for
3. forEach, foreach
4. loop
```zenuml
while(condition) {
...statements...
}
```
Example:
```mermaid
zenuml
Alice->John: Hello John, how are you?
while(true) {
John->Alice: Great!
}
```
### Alt (Alternative paths)
It is possible to express alternative paths in a sequence diagram. This is done by the notation
```zenuml
if(condition1) {
...statements...
} else if(condition2) {
...statements...
} else {
...statements...
}
```
Example:
```mermaid
zenuml
Alice->Bob: Hello Bob, how are you?
if(is_sick) {
Bob->Alice: Not so good :(
} else {
Bob->Alice: Feeling fresh like a daisy
}
```
### Opt (Optional)
It is possible to render an `opt` fragment. This is done by the notation
```zenuml
opt {
...statements...
}
```
Example:
```mermaid
zenuml
Alice->Bob: Hello Bob, how are you?
Bob->Alice: Not so good :(
opt {
Bob->Alice: Thanks for asking
}
```
### Parallel
It is possible to show actions that are happening in parallel.
This is done by the notation
```zenuml
par {
statement1
statement2
statement3
}
```
Example:
```mermaid
zenuml
par {
Alice->Bob: Hello guys!
Alice->John: Hello guys!
}
```
### Try/Catch/Finally (Break)
It is possible to indicate a stop of the sequence within the flow (usually used to model exceptions).
This is done by the notation
```
try {
...statements...
} catch {
...statements...
} finally {
...statements...
}
```
Example:
```mermaid
zenuml
try {
Consumer->API: Book something
API->BookingService: Start booking process
} catch {
API->Consumer: show failure
} finally {
API->BookingService: rollback status
}
```
## Contributing
This package is part of the [Mermaid](https://github.com/mermaid-js/mermaid) project. See the main repository for contributing guidelines.
## Contributors
- [Peng Xiao](https://github.com/MrCoder)
- [Sidharth Vinod](https://sidharth.dev)
- [Dong Cai](https://github.com/dontry)
## License
MIT
## Links
- [ZenUML Official Website](https://zenuml.com)
- [Mermaid Documentation](https://mermaid.js.org)
- [GitHub Repository](https://github.com/mermaid-js/mermaid)

View File

@@ -1,6 +1,6 @@
{
"name": "@mermaid-js/mermaid-zenuml",
"version": "0.2.1",
"version": "0.2.2",
"description": "MermaidJS plugin for ZenUML integration",
"module": "dist/mermaid-zenuml.core.mjs",
"types": "dist/detector.d.ts",
@@ -33,7 +33,7 @@
],
"license": "MIT",
"dependencies": {
"@zenuml/core": "^3.31.1"
"@zenuml/core": "^3.35.2"
},
"devDependencies": {
"mermaid": "workspace:^"

11
packages/mermaid-zenuml/src/zenuml.d.ts vendored Normal file
View File

@@ -0,0 +1,11 @@
declare module '@zenuml/core' {
interface RenderOptions {
theme?: string;
mode?: string;
}
export default class ZenUml {
constructor(container: Element);
render(text: string, options?: RenderOptions): Promise<void>;
}
}

View File

@@ -53,7 +53,6 @@ export const draw = async function (text: string, id: string) {
const { foreignObject, container, app } = createForeignObject(id);
svgContainer.appendChild(foreignObject);
// @ts-expect-error @zenuml/core@3.0.0 exports the wrong type for ZenUml
const zenuml = new ZenUml(app);
// default is a theme name. More themes to be added and will be configurable in the future
await zenuml.render(text, { theme: 'default', mode: 'static' });

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "11.9.0",
"version": "11.10.0",
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
"type": "module",
"module": "./dist/mermaid.core.mjs",

View File

@@ -109,6 +109,16 @@ export interface MermaidConfig {
| 'INTERACTIVE'
| 'MODEL_ORDER'
| 'GREEDY_MODEL_ORDER';
/**
* The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES.
*
*/
forceNodeModelOrder?: boolean;
/**
* Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting.
*
*/
considerModelOrder?: 'NONE' | 'NODES_AND_EDGES' | 'PREFER_EDGES' | 'PREFER_NODES';
};
darkMode?: boolean;
htmlLabels?: boolean;

View File

@@ -1,9 +1,9 @@
import { select } from 'd3';
import { log } from '../logger.js';
import { getConfig } from '../diagram-api/diagramAPI.js';
import { evaluate } from '../diagrams/common/common.js';
import { decodeEntities } from '../utils.js';
import { evaluate, sanitizeText } from '../diagrams/common/common.js';
import { log } from '../logger.js';
import { replaceIconSubstring } from '../rendering-util/createText.js';
import { decodeEntities } from '../utils.js';
/**
* @param dom
@@ -19,14 +19,14 @@ function applyStyle(dom, styleFn) {
* @param {any} node
* @returns {SVGForeignObjectElement} Node
*/
function addHtmlLabel(node) {
function addHtmlLabel(node, config) {
const fo = select(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'));
const div = fo.append('xhtml:div');
const label = node.label;
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
const span = div.append('span');
span.html(label);
span.html(sanitizeText(label, config));
applyStyle(span, node.labelStyle);
span.attr('class', labelClass);
@@ -49,7 +49,8 @@ const createLabel = async (_vertexText, style, isTitle, isNode) => {
if (typeof vertexText === 'object') {
vertexText = vertexText[0];
}
if (evaluate(getConfig().flowchart.htmlLabels)) {
const config = getConfig();
if (evaluate(config.flowchart.htmlLabels)) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
vertexText = vertexText.replace(/\\n|\n/g, '<br />');
log.debug('vertexText' + vertexText);
@@ -59,7 +60,7 @@ const createLabel = async (_vertexText, style, isTitle, isNode) => {
label,
labelStyle: style.replace('fill:', 'color:'),
};
let vertexNode = addHtmlLabel(node);
let vertexNode = addHtmlLabel(node, config);
// vertexNode.parentNode.removeChild(vertexNode);
return vertexNode;
} else {

View File

@@ -24,6 +24,8 @@ const config: RequiredDeep<MermaidConfig> = {
// mergeEdges is needed here to be considered
mergeEdges: false,
nodePlacementStrategy: 'BRANDES_KOEPF',
forceNodeModelOrder: false,
considerModelOrder: 'NODES_AND_EDGES',
},
themeCSS: undefined,

View File

@@ -78,5 +78,41 @@ describe('diagram-orchestration', () => {
flowchart: 1 "pie" pie: 2 "pie"`)
).toBe('pie');
});
it('should detect proper diagram when defaultRenderer is elk for flowchart', () => {
expect(
detectType('mindmap\n root\n Photograph\n Waterfall', {
flowchart: { defaultRenderer: 'elk' },
})
).toBe('mindmap');
expect(
detectType(
`
classDiagram
class Person {
+String name
-Int id
#double age
+Text demographicProfile
}
`,
{ flowchart: { defaultRenderer: 'elk' } }
)
).toBe('class');
expect(
detectType(
`
erDiagram
p[Photograph] {
varchar(12) jobId
date dateCreated
}
`,
{
flowchart: { defaultRenderer: 'elk' },
}
)
).toBe('er');
});
});
});

View File

@@ -1,21 +1,12 @@
import { it, describe, expect } from 'vitest';
import { db } from './architectureDb.js';
import { parser } from './architectureParser.js';
const {
clear,
getDiagramTitle,
getAccTitle,
getAccDescription,
getServices,
getGroups,
getEdges,
getJunctions,
} = db;
import { ArchitectureDB } from './architectureDb.js';
describe('architecture diagrams', () => {
let db: ArchitectureDB;
beforeEach(() => {
clear();
db = new ArchitectureDB();
// @ts-expect-error since type is set to undefined we will have error
parser.parser?.yy = db;
});
describe('architecture diagram definitions', () => {
@@ -36,7 +27,7 @@ describe('architecture diagrams', () => {
it('should handle title on the first line', async () => {
const str = `architecture-beta title Simple Architecture Diagram`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getDiagramTitle()).toBe('Simple Architecture Diagram');
expect(db.getDiagramTitle()).toBe('Simple Architecture Diagram');
});
it('should handle title on another line', async () => {
@@ -44,7 +35,7 @@ describe('architecture diagrams', () => {
title Simple Architecture Diagram
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getDiagramTitle()).toBe('Simple Architecture Diagram');
expect(db.getDiagramTitle()).toBe('Simple Architecture Diagram');
});
it('should handle accessibility title and description', async () => {
@@ -53,8 +44,8 @@ describe('architecture diagrams', () => {
accDescr: Accessibility Description
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getAccTitle()).toBe('Accessibility Title');
expect(getAccDescription()).toBe('Accessibility Description');
expect(db.getAccTitle()).toBe('Accessibility Title');
expect(db.getAccDescription()).toBe('Accessibility Description');
});
it('should handle multiline accessibility description', async () => {
@@ -64,7 +55,7 @@ describe('architecture diagrams', () => {
}
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getAccDescription()).toBe('Accessibility Description');
expect(db.getAccDescription()).toBe('Accessibility Description');
});
});
});

View File

@@ -1,8 +1,9 @@
import { getConfig as commonGetConfig } from '../../config.js';
import type { ArchitectureDiagramConfig } from '../../config.type.js';
import DEFAULT_CONFIG from '../../defaultConfig.js';
import { getConfig as commonGetConfig } from '../../config.js';
import type { DiagramDB } from '../../diagram-api/types.js';
import type { D3Element } from '../../types.js';
import { ImperativeState } from '../../utils/imperativeState.js';
import { cleanAndMerge } from '../../utils.js';
import {
clear as commonClear,
getAccDescription,
@@ -14,7 +15,6 @@ import {
} from '../common/commonDb.js';
import type {
ArchitectureAlignment,
ArchitectureDB,
ArchitectureDirectionPair,
ArchitectureDirectionPairMap,
ArchitectureEdge,
@@ -33,330 +33,333 @@ import {
isArchitectureService,
shiftPositionByArchitectureDirectionPair,
} from './architectureTypes.js';
import { cleanAndMerge } from '../../utils.js';
const DEFAULT_ARCHITECTURE_CONFIG: Required<ArchitectureDiagramConfig> =
DEFAULT_CONFIG.architecture;
export class ArchitectureDB implements DiagramDB {
private nodes: Record<string, ArchitectureNode> = {};
private groups: Record<string, ArchitectureGroup> = {};
private edges: ArchitectureEdge[] = [];
private registeredIds: Record<string, 'node' | 'group'> = {};
private dataStructures?: ArchitectureState['dataStructures'];
private elements: Record<string, D3Element> = {};
const state = new ImperativeState<ArchitectureState>(() => ({
nodes: {},
groups: {},
edges: [],
registeredIds: {},
config: DEFAULT_ARCHITECTURE_CONFIG,
dataStructures: undefined,
elements: {},
}));
const clear = (): void => {
state.reset();
commonClear();
};
const addService = function ({
id,
icon,
in: parent,
title,
iconText,
}: Omit<ArchitectureService, 'edges'>) {
if (state.records.registeredIds[id] !== undefined) {
throw new Error(
`The service id [${id}] is already in use by another ${state.records.registeredIds[id]}`
);
}
if (parent !== undefined) {
if (id === parent) {
throw new Error(`The service [${id}] cannot be placed within itself`);
}
if (state.records.registeredIds[parent] === undefined) {
throw new Error(
`The service [${id}]'s parent does not exist. Please make sure the parent is created before this service`
);
}
if (state.records.registeredIds[parent] === 'node') {
throw new Error(`The service [${id}]'s parent is not a group`);
}
constructor() {
this.clear();
}
state.records.registeredIds[id] = 'node';
public clear(): void {
this.nodes = {};
this.groups = {};
this.edges = [];
this.registeredIds = {};
this.dataStructures = undefined;
this.elements = {};
commonClear();
}
state.records.nodes[id] = {
public addService({
id,
type: 'service',
icon,
in: parent,
title,
iconText,
title,
edges: [],
in: parent,
};
};
const getServices = (): ArchitectureService[] =>
Object.values(state.records.nodes).filter<ArchitectureService>(isArchitectureService);
const addJunction = function ({ id, in: parent }: Omit<ArchitectureJunction, 'edges'>) {
state.records.registeredIds[id] = 'node';
state.records.nodes[id] = {
id,
type: 'junction',
edges: [],
in: parent,
};
};
const getJunctions = (): ArchitectureJunction[] =>
Object.values(state.records.nodes).filter<ArchitectureJunction>(isArchitectureJunction);
const getNodes = (): ArchitectureNode[] => Object.values(state.records.nodes);
const getNode = (id: string): ArchitectureNode | null => state.records.nodes[id];
const addGroup = function ({ id, icon, in: parent, title }: ArchitectureGroup) {
if (state.records.registeredIds[id] !== undefined) {
throw new Error(
`The group id [${id}] is already in use by another ${state.records.registeredIds[id]}`
);
}
if (parent !== undefined) {
if (id === parent) {
throw new Error(`The group [${id}] cannot be placed within itself`);
}
if (state.records.registeredIds[parent] === undefined) {
}: Omit<ArchitectureService, 'edges'>): void {
if (this.registeredIds[id] !== undefined) {
throw new Error(
`The group [${id}]'s parent does not exist. Please make sure the parent is created before this group`
`The service id [${id}] is already in use by another ${this.registeredIds[id]}`
);
}
if (state.records.registeredIds[parent] === 'node') {
throw new Error(`The group [${id}]'s parent is not a group`);
if (parent !== undefined) {
if (id === parent) {
throw new Error(`The service [${id}] cannot be placed within itself`);
}
if (this.registeredIds[parent] === undefined) {
throw new Error(
`The service [${id}]'s parent does not exist. Please make sure the parent is created before this service`
);
}
if (this.registeredIds[parent] === 'node') {
throw new Error(`The service [${id}]'s parent is not a group`);
}
}
this.registeredIds[id] = 'node';
this.nodes[id] = {
id,
type: 'service',
icon,
iconText,
title,
edges: [],
in: parent,
};
}
state.records.registeredIds[id] = 'group';
state.records.groups[id] = {
id,
icon,
title,
in: parent,
};
};
const getGroups = (): ArchitectureGroup[] => {
return Object.values(state.records.groups);
};
const addEdge = function ({
lhsId,
rhsId,
lhsDir,
rhsDir,
lhsInto,
rhsInto,
lhsGroup,
rhsGroup,
title,
}: ArchitectureEdge<string>) {
if (!isArchitectureDirection(lhsDir)) {
throw new Error(
`Invalid direction given for left hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${lhsDir}`
);
}
if (!isArchitectureDirection(rhsDir)) {
throw new Error(
`Invalid direction given for right hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${rhsDir}`
);
public getServices(): ArchitectureService[] {
return Object.values(this.nodes).filter(isArchitectureService);
}
if (state.records.nodes[lhsId] === undefined && state.records.groups[lhsId] === undefined) {
throw new Error(
`The left-hand id [${lhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
);
}
if (state.records.nodes[rhsId] === undefined && state.records.groups[lhsId] === undefined) {
throw new Error(
`The right-hand id [${rhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
);
public addJunction({ id, in: parent }: Omit<ArchitectureJunction, 'edges'>): void {
this.registeredIds[id] = 'node';
this.nodes[id] = {
id,
type: 'junction',
edges: [],
in: parent,
};
}
const lhsGroupId = state.records.nodes[lhsId].in;
const rhsGroupId = state.records.nodes[rhsId].in;
if (lhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
throw new Error(
`The left-hand id [${lhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
);
}
if (rhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
throw new Error(
`The right-hand id [${rhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
);
public getJunctions(): ArchitectureJunction[] {
return Object.values(this.nodes).filter(isArchitectureJunction);
}
const edge = {
public getNodes(): ArchitectureNode[] {
return Object.values(this.nodes);
}
public getNode(id: string): ArchitectureNode | null {
return this.nodes[id] ?? null;
}
public addGroup({ id, icon, in: parent, title }: ArchitectureGroup): void {
if (this.registeredIds?.[id] !== undefined) {
throw new Error(
`The group id [${id}] is already in use by another ${this.registeredIds[id]}`
);
}
if (parent !== undefined) {
if (id === parent) {
throw new Error(`The group [${id}] cannot be placed within itself`);
}
if (this.registeredIds?.[parent] === undefined) {
throw new Error(
`The group [${id}]'s parent does not exist. Please make sure the parent is created before this group`
);
}
if (this.registeredIds?.[parent] === 'node') {
throw new Error(`The group [${id}]'s parent is not a group`);
}
}
this.registeredIds[id] = 'group';
this.groups[id] = {
id,
icon,
title,
in: parent,
};
}
public getGroups(): ArchitectureGroup[] {
return Object.values(this.groups);
}
public addEdge({
lhsId,
lhsDir,
lhsInto,
lhsGroup,
rhsId,
lhsDir,
rhsDir,
lhsInto,
rhsInto,
lhsGroup,
rhsGroup,
title,
};
state.records.edges.push(edge);
if (state.records.nodes[lhsId] && state.records.nodes[rhsId]) {
state.records.nodes[lhsId].edges.push(state.records.edges[state.records.edges.length - 1]);
state.records.nodes[rhsId].edges.push(state.records.edges[state.records.edges.length - 1]);
}
};
const getEdges = (): ArchitectureEdge[] => state.records.edges;
/**
* Returns the current diagram's adjacency list, spatial map, & group alignments.
* If they have not been created, run the algorithms to generate them.
* @returns
*/
const getDataStructures = () => {
if (state.records.dataStructures === undefined) {
// Tracks how groups are aligned with one another. Generated while creating the adj list
const groupAlignments: Record<
string,
Record<string, Exclude<ArchitectureAlignment, 'bend'>>
> = {};
// Create an adjacency list of the diagram to perform BFS on
// Outer reduce applied on all services
// Inner reduce applied on the edges for a service
const adjList = Object.entries(state.records.nodes).reduce<
Record<string, ArchitectureDirectionPairMap>
>((prevOuter, [id, service]) => {
prevOuter[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prevInner, edge) => {
// track the direction groups connect to one another
const lhsGroupId = getNode(edge.lhsId)?.in;
const rhsGroupId = getNode(edge.rhsId)?.in;
if (lhsGroupId && rhsGroupId && lhsGroupId !== rhsGroupId) {
const alignment = getArchitectureDirectionAlignment(edge.lhsDir, edge.rhsDir);
if (alignment !== 'bend') {
groupAlignments[lhsGroupId] ??= {};
groupAlignments[lhsGroupId][rhsGroupId] = alignment;
groupAlignments[rhsGroupId] ??= {};
groupAlignments[rhsGroupId][lhsGroupId] = alignment;
}
}
if (edge.lhsId === id) {
// source is LHS
const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir);
if (pair) {
prevInner[pair] = edge.rhsId;
}
} else {
// source is RHS
const pair = getArchitectureDirectionPair(edge.rhsDir, edge.lhsDir);
if (pair) {
prevInner[pair] = edge.lhsId;
}
}
return prevInner;
}, {});
return prevOuter;
}, {});
// Configuration for the initial pass of BFS
const firstId = Object.keys(adjList)[0];
const visited = { [firstId]: 1 };
// If a key is present in this object, it has not been visited
const notVisited = Object.keys(adjList).reduce(
(prev, id) => (id === firstId ? prev : { ...prev, [id]: 1 }),
{} as Record<string, number>
);
// Perform BFS on the adjacency list
const BFS = (startingId: string): ArchitectureSpatialMap => {
const spatialMap = { [startingId]: [0, 0] };
const queue = [startingId];
while (queue.length > 0) {
const id = queue.shift();
if (id) {
visited[id] = 1;
delete notVisited[id];
const adj = adjList[id];
const [posX, posY] = spatialMap[id];
Object.entries(adj).forEach(([dir, rhsId]) => {
if (!visited[rhsId]) {
spatialMap[rhsId] = shiftPositionByArchitectureDirectionPair(
[posX, posY],
dir as ArchitectureDirectionPair
);
queue.push(rhsId);
}
});
}
}
return spatialMap;
};
const spatialMaps = [BFS(firstId)];
// If our diagram is disconnected, keep adding additional spatial maps until all disconnected graphs have been found
while (Object.keys(notVisited).length > 0) {
spatialMaps.push(BFS(Object.keys(notVisited)[0]));
}: ArchitectureEdge): void {
if (!isArchitectureDirection(lhsDir)) {
throw new Error(
`Invalid direction given for left hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${String(lhsDir)}`
);
}
state.records.dataStructures = {
adjList,
spatialMaps,
groupAlignments,
if (!isArchitectureDirection(rhsDir)) {
throw new Error(
`Invalid direction given for right hand side of edge ${lhsId}--${rhsId}. Expected (L,R,T,B) got ${String(rhsDir)}`
);
}
if (this.nodes[lhsId] === undefined && this.groups[lhsId] === undefined) {
throw new Error(
`The left-hand id [${lhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
);
}
if (this.nodes[rhsId] === undefined && this.groups[rhsId] === undefined) {
throw new Error(
`The right-hand id [${rhsId}] does not yet exist. Please create the service/group before declaring an edge to it.`
);
}
const lhsGroupId = this.nodes[lhsId].in;
const rhsGroupId = this.nodes[rhsId].in;
if (lhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
throw new Error(
`The left-hand id [${lhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
);
}
if (rhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) {
throw new Error(
`The right-hand id [${rhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.`
);
}
const edge = {
lhsId,
lhsDir,
lhsInto,
lhsGroup,
rhsId,
rhsDir,
rhsInto,
rhsGroup,
title,
};
this.edges.push(edge);
if (this.nodes[lhsId] && this.nodes[rhsId]) {
this.nodes[lhsId].edges.push(this.edges[this.edges.length - 1]);
this.nodes[rhsId].edges.push(this.edges[this.edges.length - 1]);
}
}
return state.records.dataStructures;
};
const setElementForId = (id: string, element: D3Element) => {
state.records.elements[id] = element;
};
const getElementById = (id: string) => state.records.elements[id];
public getEdges(): ArchitectureEdge[] {
return this.edges;
}
const getConfig = (): Required<ArchitectureDiagramConfig> => {
const config = cleanAndMerge({
...DEFAULT_ARCHITECTURE_CONFIG,
...commonGetConfig().architecture,
});
return config;
};
/**
* Returns the current diagram's adjacency list, spatial map, & group alignments.
* If they have not been created, run the algorithms to generate them.
* @returns
*/
public getDataStructures() {
if (this.dataStructures === undefined) {
// Tracks how groups are aligned with one another. Generated while creating the adj list
const groupAlignments: Record<
string,
Record<string, Exclude<ArchitectureAlignment, 'bend'>>
> = {};
export const db: ArchitectureDB = {
clear,
setDiagramTitle,
getDiagramTitle,
setAccTitle,
getAccTitle,
setAccDescription,
getAccDescription,
getConfig,
// Create an adjacency list of the diagram to perform BFS on
// Outer reduce applied on all services
// Inner reduce applied on the edges for a service
const adjList = Object.entries(this.nodes).reduce<
Record<string, ArchitectureDirectionPairMap>
>((prevOuter, [id, service]) => {
prevOuter[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prevInner, edge) => {
// track the direction groups connect to one another
const lhsGroupId = this.getNode(edge.lhsId)?.in;
const rhsGroupId = this.getNode(edge.rhsId)?.in;
if (lhsGroupId && rhsGroupId && lhsGroupId !== rhsGroupId) {
const alignment = getArchitectureDirectionAlignment(edge.lhsDir, edge.rhsDir);
if (alignment !== 'bend') {
groupAlignments[lhsGroupId] ??= {};
groupAlignments[lhsGroupId][rhsGroupId] = alignment;
groupAlignments[rhsGroupId] ??= {};
groupAlignments[rhsGroupId][lhsGroupId] = alignment;
}
}
addService,
getServices,
addJunction,
getJunctions,
getNodes,
getNode,
addGroup,
getGroups,
addEdge,
getEdges,
setElementForId,
getElementById,
getDataStructures,
};
if (edge.lhsId === id) {
// source is LHS
const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir);
if (pair) {
prevInner[pair] = edge.rhsId;
}
} else {
// source is RHS
const pair = getArchitectureDirectionPair(edge.rhsDir, edge.lhsDir);
if (pair) {
prevInner[pair] = edge.lhsId;
}
}
return prevInner;
}, {});
return prevOuter;
}, {});
// Configuration for the initial pass of BFS
const firstId = Object.keys(adjList)[0];
const visited = { [firstId]: 1 };
// If a key is present in this object, it has not been visited
const notVisited = Object.keys(adjList).reduce(
(prev, id) => (id === firstId ? prev : { ...prev, [id]: 1 }),
{} as Record<string, number>
);
// Perform BFS on the adjacency list
const BFS = (startingId: string): ArchitectureSpatialMap => {
const spatialMap = { [startingId]: [0, 0] };
const queue = [startingId];
while (queue.length > 0) {
const id = queue.shift();
if (id) {
visited[id] = 1;
delete notVisited[id];
const adj = adjList[id];
const [posX, posY] = spatialMap[id];
Object.entries(adj).forEach(([dir, rhsId]) => {
if (!visited[rhsId]) {
spatialMap[rhsId] = shiftPositionByArchitectureDirectionPair(
[posX, posY],
dir as ArchitectureDirectionPair
);
queue.push(rhsId);
}
});
}
}
return spatialMap;
};
const spatialMaps = [BFS(firstId)];
// If our diagram is disconnected, keep adding additional spatial maps until all disconnected graphs have been found
while (Object.keys(notVisited).length > 0) {
spatialMaps.push(BFS(Object.keys(notVisited)[0]));
}
this.dataStructures = {
adjList,
spatialMaps,
groupAlignments,
};
}
return this.dataStructures;
}
public setElementForId(id: string, element: D3Element): void {
this.elements[id] = element;
}
public getElementById(id: string): D3Element {
return this.elements[id];
}
public getConfig(): Required<ArchitectureDiagramConfig> {
return cleanAndMerge({
...DEFAULT_ARCHITECTURE_CONFIG,
...commonGetConfig().architecture,
});
}
public getConfigField<T extends keyof ArchitectureDiagramConfig>(
field: T
): Required<ArchitectureDiagramConfig>[T] {
return this.getConfig()[field];
}
public setAccTitle = setAccTitle;
public getAccTitle = getAccTitle;
public setDiagramTitle = setDiagramTitle;
public getDiagramTitle = getDiagramTitle;
public getAccDescription = getAccDescription;
public setAccDescription = setAccDescription;
}
/**
* Typed wrapper for resolving an architecture diagram's config fields. Returns the default value if undefined
* @param field - the config field to access
* @returns
*/
export function getConfigField<T extends keyof ArchitectureDiagramConfig>(
field: T
): Required<ArchitectureDiagramConfig>[T] {
return getConfig()[field];
}
// export function getConfigField<T extends keyof ArchitectureDiagramConfig>(
// field: T
// ): Required<ArchitectureDiagramConfig>[T] {
// return db.getConfig()[field];
// }

View File

@@ -1,12 +1,14 @@
import type { DiagramDefinition } from '../../diagram-api/types.js';
import { parser } from './architectureParser.js';
import { db } from './architectureDb.js';
import { ArchitectureDB } from './architectureDb.js';
import styles from './architectureStyles.js';
import { renderer } from './architectureRenderer.js';
export const diagram: DiagramDefinition = {
parser,
db,
get db() {
return new ArchitectureDB();
},
renderer,
styles,
};

View File

@@ -1,24 +1,33 @@
import type { Architecture } from '@mermaid-js/parser';
import { parse } from '@mermaid-js/parser';
import { log } from '../../logger.js';
import type { ParserDefinition } from '../../diagram-api/types.js';
import { log } from '../../logger.js';
import { populateCommonDb } from '../common/populateCommonDb.js';
import type { ArchitectureDB } from './architectureTypes.js';
import { db } from './architectureDb.js';
import { ArchitectureDB } from './architectureDb.js';
const populateDb = (ast: Architecture, db: ArchitectureDB) => {
populateCommonDb(ast, db);
ast.groups.map(db.addGroup);
ast.groups.map((group) => db.addGroup(group));
ast.services.map((service) => db.addService({ ...service, type: 'service' }));
ast.junctions.map((service) => db.addJunction({ ...service, type: 'junction' }));
// @ts-ignore TODO our parser guarantees the type is L/R/T/B and not string. How to change to union type?
ast.edges.map(db.addEdge);
ast.edges.map((edge) => db.addEdge(edge));
};
export const parser: ParserDefinition = {
parser: {
// @ts-expect-error - ArchitectureDB is not assignable to DiagramDB
yy: undefined,
},
parse: async (input: string): Promise<void> => {
const ast: Architecture = await parse('architecture', input);
log.debug(ast);
const db = parser.parser?.yy;
if (!(db instanceof ArchitectureDB)) {
throw new Error(
'parser.parser?.yy was not a ArchitectureDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.'
);
}
populateDb(ast, db);
},
};

View File

@@ -1,4 +1,3 @@
import { registerIconPacks } from '../../rendering-util/icons.js';
import type { Position } from 'cytoscape';
import cytoscape from 'cytoscape';
import type { FcoseLayoutOptions } from 'cytoscape-fcose';
@@ -7,9 +6,10 @@ import { select } from 'd3';
import type { DrawDefinition, SVG } from '../../diagram-api/types.js';
import type { Diagram } from '../../Diagram.js';
import { log } from '../../logger.js';
import { registerIconPacks } from '../../rendering-util/icons.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import { getConfigField } from './architectureDb.js';
import type { ArchitectureDB } from './architectureDb.js';
import { architectureIcons } from './architectureIcons.js';
import type {
ArchitectureAlignment,
@@ -22,7 +22,6 @@ import type {
NodeSingularData,
} from './architectureTypes.js';
import {
type ArchitectureDB,
type ArchitectureDirection,
type ArchitectureEdge,
type ArchitectureGroup,
@@ -44,7 +43,7 @@ registerIconPacks([
]);
cytoscape.use(fcose);
function addServices(services: ArchitectureService[], cy: cytoscape.Core) {
function addServices(services: ArchitectureService[], cy: cytoscape.Core, db: ArchitectureDB) {
services.forEach((service) => {
cy.add({
group: 'nodes',
@@ -54,15 +53,15 @@ function addServices(services: ArchitectureService[], cy: cytoscape.Core) {
icon: service.icon,
label: service.title,
parent: service.in,
width: getConfigField('iconSize'),
height: getConfigField('iconSize'),
width: db.getConfigField('iconSize'),
height: db.getConfigField('iconSize'),
} as NodeSingularData,
classes: 'node-service',
});
});
}
function addJunctions(junctions: ArchitectureJunction[], cy: cytoscape.Core) {
function addJunctions(junctions: ArchitectureJunction[], cy: cytoscape.Core, db: ArchitectureDB) {
junctions.forEach((junction) => {
cy.add({
group: 'nodes',
@@ -70,8 +69,8 @@ function addJunctions(junctions: ArchitectureJunction[], cy: cytoscape.Core) {
type: 'junction',
id: junction.id,
parent: junction.in,
width: getConfigField('iconSize'),
height: getConfigField('iconSize'),
width: db.getConfigField('iconSize'),
height: db.getConfigField('iconSize'),
} as NodeSingularData,
classes: 'node-junction',
});
@@ -257,7 +256,8 @@ function getAlignments(
}
function getRelativeConstraints(
spatialMaps: ArchitectureSpatialMap[]
spatialMaps: ArchitectureSpatialMap[],
db: ArchitectureDB
): fcose.FcoseRelativePlacementConstraint[] {
const relativeConstraints: fcose.FcoseRelativePlacementConstraint[] = [];
const posToStr = (pos: number[]) => `${pos[0]},${pos[1]}`;
@@ -296,7 +296,7 @@ function getRelativeConstraints(
[ArchitectureDirectionName[
getOppositeArchitectureDirection(dir as ArchitectureDirection)
]]: currId,
gap: 1.5 * getConfigField('iconSize'),
gap: 1.5 * db.getConfigField('iconSize'),
});
}
});
@@ -353,7 +353,7 @@ function layoutArchitecture(
style: {
'text-valign': 'bottom',
'text-halign': 'center',
'font-size': `${getConfigField('fontSize')}px`,
'font-size': `${db.getConfigField('fontSize')}px`,
},
},
{
@@ -375,7 +375,7 @@ function layoutArchitecture(
selector: '.node-group',
style: {
// @ts-ignore Incorrect library types
padding: `${getConfigField('padding')}px`,
padding: `${db.getConfigField('padding')}px`,
},
},
],
@@ -393,14 +393,14 @@ function layoutArchitecture(
renderEl.remove();
addGroups(groups, cy);
addServices(services, cy);
addJunctions(junctions, cy);
addServices(services, cy, db);
addJunctions(junctions, cy, db);
addEdges(edges, cy);
// Use the spatial map to create alignment arrays for fcose
const alignmentConstraint = getAlignments(db, spatialMaps, groupAlignments);
// Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it
const relativePlacementConstraint = getRelativeConstraints(spatialMaps);
const relativePlacementConstraint = getRelativeConstraints(spatialMaps, db);
const layout = cy.layout({
name: 'fcose',
@@ -415,7 +415,9 @@ function layoutArchitecture(
const { parent: parentA } = nodeData(nodeA);
const { parent: parentB } = nodeData(nodeB);
const elasticity =
parentA === parentB ? 1.5 * getConfigField('iconSize') : 0.5 * getConfigField('iconSize');
parentA === parentB
? 1.5 * db.getConfigField('iconSize')
: 0.5 * db.getConfigField('iconSize');
return elasticity;
},
edgeElasticity(edge: EdgeSingular) {
@@ -535,11 +537,11 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram)
const cy = await layoutArchitecture(services, junctions, groups, edges, db, ds);
await drawEdges(edgesElem, cy);
await drawGroups(groupElem, cy);
await drawEdges(edgesElem, cy, db);
await drawGroups(groupElem, cy, db);
positionNodes(db, cy);
setupGraphViewbox(undefined, svg, getConfigField('padding'), getConfigField('useMaxWidth'));
setupGraphViewbox(undefined, svg, db.getConfigField('padding'), db.getConfigField('useMaxWidth'));
};
export const renderer = { draw };

View File

@@ -1,9 +1,10 @@
import { getIconSVG } from '../../rendering-util/icons.js';
import type cytoscape from 'cytoscape';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import { createText } from '../../rendering-util/createText.js';
import { getIconSVG } from '../../rendering-util/icons.js';
import type { D3Element } from '../../types.js';
import { db, getConfigField } from './architectureDb.js';
import { sanitizeText } from '../common/common.js';
import type { ArchitectureDB } from './architectureDb.js';
import { architectureIcons } from './architectureIcons.js';
import {
ArchitectureDirectionArrow,
@@ -16,14 +17,17 @@ import {
isArchitectureDirectionY,
isArchitecturePairXY,
nodeData,
type ArchitectureDB,
type ArchitectureJunction,
type ArchitectureService,
} from './architectureTypes.js';
export const drawEdges = async function (edgesEl: D3Element, cy: cytoscape.Core) {
const padding = getConfigField('padding');
const iconSize = getConfigField('iconSize');
export const drawEdges = async function (
edgesEl: D3Element,
cy: cytoscape.Core,
db: ArchitectureDB
) {
const padding = db.getConfigField('padding');
const iconSize = db.getConfigField('iconSize');
const halfIconSize = iconSize / 2;
const arrowSize = iconSize / 6;
const halfArrowSize = arrowSize / 2;
@@ -183,13 +187,17 @@ export const drawEdges = async function (edgesEl: D3Element, cy: cytoscape.Core)
);
};
export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Core) {
const padding = getConfigField('padding');
export const drawGroups = async function (
groupsEl: D3Element,
cy: cytoscape.Core,
db: ArchitectureDB
) {
const padding = db.getConfigField('padding');
const groupIconSize = padding * 0.75;
const fontSize = getConfigField('fontSize');
const fontSize = db.getConfigField('fontSize');
const iconSize = getConfigField('iconSize');
const iconSize = db.getConfigField('iconSize');
const halfIconSize = iconSize / 2;
await Promise.all(
@@ -264,9 +272,10 @@ export const drawServices = async function (
elem: D3Element,
services: ArchitectureService[]
): Promise<number> {
const config = getConfig();
for (const service of services) {
const serviceElem = elem.append('g');
const iconSize = getConfigField('iconSize');
const iconSize = db.getConfigField('iconSize');
if (service.title) {
const textElem = serviceElem.append('g');
@@ -278,7 +287,7 @@ export const drawServices = async function (
width: iconSize * 1.5,
classes: 'architecture-service-label',
},
getConfig()
config
);
textElem
@@ -313,7 +322,7 @@ export const drawServices = async function (
.attr('class', 'node-icon-text')
.attr('style', `height: ${iconSize}px;`)
.append('div')
.html(service.iconText);
.html(sanitizeText(service.iconText, config));
const fontSize =
parseInt(
window
@@ -350,7 +359,7 @@ export const drawJunctions = function (
) {
junctions.forEach((junction) => {
const junctionElem = elem.append('g');
const iconSize = getConfigField('iconSize');
const iconSize = db.getConfigField('iconSize');
const bkgElem = junctionElem.append('g');
bkgElem

View File

@@ -92,7 +92,20 @@ export const setCssClass = function (itemIds: string, cssClassName: string) {
const populateBlockDatabase = (_blockList: Block[], parent: Block): void => {
const blockList = _blockList.flat();
const children = [];
const columnSettingBlock = blockList.find((b) => b?.type === 'column-setting');
const column = columnSettingBlock?.columns ?? -1;
for (const block of blockList) {
if (
typeof column === 'number' &&
column > 0 &&
block.type !== 'column-setting' &&
typeof block.widthInColumns === 'number' &&
block.widthInColumns > column
) {
log.warn(
`Block ${block.id} width ${block.widthInColumns} exceeds configured column width ${column}`
);
}
if (block.label) {
block.label = sanitizeText(block.label);
}
@@ -225,13 +238,15 @@ export function edgeTypeStr2Type(typeStr: string): string {
}
export function edgeStrToEdgeData(typeStr: string): string {
switch (typeStr.trim()) {
case '--x':
switch (typeStr.replace(/^[\s-]+|[\s-]+$/g, '')) {
case 'x':
return 'arrow_cross';
case '--o':
case 'o':
return 'arrow_circle';
default:
case '>':
return 'arrow_point';
default:
return '';
}
}

View File

@@ -3,7 +3,7 @@ import type { DiagramDetector, ExternalDiagramDefinition } from '../../diagram-a
const id = 'block';
const detector: DiagramDetector = (txt) => {
return /^\s*block-beta/.test(txt);
return /^\s*block(-beta)?/.test(txt);
};
const loader = async () => {

View File

@@ -270,7 +270,12 @@ function layoutBlocks(block: Block, db: BlockDB) {
if (child.children) {
layoutBlocks(child, db);
}
columnPos += child?.widthInColumns ?? 1;
let columnsFilled = child?.widthInColumns ?? 1;
if (columns > 0) {
// Make sure overflowing lines do not affect later lines
columnsFilled = Math.min(columnsFilled, columns - (columnPos % columns));
}
columnPos += columnsFilled;
log.debug('abc88 columnsPos', child, columnPos);
}
}

View File

@@ -36,10 +36,10 @@ CRLF \u000D\u000A
%%
"block-beta" { return 'BLOCK_DIAGRAM_KEY'; }
"block"\s+ { yy.getLogger().debug('Found space-block'); return 'block';}
"block"\n+ { yy.getLogger().debug('Found nl-block'); return 'block';}
"block:" { yy.getLogger().debug('Found space-block'); return 'id-block';}
"block-beta" { yy.getLogger().debug('Found block-beta'); return 'BLOCK_DIAGRAM_KEY'; }
"block:" { yy.getLogger().debug('Found id-block'); return 'id-block'; }
"block" { yy.getLogger().debug('Found block'); return 'BLOCK_DIAGRAM_KEY'; }
// \s*\%\%.* { yy.getLogger().debug('Found comment',yytext); }
[\s]+ { yy.getLogger().debug('.', yytext); /* skip all whitespace */ }
[\n]+ {yy.getLogger().debug('_', yytext); /* skip all whitespace */ }
@@ -240,7 +240,7 @@ columnsStatement
blockStatement
: id-block nodeStatement document end { yy.getLogger().debug('Rule: id-block statement : ', $2, $3); const id2 = yy.generateId(); $$ = { ...$2, type:'composite', children: $3 }; }
| block document end { yy.getLogger().debug('Rule: blockStatement : ', $1, $2, $3); const id = yy.generateId(); $$ = { id, type:'composite', label:'', children: $2 }; }
| BLOCK_DIAGRAM_KEY document end { yy.getLogger().debug('Rule: blockStatement : ', $1, $2, $3); const id = yy.generateId(); $$ = { id, type:'composite', label:'', children: $2 }; }
;
node

View File

@@ -1,6 +1,7 @@
// @ts-ignore: jison doesn't export types
import block from './block.jison';
import db from '../blockDB.js';
import { log } from '../../../logger.js';
describe('Block diagram', function () {
describe('when parsing a block diagram graph it should handle > ', function () {
@@ -22,7 +23,7 @@ describe('Block diagram', function () {
expect(blocks[0].label).toBe('id');
});
it('a node with a square shape and a label', () => {
const str = `block-beta
const str = `block
id["A label"]
`;
@@ -34,7 +35,7 @@ describe('Block diagram', function () {
expect(blocks[0].type).toBe('square');
});
it('a diagram with multiple nodes', () => {
const str = `block-beta
const str = `block
id1
id2
`;
@@ -50,7 +51,7 @@ describe('Block diagram', function () {
expect(blocks[1].type).toBe('na');
});
it('a diagram with multiple nodes', () => {
const str = `block-beta
const str = `block
id1
id2
id3
@@ -71,7 +72,7 @@ describe('Block diagram', function () {
});
it('a node with a square shape and a label', () => {
const str = `block-beta
const str = `block
id["A label"]
id2`;
@@ -86,7 +87,7 @@ describe('Block diagram', function () {
expect(blocks[1].type).toBe('na');
});
it('a diagram with multiple nodes with edges abc123', () => {
const str = `block-beta
const str = `block
id1["first"] --> id2["second"]
`;
@@ -100,7 +101,7 @@ describe('Block diagram', function () {
expect(edges[0].arrowTypeEnd).toBe('arrow_point');
});
it('a diagram with multiple nodes with edges abc123', () => {
const str = `block-beta
const str = `block
id1["first"] -- "a label" --> id2["second"]
`;
@@ -115,7 +116,7 @@ describe('Block diagram', function () {
expect(edges[0].label).toBe('a label');
});
it('a diagram with column statements', () => {
const str = `block-beta
const str = `block
columns 2
block1["Block 1"]
`;
@@ -126,7 +127,7 @@ describe('Block diagram', function () {
expect(blocks.length).toBe(1);
});
it('a diagram without column statements', () => {
const str = `block-beta
const str = `block
block1["Block 1"]
`;
@@ -136,7 +137,7 @@ describe('Block diagram', function () {
expect(blocks.length).toBe(1);
});
it('a diagram with auto column statements', () => {
const str = `block-beta
const str = `block
columns auto
block1["Block 1"]
`;
@@ -148,7 +149,7 @@ describe('Block diagram', function () {
});
it('blocks next to each other', () => {
const str = `block-beta
const str = `block
columns 2
block1["Block 1"]
block2["Block 2"]
@@ -162,7 +163,7 @@ describe('Block diagram', function () {
});
it('blocks on top of each other', () => {
const str = `block-beta
const str = `block
columns 1
block1["Block 1"]
block2["Block 2"]
@@ -176,7 +177,7 @@ describe('Block diagram', function () {
});
it('compound blocks 2', () => {
const str = `block-beta
const str = `block
block
aBlock["ABlock"]
bBlock["BBlock"]
@@ -204,7 +205,7 @@ describe('Block diagram', function () {
expect(bBlock.type).toBe('square');
});
it('compound blocks of compound blocks', () => {
const str = `block-beta
const str = `block
block
aBlock["ABlock"]
block
@@ -239,7 +240,7 @@ describe('Block diagram', function () {
expect(bBlock.type).toBe('square');
});
it('compound blocks with title', () => {
const str = `block-beta
const str = `block
block:compoundBlock["Compound block"]
columns 1
block2["Block 2"]
@@ -264,7 +265,7 @@ describe('Block diagram', function () {
expect(block2.type).toBe('square');
});
it('blocks mixed with compound blocks', () => {
const str = `block-beta
const str = `block
columns 1
block1["Block 1"]
@@ -291,7 +292,7 @@ describe('Block diagram', function () {
});
it('Arrow blocks', () => {
const str = `block-beta
const str = `block
columns 3
block1["Block 1"]
blockArrow<["&nbsp;&nbsp;&nbsp;"]>(right)
@@ -315,7 +316,7 @@ describe('Block diagram', function () {
expect(blockArrow.directions).toContain('right');
});
it('Arrow blocks with multiple points', () => {
const str = `block-beta
const str = `block
columns 1
A
blockArrow<["&nbsp;&nbsp;&nbsp;"]>(up, down)
@@ -338,7 +339,7 @@ describe('Block diagram', function () {
expect(blockArrow.directions).not.toContain('right');
});
it('blocks with different widths', () => {
const str = `block-beta
const str = `block
columns 3
one["One Slot"]
two["Two slots"]:2
@@ -353,7 +354,7 @@ describe('Block diagram', function () {
expect(two.widthInColumns).toBe(2);
});
it('empty blocks', () => {
const str = `block-beta
const str = `block
columns 3
space
middle["In the middle"]
@@ -372,7 +373,7 @@ describe('Block diagram', function () {
expect(middle.label).toBe('In the middle');
});
it('classDef statements applied to a block', () => {
const str = `block-beta
const str = `block
classDef black color:#ffffff, fill:#000000;
mc["Memcache"]
@@ -390,7 +391,7 @@ describe('Block diagram', function () {
expect(black.styles[0]).toEqual('color:#ffffff');
});
it('style statements applied to a block', () => {
const str = `block-beta
const str = `block
columns 1
B["A wide one in the middle"]
style B fill:#f9F,stroke:#333,stroke-width:4px
@@ -402,13 +403,32 @@ columns 1
const B = blocks[0];
expect(B.styles).toContain('fill:#f9F');
});
it('should log a warning when block width exceeds column width', () => {
const str = `block-beta
columns 1
A:1
B:2
C:3
D:4
E:3
F:2
G:1`;
const logWarnSpy = vi.spyOn(log, 'warn').mockImplementation(() => undefined);
block.parse(str);
expect(logWarnSpy).toHaveBeenCalledWith('Block B width 2 exceeds configured column width 1');
logWarnSpy.mockRestore();
});
});
describe('prototype properties', function () {
function validateProperty(prop: string) {
expect(() => block.parse(`block-beta\n${prop}`)).not.toThrow();
expect(() => block.parse(`block\n${prop}`)).not.toThrow();
expect(() =>
block.parse(`block-beta\nA; classDef ${prop} color:#ffffff,fill:#000000; class A ${prop}`)
block.parse(`block\nA; classDef ${prop} color:#ffffff,fill:#000000; class A ${prop}`)
).not.toThrow();
}

View File

@@ -15,4 +15,12 @@ describe('class diagram', function () {
expect(() => parser.parse(`classDiagram\nnamespace ${prop} {\n\tclass A\n}`)).not.toThrow();
});
});
describe('backtick escaping', function () {
it('should handle backtick-quoted namespace names', function () {
expect(() =>
parser.parse(`classDiagram\nnamespace \`A::B\` {\n\tclass \`IPC::Sender\`\n}`)
).not.toThrow();
});
});
});

View File

@@ -242,6 +242,7 @@ classLabel
namespaceName
: alphaNumToken { $$=$1; }
| classLiteralName { $$=$1; }
| alphaNumToken DOT namespaceName { $$=$1+'.'+$3; }
| alphaNumToken namespaceName { $$=$1+$2; }
;

View File

@@ -33,13 +33,13 @@ function setupDompurifyHooks() {
const TEMPORARY_ATTRIBUTE = 'data-temp-href-target';
DOMPurify.addHook('beforeSanitizeAttributes', (node) => {
if (node instanceof Element && node.tagName === 'A' && node.hasAttribute('target')) {
if (node.tagName === 'A' && node.hasAttribute('target')) {
node.setAttribute(TEMPORARY_ATTRIBUTE, node.getAttribute('target') ?? '');
}
});
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
if (node instanceof Element && node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
if (node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE) ?? '');
node.removeAttribute(TEMPORARY_ATTRIBUTE);
if (node.getAttribute('target') === '_blank') {
@@ -311,9 +311,8 @@ export const hasKatex = (text: string): boolean => (text.match(katexRegex)?.leng
* @returns Object containing \{width, height\}
*/
export const calculateMathMLDimensions = async (text: string, config: MermaidConfig) => {
text = await renderKatex(text, config);
const divElem = document.createElement('div');
divElem.innerHTML = text;
divElem.innerHTML = await renderKatexSanitized(text, config);
divElem.id = 'katex-temp';
divElem.style.visibility = 'hidden';
divElem.style.position = 'absolute';
@@ -325,14 +324,7 @@ export const calculateMathMLDimensions = async (text: string, config: MermaidCon
return dim;
};
/**
* Attempts to render and return the KaTeX portion of a string with MathML
*
* @param text - The text to test
* @param config - Configuration for Mermaid
* @returns String containing MathML if KaTeX is supported, or an error message if it is not and stylesheets aren't present
*/
export const renderKatex = async (text: string, config: MermaidConfig): Promise<string> => {
const renderKatexUnsanitized = async (text: string, config: MermaidConfig): Promise<string> => {
if (!hasKatex(text)) {
return text;
}
@@ -373,6 +365,20 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
);
};
/**
* Attempts to render and return the KaTeX portion of a string with MathML
*
* @param text - The text to test
* @param config - Configuration for Mermaid
* @returns String containing MathML if KaTeX is supported, or an error message if it is not and stylesheets aren't present
*/
export const renderKatexSanitized = async (
text: string,
config: MermaidConfig
): Promise<string> => {
return sanitizeText(await renderKatexUnsanitized(text, config), config);
};
export default {
getRows,
sanitizeText,

View File

@@ -11,7 +11,7 @@ const detector: DiagramDetector = (txt, config = {}): boolean => {
// If diagram explicitly states flowchart-elk
/^\s*flowchart-elk/.test(txt) ||
// If a flowchart/graph diagram has their default renderer set to elk
(/^\s*flowchart|graph/.test(txt) && config?.flowchart?.defaultRenderer === 'elk')
(/^\s*(flowchart|graph)/.test(txt) && config?.flowchart?.defaultRenderer === 'elk')
) {
config.layout = 'elk';
return true;

View File

@@ -125,4 +125,60 @@ describe('flow db getData', () => {
const { edges } = flowDb.getData();
expect(edges[0].curve).toBe('basis');
});
it('should support modifying interpolate using edge id syntax', () => {
flowDb.addVertex('A', { text: 'A', type: 'text' }, undefined, [], [], '', {}, undefined);
flowDb.addVertex('B', { text: 'B', type: 'text' }, undefined, [], [], '', {}, undefined);
flowDb.addVertex('C', { text: 'C', type: 'text' }, undefined, [], [], '', {}, undefined);
flowDb.addVertex('D', { text: 'D', type: 'text' }, undefined, [], [], '', {}, undefined);
flowDb.addLink(['A'], ['B'], {});
flowDb.addLink(['A'], ['C'], { id: 'e2' });
flowDb.addLink(['B'], ['D'], { id: 'e3' });
flowDb.addLink(['C'], ['D'], {});
flowDb.updateLinkInterpolate(['default'], 'stepBefore');
flowDb.updateLinkInterpolate([0], 'basis');
flowDb.addVertex(
'e2',
{ text: 'Shouldnt be used', type: 'text' },
undefined,
[],
[],
'',
{},
' curve: monotoneX '
);
flowDb.addVertex(
'e3',
{ text: 'Shouldnt be used', type: 'text' },
undefined,
[],
[],
'',
{},
' curve: catmullRom '
);
const { edges } = flowDb.getData();
expect(edges[0].curve).toBe('basis');
expect(edges[1].curve).toBe('monotoneX');
expect(edges[2].curve).toBe('catmullRom');
expect(edges[3].curve).toBe('stepBefore');
});
});
describe('flow db direction', () => {
let flowDb: FlowDB;
beforeEach(() => {
flowDb = new FlowDB();
});
it('should set direction to TB when TD is set', () => {
flowDb.setDirection('TD');
expect(flowDb.getDirection()).toBe('TB');
});
it('should correctly set direction irrespective of leading spaces', () => {
flowDb.setDirection(' TD');
expect(flowDb.getDirection()).toBe('TB');
});
});

View File

@@ -139,6 +139,9 @@ export class FlowDB implements DiagramDB {
if (edgeDoc?.animation !== undefined) {
edge.animation = edgeDoc.animation;
}
if (edgeDoc?.curve !== undefined) {
edge.interpolate = edgeDoc.curve;
}
return;
}
@@ -403,7 +406,8 @@ You have to call mermaid.initialize.`
*
*/
public setDirection(dir: string) {
this.direction = dir;
this.direction = dir.trim();
if (/.*</.exec(this.direction)) {
this.direction = 'RL';
}

View File

@@ -37,6 +37,59 @@ describe('[Lines] when parsing', () => {
expect(edges[1].interpolate).toBe('cardinal');
});
it('should handle edge curve properties using edge ID', function () {
const res = flow.parser.parse(
'graph TD\n' +
'A e1@-->B\n' +
'A uniqueName@-->C\n' +
'e1@{curve: basis}\n' +
'uniqueName@{curve: cardinal}'
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('basis');
expect(edges[1].interpolate).toBe('cardinal');
});
it('should handle edge curve properties using edge ID but without overriding default', function () {
const res = flow.parser.parse(
'graph TD\n' +
'A e1@-->B\n' +
'A-->C\n' +
'linkStyle default interpolate linear\n' +
'e1@{curve: stepAfter}'
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('stepAfter');
expect(edges.defaultInterpolate).toBe('linear');
});
it('should handle edge curve properties using edge ID mixed with line interpolation', function () {
const res = flow.parser.parse(
'graph TD\n' +
'A e1@-->B-->D\n' +
'A-->C e4@-->D-->E\n' +
'linkStyle default interpolate linear\n' +
'linkStyle 1 interpolate basis\n' +
'e1@{curve: monotoneX}\n' +
'e4@{curve: stepBefore}'
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(edges[0].interpolate).toBe('monotoneX');
expect(edges[1].interpolate).toBe('basis');
expect(edges.defaultInterpolate).toBe('linear');
expect(edges[3].interpolate).toBe('stepBefore');
expect(edges.defaultInterpolate).toBe('linear');
});
it('should handle line interpolation multi-numbered definitions', function () {
const res = flow.parser.parse(
'graph TD\n' + 'A-->B\n' + 'A-->C\n' + 'linkStyle 0,1 interpolate basis'

View File

@@ -167,7 +167,10 @@ export const getTasks = function () {
};
export const isInvalidDate = function (date, dateFormat, excludes, includes) {
if (includes.includes(date.format(dateFormat.trim()))) {
const formattedDate = date.format(dateFormat.trim());
const dateOnly = date.format('YYYY-MM-DD');
if (includes.includes(formattedDate) || includes.includes(dateOnly)) {
return false;
}
if (
@@ -180,7 +183,7 @@ export const isInvalidDate = function (date, dateFormat, excludes, includes) {
if (excludes.includes(date.format('dddd').toLowerCase())) {
return true;
}
return excludes.includes(date.format(dateFormat.trim()));
return excludes.includes(formattedDate) || excludes.includes(dateOnly);
};
export const setWeekday = function (txt) {

View File

@@ -581,17 +581,11 @@ export const draw = function (text, id, version, diagObj) {
rectangles
.append('rect')
.attr('id', function (d) {
return 'exclude-' + d.start.format('YYYY-MM-DD');
})
.attr('x', function (d) {
return timeScale(d.start) + theSidePad;
})
.attr('id', (d) => 'exclude-' + d.start.format('YYYY-MM-DD'))
.attr('x', (d) => timeScale(d.start.startOf('day')) + theSidePad)
.attr('y', conf.gridLineStartPadding)
.attr('width', function (d) {
const renderEnd = d.end.add(1, 'day');
return timeScale(renderEnd) - timeScale(d.start);
})
.attr('width', (d) => timeScale(d.end.endOf('day')) - timeScale(d.start.startOf('day')))
.attr('height', h - theTopPad - conf.gridLineStartPadding)
.attr('transform-origin', function (d, i) {
return (
@@ -615,9 +609,20 @@ export const draw = function (text, id, version, diagObj) {
* @param h
*/
function makeGrid(theSidePad, theTopPad, w, h) {
const dateFormat = diagObj.db.getDateFormat();
const userAxisFormat = diagObj.db.getAxisFormat();
let axisFormat;
if (userAxisFormat) {
axisFormat = userAxisFormat;
} else if (dateFormat === 'D') {
axisFormat = '%d';
} else {
axisFormat = conf.axisFormat ?? '%Y-%m-%d';
}
let bottomXAxis = axisBottom(timeScale)
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
.tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
.tickFormat(timeFormat(axisFormat));
const reTickInterval = /^([1-9]\d*)(millisecond|second|minute|hour|day|week|month)$/;
const resultTickInterval = reTickInterval.exec(
@@ -669,7 +674,7 @@ export const draw = function (text, id, version, diagObj) {
if (diagObj.db.topAxisEnabled() || conf.topAxis) {
let topXAxis = axisTop(timeScale)
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
.tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
.tickFormat(timeFormat(axisFormat));
if (resultTickInterval !== null) {
const every = resultTickInterval[1];

View File

@@ -1,6 +1,7 @@
import { getConfig as commonGetConfig } from '../../config.js';
import type { PacketDiagramConfig } from '../../config.type.js';
import DEFAULT_CONFIG from '../../defaultConfig.js';
import type { DiagramDB } from '../../diagram-api/types.js';
import { cleanAndMerge } from '../../utils.js';
import {
clear as commonClear,
@@ -11,49 +12,42 @@ import {
setAccTitle,
setDiagramTitle,
} from '../common/commonDb.js';
import type { PacketDB, PacketData, PacketWord } from './types.js';
const defaultPacketData: PacketData = {
packet: [],
};
let data: PacketData = structuredClone(defaultPacketData);
import type { PacketWord } from './types.js';
const DEFAULT_PACKET_CONFIG: Required<PacketDiagramConfig> = DEFAULT_CONFIG.packet;
const getConfig = (): Required<PacketDiagramConfig> => {
const config = cleanAndMerge({
...DEFAULT_PACKET_CONFIG,
...commonGetConfig().packet,
});
if (config.showBits) {
config.paddingY += 10;
export class PacketDB implements DiagramDB {
private packet: PacketWord[] = [];
public getConfig() {
const config = cleanAndMerge({
...DEFAULT_PACKET_CONFIG,
...commonGetConfig().packet,
});
if (config.showBits) {
config.paddingY += 10;
}
return config;
}
return config;
};
const getPacket = (): PacketWord[] => data.packet;
const pushWord = (word: PacketWord) => {
if (word.length > 0) {
data.packet.push(word);
public getPacket() {
return this.packet;
}
};
const clear = () => {
commonClear();
data = structuredClone(defaultPacketData);
};
public pushWord(word: PacketWord) {
if (word.length > 0) {
this.packet.push(word);
}
}
export const db: PacketDB = {
pushWord,
getPacket,
getConfig,
clear,
setAccTitle,
getAccTitle,
setDiagramTitle,
getDiagramTitle,
getAccDescription,
setAccDescription,
};
public clear() {
commonClear();
this.packet = [];
}
public setAccTitle = setAccTitle;
public getAccTitle = getAccTitle;
public setDiagramTitle = setDiagramTitle;
public getDiagramTitle = getDiagramTitle;
public getAccDescription = getAccDescription;
public setAccDescription = setAccDescription;
}

View File

@@ -1,12 +1,14 @@
import type { DiagramDefinition } from '../../diagram-api/types.js';
import { db } from './db.js';
import { PacketDB } from './db.js';
import { parser } from './parser.js';
import { renderer } from './renderer.js';
import { styles } from './styles.js';
export const diagram: DiagramDefinition = {
parser,
db,
get db() {
return new PacketDB();
},
renderer,
styles,
};

View File

@@ -1,24 +1,26 @@
import { it, describe, expect } from 'vitest';
import { db } from './db.js';
import { PacketDB } from './db.js';
import { parser } from './parser.js';
const { clear, getPacket, getDiagramTitle, getAccTitle, getAccDescription } = db;
describe('packet diagrams', () => {
let db: PacketDB;
beforeEach(() => {
clear();
db = new PacketDB();
if (parser.parser) {
parser.parser.yy = db;
}
});
it('should handle a packet-beta definition', async () => {
const str = `packet-beta`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot('[]');
expect(db.getPacket()).toMatchInlineSnapshot('[]');
});
it('should handle a packet definition', async () => {
const str = `packet`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot('[]');
expect(db.getPacket()).toMatchInlineSnapshot('[]');
});
it('should handle diagram with data and title', async () => {
@@ -29,10 +31,10 @@ describe('packet diagrams', () => {
0-10: "test"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getDiagramTitle()).toMatchInlineSnapshot('"Packet diagram"');
expect(getAccTitle()).toMatchInlineSnapshot('"Packet accTitle"');
expect(getAccDescription()).toMatchInlineSnapshot('"Packet accDescription"');
expect(getPacket()).toMatchInlineSnapshot(`
expect(db.getDiagramTitle()).toMatchInlineSnapshot('"Packet diagram"');
expect(db.getAccTitle()).toMatchInlineSnapshot('"Packet accTitle"');
expect(db.getAccDescription()).toMatchInlineSnapshot('"Packet accDescription"');
expect(db.getPacket()).toMatchInlineSnapshot(`
[
[
{
@@ -52,7 +54,7 @@ describe('packet diagrams', () => {
11: "single"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot(`
expect(db.getPacket()).toMatchInlineSnapshot(`
[
[
{
@@ -78,7 +80,7 @@ describe('packet diagrams', () => {
+16: "word"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot(`
expect(db.getPacket()).toMatchInlineSnapshot(`
[
[
{
@@ -104,7 +106,7 @@ describe('packet diagrams', () => {
+16: "word"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot(`
expect(db.getPacket()).toMatchInlineSnapshot(`
[
[
{
@@ -130,7 +132,7 @@ describe('packet diagrams', () => {
11-90: "multiple"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot(`
expect(db.getPacket()).toMatchInlineSnapshot(`
[
[
{
@@ -172,7 +174,7 @@ describe('packet diagrams', () => {
17-63: "multiple"
`;
await expect(parser.parse(str)).resolves.not.toThrow();
expect(getPacket()).toMatchInlineSnapshot(`
expect(db.getPacket()).toMatchInlineSnapshot(`
[
[
{

View File

@@ -3,12 +3,12 @@ import { parse } from '@mermaid-js/parser';
import type { ParserDefinition } from '../../diagram-api/types.js';
import { log } from '../../logger.js';
import { populateCommonDb } from '../common/populateCommonDb.js';
import { db } from './db.js';
import { PacketDB } from './db.js';
import type { PacketBlock, PacketWord } from './types.js';
const maxPacketSize = 10_000;
const populate = (ast: Packet) => {
const populate = (ast: Packet, db: PacketDB) => {
populateCommonDb(ast, db);
let lastBit = -1;
let word: PacketWord = [];
@@ -91,9 +91,17 @@ const getNextFittingBlock = (
};
export const parser: ParserDefinition = {
// @ts-expect-error - PacketDB is not assignable to DiagramDB
parser: { yy: undefined },
parse: async (input: string): Promise<void> => {
const ast: Packet = await parse('packet', input);
const db = parser.parser?.yy;
if (!(db instanceof PacketDB)) {
throw new Error(
'parser.parser?.yy was not a PacketDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.'
);
}
log.debug(ast);
populate(ast);
populate(ast, db);
},
};

View File

@@ -139,6 +139,32 @@ describe('pie', () => {
}).rejects.toThrowError();
});
it('should handle simple pie with zero slice value', async () => {
await parser.parse(`pie title Default text position: Animal adoption
accTitle: simple pie char demo
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
"dogs" : 0
"rats" : 40.12
`);
const sections = db.getSections();
expect(sections.get('dogs')).toBe(0);
expect(sections.get('rats')).toBe(40.12);
});
it('should handle simple pie with negative slice value', async () => {
await expect(async () => {
await parser.parse(`pie title Default text position: Animal adoption
accTitle: simple pie char demo
accDescr: pie chart with 3 sections: dogs, cats, rats. Most are dogs.
"dogs" : -60.67
"rats" : 40.12
`);
}).rejects.toThrowError(
'"dogs" has invalid value: -60.67. Negative values are not allowed in pie charts. All slice values must be >= 0.'
);
});
it('should handle unsafe properties', async () => {
await expect(
parser.parse(`pie title Unsafe props test

View File

@@ -34,6 +34,11 @@ const clear = (): void => {
};
const addSection = ({ label, value }: D3Section): void => {
if (value < 0) {
throw new Error(
`"${label}" has invalid value: ${value}. Negative values are not allowed in pie charts. All slice values must be >= 0.`
);
}
if (!sections.has(label)) {
sections.set(label, value);
log.debug(`added new section: ${label}, with value: ${value}`);

View File

@@ -10,20 +10,14 @@ import { cleanAndMerge, parseFontSize } from '../../utils.js';
import type { D3Section, PieDB, Sections } from './pieTypes.js';
const createPieArcs = (sections: Sections): d3.PieArcDatum<D3Section>[] => {
// Compute the position of each group on the pie:
const sum = [...sections.values()].reduce((acc, val) => acc + val, 0);
const pieData: D3Section[] = [...sections.entries()]
.map((element: [string, number]): D3Section => {
return {
label: element[0],
value: element[1],
};
})
.sort((a: D3Section, b: D3Section): number => {
return b.value - a.value;
});
const pie: d3.Pie<unknown, D3Section> = d3pie<D3Section>().value(
(d3Section: D3Section): number => d3Section.value
);
.map(([label, value]) => ({ label, value }))
.filter((d) => (d.value / sum) * 100 >= 1) // Remove values < 1%
.sort((a, b) => b.value - a.value);
const pie: d3.Pie<unknown, D3Section> = d3pie<D3Section>().value((d) => d.value);
return pie(pieData);
};
@@ -89,13 +83,21 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
themeVariables.pie11,
themeVariables.pie12,
];
let sum = 0;
sections.forEach((section) => {
sum += section;
});
// Filter out arcs that would render as 0%
const filteredArcs = arcs.filter((datum) => ((datum.data.value / sum) * 100).toFixed(0) !== '0');
// Set the color scale
const color: d3.ScaleOrdinal<string, 12, never> = scaleOrdinal(myGeneratedColors);
// Build the pie chart: each part of the pie is a path that we build using the arc function.
group
.selectAll('mySlices')
.data(arcs)
.data(filteredArcs)
.enter()
.append('path')
.attr('d', arcGenerator)
@@ -104,15 +106,11 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
})
.attr('class', 'pieCircle');
let sum = 0;
sections.forEach((section) => {
sum += section;
});
// Now add the percentage.
// Use the centroid method to get the best coordinates.
group
.selectAll('mySlices')
.data(arcs)
.data(filteredArcs)
.enter()
.append('text')
.text((datum: d3.PieArcDatum<D3Section>): string => {
@@ -133,15 +131,20 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
.attr('class', 'pieTitleText');
// Add the legends/annotations for each section
const allSectionData: D3Section[] = [...sections.entries()].map(([label, value]) => ({
label,
value,
}));
const legend = group
.selectAll('.legend')
.data(color.domain())
.data(allSectionData)
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', (_datum, index: number): string => {
const height = LEGEND_RECT_SIZE + LEGEND_SPACING;
const offset = (height * color.domain().length) / 2;
const offset = (height * allSectionData.length) / 2;
const horizontal = 12 * LEGEND_RECT_SIZE;
const vertical = index * height - offset;
return 'translate(' + horizontal + ',' + vertical + ')';
@@ -151,20 +154,18 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
.append('rect')
.attr('width', LEGEND_RECT_SIZE)
.attr('height', LEGEND_RECT_SIZE)
.style('fill', color)
.style('stroke', color);
.style('fill', (d) => color(d.label))
.style('stroke', (d) => color(d.label));
legend
.data(arcs)
.append('text')
.attr('x', LEGEND_RECT_SIZE + LEGEND_SPACING)
.attr('y', LEGEND_RECT_SIZE - LEGEND_SPACING)
.text((datum: d3.PieArcDatum<D3Section>): string => {
const { label, value } = datum.data;
.text((d) => {
if (db.getShowData()) {
return `${label} [${value}]`;
return `${d.label} [${d.value}]`;
}
return label;
return d.label;
});
const longestTextWidth = Math.max(

View File

@@ -27,6 +27,7 @@ TEXTDATA [\u0020-\u0021\u0023-\u002B\u002D-\u007E]
%%
<INITIAL>"sankey-beta" { this.pushState('csv'); return 'SANKEY'; }
<INITIAL>"sankey" { this.pushState('csv'); return 'SANKEY'; }
<INITIAL,csv><<EOF>> { return 'EOF' } // match end of file
<INITIAL,csv>({CRLF}|{LF}) { return 'NEWLINE' }
<INITIAL,csv>{COMMA} { return 'COMMA' }

View File

@@ -13,7 +13,7 @@ describe('Sankey diagram', function () {
sankey.parser.yy.clear();
});
it('parses csv', () => {
it('parses csv with sankey-beta syntax', () => {
const csv = path.resolve(__dirname, './energy.csv');
const data = fs.readFileSync(csv, 'utf8');
const graphDefinition = prepareTextForParsing(cleanupComments('sankey-beta\n\n ' + data));
@@ -21,7 +21,15 @@ describe('Sankey diagram', function () {
sankey.parser.parse(graphDefinition);
});
it('allows __proto__ as id', function () {
it('parses csv with sankey syntax', () => {
const csv = path.resolve(__dirname, './energy.csv');
const data = fs.readFileSync(csv, 'utf8');
const graphDefinition = prepareTextForParsing(cleanupComments('sankey\n\n ' + data));
sankey.parser.parse(graphDefinition);
});
it('allows __proto__ as id with sankey-beta syntax', function () {
sankey.parser.parse(
prepareTextForParsing(`sankey-beta
__proto__,A,0.597
@@ -29,5 +37,14 @@ describe('Sankey diagram', function () {
`)
);
});
it('allows __proto__ as id with sankey syntax', function () {
sankey.parser.parse(
prepareTextForParsing(`sankey
__proto__,A,0.597
A,__proto__,0.403
`)
);
});
});
});

View File

@@ -3,7 +3,7 @@ import type { DiagramDetector, ExternalDiagramDefinition } from '../../diagram-a
const id = 'sankey';
const detector: DiagramDetector = (txt) => {
return /^\s*sankey-beta/.test(txt);
return /^\s*sankey(-beta)?/.test(txt);
};
const loader = async () => {

View File

@@ -33,7 +33,7 @@
"actor" { this.begin('ID'); return 'participant_actor'; }
"create" return 'create';
"destroy" { this.begin('ID'); return 'destroy'; }
<ID>[^\<->\->:\n,;]+?([\-]*[^\<->\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
<ID>[^<\->\->:\n,;]+?([\-]*[^<\->\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
"loop" { this.begin('LINE'); return 'loop'; }
@@ -73,7 +73,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
"off" return 'off';
"," return ',';
";" return 'NEWLINE';
[^\+\<->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)))[\-]*[^\+\<->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; }
[^+<\->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)))[\-]*[^\+<\->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; }
"->>" return 'SOLID_ARROW';
"<<->>" return 'BIDIRECTIONAL_SOLID_ARROW';
"-->>" return 'DOTTED_ARROW';

View File

@@ -350,6 +350,26 @@ Bob-->Alice-in-Wonderland:I am good thanks!`);
expect(messages[1].from).toBe('Bob');
});
it('should handle equals in participant names', async () => {
const diagram = await Diagram.fromText(`
sequenceDiagram
participant Alice=Wonderland
participant Bob
Alice=Wonderland->Bob:Hello Bob, how are - you?
Bob-->Alice=Wonderland:I am good thanks!`);
const actors = diagram.db.getActors();
expect([...actors.keys()]).toEqual(['Alice=Wonderland', 'Bob']);
expect(actors.get('Alice=Wonderland').description).toBe('Alice=Wonderland');
expect(actors.get('Bob').description).toBe('Bob');
const messages = diagram.db.getMessages();
expect(messages.length).toBe(2);
expect(messages[0].from).toBe('Alice=Wonderland');
expect(messages[1].from).toBe('Bob');
});
it('should alias participants', async () => {
const diagram = await Diagram.fromText(`
sequenceDiagram

View File

@@ -1,8 +1,12 @@
import common, { calculateMathMLDimensions, hasKatex, renderKatex } from '../common/common.js';
import * as svgDrawCommon from '../common/svgDrawCommon.js';
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
import { sanitizeUrl } from '@braintree/sanitize-url';
import * as configApi from '../../config.js';
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
import common, {
calculateMathMLDimensions,
hasKatex,
renderKatexSanitized,
} from '../common/common.js';
import * as svgDrawCommon from '../common/svgDrawCommon.js';
export const ACTOR_TYPE_WIDTH = 18 * 2;
const TOP_ACTOR_CLASS = 'actor-top';
@@ -87,13 +91,13 @@ const popupMenuToggle = function (popId) {
export const drawKatex = async function (elem, textData, msgModel = null) {
let textElem = elem.append('foreignObject');
const lines = await renderKatex(textData.text, configApi.getConfig());
const linesSanitized = await renderKatexSanitized(textData.text, configApi.getConfig());
const divElem = textElem
.append('xhtml:div')
.attr('style', 'width: fit-content;')
.attr('xmlns', 'http://www.w3.org/1999/xhtml')
.html(lines);
.html(linesSanitized);
const dim = divElem.node().getBoundingClientRect();
textElem.attr('height', Math.round(dim.height)).attr('width', Math.round(dim.width));
@@ -965,7 +969,7 @@ const _drawTextCandidateFunc = (function () {
.append('div')
.style('text-align', 'center')
.style('vertical-align', 'middle')
.html(await renderKatex(content, configApi.getConfig()));
.html(await renderKatexSanitized(content, configApi.getConfig()));
byTspan(content, s, x, y, width, height, textAttrs, conf);
_setTextAttrs(text, textAttrs);

View File

@@ -30,6 +30,7 @@
<acc_descr_multiline>[^\}]* { return "acc_descr_multiline_value"; }
"xychart-beta" {return 'XYCHART';}
"xychart" {return 'XYCHART';}
(?:"vertical"|"horizontal") {return 'CHART_ORIENTATION'}
"x-axis" { this.pushState("axis_data"); return "X_AXIS"; }

View File

@@ -33,44 +33,44 @@ describe('Testing xychart jison file', () => {
clearMocks();
});
it('should throw error if xychart-beta text is not there', () => {
const str = 'xychart-beta-1';
it('should throw error if xychart text is not there', () => {
const str = 'xychart-1';
expect(parserFnConstructor(str)).toThrow();
});
it('should not throw error if only xychart is there', () => {
const str = 'xychart-beta';
const str = 'xychart';
expect(parserFnConstructor(str)).not.toThrow();
});
it('parse title of the chart within "', () => {
const str = 'xychart-beta \n title "This is a title"';
const str = 'xychart \n title "This is a title"';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('This is a title');
});
it('parse title of the chart without "', () => {
const str = 'xychart-beta \n title oneLinertitle';
const str = 'xychart \n title oneLinertitle';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setDiagramTitle).toHaveBeenCalledWith('oneLinertitle');
});
it('parse chart orientation', () => {
const str = 'xychart-beta vertical';
const str = 'xychart vertical';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setOrientation).toHaveBeenCalledWith('vertical');
});
it('parse chart orientation with spaces', () => {
let str = 'xychart-beta horizontal ';
let str = 'xychart horizontal ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setOrientation).toHaveBeenCalledWith('horizontal');
str = 'xychart-beta abc';
str = 'xychart abc';
expect(parserFnConstructor(str)).toThrow();
});
it('parse x-axis', () => {
const str = 'xychart-beta \nx-axis xAxisName\n';
const str = 'xychart \nx-axis xAxisName\n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({
text: 'xAxisName',
@@ -79,7 +79,7 @@ describe('Testing xychart jison file', () => {
});
it('parse x-axis with axis name without "', () => {
const str = 'xychart-beta \nx-axis xAxisName \n';
const str = 'xychart \nx-axis xAxisName \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({
text: 'xAxisName',
@@ -88,7 +88,7 @@ describe('Testing xychart jison file', () => {
});
it('parse x-axis with axis name with "', () => {
const str = 'xychart-beta \n x-axis "xAxisName has space"\n';
const str = 'xychart \n x-axis "xAxisName has space"\n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({
text: 'xAxisName has space',
@@ -97,7 +97,7 @@ describe('Testing xychart jison file', () => {
});
it('parse x-axis with axis name with " with spaces', () => {
const str = 'xychart-beta \n x-axis " xAxisName has space " \n';
const str = 'xychart \n x-axis " xAxisName has space " \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({
text: ' xAxisName has space ',
@@ -106,7 +106,7 @@ describe('Testing xychart jison file', () => {
});
it('parse x-axis with axis name and range data', () => {
const str = 'xychart-beta \nx-axis xAxisName 45.5 --> 33 \n';
const str = 'xychart \nx-axis xAxisName 45.5 --> 33 \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({
text: 'xAxisName',
@@ -115,11 +115,11 @@ describe('Testing xychart jison file', () => {
expect(mockDB.setXAxisRangeData).toHaveBeenCalledWith(45.5, 33);
});
it('parse x-axis throw error for invalid range data', () => {
const str = 'xychart-beta \nx-axis xAxisName aaa --> 33 \n';
const str = 'xychart \nx-axis xAxisName aaa --> 33 \n';
expect(parserFnConstructor(str)).toThrow();
});
it('parse x-axis with axis name and range data with only decimal part', () => {
const str = 'xychart-beta \nx-axis xAxisName 45.5 --> .34 \n';
const str = 'xychart \nx-axis xAxisName 45.5 --> .34 \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({
text: 'xAxisName',
@@ -129,7 +129,7 @@ describe('Testing xychart jison file', () => {
});
it('parse x-axis without axis name and range data', () => {
const str = 'xychart-beta \nx-axis 45.5 --> 1.34 \n';
const str = 'xychart \nx-axis 45.5 --> 1.34 \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({
text: '',
@@ -139,7 +139,7 @@ describe('Testing xychart jison file', () => {
});
it('parse x-axis with axis name and category data', () => {
const str = 'xychart-beta \nx-axis xAxisName [ "cat1" , cat2a ] \n ';
const str = 'xychart \nx-axis xAxisName [ "cat1" , cat2a ] \n ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({
text: 'xAxisName',
@@ -155,7 +155,7 @@ describe('Testing xychart jison file', () => {
});
it('parse x-axis without axis name and category data', () => {
const str = 'xychart-beta \nx-axis [ "cat1" , cat2a ] \n ';
const str = 'xychart \nx-axis [ "cat1" , cat2a ] \n ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({
text: '',
@@ -171,14 +171,14 @@ describe('Testing xychart jison file', () => {
});
it('parse x-axis throw error if unbalanced bracket', () => {
let str = 'xychart-beta \nx-axis xAxisName [ "cat1" [ cat2a ] \n ';
let str = 'xychart \nx-axis xAxisName [ "cat1" [ cat2a ] \n ';
expect(parserFnConstructor(str)).toThrow();
str = 'xychart-beta \nx-axis xAxisName [ "cat1" , cat2a ] ] \n ';
str = 'xychart \nx-axis xAxisName [ "cat1" , cat2a ] ] \n ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse x-axis complete variant 1', () => {
const str = `xychart-beta \n x-axis "this is x axis" [category1, "category 2", category3]\n`;
const str = `xychart \n x-axis "this is x axis" [category1, "category 2", category3]\n`;
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'this is x axis', type: 'text' });
expect(mockDB.setXAxisBand).toHaveBeenCalledWith([
@@ -189,8 +189,7 @@ describe('Testing xychart jison file', () => {
});
it('parse x-axis complete variant 2', () => {
const str =
'xychart-beta \nx-axis xAxisName [ "cat1 with space" , cat2 , cat3] \n ';
const str = 'xychart \nx-axis xAxisName [ "cat1 with space" , cat2 , cat3] \n ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' });
expect(mockDB.setXAxisBand).toHaveBeenCalledWith([
@@ -202,7 +201,7 @@ describe('Testing xychart jison file', () => {
it('parse x-axis complete variant 3', () => {
const str =
'xychart-beta \nx-axis xAxisName [ "cat1 with space" , cat2 asdf , cat3] \n ';
'xychart \nx-axis xAxisName [ "cat1 with space" , cat2 asdf , cat3] \n ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' });
expect(mockDB.setXAxisBand).toHaveBeenCalledWith([
@@ -213,17 +212,17 @@ describe('Testing xychart jison file', () => {
});
it('parse y-axis with axis name', () => {
const str = 'xychart-beta \ny-axis yAxisName\n';
const str = 'xychart \ny-axis yAxisName\n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
});
it('parse y-axis with axis name with spaces', () => {
const str = 'xychart-beta \ny-axis yAxisName \n';
const str = 'xychart \ny-axis yAxisName \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
});
it('parse y-axis with axis name with "', () => {
const str = 'xychart-beta \n y-axis "yAxisName has space"\n';
const str = 'xychart \n y-axis "yAxisName has space"\n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({
text: 'yAxisName has space',
@@ -231,7 +230,7 @@ describe('Testing xychart jison file', () => {
});
});
it('parse y-axis with axis name with " and spaces', () => {
const str = 'xychart-beta \n y-axis " yAxisName has space " \n';
const str = 'xychart \n y-axis " yAxisName has space " \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({
text: ' yAxisName has space ',
@@ -239,39 +238,39 @@ describe('Testing xychart jison file', () => {
});
});
it('parse y-axis with axis name with range data', () => {
const str = 'xychart-beta \ny-axis yAxisName 45.5 --> 33 \n';
const str = 'xychart \ny-axis yAxisName 45.5 --> 33 \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(45.5, 33);
});
it('parse y-axis without axis name with range data', () => {
const str = 'xychart-beta \ny-axis 45.5 --> 33 \n';
const str = 'xychart \ny-axis 45.5 --> 33 \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: '', type: 'text' });
expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(45.5, 33);
});
it('parse y-axis with axis name with range data with only decimal part', () => {
const str = 'xychart-beta \ny-axis yAxisName 45.5 --> .33 \n';
const str = 'xychart \ny-axis yAxisName 45.5 --> .33 \n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
expect(mockDB.setYAxisRangeData).toHaveBeenCalledWith(45.5, 0.33);
});
it('parse y-axis throw error for invalid number in range data', () => {
const str = 'xychart-beta \ny-axis yAxisName 45.5 --> abc \n';
const str = 'xychart \ny-axis yAxisName 45.5 --> abc \n';
expect(parserFnConstructor(str)).toThrow();
});
it('parse y-axis throws error if range data is passed', () => {
const str = 'xychart-beta \ny-axis yAxisName [ 45.3, 33 ] \n';
const str = 'xychart \ny-axis yAxisName [ 45.3, 33 ] \n';
expect(parserFnConstructor(str)).toThrow();
});
it('parse both axis at once', () => {
const str = 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n';
const str = 'xychart\nx-axis xAxisName\ny-axis yAxisName\n';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' });
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
});
it('parse line Data', () => {
const str = 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line lineTitle [23, 45, 56.6]';
const str = 'xychart\nx-axis xAxisName\ny-axis yAxisName\n line lineTitle [23, 45, 56.6]';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setLineData).toHaveBeenCalledWith(
{ text: 'lineTitle', type: 'text' },
@@ -282,7 +281,7 @@ describe('Testing xychart jison file', () => {
});
it('parse line Data with spaces and +,- symbols', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -45 , 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -45 , 56.6 ] ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' });
@@ -292,8 +291,7 @@ describe('Testing xychart jison file', () => {
);
});
it('parse line Data without title', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line [ +23 , -45 , 56.6 , .33] ';
const str = 'xychart\nx-axis xAxisName\ny-axis yAxisName\n line [ +23 , -45 , 56.6 , .33] ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' });
@@ -304,34 +302,32 @@ describe('Testing xychart jison file', () => {
});
it('parse line Data throws error unbalanced brackets', () => {
let str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 [ -45 , 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 [ -45 , 56.6 ] ';
expect(parserFnConstructor(str)).toThrow();
str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -45 ] 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -45 ] 56.6 ] ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse line Data throws error if data is not provided', () => {
const str = 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" ';
const str = 'xychart\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse line Data throws error if data is empty', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ ] ';
const str = 'xychart\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ ] ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse line Data throws error if , is not in proper', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , , -45 , 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , , -45 , 56.6 ] ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse line Data throws error if not number', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -4aa5 , 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n line "lineTitle with space" [ +23 , -4aa5 , 56.6 ] ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse bar Data', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar barTitle [23, 45, 56.6, .22]';
const str = 'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar barTitle [23, 45, 56.6, .22]';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' });
@@ -342,7 +338,7 @@ describe('Testing xychart jison file', () => {
});
it('parse bar Data spaces and +,- symbol', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -45 , 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -45 , 56.6 ] ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' });
@@ -352,8 +348,7 @@ describe('Testing xychart jison file', () => {
);
});
it('parse bar Data without plot title', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar [ +23 , -45 , 56.6 ] ';
const str = 'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar [ +23 , -45 , 56.6 ] ';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' });
@@ -361,34 +356,34 @@ describe('Testing xychart jison file', () => {
});
it('parse bar should throw for unbalanced brackets', () => {
let str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 [ -45 , 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 [ -45 , 56.6 ] ';
expect(parserFnConstructor(str)).toThrow();
str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -45 ] 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -45 ] 56.6 ] ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse bar should throw error if data is not provided', () => {
const str = 'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" ';
const str = 'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse bar should throw error if data is empty', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ ] ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse bar should throw error if comma is not proper', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , , -45 , 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , , -45 , 56.6 ] ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse bar should throw error if number is not passed', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -4aa5 , 56.6 ] ';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar "barTitle with space" [ +23 , -4aa5 , 56.6 ] ';
expect(parserFnConstructor(str)).toThrow();
});
it('parse multiple bar and line variant 1', () => {
const str =
'xychart-beta\nx-axis xAxisName\ny-axis yAxisName\n bar barTitle1 [23, 45, 56.6] \n line lineTitle1 [11, 45.5, 67, 23] \n bar barTitle2 [13, 42, 56.89] \n line lineTitle2 [45, 99, 012]';
'xychart\nx-axis xAxisName\ny-axis yAxisName\n bar barTitle1 [23, 45, 56.6] \n line lineTitle1 [11, 45.5, 67, 23] \n bar barTitle2 [13, 42, 56.89] \n line lineTitle2 [45, 99, 012]';
expect(parserFnConstructor(str)).not.toThrow();
expect(mockDB.setYAxisTitle).toHaveBeenCalledWith({ text: 'yAxisName', type: 'text' });
expect(mockDB.setXAxisTitle).toHaveBeenCalledWith({ text: 'xAxisName', type: 'text' });
@@ -411,8 +406,8 @@ describe('Testing xychart jison file', () => {
});
it('parse multiple bar and line variant 2', () => {
const str = `
xychart-beta horizontal
title Basic xychart
xychart horizontal
title "Basic xychart"
x-axis "this is x axis" [category1, "category 2", category3]
y-axis yaxisText 10 --> 150
bar barTitle1 [23, 45, 56.6]

View File

@@ -7,7 +7,7 @@ import type {
const id = 'xychart';
const detector: DiagramDetector = (txt) => {
return /^\s*xychart-beta/.test(txt);
return /^\s*xychart(-beta)?/.test(txt);
};
const loader: DiagramLoader = async () => {

View File

@@ -9,56 +9,136 @@ interface Feature {
interface EditorColumn {
title: string;
description: string;
redirectUrl: string;
redBarText?: string;
redirectUrl?: string;
buttonText?: string;
highlighted?: boolean;
proTrialUrl?: string;
proTrialButtonText?: string;
features: Feature[];
isButtonMargined?: boolean;
}
const editorColumns: EditorColumn[] = [
{
title: 'Playground',
description: 'Basic features, no login',
redirectUrl:
'https://www.mermaidchart.com/play?utm_source=mermaid_js&utm_medium=editor_selection&utm_campaign=playground',
features: [
{ iconUrl: '/icons/public.svg', featureName: 'Diagram stored in URL' },
{ iconUrl: '/icons/terminal.svg', featureName: 'Code editor' },
{ iconUrl: '/icons/whiteboard.svg', featureName: 'Whiteboard' },
],
},
{
title: 'Free',
description: 'Advanced features, free account',
redirectUrl:
'https://www.mermaidchart.com/app/sign-up?utm_source=mermaid_js&utm_medium=editor_selection&utm_campaign=mermaid_chart&redirect=%2Fapp%2Fdiagrams%2Fnew',
highlighted: true,
features: [
{ iconUrl: '/icons/folder.svg', featureName: 'Storage' },
{ iconUrl: '/icons/terminal.svg', featureName: 'Code editor' },
{ iconUrl: '/icons/ai-diagram.svg', featureName: 'AI diagram generator' },
{ iconUrl: '/icons/whiteboard.svg', featureName: 'Whiteboard' },
{ iconUrl: '/icons/group.svg', featureName: 'Teams' },
{ iconUrl: '/icons/groups.svg', featureName: 'Multi-user editing' },
{ iconUrl: '/icons/ai-repair.svg', featureName: 'AI diagram repair' },
{ iconUrl: '/icons/version-history.svg', featureName: 'Version history' },
{ iconUrl: '/icons/comment.svg', featureName: 'Comments' },
{ iconUrl: '/icons/presentation.svg', featureName: 'Presentations' },
{ iconUrl: '/icons/plugins.svg', featureName: 'Advanced Plugins' },
],
},
{
title: 'Open Source',
description: 'Code only, no login',
redirectUrl: 'https://mermaid.live/edit',
features: [
{ iconUrl: '/icons/public.svg', featureName: 'Diagram stored in URL' },
{ iconUrl: '/icons/terminal.svg', featureName: 'Code editor' },
{ iconUrl: '/icons/open-source.svg', featureName: 'Open source' },
{ iconUrl: '/icons/version-history.svg', featureName: 'Version history' },
],
},
const mermaidChartFeatures: Feature[] = [
{ iconUrl: '/icons/folder.svg', featureName: 'Storage' },
{ iconUrl: '/icons/terminal.svg', featureName: 'Code editor' },
{ iconUrl: '/icons/ai-diagram.svg', featureName: 'AI diagram generator' },
{ iconUrl: '/icons/whiteboard.svg', featureName: 'Whiteboard' },
{ iconUrl: '/icons/group.svg', featureName: 'Teams' },
{ iconUrl: '/icons/groups.svg', featureName: 'Multi-user editing' },
{ iconUrl: '/icons/ai-repair.svg', featureName: 'AI diagram repair' },
{ iconUrl: '/icons/version-history.svg', featureName: 'Version history' },
{ iconUrl: '/icons/comment.svg', featureName: 'Comments' },
{ iconUrl: '/icons/presentation.svg', featureName: 'Presentations' },
{ iconUrl: '/icons/plugins.svg', featureName: 'Advanced plugins' },
];
const openSourceFeatures: Feature[] = [
{ iconUrl: '/icons/public.svg', featureName: 'Diagram stored in URL' },
{ iconUrl: '/icons/terminal.svg', featureName: 'Code editor' },
{ iconUrl: '/icons/open-source.svg', featureName: 'Open source' },
{ iconUrl: '/icons/version-history.svg', featureName: 'Version history' },
];
const playgroundFeatures: Feature[] = [
{ iconUrl: '/icons/public.svg', featureName: 'Diagram stored in URL' },
{ iconUrl: '/icons/terminal.svg', featureName: 'Code editor' },
{ iconUrl: '/icons/whiteboard.svg', featureName: 'Whiteboard' },
];
const editorColumnVariants: EditorColumn[][] = [
[
{
title: 'Playground',
description: 'Basic features, no login',
redirectUrl:
'https://www.mermaidchart.com/app/sign-up?utm_source=mermaid_js&utm_medium=3_editor_selection_A&utm_campaign=start_playground',
buttonText: 'Start free',
features: playgroundFeatures,
},
{
title: 'Free or Pro',
description: 'Advanced features, Free or Pro account',
proTrialUrl:
'https://www.mermaidchart.com/app/sign-up?utm_source=mermaid_js&utm_medium=3_editor_selection_A&utm_campaign=start_free',
proTrialButtonText: 'Start free',
highlighted: true,
redBarText: 'Best for collaboration',
features: mermaidChartFeatures,
},
{
title: 'Open Source',
description: 'Code only, no login',
redirectUrl: 'https://mermaid.live/edit',
buttonText: 'Start free',
features: openSourceFeatures,
},
],
[
{
title: 'Mermaid Pro',
description: 'Unlock AI and real-time collaboration',
redirectUrl:
'https://www.mermaidchart.com/app/sign-up?utm_source=mermaid_js&utm_medium=editor_selection_B&utm_campaign=start_free',
buttonText: 'Start Free',
highlighted: true,
redBarText: 'Recommended',
proTrialButtonText: 'Start Pro trial',
proTrialUrl:
'https://www.mermaidchart.com/app/sign-up?utm_source=mermaid_js&utm_medium=editor_selection_B&utm_campaign=start_trial&redirect=%2Fapp%2Fuser%2Fbilling%2Fcheckout%3FisFromMermaid%3Dtrue',
features: mermaidChartFeatures,
},
{
title: 'Open Source',
description: 'Code only, no login',
buttonText: 'Start free',
redirectUrl: 'https://mermaid.live/edit',
isButtonMargined: true,
features: openSourceFeatures,
},
],
[
{
title: 'Mermaid Pro',
description: 'Unlock AI and real-time collaboration',
highlighted: true,
redBarText: 'Recommended',
proTrialButtonText: 'Start free trial',
proTrialUrl:
'https://www.mermaidchart.com/app/sign-up?utm_source=mermaid_js&utm_medium=editor_selection_C&utm_campaign=start_trial&redirect=%2Fapp%2Fuser%2Fbilling%2Fcheckout%3FisFromMermaid%3Dtrue',
features: mermaidChartFeatures,
},
{
title: 'Open Source',
description: 'Code only, no login',
buttonText: 'Start free',
redirectUrl: 'https://mermaid.live/edit',
features: openSourceFeatures,
},
],
[
{
title: 'Mermaid Pro',
description: 'Unlock AI and real-time collaboration',
highlighted: true,
redBarText: 'Recommended',
proTrialButtonText: 'Start free',
proTrialUrl:
'https://www.mermaidchart.com/app/sign-up?utm_source=mermaid_js&utm_medium=editor_selection_D&utm_campaign=start_free',
features: mermaidChartFeatures,
},
{
title: 'Open Source',
description: 'Code only, no login',
redirectUrl: 'https://mermaid.live/edit',
buttonText: 'Start free',
features: openSourceFeatures,
},
],
];
const editorColumns = editorColumnVariants[Math.floor(Math.random() * editorColumnVariants.length)];
const isVisible = ref(false);
const handleMouseDown = (e: MouseEvent) => {
@@ -84,44 +164,53 @@ onUnmounted(() => {
<template>
<div
v-if="isVisible"
class="fixed top-0 left-0 z-50 flex h-screen w-screen backdrop-blur-sm items-center justify-center"
class="fixed top-0 left-0 z-50 flex h-screen w-screen backdrop-blur-sm items-center justify-center bg-[#8585A4]/40 plausible-event-name=editorSelectionModalOpen"
@click.self="isVisible = false"
>
<div class="flex flex-row rounded-3xl shadow relative gap-5 pt-20 pb-10 px-10 bg-[#F1F8FA]">
<div
class="flex flex-col sm:flex-row rounded-3xl shadow relative gap-5 pt-10 sm:pt-20 pb-10 px-4 sm:px-10 bg-[#F1F8FA] overflow-y-auto max-h-full"
>
<div
v-for="column in editorColumns"
class="w-80 flex relative flex-col justify-start items-center bg-[#dceef1] p-8 text-gray-800 shadow"
:key="column.title"
class="sm:w-96 flex relative flex-col justify-start items-center p-6 sm:p-8 text-gray-800 shadow w-full"
:class="
column.highlighted ? 'bg-white rounded-b-3xl shadow-xl' : 'bg-[#DCEEF1] rounded-3xl'
column.highlighted ? 'bg-white rounded-b-3xl mt-10 sm:mt-0' : 'bg-[#DCEEF1] rounded-3xl'
"
>
<div
v-if="column.highlighted"
class="absolute -top-10 w-full rounded-t-3xl bg-[#E0095F] py-2 flex justify-center"
>
<p class="text-lg font-semibold text-white">Best for collaboration</p>
<p class="text-lg font-semibold text-white">{{ column.redBarText }}</p>
</div>
<header class="mb-6 w-full text-start space-y-1">
<p class="text-2xl font-medium capitalize text-[#1E1A2E]">
{{ column.title }}
</p>
<p class="text-sm text-gray-600">
{{ column.description }}
</p>
<p class="text-2xl font-medium text-[#1E1A2E]">{{ column.title }}</p>
<p class="text-sm text-gray-600">{{ column.description }}</p>
</header>
<a
v-if="column.redirectUrl"
:href="column.redirectUrl"
target="_blank"
class="mb-6 flex h-10 w-full items-center justify-center rounded-xl hover:bg-[#272040] hover:text-white hover:shadow-md"
:class="
column.highlighted
? 'bg-[#1e1a2e] text-[#BEDDE3] hover:text-[#5CA3B4]'
: 'bg-[#BEDDE3] hover:bg-[#5CA3B4] text-[#1E1A2E]'
"
class="flex h-10 w-full bg-[#BEDDE3] hover:bg-[#5CA3B4] text-[#1E1A2E] items-center justify-center rounded-xl hover:text-white hover:shadow-md"
:class="column.isButtonMargined ? 'mb-[88px]' : ' mb-6'"
>
Start free
{{ column.buttonText }}
</a>
<a
v-if="column.proTrialUrl"
:href="column.proTrialUrl"
target="_blank"
class="mb-6 flex h-10 w-full text-white items-center justify-center rounded-xl bg-[#E0095F] hover:bg-[#B0134A]"
>
{{ column.proTrialButtonText || 'Start Pro trial' }}
</a>
<div class="h-px w-full bg-[#bedde3] mb-6"></div>
<ul class="w-full space-y-2">
<li
v-for="{ iconUrl, featureName } in column.features"

View File

@@ -1,8 +1,8 @@
import type { MarkdownOptions } from 'vitepress';
import { defineConfig } from 'vitepress';
import packageJson from '../../../package.json' assert { type: 'json' };
import MermaidExample from './mermaid-markdown-all.js';
import { addCanonicalUrls } from './canonical-urls.js';
import MermaidExample from './mermaid-markdown-all.js';
const allMarkdownTransformers: MarkdownOptions = {
// the shiki theme to highlight code blocks

View File

@@ -23,7 +23,7 @@ export default {
'home-hero-before': () => h(TopBar),
'doc-before': () => h(TopBar),
'layout-bottom': () => h(Tooltip),
'home-hero-after': () => h(EditorSelectionModal),
'layout-top': () => h(EditorSelectionModal),
});
},
enhanceApp({ app, router }: EnhanceAppContext) {

View File

@@ -0,0 +1,189 @@
---
references:
- "File: /packages/mermaid/src/diagrams/flowchart/flowDiagram.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowDb.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowDetector.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/styles.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/types.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/flowChartShapes.js"
- "File: /packages/mermaid/src/diagrams/flowchart/parser/flowParser.ts"
- "File: /packages/mermaid/src/diagrams/flowchart/elk/detector.ts"
generationTime: 2025-07-23T10:31:53.266Z
---
flowchart TD
%% Entry Points and Detection
Input["User Input Text"] --> Detection{Detection Phase}
Detection --> flowDetector["flowDetector.ts<br/>detector(txt, config)"]
Detection --> flowDetectorV2["flowDetector-v2.ts<br/>detector(txt, config)"]
Detection --> elkDetector["elk/detector.ts<br/>detector(txt, config)"]
flowDetector --> |"Checks /^\s*graph/"| DetectLegacy{Legacy Flowchart?}
flowDetectorV2 --> |"Checks /^\s*flowchart/"| DetectNew{New Flowchart?}
elkDetector --> |"Checks /^\s*flowchart-elk/"| DetectElk{ELK Layout?}
DetectLegacy --> |Yes| LoadDiagram
DetectNew --> |Yes| LoadDiagram
DetectElk --> |Yes| LoadDiagram
%% Loading Phase
LoadDiagram["loader() function"] --> flowDiagram["flowDiagram.ts<br/>diagram object"]
flowDiagram --> DiagramStructure{Diagram Components}
DiagramStructure --> Parser["parser: flowParser"]
DiagramStructure --> Database["db: new FlowDB()"]
DiagramStructure --> Renderer["renderer: flowRenderer-v3-unified"]
DiagramStructure --> Styles["styles: flowStyles"]
DiagramStructure --> Init["init: (cnf: MermaidConfig)"]
%% Parser Phase
Parser --> flowParser["parser/flowParser.ts<br/>newParser.parse(src)"]
flowParser --> |"Preprocesses src"| RemoveWhitespace["Remove trailing whitespace<br/>src.replace(/}\s*\n/g, '}\n')"]
RemoveWhitespace --> flowJison["parser/flow.jison<br/>flowJisonParser.parse(newSrc)"]
flowJison --> ParseGraph["Parse Graph Structure"]
ParseGraph --> ParseVertices["Parse Vertices"]
ParseGraph --> ParseEdges["Parse Edges"]
ParseGraph --> ParseSubgraphs["Parse Subgraphs"]
ParseGraph --> ParseClasses["Parse Classes"]
ParseGraph --> ParseStyles["Parse Styles"]
%% Database Phase - FlowDB Class
Database --> FlowDBClass["flowDb.ts<br/>FlowDB class"]
FlowDBClass --> DBInit["constructor()<br/>- Initialize counters<br/>- Bind methods<br/>- Setup toolTips<br/>- Call clear()"]
DBInit --> DBMethods{FlowDB Methods}
DBMethods --> addVertex["addVertex(id, textObj, type, style,<br/>classes, dir, props, metadata)"]
DBMethods --> addLink["addLink(_start[], _end[], linkData)"]
DBMethods --> addSingleLink["addSingleLink(_start, _end, type, id)"]
DBMethods --> setDirection["setDirection(dir)"]
DBMethods --> addSubGraph["addSubGraph(nodes[], id, title)"]
DBMethods --> addClass["addClass(id, style)"]
DBMethods --> setClass["setClass(ids, className)"]
DBMethods --> setTooltip["setTooltip(ids, tooltip)"]
DBMethods --> setClickEvent["setClickEvent(id, functionName, args)"]
DBMethods --> setClickFun["setClickFun(id, functionName, args)"]
%% Vertex Processing
addVertex --> VertexProcess{Vertex Processing}
VertexProcess --> CreateVertex["Create FlowVertex object<br/>- id, labelType, domId<br/>- styles[], classes[]"]
VertexProcess --> SanitizeText["sanitizeText(textObj.text)"]
VertexProcess --> ParseMetadata["Parse YAML metadata<br/>yaml.load(yamlData)"]
VertexProcess --> SetVertexProps["Set vertex properties<br/>- shape, label, icon, form<br/>- pos, img, constraint, w, h"]
%% Edge Processing
addSingleLink --> EdgeProcess{Edge Processing}
EdgeProcess --> CreateEdge["Create FlowEdge object<br/>- start, end, type, text<br/>- labelType, classes[]"]
EdgeProcess --> ProcessLinkText["Process link text<br/>- sanitizeText()<br/>- strip quotes"]
EdgeProcess --> SetEdgeProps["Set edge properties<br/>- type, stroke, length"]
EdgeProcess --> GenerateEdgeId["Generate edge ID<br/>getEdgeId(start, end, counter)"]
EdgeProcess --> ValidateEdgeLimit["Validate edge limit<br/>maxEdges check"]
%% Data Collection
DBMethods --> GetData["getData()"]
GetData --> CollectNodes["Collect nodes[] from vertices"]
GetData --> CollectEdges["Collect edges[] from edges"]
GetData --> ProcessSubGraphs["Process subgraphs<br/>- parentDB Map<br/>- subGraphDB Map"]
GetData --> AddNodeFromVertex["addNodeFromVertex()<br/>for each vertex"]
GetData --> ProcessEdgeTypes["destructEdgeType()<br/>arrowTypeStart, arrowTypeEnd"]
%% Node Creation
AddNodeFromVertex --> NodeCreation{Node Creation}
NodeCreation --> FindExistingNode["findNode(nodes, vertex.id)"]
NodeCreation --> CreateBaseNode["Create base node<br/>- id, label, parentId<br/>- cssStyles, cssClasses<br/>- shape, domId, tooltip"]
NodeCreation --> GetCompiledStyles["getCompiledStyles(classDefs)"]
NodeCreation --> GetTypeFromVertex["getTypeFromVertex(vertex)"]
%% Rendering Phase
Renderer --> flowRendererV3["flowRenderer-v3-unified.ts<br/>draw(text, id, version, diag)"]
flowRendererV3 --> RenderInit["Initialize rendering<br/>- getConfig()<br/>- handle securityLevel<br/>- getDiagramElement()"]
RenderInit --> GetLayoutData["diag.db.getData()<br/>as LayoutData"]
GetLayoutData --> SetupLayoutData["Setup layout data<br/>- type, layoutAlgorithm<br/>- direction, spacing<br/>- markers, diagramId"]
SetupLayoutData --> CallRender["render(data4Layout, svg)"]
CallRender --> SetupViewPort["setupViewPortForSVG(svg, padding)"]
SetupViewPort --> ProcessLinks["Process vertex links<br/>- create anchor elements<br/>- handle click events"]
%% Shape Rendering
CallRender --> ShapeSystem["flowChartShapes.js<br/>Shape Functions"]
ShapeSystem --> ShapeFunctions{Shape Functions}
ShapeFunctions --> question["question(parent, bbox, node)"]
ShapeFunctions --> hexagon["hexagon(parent, bbox, node)"]
ShapeFunctions --> rect_left_inv_arrow["rect_left_inv_arrow(parent, bbox, node)"]
ShapeFunctions --> lean_right["lean_right(parent, bbox, node)"]
ShapeFunctions --> lean_left["lean_left(parent, bbox, node)"]
ShapeFunctions --> insertPolygonShape["insertPolygonShape(parent, w, h, points)"]
ShapeFunctions --> intersectPolygon["intersectPolygon(node, points, point)"]
ShapeFunctions --> intersectRect["intersectRect(node, point)"]
%% Styling System
Styles --> stylesTS["styles.ts<br/>getStyles(options)"]
stylesTS --> StyleOptions["FlowChartStyleOptions<br/>- arrowheadColor, border2<br/>- clusterBkg, mainBkg<br/>- fontFamily, textColor"]
StyleOptions --> GenerateCSS["Generate CSS styles<br/>- .label, .cluster-label<br/>- .node, .edgePath<br/>- .flowchart-link, .edgeLabel"]
GenerateCSS --> GetIconStyles["getIconStyles()"]
%% Type System
Parser --> TypeSystem["types.ts<br/>Type Definitions"]
TypeSystem --> FlowVertex["FlowVertex interface"]
TypeSystem --> FlowEdge["FlowEdge interface"]
TypeSystem --> FlowClass["FlowClass interface"]
TypeSystem --> FlowSubGraph["FlowSubGraph interface"]
TypeSystem --> FlowVertexTypeParam["FlowVertexTypeParam<br/>Shape types"]
%% Utility Functions
DBMethods --> UtilityFunctions{Utility Functions}
UtilityFunctions --> lookUpDomId["lookUpDomId(id)"]
UtilityFunctions --> getClasses["getClasses()"]
UtilityFunctions --> getDirection["getDirection()"]
UtilityFunctions --> getVertices["getVertices()"]
UtilityFunctions --> getEdges["getEdges()"]
UtilityFunctions --> getSubGraphs["getSubGraphs()"]
UtilityFunctions --> clear["clear()"]
UtilityFunctions --> defaultConfig["defaultConfig()"]
%% Event Handling
ProcessLinks --> EventHandling{Event Handling}
EventHandling --> setupToolTips["setupToolTips(element)"]
EventHandling --> bindFunctions["bindFunctions(element)"]
EventHandling --> runFunc["utils.runFunc(functionName, args)"]
%% Common Database Functions
DBMethods --> CommonDB["commonDb.js functions"]
CommonDB --> setAccTitle["setAccTitle()"]
CommonDB --> getAccTitle["getAccTitle()"]
CommonDB --> setAccDescription["setAccDescription()"]
CommonDB --> getAccDescription["getAccDescription()"]
CommonDB --> setDiagramTitle["setDiagramTitle()"]
CommonDB --> getDiagramTitle["getDiagramTitle()"]
CommonDB --> commonClear["clear()"]
%% Final Output
ProcessLinks --> FinalSVG["Final SVG Output"]
%% Layout Algorithm Selection
SetupLayoutData --> LayoutAlgorithm{Layout Algorithm}
LayoutAlgorithm --> Dagre["dagre<br/>(default)"]
LayoutAlgorithm --> DagreWrapper["dagre-wrapper<br/>(v2 renderer)"]
LayoutAlgorithm --> ELK["elk<br/>(external package)"]
%% Testing Components
FlowDBClass --> TestFiles["Test Files"]
TestFiles --> flowDbSpec["flowDb.spec.ts"]
TestFiles --> flowChartShapesSpec["flowChartShapes.spec.js"]
TestFiles --> ParserTests["parser/*.spec.js files<br/>- flow-text.spec.js<br/>- flow-edges.spec.js<br/>- flow-style.spec.js<br/>- subgraph.spec.js"]
%% Configuration
Init --> ConfigSetup["Configuration Setup"]
ConfigSetup --> FlowchartConfig["cnf.flowchart config"]
ConfigSetup --> ArrowMarkers["arrowMarkerAbsolute"]
ConfigSetup --> LayoutConfig["layout config"]
ConfigSetup --> SetConfig["setConfig() calls"]

Some files were not shown because too many files have changed in this diff Show More