Compare commits

..

10 Commits

Author SHA1 Message Date
Shubham P
ea8260aa51 Merge branch 'develop' into 4743-timeline-html-formatting 2025-08-26 12:12:59 +05:30
darshanr0107
dabc220ed2 fix: use new frontmatter in test case
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2025-08-20 18:12:44 +05:30
darshanr0107
d2c5cbd408 Merge branch 'develop' into 4743-timeline-html-formatting 2025-07-11 17:36:16 +05:30
darshanr0107
20467bcbe6 resolve visual regression issues and fix broken diagram in PR 2025-07-11 13:53:17 +05:30
darshanr0107
dd213fe86c Merge branch 'develop' into 4743-timeline-html-formatting 2025-07-01 12:34:09 +05:30
darshanr0107
800f23fc01 Merge branch 'develop' into 4743-timeline-html-formatting 2025-06-25 13:09:25 +05:30
darshanr0107
67aa1a4dc1 Merge branch 'develop' into 4743-timeline-html-formatting 2025-06-20 11:50:35 +05:30
darshanr0107
c1bcdcfbad added changeset 2025-06-20 11:49:37 +05:30
darshanr0107
f528e2daa4 fix multicharacter sanitization 2025-06-19 12:59:51 +05:30
darshanr0107
a867842f32 fix HTML formatting in timeline diagrams 2025-06-19 12:05:22 +05:30
27 changed files with 421 additions and 1413 deletions

View File

@@ -0,0 +1,5 @@
---
'mermaid': major
---
Currently, HTML tags such as <em>, <strong>, <sup>, <a>, <ul>, and <li> are supported in Flowchart and Class diagram labels but not in Timeline diagrams. This change introduces support for basic HTML formatting in Timeline labels, enabling richer text formatting and better usability for multi-line content like descriptions, footnotes, and styled annotations

View File

@@ -1,5 +0,0 @@
---
'mermaid': minor
---
feat: Add support for aggregation relationships in ER diagram

View File

@@ -23,6 +23,9 @@ 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

View File

@@ -58,7 +58,7 @@ jobs:
echo "EOF" >> $GITHUB_OUTPUT
- name: Commit and create pull request
uses: peter-evans/create-pull-request@18e469570b1cf0dfc11d60ec121099f8ff3e617a
uses: peter-evans/create-pull-request@cb4d3bfce175d44325c6b7697f81e0afe8a79bdf
with:
add-paths: |
cypress/timings.json

View File

@@ -369,94 +369,4 @@ ORDER ||--|{ LINE-ITEM : contains
);
});
});
describe('Aggregation Relationships', () => {
it('should render basic aggregation relationships', () => {
imgSnapshotTest(
`
erDiagram
DEPARTMENT <> EMPLOYEE : contains
PROJECT <>.. TASK : manages
TEAM <> MEMBER : consists_of
`,
{ logLevel: 1 }
);
});
it('should render aggregation with entity attributes', () => {
imgSnapshotTest(
`
erDiagram
DEPARTMENT <> EMPLOYEE : contains
DEPARTMENT {
int id PK
string name
string location
}
EMPLOYEE {
int id PK
string name
int department_id FK
}
`,
{ logLevel: 1 }
);
});
it('should render aggregation with quoted labels', () => {
imgSnapshotTest(
`
erDiagram
UNIVERSITY <> COLLEGE : "has multiple"
COLLEGE <> DEPARTMENT : "contains"
DEPARTMENT <> FACULTY : "employs"
`,
{ logLevel: 1 }
);
});
it('should render mixed relationship types', () => {
imgSnapshotTest(
`
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ ORDER_ITEM : contains
PRODUCT <> ORDER_ITEM : "aggregated in"
WAREHOUSE <>.. PRODUCT : "stores"
`,
{ logLevel: 1 }
);
});
it('should render aggregation with entity aliases', () => {
imgSnapshotTest(
`
erDiagram
d[DEPARTMENT]
e[EMPLOYEE]
p[PROJECT]
t[TASK]
d <> e : contains
p <>.. t : manages
`,
{ logLevel: 1 }
);
});
it('should render complex aggregation scenarios', () => {
imgSnapshotTest(
`
erDiagram
COMPANY <> DEPARTMENT : owns
DEPARTMENT <> EMPLOYEE : contains
EMPLOYEE <> PROJECT : works_on
PROJECT <> TASK : consists_of
TASK <> SUBTASK : includes
`,
{ logLevel: 1 }
);
});
});
});

View File

@@ -225,4 +225,24 @@ timeline
{}
);
});
it('13: should render markdown htmlLabels', () => {
imgSnapshotTest(
`---
config:
theme: forest
---
timeline
title Timeline of Industrial Revolution
section 17th-20th century
Industry 1.0 : Machinery, Water power, Steam <br>power
Industry 2.0 : Electricity, <strong>Internal combustion engine </strong>, Mass production
Industry 3.0 : Electronics, Computers, Automation
section 21st century
Industry 4.0 : Internet, Robotics, Internet of Things
Industry 5.0 : Artificial intelligence, Big data, 3D printing
`,
{}
);
});
});

View File

@@ -2,223 +2,219 @@
"durations": [
{
"spec": "cypress/integration/other/configuration.spec.js",
"duration": 6162
"duration": 6297
},
{
"spec": "cypress/integration/other/external-diagrams.spec.js",
"duration": 2148
"duration": 2187
},
{
"spec": "cypress/integration/other/ghsa.spec.js",
"duration": 3585
"duration": 3509
},
{
"spec": "cypress/integration/other/iife.spec.js",
"duration": 2099
"duration": 2218
},
{
"spec": "cypress/integration/other/interaction.spec.js",
"duration": 12119
"duration": 12104
},
{
"spec": "cypress/integration/other/rerender.spec.js",
"duration": 2063
"duration": 2151
},
{
"spec": "cypress/integration/other/xss.spec.js",
"duration": 31921
"duration": 33064
},
{
"spec": "cypress/integration/rendering/appli.spec.js",
"duration": 3385
"duration": 3488
},
{
"spec": "cypress/integration/rendering/architecture.spec.ts",
"duration": 108
"duration": 106
},
{
"spec": "cypress/integration/rendering/block.spec.js",
"duration": 18063
"duration": 18317
},
{
"spec": "cypress/integration/rendering/c4.spec.js",
"duration": 5519
"duration": 5592
},
{
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
"duration": 40040
"duration": 39358
},
{
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
"duration": 38665
"duration": 37160
},
{
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
"duration": 22836
"duration": 23660
},
{
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
"duration": 37096
"duration": 36866
},
{
"spec": "cypress/integration/rendering/classDiagram.spec.js",
"duration": 16452
"duration": 17334
},
{
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
"duration": 10387
"duration": 9871
},
{
"spec": "cypress/integration/rendering/current.spec.js",
"duration": 2803
"duration": 2833
},
{
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
"duration": 86891
"duration": 85321
},
{
"spec": "cypress/integration/rendering/erDiagram.spec.js",
"duration": 15206
"duration": 15673
},
{
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
"duration": 3540
"duration": 3724
},
{
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
"duration": 41975
"duration": 41178
},
{
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
"duration": 30909
"duration": 29966
},
{
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
"duration": 7881
"duration": 7689
},
{
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
"duration": 24294
"duration": 24709
},
{
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
"duration": 47652
"duration": 45565
},
{
"spec": "cypress/integration/rendering/flowchart.spec.js",
"duration": 32049
"duration": 31144
},
{
"spec": "cypress/integration/rendering/gantt.spec.js",
"duration": 20248
"duration": 20808
},
{
"spec": "cypress/integration/rendering/gitGraph.spec.js",
"duration": 51202
"duration": 49985
},
{
"spec": "cypress/integration/rendering/iconShape.spec.ts",
"duration": 283546
"duration": 273272
},
{
"spec": "cypress/integration/rendering/imageShape.spec.ts",
"duration": 57257
"duration": 55880
},
{
"spec": "cypress/integration/rendering/info.spec.ts",
"duration": 3352
"duration": 3271
},
{
"spec": "cypress/integration/rendering/journey.spec.js",
"duration": 7423
"duration": 7293
},
{
"spec": "cypress/integration/rendering/kanban.spec.ts",
"duration": 7804
"duration": 7861
},
{
"spec": "cypress/integration/rendering/katex.spec.js",
"duration": 3847
"duration": 3922
},
{
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
"duration": 2637
"duration": 2726
},
{
"spec": "cypress/integration/rendering/mindmap.spec.ts",
"duration": 11658
"duration": 11670
},
{
"spec": "cypress/integration/rendering/newShapes.spec.ts",
"duration": 149500
"duration": 146020
},
{
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
"duration": 115427
"duration": 114244
},
{
"spec": "cypress/integration/rendering/packet.spec.ts",
"duration": 4801
"duration": 5036
},
{
"spec": "cypress/integration/rendering/pie.spec.ts",
"duration": 6786
"duration": 6545
},
{
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
"duration": 9422
"duration": 9097
},
{
"spec": "cypress/integration/rendering/radar.spec.js",
"duration": 5652
"duration": 5676
},
{
"spec": "cypress/integration/rendering/requirement.spec.js",
"duration": 2787
"duration": 2795
},
{
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
"duration": 53631
"duration": 51660
},
{
"spec": "cypress/integration/rendering/sankey.spec.ts",
"duration": 7075
},
{
"spec": "cypress/integration/rendering/sequencediagram-v2.spec.js",
"duration": 20446
"duration": 6957
},
{
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
"duration": 37326
"duration": 36026
},
{
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
"duration": 29208
"duration": 29551
},
{
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
"duration": 16328
"duration": 17364
},
{
"spec": "cypress/integration/rendering/theme.spec.js",
"duration": 30541
"duration": 30209
},
{
"spec": "cypress/integration/rendering/timeline.spec.ts",
"duration": 8611
"duration": 8699
},
{
"spec": "cypress/integration/rendering/treemap.spec.ts",
"duration": 11878
"duration": 12168
},
{
"spec": "cypress/integration/rendering/xyChart.spec.js",
"duration": 20400
"duration": 21453
},
{
"spec": "cypress/integration/rendering/zenuml.spec.js",
"duration": 3528
"duration": 3577
}
]
}

View File

@@ -169,164 +169,6 @@
</pre>
<hr />
<!-- Aggregation Examples -->
<h2>Aggregation Examples</h2>
<h3>Basic Aggregation (Solid Line)</h3>
<pre class="mermaid">
erDiagram
DEPARTMENT <> EMPLOYEE : contains
DEPARTMENT {
int id PK
string name
string location
}
EMPLOYEE {
int id PK
string name
int department_id FK
}
</pre>
<hr />
<h3>Dashed Aggregation</h3>
<pre class="mermaid">
erDiagram
PROJECT <>.. TASK : manages
PROJECT {
int id PK
string name
string description
}
TASK {
int id PK
string title
int project_id FK
}
</pre>
<hr />
<h3>Aggregation with Different Cardinalities</h3>
<pre class="mermaid">
erDiagram
COMPANY <> DEPARTMENT : owns
DEPARTMENT <> EMPLOYEE : contains
EMPLOYEE <> PROJECT : works_on
PROJECT <> TASK : consists_of
COMPANY {
int id PK
string name
}
DEPARTMENT {
int id PK
string name
int company_id FK
}
EMPLOYEE {
int id PK
string name
int department_id FK
}
PROJECT {
int id PK
string name
int employee_id FK
}
TASK {
int id PK
string title
int project_id FK
}
</pre>
<hr />
<h3>Two-way Aggregation</h3>
<pre class="mermaid">
erDiagram
TEAM <> MEMBER : consists_of
TEAM {
int id PK
string name
}
MEMBER {
int id PK
string name
int team_id FK
}
</pre>
<hr />
<h3>Complex Aggregation with Labels</h3>
<pre class="mermaid">
erDiagram
UNIVERSITY <> COLLEGE : "has multiple"
COLLEGE <> DEPARTMENT : "contains"
DEPARTMENT <> FACULTY : "employs"
FACULTY <> STUDENT : "teaches"
UNIVERSITY {
int id PK
string name
}
COLLEGE {
int id PK
string name
int university_id FK
}
DEPARTMENT {
int id PK
string name
int college_id FK
}
FACULTY {
int id PK
string name
int department_id FK
}
STUDENT {
int id PK
string name
int faculty_id FK
}
</pre>
<hr />
<h3>Mixed Relationship Types</h3>
<pre class="mermaid">
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ ORDER_ITEM : contains
PRODUCT <> ORDER_ITEM : "aggregated in"
WAREHOUSE <>.. PRODUCT : "stores"
CUSTOMER {
int id PK
string name
}
ORDER {
int id PK
int customer_id FK
date order_date
}
ORDER_ITEM {
int id PK
int order_id FK
int product_id FK
int quantity
}
PRODUCT {
int id PK
string name
int warehouse_id FK
}
WAREHOUSE {
int id PK
string name
}
</pre>
<hr />
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({

View File

@@ -19,7 +19,6 @@
- [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)

View File

@@ -1,19 +0,0 @@
> **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)

View File

@@ -209,42 +209,6 @@ erDiagram
PERSON many(0) optionally to 0+ NAMED-DRIVER : is
```
### Aggregation
Aggregation represents a "has-a" relationship where the part can exist independently of the whole. This is different from composition, where the part cannot exist without the whole. Aggregation relationships are rendered with hollow diamond markers at the endpoints.
| Value | Alias for | Description |
| :---: | :------------------: | ------------------------------ |
| <> | _aggregation_ | Basic aggregation (solid line) |
| <>.. | _aggregation-dashed_ | Dashed aggregation line |
**Examples:**
```mermaid-example
erDiagram
DEPARTMENT <> EMPLOYEE : contains
PROJECT <>.. TASK : manages
TEAM <> MEMBER : consists_of
```
```mermaid
erDiagram
DEPARTMENT <> EMPLOYEE : contains
PROJECT <>.. TASK : manages
TEAM <> MEMBER : consists_of
```
In these examples:
- `DEPARTMENT <> EMPLOYEE` shows that a department contains employees (aggregation)
- `PROJECT <>.. TASK` shows that a project manages tasks (dashed aggregation)
- `TEAM <> MEMBER` shows that a team consists of members (aggregation)
**Aggregation vs Association**
- **Aggregation** (`<>`): "Has-a" relationship where parts can exist independently
- **Association** (`||--`, `}o--`): General relationship between entities
### Attributes
Attributes can be defined for entities by specifying the entity name followed by a block containing multiple `type name` pairs, where a block is delimited by an opening `{` and a closing `}`. The attributes are rendered inside the entity boxes. For example:

View File

@@ -68,7 +68,7 @@
},
"dependencies": {
"@braintree/sanitize-url": "^7.0.4",
"@iconify/utils": "^3.0.1",
"@iconify/utils": "^2.1.33",
"@mermaid-js/parser": "workspace:^",
"@types/d3": "^7.4.3",
"cytoscape": "^3.29.3",

View File

@@ -78,187 +78,3 @@ 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();
});
});

View File

@@ -248,17 +248,3 @@ 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;
};

View File

@@ -2,7 +2,6 @@ import { log } from '../../logger.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import type { Edge, Node } from '../../rendering-util/types.js';
import type { EntityNode, Attribute, Relationship, EntityClass, RelSpec } from './erTypes.js';
import { AggregationType } from './erTypes.js';
import {
setAccTitle,
getAccTitle,
@@ -34,11 +33,6 @@ export class ErDB implements DiagramDB {
IDENTIFYING: 'IDENTIFYING',
};
private Aggregation = {
AGGREGATION: AggregationType.AGGREGATION,
AGGREGATION_DASHED: AggregationType.AGGREGATION_DASHED,
};
constructor() {
this.clear();
this.addEntity = this.addEntity.bind(this);
@@ -137,31 +131,6 @@ export class ErDB implements DiagramDB {
return this.relationships;
}
/**
* Validate aggregation relationship
* @param rSpec - The relationship specification to validate
* @returns boolean indicating if the aggregation relationship is valid
*/
public validateAggregationRelationship(rSpec: RelSpec): boolean {
const isAggregation =
rSpec.relType === this.Aggregation.AGGREGATION ||
rSpec.relType === this.Aggregation.AGGREGATION_DASHED;
if (!isAggregation) {
return false;
}
const validCardinalities = [
this.Cardinality.ZERO_OR_ONE,
this.Cardinality.ZERO_OR_MORE,
this.Cardinality.ONE_OR_MORE,
this.Cardinality.ONLY_ONE,
this.Cardinality.MD_PARENT,
];
return validCardinalities.includes(rSpec.cardA) && validCardinalities.includes(rSpec.cardB);
}
public getDirection() {
return this.direction;
}
@@ -279,17 +248,4 @@ export class ErDB implements DiagramDB {
public setDiagramTitle = setDiagramTitle;
public getDiagramTitle = getDiagramTitle;
public getConfig = () => getConfig().er;
// Getter methods for aggregation constants
public get AggregationConstants() {
return this.Aggregation;
}
public get CardinalityConstants() {
return this.Cardinality;
}
public get IdentificationConstants() {
return this.Identification;
}
}

View File

@@ -9,10 +9,6 @@ const ERMarkers = {
ZERO_OR_MORE_END: 'ZERO_OR_MORE_END',
MD_PARENT_END: 'MD_PARENT_END',
MD_PARENT_START: 'MD_PARENT_START',
AGGREGATION_START: 'AGGREGATION_START',
AGGREGATION_END: 'AGGREGATION_END',
AGGREGATION_DASHED_START: 'AGGREGATION_DASHED_START',
AGGREGATION_DASHED_END: 'AGGREGATION_DASHED_END',
};
/**
@@ -184,66 +180,6 @@ const insertMarkers = function (elem, conf) {
.attr('fill', 'none')
.attr('d', 'M21,18 Q39,0 57,18 Q39,36 21,18');
// Aggregation markers (hollow diamond)
elem
.append('defs')
.append('marker')
.attr('id', ERMarkers.AGGREGATION_START)
.attr('refX', 0)
.attr('refY', 9)
.attr('markerWidth', 20)
.attr('markerHeight', 18)
.attr('orient', 'auto')
.append('path')
.attr('stroke', conf.stroke)
.attr('fill', 'white')
.attr('d', 'M18,9 L9,0 L0,9 L9,18 Z');
elem
.append('defs')
.append('marker')
.attr('id', ERMarkers.AGGREGATION_END)
.attr('refX', 20)
.attr('refY', 9)
.attr('markerWidth', 20)
.attr('markerHeight', 18)
.attr('orient', 'auto')
.append('path')
.attr('stroke', conf.stroke)
.attr('fill', 'white')
.attr('d', 'M2,9 L11,0 L20,9 L11,18 Z');
// Dashed aggregation markers
elem
.append('defs')
.append('marker')
.attr('id', ERMarkers.AGGREGATION_DASHED_START)
.attr('refX', 0)
.attr('refY', 9)
.attr('markerWidth', 20)
.attr('markerHeight', 18)
.attr('orient', 'auto')
.append('path')
.attr('stroke', conf.stroke)
.attr('fill', 'white')
.attr('stroke-dasharray', '3,3')
.attr('d', 'M18,9 L9,0 L0,9 L9,18 Z');
elem
.append('defs')
.append('marker')
.attr('id', ERMarkers.AGGREGATION_DASHED_END)
.attr('refX', 20)
.attr('refY', 9)
.attr('markerWidth', 20)
.attr('markerHeight', 18)
.attr('orient', 'auto')
.append('path')
.attr('stroke', conf.stroke)
.attr('fill', 'white')
.attr('stroke-dasharray', '3,3')
.attr('d', 'M2,9 L11,0 L20,9 L11,18 Z');
return;
};

View File

@@ -448,11 +448,6 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
svgPath.attr('stroke-dasharray', '8,8');
}
// Handle aggregation relationship styling
if (rel.relSpec.relType === diagObj.db.Aggregation.AGGREGATION_DASHED) {
svgPath.attr('stroke-dasharray', '8,8');
}
// TODO: Understand this better
let url = '';
if (conf.arrowMarkerAbsolute) {
@@ -508,15 +503,6 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
break;
}
// Handle aggregation markers
if (
rel.relSpec.relType === diagObj.db.Aggregation.AGGREGATION ||
rel.relSpec.relType === diagObj.db.Aggregation.AGGREGATION_DASHED
) {
// Add aggregation marker at the start (entity B side)
svgPath.attr('marker-start', 'url(' + url + '#' + erMarkers.ERMarkers.AGGREGATION_START + ')');
}
// Now label the relationship
// Find the half-way point

View File

@@ -35,15 +35,3 @@ export interface EntityClass {
styles: string[];
textStyles: string[];
}
// Aggregation relationship types
export const AggregationType = {
AGGREGATION: 'AGGREGATION',
AGGREGATION_DASHED: 'AGGREGATION_DASHED',
} as const;
// Line types for aggregation
export const AggregationLineType = {
SOLID: 'SOLID',
DASHED: 'DASHED',
} as const;

View File

@@ -72,8 +72,6 @@ o\| return 'ZERO_OR_ONE';
o\{ return 'ZERO_OR_MORE';
\|\{ return 'ONE_OR_MORE';
\s*u return 'MD_PARENT';
"<>.." return 'AGGREGATION_DASHED';
"<>" return 'AGGREGATION';
\.\. return 'NON_IDENTIFYING';
\-\- return 'IDENTIFYING';
"to" return 'IDENTIFYING';
@@ -169,47 +167,6 @@ statement
| entityName SQS entityName SQE STYLE_SEPARATOR idList BLOCK_START BLOCK_STOP { yy.addEntity($1, $3); yy.setClass([$1], $6); }
| entityName SQS entityName SQE { yy.addEntity($1, $3); }
| entityName SQS entityName SQE STYLE_SEPARATOR idList { yy.addEntity($1, $3); yy.setClass([$1], $6); }
| entityName aggregationRelSpec entityName COLON role
{
yy.addEntity($1);
yy.addEntity($3);
yy.addRelationship($1, $5, $3, $2);
}
| entityName STYLE_SEPARATOR idList aggregationRelSpec entityName STYLE_SEPARATOR idList COLON role
{
yy.addEntity($1);
yy.addEntity($5);
yy.addRelationship($1, $9, $5, $4);
yy.setClass([$1], $3);
yy.setClass([$5], $7);
}
| entityName STYLE_SEPARATOR idList aggregationRelSpec entityName COLON role
{
yy.addEntity($1);
yy.addEntity($5);
yy.addRelationship($1, $7, $5, $4);
yy.setClass([$1], $3);
}
| entityName aggregationRelSpec entityName STYLE_SEPARATOR idList COLON role
{
yy.addEntity($1);
yy.addEntity($3);
yy.addRelationship($1, $7, $3, $2);
yy.setClass([$3], $5);
}
| entityName 'AGGREGATION' entityName COLON role
{
yy.addEntity($1);
yy.addEntity($3);
yy.addRelationship($1, $5, $3, { cardA: 'ZERO_OR_MORE', relType: 'AGGREGATION', cardB: 'ZERO_OR_MORE' });
}
| entityName 'AGGREGATION_DASHED' entityName COLON role
{
yy.addEntity($1);
yy.addEntity($3);
yy.addRelationship($1, $5, $3, { cardA: 'ZERO_OR_MORE', relType: 'AGGREGATION_DASHED', cardB: 'ZERO_OR_MORE' });
}
| title title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
@@ -315,17 +272,6 @@ relSpec
}
;
aggregationRelSpec
: 'AGGREGATION' cardinality cardinality
{
$$ = { cardA: $2, relType: $1, cardB: $3 };
}
| 'AGGREGATION_DASHED' cardinality cardinality
{
$$ = { cardA: $2, relType: $1, cardB: $3 };
}
;
cardinality
: 'ZERO_OR_ONE' { $$ = yy.Cardinality.ZERO_OR_ONE; }
| 'ZERO_OR_MORE' { $$ = yy.Cardinality.ZERO_OR_MORE; }
@@ -337,8 +283,6 @@ cardinality
relType
: 'NON_IDENTIFYING' { $$ = yy.Identification.NON_IDENTIFYING; }
| 'IDENTIFYING' { $$ = yy.Identification.IDENTIFYING; }
| 'AGGREGATION' { $$ = yy.Aggregation.AGGREGATION; }
| 'AGGREGATION_DASHED' { $$ = yy.Aggregation.AGGREGATION_DASHED; }
;
role

View File

@@ -1001,75 +1001,4 @@ describe('when parsing ER diagram it...', function () {
}
);
});
describe('aggregation relationships', function () {
it('should parse basic aggregation syntax', function () {
erDiagram.parser.parse('erDiagram\nDEPARTMENT <> EMPLOYEE : contains');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.relType).toBe(erDb.Aggregation.AGGREGATION);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].roleA).toBe('contains');
});
it('should parse dashed aggregation syntax', function () {
erDiagram.parser.parse('erDiagram\nPROJECT <>.. TASK : manages');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.relType).toBe(erDb.Aggregation.AGGREGATION_DASHED);
expect(rels[0].relSpec.cardA).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].relSpec.cardB).toBe(erDb.Cardinality.ZERO_OR_MORE);
expect(rels[0].roleA).toBe('manages');
});
it('should parse aggregation with quoted labels', function () {
erDiagram.parser.parse('erDiagram\nUNIVERSITY <> COLLEGE : "has multiple"');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.relType).toBe(erDb.Aggregation.AGGREGATION);
expect(rels[0].roleA).toBe('has multiple');
});
it('should parse multiple aggregation relationships', function () {
erDiagram.parser.parse(
'erDiagram\nDEPARTMENT <> EMPLOYEE : contains\nPROJECT <>.. TASK : manages'
);
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(4);
expect(rels.length).toBe(2);
expect(rels[0].relSpec.relType).toBe(erDb.Aggregation.AGGREGATION);
expect(rels[1].relSpec.relType).toBe(erDb.Aggregation.AGGREGATION_DASHED);
});
it('should parse aggregation with entity aliases', function () {
erDiagram.parser.parse('erDiagram\nd[DEPARTMENT]\ne[EMPLOYEE]\nd <> e : contains');
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(2);
expect(rels.length).toBe(1);
expect(rels[0].relSpec.relType).toBe(erDb.Aggregation.AGGREGATION);
expect(erDb.getEntity('d').alias).toBe('DEPARTMENT');
expect(erDb.getEntity('e').alias).toBe('EMPLOYEE');
});
it('should validate aggregation relationships', function () {
erDiagram.parser.parse('erDiagram\nDEPARTMENT <> EMPLOYEE : contains');
const rels = erDb.getRelationships();
expect(erDb.validateAggregationRelationship(rels[0].relSpec)).toBe(true);
});
it('should handle mixed relationship types', function () {
erDiagram.parser.parse(
'erDiagram\nCUSTOMER ||--o{ ORDER : places\nPRODUCT <> ORDER_ITEM : "aggregated in"'
);
const rels = erDb.getRelationships();
expect(erDb.getEntities().size).toBe(4);
expect(rels.length).toBe(2);
expect(rels[0].relSpec.relType).toBe(erDb.Identification.IDENTIFYING);
expect(rels[1].relSpec.relType).toBe(erDb.Aggregation.AGGREGATION);
});
});
});

View File

@@ -68,32 +68,6 @@ const getStyles = (options: FlowChartStyleOptions) =>
stroke: ${options.lineColor} !important;
stroke-width: 1;
}
.aggregation {
stroke: ${options.lineColor};
stroke-width: 1;
fill: white;
}
.aggregation-dashed {
stroke: ${options.lineColor};
stroke-width: 1;
stroke-dasharray: 8,8;
fill: white;
}
.aggregation-marker {
fill: white !important;
stroke: ${options.lineColor} !important;
stroke-width: 1;
}
.aggregation-marker-dashed {
fill: white !important;
stroke: ${options.lineColor} !important;
stroke-width: 1;
stroke-dasharray: 3,3;
}
`;
export default getStyles;

