mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-16 13:59:54 +02:00
Compare commits
16 Commits
yash-singh
...
gh-readonl
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c5d3244511 | ||
![]() |
c41cdcaf23 | ||
![]() |
99d3701a85 | ||
![]() |
6413529a6e | ||
![]() |
f5e1df08a0 | ||
![]() |
472a883c73 | ||
![]() |
22bd26272d | ||
![]() |
da150e8767 | ||
![]() |
1f64452716 | ||
![]() |
9986b023d7 | ||
![]() |
c4ccfec316 | ||
![]() |
1ac9244e68 | ||
![]() |
18defaae6d | ||
![]() |
7f33ae0f40 | ||
![]() |
13aa3265e3 | ||
![]() |
3b0687e557 |
@@ -55,6 +55,7 @@ export const imgSnapshotTest = (
|
||||
const options: CypressMermaidConfig = {
|
||||
..._options,
|
||||
fontFamily: _options.fontFamily || 'courier',
|
||||
// @ts-ignore TODO: Fix type of fontSize
|
||||
fontSize: _options.fontSize || '16px',
|
||||
sequence: {
|
||||
...(_options.sequence || {}),
|
||||
|
BIN
docs/config/img/mathMLDifferences.png
Normal file
BIN
docs/config/img/mathMLDifferences.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@@ -84,3 +84,13 @@ Example with legacy mode enabled (the latest version of KaTeX's stylesheet can b
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Handling Rendering Differences
|
||||
|
||||
Due to differences between default fonts across operating systems and browser's MathML implementations, inconsistent results can be seen across platforms. If having consistent results are important, or the most optimal rendered results are desired, `forceLegacyMathML` can be enabled in the config.
|
||||
|
||||
This option will always use KaTeX's stylesheet instead of only when MathML is not supported (as with `legacyMathML`). Note that only `forceLegacyMathML` needs to be set.
|
||||
|
||||
If including KaTeX's stylesheet is not a concern, enabling this option is recommended to avoid scenarios where no MathML implementation within a browser provides the desired output (as seen below).
|
||||
|
||||

|
||||
|
@@ -249,6 +249,7 @@ Communication tools and platforms
|
||||
- [Jekyll](https://jekyllrb.com/)
|
||||
- [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid)
|
||||
- [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams)
|
||||
- [MarkChart: Preview Mermaid diagrams on macOS](https://markchart.app/)
|
||||
- [mermaid-isomorphic](https://github.com/remcohaszing/mermaid-isomorphic)
|
||||
- [mermaid-server: Generate diagrams using a HTTP request](https://github.com/TomWright/mermaid-server)
|
||||
- [NiceGUI: Let any browser be the frontend of your Python code](https://nicegui.io) ✅
|
||||
|
@@ -120,6 +120,13 @@ export interface MermaidConfig {
|
||||
*
|
||||
*/
|
||||
legacyMathML?: boolean;
|
||||
/**
|
||||
* This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS
|
||||
* fonts and browser's MathML implementation, this option is recommended if consistent rendering is important.
|
||||
* If set to true, ignores legacyMathML.
|
||||
*
|
||||
*/
|
||||
forceLegacyMathML?: boolean;
|
||||
/**
|
||||
* This option controls if the generated ids of nodes in the SVG are
|
||||
* generated randomly or based on a seed.
|
||||
@@ -158,7 +165,7 @@ export interface MermaidConfig {
|
||||
block?: BlockDiagramConfig;
|
||||
dompurifyConfig?: DOMPurifyConfiguration;
|
||||
wrap?: boolean;
|
||||
fontSize?: string | number;
|
||||
fontSize?: number;
|
||||
markdownAutoWrap?: boolean;
|
||||
/**
|
||||
* Suppresses inserting 'Syntax error' diagram in the DOM.
|
||||
|
@@ -4,7 +4,7 @@ import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import { render } from '../../dagre-wrapper/index.js';
|
||||
import utils from '../../utils.js';
|
||||
import utils, { getEdgeId } from '../../utils.js';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||
import common from '../common/common.js';
|
||||
@@ -231,7 +231,10 @@ export const addRelations = function (relations: ClassRelation[], g: graphlib.Gr
|
||||
//Set relationship style and line type
|
||||
classes: 'relation',
|
||||
pattern: edge.relation.lineType == 1 ? 'dashed' : 'solid',
|
||||
id: `id_${edge.id1}_${edge.id2}_${cnt}`,
|
||||
id: getEdgeId(edge.id1, edge.id2, {
|
||||
prefix: 'id',
|
||||
counter: cnt,
|
||||
}),
|
||||
// Set link type for rendering
|
||||
arrowhead: edge.type === 'arrow_open' ? 'none' : 'normal',
|
||||
//Set edge extra labels
|
||||
|
@@ -337,18 +337,20 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
|
||||
return text;
|
||||
}
|
||||
|
||||
if (!isMathMLSupported() && !config.legacyMathML) {
|
||||
if (!(isMathMLSupported() || config.legacyMathML || config.forceLegacyMathML)) {
|
||||
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
|
||||
}
|
||||
|
||||
const { default: katex } = await import('katex');
|
||||
const outputMode =
|
||||
config.forceLegacyMathML || (!isMathMLSupported() && config.legacyMathML)
|
||||
? 'htmlAndMathml'
|
||||
: 'mathml';
|
||||
return text
|
||||
.split(lineBreakRegex)
|
||||
.map((line) =>
|
||||
hasKatex(line)
|
||||
? `<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
|
||||
${line}
|
||||
</div>`
|
||||
? `<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">${line}</div>`
|
||||
: `<div>${line}</div>`
|
||||
)
|
||||
.join('')
|
||||
@@ -357,7 +359,7 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
|
||||
.renderToString(c, {
|
||||
throwOnError: true,
|
||||
displayMode: true,
|
||||
output: isMathMLSupported() ? 'mathml' : 'htmlAndMathml',
|
||||
output: outputMode,
|
||||
})
|
||||
.replace(/\n/g, ' ')
|
||||
.replace(/<annotation.*<\/annotation>/g, '')
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
|
||||
import { select, curveLinear, selectAll } from 'd3';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import utils from '../../utils.js';
|
||||
import utils, { getEdgeId } from '../../utils.js';
|
||||
import { render } from '../../dagre-wrapper/index.js';
|
||||
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
||||
import { log } from '../../logger.js';
|
||||
@@ -210,7 +210,11 @@ export const addEdges = async function (edges, g, diagObj) {
|
||||
cnt++;
|
||||
|
||||
// Identify Link
|
||||
const linkIdBase = 'L-' + edge.start + '-' + edge.end;
|
||||
const linkIdBase = getEdgeId(edge.start, edge.end, {
|
||||
counter: cnt,
|
||||
prefix: 'L',
|
||||
});
|
||||
|
||||
// count the links from+to the same node to give unique id
|
||||
if (linkIdCnt[linkIdBase] === undefined) {
|
||||
linkIdCnt[linkIdBase] = 0;
|
||||
@@ -219,7 +223,8 @@ export const addEdges = async function (edges, g, diagObj) {
|
||||
linkIdCnt[linkIdBase]++;
|
||||
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
|
||||
}
|
||||
let linkId = linkIdBase + '-' + linkIdCnt[linkIdBase];
|
||||
let linkId = `${linkIdBase}_${linkIdCnt[linkIdBase]}`;
|
||||
|
||||
log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
|
||||
const linkNameStart = 'LS-' + edge.start;
|
||||
const linkNameEnd = 'LE-' + edge.end;
|
||||
|
@@ -6,7 +6,7 @@ import { applyStyle } from 'dagre-d3-es/src/dagre-js/util.js';
|
||||
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
||||
import { log } from '../../logger.js';
|
||||
import common, { evaluate, renderKatex } from '../common/common.js';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
||||
import { interpolateToCurve, getStylesFromArray, getEdgeId } from '../../utils.js';
|
||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||
import flowChartShapes from './flowChartShapes.js';
|
||||
import { replaceIconSubstring } from '../../rendering-util/createText.js';
|
||||
@@ -175,7 +175,10 @@ export const addEdges = async function (edges, g, diagObj) {
|
||||
cnt++;
|
||||
|
||||
// Identify Link
|
||||
const linkId = 'L-' + edge.start + '-' + edge.end;
|
||||
const linkId = getEdgeId(edge.start, edge.end, {
|
||||
counter: cnt,
|
||||
prefix: 'L',
|
||||
});
|
||||
const linkNameStart = 'LS-' + edge.start;
|
||||
const linkNameEnd = 'LE-' + edge.end;
|
||||
|
||||
|
@@ -5,7 +5,7 @@ import { render } from '../../dagre-wrapper/index.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import common from '../common/common.js';
|
||||
import utils from '../../utils.js';
|
||||
import utils, { getEdgeId } from '../../utils.js';
|
||||
|
||||
import {
|
||||
DEFAULT_DIAGRAM_DIRECTION,
|
||||
@@ -252,7 +252,6 @@ const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) =>
|
||||
type: 'group',
|
||||
padding: 0, //getConfig().flowchart.padding
|
||||
};
|
||||
graphItemCount++;
|
||||
|
||||
const parentNodeId = itemId + PARENT_ID;
|
||||
g.setNode(parentNodeId, groupData);
|
||||
@@ -270,17 +269,23 @@ const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) =>
|
||||
from = noteData.id;
|
||||
to = itemId;
|
||||
}
|
||||
|
||||
g.setEdge(from, to, {
|
||||
arrowhead: 'none',
|
||||
arrowType: '',
|
||||
style: G_EDGE_STYLE,
|
||||
labelStyle: '',
|
||||
id: getEdgeId(from, to, {
|
||||
counter: graphItemCount,
|
||||
}),
|
||||
classes: CSS_EDGE_NOTE_EDGE,
|
||||
arrowheadStyle: G_EDGE_ARROWHEADSTYLE,
|
||||
labelpos: G_EDGE_LABELPOS,
|
||||
labelType: G_EDGE_LABELTYPE,
|
||||
thickness: G_EDGE_THICKNESS,
|
||||
});
|
||||
|
||||
graphItemCount++;
|
||||
} else {
|
||||
g.setNode(itemId, nodeData);
|
||||
}
|
||||
@@ -324,7 +329,9 @@ const setupDoc = (g, parentParsedItem, doc, diagramStates, diagramDb, altFlag) =
|
||||
setupNode(g, parentParsedItem, item.state1, diagramStates, diagramDb, altFlag);
|
||||
setupNode(g, parentParsedItem, item.state2, diagramStates, diagramDb, altFlag);
|
||||
const edgeData = {
|
||||
id: 'edge' + graphItemCount,
|
||||
id: getEdgeId(item.state1.id, item.state2.id, {
|
||||
counter: graphItemCount,
|
||||
}),
|
||||
arrowhead: 'normal',
|
||||
arrowTypeEnd: 'arrow_barb',
|
||||
style: G_EDGE_STYLE,
|
||||
|
@@ -36,6 +36,7 @@ export const draw = (txt: string, id: string, _version: string, diagObj: Diagram
|
||||
.attr('height', chartConfig.height)
|
||||
.attr('class', 'background');
|
||||
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
configureSvgSize(svg, chartConfig.height, chartConfig.width, true);
|
||||
|
||||
svg.attr('viewBox', `0 0 ${chartConfig.width} ${chartConfig.height}`);
|
||||
|
BIN
packages/mermaid/src/docs/config/img/mathMLDifferences.png
Normal file
BIN
packages/mermaid/src/docs/config/img/mathMLDifferences.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@@ -60,3 +60,13 @@ Example with legacy mode enabled (the latest version of KaTeX's stylesheet can b
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Handling Rendering Differences
|
||||
|
||||
Due to differences between default fonts across operating systems and browser's MathML implementations, inconsistent results can be seen across platforms. If having consistent results are important, or the most optimal rendered results are desired, `forceLegacyMathML` can be enabled in the config.
|
||||
|
||||
This option will always use KaTeX's stylesheet instead of only when MathML is not supported (as with `legacyMathML`). Note that only `forceLegacyMathML` needs to be set.
|
||||
|
||||
If including KaTeX's stylesheet is not a concern, enabling this option is recommended to avoid scenarios where no MathML implementation within a browser provides the desired output (as seen below).
|
||||
|
||||

|
||||
|
@@ -244,6 +244,7 @@ Communication tools and platforms
|
||||
- [Jekyll](https://jekyllrb.com/)
|
||||
- [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid)
|
||||
- [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams)
|
||||
- [MarkChart: Preview Mermaid diagrams on macOS](https://markchart.app/)
|
||||
- [mermaid-isomorphic](https://github.com/remcohaszing/mermaid-isomorphic)
|
||||
- [mermaid-server: Generate diagrams using a HTTP request](https://github.com/TomWright/mermaid-server)
|
||||
- [NiceGUI: Let any browser be the frontend of your Python code](https://nicegui.io) ✅
|
||||
|
@@ -10,8 +10,10 @@
|
||||
*
|
||||
* In addition to the render function, a number of behavioral configuration options are available.
|
||||
*/
|
||||
// @ts-ignore TODO: Investigate D3 issue
|
||||
import { select } from 'd3';
|
||||
import { compile, serialize, stringify } from 'stylis';
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
import { version } from '../package.json';
|
||||
import * as configApi from './config.js';
|
||||
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
|
||||
|
@@ -180,6 +180,13 @@ properties:
|
||||
fall back to legacy rendering for KaTeX.
|
||||
type: boolean
|
||||
default: false
|
||||
forceLegacyMathML:
|
||||
description: |
|
||||
This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS
|
||||
fonts and browser's MathML implementation, this option is recommended if consistent rendering is important.
|
||||
If set to true, ignores legacyMathML.
|
||||
type: boolean
|
||||
default: false
|
||||
deterministicIds:
|
||||
description: |
|
||||
This option controls if the generated ids of nodes in the SVG are
|
||||
|
@@ -1,5 +1,3 @@
|
||||
import type { interpolateToCurve } from './utils.js';
|
||||
|
||||
export interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
@@ -32,7 +30,7 @@ export interface EdgeData {
|
||||
arrowTypeEnd: string;
|
||||
style: string;
|
||||
labelStyle: string;
|
||||
curve: ReturnType<typeof interpolateToCurve>;
|
||||
curve: any;
|
||||
}
|
||||
|
||||
export type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
import type { CurveFactory, Selection } from 'd3';
|
||||
import type { CurveFactory } from 'd3';
|
||||
import {
|
||||
curveBasis,
|
||||
curveBasisClosed,
|
||||
@@ -230,12 +230,16 @@ export const isSubstringInArray = function (str: string, arr: string[]): number
|
||||
* @param defaultCurve - The default curve to return
|
||||
* @returns The curve factory to use
|
||||
*/
|
||||
export function interpolateToCurve(interpolate: string | undefined, defaultCurve: CurveFactory) {
|
||||
export function interpolateToCurve(
|
||||
interpolate: string | undefined,
|
||||
defaultCurve: CurveFactory
|
||||
): CurveFactory {
|
||||
if (!interpolate) {
|
||||
return defaultCurve;
|
||||
}
|
||||
const curveName = `curve${interpolate.charAt(0).toUpperCase() + interpolate.slice(1)}`;
|
||||
|
||||
// @ts-ignore TODO: Fix issue with curve type
|
||||
return d3CurveTypes[curveName as keyof typeof d3CurveTypes] ?? defaultCurve;
|
||||
}
|
||||
|
||||
@@ -483,25 +487,6 @@ export const random = (options: { length: number }) => {
|
||||
return makeRandomHex(options.length);
|
||||
};
|
||||
|
||||
interface TextData {
|
||||
text: string;
|
||||
x: number;
|
||||
y: number;
|
||||
anchor: 'start' | 'middle' | 'end';
|
||||
fontFamily?: string;
|
||||
fontSize?: string | number;
|
||||
fontWeight?: string | number;
|
||||
fill?: string;
|
||||
class?: string;
|
||||
textMargin: number;
|
||||
style?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
rx?: number;
|
||||
ry?: number;
|
||||
valign?: string;
|
||||
}
|
||||
|
||||
export const getTextObj = function () {
|
||||
return {
|
||||
x: 0,
|
||||
@@ -516,7 +501,7 @@ export const getTextObj = function () {
|
||||
ry: 0,
|
||||
valign: undefined,
|
||||
text: '',
|
||||
} satisfies TextData;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -527,9 +512,20 @@ export const getTextObj = function () {
|
||||
* @returns Text element with given styling and content
|
||||
*/
|
||||
export const drawSimpleText = function (
|
||||
elem: Selection<SVGSVGElement, any, HTMLElement, any>,
|
||||
textData: TextData
|
||||
) {
|
||||
elem: SVGElement,
|
||||
textData: {
|
||||
text: string;
|
||||
x: number;
|
||||
y: number;
|
||||
anchor: 'start' | 'middle' | 'end';
|
||||
fontFamily: string;
|
||||
fontSize: string | number;
|
||||
fontWeight: string | number;
|
||||
fill: string;
|
||||
class: string | undefined;
|
||||
textMargin: number;
|
||||
}
|
||||
): SVGTextElement {
|
||||
// Remove and ignore br:s
|
||||
const nText = textData.text.replace(common.lineBreakRegex, ' ');
|
||||
|
||||
@@ -693,7 +689,7 @@ export const calculateTextDimensions: (
|
||||
text: string,
|
||||
config: TextDimensionConfig
|
||||
) => TextDimensions = memoize(
|
||||
(text: string, config: TextDimensionConfig) => {
|
||||
(text: string, config: TextDimensionConfig): TextDimensions => {
|
||||
const { fontSize = 12, fontFamily = 'Arial', fontWeight = 400 } = config;
|
||||
if (!text) {
|
||||
return { width: 0, height: 0 };
|
||||
@@ -723,7 +719,9 @@ export const calculateTextDimensions: (
|
||||
for (const line of lines) {
|
||||
const textObj = getTextObj();
|
||||
textObj.text = line || ZERO_WIDTH_SPACE;
|
||||
// @ts-ignore TODO: Fix D3 types
|
||||
const textElem = drawSimpleText(g, textObj)
|
||||
// @ts-ignore TODO: Fix D3 types
|
||||
.style('font-size', _fontSizePx)
|
||||
.style('font-weight', fontWeight)
|
||||
.style('font-family', fontFamily);
|
||||
@@ -931,3 +929,19 @@ export const decodeEntities = function (text: string): string {
|
||||
export const isString = (value: unknown): value is string => {
|
||||
return typeof value === 'string';
|
||||
};
|
||||
|
||||
export const getEdgeId = (
|
||||
from: string,
|
||||
to: string,
|
||||
{
|
||||
counter = 0,
|
||||
prefix,
|
||||
suffix,
|
||||
}: {
|
||||
counter?: number;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
}
|
||||
) => {
|
||||
return `${prefix ? `${prefix}_` : ''}${from}_${to}_${counter}${suffix ? `_${suffix}` : ''}`;
|
||||
};
|
||||
|
1576
pnpm-lock.yaml
generated
1576
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user