From 0ea88df662ceb782ded3e0d031b5ef9bbb1b8995 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 29 Aug 2024 13:25:45 +0530 Subject: [PATCH 01/17] feat: Add iconify support --- packages/mermaid/package.json | 4 +- packages/mermaid/src/mermaid.ts | 3 ++ packages/mermaid/src/rendering-util/icons.ts | 36 ++++++++++++++++++ pnpm-lock.yaml | 39 ++++++++++++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 packages/mermaid/src/rendering-util/icons.ts diff --git a/packages/mermaid/package.json b/packages/mermaid/package.json index 3fee53a0da..198627560d 100644 --- a/packages/mermaid/package.json +++ b/packages/mermaid/package.json @@ -68,6 +68,7 @@ }, "dependencies": { "@braintree/sanitize-url": "^7.0.1", + "@iconify/utils": "^2.1.32", "@mermaid-js/parser": "workspace:^", "cytoscape": "^3.29.2", "cytoscape-cose-bilkent": "^4.1.0", @@ -88,8 +89,9 @@ }, "devDependencies": { "@adobe/jsonschema2md": "^8.0.0", - "@types/cytoscape-fcose": "^2.2.4", + "@iconify/types": "^2.0.0", "@types/cytoscape": "^3.21.4", + "@types/cytoscape-fcose": "^2.2.4", "@types/d3": "^7.4.3", "@types/d3-sankey": "^0.12.4", "@types/d3-scale": "^4.0.8", diff --git a/packages/mermaid/src/mermaid.ts b/packages/mermaid/src/mermaid.ts index 43fc5bd31b..52cca1cfe4 100644 --- a/packages/mermaid/src/mermaid.ts +++ b/packages/mermaid/src/mermaid.ts @@ -2,6 +2,7 @@ * Web page integration module for the mermaid framework. It uses the mermaidAPI for mermaid * functionality and to render the diagrams to svg code! */ +import { registerIconPacks } from '$root/rendering-util/icons.js'; import { dedent } from 'ts-dedent'; import type { MermaidConfig } from './config.type.js'; import { detectType, registerLazyLoadedDiagrams } from './diagram-api/detectType.js'; @@ -435,6 +436,7 @@ export interface Mermaid { contentLoaded: typeof contentLoaded; setParseErrorHandler: typeof setParseErrorHandler; detectType: typeof detectType; + registerIconPacks: typeof registerIconPacks; } const mermaid: Mermaid = { @@ -451,6 +453,7 @@ const mermaid: Mermaid = { contentLoaded, setParseErrorHandler, detectType, + registerIconPacks, }; export default mermaid; diff --git a/packages/mermaid/src/rendering-util/icons.ts b/packages/mermaid/src/rendering-util/icons.ts new file mode 100644 index 0000000000..76a03cde5c --- /dev/null +++ b/packages/mermaid/src/rendering-util/icons.ts @@ -0,0 +1,36 @@ +import { log } from '$root/logger.js'; +import type { IconifyJSON } from '@iconify/types'; +import type { IconifyIconCustomisations } from '@iconify/utils'; +import { getIconData, iconToHTML, iconToSVG, replaceIDs, stringToIcon } from '@iconify/utils'; + +export const iconsStore = new Map(); + +export const registerIconPacks = (...iconPacks: IconifyJSON[]) => { + for (const pack of iconPacks) { + iconsStore.set(pack.prefix, pack); + } +}; + +export const getIconSVG = (iconName: string, customisations?: IconifyIconCustomisations) => { + try { + const data = stringToIcon(iconName, true, true); + if (!data) { + throw new Error(`Invalid icon name: ${iconName}`); + } + const icons = iconsStore.get(data.prefix || 'default'); + if (!icons) { + throw new Error(`Icon set not found: ${data.prefix}`); + } + const iconData = getIconData(icons, data.name); + if (!iconData) { + throw new Error(`Icon not found: ${iconName}`); + } + const renderData = iconToSVG(iconData, customisations); + const svg = iconToHTML(replaceIDs(renderData.body), renderData.attributes); + return svg; + } catch (e) { + log.error(e); + // Return unknown icon svg. + return '?'; + } +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8e7d04115..6540551512 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -212,6 +212,9 @@ importers: '@braintree/sanitize-url': specifier: ^7.0.1 version: 7.1.0 + '@iconify/utils': + specifier: ^2.1.32 + version: 2.1.32 '@mermaid-js/parser': specifier: workspace:^ version: link:../parser @@ -267,6 +270,9 @@ importers: '@adobe/jsonschema2md': specifier: ^8.0.0 version: 8.0.2 + '@iconify/types': + specifier: ^2.0.0 + version: 2.0.0 '@types/cytoscape': specifier: ^3.21.4 version: 3.21.5 @@ -612,6 +618,9 @@ packages: '@antfu/install-pkg@0.1.1': resolution: {integrity: sha512-LyB/8+bSfa0DFGC06zpCEfs89/XoWZwws5ygEa5D+Xsm3OfI+aXQ86VgVG7Acyef+rSZ5HE7J8rrxzrQeM3PjQ==} + '@antfu/install-pkg@0.4.1': + resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==} + '@antfu/utils@0.7.10': resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} @@ -2175,6 +2184,9 @@ packages: '@iconify/utils@2.1.30': resolution: {integrity: sha512-bY0IO5xLOlbzJBnjWLxknp6Sss3yla03sVY9VeUz9nT6dbc+EGKlLfCt+6uytJnWm5CUvTF/BNotsLWF7kI61A==} + '@iconify/utils@2.1.32': + resolution: {integrity: sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==} + '@img/sharp-darwin-arm64@0.33.5': resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -7147,6 +7159,9 @@ packages: package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + package-manager-detector@0.2.0: + resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} + pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -8283,6 +8298,9 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + tinypool@0.8.4: resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} engines: {node: '>=14.0.0'} @@ -9281,6 +9299,11 @@ snapshots: execa: 5.1.1 find-up: 5.0.0 + '@antfu/install-pkg@0.4.1': + dependencies: + package-manager-detector: 0.2.0 + tinyexec: 0.3.0 + '@antfu/utils@0.7.10': {} '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': @@ -11249,6 +11272,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@iconify/utils@2.1.32': + dependencies: + '@antfu/install-pkg': 0.4.1 + '@antfu/utils': 0.7.10 + '@iconify/types': 2.0.0 + debug: 4.3.6(supports-color@8.1.1) + kolorist: 1.8.0 + local-pkg: 0.5.0 + mlly: 1.7.1 + transitivePeerDependencies: + - supports-color + '@img/sharp-darwin-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.0.4 @@ -17290,6 +17325,8 @@ snapshots: package-json-from-dist@1.0.0: {} + package-manager-detector@0.2.0: {} + pako@1.0.11: {} parent-module@1.0.1: @@ -18556,6 +18593,8 @@ snapshots: tinybench@2.9.0: {} + tinyexec@0.3.0: {} + tinypool@0.8.4: {} tinyspy@2.2.1: {} From e0f7ea56e129783226e780d54ba4466f5cd8b9a0 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 29 Aug 2024 13:55:20 +0530 Subject: [PATCH 02/17] fix: Unknown icon size --- packages/mermaid/src/rendering-util/icons.ts | 65 ++++++++++++++------ 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/packages/mermaid/src/rendering-util/icons.ts b/packages/mermaid/src/rendering-util/icons.ts index 76a03cde5c..199bc4206e 100644 --- a/packages/mermaid/src/rendering-util/icons.ts +++ b/packages/mermaid/src/rendering-util/icons.ts @@ -1,8 +1,14 @@ import { log } from '$root/logger.js'; -import type { IconifyJSON } from '@iconify/types'; +import type { ExtendedIconifyIcon, IconifyIcon, IconifyJSON } from '@iconify/types'; import type { IconifyIconCustomisations } from '@iconify/utils'; import { getIconData, iconToHTML, iconToSVG, replaceIDs, stringToIcon } from '@iconify/utils'; +export const unknownIcon: IconifyIcon = { + body: '?', + height: 80, + width: 80, +}; + export const iconsStore = new Map(); export const registerIconPacks = (...iconPacks: IconifyJSON[]) => { @@ -11,26 +17,47 @@ export const registerIconPacks = (...iconPacks: IconifyJSON[]) => { } }; -export const getIconSVG = (iconName: string, customisations?: IconifyIconCustomisations) => { +const getRegisteredIconData = (iconName: string, fallbackPrefix?: string) => { + const data = stringToIcon(iconName, true, fallbackPrefix !== undefined); + if (!data) { + throw new Error(`Invalid icon name: ${iconName}`); + } + const prefix = data.prefix || fallbackPrefix; + if (!prefix) { + throw new Error(`Icon name must contain a prefix: ${iconName}`); + } + const icons = iconsStore.get(prefix); + if (!icons) { + throw new Error(`Icon set not found: ${data.prefix}`); + } + const iconData = getIconData(icons, data.name); + if (!iconData) { + throw new Error(`Icon not found: ${iconName}`); + } + return iconData; +}; + +export const isIconAvailable = (iconName: string) => { + try { + getRegisteredIconData(iconName); + return true; + } catch { + return false; + } +}; + +export const getIconSVG = ( + iconName: string, + customisations?: IconifyIconCustomisations & { fallbackPrefix?: string } +) => { + let iconData: ExtendedIconifyIcon; try { - const data = stringToIcon(iconName, true, true); - if (!data) { - throw new Error(`Invalid icon name: ${iconName}`); - } - const icons = iconsStore.get(data.prefix || 'default'); - if (!icons) { - throw new Error(`Icon set not found: ${data.prefix}`); - } - const iconData = getIconData(icons, data.name); - if (!iconData) { - throw new Error(`Icon not found: ${iconName}`); - } - const renderData = iconToSVG(iconData, customisations); - const svg = iconToHTML(replaceIDs(renderData.body), renderData.attributes); - return svg; + iconData = getRegisteredIconData(iconName, customisations?.fallbackPrefix); } catch (e) { log.error(e); - // Return unknown icon svg. - return '?'; + iconData = unknownIcon; } + const renderData = iconToSVG(iconData, customisations); + const svg = iconToHTML(replaceIDs(renderData.body), renderData.attributes); + return svg; }; From 6ecdf7be688efdc53c52fea3ba891327242bc890 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 29 Aug 2024 13:56:16 +0530 Subject: [PATCH 03/17] docs: Changeset --- .changeset/eight-apricots-arrive.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/eight-apricots-arrive.md diff --git a/.changeset/eight-apricots-arrive.md b/.changeset/eight-apricots-arrive.md new file mode 100644 index 0000000000..551d798374 --- /dev/null +++ b/.changeset/eight-apricots-arrive.md @@ -0,0 +1,5 @@ +--- +'mermaid': minor +--- + +feat: Add support for iconify icons From a4b7e494dbc39a49393be144050b60d9a47613b1 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 29 Aug 2024 13:56:43 +0530 Subject: [PATCH 04/17] feat: Support - in icon names --- .../parser/src/language/architecture/architecture.langium | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/parser/src/language/architecture/architecture.langium b/packages/parser/src/language/architecture/architecture.langium index 6246b423ed..11af262432 100644 --- a/packages/parser/src/language/architecture/architecture.langium +++ b/packages/parser/src/language/architecture/architecture.langium @@ -49,7 +49,7 @@ Edge: terminal ARROW_DIRECTION: 'L' | 'R' | 'T' | 'B'; terminal ARCH_ID: /[\w]+/; terminal ARCH_TEXT_ICON: /\("[^"]+"\)/; -terminal ARCH_ICON: /\([\w:]+\)/; +terminal ARCH_ICON: /\([\w-:]+\)/; terminal ARCH_TITLE: /\[[\w ]+\]/; terminal ARROW_GROUP: /\{group\}/; -terminal ARROW_INTO: /<|>/; \ No newline at end of file +terminal ARROW_INTO: /<|>/; From 790f71bb1a118e82fb92b4fbf7fe2ccbd3c8e5a3 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 29 Aug 2024 14:00:21 +0530 Subject: [PATCH 05/17] feat: Move architecture icons into iconify format --- .../architecture/architectureIcons.ts | 43 ++++++++ .../architecture/architectureRenderer.ts | 6 +- .../diagrams/architecture/icons/default.ts | 99 ------------------- .../architecture/icons/svgRegister.ts | 50 ---------- .../src/diagrams/architecture/svgDraw.ts | 20 ++-- 5 files changed, 58 insertions(+), 160 deletions(-) create mode 100644 packages/mermaid/src/diagrams/architecture/architectureIcons.ts delete mode 100644 packages/mermaid/src/diagrams/architecture/icons/default.ts delete mode 100644 packages/mermaid/src/diagrams/architecture/icons/svgRegister.ts diff --git a/packages/mermaid/src/diagrams/architecture/architectureIcons.ts b/packages/mermaid/src/diagrams/architecture/architectureIcons.ts new file mode 100644 index 0000000000..dd6c99f9c9 --- /dev/null +++ b/packages/mermaid/src/diagrams/architecture/architectureIcons.ts @@ -0,0 +1,43 @@ +import { unknownIcon } from '$root/rendering-util/icons.js'; +import type { IconifyJSON } from '@iconify/types'; + +const wrapIcon = (icon: string) => { + return `${icon}`; +}; + +export const architectureIcons: IconifyJSON = { + prefix: 'mermaid-architecture', + height: 80, + width: 80, + icons: { + database: { + body: wrapIcon( + '' + ), + }, + server: { + body: wrapIcon( + '' + ), + }, + disk: { + body: wrapIcon( + '' + ), + }, + internet: { + body: wrapIcon( + '' + ), + }, + cloud: { + body: wrapIcon( + '' + ), + }, + unknown: unknownIcon, + blank: { + body: wrapIcon(''), + }, + }, +}; diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index 558b904835..84b9ed6554 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -1,3 +1,4 @@ +import { registerIconPacks } from '$root/rendering-util/icons.js'; import type { Position } from 'cytoscape'; import cytoscape from 'cytoscape'; import type { FcoseLayoutOptions } from 'cytoscape-fcose'; @@ -9,6 +10,7 @@ import { log } from '../../logger.js'; import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; import { setupGraphViewbox } from '../../setupGraphViewbox.js'; import { getConfigField } from './architectureDb.js'; +import { architectureIcons } from './architectureIcons.js'; import type { ArchitectureDataStructures, ArchitectureJunction, @@ -30,11 +32,9 @@ import { isArchitectureDirectionY, nodeData, } from './architectureTypes.js'; -import { defaultIconLibrary } from './icons/default.js'; -import { registerIconLibrary } from './icons/svgRegister.js'; import { drawEdges, drawGroups, drawJunctions, drawServices } from './svgDraw.js'; -registerIconLibrary(defaultIconLibrary); +registerIconPacks(architectureIcons); cytoscape.use(fcose); function addServices(services: ArchitectureService[], cy: cytoscape.Core) { diff --git a/packages/mermaid/src/diagrams/architecture/icons/default.ts b/packages/mermaid/src/diagrams/architecture/icons/default.ts deleted file mode 100644 index 8c33df09c6..0000000000 --- a/packages/mermaid/src/diagrams/architecture/icons/default.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { createIcon, type IconLibrary } from './svgRegister.js'; - -export const defaultIconLibrary: IconLibrary = { - database: createIcon( - ` - - - - - - - -`, - 80 - ), - server: createIcon( - ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -`, - 80 - ), - disk: createIcon( - ` - - - - - - - - - -`, - 80 - ), - internet: createIcon( - ` - - - - - - - - -`, - 80 - ), - cloud: createIcon( - ` - - - `, - 80 - ), - unknown: createIcon( - ` - - ? -`, - 80 - ), - blank: createIcon( - ` - -`, - 80 - ), -}; diff --git a/packages/mermaid/src/diagrams/architecture/icons/svgRegister.ts b/packages/mermaid/src/diagrams/architecture/icons/svgRegister.ts deleted file mode 100644 index 712644afa6..0000000000 --- a/packages/mermaid/src/diagrams/architecture/icons/svgRegister.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { log } from '$root/logger.js'; -import type { Selection } from 'd3-selection'; - -export type IconResolver = ( - parent: Selection, - width?: number -) => Selection; -export type IconLibrary = Record; - -/** - * Converts an SVG Icon passed as a string into a properly formatted IconResolver - * @param icon - html code for the svg icon as a string (the SVG tag should not be included) - * @param originalSize - the original size of the SVG Icon in pixels - * @returns IconResolver - */ -export const createIcon: (icon: string, originalSize: number) => IconResolver = ( - icon, - originalSize -) => { - return ( - parent: Selection, - size: number = originalSize - ) => { - parent.html(`${icon}`); - return parent; - }; -}; - -const icons: IconLibrary = {}; - -export const isIconNameInUse = (name: string): boolean => { - return icons[name] !== undefined; -}; - -export const registerIconLibrary = (library: IconLibrary) => { - Object.entries(library).forEach(([name, resolver]) => { - if (!isIconNameInUse(name)) { - icons[name] = resolver; - } else { - log.warn(`Icon with name ${name} already exists. Skipping registration.`); - } - }); -}; - -export const getIcon = (name: string): IconResolver | null => { - if (isIconNameInUse(name)) { - return icons[name]; - } - return icons.unknown; -}; diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts index ef3ddb7f6c..d1c053f717 100644 --- a/packages/mermaid/src/diagrams/architecture/svgDraw.ts +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -1,10 +1,10 @@ -// TODO remove no-console -/* eslint-disable no-console */ +import { getIconSVG } from '$root/rendering-util/icons.js'; import type cytoscape from 'cytoscape'; import { getConfig } from '../../diagram-api/diagramAPI.js'; import { createText } from '../../rendering-util/createText.js'; import type { D3Element } from '../../types.js'; import { db, getConfigField } from './architectureDb.js'; +import { architectureIcons } from './architectureIcons.js'; import { ArchitectureDirectionArrow, ArchitectureDirectionArrowShift, @@ -20,7 +20,6 @@ import { type ArchitectureJunction, type ArchitectureService, } from './architectureTypes.js'; -import { getIcon } from './icons/svgRegister.js'; export const drawEdges = async function (edgesEl: D3Element, cy: cytoscape.Core) { const padding = getConfigField('padding'); @@ -198,7 +197,6 @@ export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Cor 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') @@ -213,7 +211,9 @@ export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Cor let shiftedY1 = y1; if (data.icon) { const bkgElem = groupLabelContainer.append('g'); - getIcon(data.icon)?.(bkgElem, groupIconSize); + bkgElem.html( + `${getIconSVG(data.icon, { height: groupIconSize, width: groupIconSize, fallbackPrefix: architectureIcons.prefix })}` + ); bkgElem.attr( 'transform', 'translate(' + @@ -290,15 +290,19 @@ export const drawServices = async function ( textElem.attr('transform', 'translate(' + iconSize / 2 + ', ' + iconSize + ')'); } - let bkgElem = serviceElem.append('g'); + const bkgElem = serviceElem.append('g'); if (service.icon) { // TODO: should a warning be given to end-users saying which icon names are available? // if (!isIconNameInUse(service.icon)) { // throw new Error(`Invalid SVG Icon name: "${service.icon}"`); // } - bkgElem = getIcon(service.icon)?.(bkgElem, iconSize); + bkgElem.html( + `${getIconSVG(service.icon, { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}` + ); } else if (service.iconText) { - bkgElem = getIcon('blank')?.(bkgElem, iconSize); + bkgElem.html( + `${getIconSVG('blank', { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}` + ); const textElemContainer = bkgElem.append('g'); const fo = textElemContainer .append('foreignObject') From 7d8143b917ee3562149a0e0a821ed2d6f29cc05d Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Thu, 29 Aug 2024 14:01:26 +0530 Subject: [PATCH 06/17] docs: Changeset --- .changeset/yellow-maps-poke.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/yellow-maps-poke.md diff --git a/.changeset/yellow-maps-poke.md b/.changeset/yellow-maps-poke.md new file mode 100644 index 0000000000..4e6d6bd235 --- /dev/null +++ b/.changeset/yellow-maps-poke.md @@ -0,0 +1,5 @@ +--- +'@mermaid-js/parser': patch +--- + +feat: Support - in architecture icons From 249e18314a57970598ba95f36816454335d38f4d Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 09:36:26 +0000 Subject: [PATCH 07/17] [autofix.ci] apply automated fixes --- .../setup/interfaces/mermaid.Mermaid.md | 50 ++++++++++++++----- .../setup/interfaces/mermaid.RunOptions.md | 8 +-- docs/config/setup/modules/mermaid.md | 2 +- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/docs/config/setup/interfaces/mermaid.Mermaid.md b/docs/config/setup/interfaces/mermaid.Mermaid.md index 09fab149cd..99a054caa7 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:435](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L435) +[packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436) --- @@ -59,7 +59,7 @@ A graph definition key #### 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:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438) --- @@ -89,7 +89,7 @@ Use [initialize](mermaid.Mermaid.md#initialize) and [run](mermaid.Mermaid.md#run #### Defined in -[packages/mermaid/src/mermaid.ts:430](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L430) +[packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431) --- @@ -116,7 +116,7 @@ This function should be called before the run function. #### Defined in -[packages/mermaid/src/mermaid.ts:434](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L434) +[packages/mermaid/src/mermaid.ts:435](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L435) --- @@ -130,7 +130,7 @@ Use [parse](mermaid.Mermaid.md#parse) and [render](mermaid.Mermaid.md#render) in #### Defined in -[packages/mermaid/src/mermaid.ts:424](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L424) +[packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425) --- @@ -180,7 +180,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not #### 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:426](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L426) --- @@ -190,7 +190,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not #### Defined in -[packages/mermaid/src/mermaid.ts:419](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L419) +[packages/mermaid/src/mermaid.ts:420](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L420) --- @@ -218,7 +218,31 @@ 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:434](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L434) + +--- + +### registerIconPacks + +• **registerIconPacks**: (...`iconPacks`: `IconifyJSON`\[]) => `void` + +#### Type declaration + +▸ (`...iconPacks`): `void` + +##### Parameters + +| Name | Type | +| :------------- | :--------------- | +| `...iconPacks` | `IconifyJSON`\[] | + +##### Returns + +`void` + +#### Defined in + +[packages/mermaid/src/mermaid.ts:439](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L439) --- @@ -242,7 +266,7 @@ Used to register external diagram types. #### 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:433](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L433) --- @@ -268,7 +292,7 @@ Used to register external diagram types. #### 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:427](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L427) --- @@ -316,7 +340,7 @@ Renders the mermaid diagrams #### 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:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432) --- @@ -351,7 +375,7 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me #### Defined in -[packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436) +[packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437) --- @@ -361,4 +385,4 @@ to it (eg. dart interop wrapper). (Initially there is no parseError member of me #### Defined in -[packages/mermaid/src/mermaid.ts:418](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L418) +[packages/mermaid/src/mermaid.ts:419](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L419) diff --git a/docs/config/setup/interfaces/mermaid.RunOptions.md b/docs/config/setup/interfaces/mermaid.RunOptions.md index aae004d6d5..0bcfc2356e 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:48](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L48) +[packages/mermaid/src/mermaid.ts:49](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L49) --- @@ -44,7 +44,7 @@ A callback to call after each diagram is rendered. #### Defined in -[packages/mermaid/src/mermaid.ts:52](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L52) +[packages/mermaid/src/mermaid.ts:53](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L53) --- @@ -56,7 +56,7 @@ The query selector to use when finding elements to render. Default: `".mermaid"` #### Defined in -[packages/mermaid/src/mermaid.ts:44](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L44) +[packages/mermaid/src/mermaid.ts:45](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L45) --- @@ -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:56](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L56) +[packages/mermaid/src/mermaid.ts:57](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L57) diff --git a/docs/config/setup/modules/mermaid.md b/docs/config/setup/modules/mermaid.md index bdaeb05e1e..60c219f2d2 100644 --- a/docs/config/setup/modules/mermaid.md +++ b/docs/config/setup/modules/mermaid.md @@ -87,4 +87,4 @@ #### 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:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442) From ef26fc921c003cd90793926408d91fa9fb616d8f Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 2 Sep 2024 13:20:34 +0530 Subject: [PATCH 08/17] docs: Add iconify docs --- .cspell/libraries.txt | 1 + docs/syntax/architecture.md | 56 ++++++++++++++++++- .../mermaid/src/docs/syntax/architecture.md | 56 ++++++++++++++++++- 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/.cspell/libraries.txt b/.cspell/libraries.txt index ad0e3e7011..73a2dceeb2 100644 --- a/.cspell/libraries.txt +++ b/.cspell/libraries.txt @@ -30,6 +30,7 @@ Foswiki Gitea graphlib Grav +icones iconify Inkdrop jiti diff --git a/docs/syntax/architecture.md b/docs/syntax/architecture.md index 2d71d84fba..c204dbca6d 100644 --- a/docs/syntax/architecture.md +++ b/docs/syntax/architecture.md @@ -191,4 +191,58 @@ architecture-beta bottom_gateway:T -- B:junctionRight ``` -## Configuration +## Icons + +By default, architecture diagram supports the following icons: `cloud`, `database`, `disk`, `internet`, `server`. +Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps below. + +The icon packs available can be found at [icones.js.org](https://icones.js.org/). + +Using JSON file directly from CDN: + +```js +import mermaid from 'CDN/mermaid.esm.mjs'; + +// You have to call `initialize` with startOnLoad:false before calling `registerIconPacks`, +// to prevent mermaid from starting before the icons are loaded +mermaid.initialize({ + startOnLoad: false, + logLevel: 0, +}); +const logos = await fetch('https://unpkg.com/@iconify-json/logos/icons.json'); +mermaid.registerIconPacks(await logos.json()); +mermaid.init(); +``` + +Using packages and a bundler: + +```js +import mermaid from 'mermaid'; +// npm install @iconify-json/logos +import { icons as logos } from '@iconify-json/logos'; + +mermaid.initialize({ + startOnLoad: false, + logLevel: 0, +}); +mermaid.registerIconPacks(logos); +mermaid.init(); +``` + +After the icons are installed, they can be used in the architecture diagram by using the format "prefix:icon-name", where prefix comes from the icon pack you selected. + +```` +```mermaid +architecture-beta + group api(logos:aws-lambda)[API] + + service db(logos:aws-aurora)[Database] in api + service disk1(logos:aws-glacier)[Storage] in api + service disk2(logos:aws-s3)[Storage] in api + service server(logos:aws-ec2)[Server] in api + + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db +``` +```` diff --git a/packages/mermaid/src/docs/syntax/architecture.md b/packages/mermaid/src/docs/syntax/architecture.md index e74995ce33..c2aa2fb995 100644 --- a/packages/mermaid/src/docs/syntax/architecture.md +++ b/packages/mermaid/src/docs/syntax/architecture.md @@ -153,4 +153,58 @@ architecture-beta bottom_gateway:T -- B:junctionRight ``` -## Configuration +## Icons + +By default, architecture diagram supports the following icons: `cloud`, `database`, `disk`, `internet`, `server`. +Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps below. + +The icon packs available can be found at [icones.js.org](https://icones.js.org/). + +Using JSON file directly from CDN: + +```js +import mermaid from 'CDN/mermaid.esm.mjs'; + +// You have to call `initialize` with startOnLoad:false before calling `registerIconPacks`, +// to prevent mermaid from starting before the icons are loaded +mermaid.initialize({ + startOnLoad: false, + logLevel: 0, +}); +const logos = await fetch('https://unpkg.com/@iconify-json/logos/icons.json'); +mermaid.registerIconPacks(await logos.json()); +mermaid.init(); +``` + +Using packages and a bundler: + +```js +import mermaid from 'mermaid'; +// npm install @iconify-json/logos +import { icons as logos } from '@iconify-json/logos'; + +mermaid.initialize({ + startOnLoad: false, + logLevel: 0, +}); +mermaid.registerIconPacks(logos); +mermaid.init(); +``` + +After the icons are installed, they can be used in the architecture diagram by using the format "prefix:icon-name", where prefix comes from the icon pack you selected. + +```` +```mermaid +architecture-beta + group api(logos:aws-lambda)[API] + + service db(logos:aws-aurora)[Database] in api + service disk1(logos:aws-glacier)[Storage] in api + service disk2(logos:aws-s3)[Storage] in api + service server(logos:aws-ec2)[Server] in api + + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db +``` +```` From 2d8342261b42d6f75b3a97f1422844811eb555c1 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 2 Sep 2024 13:50:50 +0530 Subject: [PATCH 09/17] docs: Add iconify docs --- .../classes/mermaid.UnknownDiagramError.md | 6 ++--- docs/syntax/architecture.md | 23 +++++++++++++++++++ .../src/docs/.vitepress/theme/Mermaid.vue | 2 -- .../src/docs/.vitepress/theme/mermaid.ts | 14 ++++++++++- .../mermaid/src/docs/syntax/architecture.md | 23 +++++++++++++++++++ 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/docs/config/setup/classes/mermaid.UnknownDiagramError.md b/docs/config/setup/classes/mermaid.UnknownDiagramError.md index 3e1edf597e..abe205bb53 100644 --- a/docs/config/setup/classes/mermaid.UnknownDiagramError.md +++ b/docs/config/setup/classes/mermaid.UnknownDiagramError.md @@ -127,7 +127,7 @@ Error.prepareStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:28 +node_modules/@types/node/globals.d.ts:98 --- @@ -141,7 +141,7 @@ Error.stackTraceLimit #### Defined in -node_modules/@types/node/globals.d.ts:30 +node_modules/@types/node/globals.d.ts:100 ## Methods @@ -168,4 +168,4 @@ Error.captureStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:21 +node_modules/@types/node/globals.d.ts:91 diff --git a/docs/syntax/architecture.md b/docs/syntax/architecture.md index c204dbca6d..e1b13de182 100644 --- a/docs/syntax/architecture.md +++ b/docs/syntax/architecture.md @@ -246,3 +246,26 @@ architecture-beta disk2:T -- B:db ``` ```` + +
loading...
+ + diff --git a/packages/mermaid/src/docs/.vitepress/theme/Mermaid.vue b/packages/mermaid/src/docs/.vitepress/theme/Mermaid.vue index b98c49348d..5e0bbfa87e 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/Mermaid.vue +++ b/packages/mermaid/src/docs/.vitepress/theme/Mermaid.vue @@ -86,11 +86,9 @@ onUnmounted(() => mut.disconnect()); const renderChart = async () => { console.log('rendering chart' + props.id + code.value); - const hasDarkClass = document.documentElement.classList.contains('dark'); const mermaidConfig = { securityLevel: 'loose', startOnLoad: false, - theme: hasDarkClass ? 'dark' : 'default', }; let svgCode = await render(props.id, code.value, mermaidConfig); // This is a hack to force v-html to re-render, otherwise the diagram disappears diff --git a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts index 47e2386926..79ba278e0f 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts @@ -5,7 +5,19 @@ const init = mermaid.registerExternalDiagrams([zenuml]); export const render = async (id: string, code: string, config: MermaidConfig): Promise => { await init; - mermaid.initialize(config); + const hasDarkClass = document.documentElement.classList.contains('dark'); + const theme = hasDarkClass ? 'dark' : 'default'; + mermaid.initialize({ ...config, theme }); const { svg } = await mermaid.render(id, code); return svg; }; + +declare global { + interface Window { + mermaid: typeof mermaid; + render: typeof render; + } +} + +window.mermaid = mermaid; +window.render = render; diff --git a/packages/mermaid/src/docs/syntax/architecture.md b/packages/mermaid/src/docs/syntax/architecture.md index c2aa2fb995..ed50e1c0b7 100644 --- a/packages/mermaid/src/docs/syntax/architecture.md +++ b/packages/mermaid/src/docs/syntax/architecture.md @@ -208,3 +208,26 @@ architecture-beta disk2:T -- B:db ``` ```` + +
loading...
+ + From 7cda494bf4d28fe1410bf22d073cca42b7d5cb5f Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 08:25:52 +0000 Subject: [PATCH 10/17] [autofix.ci] apply automated fixes --- docs/config/setup/classes/mermaid.UnknownDiagramError.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/config/setup/classes/mermaid.UnknownDiagramError.md b/docs/config/setup/classes/mermaid.UnknownDiagramError.md index abe205bb53..3e1edf597e 100644 --- a/docs/config/setup/classes/mermaid.UnknownDiagramError.md +++ b/docs/config/setup/classes/mermaid.UnknownDiagramError.md @@ -127,7 +127,7 @@ Error.prepareStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:98 +node_modules/@types/node/globals.d.ts:28 --- @@ -141,7 +141,7 @@ Error.stackTraceLimit #### Defined in -node_modules/@types/node/globals.d.ts:100 +node_modules/@types/node/globals.d.ts:30 ## Methods @@ -168,4 +168,4 @@ Error.captureStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:91 +node_modules/@types/node/globals.d.ts:21 From 7056c20ccaea65e9f85d74c705256a9fcffdf8e9 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 2 Sep 2024 14:00:03 +0530 Subject: [PATCH 11/17] docs: Add external icons in demos --- demos/architecture.html | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/demos/architecture.html b/demos/architecture.html index 137b3fbf01..0e2ce14530 100644 --- a/demos/architecture.html +++ b/demos/architecture.html @@ -226,27 +226,23 @@

Junction Demo Groups


- + service s3(logos:aws-s3)[Cloud Store] + service ec2(logos:aws-ec2)[Server] + service api(logos:aws-api-gateway)[Api Gateway] + From f5cc0dcaea980f8553e7c6460c210d2ff679ed92 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 2 Sep 2024 14:05:37 +0530 Subject: [PATCH 12/17] test: External diagram rendering test --- cypress/helpers/util.ts | 2 +- .../rendering/architecture.spec.ts | 6 ++- cypress/platform/architecture-external.html | 39 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 cypress/platform/architecture-external.html diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts index 133a350328..74b17cf055 100644 --- a/cypress/helpers/util.ts +++ b/cypress/helpers/util.ts @@ -73,7 +73,7 @@ export const imgSnapshotTest = ( export const urlSnapshotTest = ( url: string, - options: CypressMermaidConfig, + options: CypressMermaidConfig = {}, _api = false, validation?: any ): void => { diff --git a/cypress/integration/rendering/architecture.spec.ts b/cypress/integration/rendering/architecture.spec.ts index 9eefbe1e42..6c49298dad 100644 --- a/cypress/integration/rendering/architecture.spec.ts +++ b/cypress/integration/rendering/architecture.spec.ts @@ -1,4 +1,4 @@ -import { imgSnapshotTest } from '../../helpers/util.ts'; +import { imgSnapshotTest, urlSnapshotTest } from '../../helpers/util.ts'; describe.skip('architecture diagram', () => { it('should render a simple architecture diagram with groups', () => { @@ -172,3 +172,7 @@ describe.skip('architecture diagram', () => { ); }); }); + +it('should allow adding external icons', () => { + urlSnapshotTest('http://localhost:9000/architecture-external.html'); +}); diff --git a/cypress/platform/architecture-external.html b/cypress/platform/architecture-external.html new file mode 100644 index 0000000000..6057098462 --- /dev/null +++ b/cypress/platform/architecture-external.html @@ -0,0 +1,39 @@ + + + + + + Architecture Mermaid Test Page + + + + + +

External Icons Demo

+
+    architecture-beta
+      service s3(logos:aws-s3)[Cloud Store]
+      service ec2(logos:aws-ec2)[Server]
+      service api(logos:aws-api-gateway)[Api Gateway]
+    
+ + + + From 69c9a564f2f190498f95f733ef58bfb6043f6598 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 2 Sep 2024 16:11:33 +0530 Subject: [PATCH 13/17] chore: Disable mermaid in SSR --- .../setup/classes/mermaid.UnknownDiagramError.md | 6 +++--- docs/syntax/architecture.md | 4 +++- .../mermaid/src/docs/.vitepress/theme/mermaid.ts | 13 ++++++++++--- packages/mermaid/src/docs/syntax/architecture.md | 4 +++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/config/setup/classes/mermaid.UnknownDiagramError.md b/docs/config/setup/classes/mermaid.UnknownDiagramError.md index 3e1edf597e..abe205bb53 100644 --- a/docs/config/setup/classes/mermaid.UnknownDiagramError.md +++ b/docs/config/setup/classes/mermaid.UnknownDiagramError.md @@ -127,7 +127,7 @@ Error.prepareStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:28 +node_modules/@types/node/globals.d.ts:98 --- @@ -141,7 +141,7 @@ Error.stackTraceLimit #### Defined in -node_modules/@types/node/globals.d.ts:30 +node_modules/@types/node/globals.d.ts:100 ## Methods @@ -168,4 +168,4 @@ Error.captureStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:21 +node_modules/@types/node/globals.d.ts:91 diff --git a/docs/syntax/architecture.md b/docs/syntax/architecture.md index e1b13de182..a45a9308bd 100644 --- a/docs/syntax/architecture.md +++ b/docs/syntax/architecture.md @@ -267,5 +267,7 @@ const main = async () => { document.getElementById('arch-example').innerHTML = svg; }; -setTimeout(main, 100) +if (!import.meta.env.SSR) { + setTimeout(main, 100); +} diff --git a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts index 79ba278e0f..c4021d2cb1 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts @@ -17,7 +17,14 @@ declare global { mermaid: typeof mermaid; render: typeof render; } -} -window.mermaid = mermaid; -window.render = render; + interface ImportMeta { + env: { + SSR: boolean; + }; + } +} +if (!import.meta.env.SSR) { + window.mermaid = mermaid; + window.render = render; +} diff --git a/packages/mermaid/src/docs/syntax/architecture.md b/packages/mermaid/src/docs/syntax/architecture.md index ed50e1c0b7..f4ff3bd722 100644 --- a/packages/mermaid/src/docs/syntax/architecture.md +++ b/packages/mermaid/src/docs/syntax/architecture.md @@ -229,5 +229,7 @@ const main = async () => { document.getElementById('arch-example').innerHTML = svg; }; -setTimeout(main, 100) +if (!import.meta.env.SSR) { + setTimeout(main, 100); +} From 0b127eecd96773ca511cf6991099259ae102cdd7 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:46:53 +0000 Subject: [PATCH 14/17] [autofix.ci] apply automated fixes --- docs/config/setup/classes/mermaid.UnknownDiagramError.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/config/setup/classes/mermaid.UnknownDiagramError.md b/docs/config/setup/classes/mermaid.UnknownDiagramError.md index abe205bb53..3e1edf597e 100644 --- a/docs/config/setup/classes/mermaid.UnknownDiagramError.md +++ b/docs/config/setup/classes/mermaid.UnknownDiagramError.md @@ -127,7 +127,7 @@ Error.prepareStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:98 +node_modules/@types/node/globals.d.ts:28 --- @@ -141,7 +141,7 @@ Error.stackTraceLimit #### Defined in -node_modules/@types/node/globals.d.ts:100 +node_modules/@types/node/globals.d.ts:30 ## Methods @@ -168,4 +168,4 @@ Error.captureStackTrace #### Defined in -node_modules/@types/node/globals.d.ts:91 +node_modules/@types/node/globals.d.ts:21 From c68ae309e5379fec11ccd6f7edc89a2fb7d89cb4 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 2 Sep 2024 18:34:58 +0530 Subject: [PATCH 15/17] test: Wrap in describe block --- cypress/integration/rendering/architecture.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cypress/integration/rendering/architecture.spec.ts b/cypress/integration/rendering/architecture.spec.ts index 6c49298dad..1deb1f7da1 100644 --- a/cypress/integration/rendering/architecture.spec.ts +++ b/cypress/integration/rendering/architecture.spec.ts @@ -173,6 +173,8 @@ describe.skip('architecture diagram', () => { }); }); -it('should allow adding external icons', () => { - urlSnapshotTest('http://localhost:9000/architecture-external.html'); +describe('architecture - external', () => { + it('should allow adding external icons', () => { + urlSnapshotTest('http://localhost:9000/architecture-external.html'); + }); }); From 0edfab104886de283ca6c4741ff1568f03670f77 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 2 Sep 2024 19:51:14 +0530 Subject: [PATCH 16/17] feat: Lazy load icons Co-authored-by: Alois Klink --- cypress/platform/architecture-external.html | 9 +- demos/architecture.html | 22 +++-- .../setup/interfaces/mermaid.Mermaid.md | 10 +-- docs/syntax/architecture.md | 90 ++++++++++--------- .../architecture/architectureRenderer.ts | 7 +- .../src/diagrams/architecture/svgDraw.ts | 6 +- .../src/docs/.vitepress/theme/Mermaid.vue | 2 + .../src/docs/.vitepress/theme/mermaid.ts | 28 ++---- .../mermaid/src/docs/syntax/architecture.md | 82 ++++++++--------- packages/mermaid/src/rendering-util/icons.ts | 59 +++++++++--- 10 files changed, 175 insertions(+), 140 deletions(-) diff --git a/cypress/platform/architecture-external.html b/cypress/platform/architecture-external.html index 6057098462..b76ac54ce5 100644 --- a/cypress/platform/architecture-external.html +++ b/cypress/platform/architecture-external.html @@ -28,8 +28,13 @@

External Icons Demo

startOnLoad: false, logLevel: 0, }); - const logos = await fetch('https://unpkg.com/@iconify-json/logos/icons.json'); - mermaid.registerIconPacks(await logos.json()); + mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => + fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()), + }, + ]); await mermaid.run(); if (window.Cypress) { window.rendered = true; diff --git a/demos/architecture.html b/demos/architecture.html index 0e2ce14530..6d978d9525 100644 --- a/demos/architecture.html +++ b/demos/architecture.html @@ -232,17 +232,25 @@

External Icons Demo

service s3(logos:aws-s3)[Cloud Store] service ec2(logos:aws-ec2)[Server] service api(logos:aws-api-gateway)[Api Gateway] + service fa(fa:image)[Font Awesome Icon] diff --git a/docs/config/setup/interfaces/mermaid.Mermaid.md b/docs/config/setup/interfaces/mermaid.Mermaid.md index 99a054caa7..f4e9eb4ec3 100644 --- a/docs/config/setup/interfaces/mermaid.Mermaid.md +++ b/docs/config/setup/interfaces/mermaid.Mermaid.md @@ -224,17 +224,17 @@ Used to register external diagram types. ### registerIconPacks -• **registerIconPacks**: (...`iconPacks`: `IconifyJSON`\[]) => `void` +• **registerIconPacks**: (`iconLoaders`: `IconLoader`\[]) => `void` #### Type declaration -▸ (`...iconPacks`): `void` +▸ (`iconLoaders`): `void` ##### Parameters -| Name | Type | -| :------------- | :--------------- | -| `...iconPacks` | `IconifyJSON`\[] | +| Name | Type | +| :------------ | :-------------- | +| `iconLoaders` | `IconLoader`\[] | ##### Returns diff --git a/docs/syntax/architecture.md b/docs/syntax/architecture.md index a45a9308bd..10d6b9aba7 100644 --- a/docs/syntax/architecture.md +++ b/docs/syntax/architecture.md @@ -197,42 +197,56 @@ By default, architecture diagram supports the following icons: `cloud`, `databas Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps below. The icon packs available can be found at [icones.js.org](https://icones.js.org/). +We use the name defined when registering the icon pack, to override the prefix field of the iconify pack. This allows the user to use shorter names for the icons. It also allows us to load a particular pack only when it is used in a diagram. Using JSON file directly from CDN: ```js import mermaid from 'CDN/mermaid.esm.mjs'; - -// You have to call `initialize` with startOnLoad:false before calling `registerIconPacks`, -// to prevent mermaid from starting before the icons are loaded -mermaid.initialize({ - startOnLoad: false, - logLevel: 0, -}); -const logos = await fetch('https://unpkg.com/@iconify-json/logos/icons.json'); -mermaid.registerIconPacks(await logos.json()); -mermaid.init(); +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => + fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()), + }, +]); ``` Using packages and a bundler: +```bash +npm install @iconify-json/logos +``` + +With lazy loading + ```js import mermaid from 'mermaid'; -// npm install @iconify-json/logos -import { icons as logos } from '@iconify-json/logos'; -mermaid.initialize({ - startOnLoad: false, - logLevel: 0, -}); -mermaid.registerIconPacks(logos); -mermaid.init(); +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => import('@iconify-json/logos').then((module) => module.icons), + }, +]); ``` -After the icons are installed, they can be used in the architecture diagram by using the format "prefix:icon-name", where prefix comes from the icon pack you selected. +Without lazy loading -```` -```mermaid +```js +import mermaid from 'mermaid'; +import { icons } from '@iconify-json/logos'; +mermaid.registerIconPacks([ + { + name: icons.prefix, // To use the prefix defined in the icon pack + icons, + }, +]); +``` + +After the icons are installed, they can be used in the architecture diagram by using the format "name:icon-name", where name is the value used when registering the icon pack. + +```mermaid-example architecture-beta group api(logos:aws-lambda)[API] @@ -245,29 +259,17 @@ architecture-beta disk1:T -- B:server disk2:T -- B:db ``` -```` - -
loading...
- + db:L -- R:server + disk1:T -- B:server + disk2:T -- B:db +``` diff --git a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts index 84b9ed6554..3abb69b9f7 100644 --- a/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts +++ b/packages/mermaid/src/diagrams/architecture/architectureRenderer.ts @@ -34,7 +34,12 @@ import { } from './architectureTypes.js'; import { drawEdges, drawGroups, drawJunctions, drawServices } from './svgDraw.js'; -registerIconPacks(architectureIcons); +registerIconPacks([ + { + name: architectureIcons.prefix, + icons: architectureIcons, + }, +]); cytoscape.use(fcose); function addServices(services: ArchitectureService[], cy: cytoscape.Core) { diff --git a/packages/mermaid/src/diagrams/architecture/svgDraw.ts b/packages/mermaid/src/diagrams/architecture/svgDraw.ts index d1c053f717..3578393949 100644 --- a/packages/mermaid/src/diagrams/architecture/svgDraw.ts +++ b/packages/mermaid/src/diagrams/architecture/svgDraw.ts @@ -212,7 +212,7 @@ export const drawGroups = async function (groupsEl: D3Element, cy: cytoscape.Cor if (data.icon) { const bkgElem = groupLabelContainer.append('g'); bkgElem.html( - `${getIconSVG(data.icon, { height: groupIconSize, width: groupIconSize, fallbackPrefix: architectureIcons.prefix })}` + `${await getIconSVG(data.icon, { height: groupIconSize, width: groupIconSize, fallbackPrefix: architectureIcons.prefix })}` ); bkgElem.attr( 'transform', @@ -297,11 +297,11 @@ export const drawServices = async function ( // throw new Error(`Invalid SVG Icon name: "${service.icon}"`); // } bkgElem.html( - `${getIconSVG(service.icon, { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}` + `${await getIconSVG(service.icon, { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}` ); } else if (service.iconText) { bkgElem.html( - `${getIconSVG('blank', { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}` + `${await getIconSVG('blank', { height: iconSize, width: iconSize, fallbackPrefix: architectureIcons.prefix })}` ); const textElemContainer = bkgElem.append('g'); const fo = textElemContainer diff --git a/packages/mermaid/src/docs/.vitepress/theme/Mermaid.vue b/packages/mermaid/src/docs/.vitepress/theme/Mermaid.vue index 5e0bbfa87e..b98c49348d 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/Mermaid.vue +++ b/packages/mermaid/src/docs/.vitepress/theme/Mermaid.vue @@ -86,9 +86,11 @@ onUnmounted(() => mut.disconnect()); const renderChart = async () => { console.log('rendering chart' + props.id + code.value); + const hasDarkClass = document.documentElement.classList.contains('dark'); const mermaidConfig = { securityLevel: 'loose', startOnLoad: false, + theme: hasDarkClass ? 'dark' : 'default', }; let svgCode = await render(props.id, code.value, mermaidConfig); // This is a hack to force v-html to re-render, otherwise the diagram disappears diff --git a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts index c4021d2cb1..2357fe384b 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/mermaid.ts @@ -2,29 +2,17 @@ import mermaid, { type MermaidConfig } from 'mermaid'; import zenuml from '../../../../../mermaid-zenuml/dist/mermaid-zenuml.core.mjs'; const init = mermaid.registerExternalDiagrams([zenuml]); +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => + fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()), + }, +]); export const render = async (id: string, code: string, config: MermaidConfig): Promise => { await init; - const hasDarkClass = document.documentElement.classList.contains('dark'); - const theme = hasDarkClass ? 'dark' : 'default'; - mermaid.initialize({ ...config, theme }); + mermaid.initialize(config); const { svg } = await mermaid.render(id, code); return svg; }; - -declare global { - interface Window { - mermaid: typeof mermaid; - render: typeof render; - } - - interface ImportMeta { - env: { - SSR: boolean; - }; - } -} -if (!import.meta.env.SSR) { - window.mermaid = mermaid; - window.render = render; -} diff --git a/packages/mermaid/src/docs/syntax/architecture.md b/packages/mermaid/src/docs/syntax/architecture.md index f4ff3bd722..0d199c8b15 100644 --- a/packages/mermaid/src/docs/syntax/architecture.md +++ b/packages/mermaid/src/docs/syntax/architecture.md @@ -159,42 +159,56 @@ By default, architecture diagram supports the following icons: `cloud`, `databas Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps below. The icon packs available can be found at [icones.js.org](https://icones.js.org/). +We use the name defined when registering the icon pack, to override the prefix field of the iconify pack. This allows the user to use shorter names for the icons. It also allows us to load a particular pack only when it is used in a diagram. Using JSON file directly from CDN: ```js import mermaid from 'CDN/mermaid.esm.mjs'; - -// You have to call `initialize` with startOnLoad:false before calling `registerIconPacks`, -// to prevent mermaid from starting before the icons are loaded -mermaid.initialize({ - startOnLoad: false, - logLevel: 0, -}); -const logos = await fetch('https://unpkg.com/@iconify-json/logos/icons.json'); -mermaid.registerIconPacks(await logos.json()); -mermaid.init(); +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => + fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()), + }, +]); ``` Using packages and a bundler: +```bash +npm install @iconify-json/logos +``` + +With lazy loading + ```js import mermaid from 'mermaid'; -// npm install @iconify-json/logos -import { icons as logos } from '@iconify-json/logos'; -mermaid.initialize({ - startOnLoad: false, - logLevel: 0, -}); -mermaid.registerIconPacks(logos); -mermaid.init(); +mermaid.registerIconPacks([ + { + name: 'logos', + loader: () => import('@iconify-json/logos').then((module) => module.icons), + }, +]); ``` -After the icons are installed, they can be used in the architecture diagram by using the format "prefix:icon-name", where prefix comes from the icon pack you selected. +Without lazy loading + +```js +import mermaid from 'mermaid'; +import { icons } from '@iconify-json/logos'; +mermaid.registerIconPacks([ + { + name: icons.prefix, // To use the prefix defined in the icon pack + icons, + }, +]); +``` -```` -```mermaid +After the icons are installed, they can be used in the architecture diagram by using the format "name:icon-name", where name is the value used when registering the icon pack. + +```mermaid-example architecture-beta group api(logos:aws-lambda)[API] @@ -207,29 +221,3 @@ architecture-beta disk1:T -- B:server disk2:T -- B:db ``` -```` - -
loading...
- - diff --git a/packages/mermaid/src/rendering-util/icons.ts b/packages/mermaid/src/rendering-util/icons.ts index 199bc4206e..27709b8229 100644 --- a/packages/mermaid/src/rendering-util/icons.ts +++ b/packages/mermaid/src/rendering-util/icons.ts @@ -3,21 +3,47 @@ import type { ExtendedIconifyIcon, IconifyIcon, IconifyJSON } from '@iconify/typ import type { IconifyIconCustomisations } from '@iconify/utils'; import { getIconData, iconToHTML, iconToSVG, replaceIDs, stringToIcon } from '@iconify/utils'; +interface AsyncIconLoader { + name: string; + loader: () => Promise; +} + +interface SyncIconLoader { + name: string; + icons: IconifyJSON; +} + +export type IconLoader = AsyncIconLoader | SyncIconLoader; + export const unknownIcon: IconifyIcon = { body: '?', height: 80, width: 80, }; -export const iconsStore = new Map(); +const iconsStore = new Map(); +const loaderStore = new Map(); -export const registerIconPacks = (...iconPacks: IconifyJSON[]) => { - for (const pack of iconPacks) { - iconsStore.set(pack.prefix, pack); +export const registerIconPacks = (iconLoaders: IconLoader[]) => { + for (const iconLoader of iconLoaders) { + if (!iconLoader.name) { + throw new Error( + 'Invalid icon loader. Must have a "name" property with non-empty string value.' + ); + } + log.debug('Registering icon pack:', iconLoader.name); + if ('loader' in iconLoader) { + loaderStore.set(iconLoader.name, iconLoader.loader); + } else if ('icons' in iconLoader) { + iconsStore.set(iconLoader.name, iconLoader.icons); + } else { + log.error('Invalid icon loader:', iconLoader); + throw new Error('Invalid icon loader. Must have either "icons" or "loader" property.'); + } } }; -const getRegisteredIconData = (iconName: string, fallbackPrefix?: string) => { +const getRegisteredIconData = async (iconName: string, fallbackPrefix?: string) => { const data = stringToIcon(iconName, true, fallbackPrefix !== undefined); if (!data) { throw new Error(`Invalid icon name: ${iconName}`); @@ -26,9 +52,20 @@ const getRegisteredIconData = (iconName: string, fallbackPrefix?: string) => { if (!prefix) { throw new Error(`Icon name must contain a prefix: ${iconName}`); } - const icons = iconsStore.get(prefix); + let icons = iconsStore.get(prefix); if (!icons) { - throw new Error(`Icon set not found: ${data.prefix}`); + const loader = loaderStore.get(prefix); + if (!loader) { + throw new Error(`Icon set not found: ${data.prefix}`); + } + try { + const loaded = await loader(); + icons = { ...loaded, prefix }; + iconsStore.set(prefix, icons); + } catch (e) { + log.error(e); + throw new Error(`Failed to load icon set: ${data.prefix}`); + } } const iconData = getIconData(icons, data.name); if (!iconData) { @@ -37,22 +74,22 @@ const getRegisteredIconData = (iconName: string, fallbackPrefix?: string) => { return iconData; }; -export const isIconAvailable = (iconName: string) => { +export const isIconAvailable = async (iconName: string) => { try { - getRegisteredIconData(iconName); + await getRegisteredIconData(iconName); return true; } catch { return false; } }; -export const getIconSVG = ( +export const getIconSVG = async ( iconName: string, customisations?: IconifyIconCustomisations & { fallbackPrefix?: string } ) => { let iconData: ExtendedIconifyIcon; try { - iconData = getRegisteredIconData(iconName, customisations?.fallbackPrefix); + iconData = await getRegisteredIconData(iconName, customisations?.fallbackPrefix); } catch (e) { log.error(e); iconData = unknownIcon; From e44cdbd79d8803022b56e952b37f0fd54a8e58be Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 2 Sep 2024 19:55:04 +0530 Subject: [PATCH 17/17] test: Add second icon pack --- cypress/platform/architecture-external.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cypress/platform/architecture-external.html b/cypress/platform/architecture-external.html index b76ac54ce5..71770425d6 100644 --- a/cypress/platform/architecture-external.html +++ b/cypress/platform/architecture-external.html @@ -20,6 +20,7 @@

External Icons Demo

service s3(logos:aws-s3)[Cloud Store] service ec2(logos:aws-ec2)[Server] service api(logos:aws-api-gateway)[Api Gateway] + service fa(fa:image)[Font Awesome Icon]