Compare commits

...

274 Commits

Author SHA1 Message Date
Knut Sveidqvist
7f91ed32bc Adding dev tool for simplified development 2025-12-22 09:28:04 +01:00
Sidharth Vinod
d5bc07dc0c fix: Remove unnecessary changeset 2025-12-19 16:50:52 +05:30
Knut Sveidqvist
80a686be03 Merge pull request #7245 from mermaid-js/canonicals-to-mermaid-ai
setting canonicals to mermaid.ai/open-source
2025-12-19 10:12:44 +00:00
autofix-ci[bot]
d26f2c6043 [autofix.ci] apply automated fixes 2025-12-15 14:42:00 +00:00
Knut Sveidqvist
8ca7a28bf3 Update canonicals in big-trees-walk.md
Set canonicals for Mermaid documentation to mermaid.ai/open-source.
2025-12-15 15:32:33 +01:00
Knut Sveidqvist
6b77c9c4c7 setting canonicals to mermaid.ai/open-source 2025-12-15 15:17:56 +01:00
Shubham P
7b167cf331 Merge pull request #7242 from mermaid-js/renovate/patch-dompurify
fix(deps): update dependency dompurify to ^3.3.1
2025-12-15 11:41:32 +00:00
Shubham P
d435ac6fe1 Merge pull request #7228 from mermaid-js/renovate/peter-evans-create-pull-request-digest
chore(deps): update peter-evans/create-pull-request digest to 0979079
2025-12-15 06:47:12 +00:00
renovate[bot]
ed96d067fc fix(deps): update dependency dompurify to ^3.3.1 2025-12-15 00:42:27 +00:00
renovate[bot]
09c60be450 chore(deps): update peter-evans/create-pull-request digest to 0979079 2025-12-10 10:49:20 +00:00
Sidharth Vinod
39d070fdea Merge pull request #7215 from mermaid-js/update-timings
Update E2E Timings
2025-12-04 13:42:51 +05:30
github-actions[bot]
4f6f627e75 chore: update E2E timings 2025-12-04 04:17:52 +00:00
Sidharth Vinod
53570ee815 Merge pull request #7204 from leandroebner/patch-1
Add HackMD integration to community integrations list
2025-12-03 09:58:48 +00:00
Shubham P
a3a5040d79 Merge pull request #7206 from mermaid-js/renovate/patch-all-patch
fix(deps): update all patch dependencies (patch)
2025-12-02 08:57:58 +00:00
renovate[bot]
7ff9bf1a50 fix(deps): update all patch dependencies 2025-12-02 07:42:41 +00:00
Shubham P
ffd38716d0 Merge pull request #7208 from mermaid-js/renovate/npm-express-vulnerability
chore(deps): update dependency express to v5.2.0 [security]
2025-12-02 07:28:21 +00:00
Shubham P
4313f233d2 Merge pull request #7205 from mermaid-js/renovate/patch-eslint
chore(deps): update dependency @cspell/eslint-plugin to ^9.3.2
2025-12-02 07:27:24 +00:00
renovate[bot]
56564f3807 chore(deps): update dependency express to v5.2.0 [security] 2025-12-01 20:55:04 +00:00
renovate[bot]
7e1a1de5e9 chore(deps): update dependency @cspell/eslint-plugin to ^9.3.2 2025-12-01 02:27:03 +00:00
autofix-ci[bot]
b4b90417cf [autofix.ci] apply automated fixes 2025-11-30 19:33:42 +00:00
Leandro Ebner
6bf2b2108c Add HackMD integration to community integrations list
Works natively, see hackmd docs.
2025-11-30 20:22:15 +01:00
Shubham P
614129ca31 Merge pull request #7197 from mermaid-js/fix/5496-gantt-tickinterval-app-crash
5496 : fixed UI crash from excessive tick generation with invalid dates/intervals
2025-11-27 17:09:07 +00:00
omkarht
b115ad3cd7 fix: remove fallback to new Date() for non-timestamp formats in date validation
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-27 21:54:04 +05:30
omkarht
73b8626ab0 fix: enhance Gantt chart handling for invalid date formats and tick intervals
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-27 20:42:59 +05:30
omkarht
88e8ad6f5b fix: improve error handling for invalid date formats in Gantt chart
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-27 19:17:37 +05:30
omkarht
ac5fdb8121 Merge branch 'develop' into fix/5496-gantt-tickinterval-app-crash 2025-11-27 15:16:30 +05:30
Shubham P
a30b3bb3f8 Merge pull request #7178 from mermaid-js/fix/7167-treemap-stroke-border-removal
7167 : fix classDef style application for treemap diagramtype
2025-11-27 08:47:18 +00:00
omkarht
87c561615e fix: extend dayjs with duration plugin for improved time calculations
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-27 13:28:21 +05:30
omkarht
0843a2fa7a fix: optimize tick interval calculation using dayjs for improved accuracy
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-27 12:38:30 +05:30
omkarht
1aa4cd9847 Merge branch 'develop' into fix/7167-treemap-stroke-border-removal 2025-11-27 12:23:27 +05:30
omkarht
8d1cdc41c2 test: enhance treemap tests with classDef styling scenarios
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-27 12:22:54 +05:30
omkarht
bf50ce5237 fix: handle uncaught exceptions in Gantt chart rendering test for invalid dates
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-26 18:59:18 +05:30
omkarht
8bfd47758a chore: add changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-26 17:30:00 +05:30
omkarht
88fd141276 fix: correct Gantt diagram dateFormat syntax in test case
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-26 15:46:59 +05:30
omkarht
454238867b chore: add tests for handling invalid and non-standard date formats in ganttDb
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-26 13:55:00 +05:30
omkarht
6025ec663c 5496 : fixed UI crash from excessive tick generation with invalid dates/intervals
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-26 13:39:22 +05:30
Alois Klink
2346801c30 Merge pull request #7188 from kimulaco/docs/7187_fix-radar-diagram-example
docs: Remove trailing comma from radar diagram example
2025-11-25 08:21:28 +00:00
Shubham P
d7eb94dc89 Merge pull request #7172 from mermaid-js/docs/correct-block-arrow-example
docs(block): correct block arrow example
2025-11-25 06:57:41 +00:00
Shubham P
db9c683316 Merge pull request #7192 from mermaid-js/renovate/patch-all-patch
chore(deps): update all patch dependencies (patch)
2025-11-24 08:09:55 +00:00
Shubham P
c9b5c5fed6 Merge pull request #7191 from mermaid-js/renovate/peter-evans-create-pull-request-digest
chore(deps): update peter-evans/create-pull-request digest to 84ae59a
2025-11-24 08:09:28 +00:00
renovate[bot]
324cf05afd chore(deps): update all patch dependencies 2025-11-24 00:50:00 +00:00
renovate[bot]
a357c1079f chore(deps): update peter-evans/create-pull-request digest to 84ae59a 2025-11-24 00:49:13 +00:00
kimulaco
e20b079707 docs(radar): remove trailing commas 2025-11-22 18:44:44 +09:00
Shubham P
b1fe4ffe97 Merge pull request #7136 from mermaid-js/feat/sequence-alias-support-new-participant-syntax
feat : add alias support for new participant syntax
2025-11-20 07:46:34 +00:00
Shubham P
61c18b99a0 Merge pull request #7164 from mermaid-js/renovate/patch-all-patch
chore(deps): update all patch dependencies (patch)
2025-11-20 07:20:51 +00:00
Shubham P
04ac594f5b Merge pull request #7163 from mermaid-js/renovate/peter-evans-create-pull-request-digest
chore(deps): update peter-evans/create-pull-request digest to b4733b9
2025-11-20 07:20:35 +00:00
Shubham P
a6c2b1d85e Merge pull request #7161 from mermaid-js/renovate/npm-js-yaml-vulnerability
chore(deps): update dependency js-yaml to v4.1.1 [security]
2025-11-20 07:20:25 +00:00
omkarht
96ca7c090f chore : add changeset for classDef style application
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-20 12:43:56 +05:30
renovate[bot]
3c752421a2 chore(deps): update all patch dependencies 2025-11-19 17:03:12 +00:00
omkarht
bf7c532e43 7167 : fix classDef style application for treemap diagramtype
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-19 19:46:35 +05:30
omkarht
8add133bbe Merge branch 'develop' into feat/sequence-alias-support-new-participant-syntax 2025-11-19 15:10:45 +05:30
omkarht
8cf0d3373d docs: add documentation for alias support in sequence diagram participant syntax
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-19 15:10:04 +05:30
renovate[bot]
ff3b194925 chore(deps): update dependency js-yaml to v4.1.1 [security] 2025-11-18 17:47:01 +00:00
autofix-ci[bot]
6e869ff8dc [autofix.ci] apply automated fixes 2025-11-18 08:41:51 +00:00
Alois Klink
9df18da01c docs(block): correct block arrow example
The same ID meant we were overriding the previous arrow.

Co-authored-by: jonathanpoelen <1436727+jonathanpoelen@users.noreply.github.com>
Fixes: https://github.com/mermaid-js/mermaid/issues/7159
Fixes: a0d328d734
2025-11-18 17:34:36 +09:00
renovate[bot]
608d623641 chore(deps): update peter-evans/create-pull-request digest to b4733b9 2025-11-17 01:33:08 +00:00
Sidharth Vinod
ecf9ea1134 Merge pull request #7099 from mermaid-js/fix/mindmap-level-node-rendering
7000: Fix mindmap rendering issue when Level 2 nodes exceed 11
2025-11-14 12:21:56 +00:00
autofix-ci[bot]
b33ce14932 [autofix.ci] apply automated fixes 2025-11-14 06:55:46 +00:00
omkarht
283aef54e4 Merge branch 'develop' into feat/sequence-alias-support-new-participant-syntax 2025-11-14 12:16:07 +05:30
omkarht
96f87fd597 feat: add inline alias attribute support for participants and actors in sequence diagrams
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-14 12:15:03 +05:30
darshanr0107
03e8589818 chore: add visual test
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-14 11:14:21 +05:30
Shubham P
6b9f26dac8 Merge pull request #7080 from mermaid-js/7079-c4context-componentqueue-ext-lexical-error
7079: add missing support for ComponentQueue_Ext in C4Context diagrams
2025-11-11 04:07:06 +00:00
Shubham P
ea590cdafe Merge pull request #7075 from mermaid-js/6889-fix-escaped-p-tags-in-sandbox-mode
6889: Fix escaped <p> tags in labels when securityLevel is set to "sandbox"
2025-11-11 04:05:37 +00:00
Shubham P
f3769c70bc Merge branch 'develop' into 7079-c4context-componentqueue-ext-lexical-error 2025-11-11 09:24:14 +05:30
Shubham P
4cf4d15197 Merge branch 'develop' into 6889-fix-escaped-p-tags-in-sandbox-mode 2025-11-11 09:23:23 +05:30
Shubham P
c02cf92656 Merge pull request #7149 from mermaid-js/renovate/patch-dompurify
fix(deps): update dependency dompurify to ^3.3.0
2025-11-10 10:15:31 +00:00
Shubham P
3a1266892d Merge pull request #7148 from mermaid-js/renovate/patch-all-patch
fix(deps): update all patch dependencies (patch)
2025-11-10 10:15:16 +00:00
renovate[bot]
67e81de557 fix(deps): update dependency dompurify to ^3.3.0 2025-11-10 02:54:45 +00:00
renovate[bot]
847b3aa24e fix(deps): update all patch dependencies 2025-11-10 02:54:19 +00:00
Sidharth Vinod
85a13da40f Merge pull request #7138 from mermaid-js/update-timings
Update E2E Timings
2025-11-07 21:42:11 +09:00
darshanr0107
9ec0e8f932 Merge branch 'develop' of https://github.com/mermaid-js/mermaid into 6889-fix-escaped-p-tags-in-sandbox-mode 2025-11-07 11:58:38 +05:30
darshanr0107
9585ee7533 chore:add e2e test
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-07 11:46:41 +05:30
github-actions[bot]
1269486124 chore: update E2E timings 2025-11-07 04:15:01 +00:00
Shubham P
f45ea0c60e Merge pull request #7096 from mermaid-js/renovate/npm-vite-vulnerability
chore(deps): update dependency vite to v7.1.11 [security]
2025-11-06 14:52:25 +00:00
renovate[bot]
d20955a56a chore(deps): update dependency vite to v7.1.11 [security] 2025-11-06 12:46:25 +00:00
Shubham P
fb66b3fbe3 Merge pull request #7049 from mermaid-js/renovate/major-eslint
chore(deps): update eslint (major)
2025-11-06 12:31:47 +00:00
Shubham P
82ea5d63bb Merge pull request #7017 from mermaid-js/renovate/peter-evans-create-pull-request-digest
chore(deps): update peter-evans/create-pull-request digest to 0edc001
2025-11-06 12:30:09 +00:00
renovate[bot]
881e74087a chore(deps): update eslint 2025-11-06 12:06:45 +00:00
renovate[bot]
09920c0497 chore(deps): update peter-evans/create-pull-request digest to 0edc001 2025-11-06 12:05:55 +00:00
Shubham P
8065d65cd7 Merge pull request #6973 from mermaid-js/renovate/patch-dompurify
fix(deps): update dependency dompurify to ^3.2.7
2025-11-06 11:52:40 +00:00
omkarht
6bc6617ca6 chore: add changeset 2025-11-06 17:09:42 +05:30
omkarht
29ed57ffec test: add tests for participant new syntax aliases in sequence diagrams 2025-11-06 16:59:46 +05:30
omkarht
9fdc4b8005 feat : add alias support for new participant syntax
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-06 16:49:09 +05:30
renovate[bot]
09b841f781 fix(deps): update dependency dompurify to ^3.2.7 2025-11-06 10:46:44 +00:00
Shubham P
d0f9dc0c9b Merge pull request #7018 from mermaid-js/renovate/patch-all-patch
fix(deps): update all patch dependencies (patch)
2025-11-06 10:28:10 +00:00
renovate[bot]
15e2824d53 fix(deps): update all patch dependencies 2025-11-06 10:04:11 +00:00
Shubham P
7eb582e860 Merge pull request #7135 from mermaid-js/fix/er-numeric-entity-test-conflict
refactor: update test description for standalone numeric entities in ER diagram
2025-11-06 09:51:04 +00:00
omkarht
6ca928f31f refactor: update test description for standalone numeric entities in ER diagram 2025-11-06 15:05:58 +05:30
darshanr0107
983120d945 fix: add test case for C4Context diagram with ComponentQueue_Ext
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-05 17:18:34 +05:30
darshanr0107
61f74ffc5e fix: incorrect section number logic by using index % (MAX_SECTIONS - 1)
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-05 14:54:51 +05:30
darshanr0107
74318f9337 chore: use MAX_SECTIONS constant instead of hardcoded value
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-05 14:28:59 +05:30
Sidharth Vinod
dfd59470dc Merge pull request #7129 from mermaid-js/sidv/injectVersion
feat: Optimize bundling to remove shipping package.json inside mermaid's JS bundle.
2025-11-04 18:42:32 +00:00
Sidharth Vinod
4aac6fa448 Merge pull request #7128 from CNOCTAVE/develop
add doc to use marmaid.js in GNU Octave
2025-11-04 17:42:28 +00:00
CNOCTAVE
5f96f80efb Merge branch 'develop' of https://github.com/CNOCTAVE/mermaid into develop 2025-11-05 01:16:47 +08:00
CNOCTAVE
545801e144 Update integrations-community.md 2025-11-05 01:16:17 +08:00
CNOCTAVE
0bd74759cc Merge branch 'develop' into develop 2025-11-05 01:13:00 +08:00
CNOCTAVE
e4cf266c1d remove changeset
remove changeset
2025-11-05 01:12:15 +08:00
CNOCTAVE
c0e1662e50 Update packages/mermaid/src/docs/ecosystem/integrations-community.md
Co-authored-by: Sidharth Vinod <github@sidharth.dev>
2025-11-05 01:08:04 +08:00
Sidharth Vinod
6546aed482 chore: forbid use of viewbox in code 2025-11-05 00:07:30 +07:00
Sidharth Vinod
b76ccae065 feat: Optimize bundling to remove shipping package.json inside mermaid's JS bundle.
Move all build time injected variables to `injected.` namespace to avoid conflicts.
2025-11-04 23:49:54 +07:00
Sidharth Vinod
287a9a3fcb chore: Add missing clean scripts
Co-authored-by: Alois Klink <alois@aloisklink.com>
2025-11-04 21:21:58 +07:00
Sidharth Vinod
7f5160fa4d chore: Remove NPM_TOKEN from release workflow 2025-11-04 21:12:10 +07:00
autofix-ci[bot]
7b0763f262 [autofix.ci] apply automated fixes 2025-11-04 12:43:03 +00:00
CNOCTAVE
38c289818c feat: add doc to use marmaid.js in GNU Octave
feat: add doc to use marmaid.js in GNU Octave. Resolve #7073
2025-11-04 20:34:13 +08:00
CNOCTAVE
57530076aa add doc to use marmaid.js in GNU Octave
add doc to use marmaid.js in GNU Octave. Resolve #7073
2025-11-04 20:29:53 +08:00
CNOCTAVE
f2d7877c7a Merge branch 'develop' of https://github.com/CNOCTAVE/mermaid into develop 2025-11-04 20:20:28 +08:00
Sidharth Vinod
62d2c6505e Merge pull request #7109 from arnaudrenaud/add-speccharts
docs: Add speccharts to `integrations-community.md`
2025-11-04 11:57:01 +00:00
autofix-ci[bot]
974236bbb8 [autofix.ci] apply automated fixes 2025-11-04 11:48:23 +00:00
Sidharth Vinod
cf0d1248a4 chore: Add speccharts to cspell 2025-11-04 18:42:42 +07:00
Alois Klink
ebefbd87a8 Merge pull request #7127 from mermaid-js/sidharthv96-patch-2
Refactor GitHub Actions workflow for lockfile validation
2025-11-04 07:43:17 +00:00
Sidharth Vinod
1e7b71a085 Refactor GitHub Actions workflow for lockfile validation
Removed Node.js setup step and pnpm action version.

