diff --git a/.changeset/witty-beds-turn.md b/.changeset/witty-beds-turn.md new file mode 100644 index 0000000000..a0a3ee8b10 --- /dev/null +++ b/.changeset/witty-beds-turn.md @@ -0,0 +1,5 @@ +--- +'mermaid': minor +--- + +feat:Added support for markdown-style links in line messages and notes diff --git a/cypress/integration/rendering/sequencediagram.spec.js b/cypress/integration/rendering/sequencediagram.spec.js index f18e99abf8..8a9987ab87 100644 --- a/cypress/integration/rendering/sequencediagram.spec.js +++ b/cypress/integration/rendering/sequencediagram.spec.js @@ -79,6 +79,15 @@ describe('Sequence diagram', () => { ` ); }); + it('should render links', () => { + imgSnapshotTest( + ` + sequenceDiagram + Alice->John: Look at [MermaidJS](https://mermaid.js.org/) + John->>Alice: Thanks for the link + ` + ); + }); it('should handle different line breaks', () => { imgSnapshotTest( ` diff --git a/docs/syntax/sequenceDiagram.md b/docs/syntax/sequenceDiagram.md index 84240a0cd3..9f732156e7 100644 --- a/docs/syntax/sequenceDiagram.md +++ b/docs/syntax/sequenceDiagram.md @@ -209,6 +209,8 @@ Messages can be of two displayed either solid or with a dotted line. [Actor][Arrow][Actor]:Message text ``` +### Arrow Types + There are ten types of arrows currently supported: | Type | Description | @@ -224,6 +226,26 @@ 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) | +### Links in Messages (v\+) + +You can add one or more markdown-style links to messages. + +``` +[Actor][Arrow][Actor]:[Label](URL) +``` + +Example: + +```mermaid-example +sequenceDiagram + Alice->>John: Alice shared [MermaidJS](https://mermaid.js.org/) and [the Getting Started guide](https://mermaid.js.org/intro/getting-started.html) with John +``` + +```mermaid +sequenceDiagram + Alice->>John: Alice shared [MermaidJS](https://mermaid.js.org/) and [the Getting Started guide](https://mermaid.js.org/intro/getting-started.html) with John +``` + ## Activations It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations: diff --git a/packages/mermaid/src/diagrams/common/common.ts b/packages/mermaid/src/diagrams/common/common.ts index fd76d0a45d..b7af186e97 100644 --- a/packages/mermaid/src/diagrams/common/common.ts +++ b/packages/mermaid/src/diagrams/common/common.ts @@ -303,6 +303,16 @@ export const katexRegex = /\$\$(.*)\$\$/g; */ export const hasKatex = (text: string): boolean => (text.match(katexRegex)?.length ?? 0) > 0; +export const markdownLinkRegex = /\[([^\]]+)]\(([^)]+)\)/; + +/** + * Detect markdown links + * + * @param text - The text to test + * @returns Whether or not the text has markdown links + */ +export const hasMarkdownLink = (text: string): boolean => markdownLinkRegex.test(text); + /** * Computes the minimum dimensions needed to display a div containing MathML * diff --git a/packages/mermaid/src/diagrams/sequence/styles.js b/packages/mermaid/src/diagrams/sequence/styles.js index 5c36b4ed19..10b01138e1 100644 --- a/packages/mermaid/src/diagrams/sequence/styles.js +++ b/packages/mermaid/src/diagrams/sequence/styles.js @@ -48,6 +48,10 @@ const getStyles = (options) => stroke: none; } + .link { + fill: #0000EE; + } + .labelBox { stroke: ${options.labelBoxBorderColor}; fill: ${options.labelBoxBkgColor}; diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index c681c94918..e1212bf9e7 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -1,4 +1,10 @@ -import common, { calculateMathMLDimensions, hasKatex, renderKatex } from '../common/common.js'; +import common, { + calculateMathMLDimensions, + hasKatex, + renderKatex, + hasMarkdownLink, + markdownLinkRegex, +} from '../common/common.js'; import * as svgDrawCommon from '../common/svgDrawCommon.js'; import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js'; import { sanitizeUrl } from '@braintree/sanitize-url'; @@ -127,6 +133,67 @@ export const drawKatex = async function (elem, textData, msgModel = null) { return [textElem]; }; +export const drawLink = function (elem, label, url) { + const sanitizedUrl = sanitizeUrl(url); + elem + .append('a') + .attr('href', sanitizedUrl) + .attr('xlink:href', sanitizedUrl) + .attr('target', '_blank') + .attr('xlink:show', 'new') + .attr('rel', 'noopener noreferrer') + .append('tspan') + .attr('class', 'link') + .text(label); +}; + +export const drawMarkdownLinkText = function (elem, text) { + // Split text into segments - links and text between links + const segments = []; + let remainingText = text; + let match; + + // Markdown link regex: [text](url) + const linkRegex = markdownLinkRegex; + + while ((match = remainingText.match(linkRegex))) { + // Push text before link if exists + if (match.index > 0) { + segments.push({ + type: 'text', + content: remainingText.substring(0, match.index), + }); + } + + // Push link + segments.push({ + type: 'link', + text: match[1], + url: match[2], + }); + + // Update remaining text + remainingText = remainingText.substring(match.index + match[0].length); + } + + // Push remaining text if any + if (remainingText) { + segments.push({ + type: 'text', + content: remainingText, + }); + } + + // Append all segments + segments.forEach((segment) => { + if (segment.type === 'link') { + drawLink(elem, segment.text, segment.url); + } else { + elem.append('tspan').text(segment.content); + } + }); +}; + export const drawText = function (elem, textData) { let prevTextHeight = 0; let textHeight = 0; @@ -234,13 +301,19 @@ export const drawText = function (elem, textData) { } const text = line || ZERO_WIDTH_SPACE; + let textSpan; if (textData.tspan) { - const span = textElem.append('tspan'); - span.attr('x', textData.x); + textSpan = textElem.append('tspan'); + textSpan.attr('x', textData.x); if (textData.fill !== undefined) { - span.attr('fill', textData.fill); + textSpan.attr('fill', textData.fill); } - span.text(text); + } else { + textSpan = textElem; + } + + if (hasMarkdownLink(text)) { + drawMarkdownLinkText(textSpan, text); } else { textElem.text(text); } diff --git a/packages/mermaid/src/docs/syntax/sequenceDiagram.md b/packages/mermaid/src/docs/syntax/sequenceDiagram.md index 2357b9bf43..d15d3397d0 100644 --- a/packages/mermaid/src/docs/syntax/sequenceDiagram.md +++ b/packages/mermaid/src/docs/syntax/sequenceDiagram.md @@ -144,6 +144,8 @@ Messages can be of two displayed either solid or with a dotted line. [Actor][Arrow][Actor]:Message text ``` +### Arrow Types + There are ten types of arrows currently supported: | Type | Description | @@ -159,6 +161,21 @@ 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) | +### Links in Messages (v+) + +You can add one or more markdown-style links to messages. + +``` +[Actor][Arrow][Actor]:[Label](URL) +``` + +Example: + +```mermaid-example +sequenceDiagram + Alice->>John: Alice shared [MermaidJS](https://mermaid.js.org/) and [the Getting Started guide](https://mermaid.js.org/intro/getting-started.html) with John +``` + ## Activations It is possible to activate and deactivate an actor. (de)activation can be dedicated declarations: