diff --git a/.cspell/cspell.config.yaml b/.cspell/cspell.config.yaml index 33d690193..b16040c8c 100644 --- a/.cspell/cspell.config.yaml +++ b/.cspell/cspell.config.yaml @@ -28,6 +28,9 @@ dictionaryDefinitions: - name: suggestions words: - none + - disp + - subproc + - tria suggestWords: - seperator:separator - vertice:vertex diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index ef170fb71..f26bf4ab8 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -2,6 +2,8 @@ name: autofix.ci # needed to securely identify the workflow on: pull_request: + branches-ignore: + - 'renovate/**' permissions: contents: read diff --git a/cypress/integration/rendering/newShapes.spec.ts b/cypress/integration/rendering/newShapes.spec.ts new file mode 100644 index 000000000..45e51a09b --- /dev/null +++ b/cypress/integration/rendering/newShapes.spec.ts @@ -0,0 +1,135 @@ +import { imgSnapshotTest } from '../../helpers/util.ts'; + +const looks = ['classic', 'handDrawn'] as const; +const directions = ['TB', 'BT', 'LR', 'RL'] as const; +const newShapesSet1 = [ + 'triangle', + 'slopedRect', + 'tiltedCylinder', + 'flippedTriangle', + 'hourglass', +] as const; +const newShapesSet2 = [ + 'taggedRect', + 'multiRect', + 'lightningBolt', + 'filledCircle', + 'windowPane', +] as const; + +const newShapesSet3 = [ + 'halfRoundedRectangle', + 'curvedTrapezoid', + 'bowTieRect', + 'dividedRect', + 'crossedCircle', +] as const; + +const newShapesSet4 = [ + 'waveRectangle', + 'trapezoidalPentagon', + 'linedCylinder', + 'waveEdgedRectangle', + 'multiWaveEdgedRectangle', +] as const; + +const newShapesSet5 = [ + 'linedWaveEdgedRect', + 'taggedWaveEdgedRectangle', + 'text', + 'card', + 'shadedProcess', +] as const; + +// Aggregate all shape sets into a single array +const newShapesSets = [ + newShapesSet1, + newShapesSet2, + newShapesSet3, + newShapesSet4, + newShapesSet5, +] as const; + +looks.forEach((look) => { + directions.forEach((direction) => { + newShapesSets.forEach((newShapesSet) => { + describe(`Test ${newShapesSet.join(', ')} in ${look} look and dir ${direction}`, () => { + it(`without label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape} }@\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a label' }@\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`connect all shapes with each other`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index}${index}@{ shape: ${newShape}, label: 'This is a label' }@\n`; + }); + for (let i = 0; i < newShapesSet.length; i++) { + for (let j = i + 1; j < newShapesSet.length; j++) { + flowchartCode += ` n${i}${i} --> n${j}${j}\n`; + } + } + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with very long label`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is a very very very very very long long long label' }@\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with markdown htmlLabels:true`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold**
and strong' }@\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with markdown htmlLabels:false`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'This is **bold**
and strong' }@\n`; + }); + imgSnapshotTest(flowchartCode, { + look, + htmlLabels: false, + flowchart: { htmlLabels: false }, + }); + }); + + it(`with styles`, () => { + let flowchartCode = `flowchart ${direction}\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new shape' }@\n`; + flowchartCode += ` style n${index}${index} fill:#f9f,stroke:#333,stroke-width:4px \n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + + it(`with classDef`, () => { + let flowchartCode = `flowchart ${direction}\n`; + flowchartCode += ` classDef customClazz fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n`; + newShapesSet.forEach((newShape, index) => { + flowchartCode += ` n${index} --> n${index}${index}@{ shape: ${newShape}, label: 'new shape' }@\n`; + flowchartCode += ` n${index}${index}:::customClazz\n`; + }); + imgSnapshotTest(flowchartCode, { look }); + }); + }); + }); + }); +}); diff --git a/cypress/platform/ash.html b/cypress/platform/ash.html index b15733d52..6d306390b 100644 --- a/cypress/platform/ash.html +++ b/cypress/platform/ash.html @@ -36,12 +36,15 @@ font-family: 'Arial'; /* font-size: 18px !important; */ } + h1 { color: grey; } + .mermaid2 { display: none; } + .mermaid svg { /* font-size: 18px !important; */ @@ -54,6 +57,7 @@ 10px 10px; background-repeat: repeat; */ } + .malware { position: fixed; bottom: 0; @@ -69,11 +73,13 @@ font-family: monospace; font-size: 72px; } + /* tspan { font-size: 6px !important; } */ +
 flowchart
@@ -171,7 +177,7 @@ flowchart LR
 
     
+  
+
diff --git a/cypress/platform/state-refactor-looks.html b/cypress/platform/state-refactor-looks.html
index 200739668..1761d665a 100644
--- a/cypress/platform/state-refactor-looks.html
+++ b/cypress/platform/state-refactor-looks.html
@@ -1532,7 +1532,7 @@ direction LR
 
     
+```
+
+## Supported layouts
+
+- `elk`: The default layout, which is `elk.layered`.
+- `elk.layered`: Layered layout
+- `elk.stress`: Stress layout
+- `elk.force`: Force layout
+- `elk.mrtree`: Multi-root tree layout
+- `elk.sporeOverlap`: Spore overlap layout
+
+
diff --git a/packages/mermaid-layout-elk/src/layouts.ts b/packages/mermaid-layout-elk/src/layouts.ts
index 81b7263d2..3f412464a 100644
--- a/packages/mermaid-layout-elk/src/layouts.ts
+++ b/packages/mermaid-layout-elk/src/layouts.ts
@@ -3,7 +3,7 @@ import type { LayoutLoaderDefinition } from '@mermaid-chart/mermaid';
 const loader = async () => await import(`./render.js`);
 const algos = ['elk.stress', 'elk.force', 'elk.mrtree', 'elk.sporeOverlap'];
 
-export const layouts: LayoutLoaderDefinition[] = [
+const layouts: LayoutLoaderDefinition[] = [
   {
     name: 'elk',
     loader,
@@ -15,3 +15,5 @@ export const layouts: LayoutLoaderDefinition[] = [
     algorithm: algo,
   })),
 ];
+
+export default layouts;
diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts
index 0fa897d11..97f3e0bb1 100644
--- a/packages/mermaid/src/defaultConfig.ts
+++ b/packages/mermaid/src/defaultConfig.ts
@@ -248,19 +248,6 @@ const config: RequiredDeep = {
     ...defaultConfigJson.requirement,
     useWidth: undefined,
   },
-  gitGraph: {
-    ...defaultConfigJson.gitGraph,
-    // TODO: This is a temporary override for `gitGraph`, since every other
-    //       diagram does have `useMaxWidth`, but instead sets it to `true`.
-    //       Should we set this to `true` instead?
-    useMaxWidth: false,
-  },
-  sankey: {
-    ...defaultConfigJson.sankey,
-    // this is false, unlike every other diagram (other than gitGraph)
-    // TODO: can we make this default to `true` instead?
-    useMaxWidth: false,
-  },
   packet: {
     ...defaultConfigJson.packet,
   },
diff --git a/packages/mermaid/src/diagrams/flowchart/elk/detector.ts b/packages/mermaid/src/diagrams/flowchart/elk/detector.ts
index b476ff11b..6688ffd8c 100644
--- a/packages/mermaid/src/diagrams/flowchart/elk/detector.ts
+++ b/packages/mermaid/src/diagrams/flowchart/elk/detector.ts
@@ -1,34 +1,26 @@
 import type {
-  ExternalDiagramDefinition,
   DiagramDetector,
   DiagramLoader,
+  ExternalDiagramDefinition,
 } from '../../../diagram-api/types.js';
-import { log } from '../../../logger.js';
 
 const id = 'flowchart-elk';
 
-const detector: DiagramDetector = (txt, config): boolean => {
+const detector: DiagramDetector = (txt, config = {}): boolean => {
   if (
     // If diagram explicitly states flowchart-elk
     /^\s*flowchart-elk/.test(txt) ||
     // If a flowchart/graph diagram has their default renderer set to elk
     (/^\s*flowchart|graph/.test(txt) && config?.flowchart?.defaultRenderer === 'elk')
   ) {
-    // This will log at the end, hopefully.
-    setTimeout(
-      () =>
-        log.warn(
-          'flowchart-elk was moved to an external package in Mermaid v11. Please refer [release notes](link) for more details. This diagram will be rendered using `dagre` layout as a fallback.'
-        ),
-      500
-    );
+    config.layout = 'elk';
     return true;
   }
   return false;
 };
 
 const loader: DiagramLoader = async () => {
-  const { diagram } = await import('../flowDiagram-v2.js');
+  const { diagram } = await import('../flowDiagram.js');
   return { id, diagram };
 };
 
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts
index c8728423a..add5211e0 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts
+++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts
@@ -4,6 +4,7 @@ import { getConfig, defaultConfig } from '../../diagram-api/diagramAPI.js';
 import common from '../common/common.js';
 import type { Node, Edge } from '../../rendering-util/types.js';
 import { log } from '../../logger.js';
+import * as yaml from 'js-yaml';
 import {
   setAccTitle,
   getAccTitle,
@@ -60,8 +61,10 @@ export const addVertex = function (
   style: string[],
   classes: string[],
   dir: string,
-  props = {}
+  props = {},
+  shapeData: any
 ) {
+  // console.log('addVertex', id, shapeData);
   if (!id || id.trim().length === 0) {
     return;
   }
@@ -115,6 +118,32 @@ export const addVertex = function (
   } else if (props !== undefined) {
     Object.assign(vertex.props, props);
   }
+
+  if (shapeData !== undefined) {
+    let yamlData;
+    // detect if shapeData contains a newline character
+
+    if (!shapeData.includes('\n')) {
+      // console.log('yamlData shapeData has no new lines', shapeData);
+      yamlData = '{\n' + shapeData + '\n';
+    } else {
+      // console.log('yamlData shapeData has new lines', shapeData);
+      yamlData = shapeData + '\n';
+      // Find the position of the last } and replace it with a newline
+      const lastPos = yamlData.lastIndexOf('}');
+      if (lastPos !== -1) {
+        yamlData = yamlData.substring(0, lastPos) + '\n';
+      }
+    }
+    const doc = yaml.load(yamlData, { schema: yaml.JSON_SCHEMA });
+    // console.log('yamlData doc', doc);
+    if (doc?.shape) {
+      vertex.type = doc?.shape;
+    }
+    if (doc?.label) {
+      vertex.text = doc?.label;
+    }
+  }
 };
 
 /**
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts b/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts
index dda5a67f0..b66afe4bf 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts
+++ b/packages/mermaid/src/diagrams/flowchart/flowDetector-v2.ts
@@ -1,5 +1,8 @@
-import type { DiagramDetector, DiagramLoader } from '../../diagram-api/types.js';
-import type { ExternalDiagramDefinition } from '../../diagram-api/types.js';
+import type {
+  DiagramDetector,
+  DiagramLoader,
+  ExternalDiagramDefinition,
+} from '../../diagram-api/types.js';
 
 const id = 'flowchart-v2';
 
@@ -19,7 +22,7 @@ const detector: DiagramDetector = (txt, config) => {
 };
 
 const loader: DiagramLoader = async () => {
-  const { diagram } = await import('./flowDiagram-v2.js');
+  const { diagram } = await import('./flowDiagram.js');
   return { id, diagram };
 };
 
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts b/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts
deleted file mode 100644
index 5b8012ede..000000000
--- a/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-// @ts-ignore: JISON doesn't support types
-import flowParser from './parser/flow.jison';
-import flowDb from './flowDb.js';
-import renderer from './flowRenderer-v3-unified.js';
-import flowStyles from './styles.js';
-import type { MermaidConfig } from '../../config.type.js';
-import { setConfig } from '../../diagram-api/diagramAPI.js';
-
-export const diagram = {
-  parser: flowParser,
-  db: flowDb,
-  renderer,
-  styles: flowStyles,
-  init: (cnf: MermaidConfig) => {
-    if (!cnf.flowchart) {
-      cnf.flowchart = {};
-    }
-    cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
-    setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
-    flowDb.clear();
-    flowDb.setGen('gen-2');
-  },
-};
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDiagram.ts b/packages/mermaid/src/diagrams/flowchart/flowDiagram.ts
index 5b8012ede..67cdf918f 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowDiagram.ts
+++ b/packages/mermaid/src/diagrams/flowchart/flowDiagram.ts
@@ -1,10 +1,10 @@
-// @ts-ignore: JISON doesn't support types
-import flowParser from './parser/flow.jison';
-import flowDb from './flowDb.js';
-import renderer from './flowRenderer-v3-unified.js';
-import flowStyles from './styles.js';
 import type { MermaidConfig } from '../../config.type.js';
 import { setConfig } from '../../diagram-api/diagramAPI.js';
+import flowDb from './flowDb.js';
+import renderer from './flowRenderer-v3-unified.js';
+// @ts-ignore: JISON doesn't support types
+import flowParser from './parser/flow.jison';
+import flowStyles from './styles.js';
 
 export const diagram = {
   parser: flowParser,
@@ -15,6 +15,9 @@ export const diagram = {
     if (!cnf.flowchart) {
       cnf.flowchart = {};
     }
+    if (cnf.layout) {
+      setConfig({ layout: cnf.layout });
+    }
     cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
     setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
     flowDb.clear();
diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts
index 102662ee6..5d52b64e8 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts
+++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts
@@ -3,7 +3,7 @@ import { getConfig } from '../../diagram-api/diagramAPI.js';
 import type { DiagramStyleClassDef } from '../../diagram-api/types.js';
 import { log } from '../../logger.js';
 import { getDiagramElements } from '../../rendering-util/insertElementsForSize.js';
-import { render } from '../../rendering-util/render.js';
+import { getRegisteredLayoutAlgorithm, render } from '../../rendering-util/render.js';
 import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
 import type { LayoutData } from '../../rendering-util/types.js';
 import utils from '../../utils.js';
@@ -40,7 +40,12 @@ export const draw = async function (text: string, id: string, _version: string,
   const direction = getDirection();
 
   data4Layout.type = diag.type;
-  data4Layout.layoutAlgorithm = layout;
+  data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout);
+  if (data4Layout.layoutAlgorithm === 'dagre' && layout === 'elk') {
+    log.warn(
+      'flowchart-elk was moved to an external package in Mermaid v11. Please refer [release notes](https://github.com/mermaid-js/mermaid/releases/tag/v11.0.0) for more details. This diagram will be rendered using `dagre` layout as a fallback.'
+    );
+  }
   data4Layout.direction = direction;
   data4Layout.nodeSpacing = conf?.nodeSpacing || 50;
   data4Layout.rankSpacing = conf?.rankSpacing || 50;
diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js
new file mode 100644
index 000000000..ee461de9b
--- /dev/null
+++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-node-data.spec.js
@@ -0,0 +1,199 @@
+import flowDb from '../flowDb.js';
+import flow from './flow.jison';
+import { setConfig } from '../../../config.js';
+
+setConfig({
+  securityLevel: 'strict',
+});
+
+describe('when parsing directions', function () {
+  beforeEach(function () {
+    flow.parser.yy = flowDb;
+    flow.parser.yy.clear();
+    flow.parser.yy.setGen('gen-2');
+  });
+
+  it('should handle basic shape data statements', function () {
+    const res = flow.parser.parse(`flowchart TB
+      D@{ shape: rounded }@`);
+
+    const data4Layout = flow.parser.yy.getData();
+    expect(data4Layout.nodes.length).toBe(1);
+    expect(data4Layout.nodes[0].shape).toEqual('rounded');
+    expect(data4Layout.nodes[0].label).toEqual('D');
+  });
+
+  it('should no matter of there are no leading spaces', function () {
+    const res = flow.parser.parse(`flowchart TB
+      D@{shape: rounded }@`);
+
+    const data4Layout = flow.parser.yy.getData();
+
+    expect(data4Layout.nodes.length).toBe(1);
+    expect(data4Layout.nodes[0].shape).toEqual('rounded');
+    expect(data4Layout.nodes[0].label).toEqual('D');
+  });
+
+  it('should no matter of there are many leading spaces', function () {
+    const res = flow.parser.parse(`flowchart TB
+      D@{       shape: rounded }@`);
+
+    const data4Layout = flow.parser.yy.getData();
+
+    expect(data4Layout.nodes.length).toBe(1);
+    expect(data4Layout.nodes[0].shape).toEqual('rounded');
+    expect(data4Layout.nodes[0].label).toEqual('D');
+  });
+
+  it('should be forgiving with many spaces before teh end', function () {
+    const res = flow.parser.parse(`flowchart TB
+      D@{ shape: rounded          }@`);
+
+    const data4Layout = flow.parser.yy.getData();
+
+    expect(data4Layout.nodes.length).toBe(1);
+    expect(data4Layout.nodes[0].shape).toEqual('rounded');
+    expect(data4Layout.nodes[0].label).toEqual('D');
+  });
+  it('should be possible to add multiple properties on the same line', function () {
+    const res = flow.parser.parse(`flowchart TB
+      D@{ shape: rounded , label: "DD" }@`);
+
+    const data4Layout = flow.parser.yy.getData();
+
+    expect(data4Layout.nodes.length).toBe(1);
+    expect(data4Layout.nodes[0].shape).toEqual('rounded');
+    expect(data4Layout.nodes[0].label).toEqual('DD');
+  });
+  it('should be possible to link to a node with more data', function () {
+    const res = flow.parser.parse(`flowchart TB
+      A --> D@{
+        shape: circle
+        icon: "clock"
+      }@
+
+      `);
+
+    const data4Layout = flow.parser.yy.getData();
+    expect(data4Layout.nodes.length).toBe(2);
+    expect(data4Layout.nodes[0].shape).toEqual('squareRect');
+    expect(data4Layout.nodes[0].label).toEqual('A');
+    expect(data4Layout.nodes[1].label).toEqual('D');
+    expect(data4Layout.nodes[1].shape).toEqual('circle');
+
+    expect(data4Layout.edges.length).toBe(1);
+  });
+  it('should not disturb adding multiple nodes after each other', function () {
+    const res = flow.parser.parse(`flowchart TB
+      A[hello]
+      B@{
+        shape: circle
+        icon: "clock"
+      }@
+      C[Hello]@{
+        shape: circle
+        icon: "clock"
+      }@
+      `);
+
+    const data4Layout = flow.parser.yy.getData();
+    expect(data4Layout.nodes.length).toBe(3);
+    expect(data4Layout.nodes[0].shape).toEqual('squareRect');
+    expect(data4Layout.nodes[0].label).toEqual('hello');
+    expect(data4Layout.nodes[1].shape).toEqual('circle');
+    expect(data4Layout.nodes[1].label).toEqual('B');
+    expect(data4Layout.nodes[2].shape).toEqual('circle');
+    expect(data4Layout.nodes[2].label).toEqual('Hello');
+  });
+  it('should use handle bracket end (}) character inside the shape data', function () {
+    const res = flow.parser.parse(`flowchart TB
+      A@{
+        label: "This is }"
+        icon: "clock"
+      }@
+      `);
+
+    const data4Layout = flow.parser.yy.getData();
+    expect(data4Layout.nodes.length).toBe(1);
+    expect(data4Layout.nodes[0].shape).toEqual('squareRect');
+    expect(data4Layout.nodes[0].label).toEqual('This is }');
+  });
+  it('Diamond shapes should work as usual', function () {
+    const res = flow.parser.parse(`flowchart TB
+      A{This is a label}
+`);
+
+    const data4Layout = flow.parser.yy.getData();
+    expect(data4Layout.nodes.length).toBe(1);
+    expect(data4Layout.nodes[0].shape).toEqual('diamond');
+    expect(data4Layout.nodes[0].label).toEqual('This is a label');
+  });
+  it('Multi line strings should be supported', function () {
+    const res = flow.parser.parse(`flowchart TB
+      A@{
+        label: |
+          This is a
+          multiline string
+        icon: "clock"
+      }@
+      `);
+
+    const data4Layout = flow.parser.yy.getData();
+    expect(data4Layout.nodes.length).toBe(1);
+    expect(data4Layout.nodes[0].shape).toEqual('squareRect');
+    expect(data4Layout.nodes[0].label).toEqual('This is a\nmultiline string\n');
+  });
+  it('Multi line strings should be supported', function () {
+    const res = flow.parser.parse(`flowchart TB
+      A@{
+        label: "This is a
+          multiline string"
+        icon: "clock"
+      }@
+      `);
+
+    const data4Layout = flow.parser.yy.getData();
+    expect(data4Layout.nodes.length).toBe(1);
+    expect(data4Layout.nodes[0].shape).toEqual('squareRect');
+    expect(data4Layout.nodes[0].label).toEqual('This is a
multiline string'); + }); + it(' should be possible to use } in strings', function () { + const res = flow.parser.parse(`flowchart TB + A@{ + label: "This is a string with }" + icon: "clock" + }@ + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('This is a string with }'); + }); + it(' should be possible to use @ in strings', function () { + const res = flow.parser.parse(`flowchart TB + A@{ + label: "This is a string with @" + icon: "clock" + }@ + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('This is a string with @'); + }); + it(' should be possible to use @ in strings', function () { + const res = flow.parser.parse(`flowchart TB + A@{ + label: "This is a string with }@" + icon: "clock" + }@ + `); + + const data4Layout = flow.parser.yy.getData(); + expect(data4Layout.nodes.length).toBe(1); + expect(data4Layout.nodes[0].shape).toEqual('squareRect'); + expect(data4Layout.nodes[0].label).toEqual('This is a string with }@'); + }); +}); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index cf70d6424..977613f0e 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -23,6 +23,9 @@ %x href %x callbackname %x callbackargs +%x shapeData +%x shapeDataStr +%x shapeDataEndBracket %% accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; } @@ -34,6 +37,24 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multilin [^\}]* return "acc_descr_multiline_value"; // .*[^\n]* { return "acc_descr_line"} + +\@\{ { this.pushState("shapeData"); yytext=""; return 'SHAPE_DATA' } +["] { + this.pushState("shapeDataStr"); + return 'SHAPE_DATA'; + } +["] { this.popState(); return 'SHAPE_DATA'} +[^\"]+ { + const re = /\n\s*/g; + yytext = yytext.replace(re,"
"); + return 'SHAPE_DATA'} +[^@^"]+ { + return 'SHAPE_DATA'; + } +\@+ { + this.popState(); + } + /* ---interactivity command--- 'call' adds a callback to the specified node. 'call' can only be specified when @@ -49,10 +70,11 @@ Function arguments are optional: 'call ()' simply executes 'callba \) this.popState(); [^)]* return 'CALLBACKARGS'; + [^`"]+ { return "MD_STR";} [`]["] { this.popState();} <*>["][`] { this.begin("md_string");} -[^"]+ return "STR"; +[^"]+ { return "STR"; } ["] this.popState(); <*>["] this.pushState("string"); "style" return 'STYLE'; @@ -62,6 +84,8 @@ Function arguments are optional: 'call ()' simply executes 'callba "classDef" return 'CLASSDEF'; "class" return 'CLASS'; + + /* ---interactivity command--- 'href' adds a link to the specified node. 'href' can only be specified when the @@ -356,23 +380,36 @@ statement separator: NEWLINE | SEMI | EOF ; +shapeData: + shapeData SHAPE_DATA + { $$ = $1 + $2; } + | SHAPE_DATA + { $$ = $1; } + ; -vertexStatement: vertexStatement link node - { /* console.warn('vs',$vertexStatement.stmt,$node); */ yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } } +vertexStatement: vertexStatement link node shapeData + { /* console.warn('vs shapeData',$vertexStatement.stmt,$node, $shapeData);*/ yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } } + | vertexStatement link node + { /*console.warn('vs',$vertexStatement.stmt,$node);*/ yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } } | vertexStatement link node spaceList { /* console.warn('vs',$vertexStatement.stmt,$node); */ yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } } - |node spaceList {/*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }} - |node { $$ = {stmt: $node, nodes:$node }} + |node spaceList { /*console.warn('vertexStatement: node spaceList', $node);*/ $$ = {stmt: $node, nodes:$node }} + |node shapeData { + /*console.warn('vertexStatement: node shapeData', $node[0], $shapeData);*/ + yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); + $$ = {stmt: $node, nodes:$node, shapeData: $shapeData} + } + |node { /* console.warn('vertexStatement: single node', $node); */ $$ = {stmt: $node, nodes:$node }} ; node: styledVertex - { /* console.warn('nod', $styledVertex); */ $$ = [$styledVertex];} + { /*console.warn('nod', $styledVertex);*/ $$ = [$styledVertex];} | node spaceList AMP spaceList styledVertex { $$ = $node.concat($styledVertex); /* console.warn('pip', $node[0], $styledVertex, $$); */ } ; styledVertex: vertex - { /* console.warn('nod', $vertex); */ $$ = $vertex;} + { /* console.warn('nodc', $vertex);*/ $$ = $vertex;} | vertex STYLE_SEPARATOR idString {$$ = $vertex;yy.setClass($vertex,$idString)} ; diff --git a/packages/mermaid/src/diagrams/flowchart/styles.ts b/packages/mermaid/src/diagrams/flowchart/styles.ts index 0ca2c1321..2c0feb5bf 100644 --- a/packages/mermaid/src/diagrams/flowchart/styles.ts +++ b/packages/mermaid/src/diagrams/flowchart/styles.ts @@ -75,13 +75,20 @@ const getStyles = (options: FlowChartStyleOptions) => stroke-width: 1px; } - .node .label { + .rough-node .label,.node .label { text-align: center; } .node.clickable { cursor: pointer; } + + .root .anchor path { + fill: ${options.lineColor} !important; + stroke-width: 0; + stroke: ${options.lineColor}; + } + .arrowheadPath { fill: ${options.arrowheadColor}; } @@ -151,6 +158,11 @@ const getStyles = (options: FlowChartStyleOptions) => font-size: 18px; fill: ${options.textColor}; } + + rect.text { + fill: none; + stroke-width: 0; + } `; export default getStyles; diff --git a/packages/mermaid/src/diagrams/requirement/requirementRenderer.js b/packages/mermaid/src/diagrams/requirement/requirementRenderer.js index 6b5143752..778dc36b1 100644 --- a/packages/mermaid/src/diagrams/requirement/requirementRenderer.js +++ b/packages/mermaid/src/diagrams/requirement/requirementRenderer.js @@ -1,11 +1,11 @@ import { line, select } from 'd3'; import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js'; import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; +import { getConfig } from '../../diagram-api/diagramAPI.js'; import { log } from '../../logger.js'; import { configureSvgSize } from '../../setupGraphViewbox.js'; import common from '../common/common.js'; import markers from './requirementMarkers.js'; -import { getConfig } from '../../diagram-api/diagramAPI.js'; let conf = {}; let relCnt = 0; diff --git a/packages/mermaid/src/diagrams/state/dataFetcher.js b/packages/mermaid/src/diagrams/state/dataFetcher.js index 35cde69ab..921544ff2 100644 --- a/packages/mermaid/src/diagrams/state/dataFetcher.js +++ b/packages/mermaid/src/diagrams/state/dataFetcher.js @@ -166,43 +166,11 @@ function insertOrUpdateNode(nodes, nodeData, classes) { * @returns {string} */ function getClassesFromDbInfo(dbInfoItem) { - if (dbInfoItem === undefined || dbInfoItem === null) { - return ''; - } else { - if (dbInfoItem.classes) { - let classStr = ''; - // for each class in classes, add it to the string as comma separated - for (let i = 0; i < dbInfoItem.classes.length; i++) { - //do not add comma for the last class - if (i === dbInfoItem.classes.length - 1) { - classStr += dbInfoItem.classes[i]; - } - //add comma for all other classes - else { - classStr += dbInfoItem.classes[i] + ' '; - } - } - return classStr; - } else { - return ''; - } - } + return dbInfoItem?.classes?.join(' ') ?? ''; } -/** - * 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 - */ + function getStylesFromDbInfo(dbInfoItem) { - if (dbInfoItem === undefined || dbInfoItem === null) { - return; - } else { - if (dbInfoItem.styles) { - return dbInfoItem.styles; - } else { - return []; - } - } + return dbInfoItem?.styles ?? []; } export const dataFetcher = ( @@ -224,10 +192,10 @@ export const dataFetcher = ( if (itemId !== 'root') { let shape = SHAPE_STATE; + // The if === true / false can be removed if we can guarantee that the parsedItem.start is always a boolean if (parsedItem.start === true) { shape = SHAPE_START; - } - if (parsedItem.start === false) { + } else if (parsedItem.start === false) { shape = SHAPE_END; } if (parsedItem.type !== DEFAULT_STATE_TYPE) { diff --git a/packages/mermaid/src/diagrams/timeline/parser/timeline.jison b/packages/mermaid/src/diagrams/timeline/parser/timeline.jison index 348c31fad..89bfd06f4 100644 --- a/packages/mermaid/src/diagrams/timeline/parser/timeline.jison +++ b/packages/mermaid/src/diagrams/timeline/parser/timeline.jison @@ -18,7 +18,7 @@ \#[^\n]* /* skip comments */ "timeline" return 'timeline'; -"title"\s[^#\n;]+ return 'title'; +"title"\s[^\n]+ return 'title'; accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; } (?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; } accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; } @@ -26,11 +26,11 @@ accDescr\s*":"\s* { this.begin("ac accDescr\s*"{"\s* { this.begin("acc_descr_multiline");} [\}] { this.popState(); } [^\}]* return "acc_descr_multiline_value"; -"section"\s[^#:\n;]+ return 'section'; +"section"\s[^:\n]+ return 'section'; // event starting with "==>" keyword -":"\s[^#:\n;]+ return 'event'; -[^#:\n;]+ return 'period'; +":"\s[^:\n]+ return 'event'; +[^#:\n]+ return 'period'; <> return 'EOF'; diff --git a/packages/mermaid/src/diagrams/timeline/timeline.spec.js b/packages/mermaid/src/diagrams/timeline/timeline.spec.js index 69b9df1ba..a7005cada 100644 --- a/packages/mermaid/src/diagrams/timeline/timeline.spec.js +++ b/packages/mermaid/src/diagrams/timeline/timeline.spec.js @@ -1,6 +1,7 @@ +import { setLogLevel } from '../../diagram-api/diagramAPI.js'; +import * as commonDb from '../common/commonDb.js'; import { parser as timeline } from './parser/timeline.jison'; import * as timelineDB from './timelineDb.js'; -import { setLogLevel } from '../../diagram-api/diagramAPI.js'; describe('when parsing a timeline ', function () { beforeEach(function () { @@ -9,7 +10,7 @@ describe('when parsing a timeline ', function () { setLogLevel('trace'); }); describe('Timeline', function () { - it('TL-1 should handle a simple section definition abc-123', function () { + it('should handle a simple section definition abc-123', function () { let str = `timeline section abc-123`; @@ -17,7 +18,7 @@ describe('when parsing a timeline ', function () { expect(timelineDB.getSections()).to.deep.equal(['abc-123']); }); - it('TL-2 should handle a simple section and only two tasks', function () { + it('should handle a simple section and only two tasks', function () { let str = `timeline section abc-123 task1 @@ -29,7 +30,7 @@ describe('when parsing a timeline ', function () { }); }); - it('TL-3 should handle a two section and two coressponding tasks', function () { + it('should handle a two section and two coressponding tasks', function () { let str = `timeline section abc-123 task1 @@ -50,7 +51,7 @@ describe('when parsing a timeline ', function () { }); }); - it('TL-4 should handle a section, and task and its events', function () { + it('should handle a section, and task and its events', function () { let str = `timeline section abc-123 task1: event1 @@ -74,7 +75,7 @@ describe('when parsing a timeline ', function () { }); }); - it('TL-5 should handle a section, and task and its multi line events', function () { + it('should handle a section, and task and its multi line events', function () { let str = `timeline section abc-123 task1: event1 @@ -98,5 +99,42 @@ describe('when parsing a timeline ', function () { } }); }); + + it('should handle a title, section, task, and events with semicolons', function () { + let str = `timeline + title ;my;title; + section ;a;bc-123; + ;ta;sk1;: ;ev;ent1; : ;ev;ent2; : ;ev;ent3; + `; + timeline.parse(str); + expect(commonDb.getDiagramTitle()).equal(';my;title;'); + expect(timelineDB.getSections()).to.deep.equal([';a;bc-123;']); + expect(timelineDB.getTasks()[0].events).toMatchInlineSnapshot(` + [ + ";ev;ent1; ", + ";ev;ent2; ", + ";ev;ent3;", + ] + `); + }); + + it('should handle a title, section, task, and events with hashtags', function () { + let str = `timeline + title #my#title# + section #a#bc-123# + task1: #ev#ent1# : #ev#ent2# : #ev#ent3# + `; + timeline.parse(str); + expect(commonDb.getDiagramTitle()).equal('#my#title#'); + expect(timelineDB.getSections()).to.deep.equal(['#a#bc-123#']); + expect(timelineDB.getTasks()[0].task).equal('task1'); + expect(timelineDB.getTasks()[0].events).toMatchInlineSnapshot(` + [ + "#ev#ent1# ", + "#ev#ent2# ", + "#ev#ent3#", + ] + `); + }); }); }); diff --git a/packages/mermaid/src/docs/ecosystem/integrations-community.md b/packages/mermaid/src/docs/ecosystem/integrations-community.md index d77a82b44..2d5972f20 100644 --- a/packages/mermaid/src/docs/ecosystem/integrations-community.md +++ b/packages/mermaid/src/docs/ecosystem/integrations-community.md @@ -51,6 +51,7 @@ To add an integration to this list, see the [Integrations - create page](./integ - [SVG diagram generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator) - [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) ✅ - [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid) +- [MonsterWriter](https://www.monsterwriter.com/) ✅ - [Joplin](https://joplinapp.org) ✅ - [LiveBook](https://livebook.dev) ✅ - [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) ✅ diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index 7fd5dc0e4..c83e0676f 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -25,6 +25,7 @@ import { preprocessDiagram } from './preprocess.js'; import { decodeEntities } from './utils.js'; import { toBase64 } from './utils/base64.js'; import type { D3Element, ParseOptions, RenderResult } from './types.js'; +import assignWithDepth from './assignWithDepth.js'; const MAX_TEXTLENGTH = 50_000; const MAX_TEXTLENGTH_EXCEEDED_MSG = @@ -474,9 +475,10 @@ const render = async function ( }; /** - * @param options - Initial Mermaid options + * @param userOptions - Initial Mermaid options */ -function initialize(options: MermaidConfig = {}) { +function initialize(userOptions: MermaidConfig = {}) { + const options = assignWithDepth({}, userOptions); // Handle legacy location of font-family configuration if (options?.fontFamily && !options.themeVariables?.fontFamily) { if (!options.themeVariables) { diff --git a/packages/mermaid/src/rendering-util/render.ts b/packages/mermaid/src/rendering-util/render.ts index 8e4572801..71bb5ca06 100644 --- a/packages/mermaid/src/rendering-util/render.ts +++ b/packages/mermaid/src/rendering-util/render.ts @@ -1,3 +1,5 @@ +import { log } from '$root/logger.js'; + export interface LayoutAlgorithm { render(data4Layout: any, svg: any, element: any, algorithm?: string, positions: any): any; } @@ -28,10 +30,6 @@ const registerDefaultLayoutLoaders = () => { name: 'fixed', loader: async () => await import('./layout-algorithms/fixed/index.js'), }, - // { - // name: 'elk', - // loader: async () => await import('../../../mermaid-layout-elk/src/render.js'), - // }, ]); }; @@ -73,3 +71,17 @@ export const render = async (data4Layout: any, svg: any, element: any, positions } return layoutRenderer.render(data4Layout, svg, element, layoutDefinition.algorithm, positions); }; + +/** + * Get the registered layout algorithm. If the algorithm is not registered, use the fallback algorithm. + */ +export const getRegisteredLayoutAlgorithm = (algorithm = '', { fallback = 'dagre' } = {}) => { + if (algorithm in layoutAlgorithms) { + return algorithm; + } + if (fallback in layoutAlgorithms) { + log.warn(`Layout algorithm ${algorithm} is not registered. Using ${fallback} as fallback.`); + return fallback; + } + throw new Error(`Both layout algorithms ${algorithm} and ${fallback} are not registered.`); +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js index 07be3d8f2..18750f038 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js @@ -17,35 +17,203 @@ import { doublecircle } from './shapes/doubleCircle.js'; import { rect_left_inv_arrow } from './shapes/rectLeftInvArrow.js'; import { question } from './shapes/question.js'; import { hexagon } from './shapes/hexagon.js'; +import { text } from './shapes/text.js'; +import { card } from './shapes/card.js'; +import { shadedProcess } from './shapes/shadedProcess.js'; +import { anchor } from './shapes/anchor.js'; import { lean_right } from './shapes/leanRight.js'; import { lean_left } from './shapes/leanLeft.js'; import { trapezoid } from './shapes/trapezoid.js'; import { inv_trapezoid } from './shapes/invertedTrapezoid.js'; import { labelRect } from './shapes/labelRect.js'; +import { triangle } from './shapes/triangle.js'; +import { halfRoundedRectangle } from './shapes/halfRoundedRectangle.js'; +import { curvedTrapezoid } from './shapes/curvedTrapezoid.js'; +import { slopedRect } from './shapes/slopedRect.js'; +import { bowTieRect } from './shapes/bowTieRect.js'; +import { dividedRectangle } from './shapes/dividedRect.js'; +import { crossedCircle } from './shapes/crossedCircle.js'; +import { waveRectangle } from './shapes/waveRectangle.js'; +import { tiltedCylinder } from './shapes/tiltedCylinder.js'; +import { trapezoidalPentagon } from './shapes/trapezoidalPentagon.js'; +import { flippedTriangle } from './shapes/flippedTriangle.js'; +import { hourglass } from './shapes/hourglass.js'; +import { taggedRect } from './shapes/taggedRect.js'; +import { multiRect } from './shapes/multiRect.js'; +import { linedCylinder } from './shapes/linedCylinder.js'; +import { waveEdgedRectangle } from './shapes/waveEdgedRectangle.js'; +import { lightningBolt } from './shapes/lightningBolt.js'; +import { filledCircle } from './shapes/filledCircle.js'; +import { multiWaveEdgedRectangle } from './shapes/multiWaveEdgedRectangle.js'; +import { windowPane } from './shapes/windowPane.js'; +import { linedWaveEdgedRect } from './shapes/linedWaveEdgedRect.js'; +import { taggedWaveEdgedRectangle } from './shapes/taggedWaveEdgedRectangle.js'; +//Use these names as the left side to render shapes. const shapes = { + // States state, stateStart, + 'small-circle': stateStart, + 'sm-circ': stateStart, + start: stateStart, stateEnd, + 'framed-circle': stateEnd, + stop: stateEnd, + + // Rectangles + rectWithTitle, + rect: rectWithTitle, + process: rectWithTitle, + proc: rectWithTitle, + roundedRect, + rounded: roundedRect, + event: roundedRect, + squareRect, + stadium, + pill: stadium, + term: stadium, + windowPane, + 'win-pane': windowPane, + 'internal-storage': windowPane, + dividedRectangle, + 'divided-rectangle': dividedRectangle, + 'div-rect': dividedRectangle, + 'div-proc': dividedRectangle, + taggedRect, + 'tagged-rect': taggedRect, + 'tag-rect': taggedRect, + 'tag-proc': taggedRect, + multiRect, + 'multi-rect': multiRect, + 'mul-rect': multiRect, + 'mul-proc': multiRect, + + // Subroutine + subroutine, + 'framed-rectangle': subroutine, + fr: subroutine, + subproc: subroutine, + + // Cylinders + cylinder, + cyl: cylinder, + db: cylinder, + tiltedCylinder, + 'tilted-cylinder': tiltedCylinder, + 't-cyl': tiltedCylinder, + das: tiltedCylinder, + linedCylinder, + 'lined-cylinder': linedCylinder, + 'l-cyl': linedCylinder, + disk: linedCylinder, + + // Circles + circle, + doublecircle, + dc: doublecircle, + crossedCircle, + 'crossed-circ': crossedCircle, + 'cross-circ': crossedCircle, + summary: crossedCircle, + filledCircle, + 'filled-circle': filledCircle, + fc: filledCircle, + junction: filledCircle, + shadedProcess, + 'lined-proc': shadedProcess, + 'lined-rect': shadedProcess, + + // Trapezoids + trapezoid, + trapezoidBaseBottom: trapezoid, + 'trapezoid-bottom': trapezoid, + 'trap-b': trapezoid, + priority: trapezoid, + inv_trapezoid, + 'trapezoid-top': inv_trapezoid, + 'trap-t': inv_trapezoid, + manual: inv_trapezoid, + curvedTrapezoid, + 'curved-trapezoid': curvedTrapezoid, + 'cur-trap': curvedTrapezoid, + disp: curvedTrapezoid, + + // Hexagons & Misc Geometric + hexagon, + hex: hexagon, + prepare: hexagon, + triangle, + 'small-triangle': triangle, + 'sm-tri': triangle, + extract: triangle, + flippedTriangle, + 'flipped-triangle': flippedTriangle, + 'flip-tria': flippedTriangle, + 'manual-file': flippedTriangle, + trapezoidalPentagon, + 'notched-pentagon': trapezoidalPentagon, + 'not-pen': trapezoidalPentagon, + 'loop-limit': trapezoidalPentagon, + + //wave Edged Rectangles + waveRectangle, + 'wave-rectangle': waveRectangle, + 'w-rect': waveRectangle, + flag: waveRectangle, + 'paper-tape': waveRectangle, + waveEdgedRectangle, + 'wave-rect': waveEdgedRectangle, + 'we-rect': waveEdgedRectangle, + doc: waveEdgedRectangle, + multiWaveEdgedRectangle, + 'multi-wave-edged-rectangle': multiWaveEdgedRectangle, + 'mul-we-rect': multiWaveEdgedRectangle, + 'mul-doc': multiWaveEdgedRectangle, + linedWaveEdgedRect, + 'lined-wave-edged-rect': linedWaveEdgedRect, + 'lin-we-rect': linedWaveEdgedRect, + 'lin-doc': linedWaveEdgedRect, + taggedWaveEdgedRectangle, + 'tagged-wave-edged-rect': taggedWaveEdgedRectangle, + 'tag-we-rect': taggedWaveEdgedRectangle, + + // Custom Rectangles + bowTieRect, + 'bow-tie-rect': bowTieRect, + 'bt-rect': bowTieRect, + 'stored-data': bowTieRect, + slopedRect, + 'sloped-rectangle': slopedRect, + 'sloped-rect': slopedRect, + 'manual-input': slopedRect, + halfRoundedRectangle, + 'half-rounded-rect': halfRoundedRectangle, + delay: halfRoundedRectangle, + card, + 'notched-rect': card, + 'notch-rect': card, + lean_right, + 'l-r': lean_right, + 'in-out': lean_right, + lean_left, + 'l-l': lean_left, + 'out-in': lean_left, + + // Miscellaneous + forkJoin, fork: forkJoin, join: forkJoin, choice, note, - roundedRect, - rectWithTitle, - squareRect, - stadium, - subroutine, - cylinder, - circle, - doublecircle, - odd: rect_left_inv_arrow, + text, + anchor, diamond: question, - hexagon, - lean_right, - lean_left, - trapezoid, - inv_trapezoid, + lightningBolt, + bolt: lightningBolt, + 'com-link': lightningBolt, + hourglass, + odd: rect_left_inv_arrow, labelRect, }; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts new file mode 100644 index 000000000..ea8ac8ca5 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/anchor.ts @@ -0,0 +1,47 @@ +import { log } from '$root/logger.js'; +import { updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +export const anchor = (parent: SVGAElement, node: Node): Promise => { + const { labelStyles } = styles2String(node); + node.labelStyle = labelStyles; + const classes = getNodeClasses(node); + let cssClasses = classes; + if (!classes) { + cssClasses = 'anchor'; + } + const shapeSvg = parent + .insert('g') + .attr('class', cssClasses) + .attr('id', node.domId || node.id); + + const radius = 1; + + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { fill: 'black', stroke: 'none', fillStyle: 'solid' }); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + } + const roughNode = rc.circle(0, 0, radius * 2, options); + const circleElem = shapeSvg.insert(() => roughNode, ':first-child'); + circleElem.attr('class', 'anchor').attr('style', cssStyles); + + updateNodeBounds(node, circleElem); + + node.intersect = function (point) { + log.info('Circle intersect', node, radius, point); + return intersect.circle(node, radius, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts new file mode 100644 index 000000000..a2f56b3b8 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/bowTieRect.ts @@ -0,0 +1,129 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +function generateArcPoints( + x1: number, + y1: number, + x2: number, + y2: number, + rx: number, + ry: number, + clockwise: boolean +) { + const numPoints = 20; + // Calculate midpoint + const midX = (x1 + x2) / 2; + const midY = (y1 + y2) / 2; + + // Calculate the angle of the line connecting the points + const angle = Math.atan2(y2 - y1, x2 - x1); + + // Calculate transformed coordinates for the ellipse + const dx = (x2 - x1) / 2; + const dy = (y2 - y1) / 2; + + // Scale to unit circle + const transformedX = dx / rx; + const transformedY = dy / ry; + + // Calculate the distance between points on the unit circle + const distance = Math.sqrt(transformedX ** 2 + transformedY ** 2); + + // Check if the ellipse can be drawn with the given radii + if (distance > 1) { + throw new Error('The given radii are too small to create an arc between the points.'); + } + + // Calculate the distance from the midpoint to the center of the ellipse + const scaledCenterDistance = Math.sqrt(1 - distance ** 2); + + // Calculate the center of the ellipse + const centerX = midX + scaledCenterDistance * ry * Math.sin(angle) * (clockwise ? -1 : 1); + const centerY = midY - scaledCenterDistance * rx * Math.cos(angle) * (clockwise ? -1 : 1); + + // Calculate the start and end angles on the ellipse + const startAngle = Math.atan2((y1 - centerY) / ry, (x1 - centerX) / rx); + const endAngle = Math.atan2((y2 - centerY) / ry, (x2 - centerX) / rx); + + // Adjust angles for clockwise/counterclockwise + let angleRange = endAngle - startAngle; + if (clockwise && angleRange < 0) { + angleRange += 2 * Math.PI; + } + if (!clockwise && angleRange > 0) { + angleRange -= 2 * Math.PI; + } + + // Generate points + const points = []; + for (let i = 0; i < numPoints; i++) { + const t = i / (numPoints - 1); + const angle = startAngle + t * angleRange; + const x = centerX + rx * Math.cos(angle); + const y = centerY + ry * Math.sin(angle); + points.push({ x, y }); + } + + return points; +} + +export const bowTieRect = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + node.padding + 20; + const h = bbox.height + node.padding; + + const ry = h / 2; + const rx = ry / (2.5 + h / 50); + + // let shape: d3.Selection; + const { cssStyles } = node; + + const points = [ + { x: w / 2, y: -h / 2 }, + { x: -w / 2, y: -h / 2 }, + ...generateArcPoints(-w / 2, -h / 2, -w / 2, h / 2, rx, ry, false), + { x: w / 2, y: h / 2 }, + ...generateArcPoints(w / 2, h / 2, w / 2, -h / 2, rx, ry, true), + ]; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const bowTieRectPath = createPathFromPoints(points); + const bowTieRectShapePath = rc.path(bowTieRectPath, options); + const bowTieRectShape = shapeSvg.insert(() => bowTieRectShapePath, ':first-child'); + + bowTieRectShape.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + bowTieRectShape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + bowTieRectShape.selectAll('path').attr('style', nodeStyles); + } + + bowTieRectShape.attr('transform', `translate(${rx / 2}, 0)`); + + updateNodeBounds(node, bowTieRectShape); + + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts new file mode 100644 index 000000000..56fb09be9 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/card.ts @@ -0,0 +1,72 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +import { insertPolygonShape } from './insertPolygonShape.js'; +import { createPathFromPoints } from './util.js'; + +// const createPathFromPoints = (points: { x: number; y: number }[]): string => { +// const pointStrings = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`); +// pointStrings.push('Z'); +// return pointStrings.join(' '); +// }; + +export async function card(parent: SVGAElement, node: Node): Promise { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const h = bbox.height + node.padding; + const padding = 12; + const w = bbox.width + node.padding + padding; + const left = 0; + const right = w; + const top = -h; + const bottom = 0; + const points = [ + { x: left + padding, y: top }, + { x: right, y: top }, + { x: right, y: bottom }, + { x: left, y: bottom }, + { x: left, y: top + padding }, + { x: left + padding, y: top }, + ]; + + let polygon: d3.Selection; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createPathFromPoints(points); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (nodeStyles) { + polygon.attr('style', nodeStyles); + } + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/crossedCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/crossedCircle.ts new file mode 100644 index 000000000..93a69d16f --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/crossedCircle.ts @@ -0,0 +1,67 @@ +import { log } from '$root/logger.js'; +import { labelHelper, getNodeClasses, updateNodeBounds } from './util.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; + +function createLine(r: number) { + const xAxis45 = Math.cos(Math.PI / 4); // cosine of 45 degrees + const yAxis45 = Math.sin(Math.PI / 4); // sine of 45 degrees + const lineLength = r * 2; + + const pointQ1 = { x: (lineLength / 2) * xAxis45, y: (lineLength / 2) * yAxis45 }; // Quadrant I + const pointQ2 = { x: -(lineLength / 2) * xAxis45, y: (lineLength / 2) * yAxis45 }; // Quadrant II + const pointQ3 = { x: -(lineLength / 2) * xAxis45, y: -(lineLength / 2) * yAxis45 }; // Quadrant III + const pointQ4 = { x: (lineLength / 2) * xAxis45, y: -(lineLength / 2) * yAxis45 }; // Quadrant IV + + return `M ${pointQ2.x},${pointQ2.y} L ${pointQ4.x},${pointQ4.y} + M ${pointQ1.x},${pointQ1.y} L ${pointQ3.x},${pointQ3.y}`; +} + +export const crossedCircle = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, getNodeClasses(node)); + const radius = Math.max(bbox.width, bbox.height) / 2 + halfPadding; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const circleNode = rc.circle(0, 0, radius * 2, options); + const linePath = createLine(radius); + const lineNode = rc.path(linePath, options); + + const crossedCircle = shapeSvg.insert(() => circleNode, ':first-child'); + crossedCircle.insert(() => lineNode); + + crossedCircle.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + crossedCircle.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + crossedCircle.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, crossedCircle); + + node.intersect = function (point) { + log.info('crossedCircle intersect', node, { radius, point }); + const pos = intersect.circle(node, radius, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts new file mode 100644 index 000000000..e997d5f15 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/curvedTrapezoid.ts @@ -0,0 +1,97 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +function createCurvedTrapezoidPathD( + x: number, + y: number, + totalWidth: number, + totalHeight: number, + radius: number +) { + const w = totalWidth - radius; + const tw = totalHeight / 4; + const points = [ + { x: x + w, y }, + { x: x + tw, y }, + { x: x, y: y + totalHeight / 2 }, + { x: x + tw, y: y + totalHeight }, + { x: x + w, y: y + totalHeight }, + ]; + const rectPath = createPathFromPoints(points); + const arcPath = `M ${w},0 A ${totalHeight / 2} ${totalHeight / 2} 0 0 1 ${w} ${totalHeight}`; + const finalPath = `${rectPath} ${arcPath}`.replace('Z', ''); + return finalPath; +} + +export const curvedTrapezoid = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const widthMultiplier = bbox.width < 15 ? 2 : 1.25; + const w = (bbox.width + node.padding) * widthMultiplier; + const h = bbox.height + node.padding; + const radius = h / 2; + + const { cssStyles } = node; + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const pathData = createCurvedTrapezoidPathD(0, 0, w, h, radius); + const shapeNode = rc.path(pathData, options); + + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + polygon.attr('transform', `translate(${-w / 2}, ${-h / 2})`); + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + const rx = h / 2; + const ry = h / 2; + const y = pos.y - (node.y ?? 0); + + if ( + ry != 0 && + (Math.abs(y) < (node.height ?? 0) / 2 || + (Math.abs(y) == (node.height ?? 0) / 2 && + Math.abs(pos.x - (node.x ?? 0)) > (node.width ?? 0) / 2 - rx)) + ) { + let x = rx * rx * (1 - (y * y) / (ry * ry)); + if (x != 0) { + x = Math.sqrt(x); + } + x = rx - x; + if (point.x - (node.x ?? 0) > 0) { + x = -x; + } + + pos.x += x; + } + + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts index e9f7b3067..00054ba32 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts @@ -57,7 +57,7 @@ export const cylinder = async (parent: SVGAElement, node: Node) => { const { useGradient } = themeVariables; const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; - const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); const labelPaddingX = node.look === 'neo' ? node.padding * 2 : node.padding; const labelPaddingY = node.look === 'neo' ? node.padding * 1 : node.padding; const w = bbox.width + labelPaddingY; @@ -109,6 +109,8 @@ export const cylinder = async (parent: SVGAElement, node: Node) => { updateNodeBounds(node, cylinder); + label.attr('transform', `translate(${-bbox.width / 2}, ${h / 2 - bbox.height})`); + node.intersect = function (point) { const pos = intersect.rect(node, point); const x = pos.x - (node.x ?? 0); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts new file mode 100644 index 000000000..8f66eb15e --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/dividedRect.ts @@ -0,0 +1,71 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +export const dividedRectangle = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const rectOffset = h * 0.2; + + const x = -w / 2; + const y = -h / 2 - rectOffset / 2; + + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const outerPathPoints = [ + { x, y: y }, + { x, y: -y }, + { x: -x, y: -y }, + { x: -x, y: y }, + ]; + const innerPathPoints = [ + { x: x, y: y + rectOffset }, + { x: -x, y: y + rectOffset }, + ]; + const outerPathData = createPathFromPoints(outerPathPoints); + const outerNode = rc.path(outerPathData, options); + const innerPathData = createPathFromPoints(innerPathPoints); + const innerNode = rc.path(innerPathData, options); + + const polygon = shapeSvg.insert(() => outerNode, ':first-child'); + polygon.insert(() => innerNode); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectAll('path').attr('style', nodeStyles); + } + + label.attr( + 'transform', + `translate(${x + (node.padding ?? 0) / 2 - (bbox.x - (bbox.left ?? 0))}, ${y + rectOffset + (node.padding ?? 0) / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + const pos = intersect.polygon(node, outerPathPoints, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts new file mode 100644 index 000000000..9d7e5445b --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/document.ts @@ -0,0 +1,121 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +export const createCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [ + `M${x},${y + ry}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + ].join(' '); +}; +export const createOuterCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [ + `M${x},${y + ry}`, + `M${x + width},${y + ry}`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + ].join(' '); +}; +export const createInnerCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [`M${x - width / 2},${-height / 2}`, `a${rx},${ry} 0,0,0 ${width},0`].join(' '); +}; +export const cylinder = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + node.padding; + const rx = w / 2; + const ry = rx / (2.5 + w / 50); + const h = bbox.height + ry + node.padding; + + let cylinder: d3.Selection; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry); + const innerPathData = createInnerCylinderPathD(0, ry, w, h, rx, ry); + const outerNode = rc.path(outerPathData, userNodeOverrides(node, {})); + const innerLine = rc.path(innerPathData, userNodeOverrides(node, { fill: 'none' })); + + cylinder = shapeSvg.insert(() => innerLine, ':first-child'); + cylinder = shapeSvg.insert(() => outerNode, ':first-child'); + cylinder.attr('class', 'basic label-container'); + if (cssStyles) { + cylinder.attr('style', cssStyles); + } + } else { + const pathData = createCylinderPathD(0, 0, w, h, rx, ry); + cylinder = shapeSvg + .insert('path', ':first-child') + .attr('d', pathData) + .attr('class', 'basic label-container') + .attr('style', cssStyles) + .attr('style', nodeStyles); + } + + cylinder.attr('label-offset-y', ry); + cylinder.attr('transform', `translate(${-w / 2}, ${-(h / 2 + ry)})`); + + updateNodeBounds(node, cylinder); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + const x = pos.x - (node.x ?? 0); + + if ( + rx != 0 && + (Math.abs(x) < (node.width ?? 0) / 2 || + (Math.abs(x) == (node.width ?? 0) / 2 && + Math.abs(pos.y - (node.y ?? 0)) > (node.height ?? 0) / 2 - ry)) + ) { + let y = ry * ry * (1 - (x * x) / (rx * rx)); + if (y != 0) { + y = Math.sqrt(y); + } + y = ry - y; + if (point.y - (node.y ?? 0) > 0) { + y = -y; + } + + pos.y += y; + } + + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts new file mode 100644 index 000000000..5a2888ebe --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/filledCircle.ts @@ -0,0 +1,51 @@ +import { log } from '$root/logger.js'; +import { labelHelper, getNodeClasses, updateNodeBounds } from './util.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; + +export const filledCircle = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.label = ''; + node.labelStyle = labelStyles; + const { shapeSvg } = await labelHelper(parent, node, getNodeClasses(node)); + const radius = 4; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const circleNode = rc.circle(0, 0, radius * 2, options); + + const filledCircle = shapeSvg.insert(() => circleNode, ':first-child'); + + filledCircle.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + filledCircle.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + filledCircle.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, filledCircle); + + node.intersect = function (point) { + log.info('filledCircle intersect', node, { radius, point }); + const pos = intersect.circle(node, radius, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts new file mode 100644 index 000000000..e816eac38 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/flippedTriangle.ts @@ -0,0 +1,67 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { createPathFromPoints } from './util.js'; + +export const flippedTriangle = async (parent: SVGAElement, node: Node): Promise => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + + const w = bbox.width + (node.padding ?? 0); + const h = w + bbox.height; + + const tw = w + bbox.height; + const points = [ + { x: 0, y: -h }, + { x: tw, y: -h }, + { x: tw / 2, y: 0 }, + ]; + + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const pathData = createPathFromPoints(points); + const roughNode = rc.path(pathData, options); + + const flippedTriangle = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-h / 2}, ${h / 2})`); + + if (cssStyles && node.look !== 'handDrawn') { + flippedTriangle.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + flippedTriangle.selectChildren('path').attr('style', nodeStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, flippedTriangle); + + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${-h / 2 + (bbox.y - (bbox.top ?? 0))})` + ); + + node.intersect = function (point) { + log.info('Triangle intersect', node, points, point); + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts new file mode 100644 index 000000000..1862f1838 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/halfRoundedRectangle.ts @@ -0,0 +1,86 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +function createHalfRoundedRectShapePathD(h: number, w: number, rx: number, ry: number) { + return ` M ${w} ${h} + L ${0} ${h} + L ${0} ${0} + L ${w} ${0} + A ${rx} ${ry} 0 0 1 ${w} ${h}Z`; +} + +export const halfRoundedRectangle = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const radius = h / 2; + const rx = radius; + const ry = radius; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const pathData = createHalfRoundedRectShapePathD(h, w, rx, ry); + const shapeNode = rc.path(pathData, options); + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + polygon.attr('transform', `translate(${-w / 2 - h / 4}, ${-h / 2})`); + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) - h / 4 - (bbox.x - (bbox.left ?? 0))}, ${-h / 2 + (node.padding ?? 0) - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + log.info('Pill intersect', node, { radius, point }); + const pos = intersect.rect(node, point); + const y = pos.y - (node.y ?? 0); + if ( + ry != 0 && + pos.x > (node.x ?? 0) && + (Math.abs(y) < (node.height ?? 0) / 2 || + (Math.abs(y) == (node.height ?? 0) / 2 && + Math.abs(pos.x - (node.x ?? 0)) > (node.width ?? 0) / 2 - rx)) + ) { + let x = rx * rx * (1 - (y * y) / (ry * ry)); + if (x != 0) { + x = Math.sqrt(x); + } + x = rx - x; + if (point.x - (node.x ?? 0) > 0) { + x = -x; + } + + pos.x += x; + } + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/hourglass.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hourglass.ts new file mode 100644 index 000000000..e8407df0e --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hourglass.ts @@ -0,0 +1,64 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +export const hourglass = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.label = ''; + node.labelStyle = labelStyles; + const { shapeSvg } = await labelHelper(parent, node, getNodeClasses(node)); + + const w = 100; + const h = 100; + + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: 0, y: 0 }, + { x: w, y: 0 }, + { x: 0, y: h }, + { x: w, y: h }, + ]; + + const pathData = createPathFromPoints(points); + const shapeNode = rc.path(pathData, options); + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + polygon.attr('transform', `translate(${-w / 2}, ${-h / 2})`); + + updateNodeBounds(node, polygon); + + // label.attr('transform', `translate(${-bbox.width / 2}, ${(h/2)})`); // To transform text below hourglass shape + + node.intersect = function (point) { + log.info('Pill intersect', node, { points }); + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/lightningBolt.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/lightningBolt.ts new file mode 100644 index 000000000..48ac31037 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/lightningBolt.ts @@ -0,0 +1,67 @@ +import { log } from '$root/logger.js'; +import { labelHelper, getNodeClasses, updateNodeBounds } from './util.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; +import { createPathFromPoints } from './util.js'; + +export const lightningBolt = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.label = ''; + node.labelStyle = labelStyles; + const { shapeSvg } = await labelHelper(parent, node, getNodeClasses(node)); + const { cssStyles } = node; + const height = 80; + const width = 80; + const gap = 16; + + const points = [ + { x: width, y: 0 }, + { x: 0, y: height + gap / 2 }, + { x: width - 2 * gap, y: height + gap / 2 }, + { x: 0, y: 2 * height }, + { x: width, y: height - gap / 2 }, + { x: 2 * gap, y: height - gap / 2 }, + ]; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const linePath = createPathFromPoints(points); + const lineNode = rc.path(linePath, options); + + const lightningBolt = shapeSvg.insert(() => lineNode, ':first-child'); + + lightningBolt.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + lightningBolt.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + lightningBolt.selectAll('path').attr('style', nodeStyles); + } + + lightningBolt.attr('transform', `translate(-${width / 2},${-height})`); + + updateNodeBounds(node, lightningBolt); + + node.intersect = function (point) { + log.info('lightningBolt intersect', node, point); + const pos = intersect.polygon(node, points, point); + + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts new file mode 100644 index 000000000..54719c789 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedCylinder.ts @@ -0,0 +1,99 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; + +export const createCylinderPathWithInnerArcD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number, + outerOffset: number +): string => { + return [ + `M${x},${y + ry}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + `M${x},${y + ry + outerOffset}`, // Move to the start of the offset top arc + `a${rx},${ry} 0,0,0 ${width},0`, // Draw the duplicated top ellipse + ].join(' '); +}; + +export const linedCylinder = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = bbox.width + node.padding; + const rx = w / 2; + const ry = rx / (2.5 + w / 50); + const h = bbox.height + ry + node.padding; + const outerOffset = h * 0.1; // 10% of height + + let cylinder: d3.Selection; + const { cssStyles } = node; + + if (node.look === 'handDrawn') { + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createCylinderPathWithInnerArcD(0, 0, w, h, rx, ry, outerOffset); + const innerLine = rc.path(pathData, options); + + cylinder = shapeSvg.insert(() => innerLine, ':first-child'); + cylinder.attr('class', 'basic label-container'); + if (cssStyles) { + cylinder.attr('style', cssStyles); + } + } else { + const pathData = createCylinderPathWithInnerArcD(0, 0, w, h, rx, ry, outerOffset); + cylinder = shapeSvg + .insert('path', ':first-child') + .attr('d', pathData) + .attr('class', 'basic label-container') + .attr('style', cssStyles) + .attr('style', nodeStyles); + } + + cylinder.attr('label-offset-y', ry); + cylinder.attr('transform', `translate(${-w / 2}, ${-(h / 2 + ry)})`); + + updateNodeBounds(node, cylinder); + + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${h / 2 - bbox.height + outerOffset - (bbox.y - (bbox.top ?? 0))})` + ); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + const x = pos.x - (node.x ?? 0); + + if ( + rx != 0 && + (Math.abs(x) < (node.width ?? 0) / 2 || + (Math.abs(x) == (node.width ?? 0) / 2 && + Math.abs(pos.y - (node.y ?? 0)) > (node.height ?? 0) / 2 - ry)) + ) { + let y = ry * ry * (1 - (x * x) / (rx * rx)); + if (y != 0) { + y = Math.sqrt(y); + } + y = ry - y; + if (point.y - (node.y ?? 0) > 0) { + y = -y; + } + + pos.y += y; + } + + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts new file mode 100644 index 000000000..954bbadcd --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/linedWaveEdgedRect.ts @@ -0,0 +1,86 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + generateFullSineWavePoints, + createPathFromPoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; + +export const linedWaveEdgedRect = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const waveAmplitude = h / 4; + const finalH = h + waveAmplitude; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: -w / 2 - (w / 2) * 0.1, y: finalH / 2 }, + ...generateFullSineWavePoints( + -w / 2 - (w / 2) * 0.1, + finalH / 2, + w / 2 + (w / 2) * 0.1, + finalH / 2, + waveAmplitude, + 0.8 + ), + { x: w / 2 + (w / 2) * 0.1, y: -finalH / 2 }, + { x: -w / 2 - (w / 2) * 0.1, y: -finalH / 2 }, + ]; + + const x = -w / 2; + const y = -finalH / 2; + + const innerPathPoints = [ + { x: x, y: y }, + { x: x, y: -y * 1.1 }, + ]; + + const waveEdgeRectPath = createPathFromPoints(points); + const waveEdgeRectNode = rc.path(waveEdgeRectPath, options); + + const innerSecondPath = createPathFromPoints(innerPathPoints); + const innerSecondNode = rc.path(innerSecondPath, options); + + const waveEdgeRect = shapeSvg.insert(() => innerSecondNode, ':first-child'); + waveEdgeRect.insert(() => waveEdgeRectNode, ':first-child'); + + waveEdgeRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', nodeStyles); + } + + waveEdgeRect.attr('transform', `translate(0,${-waveAmplitude / 2})`); + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) + ((w / 2) * 0.1) / 2 - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) - waveAmplitude / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, waveEdgeRect); + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts new file mode 100644 index 000000000..380020056 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiRect.ts @@ -0,0 +1,85 @@ +import { labelHelper, getNodeClasses, updateNodeBounds, createPathFromPoints } from './util.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; + +export const multiRect = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const rectOffset = 5; + const x = -w / 2; + const y = -h / 2; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const outerPathPoints = [ + { x: x - rectOffset, y: y + rectOffset }, + { x: x - rectOffset, y: y + h + rectOffset }, + { x: x + w - rectOffset, y: y + h + rectOffset }, + { x: x + w - rectOffset, y: y + h }, + { x: x + w, y: y + h }, + { x: x + w, y: y + h - rectOffset }, + { x: x + w + rectOffset, y: y + h - rectOffset }, + { x: x + w + rectOffset, y: y - rectOffset }, + { x: x + rectOffset, y: y - rectOffset }, + { x: x + rectOffset, y: y }, + { x, y }, + { x, y: y + rectOffset }, + ]; + + const innerPathPoints = [ + { x, y: y + rectOffset }, + { x: x + w - rectOffset, y: y + rectOffset }, + { x: x + w - rectOffset, y: y + h }, + { x: x + w, y: y + h }, + { x: x + w, y }, + { x, y }, + ]; + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const outerPath = createPathFromPoints(outerPathPoints); + const outerNode = rc.path(outerPath, options); + const innerPath = createPathFromPoints(innerPathPoints); + const innerNode = rc.path(innerPath, options); + + const multiRect = shapeSvg.insert(() => innerNode, ':first-child'); + multiRect.insert(() => outerNode, ':first-child'); + + multiRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + multiRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + multiRect.selectAll('path').attr('style', nodeStyles); + } + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) - rectOffset - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + rectOffset - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, multiRect); + + node.intersect = function (point) { + const pos = intersect.polygon(node, outerPathPoints, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts new file mode 100644 index 000000000..8d8a7caf2 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/multiWaveEdgedRectangle.ts @@ -0,0 +1,104 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + createPathFromPoints, + generateFullSineWavePoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; + +export const multiWaveEdgedRectangle = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const waveAmplitude = h / 4; + const finalH = h + waveAmplitude; + const x = -w / 2; + const y = -finalH / 2; + const rectOffset = 5; + + const { cssStyles } = node; + + const wavePoints = generateFullSineWavePoints( + x - rectOffset, + y + finalH + rectOffset, + x + w - rectOffset, + y + finalH + rectOffset, + waveAmplitude, + 0.8 + ); + + const lastWavePoint = wavePoints?.[wavePoints.length - 1]; + + const outerPathPoints = [ + { x: x - rectOffset, y: y + rectOffset }, + { x: x - rectOffset, y: y + finalH + rectOffset }, + ...wavePoints, + { x: x + w - rectOffset, y: lastWavePoint.y - rectOffset }, + { x: x + w, y: lastWavePoint.y - rectOffset }, + { x: x + w, y: lastWavePoint.y - 2 * rectOffset }, + { x: x + w + rectOffset, y: lastWavePoint.y - 2 * rectOffset }, + { x: x + w + rectOffset, y: y - rectOffset }, + { x: x + rectOffset, y: y - rectOffset }, + { x: x + rectOffset, y: y }, + { x, y }, + { x, y: y + rectOffset }, + ]; + + const innerPathPoints = [ + { x, y: y + rectOffset }, + { x: x + w - rectOffset, y: y + rectOffset }, + { x: x + w - rectOffset, y: lastWavePoint.y - rectOffset }, + { x: x + w, y: lastWavePoint.y - rectOffset }, + { x: x + w, y }, + { x, y }, + ]; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const outerPath = createPathFromPoints(outerPathPoints); + const outerNode = rc.path(outerPath, options); + const innerPath = createPathFromPoints(innerPathPoints); + const innerNode = rc.path(innerPath, options); + + const shape = shapeSvg.insert(() => outerNode, ':first-child'); + shape.insert(() => innerNode); + + shape.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + shape.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + shape.selectAll('path').attr('style', nodeStyles); + } + + shape.attr('transform', `translate(0,${-waveAmplitude / 2})`); + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) - rectOffset - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + rectOffset - waveAmplitude / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, shape); + + node.intersect = function (point) { + const pos = intersect.polygon(node, outerPathPoints, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts new file mode 100644 index 000000000..b60f1ec75 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/shadedProcess.ts @@ -0,0 +1,51 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +export const shadedProcess = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const halfPadding = (node?.padding || 0) / 2; + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const x = -bbox.width / 2 - halfPadding; + const y = -bbox.height / 2 - halfPadding; + + const { cssStyles } = node; + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const roughNode = rc.rectangle(x - 8, y, w + 16, h, options); + const l1 = rc.line(x, y, x, y + h, options); + + shapeSvg.insert(() => l1, ':first-child'); + const rect = shapeSvg.insert(() => roughNode, ':first-child'); + + rect.attr('class', 'basic label-container').attr('style', cssStyles); + + if (nodeStyles) { + rect.attr('style', nodeStyles); + } + + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +}; + +export default shadedProcess; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts new file mode 100644 index 000000000..6adcfd245 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/slopedRect.ts @@ -0,0 +1,65 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +export const slopedRect = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const x = -w / 2; + const y = -h / 2; + + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x, y }, + { x, y: y + h }, + { x: x + w, y: y + h }, + { x: x + w, y: y - h / 2 }, + ]; + + const pathData = createPathFromPoints(points); + const shapeNode = rc.path(pathData, options); + + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + polygon.attr('transform', `translate(0, ${h / 4})`); + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))}, ${-h / 4 + (node.padding ?? 0) - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts new file mode 100644 index 000000000..c6d048897 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedRect.ts @@ -0,0 +1,72 @@ +import { labelHelper, getNodeClasses, updateNodeBounds, createPathFromPoints } from './util.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; + +export const taggedRect = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const x = -w / 2; + const y = -h / 2; + const tagWidth = 0.2 * w; + const tagHeight = 0.2 * h; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const rectPoints = [ + { x: x - tagWidth / 2, y }, + { x: x + w + tagWidth / 2, y }, + { x: x + w + tagWidth / 2, y: y + h }, + { x: x - tagWidth / 2, y: y + h }, + ]; + + const tagPoints = [ + { x: x + w - tagWidth / 2, y: y + h }, + { x: x + w + tagWidth / 2, y: y + h }, + { x: x + w + tagWidth / 2, y: y + h - tagHeight }, + ]; + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const rectPath = createPathFromPoints(rectPoints); + const rectNode = rc.path(rectPath, options); + + const tagPath = createPathFromPoints(tagPoints); + const tagNode = rc.path(tagPath, options); + + const taggedRect = shapeSvg.insert(() => tagNode, ':first-child'); + taggedRect.insert(() => rectNode, ':first-child'); + + taggedRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + taggedRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + taggedRect.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, taggedRect); + + node.intersect = function (point) { + const pos = intersect.polygon(node, rectPoints, point); + + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts new file mode 100644 index 000000000..76449aa22 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/taggedWaveEdgedRectangle.ts @@ -0,0 +1,98 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + generateFullSineWavePoints, + createPathFromPoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; + +export const taggedWaveEdgedRectangle = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const waveAmplitude = h / 4; + const tagWidth = 0.2 * w; + const tagHeight = 0.2 * h; + const finalH = h + waveAmplitude; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: -w / 2 - (w / 2) * 0.1, y: finalH / 2 }, + ...generateFullSineWavePoints( + -w / 2 - (w / 2) * 0.1, + finalH / 2, + w / 2 + (w / 2) * 0.1, + finalH / 2, + waveAmplitude, + 0.8 + ), + + { x: w / 2 + (w / 2) * 0.1, y: -finalH / 2 }, + { x: -w / 2 - (w / 2) * 0.1, y: -finalH / 2 }, + ]; + + const x = -w / 2 + (w / 2) * 0.1; + const y = -finalH / 2 - tagHeight * 0.4; + + const tagPoints = [ + { x: x + w - tagWidth, y: (y + h) * 1.4 }, + { x: x + w, y: y + h - tagHeight }, + { x: x + w, y: (y + h) * 0.9 }, + ...generateFullSineWavePoints( + x + w, + (y + h) * 1.3, + x + w - tagWidth, + (y + h) * 1.5, + -h * 0.03, + 0.5 + ), + ]; + + const waveEdgeRectPath = createPathFromPoints(points); + const waveEdgeRectNode = rc.path(waveEdgeRectPath, options); + + const taggedWaveEdgeRectPath = createPathFromPoints(tagPoints); + const taggedWaveEdgeRectNode = rc.path(taggedWaveEdgeRectPath, options); + + const waveEdgeRect = shapeSvg.insert(() => taggedWaveEdgeRectNode, ':first-child'); + waveEdgeRect.insert(() => waveEdgeRectNode, ':first-child'); + + waveEdgeRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', nodeStyles); + } + + waveEdgeRect.attr('transform', `translate(0,${-waveAmplitude / 2})`); + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) - waveAmplitude / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, waveEdgeRect); + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts new file mode 100644 index 000000000..31b5e8d2d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/text.ts @@ -0,0 +1,36 @@ +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { styles2String } from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; + +export async function text(parent: SVGAElement, node: Node): Promise { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + + const totalWidth = Math.max(bbox.width + node.padding, node?.width || 0); + const totalHeight = Math.max(bbox.height + node.padding, node?.height || 0); + const x = -totalWidth / 2; + const y = -totalHeight / 2; + + const rect = shapeSvg.insert('rect', ':first-child'); + + rect + .attr('class', 'text') + .attr('style', nodeStyles) + .attr('rx', 0) + .attr('ry', 0) + .attr('x', x) + .attr('y', y) + .attr('width', totalWidth) + .attr('height', totalHeight); + + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts new file mode 100644 index 000000000..ed05a4f6d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/tiltedCylinder.ts @@ -0,0 +1,81 @@ +import { labelHelper, getNodeClasses, updateNodeBounds } from './util.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; + +function createCylinderPathD(rx: number, ry: number, w: number, h: number) { + return `M ${w / 2} ${-h / 2} + L ${-w / 2} ${-h / 2} + A ${rx} ${ry} 0 0 0 ${-w / 2} ${h / 2} + L ${w / 2} ${h / 2} + A ${rx} ${ry} 0 0 0 ${w / 2} ${-h / 2} + A ${rx} ${ry} 0 0 0 ${w / 2} ${h / 2}`; +} + +export const tiltedCylinder = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const h = bbox.height + node.padding; + const ry = h / 2; + const rx = ry / (2.5 + h / 50); + const w = bbox.width + rx + node.padding; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const cylinderPath = createCylinderPathD(rx, ry, w, h); + const cylinderNode = rc.path(cylinderPath, options); + + const tiltedCylinder = shapeSvg.insert(() => cylinderNode, ':first-child'); + + tiltedCylinder.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + tiltedCylinder.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + tiltedCylinder.selectChildren('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, tiltedCylinder); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + const y = pos.y - (node.y ?? 0); + + if ( + ry != 0 && + (Math.abs(y) < (node.height ?? 0) / 2 || + (Math.abs(y) == (node.height ?? 0) / 2 && + Math.abs(pos.x - (node.x ?? 0)) > (node.width ?? 0) / 2 - rx)) + ) { + let x = rx * rx * (1 - (y * y) / (ry * ry)); + if (x != 0) { + x = Math.sqrt(x); + } + x = rx - x; + if (point.x - (node.x ?? 0) > 0) { + x = -x; + } + + pos.x += x; + } + + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoidalPentagon.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoidalPentagon.ts new file mode 100644 index 000000000..b9f97f349 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoidalPentagon.ts @@ -0,0 +1,64 @@ +import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +export const trapezoidalPentagon = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const widthMultiplier = bbox.width < 40 ? 3 : 1.25; + const w = (bbox.width + node.padding) * widthMultiplier; + const h = bbox.height + node.padding; + + const { cssStyles } = node; + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const topOffset = 30; + const slopeHeight = 15; + + const points = [ + { x: topOffset, y: 0 }, + { x: w - topOffset, y: 0 }, + { x: w, y: slopeHeight }, + { x: w, y: h }, + { x: 0, y: h }, + { x: 0, y: slopeHeight }, + ]; + + const pathData = createPathFromPoints(points); + const shapeNode = rc.path(pathData, options); + + const polygon = shapeSvg.insert(() => shapeNode, ':first-child'); + polygon.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + polygon.attr('transform', `translate(${-w / 2}, ${-h / 2})`); + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts new file mode 100644 index 000000000..ec76af239 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/triangle.ts @@ -0,0 +1,67 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import { createPathFromPoints } from './util.js'; + +export const triangle = async (parent: SVGAElement, node: Node): Promise => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + + const w = bbox.width + (node.padding ?? 0); + const h = w + bbox.height; + + const tw = w + bbox.height; + const points = [ + { x: 0, y: 0 }, + { x: tw, y: 0 }, + { x: tw / 2, y: -h }, + ]; + + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + const pathData = createPathFromPoints(points); + const roughNode = rc.path(pathData, options); + + const polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-h / 2}, ${h / 2})`); + + if (cssStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + polygon.selectChildren('path').attr('style', nodeStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + label.attr( + 'transform', + `translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${h / 2 - bbox.height - (bbox.y - (bbox.top ?? 0))})` + ); + + node.intersect = function (point) { + log.info('Triangle intersect', node, points, point); + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.js b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.js index ca1290668..0f58fa131 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.js @@ -134,3 +134,31 @@ export function insertPolygonShape(parent, w, h, points) { export const getNodeClasses = (node, extra) => (node.look === 'handDrawn' ? 'rough-node' : 'node') + ' ' + node.cssClasses + ' ' + (extra || ''); + +export function createPathFromPoints(points) { + const pointStrings = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`); + pointStrings.push('Z'); + return pointStrings.join(' '); +} + +export function generateFullSineWavePoints(x1, y1, x2, y2, amplitude, numCycles) { + const points = []; + const steps = 50; // Number of segments to create a smooth curve + const deltaX = x2 - x1; + const deltaY = y2 - y1; + const cycleLength = deltaX / numCycles; + + // Calculate frequency and phase shift + const frequency = (2 * Math.PI) / cycleLength; + const midY = y1 + deltaY / 2; + + for (let i = 0; i <= steps; i++) { + const t = i / steps; + const x = x1 + t * deltaX; + const y = midY + amplitude * Math.sin(frequency * (x - x1)); + + points.push({ x, y }); + } + + return points; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts new file mode 100644 index 000000000..2538fbea2 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveEdgedRectangle.ts @@ -0,0 +1,67 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + generateFullSineWavePoints, + createPathFromPoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import rough from 'roughjs'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; + +export const waveEdgedRectangle = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const waveAmplitude = h / 4; + const finalH = h + waveAmplitude; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: -w / 2, y: finalH / 2 }, + ...generateFullSineWavePoints(-w / 2, finalH / 2, w / 2, finalH / 2, waveAmplitude, 0.8), + { x: w / 2, y: -finalH / 2 }, + { x: -w / 2, y: -finalH / 2 }, + ]; + + const waveEdgeRectPath = createPathFromPoints(points); + const waveEdgeRectNode = rc.path(waveEdgeRectPath, options); + + const waveEdgeRect = shapeSvg.insert(() => waveEdgeRectNode, ':first-child'); + + waveEdgeRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + waveEdgeRect.selectAll('path').attr('style', nodeStyles); + } + + waveEdgeRect.attr('transform', `translate(0,${-waveAmplitude / 2})`); + label.attr( + 'transform', + `translate(${-w / 2 + (node.padding ?? 0) - (bbox.x - (bbox.left ?? 0))},${-h / 2 + (node.padding ?? 0) - waveAmplitude / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, waveEdgeRect); + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts new file mode 100644 index 000000000..08e531798 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/waveRectangle.ts @@ -0,0 +1,64 @@ +import { + labelHelper, + updateNodeBounds, + getNodeClasses, + createPathFromPoints, + generateFullSineWavePoints, +} from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; + +export const waveRectangle = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const waveAmplitude = h / 4; + const finalH = h + waveAmplitude; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const points = [ + { x: -w / 2, y: finalH / 2 }, + ...generateFullSineWavePoints(-w / 2, finalH / 2, w / 2, finalH / 2, waveAmplitude, 1), + { x: w / 2, y: -finalH / 2 }, + ...generateFullSineWavePoints(w / 2, -finalH / 2, -w / 2, -finalH / 2, waveAmplitude, -1), + ]; + + const waveRectPath = createPathFromPoints(points); + const waveRectNode = rc.path(waveRectPath, options); + + const waveRect = shapeSvg.insert(() => waveRectNode, ':first-child'); + + waveRect.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + waveRect.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + waveRect.selectAll('path').attr('style', nodeStyles); + } + + updateNodeBounds(node, waveRect); + node.intersect = function (point) { + const pos = intersect.polygon(node, points, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts new file mode 100644 index 000000000..799b12b93 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/windowPane.ts @@ -0,0 +1,82 @@ +import { labelHelper, getNodeClasses, updateNodeBounds, createPathFromPoints } from './util.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { + styles2String, + userNodeOverrides, +} from '$root/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import intersect from '../intersect/index.js'; + +export const windowPane = async (parent: SVGAElement, node: Node) => { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node)); + const w = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0); + const h = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0); + const rectOffset = 5; + const x = -w / 2; + const y = -h / 2; + const { cssStyles } = node; + + // @ts-ignore - rough is not typed + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + + const outerPathPoints = [ + { x: x - rectOffset, y: y - rectOffset }, + { x: x - rectOffset, y: y + h }, + { x: x + w, y: y + h }, + { x: x + w, y: y - rectOffset }, + ]; + + const innerPathPoints = [ + { x: x - rectOffset, y }, + { x: x + w, y }, + ]; + + const innerSecondPathPoints = [ + { x, y: y - rectOffset }, + { x, y: y + h }, + ]; + + if (node.look !== 'handDrawn') { + options.roughness = 0; + options.fillStyle = 'solid'; + } + + const outerPath = createPathFromPoints(outerPathPoints); + const outerNode = rc.path(outerPath, options); + const innerPath = createPathFromPoints(innerPathPoints); + const innerNode = rc.path(innerPath, options); + const innerSecondPath = createPathFromPoints(innerSecondPathPoints); + const innerSecondNode = rc.path(innerSecondPath, options); + + const windowPane = shapeSvg.insert(() => innerNode, ':first-child'); + windowPane.insert(() => innerSecondNode, ':first-child'); + windowPane.insert(() => outerNode, ':first-child'); + windowPane.attr('transform', `translate(${rectOffset / 2}, ${rectOffset / 2})`); + + windowPane.attr('class', 'basic label-container'); + + if (cssStyles && node.look !== 'handDrawn') { + windowPane.selectAll('path').attr('style', cssStyles); + } + + if (nodeStyles && node.look !== 'handDrawn') { + windowPane.selectAll('path').attr('style', nodeStyles); + } + + label.attr( + 'transform', + `translate(${-(bbox.width / 2) + rectOffset / 2 - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) + rectOffset / 2 - (bbox.y - (bbox.top ?? 0))})` + ); + + updateNodeBounds(node, windowPane); + + node.intersect = function (point) { + const pos = intersect.polygon(node, outerPathPoints, point); + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/styles.ts b/packages/mermaid/src/styles.ts index ca2e02d17..df8c77f21 100644 --- a/packages/mermaid/src/styles.ts +++ b/packages/mermaid/src/styles.ts @@ -72,6 +72,9 @@ const getStyles = ( font-family: ${options.fontFamily}; font-size: ${options.fontSize}; } + & p { + margin: 0 + } ${diagramStyles} .node .neo-node { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11a08123e..786ef5fd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,22 +10,22 @@ importers: devDependencies: '@applitools/eyes-cypress': specifier: ^3.44.4 - version: 3.44.6(typescript@5.4.5) + version: 3.44.6(encoding@0.1.13)(typescript@5.4.5) '@argos-ci/cypress': specifier: ^2.1.0 version: 2.1.1(cypress@13.13.2) '@cspell/eslint-plugin': specifier: ^8.8.4 - version: 8.13.3(eslint@9.9.0) + version: 8.13.3(eslint@9.9.0(jiti@1.21.6)) '@cypress/code-coverage': specifier: ^3.12.30 - version: 3.12.45(@babel/core@7.25.2)(@babel/preset-env@7.25.3)(babel-loader@9.1.3)(cypress@13.13.2)(webpack@5.93.0) + version: 3.12.45(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(cypress@13.13.2)(webpack@5.93.0(esbuild@0.21.5)) '@eslint/js': specifier: ^9.4.0 version: 9.9.0 '@rollup/plugin-typescript': specifier: ^11.1.6 - version: 11.1.6(typescript@5.4.5) + version: 11.1.6(rollup@4.20.0)(tslib@2.6.3)(typescript@5.4.5) '@types/cors': specifier: ^2.8.17 version: 2.8.17 @@ -52,7 +52,7 @@ importers: version: 4.2.4 '@vitest/coverage-v8': specifier: ^1.4.0 - version: 1.6.0(vitest@1.6.0) + version: 1.6.0(vitest@1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.6)) '@vitest/spy': specifier: ^1.4.0 version: 1.6.0 @@ -82,37 +82,37 @@ importers: version: 13.13.2 cypress-image-snapshot: specifier: ^4.0.1 - version: 4.0.1(cypress@13.13.2)(jest@29.7.0) + version: 4.0.1(cypress@13.13.2)(jest@29.7.0(@types/node@20.14.15)) esbuild: specifier: ^0.21.5 version: 0.21.5 eslint: specifier: ^9.4.0 - version: 9.9.0 + version: 9.9.0(jiti@1.21.6) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.9.0) + version: 9.1.0(eslint@9.9.0(jiti@1.21.6)) eslint-plugin-cypress: specifier: ^3.3.0 - version: 3.5.0(eslint@9.9.0) + version: 3.5.0(eslint@9.9.0(jiti@1.21.6)) eslint-plugin-html: specifier: ^8.1.1 version: 8.1.1 eslint-plugin-jest: specifier: ^28.6.0 - version: 28.8.0(eslint@9.9.0)(jest@29.7.0)(typescript@5.4.5) + version: 28.8.0(@typescript-eslint/eslint-plugin@8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.9.0(jiti@1.21.6))(jest@29.7.0(@types/node@20.14.15))(typescript@5.4.5) eslint-plugin-jsdoc: specifier: ^48.2.9 - version: 48.11.0(eslint@9.9.0) + version: 48.11.0(eslint@9.9.0(jiti@1.21.6)) eslint-plugin-json: specifier: ^4.0.0 version: 4.0.1 eslint-plugin-lodash: specifier: ^8.0.0 - version: 8.0.0(eslint@9.9.0) + version: 8.0.0(eslint@9.9.0(jiti@1.21.6)) eslint-plugin-markdown: specifier: ^5.0.0 - version: 5.1.0(eslint@9.9.0) + version: 5.1.0(eslint@9.9.0(jiti@1.21.6)) eslint-plugin-no-only-tests: specifier: ^3.1.0 version: 3.1.0 @@ -121,7 +121,7 @@ importers: version: 0.3.0 eslint-plugin-unicorn: specifier: ^55.0.0 - version: 55.0.0(eslint@9.9.0) + version: 55.0.0(eslint@9.9.0(jiti@1.21.6)) express: specifier: ^4.19.1 version: 4.19.2 @@ -175,7 +175,7 @@ importers: version: 5.0.10 rollup-plugin-visualizer: specifier: ^5.12.0 - version: 5.12.0 + version: 5.12.0(rollup@4.20.0) start-server-and-test: specifier: ^2.0.3 version: 2.0.5 @@ -187,16 +187,16 @@ importers: version: 5.4.5 typescript-eslint: specifier: ^8.0.0-alpha.34 - version: 8.1.0(eslint@9.9.0)(typescript@5.4.5) + version: 8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) vite: specifier: ^5.2.3 - version: 5.4.0(@types/node@20.14.15) + version: 5.4.0(@types/node@20.14.15)(terser@5.31.6) vite-plugin-istanbul: specifier: ^6.0.0 - version: 6.0.2(vite@5.4.0) + version: 6.0.2(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6)) vitest: specifier: ^1.4.0 - version: 1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1) + version: 1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.6) packages/mermaid: dependencies: @@ -302,10 +302,10 @@ importers: version: 9.0.8 '@typescript-eslint/eslint-plugin': specifier: ^7.3.1 - version: 7.18.0(@typescript-eslint/parser@7.18.0)(eslint@8.57.0)(typescript@5.4.5) + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) '@typescript-eslint/parser': specifier: ^7.3.1 - version: 7.18.0(eslint@8.57.0)(typescript@5.4.5) + version: 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) ajv: specifier: ^8.12.0 version: 8.17.1 @@ -368,7 +368,7 @@ importers: version: 0.25.13(typescript@5.4.5) typedoc-plugin-markdown: specifier: ^3.17.1 - version: 3.17.1(typedoc@0.25.13) + version: 3.17.1(typedoc@0.25.13(typescript@5.4.5)) typescript: specifier: ~5.4.3 version: 5.4.5 @@ -380,10 +380,10 @@ importers: version: 5.0.0 vitepress: specifier: ^1.0.1 - version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(postcss@8.4.41)(search-insights@2.16.2)(typescript@5.4.5) + version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(axios@1.7.3)(postcss@8.4.41)(search-insights@2.16.2)(terser@5.31.6)(typescript@5.4.5) vitepress-plugin-search: specifier: 1.0.4-alpha.22 - version: 1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.1.4)(vue@3.4.37) + version: 1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(axios@1.7.3)(postcss@8.4.41)(search-insights@2.16.2)(terser@5.31.6)(typescript@5.4.5))(vue@3.4.37(typescript@5.4.5)) packages/mermaid-example-diagram: dependencies: @@ -407,31 +407,6 @@ importers: specifier: ^5.0.5 version: 5.0.10 - packages/mermaid-flowchart-elk: - dependencies: - d3: - specifier: ^7.9.0 - version: 7.9.0 - dagre-d3-es: - specifier: 7.0.10 - version: 7.0.10 - elkjs: - specifier: ^0.9.2 - version: 0.9.3 - khroma: - specifier: ^2.1.0 - version: 2.1.0 - devDependencies: - '@mermaid-chart/mermaid': - specifier: workspace:^ - version: link:../mermaid - concurrently: - specifier: ^8.2.2 - version: 8.2.2 - rimraf: - specifier: ^5.0.5 - version: 5.0.10 - packages/mermaid-layout-elk: dependencies: '@mermaid-chart/mermaid': @@ -464,7 +439,7 @@ importers: version: link:../.. '@vueuse/core': specifier: ^10.9.0 - version: 10.11.1(vue@3.4.37) + version: 10.11.1(vue@3.4.37(typescript@5.4.5)) font-awesome: specifier: ^4.7.0 version: 4.7.0 @@ -483,10 +458,10 @@ importers: version: 0.59.4 '@vite-pwa/vitepress': specifier: ^0.4.0 - version: 0.4.0(vite-plugin-pwa@0.19.8) + version: 0.4.0(vite-plugin-pwa@0.19.8(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0)) '@vitejs/plugin-vue': specifier: ^5.0.0 - version: 5.1.2(vite@5.4.0)(vue@3.4.37) + version: 5.1.2(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))(vue@3.4.37(typescript@5.4.5)) fast-glob: specifier: ^3.3.2 version: 3.3.2 @@ -498,19 +473,19 @@ importers: version: 1.1.2 unocss: specifier: ^0.59.0 - version: 0.59.4(postcss@8.4.41)(rollup@2.79.1)(vite@5.4.0) + version: 0.59.4(postcss@8.4.41)(rollup@2.79.1)(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6)) unplugin-vue-components: specifier: ^0.26.0 - version: 0.26.0(rollup@2.79.1)(vue@3.4.37) + version: 0.26.0(@babel/parser@7.25.3)(rollup@2.79.1)(vue@3.4.37(typescript@5.4.5)) vite: specifier: ^5.0.0 - version: 5.4.0(@types/node@20.14.15) + version: 5.4.0(@types/node@20.14.15)(terser@5.31.6) vite-plugin-pwa: specifier: ^0.19.7 - version: 0.19.8(vite@5.4.0)(workbox-build@7.1.1)(workbox-window@7.1.0) + version: 0.19.8(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) vitepress: specifier: 1.1.4 - version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(postcss@8.4.41)(search-insights@2.16.2)(typescript@5.4.5) + version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(axios@1.7.3)(postcss@8.4.41)(search-insights@2.16.2)(terser@5.31.6)(typescript@5.4.5) workbox-window: specifier: ^7.0.0 version: 7.1.0 @@ -1954,18 +1929,10 @@ packages: resolution: {integrity: sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/eslintrc@3.1.0': resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/js@9.9.0': resolution: {integrity: sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2016,19 +1983,10 @@ packages: peerDependencies: vue: ^3.2.0 - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - '@humanwhocodes/retry@0.3.0': resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} engines: {node: '>=18.18'} @@ -2949,9 +2907,6 @@ packages: resolution: {integrity: sha512-ba0lNI19awqZ5ZNKh6wCModMwoZs457StTebQ0q1NP58zSi2F6MOZRXwfKZy+jB78JNJ/WH8GSh2IQNzXX8Nag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ungap/structured-clone@1.2.0': - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - '@unocss/astro@0.59.4': resolution: {integrity: sha512-DU3OR5MMR1Uvvec4/wB9EetDASHRg19Moy6z/MiIhn8JWJ0QzWYgSeJcfUX8exomMYv6WUEQJL+CyLI34Wmn8w==} peerDependencies: @@ -4493,10 +4448,6 @@ packages: resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -4758,10 +4709,6 @@ packages: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-scope@8.0.2: resolution: {integrity: sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4774,11 +4721,6 @@ packages: resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - eslint@9.9.0: resolution: {integrity: sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4797,10 +4739,6 @@ packages: resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - esprima@1.1.1: resolution: {integrity: sha512-qxxB994/7NtERxgXdFgLHIs9M6bhLXc6qtUmWZ3L8+gTQ9qaoyki2887P2IqAYsoENyr8SUbTutStDniOHSDHg==} engines: {node: '>=0.4.0'} @@ -5002,10 +4940,6 @@ packages: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -5064,10 +4998,6 @@ packages: resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -8989,7 +8919,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@applitools/core@4.18.0(typescript@5.4.5)': + '@applitools/core@4.18.0(encoding@0.1.13)(typescript@5.4.5)': dependencies: '@applitools/core-base': 1.16.0 '@applitools/dom-capture': 11.3.0 @@ -9002,7 +8932,7 @@ snapshots: '@applitools/screenshoter': 3.8.35 '@applitools/snippets': 2.4.27 '@applitools/socket': 1.1.18 - '@applitools/spec-driver-webdriver': 1.1.11(webdriver@7.31.1) + '@applitools/spec-driver-webdriver': 1.1.11(webdriver@7.31.1(typescript@5.4.5)) '@applitools/ufg-client': 1.12.3 '@applitools/utils': 1.7.4 '@types/ws': 8.5.5 @@ -9055,7 +8985,7 @@ snapshots: '@applitools/logger': 2.0.18 '@applitools/req': 1.7.2 '@applitools/socket': 1.1.18 - '@applitools/spec-driver-webdriver': 1.1.11(webdriver@7.31.1) + '@applitools/spec-driver-webdriver': 1.1.11(webdriver@7.31.1(typescript@5.4.5)) '@applitools/tunnel-client': 1.5.7 '@applitools/utils': 1.7.4 abort-controller: 3.0.0 @@ -9091,10 +9021,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@applitools/eyes-cypress@3.44.6(typescript@5.4.5)': + '@applitools/eyes-cypress@3.44.6(encoding@0.1.13)(typescript@5.4.5)': dependencies: - '@applitools/core': 4.18.0(typescript@5.4.5) - '@applitools/eyes': 1.22.0(typescript@5.4.5) + '@applitools/core': 4.18.0(encoding@0.1.13)(typescript@5.4.5) + '@applitools/eyes': 1.22.0(encoding@0.1.13)(typescript@5.4.5) '@applitools/functional-commons': 1.6.0 '@applitools/logger': 2.0.18 '@applitools/utils': 1.7.4 @@ -9110,9 +9040,9 @@ snapshots: - typescript - utf-8-validate - '@applitools/eyes@1.22.0(typescript@5.4.5)': + '@applitools/eyes@1.22.0(encoding@0.1.13)(typescript@5.4.5)': dependencies: - '@applitools/core': 4.18.0(typescript@5.4.5) + '@applitools/core': 4.18.0(encoding@0.1.13)(typescript@5.4.5) '@applitools/logger': 2.0.18 '@applitools/utils': 1.7.4 transitivePeerDependencies: @@ -9184,7 +9114,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@applitools/spec-driver-webdriver@1.1.11(webdriver@7.31.1)': + '@applitools/spec-driver-webdriver@1.1.11(webdriver@7.31.1(typescript@5.4.5))': dependencies: '@applitools/driver': 1.18.0 '@applitools/utils': 1.7.4 @@ -10259,24 +10189,24 @@ snapshots: dependencies: import-meta-resolve: 4.1.0 - '@cspell/eslint-plugin@8.13.3(eslint@9.9.0)': + '@cspell/eslint-plugin@8.13.3(eslint@9.9.0(jiti@1.21.6))': dependencies: '@cspell/cspell-types': 8.13.3 '@cspell/url': 8.13.3 cspell-lib: 8.13.3 - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) synckit: 0.9.1 '@cspell/strong-weak-map@8.13.3': {} '@cspell/url@8.13.3': {} - '@cypress/code-coverage@3.12.45(@babel/core@7.25.2)(@babel/preset-env@7.25.3)(babel-loader@9.1.3)(cypress@13.13.2)(webpack@5.93.0)': + '@cypress/code-coverage@3.12.45(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(cypress@13.13.2)(webpack@5.93.0(esbuild@0.21.5))': dependencies: '@babel/core': 7.25.2 '@babel/preset-env': 7.25.3(@babel/core@7.25.2) - '@cypress/webpack-preprocessor': 6.0.2(@babel/core@7.25.2)(@babel/preset-env@7.25.3)(babel-loader@9.1.3)(webpack@5.93.0) - babel-loader: 9.1.3(@babel/core@7.25.2)(webpack@5.93.0) + '@cypress/webpack-preprocessor': 6.0.2(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(webpack@5.93.0(esbuild@0.21.5)) + babel-loader: 9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)) chalk: 4.1.2 cypress: 13.13.2 dayjs: 1.11.12 @@ -10286,7 +10216,7 @@ snapshots: istanbul-lib-coverage: 3.2.2 js-yaml: 4.1.0 nyc: 15.1.0 - webpack: 5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0) + webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: - supports-color @@ -10311,15 +10241,15 @@ snapshots: tunnel-agent: 0.6.0 uuid: 8.3.2 - '@cypress/webpack-preprocessor@6.0.2(@babel/core@7.25.2)(@babel/preset-env@7.25.3)(babel-loader@9.1.3)(webpack@5.93.0)': + '@cypress/webpack-preprocessor@6.0.2(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)))(webpack@5.93.0(esbuild@0.21.5))': dependencies: '@babel/core': 7.25.2 '@babel/preset-env': 7.25.3(@babel/core@7.25.2) - babel-loader: 9.1.3(@babel/core@7.25.2)(webpack@5.93.0) + babel-loader: 9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)) bluebird: 3.7.1 debug: 4.3.6(supports-color@8.1.1) lodash: 4.17.21 - webpack: 5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0) + webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: - supports-color @@ -10351,6 +10281,7 @@ snapshots: '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) '@docsearch/css': 3.6.1 algoliasearch: 4.24.0 + optionalDependencies: search-insights: 2.16.2 transitivePeerDependencies: - '@algolia/client-search' @@ -10507,14 +10438,9 @@ snapshots: '@esbuild/win32-x64@0.23.0': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.9.0(jiti@1.21.6))': dependencies: - eslint: 8.57.0 - eslint-visitor-keys: 3.4.3 - - '@eslint-community/eslint-utils@4.4.0(eslint@9.9.0)': - dependencies: - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.11.0': {} @@ -10527,20 +10453,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.12.6 - debug: 4.3.6(supports-color@8.1.1) - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 @@ -10555,8 +10467,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@8.57.0': {} - '@eslint/js@9.9.0': {} '@eslint/object-schema@2.1.4': {} @@ -10578,11 +10488,11 @@ snapshots: '@floating-ui/utils@0.2.7': {} - '@floating-ui/vue@1.1.4(vue@3.4.37)': + '@floating-ui/vue@1.1.4(vue@3.4.37(typescript@5.4.5))': dependencies: '@floating-ui/dom': 1.6.10 '@floating-ui/utils': 0.2.7 - vue-demi: 0.14.10(vue@3.4.37) + vue-demi: 0.14.10(vue@3.4.37(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -10593,12 +10503,12 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 - '@headlessui-float/vue@0.14.1(@headlessui/vue@1.7.22)(vue@3.4.37)': + '@headlessui-float/vue@0.14.1(@headlessui/vue@1.7.22(vue@3.4.37(typescript@5.4.5)))(vue@3.4.37(typescript@5.4.5))': dependencies: '@floating-ui/core': 1.6.7 '@floating-ui/dom': 1.6.10 - '@floating-ui/vue': 1.1.4(vue@3.4.37) - '@headlessui/vue': 1.7.22(vue@3.4.37) + '@floating-ui/vue': 1.1.4(vue@3.4.37(typescript@5.4.5)) + '@headlessui/vue': 1.7.22(vue@3.4.37(typescript@5.4.5)) vue: 3.4.37(typescript@5.4.5) transitivePeerDependencies: - '@vue/composition-api' @@ -10607,23 +10517,13 @@ snapshots: dependencies: tailwindcss: 3.4.9 - '@headlessui/vue@1.7.22(vue@3.4.37)': + '@headlessui/vue@1.7.22(vue@3.4.37(typescript@5.4.5))': dependencies: - '@tanstack/vue-virtual': 3.8.6(vue@3.4.37) + '@tanstack/vue-virtual': 3.8.6(vue@3.4.37(typescript@5.4.5)) vue: 3.4.37(typescript@5.4.5) - '@humanwhocodes/config-array@0.11.14': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.6(supports-color@8.1.1) - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.3': {} - '@humanwhocodes/retry@0.3.0': {} '@iconify-json/carbon@1.1.37': @@ -10956,12 +10856,14 @@ snapshots: '@polka/url@1.0.0-next.25': {} - '@rollup/plugin-babel@5.3.1(@babel/core@7.25.2)(rollup@2.79.1)': + '@rollup/plugin-babel@5.3.1(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@2.79.1)': dependencies: '@babel/core': 7.25.2 '@babel/helper-module-imports': 7.24.7 '@rollup/pluginutils': 3.1.0(rollup@2.79.1) rollup: 2.79.1 + optionalDependencies: + '@types/babel__core': 7.20.5 transitivePeerDependencies: - supports-color @@ -10973,6 +10875,7 @@ snapshots: is-builtin-module: 3.2.1 is-module: 1.0.0 resolve: 1.22.8 + optionalDependencies: rollup: 2.79.1 '@rollup/plugin-replace@2.4.2(rollup@2.79.1)': @@ -10983,16 +10886,20 @@ snapshots: '@rollup/plugin-terser@0.4.4(rollup@2.79.1)': dependencies: - rollup: 2.79.1 serialize-javascript: 6.0.2 smob: 1.5.0 terser: 5.31.6 + optionalDependencies: + rollup: 2.79.1 - '@rollup/plugin-typescript@11.1.6(typescript@5.4.5)': + '@rollup/plugin-typescript@11.1.6(rollup@4.20.0)(tslib@2.6.3)(typescript@5.4.5)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@2.79.1) + '@rollup/pluginutils': 5.1.0(rollup@4.20.0) resolve: 1.22.8 typescript: 5.4.5 + optionalDependencies: + rollup: 4.20.0 + tslib: 2.6.3 '@rollup/pluginutils@3.1.0(rollup@2.79.1)': dependencies: @@ -11006,8 +10913,17 @@ snapshots: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 + optionalDependencies: rollup: 2.79.1 + '@rollup/pluginutils@5.1.0(rollup@4.20.0)': + dependencies: + '@types/estree': 1.0.5 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 4.20.0 + '@rollup/rollup-android-arm-eabi@4.20.0': optional: true @@ -11099,7 +11015,7 @@ snapshots: '@tanstack/virtual-core@3.8.6': {} - '@tanstack/vue-virtual@3.8.6(vue@3.4.37)': + '@tanstack/vue-virtual@3.8.6(vue@3.4.37(typescript@5.4.5))': dependencies: '@tanstack/virtual-core': 3.8.6 vue: 3.4.37(typescript@5.4.5) @@ -11517,60 +11433,64 @@ snapshots: '@types/node': 20.14.15 optional: true - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0)(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/parser': 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/type-utils': 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) + '@typescript-eslint/utils': 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 8.57.0 + eslint: 9.9.0(jiti@1.21.6) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.1.0(@typescript-eslint/parser@8.1.0)(eslint@9.9.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.1.0(eslint@9.9.0)(typescript@5.4.5) + '@typescript-eslint/parser': 8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) '@typescript-eslint/scope-manager': 8.1.0 - '@typescript-eslint/type-utils': 8.1.0(eslint@9.9.0)(typescript@5.4.5) - '@typescript-eslint/utils': 8.1.0(eslint@9.9.0)(typescript@5.4.5) + '@typescript-eslint/type-utils': 8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) + '@typescript-eslint/utils': 8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) '@typescript-eslint/visitor-keys': 8.1.0 - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/parser@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.18.0 debug: 4.3.6(supports-color@8.1.1) - eslint: 8.57.0 + eslint: 9.9.0(jiti@1.21.6) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.1.0(eslint@9.9.0)(typescript@5.4.5)': + '@typescript-eslint/parser@8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 8.1.0 '@typescript-eslint/types': 8.1.0 '@typescript-eslint/typescript-estree': 8.1.0(typescript@5.4.5) '@typescript-eslint/visitor-keys': 8.1.0 debug: 4.3.6(supports-color@8.1.1) - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color @@ -11585,23 +11505,25 @@ snapshots: '@typescript-eslint/types': 8.1.0 '@typescript-eslint/visitor-keys': 8.1.0 - '@typescript-eslint/type-utils@7.18.0(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/type-utils@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.4.5) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/utils': 7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) debug: 4.3.6(supports-color@8.1.1) - eslint: 8.57.0 + eslint: 9.9.0(jiti@1.21.6) ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.1.0(eslint@9.9.0)(typescript@5.4.5)': + '@typescript-eslint/type-utils@8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: '@typescript-eslint/typescript-estree': 8.1.0(typescript@5.4.5) - '@typescript-eslint/utils': 8.1.0(eslint@9.9.0)(typescript@5.4.5) + '@typescript-eslint/utils': 8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) debug: 4.3.6(supports-color@8.1.1) ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - eslint @@ -11621,6 +11543,7 @@ snapshots: minimatch: 9.0.5 semver: 7.6.3 ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color @@ -11635,28 +11558,29 @@ snapshots: minimatch: 9.0.5 semver: 7.6.3 ts-api-utils: 1.3.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.18.0(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/utils@7.18.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0(jiti@1.21.6)) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.4.5) - eslint: 8.57.0 + eslint: 9.9.0(jiti@1.21.6) transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@8.1.0(eslint@9.9.0)(typescript@5.4.5)': + '@typescript-eslint/utils@8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0(jiti@1.21.6)) '@typescript-eslint/scope-manager': 8.1.0 '@typescript-eslint/types': 8.1.0 '@typescript-eslint/typescript-estree': 8.1.0(typescript@5.4.5) - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) transitivePeerDependencies: - supports-color - typescript @@ -11671,14 +11595,13 @@ snapshots: '@typescript-eslint/types': 8.1.0 eslint-visitor-keys: 3.4.3 - '@ungap/structured-clone@1.2.0': {} - - '@unocss/astro@0.59.4(rollup@2.79.1)(vite@5.4.0)': + '@unocss/astro@0.59.4(rollup@2.79.1)(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))': dependencies: '@unocss/core': 0.59.4 '@unocss/reset': 0.59.4 - '@unocss/vite': 0.59.4(rollup@2.79.1)(vite@5.4.0) - vite: 5.4.0(@types/node@20.14.15) + '@unocss/vite': 0.59.4(rollup@2.79.1)(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6)) + optionalDependencies: + vite: 5.4.0(@types/node@20.14.15)(terser@5.31.6) transitivePeerDependencies: - rollup @@ -11809,7 +11732,7 @@ snapshots: dependencies: '@unocss/core': 0.59.4 - '@unocss/vite@0.59.4(rollup@2.79.1)(vite@5.4.0)': + '@unocss/vite@0.59.4(rollup@2.79.1)(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))': dependencies: '@ampproject/remapping': 2.3.0 '@rollup/pluginutils': 5.1.0(rollup@2.79.1) @@ -11821,20 +11744,20 @@ snapshots: chokidar: 3.6.0 fast-glob: 3.3.2 magic-string: 0.30.11 - vite: 5.4.0(@types/node@20.14.15) + vite: 5.4.0(@types/node@20.14.15)(terser@5.31.6) transitivePeerDependencies: - rollup - '@vite-pwa/vitepress@0.4.0(vite-plugin-pwa@0.19.8)': + '@vite-pwa/vitepress@0.4.0(vite-plugin-pwa@0.19.8(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0))': dependencies: - vite-plugin-pwa: 0.19.8(vite@5.4.0)(workbox-build@7.1.1)(workbox-window@7.1.0) + vite-plugin-pwa: 0.19.8(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) - '@vitejs/plugin-vue@5.1.2(vite@5.4.0)(vue@3.4.37)': + '@vitejs/plugin-vue@5.1.2(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))(vue@3.4.37(typescript@5.4.5))': dependencies: - vite: 5.4.0(@types/node@20.14.15) + vite: 5.4.0(@types/node@20.14.15)(terser@5.31.6) vue: 3.4.37(typescript@5.4.5) - '@vitest/coverage-v8@1.6.0(vitest@1.6.0)': + '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.6))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -11849,7 +11772,7 @@ snapshots: std-env: 3.7.0 strip-literal: 2.1.0 test-exclude: 6.0.0 - vitest: 1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1) + vitest: 1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.6) transitivePeerDependencies: - supports-color @@ -11884,7 +11807,7 @@ snapshots: pathe: 1.1.2 picocolors: 1.0.1 sirv: 2.0.4 - vitest: 1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1) + vitest: 1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.6) '@vitest/utils@1.6.0': dependencies: @@ -11893,7 +11816,7 @@ snapshots: loupe: 2.3.7 pretty-format: 29.7.0 - '@vue/compat@3.4.37(vue@3.4.37)': + '@vue/compat@3.4.37(vue@3.4.37(typescript@5.4.5))': dependencies: '@babel/parser': 7.25.3 estree-walker: 2.0.2 @@ -11966,7 +11889,7 @@ snapshots: '@vue/shared': 3.4.37 csstype: 3.1.3 - '@vue/server-renderer@3.4.37(vue@3.4.37)': + '@vue/server-renderer@3.4.37(vue@3.4.37(typescript@5.4.5))': dependencies: '@vue/compiler-ssr': 3.4.37 '@vue/shared': 3.4.37 @@ -11974,31 +11897,33 @@ snapshots: '@vue/shared@3.4.37': {} - '@vueuse/core@10.11.1(vue@3.4.37)': + '@vueuse/core@10.11.1(vue@3.4.37(typescript@5.4.5))': dependencies: '@types/web-bluetooth': 0.0.20 '@vueuse/metadata': 10.11.1 - '@vueuse/shared': 10.11.1(vue@3.4.37) - vue-demi: 0.14.10(vue@3.4.37) + '@vueuse/shared': 10.11.1(vue@3.4.37(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.4.37(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/integrations@10.11.1(focus-trap@7.5.4)(vue@3.4.37)': + '@vueuse/integrations@10.11.1(axios@1.7.3)(focus-trap@7.5.4)(vue@3.4.37(typescript@5.4.5))': dependencies: - '@vueuse/core': 10.11.1(vue@3.4.37) - '@vueuse/shared': 10.11.1(vue@3.4.37) + '@vueuse/core': 10.11.1(vue@3.4.37(typescript@5.4.5)) + '@vueuse/shared': 10.11.1(vue@3.4.37(typescript@5.4.5)) + vue-demi: 0.14.10(vue@3.4.37(typescript@5.4.5)) + optionalDependencies: + axios: 1.7.3(debug@4.3.6) focus-trap: 7.5.4 - vue-demi: 0.14.10(vue@3.4.37) transitivePeerDependencies: - '@vue/composition-api' - vue '@vueuse/metadata@10.11.1': {} - '@vueuse/shared@10.11.1(vue@3.4.37)': + '@vueuse/shared@10.11.1(vue@3.4.37(typescript@5.4.5))': dependencies: - vue-demi: 0.14.10(vue@3.4.37) + vue-demi: 0.14.10(vue@3.4.37(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -12027,6 +11952,7 @@ snapshots: dependencies: '@types/node': 18.19.44 got: 11.8.6 + optionalDependencies: typescript: 5.4.5 '@wdio/utils@7.30.2(typescript@5.4.5)': @@ -12113,19 +12039,20 @@ snapshots: '@webassemblyjs/ast': 1.12.1 '@xtuc/long': 4.2.2 - '@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0)(webpack@5.93.0)': + '@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0))(webpack@5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0))': dependencies: webpack: 5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0) webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0) - '@webpack-cli/info@1.5.0(webpack-cli@4.10.0)': + '@webpack-cli/info@1.5.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0))': dependencies: envinfo: 7.13.0 webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0) - '@webpack-cli/serve@1.7.0(webpack-cli@4.10.0)(webpack-dev-server@4.15.2)': + '@webpack-cli/serve@1.7.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0))(webpack-dev-server@4.15.2(webpack-cli@4.10.0)(webpack@5.93.0))': dependencies: webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0) + optionalDependencies: webpack-dev-server: 4.15.2(webpack-cli@4.10.0)(webpack@5.93.0) '@xmldom/xmldom@0.8.10': {} @@ -12136,12 +12063,12 @@ snapshots: '@zenuml/core@3.24.2(typescript@5.4.5)': dependencies: - '@headlessui-float/vue': 0.14.1(@headlessui/vue@1.7.22)(vue@3.4.37) + '@headlessui-float/vue': 0.14.1(@headlessui/vue@1.7.22(vue@3.4.37(typescript@5.4.5)))(vue@3.4.37(typescript@5.4.5)) '@headlessui/tailwindcss': 0.2.1(tailwindcss@3.4.9) - '@headlessui/vue': 1.7.22(vue@3.4.37) + '@headlessui/vue': 1.7.22(vue@3.4.37(typescript@5.4.5)) '@types/assert': 1.5.10 '@types/ramda': 0.28.25 - '@vue/compat': 3.4.37(vue@3.4.37) + '@vue/compat': 3.4.37(vue@3.4.37(typescript@5.4.5)) antlr4: 4.11.0 color-string: 1.9.1 dom-to-image-more: 2.16.0 @@ -12156,7 +12083,7 @@ snapshots: ramda: 0.28.0 tailwindcss: 3.4.9 vue: 3.4.37(typescript@5.4.5) - vuex: 4.1.0(vue@3.4.37) + vuex: 4.1.0(vue@3.4.37(typescript@5.4.5)) transitivePeerDependencies: - '@vue/composition-api' - ts-node @@ -12214,7 +12141,7 @@ snapshots: indent-string: 5.0.0 ajv-formats@2.1.1(ajv@8.17.1): - dependencies: + optionalDependencies: ajv: 8.17.1 ajv-keywords@3.5.2(ajv@6.12.6): @@ -12419,12 +12346,12 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0): + babel-loader@9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)): dependencies: '@babel/core': 7.25.2 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0) + webpack: 5.93.0(esbuild@0.21.5) babel-plugin-istanbul@6.1.1: dependencies: @@ -13130,13 +13057,13 @@ snapshots: cuint@0.2.2: {} - cypress-image-snapshot@4.0.1(cypress@13.13.2)(jest@29.7.0): + cypress-image-snapshot@4.0.1(cypress@13.13.2)(jest@29.7.0(@types/node@20.14.15)): dependencies: chalk: 2.4.2 cypress: 13.13.2 fs-extra: 7.0.1 glob: 7.2.3 - jest-image-snapshot: 4.2.0(jest@29.7.0) + jest-image-snapshot: 4.2.0(jest@29.7.0(@types/node@20.14.15)) pkg-dir: 3.0.0 term-img: 4.1.0 transitivePeerDependencies: @@ -13415,6 +13342,7 @@ snapshots: debug@3.2.7(supports-color@8.1.1): dependencies: ms: 2.1.3 + optionalDependencies: supports-color: 8.1.1 debug@4.3.3: @@ -13428,6 +13356,7 @@ snapshots: debug@4.3.6(supports-color@8.1.1): dependencies: ms: 2.1.2 + optionalDependencies: supports-color: 8.1.1 decamelize@1.2.0: {} @@ -13518,10 +13447,6 @@ snapshots: dependencies: '@leichtgewicht/ip-codec': 2.0.5 - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -13786,36 +13711,38 @@ snapshots: optionalDependencies: source-map: 0.1.43 - eslint-config-prettier@9.1.0(eslint@9.9.0): + eslint-config-prettier@9.1.0(eslint@9.9.0(jiti@1.21.6)): dependencies: - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) - eslint-plugin-cypress@3.5.0(eslint@9.9.0): + eslint-plugin-cypress@3.5.0(eslint@9.9.0(jiti@1.21.6)): dependencies: - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) globals: 13.24.0 eslint-plugin-html@8.1.1: dependencies: htmlparser2: 9.1.0 - eslint-plugin-jest@28.8.0(eslint@9.9.0)(jest@29.7.0)(typescript@5.4.5): + eslint-plugin-jest@28.8.0(@typescript-eslint/eslint-plugin@8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.9.0(jiti@1.21.6))(jest@29.7.0(@types/node@20.14.15))(typescript@5.4.5): dependencies: - '@typescript-eslint/utils': 8.1.0(eslint@9.9.0)(typescript@5.4.5) - eslint: 9.9.0 + '@typescript-eslint/utils': 8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) + eslint: 9.9.0(jiti@1.21.6) + optionalDependencies: + '@typescript-eslint/eslint-plugin': 8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) jest: 29.7.0(@types/node@20.14.15) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-jsdoc@48.11.0(eslint@9.9.0): + eslint-plugin-jsdoc@48.11.0(eslint@9.9.0(jiti@1.21.6)): dependencies: '@es-joy/jsdoccomment': 0.46.0 are-docs-informative: 0.0.2 comment-parser: 1.4.1 debug: 4.3.6(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) espree: 10.1.0 esquery: 1.6.0 parse-imports: 2.1.1 @@ -13830,14 +13757,14 @@ snapshots: lodash: 4.17.21 vscode-json-languageservice: 4.2.1 - eslint-plugin-lodash@8.0.0(eslint@9.9.0): + eslint-plugin-lodash@8.0.0(eslint@9.9.0(jiti@1.21.6)): dependencies: - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) lodash: 4.17.21 - eslint-plugin-markdown@5.1.0(eslint@9.9.0): + eslint-plugin-markdown@5.1.0(eslint@9.9.0(jiti@1.21.6)): dependencies: - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) mdast-util-from-markdown: 0.8.5 transitivePeerDependencies: - supports-color @@ -13849,14 +13776,14 @@ snapshots: '@microsoft/tsdoc': 0.15.0 '@microsoft/tsdoc-config': 0.17.0 - eslint-plugin-unicorn@55.0.0(eslint@9.9.0): + eslint-plugin-unicorn@55.0.0(eslint@9.9.0(jiti@1.21.6)): dependencies: '@babel/helper-validator-identifier': 7.24.7 - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0(jiti@1.21.6)) ci-info: 4.0.0 clean-regexp: 1.0.0 core-js-compat: 3.38.0 - eslint: 9.9.0 + eslint: 9.9.0(jiti@1.21.6) esquery: 1.6.0 globals: 15.9.0 indent-string: 4.0.0 @@ -13874,11 +13801,6 @@ snapshots: esrecurse: 4.3.0 estraverse: 4.3.0 - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-scope@8.0.2: dependencies: esrecurse: 4.3.0 @@ -13888,52 +13810,9 @@ snapshots: eslint-visitor-keys@4.0.0: {} - eslint@8.57.0: + eslint@9.9.0(jiti@1.21.6): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.11.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.6(supports-color@8.1.1) - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - - eslint@9.9.0: - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0(jiti@1.21.6)) '@eslint-community/regexpp': 4.11.0 '@eslint/config-array': 0.17.1 '@eslint/eslintrc': 3.1.0 @@ -13967,6 +13846,8 @@ snapshots: optionator: 0.9.4 strip-ansi: 6.0.1 text-table: 0.2.0 + optionalDependencies: + jiti: 1.21.6 transitivePeerDependencies: - supports-color @@ -13983,12 +13864,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 4.0.0 - espree@9.6.1: - dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) - eslint-visitor-keys: 3.4.3 - esprima@1.1.1: {} esprima@4.0.1: {} @@ -14257,10 +14132,6 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -14338,12 +14209,6 @@ snapshots: locate-path: 7.2.0 path-exists: 5.0.0 - flat-cache@3.2.0: - dependencies: - flatted: 3.3.1 - keyv: 4.5.4 - rimraf: 3.0.2 - flat-cache@4.0.1: dependencies: flatted: 3.3.1 @@ -14367,7 +14232,7 @@ snapshots: tabbable: 6.2.0 follow-redirects@1.15.6(debug@4.3.6): - dependencies: + optionalDependencies: debug: 4.3.6(supports-color@8.1.1) font-awesome@4.7.0: {} @@ -14749,12 +14614,13 @@ snapshots: http-proxy-middleware@2.0.6(@types/express@4.17.21): dependencies: - '@types/express': 4.17.21 '@types/http-proxy': 1.17.15 http-proxy: 1.18.1 is-glob: 4.0.3 is-plain-obj: 3.0.0 micromatch: 4.0.7 + optionalDependencies: + '@types/express': 4.17.21 transitivePeerDependencies: - debug @@ -15179,7 +15045,6 @@ snapshots: '@babel/core': 7.25.2 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 babel-jest: 29.7.0(@babel/core@7.25.2) chalk: 4.1.2 ci-info: 3.9.0 @@ -15199,6 +15064,8 @@ snapshots: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.14.15 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -15249,7 +15116,7 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - jest-image-snapshot@4.2.0(jest@29.7.0): + jest-image-snapshot@4.2.0(jest@29.7.0(@types/node@20.14.15)): dependencies: chalk: 1.1.3 get-stdin: 5.0.1 @@ -15293,7 +15160,7 @@ snapshots: jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - dependencies: + optionalDependencies: jest-resolve: 29.7.0 jest-regex-util@29.6.3: {} @@ -15690,13 +15557,14 @@ snapshots: dependencies: cli-truncate: 2.1.0 colorette: 2.0.20 - enquirer: 2.4.1 log-update: 4.0.0 p-map: 4.0.0 rfdc: 1.4.1 rxjs: 7.8.1 through: 2.3.8 wrap-ansi: 7.0.0 + optionalDependencies: + enquirer: 2.4.1 listr2@8.2.4: dependencies: @@ -16308,8 +16176,9 @@ snapshots: node-fetch@2.6.7(encoding@0.1.13): dependencies: - encoding: 0.1.13 whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 node-fetch@3.3.1: dependencies: @@ -16720,8 +16589,9 @@ snapshots: postcss-load-config@4.0.2(postcss@8.4.41): dependencies: lilconfig: 3.1.2 - postcss: 8.4.41 yaml: 2.5.0 + optionalDependencies: + postcss: 8.4.41 postcss-nested@6.2.0(postcss@8.4.41): dependencies: @@ -17051,12 +16921,14 @@ snapshots: robust-predicates@3.0.2: {} - rollup-plugin-visualizer@5.12.0: + rollup-plugin-visualizer@5.12.0(rollup@4.20.0): dependencies: open: 8.4.2 picomatch: 2.3.1 source-map: 0.7.4 yargs: 17.7.2 + optionalDependencies: + rollup: 4.20.0 rollup@2.79.1: optionalDependencies: @@ -17696,15 +17568,27 @@ snapshots: ansi-escapes: 4.3.2 iterm2-version: 4.2.0 - terser-webpack-plugin@5.3.10(esbuild@0.21.5)(webpack@5.93.0): + terser-webpack-plugin@5.3.10(esbuild@0.21.5)(webpack@5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0)): dependencies: '@jridgewell/trace-mapping': 0.3.25 - esbuild: 0.21.5 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.31.6 webpack: 5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0) + optionalDependencies: + esbuild: 0.21.5 + + terser-webpack-plugin@5.3.10(esbuild@0.21.5)(webpack@5.93.0(esbuild@0.21.5)): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.31.6 + webpack: 5.93.0(esbuild@0.21.5) + optionalDependencies: + esbuild: 0.21.5 terser@5.31.6: dependencies: @@ -17879,7 +17763,7 @@ snapshots: dependencies: is-typedarray: 1.0.0 - typedoc-plugin-markdown@3.17.1(typedoc@0.25.13): + typedoc-plugin-markdown@3.17.1(typedoc@0.25.13(typescript@5.4.5)): dependencies: handlebars: 4.7.8 typedoc: 0.25.13(typescript@5.4.5) @@ -17892,11 +17776,12 @@ snapshots: shiki: 0.14.7 typescript: 5.4.5 - typescript-eslint@8.1.0(eslint@9.9.0)(typescript@5.4.5): + typescript-eslint@8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5): dependencies: - '@typescript-eslint/eslint-plugin': 8.1.0(@typescript-eslint/parser@8.1.0)(eslint@9.9.0)(typescript@5.4.5) - '@typescript-eslint/parser': 8.1.0(eslint@9.9.0)(typescript@5.4.5) - '@typescript-eslint/utils': 8.1.0(eslint@9.9.0)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 8.1.0(@typescript-eslint/parser@8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) + '@typescript-eslint/parser': 8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) + '@typescript-eslint/utils': 8.1.0(eslint@9.9.0(jiti@1.21.6))(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - eslint @@ -17999,9 +17884,9 @@ snapshots: universalify@2.0.1: {} - unocss@0.59.4(postcss@8.4.41)(rollup@2.79.1)(vite@5.4.0): + unocss@0.59.4(postcss@8.4.41)(rollup@2.79.1)(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6)): dependencies: - '@unocss/astro': 0.59.4(rollup@2.79.1)(vite@5.4.0) + '@unocss/astro': 0.59.4(rollup@2.79.1)(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6)) '@unocss/cli': 0.59.4(rollup@2.79.1) '@unocss/core': 0.59.4 '@unocss/extractor-arbitrary-variants': 0.59.4 @@ -18020,8 +17905,9 @@ snapshots: '@unocss/transformer-compile-class': 0.59.4 '@unocss/transformer-directives': 0.59.4 '@unocss/transformer-variant-group': 0.59.4 - '@unocss/vite': 0.59.4(rollup@2.79.1)(vite@5.4.0) - vite: 5.4.0(@types/node@20.14.15) + '@unocss/vite': 0.59.4(rollup@2.79.1)(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6)) + optionalDependencies: + vite: 5.4.0(@types/node@20.14.15)(terser@5.31.6) transitivePeerDependencies: - postcss - rollup @@ -18029,7 +17915,7 @@ snapshots: unpipe@1.0.0: {} - unplugin-vue-components@0.26.0(rollup@2.79.1)(vue@3.4.37): + unplugin-vue-components@0.26.0(@babel/parser@7.25.3)(rollup@2.79.1)(vue@3.4.37(typescript@5.4.5)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.1.0(rollup@2.79.1) @@ -18042,6 +17928,8 @@ snapshots: resolve: 1.22.8 unplugin: 1.12.1 vue: 3.4.37(typescript@5.4.5) + optionalDependencies: + '@babel/parser': 7.25.3 transitivePeerDependencies: - rollup - supports-color @@ -18110,13 +17998,13 @@ snapshots: unist-util-stringify-position: 4.0.0 vfile-message: 4.0.2 - vite-node@1.6.0(@types/node@20.14.15): + vite-node@1.6.0(@types/node@20.14.15)(terser@5.31.6): dependencies: cac: 6.7.14 debug: 4.3.6(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.4.0(@types/node@20.14.15) + vite: 5.4.0(@types/node@20.14.15)(terser@5.31.6) transitivePeerDependencies: - '@types/node' - less @@ -18128,7 +18016,7 @@ snapshots: - supports-color - terser - vite-plugin-istanbul@6.0.2(vite@5.4.0): + vite-plugin-istanbul@6.0.2(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6)): dependencies: '@istanbuljs/load-nyc-config': 1.1.0 espree: 10.1.0 @@ -18136,58 +18024,60 @@ snapshots: picocolors: 1.0.1 source-map: 0.7.4 test-exclude: 6.0.0 - vite: 5.4.0(@types/node@20.14.15) + vite: 5.4.0(@types/node@20.14.15)(terser@5.31.6) transitivePeerDependencies: - supports-color - vite-plugin-pwa@0.19.8(vite@5.4.0)(workbox-build@7.1.1)(workbox-window@7.1.0): + vite-plugin-pwa@0.19.8(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0): dependencies: debug: 4.3.6(supports-color@8.1.1) fast-glob: 3.3.2 pretty-bytes: 6.1.1 - vite: 5.4.0(@types/node@20.14.15) - workbox-build: 7.1.1 + vite: 5.4.0(@types/node@20.14.15)(terser@5.31.6) + workbox-build: 7.1.1(@types/babel__core@7.20.5) workbox-window: 7.1.0 transitivePeerDependencies: - supports-color - vite@5.4.0(@types/node@20.14.15): + vite@5.4.0(@types/node@20.14.15)(terser@5.31.6): dependencies: - '@types/node': 20.14.15 esbuild: 0.21.5 postcss: 8.4.41 rollup: 4.20.0 optionalDependencies: + '@types/node': 20.14.15 fsevents: 2.3.3 + terser: 5.31.6 - vitepress-plugin-search@1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.1.4)(vue@3.4.37): + vitepress-plugin-search@1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(axios@1.7.3)(postcss@8.4.41)(search-insights@2.16.2)(terser@5.31.6)(typescript@5.4.5))(vue@3.4.37(typescript@5.4.5)): dependencies: '@types/flexsearch': 0.7.6 '@types/markdown-it': 12.2.3 flexsearch: 0.7.43 glob-to-regexp: 0.4.1 markdown-it: 13.0.2 - vitepress: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(postcss@8.4.41)(search-insights@2.16.2)(typescript@5.4.5) + vitepress: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(axios@1.7.3)(postcss@8.4.41)(search-insights@2.16.2)(terser@5.31.6)(typescript@5.4.5) vue: 3.4.37(typescript@5.4.5) - vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(postcss@8.4.41)(search-insights@2.16.2)(typescript@5.4.5): + vitepress@1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.15)(axios@1.7.3)(postcss@8.4.41)(search-insights@2.16.2)(terser@5.31.6)(typescript@5.4.5): dependencies: '@docsearch/css': 3.6.1 '@docsearch/js': 3.6.1(@algolia/client-search@4.24.0)(search-insights@2.16.2) '@shikijs/core': 1.12.1 '@shikijs/transformers': 1.12.1 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.1.2(vite@5.4.0)(vue@3.4.37) + '@vitejs/plugin-vue': 5.1.2(vite@5.4.0(@types/node@20.14.15)(terser@5.31.6))(vue@3.4.37(typescript@5.4.5)) '@vue/devtools-api': 7.3.8 - '@vueuse/core': 10.11.1(vue@3.4.37) - '@vueuse/integrations': 10.11.1(focus-trap@7.5.4)(vue@3.4.37) + '@vueuse/core': 10.11.1(vue@3.4.37(typescript@5.4.5)) + '@vueuse/integrations': 10.11.1(axios@1.7.3)(focus-trap@7.5.4)(vue@3.4.37(typescript@5.4.5)) focus-trap: 7.5.4 mark.js: 8.11.1 minisearch: 6.3.0 - postcss: 8.4.41 shiki: 1.12.1 - vite: 5.4.0(@types/node@20.14.15) + vite: 5.4.0(@types/node@20.14.15)(terser@5.31.6) vue: 3.4.37(typescript@5.4.5) + optionalDependencies: + postcss: 8.4.41 transitivePeerDependencies: - '@algolia/client-search' - '@types/node' @@ -18216,20 +18106,17 @@ snapshots: - typescript - universal-cookie - vitest@1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1): + vitest@1.6.0(@types/node@20.14.15)(@vitest/ui@1.6.0)(jsdom@24.1.1)(terser@5.31.6): dependencies: - '@types/node': 20.14.15 '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 '@vitest/snapshot': 1.6.0 '@vitest/spy': 1.6.0 - '@vitest/ui': 1.6.0(vitest@1.6.0) '@vitest/utils': 1.6.0 acorn-walk: 8.3.3 chai: 4.5.0 debug: 4.3.6(supports-color@8.1.1) execa: 8.0.1 - jsdom: 24.1.1 local-pkg: 0.5.0 magic-string: 0.30.11 pathe: 1.1.2 @@ -18238,9 +18125,13 @@ snapshots: strip-literal: 2.1.0 tinybench: 2.9.0 tinypool: 0.8.4 - vite: 5.4.0(@types/node@20.14.15) - vite-node: 1.6.0(@types/node@20.14.15) + vite: 5.4.0(@types/node@20.14.15)(terser@5.31.6) + vite-node: 1.6.0(@types/node@20.14.15)(terser@5.31.6) why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.14.15 + '@vitest/ui': 1.6.0(vitest@1.6.0) + jsdom: 24.1.1 transitivePeerDependencies: - less - lightningcss @@ -18282,7 +18173,7 @@ snapshots: vscode-uri@3.0.8: {} - vue-demi@0.14.10(vue@3.4.37): + vue-demi@0.14.10(vue@3.4.37(typescript@5.4.5)): dependencies: vue: 3.4.37(typescript@5.4.5) @@ -18291,11 +18182,12 @@ snapshots: '@vue/compiler-dom': 3.4.37 '@vue/compiler-sfc': 3.4.37 '@vue/runtime-dom': 3.4.37 - '@vue/server-renderer': 3.4.37(vue@3.4.37) + '@vue/server-renderer': 3.4.37(vue@3.4.37(typescript@5.4.5)) '@vue/shared': 3.4.37 + optionalDependencies: typescript: 5.4.5 - vuex@4.1.0(vue@3.4.37): + vuex@4.1.0(vue@3.4.37(typescript@5.4.5)): dependencies: '@vue/devtools-api': 6.6.3 vue: 3.4.37(typescript@5.4.5) @@ -18352,9 +18244,9 @@ snapshots: webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0): dependencies: '@discoveryjs/json-ext': 0.5.7 - '@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0)(webpack@5.93.0) - '@webpack-cli/info': 1.5.0(webpack-cli@4.10.0) - '@webpack-cli/serve': 1.7.0(webpack-cli@4.10.0)(webpack-dev-server@4.15.2) + '@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0))(webpack@5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0)) + '@webpack-cli/info': 1.5.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0)) + '@webpack-cli/serve': 1.7.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0))(webpack-dev-server@4.15.2(webpack-cli@4.10.0)(webpack@5.93.0)) colorette: 2.0.20 commander: 7.2.0 cross-spawn: 7.0.3 @@ -18363,10 +18255,11 @@ snapshots: interpret: 2.2.0 rechoir: 0.7.1 webpack: 5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0) - webpack-dev-server: 4.15.2(webpack-cli@4.10.0)(webpack@5.93.0) webpack-merge: 5.10.0 + optionalDependencies: + webpack-dev-server: 4.15.2(webpack-cli@4.10.0)(webpack@5.93.0) - webpack-dev-middleware@5.3.4(webpack@5.93.0): + webpack-dev-middleware@5.3.4(webpack@5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0)): dependencies: colorette: 2.0.20 memfs: 3.5.3 @@ -18405,10 +18298,11 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 + webpack-dev-middleware: 5.3.4(webpack@5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0)) + ws: 8.18.0 + optionalDependencies: webpack: 5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0) webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0) - webpack-dev-middleware: 5.3.4(webpack@5.93.0) - ws: 8.18.0 transitivePeerDependencies: - bufferutil - debug @@ -18425,6 +18319,37 @@ snapshots: webpack-virtual-modules@0.6.2: {} + webpack@5.93.0(esbuild@0.21.5): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + browserslist: 4.23.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(esbuild@0.21.5)(webpack@5.93.0(esbuild@0.21.5)) + watchpack: 2.4.1 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0): dependencies: '@types/eslint-scope': 3.7.7 @@ -18448,10 +18373,11 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(esbuild@0.21.5)(webpack@5.93.0) + terser-webpack-plugin: 5.3.10(esbuild@0.21.5)(webpack@5.93.0(esbuild@0.21.5)(webpack-cli@4.10.0)) watchpack: 2.4.1 - webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0) webpack-sources: 3.2.3 + optionalDependencies: + webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.93.0) transitivePeerDependencies: - '@swc/core' - esbuild @@ -18537,13 +18463,13 @@ snapshots: dependencies: workbox-core: 7.1.0 - workbox-build@7.1.1: + workbox-build@7.1.1(@types/babel__core@7.20.5): dependencies: '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1) '@babel/core': 7.25.2 '@babel/preset-env': 7.25.3(@babel/core@7.25.2) '@babel/runtime': 7.25.0 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.25.2)(rollup@2.79.1) + '@rollup/plugin-babel': 5.3.1(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@2.79.1) '@rollup/plugin-node-resolve': 15.2.3(rollup@2.79.1) '@rollup/plugin-replace': 2.4.2(rollup@2.79.1) '@rollup/plugin-terser': 0.4.4(rollup@2.79.1)