Skip to content

fix(elk): scope rounded edge curve to ELK layout only#7452

Closed
knsv-bot wants to merge 24 commits intomasterfrom
bug/7213_elk-edges-rounded-scoped
Closed

fix(elk): scope rounded edge curve to ELK layout only#7452
knsv-bot wants to merge 24 commits intomasterfrom
bug/7213_elk-edges-rounded-scoped

Conversation

@knsv-bot
Copy link
Collaborator

@knsv-bot knsv-bot commented Mar 5, 2026

Summary

Scopes the fix from #7425 (issue #7213) so that the rounded curve is only applied when the ELK layout algorithm is used, rather than changing the global default for all flowcharts.

  • Reverts the global flowchart.curve default from 'rounded' back to 'basis'
  • In the ELK render, defaults edgeData.curve to 'rounded' when the user hasn't explicitly configured an interpolation
  • Non-ELK layouts are completely unaffected

Resolves #7213

Test plan

  • Unit tests pass (969 flowchart tests + 4 edge tests)
  • Config type verification passes
  • Cypress e2e snapshots (ELK edges route orthogonally with rounded corners)
  • CI passes (lint, unit, e2e, type-check)

🤖 Generated with Claude Code

Danny Milosavljevic and others added 22 commits January 6, 2026 15:18
… LR GitGraph

The background rectangle for branch labels was misaligned with the text
when branch names contained multi-line text (e.g. "Feature A\n(ongoing)")
in LR layout. The old transform used `pos - bbox.height / 2` which caused
the rect to shift further up as text height increased, while the text
position remained centered at `pos - 1`.

Fixed by using a constant y-offset (`pos - 11`) in the transform,
derived from the constraint that the rect center must equal the text
center regardless of bbox.height.

Closes #7362
Fix the TypeScript types for `createText.ts`, allowing us to remove the
`@ts-nocheck` statement. I've also made sure to get rid of all the `any`
types in this file.
I'm not exactly sure why `decodeEntities()` is needed and what it does,
but I added a brief comment to make it a bit more clear that it's
**NOT** decoding HTML entities, but just decoding what `encodeEntities`
is doing.
In the `createText()` function, we are calling `decodeEntities()` on the
input if `htmlLabels: true`, but we weren't doing it `htmlLabels: false`
was set.

I can't seem to find anywhere this is actually impacting diagrams, since
we're calling `decodeEntities()` already normally before calling
`createText()`, hence why this is just a `refactor` commit.

Original-commit: #7297
Co-authored-by: chandershekhar22 <chandersj.it.22@nitj.ac.in>
When creating labels using `htmlLabels: false`, e.g.

```mermaid
---
config:
    htmlLabels: false
---
flowchart TD
    A[2 < 4 && 12 > 14]
```

The SVG node label gets rendered as
`2 &lt; 4 &amp;&amp; 12 &gt; 14`. This is fine for HTML text, where we
use `.innerHTML` to set the value. But for non-HTML Labels, we use
`.textContent`, so we need to pass the unescaped values.

Ideally we would stop calling DOMPurify on this label when
`.textContent` is used, since the content doesn't need to be sanitized,
but adding a quick `&lt;`/`&gt;`/`&amp;`-> `<`/`>`/`&` also works.

I've adapted this commit from #6406.

Closes: #6406
Co-authored-by: khalil <5alil.landolsi@gmail.com>
TypeScript wasn't catching these earlier, since `bbox` was `any`,
but now that it's been typed correctly, all the `node.padding` uses in
expressions with `bbox` are throwing TypeScript errors.
According to MDN, this is only `null` if the `Node` is a `Document`.
I'm not 100% sure why TypeScript is throwing an error on this, since my
VS Code shows that `Element.textContent` will never be `null`, since
`Document` can never be an `Element`, but we do have other non-null
assertions for this scattered over the place.

See: https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
We know that the output of `createText` when `htmlLabels: true` is set
is a `<foreignObject>` that contains a `<div>`, so this can fix our
TypeScript errors.
The sequence diagram JISON parser had no catch-all rule in the exclusive
ID lexer state. When input like `participant X asAlias:text` was parsed
(missing space after "as"), none of the ID-state rules could match due
to the colon terminating the character class match before end-of-line.
This caused a lexer error that could hang browsers depending on error
recovery behavior.

Add a catch-all `<ID>[^\n]+` rule that produces an INVALID token when
no other ID-state rule matches, ensuring the parser always makes
progress and produces a clean parse error.

Resolves #6399

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replacing `&lt;` and `&gt;` in requirement shapes is no longer
necessary, now that `createText` does it automatically when `htmlLabels:
false`.
…n-htmlLabels-false

fix: prevent escaping `<` and `&` when `htmlLabels: false`
…label-alignment

fix: align branch label background with text for multi-line labels in LR GitGraph
fix: pie: Don't sort, keep order.  Keep color order constant.
…ng_space_after_as

fix(sequence): prevent hang when "as" lacks trailing space
Reverts the global flowchart curve default from 'rounded' back to 'basis'
and instead applies 'rounded' only in the ELK render when no explicit
interpolation is configured. Non-ELK layouts are unaffected.

Fixes #7213

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 5, 2026

🦋 Changeset detected

Latest commit: ee0ade3

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

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

@github-actions github-actions bot added the Type: Bug / Error Something isn't working or is incorrect label Mar 5, 2026
@codecov
Copy link

codecov bot commented Mar 5, 2026

Codecov Report

❌ Patch coverage is 0% with 87 lines in your changes missing coverage. Please review.
✅ Project coverage is 3.53%. Comparing base (fd94411) to head (ee0ade3).
⚠️ Report is 17 commits behind head on master.

Files with missing lines Patch % Lines
packages/mermaid/src/rendering-util/createText.ts 0.00% 46 Missing ⚠️
packages/mermaid/src/diagrams/pie/pieRenderer.ts 0.00% 7 Missing ⚠️
.../rendering-util/rendering-elements/shapes/erBox.ts 0.00% 4 Missing ⚠️
...ering-util/rendering-elements/shapes/subroutine.ts 0.00% 3 Missing ⚠️
packages/mermaid/src/themes/theme-dark.js 0.00% 3 Missing ⚠️
...ering-util/rendering-elements/shapes/bowTieRect.ts 0.00% 2 Missing ⚠️
...c/rendering-util/rendering-elements/shapes/card.ts 0.00% 2 Missing ⚠️
...ndering-util/rendering-elements/shapes/cylinder.ts 0.00% 2 Missing ⚠️
...ring-util/rendering-elements/shapes/dividedRect.ts 0.00% 2 Missing ⚠️
...ndering-util/rendering-elements/shapes/document.ts 0.00% 2 Missing ⚠️
... and 9 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           master   #7452      +/-   ##
=========================================
- Coverage    3.54%   3.53%   -0.01%     
=========================================
  Files         490     493       +3     
  Lines       48938   49036      +98     
  Branches      766     769       +3     
=========================================
- Hits         1734    1733       -1     
- Misses      47204   47303      +99     
Flag Coverage Δ
unit 3.53% <0.00%> (-0.01%) ⬇️

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

Files with missing lines Coverage Δ
...g-util/rendering-elements/shapes/requirementBox.ts 0.00% <ø> (ø)
packages/mermaid/src/utils.ts 18.18% <ø> (ø)
packages/mermaid-layout-elk/src/render.ts 0.12% <0.00%> (-0.01%) ⬇️
packages/mermaid/src/diagrams/class/shapeUtil.ts 0.00% <0.00%> (ø)
...kages/mermaid/src/diagrams/git/gitGraphRenderer.ts 0.00% <0.00%> (ø)
...c/rendering-util/rendering-elements/shapes/util.ts 0.00% <0.00%> (ø)
...ering-util/rendering-elements/shapes/bowTieRect.ts 0.00% <0.00%> (ø)
...c/rendering-util/rendering-elements/shapes/card.ts 0.00% <0.00%> (ø)
...ndering-util/rendering-elements/shapes/cylinder.ts 0.00% <0.00%> (ø)
...ring-util/rendering-elements/shapes/dividedRect.ts 0.00% <0.00%> (ø)
... and 11 more

... and 6 files with indirect coverage changes

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

@argos-ci
Copy link

argos-ci bot commented Mar 5, 2026

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

Build Status Details Updated (UTC)
default (Inspect) 👍 Changes approved 456 changed, 2 added Mar 5, 2026, 2:38 PM

