Merge remote-tracking branch 'os_repo/5237-unified-layout-common-renderer' into alanaV11

This commit is contained in:
Knut Sveidqvist
2024-07-19 14:15:08 +02:00
67 changed files with 2430 additions and 1195 deletions

View File

@@ -20,6 +20,7 @@ dagre-d3
Deepdwn
Docsify
Docsy
Doctave
DokuWiki
dompurify
elkjs

41
.github/workflows/autofix.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: autofix.ci # needed to securely identify the workflow
on:
pull_request:
permissions:
contents: read
jobs:
autofix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
uses: actions/setup-node@v4
with:
cache: pnpm
node-version-file: '.node-version'
- name: Install Packages
run: |
pnpm install --frozen-lockfile
env:
CYPRESS_CACHE_FOLDER: .cache/Cypress
- name: Fix Linting
shell: bash
run: pnpm -w run lint:fix
- name: Sync `./src/config.type.ts` with `./src/schemas/config.schema.yaml`
shell: bash
run: pnpm run --filter mermaid types:build-config
- name: Build Docs
working-directory: ./packages/mermaid
run: pnpm run docs:build
- uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a

View File

@@ -18,7 +18,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@@ -18,7 +18,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js

View File

@@ -32,7 +32,7 @@ jobs:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js

View File

@@ -24,6 +24,7 @@ env:
) ||
github.event.before
}}
shouldRunParallel: ${{ secrets.CYPRESS_RECORD_KEY != '' && !(github.event_name == 'push' && github.ref == 'refs/heads/develop') }}
jobs:
cache:
runs-on: ubuntu-latest
@@ -32,7 +33,7 @@ jobs:
options: --user 1001
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
@@ -79,7 +80,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
@@ -116,7 +117,7 @@ jobs:
id: cypress
# If CYPRESS_RECORD_KEY is set, run in parallel on all containers
# Otherwise (e.g. if running from fork), we run on a single container only
if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }}
if: ${{ env.shouldRunParallel == 'true' || ( matrix.containers == 1 ) }}
with:
install: false
start: pnpm run dev:coverage
@@ -124,14 +125,14 @@ jobs:
browser: chrome
# Disable recording if we don't have an API key
# e.g. if this action was run from a fork
record: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
parallel: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
record: ${{ env.shouldRunParallel == 'true' }}
parallel: ${{ env.shouldRunParallel == 'true' }}
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
VITEST_COVERAGE: true
CYPRESS_COMMIT: ${{ github.sha }}
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_PARALLEL: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
ARGOS_PARALLEL: ${{ env.shouldRunParallel == 'true' }}
ARGOS_PARALLEL_TOTAL: 4
ARGOS_PARALLEL_INDEX: ${{ matrix.containers }}

View File

@@ -19,7 +19,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js
@@ -82,15 +82,3 @@ jobs:
working-directory: ./packages/mermaid
continue-on-error: ${{ github.event_name == 'push' }}
run: pnpm run docs:verify
- name: Rebuild Docs
if: ${{ steps.verifyDocs.outcome == 'failure' && github.event_name == 'push' }}
working-directory: ./packages/mermaid
run: pnpm run docs:build
- name: Commit changes
uses: EndBug/add-and-commit@v9
if: ${{ steps.verifyDocs.outcome == 'failure' && github.event_name == 'push' }}
with:
message: 'Update docs'
add: 'docs/*'

View File

@@ -25,7 +25,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@@ -13,7 +13,7 @@ jobs:
with:
fetch-depth: 0
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@@ -11,7 +11,7 @@ jobs:
- uses: actions/checkout@v4
- uses: fregante/setup-git-user@v2
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js

View File

@@ -11,7 +11,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
# uses version from "packageManager" field in package.json
- name: Setup Node.js

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- uses: pnpm/action-setup@v4
- run: npx update-browserslist-db@latest
- name: Commit changes
uses: EndBug/add-and-commit@v9

View File

@@ -97,7 +97,7 @@ export const openURLAndVerifyRendering = (
const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
cy.visit(url);
// cy.window().should('have.property', 'rendered', true);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
if (validation) {
@@ -124,7 +124,9 @@ export const verifyScreenshot = (name: string): void => {
cy.log(`Closing eyes ${Cypress.spec.name}`);
cy.eyesClose();
} else if (useArgos) {
cy.argosScreenshot(name);
cy.argosScreenshot(name, {
threshold: 0.01,
});
} else {
cy.matchImageSnapshot(name);
}

View File

@@ -236,7 +236,7 @@ describe('Block diagram', () => {
);
});
it('BL16: width alignment - blocks shold be equal in width', () => {
it('BL17: width alignment - blocks shold be equal in width', () => {
imgSnapshotTest(
`block-beta
A("This is the text")
@@ -247,7 +247,7 @@ describe('Block diagram', () => {
);
});
it('BL17: block types 1 - square, rounded and circle', () => {
it('BL18: block types 1 - square, rounded and circle', () => {
imgSnapshotTest(
`block-beta
A["square"]
@@ -258,7 +258,7 @@ describe('Block diagram', () => {
);
});
it('BL18: block types 2 - odd, diamond and hexagon', () => {
it('BL19: block types 2 - odd, diamond and hexagon', () => {
imgSnapshotTest(
`block-beta
A>"rect_left_inv_arrow"]
@@ -269,7 +269,7 @@ describe('Block diagram', () => {
);
});
it('BL19: block types 3 - stadium', () => {
it('BL20: block types 3 - stadium', () => {
imgSnapshotTest(
`block-beta
A(["stadium"])
@@ -278,7 +278,7 @@ describe('Block diagram', () => {
);
});
it('BL20: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => {
imgSnapshotTest(
`block-beta
A[/"lean right"/]
@@ -290,7 +290,7 @@ describe('Block diagram', () => {
);
});
it('BL21: block types 1 - square, rounded and circle', () => {
it('BL22: block types 1 - square, rounded and circle', () => {
imgSnapshotTest(
`block-beta
A["square"]
@@ -301,7 +301,7 @@ describe('Block diagram', () => {
);
});
it('BL22: sizing - it should be possible to make a block wider', () => {
it('BL23: sizing - it should be possible to make a block wider', () => {
imgSnapshotTest(
`block-beta
A("rounded"):2
@@ -312,7 +312,7 @@ describe('Block diagram', () => {
);
});
it('BL23: sizing - it should be possible to make a composite block wider', () => {
it('BL24: sizing - it should be possible to make a composite block wider', () => {
imgSnapshotTest(
`block-beta
block:2
@@ -324,7 +324,7 @@ describe('Block diagram', () => {
);
});
it('BL24: block in the middle with space on each side', () => {
it('BL25: block in the middle with space on each side', () => {
imgSnapshotTest(
`block-beta
columns 3
@@ -335,7 +335,7 @@ describe('Block diagram', () => {
{}
);
});
it('BL25: space and an edge', () => {
it('BL26: space and an edge', () => {
imgSnapshotTest(
`block-beta
columns 5
@@ -345,7 +345,7 @@ describe('Block diagram', () => {
{}
);
});
it('BL26: block sizes for regular blocks', () => {
it('BL27: block sizes for regular blocks', () => {
imgSnapshotTest(
`block-beta
columns 3
@@ -354,7 +354,7 @@ describe('Block diagram', () => {
{}
);
});
it('BL27: composite block with a set width - f should use the available space', () => {
it('BL28: composite block with a set width - f should use the available space', () => {
imgSnapshotTest(
`block-beta
columns 3
@@ -367,7 +367,8 @@ describe('Block diagram', () => {
{}
);
});
it('BL23: composite block with a set width - f and g should split the available space', () => {
it('BL29: composite block with a set width - f and g should split the available space', () => {
imgSnapshotTest(
`block-beta
columns 3

View File

@@ -1,7 +1,7 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
describe('C4 diagram', () => {
it('should render a simple C4Context diagram', () => {
it('C4.1 should render a simple C4Context diagram', () => {
imgSnapshotTest(
`
C4Context
@@ -31,7 +31,7 @@ describe('C4 diagram', () => {
{}
);
});
it('should render a simple C4Container diagram', () => {
it('C4.2 should render a simple C4Container diagram', () => {
imgSnapshotTest(
`
C4Container
@@ -50,7 +50,7 @@ describe('C4 diagram', () => {
{}
);
});
it('should render a simple C4Component diagram', () => {
it('C4.3 should render a simple C4Component diagram', () => {
imgSnapshotTest(
`
C4Component
@@ -68,7 +68,7 @@ describe('C4 diagram', () => {
{}
);
});
it('should render a simple C4Dynamic diagram', () => {
it('C4.4 should render a simple C4Dynamic diagram', () => {
imgSnapshotTest(
`
C4Dynamic
@@ -91,7 +91,7 @@ describe('C4 diagram', () => {
{}
);
});
it('should render a simple C4Deployment diagram', () => {
it('C4.5 should render a simple C4Deployment diagram', () => {
imgSnapshotTest(
`
C4Deployment

View File

@@ -76,7 +76,7 @@ describe('Class diagram V2', () => {
);
});
it('should render a simple class diagram with different visibilities', () => {
it('2.1 should render a simple class diagram with different visibilities', () => {
imgSnapshotTest(
`
classDiagram-v2
@@ -93,7 +93,7 @@ describe('Class diagram V2', () => {
);
});
it('should render multiple class diagrams', () => {
it('3: should render multiple class diagrams', () => {
imgSnapshotTest(
[
`

View File

@@ -3,7 +3,7 @@ import { imgSnapshotTest } from '../../helpers/util';
describe('Error Diagrams', () => {
beforeEach(() => {
cy.on('uncaught:exception', (err) => {
expect(err.message).to.include('Parse error');
expect(err.message).to.include('error');
// return false to prevent the error from
// failing this test
return false;

View File

@@ -855,7 +855,7 @@ describe('Title and arrow styling #4813', () => {
flowchart LR
A-->B
A-->C`,
{ flowchart: { defaultRenderer: 'elk' } }
{ layout: 'elk' }
);
cy.get('svg').should((svg) => {
const title = svg[0].querySelector('text');
@@ -871,15 +871,15 @@ describe('Title and arrow styling #4813', () => {
B-.-oC
C==xD
D ~~~ A`,
{ flowchart: { defaultRenderer: 'elk' } }
{ layout: 'elk' }
);
cy.get('svg').should((svg) => {
const edges = svg[0].querySelectorAll('.edges path');
console.log(edges);
expect(edges[0]).to.have.attr('pattern', 'solid');
expect(edges[1]).to.have.attr('pattern', 'dotted');
expect(edges[2]).to.have.css('stroke-width', '3.5px');
expect(edges[3]).to.have.css('stroke-width', '1.5px');
// console.log(edges);
// expect(edges[0]).to.have.attr('pattern', 'solid');
// expect(edges[1]).to.have.attr('pattern', 'dotted');
// expect(edges[2]).to.have.css('stroke-width', '3.5px');
// expect(edges[3]).to.have.css('stroke-width', '1.5px');
});
});
});

View File

@@ -99,7 +99,7 @@ describe('Flowchart v2', () => {
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.be.within(290 * 0.95 - 1, 290 * 1.05);
expect(maxWidthValue).to.be.within(446 * 0.95 - 1, 446 * 1.05);
});
});
it('8: should render a flowchart when useMaxWidth is false', () => {
@@ -118,7 +118,7 @@ describe('Flowchart v2', () => {
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±5%
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
expect(width).to.be.within(290 * 0.95 - 1, 290 * 1.05);
expect(width).to.be.within(446 * 0.95 - 1, 446 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
@@ -1047,7 +1047,9 @@ end
A --lb3--> TOP --lb4--> B
B1 --lb5--> B2
`,
{ flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } } }
{
flowchart: { subGraphTitleMargin: { top: 10, bottom: 5 } },
}
);
});
});

View File

@@ -733,7 +733,7 @@ describe('Graph', () => {
});
it('38: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`graph TD
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
@@ -751,7 +751,7 @@ describe('Graph', () => {
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.be.within(300 * 0.9, 300 * 1.1);
expect(maxWidthValue).to.be.within(446 * 0.9, 446 * 1.1);
});
});
it('39: should render a flowchart when useMaxWidth is false', () => {
@@ -770,7 +770,7 @@ describe('Graph', () => {
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±10%
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
expect(width).to.be.within(300 * 0.9, 300 * 1.1);
expect(width).to.be.within(446 * 0.9, 446 * 1.1);
expect(svg).to.not.have.attr('style');
});
});
@@ -911,7 +911,10 @@ graph TD
style default stroke:#000,stroke-width:4px
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
{
flowchart: { htmlLabels: true },
securityLevel: 'loose',
}
);
});
});

View File

@@ -1532,5 +1532,41 @@ gitGraph TB:
{}
);
});
it('75: should render a gitGraph with multiple tags on a merge commit on bottom-to-top orientation', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
checkout main
merge develop id:"Release 1.0" type:HIGHLIGHT tag: "SAML v2.0" tag: "OpenID v1.1"
commit id:"TWO"
checkout develop
commit id:"C"`,
{}
);
});
});
it('76: should render a gitGraph with multiple tags on a merge commit on left-to-right orientation', () => {
imgSnapshotTest(
`gitGraph
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
checkout main
merge develop id:"Release 1.0" type:HIGHLIGHT tag: "SAML v2.0" tag: "OpenID v1.1"
commit id:"TWO"
checkout develop
commit id:"C"`,
{}
);
});
});

View File

@@ -10,6 +10,15 @@ describe('packet structure', () => {
);
});
it('should render a simple packet diagram without ranges', () => {
imgSnapshotTest(
`packet-beta
0: "h"
1: "i"
`
);
});
it('should render a complex packet diagram', () => {
imgSnapshotTest(
`packet-beta

View File

@@ -1,5 +1,5 @@
import mermaid from './mermaid.esm.mjs';
// import flowchartELK from './mermaid-flowchart-elk.esm.mjs';
import { layouts } from './mermaid-layout-elk.esm.mjs';
import externalExample from './mermaid-example-diagram.esm.mjs';
import zenUml from './mermaid-zenuml.esm.mjs';
@@ -47,8 +47,9 @@ const contentLoaded = async function () {
document.getElementsByTagName('body')[0].appendChild(div);
}
// await mermaid.registerExternalDiagrams([externalExample, zenUml, flowchartELK]);
await mermaid.registerExternalDiagrams([externalExample, zenUml]);
mermaid.registerLayoutLoaders(layouts);
mermaid.initialize(graphObj.mermaid);
await mermaid.run();
}

View File

@@ -0,0 +1,43 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/setup/interfaces/mermaid.LayoutData.md](../../../../packages/mermaid/src/docs/config/setup/interfaces/mermaid.LayoutData.md).
# Interface: LayoutData
[mermaid](../modules/mermaid.md).LayoutData
## Indexable
▪ \[key: `string`]: `any`
## Properties
### config
**config**: `MermaidConfig`
#### Defined in
[packages/mermaid/src/rendering-util/types.d.ts:118](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.d.ts#L118)
---
### edges
**edges**: `Edge`\[]
#### Defined in
[packages/mermaid/src/rendering-util/types.d.ts:117](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.d.ts#L117)
---
### nodes
**nodes**: `Node`\[]
#### Defined in
[packages/mermaid/src/rendering-util/types.d.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.d.ts#L116)

View File

@@ -28,7 +28,7 @@ page.
#### Defined in
[packages/mermaid/src/mermaid.ts:430](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L430)
[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432)
---
@@ -59,7 +59,7 @@ A graph definition key
#### Defined in
[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432)
[packages/mermaid/src/mermaid.ts:434](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L434)
---
@@ -89,7 +89,7 @@ Use [initialize](mermaid.Mermaid.md#initialize) and [run](mermaid.Mermaid.md#run
#### Defined in
[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425)
[packages/mermaid/src/mermaid.ts:427](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L427)
---
@@ -116,7 +116,7 @@ This function should be called before the run function.
#### Defined in
[packages/mermaid/src/mermaid.ts:429](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L429)
[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431)
---
@@ -159,7 +159,7 @@ Internal helpers for mermaid
#### Defined in
[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437)
[packages/mermaid/src/mermaid.ts:439](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L439)
---
@@ -173,7 +173,7 @@ Use [parse](mermaid.Mermaid.md#parse) and [render](mermaid.Mermaid.md#render) in
#### Defined in
[packages/mermaid/src/mermaid.ts:419](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L419)
[packages/mermaid/src/mermaid.ts:421](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L421)
---
@@ -223,7 +223,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not
#### Defined in
[packages/mermaid/src/mermaid.ts:420](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L420)
[packages/mermaid/src/mermaid.ts:422](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L422)
---
@@ -233,7 +233,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not
#### Defined in
[packages/mermaid/src/mermaid.ts:414](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L414)
[packages/mermaid/src/mermaid.ts:416](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L416)
---
@@ -261,7 +261,7 @@ Used to register external diagram types.
#### Defined in
[packages/mermaid/src/mermaid.ts:428](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L428)
[packages/mermaid/src/mermaid.ts:430](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L430)
---
@@ -285,7 +285,7 @@ Used to register external diagram types.
#### Defined in
[packages/mermaid/src/mermaid.ts:427](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L427)
[packages/mermaid/src/mermaid.ts:429](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L429)
---
@@ -311,7 +311,7 @@ Used to register external diagram types.
#### Defined in
[packages/mermaid/src/mermaid.ts:421](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L421)
[packages/mermaid/src/mermaid.ts:423](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L423)
---
@@ -359,7 +359,7 @@ Renders the mermaid diagrams
#### Defined in
[packages/mermaid/src/mermaid.ts:426](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L426)
[packages/mermaid/src/mermaid.ts:428](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L428)
---
@@ -394,7 +394,7 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me
#### Defined in
[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431)
[packages/mermaid/src/mermaid.ts:433](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L433)
---
@@ -404,4 +404,4 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me
#### Defined in
[packages/mermaid/src/mermaid.ts:413](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L413)
[packages/mermaid/src/mermaid.ts:415](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L415)

View File

@@ -16,7 +16,7 @@
#### Defined in
[packages/mermaid/src/config.type.ts:110](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L110)
[packages/mermaid/src/config.type.ts:112](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L112)
---
@@ -29,7 +29,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:129](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L129)
[packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
---
@@ -39,7 +39,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187)
[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189)
---
@@ -49,7 +49,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186)
---
@@ -59,7 +59,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:175](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L175)
[packages/mermaid/src/config.type.ts:177](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L177)
---
@@ -69,7 +69,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L101)
[packages/mermaid/src/config.type.ts:103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L103)
---
@@ -83,7 +83,7 @@ You can set this attribute to base the seed on a static string.
#### Defined in
[packages/mermaid/src/config.type.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L169)
[packages/mermaid/src/config.type.ts:171](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L171)
---
@@ -101,7 +101,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:162](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L162)
[packages/mermaid/src/config.type.ts:164](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L164)
---
@@ -111,31 +111,24 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188)
[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
---
### elk.mergeEdges
### elk
`Optional` **elk.mergeEdges**: `boolean`
`Optional` **elk**: `Object`
Elk specific option that allows egdes to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram.
#### Type declaration
| Name | Type | Description |
| :----------------------- | :---------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mergeEdges?` | `boolean` | Elk specific option that allows edges to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram. |
| `nodePlacementStrategy?` | `"SIMPLE"` \| `"NETWORK_SIMPLEX"` \| `"LINEAR_SEGMENTS"` \| `"BRANDES_KOEPF"` | Elk specific option affecting how nodes are placed. |
#### Defined in
[packages/mermaid/src/config.type.ts:95](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L95)
---
### elk.nodePlacement.strategy
`Optional` **elk.nodePlacement.strategy**: `"SIMPLE"` | `"NETWORK_SIMPLEX"` | `"LINEAR_SEGMENTS"` | `"BRANDES_KOEPF"`
Elk specific option affedcting how nodes are placed.
#### Defined in
[packages/mermaid/src/config.type.ts:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L100)
[packages/mermaid/src/config.type.ts:91](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L91)
---
@@ -145,7 +138,7 @@ Elk specific option affedcting how nodes are placed.
#### Defined in
[packages/mermaid/src/config.type.ts:177](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L177)
[packages/mermaid/src/config.type.ts:179](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L179)
---
@@ -155,7 +148,7 @@ Elk specific option affedcting how nodes are placed.
#### Defined in
[packages/mermaid/src/config.type.ts:170](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L170)
[packages/mermaid/src/config.type.ts:172](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L172)
---
@@ -169,7 +162,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
#### Defined in
[packages/mermaid/src/config.type.ts:109](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L109)
[packages/mermaid/src/config.type.ts:111](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L111)
---
@@ -179,7 +172,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
#### Defined in
[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
---
@@ -193,7 +186,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:151](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L151)
[packages/mermaid/src/config.type.ts:153](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L153)
---
@@ -203,7 +196,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:172](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L172)
[packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
---
@@ -213,7 +206,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
---
@@ -235,7 +228,7 @@ Defines the seed to be used when using handdrawn look. This is important for the
#### Defined in
[packages/mermaid/src/config.type.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L102)
[packages/mermaid/src/config.type.ts:104](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L104)
---
@@ -245,7 +238,7 @@ Defines the seed to be used when using handdrawn look. This is important for the
#### Defined in
[packages/mermaid/src/config.type.ts:173](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L173)
[packages/mermaid/src/config.type.ts:175](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L175)
---
@@ -272,7 +265,7 @@ fall back to legacy rendering for KaTeX.
#### Defined in
[packages/mermaid/src/config.type.ts:144](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L144)
[packages/mermaid/src/config.type.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L146)
---
@@ -284,7 +277,7 @@ This option decides the amount of logging to be used by mermaid.
#### Defined in
[packages/mermaid/src/config.type.ts:115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L115)
[packages/mermaid/src/config.type.ts:117](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L117)
---
@@ -306,7 +299,7 @@ Defines which main look to use for the diagram.
#### Defined in
[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
---
@@ -340,7 +333,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182)
[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
---
@@ -350,7 +343,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186)
[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188)
---
@@ -360,7 +353,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:178](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L178)
[packages/mermaid/src/config.type.ts:180](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L180)
---
@@ -370,7 +363,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:179](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L179)
[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181)
---
@@ -380,7 +373,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181)
[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
---
@@ -390,7 +383,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187)
---
@@ -404,7 +397,7 @@ This prevents malicious graph directives from overriding a site's default securi
#### Defined in
[packages/mermaid/src/config.type.ts:136](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L136)
[packages/mermaid/src/config.type.ts:138](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L138)
---
@@ -416,7 +409,7 @@ Level of trust for parsed diagram
#### Defined in
[packages/mermaid/src/config.type.ts:119](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L119)
[packages/mermaid/src/config.type.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L121)
---
@@ -426,7 +419,7 @@ Level of trust for parsed diagram
#### Defined in
[packages/mermaid/src/config.type.ts:171](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L171)
[packages/mermaid/src/config.type.ts:173](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L173)
---
@@ -438,7 +431,7 @@ Dictates whether mermaid starts on Page load
#### Defined in
[packages/mermaid/src/config.type.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L123)
[packages/mermaid/src/config.type.ts:125](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L125)
---
@@ -448,7 +441,7 @@ Dictates whether mermaid starts on Page load
#### Defined in
[packages/mermaid/src/config.type.ts:176](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L176)
[packages/mermaid/src/config.type.ts:178](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L178)
---
@@ -461,7 +454,7 @@ This is useful when you want to control how to handle syntax errors in your appl
#### Defined in
[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
---
@@ -504,7 +497,7 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
[packages/mermaid/src/config.type.ts:176](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L176)
---
@@ -514,7 +507,7 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189)
[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
---
@@ -524,4 +517,4 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:180](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L180)
[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182)

View File

@@ -18,7 +18,7 @@ The nodes to render. If this is set, `querySelector` will be ignored.
#### Defined in
[packages/mermaid/src/mermaid.ts:43](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L43)
[packages/mermaid/src/mermaid.ts:45](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L45)
---
@@ -44,7 +44,7 @@ A callback to call after each diagram is rendered.
#### Defined in
[packages/mermaid/src/mermaid.ts:47](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L47)
[packages/mermaid/src/mermaid.ts:49](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L49)
---
@@ -56,7 +56,7 @@ The query selector to use when finding elements to render. Default: `".mermaid"`
#### Defined in
[packages/mermaid/src/mermaid.ts:39](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L39)
[packages/mermaid/src/mermaid.ts:41](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L41)
---
@@ -68,4 +68,4 @@ If `true`, errors will be logged to the console, but not thrown. Default: `false
#### Defined in
[packages/mermaid/src/mermaid.ts:51](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L51)
[packages/mermaid/src/mermaid.ts:53](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L53)

View File

@@ -14,7 +14,7 @@
#### Defined in
[packages/mermaid/src/defaultConfig.ts:275](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L275)
[packages/mermaid/src/defaultConfig.ts:279](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L279)
---

View File

@@ -14,6 +14,7 @@
- [DetailedError](../interfaces/mermaid.DetailedError.md)
- [ExternalDiagramDefinition](../interfaces/mermaid.ExternalDiagramDefinition.md)
- [LayoutData](../interfaces/mermaid.LayoutData.md)
- [LayoutLoaderDefinition](../interfaces/mermaid.LayoutLoaderDefinition.md)
- [Mermaid](../interfaces/mermaid.Mermaid.md)
- [MermaidConfig](../interfaces/mermaid.MermaidConfig.md)
@@ -55,4 +56,4 @@
#### Defined in
[packages/mermaid/src/mermaid.ts:440](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L440)
[packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442)

View File

@@ -42,6 +42,8 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [CloudScript.io Mermaid Addon](https://marketplace.atlassian.com/apps/1219878/cloudscript-io-mermaid-addon?hosting=cloud&tab=overview)
- [Azure Devops](https://learn.microsoft.com/en-us/azure/devops/project/wiki/markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) ✅
- [Deepdwn](https://billiam.itch.io/deepdwn) ✅
- [Doctave](https://www.doctave.com/) ✅
- [Mermaid in Markdown code blocks](https://docs.doctave.com/components/mermaid) ✅
- [GitBook](https://gitbook.com)
- [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid)
- [Mermaid plugin for GitBook](https://github.com/wwformat/gitbook-plugin-mermaid-pdf)
@@ -206,6 +208,7 @@ Communication tools and platforms
- [gatsby-remark-mermaid](https://github.com/remcohaszing/gatsby-remark-mermaid)
- [JSDoc](https://jsdoc.app/)
- [jsdoc-mermaid](https://github.com/Jellyvision/jsdoc-mermaid)
- [Madness](https://madness.dannyb.co/)
- [mdBook](https://rust-lang.github.io/mdBook/index.html)
- [mdbook-mermaid](https://github.com/badboy/mdbook-mermaid)
- [MkDocs](https://www.mkdocs.org)

View File

@@ -136,7 +136,7 @@ Cardinality is a property that describes how many elements of another entity can
| 1+ | 1+ | One or more |
| zero or more | zero or more | Zero or more |
| zero or many | zero or many | Zero or more |
| many(0) | many(1) | Zero or more |
| many(0) | many(0) | Zero or more |
| 0+ | 0+ | Zero or more |
| only one | only one | Exactly one |
| 1 | 1 | Exactly one |

View File

@@ -12,7 +12,7 @@ A packet diagram is a visual representation used to illustrate the structure and
## Usage
This diagram type is particularly useful for network engineers, educators, and students who require a clear and concise way to represent the structure of network packets.
This diagram type is particularly useful for developers, network engineers, educators, and students who require a clear and concise way to represent the structure of network packets.
## Syntax

View File

@@ -483,7 +483,7 @@ a _[valid CSS property name](https://www.w3.org/TR/CSS/#properties)_ followed by
Here is an example of a classDef with just one property-value pair:
```
```txt
classDef movement font-style:italic;
```
@@ -496,7 +496,7 @@ If you want to have more than one _property-value pair_ then you put a comma (`,
Here is an example with three property-value pairs:
```
```txt
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
```

View File

@@ -62,7 +62,7 @@
},
"devDependencies": {
"@applitools/eyes-cypress": "^3.44.4",
"@argos-ci/cypress": "^2.0.5",
"@argos-ci/cypress": "^2.1.0",
"@cspell/eslint-plugin": "^8.8.4",
"@cypress/code-coverage": "^3.12.30",
"@eslint/js": "^9.4.0",
@@ -98,7 +98,7 @@
"eslint-plugin-markdown": "^5.0.0",
"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-tsdoc": "^0.3.0",
"eslint-plugin-unicorn": "^53.0.0",
"eslint-plugin-unicorn": "^54.0.0",
"express": "^4.19.1",
"globals": "^15.4.0",
"globby": "^14.0.1",

View File

@@ -465,8 +465,8 @@ export const render = async (data4Layout: LayoutData, svg, element, algorithm) =
layoutOptions: {
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
'elk.algorithm': algorithm,
'nodePlacement.strategy': data4Layout.config['elk.nodePlacement.strategy'],
'elk.layered.mergeEdges': data4Layout.config['elk.mergeEdges'],
'nodePlacement.strategy': data4Layout.config.elk.nodePlacementStrategy,
'elk.layered.mergeEdges': data4Layout.config.elk.mergeEdges,
'elk.direction': 'DOWN',
'spacing.baseValue': 30,
// 'spacing.nodeNode': 40,
@@ -684,32 +684,32 @@ export const render = async (data4Layout: LayoutData, svg, element, algorithm) =
edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
positionEdgeLabel(edge, paths);
}
const src = edge.sections[0].startPoint;
const dest = edge.sections[0].endPoint;
const segments = edge.sections[0].bendPoints ? edge.sections[0].bendPoints : [];
// const src = edge.sections[0].startPoint;
// const dest = edge.sections[0].endPoint;
// const segments = edge.sections[0].bendPoints ? edge.sections[0].bendPoints : [];
const segPoints = segments.map((segment) => {
return { x: segment.x + offset.x, y: segment.y + offset.y };
});
edge.points = [
{ x: src.x + offset.x, y: src.y + offset.y },
...segPoints,
{ x: dest.x + offset.x, y: dest.y + offset.y },
];
const paths = insertEdge(
edgesEl,
edge,
clusterDb,
data4Layout.type,
startNode,
endNode,
data4Layout.diagramId
);
log.info('APA12 edge points after insert', JSON.stringify(edge.points));
// const segPoints = segments.map((segment) => {
// return { x: segment.x + offset.x, y: segment.y + offset.y };
// });
// edge.points = [
// { x: src.x + offset.x, y: src.y + offset.y },
// ...segPoints,
// { x: dest.x + offset.x, y: dest.y + offset.y },
// ];
// const paths = insertEdge(
// edgesEl,
// edge,
// clusterDb,
// data4Layout.type,
// startNode,
// endNode,
// data4Layout.diagramId
// );
// log.info('APA12 edge points after insert', JSON.stringify(edge.points));
edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2;
edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
positionEdgeLabel(edge, paths);
// edge.x = edge.labels[0].x + offset.x + edge.labels[0].width / 2;
// edge.y = edge.labels[0].y + offset.y + edge.labels[0].height / 2;
// positionEdgeLabel(edge, paths);
});
};
@@ -766,7 +766,7 @@ function intersectLine(p1, p2, q1, q2) {
// The denom/2 is to get rounding instead of truncating. It
// is added or subtracted to the numerator, depending upon the
// sign of the numerator.
num = b1 * c2 - b2 * c1;
let num = b1 * c2 - b2 * c1;
const x = num < 0 ? (num - offset) / denom : (num + offset) / denom;
num = a2 * c1 - a1 * c2;
@@ -925,7 +925,6 @@ export const intersection = (node, outsidePoint, insidePoint) => {
}
};
const outsideNode = (node, point) => {
log.debug('Checking bounds ', node, point);
const x = node.x;
const y = node.y;
const dx = Math.abs(point.x - x);

View File

@@ -34,7 +34,7 @@
],
"license": "MIT",
"dependencies": {
"@zenuml/core": "^3.19.2"
"@zenuml/core": "^3.23.27"
},
"devDependencies": {
"@mermaid-chart/mermaid": "workspace:^"

View File

@@ -80,7 +80,7 @@
"katex": "^0.16.9",
"khroma": "^2.1.0",
"lodash-es": "^4.17.21",
"mdast-util-from-markdown": "^2.0.0",
"marked": "^13.0.2",
"roughjs": "^4.6.6",
"stylis": "^4.3.1",
"ts-dedent": "^2.2.0",

View File

@@ -88,16 +88,18 @@ export interface MermaidConfig {
*
*/
maxEdges?: number;
elk?: {
/**
* Elk specific option that allows egdes to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram.
* Elk specific option that allows edges to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram.
*
*/
'elk.mergeEdges'?: boolean;
mergeEdges?: boolean;
/**
* Elk specific option affedcting how nodes are placed.
* Elk specific option affecting how nodes are placed.
*
*/
'elk.nodePlacement.strategy'?: 'SIMPLE' | 'NETWORK_SIMPLEX' | 'LINEAR_SEGMENTS' | 'BRANDES_KOEPF';
nodePlacementStrategy?: 'SIMPLE' | 'NETWORK_SIMPLEX' | 'LINEAR_SEGMENTS' | 'BRANDES_KOEPF';
};
darkMode?: boolean;
htmlLabels?: boolean;
/**

View File

@@ -20,6 +20,10 @@ const config: RequiredDeep<MermaidConfig> = {
// Set, even though they're `undefined` so that `configKeys` finds these keys
// TODO: Should we replace these with `null` so that they can go in the JSON Schema?
deterministicIDSeed: undefined,
elk: {
mergeEdges: false,
nodePlacementStrategy: 'SIMPLE',
},
themeCSS: undefined,
// add non-JSON default config values

View File

@@ -113,6 +113,7 @@ export const clear = function () {
functions.push(setupToolTips);
namespaces = new Map();
namespaceCounter = 0;
direction = 'TB';
commonClear();
};

View File

@@ -444,6 +444,17 @@ class C13["With Città foreign language"]
]
`);
});
it('should revert direction to default once direction is removed', () => {
parser.parse(`classDiagram
direction RL
class A`);
expect(classDb.getDirection()).toBe('RL');
classDb.clear();
parser.parse(`classDiagram
class B`);
expect(classDb.getDirection()).toBe('TB');
});
});
describe('when parsing class defined in brackets', function () {

View File

@@ -205,6 +205,13 @@ export const updateLink = function (positions: ('default' | number)[], style: st
// style.push('fill:none');
// }
edges[pos].style = style;
// if edges[pos].style does have fill not set, set it to none
if (
(edges[pos]?.style?.length ?? 0) > 0 &&
!edges[pos]?.style?.some((s) => s?.startsWith('fill'))
) {
edges[pos]?.style?.push('fill:none');
}
}
});
};

View File

@@ -51,7 +51,7 @@ export const draw = async function (text: string, id: string, _version: string,
await render(data4Layout, svg, element);
const padding = data4Layout.config.flowchart?.padding ?? 8;
utils.insertTitle(
element,
svg,
'flowchartTitleText',
conf?.titleTopMargin || 0,
diag.db.getDiagramTitle()

View File

@@ -12,8 +12,7 @@ import {
getDiagramTitle,
} from '../common/commonDb.js';
let mainBranchName = getConfig().gitGraph.mainBranchName;
let mainBranchOrder = getConfig().gitGraph.mainBranchOrder;
let { mainBranchName, mainBranchOrder } = getConfig().gitGraph;
let commits = new Map();
let head = null;
let branchesConfig = new Map();
@@ -103,17 +102,18 @@ export const getOptions = function () {
return options;
};
export const commit = function (msg, id, type, tag) {
log.debug('Entering commit:', msg, id, type, tag);
id = common.sanitizeText(id, getConfig());
msg = common.sanitizeText(msg, getConfig());
tag = common.sanitizeText(tag, getConfig());
export const commit = function (msg, id, type, tags) {
log.debug('Entering commit:', msg, id, type, tags);
const config = getConfig();
id = common.sanitizeText(id, config);
msg = common.sanitizeText(msg, config);
tags = tags?.map((tag) => common.sanitizeText(tag, config));
const commit = {
id: id ? id : seq + '-' + getId(),
message: msg,
seq: seq++,
type: type ? type : commitType.NORMAL,
tag: tag ? tag : '',
tags: tags ?? [],
parents: head == null ? [] : [head.id],
branch: curBranch,
};
@@ -147,9 +147,10 @@ export const branch = function (name, order) {
}
};
export const merge = function (otherBranch, custom_id, override_type, custom_tag) {
otherBranch = common.sanitizeText(otherBranch, getConfig());
custom_id = common.sanitizeText(custom_id, getConfig());
export const merge = function (otherBranch, custom_id, override_type, custom_tags) {
const config = getConfig();
otherBranch = common.sanitizeText(otherBranch, config);
custom_id = common.sanitizeText(custom_id, config);
const currentCommit = commits.get(branches.get(curBranch));
const otherCommit = commits.get(branches.get(otherBranch));
@@ -216,12 +217,12 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
' already exists, use different custom Id'
);
error.hash = {
text: 'merge ' + otherBranch + custom_id + override_type + custom_tag,
token: 'merge ' + otherBranch + custom_id + override_type + custom_tag,
text: 'merge ' + otherBranch + custom_id + override_type + custom_tags?.join(','),
token: 'merge ' + otherBranch + custom_id + override_type + custom_tags?.join(','),
line: '1',
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
expected: [
'merge ' + otherBranch + ' ' + custom_id + '_UNIQUE ' + override_type + ' ' + custom_tag,
`merge ${otherBranch} ${custom_id}_UNIQUE ${override_type} ${custom_tags?.join(',')}`,
],
};
@@ -245,7 +246,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
type: commitType.MERGE,
customType: override_type,
customId: custom_id ? true : false,
tag: custom_tag ? custom_tag : '',
tags: custom_tags ? custom_tags : [],
};
head = commit;
commits.set(commit.id, commit);
@@ -255,12 +256,13 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
log.debug('in mergeBranch');
};
export const cherryPick = function (sourceId, targetId, tag, parentCommitId) {
log.debug('Entering cherryPick:', sourceId, targetId, tag);
sourceId = common.sanitizeText(sourceId, getConfig());
targetId = common.sanitizeText(targetId, getConfig());
tag = common.sanitizeText(tag, getConfig());
parentCommitId = common.sanitizeText(parentCommitId, getConfig());
export const cherryPick = function (sourceId, targetId, tags, parentCommitId) {
log.debug('Entering cherryPick:', sourceId, targetId, tags);
const config = getConfig();
sourceId = common.sanitizeText(sourceId, config);
targetId = common.sanitizeText(targetId, config);
tags = tags?.map((tag) => common.sanitizeText(tag, config));
parentCommitId = common.sanitizeText(parentCommitId, config);
if (!sourceId || !commits.has(sourceId)) {
let error = new Error(
@@ -329,11 +331,13 @@ export const cherryPick = function (sourceId, targetId, tag, parentCommitId) {
parents: [head == null ? null : head.id, sourceCommit.id],
branch: curBranch,
type: commitType.CHERRY_PICK,
tag:
tag ??
tags: tags
? tags.filter(Boolean)
: [
`cherry-pick:${sourceCommit.id}${
sourceCommit.type === commitType.MERGE ? `|parent:${parentCommitId}` : ''
}`,
],
};
head = commit;
commits.set(commit.id, commit);
@@ -356,8 +360,6 @@ export const checkout = function (branch) {
expected: ['"branch ' + branch + '"'],
};
throw error;
//branches[branch] = head != null ? head.id : null;
//log.debug('in createBranch');
} else {
curBranch = branch;
const id = branches.get(curBranch);
@@ -444,13 +446,12 @@ export const prettyPrint = function () {
export const clear = function () {
commits = new Map();
head = null;
let mainBranch = getConfig().gitGraph.mainBranchName;
let mainBranchOrder = getConfig().gitGraph.mainBranchOrder;
const { mainBranchName, mainBranchOrder } = getConfig().gitGraph;
branches = new Map();
branches.set(mainBranch, null);
branches.set(mainBranchName, null);
branchesConfig = new Map();
branchesConfig.set(mainBranch, { name: mainBranch, order: mainBranchOrder });
curBranch = mainBranch;
branchesConfig.set(mainBranchName, { name: mainBranchName, order: mainBranchOrder });
curBranch = mainBranchName;
seq = 0;
commonClear();
};

View File

@@ -20,7 +20,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).not.toBeNull();
expect(commits.get(key).tag).toBe('');
expect(commits.get(key).tags).toStrictEqual([]);
expect(commits.get(key).type).toBe(0);
});
@@ -37,7 +37,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).toBe('1111');
expect(commits.get(key).tag).toBe('');
expect(commits.get(key).tags).toStrictEqual([]);
expect(commits.get(key).type).toBe(0);
});
@@ -55,7 +55,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).not.toBeNull();
expect(commits.get(key).tag).toBe('test');
expect(commits.get(key).tags).toStrictEqual(['test']);
expect(commits.get(key).type).toBe(0);
});
@@ -73,7 +73,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).not.toBeNull();
expect(commits.get(key).tag).toBe('');
expect(commits.get(key).tags).toStrictEqual([]);
expect(commits.get(key).type).toBe(2);
});
@@ -91,7 +91,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).not.toBeNull();
expect(commits.get(key).tag).toBe('');
expect(commits.get(key).tags).toStrictEqual([]);
expect(commits.get(key).type).toBe(1);
});
@@ -109,7 +109,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).not.toBeNull();
expect(commits.get(key).tag).toBe('');
expect(commits.get(key).tags).toStrictEqual([]);
expect(commits.get(key).type).toBe(0);
});
@@ -127,7 +127,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('test commit');
expect(commits.get(key).id).not.toBeNull();
expect(commits.get(key).tag).toBe('');
expect(commits.get(key).tags).toStrictEqual([]);
expect(commits.get(key).type).toBe(0);
});
@@ -145,7 +145,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('test commit');
expect(commits.get(key).id).not.toBeNull();
expect(commits.get(key).tag).toBe('');
expect(commits.get(key).tags).toStrictEqual([]);
expect(commits.get(key).type).toBe(0);
});
@@ -163,7 +163,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).toBe('1111');
expect(commits.get(key).tag).toBe('test tag');
expect(commits.get(key).tags).toStrictEqual(['test tag']);
expect(commits.get(key).type).toBe(0);
});
@@ -181,7 +181,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).not.toBeNull();
expect(commits.get(key).tag).toBe('test tag');
expect(commits.get(key).tags).toStrictEqual(['test tag']);
expect(commits.get(key).type).toBe(2);
});
@@ -199,7 +199,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).not.toBeNull();
expect(commits.get(key).tag).toBe('test tag');
expect(commits.get(key).tags).toStrictEqual(['test tag']);
expect(commits.get(key).type).toBe(2);
});
@@ -217,7 +217,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('');
expect(commits.get(key).id).toBe('1111');
expect(commits.get(key).tag).toBe('test tag');
expect(commits.get(key).tags).toStrictEqual(['test tag']);
expect(commits.get(key).type).toBe(1);
});
@@ -235,7 +235,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('test msg');
expect(commits.get(key).id).toBe('1111');
expect(commits.get(key).tag).toBe('test tag');
expect(commits.get(key).tags).toStrictEqual(['test tag']);
expect(commits.get(key).type).toBe(1);
});
@@ -254,7 +254,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('test msg');
expect(commits.get(key).id).toBe('1111');
expect(commits.get(key).tag).toBe('test tag');
expect(commits.get(key).tags).toStrictEqual(['test tag']);
expect(commits.get(key).type).toBe(1);
});
@@ -272,7 +272,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('test msg');
expect(commits.get(key).id).toBe('1111');
expect(commits.get(key).tag).toBe('test tag');
expect(commits.get(key).tags).toStrictEqual(['test tag']);
expect(commits.get(key).type).toBe(1);
});
@@ -290,7 +290,7 @@ describe('when parsing a gitGraph', function () {
const key = commits.keys().next().value;
expect(commits.get(key).message).toBe('test msg');
expect(commits.get(key).id).toBe('1111');
expect(commits.get(key).tag).toBe('test tag');
expect(commits.get(key).tags).toStrictEqual(['test tag']);
expect(commits.get(key).type).toBe(1);
});
@@ -616,7 +616,7 @@ describe('when parsing a gitGraph', function () {
commits.get(commit1).id,
commits.get(commit2).id,
]);
expect(commits.get(commit3).tag).toBe('merge-tag');
expect(commits.get(commit3).tags).toStrictEqual(['merge-tag']);
expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([
{ name: 'main' },
{ name: 'testBranch' },
@@ -671,12 +671,12 @@ describe('when parsing a gitGraph', function () {
expect(testBranchMerge.branch).toBe('main');
expect(testBranchMerge.parents).toStrictEqual([mainCommit.id, testBranchCommit.id]);
expect(testBranchMerge.tag).toBe('merge-tag');
expect(testBranchMerge.tags).toStrictEqual(['merge-tag']);
expect(testBranchMerge.id).toBe('2-222');
expect(testBranch2Merge.branch).toBe('main');
expect(testBranch2Merge.parents).toStrictEqual([testBranchMerge.id, testBranch2Commit.id]);
expect(testBranch2Merge.tag).toBe('merge-tag2');
expect(testBranch2Merge.tags).toStrictEqual(['merge-tag2']);
expect(testBranch2Merge.id).toBe('4-444');
expect(testBranch2Merge.customType).toBe(2);
expect(testBranch2Merge.customId).toBe(true);
@@ -705,7 +705,7 @@ describe('when parsing a gitGraph', function () {
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = [...commits.keys()][2];
expect(commits.get(cherryPickCommitID).tag).toBe('cherry-pick:A');
expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['cherry-pick:A']);
expect(commits.get(cherryPickCommitID).branch).toBe('main');
});
@@ -721,7 +721,7 @@ describe('when parsing a gitGraph', function () {
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = [...commits.keys()][2];
expect(commits.get(cherryPickCommitID).tag).toBe('MyTag');
expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['MyTag']);
expect(commits.get(cherryPickCommitID).branch).toBe('main');
});
@@ -737,7 +737,7 @@ describe('when parsing a gitGraph', function () {
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = [...commits.keys()][2];
expect(commits.get(cherryPickCommitID).tag).toBe('');
expect(commits.get(cherryPickCommitID).tags).toStrictEqual([]);
expect(commits.get(cherryPickCommitID).branch).toBe('main');
});
@@ -758,7 +758,7 @@ describe('when parsing a gitGraph', function () {
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = [...commits.keys()][4];
expect(commits.get(cherryPickCommitID).tag).toBe('cherry-pick:M|parent:B');
expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['cherry-pick:M|parent:B']);
expect(commits.get(cherryPickCommitID).branch).toBe('release');
});
@@ -779,7 +779,7 @@ describe('when parsing a gitGraph', function () {
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = [...commits.keys()][4];
expect(commits.get(cherryPickCommitID).tag).toBe('v1.0');
expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['v1.0']);
expect(commits.get(cherryPickCommitID).branch).toBe('release');
});
@@ -802,7 +802,7 @@ describe('when parsing a gitGraph', function () {
parser.parse(str);
const commits = parser.yy.getCommits();
const cherryPickCommitID = [...commits.keys()][5];
expect(commits.get(cherryPickCommitID).tag).toBe('v2.1:ZERO');
expect(commits.get(cherryPickCommitID).tags).toStrictEqual(['v2.1:ZERO']);
expect(commits.get(cherryPickCommitID).branch).toBe('release');
});
@@ -827,8 +827,8 @@ describe('when parsing a gitGraph', function () {
const commits = parser.yy.getCommits();
const cherryPickCommitID = [...commits.keys()][5];
const cherryPickCommitID2 = [...commits.keys()][7];
expect(commits.get(cherryPickCommitID).tag).toBe('');
expect(commits.get(cherryPickCommitID2).tag).toBe('');
expect(commits.get(cherryPickCommitID).tags).toStrictEqual([]);
expect(commits.get(cherryPickCommitID2).tags).toStrictEqual([]);
expect(commits.get(cherryPickCommitID).branch).toBe('release');
});

View File

@@ -414,62 +414,86 @@ const drawCommits = (svg, commits, modifyGraph) => {
}
}
}
if (commit.tag) {
if (commit.tags.length > 0) {
let yOffset = 0;
let maxTagBboxWidth = 0;
let maxTagBboxHeight = 0;
const tagElements = [];
for (const tagValue of commit.tags.reverse()) {
const rect = gLabels.insert('polygon');
const hole = gLabels.append('circle');
const tag = gLabels
.append('text')
// Note that we are delaying setting the x position until we know the width of the text
.attr('y', y - 16)
.attr('y', y - 16 - yOffset)
.attr('class', 'tag-label')
.text(commit.tag);
.text(tagValue);
let tagBbox = tag.node().getBBox();
maxTagBboxWidth = Math.max(maxTagBboxWidth, tagBbox.width);
maxTagBboxHeight = Math.max(maxTagBboxHeight, tagBbox.height);
// We don't use the max over here to center the text within the tags
tag.attr('x', posWithOffset - tagBbox.width / 2);
const h2 = tagBbox.height / 2;
const ly = y - 19.2;
tagElements.push({
tag,
hole,
rect,
yOffset,
});
yOffset += 20;
}
for (const { tag, hole, rect, yOffset } of tagElements) {
const h2 = maxTagBboxHeight / 2;
const ly = y - 19.2 - yOffset;
rect.attr('class', 'tag-label-bkg').attr(
'points',
`
${pos - tagBbox.width / 2 - px / 2},${ly + py}
${pos - tagBbox.width / 2 - px / 2},${ly - py}
${posWithOffset - tagBbox.width / 2 - px},${ly - h2 - py}
${posWithOffset + tagBbox.width / 2 + px},${ly - h2 - py}
${posWithOffset + tagBbox.width / 2 + px},${ly + h2 + py}
${posWithOffset - tagBbox.width / 2 - px},${ly + h2 + py}`
${pos - maxTagBboxWidth / 2 - px / 2},${ly + py}
${pos - maxTagBboxWidth / 2 - px / 2},${ly - py}
${posWithOffset - maxTagBboxWidth / 2 - px},${ly - h2 - py}
${posWithOffset + maxTagBboxWidth / 2 + px},${ly - h2 - py}
${posWithOffset + maxTagBboxWidth / 2 + px},${ly + h2 + py}
${posWithOffset - maxTagBboxWidth / 2 - px},${ly + h2 + py}`
);
hole
.attr('cx', pos - tagBbox.width / 2 + px / 2)
.attr('cy', ly)
.attr('cx', pos - maxTagBboxWidth / 2 + px / 2)
.attr('r', 1.5)
.attr('class', 'tag-hole');
if (dir === 'TB' || dir === 'BT') {
const yOrigin = pos + yOffset;
rect
.attr('class', 'tag-label-bkg')
.attr(
'points',
`
${x},${pos + py}
${x},${pos - py}
${x + layoutOffset},${pos - h2 - py}
${x + layoutOffset + tagBbox.width + px},${pos - h2 - py}
${x + layoutOffset + tagBbox.width + px},${pos + h2 + py}
${x + layoutOffset},${pos + h2 + py}`
${x},${yOrigin + py}
${x},${yOrigin - py}
${x + layoutOffset},${yOrigin - h2 - py}
${x + layoutOffset + maxTagBboxWidth + px},${yOrigin - h2 - py}
${x + layoutOffset + maxTagBboxWidth + px},${yOrigin + h2 + py}
${x + layoutOffset},${yOrigin + h2 + py}`
)
.attr('transform', 'translate(12,12) rotate(45, ' + x + ',' + pos + ')');
hole
.attr('cx', x + px / 2)
.attr('cy', pos)
.attr('cy', yOrigin)
.attr('transform', 'translate(12,12) rotate(45, ' + x + ',' + pos + ')');
tag
.attr('x', x + 5)
.attr('y', pos + 3)
.attr('y', yOrigin + 3)
.attr('transform', 'translate(14,14) rotate(45, ' + x + ',' + pos + ')');
}
}
}
}
pos = dir === 'BT' && isParallelCommits ? pos + commitStep : pos + commitStep + layoutOffset;
if (pos > maxPos) {
maxPos = pos;

View File

@@ -112,122 +112,105 @@ branchStatement
cherryPickStatement
: CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', undefined)}
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR {yy.cherryPick($3, '', undefined,$5)}
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, '', $5)}
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR COMMIT_TAG STR {yy.cherryPick($3, '', $7,$5)}
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR PARENT_COMMIT STR {yy.cherryPick($3, '', $5,$7)}
| CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($5, '', $3)}
| CHERRY_PICK COMMIT_TAG EMPTYSTR COMMIT_ID STR {yy.cherryPick($5, '', '')}
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG EMPTYSTR {yy.cherryPick($3, '', '')}
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR COMMIT_TAG EMPTYSTR {yy.cherryPick($3, '', '',$5)}
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG EMPTYSTR PARENT_COMMIT STR {yy.cherryPick($3, '', '',$7)}
| CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR PARENT_COMMIT STR {yy.cherryPick($5, '', $3,$7)}
| CHERRY_PICK COMMIT_TAG EMPTYSTR COMMIT_ID STR PARENT_COMMIT STR{yy.cherryPick($5, '', '',$7)}
| CHERRY_PICK COMMIT_ID STR commitTags {yy.cherryPick($3, '', $4)}
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR commitTags {yy.cherryPick($3, '', $6,$5)}
| CHERRY_PICK COMMIT_ID STR commitTags PARENT_COMMIT STR {yy.cherryPick($3, '', $4,$6)}
| CHERRY_PICK commitTags COMMIT_ID STR {yy.cherryPick($4, '', $2)}
| CHERRY_PICK commitTags COMMIT_ID STR PARENT_COMMIT STR {yy.cherryPick($4, '', $2,$6)}
;
mergeStatement
: MERGE ref {yy.merge($2,'','','')}
| MERGE ref COMMIT_ID STR {yy.merge($2, $4,'','')}
| MERGE ref COMMIT_TYPE commitType {yy.merge($2,'', $4,'')}
| MERGE ref COMMIT_TAG STR {yy.merge($2, '','',$4)}
| MERGE ref COMMIT_TAG STR COMMIT_ID STR {yy.merge($2, $6,'', $4)}
| MERGE ref COMMIT_TAG STR COMMIT_TYPE commitType {yy.merge($2, '',$6, $4)}
| MERGE ref COMMIT_TYPE commitType COMMIT_TAG STR {yy.merge($2, '',$4, $6)}
| MERGE ref COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $4, $6, '')}
| MERGE ref COMMIT_ID STR COMMIT_TAG STR {yy.merge($2, $4, '', $6)}
| MERGE ref COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $6,$4, '')}
| MERGE ref COMMIT_ID STR COMMIT_TYPE commitType COMMIT_TAG STR {yy.merge($2, $4, $6, $8)}
| MERGE ref COMMIT_TYPE commitType COMMIT_TAG STR COMMIT_ID STR {yy.merge($2, $8, $4, $6)}
| MERGE ref COMMIT_ID STR COMMIT_TAG STR COMMIT_TYPE commitType {yy.merge($2, $4, $8, $6)}
| MERGE ref COMMIT_TYPE commitType COMMIT_ID STR COMMIT_TAG STR {yy.merge($2, $6, $4, $8)}
| MERGE ref COMMIT_TAG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $8, $6, $4)}
| MERGE ref COMMIT_TAG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $6, $8, $4)}
: MERGE ref {yy.merge($2,'','', undefined)}
| MERGE ref COMMIT_ID STR {yy.merge($2, $4,'', undefined)}
| MERGE ref COMMIT_TYPE commitType {yy.merge($2,'', $4, undefined)}
| MERGE ref commitTags {yy.merge($2, '','',$3)}
| MERGE ref commitTags COMMIT_ID STR {yy.merge($2, $5,'', $3)}
| MERGE ref commitTags COMMIT_TYPE commitType {yy.merge($2, '',$5, $3)}
| MERGE ref COMMIT_TYPE commitType commitTags {yy.merge($2, '',$4, $5)}
| MERGE ref COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $4, $6, undefined)}
| MERGE ref COMMIT_ID STR commitTags {yy.merge($2, $4, '', $5)}
| MERGE ref COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $6,$4, undefined)}
| MERGE ref COMMIT_ID STR COMMIT_TYPE commitType commitTags {yy.merge($2, $4, $6, $7)}
| MERGE ref COMMIT_TYPE commitType commitTags COMMIT_ID STR {yy.merge($2, $7, $4, $5)}
| MERGE ref COMMIT_ID STR commitTags COMMIT_TYPE commitType {yy.merge($2, $4, $7, $5)}
| MERGE ref COMMIT_TYPE commitType COMMIT_ID STR commitTags {yy.merge($2, $6, $4, $7)}
| MERGE ref commitTags COMMIT_TYPE commitType COMMIT_ID STR {yy.merge($2, $7, $5, $3)}
| MERGE ref commitTags COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $5, $7, $3)}
;
commitStatement
: COMMIT commit_arg {yy.commit($2)}
| COMMIT COMMIT_TAG STR {yy.commit('','',yy.commitType.NORMAL,$3)}
| COMMIT COMMIT_TYPE commitType {yy.commit('','',$3,'')}
| COMMIT COMMIT_TAG STR COMMIT_TYPE commitType {yy.commit('','',$5,$3)}
| COMMIT COMMIT_TYPE commitType COMMIT_TAG STR {yy.commit('','',$3,$5)}
| COMMIT COMMIT_ID STR {yy.commit('',$3,yy.commitType.NORMAL,'')}
| COMMIT COMMIT_ID STR COMMIT_TAG STR {yy.commit('',$3,yy.commitType.NORMAL,$5)}
| COMMIT COMMIT_TAG STR COMMIT_ID STR {yy.commit('',$5,yy.commitType.NORMAL,$3)}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType {yy.commit('',$3,$5,'')}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR {yy.commit('',$5,$3,'')}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType COMMIT_TAG STR {yy.commit('',$3,$5,$7)}
| COMMIT COMMIT_ID STR COMMIT_TAG STR COMMIT_TYPE commitType {yy.commit('',$3,$7,$5)}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR COMMIT_TAG STR {yy.commit('',$5,$3,$7)}
| COMMIT COMMIT_TYPE commitType COMMIT_TAG STR COMMIT_ID STR {yy.commit('',$7,$3,$5)}
| COMMIT COMMIT_TAG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.commit('',$7,$5,$3)}
| COMMIT COMMIT_TAG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.commit('',$5,$7,$3)}
| COMMIT COMMIT_MSG STR {yy.commit($3,'',yy.commitType.NORMAL,'')}
| COMMIT COMMIT_TAG STR COMMIT_MSG STR {yy.commit($5,'',yy.commitType.NORMAL,$3)}
| COMMIT COMMIT_MSG STR COMMIT_TAG STR {yy.commit($3,'',yy.commitType.NORMAL,$5)}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($3,'',$5,'')}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($5,'',$3,'')}
| COMMIT COMMIT_ID STR COMMIT_MSG STR {yy.commit($5,$3,yy.commitType.NORMAL,'')}
| COMMIT COMMIT_MSG STR COMMIT_ID STR {yy.commit($3,$5,yy.commitType.NORMAL,'')}
| COMMIT commitTags {yy.commit('','',yy.commitType.NORMAL,$2)}
| COMMIT COMMIT_TYPE commitType {yy.commit('','',$3, undefined)}
| COMMIT commitTags COMMIT_TYPE commitType {yy.commit('','',$4,$2)}
| COMMIT COMMIT_TYPE commitType commitTags {yy.commit('','',$3,$4)}
| COMMIT COMMIT_ID STR {yy.commit('',$3,yy.commitType.NORMAL, undefined)}
| COMMIT COMMIT_ID STR commitTags {yy.commit('',$3,yy.commitType.NORMAL,$4)}
| COMMIT commitTags COMMIT_ID STR {yy.commit('',$4,yy.commitType.NORMAL,$2)}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType {yy.commit('',$3,$5, undefined)}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR {yy.commit('',$5,$3, undefined)}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType commitTags {yy.commit('',$3,$5,$6)}
| COMMIT COMMIT_ID STR commitTags COMMIT_TYPE commitType {yy.commit('',$3,$6,$4)}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR commitTags {yy.commit('',$5,$3,$6)}
| COMMIT COMMIT_TYPE commitType commitTags COMMIT_ID STR {yy.commit('',$6,$3,$4)}
| COMMIT commitTags COMMIT_TYPE commitType COMMIT_ID STR {yy.commit('',$6,$4,$2)}
| COMMIT commitTags COMMIT_ID STR COMMIT_TYPE commitType {yy.commit('',$4,$6,$2)}
| COMMIT COMMIT_MSG STR {yy.commit($3,'',yy.commitType.NORMAL, undefined)}
| COMMIT commitTags COMMIT_MSG STR {yy.commit($4,'',yy.commitType.NORMAL,$2)}
| COMMIT COMMIT_MSG STR commitTags {yy.commit($3,'',yy.commitType.NORMAL,$4)}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($3,'',$5, undefined)}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($5,'',$3, undefined)}
| COMMIT COMMIT_ID STR COMMIT_MSG STR {yy.commit($5,$3,yy.commitType.NORMAL, undefined)}
| COMMIT COMMIT_MSG STR COMMIT_ID STR {yy.commit($3,$5,yy.commitType.NORMAL, undefined)}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_TAG STR {yy.commit($3,'',$5,$7)}
| COMMIT COMMIT_MSG STR COMMIT_TAG STR COMMIT_TYPE commitType {yy.commit($3,'',$7,$5)}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_TAG STR {yy.commit($5,'',$3,$7)}
| COMMIT COMMIT_TYPE commitType COMMIT_TAG STR COMMIT_MSG STR {yy.commit($7,'',$3,$5)}
| COMMIT COMMIT_TAG STR COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($7,'',$5,$3)}
| COMMIT COMMIT_TAG STR COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($5,'',$7,$3)}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType commitTags {yy.commit($3,'',$5,$6)}
| COMMIT COMMIT_MSG STR commitTags COMMIT_TYPE commitType {yy.commit($3,'',$6,$4)}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR commitTags {yy.commit($5,'',$3,$6)}
| COMMIT COMMIT_TYPE commitType commitTags COMMIT_MSG STR {yy.commit($6,'',$3,$4)}
| COMMIT commitTags COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($6,'',$4,$2)}
| COMMIT commitTags COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($4,'',$6,$2)}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.commit($3,$7,$5,'')}
| COMMIT COMMIT_MSG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.commit($3,$5,$7,'')}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_ID STR {yy.commit($5,$7,$3,'')}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR COMMIT_MSG STR {yy.commit($7,$5,$3,'')}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($7,$3,$5,'')}
| COMMIT COMMIT_ID STR COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($5,$3,$7,'')}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.commit($3,$7,$5, undefined)}
| COMMIT COMMIT_MSG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.commit($3,$5,$7, undefined)}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_ID STR {yy.commit($5,$7,$3, undefined)}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR COMMIT_MSG STR {yy.commit($7,$5,$3, undefined)}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($7,$3,$5, undefined)}
| COMMIT COMMIT_ID STR COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($5,$3,$7, undefined)}
| COMMIT COMMIT_MSG STR COMMIT_TAG STR COMMIT_ID STR {yy.commit($3,$7,yy.commitType.NORMAL,$5)}
| COMMIT COMMIT_MSG STR COMMIT_ID STR COMMIT_TAG STR {yy.commit($3,$5,yy.commitType.NORMAL,$7)}
| COMMIT COMMIT_TAG STR COMMIT_MSG STR COMMIT_ID STR {yy.commit($5,$7,yy.commitType.NORMAL,$3)}
| COMMIT COMMIT_TAG STR COMMIT_ID STR COMMIT_MSG STR {yy.commit($7,$5,yy.commitType.NORMAL,$3)}
| COMMIT COMMIT_ID STR COMMIT_TAG STR COMMIT_MSG STR {yy.commit($7,$3,yy.commitType.NORMAL,$5)}
| COMMIT COMMIT_ID STR COMMIT_MSG STR COMMIT_TAG STR {yy.commit($5,$3,yy.commitType.NORMAL,$7)}
| COMMIT COMMIT_MSG STR commitTags COMMIT_ID STR {yy.commit($3,$6,yy.commitType.NORMAL,$4)}
| COMMIT COMMIT_MSG STR COMMIT_ID STR commitTags {yy.commit($3,$5,yy.commitType.NORMAL,$6)}
| COMMIT commitTags COMMIT_MSG STR COMMIT_ID STR {yy.commit($4,$6,yy.commitType.NORMAL,$2)}
| COMMIT commitTags COMMIT_ID STR COMMIT_MSG STR {yy.commit($6,$4,yy.commitType.NORMAL,$2)}
| COMMIT COMMIT_ID STR commitTags COMMIT_MSG STR {yy.commit($6,$3,yy.commitType.NORMAL,$4)}
| COMMIT COMMIT_ID STR COMMIT_MSG STR commitTags {yy.commit($5,$3,yy.commitType.NORMAL,$6)}
| COMMIT COMMIT_MSG STR COMMIT_ID STR COMMIT_TYPE commitType COMMIT_TAG STR {yy.commit($3,$5,$7,$9)}
| COMMIT COMMIT_MSG STR COMMIT_ID STR COMMIT_TAG STR COMMIT_TYPE commitType {yy.commit($3,$5,$9,$7)}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_ID STR COMMIT_TAG STR {yy.commit($3,$7,$5,$9)}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_TAG STR COMMIT_ID STR {yy.commit($3,$9,$5,$7)}
| COMMIT COMMIT_MSG STR COMMIT_TAG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.commit($3,$7,$9,$5)}
| COMMIT COMMIT_MSG STR COMMIT_TAG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.commit($3,$9,$7,$5)}
| COMMIT COMMIT_MSG STR COMMIT_ID STR COMMIT_TYPE commitType commitTags {yy.commit($3,$5,$7,$8)}
| COMMIT COMMIT_MSG STR COMMIT_ID STR commitTags COMMIT_TYPE commitType {yy.commit($3,$5,$8,$6)}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_ID STR commitTags {yy.commit($3,$7,$5,$8)}
| COMMIT COMMIT_MSG STR COMMIT_TYPE commitType commitTags COMMIT_ID STR {yy.commit($3,$8,$5,$6)}
| COMMIT COMMIT_MSG STR commitTags COMMIT_ID STR COMMIT_TYPE commitType {yy.commit($3,$6,$8,$4)}
| COMMIT COMMIT_MSG STR commitTags COMMIT_TYPE commitType COMMIT_ID STR {yy.commit($3,$8,$6,$4)}
| COMMIT COMMIT_ID STR COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_TAG STR {yy.commit($5,$3,$7,$9)}
| COMMIT COMMIT_ID STR COMMIT_MSG STR COMMIT_TAG STR COMMIT_TYPE commitType {yy.commit($5,$3,$9,$7)}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_TAG STR {yy.commit($7,$3,$5,$9)}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType COMMIT_TAG STR COMMIT_MSG STR {yy.commit($9,$3,$5,$7)}
| COMMIT COMMIT_ID STR COMMIT_TAG STR COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($7,$3,$9,$5)}
| COMMIT COMMIT_ID STR COMMIT_TAG STR COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($9,$3,$7,$5)}
| COMMIT COMMIT_ID STR COMMIT_MSG STR COMMIT_TYPE commitType commitTags {yy.commit($5,$3,$7,$8)}
| COMMIT COMMIT_ID STR COMMIT_MSG STR commitTags COMMIT_TYPE commitType {yy.commit($5,$3,$8,$6)}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType COMMIT_MSG STR commitTags {yy.commit($7,$3,$5,$8)}
| COMMIT COMMIT_ID STR COMMIT_TYPE commitType commitTags COMMIT_MSG STR {yy.commit($8,$3,$5,$6)}
| COMMIT COMMIT_ID STR commitTags COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($6,$3,$8,$4)}
| COMMIT COMMIT_ID STR commitTags COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($8,$3,$6,$4)}
| COMMIT COMMIT_TAG STR COMMIT_ID STR COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($9,$5,$7,$3)}
| COMMIT COMMIT_TAG STR COMMIT_ID STR COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($7,$5,$9,$3)}
| COMMIT COMMIT_TAG STR COMMIT_TYPE commitType COMMIT_ID STR COMMIT_MSG STR {yy.commit($9,$7,$5,$3)}
| COMMIT COMMIT_TAG STR COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_ID STR {yy.commit($7,$9,$5,$3)}
| COMMIT COMMIT_TAG STR COMMIT_MSG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.commit($5,$7,$9,$3)}
| COMMIT COMMIT_TAG STR COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.commit($5,$9,$7,$3)}
| COMMIT commitTags COMMIT_ID STR COMMIT_TYPE commitType COMMIT_MSG STR {yy.commit($8,$4,$6,$2)}
| COMMIT commitTags COMMIT_ID STR COMMIT_MSG STR COMMIT_TYPE commitType {yy.commit($6,$4,$8,$2)}
| COMMIT commitTags COMMIT_TYPE commitType COMMIT_ID STR COMMIT_MSG STR {yy.commit($8,$6,$4,$2)}
| COMMIT commitTags COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_ID STR {yy.commit($6,$8,$4,$2)}
| COMMIT commitTags COMMIT_MSG STR COMMIT_ID STR COMMIT_TYPE commitType {yy.commit($4,$6,$8,$2)}
| COMMIT commitTags COMMIT_MSG STR COMMIT_TYPE commitType COMMIT_ID STR {yy.commit($4,$8,$6,$2)}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR COMMIT_MSG STR COMMIT_TAG STR {yy.commit($7,$5,$3,$9)}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR COMMIT_TAG STR COMMIT_MSG STR {yy.commit($9,$5,$3,$7)}
| COMMIT COMMIT_TYPE commitType COMMIT_TAG STR COMMIT_MSG STR COMMIT_ID STR {yy.commit($7,$9,$3,$5)}
| COMMIT COMMIT_TYPE commitType COMMIT_TAG STR COMMIT_ID STR COMMIT_MSG STR {yy.commit($9,$7,$3,$5)}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_ID STR COMMIT_TAG STR {yy.commit($5,$7,$3,$9)}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_TAG STR COMMIT_ID STR {yy.commit($5,$9,$3,$7)}
// | COMMIT COMMIT_ID STR {yy.commit('',$3,yy.commitType.NORMAL,'')}
// | COMMIT COMMIT_TYPE commitType {yy.commit('','',$3,'')}
// | COMMIT COMMIT_TAG STR {yy.commit('','',yy.commitType.NORMAL,$3)}
// | COMMIT COMMIT_MSG STR {yy.commit($3,'',yy.commitType.NORMAL,'')}
// | COMMIT COMMIT_TAG STR COMMIT_TYPE commitType {yy.commit('','',$5,$3)}
// | COMMIT COMMIT_TYPE commitType COMMIT_TAG STR {yy.commit('','',$3,$5)}
// | COMMIT COMMIT_ID STR COMMIT_TYPE commitType {yy.commit('',$3,$5,'')}
// | COMMIT COMMIT_ID STR COMMIT_TAG STR {yy.commit('',$3,yy.commitType.NORMAL,$5)}
// | COMMIT COMMIT_ID STR COMMIT_TYPE commitType COMMIT_TAG STR {yy.commit('',$3,$5,$7)}
// | COMMIT COMMIT_ID STR COMMIT_TAG STR COMMIT_TYPE commitType {yy.commit('',$3,$7,$5)}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR COMMIT_MSG STR commitTags {yy.commit($7,$5,$3,$8)}
| COMMIT COMMIT_TYPE commitType COMMIT_ID STR commitTags COMMIT_MSG STR {yy.commit($8,$5,$3,$6)}
| COMMIT COMMIT_TYPE commitType commitTags COMMIT_MSG STR COMMIT_ID STR {yy.commit($6,$8,$3,$4)}
| COMMIT COMMIT_TYPE commitType commitTags COMMIT_ID STR COMMIT_MSG STR {yy.commit($8,$6,$3,$4)}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR COMMIT_ID STR commitTags {yy.commit($5,$7,$3,$8)}
| COMMIT COMMIT_TYPE commitType COMMIT_MSG STR commitTags COMMIT_ID STR {yy.commit($5,$8,$3,$6)}
;
commit_arg
: /* empty */ {$$ = ""}
@@ -238,6 +221,12 @@ commitType
| REVERSE { $$=yy.commitType.REVERSE;}
| HIGHLIGHT { $$=yy.commitType.HIGHLIGHT;}
;
commitTags
: COMMIT_TAG STR {$$=[$2]}
| COMMIT_TAG EMPTYSTR {$$=['']}
| commitTags COMMIT_TAG STR {$commitTags.push($3); $$=$commitTags;}
| commitTags COMMIT_TAG EMPTYSTR {$commitTags.push(''); $$=$commitTags;}
;
ref
: ID

View File

@@ -198,9 +198,6 @@ function getClassesFromDbInfo(dbInfoItem) {
* Get classes from the db for the info item.
* If there aren't any or if dbInfoItem isn't defined, return an empty string.
* Else create 1 string from the list of classes found
*
* @param {undefined | null | object} dbInfoItem
* @returns {string}
*/
function getStylesFromDbInfo(dbInfoItem) {
if (dbInfoItem === undefined || dbInfoItem === null) {

View File

@@ -37,6 +37,8 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [CloudScript.io Mermaid Addon](https://marketplace.atlassian.com/apps/1219878/cloudscript-io-mermaid-addon?hosting=cloud&tab=overview)
- [Azure Devops](https://learn.microsoft.com/en-us/azure/devops/project/wiki/markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) ✅
- [Deepdwn](https://billiam.itch.io/deepdwn) ✅
- [Doctave](https://www.doctave.com/) ✅
- [Mermaid in Markdown code blocks](https://docs.doctave.com/components/mermaid) ✅
- [GitBook](https://gitbook.com)
- [Mermaid Plugin](https://github.com/JozoVilcek/gitbook-plugin-mermaid)
- [Mermaid plugin for GitBook](https://github.com/wwformat/gitbook-plugin-mermaid-pdf)
@@ -201,6 +203,7 @@ Communication tools and platforms
- [gatsby-remark-mermaid](https://github.com/remcohaszing/gatsby-remark-mermaid)
- [JSDoc](https://jsdoc.app/)
- [jsdoc-mermaid](https://github.com/Jellyvision/jsdoc-mermaid)
- [Madness](https://madness.dannyb.co/)
- [mdBook](https://rust-lang.github.io/mdBook/index.html)
- [mdbook-mermaid](https://github.com/badboy/mdbook-mermaid)
- [MkDocs](https://www.mkdocs.org)

View File

@@ -100,7 +100,7 @@ Cardinality is a property that describes how many elements of another entity can
| 1+ | 1+ | One or more |
| zero or more | zero or more | Zero or more |
| zero or many | zero or many | Zero or more |
| many(0) | many(1) | Zero or more |
| many(0) | many(0) | Zero or more |
| 0+ | 0+ | Zero or more |
| only one | only one | Exactly one |
| 1 | 1 | Exactly one |

View File

@@ -6,7 +6,7 @@ A packet diagram is a visual representation used to illustrate the structure and
## Usage
This diagram type is particularly useful for network engineers, educators, and students who require a clear and concise way to represent the structure of network packets.
This diagram type is particularly useful for developers, network engineers, educators, and students who require a clear and concise way to represent the structure of network packets.
## Syntax

View File

@@ -288,7 +288,7 @@ a _[valid CSS property name](https://www.w3.org/TR/CSS/#properties)_ followed by
Here is an example of a classDef with just one property-value pair:
```
```txt
classDef movement font-style:italic;
```
@@ -301,7 +301,7 @@ If you want to have more than one _property-value pair_ then you put a comma (`,
Here is an example with three property-value pairs:
```
```txt
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
```

View File

@@ -49,8 +49,8 @@ async function addHtmlSpan(element, node, width, classes, addBackground = false)
bbox = div.node().getBoundingClientRect();
}
fo.style('width', bbox.width);
fo.style('height', bbox.height);
// fo.style('width', bbox.width);
// fo.style('height', bbox.height);
return fo.node();
}
@@ -159,7 +159,7 @@ function updateTextContentAndStyles(tspan: any, wrappedLine: MarkdownWord[]) {
wrappedLine.forEach((word, index) => {
const innerTspan = tspan
.append('tspan')
.attr('font-style', word.type === 'emphasis' ? 'italic' : 'normal')
.attr('font-style', word.type === 'em' ? 'italic' : 'normal')
.attr('class', 'text-inner-tspan')
.attr('font-weight', word.type === 'strong' ? 'bold' : 'normal');
if (index === 0) {
@@ -216,28 +216,61 @@ export const createText = async (
const htmlText = markdownToHTML(text, config);
const decodedReplacedText = replaceIconSubstring(decodeEntities(htmlText));
//for Katex the text could contain escaped characters, \\relax that should be transformed to \relax
const inputForKatex = text.replace(/\\\\/g, '\\');
const node = {
isNode,
label: decodedReplacedText,
label: hasKatex(text) ? inputForKatex : decodedReplacedText,
labelStyle: style.replace('fill:', 'color:'),
};
const vertexNode = await addHtmlSpan(el, node, width, classes, addSvgBackground);
return vertexNode;
} else {
const structuredText = markdownToLines(text.replace('<br>', '<br/>'), config);
//sometimes the user might add br tags with 1 or more spaces in between, so we need to replace them with <br/>
const sanitizeBR = text.replace(/<br\s*\/?>/g, '<br/>');
const structuredText = markdownToLines(sanitizeBR.replace('<br>', '<br/>'), config);
const svgLabel = createFormattedText(
width,
el,
structuredText,
text ? addSvgBackground : false
);
if (isNode) {
if (/stroke:/.exec(style)) {
style = style.replace('stroke:', 'lineColor:');
}
select(svgLabel)
.select('text')
.attr('style', style.replace(/color:/g, 'fill:'));
const nodeLabelTextStyle = style
.replace(/stroke:[^;]+;?/g, '')
.replace(/stroke-width:[^;]+;?/g, '')
.replace(/fill:[^;]+;?/g, '')
.replace(/color:/g, 'fill:');
select(svgLabel).attr('style', nodeLabelTextStyle);
// svgLabel.setAttribute('style', style);
} else {
//On style, assume `stroke`, `stroke-width` are used for edge path, so remove them
// remove `fill`
// use `background` as `fill` for label rect,
const edgeLabelRectStyle = style
.replace(/stroke:[^;]+;?/g, '')
.replace(/stroke-width:[^;]+;?/g, '')
.replace(/fill:[^;]+;?/g, '')
.replace(/background:/g, 'fill:');
select(svgLabel)
.select('rect')
.attr('style', edgeLabelRectStyle.replace(/background:/g, 'fill:'));
// for text, update fill color with `color`
const edgeLabelTextStyle = style
.replace(/stroke:[^;]+;?/g, '')
.replace(/stroke-width:[^;]+;?/g, '')
.replace(/fill:[^;]+;?/g, '')
.replace(/color:/g, 'fill:');
select(svgLabel).select('text').attr('style', edgeLabelTextStyle);
}
return svgLabel;
}
};

View File

@@ -1,4 +1,3 @@
/* eslint-disable no-irregular-whitespace */
import { markdownToLines, markdownToHTML } from './handle-markdown-text.js';
import { test, expect } from 'vitest';
@@ -37,9 +36,9 @@ Here is a line *with an italic* section`;
{ content: 'is', type: 'normal' },
{ content: 'a', type: 'normal' },
{ content: 'line', type: 'normal' },
{ content: 'with', type: 'emphasis' },
{ content: 'an', type: 'emphasis' },
{ content: 'italic', type: 'emphasis' },
{ content: 'with', type: 'em' },
{ content: 'an', type: 'em' },
{ content: 'italic', type: 'em' },
{ content: 'section', type: 'normal' },
],
];
@@ -143,7 +142,7 @@ test('markdownToLines - Only italic formatting', () => {
{ content: 'This', type: 'normal' },
{ content: 'is', type: 'normal' },
{ content: 'an', type: 'normal' },
{ content: 'italic', type: 'emphasis' },
{ content: 'italic', type: 'em' },
{ content: 'test', type: 'normal' },
],
];
@@ -156,7 +155,7 @@ it('markdownToLines - Mixed formatting', () => {
let input = `*Italic* and **bold** formatting`;
let expected = [
[
{ content: 'Italic', type: 'emphasis' },
{ content: 'Italic', type: 'em' },
{ content: 'and', type: 'normal' },
{ content: 'bold', type: 'strong' },
{ content: 'formatting', type: 'normal' },
@@ -167,9 +166,9 @@ it('markdownToLines - Mixed formatting', () => {
input = `*Italic with space* and **bold ws** formatting`;
expected = [
[
{ content: 'Italic', type: 'emphasis' },
{ content: 'with', type: 'emphasis' },
{ content: 'space', type: 'emphasis' },
{ content: 'Italic', type: 'em' },
{ content: 'with', type: 'em' },
{ content: 'space', type: 'em' },
{ content: 'and', type: 'normal' },
{ content: 'bold', type: 'strong' },
{ content: 'ws', type: 'strong' },
@@ -191,9 +190,9 @@ Word!`;
{ content: 'the', type: 'strong' },
{ content: 'hog...', type: 'normal' },
{ content: 'a', type: 'normal' },
{ content: 'very', type: 'emphasis' },
{ content: 'long', type: 'emphasis' },
{ content: 'text', type: 'emphasis' },
{ content: 'very', type: 'em' },
{ content: 'long', type: 'em' },
{ content: 'text', type: 'em' },
{ content: 'about', type: 'normal' },
{ content: 'it', type: 'normal' },
],
@@ -215,13 +214,13 @@ test('markdownToLines - No auto wrapping', () => {
[
[
{
"content": "Hello, how do",
"content": "Hello,&nbsp;how&nbsp;do",
"type": "normal",
},
],
[
{
"content": "you do?",
"content": "you&nbsp;do?",
"type": "normal",
},
],
@@ -298,3 +297,13 @@ test('markdownToHTML - no auto wrapping', () => {
)
).toMatchInlineSnapshot('"<p>Hello,&nbsp;how&nbsp;do<br/>you&nbsp;do?</p>"');
});
test('markdownToHTML - auto wrapping', () => {
expect(
markdownToHTML(
`Hello, how do
you do?`,
{ markdownAutoWrap: true }
)
).toMatchInlineSnapshot('"<p>Hello, how do<br/>you do?</p>"');
});

View File

@@ -1,5 +1,5 @@
import type { Content } from 'mdast';
import { fromMarkdown } from 'mdast-util-from-markdown';
import type { MarkedToken, Token } from 'marked';
import { marked } from 'marked';
import { dedent } from 'ts-dedent';
import type { MarkdownLine, MarkdownWordType } from './types.js';
import type { MermaidConfig } from '../config.type.js';
@@ -9,8 +9,10 @@ import type { MermaidConfig } from '../config.type.js';
* @returns processed markdown
*/
function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfig): string {
//Replace <br/>with \n
const withoutBR = markdown.replace(/<br\/>/g, '\n');
// Replace multiple newlines with a single newline
const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
const withoutMultipleNewlines = withoutBR.replace(/\n{2,}/g, '\n');
// Remove extra spaces at the beginning of each line
const withoutExtraSpaces = dedent(withoutMultipleNewlines);
if (markdownAutoWrap === false) {
@@ -24,13 +26,13 @@ function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfi
*/
export function markdownToLines(markdown: string, config: MermaidConfig = {}): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown, config);
const { children } = fromMarkdown(preprocessedMarkdown);
const nodes = marked.lexer(preprocessedMarkdown);
const lines: MarkdownLine[] = [[]];
let currentLine = 0;
function processNode(node: Content, parentType: MarkdownWordType = 'normal') {
function processNode(node: MarkedToken, parentType: MarkdownWordType = 'normal') {
if (node.type === 'text') {
const textLines = node.value.split('\n');
const textLines = node.text.split('\n');
textLines.forEach((textLine, index) => {
if (index !== 0) {
currentLine++;
@@ -42,18 +44,22 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M
}
});
});
} else if (node.type === 'strong' || node.type === 'emphasis') {
node.children.forEach((contentNode) => {
processNode(contentNode, node.type);
} else if (node.type === 'strong' || node.type === 'em') {
node.tokens.forEach((contentNode) => {
processNode(contentNode as MarkedToken, node.type);
});
} else if (node.type === 'html') {
lines[currentLine].push({ content: node.text, type: 'normal' });
}
}
children.forEach((treeNode) => {
nodes.forEach((treeNode) => {
if (treeNode.type === 'paragraph') {
treeNode.children.forEach((contentNode) => {
processNode(contentNode);
treeNode.tokens?.forEach((contentNode) => {
processNode(contentNode as MarkedToken);
});
} else if (treeNode.type === 'html') {
lines[currentLine].push({ content: treeNode.text, type: 'normal' });
}
});
@@ -61,25 +67,27 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M
}
export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidConfig = {}) {
const { children } = fromMarkdown(markdown);
const nodes = marked.lexer(markdown);
function output(node: Content): string {
function output(node: Token): string {
if (node.type === 'text') {
if (markdownAutoWrap === false) {
return node.value.replace(/\n/g, '<br/>').replace(/ /g, '&nbsp;');
return node.text.replace(/\n */g, '<br/>').replace(/ /g, '&nbsp;');
}
return node.value.replace(/\n/g, '<br/>');
return node.text.replace(/\n */g, '<br/>');
} else if (node.type === 'strong') {
return `<strong>${node.children.map(output).join('')}</strong>`;
} else if (node.type === 'emphasis') {
return `<em>${node.children.map(output).join('')}</em>`;
return `<strong>${node.tokens?.map(output).join('')}</strong>`;
} else if (node.type === 'em') {
return `<em>${node.tokens?.map(output).join('')}</em>`;
} else if (node.type === 'paragraph') {
return `<p>${node.children.map(output).join('')}</p>`;
return `<p>${node.tokens?.map(output).join('')}</p>`;
} else if (node.type === 'space') {
return '';
} else if (node.type === 'html') {
return `${node.value}`;
return `${node.text}`;
}
return `Unsupported markdown: ${node.type}`;
}
return children.map(output).join('');
return nodes.map(output).join('');
}

View File

@@ -121,7 +121,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
graph
);
log.info(findNonClusterChild(node.id, graph));
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
clusterDb.set(node.id, { id: findNonClusterChild(node.id, graph), node });
// insertCluster(clusters, graph.node(v));
} else {
log.trace('Node - the non recursive path XAX', v, node.id, node);
@@ -138,7 +138,16 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
// Check if link is either from or to a cluster
log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]);
log.info(
'Fix',
clusterDb,
'ids:',
e.v,
e.w,
'Translating: ',
clusterDb.get(e.v),
clusterDb.get(e.w)
);
await insertEdgeLabel(edgeLabels, edge);
});
@@ -147,20 +156,6 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
await processEdges();
// // Insert labels, this will insert them into the dom so that the width can be calculated
// // Also figure out which edges point to/from clusters and adjust them accordingly
// // Edges from/to clusters really points to the first child in the cluster.
// // TODO: pick optimal child in the cluster to us as link anchor
// await graph.edges().forEach(async function (e) {
// const edge = graph.edge(e.v, e.w, e.name);
// log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
// log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
// // Check if link is either from or to a cluster
// log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]);
// await insertEdgeLabel(edgeLabels, edge);
// });
log.info('Graph before layout:', JSON.stringify(graphlibJson.write(graph)));
log.info('############################################# XXX');
@@ -198,7 +193,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
node.y,
graph.parent(v)
);
clusterDb[node.id].node = node;
clusterDb.get(node.id).node = node;
positionNode(node);
} else {
// A tainted cluster node
@@ -222,7 +217,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
await insertCluster(clusters, node);
// A cluster in the non-recursive way
clusterDb[node.id].node = node;
clusterDb.get(node.id).node = node;
} else {
// Regular node
const parent = graph.node(node.parentId);
@@ -255,7 +250,6 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
const edge = graph.edge(e);
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
// OBS HERE
edge.points.forEach((point) => (point.y += subGraphTitleTotalMargin / 2));
const startNode = graph.node(e.v);
var endNode = graph.node(e.w);
@@ -273,17 +267,8 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
log.warn('Returning from recursive render XAX', elem, diff);
return { elem, diff };
};
/**
* ###############################################################
* Render the graph
* ###############################################################
* @param data4Layout
* @param svg
* @param element
*/
export const render = async (data4Layout, svg, element) => {
// Create the input mermaid.graph
// console.log('XYZ data4Layout', data4Layout);
const graph = new graphlib.Graph({
multigraph: true,
compound: true,
@@ -305,15 +290,12 @@ export const render = async (data4Layout, svg, element) => {
return {};
});
// Org
insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId);
clearNodes();
clearEdges();
clearClusters();
clearGraphlib();
// Add the nodes and edges to the graph
data4Layout.nodes.forEach((node) => {
graph.setNode(node.id, { ...node });
if (node.parentId) {

View File

@@ -3,39 +3,40 @@ import { log } from '$root/logger.js';
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json.js';
export let clusterDb = {};
let descendants = {};
let parents = {};
export let clusterDb = new Map();
let descendants = new Map();
let parents = new Map();
export const clear = () => {
descendants = {};
parents = {};
clusterDb = {};
descendants.clear();
parents.clear();
clusterDb.clear();
};
const isDescendant = (id, ancestorId) => {
log.trace('In isDescendant', ancestorId, ' ', id, ' = ', descendants[ancestorId].includes(id));
return descendants[ancestorId].includes(id);
const ancestorDescendants = descendants.get(ancestorId) || [];
log.trace('In isDescendant', ancestorId, ' ', id, ' = ', ancestorDescendants.includes(id));
return ancestorDescendants.includes(id);
};
const edgeInCluster = (edge, clusterId) => {
log.info('Descendants of ', clusterId, ' is ', descendants[clusterId]);
const clusterDescendants = descendants.get(clusterId) || [];
log.info('Descendants of ', clusterId, ' is ', clusterDescendants);
log.info('Edge is ', edge);
// Edges to/from the cluster is not in the cluster, they are in the parent
if (edge.v === clusterId || edge.w === clusterId) {
return false;
}
if (!descendants[clusterId]) {
if (!clusterDescendants) {
log.debug('Tilt, ', clusterId, ',not in descendants');
return false;
}
return (
descendants[clusterId].includes(edge.v) ||
clusterDescendants.includes(edge.v) ||
isDescendant(edge.v, clusterId) ||
isDescendant(edge.w, clusterId) ||
descendants[clusterId].includes(edge.w)
clusterDescendants.includes(edge.w)
);
};
@@ -51,7 +52,6 @@ const copy = (clusterId, graph, newGraph, rootId) => {
);
const nodes = graph.children(clusterId) || [];
// Include cluster node if it is not the root
if (clusterId !== rootId) {
nodes.push(clusterId);
}
@@ -63,7 +63,7 @@ const copy = (clusterId, graph, newGraph, rootId) => {
copy(node, graph, newGraph, rootId);
} else {
const data = graph.node(node);
log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId); //,node, data, ' parent is ', clusterId);
log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId);
newGraph.setNode(node, data);
if (rootId !== graph.parent(node)) {
log.warn('Setting parent', node, graph.parent(node));
@@ -91,7 +91,6 @@ const copy = (clusterId, graph, newGraph, rootId) => {
const data = graph.edge(edge.v, edge.w, edge.name);
log.info('Edge data', data, rootId);
try {
// Do not copy edges in and out of the root cluster, they belong to the parent graph
if (edgeInCluster(edge, rootId)) {
log.info('Copying as ', edge.v, edge.w, data, edge.name);
newGraph.setEdge(edge.v, edge.w, data, edge.name);
@@ -117,25 +116,19 @@ const copy = (clusterId, graph, newGraph, rootId) => {
graph.removeNode(node);
});
};
export const extractDescendants = (id, graph) => {
// log.debug('Extracting ', id);
const children = graph.children(id);
let res = [...children];
for (const child of children) {
parents[child] = id;
parents.set(child, id);
res = [...res, ...extractDescendants(child, graph)];
}
return res;
};
/**
* Validates the graph, checking that all parent child relation points to existing nodes and that
* edges between nodes also ia correct. When not correct the function logs the discrepancies.
*
* @param graph
*/
export const validate = (graph) => {
const edges = graph.edges();
log.trace('Edges: ', edges);
@@ -168,13 +161,6 @@ const findCommonEdges = (graph, id1, id2) => {
return result;
};
/**
* Finds a child that is not a cluster. When faking an edge between a node and a cluster.
*
* @param id
* @param {any} graph
* @param {string} clusterId
*/
export const findNonClusterChild = (id, graph, clusterId) => {
const children = graph.children(id);
log.trace('Searching children of id ', id, children);
@@ -185,19 +171,10 @@ export const findNonClusterChild = (id, graph, clusterId) => {
for (const child of children) {
const _id = findNonClusterChild(child, graph, clusterId);
// Edge chase where the cluster has an edge to a node and the selected
// child has a link to the same node
const commonEdges = findCommonEdges(graph, clusterId, _id);
if (_id) {
if (commonEdges.length > 0) {
// console.log(
// '\x1B[44;93;4m abc24 The replacement also has an edge',
// clusterId,
// ' => ',
// _id,
// graph.edges()
// );
reserve = _id;
} else {
return _id;
@@ -208,17 +185,15 @@ export const findNonClusterChild = (id, graph, clusterId) => {
};
const getAnchorId = (id) => {
if (!clusterDb[id]) {
if (!clusterDb.has(id)) {
return id;
}
// If the cluster has no external connections
if (!clusterDb[id].externalConnections) {
if (!clusterDb.get(id).externalConnections) {
return id;
}
// Return the replacement node
if (clusterDb[id]) {
return clusterDb[id].id;
if (clusterDb.has(id)) {
return clusterDb.get(id).id;
}
return id;
};
@@ -230,8 +205,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
} else {
log.debug('Opting in, graph ');
}
// Go through the nodes and for each cluster found, save a replacement node, this can be used when
// faking a link to a cluster
graph.nodes().forEach(function (id) {
const children = graph.children(id);
if (children.length > 0) {
@@ -241,34 +215,24 @@ export const adjustClustersAndEdges = (graph, depth) => {
' Replacement id in edges: ',
findNonClusterChild(id, graph, id)
);
descendants[id] = extractDescendants(id, graph);
clusterDb[id] = { id: findNonClusterChild(id, graph, id), clusterData: graph.node(id) };
descendants.set(id, extractDescendants(id, graph));
clusterDb.set(id, { id: findNonClusterChild(id, graph, id), clusterData: graph.node(id) });
}
});
// Check incoming and outgoing edges for each cluster
graph.nodes().forEach(function (id) {
const children = graph.children(id);
const edges = graph.edges();
if (children.length > 0) {
log.debug('Cluster identified', id, descendants);
edges.forEach((edge) => {
// log.debug('Edge, descendants: ', edge, descendants[id]);
// Check if any edge leaves the cluster (not the actual cluster, that's a link from the box)
if (edge.v !== id && edge.w !== id) {
// Any edge where either the one of the nodes is descending to the cluster but not the other
// if (descendants[id].indexOf(edge.v) < 0 && descendants[id].indexOf(edge.w) < 0) {
const d1 = isDescendant(edge.v, id);
const d2 = isDescendant(edge.w, id);
// d1 xor d2 - if either d1 is true and d2 is false or the other way around
if (d1 ^ d2) {
log.warn('Edge: ', edge, ' leaves cluster ', id);
log.warn('Descendants of XXX ', id, ': ', descendants[id]);
clusterDb[id].externalConnections = true;
}
log.warn('Descendants of XXX ', id, ': ', descendants.get(id));
clusterDb.get(id).externalConnections = true;
}
});
} else {
@@ -276,18 +240,15 @@ export const adjustClustersAndEdges = (graph, depth) => {
}
});
for (let id of Object.keys(clusterDb)) {
const nonClusterChild = clusterDb[id].id;
for (let id of clusterDb.keys()) {
const nonClusterChild = clusterDb.get(id).id;
const parent = graph.parent(nonClusterChild);
// Change replacement node of id to parent of current replacement node if valid
if (parent !== id && clusterDb[parent] && !clusterDb[parent].externalConnections) {
clusterDb[id].id = parent;
if (parent !== id && clusterDb.has(parent) && !clusterDb.get(parent).externalConnections) {
clusterDb.get(id).id = parent;
}
}
// For clusters with incoming and/or outgoing edges translate those edges to a real node
// in the cluster in order to fake the edge
graph.edges().forEach(function (e) {
const edge = graph.edge(e);
log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
@@ -295,7 +256,6 @@ export const adjustClustersAndEdges = (graph, depth) => {
let v = e.v;
let w = e.w;
// Check if link is either from or to a cluster
log.warn(
'Fix XXX',
clusterDb,
@@ -303,14 +263,13 @@ export const adjustClustersAndEdges = (graph, depth) => {
e.v,
e.w,
'Translating: ',
clusterDb[e.v],
clusterDb.get(e.v),
' --- ',
clusterDb[e.w]
clusterDb.get(e.w)
);
if (clusterDb[e.v] && clusterDb[e.w] && clusterDb[e.v] === clusterDb[e.w]) {
// cspell:ignore trixing
log.warn('Fixing and trixing link to self - removing XXX', e.v, e.w, e.name);
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
if (clusterDb.get(e.v) && clusterDb.get(e.w) && clusterDb.get(e.v) === clusterDb.get(e.w)) {
log.warn('Fixing and trying link to self - removing XXX', e.v, e.w, e.name);
log.warn('Fixing and trying - removing XXX', e.v, e.w, e.name);
v = getAnchorId(e.v);
w = getAnchorId(e.w);
graph.removeEdge(e.v, e.w, e.name);
@@ -320,7 +279,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
domId: specialId1,
id: specialId1,
labelStyle: '',
label: edge.label,
label: '',
padding: 0,
shape: 'labelRect',
style: '',
@@ -342,27 +301,29 @@ export const adjustClustersAndEdges = (graph, depth) => {
const edge2 = structuredClone(edge);
edge1.label = '';
edge1.arrowTypeEnd = 'none';
edge1.id = e.name + '-cyclic-special-1';
edgeMid.arrowTypeEnd = 'none';
edgeMid.id = e.name + '-cyclic-special-mid';
edge2.label = '';
edge1.fromCluster = e.v;
edge2.toCluster = e.v;
graph.setEdge(v, specialId1, edge1, e.name + '-cyclic-special');
graph.setEdge(specialId1, specialId2, edgeMid, e.name + '-cyclic-special');
graph.setEdge(specialId2, w, edge2, e.name + '-cyclic-special');
} else if (clusterDb[e.v] || clusterDb[e.w]) {
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
edge2.id = e.name + '-cyclic-special-2';
graph.setEdge(v, specialId1, edge1, e.name + '-cyclic-special-0');
graph.setEdge(specialId1, specialId2, edgeMid, e.name + '-cyclic-special-1');
graph.setEdge(specialId2, w, edge2, e.name + '-cyclic-special-2');
} else if (clusterDb.get(e.v) || clusterDb.get(e.w)) {
log.warn('Fixing and trying - removing XXX', e.v, e.w, e.name);
v = getAnchorId(e.v);
w = getAnchorId(e.w);
graph.removeEdge(e.v, e.w, e.name);
if (v !== e.v) {
const parent = graph.parent(v);
clusterDb[parent].externalConnections = true;
clusterDb.get(parent).externalConnections = true;
edge.fromCluster = e.v;
}
if (w !== e.w) {
const parent = graph.parent(w);
clusterDb[parent].externalConnections = true;
clusterDb.get(parent).externalConnections = true;
edge.toCluster = e.w;
}
log.warn('Fix Replacing with XXX', v, w, e.name);
@@ -375,7 +336,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
log.trace(clusterDb);
// Remove references to extracted cluster
// graph.edges().forEach(edge => {
// graph.edges().forEach((edge) => {
// if (isDescendant(edge.v, clusterId) || isDescendant(edge.w, clusterId)) {
// graph.removeEdge(edge);
// }
@@ -388,9 +349,6 @@ export const extractor = (graph, depth) => {
log.error('Bailing out');
return;
}
// For clusters without incoming and/or outgoing edges, create a new cluster-node
// containing the nodes and edges in the custer in a new graph
// for (let i = 0;)
let nodes = graph.nodes();
let hasChildren = false;
for (const node of nodes) {
@@ -402,30 +360,23 @@ export const extractor = (graph, depth) => {
log.debug('Done, no node has children', graph.nodes());
return;
}
// const clusters = Object.keys(clusterDb);
// clusters.forEach(clusterId => {
log.debug('Nodes = ', nodes, depth);
for (const node of nodes) {
log.debug(
'Extracting node',
node,
clusterDb,
clusterDb[node] && !clusterDb[node].externalConnections,
clusterDb.has(node) && !clusterDb.get(node).externalConnections,
!graph.parent(node),
graph.node(node),
graph.children('D'),
' Depth ',
depth
);
// Note that the node might have been removed after the Object.keys call so better check
// that it still is in the game
if (!clusterDb[node]) {
// Skip if the node is not a cluster
if (!clusterDb.has(node)) {
log.debug('Not a cluster', node, depth);
// break;
} else if (
!clusterDb[node].externalConnections &&
// !graph.parent(node) &&
!clusterDb.get(node).externalConnections &&
graph.children(node) &&
graph.children(node).length > 0
) {
@@ -437,9 +388,9 @@ export const extractor = (graph, depth) => {
const graphSettings = graph.graph();
let dir = graphSettings.rankdir === 'TB' ? 'LR' : 'TB';
if (clusterDb[node]?.clusterData?.dir) {
dir = clusterDb[node].clusterData.dir;
log.warn('Fixing dir', clusterDb[node].clusterData.dir, dir);
if (clusterDb.get(node)?.clusterData?.dir) {
dir = clusterDb.get(node).clusterData.dir;
log.warn('Fixing dir', clusterDb.get(node).clusterData.dir, dir);
}
const clusterGraph = new graphlib.Graph({
@@ -447,7 +398,7 @@ export const extractor = (graph, depth) => {
compound: true,
})
.setGraph({
rankdir: dir, // Todo: set proper spacing
rankdir: dir,
nodesep: 50,
ranksep: 50,
marginx: 8,
@@ -462,8 +413,8 @@ export const extractor = (graph, depth) => {
graph.setNode(node, {
clusterNode: true,
id: node,
clusterData: clusterDb[node].clusterData,
label: clusterDb[node].label,
clusterData: clusterDb.get(node).clusterData,
label: clusterDb.get(node).label,
graph: clusterGraph,
});
log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph));
@@ -473,7 +424,7 @@ export const extractor = (graph, depth) => {
'Cluster ** ',
node,
' **not meeting the criteria !externalConnections:',
!clusterDb[node].externalConnections,
!clusterDb.get(node).externalConnections,
' no parent: ',
!graph.parent(node),
' children ',
@@ -500,7 +451,7 @@ const sorter = (graph, nodes) => {
if (nodes.length === 0) {
return [];
}
let result = Object.assign(nodes);
let result = Object.assign([], nodes);
nodes.forEach((node) => {
const children = graph.children(node);
const sorted = sorter(graph, children);

View File

@@ -154,7 +154,6 @@ export const positionEdgeLabel = (edge, paths) => {
let x = edge.x;
let y = edge.y;
if (path) {
// // debugger;
const pos = utils.calcLabelPosition(path);
log.debug(
'Moving label ' + edge.label + ' from (',
@@ -175,13 +174,11 @@ export const positionEdgeLabel = (edge, paths) => {
el.attr('transform', `translate(${x}, ${y + subGraphTitleTotalMargin / 2})`);
}
//let path = paths.updatedPath ? paths.updatedPath : paths.originalPath;
if (edge.startLabelLeft) {
const el = terminalLabels.get(edge.id).startLeft;
let x = edge.x;
let y = edge.y;
if (path) {
// debugger;
const pos = utils.calcTerminalLabelPosition(edge.arrowTypeStart ? 10 : 0, 'start_left', path);
x = pos.x;
y = pos.y;
@@ -193,7 +190,6 @@ export const positionEdgeLabel = (edge, paths) => {
let x = edge.x;
let y = edge.y;
if (path) {
// debugger;
const pos = utils.calcTerminalLabelPosition(
edge.arrowTypeStart ? 10 : 0,
'start_right',
@@ -209,7 +205,6 @@ export const positionEdgeLabel = (edge, paths) => {
let x = edge.x;
let y = edge.y;
if (path) {
// debugger;
const pos = utils.calcTerminalLabelPosition(edge.arrowTypeEnd ? 10 : 0, 'end_left', path);
x = pos.x;
y = pos.y;
@@ -275,21 +270,18 @@ export const intersection = (node, outsidePoint, insidePoint) => {
res.y = outsidePoint.y;
}
log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line
log.debug(`abc89 top/bottom calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res);
return res;
} else {
// Intersection onn sides of rect
// Intersection on sides of rect
if (insidePoint.x < outsidePoint.x) {
r = outsidePoint.x - w - x;
} else {
r = x - w - outsidePoint.x;
}
let q = (Q * r) / R;
// OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w;
// OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r;
// let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r;
let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q;
log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y });
if (r === 0) {
@@ -306,49 +298,34 @@ export const intersection = (node, outsidePoint, insidePoint) => {
return { x: _x, y: _y };
}
};
/**
* This function will page a path and node where the last point(s) in the path is inside the node
* and return an update path ending by the border of the node.
*
* @param {Array} _points
* @param {any} boundaryNode
* @returns {Array} Points
*/
const cutPathAtIntersect = (_points, boundaryNode) => {
log.warn('abc88 cutPathAtIntersect', _points, boundaryNode);
let points = [];
let lastPointOutside = _points[0];
let isInside = false;
_points.forEach((point) => {
// const node = clusterDb[edge.toCluster].node;
log.info('abc88 checking point', point, boundaryNode);
// check if point is inside the boundary rect
if (!outsideNode(boundaryNode, point) && !isInside) {
// First point inside the rect found
// Calc the intersection coord between the point anf the last point outside the rect
const inter = intersection(boundaryNode, lastPointOutside, point);
log.debug('abc88 inside', point, lastPointOutside, inter);
log.debug('abc88 intersection', inter, boundaryNode);
// // Check case where the intersection is the same as the last point
let pointPresent = false;
points.forEach((p) => {
pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y);
});
// // if (!pointPresent) {
if (!points.some((e) => e.x === inter.x && e.y === inter.y)) {
points.push(inter);
} else {
log.warn('abc88 no intersect', inter, points);
}
// points.push(inter);
isInside = true;
} else {
// Outside
log.warn('abc88 outside', point, lastPointOutside);
lastPointOutside = point;
// points.push(point);
if (!isInside) {
points.push(point);
}
@@ -358,13 +335,6 @@ const cutPathAtIntersect = (_points, boundaryNode) => {
return points;
};
/**
* Given an edge, this function will return the corner points of the edge. This is defined as:
* one point that has a previous point and a next point such as the angle between the previous
* point and the next point is 90 degrees. Meaning that the previous point has the same x coordinate
* as the center point and at the same time the next point has the same y coordinate or vice versa.
* @param points
*/
function extractCornerPoints(points) {
const cornerPoints = [];
const cornerPointPositions = [];
@@ -401,11 +371,6 @@ const findAdjacentPoint = function (pointA, pointB, distance) {
return { x: pointB.x - ratio * xDiff, y: pointB.y - ratio * yDiff };
};
/**
* Given an array of points, this function will return a new array of points where the corners have been removed and replaced with
* adjacent points in each direction. SO a corder will be replaced with a point before and the point after the corner.
*/
const fixCorners = function (lineData) {
const { cornerPointPositions } = extractCornerPoints(lineData);
const newLineData = [];
@@ -415,7 +380,6 @@ const fixCorners = function (lineData) {
const nextPoint = lineData[i + 1];
const cornerPoint = lineData[i];
// Find a new point on the line point 5 points back and push it to the new array
const newPrevPoint = findAdjacentPoint(prevPoint, cornerPoint, 5);
const newNextPoint = findAdjacentPoint(nextPoint, cornerPoint, 5);
@@ -466,9 +430,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
var head = endNode;
if (head.intersect && tail.intersect) {
// log.info('abc88 InsertEdge: 0.5', edge.start, '-->', edge.end, JSON.stringify(points));
points = points.slice(1, edge.points.length - 1);
// log.info('abc88 InsertEdge APA12: 0.7', edge.start, '-->', edge.end, JSON.stringify(points));
points.unshift(tail.intersect(points[0]));
log.debug(
'Last point APA12',
@@ -481,59 +443,43 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
);
points.push(head.intersect(points[points.length - 1]));
}
// log.info('abc88 InsertEdge 2 SPLIT: ', points);
if (edge.toCluster) {
log.info('to cluster abc88', clusterDb[edge.toCluster]);
points = cutPathAtIntersect(edge.points, clusterDb[edge.toCluster].node);
log.info('to cluster abc88', clusterDb.get(edge.toCluster));
points = cutPathAtIntersect(edge.points, clusterDb.get(edge.toCluster).node);
pointsHasChanged = true;
}
if (edge.fromCluster) {
log.debug('from cluster abc88', clusterDb[edge.fromCluster], JSON.stringify(points, null, 2));
points = cutPathAtIntersect(points.reverse(), clusterDb[edge.fromCluster].node).reverse();
// log.info('from cluster abc88 fixed', JSON.stringify(points, null, 2));
log.debug(
'from cluster abc88',
clusterDb.get(edge.fromCluster),
JSON.stringify(points, null, 2)
);
points = cutPathAtIntersect(points.reverse(), clusterDb.get(edge.fromCluster).node).reverse();
pointsHasChanged = true;
}
// The data for our line
let lineData = points.filter((p) => !Number.isNaN(p.y));
// const { cornerPoints, cornerPointPositions } = extractCornerPoints(lineData);
lineData = fixCorners(lineData);
let lastPoint = lineData[lineData.length - 1];
if (lineData.length > 1) {
lastPoint = lineData[lineData.length - 1];
const secondLastPoint = lineData[lineData.length - 2];
// Calculate the mid point of the last two points
const diffX = (lastPoint.x - secondLastPoint.x) / 2;
const diffY = (lastPoint.y - secondLastPoint.y) / 2;
const midPoint = { x: secondLastPoint.x + diffX, y: secondLastPoint.y + diffY };
lineData.splice(-1, 0, midPoint);
}
// This is the accessor function we talked about above
let curve = curveBasis;
// curve = curveCardinal;
// curve = curveLinear;
// curve = curveNatural;
// curve = curveCatmullRom.alpha(0.5);
// curve = curveCatmullRom;
// curve = curveCardinal.tension(0.7);
// curve = curveMonotoneY;
// let curve = interpolateToCurve([5], curveNatural, 0.01, 10);
// Currently only flowcharts get the curve from the settings, perhaps this should
// be expanded to a common setting? Restricting it for now in order not to cause side-effects that
// have not been thought through
if (edge.curve) {
curve = edge.curve;
}
const { x, y } = getLineFunctionsWithOffset(edge);
// const lineFunction = edge.curve ? line().x(x).y(y).curve(curve) : roundedCornersLine;
const lineFunction = line().x(x).y(y).curve(curve);
// Construct stroke classes based on properties
let strokeClasses;
switch (edge.thickness) {
case 'normal':
@@ -563,14 +509,11 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
}
let svgPath;
let linePath = lineFunction(lineData);
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
if (edge.look === 'handdrawn') {
const rc = rough.svg(elem);
Object.assign([], lineData);
// const svgPathNode = rc.path(lineFunction(ld.splice(0, ld.length-1)), {
// const svgPathNode = rc.path(lineFunction(ld), {
// roughness: 0.3,
// seed: handdrawnSeed,
// });
const svgPathNode = rc.path(linePath, {
roughness: 0.3,
seed: handdrawnSeed,
@@ -580,10 +523,9 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
svgPath = select(svgPathNode)
.select('path')
// .attr('d', lineFunction(lineData))
.attr('id', edge.id)
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
.attr('style', edge.style);
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
let d = svgPath.attr('d');
svgPath.attr('d', d);
elem.node().appendChild(svgPath.node());
@@ -593,7 +535,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
.attr('d', linePath)
.attr('id', edge.id)
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
.attr('style', edge.style);
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
}
// MC Special
@@ -611,18 +553,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
// .attr('cy', point.y);
// });
// lineData.forEach((point) => {
// elem
// .append('circle')
// .style('stroke', 'red')
// .style('fill', 'red')
// .attr('r', 1)
// .attr('cx', point.x)
// .attr('cy', point.y);
// });
let url = '';
// // TODO: Can we load this config only from the rendered graph type?
if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) {
url =
window.location.protocol +
@@ -630,8 +561,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
window.location.host +
window.location.pathname +
window.location.search;
url = url.replace(/\(/g, '\\(');
url = url.replace(/\)/g, '\\)');
url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)');
}
log.info('arrowTypeStart', edge.arrowTypeStart);
log.info('arrowTypeEnd', edge.arrowTypeEnd);

View File

@@ -23,8 +23,8 @@ export const labelRect = async (parent: SVGElement, node: Node) => {
const rect = shapeSvg.insert('rect', ':first-child');
// Hide the rect we are only after the label
const totalWidth = 0;
const totalHeight = 0;
const totalWidth = 0.1;
const totalHeight = 0.1;
rect.attr('width', totalWidth).attr('height', totalHeight);
shapeSvg.attr('class', 'label edgeLabel');
@@ -40,6 +40,8 @@ export const labelRect = async (parent: SVGElement, node: Node) => {
// }
updateNodeBounds(node, rect);
// node.width = 1;
// node.height = 1;
node.intersect = function (point) {
return intersect.rect(node, point);

View File

@@ -1,5 +1,5 @@
export type MarkdownWordType = 'normal' | 'strong' | 'em';
import type { MermaidConfig } from '../../dist/config.type';
export type MarkdownWordType = 'normal' | 'strong' | 'emphasis';
export interface MarkdownWord {
content: string;
type: MarkdownWordType;

View File

@@ -101,14 +101,17 @@ properties:
type: integer
default: 500
minimum: 0
elk.mergeEdges:
elk:
type: object
properties:
mergeEdges:
description: |
Elk specific option that allows egdes to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram.
Elk specific option that allows edges to share path where it convenient. It can make for pretty diagrams but can also make it harder to read the diagram.
type: boolean
default: false
elk.nodePlacement.strategy:
nodePlacementStrategy:
description: |
Elk specific option affedcting how nodes are placed.
Elk specific option affecting how nodes are placed.
type: string
enum:
- SIMPLE

View File

@@ -104,7 +104,10 @@ export class MockedD3 {
// This allows different tests to succeed -- some need a top level 'svg' and some need a 'svg' element to be the firstChild
// Real implementation returns an HTML Element
public node = vi.fn().mockImplementation(() => {
//create a top level svg element
const topElem = this._containingHTMLdoc.createElement('svg');
//@ts-ignore - this is a mock SVG element
topElem.getBBox = this.getBBox;
const elem_svgChild = this._containingHTMLdoc.createElement('svg'); // another svg element
topElem.appendChild(elem_svgChild);
return topElem;

View File

@@ -567,7 +567,7 @@ export const wrapLabel: (label: string, maxWidth: number, config: WrapLabelConfi
if (common.lineBreakRegex.test(label)) {
return label;
}
const words = label.split(' ');
const words = label.split(' ').filter(Boolean);
const completedLines: string[] = [];
let nextLine = '';
words.forEach((word, index) => {

2114
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,10 +19,12 @@ pnpm build:esbuild
pnpm build:types
# Clone the Mermaid Live Editor repository
rm -rf mermaid-live-editor
if [ ! -d "mermaid-live-editor" ]; then
git clone --single-branch https://github.com/mermaid-js/mermaid-live-editor.git
fi
cd mermaid-live-editor
git clean -xdf
rm -rf docs/
# We have to use npm instead of yarn because it causes trouble in netlify
# Install dependencies

View File

@@ -25,6 +25,7 @@ const formatBytes = (bytes: number): string => {
if (bytes == 0) {
return '0 Bytes';
}
bytes = Math.abs(bytes);
const base = 1024;
const decimals = 2;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

View File

@@ -9,7 +9,7 @@ export default defineConfig({
extensions: ['.js'],
alias: {
// Define your alias here
'$root/*': path.resolve(__dirname, 'src/*'),
$root: path.resolve(__dirname, 'packages/mermaid/src'),
},
},
plugins: [