Compare commits

..

48 Commits

Author SHA1 Message Date
darshanr0107
70c9e6e57a Merge branch 'develop' of https://github.com/mermaid-js/mermaid into fix-markdown-labels-6087 2025-10-14 12:54:25 +05:30
Shubham P
18f51eb14e Merge pull request #6843 from saurabhg772244/saurabh/fix-edge-animation-for-hand-dranw-shapes
Fixed edge animation for hand drawn shapes
2025-10-13 13:14:24 +00:00
Shubham P
2bb57bf7d2 Merge pull request #6989 from mermaid-js/subgraph-td-direction
6338: allow direction TD inside subgraphs
2025-10-13 13:13:46 +00:00
Shubham P
a6276daffd Merge pull request #7055 from mermaid-js/sequence-diagram-participant-name-parsing
6853:  prevent browser freeze caused by invalid participant name in sequenceDiagram
2025-10-13 13:12:08 +00:00
Justin Greywolf
d80a638e55 Merge pull request #6023 from yari-dewalt/fix_lollipop_stroke
Fix: Delete 'stroke: black' from lollipop marker so it matches themes and edge paths
2025-10-11 20:43:08 +00:00
Justin Greywolf
7a869c08a2 Merge pull request #6026 from yari-dewalt/bug/5669_class-diagram-fix-namespace-themes
Fix: Class diagram namespaces black lines, not responding to theme variables
2025-10-11 20:42:53 +00:00
Justin Greywolf
44e8cbb1de Merge branch 'develop' into bug/5669_class-diagram-fix-namespace-themes 2025-10-11 13:27:47 -07:00
Justin Greywolf
efe38b8425 Merge branch 'develop' into fix_lollipop_stroke 2025-10-11 13:26:59 -07:00
darshanr0107
1f8ba5591d fix: failing argos for mindmap , kanban diagrams
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-10 17:23:18 +05:30
darshanr0107
cd6f7a3019 Merge branch 'fix-markdown-labels-6087' of https://github.com/mermaid-js/mermaid into fix-markdown-labels-6087 2025-10-09 19:10:38 +05:30
darshanr0107
1f0cb96d16 Revert: rollback recent diagram utility and markdown label changes 2025-10-09 19:09:34 +05:30
autofix-ci[bot]
db4a837e9b [autofix.ci] apply automated fixes 2025-10-09 12:56:41 +00:00
darshanr0107
047b7fccc7 Merge branch 'fix-markdown-labels-6087' of https://github.com/mermaid-js/mermaid into fix-markdown-labels-6087 2025-10-09 18:20:50 +05:30
darshanr0107
a53c0d4305 refactor: remove diagram specific checks from utils
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-09 18:18:20 +05:30
autofix-ci[bot]
13f2040b37 [autofix.ci] apply automated fixes 2025-10-09 11:24:45 +00:00
darshanr0107
e351a7a488 fix: some argos , added checks for flowchart
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-09 16:48:35 +05:30
darshanr0107
6fecb985e8 fix: failing argos
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-09 11:50:44 +05:30
darshanr0107
69b338d8af fix: formatting issues
Co-authored-by: omkarht <omkar@mermaidchart.com>
2025-10-09 11:37:05 +05:30
autofix-ci[bot]
fe8597d0c8 [autofix.ci] apply automated fixes 2025-10-08 14:34:38 +00:00
darshanr0107
178ea182c3 Resolve merge conflicts with develop 2025-10-08 19:56:34 +05:30
darshanr0107
fa15ce8502 chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-08 13:23:28 +05:30
darshanr0107
6d0650918f fix: handle participant names with spaces correctly in sequenceDiagram
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-08 13:09:30 +05:30
darshanr0107
1a9d45abf0 fix: allow direction TD inside subgraphs
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-09-24 12:22:31 +05:30
Saurabh Gore
bbb93b263d Merge branch 'develop' into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-13 11:37:21 +05:30
saurabhg772244
4240340a18 Merge branch 'develop' of github.com:mermaid-js/mermaid into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-11 11:36:26 +05:30
omkarht
ca10a259fa Merge branch 'develop' into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-05 16:28:39 +05:30
Saurabh Gore
0ed9c65572 Merge branch 'develop' into saurabh/fix-edge-animation-for-hand-dranw-shapes 2025-09-05 12:56:27 +05:30
saurabhg772244
56cc12690f Added option to skip screenshot for cypress tests 2025-09-05 12:47:07 +05:30
autofix-ci[bot]
3a23372af4 [autofix.ci] apply automated fixes 2025-08-14 20:01:40 +00:00
Anthony Juckel
cd6f7192bf Use explicit markdown for unsupported markdown integration test 2025-08-14 14:56:27 -05:00
Anthony Juckel
ebb77578e8 Check for htmlLabels in the flowchart config 2025-08-14 14:54:52 -05:00
Anthony Juckel
4d76af679b Revert changes in this branch to knsv2.html 2025-08-14 14:54:21 -05:00
Anthony Juckel
b7b05f4a55 wip: some nullability changes...? 2025-08-14 12:27:21 -05:00
Anthony Juckel
44a6434e59 Apply non-markdown label change for subgroups 2025-08-14 12:27:21 -05:00
Anthony Juckel
37269b47b5 Default to markdown for nodes from metadata 2025-08-14 12:25:10 -05:00
Anthony Juckel
560abf4218 Only process node labels as markdown if properly delimited 2025-08-14 12:25:10 -05:00
Justin Greywolf
0943edc114 Update cypress/integration/rendering/flowchart.spec.js
Co-authored-by: Sidharth Vinod <github@sidharth.dev>
2025-08-14 12:25:10 -05:00
Knut Sveidqvist
40402a2bd9 Adding changeset 2025-08-14 12:25:10 -05:00
Knut Sveidqvist
dc51c027d6 Adding back create label for the cases where you do not want markdown text 2025-08-14 12:25:10 -05:00
saurabhg772244
e6fb4a84da code refactor 2025-08-11 16:32:03 +05:30
saurabhg772244
32723b2de1 Added change set 2025-08-11 15:59:07 +05:30
saurabhg772244
18703782ee Fixed edge animation for hand drawn shapes 2025-08-11 15:52:49 +05:30
autofix-ci[bot]
47297f7c26 [autofix.ci] apply automated fixes 2025-02-26 17:49:51 +00:00
Yari DeWalt
967aa0629e Merge branch 'develop' into bug/5669_class-diagram-fix-namespace-themes 2025-02-26 09:45:14 -08:00
Yari DeWalt
04b20a79b9 Merge branch 'develop' into fix_lollipop_stroke 2025-02-26 09:20:16 -08:00
yari-dewalt
4ff2ae9f4e Add styles for clusters and remove hard-coded styling for namespace nodes 2024-11-06 09:00:21 -08:00
Yari DeWalt
7a729e8f16 Merge branch 'develop' into fix_lollipop_stroke 2024-11-04 10:21:00 -08:00
yari-dewalt
3c7fd95617 Remove black stroke from lollipops so it matches edge paths 2024-11-04 10:15:51 -08:00
29 changed files with 279 additions and 105 deletions

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Support edge animation in hand drawn look

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Resolved parsing error where direction TD was not recognized within subgraphs

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Improve participant parsing and prevent recursive loops on invalid syntax

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Proper separation between strings and markdown strings

View File

@@ -6,6 +6,7 @@ interface CypressConfig {
listUrl?: boolean; listUrl?: boolean;
listId?: string; listId?: string;
name?: string; name?: string;
screenshot?: boolean;
} }
type CypressMermaidConfig = MermaidConfig & CypressConfig; type CypressMermaidConfig = MermaidConfig & CypressConfig;
@@ -90,7 +91,7 @@ export const renderGraph = (
export const openURLAndVerifyRendering = ( export const openURLAndVerifyRendering = (
url: string, url: string,
options: CypressMermaidConfig, { screenshot = true, ...options }: CypressMermaidConfig,
validation?: any validation?: any
): void => { ): void => {
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
@@ -103,7 +104,9 @@ export const openURLAndVerifyRendering = (
cy.get('svg').should(validation); cy.get('svg').should(validation);
} }
verifyScreenshot(name); if (screenshot) {
verifyScreenshot(name);
}
}; };
export const verifyScreenshot = (name: string): void => { export const verifyScreenshot = (name: string): void => {

View File

@@ -1029,4 +1029,19 @@ graph TD
} }
); );
}); });
it('FDH49: should add edge animation', () => {
renderGraph(
`
flowchart TD
A(["Start"]) L_A_B_0@--> B{"Decision"}
B --> C["Option A"] & D["Option B"]
style C stroke-width:4px,stroke-dasharray: 5
L_A_B_0@{ animation: slow }
L_B_D_0@{ animation: fast }`,
{ look: 'handDrawn', screenshot: false }
);
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
});
}); });

View File

@@ -1174,8 +1174,8 @@ end
end end
githost["Github, Gitlab, BitBucket, etc."] githost["Github, Gitlab, BitBucket, etc."]
githost2["\`Github, Gitlab, BitBucket, etc.\`"] githost2["\`Github, Gitlab, BitBucket, etc.\`"]
a["1."] a["\`1.\`"]
b["- x"] b["\`- x\`"]
`; `;
it('should render raw strings', () => { it('should render raw strings', () => {

View File

@@ -774,6 +774,21 @@ describe('Graph', () => {
expect(svg).to.not.have.attr('style'); expect(svg).to.not.have.attr('style');
}); });
}); });
it('40: should add edge animation', () => {
renderGraph(
`
flowchart TD
A(["Start"]) L_A_B_0@--> B{"Decision"}
B --> C["Option A"] & D["Option B"]
style C stroke-width:4px,stroke-dasharray: 5
L_A_B_0@{ animation: slow }
L_B_D_0@{ animation: fast }`,
{ screenshot: false }
);
// Verify animation classes are applied to both edges
cy.get('path#L_A_B_0').should('have.class', 'edge-animation-slow');
cy.get('path#L_B_D_0').should('have.class', 'edge-animation-fast');
});
it('58: handle styling with style expressions', () => { it('58: handle styling with style expressions', () => {
imgSnapshotTest( imgSnapshotTest(
` `
@@ -973,4 +988,64 @@ graph TD
} }
); );
}); });
it('70: should render a subgraph with direction TD', () => {
imgSnapshotTest(
`
flowchart LR
subgraph A
direction TD
a --> b
end
`,
{
fontFamily: 'courier',
}
);
});
it('#5824: should be able to render string and markdown labels', () => {
imgSnapshotTest(
`
flowchart TB
mermaid{"What is\nyourmermaid version?"} --> v10["<11"] --"\`<**1**1\`"--> fine["No bug"]
mermaid --> v11[">= v11"] -- ">= v11" --> broken["Affected by https://github.com/mermaid-js/mermaid/issues/5824"]
subgraph subgraph1["\`How to fix **fix**\`"]
broken --> B["B"]
end
githost["Github, Gitlab, BitBucket, etc."]
githost2["\`Github, Gitlab, BitBucket, etc.\`"]
a["1."]
b["- x"]
`,
{
flowchart: { htmlLabels: true },
securityLevel: 'loose',
}
);
});
it('69: should render subgraphs with adhoc list headings', () => {
imgSnapshotTest(
`
graph TB
subgraph "1. first"
a1-->a2
end
subgraph 2. second
b1-->b2
end
`,
{ fontFamily: 'courier' }
);
});
it('70: should render subgraphs with markdown headings', () => {
imgSnapshotTest(
`
graph TB
subgraph "\`**strong**\`"
a1-->a2
end
`,
{ fontFamily: 'courier' }
);
});
}); });

View File

@@ -603,6 +603,10 @@
</div> </div>
<div class="test"> <div class="test">
<pre class="mermaid"> <pre class="mermaid">
---
config:
theme: dark
---
classDiagram classDiagram
test ()--() test2 test ()--() test2
</pre> </pre>

View File

@@ -10,7 +10,7 @@
# Interface: ParseOptions # Interface: ParseOptions
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88) Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
## Properties ## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mer
> `optional` **suppressErrors**: `boolean` > `optional` **suppressErrors**: `boolean`
Defined in: [packages/mermaid/src/types.ts:93](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L93) Defined in: [packages/mermaid/src/types.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L94)
If `true`, parse will return `false` instead of throwing error when the diagram is invalid. If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
The `parseError` function will not be called. The `parseError` function will not be called.

View File

@@ -10,7 +10,7 @@
# Interface: ParseResult # Interface: ParseResult
Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L96) Defined in: [packages/mermaid/src/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L97)
## Properties ## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mer
> **config**: [`MermaidConfig`](MermaidConfig.md) > **config**: [`MermaidConfig`](MermaidConfig.md)
Defined in: [packages/mermaid/src/types.ts:104](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L104) Defined in: [packages/mermaid/src/types.ts:105](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L105)
The config passed as YAML frontmatter or directives The config passed as YAML frontmatter or directives
@@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives
> **diagramType**: `string` > **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L100) Defined in: [packages/mermaid/src/types.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L101)
The diagram type, e.g. 'flowchart', 'sequence', etc. The diagram type, e.g. 'flowchart', 'sequence', etc.

View File

@@ -10,7 +10,7 @@
# Interface: RenderResult # Interface: RenderResult
Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L114) Defined in: [packages/mermaid/src/types.ts:115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L115)
## Properties ## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/me
> `optional` **bindFunctions**: (`element`) => `void` > `optional` **bindFunctions**: (`element`) => `void`
Defined in: [packages/mermaid/src/types.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L132) Defined in: [packages/mermaid/src/types.ts:133](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L133)
Bind function to be called after the svg has been inserted into the DOM. Bind function to be called after the svg has been inserted into the DOM.
This is necessary for adding event listeners to the elements in the svg. This is necessary for adding event listeners to the elements in the svg.
@@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
> **diagramType**: `string` > **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L122) Defined in: [packages/mermaid/src/types.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L123)
The diagram type, e.g. 'flowchart', 'sequence', etc. The diagram type, e.g. 'flowchart', 'sequence', etc.
@@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
> **svg**: `string` > **svg**: `string`
Defined in: [packages/mermaid/src/types.ts:118](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L118) Defined in: [packages/mermaid/src/types.ts:119](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L119)
The svg code for the rendered graph. The svg code for the rendered graph.

View File

@@ -627,7 +627,7 @@ export class ClassDB implements DiagramDB {
padding: config.class!.padding ?? 16, padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider] // parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect', shape: 'rect',
cssStyles: ['fill: none', 'stroke: black'], cssStyles: [],
look: config.look, look: config.look,
}; };
nodes.push(node); nodes.push(node);

View File

@@ -13,6 +13,30 @@ const getStyles = (options) =>
} }
.cluster-label text {
fill: ${options.titleColor};
}
.cluster-label span {
color: ${options.titleColor};
}
.cluster-label span p {
background-color: transparent;
}
.cluster rect {
fill: ${options.clusterBkg};
stroke: ${options.clusterBorder};
stroke-width: 1px;
}
.cluster text {
fill: ${options.titleColor};
}
.cluster span {
color: ${options.titleColor};
}
.nodeLabel, .edgeLabel { .nodeLabel, .edgeLabel {
color: ${options.classText}; color: ${options.classText};
} }

View File

@@ -85,6 +85,17 @@ export class FlowDB implements DiagramDB {
return common.sanitizeText(txt, this.config); return common.sanitizeText(txt, this.config);
} }
private sanitizeNodeLabelType(labelType?: string) {
switch (labelType) {
case 'markdown':
case 'string':
case 'text':
return labelType;
default:
return 'markdown';
}
}
/** /**
* Function to lookup domId from id in the graph definition. * Function to lookup domId from id in the graph definition.
* *
@@ -208,6 +219,7 @@ export class FlowDB implements DiagramDB {
if (doc?.label) { if (doc?.label) {
vertex.text = doc?.label; vertex.text = doc?.label;
vertex.labelType = this.sanitizeNodeLabelType(doc?.labelType);
} }
if (doc?.icon) { if (doc?.icon) {
vertex.icon = doc?.icon; vertex.icon = doc?.icon;
@@ -267,7 +279,7 @@ export class FlowDB implements DiagramDB {
if (edge.text.startsWith('"') && edge.text.endsWith('"')) { if (edge.text.startsWith('"') && edge.text.endsWith('"')) {
edge.text = edge.text.substring(1, edge.text.length - 1); edge.text = edge.text.substring(1, edge.text.length - 1);
} }
edge.labelType = linkTextObj.type; edge.labelType = this.sanitizeNodeLabelType(linkTextObj.type);
} }
if (type !== undefined) { if (type !== undefined) {
@@ -702,7 +714,7 @@ You have to call mermaid.initialize.`
title: title.trim(), title: title.trim(),
classes: [], classes: [],
dir, dir,
labelType: _title.type, labelType: this.sanitizeNodeLabelType(_title?.type),
}; };
log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir); log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir);
@@ -1012,6 +1024,7 @@ You have to call mermaid.initialize.`
const baseNode = { const baseNode = {
id: vertex.id, id: vertex.id,
label: vertex.text, label: vertex.text,
labelType: vertex.labelType,
labelStyle: '', labelStyle: '',
parentId, parentId,
padding: config.flowchart?.padding || 8, padding: config.flowchart?.padding || 8,
@@ -1088,6 +1101,7 @@ You have to call mermaid.initialize.`
id: subGraph.id, id: subGraph.id,
label: subGraph.title, label: subGraph.title,
labelStyle: '', labelStyle: '',
labelType: subGraph.labelType,
parentId: parentDB.get(subGraph.id), parentId: parentDB.get(subGraph.id),
padding: 8, padding: 8,
cssCompiledStyles: this.getCompiledStyles(subGraph.classes), cssCompiledStyles: this.getCompiledStyles(subGraph.classes),
@@ -1119,6 +1133,7 @@ You have to call mermaid.initialize.`
end: rawEdge.end, end: rawEdge.end,
type: rawEdge.type ?? 'normal', type: rawEdge.type ?? 'normal',
label: rawEdge.text, label: rawEdge.text,
labelType: rawEdge.labelType,
labelpos: 'c', labelpos: 'c',
thickness: rawEdge.stroke, thickness: rawEdge.stroke,
minlen: rawEdge.length, minlen: rawEdge.length,

View File

@@ -140,6 +140,7 @@ that id.
.*direction\s+BT[^\n]* return 'direction_bt'; .*direction\s+BT[^\n]* return 'direction_bt';
.*direction\s+RL[^\n]* return 'direction_rl'; .*direction\s+RL[^\n]* return 'direction_rl';
.*direction\s+LR[^\n]* return 'direction_lr'; .*direction\s+LR[^\n]* return 'direction_lr';
.*direction\s+TD[^\n]* return 'direction_td';
[^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; } [^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; }
[0-9]+ return 'NUM'; [0-9]+ return 'NUM';
@@ -626,6 +627,8 @@ direction
{ $$={stmt:'dir', value:'RL'};} { $$={stmt:'dir', value:'RL'};}
| direction_lr | direction_lr
{ $$={stmt:'dir', value:'LR'};} { $$={stmt:'dir', value:'LR'};}
| direction_td
{ $$={stmt:'dir', value:'TD'};}
; ;
%% %%

View File

@@ -309,4 +309,21 @@ describe('when parsing subgraphs', function () {
expect(subgraphA.nodes).toContain('a'); expect(subgraphA.nodes).toContain('a');
expect(subgraphA.nodes).not.toContain('c'); expect(subgraphA.nodes).not.toContain('c');
}); });
it('should correctly parse direction TD inside a subgraph', function () {
const res = flow.parser.parse(`
graph LR
subgraph WithTD
direction TD
A1 --> A2
end
`);
const subgraphs = flow.parser.yy.getSubGraphs();
expect(subgraphs.length).toBe(1);
const subgraph = subgraphs[0];
expect(subgraph.dir).toBe('TD');
expect(subgraph.nodes).toContain('A1');
expect(subgraph.nodes).toContain('A2');
});
}); });

View File

@@ -29,7 +29,7 @@ export interface FlowVertex {
domId: string; domId: string;
haveCallback?: boolean; haveCallback?: boolean;
id: string; id: string;
labelType: 'text'; labelType: 'markdown' | 'string' | 'text';
link?: string; link?: string;
linkTarget?: string; linkTarget?: string;
props?: any; props?: any;
@@ -62,7 +62,7 @@ export interface FlowEdge {
style?: string[]; style?: string[];
length?: number; length?: number;
text: string; text: string;
labelType: 'text'; labelType: 'markdown' | 'string' | 'text';
classes: string[]; classes: string[];
id?: string; id?: string;
animation?: 'fast' | 'slow'; animation?: 'fast' | 'slow';

View File

@@ -62,6 +62,7 @@ const getData = function () {
const node = { const node = {
id: section.id, id: section.id,
label: sanitizeText(section.label ?? '', conf), label: sanitizeText(section.label ?? '', conf),
labelType: 'markdown',
isGroup: true, isGroup: true,
ticket: section.ticket, ticket: section.ticket,
shape: 'kanbanSection', shape: 'kanbanSection',
@@ -76,6 +77,7 @@ const getData = function () {
id: item.id, id: item.id,
parentId: section.id, parentId: section.id,
label: sanitizeText(item.label ?? '', conf), label: sanitizeText(item.label ?? '', conf),
labelType: 'markdown',
isGroup: false, isGroup: false,
ticket: item?.ticket, ticket: item?.ticket,
priority: item?.priority, priority: item?.priority,

View File

@@ -260,6 +260,7 @@ export class MindmapDB {
id: node.id.toString(), id: node.id.toString(),
domId: 'node_' + node.id.toString(), domId: 'node_' + node.id.toString(),
label: node.descr, label: node.descr,
labelType: 'markdown',
isGroup: false, isGroup: false,
shape: getShapeFromType(node.type), shape: getShapeFromType(node.type),
width: node.width, width: node.width,

View File

@@ -16,6 +16,7 @@ export interface MindmapNode {
x?: number; x?: number;
y?: number; y?: number;
isRoot?: boolean; isRoot?: boolean;
labelType?: string;
} }
export type FilledMindMapNode = RequiredDeep<MindmapNode>; export type FilledMindMapNode = RequiredDeep<MindmapNode>;

View File

@@ -32,13 +32,14 @@
<CONFIG>[^\}]+ { return 'CONFIG_CONTENT'; } <CONFIG>[^\}]+ { return 'CONFIG_CONTENT'; }
<CONFIG>\} { this.popState(); this.popState(); return 'CONFIG_END'; } <CONFIG>\} { this.popState(); this.popState(); return 'CONFIG_END'; }
<ID>[^\<->\->:\n,;@\s]+(?=\@\{) { yytext = yytext.trim(); return 'ACTOR'; } <ID>[^\<->\->:\n,;@\s]+(?=\@\{) { yytext = yytext.trim(); return 'ACTOR'; }
<ID>[^\<->\->:\n,;@]+?([\-]*[^\<->\->:\n,;@]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; } <ID>[^<>:\n,;@\s]+(?=\s+as\s) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
<ID>[^<>:\n,;@]+(?=\s*[\n;#]|$) { yytext = yytext.trim(); this.popState(); return 'ACTOR'; }
<ID>[^<>:\n,;@]*\<[^\n]* { this.popState(); return 'INVALID'; }
"box" { this.begin('LINE'); return 'box'; } "box" { this.begin('LINE'); return 'box'; }
"participant" { this.begin('ID'); return 'participant'; } "participant" { this.begin('ID'); return 'participant'; }
"actor" { this.begin('ID'); return 'participant_actor'; } "actor" { this.begin('ID'); return 'participant_actor'; }
"create" return 'create'; "create" return 'create';
"destroy" { this.begin('ID'); return 'destroy'; } "destroy" { this.begin('ID'); return 'destroy'; }
<ID>[^<\->\->:\n,;]+?([\-]*[^<\->\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; } <ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; } <ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
"loop" { this.begin('LINE'); return 'loop'; } "loop" { this.begin('LINE'); return 'loop'; }
@@ -145,6 +146,7 @@ line
: SPACE statement { $$ = $2 } : SPACE statement { $$ = $2 }
| statement { $$ = $1 } | statement { $$ = $1 }
| NEWLINE { $$=[]; } | NEWLINE { $$=[]; }
| INVALID { $$=[]; }
; ;
box_section box_section
@@ -411,4 +413,4 @@ text2
: TXT {$$ = yy.parseMessage($1.trim().substring(1)) } : TXT {$$ = yy.parseMessage($1.trim().substring(1)) }
; ;
%% %%

View File

@@ -2609,5 +2609,17 @@ Bob->>Alice:Got it!
expect(actors.get('E').type).toBe('entity'); expect(actors.get('E').type).toBe('entity');
expect(actors.get('E').description).toBe('E'); expect(actors.get('E').description).toBe('E');
}); });
it('should handle fail parsing when alias token causes conflicts in participant definition', async () => {
let error = false;
try {
await Diagram.fromText(`
sequenceDiagram
participant SAS MyServiceWithMoreThan20Chars <br> service decription
`);
} catch (e) {
error = true;
}
expect(error).toBe(true);
});
}); });
}); });

View File

@@ -27,53 +27,6 @@ import { log } from '../../../logger.js';
import { getSubGraphTitleMargins } from '../../../utils/subGraphTitleMargins.js'; import { getSubGraphTitleMargins } from '../../../utils/subGraphTitleMargins.js';
import { getConfig } from '../../../diagram-api/diagramAPI.js'; import { getConfig } from '../../../diagram-api/diagramAPI.js';
/**
* Apply absolute note positioning after dagre layout
* This fixes the issue where TB and LR directions position notes differently
* by making note positioning truly absolute
*/
const positionNotes = (graph) => {
const noteStatePairs = [];
graph.nodes().forEach((nodeId) => {
const node = graph.node(nodeId);
if (node.position && node.shape === 'note') {
const edges = graph.nodeEdges(nodeId);
for (const edge of edges) {
const otherNodeId = edge.v === nodeId ? edge.w : edge.v;
const otherNode = graph.node(otherNodeId);
if (otherNode && otherNode.shape !== 'note' && otherNode.shape !== 'noteGroup') {
noteStatePairs.push({
noteId: nodeId,
noteNode: node,
stateId: otherNodeId,
stateNode: otherNode,
position: node.position,
});
}
}
}
});
noteStatePairs.forEach(({ noteNode, stateNode, position }) => {
const spacing = 60;
let noteX = noteNode.x;
let noteY = stateNode.y;
if (position === 'right of') {
noteX = stateNode.x + stateNode.width / 2 + spacing + noteNode.width / 2;
} else if (position === 'left of') {
noteX = stateNode.x - stateNode.width / 2 - spacing - noteNode.width / 2;
}
noteNode.x = noteX;
noteNode.y = noteY;
});
};
const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, siteConfig) => { const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, siteConfig) => {
log.warn('Graph in recursive render:XAX', graphlibJson.write(graph), parentCluster); log.warn('Graph in recursive render:XAX', graphlibJson.write(graph), parentCluster);
const dir = graph.graph().rankdir; const dir = graph.graph().rankdir;
@@ -211,9 +164,6 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
dagreLayout(graph); dagreLayout(graph);
// Apply absolute note positioning after dagre layout
positionNotes(graph);
log.info('Graph after layout:', JSON.stringify(graphlibJson.write(graph))); log.info('Graph after layout:', JSON.stringify(graphlibJson.write(graph)));
// Move the nodes to the correct place // Move the nodes to the correct place
let diff = 0; let diff = 0;

View File

@@ -30,11 +30,17 @@ const rect = async (parent, node) => {
// Create the label and insert it after the rect // Create the label and insert it after the rect
const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label '); const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label ');
const text = await createText(labelEl, node.label, { let text;
style: node.labelStyle, if (node.labelType === 'markdown') {
useHtmlLabels, text = await createText(labelEl, node.label, {
isNode: true, style: node.labelStyle,
}); useHtmlLabels,
isNode: true,
});
} else {
const labelElement = await createLabel(node.label, node.labelStyle, false, true);
text = labelEl.node()?.appendChild(labelElement);
}
// Get the size of the label // Get the size of the label
let bbox = text.getBBox(); let bbox = text.getBBox();

View File

@@ -29,7 +29,7 @@ import {
import rough from 'roughjs'; import rough from 'roughjs';
import createLabel from './createLabel.js'; import createLabel from './createLabel.js';
import { addEdgeMarkers } from './edgeMarker.ts'; import { addEdgeMarkers } from './edgeMarker.ts';
import { isLabelStyle, styles2String } from './shapes/handDrawnShapeStyles.js'; import { isLabelStyle } from './shapes/handDrawnShapeStyles.js';
export const edgeLabels = new Map(); export const edgeLabels = new Map();
export const terminalLabels = new Map(); export const terminalLabels = new Map();
@@ -47,14 +47,16 @@ export const getLabelStyles = (styleArray) => {
export const insertEdgeLabel = async (elem, edge) => { export const insertEdgeLabel = async (elem, edge) => {
let useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels); let useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
const { labelStyles } = styles2String(edge); const labelElement =
edge.labelStyle = labelStyles; edge.labelType === 'markdown'
const labelElement = await createText(elem, edge.label, { ? await createText(elem, edge.label, {
style: edge.labelStyle, style: getLabelStyles(edge.labelStyle),
useHtmlLabels, useHtmlLabels,
addSvgBackground: true, addSvgBackground: true,
isNode: false, isNode: false,
}); })
: await createLabel(edge.label, getLabelStyles(edge.labelStyle), undefined, false);
log.info('abc82', edge, edge.labelType); log.info('abc82', edge, edge.labelType);
// Create outer g, edgeLabel, this will be positioned after graph layout // Create outer g, edgeLabel, this will be positioned after graph layout
@@ -605,6 +607,14 @@ export const insertEdge = function (
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style]; const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:')); let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:'));
let animationClass = '';
if (edge.animate) {
animationClass = 'edge-animation-fast';
}
if (edge.animation) {
animationClass = 'edge-animation-' + edge.animation;
}
let animatedEdge = false; let animatedEdge = false;
if (edge.look === 'handDrawn') { if (edge.look === 'handDrawn') {
const rc = rough.svg(elem); const rc = rough.svg(elem);
@@ -620,7 +630,13 @@ export const insertEdge = function (
svgPath = select(svgPathNode) svgPath = select(svgPathNode)
.select('path') .select('path')
.attr('id', edge.id) .attr('id', edge.id)
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '')) .attr(
'class',
' ' +
strokeClasses +
(edge.classes ? ' ' + edge.classes : '') +
(animationClass ? ' ' + animationClass : '')
)
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : ''); .attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
let d = svgPath.attr('d'); let d = svgPath.attr('d');
svgPath.attr('d', d); svgPath.attr('d', d);
@@ -628,13 +644,6 @@ export const insertEdge = function (
} else { } else {
const stylesFromClasses = edgeClassStyles.join(';'); const stylesFromClasses = edgeClassStyles.join(';');
const styles = edgeStyles ? edgeStyles.reduce((acc, style) => acc + style + ';', '') : ''; const styles = edgeStyles ? edgeStyles.reduce((acc, style) => acc + style + ';', '') : '';
let animationClass = '';
if (edge.animate) {
animationClass = ' edge-animation-fast';
}
if (edge.animation) {
animationClass = ' edge-animation-' + edge.animation;
}
const pathStyle = const pathStyle =
(stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) + (stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) +
@@ -646,7 +655,10 @@ export const insertEdge = function (
.attr('id', edge.id) .attr('id', edge.id)
.attr( .attr(
'class', 'class',
' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '') + (animationClass ?? '') ' ' +
strokeClasses +
(edge.classes ? ' ' + edge.classes : '') +
(animationClass ? ' ' + animationClass : '')
) )
.attr('style', pathStyle); .attr('style', pathStyle);

View File

@@ -130,7 +130,6 @@ const lollipop = (elem, type, id) => {
.attr('markerHeight', 240) .attr('markerHeight', 240)
.attr('orient', 'auto') .attr('orient', 'auto')
.append('circle') .append('circle')
.attr('stroke', 'black')
.attr('fill', 'transparent') .attr('fill', 'transparent')
.attr('cx', 7) .attr('cx', 7)
.attr('cy', 7) .attr('cy', 7)
@@ -147,7 +146,6 @@ const lollipop = (elem, type, id) => {
.attr('markerHeight', 240) .attr('markerHeight', 240)
.attr('orient', 'auto') .attr('orient', 'auto')
.append('circle') .append('circle')
.attr('stroke', 'black')
.attr('fill', 'transparent') .attr('fill', 'transparent')
.attr('cx', 7) .attr('cx', 7)
.attr('cy', 7) .attr('cy', 7)

View File

@@ -1,3 +1,4 @@
import createLabel from '../createLabel.js';
import { createText } from '../../createText.js'; import { createText } from '../../createText.js';
import type { Node } from '../../types.js'; import type { Node } from '../../types.js';
import { getConfig } from '../../../diagram-api/diagramAPI.js'; import { getConfig } from '../../../diagram-api/diagramAPI.js';
@@ -13,7 +14,7 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
_classes?: string _classes?: string
) => { ) => {
let cssClasses; let cssClasses;
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.htmlLabels); const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels);
if (!_classes) { if (!_classes) {
cssClasses = 'node default'; cssClasses = 'node default';
} else { } else {
@@ -40,14 +41,26 @@ export const labelHelper = async <T extends SVGGraphicsElement>(
label = typeof node.label === 'string' ? node.label : node.label[0]; label = typeof node.label === 'string' ? node.label : node.label[0];
} }
const text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), { let text;
useHtmlLabels, if (node.labelType === 'markdown') {
width: node.width || getConfig().flowchart?.wrappingWidth, text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
// @ts-expect-error -- This is currently not used. Should this be `classes` instead? useHtmlLabels,
cssClasses: 'markdown-node-label', width: node.width || getConfig().flowchart?.wrappingWidth,
style: node.labelStyle, // @ts-expect-error -- This is currently not used. Should this be `classes` instead?
addSvgBackground: !!node.icon || !!node.img, cssClasses: 'markdown-node-label',
}); style: node.labelStyle,
addSvgBackground: !!node.icon || !!node.img,
});
} else {
const labelElement = await createLabel(
sanitizeText(decodeEntities(label), getConfig()),
node.labelStyle,
false,
true
);
text = labelEl.node()?.appendChild(labelElement);
}
// Get the size of the label // Get the size of the label
let bbox = text.getBBox(); let bbox = text.getBBox();
const halfPadding = (node?.padding ?? 0) / 2; const halfPadding = (node?.padding ?? 0) / 2;

View File

@@ -1,6 +1,7 @@
export interface NodeMetaData { export interface NodeMetaData {
shape?: string; shape?: string;
label?: string; label?: string;
labelType?: string;
icon?: string; icon?: string;
form?: string; form?: string;
pos?: 't' | 'b'; pos?: 't' | 'b';