diff --git a/.changeset/angry-bags-brake.md b/.changeset/angry-bags-brake.md
deleted file mode 100644
index 472e486ec..000000000
--- a/.changeset/angry-bags-brake.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-fix: architecture diagrams no longer grow to extreme heights due to conflicting alignments
diff --git a/.changeset/bright-ads-exist.md b/.changeset/bright-ads-exist.md
deleted file mode 100644
index ef2f76f4c..000000000
--- a/.changeset/bright-ads-exist.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-Fixes for consistent edge id creation & handling edge cases for animate edge feature
diff --git a/.changeset/chatty-elephants-warn.md b/.changeset/chatty-elephants-warn.md
deleted file mode 100644
index 225047ece..000000000
--- a/.changeset/chatty-elephants-warn.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-Fix for issue #6195 - allowing @ signs inside node labels
diff --git a/.changeset/chilly-years-cheat.md b/.changeset/chilly-years-cheat.md
deleted file mode 100644
index e665af75b..000000000
--- a/.changeset/chilly-years-cheat.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each class diagram
diff --git a/.changeset/dull-tips-cough.md b/.changeset/dull-tips-cough.md
deleted file mode 100644
index 1f5179417..000000000
--- a/.changeset/dull-tips-cough.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-fix: revert state db to resolve getData returning empty nodes and edges
diff --git a/.changeset/great-ghosts-rule.md b/.changeset/great-ghosts-rule.md
deleted file mode 100644
index f11c6e2a9..000000000
--- a/.changeset/great-ghosts-rule.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-'mermaid': minor
----
-
-Flowchart new syntax for node metadata bugs
-
-- Incorrect label mapping for nodes when using `&`
-- Syntax error when `}` with trailing spaces before new line
diff --git a/.changeset/grumpy-cheetahs-deliver.md b/.changeset/grumpy-cheetahs-deliver.md
deleted file mode 100644
index fa6736d42..000000000
--- a/.changeset/grumpy-cheetahs-deliver.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-`mermaidAPI.getDiagramFromText()` now returns a new db instance on each call for state diagrams
diff --git a/.changeset/heavy-moose-mix.md b/.changeset/heavy-moose-mix.md
deleted file mode 100644
index c02d62446..000000000
--- a/.changeset/heavy-moose-mix.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-Added versioning to StateDB and updated tests and diagrams to use it.
diff --git a/.changeset/many-brooms-promise.md b/.changeset/many-brooms-promise.md
deleted file mode 100644
index fec442b34..000000000
--- a/.changeset/many-brooms-promise.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': minor
----
-
-Adding support for animation of flowchart edges
diff --git a/.changeset/new-kiwis-listen.md b/.changeset/new-kiwis-listen.md
deleted file mode 100644
index 24306573c..000000000
--- a/.changeset/new-kiwis-listen.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each flowchart
diff --git a/.changeset/silver-olives-marry.md b/.changeset/silver-olives-marry.md
deleted file mode 100644
index d709b17ba..000000000
--- a/.changeset/silver-olives-marry.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each sequence diagram. Added unique IDs for messages.
diff --git a/.changeset/stupid-dots-do.md b/.changeset/stupid-dots-do.md
deleted file mode 100644
index 594fa9536..000000000
--- a/.changeset/stupid-dots-do.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-fix: Gantt, Sankey and User Journey diagram are now able to pick font-family from mermaid config.
diff --git a/.changeset/weak-trees-perform.md b/.changeset/weak-trees-perform.md
deleted file mode 100644
index 17175301d..000000000
--- a/.changeset/weak-trees-perform.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-fix: `getDirection` and `setDirection` in `stateDb` refactored to return and set actual direction
diff --git a/.changeset/witty-crews-smell.md b/.changeset/witty-crews-smell.md
deleted file mode 100644
index 4213083f2..000000000
--- a/.changeset/witty-crews-smell.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'mermaid': patch
----
-
-`mermaidAPI.getDiagramFromText()` now returns a new different db for each state diagram
diff --git a/cypress/integration/rendering/erDiagram-unified.spec.js b/cypress/integration/rendering/erDiagram-unified.spec.js
new file mode 100644
index 000000000..8cecba21d
--- /dev/null
+++ b/cypress/integration/rendering/erDiagram-unified.spec.js
@@ -0,0 +1,652 @@
+import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts';
+
+const testOptions = [
+ { description: '', options: { logLevel: 1 } },
+ { description: 'ELK: ', options: { logLevel: 1, layout: 'elk' } },
+ { description: 'HD: ', options: { logLevel: 1, look: 'handDrawn' } },
+];
+
+describe('Entity Relationship Diagram Unified', () => {
+ testOptions.forEach(({ description, options }) => {
+ it(`${description}should render a simple ER diagram`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a simple ER diagram without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render an ER diagram with a recursive relationship`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||..o{ CUSTOMER : refers
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with multiple relationships between the same two entities`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER ||--|{ ADDRESS : "invoiced at"
+ CUSTOMER ||--|{ ADDRESS : "receives goods at"
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a cyclical ER diagram`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ A ||--|{ B : likes
+ B ||--|{ C : likes
+ C ||--|{ A : likes
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a not-so-simple ER diagram`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER }|..|{ DELIVERY-ADDRESS : has
+ CUSTOMER ||--o{ ORDER : places
+ CUSTOMER ||--o{ INVOICE : "liable for"
+ DELIVERY-ADDRESS ||--o{ ORDER : receives
+ INVOICE ||--|{ ORDER : covers
+ ORDER ||--|{ ORDER-ITEM : includes
+ PRODUCT-CATEGORY ||--|{ PRODUCT : contains
+ PRODUCT ||--o{ ORDER-ITEM : "ordered in"
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a not-so-simple ER diagram without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ CUSTOMER }|..|{ DELIVERY-ADDRESS : has
+ CUSTOMER ||--o{ ORDER : places
+ CUSTOMER ||--o{ INVOICE : "liable for"
+ DELIVERY-ADDRESS ||--o{ ORDER : receives
+ INVOICE ||--|{ ORDER : covers
+ ORDER ||--|{ ORDER-ITEM : includes
+ PRODUCT-CATEGORY ||--|{ PRODUCT : contains
+ PRODUCT ||--o{ ORDER-ITEM : "ordered in"
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render multiple ER diagrams`, () => {
+ imgSnapshotTest(
+ [
+ `
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ `
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ ],
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with blank or empty labels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ BOOK }|..|{ AUTHOR : ""
+ BOOK }|..|{ GENRE : " "
+ AUTHOR }|..|{ GENRE : " "
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities that have no relationships`, () => {
+ renderGraph(
+ `
+ erDiagram
+ DEAD_PARROT
+ HERMIT
+ RECLUSE
+ SOCIALITE }o--o{ SOCIALITE : "interacts with"
+ RECLUSE }o--o{ SOCIALITE : avoids
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with and without attributes`, () => {
+ renderGraph(
+ `
+ erDiagram
+ BOOK { string title }
+ AUTHOR }|..|{ BOOK : writes
+ BOOK { float price }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with generic and array attributes`, () => {
+ renderGraph(
+ `
+ erDiagram
+ BOOK {
+ string title
+ string[] authors
+ type~T~ type
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with generic and array attributes without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ BOOK {
+ string title
+ string[] authors
+ type~T~ type
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with length in attributes type`, () => {
+ renderGraph(
+ `
+ erDiagram
+ CLUSTER {
+ varchar(99) name
+ string(255) description
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with length in attributes type without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ CLUSTER {
+ varchar(99) name
+ string(255) description
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities and attributes with big and small entity names`, () => {
+ renderGraph(
+ `
+ erDiagram
+ PRIVATE_FINANCIAL_INSTITUTION {
+ string name
+ int turnover
+ }
+ PRIVATE_FINANCIAL_INSTITUTION ||..|{ EMPLOYEE : employs
+ EMPLOYEE { bool officer_of_firm }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities and attributes with big and small entity names without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ PRIVATE_FINANCIAL_INSTITUTION {
+ string name
+ int turnover
+ }
+ PRIVATE_FINANCIAL_INSTITUTION ||..|{ EMPLOYEE : employs
+ EMPLOYEE { bool officer_of_firm }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with attributes that begin with asterisk`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ BOOK {
+ int *id
+ string name
+ varchar(99) summary
+ }
+ BOOK }o..o{ STORE : soldBy
+ STORE {
+ int *id
+ string name
+ varchar(50) address
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with attributes that begin with asterisk without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ BOOK {
+ int *id
+ string name
+ varchar(99) summary
+ }
+ BOOK }o..o{ STORE : soldBy
+ STORE {
+ int *id
+ string name
+ varchar(50) address
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with keys`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name PK
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ float price
+ string author FK
+ string title PK
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with keys without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name PK
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ float price
+ string author FK
+ string title PK
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with comments`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name "comment"
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ string author
+ string title "author comment"
+ float price "price comment"
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with comments without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name "comment"
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ string author
+ string title "author comment"
+ float price "price comment"
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with keys and comments`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name PK "comment"
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ string description
+ float price "price comment"
+ string title PK "title comment"
+ string author FK
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with keys and comments without htmlLabels`, () => {
+ renderGraph(
+ `
+ erDiagram
+ AUTHOR_WITH_LONG_ENTITY_NAME {
+ string name PK "comment"
+ }
+ AUTHOR_WITH_LONG_ENTITY_NAME }|..|{ BOOK : writes
+ BOOK {
+ string description
+ float price "price comment"
+ string title PK "title comment"
+ string author FK
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with aliases`, () => {
+ renderGraph(
+ `
+ erDiagram
+ T1 one or zero to one or more T2 : test
+ T2 one or many optionally to zero or one T3 : test
+ T3 zero or more to zero or many T4 : test
+ T4 many(0) to many(1) T5 : test
+ T5 many optionally to one T6 : test
+ T6 only one optionally to only one T1 : test
+ T4 0+ to 1+ T6 : test
+ T1 1 to 1 T3 : test
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render a simple ER diagram with a title`, () => {
+ imgSnapshotTest(
+ `---
+ title: simple ER diagram
+ ---
+ erDiagram
+ CUSTOMER ||--o{ ORDER : places
+ ORDER ||--|{ LINE-ITEM : contains
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with entity name aliases`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ p[Person] {
+ varchar(64) firstName
+ varchar(64) lastName
+ }
+ c["Customer Account"] {
+ varchar(128) email
+ }
+ p ||--o| c : has
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render relationship labels with line breaks`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ p[Person] {
+ string firstName
+ string lastName
+ }
+ a["Customer Account"] {
+ string email
+ }
+
+ b["Customer Account Secondary"] {
+ string email
+ }
+
+ c["Customer Account Tertiary"] {
+ string email
+ }
+
+ d["Customer Account Nth"] {
+ string email
+ }
+
+ p ||--o| a : "has
one"
+ p ||--o| b : "has
one
two"
+ p ||--o| c : "has
one
two
three"
+ p ||--o| d : "has
one
two
three
...
Nth"
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with unicode text`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ _**testẽζ➕Ø😀㌕ぼ**_ {
+ *__List~List~int~~sdfds__* **driversLicense** PK "***The l😀icense #***"
+ *string(99)~T~~~~~~* firstName "Only __99__
characters are a
llowed dsfsdfsdfsdfs"
+ string last*Name*
+ string __phone__ UK
+ int _age_
+ }
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with unicode text without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ _**testẽζ➕Ø😀㌕ぼ**_ {
+ *__List~List~int~~sdfds__* **driversLicense** PK "***The l😀icense #***"
+ *string(99)~T~~~~~~* firstName "Only __99__
characters are a
llowed dsfsdfsdfsdfs"
+ string last*Name*
+ string __phone__ UK
+ int _age_
+ }
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render an ER diagram with relationships with unicode text`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ person[😀] {
+ string *first*Name
+ string _**last**Name_
+ }
+ a["*Customer Account*"] {
+ **string** ema*i*l
+ }
+ person ||--o| a : __hẽ😀__
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with relationships with unicode text without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ person[😀] {
+ string *first*Name
+ string _**last**Name_
+ }
+ a["*Customer Account*"] {
+ **string** ema*i*l
+ }
+ person ||--o| a : __hẽ😀__
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render an ER diagram with TB direction`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ direction TB
+ CAR ||--|{ NAMED-DRIVER : allows
+ PERSON ||..o{ NAMED-DRIVER : is
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with BT direction`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ direction BT
+ CAR ||--|{ NAMED-DRIVER : allows
+ PERSON ||..o{ NAMED-DRIVER : is
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with LR direction`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ direction LR
+ CAR ||--|{ NAMED-DRIVER : allows
+ PERSON ||..o{ NAMED-DRIVER : is
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render an ER diagram with RL direction`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ direction RL
+ CAR ||--|{ NAMED-DRIVER : allows
+ PERSON ||..o{ NAMED-DRIVER : is
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with styles applied from style statement`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]
+ style c,p fill:#f9f,stroke:blue, color:grey, font-size:24px,font-weight:bold
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with styles applied from style statement without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]
+ style c,p fill:#f9f,stroke:blue, color:grey, font-size:24px,font-weight:bold
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with styles applied from class statement`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]:::blue
+ classDef bold font-size:24px, font-weight: bold
+ classDef blue stroke:lightblue, color: #0000FF
+ class c,p bold
+ `,
+ options
+ );
+ });
+
+ it(`${description}should render entities with styles applied from class statement without htmlLabels`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]:::blue
+ classDef bold font-size:24px, font-weight: bold
+ classDef blue stroke:lightblue, color: #0000FF
+ class c,p bold
+ `,
+ { ...options, htmlLabels: false }
+ );
+ });
+
+ it(`${description}should render entities with styles applied from the default class and other styles`, () => {
+ imgSnapshotTest(
+ `
+ erDiagram
+ c[CUSTOMER]
+ p[PERSON]:::blue
+ classDef blue stroke:lightblue, color: #0000FF
+ classDef default fill:pink
+ style c color:green
+ `,
+ { ...options }
+ );
+ });
+ });
+});
diff --git a/cypress/integration/rendering/erDiagram.spec.js b/cypress/integration/rendering/erDiagram.spec.js
index aad9b1cf7..cbfec8218 100644
--- a/cypress/integration/rendering/erDiagram.spec.js
+++ b/cypress/integration/rendering/erDiagram.spec.js
@@ -109,8 +109,8 @@ describe('Entity Relationship Diagram', () => {
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
- // use within because the absolute value can be slightly different depending on the environment ±5%
- expect(maxWidthValue).to.be.within(140 * 0.95, 140 * 1.05);
+ // use within because the absolute value can be slightly different depending on the environment ±6%
+ expect(maxWidthValue).to.be.within(140 * 0.96, 140 * 1.06);
});
});
@@ -125,8 +125,8 @@ describe('Entity Relationship Diagram', () => {
);
cy.get('svg').should((svg) => {
const width = parseFloat(svg.attr('width'));
- // use within because the absolute value can be slightly different depending on the environment ±5%
- expect(width).to.be.within(140 * 0.95, 140 * 1.05);
+ // use within because the absolute value can be slightly different depending on the environment ±6%
+ expect(width).to.be.within(140 * 0.96, 140 * 1.06);
// expect(svg).to.have.attr('height', '465');
expect(svg).to.not.have.attr('style');
});
diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js
index d3a83ae5f..7b986cd2f 100644
--- a/cypress/integration/rendering/flowchart.spec.js
+++ b/cypress/integration/rendering/flowchart.spec.js
@@ -895,7 +895,7 @@ graph TD
imgSnapshotTest(
`
graph TD
- classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff
+ classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff
hello --> default
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
@@ -917,4 +917,21 @@ graph TD
}
);
});
+ it('#6369: edge color should affect arrow head', () => {
+ imgSnapshotTest(
+ `
+ flowchart LR
+ A --> B
+ A --> C
+ C --> D
+
+ linkStyle 0 stroke:#D50000
+ linkStyle 2 stroke:#D50000
+ `,
+ {
+ flowchart: { htmlLabels: true },
+ securityLevel: 'loose',
+ }
+ );
+ });
});
diff --git a/cypress/platform/yari2.html b/cypress/platform/yari2.html
new file mode 100644
index 000000000..bd5ddffc2
--- /dev/null
+++ b/cypress/platform/yari2.html
@@ -0,0 +1,337 @@
+
+
+
+
+
+
Basic ErNode
+
+ ---
+ config:
+ htmlLabels: false
+ look: handDrawn
+ theme: forest
+ ---
+ erDiagram
+ _**hiØ**_[*test*] {
+ *__List~List~int~~sdfds__* __driversLicense__ PK "***The l😀icense #***"
+ *string(99)~T~~~~~~* firstName "Only 99
characters are a
llowed dsfsdfsdfsdfs"
+ ~str ing~ lastName
+ string phone UK
+ int age
+ }
+ style PERSON color:red, stroke:blue,fill:#f9f
+ classDef test,test2 stroke:red
+ class PERSON test,test2
+
+
+
+
Basic ErNode
+
+ erDiagram
+ CAR {
+ string registrationNumber
+ string make
+ string model
+ }
+ PERSON {
+ string firstName
+ string lastName
+ int age
+ }
+
+ CAR:::someclass
+ PERSON:::anotherclass,someclass
+
+ classDef someclass fill:#f96
+ classDef anotherclass color:blue
+
+
+
+
+
+
+
Basic Relationship
+
+ ---
+ config:
+ htmlLabels: false
+ layout: elk
+ look: handDrawn
+ theme: forest
+ ---
+ erDiagram
+ "hi" }o..o{ ORDER : places
+ style hi fill:lightblue
+
+
+
+
Basic Relationship
+
+ ---
+ config:
+ htmlLabels: false
+ look: handDrawn
+ layout: elk
+ ---
+ erDiagram
+ CAR ||--|{ NAMED-DRIVER : allows
+ PERSON ||..o{ NAMED-DRIVER : is
+
+
+
+
Basic Relationship
+
+ ---
+ config:
+ htmlLabels: true
+ look: handDrawn
+ theme: forest
+ ---
+ erDiagram
+ CAR ||--o{ NAMED-DRIVER : allows
+ CAR {
+ test test PK "comment"
+ string make
+ string model
+ string[] parts
+ }
+ PERSON ||--o{ NAMED-DRIVER : is
+ PERSON ||--o{ CAR : is
+ PERSON {
+ string driversLicense PK "The license #"
+ string(99) firstName "Only 99 characters are allowed"
+ string lastName
+ string phone UK
+ int age
+ }
+ NAMED-DRIVER {
+ string carRegistrationNumber PK, FK
+ string driverLicence PK, FK
+ }
+ MANUFACTURER only one to zero or more CAR : makes
+
+
+
+
Basic Relationship
+
+ ---
+ title: simple ER diagram
+ config:
+ theme: forest
+ ---
+ erDiagram
+ direction TB
+ p[Pers😀on] {
+ string firstName
+ string lastName
+ }
+ a["Customer Account"] {
+ string email
+ }
+ p ||--o| a : has
+
+
+
+
+
Basic Relationship
+
+ ---
+ config:
+ layout: elk
+ ---
+ erDiagram
+ CUSTOMER }|..|{ DELIVERY-ADDRESS : has
+ CUSTOMER ||--o{ ORDER : places
+ CUSTOMER ||--o{ INVOICE : "liable for"
+ DELIVERY-ADDRESS ||--o{ ORDER : receives
+ INVOICE ||--|{ ORDER : covers
+ ORDER ||--|{ ORDER-ITEM : includes
+ PRODUCT-CATEGORY ||--|{ PRODUCT : contains
+ PRODUCT ||--o{ ORDER-ITEM : "ordered in"
+
+
+
+
Basic Relationship
+
+---
+ config:
+ layout: elk
+---
+ erDiagram
+ rental{
+ ~timestamp with time zone~ rental_date "NN"
+ ~integer~ inventory_id "NN"
+ ~integer~ customer_id "NN"
+ ~timestamp with time zone~ return_date
+ ~integer~ staff_id "NN"
+ ~integer~ rental_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ film_actor{
+ ~integer~ actor_id "NN"
+ ~integer~ film_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ film{
+ ~text~ title "NN"
+ ~text~ description
+ ~public.year~ release_year
+ ~integer~ language_id "NN"
+ ~integer~ original_language_id
+ ~smallint~ length
+ ~text[]~ special_features
+ ~tsvector~ fulltext "NN"
+ ~integer~ film_id "NN"
+ ~smallint~ rental_duration "NN"
+ ~numeric(4,2)~ rental_rate "NN"
+ ~numeric(5,2)~ replacement_cost "NN"
+ ~public.mpaa_rating~ rating
+ ~timestamp with time zone~ last_update "NN"
+ }
+ customer{
+ ~integer~ store_id "NN"
+ ~text~ first_name "NN"
+ ~text~ last_name "NN"
+ ~text~ email
+ ~integer~ address_id "NN"
+ ~integer~ active
+ ~integer~ customer_id "NN"
+ ~boolean~ activebool "NN"
+ ~date~ create_date "NN"
+ ~timestamp with time zone~ last_update
+ }
+ film_category{
+ ~integer~ film_id "NN"
+ ~integer~ category_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ actor{
+ ~text~ first_name "NN"
+ ~text~ last_name "NN"
+ ~integer~ actor_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ store{
+ ~integer~ manager_staff_id "NN"
+ ~integer~ address_id "NN"
+ ~integer~ store_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ city{
+ ~text~ city "NN"
+ ~integer~ country_id "NN"
+ ~integer~ city_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ language{
+ ~character(20)~ name "NN"
+ ~integer~ language_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ payment{
+ ~integer~ customer_id "NN"
+ ~integer~ staff_id "NN"
+ ~integer~ rental_id "NN"
+ ~numeric(5,2)~ amount "NN"
+ ~timestamp with time zone~ payment_date "NN"
+ ~integer~ payment_id "NN"
+ }
+ category{
+ ~text~ name "NN"
+ ~integer~ category_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ inventory{
+ ~integer~ film_id "NN"
+ ~integer~ store_id "NN"
+ ~integer~ inventory_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ address{
+ ~text~ address "NN"
+ ~text~ address2
+ ~text~ district "NN"
+ ~integer~ city_id "NN"
+ ~text~ postal_code
+ ~text~ phone "NN"
+ ~integer~ address_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ staff{
+ ~text~ first_name "NN"
+ ~text~ last_name "NN"
+ ~integer~ address_id "NN"
+ ~text~ email
+ ~integer~ store_id "NN"
+ ~text~ username "NN"
+ ~text~ password
+ ~bytea~ picture
+ ~integer~ staff_id "NN"
+ ~boolean~ active "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ country{
+ ~text~ country "NN"
+ ~integer~ country_id "NN"
+ ~timestamp with time zone~ last_update "NN"
+ }
+ film_actor }|..|| film : film_actor_film_id_fkey
+ film_actor }|..|| actor : film_actor_actor_id_fkey
+ address }|..|| city : address_city_id_fkey
+ city }|..|| country : city_country_id_fkey
+ customer }|..|| store : customer_store_id_fkey
+ customer }|..|| address : customer_address_id_fkey
+ film }|..|| language : film_original_language_id_fkey
+ film }|..|| language : film_language_id_fkey
+ film_category }|..|| film : film_category_film_id_fkey
+ film_category }|..|| category : film_category_category_id_fkey
+ inventory }|..|| store : inventory_store_id_fkey
+
+
+
+
+
+
+
+
diff --git a/docs/ecosystem/integrations-community.md b/docs/ecosystem/integrations-community.md
index 101252303..1dbfab8b3 100644
--- a/docs/ecosystem/integrations-community.md
+++ b/docs/ecosystem/integrations-community.md
@@ -70,6 +70,7 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [Notion](https://notion.so) ✅
- [Observable](https://observablehq.com/@observablehq/mermaid) ✅
- [Obsidian](https://help.obsidian.md/Editing+and+formatting/Advanced+formatting+syntax#Diagram) ✅
+- [Outline](https://docs.getoutline.com/s/guide/doc/diagrams-KQiKoT4wzK) ✅
- [Redmine](https://redmine.org)
- [Mermaid Macro](https://www.redmine.org/plugins/redmine_mermaid_macro)
- [Markdown for mermaid plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin)
diff --git a/docs/syntax/entityRelationshipDiagram.md b/docs/syntax/entityRelationshipDiagram.md
index 693cb53c7..3dec08524 100644
--- a/docs/syntax/entityRelationshipDiagram.md
+++ b/docs/syntax/entityRelationshipDiagram.md
@@ -92,7 +92,7 @@ Mermaid syntax for ER diagrams is compatible with PlantUML, with an extension to
Where:
-- `first-entity` is the name of an entity. Names must begin with an alphabetic character or an underscore (from v10.5.0+), and may also contain digits and hyphens.
+- `first-entity` is the name of an entity. Names support any unicode characters and can include spaces if surrounded by double quotes (e.g. "name with space").
- `relationship` describes the way that both entities inter-relate. See below.
- `second-entity` is the name of the other entity.
- `relationship-label` describes the relationship from the perspective of the first entity.
@@ -107,6 +107,34 @@ This statement can be read as _a property contains one or more rooms, and a room
Only the `first-entity` part of a statement is mandatory. This makes it possible to show an entity with no relationships, which can be useful during iterative construction of diagrams. If any other parts of a statement are specified, then all parts are mandatory.
+#### Unicode text
+
+Entity names, relationships, and attributes all support unicode text.
+
+```mermaid-example
+erDiagram
+ "This ❤ Unicode"
+```
+
+```mermaid
+erDiagram
+ "This ❤ Unicode"
+```
+
+#### Markdown formatting
+
+Markdown formatting and text is also supported.
+
+```mermaid-example
+erDiagram
+ "This **is** _Markdown_"
+```
+
+```mermaid
+erDiagram
+ "This **is** _Markdown_"
+```
+
### Relationship Syntax
The `relationship` part of each statement can be broken down into three sub-components:
@@ -145,6 +173,11 @@ Cardinality is a property that describes how many elements of another entity can
Relationships may be classified as either _identifying_ or _non-identifying_ and these are rendered with either solid or dashed lines respectively. This is relevant when one of the entities in question can not have independent existence without the other. For example a firm that insures people to drive cars might need to store data on `NAMED-DRIVER`s. In modelling this we might start out by observing that a `CAR` can be driven by many `PERSON` instances, and a `PERSON` can drive many `CAR`s - both entities can exist without the other, so this is a non-identifying relationship that we might specify in Mermaid as: `PERSON }|..|{ CAR : "driver"`. Note the two dots in the middle of the relationship that will result in a dashed line being drawn between the two entities. But when this many-to-many relationship is resolved into two one-to-many relationships, we observe that a `NAMED-DRIVER` cannot exist without both a `PERSON` and a `CAR` - the relationships become identifying and would be specified using hyphens, which translate to a solid line:
+| Value | Alias for |
+| :---: | :---------------: |
+| -- | _identifying_ |
+| .. | _non-identifying_ |
+
**Aliases**
| Value | Alias for |
@@ -155,13 +188,25 @@ Relationships may be classified as either _identifying_ or _non-identifying_ and
```mermaid-example
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
- PERSON ||--o{ NAMED-DRIVER : is
+ PERSON }o..o{ NAMED-DRIVER : is
```
```mermaid
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
- PERSON ||--o{ NAMED-DRIVER : is
+ PERSON }o..o{ NAMED-DRIVER : is
+```
+
+```mermaid-example
+erDiagram
+ CAR 1 to zero or more NAMED-DRIVER : allows
+ PERSON many(0) optionally to 0+ NAMED-DRIVER : is
+```
+
+```mermaid
+erDiagram
+ CAR 1 to zero or more NAMED-DRIVER : allows
+ PERSON many(0) optionally to 0+ NAMED-DRIVER : is
```
### Attributes
@@ -202,9 +247,9 @@ erDiagram
The `type` values must begin with an alphabetic character and may contain digits, hyphens, underscores, parentheses and square brackets. The `name` values follow a similar format to `type`, but may start with an asterisk as another option to indicate an attribute is a primary key. Other than that, there are no restrictions, and there is no implicit set of valid data types.
-### Entity Name Aliases (v10.5.0+)
+### Entity Name Aliases
-An alias can be added to an entity using square brackets. If provided, the alias will be showed in the diagram instead of the entity name.
+An alias can be added to an entity using square brackets. If provided, the alias will be showed in the diagram instead of the entity name. Alias names follow all of the same rules as entity names.
```mermaid-example
erDiagram
@@ -232,7 +277,7 @@ erDiagram
#### Attribute Keys and Comments
-Attributes may also have a `key` or comment defined. Keys can be `PK`, `FK` or `UK`, for Primary Key, Foreign Key or Unique Key. To specify multiple key constraints on a single attribute, separate them with a comma (e.g., `PK, FK`). A `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
+Attributes may also have a `key` or comment defined. Keys can be `PK`, `FK` or `UK`, for Primary Key, Foreign Key or Unique Key (markdown formatting and unicode is not supported for keys). To specify multiple key constraints on a single attribute, separate them with a comma (e.g., `PK, FK`). A `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
```mermaid-example
erDiagram
@@ -282,35 +327,318 @@ erDiagram
MANUFACTURER only one to zero or more CAR : makes
```
-### Other Things
+### Direction
-- If you want the relationship label to be more than one word, you must use double quotes around the phrase
-- If you don't want a label at all on a relationship, you must use an empty double-quoted string
-- (v11.1.0+) If you want a multi-line label on a relationship, use `
` between the two lines (`"first line
second line"`)
+The direction statement declares the direction of the diagram.
-## Styling
+This declares that the diagram is oriented from top to bottom (`TB`). This can be reversed to be oriented from bottom to top (`BT`).
-### Config options
+```mermaid-example
+erDiagram
+ direction TB
+ CUSTOMER ||--o{ ORDER : places
+ CUSTOMER {
+ string name
+ string custNumber
+ string sector
+ }
+ ORDER ||--|{ LINE-ITEM : contains
+ ORDER {
+ int orderNumber
+ string deliveryAddress
+ }
+ LINE-ITEM {
+ string productCode
+ int quantity
+ float pricePerUnit
+ }
+```
-For simple color customization:
+```mermaid
+erDiagram
+ direction TB
+ CUSTOMER ||--o{ ORDER : places
+ CUSTOMER {
+ string name
+ string custNumber
+ string sector
+ }
+ ORDER ||--|{ LINE-ITEM : contains
+ ORDER {
+ int orderNumber
+ string deliveryAddress
+ }
+ LINE-ITEM {
+ string productCode
+ int quantity
+ float pricePerUnit
+ }
+```
-| Name | Used as |
-| :------- | :------------------------------------------------------------------- |
-| `fill` | Background color of an entity or attribute |
-| `stroke` | Border color of an entity or attribute, line color of a relationship |
+This declares that the diagram is oriented from left to right (`LR`). This can be reversed to be oriented from right to left (`RL`).
-### Classes used
+```mermaid-example
+erDiagram
+ direction LR
+ CUSTOMER ||--o{ ORDER : places
+ CUSTOMER {
+ string name
+ string custNumber
+ string sector
+ }
+ ORDER ||--|{ LINE-ITEM : contains
+ ORDER {
+ int orderNumber
+ string deliveryAddress
+ }
+ LINE-ITEM {
+ string productCode
+ int quantity
+ float pricePerUnit
+ }
+```
-The following CSS class selectors are available for richer styling:
+```mermaid
+erDiagram
+ direction LR
+ CUSTOMER ||--o{ ORDER : places
+ CUSTOMER {
+ string name
+ string custNumber
+ string sector
+ }
+ ORDER ||--|{ LINE-ITEM : contains
+ ORDER {
+ int orderNumber
+ string deliveryAddress
+ }
+ LINE-ITEM {
+ string productCode
+ int quantity
+ float pricePerUnit
+ }
+```
-| Selector | Description |
-| :------------------------- | :---------------------------------------------------- |
-| `.er.attributeBoxEven` | The box containing attributes on even-numbered rows |
-| `.er.attributeBoxOdd` | The box containing attributes on odd-numbered rows |
-| `.er.entityBox` | The box representing an entity |
-| `.er.entityLabel` | The label for an entity |
-| `.er.relationshipLabel` | The label for a relationship |
-| `.er.relationshipLabelBox` | The box surrounding a relationship label |
-| `.er.relationshipLine` | The line representing a relationship between entities |
+Possible diagram orientations are:
+
+- TB - Top to bottom
+- BT - Bottom to top
+- RL - Right to left
+- LR - Left to right
+
+### Styling a node
+
+It is possible to apply specific styles such as a thicker border or a different background color to a node.
+
+```mermaid-example
+erDiagram
+ id1||--||id2 : label
+ style id1 fill:#f9f,stroke:#333,stroke-width:4px
+ style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+```
+
+```mermaid
+erDiagram
+ id1||--||id2 : label
+ style id1 fill:#f9f,stroke:#333,stroke-width:4px
+ style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
+```
+
+It is also possible to attach styles to a list of nodes in one statement:
+
+```
+ style nodeId1,nodeId2 styleList
+```
+
+#### Classes
+
+More convenient than defining the style every time is to define a class of styles and attach this class to the nodes that
+should have a different look.
+
+A class definition looks like the example below:
+
+```
+ classDef className fill:#f9f,stroke:#333,stroke-width:4px
+```
+
+It is also possible to define multiple classes in one statement:
+
+```
+ classDef firstClassName,secondClassName font-size:12pt
+```
+
+Attachment of a class to a node is done as per below:
+
+```
+ class nodeId1 className
+```
+
+It is also possible to attach a class to a list of nodes in one statement:
+
+```
+ class nodeId1,nodeId2 className
+```
+
+Multiple classes can be attached at the same time as well:
+
+```
+ class nodeId1,nodeId2 className1,className2
+```
+
+A shorter form of adding a class is to attach the classname to the node using the `:::`operator as per below:
+
+```mermaid-example
+erDiagram
+ direction TB
+ CAR:::someclass {
+ string registrationNumber
+ string make
+ string model
+ }
+ PERSON:::someclass {
+ string firstName
+ string lastName
+ int age
+ }
+ HOUSE:::someclass
+
+ classDef someclass fill:#f96
+```
+
+```mermaid
+erDiagram
+ direction TB
+ CAR:::someclass {
+ string registrationNumber
+ string make
+ string model
+ }
+ PERSON:::someclass {
+ string firstName
+ string lastName
+ int age
+ }
+ HOUSE:::someclass
+
+ classDef someclass fill:#f96
+```
+
+This form can be used when declaring relationships between entities:
+
+```mermaid-example
+erDiagram
+ CAR {
+ string registrationNumber
+ string make
+ string model
+ }
+ PERSON {
+ string firstName
+ string lastName
+ int age
+ }
+ PERSON:::foo ||--|| CAR : owns
+ PERSON o{--|| HOUSE:::bar : has
+
+ classDef foo stroke:#f00
+ classDef bar stroke:#0f0
+ classDef foobar stroke:#00f
+```
+
+```mermaid
+erDiagram
+ CAR {
+ string registrationNumber
+ string make
+ string model
+ }
+ PERSON {
+ string firstName
+ string lastName
+ int age
+ }
+ PERSON:::foo ||--|| CAR : owns
+ PERSON o{--|| HOUSE:::bar : has
+
+ classDef foo stroke:#f00
+ classDef bar stroke:#0f0
+ classDef foobar stroke:#00f
+```
+
+Similar to the class statement, the shorthand syntax can also apply multiple classes at once:
+
+```
+ nodeId:::className1,className2
+```
+
+### Default class
+
+If a class is named default it will be assigned to all classes without specific class definitions.
+
+```
+ classDef default fill:#f9f,stroke:#333,stroke-width:4px;
+```
+
+> **Note:** Custom styles from style or other class statements take priority and will overwrite the default styles. (e.g. The `default` class gives nodes a background color of pink but the `blue` class will give that node a background color of blue if applied.)
+
+```mermaid-example
+erDiagram
+ CAR {
+ string registrationNumber
+ string make
+ string model
+ }
+ PERSON {
+ string firstName
+ string lastName
+ int age
+ }
+ PERSON:::foo ||--|| CAR : owns
+ PERSON o{--|| HOUSE:::bar : has
+
+ classDef default fill:#f9f,stroke-width:4px
+ classDef foo stroke:#f00
+ classDef bar stroke:#0f0
+ classDef foobar stroke:#00f
+```
+
+```mermaid
+erDiagram
+ CAR {
+ string registrationNumber
+ string make
+ string model
+ }
+ PERSON {
+ string firstName
+ string lastName
+ int age
+ }
+ PERSON:::foo ||--|| CAR : owns
+ PERSON o{--|| HOUSE:::bar : has
+
+ classDef default fill:#f9f,stroke-width:4px
+ classDef foo stroke:#f00
+ classDef bar stroke:#0f0
+ classDef foobar stroke:#00f
+```
+
+## Configuration
+
+### Renderer
+
+The layout of the diagram is done with the renderer. The default renderer is dagre.
+
+You can opt to use an alternate renderer named elk by editing the configuration. The elk renderer is better for larger and/or more complex diagrams.
+
+```
+---
+ config:
+ layout: elk
+---
+```
+
+> **Note**
+> Note that the site needs to use mermaid version 9.4+ for this to work and have this featured enabled in the lazy-loading configuration.
diff --git a/packages/mermaid/CHANGELOG.md b/packages/mermaid/CHANGELOG.md
index 221d7c735..49407cda5 100644
--- a/packages/mermaid/CHANGELOG.md
+++ b/packages/mermaid/CHANGELOG.md
@@ -1,5 +1,49 @@
# mermaid
+## 11.5.0
+
+### Minor Changes
+
+- [#6187](https://github.com/mermaid-js/mermaid/pull/6187) [`7809b5a`](https://github.com/mermaid-js/mermaid/commit/7809b5a93fae127f45727071f5ff14325222c518) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Flowchart new syntax for node metadata bugs
+
+ - Incorrect label mapping for nodes when using `&`
+ - Syntax error when `}` with trailing spaces before new line
+
+- [#6136](https://github.com/mermaid-js/mermaid/pull/6136) [`ec0d9c3`](https://github.com/mermaid-js/mermaid/commit/ec0d9c389aa6018043187654044c1e0b5aa4f600) Thanks [@knsv](https://github.com/knsv)! - Adding support for animation of flowchart edges
+
+- [#6373](https://github.com/mermaid-js/mermaid/pull/6373) [`05bdf0e`](https://github.com/mermaid-js/mermaid/commit/05bdf0e20e2629fe77513218fbd4e28e65f75882) Thanks [@ashishjain0512](https://github.com/ashishjain0512)! - Upgrade Requirement and ER diagram to use the common renderer flow
+
+ - Added support for directions
+ - Added support for hand drawn look
+
+- [#6371](https://github.com/mermaid-js/mermaid/pull/6371) [`4d25cab`](https://github.com/mermaid-js/mermaid/commit/4d25caba8e65df078966a283e7e0ae1200bef595) Thanks [@knsv](https://github.com/knsv)! - The arrowhead color should match the color of the edge. Creates a unique clone of the arrow marker with the appropriate color.
+
+### Patch Changes
+
+- [#6064](https://github.com/mermaid-js/mermaid/pull/6064) [`2a91849`](https://github.com/mermaid-js/mermaid/commit/2a91849a38641e97ed6b20cb60aa4506d1b63177) Thanks [@NicolasNewman](https://github.com/NicolasNewman)! - fix: architecture diagrams no longer grow to extreme heights due to conflicting alignments
+
+- [#6198](https://github.com/mermaid-js/mermaid/pull/6198) [`963efa6`](https://github.com/mermaid-js/mermaid/commit/963efa64c794466dcd0f06bad6de6ba554d05a54) Thanks [@ferozmht](https://github.com/ferozmht)! - Fixes for consistent edge id creation & handling edge cases for animate edge feature
+
+- [#6196](https://github.com/mermaid-js/mermaid/pull/6196) [`127bac1`](https://github.com/mermaid-js/mermaid/commit/127bac1147034d8a8588cc8f7870abe92ebc945e) Thanks [@knsv](https://github.com/knsv)! - Fix for issue #6195 - allowing @ signs inside node labels
+
+- [#6212](https://github.com/mermaid-js/mermaid/pull/6212) [`90bbf90`](https://github.com/mermaid-js/mermaid/commit/90bbf90a83bf5da53fc8030cf1370bc8238fa4aa) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each class diagram
+
+- [#6218](https://github.com/mermaid-js/mermaid/pull/6218) [`232e60c`](https://github.com/mermaid-js/mermaid/commit/232e60c8cbaea804e6d98aa90f90d1ce76730e17) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: revert state db to resolve getData returning empty nodes and edges
+
+- [#6250](https://github.com/mermaid-js/mermaid/pull/6250) [`9cad3c7`](https://github.com/mermaid-js/mermaid/commit/9cad3c7aea3bbbc61495b23225ccff76d312783f) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - `mermaidAPI.getDiagramFromText()` now returns a new db instance on each call for state diagrams
+
+- [#6293](https://github.com/mermaid-js/mermaid/pull/6293) [`cfd84e5`](https://github.com/mermaid-js/mermaid/commit/cfd84e54d502f4d36a35b50478121558cfbef2c4) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - Added versioning to StateDB and updated tests and diagrams to use it.
+
+- [#6161](https://github.com/mermaid-js/mermaid/pull/6161) [`6cc31b7`](https://github.com/mermaid-js/mermaid/commit/6cc31b74530baa6d0f527346ab1395b0896bb3c2) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each flowchart
+
+- [#6272](https://github.com/mermaid-js/mermaid/pull/6272) [`ffa7804`](https://github.com/mermaid-js/mermaid/commit/ffa7804af0701b3d044d6794e36bd9132d6c7e8d) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each sequence diagram. Added unique IDs for messages.
+
+- [#6205](https://github.com/mermaid-js/mermaid/pull/6205) [`32a68d4`](https://github.com/mermaid-js/mermaid/commit/32a68d489ed83a5b79f516d6b2fb3a7505c5eb24) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: Gantt, Sankey and User Journey diagram are now able to pick font-family from mermaid config.
+
+- [#6295](https://github.com/mermaid-js/mermaid/pull/6295) [`da6361f`](https://github.com/mermaid-js/mermaid/commit/da6361f6527918b4b6a9c07cc9558cf2e2c709d2) Thanks [@omkarht](https://github.com/omkarht)! - fix: `getDirection` and `setDirection` in `stateDb` refactored to return and set actual direction
+
+- [#6185](https://github.com/mermaid-js/mermaid/pull/6185) [`3e32332`](https://github.com/mermaid-js/mermaid/commit/3e32332814c659e7ed1bb73d4a26ed4e61b77d59) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - `mermaidAPI.getDiagramFromText()` now returns a new different db for each state diagram
+
## 11.4.1
### Patch Changes
diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json
index 04911df64..0751d5a5f 100644
--- a/packages/mermaid/package.json
+++ b/packages/mermaid/package.json
@@ -1,6 +1,6 @@
{
"name": "mermaid",
- "version": "11.4.1",
+ "version": "11.5.0",
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
"type": "module",
"module": "./dist/mermaid.core.mjs",
diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts
index ff547eeec..400577638 100644
--- a/packages/mermaid/src/config.type.ts
+++ b/packages/mermaid/src/config.type.ts
@@ -799,6 +799,8 @@ export interface ErDiagramConfig extends BaseDiagramConfig {
*
*/
entityPadding?: number;
+ nodeSpacing?: number;
+ rankSpacing?: number;
/**
* Stroke color of box edges and lines.
*/
diff --git a/packages/mermaid/src/diagrams/er/erDb.js b/packages/mermaid/src/diagrams/er/erDb.js
deleted file mode 100644
index f24f48198..000000000
--- a/packages/mermaid/src/diagrams/er/erDb.js
+++ /dev/null
@@ -1,103 +0,0 @@
-import { log } from '../../logger.js';
-import { getConfig } from '../../diagram-api/diagramAPI.js';
-
-import {
- setAccTitle,
- getAccTitle,
- getAccDescription,
- setAccDescription,
- clear as commonClear,
- setDiagramTitle,
- getDiagramTitle,
-} from '../common/commonDb.js';
-
-let entities = new Map();
-let relationships = [];
-
-const Cardinality = {
- ZERO_OR_ONE: 'ZERO_OR_ONE',
- ZERO_OR_MORE: 'ZERO_OR_MORE',
- ONE_OR_MORE: 'ONE_OR_MORE',
- ONLY_ONE: 'ONLY_ONE',
- MD_PARENT: 'MD_PARENT',
-};
-
-const Identification = {
- NON_IDENTIFYING: 'NON_IDENTIFYING',
- IDENTIFYING: 'IDENTIFYING',
-};
-/**
- * Add entity
- * @param {string} name - The name of the entity
- * @param {string | undefined} alias - The alias of the entity
- */
-const addEntity = function (name, alias = undefined) {
- if (!entities.has(name)) {
- entities.set(name, { attributes: [], alias });
- log.info('Added new entity :', name);
- } else if (!entities.get(name).alias && alias) {
- entities.get(name).alias = alias;
- log.info(`Add alias '${alias}' to entity '${name}'`);
- }
-
- return entities.get(name);
-};
-
-const getEntities = () => entities;
-
-const addAttributes = function (entityName, attribs) {
- let entity = addEntity(entityName); // May do nothing (if entity has already been added)
-
- // Process attribs in reverse order due to effect of recursive construction (last attribute is first)
- let i;
- for (i = attribs.length - 1; i >= 0; i--) {
- entity.attributes.push(attribs[i]);
- log.debug('Added attribute ', attribs[i].attributeName);
- }
-};
-
-/**
- * Add a relationship
- *
- * @param entA The first entity in the relationship
- * @param rolA The role played by the first entity in relation to the second
- * @param entB The second entity in the relationship
- * @param rSpec The details of the relationship between the two entities
- */
-const addRelationship = function (entA, rolA, entB, rSpec) {
- let rel = {
- entityA: entA,
- roleA: rolA,
- entityB: entB,
- relSpec: rSpec,
- };
-
- relationships.push(rel);
- log.debug('Added new relationship :', rel);
-};
-
-const getRelationships = () => relationships;
-
-const clear = function () {
- entities = new Map();
- relationships = [];
- commonClear();
-};
-
-export default {
- Cardinality,
- Identification,
- getConfig: () => getConfig().er,
- addEntity,
- addAttributes,
- getEntities,
- addRelationship,
- getRelationships,
- clear,
- setAccTitle,
- getAccTitle,
- setAccDescription,
- getAccDescription,
- setDiagramTitle,
- getDiagramTitle,
-};
diff --git a/packages/mermaid/src/diagrams/er/erDb.ts b/packages/mermaid/src/diagrams/er/erDb.ts
new file mode 100644
index 000000000..95f2210b7
--- /dev/null
+++ b/packages/mermaid/src/diagrams/er/erDb.ts
@@ -0,0 +1,251 @@
+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 {
+ setAccTitle,
+ getAccTitle,
+ getAccDescription,
+ setAccDescription,
+ clear as commonClear,
+ setDiagramTitle,
+ getDiagramTitle,
+} from '../common/commonDb.js';
+import { getEdgeId } from '../../utils.js';
+import type { DiagramDB } from '../../diagram-api/types.js';
+
+export class ErDB implements DiagramDB {
+ private entities = new Map();
+ private relationships: Relationship[] = [];
+ private classes = new Map();
+ private direction = 'TB';
+
+ private Cardinality = {
+ ZERO_OR_ONE: 'ZERO_OR_ONE',
+ ZERO_OR_MORE: 'ZERO_OR_MORE',
+ ONE_OR_MORE: 'ONE_OR_MORE',
+ ONLY_ONE: 'ONLY_ONE',
+ MD_PARENT: 'MD_PARENT',
+ };
+
+ private Identification = {
+ NON_IDENTIFYING: 'NON_IDENTIFYING',
+ IDENTIFYING: 'IDENTIFYING',
+ };
+
+ constructor() {
+ this.clear();
+ this.addEntity = this.addEntity.bind(this);
+ this.addAttributes = this.addAttributes.bind(this);
+ this.addRelationship = this.addRelationship.bind(this);
+ this.setDirection = this.setDirection.bind(this);
+ this.addCssStyles = this.addCssStyles.bind(this);
+ this.addClass = this.addClass.bind(this);
+ this.setClass = this.setClass.bind(this);
+ this.setAccTitle = this.setAccTitle.bind(this);
+ this.setAccDescription = this.setAccDescription.bind(this);
+ }
+
+ /**
+ * Add entity
+ * @param name - The name of the entity
+ * @param alias - The alias of the entity
+ */
+ public addEntity(name: string, alias = ''): EntityNode {
+ if (!this.entities.has(name)) {
+ this.entities.set(name, {
+ id: `entity-${name}-${this.entities.size}`,
+ label: name,
+ attributes: [],
+ alias,
+ shape: 'erBox',
+ look: getConfig().look ?? 'default',
+ cssClasses: 'default',
+ cssStyles: [],
+ });
+ log.info('Added new entity :', name);
+ } else if (!this.entities.get(name)?.alias && alias) {
+ this.entities.get(name)!.alias = alias;
+ log.info(`Add alias '${alias}' to entity '${name}'`);
+ }
+
+ return this.entities.get(name)!;
+ }
+
+ public getEntity(name: string) {
+ return this.entities.get(name);
+ }
+
+ public getEntities() {
+ return this.entities;
+ }
+
+ public getClasses() {
+ return this.classes;
+ }
+
+ public addAttributes(entityName: string, attribs: Attribute[]) {
+ const entity = this.addEntity(entityName); // May do nothing (if entity has already been added)
+
+ // Process attribs in reverse order due to effect of recursive construction (last attribute is first)
+ let i;
+ for (i = attribs.length - 1; i >= 0; i--) {
+ if (!attribs[i].keys) {
+ attribs[i].keys = [];
+ }
+ if (!attribs[i].comment) {
+ attribs[i].comment = '';
+ }
+ entity.attributes.push(attribs[i]);
+ log.debug('Added attribute ', attribs[i].name);
+ }
+ }
+
+ /**
+ * Add a relationship
+ *
+ * @param entA - The first entity in the relationship
+ * @param rolA - The role played by the first entity in relation to the second
+ * @param entB - The second entity in the relationship
+ * @param rSpec - The details of the relationship between the two entities
+ */
+ public addRelationship(entA: string, rolA: string, entB: string, rSpec: RelSpec) {
+ const entityA = this.entities.get(entA);
+ const entityB = this.entities.get(entB);
+ if (!entityA || !entityB) {
+ return;
+ }
+
+ const rel = {
+ entityA: entityA.id,
+ roleA: rolA,
+ entityB: entityB.id,
+ relSpec: rSpec,
+ };
+
+ this.relationships.push(rel);
+ log.debug('Added new relationship :', rel);
+ }
+
+ public getRelationships() {
+ return this.relationships;
+ }
+
+ public getDirection() {
+ return this.direction;
+ }
+
+ public setDirection(dir: string) {
+ this.direction = dir;
+ }
+
+ private getCompiledStyles(classDefs: string[]) {
+ let compiledStyles: string[] = [];
+ for (const customClass of classDefs) {
+ const cssClass = this.classes.get(customClass);
+ if (cssClass?.styles) {
+ compiledStyles = [...compiledStyles, ...(cssClass.styles ?? [])].map((s) => s.trim());
+ }
+ if (cssClass?.textStyles) {
+ compiledStyles = [...compiledStyles, ...(cssClass.textStyles ?? [])].map((s) => s.trim());
+ }
+ }
+ return compiledStyles;
+ }
+
+ public addCssStyles(ids: string[], styles: string[]) {
+ for (const id of ids) {
+ const entity = this.entities.get(id);
+ if (!styles || !entity) {
+ return;
+ }
+ for (const style of styles) {
+ entity.cssStyles!.push(style);
+ }
+ }
+ }
+
+ public addClass(ids: string[], style: string[]) {
+ ids.forEach((id) => {
+ let classNode = this.classes.get(id);
+ if (classNode === undefined) {
+ classNode = { id, styles: [], textStyles: [] };
+ this.classes.set(id, classNode);
+ }
+
+ if (style) {
+ style.forEach(function (s) {
+ if (/color/.exec(s)) {
+ const newStyle = s.replace('fill', 'bgFill');
+ classNode.textStyles.push(newStyle);
+ }
+ classNode.styles.push(s);
+ });
+ }
+ });
+ }
+
+ public setClass(ids: string[], classNames: string[]) {
+ for (const id of ids) {
+ const entity = this.entities.get(id);
+ if (entity) {
+ for (const className of classNames) {
+ entity.cssClasses += ' ' + className;
+ }
+ }
+ }
+ }
+
+ public clear() {
+ this.entities = new Map();
+ this.classes = new Map();
+ this.relationships = [];
+ commonClear();
+ }
+
+ public getData() {
+ const nodes: Node[] = [];
+ const edges: Edge[] = [];
+ const config = getConfig();
+
+ for (const entityKey of this.entities.keys()) {
+ const entityNode = this.entities.get(entityKey);
+ if (entityNode) {
+ entityNode.cssCompiledStyles = this.getCompiledStyles(entityNode.cssClasses!.split(' '));
+ nodes.push(entityNode as unknown as Node);
+ }
+ }
+
+ let count = 0;
+ for (const relationship of this.relationships) {
+ const edge: Edge = {
+ id: getEdgeId(relationship.entityA, relationship.entityB, {
+ prefix: 'id',
+ counter: count++,
+ }),
+ type: 'normal',
+ curve: 'basis',
+ start: relationship.entityA,
+ end: relationship.entityB,
+ label: relationship.roleA,
+ labelpos: 'c',
+ thickness: 'normal',
+ classes: 'relationshipLine',
+ arrowTypeStart: relationship.relSpec.cardB.toLowerCase(),
+ arrowTypeEnd: relationship.relSpec.cardA.toLowerCase(),
+ pattern: relationship.relSpec.relType == 'IDENTIFYING' ? 'solid' : 'dashed',
+ look: config.look,
+ };
+ edges.push(edge);
+ }
+ return { nodes, edges, other: {}, config, direction: 'TB' };
+ }
+
+ public setAccTitle = setAccTitle;
+ public getAccTitle = getAccTitle;
+ public setAccDescription = setAccDescription;
+ public getAccDescription = getAccDescription;
+ public setDiagramTitle = setDiagramTitle;
+ public getDiagramTitle = getDiagramTitle;
+ public getConfig = () => getConfig().er;
+}
diff --git a/packages/mermaid/src/diagrams/er/erDiagram.ts b/packages/mermaid/src/diagrams/er/erDiagram.ts
index adfa525fc..1e3f0a4e1 100644
--- a/packages/mermaid/src/diagrams/er/erDiagram.ts
+++ b/packages/mermaid/src/diagrams/er/erDiagram.ts
@@ -1,12 +1,14 @@
// @ts-ignore: TODO: Fix ts errors
import erParser from './parser/erDiagram.jison';
-import erDb from './erDb.js';
-import erRenderer from './erRenderer.js';
+import { ErDB } from './erDb.js';
+import * as renderer from './erRenderer-unified.js';
import erStyles from './styles.js';
export const diagram = {
parser: erParser,
- db: erDb,
- renderer: erRenderer,
+ get db() {
+ return new ErDB();
+ },
+ renderer,
styles: erStyles,
};
diff --git a/packages/mermaid/src/diagrams/er/erRenderer-unified.ts b/packages/mermaid/src/diagrams/er/erRenderer-unified.ts
new file mode 100644
index 000000000..9735dd3bb
--- /dev/null
+++ b/packages/mermaid/src/diagrams/er/erRenderer-unified.ts
@@ -0,0 +1,66 @@
+import { getConfig } from '../../diagram-api/diagramAPI.js';
+import { log } from '../../logger.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 utils from '../../utils.js';
+import { select } from 'd3';
+
+export const draw = async function (text: string, id: string, _version: string, diag: any) {
+ log.info('REF0:');
+ log.info('Drawing er diagram (unified)', id);
+ const { securityLevel, er: conf, layout } = getConfig();
+
+ // 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 = diag.db.getData() as LayoutData;
+
+ // Create the root SVG - the element is the div containing the SVG element
+ const svg = getDiagramElement(id, securityLevel);
+
+ data4Layout.type = diag.type;
+ data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout);
+
+ // Workaround as when rendering and setting up the graph it uses flowchart spacing before data4Layout spacing?
+ data4Layout.config.flowchart!.nodeSpacing = conf?.nodeSpacing || 140;
+ data4Layout.config.flowchart!.rankSpacing = conf?.rankSpacing || 80;
+ data4Layout.direction = diag.db.getDirection();
+
+ data4Layout.markers = ['only_one', 'zero_or_one', 'one_or_more', 'zero_or_more'];
+ data4Layout.diagramId = id;
+ await render(data4Layout, svg);
+ // Elk layout algorithm displays markers above nodes, so move edges to top so they are "painted" over by the nodes.
+ if (data4Layout.layoutAlgorithm === 'elk') {
+ svg.select('.edges').lower();
+ }
+
+ // Sets the background nodes to the same position as their original counterparts.
+ // Background nodes are created when the look is handDrawn so the ER diagram markers do not show underneath.
+ const backgroundNodes = svg.selectAll('[id*="-background"]');
+ // eslint-disable-next-line unicorn/prefer-spread
+ if (Array.from(backgroundNodes).length > 0) {
+ backgroundNodes.each(function (this: SVGElement) {
+ const backgroundNode = select(this);
+ const backgroundId = backgroundNode.attr('id');
+
+ const nonBackgroundId = backgroundId.replace('-background', '');
+ const nonBackgroundNode = svg.select(`#${CSS.escape(nonBackgroundId)}`);
+
+ if (!nonBackgroundNode.empty()) {
+ const transform = nonBackgroundNode.attr('transform');
+ backgroundNode.attr('transform', transform);
+ }
+ });
+ }
+
+ const padding = 8;
+ utils.insertTitle(
+ svg,
+ 'erDiagramTitleText',
+ conf?.titleTopMargin ?? 25,
+ diag.db.getDiagramTitle()
+ );
+
+ setupViewPortForSVG(svg, padding, 'erDiagram', conf?.useMaxWidth ?? true);
+};
diff --git a/packages/mermaid/src/diagrams/er/erTypes.ts b/packages/mermaid/src/diagrams/er/erTypes.ts
new file mode 100644
index 000000000..13f9b2669
--- /dev/null
+++ b/packages/mermaid/src/diagrams/er/erTypes.ts
@@ -0,0 +1,37 @@
+export interface EntityNode {
+ id: string;
+ label: string;
+ attributes: Attribute[];
+ alias: string;
+ shape: string;
+ look?: string;
+ cssClasses?: string;
+ cssStyles?: string[];
+ cssCompiledStyles?: string[];
+}
+
+export interface Attribute {
+ type: string;
+ name: string;
+ keys: ('PK' | 'FK' | 'UK')[];
+ comment: string;
+}
+
+export interface Relationship {
+ entityA: string;
+ roleA: string;
+ entityB: string;
+ relSpec: RelSpec;
+}
+
+export interface RelSpec {
+ cardA: string;
+ cardB: string;
+ relType: string;
+}
+
+export interface EntityClass {
+ id: string;
+ styles: string[];
+ textStyles: string[];
+}
diff --git a/packages/mermaid/src/diagrams/er/parser/erDiagram.jison b/packages/mermaid/src/diagrams/er/parser/erDiagram.jison
index 135efc784..2b59309fb 100644
--- a/packages/mermaid/src/diagrams/er/parser/erDiagram.jison
+++ b/packages/mermaid/src/diagrams/er/parser/erDiagram.jison
@@ -5,6 +5,7 @@
%x acc_title
%x acc_descr
%x acc_descr_multiline
+%x style
%%
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
@@ -14,6 +15,10 @@ accDescr\s*":"\s* { this.begin("ac
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
[\}] { this.popState(); }
[^\}]* return "acc_descr_multiline_value";
+.*direction\s+TB[^\n]* return 'direction_tb';
+.*direction\s+BT[^\n]* return 'direction_bt';
+.*direction\s+RL[^\n]* return 'direction_rl';
+.*direction\s+LR[^\n]* return 'direction_lr';
[\n]+ return 'NEWLINE';
\s+ /* skip whitespace */
[\s]+ return 'SPACE';
@@ -21,11 +26,15 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
\"[^"]*\" return 'WORD';
"erDiagram" return 'ER_DIAGRAM';
"{" { this.begin("block"); return 'BLOCK_START'; }
-"," return 'COMMA';
+\# return 'BRKT';
+"#" return 'BRKT';
+"," return 'COMMA';
+":::" return 'STYLE_SEPARATOR';
+":" return 'COLON';
\s+ /* skip whitespace in block */
\b((?:PK)|(?:FK)|(?:UK))\b return 'ATTRIBUTE_KEY'
-(.*?)[~](.*?)*[~] return 'ATTRIBUTE_WORD';
-[\*A-Za-z_][A-Za-z0-9\-_\[\]\(\)]* return 'ATTRIBUTE_WORD'
+([^\s]*)[~].*[~]([^\s]*) return 'ATTRIBUTE_WORD';
+([\*A-Za-z_\u00C0-\uFFFF][A-Za-z0-9\-\_\[\]\(\)\u00C0-\uFFFF\*]*) return 'ATTRIBUTE_WORD';
\"[^"]*\" return 'COMMENT';
[\n]+ /* nothing */
"}" { this.popState(); return 'BLOCK_STOP'; }
@@ -33,6 +42,14 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
"[" return 'SQS';
"]" return 'SQE';
+"style" { this.begin("style"); return 'STYLE'; }
+