Compare commits

..

52 Commits

Author SHA1 Message Date
Shubham P
a8d89db397 Merge branch 'develop' into tooltip-positioning-issue 2025-11-11 09:58:43 +05:30
Shubham P
6b9f26dac8 Merge pull request #7080 from mermaid-js/7079-c4context-componentqueue-ext-lexical-error
7079: add missing support for ComponentQueue_Ext in C4Context diagrams
2025-11-11 04:07:06 +00:00
Shubham P
ea590cdafe Merge pull request #7075 from mermaid-js/6889-fix-escaped-p-tags-in-sandbox-mode
6889: Fix escaped <p> tags in labels when securityLevel is set to "sandbox"
2025-11-11 04:05:37 +00:00
Shubham P
f3769c70bc Merge branch 'develop' into 7079-c4context-componentqueue-ext-lexical-error 2025-11-11 09:24:14 +05:30
Shubham P
4cf4d15197 Merge branch 'develop' into 6889-fix-escaped-p-tags-in-sandbox-mode 2025-11-11 09:23:23 +05:30
Shubham P
9448aec481 Merge branch 'develop' into tooltip-positioning-issue 2025-11-11 09:22:29 +05:30
Shubham P
c02cf92656 Merge pull request #7149 from mermaid-js/renovate/patch-dompurify
fix(deps): update dependency dompurify to ^3.3.0
2025-11-10 10:15:31 +00:00
Shubham P
3a1266892d Merge pull request #7148 from mermaid-js/renovate/patch-all-patch
fix(deps): update all patch dependencies (patch)
2025-11-10 10:15:16 +00:00
renovate[bot]
67e81de557 fix(deps): update dependency dompurify to ^3.3.0 2025-11-10 02:54:45 +00:00
renovate[bot]
847b3aa24e fix(deps): update all patch dependencies 2025-11-10 02:54:19 +00:00
Sidharth Vinod
85a13da40f Merge pull request #7138 from mermaid-js/update-timings
Update E2E Timings
2025-11-07 21:42:11 +09:00
darshanr0107
9ec0e8f932 Merge branch 'develop' of https://github.com/mermaid-js/mermaid into 6889-fix-escaped-p-tags-in-sandbox-mode 2025-11-07 11:58:38 +05:30
darshanr0107
9585ee7533 chore:add e2e test
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-07 11:46:41 +05:30
github-actions[bot]
1269486124 chore: update E2E timings 2025-11-07 04:15:01 +00:00
Shubham P
f45ea0c60e Merge pull request #7096 from mermaid-js/renovate/npm-vite-vulnerability
chore(deps): update dependency vite to v7.1.11 [security]
2025-11-06 14:52:25 +00:00
renovate[bot]
d20955a56a chore(deps): update dependency vite to v7.1.11 [security] 2025-11-06 12:46:25 +00:00
Shubham P
fb66b3fbe3 Merge pull request #7049 from mermaid-js/renovate/major-eslint
chore(deps): update eslint (major)
2025-11-06 12:31:47 +00:00
Shubham P
82ea5d63bb Merge pull request #7017 from mermaid-js/renovate/peter-evans-create-pull-request-digest
chore(deps): update peter-evans/create-pull-request digest to 0edc001
2025-11-06 12:30:09 +00:00
renovate[bot]
881e74087a chore(deps): update eslint 2025-11-06 12:06:45 +00:00
renovate[bot]
09920c0497 chore(deps): update peter-evans/create-pull-request digest to 0edc001 2025-11-06 12:05:55 +00:00
Shubham P
8065d65cd7 Merge pull request #6973 from mermaid-js/renovate/patch-dompurify
fix(deps): update dependency dompurify to ^3.2.7
2025-11-06 11:52:40 +00:00
renovate[bot]
09b841f781 fix(deps): update dependency dompurify to ^3.2.7 2025-11-06 10:46:44 +00:00
Shubham P
d0f9dc0c9b Merge pull request #7018 from mermaid-js/renovate/patch-all-patch
fix(deps): update all patch dependencies (patch)
2025-11-06 10:28:10 +00:00
renovate[bot]
15e2824d53 fix(deps): update all patch dependencies 2025-11-06 10:04:11 +00:00
Shubham P
7eb582e860 Merge pull request #7135 from mermaid-js/fix/er-numeric-entity-test-conflict
refactor: update test description for standalone numeric entities in ER diagram
2025-11-06 09:51:04 +00:00
omkarht
6ca928f31f refactor: update test description for standalone numeric entities in ER diagram 2025-11-06 15:05:58 +05:30
darshanr0107
983120d945 fix: add test case for C4Context diagram with ComponentQueue_Ext
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-11-05 17:18:34 +05:30
Sidharth Vinod
dfd59470dc Merge pull request #7129 from mermaid-js/sidv/injectVersion
feat: Optimize bundling to remove shipping package.json inside mermaid's JS bundle.
2025-11-04 18:42:32 +00:00
Sidharth Vinod
4aac6fa448 Merge pull request #7128 from CNOCTAVE/develop
add doc to use marmaid.js in GNU Octave
2025-11-04 17:42:28 +00:00
CNOCTAVE
5f96f80efb Merge branch 'develop' of https://github.com/CNOCTAVE/mermaid into develop 2025-11-05 01:16:47 +08:00
CNOCTAVE
545801e144 Update integrations-community.md 2025-11-05 01:16:17 +08:00
CNOCTAVE
0bd74759cc Merge branch 'develop' into develop 2025-11-05 01:13:00 +08:00
CNOCTAVE
e4cf266c1d remove changeset
remove changeset
2025-11-05 01:12:15 +08:00
CNOCTAVE
c0e1662e50 Update packages/mermaid/src/docs/ecosystem/integrations-community.md
Co-authored-by: Sidharth Vinod <github@sidharth.dev>
2025-11-05 01:08:04 +08:00
Sidharth Vinod
b76ccae065 feat: Optimize bundling to remove shipping package.json inside mermaid's JS bundle.
Move all build time injected variables to `injected.` namespace to avoid conflicts.
2025-11-04 23:49:54 +07:00
autofix-ci[bot]
7b0763f262 [autofix.ci] apply automated fixes 2025-11-04 12:43:03 +00:00
CNOCTAVE
38c289818c feat: add doc to use marmaid.js in GNU Octave
feat: add doc to use marmaid.js in GNU Octave. Resolve #7073
2025-11-04 20:34:13 +08:00
CNOCTAVE
57530076aa add doc to use marmaid.js in GNU Octave
add doc to use marmaid.js in GNU Octave. Resolve #7073
2025-11-04 20:29:53 +08:00
CNOCTAVE
f2d7877c7a Merge branch 'develop' of https://github.com/CNOCTAVE/mermaid into develop 2025-11-04 20:20:28 +08:00
darshanr0107
fcd2791b2d fix: Use DOMPurify to sanitize HTML content
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-16 11:52:26 +05:30
darshanr0107
feed9d75bb refactor: use a shared utility function for creating tooltip
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-15 19:48:52 +05:30
darshanr0107
835de0012d fix:ComponentQueue_Ext throws lexical error
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-14 19:00:17 +05:30
darshanr0107
96a766dcdb chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-13 13:42:41 +05:30
darshanr0107
39d7ebd32e fix: escaped p tags in sandbox mode
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-13 13:16:58 +05:30
CNOCTAVE
34f40f0794 add doc to use marmaid.js in GNU Octave 2025-10-13 15:11:44 +08:00
CNOCTAVE
32ac2c689d add doc to use marmaid.js in GNU Octave 2025-10-13 15:02:46 +08:00
CNOCTAVE
dbcadc1d0b add doc to use marmaid.js in GNU Octave 2025-10-13 13:56:04 +08:00
darshanr0107
f356836f71 Merge branch 'tooltip-positioning-issue' of https://github.com/mermaid-js/mermaid into tooltip-positioning-issue 2025-10-07 12:53:22 +05:30
darshanr0107
ff15e51d2e chore: added changeset
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-07 12:53:10 +05:30
autofix-ci[bot]
ddd4763db2 [autofix.ci] apply automated fixes 2025-10-07 06:50:56 +00:00
darshanr0107
6670ad7229 fix : escape HTML in tooltip titles to prevent DOM injection
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-10-07 12:15:32 +05:30
darshanr0107
b4a5fe6c45 fix: tooltip appears at bottom of page instead of near hovered element
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-10-07 11:56:26 +05:30
47 changed files with 2486 additions and 1734 deletions

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Prevent HTML tags from being escaped in sandbox label rendering

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Support ComponentQueue_Ext to prevent parsing error

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Correct tooltip placement to appear near hovered element

View File

@@ -1,8 +0,0 @@
---
'mermaid': minor
---
feat: Support `handDrawn` look for all diagrams
This uses svg2roughjs on diagrams that do not natively support roughjs.
So your mileage may vary.

View File

@@ -71,6 +71,9 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const outFileName = getFileName(name, options);
const { dependencies, version } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
const output: BuildOptions = buildOptions({
...rest,
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
@@ -82,15 +85,13 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
define: {
// This needs to be stringified for esbuild
includeLargeFeatures: `${includeLargeFeatures}`,
'injected.includeLargeFeatures': `${includeLargeFeatures}`,
'injected.version': `'${version}'`,
'import.meta.vitest': 'undefined',
},
});
if (core) {
const { dependencies } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
// Ignore dependencies and any dependencies of dependencies

View File

@@ -58,7 +58,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT
- name: Commit and create pull request
uses: peter-evans/create-pull-request@915d841dae6a4f191bb78faf61a257411d7be4d2
uses: peter-evans/create-pull-request@0edc001d28a2959cd7a6b505629f1d82f0a6e67d
with:
add-paths: |
cypress/timings.json

View File

@@ -20,7 +20,7 @@ jobs:
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif

View File

@@ -78,6 +78,8 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
},
define: {
'import.meta.vitest': 'undefined',
'injected.includeLargeFeatures': 'true',
'injected.version': `'0.0.0'`,
},
resolve: {
extensions: [],
@@ -94,10 +96,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
}),
...visualizerOptions(packageName, core),
],
define: {
// Needs to be string
includeLargeFeatures: 'true',
},
};
if (watch && config.build) {

View File

@@ -98,12 +98,21 @@ export const openURLAndVerifyRendering = (
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
// cspell:ignore viewbox
cy.get('svg').should('not.have.attr', 'viewbox');
if (validation) {
cy.get('svg').should(validation);
// Handle sandbox mode where SVG is inside an iframe
if (options.securityLevel === 'sandbox') {
cy.get('iframe').should('be.visible');
if (validation) {
cy.get('iframe').should(validation);
}
} else {
cy.get('svg').should('be.visible');
// cspell:ignore viewbox
cy.get('svg').should('not.have.attr', 'viewbox');
if (validation) {
cy.get('svg').should(validation);
}
}
if (screenshot) {

View File

@@ -114,4 +114,28 @@ describe('C4 diagram', () => {
{}
);
});
it('C4.6 should render C4Context diagram with ComponentQueue_Ext', () => {
imgSnapshotTest(
`
C4Context
title System Context diagram with ComponentQueue_Ext
Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
Enterprise_Boundary(b1, "BankBoundary") {
ComponentQueue_Ext(msgQueue, "Message Queue", "RabbitMQ", "External message queue system for processing banking transactions")
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
}
}
BiRel(customerA, SystemAA, "Uses")
Rel(SystemAA, msgQueue, "Sends messages to")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
`,
{}
);
});
});

View File

@@ -445,7 +445,7 @@ ORDER ||--|{ LINE-ITEM : contains
{ logLevel: 1 }
);
});
it('should render ER diagram with numeric entity names and attributes', () => {
it('should render ER diagram with standalone numeric entities', () => {
imgSnapshotTest(
`erDiagram
PRODUCT ||--o{ ORDER-ITEM : has

View File

@@ -79,6 +79,18 @@ describe('Flowchart v2', () => {
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('6a: should render complex HTML in labels with sandbox security', () => {
imgSnapshotTest(
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ securityLevel: 'sandbox', flowchart: { htmlLabels: true } }
);
});
it('7: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`flowchart TD

View File

@@ -1,9 +0,0 @@
import { urlSnapshotTest } from '../../helpers/util.ts';
describe('Hand Draw', () => {
it('should render the hand drawn look for all diagrams', () => {
urlSnapshotTest('http://localhost:9000/handDrawn.html', {
logLevel: 1,
});
});
});

View File

@@ -1,211 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hand Drawn Diagrams</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 32px;
display: flex;
flex-direction: column;
gap: 48px;
}
h1,
h2,
h3,
h4 {
margin: 0;
font-weight: 600;
letter-spacing: -0.01em;
}
h1 {
font-size: 32px;
}
h2 {
font-size: 24px;
margin-bottom: 12px;
}
h3 {
font-size: 18px;
}
main {
display: flex;
flex-direction: column;
gap: 64px;
}
section.diagram-group {
display: flex;
flex-direction: column;
gap: 32px;
}
.diagram-example {
display: flex;
flex-direction: column;
gap: 16px;
}
.diagram-row {
display: flex;
gap: 24px;
flex-wrap: wrap;
}
.diagram-panel {
flex: 1 1 420px;
min-width: 0;
display: flex;
flex-direction: column;
gap: 12px;
}
.diagram-label {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
font-weight: 500;
color: #9ca3af;
text-transform: uppercase;
letter-spacing: 0.08em;
}
@media (max-width: 960px) {
body {
padding: 20px;
}
.diagram-row {
flex-direction: column;
}
}
</style>
</head>
<body>
<h1>Hand-drawn Look Comparison</h1>
<main id="app"></main>
<script type="module">
import mermaid from './mermaid.esm.mjs';
import { diagramData } from './mermaid-examples.esm.mjs';
mermaid.initialize({ startOnLoad: false });
const app = document.getElementById('app');
const seen = new Set();
const ensureObject = (value) =>
value && typeof value === 'object' && !Array.isArray(value) ? value : {};
let renderIndex = 0;
const renderDiagram = async (target, source, overrides = {}) => {
const id = `diagram-${renderIndex++}`;
const config = {
startOnLoad: false,
...overrides,
};
mermaid.initialize(config);
try {
const { svg, bindFunctions } = await mermaid.render(id, source);
target.innerHTML = svg;
if (typeof bindFunctions === 'function') {
bindFunctions(target);
}
} catch (error) {
console.error('Failed to render diagram', { id, error });
target.innerHTML = '';
const pre = document.createElement('pre');
pre.className = 'diagram-error';
pre.textContent = String(error);
target.appendChild(pre);
}
};
const createPanel = (label) => {
const panel = document.createElement('article');
panel.className = 'diagram-panel';
const heading = document.createElement('div');
heading.className = 'diagram-label';
heading.textContent = label;
panel.appendChild(heading);
const surface = document.createElement('div');
surface.className = 'diagram-surface';
panel.appendChild(surface);
return { panel, surface };
};
const bootstrap = async () => {
const fragment = document.createDocumentFragment();
const groups = diagramData.filter((group) => {
if (!group || seen.has(group.id)) {
return false;
}
seen.add(group.id);
return Array.isArray(group.examples) && group.examples.length > 0;
});
for (const group of groups) {
const groupSection = document.createElement('section');
groupSection.className = 'diagram-group';
const groupHeading = document.createElement('h2');
groupHeading.textContent = group.name ?? group.id;
groupSection.appendChild(groupHeading);
for (const example of group.examples) {
const exampleWrapper = document.createElement('div');
exampleWrapper.className = 'diagram-example';
const exampleHeading = document.createElement('h3');
exampleHeading.textContent = example.title ?? 'Example';
exampleWrapper.appendChild(exampleHeading);
const row = document.createElement('div');
row.className = 'diagram-row';
const defaultPanel = createPanel('Default config');
const handPanel = createPanel('config.look: handDrawn');
row.append(defaultPanel.panel, handPanel.panel);
exampleWrapper.appendChild(row);
groupSection.appendChild(exampleWrapper);
await renderDiagram(defaultPanel.surface, example.code);
await renderDiagram(handPanel.surface, example.code, {
look: 'handDrawn',
handDrawnSeed: 42,
});
}
fragment.appendChild(groupSection);
}
app.appendChild(fragment);
if (window.Cypress) {
window.rendered = true;
}
};
bootstrap().catch((error) => {
console.error('Failed to bootstrap hand-drawn comparison page', error);
});
</script>
</body>
</html>

View File

@@ -2,227 +2,227 @@
"durations": [
{
"spec": "cypress/integration/other/configuration.spec.js",
"duration": 5841
"duration": 6099
},
{
"spec": "cypress/integration/other/external-diagrams.spec.js",
"duration": 2138
"duration": 2236
},
{
"spec": "cypress/integration/other/ghsa.spec.js",
"duration": 3370
"duration": 3405
},
{
"spec": "cypress/integration/other/iife.spec.js",
"duration": 2052
"duration": 2176
},
{
"spec": "cypress/integration/other/interaction.spec.js",
"duration": 12243
"duration": 12300
},
{
"spec": "cypress/integration/other/rerender.spec.js",
"duration": 2065
"duration": 2089
},
{
"spec": "cypress/integration/other/xss.spec.js",
"duration": 31288
"duration": 32033
},
{
"spec": "cypress/integration/rendering/appli.spec.js",
"duration": 3421
"duration": 3672
},
{
"spec": "cypress/integration/rendering/architecture.spec.ts",
"duration": 97
"duration": 103
},
{
"spec": "cypress/integration/rendering/block.spec.js",
"duration": 18500
"duration": 18135
},
{
"spec": "cypress/integration/rendering/c4.spec.js",
"duration": 5793
"duration": 5661
},
{
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
"duration": 40966
"duration": 41456
},
{
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
"duration": 39176
"duration": 38910
},
{
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
"duration": 23468
"duration": 24120
},
{
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
"duration": 38291
"duration": 38454
},
{
"spec": "cypress/integration/rendering/classDiagram.spec.js",
"duration": 16949
"duration": 17099
},
{
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
"duration": 9480
"duration": 9844
},
{
"spec": "cypress/integration/rendering/current.spec.js",
"duration": 2753
"duration": 2951
},
{
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
"duration": 88028
"duration": 90081
},
{
"spec": "cypress/integration/rendering/erDiagram.spec.js",
"duration": 15615
"duration": 19496
},
{
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
"duration": 3706
"duration": 3829
},
{
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
"duration": 43905
"duration": 42517
},
{
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
"duration": 31217
"duration": 31541
},
{
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
"duration": 7531
"duration": 7749
},
{
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
"duration": 25423
"duration": 25230
},
{
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
"duration": 49664
"duration": 49359
},
{
"spec": "cypress/integration/rendering/flowchart.spec.js",
"duration": 32525
"duration": 33028
},
{
"spec": "cypress/integration/rendering/gantt.spec.js",
"duration": 20915
"duration": 22271
},
{
"spec": "cypress/integration/rendering/gitGraph.spec.js",
"duration": 53556
"duration": 51837
},
{
"spec": "cypress/integration/rendering/iconShape.spec.ts",
"duration": 283038
"duration": 285060
},
{
"spec": "cypress/integration/rendering/imageShape.spec.ts",
"duration": 59434
"duration": 59517
},
{
"spec": "cypress/integration/rendering/info.spec.ts",
"duration": 3101
"duration": 3501
},
{
"spec": "cypress/integration/rendering/journey.spec.js",
"duration": 7099
"duration": 7405
},
{
"spec": "cypress/integration/rendering/kanban.spec.ts",
"duration": 7567
"duration": 7975
},
{
"spec": "cypress/integration/rendering/katex.spec.js",
"duration": 3817
"duration": 4312
},
{
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
"duration": 2624
"duration": 2630
},
{
"spec": "cypress/integration/rendering/mindmap-tidy-tree.spec.js",
"duration": 4246
"duration": 4541
},
{
"spec": "cypress/integration/rendering/mindmap.spec.ts",
"duration": 11967
"duration": 12134
},
{
"spec": "cypress/integration/rendering/newShapes.spec.ts",
"duration": 151914
"duration": 151160
},
{
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
"duration": 116698
"duration": 118044
},
{
"spec": "cypress/integration/rendering/packet.spec.ts",
"duration": 4967
"duration": 5166
},
{
"spec": "cypress/integration/rendering/pie.spec.ts",
"duration": 6700
"duration": 7074
},
{
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
"duration": 8963
"duration": 9518
},
{
"spec": "cypress/integration/rendering/radar.spec.js",
"duration": 5540
"duration": 5846
},
{
"spec": "cypress/integration/rendering/requirement.spec.js",
"duration": 2782
"duration": 3089
},
{
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
"duration": 54797
"duration": 55361
},
{
"spec": "cypress/integration/rendering/sankey.spec.ts",
"duration": 6914
"duration": 7236
},
{
"spec": "cypress/integration/rendering/sequencediagram-v2.spec.js",
"duration": 20481
"duration": 26057
},
{
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
"duration": 38490
"duration": 48401
},
{
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
"duration": 30766
"duration": 30364
},
{
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
"duration": 16705
"duration": 16862
},
{
"spec": "cypress/integration/rendering/theme.spec.js",
"duration": 30928
"duration": 30553
},
{
"spec": "cypress/integration/rendering/timeline.spec.ts",
"duration": 8424
"duration": 8962
},
{
"spec": "cypress/integration/rendering/treemap.spec.ts",
"duration": 12533
"duration": 12486
},
{
"spec": "cypress/integration/rendering/xyChart.spec.js",
"duration": 21197
"duration": 21718
},
{
"spec": "cypress/integration/rendering/zenuml.spec.js",
"duration": 3455
"duration": 3882
}
]
}

View File

@@ -10,7 +10,7 @@
# Interface: ExternalDiagramDefinition
Defined in: [packages/mermaid/src/diagram-api/types.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L101)
Defined in: [packages/mermaid/src/diagram-api/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L96)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:101](https://github.com/m
> **detector**: `DiagramDetector`
Defined in: [packages/mermaid/src/diagram-api/types.ts:103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L103)
Defined in: [packages/mermaid/src/diagram-api/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L98)
---
@@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:103](https://github.com/m
> **id**: `string`
Defined in: [packages/mermaid/src/diagram-api/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L102)
Defined in: [packages/mermaid/src/diagram-api/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L97)
---
@@ -34,4 +34,4 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:102](https://github.com/m
> **loader**: `DiagramLoader`
Defined in: [packages/mermaid/src/diagram-api/types.ts:104](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L104)
Defined in: [packages/mermaid/src/diagram-api/types.ts:99](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L99)

View File

@@ -12,4 +12,4 @@
> **SVG** = `d3.Selection`<`SVGSVGElement`, `unknown`, `Element` | `null`, `unknown`>
Defined in: [packages/mermaid/src/diagram-api/types.ts:133](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L133)
Defined in: [packages/mermaid/src/diagram-api/types.ts:128](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L128)

View File

@@ -12,4 +12,4 @@
> **SVGGroup** = `d3.Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`>
Defined in: [packages/mermaid/src/diagram-api/types.ts:135](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L135)
Defined in: [packages/mermaid/src/diagram-api/types.ts:130](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L130)

View File

@@ -57,6 +57,8 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [GitHub Writer](https://github.com/ckeditor/github-writer)
- [SVG diagram generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) ✅
- [GNU Octave](https://octave.org/) ✅
- [octave_mermaid_js](https://github.com/CNOCTAVE/octave_mermaid_js) ✅
- [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid)
- [MonsterWriter](https://www.monsterwriter.com/) ✅
- [Joplin](https://joplinapp.org) ✅

View File

@@ -63,21 +63,21 @@
]
},
"devDependencies": {
"@applitools/eyes-cypress": "^3.55.2",
"@argos-ci/cypress": "^6.1.3",
"@applitools/eyes-cypress": "^3.56.3",
"@argos-ci/cypress": "^6.2.1",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.29.7",
"@cspell/eslint-plugin": "^8.19.4",
"@cypress/code-coverage": "^3.14.6",
"@cspell/eslint-plugin": "^9.3.0",
"@cypress/code-coverage": "^3.14.7",
"@eslint/js": "^9.26.0",
"@rollup/plugin-typescript": "^12.1.4",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/express": "^5.0.5",
"@types/js-yaml": "^4.0.9",
"@types/jsdom": "^21.1.7",
"@types/lodash": "^4.17.20",
"@types/mdast": "^4.0.4",
"@types/node": "^22.18.6",
"@types/node": "^22.19.0",
"@types/rollup-plugin-visualizer": "^5.0.3",
"@vitest/coverage-v8": "^3.2.4",
"@vitest/spy": "^3.2.4",
@@ -88,23 +88,23 @@
"cors": "^2.8.5",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
"cspell": "^9.2.1",
"cspell": "^9.3.0",
"cypress": "^14.5.4",
"cypress-image-snapshot": "^4.0.1",
"cypress-split": "^1.24.23",
"esbuild": "^0.25.10",
"cypress-split": "^1.24.25",
"esbuild": "^0.25.12",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-cypress": "^4.3.0",
"eslint-plugin-cypress": "^5.2.0",
"eslint-plugin-html": "^8.1.3",
"eslint-plugin-jest": "^28.14.0",
"eslint-plugin-jsdoc": "^50.8.0",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-jsdoc": "^61.1.12",
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-lodash": "^8.0.0",
"eslint-plugin-markdown": "^5.1.0",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-tsdoc": "^0.4.0",
"eslint-plugin-unicorn": "^59.0.1",
"eslint-plugin-unicorn": "^62.0.0",
"express": "^5.1.0",
"globals": "^16.4.0",
"globby": "^14.1.0",
@@ -121,13 +121,13 @@
"prettier": "^3.6.2",
"prettier-plugin-jsdoc": "^1.3.3",
"rimraf": "^6.0.1",
"rollup-plugin-visualizer": "^6.0.3",
"rollup-plugin-visualizer": "^6.0.5",
"start-server-and-test": "^2.1.2",
"tslib": "^2.8.1",
"tsx": "^4.20.5",
"tsx": "^4.20.6",
"typescript": "~5.7.3",
"typescript-eslint": "^8.38.0",
"vite": "^7.0.7",
"vite": "^7.0.8",
"vite-plugin-istanbul": "^7.0.0",
"vitest": "^3.2.4"
},

View File

@@ -33,7 +33,7 @@
],
"license": "MIT",
"dependencies": {
"@zenuml/core": "^3.41.4"
"@zenuml/core": "^3.41.6"
},
"devDependencies": {
"mermaid": "workspace:^"

View File

@@ -77,23 +77,22 @@
"d3": "^7.9.0",
"d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.13",
"dayjs": "^1.11.18",
"dompurify": "^3.2.5",
"katex": "^0.16.22",
"dayjs": "^1.11.19",
"dompurify": "^3.3.0",
"katex": "^0.16.25",
"khroma": "^2.1.0",
"lodash-es": "^4.17.21",
"marked": "^16.3.0",
"roughjs": "^4.6.6",
"stylis": "^4.3.6",
"svg2roughjs": "^3.2.1",
"ts-dedent": "^2.2.0",
"uuid": "^11.1.0"
},
"devDependencies": {
"@adobe/jsonschema2md": "^8.0.5",
"@adobe/jsonschema2md": "^8.0.7",
"@iconify/types": "^2.0.0",
"@types/cytoscape": "^3.21.9",
"@types/cytoscape-fcose": "^2.2.4",
"@types/cytoscape-fcose": "^2.2.5",
"@types/d3-sankey": "^0.12.4",
"@types/d3-scale": "^4.0.9",
"@types/d3-scale-chromatic": "^3.1.0",
@@ -102,7 +101,7 @@
"@types/jsdom": "^21.1.7",
"@types/katex": "^0.16.7",
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9",
"@types/micromatch": "^4.0.10",
"@types/stylis": "^4.2.7",
"@types/uuid": "^10.0.0",
"ajv": "^8.17.1",
@@ -124,7 +123,7 @@
"rimraf": "^6.0.1",
"start-server-and-test": "^2.1.2",
"type-fest": "^4.41.0",
"typedoc": "^0.28.13",
"typedoc": "^0.28.14",
"typedoc-plugin-markdown": "^4.8.1",
"typescript": "~5.7.3",
"unist-util-flatmap": "^1.0.0",

View File

@@ -30,7 +30,7 @@ export class Diagram {
const { id, diagram } = await loader();
registerDiagram(id, diagram);
}
const { db, parser, renderer, init, capabilities } = getDiagram(type);
const { db, parser, renderer, init } = getDiagram(type);
if (parser.parser) {
// The parser.parser.yy is only present in JISON parsers. So, we'll only set if required.
parser.parser.yy = db;
@@ -42,7 +42,7 @@ export class Diagram {
db.setDiagramTitle?.(metadata.title);
}
await parser.parse(text);
return new Diagram(type, text, db, parser, renderer, capabilities);
return new Diagram(type, text, db, parser, renderer);
}
private constructor(
@@ -50,8 +50,7 @@ export class Diagram {
public text: string,
public db: DiagramDefinition['db'],
public parser: DiagramDefinition['parser'],
public renderer: DiagramDefinition['renderer'],
public capabilities: DiagramDefinition['capabilities']
public renderer: DiagramDefinition['renderer']
) {}
async render(id: string, version: string) {

View File

@@ -72,7 +72,7 @@ export const addDiagrams = () => {
}
);
if (includeLargeFeatures) {
if (injected.includeLargeFeatures) {
registerLazyLoadedDiagrams(flowchartElk, mindmap, architecture);
}

View File

@@ -75,15 +75,10 @@ export interface DiagramRenderer {
) => Map<string, DiagramStyleClassDef>;
}
export interface DiagramCapabilities {
handDrawn?: boolean;
}
export interface DiagramDefinition {
db: DiagramDB;
renderer: DiagramRenderer;
parser: ParserDefinition;
capabilities?: DiagramCapabilities;
styles?: any;
init?: (config: MermaidConfig) => void;
injectUtils?: (

View File

@@ -0,0 +1,58 @@
import c4Db from '../c4Db.js';
import c4 from './c4Diagram.jison';
import { setConfig } from '../../../config.js';
setConfig({
securityLevel: 'strict',
});
describe.each([
['Component', 'component'],
['ComponentDb', 'component_db'],
['ComponentQueue', 'component_queue'],
['Component_Ext', 'external_component'],
['ComponentDb_Ext', 'external_component_db'],
['ComponentQueue_Ext', 'external_component_queue'],
])('parsing a C4 %s', function (macroName, elementName) {
beforeEach(function () {
c4.parser.yy = c4Db;
c4.parser.yy.clear();
});
it('should parse a C4 diagram with one Component correctly', function () {
c4.parser.parse(`C4Component
title Component diagram for Internet Banking Component
${macroName}(ComponentAA, "Internet Banking Component", "Technology", "Allows customers to view information about their bank accounts, and make payments.")`);
const yy = c4.parser.yy;
const shapes = yy.getC4ShapeArray();
expect(shapes.length).toBe(1);
const onlyShape = shapes[0];
expect(onlyShape).toMatchObject({
alias: 'ComponentAA',
descr: {
text: 'Allows customers to view information about their bank accounts, and make payments.',
},
label: {
text: 'Internet Banking Component',
},
techn: {
text: 'Technology',
},
typeC4Shape: {
text: elementName,
},
});
});
it('should handle a trailing whitespaces after Component', function () {
const whitespace = ' ';
const rendered = c4.parser.parse(`C4Component${whitespace}
title Component diagram for Internet Banking Component${whitespace}
${macroName}(ComponentAA, "Internet Banking Component", "Technology", "Allows customers to view information about their bank accounts, and make payments.")${whitespace}`);
expect(rendered).toBe(true);
});
});

View File

@@ -158,10 +158,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
"UpdateRelStyle" { this.begin("update_rel_style"); return 'UPDATE_REL_STYLE';}
"UpdateLayoutConfig" { this.begin("update_layout_config"); return 'UPDATE_LAYOUT_CONFIG';}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config><<EOF>> return "EOF_IN_STRUCT";
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(][ ]*[,] { this.begin("attribute"); return "ATTRIBUTE_EMPTY";}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(] { this.begin("attribute"); }
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config,attribute>[)] { this.popState();this.popState();}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config><<EOF>> return "EOF_IN_STRUCT";
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(][ ]*[,] { this.begin("attribute"); return "ATTRIBUTE_EMPTY";}
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config>[(] { this.begin("attribute"); }
<person,person_ext,system_ext_queue,system_ext_db,system_ext,system_queue,system_db,system,boundary,enterprise_boundary,system_boundary,container_ext_db,container_ext_queue,container_ext,container_queue,container_db,container,container_boundary,component_ext_db,component_ext_queue,component_ext,component_queue,component_db,component,node,node_l,node_r,rel,birel,rel_u,rel_d,rel_l,rel_r,rel_b,rel_index,update_el_style,update_rel_style,update_layout_config,attribute>[)] { this.popState();this.popState();}
<attribute>",," { return 'ATTRIBUTE_EMPTY';}
<attribute>"," { }

View File

@@ -1,4 +1,4 @@
import { select, type Selection } from 'd3';
import { select } from 'd3';
import { log } from '../../logger.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import common from '../common/common.js';
@@ -12,6 +12,7 @@ import {
setDiagramTitle,
getDiagramTitle,
} from '../common/commonDb.js';
import { createTooltip } from '../common/svgDrawCommon.js';
import { ClassMember } from './classTypes.js';
import type {
ClassRelation,
@@ -26,6 +27,7 @@ import type {
} from './classTypes.js';
import type { Node, Edge } from '../../rendering-util/types.js';
import type { DiagramDB } from '../../diagram-api/types.js';
import DOMPurify from 'dompurify';
const MERMAID_DOM_ID_PREFIX = 'classId-';
let classCounter = 0;
@@ -483,43 +485,45 @@ export class ClassDB implements DiagramDB {
LOLLIPOP: 4,
};
// Utility function to escape HTML meta-characters
private escapeHtml(str: string): string {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
private readonly setupToolTips = (element: Element) => {
let tooltipElem: Selection<HTMLDivElement, unknown, HTMLElement, unknown> =
select('.mermaidTooltip');
// @ts-expect-error - Incorrect types
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
tooltipElem = select('body')
.append('div')
.attr('class', 'mermaidTooltip')
.style('opacity', 0);
}
const tooltipElem = createTooltip();
const svg = select(element).select('svg');
const nodes = svg.selectAll('g.node');
const nodes = svg.selectAll('g').filter(function () {
return select(this).attr('title') !== null;
});
nodes
.on('mouseover', (event: MouseEvent) => {
const el = select(event.currentTarget as HTMLElement);
const title = el.attr('title');
// Don't try to draw a tooltip if no data is provided
if (title === null) {
if (!title) {
return;
}
// @ts-ignore - getBoundingClientRect is not part of the d3 type definition
const rect = this.getBoundingClientRect();
const rect = (event.currentTarget as Element).getBoundingClientRect();
tooltipElem.transition().duration(200).style('opacity', '.9');
tooltipElem
.text(el.attr('title'))
.style('left', window.scrollX + rect.left + (rect.right - rect.left) / 2 + 'px')
.style('top', window.scrollY + rect.top - 14 + document.body.scrollTop + 'px');
tooltipElem.html(tooltipElem.html().replace(/&lt;br\/&gt;/g, '<br/>'));
.html(DOMPurify.sanitize(title))
.style('left', `${window.scrollX + rect.left + rect.width / 2}px`)
.style('top', `${window.scrollY + rect.bottom + 4}px`);
el.classed('hover', true);
})
.on('mouseout', (event: MouseEvent) => {
tooltipElem.transition().duration(500).style('opacity', 0);
const el = select(event.currentTarget as HTMLElement);
el.classed('hover', false);
select(event.currentTarget as HTMLElement).classed('hover', false);
});
};

View File

@@ -10,9 +10,6 @@ export const diagram: DiagramDefinition = {
get db() {
return new ClassDB();
},
capabilities: {
handDrawn: true,
},
renderer,
styles,
init: (cnf) => {

View File

@@ -70,6 +70,31 @@ describe('Sanitize text', () => {
});
expect(result).not.toContain('javascript:alert(1)');
});
it('should allow HTML tags in sandbox mode', () => {
const htmlStr = '<p>This is a <strong>bold</strong> text</p>';
const result = sanitizeText(htmlStr, {
securityLevel: 'sandbox',
flowchart: { htmlLabels: true },
});
expect(result).toContain('<p>');
expect(result).toContain('<strong>');
expect(result).toContain('</strong>');
expect(result).toContain('</p>');
});
it('should remove script tags in sandbox mode', () => {
const maliciousStr = '<p>Hello <script>alert(1)</script> world</p>';
const result = sanitizeText(maliciousStr, {
securityLevel: 'sandbox',
flowchart: { htmlLabels: true },
});
expect(result).not.toContain('<script>');
expect(result).not.toContain('alert(1)');
expect(result).toContain('<p>');
expect(result).toContain('Hello');
expect(result).toContain('world');
});
});
describe('generic parser', () => {

View File

@@ -66,7 +66,7 @@ export const removeScript = (txt: string): string => {
const sanitizeMore = (text: string, config: MermaidConfig) => {
if (config.flowchart?.htmlLabels !== false) {
const level = config.securityLevel;
if (level === 'antiscript' || level === 'strict') {
if (level === 'antiscript' || level === 'strict' || level === 'sandbox') {
text = removeScript(text);
} else if (level !== 'loose') {
text = breakToPlaceholder(text);
@@ -333,7 +333,7 @@ const renderKatexUnsanitized = async (text: string, config: MermaidConfig): Prom
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
}
if (includeLargeFeatures) {
if (injected.includeLargeFeatures) {
const { default: katex } = await import('katex');
const outputMode =
config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML)

View File

@@ -1,4 +1,5 @@
import { sanitizeUrl } from '@braintree/sanitize-url';
import { select } from 'd3';
import type { SVG, SVGGroup } from '../../diagram-api/types.js';
import { lineBreakRegex } from './common.js';
import type {
@@ -135,3 +136,24 @@ export const getTextObj = (): TextObject => {
};
return testObject;
};
export const createTooltip = () => {
let tooltipElem = select<HTMLDivElement, unknown>('.mermaidTooltip');
if (tooltipElem.empty()) {
tooltipElem = select('body')
.append('div')
.attr('class', 'mermaidTooltip')
.style('opacity', 0)
.style('position', 'absolute')
.style('text-align', 'center')
.style('max-width', '200px')
.style('padding', '2px')
.style('font-size', '12px')
.style('background', '#ffffde')
.style('border', '1px solid #333')
.style('border-radius', '2px')
.style('pointer-events', 'none')
.style('z-index', '100');
}
return tooltipElem;
};

View File

@@ -2,16 +2,13 @@
import erParser from './parser/erDiagram.jison';
import { ErDB } from './erDb.js';
import * as renderer from './erRenderer-unified.js';
import styles from './styles.js';
import erStyles from './styles.js';
export const diagram = {
parser: erParser,
get db() {
return new ErDB();
},
capabilities: {
handDrawn: true,
},
renderer,
styles,
styles: erStyles,
};

View File

@@ -17,6 +17,7 @@ import {
setDiagramTitle,
getDiagramTitle,
} from '../common/commonDb.js';
import { createTooltip } from '../common/svgDrawCommon.js';
import type {
FlowClass,
FlowEdge,
@@ -26,7 +27,7 @@ import type {
FlowVertex,
FlowVertexTypeParam,
} from './types.js';
import DOMPurify from 'dompurify';
interface LinkData {
id: string;
}
@@ -574,15 +575,7 @@ You have to call mermaid.initialize.`
}
private setupToolTips(element: Element) {
let tooltipElem = select('.mermaidTooltip');
// @ts-ignore TODO: fix this
if ((tooltipElem._groups || tooltipElem)[0][0] === null) {
// @ts-ignore TODO: fix this
tooltipElem = select('body')
.append('div')
.attr('class', 'mermaidTooltip')
.style('opacity', 0);
}
const tooltipElem = createTooltip();
const svg = select(element).select('svg');
@@ -603,7 +596,7 @@ You have to call mermaid.initialize.`
.text(el.attr('title'))
.style('left', window.scrollX + rect.left + (rect.right - rect.left) / 2 + 'px')
.style('top', window.scrollY + rect.bottom + 'px');
tooltipElem.html(tooltipElem.html().replace(/&lt;br\/&gt;/g, '<br/>'));
tooltipElem.html(DOMPurify.sanitize(title));
el.classed('hover', true);
})
.on('mouseout', (e: MouseEvent) => {

View File

@@ -13,9 +13,6 @@ export const diagram = {
return new FlowDB();
},
renderer,
capabilities: {
handDrawn: true,
},
styles: flowStyles,
init: (cnf: MermaidConfig) => {
if (!cnf.flowchart) {

View File

@@ -1,8 +1,7 @@
import type { InfoFields, InfoDB } from './infoTypes.js';
import packageJson from '../../../package.json' assert { type: 'json' };
export const DEFAULT_INFO_DB: InfoFields = {
version: packageJson.version + (includeLargeFeatures ? '' : '-tiny'),
version: injected.version + (injected.includeLargeFeatures ? '' : '-tiny'),
} as const;
export const getVersion = (): string => DEFAULT_INFO_DB.version;

View File

@@ -43,7 +43,7 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
const group: SVGGroup = svg.append('g');
group.attr('transform', 'translate(' + pieWidth / 2 + ',' + height / 2 + ')');
const { themeVariables, look } = globalConfig;
const { themeVariables } = globalConfig;
let [outerStrokeWidth] = parseFontSize(themeVariables.pieOuterStrokeWidth);
outerStrokeWidth ??= 2;
@@ -59,14 +59,12 @@ export const draw: DrawDefinition = (text, id, _version, diagObj) => {
.innerRadius(radius * textPosition)
.outerRadius(radius * textPosition);
if (look !== 'handDrawn') {
group
.append('circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', radius + outerStrokeWidth / 2)
.attr('class', 'pieOuterCircle');
}
group
.append('circle')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', radius + outerStrokeWidth / 2)
.attr('class', 'pieOuterCircle');
const sections: Sections = db.getSections();
const arcs: d3.PieArcDatum<D3Section>[] = createPieArcs(sections);

View File

@@ -10,9 +10,6 @@ export const diagram: DiagramDefinition = {
get db() {
return new RequirementDB();
},
capabilities: {
handDrawn: true,
},
renderer,
styles,
};

View File

@@ -10,9 +10,6 @@ export const diagram: DiagramDefinition = {
get db() {
return new StateDB(2);
},
capabilities: {
handDrawn: true,
},
renderer,
styles,
init: (cnf) => {

View File

@@ -1,6 +1,6 @@
import type { MarkdownOptions } from 'vitepress';
import { defineConfig } from 'vitepress';
import packageJson from '../../../package.json' assert { type: 'json' };
import packageJson from '../../../package.json' with { type: 'json' };
import { addCanonicalUrls } from './canonical-urls.js';
import MermaidExample from './mermaid-markdown-all.js';

View File

@@ -52,6 +52,8 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [GitHub Writer](https://github.com/ckeditor/github-writer)
- [SVG diagram generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) ✅
- [GNU Octave](https://octave.org/) ✅
- [octave_mermaid_js](https://github.com/CNOCTAVE/octave_mermaid_js) ✅
- [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid)
- [MonsterWriter](https://www.monsterwriter.com/) ✅
- [Joplin](https://joplinapp.org) ✅

View File

@@ -21,19 +21,19 @@
"font-awesome": "^4.7.0",
"jiti": "^2.4.2",
"mermaid": "workspace:^",
"vue": "^3.5.21"
"vue": "^3.5.24"
},
"devDependencies": {
"@iconify-json/carbon": "^1.2.13",
"@unocss/reset": "^66.5.1",
"@vite-pwa/vitepress": "^1.0.0",
"@iconify-json/carbon": "^1.2.14",
"@unocss/reset": "^66.5.5",
"@vite-pwa/vitepress": "^1.0.1",
"@vitejs/plugin-vue": "^6.0.1",
"fast-glob": "^3.3.3",
"https-localhost": "^4.7.1",
"pathe": "^2.0.3",
"unocss": "^66.5.1",
"unplugin-vue-components": "^28.4.1",
"vite": "^7.0.7",
"unocss": "^66.5.5",
"unplugin-vue-components": "^28.8.0",
"vite": "^7.0.8",
"vite-plugin-pwa": "^1.0.3",
"vitepress": "1.6.4",
"workbox-window": "^7.3.0"

View File

@@ -7,7 +7,6 @@ import { select } from 'd3';
import { compile, serialize, stringify } from 'stylis';
import DOMPurify from 'dompurify';
import isEmpty from 'lodash-es/isEmpty.js';
import packageJson from '../package.json' with { type: 'json' };
import { addSVGa11yTitleDescription, setA11yDiagramInfo } from './accessibility.js';
import assignWithDepth from './assignWithDepth.js';
import * as configApi from './config.js';
@@ -421,12 +420,12 @@ const render = async function (
// -------------------------------------------------------------------------------
// Draw the diagram with the renderer
try {
await diag.renderer.draw(text, id, packageJson.version, diag);
await diag.renderer.draw(text, id, injected.version, diag);
} catch (e) {
if (config.suppressErrorRendering) {
removeTempElements();
} else {
errorRenderer.draw(text, id, packageJson.version);
errorRenderer.draw(text, id, injected.version);
}
throw e;
}
@@ -445,29 +444,6 @@ const render = async function (
log.debug('config.arrowMarkerAbsolute', config.arrowMarkerAbsolute);
svgCode = cleanUpSvgCode(svgCode, isSandboxed, evaluate(config.arrowMarkerAbsolute));
if (config.look === 'handDrawn' && !diag.capabilities?.handDrawn && includeLargeFeatures) {
const { OutputType, Svg2Roughjs } = await import('svg2roughjs');
const svg2roughjs = new Svg2Roughjs(enclosingDivID_selector, OutputType.SVG, {
seed: config.handDrawnSeed,
});
const graphDiv = document.querySelector<SVGSVGElement>(idSelector)!;
svg2roughjs.svg = graphDiv;
await svg2roughjs.sketch();
graphDiv.remove();
const sketch = document.querySelector<SVGSVGElement>(`${enclosingDivID_selector} > svg`);
if (!sketch) {
throw new Error('sketch not found');
}
const height = sketch.getAttribute('height');
const width = sketch.getAttribute('width');
sketch.setAttribute('id', id);
sketch.removeAttribute('height');
sketch.setAttribute('width', '100%');
sketch.setAttribute('viewBox', `0 0 ${width} ${height}`);
svgCode = sketch.outerHTML;
}
if (isSandboxed) {
const svgEl = root.select(enclosingDivID_selector + ' svg').node();

View File

@@ -42,7 +42,7 @@ const registerDefaultLayoutLoaders = () => {
name: 'dagre',
loader: async () => await import('./layout-algorithms/dagre/index.js'),
},
...(includeLargeFeatures
...(injected.includeLargeFeatures
? [
{
name: 'cose-bilkent',

View File

@@ -1,2 +1,5 @@
// eslint-disable-next-line no-var
declare var includeLargeFeatures: boolean;
declare var injected: {
version: string;
includeLargeFeatures: boolean;
};

3440
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -35,7 +35,8 @@ export default defineConfig({
},
define: {
// Needs to be string
includeLargeFeatures: 'true',
'injected.includeLargeFeatures': 'true',
'import.meta.vitest': 'undefined',
packageVersion: "'0.0.0'",
},
});