From 584a789183f9d998d2392a4de80c2145f8911dcf Mon Sep 17 00:00:00 2001 From: omkarht Date: Mon, 21 Jul 2025 13:33:52 +0530 Subject: [PATCH 01/18] 6638: add support for additional message types for sequence diagram on-behalf-of: @Mermaid-Chart --- .../sequence/parser/sequenceDiagram.jison | 8 ++ .../src/diagrams/sequence/sequenceDb.ts | 4 + .../src/diagrams/sequence/sequenceRenderer.ts | 35 ++++++++- .../mermaid/src/diagrams/sequence/svgDraw.js | 76 +++++++++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) diff --git a/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison b/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison index d2e81df5f..873cee10d 100644 --- a/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison +++ b/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison @@ -88,6 +88,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili ":" return 'TXT'; "+" return '+'; "-" return '-'; +"=>" return 'SOLID_ARROW_TOP'; +"==>" return 'SOLID_ARROW_BOTTOM'; +"=->" return 'STICK_ARROW_TOP'; +"==->" return 'STICK_ARROW_BOTTOM'; <> return 'NEWLINE'; . return 'INVALID'; @@ -313,6 +317,10 @@ signaltype : SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; } | DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; } | SOLID_ARROW { $$ = yy.LINETYPE.SOLID; } + | SOLID_ARROW_TOP { $$ = yy.LINETYPE.SOLID_TOP; } + | SOLID_ARROW_BOTTOM { $$ = yy.LINETYPE.SOLID_BOTTOM; } + | STICK_ARROW_TOP { $$ = yy.LINETYPE.STICK_TOP; } + | STICK_ARROW_BOTTOM { $$ = yy.LINETYPE.STICK_BOTTOM; } | BIDIRECTIONAL_SOLID_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_SOLID; } | DOTTED_ARROW { $$ = yy.LINETYPE.DOTTED; } | BIDIRECTIONAL_DOTTED_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_DOTTED; } diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts index c6b44dac0..9b5868618 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts @@ -62,6 +62,10 @@ const LINETYPE = { PAR_OVER_START: 32, BIDIRECTIONAL_SOLID: 33, BIDIRECTIONAL_DOTTED: 34, + SOLID_TOP: 41, + SOLID_BOTTOM: 42, + STICK_TOP: 43, + STICK_BOTTOM: 44, } as const; const ARROWTYPE = { diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index cfba92b79..c0471273a 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -456,6 +456,20 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO line.attr('stroke-width', 2); line.attr('stroke', 'none'); // handled by theme/css anyway line.style('fill', 'none'); // remove any fill colour + + if (type === diagObj.db.LINETYPE.SOLID_TOP) { + line.attr('marker-end', 'url(' + url + '#solidTopArrowHead)'); + } + if (type === diagObj.db.LINETYPE.SOLID_BOTTOM) { + line.attr('marker-end', 'url(' + url + '#solidBottomArrowHead)'); + } + if (type === diagObj.db.LINETYPE.STICK_TOP) { + line.attr('marker-end', 'url(' + url + '#stickTopArrowHead)'); + } + if (type === diagObj.db.LINETYPE.STICK_BOTTOM) { + line.attr('marker-end', 'url(' + url + '#stickBottomArrowHead)'); + } + if (type === diagObj.db.LINETYPE.SOLID || type === diagObj.db.LINETYPE.DOTTED) { line.attr('marker-end', 'url(' + url + '#arrowhead)'); } @@ -824,6 +838,10 @@ export const draw = async function (_text: string, id: string, _version: string, svgDraw.insertArrowCrossHead(diagram); svgDraw.insertArrowFilledHead(diagram); svgDraw.insertSequenceNumber(diagram); + svgDraw.insertSolidTopArrowHead(diagram); + svgDraw.insertSolidBottomArrowHead(diagram); + svgDraw.insertStickTopArrowHead(diagram); + svgDraw.insertStickBottomArrowHead(diagram); /** * @param msg - The message to draw. @@ -1035,6 +1053,10 @@ export const draw = async function (_text: string, id: string, _version: string, diagObj.db.LINETYPE.SOLID_OPEN, diagObj.db.LINETYPE.DOTTED_OPEN, diagObj.db.LINETYPE.SOLID, + diagObj.db.LINETYPE.SOLID_TOP, + diagObj.db.LINETYPE.SOLID_BOTTOM, + diagObj.db.LINETYPE.STICK_TOP, + diagObj.db.LINETYPE.STICK_BOTTOM, diagObj.db.LINETYPE.DOTTED, diagObj.db.LINETYPE.SOLID_CROSS, diagObj.db.LINETYPE.DOTTED_CROSS, @@ -1430,6 +1452,10 @@ const buildMessageModel = function (msg, actors, diagObj) { diagObj.db.LINETYPE.SOLID_OPEN, diagObj.db.LINETYPE.DOTTED_OPEN, diagObj.db.LINETYPE.SOLID, + diagObj.db.LINETYPE.SOLID_TOP, + diagObj.db.LINETYPE.SOLID_BOTTOM, + diagObj.db.LINETYPE.STICK_TOP, + diagObj.db.LINETYPE.STICK_BOTTOM, diagObj.db.LINETYPE.DOTTED, diagObj.db.LINETYPE.SOLID_CROSS, diagObj.db.LINETYPE.DOTTED_CROSS, @@ -1480,7 +1506,14 @@ const buildMessageModel = function (msg, actors, diagObj) { * Shorten the length of arrow at the end and move the marker forward (using refX) to have a clean arrowhead * This is not required for open arrows that don't have arrowheads */ - if (![diagObj.db.LINETYPE.SOLID_OPEN, diagObj.db.LINETYPE.DOTTED_OPEN].includes(msg.type)) { + if ( + ![ + diagObj.db.LINETYPE.SOLID_OPEN, + diagObj.db.LINETYPE.DOTTED_OPEN, + diagObj.db.LINETYPE.STICK_TOP, + diagObj.db.LINETYPE.STICK_BOTTOM, + ].includes(msg.type) + ) { stopx += adjustValue(3); } diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index 04ccd8a84..32f268ea4 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -1099,6 +1099,77 @@ const _drawMenuItemTextCandidateFunc = (function () { }; })(); +/** + * Setup arrow head and define the marker. The result is appended to the svg. + * + * @param elem + */ +export const insertSolidTopArrowHead = function (elem) { + elem + .append('defs') + .append('marker') + .attr('id', 'solidTopArrowHead') + .attr('refX', 7.9) + .attr('refY', 5.25) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 12) + .attr('markerHeight', 12) + .attr('orient', 'auto-start-reverse') + .append('path') + .attr('d', 'M 0 -0.25 L 10 6 L 0 6 z'); // this is actual shape for arrowhead +}; + +export const insertSolidBottomArrowHead = function (elem) { + elem + .append('defs') + .append('marker') + .attr('id', 'solidBottomArrowHead') + .attr('refX', 7.9) + .attr('refY', 4.7) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 12) + .attr('markerHeight', 12) + .attr('orient', 'auto-start-reverse') + .append('path') + .attr('d', 'M 0.1 10 L 10 4 L 0 4 z'); +}; + +export const insertStickTopArrowHead = function (elem) { + elem + .append('defs') + .append('marker') + .attr('id', ' stickTopArrowHead') + .attr('refX', 10.5) + .attr('refY', 10) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 12) + .attr('markerHeight', 12) + .attr('orient', 'auto-start-reverse') + .append('path') + .attr('d', 'M 0 0 L 10 10') + .attr('stroke', 'black') + .attr('stroke-width', 1.5) + .attr('fill', 'none'); +}; + +export const insertStickBottomArrowHead = function (elem) { + elem + .append('defs') + .append('marker') + .attr('id', ' stickBottomArrowHead') + .attr('refX', 10) + .attr('refY', 5.25) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 12) + .attr('markerHeight', 12) + .attr('orient', 'auto-start-reverse') + .append('path') + .attr('d', 'M 10 5 L 0 10') // Diagonal line down from (10,5) to (0,10) + .attr('stroke', 'black') + .attr('stroke-width', 1.5) + .attr('fill', 'none'); +}; + export default { drawRect, drawText, @@ -1121,4 +1192,9 @@ export default { getNoteRect, fixLifeLineHeights, sanitizeUrl, + + insertSolidTopArrowHead, + insertSolidBottomArrowHead, + insertStickTopArrowHead, + insertStickBottomArrowHead, }; From fda640c90c892abfab061db4d605bcee59ebbe77 Mon Sep 17 00:00:00 2001 From: omkarht Date: Mon, 21 Jul 2025 19:03:29 +0530 Subject: [PATCH 02/18] fix: adjusted arrowhead design on-behalf-of: @Mermaid-Chart --- .../mermaid/src/diagrams/sequence/svgDraw.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index 32f268ea4..4d2a574d1 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -1110,13 +1110,13 @@ export const insertSolidTopArrowHead = function (elem) { .append('marker') .attr('id', 'solidTopArrowHead') .attr('refX', 7.9) - .attr('refY', 5.25) + .attr('refY', 7.25) .attr('markerUnits', 'userSpaceOnUse') .attr('markerWidth', 12) .attr('markerHeight', 12) .attr('orient', 'auto-start-reverse') .append('path') - .attr('d', 'M 0 -0.25 L 10 6 L 0 6 z'); // this is actual shape for arrowhead + .attr('d', 'M 0 0 L 10 8 L 0 8 z'); // this is actual shape for arrowhead }; export const insertSolidBottomArrowHead = function (elem) { @@ -1125,13 +1125,13 @@ export const insertSolidBottomArrowHead = function (elem) { .append('marker') .attr('id', 'solidBottomArrowHead') .attr('refX', 7.9) - .attr('refY', 4.7) + .attr('refY', 0.75) .attr('markerUnits', 'userSpaceOnUse') .attr('markerWidth', 12) .attr('markerHeight', 12) .attr('orient', 'auto-start-reverse') .append('path') - .attr('d', 'M 0.1 10 L 10 4 L 0 4 z'); + .attr('d', 'M 0 0 L 10 0 L 0 8 z'); }; export const insertStickTopArrowHead = function (elem) { @@ -1139,14 +1139,14 @@ export const insertStickTopArrowHead = function (elem) { .append('defs') .append('marker') .attr('id', ' stickTopArrowHead') - .attr('refX', 10.5) - .attr('refY', 10) + .attr('refX', 7.5) + .attr('refY', 7) .attr('markerUnits', 'userSpaceOnUse') .attr('markerWidth', 12) .attr('markerHeight', 12) .attr('orient', 'auto-start-reverse') .append('path') - .attr('d', 'M 0 0 L 10 10') + .attr('d', 'M 0 0 L 7 7') .attr('stroke', 'black') .attr('stroke-width', 1.5) .attr('fill', 'none'); @@ -1157,14 +1157,14 @@ export const insertStickBottomArrowHead = function (elem) { .append('defs') .append('marker') .attr('id', ' stickBottomArrowHead') - .attr('refX', 10) - .attr('refY', 5.25) + .attr('refX', 7.5) + .attr('refY', 0) .attr('markerUnits', 'userSpaceOnUse') .attr('markerWidth', 12) .attr('markerHeight', 12) .attr('orient', 'auto-start-reverse') .append('path') - .attr('d', 'M 10 5 L 0 10') // Diagonal line down from (10,5) to (0,10) + .attr('d', 'M 0 7 L 7 0') .attr('stroke', 'black') .attr('stroke-width', 1.5) .attr('fill', 'none'); From 6b9c15d7f0b87bd67e7a9d8aea42e5b384176a1d Mon Sep 17 00:00:00 2001 From: omkarht Date: Tue, 22 Jul 2025 16:30:21 +0530 Subject: [PATCH 03/18] added reverse arrow head types on-behalf-of: @Mermaid-Chart --- .../src/diagrams/sequence/sequenceDb.ts | 4 ++++ .../src/diagrams/sequence/sequenceRenderer.ts | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts index 9b5868618..0112c5352 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts @@ -66,6 +66,10 @@ const LINETYPE = { SOLID_BOTTOM: 42, STICK_TOP: 43, STICK_BOTTOM: 44, + SOLID_ARROW_TOP_REVERSE: 45, + SOLID_ARROW_BOTTOM_REVERSE: 46, + STICK_ARROW_TOP_REVERSE: 47, + STICK_ARROW_BOTTOM_REVERSE: 48, } as const; const ARROWTYPE = { diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index c0471273a..6b9ecc202 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -470,6 +470,19 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO line.attr('marker-end', 'url(' + url + '#stickBottomArrowHead)'); } + if (type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE) { + line.attr('marker-start', 'url(' + url + '#solidBottomArrowHead)'); + } + if (type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE) { + line.attr('marker-start', 'url(' + url + '#solidTopArrowHead)'); + } + if (type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE) { + line.attr('marker-start', 'url(' + url + '#stickBottomArrowHead)'); + } + if (type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE) { + line.attr('marker-start', 'url(' + url + '#stickTopArrowHead)'); + } + if (type === diagObj.db.LINETYPE.SOLID || type === diagObj.db.LINETYPE.DOTTED) { line.attr('marker-end', 'url(' + url + '#arrowhead)'); } @@ -1057,6 +1070,10 @@ export const draw = async function (_text: string, id: string, _version: string, diagObj.db.LINETYPE.SOLID_BOTTOM, diagObj.db.LINETYPE.STICK_TOP, diagObj.db.LINETYPE.STICK_BOTTOM, + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE, + diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE, + diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE, + diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE, diagObj.db.LINETYPE.DOTTED, diagObj.db.LINETYPE.SOLID_CROSS, diagObj.db.LINETYPE.DOTTED_CROSS, @@ -1456,6 +1473,10 @@ const buildMessageModel = function (msg, actors, diagObj) { diagObj.db.LINETYPE.SOLID_BOTTOM, diagObj.db.LINETYPE.STICK_TOP, diagObj.db.LINETYPE.STICK_BOTTOM, + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE, + diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE, + diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE, + diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE, diagObj.db.LINETYPE.DOTTED, diagObj.db.LINETYPE.SOLID_CROSS, diagObj.db.LINETYPE.DOTTED_CROSS, @@ -1512,6 +1533,8 @@ const buildMessageModel = function (msg, actors, diagObj) { diagObj.db.LINETYPE.DOTTED_OPEN, diagObj.db.LINETYPE.STICK_TOP, diagObj.db.LINETYPE.STICK_BOTTOM, + diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE, + diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE, ].includes(msg.type) ) { stopx += adjustValue(3); From 252b1837f70bc10711beba34fc49f7687bae8662 Mon Sep 17 00:00:00 2001 From: omkarht Date: Tue, 22 Jul 2025 19:20:06 +0530 Subject: [PATCH 04/18] added arrowhead types for dotted line type on-behalf-of: @Mermaid-Chart --- .../src/diagrams/sequence/sequenceDb.ts | 12 +++ .../src/diagrams/sequence/sequenceRenderer.ts | 95 ++++++++++++++++--- 2 files changed, 95 insertions(+), 12 deletions(-) diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts index 0112c5352..ff1dea393 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts @@ -62,14 +62,26 @@ const LINETYPE = { PAR_OVER_START: 32, BIDIRECTIONAL_SOLID: 33, BIDIRECTIONAL_DOTTED: 34, + SOLID_TOP: 41, SOLID_BOTTOM: 42, STICK_TOP: 43, STICK_BOTTOM: 44, + SOLID_ARROW_TOP_REVERSE: 45, SOLID_ARROW_BOTTOM_REVERSE: 46, STICK_ARROW_TOP_REVERSE: 47, STICK_ARROW_BOTTOM_REVERSE: 48, + + SOLID_TOP_DOTTED: 51, + SOLID_BOTTOM_DOTTED: 52, + STICK_TOP_DOTTED: 53, + STICK_BOTTOM_DOTTED: 54, + + SOLID_ARROW_TOP_REVERSE_DOTTED: 55, + SOLID_ARROW_BOTTOM_REVERSE_DOTTED: 56, + STICK_ARROW_TOP_REVERSE_DOTTED: 57, + STICK_ARROW_BOTTOM_REVERSE_DOTTED: 58, } as const; const ARROWTYPE = { diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index 6b9ecc202..d902cac97 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -440,7 +440,15 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO type === diagObj.db.LINETYPE.DOTTED_CROSS || type === diagObj.db.LINETYPE.DOTTED_POINT || type === diagObj.db.LINETYPE.DOTTED_OPEN || - type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED + type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED || + type === diagObj.db.LINETYPE.SOLID_TOP_DOTTED || + type === diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED || + type === diagObj.db.LINETYPE.STICK_TOP_DOTTED || + type === diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED || + type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED || + type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED || + type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED || + type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED ) { line.style('stroke-dasharray', '3, 3'); line.attr('class', 'messageLine1'); @@ -457,29 +465,47 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO line.attr('stroke', 'none'); // handled by theme/css anyway line.style('fill', 'none'); // remove any fill colour - if (type === diagObj.db.LINETYPE.SOLID_TOP) { + if (type === diagObj.db.LINETYPE.SOLID_TOP || type === diagObj.db.LINETYPE.SOLID_TOP_DOTTED) { line.attr('marker-end', 'url(' + url + '#solidTopArrowHead)'); } - if (type === diagObj.db.LINETYPE.SOLID_BOTTOM) { + if ( + type === diagObj.db.LINETYPE.SOLID_BOTTOM || + type === diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED + ) { line.attr('marker-end', 'url(' + url + '#solidBottomArrowHead)'); } - if (type === diagObj.db.LINETYPE.STICK_TOP) { + if (type === diagObj.db.LINETYPE.STICK_TOP || type === diagObj.db.LINETYPE.STICK_TOP_DOTTED) { line.attr('marker-end', 'url(' + url + '#stickTopArrowHead)'); } - if (type === diagObj.db.LINETYPE.STICK_BOTTOM) { + if ( + type === diagObj.db.LINETYPE.STICK_BOTTOM || + type === diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED + ) { line.attr('marker-end', 'url(' + url + '#stickBottomArrowHead)'); } - if (type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE) { + if ( + type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE || + type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED + ) { line.attr('marker-start', 'url(' + url + '#solidBottomArrowHead)'); } - if (type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE) { + if ( + type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE || + type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED + ) { line.attr('marker-start', 'url(' + url + '#solidTopArrowHead)'); } - if (type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE) { + if ( + type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE || + type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED + ) { line.attr('marker-start', 'url(' + url + '#stickBottomArrowHead)'); } - if (type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE) { + if ( + type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE || + type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED + ) { line.attr('marker-start', 'url(' + url + '#stickTopArrowHead)'); } @@ -1066,14 +1092,27 @@ export const draw = async function (_text: string, id: string, _version: string, diagObj.db.LINETYPE.SOLID_OPEN, diagObj.db.LINETYPE.DOTTED_OPEN, diagObj.db.LINETYPE.SOLID, + diagObj.db.LINETYPE.SOLID_TOP, diagObj.db.LINETYPE.SOLID_BOTTOM, diagObj.db.LINETYPE.STICK_TOP, diagObj.db.LINETYPE.STICK_BOTTOM, + + diagObj.db.LINETYPE.SOLID_TOP_DOTTED, + diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED, + diagObj.db.LINETYPE.STICK_TOP_DOTTED, + diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED, + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE, diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE, diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE, diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE, + + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED, + diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED, + diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED, + diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED, + diagObj.db.LINETYPE.DOTTED, diagObj.db.LINETYPE.SOLID_CROSS, diagObj.db.LINETYPE.DOTTED_CROSS, @@ -1469,14 +1508,27 @@ const buildMessageModel = function (msg, actors, diagObj) { diagObj.db.LINETYPE.SOLID_OPEN, diagObj.db.LINETYPE.DOTTED_OPEN, diagObj.db.LINETYPE.SOLID, + diagObj.db.LINETYPE.SOLID_TOP, diagObj.db.LINETYPE.SOLID_BOTTOM, diagObj.db.LINETYPE.STICK_TOP, diagObj.db.LINETYPE.STICK_BOTTOM, + + diagObj.db.LINETYPE.SOLID_TOP_DOTTED, + diagObj.db.LINETYPE.SOLID_BOTTOM_DOTTED, + diagObj.db.LINETYPE.STICK_TOP_DOTTED, + diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED, + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE, diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE, diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE, diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE, + + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED, + diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED, + diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED, + diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED, + diagObj.db.LINETYPE.DOTTED, diagObj.db.LINETYPE.SOLID_CROSS, diagObj.db.LINETYPE.DOTTED_CROSS, @@ -1531,10 +1583,24 @@ const buildMessageModel = function (msg, actors, diagObj) { ![ diagObj.db.LINETYPE.SOLID_OPEN, diagObj.db.LINETYPE.DOTTED_OPEN, + diagObj.db.LINETYPE.STICK_TOP, diagObj.db.LINETYPE.STICK_BOTTOM, + + diagObj.db.LINETYPE.STICK_TOP_DOTTED, + diagObj.db.LINETYPE.STICK_BOTTOM_DOTTED, + + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED, + diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED, + diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE, diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE, + + diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED, + diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED, + + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE, + diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE, ].includes(msg.type) ) { stopx += adjustValue(3); @@ -1544,9 +1610,14 @@ const buildMessageModel = function (msg, actors, diagObj) { * Shorten start position of bidirectional arrow to accommodate for second arrowhead */ if ( - [diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID, diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED].includes( - msg.type - ) + [ + diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID, + diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED, + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED, + diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED, + diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE, + diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE, + ].includes(msg.type) ) { startx -= adjustValue(3); } From ba9db26bfad60e46a15e516be1d23f13dcac7c7d Mon Sep 17 00:00:00 2001 From: omkarht Date: Tue, 22 Jul 2025 19:25:27 +0530 Subject: [PATCH 05/18] Lexing : Added Support for new arrow types through lexing on-behalf-of: @Mermaid-Chart --- .../sequence/parser/sequenceDiagram.jison | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison b/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison index 873cee10d..2685a5d50 100644 --- a/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison +++ b/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison @@ -73,7 +73,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili "off" return 'off'; "," return ','; ";" return 'NEWLINE'; -[^\+\<->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)))[\-]*[^\+\<->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; } +[^\/\\\+\<->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)|\-\|\\|\-\\|\-\/|\-\/\/|\-\|\/|\/\|\-|\\\|\-|\/\/\-|\\\\\-|\/\|\-|\-\-\|\\|\-\-))[\-]*[^\+\<->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; } //final_4.11 "->>" return 'SOLID_ARROW'; "<<->>" return 'BIDIRECTIONAL_SOLID_ARROW'; "-->>" return 'DOTTED_ARROW'; @@ -84,14 +84,35 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili \-\-[x] return 'DOTTED_CROSS'; \-[\)] return 'SOLID_POINT'; \-\-[\)] return 'DOTTED_POINT'; + +//normal-dotted +\-\-\|\\ return 'SOLID_ARROW_TOP_DOTTED'; +\-\-\|\/ return 'SOLID_ARROW_BOTTOM_DOTTED'; +\-\-\\\\ return 'STICK_ARROW_TOP_DOTTED'; +\-\-\/\/ return 'STICK_ARROW_BOTTOM_DOTTED'; + +//reverse-dotted +\/\|\-\- return 'SOLID_ARROW_TOP_REVERSE_DOTTED'; +\\\|\-\- return 'SOLID_ARROW_BOTTOM_REVERSE_DOTTED'; +\/\/\-\- return 'STICK_ARROW_TOP_REVERSE_DOTTED'; +\\\\\-\- return 'STICK_ARROW_BOTTOM_REVERSE_DOTTED'; + +//normal +\-\|\\ return 'SOLID_ARROW_TOP'; +\-\|\/ return 'SOLID_ARROW_BOTTOM'; +\-\\\\ return 'STICK_ARROW_TOP'; +\-\/\/ return 'STICK_ARROW_BOTTOM'; + +//reverse +\/\|\- return 'SOLID_ARROW_TOP_REVERSE'; +\\\|\- return 'SOLID_ARROW_BOTTOM_REVERSE'; +\/\/\- return 'STICK_ARROW_TOP_REVERSE'; +\\\\\- return 'STICK_ARROW_BOTTOM_REVERSE'; + ":"(?:(?:no)?wrap:)?[^#\n;]* return 'TXT'; ":" return 'TXT'; "+" return '+'; "-" return '-'; -"=>" return 'SOLID_ARROW_TOP'; -"==>" return 'SOLID_ARROW_BOTTOM'; -"=->" return 'STICK_ARROW_TOP'; -"==->" return 'STICK_ARROW_BOTTOM'; <> return 'NEWLINE'; . return 'INVALID'; @@ -317,10 +338,27 @@ signaltype : SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; } | DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; } | SOLID_ARROW { $$ = yy.LINETYPE.SOLID; } + | SOLID_ARROW_TOP { $$ = yy.LINETYPE.SOLID_TOP; } | SOLID_ARROW_BOTTOM { $$ = yy.LINETYPE.SOLID_BOTTOM; } | STICK_ARROW_TOP { $$ = yy.LINETYPE.STICK_TOP; } | STICK_ARROW_BOTTOM { $$ = yy.LINETYPE.STICK_BOTTOM; } + + | SOLID_ARROW_TOP_DOTTED { $$ = yy.LINETYPE.SOLID_TOP_DOTTED; } + | SOLID_ARROW_BOTTOM_DOTTED { $$ = yy.LINETYPE.SOLID_BOTTOM_DOTTED; } + | STICK_ARROW_TOP_DOTTED { $$ = yy.LINETYPE.STICK_TOP_DOTTED; } + | STICK_ARROW_BOTTOM_DOTTED { $$ = yy.LINETYPE.STICK_BOTTOM_DOTTED; } + + | SOLID_ARROW_TOP_REVERSE { $$ = yy.LINETYPE.SOLID_ARROW_TOP_REVERSE; } + | SOLID_ARROW_BOTTOM_REVERSE { $$ = yy.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE; } + | STICK_ARROW_TOP_REVERSE { $$ = yy.LINETYPE.STICK_ARROW_TOP_REVERSE; } + | STICK_ARROW_BOTTOM_REVERSE { $$ = yy.LINETYPE.STICK_ARROW_BOTTOM_REVERSE; } + + | SOLID_ARROW_TOP_REVERSE_DOTTED { $$ = yy.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED; } + | SOLID_ARROW_BOTTOM_REVERSE_DOTTED { $$ = yy.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED; } + | STICK_ARROW_TOP_REVERSE_DOTTED { $$ = yy.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED; } + | STICK_ARROW_BOTTOM_REVERSE_DOTTED { $$ = yy.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED; } + | BIDIRECTIONAL_SOLID_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_SOLID; } | DOTTED_ARROW { $$ = yy.LINETYPE.DOTTED; } | BIDIRECTIONAL_DOTTED_ARROW { $$ = yy.LINETYPE.BIDIRECTIONAL_DOTTED; } From cb4ed605b294bd83fe812084ac02f401d41e482c Mon Sep 17 00:00:00 2001 From: omkarht Date: Wed, 23 Jul 2025 16:59:04 +0530 Subject: [PATCH 06/18] chore: added rendering test cases for arrow head type on-behalf-of: @Mermaid-Chart --- .../rendering/sequencediagram.spec.js | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js index f18e99abf..c47bd7075 100644 --- a/cypress/integration/rendering/sequencediagram.spec.js +++ b/cypress/integration/rendering/sequencediagram.spec.js @@ -1042,4 +1042,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` + ); + }); + }); }); From e8d6daf4f637aa798c08b4aa48296a0c129a8195 Mon Sep 17 00:00:00 2001 From: omkarht Date: Mon, 28 Jul 2025 15:37:33 +0530 Subject: [PATCH 07/18] add support for central connection circle after arrow in lifeline direction on-behalf-of: @Mermaid-Chart --- .../sequence/parser/sequenceDiagram.jison | 17 ++++++- .../src/diagrams/sequence/sequenceDb.ts | 23 ++++++++- .../src/diagrams/sequence/sequenceRenderer.ts | 51 +++++++++++++++++-- .../mermaid/src/diagrams/sequence/types.ts | 3 ++ 4 files changed, 88 insertions(+), 6 deletions(-) diff --git a/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison b/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison index 2685a5d50..696b39563 100644 --- a/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison +++ b/packages/mermaid/src/diagrams/sequence/parser/sequenceDiagram.jison @@ -73,7 +73,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili "off" return 'off'; "," return ','; ";" return 'NEWLINE'; -[^\/\\\+\<->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)|\-\|\\|\-\\|\-\/|\-\/\/|\-\|\/|\/\|\-|\\\|\-|\/\/\-|\\\\\-|\/\|\-|\-\-\|\\|\-\-))[\-]*[^\+\<->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; } //final_4.11 +[^\/\\\+\()\<->\->:\n,;]+((?!(\-x|\-\-x|\-\)|\-\-\)|\-\|\\|\-\\|\-\/|\-\/\/|\-\|\/|\/\|\-|\\\|\-|\/\/\-|\\\\\-|\/\|\-|\-\-\|\\|\-\-|\(\)))[\-]*[^\+\<->\->:\n,;]+)* { yytext = yytext.trim(); return 'ACTOR'; } //final_4.11 "->>" return 'SOLID_ARROW'; "<<->>" return 'BIDIRECTIONAL_SOLID_ARROW'; "-->>" return 'DOTTED_ARROW'; @@ -113,6 +113,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili ":" return 'TXT'; "+" return '+'; "-" return '-'; +"()" return '()'; <> return 'NEWLINE'; . return 'INVALID'; @@ -322,6 +323,20 @@ signal { $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5}, {type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $1.actor} ]} + | actor signaltype '()' actor text2 + { $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5, activate: true, centralConnection: yy.LINETYPE.CENTRAL_CONNECTION}, + {type: 'centralConnection', signalType: yy.LINETYPE.CENTRAL_CONNECTION, actor: $4.actor, } + ]} + + | actor '()' signaltype actor text2 + { $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$3, msg:$5, activate: false, centralConnection: yy.LINETYPE.CENTRAL_CONNECTION_REVERSE}, + {type: 'centralConnectionReverse', signalType: yy.LINETYPE.CENTRAL_CONNECTION_REVERSE, actor: $1.actor} + ]} + | actor '()' signaltype '()' actor text2 + { $$ = [$1,$5,{type: 'addMessage', from:$1.actor, to:$5.actor, signalType:$3, msg:$6, activate: true, centralConnection: yy.LINETYPE.CENTRAL_CONNECTION_DUAL}, + {type: 'centralConnection', signalType: yy.LINETYPE.CENTRAL_CONNECTION, actor: $5.actor, }, + {type: 'centralConnectionReverse', signalType: yy.LINETYPE.CENTRAL_CONNECTION_REVERSE, actor: $1.actor} + ]} | actor signaltype actor text2 { $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]} ; diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts index ff1dea393..671f3038e 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts @@ -82,6 +82,10 @@ const LINETYPE = { SOLID_ARROW_BOTTOM_REVERSE_DOTTED: 56, STICK_ARROW_TOP_REVERSE_DOTTED: 57, STICK_ARROW_BOTTOM_REVERSE_DOTTED: 58, + + CENTRAL_CONNECTION: 59, + CENTRAL_CONNECTION_REVERSE: 60, + CENTRAL_CONNECTION_DUAL: 61, } as const; const ARROWTYPE = { @@ -238,7 +242,8 @@ export class SequenceDB implements DiagramDB { idTo?: Message['to'], message?: { text: string; wrap: boolean }, messageType?: number, - activate = false + activate = false, + centralConnection?: number ) { if (messageType === this.LINETYPE.ACTIVE_END) { const cnt = this.activationCount(idFrom ?? ''); @@ -265,6 +270,7 @@ export class SequenceDB implements DiagramDB { wrap: message?.wrap ?? this.autoWrap(), type: messageType, activate, + centralConnection, }); return true; } @@ -557,6 +563,12 @@ export class SequenceDB implements DiagramDB { case 'activeStart': this.addSignal(param.actor, undefined, undefined, param.signalType); break; + case 'centralConnection': + this.addSignal(param.actor, undefined, undefined, param.signalType); + break; + case 'centralConnectionReverse': + this.addSignal(param.actor, undefined, undefined, param.signalType); + break; case 'activeEnd': this.addSignal(param.actor, undefined, undefined, param.signalType); break; @@ -600,7 +612,14 @@ export class SequenceDB implements DiagramDB { this.state.records.lastDestroyed = undefined; } } - this.addSignal(param.from, param.to, param.msg, param.signalType, param.activate); + this.addSignal( + param.from, + param.to, + param.msg, + param.signalType, + param.activate, + param.centralConnection + ); break; case 'boxStart': this.addBox(param.boxData); diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index d902cac97..9c73b9f2a 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -281,6 +281,36 @@ const drawNote = async function (elem: any, noteModel: NoteModel) { bounds.models.addNote(noteModel); }; +const drawCentralConnection = function ( + elem: any, + msg: any, + msgModel: any, + diagObj: Diagram, + startx, + stopx, + lineStartY +) { + const g = elem.append('g'); + const circle = g.append('circle'); + circle.attr( + 'cx', + msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION ? stopx + 7.5 : startx - 5 + ); + // circle.attr('cx',x); + circle.attr('cy', lineStartY); + circle.attr('r', 5); + circle.attr('width', 10); + circle.attr('height', 10); + if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_DUAL) { + const circle = g.append('circle'); + circle.attr('cx', stopx + 7.5); + circle.attr('cy', lineStartY); + circle.attr('r', 5); + circle.attr('width', 10); + circle.attr('height', 10); + } +}; + const messageFont = (cnf) => { return { fontFamily: cnf.messageFontFamily, @@ -366,7 +396,7 @@ async function boundMessage(_diagram, msgModel): Promise { * @param lineStartY - The Y coordinate at which the message line starts * @param diagObj - The diagram object. */ -const drawMessage = async function (diagram, msgModel, lineStartY: number, diagObj: Diagram) { +const drawMessage = async function (diagram, msgModel, lineStartY: number, diagObj: Diagram, msg) { const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel; const textDims = utils.calculateTextDimensions(message, messageFont(conf)); const textObj = svgDrawCommon.getTextObj(); @@ -432,6 +462,9 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO line.attr('y1', lineStartY); line.attr('x2', stopx); line.attr('y2', lineStartY); + if (msg.centralConnection) { + drawCentralConnection(diagram, msg, msgModel, diagObj, startx, stopx, lineStartY); + } } // Make an SVG Container // Draw the line @@ -921,6 +954,12 @@ export const draw = async function (_text: string, id: string, _version: string, case diagObj.db.LINETYPE.ACTIVE_START: bounds.newActivation(msg, diagram, actors); break; + case diagObj.db.LINETYPE.CENTRAL_CONNECTION: + bounds.newActivation(msg, diagram, actors); + break; + case diagObj.db.LINETYPE.CENTRAL_CONNECTION_REVERSE: + bounds.newActivation(msg, diagram, actors); + break; case diagObj.db.LINETYPE.ACTIVE_END: activeEnd(msg, bounds.getVerticalPos()); break; @@ -1079,7 +1118,7 @@ export const draw = async function (_text: string, id: string, _version: string, createdActors, destroyedActors ); - messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY }); + messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY, msg }); bounds.models.addMessage(msgModel); } catch (e) { log.error('error while drawing message', e); @@ -1132,7 +1171,7 @@ export const draw = async function (_text: string, id: string, _version: string, await drawActors(diagram, actors, actorKeys, false); for (const e of messagesToDraw) { - await drawMessage(diagram, e.messageModel, e.lineStartY, diagObj); + await drawMessage(diagram, e.messageModel, e.lineStartY, diagObj, e.msg); } if (conf.mirrorActors) { await drawActors(diagram, actors, actorKeys, true); @@ -1546,6 +1585,12 @@ const buildMessageModel = function (msg, actors, diagObj) { let startx = isArrowToRight ? fromRight : fromLeft; let stopx = isArrowToRight ? toLeft : toRight; + if ( + msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_REVERSE || + msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_DUAL + ) { + startx += 4; + } // As the line width is considered, the left and right values will be off by 2. const isArrowToActivation = Math.abs(toLeft - toRight) > 2; diff --git a/packages/mermaid/src/diagrams/sequence/types.ts b/packages/mermaid/src/diagrams/sequence/types.ts index 7cf2ead9c..c25463ac6 100644 --- a/packages/mermaid/src/diagrams/sequence/types.ts +++ b/packages/mermaid/src/diagrams/sequence/types.ts @@ -35,6 +35,7 @@ export interface Message { type?: number; activate?: boolean; placement?: string; + centralConnection?: number; } export interface AddMessageParams { @@ -50,6 +51,8 @@ export interface AddMessageParams { | 'destroyParticipant' | 'activeStart' | 'activeEnd' + | 'centralConnection' + | 'centralConnectionReverse' | 'addNote' | 'addLinks' | 'addALink' From 0e40d8e8a80e79ff3ba438acbb329973fccde125 Mon Sep 17 00:00:00 2001 From: omkarht Date: Mon, 28 Jul 2025 15:55:07 +0530 Subject: [PATCH 08/18] fix: fixed failing test cases on-behalf-of: @Mermaid-Chart --- .../diagrams/sequence/sequenceDiagram.spec.js | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js index c3b8c2b4a..776201f62 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js +++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js @@ -104,6 +104,7 @@ describe('more than one sequence diagram', () => { [ { "activate": false, + "centralConnection": 0, "from": "Alice", "id": "0", "message": "Hello Bob, how are you?", @@ -113,6 +114,7 @@ describe('more than one sequence diagram', () => { }, { "activate": false, + "centralConnection": 0, "from": "Bob", "id": "1", "message": "I am good thanks!", @@ -131,6 +133,7 @@ describe('more than one sequence diagram', () => { [ { "activate": false, + "centralConnection": 0, "from": "Alice", "id": "0", "message": "Hello Bob, how are you?", @@ -140,6 +143,7 @@ describe('more than one sequence diagram', () => { }, { "activate": false, + "centralConnection": 0, "from": "Bob", "id": "1", "message": "I am good thanks!", @@ -160,6 +164,7 @@ describe('more than one sequence diagram', () => { [ { "activate": false, + "centralConnection": 0, "from": "Alice", "id": "0", "message": "Hello John, how are you?", @@ -169,6 +174,7 @@ describe('more than one sequence diagram', () => { }, { "activate": false, + "centralConnection": 0, "from": "John", "id": "1", "message": "I am good thanks!", @@ -2038,4 +2044,34 @@ Bob->>Alice:Got it! expect(messages[0].from).toBe('Alice'); expect(messages[0].to).toBe('Bob'); }); + + it('1 should parse ', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + actor Bob + actor Alice + Bob -|\\ Alice: Hello Alice, how are you? + Bob -|/ Alice: Hello Alice, how are you? + Bob -// Alice: Hello Alice, how are you? + Bob -\\\\ Alice: Hello Alice, how are you? + + Bob \\|- Alice: Hello Alice, how are you? + Bob /|- Alice: Hello Alice, how are you? + Bob //- Alice: Hello Alice, how are you? + Bob \\\\- Alice: Hello Alice, how are you? + `); + + const messages = diagram.db.getMessages(); + }); + + it('2 should parse ', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + actor Bob + actor Alice + Alice ()<<->>() Bob: hey? + `); + + const messages = diagram.db.getMessages(); + }); }); From 62faacdeebbd9980f53bf103ac3531b1f52d4a08 Mon Sep 17 00:00:00 2001 From: omkarht Date: Mon, 28 Jul 2025 16:01:43 +0530 Subject: [PATCH 09/18] fix: fixed failing test cases on-behalf-of: @Mermaid-Chart --- packages/mermaid/src/diagrams/sequence/sequenceDb.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts index 671f3038e..0d411a7f5 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts @@ -270,7 +270,7 @@ export class SequenceDB implements DiagramDB { wrap: message?.wrap ?? this.autoWrap(), type: messageType, activate, - centralConnection, + centralConnection: centralConnection ?? 0, }); return true; } From 7ca066576471dd4b17ab7f986f85e882a124bae9 Mon Sep 17 00:00:00 2001 From: omkarht Date: Mon, 28 Jul 2025 19:13:51 +0530 Subject: [PATCH 10/18] fix: fixed failing test case on-behalf-of: @Mermaid-Chart --- packages/mermaid/src/mermaid.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mermaid/src/mermaid.spec.ts b/packages/mermaid/src/mermaid.spec.ts index 586d3605c..5a7551683 100644 --- a/packages/mermaid/src/mermaid.spec.ts +++ b/packages/mermaid/src/mermaid.spec.ts @@ -207,7 +207,7 @@ describe('when using mermaid and ', () => { [Error: Parse error on line 2: ...equenceDiagramAlice:->Bob: Hello Bob, h... ----------------------^ - Expecting 'SOLID_OPEN_ARROW', 'DOTTED_OPEN_ARROW', 'SOLID_ARROW', 'BIDIRECTIONAL_SOLID_ARROW', 'DOTTED_ARROW', 'BIDIRECTIONAL_DOTTED_ARROW', 'SOLID_CROSS', 'DOTTED_CROSS', 'SOLID_POINT', 'DOTTED_POINT', got 'TXT'] + Expecting '()', 'SOLID_OPEN_ARROW', 'DOTTED_OPEN_ARROW', 'SOLID_ARROW', 'SOLID_ARROW_TOP', 'SOLID_ARROW_BOTTOM', 'STICK_ARROW_TOP', 'STICK_ARROW_BOTTOM', 'SOLID_ARROW_TOP_DOTTED', 'SOLID_ARROW_BOTTOM_DOTTED', 'STICK_ARROW_TOP_DOTTED', 'STICK_ARROW_BOTTOM_DOTTED', 'SOLID_ARROW_TOP_REVERSE', 'SOLID_ARROW_BOTTOM_REVERSE', 'STICK_ARROW_TOP_REVERSE', 'STICK_ARROW_BOTTOM_REVERSE', 'SOLID_ARROW_TOP_REVERSE_DOTTED', 'SOLID_ARROW_BOTTOM_REVERSE_DOTTED', 'STICK_ARROW_TOP_REVERSE_DOTTED', 'STICK_ARROW_BOTTOM_REVERSE_DOTTED', 'BIDIRECTIONAL_SOLID_ARROW', 'DOTTED_ARROW', 'BIDIRECTIONAL_DOTTED_ARROW', 'SOLID_CROSS', 'DOTTED_CROSS', 'SOLID_POINT', 'DOTTED_POINT', got 'TXT'] `); }); From bd47c57eaf3f5ab4620dd8ccbe8cf449603296de Mon Sep 17 00:00:00 2001 From: omkarht Date: Wed, 6 Aug 2025 18:56:58 +0530 Subject: [PATCH 11/18] chore: refactored code on-behalf-of: @Mermaid-Chart --- .../src/diagrams/sequence/sequenceRenderer.ts | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index 9c73b9f2a..fabce3bdc 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -286,28 +286,35 @@ const drawCentralConnection = function ( msg: any, msgModel: any, diagObj: Diagram, - startx, - stopx, - lineStartY + startx: number, + stopx: number, + lineStartY: number ) { + const actors = diagObj.db.getActors(); + const [fromLeft] = activationBounds(msg.from, actors); + const [toLeft] = activationBounds(msg.to, actors); + const isArrowToRight = fromLeft <= toLeft; + const g = elem.append('g'); - const circle = g.append('circle'); - circle.attr( - 'cx', - msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION ? stopx + 7.5 : startx - 5 - ); - // circle.attr('cx',x); - circle.attr('cy', lineStartY); - circle.attr('r', 5); - circle.attr('width', 10); - circle.attr('height', 10); - if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_DUAL) { - const circle = g.append('circle'); - circle.attr('cx', stopx + 7.5); - circle.attr('cy', lineStartY); - circle.attr('r', 5); - circle.attr('width', 10); - circle.attr('height', 10); + + const drawCircle = (cx: number) => { + g.append('circle') + .attr('cx', cx) + .attr('cy', lineStartY) + .attr('r', 5) + .attr('width', 10) + .attr('height', 10); + }; + + if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION) { + const cx = isArrowToRight ? stopx + 8 : stopx - 8; + drawCircle(cx); + } else if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_DUAL) { + const offset = isArrowToRight ? 8 : -8; + drawCircle(stopx + offset); + drawCircle(startx - 2.75); // second circle (near start) + } else { + drawCircle(startx - 2.75); } }; From 74c96db3e2f4077eee742ec6944ef2585360b2ad Mon Sep 17 00:00:00 2001 From: omkarht Date: Thu, 7 Aug 2025 12:47:31 +0530 Subject: [PATCH 12/18] docs: document new syntax for half-arrow message and central connection on-behalf-of: @Mermaid-Chart --- docs/syntax/sequenceDiagram.md | 76 ++++++++++++++++++- .../src/docs/syntax/sequenceDiagram.md | 58 +++++++++++++- 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/docs/syntax/sequenceDiagram.md b/docs/syntax/sequenceDiagram.md index 84240a0cd..d3d5808bb 100644 --- a/docs/syntax/sequenceDiagram.md +++ b/docs/syntax/sequenceDiagram.md @@ -209,7 +209,11 @@ Messages can be of two displayed either solid or with a dotted line. [Actor][Arrow][Actor]:Message text ``` -There are ten types of arrows currently supported: +Lines can be solid or dotted, and can end with various types of arrowheads, crosses, or open arrows. + +#### Supported Arrow Types + +**Standard Arrow Types** | Type | Description | | -------- | ---------------------------------------------------- | @@ -224,6 +228,76 @@ There are ten types of arrows currently supported: | `-)` | Solid line with an open arrow at the end (async) | | `--)` | Dotted line with a open arrow at the end (async) | +**New Half-Arrows** + +The following half-arrow types are now supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`). + +--- + +\| Type | Description | +\| ------- | ---------------------------------------------------- | ---------------------------------------------- | +\| `-\|\` | Solid line with top half arrowhead | +\| `--\|\` | Dotted line with top half arrowhead | +\| `-\|/` | Solid line with bottom half arrowhead | +\| `--\|/` | Dotted line with bottom half arrowhead | +\| `/\|-` | Solid line with reverse top half arrowhead | +\| `/\|--` | Dotted line with reverse top half arrowhead | +\| `\\ | -` | Solid line with reverse bottom half arrowhead | +\| `\\ | --` | Dotted line with reverse bottom half arrowhead | +\| `-\\` | Solid line with top stick half arrowhead | +\| `--\\` | Dotted line with top stick half arrowhead | +\| `-//` | Solid line with bottom stick half arrowhead | +\| `--//` | Dotted line with bottom stick half arrowhead | +\| `//-` | Solid line with reverse top stick half arrowhead | +\| `//--` | Dotted line with reverse top stick half arrowhead | +\| `\\-` | Solid line with reverse bottom stick half arrowhead | +\| `\\--` | Dotted line with reverse bottom stick half arrowhead | + +## Central Connections + +Mermaid sequence diagrams now support **central lifeline connections** using a `()`.\ +This is useful to represent messages or signals that connect to a central point, rather than from one actor directly to another. + +To indicate a central connection, append `()` to the arrow syntax. + +#### Basic Syntax + +```mermaid-example +sequenceDiagram + participant Alice + participant John + Alice->>()John: Hello John + Alice()->>John: How are you? + John()->>()Alice: Great! +``` + +```mermaid +sequenceDiagram + participant Alice + participant John + Alice->>()John: Hello John + Alice()->>John: How are you? + John()->>()Alice: Great! +``` + +```mermaid-example +sequenceDiagram + participant Alice + participant John + Alice->>()John: Hello John + Alice()->>John: How are you? + John()->>()Alice: Great! +``` + +```mermaid +sequenceDiagram + participant Alice + participant John + Alice->>()John: Hello John + Alice()->>John: How are you? + John()->>()Alice: Great! +``` + ## Activations It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations: diff --git a/packages/mermaid/src/docs/syntax/sequenceDiagram.md b/packages/mermaid/src/docs/syntax/sequenceDiagram.md index 3087eb743..5112227a0 100644 --- a/packages/mermaid/src/docs/syntax/sequenceDiagram.md +++ b/packages/mermaid/src/docs/syntax/sequenceDiagram.md @@ -144,7 +144,11 @@ Messages can be of two displayed either solid or with a dotted line. [Actor][Arrow][Actor]:Message text ``` -There are ten types of arrows currently supported: +Lines can be solid or dotted, and can end with various types of arrowheads, crosses, or open arrows. + +#### Supported Arrow Types + +**Standard Arrow Types** | Type | Description | | -------- | ---------------------------------------------------- | @@ -159,6 +163,58 @@ There are ten types of arrows currently supported: | `-)` | Solid line with an open arrow at the end (async) | | `--)` | Dotted line with a open arrow at the end (async) | +**New Half-Arrows** + +The following half-arrow types are now supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`). + +--- + +| Type | Description | +| ------- | ---------------------------------------------------- | ---------------------------------------------- | +| `-\|\` | Solid line with top half arrowhead | +| `--\|\` | Dotted line with top half arrowhead | +| `-\|/` | Solid line with bottom half arrowhead | +| `--\|/` | Dotted line with bottom half arrowhead | +| `/\|-` | Solid line with reverse top half arrowhead | +| `/\|--` | Dotted line with reverse top half arrowhead | +| `\\ | -` | Solid line with reverse bottom half arrowhead | +| `\\ | --` | Dotted line with reverse bottom half arrowhead | +| `-\\` | Solid line with top stick half arrowhead | +| `--\\` | Dotted line with top stick half arrowhead | +| `-//` | Solid line with bottom stick half arrowhead | +| `--//` | Dotted line with bottom stick half arrowhead | +| `//-` | Solid line with reverse top stick half arrowhead | +| `//--` | Dotted line with reverse top stick half arrowhead | +| `\\-` | Solid line with reverse bottom stick half arrowhead | +| `\\--` | Dotted line with reverse bottom stick half arrowhead | + +## Central Connections + +Mermaid sequence diagrams now support **central lifeline connections** using a `()`. +This is useful to represent messages or signals that connect to a central point, rather than from one actor directly to another. + +To indicate a central connection, append `()` to the arrow syntax. + +#### Basic Syntax + +```mermaid-example +sequenceDiagram + participant Alice + participant John + Alice->>()John: Hello John + Alice()->>John: How are you? + John()->>()Alice: Great! +``` + +```mermaid +sequenceDiagram + participant Alice + participant John + Alice->>()John: Hello John + Alice()->>John: How are you? + John()->>()Alice: Great! +``` + ## Activations It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations: From 2b58df9665ad15679a960ca0ce845f1d417286f0 Mon Sep 17 00:00:00 2001 From: omkarht Date: Tue, 19 Aug 2025 14:53:08 +0530 Subject: [PATCH 13/18] fix: refactored documentation on-behalf-of: @Mermaid-Chart --- docs/syntax/sequenceDiagram.md | 56 +++++++------------ .../mermaid/src/diagrams/sequence/svgDraw.js | 1 - .../src/docs/syntax/sequenceDiagram.md | 17 ++---- 3 files changed, 23 insertions(+), 51 deletions(-) diff --git a/docs/syntax/sequenceDiagram.md b/docs/syntax/sequenceDiagram.md index d3d5808bb..0b5b038e2 100644 --- a/docs/syntax/sequenceDiagram.md +++ b/docs/syntax/sequenceDiagram.md @@ -228,30 +228,30 @@ Lines can be solid or dotted, and can end with various types of arrowheads, cros | `-)` | Solid line with an open arrow at the end (async) | | `--)` | Dotted line with a open arrow at the end (async) | -**New Half-Arrows** +**Half-Arrows** The following half-arrow types are now supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`). --- -\| Type | Description | -\| ------- | ---------------------------------------------------- | ---------------------------------------------- | -\| `-\|\` | Solid line with top half arrowhead | -\| `--\|\` | Dotted line with top half arrowhead | -\| `-\|/` | Solid line with bottom half arrowhead | -\| `--\|/` | Dotted line with bottom half arrowhead | -\| `/\|-` | Solid line with reverse top half arrowhead | -\| `/\|--` | Dotted line with reverse top half arrowhead | -\| `\\ | -` | Solid line with reverse bottom half arrowhead | -\| `\\ | --` | Dotted line with reverse bottom half arrowhead | -\| `-\\` | Solid line with top stick half arrowhead | -\| `--\\` | Dotted line with top stick half arrowhead | -\| `-//` | Solid line with bottom stick half arrowhead | -\| `--//` | Dotted line with bottom stick half arrowhead | -\| `//-` | Solid line with reverse top stick half arrowhead | -\| `//--` | Dotted line with reverse top stick half arrowhead | -\| `\\-` | Solid line with reverse bottom stick half arrowhead | -\| `\\--` | Dotted line with reverse bottom stick half arrowhead | +| Type | Description | +| ------- | ---------------------------------------------------- | +| `-\|\` | Solid line with top half arrowhead | +| `--\|\` | Dotted line with top half arrowhead | +| `-\|/` | Solid line with bottom half arrowhead | +| `--\|/` | Dotted line with bottom half arrowhead | +| `/\|-` | Solid line with reverse top half arrowhead | +| `/\|--` | Dotted line with reverse top half arrowhead | +| `\\-` | Solid line with reverse bottom half arrowhead | +| `\\--` | Dotted line with reverse bottom half arrowhead | +| `-\\` | Solid line with top stick half arrowhead | +| `--\\` | Dotted line with top stick half arrowhead | +| `-//` | Solid line with bottom stick half arrowhead | +| `--//` | Dotted line with bottom stick half arrowhead | +| `//-` | Solid line with reverse top stick half arrowhead | +| `//--` | Dotted line with reverse top stick half arrowhead | +| `\\-` | Solid line with reverse bottom stick half arrowhead | +| `\\--` | Dotted line with reverse bottom stick half arrowhead | ## Central Connections @@ -280,24 +280,6 @@ sequenceDiagram John()->>()Alice: Great! ``` -```mermaid-example -sequenceDiagram - participant Alice - participant John - Alice->>()John: Hello John - Alice()->>John: How are you? - John()->>()Alice: Great! -``` - -```mermaid -sequenceDiagram - participant Alice - participant John - Alice->>()John: Hello John - Alice()->>John: How are you? - John()->>()Alice: Great! -``` - ## Activations It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations: diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index 4d2a574d1..17c5f9185 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -1192,7 +1192,6 @@ export default { getNoteRect, fixLifeLineHeights, sanitizeUrl, - insertSolidTopArrowHead, insertSolidBottomArrowHead, insertStickTopArrowHead, diff --git a/packages/mermaid/src/docs/syntax/sequenceDiagram.md b/packages/mermaid/src/docs/syntax/sequenceDiagram.md index 5112227a0..c0e927dae 100644 --- a/packages/mermaid/src/docs/syntax/sequenceDiagram.md +++ b/packages/mermaid/src/docs/syntax/sequenceDiagram.md @@ -163,22 +163,22 @@ Lines can be solid or dotted, and can end with various types of arrowheads, cros | `-)` | Solid line with an open arrow at the end (async) | | `--)` | Dotted line with a open arrow at the end (async) | -**New Half-Arrows** +**Half-Arrows** The following half-arrow types are now supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`). --- | Type | Description | -| ------- | ---------------------------------------------------- | ---------------------------------------------- | +| ------- | ---------------------------------------------------- | | `-\|\` | Solid line with top half arrowhead | | `--\|\` | Dotted line with top half arrowhead | | `-\|/` | Solid line with bottom half arrowhead | | `--\|/` | Dotted line with bottom half arrowhead | | `/\|-` | Solid line with reverse top half arrowhead | | `/\|--` | Dotted line with reverse top half arrowhead | -| `\\ | -` | Solid line with reverse bottom half arrowhead | -| `\\ | --` | Dotted line with reverse bottom half arrowhead | +| `\\-` | Solid line with reverse bottom half arrowhead | +| `\\--` | Dotted line with reverse bottom half arrowhead | | `-\\` | Solid line with top stick half arrowhead | | `--\\` | Dotted line with top stick half arrowhead | | `-//` | Solid line with bottom stick half arrowhead | @@ -206,15 +206,6 @@ sequenceDiagram John()->>()Alice: Great! ``` -```mermaid -sequenceDiagram - participant Alice - participant John - Alice->>()John: Hello John - Alice()->>John: How are you? - John()->>()Alice: Great! -``` - ## Activations It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations: From 73e9849f993cd766eecddf349e335a4473560f37 Mon Sep 17 00:00:00 2001 From: omkarht Date: Thu, 21 Aug 2025 16:12:25 +0530 Subject: [PATCH 14/18] chore: added changeset on-behalf-of: @Mermaid-Chart --- .changeset/loud-results-melt.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/loud-results-melt.md diff --git a/.changeset/loud-results-melt.md b/.changeset/loud-results-melt.md new file mode 100644 index 000000000..7005750c6 --- /dev/null +++ b/.changeset/loud-results-melt.md @@ -0,0 +1,5 @@ +--- +'mermaid': minor +--- + +feat: Add half-arrowheads (solid & stick) and central connection support From 1988d2422783d73b793a208c1656d45b82c581d1 Mon Sep 17 00:00:00 2001 From: omkarht Date: Fri, 22 Aug 2025 16:46:49 +0530 Subject: [PATCH 15/18] fix: fixed reverse arrows placing for autonumber on-behalf-of: @Mermaid-Chart --- .../src/diagrams/sequence/sequenceRenderer.ts | 31 ++++++++++++++----- .../mermaid/src/diagrams/sequence/svgDraw.js | 4 +-- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index c13a15fef..c344968a1 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -307,14 +307,15 @@ const drawCentralConnection = function ( }; if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION) { - const cx = isArrowToRight ? stopx + 8 : stopx - 8; + const cx = isArrowToRight ? stopx + 5 : stopx - 8; + drawCircle(cx); + } else if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_REVERSE) { + const cx = isArrowToRight ? startx - 5 : stopx + 8; drawCircle(cx); } else if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_DUAL) { - const offset = isArrowToRight ? 8 : -8; + const offset = isArrowToRight ? 5 : -5; drawCircle(stopx + offset); - drawCircle(startx - 2.75); // second circle (near start) - } else { - drawCircle(startx - 2.75); + drawCircle(startx - offset); } }; @@ -573,7 +574,18 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO type === diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID || type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED; - if (isBidirectional) { + const isReverseArrowType = + type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE || + type === diagObj.db.LINETYPE.SOLID_ARROW_TOP_REVERSE_DOTTED || + type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE || + type === diagObj.db.LINETYPE.SOLID_ARROW_BOTTOM_REVERSE_DOTTED || + type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE || + type === diagObj.db.LINETYPE.STICK_ARROW_TOP_REVERSE_DOTTED || + type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE || + type === diagObj.db.LINETYPE.STICK_ARROW_BOTTOM_REVERSE_DOTTED; + + let x = 0; + if (isBidirectional || isReverseArrowType) { const SEQUENCE_NUMBER_RADIUS = 6; if (startx < stopx) { @@ -581,6 +593,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO } else { line.attr('x1', startx + SEQUENCE_NUMBER_RADIUS); } + x = 3.5; } diagram @@ -590,7 +603,8 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO .attr('x2', startx) .attr('y2', lineStartY) .attr('stroke-width', 0) - .attr('marker-start', 'url(' + url + '#sequencenumber)'); + .attr('marker-start', 'url(' + url + '#sequencenumber)') + .attr('transform', `translate(-${x}, 0)`); diagram .append('text') @@ -600,7 +614,8 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO .attr('font-size', '12px') .attr('text-anchor', 'middle') .attr('class', 'sequenceNumber') - .text(sequenceIndex); + .text(sequenceIndex) + .attr('transform', `translate(-${x}, 0)`); } }; diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index 57df19165..9a7c1bf54 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -1142,7 +1142,7 @@ export const insertStickTopArrowHead = function (elem) { elem .append('defs') .append('marker') - .attr('id', ' stickTopArrowHead') + .attr('id', 'stickTopArrowHead') .attr('refX', 7.5) .attr('refY', 7) .attr('markerUnits', 'userSpaceOnUse') @@ -1160,7 +1160,7 @@ export const insertStickBottomArrowHead = function (elem) { elem .append('defs') .append('marker') - .attr('id', ' stickBottomArrowHead') + .attr('id', 'stickBottomArrowHead') .attr('refX', 7.5) .attr('refY', 0) .attr('markerUnits', 'userSpaceOnUse') From 11cd3f1262f070c251cb47c371696f04be23fa53 Mon Sep 17 00:00:00 2001 From: omkarht Date: Tue, 2 Sep 2025 18:52:18 +0530 Subject: [PATCH 16/18] feat: add central connection rendering and parsing tests --- .../rendering/sequencediagram-v2.spec.js | 121 +++++++++ .../diagrams/sequence/sequenceDiagram.spec.js | 248 ++++++++++++++++++ .../src/diagrams/sequence/sequenceRenderer.ts | 86 ++++-- 3 files changed, 435 insertions(+), 20 deletions(-) diff --git a/cypress/integration/rendering/sequencediagram-v2.spec.js b/cypress/integration/rendering/sequencediagram-v2.spec.js index f1c2aafbd..42db4001d 100644 --- a/cypress/integration/rendering/sequencediagram-v2.spec.js +++ b/cypress/integration/rendering/sequencediagram-v2.spec.js @@ -655,5 +655,126 @@ 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 } } + ); + }); + }); }); }); diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js index 68c1c7a29..c09a92737 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js +++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js @@ -187,6 +187,254 @@ describe('more than one sequence diagram', () => { }); }); +describe('Central Connection Parsing', () => { + describe('when parsing central connection syntax', () => { + it('should parse actor ()->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + Alice ()->>() Bob: Hello Bob, how are you? + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse + + // Find the actual message (type: 'addMessage') + const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to); + expect(actualMessage).toMatchObject({ + from: 'Alice', + to: 'Bob', + message: 'Hello Bob, how are you?', + centralConnection: 61, // CENTRAL_CONNECTION_DUAL + activate: true, + type: 0, // SOLID (based on test output) + }); + }); + + it('should parse actor ()-->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + Alice ()-->>() Bob: Hello Bob, how are you? + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse + + const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to); + expect(actualMessage).toMatchObject({ + from: 'Alice', + to: 'Bob', + message: 'Hello Bob, how are you?', + centralConnection: 61, // CENTRAL_CONNECTION_DUAL + activate: true, + type: 1, // DOTTED (based on test output) + }); + }); + + it('should parse actor ->>() actor syntax as CENTRAL_CONNECTION', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + Alice ->>() Bob: Hello Bob, how are you? + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(2); // addMessage + centralConnection (no activation for this pattern) + + const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to); + expect(actualMessage).toMatchObject({ + from: 'Alice', + to: 'Bob', + message: 'Hello Bob, how are you?', + centralConnection: 59, // CENTRAL_CONNECTION + activate: true, + type: 0, // SOLID (based on actual parsing) + }); + }); + + it('should parse actor ()-->> actor syntax as CENTRAL_CONNECTION_REVERSE', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + Alice ()-->> Bob: Hello Bob, how are you? + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(2); // addMessage + centralConnectionReverse + + const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to); + expect(actualMessage).toMatchObject({ + from: 'Alice', + to: 'Bob', + message: 'Hello Bob, how are you?', + centralConnection: 60, // CENTRAL_CONNECTION_REVERSE + activate: false, + type: 1, // DOTTED (based on test output) + }); + }); + + it('should parse actor ()->> actor syntax as CENTRAL_CONNECTION_REVERSE', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + Alice ()->> Bob: Hello Bob, how are you? + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(2); // addMessage + centralConnectionReverse + + const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to); + expect(actualMessage).toMatchObject({ + from: 'Alice', + to: 'Bob', + message: 'Hello Bob, how are you?', + centralConnection: 60, // CENTRAL_CONNECTION_REVERSE + activate: false, + type: 0, // SOLID (based on test output) + }); + }); + + it('should parse actor ()<<-->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + Alice ()<<-->>() Bob: Hello Bob, how are you? + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse + + const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to); + expect(actualMessage).toMatchObject({ + from: 'Alice', + to: 'Bob', + message: 'Hello Bob, how are you?', + centralConnection: 61, // CENTRAL_CONNECTION_DUAL + activate: true, + type: 34, // BIDIRECTIONAL_DOTTED + }); + }); + + it('should parse actor ()<<->>() actor syntax as CENTRAL_CONNECTION_DUAL', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + Alice ()<<->>() Bob: Hello Bob, how are you? + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(3); // addMessage + centralConnection + centralConnectionReverse + + const actualMessage = messages.find((msg) => msg.type !== undefined && msg.from && msg.to); + expect(actualMessage).toMatchObject({ + from: 'Alice', + to: 'Bob', + message: 'Hello Bob, how are you?', + centralConnection: 61, // CENTRAL_CONNECTION_DUAL + activate: true, + type: 33, // BIDIRECTIONAL_SOLID + }); + }); + + it('should handle multiple central connection types in one diagram', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + participant Charlie + Alice ()->>() Bob: Message 1 + Bob ()-->> Charlie: Message 2 + Charlie ()<<-->>() Alice: Message 3 + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(8); // 3 addMessages + 5 central connection markers + + // Filter to get only the actual messages + const actualMessages = messages.filter((msg) => msg.type !== undefined && msg.from && msg.to); + expect(actualMessages).toHaveLength(3); + + expect(actualMessages[0]).toMatchObject({ + from: 'Alice', + to: 'Bob', + centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()->>()) + }); + + expect(actualMessages[1]).toMatchObject({ + from: 'Bob', + to: 'Charlie', + centralConnection: 60, // CENTRAL_CONNECTION_REVERSE (()-->>) + }); + + expect(actualMessages[2]).toMatchObject({ + from: 'Charlie', + to: 'Alice', + centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()<<-->>()) + }); + }); + + it('should handle central connections with different arrow types', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + Alice ()-x() Bob: Cross message + Alice ()--x() Bob: Dotted cross message + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(6); // 2 addMessages + 4 central connection markers + + const actualMessages = messages.filter((msg) => msg.type !== undefined && msg.from && msg.to); + expect(actualMessages).toHaveLength(2); + + expect(actualMessages[0]).toMatchObject({ + from: 'Alice', + to: 'Bob', + centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()-x()) + type: 3, // SOLID_CROSS + }); + + expect(actualMessages[1]).toMatchObject({ + from: 'Alice', + to: 'Bob', + centralConnection: 61, // CENTRAL_CONNECTION_DUAL (()--x()) + type: 4, // DOTTED_CROSS + }); + }); + + it('should not break existing parsing without central connections', async () => { + const diagram = await Diagram.fromText(` + sequenceDiagram + participant Alice + participant Bob + Alice ->> Bob: Normal message + Bob -->> Alice: Normal dotted message + Alice -x Bob: Normal cross message + `); + + const messages = diagram.db.getMessages(); + expect(messages).toHaveLength(3); + + messages.forEach((msg) => { + expect(msg.centralConnection).toBe(0); // No central connection + }); + + expect(messages[0].type).toBe(0); // SOLID (based on actual parsing) + expect(messages[1].type).toBe(1); // DOTTED (based on actual parsing) + expect(messages[2].type).toBe(3); // SOLID_CROSS + }); + }); +}); + describe('when parsing a sequenceDiagram', function () { let diagram; beforeEach(async function () { diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index 86d57757a..6c87ef124 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -292,9 +292,10 @@ const drawCentralConnection = function ( lineStartY: number ) { const actors = diagObj.db.getActors(); - const [fromLeft] = activationBounds(msg.from, actors); - const [toLeft] = activationBounds(msg.to, actors); - const isArrowToRight = fromLeft <= toLeft; + const fromActor = actors.get(msg.from); + const toActor = actors.get(msg.to); + const fromCenter = fromActor.x + fromActor.width / 2; + const toCenter = toActor.x + toActor.width / 2; const g = elem.append('g'); @@ -307,16 +308,20 @@ const drawCentralConnection = function ( .attr('height', 10); }; - if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION) { - const cx = isArrowToRight ? stopx + 5 : stopx - 8; - drawCircle(cx); - } else if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_REVERSE) { - const cx = isArrowToRight ? startx - 5 : stopx + 8; - drawCircle(cx); - } else if (msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_DUAL) { - const offset = isArrowToRight ? 5 : -5; - drawCircle(stopx + offset); - drawCircle(startx - offset); + const { CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL } = + diagObj.db.LINETYPE; + + switch (msg.centralConnection) { + case CENTRAL_CONNECTION: + drawCircle(toCenter); + break; + case CENTRAL_CONNECTION_REVERSE: + drawCircle(fromCenter); + break; + case CENTRAL_CONNECTION_DUAL: + drawCircle(fromCenter); + drawCircle(toCenter); + break; } }; @@ -471,7 +476,7 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO line.attr('y1', lineStartY); line.attr('x2', stopx); line.attr('y2', lineStartY); - if (msg.centralConnection) { + if (hasCentralConnection(msg, diagObj)) { drawCentralConnection(diagram, msg, msgModel, diagObj, startx, stopx, lineStartY); } } @@ -1600,6 +1605,51 @@ const buildNoteModel = async function (msg, actors, diagObj) { return noteModel; }; +// Central connection positioning constants +const CENTRAL_CONNECTION_BASE_OFFSET = 4; +const CENTRAL_CONNECTION_BIDIRECTIONAL_OFFSET = 6; + +/** + * Check if a message has central connection + * @param msg - The message object + * @param diagObj - The diagram object containing LINETYPE constants + * @returns True if the message has any type of central connection + */ +const hasCentralConnection = function (msg, diagObj) { + const { CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL } = + diagObj.db.LINETYPE; + return [CENTRAL_CONNECTION, CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL].includes( + msg.centralConnection + ); +}; + +/** + * Calculate the positioning offset for central connection arrows + * @param msg - The message object + * @param diagObj - The diagram object containing LINETYPE constants + * @param isArrowToRight - Whether the arrow is pointing to the right + * @returns The offset to apply to startx position + */ +const calculateCentralConnectionOffset = function (msg, diagObj, isArrowToRight) { + const { CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL, BIDIRECTIONAL_SOLID } = + diagObj.db.LINETYPE; + + let offset = 0; + + if ( + msg.centralConnection === CENTRAL_CONNECTION_REVERSE || + msg.centralConnection === CENTRAL_CONNECTION_DUAL + ) { + offset += CENTRAL_CONNECTION_BASE_OFFSET; + } + + if (msg.centralConnection === CENTRAL_CONNECTION_DUAL && msg.type === BIDIRECTIONAL_SOLID) { + offset += isArrowToRight ? 0 : -CENTRAL_CONNECTION_BIDIRECTIONAL_OFFSET; + } + + return offset; +}; + const buildMessageModel = function (msg, actors, diagObj) { if ( ![ @@ -1644,12 +1694,8 @@ const buildMessageModel = function (msg, actors, diagObj) { let startx = isArrowToRight ? fromRight : fromLeft; let stopx = isArrowToRight ? toLeft : toRight; - if ( - msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_REVERSE || - msg.centralConnection === diagObj.db.LINETYPE.CENTRAL_CONNECTION_DUAL - ) { - startx += 4; - } + // Apply central connection positioning adjustments + startx += calculateCentralConnectionOffset(msg, diagObj, isArrowToRight); // As the line width is considered, the left and right values will be off by 2. const isArrowToActivation = Math.abs(toLeft - toRight) > 2; From 82ef7b5fdb6322419b682a507f5c7c60bf6ffb9b Mon Sep 17 00:00:00 2001 From: omkarht Date: Tue, 2 Sep 2025 19:02:02 +0530 Subject: [PATCH 17/18] docs: add version placeholders for new features --- docs/syntax/sequenceDiagram.md | 8 ++++---- packages/mermaid/src/docs/syntax/sequenceDiagram.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/syntax/sequenceDiagram.md b/docs/syntax/sequenceDiagram.md index e473cc00f..eb3cfc996 100644 --- a/docs/syntax/sequenceDiagram.md +++ b/docs/syntax/sequenceDiagram.md @@ -348,9 +348,9 @@ Lines can be solid or dotted, and can end with various types of arrowheads, cros | `-)` | Solid line with an open arrow at the end (async) | | `--)` | Dotted line with a open arrow at the end (async) | -**Half-Arrows** +**Half-Arrows (v\+)** -The following half-arrow types are now supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`). +The following half-arrow types are supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`). --- @@ -373,9 +373,9 @@ The following half-arrow types are now supported for more expressive sequence di | `\\-` | Solid line with reverse bottom stick half arrowhead | | `\\--` | Dotted line with reverse bottom stick half arrowhead | -## Central Connections +## Central Connections (v\+) -Mermaid sequence diagrams now support **central lifeline connections** using a `()`.\ +Mermaid sequence diagrams support **central lifeline connections** using a `()`. This is useful to represent messages or signals that connect to a central point, rather than from one actor directly to another. To indicate a central connection, append `()` to the arrow syntax. diff --git a/packages/mermaid/src/docs/syntax/sequenceDiagram.md b/packages/mermaid/src/docs/syntax/sequenceDiagram.md index 6d10c6f97..25b770484 100644 --- a/packages/mermaid/src/docs/syntax/sequenceDiagram.md +++ b/packages/mermaid/src/docs/syntax/sequenceDiagram.md @@ -235,9 +235,9 @@ Lines can be solid or dotted, and can end with various types of arrowheads, cros | `-)` | Solid line with an open arrow at the end (async) | | `--)` | Dotted line with a open arrow at the end (async) | -**Half-Arrows** +**Half-Arrows (v+)** -The following half-arrow types are now supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`). +The following half-arrow types are supported for more expressive sequence diagrams. Both solid and dotted variants are available by increasing the number of dashes (`-` → `--`). --- @@ -260,9 +260,9 @@ The following half-arrow types are now supported for more expressive sequence di | `\\-` | Solid line with reverse bottom stick half arrowhead | | `\\--` | Dotted line with reverse bottom stick half arrowhead | -## Central Connections +## Central Connections (v+) -Mermaid sequence diagrams now support **central lifeline connections** using a `()`. +Mermaid sequence diagrams support **central lifeline connections** using a `()`. This is useful to represent messages or signals that connect to a central point, rather than from one actor directly to another. To indicate a central connection, append `()` to the arrow syntax. From e3ef5e42084d6916d8af5aba8dcbb9e04d7ef3b2 Mon Sep 17 00:00:00 2001 From: omkarht Date: Thu, 11 Sep 2025 13:03:06 +0530 Subject: [PATCH 18/18] fix: fixed central connection for bidirectional dotted arrow on-behalf-of: @Mermaid-Chart --- .../src/diagrams/sequence/sequenceRenderer.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index 6c87ef124..04d5607ad 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -1631,8 +1631,12 @@ const hasCentralConnection = function (msg, diagObj) { * @returns The offset to apply to startx position */ const calculateCentralConnectionOffset = function (msg, diagObj, isArrowToRight) { - const { CENTRAL_CONNECTION_REVERSE, CENTRAL_CONNECTION_DUAL, BIDIRECTIONAL_SOLID } = - diagObj.db.LINETYPE; + const { + CENTRAL_CONNECTION_REVERSE, + CENTRAL_CONNECTION_DUAL, + BIDIRECTIONAL_SOLID, + BIDIRECTIONAL_DOTTED, + } = diagObj.db.LINETYPE; let offset = 0; @@ -1643,7 +1647,10 @@ const calculateCentralConnectionOffset = function (msg, diagObj, isArrowToRight) offset += CENTRAL_CONNECTION_BASE_OFFSET; } - if (msg.centralConnection === CENTRAL_CONNECTION_DUAL && msg.type === BIDIRECTIONAL_SOLID) { + if ( + msg.centralConnection === CENTRAL_CONNECTION_DUAL && + (msg.type === BIDIRECTIONAL_SOLID || msg.type === BIDIRECTIONAL_DOTTED) + ) { offset += isArrowToRight ? 0 : -CENTRAL_CONNECTION_BIDIRECTIONAL_OFFSET; }