View File

@@ -1,6 +1,91 @@
import { arc as d3arc, select } from 'd3';
import { createText } from '../../rendering-util/createText.js';
import DOMPurify from 'dompurify';
const MAX_SECTIONS = 12;
/**
* Process HTML content in node descriptions
* @param {object} textElem - The SVG element to append text to
* @param {object} node - The node object containing description and dimensions
* @param {object} conf - Configuration object
* @param {boolean} isVirtual - Whether this is for virtual height calculation
*/
const processHtmlContent = async function (textElem, node, conf, isVirtual = false) {
// Create temporary text to get initial dimensions
const sanitizedHtml = DOMPurify.sanitize(node.descr, { ALLOWED_TAGS: [] });
const tempText = textElem
.append('text')
.text(sanitizedHtml)
.attr('dy', '1em')
.attr('alignment-baseline', 'middle')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle')
.call(wrap, node.width);
if (!isVirtual) {
tempText.attr('visibility', 'hidden');
}
const bbox = tempText.node().getBBox();
tempText.remove();
// Create the actual HTML content
const textObj = await createText(
textElem,
node.descr,
{
useHtmlLabels: true,
width: node.width,
classes: 'timeline-node-label',
isNode: true,
},
conf
);
if (!isVirtual) {
select(textObj).attr('transform', 'translate(0, 0)');
}
// Process the foreign object
const foreignObject = textElem.select('foreignObject');
if (foreignObject.node()) {
foreignObject.attr('width', `${10 * node.width}px`).attr('height', `${10 * node.width}px`);
const div = foreignObject.select('div');
if (div.node()) {
div
.style('display', 'table-cell')
.style('white-space', 'nowrap')
.style('line-height', '1.5')
.style('max-width', node.width + 'px')
.style('text-align', 'center');
let divBBox = div.node().getBoundingClientRect();
if (divBBox.width === node.width) {
div
.style('display', 'table')
.style('white-space', 'break-spaces')
.style('width', node.width + 'px');
divBBox = div.node().getBoundingClientRect();
}
foreignObject.attr('width', node.width).attr('height', divBBox.height);
if (!isVirtual) {
foreignObject.attr('x', -node.width / 2).attr('y', 3);
div.style('width', node.width + 'px');
}
bbox.height = divBBox.height;
}
}
return bbox;
};
export const drawRect = function (elem, rectData) {
const rectElem = elem.append('rect');
rectElem.attr('x', rectData.x);
@@ -409,6 +494,9 @@ const _drawTextCandidateFunc = (function () {
.style('display', 'table-cell')
.style('text-align', 'center')
.style('vertical-align', 'middle')
.style('word-wrap', 'break-word')
.style('overflow-wrap', 'break-word')
.style('white-space', 'normal')
.text(content);
byTspan(content, body, x, y, width, height, textAttrs, conf);
@@ -493,7 +581,7 @@ function wrap(text, width) {
});
}
export const drawNode = function (elem, node, fullSection, conf) {
export const drawNode = async function (elem, node, fullSection, conf) {
const section = (fullSection % MAX_SECTIONS) - 1;
const nodeElem = elem.append('g');
node.section = section;
@@ -506,19 +594,28 @@ export const drawNode = function (elem, node, fullSection, conf) {
// Create the wrapped text element
const textElem = nodeElem.append('g');
const txt = textElem
.append('text')
.text(node.descr)
.attr('dy', '1em')
.attr('alignment-baseline', 'middle')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle')
.call(wrap, node.width);
const bbox = txt.node().getBBox();
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
node.height = Math.max(node.height, node.maxHeight);
node.width = node.width + 2 * node.padding;
const hasHtml = /<[a-z][\S\s]*>/i.test(node.descr);
if (hasHtml) {
const bbox = await processHtmlContent(textElem, node, conf, false);
node.height = bbox.height + node.padding;
node.height = Math.max(node.height, node.maxHeight);
node.width = node.width + 2 * node.padding;
} else {
const txt = textElem
.append('text')
.text(node.descr)
.attr('dy', '1em')
.attr('alignment-baseline', 'middle')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle')
.call(wrap, node.width);
const bbox = txt.node().getBBox();
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
node.height = Math.max(node.height, node.maxHeight);
node.width = node.width + 2 * node.padding;
}
textElem.attr('transform', 'translate(' + node.width / 2 + ', ' + node.padding / 2 + ')');
@@ -528,17 +625,25 @@ export const drawNode = function (elem, node, fullSection, conf) {
return node;
};
export const getVirtualNodeHeight = function (elem, node, conf) {
export const getVirtualNodeHeight = async function (elem, node, conf) {
const textElem = elem.append('g');
const txt = textElem
.append('text')
.text(node.descr)
.attr('dy', '1em')
.attr('alignment-baseline', 'middle')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle')
.call(wrap, node.width);
const bbox = txt.node().getBBox();
const hasHtml = /<[a-z][\S\s]*>/i.test(node.descr);
let bbox;
if (hasHtml) {
bbox = await processHtmlContent(textElem, node, conf, true);
} else {
const txt = textElem
.append('text')
.text(node.descr)
.attr('dy', '1em')
.attr('alignment-baseline', 'middle')
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'middle')
.call(wrap, node.width);
bbox = txt.node().getBBox();
}
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
textElem.remove();
return bbox.height + fontSize * 1.1 * 0.5 + node.padding;

View File

@@ -25,7 +25,7 @@ interface TimelineTask {
score: number;
events: string[];
}
export const draw = function (text: string, id: string, version: string, diagObj: Diagram) {
export const draw = async function (text: string, id: string, version: string, diagObj: Diagram) {
//1. Fetch the configuration
const conf = getConfig();
const LEFT_MARGIN = conf.timeline?.leftMargin ?? 50;
@@ -76,7 +76,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
let hasSections = true;
//Calculate the max height of the sections
sections.forEach(function (section: string) {
for (const section of sections) {
const sectionNode: Block<string, number> = {
number: sectionNumber,
descr: section,
@@ -85,10 +85,10 @@ export const draw = function (text: string, id: string, version: string, diagObj
padding: 20,
maxHeight: maxSectionHeight,
};
const sectionHeight = svgDraw.getVirtualNodeHeight(svg, sectionNode, conf);
const sectionHeight = await svgDraw.getVirtualNodeHeight(svg, sectionNode, conf);
log.debug('sectionHeight before draw', sectionHeight);
maxSectionHeight = Math.max(maxSectionHeight, sectionHeight + 20);
});
}
//tasks length and maxEventCount
let maxEventCount = 0;
@@ -106,7 +106,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
padding: 20,
maxHeight: maxTaskHeight,
};
const taskHeight = svgDraw.getVirtualNodeHeight(svg, taskNode, conf);
const taskHeight = await svgDraw.getVirtualNodeHeight(svg, taskNode, conf);
log.debug('taskHeight before draw', taskHeight);
maxTaskHeight = Math.max(maxTaskHeight, taskHeight + 20);
@@ -123,9 +123,8 @@ export const draw = function (text: string, id: string, version: string, diagObj
padding: 20,
maxHeight: 50,
};
maxEventLineLengthTemp += svgDraw.getVirtualNodeHeight(svg, eventNode, conf);
maxEventLineLengthTemp += await svgDraw.getVirtualNodeHeight(svg, eventNode, conf);
}
// Add spacing between events (10px per event except the last one)
if (task.events.length > 0) {
maxEventLineLengthTemp += (task.events.length - 1) * 10;
}
@@ -136,7 +135,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
log.debug('maxTaskHeight before draw', maxTaskHeight);
if (sections && sections.length > 0) {
sections.forEach((section) => {
for (const section of sections) {
//filter task where tasks.section == section
const tasksForSection = tasks.filter((task) => task.section === section);
@@ -150,7 +149,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
};
log.debug('sectionNode', sectionNode);
const sectionNodeWrapper = svg.append('g');
const node = svgDraw.drawNode(sectionNodeWrapper, sectionNode, sectionNumber, conf);
const node = await svgDraw.drawNode(sectionNodeWrapper, sectionNode, sectionNumber, conf);
log.debug('sectionNode output', node);
sectionNodeWrapper.attr('transform', `translate(${masterX}, ${sectionBeginY})`);
@@ -159,7 +158,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
//draw tasks for this section
if (tasksForSection.length > 0) {
drawTasks(
await drawTasks(
svg,
tasksForSection,
sectionNumber,
@@ -178,11 +177,11 @@ export const draw = function (text: string, id: string, version: string, diagObj
masterY = sectionBeginY;
sectionNumber++;
});
}
} else {
//draw tasks
hasSections = false;
drawTasks(
await drawTasks(
svg,
tasks,
sectionNumber,
@@ -236,7 +235,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
// addSVGAccessibilityFields(diagObj.db, diagram, id);
};
export const drawTasks = function (
export const drawTasks = async function (
diagram: Selection<SVGElement, unknown, null, undefined>,
tasks: TimelineTask[],
sectionColor: number,
@@ -265,7 +264,7 @@ export const drawTasks = function (
// create task wrapper
const taskWrapper = diagram.append('g').attr('class', 'taskWrapper');
const node = svgDraw.drawNode(taskWrapper, taskNode, sectionColor, conf);
const node = await svgDraw.drawNode(taskWrapper, taskNode, sectionColor, conf);
const taskHeight = node.height;
//log task height
log.debug('taskHeight after draw', taskHeight);
@@ -282,7 +281,7 @@ export const drawTasks = function (
//add margin to task
masterY += 100;
lineLength =
lineLength + drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf);
lineLength + (await drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf));
masterY -= 100;
lineWrapper
@@ -307,7 +306,7 @@ export const drawTasks = function (
masterY = masterY - 10;
};
export const drawEvents = function (
export const drawEvents = async function (
diagram: Selection<SVGElement, unknown, null, undefined>,
events: string[],
sectionColor: number,
@@ -334,7 +333,7 @@ export const drawEvents = function (
log.debug('eventNode', eventNode);
// create event wrapper
const eventWrapper = diagram.append('g').attr('class', 'eventWrapper');
const node = svgDraw.drawNode(eventWrapper, eventNode, sectionColor, conf);
const node = await svgDraw.drawNode(eventWrapper, eventNode, sectionColor, conf);
const eventHeight = node.height;
maxEventHeight = maxEventHeight + eventHeight;
eventWrapper.attr('transform', `translate(${masterX}, ${masterY})`);

View File

@@ -31,7 +31,7 @@
"fast-glob": "^3.3.3",
"https-localhost": "^4.7.1",
"pathe": "^2.0.3",
"unocss": "^66.4.2",
"unocss": "^66.0.0",
"unplugin-vue-components": "^28.4.0",
"vite": "^6.1.1",
"vite-plugin-pwa": "^1.0.0",

View File

@@ -151,35 +151,6 @@ erDiagram
PERSON many(0) optionally to 0+ NAMED-DRIVER : is
```
### Aggregation
Aggregation represents a "has-a" relationship where the part can exist independently of the whole. This is different from composition, where the part cannot exist without the whole. Aggregation relationships are rendered with hollow diamond markers at the endpoints.
| Value | Alias for | Description |
| :---: | :------------------: | ------------------------------ |
| <> | _aggregation_ | Basic aggregation (solid line) |
| <>.. | _aggregation-dashed_ | Dashed aggregation line |
**Examples:**
```mermaid-example
erDiagram
DEPARTMENT <> EMPLOYEE : contains
PROJECT <>.. TASK : manages
TEAM <> MEMBER : consists_of
```
In these examples:
- `DEPARTMENT <> EMPLOYEE` shows that a department contains employees (aggregation)
- `PROJECT <>.. TASK` shows that a project manages tasks (dashed aggregation)
- `TEAM <> MEMBER` shows that a team consists of members (aggregation)
**Aggregation vs Association**
- **Aggregation** (`<>`): "Has-a" relationship where parts can exist independently
- **Association** (`||--`, `}o--`): General relationship between entities
### Attributes
Attributes can be defined for entities by specifying the entity name followed by a block containing multiple `type name` pairs, where a block is delimited by an opening `{` and a closing `}`. The attributes are rendered inside the entity boxes. For example:

View File

@@ -41,6 +41,7 @@ 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';
/**
@@ -49,6 +50,7 @@ import { JSDOM } from 'jsdom';
*/
// -------------------------------------------------------------------------------------
describe('mermaidAPI', () => {
describe('encodeEntities', () => {
it('removes the ending ; from style [text1]:[optional word]#[text2]; with ', () => {
@@ -911,241 +913,4 @@ 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');
}
);
});
});

440
pnpm-lock.yaml generated
View File

@@ -227,8 +227,8 @@ importers:
specifier: ^7.0.4
version: 7.1.0
'@iconify/utils':
specifier: ^3.0.1
version: 3.0.1
specifier: ^2.1.33
version: 2.3.0
'@mermaid-js/parser':
specifier: workspace:^
version: link:../parser
@@ -499,8 +499,8 @@ importers:
specifier: ^2.0.3
version: 2.0.3
unocss:
specifier: ^66.4.2
version: 66.4.2(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))
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))
unplugin-vue-components:
specifier: ^28.4.0
version: 28.4.0(@babel/parser@7.28.0)(vue@3.5.13(typescript@5.7.3))
@@ -647,11 +647,11 @@ packages:
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
'@antfu/install-pkg@1.1.0':
resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
'@antfu/install-pkg@1.0.0':
resolution: {integrity: sha512-xvX6P/lo1B3ej0OsaErAjqgFYzYVcJpamjLAFLYh9vRJngBrMoUG7aVnrGTeqM7yxbyTD5p3F2+0/QUEh8Vzhw==}
'@antfu/utils@9.2.0':
resolution: {integrity: sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw==}
'@antfu/utils@8.1.1':
resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==}
'@apideck/better-ajv-errors@0.3.6':
resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==}
@@ -2460,8 +2460,8 @@ packages:
'@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
'@iconify/utils@3.0.1':
resolution: {integrity: sha512-A78CUEnFGX8I/WlILxJCuIJXloL0j/OJ9PSchPAfCargEIKmUBWvvEMmKWB5oONwiUqlNt+5eRufdkLxeHIWYw==}
'@iconify/utils@2.3.0':
resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==}
'@img/sharp-darwin-arm64@0.33.5':
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
@@ -2740,9 +2740,6 @@ 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:
@@ -3541,94 +3538,88 @@ packages:
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
'@unocss/astro@66.4.2':
resolution: {integrity: sha512-En3AKHwkiPxtZT95vkVrNiRYrB+DFVCikew6/dMMCWDWVKK0+5tEVUTzR1ak3+YnzAXl0NpWj8D4zHb0PxOs/A==}
'@unocss/astro@66.0.0':
resolution: {integrity: sha512-GBhXT6JPqXjDXoJZTXhySk83NgOt0UigChqrUUdG4x7Z+DVYkDBION8vZUJjw0OdIaxNQ4euGWu4GDsMF6gQQg==}
peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0
peerDependenciesMeta:
vite:
optional: true
'@unocss/cli@66.4.2':
resolution: {integrity: sha512-WsXzrB0SHbSt2nOHtD5QM91VN8j38+wObqyGcoIhtBSugqzsc+t7AdPkxV/ZaYgtPAz87bR0WFEVKcbiBRnmJw==}
'@unocss/cli@66.0.0':
resolution: {integrity: sha512-KVQiskoOjVkLVpNaG6WpLa4grPplrZROYZJVIUYSTqZyZRFNSvjttHcsCwpoWUEUdEombPtVZl8FrXePjY5IiQ==}
engines: {node: '>=14'}
hasBin: true
'@unocss/config@66.4.2':
resolution: {integrity: sha512-plji1gNGSzlWjuV2Uh0q6Dt5ZlNkOKCHpgxekW9J458WghGAMBeXgB9uNpWg6flilqP1g0GJQv+XvJcSkYRGpQ==}
'@unocss/config@66.0.0':
resolution: {integrity: sha512-nFRGop/guBa4jLkrgXjaRDm5JPz4x3YpP10m5IQkHpHwlnHUVn1L9smyPl04ohYWhYn9ZcAHgR28Ih2jwta8hw==}
engines: {node: '>=14'}
'@unocss/core@66.4.2':
resolution: {integrity: sha512-cYgMQrLhB9nRekv5c+yPDDa+5dzlMkA2UMQRil0s5D9Lb5n7NsCMcr6+nfxkcSYVLy92SbwDV45c6T7vIxFTOA==}
'@unocss/core@66.0.0':
resolution: {integrity: sha512-PdVbSMHNDDkr++9nkqzsZRAkaU84gxMTEgYbqI7dt2p1DXp/5tomVtmMsr2/whXGYKRiUc0xZ3p4Pzraz8TcXA==}
'@unocss/extractor-arbitrary-variants@66.4.2':
resolution: {integrity: sha512-T/eSeodfAp7HaWnQGqVLOsW4PbKUAvuybNRyvFWThMneM2qo+dOo3kFnA5my9ULAmRSFsAlyB1DnupD3qv5Klg==}
'@unocss/extractor-arbitrary-variants@66.0.0':
resolution: {integrity: sha512-vlkOIOuwBfaFBJcN6o7+obXjigjOlzVFN/jT6pG1WXbQDTRZ021jeF3i9INdb9D/0cQHSeDvNgi1TJ5oUxfiow==}
'@unocss/inspector@66.4.2':
resolution: {integrity: sha512-ugcJK8r2ypM4eIdgetVn8RhfKrbA3AF3OQ/RohK5PPk2UPDAScqabzYpfdNW4eYQsBOZOgoiqWtnfc8weqo8LQ==}
'@unocss/inspector@66.0.0':
resolution: {integrity: sha512-mkIxieVm0kMOKw+E4ABpIerihYMdjgq9A92RD5h2+W/ebpxTEw5lTTK1xcMLiAlmOrVYMQKjpgPeu3vQmDyGZQ==}
'@unocss/postcss@66.4.2':
resolution: {integrity: sha512-tu4lnh6K27pIAuaQHlFlhXin8korwC0r1kQl00YMmF3THiX7orXkTP6xWGcQwnkbx4uQz1dw+tBimYxeaAMrhA==}
'@unocss/postcss@66.0.0':
resolution: {integrity: sha512-6bi+ujzh8I1PJwtmHX71LH8z/H9+vPxeYD4XgFihyU1k4Y6MVhjr7giGjLX4yP27IP+NsVyotD22V7by/dBVEA==}
engines: {node: '>=14'}
peerDependencies:
postcss: ^8.4.21
'@unocss/preset-attributify@66.4.2':
resolution: {integrity: sha512-DwFJJkkawmHpjo3pGQE8FyoPsvhbxh+QMvvaAdYpo+iZ5HRkeDml9SOj7u6SGTcmbNyI+QR61s0KM8fxx6HcVQ==}
'@unocss/preset-attributify@66.0.0':
resolution: {integrity: sha512-eYsOgmcDoiIgGAepIwRX+DKGYxc/wm0r4JnDuZdz29AB+A6oY/FGHS1BVt4rq9ny4B5PofP4p6Rty+vwD9rigw==}
'@unocss/preset-icons@66.4.2':
resolution: {integrity: sha512-qJx9gmesrvrmoTe9Mqoidihad8hm2MSD4QAezhfDSAyllioJOgyT0Bev/IEWAbehe9jtqYIh8v1oCerBPbGn6Q==}
'@unocss/preset-icons@66.0.0':
resolution: {integrity: sha512-6ObwTvEGuPBbKWRoMMiDioHtwwQTFI5oojFLJ32Y8tW6TdXvBLkO88d7qpgQxEjgVt4nJrqF1WEfR4niRgBm0Q==}
'@unocss/preset-mini@66.4.2':
resolution: {integrity: sha512-Ry+5hM+XLmT8HrEb182mUfcZuyrZ8xR+TBe72DBcliJ1DhOV3K67TCxwQucfb0zHbGV71HNWdPmHsLKxPDgweQ==}
'@unocss/preset-mini@66.0.0':
resolution: {integrity: sha512-d62eACnuKtR0dwCFOQXgvw5VLh5YSyK56xCzpHkh0j0GstgfDLfKTys0T/XVAAvdSvAy/8A8vhSNJ4PlIc9V2A==}
'@unocss/preset-tagify@66.4.2':
resolution: {integrity: sha512-dECS09LqWJY4sYpgPUH2OAUftWU/tiZPR2XDRoTngeGU37GxSN+1sWtSmB7vwDm3C7opsdVUN20he8F1LUNubw==}
'@unocss/preset-tagify@66.0.0':
resolution: {integrity: sha512-GGYGyWxaevh0jN0NoATVO1Qe7DFXM3ykLxchlXmG6/zy963pZxItg/njrKnxE9la4seCdxpFH7wQBa68imwwdA==}
'@unocss/preset-typography@66.4.2':
resolution: {integrity: sha512-ZOKRuR5+V0r30QTVq04/6ZoIw75me3V25v2dU2YWJXIzwpMKmQ9TUN/M1yeiEUFfXjOaruWX6Ad6CvAw2MlCew==}
'@unocss/preset-typography@66.0.0':
resolution: {integrity: sha512-apjckP5nPU5mtaHTCzz5u/dK9KJWwJ2kOFCVk0+a/KhUWmnqnzmjRYZlEuWxxr5QxTdCW+9cIoRDSA0lYZS5tg==}
'@unocss/preset-uno@66.4.2':
resolution: {integrity: sha512-1MFtPivGcpqRQFWdjtP40Enop1y3XDb3tlZXoMQUX0IGLG8HJOT+lfQx/Xl9t73ShJ8aAJ/l6qTxC43ZGNACzA==}
'@unocss/preset-uno@66.0.0':
resolution: {integrity: sha512-qgoZ/hzTI32bQvcyjcwvv1X/dbPlmQNehzgjUaL7QFT0q0/CN/SRpysfzoQ8DLl2se9T+YCOS9POx3KrpIiYSQ==}
'@unocss/preset-web-fonts@66.4.2':
resolution: {integrity: sha512-4FYmleeRoM8r2DqGl6dfIjnX57tepcfZCvVfeCqYnk7475Yddmv1OYkoMjkWMnkK9MzdSxsFwHMU6CIUTmFTzQ==}
'@unocss/preset-web-fonts@66.0.0':
resolution: {integrity: sha512-9MzfDc6AJILN4Kq7Z91FfFbizBOYgw3lJd2UwqIs3PDYWG5iH5Zv5zhx6jelZVqEW5uWcIARYEEg2m4stZO1ZA==}
'@unocss/preset-wind3@66.4.2':
resolution: {integrity: sha512-0Aye/PaT08M/cQhPnGKn93iEVoRJbym0/1eomMvXoL+8oc7DVry35ws06r5CLu5h1sXI6UmS6sejoePFlSkLJQ==}
'@unocss/preset-wind3@66.0.0':
resolution: {integrity: sha512-WAGRmpi1sb2skvYn9DBQUvhfqrJ+VmQmn5ZGsT2ewvsk7HFCvVLAMzZeKrrTQepeNBRhg6HzFDDi8yg6yB5c9g==}
'@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/preset-wind@66.0.0':
resolution: {integrity: sha512-FtvGpHnGC7FiyKJavPnn5y9lsaoWRhXlujCqlT5Bw63kKhMNr0ogKySBpenUhJOhWhVM0OQXn2nZ3GZRxW2qpw==}
'@unocss/reset@66.0.0':
resolution: {integrity: sha512-YLFz/5yT7mFJC8JSmIUA5+bS3CBCJbtztOw+8rWzjQr/BEVSGuihWUUpI2Df6VVxXIXxKanZR6mIl59yvf+GEA==}
'@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==}
'@unocss/rule-utils@66.0.0':
resolution: {integrity: sha512-UJ51YHbwxYTGyj35ugsPlOT4gaa7tCbXdywZ3m5Nn0JgywwIqGmBFyiN9ZjHBHfJuDxmmPd6lxojoBscih/WMQ==}
engines: {node: '>=14'}
'@unocss/transformer-attributify-jsx@66.4.2':
resolution: {integrity: sha512-de6LzoyW1tkdOftlCrj6z8wEb4j6l1sqmOU1nYKkYHw7luLFGxRUELC7iujlI9KmylbM02bcKfLETAfJy/je2w==}
'@unocss/transformer-attributify-jsx@66.0.0':
resolution: {integrity: sha512-jS7szFXXC6RjTv9wo0NACskf618w981bkbyQ5izRO7Ha47sNpHhHDpaltnG7SR9qV4cCtGalOw4onVMHsRKwRg==}
'@unocss/transformer-compile-class@66.4.2':
resolution: {integrity: sha512-+oiIrV8c3T7qiJdICr6YsEWik5sjbWirXF0mlpcBvZu2HyV559hvHjzuWKr/fl7xYYZKDL9FvddbqWo3DOXh3Q==}
'@unocss/transformer-compile-class@66.0.0':
resolution: {integrity: sha512-ytUIE0nAcHRMACuTXkHp8auZ483DXrOZw99jk3FJ+aFjpD/pVSFmX14AWJ7bqPFObxb4SLFs6KhQma30ESC22A==}
'@unocss/transformer-directives@66.4.2':
resolution: {integrity: sha512-7m/dTrCUkBkZeSRKPxPEo65Rav239orQSLq6sztwZhoA4x/6H8r58xCkAK0qC9VEalyerpCpyarU3sKN4+ehNg==}
'@unocss/transformer-directives@66.0.0':
resolution: {integrity: sha512-utcg7m2Foi7uHrU5WHadNuJ0a3qWG8tZNkQMi+m0DQpX6KWfuDtDn0zDZ1X+z5lmiB3WGSJERRrsvZbj1q50Mw==}
'@unocss/transformer-variant-group@66.4.2':
resolution: {integrity: sha512-SbPDbZUrhQyL4CpvnpvUfrr1DFq8AKf8ofPGbMJDm5S2TInQ34vFaIrhNroGR0szntMZRH5Zlkq6LtVUKDRs5g==}
'@unocss/transformer-variant-group@66.0.0':
resolution: {integrity: sha512-1BLjNWtAnR1JAcQGw0TS+nGrVoB9aznzvVZRoTx23dtRr3btvgKPHb8LrD48eD/p8Dtw9j3WfuxMDKXKegKDLg==}
'@unocss/vite@66.4.2':
resolution: {integrity: sha512-7eON9iPF3qWzuI+M6u0kq7K3y9nEbimZlLj01nGoqrgSGxEsyJpP01QQQsmT7FPRiZzRMJv7BiKMEyDQSuRRCA==}
'@unocss/vite@66.0.0':
resolution: {integrity: sha512-IVcPX8xL+2edyXKt4tp9yu5A6gcbPVCsspfcL0XgziCr01kS+4qSoZ90F3IUs3hXc/AyO5eCpRtGFMPLpOjXQg==}
peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
@@ -4788,15 +4779,12 @@ 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.2:
resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
consola@3.4.0:
resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==}
engines: {node: ^14.18.0 || >=16.10.0}
console.table@0.10.0:
@@ -5864,9 +5852,6 @@ 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==}
@@ -7291,10 +7276,6 @@ 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'}
@@ -8059,9 +8040,6 @@ 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==}
@@ -8245,9 +8223,6 @@ 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'}
@@ -8430,9 +8405,6 @@ 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==}
@@ -9024,7 +8996,6 @@ 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==}
@@ -9356,9 +9327,6 @@ 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'}
@@ -9579,8 +9547,8 @@ packages:
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
engines: {node: '>= 0.4'}
unconfig@7.3.2:
resolution: {integrity: sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg==}
unconfig@7.0.0:
resolution: {integrity: sha512-G5CJSoG6ZTxgzCJblEfgpdRK2tos9+UdD2WtecDUVfImzQ0hFjwpH5RVvGMhP4pRpC9ML7NrC4qBsBl0Ttj35A==}
underscore@1.1.7:
resolution: {integrity: sha512-w4QtCHoLBXw1mjofIDoMyexaEdWGMedWNDhlWTtT1V1lCRqi65Pnoygkh6+WRdr+Bm8ldkBNkNeCsXGMlQS9HQ==}
@@ -9657,12 +9625,12 @@ packages:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
unocss@66.4.2:
resolution: {integrity: sha512-PsZ+4XF/ekiParR7PZEM7AchvHJ78EIfOXlqTPflTOXCYgZ77kG9NaIaIf4lHRevY+rRTyrHrjxdg1Ern2j8qw==}
unocss@66.0.0:
resolution: {integrity: sha512-SHstiv1s7zGPSjzOsADzlwRhQM+6817+OqQE3Fv+N/nn2QLNx1bi3WXybFfz5tWkzBtyTZlwdPmeecsIs1yOCA==}
engines: {node: '>=14'}
peerDependencies:
'@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
'@unocss/webpack': 66.0.0
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0
peerDependenciesMeta:
'@unocss/webpack':
optional: true
@@ -10006,8 +9974,10 @@ packages:
vscode-uri@3.1.0:
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
vue-flow-layout@0.2.0:
resolution: {integrity: sha512-zKgsWWkXq0xrus7H4Mc+uFs1ESrmdTXlO0YNbR6wMdPaFvosL3fMB8N7uTV308UhGy9UvTrGhIY7mVz9eN+L0Q==}
vue-flow-layout@0.1.1:
resolution: {integrity: sha512-JdgRRUVrN0Y2GosA0M68DEbKlXMqJ7FQgsK8CjQD2vxvNSqAU6PZEpi4cfcTVtfM2GVOMjHo7GKKLbXxOBqDqA==}
peerDependencies:
vue: ^3.4.37
vue@3.5.13:
resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
@@ -10528,12 +10498,12 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
'@antfu/install-pkg@1.1.0':
'@antfu/install-pkg@1.0.0':
dependencies:
package-manager-detector: 1.3.0
tinyexec: 1.0.1
package-manager-detector: 0.2.9
tinyexec: 0.3.2
'@antfu/utils@9.2.0': {}
'@antfu/utils@8.1.1': {}
'@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)':
dependencies:
@@ -10886,7 +10856,7 @@ snapshots:
'@babel/generator@7.27.1':
dependencies:
'@babel/parser': 7.28.0
'@babel/parser': 7.27.2
'@babel/types': 7.27.1
'@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25
@@ -10985,7 +10955,7 @@ snapshots:
'@babel/helper-module-imports@7.27.1':
dependencies:
'@babel/traverse': 7.28.0
'@babel/traverse': 7.27.1
'@babel/types': 7.27.1
transitivePeerDependencies:
- supports-color
@@ -10995,7 +10965,7 @@ snapshots:
'@babel/core': 7.27.1
'@babel/helper-module-imports': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
'@babel/traverse': 7.28.0
'@babel/traverse': 7.27.1
transitivePeerDependencies:
- supports-color
@@ -12110,14 +12080,14 @@ snapshots:
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/parser': 7.28.0
'@babel/parser': 7.27.2
'@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.28.0
'@babel/parser': 7.27.2
'@babel/template': 7.27.2
'@babel/types': 7.27.1
debug: 4.4.1(supports-color@8.1.1)
@@ -12703,7 +12673,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.1(supports-color@8.1.1)
debug: 4.4.0
lodash: 4.17.21
webpack: 5.95.0(esbuild@0.25.0)
transitivePeerDependencies:
@@ -13141,15 +13111,15 @@ snapshots:
'@iconify/types@2.0.0': {}
'@iconify/utils@3.0.1':
'@iconify/utils@2.3.0':
dependencies:
'@antfu/install-pkg': 1.1.0
'@antfu/utils': 9.2.0
'@antfu/install-pkg': 1.0.0
'@antfu/utils': 8.1.1
'@iconify/types': 2.0.0
debug: 4.4.1(supports-color@8.1.1)
debug: 4.4.0
globals: 15.15.0
kolorist: 1.8.0
local-pkg: 1.1.1
local-pkg: 1.0.0
mlly: 1.7.4
transitivePeerDependencies:
- supports-color
@@ -13531,10 +13501,6 @@ 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)
@@ -13874,7 +13840,7 @@ snapshots:
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.28.0
'@babel/parser': 7.27.2
'@babel/types': 7.27.1
'@types/babel__generator': 7.6.8
'@types/babel__template': 7.4.4
@@ -13886,7 +13852,7 @@ snapshots:
'@types/babel__template@7.4.4':
dependencies:
'@babel/parser': 7.28.0
'@babel/parser': 7.27.2
'@babel/types': 7.27.1
'@types/babel__traverse@7.20.6':
@@ -14392,157 +14358,150 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
'@unocss/astro@66.4.2(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))':
'@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))':
dependencies:
'@unocss/core': 66.4.2
'@unocss/reset': 66.4.2
'@unocss/vite': 66.4.2(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))
'@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))
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
'@unocss/cli@66.4.2':
'@unocss/cli@66.0.0':
dependencies:
'@ampproject/remapping': 2.3.0
'@unocss/config': 66.4.2
'@unocss/core': 66.4.2
'@unocss/preset-uno': 66.4.2
'@unocss/config': 66.0.0
'@unocss/core': 66.0.0
'@unocss/preset-uno': 66.0.0
cac: 6.7.14
chokidar: 3.6.0
colorette: 2.0.20
consola: 3.4.2
consola: 3.4.0
magic-string: 0.30.17
pathe: 2.0.3
perfect-debounce: 1.0.0
tinyglobby: 0.2.14
tinyglobby: 0.2.12
unplugin-utils: 0.2.4
'@unocss/config@66.4.2':
'@unocss/config@66.0.0':
dependencies:
'@unocss/core': 66.4.2
unconfig: 7.3.2
'@unocss/core': 66.0.0
unconfig: 7.0.0
'@unocss/core@66.4.2': {}
'@unocss/core@66.0.0': {}
'@unocss/extractor-arbitrary-variants@66.4.2':
'@unocss/extractor-arbitrary-variants@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/core': 66.0.0
'@unocss/inspector@66.4.2':
'@unocss/inspector@66.0.0(vue@3.5.13(typescript@5.7.3))':
dependencies:
'@unocss/core': 66.4.2
'@unocss/rule-utils': 66.4.2
'@unocss/core': 66.0.0
'@unocss/rule-utils': 66.0.0
colorette: 2.0.20
gzip-size: 6.0.0
sirv: 3.0.1
vue-flow-layout: 0.2.0
vue-flow-layout: 0.1.1(vue@3.5.13(typescript@5.7.3))
transitivePeerDependencies:
- vue
'@unocss/postcss@66.4.2(postcss@8.5.6)':
'@unocss/postcss@66.0.0(postcss@8.5.6)':
dependencies:
'@unocss/config': 66.4.2
'@unocss/core': 66.4.2
'@unocss/rule-utils': 66.4.2
'@unocss/config': 66.0.0
'@unocss/core': 66.0.0
'@unocss/rule-utils': 66.0.0
css-tree: 3.1.0
postcss: 8.5.6
tinyglobby: 0.2.14
tinyglobby: 0.2.12
'@unocss/preset-attributify@66.4.2':
'@unocss/preset-attributify@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/core': 66.0.0
'@unocss/preset-icons@66.4.2':
'@unocss/preset-icons@66.0.0':
dependencies:
'@iconify/utils': 3.0.1
'@unocss/core': 66.4.2
'@iconify/utils': 2.3.0
'@unocss/core': 66.0.0
ofetch: 1.4.1
transitivePeerDependencies:
- supports-color
'@unocss/preset-mini@66.4.2':
'@unocss/preset-mini@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/extractor-arbitrary-variants': 66.4.2
'@unocss/rule-utils': 66.4.2
'@unocss/core': 66.0.0
'@unocss/extractor-arbitrary-variants': 66.0.0
'@unocss/rule-utils': 66.0.0
'@unocss/preset-tagify@66.4.2':
'@unocss/preset-tagify@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/core': 66.0.0
'@unocss/preset-typography@66.4.2':
'@unocss/preset-typography@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/preset-mini': 66.4.2
'@unocss/rule-utils': 66.4.2
'@unocss/core': 66.0.0
'@unocss/preset-mini': 66.0.0
'@unocss/rule-utils': 66.0.0
'@unocss/preset-uno@66.4.2':
'@unocss/preset-uno@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/preset-wind3': 66.4.2
'@unocss/core': 66.0.0
'@unocss/preset-wind3': 66.0.0
'@unocss/preset-web-fonts@66.4.2':
'@unocss/preset-web-fonts@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/core': 66.0.0
ofetch: 1.4.1
'@unocss/preset-wind3@66.4.2':
'@unocss/preset-wind3@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/preset-mini': 66.4.2
'@unocss/rule-utils': 66.4.2
'@unocss/core': 66.0.0
'@unocss/preset-mini': 66.0.0
'@unocss/rule-utils': 66.0.0
'@unocss/preset-wind4@66.4.2':
'@unocss/preset-wind@66.0.0':
dependencies:
'@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/core': 66.0.0
'@unocss/preset-wind3': 66.0.0
'@unocss/reset@66.0.0': {}
'@unocss/reset@66.4.2': {}
'@unocss/rule-utils@66.4.2':
'@unocss/rule-utils@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/core': 66.0.0
magic-string: 0.30.17
'@unocss/transformer-attributify-jsx@66.4.2':
'@unocss/transformer-attributify-jsx@66.0.0':
dependencies:
'@babel/parser': 7.28.0
'@babel/traverse': 7.28.0
'@unocss/core': 66.4.2
transitivePeerDependencies:
- supports-color
'@unocss/core': 66.0.0
'@unocss/transformer-compile-class@66.4.2':
'@unocss/transformer-compile-class@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/core': 66.0.0
'@unocss/transformer-directives@66.4.2':
'@unocss/transformer-directives@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/rule-utils': 66.4.2
'@unocss/core': 66.0.0
'@unocss/rule-utils': 66.0.0
css-tree: 3.1.0
'@unocss/transformer-variant-group@66.4.2':
'@unocss/transformer-variant-group@66.0.0':
dependencies:
'@unocss/core': 66.4.2
'@unocss/core': 66.0.0
'@unocss/vite@66.4.2(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))':
'@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))':
dependencies:
'@ampproject/remapping': 2.3.0
'@unocss/config': 66.4.2
'@unocss/core': 66.4.2
'@unocss/inspector': 66.4.2
'@unocss/config': 66.0.0
'@unocss/core': 66.0.0
'@unocss/inspector': 66.0.0(vue@3.5.13(typescript@5.7.3))
chokidar: 3.6.0
magic-string: 0.30.17
pathe: 2.0.3
tinyglobby: 0.2.14
tinyglobby: 0.2.12
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
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
optional: true
@@ -15812,11 +15771,9 @@ snapshots:
confbox@0.1.8: {}
confbox@0.2.2: {}
connect-history-api-fallback@2.0.0: {}
consola@3.4.2: {}
consola@3.4.0: {}
console.table@0.10.0:
dependencies:
@@ -17240,8 +17197,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
exsolve@1.0.7: {}
extend@3.0.2: {}
extendable-error@0.1.7: {}
@@ -17447,7 +17402,7 @@ snapshots:
'@actions/core': 1.11.1
arg: 5.0.2
console.table: 0.10.0
debug: 4.4.1(supports-color@8.1.1)
debug: 4.4.0
find-test-names: 1.29.5(@babel/core@7.27.1)
globby: 11.1.0
minimatch: 3.1.2
@@ -18348,7 +18303,7 @@ snapshots:
istanbul-lib-source-maps@5.0.6:
dependencies:
'@jridgewell/trace-mapping': 0.3.25
debug: 4.4.1(supports-color@8.1.1)
debug: 4.4.0
istanbul-lib-coverage: 3.2.2
transitivePeerDependencies:
- supports-color
@@ -18981,12 +18936,6 @@ 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
@@ -19666,7 +19615,7 @@ snapshots:
node-source-walk@7.0.0:
dependencies:
'@babel/parser': 7.28.0
'@babel/parser': 7.27.2
nomnom@1.5.2:
dependencies:
@@ -19924,8 +19873,6 @@ snapshots:
package-manager-detector@0.2.9: {}
package-manager-detector@1.3.0: {}
pako@1.0.11: {}
pako@2.1.0: {}
@@ -20100,12 +20047,6 @@ 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
@@ -20287,8 +20228,6 @@ snapshots:
dependencies:
side-channel: 1.1.0
quansync@0.2.10: {}
queue-microtask@1.2.3: {}
quick-format-unescaped@4.0.4: {}
@@ -21078,7 +21017,7 @@ snapshots:
spdy@4.0.2:
dependencies:
debug: 4.4.1(supports-color@8.1.1)
debug: 4.4.0
handle-thing: 2.0.1
http-deceiver: 1.2.7
select-hose: 2.0.0
@@ -21458,8 +21397,6 @@ snapshots:
tinyexec@0.3.2: {}
tinyexec@1.0.1: {}
tinyglobby@0.2.12:
dependencies:
fdir: 6.4.3(picomatch@4.0.2)
@@ -21662,12 +21599,11 @@ snapshots:
has-symbols: 1.1.0
which-boxed-primitive: 1.1.1
unconfig@7.3.2:
unconfig@7.0.0:
dependencies:
'@quansync/fs': 0.1.4
'@antfu/utils': 8.1.1
defu: 6.1.4
jiti: 2.4.2
quansync: 0.2.10
underscore@1.1.7: {}
@@ -21753,32 +21689,32 @@ snapshots:
universalify@2.0.1: {}
unocss@66.4.2(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)):
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)):
dependencies:
'@unocss/astro': 66.4.2(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))
'@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@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(terser@5.39.0)(tsx@4.19.3)(yaml@2.8.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/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))
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:
- postcss
- supports-color
- vue
unpipe@1.0.0: {}
@@ -22115,7 +22051,9 @@ snapshots:
vscode-uri@3.1.0: {}
vue-flow-layout@0.2.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@3.5.13(typescript@5.7.3):
dependencies: