From 03bbfa0b9467ac79d91542760b7bbe624eb4c042 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Mon, 11 Mar 2024 13:37:47 -0500 Subject: [PATCH 01/60] build: added cytoscape-fcose --- packages/mermaid/package.json | 2 ++ pnpm-lock.yaml | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index 3626671ca5..ad2a708d2e 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -64,6 +64,7 @@ "@types/d3-scale-chromatic": "^3.0.0", "cytoscape": "^3.28.1", "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", "d3": "^7.4.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.10", @@ -83,6 +84,7 @@ "devDependencies": { "@adobe/jsonschema2md": "^7.1.4", "@types/cytoscape": "^3.19.9", + "@types/cytoscape-fcose": "^2.2.4", "@types/d3": "^7.4.0", "@types/d3-sankey": "^0.12.1", "@types/d3-scale": "^4.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index faac50fae9..0853a8d390 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -214,6 +214,9 @@ importers: cytoscape-cose-bilkent: specifier: ^4.1.0 version: 4.1.0(cytoscape@3.28.1) + cytoscape-fcose: + specifier: ^2.2.0 + version: 2.2.0(cytoscape@3.28.1) d3: specifier: ^7.4.0 version: 7.8.5 @@ -266,6 +269,9 @@ importers: '@types/cytoscape': specifier: ^3.19.9 version: 3.19.16 + '@types/cytoscape-fcose': + specifier: ^2.2.4 + version: 2.2.4 '@types/d3': specifier: ^7.4.0 version: 7.4.3 @@ -4811,6 +4817,12 @@ packages: '@types/node': 20.11.24 dev: true + /@types/cytoscape-fcose@2.2.4: + resolution: {integrity: sha512-QwWtnT8HI9h+DHhG5krGc1ZY0Ex+cn85MvX96ZNAjSxuXiZDnjIZW/ypVkvvubTjIY4rSdkJY1D/Nsn8NDpmAw==} + dependencies: + '@types/cytoscape': 3.19.16 + dev: true + /@types/cytoscape@3.19.16: resolution: {integrity: sha512-A3zkjaZ6cOGyqEvrVuC1YUgiRSJhDZOj8Qhd1ALH2/+YxH2za1BOmR4RWQsKYHsc+aMP/IWoqg1COuUbZ39t/g==} dev: true @@ -7885,6 +7897,12 @@ packages: layout-base: 1.0.2 dev: false + /cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + dependencies: + layout-base: 2.0.1 + dev: false + /cosmiconfig-typescript-loader@4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6)(ts-node@10.9.2)(typescript@5.3.3): resolution: {integrity: sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw==} engines: {node: '>=v14.21.3'} @@ -8230,6 +8248,15 @@ packages: cytoscape: 3.28.1(patch_hash=claipxynndhyqyu2csninuoh5e) dev: false + /cytoscape-fcose@2.2.0(cytoscape@3.28.1): + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + dependencies: + cose-base: 2.2.0 + cytoscape: 3.28.1(patch_hash=claipxynndhyqyu2csninuoh5e) + dev: false + /cytoscape@3.28.1(patch_hash=claipxynndhyqyu2csninuoh5e): resolution: {integrity: sha512-xyItz4O/4zp9/239wCcH8ZcFuuZooEeF8KHRmzjDfGdXsj3OG9MFSMA0pJE0uX3uCN/ygof6hHf4L7lst+JaDg==} engines: {node: '>=0.10'} @@ -12125,6 +12152,10 @@ packages: resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} dev: false + /layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + dev: false + /lazy-ass@1.6.0: resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} engines: {node: '> 0.8'} From 5a4d4972e22828de1b6dc630e987b525efdb8ca2 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Mon, 11 Mar 2024 13:41:43 -0500 Subject: [PATCH 02/60] feat: added architecture diagram parser --- .../architecture/parser/architecture.jison | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 packages/mermaid/src/diagrams/architecture/parser/architecture.jison diff --git a/packages/mermaid/src/diagrams/architecture/parser/architecture.jison b/packages/mermaid/src/diagrams/architecture/parser/architecture.jison new file mode 100644 index 0000000000..d92a1d1dbc --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/parser/architecture.jison @@ -0,0 +1,119 @@ +%lex +%options case-insensitive + +%x GROUP SERVICE LINE +%% + +\%\%(?!\{)[^\n]* /* skip comments */ +[^\}]\%\%[^\n]* /* skip comments */ +[\n\r]+ { this.popState(); return 'NEWLINE'; } +\%\%[^\n]* /* do nothing */ +[\s]+ /* skip all whitespace */ +"architecture" return 'ARCHITECTURE'; +"service" { this.begin('SERVICE'); return 'SERVICE'; } +"group" { this.begin('GROUP'); return 'GROUP'; } +((?!\n)\s)+ /* skip same-line whitespace */ +"in" return 'IN'; +[\w]+ return 'id'; +\([\w]*\) return 'icon'; +\[[\w ]*\] return 'title'; +[\w]+ { this.begin('LINE'); return 'id'; } +\<[L|R|T|B]"-" return 'ARROW_LEFT_INTO'; +[L|R|T|B]"-" return 'ARROW_LEFT'; +"-"[L|R|T|B]\> return 'ARROW_RIGHT_INTO'; +"-"[L|R|T|B] return 'ARROW_RIGHT'; +[\w]+ return 'id'; +\[[\w ]*\] return 'title'; +<> return 'EOF'; + +/lex + +%start start + +%% + +start + : eol start + | ARCHITECTURE document { return $2; } + ; + +document + : /* empty */ + | document line + ; + +line + : statement eol { $$ = $1 } + ; + +statement + : /* empty */ + | group_statement + | service_statement + | line_statement + ; + +line_statement + : id ARROW_LEFT_INTO ARROW_RIGHT_INTO id + { yy.addLine($1, $2[1], $4, $3[1], {lhs_into: true, rhs_into: true}) } + | id ARROW_LEFT_INTO ARROW_RIGHT id + { yy.addLine($1, $2[1], $4, $3[1], {lhs_into: true}) } + | id ARROW_LEFT ARROW_RIGHT_INTO id + { yy.addLine($1, $2[0], $4, $3[1], {rhs_into: true}) } + | id ARROW_LEFT ARROW_RIGHT id + { yy.addLine($1, $2[0], $4, $3[1]) } + | id ARROW_LEFT_INTO title ARROW_RIGHT_INTO id + { yy.addLine($1, $2[1], $5, $4[1], { title: $3.slice(1,-1), lhs_into: true, rhs_into: true }) } + | id ARROW_LEFT_INTO title ARROW_RIGHT id + { yy.addLine($1, $2[1], $5, $4[1], { title: $3.slice(1,-1), lhs_into: true }) } + | id ARROW_LEFT title ARROW_RIGHT_INTO id + { yy.addLine($1, $2[0], $5, $4[1], { title: $3.slice(1,-1), rhs_into: true }) } + | id ARROW_LEFT title ARROW_RIGHT id + { yy.addLine($1, $2[0], $5, $4[1], { title: $3.slice(1,-1) }) } + ; + +group_statement + : 'GROUP' id + { yy.addGroup($2) } + | 'GROUP' id icon + { yy.addGroup($2, {icon: $3.slice(1,-1)}) } + | 'GROUP' id title + { yy.addGroup($2, {title: $3.slice(1,-1)}) } + | 'GROUP' id icon title + { yy.addGroup($2, {icon: $3.slice(1,-1), title: $4.slice(1,-1)}) } + | 'GROUP' id 'IN' id + { yy.addGroup($2, {in: $4.trim()}) } + | 'GROUP' id icon 'IN' id + { yy.addGroup($2, {icon: $3.slice(1,-1), in: $5.trim()}) } + | 'GROUP' id title 'IN' id + { yy.addGroup($2, {title: $3.slice(1,-1), in: $5.trim()}) } + | 'GROUP' id icon title 'IN' id + { yy.addGroup($2, {icon: $3.slice(1,-1), title: $4.slice(1,-1), in: $6.trim()}) } + ; + +service_statement + : 'SERVICE' id + { yy.addService($2) } + | 'SERVICE' id icon + { yy.addService($2, { icon: $3.slice(1,-1) }) } + | 'SERVICE' id title + { yy.addService($2, { title: $3.slice(1,-1) }) } + | 'SERVICE' id icon title + { yy.addService($2, { icon: $3.slice(1,-1), title: $4.slice(1,-1) }) } + | 'SERVICE' id 'IN' id + { yy.addService($2, { in: $4.trim() }) } + | 'SERVICE' id icon 'IN' id + { yy.addService($2, { icon: $3.slice(1,-1), in: $5.trim() }) } + | 'SERVICE' id title 'IN' id + { yy.addService($2, { title: $3.slice(1,-1), in: $5.trim() }) } + | 'SERVICE' id icon title 'IN' id + { yy.addService($2, { icon: $3.slice(1,-1), title: $4.slice(1,-1), in: $6.trim() }) } + ; + +eol + : NEWLINE + | ';' + | EOF + ; + +%% \ No newline at end of file From 346ae22108c45ee0b204897d153c6929fcedcfb4 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Mon, 11 Mar 2024 15:13:05 -0500 Subject: [PATCH 03/60] feat: registered architecture diagram --- .vite/jsonSchemaPlugin.ts | 1 + .../scripts/create-types-from-json-schema.mts | 1 + packages/mermaid/src/config.type.ts | 11 +++++++++ .../src/diagram-api/diagram-orchestration.ts | 4 +++- .../architecture/architectureDetector.ts | 24 +++++++++++++++++++ .../mermaid/src/schemas/config.schema.yaml | 21 ++++++++++++++++ 6 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 packages/mermaid/src/diagrams/architecture/architectureDetector.ts diff --git a/.vite/jsonSchemaPlugin.ts b/.vite/jsonSchemaPlugin.ts index dd9af8cc55..a69f6889b3 100644 --- a/.vite/jsonSchemaPlugin.ts +++ b/.vite/jsonSchemaPlugin.ts @@ -26,6 +26,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [ 'c4', 'sankey', 'block', + 'architecture' ] as const; /** diff --git a/packages/mermaid/scripts/create-types-from-json-schema.mts b/packages/mermaid/scripts/create-types-from-json-schema.mts index b028fe818d..fbd12a37ff 100644 --- a/packages/mermaid/scripts/create-types-from-json-schema.mts +++ b/packages/mermaid/scripts/create-types-from-json-schema.mts @@ -54,6 +54,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [ 'gitGraph', 'c4', 'sankey', + 'architecture' ]; /** diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 0ba3178680..014ff65e77 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -165,6 +165,7 @@ export interface MermaidConfig { quadrantChart?: QuadrantChartConfig; xyChart?: XYChartConfig; requirement?: RequirementDiagramConfig; + architecture?: ArchitectureDiagramConfig; mindmap?: MindmapDiagramConfig; gitGraph?: GitGraphDiagramConfig; c4?: C4DiagramConfig; @@ -629,6 +630,16 @@ export interface RequirementDiagramConfig extends BaseDiagramConfig { rect_padding?: number; line_height?: number; } +/** + * The object containing configurations specific for architecture diagrams + * + * This interface was referenced by `MermaidConfig`'s JSON-Schema + * via the `definition` "ArchitectureDiagramConfig". + */ +export interface ArchitectureDiagramConfig extends BaseDiagramConfig { + padding?: number; + maxNodeWidth?: number; +} /** * The object containing configurations specific for mindmap diagrams * diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index eb123c4a20..84697022ca 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -21,6 +21,7 @@ import timeline from '../diagrams/timeline/detector.js'; import mindmap from '../diagrams/mindmap/detector.js'; import sankey from '../diagrams/sankey/sankeyDetector.js'; import block from '../diagrams/block/blockDetector.js'; +import architecture from '../diagrams/architecture/architectureDetector.js' import { registerLazyLoadedDiagrams } from './detectType.js'; import { registerDiagram } from './diagramAPI.js'; @@ -89,6 +90,7 @@ export const addDiagrams = () => { quadrantChart, sankey, xychart, - block + block, + architecture ); }; diff --git a/packages/mermaid/src/diagrams/architecture/architectureDetector.ts b/packages/mermaid/src/diagrams/architecture/architectureDetector.ts new file mode 100644 index 0000000000..c15b474abc --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureDetector.ts @@ -0,0 +1,24 @@ +import type { + DiagramDetector, + DiagramLoader, + ExternalDiagramDefinition, +} from '../../diagram-api/types.js'; + +const id = 'architecture'; + +const detector: DiagramDetector = (txt) => { + return /^\s*architecture/.test(txt); +}; + +const loader: DiagramLoader = async () => { + const { diagram } = await import('./architectureDiagram.js'); + return { id, diagram }; +}; + +const architecture: ExternalDiagramDefinition = { + id, + detector, + loader, +}; + +export default architecture; diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index 3e7fd58ec5..601939beed 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -45,6 +45,7 @@ required: - quadrantChart - xyChart - requirement + - architecture - mindmap - gitGraph - c4 @@ -217,6 +218,8 @@ properties: $ref: '#/$defs/XYChartConfig' requirement: $ref: '#/$defs/RequirementDiagramConfig' + architecture: + $ref: '#/$defs/ArchitectureDiagramConfig' mindmap: $ref: '#/$defs/MindmapDiagramConfig' gitGraph: @@ -853,6 +856,24 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) type: number default: 20 + ArchitectureDiagramConfig: + title: Architecture Diagram Config + allOf: [{ $ref: '#/$defs/BaseDiagramConfig'}] + description: The object containing configurations specific for architecture diagrams + type: object + unevaluatedProperties: false + required: + - useMaxWidth + - padding + - maxNodeWidth + properties: + padding: + type: number + default: 10 + maxNodeWidth: + type: number + default: 200 + MindmapDiagramConfig: title: Mindmap Diagram Config allOf: [{ $ref: '#/$defs/BaseDiagramConfig' }] From cc22e13e71423491926d413199702a3d85ebff16 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Mon, 11 Mar 2024 15:14:21 -0500 Subject: [PATCH 04/60] feat: added architecture DB & types --- .../diagrams/architecture/architectureDb.ts | 115 ++++++++++++++++++ .../architecture/architectureTypes.ts | 64 ++++++++++ 2 files changed, 179 insertions(+) create mode 100644 packages/mermaid/src/diagrams/architecture/architectureDb.ts create mode 100644 packages/mermaid/src/diagrams/architecture/architectureTypes.ts diff --git a/packages/mermaid/src/diagrams/architecture/architectureDb.ts b/packages/mermaid/src/diagrams/architecture/architectureDb.ts new file mode 100644 index 0000000000..5aecba746d --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureDb.ts @@ -0,0 +1,115 @@ +import type { ArchitectureFields, ArchitectureDB, ArchitectureService, ArchitectureGroup, ArchitectureDirection, ArchitectureLine } from "./architectureTypes.js"; +import { isArchitectureDirection } from "./architectureTypes.js"; +import { + setAccTitle, + getAccTitle, + setDiagramTitle, + getDiagramTitle, + getAccDescription, + setAccDescription, + clear as commonClear, +} from '../common/commonDb.js'; +import type { ArchitectureDiagramConfig } from '../../config.type.js'; +import DEFAULT_CONFIG from '../../defaultConfig.js'; +import type { D3Element } from "../../mermaidAPI.js"; + +export const DEFAULT_ARCHITECTURE_CONFIG: Required = DEFAULT_CONFIG.architecture; +export const DEFAULT_ARCHITECTURE_DB: ArchitectureFields = { + services: [], + groups: [], + lines: [], + cnt: 0, + config: DEFAULT_ARCHITECTURE_CONFIG, +} as const; + +let services = DEFAULT_ARCHITECTURE_DB.services; +let groups = DEFAULT_ARCHITECTURE_DB.groups; +let lines = DEFAULT_ARCHITECTURE_DB.lines; +let elements: Record = {}; +let cnt = DEFAULT_ARCHITECTURE_DB.cnt; + +const config: Required = structuredClone(DEFAULT_ARCHITECTURE_CONFIG); + +const getConfig = (): Required => structuredClone(config); + +const clear = (): void => { + services = structuredClone(DEFAULT_ARCHITECTURE_DB.services); + groups = structuredClone(DEFAULT_ARCHITECTURE_DB.groups); + lines = structuredClone(DEFAULT_ARCHITECTURE_DB.lines); + elements = {}; + cnt = 0; + commonClear(); +}; + +const addService = function (id: string, opts: Omit = {}) { + const {icon, in: inside, title} = opts; + services.push({ + id, + icon, + title, + in: inside + }); +} +const getServices = (): ArchitectureService[] => services; + +const addGroup = function (id: string, opts: Omit = {}) { + const {icon, in: inside, title} = opts; + groups.push({ + id, + icon, + title, + in: inside + }); +} +const getGroups = (): ArchitectureGroup[] => groups; + + +const addLine = function (lhs_id: string, lhs_dir: ArchitectureDirection, rhs_id: string, rhs_dir: ArchitectureDirection, opts: Omit = {}) { + const {title, lhs_into, rhs_into} = opts; + + if (!isArchitectureDirection(lhs_dir)) { + throw new Error(`Invalid direction given for left hand side of line ${lhs_id}--${rhs_id}. Expected (L,R,T,B) got ${lhs_dir}`) + } + if (!isArchitectureDirection(rhs_dir)) { + throw new Error(`Invalid direction given for right hand side of line ${lhs_id}--${rhs_id}. Expected (L,R,T,B) got ${rhs_dir}`) + } + + lines.push({ + lhs_id, + lhs_dir, + rhs_id, + rhs_dir, + title, + lhs_into, + rhs_into + }); +} +const getLines = (): ArchitectureLine[] => lines; + + +const setElementForId = (id: string, element: D3Element) => { + elements[id] = element; +}; +const getElementById = (id: string) => elements[id]; + + + +export const db: ArchitectureDB = { + getConfig, + clear, + setDiagramTitle, + getDiagramTitle, + setAccTitle, + getAccTitle, + setAccDescription, + getAccDescription, + + addService, + getServices, + addGroup, + getGroups, + addLine, + getLines, + setElementForId, + getElementById +}; \ No newline at end of file diff --git a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts new file mode 100644 index 0000000000..dbc663b73d --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts @@ -0,0 +1,64 @@ +import type { DiagramDB } from '../../diagram-api/types.js'; +import type { ArchitectureDiagramConfig } from '../../config.type.js'; +import type { D3Element } from '../../mermaidAPI.js'; + +export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B' +export const isArchitectureDirection = function(x: unknown): x is ArchitectureDirection { + const temp = x as ArchitectureDirection; + return (temp === 'L' || temp === 'R' || temp === 'T' || temp === 'B') +} +export const isArchitectureDirectionX = function(x: ArchitectureDirection): x is Extract { + const temp = x as Extract + return (temp === 'L' || temp === 'R') +} +export const isArchitectureDirectionY = function(x: ArchitectureDirection): x is Extract { + const temp = x as Extract + return (temp === 'T' || temp === 'B') +} + +export interface ArchitectureStyleOptions { + fontFamily: string; +} + +export interface ArchitectureService { + id: string; + icon?: string; + title?: string; + in?: string; +} + +export interface ArchitectureGroup { + id: string; + icon?: string; + title?: string; + in?: string; +} + +export interface ArchitectureLine { + lhs_id: string; + lhs_dir: ArchitectureDirection; + title?: string; + rhs_id: string; + rhs_dir: ArchitectureDirection; + lhs_into?: boolean; + rhs_into?: boolean; +} + +export interface ArchitectureDB extends DiagramDB { + addService: (id: string, opts: Omit) => void + getServices: () => ArchitectureService[] + addGroup: (id: string, opts: Omit) => void + getGroups: () => ArchitectureGroup[] + addLine: (lhs_id: string, lhs_dir: ArchitectureDirection, rhs_id: string, rhs_dir: ArchitectureDirection, opts: Omit) => void + getLines: () => ArchitectureLine[] + setElementForId: (id: string, element: D3Element) => void; + getElementById: (id: string) => D3Element; +} + +export interface ArchitectureFields { + services: ArchitectureService[], + groups: ArchitectureGroup[], + lines: ArchitectureLine[], + cnt: number, + config: ArchitectureDiagramConfig +} \ No newline at end of file From e01acec12b631a1385aeddc354b6db947b37b0ae Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Wed, 13 Mar 2024 09:24:04 -0500 Subject: [PATCH 05/60] feat(arch): implemented icon registration --- .../mermaid/src/rendering-util/svgRegister.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 packages/mermaid/src/rendering-util/svgRegister.ts diff --git a/packages/mermaid/src/rendering-util/svgRegister.ts b/packages/mermaid/src/rendering-util/svgRegister.ts new file mode 100644 index 0000000000..303c43f579 --- /dev/null +++ b/packages/mermaid/src/rendering-util/svgRegister.ts @@ -0,0 +1,33 @@ +import { Selection } from "d3-selection"; + +type IconResolver = (parent: Selection) => Selection +type IconLibrary = Record + +const icons: IconLibrary = {} + +const isIconNameInUse = (name: string): boolean => { + return icons[name] !== undefined; +} + +const registerIcon = (name: string, resolver: IconResolver) => { + if(!isIconNameInUse(name)) { + icons[name] = resolver; + } +} + +const registerIcons = (library: IconLibrary) => { + Object.entries(library).forEach(([name, resolver]) => { + if (!isIconNameInUse(name)) { + icons[name] = resolver; + } + }) +} + +const getIcon = (name: string): IconResolver | null => { + if (isIconNameInUse(name)) { + return icons[name]; + } + return null; // TODO: return default +} + +export { registerIcon, registerIcons, getIcon, isIconNameInUse, IconLibrary } \ No newline at end of file From 6c6ce28f7df4d52891d341051a9219e768accead Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Wed, 13 Mar 2024 09:25:20 -0500 Subject: [PATCH 06/60] feat(arch): implemented basic rendering for diagram --- .../architecture/architectureDiagram.ts | 13 ++ .../architecture/architectureRenderer.ts | 216 ++++++++++++++++++ .../architecture/architectureStyles.ts | 73 ++++++ .../src/diagrams/architecture/svgDraw.ts | 120 ++++++++++ 4 files changed, 422 insertions(+) create mode 100644 packages/mermaid/src/diagrams/architecture/architectureDiagram.ts create mode 100644 packages/mermaid/src/diagrams/architecture/architectureRenderer.ts create mode 100644 packages/mermaid/src/diagrams/architecture/architectureStyles.ts create mode 100644 packages/mermaid/src/diagrams/architecture/svgDraw.ts diff --git a/packages/mermaid/src/diagrams/architecture/architectureDiagram.ts b/packages/mermaid/src/diagrams/architecture/architectureDiagram.ts new file mode 100644 index 0000000000..614e2a7f3a --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureDiagram.ts @@ -0,0 +1,13 @@ +import type { DiagramDefinition } from '../../diagram-api/types.js'; +// @ts-ignore: JISON doesn't support types +import parser from './parser/architecture.jison'; +import { db } from './architectureDb.js'; +import styles from './architectureStyles.js'; +import { renderer } from './architectureRenderer.js'; + +export const diagram: DiagramDefinition = { + parser, + db, + renderer, + styles, +}; diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts new file mode 100644 index 0000000000..a55b23bffd --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -0,0 +1,216 @@ +import cytoscape from 'cytoscape'; +import type { Diagram } from '../../Diagram.js'; +import fcose, {FcoseLayoutOptions} from 'cytoscape-fcose'; +import type { MermaidConfig } from '../../config.type.js'; +import { getConfig } from '../../diagram-api/diagramAPI.js'; +import type { DrawDefinition, SVG } from '../../diagram-api/types.js'; +import { log } from '../../logger.js'; +import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; +import { + isArchitectureDirectionX, + type ArchitectureDB, + type ArchitectureDirection, + type ArchitectureGroup, + type ArchitectureLine, + type ArchitectureService, + isArchitectureDirectionY, +} from './architectureTypes.js'; +import { select } from 'd3'; +import { setupGraphViewbox } from '../../setupGraphViewbox.js'; +import defaultConfig from '../../defaultConfig.js'; +import type { D3Element } from '../../mermaidAPI.js'; +import { drawEdges, drawService, getEdgeThicknessCallback } from './svgDraw.js'; + +cytoscape.use(fcose); + +function addServices(services: ArchitectureService[], cy: cytoscape.Core) { + services.forEach((service) => { + cy.add({ + group: 'nodes', + data: { + id: service.id, + icon: service.icon, + title: service.title, + parent: service.in, + // TODO: dynamic size + width: 80, + height: 80 + }, + }); + }); +} + +function drawServices( + db: ArchitectureDB, + svg: D3Element, + services: ArchitectureService[], + conf: MermaidConfig +) { + services.forEach((service) => drawService(db, svg, service, conf)); +} + +function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) { + groups.forEach((group) => { + cy.add({ + group: 'nodes', + data: { + id: group.id, + icon: group.icon, + title: group.title, + parent: group.in + }, + }); + }); +} + +function positionServices(db: ArchitectureDB, cy: cytoscape.Core) { + cy.nodes().map((node, id) => { + + const data = node.data(); + data.x = node.position().x; + data.y = node.position().y; + + const nodeElem = db.getElementById(data.id); + nodeElem.attr('transform', 'translate(' + (data.x || 0) + ',' + (data.y || 0) + ')'); + }); +} + +function addEdges(lines: ArchitectureLine[], cy: cytoscape.Core) { + lines.forEach((line) => { + cy.add({ + group: 'edges', + data: { + id: `${line.lhs_id}-${line.rhs_id}`, + source: line.lhs_id, + sourceDir: line.lhs_dir, + target: line.rhs_id, + targetDir: line.rhs_dir, + }, + }); + }); +} + +function layoutArchitecture( + services: ArchitectureService[], + groups: ArchitectureGroup[], + lines: ArchitectureLine[] +): Promise { + return new Promise((resolve) => { + const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none'); + const cy = cytoscape({ + container: document.getElementById('cy'), + style: [ + { + selector: 'edge', + style: { + 'curve-style': 'straight', + 'source-endpoint': '100% 100%', + 'target-endpoint': '100% 100%', + }, + }, + ], + }); + // Remove element after layout + renderEl.remove(); + + addGroups(groups, cy); + addServices(services, cy); + addEdges(lines, cy); + + // Make cytoscape care about the dimensions of the nodes + cy.nodes().forEach(function (n) { + n.layoutDimensions = () => { + const data = n.data(); + return { w: data.width, h: data.height }; + }; + }); + + cy.layout({ + name: 'fcose', + quality: 'proof', + styleEnabled: false, + animate: false, + alignmentConstraint: { + horizontal: cy + .edges() + .filter( + (edge) => + isArchitectureDirectionX(edge.data('sourceDir')) && + isArchitectureDirectionX(edge.data('targetDir')) + ) + .map((edge) => [edge.data('source'), edge.data('target')]), + vertical: cy + .edges() + .filter( + (edge) => + isArchitectureDirectionY(edge.data('sourceDir')) && + isArchitectureDirectionY(edge.data('targetDir')) + ) + .map((edge) => [edge.data('source'), edge.data('target')]), + }, + relativePlacementConstraint: cy.edges().map((edge) => { + const sourceDir = edge.data('sourceDir') as ArchitectureDirection; + const targetDir = edge.data('targetDir') as ArchitectureDirection; + const sourceId = edge.data('source') as ArchitectureDirection; + const targetId = edge.data('target') as ArchitectureDirection; + + if ( + isArchitectureDirectionX(sourceDir) && + isArchitectureDirectionX(targetDir) + ) { + return {left: sourceDir === 'L' ? sourceId : targetId, right: sourceDir === 'R' ? sourceId : targetId, gap: 180} + } else if ( + isArchitectureDirectionY(sourceDir) && + isArchitectureDirectionY(targetDir) + ) { + return {top: sourceDir === 'T' ? sourceId : targetId, bottom: sourceDir === 'B' ? sourceId : targetId, gap: 180} + } + // TODO: fallback case + RB, TL, etc + + }), + } as FcoseLayoutOptions).run(); + cy.ready((e) => { + log.info('Ready', e); + resolve(cy); + }); + }); +} + +export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) => { + const db = diagObj.db as ArchitectureDB; + const conf: MermaidConfig = getConfig(); + + const services = db.getServices(); + const groups = db.getGroups(); + const lines = db.getLines(); + log.info('Services: ', services); + log.info('Lines: ', lines); + + const svg: SVG = selectSvgElement(id); + + const edgesElem = svg.append('g'); + edgesElem.attr('class', 'architecture-edges'); + + const servicesElem = svg.append('g'); + servicesElem.attr('class', 'architecture-services'); + + drawServices(db, servicesElem, services, conf); + const getEdgeThickness = getEdgeThicknessCallback(svg); + + const cy = await layoutArchitecture(services, groups, lines); + + const edgeThickness = getEdgeThickness(); + drawEdges(edgesElem, edgeThickness, cy); + positionServices(db, cy); + + setupGraphViewbox( + undefined, + svg, + conf.architecture?.padding ?? defaultConfig.architecture.padding, + conf.architecture?.useMaxWidth ?? defaultConfig.architecture.useMaxWidth + ); + + +}; + +export const renderer = { draw }; diff --git a/packages/mermaid/src/diagrams/architecture/architectureStyles.ts b/packages/mermaid/src/diagrams/architecture/architectureStyles.ts new file mode 100644 index 0000000000..b92d227e63 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureStyles.ts @@ -0,0 +1,73 @@ +import type { DiagramStylesProvider } from '../../diagram-api/types.js'; +import type { ArchitectureStyleOptions } from './architectureTypes.js'; +// @ts-expect-error Incorrect khroma types +import { darken, lighten, isDark } from 'khroma'; + +const genSections: DiagramStylesProvider = (options) => { + let sections = ''; + + for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) { + options['lineColor' + i] = options['lineColor' + i] || options['cScaleInv' + i]; + if (isDark(options['lineColor' + i])) { + options['lineColor' + i] = lighten(options['lineColor' + i], 20); + } else { + options['lineColor' + i] = darken(options['lineColor' + i], 20); + } + } + + for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) { + const sw = '' + (17 - 3 * i); + sections += ` + .section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${ + i - 1 + } polygon, .section-${i - 1} path { + fill: ${options['cScale' + i]}; + } + .section-${i - 1} text { + fill: ${options['cScaleLabel' + i]}; + } + .node-icon-${i - 1} { + font-size: 40px; + color: ${options['cScaleLabel' + i]}; + } + .section-edge-${i - 1}{ + stroke: ${options['cScale' + i]}; + } + .edge-depth-${i - 1}{ + stroke-width: ${sw}; + } + .section-${i - 1} line { + stroke: ${options['cScaleInv' + i]} ; + stroke-width: 3; + } + + .disabled, .disabled circle, .disabled text { + fill: lightgray; + } + .disabled text { + fill: #efefef; + } + `; + } + return sections; +}; + +const getStyles: DiagramStylesProvider = (options: ArchitectureStyleOptions) => + ` + .edge { + stroke-width: 3; + stroke: #777; + } + .section-root rect, .section-root path, .section-root circle, .section-root polygon { + fill: #333; + } + .section-root text { + fill:#333; + } + ${genSections(options)} + .edge { + fill: none; + } +`; + +export default getStyles; diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts new file mode 100644 index 0000000000..55742ed7fe --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -0,0 +1,120 @@ +import type { D3Element } from '../../mermaidAPI.js'; +import { createText } from '../../rendering-util/createText.js'; +import type { ArchitectureDB, ArchitectureService } from './architectureTypes.js'; +import type { MermaidConfig } from '../../config.type.js'; +import type cytoscape from 'cytoscape'; +import { log } from '../../logger.js'; +import {getIcon, isIconNameInUse} from '../../rendering-util/svgRegister.js'; + +declare module 'cytoscape' { + interface EdgeSingular { + _private: { + bodyBounds: unknown; + rscratch: { + startX: number; + startY: number; + midX: number; + midY: number; + endX: number; + endY: number; + }; + }; + } +} + +/** + * Creates a temporary path which can be used to compute the line thickness. + * @param root root element to add the temporary path to + * @returns callback function which gets the bounding box dimensions and removes the path from root + */ +export const getEdgeThicknessCallback = function (root: D3Element) { + const tempPath = root.insert('path') + .attr( + 'd', + `M 10,10 L 10,20` + ) + .attr('class', 'edge') + .attr('id', 'temp-thickness-edge'); + + return () => { + const dims = tempPath.node().getBBox(); + tempPath.remove(); + return dims.height as number; + } +} + +export const drawEdges = function (edgesEl: D3Element, edgeThickness: number, cy: cytoscape.Core) { + cy.edges().map((edge, id) => { + const data = edge.data(); + if (edge[0]._private.bodyBounds) { + const bounds = edge[0]._private.rscratch; + const translateX = bounds.startX === bounds.endX ? ((edgeThickness + 2) / 1.5) : 0; + const translateY = bounds.startY === bounds.endY ? ((edgeThickness + 2) / 1.5) : 0; + + log.trace('Edge: ', id, data); + edgesEl + .insert('path') + .attr( + 'd', + `M ${bounds.startX},${bounds.startY} L ${bounds.midX},${bounds.midY} L${bounds.endX},${bounds.endY} ` + ) + .attr('class', 'edge') + .attr( + 'transform', + 'translate(' + translateX + ', ' + translateY + ')' + ); + } + }) +} + +export const drawService = function ( + db: ArchitectureDB, + elem: D3Element, + service: ArchitectureService, + conf: MermaidConfig +): number { + const serviceElem = elem.append('g'); + + if (service.title) { + const textElem = serviceElem.append('g'); + createText(textElem, service.title, { + useHtmlLabels: false, + width: 80, + classes: 'architecture-service-label', + }); + textElem + .attr('dy', '1em') + .attr('alignment-baseline', 'middle') + .attr('dominant-baseline', 'middle') + .attr('text-anchor', 'middle'); + + textElem.attr( + 'transform', + 'translate(' + 80 / 2 + ', ' + 80 + ')' + ); + + } + + let bkgElem = serviceElem.append('g'); + if (service.icon) { + if (!isIconNameInUse(service.icon)) { + throw new Error(`Invalid SVG Icon name: "${service.icon}"`) + } + bkgElem = getIcon(service.icon)?.(bkgElem); + } else { + bkgElem.append('path').attr('class', 'node-bkg').attr('id', 'node-' + service.id).attr( + 'd', + `M0 ${80 - 0} v${-80 + 2 * 0} q0,-5 5,-5 h${ + 80 - 2 * 0 + } q5,0 5,5 v${80 - 0} H0 Z` + ); + } + + serviceElem.attr('class', 'architecture-service'); + + const icon = serviceElem.append('foreignObject').attr('height', '80px').attr('width', '80px'); + icon.append('div').attr('class', 'icon-container').append('i').attr('class', 'service-icon fa fa-phone') + + db.setElementForId(service.id, serviceElem); + return 0; +}; From 84bd20b04b3fca5c948cac068dcbaae420398644 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Sun, 17 Mar 2024 15:24:17 -0500 Subject: [PATCH 07/60] feat(arch): improved group rendering --- .../architecture/architectureRenderer.ts | 182 +++++++++++++----- .../architecture/architectureStyles.ts | 7 + .../architecture/architectureTypes.ts | 2 + .../src/diagrams/architecture/svgDraw.ts | 139 ++++++++----- 4 files changed, 229 insertions(+), 101 deletions(-) diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index a55b23bffd..f4566e85eb 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -1,6 +1,6 @@ import cytoscape from 'cytoscape'; import type { Diagram } from '../../Diagram.js'; -import fcose, {FcoseLayoutOptions} from 'cytoscape-fcose'; +import fcose, { FcoseLayoutOptions } from 'cytoscape-fcose'; import type { MermaidConfig } from '../../config.type.js'; import { getConfig } from '../../diagram-api/diagramAPI.js'; import type { DrawDefinition, SVG } from '../../diagram-api/types.js'; @@ -17,9 +17,9 @@ import { } from './architectureTypes.js'; import { select } from 'd3'; import { setupGraphViewbox } from '../../setupGraphViewbox.js'; -import defaultConfig from '../../defaultConfig.js'; +import defaultConfig from '../../defaultConfig.js'; import type { D3Element } from '../../mermaidAPI.js'; -import { drawEdges, drawService, getEdgeThicknessCallback } from './svgDraw.js'; +import { drawEdges, drawGroups, drawService } from './svgDraw.js'; cytoscape.use(fcose); @@ -28,23 +28,25 @@ function addServices(services: ArchitectureService[], cy: cytoscape.Core) { cy.add({ group: 'nodes', data: { + type: 'service', id: service.id, icon: service.icon, - title: service.title, + label: service.title, parent: service.in, // TODO: dynamic size width: 80, - height: 80 + height: 80, }, + classes: 'node-service' }); }); } function drawServices( - db: ArchitectureDB, - svg: D3Element, - services: ArchitectureService[], - conf: MermaidConfig + db: ArchitectureDB, + svg: D3Element, + services: ArchitectureService[], + conf: MermaidConfig ) { services.forEach((service) => drawService(db, svg, service, conf)); } @@ -54,19 +56,22 @@ function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) { cy.add({ group: 'nodes', data: { + type: 'group', id: group.id, icon: group.icon, - title: group.title, + label: group.title, parent: group.in }, + classes: 'node-group' }); }); } function positionServices(db: ArchitectureDB, cy: cytoscape.Core) { cy.nodes().map((node, id) => { - + const data = node.data(); + if (data.type === 'group') return; data.x = node.position().x; data.y = node.position().y; @@ -104,10 +109,40 @@ function layoutArchitecture( selector: 'edge', style: { 'curve-style': 'straight', - 'source-endpoint': '100% 100%', - 'target-endpoint': '100% 100%', + 'source-endpoint': '50% 50%', + 'target-endpoint': '50% 50%', }, }, + { + selector: 'node', + style: { + //@ts-ignore + 'compound-sizing-wrt-labels': 'include', + } + }, + { + selector: 'node[label]', + style: { + 'text-valign': 'bottom', + 'text-halign': 'center', + 'font-size': '16px', + } + }, + { + selector: '.node-service', + style: { + 'label': 'data(label)', + 'width': 'data(width)', + 'height': 'data(height)', + } + }, + { + selector: '.node-group', + style: { + //@ts-ignore + "padding": '30px' + } + } ], }); // Remove element after layout @@ -115,55 +150,97 @@ function layoutArchitecture( addGroups(groups, cy); addServices(services, cy); - addEdges(lines, cy); - - // Make cytoscape care about the dimensions of the nodes - cy.nodes().forEach(function (n) { - n.layoutDimensions = () => { - const data = n.data(); - return { w: data.width, h: data.height }; - }; - }); + addEdges(lines, cy); + + /** + * Merge alignment pairs together if they share a common node. + * + * Example: [["a", "b"], ["b", "c"], ["d", "e"]] -> [["a", "b", "c"], ["d", "e"]] + */ + const mergeAlignments = (orig: string[][]): string[][] => { + console.log('Start: ', orig); + // Mapping of discovered ids to their index in the new alignment array + const map: Record = {}; + const newAlignments: string[][] = [orig[0]]; + map[orig[0][0]] = 0; + map[orig[0][1]] = 0; + orig = orig.slice(1); + while (orig.length > 0) { + const pair = orig[0]; + const pairLHSIdx = map[pair[0]]; + const pairRHSIdx = map[pair[1]]; + console.log(pair); + console.log(map); + console.log(newAlignments); + // If neither id appears in the new array, add the pair to the new array + if (pairLHSIdx === undefined && pairRHSIdx === undefined) { + newAlignments.push(pair); + map[pair[0]] = newAlignments.length - 1; + map[pair[1]] = newAlignments.length - 1; + // If the LHS of the pair doesn't appear in the new array, add the LHS to the existing array it shares an id with + } else if (pairLHSIdx === undefined) { + newAlignments[pairRHSIdx].push(pair[0]); + map[pair[0]] = pairRHSIdx; + // If the RHS of the pair doesn't appear in the new array, add the RHS to the existing array it shares an id with + } else if (pairRHSIdx === undefined) { + newAlignments[pairLHSIdx].push(pair[1]); + map[pair[1]] = pairLHSIdx; + // If both ids already have been added to the new array and their index is different, merge all 3 arrays + } else if (pairLHSIdx != pairRHSIdx) { + console.log('ELSE'); + newAlignments.push(pair); + } + orig = orig.slice(1); + } + + console.log('End: ', newAlignments); + return newAlignments; + } + + const horizontalAlignments = cy + .edges() + .filter( + (edge) => + isArchitectureDirectionX(edge.data('sourceDir')) && + isArchitectureDirectionX(edge.data('targetDir')) + ) + .map((edge) => [edge.data('source'), edge.data('target')]); + + const verticalAlignments = cy + .edges() + .filter( + (edge) => + isArchitectureDirectionY(edge.data('sourceDir')) && + isArchitectureDirectionY(edge.data('targetDir')) + ) + .map((edge) => [edge.data('source'), edge.data('target')]); cy.layout({ name: 'fcose', quality: 'proof', styleEnabled: false, animate: false, + nodeDimensionsIncludeLabels: true, alignmentConstraint: { - horizontal: cy - .edges() - .filter( - (edge) => - isArchitectureDirectionX(edge.data('sourceDir')) && - isArchitectureDirectionX(edge.data('targetDir')) - ) - .map((edge) => [edge.data('source'), edge.data('target')]), - vertical: cy - .edges() - .filter( - (edge) => - isArchitectureDirectionY(edge.data('sourceDir')) && - isArchitectureDirectionY(edge.data('targetDir')) - ) - .map((edge) => [edge.data('source'), edge.data('target')]), + horizontal: mergeAlignments(horizontalAlignments), + vertical: mergeAlignments(verticalAlignments) }, relativePlacementConstraint: cy.edges().map((edge) => { const sourceDir = edge.data('sourceDir') as ArchitectureDirection; const targetDir = edge.data('targetDir') as ArchitectureDirection; - const sourceId = edge.data('source') as ArchitectureDirection; - const targetId = edge.data('target') as ArchitectureDirection; + const sourceId = edge.data('source') as string; + const targetId = edge.data('target') as string; if ( - isArchitectureDirectionX(sourceDir) && - isArchitectureDirectionX(targetDir) + isArchitectureDirectionX(sourceDir) && + isArchitectureDirectionX(targetDir) ) { - return {left: sourceDir === 'L' ? sourceId : targetId, right: sourceDir === 'R' ? sourceId : targetId, gap: 180} + return { left: sourceDir === 'R' ? sourceId : targetId, right: sourceDir === 'L' ? sourceId : targetId, gap: 180 } } else if ( - isArchitectureDirectionY(sourceDir) && - isArchitectureDirectionY(targetDir) + isArchitectureDirectionY(sourceDir) && + isArchitectureDirectionY(targetDir) ) { - return {top: sourceDir === 'T' ? sourceId : targetId, bottom: sourceDir === 'B' ? sourceId : targetId, gap: 180} + return { top: sourceDir === 'B' ? sourceId : targetId, bottom: sourceDir === 'T' ? sourceId : targetId, gap: 180 } } // TODO: fallback case + RB, TL, etc @@ -183,8 +260,9 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) const services = db.getServices(); const groups = db.getGroups(); const lines = db.getLines(); - log.info('Services: ', services); - log.info('Lines: ', lines); + console.log('Services: ', services); + console.log('Lines: ', lines); + console.log('Groups: ', groups); const svg: SVG = selectSvgElement(id); @@ -194,13 +272,15 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) const servicesElem = svg.append('g'); servicesElem.attr('class', 'architecture-services'); + const groupElem = svg.append('g'); + groupElem.attr('class', 'architecture-groups'); + drawServices(db, servicesElem, services, conf); - const getEdgeThickness = getEdgeThicknessCallback(svg); const cy = await layoutArchitecture(services, groups, lines); - const edgeThickness = getEdgeThickness(); - drawEdges(edgesElem, edgeThickness, cy); + drawEdges(edgesElem, cy); + drawGroups(groupElem, cy); positionServices(db, cy); setupGraphViewbox( diff --git a/packages/mermaid/src/diagrams/architecture/architectureStyles.ts b/packages/mermaid/src/diagrams/architecture/architectureStyles.ts index b92d227e63..7cdcbf4aef 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureStyles.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureStyles.ts @@ -68,6 +68,13 @@ const getStyles: DiagramStylesProvider = (options: ArchitectureStyleOptions) => .edge { fill: none; } + + .node-bkg { + fill: none; + stroke: #000; + stroke-width: 2px; + stroke-dasharray: 8; + } `; export default getStyles; diff --git a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts index dbc663b73d..9cf78443a0 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts @@ -25,6 +25,8 @@ export interface ArchitectureService { icon?: string; title?: string; in?: string; + width?: number; + height?: number; } export interface ArchitectureGroup { diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts index 55742ed7fe..b78d2d0449 100644 --- a/packages/mermaid/src/diagrams/architecture/svgDraw.ts +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -1,10 +1,10 @@ import type { D3Element } from '../../mermaidAPI.js'; import { createText } from '../../rendering-util/createText.js'; -import type { ArchitectureDB, ArchitectureService } from './architectureTypes.js'; +import type { ArchitectureDB, ArchitectureGroup, ArchitectureService } from './architectureTypes.js'; import type { MermaidConfig } from '../../config.type.js'; import type cytoscape from 'cytoscape'; import { log } from '../../logger.js'; -import {getIcon, isIconNameInUse} from '../../rendering-util/svgRegister.js'; +import { getIcon, isIconNameInUse } from '../../rendering-util/svgRegister.js'; declare module 'cytoscape' { interface EdgeSingular { @@ -20,48 +20,86 @@ declare module 'cytoscape' { }; }; } -} - -/** - * Creates a temporary path which can be used to compute the line thickness. - * @param root root element to add the temporary path to - * @returns callback function which gets the bounding box dimensions and removes the path from root - */ -export const getEdgeThicknessCallback = function (root: D3Element) { - const tempPath = root.insert('path') - .attr( - 'd', - `M 10,10 L 10,20` - ) - .attr('class', 'edge') - .attr('id', 'temp-thickness-edge'); - - return () => { - const dims = tempPath.node().getBBox(); - tempPath.remove(); - return dims.height as number; + interface NodeSingular { + _private: { + bodyBounds: { + h: number; + w: number; + x1: number; + x2: number; + y1: number; + y2: number; + }; + children: cytoscape.NodeSingular[] + }; + data: () => { + type: 'service', + id: string, + icon?: string, + label?: string, + parent?: string, + width: number, + height: number, + [key: string]: any + } | { + type: 'group', + id: string, + icon?: string, + label?: string, + parent?: string, + [key: string]: any + } } } -export const drawEdges = function (edgesEl: D3Element, edgeThickness: number, cy: cytoscape.Core) { +export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) { cy.edges().map((edge, id) => { const data = edge.data(); if (edge[0]._private.bodyBounds) { const bounds = edge[0]._private.rscratch; - const translateX = bounds.startX === bounds.endX ? ((edgeThickness + 2) / 1.5) : 0; - const translateY = bounds.startY === bounds.endY ? ((edgeThickness + 2) / 1.5) : 0; - log.trace('Edge: ', id, data); - edgesEl - .insert('path') - .attr( - 'd', - `M ${bounds.startX},${bounds.startY} L ${bounds.midX},${bounds.midY} L${bounds.endX},${bounds.endY} ` - ) - .attr('class', 'edge') - .attr( + log.trace('Edge: ', id, data); + edgesEl + .insert('path') + .attr( + 'd', + `M ${bounds.startX},${bounds.startY} L ${bounds.midX},${bounds.midY} L${bounds.endX},${bounds.endY} ` + ) + .attr('class', 'edge') + } + }) +} + +export const drawGroups = function ( + groupsEl: D3Element, + cy: cytoscape.Core +) { + cy.nodes().map((node, id) => { + const data = node.data(); + if (data.type === 'group') { + const { h, w, x1, x2, y1, y2 } = node.boundingBox(); + let bkgElem = groupsEl.append('rect') + .attr('x', x1 + 40) + .attr('y', y1 + 40) + .attr('width', w) + .attr('height', h) + .attr('class', 'node-bkg'); + + const textElem = groupsEl.append('g'); + createText(textElem, data.title, { + useHtmlLabels: false, + width: w, + classes: 'architecture-service-label', + }); + textElem + .attr('dy', '1em') + .attr('alignment-baseline', 'middle') + .attr('dominant-baseline', 'start') + .attr('text-anchor', 'start'); + + textElem.attr( 'transform', - 'translate(' + translateX + ', ' + translateY + ')' + 'translate(' + (x1 + 44) + ', ' + (y1 + 42) + ')' ); } }) @@ -76,22 +114,23 @@ export const drawService = function ( const serviceElem = elem.append('g'); if (service.title) { - const textElem = serviceElem.append('g'); - createText(textElem, service.title, { - useHtmlLabels: false, - width: 80, - classes: 'architecture-service-label', - }); - textElem + const textElem = serviceElem.append('g'); + createText(textElem, service.title, { + useHtmlLabels: false, + width: 110, + classes: 'architecture-service-label', + }); + textElem .attr('dy', '1em') .attr('alignment-baseline', 'middle') .attr('dominant-baseline', 'middle') .attr('text-anchor', 'middle'); - textElem.attr( - 'transform', - 'translate(' + 80 / 2 + ', ' + 80 + ')' - ); + textElem.attr( + 'transform', + // TODO: dynamic size + 'translate(' + 80 / 2 + ', ' + 80 + ')' + ); } @@ -104,16 +143,16 @@ export const drawService = function ( } else { bkgElem.append('path').attr('class', 'node-bkg').attr('id', 'node-' + service.id).attr( 'd', - `M0 ${80 - 0} v${-80 + 2 * 0} q0,-5 5,-5 h${ - 80 - 2 * 0 + `M0 ${80 - 0} v${-80 + 2 * 0} q0,-5 5,-5 h${80 - 2 * 0 } q5,0 5,5 v${80 - 0} H0 Z` ); } serviceElem.attr('class', 'architecture-service'); - const icon = serviceElem.append('foreignObject').attr('height', '80px').attr('width', '80px'); - icon.append('div').attr('class', 'icon-container').append('i').attr('class', 'service-icon fa fa-phone') + const { width, height } = serviceElem._groups[0][0].getBBox(); + service.width = width; + service.height = height; db.setElementForId(service.id, serviceElem); return 0; From 5e214877a421d3d26a1c72d5c5f8b914e152875c Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Tue, 19 Mar 2024 13:57:37 -0500 Subject: [PATCH 08/60] style(arch): prettier formatting --- .vite/jsonSchemaPlugin.ts | 2 +- .../scripts/create-types-from-json-schema.mts | 2 +- .../src/diagram-api/diagram-orchestration.ts | 2 +- .../diagrams/architecture/architectureDb.ts | 64 ++++++----- .../architecture/architectureRenderer.ts | 58 +++++----- .../architecture/architectureTypes.ts | 100 ++++++++++-------- .../src/diagrams/architecture/svgDraw.ts | 79 +++++++------- .../mermaid/src/schemas/config.schema.yaml | 2 +- 8 files changed, 165 insertions(+), 144 deletions(-) diff --git a/.vite/jsonSchemaPlugin.ts b/.vite/jsonSchemaPlugin.ts index a69f6889b3..e83acd31f1 100644 --- a/.vite/jsonSchemaPlugin.ts +++ b/.vite/jsonSchemaPlugin.ts @@ -26,7 +26,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [ 'c4', 'sankey', 'block', - 'architecture' + 'architecture', ] as const; /** diff --git a/packages/mermaid/scripts/create-types-from-json-schema.mts b/packages/mermaid/scripts/create-types-from-json-schema.mts index fbd12a37ff..7129cb94eb 100644 --- a/packages/mermaid/scripts/create-types-from-json-schema.mts +++ b/packages/mermaid/scripts/create-types-from-json-schema.mts @@ -54,7 +54,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [ 'gitGraph', 'c4', 'sankey', - 'architecture' + 'architecture', ]; /** diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index 84697022ca..4259043b1a 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -21,7 +21,7 @@ import timeline from '../diagrams/timeline/detector.js'; import mindmap from '../diagrams/mindmap/detector.js'; import sankey from '../diagrams/sankey/sankeyDetector.js'; import block from '../diagrams/block/blockDetector.js'; -import architecture from '../diagrams/architecture/architectureDetector.js' +import architecture from '../diagrams/architecture/architectureDetector.js'; import { registerLazyLoadedDiagrams } from './detectType.js'; import { registerDiagram } from './diagramAPI.js'; diff --git a/packages/mermaid/src/diagrams/architecture/architectureDb.ts b/packages/mermaid/src/diagrams/architecture/architectureDb.ts index 5aecba746d..896c342aa6 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureDb.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureDb.ts @@ -1,5 +1,12 @@ -import type { ArchitectureFields, ArchitectureDB, ArchitectureService, ArchitectureGroup, ArchitectureDirection, ArchitectureLine } from "./architectureTypes.js"; -import { isArchitectureDirection } from "./architectureTypes.js"; +import type { + ArchitectureFields, + ArchitectureDB, + ArchitectureService, + ArchitectureGroup, + ArchitectureDirection, + ArchitectureLine, +} from './architectureTypes.js'; +import { isArchitectureDirection } from './architectureTypes.js'; import { setAccTitle, getAccTitle, @@ -11,9 +18,10 @@ import { } from '../common/commonDb.js'; import type { ArchitectureDiagramConfig } from '../../config.type.js'; import DEFAULT_CONFIG from '../../defaultConfig.js'; -import type { D3Element } from "../../mermaidAPI.js"; +import type { D3Element } from '../../mermaidAPI.js'; -export const DEFAULT_ARCHITECTURE_CONFIG: Required = DEFAULT_CONFIG.architecture; +export const DEFAULT_ARCHITECTURE_CONFIG: Required = + DEFAULT_CONFIG.architecture; export const DEFAULT_ARCHITECTURE_DB: ArchitectureFields = { services: [], groups: [], @@ -41,37 +49,46 @@ const clear = (): void => { commonClear(); }; -const addService = function (id: string, opts: Omit = {}) { - const {icon, in: inside, title} = opts; +const addService = function (id: string, opts: Omit = {}) { + const { icon, in: inside, title } = opts; services.push({ id, icon, title, - in: inside + in: inside, }); -} +}; const getServices = (): ArchitectureService[] => services; -const addGroup = function (id: string, opts: Omit = {}) { - const {icon, in: inside, title} = opts; +const addGroup = function (id: string, opts: Omit = {}) { + const { icon, in: inside, title } = opts; groups.push({ id, icon, title, - in: inside + in: inside, }); -} +}; const getGroups = (): ArchitectureGroup[] => groups; +const addLine = function ( + lhs_id: string, + lhs_dir: ArchitectureDirection, + rhs_id: string, + rhs_dir: ArchitectureDirection, + opts: Omit = {} +) { + const { title, lhs_into, rhs_into } = opts; -const addLine = function (lhs_id: string, lhs_dir: ArchitectureDirection, rhs_id: string, rhs_dir: ArchitectureDirection, opts: Omit = {}) { - const {title, lhs_into, rhs_into} = opts; - if (!isArchitectureDirection(lhs_dir)) { - throw new Error(`Invalid direction given for left hand side of line ${lhs_id}--${rhs_id}. Expected (L,R,T,B) got ${lhs_dir}`) + throw new Error( + `Invalid direction given for left hand side of line ${lhs_id}--${rhs_id}. Expected (L,R,T,B) got ${lhs_dir}` + ); } if (!isArchitectureDirection(rhs_dir)) { - throw new Error(`Invalid direction given for right hand side of line ${lhs_id}--${rhs_id}. Expected (L,R,T,B) got ${rhs_dir}`) + throw new Error( + `Invalid direction given for right hand side of line ${lhs_id}--${rhs_id}. Expected (L,R,T,B) got ${rhs_dir}` + ); } lines.push({ @@ -81,19 +98,16 @@ const addLine = function (lhs_id: string, lhs_dir: ArchitectureDirection, rhs_id rhs_dir, title, lhs_into, - rhs_into + rhs_into, }); -} +}; const getLines = (): ArchitectureLine[] => lines; - const setElementForId = (id: string, element: D3Element) => { elements[id] = element; }; const getElementById = (id: string) => elements[id]; - - export const db: ArchitectureDB = { getConfig, clear, @@ -103,7 +117,7 @@ export const db: ArchitectureDB = { getAccTitle, setAccDescription, getAccDescription, - + addService, getServices, addGroup, @@ -111,5 +125,5 @@ export const db: ArchitectureDB = { addLine, getLines, setElementForId, - getElementById -}; \ No newline at end of file + getElementById, +}; diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index f4566e85eb..3063849c37 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -37,7 +37,7 @@ function addServices(services: ArchitectureService[], cy: cytoscape.Core) { width: 80, height: 80, }, - classes: 'node-service' + classes: 'node-service', }); }); } @@ -60,16 +60,15 @@ function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) { id: group.id, icon: group.icon, label: group.title, - parent: group.in + parent: group.in, }, - classes: 'node-group' + classes: 'node-group', }); }); } function positionServices(db: ArchitectureDB, cy: cytoscape.Core) { cy.nodes().map((node, id) => { - const data = node.data(); if (data.type === 'group') return; data.x = node.position().x; @@ -118,7 +117,7 @@ function layoutArchitecture( style: { //@ts-ignore 'compound-sizing-wrt-labels': 'include', - } + }, }, { selector: 'node[label]', @@ -126,23 +125,23 @@ function layoutArchitecture( 'text-valign': 'bottom', 'text-halign': 'center', 'font-size': '16px', - } + }, }, { selector: '.node-service', style: { - 'label': 'data(label)', - 'width': 'data(width)', - 'height': 'data(height)', - } + label: 'data(label)', + width: 'data(width)', + height: 'data(height)', + }, }, { selector: '.node-group', style: { //@ts-ignore - "padding": '30px' - } - } + padding: '30px', + }, + }, ], }); // Remove element after layout @@ -150,11 +149,11 @@ function layoutArchitecture( addGroups(groups, cy); addServices(services, cy); - addEdges(lines, cy); + addEdges(lines, cy); /** * Merge alignment pairs together if they share a common node. - * + * * Example: [["a", "b"], ["b", "c"], ["d", "e"]] -> [["a", "b", "c"], ["d", "e"]] */ const mergeAlignments = (orig: string[][]): string[][] => { @@ -195,7 +194,7 @@ function layoutArchitecture( console.log('End: ', newAlignments); return newAlignments; - } + }; const horizontalAlignments = cy .edges() @@ -223,7 +222,7 @@ function layoutArchitecture( nodeDimensionsIncludeLabels: true, alignmentConstraint: { horizontal: mergeAlignments(horizontalAlignments), - vertical: mergeAlignments(verticalAlignments) + vertical: mergeAlignments(verticalAlignments), }, relativePlacementConstraint: cy.edges().map((edge) => { const sourceDir = edge.data('sourceDir') as ArchitectureDirection; @@ -231,19 +230,20 @@ function layoutArchitecture( const sourceId = edge.data('source') as string; const targetId = edge.data('target') as string; - if ( - isArchitectureDirectionX(sourceDir) && - isArchitectureDirectionX(targetDir) - ) { - return { left: sourceDir === 'R' ? sourceId : targetId, right: sourceDir === 'L' ? sourceId : targetId, gap: 180 } - } else if ( - isArchitectureDirectionY(sourceDir) && - isArchitectureDirectionY(targetDir) - ) { - return { top: sourceDir === 'B' ? sourceId : targetId, bottom: sourceDir === 'T' ? sourceId : targetId, gap: 180 } + if (isArchitectureDirectionX(sourceDir) && isArchitectureDirectionX(targetDir)) { + return { + left: sourceDir === 'R' ? sourceId : targetId, + right: sourceDir === 'L' ? sourceId : targetId, + gap: 180, + }; + } else if (isArchitectureDirectionY(sourceDir) && isArchitectureDirectionY(targetDir)) { + return { + top: sourceDir === 'B' ? sourceId : targetId, + bottom: sourceDir === 'T' ? sourceId : targetId, + gap: 180, + }; } // TODO: fallback case + RB, TL, etc - }), } as FcoseLayoutOptions).run(); cy.ready((e) => { @@ -289,8 +289,6 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) conf.architecture?.padding ?? defaultConfig.architecture.padding, conf.architecture?.useMaxWidth ?? defaultConfig.architecture.useMaxWidth ); - - }; export const renderer = { draw }; diff --git a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts index 9cf78443a0..cb95074e4a 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts @@ -2,65 +2,75 @@ import type { DiagramDB } from '../../diagram-api/types.js'; import type { ArchitectureDiagramConfig } from '../../config.type.js'; import type { D3Element } from '../../mermaidAPI.js'; -export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B' -export const isArchitectureDirection = function(x: unknown): x is ArchitectureDirection { - const temp = x as ArchitectureDirection; - return (temp === 'L' || temp === 'R' || temp === 'T' || temp === 'B') -} -export const isArchitectureDirectionX = function(x: ArchitectureDirection): x is Extract { - const temp = x as Extract - return (temp === 'L' || temp === 'R') -} -export const isArchitectureDirectionY = function(x: ArchitectureDirection): x is Extract { - const temp = x as Extract - return (temp === 'T' || temp === 'B') -} +export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B'; +export const isArchitectureDirection = function (x: unknown): x is ArchitectureDirection { + const temp = x as ArchitectureDirection; + return temp === 'L' || temp === 'R' || temp === 'T' || temp === 'B'; +}; +export const isArchitectureDirectionX = function ( + x: ArchitectureDirection +): x is Extract { + const temp = x as Extract; + return temp === 'L' || temp === 'R'; +}; +export const isArchitectureDirectionY = function ( + x: ArchitectureDirection +): x is Extract { + const temp = x as Extract; + return temp === 'T' || temp === 'B'; +}; export interface ArchitectureStyleOptions { - fontFamily: string; + fontFamily: string; } export interface ArchitectureService { - id: string; - icon?: string; - title?: string; - in?: string; - width?: number; - height?: number; + id: string; + icon?: string; + title?: string; + in?: string; + width?: number; + height?: number; } export interface ArchitectureGroup { - id: string; - icon?: string; - title?: string; - in?: string; + id: string; + icon?: string; + title?: string; + in?: string; } export interface ArchitectureLine { - lhs_id: string; - lhs_dir: ArchitectureDirection; - title?: string; - rhs_id: string; - rhs_dir: ArchitectureDirection; - lhs_into?: boolean; - rhs_into?: boolean; + lhs_id: string; + lhs_dir: ArchitectureDirection; + title?: string; + rhs_id: string; + rhs_dir: ArchitectureDirection; + lhs_into?: boolean; + rhs_into?: boolean; } export interface ArchitectureDB extends DiagramDB { - addService: (id: string, opts: Omit) => void - getServices: () => ArchitectureService[] - addGroup: (id: string, opts: Omit) => void - getGroups: () => ArchitectureGroup[] - addLine: (lhs_id: string, lhs_dir: ArchitectureDirection, rhs_id: string, rhs_dir: ArchitectureDirection, opts: Omit) => void - getLines: () => ArchitectureLine[] - setElementForId: (id: string, element: D3Element) => void; - getElementById: (id: string) => D3Element; + addService: (id: string, opts: Omit) => void; + getServices: () => ArchitectureService[]; + addGroup: (id: string, opts: Omit) => void; + getGroups: () => ArchitectureGroup[]; + addLine: ( + lhs_id: string, + lhs_dir: ArchitectureDirection, + rhs_id: string, + rhs_dir: ArchitectureDirection, + opts: Omit + ) => void; + getLines: () => ArchitectureLine[]; + setElementForId: (id: string, element: D3Element) => void; + getElementById: (id: string) => D3Element; } export interface ArchitectureFields { - services: ArchitectureService[], - groups: ArchitectureGroup[], - lines: ArchitectureLine[], - cnt: number, - config: ArchitectureDiagramConfig -} \ No newline at end of file + services: ArchitectureService[]; + groups: ArchitectureGroup[]; + lines: ArchitectureLine[]; + cnt: number; + config: ArchitectureDiagramConfig; +} diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts index b78d2d0449..473bf74074 100644 --- a/packages/mermaid/src/diagrams/architecture/svgDraw.ts +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -1,6 +1,9 @@ import type { D3Element } from '../../mermaidAPI.js'; import { createText } from '../../rendering-util/createText.js'; -import type { ArchitectureDB, ArchitectureGroup, ArchitectureService } from './architectureTypes.js'; +import type { + ArchitectureDB, + ArchitectureService, +} from './architectureTypes.js'; import type { MermaidConfig } from '../../config.type.js'; import type cytoscape from 'cytoscape'; import { log } from '../../logger.js'; @@ -30,25 +33,27 @@ declare module 'cytoscape' { y1: number; y2: number; }; - children: cytoscape.NodeSingular[] + children: cytoscape.NodeSingular[]; }; - data: () => { - type: 'service', - id: string, - icon?: string, - label?: string, - parent?: string, - width: number, - height: number, - [key: string]: any - } | { - type: 'group', - id: string, - icon?: string, - label?: string, - parent?: string, - [key: string]: any - } + data: () => + | { + type: 'service'; + id: string; + icon?: string; + label?: string; + parent?: string; + width: number; + height: number; + [key: string]: any; + } + | { + type: 'group'; + id: string; + icon?: string; + label?: string; + parent?: string; + [key: string]: any; + }; } } @@ -65,20 +70,18 @@ export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) { 'd', `M ${bounds.startX},${bounds.startY} L ${bounds.midX},${bounds.midY} L${bounds.endX},${bounds.endY} ` ) - .attr('class', 'edge') + .attr('class', 'edge'); } - }) -} + }); +}; -export const drawGroups = function ( - groupsEl: D3Element, - cy: cytoscape.Core -) { +export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) { cy.nodes().map((node, id) => { const data = node.data(); if (data.type === 'group') { const { h, w, x1, x2, y1, y2 } = node.boundingBox(); - let bkgElem = groupsEl.append('rect') + let bkgElem = groupsEl + .append('rect') .attr('x', x1 + 40) .attr('y', y1 + 40) .attr('width', w) @@ -97,13 +100,10 @@ export const drawGroups = function ( .attr('dominant-baseline', 'start') .attr('text-anchor', 'start'); - textElem.attr( - 'transform', - 'translate(' + (x1 + 44) + ', ' + (y1 + 42) + ')' - ); + textElem.attr('transform', 'translate(' + (x1 + 44) + ', ' + (y1 + 42) + ')'); } - }) -} + }); +}; export const drawService = function ( db: ArchitectureDB, @@ -131,21 +131,20 @@ export const drawService = function ( // TODO: dynamic size 'translate(' + 80 / 2 + ', ' + 80 + ')' ); - } let bkgElem = serviceElem.append('g'); if (service.icon) { if (!isIconNameInUse(service.icon)) { - throw new Error(`Invalid SVG Icon name: "${service.icon}"`) + throw new Error(`Invalid SVG Icon name: "${service.icon}"`); } bkgElem = getIcon(service.icon)?.(bkgElem); } else { - bkgElem.append('path').attr('class', 'node-bkg').attr('id', 'node-' + service.id).attr( - 'd', - `M0 ${80 - 0} v${-80 + 2 * 0} q0,-5 5,-5 h${80 - 2 * 0 - } q5,0 5,5 v${80 - 0} H0 Z` - ); + bkgElem + .append('path') + .attr('class', 'node-bkg') + .attr('id', 'node-' + service.id) + .attr('d', `M0 ${80 - 0} v${-80 + 2 * 0} q0,-5 5,-5 h${80 - 2 * 0} q5,0 5,5 v${80 - 0} H0 Z`); } serviceElem.attr('class', 'architecture-service'); diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index 601939beed..8723d4ca12 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -858,7 +858,7 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) ArchitectureDiagramConfig: title: Architecture Diagram Config - allOf: [{ $ref: '#/$defs/BaseDiagramConfig'}] + allOf: [{ $ref: '#/$defs/BaseDiagramConfig' }] description: The object containing configurations specific for architecture diagrams type: object unevaluatedProperties: false From a493e2fbb31d141301f717efc0b21fe402161af7 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Mon, 25 Mar 2024 14:17:05 -0500 Subject: [PATCH 09/60] feat(arch): dynamic node sizing --- packages/mermaid/src/config.type.ts | 2 +- .../diagrams/architecture/architectureDb.ts | 17 ++++++++++++----- .../architecture/architectureRenderer.ts | 11 +++++------ .../src/diagrams/architecture/svgDraw.ts | 19 ++++++++++++------- .../mermaid/src/schemas/config.schema.yaml | 6 +++--- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 014ff65e77..e57e0d886b 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -638,7 +638,7 @@ export interface RequirementDiagramConfig extends BaseDiagramConfig { */ export interface ArchitectureDiagramConfig extends BaseDiagramConfig { padding?: number; - maxNodeWidth?: number; + iconSize?: number; } /** * The object containing configurations specific for mindmap diagrams diff --git a/packages/mermaid/src/diagrams/architecture/architectureDb.ts b/packages/mermaid/src/diagrams/architecture/architectureDb.ts index 896c342aa6..6f00be0d44 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureDb.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureDb.ts @@ -6,6 +6,7 @@ import type { ArchitectureDirection, ArchitectureLine, } from './architectureTypes.js'; +import { getConfig } from '../../diagram-api/diagramAPI.js'; import { isArchitectureDirection } from './architectureTypes.js'; import { setAccTitle, @@ -36,10 +37,6 @@ let lines = DEFAULT_ARCHITECTURE_DB.lines; let elements: Record = {}; let cnt = DEFAULT_ARCHITECTURE_DB.cnt; -const config: Required = structuredClone(DEFAULT_ARCHITECTURE_CONFIG); - -const getConfig = (): Required => structuredClone(config); - const clear = (): void => { services = structuredClone(DEFAULT_ARCHITECTURE_DB.services); groups = structuredClone(DEFAULT_ARCHITECTURE_DB.groups); @@ -109,7 +106,6 @@ const setElementForId = (id: string, element: D3Element) => { const getElementById = (id: string) => elements[id]; export const db: ArchitectureDB = { - getConfig, clear, setDiagramTitle, getDiagramTitle, @@ -127,3 +123,14 @@ export const db: ArchitectureDB = { setElementForId, getElementById, }; + +function getConfigField(field: T): Required[T] { + const arch = getConfig().architecture; + if (arch && arch[field] !== undefined) { + const a = arch[field]; + return arch[field] as Required[T] + } + return DEFAULT_ARCHITECTURE_CONFIG[field] +} + +export { getConfigField } \ No newline at end of file diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index 3063849c37..29b6e4d44c 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -17,9 +17,9 @@ import { } from './architectureTypes.js'; import { select } from 'd3'; import { setupGraphViewbox } from '../../setupGraphViewbox.js'; -import defaultConfig from '../../defaultConfig.js'; import type { D3Element } from '../../mermaidAPI.js'; import { drawEdges, drawGroups, drawService } from './svgDraw.js'; +import { getConfigField } from './architectureDb.js'; cytoscape.use(fcose); @@ -33,9 +33,8 @@ function addServices(services: ArchitectureService[], cy: cytoscape.Core) { icon: service.icon, label: service.title, parent: service.in, - // TODO: dynamic size - width: 80, - height: 80, + width: getConfigField('iconSize'), + height: getConfigField('iconSize'), }, classes: 'node-service', }); @@ -286,8 +285,8 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) setupGraphViewbox( undefined, svg, - conf.architecture?.padding ?? defaultConfig.architecture.padding, - conf.architecture?.useMaxWidth ?? defaultConfig.architecture.useMaxWidth + getConfigField('padding'), + getConfigField('useMaxWidth') ); }; diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts index 473bf74074..446704af6d 100644 --- a/packages/mermaid/src/diagrams/architecture/svgDraw.ts +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -8,6 +8,7 @@ import type { MermaidConfig } from '../../config.type.js'; import type cytoscape from 'cytoscape'; import { log } from '../../logger.js'; import { getIcon, isIconNameInUse } from '../../rendering-util/svgRegister.js'; +import { getConfigField } from './architectureDb.js'; declare module 'cytoscape' { interface EdgeSingular { @@ -76,20 +77,23 @@ export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) { }; export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) { + const iconSize = getConfigField('iconSize') + const halfIconSize = iconSize / 2 + cy.nodes().map((node, id) => { const data = node.data(); if (data.type === 'group') { const { h, w, x1, x2, y1, y2 } = node.boundingBox(); let bkgElem = groupsEl .append('rect') - .attr('x', x1 + 40) - .attr('y', y1 + 40) + .attr('x', x1 + halfIconSize) + .attr('y', y1 + halfIconSize) .attr('width', w) .attr('height', h) .attr('class', 'node-bkg'); const textElem = groupsEl.append('g'); - createText(textElem, data.title, { + createText(textElem, data.label, { useHtmlLabels: false, width: w, classes: 'architecture-service-label', @@ -100,7 +104,7 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) { .attr('dominant-baseline', 'start') .attr('text-anchor', 'start'); - textElem.attr('transform', 'translate(' + (x1 + 44) + ', ' + (y1 + 42) + ')'); + textElem.attr('transform', 'translate(' + (x1 + halfIconSize + 4) + ', ' + (y1 + halfIconSize + 2) + ')'); } }); }; @@ -112,6 +116,7 @@ export const drawService = function ( conf: MermaidConfig ): number { const serviceElem = elem.append('g'); + const iconSize = getConfigField('iconSize') if (service.title) { const textElem = serviceElem.append('g'); @@ -129,7 +134,7 @@ export const drawService = function ( textElem.attr( 'transform', // TODO: dynamic size - 'translate(' + 80 / 2 + ', ' + 80 + ')' + 'translate(' + (iconSize / 2) + ', ' + iconSize + ')' ); } @@ -138,13 +143,13 @@ export const drawService = function ( if (!isIconNameInUse(service.icon)) { throw new Error(`Invalid SVG Icon name: "${service.icon}"`); } - bkgElem = getIcon(service.icon)?.(bkgElem); + bkgElem = getIcon(service.icon)?.(bkgElem, iconSize); } else { bkgElem .append('path') .attr('class', 'node-bkg') .attr('id', 'node-' + service.id) - .attr('d', `M0 ${80 - 0} v${-80 + 2 * 0} q0,-5 5,-5 h${80 - 2 * 0} q5,0 5,5 v${80 - 0} H0 Z`); + .attr('d', `M0 ${iconSize - 0} v${-iconSize + 2 * 0} q0,-5 5,-5 h${iconSize - 2 * 0} q5,0 5,5 v${iconSize - 0} H0 Z`); } serviceElem.attr('class', 'architecture-service'); diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index 8723d4ca12..146237eb88 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -865,14 +865,14 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) required: - useMaxWidth - padding - - maxNodeWidth + - iconSize properties: padding: type: number default: 10 - maxNodeWidth: + iconSize: type: number - default: 200 + default: 80 MindmapDiagramConfig: title: Mindmap Diagram Config From 0ab7a3d8ec0ef5aa6735d91acb3134c4d81c113a Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Wed, 27 Mar 2024 09:30:44 -0500 Subject: [PATCH 10/60] feat(arch): added 4 default icons, added config field for icons --- packages/mermaid/src/config.type.ts | 6 +++ packages/mermaid/src/mermaidAPI.ts | 9 ++++ .../src/rendering-util/svg/database.ts | 16 ++++++ .../mermaid/src/rendering-util/svg/disk.ts | 18 +++++++ .../mermaid/src/rendering-util/svg/index.ts | 14 +++++ .../src/rendering-util/svg/internet.ts | 17 +++++++ .../mermaid/src/rendering-util/svg/server.ts | 40 +++++++++++++++ .../mermaid/src/rendering-util/svgRegister.ts | 51 +++++++++++-------- .../mermaid/src/schemas/config.schema.yaml | 6 +++ 9 files changed, 156 insertions(+), 21 deletions(-) create mode 100644 packages/mermaid/src/rendering-util/svg/database.ts create mode 100644 packages/mermaid/src/rendering-util/svg/disk.ts create mode 100644 packages/mermaid/src/rendering-util/svg/index.ts create mode 100644 packages/mermaid/src/rendering-util/svg/internet.ts create mode 100644 packages/mermaid/src/rendering-util/svg/server.ts diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index e57e0d886b..1c88621403 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -135,6 +135,12 @@ export interface MermaidConfig { * */ legacyMathML?: boolean; + /** + * This option specifies an object contianing a mappig of SVG icon names to a resolver that returns the svg code. + * For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram. + * + */ + iconLibraries?: Array; /** * This option controls if the generated ids of nodes in the SVG are * generated randomly or based on a seed. diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index ad9d7d286b..ba580be88a 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -31,6 +31,8 @@ import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility. import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js'; import { preprocessDiagram } from './preprocess.js'; import { decodeEntities } from './utils.js'; +import { registerIcons } from './rendering-util/svgRegister.js'; +import defaultIconLibrary from './rendering-util/svg/index.js'; const MAX_TEXTLENGTH = 50_000; const MAX_TEXTLENGTH_EXCEEDED_MSG = @@ -502,6 +504,13 @@ function initialize(options: MermaidConfig = {}) { // Set default options configApi.saveConfigFromInitialize(options); + registerIcons(defaultIconLibrary) + if (options?.iconLibraries) { + options.iconLibraries.forEach((library) => { + registerIcons(library); + }); + } + if (options?.theme && options.theme in theme) { // Todo merge with user options options.themeVariables = theme[options.theme as keyof typeof theme].getThemeVariables( diff --git a/packages/mermaid/src/rendering-util/svg/database.ts b/packages/mermaid/src/rendering-util/svg/database.ts new file mode 100644 index 0000000000..697114b314 --- /dev/null +++ b/packages/mermaid/src/rendering-util/svg/database.ts @@ -0,0 +1,16 @@ +/** + * @author Nicolas Newman + * @see https://github.com/NicolasNewman/IconLibrary + */ +import { createIcon } from "../svgRegister.js"; + +export default createIcon(` + + + + + + + +`, 80) + diff --git a/packages/mermaid/src/rendering-util/svg/disk.ts b/packages/mermaid/src/rendering-util/svg/disk.ts new file mode 100644 index 0000000000..e96d337626 --- /dev/null +++ b/packages/mermaid/src/rendering-util/svg/disk.ts @@ -0,0 +1,18 @@ +/** + * @author Nicolas Newman + * @see https://github.com/NicolasNewman/IconLibrary + */ +import { createIcon } from "../svgRegister.js"; + +export default createIcon(` + + + + + + + + + +`, 80) + diff --git a/packages/mermaid/src/rendering-util/svg/index.ts b/packages/mermaid/src/rendering-util/svg/index.ts new file mode 100644 index 0000000000..7a34b78e4e --- /dev/null +++ b/packages/mermaid/src/rendering-util/svg/index.ts @@ -0,0 +1,14 @@ +import { IconLibrary } from "../svgRegister.js"; +import database from "./database.js"; +import server from "./server.js"; +import disk from "./disk.js"; +import internet from "./internet.js"; + +const defaultIconLibrary: IconLibrary = { + database: database, + server: server, + disk: disk, + internet: internet, +} + +export default defaultIconLibrary \ No newline at end of file diff --git a/packages/mermaid/src/rendering-util/svg/internet.ts b/packages/mermaid/src/rendering-util/svg/internet.ts new file mode 100644 index 0000000000..c4edf9b4e2 --- /dev/null +++ b/packages/mermaid/src/rendering-util/svg/internet.ts @@ -0,0 +1,17 @@ +/** + * @author Nicolas Newman + * @see https://github.com/NicolasNewman/IconLibrary + */ +import { createIcon } from "../svgRegister.js"; + +export default createIcon(` + + + + + + + + +`, 80) + diff --git a/packages/mermaid/src/rendering-util/svg/server.ts b/packages/mermaid/src/rendering-util/svg/server.ts new file mode 100644 index 0000000000..559148a71b --- /dev/null +++ b/packages/mermaid/src/rendering-util/svg/server.ts @@ -0,0 +1,40 @@ +/** + * @author Nicolas Newman + * @see https://github.com/NicolasNewman/IconLibrary + */ +import { createIcon } from "../svgRegister.js"; + +export default createIcon(` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`, 80) + diff --git a/packages/mermaid/src/rendering-util/svgRegister.ts b/packages/mermaid/src/rendering-util/svgRegister.ts index 303c43f579..4ab15b560e 100644 --- a/packages/mermaid/src/rendering-util/svgRegister.ts +++ b/packages/mermaid/src/rendering-util/svgRegister.ts @@ -1,33 +1,42 @@ -import { Selection } from "d3-selection"; +import { Selection } from 'd3-selection'; -type IconResolver = (parent: Selection) => Selection -type IconLibrary = Record +type IconResolver = ( + parent: Selection, width?: number +) => Selection; +type IconLibrary = Record; -const icons: IconLibrary = {} +const icons: IconLibrary = {}; const isIconNameInUse = (name: string): boolean => { - return icons[name] !== undefined; -} + return icons[name] !== undefined; +}; const registerIcon = (name: string, resolver: IconResolver) => { - if(!isIconNameInUse(name)) { - icons[name] = resolver; - } -} + if (!isIconNameInUse(name)) { + icons[name] = resolver; + } +}; const registerIcons = (library: IconLibrary) => { - Object.entries(library).forEach(([name, resolver]) => { - if (!isIconNameInUse(name)) { - icons[name] = resolver; - } - }) -} + Object.entries(library).forEach(([name, resolver]) => { + if (!isIconNameInUse(name)) { + icons[name] = resolver; + } + }); +}; const getIcon = (name: string): IconResolver | null => { - if (isIconNameInUse(name)) { - return icons[name]; - } - return null; // TODO: return default + if (isIconNameInUse(name)) { + return icons[name]; + } + return null; // TODO: return default +}; + +const createIcon = (icon: string, originalSize: number): IconResolver => { + return (parent: Selection, size: number = originalSize) => { + parent.html(`${icon}`) + return parent + } } -export { registerIcon, registerIcons, getIcon, isIconNameInUse, IconLibrary } \ No newline at end of file +export { registerIcon, registerIcons, getIcon, isIconNameInUse, createIcon, IconLibrary }; diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index 146237eb88..349de47f70 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -177,6 +177,12 @@ properties: fall back to legacy rendering for KaTeX. type: boolean default: false + iconLibraries: + description: | + This option specifies an object contianing a mappig of SVG icon names to a resolver that returns the svg code. + For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram. + tsType: Array + # tsType: Record deterministicIds: description: | This option controls if the generated ids of nodes in the SVG are From 10682ef31f10edd67f4db16313ec5f3caf93859b Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Wed, 27 Mar 2024 09:31:06 -0500 Subject: [PATCH 11/60] feat(arch): added demo --- demos/architecture.html | 102 ++++++++++++++++++++++++++++++++++++++++ demos/index.html | 3 ++ 2 files changed, 105 insertions(+) create mode 100644 demos/architecture.html diff --git a/demos/architecture.html b/demos/architecture.html new file mode 100644 index 0000000000..19fd09d975 --- /dev/null +++ b/demos/architecture.html @@ -0,0 +1,102 @@ + + + + + + Architecture Mermaid Quick Test Page + + + + + +

Architecture diagram demo

+
+      architecture
+        group api[API]
+
+        service db(database)[Database] in api
+        service disk1(disk)[Storage] in api
+        service disk2(disk)[Storage] in api
+        service server(server)[Server] in api
+        service gateway(internet)[Gateway] 
+
+        db L--R server
+        disk1 T--B server
+        disk2 T--B db
+        server T--B gateway
+    
+ + + + diff --git a/demos/index.html b/demos/index.html index efe054b4d5..4e35e72a7f 100644 --- a/demos/index.html +++ b/demos/index.html @@ -84,6 +84,9 @@

Sankey

  • Layered Blocks

  • +
  • +

    Architecture

    +
  • From b911bd3e42012d7026744f4c5d2e04aad92fe42c Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Sun, 31 Mar 2024 13:51:40 -0500 Subject: [PATCH 12/60] feat(arch): improved error handling --- .../diagrams/architecture/architectureDb.ts | 40 +++++++++++++++++-- .../architecture/architectureTypes.ts | 3 +- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/mermaid/src/diagrams/architecture/architectureDb.ts b/packages/mermaid/src/diagrams/architecture/architectureDb.ts index 6f00be0d44..e08e71c366 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureDb.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureDb.ts @@ -27,27 +27,44 @@ export const DEFAULT_ARCHITECTURE_DB: ArchitectureFields = { services: [], groups: [], lines: [], - cnt: 0, + registeredIds: {}, config: DEFAULT_ARCHITECTURE_CONFIG, } as const; let services = DEFAULT_ARCHITECTURE_DB.services; let groups = DEFAULT_ARCHITECTURE_DB.groups; let lines = DEFAULT_ARCHITECTURE_DB.lines; +let registeredIds = DEFAULT_ARCHITECTURE_DB.registeredIds; let elements: Record = {}; -let cnt = DEFAULT_ARCHITECTURE_DB.cnt; const clear = (): void => { services = structuredClone(DEFAULT_ARCHITECTURE_DB.services); groups = structuredClone(DEFAULT_ARCHITECTURE_DB.groups); lines = structuredClone(DEFAULT_ARCHITECTURE_DB.lines); + registeredIds = structuredClone(DEFAULT_ARCHITECTURE_DB.registeredIds) elements = {}; - cnt = 0; commonClear(); }; const addService = function (id: string, opts: Omit = {}) { const { icon, in: inside, title } = opts; + if (registeredIds[id] !== undefined) { + throw new Error(`The service id [${id}] is already in use by another ${registeredIds[id]}`) + } + if (inside !== undefined) { + if (id === inside) { + throw new Error(`The service [${id}] cannot be placed within itself`) + } + if (registeredIds[inside] === undefined) { + throw new Error(`The service [${id}]'s parent does not exist. Please make sure the parent is created before this service`) + } + if (registeredIds[inside] === 'service') { + throw new Error(`The service [${id}]'s parent is not a group`); + } + } + + registeredIds[id] = 'service'; + services.push({ id, icon, @@ -59,6 +76,23 @@ const getServices = (): ArchitectureService[] => services; const addGroup = function (id: string, opts: Omit = {}) { const { icon, in: inside, title } = opts; + if (registeredIds[id] !== undefined) { + throw new Error(`The group id [${id}] is already in use by another ${registeredIds[id]}`) + } + if (inside !== undefined) { + if (id === inside) { + throw new Error(`The group [${id}] cannot be placed within itself`) + } + if (registeredIds[inside] === undefined) { + throw new Error(`The group [${id}]'s parent does not exist. Please make sure the parent is created before this group`) + } + if (registeredIds[inside] === 'service') { + throw new Error(`The group [${id}]'s parent is not a group`); + } + } + + registeredIds[id] = 'group'; + groups.push({ id, icon, diff --git a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts index cb95074e4a..21c620c616 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts @@ -51,6 +51,7 @@ export interface ArchitectureLine { } export interface ArchitectureDB extends DiagramDB { + clear: () => void; addService: (id: string, opts: Omit) => void; getServices: () => ArchitectureService[]; addGroup: (id: string, opts: Omit) => void; @@ -71,6 +72,6 @@ export interface ArchitectureFields { services: ArchitectureService[]; groups: ArchitectureGroup[]; lines: ArchitectureLine[]; - cnt: number; + registeredIds: Record; config: ArchitectureDiagramConfig; } From 46a37a6eea72dbb7b3c845e5f94fa9f66dce9878 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Sun, 31 Mar 2024 13:52:23 -0500 Subject: [PATCH 13/60] feat(arch): added fallback icon --- packages/mermaid/src/rendering-util/svg/index.ts | 2 ++ .../mermaid/src/rendering-util/svg/unknown.ts | 10 ++++++++++ .../mermaid/src/rendering-util/svgRegister.ts | 16 ++++++++-------- 3 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 packages/mermaid/src/rendering-util/svg/unknown.ts diff --git a/packages/mermaid/src/rendering-util/svg/index.ts b/packages/mermaid/src/rendering-util/svg/index.ts index 7a34b78e4e..bc8f1c5ac3 100644 --- a/packages/mermaid/src/rendering-util/svg/index.ts +++ b/packages/mermaid/src/rendering-util/svg/index.ts @@ -3,12 +3,14 @@ import database from "./database.js"; import server from "./server.js"; import disk from "./disk.js"; import internet from "./internet.js"; +import unknown from "./unknown.js"; const defaultIconLibrary: IconLibrary = { database: database, server: server, disk: disk, internet: internet, + unknown: unknown, } export default defaultIconLibrary \ No newline at end of file diff --git a/packages/mermaid/src/rendering-util/svg/unknown.ts b/packages/mermaid/src/rendering-util/svg/unknown.ts new file mode 100644 index 0000000000..25b1ef5c40 --- /dev/null +++ b/packages/mermaid/src/rendering-util/svg/unknown.ts @@ -0,0 +1,10 @@ +/** + * @author Nicolas Newman + * @see https://github.com/NicolasNewman/IconLibrary + */ +import { createIcon } from "../svgRegister.js"; + +export default createIcon(` + + ? +`, 80) \ No newline at end of file diff --git a/packages/mermaid/src/rendering-util/svgRegister.ts b/packages/mermaid/src/rendering-util/svgRegister.ts index 4ab15b560e..b5b4c5e280 100644 --- a/packages/mermaid/src/rendering-util/svgRegister.ts +++ b/packages/mermaid/src/rendering-util/svgRegister.ts @@ -5,6 +5,13 @@ type IconResolver = ( ) => Selection; type IconLibrary = Record; +const createIcon = (icon: string, originalSize: number): IconResolver => { + return (parent: Selection, size: number = originalSize) => { + parent.html(`${icon}`) + return parent + } +} + const icons: IconLibrary = {}; const isIconNameInUse = (name: string): boolean => { @@ -29,14 +36,7 @@ const getIcon = (name: string): IconResolver | null => { if (isIconNameInUse(name)) { return icons[name]; } - return null; // TODO: return default + return icons["unknown"]; }; -const createIcon = (icon: string, originalSize: number): IconResolver => { - return (parent: Selection, size: number = originalSize) => { - parent.html(`${icon}`) - return parent - } -} - export { registerIcon, registerIcons, getIcon, isIconNameInUse, createIcon, IconLibrary }; From 361e25ba34df09e2cc03a239ae0b20b3960202c2 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Sun, 31 Mar 2024 13:53:34 -0500 Subject: [PATCH 14/60] fix(arch): fixed compound nodes overlapping --- .../architecture/architectureRenderer.ts | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index 29b6e4d44c..e517183563 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -72,6 +72,7 @@ function positionServices(db: ArchitectureDB, cy: cytoscape.Core) { if (data.type === 'group') return; data.x = node.position().x; data.y = node.position().y; + console.log(`Position service (${data.id}): (${data.x}, ${data.y})`) const nodeElem = db.getElementById(data.id); nodeElem.attr('transform', 'translate(' + (data.x || 0) + ',' + (data.y || 0) + ')'); @@ -156,6 +157,8 @@ function layoutArchitecture( * Example: [["a", "b"], ["b", "c"], ["d", "e"]] -> [["a", "b", "c"], ["d", "e"]] */ const mergeAlignments = (orig: string[][]): string[][] => { + if (orig.length < 1) return orig; + console.log('===== mergeAlignments ====='); console.log('Start: ', orig); // Mapping of discovered ids to their index in the new alignment array const map: Record = {}; @@ -192,6 +195,8 @@ function layoutArchitecture( } console.log('End: ', newAlignments); + console.log('==========================='); + return newAlignments; }; @@ -219,6 +224,23 @@ function layoutArchitecture( styleEnabled: false, animate: false, nodeDimensionsIncludeLabels: true, + // Adjust the edge parameters if it passes through the border of a group + // Hacky fix for: https://github.com/iVis-at-Bilkent/cytoscape.js-fcose/issues/67 + idealEdgeLength(edge) { + const [nodeA, nodeB] = edge.connectedNodes() + const {parent: parentA} = nodeA.data(); + const {parent: parentB} = nodeB.data(); + const elasticity = parentA === parentB ? 1.25*getConfigField('iconSize') : 0.5*getConfigField('iconSize'); + return elasticity; + }, + edgeElasticity(edge) { + const [nodeA, nodeB] = edge.connectedNodes() + console.log(nodeA.data()); + const {parent: parentA} = nodeA.data(); + const {parent: parentB} = nodeB.data(); + const elasticity = parentA === parentB ? 0.45 : 0.001; + return elasticity + }, alignmentConstraint: { horizontal: mergeAlignments(horizontalAlignments), vertical: mergeAlignments(verticalAlignments), @@ -228,19 +250,23 @@ function layoutArchitecture( const targetDir = edge.data('targetDir') as ArchitectureDirection; const sourceId = edge.data('source') as string; const targetId = edge.data('target') as string; - + + let gap = 1.25*getConfigField('iconSize'); + console.log(`relativeConstraint: ${sourceId} ${sourceDir}--${targetDir} ${targetId} (gap=${gap})`); if (isArchitectureDirectionX(sourceDir) && isArchitectureDirectionX(targetDir)) { return { left: sourceDir === 'R' ? sourceId : targetId, right: sourceDir === 'L' ? sourceId : targetId, - gap: 180, + gap, }; } else if (isArchitectureDirectionY(sourceDir) && isArchitectureDirectionY(targetDir)) { return { top: sourceDir === 'B' ? sourceId : targetId, bottom: sourceDir === 'T' ? sourceId : targetId, - gap: 180, + gap, }; + } else { + console.log('FALLBACK CASE NEEDED') } // TODO: fallback case + RB, TL, etc }), @@ -277,6 +303,7 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) drawServices(db, servicesElem, services, conf); const cy = await layoutArchitecture(services, groups, lines); + console.log(cy.nodes().map(node => ({a: node.data()}))); drawEdges(edgesElem, cy); drawGroups(groupElem, cy); @@ -288,6 +315,8 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) getConfigField('padding'), getConfigField('useMaxWidth') ); + + console.log('==============================================================') }; export const renderer = { draw }; From f47bbee24af2e1223c85abaf2164900a66828bcc Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Sun, 31 Mar 2024 13:54:06 -0500 Subject: [PATCH 15/60] feat(arch): added more demos --- demos/architecture.html | 61 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/demos/architecture.html b/demos/architecture.html index 19fd09d975..96a376957e 100644 --- a/demos/architecture.html +++ b/demos/architecture.html @@ -15,6 +15,7 @@

    Architecture diagram demo

    +

    Simple diagram with groups

           architecture
             group api[API]
    @@ -31,6 +32,66 @@ 

    Architecture diagram demo

    server T--B gateway
    +
    +

    Groups within groups

    +
    +      architecture
    +        group api[API]
    +        group public[Public API] in api
    +        group private[Private API] in api
    +
    +
    +        service serv1(server)[Server] in public
    +
    +
    +        service serv2(server)[Server] in private
    +        service db(database)[Database] in private
    +
    +        service gateway(internet)[Gateway] in api
    +
    +        serv1 B--T serv2
    +
    +        serv2 L--R db
    +
    +        serv1 L--R gateway
    +    
    + +
    + +

    Default icon (?) from uknown icon name

    +
    +      architecture
    +        service unknown(iconnamedoesntexist)[Uknown Icon]
    +    
    + +
    + +
    +        architecture
    +            group vpc[Private VPC]
    +            group vpc2[Public VPC]
    +
    +            service s3(s3)[S3 Bucket]
    +            service rds(database)[RDS DB]
    +            service ddb(dynamodb)[DynamoDB]
    +            service ec2(ec2)[EC2 Server] in vpc
    +            service gateway(api_gateway)[API Gateway] in vpc
    +            service docdb(documentdb)[DocumentDB]
    +            service lambda(lambda)[Lambda] in vpc
    +
    +            service serv1(ec2)[Public Server] in vpc2
    +            service serv2(ec2)[Private Server] in vpc2
    +
    +            serv1 L--R serv2
    +
    +            s3 L--R rds
    +            s3 B--T ddb
    +            s3 R--L docdb
    +            gateway L--R ec2
    +            gateway T--B rds
    +            gateway B--T lambda
    +    
    + - - + }, + architecture: { + iconSize: 80, + }, + useMaxWidth: false, + }); + function callback() { + alert('It worked'); + } + mermaid.parseError = function (err, hash) { + console.error('In parse error:'); + console.error(err); + }; + + + + \ No newline at end of file diff --git a/packages/mermaid/src/diagrams/architecture/architectureDb.ts b/packages/mermaid/src/diagrams/architecture/architectureDb.ts index d3338260fe..3c30ca1d47 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureDb.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureDb.ts @@ -128,6 +128,8 @@ const addEdge = function ({ rhsDir, lhsInto, rhsInto, + lhsGroup, + rhsGroup, title, }: ArchitectureEdge) { if (!isArchitectureDirection(lhsDir)) { @@ -152,14 +154,29 @@ const addEdge = function ({ ); } + const lhsGroupId = state.records.services[lhsId].in + const rhsGroupId = state.records.services[rhsId].in + if (lhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) { + throw new Error( + `The left-hand id [${lhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.` + ) + } + if (rhsGroup && lhsGroupId && rhsGroupId && lhsGroupId == rhsGroupId) { + throw new Error( + `The right-hand id [${rhsId}] is modified to traverse the group boundary, but the edge does not pass through two groups.` + ) + } + const edge = { lhsId, lhsDir, + lhsInto, + lhsGroup, rhsId, rhsDir, - title, - lhsInto, rhsInto, + rhsGroup, + title, }; state.records.edges.push(edge); diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index dd02c2f3d5..af90a58ddb 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -84,7 +84,7 @@ function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) { function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) { edges.forEach((parsedEdge) => { - const { lhsId, rhsId, lhsInto, rhsInto, lhsDir, rhsDir, title } = parsedEdge; + const { lhsId, rhsId, lhsInto, lhsGroup, rhsInto, lhsDir, rhsDir, rhsGroup, title } = parsedEdge; const edgeType = isArchitectureDirectionXY(parsedEdge.lhsDir, parsedEdge.rhsDir) ? 'segments' : 'straight'; @@ -94,6 +94,7 @@ function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) { source: lhsId, sourceDir: lhsDir, sourceArrow: lhsInto, + sourceGroup: lhsGroup, sourceEndpoint: lhsDir === 'L' ? '0 50%' @@ -105,6 +106,7 @@ function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) { target: rhsId, targetDir: rhsDir, targetArrow: rhsInto, + targetGroup: rhsGroup, targetEndpoint: rhsDir === 'L' ? '0 50%' diff --git a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts index 34ea04658b..9e043d6d3c 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureTypes.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureTypes.ts @@ -199,11 +199,13 @@ export interface ArchitectureGroup { export interface ArchitectureEdge { lhsId: string; lhsDir: ArchitectureDirection; - title?: string; + lhsInto?: boolean; + lhsGroup?: boolean; rhsId: string; rhsDir: ArchitectureDirection; - lhsInto?: boolean; rhsInto?: boolean; + rhsGroup?: boolean; + title?: string; } export interface ArchitectureDB extends DiagramDB { @@ -246,9 +248,11 @@ export type EdgeSingularData = { source: string; sourceDir: ArchitectureDirection; sourceArrow?: boolean; + sourceGroup?: boolean; target: string; targetDir: ArchitectureDirection; targetArrow?: boolean; + targetGroup?: boolean; [key: string]: any; }; @@ -274,23 +278,23 @@ export interface EdgeSingular extends cytoscape.EdgeSingular { export type NodeSingularData = | { - type: 'service'; - id: string; - icon?: string; - label?: string; - parent?: string; - width: number; - height: number; - [key: string]: any; - } + type: 'service'; + id: string; + icon?: string; + label?: string; + parent?: string; + width: number; + height: number; + [key: string]: any; + } | { - type: 'group'; - id: string; - icon?: string; - label?: string; - parent?: string; - [key: string]: any; - }; + type: 'group'; + id: string; + icon?: string; + label?: string; + parent?: string; + [key: string]: any; + }; export const nodeData = (node: cytoscape.NodeSingular) => { return node.data() as NodeSingularData; diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts index 5218bf1cc7..41b5135399 100644 --- a/packages/mermaid/src/diagrams/architecture/svgDraw.ts +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -22,17 +22,37 @@ import { getConfigField } from './architectureDb.js'; import { getConfig } from '../../diagram-api/diagramAPI.js'; export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) { + const padding = getConfigField('padding'); const iconSize = getConfigField('iconSize'); const arrowSize = iconSize / 6; const halfArrowSize = arrowSize / 2; cy.edges().map((edge, id) => { - const { sourceDir, sourceArrow, targetDir, targetArrow, label } = edgeData(edge); - const { x: startX, y: startY } = edge[0].sourceEndpoint(); + const { sourceDir, sourceArrow, sourceGroup, targetDir, targetArrow, targetGroup, label } = edgeData(edge); + let { x: startX, y: startY } = edge[0].sourceEndpoint(); const { x: midX, y: midY } = edge[0].midpoint(); - const { x: endX, y: endY } = edge[0].targetEndpoint(); + let { x: endX, y: endY } = edge[0].targetEndpoint(); + + const groupEdgeShift = padding + 4; + // +18 comes from the service label height that extends the padding on the bottom side of each group + if (sourceGroup) { + if (isArchitectureDirectionX(sourceDir)) { + sourceDir === 'L' ? startX -= groupEdgeShift : startX += groupEdgeShift; + } else { + sourceDir === 'T' ? startY -= groupEdgeShift : startY += (groupEdgeShift + 18); + } + } + + if (targetGroup) { + if (isArchitectureDirectionX(targetDir)) { + targetDir === 'L' ? endX -= groupEdgeShift : endX += groupEdgeShift; + } else { + targetDir === 'T' ? endY -= groupEdgeShift : endY += (groupEdgeShift + 18); + } + } + if (edge[0]._private.rscratch) { - const bounds = edge[0]._private.rscratch; + // const bounds = edge[0]._private.rscratch; const g = edgesEl.insert('g'); @@ -42,11 +62,11 @@ export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) { if (sourceArrow) { const xShift = isArchitectureDirectionX(sourceDir) - ? ArchitectureDirectionArrowShift[sourceDir](bounds.startX, arrowSize) - : bounds.startX - halfArrowSize; + ? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize) + : startX - halfArrowSize; const yShift = isArchitectureDirectionY(sourceDir) - ? ArchitectureDirectionArrowShift[sourceDir](bounds.startY, arrowSize) - : bounds.startY - halfArrowSize; + ? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize) + : startY - halfArrowSize; g.insert('polygon') .attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize)) @@ -55,11 +75,11 @@ export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) { } if (targetArrow) { const xShift = isArchitectureDirectionX(targetDir) - ? ArchitectureDirectionArrowShift[targetDir](bounds.endX, arrowSize) - : bounds.endX - halfArrowSize; + ? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize) + : endX - halfArrowSize; const yShift = isArchitectureDirectionY(targetDir) - ? ArchitectureDirectionArrowShift[targetDir](bounds.endY, arrowSize) - : bounds.endY - halfArrowSize; + ? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize) + : endY - halfArrowSize; g.insert('polygon') .attr('points', ArchitectureDirectionArrow[targetDir](arrowSize)) @@ -165,10 +185,10 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) { bkgElem.attr( 'transform', 'translate(' + - (shiftedX1 + halfIconSize + 1) + - ', ' + - (shiftedY1 + halfIconSize + 1) + - ')' + (shiftedX1 + halfIconSize + 1) + + ', ' + + (shiftedY1 + halfIconSize + 1) + + ')' ); shiftedX1 += groupIconSize; // TODO: test with more values @@ -196,10 +216,10 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) { textElem.attr( 'transform', 'translate(' + - (shiftedX1 + halfIconSize + 4) + - ', ' + - (shiftedY1 + halfIconSize + 2) + - ')' + (shiftedX1 + halfIconSize + 4) + + ', ' + + (shiftedY1 + halfIconSize + 2) + + ')' ); } } diff --git a/packages/parser/src/language/architecture/architecture.langium b/packages/parser/src/language/architecture/architecture.langium index 101390e841..18a823b356 100644 --- a/packages/parser/src/language/architecture/architecture.langium +++ b/packages/parser/src/language/architecture/architecture.langium @@ -30,7 +30,7 @@ Service: ; Edge: - lhsId=ARCH_ID Arrow rhsId=ARCH_ID EOL + lhsId=ARCH_ID lhsGroup?=ARROW_GROUP? Arrow rhsId=ARCH_ID rhsGroup?=ARROW_GROUP? EOL ; terminal ARROW_DIRECTION: 'L' | 'R' | 'T' | 'B'; @@ -38,4 +38,5 @@ terminal ARCH_ID: /[\w]+/; terminal ARCH_TEXT_ICON: /\("[^"]+"\)/; terminal ARCH_ICON: /\([\w]+\)/; terminal ARCH_TITLE: /\[[\w ]+\]/; +terminal ARROW_GROUP: /\{group\}/; terminal ARROW_INTO: /\(|\)/; \ No newline at end of file From b09dc5db67000ca726cb158eae3d855bc9a4e397 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Fri, 10 May 2024 10:10:19 -0500 Subject: [PATCH 41/60] feat(arch): implemented junction nodes --- demos/architecture.html | 45 +++++++++++++- .../diagrams/architecture/architectureDb.ts | 58 ++++++++++++++----- .../architecture/architectureParser.ts | 3 +- .../architecture/architectureRenderer.ts | 41 +++++++++++-- .../architecture/architectureTypes.ts | 38 +++++++++++- .../src/diagrams/architecture/svgDraw.ts | 50 +++++++++++++++- .../architecture/architecture.langium | 5 ++ 7 files changed, 213 insertions(+), 27 deletions(-) diff --git a/demos/architecture.html b/demos/architecture.html index 2f805547ed..32edb6f1d7 100644 --- a/demos/architecture.html +++ b/demos/architecture.html @@ -16,7 +16,6 @@

    Architecture diagram demo

    -

    Simple diagram with groups

           architecture
    @@ -182,6 +181,50 @@ 

    Edge Label Test


    +

    Junction Demo

    +
    +      architecture
    +        service left_disk(disk)[Disk]
    +        service top_disk(disk)[Disk]
    +        service bottom_disk(disk)[Disk]
    +        service top_gateway(internet)[Gateway]
    +        service bottom_gateway(internet)[Gateway]
    +        junction juncC
    +        junction juncR
    +
    +        left_disk R--L juncC
    +        top_disk B--T juncC
    +        bottom_disk T--B juncC
    +        juncC R--L juncR
    +        top_gateway B--T juncR
    +        bottom_gateway T--B juncR
    +    
    +
    + +

    Junction Demo Groups

    +
    +      architecture
    +        group left
    +        group right
    +        service left_disk(disk)[Disk] in left
    +        service top_disk(disk)[Disk] in left
    +        service bottom_disk(disk)[Disk] in left
    +        service top_gateway(internet)[Gateway] in right
    +        service bottom_gateway(internet)[Gateway] in right
    +        junction juncC in left
    +        junction juncR in right
    +
    +        left_disk R--L juncC
    +        top_disk B--T juncC
    +        bottom_disk T--B juncC
    +
    +
    +        top_gateway (B--T juncR
    +        bottom_gateway (T--B juncR
    +
    +        juncC{group} R--L) juncR{group}
    +    
    +
    - - - \ No newline at end of file + iconLibraries: ['aws:full'], + }); + function callback() { + alert('It worked'); + } + mermaid.parseError = function (err, hash) { + console.error('In parse error:'); + console.error(err); + }; + + + diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index 991c3e42be..9545282586 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -140,4 +140,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index 5d1be3e040..0ce02005e7 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -102,7 +102,8 @@ function addGroups(groups: ArchitectureGroup[], cy: cytoscape.Core) { function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) { edges.forEach((parsedEdge) => { - const { lhsId, rhsId, lhsInto, lhsGroup, rhsInto, lhsDir, rhsDir, rhsGroup, title } = parsedEdge; + const { lhsId, rhsId, lhsInto, lhsGroup, rhsInto, lhsDir, rhsDir, rhsGroup, title } = + parsedEdge; const edgeType = isArchitectureDirectionXY(parsedEdge.lhsDir, parsedEdge.rhsDir) ? 'segments' : 'straight'; From 25609d69c49852772dabf818b9688086fd07187a Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Mon, 22 Jul 2024 09:34:36 -0500 Subject: [PATCH 54/60] chore(arch): changed syntax keyword from architecture -> architecture-beta --- demos/architecture.html | 270 +++++++++--------- docs/syntax/architecture.md | 10 +- .../mermaid/src/docs/syntax/architecture.md | 6 +- .../architecture/architecture.langium | 2 +- 4 files changed, 144 insertions(+), 144 deletions(-) diff --git a/demos/architecture.html b/demos/architecture.html index 3855d6d0af..02f7c0a11f 100644 --- a/demos/architecture.html +++ b/demos/architecture.html @@ -1,24 +1,23 @@ - - - - - Architecture Mermaid Quick Test Page - - - - - -

    Architecture diagram demo

    -

    Simple diagram with groups

    -
    -      architecture
    +  
    +    
    +    
    +    Architecture Mermaid Quick Test Page
    +    
    +    
    +  
    +
    +  
    +    

    Architecture diagram demo

    +

    Simple diagram with groups

    +
    +      architecture-beta
             group api(cloud)[API]
     
             service db(database)[Database] in api
    @@ -32,11 +31,11 @@ 

    Simple diagram with groups

    disk2 T--B db server T--B gateway
    -
    +
    -

    Groups within groups

    -
    -      architecture
    +    

    Groups within groups

    +
    +      architecture-beta
             group api[API]
             group public[Public API] in api
             group private[Private API] in api
    @@ -56,18 +55,18 @@ 

    Groups within groups

    serv1 L--R gateway
    -
    +
    -

    Default icon (?) from unknown icon name

    -
    -      architecture
    +    

    Default icon (?) from unknown icon name

    +
    +      architecture-beta
             service unknown(iconnamedoesntexist)[Unknown Icon]
         
    -
    +
    -

    Split Direction

    -
    -      architecture
    +    

    Split Direction

    +
    +      architecture-beta
             service db(database)[Database]
             service s3(disk)[Storage]
             service serv1(server)[Server 1]
    @@ -79,11 +78,11 @@ 

    Split Direction

    serv2 L--B s3 serv1 T--B disk
    -
    +
    -

    Arrow Tests

    -
    -      architecture
    +    

    Arrow Tests

    +
    +      architecture-beta
             service servC(server)[Server 1]
             service servL(server)[Server 2]
             service servR(server)[Server 3]
    @@ -100,8 +99,8 @@ 

    Arrow Tests

    servR (T--R) servT servR (B--R) servB
    -
    -      architecture
    +    
    +      architecture-beta
             service servC(server)[Server 1]
             service servL(server)[Server 2]
             service servR(server)[Server 3]
    @@ -118,11 +117,11 @@ 

    Arrow Tests

    servT (R--T) servR servB (R--B) servR
    -
    +
    -

    Group Edges

    -
    -      architecture
    +    

    Group Edges

    +
    +      architecture-beta
             group left_group(cloud)[Left]
             group right_group(cloud)[Right]
             group top_group(cloud)[Top]
    @@ -139,12 +138,13 @@ 

    Group Edges

    right_disk{group} (L--R) center_disk{group} top_disk{group} (B--T) center_disk{group} bottom_disk{group} (T--B) center_disk{group} -
    -
    +
    +
    -

    Edge Label Test

    -
    -      architecture
    +    

    Edge Label Test

    +
    +      architecture-beta
             service servC(server)[Server 1]
             service servL(server)[Server 2]
             service servR(server)[Server 3]
    @@ -161,8 +161,8 @@ 

    Edge Label Test

    servR T-[Label]-R servT servR B-[Label]-R servB
    -
    -      architecture
    +    
    +      architecture-beta
             service servC(server)[Server 1]
             service servL(server)[Server 2]
             service servR(server)[Server 3]
    @@ -180,10 +180,10 @@ 

    Edge Label Test

    servR B-[Label that is Long]-R servB
    -
    -

    Junction Demo

    -
    -      architecture
    +    
    +

    Junction Demo

    +
    +      architecture-beta
             service left_disk(disk)[Disk]
             service top_disk(disk)[Disk]
             service bottom_disk(disk)[Disk]
    @@ -199,11 +199,11 @@ 

    Junction Demo

    top_gateway B--T juncR bottom_gateway T--B juncR
    -
    +
    -

    Junction Demo Groups

    -
    -      architecture
    +    

    Junction Demo Groups

    +
    +      architecture-beta
             group left
             group right
             service left_disk(disk)[Disk] in left
    @@ -224,87 +224,87 @@ 

    Junction Demo Groups

    juncC{group} R--L) juncR{group}
    -
    +
    -

    AWS Icon Demo

    -
    -    architecture
    -      service s3(s3)[Cloud Store]
    -      service ec2(ec2)[Server]
    -      service wave(wavelength)[Wave]
    +    

    AWS Icon Demo

    +
    +    architecture-beta
    +      service s3(aws:s3)[Cloud Store]
    +      service ec2(aws:ec2)[Server]
    +      service wave(aws:wavelength)[Wave]
     
           s3 L--R ec2
           s3 T--B wave
    -  
    - - - - - \ No newline at end of file + iconLibraries: ['aws:full'], + }); + function callback() { + alert('It worked'); + } + mermaid.parseError = function (err, hash) { + console.error('In parse error:'); + console.error(err); + }; + + + diff --git a/docs/syntax/architecture.md b/docs/syntax/architecture.md index 30b3bd581a..361e84aa82 100644 --- a/docs/syntax/architecture.md +++ b/docs/syntax/architecture.md @@ -11,7 +11,7 @@ ## Example ```mermaid-example -architecture +architecture-beta group api(cloud)[API] service db(database)[Database] in api @@ -25,7 +25,7 @@ architecture ``` ```mermaid -architecture +architecture-beta group api(cloud)[API] service db(database)[Database] in api @@ -44,7 +44,7 @@ The building blocks of an architecture are `groups`, `services`, `edges`, and `j For supporting components, icons are declared by surrounding the icon name with `()`, while labels are declared by surrounding the text with `[]`. -To begin an architecture diagram, use the keyword `architecture`, followed by your groups, services, edges, and junctions. While each of the 3 building blocks can be declared in any order, care must be taken to ensure the identifier was previously declared by another component. +To begin an architecture diagram, use the keyword `architecture-beta`, followed by your groups, services, edges, and junctions. While each of the 3 building blocks can be declared in any order, care must be taken to ensure the identifier was previously declared by another component. ### Groups @@ -156,7 +156,7 @@ junction {junction id} (in {parent id})? ``` ```mermaid-example -architecture +architecture-beta service left_disk(disk)[Disk] service top_disk(disk)[Disk] service bottom_disk(disk)[Disk] @@ -174,7 +174,7 @@ architecture ``` ```mermaid -architecture +architecture-beta service left_disk(disk)[Disk] service top_disk(disk)[Disk] service bottom_disk(disk)[Disk] diff --git a/packages/mermaid/src/docs/syntax/architecture.md b/packages/mermaid/src/docs/syntax/architecture.md index 8be3755222..336269048a 100644 --- a/packages/mermaid/src/docs/syntax/architecture.md +++ b/packages/mermaid/src/docs/syntax/architecture.md @@ -5,7 +5,7 @@ ## Example ```mermaid-example -architecture +architecture-beta group api(cloud)[API] service db(database)[Database] in api @@ -24,7 +24,7 @@ The building blocks of an architecture are `groups`, `services`, `edges`, and `j For supporting components, icons are declared by surrounding the icon name with `()`, while labels are declared by surrounding the text with `[]`. -To begin an architecture diagram, use the keyword `architecture`, followed by your groups, services, edges, and junctions. While each of the 3 building blocks can be declared in any order, care must be taken to ensure the identifier was previously declared by another component. +To begin an architecture diagram, use the keyword `architecture-beta`, followed by your groups, services, edges, and junctions. While each of the 3 building blocks can be declared in any order, care must be taken to ensure the identifier was previously declared by another component. ### Groups @@ -136,7 +136,7 @@ junction {junction id} (in {parent id})? ``` ```mermaid-example -architecture +architecture-beta service left_disk(disk)[Disk] service top_disk(disk)[Disk] service bottom_disk(disk)[Disk] diff --git a/packages/parser/src/language/architecture/architecture.langium b/packages/parser/src/language/architecture/architecture.langium index 31eeaa7d03..79a3899e86 100644 --- a/packages/parser/src/language/architecture/architecture.langium +++ b/packages/parser/src/language/architecture/architecture.langium @@ -3,7 +3,7 @@ import "../common/common"; entry Architecture: NEWLINE* - "architecture" + "architecture-beta" ( NEWLINE* TitleAndAccessibilities | NEWLINE* Statement* From 880c4d4ed696a2c36e59cb3d16173d2de68c0d7e Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Tue, 23 Jul 2024 15:31:11 -0500 Subject: [PATCH 55/60] feat(arch): updated syntax & demos/docs to reflect changes --- demos/architecture.html | 122 +++++++++--------- docs/syntax/architecture.md | 50 +++---- .../mermaid/src/docs/syntax/architecture.md | 32 ++--- .../architecture/architecture.langium | 12 +- 4 files changed, 112 insertions(+), 104 deletions(-) diff --git a/demos/architecture.html b/demos/architecture.html index 02f7c0a11f..deacbc2423 100644 --- a/demos/architecture.html +++ b/demos/architecture.html @@ -26,10 +26,10 @@

    Simple diagram with groups

    service server(server)[Server] in api service gateway(internet)[Gateway] - db L--R server - disk1 T--B server - disk2 T--B db - server T--B gateway + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db + server:T -- B:gateway

    @@ -49,11 +49,11 @@

    Groups within groups

    service gateway(internet)[Gateway] in api - serv1 B--T serv2 + serv1:B -- T:serv2 - serv2 L--R db + serv2:L -- R:db - serv1 L--R gateway + serv1:L -- R:gateway

    @@ -73,10 +73,10 @@

    Split Direction

    service serv2(server)[Server 2] service disk(disk)[Disk] - db L--R s3 - serv1 L--T s3 - serv2 L--B s3 - serv1 T--B disk + db:L -- R:s3 + serv1:L -- T:s3 + serv2:L -- B:s3 + serv1:T -- B:disk

    @@ -89,15 +89,15 @@

    Arrow Tests

    service servT(server)[Server 4] service servB(server)[Server 5] - servC (L--R) servL - servC (R--L) servR - servC (T--B) servT - servC (B--T) servB + servC:L <--> R:servL + servC:R <--> L:servR + servC:T <--> B:servT + servC:B <--> T:servB - servL (T--L) servT - servL (B--L) servB - servR (T--R) servT - servR (B--R) servB + servL:T <--> L:servT + servL:B <--> L:servB + servR:T <--> R:servT + servR:B <--> R:servB
           architecture-beta
    @@ -107,15 +107,15 @@ 

    Arrow Tests

    service servT(server)[Server 4] service servB(server)[Server 5] - servC (L--R) servL - servC (R--L) servR - servC (T--B) servT - servC (B--T) servB + servC:L <--> R:servL + servC:R <--> L:servR + servC:T <--> B:servT + servC:B <--> T:servB - servT (L--T) servL - servB (L--B) servL - servT (R--T) servR - servB (R--B) servR + servT:L <--> T:servL + servB:L <--> B:servL + servT:R <--> T:servR + servB:R <--> B:servR

    @@ -134,10 +134,10 @@

    Group Edges

    service bottom_disk(disk)[Disk] in bottom_group service center_disk(disk)[Disk] in center_group - left_disk{group} (R--L) center_disk{group} - right_disk{group} (L--R) center_disk{group} - top_disk{group} (B--T) center_disk{group} - bottom_disk{group} (T--B) center_disk{group} + left_disk{group}:R <--> L:center_disk{group} + right_disk{group}:L <--> R:center_disk{group} + top_disk{group}:B <--> T:center_disk{group} + bottom_disk{group}:T <--> B:center_disk{group}

    @@ -151,15 +151,15 @@

    Edge Label Test

    service servT(server)[Server 4] service servB(server)[Server 5] - servC L-[Label]-R servL - servC R-[Label]-L servR - servC T-[Label]-B servT - servC B-[Label]-T servB + servC:L -[Label]- R:servL + servC:R -[Label]- L:servR + servC:T -[Label]- B:servT + servC:B -[Label]- T:servB - servL T-[Label]-L servT - servL B-[Label]-L servB - servR T-[Label]-R servT - servR B-[Label]-R servB + servL:T -[Label]- L:servT + servL:B -[Label]- L:servB + servR:T -[Label]- R:servT + servR:B -[Label]- R:servB
           architecture-beta
    @@ -169,15 +169,15 @@ 

    Edge Label Test

    service servT(server)[Server 4] service servB(server)[Server 5] - servC L-[Label that is Long]-R servL - servC R-[Label that is Long]-L servR - servC T-[Label that is Long]-B servT - servC B-[Label that is Long]-T servB + servC:L -[Label that is Long]- R:servL + servC:R -[Label that is Long]- L:servR + servC:T -[Label that is Long]- B:servT + servC:B -[Label that is Long]- T:servB - servL T-[Label that is Long]-L servT - servL B-[Label that is Long]-L servB - servR T-[Label that is Long]-R servT - servR B-[Label that is Long]-R servB + servL:T -[Label that is Long]- L:servT + servL:B -[Label that is Long]- L:servB + servR:T -[Label that is Long]- R:servT + servR:B -[Label that is Long]- R:servB

    @@ -192,12 +192,12 @@

    Junction Demo

    junction juncC junction juncR - left_disk R--L juncC - top_disk B--T juncC - bottom_disk T--B juncC - juncC R--L juncR - top_gateway B--T juncR - bottom_gateway T--B juncR + left_disk:R -- L:juncC + top_disk:B -- T:juncC + bottom_disk:T -- B:juncC + juncC:R -- L:juncR + top_gateway:B -- T:juncR + bottom_gateway:T -- B:juncR

    @@ -214,15 +214,15 @@

    Junction Demo Groups

    junction juncC in left junction juncR in right - left_disk R--L juncC - top_disk B--T juncC - bottom_disk T--B juncC + left_disk:R -- L:juncC + top_disk:B -- T:juncC + bottom_disk:T -- B:juncC - top_gateway (B--T juncR - bottom_gateway (T--B juncR + top_gateway:B <-- T:juncR + bottom_gateway:T <-- B:juncR - juncC{group} R--L) juncR{group} + juncC{group}:R --> L:juncR{group}

    @@ -233,8 +233,8 @@

    AWS Icon Demo

    service ec2(aws:ec2)[Server] service wave(aws:wavelength)[Wave] - s3 L--R ec2 - s3 T--B wave + s3:L -- R:ec2 + s3:T -- B:wave
    diff --git a/docs/syntax/architecture.md b/docs/syntax/architecture.md index 361e84aa82..2d71d84fba 100644 --- a/docs/syntax/architecture.md +++ b/docs/syntax/architecture.md @@ -19,9 +19,9 @@ architecture-beta service disk2(disk)[Storage] in api service server(server)[Server] in api - db L--R server - disk1 T--B server - disk2 T--B db + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db ``` ```mermaid @@ -33,9 +33,9 @@ architecture-beta service disk2(disk)[Storage] in api service server(server)[Server] in api - db L--R server - disk1 T--B server - disk2 T--B db + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db ``` ## Syntax @@ -95,35 +95,35 @@ service database(db)[Database] in private_api The syntax for declaring an edge is: ``` -{serviceId}{{group}}? {(}?{T|B|L|R}--{T|B|L|R}{)}? {serviceId}{{group}}? +{serviceId}{{group}}?:{T|B|L|R} {<}?--{>}? {T|B|L|R}:{serviceId}{{group}}? ``` #### Edge Direction -The side of the service the edge comes out of is specified by adding `L|R|T|B` to each end of `--`. +The side of the service the edge comes out of is specified by adding a colon (`:`) to the side of the service connecting to the arrow and adding `L|R|T|B` For example: ``` -db R--L server +db:R -- L:server ``` creates an edge between the services `db` and `server`, with the edge coming out of the right of `db` and the left of `server`. ``` -db T--L server +db:T -- L:server ``` creates a 90 degree edge between the services `db` and `server`, with the edge coming out of the top of `db` and the left of `server`. #### Arrows -Arrows can be added to each side of an edge by adding `(` before the direction on the left, and/or `)` after the direction on the right. +Arrows can be added to each side of an edge by adding `<` before the direction on the left, and/or `>` after the direction on the right. For example: ``` -subnet R--L) gateway +subnet:R --> L:gateway ``` creates an edge with the arrow going into the `gateway` service @@ -138,7 +138,7 @@ For example: service server[Server] in groupOne service subnet[Subnet] in groupTwo -server{group} B--T) subnet{group} +server{group}:B --> T:subnet{group} ``` creates an edge going out of `groupOne`, adjacent to `server`, and into `groupTwo`, adjacent to `subnet`. @@ -165,12 +165,12 @@ architecture-beta junction junctionCenter junction junctionRight - left_disk R--L junctionCenter - top_disk B--T junctionCenter - bottom_disk T--B junctionCenter - junctionCenter R--L junctionRight - top_gateway B--T junctionRight - bottom_gateway T--B junctionRight + left_disk:R -- L:junctionCenter + top_disk:B -- T:junctionCenter + bottom_disk:T -- B:junctionCenter + junctionCenter:R -- L:junctionRight + top_gateway:B -- T:junctionRight + bottom_gateway:T -- B:junctionRight ``` ```mermaid @@ -183,12 +183,12 @@ architecture-beta junction junctionCenter junction junctionRight - left_disk R--L junctionCenter - top_disk B--T junctionCenter - bottom_disk T--B junctionCenter - junctionCenter R--L junctionRight - top_gateway B--T junctionRight - bottom_gateway T--B junctionRight + left_disk:R -- L:junctionCenter + top_disk:B -- T:junctionCenter + bottom_disk:T -- B:junctionCenter + junctionCenter:R -- L:junctionRight + top_gateway:B -- T:junctionRight + bottom_gateway:T -- B:junctionRight ``` ## Configuration diff --git a/packages/mermaid/src/docs/syntax/architecture.md b/packages/mermaid/src/docs/syntax/architecture.md index 336269048a..e74995ce33 100644 --- a/packages/mermaid/src/docs/syntax/architecture.md +++ b/packages/mermaid/src/docs/syntax/architecture.md @@ -13,9 +13,9 @@ architecture-beta service disk2(disk)[Storage] in api service server(server)[Server] in api - db L--R server - disk1 T--B server - disk2 T--B db + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db ``` ## Syntax @@ -75,35 +75,35 @@ service database(db)[Database] in private_api The syntax for declaring an edge is: ``` -{serviceId}{{group}}? {(}?{T|B|L|R}--{T|B|L|R}{)}? {serviceId}{{group}}? +{serviceId}{{group}}?:{T|B|L|R} {<}?--{>}? {T|B|L|R}:{serviceId}{{group}}? ``` #### Edge Direction -The side of the service the edge comes out of is specified by adding `L|R|T|B` to each end of `--`. +The side of the service the edge comes out of is specified by adding a colon (`:`) to the side of the service connecting to the arrow and adding `L|R|T|B` For example: ``` -db R--L server +db:R -- L:server ``` creates an edge between the services `db` and `server`, with the edge coming out of the right of `db` and the left of `server`. ``` -db T--L server +db:T -- L:server ``` creates a 90 degree edge between the services `db` and `server`, with the edge coming out of the top of `db` and the left of `server`. #### Arrows -Arrows can be added to each side of an edge by adding `(` before the direction on the left, and/or `)` after the direction on the right. +Arrows can be added to each side of an edge by adding `<` before the direction on the left, and/or `>` after the direction on the right. For example: ``` -subnet R--L) gateway +subnet:R --> L:gateway ``` creates an edge with the arrow going into the `gateway` service @@ -118,7 +118,7 @@ For example: service server[Server] in groupOne service subnet[Subnet] in groupTwo -server{group} B--T) subnet{group} +server{group}:B --> T:subnet{group} ``` creates an edge going out of `groupOne`, adjacent to `server`, and into `groupTwo`, adjacent to `subnet`. @@ -145,12 +145,12 @@ architecture-beta junction junctionCenter junction junctionRight - left_disk R--L junctionCenter - top_disk B--T junctionCenter - bottom_disk T--B junctionCenter - junctionCenter R--L junctionRight - top_gateway B--T junctionRight - bottom_gateway T--B junctionRight + left_disk:R -- L:junctionCenter + top_disk:B -- T:junctionCenter + bottom_disk:T -- B:junctionCenter + junctionCenter:R -- L:junctionRight + top_gateway:B -- T:junctionRight + bottom_gateway:T -- B:junctionRight ``` ## Configuration diff --git a/packages/parser/src/language/architecture/architecture.langium b/packages/parser/src/language/architecture/architecture.langium index 79a3899e86..6246b423ed 100644 --- a/packages/parser/src/language/architecture/architecture.langium +++ b/packages/parser/src/language/architecture/architecture.langium @@ -18,8 +18,16 @@ fragment Statement: | edges+=Edge ; +fragment LeftPort: + ':'lhsDir=ARROW_DIRECTION +; + +fragment RightPort: + rhsDir=ARROW_DIRECTION':' +; + fragment Arrow: - lhsInto?=ARROW_INTO? lhsDir=ARROW_DIRECTION ('--' | '-' title=ARCH_TITLE '-') rhsDir=ARROW_DIRECTION rhsInto?=ARROW_INTO? + LeftPort lhsInto?=ARROW_INTO? ('--' | '-' title=ARCH_TITLE '-') rhsInto?=ARROW_INTO? RightPort ; Group: @@ -44,4 +52,4 @@ terminal ARCH_TEXT_ICON: /\("[^"]+"\)/; terminal ARCH_ICON: /\([\w:]+\)/; terminal ARCH_TITLE: /\[[\w ]+\]/; terminal ARROW_GROUP: /\{group\}/; -terminal ARROW_INTO: /\(|\)/; \ No newline at end of file +terminal ARROW_INTO: /<|>/; \ No newline at end of file From 1df90b4a05a9976f3880cc3f39f70b826ebddb54 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Wed, 14 Aug 2024 10:11:56 -0500 Subject: [PATCH 56/60] build(arch): pnpm-lock update --- pnpm-lock.yaml | 104 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 23 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c2fe726e0..c0663f9c82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -212,6 +212,9 @@ importers: cytoscape-cose-bilkent: specifier: ^4.1.0 version: 4.1.0(cytoscape@3.30.1) + cytoscape-fcose: + specifier: ^2.2.0 + version: 2.2.0(cytoscape@3.30.1) d3: specifier: ^7.9.0 version: 7.9.0 @@ -258,6 +261,9 @@ importers: '@types/cytoscape': specifier: ^3.21.4 version: 3.21.5 + '@types/cytoscape-fcose': + specifier: ^2.2.4 + version: 2.2.4 '@types/d3': specifier: ^7.4.3 version: 7.4.3 @@ -516,7 +522,7 @@ importers: version: 7.4.47 '@vueuse/core': specifier: ^10.9.0 - version: 10.9.0(vue@3.4.31(typescript@5.4.5)) + version: 10.11.0(vue@3.4.35(typescript@5.4.5)) font-awesome: specifier: ^4.7.0 version: 4.7.0 @@ -528,20 +534,20 @@ importers: version: link:../.. vue: specifier: ^3.4.21 - version: 3.4.31(typescript@5.4.5) + version: 3.4.35(typescript@5.4.5) devDependencies: '@iconify-json/carbon': specifier: ^1.1.31 - version: 1.1.36 + version: 1.1.37 '@unocss/reset': specifier: ^0.59.0 version: 0.59.4 '@vite-pwa/vitepress': specifier: ^0.4.0 - version: 0.4.0(vite-plugin-pwa@0.19.8(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.1.0)) + version: 0.4.0(vite-plugin-pwa@0.19.8(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0)) '@vitejs/plugin-vue': specifier: ^5.0.0 - version: 5.0.5(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1))(vue@3.4.31(typescript@5.4.5)) + version: 5.1.2(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(vue@3.4.35(typescript@5.4.5)) fast-glob: specifier: ^3.3.2 version: 3.3.2 @@ -553,19 +559,19 @@ importers: version: 1.1.2 unocss: specifier: ^0.59.0 - version: 0.59.4(postcss@8.4.39)(rollup@4.18.0)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1)) + version: 0.59.4(postcss@8.4.40)(rollup@4.20.0)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)) unplugin-vue-components: specifier: ^0.26.0 - version: 0.26.0(@babel/parser@7.24.7)(rollup@4.18.0)(vue@3.4.31(typescript@5.4.5)) + version: 0.26.0(@babel/parser@7.25.3)(rollup@4.20.0)(vue@3.4.35(typescript@5.4.5)) vite: specifier: ^5.0.0 - version: 5.2.13(@types/node@20.14.7)(terser@5.31.1) + version: 5.3.5(@types/node@20.14.14)(terser@5.31.3) vite-plugin-pwa: specifier: ^0.19.7 - version: 0.19.8(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.1.0) + version: 0.19.8(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) vitepress: specifier: 1.1.4 - version: 1.1.4(@algolia/client-search@4.23.3)(@types/node@20.14.7)(axios@1.7.2)(postcss@8.4.39)(search-insights@2.13.0)(terser@5.31.1)(typescript@5.4.5) + version: 1.1.4(@algolia/client-search@4.24.0)(@types/node@20.14.14)(axios@1.7.3)(postcss@8.4.40)(search-insights@2.15.0)(terser@5.31.3)(typescript@5.4.5) workbox-window: specifier: ^7.0.0 version: 7.1.0 @@ -2415,6 +2421,9 @@ packages: '@types/cors@2.8.17': resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} + '@types/cytoscape-fcose@2.2.4': + resolution: {integrity: sha512-QwWtnT8HI9h+DHhG5krGc1ZY0Ex+cn85MvX96ZNAjSxuXiZDnjIZW/ypVkvvubTjIY4rSdkJY1D/Nsn8NDpmAw==} + '@types/cytoscape@3.21.5': resolution: {integrity: sha512-fzYT3vqY5J4gxVXDOsCgDpm0ZdU8bQq+wCv0ucS0MSTtvQdjs3lcb2VetJiUSAd4WBgouqizI+JT1f8Yc6eY7Q==} @@ -3987,6 +3996,11 @@ packages: peerDependencies: cytoscape: ^3.2.0 + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + cytoscape@3.30.1: resolution: {integrity: sha512-TRJc3HbBPkHd50u9YfJh2FxD1lDLZ+JXnJoyBn5LkncoeuT7fapO/Hq/Ed8TdFclaKshzInge2i30bg7VKeoPQ==} engines: {node: '>=0.10'} @@ -10123,7 +10137,7 @@ snapshots: '@babel/preset-env': 7.25.3(@babel/core@7.25.2) babel-loader: 9.1.3(@babel/core@7.25.2)(webpack@5.93.0(esbuild@0.21.5)) bluebird: 3.7.1 - debug: 4.3.5 + debug: 4.3.6(supports-color@8.1.1) lodash: 4.17.21 webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: @@ -10880,6 +10894,10 @@ snapshots: dependencies: '@types/node': 20.14.14 + '@types/cytoscape-fcose@2.2.4': + dependencies: + '@types/cytoscape': 3.21.5 + '@types/cytoscape@3.21.5': {} '@types/d3-array@3.2.1': {} @@ -11328,13 +11346,13 @@ snapshots: transitivePeerDependencies: - rollup - '@unocss/astro@0.59.4(rollup@4.18.0)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1))': + '@unocss/astro@0.59.4(rollup@4.20.0)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))': dependencies: '@unocss/core': 0.59.4 '@unocss/reset': 0.59.4 - '@unocss/vite': 0.59.4(rollup@4.18.0)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1)) + '@unocss/vite': 0.59.4(rollup@4.20.0)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)) optionalDependencies: - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.1) + vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) transitivePeerDependencies: - rollup @@ -11356,10 +11374,10 @@ snapshots: transitivePeerDependencies: - rollup - '@unocss/cli@0.59.4(rollup@4.18.0)': + '@unocss/cli@0.59.4(rollup@4.20.0)': dependencies: '@ampproject/remapping': 2.3.0 - '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@rollup/pluginutils': 5.1.0(rollup@4.20.0) '@unocss/config': 0.59.4 '@unocss/core': 0.59.4 '@unocss/preset-uno': 0.59.4 @@ -11368,7 +11386,7 @@ snapshots: colorette: 2.0.20 consola: 3.2.3 fast-glob: 3.3.2 - magic-string: 0.30.10 + magic-string: 0.30.11 pathe: 1.1.2 perfect-debounce: 1.0.0 transitivePeerDependencies: @@ -11499,6 +11517,22 @@ snapshots: transitivePeerDependencies: - rollup + '@unocss/vite@0.59.4(rollup@4.20.0)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@rollup/pluginutils': 5.1.0(rollup@4.20.0) + '@unocss/config': 0.59.4 + '@unocss/core': 0.59.4 + '@unocss/inspector': 0.59.4 + '@unocss/scope': 0.59.4 + '@unocss/transformer-directives': 0.59.4 + chokidar: 3.6.0 + fast-glob: 3.3.2 + magic-string: 0.30.11 + vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) + transitivePeerDependencies: + - rollup + '@vite-pwa/vitepress@0.4.0(vite-plugin-pwa@0.19.8(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0))': dependencies: vite-plugin-pwa: 0.19.8(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3))(workbox-build@7.1.1(@types/babel__core@7.20.5))(workbox-window@7.1.0) @@ -12876,6 +12910,11 @@ snapshots: cose-base: 1.0.3 cytoscape: 3.30.1 + cytoscape-fcose@2.2.0(cytoscape@3.30.1): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.30.1 + cytoscape@3.30.1: {} d3-array@2.12.1: @@ -17643,13 +17682,13 @@ snapshots: - rollup - supports-color - unocss@0.59.4(postcss@8.4.39)(rollup@4.18.0)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1)): + unocss@0.59.4(postcss@8.4.40)(rollup@4.20.0)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)): dependencies: - '@unocss/astro': 0.59.4(rollup@4.18.0)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1)) - '@unocss/cli': 0.59.4(rollup@4.18.0) + '@unocss/astro': 0.59.4(rollup@4.20.0)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)) + '@unocss/cli': 0.59.4(rollup@4.20.0) '@unocss/core': 0.59.4 '@unocss/extractor-arbitrary-variants': 0.59.4 - '@unocss/postcss': 0.59.4(postcss@8.4.39) + '@unocss/postcss': 0.59.4(postcss@8.4.40) '@unocss/preset-attributify': 0.59.4 '@unocss/preset-icons': 0.59.4 '@unocss/preset-mini': 0.59.4 @@ -17664,9 +17703,9 @@ snapshots: '@unocss/transformer-compile-class': 0.59.4 '@unocss/transformer-directives': 0.59.4 '@unocss/transformer-variant-group': 0.59.4 - '@unocss/vite': 0.59.4(rollup@4.18.0)(vite@5.2.13(@types/node@20.14.7)(terser@5.31.1)) + '@unocss/vite': 0.59.4(rollup@4.20.0)(vite@5.3.5(@types/node@20.14.14)(terser@5.31.3)) optionalDependencies: - vite: 5.2.13(@types/node@20.14.7)(terser@5.31.1) + vite: 5.3.5(@types/node@20.14.14)(terser@5.31.3) transitivePeerDependencies: - postcss - rollup @@ -17693,6 +17732,25 @@ snapshots: - rollup - supports-color + unplugin-vue-components@0.26.0(@babel/parser@7.25.3)(rollup@4.20.0)(vue@3.4.35(typescript@5.4.5)): + dependencies: + '@antfu/utils': 0.7.10 + '@rollup/pluginutils': 5.1.0(rollup@4.20.0) + chokidar: 3.6.0 + debug: 4.3.6(supports-color@8.1.1) + fast-glob: 3.3.2 + local-pkg: 0.4.3 + magic-string: 0.30.11 + minimatch: 9.0.5 + resolve: 1.22.8 + unplugin: 1.12.0 + vue: 3.4.35(typescript@5.4.5) + optionalDependencies: + '@babel/parser': 7.25.3 + transitivePeerDependencies: + - rollup + - supports-color + unplugin@1.12.0: dependencies: acorn: 8.12.1 From d36522648f375199d446618730eb9196cc8fa9c8 Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Wed, 14 Aug 2024 10:32:56 -0500 Subject: [PATCH 57/60] fix(arch): async/await fixes for drawText changes --- .../architecture/architectureRenderer.ts | 6 +- .../src/diagrams/architecture/svgDraw.ts | 400 +++++++++--------- 2 files changed, 205 insertions(+), 201 deletions(-) diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index 0ce02005e7..f23c1894bd 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -455,13 +455,13 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram) const groupElem = svg.append('g'); groupElem.attr('class', 'architecture-groups'); - drawServices(db, servicesElem, services); + await drawServices(db, servicesElem, services); drawJunctions(db, servicesElem, junctions); const cy = await layoutArchitecture(services, junctions, groups, edges, ds); - drawEdges(edgesElem, cy); - drawGroups(groupElem, cy); + await drawEdges(edgesElem, cy); + await drawGroups(groupElem, cy); positionNodes(db, cy); setupGraphViewbox(undefined, svg, getConfigField('padding'), getConfigField('useMaxWidth')); diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts index e8e651ebd1..cbe385f27d 100644 --- a/packages/mermaid/src/diagrams/architecture/svgDraw.ts +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -22,167 +22,169 @@ import { getIcon } from '../../rendering-util/svgRegister.js'; import { db, getConfigField } from './architectureDb.js'; import { getConfig } from '../../diagram-api/diagramAPI.js'; -export const drawEdges = function (edgesEl: D3Element, cy: cytoscape.Core) { +export const drawEdges = async function (edgesEl: D3Element, cy: cytoscape.Core) { const padding = getConfigField('padding'); const iconSize = getConfigField('iconSize'); const halfIconSize = iconSize / 2; const arrowSize = iconSize / 6; const halfArrowSize = arrowSize / 2; - cy.edges().map((edge) => { - const { - source, - sourceDir, - sourceArrow, - sourceGroup, - target, - targetDir, - targetArrow, - targetGroup, - label, - } = edgeData(edge); - let { x: startX, y: startY } = edge[0].sourceEndpoint(); - const { x: midX, y: midY } = edge[0].midpoint(); - let { x: endX, y: endY } = edge[0].targetEndpoint(); - - // Adjust the edge distance if it has the {group} modifier - const groupEdgeShift = padding + 4; - // +18 comes from the service label height that extends the padding on the bottom side of each group - if (sourceGroup) { - if (isArchitectureDirectionX(sourceDir)) { - startX += sourceDir === 'L' ? -groupEdgeShift : groupEdgeShift; - } else { - startY += sourceDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18; + await Promise.all( + cy.edges().map(async (edge) => { + const { + source, + sourceDir, + sourceArrow, + sourceGroup, + target, + targetDir, + targetArrow, + targetGroup, + label, + } = edgeData(edge); + let { x: startX, y: startY } = edge[0].sourceEndpoint(); + const { x: midX, y: midY } = edge[0].midpoint(); + let { x: endX, y: endY } = edge[0].targetEndpoint(); + + // Adjust the edge distance if it has the {group} modifier + const groupEdgeShift = padding + 4; + // +18 comes from the service label height that extends the padding on the bottom side of each group + if (sourceGroup) { + if (isArchitectureDirectionX(sourceDir)) { + startX += sourceDir === 'L' ? -groupEdgeShift : groupEdgeShift; + } else { + startY += sourceDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18; + } } - } - if (targetGroup) { - if (isArchitectureDirectionX(targetDir)) { - endX += targetDir === 'L' ? -groupEdgeShift : groupEdgeShift; - } else { - endY += targetDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18; + if (targetGroup) { + if (isArchitectureDirectionX(targetDir)) { + endX += targetDir === 'L' ? -groupEdgeShift : groupEdgeShift; + } else { + endY += targetDir === 'T' ? -groupEdgeShift : groupEdgeShift + 18; + } } - } - // Adjust the edge distance if it doesn't have the {group} modifier and the endpoint is a junction node - if (!sourceGroup && db.getNode(source)?.type === 'junction') { - if (isArchitectureDirectionX(sourceDir)) { - startX += sourceDir === 'L' ? halfIconSize : -halfIconSize; - } else { - startY += sourceDir === 'T' ? halfIconSize : -halfIconSize; + // Adjust the edge distance if it doesn't have the {group} modifier and the endpoint is a junction node + if (!sourceGroup && db.getNode(source)?.type === 'junction') { + if (isArchitectureDirectionX(sourceDir)) { + startX += sourceDir === 'L' ? halfIconSize : -halfIconSize; + } else { + startY += sourceDir === 'T' ? halfIconSize : -halfIconSize; + } } - } - if (!targetGroup && db.getNode(target)?.type === 'junction') { - if (isArchitectureDirectionX(targetDir)) { - endX += targetDir === 'L' ? halfIconSize : -halfIconSize; - } else { - endY += targetDir === 'T' ? halfIconSize : -halfIconSize; + if (!targetGroup && db.getNode(target)?.type === 'junction') { + if (isArchitectureDirectionX(targetDir)) { + endX += targetDir === 'L' ? halfIconSize : -halfIconSize; + } else { + endY += targetDir === 'T' ? halfIconSize : -halfIconSize; + } } - } - if (edge[0]._private.rscratch) { - // const bounds = edge[0]._private.rscratch; + if (edge[0]._private.rscratch) { + // const bounds = edge[0]._private.rscratch; - const g = edgesEl.insert('g'); + const g = edgesEl.insert('g'); - g.insert('path') - .attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `) - .attr('class', 'edge'); + g.insert('path') + .attr('d', `M ${startX},${startY} L ${midX},${midY} L${endX},${endY} `) + .attr('class', 'edge'); - if (sourceArrow) { - const xShift = isArchitectureDirectionX(sourceDir) - ? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize) - : startX - halfArrowSize; - const yShift = isArchitectureDirectionY(sourceDir) - ? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize) - : startY - halfArrowSize; + if (sourceArrow) { + const xShift = isArchitectureDirectionX(sourceDir) + ? ArchitectureDirectionArrowShift[sourceDir](startX, arrowSize) + : startX - halfArrowSize; + const yShift = isArchitectureDirectionY(sourceDir) + ? ArchitectureDirectionArrowShift[sourceDir](startY, arrowSize) + : startY - halfArrowSize; - g.insert('polygon') - .attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize)) - .attr('transform', `translate(${xShift},${yShift})`) - .attr('class', 'arrow'); - } - if (targetArrow) { - const xShift = isArchitectureDirectionX(targetDir) - ? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize) - : endX - halfArrowSize; - const yShift = isArchitectureDirectionY(targetDir) - ? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize) - : endY - halfArrowSize; - - g.insert('polygon') - .attr('points', ArchitectureDirectionArrow[targetDir](arrowSize)) - .attr('transform', `translate(${xShift},${yShift})`) - .attr('class', 'arrow'); - } - - if (label) { - const axis = !isArchitectureDirectionXY(sourceDir, targetDir) - ? isArchitectureDirectionX(sourceDir) - ? 'X' - : 'Y' - : 'XY'; - - let width = 0; - if (axis === 'X') { - width = Math.abs(startX - endX); - } else if (axis === 'Y') { - // Reduce width by a factor of 1.5 to avoid overlapping service labels - width = Math.abs(startY - endY) / 1.5; - } else { - width = Math.abs(startX - endX) / 2; + g.insert('polygon') + .attr('points', ArchitectureDirectionArrow[sourceDir](arrowSize)) + .attr('transform', `translate(${xShift},${yShift})`) + .attr('class', 'arrow'); + } + if (targetArrow) { + const xShift = isArchitectureDirectionX(targetDir) + ? ArchitectureDirectionArrowShift[targetDir](endX, arrowSize) + : endX - halfArrowSize; + const yShift = isArchitectureDirectionY(targetDir) + ? ArchitectureDirectionArrowShift[targetDir](endY, arrowSize) + : endY - halfArrowSize; + + g.insert('polygon') + .attr('points', ArchitectureDirectionArrow[targetDir](arrowSize)) + .attr('transform', `translate(${xShift},${yShift})`) + .attr('class', 'arrow'); } - const textElem = g.append('g'); - createText( - textElem, - label, - { - useHtmlLabels: false, - width, - classes: 'architecture-service-label', - }, - getConfig() - ); + if (label) { + const axis = !isArchitectureDirectionXY(sourceDir, targetDir) + ? isArchitectureDirectionX(sourceDir) + ? 'X' + : 'Y' + : 'XY'; + + let width = 0; + if (axis === 'X') { + width = Math.abs(startX - endX); + } else if (axis === 'Y') { + // Reduce width by a factor of 1.5 to avoid overlapping service labels + width = Math.abs(startY - endY) / 1.5; + } else { + width = Math.abs(startX - endX) / 2; + } - textElem - .attr('dy', '1em') - .attr('alignment-baseline', 'middle') - .attr('dominant-baseline', 'middle') - .attr('text-anchor', 'middle'); - - if (axis === 'X') { - textElem.attr('transform', 'translate(' + midX + ', ' + midY + ')'); - } else if (axis === 'Y') { - textElem.attr('transform', 'translate(' + midX + ', ' + midY + ') rotate(-90)'); - } else if (axis === 'XY') { - const pair = getArchitectureDirectionPair(sourceDir, targetDir); - if (pair && isArchitecturePairXY(pair)) { - const bboxOrig = textElem.node().getBoundingClientRect(); - const [x, y] = getArchitectureDirectionXYFactors(pair); - - textElem - .attr('dominant-baseline', 'auto') - .attr('transform', `rotate(${-1 * x * y * 45})`); - - // Calculate the new width/height with the rotation applied, and transform to the proper position - const bboxNew = textElem.node().getBoundingClientRect(); - textElem.attr( - 'transform', - ` + const textElem = g.append('g'); + await createText( + textElem, + label, + { + useHtmlLabels: false, + width, + classes: 'architecture-service-label', + }, + getConfig() + ); + + textElem + .attr('dy', '1em') + .attr('alignment-baseline', 'middle') + .attr('dominant-baseline', 'middle') + .attr('text-anchor', 'middle'); + + if (axis === 'X') { + textElem.attr('transform', 'translate(' + midX + ', ' + midY + ')'); + } else if (axis === 'Y') { + textElem.attr('transform', 'translate(' + midX + ', ' + midY + ') rotate(-90)'); + } else if (axis === 'XY') { + const pair = getArchitectureDirectionPair(sourceDir, targetDir); + if (pair && isArchitecturePairXY(pair)) { + const bboxOrig = textElem.node().getBoundingClientRect(); + const [x, y] = getArchitectureDirectionXYFactors(pair); + + textElem + .attr('dominant-baseline', 'auto') + .attr('transform', `rotate(${-1 * x * y * 45})`); + + // Calculate the new width/height with the rotation applied, and transform to the proper position + const bboxNew = textElem.node().getBoundingClientRect(); + textElem.attr( + 'transform', + ` translate(${midX}, ${midY - bboxOrig.height / 2}) translate(${(x * bboxNew.width) / 2}, ${(y * bboxNew.height) / 2}) rotate(${-1 * x * y * 45}, 0, ${bboxOrig.height / 2}) ` - ); + ); + } } } } - } - }); + }) + ); }; -export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) { +export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Core) { const padding = getConfigField('padding'); const groupIconSize = padding * 0.75; @@ -191,82 +193,84 @@ export const drawGroups = function (groupsEl: D3Element, cy: cytoscape.Core) { const iconSize = getConfigField('iconSize'); const halfIconSize = iconSize / 2; - cy.nodes().map((node) => { - const data = nodeData(node); - if (data.type === 'group') { - const { h, w, x1, y1 } = node.boundingBox(); - console.log(`Draw group (${data.id}): pos=(${x1}, ${y1}), dim=(${w}, ${h})`); - - groupsEl - .append('rect') - .attr('x', x1 + halfIconSize) - .attr('y', y1 + halfIconSize) - .attr('width', w) - .attr('height', h) - .attr('class', 'node-bkg'); - - const groupLabelContainer = groupsEl.append('g'); - let shiftedX1 = x1; - let shiftedY1 = y1; - if (data.icon) { - const bkgElem = groupLabelContainer.append('g'); - getIcon(data.icon)?.(bkgElem, groupIconSize); - bkgElem.attr( - 'transform', - 'translate(' + - (shiftedX1 + halfIconSize + 1) + - ', ' + - (shiftedY1 + halfIconSize + 1) + - ')' - ); - shiftedX1 += groupIconSize; - // TODO: test with more values - // - 1 - 2 comes from the Y axis transform of the icon and label - shiftedY1 += fontSize / 2 - 1 - 2; - } - if (data.label) { - const textElem = groupLabelContainer.append('g'); - createText( - textElem, - data.label, - { - useHtmlLabels: false, - width: w, - classes: 'architecture-service-label', - }, - getConfig() - ); - textElem - .attr('dy', '1em') - .attr('alignment-baseline', 'middle') - .attr('dominant-baseline', 'start') - .attr('text-anchor', 'start'); - - textElem.attr( - 'transform', - 'translate(' + - (shiftedX1 + halfIconSize + 4) + - ', ' + - (shiftedY1 + halfIconSize + 2) + - ')' - ); + await Promise.all( + cy.nodes().map(async (node) => { + const data = nodeData(node); + if (data.type === 'group') { + const { h, w, x1, y1 } = node.boundingBox(); + console.log(`Draw group (${data.id}): pos=(${x1}, ${y1}), dim=(${w}, ${h})`); + + groupsEl + .append('rect') + .attr('x', x1 + halfIconSize) + .attr('y', y1 + halfIconSize) + .attr('width', w) + .attr('height', h) + .attr('class', 'node-bkg'); + + const groupLabelContainer = groupsEl.append('g'); + let shiftedX1 = x1; + let shiftedY1 = y1; + if (data.icon) { + const bkgElem = groupLabelContainer.append('g'); + getIcon(data.icon)?.(bkgElem, groupIconSize); + bkgElem.attr( + 'transform', + 'translate(' + + (shiftedX1 + halfIconSize + 1) + + ', ' + + (shiftedY1 + halfIconSize + 1) + + ')' + ); + shiftedX1 += groupIconSize; + // TODO: test with more values + // - 1 - 2 comes from the Y axis transform of the icon and label + shiftedY1 += fontSize / 2 - 1 - 2; + } + if (data.label) { + const textElem = groupLabelContainer.append('g'); + await createText( + textElem, + data.label, + { + useHtmlLabels: false, + width: w, + classes: 'architecture-service-label', + }, + getConfig() + ); + textElem + .attr('dy', '1em') + .attr('alignment-baseline', 'middle') + .attr('dominant-baseline', 'start') + .attr('text-anchor', 'start'); + + textElem.attr( + 'transform', + 'translate(' + + (shiftedX1 + halfIconSize + 4) + + ', ' + + (shiftedY1 + halfIconSize + 2) + + ')' + ); + } } - } - }); + }) + ); }; -export const drawServices = function ( +export const drawServices = async function ( db: ArchitectureDB, elem: D3Element, services: ArchitectureService[] -): number { - services.forEach((service) => { +): Promise { + for (const service of services) { const serviceElem = elem.append('g'); const iconSize = getConfigField('iconSize'); if (service.title) { const textElem = serviceElem.append('g'); - createText( + await createText( textElem, service.title, { @@ -331,7 +335,7 @@ export const drawServices = function ( service.width = width; service.height = height; db.setElementForId(service.id, serviceElem); - }); + } return 0; }; From 95c483934dd473a90746efa0e6a97bca1398523f Mon Sep 17 00:00:00 2001 From: NicolasNewman Date: Mon, 19 Aug 2024 17:40:12 -0500 Subject: [PATCH 58/60] feat(arch): all non-generic icon packs are now lazy loaded --- demos/architecture.html | 7 +-- docs/config/icons.md | 12 +++- .../setup/interfaces/mermaid.MermaidConfig.md | 56 +++++++++---------- packages/mermaid/src/config.type.ts | 5 +- packages/mermaid/src/docs/config/icons.md | 12 +++- packages/mermaid/src/mermaidAPI.ts | 14 ++++- .../digitalOcean.ts | 0 .../mermaid/src/rendering-util/svg/index.ts | 18 ++++-- .../mermaid/src/schemas/config.schema.yaml | 2 +- 9 files changed, 77 insertions(+), 49 deletions(-) rename packages/mermaid/src/rendering-util/svg/{digitial-ocean => digital-ocean}/digitalOcean.ts (100%) diff --git a/demos/architecture.html b/demos/architecture.html index deacbc2423..fc65b6bebf 100644 --- a/demos/architecture.html +++ b/demos/architecture.html @@ -232,9 +232,8 @@

    AWS Icon Demo

    service s3(aws:s3)[Cloud Store] service ec2(aws:ec2)[Server] service wave(aws:wavelength)[Wave] - - s3:L -- R:ec2 - s3:T -- B:wave + service droplet(do:droplet)[Droplet] + service repo(gh:github)[Repository]
    @@ -296,7 +295,7 @@

    AWS Icon Demo

    iconSize: 80, }, useMaxWidth: false, - iconLibraries: ['aws:full'], + iconLibraries: ['aws:common', 'aws:full', 'github', 'digital-ocean'], }); function callback() { alert('It worked'); diff --git a/docs/config/icons.md b/docs/config/icons.md index 58487acbb4..d0a80f2d83 100644 --- a/docs/config/icons.md +++ b/docs/config/icons.md @@ -23,9 +23,15 @@ SVG Icons can be used with supported diagrams. Alongside the icon packs included | Digital Ocean | `do:` | | GitHub | `gh:` | -### Extended AWS Icons +Note that in order to use non-generic icons that are provided with Mermaid, the packs must be explicitly loaded when on initialization initialized. -Due to the large number of icons available to represent AWS services, only the most important ones are enabled by default. For full access, add `'aws:full'` to the `iconLibraries` field when initializing mermaid. +```js +import sampleIconPack from 'sample-icon-pack'; + +mermaid.initialize({ + iconLibraries: ['aws:common', 'aws:full', 'github', 'digital-ocean'], +}); +``` ## Using Custom Icon Packs @@ -35,7 +41,7 @@ Custom icon packs can be used by including them in the `iconLibraries` array on import sampleIconPack from 'sample-icon-pack'; mermaid.initialize({ - iconLibraries: [sampleIconPack, 'aws:full'], + iconLibraries: [sampleIconPack, 'aws:full', ...], }); ``` diff --git a/docs/config/setup/interfaces/mermaid.MermaidConfig.md b/docs/config/setup/interfaces/mermaid.MermaidConfig.md index d0f689ce09..c7ac33dde5 100644 --- a/docs/config/setup/interfaces/mermaid.MermaidConfig.md +++ b/docs/config/setup/interfaces/mermaid.MermaidConfig.md @@ -26,7 +26,7 @@ #### Defined in -[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190) +[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193) --- @@ -49,7 +49,7 @@ This matters if you are using base tag settings. #### Defined in -[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196) +[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199) --- @@ -59,7 +59,7 @@ This matters if you are using base tag settings. #### Defined in -[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193) +[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196) --- @@ -69,7 +69,7 @@ This matters if you are using base tag settings. #### Defined in -[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183) +[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186) --- @@ -93,7 +93,7 @@ You can set this attribute to base the seed on a static string. #### Defined in -[packages/mermaid/src/config.type.ts:177](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L177) +[packages/mermaid/src/config.type.ts:180](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L180) --- @@ -111,7 +111,7 @@ should not change unless content is changed. #### Defined in -[packages/mermaid/src/config.type.ts:170](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L170) +[packages/mermaid/src/config.type.ts:173](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L173) --- @@ -121,7 +121,7 @@ should not change unless content is changed. #### Defined in -[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197) +[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200) --- @@ -148,7 +148,7 @@ should not change unless content is changed. #### Defined in -[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185) +[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188) --- @@ -158,7 +158,7 @@ should not change unless content is changed. #### Defined in -[packages/mermaid/src/config.type.ts:178](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L178) +[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181) --- @@ -182,7 +182,7 @@ See #### Defined in -[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199) +[packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202) --- @@ -196,7 +196,7 @@ If set to true, ignores legacyMathML. #### Defined in -[packages/mermaid/src/config.type.ts:159](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L159) +[packages/mermaid/src/config.type.ts:162](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L162) --- @@ -206,7 +206,7 @@ If set to true, ignores legacyMathML. #### Defined in -[packages/mermaid/src/config.type.ts:180](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L180) +[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183) --- @@ -216,7 +216,7 @@ If set to true, ignores legacyMathML. #### Defined in -[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192) +[packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195) --- @@ -244,7 +244,7 @@ Defines the seed to be used when using handDrawn look. This is important for the ### iconLibraries -• `Optional` **iconLibraries**: ([`IconLibrary`](../modules/mermaid.md#iconlibrary) | `"aws:full"`)\[] +• `Optional` **iconLibraries**: ([`IconLibrary`](../modules/mermaid.md#iconlibrary) | `"aws:common"` | `"aws:full"` | `"github"` | `"digital-ocean"`)\[] This option specifies an object contianing a mappig of SVG icon names to a resolver that returns the svg code. For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram. @@ -261,7 +261,7 @@ For supported diagrams (i.e., Architecture), their syntax allows refering to key #### Defined in -[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181) +[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184) --- @@ -322,7 +322,7 @@ Defines which main look to use for the diagram. #### Defined in -[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200) +[packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203) --- @@ -356,7 +356,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191) +[packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194) --- @@ -366,7 +366,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195) +[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198) --- @@ -376,7 +376,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186) +[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189) --- @@ -386,7 +386,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187) +[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190) --- @@ -396,7 +396,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189) +[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192) --- @@ -406,7 +406,7 @@ The maximum allowed size of the users text diagram #### Defined in -[packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194) +[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197) --- @@ -442,7 +442,7 @@ Level of trust for parsed diagram #### Defined in -[packages/mermaid/src/config.type.ts:179](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L179) +[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182) --- @@ -464,7 +464,7 @@ Dictates whether mermaid starts on Page load #### Defined in -[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184) +[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187) --- @@ -477,7 +477,7 @@ This is useful when you want to control how to handle syntax errors in your appl #### Defined in -[packages/mermaid/src/config.type.ts:206](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L206) +[packages/mermaid/src/config.type.ts:209](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L209) --- @@ -520,7 +520,7 @@ You may also use `themeCSS` to override this value. #### Defined in -[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182) +[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185) --- @@ -530,7 +530,7 @@ You may also use `themeCSS` to override this value. #### Defined in -[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198) +[packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201) --- @@ -540,4 +540,4 @@ You may also use `themeCSS` to override this value. #### Defined in -[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188) +[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191) diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index 3f8144f239..7c2eccefdc 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -149,7 +149,10 @@ export interface MermaidConfig { * For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram. * */ - iconLibraries?: Array; + iconLibraries?: Array< + | import('./rendering-util/svgRegister.js').IconLibrary + | import('./rendering-util/svg/index.js').IconNamespaceKeys + >; /** * This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS * fonts and browser's MathML implementation, this option is recommended if consistent rendering is important. diff --git a/packages/mermaid/src/docs/config/icons.md b/packages/mermaid/src/docs/config/icons.md index 2850371829..0b9c78d541 100644 --- a/packages/mermaid/src/docs/config/icons.md +++ b/packages/mermaid/src/docs/config/icons.md @@ -17,9 +17,15 @@ SVG Icons can be used with supported diagrams. Alongside the icon packs included | Digital Ocean | `do:` | | GitHub | `gh:` | -### Extended AWS Icons +Note that in order to use non-generic icons that are provided with Mermaid, the packs must be explicitly loaded when on initialization initialized. -Due to the large number of icons available to represent AWS services, only the most important ones are enabled by default. For full access, add `'aws:full'` to the `iconLibraries` field when initializing mermaid. +```js +import sampleIconPack from 'sample-icon-pack'; + +mermaid.initialize({ + iconLibraries: ['aws:common', 'aws:full', 'github', 'digital-ocean'], +}); +``` ## Using Custom Icon Packs @@ -29,7 +35,7 @@ Custom icon packs can be used by including them in the `iconLibraries` array on import sampleIconPack from 'sample-icon-pack'; mermaid.initialize({ - iconLibraries: [sampleIconPack, 'aws:full'], + iconLibraries: [sampleIconPack, 'aws:full', ...], }); ``` diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index 861d115ff4..7fb8b84011 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -23,6 +23,7 @@ import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility. import type { DiagramMetadata, DiagramStyleClassDef } from './diagram-api/types.js'; import { preprocessDiagram } from './preprocess.js'; import { decodeEntities } from './utils.js'; +import type { IconLibrary } from './rendering-util/svgRegister.js'; import { registerIcons } from './rendering-util/svgRegister.js'; import defaultIconLibrary from './rendering-util/svg/index.js'; import { toBase64 } from './utils/base64.js'; @@ -495,10 +496,17 @@ function initialize(options: MermaidConfig = {}) { // eslint-disable-next-line @typescript-eslint/no-misused-promises options.iconLibraries.forEach(async (library) => { if (typeof library === 'string') { - if (library === 'aws:full') { - const { default: awsFull } = await import('./rendering-util/svg/aws/awsFull.js'); - registerIcons(awsFull); + let lib: IconLibrary = {}; + if (library === 'aws:common') { + lib = (await import('./rendering-util/svg/aws/awsCommon.js')).default; + } else if (library === 'aws:full') { + lib = (await import('./rendering-util/svg/aws/awsFull.js')).default; + } else if (library === 'digital-ocean') { + lib = (await import('./rendering-util/svg/digital-ocean/digitalOcean.js')).default; + } else if (library === 'github') { + lib = (await import('./rendering-util/svg/github/github.js')).default; } + registerIcons(lib); } else { registerIcons(library); } diff --git a/packages/mermaid/src/rendering-util/svg/digitial-ocean/digitalOcean.ts b/packages/mermaid/src/rendering-util/svg/digital-ocean/digitalOcean.ts similarity index 100% rename from packages/mermaid/src/rendering-util/svg/digitial-ocean/digitalOcean.ts rename to packages/mermaid/src/rendering-util/svg/digital-ocean/digitalOcean.ts diff --git a/packages/mermaid/src/rendering-util/svg/index.ts b/packages/mermaid/src/rendering-util/svg/index.ts index 01329bac57..22a8514a78 100644 --- a/packages/mermaid/src/rendering-util/svg/index.ts +++ b/packages/mermaid/src/rendering-util/svg/index.ts @@ -6,9 +6,16 @@ import internet from './default/internet.js'; import cloud from './default/cloud.js'; import unknown from './default/unknown.js'; import blank from './default/blank.js'; -import awsCommon from './aws/awsCommon.js'; -import digitalOcean from './digitial-ocean/digitalOcean.js'; -import github from './github/github.js'; + +/** Creates a resolver to the path to lazy-load included icon packs */ +const getIconNamespaces = (basePath: string) => ({ + 'aws:common': `${basePath}/aws/awsCommon.js`, + 'aws:full': `${basePath}/aws/awsFull.js`, + github: `${basePath}/github/github.js`, + 'digital-ocean': `${basePath}/digital-ocean/digitalOcean.js`, +}); + +type IconNamespaceKeys = keyof ReturnType; const defaultIconLibrary: IconLibrary = { database: database, @@ -18,9 +25,8 @@ const defaultIconLibrary: IconLibrary = { cloud: cloud, unknown: unknown, blank: blank, - ...awsCommon, - ...digitalOcean, - ...github, }; export default defaultIconLibrary; +export { getIconNamespaces }; +export type { IconNamespaceKeys }; diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index fddd4a6faf..0170607561 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -220,7 +220,7 @@ properties: description: | This option specifies an object contianing a mappig of SVG icon names to a resolver that returns the svg code. For supported diagrams (i.e., Architecture), their syntax allows refering to key names in this object to display the corresponding SVG icon in the rendered diagram. - tsType: Array + tsType: Array forceLegacyMathML: description: | This option forces Mermaid to rely on KaTeX's own stylesheet for rendering MathML. Due to differences between OS From 792a62438fb3c995ed2765146c72808773a6bb54 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:35:41 +0000 Subject: [PATCH 59/60] [autofix.ci] apply automated fixes --- .../setup/interfaces/mermaid.Mermaid.md | 26 +++++++++---------- .../setup/interfaces/mermaid.RunOptions.md | 8 +++--- docs/config/setup/modules/mermaid.md | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/config/setup/interfaces/mermaid.Mermaid.md b/docs/config/setup/interfaces/mermaid.Mermaid.md index 485bc77eb7..04f5bcc939 100644 --- a/docs/config/setup/interfaces/mermaid.Mermaid.md +++ b/docs/config/setup/interfaces/mermaid.Mermaid.md @@ -28,7 +28,7 @@ page. #### Defined in -[packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442) +[packages/mermaid/src/mermaid.ts:441](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L441) --- @@ -59,7 +59,7 @@ A graph definition key #### Defined in -[packages/mermaid/src/mermaid.ts:444](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L444) +[packages/mermaid/src/mermaid.ts:443](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L443) --- @@ -89,7 +89,7 @@ Use [initialize](mermaid.Mermaid.md#initialize) and [run](mermaid.Mermaid.md#run #### Defined in -[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437) +[packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436) --- @@ -116,7 +116,7 @@ This function should be called before the run function. #### Defined in -[packages/mermaid/src/mermaid.ts:441](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L441) +[packages/mermaid/src/mermaid.ts:440](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L440) --- @@ -130,7 +130,7 @@ Use [parse](mermaid.Mermaid.md#parse) and [render](mermaid.Mermaid.md#render) in #### Defined in -[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431) +[packages/mermaid/src/mermaid.ts:430](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L430) --- @@ -180,7 +180,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not #### Defined in -[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432) +[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431) --- @@ -190,7 +190,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not #### Defined in -[packages/mermaid/src/mermaid.ts:426](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L426) +[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425) --- @@ -218,7 +218,7 @@ Used to register external diagram types. #### Defined in -[packages/mermaid/src/mermaid.ts:440](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L440) +[packages/mermaid/src/mermaid.ts:439](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L439) --- @@ -242,7 +242,7 @@ Used to register external diagram types. #### Defined in -[packages/mermaid/src/mermaid.ts:439](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L439) +[packages/mermaid/src/mermaid.ts:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438) --- @@ -268,7 +268,7 @@ Used to register external diagram types. #### Defined in -[packages/mermaid/src/mermaid.ts:433](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L433) +[packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432) --- @@ -316,7 +316,7 @@ Renders the mermaid diagrams #### Defined in -[packages/mermaid/src/mermaid.ts:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438) +[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437) --- @@ -351,7 +351,7 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me #### Defined in -[packages/mermaid/src/mermaid.ts:443](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L443) +[packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442) --- @@ -361,4 +361,4 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me #### Defined in -[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425) +[packages/mermaid/src/mermaid.ts:424](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L424) diff --git a/docs/config/setup/interfaces/mermaid.RunOptions.md b/docs/config/setup/interfaces/mermaid.RunOptions.md index 8d38080bf6..6d45d35493 100644 --- a/docs/config/setup/interfaces/mermaid.RunOptions.md +++ b/docs/config/setup/interfaces/mermaid.RunOptions.md @@ -18,7 +18,7 @@ The nodes to render. If this is set, `querySelector` will be ignored. #### Defined in -[packages/mermaid/src/mermaid.ts:55](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L55) +[packages/mermaid/src/mermaid.ts:54](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L54) --- @@ -44,7 +44,7 @@ A callback to call after each diagram is rendered. #### Defined in -[packages/mermaid/src/mermaid.ts:59](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L59) +[packages/mermaid/src/mermaid.ts:58](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L58) --- @@ -56,7 +56,7 @@ The query selector to use when finding elements to render. Default: `".mermaid"` #### Defined in -[packages/mermaid/src/mermaid.ts:51](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L51) +[packages/mermaid/src/mermaid.ts:50](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L50) --- @@ -68,4 +68,4 @@ If `true`, errors will be logged to the console, but not thrown. Default: `false #### Defined in -[packages/mermaid/src/mermaid.ts:63](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L63) +[packages/mermaid/src/mermaid.ts:62](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L62) diff --git a/docs/config/setup/modules/mermaid.md b/docs/config/setup/modules/mermaid.md index a4361298e5..8e3b025b74 100644 --- a/docs/config/setup/modules/mermaid.md +++ b/docs/config/setup/modules/mermaid.md @@ -122,7 +122,7 @@ #### Defined in -[packages/mermaid/src/mermaid.ts:447](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L447) +[packages/mermaid/src/mermaid.ts:446](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L446) ## Functions From 256a148bbf484fc7db6c19f94dd69d5d268ee048 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Wed, 28 Aug 2024 13:52:51 +0200 Subject: [PATCH 60/60] Adding changeset for acrhitecture diagrams --- .changeset/nice-flowers-yawn.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/nice-flowers-yawn.md diff --git a/.changeset/nice-flowers-yawn.md b/.changeset/nice-flowers-yawn.md new file mode 100644 index 0000000000..31c0e81aff --- /dev/null +++ b/.changeset/nice-flowers-yawn.md @@ -0,0 +1,8 @@ +--- +"mermaid": minor +"@mermaid-js/docs": patch +--- + +New Diagram: Architecture + +Adds architecture diagrams which allows users to show relations between services.