Co-authored-by: Alois Klink <alois@mermaidchart.com>
2025-11-03 23:30:27 -08:00
Sidharth Vinod
f28f3c25aa Merge pull request #7116 from mermaid-js/sidv/lockfileValidation
fix: Lockfile validation
2025-10-30 02:29:50 +09:00
Sidharth Vinod
58137aa631 feat: Allow validation workflow to run on forks 2025-10-30 01:56:18 +09:00
Sidharth Vinod
e7719f14c5 fix: Prevent duplicate comments by validation workflow 2025-10-30 01:55:58 +09:00
Sidharth Vinod
35d9cead8a chore: Cleanup lockfile 2025-10-30 01:22:17 +09:00
Ashish Jain
13baf51b35 Merge 11.12.1 back to develop 2025-10-28 11:23:19 +01:00
Justin Greywolf
9af985ba9b Merge pull request #5814 from kairi003/feature/4706_allow_notes_in_namespace
feat: allow to put notes in namespaces on classDiagram
2025-10-27 19:21:00 +00:00
Arnaud Renaud
c7f8a11ded Add speccharts to integrations-community.md 2025-10-27 14:16:35 +01:00
Sidharth Vinod
762b44cf33 Merge pull request #7108 from mermaid-js/changeset-release/master
Version Packages
2025-10-27 18:35:34 +05:30
github-actions[bot]
02c0091106 Version Packages 2025-10-27 12:08:22 +00:00
Sidharth Vinod
16359adc33 Merge pull request #7107 from mermaid-js/patch/dagre-d3-es-7.0.13
fix: update dagre-d3-es to version 7.0.13
2025-10-27 17:35:36 +05:30
Shubham P
061632c580 Update .changeset/slick-wasps-bathe.md
Co-authored-by: Alois Klink <alois@mermaidchart.com>
2025-10-27 16:20:24 +05:30
shubhamparikh2704
cbf89462ac fix: update dagre-d3-es to version 7.0.13 2025-10-27 15:06:24 +05:30
kairi003
700aa100f2 fix: style broken caused by a mistake in merge 3f46c94a 2025-10-26 04:18:19 +09:00
kairi003
49103ea654 fix: update ClassDB and ClassTypes for improved type safety and consistency 2025-10-26 03:01:00 +09:00
kairi003
3f46c94ab2 Merge branch 'develop' into tmp/feature/4706_allow_notes_in_namespace 2025-10-26 02:39:38 +09:00
darshanr0107
b136acdc67 chore:add changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-22 17:53:48 +05:30
darshanr0107
bba5e5938e fix: mindmap rendering issue when level nodes exceed 11
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-22 17:06:21 +05:30
Knut Sveidqvist
fed8a523a4 Merge pull request #7076 from mermaid-js/6919-fix-incorrect-viewBox-casing
6919: correct viewBox casing in Radar & Packet
2025-10-21 12:20:04 +00:00
Knut Sveidqvist
33b4946e21 Merge branch 'develop' into 6919-fix-incorrect-viewBox-casing 2025-10-21 10:33:21 +02:00
Justin Greywolf
3d768f3adf Merge pull request #6975 from ilovelinux/fix/6974-ilovelinux-fix-classdiagram-example
Fix classDiagram example
2025-10-17 02:56:34 +00:00
darshanr0107
76e17ffd20 fix: add validation to ensure correct casing of 'viewBox' in all rendering tests
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-15 18:56:49 +05:30
darshanr0107
835de0012d fix:ComponentQueue_Ext throws lexical error
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-14 19:00:17 +05:30
darshanr0107
60f633101c chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-13 19:14:22 +05:30
Shubham P
18f51eb14e Merge pull request #6843 from saurabhg772244/saurabh/fix-edge-animation-for-hand-dranw-shapes
Fixed edge animation for hand drawn shapes
2025-10-13 13:14:24 +00:00
Shubham P
2bb57bf7d2 Merge pull request #6989 from mermaid-js/subgraph-td-direction
6338: allow direction TD inside subgraphs
2025-10-13 13:13:46 +00:00
Shubham P
a6276daffd Merge pull request #7055 from mermaid-js/sequence-diagram-participant-name-parsing
6853:  prevent browser freeze caused by invalid participant name in sequenceDiagram
2025-10-13 13:12:08 +00:00
darshanr0107
7def6eecbf fix: correct viewBox casing in radar and packet diagram
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-13 18:15:38 +05:30
darshanr0107
96a766dcdb chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-13 13:42:41 +05:30
darshanr0107
39d7ebd32e fix: escaped p tags in sandbox mode
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-13 13:16:58 +05:30
CNOCTAVE
34f40f0794 add doc to use marmaid.js in GNU Octave 2025-10-13 15:11:44 +08:00
CNOCTAVE
32ac2c689d add doc to use marmaid.js in GNU Octave 2025-10-13 15:02:46 +08:00
CNOCTAVE
dbcadc1d0b add doc to use marmaid.js in GNU Octave 2025-10-13 13:56:04 +08:00
Antonio Spadaro
ac411a7d7e Fix class diagram example 2025-10-11 22:56:37 +02:00
Justin Greywolf
d80a638e55 Merge pull request #6023 from yari-dewalt/fix_lollipop_stroke
Fix: Delete 'stroke: black' from lollipop marker so it matches themes and edge paths
2025-10-11 20:43:08 +00:00
Justin Greywolf
7a869c08a2 Merge pull request #6026 from yari-dewalt/bug/5669_class-diagram-fix-namespace-themes
Fix: Class diagram namespaces black lines, not responding to theme variables
2025-10-11 20:42:53 +00:00
Justin Greywolf
44e8cbb1de Merge branch 'develop' into bug/5669_class-diagram-fix-namespace-themes 2025-10-11 13:27:47 -07:00
Justin Greywolf
efe38b8425 Merge branch 'develop' into fix_lollipop_stroke 2025-10-11 13:26:59 -07:00
darshanr0107
6fecb985e8 fix: failing argos
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-09 11:50:44 +05:30
darshanr0107
69b338d8af fix: formatting issues
Co-authored-by: omkarht <omkar@mermaidchart.com>
2025-10-09 11:37:05 +05:30
darshanr0107
fa15ce8502 chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-08 13:23:28 +05:30
darshanr0107
6d0650918f fix: handle participant names with spaces correctly in sequenceDiagram
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-08 13:09:30 +05:30
Shubham P
c728d864c8 Merge pull request #7054 from mermaid-js/fix/update-argos-ci-version
fix: update @argos-ci/cypress to version 6.1.3
2025-10-07 08:34:58 +00:00
shubhamparikh2704
99f17bea3a fix: update @argos-ci/cypress to version 6.1.3 2025-10-07 13:39:28 +05:30
Sidharth Vinod
ad82448084 Merge pull request #7053 from mermaid-js/changeset-release/master
Version Packages
2025-10-07 13:23:31 +05:30
github-actions[bot]
9498619d3c Version Packages 2025-10-07 07:36:05 +00:00
Sidharth Vinod
7a8557a1a2 Merge pull request #7036 from mermaid-js/knsv-patch-1
Update free plan diagram limit from 3 to 6
2025-10-07 13:03:39 +05:30
Sidharth Vinod
74863c94fb Merge pull request #7051 from mermaid-js/patch-parser-release
Pre Release
2025-10-07 12:34:22 +05:30
shubhamparikh2704
63df702146 chore: added changeset file 2025-10-07 10:47:16 +05:30
autofix-ci[bot]
421f8d4633 [autofix.ci] apply automated fixes 2025-10-03 07:41:08 +00:00
Knut Sveidqvist
bf6e1a594c Update free plan diagram limit from 3 to 6 2025-10-03 09:35:21 +02:00
Ashish Jain
c1c14e401a Merge pull request #7019 from mermaid-js/tmp-mindmap-elk
mindmap breaking issue in ELK layout
2025-09-30 09:02:40 +00:00
darshanr0107
8b3057f27c fix: guard nodeDb[node.id] against undefined
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-30 12:27:53 +05:30
Shubham P
717d3b3bb2 Merge pull request #6984 from mermaid-js/fix/er-diagram-syntax-error-special-chars
fix(er-diagram): handle syntax errors for special characters in node names
2025-09-29 08:37:03 +00:00
Shubham P
2f8d9ba958 Merge pull request #6789 from mermaid-js/6638-sequence-diagram-additional-messages
6638:sequence diagram additional messages
2025-09-29 08:08:33 +00:00
darshanr0107
ace0367afd chore: add changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-29 13:37:10 +05:30
Knut Sveidqvist
b983626587 Fix for reference issue affecting mindmaps 2025-09-26 15:24:20 +02:00
Shubham P
7effdc147b Merge pull request #6997 from mermaid-js/ci/enable-codeql-for-github-actions
ci(codeql): enable CodeQL for GitHub Actions
2025-09-25 08:47:48 +00:00
Alois Klink
6e67515f41 ci(codeql): enable CodeQL for GitHub Actions
Support for scanning GitHub Actions was added in 2024-12-17, see
https://github.blog/changelog/2024-12-17-find-and-fix-actions-workflows-vulnerabilities-with-codeql-public-preview/
2025-09-25 17:17:12 +09:00
darshanr0107
1a9d45abf0 fix: allow direction TD inside subgraphs
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-24 12:22:31 +05:30
omkarht
09b74f1c29 chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-23 13:39:45 +05:30
omkarht
880da21908 test: add tests for handling special characters and numeric entity names
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-23 13:29:19 +05:30
omkarht
38191243be Merge branch 'develop' into fix/er-diagram-syntax-error-special-chars 2025-09-23 12:41:42 +05:30
omkarht
b75dcb8a82 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-23 12:40:39 +05:30
omkarht
4c1e170f4a fix(er-diagram): handle syntax errors for special characters in node names 2025-09-23 12:39:29 +05:30
Shubham P
d5c4eff251 Merge pull request #6972 from mermaid-js/renovate/patch-all-patch
fix(deps): update all patch dependencies (patch)
2025-09-22 13:49:30 +00:00
renovate[bot]
5324fd8dfd fix(deps): update all patch dependencies 2025-09-22 13:35:45 +00:00
omkarht
bd25b88a01 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-22 13:36:01 +05:30
Sidharth Vinod
0116b272b4 Merge pull request #6961 from mermaid-js/changeset-release/master
Version Packages
2025-09-18 10:50:42 +05:30
github-actions[bot]
634f3367da Version Packages 2025-09-17 17:48:06 +00:00
Sidharth Vinod
37e3a6951b Merge pull request #6956 from mermaid-js/develop
Pre Release
2025-09-17 23:15:32 +05:30
Sidharth Vinod
0de0b063e4 Merge pull request #6958 from dijitali/fix/arch-icon-docs
fix(docs) correct phrasing for architecture icon installation
2025-09-17 23:06:48 +05:30
Shubham P
619515e5a9 Merge pull request #6925 from mermaid-js/renovate/patch-all-patch
fix(deps): update all patch dependencies (patch)
2025-09-17 14:16:47 +00:00
shubhamparikh2704
59c8b07509 Merge branch 'develop' of https://github.com/mermaid-js/mermaid into renovate/patch-all-patch 2025-09-17 19:34:56 +05:30
Sidharth Vinod
9e72bbf62d Merge pull request #6955 from mermaid-js/sidv/updateArgos
chore: Update argos to 6.1.0
2025-09-17 18:48:40 +05:30
Sidharth Vinod
2a2c46f1e2 chore: Update argos to 6.1.1 2025-09-17 18:14:03 +05:30
Shubham P
f25df353d4 Merge pull request #6959 from mermaid-js/remove-changeset
chore: remove duplicate changeset file
2025-09-17 10:50:09 +00:00
shubhamparikh2704
398345a8bc chore: remove duplicate changeset file 2025-09-17 16:07:45 +05:30
autofix-ci[bot]
7fd2d94ef7 [autofix.ci] apply automated fixes 2025-09-17 08:47:15 +00:00
Ieuan Jenkins
bcc1472b9d fix(docs) correct phrasing for architecture icon installation
docs gave the incorrect impression that iconify.design icons came preinstalled
2025-09-17 09:21:42 +01:00
shubhamparikh2704
ddc1cfe6c8 Revert "fix: downgrade @argos-ci/cypress to version 5.0.2"
This reverts commit 18e9c1174d.
2025-09-17 13:07:43 +05:30
omkarht
d3de3ecbbb Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-17 12:41:51 +05:30
shubhamparikh2704
18e9c1174d fix: downgrade @argos-ci/cypress to version 5.0.2 2025-09-17 12:14:29 +05:30
Sidharth Vinod
789018abf6 chore: Update argos to 6.1.0 2025-09-17 11:38:57 +05:30
Ashish Jain
e87f77a865 Merge pull request #6950 from mermaid-js/patch/mindmap-fix
Pre Release
2025-09-16 12:48:55 +02:00
shubhamparikh2704
a9579083bf chore: add changeset for fixing mindmap rendering and applying tidytree layout 2025-09-16 13:33:31 +05:30
Knut Sveidqvist
6fd78d0856 Merge pull request #6944 from mermaid-js/docs/fix-mindmap-rendering
6932:Fix mindmap rendering in docs and apply tidytree layout
2025-09-16 13:27:23 +05:30
Sidharth Vinod
531f5e9380 Merge pull request #6948 from mermaid-js/add-newest-blog-posts
DOCS: add newest blog posts
2025-09-16 11:45:06 +05:30
Steph
ad4c227477 add newest blog posts 2025-09-15 13:24:39 -07:00
omkarht
3964ce0a0f Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-15 18:15:59 +05:30
shubhamparikh2704
a44e3e992c refactor: remove unused dependencies from pnpm-lock.yaml 2025-09-15 14:37:50 +05:30
shubhamparikh2704
ca5b370ffb Merge branch 'develop' of https://github.com/mermaid-js/mermaid into renovate/patch-all-patch 2025-09-15 14:32:02 +05:30
omkarht
4dbabba8e8 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-15 12:50:11 +05:30
Saurabh Gore
bbb93b263d Merge branch 'develop' into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-13 11:37:21 +05:30
Sidharth Vinod
450754221e Merge pull request #6930 from mermaid-js/rajat-ht/chore-editor-selection-update
Chore: Simplify editor selection to single version
2025-09-11 19:57:08 +05:30
rajat-ht
7a4f5b62c9 chore: update variable name 2025-09-11 19:54:14 +05:30
omkarht
e3ef5e4208 fix: fixed central connection for bidirectional dotted arrow
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-11 13:03:06 +05:30
omkarht
daeb85bac2 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-11 12:40:55 +05:30
saurabhg772244
4240340a18 Merge branch 'develop' of github.com:mermaid-js/mermaid into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-11 11:36:26 +05:30
Shubham P
cf08ba0ef8 Merge branch 'develop' into renovate/patch-all-patch 2025-09-10 21:03:45 +05:30
rajat-ht
4829dfa8c5 Chore: Simplify editor selection to single version 2025-09-09 22:16:52 +05:30
autofix-ci[bot]
e7811886c3 [autofix.ci] apply automated fixes 2025-09-08 01:00:25 +00:00
renovate[bot]
32eda8565c fix(deps): update all patch dependencies 2025-09-08 00:54:41 +00:00
omkarht
ca10a259fa Merge branch 'develop' into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-05 16:28:39 +05:30
Saurabh Gore
0ed9c65572 Merge branch 'develop' into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-05 12:56:27 +05:30
saurabhg772244
56cc12690f Added option to skip screenshot for cypress tests 2025-09-05 12:47:07 +05:30
omkarht
2cdaf03ada Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-05 12:12:01 +05:30
Sidharth Vinod
9c85521e16 Merge pull request #6914 from mermaid-js/changeset-release/master
Version Packages
2025-09-04 20:36:52 +05:30
github-actions[bot]
8a565bce92 Version Packages 2025-09-04 14:12:57 +00:00
Sidharth Vinod
baf510b935 Merge pull request #6912 from mermaid-js/develop
Pre Release
2025-09-04 19:39:17 +05:30
omkarht
f6fa0260e7 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-04 12:56:49 +05:30
omkarht
29aad6d23c Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-04 12:12:03 +05:30
omkarht
82ef7b5fdb docs: add version placeholders for new features 2025-09-02 19:02:02 +05:30
omkarht
11cd3f1262 feat: add central connection rendering and parsing tests 2025-09-02 18:52:18 +05:30
omkarht
ac4aa94e78 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-09-02 15:13:07 +05:30
omkarht
c40faac80d Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-08-29 13:26:48 +05:30
omkarht
c530baed3f Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-08-28 13:07:57 +05:30
omkarht
045699de10 Merge branch 'develop' of https://github.com/mermaid-js/mermaid into 6638-sequence-diagram-additional-messages 2025-08-25 15:14:50 +05:30
omkarht
1988d24227 fix: fixed reverse arrows placing for autonumber
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-22 16:46:49 +05:30
omkarht
39f90debe7 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-08-22 13:03:01 +05:30
omkarht
73e9849f99 chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-21 16:12:25 +05:30
omkarht
5a05540a5f Merge branch 'develop' of https://github.com/mermaid-js/mermaid into 6638-sequence-diagram-additional-messages 2025-08-21 16:09:18 +05:30
omkarht
2b58df9665 fix: refactored documentation
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-19 14:53:08 +05:30
saurabhg772244
e6fb4a84da code refactor 2025-08-11 16:32:03 +05:30
saurabhg772244
32723b2de1 Added change set 2025-08-11 15:59:07 +05:30
saurabhg772244
18703782ee Fixed edge animation for hand drawn shapes 2025-08-11 15:52:49 +05:30
omkarht
0b42bdba07 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-08-11 15:28:48 +05:30
omkarht
74c96db3e2 docs: document new syntax for half-arrow message and central connection
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-07 12:47:31 +05:30
omkarht
bd47c57eaf chore: refactored code
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-06 18:56:58 +05:30
omkarht
3e5d2db514 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-08-06 18:56:18 +05:30
omkarht
40990bb096 Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-07-29 18:17:34 +05:30
omkarht
7ca0665764 fix: fixed failing test case
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-28 19:13:51 +05:30
omkarht
81a6a361ab Merge branch 'develop' into 6638-sequence-diagram-additional-messages 2025-07-28 16:51:38 +05:30
omkarht
62faacdeeb fix: fixed failing test cases
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-28 16:01:43 +05:30
omkarht
0e40d8e8a8 fix: fixed failing test cases
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-28 15:55:07 +05:30
omkarht
e8d6daf4f6 add support for central connection circle after arrow in lifeline direction
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-28 15:37:33 +05:30
omkarht
cb4ed605b2 chore: added rendering test cases for arrow head type
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-23 16:59:04 +05:30
omkarht
ba9db26bfa Lexing : Added Support for new arrow types through lexing
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-22 19:25:27 +05:30
omkarht
252b1837f7 added arrowhead types for dotted line type
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-22 19:20:06 +05:30
omkarht
6b9c15d7f0 added reverse arrow head types
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-22 16:30:21 +05:30
omkarht
fda640c90c fix: adjusted arrowhead design
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-21 19:03:29 +05:30
omkarht
584a789183 6638: add support for additional message types for sequence diagram
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-07-21 13:33:52 +05:30
autofix-ci[bot]
47297f7c26 [autofix.ci] apply automated fixes 2025-02-26 17:49:51 +00:00
Yari DeWalt
967aa0629e Merge branch 'develop' into bug/5669_class-diagram-fix-namespace-themes 2025-02-26 09:45:14 -08:00
Yari DeWalt
04b20a79b9 Merge branch 'develop' into fix_lollipop_stroke 2025-02-26 09:20:16 -08:00
kairi
d60b09cafc Merge branch 'develop' into feature/4706_allow_notes_in_namespace 2024-12-01 09:20:39 +09:00
kairi003
875827f59b Merge branch 'develop' into feature/4706_allow_notes_in_namespace 2024-11-21 07:16:47 +09:00
kairi003
c4e08261b5 Merge branch 'develop' into feature/4706_allow_notes_in_namespace 2024-11-10 23:56:48 +09:00
kairi003
70f679d2fa add: e2e test for classDiagram-v3 2024-11-10 20:27:35 +09:00
kairi003
25c43fa439 add: horizontal rules to demos/classchart.html 2024-11-10 02:34:31 +09:00
kairi003
ec1c6325d4 refactor: Add type annotations and optimize redundant set loop 2024-11-10 02:34:31 +09:00
kairi003
309ff6be38 fix: Reimplement notes in namespaces for classRenderer-v3 (#5880) 2024-11-10 02:32:20 +09:00
kairi003
02d368df05 chore: relax git version constraint in Dockerfile to allow patch updates 2024-11-10 01:58:29 +09:00
kairi003
4ee1fe2ca4 Merge branch 'master' into feature/4706_allow_notes_in_namespace 2024-11-10 01:52:46 +09:00
yari-dewalt
4ff2ae9f4e Add styles for clusters and remove hard-coded styling for namespace nodes 2024-11-06 09:00:21 -08:00
Yari DeWalt
7a729e8f16 Merge branch 'develop' into fix_lollipop_stroke 2024-11-04 10:21:00 -08:00
yari-dewalt
3c7fd95617 Remove black stroke from lollipops so it matches edge paths 2024-11-04 10:15:51 -08:00
kairi003
2dd29bee25 add changelogs 2024-09-08 02:13:33 +09:00
kairi003
259a508d8a add e2e test 2024-09-08 01:31:26 +09:00
kairi
1963064369 Merge branch 'develop' into feature/4706_allow_notes_in_namespace 2024-09-08 00:27:14 +09:00
kairi
a101ce803c Merge branch 'develop' into feature/4706_allow_notes_in_namespace 2024-09-04 14:14:19 +09:00
kairi003
fc0c7936d1 feat: Add support for adding notes to namespaces in class diagrams 2024-09-03 15:27:31 +09:00
kairi003
2d2add5b44 Change: use Map for notes 2024-09-03 15:22:59 +09:00
kairi003
58fd5ddbaf Add: demo for notes in namespace on classDialog 2024-09-03 15:22:41 +09:00
204 changed files with 11779 additions and 6718 deletions

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Prevent HTML tags from being escaped in sandbox label rendering

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Support edge animation in hand drawn look

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Resolved parsing error where direction TD was not recognized within subgraphs

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix(treemap): Fixed treemap classDef style application to properly apply user-defined styles

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Correct viewBox casing and make SVGs responsive

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Render newlines as spaces in class diagrams

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Handle arrows correctly when auto number is enabled

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Improve participant parsing and prevent recursive loops on invalid syntax

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
Add IDs in architecture diagrams

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Ensure edge label color is applied when using classDef with edge IDs

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: Added support for new participant types (`actor`, `boundary`, `control`, `entity`, `database`, `collections`, `queue`) in `sequenceDiagram`.

View File

@@ -1,7 +0,0 @@
---
'mermaid': minor
'@mermaid-js/layout-tidy-tree': minor
'@mermaid-js/layout-elk': minor
---
feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
feat: add alias support for new participant syntax of sequence diagrams

View File

@@ -0,0 +1,5 @@
---
'mermaid': minor
---
feat: Add half-arrowheads (solid & stick) and central connection support

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Resolve gantt chart crash due to invalid array length

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: Add IDs in architecture diagrams

View File

@@ -1,9 +0,0 @@
---
'mermaid': patch
---
chore: revert marked dependency from ^15.0.7 to ^16.0.0
- Reverted marked package version to ^16.0.0 for better compatibility
- This is a dependency update that maintains API compatibility
- All tests pass with the updated version

View File

@@ -0,0 +1,5 @@
---
'mermaid': minor
---
feat: allow to put notes in namespaces on classDiagram

View File

@@ -0,0 +1,5 @@
---
'@mermaid': patch
---
fix: Mindmap breaking in ELK layout

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix(er-diagram): prevent syntax error when using 'u', numbers, and decimals in node names

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Support ComponentQueue_Ext to prevent parsing error

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: validate dates and tick interval to prevent UI freeze/crash in gantt diagramtype

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Mindmap rendering issue when the number of Level 2 nodes exceeds 11

View File

@@ -1,3 +1,5 @@
!viewbox
# It should be viewBox
# This file contains coding related terms
ALPHANUM
antiscript

View File

@@ -64,6 +64,7 @@ rscratch
shiki
Slidev
sparkline
speccharts
sphinxcontrib
ssim
stylis

View File

@@ -0,0 +1,53 @@
# Dev Explorer frontend: architecture + debugging notes
## Root cause of the “CSS changes do nothing” problem
The page loads `/dev/styles.css`, but **document-level CSS does not apply through a shadow DOM boundary**.
Historically, `dev-explorer-app` was a `LitElement` using Lits default `shadowRoot`, while the rest of the UI used light DOM. That meant:
- The browser showed the _right classes_ (`card`, `card-folder`, `card-file`) in Elements panel.
- `/dev/styles.css` was clearly being served/updated.
- Yet computed styles for `.card` looked like UA defaults because the selector never matched across the shadow root.
Fix: make `dev-explorer-app` light DOM too (`createRenderRoot() { return this; }`), so `/dev/styles.css` reliably styles the whole UI.
## Debugging traps (and fast detection)
- **Shadow DOM trap**
- Symptom: “CSS is loaded but doesnt apply”, especially for simple class selectors.
- Fast check:
- DevTools console: `document.querySelector('dev-explorer-app')?.shadowRoot`
- If non-null, global CSS wont style inside it.
- Or: right-click an element you expect styled → “Reveal in Elements” → see if its under `#shadow-root`.
- **“Light DOM child inside shadow DOM parent” trap**
- Even if a child component uses `createRenderRoot() { return this; }`,
if its _rendered inside the parents shadow root_, its still effectively in shadow for document styles.
- **Dev loop trap (CSS-only changes dont trigger reload)**
- The server watches TypeScript bundle inputs + `.mmd` files; static `/dev/styles.css` previously didnt emit SSE reload events.
- That makes CSS changes look flaky unless you manually refresh.
- Fix: watch `.esbuild/dev-explorer/public/**/*` and emit SSE on changes.
- **Caching trap (less common here, but real)**
- If a query param is constant (`?v=3`) and you dont reload, the browser can keep a cached stylesheet.
- Fast check: DevTools → Network → disable cache + hard reload; or check “(from disk cache)” on the CSS request.
## Styling strategy recommendation (pragmatic)
For a dev-only explorer, keep it simple:
- **Light DOM everywhere**
- **One stylesheet**: `.esbuild/dev-explorer/public/styles.css` served as `/dev/styles.css`
- **Scoped selectors** under `dev-explorer-app` to avoid generic class collisions (`.header`, `.content`, etc.)
If you later _want_ Shadow DOM isolation, do it deliberately:
- Put UI styles in Lit `static styles` or adopt a `CSSStyleSheet` into `this.renderRoot.adoptedStyleSheets`.
- Avoid relying on document CSS selectors for component internals.
## Shoelace integration notes
- Current setup is correct for dev: `setBasePath('/dev/vendor/shoelace')` and `registerIconLibrary(...)`.
- Prefer theming via CSS variables (Shoelace tokens) rather than overriding internal parts everywhere.

View File

@@ -0,0 +1,187 @@
import { LitElement, html } from 'lit';
import '@shoelace-style/shoelace/dist/components/input/input.js';
export type LogLevel = 'info' | 'warn' | 'error';
export type LogEntry = {
ts: number;
level: LogLevel;
message: string;
};
function formatTs(ts: number) {
const d = new Date(ts);
return (
d.toLocaleTimeString(undefined, { hour12: false }) +
'.' +
String(d.getMilliseconds()).padStart(3, '0')
);
}
function levelVariant(level: LogLevel) {
switch (level) {
case 'error':
return 'danger';
case 'warn':
return 'warning';
default:
return 'neutral';
}
}
type DisplayLevel = 'debug' | LogLevel;
function displayLevel(entry: LogEntry): DisplayLevel {
// Mermaid often emits debug lines through console.log/info with a marker.
if (entry.message.includes(': DEBUG :')) return 'debug';
return entry.level;
}
function displayVariant(level: DisplayLevel) {
switch (level) {
case 'error':
return 'danger';
case 'warn':
return 'warning';
case 'debug':
return 'success';
default:
return 'neutral';
}
}
export class DevConsolePanel extends LitElement {
static properties = {
logs: { state: true },
showInfo: { state: true },
showWarn: { state: true },
showError: { state: true },
showDebug: { state: true },
filterText: { state: true },
};
declare logs: LogEntry[];
declare showInfo: boolean;
declare showWarn: boolean;
declare showError: boolean;
declare showDebug: boolean;
declare filterText: string;
constructor() {
super();
this.logs = [];
this.showInfo = true;
this.showWarn = true;
this.showError = true;
this.showDebug = true;
this.filterText = '';
}
createRenderRoot() {
return this;
}
clear() {
this.logs = [];
}
append(entry: LogEntry) {
this.logs = [...this.logs, entry];
}
async copyVisible() {
const visible = this.filteredLogs();
const text = visible
.map((l) => `[${formatTs(l.ts)}] ${l.level.toUpperCase()} ${l.message}`)
.join('\n');
await navigator.clipboard.writeText(text);
}
filteredLogs() {
const q = this.filterText.trim().toLowerCase();
return this.logs.filter((l) => {
const isDebugLine = l.message.includes(': DEBUG :');
// Treat debug-marked lines as their own independent toggle, since Mermaid often routes them through
// console.log/info with a marker rather than a distinct "debug" level.
if (isDebugLine && !this.showDebug) return false;
if (!isDebugLine) {
const levelOk =
l.level === 'info' ? this.showInfo : l.level === 'warn' ? this.showWarn : this.showError;
if (!levelOk) return false;
}
if (!q) return true;
return l.message.toLowerCase().includes(q);
});
}
render() {
const visible = this.filteredLogs();
return html`
<div class="console">
<div class="console-toolbar">
<div class="spacer"></div>
<sl-input
size="small"
placeholder="filter…"
clearable
value=${this.filterText}
@sl-input=${(e: any) => (this.filterText = e.target.value ?? '')}
></sl-input>
<sl-checkbox
size="small"
?checked=${this.showDebug}
@sl-change=${(e: any) => (this.showDebug = e.target.checked)}
>debug</sl-checkbox
>
<sl-checkbox
size="small"
?checked=${this.showInfo}
@sl-change=${(e: any) => (this.showInfo = e.target.checked)}
>info</sl-checkbox
>
<sl-checkbox
size="small"
?checked=${this.showWarn}
@sl-change=${(e: any) => (this.showWarn = e.target.checked)}
>warn</sl-checkbox
>
<sl-checkbox
size="small"
?checked=${this.showError}
@sl-change=${(e: any) => (this.showError = e.target.checked)}
>error</sl-checkbox
>
<sl-button size="small" variant="default" @click=${() => void this.copyVisible()}>
<sl-icon slot="prefix" name="clipboard"></sl-icon>
Copy
</sl-button>
<sl-button size="small" variant="default" @click=${() => this.clear()}>
<sl-icon slot="prefix" name="trash"></sl-icon>
Clear
</sl-button>
</div>
<div class="console-body">
${visible.length === 0
? html`<div class="empty">No logs yet.</div>`
: visible.map((l) => {
const lvl = displayLevel(l);
return html`
<div class="logline">
<div class="logmeta">
<sl-badge variant=${displayVariant(lvl)}>${lvl}</sl-badge>
<span class="path">${formatTs(l.ts)}</span>
</div>
<div>${l.message}</div>
</div>
`;
})}
</div>
</div>
`;
}
}
customElements.define('dev-console-panel', DevConsolePanel);

View File

@@ -0,0 +1,551 @@
import { LitElement, html, nothing } from 'lit';
import '@shoelace-style/shoelace/dist/components/button/button.js';
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import '@shoelace-style/shoelace/dist/components/select/select.js';
import '@shoelace-style/shoelace/dist/components/option/option.js';
import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js';
import '@shoelace-style/shoelace/dist/components/split-panel/split-panel.js';
import './console-panel';
import type { LogEntry, LogLevel } from './console-panel';
type MermaidIife = {
initialize: (config: Record<string, unknown>) => void | Promise<void>;
render: (
id: string,
text: string,
container?: Element
) => Promise<{ svg: string; bindFunctions?: (el: Element) => void }>;
};
declare global {
interface Window {
mermaid?: MermaidIife;
mermaidReady?: Promise<MermaidIife>;
}
}
function stringifyArgs(args: unknown[]) {
// Mermaid's internal logger frequently uses console formatting like:
// console.log('%c...message...', 'color: lightgreen', ...)
// For the log panel we want the human text, not the formatting tokens/styles.
let normalized = [...args];
if (typeof normalized[0] === 'string') {
const fmt = normalized[0];
const cssCount = (fmt.match(/%c/g) ?? []).length;
if (cssCount > 0) {
normalized[0] = fmt.replaceAll('%c', '');
// Drop the corresponding CSS args that follow the format string.
normalized.splice(1, cssCount);
}
}
return normalized
.map((a) => {
if (typeof a === 'string') return a;
if (a instanceof Error) return a.stack ?? a.message;
try {
return JSON.stringify(a);
} catch {
return String(a);
}
})
.join(' ')
.replace(/\s+/g, ' ')
.trim();
}
type MermaidTheme = 'default' | 'dark' | 'forest' | 'neutral' | 'base';
type MermaidLayout = 'dagre' | 'elk';
type MermaidLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
const DEFAULT_THEME: MermaidTheme = 'default';
const DEFAULT_LAYOUT: MermaidLayout = 'dagre';
const DEFAULT_MERMAID_LOG_LEVEL: MermaidLogLevel = 'warn';
function readUrlParam(name: string) {
try {
return new URL(window.location.href).searchParams.get(name);
} catch {
return null;
}
}
function setUrlParams(pairs: Record<string, string | null | undefined>) {
const url = new URL(window.location.href);
for (const [k, v] of Object.entries(pairs)) {
if (!v) url.searchParams.delete(k);
else url.searchParams.set(k, v);
}
history.replaceState(null, '', url);
}
function readStorage(key: string) {
try {
return localStorage.getItem(key);
} catch {
return null;
}
}
function writeStorage(key: string, value: string) {
try {
localStorage.setItem(key, value);
} catch {
// ignore
}
}
function isTheme(v: unknown): v is MermaidTheme {
return v === 'default' || v === 'dark' || v === 'forest' || v === 'neutral' || v === 'base';
}
function isLayout(v: unknown): v is MermaidLayout {
return v === 'dagre' || v === 'elk';
}
function isMermaidLogLevel(v: unknown): v is MermaidLogLevel {
return (
v === 'trace' || v === 'debug' || v === 'info' || v === 'warn' || v === 'error' || v === 'fatal'
);
}
function normalizeLayout(v: unknown): MermaidLayout | null {
// Back-compat:
// - older UI used `renderer=dagre-d3|dagre-wrapper|elk`
// - new UI uses `layout=dagre|elk`
if (v === 'dagre' || v === 'elk') return v;
if (v === 'dagre-d3' || v === 'dagre-wrapper') return 'dagre';
return null;
}
function parseBoolean(v: unknown): boolean | null {
if (typeof v !== 'string') return null;
const s = v.trim().toLowerCase();
if (['1', 'true', 'yes', 'on'].includes(s)) return true;
if (['0', 'false', 'no', 'off'].includes(s)) return false;
return null;
}
export class DevDiagramViewer extends LitElement {
static properties = {
filePath: { type: String },
sseToken: { type: Number },
theme: { state: true },
layout: { state: true },
mermaidLogLevel: { state: true },
useMaxWidth: { state: true },
loading: { state: true },
error: { state: true },
source: { state: true },
svg: { state: true },
};
declare filePath: string;
declare sseToken: number;
declare theme: MermaidTheme;
declare layout: MermaidLayout;
declare mermaidLogLevel: MermaidLogLevel;
declare useMaxWidth: boolean;
declare loading: boolean;
declare error: string;
declare source: string;
declare svg: string;
#renderSeq = 0;
#consolePatched = false;
#originalConsole?: {
log: typeof console.log;
info: typeof console.info;
debug: typeof console.debug;
warn: typeof console.warn;
error: typeof console.error;
};
constructor() {
super();
const themeParam = readUrlParam('theme');
const layoutParam = readUrlParam('layout');
const rendererParam = readUrlParam('renderer'); // legacy
const logParam = readUrlParam('logLevel');
const useMaxWidthParam = readUrlParam('useMaxWidth');
const storedTheme = readStorage('devExplorer.viewer.theme');
const storedLayout = readStorage('devExplorer.viewer.layout');
const storedRenderer = readStorage('devExplorer.viewer.renderer'); // legacy
const storedLog = readStorage('devExplorer.viewer.logLevel');
const storedUseMaxWidth = readStorage('devExplorer.viewer.useMaxWidth');
this.theme = isTheme(themeParam)
? themeParam
: isTheme(storedTheme)
? storedTheme
: DEFAULT_THEME;
this.layout =
normalizeLayout(layoutParam) ??
normalizeLayout(rendererParam) ??
normalizeLayout(storedLayout) ??
normalizeLayout(storedRenderer) ??
DEFAULT_LAYOUT;
this.mermaidLogLevel = isMermaidLogLevel(logParam)
? logParam
: isMermaidLogLevel(storedLog)
? storedLog
: DEFAULT_MERMAID_LOG_LEVEL;
this.useMaxWidth = parseBoolean(useMaxWidthParam) ?? parseBoolean(storedUseMaxWidth) ?? true;
this.filePath = '';
this.sseToken = 0;
this.loading = true;
this.error = '';
this.source = '';
this.svg = '';
}
createRenderRoot() {
return this;
}
connectedCallback() {
super.connectedCallback();
this.#installConsoleCapture();
}
disconnectedCallback() {
super.disconnectedCallback();
this.#restoreConsoleCapture();
}
updated(changed: Map<string, unknown>) {
if (changed.has('filePath')) {
void this.#loadAndRender();
} else if (changed.has('sseToken')) {
// On rebuild events, re-fetch + re-render the currently open diagram.
if (this.filePath) void this.#loadAndRender();
} else if (
changed.has('theme') ||
changed.has('layout') ||
changed.has('mermaidLogLevel') ||
changed.has('useMaxWidth')
) {
// Re-render the currently loaded diagram with the new config without refetching.
if (this.source) void this.#renderCurrentSource();
}
}
#back() {
this.dispatchEvent(new CustomEvent('back', { bubbles: true, composed: true }));
}
#persistSettings() {
writeStorage('devExplorer.viewer.theme', this.theme);
writeStorage('devExplorer.viewer.layout', this.layout);
writeStorage('devExplorer.viewer.logLevel', this.mermaidLogLevel);
writeStorage('devExplorer.viewer.useMaxWidth', String(this.useMaxWidth));
setUrlParams({
theme: this.theme,
layout: this.layout,
renderer: null, // drop legacy param
logLevel: this.mermaidLogLevel,
useMaxWidth: this.useMaxWidth ? '1' : '0',
});
}
#syncConsolePanelFilters() {
const panel = this.querySelector('dev-console-panel') as any;
if (!panel) return;
// This is intentionally opinionated: less noise by default as logLevel increases.
if (
this.mermaidLogLevel === 'trace' ||
this.mermaidLogLevel === 'debug' ||
this.mermaidLogLevel === 'info'
) {
panel.showInfo = true;
panel.showWarn = true;
panel.showError = true;
return;
}
if (this.mermaidLogLevel === 'warn') {
panel.showInfo = false;
panel.showWarn = true;
panel.showError = true;
return;
}
// error / fatal
panel.showInfo = false;
panel.showWarn = false;
panel.showError = true;
}
#appendLog(entry: LogEntry) {
const panel = this.querySelector('dev-console-panel') as any;
panel?.append?.(entry);
}
#installConsoleCapture() {
if (this.#consolePatched) return;
this.#consolePatched = true;
this.#originalConsole = {
log: console.log,
info: console.info,
debug: console.debug,
warn: console.warn,
error: console.error,
};
const capture = (level: LogLevel, args: unknown[]) => {
this.#appendLog({
ts: Date.now(),
level,
message: stringifyArgs(args),
});
};
// Mermaid uses its own logger which routes to console.info/debug/warn/error.
// Capture those too (map debug/info/log -> panel "info").
console.log = (...args) => {
capture('info', args);
this.#originalConsole!.log.apply(console, args as any);
};
console.info = (...args) => {
capture('info', args);
this.#originalConsole!.info.apply(console, args as any);
};
console.debug = (...args) => {
capture('info', args);
this.#originalConsole!.debug.apply(console, args as any);
};
console.warn = (...args) => {
capture('warn', args);
this.#originalConsole!.warn.apply(console, args as any);
};
console.error = (...args) => {
capture('error', args);
this.#originalConsole!.error.apply(console, args as any);
};
}
#restoreConsoleCapture() {
if (!this.#consolePatched) return;
this.#consolePatched = false;
if (!this.#originalConsole) return;
console.log = this.#originalConsole.log;
console.info = this.#originalConsole.info;
console.debug = this.#originalConsole.debug;
console.warn = this.#originalConsole.warn;
console.error = this.#originalConsole.error;
this.#originalConsole = undefined;
}
#clearLogs() {
const panel = this.querySelector('dev-console-panel') as any;
panel?.clear?.();
}
async #fetchSource() {
const url = new URL('/dev/api/file', window.location.origin);
url.searchParams.set('path', this.filePath);
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.text();
}
async #loadAndRender() {
const seq = ++this.#renderSeq;
this.loading = true;
this.error = '';
this.svg = '';
this.#clearLogs();
this.#syncConsolePanelFilters();
try {
const source = await this.#fetchSource();
if (seq !== this.#renderSeq) return;
this.source = source;
await this.#renderMermaid(source);
} catch (e) {
this.error = e instanceof Error ? e.message : String(e);
} finally {
this.loading = false;
}
}
async #renderCurrentSource() {
const seq = ++this.#renderSeq;
this.loading = true;
this.error = '';
this.svg = '';
this.#clearLogs();
this.#syncConsolePanelFilters();
try {
const source = this.source;
if (!source) return;
if (seq !== this.#renderSeq) return;
await this.#renderMermaid(source);
} catch (e) {
this.error = e instanceof Error ? e.message : String(e);
} finally {
this.loading = false;
}
}
async #renderMermaid(text: string) {
const m = (await window.mermaidReady?.catch(() => undefined)) ?? window.mermaid;
if (!m) {
throw new Error(
'window.mermaid is not available (did /mermaid.esm.mjs load and did the bootstrap set window.mermaid?)'
);
}
const initConfig = {
startOnLoad: false,
securityLevel: 'strict',
theme: this.theme,
layout: this.layout,
logLevel: this.mermaidLogLevel,
flowchart: {
useMaxWidth: this.useMaxWidth,
},
};
// Debugging aid: log exactly what we are about to initialize/render with.
// Do it *before* initialize so detector issues can be correlated.
const previewLimit = 4000;
const preview =
text.length > previewLimit
? `${text.slice(0, previewLimit)}\n… (${text.length - previewLimit} more chars)`
: text;
console.log('[dev-explorer] mermaid.initialize config:', initConfig);
console.log('[dev-explorer] diagram source preview:\n' + preview);
// Keep it deterministic-ish between reloads.
await m.initialize(initConfig);
const id = `dev-explorer-${Date.now()}-${Math.random().toString(16).slice(2)}`;
const { svg, bindFunctions } = await m.render(id, text);
this.svg = svg;
// Allow mermaid to attach event handlers (e.g. links).
await this.updateComplete;
// If the page ever ended up scrolled down due to a previous oversized render, snap back to top.
// (We intentionally removed vertical scrollbars in the viewer.)
try {
window.scrollTo(0, 0);
} catch {
// ignore
}
const container = this.querySelector('.diagram-inner');
if (container && bindFunctions) bindFunctions(container);
}
render() {
return html`
<div class="header">
<sl-button size="small" variant="default" @click=${() => this.#back()}>
<sl-icon slot="prefix" name="arrow-left"></sl-icon>
Back
</sl-button>
<div style="min-width: 0;">
<div class="title">Diagram</div>
<div class="path">${this.filePath}</div>
</div>
<div class="spacer"></div>
<div class="viewer-controls">
<div class="control">
<span class="label">Theme</span>
<sl-select
size="small"
value=${this.theme}
@sl-change=${(e: any) => {
const v = e.target?.value;
if (isTheme(v)) {
this.theme = v;
this.#persistSettings();
}
}}
>
<sl-option value="default">default</sl-option>
<sl-option value="dark">dark</sl-option>
<sl-option value="forest">forest</sl-option>
<sl-option value="neutral">neutral</sl-option>
<sl-option value="base">base</sl-option>
</sl-select>
</div>
<div class="control">
<span class="label">Layout</span>
<sl-select
size="small"
value=${this.layout}
@sl-change=${(e: any) => {
const v = e.target?.value;
if (isLayout(v)) {
this.layout = v;
this.#persistSettings();
}
}}
>
<sl-option value="dagre">dagre</sl-option>
<sl-option value="elk">elk</sl-option>
</sl-select>
</div>
<div class="control">
<span class="label">Log</span>
<sl-select
size="small"
value=${this.mermaidLogLevel}
@sl-change=${(e: any) => {
const v = e.target?.value;
if (isMermaidLogLevel(v)) {
this.mermaidLogLevel = v;
this.#persistSettings();
this.#syncConsolePanelFilters();
}
}}
>
<sl-option value="trace">trace</sl-option>
<sl-option value="debug">debug</sl-option>
<sl-option value="info">info</sl-option>
<sl-option value="warn">warn</sl-option>
<sl-option value="error">error</sl-option>
<sl-option value="fatal">fatal</sl-option>
</sl-select>
</div>
<div class="control">
<sl-checkbox
size="small"
?checked=${this.useMaxWidth}
@sl-change=${(e: any) => {
this.useMaxWidth = Boolean(e.target?.checked);
this.#persistSettings();
}}
>useMaxWidth</sl-checkbox
>
</div>
</div>
${this.loading ? html`<div class="subtle">rendering…</div>` : nothing}
</div>
${this.error
? html`<div class="empty">Error: <span class="path">${this.error}</span></div>`
: nothing}
<div class="content">
<sl-split-panel position="75" style="height: 100%;">
<div slot="start" class="diagram">
<div class="diagram-inner" data-theme=${this.theme} .innerHTML=${this.svg}></div>
</div>
<div slot="end" style="height: 100%;">
<dev-console-panel></dev-console-panel>
</div>
</sl-split-panel>
</div>
`;
}
}
customElements.define('dev-diagram-viewer', DevDiagramViewer);

View File

@@ -0,0 +1,143 @@
import { LitElement, html, nothing } from 'lit';
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.js';
import { registerIconLibrary } from '@shoelace-style/shoelace/dist/utilities/icon-library.js';
import '@shoelace-style/shoelace/dist/components/breadcrumb/breadcrumb.js';
import '@shoelace-style/shoelace/dist/components/breadcrumb-item/breadcrumb-item.js';
import '@shoelace-style/shoelace/dist/components/button/button.js';
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import '@shoelace-style/shoelace/dist/components/split-panel/split-panel.js';
import '@shoelace-style/shoelace/dist/components/badge/badge.js';
import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js';
import './file-explorer';
import './diagram-viewer';
type ViewMode = 'explorer' | 'viewer';
function getInitialStateFromUrl() {
const url = new URL(window.location.href);
const dir = url.searchParams.get('path') ?? '';
const file = url.searchParams.get('file') ?? '';
return { dir, file };
}
function setUrlState({ dir, file }: { dir: string; file: string }) {
const url = new URL(window.location.href);
url.searchParams.delete('path');
url.searchParams.delete('file');
if (dir) url.searchParams.set('path', dir);
if (file) url.searchParams.set('file', file);
history.replaceState(null, '', url);
}
export class DevExplorerApp extends LitElement {
static properties = {
mode: { state: true },
dirPath: { state: true },
lastDirPath: { state: true },
filePath: { state: true },
sseToken: { state: true },
};
declare mode: ViewMode;
declare dirPath: string;
declare lastDirPath: string;
declare filePath: string;
declare sseToken: number;
#events?: EventSource;
constructor() {
super();
const { dir, file } = getInitialStateFromUrl();
this.dirPath = dir;
this.lastDirPath = dir;
this.filePath = file;
this.mode = file ? 'viewer' : 'explorer';
this.sseToken = 0;
setBasePath('/dev/vendor/shoelace');
registerIconLibrary('default', {
resolver: (name) => `/dev/vendor/shoelace/assets/icons/${name}.svg`,
});
}
createRenderRoot() {
// Use light DOM so document-level CSS (public/styles.css => /dev/styles.css) applies to the UI.
// Without this, Lit's default shadow root will block global selectors like `.list` / `button.card`.
return this;
}
connectedCallback() {
super.connectedCallback();
this.#events = new EventSource('/events');
this.#events.onmessage = () => {
this.sseToken++;
};
window.addEventListener('popstate', this.#onPopState);
}
disconnectedCallback() {
super.disconnectedCallback();
this.#events?.close();
window.removeEventListener('popstate', this.#onPopState);
}
#onPopState = () => {
const { dir, file } = getInitialStateFromUrl();
this.dirPath = dir;
this.lastDirPath = dir;
this.filePath = file;
this.mode = file ? 'viewer' : 'explorer';
};
#goToDir = (dir: string) => {
this.dirPath = dir;
this.lastDirPath = dir;
this.mode = 'explorer';
this.filePath = '';
setUrlState({ dir, file: '' });
};
#openFile = (filePath: string) => {
this.filePath = filePath;
this.mode = 'viewer';
setUrlState({ dir: this.lastDirPath, file: filePath });
};
#backToExplorer = () => {
this.mode = 'explorer';
this.filePath = '';
setUrlState({ dir: this.lastDirPath, file: '' });
};
render() {
return html`
<div class="app">
${this.mode === 'explorer'
? html`
<dev-file-explorer
.path=${this.dirPath}
.sseToken=${this.sseToken}
@navigate=${(e: CustomEvent<{ path: string }>) => this.#goToDir(e.detail.path)}
@open-file=${(e: CustomEvent<{ path: string }>) => this.#openFile(e.detail.path)}
></dev-file-explorer>
`
: nothing}
${this.mode === 'viewer'
? html`
<dev-diagram-viewer
.filePath=${this.filePath}
.sseToken=${this.sseToken}
@back=${this.#backToExplorer}
></dev-diagram-viewer>
`
: nothing}
</div>
`;
}
}
customElements.define('dev-explorer-app', DevExplorerApp);

View File

@@ -0,0 +1,182 @@
import { LitElement, html, nothing } from 'lit';
type Entry = {
name: string;
kind: 'dir' | 'file';
path: string;
};
type FilesResponse = {
root: string;
path: string;
entries: Entry[];
};
function dirname(posixPath: string) {
const p = posixPath.replaceAll('\\', '/').replace(/\/+$/, '');
if (!p) return '';
const idx = p.lastIndexOf('/');
if (idx <= 0) return '';
return p.slice(0, idx);
}
function pathSegments(posixPath: string) {
const p = posixPath.replaceAll('\\', '/').replace(/^\/+/, '').replace(/\/+$/, '');
if (!p) return [];
return p.split('/').filter(Boolean);
}
export class DevFileExplorer extends LitElement {
static properties = {
path: { type: String },
sseToken: { type: Number },
loading: { state: true },
error: { state: true },
root: { state: true },
entries: { state: true },
};
declare path: string;
declare sseToken: number;
declare loading: boolean;
declare error: string;
declare root: string;
declare entries: Entry[];
constructor() {
super();
this.path = '';
this.sseToken = 0;
this.loading = true;
this.error = '';
this.root = '';
this.entries = [];
}
createRenderRoot() {
// Use light DOM so global CSS in public/styles.css applies.
return this;
}
updated(changed: Map<string, unknown>) {
if (changed.has('path') || changed.has('sseToken')) {
void this.#load();
}
}
async #load() {
this.loading = true;
this.error = '';
try {
const url = new URL('/dev/api/files', window.location.origin);
if (this.path) url.searchParams.set('path', this.path);
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = (await res.json()) as FilesResponse;
this.root = json.root ?? '';
this.entries = json.entries ?? [];
} catch (e) {
this.error = e instanceof Error ? e.message : String(e);
this.entries = [];
} finally {
this.loading = false;
}
}
#emitNavigate(nextPath: string) {
this.dispatchEvent(
new CustomEvent('navigate', { detail: { path: nextPath }, bubbles: true, composed: true })
);
}
#emitOpenFile(filePath: string) {
this.dispatchEvent(
new CustomEvent('open-file', { detail: { path: filePath }, bubbles: true, composed: true })
);
}
#onActivate(kind: Entry['kind'], entryPath: string) {
if (kind === 'dir') {
this.#emitNavigate(entryPath);
} else {
this.#emitOpenFile(entryPath);
}
}
render() {
const segments = pathSegments(this.path);
const itemLabel = this.entries.length === 1 ? 'item' : 'items';
return html`
<div class="header">
<div style="min-width: 0;">
<div class="title">Dev Explorer</div>
<div class="subtle">
root:
<span class="path">${this.root || 'cypress/platform/dev-diagrams'}</span>
</div>
<div style="margin-top: 6px;">
<sl-breadcrumb>
<sl-breadcrumb-item @click=${() => this.#emitNavigate('')}>root</sl-breadcrumb-item>
${segments.map((seg, idx) => {
const to = segments.slice(0, idx + 1).join('/');
return html`<sl-breadcrumb-item @click=${() => this.#emitNavigate(to)}
>${seg}</sl-breadcrumb-item
>`;
})}
</sl-breadcrumb>
</div>
</div>
<div class="spacer"></div>
<div class="subtle">
${this.loading ? 'loading…' : html`<span>${this.entries.length} ${itemLabel}</span>`}
</div>
<sl-button
size="small"
variant="default"
?disabled=${!this.path}
@click=${() => this.#emitNavigate(dirname(this.path))}
>
<sl-icon slot="prefix" name="arrow-left"></sl-icon>
Up
</sl-button>
</div>
<div class="content">
${this.error
? html`<div class="empty">Error: <span class="path">${this.error}</span></div>`
: nothing}
${!this.error && !this.loading && this.entries.length === 0
? html`<div class="empty">No folders or <span class="path">.mmd</span> files here.</div>`
: nothing}
<div class="list">
${this.entries.map((e) => {
const icon = e.kind === 'dir' ? 'folder-fill' : 'file-earmark-code';
const cardClass = e.kind === 'dir' ? 'card card-folder' : 'card card-file';
const click =
e.kind === 'dir'
? () => this.#emitNavigate(e.path)
: () => this.#emitOpenFile(e.path);
const onKeyDown = (ev: KeyboardEvent) => {
if (ev.key === 'Enter' || ev.key === ' ') {
ev.preventDefault();
this.#onActivate(e.kind, e.path);
}
};
return html`
<button class=${cardClass} type="button" @click=${click} @keydown=${onKeyDown}>
<div class="card-inner">
<sl-icon class="card-icon" name=${icon}></sl-icon>
<div class="card-title">${e.name}</div>
</div>
</button>
`;
})}
</div>
</div>
`;
}
}
customElements.define('dev-file-explorer', DevFileExplorer);

View File

@@ -0,0 +1,39 @@
<!doctype html>
<html lang="en" class="sl-theme-dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Mermaid Dev Explorer</title>
<link rel="stylesheet" href="/dev/vendor/shoelace/themes/dark.css" />
<link rel="stylesheet" href="/dev/styles.css?v=6" />
</head>
<body>
<!-- Mermaid ESM + ELK layout loaders (required for `layout: 'elk'`) -->
<script type="module">
// Expose a single async hook so the app can reliably await Mermaid "activation".
// This avoids races where the UI calls initialize/render before layouts/diagrams are ready.
window.mermaidReady = (async () => {
try {
const [{ default: mermaid }, { default: layouts }] = await Promise.all([
import('/mermaid.esm.mjs'),
import('/mermaid-layout-elk.esm.mjs'),
]);
mermaid.registerLayoutLoaders(layouts);
// Keep the rest of the dev explorer simple: expose mermaid on window for the Lit components.
window.mermaid = mermaid;
return mermaid;
} catch (err) {
console.error('[dev-explorer] Failed to initialize mermaid (ESM + elk loaders).', err);
throw err;
}
})();
</script>
<dev-explorer-app></dev-explorer-app>
<script type="module" src="/dev/assets/explorer-app.js?v=6"></script>
</body>
</html>

