Skip to content

feat(c4): migrate C4 relationships to the unified edge rendering - #7883

Open
filipsajdak wants to merge 1 commit into
mermaid-js:developfrom
filipsajdak:feature/c4-migrate-edges
Open

filipsajdak wants to merge 1 commit into
mermaid-js:developfrom
filipsajdak:feature/c4-migrate-edges

Conversation

@filipsajdak

@filipsajdak filipsajdak commented Jun 18, 2026 •

Copy link
Copy Markdown
Collaborator

What this is

The edges slice of the in-place C4 renderer migration (plan: #7849). Standalone against develop. (It was stacked on #7842, the shapes slice, which merged on 2026-07-30; the diff is now this PR's own 5 files, +202/-42.) Its commits are:

  • feat(c4): render C4 relationships through the unified edge rendering
  • style(c4): dashed relationship lines and labels
  • feat(c4): honour UpdateRelStyle offsets and colours for relationship labels
  • fix(c4): place relationship endpoints via each shape's intersection

This slice: migrate relationships

The legacy svgDraw relationship drawing is replaced with mermaid's unified edge renderer (insertEdge/insertEdgeLabel): each Rel becomes a dashed edge with an arrowhead and an HTML label (BiRel -> arrowheads at both ends), matching c4model.com. The legacy grid still supplies the geometry (a straight line between the two shapes' boundary intersection points), fed to insertEdge with skipIntersect. Those endpoints now come from each shape's own node.intersect (its real outline), so arrows meet the person/cylinder/queue silhouette instead of a bounding box, with the rectangular approximation kept as a fallback. Boundaries and the grid layout are untouched - the layout slice is next.

UpdateRelStyle is honoured: $offsetX/$offsetY position the label (nudging it off nodes and separating parallel relationships) and $textColor/$lineColor recolour it. (The db stores named offsets as strings, so the renderer coerces them.)

Impact / how to see it

No flag - the existing C4 cypress baselines now route relationships through the unified renderer, so Argos shows the relationship change per diagram. Layout/positions are unchanged from the shapes slice.

Note

These $offset nudges are a legacy-syntax crutch; real auto-spacing arrives with the layout slice (dagre). Honouring them here keeps parity with the legacy renderer (no regression).

Summary

Migrates C4 relationship rendering to Mermaid’s unified edge system as part of an in-place C4 renderer migration, replacing legacy svgDraw relationship drawing with insertEdge and insertEdgeLabel. Relationships now render as dashed edges with arrowheads (including BiRel arrowheads on both ends) and relationship labels are built/rendered through unified edge-label primitives. The legacy grid continues to supply relationship geometry and boundary intersection points, while layout/boundary recursion ordering is preserved via async rendering.

Highlights

  • Unified C4 relationship rendering

    • Switches C4 relationship drawing from svgDraw to insertEdge / insertEdgeLabel.
    • Dashed edge styling + marker/arrowhead insertion; BiRel supports arrowheads at both ends.
    • Relationship label rendering honors UpdateRelStyle for label offsets and color, including coercing stored string $offsetX/$offsetY values into numeric offsets when needed.
    • drawC4ShapeArray, drawRels, and boundary recursion are converted to async to ensure computed bounds/intersections exist before subsequent drawing steps.
    • Shape element DOM IDs are namespaced per diagram ID to prevent cross-diagram collisions.
  • New unified C4 shape/label adapters

    • Adds c4ShapeAdapter.ts to adapt legacy C4 node/edge inputs into unified-renderer shapes/labels.
      • Resolves unified shapes via precedence: explicit shape/sprite → tags → legacy typeC4Shape mapping (including external variants) → default rounded.
      • Builds HTML-safe edge labels for relationships (escapes label text, and conditionally appends technology/description lines).
      • Constructs unified node objects via buildC4Node with computed CSS classes/styles and legacy layout fields retained for geometry.
    • Adds c4LabelHelper.ts for stacked C4-style labels as pure SVG text (name/type/stereotype + multi-line description), including wrapping behavior constrained by c4.width.
    • Updates the shared labelHelper to use c4LabelHelper when stereotyping is present.
    • Extends node types with optional stereotype?: string in rendering-util/types.ts.
  • C4 styling + edge/label CSS alignment

    • Replaces minimal C4 getStyles() with a fuller C4 stylesheet in src/diagrams/c4/styles.js.
    • Adds per-C4-element label font rules generated from getConfig().c4.
    • Restyles C4 relationships to align with dashed unified edge rendering and updates marker/label styling (including background/opacity behavior for edge labels).
  • Person shape enablement

    • Adds a person shape handler to the unified shapes registry (rendering-util/rendering-elements/shapes.ts).
    • Implements the person renderer (person.ts) using unified node/label helpers, including support for hand-drawn (look === 'handDrawn' / roughjs) rendering modes.
    • Adds a changeset for the new person shape and updates shape documentation snapshots accordingly.
  • Related shape/layout correctness tweaks

    • Refactors cylinder.ts and tiltedCylinder.ts dimension normalization to improve width/height edge cases when only one dimension is provided.
  • Docs/tests/changesets updated

    • Adds C4 documentation for element text wrapping (wrap: true + c4.width).
    • Updates Cypress C4 rendering characterization tests for element-shape overrides and description rendering/wrapping behavior.
    • Extends Cypress snapshot coverage with:
      • C4.7: per-element personFontFamily/personFontSize snapshot variations
      • C4.8: c4.width wrapping vs non-wrapping behavior
    • Updates docs.spec.ts inline snapshots by regenerating the shapes table after introducing C4 c4-* shapes and related shape docs.
    • Adds changesets (including c4-unified-shapes) and updates the cspell dictionary with Structurizr.

Testing

  • Full C4 unit suite passed, along with multi-diagram-id-uniqueness and docs.spec.
  • Visual comparison against develop showed updated output across the C4 corpus tiles (including general/directional and Rel_Back relationship cases).
  • Cypress E2E: an unrelated shard reported a Chrome renderer crash, but the remaining shard assertions were reported as passing.

@changeset-bot

changeset-bot Bot commented Jun 18, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 05df8c2

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

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

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

@netlify

netlify Bot commented Jun 18, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for mermaid-js ready!

Name Link
🔨 Latest commit 05df8c2
🔍 Latest deploy log https://app.netlify.com/projects/mermaid-js/deploys/6aad938df37b7f000891da6f
😎 Deploy Preview https://deploy-preview-7883--mermaid-js.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

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

@pkg-pr-new

pkg-pr-new Bot commented Jun 18, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@mermaid-js/examples

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

mermaid

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

@mermaid-js/layout-elk

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

@mermaid-js/layout-tidy-tree

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

@mermaid-js/mermaid-zenuml

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

@mermaid-js/parser

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

@mermaid-js/tiny

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

commit: 05df8c2

@argos-ci

argos-ci Bot commented Jun 18, 2026 •

Copy link
Copy Markdown

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

Build Status Details Updated (UTC)
default (Inspect) ⚠️ Changes detected (Review) 6 changed, 1 ignored Sep 18, 2026, 8:11 PM

@filipsajdak
filipsajdak force-pushed the feature/c4-migrate-edges branch from a54560f to 0673951 Compare June 18, 2026 18:29
@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Both unit-test failures are fixed (the fixes live in the underlying #7842 commits, since both rooted in the shapes slice, and this branch carries them):

  1. Multi-diagram ID uniqueness > "c4" - the unified shapes used the bare element alias (user, system) as the DOM id, so two identical C4 diagrams on one page collided. Fixed by namespacing the shape id per diagram (node.domId = \${diagramId}-${node.id}`) in drawC4ShapeArray, matching how the other diagrams scope their node ids. Folded into feat(c4): render C4 elements through the unified shapes`.
  2. docs.mts > buildShapeDoc > should build shapesTable - registering the seven c4-* shapes added rows to the generated shapes table; the inline snapshot just needed regenerating (vitest -u). Folded into feat(c4): add C4 element shapes to the unified shape registry.

Both PRs were force-pushed (fixes squashed into the relevant commits, no fix-on-top noise); #7883 was rebased onto the new #7842 tip. Verified locally: full c4 unit suite (161 passing), multi-diagram-id-uniqueness and docs.spec both green.

@codecov

codecov Bot commented Jun 18, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.24561% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.37%. Comparing base (cf83441) to head (05df8c2).
⚠️ Report is 5 commits behind head on develop.

Files with missing lines Patch % Lines
packages/mermaid/src/diagrams/c4/c4Renderer.ts 96.61% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #7883      +/-   ##
===========================================
- Coverage    80.73%   78.37%   -2.37%     
===========================================
  Files          619      619              
  Lines        84970    85049      +79     
  Branches     18596    18536      -60     
===========================================
- Hits         68601    66656    -1945     
- Misses       15350    17500    +2150     
+ Partials      1019      893     -126     
Flag Coverage Δ
e2e 64.18% <81.48%> (-9.46%) ⬇️
unit 78.64% <97.36%> (-0.16%) ⬇️

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

Files with missing lines Coverage Δ
packages/mermaid/src/diagrams/c4/c4ShapeAdapter.ts 95.65% <100.00%> (+1.03%) ⬆️
packages/mermaid/src/diagrams/c4/styles.js 100.00% <100.00%> (ø)
packages/mermaid/src/diagrams/c4/c4Renderer.ts 82.30% <96.61%> (+0.48%) ⬆️

... and 118 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.

@filipsajdak
filipsajdak force-pushed the feature/c4-migrate-edges branch 2 times, most recently from 655d903 to b7d0efd Compare June 19, 2026 07:23
@sidharthv96

Copy link
Copy Markdown
Member

@filipsajdak please refrain from force pushing across 15+ PRs, as it blocks our CI for everyone else. We're also at 97% use for screenshot tests for this month, so just trying to be conservative. Thank you.

image

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

@sidharthv96 sorry, I was trying to keep the PRs in easy to merge state.

@sidharthv96

Copy link
Copy Markdown
Member

@sidharthv96 sorry, I was trying to keep the PRs in easy to merge state.

No worries, I was just letting you know of the problem. You've been doing amazing work with C4! 🚀

It seems like the workflow of having multiple stacked PRs from a fork is poorly done in GitHub, as we cannot point to the previous branch as base.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

C4 rendering now uses unified shapes, SVG labels, asynchronous layout, and unified relationship edges. A new Person shape is registered for flowcharts and C4 elements. Typography, wrapping, documentation, release metadata, and rendering tests are updated.

Changes

C4 unified renderer migration

Layer / File(s) Summary
C4 shape and label primitives
packages/mermaid/src/rendering-util/...
Adds Person rendering, stereotype-aware SVG labels, node stereotype data, and dimension normalization updates.
Legacy-to-unified C4 adapter
packages/mermaid/src/diagrams/c4/c4ShapeAdapter.ts
Maps legacy C4 elements to unified shapes, labels, styles, and node data.
C4 renderer async unified rendering
packages/mermaid/src/diagrams/c4/c4Renderer.ts
Adds two-pass asynchronous node rendering and unified relationship edge, marker, and label insertion.
C4 styling
packages/mermaid/src/diagrams/c4/styles.js
Adds configurable typography, label styling, outlines, relationship colors, dashed edges, and opaque label backgrounds.
C4 validation and documentation
cypress/integration/rendering/c4/*, cypress/integration/rendering/newShapes.spec.ts, docs/syntax/*, packages/mermaid/src/docs/*, .changeset/*, .cspell/*
Updates rendering assertions, adds Person coverage, documents wrapping and shapes, refreshes generated docs, and records release metadata.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant draw
  participant drawInsideBoundary
  participant drawC4ShapeArray
  participant buildC4Node
  participant drawRels
  participant insertEdge

  draw->>drawInsideBoundary: render root boundary
  drawInsideBoundary->>drawC4ShapeArray: render and measure C4 nodes
  drawC4ShapeArray->>buildC4Node: adapt legacy C4 shape
  draw->>drawRels: render relationships
  drawRels->>insertEdge: insert edge and label
Loading

Possibly related issues

Possibly related PRs

Suggested labels: Graph: C4

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the primary change: migrating C4 relationships to unified edge rendering.
Description check ✅ Passed The description is detailed, relevant, and explains the migration scope, implementation, behavior changes, testing context, and impact. It does not use the template headings or include the requested i…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (5)
packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Person.ts (2)

51-51: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Pass knownBounds to updateNodeBounds to skip the reflow-causing getBBox().

w/totalHeight are already computed analytically here (body rect ∪ head circle exactly bounds to w × totalHeight), so updateNodeBounds doesn't need to fall back to getBBox(). Per util.ts's own docstring, this measurement is explicitly the costly path on large diagrams. The same applies to c4Terminal.ts, c4Browser.ts, c4Database.ts, and c4Queue.ts, which also compute exact w/h before drawing.

⚡ Proposed fix
-  updateNodeBounds(node, group);
+  updateNodeBounds(node, group, { width: w, height: totalHeight });

Also applies to: 17-29

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Person.ts` at
line 51, Pass the analytically computed bounds into updateNodeBounds instead of
relying on the fallback getBBox() path. In c4Person and the related shape
renderers c4Terminal, c4Browser, c4Database, and c4Queue, use the already-known
w/totalHeight (or w/h) as knownBounds when calling updateNodeBounds so the
layout avoids the costly reflow-triggering measurement.

11-64: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Shared boilerplate duplicated across all new C4 shapes.

The setup (styles2String/labelHelper/group insert), the label-centering transform formula, and the node.intersect = intersect.rect assignment are copy-pasted verbatim across c4Person.ts, c4Terminal.ts, c4Browser.ts, c4Database.ts, c4Queue.ts (and reportedly c4Folder.ts/c4Bucket.ts). Only the geometry/path differs per shape. The accentColor regex helper is also duplicated verbatim between c4Terminal.ts and c4Browser.ts.

Consider extracting a shared helper (e.g. in util.ts) that takes the computed body geometry and returns the positioned label transform, plus a single exported accentColor util, to avoid the 5-7x duplication and reduce future drift.

♻️ Sketch of a shared helper
// util.ts
export const centerLabel = (
  label: D3Selection<SVGGElement>,
  bbox: DOMRect,
  centerY: number
) => {
  label.attr(
    'transform',
    `translate(${-(bbox.width / 2) - (bbox.x - (bbox.left ?? 0))}, ${centerY - bbox.height / 2 - (bbox.y - (bbox.top ?? 0))})`
  );
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Person.ts`
around lines 11 - 64, The C4 shape implementations are repeating the same setup
and label-positioning logic across c4Person and the other new shapes. Extract
the shared boilerplate from c4Person’s styles2String/labelHelper/group insert
flow, the label transform calculation, and the node.intersect = intersect.rect
assignment into a common helper (for example in util.ts) that each shape can
call with its own geometry values. Also move the duplicated accentColor regex
logic into a single shared utility and import it from c4Terminal and c4Browser
to keep the shape files focused only on their unique drawing code.
packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Bucket.ts (1)

43-43: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Same forced-reflow concern as c4Folder.ts.

w and totalHeight are already known analytically (the path + rim ellipse bounds are exactly w × totalHeight). Passing knownBounds avoids the getBBox() reflow, which matters more here since this shape is rendered twice per node in the two-pass drawC4ShapeArray flow.

♻️ Proposed fix
-  updateNodeBounds(node, group);
+  updateNodeBounds(node, group, { width: w, height: totalHeight });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Bucket.ts` at
line 43, The C4 bucket shape is still forcing a reflow by calling
updateNodeBounds after rendering, even though its bounds are already known
analytically. Update the c4Bucket rendering path to pass knownBounds (using w
and totalHeight) into the node bounds update logic instead of relying on
getBBox(), and make sure the change is applied in the same render flow used by
drawC4ShapeArray so the two-pass rendering path avoids the extra layout work.
packages/mermaid/src/diagrams/c4/c4Renderer.ts (1)

492-501: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use the exported edgeLabels map instead of DOM traversal for label positioning.

Locating the just-inserted label via edgeLabels.node()?.lastElementChild works today because C4 edges never populate startLabelLeft/Right/endLabelLeft/Right (which insertEdgeLabel would otherwise append as additional siblings into the same container, breaking this assumption). edges.js already exports an edgeLabels Map keyed by edge.id for exactly this lookup purpose (edgeLabels.set(edge.id, edgeLabel) inside insertEdgeLabel), which would be more robust than relying on DOM child order.

♻️ Proposed fix
-import { insertEdgeLabel, insertEdge } from '../../rendering-util/rendering-elements/edges.js';
+import { insertEdgeLabel, insertEdge, edgeLabels as edgeLabelRegistry } from '../../rendering-util/rendering-elements/edges.js';
...
     if (edge.label) {
       await insertEdgeLabel(edgeLabels, edge);
-      const labelGroup = edgeLabels.node()?.lastElementChild;
-      if (labelGroup) {
-        labelGroup.setAttribute('transform', `translate(${labelX}, ${labelY})`);
+      const labelGroup = edgeLabelRegistry.get(edge.id);
+      if (labelGroup) {
+        labelGroup.attr('transform', `translate(${labelX}, ${labelY})`);
       }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mermaid/src/diagrams/c4/c4Renderer.ts` around lines 492 - 501, The
label positioning in c4Renderer’s edge-label handling is using DOM traversal via
edgeLabels.node()?.lastElementChild, which is brittle when insertEdgeLabel adds
extra sibling labels. Update the logic in the edge-label block to retrieve the
newly created label from the exported edgeLabels Map keyed by edge.id, matching
how insertEdgeLabel stores it, and then apply the transform to that specific
element instead of assuming the last child is the target.
packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Folder.ts (1)

36-36: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid forced reflow: pass known bounds to updateNodeBounds.

w/totalHeight are already computed analytically above; calling updateNodeBounds(node, group) without knownBounds forces a synchronous getBBox() read. Since c4Renderer's two-pass flow (measure + draw) invokes this shape function twice per node, that's two avoidable reflows per shape. util.ts's own docs recommend supplying knownBounds when the value exactly matches what getBBox() would return, which is the case here (the path bounds are exactly w × totalHeight).

♻️ Proposed fix
-  updateNodeBounds(node, group);
+  updateNodeBounds(node, group, { width: w, height: totalHeight });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Folder.ts` at
line 36, The c4Folder shape currently calls updateNodeBounds(node, group)
without supplying knownBounds, which causes an unnecessary getBBox() reflow. In
c4Folder.ts, use the already computed w and totalHeight to pass knownBounds into
updateNodeBounds so the measured bounds match the analytic path size and avoid
the extra synchronous layout read in the c4Renderer two-pass flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/mermaid/src/diagrams/c4/c4Renderer.ts`:
- Around line 492-501: The label positioning in c4Renderer’s edge-label handling
is using DOM traversal via edgeLabels.node()?.lastElementChild, which is brittle
when insertEdgeLabel adds extra sibling labels. Update the logic in the
edge-label block to retrieve the newly created label from the exported
edgeLabels Map keyed by edge.id, matching how insertEdgeLabel stores it, and
then apply the transform to that specific element instead of assuming the last
child is the target.

In `@packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Bucket.ts`:
- Line 43: The C4 bucket shape is still forcing a reflow by calling
updateNodeBounds after rendering, even though its bounds are already known
analytically. Update the c4Bucket rendering path to pass knownBounds (using w
and totalHeight) into the node bounds update logic instead of relying on
getBBox(), and make sure the change is applied in the same render flow used by
drawC4ShapeArray so the two-pass rendering path avoids the extra layout work.

In `@packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Folder.ts`:
- Line 36: The c4Folder shape currently calls updateNodeBounds(node, group)
without supplying knownBounds, which causes an unnecessary getBBox() reflow. In
c4Folder.ts, use the already computed w and totalHeight to pass knownBounds into
updateNodeBounds so the measured bounds match the analytic path size and avoid
the extra synchronous layout read in the c4Renderer two-pass flow.

In `@packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Person.ts`:
- Line 51: Pass the analytically computed bounds into updateNodeBounds instead
of relying on the fallback getBBox() path. In c4Person and the related shape
renderers c4Terminal, c4Browser, c4Database, and c4Queue, use the already-known
w/totalHeight (or w/h) as knownBounds when calling updateNodeBounds so the
layout avoids the costly reflow-triggering measurement.
- Around line 11-64: The C4 shape implementations are repeating the same setup
and label-positioning logic across c4Person and the other new shapes. Extract
the shared boilerplate from c4Person’s styles2String/labelHelper/group insert
flow, the label transform calculation, and the node.intersect = intersect.rect
assignment into a common helper (for example in util.ts) that each shape can
call with its own geometry values. Also move the duplicated accentColor regex
logic into a single shared utility and import it from c4Terminal and c4Browser
to keep the shape files focused only on their unique drawing code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a96b4b07-ba16-42a2-80b7-709a79d9bd66

📥 Commits

Reviewing files that changed from the base of the PR and between 0d1ee0a and 42bf443.

📒 Files selected for processing (14)
  • .cspell/mermaid-terms.txt
  • docs/syntax/flowchart.md
  • packages/mermaid/scripts/docs.spec.ts
  • packages/mermaid/src/diagrams/c4/c4Renderer.ts
  • packages/mermaid/src/diagrams/c4/c4ShapeAdapter.ts
  • packages/mermaid/src/diagrams/c4/styles.js
  • packages/mermaid/src/rendering-util/rendering-elements/shapes.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Browser.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Bucket.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Database.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Folder.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Person.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Queue.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/c4Terminal.ts

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Re the CodeRabbit nitpicks: the c4Renderer.ts suggestions (edgeLabels map, knownBounds) target code the next slice removes - the layout migration deletes c4Renderer.ts/svgDraw.ts and moves C4 onto the unified pipeline (tracked in #7849), so investing there would be churn. The shape-boilerplate/reflow points are folded into the planned shared-shape-helper refactor after the migration slices merge.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
cypress/integration/rendering/c4/c4-characterization.spec.js (1)

127-182: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Relationship characterization tests only assert on snapshots, not the new edge structure.

This PR's core change is migrating Rel/BiRel to insertEdge/insertEdgeLabel (dashed stroke, arrowhead, double-arrowhead for BiRel, HTML edge labels). None of the "relationships" tests add DOM assertions for this (e.g., stroke-dasharray, marker-end on the path, or .edgeLabel presence), unlike the shape/attribute tests in this same file which do add explicit cy.get(...) checks. Relying solely on image snapshots means a regression in edge rendering (e.g., missing arrowhead, wrong dash pattern) could pass unnoticed if baselines drift or if screenshots aren't diffed carefully in CI.

Consider adding at least one structural assertion per relationship test, e.g.:

cy.get('path.relation').should('have.attr', 'marker-end');
cy.get('path.relation').should('have.css', 'stroke-dasharray');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cypress/integration/rendering/c4/c4-characterization.spec.js` around lines
127 - 182, The relationship characterization tests in the relationships describe
block rely only on imgSnapshotTest, so they do not verify the new edge DOM
structure produced by insertEdge and insertEdgeLabel. Update the
CHAR.rel-directions, CHAR.rel-bidirectional, CHAR.rel-back, and
CHAR.rel-techn-descr cases to add structural Cypress assertions against the
rendered relationship elements (for example checking the relation path for
stroke-dasharray and marker-end, and asserting the edge label node exists) in
addition to the snapshots. Use the existing imgSnapshotTest calls and nearby
cy.get assertions in this spec as the pattern to locate where to add the new
checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cypress/integration/rendering/c4/c4-characterization.spec.js`:
- Around line 127-182: The relationship characterization tests in the
relationships describe block rely only on imgSnapshotTest, so they do not verify
the new edge DOM structure produced by insertEdge and insertEdgeLabel. Update
the CHAR.rel-directions, CHAR.rel-bidirectional, CHAR.rel-back, and
CHAR.rel-techn-descr cases to add structural Cypress assertions against the
rendered relationship elements (for example checking the relation path for
stroke-dasharray and marker-end, and asserting the edge label node exists) in
addition to the snapshots. Use the existing imgSnapshotTest calls and nearby
cy.get assertions in this spec as the pattern to locate where to add the new
checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6512ae46-3cba-4bc1-8547-ff98f1fa8b56

📥 Commits

Reviewing files that changed from the base of the PR and between 42bf443 and ef9b917.

📒 Files selected for processing (1)
  • cypress/integration/rendering/c4/c4-characterization.spec.js

@filipsajdak

filipsajdak commented Jul 8, 2026 •

Copy link
Copy Markdown
Collaborator Author

Visual proof for this PR's actual diff (not the converged end-state)

Same context as my note on #7842: the #7849 tracker's gallery is a preview of the converged end-state combining several future phases (not any single open PR's current diff). This PR had no dedicated visual proof of its own either, so here is one via the same local c4:visual-diff harness (Argos-free): upstream/develop (f3dea58) vs this PR's branch tip (ef9b917eb).

Note: #7883 is stacked on #7842, so this diff includes #7842's shape changes too, plus #7883's own relationship/edge migration on top - that's expected, not double-counting.

25 of 25 corpus tiles changed (1.8%-14.5%). A few representative tiles, chosen for relationship coverage:

internet-banking-context (14.51%) - whole-diagram overview

before (develop) after (this PR, stacked on #7842) diff
before after diff

CHAR.rel-directions (6.31%) - every directional Rel

before after diff
before after diff

CHAR.rel-back (4.72%) - Rel_Back

before after diff
before after diff

Full 25-tile before/after/diff gallery + summary table: summary.md (rendered HTML at index.html, download to view locally since GitHub serves raw HTML as text).

Scope reminder: boundaries and the legacy grid layout are still untouched by this stack - that's the next slice (layout migration, opens once this lands).

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Hi @aloisklink - friendly nudge on this one too, since it's stacked on #7842. Same CI pattern: green aside from the expected argos visual-diff. No merge conflicts, no open threads. Whenever you have a moment, a review would be very welcome.

The full staged plan - what's landed, what's open, and the recommended merge order - is kept up to date in #7849.

Thanks for your time!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/mermaid/src/diagrams/c4/c4ShapeAdapter.ts`:
- Around line 116-117: Update the escapeHtml function to encode both
double-quote and single-quote characters in addition to &, <, and >, preserving
the existing escaping behavior. Prefer the vetted sanitization approach already
used by Mermaid if appropriate, otherwise complete the manual entity encoding
within escapeHtml.

In
`@packages/mermaid/src/rendering-util/rendering-elements/shapes/c4LabelHelper.ts`:
- Line 46: Update the wrapWidth calculation in the C4 label helper to use the
configured wrapping width when node.width is undefined, while retaining padding
subtraction and MIN_WRAP_WIDTH clamping. Follow the fallback behavior used by
the standard labelHelper and preserve the existing width-based calculation for
explicitly sized nodes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: eab4e03f-2fae-43bb-a1fc-ca562a88a8c9

📥 Commits

Reviewing files that changed from the base of the PR and between ef9b917 and 4575d20.

📒 Files selected for processing (19)
  • .changeset/c4-unified-shapes.md
  • .changeset/person-shape.md
  • cypress/integration/rendering/c4/c4-characterization.spec.js
  • cypress/integration/rendering/c4/c4.spec.js
  • cypress/integration/rendering/newShapes.spec.ts
  • docs/config/setup/mermaid/interfaces/LayoutData.md
  • docs/syntax/c4.md
  • docs/syntax/flowchart.md
  • packages/mermaid/scripts/docs.spec.ts
  • packages/mermaid/src/diagrams/c4/c4ShapeAdapter.ts
  • packages/mermaid/src/diagrams/c4/styles.js
  • packages/mermaid/src/docs/syntax/c4.md
  • packages/mermaid/src/rendering-util/rendering-elements/shapes.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/c4LabelHelper.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/person.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts
  • packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts
  • packages/mermaid/src/rendering-util/types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/syntax/flowchart.md

Comment thread packages/mermaid/src/diagrams/c4/c4ShapeAdapter.ts Outdated
Comment thread packages/mermaid/src/rendering-util/rendering-elements/shapes/c4LabelHelper.ts Outdated
@filipsajdak

Copy link
Copy Markdown
Collaborator Author

CI note: the e2e (4) failure on the latest push is an unrelated flake - Cypress reports the Chrome renderer process crashed while starting rendering/iconShape-square.spec.ts (0 assertions failed; the other specs in the shard passed 332/332). I cannot re-run the job from a fork; a maintainer re-run of the failed job should bring e2e-required back to green.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Reopened - this was closed in error a few minutes ago, not intentionally. Nothing about the PR changed; sorry for the noise on your notifications, @aloisklink @sidharthv96.

While I am here, the useful update: now that #7842 has merged, this PR stands on its own. It no longer carries the 20 shape-migration commits, and the diff is down from 23 files to 3:

packages/mermaid/src/diagrams/c4/c4Renderer.ts     | 111 +++++++++++++--------
packages/mermaid/src/diagrams/c4/c4ShapeAdapter.ts |  20 ++++
packages/mermaid/src/diagrams/c4/styles.js         |  24 +++++
3 files changed, 113 insertions(+), 42 deletions(-)

That is the whole relationship migration: relationships render through the unified edge renderer (dashed lines, arrowheads, BiRel at both ends, UpdateRelStyle offsets and colours), with the legacy grid still supplying the straight-line geometry. c4 unit suite 126/126, tsc clean.

This is the last slice before the layout migration, which replaces the legacy Bounds grid with the unified dagre pipeline and deletes c4Renderer.ts/svgDraw.ts outright.

@filipsajdak
filipsajdak force-pushed the feature/c4-migrate-edges branch from c655077 to 4078c97 Compare August 1, 2026 20:07
@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Rebased onto current develop and dropped the trailing merge commit: 3 commits -> 2, one per concern:

feat(c4): render C4 relationships through the unified edge rendering
style(c4): dashed relationship lines and labels

The tree is byte-identical to the previous head - the series was rebuilt from that exact tree rather than replayed, and git diff <old head> <new head> was empty before pushing. 126 c4 unit tests pass, tsc clean.

Still 3 files, +113/-42.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

This PR has not had a human review yet. Since #7842 merged it stands alone at 3 files, +113/-42, and it is the last dependency of the C4 layout slice.

One thing to know before opening the checks: the argos failure here is the intended visual change (relationships move to the unified edge renderer) sitting unapproved. Approving or rejecting those diffs in Argos is the visual review for this PR; there is no broken test behind it.

Full stack status: #7849 (comment)

@knsv-bot knsv-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[sisyphus-bot]

Thanks for this, @filipsajdak — the edges slice of the C4 migration is a pleasure to read. Now that #7842 has merged, the diff is a tidy 3 files and the intent is very easy to follow. I did a close read of the renderer changes plus a full trace through the shared edge-label pipeline (and an independent XSS pass). A few things to tighten up before merge, but this is close.

🎉 What's working well

  • 🎉 [praise] The migration is genuinely well-documented. The comments explaining why — drawing edges/labels on top of shapes so the line is never hidden, the skipIntersect rationale for the node stubs, and the note on why you set the label transform directly instead of via positionEdgeLabel — are exactly the kind of context that saves the next maintainer an hour. Thank you for writing those.
  • 🎉 [praise] Security-conscious label building: escapeHtml is applied to every user-controlled field (label.text, techn.text, descr.text) before it's wrapped in markup, and the values only ever land in element-content position. I traced the full pipeline (buildEdgeLabel → insertEdgeLabel → createText → nonMarkdownToHTML → decodeEntities (markers-only, doesn't decode &lt;) → sanitizeText/DOMPurify → span.html()), plus the final whole-SVG sanitize, and an independent security review agreed: no XSS/injection vector. Nicely done.
  • 🎉 [praise] Good housekeeping: removing the now-dead messageFont helper and the four legacy svgDraw.insertArrow* defs is safe (only the retired svgDraw.drawRels referenced those markers), and the getIntersectPoints rewrite with Point objects is cleaner and behaviorally equivalent to the old version.
  • 🎉 [praise] UpdateRelStyle offsets/colours and birel bidirectional arrowheads are both honoured, and the defensive toOffset coercion (strings vs numbers depending on positional vs named args) shows you've thought about the db's quirks.

Things to address

  • 🟡 [important] — the getIntersectPoints comment describes behaviour the code doesn't have. c4Renderer.ts (the block added above getIntersectPoints) says it "Uses the drawn shape's own node.intersect (its real outline) when available, so arrows meet the person/cylinder/queue silhouette rather than a bounding box; falls back to the rectangular approximation…". But the function only ever calls the rectangular getIntersectPoint for both endpoints — there is no node.intersect anywhere in the C4 folder (grep finds zero references), and insertEdge is called with identity nodeStubs + skipIntersect: true, so no real-outline clipping happens either. As written, arrows meet the bounding box, not the silhouette. Could you either wire up the node.intersect lookup the comment promises, or correct the comment to match reality? Right now it'll actively mislead.

  • 🟡 [important] — htmlLabels: false renders the markup as literal text. buildEdgeLabel emits <b>…</b>, <small><i>…</i></small>, <br/>. When htmlLabels is disabled globally, createText takes the non-HTML branch (nonMarkdownToLines → tspan.text() / textContent), which doesn't interpret those tags — so a relationship label shows up as the literal string <b>Uses</b>, <small><i>[HTTPS]</i></small>, etc. That's a regression from the legacy renderer, which drew plain readable text. Worth gating buildEdgeLabel on getEffectiveHtmlLabels(config) (emitting a plain-text label when HTML labels are off), or switching to labelType: 'markdown' with **name** so both branches render correctly. A small e2e case with htmlLabels: false would lock this down.

  • 🟡 [important] — no changeset. This is a user-visible rendering change (dashed relationship lines, HTML-formatted labels, new arrowheads, offset/colour support) under a feat(c4) title, so it should carry a changeset (pnpm changeset, likely minor). If the umbrella plan (#7849) intends a single changeset for the whole migration at the end, no problem — just confirm that's the plan so it doesn't fall through the cracks.

Nits (non-blocking)

  • 🟢 [nit] — CSS injection hardening on the colour values. style: ['stroke:${lineColor}'] and cssStyles: ['color:${textColor}'] interpolate raw UpdateRelStyle values into a CSS declaration string. It's not script-executable and is blocked by GitHub/GitLab CSP, so no security blocker — but it does widen the surface vs the legacy presentation-attribute approach (a value like red;background:url(https://…) injects extra declarations). Consider validating with CSS.supports('color', value) or a small colour allowlist before interpolating, and dropping anything containing ;/url(//*.
  • 🟢 [nit] — edge-label map retention. insertEdgeLabel stores each label in the module-level edgeLabels Map keyed by edge.id, but C4 never calls edges.js clear() (nor positionEdgeLabel). Harmless since ids are unique per render, but it retains stale d3 selections across renders. Minor.
  • 🟢 [nit] — edgeLabels.node()?.lastElementChild is a bit fragile. It's correct for C4 today (only a mid-label, no start/end terminal labels), and the comment explains the rationale, so this is fine — just flagging that it assumes insertEdgeLabel appends exactly one direct child.

Tests

The existing C4 e2e specs (C4.1–C4.6) already exercise Rel, UpdateRelStyle, and BiRel, so Argos will capture this migration's visual change against the baselines — good coverage for the happy path. The two gaps worth a dedicated case are the htmlLabels: false behaviour above and a label containing special characters (<, &, quotes) to pin the escaping.

None of this is far off — mostly the comment/behaviour mismatch, the htmlLabels: false path, and a changeset. Happy to re-review as soon as those are addressed. 🙏

@filipsajdak
filipsajdak force-pushed the feature/c4-migrate-edges branch from 4078c97 to 8c8e95e Compare August 10, 2026 17:12
@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Thank you - and thank you especially for tracing the label pipeline end to end rather than eyeballing the escaping. That is a lot of work to do on someone else's PR.

All three important items and the first nit are addressed. One of them I have to correct, though the mistake behind it was mine.

🟡 The getIntersectPoints comment - corrected, and it is the comment that was wrong, not the code

The delegation you were looking for is there. getIntersectPoint is, in full:

const getIntersectPoint = function (fromNode: C4Shape, endPoint: Point): Point | null {
  if (!fromNode.intersect) {
    throw new Error(`C4 shape "${fromNode.alias}" has no intersect function. ...`);
  }
  const { x, y } = fromNode.intersect(endPoint);
  return new Point(x, y);
};

c4Shape.intersect is captured from the drawn unified shape in drawC4ShapeArray (c4Renderer.ts:317, c4Shape.intersect = node.intersect;), with the node pre-centred at its grid centre so the closure works in absolute coordinates. C4Shape.intersect is declared in c4Types.ts:59. That plumbing arrived with #7842 ("place relationship endpoints via each shape's intersection"), which is why a grep against a pre-#7842 base finds nothing - on this branch grep -ri intersect packages/mermaid/src/diagrams/c4/ returns 15 hits. So arrows do meet the person and cylinder silhouettes, and skipIntersect is passed precisely because the points are already clipped.

But you were misled by my comment, and that is a fair thing to be misled by: it claimed a fallback to "the rectangular approximation" that does not exist - the guard throws. I have rewritten it to describe what the code does. A comment promising behaviour the code lacks is worse than no comment, and this one cost you real time. Sorry about that.

One genuine leftover you have surfaced indirectly: getIntersectPoint's Point | null return type is now unreachable-null, which makes the caller's if (!points.startPoint || ...) guard dead. That arrived with #7842, so I have left it out of this PR rather than widen the diff - happy to clean it up in a follow-up if you would like it gone.

🟡 htmlLabels: false rendering markup as literal text - real regression, fixed

You are right, and the failure is exactly as you describe. With HTML labels off, createText takes the nonMarkdownToLines branch, which captures each tag as its own word and hard-codes type: 'normal' (handle-markdown-text.ts:33-41), so the tags reach innerTspan.text(...) and show as literal characters. Your repo's own test pins that behaviour at handle-markdown-text.spec.ts:316-334.

buildEdgeLabel now takes the effective setting and emits the plain form when HTML labels are off:

Uses<br/>[HTTPS]<br/>to read accounts

<br/> is kept in both forms deliberately - it is a line break in an HTML label and a line delimiter in a plain one, so the three-line shape survives either way.

I went with gating rather than labelType: 'markdown' because markdown gives bold and italic in both branches but has no equivalent for <small>, so the size distinction between the name and the technology/description lines would be lost for everyone. If you would rather have one code path than the styled default, say so and I will switch.

Covered by unit tests on buildEdgeLabel: both forms, the omitted-parts cases, and a hostile string (<script>, &, quotes) asserting it is escaped in the HTML form and left as-is in the plain one, where it is set as text content.

🟡 No changeset - added

minor, per the template's rule for features. Not deferring it to an umbrella changeset - one per PR is easier to audit, and #7842 already shipped with its own.

🟢 CSS injection hardening - taken

Colour values are now accepted only if they stand alone as a colour, via CSS.supports('color', value) with a conservative pattern fallback for environments without the CSS API (jsdom). A value like red;background:url(...) is dropped rather than interpolated. Cheap, and it keeps the surface no wider than the presentation-attribute approach it replaced.

🟢 The two remaining nits

  • Edge-label map retention - agreed, and I have left it. C4 never calls positionEdgeLabel, so wiring up clear() for its benefit alone felt like reaching into the shared module for no behavioural gain; ids are unique per render. Happy to add it if you would prefer the hygiene.
  • lastElementChild - agreed it is an assumption. It holds while C4 emits a mid-label only, and the comment says so. The alternative is a returned handle from insertEdgeLabel, which is a shared-signature change I would rather not make from this PR.

Also

Both commit messages had Co-Authored-By: Claude / Claude-Session: trailers. Those are gone - I hit the same point on #7859 and it applies here equally, so I have not waited for you to raise it. The first commit message also described the label as unconditionally HTML, which is no longer true; corrected.

C4 unit tests: 131 pass (126 + the 5 new). tsc clean.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Note on the red lint check: it is not a circular dependency, despite the workflow's error text.

The step failed while npx was installing madge itself:

npm error code ETARGET
npm error notarget No matching version found for @typescript-eslint/tsconfig-utils@8.67.0.
ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  mermaid@11.16.1 checkCircle: `npx madge --circular ./src`

checkCircle never ran, so the "Circular dependency detected" message is the workflow's generic failure text rather than a real finding. Running the same command on this branch locally:

$ npx madge --circular ./src
Processed 827 files (4.2s)
✔ No circular dependency found!

lint is green on the other open PRs and on develop (14:34 UTC today), so it looks like a transient registry resolution issue in that window. A re-run should clear it — I do not have permission to trigger one, and I would rather not push a filler commit given this repo does not squash-merge.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Merged current develop in - #7874 landed and touches this same file, so this branch was conflicting.

One resolution worth recording, since it is a semantic merge rather than a textual one. Both sides changed getIntersectPoints:

Neither side's version is the answer on its own, so the widening is applied to the rewritten function. drawRels' getC4ShapeObj parameter is widened to match c4Db.getC4Shape, which #7874 changed to fall back to boundaries. The import hunk simply takes both sides.

132 C4 unit tests pass; tsc, eslint and Prettier clean. Merged rather than rebased, and the push is a fast-forward.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Merged develop for the Cypress-to-Playwright migration. This branch adds no e2e specs, so the only conflict was the generated LayoutData typedoc page.

One note in case it recurs elsewhere: autofix.ci had pushed a regeneration of that page computed against the pre-merge base, so its line numbers disagreed with develop's post-merge ones. Develop's are current and autofix re-runs on the push. I did not regenerate locally - a local docs:build strips the GitHub source links, which would have been a much worse thing to commit.

132 C4 unit tests pass.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Gentle nudge - no rush. The requested changes were addressed on 2026-08-20 and all review threads are resolved; the only red check is argos (7 changed snapshots) waiting on a baseline decision. The changed sheets are the relationship-rendering migration this PR is about, so they are expected rather than a regression.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Same ask as #7859: could a maintainer dismiss or refresh the knsv-bot review? reviewDecision is still CHANGES_REQUESTED from 2026-08-10, and the commits addressing it landed 2026-08-20. This one matters more than most, because #8042 is stacked on it - the two together are what actually delete c4Renderer.ts and svgDraw.ts and finish the migration.

All three "things to address" are in, and each is checkable:

review item status
getIntersectPoints comment describes behaviour the code does not have rewritten - the "real outline / silhouette rather than a bounding box" wording is gone; grep returns zero
htmlLabels: false renders markup as literal text fixed - buildEdgeLabel takes the effective setting via getEffectiveHtmlLabels and emits a plain form when HTML labels are off, with unit tests for both branches and a hostile string
no changeset added - .changeset/c4-unified-edges.md

The CSS.supports colour-validation nit is in as well, which then tripped unicorn/better-regex; autofix.ci pushed the corrected pattern and I left that commit rather than spend a screenshot run folding it away.

Branch 01f3e5b99, own diff 5 files (+202/-42). Threads all resolved; only red is argos (7 changed).

Also correcting the PR body: it still says "stacked on #7842", which merged on 2026-07-30. This is standalone against develop now - I will update the description.

@filipsajdak

Copy link
Copy Markdown
Collaborator Author

Correcting one line of my comment above, because it undersells what happened and a maintainer reading it would check the wrong thing.

I wrote that the getIntersectPoints comment was "rewritten" and that the old wording is gone. The wording is gone, but that is not how the finding was resolved. The review offered two options - "either wire up the node.intersect lookup the comment promises, or correct the comment to match reality" - and the first one is what happened: c4Renderer.ts:340 captures c4Shape.intersect = node.intersect from the unified shape that drew each element, and :366 uses it (fromNode.intersect(endPoint)). So arrows really do stop at the person/cylinder silhouette now, and the comment describes what the code does.

The PR description said the same thing all along and is therefore accurate; I have only corrected its stale opening line, which still claimed this was stacked on #7842 (merged 2026-07-30). It is standalone against develop.

Sorry for the double post - better to have the record right than tidy.

@filipsajdak
filipsajdak force-pushed the feature/c4-migrate-edges branch from 01f3e5b to 73b60ba Compare September 18, 2026 07:15
The second slice of the in-place migration, after elements moved in mermaid-js#7842.
Relationships now go through the shared edge renderer: dashed lines, arrowheads,
labels and the offsets and colours `UpdateRelStyle` sets, instead of the legacy
renderer's own line drawing.

Two things the shared path needs that the legacy one did not:

A label is built for the effective `htmlLabels` setting, because `createText`'s
non-markdown branch emits tags as ordinary words - with HTML labels off, markup
rendered as literal characters. Unit tests cover both forms and a hostile
string.

A colour from `UpdateRelStyle` is checked with `CSS.supports('color', ...)`
before it reaches a style string, so a value carrying a `;` cannot corrupt the
declaration it lands in.
@filipsajdak
filipsajdak force-pushed the feature/c4-migrate-edges branch from 73b60ba to 05df8c2 Compare September 18, 2026 19:39
@filipsajdak

Copy link
Copy Markdown
Collaborator Author

The knsv-bot review from 2026-08-10 still shows changes requested. Its three blocking items are addressed on the current head (05df8c234). An author cannot dismiss a bot review, so this needs a maintainer: could someone dismiss review 4898005455 or ask the bot to re-review? @knsv

Review item Status Evidence at 05df8c234
The getIntersectPoints comment described behaviour the code lacks addressed the comment now matches the code: it uses the shape's own intersect and throws if one is missing
htmlLabels: false rendered <b> / <small> as literal text addressed buildEdgeLabel emits a plain <br/>-joined label when HTML labels are off; c4ShapeAdapter.spec.ts covers it
No changeset addressed .changeset/c4-unified-edges.md (minor)
CSS injection through UpdateRelStyle colours addressed asColor validates with CSS.supports('color', ...) before a colour is used
edgeLabels map is never cleared (suggestion) declined ids are unique per render; clearing it means changing the shared module, reasoning in my 2026-08-10 reply
lastElementChild is fragile (suggestion) declined the alternative changes a shared signature, same reply
Tests for htmlLabels: false and special characters unit only c4ShapeAdapter.spec.ts covers both; no e2e fixture

Happy to add the two e2e fixtures if a maintainer wants them in this PR.

This branch has not been deployed

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

Labels

Type: Enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants