mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
Merge pull request #2885 from NicolasNewman/feature/2776_katex_math
Integrated Katex typesetting into flowcharts
This commit is contained in:
commit
fe1cff3f55
@ -68,6 +68,7 @@
|
||||
"jgreywolf",
|
||||
"jison",
|
||||
"jiti",
|
||||
"katex",
|
||||
"kaufmann",
|
||||
"khroma",
|
||||
"klemm",
|
||||
@ -81,6 +82,7 @@
|
||||
"logmsg",
|
||||
"lucida",
|
||||
"markdownish",
|
||||
"mathml",
|
||||
"matthieu",
|
||||
"matthieumorel",
|
||||
"mdast",
|
||||
|
36
cypress/integration/rendering/katex.spec.js
Normal file
36
cypress/integration/rendering/katex.spec.js
Normal file
@ -0,0 +1,36 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
|
||||
describe('Katex', () => {
|
||||
it('1: should render a complex Katex flowchart no htmlLabels', () => {
|
||||
imgSnapshotTest(
|
||||
`graph LR
|
||||
A["$$f(\\relax{x}) = \\int_{-\\infty}^\\infty \\hat{f}(\\xi)\\,e^{2 \\pi i \\xi x}\\,d\\xi$$"] -->|"$$\\Bigg(\\bigg(\\Big(\\big((\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a})\\big)\\Big)\\bigg)\\Bigg)$$"| B("$$1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}} {1+\\frac{e^{-8\\pi}} {1+\\cdots}}}}$$")
|
||||
A -->|"$$\\overbrace{a+b+c}^{\\text{note}}$$"| C("$$\\phase{-78^\\circ}$$")
|
||||
B --> D("$$x = \\begin{cases} a &\\text{if } b \\\\ c &\\text{if } d \\end{cases}$$")
|
||||
C --> E("$$x(t)=c_1\\begin{bmatrix}-\\cos{t}+\\sin{t}\\\\ 2\\cos{t} \\end{bmatrix}e^{2t}$$")`,
|
||||
{ fontFamily: 'courier' }
|
||||
);
|
||||
});
|
||||
it('2: should render a Katex flowchart containing the Greek alphabet', () => {
|
||||
imgSnapshotTest(
|
||||
`graph LR
|
||||
A["$$\\alpha\\beta\\gamma\\delta\\epsilon\\zeta\\eta\\theta\\iota\\kappa\\lambda\\mu\\nu\\xi\\omicron\\pi\\rho\\sigma\\tau\\upsilon\\phi\\chi\\psi\\omega$$"] --> B["$$\\Alpha\\Beta\\Gamma\\Delta\\Epsilon\\Zeta\\Eta\\Theta\\Iota\\Kappa\\Lambda\\Mu\\Nu\\Xi\\Omicron\\Pi\\Rho\\Sigma\\Tau\\Upsilon\\Phi\\Chi\\Psi\\Omega$$"]`,
|
||||
{ fontFamily: 'courier' }
|
||||
);
|
||||
});
|
||||
it('3: should render a Katex flowchart containing set theory symbols', () => {
|
||||
imgSnapshotTest(
|
||||
`graph LR
|
||||
A["$$\\forall\\complement\\therefore\\emptyset\\exists\\subset\\because\\empty\\exist\\supset\\mapsto\\varnothing\\nexists\\mid\\to\\implies\\in\\land\\gets\\impliedby\\isin\\lor\\leftrightarrow\\iff\\notin\\ni\\notni\\lnot$$"] --> B["$$\\nabla\\Im\\Reals\\jmath\\partial\\image\\wp\\aleph\\Game\\weierp\\alef\\Finv\\N\\Z\\alefsym\\cnums\\natnums\\beth\\Complex\\R\\gimel\\ell\\Re\\daleth\\hbar\\real\\eth\\hslash\\reals$$"]`,
|
||||
{ fontFamily: 'courier' }
|
||||
);
|
||||
});
|
||||
// TODO: changes made to develop between Feb 13 - Feb 23 cause this test to no longer function
|
||||
// it.skip('4: should render an error box originating from Katex', () => {
|
||||
// imgSnapshotTest(
|
||||
// `graph LR
|
||||
// A["$$\\shouldBeError$$"]`,
|
||||
// { fontFamily: 'courier' }
|
||||
// );
|
||||
// });
|
||||
});
|
@ -1102,6 +1102,57 @@
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Sample 20</h2>
|
||||
<h3>graph</h3>
|
||||
<pre class="mermaid">
|
||||
graph LR
|
||||
A["$$f(\relax{x}) = \int_{-\infty}^\infty \hat{f}(\xi)\,e^{2 \pi i \xi x}\,d\xi$$"] -->|"$$\Bigg(\bigg(\Big(\big((\frac{-b\pm\sqrt{b^2-4ac}}{2a})\big)\Big)\bigg)\Bigg)$$"| B("$$1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots}}}}$$")
|
||||
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\phase{-78^\circ}$$")
|
||||
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h3>flowchart</h3>
|
||||
<pre class="mermaid">
|
||||
flowchart LR
|
||||
A["$$f(\relax{x}) = \int_{-\infty}^\infty \hat{f}(\xi)\,e^{2 \pi i \xi x}\,d\xi$$"] -->|"$$\Bigg(\bigg(\Big(\big((\frac{-b\pm\sqrt{b^2-4ac}}{2a})\big)\Big)\bigg)\Bigg)$$"| B("$$1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots}}}}$$")
|
||||
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\phase{-78^\circ}$$")
|
||||
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Sample 21</h2>
|
||||
<h3>graph</h3>
|
||||
<pre class="mermaid">
|
||||
graph LR
|
||||
A["$$\alpha\beta\gamma\delta\epsilon\zeta\eta\theta\iota\kappa\lambda\mu\nu\xi\omicron\pi\rho\sigma\tau\upsilon\phi\chi\psi\omega$$"] --> B["$$\Alpha\Beta\Gamma\Delta\Epsilon\Zeta\Eta\Theta\Iota\Kappa\Lambda\Mu\Nu\Xi\Omicron\Pi\Rho\Sigma\Tau\Upsilon\Phi\Chi\Psi\Omega$$"]
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h3>flowchart</h3>
|
||||
<pre class="mermaid">
|
||||
graph LR
|
||||
A["$$\alpha\beta\gamma\delta\epsilon\zeta\eta\theta\iota\kappa\lambda\mu\nu\xi\omicron\pi\rho\sigma\tau\upsilon\phi\chi\psi\omega$$"] --> B["$$\Alpha\Beta\Gamma\Delta\Epsilon\Zeta\Eta\Theta\Iota\Kappa\Lambda\Mu\Nu\Xi\Omicron\Pi\Rho\Sigma\Tau\Upsilon\Phi\Chi\Psi\Omega$$"]
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>Sample 22</h2>
|
||||
<h3>graph</h3>
|
||||
<pre class="mermaid">
|
||||
graph LR
|
||||
A["$$\forall\complement\therefore\emptyset\exists\subset\because\empty\exist\supset\mapsto\varnothing\nexists\mid\to\implies\in\land\gets\impliedby\isin\lor\leftrightarrow\iff\notin\ni\notni\lnot$$"] --> B["$$\nabla\Im\Reals\jmath\partial\image\wp\aleph\Game\weierp\alef\Finv\N\Z\alefsym\cnums\natnums\beth\Complex\R\gimel\ell\Re\daleth\hbar\real\eth\hslash\reals$$"]
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h3>flowchart</h3>
|
||||
<pre class="mermaid">
|
||||
graph LR
|
||||
A["$$\forall\complement\therefore\emptyset\exists\subset\because\empty\exist\supset\mapsto\varnothing\nexists\mid\to\implies\in\land\gets\impliedby\isin\lor\leftrightarrow\iff\notin\ni\notni\lnot$$"] --> B["$$\nabla\Im\Reals\jmath\partial\image\wp\aleph\Game\weierp\alef\Finv\N\Z\alefsym\cnums\natnums\beth\Complex\R\gimel\ell\Re\daleth\hbar\real\eth\hslash\reals$$"]
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<hr />
|
||||
|
||||
<pre class="mermaid">
|
||||
@ -1524,11 +1575,11 @@
|
||||
F{Flow 2} == Choice 2.1 ==> H[Feedback node]
|
||||
H[Feedback node] ==> B[Step 1]
|
||||
F{Flow 2} == Choice 2.2 ==> G((Finish))
|
||||
|
||||
|
||||
linkStyle 0,1,4,6,7,8,9 stroke:gold, stroke-width:4px
|
||||
|
||||
classDef active_node fill:#0CF,stroke:#09F,stroke-width:6px
|
||||
classDef unactive_node fill:#e0e0e0,stroke:#bdbdbd,stroke-width:3px
|
||||
classDef unactive_node fill:#e0e0e0,stroke:#bdbdbd,stroke-width:3px
|
||||
classDef bugged_node fill:#F88,stroke:#F22,stroke-width:3px
|
||||
classDef start_node,finish_node fill:#3B1,stroke:#391,stroke-width:8px
|
||||
|
||||
|
@ -16,9 +16,9 @@
|
||||
<body>
|
||||
<h1>Sequence diagram demos</h1>
|
||||
<pre class="mermaid">
|
||||
sequenceDiagram
|
||||
accTitle: test the accTitle
|
||||
accDescr: Test a description
|
||||
sequenceDiagram
|
||||
accTitle: test the accTitle
|
||||
accDescr: Test a description
|
||||
|
||||
participant Alice
|
||||
participant Bob
|
||||
@ -31,39 +31,39 @@
|
||||
rect rgb(200, 220, 100)
|
||||
rect rgb(200, 255, 200)
|
||||
|
||||
Alice ->> Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?
|
||||
end
|
||||
Alice ->> Bob: Hello Bob, how are you?
|
||||
Bob-->>John: How about you John?
|
||||
end
|
||||
|
||||
Bob--x Alice: I am good thanks!
|
||||
Bob-x John: I am good thanks!
|
||||
Note right of John: John thinks a long<br />long time, so long<br />that the text does<br />not fit on a row.
|
||||
Bob--x Alice: I am good thanks!
|
||||
Bob-x John: I am good thanks!
|
||||
Note right of John: John thinks a long<br />long time, so long<br />that the text does<br />not fit on a row.
|
||||
|
||||
Bob-->Alice: Checking with John...
|
||||
Note over John:wrap: John looks like he's still thinking, so Bob prods him a bit.
|
||||
Bob-x John: Hey John - we're still waiting to know<br />how you're doing
|
||||
Note over John:nowrap: John's trying hard not to break his train of thought.
|
||||
Bob-x John:wrap: John! Are you still debating about how you're doing? How long does it take??
|
||||
Note over John: After a few more moments, John<br />finally snaps out of it.
|
||||
end
|
||||
Bob-->Alice: Checking with John...
|
||||
Note over John:wrap: John looks like he's still thinking, so Bob prods him a bit.
|
||||
Bob-x John: Hey John - we're still waiting to know<br />how you're doing
|
||||
Note over John:nowrap: John's trying hard not to break his train of thought.
|
||||
Bob-x John:wrap: John! Are you still debating about how you're doing? How long does it take??
|
||||
Note over John: After a few more moments, John<br />finally snaps out of it.
|
||||
end
|
||||
|
||||
autonumber off
|
||||
alt either this
|
||||
Alice->>+John: Yes
|
||||
John-->>-Alice: OK
|
||||
else or this
|
||||
autonumber
|
||||
Alice->>John: No
|
||||
else or this will happen
|
||||
Alice->John: Maybe
|
||||
end
|
||||
autonumber 200
|
||||
par this happens in parallel
|
||||
Alice -->> Bob: Parallel message 1
|
||||
and
|
||||
Alice -->> John: Parallel message 2
|
||||
end
|
||||
</pre>
|
||||
autonumber off
|
||||
alt either this
|
||||
Alice->>+John: Yes
|
||||
John-->>-Alice: OK
|
||||
else or this
|
||||
autonumber
|
||||
Alice->>John: No
|
||||
else or this will happen
|
||||
Alice->John: Maybe
|
||||
end
|
||||
autonumber 200
|
||||
par this happens in parallel
|
||||
Alice -->> Bob: Parallel message 1
|
||||
and
|
||||
Alice -->> John: Parallel message 2
|
||||
end
|
||||
</pre>
|
||||
<hr />
|
||||
<pre class="mermaid">
|
||||
---
|
||||
@ -153,18 +153,18 @@
|
||||
<hr />
|
||||
|
||||
<pre class="mermaid">
|
||||
sequenceDiagram
|
||||
box lightgreen Alice & John
|
||||
participant A
|
||||
participant J
|
||||
end
|
||||
box Another Group very very long description not wrapped
|
||||
participant B
|
||||
end
|
||||
A->>J: Hello John, how are you?
|
||||
J->>A: Great!
|
||||
A->>B: Hello Bob, how are you ?
|
||||
</pre
|
||||
sequenceDiagram
|
||||
box lightgreen Alice & John
|
||||
participant A
|
||||
participant J
|
||||
end
|
||||
box Another Group very very long description not wrapped
|
||||
participant B
|
||||
end
|
||||
A->>J: Hello John, how are you?
|
||||
J->>A: Great!
|
||||
A->>B: Hello Bob, how are you ?
|
||||
</pre
|
||||
>
|
||||
<hr />
|
||||
|
||||
@ -188,6 +188,49 @@
|
||||
end
|
||||
</pre>
|
||||
|
||||
<hr />
|
||||
|
||||
<pre class="mermaid">
|
||||
sequenceDiagram
|
||||
participant 1 as $$\frac{\lim_{x\rightarrow0}{\frac{1}{x}}}{\frac{-b\pm\sqrt{b^2-4ac}}{2a}}$$
|
||||
participant 2 as $$\beta$$
|
||||
participant 3 as $$\delta$$
|
||||
participant 4 as $$\frac{\frac{\lim_{x\rightarrow0}{\frac{1}{x}}}{\frac{-b\pm\sqrt{b^2-4ac}}{2a}}}{\frac{\text{d}}{\text{d}x}{x^2}}$$
|
||||
1->>2: $$\sqrt{2}$$
|
||||
note right of 2: $$\frac{1+\frac{1+\frac{1+\frac{1}{2}}{2}}{2}}{2}+\frac{-b\pm\sqrt{b^2-4ac}}{2a}$$
|
||||
2->>3: $$\frac{\lim_{x\rightarrow0}{\frac{1}{x}}}{\frac{-b\pm\sqrt{b^2-4ac}}{2a}}$$
|
||||
note right of 3: $$\frac{-b\pm\sqrt{b^2-4ac}}{2a}$$
|
||||
3->>4: $$\lim_{x\rightarrow0}{\frac{1}{x}}$$;
|
||||
note right of 4: multiline
|
||||
4->>1: multiline<br />using #lt;br /#gt;
|
||||
note right of 1: multiline<br />$$\frac{1}{2}$$<br />3rd line
|
||||
</pre>
|
||||
<hr />
|
||||
<pre class="mermaid">
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant 1 as $$\alpha$$lex
|
||||
participant 2 as $$\beta$$ob
|
||||
participant 3 as $$\theta$$iffany
|
||||
1->>2: Hello John, does $$\frac{1}{2}+1=2$$?
|
||||
loop $$\frac{1}{2}+1=2$$
|
||||
2->>2: $$\frac{1}{2}+1=\frac{3}{2}$$
|
||||
end
|
||||
Note right of 2: $$x = \begin{cases} 1 &\text{if } \frac{1}{2}+1=2 \\ 0 &\text{if } \frac{1}{2}+1\ne2 \end{cases}$$
|
||||
2-->>1: $$\frac{1}{2}+1\ne2\implies 1$$
|
||||
2->>3: $$\frac{\text{d}}{\text{d}x}{3x^2+2x+1}$$
|
||||
3-->>2: $$6x+2$$
|
||||
</pre>
|
||||
|
||||
<hr />
|
||||
|
||||
<pre class="mermaid">
|
||||
sequenceDiagram
|
||||
actor Alice
|
||||
actor John
|
||||
Alice-xJohn: Hello John, how are you?
|
||||
John--xAlice: Great!
|
||||
</pre>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.initialize({
|
||||
|
86
docs/config/math.md
Normal file
86
docs/config/math.md
Normal file
@ -0,0 +1,86 @@
|
||||
> **Warning**
|
||||
>
|
||||
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||
>
|
||||
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/math.md](../../packages/mermaid/src/docs/config/math.md).
|
||||
|
||||
# Math Configuration (v\<MERMAID_RELEASE_VERSION>+)
|
||||
|
||||
Mermaid supports rendering mathematical expressions through the [KaTeX](https://katex.org/) typesetter.
|
||||
|
||||
## Usage
|
||||
|
||||
To render math within a diagram, surround the mathematical expression with the `$$` delimiter.
|
||||
|
||||
Note that at the moment, the only supported diagrams are below:
|
||||
|
||||
### Flowcharts
|
||||
|
||||
```mermaid-example
|
||||
graph LR
|
||||
A["$$x^2$$"] -->|"$$\sqrt{x+3}$$"| B("$$\frac{1}{2}$$")
|
||||
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\pi r^2$$")
|
||||
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||
```
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A["$$x^2$$"] -->|"$$\sqrt{x+3}$$"| B("$$\frac{1}{2}$$")
|
||||
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\pi r^2$$")
|
||||
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||
```
|
||||
|
||||
### Sequence
|
||||
|
||||
```mermaid-example
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant 1 as $$\alpha$$
|
||||
participant 2 as $$\beta$$
|
||||
1->>2: Solve: $$\sqrt{2+2}$$
|
||||
2-->>1: Answer: $$2$$
|
||||
Note right of 2: $$\sqrt{2+2}=\sqrt{4}=2$$
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant 1 as $$\alpha$$
|
||||
participant 2 as $$\beta$$
|
||||
1->>2: Solve: $$\sqrt{2+2}$$
|
||||
2-->>1: Answer: $$2$$
|
||||
Note right of 2: $$\sqrt{2+2}=\sqrt{4}=2$$
|
||||
```
|
||||
|
||||
## Legacy Support
|
||||
|
||||
By default, MathML is used for rendering mathematical expressions. If you have users on [unsupported browsers](https://caniuse.com/?search=mathml), `legacyMathML` can be set in the config to fall back to CSS rendering. Note that **you must provide KaTeX's stylesheets on your own** as they do not come bundled with Mermaid.
|
||||
|
||||
Example with legacy mode enabled (the latest version of KaTeX's stylesheet can be found on their [docs](https://katex.org/docs/browser.html)):
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Please ensure the stylesheet's version matches with the KaTeX version in your package-lock -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/katex@{version_number}/dist/katex.min.css"
|
||||
integrity="sha384-{hash}"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.initialize({
|
||||
legacyMathML: true,
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
@ -70,6 +70,7 @@
|
||||
"dayjs": "^1.11.7",
|
||||
"dompurify": "^3.0.5",
|
||||
"elkjs": "^0.9.0",
|
||||
"katex": "^0.16.9",
|
||||
"khroma": "^2.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mdast-util-from-markdown": "^1.3.0",
|
||||
@ -89,6 +90,7 @@
|
||||
"@types/d3-shape": "^3.1.1",
|
||||
"@types/dompurify": "^3.0.2",
|
||||
"@types/jsdom": "^21.1.1",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/lodash-es": "^4.17.7",
|
||||
"@types/micromatch": "^4.0.2",
|
||||
"@types/prettier": "^2.7.2",
|
||||
|
@ -127,6 +127,14 @@ export interface MermaidConfig {
|
||||
*
|
||||
*/
|
||||
secure?: string[];
|
||||
/**
|
||||
* This option specifies if Mermaid can expect the dependent to include KaTeX stylesheets for browsers
|
||||
* without their own MathML implementation. If this option is disabled and MathML is not supported, the math
|
||||
* equations are replaced with a warning. If this option is enabled and MathML is not supported, Mermaid will
|
||||
* fall back to legacy rendering for KaTeX.
|
||||
*
|
||||
*/
|
||||
legacyMathML?: boolean;
|
||||
/**
|
||||
* This option controls if the generated ids of nodes in the SVG are
|
||||
* generated randomly or based on a seed.
|
||||
|
@ -289,6 +289,83 @@ const processSet = (input: string): string => {
|
||||
return chars.join('');
|
||||
};
|
||||
|
||||
// TODO: find a better method for detecting support. This interface was added in the MathML 4 spec.
|
||||
// Firefox versions between [4,71] (0.47%) and Safari versions between [5,13.4] (0.17%) don't have this interface implemented but MathML is supported
|
||||
export const isMathMLSupported = () => window.MathMLElement !== undefined;
|
||||
|
||||
export const katexRegex = /\$\$(.*)\$\$/g;
|
||||
|
||||
/**
|
||||
* Whether or not a text has KaTeX delimiters
|
||||
*
|
||||
* @param text - The text to test
|
||||
* @returns Whether or not the text has KaTeX delimiters
|
||||
*/
|
||||
export const hasKatex = (text: string): boolean => (text.match(katexRegex)?.length ?? 0) > 0;
|
||||
|
||||
/**
|
||||
* Computes the minimum dimensions needed to display a div containing MathML
|
||||
*
|
||||
* @param text - The text to test
|
||||
* @param config - Configuration for Mermaid
|
||||
* @returns Object containing \{width, height\}
|
||||
*/
|
||||
export const calculateMathMLDimensions = async (text: string, config: MermaidConfig) => {
|
||||
text = await renderKatex(text, config);
|
||||
const divElem = document.createElement('div');
|
||||
divElem.innerHTML = text;
|
||||
divElem.id = 'katex-temp';
|
||||
divElem.style.visibility = 'hidden';
|
||||
divElem.style.position = 'absolute';
|
||||
divElem.style.top = '0';
|
||||
const body = document.querySelector('body');
|
||||
body?.insertAdjacentElement('beforeend', divElem);
|
||||
const dim = { width: divElem.clientWidth, height: divElem.clientHeight };
|
||||
divElem.remove();
|
||||
return dim;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to render and return the KaTeX portion of a string with MathML
|
||||
*
|
||||
* @param text - The text to test
|
||||
* @param config - Configuration for Mermaid
|
||||
* @returns String containing MathML if KaTeX is supported, or an error message if it is not and stylesheets aren't present
|
||||
*/
|
||||
export const renderKatex = async (text: string, config: MermaidConfig): Promise<string> => {
|
||||
if (!hasKatex(text)) {
|
||||
return text;
|
||||
}
|
||||
|
||||
if (!isMathMLSupported() && !config.legacyMathML) {
|
||||
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
|
||||
}
|
||||
|
||||
const { default: katex } = await import('katex');
|
||||
return text
|
||||
.split(lineBreakRegex)
|
||||
.map((line) =>
|
||||
hasKatex(line)
|
||||
? `
|
||||
<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
|
||||
${line}
|
||||
</div>
|
||||
`
|
||||
: `<div>${line}</div>`
|
||||
)
|
||||
.join('')
|
||||
.replace(katexRegex, (_, c) =>
|
||||
katex
|
||||
.renderToString(c, {
|
||||
throwOnError: true,
|
||||
displayMode: true,
|
||||
output: isMathMLSupported() ? 'mathml' : 'htmlAndMathml',
|
||||
})
|
||||
.replace(/\n/g, ' ')
|
||||
.replace(/<annotation.*<\/annotation>/g, '')
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
getRows,
|
||||
sanitizeText,
|
||||
|
@ -4,7 +4,7 @@ import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
|
||||
/**
|
||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
||||
* Draws an info picture in the tag with id: id based on the graph definition in text.
|
||||
*
|
||||
* @param _text - Mermaid graph definition.
|
||||
* @param id - The text for the error
|
||||
@ -12,12 +12,12 @@ import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
*/
|
||||
export const draw = (_text: string, id: string, version: string) => {
|
||||
log.debug('rendering svg for syntax error\n');
|
||||
|
||||
const svg: SVG = selectSvgElement(id);
|
||||
const g: Group = svg.append('g');
|
||||
|
||||
svg.attr('viewBox', '0 0 2412 512');
|
||||
configureSvgSize(svg, 100, 512, true);
|
||||
|
||||
const g: Group = svg.append('g');
|
||||
g.append('path')
|
||||
.attr('class', 'error-icon')
|
||||
.attr(
|
||||
|
@ -5,7 +5,7 @@ import utils 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';
|
||||
import common, { evaluate } from '../common/common.js';
|
||||
import common, { evaluate, renderKatex } from '../common/common.js';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||
|
||||
@ -27,12 +27,12 @@ export const setConf = function (cnf) {
|
||||
* @param doc
|
||||
* @param diagObj
|
||||
*/
|
||||
export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
||||
export const addVertices = async function (vert, g, svgId, root, doc, diagObj) {
|
||||
const svg = root.select(`[id="${svgId}"]`);
|
||||
const keys = Object.keys(vert);
|
||||
|
||||
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
|
||||
keys.forEach(function (id) {
|
||||
for (const id of keys) {
|
||||
const vertex = vert[id];
|
||||
|
||||
/**
|
||||
@ -59,10 +59,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
const node = {
|
||||
label: vertexText.replace(
|
||||
/fa[blrs]?:fa-[\w-]+/g,
|
||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||
),
|
||||
label: vertexText,
|
||||
};
|
||||
vertexNode = addHtmlLabel(svg, node).node();
|
||||
vertexNode.parentNode.removeChild(vertexNode);
|
||||
@ -143,11 +140,13 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
||||
default:
|
||||
_shape = 'rect';
|
||||
}
|
||||
const labelText = await renderKatex(vertexText, getConfig());
|
||||
|
||||
// Add the node
|
||||
g.setNode(vertex.id, {
|
||||
labelStyle: styles.labelStyle,
|
||||
shape: _shape,
|
||||
labelText: vertexText,
|
||||
labelText,
|
||||
labelType: vertex.labelType,
|
||||
rx: radious,
|
||||
ry: radious,
|
||||
@ -170,7 +169,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
||||
labelStyle: styles.labelStyle,
|
||||
labelType: vertex.labelType,
|
||||
shape: _shape,
|
||||
labelText: vertexText,
|
||||
labelText,
|
||||
rx: radious,
|
||||
ry: radious,
|
||||
class: classStr,
|
||||
@ -183,7 +182,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
||||
props: vertex.props,
|
||||
padding: getConfig().flowchart.padding,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -193,7 +192,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
||||
* @param {object} g The graph object
|
||||
* @param diagObj
|
||||
*/
|
||||
export const addEdges = function (edges, g, diagObj) {
|
||||
export const addEdges = async function (edges, g, diagObj) {
|
||||
log.info('abc78 edges = ', edges);
|
||||
let cnt = 0;
|
||||
let linkIdCnt = {};
|
||||
@ -207,7 +206,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
defaultLabelStyle = defaultStyles.labelStyle;
|
||||
}
|
||||
|
||||
edges.forEach(function (edge) {
|
||||
for (const edge of edges) {
|
||||
cnt++;
|
||||
|
||||
// Identify Link
|
||||
@ -315,9 +314,8 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
edgeData.arrowheadStyle = 'fill: #333';
|
||||
edgeData.labelpos = 'c';
|
||||
}
|
||||
|
||||
edgeData.labelType = edge.labelType;
|
||||
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
|
||||
edgeData.label = await renderKatex(edge.text.replace(common.lineBreakRegex, '\n'), getConfig());
|
||||
|
||||
if (edge.style === undefined) {
|
||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
|
||||
@ -330,7 +328,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
|
||||
// Add the edge to the graph
|
||||
g.setEdge(edge.start, edge.end, edgeData, cnt);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -427,8 +425,8 @@ export const draw = async function (text, id, _version, diagObj) {
|
||||
g.setParent(subG.nodes[j], subG.id);
|
||||
}
|
||||
}
|
||||
addVertices(vert, g, id, root, doc, diagObj);
|
||||
addEdges(edges, g, diagObj);
|
||||
await addVertices(vert, g, id, root, doc, diagObj);
|
||||
await addEdges(edges, g, diagObj);
|
||||
|
||||
// Add custom shapes
|
||||
// flowChartShapes.addToRenderV2(addShape);
|
||||
|
@ -15,7 +15,7 @@ describe('when using mermaid and ', function () {
|
||||
flowDb.clear();
|
||||
flowDb.setGen('gen-2');
|
||||
});
|
||||
it('should handle edges with text', () => {
|
||||
it('should handle edges with text', async () => {
|
||||
parser.parse('graph TD;A-->|text ex|B;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
@ -29,7 +29,7 @@ describe('when using mermaid and ', function () {
|
||||
},
|
||||
};
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
await flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should handle edges without text', async function () {
|
||||
@ -45,10 +45,10 @@ describe('when using mermaid and ', function () {
|
||||
},
|
||||
};
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
await flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should handle open-ended edges', () => {
|
||||
it('should handle open-ended edges', async () => {
|
||||
parser.parse('graph TD;A---B;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
@ -61,10 +61,10 @@ describe('when using mermaid and ', function () {
|
||||
},
|
||||
};
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
await flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should handle edges with styles defined', () => {
|
||||
it('should handle edges with styles defined', async () => {
|
||||
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
@ -78,9 +78,9 @@ describe('when using mermaid and ', function () {
|
||||
},
|
||||
};
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
await flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
it('should handle edges with interpolation defined', () => {
|
||||
it('should handle edges with interpolation defined', async () => {
|
||||
parser.parse('graph TD;A---B; linkStyle 0 interpolate basis');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
@ -94,9 +94,9 @@ describe('when using mermaid and ', function () {
|
||||
},
|
||||
};
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
await flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
it('should handle edges with text and styles defined', () => {
|
||||
it('should handle edges with text and styles defined', async () => {
|
||||
parser.parse('graph TD;A---|the text|B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
@ -111,10 +111,10 @@ describe('when using mermaid and ', function () {
|
||||
},
|
||||
};
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
await flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should set fill to "none" by default when handling edges', () => {
|
||||
it('should set fill to "none" by default when handling edges', async () => {
|
||||
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
@ -128,10 +128,10 @@ describe('when using mermaid and ', function () {
|
||||
},
|
||||
};
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
await flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
|
||||
it('should not set fill to none if fill is set in linkStyle', () => {
|
||||
it('should not set fill to none if fill is set in linkStyle', async () => {
|
||||
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2,fill:blue;');
|
||||
flowDb.getVertices();
|
||||
const edges = flowDb.getEdges();
|
||||
@ -144,7 +144,7 @@ describe('when using mermaid and ', function () {
|
||||
},
|
||||
};
|
||||
|
||||
flowRenderer.addEdges(edges, mockG, diag);
|
||||
await flowRenderer.addEdges(edges, mockG, diag);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ import { render as Render } from 'dagre-d3-es';
|
||||
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 } from '../common/common.js';
|
||||
import common, { evaluate, renderKatex } from '../common/common.js';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||
import flowChartShapes from './flowChartShapes.js';
|
||||
@ -28,13 +28,13 @@ export const setConf = function (cnf) {
|
||||
* @param _doc
|
||||
* @param diagObj
|
||||
*/
|
||||
export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
||||
export const addVertices = async function (vert, g, svgId, root, _doc, diagObj) {
|
||||
const svg = !root ? select(`[id="${svgId}"]`) : root.select(`[id="${svgId}"]`);
|
||||
const doc = !_doc ? document : _doc;
|
||||
const keys = Object.keys(vert);
|
||||
|
||||
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
|
||||
keys.forEach(function (id) {
|
||||
for (const id of keys) {
|
||||
const vertex = vert[id];
|
||||
|
||||
/**
|
||||
@ -57,9 +57,12 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
const node = {
|
||||
label: vertexText.replace(
|
||||
/fa[blrs]?:fa-[\w-]+/g,
|
||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||
label: await renderKatex(
|
||||
vertexText.replace(
|
||||
/fa[blrs]?:fa-[\w-]+/g,
|
||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||
),
|
||||
getConfig()
|
||||
),
|
||||
};
|
||||
vertexNode = addHtmlLabel(svg, node).node();
|
||||
@ -150,7 +153,7 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
||||
style: styles.style,
|
||||
id: diagObj.db.lookUpDomId(vertex.id),
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -160,7 +163,7 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
||||
* @param {object} g The graph object
|
||||
* @param diagObj
|
||||
*/
|
||||
export const addEdges = function (edges, g, diagObj) {
|
||||
export const addEdges = async function (edges, g, diagObj) {
|
||||
let cnt = 0;
|
||||
|
||||
let defaultStyle;
|
||||
@ -172,7 +175,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
defaultLabelStyle = defaultStyles.labelStyle;
|
||||
}
|
||||
|
||||
edges.forEach(function (edge) {
|
||||
for (const edge of edges) {
|
||||
cnt++;
|
||||
|
||||
// Identify Link
|
||||
@ -239,9 +242,12 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
edgeData.labelType = 'html';
|
||||
edgeData.label = `<span id="L-${linkId}" class="edgeLabel L-${linkNameStart}' L-${linkNameEnd}" style="${
|
||||
edgeData.labelStyle
|
||||
}">${edge.text.replace(
|
||||
/fa[blrs]?:fa-[\w-]+/g,
|
||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||
}">${await renderKatex(
|
||||
edge.text.replace(
|
||||
/fa[blrs]?:fa-[\w-]+/g,
|
||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||
),
|
||||
getConfig()
|
||||
)}</span>`;
|
||||
} else {
|
||||
edgeData.labelType = 'text';
|
||||
@ -261,7 +267,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
|
||||
// Add the edge to the graph
|
||||
g.setEdge(diagObj.db.lookUpDomId(edge.start), diagObj.db.lookUpDomId(edge.end), edgeData, cnt);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -284,7 +290,7 @@ export const getClasses = function (text, diagObj) {
|
||||
* @param _version
|
||||
* @param diagObj
|
||||
*/
|
||||
export const draw = function (text, id, _version, diagObj) {
|
||||
export const draw = async function (text, id, _version, diagObj) {
|
||||
log.info('Drawing flowchart');
|
||||
const { securityLevel, flowchart: conf } = getConfig();
|
||||
let sandboxElement;
|
||||
@ -350,8 +356,8 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
g.setParent(diagObj.db.lookUpDomId(subG.nodes[j]), diagObj.db.lookUpDomId(subG.id));
|
||||
}
|
||||
}
|
||||
addVertices(vert, g, id, root, doc, diagObj);
|
||||
addEdges(edges, g, diagObj);
|
||||
await addVertices(vert, g, id, root, doc, diagObj);
|
||||
await addEdges(edges, g, diagObj);
|
||||
|
||||
// Create the renderer
|
||||
const render = new Render();
|
||||
|
@ -27,7 +27,7 @@ describe('the flowchart renderer', function () {
|
||||
['cylinder', 'cylinder'],
|
||||
['group', 'rect'],
|
||||
].forEach(function ([type, expectedShape, expectedRadios = 0]) {
|
||||
it(`should add the correct shaped node to the graph for vertex type ${type}`, function () {
|
||||
it(`should add the correct shaped node to the graph for vertex type ${type}`, async function () {
|
||||
const fakeDiag = {
|
||||
db: {
|
||||
lookUpDomId: () => {
|
||||
@ -41,7 +41,7 @@ describe('the flowchart renderer', function () {
|
||||
addedNodes.push([id, object]);
|
||||
},
|
||||
};
|
||||
addVertices(
|
||||
await addVertices(
|
||||
{
|
||||
v1: {
|
||||
type,
|
||||
@ -70,7 +70,7 @@ describe('the flowchart renderer', function () {
|
||||
['Multi<br>Line', 'Multi<br/>Line', 'Multi<br />Line', 'Multi<br\t/>Line'].forEach(function (
|
||||
labelText
|
||||
) {
|
||||
it('should handle multiline texts with different line breaks', function () {
|
||||
it('should handle multiline texts with different line breaks', async function () {
|
||||
const addedNodes = [];
|
||||
const fakeDiag = {
|
||||
db: {
|
||||
@ -84,7 +84,7 @@ describe('the flowchart renderer', function () {
|
||||
addedNodes.push([id, object]);
|
||||
},
|
||||
};
|
||||
addVertices(
|
||||
await addVertices(
|
||||
{
|
||||
v1: {
|
||||
type: 'rect',
|
||||
@ -121,7 +121,7 @@ describe('the flowchart renderer', function () {
|
||||
'color:#ccc;text-align:center;',
|
||||
],
|
||||
].forEach(function ([style, expectedStyle, expectedLabelStyle]) {
|
||||
it(`should add the styles to style and/or labelStyle for style ${style}`, function () {
|
||||
it(`should add the styles to style and/or labelStyle for style ${style}`, async function () {
|
||||
const addedNodes = [];
|
||||
const fakeDiag = {
|
||||
db: {
|
||||
@ -135,7 +135,7 @@ describe('the flowchart renderer', function () {
|
||||
addedNodes.push([id, object]);
|
||||
},
|
||||
};
|
||||
addVertices(
|
||||
await addVertices(
|
||||
{
|
||||
v1: {
|
||||
type: 'rect',
|
||||
@ -160,7 +160,7 @@ describe('the flowchart renderer', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it(`should add default class to all nodes which do not have another class assigned`, function () {
|
||||
it(`should add default class to all nodes which do not have another class assigned`, async function () {
|
||||
const addedNodes = [];
|
||||
const mockG = {
|
||||
setNode: function (id, object) {
|
||||
@ -174,7 +174,7 @@ describe('the flowchart renderer', function () {
|
||||
},
|
||||
},
|
||||
};
|
||||
addVertices(
|
||||
await addVertices(
|
||||
{
|
||||
v1: {
|
||||
type: 'rect',
|
||||
@ -206,7 +206,7 @@ describe('the flowchart renderer', function () {
|
||||
});
|
||||
|
||||
describe('when adding edges to a graph', function () {
|
||||
it('should handle multiline texts and set centered label position', function () {
|
||||
it('should handle multiline texts and set centered label position', async function () {
|
||||
const addedEdges = [];
|
||||
const fakeDiag = {
|
||||
db: {
|
||||
@ -220,7 +220,7 @@ describe('the flowchart renderer', function () {
|
||||
addedEdges.push(data);
|
||||
},
|
||||
};
|
||||
addEdges(
|
||||
await addEdges(
|
||||
[
|
||||
{ text: 'Multi<br>Line' },
|
||||
{ text: 'Multi<br/>Line' },
|
||||
@ -251,7 +251,7 @@ describe('the flowchart renderer', function () {
|
||||
'fill:red;',
|
||||
],
|
||||
].forEach(function ([style, expectedStyle, expectedLabelStyle]) {
|
||||
it(`should add the styles to style and/or labelStyle for style ${style}`, function () {
|
||||
it(`should add the styles to style and/or labelStyle for style ${style}`, async function () {
|
||||
const addedEdges = [];
|
||||
const fakeDiag = {
|
||||
db: {
|
||||
@ -265,7 +265,7 @@ describe('the flowchart renderer', function () {
|
||||
addedEdges.push(data);
|
||||
},
|
||||
};
|
||||
addEdges([{ style: style, text: 'styling' }], mockG, fakeDiag);
|
||||
await addEdges([{ style: style, text: 'styling' }], mockG, fakeDiag);
|
||||
|
||||
expect(addedEdges).toHaveLength(1);
|
||||
expect(addedEdges[0]).toHaveProperty('style', expectedStyle);
|
||||
|
@ -66,6 +66,12 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
||||
// text-anchor: start;
|
||||
// }
|
||||
|
||||
.node .katex path {
|
||||
fill: #000;
|
||||
stroke: #000;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.node .label {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ Note right of Bob: Bob thinks
|
||||
Bob-->Alice: I am good thanks!`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
|
||||
expect(diagram.db.showSequenceNumbers()).toBe(false);
|
||||
});
|
||||
it('should show sequence numbers when autonumber is enabled', async () => {
|
||||
@ -221,7 +221,7 @@ Note right of Bob: Bob thinks
|
||||
Bob-->Alice: I am good thanks!`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
|
||||
expect(diagram.db.showSequenceNumbers()).toBe(true);
|
||||
});
|
||||
|
||||
@ -1648,7 +1648,7 @@ participant Alice`;
|
||||
// mermaidAPI.reinitialize({ sequence: { textPlacement: textPlacement } });
|
||||
await mermaidAPI.parse(str);
|
||||
// diagram.renderer.setConf(mermaidAPI.getConfig().sequence);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
@ -1679,7 +1679,7 @@ Note over Alice: Alice thinks
|
||||
|
||||
expect(mermaidAPI.getConfig().sequence.mirrorActors).toBeFalsy();
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
@ -1695,7 +1695,7 @@ participant Alice
|
||||
Note left of Alice: Alice thinks`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
|
||||
@ -1711,7 +1711,7 @@ participant Alice
|
||||
Note right of Alice: Alice thinks`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
@ -1726,7 +1726,7 @@ sequenceDiagram
|
||||
Alice->Bob: Hello Bob, how are you?`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
@ -1744,7 +1744,7 @@ end
|
||||
Alice->Bob: Hello Bob, how are you?`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
@ -1759,7 +1759,7 @@ sequenceDiagram
|
||||
Alice->Bob: Hello Bob, how are you?`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
const mermaid = mermaidAPI.getConfig();
|
||||
@ -1779,7 +1779,7 @@ wrap
|
||||
Alice->Bob: Hello Bob, how are you?`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const msgs = diagram.db.getMessages();
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
@ -1800,7 +1800,7 @@ Note over Bob,Alice: Looks back
|
||||
`;
|
||||
// mermaidAPI.initialize({logLevel:0})
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
@ -1815,7 +1815,7 @@ Alice->Bob: Hello Bob, how are you?
|
||||
Bob->Alice: Fine!`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
@ -1831,7 +1831,7 @@ Note right of Bob: Bob thinks
|
||||
Bob->Alice: Fine!`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
@ -1850,7 +1850,7 @@ Note left of Alice: Bob thinks
|
||||
Bob->Alice: Fine!`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
|
||||
@ -1867,7 +1867,7 @@ Note left of Alice: Bob thinks
|
||||
Bob->>Alice: Fine!`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
const msgs = diagram.db.getMessages();
|
||||
@ -1888,7 +1888,7 @@ Note left of Alice: Bob thinks
|
||||
Bob->>Alice: Fine!`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
const msgs = diagram.db.getMessages();
|
||||
@ -1911,7 +1911,7 @@ Note left of Alice: Bob thinks
|
||||
Bob->>Alice: Fine!`;
|
||||
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
const msgs = diagram.db.getMessages();
|
||||
@ -1933,7 +1933,7 @@ Note left of Alice: Bob thinks
|
||||
Bob->>Alice: Fine!`;
|
||||
// mermaidAPI.initialize({ logLevel: 0 });
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
const msgs = diagram.db.getMessages();
|
||||
@ -1957,7 +1957,7 @@ loop Cheers
|
||||
Bob->Alice: Fine!
|
||||
end`;
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
@ -1975,7 +1975,7 @@ end`;
|
||||
end
|
||||
`;
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
expect(bounds.starty).toBe(0);
|
||||
@ -2022,7 +2022,7 @@ sequenceDiagram
|
||||
participant Alice`;
|
||||
diagram.renderer.bounds.init();
|
||||
await mermaidAPI.parse(str);
|
||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||
|
||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||
expect(bounds.startx).toBe(0);
|
||||
|
@ -1,8 +1,8 @@
|
||||
// @ts-nocheck TODO: fix file
|
||||
import { select } from 'd3';
|
||||
import svgDraw, { ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
|
||||
import svgDraw, { drawKatex, ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
|
||||
import { log } from '../../logger.js';
|
||||
import common from '../common/common.js';
|
||||
import common, { calculateMathMLDimensions, hasKatex } from '../common/common.js';
|
||||
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||
import assignWithDepth from '../../assignWithDepth.js';
|
||||
@ -237,7 +237,7 @@ interface NoteModel {
|
||||
* @param elem - The diagram to draw to.
|
||||
* @param noteModel - Note model options.
|
||||
*/
|
||||
const drawNote = function (elem: any, noteModel: NoteModel) {
|
||||
const drawNote = async function (elem: any, noteModel: NoteModel) {
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
noteModel.height = conf.boxMargin;
|
||||
noteModel.starty = bounds.getVerticalPos();
|
||||
@ -263,7 +263,7 @@ const drawNote = function (elem: any, noteModel: NoteModel) {
|
||||
textObj.textMargin = conf.noteMargin;
|
||||
textObj.valign = 'center';
|
||||
|
||||
const textElem = drawText(g, textObj);
|
||||
const textElem = hasKatex(textObj.text) ? await drawKatex(g, textObj) : drawText(g, textObj);
|
||||
|
||||
const textHeight = Math.round(
|
||||
textElem
|
||||
@ -311,15 +311,20 @@ const actorFont = (cnf) => {
|
||||
* @param msgModel - The model containing fields describing a message
|
||||
* @returns `lineStartY` - The Y coordinate at which the message line starts
|
||||
*/
|
||||
function boundMessage(_diagram, msgModel): number {
|
||||
async function boundMessage(_diagram, msgModel): Promise<number> {
|
||||
bounds.bumpVerticalPos(10);
|
||||
const { startx, stopx, message } = msgModel;
|
||||
const lines = common.splitBreaks(message).length;
|
||||
const textDims = utils.calculateTextDimensions(message, messageFont(conf));
|
||||
const lineHeight = textDims.height / lines;
|
||||
msgModel.height += lineHeight;
|
||||
const isKatexMsg = hasKatex(message);
|
||||
const textDims = isKatexMsg
|
||||
? await calculateMathMLDimensions(message, getConfig())
|
||||
: utils.calculateTextDimensions(message, messageFont(conf));
|
||||
|
||||
bounds.bumpVerticalPos(lineHeight);
|
||||
if (!isKatexMsg) {
|
||||
const lineHeight = textDims.height / lines;
|
||||
msgModel.height += lineHeight;
|
||||
bounds.bumpVerticalPos(lineHeight);
|
||||
}
|
||||
|
||||
let lineStartY;
|
||||
let totalOffset = textDims.height - 10;
|
||||
@ -360,7 +365,7 @@ function boundMessage(_diagram, msgModel): number {
|
||||
* @param lineStartY - The Y coordinate at which the message line starts
|
||||
* @param diagObj - The diagram object.
|
||||
*/
|
||||
const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Diagram) {
|
||||
const drawMessage = async function (diagram, msgModel, lineStartY: number, diagObj: Diagram) {
|
||||
const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel;
|
||||
const textDims = utils.calculateTextDimensions(message, messageFont(conf));
|
||||
const textObj = svgDrawCommon.getTextObj();
|
||||
@ -378,7 +383,9 @@ const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Di
|
||||
textObj.textMargin = conf.wrapPadding;
|
||||
textObj.tspan = false;
|
||||
|
||||
drawText(diagram, textObj);
|
||||
hasKatex(textObj.text)
|
||||
? await drawKatex(diagram, textObj, { startx, stopx, starty: lineStartY })
|
||||
: drawText(diagram, textObj);
|
||||
|
||||
const textWidth = textDims.width;
|
||||
|
||||
@ -478,7 +485,7 @@ const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Di
|
||||
}
|
||||
};
|
||||
|
||||
const addActorRenderingData = function (
|
||||
const addActorRenderingData = async function (
|
||||
diagram,
|
||||
actors,
|
||||
createdActors,
|
||||
@ -548,12 +555,12 @@ const addActorRenderingData = function (
|
||||
bounds.bumpVerticalPos(maxHeight);
|
||||
};
|
||||
|
||||
export const drawActors = function (diagram, actors, actorKeys, isFooter) {
|
||||
export const drawActors = async function (diagram, actors, actorKeys, isFooter) {
|
||||
if (!isFooter) {
|
||||
for (const actorKey of actorKeys) {
|
||||
const actor = actors[actorKey];
|
||||
// Draw the box with the attached line
|
||||
svgDraw.drawActor(diagram, actor, conf, false);
|
||||
await svgDraw.drawActor(diagram, actor, conf, false);
|
||||
}
|
||||
} else {
|
||||
let maxHeight = 0;
|
||||
@ -563,7 +570,7 @@ export const drawActors = function (diagram, actors, actorKeys, isFooter) {
|
||||
if (!actor.stopy) {
|
||||
actor.stopy = bounds.getVerticalPos();
|
||||
}
|
||||
const height = svgDraw.drawActor(diagram, actor, conf, true);
|
||||
const height = await svgDraw.drawActor(diagram, actor, conf, true);
|
||||
maxHeight = common.getMax(maxHeight, height);
|
||||
}
|
||||
bounds.bumpVerticalPos(maxHeight + conf.boxMargin);
|
||||
@ -746,7 +753,7 @@ function adjustCreatedDestroyedData(
|
||||
* @param _version - Mermaid version from package.json
|
||||
* @param diagObj - A standard diagram containing the db and the text and type etc of the diagram
|
||||
*/
|
||||
export const draw = function (_text: string, id: string, _version: string, diagObj: Diagram) {
|
||||
export const draw = async function (_text: string, id: string, _version: string, diagObj: Diagram) {
|
||||
const { securityLevel, sequence } = getConfig();
|
||||
conf = sequence;
|
||||
// Handle root and Document for when rendering in sandbox mode
|
||||
@ -776,8 +783,8 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
const title = diagObj.db.getDiagramTitle();
|
||||
const hasBoxes = diagObj.db.hasAtLeastOneBox();
|
||||
const hasBoxTitles = diagObj.db.hasAtLeastOneBoxWithTitle();
|
||||
const maxMessageWidthPerActor = getMaxMessageWidthPerActor(actors, messages, diagObj);
|
||||
conf.height = calculateActorMargins(actors, maxMessageWidthPerActor, boxes);
|
||||
const maxMessageWidthPerActor = await getMaxMessageWidthPerActor(actors, messages, diagObj);
|
||||
conf.height = await calculateActorMargins(actors, maxMessageWidthPerActor, boxes);
|
||||
|
||||
svgDraw.insertComputerIcon(diagram);
|
||||
svgDraw.insertDatabaseIcon(diagram);
|
||||
@ -799,8 +806,8 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey));
|
||||
}
|
||||
|
||||
addActorRenderingData(diagram, actors, createdActors, actorKeys, 0, messages, false);
|
||||
const loopWidths = calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj);
|
||||
await addActorRenderingData(diagram, actors, createdActors, actorKeys, 0, messages, false);
|
||||
const loopWidths = await calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj);
|
||||
|
||||
// The arrow head definition is attached to the svg once
|
||||
svgDraw.insertArrowHead(diagram);
|
||||
@ -834,14 +841,15 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
let sequenceIndexStep = 1;
|
||||
const messagesToDraw = [];
|
||||
const backgrounds = [];
|
||||
messages.forEach(function (msg, index) {
|
||||
let index = 0;
|
||||
for (const msg of messages) {
|
||||
let loopModel, noteModel, msgModel;
|
||||
|
||||
switch (msg.type) {
|
||||
case diagObj.db.LINETYPE.NOTE:
|
||||
bounds.resetVerticalPos();
|
||||
noteModel = msg.noteModel;
|
||||
drawNote(diagram, noteModel);
|
||||
await drawNote(diagram, noteModel);
|
||||
break;
|
||||
case diagObj.db.LINETYPE.ACTIVE_START:
|
||||
bounds.newActivation(msg, diagram, actors);
|
||||
@ -860,7 +868,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
break;
|
||||
case diagObj.db.LINETYPE.LOOP_END:
|
||||
loopModel = bounds.endLoop();
|
||||
svgDraw.drawLoop(diagram, loopModel, 'loop', conf);
|
||||
await svgDraw.drawLoop(diagram, loopModel, 'loop', conf);
|
||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||
bounds.models.addLoop(loopModel);
|
||||
break;
|
||||
@ -886,7 +894,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
break;
|
||||
case diagObj.db.LINETYPE.OPT_END:
|
||||
loopModel = bounds.endLoop();
|
||||
svgDraw.drawLoop(diagram, loopModel, 'opt', conf);
|
||||
await svgDraw.drawLoop(diagram, loopModel, 'opt', conf);
|
||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||
bounds.models.addLoop(loopModel);
|
||||
break;
|
||||
@ -910,7 +918,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
break;
|
||||
case diagObj.db.LINETYPE.ALT_END:
|
||||
loopModel = bounds.endLoop();
|
||||
svgDraw.drawLoop(diagram, loopModel, 'alt', conf);
|
||||
await svgDraw.drawLoop(diagram, loopModel, 'alt', conf);
|
||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||
bounds.models.addLoop(loopModel);
|
||||
break;
|
||||
@ -936,7 +944,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
break;
|
||||
case diagObj.db.LINETYPE.PAR_END:
|
||||
loopModel = bounds.endLoop();
|
||||
svgDraw.drawLoop(diagram, loopModel, 'par', conf);
|
||||
await svgDraw.drawLoop(diagram, loopModel, 'par', conf);
|
||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||
bounds.models.addLoop(loopModel);
|
||||
break;
|
||||
@ -969,7 +977,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
break;
|
||||
case diagObj.db.LINETYPE.CRITICAL_END:
|
||||
loopModel = bounds.endLoop();
|
||||
svgDraw.drawLoop(diagram, loopModel, 'critical', conf);
|
||||
await svgDraw.drawLoop(diagram, loopModel, 'critical', conf);
|
||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||
bounds.models.addLoop(loopModel);
|
||||
break;
|
||||
@ -984,7 +992,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
break;
|
||||
case diagObj.db.LINETYPE.BREAK_END:
|
||||
loopModel = bounds.endLoop();
|
||||
svgDraw.drawLoop(diagram, loopModel, 'break', conf);
|
||||
await svgDraw.drawLoop(diagram, loopModel, 'break', conf);
|
||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||
bounds.models.addLoop(loopModel);
|
||||
break;
|
||||
@ -994,7 +1002,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
msgModel.starty = bounds.getVerticalPos();
|
||||
msgModel.sequenceIndex = sequenceIndex;
|
||||
msgModel.sequenceVisible = diagObj.db.showSequenceNumbers();
|
||||
const lineStartY = boundMessage(diagram, msgModel);
|
||||
const lineStartY = await boundMessage(diagram, msgModel);
|
||||
adjustCreatedDestroyedData(
|
||||
msg,
|
||||
msgModel,
|
||||
@ -1026,20 +1034,23 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
) {
|
||||
sequenceIndex = sequenceIndex + sequenceIndexStep;
|
||||
}
|
||||
});
|
||||
index++;
|
||||
}
|
||||
|
||||
log.debug('createdActors', createdActors);
|
||||
log.debug('destroyedActors', destroyedActors);
|
||||
await drawActors(diagram, actors, actorKeys, false);
|
||||
|
||||
drawActors(diagram, actors, actorKeys, false);
|
||||
messagesToDraw.forEach((e) => drawMessage(diagram, e.messageModel, e.lineStartY, diagObj));
|
||||
for (const e of messagesToDraw) {
|
||||
await drawMessage(diagram, e.messageModel, e.lineStartY, diagObj);
|
||||
}
|
||||
if (conf.mirrorActors) {
|
||||
drawActors(diagram, actors, actorKeys, true);
|
||||
await drawActors(diagram, actors, actorKeys, true);
|
||||
}
|
||||
backgrounds.forEach((e) => svgDraw.drawBackgroundRect(diagram, e));
|
||||
fixLifeLineHeights(diagram, actors, actorKeys, conf);
|
||||
|
||||
bounds.models.boxes.forEach(function (box) {
|
||||
for (const box of bounds.models.boxes) {
|
||||
box.height = bounds.getVerticalPos() - box.y;
|
||||
bounds.insert(box.x, box.y, box.x + box.width, box.height);
|
||||
box.startx = box.x;
|
||||
@ -1047,8 +1058,8 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
box.stopx = box.startx + box.width;
|
||||
box.stopy = box.starty + box.height;
|
||||
box.stroke = 'rgb(0,0,0, 0.5)';
|
||||
svgDraw.drawBox(diagram, box, conf);
|
||||
});
|
||||
await svgDraw.drawBox(diagram, box, conf);
|
||||
}
|
||||
|
||||
if (hasBoxes) {
|
||||
bounds.bumpVerticalPos(conf.boxMargin);
|
||||
@ -1114,25 +1125,25 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
||||
* @param diagObj - The diagram object.
|
||||
* @returns The max message width of each actor.
|
||||
*/
|
||||
function getMaxMessageWidthPerActor(
|
||||
async function getMaxMessageWidthPerActor(
|
||||
actors: { [id: string]: any },
|
||||
messages: any[],
|
||||
diagObj: Diagram
|
||||
): { [id: string]: number } {
|
||||
): Promise<{ [id: string]: number }> {
|
||||
const maxMessageWidthPerActor = {};
|
||||
|
||||
messages.forEach(function (msg) {
|
||||
for (const msg of messages) {
|
||||
if (actors[msg.to] && actors[msg.from]) {
|
||||
const actor = actors[msg.to];
|
||||
|
||||
// If this is the first actor, and the message is left of it, no need to calculate the margin
|
||||
if (msg.placement === diagObj.db.PLACEMENT.LEFTOF && !actor.prevActor) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is the last actor, and the message is right of it, no need to calculate the margin
|
||||
if (msg.placement === diagObj.db.PLACEMENT.RIGHTOF && !actor.nextActor) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
const isNote = msg.placement !== undefined;
|
||||
@ -1142,7 +1153,9 @@ function getMaxMessageWidthPerActor(
|
||||
const wrappedMessage = msg.wrap
|
||||
? utils.wrapLabel(msg.message, conf.width - 2 * conf.wrapPadding, textFont)
|
||||
: msg.message;
|
||||
const messageDimensions = utils.calculateTextDimensions(wrappedMessage, textFont);
|
||||
const messageDimensions = hasKatex(wrappedMessage)
|
||||
? await calculateMathMLDimensions(msg.message, getConfig())
|
||||
: utils.calculateTextDimensions(wrappedMessage, textFont);
|
||||
const messageWidth = messageDimensions.width + 2 * conf.wrapPadding;
|
||||
|
||||
/*
|
||||
@ -1207,7 +1220,7 @@ function getMaxMessageWidthPerActor(
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
log.debug('maxMessageWidthPerActor:', maxMessageWidthPerActor);
|
||||
return maxMessageWidthPerActor;
|
||||
@ -1238,13 +1251,13 @@ const getRequiredPopupWidth = function (actor) {
|
||||
* @param actorToMessageWidth - A map of actor key → max message width it holds
|
||||
* @param boxes - The boxes around the actors if any
|
||||
*/
|
||||
function calculateActorMargins(
|
||||
async function calculateActorMargins(
|
||||
actors: { [id: string]: any },
|
||||
actorToMessageWidth: ReturnType<typeof getMaxMessageWidthPerActor>,
|
||||
actorToMessageWidth: Awaited<ReturnType<typeof getMaxMessageWidthPerActor>>,
|
||||
boxes
|
||||
) {
|
||||
let maxHeight = 0;
|
||||
Object.keys(actors).forEach((prop) => {
|
||||
for (const prop of Object.keys(actors)) {
|
||||
const actor = actors[prop];
|
||||
if (actor.wrap) {
|
||||
actor.description = utils.wrapLabel(
|
||||
@ -1253,14 +1266,17 @@ function calculateActorMargins(
|
||||
actorFont(conf)
|
||||
);
|
||||
}
|
||||
const actDims = utils.calculateTextDimensions(actor.description, actorFont(conf));
|
||||
const actDims = hasKatex(actor.description)
|
||||
? await calculateMathMLDimensions(actor.description, getConfig())
|
||||
: utils.calculateTextDimensions(actor.description, actorFont(conf));
|
||||
|
||||
actor.width = actor.wrap
|
||||
? conf.width
|
||||
: common.getMax(conf.width, actDims.width + 2 * conf.wrapPadding);
|
||||
|
||||
actor.height = actor.wrap ? common.getMax(actDims.height, conf.height) : conf.height;
|
||||
maxHeight = common.getMax(maxHeight, actor.height);
|
||||
});
|
||||
}
|
||||
|
||||
for (const actorKey in actorToMessageWidth) {
|
||||
const actor = actors[actorKey];
|
||||
@ -1311,15 +1327,17 @@ function calculateActorMargins(
|
||||
return common.getMax(maxHeight, conf.height);
|
||||
}
|
||||
|
||||
const buildNoteModel = function (msg, actors, diagObj) {
|
||||
const buildNoteModel = async function (msg, actors, diagObj) {
|
||||
const startx = actors[msg.from].x;
|
||||
const stopx = actors[msg.to].x;
|
||||
const shouldWrap = msg.wrap && msg.message;
|
||||
|
||||
let textDimensions = utils.calculateTextDimensions(
|
||||
shouldWrap ? utils.wrapLabel(msg.message, conf.width, noteFont(conf)) : msg.message,
|
||||
noteFont(conf)
|
||||
);
|
||||
let textDimensions: { width: number; height: number; lineHeight?: number } = hasKatex(msg.message)
|
||||
? await calculateMathMLDimensions(msg.message, getConfig())
|
||||
: utils.calculateTextDimensions(
|
||||
shouldWrap ? utils.wrapLabel(msg.message, conf.width, noteFont(conf)) : msg.message,
|
||||
noteFont(conf)
|
||||
);
|
||||
const noteModel = {
|
||||
width: shouldWrap
|
||||
? conf.width
|
||||
@ -1477,12 +1495,12 @@ const buildMessageModel = function (msg, actors, diagObj) {
|
||||
};
|
||||
};
|
||||
|
||||
const calculateLoopBounds = function (messages, actors, _maxWidthPerActor, diagObj) {
|
||||
const calculateLoopBounds = async function (messages, actors, _maxWidthPerActor, diagObj) {
|
||||
const loops = {};
|
||||
const stack = [];
|
||||
let current, noteModel, msgModel;
|
||||
|
||||
messages.forEach(function (msg) {
|
||||
for (const msg of messages) {
|
||||
msg.id = utils.random({ length: 10 });
|
||||
switch (msg.type) {
|
||||
case diagObj.db.LINETYPE.LOOP_START:
|
||||
@ -1545,7 +1563,7 @@ const calculateLoopBounds = function (messages, actors, _maxWidthPerActor, diagO
|
||||
}
|
||||
const isNote = msg.placement !== undefined;
|
||||
if (isNote) {
|
||||
noteModel = buildNoteModel(msg, actors, diagObj);
|
||||
noteModel = await buildNoteModel(msg, actors, diagObj);
|
||||
msg.noteModel = noteModel;
|
||||
stack.forEach((stk) => {
|
||||
current = stk;
|
||||
@ -1584,7 +1602,7 @@ const calculateLoopBounds = function (messages, actors, _maxWidthPerActor, diagO
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
bounds.activations = [];
|
||||
log.debug('Loop type widths:', loops);
|
||||
return loops;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import common from '../common/common.js';
|
||||
import common, { calculateMathMLDimensions, hasKatex, renderKatex } from '../common/common.js';
|
||||
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||
import { addFunction } from '../../interactionDb.js';
|
||||
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||
import * as configApi from '../../config.js';
|
||||
|
||||
export const ACTOR_TYPE_WIDTH = 18 * 2;
|
||||
const TOP_ACTOR_CLASS = 'actor-top';
|
||||
@ -83,6 +84,47 @@ const popupMenuToggle = function (popid) {
|
||||
);
|
||||
};
|
||||
|
||||
export const drawKatex = async function (elem, textData, msgModel = null) {
|
||||
let textElem = elem.append('foreignObject');
|
||||
const lines = await renderKatex(textData.text, configApi.getConfig());
|
||||
|
||||
const divElem = textElem
|
||||
.append('xhtml:div')
|
||||
.attr('style', 'width: fit-content;')
|
||||
.attr('xmlns', 'http://www.w3.org/1999/xhtml')
|
||||
.html(lines);
|
||||
const dim = divElem.node().getBoundingClientRect();
|
||||
|
||||
textElem.attr('height', Math.round(dim.height)).attr('width', Math.round(dim.width));
|
||||
|
||||
if (textData.class === 'noteText') {
|
||||
const rectElem = elem.node().firstChild;
|
||||
|
||||
rectElem.setAttribute('height', dim.height + 2 * textData.textMargin);
|
||||
const rectDim = rectElem.getBBox();
|
||||
|
||||
textElem
|
||||
.attr('x', Math.round(rectDim.x + rectDim.width / 2 - dim.width / 2))
|
||||
.attr('y', Math.round(rectDim.y + rectDim.height / 2 - dim.height / 2));
|
||||
} else if (msgModel) {
|
||||
let { startx, stopx, starty } = msgModel;
|
||||
if (startx > stopx) {
|
||||
const temp = startx;
|
||||
startx = stopx;
|
||||
stopx = temp;
|
||||
}
|
||||
|
||||
textElem.attr('x', Math.round(startx + Math.abs(startx - stopx) / 2 - dim.width / 2));
|
||||
if (textData.class === 'loopText') {
|
||||
textElem.attr('y', Math.round(starty));
|
||||
} else {
|
||||
textElem.attr('y', Math.round(starty - dim.height));
|
||||
}
|
||||
}
|
||||
|
||||
return [textElem];
|
||||
};
|
||||
|
||||
export const drawText = function (elem, textData) {
|
||||
let prevTextHeight = 0;
|
||||
let textHeight = 0;
|
||||
@ -282,7 +324,7 @@ export const fixLifeLineHeights = (diagram, actors, actorKeys, conf) => {
|
||||
* @param {any} conf - DrawText implementation discriminator object
|
||||
* @param {boolean} isFooter - If the actor is the footer one
|
||||
*/
|
||||
const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
||||
const drawActorTypeParticipant = async function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + 5;
|
||||
@ -345,7 +387,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
||||
}
|
||||
}
|
||||
|
||||
_drawTextCandidateFunc(conf)(
|
||||
await _drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
g,
|
||||
rect.x,
|
||||
@ -366,7 +408,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
||||
return height;
|
||||
};
|
||||
|
||||
const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
||||
const drawActorTypeActor = async function (elem, actor, conf, isFooter) {
|
||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||
const center = actor.x + actor.width / 2;
|
||||
const centerY = actorY + 80;
|
||||
@ -446,7 +488,7 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
||||
const bounds = actElem.node().getBBox();
|
||||
actor.height = bounds.height;
|
||||
|
||||
_drawTextCandidateFunc(conf)(
|
||||
await _drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||
actor.description,
|
||||
actElem,
|
||||
rect.x,
|
||||
@ -460,21 +502,21 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
||||
return actor.height;
|
||||
};
|
||||
|
||||
export const drawActor = function (elem, actor, conf, isFooter) {
|
||||
export const drawActor = async function (elem, actor, conf, isFooter) {
|
||||
switch (actor.type) {
|
||||
case 'actor':
|
||||
return drawActorTypeActor(elem, actor, conf, isFooter);
|
||||
return await drawActorTypeActor(elem, actor, conf, isFooter);
|
||||
case 'participant':
|
||||
return drawActorTypeParticipant(elem, actor, conf, isFooter);
|
||||
return await drawActorTypeParticipant(elem, actor, conf, isFooter);
|
||||
}
|
||||
};
|
||||
|
||||
export const drawBox = function (elem, box, conf) {
|
||||
export const drawBox = async function (elem, box, conf) {
|
||||
const boxplustextGroup = elem.append('g');
|
||||
const g = boxplustextGroup;
|
||||
drawBackgroundRect(g, box);
|
||||
if (box.name) {
|
||||
_drawTextCandidateFunc(conf)(
|
||||
await _drawTextCandidateFunc(conf)(
|
||||
box.name,
|
||||
g,
|
||||
box.x,
|
||||
@ -521,7 +563,7 @@ export const drawActivation = function (elem, bounds, verticalPos, conf, actorAc
|
||||
* @param {any} conf - Diagram configuration
|
||||
* @returns {any}
|
||||
*/
|
||||
export const drawLoop = function (elem, loopModel, labelText, conf) {
|
||||
export const drawLoop = async function (elem, loopModel, labelText, conf) {
|
||||
const {
|
||||
boxMargin,
|
||||
boxTextMargin,
|
||||
@ -583,10 +625,10 @@ export const drawLoop = function (elem, loopModel, labelText, conf) {
|
||||
txt.fontWeight = fontWeight;
|
||||
txt.wrap = true;
|
||||
|
||||
let textElem = drawText(g, txt);
|
||||
let textElem = hasKatex(txt.text) ? await drawKatex(g, txt, loopModel) : drawText(g, txt);
|
||||
|
||||
if (loopModel.sectionTitles !== undefined) {
|
||||
loopModel.sectionTitles.forEach(function (item, idx) {
|
||||
for (const [idx, item] of Object.entries(loopModel.sectionTitles)) {
|
||||
if (item.message) {
|
||||
txt.text = item.message;
|
||||
txt.x = loopModel.startx + (loopModel.stopx - loopModel.startx) / 2;
|
||||
@ -599,7 +641,13 @@ export const drawLoop = function (elem, loopModel, labelText, conf) {
|
||||
txt.fontSize = fontSize;
|
||||
txt.fontWeight = fontWeight;
|
||||
txt.wrap = loopModel.wrap;
|
||||
textElem = drawText(g, txt);
|
||||
|
||||
if (hasKatex(txt.text)) {
|
||||
loopModel.starty = loopModel.sections[idx].y;
|
||||
await drawKatex(g, txt, loopModel);
|
||||
} else {
|
||||
drawText(g, txt);
|
||||
}
|
||||
let sectionHeight = Math.round(
|
||||
textElem
|
||||
.map((te) => (te._groups || te)[0][0].getBBox().height)
|
||||
@ -607,7 +655,7 @@ export const drawLoop = function (elem, loopModel, labelText, conf) {
|
||||
);
|
||||
loopModel.sections[idx].height += sectionHeight - (boxMargin + boxTextMargin);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loopModel.height = Math.round(loopModel.stopy - loopModel.starty);
|
||||
@ -884,6 +932,41 @@ const _drawTextCandidateFunc = (function () {
|
||||
_setTextAttrs(text, textAttrs);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param content
|
||||
* @param g
|
||||
* @param x
|
||||
* @param y
|
||||
* @param width
|
||||
* @param height
|
||||
* @param textAttrs
|
||||
* @param conf
|
||||
*/
|
||||
async function byKatex(content, g, x, y, width, height, textAttrs, conf) {
|
||||
// TODO duplicate render calls, optimize
|
||||
|
||||
const dim = await calculateMathMLDimensions(content, configApi.getConfig());
|
||||
const s = g.append('switch');
|
||||
const f = s
|
||||
.append('foreignObject')
|
||||
.attr('x', x + width / 2 - dim.width / 2)
|
||||
.attr('y', y + height / 2 - dim.height / 2)
|
||||
.attr('width', dim.width)
|
||||
.attr('height', dim.height);
|
||||
|
||||
const text = f.append('xhtml:div').style('height', '100%').style('width', '100%');
|
||||
|
||||
text
|
||||
.append('div')
|
||||
.style('text-align', 'center')
|
||||
.style('vertical-align', 'middle')
|
||||
.html(await renderKatex(content, configApi.getConfig()));
|
||||
|
||||
byTspan(content, s, x, y, width, height, textAttrs, conf);
|
||||
_setTextAttrs(text, textAttrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} toText
|
||||
* @param {any} fromTextAttrsDict
|
||||
@ -896,7 +979,10 @@ const _drawTextCandidateFunc = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
return function (conf) {
|
||||
return function (conf, hasKatex = false) {
|
||||
if (hasKatex) {
|
||||
return byKatex;
|
||||
}
|
||||
return conf.textPlacement === 'fo' ? byFo : conf.textPlacement === 'old' ? byText : byTspan;
|
||||
};
|
||||
})();
|
||||
|
@ -172,6 +172,7 @@ function sidebarConfig() {
|
||||
{ text: 'Mermaid Configuration Options', link: '/config/schema-docs/config' },
|
||||
{ text: 'Directives', link: '/config/directives' },
|
||||
{ text: 'Theming', link: '/config/theming' },
|
||||
{ text: 'Math', link: '/config/math' },
|
||||
{ text: 'Accessibility', link: '/config/accessibility' },
|
||||
{ text: 'Mermaid CLI', link: '/config/mermaidCLI' },
|
||||
{ text: 'FAQ', link: '/config/faq' },
|
||||
|
62
packages/mermaid/src/docs/config/math.md
Normal file
62
packages/mermaid/src/docs/config/math.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Math Configuration (v<MERMAID_RELEASE_VERSION>+)
|
||||
|
||||
Mermaid supports rendering mathematical expressions through the [KaTeX](https://katex.org/) typesetter.
|
||||
|
||||
## Usage
|
||||
|
||||
To render math within a diagram, surround the mathematical expression with the `$$` delimiter.
|
||||
|
||||
Note that at the moment, the only supported diagrams are below:
|
||||
|
||||
### Flowcharts
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A["$$x^2$$"] -->|"$$\sqrt{x+3}$$"| B("$$\frac{1}{2}$$")
|
||||
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\pi r^2$$")
|
||||
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||
```
|
||||
|
||||
### Sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant 1 as $$\alpha$$
|
||||
participant 2 as $$\beta$$
|
||||
1->>2: Solve: $$\sqrt{2+2}$$
|
||||
2-->>1: Answer: $$2$$
|
||||
Note right of 2: $$\sqrt{2+2}=\sqrt{4}=2$$
|
||||
```
|
||||
|
||||
## Legacy Support
|
||||
|
||||
By default, MathML is used for rendering mathematical expressions. If you have users on [unsupported browsers](https://caniuse.com/?search=mathml), `legacyMathML` can be set in the config to fall back to CSS rendering. Note that **you must provide KaTeX's stylesheets on your own** as they do not come bundled with Mermaid.
|
||||
|
||||
Example with legacy mode enabled (the latest version of KaTeX's stylesheet can be found on their [docs](https://katex.org/docs/browser.html)):
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Please ensure the stylesheet's version matches with the KaTeX version in your package-lock -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/katex@{version_number}/dist/katex.min.css"
|
||||
integrity="sha384-{hash}"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.initialize({
|
||||
legacyMathML: true,
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
@ -168,6 +168,14 @@ properties:
|
||||
items:
|
||||
type: string
|
||||
uniqueItems: false # Should be enabled, but it may be a breaking change from the old config
|
||||
legacyMathML:
|
||||
description: |
|
||||
This option specifies if Mermaid can expect the dependent to include KaTeX stylesheets for browsers
|
||||
without their own MathML implementation. If this option is disabled and MathML is not supported, the math
|
||||
equations are replaced with a warning. If this option is enabled and MathML is not supported, Mermaid will
|
||||
fall back to legacy rendering for KaTeX.
|
||||
type: boolean
|
||||
default: false
|
||||
deterministicIds:
|
||||
description: |
|
||||
This option controls if the generated ids of nodes in the SVG are
|
||||
|
28
pnpm-lock.yaml
generated
28
pnpm-lock.yaml
generated
@ -229,6 +229,9 @@ importers:
|
||||
elkjs:
|
||||
specifier: ^0.9.0
|
||||
version: 0.9.1
|
||||
katex:
|
||||
specifier: ^0.16.9
|
||||
version: 0.16.9
|
||||
khroma:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
@ -252,7 +255,7 @@ importers:
|
||||
version: 9.0.0
|
||||
web-worker:
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0
|
||||
version: 1.3.0
|
||||
devDependencies:
|
||||
'@adobe/jsonschema2md':
|
||||
specifier: ^7.1.4
|
||||
@ -278,6 +281,9 @@ importers:
|
||||
'@types/jsdom':
|
||||
specifier: ^21.1.1
|
||||
version: 21.1.1
|
||||
'@types/katex':
|
||||
specifier: ^0.16.7
|
||||
version: 0.16.7
|
||||
'@types/lodash-es':
|
||||
specifier: ^4.17.7
|
||||
version: 4.17.7
|
||||
@ -4897,6 +4903,10 @@ packages:
|
||||
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
||||
dev: true
|
||||
|
||||
/@types/katex@0.16.7:
|
||||
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
|
||||
dev: true
|
||||
|
||||
/@types/keyv@3.1.4:
|
||||
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
|
||||
dependencies:
|
||||
@ -7436,6 +7446,11 @@ packages:
|
||||
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
|
||||
engines: {node: '>= 10'}
|
||||
|
||||
/commander@8.3.0:
|
||||
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
||||
engines: {node: '>= 12'}
|
||||
dev: false
|
||||
|
||||
/comment-json@4.2.3:
|
||||
resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==}
|
||||
engines: {node: '>= 6'}
|
||||
@ -11843,6 +11858,13 @@ packages:
|
||||
engines: {node: '>=12.20'}
|
||||
dev: true
|
||||
|
||||
/katex@0.16.9:
|
||||
resolution: {integrity: sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
commander: 8.3.0
|
||||
dev: false
|
||||
|
||||
/keyv@4.5.3:
|
||||
resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==}
|
||||
dependencies:
|
||||
@ -16522,8 +16544,8 @@ packages:
|
||||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/web-worker@1.2.0:
|
||||
resolution: {integrity: sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==}
|
||||
/web-worker@1.3.0:
|
||||
resolution: {integrity: sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==}
|
||||
dev: false
|
||||
|
||||
/webdriver@7.31.1(typescript@5.1.6):
|
||||
|
Loading…
x
Reference in New Issue
Block a user