View File

@@ -0,0 +1,359 @@
/* Dev Explorer tokens. Keep on :root so the page background picks them up too. */
:root {
--app-bg: #0b1020;
--app-fg: #e8eefc;
--panel-bg: #0f1733;
--muted: rgba(232, 238, 252, 0.75);
--border: rgba(232, 238, 252, 0.12);
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
}
html,
body {
height: 100%;
}
body {
margin: 0;
background: var(--app-bg);
color: var(--app-fg);
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
/* Keep the page from becoming scrollable when components accidentally overflow. */
overflow: hidden;
}
a {
color: inherit;
}
/* Scope app-level layout rules to avoid generic class collisions. */
dev-explorer-app {
display: block;
height: 100%;
}
dev-explorer-app .app {
height: 100vh;
display: flex;
flex-direction: column;
}
/* Let the top-level view components actually fill the available vertical space. */
dev-explorer-app dev-file-explorer,
dev-explorer-app dev-diagram-viewer {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
}
dev-explorer-app .header {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
background: var(--panel-bg);
}
dev-explorer-app .viewer-controls {
display: inline-flex;
align-items: center;
gap: 10px;
margin-right: 8px;
}
dev-explorer-app .viewer-controls .control {
display: inline-flex;
align-items: center;
gap: 6px;
}
dev-explorer-app .viewer-controls .label {
font-size: 12px;
color: var(--muted);
user-select: none;
}
dev-explorer-app .viewer-controls sl-select {
width: 140px;
}
dev-explorer-app .header .spacer {
flex: 1;
}
dev-explorer-app .title {
font-weight: 600;
letter-spacing: 0.2px;
}
dev-explorer-app .subtle {
color: var(--muted);
font-size: 12px;
}
dev-explorer-app .content {
flex: 1;
min-height: 0;
overflow: auto;
}
/* Diagram view should behave like a full-height canvas; avoid nested scrollbars. */
dev-explorer-app dev-diagram-viewer .content {
overflow: hidden;
}
/* Shoelace split panel: ensure slot content can't overflow and push itself off-screen. */
dev-explorer-app sl-split-panel {
height: 100%;
overflow: hidden;
}
dev-explorer-app sl-split-panel::part(base) {
height: 100%;
overflow: hidden;
}
dev-explorer-app sl-split-panel::part(start),
dev-explorer-app sl-split-panel::part(end) {
overflow: hidden;
min-height: 0;
}
dev-explorer-app .list {
padding: 24px;
display: grid;
gap: 20px;
grid-template-columns: repeat(auto-fill, 260px);
}
/* Card base - use button element */
dev-explorer-app button.card {
all: unset;
box-sizing: border-box;
width: 260px;
height: 200px;
border-radius: 20px;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
/* Folder card style */
dev-explorer-app button.card.card-folder {
background: linear-gradient(160deg, #1a3052 0%, #0d1a2d 100%);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
0 2px 8px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
dev-explorer-app button.card.card-folder:hover {
transform: translateY(-6px) scale(1.02);
box-shadow:
0 16px 48px rgba(0, 0, 0, 0.5),
0 4px 12px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
/* File card style */
dev-explorer-app button.card.card-file {
background: linear-gradient(160deg, #1e2d4a 0%, #111827 100%);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
0 2px 8px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
dev-explorer-app button.card.card-file:hover {
transform: translateY(-6px) scale(1.02);
box-shadow:
0 16px 48px rgba(0, 0, 0, 0.5),
0 4px 12px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
dev-explorer-app button.card:focus-visible {
outline: 3px solid rgba(96, 165, 250, 0.7);
outline-offset: 4px;
}
/* Subtle border overlay */
dev-explorer-app button.card::before {
content: '';
position: absolute;
inset: 0;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.08);
pointer-events: none;
}
dev-explorer-app .card-inner {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
padding: 24px;
width: 100%;
height: 100%;
box-sizing: border-box;
}
/* Icon styling */
dev-explorer-app .card-icon {
font-size: 56px !important;
width: 56px;
height: 56px;
}
dev-explorer-app button.card.card-folder .card-icon {
color: #f59e0b;
filter: drop-shadow(0 4px 12px rgba(245, 158, 11, 0.4));
}
dev-explorer-app button.card.card-file .card-icon {
color: #3b82f6;
filter: drop-shadow(0 4px 12px rgba(59, 130, 246, 0.35));
}
/* Title styling */
dev-explorer-app .card-title {
font-size: 12px;
font-family: var(--mono);
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 230px;
color: rgba(255, 255, 255, 0.85);
line-height: 1.4;
text-align: center;
}
dev-explorer-app .row {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 8px;
border: 1px solid var(--border);
border-radius: 8px;
background: rgba(255, 255, 255, 0.02);
}
dev-explorer-app .row:hover {
background: rgba(255, 255, 255, 0.04);
}
dev-explorer-app .row sl-icon {
font-size: 16px;
}
dev-explorer-app .path {
font-family: var(--mono);
font-size: 12px;
color: var(--muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
dev-explorer-app .diagram {
height: 100%;
overflow: hidden;
padding: 12px;
box-sizing: border-box;
}
dev-explorer-app .diagram-inner {
/* Canvas background behind the rendered SVG (theme-dependent). */
background: #fff;
color: #111;
border-radius: 10px;
padding: 12px;
display: flex;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
height: 100%;
box-sizing: border-box;
}
/* Fit the rendered SVG within the available pane (no scrollbars). */
dev-explorer-app .diagram-inner > svg {
width: auto;
height: auto;
max-width: 100% !important;
max-height: 100% !important;
}
dev-explorer-app .diagram-inner[data-theme='dark'] {
background: #0b1020;
color: #e8eefc;
}
dev-explorer-app .console {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
border-left: 1px solid var(--border);
background: var(--panel-bg);
}
dev-explorer-app .spacer {
flex: 1;
}
dev-explorer-app .console-toolbar {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
}
dev-explorer-app .console-toolbar sl-input {
width: 260px;
}
dev-explorer-app .console-body {
flex: 1;
min-height: 0;
overflow: auto;
padding: 10px 12px;
}
dev-explorer-app .logline {
font-family: var(--mono);
font-size: 12px;
white-space: pre-wrap;
word-break: break-word;
margin: 0 0 8px 0;
line-height: 1.35;
}
dev-explorer-app .logmeta {
display: inline-flex;
gap: 8px;
align-items: center;
margin-bottom: 4px;
}
dev-explorer-app .empty {
padding: 18px 12px;
color: var(--muted);
font-size: 13px;
}
dev-explorer-app sl-breadcrumb::part(base) {
font-size: 12px;
}

View File

@@ -2,12 +2,17 @@
import chokidar from 'chokidar';
import cors from 'cors';
import { context } from 'esbuild';
import { promises as fs } from 'fs';
import type { Request, Response } from 'express';
import express from 'express';
import path, { resolve } from 'path';
import { fileURLToPath } from 'url';
import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.js';
import { defaultOptions, getBuildConfig } from './util.js';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
const configs = Object.values(packageOptions).map(({ packageName }) =>
getBuildConfig({
...defaultOptions,
@@ -18,7 +23,7 @@ const configs = Object.values(packageOptions).map(({ packageName }) =>
);
const mermaidIIFEConfig = getBuildConfig({
...defaultOptions,
minify: false,
minify: true,
core: false,
options: packageOptions.mermaid,
format: 'iife',
@@ -84,6 +89,81 @@ function sendEventsToAll() {
clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`));
}
interface DevExplorerEntry {
name: string;
kind: 'dir' | 'file';
path: string; // posix-style, relative to root
}
const devExplorerRootAbs = resolve(
process.cwd(),
process.env.MERMAID_DEV_EXPLORER_ROOT ?? 'cypress/platform/dev-diagrams'
);
function toPosixPath(p: string) {
return p.split(path.sep).join('/');
}
function resolveWithinDevExplorerRoot(requestedPath: unknown) {
const requested = typeof requestedPath === 'string' ? requestedPath : '';
if (requested.includes('\0')) {
throw new Error('Invalid path');
}
// Normalize slashes and avoid weird absolute-path cases.
const cleaned = requested.replaceAll('\\', '/').replace(/^\/+/, '');
const absPath = resolve(devExplorerRootAbs, cleaned);
const rel = path.relative(devExplorerRootAbs, absPath);
// Prevent traversal above root.
if (rel.startsWith('..') || path.isAbsolute(rel)) {
throw new Error('Path escapes configured root');
}
return { absPath, relPath: toPosixPath(rel) };
}
async function createDevExplorerBundle() {
const devExplorerDir = resolve(__dirname, 'dev-explorer');
const entryPoint = resolve(devExplorerDir, 'explorer-app.ts');
const outDir = resolve(devExplorerDir, 'dist');
try {
const devExplorerCtx = await context({
absWorkingDir: process.cwd(),
entryPoints: [entryPoint],
bundle: true,
format: 'esm',
target: 'es2020',
sourcemap: true,
outdir: outDir,
logLevel: 'info',
plugins: [
{
name: 'dev-explorer-reload',
setup(build) {
build.onEnd(() => {
sendEventsToAll();
});
},
},
],
});
await devExplorerCtx.watch();
await devExplorerCtx.rebuild();
} catch (err) {
console.error(
[
'Dev Explorer bundle build failed.',
'Install dependencies: pnpm add -D lit @shoelace-style/shoelace',
'Then restart the dev server.',
].join('\n')
);
console.error(err);
}
}
async function createServer() {
await generateLangium();
handleFileChange();
@@ -106,8 +186,117 @@ async function createServer() {
handleFileChange();
});
// Rebuild the dev-explorer client bundle on changes (and emit SSE so the browser reloads).
await createDevExplorerBundle();
// Emit SSE when Dev Explorer static assets change (e.g. public/styles.css),
// otherwise CSS-only changes can look "ignored" unless the user manually refreshes.
chokidar
.watch(['.esbuild/dev-explorer/public/**/*'], {
ignoreInitial: true,
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
})
.on('all', (event, changedPath) => {
if (!['add', 'change', 'unlink'].includes(event)) {
return;
}
console.log(`[dev-explorer] ${event}: ${changedPath}`);
sendEventsToAll();
});
// Emit SSE when .mmd files inside the configured explorer root change.
chokidar
.watch('**/*.mmd', {
cwd: devExplorerRootAbs,
ignoreInitial: true,
})
.on('all', (event, changedPath) => {
if (!['add', 'change', 'unlink'].includes(event)) {
return;
}
console.log(`[dev-explorer] ${event}: ${changedPath}`);
sendEventsToAll();
});
app.use(cors());
app.get('/events', eventsHandler);
// --- Dev Explorer API + UI -------------------------------------------------
const devExplorerDir = resolve(__dirname, 'dev-explorer');
const devExplorerPublicDir = resolve(devExplorerDir, 'public');
const devExplorerDistDir = resolve(devExplorerDir, 'dist');
// Shoelace assets (theme css + icons). Safe: only mounted in dev server.
app.use(
'/dev/vendor/shoelace',
express.static(resolve(process.cwd(), 'node_modules/@shoelace-style/shoelace/dist'))
);
app.get('/dev/api/files', async (req, res) => {
try {
const { absPath, relPath } = resolveWithinDevExplorerRoot(req.query.path);
const stats = await fs.stat(absPath);
if (!stats.isDirectory()) {
res.status(400).json({ error: 'Not a directory' });
return;
}
const dirEntries = await fs.readdir(absPath, { withFileTypes: true });
const entries = dirEntries
.filter((d) => d.isDirectory() || (d.isFile() && d.name.endsWith('.mmd')))
.map<DevExplorerEntry>((d) => ({
name: d.name,
kind: d.isDirectory() ? 'dir' : 'file',
path: toPosixPath(path.join(relPath, d.name)),
}))
.sort((a, b) => {
if (a.kind !== b.kind) {
return a.kind === 'dir' ? -1 : 1;
}
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
});
res.json({
root: toPosixPath(path.relative(process.cwd(), devExplorerRootAbs)),
path: relPath === '' ? '' : relPath,
entries,
});
} catch (_e) {
res.status(400).json({ error: 'Invalid path' });
}
});
app.get('/dev/api/file', async (req, res) => {
try {
const { absPath, relPath } = resolveWithinDevExplorerRoot(req.query.path);
if (!absPath.endsWith('.mmd')) {
res.status(400).send('Only .mmd files are allowed');
return;
}
const stats = await fs.stat(absPath);
if (!stats.isFile()) {
res.status(400).send('Not a file');
return;
}
const content = await fs.readFile(absPath, 'utf-8');
res.type('text/plain').send(content);
// Optional: include relPath for debugging.
void relPath;
} catch (_e) {
res.status(400).send('Invalid path');
}
});
// Static assets for the dev-explorer UI.
app.use('/dev/assets', express.static(devExplorerDistDir));
// Serve `/dev/` (and `/dev`) from public/, including index.html.
app.use(
'/dev',
express.static(devExplorerPublicDir, {
index: ['index.html'],
})
);
for (const { packageName } of Object.values(packageOptions)) {
app.use(express.static(`./packages/${packageName}/dist`));
}

View File

@@ -71,6 +71,9 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const outFileName = getFileName(name, options);
const { dependencies, version } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
const output: BuildOptions = buildOptions({
...rest,
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
@@ -82,15 +85,13 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
define: {
// This needs to be stringified for esbuild
includeLargeFeatures: `${includeLargeFeatures}`,
'injected.includeLargeFeatures': `${includeLargeFeatures}`,
'injected.version': `'${version}'`,
'import.meta.vitest': 'undefined',
},
});
if (core) {
const { dependencies } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
// Ignore dependencies and any dependencies of dependencies

View File

@@ -26,8 +26,8 @@ jobs:
strategy:
fail-fast: false
matrix:
language: ['javascript']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
language: ['javascript', 'actions']
# CodeQL supports [ 'actions', 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
@@ -36,7 +36,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
uses: github/codeql-action/init@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
with:
config-file: ./.github/codeql/codeql-config.yml
languages: ${{ matrix.language }}
@@ -48,7 +48,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
uses: github/codeql-action/autobuild@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -62,4 +62,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
uses: github/codeql-action/analyze@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21

View File

@@ -53,7 +53,7 @@ jobs:
args: -X POST "$APPLITOOLS_SERVER_URL/api/externals/github/push?apiKey=$APPLITOOLS_API_KEY&CommitSha=$GITHUB_SHA&BranchName=${APPLITOOLS_BRANCH}$&ParentBranchName=$APPLITOOLS_PARENT_BRANCH"
- name: Cypress run
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
id: cypress
with:
start: pnpm run dev

View File

@@ -27,12 +27,12 @@ jobs:
with:
node-version-file: '.node-version'
- name: Install dependencies
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
with:
runTests: false
- name: Cypress run
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
id: cypress
with:
install: false
@@ -58,7 +58,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT
- name: Commit and create pull request
uses: peter-evans/create-pull-request@915d841dae6a4f191bb78faf61a257411d7be4d2
uses: peter-evans/create-pull-request@0979079bc20c05bbbb590a56c21c4e2b1d1f1bbe
with:
add-paths: |
cypress/timings.json

View File

@@ -45,7 +45,7 @@ jobs:
node-version-file: '.node-version'
- name: Cache snapshots
id: cache-snapshot
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: ./cypress/snapshots
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
@@ -59,7 +59,7 @@ jobs:
- name: Install dependencies
if: ${{ steps.cache-snapshot.outputs.cache-hit != 'true' }}
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
with:
# just perform install
runTests: false
@@ -95,13 +95,13 @@ jobs:
# These cached snapshots are downloaded, providing the reference snapshots.
- name: Cache snapshots
id: cache-snapshot
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: ./cypress/snapshots
key: ${{ runner.os }}-snapshots-${{ env.targetHash }}
- name: Install dependencies
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
with:
runTests: false
@@ -117,7 +117,7 @@ jobs:
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@18a6541367f4580a515371905f499a27a44e8dbe # v6.7.12
uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f # v6.7.16
id: cypress
with:
install: false

View File

@@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Restore lychee cache
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}

View File

@@ -36,11 +36,10 @@ jobs:
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9
uses: changesets/action@06245a4e0a36c064a573d4150030f5ec548e4fcc # v1.4.10
with:
version: pnpm changeset:version
publish: pnpm changeset:publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

View File

@@ -20,18 +20,18 @@ jobs:
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: Upload artifact
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: SARIF file
path: results.sarif
retention-days: 5
- name: Upload to code-scanning
uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10
uses: github/codeql-action/upload-sarif@5378192d256ef1302a6980fffe5ca04426d43091 # v3.28.21
with:
sarif_file: results.sarif

View File

@@ -19,7 +19,7 @@ jobs:
message: 'chore: update browsers list'
push: false
- name: Create Pull Request
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
with:
branch: update-browserslist
title: Update Browserslist

View File

@@ -1,7 +1,7 @@
name: Validate pnpm-lock.yaml
on:
pull_request:
pull_request_target:
paths:
- 'pnpm-lock.yaml'
- '**/package.json'
@@ -15,13 +15,8 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Validate pnpm-lock.yaml entries
id: validate # give this step an ID so we can reference its outputs
@@ -55,16 +50,41 @@ jobs:
exit 1
fi
- name: Find existing lockfile validation comment
if: always()
uses: peter-evans/find-comment@v3
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: 'Lockfile Validation Failed'
- name: Comment on PR if validation failed
if: failure()
uses: peter-evans/create-or-update-comment@v4
uses: peter-evans/create-or-update-comment@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.find-comment.outputs.comment-id }}
edit-mode: replace
body: |
❌ **Lockfile Validation Failed**
The following issue(s) were detected:
${{ steps.validate.outputs.errors }}
Please address these and push an update.
_Posted automatically by GitHub Actions_
- name: Delete comment if validation passed
if: success() && steps.find-comment.outputs.comment-id != ''
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: ${{ steps.find-comment.outputs.comment-id }},
});

View File

@@ -78,6 +78,8 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
},
define: {
'import.meta.vitest': 'undefined',
'injected.includeLargeFeatures': 'true',
'injected.version': `'0.0.0'`,
},
resolve: {
extensions: [],
@@ -94,10 +96,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
}),
...visualizerOptions(packageName, core),
],
define: {
// Needs to be string
includeLargeFeatures: 'true',
},
};
if (watch && config.build) {

View File

@@ -5,7 +5,7 @@ USER 0:0
RUN corepack enable \
&& corepack enable pnpm
RUN apk add --no-cache git~=2.43.4 \
RUN apk add --no-cache git~=2.43 \
&& git config --add --system safe.directory /mermaid
ENV NODE_OPTIONS="--max_old_space_size=8192"

View File

@@ -6,6 +6,7 @@ interface CypressConfig {
listUrl?: boolean;
listId?: string;
name?: string;
screenshot?: boolean;
}
type CypressMermaidConfig = MermaidConfig & CypressConfig;
@@ -90,20 +91,33 @@ export const renderGraph = (
export const openURLAndVerifyRendering = (
url: string,
options: CypressMermaidConfig,
{ screenshot = true, ...options }: CypressMermaidConfig,
validation?: any
): void => {
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
if (validation) {
cy.get('svg').should(validation);
// Handle sandbox mode where SVG is inside an iframe
if (options.securityLevel === 'sandbox') {
cy.get('iframe').should('be.visible');
if (validation) {
cy.get('iframe').should(validation);
}
} else {
cy.get('svg').should('be.visible');
// cspell:ignore viewbox
cy.get('svg').should('not.have.attr', 'viewbox');
if (validation) {
cy.get('svg').should(validation);
}
}
verifyScreenshot(name);
if (screenshot) {
verifyScreenshot(name);
}
};
export const verifyScreenshot = (name: string): void => {

View File

@@ -114,4 +114,28 @@ describe('C4 diagram', () => {
{}
);
});
it('C4.6 should render C4Context diagram with ComponentQueue_Ext', () => {
imgSnapshotTest(
`
C4Context
title System Context diagram with ComponentQueue_Ext
Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
Enterprise_Boundary(b1, "BankBoundary") {
ComponentQueue_Ext(msgQueue, "Message Queue", "RabbitMQ", "External message queue system for processing banking transactions")
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
}
}
BiRel(customerA, SystemAA, "Uses")
Rel(SystemAA, msgQueue, "Sends messages to")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
`,
{}
);
});
});

View File

@@ -562,6 +562,20 @@ class C13["With Città foreign language"]
`
);
});
it('should add notes in namespaces', function () {
imgSnapshotTest(
`
classDiagram
note "This is a outer note"
note for C1 "This is a outer note for C1"
namespace Namespace1 {
note "This is a inner note"
note for C1 "This is a inner note for C1"
class C1
}
`
);
});
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`

View File

@@ -709,6 +709,20 @@ class C13["With Città foreign language"]
`
);
});
it('should add notes in namespaces', function () {
imgSnapshotTest(
`
classDiagram
note "This is a outer note"
note for C1 "This is a outer note for C1"
namespace Namespace1 {
note "This is a inner note"
note for C1 "This is a inner note for C1"
class C1
}
`
);
});
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`

View File

@@ -369,4 +369,92 @@ ORDER ||--|{ LINE-ITEM : contains
);
});
});
describe('Special characters and numbers syntax', () => {
it('should render ER diagram with numeric entity names', () => {
imgSnapshotTest(
`
erDiagram
1 ||--|| ORDER : places
ORDER ||--|{ 2 : contains
2 ||--o{ 3.5 : references
`,
{ logLevel: 1 }
);
});
it('should render ER diagram with "u" character in entity names and cardinality', () => {
imgSnapshotTest(
`
erDiagram
CUSTOMER ||--|| u : has
u ||--|| ORDER : places
PROJECT u--o{ TEAM_MEMBER : "parent"
`,
{ logLevel: 1 }
);
});
it('should render ER diagram with decimal numbers in relationships', () => {
imgSnapshotTest(
`
erDiagram
2.5 ||--|| 1.5 : has
CUSTOMER ||--o{ 3.14 : references
1.0 ||--|{ ORDER : contains
`,
{ logLevel: 1 }
);
});
it('should render ER diagram with numeric entity names and attributes', () => {
imgSnapshotTest(
`
erDiagram
1 {
string name
int value
}
1 ||--|| ORDER : places
ORDER {
float price
string description
}
`,
{ logLevel: 1 }
);
});
it('should render complex ER diagram with mixed special entity names', () => {
imgSnapshotTest(
`
erDiagram
CUSTOMER ||--o{ 1 : places
1 ||--|{ u : contains
1.5
u ||--|| 2.5 : processes
2.5 {
string id
float value
}
u {
varchar(50) name
int count
}
`,
{ logLevel: 1 }
);
});
it('should render ER diagram with standalone numeric entities', () => {
imgSnapshotTest(
`erDiagram
PRODUCT ||--o{ ORDER-ITEM : has
1.5
u
1
`,
{ logLevel: 1 }
);
});
});
});

View File

@@ -1029,4 +1029,19 @@ graph TD
}
);
});
it('FDH49: should add edge animation', () => {
renderGraph(
`
flowchart TD
A(["Start"]) L_A_B_0@--> B{"Decision"}
B --> C["Option A"] & D["Option B"]
style C stroke-width:4px,stroke-dasharray: 5
L_A_B_0@{ animation: slow }
L_B_D_0@{ animation: fast }`,
{ look: 'handDrawn', screenshot: false }
);
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
});
});

View File

@@ -79,6 +79,18 @@ describe('Flowchart v2', () => {
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('6a: should render complex HTML in labels with sandbox security', () => {
imgSnapshotTest(
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ securityLevel: 'sandbox', flowchart: { htmlLabels: true } }
);
});
it('7: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`flowchart TD

View File

@@ -774,6 +774,21 @@ describe('Graph', () => {
expect(svg).to.not.have.attr('style');
});
});
it('40: should add edge animation', () => {
renderGraph(
`
flowchart TD
A(["Start"]) L_A_B_0@--> B{"Decision"}
B --> C["Option A"] & D["Option B"]
style C stroke-width:4px,stroke-dasharray: 5
L_A_B_0@{ animation: slow }
L_B_D_0@{ animation: fast }`,
{ screenshot: false }
);
// Verify animation classes are applied to both edges
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
});
it('58: handle styling with style expressions', () => {
imgSnapshotTest(
`
@@ -973,4 +988,19 @@ graph TD
}
);
});
it('70: should render a subgraph with direction TD', () => {
imgSnapshotTest(
`
flowchart LR
subgraph A
direction TD
a --> b
end
`,
{
fontFamily: 'courier',
}
);
});
});

View File

@@ -833,4 +833,34 @@ describe('Gantt diagram', () => {
{}
);
});
it('should handle seconds-only format with tickInterval (issue #5496)', () => {
imgSnapshotTest(
`
gantt
tickInterval 1second
dateFormat ss
axisFormat %s
section Network Request
RTT : rtt, 0, 20
`,
{}
);
});
it('should handle dates with year typo like 202 instead of 2024 (issue #5496)', () => {
imgSnapshotTest(
`
gantt
title Schedule
dateFormat YYYY-MM-DD
tickInterval 1week
axisFormat %m-%d
section Vacation
London : 2024-12-01, 7d
London : 202-12-01, 7d
`,
{}
);
});
});

View File

@@ -247,5 +247,31 @@ root
);
});
});
describe('Level 2 nodes exceeding 11', () => {
it('should render all Level 2 nodes correctly when there are more than 11', () => {
imgSnapshotTest(
`mindmap
root
Node1
Node2
Node3
Node4
Node5
Node6
Node7
Node8
Node9
Node10
Node11
Node12
Node13
Node14
Node15`,
{},
undefined,
shouldHaveRoot
);
});
});
/* The end */
});

View File

@@ -655,5 +655,315 @@ describe('Sequence Diagram Special Cases', () => {
expect(svg).to.not.have.attr('style');
});
});
describe('Central Connection Rendering Tests', () => {
it('should render central connection circles on actor vertical lines', () => {
imgSnapshotTest(
`sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice ()->>() Bob: Central connection
Bob ()-->> Charlie: Reverse central connection
Charlie ()<<-->>() Alice: Dual central connection`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render central connections with different arrow types', () => {
imgSnapshotTest(
`sequenceDiagram
participant Alice
participant Bob
Alice ()->>() Bob: Solid open arrow
Alice ()-->>() Bob: Dotted open arrow
Alice ()-x() Bob: Solid cross
Alice ()--x() Bob: Dotted cross
Alice ()->() Bob: Solid arrow`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render central connections with bidirectional arrows', () => {
imgSnapshotTest(
`sequenceDiagram
participant Alice
participant Bob
Alice ()<<->>() Bob: Bidirectional solid
Alice ()<<-->>() Bob: Bidirectional dotted`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render central connections with activations', () => {
imgSnapshotTest(
`sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice ()->>() Bob: Activate Bob
activate Bob
Bob ()-->> Charlie: Message to Charlie
Bob ()->>() Alice: Response to Alice
deactivate Bob`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render central connections mixed with normal messages', () => {
imgSnapshotTest(
`sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice ->> Bob: Normal message
Bob ()->>() Charlie: Central connection
Charlie -->> Alice: Normal dotted message
Alice ()<<-->>() Bob: Dual central connection
Bob -x Charlie: Normal cross message`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render central connections with notes', () => {
imgSnapshotTest(
`sequenceDiagram
participant Alice
participant Bob
participant Charlie
Alice ()->>() Bob: Central connection
Note over Alice,Bob: Central connection note
Bob ()-->> Charlie: Reverse central connection
Note right of Charlie: Response note
Charlie ()<<-->>() Alice: Dual central connection`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render central connections with loops and alternatives', () => {
imgSnapshotTest(
`sequenceDiagram
participant Alice
participant Bob
participant Charlie
loop Every minute
Alice ()->>() Bob: Central heartbeat
Bob ()-->> Charlie: Forward heartbeat
end
alt Success
Charlie ()<<-->>() Alice: Success response
else Failure
Charlie ()-x() Alice: Failure response
end`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render central connections with different participant types', () => {
imgSnapshotTest(
`sequenceDiagram
participant Alice
actor Bob
participant Charlie@{"type":"boundary"}
participant David@{"type":"control"}
participant Eve@{"type":"entity"}
Alice ()->>() Bob: To actor
Bob ()-->> Charlie: To boundary
Charlie ()->>() David: To control
David ()<<-->>() Eve: To entity
Eve ()-x() Alice: Back to participant`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
describe('Participant Stereotypes with Aliases', () => {
it('should render participants with stereotypes and aliases', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "type" : "boundary" } as Public API
participant Auth@{ "type" : "control" } as Auth Controller
participant DB@{ "type" : "database" } as User Database
participant Cache@{ "type" : "entity" } as Cache Layer
API ->> Auth: Authenticate request
Auth ->> DB: Query user
DB -->> Auth: User data
Auth ->> Cache: Store session
Cache -->> Auth: Confirmed
Auth -->> API: Token`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render actors with stereotypes and aliases', () => {
imgSnapshotTest(
`sequenceDiagram
actor U@{ "type" : "actor" } as End User
actor A@{ "type" : "boundary" } as API Gateway
actor S@{ "type" : "control" } as Service Layer
actor D@{ "type" : "database" } as Data Store
U ->> A: Send request
A ->> S: Process
S ->> D: Persist
D -->> S: Success
S -->> A: Response
A -->> U: Result`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render mixed participants and actors with stereotypes and aliases', () => {
imgSnapshotTest(
`sequenceDiagram
actor Client@{ "type" : "actor" } AS Mobile Client
participant Gateway@{ "type" : "boundary" } as API Gateway
participant OrderSvc@{ "type" : "control" } as Order Service
participant Queue@{ "type" : "queue" } as Message Queue
participant DB@{ "type" : "database" } as Order Database
participant Logs@{ "type" : "collections" } as Audit Logs
Client ->> Gateway: Place order
Gateway ->> OrderSvc: Validate order
OrderSvc ->> Queue: Queue for processing as well
OrderSvc ->> DB: Save order
OrderSvc ->> Logs: Log transaction
Queue -->> OrderSvc: Processing started AS Well
DB -->> OrderSvc: Order saved
Logs -->> OrderSvc: Logged
OrderSvc -->> Gateway: Order confirmed
Gateway -->> Client: Confirmation`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render stereotypes with aliases in boxes', () => {
imgSnapshotTest(
`sequenceDiagram
box rgb(200,220,255) Frontend Layer
actor User@{ "type" : "actor" } as End User
participant UI@{ "type" : "boundary" } as User Interface
end
box rgb(255,220,200) Backend Layer
participant API@{ "type" : "boundary" } as REST API
participant Svc@{ "type" : "control" } as Business Logic
end
box rgb(220,255,200) Data Layer
participant DB@{ "type" : "database" } as Primary DB
participant Cache@{ "type" : "entity" } as Cache Store
end
User ->> UI: Click button
UI ->> API: HTTP request
API ->> Svc: Process
Svc ->> Cache: Check cache
Cache -->> Svc: Cache miss
Svc ->> DB: Query data
DB -->> Svc: Data
Svc ->> Cache: Update cache
Svc -->> API: Response
API -->> UI: Data
UI -->> User: Display`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render stereotypes with aliases and complex interactions', () => {
imgSnapshotTest(
`sequenceDiagram
participant Web@{ "type" : "boundary" } as Web Portal
participant Auth@{ "type" : "control" } as Auth Service
participant UserDB@{ "type" : "database" } as User DB
participant Queue@{ "type" : "queue" } as Event Queue
participant Audit@{ "type" : "collections" } as Audit Trail
Web ->> Auth: Login request
activate Auth
Auth ->> UserDB: Verify credentials
activate UserDB
UserDB -->> Auth: User found
deactivate UserDB
alt Valid credentials
Auth ->> Queue: Publish login event
Auth ->> Audit: Log success
par Parallel processing
Queue -->> Auth: Event queued
and
Audit -->> Auth: Logged
end
Auth -->> Web: Success token
else Invalid credentials
Auth ->> Audit: Log failure
Audit -->> Auth: Logged
Auth --x Web: Access denied
end
deactivate Auth
Note over Web,Audit: All interactions logged`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
describe('Participant Inline Alias in Config', () => {
it('should render participants with inline alias in config object', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "type" : "boundary", "alias": "Public API" }
participant Auth@{ "type" : "control", "alias": "Auth Service" }
participant DB@{ "type" : "database", "alias": "User DB" }
API ->> Auth: Login request
Auth ->> DB: Query user
DB -->> Auth: User data
Auth -->> API: Token`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render actors with inline alias in config object', () => {
imgSnapshotTest(
`sequenceDiagram
actor U@{ "type" : "actor", "alias": "End User" }
actor G@{ "type" : "boundary", "alias": "Gateway" }
actor S@{ "type" : "control", "alias": "Service" }
U ->> G: Request
G ->> S: Process
S -->> G: Response
G -->> U: Result`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should handle mixed inline and external alias syntax', () => {
imgSnapshotTest(
`sequenceDiagram
participant A@{ "type" : "boundary", "alias": "Service A" }
participant B@{ "type" : "control" } as Service B
participant C@{ "type" : "database" }
A ->> B: Request
B ->> C: Query
C -->> B: Data
B -->> A: Response`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should prioritize external alias over inline alias', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "type" : "boundary", "alias": "Internal Name" } as External Name
participant DB@{ "type" : "database", "alias": "Internal DB" } AS External DB
API ->> DB: Query
DB -->> API: Result`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render inline alias with only alias field (no type)', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "alias": "Public API" }
participant Auth@{ "alias": "Auth Service" }
API ->> Auth: Request
Auth -->> API: Response`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
});
});

View File

@@ -1053,4 +1053,167 @@ describe('Sequence diagram', () => {
]);
});
});
describe('render new arrow type', () => {
it('should render Solid half arrow top', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice -|\\ John: Hello John, how are you?
Alice-|\\ John: Hi Alice, I can hear you!
Alice -|\\ John: Test
`
);
});
it('should render Solid half arrow bottom', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice-|/John: Hello John, how are you?
Alice-|/John: Hi Alice, I can hear you!
Alice-|/John: Test
`
);
});
it('should render Stick half arrow top ', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice-\\\\John: Hello John, how are you?
Alice-\\\\John: Hi Alice, I can hear you!
Alice-\\\\John: Test
`
);
});
it('should render Stick half arrow bottom ', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice-//John: Hello John, how are you?
Alice-//John: Hi Alice, I can hear you!
Alice-//John: Test
`
);
});
it('should render Solid half arrow top reverse ', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice/|-John: Hello Alice, how are you?
Alice/|-John: Hi Alice, I can hear you!
Alice/|-John: Test
`
);
});
it('should render Solid half arrow bottom reverse ', () => {
imgSnapshotTest(
`sequenceDiagram
Alice \\|- John: Hello Alice, how are you?
Alice \\|- John: Hi Alice, I can hear you!
Alice \\|- John: Test`
);
});
it('should render Stick half arrow top reverse ', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice //-John: Hello Alice, how are you?
Alice //-John: Hi Alice, I can hear you!
Alice //-John: Test`
);
});
it('should render Stick half arrow bottom reverse ', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice \\\\-John: Hello Alice, how are you?
Alice \\\\-John: Hi Alice, I can hear you!
Alice \\\\-John: Test`
);
});
it('should render Solid half arrow top dotted', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice --|\\John: Hello John, how are you?
Alice --|\\John: Hi Alice, I can hear you!
Alice --|\\John: Test`
);
});
it('should render Solid half arrow bottom dotted', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice --|/John: Hello John, how are you?
Alice --|/John: Hi Alice, I can hear you!
Alice --|/John: Test`
);
});
it('should render Stick half arrow top dotted', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice--\\\\John: Hello John, how are you?
Alice--\\\\John: Hi Alice, I can hear you!
Alice--\\\\John: Test`
);
});
it('should render Stick half arrow bottom dotted', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice--//John: Hello John, how are you?
Alice--//John: Hi Alice, I can hear you!
Alice--//John: Test`
);
});
it('should render Solid half arrow top reverse dotted', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice/|--John: Hello Alice, how are you?
Alice/|--John: Hi Alice, I can hear you!
Alice/|--John: Test`
);
});
it('should render Solid half arrow bottom reverse dotted', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice\\|--John: Hello Alice, how are you?
Alice\\|--John: Hi Alice, I can hear you!
Alice\\|--John: Test`
);
});
it('should render Stick half arrow top reverse dotted ', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice//--John: Hello Alice, how are you?
Alice//--John: Hi Alice, I can hear you!
Alice//--John: Test`
);
});
it('should render Stick half arrow bottom reverse dotted ', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice\\\\--John: Hello Alice, how are you?
Alice\\\\--John: Hi Alice, I can hear you!
Alice\\\\--John: Test`
);
});
});
});

View File

@@ -327,8 +327,97 @@ classDef sales fill:#c3a66b,stroke:#333;
{}
);
});
it('12: should apply classDef fill color to leaf nodes', () => {
imgSnapshotTest(
`treemap-beta
"Root"
"Item A": 30:::redClass
"Item B": 20
"Item C": 25:::blueClass
classDef redClass fill:#ff0000;
classDef blueClass fill:#0000ff;
`,
{}
);
});
it('13: should apply classDef stroke styles to sections', () => {
imgSnapshotTest(
`treemap-beta
%% This is a comment
"Category A":::thickBorder
"Item A1": 10
"Item A2": 20
%% Another comment
"Category B":::dashedBorder
"Item B1": 15
"Item B2": 25
classDef thickBorder stroke:red,stroke-width:8px;
classDef dashedBorder stroke:black,stroke-dasharray:5,stroke-width:8px;
`,
{}
);
});
it('14: should apply classDef color to text labels', () => {
imgSnapshotTest(
`treemap-beta
"Products"
"Electronics":::whiteText
"Phones": 40
"Laptops": 30
"Furniture":::darkText
"Chairs": 25
"Tables": 20
classDef whiteText fill:#2c3e50,color:#ffffff;
classDef darkText fill:#ecf0f1,color:#000000;
`,
{}
);
});
it('15: should apply multiple classDef properties simultaneously', () => {
imgSnapshotTest(
`treemap-beta
"Budget"
"Critical":::critical
"Server Costs": 50000
"Salaries": 80000
"Normal":::normal
"Office Supplies": 5000
"Marketing": 15000
classDef critical fill:#e74c3c,color:#fff,stroke:#c0392b,stroke-width:3px;
classDef normal fill:#3498db,color:#fff,stroke:#2980b9,stroke-width:1px;
`,
{}
);
});
it('16: should handle classDef on nested sections and leaves', () => {
imgSnapshotTest(
`treemap-beta
"Company"
"Engineering":::engSection
"Frontend": 30:::highlight
"Backend": 40
"DevOps": 20:::highlight
"Sales"
"Direct": 35
"Channel": 25:::highlight
classDef engSection fill:#9b59b6,stroke:#8e44ad,stroke-width:2px;
classDef highlight fill:#f39c12,color:#000,stroke:#e67e22,stroke-width:2px;
`,
{}
);
});
/*
it.skip('12: should render a treemap with title', () => {
it.skip('17: should render a treemap with title', () => {
imgSnapshotTest(
`
treemap-beta

View File

@@ -0,0 +1,16 @@
sequenceDiagram
actor Alice
participant John@{ "type": "boundary" }
participant P1@{ "type": "control" } as New Control
participant P2@{ "type": "entity" } as New Entity
participant P3@{ "type": "database" } as New Database
Alice->>+John: Hello John, how are you?
John->>P1: new msg
P1->>P2: new msg
P2->>P3: new msg
Alice->>+John: John, can you hear me?
P3->>P2: new msg
P2->>P1: new msg
John-->>-Alice: Hi Alice, I can hear you!
P1->>John: new msg
John-->>-Alice: I feel great!

View File

@@ -0,0 +1,9 @@
treemap
"Section 1"
"Leaf 1.1": 12
"Section 1.2":::class1
"Leaf 1.2.1": 12
"Section 2"
"Leaf 2.1": 20:::class1
"Leaf 2.2": 25
"Leaf 2.3": 12

View File

@@ -0,0 +1,11 @@
---
title: "Grades"
---
radar-beta
axis m["Math"], s["Science"], e["English"]
axis h["History"], g["Geography"], a["Art"]
curve a["Alice"]{85, 90, 80, 70, 75, 90}
curve b["Bob"]{70, 75, 85, 80, 90, 85}
max 100
min 0

View File

@@ -0,0 +1,11 @@
flowchart LR
A["A"] e1@==> B["B"]
A L_A_n1_0@--> n1["C"]
B L_B_n2_0@==> n2["D"]
n1 L_n1_n2_0@--> n2
e1@{ animate: true }
L_A_n1_0@{ animation: slow }
L_B_n2_0@{ animation: slow }
L_n1_n2_0@{ animation: fast }

View File

@@ -0,0 +1,3 @@
flowchart LR
A e1@==> B
e1@{ animate: true }

View File

@@ -0,0 +1,18 @@
---
config:
layout: ogdc
---
flowchart-elk TB
c1-->a2
subgraph one
a1-->a2
end
subgraph two
b1-->b2
end
subgraph three
c1-->c2
end
one --> two
three --> two
two --> c2

View File

@@ -0,0 +1,17 @@
---
config:
layout: elk
---
flowchart TB
process_C
subgraph container_Alpha
subgraph process_B
pppB
end
subgraph process_A
pppA
end
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|container_Beta
end

View File

@@ -0,0 +1,18 @@
---
config:
layout: elk
---
flowchart TB
subgraph container_Beta
process_C
end
subgraph container_Alpha
subgraph process_B
pppB
end
subgraph process_A
pppA
end
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|container_Beta
end

View File

@@ -0,0 +1,10 @@
---
config:
layout: elk
---
flowchart TB
subgraph container_Beta
process_C
end
process_B-->|via_AWSBatch|container_Beta

View File

@@ -0,0 +1,31 @@
---
config:
layout: elk
---
classDiagram
note "I love this diagram!\nDo you love it?"
Class01 <|-- AveryLongClass : Cool
<<interface>> Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class12 <|.. Class08
Class11 ..>Class12
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class01 : -int privateChimp
Class01 : +int publicGorilla
Class01 : #int protectedMarmoset
Class08 <--> C2: Cool label
class Class10 {
<<service>>
int id
test()
}
note for Class10 "Cool class\nI said it's very cool class!"

View File

@@ -0,0 +1,17 @@
---
config:
layout: elk
---
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
element test_entity {
type: simulation
}
test_entity - satisfies -> test_req

View File

@@ -0,0 +1,21 @@
---
config:
layout: elk
---
flowchart-elk TB
internet
nat
router
compute1
subgraph project
router
nat
subgraph subnet1
compute1
end
end
%% router --> subnet1
subnet1 --> nat
%% nat --> internet

View File

@@ -0,0 +1,27 @@
---
config:
layout: elk
---
flowchart-elk TB
internet
nat
router
lb1
lb2
compute1
compute2
subgraph project
router
nat
subgraph subnet1
compute1
lb1
end
subgraph subnet2
compute2
lb2
end
end
internet --> router
router --> subnet1 & subnet2
subnet1 & subnet2 --> nat --> internet

View File

@@ -0,0 +1,14 @@
---
config:
layout: elk
elk:
mergeEdges: false
forceNodeModelOrder: false
considerModelOrder: NONE
---
flowchart TB
a --> a1 & a2 & a3 & a4
b --> b1 & b2
b2 --> b3
b1 --> b4

View File

@@ -0,0 +1,11 @@
---
config:
layout: elk
flowchart:
curve: rounded
---
flowchart LR
I["fa:fa-code Text"] -- Mermaid js --> D["Use<br/>the<br/>editor!"]
I --> D & D
D@{ shape: question}
I@{ shape: question}

View File

@@ -0,0 +1,21 @@
---
config:
layout: tidy-tree
---
mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
Pen and paper
Mermaid

View File

@@ -0,0 +1,11 @@
---
config:
layout: elk
flowchart:
curve: linear
---
flowchart LR
A[A] --> B[B]
A[A] --- B([C])
A@{ shape: diamond}
%%B@{ shape: diamond}

View File

@@ -0,0 +1,11 @@
---
config:
layout: elk
flowchart:
curve: linear
---
flowchart LR
A[A] -- Mermaid js --> B[B]
A[A] -- Mermaid js --- B[B]
A@{ shape: diamond}
B@{ shape: diamond}

View File

@@ -0,0 +1,11 @@
---
config:
layout: elk
flowchart:
curve: rounded
---
flowchart LR
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
I --> D & D
D@{ shape: question}
I@{ shape: question}

View File

@@ -0,0 +1,14 @@
---
config:
layout: elk
flowchart:
curve: rounded
elk:
nodePlacementStrategy: NETWORK_SIMPLEX
---
flowchart LR
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
D --> I & I
a["a"]
D@{ shape: trap-b}
I@{ shape: lean-l}

View File

@@ -0,0 +1,11 @@
---
config:
layout: elk
---
flowchart LR
%% subgraph s1["Untitled subgraph"]
C["Evaluate"]
%% end
B --> C

View File

@@ -0,0 +1,10 @@
---
config:
layout: elk
flowchart:
//curve: linear
---
flowchart LR
%% A ==> B
%% A2 --> B2
A{A} --> B((Bo boo)) & B & B & B

View File

@@ -0,0 +1,18 @@
---
config:
layout: elk
theme: default
look: classic
---
flowchart LR
subgraph s1["APA"]
D{"Use the editor"}
end
subgraph S2["S2"]
s1
I>"fa:fa-code Text"]
E["E"]
end
D -- Mermaid js --> I
D --> I & E
E --> I

View File

@@ -0,0 +1,23 @@
---
config:
layout: elk
---
flowchart LR
a
subgraph s0["APA"]
subgraph s8["APA"]
subgraph s1["APA"]
D{"X"}
E[Q]
end
subgraph s3["BAPA"]
F[Q]
I
end
D --> I
D --> I
D --> I
I{"X"}
end
end

View File

@@ -0,0 +1,11 @@
---
config:
layout: elk
---
flowchart LR
a
D{"Use the editor"}
D -- Mermaid js --> I{"fa:fa-code Text"}
D-->I
D-->I

View File

@@ -0,0 +1,36 @@
---
config:
layout: elk
---
flowchart LR
subgraph s1["Untitled subgraph"]
n1["Evaluate"]
n2["Option 1"]
n3["Option 2"]
n4["fa:fa-car Option 3"]
end
subgraph s2["Untitled subgraph"]
n5["Evaluate"]
n6["Option 1"]
n7["Option 2"]
n8["fa:fa-car Option 3"]
end
A["Start"] -- Some text --> B("Continue")
B --> C{"Evaluate"}
C -- One --> D["Option 1"]
C -- Two --> E["Option 2"]
C -- Three --> F["fa:fa-car Option 3"]
n1 -- One --> n2
n1 -- Two --> n3
n1 -- Three --> n4
n5 -- One --> n6
n5 -- Two --> n7
n5 -- Three --> n8
n1@{ shape: diam}
n2@{ shape: rect}
n3@{ shape: rect}
n4@{ shape: rect}
n5@{ shape: diam}
n6@{ shape: rect}
n7@{ shape: rect}
n8@{ shape: rect}

View File

@@ -0,0 +1,10 @@
---
config:
layout: elk
---
flowchart LR
subgraph s1["Untitled subgraph"]
n1["Evaluate"]
n2["Option 1"]
end
n1 -- One --> n2

View File

@@ -0,0 +1,6 @@
---
config:
layout: elk
---
flowchart LR
A{A} --> B & C

View File

@@ -0,0 +1,9 @@
---
config:
layout: elk
---
flowchart LR
A{A} --> B & C
subgraph "subbe"
A
end

View File

@@ -0,0 +1,14 @@
---
config:
layout: elk
---
flowchart LR
n2@{ shape: rect}
n3@{ shape: rect}
n4@{ shape: rect}
A["Start"] -- Some text --> B("Continue")
B --> C{"Evaluate"}
C -- One --> D["Option 1"]
C -- Two --> E["Option 2"]
C -- Three --> F["fa:fa-car Option 3"]
%% C@{ shape: hexagon}

View File

@@ -0,0 +1,16 @@
---
config:
kanban:
ticketBaseUrl: 'https://github.com/your-repo/issues/#TICKET#'
---
kanban
Backlog
task1[📝 Define project requirements]@{ ticket: a101 }
To Do
task2[🔍 Research technologies]@{ ticket: a102 }
Review
task4[🔍 Code review for login feature]@{ ticket: a104 }
Done
task5[✅ Deploy initial version]@{ ticket: a105 }
In Progress
task3[💻 Develop login feature]@{ ticket: 103 }

View File

@@ -0,0 +1,2 @@
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }

View File

@@ -0,0 +1,3 @@
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
style A fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -0,0 +1,4 @@
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -0,0 +1,2 @@
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }

View File

@@ -0,0 +1,2 @@
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }

View File

@@ -0,0 +1,3 @@
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
style A fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -0,0 +1,4 @@
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -0,0 +1,2 @@
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }

View File

@@ -0,0 +1,2 @@
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }

View File

@@ -0,0 +1,3 @@
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -0,0 +1,4 @@
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px

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