knsv-bot and others added 2 commits March 5, 2026 15:14
The previous commit set 'rounded' on edgeData which is not passed to
insertEdge. The actual edge object (from ELK output) already has
curve:'basis' set by flowDb.getData(), so it must be overridden
unconditionally right before the insertEdge call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@knsv knsv marked this pull request as ready for review March 5, 2026 15:06
Copy link
Collaborator

@ashishjain0512 ashishjain0512 left a comment

Choose a reason for hiding this comment

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

[sisyphus-bot]

This is a substantial PR that bundles the ELK edge curve scoping with several other fixes (createText typing, pie chart ordering, sequence hang, gitGraph label alignment). Reviewing the full scope here.

Note: PR targets master rather than develop. This appears to be an intentional hotfix/release PR given it's authored by knsv-bot and approved by @knsv.

File Triage

Tier Count Files
Tier 1 (full read) 1 createText.ts
Tier 2 (diff + context) 8 render.ts, pieRenderer.ts, sequenceDiagram.jison, gitGraphRenderer.ts, shapeUtil.ts, shapes/util.ts, test files
Tier 3 (diff only) 5 changesets, .gitignore, cypress specs
Skip 12 shape files (mechanical node.padding(node.padding ?? 0) null-safety)

What's working well

🎉 [praise] The createText.ts cleanup is excellent — removing @ts-nocheck and eslint-disable @typescript-eslint/no-explicit-any, then adding proper types throughout. This makes the shared rendering code significantly more maintainable. The D3Selection typing and non-null assertions (node()!) are well-placed.

🎉 [praise] The decodeHTMLEntities() function (createText.ts:189) is well-designed: limited to the three entities that sanitizeText encodes, clearly scoped to the .text() (textContent) path, and includes a helpful TODO comment about potentially skipping sanitization entirely for this path. Good defensive design.

🎉 [praise] The ELK fix is properly scoped (render.ts:1073) — setting edge.curve = 'rounded' only in the ELK render loop, with a clear comment explaining why. This avoids the regression from the previous global default change.

🎉 [praise] The pie chart fix (pieRenderer.ts:17,96-98) correctly preserves insertion order with .sort(null) and sets an explicit .domain([...sections.keys()]) to maintain color consistency when slices are filtered out. The unit tests (pieRenderer.spec.ts) verify both ordering and color stability — thoughtful coverage.

🎉 [praise] The sequence diagram hang fix (sequenceDiagram.jison:39) is a clean catch-all that prevents the lexer from looping infinitely when as is used without a trailing space. The unit test has a 5-second timeout guard — good.

Things to address

🟡 [important] The PR checklist shows unchecked items for Cypress e2e snapshots and CI. The createText.ts changes affect the shared rendering pipeline used by all diagram types — rendering-util/ modifications can regress any diagram. Per project conventions, full e2e verification (pnpm e2e) is needed before merge. The new Cypress tests for flowchart angle brackets and gitGraph multi-line labels are great, but the shape null-safety changes touch 12 shape files and should be verified visually too. Worth confirming CI passes cleanly.

🟢 [nit] gitGraphRenderer.ts:880pos - 11 replaces pos - bbox.height / 2. The magic number 11 is fragile and will break with different font sizes or scaling. A brief comment explaining the derivation (e.g., "empirically matches single-line label height") would help future maintainers.

🟢 [nit] requirementBox.ts:201 — The manual .replaceAll('&gt;', '>') / .replaceAll('&lt;', '<') is removed. This is correct since decodeHTMLEntities in createText.ts now handles this upstream, but it's worth confirming the requirementBox rendering path goes through createText for its labels (I believe it does via labelHelpercreateText).

Security

No XSS or injection issues identified. The decodeHTMLEntities() function outputs exclusively to D3's .text() method (which uses textContent, inherently safe against XSS). The decodeEntities() addition on line 329 is similarly safe — it feeds only into the non-HTML label path. No decoded content can reach any .html() or innerHTML sink through any code path introduced by this PR. The existing addHtmlSpan path with .html() is unaffected — it continues to use sanitizeText() before insertion.

Self-check

  • At least one 🎉 [praise] item exists (5)
  • No duplicate comments
  • Severity tally: 0 🔴 blocking / 1 🟡 important / 2 🟢 nit / 0 💡 suggestion / 5 🎉 praise
  • Verdict: COMMENT (1 🟡 — e2e verification needed)
  • Not a draft PR
  • No inline comments used
  • Tone check: constructive, appreciative, actionable

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants