diff --git a/.build/common.ts b/.build/common.ts index efd0e3a85..2497d443f 100644 --- a/.build/common.ts +++ b/.build/common.ts @@ -33,6 +33,11 @@ export const packageOptions = { packageName: 'mermaid-layout-elk', file: 'layouts.ts', }, + 'mermaid-layout-tidy-tree': { + name: 'mermaid-layout-tidy-tree', + packageName: 'mermaid-layout-tidy-tree', + file: 'index.ts', + }, examples: { name: 'mermaid-examples', packageName: 'examples', diff --git a/.changeset/clean-wolves-turn.md b/.changeset/clean-wolves-turn.md new file mode 100644 index 000000000..7a44c1c16 --- /dev/null +++ b/.changeset/clean-wolves-turn.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Render newlines as spaces in class diagrams diff --git a/.changeset/deep-times-make.md b/.changeset/deep-times-make.md new file mode 100644 index 000000000..3f126736f --- /dev/null +++ b/.changeset/deep-times-make.md @@ -0,0 +1,5 @@ +--- +'mermaid': minor +--- + +Add IDs in architecture diagrams diff --git a/.changeset/four-eyes-wish.md b/.changeset/four-eyes-wish.md new file mode 100644 index 000000000..3bbbc1e47 --- /dev/null +++ b/.changeset/four-eyes-wish.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: Ensure edge label color is applied when using classDef with edge IDs diff --git a/.changeset/hungry-guests-drive.md b/.changeset/hungry-guests-drive.md new file mode 100644 index 000000000..1b0e0a07b --- /dev/null +++ b/.changeset/hungry-guests-drive.md @@ -0,0 +1,7 @@ +--- +'mermaid': minor +'@mermaid-js/layout-tidy-tree': minor +'@mermaid-js/layout-elk': minor +--- + +feat: Update mindmap rendering to support multiple layouts, improved edge intersections, and new shapes diff --git a/.changeset/proud-colts-smell.md b/.changeset/proud-colts-smell.md new file mode 100644 index 000000000..610d69253 --- /dev/null +++ b/.changeset/proud-colts-smell.md @@ -0,0 +1,5 @@ +--- +'mermaid': minor +--- + +feat: Add IDs in architecture diagrams diff --git a/.changeset/revert-marked-dependency.md b/.changeset/revert-marked-dependency.md new file mode 100644 index 000000000..aded58871 --- /dev/null +++ b/.changeset/revert-marked-dependency.md @@ -0,0 +1,9 @@ +--- +'mermaid': patch +--- + +chore: revert marked dependency from ^15.0.7 to ^16.0.0 + +- Reverted marked package version to ^16.0.0 for better compatibility +- This is a dependency update that maintains API compatibility +- All tests pass with the updated version diff --git a/.cspell/mermaid-terms.txt b/.cspell/mermaid-terms.txt index b0cfa0a1d..45152a0ce 100644 --- a/.cspell/mermaid-terms.txt +++ b/.cspell/mermaid-terms.txt @@ -5,8 +5,10 @@ bmatrix braintree catmull compositTitleSize +cose curv doublecircle +elem elems gantt gitgraph diff --git a/.cspell/misc-terms.txt b/.cspell/misc-terms.txt index 1820e3c86..2906a02fa 100644 --- a/.cspell/misc-terms.txt +++ b/.cspell/misc-terms.txt @@ -1,4 +1,5 @@ BRANDES +Buzan circo handDrawn KOEPF diff --git a/.github/workflows/e2e-applitools.yml b/.github/workflows/e2e-applitools.yml index dd97b49e1..6aaa91eb8 100644 --- a/.github/workflows/e2e-applitools.yml +++ b/.github/workflows/e2e-applitools.yml @@ -23,9 +23,6 @@ env: jobs: e2e-applitools: runs-on: ubuntu-latest - container: - image: cypress/browsers:node-20.11.0-chrome-121.0.6167.85-1-ff-120.0-edge-121.0.2277.83-1 - options: --user 1001 steps: - if: ${{ ! env.USE_APPLI }} name: Warn if not using Applitools diff --git a/.github/workflows/e2e-timings.yml b/.github/workflows/e2e-timings.yml index 2bbfa8412..21dbda293 100644 --- a/.github/workflows/e2e-timings.yml +++ b/.github/workflows/e2e-timings.yml @@ -58,7 +58,7 @@ jobs: echo "EOF" >> $GITHUB_OUTPUT - name: Commit and create pull request - uses: peter-evans/create-pull-request@cb4d3bfce175d44325c6b7697f81e0afe8a79bdf + uses: peter-evans/create-pull-request@18e469570b1cf0dfc11d60ec121099f8ff3e617a with: add-paths: | cypress/timings.json diff --git a/.github/workflows/validate-lockfile.yml b/.github/workflows/validate-lockfile.yml index 6eb0a63ca..59a6df96d 100644 --- a/.github/workflows/validate-lockfile.yml +++ b/.github/workflows/validate-lockfile.yml @@ -35,7 +35,7 @@ jobs: # 2) No unwanted vitepress paths if grep -qF 'packages/mermaid/src/vitepress' pnpm-lock.yaml; then - issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run `rm -rf packages/mermaid/src/vitepress && pnpm install` to regenerate.") + issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run \`rm -rf packages/mermaid/src/vitepress && pnpm install\` to regenerate.") fi # 3) Lockfile only changes when package.json changes diff --git a/.gitignore b/.gitignore index 7448f2a81..7eb55d5cb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules/ coverage/ .idea/ .pnpm-store/ +.instructions/ dist v8-compile-cache-0 diff --git a/cypress/integration/other/configuration.spec.js b/cypress/integration/other/configuration.spec.js index b48a197a4..a699e03a7 100644 --- a/cypress/integration/other/configuration.spec.js +++ b/cypress/integration/other/configuration.spec.js @@ -98,12 +98,12 @@ describe('Configuration', () => { it('should handle arrowMarkerAbsolute set to true', () => { renderGraph( `flowchart TD - A[Christmas] -->|Get money| B(Go shopping) - B --> C{Let me think} - C -->|One| D[Laptop] - C -->|Two| E[iPhone] - C -->|Three| F[fa:fa-car Car] - `, + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car] + `, { arrowMarkerAbsolute: true, } @@ -113,8 +113,7 @@ describe('Configuration', () => { cy.get('path') .first() .should('have.attr', 'marker-end') - .should('exist') - .and('include', 'url(http\\:\\/\\/localhost'); + .and('include', 'url(http://localhost'); }); }); it('should not taint the initial configuration when using multiple directives', () => { diff --git a/cypress/integration/rendering/classDiagram.spec.js b/cypress/integration/rendering/classDiagram.spec.js index bd2a96b34..6cea402f8 100644 --- a/cypress/integration/rendering/classDiagram.spec.js +++ b/cypress/integration/rendering/classDiagram.spec.js @@ -524,5 +524,18 @@ describe('Class diagram', () => { `, {} ); + it('should handle an empty class body with empty braces', () => { + imgSnapshotTest( + ` classDiagram + class FooBase~T~ {} + class Bar { + +Zip + +Zap() + } + FooBase <|-- Ba + `, + { flowchart: { defaultRenderer: 'elk' } } + ); + }); }); }); diff --git a/cypress/integration/rendering/flowchart-elk.spec.js b/cypress/integration/rendering/flowchart-elk.spec.js index 312e1d5b4..fac4f3b4b 100644 --- a/cypress/integration/rendering/flowchart-elk.spec.js +++ b/cypress/integration/rendering/flowchart-elk.spec.js @@ -109,7 +109,7 @@ describe('Flowchart ELK', () => { const style = svg.attr('style'); expect(style).to.match(/^max-width: [\d.]+px;$/); const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join('')); - verifyNumber(maxWidthValue, 380); + verifyNumber(maxWidthValue, 380, 15); }); }); it('8-elk: should render a flowchart when useMaxWidth is false', () => { @@ -128,7 +128,7 @@ describe('Flowchart ELK', () => { const width = parseFloat(svg.attr('width')); // use within because the absolute value can be slightly different depending on the environment ±5% // expect(height).to.be.within(446 * 0.95, 446 * 1.05); - verifyNumber(width, 380); + verifyNumber(width, 380, 15); expect(svg).to.not.have.attr('style'); }); }); diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js index 8c6cde57a..5ef32c269 100644 --- a/cypress/integration/rendering/flowchart-v2.spec.js +++ b/cypress/integration/rendering/flowchart-v2.spec.js @@ -1186,4 +1186,17 @@ end imgSnapshotTest(graph, { htmlLabels: false }); }); }); + + it('V2 - 17: should apply class def colour to edge label', () => { + imgSnapshotTest( + ` graph LR + id1(Start) link@-- "Label" -->id2(Stop) + style id1 fill:#f9f,stroke:#333,stroke-width:4px + +class id2 myClass +classDef myClass fill:#bbf,stroke:#f66,stroke-width:2px,color:white,stroke-dasharray: 5 5 +class link myClass +` + ); + }); }); diff --git a/cypress/integration/rendering/mindmap-tidy-tree.spec.js b/cypress/integration/rendering/mindmap-tidy-tree.spec.js new file mode 100644 index 000000000..e111c281a --- /dev/null +++ b/cypress/integration/rendering/mindmap-tidy-tree.spec.js @@ -0,0 +1,79 @@ +import { imgSnapshotTest } from '../../helpers/util.ts'; + +describe('Mindmap Tidy Tree', () => { + it('1-tidy-tree: should render a simple mindmap without children', () => { + imgSnapshotTest( + ` --- + config: + layout: tidy-tree + --- + mindmap + root((mindmap)) + A + B + ` + ); + }); + it('2-tidy-tree: should render a simple mindmap', () => { + imgSnapshotTest( + ` --- + config: + layout: tidy-tree + --- + mindmap + root((mindmap is a long thing)) + A + B + C + D + ` + ); + }); + it('3-tidy-tree: should render a mindmap with different shapes', () => { + imgSnapshotTest( + ` --- + config: + layout: tidy-tree + --- + mindmap + root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness<br/>and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping + Tools + id)I am a cloud( + id))I am a bang(( + Tools + ` + ); + }); + it('4-tidy-tree: should render a mindmap with children', () => { + imgSnapshotTest( + ` --- + config: + layout: tidy-tree + --- + mindmap + ((This is a mindmap)) + child1 + grandchild 1 + grandchild 2 + child2 + grandchild 3 + grandchild 4 + child3 + grandchild 5 + grandchild 6 + ` + ); + }); +}); diff --git a/cypress/integration/rendering/mindmap.spec.ts b/cypress/integration/rendering/mindmap.spec.ts index d76e58c56..e0409ed46 100644 --- a/cypress/integration/rendering/mindmap.spec.ts +++ b/cypress/integration/rendering/mindmap.spec.ts @@ -159,12 +159,10 @@ root }); it('square shape', () => { imgSnapshotTest( - ` -mindmap + `mindmap root[ The root - ] - `, + ]`, {}, undefined, shouldHaveRoot @@ -172,12 +170,10 @@ mindmap }); it('rounded rect shape', () => { imgSnapshotTest( - ` -mindmap + `mindmap root(( The root - )) - `, + ))`, {}, undefined, shouldHaveRoot @@ -185,12 +181,10 @@ mindmap }); it('circle shape', () => { imgSnapshotTest( - ` -mindmap + `mindmap root( The root - ) - `, + )`, {}, undefined, shouldHaveRoot @@ -198,10 +192,8 @@ mindmap }); it('default shape', () => { imgSnapshotTest( - ` -mindmap - The root - `, + `mindmap + The root`, {}, undefined, shouldHaveRoot @@ -209,12 +201,10 @@ mindmap }); it('adding children', () => { imgSnapshotTest( - ` -mindmap + `mindmap The root child1 - child2 - `, + child2`, {}, undefined, shouldHaveRoot @@ -222,13 +212,11 @@ mindmap }); it('adding grand children', () => { imgSnapshotTest( - ` -mindmap + `mindmap The root child1 child2 - child3 - `, + child3`, {}, undefined, shouldHaveRoot @@ -240,25 +228,21 @@ mindmap `mindmap id1[\`**Start** with a second line 😎\`] - id2[\`The dog in **the** hog... a *very long text* about it -Word!\`] -` + id2[\`The dog in **the** hog... a *very long text* about it Word!\`]` ); }); }); describe('Include char sequence "graph" in text (#6795)', () => { it('has a label with char sequence "graph"', () => { imgSnapshotTest( - ` - mindmap + ` mindmap root Photograph Waterfall Landscape Geography Mountains - Rocks - `, + Rocks`, { flowchart: { defaultRenderer: 'elk' } } ); }); diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index eb5528844..7ac7aeac8 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -32,26 +32,8 @@ href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap" rel="stylesheet" /> - - - + + + +
+ ---
+      config:
+        layout: tidy-tree
+      ---
+      mindmap
+      root((mindmap))
+        A
+        B
+    
+
+ ---
+      config:
+        layout: dagre
+      ---
+      mindmap
+      root((mindmap))
+        A
+        B
+    
+
+ ---
+      config:
+        layout: elk
+      ---
+      mindmap
+      root((mindmap))
+        A
+        B
+    
+
+ ---
+      config:
+        layout: cose-bilkent
+      ---
+      mindmap
+      root((mindmap))
+        A
+        B
+    
+
+    ---
+      config:
+        layout: tidy-tree
+      ---
+      mindmap
+      root((mindmap is a long thing))
+        A
+        B
+        C
+        D
+    
+
+    ---
+      config:
+        layout: dagre
+      ---
+      mindmap
+      root((mindmap is a long thing))
+        A
+        B
+        C
+        D
+    
+
+    ---
+      config:
+        layout: elk
+      ---
+      mindmap
+      root((mindmap is a long thing))
+        A
+        B
+        C
+        D
+    
+
+    ---
+      config:
+        layout: cose-bilkent
+      ---
+      mindmap
+      root((mindmap is a long thing))
+        A
+        B
+        C
+        D
+    
+ +
+    ---
+      config:
+        layout: tidy-tree
+      ---
+      mindmap
+      root((mindmap))
+        Origins
+          Long history
+          ::icon(fa fa-book)
+          Popularisation
+            British popular psychology author Tony Buzan
+        Research
+          On effectiveness<br/>and features
+          On Automatic creation
+            Uses
+                Creative techniques
+                Strategic planning
+                Argument mapping
+        Tools
+              id)I am a cloud(
+                  id))I am a bang((
+                    Tools
+    
+
+    ---
+      config:
+        layout: dagre
+      ---
+      mindmap
+      root((mindmap))
+        Origins
+          Long history
+          ::icon(fa fa-book)
+          Popularisation
+            British popular psychology author Tony Buzan
+        Research
+          On effectiveness<br/>and features
+          On Automatic creation
+            Uses
+                Creative techniques
+                Strategic planning
+                Argument mapping
+        Tools
+              id)I am a cloud(
+                  id))I am a bang((
+                    Tools
+    
+
+    ---
+      config:
+        layout: elk
+      ---
+      mindmap
+      root((mindmap))
+        Origins
+          Long history
+          ::icon(fa fa-book)
+          Popularisation
+            British popular psychology author Tony Buzan
+        Research
+          On effectiveness<br/>and features
+          On Automatic creation
+            Uses
+                Creative techniques
+                Strategic planning
+                Argument mapping
+        Tools
+              id)I am a cloud(
+                  id))I am a bang((
+                    Tools
+    
+
+    ---
+      config:
+        layout: cose-bilkent
+      ---
+      mindmap
+      root((mindmap))
+        Origins
+          Long history
+          ::icon(fa fa-book)
+          Popularisation
+            British popular psychology author Tony Buzan
+        Research
+          On effectiveness<br/>and features
+          On Automatic creation
+            Uses
+                Creative techniques
+                Strategic planning
+                Argument mapping
+        Tools
+              id)I am a cloud(
+                  id))I am a bang((
+                    Tools
+    
+
+      ---
+      config:
+        layout: tidy-tree
+      ---
+      mindmap
+      root((mindmap))
+        A
+          a
+            apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+            apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+          b
+          c
+          d
+        B
+            apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+        D
+          apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+          apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+
+    
+
+      ---
+      config:
+        layout: dagre
+      ---
+      mindmap
+      root((mindmap))
+        A
+          a
+            apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+            apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+          b
+          c
+          d
+        B
+            apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+        D
+          apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+          apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+
+    
+
+      ---
+      config:
+        layout: elk
+      ---
+      mindmap
+      root((mindmap))
+        A
+          a
+            apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+            apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+          b
+          c
+          d
+        B
+            apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+        D
+          apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+          apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+
+    
+
+      ---
+      config:
+        layout: cose-bilkent
+      ---
+      mindmap
+      root((mindmap))
+        A
+          a
+            apa[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+            apa2[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+          b
+          c
+          d
+        B
+            apa3[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+        D
+          apa5[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+          apa4[I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on. I am a long long multline string passing several levels of text. Lorum ipsum and so on]
+
+    
+ +
+ ---
+      config:
+        layout: tidy-tree
+      ---
+      mindmap
+      ((This is a mindmap))
+        child1
+         grandchild 1
+         grandchild 2
+        child2
+         grandchild 3
+         grandchild 4
+        child3
+         grandchild 5
+         grandchild 6
+      
+    
+ +
+ ---
+      config:
+        layout: dagre
+      ---
+      mindmap
+      ((This is a mindmap))
+        child1
+         grandchild 1
+         grandchild 2
+        child2
+         grandchild 3
+         grandchild 4
+        child3
+         grandchild 5
+         grandchild 6
+      
+    
+ +
+ ---
+      config:
+        layout: elk
+      ---
+      mindmap
+      ((This is a mindmap))
+        child1
+         grandchild 1
+         grandchild 2
+        child2
+         grandchild 3
+         grandchild 4
+        child3
+         grandchild 5
+         grandchild 6
+      
+    
+ +
+ ---
+      config:
+        layout: cose-bilkent
+      ---
+      mindmap
+      ((This is a mindmap))
+        child1
+         grandchild 1
+         grandchild 2
+        child2
+         grandchild 3
+         grandchild 4
+        child3
+         grandchild 5
+         grandchild 6
+      
+    
+ +
+ + + diff --git a/cypress/platform/viewer.js b/cypress/platform/viewer.js index 7ff95e163..de7dcafe8 100644 --- a/cypress/platform/viewer.js +++ b/cypress/platform/viewer.js @@ -1,5 +1,6 @@ import externalExample from './mermaid-example-diagram.esm.mjs'; import layouts from './mermaid-layout-elk.esm.mjs'; +import tidyTree from './mermaid-layout-tidy-tree.esm.mjs'; import zenUml from './mermaid-zenuml.esm.mjs'; import mermaid from './mermaid.esm.mjs'; @@ -65,6 +66,7 @@ const contentLoaded = async function () { await mermaid.registerExternalDiagrams([externalExample, zenUml]); mermaid.registerLayoutLoaders(layouts); + mermaid.registerLayoutLoaders(tidyTree); mermaid.initialize(graphObj.mermaid); /** * CC-BY-4.0 diff --git a/cypress/timings.json b/cypress/timings.json index 86d5b5222..8a6a4c00e 100644 --- a/cypress/timings.json +++ b/cypress/timings.json @@ -2,219 +2,227 @@ "durations": [ { "spec": "cypress/integration/other/configuration.spec.js", - "duration": 6297 + "duration": 5841 }, { "spec": "cypress/integration/other/external-diagrams.spec.js", - "duration": 2187 + "duration": 2138 }, { "spec": "cypress/integration/other/ghsa.spec.js", - "duration": 3509 + "duration": 3370 }, { "spec": "cypress/integration/other/iife.spec.js", - "duration": 2218 + "duration": 2052 }, { "spec": "cypress/integration/other/interaction.spec.js", - "duration": 12104 + "duration": 12243 }, { "spec": "cypress/integration/other/rerender.spec.js", - "duration": 2151 + "duration": 2065 }, { "spec": "cypress/integration/other/xss.spec.js", - "duration": 33064 + "duration": 31288 }, { "spec": "cypress/integration/rendering/appli.spec.js", - "duration": 3488 + "duration": 3421 }, { "spec": "cypress/integration/rendering/architecture.spec.ts", - "duration": 106 + "duration": 97 }, { "spec": "cypress/integration/rendering/block.spec.js", - "duration": 18317 + "duration": 18500 }, { "spec": "cypress/integration/rendering/c4.spec.js", - "duration": 5592 + "duration": 5793 }, { "spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js", - "duration": 39358 + "duration": 40966 }, { "spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js", - "duration": 37160 + "duration": 39176 }, { "spec": "cypress/integration/rendering/classDiagram-v2.spec.js", - "duration": 23660 + "duration": 23468 }, { "spec": "cypress/integration/rendering/classDiagram-v3.spec.js", - "duration": 36866 + "duration": 38291 }, { "spec": "cypress/integration/rendering/classDiagram.spec.js", - "duration": 17334 + "duration": 16949 }, { "spec": "cypress/integration/rendering/conf-and-directives.spec.js", - "duration": 9871 + "duration": 9480 }, { "spec": "cypress/integration/rendering/current.spec.js", - "duration": 2833 + "duration": 2753 }, { "spec": "cypress/integration/rendering/erDiagram-unified.spec.js", - "duration": 85321 + "duration": 88028 }, { "spec": "cypress/integration/rendering/erDiagram.spec.js", - "duration": 15673 + "duration": 15615 }, { "spec": "cypress/integration/rendering/errorDiagram.spec.js", - "duration": 3724 + "duration": 3706 }, { "spec": "cypress/integration/rendering/flowchart-elk.spec.js", - "duration": 41178 + "duration": 43905 }, { "spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js", - "duration": 29966 + "duration": 31217 }, { "spec": "cypress/integration/rendering/flowchart-icon.spec.js", - "duration": 7689 + "duration": 7531 }, { "spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts", - "duration": 24709 + "duration": 25423 }, { "spec": "cypress/integration/rendering/flowchart-v2.spec.js", - "duration": 45565 + "duration": 49664 }, { "spec": "cypress/integration/rendering/flowchart.spec.js", - "duration": 31144 + "duration": 32525 }, { "spec": "cypress/integration/rendering/gantt.spec.js", - "duration": 20808 + "duration": 20915 }, { "spec": "cypress/integration/rendering/gitGraph.spec.js", - "duration": 49985 + "duration": 53556 }, { "spec": "cypress/integration/rendering/iconShape.spec.ts", - "duration": 273272 + "duration": 283038 }, { "spec": "cypress/integration/rendering/imageShape.spec.ts", - "duration": 55880 + "duration": 59434 }, { "spec": "cypress/integration/rendering/info.spec.ts", - "duration": 3271 + "duration": 3101 }, { "spec": "cypress/integration/rendering/journey.spec.js", - "duration": 7293 + "duration": 7099 }, { "spec": "cypress/integration/rendering/kanban.spec.ts", - "duration": 7861 + "duration": 7567 }, { "spec": "cypress/integration/rendering/katex.spec.js", - "duration": 3922 + "duration": 3817 }, { "spec": "cypress/integration/rendering/marker_unique_id.spec.js", - "duration": 2726 + "duration": 2624 + }, + { + "spec": "cypress/integration/rendering/mindmap-tidy-tree.spec.js", + "duration": 4246 }, { "spec": "cypress/integration/rendering/mindmap.spec.ts", - "duration": 11670 + "duration": 11967 }, { "spec": "cypress/integration/rendering/newShapes.spec.ts", - "duration": 146020 + "duration": 151914 }, { "spec": "cypress/integration/rendering/oldShapes.spec.ts", - "duration": 114244 + "duration": 116698 }, { "spec": "cypress/integration/rendering/packet.spec.ts", - "duration": 5036 + "duration": 4967 }, { "spec": "cypress/integration/rendering/pie.spec.ts", - "duration": 6545 + "duration": 6700 }, { "spec": "cypress/integration/rendering/quadrantChart.spec.js", - "duration": 9097 + "duration": 8963 }, { "spec": "cypress/integration/rendering/radar.spec.js", - "duration": 5676 + "duration": 5540 }, { "spec": "cypress/integration/rendering/requirement.spec.js", - "duration": 2795 + "duration": 2782 }, { "spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js", - "duration": 51660 + "duration": 54797 }, { "spec": "cypress/integration/rendering/sankey.spec.ts", - "duration": 6957 + "duration": 6914 + }, + { + "spec": "cypress/integration/rendering/sequencediagram-v2.spec.js", + "duration": 20481 }, { "spec": "cypress/integration/rendering/sequencediagram.spec.js", - "duration": 36026 + "duration": 38490 }, { "spec": "cypress/integration/rendering/stateDiagram-v2.spec.js", - "duration": 29551 + "duration": 30766 }, { "spec": "cypress/integration/rendering/stateDiagram.spec.js", - "duration": 17364 + "duration": 16705 }, { "spec": "cypress/integration/rendering/theme.spec.js", - "duration": 30209 + "duration": 30928 }, { "spec": "cypress/integration/rendering/timeline.spec.ts", - "duration": 8699 + "duration": 8424 }, { "spec": "cypress/integration/rendering/treemap.spec.ts", - "duration": 12168 + "duration": 12533 }, { "spec": "cypress/integration/rendering/xyChart.spec.js", - "duration": 21453 + "duration": 21197 }, { "spec": "cypress/integration/rendering/zenuml.spec.js", - "duration": 3577 + "duration": 3455 } ] } diff --git a/docs/config/faq.md b/docs/config/faq.md index db775e438..6d27b658e 100644 --- a/docs/config/faq.md +++ b/docs/config/faq.md @@ -6,7 +6,7 @@ # Frequently Asked Questions -1. [How to add title to flowchart?](https://github.com/mermaid-js/mermaid/issues/556#issuecomment-363182217) +1. [How to add title to flowchart?](https://github.com/mermaid-js/mermaid/issues/1433#issuecomment-1991554712) 2. [How to specify custom CSS file?](https://github.com/mermaidjs/mermaid.cli/pull/24#issuecomment-373402785) 3. [How to fix tooltip misplacement issue?](https://github.com/mermaid-js/mermaid/issues/542#issuecomment-3343564621) 4. [How to specify gantt diagram xAxis format?](https://github.com/mermaid-js/mermaid/issues/269#issuecomment-373229136) diff --git a/docs/config/layouts.md b/docs/config/layouts.md new file mode 100644 index 000000000..18e9c9423 --- /dev/null +++ b/docs/config/layouts.md @@ -0,0 +1,40 @@ +> **Warning** +> +> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. +> +> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/layouts.md](../../packages/mermaid/src/docs/config/layouts.md). + +# Layouts + +This page lists the available layout algorithms supported in Mermaid diagrams. + +## Supported Layouts + +- **elk**: [ELK (Eclipse Layout Kernel)](https://www.eclipse.org/elk/) +- **tidy-tree**: Tidy tree layout for hierarchical diagrams [Tidy Tree Configuration](/config/tidy-tree) +- **cose-bilkent**: Cose Bilkent layout for force-directed graphs +- **dagre**: Dagre layout for layered graphs + +## How to Use + +You can specify the layout in your diagram's YAML config or initialization options. For example: + +```mermaid-example +--- +config: + layout: elk +--- +graph TD; + A-->B; + B-->C; +``` + +```mermaid +--- +config: + layout: elk +--- +graph TD; + A-->B; + B-->C; +``` diff --git a/docs/config/setup/config/README.md b/docs/config/setup/config/README.md index 67fca78eb..c811c7b08 100644 --- a/docs/config/setup/config/README.md +++ b/docs/config/setup/config/README.md @@ -19,6 +19,7 @@ - [addDirective](functions/addDirective.md) - [getConfig](functions/getConfig.md) - [getSiteConfig](functions/getSiteConfig.md) +- [getUserDefinedConfig](functions/getUserDefinedConfig.md) - [reset](functions/reset.md) - [sanitize](functions/sanitize.md) - [saveConfigFromInitialize](functions/saveConfigFromInitialize.md) diff --git a/docs/config/setup/config/functions/getUserDefinedConfig.md b/docs/config/setup/config/functions/getUserDefinedConfig.md new file mode 100644 index 000000000..ed39f1337 --- /dev/null +++ b/docs/config/setup/config/functions/getUserDefinedConfig.md @@ -0,0 +1,19 @@ +> **Warning** +> +> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. +> +> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/setup/config/functions/getUserDefinedConfig.md](../../../../../packages/mermaid/src/docs/config/setup/config/functions/getUserDefinedConfig.md). + +[**mermaid**](../../README.md) + +--- + +# Function: getUserDefinedConfig() + +> **getUserDefinedConfig**(): [`MermaidConfig`](../../mermaid/interfaces/MermaidConfig.md) + +Defined in: [packages/mermaid/src/config.ts:252](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L252) + +## Returns + +[`MermaidConfig`](../../mermaid/interfaces/MermaidConfig.md) diff --git a/docs/config/setup/mermaid/README.md b/docs/config/setup/mermaid/README.md index 3e2cd7a28..653d90592 100644 --- a/docs/config/setup/mermaid/README.md +++ b/docs/config/setup/mermaid/README.md @@ -10,10 +10,6 @@ # mermaid -## Classes - -- [UnknownDiagramError](classes/UnknownDiagramError.md) - ## Interfaces - [DetailedError](interfaces/DetailedError.md) @@ -27,6 +23,7 @@ - [RenderOptions](interfaces/RenderOptions.md) - [RenderResult](interfaces/RenderResult.md) - [RunOptions](interfaces/RunOptions.md) +- [UnknownDiagramError](interfaces/UnknownDiagramError.md) ## Type Aliases diff --git a/docs/config/setup/mermaid/classes/UnknownDiagramError.md b/docs/config/setup/mermaid/classes/UnknownDiagramError.md deleted file mode 100644 index c077f0e34..000000000 --- a/docs/config/setup/mermaid/classes/UnknownDiagramError.md +++ /dev/null @@ -1,159 +0,0 @@ -> **Warning** -> -> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. -> -> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/setup/mermaid/classes/UnknownDiagramError.md](../../../../../packages/mermaid/src/docs/config/setup/mermaid/classes/UnknownDiagramError.md). - -[**mermaid**](../../README.md) - ---- - -# Class: UnknownDiagramError - -Defined in: [packages/mermaid/src/errors.ts:1](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/errors.ts#L1) - -## Extends - -- `Error` - -## Constructors - -### new UnknownDiagramError() - -> **new UnknownDiagramError**(`message`): [`UnknownDiagramError`](UnknownDiagramError.md) - -Defined in: [packages/mermaid/src/errors.ts:2](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/errors.ts#L2) - -#### Parameters - -##### message - -`string` - -#### Returns - -[`UnknownDiagramError`](UnknownDiagramError.md) - -#### Overrides - -`Error.constructor` - -## Properties - -### cause? - -> `optional` **cause**: `unknown` - -Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es2022.error.d.ts:26 - -#### Inherited from - -`Error.cause` - ---- - -### message - -> **message**: `string` - -Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1077 - -#### Inherited from - -`Error.message` - ---- - -### name - -> **name**: `string` - -Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1076 - -#### Inherited from - -`Error.name` - ---- - -### stack? - -> `optional` **stack**: `string` - -Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1078 - -#### Inherited from - -`Error.stack` - ---- - -### prepareStackTrace()? - -> `static` `optional` **prepareStackTrace**: (`err`, `stackTraces`) => `any` - -Defined in: node_modules/.pnpm/@types+node\@22.13.5/node_modules/@types/node/globals.d.ts:143 - -Optional override for formatting stack traces - -#### Parameters - -##### err - -`Error` - -##### stackTraces - -`CallSite`\[] - -#### Returns - -`any` - -#### See - - - -#### Inherited from - -`Error.prepareStackTrace` - ---- - -### stackTraceLimit - -> `static` **stackTraceLimit**: `number` - -Defined in: node_modules/.pnpm/@types+node\@22.13.5/node_modules/@types/node/globals.d.ts:145 - -#### Inherited from - -`Error.stackTraceLimit` - -## Methods - -### captureStackTrace() - -> `static` **captureStackTrace**(`targetObject`, `constructorOpt`?): `void` - -Defined in: node_modules/.pnpm/@types+node\@22.13.5/node_modules/@types/node/globals.d.ts:136 - -Create .stack property on a target object - -#### Parameters - -##### targetObject - -`object` - -##### constructorOpt? - -`Function` - -#### Returns - -`void` - -#### Inherited from - -`Error.captureStackTrace` diff --git a/docs/config/setup/mermaid/interfaces/ExternalDiagramDefinition.md b/docs/config/setup/mermaid/interfaces/ExternalDiagramDefinition.md index 35efcddeb..e8aaed4b9 100644 --- a/docs/config/setup/mermaid/interfaces/ExternalDiagramDefinition.md +++ b/docs/config/setup/mermaid/interfaces/ExternalDiagramDefinition.md @@ -10,7 +10,7 @@ # Interface: ExternalDiagramDefinition -Defined in: [packages/mermaid/src/diagram-api/types.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L94) +Defined in: [packages/mermaid/src/diagram-api/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L96) ## Properties @@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:94](https://github.com/me > **detector**: `DiagramDetector` -Defined in: [packages/mermaid/src/diagram-api/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L96) +Defined in: [packages/mermaid/src/diagram-api/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L98) --- @@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:96](https://github.com/me > **id**: `string` -Defined in: [packages/mermaid/src/diagram-api/types.ts:95](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L95) +Defined in: [packages/mermaid/src/diagram-api/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L97) --- @@ -34,4 +34,4 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:95](https://github.com/me > **loader**: `DiagramLoader` -Defined in: [packages/mermaid/src/diagram-api/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L97) +Defined in: [packages/mermaid/src/diagram-api/types.ts:99](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L99) diff --git a/docs/config/setup/mermaid/interfaces/LayoutData.md b/docs/config/setup/mermaid/interfaces/LayoutData.md index b4c88454e..32bef322c 100644 --- a/docs/config/setup/mermaid/interfaces/LayoutData.md +++ b/docs/config/setup/mermaid/interfaces/LayoutData.md @@ -10,7 +10,7 @@ # Interface: LayoutData -Defined in: [packages/mermaid/src/rendering-util/types.ts:145](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L145) +Defined in: [packages/mermaid/src/rendering-util/types.ts:168](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L168) ## Indexable @@ -22,7 +22,7 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:145](https://github.co > **config**: [`MermaidConfig`](MermaidConfig.md) -Defined in: [packages/mermaid/src/rendering-util/types.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L148) +Defined in: [packages/mermaid/src/rendering-util/types.ts:171](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L171) --- @@ -30,7 +30,7 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:148](https://github.co > **edges**: `Edge`\[] -Defined in: [packages/mermaid/src/rendering-util/types.ts:147](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L147) +Defined in: [packages/mermaid/src/rendering-util/types.ts:170](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L170) --- @@ -38,4 +38,4 @@ Defined in: [packages/mermaid/src/rendering-util/types.ts:147](https://github.co > **nodes**: `Node`\[] -Defined in: [packages/mermaid/src/rendering-util/types.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L146) +Defined in: [packages/mermaid/src/rendering-util/types.ts:169](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L169) diff --git a/docs/config/setup/mermaid/interfaces/LayoutLoaderDefinition.md b/docs/config/setup/mermaid/interfaces/LayoutLoaderDefinition.md index aac23f764..789e62045 100644 --- a/docs/config/setup/mermaid/interfaces/LayoutLoaderDefinition.md +++ b/docs/config/setup/mermaid/interfaces/LayoutLoaderDefinition.md @@ -10,7 +10,7 @@ # Interface: LayoutLoaderDefinition -Defined in: [packages/mermaid/src/rendering-util/render.ts:21](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L21) +Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L24) ## Properties @@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:21](https://github.co > `optional` **algorithm**: `string` -Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L24) +Defined in: [packages/mermaid/src/rendering-util/render.ts:27](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L27) --- @@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:24](https://github.co > **loader**: `LayoutLoader` -Defined in: [packages/mermaid/src/rendering-util/render.ts:23](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L23) +Defined in: [packages/mermaid/src/rendering-util/render.ts:26](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L26) --- @@ -34,4 +34,4 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:23](https://github.co > **name**: `string` -Defined in: [packages/mermaid/src/rendering-util/render.ts:22](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L22) +Defined in: [packages/mermaid/src/rendering-util/render.ts:25](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L25) diff --git a/docs/config/setup/mermaid/interfaces/Mermaid.md b/docs/config/setup/mermaid/interfaces/Mermaid.md index fd15b306b..0c63d140a 100644 --- a/docs/config/setup/mermaid/interfaces/Mermaid.md +++ b/docs/config/setup/mermaid/interfaces/Mermaid.md @@ -32,7 +32,7 @@ page. ### detectType() -> **detectType**: (`text`, `config`?) => `string` +> **detectType**: (`text`, `config?`) => `string` Defined in: [packages/mermaid/src/mermaid.ts:449](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L449) @@ -105,7 +105,7 @@ An array of objects with the id of the diagram. ### ~~init()~~ -> **init**: (`config`?, `nodes`?, `callback`?) => `Promise`<`void`> +> **init**: (`config?`, `nodes?`, `callback?`) => `Promise`<`void`> Defined in: [packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442) @@ -117,7 +117,7 @@ Defined in: [packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/ [`MermaidConfig`](MermaidConfig.md) -**Deprecated**, please set configuration in [initialize](Mermaid.md#initialize). +**Deprecated**, please set configuration in [initialize](#initialize). ##### nodes? @@ -141,13 +141,13 @@ Called once for each rendered diagram's id. #### Deprecated -Use [initialize](Mermaid.md#initialize) and [run](Mermaid.md#run) instead. +Use [initialize](#initialize) and [run](#run) instead. Renders the mermaid diagrams #### Deprecated -Use [initialize](Mermaid.md#initialize) and [run](Mermaid.md#run) instead. +Use [initialize](#initialize) and [run](#run) instead. --- @@ -176,7 +176,7 @@ Configuration object for mermaid. ### ~~mermaidAPI~~ -> **mermaidAPI**: `Readonly`<{ `defaultConfig`: [`MermaidConfig`](MermaidConfig.md); `getConfig`: () => [`MermaidConfig`](MermaidConfig.md); `getDiagramFromText`: (`text`, `metadata`) => `Promise`<`Diagram`>; `getSiteConfig`: () => [`MermaidConfig`](MermaidConfig.md); `globalReset`: () => `void`; `initialize`: (`userOptions`) => `void`; `parse`: (`text`, `parseOptions`) => `Promise`<`false` | [`ParseResult`](ParseResult.md)>(`text`, `parseOptions`?) => `Promise`<[`ParseResult`](ParseResult.md)>; `render`: (`id`, `text`, `svgContainingElement`?) => `Promise`<[`RenderResult`](RenderResult.md)>; `reset`: () => `void`; `setConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); `updateSiteConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); }> +> **mermaidAPI**: `Readonly`<{ `defaultConfig`: [`MermaidConfig`](MermaidConfig.md); `getConfig`: () => [`MermaidConfig`](MermaidConfig.md); `getDiagramFromText`: (`text`, `metadata`) => `Promise`<`Diagram`>; `getSiteConfig`: () => [`MermaidConfig`](MermaidConfig.md); `globalReset`: () => `void`; `initialize`: (`userOptions`) => `void`; `parse`: {(`text`, `parseOptions`): `Promise`<`false` | [`ParseResult`](ParseResult.md)>; (`text`, `parseOptions?`): `Promise`<[`ParseResult`](ParseResult.md)>; }; `render`: (`id`, `text`, `svgContainingElement?`) => `Promise`<[`RenderResult`](RenderResult.md)>; `reset`: () => `void`; `setConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); `updateSiteConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); }> Defined in: [packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436) @@ -184,73 +184,81 @@ Defined in: [packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/ #### Deprecated -Use [parse](Mermaid.md#parse) and [render](Mermaid.md#render) instead. Please [open a discussion](https://github.com/mermaid-js/mermaid/discussions) if your use case does not fit the new API. +Use [parse](#parse) and [render](#render) instead. Please [open a discussion](https://github.com/mermaid-js/mermaid/discussions) if your use case does not fit the new API. --- ### parse() -> **parse**: (`text`, `parseOptions`) => `Promise`<`false` | [`ParseResult`](ParseResult.md)>(`text`, `parseOptions`?) => `Promise`<[`ParseResult`](ParseResult.md)> +> **parse**: {(`text`, `parseOptions`): `Promise`<`false` | [`ParseResult`](ParseResult.md)>; (`text`, `parseOptions?`): `Promise`<[`ParseResult`](ParseResult.md)>; } Defined in: [packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437) +#### Call Signature + +> (`text`, `parseOptions`): `Promise`<`false` | [`ParseResult`](ParseResult.md)> + Parse the text and validate the syntax. -#### Parameters +##### Parameters -##### text +###### text `string` The mermaid diagram definition. -##### parseOptions +###### parseOptions [`ParseOptions`](ParseOptions.md) & `object` Options for parsing. -#### Returns +##### Returns `Promise`<`false` | [`ParseResult`](ParseResult.md)> An object with the `diagramType` set to type of the diagram if valid. Otherwise `false` if parseOptions.suppressErrors is `true`. -#### See +##### See [ParseOptions](ParseOptions.md) -#### Throws +##### Throws Error if the diagram is invalid and parseOptions.suppressErrors is false or not set. +#### Call Signature + +> (`text`, `parseOptions?`): `Promise`<[`ParseResult`](ParseResult.md)> + Parse the text and validate the syntax. -#### Parameters +##### Parameters -##### text +###### text `string` The mermaid diagram definition. -##### parseOptions? +###### parseOptions? [`ParseOptions`](ParseOptions.md) Options for parsing. -#### Returns +##### Returns `Promise`<[`ParseResult`](ParseResult.md)> An object with the `diagramType` set to type of the diagram if valid. Otherwise `false` if parseOptions.suppressErrors is `true`. -#### See +##### See [ParseOptions](ParseOptions.md) -#### Throws +##### Throws Error if the diagram is invalid and parseOptions.suppressErrors is false or not set. @@ -332,7 +340,7 @@ Defined in: [packages/mermaid/src/mermaid.ts:444](https://github.com/mermaid-js/ ### render() -> **render**: (`id`, `text`, `svgContainingElement`?) => `Promise`<[`RenderResult`](RenderResult.md)> +> **render**: (`id`, `text`, `svgContainingElement?`) => `Promise`<[`RenderResult`](RenderResult.md)> Defined in: [packages/mermaid/src/mermaid.ts:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438) diff --git a/docs/config/setup/mermaid/interfaces/ParseOptions.md b/docs/config/setup/mermaid/interfaces/ParseOptions.md index ea96f2706..70e4da183 100644 --- a/docs/config/setup/mermaid/interfaces/ParseOptions.md +++ b/docs/config/setup/mermaid/interfaces/ParseOptions.md @@ -10,7 +10,7 @@ # Interface: ParseOptions -Defined in: [packages/mermaid/src/types.ts:72](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L72) +Defined in: [packages/mermaid/src/types.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L76) ## Properties @@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:72](https://github.com/mermaid-js/mer > `optional` **suppressErrors**: `boolean` -Defined in: [packages/mermaid/src/types.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L77) +Defined in: [packages/mermaid/src/types.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L81) If `true`, parse will return `false` instead of throwing error when the diagram is invalid. The `parseError` function will not be called. diff --git a/docs/config/setup/mermaid/interfaces/ParseResult.md b/docs/config/setup/mermaid/interfaces/ParseResult.md index 7a5990610..92802cd41 100644 --- a/docs/config/setup/mermaid/interfaces/ParseResult.md +++ b/docs/config/setup/mermaid/interfaces/ParseResult.md @@ -10,7 +10,7 @@ # Interface: ParseResult -Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L80) +Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84) ## Properties @@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mer > **config**: [`MermaidConfig`](MermaidConfig.md) -Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88) +Defined in: [packages/mermaid/src/types.ts:92](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L92) The config passed as YAML frontmatter or directives @@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives > **diagramType**: `string` -Defined in: [packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84) +Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88) The diagram type, e.g. 'flowchart', 'sequence', etc. diff --git a/docs/config/setup/mermaid/interfaces/RenderOptions.md b/docs/config/setup/mermaid/interfaces/RenderOptions.md index e3a9483f2..a90912069 100644 --- a/docs/config/setup/mermaid/interfaces/RenderOptions.md +++ b/docs/config/setup/mermaid/interfaces/RenderOptions.md @@ -10,7 +10,7 @@ # Interface: RenderOptions -Defined in: [packages/mermaid/src/rendering-util/render.ts:7](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L7) +Defined in: [packages/mermaid/src/rendering-util/render.ts:10](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L10) ## Properties @@ -18,4 +18,4 @@ Defined in: [packages/mermaid/src/rendering-util/render.ts:7](https://github.com > `optional` **algorithm**: `string` -Defined in: [packages/mermaid/src/rendering-util/render.ts:8](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L8) +Defined in: [packages/mermaid/src/rendering-util/render.ts:11](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/render.ts#L11) diff --git a/docs/config/setup/mermaid/interfaces/RenderResult.md b/docs/config/setup/mermaid/interfaces/RenderResult.md index fc5fac4f5..b9420e2e4 100644 --- a/docs/config/setup/mermaid/interfaces/RenderResult.md +++ b/docs/config/setup/mermaid/interfaces/RenderResult.md @@ -10,7 +10,7 @@ # Interface: RenderResult -Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98) +Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102) ## Properties @@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mer > `optional` **bindFunctions**: (`element`) => `void` -Defined in: [packages/mermaid/src/types.ts:116](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L116) +Defined in: [packages/mermaid/src/types.ts:120](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L120) Bind function to be called after the svg has been inserted into the DOM. This is necessary for adding event listeners to the elements in the svg. @@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present. > **diagramType**: `string` -Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106) +Defined in: [packages/mermaid/src/types.ts:110](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L110) The diagram type, e.g. 'flowchart', 'sequence', etc. @@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc. > **svg**: `string` -Defined in: [packages/mermaid/src/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L102) +Defined in: [packages/mermaid/src/types.ts:106](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L106) The svg code for the rendered graph. diff --git a/docs/config/setup/mermaid/interfaces/UnknownDiagramError.md b/docs/config/setup/mermaid/interfaces/UnknownDiagramError.md new file mode 100644 index 000000000..2415d77ee --- /dev/null +++ b/docs/config/setup/mermaid/interfaces/UnknownDiagramError.md @@ -0,0 +1,65 @@ +> **Warning** +> +> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. +> +> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/setup/mermaid/interfaces/UnknownDiagramError.md](../../../../../packages/mermaid/src/docs/config/setup/mermaid/interfaces/UnknownDiagramError.md). + +[**mermaid**](../../README.md) + +--- + +# Interface: UnknownDiagramError + +Defined in: [packages/mermaid/src/errors.ts:1](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/errors.ts#L1) + +## Extends + +- `Error` + +## Properties + +### cause? + +> `optional` **cause**: `unknown` + +Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es2022.error.d.ts:26 + +#### Inherited from + +`Error.cause` + +--- + +### message + +> **message**: `string` + +Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1077 + +#### Inherited from + +`Error.message` + +--- + +### name + +> **name**: `string` + +Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1076 + +#### Inherited from + +`Error.name` + +--- + +### stack? + +> `optional` **stack**: `string` + +Defined in: node_modules/.pnpm/typescript\@5.7.3/node_modules/typescript/lib/lib.es5.d.ts:1078 + +#### Inherited from + +`Error.stack` diff --git a/docs/config/setup/mermaid/type-aliases/InternalHelpers.md b/docs/config/setup/mermaid/type-aliases/InternalHelpers.md index 6baf786fe..bfaeabd12 100644 --- a/docs/config/setup/mermaid/type-aliases/InternalHelpers.md +++ b/docs/config/setup/mermaid/type-aliases/InternalHelpers.md @@ -10,6 +10,6 @@ # Type Alias: InternalHelpers -> **InternalHelpers**: _typeof_ `internalHelpers` +> **InternalHelpers** = _typeof_ `internalHelpers` Defined in: [packages/mermaid/src/internals.ts:33](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/internals.ts#L33) diff --git a/docs/config/setup/mermaid/type-aliases/ParseErrorFunction.md b/docs/config/setup/mermaid/type-aliases/ParseErrorFunction.md index 78f27854c..dd5938478 100644 --- a/docs/config/setup/mermaid/type-aliases/ParseErrorFunction.md +++ b/docs/config/setup/mermaid/type-aliases/ParseErrorFunction.md @@ -10,7 +10,7 @@ # Type Alias: ParseErrorFunction() -> **ParseErrorFunction**: (`err`, `hash`?) => `void` +> **ParseErrorFunction** = (`err`, `hash?`) => `void` Defined in: [packages/mermaid/src/Diagram.ts:10](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/Diagram.ts#L10) diff --git a/docs/config/setup/mermaid/type-aliases/SVG.md b/docs/config/setup/mermaid/type-aliases/SVG.md index 8bfb7bda0..d6b628556 100644 --- a/docs/config/setup/mermaid/type-aliases/SVG.md +++ b/docs/config/setup/mermaid/type-aliases/SVG.md @@ -10,6 +10,6 @@ # Type Alias: SVG -> **SVG**: `d3.Selection`<`SVGSVGElement`, `unknown`, `Element` | `null`, `unknown`> +> **SVG** = `d3.Selection`<`SVGSVGElement`, `unknown`, `Element` | `null`, `unknown`> -Defined in: [packages/mermaid/src/diagram-api/types.ts:126](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L126) +Defined in: [packages/mermaid/src/diagram-api/types.ts:128](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L128) diff --git a/docs/config/setup/mermaid/type-aliases/SVGGroup.md b/docs/config/setup/mermaid/type-aliases/SVGGroup.md index 5e53052fd..7b2e92f52 100644 --- a/docs/config/setup/mermaid/type-aliases/SVGGroup.md +++ b/docs/config/setup/mermaid/type-aliases/SVGGroup.md @@ -10,6 +10,6 @@ # Type Alias: SVGGroup -> **SVGGroup**: `d3.Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`> +> **SVGGroup** = `d3.Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`> -Defined in: [packages/mermaid/src/diagram-api/types.ts:128](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L128) +Defined in: [packages/mermaid/src/diagram-api/types.ts:130](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L130) diff --git a/docs/config/tidy-tree.md b/docs/config/tidy-tree.md new file mode 100644 index 000000000..1d4227596 --- /dev/null +++ b/docs/config/tidy-tree.md @@ -0,0 +1,89 @@ +> **Warning** +> +> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. +> +> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/tidy-tree.md](../../packages/mermaid/src/docs/config/tidy-tree.md). + +# Tidy-tree Layout + +The **tidy-tree** layout arranges nodes in a hierarchical, tree-like structure. It is especially useful for diagrams where parent-child relationships are important, such as mindmaps. + +## Features + +- Organizes nodes in a tidy, non-overlapping tree +- Ideal for mindmaps and hierarchical data +- Automatically adjusts spacing for readability + +## Example Usage + +```mermaid-example +--- +config: + layout: tidy-tree +--- +mindmap +root((mindmap is a long thing)) + A + B + C + D +``` + +```mermaid +--- +config: + layout: tidy-tree +--- +mindmap +root((mindmap is a long thing)) + A + B + C + D +``` + +```mermaid-example +--- +config: + layout: tidy-tree +--- +mindmap +root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness
and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping +``` + +```mermaid +--- +config: + layout: tidy-tree +--- +mindmap +root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness
and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping +``` + +## Note + +- Currently, tidy-tree is primarily supported for mindmap diagrams. diff --git a/docs/syntax/flowchart.md b/docs/syntax/flowchart.md index daaa29581..23c34509c 100644 --- a/docs/syntax/flowchart.md +++ b/docs/syntax/flowchart.md @@ -326,7 +326,9 @@ Below is a comprehensive list of the newly introduced shapes and their correspon | **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** | | --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- | +| Bang | Bang | `bang` | Bang | `bang` | | Card | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` | +| Cloud | Cloud | `cloud` | cloud | `cloud` | | Collate | Hourglass | `hourglass` | Represents a collate operation | `collate`, `hourglass` | | Com Link | Lightning Bolt | `bolt` | Communication link | `com-link`, `lightning-bolt` | | Comment | Curly Brace | `brace` | Adds a comment | `brace-l`, `comment` | @@ -983,11 +985,23 @@ flowchart TD - `b` - **w**: The width of the image. If not defined, this will default to the natural width of the image. - **h**: The height of the image. If not defined, this will default to the natural height of the image. -- **constraint**: Determines if the image should constrain the node size. This setting also ensures the image maintains its original aspect ratio, adjusting the height (`h`) accordingly to the width (`w`). If not defined, this will default to `off` Possible values are: +- **constraint**: Determines if the image should constrain the node size. This setting also ensures the image maintains its original aspect ratio, adjusting the width (`w`) accordingly to the height (`h`). If not defined, this will default to `off` Possible values are: - `on` - `off` -These new shapes provide additional flexibility and visual appeal to your flowcharts, making them more informative and engaging. +If you want to resize an image, but keep the same aspect ratio, set `h`, and set `constraint: on` to constrain the aspect ratio. E.g. + +```mermaid-example +flowchart TD + %% My image with a constrained aspect ratio + A@{ img: "https://mermaid.js.org/favicon.svg", label: "My example image label", pos: "t", h: 60, constraint: "on" } +``` + +```mermaid +flowchart TD + %% My image with a constrained aspect ratio + A@{ img: "https://mermaid.js.org/favicon.svg", label: "My example image label", pos: "t", h: 60, constraint: "on" } +``` ## Links between nodes diff --git a/docs/syntax/mindmap.md b/docs/syntax/mindmap.md index 1adaa2c49..844b3293c 100644 --- a/docs/syntax/mindmap.md +++ b/docs/syntax/mindmap.md @@ -314,3 +314,22 @@ You can also refer the [implementation in the live editor](https://github.com/me cspell:locale en,en-gb cspell:ignore Buzan ---> + +## Layouts + +Mermaid also supports a Tidy Tree layout for mindmaps. + +``` +--- +config: + layout: tidy-tree +--- +mindmap +root((mindmap is a long thing)) + A + B + C + D +``` + +Instructions to add and register tidy-tree layout are present in [Tidy Tree Configuration](/config/tidy-tree) diff --git a/docs/syntax/userJourney.md b/docs/syntax/userJourney.md index 73fcb7468..5a2fb68b5 100644 --- a/docs/syntax/userJourney.md +++ b/docs/syntax/userJourney.md @@ -38,3 +38,5 @@ Each user journey is split into sections, these describe the part of the task the user is trying to complete. Tasks syntax is `Task name: : ` + +Score is a number between 1 and 5, inclusive. diff --git a/docs/syntax/xyChart.md b/docs/syntax/xyChart.md index dec16a518..742a4f18a 100644 --- a/docs/syntax/xyChart.md +++ b/docs/syntax/xyChart.md @@ -138,7 +138,7 @@ xychart ## Chart Theme Variables -Themes for xychart resides inside xychart attribute so to set the variables use this syntax: +Themes for xychart reside inside the `xychart` attribute, allowing customization through the following syntax: ```yaml --- @@ -163,6 +163,52 @@ config: | yAxisLineColor | Color of the y-axis line | | plotColorPalette | String of colors separated by comma e.g. "#f3456, #43445" | +### Setting Colors for Lines and Bars + +To set the color for lines and bars, use the `plotColorPalette` parameter. Colors in the palette will correspond sequentially to the elements in your chart (e.g., first bar/line will use the first color specified in the palette). + +```mermaid-example +--- +config: + themeVariables: + xyChart: + plotColorPalette: '#000000, #0000FF, #00FF00, #FF0000' +--- +xychart +title "Different Colors in xyChart" +x-axis "categoriesX" ["Category 1", "Category 2", "Category 3", "Category 4"] +y-axis "valuesY" 0 --> 50 +%% Black line +line [10,20,30,40] +%% Blue bar +bar [20,30,25,35] +%% Green bar +bar [15,25,20,30] +%% Red line +line [5,15,25,35] +``` + +```mermaid +--- +config: + themeVariables: + xyChart: + plotColorPalette: '#000000, #0000FF, #00FF00, #FF0000' +--- +xychart +title "Different Colors in xyChart" +x-axis "categoriesX" ["Category 1", "Category 2", "Category 3", "Category 4"] +y-axis "valuesY" 0 --> 50 +%% Black line +line [10,20,30,40] +%% Blue bar +bar [20,30,25,35] +%% Green bar +bar [15,25,20,30] +%% Red line +line [5,15,25,35] +``` + ## Example on config and theme ```mermaid-example diff --git a/eslint.config.js b/eslint.config.js index 7a144ee00..416fca2c6 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -17,6 +17,7 @@ export default tseslint.config( ...tseslint.configs.stylisticTypeChecked, { ignores: [ + '**/*.d.ts', '**/dist/', '**/node_modules/', '.git/', diff --git a/packages/examples/package.json b/packages/examples/package.json index cd0fc0bd0..10f66c2b9 100644 --- a/packages/examples/package.json +++ b/packages/examples/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "description": "Mermaid examples package", "author": "Sidharth Vinod", + "license": "MIT", "type": "module", "module": "./dist/mermaid-examples.core.mjs", "types": "./dist/mermaid.d.ts", diff --git a/packages/mermaid-layout-elk/README.md b/packages/mermaid-layout-elk/README.md index ab3289d6e..eec287263 100644 --- a/packages/mermaid-layout-elk/README.md +++ b/packages/mermaid-layout-elk/README.md @@ -2,7 +2,7 @@ This package provides a layout engine for Mermaid based on the [ELK](https://www.eclipse.org/elk/) layout engine. -> [!NOTE] +> [!NOTE] > The ELK Layout engine will not be available in all providers that support mermaid by default. > The websites will have to install the `@mermaid-js/layout-elk` package to use the ELK layout engine. @@ -69,4 +69,4 @@ mermaid.registerLayoutLoaders(elkLayouts); - `elk.mrtree`: Multi-root tree layout - `elk.sporeOverlap`: Spore overlap layout - + diff --git a/packages/mermaid-layout-elk/src/__tests__/geometry.spec.ts b/packages/mermaid-layout-elk/src/__tests__/geometry.spec.ts new file mode 100644 index 000000000..101dc5f10 --- /dev/null +++ b/packages/mermaid-layout-elk/src/__tests__/geometry.spec.ts @@ -0,0 +1,67 @@ +import { describe, it, expect } from 'vitest'; +import { + intersection, + ensureTrulyOutside, + makeInsidePoint, + tryNodeIntersect, + replaceEndpoint, + type RectLike, + type P, +} from '../geometry.js'; + +const approx = (a: number, b: number, eps = 1e-6) => Math.abs(a - b) < eps; + +describe('geometry helpers', () => { + it('intersection: vertical approach hits bottom border', () => { + const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 }; + const h = rect.height / 2; // 25 + const outside: P = { x: 0, y: 100 }; + const inside: P = { x: 0, y: 0 }; + const res = intersection(rect, outside, inside); + expect(approx(res.x, 0)).toBe(true); + expect(approx(res.y, h)).toBe(true); + }); + + it('ensureTrulyOutside nudges near-boundary point outward', () => { + const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 }; + // near bottom boundary (y ~ h) + const near: P = { x: 0, y: rect.height / 2 - 0.2 }; + const out = ensureTrulyOutside(rect, near, 10); + expect(out.y).toBeGreaterThan(rect.height / 2); + }); + + it('makeInsidePoint keeps x for vertical and y from center', () => { + const rect: RectLike = { x: 10, y: 5, width: 100, height: 50 }; + const outside: P = { x: 10, y: 40 }; + const center: P = { x: 99, y: -123 }; // center y should be used + const inside = makeInsidePoint(rect, outside, center); + expect(inside.x).toBe(outside.x); + expect(inside.y).toBe(center.y); + }); + + it('tryNodeIntersect returns null for wrong-side intersections', () => { + const rect: RectLike = { x: 0, y: 0, width: 100, height: 50 }; + const outside: P = { x: -50, y: 0 }; + const node = { intersect: () => ({ x: 10, y: 0 }) } as any; // right side of center + const res = tryNodeIntersect(node, rect, outside); + expect(res).toBeNull(); + }); + + it('replaceEndpoint dedup removes end/start appropriately', () => { + const pts: P[] = [ + { x: 0, y: 0 }, + { x: 1, y: 1 }, + ]; + // remove duplicate end + replaceEndpoint(pts, 'end', { x: 1, y: 1 }); + expect(pts.length).toBe(1); + + const pts2: P[] = [ + { x: 0, y: 0 }, + { x: 1, y: 1 }, + ]; + // remove duplicate start + replaceEndpoint(pts2, 'start', { x: 0, y: 0 }); + expect(pts2.length).toBe(1); + }); +}); diff --git a/packages/mermaid-layout-elk/src/find-common-ancestor.d.ts b/packages/mermaid-layout-elk/src/find-common-ancestor.d.ts new file mode 100644 index 000000000..db94f42c9 --- /dev/null +++ b/packages/mermaid-layout-elk/src/find-common-ancestor.d.ts @@ -0,0 +1,9 @@ +export interface TreeData { + parentById: Record; + childrenById: Record; +} +export declare const findCommonAncestor: ( + id1: string, + id2: string, + { parentById }: TreeData +) => string; diff --git a/packages/mermaid-layout-elk/src/geometry.ts b/packages/mermaid-layout-elk/src/geometry.ts new file mode 100644 index 000000000..5cda865de --- /dev/null +++ b/packages/mermaid-layout-elk/src/geometry.ts @@ -0,0 +1,209 @@ +/* Geometry utilities extracted from render.ts for reuse and testing */ + +export interface P { + x: number; + y: number; +} + +export interface RectLike { + x: number; // center x + y: number; // center y + width: number; + height: number; + padding?: number; +} + +export interface NodeLike { + intersect?: (p: P) => P | null; +} + +export const EPS = 1; +export const PUSH_OUT = 10; + +export const onBorder = (bounds: RectLike, p: P, tol = 0.5): boolean => { + const halfW = bounds.width / 2; + const halfH = bounds.height / 2; + const left = bounds.x - halfW; + const right = bounds.x + halfW; + const top = bounds.y - halfH; + const bottom = bounds.y + halfH; + + const onLeft = Math.abs(p.x - left) <= tol && p.y >= top - tol && p.y <= bottom + tol; + const onRight = Math.abs(p.x - right) <= tol && p.y >= top - tol && p.y <= bottom + tol; + const onTop = Math.abs(p.y - top) <= tol && p.x >= left - tol && p.x <= right + tol; + const onBottom = Math.abs(p.y - bottom) <= tol && p.x >= left - tol && p.x <= right + tol; + return onLeft || onRight || onTop || onBottom; +}; + +/** + * Compute intersection between a rectangle (center x/y, width/height) and the line + * segment from insidePoint -\> outsidePoint. Returns the point on the rectangle border. + * + * This version avoids snapping to outsidePoint when certain variables evaluate to 0 + * (previously caused vertical top/bottom cases to miss the border). It only enforces + * axis-constant behavior for purely vertical/horizontal approaches. + */ +export const intersection = (node: RectLike, outsidePoint: P, insidePoint: P): P => { + const x = node.x; + const y = node.y; + + const dx = Math.abs(x - insidePoint.x); + const w = node.width / 2; + let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx; + const h = node.height / 2; + + const Q = Math.abs(outsidePoint.y - insidePoint.y); + const R = Math.abs(outsidePoint.x - insidePoint.x); + + if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) { + // Intersection is top or bottom of rect. + const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y; + r = (R * q) / Q; + const res = { + x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r, + y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q, + }; + + // Keep axis-constant special-cases only + if (R === 0) { + res.x = outsidePoint.x; + } + if (Q === 0) { + res.y = outsidePoint.y; + } + return res; + } else { + // Intersection on sides of rect + if (insidePoint.x < outsidePoint.x) { + r = outsidePoint.x - w - x; + } else { + r = x - w - outsidePoint.x; + } + const q = (Q * r) / R; + let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r; + let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q; + + // Only handle axis-constant cases + if (R === 0) { + _x = outsidePoint.x; + } + if (Q === 0) { + _y = outsidePoint.y; + } + + return { x: _x, y: _y }; + } +}; + +export const outsideNode = (node: RectLike, point: P): boolean => { + const x = node.x; + const y = node.y; + const dx = Math.abs(point.x - x); + const dy = Math.abs(point.y - y); + const w = node.width / 2; + const h = node.height / 2; + return dx >= w || dy >= h; +}; + +export const ensureTrulyOutside = (bounds: RectLike, p: P, push = PUSH_OUT): P => { + const dx = Math.abs(p.x - bounds.x); + const dy = Math.abs(p.y - bounds.y); + const w = bounds.width / 2; + const h = bounds.height / 2; + if (Math.abs(dx - w) < EPS || Math.abs(dy - h) < EPS) { + const dirX = p.x - bounds.x; + const dirY = p.y - bounds.y; + const len = Math.sqrt(dirX * dirX + dirY * dirY); + if (len > 0) { + return { + x: bounds.x + (dirX / len) * (len + push), + y: bounds.y + (dirY / len) * (len + push), + }; + } + } + return p; +}; + +export const makeInsidePoint = (bounds: RectLike, outside: P, center: P): P => { + const isVertical = Math.abs(outside.x - bounds.x) < EPS; + const isHorizontal = Math.abs(outside.y - bounds.y) < EPS; + return { + x: isVertical + ? outside.x + : outside.x < bounds.x + ? bounds.x - bounds.width / 4 + : bounds.x + bounds.width / 4, + y: isHorizontal ? outside.y : center.y, + }; +}; + +export const tryNodeIntersect = (node: NodeLike, bounds: RectLike, outside: P): P | null => { + if (!node?.intersect) { + return null; + } + const res = node.intersect(outside); + if (!res) { + return null; + } + const wrongSide = + (outside.x < bounds.x && res.x > bounds.x) || (outside.x > bounds.x && res.x < bounds.x); + if (wrongSide) { + return null; + } + const dist = Math.hypot(outside.x - res.x, outside.y - res.y); + if (dist <= EPS) { + return null; + } + return res; +}; + +export const fallbackIntersection = (bounds: RectLike, outside: P, center: P): P => { + const inside = makeInsidePoint(bounds, outside, center); + return intersection(bounds, outside, inside); +}; + +export const computeNodeIntersection = ( + node: NodeLike, + bounds: RectLike, + outside: P, + center: P +): P => { + const outside2 = ensureTrulyOutside(bounds, outside); + return tryNodeIntersect(node, bounds, outside2) ?? fallbackIntersection(bounds, outside2, center); +}; + +export const replaceEndpoint = ( + points: P[], + which: 'start' | 'end', + value: P | null | undefined, + tol = 0.1 +) => { + if (!value || points.length === 0) { + return; + } + + if (which === 'start') { + if ( + points.length > 0 && + Math.abs(points[0].x - value.x) < tol && + Math.abs(points[0].y - value.y) < tol + ) { + // duplicate start remove it + points.shift(); + } else { + points[0] = value; + } + } else { + const last = points.length - 1; + if ( + points.length > 0 && + Math.abs(points[last].x - value.x) < tol && + Math.abs(points[last].y - value.y) < tol + ) { + // duplicate end remove it + points.pop(); + } else { + points[last] = value; + } + } +}; diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts index d1c44b67f..4680b0c68 100644 --- a/packages/mermaid-layout-elk/src/render.ts +++ b/packages/mermaid-layout-elk/src/render.ts @@ -1,10 +1,26 @@ +import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from 'mermaid'; +// @ts-ignore TODO: Investigate D3 issue import { curveLinear } from 'd3'; import ELK from 'elkjs/lib/elk.bundled.js'; -import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from 'mermaid'; import { type TreeData, findCommonAncestor } from './find-common-ancestor.js'; +import { + type P, + type RectLike, + outsideNode, + computeNodeIntersection, + replaceEndpoint, + onBorder, +} from './geometry.js'; + type Node = LayoutData['nodes'][number]; +// Minimal structural type to avoid depending on d3 Selection typings +interface D3Selection { + node(): T | null; + attr(name: string, value: string): D3Selection; +} + interface LabelData { width: number; height: number; @@ -13,9 +29,9 @@ interface LabelData { } interface NodeWithVertex extends Omit { - children?: unknown[]; + children?: LayoutData['nodes']; labelData?: LabelData; - domId?: Node['domId'] | SVGGroup | d3.Selection; + domId?: D3Selection; } export const render = async ( @@ -51,14 +67,13 @@ export const render = async ( // Add the element to the DOM if (!node.isGroup) { - const child: NodeWithVertex = { - ...node, - }; + const child = node as NodeWithVertex; graph.children.push(child); - nodeDb[node.id] = child; + nodeDb[node.id] = node; const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir }); const boundingBox = childNodeEl.node()!.getBBox(); + // Store the domId separately for rendering, not in the ELK graph child.domId = childNodeEl; child.width = boundingBox.width; child.height = boundingBox.height; @@ -66,9 +81,12 @@ export const render = async ( // A subgraph const child: NodeWithVertex & { children: NodeWithVertex[] } = { ...node, + domId: undefined, children: [], }; + // Let elk render with the copy graph.children.push(child); + // Save the original containing the intersection function nodeDb[node.id] = child; await addVertices(nodeEl, nodeArr, child, node.id); @@ -143,7 +161,7 @@ export const render = async ( height: node.height, }; if (node.isGroup) { - log.debug('id abc88 subgraph = ', node.id, node.x, node.y, node.labelData); + log.debug('Id abc88 subgraph = ', node.id, node.x, node.y, node.labelData); const subgraphEl = subgraphsEl.insert('g').attr('class', 'subgraph'); // TODO use faster way of cloning const clusterNode = JSON.parse(JSON.stringify(node)); @@ -152,10 +170,10 @@ export const render = async ( clusterNode.width = Math.max(clusterNode.width, node.labelData.width); await insertCluster(subgraphEl, clusterNode); - log.debug('id (UIO)= ', node.id, node.width, node.shape, node.labels); + log.debug('Id (UIO)= ', node.id, node.width, node.shape, node.labels); } else { log.info( - 'id NODE = ', + 'Id NODE = ', node.id, node.x, node.y, @@ -197,25 +215,19 @@ export const render = async ( }); }); - subgraphs.forEach(function (subgraph: { id: string | number }) { - const data: any = { id: subgraph.id }; - if (parentLookupDb.parentById[subgraph.id] !== undefined) { - data.parent = parentLookupDb.parentById[subgraph.id]; - } - }); return parentLookupDb; }; const getEdgeStartEndPoint = (edge: any) => { - const source: any = edge.start; - const target: any = edge.end; + // edge.start and edge.end are IDs (string/number) in our layout data + const sourceId: string | number = edge.start; + const targetId: string | number = edge.end; - // Save the original source and target - const sourceId = source; - const targetId = target; + const source = sourceId; + const target = targetId; - const startNode = nodeDb[edge.start.id]; - const endNode = nodeDb[edge.end.id]; + const startNode = nodeDb[sourceId]; + const endNode = nodeDb[targetId]; if (!startNode || !endNode) { return { source, target }; @@ -238,6 +250,112 @@ export const render = async ( /** * Add edges to graph based on parsed graph definition */ + // Edge helper maps and utilities (de-duplicated) + const ARROW_MAP: Record = { + arrow_open: ['arrow_open', 'arrow_open'], + arrow_cross: ['arrow_open', 'arrow_cross'], + double_arrow_cross: ['arrow_cross', 'arrow_cross'], + arrow_point: ['arrow_open', 'arrow_point'], + double_arrow_point: ['arrow_point', 'arrow_point'], + arrow_circle: ['arrow_open', 'arrow_circle'], + double_arrow_circle: ['arrow_circle', 'arrow_circle'], + }; + + const computeStroke = ( + stroke: string | undefined, + defaultStyle?: string, + defaultLabelStyle?: string + ) => { + // Defaults correspond to 'normal' + let thickness = 'normal'; + let pattern = 'solid'; + let style = ''; + let labelStyle = ''; + + if (stroke === 'dotted') { + pattern = 'dotted'; + style = 'fill:none;stroke-width:2px;stroke-dasharray:3;'; + } else if (stroke === 'thick') { + thickness = 'thick'; + style = 'stroke-width: 3.5px;fill:none;'; + } else { + // normal + style = defaultStyle ?? 'fill:none;'; + if (defaultLabelStyle !== undefined) { + labelStyle = defaultLabelStyle; + } + } + return { thickness, pattern, style, labelStyle }; + }; + + const getCurve = (edgeInterpolate: any, edgesDefaultInterpolate: any, confCurve: any) => { + if (edgeInterpolate !== undefined) { + return interpolateToCurve(edgeInterpolate, curveLinear); + } + if (edgesDefaultInterpolate !== undefined) { + return interpolateToCurve(edgesDefaultInterpolate, curveLinear); + } + // @ts-ignore TODO: fix this + return interpolateToCurve(confCurve, curveLinear); + }; + const buildEdgeData = ( + edge: any, + defaults: { + defaultStyle?: string; + defaultLabelStyle?: string; + defaultInterpolate?: any; + confCurve: any; + }, + common: any + ) => { + const edgeData: any = { style: '', labelStyle: '' }; + edgeData.minlen = edge.length || 1; + // maintain legacy behavior + edge.text = edge.label; + + // Arrowhead fill vs none + edgeData.arrowhead = edge.type === 'arrow_open' ? 'none' : 'normal'; + + // Arrow types + const arrowMap = ARROW_MAP[edge.type] ?? ARROW_MAP.arrow_open; + edgeData.arrowTypeStart = arrowMap[0]; + edgeData.arrowTypeEnd = arrowMap[1]; + + // Optional edge label positioning flags + edgeData.startLabelRight = edge.startLabelRight; + edgeData.endLabelLeft = edge.endLabelLeft; + + // Stroke + const strokeRes = computeStroke(edge.stroke, defaults.defaultStyle, defaults.defaultLabelStyle); + edgeData.thickness = strokeRes.thickness; + edgeData.pattern = strokeRes.pattern; + edgeData.style = (edgeData.style || '') + (strokeRes.style || ''); + edgeData.labelStyle = (edgeData.labelStyle || '') + (strokeRes.labelStyle || ''); + + // Curve + // @ts-ignore - defaults.confCurve is present at runtime but missing in type + edgeData.curve = getCurve(edge.interpolate, defaults.defaultInterpolate, defaults.confCurve); + + // Arrowhead style + labelpos when we have label text + const hasText = (edge?.text ?? '') !== ''; + if (hasText) { + edgeData.arrowheadStyle = 'fill: #333'; + edgeData.labelpos = 'c'; + } else if (edge.style !== undefined) { + edgeData.arrowheadStyle = 'fill: #333'; + } + + edgeData.labelType = edge.labelType; + edgeData.label = (edge?.text ?? '').replace(common.lineBreakRegex, '\n'); + + if (edge.style === undefined) { + edgeData.style = edgeData.style ?? 'stroke: #333; stroke-width: 1.5px;fill:none;'; + } + + edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:'); + return edgeData; + }; + const addEdges = async function ( dataForLayout: { edges: any; direction?: string }, graph: { @@ -259,7 +377,6 @@ export const render = async ( const edges = dataForLayout.edges; const labelsEl = svg.insert('g').attr('class', 'edgeLabels'); const linkIdCnt: any = {}; - const dir = dataForLayout.direction || 'DOWN'; let defaultStyle: string | undefined; let defaultLabelStyle: string | undefined; @@ -289,105 +406,24 @@ export const render = async ( linkIdCnt[linkIdBase]++; log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]); } - const linkId = linkIdBase + '_' + linkIdCnt[linkIdBase]; + const linkId = linkIdBase; // + '_' + linkIdCnt[linkIdBase]; edge.id = linkId; log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]); const linkNameStart = 'LS_' + edge.start; const linkNameEnd = 'LE_' + edge.end; - const edgeData: any = { style: '', labelStyle: '' }; - edgeData.minlen = edge.length || 1; - edge.text = edge.label; - // Set link type for rendering - if (edge.type === 'arrow_open') { - edgeData.arrowhead = 'none'; - } else { - edgeData.arrowhead = 'normal'; - } - - // Check of arrow types, placed here in order not to break old rendering - edgeData.arrowTypeStart = 'arrow_open'; - edgeData.arrowTypeEnd = 'arrow_open'; - - /* eslint-disable no-fallthrough */ - switch (edge.type) { - case 'double_arrow_cross': - edgeData.arrowTypeStart = 'arrow_cross'; - case 'arrow_cross': - edgeData.arrowTypeEnd = 'arrow_cross'; - break; - case 'double_arrow_point': - edgeData.arrowTypeStart = 'arrow_point'; - case 'arrow_point': - edgeData.arrowTypeEnd = 'arrow_point'; - break; - case 'double_arrow_circle': - edgeData.arrowTypeStart = 'arrow_circle'; - case 'arrow_circle': - edgeData.arrowTypeEnd = 'arrow_circle'; - break; - } - - let style = ''; - let labelStyle = ''; - - edgeData.startLabelRight = edge.startLabelRight; - edgeData.endLabelLeft = edge.endLabelLeft; - - switch (edge.stroke) { - case 'normal': - style = 'fill:none;'; - if (defaultStyle !== undefined) { - style = defaultStyle; - } - if (defaultLabelStyle !== undefined) { - labelStyle = defaultLabelStyle; - } - edgeData.thickness = 'normal'; - edgeData.pattern = 'solid'; - break; - case 'dotted': - edgeData.thickness = 'normal'; - edgeData.pattern = 'dotted'; - edgeData.style = 'fill:none;stroke-width:2px;stroke-dasharray:3;'; - break; - case 'thick': - edgeData.thickness = 'thick'; - edgeData.pattern = 'solid'; - edgeData.style = 'stroke-width: 3.5px;fill:none;'; - break; - } - - edgeData.style = edgeData.style += style; - edgeData.labelStyle = edgeData.labelStyle += labelStyle; - const conf = getConfig(); - if (edge.interpolate !== undefined) { - edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear); - } else if (edges.defaultInterpolate !== undefined) { - edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear); - } else { - // @ts-ignore TODO: fix this - edgeData.curve = interpolateToCurve(conf.curve, curveLinear); - } - - if (edge.text === undefined) { - if (edge.style !== undefined) { - edgeData.arrowheadStyle = 'fill: #333'; - } - } else { - edgeData.arrowheadStyle = 'fill: #333'; - edgeData.labelpos = 'c'; - } - - edgeData.labelType = edge.labelType; - edgeData.label = (edge?.text || '').replace(common.lineBreakRegex, '\n'); - - if (edge.style === undefined) { - edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;'; - } - - edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:'); + const edgeData = buildEdgeData( + edge, + { + defaultStyle, + defaultLabelStyle, + defaultInterpolate: edges.defaultInterpolate, + // @ts-ignore - conf.curve exists at runtime but is missing from typing + confCurve: conf.curve, + }, + common + ); edgeData.id = linkId; edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd; @@ -396,13 +432,11 @@ export const render = async ( // calculate start and end points of the edge, note that the source and target // can be modified for shapes that have ports - // @ts-ignore TODO: fix this - const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge, dir); + + const { source, target, sourceId, targetId } = getEdgeStartEndPoint(edge); log.debug('abc78 source and target', source, target); // Add the edge to the graph graph.edges.push({ - // @ts-ignore TODO: fix this - id: 'e' + edge.start + edge.end, ...edge, sources: [source], targets: [target], @@ -436,6 +470,7 @@ export const render = async ( case 'RL': return 'LEFT'; case 'TB': + case 'TD': // TD is an alias for TB in Mermaid return 'DOWN'; case 'BT': return 'UP'; @@ -459,299 +494,200 @@ export const render = async ( } } - function intersectLine( - p1: { y: number; x: number }, - p2: { y: number; x: number }, - q1: { x: any; y: any }, - q2: { x: any; y: any } - ) { - log.debug('UIO intersectLine', p1, p2, q1, q2); - // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994, - // p7 and p473. - - // let a1, a2, b1, b2, c1, c2; - // let r1, r2, r3, r4; - // let denom, offset, num; - // let x, y; - - // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x + - // b1 y + c1 = 0. - const a1 = p2.y - p1.y; - const b1 = p1.x - p2.x; - const c1 = p2.x * p1.y - p1.x * p2.y; - - // Compute r3 and r4. - const r3 = a1 * q1.x + b1 * q1.y + c1; - const r4 = a1 * q2.x + b1 * q2.y + c1; - - const epsilon = 1e-6; - - // Check signs of r3 and r4. If both point 3 and point 4 lie on - // same side of line 1, the line segments do not intersect. - if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) { - return /*DON'T_INTERSECT*/; - } - - // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0 - const a2 = q2.y - q1.y; - const b2 = q1.x - q2.x; - const c2 = q2.x * q1.y - q1.x * q2.y; - - // Compute r1 and r2 - const r1 = a2 * p1.x + b2 * p1.y + c2; - const r2 = a2 * p2.x + b2 * p2.y + c2; - - // Check signs of r1 and r2. If both point 1 and point 2 lie - // on same side of second line segment, the line segments do - // not intersect. - if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) { - return /*DON'T_INTERSECT*/; - } - - // Line segments intersect: compute intersection point. - const denom = a1 * b2 - a2 * b1; - if (denom === 0) { - return /*COLLINEAR*/; - } - - const offset = Math.abs(denom / 2); - - // The denom/2 is to get rounding instead of truncating. It - // is added or subtracted to the numerator, depending upon the - // sign of the numerator. - let num = b1 * c2 - b2 * c1; - const x = num < 0 ? (num - offset) / denom : (num + offset) / denom; - - num = a2 * c1 - a1 * c2; - const y = num < 0 ? (num - offset) / denom : (num + offset) / denom; - - return { x: x, y: y }; - } - - function sameSign(r1: number, r2: number) { - return r1 * r2 > 0; - } - const diamondIntersection = ( - bounds: { x: any; y: any; width: any; height: any }, - outsidePoint: { x: number; y: number }, - insidePoint: any - ) => { - const x1 = bounds.x; - const y1 = bounds.y; - - const w = bounds.width; //+ bounds.padding; - const h = bounds.height; // + bounds.padding; - - const polyPoints = [ - { x: x1, y: y1 - h / 2 }, - { x: x1 + w / 2, y: y1 }, - { x: x1, y: y1 + h / 2 }, - { x: x1 - w / 2, y: y1 }, - ]; - log.debug( - `APA16 diamondIntersection calc abc89: - outsidePoint: ${JSON.stringify(outsidePoint)} - insidePoint : ${JSON.stringify(insidePoint)} - node-bounds : x:${bounds.x} y:${bounds.y} w:${bounds.width} h:${bounds.height}`, - JSON.stringify(polyPoints) - ); - - const intersections = []; - - let minX = Number.POSITIVE_INFINITY; - let minY = Number.POSITIVE_INFINITY; - - polyPoints.forEach(function (entry) { - minX = Math.min(minX, entry.x); - minY = Math.min(minY, entry.y); - }); - - const left = x1 - w / 2 - minX; - const top = y1 - h / 2 - minY; - - for (let i = 0; i < polyPoints.length; i++) { - const p1 = polyPoints[i]; - const p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0]; - const intersect = intersectLine( - bounds, - outsidePoint, - { x: left + p1.x, y: top + p1.y }, - { x: left + p2.x, y: top + p2.y } - ); - - if (intersect) { - intersections.push(intersect); - } - } - - if (!intersections.length) { - return bounds; - } - - log.debug('UIO intersections', intersections); - - if (intersections.length > 1) { - // More intersections, find the one nearest to edge end point - intersections.sort(function (p, q) { - const pdx = p.x - outsidePoint.x; - const pdy = p.y - outsidePoint.y; - const distp = Math.sqrt(pdx * pdx + pdy * pdy); - - const qdx = q.x - outsidePoint.x; - const qdy = q.y - outsidePoint.y; - const distq = Math.sqrt(qdx * qdx + qdy * qdy); - - return distp < distq ? -1 : distp === distq ? 0 : 1; - }); - } - - return intersections[0]; + // Node bounds helpers (global) + const getEffectiveGroupWidth = (node: any): number => { + const labelW = node?.labels?.[0]?.width ?? 0; + const padding = node?.padding ?? 0; + return Math.max(node.width ?? 0, labelW + padding); }; - const intersection = ( - node: { x: any; y: any; width: number; height: number }, - outsidePoint: { x: number; y: number }, - insidePoint: { x: number; y: number } - ) => { - log.debug(`intersection calc abc89: - outsidePoint: ${JSON.stringify(outsidePoint)} - insidePoint : ${JSON.stringify(insidePoint)} - node : x:${node.x} y:${node.y} w:${node.width} h:${node.height}`); - const x = node.x; - const y = node.y; + const boundsFor = (node: any): RectLike => { + const width = node?.isGroup ? getEffectiveGroupWidth(node) : node.width; + return { + x: node.offset.posX + node.width / 2, + y: node.offset.posY + node.height / 2, + width, + height: node.height, + padding: node.padding, + }; + }; + // Helper utilities for endpoint handling around cutter2 + type Side = 'start' | 'end'; + const approxEq = (a: number, b: number, eps = 1e-6) => Math.abs(a - b) < eps; + const isCenterApprox = (pt: P, node: { x: number; y: number }) => + approxEq(pt.x, node.x) && approxEq(pt.y, node.y); - const dx = Math.abs(x - insidePoint.x); - // const dy = Math.abs(y - insidePoint.y); - const w = node.width / 2; - let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx; - const h = node.height / 2; - - const Q = Math.abs(outsidePoint.y - insidePoint.y); - const R = Math.abs(outsidePoint.x - insidePoint.x); - - if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) { - // Intersection is top or bottom of rect. - const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y; - r = (R * q) / Q; - const res = { - x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r, - y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q, - }; - - if (r === 0) { - res.x = outsidePoint.x; - res.y = outsidePoint.y; - } - if (R === 0) { - res.x = outsidePoint.x; - } - if (Q === 0) { - res.y = outsidePoint.y; - } - - log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line - - return res; + const getCandidateBorderPoint = ( + points: P[], + node: any, + side: Side + ): { candidate: P; centerApprox: boolean } => { + if (!points?.length) { + return { candidate: { x: node.x, y: node.y } as P, centerApprox: true }; + } + if (side === 'start') { + const first = points[0]; + const centerApprox = isCenterApprox(first, node); + const candidate = centerApprox && points.length > 1 ? points[1] : first; + return { candidate, centerApprox }; } else { - // Intersection on sides of rect - if (insidePoint.x < outsidePoint.x) { - r = outsidePoint.x - w - x; - } else { - // r = outsidePoint.x - w - x; - r = x - w - outsidePoint.x; - } - const q = (Q * r) / R; - // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w; - // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; - let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r; - // let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; - let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q; - log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y }); - if (r === 0) { - _x = outsidePoint.x; - _y = outsidePoint.y; - } - if (R === 0) { - _x = outsidePoint.x; - } - if (Q === 0) { - _y = outsidePoint.y; - } - - return { x: _x, y: _y }; + const last = points[points.length - 1]; + const centerApprox = isCenterApprox(last, node); + const candidate = centerApprox && points.length > 1 ? points[points.length - 2] : last; + return { candidate, centerApprox }; } }; - const outsideNode = ( - node: { x: any; y: any; width: number; height: number }, - point: { x: number; y: number } - ) => { - const x = node.x; - const y = node.y; - const dx = Math.abs(point.x - x); - const dy = Math.abs(point.y - y); - const w = node.width / 2; - const h = node.height / 2; - if (dx >= w || dy >= h) { - return true; + + const dropAutoCenterPoint = (points: P[], side: Side, doDrop: boolean) => { + if (!doDrop) { + return; + } + if (side === 'start') { + if (points.length > 0) { + points.shift(); + } + } else { + if (points.length > 0) { + points.pop(); + } } - return false; }; - /** - * This function will page a path and node where the last point(s) in the path is inside the node - * and return an update path ending by the border of the node. - */ - const cutPathAtIntersect = ( - _points: any[], - bounds: { x: any; y: any; width: any; height: any; padding: any }, - isDiamond: boolean - ) => { - log.debug('APA18 cutPathAtIntersect Points:', _points, 'node:', bounds, 'isDiamond', isDiamond); - const points: any[] = []; - let lastPointOutside = _points[0]; - let isInside = false; - _points.forEach((point: any) => { - // check if point is inside the boundary rect - if (!outsideNode(bounds, point) && !isInside) { - // First point inside the rect found - // Calc the intersection coord between the point and the last point outside the rect - let inter; - if (isDiamond) { - const inter2 = diamondIntersection(bounds, lastPointOutside, point); - const distance = Math.sqrt( - (lastPointOutside.x - inter2.x) ** 2 + (lastPointOutside.y - inter2.y) ** 2 - ); - if (distance > 1) { - inter = inter2; - } - } - if (!inter) { - inter = intersection(bounds, lastPointOutside, point); - } + const applyStartIntersectionIfNeeded = (points: P[], startNode: any, startBounds: RectLike) => { + let firstOutsideStartIndex = -1; + for (const [i, p] of points.entries()) { + if (outsideNode(startBounds, p)) { + firstOutsideStartIndex = i; + break; + } + } + if (firstOutsideStartIndex !== -1) { + const outsidePointForStart = points[firstOutsideStartIndex]; + const startCenter = points[0]; + const startIntersection = computeNodeIntersection( + startNode, + startBounds, + outsidePointForStart, + startCenter + ); + replaceEndpoint(points, 'start', startIntersection); + log.debug('UIO cutter2: start-only intersection applied', { startIntersection }); + } + }; - // Check case where the intersection is the same as the last point - let pointPresent = false; - points.forEach((p) => { - pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y); + const applyEndIntersectionIfNeeded = (points: P[], endNode: any, endBounds: RectLike) => { + let outsideIndexForEnd = -1; + for (let i = points.length - 1; i >= 0; i--) { + if (outsideNode(endBounds, points[i])) { + outsideIndexForEnd = i; + break; + } + } + if (outsideIndexForEnd !== -1) { + const outsidePointForEnd = points[outsideIndexForEnd]; + const endCenter = points[points.length - 1]; + const endIntersection = computeNodeIntersection( + endNode, + endBounds, + outsidePointForEnd, + endCenter + ); + replaceEndpoint(points, 'end', endIntersection); + log.debug('UIO cutter2: end-only intersection applied', { endIntersection }); + } + }; + + const cutter2 = (startNode: any, endNode: any, _points: any[]) => { + const startBounds = boundsFor(startNode); + const endBounds = boundsFor(endNode); + + if (_points.length === 0) { + return []; + } + + // Copy the original points array + const points: P[] = [..._points] as P[]; + + // The first point is the center of sNode, the last point is the center of eNode + const startCenter = points[0]; + const endCenter = points[points.length - 1]; + + // Minimal, structured logging for diagnostics + log.debug('PPP cutter2: bounds', { startBounds, endBounds }); + log.debug('PPP cutter2: original points', _points); + + let firstOutsideStartIndex = -1; + + // Single iteration through the array + for (const [i, point] of points.entries()) { + if (firstOutsideStartIndex === -1 && outsideNode(startBounds, point)) { + firstOutsideStartIndex = i; + } + if (outsideNode(endBounds, point)) { + // keep scanning; we'll also scan from the end for the last outside point + } + } + + // Calculate intersection with start node if we found a point outside it + if (firstOutsideStartIndex !== -1) { + const outsidePointForStart = points[firstOutsideStartIndex]; + const startIntersection = computeNodeIntersection( + startNode, + startBounds, + outsidePointForStart, + startCenter + ); + log.debug('UIO cutter2: start intersection', startIntersection); + replaceEndpoint(points, 'start', startIntersection); + } + + // Calculate intersection with end node + let outsidePointForEnd = null; + let outsideIndexForEnd = -1; + + for (let i = points.length - 1; i >= 0; i--) { + if (outsideNode(endBounds, points[i])) { + outsidePointForEnd = points[i]; + outsideIndexForEnd = i; + break; + } + } + + if (!outsidePointForEnd && points.length > 1) { + outsidePointForEnd = points[points.length - 2]; + outsideIndexForEnd = points.length - 2; + } + + if (outsidePointForEnd) { + const endIntersection = computeNodeIntersection( + endNode, + endBounds, + outsidePointForEnd, + endCenter + ); + log.debug('UIO cutter2: end intersection', { endIntersection, outsideIndexForEnd }); + replaceEndpoint(points, 'end', endIntersection); + } + + // Final cleanup: Check if the last point is too close to the previous point + if (points.length > 1) { + const lastPoint = points[points.length - 1]; + const secondLastPoint = points[points.length - 2]; + const distance = Math.sqrt( + (lastPoint.x - secondLastPoint.x) ** 2 + (lastPoint.y - secondLastPoint.y) ** 2 + ); + if (distance < 2) { + log.debug('UIO cutter2: trimming tail point (too close)', { + distance, + lastPoint, + secondLastPoint, }); - // if (!pointPresent) { - if (!points.some((e) => e.x === inter.x && e.y === inter.y)) { - points.push(inter); - } else { - log.debug('abc88 no intersect', inter, points); - } - // points.push(inter); - isInside = true; - } else { - // Outside - log.debug('abc88 outside', point, lastPointOutside, points); - lastPointOutside = point; - // points.push(point); - if (!isInside) { - points.push(point); - } + points.pop(); } - }); + } + + log.debug('UIO cutter2: final points', points); + return points; }; @@ -766,17 +702,19 @@ export const render = async ( id: 'root', layoutOptions: { 'elk.hierarchyHandling': 'INCLUDE_CHILDREN', - 'elk.layered.crossingMinimization.forceNodeModelOrder': - data4Layout.config.elk?.forceNodeModelOrder, - 'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder, - 'elk.algorithm': algorithm, 'nodePlacement.strategy': data4Layout.config.elk?.nodePlacementStrategy, 'elk.layered.mergeEdges': data4Layout.config.elk?.mergeEdges, 'elk.direction': 'DOWN', - 'spacing.baseValue': 35, + 'spacing.baseValue': 40, + 'elk.layered.crossingMinimization.forceNodeModelOrder': + data4Layout.config.elk?.forceNodeModelOrder, + 'elk.layered.considerModelOrder.strategy': data4Layout.config.elk?.considerModelOrder, 'elk.layered.unnecessaryBendpoints': true, 'elk.layered.cycleBreaking.strategy': data4Layout.config.elk?.cycleBreakingStrategy, + + // 'elk.layered.cycleBreaking.strategy': 'GREEDY_MODEL_ORDER', + // 'elk.layered.cycleBreaking.strategy': 'MODEL_ORDER', // 'spacing.nodeNode': 20, // 'spacing.nodeNodeBetweenLayers': 25, // 'spacing.edgeNode': 20, @@ -784,22 +722,28 @@ export const render = async ( // 'spacing.edgeEdge': 10, // 'spacing.edgeEdgeBetweenLayers': 20, // 'spacing.nodeSelfLoop': 20, + // Tweaking options + // 'nodePlacement.favorStraightEdges': true, // 'elk.layered.nodePlacement.favorStraightEdges': true, // 'nodePlacement.feedbackEdges': true, - // 'elk.layered.wrapping.multiEdge.improveCuts': true, - // 'elk.layered.wrapping.multiEdge.improveWrappedEdges': true, + 'elk.layered.wrapping.multiEdge.improveCuts': true, + 'elk.layered.wrapping.multiEdge.improveWrappedEdges': true, // 'elk.layered.wrapping.strategy': 'MULTI_EDGE', - // 'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY', - // 'elk.layered.mergeHierarchyEdges': true, + // 'elk.layered.wrapping.strategy': 'SINGLE_EDGE', + 'elk.layered.edgeRouting.selfLoopDistribution': 'EQUALLY', + 'elk.layered.mergeHierarchyEdges': true, + // 'elk.layered.feedbackEdges': true, // 'elk.layered.crossingMinimization.semiInteractive': true, // 'elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor': 1, // 'elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth': 4.0, // 'elk.layered.wrapping.validify.strategy': 'LOOK_BACK', // 'elk.insideSelfLoops.activate': true, + // 'elk.separateConnectedComponents': true, // 'elk.alg.layered.options.EdgeStraighteningStrategy': 'NONE', // 'elk.layered.considerModelOrder.strategy': 'NODES_AND_EDGES', // NODES_AND_EDGES + // 'elk.layered.considerModelOrder.strategy': 'EDGES', // NODES_AND_EDGES // 'elk.layered.wrapping.cutting.strategy': 'ARD', // NODES_AND_EDGES }, children: [], @@ -809,7 +753,7 @@ export const render = async ( log.info('Drawing flowchart using v4 renderer', elk); // Set the direction of the graph based on the parsed information - const dir = data4Layout.direction || 'DOWN'; + const dir = data4Layout.direction ?? 'DOWN'; elkGraph.layoutOptions['elk.direction'] = dir2ElkDirection(dir); // Create the lookup db for the subgraphs and their children to used when creating @@ -840,15 +784,16 @@ export const render = async ( // Subgraph if (parentLookupDb.childrenById[node.id] !== undefined) { + // Set label and adjust node width separately (avoid side effects in labels array) node.labels = [ { text: node.label, - width: node?.labelData?.width || 50, - height: node?.labelData?.height || 50, + width: node?.labelData?.width ?? 50, + height: node?.labelData?.height ?? 50, }, - (node.width = node.width + 2 * node.padding), - log.debug('UIO node label', node?.labelData?.width, node.padding), ]; + node.width = node.width + 2 * node.padding; + log.debug('UIO node label', node?.labelData?.width, node.padding); node.layoutOptions = { 'spacing.baseValue': 30, 'nodeLabels.placement': '[H_CENTER V_TOP, INSIDE]', @@ -869,11 +814,16 @@ export const render = async ( delete node.height; } }); - elkGraph.edges.forEach((edge: any) => { + log.debug('APA01 processing edges, count:', elkGraph.edges.length); + elkGraph.edges.forEach((edge: any, index: number) => { + log.debug('APA01 processing edge', index, ':', edge); const source = edge.sources[0]; const target = edge.targets[0]; + log.debug('APA01 source:', source, 'target:', target); + log.debug('APA01 nodeDb[source]:', nodeDb[source]); + log.debug('APA01 nodeDb[target]:', nodeDb[target]); - if (nodeDb[source].parentId !== nodeDb[target].parentId) { + if (nodeDb[source] && nodeDb[target] && nodeDb[source].parentId !== nodeDb[target].parentId) { const ancestorId = findCommonAncestor(source, target, parentLookupDb); // an edge that breaks a subgraph has been identified, set configuration accordingly setIncludeChildrenPolicy(source, ancestorId); @@ -881,7 +831,37 @@ export const render = async ( } }); - const g = await elk.layout(elkGraph); + log.debug('APA01 before'); + log.debug('APA01 elkGraph structure:', JSON.stringify(elkGraph, null, 2)); + log.debug('APA01 elkGraph.children length:', elkGraph.children?.length); + log.debug('APA01 elkGraph.edges length:', elkGraph.edges?.length); + + // Validate that all edge references exist as nodes + elkGraph.edges?.forEach((edge: any, index: number) => { + log.debug(`APA01 validating edge ${index}:`, edge); + if (edge.sources) { + edge.sources.forEach((sourceId: any) => { + const sourceExists = elkGraph.children?.some((child: any) => child.id === sourceId); + log.debug(`APA01 source ${sourceId} exists:`, sourceExists); + }); + } + if (edge.targets) { + edge.targets.forEach((targetId: any) => { + const targetExists = elkGraph.children?.some((child: any) => child.id === targetId); + log.debug(`APA01 target ${targetId} exists:`, targetExists); + }); + } + }); + + let g; + try { + g = await elk.layout(elkGraph); + log.debug('APA01 after - success'); + log.info('APA01 layout result:', JSON.stringify(g, null, 2)); + } catch (error) { + log.error('APA01 ELK layout error:', error); + throw error; + } // debugger; await drawNodes(0, 0, g.children, svg, subGraphsEl, 0); @@ -938,10 +918,10 @@ export const render = async ( // sw = Math.max(bbox.width, startNode.width, startNode.labels[0].width); sw = Math.max(startNode.width, startNode.labels[0].width + startNode.padding); // sw = startNode.width; - log.debug( + log.info( 'UIO width', startNode.id, - startNode.with, + startNode.width, 'bbox.width=', bbox.width, 'lw=', @@ -961,7 +941,7 @@ export const render = async ( log.debug( 'UIO width', startNode.id, - startNode.with, + startNode.width, bbox.width, 'EW = ', ew, @@ -969,43 +949,109 @@ export const render = async ( startNode.innerHTML ); } - if (startNode.shape === 'diamond' || startNode.shape === 'diam') { + startNode.x = startNode.offset.posX + startNode.width / 2; + startNode.y = startNode.offset.posY + startNode.height / 2; + endNode.x = endNode.offset.posX + endNode.width / 2; + endNode.y = endNode.offset.posY + endNode.height / 2; + + // Only add center points for non-subgraph nodes or when the edge path doesn't already end near the target + const shouldAddStartCenter = startNode.shape !== 'rect33'; + const shouldAddEndCenter = endNode.shape !== 'rect33'; + + if (shouldAddStartCenter) { edge.points.unshift({ - x: startNode.offset.posX + startNode.width / 2, - y: startNode.offset.posY + startNode.height / 2, + x: startNode.x, + y: startNode.y, }); } - if (endNode.shape === 'diamond' || endNode.shape === 'diam') { + + if (shouldAddEndCenter) { edge.points.push({ - x: endNode.offset.posX + endNode.width / 2, - y: endNode.offset.posY + endNode.height / 2, + x: endNode.x, + y: endNode.y, }); } - edge.points = cutPathAtIntersect( - edge.points.reverse(), - { - x: startNode.offset.posX + startNode.width / 2, - y: startNode.offset.posY + startNode.height / 2, - width: sw, - height: startNode.height, - padding: startNode.padding, - }, - startNode.shape === 'diamond' || startNode.shape === 'diam' - ).reverse(); - - edge.points = cutPathAtIntersect( - edge.points, - { - x: endNode.offset.posX + endNode.width / 2, - y: endNode.offset.posY + endNode.height / 2, - width: ew, - height: endNode.height, - padding: endNode.padding, - }, - endNode.shape === 'diamond' || endNode.shape === 'diam' + // Debug and sanitize points around cutter2 + const prevPoints = Array.isArray(edge.points) ? [...edge.points] : []; + const endBounds = boundsFor(endNode); + log.debug( + 'PPP cutter2: Points before cutter2:', + JSON.stringify(edge.points), + 'endBounds:', + endBounds, + onBorder(endBounds, edge.points[edge.points.length - 1]) ); + // Block for reducing variable scope and guardrails for the cutter function + { + const startBounds = boundsFor(startNode); + const endBounds = boundsFor(endNode); + const startIsGroup = !!startNode?.isGroup; + const endIsGroup = !!endNode?.isGroup; + + const { candidate: startCandidate, centerApprox: startCenterApprox } = + getCandidateBorderPoint(prevPoints as P[], startNode, 'start'); + const { candidate: endCandidate, centerApprox: endCenterApprox } = + getCandidateBorderPoint(prevPoints as P[], endNode, 'end'); + + const skipStart = startIsGroup && onBorder(startBounds, startCandidate); + const skipEnd = endIsGroup && onBorder(endBounds, endCandidate); + + dropAutoCenterPoint(prevPoints as P[], 'start', skipStart && startCenterApprox); + dropAutoCenterPoint(prevPoints as P[], 'end', skipEnd && endCenterApprox); + + if (skipStart || skipEnd) { + if (!skipStart) { + applyStartIntersectionIfNeeded(prevPoints as P[], startNode, startBounds); + } + if (!skipEnd) { + applyEndIntersectionIfNeeded(prevPoints as P[], endNode, endBounds); + } + + log.debug('PPP cutter2: skipping cutter2 due to on-border group endpoint(s)', { + skipStart, + skipEnd, + startCenterApprox, + endCenterApprox, + startCandidate, + endCandidate, + }); + edge.points = prevPoints; + } else { + edge.points = cutter2(startNode, endNode, prevPoints); + } + } + log.debug('PPP cutter2: Points after cutter2:', JSON.stringify(edge.points)); + const hasNaN = (pts: { x: number; y: number }[]) => + pts?.some((p) => !Number.isFinite(p?.x) || !Number.isFinite(p?.y)); + if (!Array.isArray(edge.points) || edge.points.length < 2 || hasNaN(edge.points)) { + log.warn( + 'POI cutter2: Invalid points from cutter2, falling back to prevPoints', + edge.points + ); + // Fallback to previous points and strip any invalid ones just in case + const cleaned = prevPoints.filter((p) => Number.isFinite(p?.x) && Number.isFinite(p?.y)); + edge.points = cleaned.length >= 2 ? cleaned : prevPoints; + } + log.debug('UIO cutter2: Points after cutter2 (sanitized):', edge.points); + // Remove consecutive duplicate points to avoid zero-length segments in path builders + const deduped = edge.points.filter( + (p: { x: number; y: number }, i: number, arr: { x: number; y: number }[]) => { + if (i === 0) { + return true; + } + const prev = arr[i - 1]; + return Math.abs(p.x - prev.x) > 1e-6 || Math.abs(p.y - prev.y) > 1e-6; + } + ); + if (deduped.length !== edge.points.length) { + log.debug('UIO cutter2: removed consecutive duplicate points', { + before: edge.points, + after: deduped, + }); + } + edge.points = deduped; const paths = insertEdge( edgesEl, edge, @@ -1013,7 +1059,8 @@ export const render = async ( data4Layout.type, startNode, endNode, - data4Layout.diagramId + data4Layout.diagramId, + true ); log.info('APA12 edge points after insert', JSON.stringify(edge.points)); diff --git a/packages/mermaid-layout-elk/tsconfig.json b/packages/mermaid-layout-elk/tsconfig.json index 0d701cede..8f83e2bad 100644 --- a/packages/mermaid-layout-elk/tsconfig.json +++ b/packages/mermaid-layout-elk/tsconfig.json @@ -5,6 +5,6 @@ "outDir": "./dist", "types": ["vitest/importMeta", "vitest/globals"] }, - "include": ["./src/**/*.ts"], + "include": ["./src/**/*.ts", "./src/**/*.d.ts"], "typeRoots": ["./src/types"] } diff --git a/packages/mermaid-layout-tidy-tree/README.md b/packages/mermaid-layout-tidy-tree/README.md new file mode 100644 index 000000000..e8ae05f4c --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/README.md @@ -0,0 +1,59 @@ +# @mermaid-js/layout-tidy-tree + +This package provides a bidirectional tidy tree layout engine for Mermaid based on the non-layered-tidy-tree-layout algorithm. + +> [!NOTE] +> The Tidy Tree Layout engine will not be available in all providers that support mermaid by default. +> The websites will have to install the @mermaid-js/layout-tidy-tree package to use the Tidy Tree layout engine. + +## Usage + +``` +--- +config: + layout: tidy-tree +--- +mindmap +root((mindmap)) + A + B +``` + +### With bundlers + +```sh +npm install @mermaid-js/layout-tidy-tree +``` + +```ts +import mermaid from 'mermaid'; +import tidyTreeLayouts from '@mermaid-js/layout-tidy-tree'; + +mermaid.registerLayoutLoaders(tidyTreeLayouts); +``` + +### With CDN + +```html + +``` + +## Tidy Tree Layout Overview + +tidy-tree: The bidirectional tidy tree layout + +The bidirectional tidy tree layout algorithm creates two separate trees that grow horizontally in opposite directions from a central root node: +Left tree: grows horizontally to the left (children alternate: 1st, 3rd, 5th...) +Right tree: grows horizontally to the right (children alternate: 2nd, 4th, 6th...) + +This creates a balanced, symmetric layout that is ideal for mindmaps, organizational charts, and other tree-based diagrams. + +Layout Structure: +[Child 3] ← [Child 1] ← [Root] → [Child 2] → [Child 4] +↓ ↓ ↓ ↓ +[GrandChild] [GrandChild] [GrandChild] [GrandChild] diff --git a/packages/mermaid-layout-tidy-tree/package.json b/packages/mermaid-layout-tidy-tree/package.json new file mode 100644 index 000000000..d8c3ed965 --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/package.json @@ -0,0 +1,46 @@ +{ + "name": "@mermaid-js/layout-tidy-tree", + "version": "0.1.0", + "description": "Tidy-tree layout engine for mermaid", + "module": "dist/mermaid-layout-tidy-tree.core.mjs", + "types": "dist/layouts.d.ts", + "type": "module", + "exports": { + ".": { + "import": "./dist/mermaid-layout-tidy-tree.core.mjs", + "types": "./dist/layouts.d.ts" + }, + "./": "./" + }, + "keywords": [ + "diagram", + "markdown", + "tidy-tree", + "mermaid", + "layout" + ], + "scripts": {}, + "repository": { + "type": "git", + "url": "https://github.com/mermaid-js/mermaid" + }, + "contributors": [ + "Knut Sveidqvist", + "Sidharth Vinod" + ], + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "non-layered-tidy-tree-layout": "^2.0.2" + }, + "devDependencies": { + "@types/d3": "^7.4.3", + "mermaid": "workspace:^" + }, + "peerDependencies": { + "mermaid": "^11.0.2" + }, + "files": [ + "dist" + ] +} diff --git a/packages/mermaid-layout-tidy-tree/src/index.ts b/packages/mermaid-layout-tidy-tree/src/index.ts new file mode 100644 index 000000000..2be1b59e6 --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/src/index.ts @@ -0,0 +1,50 @@ +/** + * Bidirectional Tidy-Tree Layout Algorithm for Generic Diagrams + * + * This module provides a layout algorithm implementation using the + * non-layered-tidy-tree-layout algorithm for positioning nodes and edges + * in tree structures with a bidirectional approach. + * + * The algorithm creates two separate trees that grow horizontally in opposite + * directions from a central root node: + * - Left tree: grows horizontally to the left (children alternate: 1st, 3rd, 5th...) + * - Right tree: grows horizontally to the right (children alternate: 2nd, 4th, 6th...) + * + * This creates a balanced, symmetric layout that is ideal for mindmaps, + * organizational charts, and other tree-based diagrams. + * + * The algorithm follows the unified rendering pattern and can be used + * by any diagram type that provides compatible LayoutData. + */ + +/** + * Render function for the bidirectional tidy-tree layout algorithm + * + * This function follows the unified rendering pattern used by all layout algorithms. + * It takes LayoutData, inserts nodes into DOM, runs the bidirectional tidy-tree layout algorithm, + * and renders the positioned elements to the SVG. + * + * Features: + * - Alternates root children between left and right trees + * - Left tree grows horizontally to the left (rotated 90° counterclockwise) + * - Right tree grows horizontally to the right (rotated 90° clockwise) + * - Uses tidy-tree algorithm for optimal spacing within each tree + * - Creates symmetric, balanced layouts + * - Maintains proper edge connections between all nodes + * + * Layout Structure: + * ``` + * [Child 3] ← [Child 1] ← [Root] → [Child 2] → [Child 4] + * ↓ ↓ ↓ ↓ + * [GrandChild] [GrandChild] [GrandChild] [GrandChild] + * ``` + * + * @param layoutData - Layout data containing nodes, edges, and configuration + * @param svg - SVG element to render to + * @param helpers - Internal helper functions for rendering + * @param options - Rendering options + */ +export { default } from './layouts.js'; +export * from './types.js'; +export * from './layout.js'; +export { render } from './render.js'; diff --git a/packages/mermaid-layout-tidy-tree/src/layout.test.ts b/packages/mermaid-layout-tidy-tree/src/layout.test.ts new file mode 100644 index 000000000..2b3b79b37 --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/src/layout.test.ts @@ -0,0 +1,409 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { executeTidyTreeLayout, validateLayoutData } from './layout.js'; +import type { LayoutResult } from './types.js'; +import type { LayoutData, MermaidConfig } from 'mermaid'; + +// Mock non-layered-tidy-tree-layout +vi.mock('non-layered-tidy-tree-layout', () => ({ + BoundingBox: vi.fn().mockImplementation(() => ({})), + Layout: vi.fn().mockImplementation(() => ({ + layout: vi.fn().mockImplementation((treeData) => { + const result = { ...treeData }; + + if (result.id?.toString().startsWith('virtual-root')) { + result.x = 0; + result.y = 0; + } else { + result.x = 100; + result.y = 50; + } + + if (result.children) { + result.children.forEach((child: any, index: number) => { + child.x = 50 + index * 100; + child.y = 100; + + if (child.children) { + child.children.forEach((grandchild: any, gIndex: number) => { + grandchild.x = 25 + gIndex * 50; + grandchild.y = 200; + }); + } + }); + } + + return { + result, + boundingBox: { + left: 0, + right: 200, + top: 0, + bottom: 250, + }, + }; + }), + })), +})); + +describe('Tidy-Tree Layout Algorithm', () => { + let mockConfig: MermaidConfig; + let mockLayoutData: LayoutData; + + beforeEach(() => { + mockConfig = { + theme: 'default', + } as MermaidConfig; + + mockLayoutData = { + nodes: [ + { + id: 'root', + label: 'Root', + isGroup: false, + shape: 'rect', + width: 100, + height: 50, + padding: 10, + x: 0, + y: 0, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + { + id: 'child1', + label: 'Child 1', + isGroup: false, + shape: 'rect', + width: 80, + height: 40, + padding: 10, + x: 0, + y: 0, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + { + id: 'child2', + label: 'Child 2', + isGroup: false, + shape: 'rect', + width: 80, + height: 40, + padding: 10, + x: 0, + y: 0, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + { + id: 'child3', + label: 'Child 3', + isGroup: false, + shape: 'rect', + width: 80, + height: 40, + padding: 10, + x: 0, + y: 0, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + { + id: 'child4', + label: 'Child 4', + isGroup: false, + shape: 'rect', + width: 80, + height: 40, + padding: 10, + x: 0, + y: 0, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + ], + edges: [ + { + id: 'root_child1', + start: 'root', + end: 'child1', + type: 'edge', + classes: '', + style: [], + animate: false, + arrowTypeEnd: 'arrow_point', + arrowTypeStart: 'none', + }, + { + id: 'root_child2', + start: 'root', + end: 'child2', + type: 'edge', + classes: '', + style: [], + animate: false, + arrowTypeEnd: 'arrow_point', + arrowTypeStart: 'none', + }, + { + id: 'root_child3', + start: 'root', + end: 'child3', + type: 'edge', + classes: '', + style: [], + animate: false, + arrowTypeEnd: 'arrow_point', + arrowTypeStart: 'none', + }, + { + id: 'root_child4', + start: 'root', + end: 'child4', + type: 'edge', + classes: '', + style: [], + animate: false, + arrowTypeEnd: 'arrow_point', + arrowTypeStart: 'none', + }, + ], + config: mockConfig, + direction: 'TB', + type: 'test', + diagramId: 'test-diagram', + markers: [], + }; + }); + + describe('validateLayoutData', () => { + it('should validate correct layout data', () => { + expect(() => validateLayoutData(mockLayoutData)).not.toThrow(); + }); + + it('should throw error for missing data', () => { + expect(() => validateLayoutData(null as any)).toThrow('Layout data is required'); + }); + + it('should throw error for missing config', () => { + const invalidData = { ...mockLayoutData, config: null as any }; + expect(() => validateLayoutData(invalidData)).toThrow('Configuration is required'); + }); + + it('should throw error for invalid nodes array', () => { + const invalidData = { ...mockLayoutData, nodes: null as any }; + expect(() => validateLayoutData(invalidData)).toThrow('Nodes array is required'); + }); + + it('should throw error for invalid edges array', () => { + const invalidData = { ...mockLayoutData, edges: null as any }; + expect(() => validateLayoutData(invalidData)).toThrow('Edges array is required'); + }); + }); + + describe('executeTidyTreeLayout function', () => { + it('should execute layout algorithm successfully', async () => { + const result: LayoutResult = await executeTidyTreeLayout(mockLayoutData); + + expect(result).toBeDefined(); + expect(result.nodes).toBeDefined(); + expect(result.edges).toBeDefined(); + expect(Array.isArray(result.nodes)).toBe(true); + expect(Array.isArray(result.edges)).toBe(true); + }); + + it('should return positioned nodes with coordinates', async () => { + const result: LayoutResult = await executeTidyTreeLayout(mockLayoutData); + + expect(result.nodes.length).toBeGreaterThan(0); + result.nodes.forEach((node) => { + expect(node.x).toBeDefined(); + expect(node.y).toBeDefined(); + expect(typeof node.x).toBe('number'); + expect(typeof node.y).toBe('number'); + }); + }); + + it('should return positioned edges with coordinates', async () => { + const result: LayoutResult = await executeTidyTreeLayout(mockLayoutData); + + expect(result.edges.length).toBeGreaterThan(0); + result.edges.forEach((edge) => { + expect(edge.startX).toBeDefined(); + expect(edge.startY).toBeDefined(); + expect(edge.midX).toBeDefined(); + expect(edge.midY).toBeDefined(); + expect(edge.endX).toBeDefined(); + expect(edge.endY).toBeDefined(); + }); + }); + + it('should handle empty layout data gracefully', async () => { + const emptyData: LayoutData = { + ...mockLayoutData, + nodes: [], + edges: [], + }; + + await expect(executeTidyTreeLayout(emptyData)).rejects.toThrow( + 'No nodes found in layout data' + ); + }); + + it('should throw error for missing nodes', async () => { + const invalidData = { ...mockLayoutData, nodes: [] }; + + await expect(executeTidyTreeLayout(invalidData)).rejects.toThrow( + 'No nodes found in layout data' + ); + }); + + it('should handle empty edges (single node tree)', async () => { + const singleNodeData = { + ...mockLayoutData, + edges: [], + nodes: [mockLayoutData.nodes[0]], + }; + + const result = await executeTidyTreeLayout(singleNodeData); + expect(result).toBeDefined(); + expect(result.nodes).toHaveLength(1); + expect(result.edges).toHaveLength(0); + }); + + it('should create bidirectional dual-tree layout with alternating left/right children', async () => { + const result = await executeTidyTreeLayout(mockLayoutData); + + expect(result).toBeDefined(); + expect(result.nodes).toHaveLength(5); + + const rootNode = result.nodes.find((node) => node.id === 'root'); + expect(rootNode).toBeDefined(); + expect(rootNode!.x).toBe(0); + expect(rootNode!.y).toBe(20); + + const child1 = result.nodes.find((node) => node.id === 'child1'); + const child2 = result.nodes.find((node) => node.id === 'child2'); + const child3 = result.nodes.find((node) => node.id === 'child3'); + const child4 = result.nodes.find((node) => node.id === 'child4'); + + expect(child1).toBeDefined(); + expect(child2).toBeDefined(); + expect(child3).toBeDefined(); + expect(child4).toBeDefined(); + + expect(child1!.x).toBeLessThan(rootNode!.x); + expect(child2!.x).toBeGreaterThan(rootNode!.x); + expect(child3!.x).toBeLessThan(rootNode!.x); + expect(child4!.x).toBeGreaterThan(rootNode!.x); + + expect(child1!.x).toBeLessThan(-100); + expect(child3!.x).toBeLessThan(-100); + + expect(child2!.x).toBeGreaterThan(100); + expect(child4!.x).toBeGreaterThan(100); + }); + + it('should correctly transpose coordinates to prevent high nodes from covering nodes above them', async () => { + const testData = { + ...mockLayoutData, + nodes: [ + { + id: 'root', + label: 'Root', + isGroup: false, + shape: 'rect' as const, + width: 100, + height: 50, + padding: 10, + x: 0, + y: 0, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + { + id: 'tall-child', + label: 'Tall Child', + isGroup: false, + shape: 'rect' as const, + width: 80, + height: 120, + padding: 10, + x: 0, + y: 0, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + { + id: 'short-child', + label: 'Short Child', + isGroup: false, + shape: 'rect' as const, + width: 80, + height: 30, + padding: 10, + x: 0, + y: 0, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + ], + edges: [ + { + id: 'root_tall', + start: 'root', + end: 'tall-child', + type: 'edge', + classes: '', + style: [], + animate: false, + arrowTypeEnd: 'arrow_point', + arrowTypeStart: 'none', + }, + { + id: 'root_short', + start: 'root', + end: 'short-child', + type: 'edge', + classes: '', + style: [], + animate: false, + arrowTypeEnd: 'arrow_point', + arrowTypeStart: 'none', + }, + ], + }; + + const result = await executeTidyTreeLayout(testData); + + expect(result).toBeDefined(); + expect(result.nodes).toHaveLength(3); + + const rootNode = result.nodes.find((node) => node.id === 'root'); + const tallChild = result.nodes.find((node) => node.id === 'tall-child'); + const shortChild = result.nodes.find((node) => node.id === 'short-child'); + + expect(rootNode).toBeDefined(); + expect(tallChild).toBeDefined(); + expect(shortChild).toBeDefined(); + + expect(tallChild!.x).not.toBe(shortChild!.x); + + expect(tallChild!.width).toBe(80); + expect(tallChild!.height).toBe(120); + expect(shortChild!.width).toBe(80); + expect(shortChild!.height).toBe(30); + + const yDifference = Math.abs(tallChild!.y - shortChild!.y); + expect(yDifference).toBeGreaterThanOrEqual(0); + }); + }); +}); diff --git a/packages/mermaid-layout-tidy-tree/src/layout.ts b/packages/mermaid-layout-tidy-tree/src/layout.ts new file mode 100644 index 000000000..6cc06a9ab --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/src/layout.ts @@ -0,0 +1,629 @@ +import type { LayoutData } from 'mermaid'; +import type { Bounds, Point } from 'mermaid/src/types.js'; +import { BoundingBox, Layout } from 'non-layered-tidy-tree-layout'; +import type { + Edge, + LayoutResult, + Node, + PositionedEdge, + PositionedNode, + TidyTreeNode, +} from './types.js'; + +/** + * Execute the tidy-tree layout algorithm on generic layout data + * + * This function takes layout data and uses the non-layered-tidy-tree-layout + * algorithm to calculate optimal node positions for tree structures. + * + * @param data - The layout data containing nodes, edges, and configuration + * @param config - Mermaid configuration object + * @returns Promise resolving to layout result with positioned nodes and edges + */ +export function executeTidyTreeLayout(data: LayoutData): Promise { + let intersectionShift = 50; + + return new Promise((resolve, reject) => { + try { + if (!data.nodes || !Array.isArray(data.nodes) || data.nodes.length === 0) { + throw new Error('No nodes found in layout data'); + } + + if (!data.edges || !Array.isArray(data.edges)) { + data.edges = []; + } + + const { leftTree, rightTree, rootNode } = convertToDualTreeFormat(data); + + const gap = 20; + const bottomPadding = 40; + intersectionShift = 30; + + const bb = new BoundingBox(gap, bottomPadding); + const layout = new Layout(bb); + + let leftResult = null; + let rightResult = null; + + if (leftTree) { + const leftLayoutResult = layout.layout(leftTree); + leftResult = leftLayoutResult.result; + } + + if (rightTree) { + const rightLayoutResult = layout.layout(rightTree); + rightResult = rightLayoutResult.result; + } + + const positionedNodes = combineAndPositionTrees(rootNode, leftResult, rightResult); + const positionedEdges = calculateEdgePositions( + data.edges, + positionedNodes, + intersectionShift + ); + resolve({ + nodes: positionedNodes, + edges: positionedEdges, + }); + } catch (error) { + reject(error); + } + }); +} + +/** + * Convert LayoutData to dual-tree format (left and right trees) + * + * This function builds two separate tree structures from the nodes and edges, + * alternating children between left and right trees. + */ +function convertToDualTreeFormat(data: LayoutData): { + leftTree: TidyTreeNode | null; + rightTree: TidyTreeNode | null; + rootNode: TidyTreeNode; +} { + const { nodes, edges } = data; + + const nodeMap = new Map(); + nodes.forEach((node) => nodeMap.set(node.id, node)); + + const children = new Map(); + const parents = new Map(); + + edges.forEach((edge) => { + const parentId = edge.start; + const childId = edge.end; + + if (parentId && childId) { + if (!children.has(parentId)) { + children.set(parentId, []); + } + children.get(parentId)!.push(childId); + parents.set(childId, parentId); + } + }); + + const rootNodeData = nodes.find((node) => !parents.has(node.id)); + if (!rootNodeData && nodes.length === 0) { + throw new Error('No nodes available to create tree'); + } + + const actualRoot = rootNodeData ?? nodes[0]; + + const rootNode: TidyTreeNode = { + id: actualRoot.id, + width: actualRoot.width ?? 100, + height: actualRoot.height ?? 50, + _originalNode: actualRoot, + }; + + const rootChildren = children.get(actualRoot.id) ?? []; + const leftChildren: string[] = []; + const rightChildren: string[] = []; + + rootChildren.forEach((childId, index) => { + if (index % 2 === 0) { + leftChildren.push(childId); + } else { + rightChildren.push(childId); + } + }); + + const leftTree = leftChildren.length > 0 ? buildSubTree(leftChildren, children, nodeMap) : null; + + const rightTree = + rightChildren.length > 0 ? buildSubTree(rightChildren, children, nodeMap) : null; + + return { leftTree, rightTree, rootNode }; +} + +/** + * Build a subtree from a list of root children + * For horizontal trees, we need to transpose width/height since the tree will be rotated 90° + */ +function buildSubTree( + rootChildren: string[], + children: Map, + nodeMap: Map +): TidyTreeNode { + const virtualRoot: TidyTreeNode = { + id: `virtual-root-${Math.random()}`, + width: 1, + height: 1, + children: rootChildren + .map((childId) => nodeMap.get(childId)) + .filter((child): child is Node => child !== undefined) + .map((child) => convertNodeToTidyTreeTransposed(child, children, nodeMap)), + }; + + return virtualRoot; +} + +/** + * Recursively convert a node and its children to tidy-tree format + * This version transposes width/height for horizontal tree layout + */ +function convertNodeToTidyTreeTransposed( + node: Node, + children: Map, + nodeMap: Map +): TidyTreeNode { + const childIds = children.get(node.id) ?? []; + const childNodes = childIds + .map((childId) => nodeMap.get(childId)) + .filter((child): child is Node => child !== undefined) + .map((child) => convertNodeToTidyTreeTransposed(child, children, nodeMap)); + + return { + id: node.id, + width: node.height ?? 50, + height: node.width ?? 100, + children: childNodes.length > 0 ? childNodes : undefined, + _originalNode: node, + }; +} +/** + * Combine and position the left and right trees around the root node + * Creates a bidirectional layout where left tree grows left and right tree grows right + */ +function combineAndPositionTrees( + rootNode: TidyTreeNode, + leftResult: TidyTreeNode | null, + rightResult: TidyTreeNode | null +): PositionedNode[] { + const positionedNodes: PositionedNode[] = []; + + const rootX = 0; + const rootY = 0; + + const treeSpacing = rootNode.width / 2 + 30; + const leftTreeNodes: PositionedNode[] = []; + const rightTreeNodes: PositionedNode[] = []; + + if (leftResult?.children) { + positionLeftTreeBidirectional(leftResult.children, leftTreeNodes, rootX - treeSpacing, rootY); + } + + if (rightResult?.children) { + positionRightTreeBidirectional( + rightResult.children, + rightTreeNodes, + rootX + treeSpacing, + rootY + ); + } + + let leftTreeCenterY = 0; + let rightTreeCenterY = 0; + + if (leftTreeNodes.length > 0) { + const leftTreeXPositions = [...new Set(leftTreeNodes.map((node) => node.x))].sort( + (a, b) => b - a + ); + const firstLevelLeftX = leftTreeXPositions[0]; + const firstLevelLeftNodes = leftTreeNodes.filter((node) => node.x === firstLevelLeftX); + + if (firstLevelLeftNodes.length > 0) { + const leftMinY = Math.min( + ...firstLevelLeftNodes.map((node) => node.y - (node.height ?? 50) / 2) + ); + const leftMaxY = Math.max( + ...firstLevelLeftNodes.map((node) => node.y + (node.height ?? 50) / 2) + ); + leftTreeCenterY = (leftMinY + leftMaxY) / 2; + } + } + + if (rightTreeNodes.length > 0) { + const rightTreeXPositions = [...new Set(rightTreeNodes.map((node) => node.x))].sort( + (a, b) => a - b + ); + const firstLevelRightX = rightTreeXPositions[0]; + const firstLevelRightNodes = rightTreeNodes.filter((node) => node.x === firstLevelRightX); + + if (firstLevelRightNodes.length > 0) { + const rightMinY = Math.min( + ...firstLevelRightNodes.map((node) => node.y - (node.height ?? 50) / 2) + ); + const rightMaxY = Math.max( + ...firstLevelRightNodes.map((node) => node.y + (node.height ?? 50) / 2) + ); + rightTreeCenterY = (rightMinY + rightMaxY) / 2; + } + } + + const leftTreeOffset = -leftTreeCenterY; + const rightTreeOffset = -rightTreeCenterY; + + positionedNodes.push({ + id: String(rootNode.id), + x: rootX, + y: rootY + 20, + section: 'root', + width: rootNode._originalNode?.width ?? rootNode.width, + height: rootNode._originalNode?.height ?? rootNode.height, + originalNode: rootNode._originalNode, + }); + + const leftTreeNodesWithOffset = leftTreeNodes.map((node) => ({ + id: node.id, + x: node.x - (node.width ?? 0) / 2, + y: node.y + leftTreeOffset + (node.height ?? 0) / 2, + section: 'left' as const, + width: node.width, + height: node.height, + originalNode: node.originalNode, + })); + + const rightTreeNodesWithOffset = rightTreeNodes.map((node) => ({ + id: node.id, + x: node.x + (node.width ?? 0) / 2, + y: node.y + rightTreeOffset + (node.height ?? 0) / 2, + section: 'right' as const, + width: node.width, + height: node.height, + originalNode: node.originalNode, + })); + + positionedNodes.push(...leftTreeNodesWithOffset); + positionedNodes.push(...rightTreeNodesWithOffset); + + return positionedNodes; +} + +/** + * Position nodes from the left tree in a bidirectional layout (grows to the left) + * Rotates the tree 90 degrees counterclockwise so it grows horizontally to the left + */ +function positionLeftTreeBidirectional( + nodes: TidyTreeNode[], + positionedNodes: PositionedNode[], + offsetX: number, + offsetY: number +): void { + nodes.forEach((node) => { + const distanceFromRoot = node.y ?? 0; + const verticalPosition = node.x ?? 0; + + const originalWidth = node._originalNode?.width ?? 100; + const originalHeight = node._originalNode?.height ?? 50; + + const adjustedY = offsetY + verticalPosition; + + positionedNodes.push({ + id: String(node.id), + x: offsetX - distanceFromRoot, + y: adjustedY, + width: originalWidth, + height: originalHeight, + originalNode: node._originalNode, + }); + + if (node.children) { + positionLeftTreeBidirectional(node.children, positionedNodes, offsetX, offsetY); + } + }); +} + +/** + * Position nodes from the right tree in a bidirectional layout (grows to the right) + * Rotates the tree 90 degrees clockwise so it grows horizontally to the right + */ +function positionRightTreeBidirectional( + nodes: TidyTreeNode[], + positionedNodes: PositionedNode[], + offsetX: number, + offsetY: number +): void { + nodes.forEach((node) => { + const distanceFromRoot = node.y ?? 0; + const verticalPosition = node.x ?? 0; + + const originalWidth = node._originalNode?.width ?? 100; + const originalHeight = node._originalNode?.height ?? 50; + + const adjustedY = offsetY + verticalPosition; + + positionedNodes.push({ + id: String(node.id), + x: offsetX + distanceFromRoot, + y: adjustedY, + width: originalWidth, + height: originalHeight, + originalNode: node._originalNode, + }); + + if (node.children) { + positionRightTreeBidirectional(node.children, positionedNodes, offsetX, offsetY); + } + }); +} + +/** + * Calculate the intersection point of a line with a circle + * @param circle - Circle coordinates and radius + * @param lineStart - Starting point of the line + * @param lineEnd - Ending point of the line + * @returns The intersection point + */ +function computeCircleEdgeIntersection(circle: Bounds, lineStart: Point, lineEnd: Point): Point { + const radius = Math.min(circle.width, circle.height) / 2; + + const dx = lineEnd.x - lineStart.x; + const dy = lineEnd.y - lineStart.y; + const length = Math.sqrt(dx * dx + dy * dy); + + if (length === 0) { + return lineStart; + } + + const nx = dx / length; + const ny = dy / length; + + return { + x: circle.x - nx * radius, + y: circle.y - ny * radius, + }; +} + +function intersection(node: PositionedNode, outsidePoint: Point, insidePoint: Point): Point { + const x = node.x; + const y = node.y; + + if (!node.width || !node.height) { + return { x: outsidePoint.x, y: outsidePoint.y }; + } + const dx = Math.abs(x - insidePoint.x); + const w = node?.width / 2; + let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx; + const h = node.height / 2; + + const Q = Math.abs(outsidePoint.y - insidePoint.y); + const R = Math.abs(outsidePoint.x - insidePoint.x); + + if (Math.abs(y - outsidePoint.y) * w > Math.abs(x - outsidePoint.x) * h) { + // Intersection is top or bottom of rect. + const q = insidePoint.y < outsidePoint.y ? outsidePoint.y - h - y : y - h - outsidePoint.y; + r = (R * q) / Q; + const res = { + x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - R + r, + y: insidePoint.y < outsidePoint.y ? insidePoint.y + Q - q : insidePoint.y - Q + q, + }; + + if (r === 0) { + res.x = outsidePoint.x; + res.y = outsidePoint.y; + } + if (R === 0) { + res.x = outsidePoint.x; + } + if (Q === 0) { + res.y = outsidePoint.y; + } + + return res; + } else { + if (insidePoint.x < outsidePoint.x) { + r = outsidePoint.x - w - x; + } else { + r = x - w - outsidePoint.x; + } + const q = (Q * r) / R; + let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r; + let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q; + + if (r === 0) { + _x = outsidePoint.x; + _y = outsidePoint.y; + } + if (R === 0) { + _x = outsidePoint.x; + } + if (Q === 0) { + _y = outsidePoint.y; + } + + return { x: _x, y: _y }; + } +} + +/** + * Calculate edge positions based on positioned nodes + * Now includes tree membership and node dimensions for precise edge calculations + * Edges now stop at shape boundaries instead of extending to centers + */ +function calculateEdgePositions( + edges: Edge[], + positionedNodes: PositionedNode[], + intersectionShift: number +): PositionedEdge[] { + const nodeInfo = new Map(); + positionedNodes.forEach((node) => { + nodeInfo.set(node.id, node); + }); + + return edges.map((edge) => { + const sourceNode = nodeInfo.get(edge.start ?? ''); + const targetNode = nodeInfo.get(edge.end ?? ''); + + if (!sourceNode || !targetNode) { + return { + id: edge.id, + source: edge.start ?? '', + target: edge.end ?? '', + startX: 0, + startY: 0, + midX: 0, + midY: 0, + endX: 0, + endY: 0, + points: [{ x: 0, y: 0 }], + sourceSection: undefined, + targetSection: undefined, + sourceWidth: undefined, + sourceHeight: undefined, + targetWidth: undefined, + targetHeight: undefined, + }; + } + + const sourceCenter = { x: sourceNode.x, y: sourceNode.y }; + const targetCenter = { x: targetNode.x, y: targetNode.y }; + + const isSourceRound = ['circle', 'cloud', 'bang'].includes( + sourceNode.originalNode?.shape ?? '' + ); + const isTargetRound = ['circle', 'cloud', 'bang'].includes( + targetNode.originalNode?.shape ?? '' + ); + + let startPos = isSourceRound + ? computeCircleEdgeIntersection( + { + x: sourceNode.x, + y: sourceNode.y, + width: sourceNode.width ?? 100, + height: sourceNode.height ?? 100, + }, + targetCenter, + sourceCenter + ) + : intersection(sourceNode, sourceCenter, targetCenter); + + let endPos = isTargetRound + ? computeCircleEdgeIntersection( + { + x: targetNode.x, + y: targetNode.y, + width: targetNode.width ?? 100, + height: targetNode.height ?? 100, + }, + sourceCenter, + targetCenter + ) + : intersection(targetNode, targetCenter, sourceCenter); + + const midX = (startPos.x + endPos.x) / 2; + const midY = (startPos.y + endPos.y) / 2; + + const points = [startPos]; + if (sourceNode.section === 'left') { + points.push({ + x: sourceNode.x - (sourceNode.width ?? 0) / 2 - intersectionShift, + y: sourceNode.y, + }); + } else if (sourceNode.section === 'right') { + points.push({ + x: sourceNode.x + (sourceNode.width ?? 0) / 2 + intersectionShift, + y: sourceNode.y, + }); + } + if (targetNode.section === 'left') { + points.push({ + x: targetNode.x + (targetNode.width ?? 0) / 2 + intersectionShift, + y: targetNode.y, + }); + } else if (targetNode.section === 'right') { + points.push({ + x: targetNode.x - (targetNode.width ?? 0) / 2 - intersectionShift, + y: targetNode.y, + }); + } + + points.push(endPos); + + const secondPoint = points.length > 1 ? points[1] : targetCenter; + startPos = isSourceRound + ? computeCircleEdgeIntersection( + { + x: sourceNode.x, + y: sourceNode.y, + width: sourceNode.width ?? 100, + height: sourceNode.height ?? 100, + }, + secondPoint, + sourceCenter + ) + : intersection(sourceNode, secondPoint, sourceCenter); + points[0] = startPos; + + const secondLastPoint = points.length > 1 ? points[points.length - 2] : sourceCenter; + endPos = isTargetRound + ? computeCircleEdgeIntersection( + { + x: targetNode.x, + y: targetNode.y, + width: targetNode.width ?? 100, + height: targetNode.height ?? 100, + }, + secondLastPoint, + targetCenter + ) + : intersection(targetNode, secondLastPoint, targetCenter); + points[points.length - 1] = endPos; + + return { + id: edge.id, + source: edge.start ?? '', + target: edge.end ?? '', + startX: startPos.x, + startY: startPos.y, + midX, + midY, + endX: endPos.x, + endY: endPos.y, + points, + sourceSection: sourceNode?.section, + targetSection: targetNode?.section, + sourceWidth: sourceNode?.width, + sourceHeight: sourceNode?.height, + targetWidth: targetNode?.width, + targetHeight: targetNode?.height, + }; + }); +} + +/** + * Validate layout data structure + * @param data - The data to validate + * @returns True if data is valid, throws error otherwise + */ +export function validateLayoutData(data: LayoutData): boolean { + if (!data) { + throw new Error('Layout data is required'); + } + + if (!data.config) { + throw new Error('Configuration is required in layout data'); + } + + if (!Array.isArray(data.nodes)) { + throw new Error('Nodes array is required in layout data'); + } + + if (!Array.isArray(data.edges)) { + throw new Error('Edges array is required in layout data'); + } + + return true; +} diff --git a/packages/mermaid-layout-tidy-tree/src/layouts.ts b/packages/mermaid-layout-tidy-tree/src/layouts.ts new file mode 100644 index 000000000..d5eac8992 --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/src/layouts.ts @@ -0,0 +1,13 @@ +import type { LayoutLoaderDefinition } from 'mermaid'; + +const loader = async () => await import(`./render.js`); + +const tidyTreeLayout: LayoutLoaderDefinition[] = [ + { + name: 'tidy-tree', + loader, + algorithm: 'tidy-tree', + }, +]; + +export default tidyTreeLayout; diff --git a/packages/mermaid-layout-tidy-tree/src/non-layered-tidy-tree-layout.d.ts b/packages/mermaid-layout-tidy-tree/src/non-layered-tidy-tree-layout.d.ts new file mode 100644 index 000000000..248b5c05f --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/src/non-layered-tidy-tree-layout.d.ts @@ -0,0 +1,18 @@ +declare module 'non-layered-tidy-tree-layout' { + export class BoundingBox { + constructor(gap: number, bottomPadding: number); + } + + export class Layout { + constructor(boundingBox: BoundingBox); + layout(data: any): { + result: any; + boundingBox: { + left: number; + right: number; + top: number; + bottom: number; + }; + }; + } +} diff --git a/packages/mermaid-layout-tidy-tree/src/render.ts b/packages/mermaid-layout-tidy-tree/src/render.ts new file mode 100644 index 000000000..4ce5e1deb --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/src/render.ts @@ -0,0 +1,180 @@ +import type { InternalHelpers, LayoutData, RenderOptions, SVG } from 'mermaid'; +import { executeTidyTreeLayout } from './layout.js'; + +interface NodeWithPosition { + id: string; + x?: number; + y?: number; + width?: number; + height?: number; + domId?: any; + [key: string]: any; +} + +/** + * Render function for bidirectional tidy-tree layout algorithm + * + * This follows the same pattern as ELK and dagre renderers: + * 1. Insert nodes into DOM to get their actual dimensions + * 2. Run the bidirectional tidy-tree layout algorithm to calculate positions + * 3. Position the nodes and edges based on layout results + * + * The bidirectional layout creates two trees that grow horizontally in opposite + * directions from a central root node: + * - Left tree: grows horizontally to the left (children: 1st, 3rd, 5th...) + * - Right tree: grows horizontally to the right (children: 2nd, 4th, 6th...) + */ +export const render = async ( + data4Layout: LayoutData, + svg: SVG, + { + insertCluster, + insertEdge, + insertEdgeLabel, + insertMarkers, + insertNode, + log, + positionEdgeLabel, + }: InternalHelpers, + { algorithm: _algorithm }: RenderOptions +) => { + const nodeDb: Record = {}; + const clusterDb: Record = {}; + + const element = svg.select('g'); + insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId); + + const subGraphsEl = element.insert('g').attr('class', 'subgraphs'); + const edgePaths = element.insert('g').attr('class', 'edgePaths'); + const edgeLabels = element.insert('g').attr('class', 'edgeLabels'); + const nodes = element.insert('g').attr('class', 'nodes'); + // Step 1: Insert nodes into DOM to get their actual dimensions + log.debug('Inserting nodes into DOM for dimension calculation'); + + await Promise.all( + data4Layout.nodes.map(async (node) => { + if (node.isGroup) { + const clusterNode: NodeWithPosition = { + ...node, + id: node.id, + width: node.width, + height: node.height, + }; + clusterDb[node.id] = clusterNode; + nodeDb[node.id] = clusterNode; + + await insertCluster(subGraphsEl, node); + } else { + const nodeWithPosition: NodeWithPosition = { + ...node, + id: node.id, + width: node.width, + height: node.height, + }; + nodeDb[node.id] = nodeWithPosition; + + const nodeEl = await insertNode(nodes, node, { + config: data4Layout.config, + dir: data4Layout.direction || 'TB', + }); + + const boundingBox = nodeEl.node()!.getBBox(); + nodeWithPosition.width = boundingBox.width; + nodeWithPosition.height = boundingBox.height; + nodeWithPosition.domId = nodeEl; + + log.debug(`Node ${node.id} dimensions: ${boundingBox.width}x${boundingBox.height}`); + } + }) + ); + // Step 2: Run the bidirectional tidy-tree layout algorithm + log.debug('Running bidirectional tidy-tree layout algorithm'); + + const updatedLayoutData = { + ...data4Layout, + nodes: data4Layout.nodes.map((node) => { + const nodeWithDimensions = nodeDb[node.id]; + return { + ...node, + width: nodeWithDimensions.width ?? node.width ?? 100, + height: nodeWithDimensions.height ?? node.height ?? 50, + }; + }), + }; + + const layoutResult = await executeTidyTreeLayout(updatedLayoutData); + // Step 3: Position the nodes based on bidirectional layout results + log.debug('Positioning nodes based on bidirectional layout results'); + + layoutResult.nodes.forEach((positionedNode) => { + const node = nodeDb[positionedNode.id]; + if (node?.domId) { + // Position the node at the calculated coordinates from bidirectional layout + // The layout algorithm has already calculated positions for: + // - Root node at center (0, 0) + // - Left tree nodes with negative x coordinates (growing left) + // - Right tree nodes with positive x coordinates (growing right) + node.domId.attr('transform', `translate(${positionedNode.x}, ${positionedNode.y})`); + // Store the final position + node.x = positionedNode.x; + node.y = positionedNode.y; + // Step 3: Position the nodes based on bidirectional layout results + log.debug(`Positioned node ${node.id} at (${positionedNode.x}, ${positionedNode.y})`); + } + }); + + log.debug('Inserting and positioning edges'); + + await Promise.all( + data4Layout.edges.map(async (edge) => { + await insertEdgeLabel(edgeLabels, edge); + + const startNode = nodeDb[edge.start ?? '']; + const endNode = nodeDb[edge.end ?? '']; + + if (startNode && endNode) { + const positionedEdge = layoutResult.edges.find((e) => e.id === edge.id); + + if (positionedEdge) { + log.debug('APA01 positionedEdge', positionedEdge); + const edgeWithPath = { + ...edge, + points: positionedEdge.points, + }; + const paths = insertEdge( + edgePaths, + edgeWithPath, + clusterDb, + data4Layout.type, + startNode, + endNode, + data4Layout.diagramId + ); + + positionEdgeLabel(edgeWithPath, paths); + } else { + const edgeWithPath = { + ...edge, + points: [ + { x: startNode.x ?? 0, y: startNode.y ?? 0 }, + { x: endNode.x ?? 0, y: endNode.y ?? 0 }, + ], + }; + + const paths = insertEdge( + edgePaths, + edgeWithPath, + clusterDb, + data4Layout.type, + startNode, + endNode, + data4Layout.diagramId + ); + positionEdgeLabel(edgeWithPath, paths); + } + } + }) + ); + + log.debug('Bidirectional tidy-tree rendering completed'); +}; diff --git a/packages/mermaid-layout-tidy-tree/src/types.ts b/packages/mermaid-layout-tidy-tree/src/types.ts new file mode 100644 index 000000000..2015a4909 --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/src/types.ts @@ -0,0 +1,69 @@ +import type { LayoutData } from 'mermaid'; + +export type Node = LayoutData['nodes'][number]; +export type Edge = LayoutData['edges'][number]; + +/** + * Positioned node after layout calculation + */ +export interface PositionedNode { + id: string; + x: number; + y: number; + section?: 'root' | 'left' | 'right'; + width?: number; + height?: number; + originalNode?: Node; + [key: string]: unknown; +} + +/** + * Positioned edge after layout calculation + */ +export interface PositionedEdge { + id: string; + source: string; + target: string; + startX: number; + startY: number; + midX: number; + midY: number; + endX: number; + endY: number; + sourceSection?: 'root' | 'left' | 'right'; + targetSection?: 'root' | 'left' | 'right'; + sourceWidth?: number; + sourceHeight?: number; + targetWidth?: number; + targetHeight?: number; + [key: string]: unknown; +} + +/** + * Result of layout algorithm execution + */ +export interface LayoutResult { + nodes: PositionedNode[]; + edges: PositionedEdge[]; +} + +/** + * Tidy-tree node structure compatible with non-layered-tidy-tree-layout + */ +export interface TidyTreeNode { + id: string | number; + width: number; + height: number; + x?: number; + y?: number; + children?: TidyTreeNode[]; + _originalNode?: Node; +} + +/** + * Tidy-tree layout configuration + */ +export interface TidyTreeLayoutConfig { + gap: number; + bottomPadding: number; +} diff --git a/packages/mermaid-layout-tidy-tree/tsconfig.json b/packages/mermaid-layout-tidy-tree/tsconfig.json new file mode 100644 index 000000000..8f83e2bad --- /dev/null +++ b/packages/mermaid-layout-tidy-tree/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "types": ["vitest/importMeta", "vitest/globals"] + }, + "include": ["./src/**/*.ts", "./src/**/*.d.ts"], + "typeRoots": ["./src/types"] +} diff --git a/packages/mermaid/CHANGELOG.md b/packages/mermaid/CHANGELOG.md index df67f0cfd..fc2f97fdf 100644 --- a/packages/mermaid/CHANGELOG.md +++ b/packages/mermaid/CHANGELOG.md @@ -229,7 +229,6 @@ - [#5999](https://github.com/mermaid-js/mermaid/pull/5999) [`742ad7c`](https://github.com/mermaid-js/mermaid/commit/742ad7c130964df1fb5544e909d9556081285f68) Thanks [@knsv](https://github.com/knsv)! - Adding Kanban board, a new diagram type - [#5880](https://github.com/mermaid-js/mermaid/pull/5880) [`bdf145f`](https://github.com/mermaid-js/mermaid/commit/bdf145ffe362462176d9c1e68d5f3ff5c9d962b0) Thanks [@yari-dewalt](https://github.com/yari-dewalt)! - Class diagram changes: - - Updates the class diagram to the new unified way of rendering. - Includes a new "classBox" shape to be used in diagrams - Other updates such as: diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index 56446c34b..43385ffe0 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -68,7 +68,7 @@ }, "dependencies": { "@braintree/sanitize-url": "^7.0.4", - "@iconify/utils": "^2.1.33", + "@iconify/utils": "^3.0.1", "@mermaid-js/parser": "workspace:^", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", @@ -123,8 +123,8 @@ "rimraf": "^6.0.1", "start-server-and-test": "^2.0.10", "type-fest": "^4.35.0", - "typedoc": "^0.27.8", - "typedoc-plugin-markdown": "^4.4.2", + "typedoc": "^0.28.9", + "typedoc-plugin-markdown": "^4.8.0", "typescript": "~5.7.3", "unist-util-flatmap": "^1.0.0", "unist-util-visit": "^5.0.0", diff --git a/packages/mermaid/scripts/docs.spec.ts b/packages/mermaid/scripts/docs.spec.ts index 68677d4c9..70923e226 100644 --- a/packages/mermaid/scripts/docs.spec.ts +++ b/packages/mermaid/scripts/docs.spec.ts @@ -171,7 +171,9 @@ This Markdown should be kept. expect(buildShapeDoc()).toMatchInlineSnapshot(` "| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** | | --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- | + | Bang | Bang | \`bang\` | Bang | \`bang\` | | Card | Notched Rectangle | \`notch-rect\` | Represents a card | \`card\`, \`notched-rectangle\` | + | Cloud | Cloud | \`cloud\` | cloud | \`cloud\` | | Collate | Hourglass | \`hourglass\` | Represents a collate operation | \`collate\`, \`hourglass\` | | Com Link | Lightning Bolt | \`bolt\` | Communication link | \`com-link\`, \`lightning-bolt\` | | Comment | Curly Brace | \`brace\` | Adds a comment | \`brace-l\`, \`comment\` | diff --git a/packages/mermaid/src/config.spec.ts b/packages/mermaid/src/config.spec.ts index 000be1282..7fbae03af 100644 --- a/packages/mermaid/src/config.spec.ts +++ b/packages/mermaid/src/config.spec.ts @@ -78,3 +78,187 @@ describe('when working with site config', () => { expect(config_4.altFontFamily).toBeUndefined(); }); }); + +describe('getUserDefinedConfig', () => { + beforeEach(() => { + configApi.reset(); + }); + + it('should return empty object when no user config is defined', () => { + const userConfig = configApi.getUserDefinedConfig(); + expect(userConfig).toEqual({}); + }); + + it('should return config from initialize only', () => { + const initConfig: MermaidConfig = { theme: 'dark', fontFamily: 'Arial' }; + configApi.saveConfigFromInitialize(initConfig); + + const userConfig = configApi.getUserDefinedConfig(); + expect(userConfig).toEqual(initConfig); + }); + + it('should return config from directives only', () => { + const directive1: MermaidConfig = { layout: 'elk', fontSize: 14 }; + const directive2: MermaidConfig = { theme: 'forest' }; + + configApi.addDirective(directive1); + configApi.addDirective(directive2); + + expect(configApi.getUserDefinedConfig()).toMatchInlineSnapshot(` + { + "fontFamily": "Arial", + "fontSize": 14, + "layout": "elk", + "theme": "forest", + } + `); + }); + + it('should combine initialize config and directives', () => { + const initConfig: MermaidConfig = { theme: 'dark', fontFamily: 'Arial', layout: 'dagre' }; + const directive1: MermaidConfig = { layout: 'elk', fontSize: 14 }; + const directive2: MermaidConfig = { theme: 'forest' }; + + configApi.saveConfigFromInitialize(initConfig); + configApi.addDirective(directive1); + configApi.addDirective(directive2); + + const userConfig = configApi.getUserDefinedConfig(); + expect(userConfig).toMatchInlineSnapshot(` + { + "fontFamily": "Arial", + "fontSize": 14, + "layout": "elk", + "theme": "forest", + } + `); + }); + + it('should handle nested config objects properly', () => { + const initConfig: MermaidConfig = { + flowchart: { nodeSpacing: 50, rankSpacing: 100 }, + theme: 'default', + }; + const directive: MermaidConfig = { + flowchart: { nodeSpacing: 75, curve: 'basis' }, + mindmap: { padding: 20 }, + }; + + configApi.saveConfigFromInitialize(initConfig); + configApi.addDirective(directive); + + const userConfig = configApi.getUserDefinedConfig(); + expect(userConfig).toMatchInlineSnapshot(` + { + "flowchart": { + "curve": "basis", + "nodeSpacing": 75, + "rankSpacing": 100, + }, + "mindmap": { + "padding": 20, + }, + "theme": "default", + } + `); + }); + + it('should handle complex nested overrides', () => { + const initConfig: MermaidConfig = { + flowchart: { + nodeSpacing: 50, + rankSpacing: 100, + curve: 'linear', + }, + theme: 'default', + }; + const directive1: MermaidConfig = { + flowchart: { + nodeSpacing: 75, + }, + fontSize: 12, + }; + const directive2: MermaidConfig = { + flowchart: { + curve: 'basis', + nodeSpacing: 100, + }, + mindmap: { + padding: 15, + }, + }; + + configApi.saveConfigFromInitialize(initConfig); + configApi.addDirective(directive1); + configApi.addDirective(directive2); + + const userConfig = configApi.getUserDefinedConfig(); + expect(userConfig).toMatchInlineSnapshot(` + { + "flowchart": { + "curve": "basis", + "nodeSpacing": 100, + "rankSpacing": 100, + }, + "fontSize": 12, + "mindmap": { + "padding": 15, + }, + "theme": "default", + } + `); + }); + + it('should return independent copies (not references)', () => { + const initConfig: MermaidConfig = { theme: 'dark', flowchart: { nodeSpacing: 50 } }; + configApi.saveConfigFromInitialize(initConfig); + + const userConfig1 = configApi.getUserDefinedConfig(); + const userConfig2 = configApi.getUserDefinedConfig(); + + userConfig1.theme = 'neutral'; + userConfig1.flowchart!.nodeSpacing = 999; + + expect(userConfig2).toMatchInlineSnapshot(` + { + "flowchart": { + "nodeSpacing": 50, + }, + "theme": "dark", + } + `); + }); + + it('should handle edge cases with undefined values', () => { + const initConfig: MermaidConfig = { theme: 'dark', layout: undefined }; + const directive: MermaidConfig = { fontSize: 14, fontFamily: undefined }; + + configApi.saveConfigFromInitialize(initConfig); + configApi.addDirective(directive); + + expect(configApi.getUserDefinedConfig()).toMatchInlineSnapshot(` + { + "fontSize": 14, + "layout": undefined, + "theme": "dark", + } + `); + }); + + it('should retain config from initialize after reset', () => { + const initConfig: MermaidConfig = { theme: 'dark' }; + const directive: MermaidConfig = { layout: 'elk' }; + + configApi.saveConfigFromInitialize(initConfig); + configApi.addDirective(directive); + + expect(configApi.getUserDefinedConfig()).toMatchInlineSnapshot(` + { + "layout": "elk", + "theme": "dark", + } + `); + + configApi.reset(); + }); +}); diff --git a/packages/mermaid/src/config.ts b/packages/mermaid/src/config.ts index 9468a3e46..4fcb3224d 100644 --- a/packages/mermaid/src/config.ts +++ b/packages/mermaid/src/config.ts @@ -248,3 +248,17 @@ const checkConfig = (config: MermaidConfig) => { issueWarning('LAZY_LOAD_DEPRECATED'); } }; + +export const getUserDefinedConfig = (): MermaidConfig => { + let userConfig: MermaidConfig = {}; + + if (configFromInitialize) { + userConfig = assignWithDepth(userConfig, configFromInitialize); + } + + for (const d of directives) { + userConfig = assignWithDepth(userConfig, d); + } + + return userConfig; +}; diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 70391f2e5..79fadd195 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -1075,6 +1075,10 @@ export interface ArchitectureDiagramConfig extends BaseDiagramConfig { export interface MindmapDiagramConfig extends BaseDiagramConfig { padding?: number; maxNodeWidth?: number; + /** + * Layout algorithm to use for positioning mindmap nodes + */ + layoutAlgorithm?: string; } /** * The object containing configurations specific for kanban diagrams diff --git a/packages/mermaid/src/diagram-api/comments.spec.ts b/packages/mermaid/src/diagram-api/comments.spec.ts index 57a7d4a34..febca83e9 100644 --- a/packages/mermaid/src/diagram-api/comments.spec.ts +++ b/packages/mermaid/src/diagram-api/comments.spec.ts @@ -1,5 +1,3 @@ -// tests to check that comments are removed - import { cleanupComments } from './comments.js'; import { describe, it, expect } from 'vitest'; @@ -10,12 +8,12 @@ describe('comments', () => { %% This is a comment %% This is another comment graph TD - A-->B + A-->B %% This is a comment `; expect(cleanupComments(text)).toMatchInlineSnapshot(` "graph TD - A-->B + A-->B " `); }); @@ -29,9 +27,9 @@ graph TD %%{ init: {'theme': 'space before init'}}%% %%{init: {'theme': 'space after ending'}}%% graph TD - A-->B + A-->B - B-->C + B-->C %% This is a comment `; expect(cleanupComments(text)).toMatchInlineSnapshot(` @@ -39,9 +37,9 @@ graph TD %%{ init: {'theme': 'space before init'}}%% %%{init: {'theme': 'space after ending'}}%% graph TD - A-->B + A-->B - B-->C + B-->C " `); }); @@ -50,14 +48,14 @@ graph TD const text = ` %% This is a comment graph TD - A-->B - %% This is a comment - C-->D + A-->B + %% This is a comment + C-->D `; expect(cleanupComments(text)).toMatchInlineSnapshot(` "graph TD - A-->B - C-->D + A-->B + C-->D " `); }); @@ -70,11 +68,11 @@ graph TD %% This is a comment graph TD - A-->B + A-->B `; expect(cleanupComments(text)).toMatchInlineSnapshot(` "graph TD - A-->B + A-->B " `); }); @@ -82,12 +80,12 @@ graph TD it('should remove comments at end of text with no newline', () => { const text = ` graph TD - A-->B + A-->B %% This is a comment`; expect(cleanupComments(text)).toMatchInlineSnapshot(` "graph TD - A-->B + A-->B " `); }); diff --git a/packages/mermaid/src/diagram-api/types.ts b/packages/mermaid/src/diagram-api/types.ts index 56364e9c6..ed90eec57 100644 --- a/packages/mermaid/src/diagram-api/types.ts +++ b/packages/mermaid/src/diagram-api/types.ts @@ -3,6 +3,7 @@ import type * as d3 from 'd3'; import type { SetOptional, SetRequired } from 'type-fest'; import type { Diagram } from '../Diagram.js'; import type { BaseDiagramConfig, MermaidConfig } from '../config.type.js'; +import type { DiagramOrientation } from '../diagrams/git/gitGraphTypes.js'; export interface DiagramMetadata { title?: string; @@ -35,7 +36,8 @@ export interface DiagramDB { getAccTitle?: () => string; setAccDescription?: (description: string) => void; getAccDescription?: () => string; - + getDirection?: () => string | undefined; + setDirection?: (dir: DiagramOrientation) => void; setDisplayMode?: (title: string) => void; bindFunctions?: (element: Element) => void; } diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index b29567236..608b11816 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -1,6 +1,5 @@ -import type { Position } from 'cytoscape'; +import type { LayoutOptions, Position } from 'cytoscape'; import cytoscape from 'cytoscape'; -import type { FcoseLayoutOptions } from 'cytoscape-fcose'; import fcose from 'cytoscape-fcose'; import { select } from 'd3'; import type { DrawDefinition, SVG } from '../../diagram-api/types.js'; @@ -41,7 +40,7 @@ registerIconPacks([ icons: architectureIcons, }, ]); -cytoscape.use(fcose); +cytoscape.use(fcose as any); function addServices(services: ArchitectureService[], cy: cytoscape.Core, db: ArchitectureDB) { services.forEach((service) => { @@ -429,7 +428,7 @@ function layoutArchitecture( }, alignmentConstraint, relativePlacementConstraint, - } as FcoseLayoutOptions); + } as LayoutOptions); // Once the diagram has been generated and the service's position cords are set, adjust the XY edges to have a 90deg bend layout.one('layoutstop', () => { diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.spec.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.spec.ts new file mode 100644 index 000000000..21d56b5af --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.spec.ts @@ -0,0 +1,48 @@ +import { describe } from 'vitest'; +import { draw } from './architectureRenderer.js'; +import { Diagram } from '../../Diagram.js'; +import { addDetector } from '../../diagram-api/detectType.js'; +import architectureDetector from './architectureDetector.js'; +import { ensureNodeFromSelector, jsdomIt } from '../../tests/util.js'; + +const { id, detector, loader } = architectureDetector; + +addDetector(id, detector, loader); // Add architecture schemas to Mermaid + +describe('architecture diagram SVGs', () => { + jsdomIt('should add ids', async () => { + const svgNode = await drawDiagram(` + architecture-beta + group api(cloud)[API] + + service db(database)[Database] in api + service disk1(disk)[Storage] in api + service disk2(disk)[Storage] in api + service server(server)[Server] in api + + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db + `); + + const nodesForGroup = svgNode.querySelectorAll(`#group-api`); + expect(nodesForGroup.length).toBe(1); + + const serviceIds = [...svgNode.querySelectorAll(`[id^=service-]`)].map(({ id }) => id).sort(); + expect(serviceIds).toStrictEqual([ + 'service-db', + 'service-disk1', + 'service-disk2', + 'service-server', + ]); + + const edgeIds = [...svgNode.querySelectorAll(`.edge[id^=L_]`)].map(({ id }) => id).sort(); + expect(edgeIds).toStrictEqual(['L_db_server_0', 'L_disk1_server_0', 'L_disk2_db_0']); + }); +}); + +async function drawDiagram(diagramText: string): Promise { + const diagram = await Diagram.fromText(diagramText, {}); + await draw('NOT_USED', 'svg', '1.0.0', diagram); + return ensureNodeFromSelector('#svg'); +} diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts index 6e470caa2..b35e453e7 100644 --- a/packages/mermaid/src/diagrams/architecture/svgDraw.ts +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -20,6 +20,7 @@ import { type ArchitectureJunction, type ArchitectureService, } from './architectureTypes.js'; +import { getEdgeId } from '../../utils.js'; export const drawEdges = async function ( edgesEl: D3Element, @@ -91,7 +92,8 @@ export const drawEdges = async function ( g.insert('path') .attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `) - .attr('class', 'edge'); + .attr('class', 'edge') + .attr('id', getEdgeId(source, target, { prefix: 'L' })); if (sourceArrow) { const xShift = isArchitectureDirectionX(sourceDir) @@ -206,8 +208,9 @@ export const drawGroups = async function ( if (data.type === 'group') { const { h, w, x1, y1 } = node.boundingBox(); - groupsEl - .append('rect') + const groupsNode = groupsEl.append('rect'); + groupsNode + .attr('id', `group-${data.id}`) .attr('x', x1 + halfIconSize) .attr('y', y1 + halfIconSize) .attr('width', w) @@ -262,6 +265,7 @@ export const drawGroups = async function ( ')' ); } + db.setElementForId(data.id, groupsNode); } }) ); @@ -342,9 +346,9 @@ export const drawServices = async function ( ); } - serviceElem.attr('class', 'architecture-service'); + serviceElem.attr('id', `service-${service.id}`).attr('class', 'architecture-service'); - const { width, height } = serviceElem._groups[0][0].getBBox(); + const { width, height } = serviceElem.node().getBBox(); service.width = width; service.height = height; db.setElementForId(service.id, serviceElem); diff --git a/packages/mermaid/src/diagrams/class/classDiagram.spec.ts b/packages/mermaid/src/diagrams/class/classDiagram.spec.ts index 7c88f2e41..aa5e514e0 100644 --- a/packages/mermaid/src/diagrams/class/classDiagram.spec.ts +++ b/packages/mermaid/src/diagrams/class/classDiagram.spec.ts @@ -1070,6 +1070,14 @@ describe('given a class diagram with members and methods ', function () { parser.parse(str); }); + it('should handle an empty class body with {}', function () { + const str = 'classDiagram\nclass EmptyClass {}'; + parser.parse(str); + const actual = parser.yy.getClass('EmptyClass'); + expect(actual.label).toBe('EmptyClass'); + expect(actual.members.length).toBe(0); + expect(actual.methods.length).toBe(0); + }); }); }); diff --git a/packages/mermaid/src/diagrams/class/parser/classDiagram.jison b/packages/mermaid/src/diagrams/class/parser/classDiagram.jison index 0f971c8b9..9a1f991a7 100644 --- a/packages/mermaid/src/diagrams/class/parser/classDiagram.jison +++ b/packages/mermaid/src/diagrams/class/parser/classDiagram.jison @@ -293,6 +293,7 @@ classStatement : classIdentifier | classIdentifier STYLE_SEPARATOR alphaNumToken {yy.setCssClass($1, $3);} | classIdentifier STRUCT_START members STRUCT_STOP {yy.addMembers($1,$3);} + | classIdentifier STRUCT_START STRUCT_STOP {} | classIdentifier STYLE_SEPARATOR alphaNumToken STRUCT_START members STRUCT_STOP {yy.setCssClass($1, $3);yy.addMembers($1,$5);} ; @@ -301,8 +302,15 @@ classIdentifier | CLASS className classLabel {$$=$2; yy.addClass($2);yy.setClassLabel($2, $3);} ; + +emptyBody + : + | SPACE emptyBody + | NEWLINE emptyBody + ; + annotationStatement - :ANNOTATION_START alphaNumToken ANNOTATION_END className { yy.addAnnotation($4,$2); } + : ANNOTATION_START alphaNumToken ANNOTATION_END className { yy.addAnnotation($4,$2); } ; members diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapDb.getData.test.ts b/packages/mermaid/src/diagrams/mindmap/mindmapDb.getData.test.ts new file mode 100644 index 000000000..7c10c0104 --- /dev/null +++ b/packages/mermaid/src/diagrams/mindmap/mindmapDb.getData.test.ts @@ -0,0 +1,297 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { MindmapDB } from './mindmapDb.js'; +import type { MindmapLayoutNode, MindmapLayoutEdge } from './mindmapDb.js'; +import type { Edge } from '../../rendering-util/types.js'; + +// Mock the getConfig function +vi.mock('../../diagram-api/diagramAPI.js', () => ({ + getConfig: vi.fn(() => ({ + mindmap: { + layoutAlgorithm: 'cose-bilkent', + padding: 10, + maxNodeWidth: 200, + useMaxWidth: true, + }, + })), +})); + +describe('MindmapDb getData function', () => { + let db: MindmapDB; + + beforeEach(() => { + db = new MindmapDB(); + // Clear the database before each test + db.clear(); + }); + + describe('getData', () => { + it('should return empty data when no mindmap is set', () => { + const result = db.getData(); + + expect(result.nodes).toEqual([]); + expect(result.edges).toEqual([]); + expect(result.config).toBeDefined(); + expect(result.rootNode).toBeUndefined(); + }); + + it('should return structured data for simple mindmap', () => { + // Create a simple mindmap structure + db.addNode(0, 'root', 'Root Node', 0); + db.addNode(1, 'child1', 'Child 1', 0); + db.addNode(1, 'child2', 'Child 2', 0); + + const result = db.getData(); + + expect(result.nodes).toHaveLength(3); + expect(result.edges).toHaveLength(2); + expect(result.config).toBeDefined(); + expect(result.rootNode).toBeDefined(); + + // Check root node + const rootNode = (result.nodes as MindmapLayoutNode[]).find((n) => n.id === '0'); + expect(rootNode).toBeDefined(); + expect(rootNode?.label).toBe('Root Node'); + expect(rootNode?.level).toBe(0); + + // Check child nodes + const child1 = (result.nodes as MindmapLayoutNode[]).find((n) => n.id === '1'); + expect(child1).toBeDefined(); + expect(child1?.label).toBe('Child 1'); + expect(child1?.level).toBe(1); + + // Check edges + expect(result.edges).toContainEqual( + expect.objectContaining({ + start: '0', + end: '1', + depth: 0, + }) + ); + }); + + it('should return structured data for hierarchical mindmap', () => { + // Create a hierarchical mindmap structure + db.addNode(0, 'root', 'Root Node', 0); + db.addNode(1, 'child1', 'Child 1', 0); + db.addNode(2, 'grandchild1', 'Grandchild 1', 0); + db.addNode(2, 'grandchild2', 'Grandchild 2', 0); + db.addNode(1, 'child2', 'Child 2', 0); + + const result = db.getData(); + + expect(result.nodes).toHaveLength(5); + expect(result.edges).toHaveLength(4); + + // Check that all levels are represented + const levels = result.nodes.map((n) => (n as MindmapLayoutNode).level); + expect(levels).toContain(0); // root + expect(levels).toContain(1); // children + expect(levels).toContain(2); // grandchildren + + // Check edge relationships + const edgeRelations = result.edges.map( + (e) => `${(e as MindmapLayoutEdge).start}->${(e as MindmapLayoutEdge).end}` + ); + expect(edgeRelations).toContain('0->1'); // root to child1 + expect(edgeRelations).toContain('1->2'); // child1 to grandchild1 + expect(edgeRelations).toContain('1->3'); // child1 to grandchild2 + expect(edgeRelations).toContain('0->4'); // root to child2 + }); + + it('should preserve node properties in processed data', () => { + // Add a node with specific properties + db.addNode(0, 'root', 'Root Node', 2); // type 2 = rectangle + + // Set additional properties + const mindmap = db.getMindmap(); + if (mindmap) { + mindmap.width = 150; + mindmap.height = 75; + mindmap.padding = 15; + mindmap.section = 1; + mindmap.class = 'custom-class'; + mindmap.icon = 'star'; + } + + const result = db.getData(); + + expect(result.nodes).toHaveLength(1); + const node = result.nodes[0] as MindmapLayoutNode; + + expect(node.type).toBe(2); + expect(node.width).toBe(150); + expect(node.height).toBe(75); + expect(node.padding).toBe(15); + expect(node.section).toBeUndefined(); // Root node has undefined section + expect(node.cssClasses).toBe('mindmap-node section-root section--1 custom-class'); + expect(node.icon).toBe('star'); + }); + + it('should generate unique edge IDs', () => { + db.addNode(0, 'root', 'Root Node', 0); + db.addNode(1, 'child1', 'Child 1', 0); + db.addNode(1, 'child2', 'Child 2', 0); + db.addNode(1, 'child3', 'Child 3', 0); + + const result = db.getData(); + + const edgeIds = result.edges.map((e: Edge) => e.id); + const uniqueIds = new Set(edgeIds); + + expect(edgeIds).toHaveLength(3); + expect(uniqueIds.size).toBe(3); // All IDs should be unique + }); + + it('should handle nodes with missing optional properties', () => { + db.addNode(0, 'root', 'Root Node', 0); + + const result = db.getData(); + const node = result.nodes[0] as MindmapLayoutNode; + + // Should handle undefined/missing properties gracefully + expect(node.section).toBeUndefined(); // Root node has undefined section + expect(node.cssClasses).toBe('mindmap-node section-root section--1'); // Root node gets special classes + expect(node.icon).toBeUndefined(); + expect(node.x).toBeUndefined(); + expect(node.y).toBeUndefined(); + }); + + it('should assign correct section classes based on sibling position', () => { + // Create the example mindmap structure: + // A + // a0 + // aa0 + // a1 + // aaa + // a2 + db.addNode(0, 'A', 'A', 0); // Root + db.addNode(1, 'a0', 'a0', 0); // First child of root + db.addNode(2, 'aa0', 'aa0', 0); // Child of a0 + db.addNode(1, 'a1', 'a1', 0); // Second child of root + db.addNode(2, 'aaa', 'aaa', 0); // Child of a1 + db.addNode(1, 'a2', 'a2', 0); // Third child of root + + const result = db.getData(); + + // Find nodes by their labels + const nodeA = result.nodes.find((n) => n.label === 'A') as MindmapLayoutNode; + const nodeA0 = result.nodes.find((n) => n.label === 'a0') as MindmapLayoutNode; + const nodeAa0 = result.nodes.find((n) => n.label === 'aa0') as MindmapLayoutNode; + const nodeA1 = result.nodes.find((n) => n.label === 'a1') as MindmapLayoutNode; + const nodeAaa = result.nodes.find((n) => n.label === 'aaa') as MindmapLayoutNode; + const nodeA2 = result.nodes.find((n) => n.label === 'a2') as MindmapLayoutNode; + + // Check section assignments + expect(nodeA.section).toBeUndefined(); // Root has undefined section + expect(nodeA0.section).toBe(0); // First child of root + expect(nodeAa0.section).toBe(0); // Inherits from parent a0 + expect(nodeA1.section).toBe(1); // Second child of root + expect(nodeAaa.section).toBe(1); // Inherits from parent a1 + expect(nodeA2.section).toBe(2); // Third child of root + + // Check CSS classes + expect(nodeA.cssClasses).toBe('mindmap-node section-root section--1'); + expect(nodeA0.cssClasses).toBe('mindmap-node section-0'); + expect(nodeAa0.cssClasses).toBe('mindmap-node section-0'); + expect(nodeA1.cssClasses).toBe('mindmap-node section-1'); + expect(nodeAaa.cssClasses).toBe('mindmap-node section-1'); + expect(nodeA2.cssClasses).toBe('mindmap-node section-2'); + }); + + it('should preserve custom classes while adding section classes', () => { + db.addNode(0, 'root', 'Root Node', 0); + db.addNode(1, 'child', 'Child Node', 0); + + // Add custom classes to nodes + const mindmap = db.getMindmap(); + if (mindmap) { + mindmap.class = 'custom-root-class'; + if (mindmap.children?.[0]) { + mindmap.children[0].class = 'custom-child-class'; + } + } + + const result = db.getData(); + const rootNode = result.nodes.find((n) => n.label === 'Root Node') as MindmapLayoutNode; + const childNode = result.nodes.find((n) => n.label === 'Child Node') as MindmapLayoutNode; + + // Should include both section classes and custom classes + expect(rootNode.cssClasses).toBe('mindmap-node section-root section--1 custom-root-class'); + expect(childNode.cssClasses).toBe('mindmap-node section-0 custom-child-class'); + }); + + it('should not create any fake root nodes', () => { + // Create a simple mindmap + db.addNode(0, 'A', 'A', 0); + db.addNode(1, 'a0', 'a0', 0); + db.addNode(1, 'a1', 'a1', 0); + + const result = db.getData(); + + // Check that we only have the expected nodes + expect(result.nodes).toHaveLength(3); + expect(result.nodes.map((n) => n.label)).toEqual(['A', 'a0', 'a1']); + + // Check that there's no node with label "mindmap" or any other fake root + const mindmapNode = result.nodes.find((n) => n.label === 'mindmap'); + expect(mindmapNode).toBeUndefined(); + + // Verify the root node has the correct classes + const rootNode = result.nodes.find((n) => n.label === 'A') as MindmapLayoutNode; + expect(rootNode.cssClasses).toBe('mindmap-node section-root section--1'); + expect(rootNode.level).toBe(0); + }); + + it('should assign correct section classes to edges', () => { + // Create the example mindmap structure: + // A + // a0 + // aa0 + // a1 + // aaa + // a2 + db.addNode(0, 'A', 'A', 0); // Root + db.addNode(1, 'a0', 'a0', 0); // First child of root + db.addNode(2, 'aa0', 'aa0', 0); // Child of a0 + db.addNode(1, 'a1', 'a1', 0); // Second child of root + db.addNode(2, 'aaa', 'aaa', 0); // Child of a1 + db.addNode(1, 'a2', 'a2', 0); // Third child of root + + const result = db.getData(); + + // Should have 5 edges: A->a0, a0->aa0, A->a1, a1->aaa, A->a2 + expect(result.edges).toHaveLength(5); + + // Find edges by their start and end nodes + const edgeA_a0 = result.edges.find( + (e) => e.start === '0' && e.end === '1' + ) as MindmapLayoutEdge; + const edgeA0_aa0 = result.edges.find( + (e) => e.start === '1' && e.end === '2' + ) as MindmapLayoutEdge; + const edgeA_a1 = result.edges.find( + (e) => e.start === '0' && e.end === '3' + ) as MindmapLayoutEdge; + const edgeA1_aaa = result.edges.find( + (e) => e.start === '3' && e.end === '4' + ) as MindmapLayoutEdge; + const edgeA_a2 = result.edges.find( + (e) => e.start === '0' && e.end === '5' + ) as MindmapLayoutEdge; + + // Check edge classes + expect(edgeA_a0.classes).toBe('edge section-edge-0 edge-depth-1'); // A->a0: section-0, depth-1 + expect(edgeA0_aa0.classes).toBe('edge section-edge-0 edge-depth-2'); // a0->aa0: section-0, depth-2 + expect(edgeA_a1.classes).toBe('edge section-edge-1 edge-depth-1'); // A->a1: section-1, depth-1 + expect(edgeA1_aaa.classes).toBe('edge section-edge-1 edge-depth-2'); // a1->aaa: section-1, depth-2 + expect(edgeA_a2.classes).toBe('edge section-edge-2 edge-depth-1'); // A->a2: section-2, depth-1 + + // Check section assignments match the child nodes + expect(edgeA_a0.section).toBe(0); + expect(edgeA0_aa0.section).toBe(0); + expect(edgeA_a1.section).toBe(1); + expect(edgeA1_aaa.section).toBe(1); + expect(edgeA_a2.section).toBe(2); + }); + }); +}); diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts b/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts index 703ba8434..a86c3fdc5 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmapDb.ts @@ -1,9 +1,26 @@ import { getConfig } from '../../diagram-api/diagramAPI.js'; +import { v4 } from 'uuid'; import type { D3Element } from '../../types.js'; import { sanitizeText } from '../../diagrams/common/common.js'; import { log } from '../../logger.js'; import type { MindmapNode } from './mindmapTypes.js'; import defaultConfig from '../../defaultConfig.js'; +import type { LayoutData, Node, Edge } from '../../rendering-util/types.js'; +import { getUserDefinedConfig } from '../../config.js'; + +// Extend Node type for mindmap-specific properties +export type MindmapLayoutNode = Node & { + level: number; + nodeId: string; + type: number; + section?: number; +}; + +// Extend Edge type for mindmap-specific properties +export type MindmapLayoutEdge = Edge & { + depth: number; + section?: number; +}; const nodeType = { DEFAULT: 0, @@ -20,6 +37,7 @@ export class MindmapDB { private nodes: MindmapNode[] = []; private count = 0; private elements: Record = {}; + private baseLevel?: number; public readonly nodeType: typeof nodeType; constructor() { @@ -27,7 +45,6 @@ export class MindmapDB { this.nodeType = nodeType; this.clear(); this.getType = this.getType.bind(this); - this.getMindmap = this.getMindmap.bind(this); this.getElementById = this.getElementById.bind(this); this.getParent = this.getParent.bind(this); this.getMindmap = this.getMindmap.bind(this); @@ -38,6 +55,7 @@ export class MindmapDB { this.nodes = []; this.count = 0; this.elements = {}; + this.baseLevel = undefined; } public getParent(level: number): MindmapNode | null { @@ -56,6 +74,17 @@ export class MindmapDB { public addNode(level: number, id: string, descr: string, type: number): void { log.info('addNode', level, id, descr, type); + let isRoot = false; + + if (this.nodes.length === 0) { + this.baseLevel = level; + level = 0; + isRoot = true; + } else if (this.baseLevel !== undefined) { + level = level - this.baseLevel; + isRoot = false; + } + const conf = getConfig(); let padding = conf.mindmap?.padding ?? defaultConfig.mindmap.padding; @@ -76,6 +105,7 @@ export class MindmapDB { children: [], width: conf.mindmap?.maxNodeWidth ?? defaultConfig.mindmap.maxNodeWidth, padding, + isRoot, }; const parent = this.getParent(level); @@ -83,7 +113,7 @@ export class MindmapDB { parent.children.push(node); this.nodes.push(node); } else { - if (this.nodes.length === 0) { + if (isRoot) { this.nodes.push(node); } else { throw new Error( @@ -156,6 +186,222 @@ export class MindmapDB { } } + /** + * Assign section numbers to nodes based on their position relative to root + * @param node - The mindmap node to process + * @param sectionNumber - The section number to assign (undefined for root) + */ + public assignSections(node: MindmapNode, sectionNumber?: number): void { + // For root node, section should be undefined (not -1) + if (node.level === 0) { + node.section = undefined; + } else { + // For non-root nodes, assign the section number + node.section = sectionNumber; + } + // For root node's children, assign section numbers based on their index + // For other nodes, inherit parent's section number + if (node.children) { + for (const [index, child] of node.children.entries()) { + const childSectionNumber = node.level === 0 ? index : sectionNumber; + this.assignSections(child, childSectionNumber); + } + } + } + + /** + * Convert mindmap tree structure to flat array of nodes + * @param node - The mindmap node to process + * @param processedNodes - Array to collect processed nodes + */ + public flattenNodes(node: MindmapNode, processedNodes: MindmapLayoutNode[]): void { + // Build CSS classes for the node + const cssClasses = ['mindmap-node']; + + if (node.isRoot === true) { + // Root node gets special classes + cssClasses.push('section-root', 'section--1'); + } else if (node.section !== undefined) { + // Child nodes get section class based on their section number + cssClasses.push(`section-${node.section}`); + } + + // Add any custom classes from the node + if (node.class) { + cssClasses.push(node.class); + } + + const classes = cssClasses.join(' '); + + // Map mindmap node type to valid shape name + const getShapeFromType = (type: number) => { + switch (type) { + case nodeType.CIRCLE: + return 'mindmapCircle'; + case nodeType.RECT: + return 'rect'; + case nodeType.ROUNDED_RECT: + return 'rounded'; + case nodeType.CLOUD: + return 'cloud'; + case nodeType.BANG: + return 'bang'; + case nodeType.HEXAGON: + return 'hexagon'; + case nodeType.DEFAULT: + return 'defaultMindmapNode'; + case nodeType.NO_BORDER: + default: + return 'rect'; + } + }; + + const processedNode: MindmapLayoutNode = { + id: node.id.toString(), + domId: 'node_' + node.id.toString(), + label: node.descr, + isGroup: false, + shape: getShapeFromType(node.type), + width: node.width, + height: node.height ?? 0, + padding: node.padding, + cssClasses: classes, + cssStyles: [], + look: 'default', + icon: node.icon, + x: node.x, + y: node.y, + // Mindmap-specific properties + level: node.level, + nodeId: node.nodeId, + type: node.type, + section: node.section, + }; + + processedNodes.push(processedNode); + + // Recursively process children + if (node.children) { + for (const child of node.children) { + this.flattenNodes(child, processedNodes); + } + } + } + + /** + * Generate edges from parent-child relationships in mindmap tree + * @param node - The mindmap node to process + * @param edges - Array to collect edges + */ + public generateEdges(node: MindmapNode, edges: MindmapLayoutEdge[]): void { + if (!node.children) { + return; + } + for (const child of node.children) { + // Build CSS classes for the edge + let edgeClasses = 'edge'; + + // Add section-specific classes based on the child's section + if (child.section !== undefined) { + edgeClasses += ` section-edge-${child.section}`; + } + + // Add depth class based on the parent's level + 1 (depth of the edge) + const edgeDepth = node.level + 1; + edgeClasses += ` edge-depth-${edgeDepth}`; + + const edge: MindmapLayoutEdge = { + id: `edge_${node.id}_${child.id}`, + start: node.id.toString(), + end: child.id.toString(), + type: 'normal', + curve: 'basis', + thickness: 'normal', + look: 'default', + classes: edgeClasses, + // Store mindmap-specific data + depth: node.level, + section: child.section, + }; + + edges.push(edge); + + // Recursively process child edges + this.generateEdges(child, edges); + } + } + + /** + * Get structured data for layout algorithms + * Following the pattern established by ER diagrams + * @returns Structured data containing nodes, edges, and config + */ + public getData(): LayoutData { + const mindmapRoot = this.getMindmap(); + const config = getConfig(); + + const userDefinedConfig = getUserDefinedConfig(); + const hasUserDefinedLayout = userDefinedConfig.layout !== undefined; + + const finalConfig = config; + if (!hasUserDefinedLayout) { + finalConfig.layout = 'cose-bilkent'; + } + + if (!mindmapRoot) { + return { + nodes: [], + edges: [], + config: finalConfig, + }; + } + log.debug('getData: mindmapRoot', mindmapRoot, config); + + // Assign section numbers to all nodes based on their position relative to root + this.assignSections(mindmapRoot); + + // Convert tree structure to flat arrays + const processedNodes: MindmapLayoutNode[] = []; + const processedEdges: MindmapLayoutEdge[] = []; + + this.flattenNodes(mindmapRoot, processedNodes); + this.generateEdges(mindmapRoot, processedEdges); + + log.debug( + `getData: processed ${processedNodes.length} nodes and ${processedEdges.length} edges` + ); + + // Create shapes map for ELK compatibility + const shapes = new Map(); + for (const node of processedNodes) { + shapes.set(node.id, { + shape: node.shape, + width: node.width, + height: node.height, + padding: node.padding, + }); + } + + return { + nodes: processedNodes, + edges: processedEdges, + config: finalConfig, + // Store the root node for mindmap-specific layout algorithms + rootNode: mindmapRoot, + // Properties required by dagre layout algorithm + markers: ['point'], // Mindmaps don't use markers + direction: 'TB', // Top-to-bottom direction for mindmaps + nodeSpacing: 50, // Default spacing between nodes + rankSpacing: 50, // Default spacing between ranks + // Add shapes for ELK compatibility + shapes: Object.fromEntries(shapes), + // Additional properties that layout algorithms might expect + type: 'mindmap', + diagramId: 'mindmap-' + v4(), + }; + } + + // Expose logger to grammar public getLogger() { return log; } diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts b/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts index ef9be0565..a962dc924 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmapRenderer.ts @@ -1,200 +1,83 @@ -import cytoscape from 'cytoscape'; -// @ts-expect-error No types available -import coseBilkent from 'cytoscape-cose-bilkent'; -import { select } from 'd3'; -import type { MermaidConfig } from '../../config.type.js'; -import { getConfig } from '../../diagram-api/diagramAPI.js'; import type { DrawDefinition } from '../../diagram-api/types.js'; import { log } from '../../logger.js'; -import type { D3Element } from '../../types.js'; -import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; -import { setupGraphViewbox } from '../../setupGraphViewbox.js'; -import type { FilledMindMapNode, MindmapNode } from './mindmapTypes.js'; -import { drawNode, positionNode } from './svgDraw.js'; +import { getDiagramElement } from '../../rendering-util/insertElementsForSize.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 type { FilledMindMapNode } from './mindmapTypes.js'; import defaultConfig from '../../defaultConfig.js'; import type { MindmapDB } from './mindmapDb.js'; -// Inject the layout algorithm into cytoscape -cytoscape.use(coseBilkent); -async function drawNodes( - db: MindmapDB, - svg: D3Element, - mindmap: FilledMindMapNode, - section: number, - conf: MermaidConfig -) { - await drawNode(db, svg, mindmap, section, conf); - if (mindmap.children) { - await Promise.all( - mindmap.children.map((child, index) => - drawNodes(db, svg, child, section < 0 ? index : section, conf) - ) - ); - } -} - -declare module 'cytoscape' { - interface EdgeSingular { - _private: { - bodyBounds: unknown; - rscratch: { - startX: number; - startY: number; - midX: number; - midY: number; - endX: number; - endY: number; - }; - }; - } -} - -function drawEdges(edgesEl: D3Element, cy: cytoscape.Core) { - cy.edges().map((edge, id) => { - const data = edge.data(); - if (edge[0]._private.bodyBounds) { - const bounds = edge[0]._private.rscratch; - log.trace('Edge: ', id, data); - edgesEl - .insert('path') - .attr( - 'd', - `M ${bounds.startX},${bounds.startY} L ${bounds.midX},${bounds.midY} L${bounds.endX},${bounds.endY} ` - ) - .attr('class', 'edge section-edge-' + data.section + ' edge-depth-' + data.depth); +/** + * Update the layout data with actual node dimensions after drawing + */ +function _updateNodeDimensions(data4Layout: LayoutData, mindmapRoot: FilledMindMapNode) { + const updateNode = (node: FilledMindMapNode) => { + // Find the corresponding node in the layout data + const layoutNode = data4Layout.nodes.find((n) => n.id === node.id.toString()); + if (layoutNode) { + // Update with the actual dimensions calculated by drawNode + layoutNode.width = node.width; + layoutNode.height = node.height; + log.debug('Updated node dimensions:', node.id, 'width:', node.width, 'height:', node.height); } - }); -} -function addNodes(mindmap: MindmapNode, cy: cytoscape.Core, conf: MermaidConfig, level: number) { - cy.add({ - group: 'nodes', - data: { - id: mindmap.id.toString(), - labelText: mindmap.descr, - height: mindmap.height, - width: mindmap.width, - level: level, - nodeId: mindmap.id, - padding: mindmap.padding, - type: mindmap.type, - }, - position: { - x: mindmap.x!, - y: mindmap.y!, - }, - }); - if (mindmap.children) { - mindmap.children.forEach((child) => { - addNodes(child, cy, conf, level + 1); - cy.add({ - group: 'edges', - data: { - id: `${mindmap.id}_${child.id}`, - source: mindmap.id, - target: child.id, - depth: level, - section: child.section, - }, - }); - }); - } -} + // Recursively update children + node.children?.forEach(updateNode); + }; -function layoutMindmap(node: MindmapNode, conf: MermaidConfig): Promise { - return new Promise((resolve) => { - // Add temporary render element - const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none'); - const cy = cytoscape({ - container: document.getElementById('cy'), // container to render in - style: [ - { - selector: 'edge', - style: { - 'curve-style': 'bezier', - }, - }, - ], - }); - // Remove element after layout - renderEl.remove(); - addNodes(node, cy, conf, 0); - - // Make cytoscape care about the dimensions of the nodes - cy.nodes().forEach(function (n) { - n.layoutDimensions = () => { - const data = n.data(); - return { w: data.width, h: data.height }; - }; - }); - - cy.layout({ - name: 'cose-bilkent', - // @ts-ignore Types for cose-bilkent are not correct? - quality: 'proof', - styleEnabled: false, - animate: false, - }).run(); - cy.ready((e) => { - log.info('Ready', e); - resolve(cy); - }); - }); -} - -function positionNodes(db: MindmapDB, cy: cytoscape.Core) { - cy.nodes().map((node, id) => { - const data = node.data(); - data.x = node.position().x; - data.y = node.position().y; - positionNode(db, data); - const el = db.getElementById(data.nodeId); - log.info('id:', id, 'Position: (', node.position().x, ', ', node.position().y, ')', data); - el.attr( - 'transform', - `translate(${node.position().x - data.width / 2}, ${node.position().y - data.height / 2})` - ); - el.attr('attr', `apa-${id})`); - }); + updateNode(mindmapRoot); } export const draw: DrawDefinition = async (text, id, _version, diagObj) => { log.debug('Rendering mindmap diagram\n' + text); + // Draw the nodes first to get their dimensions, then update the layout data const db = diagObj.db as MindmapDB; + + // The getData method provided in all supported diagrams is used to extract the data from the parsed structure + // into the Layout data format + const data4Layout = db.getData(); + + // Create the root SVG - the element is the div containing the SVG element + const svg = getDiagramElement(id, data4Layout.config.securityLevel); + + data4Layout.type = diagObj.type; + data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(data4Layout.config.layout, { + fallback: 'cose-bilkent', + }); + + data4Layout.diagramId = id; + const mm = db.getMindmap(); if (!mm) { return; } - const conf = getConfig(); - conf.htmlLabels = false; + data4Layout.nodes.forEach((node) => { + if (node.shape === 'rounded') { + node.radius = 15; + node.taper = 15; + node.stroke = 'none'; + node.width = 0; + node.padding = 15; + } else if (node.shape === 'circle') { + node.padding = 10; + } else if (node.shape === 'rect') { + node.width = 0; + node.padding = 10; + } + }); - const svg = selectSvgElement(id); + // Use the unified rendering system + await render(data4Layout, svg); - // Draw the graph and start with drawing the nodes without proper position - // this gives us the size of the nodes and we can set the positions later - - const edgesElem = svg.append('g'); - edgesElem.attr('class', 'mindmap-edges'); - const nodesElem = svg.append('g'); - nodesElem.attr('class', 'mindmap-nodes'); - await drawNodes(db, nodesElem, mm as FilledMindMapNode, -1, conf); - - // Next step is to layout the mindmap, giving each node a position - - const cy = await layoutMindmap(mm, conf); - - // After this we can draw, first the edges and the then nodes with the correct position - drawEdges(edgesElem, cy); - positionNodes(db, cy); - - // Setup the view box and size of the svg element - setupGraphViewbox( - undefined, + // Setup the view box and size of the svg element using config from data4Layout + setupViewPortForSVG( svg, - conf.mindmap?.padding ?? defaultConfig.mindmap.padding, - conf.mindmap?.useMaxWidth ?? defaultConfig.mindmap.useMaxWidth + data4Layout.config.mindmap?.padding ?? defaultConfig.mindmap.padding, + 'mindmapDiagram', + data4Layout.config.mindmap?.useMaxWidth ?? defaultConfig.mindmap.useMaxWidth ); }; diff --git a/packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts b/packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts index be8effab1..aeed2358f 100644 --- a/packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts +++ b/packages/mermaid/src/diagrams/mindmap/mindmapTypes.ts @@ -15,6 +15,7 @@ export interface MindmapNode { icon?: string; x?: number; y?: number; + isRoot?: boolean; } export type FilledMindMapNode = RequiredDeep; diff --git a/packages/mermaid/src/diagrams/mindmap/styles.ts b/packages/mermaid/src/diagrams/mindmap/styles.ts index fffa6e4d9..8372bddf1 100644 --- a/packages/mermaid/src/diagrams/mindmap/styles.ts +++ b/packages/mermaid/src/diagrams/mindmap/styles.ts @@ -64,6 +64,12 @@ const getStyles: DiagramStylesProvider = (options) => .section-root text { fill: ${options.gitBranchLabel0}; } + .section-root span { + color: ${options.gitBranchLabel0}; + } + .section-2 span { + color: ${options.gitBranchLabel0}; + } .icon-container { height:100%; display: flex; diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js index 7d48db5fc..a25794eb1 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js +++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js @@ -1368,7 +1368,7 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com it('should handle box without description', async () => { const diagram = await Diagram.fromText(` sequenceDiagram - box Aqua + box aqua participant a as Alice participant b as Bob end @@ -1384,7 +1384,7 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com const boxes = diagram.db.getBoxes(); expect(boxes[0].name).toBeFalsy(); expect(boxes[0].actorKeys).toEqual(['a', 'b']); - expect(boxes[0].fill).toEqual('Aqua'); + expect(boxes[0].fill).toEqual('aqua'); }); it('should handle simple actor creation', async () => { diff --git a/packages/mermaid/src/docs/.vitepress/config.ts b/packages/mermaid/src/docs/.vitepress/config.ts index 1c41e7cba..066fde693 100644 --- a/packages/mermaid/src/docs/.vitepress/config.ts +++ b/packages/mermaid/src/docs/.vitepress/config.ts @@ -203,6 +203,7 @@ function sidebarConfig() { { text: 'Accessibility', link: '/config/accessibility' }, { text: 'Mermaid CLI', link: '/config/mermaidCLI' }, { text: 'FAQ', link: '/config/faq' }, + { text: 'Layouts', link: '/config/layouts' }, ], }, ]; diff --git a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts index 2357fe384..4b44f20b4 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts @@ -1,7 +1,13 @@ import mermaid, { type MermaidConfig } from 'mermaid'; import zenuml from '../../../../../mermaid-zenuml/dist/mermaid-zenuml.core.mjs'; +import tidyTreeLayout from '../../../../../mermaid-layout-tidy-tree/dist/mermaid-layout-tidy-tree.core.mjs'; +import layouts from '../../../../../mermaid-layout-elk/dist/mermaid-layout-elk.core.mjs'; -const init = mermaid.registerExternalDiagrams([zenuml]); +const init = Promise.all([ + mermaid.registerExternalDiagrams([zenuml]), + mermaid.registerLayoutLoaders(layouts), + mermaid.registerLayoutLoaders(tidyTreeLayout), +]); mermaid.registerIconPacks([ { name: 'logos', diff --git a/packages/mermaid/src/docs/config/faq.md b/packages/mermaid/src/docs/config/faq.md index 6d1261fc1..4acf0c3d3 100644 --- a/packages/mermaid/src/docs/config/faq.md +++ b/packages/mermaid/src/docs/config/faq.md @@ -1,6 +1,6 @@ # Frequently Asked Questions -1. [How to add title to flowchart?](https://github.com/mermaid-js/mermaid/issues/556#issuecomment-363182217) +1. [How to add title to flowchart?](https://github.com/mermaid-js/mermaid/issues/1433#issuecomment-1991554712) 1. [How to specify custom CSS file?](https://github.com/mermaidjs/mermaid.cli/pull/24#issuecomment-373402785) 1. [How to fix tooltip misplacement issue?](https://github.com/mermaid-js/mermaid/issues/542#issuecomment-3343564621) 1. [How to specify gantt diagram xAxis format?](https://github.com/mermaid-js/mermaid/issues/269#issuecomment-373229136) diff --git a/packages/mermaid/src/docs/config/layouts.md b/packages/mermaid/src/docs/config/layouts.md new file mode 100644 index 000000000..56f5072f6 --- /dev/null +++ b/packages/mermaid/src/docs/config/layouts.md @@ -0,0 +1,24 @@ +# Layouts + +This page lists the available layout algorithms supported in Mermaid diagrams. + +## Supported Layouts + +- **elk**: [ELK (Eclipse Layout Kernel)](https://www.eclipse.org/elk/) +- **tidy-tree**: Tidy tree layout for hierarchical diagrams [Tidy Tree Configuration](/config/tidy-tree) +- **cose-bilkent**: Cose Bilkent layout for force-directed graphs +- **dagre**: Dagre layout for layered graphs + +## How to Use + +You can specify the layout in your diagram's YAML config or initialization options. For example: + +```mermaid +--- +config: + layout: elk +--- +graph TD; + A-->B; + B-->C; +``` diff --git a/packages/mermaid/src/docs/config/tidy-tree.md b/packages/mermaid/src/docs/config/tidy-tree.md new file mode 100644 index 000000000..f98d36379 --- /dev/null +++ b/packages/mermaid/src/docs/config/tidy-tree.md @@ -0,0 +1,49 @@ +# Tidy-tree Layout + +The **tidy-tree** layout arranges nodes in a hierarchical, tree-like structure. It is especially useful for diagrams where parent-child relationships are important, such as mindmaps. + +## Features + +- Organizes nodes in a tidy, non-overlapping tree +- Ideal for mindmaps and hierarchical data +- Automatically adjusts spacing for readability + +## Example Usage + +```mermaid-example +--- +config: + layout: tidy-tree +--- +mindmap +root((mindmap is a long thing)) + A + B + C + D +``` + +```mermaid-example +--- +config: + layout: tidy-tree +--- +mindmap +root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectiveness
and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping +``` + +## Note + +- Currently, tidy-tree is primarily supported for mindmap diagrams. diff --git a/packages/mermaid/src/docs/package.json b/packages/mermaid/src/docs/package.json index 6ec43eb93..d0535c5e0 100644 --- a/packages/mermaid/src/docs/package.json +++ b/packages/mermaid/src/docs/package.json @@ -31,9 +31,9 @@ "fast-glob": "^3.3.3", "https-localhost": "^4.7.1", "pathe": "^2.0.3", - "unocss": "^66.0.0", + "unocss": "^66.4.2", "unplugin-vue-components": "^28.4.0", - "vite": "^6.1.1", + "vite": "^7.0.0", "vite-plugin-pwa": "^1.0.0", "vitepress": "1.6.3", "workbox-window": "^7.3.0" diff --git a/packages/mermaid/src/docs/syntax/flowchart.md b/packages/mermaid/src/docs/syntax/flowchart.md index a19dcff21..341143c47 100644 --- a/packages/mermaid/src/docs/syntax/flowchart.md +++ b/packages/mermaid/src/docs/syntax/flowchart.md @@ -590,11 +590,17 @@ flowchart TD - `b` - **w**: The width of the image. If not defined, this will default to the natural width of the image. - **h**: The height of the image. If not defined, this will default to the natural height of the image. -- **constraint**: Determines if the image should constrain the node size. This setting also ensures the image maintains its original aspect ratio, adjusting the height (`h`) accordingly to the width (`w`). If not defined, this will default to `off` Possible values are: +- **constraint**: Determines if the image should constrain the node size. This setting also ensures the image maintains its original aspect ratio, adjusting the width (`w`) accordingly to the height (`h`). If not defined, this will default to `off` Possible values are: - `on` - `off` -These new shapes provide additional flexibility and visual appeal to your flowcharts, making them more informative and engaging. +If you want to resize an image, but keep the same aspect ratio, set `h`, and set `constraint: on` to constrain the aspect ratio. E.g. + +```mermaid +flowchart TD + %% My image with a constrained aspect ratio + A@{ img: "https://mermaid.js.org/favicon.svg", label: "My example image label", pos: "t", h: 60, constraint: "on" } +``` ## Links between nodes diff --git a/packages/mermaid/src/docs/syntax/mindmap.md b/packages/mermaid/src/docs/syntax/mindmap.md index 3dfbed2f6..41d736798 100644 --- a/packages/mermaid/src/docs/syntax/mindmap.md +++ b/packages/mermaid/src/docs/syntax/mindmap.md @@ -209,3 +209,22 @@ You can also refer the [implementation in the live editor](https://github.com/me cspell:locale en,en-gb cspell:ignore Buzan ---> + +## Layouts + +Mermaid also supports a Tidy Tree layout for mindmaps. + +``` +--- +config: + layout: tidy-tree +--- +mindmap +root((mindmap is a long thing)) + A + B + C + D +``` + +Instructions to add and register tidy-tree layout are present in [Tidy Tree Configuration](/config/tidy-tree) diff --git a/packages/mermaid/src/docs/syntax/userJourney.md b/packages/mermaid/src/docs/syntax/userJourney.md index 3476088ab..0515ae814 100644 --- a/packages/mermaid/src/docs/syntax/userJourney.md +++ b/packages/mermaid/src/docs/syntax/userJourney.md @@ -20,3 +20,5 @@ Each user journey is split into sections, these describe the part of the task the user is trying to complete. Tasks syntax is `Task name: : ` + +Score is a number between 1 and 5, inclusive. diff --git a/packages/mermaid/src/docs/syntax/xyChart.md b/packages/mermaid/src/docs/syntax/xyChart.md index 4154fb2f0..cfff201d3 100644 --- a/packages/mermaid/src/docs/syntax/xyChart.md +++ b/packages/mermaid/src/docs/syntax/xyChart.md @@ -126,7 +126,7 @@ xychart ## Chart Theme Variables -Themes for xychart resides inside xychart attribute so to set the variables use this syntax: +Themes for xychart reside inside the `xychart` attribute, allowing customization through the following syntax: ```yaml --- @@ -151,6 +151,31 @@ config: | yAxisLineColor | Color of the y-axis line | | plotColorPalette | String of colors separated by comma e.g. "#f3456, #43445" | +### Setting Colors for Lines and Bars + +To set the color for lines and bars, use the `plotColorPalette` parameter. Colors in the palette will correspond sequentially to the elements in your chart (e.g., first bar/line will use the first color specified in the palette). + +```mermaid-example +--- +config: + themeVariables: + xyChart: + plotColorPalette: '#000000, #0000FF, #00FF00, #FF0000' +--- +xychart +title "Different Colors in xyChart" +x-axis "categoriesX" ["Category 1", "Category 2", "Category 3", "Category 4"] +y-axis "valuesY" 0 --> 50 +%% Black line +line [10,20,30,40] +%% Blue bar +bar [20,30,25,35] +%% Green bar +bar [15,25,20,30] +%% Red line +line [5,15,25,35] +``` + ## Example on config and theme ```mermaid-example diff --git a/packages/mermaid/src/docs/vite.config.ts b/packages/mermaid/src/docs/vite.config.ts index 399e5c65e..9a1e0cc59 100644 --- a/packages/mermaid/src/docs/vite.config.ts +++ b/packages/mermaid/src/docs/vite.config.ts @@ -13,6 +13,10 @@ const virtualModuleId = 'virtual:mermaid-config'; const resolvedVirtualModuleId = '\0' + virtualModuleId; export default defineConfig({ + build: { + // Vite v7 changes the default target and drops old browser support + target: 'modules', + }, optimizeDeps: { // vitepress is aliased with replacement `join(DIST_CLIENT_PATH, '/index')` // This needs to be excluded from optimization diff --git a/packages/mermaid/src/mermaidAPI.spec.ts b/packages/mermaid/src/mermaidAPI.spec.ts index b2d2d3cd3..ff794abb1 100644 --- a/packages/mermaid/src/mermaidAPI.spec.ts +++ b/packages/mermaid/src/mermaidAPI.spec.ts @@ -41,7 +41,6 @@ import { decodeEntities, encodeEntities } from './utils.js'; import { toBase64 } from './utils/base64.js'; import { StateDB } from './diagrams/state/stateDb.js'; import { ensureNodeFromSelector, jsdomIt } from './tests/util.js'; -import { select } from 'd3'; import { JSDOM } from 'jsdom'; /** @@ -50,7 +49,6 @@ import { JSDOM } from 'jsdom'; */ // ------------------------------------------------------------------------------------- - describe('mermaidAPI', () => { describe('encodeEntities', () => { it('removes the ending ; from style [text1]:[optional word]#[text2]; with ', () => { @@ -913,4 +911,241 @@ graph TD;A--x|text including URL space|B;`) expect(sequenceDiagram1.db.getActors()).not.toEqual(sequenceDiagram2.db.getActors()); }); }); + + describe('mermaidAPI config precedence', () => { + const id = 'mermaid-config-test'; + + beforeEach(() => { + mermaidAPI.globalReset(); + }); + + jsdomIt('renders with YAML config taking precedence over initialize config', async () => { + mermaid.initialize({ + theme: 'forest', + fontFamily: 'Arial', + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + flowchart: { htmlLabels: false }, + }); + + const diagramText = `--- +config: + theme: base + fontFamily: Courier + themeVariables: + fontFamily: "Courier New" + fontSize: "20px" + flowchart: + htmlLabels: true +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render('yaml-over-init', diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.theme).toBe('base'); + expect(config.fontFamily).toBe('Courier'); + expect(config.themeVariables.fontFamily).toBe('Courier New'); + expect(config.themeVariables.fontSize).toBe('20px'); + expect(config.flowchart?.htmlLabels).toBe(true); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + }); + + jsdomIt( + 'renders with YAML themeVariables fully overriding initialize themeVariables', + async () => { + mermaid.initialize({ + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + }); + + const diagramText = `--- +config: + themeVariables: + fontFamily: "Courier New" + fontSize: "20px" +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render(id, diagramText); + const config = mermaidAPI.getConfig(); + + expect(config.themeVariables.fontFamily).toBe('Courier New'); + expect(config.themeVariables.fontSize).toBe('20px'); + expect(config.themeVariables.fontFamily).not.toBe('Arial'); + expect(config.themeVariables.fontSize).not.toBe('16px'); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + } + ); + + jsdomIt( + 'renders with YAML themeVariables overriding only provided keys and keeping others from initialize', + async () => { + mermaid.initialize({ + theme: 'forest', + fontFamily: 'Arial', + themeVariables: { fontFamily: 'Arial', fontSize: '16px', colorPrimary: '#ff0000' }, + }); + + const diagramText = `--- +config: + themeVariables: + fontFamily: "Courier New" +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.themeVariables.fontFamily).toBe('Courier New'); + expect(config.themeVariables.fontSize).toBe('16px'); + expect(config.themeVariables.colorPrimary).toBe('#ff0000'); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + } + ); + + jsdomIt( + 'renders with YAML config (no themeVariables) and falls back to initialize themeVariables', + async () => { + mermaid.initialize({ + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + }); + + const diagramText = `--- +config: + theme: base +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.themeVariables.fontFamily).toBe('Arial'); + expect(config.themeVariables.fontSize).toBe('16px'); + expect(config.theme).toBe('base'); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + } + ); + + jsdomIt( + 'renders with full YAML config block taking full precedence over initialize config', + async () => { + mermaid.initialize({ + theme: 'forest', + fontFamily: 'Arial', + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + flowchart: { htmlLabels: false }, + }); + + const diagramText = `--- +config: + theme: base + fontFamily: Courier + themeVariables: + fontFamily: "Courier New" + fontSize: "20px" + flowchart: + htmlLabels: true +--- +flowchart TD + A --> B +`; + + const { svg } = await mermaidAPI.render('yaml-over-init', diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.theme).toBe('base'); + expect(config.fontFamily).toBe('Courier'); + expect(config.themeVariables.fontFamily).toBe('Courier New'); + expect(config.themeVariables.fontSize).toBe('20px'); + expect(config.flowchart?.htmlLabels).toBe(true); + + const svgNode = ensureNodeFromSelector('svg', new JSDOM(svg).window.document); + expect(svgNode).not.toBeNull(); + } + ); + + jsdomIt( + 'renders with YAML config (no themeVariables) and falls back to initialize themeVariables (duplicate scenario)', + async () => { + mermaid.initialize({ + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + }); + + const diagramText = `--- +config: + theme: base +--- +flowchart TD + A --> B +`; + + await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.themeVariables.fontFamily).toBe('Arial'); + expect(config.themeVariables.fontSize).toBe('16px'); + expect(config.theme).toBe('base'); + } + ); + + jsdomIt('renders with no YAML config so initialize config is fully applied', async () => { + mermaid.initialize({ + theme: 'forest', + fontFamily: 'Arial', + themeVariables: { fontFamily: 'Arial', fontSize: '16px' }, + }); + + const diagramText = ` +flowchart TD + A --> B +`; + + await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.theme).toBe('forest'); + expect(config.fontFamily).toBe('Arial'); + expect(config.themeVariables.fontFamily).toBe('Arial'); + expect(config.themeVariables.fontSize).toBe('16px'); + }); + + jsdomIt( + 'renders with empty YAML config block and falls back to initialize config', + async () => { + mermaid.initialize({ + theme: 'dark', + themeVariables: { fontFamily: 'Times', fontSize: '14px' }, + }); + + const diagramText = `--- +config: {} +--- +flowchart TD + A --> B +`; + + await mermaidAPI.render(id, diagramText); + + const config = mermaidAPI.getConfig(); + expect(config.theme).toBe('dark'); + expect(config.themeVariables.fontFamily).toBe('Times'); + expect(config.themeVariables.fontSize).toBe('14px'); + } + ); + }); }); diff --git a/packages/mermaid/src/preprocess.ts b/packages/mermaid/src/preprocess.ts index a62326070..2334ff0b1 100644 --- a/packages/mermaid/src/preprocess.ts +++ b/packages/mermaid/src/preprocess.ts @@ -26,6 +26,7 @@ const processFrontmatter = (code: string) => { } config.gantt.displayMode = displayMode; } + return { title, config, text }; }; diff --git a/packages/mermaid/src/rendering-util/createGraph.ts b/packages/mermaid/src/rendering-util/createGraph.ts new file mode 100644 index 000000000..b08a3aae0 --- /dev/null +++ b/packages/mermaid/src/rendering-util/createGraph.ts @@ -0,0 +1,148 @@ +import { insertNode } from './rendering-elements/nodes.js'; +import type { LayoutData, NonClusterNode } from './types.ts'; +import type { Selection } from 'd3'; +import { getConfig } from '../diagram-api/diagramAPI.js'; +import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; + +// Update type: +type D3Selection = Selection< + T, + unknown, + Element | null, + unknown +>; + +/** + * Creates a graph by merging the graph construction and DOM element insertion. + * + * This function creates the graph, inserts the SVG groups (clusters, edgePaths, edgeLabels, nodes) + * into the provided element, and uses `insertNode` to add nodes to the diagram. Node dimensions + * are computed using each node's bounding box. + * + * @param element - The D3 selection in which the SVG groups are inserted. + * @param data4Layout - The layout data containing nodes and edges. + * @returns A promise resolving to an object containing the Graphology graph and the inserted groups. + */ +export async function createGraphWithElements( + element: D3Selection, + data4Layout: LayoutData +): Promise<{ + graph: graphlib.Graph; + groups: { + clusters: D3Selection; + edgePaths: D3Selection; + edgeLabels: D3Selection; + nodes: D3Selection; + rootGroups: D3Selection; + }; + nodeElements: Map>; +}> { + // Create a directed, multi graph. + const graph = new graphlib.Graph({ + multigraph: true, + compound: true, + }); + const edgesToProcess = [...data4Layout.edges]; + const config = getConfig(); + // Create groups for clusters, edge paths, edge labels, and nodes. + const clusters = element.insert('g').attr('class', 'clusters'); + const edgePaths = element.insert('g').attr('class', 'edges edgePath'); + const edgeLabels = element.insert('g').attr('class', 'edgeLabels'); + const nodesGroup = element.insert('g').attr('class', 'nodes'); + const rootGroups = element.insert('g').attr('class', 'root'); + + const nodeElements = new Map>(); + + // Insert nodes into the DOM and add them to the graph. + await Promise.all( + data4Layout.nodes.map(async (node) => { + if (node.isGroup) { + graph.setNode(node.id, { ...node }); + } else { + const childNodeEl = await insertNode(nodesGroup, node, { config, dir: node.dir }); + const boundingBox = childNodeEl.node()?.getBBox() ?? { width: 0, height: 0 }; + nodeElements.set(node.id, childNodeEl as D3Selection); + node.width = boundingBox.width; + node.height = boundingBox.height; + graph.setNode(node.id, { ...node }); + } + }) + ); + // Add edges to the graph. + for (const edge of edgesToProcess) { + if (edge.label && edge.label?.length > 0) { + // Create a label node for the edge + const labelNodeId = `edge-label-${edge.start}-${edge.end}-${edge.id}`; + const labelNode = { + id: labelNodeId, + label: edge.label, + edgeStart: edge.start, + edgeEnd: edge.end, + shape: 'labelRect', + width: 0, // Will be updated after insertion + height: 0, // Will be updated after insertion + isEdgeLabel: true, + isDummy: true, + isGroup: false, + parentId: edge.parentId, + ...(edge.dir ? { dir: edge.dir } : {}), + } as NonClusterNode; + + // Insert the label node into the DOM + const labelNodeEl = await insertNode(nodesGroup, labelNode, { config, dir: edge.dir }); + const boundingBox = labelNodeEl.node()?.getBBox() ?? { width: 0, height: 0 }; + + // Update node dimensions + labelNode.width = boundingBox.width; + labelNode.height = boundingBox.height; + + // Add to graph and tracking maps + graph.setNode(labelNodeId, { ...labelNode }); + nodeElements.set(labelNodeId, labelNodeEl as D3Selection); + data4Layout.nodes.push(labelNode); + + // Create two edges to replace the original one + const edgeToLabel = { + ...edge, + id: `${edge.id}-to-label`, + end: labelNodeId, + label: undefined, + isLabelEdge: true, + arrowTypeEnd: 'none', + arrowTypeStart: 'none', + }; + const edgeFromLabel = { + ...edge, + id: `${edge.id}-from-label`, + start: labelNodeId, + end: edge.end, + label: undefined, + isLabelEdge: true, + arrowTypeStart: 'none', + arrowTypeEnd: 'arrow_point', + }; + graph.setEdge(edgeToLabel.id, edgeToLabel.start, edgeToLabel.end, { ...edgeToLabel }); + graph.setEdge(edgeFromLabel.id, edgeFromLabel.start, edgeFromLabel.end, { ...edgeFromLabel }); + data4Layout.edges.push(edgeToLabel, edgeFromLabel); + const edgeIdToRemove = edge.id; + data4Layout.edges = data4Layout.edges.filter((edge) => edge.id !== edgeIdToRemove); + const indexInOriginal = data4Layout.edges.findIndex((e) => e.id === edge.id); + if (indexInOriginal !== -1) { + data4Layout.edges.splice(indexInOriginal, 1); + } + } else { + // Regular edge without label + graph.setEdge(edge.id, edge.start, edge.end, { ...edge }); + const edgeExists = data4Layout.edges.some((existingEdge) => existingEdge.id === edge.id); + if (!edgeExists) { + data4Layout.edges.push(edge); + } + } + } + + return { + graph, + groups: { clusters, edgePaths, edgeLabels, nodes: nodesGroup, rootGroups }, + nodeElements, + }; +} diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/cytoscape-setup.test.ts b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/cytoscape-setup.test.ts new file mode 100644 index 000000000..707b031f4 --- /dev/null +++ b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/cytoscape-setup.test.ts @@ -0,0 +1,265 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { + addNodes, + addEdges, + extractPositionedNodes, + extractPositionedEdges, +} from './cytoscape-setup.js'; +import type { Node, Edge } from '../../types.js'; + +// Mock cytoscape +const mockCy = { + add: vi.fn(), + nodes: vi.fn(), + edges: vi.fn(), +}; + +vi.mock('cytoscape', () => { + const mockCytoscape = vi.fn(() => mockCy) as any; + mockCytoscape.use = vi.fn(); + return { + default: mockCytoscape, + }; +}); + +describe('Cytoscape Setup', () => { + let mockNodes: Node[]; + let mockEdges: Edge[]; + + beforeEach(() => { + vi.clearAllMocks(); + + mockNodes = [ + { + id: '1', + label: 'Root', + isGroup: false, + shape: 'rect', + width: 100, + height: 50, + padding: 10, + x: 100, + y: 100, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + { + id: '2', + label: 'Child 1', + isGroup: false, + shape: 'rect', + width: 80, + height: 40, + padding: 10, + x: 150, + y: 150, + cssClasses: '', + cssStyles: [], + look: 'default', + }, + ]; + + mockEdges = [ + { + id: '1_2', + start: '1', + end: '2', + type: 'edge', + classes: '', + style: [], + animate: false, + arrowTypeEnd: 'arrow_point', + arrowTypeStart: 'none', + }, + ]; + }); + + describe('addNodes', () => { + it('should add nodes to cytoscape', () => { + addNodes([mockNodes[0]], mockCy as unknown as any); + + expect(mockCy.add).toHaveBeenCalledWith({ + group: 'nodes', + data: { + id: '1', + labelText: 'Root', + height: 50, + width: 100, + padding: 10, + isGroup: false, + shape: 'rect', + cssClasses: '', + cssStyles: [], + look: 'default', + }, + position: { + x: 100, + y: 100, + }, + }); + }); + + it('should add multiple nodes to cytoscape', () => { + addNodes(mockNodes, mockCy as unknown as any); + + expect(mockCy.add).toHaveBeenCalledTimes(2); + + expect(mockCy.add).toHaveBeenCalledWith({ + group: 'nodes', + data: { + id: '1', + labelText: 'Root', + height: 50, + width: 100, + padding: 10, + isGroup: false, + shape: 'rect', + cssClasses: '', + cssStyles: [], + look: 'default', + }, + position: { + x: 100, + y: 100, + }, + }); + + expect(mockCy.add).toHaveBeenCalledWith({ + group: 'nodes', + data: { + id: '2', + labelText: 'Child 1', + height: 40, + width: 80, + padding: 10, + isGroup: false, + shape: 'rect', + cssClasses: '', + cssStyles: [], + look: 'default', + }, + position: { + x: 150, + y: 150, + }, + }); + }); + }); + + describe('addEdges', () => { + it('should add edges to cytoscape', () => { + addEdges(mockEdges, mockCy as unknown as any); + + expect(mockCy.add).toHaveBeenCalledWith({ + group: 'edges', + data: { + id: '1_2', + source: '1', + target: '2', + type: 'edge', + classes: '', + style: [], + animate: false, + arrowTypeEnd: 'arrow_point', + arrowTypeStart: 'none', + }, + }); + }); + }); + + describe('extractPositionedNodes', () => { + it('should extract positioned nodes from cytoscape', () => { + const mockCytoscapeNodes = [ + { + data: () => ({ + id: '1', + labelText: 'Root', + width: 100, + height: 50, + padding: 10, + isGroup: false, + shape: 'rect', + }), + position: () => ({ x: 100, y: 100 }), + }, + { + data: () => ({ + id: '2', + labelText: 'Child 1', + width: 80, + height: 40, + padding: 10, + isGroup: false, + shape: 'rect', + }), + position: () => ({ x: 150, y: 150 }), + }, + ]; + + mockCy.nodes.mockReturnValue({ + map: (fn: unknown) => mockCytoscapeNodes.map(fn as any), + }); + + const result = extractPositionedNodes(mockCy as unknown as any); + + expect(result).toHaveLength(2); + expect(result[0]).toEqual({ + id: '1', + x: 100, + y: 100, + labelText: 'Root', + width: 100, + height: 50, + padding: 10, + isGroup: false, + shape: 'rect', + }); + }); + }); + + describe('extractPositionedEdges', () => { + it('should extract positioned edges from cytoscape', () => { + const mockCytoscapeEdges = [ + { + data: () => ({ + id: '1_2', + source: '1', + target: '2', + type: 'edge', + }), + _private: { + rscratch: { + startX: 100, + startY: 100, + midX: 125, + midY: 125, + endX: 150, + endY: 150, + }, + }, + }, + ]; + + mockCy.edges.mockReturnValue({ + map: (fn: unknown) => mockCytoscapeEdges.map(fn as any), + }); + + const result = extractPositionedEdges(mockCy as unknown as any); + + expect(result).toHaveLength(1); + expect(result[0]).toEqual({ + id: '1_2', + source: '1', + target: '2', + type: 'edge', + startX: 100, + startY: 100, + midX: 125, + midY: 125, + endX: 150, + endY: 150, + }); + }); + }); +}); diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/cytoscape-setup.ts b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/cytoscape-setup.ts new file mode 100644 index 000000000..8fb9b2599 --- /dev/null +++ b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/cytoscape-setup.ts @@ -0,0 +1,207 @@ +import cytoscape from 'cytoscape'; +import coseBilkent from 'cytoscape-cose-bilkent'; +import { select } from 'd3'; +import { log } from '../../../logger.js'; +import type { LayoutData, Node, Edge } from '../../types.js'; +import type { CytoscapeLayoutConfig, PositionedNode, PositionedEdge } from './types.js'; + +// Inject the layout algorithm into cytoscape +cytoscape.use(coseBilkent); + +/** + * Declare module augmentation for cytoscape edge types + */ +declare module 'cytoscape' { + interface EdgeSingular { + _private: { + bodyBounds: unknown; + rscratch: { + startX: number; + startY: number; + midX: number; + midY: number; + endX: number; + endY: number; + }; + }; + } +} + +/** + * Add nodes to cytoscape instance from provided node array + * This function processes only the nodes provided in the data structure + * @param nodes - Array of nodes to add + * @param cy - The cytoscape instance + */ +export function addNodes(nodes: Node[], cy: cytoscape.Core): void { + nodes.forEach((node) => { + const nodeData: Record = { + id: node.id, + labelText: node.label, + height: node.height, + width: node.width, + padding: node.padding ?? 0, + }; + + // Add any additional properties from the node + Object.keys(node).forEach((key) => { + if (!['id', 'label', 'height', 'width', 'padding', 'x', 'y'].includes(key)) { + nodeData[key] = (node as unknown as Record)[key]; + } + }); + + cy.add({ + group: 'nodes', + data: nodeData, + position: { + x: node.x ?? 0, + y: node.y ?? 0, + }, + }); + }); +} + +/** + * Add edges to cytoscape instance from provided edge array + * This function processes only the edges provided in the data structure + * @param edges - Array of edges to add + * @param cy - The cytoscape instance + */ +export function addEdges(edges: Edge[], cy: cytoscape.Core): void { + edges.forEach((edge) => { + const edgeData: Record = { + id: edge.id, + source: edge.start, + target: edge.end, + }; + + // Add any additional properties from the edge + Object.keys(edge).forEach((key) => { + if (!['id', 'start', 'end'].includes(key)) { + edgeData[key] = (edge as unknown as Record)[key]; + } + }); + + cy.add({ + group: 'edges', + data: edgeData, + }); + }); +} + +/** + * Create and configure cytoscape instance + * @param data - Layout data containing nodes and edges + * @returns Promise resolving to configured cytoscape instance + */ +export function createCytoscapeInstance(data: LayoutData): Promise { + return new Promise((resolve) => { + // Add temporary render element + const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none'); + + const cy = cytoscape({ + container: document.getElementById('cy'), // container to render in + style: [ + { + selector: 'edge', + style: { + 'curve-style': 'bezier', + }, + }, + ], + }); + + // Remove element after layout + renderEl.remove(); + + // Add all nodes and edges to cytoscape using the generic functions + addNodes(data.nodes, cy); + addEdges(data.edges, cy); + + // Make cytoscape care about the dimensions of the nodes + cy.nodes().forEach(function (n) { + n.layoutDimensions = () => { + const nodeData = n.data(); + return { w: nodeData.width, h: nodeData.height }; + }; + }); + + // Configure and run the cose-bilkent layout + const layoutConfig: CytoscapeLayoutConfig = { + name: 'cose-bilkent', + // @ts-ignore Types for cose-bilkent are not correct? + quality: 'proof', + styleEnabled: false, + animate: false, + }; + + cy.layout(layoutConfig).run(); + + cy.ready((e) => { + log.info('Cytoscape ready', e); + resolve(cy); + }); + }); +} + +/** + * Extract positioned nodes from cytoscape instance + * @param cy - The cytoscape instance after layout + * @returns Array of positioned nodes + */ +export function extractPositionedNodes(cy: cytoscape.Core): PositionedNode[] { + return cy.nodes().map((node) => { + const data = node.data(); + const position = node.position(); + + // Create a positioned node with all original data plus position + const positionedNode: PositionedNode = { + id: data.id, + x: position.x, + y: position.y, + }; + + // Add all other properties from the original data + Object.keys(data).forEach((key) => { + if (key !== 'id') { + positionedNode[key] = data[key]; + } + }); + + return positionedNode; + }); +} + +/** + * Extract positioned edges from cytoscape instance + * @param cy - The cytoscape instance after layout + * @returns Array of positioned edges + */ +export function extractPositionedEdges(cy: cytoscape.Core): PositionedEdge[] { + return cy.edges().map((edge) => { + const data = edge.data(); + const rscratch = edge._private.rscratch; + + // Create a positioned edge with all original data plus position + const positionedEdge: PositionedEdge = { + id: data.id, + source: data.source, + target: data.target, + startX: rscratch.startX, + startY: rscratch.startY, + midX: rscratch.midX, + midY: rscratch.midY, + endX: rscratch.endX, + endY: rscratch.endY, + }; + + // Add all other properties from the original data + Object.keys(data).forEach((key) => { + if (!['id', 'source', 'target'].includes(key)) { + positionedEdge[key] = data[key]; + } + }); + + return positionedEdge; + }); +} diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/index.ts b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/index.ts new file mode 100644 index 000000000..9e12d38a7 --- /dev/null +++ b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/index.ts @@ -0,0 +1,25 @@ +import { render as renderWithCoseBilkent } from './render.js'; + +/** + * Cose-Bilkent Layout Algorithm for Generic Diagrams + * + * This module provides a layout algorithm implementation using Cytoscape + * with the cose-bilkent algorithm for positioning nodes and edges. + * + * The algorithm follows the unified rendering pattern and can be used + * by any diagram type that provides compatible LayoutData. + */ + +/** + * Render function for the cose-bilkent layout algorithm + * + * This function follows the unified rendering pattern used by all layout algorithms. + * It takes LayoutData, inserts nodes into DOM, runs the cose-bilkent layout algorithm, + * and renders the positioned elements to the SVG. + * + * @param layoutData - Layout data containing nodes, edges, and configuration + * @param svg - SVG element to render to + * @param helpers - Internal helper functions for rendering + * @param options - Rendering options + */ +export const render = renderWithCoseBilkent; diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/layout.test.ts b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/layout.test.ts new file mode 100644 index 000000000..f5650d3e8 --- /dev/null +++ b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/layout.test.ts @@ -0,0 +1,236 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { validateLayoutData, executeCoseBilkentLayout } from './layout.js'; +import type { LayoutResult } from './types.js'; +import type { MindmapNode } from '../../../diagrams/mindmap/mindmapTypes.js'; +import type { MermaidConfig } from '../../../config.type.js'; +import type { LayoutData } from '../../types.js'; + +// Mock cytoscape and cytoscape-cose-bilkent before importing the modules + +vi.mock('cytoscape', () => { + const mockCy = { + add: vi.fn(), + nodes: vi.fn(() => ({ + forEach: vi.fn(), + map: vi.fn((fn) => [ + fn({ + data: () => ({ + id: '1', + nodeId: '1', + labelText: 'Root', + level: 0, + type: 0, + width: 100, + height: 50, + padding: 10, + }), + position: () => ({ x: 100, y: 100 }), + }), + ]), + })), + edges: vi.fn(() => ({ + map: vi.fn((fn) => [ + fn({ + data: () => ({ + id: '1_2', + source: '1', + target: '2', + depth: 0, + }), + _private: { + rscratch: { + startX: 100, + startY: 100, + midX: 150, + midY: 150, + endX: 200, + endY: 200, + }, + }, + }), + ]), + })), + layout: vi.fn(() => ({ + run: vi.fn(), + })), + ready: vi.fn((callback) => callback({})), + }; + + const mockCytoscape = vi.fn(() => mockCy); + (mockCytoscape as any).use = vi.fn(); + + return { + default: mockCytoscape, + }; +}); + +describe('Cose-Bilkent Layout Algorithm', () => { + let mockConfig: MermaidConfig; + let mockRootNode: MindmapNode; + let mockLayoutData: LayoutData; + + beforeEach(() => { + mockConfig = { + mindmap: { + layoutAlgorithm: 'cose-bilkent', + padding: 10, + maxNodeWidth: 200, + useMaxWidth: true, + }, + } as MermaidConfig; + + mockRootNode = { + id: 1, + nodeId: '1', + level: 0, + descr: 'Root', + type: 0, + width: 100, + height: 50, + padding: 10, + x: 0, + y: 0, + children: [ + { + id: 2, + nodeId: '2', + level: 1, + descr: 'Child 1', + type: 0, + width: 80, + height: 40, + padding: 10, + x: 0, + y: 0, + }, + ], + } as MindmapNode; + + mockLayoutData = { + nodes: [ + { + id: '1', + nodeId: '1', + level: 0, + descr: 'Root', + type: 0, + width: 100, + height: 50, + padding: 10, + isGroup: false, + }, + { + id: '2', + nodeId: '2', + level: 1, + descr: 'Child 1', + type: 0, + width: 80, + height: 40, + padding: 10, + isGroup: false, + }, + ], + edges: [ + { + id: '1_2', + source: '1', + target: '2', + depth: 0, + }, + ], + config: mockConfig, + rootNode: mockRootNode, + }; + }); + + describe('validateLayoutData', () => { + it('should validate correct layout data', () => { + expect(() => validateLayoutData(mockLayoutData)).not.toThrow(); + }); + + it('should throw error for missing data', () => { + expect(() => validateLayoutData(null as any)).toThrow('Layout data is required'); + }); + + it('should throw error for missing root node', () => { + const invalidData = { ...mockLayoutData, rootNode: null as any }; + expect(() => validateLayoutData(invalidData)).toThrow('Root node is required'); + }); + + it('should throw error for missing config', () => { + const invalidData = { ...mockLayoutData, config: null as any }; + expect(() => validateLayoutData(invalidData)).toThrow('Configuration is required'); + }); + + it('should throw error for invalid nodes array', () => { + const invalidData = { ...mockLayoutData, nodes: null as any }; + expect(() => validateLayoutData(invalidData)).toThrow('No nodes found in layout data'); + }); + + it('should throw error for invalid edges array', () => { + const invalidData = { ...mockLayoutData, edges: null as any }; + expect(() => validateLayoutData(invalidData)).toThrow('Edges array is required'); + }); + }); + + describe('layout function', () => { + it('should execute layout algorithm successfully', async () => { + const result: LayoutResult = await executeCoseBilkentLayout(mockLayoutData, mockConfig); + + expect(result).toBeDefined(); + expect(result.nodes).toBeDefined(); + expect(result.edges).toBeDefined(); + expect(Array.isArray(result.nodes)).toBe(true); + expect(Array.isArray(result.edges)).toBe(true); + }); + + it('should return positioned nodes with coordinates', async () => { + const result: LayoutResult = await executeCoseBilkentLayout(mockLayoutData, mockConfig); + + expect(result.nodes.length).toBeGreaterThan(0); + result.nodes.forEach((node) => { + expect(node.x).toBeDefined(); + expect(node.y).toBeDefined(); + expect(typeof node.x).toBe('number'); + expect(typeof node.y).toBe('number'); + }); + }); + + it('should return positioned edges with coordinates', async () => { + const result: LayoutResult = await executeCoseBilkentLayout(mockLayoutData, mockConfig); + + expect(result.edges.length).toBeGreaterThan(0); + result.edges.forEach((edge) => { + expect(edge.startX).toBeDefined(); + expect(edge.startY).toBeDefined(); + expect(edge.midX).toBeDefined(); + expect(edge.midY).toBeDefined(); + expect(edge.endX).toBeDefined(); + expect(edge.endY).toBeDefined(); + }); + }); + + it('should handle empty mindmap data gracefully', async () => { + const emptyData: LayoutData = { + nodes: [], + edges: [], + config: mockConfig, + rootNode: mockRootNode, + }; + + const result: LayoutResult = await executeCoseBilkentLayout(emptyData, mockConfig); + expect(result).toBeDefined(); + expect(result.nodes).toBeDefined(); + expect(result.edges).toBeDefined(); + expect(Array.isArray(result.nodes)).toBe(true); + expect(Array.isArray(result.edges)).toBe(true); + }); + + it('should throw error for invalid data', async () => { + const invalidData = { ...mockLayoutData, rootNode: null as any }; + + await expect(executeCoseBilkentLayout(invalidData, mockConfig)).rejects.toThrow(); + }); + }); +}); diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/layout.ts b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/layout.ts new file mode 100644 index 000000000..433723259 --- /dev/null +++ b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/layout.ts @@ -0,0 +1,77 @@ +import type { MermaidConfig } from '../../../config.type.js'; +import { log } from '../../../logger.js'; +import type { LayoutData } from '../../types.js'; +import type { LayoutResult } from './types.js'; +import { + createCytoscapeInstance, + extractPositionedNodes, + extractPositionedEdges, +} from './cytoscape-setup.js'; + +/** + * Execute the cose-bilkent layout algorithm on generic layout data + * + * This function takes layout data and uses Cytoscape with the cose-bilkent + * algorithm to calculate optimal node positions and edge paths. + * + * @param data - The layout data containing nodes, edges, and configuration + * @param config - Mermaid configuration object + * @returns Promise resolving to layout result with positioned nodes and edges + */ +export async function executeCoseBilkentLayout( + data: LayoutData, + _config: MermaidConfig +): Promise { + log.debug('Starting cose-bilkent layout algorithm'); + + try { + // Validate layout data structure + validateLayoutData(data); + + // Create and configure cytoscape instance + const cy = await createCytoscapeInstance(data); + + // Extract positioned nodes and edges after layout + const positionedNodes = extractPositionedNodes(cy); + const positionedEdges = extractPositionedEdges(cy); + + log.debug(`Layout completed: ${positionedNodes.length} nodes, ${positionedEdges.length} edges`); + + return { + nodes: positionedNodes, + edges: positionedEdges, + }; + } catch (error) { + log.error('Error in cose-bilkent layout algorithm:', error); + throw error; + } +} + +/** + * Validate layout data structure + * @param data - The data to validate + * @returns True if data is valid, throws error otherwise + */ +export function validateLayoutData(data: LayoutData): boolean { + if (!data) { + throw new Error('Layout data is required'); + } + + if (!data.config) { + throw new Error('Configuration is required in layout data'); + } + + if (!data.rootNode) { + throw new Error('Root node is required'); + } + + if (!data.nodes || !Array.isArray(data.nodes)) { + throw new Error('No nodes found in layout data'); + } + + if (!Array.isArray(data.edges)) { + throw new Error('Edges array is required in layout data'); + } + + return true; +} diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/render.ts b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/render.ts new file mode 100644 index 000000000..2dbbf5d7e --- /dev/null +++ b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/render.ts @@ -0,0 +1,197 @@ +import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from 'mermaid'; +import { executeCoseBilkentLayout } from './layout.js'; +import type { D3Selection } from '../../../types.js'; + +type Node = Record; + +interface NodeWithPosition extends Node { + x?: number; + y?: number; + domId?: string | SVGGroup | D3Selection; + width?: number; + height?: number; + id?: string; +} + +/** + * Render function for cose-bilkent layout algorithm + * + * This follows the same pattern as ELK and dagre renderers: + * 1. Insert nodes into DOM to get their actual dimensions + * 2. Run the layout algorithm to calculate positions + * 3. Position the nodes and edges based on layout results + */ +export const render = async ( + data4Layout: LayoutData, + svg: SVG, + { + insertCluster, + insertEdge, + insertEdgeLabel, + insertMarkers, + insertNode, + log, + positionEdgeLabel, + }: InternalHelpers, + { algorithm: _algorithm }: RenderOptions +) => { + const nodeDb: Record = {}; + const clusterDb: Record = {}; + + // Insert markers for edges + const element = svg.select('g'); + insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId); + + // Create container groups + const subGraphsEl = element.insert('g').attr('class', 'subgraphs'); + const edgePaths = element.insert('g').attr('class', 'edgePaths'); + const edgeLabels = element.insert('g').attr('class', 'edgeLabels'); + const nodes = element.insert('g').attr('class', 'nodes'); + + // Step 1: Insert nodes into DOM to get their actual dimensions + log.debug('Inserting nodes into DOM for dimension calculation'); + + await Promise.all( + data4Layout.nodes.map(async (node) => { + if (node.isGroup) { + // Handle subgraphs/clusters + const clusterNode: NodeWithPosition = { ...node }; + clusterDb[node.id] = clusterNode; + nodeDb[node.id] = clusterNode; + + // Insert cluster to get dimensions + await insertCluster(subGraphsEl, node); + } else { + // Handle regular nodes + const nodeWithPosition: NodeWithPosition = { ...node }; + nodeDb[node.id] = nodeWithPosition; + + // Insert node to get actual dimensions + const nodeEl = await insertNode(nodes, node, { + config: data4Layout.config, + dir: data4Layout.direction || 'TB', + }); + + // Get the actual bounding box after insertion + const boundingBox = nodeEl.node()!.getBBox(); + nodeWithPosition.width = boundingBox.width; + nodeWithPosition.height = boundingBox.height; + nodeWithPosition.domId = nodeEl; + + log.debug(`Node ${node.id} dimensions: ${boundingBox.width}x${boundingBox.height}`); + } + }) + ); + + // Step 2: Run the cose-bilkent layout algorithm + log.debug('Running cose-bilkent layout algorithm'); + + // Update the layout data with actual dimensions + const updatedLayoutData = { + ...data4Layout, + nodes: data4Layout.nodes.map((node) => { + const nodeWithDimensions = nodeDb[node.id]; + return { + ...node, + width: nodeWithDimensions.width, + height: nodeWithDimensions.height, + }; + }), + }; + + const layoutResult = await executeCoseBilkentLayout(updatedLayoutData, data4Layout.config); + + // Step 3: Position the nodes based on layout results + log.debug('Positioning nodes based on layout results'); + + layoutResult.nodes.forEach((positionedNode) => { + const node = nodeDb[positionedNode.id]; + if (node?.domId) { + // Position the node at the calculated coordinates + // The positionedNode.x/y represents the center of the node, so use directly + (node.domId as D3Selection).attr( + 'transform', + `translate(${positionedNode.x}, ${positionedNode.y})` + ); + + // Store the final position + node.x = positionedNode.x; + node.y = positionedNode.y; + + log.debug(`Positioned node ${node.id} at center (${positionedNode.x}, ${positionedNode.y})`); + } + }); + + layoutResult.edges.forEach((positionedEdge) => { + const edge = data4Layout.edges.find((e) => e.id === positionedEdge.id); + if (edge) { + // Update the edge data with positioned coordinates + edge.points = [ + { x: positionedEdge.startX, y: positionedEdge.startY }, + { x: positionedEdge.midX, y: positionedEdge.midY }, + { x: positionedEdge.endX, y: positionedEdge.endY }, + ]; + } + }); + + // Step 4: Insert and position edges + log.debug('Inserting and positioning edges'); + + await Promise.all( + data4Layout.edges.map(async (edge) => { + // Insert edge label first + const _edgeLabel = await insertEdgeLabel(edgeLabels, edge); + + // Get start and end nodes + const startNode = nodeDb[edge.start ?? '']; + const endNode = nodeDb[edge.end ?? '']; + + if (startNode && endNode) { + // Find the positioned edge data + const positionedEdge = layoutResult.edges.find((e) => e.id === edge.id); + + if (positionedEdge) { + log.debug('APA01 positionedEdge', positionedEdge); + // Create edge path with positioned coordinates + const edgeWithPath = { ...edge }; + + // Insert the edge path + const paths = insertEdge( + edgePaths, + edgeWithPath, + clusterDb, + data4Layout.type, + startNode, + endNode, + data4Layout.diagramId + ); + + // Position the edge label + positionEdgeLabel(edgeWithPath, paths); + } else { + // Fallback: create a simple straight line between nodes + const edgeWithPath = { + ...edge, + points: [ + { x: startNode.x || 0, y: startNode.y || 0 }, + { x: endNode.x || 0, y: endNode.y || 0 }, + ], + }; + + const paths = insertEdge( + edgePaths, + edgeWithPath, + clusterDb, + data4Layout.type, + startNode, + endNode, + data4Layout.diagramId + ); + positionEdgeLabel(edgeWithPath, paths); + } + } + }) + ); + + log.debug('Cose-bilkent rendering completed'); +}; diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/types.ts b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/types.ts new file mode 100644 index 000000000..fade24682 --- /dev/null +++ b/packages/mermaid/src/rendering-util/layout-algorithms/cose-bilkent/types.ts @@ -0,0 +1,43 @@ +/** + * Positioned node after layout calculation + */ +export interface PositionedNode { + id: string; + x: number; + y: number; + [key: string]: unknown; // Allow additional properties +} + +/** + * Positioned edge after layout calculation + */ +export interface PositionedEdge { + id: string; + source: string; + target: string; + startX: number; + startY: number; + midX: number; + midY: number; + endX: number; + endY: number; + [key: string]: unknown; // Allow additional properties +} + +/** + * Result of layout algorithm execution + */ +export interface LayoutResult { + nodes: PositionedNode[]; + edges: PositionedEdge[]; +} + +/** + * Cytoscape layout configuration + */ +export interface CytoscapeLayoutConfig { + name: 'cose-bilkent'; + quality: 'proof'; + styleEnabled: boolean; + animate: boolean; +} diff --git a/packages/mermaid/src/rendering-util/render.ts b/packages/mermaid/src/rendering-util/render.ts index b975e7bf9..aa4bceca3 100644 --- a/packages/mermaid/src/rendering-util/render.ts +++ b/packages/mermaid/src/rendering-util/render.ts @@ -4,6 +4,9 @@ import { internalHelpers } from '../internals.js'; import { log } from '../logger.js'; import type { LayoutData } from './types.js'; +// console.log('MUST be removed, this only for keeping dev server working'); +// import tmp from './layout-algorithms/dagre/index.js'; + export interface RenderOptions { algorithm?: string; } @@ -39,6 +42,14 @@ const registerDefaultLayoutLoaders = () => { name: 'dagre', loader: async () => await import('./layout-algorithms/dagre/index.js'), }, + ...(includeLargeFeatures + ? [ + { + name: 'cose-bilkent', + loader: async () => await import('./layout-algorithms/cose-bilkent/index.js'), + }, + ] + : []), ]); }; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edges.js b/packages/mermaid/src/rendering-util/rendering-elements/edges.js index db48e313c..9e308631a 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/edges.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/edges.js @@ -1,9 +1,13 @@ import { getConfig } from '../../diagram-api/diagramAPI.js'; -import { evaluate, getUrl } from '../../diagrams/common/common.js'; +import { evaluate } from '../../diagrams/common/common.js'; import { log } from '../../logger.js'; import { createText } from '../createText.js'; import utils from '../../utils.js'; -import { getLineFunctionsWithOffset } from '../../utils/lineWithOffset.js'; +import { + getLineFunctionsWithOffset, + markerOffsets, + markerOffsets2, +} from '../../utils/lineWithOffset.js'; import { getSubGraphTitleMargins } from '../../utils/subGraphTitleMargins.js'; import { @@ -25,10 +29,10 @@ import { import rough from 'roughjs'; import createLabel from './createLabel.js'; import { addEdgeMarkers } from './edgeMarker.ts'; -import { isLabelStyle } from './shapes/handDrawnShapeStyles.js'; +import { isLabelStyle, styles2String } from './shapes/handDrawnShapeStyles.js'; -const edgeLabels = new Map(); -const terminalLabels = new Map(); +export const edgeLabels = new Map(); +export const terminalLabels = new Map(); export const clear = () => { edgeLabels.clear(); @@ -43,8 +47,10 @@ export const getLabelStyles = (styleArray) => { export const insertEdgeLabel = async (elem, edge) => { let useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels); + const { labelStyles } = styles2String(edge); + edge.labelStyle = labelStyles; const labelElement = await createText(elem, edge.label, { - style: getLabelStyles(edge.labelStyle), + style: edge.labelStyle, useHtmlLabels, addSvgBackground: true, isNode: false, @@ -55,7 +61,7 @@ export const insertEdgeLabel = async (elem, edge) => { const edgeLabel = elem.insert('g').attr('class', 'edgeLabel'); // Create inner g, label, this will be positioned now for centering the text - const label = edgeLabel.insert('g').attr('class', 'label'); + const label = edgeLabel.insert('g').attr('class', 'label').attr('data-id', edge.id); label.node().appendChild(labelElement); // Center the label @@ -438,8 +444,33 @@ const fixCorners = function (lineData) { } return newLineData; }; +const generateDashArray = (len, oValueS, oValueE) => { + const middleLength = len - oValueS - oValueE; + const dashLength = 2; // Length of each dash + const gapLength = 2; // Length of each gap + const dashGapPairLength = dashLength + gapLength; -export const insertEdge = function (elem, edge, clusterDb, diagramType, startNode, endNode, id) { + // Calculate number of complete dash-gap pairs that can fit + const numberOfPairs = Math.floor(middleLength / dashGapPairLength); + + // Generate the middle pattern array + const middlePattern = Array(numberOfPairs).fill(`${dashLength} ${gapLength}`).join(' '); + + // Combine all parts + const dashArray = `0 ${oValueS} ${middlePattern} ${oValueE}`; + + return dashArray; +}; +export const insertEdge = function ( + elem, + edge, + clusterDb, + diagramType, + startNode, + endNode, + id, + skipIntersect = false +) { const { handDrawnSeed } = getConfig(); let points = edge.points; let pointsHasChanged = false; @@ -453,11 +484,12 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod edgeClassStyles.push(edge.cssCompiledStyles[key]); } - if (head.intersect && tail.intersect) { + log.debug('UIO intersect check', edge.points, head.x, tail.x); + if (head.intersect && tail.intersect && !skipIntersect) { points = points.slice(1, edge.points.length - 1); points.unshift(tail.intersect(points[0])); log.debug( - 'Last point APA12', + 'Last point UIO', edge.start, '-->', edge.end, @@ -467,6 +499,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod ); points.push(head.intersect(points[points.length - 1])); } + const pointsStr = btoa(JSON.stringify(points)); if (edge.toCluster) { log.info('to cluster abc88', clusterDb.get(edge.toCluster)); points = cutPathAtIntersect(edge.points, clusterDb.get(edge.toCluster).node); @@ -530,6 +563,10 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod curve = curveBasis; } + // if (edge.curve) { + // curve = edge.curve; + // } + const { x, y } = getLineFunctionsWithOffset(edge); const lineFunction = line().x(x).y(y).curve(curve); @@ -561,10 +598,14 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod strokeClasses += ' edge-pattern-solid'; } let svgPath; - let linePath = lineFunction(lineData); - const edgeStyles = Array.isArray(edge.style) ? edge.style : edge.style ? [edge.style] : []; + let linePath = + edge.curve === 'rounded' + ? generateRoundedPath(applyMarkerOffsetsToPoints(lineData, edge), 5) + : lineFunction(lineData); + const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style]; let strokeColor = edgeStyles.find((style) => style?.startsWith('stroke:')); + let animatedEdge = false; if (edge.look === 'handDrawn') { const rc = rough.svg(elem); Object.assign([], lineData); @@ -595,7 +636,10 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod animationClass = ' edge-animation-' + edge.animation; } - const pathStyle = stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles; + const pathStyle = + (stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) + + ';' + + (edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : ''); svgPath = elem .append('path') .attr('d', linePath) @@ -605,11 +649,39 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '') + (animationClass ?? '') ) .attr('style', pathStyle); + + //eslint-disable-next-line @typescript-eslint/prefer-regexp-exec strokeColor = pathStyle.match(/stroke:([^;]+)/)?.[1]; + + // Possible fix to remove eslint-disable-next-line + //strokeColor = /stroke:([^;]+)/.exec(pathStyle)?.[1]; + + animatedEdge = + edge.animate === true || !!edge.animation || stylesFromClasses.includes('animation'); + const pathNode = svgPath.node(); + const len = typeof pathNode.getTotalLength === 'function' ? pathNode.getTotalLength() : 0; + const oValueS = markerOffsets2[edge.arrowTypeStart] || 0; + const oValueE = markerOffsets2[edge.arrowTypeEnd] || 0; + + if (edge.look === 'neo' && !animatedEdge) { + const dashArray = + edge.pattern === 'dotted' || edge.pattern === 'dashed' + ? generateDashArray(len, oValueS, oValueE) + : `0 ${oValueS} ${len - oValueS - oValueE} ${oValueE}`; + + // No offset needed because we already start with a zero-length dash that effectively sets us up for a gap at the start. + const mOffset = `stroke-dasharray: ${dashArray}; stroke-dashoffset: 0;`; + svgPath.attr('style', mOffset + svgPath.attr('style')); + } } - // DEBUG code, DO NOT REMOVE - // adds a red circle at each edge coordinate + // MC Special + svgPath.attr('data-edge', true); + svgPath.attr('data-et', 'edge'); + svgPath.attr('data-id', edge.id); + svgPath.attr('data-points', pointsStr); + + // DEBUG code, adds a red circle at each edge coordinate // cornerPoints.forEach((point) => { // elem // .append('circle') @@ -619,19 +691,27 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod // .attr('cx', point.x) // .attr('cy', point.y); // }); - // lineData.forEach((point) => { - // elem - // .append('circle') - // .style('stroke', 'blue') - // .style('fill', 'blue') - // .attr('r', 3) - // .attr('cx', point.x) - // .attr('cy', point.y); - // }); + if (edge.showPoints) { + lineData.forEach((point) => { + elem + .append('circle') + .style('stroke', 'red') + .style('fill', 'red') + .attr('r', 1) + .attr('cx', point.x) + .attr('cy', point.y); + }); + } let url = ''; if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) { - url = getUrl(true); + url = + window.location.protocol + + '//' + + window.location.host + + window.location.pathname + + window.location.search; + url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)'); } log.info('arrowTypeStart', edge.arrowTypeStart); log.info('arrowTypeEnd', edge.arrowTypeEnd); @@ -650,3 +730,134 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod paths.originalPath = edge.points; return paths; }; + +/** + * Generates SVG path data with rounded corners from an array of points. + * @param {Array} points - Array of points in the format [{x: Number, y: Number}, ...] + * @param {Number} radius - The radius of the rounded corners + * @returns {String} - SVG path data string + */ +function generateRoundedPath(points, radius) { + if (points.length < 2) { + return ''; + } + + let path = ''; + const size = points.length; + const epsilon = 1e-5; + + for (let i = 0; i < size; i++) { + const currPoint = points[i]; + const prevPoint = points[i - 1]; + const nextPoint = points[i + 1]; + + if (i === 0) { + // Move to the first point + path += `M${currPoint.x},${currPoint.y}`; + } else if (i === size - 1) { + // Last point, draw a straight line to the final point + path += `L${currPoint.x},${currPoint.y}`; + } else { + // Calculate vectors for incoming and outgoing segments + const dx1 = currPoint.x - prevPoint.x; + const dy1 = currPoint.y - prevPoint.y; + const dx2 = nextPoint.x - currPoint.x; + const dy2 = nextPoint.y - currPoint.y; + + const len1 = Math.hypot(dx1, dy1); + const len2 = Math.hypot(dx2, dy2); + + // Prevent division by zero + if (len1 < epsilon || len2 < epsilon) { + path += `L${currPoint.x},${currPoint.y}`; + continue; + } + + // Normalize the vectors + const nx1 = dx1 / len1; + const ny1 = dy1 / len1; + const nx2 = dx2 / len2; + const ny2 = dy2 / len2; + + // Calculate the angle between the vectors + const dot = nx1 * nx2 + ny1 * ny2; + // Clamp the dot product to avoid numerical issues with acos + const clampedDot = Math.max(-1, Math.min(1, dot)); + const angle = Math.acos(clampedDot); + + // Skip rounding if the angle is too small or too close to 180 degrees + if (angle < epsilon || Math.abs(Math.PI - angle) < epsilon) { + path += `L${currPoint.x},${currPoint.y}`; + continue; + } + + // Calculate the distance to offset the control point + const cutLen = Math.min(radius / Math.sin(angle / 2), len1 / 2, len2 / 2); + + // Calculate the start and end points of the curve + const startX = currPoint.x - nx1 * cutLen; + const startY = currPoint.y - ny1 * cutLen; + const endX = currPoint.x + nx2 * cutLen; + const endY = currPoint.y + ny2 * cutLen; + + // Draw the line to the start of the curve + path += `L${startX},${startY}`; + + // Draw the quadratic Bezier curve + path += `Q${currPoint.x},${currPoint.y} ${endX},${endY}`; + } + } + + return path; +} +// Helper function to calculate delta and angle between two points +function calculateDeltaAndAngle(point1, point2) { + if (!point1 || !point2) { + return { angle: 0, deltaX: 0, deltaY: 0 }; + } + const deltaX = point2.x - point1.x; + const deltaY = point2.y - point1.y; + const angle = Math.atan2(deltaY, deltaX); + return { angle, deltaX, deltaY }; +} + +// Function to adjust the first and last points of the points array +function applyMarkerOffsetsToPoints(points, edge) { + // Copy the points array to avoid mutating the original data + const newPoints = points.map((point) => ({ ...point })); + + // Handle the first point (start of the edge) + if (points.length >= 2 && markerOffsets[edge.arrowTypeStart]) { + const offsetValue = markerOffsets[edge.arrowTypeStart]; + + const point1 = points[0]; + const point2 = points[1]; + + const { angle } = calculateDeltaAndAngle(point1, point2); + + const offsetX = offsetValue * Math.cos(angle); + const offsetY = offsetValue * Math.sin(angle); + + newPoints[0].x = point1.x + offsetX; + newPoints[0].y = point1.y + offsetY; + } + + // Handle the last point (end of the edge) + const n = points.length; + if (n >= 2 && markerOffsets[edge.arrowTypeEnd]) { + const offsetValue = markerOffsets[edge.arrowTypeEnd]; + + const point1 = points[n - 1]; + const point2 = points[n - 2]; + + const { angle } = calculateDeltaAndAngle(point2, point1); + + const offsetX = offsetValue * Math.cos(angle); + const offsetY = offsetValue * Math.sin(angle); + + newPoints[n - 1].x = point1.x - offsetX; + newPoints[n - 1].y = point1.y - offsetY; + } + + return newPoints; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js index bd3eb497f..6d476fac9 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/intersect/intersect-line.js @@ -2,64 +2,63 @@ * Returns the point at which two lines, p and q, intersect or returns undefined if they do not intersect. */ function intersectLine(p1, p2, q1, q2) { - // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994, - // p7 and p473. + { + // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994, + // p7 and p473. - var a1, a2, b1, b2, c1, c2; - var r1, r2, r3, r4; - var denom, offset, num; - var x, y; + // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x + + // b1 y + c1 = 0. + const a1 = p2.y - p1.y; + const b1 = p1.x - p2.x; + const c1 = p2.x * p1.y - p1.x * p2.y; - // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x + - // b1 y + c1 = 0. - a1 = p2.y - p1.y; - b1 = p1.x - p2.x; - c1 = p2.x * p1.y - p1.x * p2.y; + // Compute r3 and r4. + const r3 = a1 * q1.x + b1 * q1.y + c1; + const r4 = a1 * q2.x + b1 * q2.y + c1; - // Compute r3 and r4. - r3 = a1 * q1.x + b1 * q1.y + c1; - r4 = a1 * q2.x + b1 * q2.y + c1; + const epsilon = 1e-6; - // Check signs of r3 and r4. If both point 3 and point 4 lie on - // same side of line 1, the line segments do not intersect. - if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) { - return /*DON'T_INTERSECT*/; + // Check signs of r3 and r4. If both point 3 and point 4 lie on + // same side of line 1, the line segments do not intersect. + if (r3 !== 0 && r4 !== 0 && sameSign(r3, r4)) { + return /*DON'T_INTERSECT*/; + } + + // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0 + const a2 = q2.y - q1.y; + const b2 = q1.x - q2.x; + const c2 = q2.x * q1.y - q1.x * q2.y; + + // Compute r1 and r2 + const r1 = a2 * p1.x + b2 * p1.y + c2; + const r2 = a2 * p2.x + b2 * p2.y + c2; + + // Check signs of r1 and r2. If both point 1 and point 2 lie + // on same side of second line segment, the line segments do + // not intersect. + if (Math.abs(r1) < epsilon && Math.abs(r2) < epsilon && sameSign(r1, r2)) { + return /*DON'T_INTERSECT*/; + } + + // Line segments intersect: compute intersection point. + const denom = a1 * b2 - a2 * b1; + if (denom === 0) { + return /*COLLINEAR*/; + } + + const offset = Math.abs(denom / 2); + + // The denom/2 is to get rounding instead of truncating. It + // is added or subtracted to the numerator, depending upon the + // sign of the numerator. + let num = b1 * c2 - b2 * c1; + const x = num < 0 ? (num - offset) / denom : (num + offset) / denom; + + num = a2 * c1 - a1 * c2; + const y = num < 0 ? (num - offset) / denom : (num + offset) / denom; + + return { x: x, y: y }; } - - // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0 - a2 = q2.y - q1.y; - b2 = q1.x - q2.x; - c2 = q2.x * q1.y - q1.x * q2.y; - - // Compute r1 and r2 - r1 = a2 * p1.x + b2 * p1.y + c2; - r2 = a2 * p2.x + b2 * p2.y + c2; - - // Check signs of r1 and r2. If both point 1 and point 2 lie - // on same side of second line segment, the line segments do - // not intersect. - if (r1 !== 0 && r2 !== 0 && sameSign(r1, r2)) { - return /*DON'T_INTERSECT*/; - } - - // Line segments intersect: compute intersection point. - denom = a1 * b2 - a2 * b1; - if (denom === 0) { - return /*COLLINEAR*/; - } - - offset = Math.abs(denom / 2); - - // The denom/2 is to get rounding instead of truncating. It - // is added or subtracted to the numerator, depending upon the - // sign of the numerator. - num = b1 * c2 - b2 * c1; - x = num < 0 ? (num - offset) / denom : (num + offset) / denom; - - num = a2 * c1 - a1 * c2; - y = num < 0 ? (num - offset) / denom : (num + offset) / denom; - - return { x: x, y: y }; } function sameSign(r1, r2) { diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts index 829f89a8f..2509dead4 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts @@ -61,6 +61,10 @@ import { erBox } from './shapes/erBox.js'; import { classBox } from './shapes/classBox.js'; import { requirementBox } from './shapes/requirementBox.js'; import { kanbanItem } from './shapes/kanbanItem.js'; +import { bang } from './shapes/bang.js'; +import { cloud } from './shapes/cloud.js'; +import { defaultMindmapNode } from './shapes/defaultMindmapNode.js'; +import { mindmapCircle } from './shapes/mindmapCircle.js'; type ShapeHandler = ( parent: D3Selection, @@ -135,6 +139,22 @@ export const shapesDefs = [ aliases: ['circ'], handler: circle, }, + { + semanticName: 'Bang', + name: 'Bang', + shortName: 'bang', + description: 'Bang', + aliases: ['bang'], + handler: bang, + }, + { + semanticName: 'Cloud', + name: 'Cloud', + shortName: 'cloud', + description: 'cloud', + aliases: ['cloud'], + handler: cloud, + }, { semanticName: 'Decision', name: 'Diamond', @@ -476,6 +496,9 @@ const generateShapeMap = () => { // Kanban diagram kanbanItem, + //Mindmap diagram + mindmapCircle, + defaultMindmapNode, // class diagram classBox, diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/bang.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/bang.ts new file mode 100644 index 000000000..bfc8896a5 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/bang.ts @@ -0,0 +1,81 @@ +import { log } from '../../../logger.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; +import intersect from '../intersect/index.js'; +import type { Node } from '../../types.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; +import { handleUndefinedAttr } from '../../../utils.js'; +import type { Bounds, Point } from '../../../types.js'; + +export async function bang(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + const { shapeSvg, bbox, halfPadding, label } = await labelHelper( + parent, + node, + getNodeClasses(node) + ); + + const w = bbox.width + 10 * halfPadding; + const h = bbox.height + 8 * halfPadding; + const r = 0.15 * w; + const { cssStyles } = node; + + const minWidth = bbox.width + 20; + const minHeight = bbox.height + 20; + const effectiveWidth = Math.max(w, minWidth); + const effectiveHeight = Math.max(h, minHeight); + + label.attr('transform', `translate(${-bbox.width / 2}, ${-bbox.height / 2})`); + + let bangElem; + const path = `M0 0 + a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${-1 * effectiveHeight * 0.1} + a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${0} + a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${0} + a${r},${r} 1 0,0 ${effectiveWidth * 0.25},${effectiveHeight * 0.1} + + a${r},${r} 1 0,0 ${effectiveWidth * 0.15},${effectiveHeight * 0.33} + a${r * 0.8},${r * 0.8} 1 0,0 0,${effectiveHeight * 0.34} + a${r},${r} 1 0,0 ${-1 * effectiveWidth * 0.15},${effectiveHeight * 0.33} + + a${r},${r} 1 0,0 ${-1 * effectiveWidth * 0.25},${effectiveHeight * 0.15} + a${r},${r} 1 0,0 ${-1 * effectiveWidth * 0.25},0 + a${r},${r} 1 0,0 ${-1 * effectiveWidth * 0.25},0 + a${r},${r} 1 0,0 ${-1 * effectiveWidth * 0.25},${-1 * effectiveHeight * 0.15} + + a${r},${r} 1 0,0 ${-1 * effectiveWidth * 0.1},${-1 * effectiveHeight * 0.33} + a${r * 0.8},${r * 0.8} 1 0,0 0,${-1 * effectiveHeight * 0.34} + a${r},${r} 1 0,0 ${effectiveWidth * 0.1},${-1 * effectiveHeight * 0.33} + H0 V0 Z`; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const roughNode = rc.path(path, options); + bangElem = shapeSvg.insert(() => roughNode, ':first-child'); + bangElem.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); + } else { + bangElem = shapeSvg + .insert('path', ':first-child') + .attr('class', 'basic label-container') + .attr('style', nodeStyles) + .attr('d', path); + } + + // Translate the path (center the shape) + bangElem.attr('transform', `translate(${-effectiveWidth / 2}, ${-effectiveHeight / 2})`); + + updateNodeBounds(node, bangElem); + node.calcIntersect = function (bounds: Bounds, point: Point) { + return intersect.rect(bounds, point); + }; + node.intersect = function (point) { + log.info('Bang intersect', node, point); + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts index 6b3be6765..57f72fd2d 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts @@ -1,18 +1,22 @@ -import { log } from '../../../logger.js'; -import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; -import intersect from '../intersect/index.js'; -import type { Node } from '../../types.js'; -import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; -import type { D3Selection } from '../../../types.js'; +import { log } from '../../../logger.js'; +import type { Bounds, D3Selection, Point } from '../../../types.js'; import { handleUndefinedAttr } from '../../../utils.js'; +import type { MindmapOptions, Node, ShapeRenderOptions } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; +import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js'; -export async function circle(parent: D3Selection, node: Node) { +export async function circle( + parent: D3Selection, + node: Node, + options?: MindmapOptions | ShapeRenderOptions +) { const { labelStyles, nodeStyles } = styles2String(node); node.labelStyle = labelStyles; const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, getNodeClasses(node)); - - const radius = bbox.width / 2 + halfPadding; + const padding = options?.padding ?? halfPadding; + const radius = bbox.width / 2 + padding; let circleElem; const { cssStyles } = node; @@ -35,7 +39,10 @@ export async function circle(parent: D3Selection(parent: D3Selection, node: Node) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + + const { shapeSvg, bbox, halfPadding, label } = await labelHelper( + parent, + node, + getNodeClasses(node) + ); + + const w = bbox.width + 2 * halfPadding; + const h = bbox.height + 2 * halfPadding; + + // Cloud radii + const r1 = 0.15 * w; + const r2 = 0.25 * w; + const r3 = 0.35 * w; + const r4 = 0.2 * w; + + const { cssStyles } = node; + let cloudElem; + + // Cloud path + const path = `M0 0 + a${r1},${r1} 0 0,1 ${w * 0.25},${-1 * w * 0.1} + a${r3},${r3} 1 0,1 ${w * 0.4},${-1 * w * 0.1} + a${r2},${r2} 1 0,1 ${w * 0.35},${w * 0.2} + + a${r1},${r1} 1 0,1 ${w * 0.15},${h * 0.35} + a${r4},${r4} 1 0,1 ${-1 * w * 0.15},${h * 0.65} + + a${r2},${r1} 1 0,1 ${-1 * w * 0.25},${w * 0.15} + a${r3},${r3} 1 0,1 ${-1 * w * 0.5},0 + a${r1},${r1} 1 0,1 ${-1 * w * 0.25},${-1 * w * 0.15} + + a${r1},${r1} 1 0,1 ${-1 * w * 0.1},${-1 * h * 0.35} + a${r4},${r4} 1 0,1 ${w * 0.1},${-1 * h * 0.65} + H0 V0 Z`; + + if (node.look === 'handDrawn') { + // @ts-expect-error -- Passing a D3.Selection seems to work for some reason + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const roughNode = rc.path(path, options); + cloudElem = shapeSvg.insert(() => roughNode, ':first-child'); + cloudElem.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles)); + } else { + cloudElem = shapeSvg + .insert('path', ':first-child') + .attr('class', 'basic label-container') + .attr('style', nodeStyles) + .attr('d', path); + } + + label.attr('transform', `translate(${-bbox.width / 2}, ${-bbox.height / 2})`); + + // Center the shape + cloudElem.attr('transform', `translate(${-w / 2}, ${-h / 2})`); + + updateNodeBounds(node, cloudElem); + + node.calcIntersect = function (bounds: Bounds, point: Point) { + return intersect.rect(bounds, point); + }; + node.intersect = function (point) { + log.info('Cloud intersect', node, point); + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/defaultMindmapNode.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/defaultMindmapNode.ts new file mode 100644 index 000000000..f30c80844 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/defaultMindmapNode.ts @@ -0,0 +1,64 @@ +import type { Bounds, D3Selection, Point } from '../../../types.js'; +import type { Node } from '../../types.js'; +import intersect from '../intersect/index.js'; +import { styles2String } from './handDrawnShapeStyles.js'; +import { getNodeClasses, labelHelper, updateNodeBounds } from './util.js'; + +export async function defaultMindmapNode( + parent: D3Selection, + node: Node +) { + const { labelStyles, nodeStyles } = styles2String(node); + node.labelStyle = labelStyles; + + const { shapeSvg, bbox, halfPadding, label } = await labelHelper( + parent, + node, + getNodeClasses(node) + ); + + const w = bbox.width + 8 * halfPadding; + const h = bbox.height + 2 * halfPadding; + const rd = 5; + + const rectPath = ` + M${-w / 2} ${h / 2 - rd} + v${-h + 2 * rd} + q0,-${rd} ${rd},-${rd} + h${w - 2 * rd} + q${rd},0 ${rd},${rd} + v${h - 2 * rd} + q0,${rd} -${rd},${rd} + h${-w + 2 * rd} + q-${rd},0 -${rd},-${rd} + Z + `; + + const bg = shapeSvg + .append('path') + .attr('id', 'node-' + node.id) + .attr('class', 'node-bkg node-' + node.type) + .attr('style', nodeStyles) + .attr('d', rectPath); + + shapeSvg + .append('line') + .attr('class', 'node-line-') + .attr('x1', -w / 2) + .attr('y1', h / 2) + .attr('x2', w / 2) + .attr('y2', h / 2); + + label.attr('transform', `translate(${-bbox.width / 2}, ${-bbox.height / 2})`); + shapeSvg.append(() => label.node()); + + updateNodeBounds(node, bg); + node.calcIntersect = function (bounds: Bounds, point: Point) { + return intersect.rect(bounds, point); + }; + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts index 707aed2c7..8f70f82fc 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts @@ -6,6 +6,7 @@ import { userNodeOverrides, styles2String } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import type { D3Selection } from '../../../types.js'; import { handleUndefinedAttr } from '../../../utils.js'; +import type { Bounds, Point } from '../../../types.js'; export async function drawRect( parent: D3Selection, @@ -62,6 +63,10 @@ export async function drawRect( updateNodeBounds(node, rect); + node.calcIntersect = function (bounds: Bounds, point: Point) { + return intersect.rect(bounds, point); + }; + node.intersect = function (point) { return intersect.rect(node, point); }; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.ts index 16a201e14..25ddff46b 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handDrawnShapeStyles.ts @@ -20,7 +20,11 @@ export const compileStyles = (node: Node) => { // the array is the styles of node from the classes it is using // node.cssStyles is an array of styles directly set on the node // concat the arrays and remove duplicates such that the values from node.cssStyles are used if there are duplicates - const stylesMap = styles2Map([...(node.cssCompiledStyles || []), ...(node.cssStyles || [])]); + const stylesMap = styles2Map([ + ...(node.cssCompiledStyles || []), + ...(node.cssStyles || []), + ...(node.labelStyle || []), + ]); return { stylesMap, stylesArray: [...stylesMap] }; }; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/mindmapCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/mindmapCircle.ts new file mode 100644 index 000000000..5b9dab3fd --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/mindmapCircle.ts @@ -0,0 +1,13 @@ +import { circle } from './circle.js'; +import type { Node, MindmapOptions } from '../../types.js'; +import type { D3Selection } from '../../../types.js'; + +export async function mindmapCircle( + parent: D3Selection, + node: Node +) { + const options = { + padding: node.padding ?? 0, + } as MindmapOptions; + return circle(parent, node, options); +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts index 24c811b85..87adc4814 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts @@ -1,4 +1,3 @@ -import { log } from '../../../logger.js'; import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; import type { Node } from '../../types.js'; @@ -6,6 +5,7 @@ import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import { insertPolygonShape } from './insertPolygonShape.js'; import type { D3Selection } from '../../../types.js'; +import type { Bounds, Point } from '../../../types.js'; export const createDecisionBoxPathD = (x: number, y: number, size: number): string => { return [ @@ -61,17 +61,26 @@ export async function question(parent: D3Selection } updateNodeBounds(node, polygon); + node.calcIntersect = function (bounds: Bounds, point: Point) { + const s = bounds.width; + + // Define polygon points + const points = [ + { x: s / 2, y: 0 }, + { x: s, y: -s / 2 }, + { x: s / 2, y: -s }, + { x: 0, y: -s / 2 }, + ]; + + // Calculate the intersection point + const res = intersect.polygon(bounds, points, point); + + return { x: res.x - 0.5, y: res.y - 0.5 }; // Adjusted result + }; node.intersect = function (point) { - log.debug( - 'APA12 Intersect called SPLIT\npoint:', - point, - '\nnode:\n', - node, - '\nres:', - intersect.polygon(node, points, point) - ); - return intersect.polygon(node, points, point); + // @ts-ignore TODO fix this (KNSV) + return this.calcIntersect(node as Bounds, point); }; return shapeSvg; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts index 40d71429c..2b8f03d92 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts @@ -98,18 +98,19 @@ export async function roundedRect( const w = (node?.width ? node?.width : bbox.width) + labelPaddingX * 2; const h = (node?.height ? node?.height : bbox.height) + labelPaddingY * 2; - const radius = 5; - const taper = 5; // Taper width for the rounded corners + const radius = node.radius || 5; + const taper = node.taper || 5; // Taper width for the rounded corners const { cssStyles } = node; // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); const options = userNodeOverrides(node, {}); - + if (node.stroke) { + options.stroke = node.stroke; + } if (node.look !== 'handDrawn') { options.roughness = 0; options.fillStyle = 'solid'; } - const points = [ // Top edge (left to right) { x: -w / 2 + taper, y: -h / 2 }, // Top-left corner start (1) diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts index af72a798f..0e5ec730e 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts @@ -7,7 +7,7 @@ export async function squareRect(parent: D3Selecti rx: 0, ry: 0, classes: '', - labelPaddingX: (node?.padding || 0) * 2, + labelPaddingX: node.labelPaddingX ?? (node?.padding || 0) * 2, labelPaddingY: (node?.padding || 0) * 1, } as RectOptions; return drawRect(parent, node, options); diff --git a/packages/mermaid/src/rendering-util/types.ts b/packages/mermaid/src/rendering-util/types.ts index b11d2f314..c8439b534 100644 --- a/packages/mermaid/src/rendering-util/types.ts +++ b/packages/mermaid/src/rendering-util/types.ts @@ -2,6 +2,7 @@ export type MarkdownWordType = 'normal' | 'strong' | 'em'; import type { MermaidConfig } from '../config.type.js'; import type { ClusterShapeID } from './rendering-elements/clusters.js'; import type { ShapeID } from './rendering-elements/shapes.js'; +import type { Bounds, Point } from '../types.js'; export interface MarkdownWord { content: string; type: MarkdownWordType; @@ -38,11 +39,12 @@ interface BaseNode { linkTarget?: string; tooltip?: string; padding?: number; //REMOVE?, use from LayoutData.config - Keep, this could be shape specific - isGroup: boolean; + isGroup?: boolean; width?: number; height?: number; // Specific properties for State Diagram nodes TODO remove and use generic properties intersect?: (point: any) => any; + calcIntersect?: (bounds: Bounds, point: Point) => any; // Non-generic properties rx?: number; // Used for rounded corners in Rect, Ellipse, etc.Maybe it to specialized RectNode, EllipseNode, etc. @@ -58,6 +60,8 @@ interface BaseNode { borderStyle?: string; borderWidth?: number; labelTextColor?: string; + labelPaddingX?: number; + labelPaddingY?: number; // Flowchart specific properties x?: number; @@ -72,16 +76,25 @@ interface BaseNode { defaultWidth?: number; imageAspectRatio?: number; constraint?: 'on' | 'off'; + children?: NodeChildren; + nodeId?: string; + level?: number; + descr?: string; + type?: number; + radius?: number; + taper?: number; + stroke?: string; } /** * Group/cluster nodes, e.g. nodes that contain other nodes. */ +export type NodeChildren = Node[]; + export interface ClusterNode extends BaseNode { shape?: ClusterShapeID; isGroup: true; } - export interface NonClusterNode extends BaseNode { shape?: ShapeID; isGroup: false; @@ -113,7 +126,7 @@ export interface Edge { start?: string; stroke?: string; text?: string; - type: string; + type?: string; // Class Diagram specific properties startLabelRight?: string; endLabelLeft?: string; @@ -126,6 +139,12 @@ export interface Edge { thickness?: 'normal' | 'thick' | 'invisible' | 'dotted'; look?: string; isUserDefinedId?: boolean; + points?: Point[]; + parentId?: string; + dir?: string; + source?: string; + target?: string; + depth?: number; } export interface RectOptions { @@ -136,6 +155,10 @@ export interface RectOptions { classes: string; } +export interface MindmapOptions { + padding: number; +} + // Extending the Node interface for specific types if needed export type ClassDiagramNode = Node & { memberData: any; // Specific property for class diagram nodes @@ -171,6 +194,7 @@ export interface ShapeRenderOptions { config: MermaidConfig; /** Some shapes render differently if a diagram has a direction `LR` */ dir?: Node['dir']; + padding?: number; } export type KanbanNode = Node & { diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index 0ff385c61..4b75c9704 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -977,6 +977,7 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) - useMaxWidth - padding - maxNodeWidth + - layoutAlgorithm properties: padding: type: number @@ -984,6 +985,10 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) maxNodeWidth: type: number default: 200 + layoutAlgorithm: + description: Layout algorithm to use for positioning mindmap nodes + type: string + default: 'cose-bilkent' KanbanDiagramConfig: title: Kanban Diagram Config diff --git a/packages/mermaid/src/types.ts b/packages/mermaid/src/types.ts index d1394e71b..b4d833ccf 100644 --- a/packages/mermaid/src/types.ts +++ b/packages/mermaid/src/types.ts @@ -36,6 +36,10 @@ export interface Point { x: number; y: number; } +export interface Bounds extends Point { + width: number; + height: number; +} export interface TextDimensionConfig { fontSize?: number; diff --git a/packages/mermaid/src/types/cytoscape-cose-bilkent.d.ts b/packages/mermaid/src/types/cytoscape-cose-bilkent.d.ts new file mode 100644 index 000000000..6e2930a47 --- /dev/null +++ b/packages/mermaid/src/types/cytoscape-cose-bilkent.d.ts @@ -0,0 +1,4 @@ +declare module 'cytoscape-cose-bilkent' { + const coseBilkent: any; + export default coseBilkent; +} diff --git a/packages/mermaid/src/utils/lineWithOffset.ts b/packages/mermaid/src/utils/lineWithOffset.ts index 800a5ffaf..e5d9b41f4 100644 --- a/packages/mermaid/src/utils/lineWithOffset.ts +++ b/packages/mermaid/src/utils/lineWithOffset.ts @@ -3,13 +3,23 @@ import type { EdgeData, Point } from '../types.js'; // We need to draw the lines a bit shorter to avoid drawing // under any transparent markers. // The offsets are calculated from the markers' dimensions. -const markerOffsets = { - aggregation: 18, - extension: 18, - composition: 18, +export const markerOffsets = { + aggregation: 17.25, + extension: 17.25, + composition: 17.25, dependency: 6, lollipop: 13.5, arrow_point: 4, + //arrow_cross: 24, +} as const; + +// We need to draw the lines a bit shorter to avoid drawing +// under any transparent markers. +// The offsets are calculated from the markers' dimensions. +export const markerOffsets2 = { + arrow_point: 9, + arrow_cross: 12.5, + arrow_circle: 12.5, } as const; /** diff --git a/packages/parser/README.md b/packages/parser/README.md index 0a1ef04ed..07ef5a5b4 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -59,5 +59,4 @@ ValueConverter -->> Package: Return AST - To insert or modify attributes that can't be parsed. - When to override `ValueConverter`? - - To modify the returned value from the parser. diff --git a/packages/tiny/CHANGELOG.md b/packages/tiny/CHANGELOG.md index df67f0cfd..fc2f97fdf 100644 --- a/packages/tiny/CHANGELOG.md +++ b/packages/tiny/CHANGELOG.md @@ -229,7 +229,6 @@ - [#5999](https://github.com/mermaid-js/mermaid/pull/5999) [`742ad7c`](https://github.com/mermaid-js/mermaid/commit/742ad7c130964df1fb5544e909d9556081285f68) Thanks [@knsv](https://github.com/knsv)! - Adding Kanban board, a new diagram type - [#5880](https://github.com/mermaid-js/mermaid/pull/5880) [`bdf145f`](https://github.com/mermaid-js/mermaid/commit/bdf145ffe362462176d9c1e68d5f3ff5c9d962b0) Thanks [@yari-dewalt](https://github.com/yari-dewalt)! - Class diagram changes: - - Updates the class diagram to the new unified way of rendering. - Includes a new "classBox" shape to be used in diagrams - Other updates such as: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8be2d5aca..76b95f3a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,7 +36,7 @@ importers: version: 9.26.0 '@rollup/plugin-typescript': specifier: ^12.1.2 - version: 12.1.2(rollup@4.40.2)(tslib@2.8.1)(typescript@5.7.3) + version: 12.1.2(rollup@4.50.1)(tslib@2.8.1)(typescript@5.7.3) '@types/cors': specifier: ^2.8.17 version: 2.8.17 @@ -60,7 +60,7 @@ importers: version: 22.13.5 '@types/rollup-plugin-visualizer': specifier: ^5.0.3 - version: 5.0.3(rollup@4.40.2) + version: 5.0.3(rollup@4.50.1) '@vitest/coverage-v8': specifier: ^3.0.6 version: 3.0.6(vitest@3.0.6) @@ -189,7 +189,7 @@ importers: version: 6.0.1 rollup-plugin-visualizer: specifier: ^6.0.3 - version: 6.0.3(rollup@4.40.2) + version: 6.0.3(rollup@4.50.1) start-server-and-test: specifier: ^2.0.10 version: 2.0.10 @@ -207,13 +207,13 @@ importers: version: 8.38.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3) vite: specifier: ^7.0.3 - version: 7.0.3(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + version: 7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) vite-plugin-istanbul: specifier: ^7.0.0 - version: 7.0.0(vite@7.0.3(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)) + version: 7.0.0(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0)) vitest: specifier: ^3.0.6 - version: 3.0.6(@types/debug@4.1.12)(@types/node@22.13.5)(@vitest/ui@3.0.6)(jiti@2.4.2)(jsdom@26.1.0(canvas@3.1.2))(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + version: 3.0.6(@types/debug@4.1.12)(@types/node@22.13.5)(@vitest/ui@3.0.6)(jiti@2.4.2)(jsdom@26.1.0(canvas@3.1.2))(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) packages/examples: devDependencies: @@ -227,8 +227,8 @@ importers: specifier: ^7.0.4 version: 7.1.0 '@iconify/utils': - specifier: ^2.1.33 - version: 2.3.0 + specifier: ^3.0.1 + version: 3.0.1 '@mermaid-js/parser': specifier: workspace:^ version: link:../parser @@ -270,7 +270,7 @@ importers: version: 4.17.21 marked: specifier: ^16.0.0 - version: 16.0.0 + version: 16.2.1 roughjs: specifier: ^4.6.6 version: 4.6.6(patch_hash=3543d47108cb41b68ec6a671c0e1f9d0cfe2ce524fea5b0992511ae84c3c6b64) @@ -387,11 +387,11 @@ importers: specifier: ^4.35.0 version: 4.35.0 typedoc: - specifier: ^0.27.8 - version: 0.27.8(typescript@5.7.3) + specifier: ^0.28.9 + version: 0.28.11(typescript@5.7.3) typedoc-plugin-markdown: - specifier: ^4.4.2 - version: 4.4.2(typedoc@0.27.8(typescript@5.7.3)) + specifier: ^4.8.0 + version: 4.8.1(typedoc@0.28.11(typescript@5.7.3)) typescript: specifier: ~5.7.3 version: 5.7.3 @@ -403,10 +403,10 @@ importers: version: 5.0.0 vitepress: specifier: ^1.0.2 - version: 1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.39.0)(typescript@5.7.3) + version: 1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.44.0)(typescript@5.7.3) vitepress-plugin-search: specifier: 1.0.4-alpha.22 - version: 1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.39.0)(typescript@5.7.3))(vue@3.5.13(typescript@5.7.3)) + version: 1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.44.0)(typescript@5.7.3))(vue@3.5.13(typescript@5.7.3)) packages/mermaid-example-diagram: dependencies: @@ -446,6 +446,22 @@ importers: specifier: workspace:^ version: link:../mermaid + packages/mermaid-layout-tidy-tree: + dependencies: + d3: + specifier: ^7.9.0 + version: 7.9.0 + non-layered-tidy-tree-layout: + specifier: ^2.0.2 + version: 2.0.2 + devDependencies: + '@types/d3': + specifier: ^7.4.3 + version: 7.4.3 + mermaid: + specifier: workspace:^ + version: link:../mermaid + packages/mermaid-zenuml: dependencies: '@zenuml/core': @@ -485,10 +501,10 @@ importers: version: 66.0.0 '@vite-pwa/vitepress': specifier: ^1.0.0 - version: 1.0.0(vite-plugin-pwa@1.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0)) + version: 1.0.0(vite-plugin-pwa@1.0.0(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0)) '@vitejs/plugin-vue': specifier: ^6.0.0 - version: 6.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3)) + version: 6.0.0(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3)) fast-glob: specifier: ^3.3.3 version: 3.3.3 @@ -499,20 +515,20 @@ importers: specifier: ^2.0.3 version: 2.0.3 unocss: - specifier: ^66.0.0 - version: 66.0.0(postcss@8.5.6)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3)) + specifier: ^66.4.2 + version: 66.4.2(postcss@8.5.6)(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0)) unplugin-vue-components: specifier: ^28.4.0 - version: 28.4.0(@babel/parser@7.28.0)(vue@3.5.13(typescript@5.7.3)) + version: 28.4.0(@babel/parser@7.28.4)(vue@3.5.13(typescript@5.7.3)) vite: - specifier: ^6.1.1 - version: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + specifier: ^7.0.0 + version: 7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) vite-plugin-pwa: specifier: ^1.0.0 - version: 1.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0) + version: 1.0.0(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0) vitepress: specifier: 1.6.3 - version: 1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.39.0)(typescript@5.7.3) + version: 1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.44.0)(typescript@5.7.3) workbox-window: specifier: ^7.3.0 version: 7.3.0 @@ -647,11 +663,11 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@antfu/install-pkg@1.0.0': - resolution: {integrity: sha512-xvX6P/lo1B3ej0OsaErAjqgFYzYVcJpamjLAFLYh9vRJngBrMoUG7aVnrGTeqM7yxbyTD5p3F2+0/QUEh8Vzhw==} + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@antfu/utils@8.1.1': - resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} + '@antfu/utils@9.2.0': + resolution: {integrity: sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==} '@apideck/better-ajv-errors@0.3.6': resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} @@ -812,6 +828,10 @@ packages: resolution: {integrity: sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + engines: {node: '>=6.9.0'} + '@babel/core@7.27.1': resolution: {integrity: sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==} engines: {node: '>=6.9.0'} @@ -820,6 +840,10 @@ packages: resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} engines: {node: '>=6.9.0'} + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.27.1': resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} engines: {node: '>=6.9.0'} @@ -828,16 +852,20 @@ packages: resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.27.1': - resolution: {integrity: sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==} + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.27.1': - resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==} + '@babel/helper-create-class-features-plugin@7.28.3': + resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -848,8 +876,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-define-polyfill-provider@0.6.4': - resolution: {integrity: sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==} + '@babel/helper-define-polyfill-provider@0.6.5': + resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -877,6 +905,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.27.1': resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} @@ -917,8 +951,8 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.27.1': - resolution: {integrity: sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==} + '@babel/helper-wrap-function@7.28.3': + resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==} engines: {node: '>=6.9.0'} '@babel/helpers@7.27.1': @@ -929,6 +963,10 @@ packages: resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.26.9': resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} engines: {node: '>=6.0.0'} @@ -944,6 +982,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} engines: {node: '>=6.9.0'} @@ -968,8 +1011,8 @@ packages: peerDependencies: '@babel/core': ^7.13.0 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1': - resolution: {integrity: sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3': + resolution: {integrity: sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1095,8 +1138,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.27.1': - resolution: {integrity: sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==} + '@babel/plugin-transform-async-generator-functions@7.28.0': + resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1113,8 +1156,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.27.1': - resolution: {integrity: sha512-QEcFlMl9nGTgh1rn2nIeU5bkfb9BAjaQcWbiP4LvKxUot52ABcTkpcyJ7f2Q2U2RuQ84BNLgts3jRme2dTx6Fw==} + '@babel/plugin-transform-block-scoping@7.28.4': + resolution: {integrity: sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1125,14 +1168,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-static-block@7.27.1': - resolution: {integrity: sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==} + '@babel/plugin-transform-class-static-block@7.28.3': + resolution: {integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.27.1': - resolution: {integrity: sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==} + '@babel/plugin-transform-classes@7.28.4': + resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1143,8 +1186,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.27.1': - resolution: {integrity: sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q==} + '@babel/plugin-transform-destructuring@7.28.0': + resolution: {integrity: sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1173,6 +1216,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-explicit-resource-management@7.28.0': + resolution: {integrity: sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-exponentiation-operator@7.27.1': resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} engines: {node: '>=6.9.0'} @@ -1269,8 +1318,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.27.2': - resolution: {integrity: sha512-AIUHD7xJ1mCrj3uPozvtngY3s0xpv7Nu7DoUSnzNY6Xam1Cy4rUznR//pvMHOhQ4AvbCexhbqXCtpxGHOGOO6g==} + '@babel/plugin-transform-object-rest-spread@7.28.4': + resolution: {integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1293,8 +1342,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-parameters@7.27.1': - resolution: {integrity: sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==} + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1317,8 +1366,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.27.1': - resolution: {integrity: sha512-B19lbbL7PMrKr52BNPjCqg1IyNUIjTcxKj8uX9zHO+PmWN93s19NDr/f69mIkEp2x9nmDJ08a7lgHaTTzvW7mw==} + '@babel/plugin-transform-regenerator@7.28.4': + resolution: {integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1395,6 +1444,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/preset-env@7.28.3': + resolution: {integrity: sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/preset-modules@0.1.6-no-external-plugins': resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: @@ -1404,8 +1459,8 @@ packages: resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.27.1': - resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} '@babel/template@7.27.2': @@ -1420,6 +1475,10 @@ packages: resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} + '@babel/types@7.26.9': resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} engines: {node: '>=6.9.0'} @@ -1432,6 +1491,10 @@ packages: resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -1887,14 +1950,14 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.24.2': - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + '@esbuild/aix-ppc64@0.25.0': + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.0': - resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -1905,14 +1968,14 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.24.2': - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + '@esbuild/android-arm64@0.25.0': + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.0': - resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -1923,14 +1986,14 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.24.2': - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + '@esbuild/android-arm@0.25.0': + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.0': - resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -1941,14 +2004,14 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.24.2': - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + '@esbuild/android-x64@0.25.0': + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.0': - resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -1959,14 +2022,14 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.24.2': - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + '@esbuild/darwin-arm64@0.25.0': + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.0': - resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -1977,14 +2040,14 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.24.2': - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + '@esbuild/darwin-x64@0.25.0': + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.0': - resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -1995,14 +2058,14 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.24.2': - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + '@esbuild/freebsd-arm64@0.25.0': + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.0': - resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -2013,14 +2076,14 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.2': - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + '@esbuild/freebsd-x64@0.25.0': + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.0': - resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -2031,14 +2094,14 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.24.2': - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + '@esbuild/linux-arm64@0.25.0': + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.0': - resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -2049,14 +2112,14 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.24.2': - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + '@esbuild/linux-arm@0.25.0': + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.0': - resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -2067,14 +2130,14 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.24.2': - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + '@esbuild/linux-ia32@0.25.0': + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.0': - resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -2085,14 +2148,14 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.24.2': - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + '@esbuild/linux-loong64@0.25.0': + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.0': - resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -2103,14 +2166,14 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.24.2': - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + '@esbuild/linux-mips64el@0.25.0': + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.0': - resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -2121,14 +2184,14 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.24.2': - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + '@esbuild/linux-ppc64@0.25.0': + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.0': - resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -2139,14 +2202,14 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.24.2': - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + '@esbuild/linux-riscv64@0.25.0': + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.0': - resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -2157,14 +2220,14 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.24.2': - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + '@esbuild/linux-s390x@0.25.0': + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.0': - resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -2175,26 +2238,26 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.24.2': - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.25.0': resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.24.2': - resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.0': + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.25.0': - resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -2205,26 +2268,26 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.2': - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.25.0': resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.24.2': - resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.0': + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.25.0': - resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -2235,32 +2298,38 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.2': - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.25.0': resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.24.2': - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + '@esbuild/sunos-x64@0.25.0': + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.0': - resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -2271,14 +2340,14 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.24.2': - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + '@esbuild/win32-arm64@0.25.0': + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.0': - resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -2289,14 +2358,14 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.24.2': - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + '@esbuild/win32-ia32@0.25.0': + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.0': - resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -2307,14 +2376,14 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.24.2': - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + '@esbuild/win32-x64@0.25.0': + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.0': - resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2409,8 +2478,8 @@ packages: '@floating-ui/utils@0.2.9': resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} - '@gerrit0/mini-shiki@1.27.2': - resolution: {integrity: sha512-GeWyHz8ao2gBiUW4OJnQDxXQnFgZQwwQk05t/CVVgNBN7/rK8XZ7xY6YhLVv9tH3VppWWmr9DCl3MwemB/i+Og==} + '@gerrit0/mini-shiki@3.12.0': + resolution: {integrity: sha512-CF1vkfe2ViPtmoFEvtUWilEc4dOCiFzV8+J7/vEISSsslKQ97FjeTPNMCqUhZEiKySmKRgK3UO/CxtkyOp7DvA==} '@hapi/hoek@9.3.0': resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} @@ -2460,8 +2529,8 @@ packages: '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@iconify/utils@2.3.0': - resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==} + '@iconify/utils@3.0.1': + resolution: {integrity: sha512-A78CUEnFGX8I/WlILxJCuIJXloL0j/OJ9PSchPAfCargEIKmUBWvvEMmKWB5oONwiUqlNt+5eRufdkLxeHIWYw==} '@img/sharp-darwin-arm64@0.33.5': resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} @@ -2665,10 +2734,16 @@ packages: '@jridgewell/gen-mapping@0.3.12': resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -2677,18 +2752,27 @@ packages: resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + '@jridgewell/source-map@0.3.6': resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} @@ -2740,6 +2824,9 @@ packages: '@polka/url@1.0.0-next.28': resolution: {integrity: sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==} + '@quansync/fs@0.1.4': + resolution: {integrity: sha512-vy/41FCdnIalPTQCb2Wl0ic1caMdzGus4ktDp+gpZesQNydXcx8nhh8qB3qMPbGkictOTaXgXEUUfQEm8DQYoA==} + '@react-aria/focus@3.21.0': resolution: {integrity: sha512-7NEGtTPsBy52EZ/ToVKCu0HSelE3kq9qeis+2eEq90XSuJOMaDHUQrA7RC2Y89tlEwQB31bud/kKRi9Qme1dkA==} peerDependencies: @@ -2842,19 +2929,23 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.34.8': - resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} - cpu: [arm] - os: [android] + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true '@rollup/rollup-android-arm-eabi@4.40.2': resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.34.8': - resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} - cpu: [arm64] + '@rollup/rollup-android-arm-eabi@4.50.1': + resolution: {integrity: sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==} + cpu: [arm] os: [android] '@rollup/rollup-android-arm64@4.40.2': @@ -2862,19 +2953,19 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.34.8': - resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} + '@rollup/rollup-android-arm64@4.50.1': + resolution: {integrity: sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==} cpu: [arm64] - os: [darwin] + os: [android] '@rollup/rollup-darwin-arm64@4.40.2': resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.34.8': - resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} - cpu: [x64] + '@rollup/rollup-darwin-arm64@4.50.1': + resolution: {integrity: sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==} + cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-x64@4.40.2': @@ -2882,19 +2973,19 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.34.8': - resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} - cpu: [arm64] - os: [freebsd] + '@rollup/rollup-darwin-x64@4.50.1': + resolution: {integrity: sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==} + cpu: [x64] + os: [darwin] '@rollup/rollup-freebsd-arm64@4.40.2': resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.34.8': - resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} - cpu: [x64] + '@rollup/rollup-freebsd-arm64@4.50.1': + resolution: {integrity: sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==} + cpu: [arm64] os: [freebsd] '@rollup/rollup-freebsd-x64@4.40.2': @@ -2902,18 +2993,18 @@ packages: cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.34.8': - resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} - cpu: [arm] - os: [linux] + '@rollup/rollup-freebsd-x64@4.50.1': + resolution: {integrity: sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==} + cpu: [x64] + os: [freebsd] '@rollup/rollup-linux-arm-gnueabihf@4.40.2': resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.34.8': - resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': + resolution: {integrity: sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==} cpu: [arm] os: [linux] @@ -2922,9 +3013,9 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.34.8': - resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} - cpu: [arm64] + '@rollup/rollup-linux-arm-musleabihf@4.50.1': + resolution: {integrity: sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==} + cpu: [arm] os: [linux] '@rollup/rollup-linux-arm64-gnu@4.40.2': @@ -2932,8 +3023,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.34.8': - resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} + '@rollup/rollup-linux-arm64-gnu@4.50.1': + resolution: {integrity: sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==} cpu: [arm64] os: [linux] @@ -2942,9 +3033,9 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.34.8': - resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} - cpu: [loong64] + '@rollup/rollup-linux-arm64-musl@4.50.1': + resolution: {integrity: sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==} + cpu: [arm64] os: [linux] '@rollup/rollup-linux-loongarch64-gnu@4.40.2': @@ -2952,9 +3043,9 @@ packages: cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': - resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} - cpu: [ppc64] + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': + resolution: {integrity: sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==} + cpu: [loong64] os: [linux] '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': @@ -2962,9 +3053,9 @@ packages: cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.34.8': - resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} - cpu: [riscv64] + '@rollup/rollup-linux-ppc64-gnu@4.50.1': + resolution: {integrity: sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==} + cpu: [ppc64] os: [linux] '@rollup/rollup-linux-riscv64-gnu@4.40.2': @@ -2972,14 +3063,19 @@ packages: cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + resolution: {integrity: sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.40.2': resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.34.8': - resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} - cpu: [s390x] + '@rollup/rollup-linux-riscv64-musl@4.50.1': + resolution: {integrity: sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==} + cpu: [riscv64] os: [linux] '@rollup/rollup-linux-s390x-gnu@4.40.2': @@ -2987,9 +3083,9 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.34.8': - resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} - cpu: [x64] + '@rollup/rollup-linux-s390x-gnu@4.50.1': + resolution: {integrity: sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==} + cpu: [s390x] os: [linux] '@rollup/rollup-linux-x64-gnu@4.40.2': @@ -2997,8 +3093,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.34.8': - resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} + '@rollup/rollup-linux-x64-gnu@4.50.1': + resolution: {integrity: sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==} cpu: [x64] os: [linux] @@ -3007,19 +3103,24 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.34.8': - resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} + '@rollup/rollup-linux-x64-musl@4.50.1': + resolution: {integrity: sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.50.1': + resolution: {integrity: sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==} cpu: [arm64] - os: [win32] + os: [openharmony] '@rollup/rollup-win32-arm64-msvc@4.40.2': resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.34.8': - resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} - cpu: [ia32] + '@rollup/rollup-win32-arm64-msvc@4.50.1': + resolution: {integrity: sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==} + cpu: [arm64] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.40.2': @@ -3027,9 +3128,9 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.34.8': - resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} - cpu: [x64] + '@rollup/rollup-win32-ia32-msvc@4.50.1': + resolution: {integrity: sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==} + cpu: [ia32] os: [win32] '@rollup/rollup-win32-x64-msvc@4.40.2': @@ -3037,33 +3138,44 @@ packages: cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.50.1': + resolution: {integrity: sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==} + cpu: [x64] + os: [win32] + '@shikijs/core@2.5.0': resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==} '@shikijs/engine-javascript@2.5.0': resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==} - '@shikijs/engine-oniguruma@1.29.2': - resolution: {integrity: sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==} - '@shikijs/engine-oniguruma@2.5.0': resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} + '@shikijs/engine-oniguruma@3.12.0': + resolution: {integrity: sha512-IfDl3oXPbJ/Jr2K8mLeQVpnF+FxjAc7ZPDkgr38uEw/Bg3u638neSrpwqOTnTHXt1aU0Fk1/J+/RBdst1kVqLg==} + '@shikijs/langs@2.5.0': resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} + '@shikijs/langs@3.12.0': + resolution: {integrity: sha512-HIca0daEySJ8zuy9bdrtcBPhcYBo8wR1dyHk1vKrOuwDsITtZuQeGhEkcEfWc6IDyTcom7LRFCH6P7ljGSCEiQ==} + '@shikijs/themes@2.5.0': resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} + '@shikijs/themes@3.12.0': + resolution: {integrity: sha512-/lxvQxSI5s4qZLV/AuFaA4Wt61t/0Oka/P9Lmpr1UV+HydNCczO3DMHOC/CsXCCpbv4Zq8sMD0cDa7mvaVoj0Q==} + '@shikijs/transformers@2.5.0': resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} - '@shikijs/types@1.29.2': - resolution: {integrity: sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==} - '@shikijs/types@2.5.0': resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} + '@shikijs/types@3.12.0': + resolution: {integrity: sha512-jsFzm8hCeTINC3OCmTZdhR9DOl/foJWplH2Px0bTi4m8z59fnsueLsweX82oGcjRQ7mfQAluQYKGoH2VzsWY4A==} + '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -3272,6 +3384,9 @@ packages: '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/express-serve-static-core@4.19.6': resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} @@ -3538,88 +3653,94 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@unocss/astro@66.0.0': - resolution: {integrity: sha512-GBhXT6JPqXjDXoJZTXhySk83NgOt0UigChqrUUdG4x7Z+DVYkDBION8vZUJjw0OdIaxNQ4euGWu4GDsMF6gQQg==} + '@unocss/astro@66.4.2': + resolution: {integrity: sha512-En3AKHwkiPxtZT95vkVrNiRYrB+DFVCikew6/dMMCWDWVKK0+5tEVUTzR1ak3+YnzAXl0NpWj8D4zHb0PxOs/A==} peerDependencies: - vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 peerDependenciesMeta: vite: optional: true - '@unocss/cli@66.0.0': - resolution: {integrity: sha512-KVQiskoOjVkLVpNaG6WpLa4grPplrZROYZJVIUYSTqZyZRFNSvjttHcsCwpoWUEUdEombPtVZl8FrXePjY5IiQ==} + '@unocss/cli@66.4.2': + resolution: {integrity: sha512-WsXzrB0SHbSt2nOHtD5QM91VN8j38+wObqyGcoIhtBSugqzsc+t7AdPkxV/ZaYgtPAz87bR0WFEVKcbiBRnmJw==} engines: {node: '>=14'} hasBin: true - '@unocss/config@66.0.0': - resolution: {integrity: sha512-nFRGop/guBa4jLkrgXjaRDm5JPz4x3YpP10m5IQkHpHwlnHUVn1L9smyPl04ohYWhYn9ZcAHgR28Ih2jwta8hw==} + '@unocss/config@66.4.2': + resolution: {integrity: sha512-plji1gNGSzlWjuV2Uh0q6Dt5ZlNkOKCHpgxekW9J458WghGAMBeXgB9uNpWg6flilqP1g0GJQv+XvJcSkYRGpQ==} engines: {node: '>=14'} - '@unocss/core@66.0.0': - resolution: {integrity: sha512-PdVbSMHNDDkr++9nkqzsZRAkaU84gxMTEgYbqI7dt2p1DXp/5tomVtmMsr2/whXGYKRiUc0xZ3p4Pzraz8TcXA==} + '@unocss/core@66.4.2': + resolution: {integrity: sha512-cYgMQrLhB9nRekv5c+yPDDa+5dzlMkA2UMQRil0s5D9Lb5n7NsCMcr6+nfxkcSYVLy92SbwDV45c6T7vIxFTOA==} - '@unocss/extractor-arbitrary-variants@66.0.0': - resolution: {integrity: sha512-vlkOIOuwBfaFBJcN6o7+obXjigjOlzVFN/jT6pG1WXbQDTRZ021jeF3i9INdb9D/0cQHSeDvNgi1TJ5oUxfiow==} + '@unocss/extractor-arbitrary-variants@66.4.2': + resolution: {integrity: sha512-T/eSeodfAp7HaWnQGqVLOsW4PbKUAvuybNRyvFWThMneM2qo+dOo3kFnA5my9ULAmRSFsAlyB1DnupD3qv5Klg==} - '@unocss/inspector@66.0.0': - resolution: {integrity: sha512-mkIxieVm0kMOKw+E4ABpIerihYMdjgq9A92RD5h2+W/ebpxTEw5lTTK1xcMLiAlmOrVYMQKjpgPeu3vQmDyGZQ==} + '@unocss/inspector@66.4.2': + resolution: {integrity: sha512-ugcJK8r2ypM4eIdgetVn8RhfKrbA3AF3OQ/RohK5PPk2UPDAScqabzYpfdNW4eYQsBOZOgoiqWtnfc8weqo8LQ==} - '@unocss/postcss@66.0.0': - resolution: {integrity: sha512-6bi+ujzh8I1PJwtmHX71LH8z/H9+vPxeYD4XgFihyU1k4Y6MVhjr7giGjLX4yP27IP+NsVyotD22V7by/dBVEA==} + '@unocss/postcss@66.4.2': + resolution: {integrity: sha512-tu4lnh6K27pIAuaQHlFlhXin8korwC0r1kQl00YMmF3THiX7orXkTP6xWGcQwnkbx4uQz1dw+tBimYxeaAMrhA==} engines: {node: '>=14'} peerDependencies: postcss: ^8.4.21 - '@unocss/preset-attributify@66.0.0': - resolution: {integrity: sha512-eYsOgmcDoiIgGAepIwRX+DKGYxc/wm0r4JnDuZdz29AB+A6oY/FGHS1BVt4rq9ny4B5PofP4p6Rty+vwD9rigw==} + '@unocss/preset-attributify@66.4.2': + resolution: {integrity: sha512-DwFJJkkawmHpjo3pGQE8FyoPsvhbxh+QMvvaAdYpo+iZ5HRkeDml9SOj7u6SGTcmbNyI+QR61s0KM8fxx6HcVQ==} - '@unocss/preset-icons@66.0.0': - resolution: {integrity: sha512-6ObwTvEGuPBbKWRoMMiDioHtwwQTFI5oojFLJ32Y8tW6TdXvBLkO88d7qpgQxEjgVt4nJrqF1WEfR4niRgBm0Q==} + '@unocss/preset-icons@66.4.2': + resolution: {integrity: sha512-qJx9gmesrvrmoTe9Mqoidihad8hm2MSD4QAezhfDSAyllioJOgyT0Bev/IEWAbehe9jtqYIh8v1oCerBPbGn6Q==} - '@unocss/preset-mini@66.0.0': - resolution: {integrity: sha512-d62eACnuKtR0dwCFOQXgvw5VLh5YSyK56xCzpHkh0j0GstgfDLfKTys0T/XVAAvdSvAy/8A8vhSNJ4PlIc9V2A==} + '@unocss/preset-mini@66.4.2': + resolution: {integrity: sha512-Ry+5hM+XLmT8HrEb182mUfcZuyrZ8xR+TBe72DBcliJ1DhOV3K67TCxwQucfb0zHbGV71HNWdPmHsLKxPDgweQ==} - '@unocss/preset-tagify@66.0.0': - resolution: {integrity: sha512-GGYGyWxaevh0jN0NoATVO1Qe7DFXM3ykLxchlXmG6/zy963pZxItg/njrKnxE9la4seCdxpFH7wQBa68imwwdA==} + '@unocss/preset-tagify@66.4.2': + resolution: {integrity: sha512-dECS09LqWJY4sYpgPUH2OAUftWU/tiZPR2XDRoTngeGU37GxSN+1sWtSmB7vwDm3C7opsdVUN20he8F1LUNubw==} - '@unocss/preset-typography@66.0.0': - resolution: {integrity: sha512-apjckP5nPU5mtaHTCzz5u/dK9KJWwJ2kOFCVk0+a/KhUWmnqnzmjRYZlEuWxxr5QxTdCW+9cIoRDSA0lYZS5tg==} + '@unocss/preset-typography@66.4.2': + resolution: {integrity: sha512-ZOKRuR5+V0r30QTVq04/6ZoIw75me3V25v2dU2YWJXIzwpMKmQ9TUN/M1yeiEUFfXjOaruWX6Ad6CvAw2MlCew==} - '@unocss/preset-uno@66.0.0': - resolution: {integrity: sha512-qgoZ/hzTI32bQvcyjcwvv1X/dbPlmQNehzgjUaL7QFT0q0/CN/SRpysfzoQ8DLl2se9T+YCOS9POx3KrpIiYSQ==} + '@unocss/preset-uno@66.4.2': + resolution: {integrity: sha512-1MFtPivGcpqRQFWdjtP40Enop1y3XDb3tlZXoMQUX0IGLG8HJOT+lfQx/Xl9t73ShJ8aAJ/l6qTxC43ZGNACzA==} - '@unocss/preset-web-fonts@66.0.0': - resolution: {integrity: sha512-9MzfDc6AJILN4Kq7Z91FfFbizBOYgw3lJd2UwqIs3PDYWG5iH5Zv5zhx6jelZVqEW5uWcIARYEEg2m4stZO1ZA==} + '@unocss/preset-web-fonts@66.4.2': + resolution: {integrity: sha512-4FYmleeRoM8r2DqGl6dfIjnX57tepcfZCvVfeCqYnk7475Yddmv1OYkoMjkWMnkK9MzdSxsFwHMU6CIUTmFTzQ==} - '@unocss/preset-wind3@66.0.0': - resolution: {integrity: sha512-WAGRmpi1sb2skvYn9DBQUvhfqrJ+VmQmn5ZGsT2ewvsk7HFCvVLAMzZeKrrTQepeNBRhg6HzFDDi8yg6yB5c9g==} + '@unocss/preset-wind3@66.4.2': + resolution: {integrity: sha512-0Aye/PaT08M/cQhPnGKn93iEVoRJbym0/1eomMvXoL+8oc7DVry35ws06r5CLu5h1sXI6UmS6sejoePFlSkLJQ==} - '@unocss/preset-wind@66.0.0': - resolution: {integrity: sha512-FtvGpHnGC7FiyKJavPnn5y9lsaoWRhXlujCqlT5Bw63kKhMNr0ogKySBpenUhJOhWhVM0OQXn2nZ3GZRxW2qpw==} + '@unocss/preset-wind4@66.4.2': + resolution: {integrity: sha512-F4RZsDqIpnSevD9hY353+Tw5gxpJuHA5HwdKjLnC/TnT9VKKVmV7qUEZ6M0jEuAk1kz2x3/ngnQ9Ftw+C2L84A==} + + '@unocss/preset-wind@66.4.2': + resolution: {integrity: sha512-z/rFYFINNqmBtl3Dh+7UCKpPnPkxM7IIUGszMnvdntky9uhLauJ11dt/Puir73sM2cAfywfgvnHyZ00m0pg7rA==} '@unocss/reset@66.0.0': resolution: {integrity: sha512-YLFz/5yT7mFJC8JSmIUA5+bS3CBCJbtztOw+8rWzjQr/BEVSGuihWUUpI2Df6VVxXIXxKanZR6mIl59yvf+GEA==} - '@unocss/rule-utils@66.0.0': - resolution: {integrity: sha512-UJ51YHbwxYTGyj35ugsPlOT4gaa7tCbXdywZ3m5Nn0JgywwIqGmBFyiN9ZjHBHfJuDxmmPd6lxojoBscih/WMQ==} + '@unocss/reset@66.4.2': + resolution: {integrity: sha512-s3Kq4Q6a/d3/jYe6HTCfXUx7zYAYufetId5n66DZHzQxpeu6CoBS83+b37STTKsw27SOgV28cPJlJtZ6/D6Bhw==} + + '@unocss/rule-utils@66.4.2': + resolution: {integrity: sha512-7z3IuajwXhy2cx3E0IGOFXIiuKC79/jzm4Tt56TC68nXLh/etlH0fKhxVwkZ/HbcQRpVwWyDRNcbh29pmA3DwQ==} engines: {node: '>=14'} - '@unocss/transformer-attributify-jsx@66.0.0': - resolution: {integrity: sha512-jS7szFXXC6RjTv9wo0NACskf618w981bkbyQ5izRO7Ha47sNpHhHDpaltnG7SR9qV4cCtGalOw4onVMHsRKwRg==} + '@unocss/transformer-attributify-jsx@66.4.2': + resolution: {integrity: sha512-de6LzoyW1tkdOftlCrj6z8wEb4j6l1sqmOU1nYKkYHw7luLFGxRUELC7iujlI9KmylbM02bcKfLETAfJy/je2w==} - '@unocss/transformer-compile-class@66.0.0': - resolution: {integrity: sha512-ytUIE0nAcHRMACuTXkHp8auZ483DXrOZw99jk3FJ+aFjpD/pVSFmX14AWJ7bqPFObxb4SLFs6KhQma30ESC22A==} + '@unocss/transformer-compile-class@66.4.2': + resolution: {integrity: sha512-+oiIrV8c3T7qiJdICr6YsEWik5sjbWirXF0mlpcBvZu2HyV559hvHjzuWKr/fl7xYYZKDL9FvddbqWo3DOXh3Q==} - '@unocss/transformer-directives@66.0.0': - resolution: {integrity: sha512-utcg7m2Foi7uHrU5WHadNuJ0a3qWG8tZNkQMi+m0DQpX6KWfuDtDn0zDZ1X+z5lmiB3WGSJERRrsvZbj1q50Mw==} + '@unocss/transformer-directives@66.4.2': + resolution: {integrity: sha512-7m/dTrCUkBkZeSRKPxPEo65Rav239orQSLq6sztwZhoA4x/6H8r58xCkAK0qC9VEalyerpCpyarU3sKN4+ehNg==} - '@unocss/transformer-variant-group@66.0.0': - resolution: {integrity: sha512-1BLjNWtAnR1JAcQGw0TS+nGrVoB9aznzvVZRoTx23dtRr3btvgKPHb8LrD48eD/p8Dtw9j3WfuxMDKXKegKDLg==} + '@unocss/transformer-variant-group@66.4.2': + resolution: {integrity: sha512-SbPDbZUrhQyL4CpvnpvUfrr1DFq8AKf8ofPGbMJDm5S2TInQ34vFaIrhNroGR0szntMZRH5Zlkq6LtVUKDRs5g==} - '@unocss/vite@66.0.0': - resolution: {integrity: sha512-IVcPX8xL+2edyXKt4tp9yu5A6gcbPVCsspfcL0XgziCr01kS+4qSoZ90F3IUs3hXc/AyO5eCpRtGFMPLpOjXQg==} + '@unocss/vite@66.4.2': + resolution: {integrity: sha512-7eON9iPF3qWzuI+M6u0kq7K3y9nEbimZlLj01nGoqrgSGxEsyJpP01QQQsmT7FPRiZzRMJv7BiKMEyDQSuRRCA==} peerDependencies: - vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} @@ -4033,6 +4154,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -4287,8 +4413,8 @@ packages: resolution: {integrity: sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - babel-plugin-polyfill-corejs2@0.4.13: - resolution: {integrity: sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==} + babel-plugin-polyfill-corejs2@0.4.14: + resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -4297,8 +4423,13 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-regenerator@0.6.4: - resolution: {integrity: sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==} + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.5: + resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -4395,6 +4526,11 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + browserslist@4.25.4: + resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} @@ -4477,6 +4613,9 @@ packages: caniuse-lite@1.0.30001700: resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==} + caniuse-lite@1.0.30001741: + resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==} + canvas@3.1.2: resolution: {integrity: sha512-Z/tzFAcBzoCvJlOSlCnoekh1Gu8YMn0J51+UAuXJAbW1Z6I9l2mZgdD7738MepoeeIcUdDtbMnOg6cC7GJxy/g==} engines: {node: ^18.12.0 || >= 20.9.0} @@ -4779,12 +4918,15 @@ packages: confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + connect-history-api-fallback@2.0.0: resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} engines: {node: '>=0.8'} - consola@3.4.0: - resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} console.table@0.10.0: @@ -4835,6 +4977,9 @@ packages: core-js-compat@3.42.0: resolution: {integrity: sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==} + core-js-compat@3.45.1: + resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==} + core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} @@ -5473,6 +5618,9 @@ packages: electron-to-chromium@1.5.101: resolution: {integrity: sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA==} + electron-to-chromium@1.5.217: + resolution: {integrity: sha512-Pludfu5iBxp9XzNl0qq2G87hdD17ZV7h5T4n6rQXDi3nCyloBV3jreE9+8GC6g4X/5yxqVgXEURpcLtM0WS4jA==} + elkjs@0.9.3: resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==} @@ -5542,8 +5690,8 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-abstract@1.23.9: - resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -5587,13 +5735,13 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} engines: {node: '>=18'} hasBin: true - esbuild@0.25.0: - resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} engines: {node: '>=18'} hasBin: true @@ -5677,6 +5825,7 @@ packages: eslint-plugin-markdown@5.1.0: resolution: {integrity: sha512-SJeyKko1K6GwI0AN6xeCDToXDkfKZfXcexA6B+O2Wr2btUS9GrC+YgwSyVli5DJnctUHjFXcQ2cqTaAmVoLi2A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: Please use @eslint/markdown instead peerDependencies: eslint: '>=8' @@ -5852,6 +6001,9 @@ packages: resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} engines: {node: '>= 18'} + exsolve@1.0.7: + resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -5958,6 +6110,15 @@ packages: picomatch: optional: true + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + ferrum@1.9.4: resolution: {integrity: sha512-ooNerLoIht/dK4CQJux93z/hnt9JysrXniJCI3r6YRgmHeXC57EJ8XaTCT1Gm8LfhIAeWxyJA0O7d/W3pqDYRg==} @@ -6713,6 +6874,10 @@ packages: is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} @@ -6888,8 +7053,8 @@ packages: resolution: {integrity: sha512-oSwM7q8PTHQWuZAlp995iPpPJ4Vkl7qT0ZRD+9duL9j2oBy6KcTfyxc8mEuHJYC+z/kbps80aJLkaNzTOrf/kw==} engines: {node: 20 || >=22} - jake@10.9.2: - resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} engines: {node: '>=10'} hasBin: true @@ -7276,6 +7441,10 @@ packages: resolution: {integrity: sha512-bbgPw/wmroJsil/GgL4qjDzs5YLTBMQ99weRsok1XCDccQeehbHA/I1oRvk2NPtr7KGZgT/Y5tPRnAtMqeG2Kg==} engines: {node: '>=14'} + local-pkg@1.1.1: + resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} + engines: {node: '>=14'} + locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -7404,8 +7573,8 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - marked@16.0.0: - resolution: {integrity: sha512-MUKMXDjsD/eptB7GPzxo4xcnLS6oo7/RHimUMHEDRhUooPwmN9BEpMl7AEOJv3bmso169wHI2wUF9VQgL7zfmA==} + marked@16.2.1: + resolution: {integrity: sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==} engines: {node: '>= 20'} hasBin: true @@ -7822,6 +7991,9 @@ packages: node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.20: + resolution: {integrity: sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==} + node-source-walk@7.0.0: resolution: {integrity: sha512-1uiY543L+N7Og4yswvlm5NCKgPKDEXd9AUR9Jh3gen6oOeBsesr6LqhXom1er3eRzSUcVRWXzhv8tSNrIfGHKw==} engines: {node: '>=18'} @@ -7830,6 +8002,9 @@ packages: resolution: {integrity: sha512-fiVbT7BqxiQqjlR9U3FDGOSERFCKoXVCdxV2FwZuNN7/cmJ42iQx35nUFOAFDcyvemu9Adp+IlsCGlKQYLmBKw==} deprecated: Package no longer supported. Contact support@npmjs.com for more info. + non-layered-tidy-tree-layout@2.0.2: + resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -8040,6 +8215,9 @@ packages: package-manager-detector@0.2.9: resolution: {integrity: sha512-+vYvA/Y31l8Zk8dwxHhL3JfTuHPm6tlxM2A3GeQyl7ovYnSp1+mzAxClxaOr0qO1TtPxbQxetI7v5XqKLJZk7Q==} + package-manager-detector@1.3.0: + resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -8163,6 +8341,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pidtree@0.6.0: resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} engines: {node: '>=0.10'} @@ -8223,6 +8405,9 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + pkg-types@2.2.0: + resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} + plist@3.1.0: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} @@ -8405,6 +8590,9 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + quansync@0.2.10: + resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -8500,8 +8688,8 @@ packages: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} - regenerate-unicode-properties@10.2.0: - resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} engines: {node: '>=4'} regenerate@1.4.2: @@ -8527,8 +8715,8 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - regexpu-core@6.2.0: - resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==} + regexpu-core@6.3.1: + resolution: {integrity: sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ==} engines: {node: '>=4'} regjsgen@0.8.0: @@ -8680,13 +8868,13 @@ packages: engines: {node: '>=10.0.0'} hasBin: true - rollup@4.34.8: - resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} + rollup@4.40.2: + resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.40.2: - resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} + rollup@4.50.1: + resolution: {integrity: sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -8996,6 +9184,7 @@ packages: source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions sourcemap-codec@1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} @@ -9080,6 +9269,10 @@ packages: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + stream-combiner@0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} @@ -9283,8 +9476,8 @@ packages: engines: {node: '>=10'} hasBin: true - terser@5.39.0: - resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==} + terser@5.44.0: + resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} engines: {node: '>=10'} hasBin: true @@ -9327,6 +9520,9 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.0.1: + resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyglobby@0.2.12: resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} @@ -9335,6 +9531,10 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + tinypool@1.0.2: resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -9504,18 +9704,18 @@ packages: typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - typedoc-plugin-markdown@4.4.2: - resolution: {integrity: sha512-kJVkU2Wd+AXQpyL6DlYXXRrfNrHrEIUgiABWH8Z+2Lz5Sq6an4dQ/hfvP75bbokjNDUskOdFlEEm/0fSVyC7eg==} + typedoc-plugin-markdown@4.8.1: + resolution: {integrity: sha512-ug7fc4j0SiJxSwBGLncpSo8tLvrT9VONvPUQqQDTKPxCoFQBADLli832RGPtj6sfSVJebNSrHZQRUdEryYH/7g==} engines: {node: '>= 18'} peerDependencies: - typedoc: 0.27.x + typedoc: 0.28.x - typedoc@0.27.8: - resolution: {integrity: sha512-q0/2TUunNEDmWkn23ULKGXieK8cgGuAmBUXC/HcZ/rgzMI9Yr4Nq3in1K1vT1NZ9zx6M78yTk3kmIPbwJgK5KA==} - engines: {node: '>= 18'} + typedoc@0.28.11: + resolution: {integrity: sha512-1FqgrrUYGNuE3kImAiEDgAVVVacxdO4ZVTKbiOVDGkoeSB4sNwQaDpa8mta+Lw5TEzBFmGXzsg0I1NLRIoaSFw==} + engines: {node: '>= 18', pnpm: '>= 10'} hasBin: true peerDependencies: - typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x + typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x typescript-eslint@8.38.0: resolution: {integrity: sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==} @@ -9547,8 +9747,8 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} - unconfig@7.0.0: - resolution: {integrity: sha512-G5CJSoG6ZTxgzCJblEfgpdRK2tos9+UdD2WtecDUVfImzQ0hFjwpH5RVvGMhP4pRpC9ML7NrC4qBsBl0Ttj35A==} + unconfig@7.3.2: + resolution: {integrity: sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg==} underscore@1.1.7: resolution: {integrity: sha512-w4QtCHoLBXw1mjofIDoMyexaEdWGMedWNDhlWTtT1V1lCRqi65Pnoygkh6+WRdr+Bm8ldkBNkNeCsXGMlQS9HQ==} @@ -9571,8 +9771,8 @@ packages: resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} engines: {node: '>=4'} - unicode-match-property-value-ecmascript@2.2.0: - resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} engines: {node: '>=4'} unicode-property-aliases-ecmascript@2.1.0: @@ -9625,12 +9825,12 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - unocss@66.0.0: - resolution: {integrity: sha512-SHstiv1s7zGPSjzOsADzlwRhQM+6817+OqQE3Fv+N/nn2QLNx1bi3WXybFfz5tWkzBtyTZlwdPmeecsIs1yOCA==} + unocss@66.4.2: + resolution: {integrity: sha512-PsZ+4XF/ekiParR7PZEM7AchvHJ78EIfOXlqTPflTOXCYgZ77kG9NaIaIf4lHRevY+rRTyrHrjxdg1Ern2j8qw==} engines: {node: '>=14'} peerDependencies: - '@unocss/webpack': 66.0.0 - vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 + '@unocss/webpack': 66.4.2 + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 peerDependenciesMeta: '@unocss/webpack': optional: true @@ -9679,6 +9879,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -9777,8 +9983,8 @@ packages: terser: optional: true - vite@6.1.1: - resolution: {integrity: sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==} + vite@6.3.6: + resolution: {integrity: sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -9817,48 +10023,8 @@ packages: yaml: optional: true - vite@6.1.6: - resolution: {integrity: sha512-u+jokLMwHVFUoUkfL+m/1hzucejL2639g9QXcrRdtN3WPHfW7imI83V96Oh1R0xVZqDjvcgp+7S8bSQpdVlmPA==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vite@7.0.3: - resolution: {integrity: sha512-y2L5oJZF7bj4c0jgGYgBNSdIu+5HF+m68rn2cQXFbGoShdhV1phX9rbnxy9YXj82aS8MMsCLAAFkRxZeWdldrQ==} + vite@7.0.7: + resolution: {integrity: sha512-hc6LujN/EkJHmxeiDJMs0qBontZ1cdBvvoCbWhVjzUFTU329VRyOC46gHNSA8NcOC5yzCeXpwI40tieI3DEZqg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -9974,10 +10140,8 @@ packages: vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - vue-flow-layout@0.1.1: - resolution: {integrity: sha512-JdgRRUVrN0Y2GosA0M68DEbKlXMqJ7FQgsK8CjQD2vxvNSqAU6PZEpi4cfcTVtfM2GVOMjHo7GKKLbXxOBqDqA==} - peerDependencies: - vue: ^3.4.37 + vue-flow-layout@0.2.0: + resolution: {integrity: sha512-zKgsWWkXq0xrus7H4Mc+uFs1ESrmdTXlO0YNbR6wMdPaFvosL3fMB8N7uTV308UhGy9UvTrGhIY7mVz9eN+L0Q==} vue@3.5.13: resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} @@ -10294,11 +10458,6 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} - engines: {node: '>= 14'} - hasBin: true - yaml@2.8.0: resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} engines: {node: '>= 14.6'} @@ -10498,12 +10657,12 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - '@antfu/install-pkg@1.0.0': + '@antfu/install-pkg@1.1.0': dependencies: - package-manager-detector: 0.2.9 - tinyexec: 0.3.2 + package-manager-detector: 1.3.0 + tinyexec: 1.0.1 - '@antfu/utils@8.1.1': {} + '@antfu/utils@9.2.0': {} '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': dependencies: @@ -10814,6 +10973,8 @@ snapshots: '@babel/compat-data@7.27.2': {} + '@babel/compat-data@7.28.4': {} + '@babel/core@7.27.1': dependencies: '@ampproject/remapping': 2.3.0 @@ -10854,9 +11015,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.1(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.27.1': dependencies: - '@babel/parser': 7.27.2 + '@babel/parser': 7.28.0 '@babel/types': 7.27.1 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 @@ -10870,9 +11051,17 @@ snapshots: '@jridgewell/trace-mapping': 0.3.29 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.27.1': + '@babel/generator@7.28.3': dependencies: - '@babel/types': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.4 '@babel/helper-compilation-targets@7.27.2': dependencies: @@ -10882,28 +11071,28 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.1)': + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.1) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.28.0)': + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -10911,18 +11100,18 @@ snapshots: '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-annotate-as-pure': 7.27.1 - regexpu-core: 6.2.0 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.3.1 semver: 6.3.1 - '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.28.0)': + '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-annotate-as-pure': 7.27.1 - regexpu-core: 6.2.0 + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.3.1 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.27.1)': + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-compilation-targets': 7.27.2 @@ -10933,9 +11122,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.28.0)': + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 debug: 4.4.1(supports-color@8.1.1) @@ -10948,28 +11137,19 @@ snapshots: '@babel/helper-member-expression-to-functions@7.27.1': dependencies: - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.1 + '@babel/traverse': 7.28.0 '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color '@babel/helper-module-transforms@7.27.1(@babel/core@7.27.1)': - dependencies: - '@babel/core': 7.27.1 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-module-imports': 7.27.1 @@ -10987,9 +11167,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.27.1)': + dependencies: + '@babel/core': 7.27.1 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.0 + '@babel/types': 7.28.4 '@babel/helper-plugin-utils@7.26.5': {} @@ -10998,18 +11196,18 @@ snapshots: '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-annotate-as-pure': 7.27.1 - '@babel/helper-wrap-function': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.3 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.0)': + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-annotate-as-pure': 7.27.1 - '@babel/helper-wrap-function': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.3 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -11018,23 +11216,23 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.0)': + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color @@ -11044,11 +11242,11 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-wrap-function@7.27.1': + '@babel/helper-wrap-function@7.28.3': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 transitivePeerDependencies: - supports-color @@ -11062,6 +11260,11 @@ snapshots: '@babel/template': 7.27.2 '@babel/types': 7.28.0 + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + '@babel/parser@7.26.9': dependencies: '@babel/types': 7.27.1 @@ -11074,19 +11277,23 @@ snapshots: dependencies: '@babel/types': 7.28.0 + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -11095,9 +11302,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.27.1)': @@ -11105,9 +11312,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.27.1)': @@ -11119,28 +11326,28 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -11148,9 +11355,9 @@ snapshots: dependencies: '@babel/core': 7.27.1 - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.0)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0)': dependencies: @@ -11177,9 +11384,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.27.1)': @@ -11192,6 +11399,11 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -11263,10 +11475,10 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.0)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.27.1)': @@ -11274,26 +11486,26 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-async-generator-functions@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.1) - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-generator-functions@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.0) - '@babel/traverse': 7.28.0 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -11306,12 +11518,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.0) + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) transitivePeerDependencies: - supports-color @@ -11320,74 +11532,74 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-block-scoping@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-transform-block-scoping@7.28.4(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-block-scoping@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-block-scoping@7.28.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.1) - '@babel/traverse': 7.28.0 - globals: 11.12.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0) - '@babel/traverse': 7.28.0 - globals: 11.12.0 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -11397,21 +11609,27 @@ snapshots: '@babel/helper-plugin-utils': 7.27.1 '@babel/template': 7.27.2 - '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/template': 7.27.2 - '@babel/plugin-transform-destructuring@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color - '@babel/plugin-transform-destructuring@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.27.1)': dependencies: @@ -11419,10 +11637,10 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.27.1)': @@ -11430,9 +11648,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.27.1)': @@ -11441,10 +11659,10 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.27.1)': @@ -11452,19 +11670,27 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.27.1)': @@ -11472,9 +11698,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.27.1)': @@ -11485,9 +11711,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: @@ -11498,16 +11724,16 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color @@ -11516,9 +11742,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-literals@7.27.1(@babel/core@7.27.1)': @@ -11526,9 +11752,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.27.1)': @@ -11536,9 +11762,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.27.1)': @@ -11546,23 +11772,23 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.1) + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -11570,15 +11796,15 @@ snapshots: '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.1) + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -11586,35 +11812,35 @@ snapshots: '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.1) + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.28.4 transitivePeerDependencies: - supports-color '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.1) + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -11625,10 +11851,10 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.27.1)': @@ -11636,9 +11862,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.27.1)': @@ -11646,9 +11872,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.27.1)': @@ -11656,26 +11882,32 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-object-rest-spread@7.27.2(@babel/core@7.27.1)': + '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.27.1) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.27.1) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color - '@babel/plugin-transform-object-rest-spread@7.27.2(@babel/core@7.28.0)': + '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.27.1)': dependencies: @@ -11685,11 +11917,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) transitivePeerDependencies: - supports-color @@ -11698,9 +11930,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.27.1)': @@ -11711,36 +11943,36 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-parameters@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -11748,17 +11980,17 @@ snapshots: '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-annotate-as-pure': 7.27.1 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-annotate-as-pure': 7.27.1 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -11768,19 +12000,19 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regenerator@7.27.1(@babel/core@7.27.1)': + '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regenerator@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.27.1)': @@ -11789,10 +12021,10 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.27.1)': @@ -11800,9 +12032,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.27.1)': @@ -11810,9 +12042,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-spread@7.27.1(@babel/core@7.27.1)': @@ -11823,9 +12055,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: @@ -11836,9 +12068,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.27.1)': @@ -11846,9 +12078,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.27.1)': @@ -11856,9 +12088,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.27.1)': @@ -11866,9 +12098,9 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.27.1)': @@ -11877,10 +12109,10 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.27.1)': @@ -11889,10 +12121,10 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.27.1)': @@ -11901,15 +12133,15 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.0)': + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/preset-env@7.27.2(@babel/core@7.27.1)': dependencies: - '@babel/compat-data': 7.27.2 + '@babel/compat-data': 7.28.4 '@babel/core': 7.27.1 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 @@ -11918,21 +12150,21 @@ snapshots: '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.27.1) '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.27.1) '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.27.1) '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.1) '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.27.1) '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.27.1) '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.27.1) '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-async-generator-functions': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.27.1) '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-block-scoping': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-block-scoping': 7.28.4(@babel/core@7.27.1) '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-classes': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.27.1) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.27.1) '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-destructuring': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.27.1) '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.1) @@ -11953,15 +12185,15 @@ snapshots: '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-object-rest-spread': 7.27.2(@babel/core@7.27.1) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.27.1) '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.27.1) '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.27.1) - '@babel/plugin-transform-regenerator': 7.27.1(@babel/core@7.27.1) + '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.27.1) '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.27.1) @@ -11974,85 +12206,86 @@ snapshots: '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.27.1) '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.27.1) '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.27.1) - babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.27.1) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.27.1) babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.1) - babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.27.1) - core-js-compat: 3.42.0 + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.27.1) + core-js-compat: 3.45.1 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-env@7.27.2(@babel/core@7.28.0)': + '@babel/preset-env@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/compat-data': 7.27.2 - '@babel/core': 7.28.0 + '@babel/compat-data': 7.28.4 + '@babel/core': 7.28.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.0) - '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.0) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-async-generator-functions': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-block-scoping': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-classes': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-destructuring': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-object-rest-spread': 7.27.2(@babel/core@7.28.0) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-regenerator': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.0) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.0) - babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.28.0) - babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.28.0) - babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.28.0) - core-js-compat: 3.42.0 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.4) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.4) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-block-scoping': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.4) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.4) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.4) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.4) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.4) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.4) + core-js-compat: 3.45.1 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -12061,33 +12294,33 @@ snapshots: dependencies: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/types': 7.28.0 + '@babel/types': 7.28.4 esutils: 2.0.3 - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.0)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.4)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/types': 7.28.0 + '@babel/types': 7.28.4 esutils: 2.0.3 '@babel/runtime@7.26.9': dependencies: regenerator-runtime: 0.14.1 - '@babel/runtime@7.27.1': {} + '@babel/runtime@7.28.4': {} '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.2 + '@babel/parser': 7.28.0 '@babel/types': 7.27.1 '@babel/traverse@7.27.1': dependencies: '@babel/code-frame': 7.27.1 '@babel/generator': 7.27.1 - '@babel/parser': 7.27.2 + '@babel/parser': 7.28.0 '@babel/template': 7.27.2 '@babel/types': 7.27.1 debug: 4.4.1(supports-color@8.1.1) @@ -12107,6 +12340,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + '@babel/types@7.26.9': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -12122,6 +12367,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@bcoe/v8-coverage@0.2.3': {} '@bcoe/v8-coverage@1.0.2': {} @@ -12673,7 +12923,7 @@ snapshots: '@babel/preset-env': 7.27.2(@babel/core@7.27.1) babel-loader: 9.2.1(@babel/core@7.27.1)(webpack@5.95.0(esbuild@0.25.0)) bluebird: 3.7.1 - debug: 4.4.0 + debug: 4.4.1(supports-color@8.1.1) lodash: 4.17.21 webpack: 5.95.0(esbuild@0.25.0) transitivePeerDependencies: @@ -12744,220 +12994,223 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.24.2': + '@esbuild/aix-ppc64@0.25.0': optional: true - '@esbuild/aix-ppc64@0.25.0': + '@esbuild/aix-ppc64@0.25.9': optional: true '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.24.2': + '@esbuild/android-arm64@0.25.0': optional: true - '@esbuild/android-arm64@0.25.0': + '@esbuild/android-arm64@0.25.9': optional: true '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.24.2': + '@esbuild/android-arm@0.25.0': optional: true - '@esbuild/android-arm@0.25.0': + '@esbuild/android-arm@0.25.9': optional: true '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.24.2': + '@esbuild/android-x64@0.25.0': optional: true - '@esbuild/android-x64@0.25.0': + '@esbuild/android-x64@0.25.9': optional: true '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.24.2': + '@esbuild/darwin-arm64@0.25.0': optional: true - '@esbuild/darwin-arm64@0.25.0': + '@esbuild/darwin-arm64@0.25.9': optional: true '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.24.2': + '@esbuild/darwin-x64@0.25.0': optional: true - '@esbuild/darwin-x64@0.25.0': + '@esbuild/darwin-x64@0.25.9': optional: true '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.24.2': + '@esbuild/freebsd-arm64@0.25.0': optional: true - '@esbuild/freebsd-arm64@0.25.0': + '@esbuild/freebsd-arm64@0.25.9': optional: true '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.24.2': + '@esbuild/freebsd-x64@0.25.0': optional: true - '@esbuild/freebsd-x64@0.25.0': + '@esbuild/freebsd-x64@0.25.9': optional: true '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.24.2': + '@esbuild/linux-arm64@0.25.0': optional: true - '@esbuild/linux-arm64@0.25.0': + '@esbuild/linux-arm64@0.25.9': optional: true '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.24.2': + '@esbuild/linux-arm@0.25.0': optional: true - '@esbuild/linux-arm@0.25.0': + '@esbuild/linux-arm@0.25.9': optional: true '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.24.2': + '@esbuild/linux-ia32@0.25.0': optional: true - '@esbuild/linux-ia32@0.25.0': + '@esbuild/linux-ia32@0.25.9': optional: true '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.24.2': + '@esbuild/linux-loong64@0.25.0': optional: true - '@esbuild/linux-loong64@0.25.0': + '@esbuild/linux-loong64@0.25.9': optional: true '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.24.2': + '@esbuild/linux-mips64el@0.25.0': optional: true - '@esbuild/linux-mips64el@0.25.0': + '@esbuild/linux-mips64el@0.25.9': optional: true '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.24.2': + '@esbuild/linux-ppc64@0.25.0': optional: true - '@esbuild/linux-ppc64@0.25.0': + '@esbuild/linux-ppc64@0.25.9': optional: true '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.24.2': + '@esbuild/linux-riscv64@0.25.0': optional: true - '@esbuild/linux-riscv64@0.25.0': + '@esbuild/linux-riscv64@0.25.9': optional: true '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.24.2': + '@esbuild/linux-s390x@0.25.0': optional: true - '@esbuild/linux-s390x@0.25.0': + '@esbuild/linux-s390x@0.25.9': optional: true '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.24.2': - optional: true - '@esbuild/linux-x64@0.25.0': optional: true - '@esbuild/netbsd-arm64@0.24.2': + '@esbuild/linux-x64@0.25.9': optional: true '@esbuild/netbsd-arm64@0.25.0': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/netbsd-arm64@0.25.9': optional: true - '@esbuild/netbsd-x64@0.24.2': + '@esbuild/netbsd-x64@0.21.5': optional: true '@esbuild/netbsd-x64@0.25.0': optional: true - '@esbuild/openbsd-arm64@0.24.2': + '@esbuild/netbsd-x64@0.25.9': optional: true '@esbuild/openbsd-arm64@0.25.0': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/openbsd-arm64@0.25.9': optional: true - '@esbuild/openbsd-x64@0.24.2': + '@esbuild/openbsd-x64@0.21.5': optional: true '@esbuild/openbsd-x64@0.25.0': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/openbsd-x64@0.25.9': optional: true - '@esbuild/sunos-x64@0.24.2': + '@esbuild/openharmony-arm64@0.25.9': + optional: true + + '@esbuild/sunos-x64@0.21.5': optional: true '@esbuild/sunos-x64@0.25.0': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/sunos-x64@0.25.9': optional: true - '@esbuild/win32-arm64@0.24.2': + '@esbuild/win32-arm64@0.21.5': optional: true '@esbuild/win32-arm64@0.25.0': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/win32-arm64@0.25.9': optional: true - '@esbuild/win32-ia32@0.24.2': + '@esbuild/win32-ia32@0.21.5': optional: true '@esbuild/win32-ia32@0.25.0': optional: true + '@esbuild/win32-ia32@0.25.9': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.24.2': + '@esbuild/win32-x64@0.25.0': optional: true - '@esbuild/win32-x64@0.25.0': + '@esbuild/win32-x64@0.25.9': optional: true '@eslint-community/eslint-utils@4.5.1(eslint@9.26.0(jiti@2.4.2))': @@ -13062,10 +13315,12 @@ snapshots: '@floating-ui/utils@0.2.9': {} - '@gerrit0/mini-shiki@1.27.2': + '@gerrit0/mini-shiki@3.12.0': dependencies: - '@shikijs/engine-oniguruma': 1.29.2 - '@shikijs/types': 1.29.2 + '@shikijs/engine-oniguruma': 3.12.0 + '@shikijs/langs': 3.12.0 + '@shikijs/themes': 3.12.0 + '@shikijs/types': 3.12.0 '@shikijs/vscode-textmate': 10.0.2 '@hapi/hoek@9.3.0': {} @@ -13111,15 +13366,15 @@ snapshots: '@iconify/types@2.0.0': {} - '@iconify/utils@2.3.0': + '@iconify/utils@3.0.1': dependencies: - '@antfu/install-pkg': 1.0.0 - '@antfu/utils': 8.1.1 + '@antfu/install-pkg': 1.1.0 + '@antfu/utils': 9.2.0 '@iconify/types': 2.0.0 - debug: 4.4.0 + debug: 4.4.1(supports-color@8.1.1) globals: 15.15.0 kolorist: 1.8.0 - local-pkg: 1.0.0 + local-pkg: 1.1.1 mlly: 1.7.4 transitivePeerDependencies: - supports-color @@ -13402,16 +13657,31 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/set-array@1.2.1': {} + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/source-map@0.3.6': dependencies: '@jridgewell/gen-mapping': 0.3.8 @@ -13419,6 +13689,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -13429,6 +13701,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jsdevtools/ono@7.1.3': {} '@leichtgewicht/ip-codec@2.0.5': {} @@ -13501,6 +13778,10 @@ snapshots: '@polka/url@1.0.0-next.28': {} + '@quansync/fs@0.1.4': + dependencies: + quansync: 0.2.10 + '@react-aria/focus@3.21.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@react-aria/interactions': 3.25.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -13552,9 +13833,9 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.19': {} - '@rollup/plugin-babel@5.3.1(@babel/core@7.28.0)(@types/babel__core@7.20.5)(rollup@2.79.2)': + '@rollup/plugin-babel@5.3.1(@babel/core@7.28.4)(@types/babel__core@7.20.5)(rollup@2.79.2)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 '@rollup/pluginutils': 3.1.0(rollup@2.79.2) rollup: 2.79.2 @@ -13565,7 +13846,7 @@ snapshots: '@rollup/plugin-node-resolve@15.3.1(rollup@2.79.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@2.79.2) + '@rollup/pluginutils': 5.3.0(rollup@2.79.2) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 @@ -13583,17 +13864,17 @@ snapshots: dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 - terser: 5.39.0 + terser: 5.44.0 optionalDependencies: rollup: 2.79.2 - '@rollup/plugin-typescript@12.1.2(rollup@4.40.2)(tslib@2.8.1)(typescript@5.7.3)': + '@rollup/plugin-typescript@12.1.2(rollup@4.50.1)(tslib@2.8.1)(typescript@5.7.3)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.40.2) + '@rollup/pluginutils': 5.1.4(rollup@4.50.1) resolve: 1.22.10 typescript: 5.7.3 optionalDependencies: - rollup: 4.40.2 + rollup: 4.50.1 tslib: 2.8.1 '@rollup/pluginutils@3.1.0(rollup@2.79.2)': @@ -13603,139 +13884,145 @@ snapshots: picomatch: 2.3.1 rollup: 2.79.2 - '@rollup/pluginutils@5.1.4(rollup@2.79.2)': + '@rollup/pluginutils@5.1.4(rollup@4.50.1)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 + optionalDependencies: + rollup: 4.50.1 + + '@rollup/pluginutils@5.3.0(rollup@2.79.2)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 optionalDependencies: rollup: 2.79.2 - '@rollup/pluginutils@5.1.4(rollup@4.40.2)': - dependencies: - '@types/estree': 1.0.6 - estree-walker: 2.0.2 - picomatch: 4.0.2 - optionalDependencies: - rollup: 4.40.2 - - '@rollup/rollup-android-arm-eabi@4.34.8': - optional: true - '@rollup/rollup-android-arm-eabi@4.40.2': optional: true - '@rollup/rollup-android-arm64@4.34.8': + '@rollup/rollup-android-arm-eabi@4.50.1': optional: true '@rollup/rollup-android-arm64@4.40.2': optional: true - '@rollup/rollup-darwin-arm64@4.34.8': + '@rollup/rollup-android-arm64@4.50.1': optional: true '@rollup/rollup-darwin-arm64@4.40.2': optional: true - '@rollup/rollup-darwin-x64@4.34.8': + '@rollup/rollup-darwin-arm64@4.50.1': optional: true '@rollup/rollup-darwin-x64@4.40.2': optional: true - '@rollup/rollup-freebsd-arm64@4.34.8': + '@rollup/rollup-darwin-x64@4.50.1': optional: true '@rollup/rollup-freebsd-arm64@4.40.2': optional: true - '@rollup/rollup-freebsd-x64@4.34.8': + '@rollup/rollup-freebsd-arm64@4.50.1': optional: true '@rollup/rollup-freebsd-x64@4.40.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + '@rollup/rollup-freebsd-x64@4.50.1': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.40.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.34.8': + '@rollup/rollup-linux-arm-gnueabihf@4.50.1': optional: true '@rollup/rollup-linux-arm-musleabihf@4.40.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.34.8': + '@rollup/rollup-linux-arm-musleabihf@4.50.1': optional: true '@rollup/rollup-linux-arm64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.34.8': + '@rollup/rollup-linux-arm64-gnu@4.50.1': optional: true '@rollup/rollup-linux-arm64-musl@4.40.2': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + '@rollup/rollup-linux-arm64-musl@4.50.1': optional: true '@rollup/rollup-linux-loongarch64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + '@rollup/rollup-linux-loongarch64-gnu@4.50.1': optional: true '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.34.8': + '@rollup/rollup-linux-ppc64-gnu@4.50.1': optional: true '@rollup/rollup-linux-riscv64-gnu@4.40.2': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.50.1': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.40.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.34.8': + '@rollup/rollup-linux-riscv64-musl@4.50.1': optional: true '@rollup/rollup-linux-s390x-gnu@4.40.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.34.8': + '@rollup/rollup-linux-s390x-gnu@4.50.1': optional: true '@rollup/rollup-linux-x64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-x64-musl@4.34.8': + '@rollup/rollup-linux-x64-gnu@4.50.1': optional: true '@rollup/rollup-linux-x64-musl@4.40.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.34.8': + '@rollup/rollup-linux-x64-musl@4.50.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.50.1': optional: true '@rollup/rollup-win32-arm64-msvc@4.40.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.34.8': + '@rollup/rollup-win32-arm64-msvc@4.50.1': optional: true '@rollup/rollup-win32-ia32-msvc@4.40.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.34.8': + '@rollup/rollup-win32-ia32-msvc@4.50.1': optional: true '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true + '@rollup/rollup-win32-x64-msvc@4.50.1': + optional: true + '@shikijs/core@2.5.0': dependencies: '@shikijs/engine-javascript': 2.5.0 @@ -13751,35 +14038,43 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 3.1.1 - '@shikijs/engine-oniguruma@1.29.2': - dependencies: - '@shikijs/types': 1.29.2 - '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/engine-oniguruma@2.5.0': dependencies: '@shikijs/types': 2.5.0 '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/engine-oniguruma@3.12.0': + dependencies: + '@shikijs/types': 3.12.0 + '@shikijs/vscode-textmate': 10.0.2 + '@shikijs/langs@2.5.0': dependencies: '@shikijs/types': 2.5.0 + '@shikijs/langs@3.12.0': + dependencies: + '@shikijs/types': 3.12.0 + '@shikijs/themes@2.5.0': dependencies: '@shikijs/types': 2.5.0 + '@shikijs/themes@3.12.0': + dependencies: + '@shikijs/types': 3.12.0 + '@shikijs/transformers@2.5.0': dependencies: '@shikijs/core': 2.5.0 '@shikijs/types': 2.5.0 - '@shikijs/types@1.29.2': + '@shikijs/types@2.5.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 - '@shikijs/types@2.5.0': + '@shikijs/types@3.12.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -13840,7 +14135,7 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.2 + '@babel/parser': 7.28.0 '@babel/types': 7.27.1 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 @@ -13852,7 +14147,7 @@ snapshots: '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.2 + '@babel/parser': 7.28.0 '@babel/types': 7.27.1 '@types/babel__traverse@7.20.6': @@ -14033,6 +14328,8 @@ snapshots: '@types/estree@1.0.7': {} + '@types/estree@1.0.8': {} + '@types/express-serve-static-core@4.19.6': dependencies: '@types/node': 22.13.5 @@ -14174,9 +14471,9 @@ snapshots: '@types/retry@0.12.0': {} - '@types/rollup-plugin-visualizer@5.0.3(rollup@4.40.2)': + '@types/rollup-plugin-visualizer@5.0.3(rollup@4.50.1)': dependencies: - rollup-plugin-visualizer: 6.0.3(rollup@4.40.2) + rollup-plugin-visualizer: 6.0.3(rollup@4.50.1) transitivePeerDependencies: - rolldown - rollup @@ -14358,150 +14655,157 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@unocss/astro@66.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3))': + '@unocss/astro@66.4.2(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))': dependencies: - '@unocss/core': 66.0.0 - '@unocss/reset': 66.0.0 - '@unocss/vite': 66.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3)) + '@unocss/core': 66.4.2 + '@unocss/reset': 66.4.2 + '@unocss/vite': 66.4.2(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0)) optionalDependencies: - vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) - transitivePeerDependencies: - - vue + vite: 7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) - '@unocss/cli@66.0.0': + '@unocss/cli@66.4.2': dependencies: '@ampproject/remapping': 2.3.0 - '@unocss/config': 66.0.0 - '@unocss/core': 66.0.0 - '@unocss/preset-uno': 66.0.0 + '@unocss/config': 66.4.2 + '@unocss/core': 66.4.2 + '@unocss/preset-uno': 66.4.2 cac: 6.7.14 chokidar: 3.6.0 colorette: 2.0.20 - consola: 3.4.0 + consola: 3.4.2 magic-string: 0.30.17 pathe: 2.0.3 perfect-debounce: 1.0.0 - tinyglobby: 0.2.12 + tinyglobby: 0.2.14 unplugin-utils: 0.2.4 - '@unocss/config@66.0.0': + '@unocss/config@66.4.2': dependencies: - '@unocss/core': 66.0.0 - unconfig: 7.0.0 + '@unocss/core': 66.4.2 + unconfig: 7.3.2 - '@unocss/core@66.0.0': {} + '@unocss/core@66.4.2': {} - '@unocss/extractor-arbitrary-variants@66.0.0': + '@unocss/extractor-arbitrary-variants@66.4.2': dependencies: - '@unocss/core': 66.0.0 + '@unocss/core': 66.4.2 - '@unocss/inspector@66.0.0(vue@3.5.13(typescript@5.7.3))': + '@unocss/inspector@66.4.2': dependencies: - '@unocss/core': 66.0.0 - '@unocss/rule-utils': 66.0.0 + '@unocss/core': 66.4.2 + '@unocss/rule-utils': 66.4.2 colorette: 2.0.20 gzip-size: 6.0.0 sirv: 3.0.1 - vue-flow-layout: 0.1.1(vue@3.5.13(typescript@5.7.3)) - transitivePeerDependencies: - - vue + vue-flow-layout: 0.2.0 - '@unocss/postcss@66.0.0(postcss@8.5.6)': + '@unocss/postcss@66.4.2(postcss@8.5.6)': dependencies: - '@unocss/config': 66.0.0 - '@unocss/core': 66.0.0 - '@unocss/rule-utils': 66.0.0 + '@unocss/config': 66.4.2 + '@unocss/core': 66.4.2 + '@unocss/rule-utils': 66.4.2 css-tree: 3.1.0 postcss: 8.5.6 - tinyglobby: 0.2.12 + tinyglobby: 0.2.14 - '@unocss/preset-attributify@66.0.0': + '@unocss/preset-attributify@66.4.2': dependencies: - '@unocss/core': 66.0.0 + '@unocss/core': 66.4.2 - '@unocss/preset-icons@66.0.0': + '@unocss/preset-icons@66.4.2': dependencies: - '@iconify/utils': 2.3.0 - '@unocss/core': 66.0.0 + '@iconify/utils': 3.0.1 + '@unocss/core': 66.4.2 ofetch: 1.4.1 transitivePeerDependencies: - supports-color - '@unocss/preset-mini@66.0.0': + '@unocss/preset-mini@66.4.2': dependencies: - '@unocss/core': 66.0.0 - '@unocss/extractor-arbitrary-variants': 66.0.0 - '@unocss/rule-utils': 66.0.0 + '@unocss/core': 66.4.2 + '@unocss/extractor-arbitrary-variants': 66.4.2 + '@unocss/rule-utils': 66.4.2 - '@unocss/preset-tagify@66.0.0': + '@unocss/preset-tagify@66.4.2': dependencies: - '@unocss/core': 66.0.0 + '@unocss/core': 66.4.2 - '@unocss/preset-typography@66.0.0': + '@unocss/preset-typography@66.4.2': dependencies: - '@unocss/core': 66.0.0 - '@unocss/preset-mini': 66.0.0 - '@unocss/rule-utils': 66.0.0 + '@unocss/core': 66.4.2 + '@unocss/preset-mini': 66.4.2 + '@unocss/rule-utils': 66.4.2 - '@unocss/preset-uno@66.0.0': + '@unocss/preset-uno@66.4.2': dependencies: - '@unocss/core': 66.0.0 - '@unocss/preset-wind3': 66.0.0 + '@unocss/core': 66.4.2 + '@unocss/preset-wind3': 66.4.2 - '@unocss/preset-web-fonts@66.0.0': + '@unocss/preset-web-fonts@66.4.2': dependencies: - '@unocss/core': 66.0.0 + '@unocss/core': 66.4.2 ofetch: 1.4.1 - '@unocss/preset-wind3@66.0.0': + '@unocss/preset-wind3@66.4.2': dependencies: - '@unocss/core': 66.0.0 - '@unocss/preset-mini': 66.0.0 - '@unocss/rule-utils': 66.0.0 + '@unocss/core': 66.4.2 + '@unocss/preset-mini': 66.4.2 + '@unocss/rule-utils': 66.4.2 - '@unocss/preset-wind@66.0.0': + '@unocss/preset-wind4@66.4.2': dependencies: - '@unocss/core': 66.0.0 - '@unocss/preset-wind3': 66.0.0 + '@unocss/core': 66.4.2 + '@unocss/extractor-arbitrary-variants': 66.4.2 + '@unocss/rule-utils': 66.4.2 + + '@unocss/preset-wind@66.4.2': + dependencies: + '@unocss/core': 66.4.2 + '@unocss/preset-wind3': 66.4.2 '@unocss/reset@66.0.0': {} - '@unocss/rule-utils@66.0.0': + '@unocss/reset@66.4.2': {} + + '@unocss/rule-utils@66.4.2': dependencies: - '@unocss/core': 66.0.0 + '@unocss/core': 66.4.2 magic-string: 0.30.17 - '@unocss/transformer-attributify-jsx@66.0.0': + '@unocss/transformer-attributify-jsx@66.4.2': dependencies: - '@unocss/core': 66.0.0 + '@babel/parser': 7.28.0 + '@babel/traverse': 7.28.0 + '@unocss/core': 66.4.2 + transitivePeerDependencies: + - supports-color - '@unocss/transformer-compile-class@66.0.0': + '@unocss/transformer-compile-class@66.4.2': dependencies: - '@unocss/core': 66.0.0 + '@unocss/core': 66.4.2 - '@unocss/transformer-directives@66.0.0': + '@unocss/transformer-directives@66.4.2': dependencies: - '@unocss/core': 66.0.0 - '@unocss/rule-utils': 66.0.0 + '@unocss/core': 66.4.2 + '@unocss/rule-utils': 66.4.2 css-tree: 3.1.0 - '@unocss/transformer-variant-group@66.0.0': + '@unocss/transformer-variant-group@66.4.2': dependencies: - '@unocss/core': 66.0.0 + '@unocss/core': 66.4.2 - '@unocss/vite@66.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3))': + '@unocss/vite@66.4.2(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))': dependencies: '@ampproject/remapping': 2.3.0 - '@unocss/config': 66.0.0 - '@unocss/core': 66.0.0 - '@unocss/inspector': 66.0.0(vue@3.5.13(typescript@5.7.3)) + '@unocss/config': 66.4.2 + '@unocss/core': 66.4.2 + '@unocss/inspector': 66.4.2 chokidar: 3.6.0 magic-string: 0.30.17 - tinyglobby: 0.2.12 + pathe: 2.0.3 + tinyglobby: 0.2.14 unplugin-utils: 0.2.4 - vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) - transitivePeerDependencies: - - vue + vite: 7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) '@unrs/resolver-binding-android-arm-eabi@1.11.1': optional: true @@ -14562,19 +14866,19 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vite-pwa/vitepress@1.0.0(vite-plugin-pwa@1.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0))': + '@vite-pwa/vitepress@1.0.0(vite-plugin-pwa@1.0.0(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0))': dependencies: - vite-plugin-pwa: 1.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0) + vite-plugin-pwa: 1.0.0(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0) - '@vitejs/plugin-vue@5.2.1(vite@5.4.19(@types/node@22.13.5)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))': + '@vitejs/plugin-vue@5.2.1(vite@5.4.19(@types/node@22.13.5)(terser@5.44.0))(vue@3.5.13(typescript@5.7.3))': dependencies: - vite: 5.4.19(@types/node@22.13.5)(terser@5.39.0) + vite: 5.4.19(@types/node@22.13.5)(terser@5.44.0) vue: 3.5.13(typescript@5.7.3) - '@vitejs/plugin-vue@6.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3))': + '@vitejs/plugin-vue@6.0.0(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.19 - vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + vite: 7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) vue: 3.5.13(typescript@5.7.3) '@vitest/coverage-v8@3.0.6(vitest@3.0.6)': @@ -14591,7 +14895,7 @@ snapshots: std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.6(@types/debug@4.1.12)(@types/node@22.13.5)(@vitest/ui@3.0.6)(jiti@2.4.2)(jsdom@26.1.0(canvas@3.1.2))(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + vitest: 3.0.6(@types/debug@4.1.12)(@types/node@22.13.5)(@vitest/ui@3.0.6)(jiti@2.4.2)(jsdom@26.1.0(canvas@3.1.2))(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -14602,13 +14906,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.6(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))': + '@vitest/mocker@3.0.6(vite@6.3.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.0.6 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + vite: 6.3.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) '@vitest/pretty-format@3.0.6': dependencies: @@ -14638,7 +14942,7 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.12 tinyrainbow: 2.0.0 - vitest: 3.0.6(@types/debug@4.1.12)(@types/node@22.13.5)(@vitest/ui@3.0.6)(jiti@2.4.2)(jsdom@26.1.0(canvas@3.1.2))(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + vitest: 3.0.6(@types/debug@4.1.12)(@types/node@22.13.5)(@vitest/ui@3.0.6)(jiti@2.4.2)(jsdom@26.1.0(canvas@3.1.2))(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) '@vitest/utils@3.0.6': dependencies: @@ -14955,6 +15259,8 @@ snapshots: acorn@8.14.1: {} + acorn@8.15.0: {} + agent-base@6.0.2: dependencies: debug: 4.4.1(supports-color@8.1.1) @@ -15117,7 +15423,7 @@ snapshots: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 @@ -15211,20 +15517,20 @@ snapshots: '@babel/types': 7.28.0 '@types/babel__core': 7.20.5 - babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.27.1): + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.27.1): dependencies: - '@babel/compat-data': 7.27.2 + '@babel/compat-data': 7.28.4 '@babel/core': 7.27.1 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.1) + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.27.1) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.28.0): + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4): dependencies: - '@babel/compat-data': 7.27.2 - '@babel/core': 7.28.0 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.28.0) + '@babel/compat-data': 7.28.4 + '@babel/core': 7.28.4 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -15232,30 +15538,30 @@ snapshots: babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.27.1): dependencies: '@babel/core': 7.27.1 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.1) - core-js-compat: 3.42.0 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.27.1) + core-js-compat: 3.45.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.28.0): + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.4): dependencies: - '@babel/core': 7.28.0 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.28.0) - core-js-compat: 3.42.0 + '@babel/core': 7.28.4 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) + core-js-compat: 3.45.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.27.1): + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.27.1): dependencies: '@babel/core': 7.27.1 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.1) + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.27.1) transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.28.0): + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.4): dependencies: - '@babel/core': 7.28.0 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.28.0) + '@babel/core': 7.28.4 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) transitivePeerDependencies: - supports-color @@ -15399,6 +15705,13 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.2(browserslist@4.24.4) + browserslist@4.25.4: + dependencies: + caniuse-lite: 1.0.30001741 + electron-to-chromium: 1.5.217 + node-releases: 2.0.20 + update-browserslist-db: 1.1.3(browserslist@4.25.4) + bser@2.1.1: dependencies: node-int64: 0.4.0 @@ -15475,6 +15788,8 @@ snapshots: caniuse-lite@1.0.30001700: {} + caniuse-lite@1.0.30001741: {} + canvas@3.1.2: dependencies: node-addon-api: 7.1.1 @@ -15771,9 +16086,11 @@ snapshots: confbox@0.1.8: {} + confbox@0.2.2: {} + connect-history-api-fallback@2.0.0: {} - consola@3.4.0: {} + consola@3.4.2: {} console.table@0.10.0: dependencies: @@ -15814,6 +16131,10 @@ snapshots: dependencies: browserslist: 4.24.4 + core-js-compat@3.45.1: + dependencies: + browserslist: 4.25.4 + core-util-is@1.0.2: {} core-util-is@1.0.3: {} @@ -15915,7 +16236,7 @@ snapshots: cspell-glob@8.19.4: dependencies: '@cspell/url': 8.19.4 - picomatch: 4.0.2 + picomatch: 4.0.3 cspell-glob@9.1.3: dependencies: @@ -16498,11 +16819,11 @@ snapshots: dependencies: node-source-walk: 7.0.0 - detective-postcss@7.0.0(postcss@8.5.3): + detective-postcss@7.0.0(postcss@8.5.6): dependencies: is-url: 1.2.4 - postcss: 8.5.3 - postcss-values-parser: 6.0.2(postcss@8.5.3) + postcss: 8.5.6 + postcss-values-parser: 6.0.2(postcss@8.5.6) detective-sass@6.0.0: dependencies: @@ -16604,10 +16925,12 @@ snapshots: ejs@3.1.10: dependencies: - jake: 10.9.2 + jake: 10.9.4 electron-to-chromium@1.5.101: {} + electron-to-chromium@1.5.217: {} + elkjs@0.9.3: {} emittery@0.13.1: {} @@ -16658,7 +16981,7 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-abstract@1.23.9: + es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -16687,7 +17010,9 @@ snapshots: is-array-buffer: 3.0.5 is-callable: 1.2.7 is-data-view: 1.0.2 + is-negative-zero: 2.0.3 is-regex: 1.2.1 + is-set: 2.0.3 is-shared-array-buffer: 1.0.4 is-string: 1.1.1 is-typed-array: 1.1.15 @@ -16702,6 +17027,7 @@ snapshots: safe-push-apply: 1.0.0 safe-regex-test: 1.1.0 set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 string.prototype.trim: 1.2.10 string.prototype.trimend: 1.0.9 string.prototype.trimstart: 1.0.8 @@ -16779,34 +17105,6 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.24.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.2 - '@esbuild/android-arm': 0.24.2 - '@esbuild/android-arm64': 0.24.2 - '@esbuild/android-x64': 0.24.2 - '@esbuild/darwin-arm64': 0.24.2 - '@esbuild/darwin-x64': 0.24.2 - '@esbuild/freebsd-arm64': 0.24.2 - '@esbuild/freebsd-x64': 0.24.2 - '@esbuild/linux-arm': 0.24.2 - '@esbuild/linux-arm64': 0.24.2 - '@esbuild/linux-ia32': 0.24.2 - '@esbuild/linux-loong64': 0.24.2 - '@esbuild/linux-mips64el': 0.24.2 - '@esbuild/linux-ppc64': 0.24.2 - '@esbuild/linux-riscv64': 0.24.2 - '@esbuild/linux-s390x': 0.24.2 - '@esbuild/linux-x64': 0.24.2 - '@esbuild/netbsd-arm64': 0.24.2 - '@esbuild/netbsd-x64': 0.24.2 - '@esbuild/openbsd-arm64': 0.24.2 - '@esbuild/openbsd-x64': 0.24.2 - '@esbuild/sunos-x64': 0.24.2 - '@esbuild/win32-arm64': 0.24.2 - '@esbuild/win32-ia32': 0.24.2 - '@esbuild/win32-x64': 0.24.2 - esbuild@0.25.0: optionalDependencies: '@esbuild/aix-ppc64': 0.25.0 @@ -16835,6 +17133,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.0 '@esbuild/win32-x64': 0.25.0 + esbuild@0.25.9: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -17038,7 +17365,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 esutils@1.0.0: {} @@ -17197,6 +17524,8 @@ snapshots: transitivePeerDependencies: - supports-color + exsolve@1.0.7: {} + extend@3.0.2: {} extendable-error@0.1.7: {} @@ -17316,6 +17645,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + ferrum@1.9.4: dependencies: fastestsmallesttextencoderdecoder: 1.0.22 @@ -17402,7 +17735,7 @@ snapshots: '@actions/core': 1.11.1 arg: 5.0.2 console.table: 0.10.0 - debug: 4.4.0 + debug: 4.4.1(supports-color@8.1.1) find-test-names: 1.29.5(@babel/core@7.27.1) globby: 11.1.0 minimatch: 3.1.2 @@ -18154,6 +18487,8 @@ snapshots: is-module@1.0.0: {} + is-negative-zero@2.0.3: {} + is-number-object@1.1.1: dependencies: call-bound: 1.0.4 @@ -18303,7 +18638,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.4.0 + debug: 4.4.1(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -18328,12 +18663,11 @@ snapshots: dependencies: '@isaacs/cliui': 8.0.2 - jake@10.9.2: + jake@10.9.4: dependencies: async: 3.2.6 - chalk: 4.1.2 filelist: 1.0.4 - minimatch: 3.1.2 + picocolors: 1.1.1 jest-changed-files@30.0.2: dependencies: @@ -18616,7 +18950,7 @@ snapshots: chalk: 4.1.2 ci-info: 4.2.0 graceful-fs: 4.2.11 - picomatch: 4.0.2 + picomatch: 4.0.3 jest-validate@30.0.2: dependencies: @@ -18936,6 +19270,12 @@ snapshots: mlly: 1.7.4 pkg-types: 1.3.1 + local-pkg@1.1.1: + dependencies: + mlly: 1.7.4 + pkg-types: 2.2.0 + quansync: 0.2.10 + locate-path@3.0.0: dependencies: p-locate: 3.0.0 @@ -19064,7 +19404,7 @@ snapshots: markdown-table@3.0.4: {} - marked@16.0.0: {} + marked@16.2.1: {} marked@4.3.0: {} @@ -19613,15 +19953,19 @@ snapshots: node-releases@2.0.19: {} + node-releases@2.0.20: {} + node-source-walk@7.0.0: dependencies: - '@babel/parser': 7.27.2 + '@babel/parser': 7.28.0 nomnom@1.5.2: dependencies: colors: 0.5.1 underscore: 1.1.7 + non-layered-tidy-tree-layout@2.0.2: {} + normalize-path@3.0.0: {} normalize-url@6.1.0: {} @@ -19873,6 +20217,8 @@ snapshots: package-manager-detector@0.2.9: {} + package-manager-detector@1.3.0: {} + pako@1.0.11: {} pako@2.1.0: {} @@ -19974,6 +20320,8 @@ snapshots: picomatch@4.0.2: {} + picomatch@4.0.3: {} + pidtree@0.6.0: {} pify@2.3.0: {} @@ -20047,6 +20395,12 @@ snapshots: mlly: 1.7.4 pathe: 2.0.3 + pkg-types@2.2.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.7 + pathe: 2.0.3 + plist@3.1.0: dependencies: '@xmldom/xmldom': 0.8.10 @@ -20101,11 +20455,11 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss-values-parser@6.0.2(postcss@8.5.3): + postcss-values-parser@6.0.2(postcss@8.5.6): dependencies: color-name: 1.1.4 is-url-superb: 4.0.0 - postcss: 8.5.3 + postcss: 8.5.6 quote-unquote: 1.0.0 postcss@8.5.3: @@ -20144,7 +20498,7 @@ snapshots: detective-amd: 6.0.0 detective-cjs: 6.0.0 detective-es6: 5.0.0 - detective-postcss: 7.0.0(postcss@8.5.3) + detective-postcss: 7.0.0(postcss@8.5.6) detective-sass: 6.0.0 detective-scss: 5.0.0 detective-stylus: 5.0.0 @@ -20152,7 +20506,7 @@ snapshots: detective-vue2: 2.0.3(typescript@5.7.3) module-definition: 6.0.0 node-source-walk: 7.0.0 - postcss: 8.5.3 + postcss: 8.5.6 typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -20228,6 +20582,8 @@ snapshots: dependencies: side-channel: 1.1.0 + quansync@0.2.10: {} + queue-microtask@1.2.3: {} quick-format-unescaped@4.0.4: {} @@ -20333,14 +20689,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 - regenerate-unicode-properties@10.2.0: + regenerate-unicode-properties@10.2.2: dependencies: regenerate: 1.4.2 @@ -20369,14 +20725,14 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - regexpu-core@6.2.0: + regexpu-core@6.3.1: dependencies: regenerate: 1.4.2 - regenerate-unicode-properties: 10.2.0 + regenerate-unicode-properties: 10.2.2 regjsgen: 0.8.0 regjsparser: 0.12.0 unicode-match-property-ecmascript: 2.0.0 - unicode-match-property-value-ecmascript: 2.2.0 + unicode-match-property-value-ecmascript: 2.2.1 regjsgen@0.8.0: {} @@ -20516,44 +20872,19 @@ snapshots: robust-predicates@3.0.2: {} - rollup-plugin-visualizer@6.0.3(rollup@4.40.2): + rollup-plugin-visualizer@6.0.3(rollup@4.50.1): dependencies: open: 8.4.2 picomatch: 4.0.2 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.40.2 + rollup: 4.50.1 rollup@2.79.2: optionalDependencies: fsevents: 2.3.3 - rollup@4.34.8: - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.34.8 - '@rollup/rollup-android-arm64': 4.34.8 - '@rollup/rollup-darwin-arm64': 4.34.8 - '@rollup/rollup-darwin-x64': 4.34.8 - '@rollup/rollup-freebsd-arm64': 4.34.8 - '@rollup/rollup-freebsd-x64': 4.34.8 - '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 - '@rollup/rollup-linux-arm-musleabihf': 4.34.8 - '@rollup/rollup-linux-arm64-gnu': 4.34.8 - '@rollup/rollup-linux-arm64-musl': 4.34.8 - '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 - '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 - '@rollup/rollup-linux-riscv64-gnu': 4.34.8 - '@rollup/rollup-linux-s390x-gnu': 4.34.8 - '@rollup/rollup-linux-x64-gnu': 4.34.8 - '@rollup/rollup-linux-x64-musl': 4.34.8 - '@rollup/rollup-win32-arm64-msvc': 4.34.8 - '@rollup/rollup-win32-ia32-msvc': 4.34.8 - '@rollup/rollup-win32-x64-msvc': 4.34.8 - fsevents: 2.3.3 - rollup@4.40.2: dependencies: '@types/estree': 1.0.7 @@ -20580,6 +20911,33 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.40.2 fsevents: 2.3.3 + rollup@4.50.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.50.1 + '@rollup/rollup-android-arm64': 4.50.1 + '@rollup/rollup-darwin-arm64': 4.50.1 + '@rollup/rollup-darwin-x64': 4.50.1 + '@rollup/rollup-freebsd-arm64': 4.50.1 + '@rollup/rollup-freebsd-x64': 4.50.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.1 + '@rollup/rollup-linux-arm-musleabihf': 4.50.1 + '@rollup/rollup-linux-arm64-gnu': 4.50.1 + '@rollup/rollup-linux-arm64-musl': 4.50.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.1 + '@rollup/rollup-linux-ppc64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-gnu': 4.50.1 + '@rollup/rollup-linux-riscv64-musl': 4.50.1 + '@rollup/rollup-linux-s390x-gnu': 4.50.1 + '@rollup/rollup-linux-x64-gnu': 4.50.1 + '@rollup/rollup-linux-x64-musl': 4.50.1 + '@rollup/rollup-openharmony-arm64': 4.50.1 + '@rollup/rollup-win32-arm64-msvc': 4.50.1 + '@rollup/rollup-win32-ia32-msvc': 4.50.1 + '@rollup/rollup-win32-x64-msvc': 4.50.1 + fsevents: 2.3.3 + roughjs@4.6.6(patch_hash=3543d47108cb41b68ec6a671c0e1f9d0cfe2ce524fea5b0992511ae84c3c6b64): dependencies: hachure-fill: 0.5.2 @@ -21017,7 +21375,7 @@ snapshots: spdy@4.0.2: dependencies: - debug: 4.4.0 + debug: 4.4.1(supports-color@8.1.1) handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -21034,7 +21392,7 @@ snapshots: deep-equal: 2.2.3 dependency-tree: 11.0.1 lazy-ass: 2.0.3 - tinyglobby: 0.2.12 + tinyglobby: 0.2.15 transitivePeerDependencies: - supports-color @@ -21089,6 +21447,11 @@ snapshots: dependencies: internal-slot: 1.1.0 + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + stream-combiner@0.0.4: dependencies: duplexer: 0.1.2 @@ -21123,7 +21486,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -21140,7 +21503,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.23.9 + es-abstract: 1.24.0 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -21350,10 +21713,10 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - terser@5.39.0: + terser@5.44.0: dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.14.1 + '@jridgewell/source-map': 0.3.11 + acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -21397,6 +21760,8 @@ snapshots: tinyexec@0.3.2: {} + tinyexec@1.0.1: {} + tinyglobby@0.2.12: dependencies: fdir: 6.4.3(picomatch@4.0.2) @@ -21407,6 +21772,11 @@ snapshots: fdir: 6.4.6(picomatch@4.0.2) picomatch: 4.0.2 + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + tinypool@1.0.2: {} tinyrainbow@2.0.0: {} @@ -21558,18 +21928,18 @@ snapshots: dependencies: is-typedarray: 1.0.0 - typedoc-plugin-markdown@4.4.2(typedoc@0.27.8(typescript@5.7.3)): + typedoc-plugin-markdown@4.8.1(typedoc@0.28.11(typescript@5.7.3)): dependencies: - typedoc: 0.27.8(typescript@5.7.3) + typedoc: 0.28.11(typescript@5.7.3) - typedoc@0.27.8(typescript@5.7.3): + typedoc@0.28.11(typescript@5.7.3): dependencies: - '@gerrit0/mini-shiki': 1.27.2 + '@gerrit0/mini-shiki': 3.12.0 lunr: 2.3.9 markdown-it: 14.1.0 minimatch: 9.0.5 typescript: 5.7.3 - yaml: 2.7.0 + yaml: 2.8.0 typescript-eslint@8.38.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.7.3): dependencies: @@ -21599,11 +21969,12 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - unconfig@7.0.0: + unconfig@7.3.2: dependencies: - '@antfu/utils': 8.1.1 + '@quansync/fs': 0.1.4 defu: 6.1.4 jiti: 2.4.2 + quansync: 0.2.10 underscore@1.1.7: {} @@ -21622,7 +21993,7 @@ snapshots: unicode-canonical-property-names-ecmascript: 2.0.1 unicode-property-aliases-ecmascript: 2.1.0 - unicode-match-property-value-ecmascript@2.2.0: {} + unicode-match-property-value-ecmascript@2.2.1: {} unicode-property-aliases-ecmascript@2.1.0: {} @@ -21689,32 +22060,32 @@ snapshots: universalify@2.0.1: {} - unocss@66.0.0(postcss@8.5.6)(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3)): + unocss@66.4.2(postcss@8.5.6)(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0)): dependencies: - '@unocss/astro': 66.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3)) - '@unocss/cli': 66.0.0 - '@unocss/core': 66.0.0 - '@unocss/postcss': 66.0.0(postcss@8.5.6) - '@unocss/preset-attributify': 66.0.0 - '@unocss/preset-icons': 66.0.0 - '@unocss/preset-mini': 66.0.0 - '@unocss/preset-tagify': 66.0.0 - '@unocss/preset-typography': 66.0.0 - '@unocss/preset-uno': 66.0.0 - '@unocss/preset-web-fonts': 66.0.0 - '@unocss/preset-wind': 66.0.0 - '@unocss/preset-wind3': 66.0.0 - '@unocss/transformer-attributify-jsx': 66.0.0 - '@unocss/transformer-compile-class': 66.0.0 - '@unocss/transformer-directives': 66.0.0 - '@unocss/transformer-variant-group': 66.0.0 - '@unocss/vite': 66.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(vue@3.5.13(typescript@5.7.3)) + '@unocss/astro': 66.4.2(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0)) + '@unocss/cli': 66.4.2 + '@unocss/core': 66.4.2 + '@unocss/postcss': 66.4.2(postcss@8.5.6) + '@unocss/preset-attributify': 66.4.2 + '@unocss/preset-icons': 66.4.2 + '@unocss/preset-mini': 66.4.2 + '@unocss/preset-tagify': 66.4.2 + '@unocss/preset-typography': 66.4.2 + '@unocss/preset-uno': 66.4.2 + '@unocss/preset-web-fonts': 66.4.2 + '@unocss/preset-wind': 66.4.2 + '@unocss/preset-wind3': 66.4.2 + '@unocss/preset-wind4': 66.4.2 + '@unocss/transformer-attributify-jsx': 66.4.2 + '@unocss/transformer-compile-class': 66.4.2 + '@unocss/transformer-directives': 66.4.2 + '@unocss/transformer-variant-group': 66.4.2 + '@unocss/vite': 66.4.2(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0)) optionalDependencies: - vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + vite: 7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) transitivePeerDependencies: - postcss - supports-color - - vue unpipe@1.0.0: {} @@ -21723,7 +22094,7 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.2 - unplugin-vue-components@28.4.0(@babel/parser@7.28.0)(vue@3.5.13(typescript@5.7.3)): + unplugin-vue-components@28.4.0(@babel/parser@7.28.4)(vue@3.5.13(typescript@5.7.3)): dependencies: chokidar: 3.6.0 debug: 4.4.0 @@ -21735,7 +22106,7 @@ snapshots: unplugin-utils: 0.2.4 vue: 3.5.13(typescript@5.7.3) optionalDependencies: - '@babel/parser': 7.28.0 + '@babel/parser': 7.28.4 transitivePeerDependencies: - supports-color @@ -21784,6 +22155,12 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-browserslist-db@1.1.3(browserslist@4.25.4): + dependencies: + browserslist: 4.25.4 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -21826,13 +22203,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@3.0.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0): + vite-node@3.0.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0): dependencies: cac: 6.7.14 debug: 4.4.1(supports-color@8.1.1) es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + vite: 6.3.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) transitivePeerDependencies: - '@types/node' - jiti @@ -21847,7 +22224,7 @@ snapshots: - tsx - yaml - vite-plugin-istanbul@7.0.0(vite@7.0.3(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)): + vite-plugin-istanbul@7.0.0(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0)): dependencies: '@istanbuljs/load-nyc-config': 1.1.0 espree: 10.3.0 @@ -21855,22 +22232,22 @@ snapshots: picocolors: 1.1.1 source-map: 0.7.4 test-exclude: 7.0.1 - vite: 7.0.3(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + vite: 7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) transitivePeerDependencies: - supports-color - vite-plugin-pwa@1.0.0(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0): + vite-plugin-pwa@1.0.0(vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.3.0): dependencies: debug: 4.4.0 pretty-bytes: 6.1.1 tinyglobby: 0.2.12 - vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + vite: 7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) workbox-build: 7.1.1(@types/babel__core@7.20.5) workbox-window: 7.3.0 transitivePeerDependencies: - supports-color - vite@5.4.19(@types/node@22.13.5)(terser@5.39.0): + vite@5.4.19(@types/node@22.13.5)(terser@5.44.0): dependencies: esbuild: 0.21.5 postcss: 8.5.6 @@ -21878,61 +22255,51 @@ snapshots: optionalDependencies: '@types/node': 22.13.5 fsevents: 2.3.3 - terser: 5.39.0 + terser: 5.44.0 - vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0): - dependencies: - esbuild: 0.24.2 - postcss: 8.5.3 - rollup: 4.34.8 - optionalDependencies: - '@types/node': 22.13.5 - fsevents: 2.3.3 - jiti: 2.4.2 - terser: 5.39.0 - tsx: 4.19.3 - yaml: 2.8.0 - - vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0): - dependencies: - esbuild: 0.24.2 - postcss: 8.5.6 - rollup: 4.40.2 - optionalDependencies: - '@types/node': 22.13.5 - fsevents: 2.3.3 - jiti: 2.4.2 - terser: 5.39.0 - tsx: 4.19.3 - yaml: 2.8.0 - - vite@7.0.3(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0): + vite@6.3.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0): dependencies: esbuild: 0.25.0 - fdir: 6.4.6(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.40.2 - tinyglobby: 0.2.14 + rollup: 4.50.1 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.13.5 fsevents: 2.3.3 jiti: 2.4.2 - terser: 5.39.0 + terser: 5.44.0 tsx: 4.19.3 yaml: 2.8.0 - vitepress-plugin-search@1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.39.0)(typescript@5.7.3))(vue@3.5.13(typescript@5.7.3)): + vite@7.0.7(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0): + dependencies: + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.50.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.13.5 + fsevents: 2.3.3 + jiti: 2.4.2 + terser: 5.44.0 + tsx: 4.19.3 + yaml: 2.8.0 + + vitepress-plugin-search@1.0.4-alpha.22(flexsearch@0.7.43)(vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.44.0)(typescript@5.7.3))(vue@3.5.13(typescript@5.7.3)): 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.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.39.0)(typescript@5.7.3) + vitepress: 1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.44.0)(typescript@5.7.3) vue: 3.5.13(typescript@5.7.3) - vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.39.0)(typescript@5.7.3): + vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@22.13.5)(axios@1.8.4)(postcss@8.5.6)(search-insights@2.17.2)(terser@5.44.0)(typescript@5.7.3): dependencies: '@docsearch/css': 3.8.2 '@docsearch/js': 3.8.2(@algolia/client-search@5.20.3)(search-insights@2.17.2) @@ -21941,7 +22308,7 @@ snapshots: '@shikijs/transformers': 2.5.0 '@shikijs/types': 2.5.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.1(vite@5.4.19(@types/node@22.13.5)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3)) + '@vitejs/plugin-vue': 5.2.1(vite@5.4.19(@types/node@22.13.5)(terser@5.44.0))(vue@3.5.13(typescript@5.7.3)) '@vue/devtools-api': 7.7.2 '@vue/shared': 3.5.13 '@vueuse/core': 12.7.0(typescript@5.7.3) @@ -21950,7 +22317,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.1.2 shiki: 2.5.0 - vite: 5.4.19(@types/node@22.13.5)(terser@5.39.0) + vite: 5.4.19(@types/node@22.13.5)(terser@5.44.0) vue: 3.5.13(typescript@5.7.3) optionalDependencies: postcss: 8.5.6 @@ -21981,10 +22348,10 @@ snapshots: - typescript - universal-cookie - vitest@3.0.6(@types/debug@4.1.12)(@types/node@22.13.5)(@vitest/ui@3.0.6)(jiti@2.4.2)(jsdom@26.1.0(canvas@3.1.2))(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0): + vitest@3.0.6(@types/debug@4.1.12)(@types/node@22.13.5)(@vitest/ui@3.0.6)(jiti@2.4.2)(jsdom@26.1.0(canvas@3.1.2))(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0): dependencies: '@vitest/expect': 3.0.6 - '@vitest/mocker': 3.0.6(vite@6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0)) + '@vitest/mocker': 3.0.6(vite@6.3.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0)) '@vitest/pretty-format': 3.0.6 '@vitest/runner': 3.0.6 '@vitest/snapshot': 3.0.6 @@ -22000,8 +22367,8 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.1.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) - vite-node: 3.0.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.0) + vite: 6.3.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) + vite-node: 3.0.6(@types/node@22.13.5)(jiti@2.4.2)(terser@5.44.0)(tsx@4.19.3)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -22051,9 +22418,7 @@ snapshots: vscode-uri@3.1.0: {} - vue-flow-layout@0.1.1(vue@3.5.13(typescript@5.7.3)): - dependencies: - vue: 3.5.13(typescript@5.7.3) + vue-flow-layout@0.2.0: {} vue@3.5.13(typescript@5.7.3): dependencies: @@ -22374,10 +22739,10 @@ snapshots: 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.28.0 - '@babel/preset-env': 7.27.2(@babel/core@7.28.0) - '@babel/runtime': 7.27.1 - '@rollup/plugin-babel': 5.3.1(@babel/core@7.28.0)(@types/babel__core@7.20.5)(rollup@2.79.2) + '@babel/core': 7.28.4 + '@babel/preset-env': 7.28.3(@babel/core@7.28.4) + '@babel/runtime': 7.28.4 + '@rollup/plugin-babel': 5.3.1(@babel/core@7.28.4)(@types/babel__core@7.20.5)(rollup@2.79.2) '@rollup/plugin-node-resolve': 15.3.1(rollup@2.79.2) '@rollup/plugin-replace': 2.4.2(rollup@2.79.2) '@rollup/plugin-terser': 0.4.4(rollup@2.79.2) @@ -22542,8 +22907,6 @@ snapshots: yallist@3.1.1: {} - yaml@2.7.0: {} - yaml@2.8.0: {} yargs-parser@18.1.3: diff --git a/scripts/editor.bash b/scripts/editor.bash index d04246cc7..32d2f60ee 100755 --- a/scripts/editor.bash +++ b/scripts/editor.bash @@ -11,7 +11,6 @@ pushd packages/mermaid # Append commit hash to version jq ".version = .version + \"+${COMMIT_REF:0:7}\"" package.json > package.tmp.json mv package.tmp.json package.json -yarn link popd pnpm run -r clean @@ -26,13 +25,14 @@ cd mermaid-live-editor git clean -xdf rm -rf docs/ -# We have to use npm instead of yarn because it causes trouble in netlify +# Tells PNPM that mermaid-live-editor is not part of this workspace +touch pnpm-workspace.yaml + # Install dependencies -yarn install +pnpm install --frozen-lockfile # Link local mermaid to live editor -yarn link mermaid +pnpm link ../packages/mermaid # Force Build the site -yarn run build - +pnpm run build