diff --git a/.changeset/brave-memes-flash.md b/.changeset/brave-memes-flash.md new file mode 100644 index 000000000..720cd7202 --- /dev/null +++ b/.changeset/brave-memes-flash.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Support edge animation in hand drawn look diff --git a/.changeset/busy-mirrors-try.md b/.changeset/busy-mirrors-try.md new file mode 100644 index 000000000..7e5d3b632 --- /dev/null +++ b/.changeset/busy-mirrors-try.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Resolved parsing error where direction TD was not recognized within subgraphs diff --git a/.changeset/chilly-words-march.md b/.changeset/chilly-words-march.md new file mode 100644 index 000000000..54c0b4ebf --- /dev/null +++ b/.changeset/chilly-words-march.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Correct viewBox casing and make SVGs responsive diff --git a/.changeset/curly-apes-prove.md b/.changeset/curly-apes-prove.md new file mode 100644 index 000000000..2acf3d1a3 --- /dev/null +++ b/.changeset/curly-apes-prove.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Improve participant parsing and prevent recursive loops on invalid syntax diff --git a/.changeset/deep-pumas-run.md b/.changeset/deep-pumas-run.md deleted file mode 100644 index eca83fd6e..000000000 --- a/.changeset/deep-pumas-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'mermaid': patch ---- - -chore: Fix mindmap rendering in docs and apply tidytree layout diff --git a/.changeset/four-eyes-wish.md b/.changeset/four-eyes-wish.md deleted file mode 100644 index 3bbbc1e47..000000000 --- a/.changeset/four-eyes-wish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'mermaid': patch ---- - -fix: Ensure edge label color is applied when using classDef with edge IDs diff --git a/.changeset/moody-fans-try.md b/.changeset/moody-fans-try.md deleted file mode 100644 index f6920a629..000000000 --- a/.changeset/moody-fans-try.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'mermaid': patch ---- - -fix: Resolve gantt chart crash due to invalid array length diff --git a/.changeset/proud-colts-smell.md b/.changeset/proud-colts-smell.md deleted file mode 100644 index 610d69253..000000000 --- a/.changeset/proud-colts-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'mermaid': minor ---- - -feat: Add IDs in architecture diagrams diff --git a/.changeset/revert-marked-dependency.md b/.changeset/revert-marked-dependency.md deleted file mode 100644 index aded58871..000000000 --- a/.changeset/revert-marked-dependency.md +++ /dev/null @@ -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 diff --git a/.changeset/short-seals-sort.md b/.changeset/short-seals-sort.md new file mode 100644 index 000000000..db8309c7f --- /dev/null +++ b/.changeset/short-seals-sort.md @@ -0,0 +1,5 @@ +--- +'mermaid': minor +--- + +feat: allow to put notes in namespaces on classDiagram diff --git a/.cspell/code-terms.txt b/.cspell/code-terms.txt index 8b549f888..5f72ea221 100644 --- a/.cspell/code-terms.txt +++ b/.cspell/code-terms.txt @@ -1,3 +1,5 @@ +!viewbox +# It should be viewBox # This file contains coding related terms ALPHANUM antiscript diff --git a/.cspell/libraries.txt b/.cspell/libraries.txt index feee10fd1..4fceed5bb 100644 --- a/.cspell/libraries.txt +++ b/.cspell/libraries.txt @@ -64,6 +64,7 @@ rscratch shiki Slidev sparkline +speccharts sphinxcontrib ssim stylis diff --git a/.esbuild/util.ts b/.esbuild/util.ts index 3a0ec6b41..a3e2ffe55 100644 --- a/.esbuild/util.ts +++ b/.esbuild/util.ts @@ -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 diff --git a/.github/workflows/e2e-timings.yml b/.github/workflows/e2e-timings.yml index e59903df6..21f6b4049 100644 --- a/.github/workflows/e2e-timings.yml +++ b/.github/workflows/e2e-timings.yml @@ -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@0edc001d28a2959cd7a6b505629f1d82f0a6e67d with: add-paths: | cypress/timings.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7dbf85d94..ece84ac20 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,5 +42,4 @@ jobs: publish: pnpm changeset:publish env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_CONFIG_PROVENANCE: true diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 539f6ee2f..3177ca15a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -20,7 +20,7 @@ jobs: with: persist-credentials: false - name: Run analysis - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif diff --git a/.github/workflows/validate-lockfile.yml b/.github/workflows/validate-lockfile.yml index 59a6df96d..dcfb255b6 100644 --- a/.github/workflows/validate-lockfile.yml +++ b/.github/workflows/validate-lockfile.yml @@ -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 }}, + }); diff --git a/.vite/build.ts b/.vite/build.ts index 480dd6b30..d59f0fac3 100644 --- a/.vite/build.ts +++ b/.vite/build.ts @@ -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) { diff --git a/Dockerfile b/Dockerfile index 533604407..d7e6c8f84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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" diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts index c77ae50aa..51268c2a9 100644 --- a/cypress/helpers/util.ts +++ b/cypress/helpers/util.ts @@ -6,6 +6,7 @@ interface CypressConfig { listUrl?: boolean; listId?: string; name?: string; + screenshot?: boolean; } type CypressMermaidConfig = MermaidConfig & CypressConfig; @@ -90,7 +91,7 @@ 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, '-'); @@ -106,12 +107,17 @@ export const openURLAndVerifyRendering = ( } } 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 => { diff --git a/cypress/integration/rendering/classDiagram-v2.spec.js b/cypress/integration/rendering/classDiagram-v2.spec.js index 0c5dbc04b..f54768d9a 100644 --- a/cypress/integration/rendering/classDiagram-v2.spec.js +++ b/cypress/integration/rendering/classDiagram-v2.spec.js @@ -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( ` diff --git a/cypress/integration/rendering/classDiagram-v3.spec.js b/cypress/integration/rendering/classDiagram-v3.spec.js index 626d6fcea..7e8d2ff0a 100644 --- a/cypress/integration/rendering/classDiagram-v3.spec.js +++ b/cypress/integration/rendering/classDiagram-v3.spec.js @@ -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( ` diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js index b9c8bbbf4..6df44932d 100644 --- a/cypress/integration/rendering/erDiagram.spec.js +++ b/cypress/integration/rendering/erDiagram.spec.js @@ -445,7 +445,7 @@ ORDER ||--|{ LINE-ITEM : contains { logLevel: 1 } ); }); - it('should render ER diagram with numeric entity names and attributes', () => { + it('should render ER diagram with standalone numeric entities', () => { imgSnapshotTest( `erDiagram PRODUCT ||--o{ ORDER-ITEM : has diff --git a/cypress/integration/rendering/flowchart-handDrawn.spec.js b/cypress/integration/rendering/flowchart-handDrawn.spec.js index 49c55c628..d3ca1d1f1 100644 --- a/cypress/integration/rendering/flowchart-handDrawn.spec.js +++ b/cypress/integration/rendering/flowchart-handDrawn.spec.js @@ -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'); + }); }); diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 40713ac4e..5e1984377 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -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', + } + ); + }); }); diff --git a/demos/classchart.html b/demos/classchart.html index 10d8e6b70..7f285533a 100644 --- a/demos/classchart.html +++ b/demos/classchart.html @@ -184,6 +184,7 @@ } Admin --> Report : generates +
     classDiagram
       namespace Company.Project.Module {
@@ -240,6 +241,20 @@
       Bike --> Square : "Logo Shape"
 
     
+
+
+      classDiagram
+        note "This is a outer note"
+        note for Class1 "This is a outer note for Class1"
+        namespace ns {
+          note "This is a inner note"
+          note for Class1 "This is a inner note for Class1"
+          class Class1
+          class Class2
+        }
+    
+
+