Compare commits

..

9 Commits

Author SHA1 Message Date
Sidharth Vinod
f8a0a61b5e Merge branch 'develop' into sidv/iconifyNative 2025-10-30 02:30:15 +09:00
Sidharth Vinod
525e630ebc fix: indentation 2025-10-30 02:01:45 +09:00
Sidharth Vinod
928ae32063 docs: Update icons documentation 2025-10-30 01:20:53 +09:00
Sidharth Vinod
25d96a90de Merge branch 'sidv/iconifyNative' of https://github.com/mermaid-js/mermaid into sidv/iconifyNative
* 'sidv/iconifyNative' of https://github.com/mermaid-js/mermaid:
  [autofix.ci] apply automated fixes
2025-10-29 22:08:39 +09:00
Sidharth Vinod
c24a1fb1b9 test: Add test for timeout 2025-10-29 22:08:18 +09:00
autofix-ci[bot]
c607163999 [autofix.ci] apply automated fixes 2025-10-29 08:50:25 +00:00
Sidharth Vinod
df21885a27 test: Visual tests for icons 2025-10-29 17:43:19 +09:00
Sidharth Vinod
172030377f feat: Add support for icons packs via config 2025-10-29 17:11:50 +09:00
Sidharth Vinod
a23c2baed8 refactor: Convert icon manager into class 2025-10-29 01:38:42 +09:00
139 changed files with 3529 additions and 7226 deletions

View File

@@ -28,6 +28,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [
'packet',
'architecture',
'radar',
'icons',
] as const;
/**

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Prevent HTML tags from being escaped in sandbox label rendering

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix(treemap): Fixed treemap classDef style application to properly apply user-defined styles

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
feat: add alias support for new participant syntax of sequence diagrams

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Support ComponentQueue_Ext to prevent parsing error

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: validate dates and tick interval to prevent UI freeze/crash in gantt diagramtype

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
fix: Mindmap rendering issue when the number of Level 2 nodes exceeds 11

View File

@@ -1,5 +1,3 @@
!viewbox
# It should be viewBox
# This file contains coding related terms
ALPHANUM
antiscript

View File

@@ -64,7 +64,6 @@ rscratch
shiki
Slidev
sparkline
speccharts
sphinxcontrib
ssim
stylis

View File

@@ -22,6 +22,7 @@ mermaidchart
mermaidjs
mindmap
mindmaps
mmdc
mrtree
multigraph
nodesep

View File

@@ -1,53 +0,0 @@
# Dev Explorer frontend: architecture + debugging notes
## Root cause of the “CSS changes do nothing” problem
The page loads `/dev/styles.css`, but **document-level CSS does not apply through a shadow DOM boundary**.
Historically, `dev-explorer-app` was a `LitElement` using Lits default `shadowRoot`, while the rest of the UI used light DOM. That meant:
- The browser showed the _right classes_ (`card`, `card-folder`, `card-file`) in Elements panel.
- `/dev/styles.css` was clearly being served/updated.
- Yet computed styles for `.card` looked like UA defaults because the selector never matched across the shadow root.
Fix: make `dev-explorer-app` light DOM too (`createRenderRoot() { return this; }`), so `/dev/styles.css` reliably styles the whole UI.
## Debugging traps (and fast detection)
- **Shadow DOM trap**
- Symptom: “CSS is loaded but doesnt apply”, especially for simple class selectors.
- Fast check:
- DevTools console: `document.querySelector('dev-explorer-app')?.shadowRoot`
- If non-null, global CSS wont style inside it.
- Or: right-click an element you expect styled → “Reveal in Elements” → see if its under `#shadow-root`.
- **“Light DOM child inside shadow DOM parent” trap**
- Even if a child component uses `createRenderRoot() { return this; }`,
if its _rendered inside the parents shadow root_, its still effectively in shadow for document styles.
- **Dev loop trap (CSS-only changes dont trigger reload)**
- The server watches TypeScript bundle inputs + `.mmd` files; static `/dev/styles.css` previously didnt emit SSE reload events.
- That makes CSS changes look flaky unless you manually refresh.
- Fix: watch `.esbuild/dev-explorer/public/**/*` and emit SSE on changes.
- **Caching trap (less common here, but real)**
- If a query param is constant (`?v=3`) and you dont reload, the browser can keep a cached stylesheet.
- Fast check: DevTools → Network → disable cache + hard reload; or check “(from disk cache)” on the CSS request.
## Styling strategy recommendation (pragmatic)
For a dev-only explorer, keep it simple:
- **Light DOM everywhere**
- **One stylesheet**: `.esbuild/dev-explorer/public/styles.css` served as `/dev/styles.css`
- **Scoped selectors** under `dev-explorer-app` to avoid generic class collisions (`.header`, `.content`, etc.)
If you later _want_ Shadow DOM isolation, do it deliberately:
- Put UI styles in Lit `static styles` or adopt a `CSSStyleSheet` into `this.renderRoot.adoptedStyleSheets`.
- Avoid relying on document CSS selectors for component internals.
## Shoelace integration notes
- Current setup is correct for dev: `setBasePath('/dev/vendor/shoelace')` and `registerIconLibrary(...)`.
- Prefer theming via CSS variables (Shoelace tokens) rather than overriding internal parts everywhere.

View File

@@ -1,187 +0,0 @@
import { LitElement, html } from 'lit';
import '@shoelace-style/shoelace/dist/components/input/input.js';
export type LogLevel = 'info' | 'warn' | 'error';
export type LogEntry = {
ts: number;
level: LogLevel;
message: string;
};
function formatTs(ts: number) {
const d = new Date(ts);
return (
d.toLocaleTimeString(undefined, { hour12: false }) +
'.' +
String(d.getMilliseconds()).padStart(3, '0')
);
}
function levelVariant(level: LogLevel) {
switch (level) {
case 'error':
return 'danger';
case 'warn':
return 'warning';
default:
return 'neutral';
}
}
type DisplayLevel = 'debug' | LogLevel;
function displayLevel(entry: LogEntry): DisplayLevel {
// Mermaid often emits debug lines through console.log/info with a marker.
if (entry.message.includes(': DEBUG :')) return 'debug';
return entry.level;
}
function displayVariant(level: DisplayLevel) {
switch (level) {
case 'error':
return 'danger';
case 'warn':
return 'warning';
case 'debug':
return 'success';
default:
return 'neutral';
}
}
export class DevConsolePanel extends LitElement {
static properties = {
logs: { state: true },
showInfo: { state: true },
showWarn: { state: true },
showError: { state: true },
showDebug: { state: true },
filterText: { state: true },
};
declare logs: LogEntry[];
declare showInfo: boolean;
declare showWarn: boolean;
declare showError: boolean;
declare showDebug: boolean;
declare filterText: string;
constructor() {
super();
this.logs = [];
this.showInfo = true;
this.showWarn = true;
this.showError = true;
this.showDebug = true;
this.filterText = '';
}
createRenderRoot() {
return this;
}
clear() {
this.logs = [];
}
append(entry: LogEntry) {
this.logs = [...this.logs, entry];
}
async copyVisible() {
const visible = this.filteredLogs();
const text = visible
.map((l) => `[${formatTs(l.ts)}] ${l.level.toUpperCase()} ${l.message}`)
.join('\n');
await navigator.clipboard.writeText(text);
}
filteredLogs() {
const q = this.filterText.trim().toLowerCase();
return this.logs.filter((l) => {
const isDebugLine = l.message.includes(': DEBUG :');
// Treat debug-marked lines as their own independent toggle, since Mermaid often routes them through
// console.log/info with a marker rather than a distinct "debug" level.
if (isDebugLine && !this.showDebug) return false;
if (!isDebugLine) {
const levelOk =
l.level === 'info' ? this.showInfo : l.level === 'warn' ? this.showWarn : this.showError;
if (!levelOk) return false;
}
if (!q) return true;
return l.message.toLowerCase().includes(q);
});
}
render() {
const visible = this.filteredLogs();
return html`
<div class="console">
<div class="console-toolbar">
<div class="spacer"></div>
<sl-input
size="small"
placeholder="filter…"
clearable
value=${this.filterText}
@sl-input=${(e: any) => (this.filterText = e.target.value ?? '')}
></sl-input>
<sl-checkbox
size="small"
?checked=${this.showDebug}
@sl-change=${(e: any) => (this.showDebug = e.target.checked)}
>debug</sl-checkbox
>
<sl-checkbox
size="small"
?checked=${this.showInfo}
@sl-change=${(e: any) => (this.showInfo = e.target.checked)}
>info</sl-checkbox
>
<sl-checkbox
size="small"
?checked=${this.showWarn}
@sl-change=${(e: any) => (this.showWarn = e.target.checked)}
>warn</sl-checkbox
>
<sl-checkbox
size="small"
?checked=${this.showError}
@sl-change=${(e: any) => (this.showError = e.target.checked)}
>error</sl-checkbox
>
<sl-button size="small" variant="default" @click=${() => void this.copyVisible()}>
<sl-icon slot="prefix" name="clipboard"></sl-icon>
Copy
</sl-button>
<sl-button size="small" variant="default" @click=${() => this.clear()}>
<sl-icon slot="prefix" name="trash"></sl-icon>
Clear
</sl-button>
</div>
<div class="console-body">
${visible.length === 0
? html`<div class="empty">No logs yet.</div>`
: visible.map((l) => {
const lvl = displayLevel(l);
return html`
<div class="logline">
<div class="logmeta">
<sl-badge variant=${displayVariant(lvl)}>${lvl}</sl-badge>
<span class="path">${formatTs(l.ts)}</span>
</div>
<div>${l.message}</div>
</div>
`;
})}
</div>
</div>
`;
}
}
customElements.define('dev-console-panel', DevConsolePanel);

View File

@@ -1,551 +0,0 @@
import { LitElement, html, nothing } from 'lit';
import '@shoelace-style/shoelace/dist/components/button/button.js';
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import '@shoelace-style/shoelace/dist/components/select/select.js';
import '@shoelace-style/shoelace/dist/components/option/option.js';
import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js';
import '@shoelace-style/shoelace/dist/components/split-panel/split-panel.js';
import './console-panel';
import type { LogEntry, LogLevel } from './console-panel';
type MermaidIife = {
initialize: (config: Record<string, unknown>) => void | Promise<void>;
render: (
id: string,
text: string,
container?: Element
) => Promise<{ svg: string; bindFunctions?: (el: Element) => void }>;
};
declare global {
interface Window {
mermaid?: MermaidIife;
mermaidReady?: Promise<MermaidIife>;
}
}
function stringifyArgs(args: unknown[]) {
// Mermaid's internal logger frequently uses console formatting like:
// console.log('%c...message...', 'color: lightgreen', ...)
// For the log panel we want the human text, not the formatting tokens/styles.
let normalized = [...args];
if (typeof normalized[0] === 'string') {
const fmt = normalized[0];
const cssCount = (fmt.match(/%c/g) ?? []).length;
if (cssCount > 0) {
normalized[0] = fmt.replaceAll('%c', '');
// Drop the corresponding CSS args that follow the format string.
normalized.splice(1, cssCount);
}
}
return normalized
.map((a) => {
if (typeof a === 'string') return a;
if (a instanceof Error) return a.stack ?? a.message;
try {
return JSON.stringify(a);
} catch {
return String(a);
}
})
.join(' ')
.replace(/\s+/g, ' ')
.trim();
}
type MermaidTheme = 'default' | 'dark' | 'forest' | 'neutral' | 'base';
type MermaidLayout = 'dagre' | 'elk';
type MermaidLogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
const DEFAULT_THEME: MermaidTheme = 'default';
const DEFAULT_LAYOUT: MermaidLayout = 'dagre';
const DEFAULT_MERMAID_LOG_LEVEL: MermaidLogLevel = 'warn';
function readUrlParam(name: string) {
try {
return new URL(window.location.href).searchParams.get(name);
} catch {
return null;
}
}
function setUrlParams(pairs: Record<string, string | null | undefined>) {
const url = new URL(window.location.href);
for (const [k, v] of Object.entries(pairs)) {
if (!v) url.searchParams.delete(k);
else url.searchParams.set(k, v);
}
history.replaceState(null, '', url);
}
function readStorage(key: string) {
try {
return localStorage.getItem(key);
} catch {
return null;
}
}
function writeStorage(key: string, value: string) {
try {
localStorage.setItem(key, value);
} catch {
// ignore
}
}
function isTheme(v: unknown): v is MermaidTheme {
return v === 'default' || v === 'dark' || v === 'forest' || v === 'neutral' || v === 'base';
}
function isLayout(v: unknown): v is MermaidLayout {
return v === 'dagre' || v === 'elk';
}
function isMermaidLogLevel(v: unknown): v is MermaidLogLevel {
return (
v === 'trace' || v === 'debug' || v === 'info' || v === 'warn' || v === 'error' || v === 'fatal'
);
}
function normalizeLayout(v: unknown): MermaidLayout | null {
// Back-compat:
// - older UI used `renderer=dagre-d3|dagre-wrapper|elk`
// - new UI uses `layout=dagre|elk`
if (v === 'dagre' || v === 'elk') return v;
if (v === 'dagre-d3' || v === 'dagre-wrapper') return 'dagre';
return null;
}
function parseBoolean(v: unknown): boolean | null {
if (typeof v !== 'string') return null;
const s = v.trim().toLowerCase();
if (['1', 'true', 'yes', 'on'].includes(s)) return true;
if (['0', 'false', 'no', 'off'].includes(s)) return false;
return null;
}
export class DevDiagramViewer extends LitElement {
static properties = {
filePath: { type: String },
sseToken: { type: Number },
theme: { state: true },
layout: { state: true },
mermaidLogLevel: { state: true },
useMaxWidth: { state: true },
loading: { state: true },
error: { state: true },
source: { state: true },
svg: { state: true },
};
declare filePath: string;
declare sseToken: number;
declare theme: MermaidTheme;
declare layout: MermaidLayout;
declare mermaidLogLevel: MermaidLogLevel;
declare useMaxWidth: boolean;
declare loading: boolean;
declare error: string;
declare source: string;
declare svg: string;
#renderSeq = 0;
#consolePatched = false;
#originalConsole?: {
log: typeof console.log;
info: typeof console.info;
debug: typeof console.debug;
warn: typeof console.warn;
error: typeof console.error;
};
constructor() {
super();
const themeParam = readUrlParam('theme');
const layoutParam = readUrlParam('layout');
const rendererParam = readUrlParam('renderer'); // legacy
const logParam = readUrlParam('logLevel');
const useMaxWidthParam = readUrlParam('useMaxWidth');
const storedTheme = readStorage('devExplorer.viewer.theme');
const storedLayout = readStorage('devExplorer.viewer.layout');
const storedRenderer = readStorage('devExplorer.viewer.renderer'); // legacy
const storedLog = readStorage('devExplorer.viewer.logLevel');
const storedUseMaxWidth = readStorage('devExplorer.viewer.useMaxWidth');
this.theme = isTheme(themeParam)
? themeParam
: isTheme(storedTheme)
? storedTheme
: DEFAULT_THEME;
this.layout =
normalizeLayout(layoutParam) ??
normalizeLayout(rendererParam) ??
normalizeLayout(storedLayout) ??
normalizeLayout(storedRenderer) ??
DEFAULT_LAYOUT;
this.mermaidLogLevel = isMermaidLogLevel(logParam)
? logParam
: isMermaidLogLevel(storedLog)
? storedLog
: DEFAULT_MERMAID_LOG_LEVEL;
this.useMaxWidth = parseBoolean(useMaxWidthParam) ?? parseBoolean(storedUseMaxWidth) ?? true;
this.filePath = '';
this.sseToken = 0;
this.loading = true;
this.error = '';
this.source = '';
this.svg = '';
}
createRenderRoot() {
return this;
}
connectedCallback() {
super.connectedCallback();
this.#installConsoleCapture();
}
disconnectedCallback() {
super.disconnectedCallback();
this.#restoreConsoleCapture();
}
updated(changed: Map<string, unknown>) {
if (changed.has('filePath')) {
void this.#loadAndRender();
} else if (changed.has('sseToken')) {
// On rebuild events, re-fetch + re-render the currently open diagram.
if (this.filePath) void this.#loadAndRender();
} else if (
changed.has('theme') ||
changed.has('layout') ||
changed.has('mermaidLogLevel') ||
changed.has('useMaxWidth')
) {
// Re-render the currently loaded diagram with the new config without refetching.
if (this.source) void this.#renderCurrentSource();
}
}
#back() {
this.dispatchEvent(new CustomEvent('back', { bubbles: true, composed: true }));
}
#persistSettings() {
writeStorage('devExplorer.viewer.theme', this.theme);
writeStorage('devExplorer.viewer.layout', this.layout);
writeStorage('devExplorer.viewer.logLevel', this.mermaidLogLevel);
writeStorage('devExplorer.viewer.useMaxWidth', String(this.useMaxWidth));
setUrlParams({
theme: this.theme,
layout: this.layout,
renderer: null, // drop legacy param
logLevel: this.mermaidLogLevel,
useMaxWidth: this.useMaxWidth ? '1' : '0',
});
}
#syncConsolePanelFilters() {
const panel = this.querySelector('dev-console-panel') as any;
if (!panel) return;
// This is intentionally opinionated: less noise by default as logLevel increases.
if (
this.mermaidLogLevel === 'trace' ||
this.mermaidLogLevel === 'debug' ||
this.mermaidLogLevel === 'info'
) {
panel.showInfo = true;
panel.showWarn = true;
panel.showError = true;
return;
}
if (this.mermaidLogLevel === 'warn') {
panel.showInfo = false;
panel.showWarn = true;
panel.showError = true;
return;
}
// error / fatal
panel.showInfo = false;
panel.showWarn = false;
panel.showError = true;
}
#appendLog(entry: LogEntry) {
const panel = this.querySelector('dev-console-panel') as any;
panel?.append?.(entry);
}
#installConsoleCapture() {
if (this.#consolePatched) return;
this.#consolePatched = true;
this.#originalConsole = {
log: console.log,
info: console.info,
debug: console.debug,
warn: console.warn,
error: console.error,
};
const capture = (level: LogLevel, args: unknown[]) => {
this.#appendLog({
ts: Date.now(),
level,
message: stringifyArgs(args),
});
};
// Mermaid uses its own logger which routes to console.info/debug/warn/error.
// Capture those too (map debug/info/log -> panel "info").
console.log = (...args) => {
capture('info', args);
this.#originalConsole!.log.apply(console, args as any);
};
console.info = (...args) => {
capture('info', args);
this.#originalConsole!.info.apply(console, args as any);
};
console.debug = (...args) => {
capture('info', args);
this.#originalConsole!.debug.apply(console, args as any);
};
console.warn = (...args) => {
capture('warn', args);
this.#originalConsole!.warn.apply(console, args as any);
};
console.error = (...args) => {
capture('error', args);
this.#originalConsole!.error.apply(console, args as any);
};
}
#restoreConsoleCapture() {
if (!this.#consolePatched) return;
this.#consolePatched = false;
if (!this.#originalConsole) return;
console.log = this.#originalConsole.log;
console.info = this.#originalConsole.info;
console.debug = this.#originalConsole.debug;
console.warn = this.#originalConsole.warn;
console.error = this.#originalConsole.error;
this.#originalConsole = undefined;
}
#clearLogs() {
const panel = this.querySelector('dev-console-panel') as any;
panel?.clear?.();
}
async #fetchSource() {
const url = new URL('/dev/api/file', window.location.origin);
url.searchParams.set('path', this.filePath);
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.text();
}
async #loadAndRender() {
const seq = ++this.#renderSeq;
this.loading = true;
this.error = '';
this.svg = '';
this.#clearLogs();
this.#syncConsolePanelFilters();
try {
const source = await this.#fetchSource();
if (seq !== this.#renderSeq) return;
this.source = source;
await this.#renderMermaid(source);
} catch (e) {
this.error = e instanceof Error ? e.message : String(e);
} finally {
this.loading = false;
}
}
async #renderCurrentSource() {
const seq = ++this.#renderSeq;
this.loading = true;
this.error = '';
this.svg = '';
this.#clearLogs();
this.#syncConsolePanelFilters();
try {
const source = this.source;
if (!source) return;
if (seq !== this.#renderSeq) return;
await this.#renderMermaid(source);
} catch (e) {
this.error = e instanceof Error ? e.message : String(e);
} finally {
this.loading = false;
}
}
async #renderMermaid(text: string) {
const m = (await window.mermaidReady?.catch(() => undefined)) ?? window.mermaid;
if (!m) {
throw new Error(
'window.mermaid is not available (did /mermaid.esm.mjs load and did the bootstrap set window.mermaid?)'
);
}
const initConfig = {
startOnLoad: false,
securityLevel: 'strict',
theme: this.theme,
layout: this.layout,
logLevel: this.mermaidLogLevel,
flowchart: {
useMaxWidth: this.useMaxWidth,
},
};
// Debugging aid: log exactly what we are about to initialize/render with.
// Do it *before* initialize so detector issues can be correlated.
const previewLimit = 4000;
const preview =
text.length > previewLimit
? `${text.slice(0, previewLimit)}\n… (${text.length - previewLimit} more chars)`
: text;
console.log('[dev-explorer] mermaid.initialize config:', initConfig);
console.log('[dev-explorer] diagram source preview:\n' + preview);
// Keep it deterministic-ish between reloads.
await m.initialize(initConfig);
const id = `dev-explorer-${Date.now()}-${Math.random().toString(16).slice(2)}`;
const { svg, bindFunctions } = await m.render(id, text);
this.svg = svg;
// Allow mermaid to attach event handlers (e.g. links).
await this.updateComplete;
// If the page ever ended up scrolled down due to a previous oversized render, snap back to top.
// (We intentionally removed vertical scrollbars in the viewer.)
try {
window.scrollTo(0, 0);
} catch {
// ignore
}
const container = this.querySelector('.diagram-inner');
if (container && bindFunctions) bindFunctions(container);
}
render() {
return html`
<div class="header">
<sl-button size="small" variant="default" @click=${() => this.#back()}>
<sl-icon slot="prefix" name="arrow-left"></sl-icon>
Back
</sl-button>
<div style="min-width: 0;">
<div class="title">Diagram</div>
<div class="path">${this.filePath}</div>
</div>
<div class="spacer"></div>
<div class="viewer-controls">
<div class="control">
<span class="label">Theme</span>
<sl-select
size="small"
value=${this.theme}
@sl-change=${(e: any) => {
const v = e.target?.value;
if (isTheme(v)) {
this.theme = v;
this.#persistSettings();
}
}}
>
<sl-option value="default">default</sl-option>
<sl-option value="dark">dark</sl-option>
<sl-option value="forest">forest</sl-option>
<sl-option value="neutral">neutral</sl-option>
<sl-option value="base">base</sl-option>
</sl-select>
</div>
<div class="control">
<span class="label">Layout</span>
<sl-select
size="small"
value=${this.layout}
@sl-change=${(e: any) => {
const v = e.target?.value;
if (isLayout(v)) {
this.layout = v;
this.#persistSettings();
}
}}
>
<sl-option value="dagre">dagre</sl-option>
<sl-option value="elk">elk</sl-option>
</sl-select>
</div>
<div class="control">
<span class="label">Log</span>
<sl-select
size="small"
value=${this.mermaidLogLevel}
@sl-change=${(e: any) => {
const v = e.target?.value;
if (isMermaidLogLevel(v)) {
this.mermaidLogLevel = v;
this.#persistSettings();
this.#syncConsolePanelFilters();
}
}}
>
<sl-option value="trace">trace</sl-option>
<sl-option value="debug">debug</sl-option>
<sl-option value="info">info</sl-option>
<sl-option value="warn">warn</sl-option>
<sl-option value="error">error</sl-option>
<sl-option value="fatal">fatal</sl-option>
</sl-select>
</div>
<div class="control">
<sl-checkbox
size="small"
?checked=${this.useMaxWidth}
@sl-change=${(e: any) => {
this.useMaxWidth = Boolean(e.target?.checked);
this.#persistSettings();
}}
>useMaxWidth</sl-checkbox
>
</div>
</div>
${this.loading ? html`<div class="subtle">rendering…</div>` : nothing}
</div>
${this.error
? html`<div class="empty">Error: <span class="path">${this.error}</span></div>`
: nothing}
<div class="content">
<sl-split-panel position="75" style="height: 100%;">
<div slot="start" class="diagram">
<div class="diagram-inner" data-theme=${this.theme} .innerHTML=${this.svg}></div>
</div>
<div slot="end" style="height: 100%;">
<dev-console-panel></dev-console-panel>
</div>
</sl-split-panel>
</div>
`;
}
}
customElements.define('dev-diagram-viewer', DevDiagramViewer);

View File

@@ -1,143 +0,0 @@
import { LitElement, html, nothing } from 'lit';
import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.js';
import { registerIconLibrary } from '@shoelace-style/shoelace/dist/utilities/icon-library.js';
import '@shoelace-style/shoelace/dist/components/breadcrumb/breadcrumb.js';
import '@shoelace-style/shoelace/dist/components/breadcrumb-item/breadcrumb-item.js';
import '@shoelace-style/shoelace/dist/components/button/button.js';
import '@shoelace-style/shoelace/dist/components/icon/icon.js';
import '@shoelace-style/shoelace/dist/components/split-panel/split-panel.js';
import '@shoelace-style/shoelace/dist/components/badge/badge.js';
import '@shoelace-style/shoelace/dist/components/checkbox/checkbox.js';
import './file-explorer';
import './diagram-viewer';
type ViewMode = 'explorer' | 'viewer';
function getInitialStateFromUrl() {
const url = new URL(window.location.href);
const dir = url.searchParams.get('path') ?? '';
const file = url.searchParams.get('file') ?? '';
return { dir, file };
}
function setUrlState({ dir, file }: { dir: string; file: string }) {
const url = new URL(window.location.href);
url.searchParams.delete('path');
url.searchParams.delete('file');
if (dir) url.searchParams.set('path', dir);
if (file) url.searchParams.set('file', file);
history.replaceState(null, '', url);
}
export class DevExplorerApp extends LitElement {
static properties = {
mode: { state: true },
dirPath: { state: true },
lastDirPath: { state: true },
filePath: { state: true },
sseToken: { state: true },
};
declare mode: ViewMode;
declare dirPath: string;
declare lastDirPath: string;
declare filePath: string;
declare sseToken: number;
#events?: EventSource;
constructor() {
super();
const { dir, file } = getInitialStateFromUrl();
this.dirPath = dir;
this.lastDirPath = dir;
this.filePath = file;
this.mode = file ? 'viewer' : 'explorer';
this.sseToken = 0;
setBasePath('/dev/vendor/shoelace');
registerIconLibrary('default', {
resolver: (name) => `/dev/vendor/shoelace/assets/icons/${name}.svg`,
});
}
createRenderRoot() {
// Use light DOM so document-level CSS (public/styles.css => /dev/styles.css) applies to the UI.
// Without this, Lit's default shadow root will block global selectors like `.list` / `button.card`.
return this;
}
connectedCallback() {
super.connectedCallback();
this.#events = new EventSource('/events');
this.#events.onmessage = () => {
this.sseToken++;
};
window.addEventListener('popstate', this.#onPopState);
}
disconnectedCallback() {
super.disconnectedCallback();
this.#events?.close();
window.removeEventListener('popstate', this.#onPopState);
}
#onPopState = () => {
const { dir, file } = getInitialStateFromUrl();
this.dirPath = dir;
this.lastDirPath = dir;
this.filePath = file;
this.mode = file ? 'viewer' : 'explorer';
};
#goToDir = (dir: string) => {
this.dirPath = dir;
this.lastDirPath = dir;
this.mode = 'explorer';
this.filePath = '';
setUrlState({ dir, file: '' });
};
#openFile = (filePath: string) => {
this.filePath = filePath;
this.mode = 'viewer';
setUrlState({ dir: this.lastDirPath, file: filePath });
};
#backToExplorer = () => {
this.mode = 'explorer';
this.filePath = '';
setUrlState({ dir: this.lastDirPath, file: '' });
};
render() {
return html`
<div class="app">
${this.mode === 'explorer'
? html`
<dev-file-explorer
.path=${this.dirPath}
.sseToken=${this.sseToken}
@navigate=${(e: CustomEvent<{ path: string }>) => this.#goToDir(e.detail.path)}
@open-file=${(e: CustomEvent<{ path: string }>) => this.#openFile(e.detail.path)}
></dev-file-explorer>
`
: nothing}
${this.mode === 'viewer'
? html`
<dev-diagram-viewer
.filePath=${this.filePath}
.sseToken=${this.sseToken}
@back=${this.#backToExplorer}
></dev-diagram-viewer>
`
: nothing}
</div>
`;
}
}
customElements.define('dev-explorer-app', DevExplorerApp);

View File

@@ -1,182 +0,0 @@
import { LitElement, html, nothing } from 'lit';
type Entry = {
name: string;
kind: 'dir' | 'file';
path: string;
};
type FilesResponse = {
root: string;
path: string;
entries: Entry[];
};
function dirname(posixPath: string) {
const p = posixPath.replaceAll('\\', '/').replace(/\/+$/, '');
if (!p) return '';
const idx = p.lastIndexOf('/');
if (idx <= 0) return '';
return p.slice(0, idx);
}
function pathSegments(posixPath: string) {
const p = posixPath.replaceAll('\\', '/').replace(/^\/+/, '').replace(/\/+$/, '');
if (!p) return [];
return p.split('/').filter(Boolean);
}
export class DevFileExplorer extends LitElement {
static properties = {
path: { type: String },
sseToken: { type: Number },
loading: { state: true },
error: { state: true },
root: { state: true },
entries: { state: true },
};
declare path: string;
declare sseToken: number;
declare loading: boolean;
declare error: string;
declare root: string;
declare entries: Entry[];
constructor() {
super();
this.path = '';
this.sseToken = 0;
this.loading = true;
this.error = '';
this.root = '';
this.entries = [];
}
createRenderRoot() {
// Use light DOM so global CSS in public/styles.css applies.
return this;
}
updated(changed: Map<string, unknown>) {
if (changed.has('path') || changed.has('sseToken')) {
void this.#load();
}
}
async #load() {
this.loading = true;
this.error = '';
try {
const url = new URL('/dev/api/files', window.location.origin);
if (this.path) url.searchParams.set('path', this.path);
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = (await res.json()) as FilesResponse;
this.root = json.root ?? '';
this.entries = json.entries ?? [];
} catch (e) {
this.error = e instanceof Error ? e.message : String(e);
this.entries = [];
} finally {
this.loading = false;
}
}
#emitNavigate(nextPath: string) {
this.dispatchEvent(
new CustomEvent('navigate', { detail: { path: nextPath }, bubbles: true, composed: true })
);
}
#emitOpenFile(filePath: string) {
this.dispatchEvent(
new CustomEvent('open-file', { detail: { path: filePath }, bubbles: true, composed: true })
);
}
#onActivate(kind: Entry['kind'], entryPath: string) {
if (kind === 'dir') {
this.#emitNavigate(entryPath);
} else {
this.#emitOpenFile(entryPath);
}
}
render() {
const segments = pathSegments(this.path);
const itemLabel = this.entries.length === 1 ? 'item' : 'items';
return html`
<div class="header">
<div style="min-width: 0;">
<div class="title">Dev Explorer</div>
<div class="subtle">
root:
<span class="path">${this.root || 'cypress/platform/dev-diagrams'}</span>
</div>
<div style="margin-top: 6px;">
<sl-breadcrumb>
<sl-breadcrumb-item @click=${() => this.#emitNavigate('')}>root</sl-breadcrumb-item>
${segments.map((seg, idx) => {
const to = segments.slice(0, idx + 1).join('/');
return html`<sl-breadcrumb-item @click=${() => this.#emitNavigate(to)}
>${seg}</sl-breadcrumb-item
>`;
})}
</sl-breadcrumb>
</div>
</div>
<div class="spacer"></div>
<div class="subtle">
${this.loading ? 'loading…' : html`<span>${this.entries.length} ${itemLabel}</span>`}
</div>
<sl-button
size="small"
variant="default"
?disabled=${!this.path}
@click=${() => this.#emitNavigate(dirname(this.path))}
>
<sl-icon slot="prefix" name="arrow-left"></sl-icon>
Up
</sl-button>
</div>
<div class="content">
${this.error
? html`<div class="empty">Error: <span class="path">${this.error}</span></div>`
: nothing}
${!this.error && !this.loading && this.entries.length === 0
? html`<div class="empty">No folders or <span class="path">.mmd</span> files here.</div>`
: nothing}
<div class="list">
${this.entries.map((e) => {
const icon = e.kind === 'dir' ? 'folder-fill' : 'file-earmark-code';
const cardClass = e.kind === 'dir' ? 'card card-folder' : 'card card-file';
const click =
e.kind === 'dir'
? () => this.#emitNavigate(e.path)
: () => this.#emitOpenFile(e.path);
const onKeyDown = (ev: KeyboardEvent) => {
if (ev.key === 'Enter' || ev.key === ' ') {
ev.preventDefault();
this.#onActivate(e.kind, e.path);
}
};
return html`
<button class=${cardClass} type="button" @click=${click} @keydown=${onKeyDown}>
<div class="card-inner">
<sl-icon class="card-icon" name=${icon}></sl-icon>
<div class="card-title">${e.name}</div>
</div>
</button>
`;
})}
</div>
</div>
`;
}
}
customElements.define('dev-file-explorer', DevFileExplorer);

View File

@@ -1,39 +0,0 @@
<!doctype html>
<html lang="en" class="sl-theme-dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Mermaid Dev Explorer</title>
<link rel="stylesheet" href="/dev/vendor/shoelace/themes/dark.css" />
<link rel="stylesheet" href="/dev/styles.css?v=6" />
</head>
<body>
<!-- Mermaid ESM + ELK layout loaders (required for `layout: 'elk'`) -->
<script type="module">
// Expose a single async hook so the app can reliably await Mermaid "activation".
// This avoids races where the UI calls initialize/render before layouts/diagrams are ready.
window.mermaidReady = (async () => {
try {
const [{ default: mermaid }, { default: layouts }] = await Promise.all([
import('/mermaid.esm.mjs'),
import('/mermaid-layout-elk.esm.mjs'),
]);
mermaid.registerLayoutLoaders(layouts);
// Keep the rest of the dev explorer simple: expose mermaid on window for the Lit components.
window.mermaid = mermaid;
return mermaid;
} catch (err) {
console.error('[dev-explorer] Failed to initialize mermaid (ESM + elk loaders).', err);
throw err;
}
})();
</script>
<dev-explorer-app></dev-explorer-app>
<script type="module" src="/dev/assets/explorer-app.js?v=6"></script>
</body>
</html>

View File

@@ -1,359 +0,0 @@
/* Dev Explorer tokens. Keep on :root so the page background picks them up too. */
:root {
--app-bg: #0b1020;
--app-fg: #e8eefc;
--panel-bg: #0f1733;
--muted: rgba(232, 238, 252, 0.75);
--border: rgba(232, 238, 252, 0.12);
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
monospace;
}
html,
body {
height: 100%;
}
body {
margin: 0;
background: var(--app-bg);
color: var(--app-fg);
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
/* Keep the page from becoming scrollable when components accidentally overflow. */
overflow: hidden;
}
a {
color: inherit;
}
/* Scope app-level layout rules to avoid generic class collisions. */
dev-explorer-app {
display: block;
height: 100%;
}
dev-explorer-app .app {
height: 100vh;
display: flex;
flex-direction: column;
}
/* Let the top-level view components actually fill the available vertical space. */
dev-explorer-app dev-file-explorer,
dev-explorer-app dev-diagram-viewer {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
}
dev-explorer-app .header {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
background: var(--panel-bg);
}
dev-explorer-app .viewer-controls {
display: inline-flex;
align-items: center;
gap: 10px;
margin-right: 8px;
}
dev-explorer-app .viewer-controls .control {
display: inline-flex;
align-items: center;
gap: 6px;
}
dev-explorer-app .viewer-controls .label {
font-size: 12px;
color: var(--muted);
user-select: none;
}
dev-explorer-app .viewer-controls sl-select {
width: 140px;
}
dev-explorer-app .header .spacer {
flex: 1;
}
dev-explorer-app .title {
font-weight: 600;
letter-spacing: 0.2px;
}
dev-explorer-app .subtle {
color: var(--muted);
font-size: 12px;
}
dev-explorer-app .content {
flex: 1;
min-height: 0;
overflow: auto;
}
/* Diagram view should behave like a full-height canvas; avoid nested scrollbars. */
dev-explorer-app dev-diagram-viewer .content {
overflow: hidden;
}
/* Shoelace split panel: ensure slot content can't overflow and push itself off-screen. */
dev-explorer-app sl-split-panel {
height: 100%;
overflow: hidden;
}
dev-explorer-app sl-split-panel::part(base) {
height: 100%;
overflow: hidden;
}
dev-explorer-app sl-split-panel::part(start),
dev-explorer-app sl-split-panel::part(end) {
overflow: hidden;
min-height: 0;
}
dev-explorer-app .list {
padding: 24px;
display: grid;
gap: 20px;
grid-template-columns: repeat(auto-fill, 260px);
}
/* Card base - use button element */
dev-explorer-app button.card {
all: unset;
box-sizing: border-box;
width: 260px;
height: 200px;
border-radius: 20px;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
/* Folder card style */
dev-explorer-app button.card.card-folder {
background: linear-gradient(160deg, #1a3052 0%, #0d1a2d 100%);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
0 2px 8px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
dev-explorer-app button.card.card-folder:hover {
transform: translateY(-6px) scale(1.02);
box-shadow:
0 16px 48px rgba(0, 0, 0, 0.5),
0 4px 12px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
/* File card style */
dev-explorer-app button.card.card-file {
background: linear-gradient(160deg, #1e2d4a 0%, #111827 100%);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
0 2px 8px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
dev-explorer-app button.card.card-file:hover {
transform: translateY(-6px) scale(1.02);
box-shadow:
0 16px 48px rgba(0, 0, 0, 0.5),
0 4px 12px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
dev-explorer-app button.card:focus-visible {
outline: 3px solid rgba(96, 165, 250, 0.7);
outline-offset: 4px;
}
/* Subtle border overlay */
dev-explorer-app button.card::before {
content: '';
position: absolute;
inset: 0;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.08);
pointer-events: none;
}
dev-explorer-app .card-inner {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
padding: 24px;
width: 100%;
height: 100%;
box-sizing: border-box;
}
/* Icon styling */
dev-explorer-app .card-icon {
font-size: 56px !important;
width: 56px;
height: 56px;
}
dev-explorer-app button.card.card-folder .card-icon {
color: #f59e0b;
filter: drop-shadow(0 4px 12px rgba(245, 158, 11, 0.4));
}
dev-explorer-app button.card.card-file .card-icon {
color: #3b82f6;
filter: drop-shadow(0 4px 12px rgba(59, 130, 246, 0.35));
}
/* Title styling */
dev-explorer-app .card-title {
font-size: 12px;
font-family: var(--mono);
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 230px;
color: rgba(255, 255, 255, 0.85);
line-height: 1.4;
text-align: center;
}
dev-explorer-app .row {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 8px;
border: 1px solid var(--border);
border-radius: 8px;
background: rgba(255, 255, 255, 0.02);
}
dev-explorer-app .row:hover {
background: rgba(255, 255, 255, 0.04);
}
dev-explorer-app .row sl-icon {
font-size: 16px;
}
dev-explorer-app .path {
font-family: var(--mono);
font-size: 12px;
color: var(--muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
dev-explorer-app .diagram {
height: 100%;
overflow: hidden;
padding: 12px;
box-sizing: border-box;
}
dev-explorer-app .diagram-inner {
/* Canvas background behind the rendered SVG (theme-dependent). */
background: #fff;
color: #111;
border-radius: 10px;
padding: 12px;
display: flex;
align-items: flex-start;
justify-content: flex-start;
width: 100%;
height: 100%;
box-sizing: border-box;
}
/* Fit the rendered SVG within the available pane (no scrollbars). */
dev-explorer-app .diagram-inner > svg {
width: auto;
height: auto;
max-width: 100% !important;
max-height: 100% !important;
}
dev-explorer-app .diagram-inner[data-theme='dark'] {
background: #0b1020;
color: #e8eefc;
}
dev-explorer-app .console {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
border-left: 1px solid var(--border);
background: var(--panel-bg);
}
dev-explorer-app .spacer {
flex: 1;
}
dev-explorer-app .console-toolbar {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
}
dev-explorer-app .console-toolbar sl-input {
width: 260px;
}
dev-explorer-app .console-body {
flex: 1;
min-height: 0;
overflow: auto;
padding: 10px 12px;
}
dev-explorer-app .logline {
font-family: var(--mono);
font-size: 12px;
white-space: pre-wrap;
word-break: break-word;
margin: 0 0 8px 0;
line-height: 1.35;
}
dev-explorer-app .logmeta {
display: inline-flex;
gap: 8px;
align-items: center;
margin-bottom: 4px;
}
dev-explorer-app .empty {
padding: 18px 12px;
color: var(--muted);
font-size: 13px;
}
dev-explorer-app sl-breadcrumb::part(base) {
font-size: 12px;
}

View File

@@ -2,17 +2,12 @@
import chokidar from 'chokidar';
import cors from 'cors';
import { context } from 'esbuild';
import { promises as fs } from 'fs';
import type { Request, Response } from 'express';
import express from 'express';
import path, { resolve } from 'path';
import { fileURLToPath } from 'url';
import { packageOptions } from '../.build/common.js';
import { generateLangium } from '../.build/generateLangium.js';
import { defaultOptions, getBuildConfig } from './util.js';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
const configs = Object.values(packageOptions).map(({ packageName }) =>
getBuildConfig({
...defaultOptions,
@@ -23,7 +18,7 @@ const configs = Object.values(packageOptions).map(({ packageName }) =>
);
const mermaidIIFEConfig = getBuildConfig({
...defaultOptions,
minify: true,
minify: false,
core: false,
options: packageOptions.mermaid,
format: 'iife',
@@ -89,81 +84,6 @@ function sendEventsToAll() {
clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`));
}
interface DevExplorerEntry {
name: string;
kind: 'dir' | 'file';
path: string; // posix-style, relative to root
}
const devExplorerRootAbs = resolve(
process.cwd(),
process.env.MERMAID_DEV_EXPLORER_ROOT ?? 'cypress/platform/dev-diagrams'
);
function toPosixPath(p: string) {
return p.split(path.sep).join('/');
}
function resolveWithinDevExplorerRoot(requestedPath: unknown) {
const requested = typeof requestedPath === 'string' ? requestedPath : '';
if (requested.includes('\0')) {
throw new Error('Invalid path');
}
// Normalize slashes and avoid weird absolute-path cases.
const cleaned = requested.replaceAll('\\', '/').replace(/^\/+/, '');
const absPath = resolve(devExplorerRootAbs, cleaned);
const rel = path.relative(devExplorerRootAbs, absPath);
// Prevent traversal above root.
if (rel.startsWith('..') || path.isAbsolute(rel)) {
throw new Error('Path escapes configured root');
}
return { absPath, relPath: toPosixPath(rel) };
}
async function createDevExplorerBundle() {
const devExplorerDir = resolve(__dirname, 'dev-explorer');
const entryPoint = resolve(devExplorerDir, 'explorer-app.ts');
const outDir = resolve(devExplorerDir, 'dist');
try {
const devExplorerCtx = await context({
absWorkingDir: process.cwd(),
entryPoints: [entryPoint],
bundle: true,
format: 'esm',
target: 'es2020',
sourcemap: true,
outdir: outDir,
logLevel: 'info',
plugins: [
{
name: 'dev-explorer-reload',
setup(build) {
build.onEnd(() => {
sendEventsToAll();
});
},
},
],
});
await devExplorerCtx.watch();
await devExplorerCtx.rebuild();
} catch (err) {
console.error(
[
'Dev Explorer bundle build failed.',
'Install dependencies: pnpm add -D lit @shoelace-style/shoelace',
'Then restart the dev server.',
].join('\n')
);
console.error(err);
}
}
async function createServer() {
await generateLangium();
handleFileChange();
@@ -186,117 +106,8 @@ async function createServer() {
handleFileChange();
});
// Rebuild the dev-explorer client bundle on changes (and emit SSE so the browser reloads).
await createDevExplorerBundle();
// Emit SSE when Dev Explorer static assets change (e.g. public/styles.css),
// otherwise CSS-only changes can look "ignored" unless the user manually refreshes.
chokidar
.watch(['.esbuild/dev-explorer/public/**/*'], {
ignoreInitial: true,
ignored: [/node_modules/, /dist/, /docs/, /coverage/],
})
.on('all', (event, changedPath) => {
if (!['add', 'change', 'unlink'].includes(event)) {
return;
}
console.log(`[dev-explorer] ${event}: ${changedPath}`);
sendEventsToAll();
});
// Emit SSE when .mmd files inside the configured explorer root change.
chokidar
.watch('**/*.mmd', {
cwd: devExplorerRootAbs,
ignoreInitial: true,
})
.on('all', (event, changedPath) => {
if (!['add', 'change', 'unlink'].includes(event)) {
return;
}
console.log(`[dev-explorer] ${event}: ${changedPath}`);
sendEventsToAll();
});
app.use(cors());
app.get('/events', eventsHandler);
// --- Dev Explorer API + UI -------------------------------------------------
const devExplorerDir = resolve(__dirname, 'dev-explorer');
const devExplorerPublicDir = resolve(devExplorerDir, 'public');
const devExplorerDistDir = resolve(devExplorerDir, 'dist');
// Shoelace assets (theme css + icons). Safe: only mounted in dev server.
app.use(
'/dev/vendor/shoelace',
express.static(resolve(process.cwd(), 'node_modules/@shoelace-style/shoelace/dist'))
);
app.get('/dev/api/files', async (req, res) => {
try {
const { absPath, relPath } = resolveWithinDevExplorerRoot(req.query.path);
const stats = await fs.stat(absPath);
if (!stats.isDirectory()) {
res.status(400).json({ error: 'Not a directory' });
return;
}
const dirEntries = await fs.readdir(absPath, { withFileTypes: true });
const entries = dirEntries
.filter((d) => d.isDirectory() || (d.isFile() && d.name.endsWith('.mmd')))
.map<DevExplorerEntry>((d) => ({
name: d.name,
kind: d.isDirectory() ? 'dir' : 'file',
path: toPosixPath(path.join(relPath, d.name)),
}))
.sort((a, b) => {
if (a.kind !== b.kind) {
return a.kind === 'dir' ? -1 : 1;
}
return a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
});
res.json({
root: toPosixPath(path.relative(process.cwd(), devExplorerRootAbs)),
path: relPath === '' ? '' : relPath,
entries,
});
} catch (_e) {
res.status(400).json({ error: 'Invalid path' });
}
});
app.get('/dev/api/file', async (req, res) => {
try {
const { absPath, relPath } = resolveWithinDevExplorerRoot(req.query.path);
if (!absPath.endsWith('.mmd')) {
res.status(400).send('Only .mmd files are allowed');
return;
}
const stats = await fs.stat(absPath);
if (!stats.isFile()) {
res.status(400).send('Not a file');
return;
}
const content = await fs.readFile(absPath, 'utf-8');
res.type('text/plain').send(content);
// Optional: include relPath for debugging.
void relPath;
} catch (_e) {
res.status(400).send('Invalid path');
}
});
// Static assets for the dev-explorer UI.
app.use('/dev/assets', express.static(devExplorerDistDir));
// Serve `/dev/` (and `/dev`) from public/, including index.html.
app.use(
'/dev',
express.static(devExplorerPublicDir, {
index: ['index.html'],
})
);
for (const { packageName } of Object.values(packageOptions)) {
app.use(express.static(`./packages/${packageName}/dist`));
}

View File

@@ -71,9 +71,6 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const external: string[] = ['require', 'fs', 'path'];
const outFileName = getFileName(name, options);
const { dependencies, version } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
const output: BuildOptions = buildOptions({
...rest,
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),
@@ -85,13 +82,15 @@ export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
chunkNames: `chunks/${outFileName}/[name]-[hash]`,
define: {
// This needs to be stringified for esbuild
'injected.includeLargeFeatures': `${includeLargeFeatures}`,
'injected.version': `'${version}'`,
includeLargeFeatures: `${includeLargeFeatures}`,
'import.meta.vitest': 'undefined',
},
});
if (core) {
const { dependencies } = JSON.parse(
readFileSync(resolve(__dirname, `../packages/${packageName}/package.json`), 'utf-8')
);
// Core build is used to generate file without bundled dependencies.
// This is used by downstream projects to bundle dependencies themselves.
// Ignore dependencies and any dependencies of dependencies

View File

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

View File

@@ -42,4 +42,5 @@ jobs:
publish: pnpm changeset:publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: true

View File

@@ -20,7 +20,7 @@ jobs:
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif

View File

@@ -19,7 +19,7 @@ jobs:
message: 'chore: update browsers list'
push: false
- name: Create Pull Request
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with:
branch: update-browserslist
title: Update Browserslist

View File

@@ -18,6 +18,13 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Validate pnpm-lock.yaml entries
id: validate # give this step an ID so we can reference its outputs
run: |

View File

@@ -78,8 +78,6 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
},
define: {
'import.meta.vitest': 'undefined',
'injected.includeLargeFeatures': 'true',
'injected.version': `'0.0.0'`,
},
resolve: {
extensions: [],
@@ -96,6 +94,10 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
}),
...visualizerOptions(packageName, core),
],
define: {
// Needs to be string
includeLargeFeatures: 'true',
},
};
if (watch && config.build) {

View File

@@ -98,21 +98,11 @@ export const openURLAndVerifyRendering = (
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
cy.get('svg').should('not.have.attr', 'viewbox');
// Handle sandbox mode where SVG is inside an iframe
if (options.securityLevel === 'sandbox') {
cy.get('iframe').should('be.visible');
if (validation) {
cy.get('iframe').should(validation);
}
} else {
cy.get('svg').should('be.visible');
// cspell:ignore viewbox
cy.get('svg').should('not.have.attr', 'viewbox');
if (validation) {
cy.get('svg').should(validation);
}
if (validation) {
cy.get('svg').should(validation);
}
if (screenshot) {

View File

@@ -114,28 +114,4 @@ describe('C4 diagram', () => {
{}
);
});
it('C4.6 should render C4Context diagram with ComponentQueue_Ext', () => {
imgSnapshotTest(
`
C4Context
title System Context diagram with ComponentQueue_Ext
Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
Enterprise_Boundary(b1, "BankBoundary") {
ComponentQueue_Ext(msgQueue, "Message Queue", "RabbitMQ", "External message queue system for processing banking transactions")
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
}
}
BiRel(customerA, SystemAA, "Uses")
Rel(SystemAA, msgQueue, "Sends messages to")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
`,
{}
);
});
});

View File

@@ -445,7 +445,7 @@ ORDER ||--|{ LINE-ITEM : contains
{ logLevel: 1 }
);
});
it('should render ER diagram with standalone numeric entities', () => {
it('should render ER diagram with numeric entity names and attributes', () => {
imgSnapshotTest(
`erDiagram
PRODUCT ||--o{ ORDER-ITEM : has

View File

@@ -79,18 +79,6 @@ describe('Flowchart v2', () => {
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('6a: should render complex HTML in labels with sandbox security', () => {
imgSnapshotTest(
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ securityLevel: 'sandbox', flowchart: { htmlLabels: true } }
);
});
it('7: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`flowchart TD

View File

@@ -833,34 +833,4 @@ describe('Gantt diagram', () => {
{}
);
});
it('should handle seconds-only format with tickInterval (issue #5496)', () => {
imgSnapshotTest(
`
gantt
tickInterval 1second
dateFormat ss
axisFormat %s
section Network Request
RTT : rtt, 0, 20
`,
{}
);
});
it('should handle dates with year typo like 202 instead of 2024 (issue #5496)', () => {
imgSnapshotTest(
`
gantt
title Schedule
dateFormat YYYY-MM-DD
tickInterval 1week
axisFormat %m-%d
section Vacation
London : 2024-12-01, 7d
London : 202-12-01, 7d
`,
{}
);
});
});

View File

@@ -0,0 +1,240 @@
import { imgSnapshotTest } from '../../helpers/util';
describe('Icons rendering tests', () => {
it('should render icon from config pack', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'Docker' }
B --> C[End]
`);
});
it('should render icons from different packs', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
simple-icons: "@iconify-json/simple-icons@1"
---
flowchart TB
A@{ icon: 'logos:aws', label: 'AWS' } --> B@{ icon: 'logos:docker', label: 'Docker' }
B --> C@{ icon: 'logos:kubernetes', label: 'K8s' }
C --> D@{ icon: 'simple-icons:github', label: 'GitHub' }
`);
});
it('should use custom CDN template', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
cdnTemplate: "https://cdn.jsdelivr.net/npm/\${packageSpec}/icons.json"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'Docker' }
B --> C[End]
`);
});
it('should use different allowed hosts', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
allowedHosts:
- cdn.jsdelivr.net
- unpkg.com
---
flowchart TB
A[Start] --> B@{ icon: 'logos:aws', label: 'AWS' }
`);
});
it('should render icon with label at top', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'Docker Container', pos: 't' }
`);
});
it('should render icon with label at bottom', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:kubernetes', label: 'Kubernetes', pos: 'b' }
`);
});
it('should render icon with long label', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'This is a very long label for Docker container orchestration', h: 64 }
`);
});
it('should render large icon', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'Large', h: 80, w: 80 }
`);
});
it('should render small icon', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'Small', h: 32, w: 32 }
`);
});
it('should apply custom styles to icon shape', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'Styled', form: 'square' }
B --> C[End]
style B fill:#0db7ed,stroke:#333,stroke-width:4px
`);
});
it('should use classDef with icons', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
classDef dockerIcon fill:#0db7ed,stroke:#fff,stroke-width:2px
classDef awsIcon fill:#FF9900,stroke:#fff,stroke-width:2px
A[Start] --> B@{ icon: 'logos:docker', label: 'Docker' }
B --> C@{ icon: 'logos:aws', label: 'AWS' }
B:::dockerIcon
C:::awsIcon
`);
});
it('should render in TB layout', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'Docker' }
B --> C[End]
`);
});
it('should render in LR layout', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart LR
A[Start] --> B@{ icon: 'logos:kubernetes', label: 'K8s' }
B --> C[End]
`);
});
it('should handle unknown icon gracefully', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'unknown:invalid', label: 'Unknown Icon' }
B --> C[End]
`);
});
it('should handle timeouts gracefully', () => {
imgSnapshotTest(`---
config:
icons:
timeout: 1
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:aws', label: 'Timeout' }
B --> C[End]
`);
});
it('should handle missing pack gracefully', () => {
imgSnapshotTest(`flowchart TB
A[Start] --> B@{ icon: 'missing:icon', label: 'Missing Pack Icon' }
`);
});
it('should render multiple icons in sequence', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:aws', label: 'AWS' }
B --> C@{ icon: 'logos:docker', label: 'Docker' }
C --> D@{ icon: 'logos:kubernetes', label: 'K8s' }
D --> E[End]
`);
});
it('should render icons in parallel branches', () => {
imgSnapshotTest(`---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'Docker' }
A --> C@{ icon: 'logos:kubernetes', label: 'K8s' }
B --> D[End]
C --> D
`);
});
});

View File

@@ -247,31 +247,5 @@ root
);
});
});
describe('Level 2 nodes exceeding 11', () => {
it('should render all Level 2 nodes correctly when there are more than 11', () => {
imgSnapshotTest(
`mindmap
root
Node1
Node2
Node3
Node4
Node5
Node6
Node7
Node8
Node9
Node10
Node11
Node12
Node13
Node14
Node15`,
{},
undefined,
shouldHaveRoot
);
});
});
/* The end */
});

View File

@@ -776,194 +776,5 @@ describe('Sequence Diagram Special Cases', () => {
);
});
});
describe('Participant Stereotypes with Aliases', () => {
it('should render participants with stereotypes and aliases', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "type" : "boundary" } as Public API
participant Auth@{ "type" : "control" } as Auth Controller
participant DB@{ "type" : "database" } as User Database
participant Cache@{ "type" : "entity" } as Cache Layer
API ->> Auth: Authenticate request
Auth ->> DB: Query user
DB -->> Auth: User data
Auth ->> Cache: Store session
Cache -->> Auth: Confirmed
Auth -->> API: Token`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render actors with stereotypes and aliases', () => {
imgSnapshotTest(
`sequenceDiagram
actor U@{ "type" : "actor" } as End User
actor A@{ "type" : "boundary" } as API Gateway
actor S@{ "type" : "control" } as Service Layer
actor D@{ "type" : "database" } as Data Store
U ->> A: Send request
A ->> S: Process
S ->> D: Persist
D -->> S: Success
S -->> A: Response
A -->> U: Result`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render mixed participants and actors with stereotypes and aliases', () => {
imgSnapshotTest(
`sequenceDiagram
actor Client@{ "type" : "actor" } AS Mobile Client
participant Gateway@{ "type" : "boundary" } as API Gateway
participant OrderSvc@{ "type" : "control" } as Order Service
participant Queue@{ "type" : "queue" } as Message Queue
participant DB@{ "type" : "database" } as Order Database
participant Logs@{ "type" : "collections" } as Audit Logs
Client ->> Gateway: Place order
Gateway ->> OrderSvc: Validate order
OrderSvc ->> Queue: Queue for processing as well
OrderSvc ->> DB: Save order
OrderSvc ->> Logs: Log transaction
Queue -->> OrderSvc: Processing started AS Well
DB -->> OrderSvc: Order saved
Logs -->> OrderSvc: Logged
OrderSvc -->> Gateway: Order confirmed
Gateway -->> Client: Confirmation`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render stereotypes with aliases in boxes', () => {
imgSnapshotTest(
`sequenceDiagram
box rgb(200,220,255) Frontend Layer
actor User@{ "type" : "actor" } as End User
participant UI@{ "type" : "boundary" } as User Interface
end
box rgb(255,220,200) Backend Layer
participant API@{ "type" : "boundary" } as REST API
participant Svc@{ "type" : "control" } as Business Logic
end
box rgb(220,255,200) Data Layer
participant DB@{ "type" : "database" } as Primary DB
participant Cache@{ "type" : "entity" } as Cache Store
end
User ->> UI: Click button
UI ->> API: HTTP request
API ->> Svc: Process
Svc ->> Cache: Check cache
Cache -->> Svc: Cache miss
Svc ->> DB: Query data
DB -->> Svc: Data
Svc ->> Cache: Update cache
Svc -->> API: Response
API -->> UI: Data
UI -->> User: Display`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render stereotypes with aliases and complex interactions', () => {
imgSnapshotTest(
`sequenceDiagram
participant Web@{ "type" : "boundary" } as Web Portal
participant Auth@{ "type" : "control" } as Auth Service
participant UserDB@{ "type" : "database" } as User DB
participant Queue@{ "type" : "queue" } as Event Queue
participant Audit@{ "type" : "collections" } as Audit Trail
Web ->> Auth: Login request
activate Auth
Auth ->> UserDB: Verify credentials
activate UserDB
UserDB -->> Auth: User found
deactivate UserDB
alt Valid credentials
Auth ->> Queue: Publish login event
Auth ->> Audit: Log success
par Parallel processing
Queue -->> Auth: Event queued
and
Audit -->> Auth: Logged
end
Auth -->> Web: Success token
else Invalid credentials
Auth ->> Audit: Log failure
Audit -->> Auth: Logged
Auth --x Web: Access denied
end
deactivate Auth
Note over Web,Audit: All interactions logged`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
describe('Participant Inline Alias in Config', () => {
it('should render participants with inline alias in config object', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "type" : "boundary", "alias": "Public API" }
participant Auth@{ "type" : "control", "alias": "Auth Service" }
participant DB@{ "type" : "database", "alias": "User DB" }
API ->> Auth: Login request
Auth ->> DB: Query user
DB -->> Auth: User data
Auth -->> API: Token`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render actors with inline alias in config object', () => {
imgSnapshotTest(
`sequenceDiagram
actor U@{ "type" : "actor", "alias": "End User" }
actor G@{ "type" : "boundary", "alias": "Gateway" }
actor S@{ "type" : "control", "alias": "Service" }
U ->> G: Request
G ->> S: Process
S -->> G: Response
G -->> U: Result`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should handle mixed inline and external alias syntax', () => {
imgSnapshotTest(
`sequenceDiagram
participant A@{ "type" : "boundary", "alias": "Service A" }
participant B@{ "type" : "control" } as Service B
participant C@{ "type" : "database" }
A ->> B: Request
B ->> C: Query
C -->> B: Data
B -->> A: Response`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should prioritize external alias over inline alias', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "type" : "boundary", "alias": "Internal Name" } as External Name
participant DB@{ "type" : "database", "alias": "Internal DB" } AS External DB
API ->> DB: Query
DB -->> API: Result`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
it('should render inline alias with only alias field (no type)', () => {
imgSnapshotTest(
`sequenceDiagram
participant API@{ "alias": "Public API" }
participant Auth@{ "alias": "Auth Service" }
API ->> Auth: Request
Auth -->> API: Response`,
{ look: 'classic', sequence: { diagramMarginX: 50, diagramMarginY: 10 } }
);
});
});
});
});

View File

@@ -327,97 +327,8 @@ classDef sales fill:#c3a66b,stroke:#333;
{}
);
});
it('12: should apply classDef fill color to leaf nodes', () => {
imgSnapshotTest(
`treemap-beta
"Root"
"Item A": 30:::redClass
"Item B": 20
"Item C": 25:::blueClass
classDef redClass fill:#ff0000;
classDef blueClass fill:#0000ff;
`,
{}
);
});
it('13: should apply classDef stroke styles to sections', () => {
imgSnapshotTest(
`treemap-beta
%% This is a comment
"Category A":::thickBorder
"Item A1": 10
"Item A2": 20
%% Another comment
"Category B":::dashedBorder
"Item B1": 15
"Item B2": 25
classDef thickBorder stroke:red,stroke-width:8px;
classDef dashedBorder stroke:black,stroke-dasharray:5,stroke-width:8px;
`,
{}
);
});
it('14: should apply classDef color to text labels', () => {
imgSnapshotTest(
`treemap-beta
"Products"
"Electronics":::whiteText
"Phones": 40
"Laptops": 30
"Furniture":::darkText
"Chairs": 25
"Tables": 20
classDef whiteText fill:#2c3e50,color:#ffffff;
classDef darkText fill:#ecf0f1,color:#000000;
`,
{}
);
});
it('15: should apply multiple classDef properties simultaneously', () => {
imgSnapshotTest(
`treemap-beta
"Budget"
"Critical":::critical
"Server Costs": 50000
"Salaries": 80000
"Normal":::normal
"Office Supplies": 5000
"Marketing": 15000
classDef critical fill:#e74c3c,color:#fff,stroke:#c0392b,stroke-width:3px;
classDef normal fill:#3498db,color:#fff,stroke:#2980b9,stroke-width:1px;
`,
{}
);
});
it('16: should handle classDef on nested sections and leaves', () => {
imgSnapshotTest(
`treemap-beta
"Company"
"Engineering":::engSection
"Frontend": 30:::highlight
"Backend": 40
"DevOps": 20:::highlight
"Sales"
"Direct": 35
"Channel": 25:::highlight
classDef engSection fill:#9b59b6,stroke:#8e44ad,stroke-width:2px;
classDef highlight fill:#f39c12,color:#000,stroke:#e67e22,stroke-width:2px;
`,
{}
);
});
/*
it.skip('17: should render a treemap with title', () => {
it.skip('12: should render a treemap with title', () => {
imgSnapshotTest(
`
treemap-beta

View File

@@ -1,16 +0,0 @@
sequenceDiagram
actor Alice
participant John@{ "type": "boundary" }
participant P1@{ "type": "control" } as New Control
participant P2@{ "type": "entity" } as New Entity
participant P3@{ "type": "database" } as New Database
Alice->>+John: Hello John, how are you?
John->>P1: new msg
P1->>P2: new msg
P2->>P3: new msg
Alice->>+John: John, can you hear me?
P3->>P2: new msg
P2->>P1: new msg
John-->>-Alice: Hi Alice, I can hear you!
P1->>John: new msg
John-->>-Alice: I feel great!

View File

@@ -1,9 +0,0 @@
treemap
"Section 1"
"Leaf 1.1": 12
"Section 1.2":::class1
"Leaf 1.2.1": 12
"Section 2"
"Leaf 2.1": 20:::class1
"Leaf 2.2": 25
"Leaf 2.3": 12

View File

@@ -1,11 +0,0 @@
---
title: "Grades"
---
radar-beta
axis m["Math"], s["Science"], e["English"]
axis h["History"], g["Geography"], a["Art"]
curve a["Alice"]{85, 90, 80, 70, 75, 90}
curve b["Bob"]{70, 75, 85, 80, 90, 85}
max 100
min 0

View File

@@ -1,11 +0,0 @@
flowchart LR
A["A"] e1@==> B["B"]
A L_A_n1_0@--> n1["C"]
B L_B_n2_0@==> n2["D"]
n1 L_n1_n2_0@--> n2
e1@{ animate: true }
L_A_n1_0@{ animation: slow }
L_B_n2_0@{ animation: slow }
L_n1_n2_0@{ animation: fast }

View File

@@ -1,3 +0,0 @@
flowchart LR
A e1@==> B
e1@{ animate: true }

View File

@@ -1,18 +0,0 @@
---
config:
layout: ogdc
---
flowchart-elk TB
c1-->a2
subgraph one
a1-->a2
end
subgraph two
b1-->b2
end
subgraph three
c1-->c2
end
one --> two
three --> two
two --> c2

View File

@@ -1,17 +0,0 @@
---
config:
layout: elk
---
flowchart TB
process_C
subgraph container_Alpha
subgraph process_B
pppB
end
subgraph process_A
pppA
end
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|container_Beta
end

View File

@@ -1,18 +0,0 @@
---
config:
layout: elk
---
flowchart TB
subgraph container_Beta
process_C
end
subgraph container_Alpha
subgraph process_B
pppB
end
subgraph process_A
pppA
end
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|container_Beta
end

View File

@@ -1,10 +0,0 @@
---
config:
layout: elk
---
flowchart TB
subgraph container_Beta
process_C
end
process_B-->|via_AWSBatch|container_Beta

View File

@@ -1,31 +0,0 @@
---
config:
layout: elk
---
classDiagram
note "I love this diagram!\nDo you love it?"
Class01 <|-- AveryLongClass : Cool
<<interface>> Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class12 <|.. Class08
Class11 ..>Class12
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class01 : -int privateChimp
Class01 : +int publicGorilla
Class01 : #int protectedMarmoset
Class08 <--> C2: Cool label
class Class10 {
<<service>>
int id
test()
}
note for Class10 "Cool class\nI said it's very cool class!"

View File

@@ -1,17 +0,0 @@
---
config:
layout: elk
---
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
element test_entity {
type: simulation
}
test_entity - satisfies -> test_req

View File

@@ -1,21 +0,0 @@
---
config:
layout: elk
---
flowchart-elk TB
internet
nat
router
compute1
subgraph project
router
nat
subgraph subnet1
compute1
end
end
%% router --> subnet1
subnet1 --> nat
%% nat --> internet

View File

@@ -1,27 +0,0 @@
---
config:
layout: elk
---
flowchart-elk TB
internet
nat
router
lb1
lb2
compute1
compute2
subgraph project
router
nat
subgraph subnet1
compute1
lb1
end
subgraph subnet2
compute2
lb2
end
end
internet --> router
router --> subnet1 & subnet2
subnet1 & subnet2 --> nat --> internet

View File

@@ -1,14 +0,0 @@
---
config:
layout: elk
elk:
mergeEdges: false
forceNodeModelOrder: false
considerModelOrder: NONE
---
flowchart TB
a --> a1 & a2 & a3 & a4
b --> b1 & b2
b2 --> b3
b1 --> b4

View File

@@ -1,11 +0,0 @@
---
config:
layout: elk
flowchart:
curve: rounded
---
flowchart LR
I["fa:fa-code Text"] -- Mermaid js --> D["Use<br/>the<br/>editor!"]
I --> D & D
D@{ shape: question}
I@{ shape: question}

View File

@@ -1,21 +0,0 @@
---
config:
layout: tidy-tree
---
mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
Pen and paper
Mermaid

View File

@@ -1,11 +0,0 @@
---
config:
layout: elk
flowchart:
curve: linear
---
flowchart LR
A[A] --> B[B]
A[A] --- B([C])
A@{ shape: diamond}
%%B@{ shape: diamond}

View File

@@ -1,11 +0,0 @@
---
config:
layout: elk
flowchart:
curve: linear
---
flowchart LR
A[A] -- Mermaid js --> B[B]
A[A] -- Mermaid js --- B[B]
A@{ shape: diamond}
B@{ shape: diamond}

View File

@@ -1,11 +0,0 @@
---
config:
layout: elk
flowchart:
curve: rounded
---
flowchart LR
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
I --> D & D
D@{ shape: question}
I@{ shape: question}

View File

@@ -1,14 +0,0 @@
---
config:
layout: elk
flowchart:
curve: rounded
elk:
nodePlacementStrategy: NETWORK_SIMPLEX
---
flowchart LR
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
D --> I & I
a["a"]
D@{ shape: trap-b}
I@{ shape: lean-l}

View File

@@ -1,11 +0,0 @@
---
config:
layout: elk
---
flowchart LR
%% subgraph s1["Untitled subgraph"]
C["Evaluate"]
%% end
B --> C

View File

@@ -1,10 +0,0 @@
---
config:
layout: elk
flowchart:
//curve: linear
---
flowchart LR
%% A ==> B
%% A2 --> B2
A{A} --> B((Bo boo)) & B & B & B

View File

@@ -1,18 +0,0 @@
---
config:
layout: elk
theme: default
look: classic
---
flowchart LR
subgraph s1["APA"]
D{"Use the editor"}
end
subgraph S2["S2"]
s1
I>"fa:fa-code Text"]
E["E"]
end
D -- Mermaid js --> I
D --> I & E
E --> I

View File

@@ -1,23 +0,0 @@
---
config:
layout: elk
---
flowchart LR
a
subgraph s0["APA"]
subgraph s8["APA"]
subgraph s1["APA"]
D{"X"}
E[Q]
end
subgraph s3["BAPA"]
F[Q]
I
end
D --> I
D --> I
D --> I
I{"X"}
end
end

View File

@@ -1,11 +0,0 @@
---
config:
layout: elk
---
flowchart LR
a
D{"Use the editor"}
D -- Mermaid js --> I{"fa:fa-code Text"}
D-->I
D-->I

View File

@@ -1,36 +0,0 @@
---
config:
layout: elk
---
flowchart LR
subgraph s1["Untitled subgraph"]
n1["Evaluate"]
n2["Option 1"]
n3["Option 2"]
n4["fa:fa-car Option 3"]
end
subgraph s2["Untitled subgraph"]
n5["Evaluate"]
n6["Option 1"]
n7["Option 2"]
n8["fa:fa-car Option 3"]
end
A["Start"] -- Some text --> B("Continue")
B --> C{"Evaluate"}
C -- One --> D["Option 1"]
C -- Two --> E["Option 2"]
C -- Three --> F["fa:fa-car Option 3"]
n1 -- One --> n2
n1 -- Two --> n3
n1 -- Three --> n4
n5 -- One --> n6
n5 -- Two --> n7
n5 -- Three --> n8
n1@{ shape: diam}
n2@{ shape: rect}
n3@{ shape: rect}
n4@{ shape: rect}
n5@{ shape: diam}
n6@{ shape: rect}
n7@{ shape: rect}
n8@{ shape: rect}

View File

@@ -1,10 +0,0 @@
---
config:
layout: elk
---
flowchart LR
subgraph s1["Untitled subgraph"]
n1["Evaluate"]
n2["Option 1"]
end
n1 -- One --> n2

View File

@@ -1,6 +0,0 @@
---
config:
layout: elk
---
flowchart LR
A{A} --> B & C

View File

@@ -1,9 +0,0 @@
---
config:
layout: elk
---
flowchart LR
A{A} --> B & C
subgraph "subbe"
A
end

View File

@@ -1,14 +0,0 @@
---
config:
layout: elk
---
flowchart LR
n2@{ shape: rect}
n3@{ shape: rect}
n4@{ shape: rect}
A["Start"] -- Some text --> B("Continue")
B --> C{"Evaluate"}
C -- One --> D["Option 1"]
C -- Two --> E["Option 2"]
C -- Three --> F["fa:fa-car Option 3"]
%% C@{ shape: hexagon}

View File

@@ -1,16 +0,0 @@
---
config:
kanban:
ticketBaseUrl: 'https://github.com/your-repo/issues/#TICKET#'
---
kanban
Backlog
task1[📝 Define project requirements]@{ ticket: a101 }
To Do
task2[🔍 Research technologies]@{ ticket: a102 }
Review
task4[🔍 Code review for login feature]@{ ticket: a104 }
Done
task5[✅ Deploy initial version]@{ ticket: a105 }
In Progress
task3[💻 Develop login feature]@{ ticket: 103 }

View File

@@ -1,2 +0,0 @@
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }

View File

@@ -1,3 +0,0 @@
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
style A fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -1,4 +0,0 @@
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -1,2 +0,0 @@
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }

View File

@@ -1,2 +0,0 @@
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }

View File

@@ -1,3 +0,0 @@
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
style A fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -1,4 +0,0 @@
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -1,2 +0,0 @@
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }

View File

@@ -1,2 +0,0 @@
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }

View File

@@ -1,3 +0,0 @@
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -1,4 +0,0 @@
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -1,4 +0,0 @@
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -1,3 +0,0 @@
flowchart LR
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px

View File

@@ -1,3 +0,0 @@
kanban
id2[In progress]
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }

View File

@@ -1,24 +0,0 @@
---
config:
kanban:
ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#TICKET#'
# sectionWidth: 300
---
kanban
Todo
[Create Documentation]
docs[Create Blog about the new diagram]
id7[In progress]
id6[Create renderer so that it works in all cases. We also add some extra text here for testing purposes. And some more just for the extra flare.]
id9[Ready for deploy]
id8[Design grammar]@{ assigned: 'knsv' }
id10[Ready for test]
id4[Create parsing tests]@{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' }
id66[last item]@{ priority: 'Very Low', assigned: 'knsv' }
id11[Done]
id5[define getData]
id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]@{ ticket: MC-2036, priority: 'Very High'}
id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
id12[Can't reproduce]
id3[Weird flickering in Firefox]

View File

@@ -1,9 +0,0 @@
flowchart TD
B("B: Investigate how SOX2 is implicated in endothelial to mesenchymal transition and how expression affects the migration of GBM cells")
B --> D["D: Investigate and isolate migratory glioblastoma with regards to phenotype"] & E["E: Investigate the effect of SOX2 on EMT in GBM"] & F["F: Investigate the effect of SOX2 downregulation on migratory/invasive phenotype in the in vitro model and the effect on metastasis formation in the in vivo model"]
L("L: Expression of markers for stemness, EMT and invasion pattern")
D -- RNA Sequencing --> L
E -- "Lentiviral-mediated RNA interference" --> J("J:Sox2 Knock-Down")
J --> O("O: Up/downregulation of stemness markers") & P("P: Up/downregulation of EMT markers")
F -- Transwell/Wound Healing Assay --> K("K: In Vitro m")

View File

@@ -105,77 +105,49 @@
</head>
<body>
<div class="grid grid-cols-2 gap-4">
<pre id="diagram4" class="mermaid">
sequenceDiagram
actor Alice
participant John@{ "type": "boundary" }
participant P1@{ "type": "control" } as New Control
participant P2@{ "type": "entity" } as New Entity
participant P3@{ "type": "database" } as New Database
Alice->>+John: Hello John, how are you?
John->>P1: new msg
P1->>P2: new msg
P2->>P3: new msg
Alice->>+John: John, can you hear me?
P3->>P2: new msg
P2->>P1: new msg
John-->>-Alice: Hi Alice, I can hear you!
P1->>John: new msg
John-->>-Alice: I feel great!
</pre
>
<pre id="diagram4" class="mermaid">
treemap
"Section 1"
"Leaf 1.1": 12
"Section 1.2":::class1
"Leaf 1.2.1": 12
"Section 2"
"Leaf 2.1": 20:::class1
"Leaf 2.2": 25
"Leaf 2.3": 12
</pre
>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid">
---
title: "Grades"
---
radar-beta
axis m["Math"], s["Science"], e["English"]
axis h["History"], g["Geography"], a["Art"]
curve a["Alice"]{85, 90, 80, 70, 75, 90}
curve b["Bob"]{70, 75, 85, 80, 90, 85}
max 100
min 0
</pre
>
<pre id="diagram4" class="mermaid">
flowchart LR
A["A"] e1@==> B["B"]
A L_A_n1_0@--> n1["C"]
B L_B_n2_0@==> n2["D"]
n1 L_n1_n2_0@--> n2
e1@{ animate: true }
L_A_n1_0@{ animation: slow }
L_B_n2_0@{ animation: slow }
L_n1_n2_0@{ animation: fast }
</pre
>
</div>
<pre id="diagram4" class="mermaid2">
flowchart LR
A e1@==> B
e1@{ animate: true }
config:
layout: elk
---
mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectiveness&lt;br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
id)I am a cloud(
id))I am a bang((
Tools
</pre>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
flowchart
aid0
</pre
>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
mindmap
aid0
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: ogdc
@@ -197,7 +169,7 @@ radar-beta
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -218,7 +190,7 @@ radar-beta
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -240,7 +212,7 @@ radar-beta
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -255,7 +227,7 @@ radar-beta
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -289,7 +261,7 @@ radar-beta
note for Class10 "Cool class\nI said it's very cool class!"
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -309,7 +281,7 @@ radar-beta
test_entity - satisfies -> test_req
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -333,7 +305,7 @@ radar-beta
%% nat --> internet
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -363,7 +335,7 @@ radar-beta
subnet1 & subnet2 --> nat --> internet
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -379,7 +351,19 @@ config:
b2 --> b3
b1 --> b4</pre
>
<pre id="diagram5" class="mermaid2">
<pre id="diagram4" class="mermaid">
treemap
"Section 1"
"Leaf 1.1": 12
"Section 1.2":::class1
"Leaf 1.2.1": 12
"Section 2"
"Leaf 2.1": 20:::class1
"Leaf 2.2": 25
"Leaf 2.3": 12
</pre>
<pre id="diagram5" class="mermaid">
---
config:
layout: elk
@@ -393,7 +377,7 @@ config:
I@{ shape: question}
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: tidy-tree
@@ -417,7 +401,7 @@ config:
Mermaid
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -431,7 +415,7 @@ config:
%%B@{ shape: diamond}
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -445,7 +429,7 @@ config:
B@{ shape: diamond}
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -458,7 +442,7 @@ config:
D@{ shape: question}
I@{ shape: question}
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -474,7 +458,7 @@ config:
D@{ shape: trap-b}
I@{ shape: lean-l}
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -487,7 +471,7 @@ flowchart LR
B --> C
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -500,7 +484,7 @@ flowchart LR
A{A} --> B((Bo boo)) & B & B & B
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -520,7 +504,7 @@ A{A} --> B((Bo boo)) & B & B & B
D --> I & E
E --> I
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -545,7 +529,7 @@ config:
end
end
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -558,7 +542,7 @@ config:
D-->I
D-->I
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -597,7 +581,7 @@ flowchart LR
n8@{ shape: rect}
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -613,7 +597,7 @@ flowchart LR
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -622,7 +606,7 @@ flowchart LR
A{A} --> B & C
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -634,7 +618,7 @@ flowchart LR
end
</pre
>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
@@ -652,7 +636,7 @@ flowchart LR
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
kanban:
@@ -671,81 +655,81 @@ kanban
task3[💻 Develop login feature]@{ ticket: 103 }
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
kanban
id2[In progress]
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
</pre>
<pre id="diagram4" class="mermaid2">
<pre id="diagram4" class="mermaid">
---
config:
kanban:
@@ -816,7 +800,7 @@ kanban
// look: 'handDrawn',
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
// layout: 'dagre',
layout: 'dagre',
layout: 'elk',
// layout: 'fixed',
// htmlLabels: false,
flowchart: { titleTopMargin: 10 },

View File

@@ -1,818 +0,0 @@
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/font-awesome.min.css"
/>
<link
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
rel="stylesheet"
/>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Caveat:wght@400..700&family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
rel="stylesheet"
/>
<style>
body {
/* background: rgb(221, 208, 208); */
/* background: #333; */
font-family: 'Arial';
/* color: white; */
/* font-size: 18px !important; */
}
h1 {
color: grey;
}
.mermaid2 {
display: none;
}
.mermaid svg {
/* font-size: 18px !important; */
/* background-color: #efefef;
background-image: radial-gradient(#fff 51%, transparent 91%),
radial-gradient(#fff 51%, transparent 91%);
background-size: 20px 20px;
background-position:
0 0,
10px 10px;
background-repeat: repeat; */
}
.malware {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 150px;
background: red;
color: black;
display: flex;
display: flex;
justify-content: center;
align-items: center;
font-family: monospace;
font-size: 72px;
}
pre {
width: 100%;
}
/* tspan {
font-size: 6px !important;
} */
/* .flowchart-link {
stroke-dasharray: 4, 4 !important;
animation: flow 1s linear infinite;
animation: dashdraw 4.93282s linear infinite;
stroke-width: 2px !important;
} */
@keyframes dashdraw {
from {
stroke-dashoffset: 0;
}
}
/*stroke-width:2;stroke-dasharray:10.000000,9.865639;stroke-dashoffset:-198.656393;animation: 4.932820s linear infinite;*/
/* stroke-width:2;stroke-dasharray:10.000000,9.865639;stroke-dashoffset:-198.656393;animation: dashdraw 4.932820s linear infinite;*/
</style>
</head>
<body>
<pre id="diagram4" class="mermaid">
---
config:
layout: tidy-tree
---
mindmap
[My User Manual ]
)What motivates me?(
a("Impact: Create real change, see outcomes")
People Energy: Connection, fun, joy
)What drives me nuts?(
Inefficiency: Poor execution, no follow-through
Indecision: Limbo > Action, prefer decisive moves
)How can people really impress me?(
Bring energy, ideas, enthusiasm
Execute + follow through!
Make things better, not just maintain
)What qualities do I value?(
Approachable & collaborative
Direct, transparent communication
Ownership with healthy boundaries
Willingness to learn and share
New perspectives & expertise
)What people might misunderstand(
Passion ≠ upset; high energy!
Many ideas ≠ all top priorities
I move fast - let me know if pace is overwhelming
)Real talk: My quirks & weaknesses(
Thrive on momentum, find it hard to slow down
Prefer "real" over "polished"
"Why" is my default question
Action > reflection sometimes
My energy can overshadow quieter voices
Help me pause & amplify all voices!
)Working & Growing Together(
How I like to be coached: Help me see blind spots, push/anchor me
Communication: Slack for quick; docs for updates; calls for nuance
Feedback: Real-time & caring, structured for growth
Receiving feedback: Direct, soon, with impact; ideally 1:1 or positive+constructive together
How I coach: Focus on outcomes, stretch/celebrate, creative growth opportunities, consultative problem-solving
Mistakes: Be quick, transparent, own it, share learnings
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart
aid0
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
mindmap
aid0
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: ogdc
---
flowchart-elk TB
c1-->a2
subgraph one
a1-->a2
end
subgraph two
b1-->b2
end
subgraph three
c1-->c2
end
one --> two
three --> two
two --> c2
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart TB
process_C
subgraph container_Alpha
subgraph process_B
pppB
end
subgraph process_A
pppA
end
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|container_Beta
end
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart TB
subgraph container_Beta
process_C
end
subgraph container_Alpha
subgraph process_B
pppB
end
subgraph process_A
pppA
end
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|container_Beta
end
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart TB
subgraph container_Beta
process_C
end
process_B-->|via_AWSBatch|container_Beta
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
classDiagram
note "I love this diagram!\nDo you love it?"
Class01 <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
Class12 <|.. Class08
Class11 ..>Class12
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class01 : -int privateChimp
Class01 : +int publicGorilla
Class01 : #int protectedMarmoset
Class08 <--> C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
note for Class10 "Cool class\nI said it's very cool class!"
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
requirementDiagram
requirement test_req {
id: 1
text: the test text.
risk: high
verifymethod: test
}
element test_entity {
type: simulation
}
test_entity - satisfies -> test_req
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart-elk TB
internet
nat
router
compute1
subgraph project
router
nat
subgraph subnet1
compute1
end
end
%% router --> subnet1
subnet1 --> nat
%% nat --> internet
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart-elk TB
internet
nat
router
lb1
lb2
compute1
compute2
subgraph project
router
nat
subgraph subnet1
compute1
lb1
end
subgraph subnet2
compute2
lb2
end
end
internet --> router
router --> subnet1 & subnet2
subnet1 & subnet2 --> nat --> internet
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
elk:
mergeEdges: false
forceNodeModelOrder: false
considerModelOrder: NONE
---
flowchart TB
a --> a1 & a2 & a3 & a4
b --> b1 & b2
b2 --> b3
b1 --> b4</pre
>
<pre id="diagram4" class="mermaid2">
treemap
"Section 1"
"Leaf 1.1": 12
"Section 1.2":::class1
"Leaf 1.2.1": 12
"Section 2"
"Leaf 2.1": 20:::class1
"Leaf 2.2": 25
"Leaf 2.3": 12
</pre>
<pre id="diagram5" class="mermaid2">
---
config:
layout: elk
flowchart:
curve: rounded
---
flowchart LR
I["fa:fa-code Text"] -- Mermaid js --> D["Use<br/>the<br/>editor!"]
I --> D & D
D@{ shape: question}
I@{ shape: question}
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: tidy-tree
---
mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
Pen and paper
Mermaid
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
flowchart:
curve: linear
---
flowchart LR
A[A] --> B[B]
A[A] --- B([C])
A@{ shape: diamond}
%%B@{ shape: diamond}
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
flowchart:
curve: linear
---
flowchart LR
A[A] -- Mermaid js --> B[B]
A[A] -- Mermaid js --- B[B]
A@{ shape: diamond}
B@{ shape: diamond}
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
flowchart:
curve: rounded
---
flowchart LR
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
I --> D & D
D@{ shape: question}
I@{ shape: question}
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
flowchart:
curve: rounded
elk:
nodePlacementStrategy: NETWORK_SIMPLEX
---
flowchart LR
D["Use the editor"] -- Mermaid js --> I["fa:fa-code Text"]
D --> I & I
a["a"]
D@{ shape: trap-b}
I@{ shape: lean-l}
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart LR
%% subgraph s1["Untitled subgraph"]
C["Evaluate"]
%% end
B --> C
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
flowchart:
//curve: linear
---
flowchart LR
%% A ==> B
%% A2 --> B2
A{A} --> B((Bo boo)) & B & B & B
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
theme: default
look: classic
---
flowchart LR
subgraph s1["APA"]
D{"Use the editor"}
end
subgraph S2["S2"]
s1
I>"fa:fa-code Text"]
E["E"]
end
D -- Mermaid js --> I
D --> I & E
E --> I
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart LR
a
subgraph s0["APA"]
subgraph s8["APA"]
subgraph s1["APA"]
D{"X"}
E[Q]
end
subgraph s3["BAPA"]
F[Q]
I
end
D --> I
D --> I
D --> I
I{"X"}
end
end
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart LR
a
D{"Use the editor"}
D -- Mermaid js --> I{"fa:fa-code Text"}
D-->I
D-->I
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart LR
subgraph s1["Untitled subgraph"]
n1["Evaluate"]
n2["Option 1"]
n3["Option 2"]
n4["fa:fa-car Option 3"]
end
subgraph s2["Untitled subgraph"]
n5["Evaluate"]
n6["Option 1"]
n7["Option 2"]
n8["fa:fa-car Option 3"]
end
A["Start"] -- Some text --> B("Continue")
B --> C{"Evaluate"}
C -- One --> D["Option 1"]
C -- Two --> E["Option 2"]
C -- Three --> F["fa:fa-car Option 3"]
n1 -- One --> n2
n1 -- Two --> n3
n1 -- Three --> n4
n5 -- One --> n6
n5 -- Two --> n7
n5 -- Three --> n8
n1@{ shape: diam}
n2@{ shape: rect}
n3@{ shape: rect}
n4@{ shape: rect}
n5@{ shape: diam}
n6@{ shape: rect}
n7@{ shape: rect}
n8@{ shape: rect}
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart LR
subgraph s1["Untitled subgraph"]
n1["Evaluate"]
n2["Option 1"]
end
n1 -- One --> n2
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart LR
A{A} --> B & C
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart LR
A{A} --> B & C
subgraph "subbe"
A
end
</pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
---
flowchart LR
n2@{ shape: rect}
n3@{ shape: rect}
n4@{ shape: rect}
A["Start"] -- Some text --> B("Continue")
B --> C{"Evaluate"}
C -- One --> D["Option 1"]
C -- Two --> E["Option 2"]
C -- Three --> F["fa:fa-car Option 3"]
%% C@{ shape: hexagon}
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
kanban:
ticketBaseUrl: 'https://github.com/your-repo/issues/#TICKET#'
---
kanban
Backlog
task1[📝 Define project requirements]@{ ticket: a101 }
To Do
task2[🔍 Research technologies]@{ ticket: a102 }
Review
task4[🔍 Code review for login feature]@{ ticket: a104 }
Done
task5[✅ Deploy initial version]@{ ticket: a105 }
In Progress
task3[💻 Develop login feature]@{ ticket: 103 }
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
flowchart LR
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
kanban
id2[In progress]
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
kanban:
ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#TICKET#'
# sectionWidth: 300
---
kanban
Todo
[Create Documentation]
docs[Create Blog about the new diagram]
id7[In progress]
id6[Create renderer so that it works in all cases. We also add some extra text here for testing purposes. And some more just for the extra flare.]
id9[Ready for deploy]
id8[Design grammar]@{ assigned: 'knsv' }
id10[Ready for test]
id4[Create parsing tests]@{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' }
id66[last item]@{ priority: 'Very Low', assigned: 'knsv' }
id11[Done]
id5[define getData]
id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]@{ ticket: MC-2036, priority: 'Very High'}
id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
id12[Can't reproduce]
id3[Weird flickering in Firefox]
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
import layoutElk from './mermaid-layout-elk.esm.mjs';
import layouts from './mermaid-layout-tidy-tree.esm.mjs';
const staticBellIconPack = {
prefix: 'fa6-regular',
icons: {
bell: {
body: '<path fill="currentColor" d="M224 0c-17.7 0-32 14.3-32 32v19.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416h400c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6c-28.3-35.5-43.8-79.6-43.8-125V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32m0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3c25.8-40 39.7-86.7 39.7-134.6V208c0-61.9 50.1-112 112-112m64 352H160c0 17 6.7 33.3 18.7 45.3S207 512 224 512s33.3-6.7 45.3-18.7S288 465 288 448"/>',
width: 448,
},
},
width: 512,
height: 512,
};
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos@1/icons.json').then((res) => res.json()),
},
{
name: 'fa',
loader: () => staticBellIconPack,
},
]);
mermaid.registerLayoutLoaders(layouts);
await mermaid.initialize({
logLevel: 0,
securityLevel: 'loose',
callback,
});
mermaid.parseError = function (err, hash) {
console.error('In parse error:');
console.error(err);
};
</script>
</body>
</html>

View File

@@ -2,227 +2,227 @@
"durations": [
{
"spec": "cypress/integration/other/configuration.spec.js",
"duration": 5944
"duration": 5841
},
{
"spec": "cypress/integration/other/external-diagrams.spec.js",
"duration": 2180
"duration": 2138
},
{
"spec": "cypress/integration/other/ghsa.spec.js",
"duration": 3282
"duration": 3370
},
{
"spec": "cypress/integration/other/iife.spec.js",
"duration": 2137
"duration": 2052
},
{
"spec": "cypress/integration/other/interaction.spec.js",
"duration": 11926
"duration": 12243
},
{
"spec": "cypress/integration/other/rerender.spec.js",
"duration": 2021
"duration": 2065
},
{
"spec": "cypress/integration/other/xss.spec.js",
"duration": 31377
"duration": 31288
},
{
"spec": "cypress/integration/rendering/appli.spec.js",
"duration": 3442
"duration": 3421
},
{
"spec": "cypress/integration/rendering/architecture.spec.ts",
"duration": 103
"duration": 97
},
{
"spec": "cypress/integration/rendering/block.spec.js",
"duration": 18390
"duration": 18500
},
{
"spec": "cypress/integration/rendering/c4.spec.js",
"duration": 6468
"duration": 5793
},
{
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
"duration": 41282
"duration": 40966
},
{
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
"duration": 39226
"duration": 39176
},
{
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
"duration": 25028
"duration": 23468
},
{
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
"duration": 38458
"duration": 38291
},
{
"spec": "cypress/integration/rendering/classDiagram.spec.js",
"duration": 17305
"duration": 16949
},
{
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
"duration": 9762
"duration": 9480
},
{
"spec": "cypress/integration/rendering/current.spec.js",
"duration": 2923
"duration": 2753
},
{
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
"duration": 89135
"duration": 88028
},
{
"spec": "cypress/integration/rendering/erDiagram.spec.js",
"duration": 18976
"duration": 15615
},
{
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
"duration": 3643
"duration": 3706
},
{
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
"duration": 43103
"duration": 43905
},
{
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
"duration": 31637
"duration": 31217
},
{
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
"duration": 7630
"duration": 7531
},
{
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
"duration": 25642
"duration": 25423
},
{
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
"duration": 50365
"duration": 49664
},
{
"spec": "cypress/integration/rendering/flowchart.spec.js",
"duration": 32790
"duration": 32525
},
{
"spec": "cypress/integration/rendering/gantt.spec.js",
"duration": 23065
"duration": 20915
},
{
"spec": "cypress/integration/rendering/gitGraph.spec.js",
"duration": 52238
"duration": 53556
},
{
"spec": "cypress/integration/rendering/iconShape.spec.ts",
"duration": 289380
"duration": 283038
},
{
"spec": "cypress/integration/rendering/imageShape.spec.ts",
"duration": 59265
"duration": 59434
},
{
"spec": "cypress/integration/rendering/info.spec.ts",
"duration": 3269
"duration": 3101
},
{
"spec": "cypress/integration/rendering/journey.spec.js",
"duration": 7470
"duration": 7099
},
{
"spec": "cypress/integration/rendering/kanban.spec.ts",
"duration": 7980
"duration": 7567
},
{
"spec": "cypress/integration/rendering/katex.spec.js",
"duration": 3896
"duration": 3817
},
{
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
"duration": 2640
"duration": 2624
},
{
"spec": "cypress/integration/rendering/mindmap-tidy-tree.spec.js",
"duration": 4327
"duration": 4246
},
{
"spec": "cypress/integration/rendering/mindmap.spec.ts",
"duration": 12588
"duration": 11967
},
{
"spec": "cypress/integration/rendering/newShapes.spec.ts",
"duration": 153490
"duration": 151914
},
{
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
"duration": 117833
"duration": 116698
},
{
"spec": "cypress/integration/rendering/packet.spec.ts",
"duration": 4975
"duration": 4967
},
{
"spec": "cypress/integration/rendering/pie.spec.ts",
"duration": 6682
"duration": 6700
},
{
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
"duration": 8972
"duration": 8963
},
{
"spec": "cypress/integration/rendering/radar.spec.js",
"duration": 5631
"duration": 5540
},
{
"spec": "cypress/integration/rendering/requirement.spec.js",
"duration": 2776
"duration": 2782
},
{
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
"duration": 54373
"duration": 54797
},
{
"spec": "cypress/integration/rendering/sankey.spec.ts",
"duration": 7203
"duration": 6914
},
{
"spec": "cypress/integration/rendering/sequencediagram-v2.spec.js",
"duration": 31707
"duration": 20481
},
{
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
"duration": 48327
"duration": 38490
},
{
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
"duration": 30728
"duration": 30766
},
{
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
"duration": 16881
"duration": 16705
},
{
"spec": "cypress/integration/rendering/theme.spec.js",
"duration": 30715
"duration": 30928
},
{
"spec": "cypress/integration/rendering/timeline.spec.ts",
"duration": 8586
"duration": 8424
},
{
"spec": "cypress/integration/rendering/treemap.spec.ts",
"duration": 15184
"duration": 12533
},
{
"spec": "cypress/integration/rendering/xyChart.spec.js",
"duration": 21282
"duration": 21197
},
{
"spec": "cypress/integration/rendering/zenuml.spec.js",
"duration": 3576
"duration": 3455
}
]
}

View File

@@ -4,15 +4,150 @@
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/icons.md](../../packages/mermaid/src/docs/config/icons.md).
# Registering icon pack in mermaid
# Icon Pack Configuration
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.
Mermaid supports icons through Iconify-compatible icon packs. You can register icon packs either **declaratively via configuration** (recommended for most use cases) or **programmatically via JavaScript API** (for advanced/offline scenarios).
Using JSON file directly from CDN:
## Declarative Configuration (v\<MERMAID_RELEASE_VERSION>+) (Recommended)
The easiest way to use icons in Mermaid is through declarative configuration. This works in browsers, CLI, Live Editor, and headless renders without requiring custom JavaScript.
### Basic Usage
Configure icon packs in your Mermaid config:
```yaml
---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:docker', label: 'Docker' }
B --> C[End]
```
Or in JavaScript:
```js
mermaid.initialize({
icons: {
packs: {
logos: '@iconify-json/logos@1',
'simple-icons': '@iconify-json/simple-icons@1',
},
},
});
```
### Package Spec Format
Icon packs are specified using **package specs** with version pinning:
- Format: `@scope/package@<version>` or `package@<version>`
- **Must include at least a major version** (e.g., `@iconify-json/logos@1`)
- Minor and patch versions are optional (e.g., `@iconify-json/logos@1.2.3`)
**Important**: Only package specs are supported. Direct URLs are not allowed for security reasons.
### Configuration Options
```yaml
icons:
packs:
# Icon pack configuration
# Key: local name to use in diagrams
# Value: package spec with version
logos: '@iconify-json/logos@1'
'simple-icons': '@iconify-json/simple-icons@1'
# CDN template for resolving package specs
# Must contain ${packageSpec} placeholder
cdnTemplate: 'https://cdn.jsdelivr.net/npm/${packageSpec}/icons.json'
# Maximum file size in MB for icon pack JSON files
# Range: 1-10 MB, default: 5 MB
maxFileSizeMB: 5
# Network timeout in milliseconds for icon pack fetches
# Range: 1000-30000 ms, default: 5000 ms
timeout: 5000
# List of allowed hosts to fetch icons from
allowedHosts:
- 'unpkg.com'
- 'cdn.jsdelivr.net'
- 'npmjs.com'
```
### Security Features
- **Host allowlisting**: Only fetch from hosts in `allowedHosts`
- **Size limits**: Maximum file size enforced via `maxFileSizeMB`
- **Timeouts**: Network requests timeout after specified milliseconds
- **HTTPS only**: All remote fetches occur over HTTPS
- **Version pinning**: Package specs must include version for reproducibility
### Examples
#### Using Custom CDN Template
```yaml
---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
cdnTemplate: "https://unpkg.com/${packageSpec}/icons.json"
---
flowchart TB
A[Start] --> B@{ icon: 'logos:aws', label: 'AWS' }
```
#### Multiple Icon Packs
```yaml
---
config:
icons:
packs:
logos: "@iconify-json/logos@1"
"simple-icons": "@iconify-json/simple-icons@1"
---
flowchart TB
A@{ icon: 'logos:docker', label: 'Docker' } --> B@{ icon: 'simple-icons:github', label: 'GitHub' }
```
#### CLI Usage
Create a `mermaid.json` config file:
```json
{
"icons": {
"packs": {
"logos": "@iconify-json/logos@1"
}
}
}
```
Then use it with the CLI:
```bash
mmdc -i diagram.mmd -o diagram.svg --configFile mermaid.json
```
## Programmatic API (v11.1.0+) (Advanced)
For advanced scenarios or offline use, you can register icon packs programmatically:
### Using JSON File from CDN
```js
import mermaid from 'CDN/mermaid.esm.mjs';
mermaid.registerIconPacks([
{
name: 'logos',
@@ -22,13 +157,15 @@ mermaid.registerIconPacks([
]);
```
Using packages and a bundler:
### Using Packages with Bundler
Install the icon pack:
```bash
npm install @iconify-json/logos@1
```
With lazy loading
#### With Lazy Loading
```js
import mermaid from 'mermaid';
@@ -41,15 +178,39 @@ mermaid.registerIconPacks([
]);
```
Without lazy loading
#### 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
name: icons.prefix, // Use the prefix defined in the icon pack
icons,
},
]);
```
## Finding Icon Packs
Icon packs available for use can be found at [iconify.design](https://icon-sets.iconify.design/) or [icones.js.org](https://icones.js.org/).
The pack name you register is the **local name** used in diagrams. It can differ from the pack's prefix, allowing you to:
- Use shorter names (e.g., register `@iconify-json/material-design-icons` as `mdi`)
- Load specific packs only when used in diagrams (lazy loading)
## Error Handling
If an icon cannot be found:
- The diagram still renders (non-fatal)
- A fallback icon is displayed
- A warning is logged (visible in CLI stderr or browser console)
## Licensing
Iconify JSON format is MIT licensed, but **individual icons may have varying licenses**. Please verify the licenses of the icon packs you configure before use in production.
Mermaid does **not** redistribute third-party icon assets in the core bundle.

View File

@@ -229,6 +229,14 @@ Defined in: [packages/mermaid/src/config.type.ts:124](https://github.com/mermaid
---
### icons?
> `optional` **icons**: `IconsConfig`
Defined in: [packages/mermaid/src/config.type.ts:223](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L223)
---
### journey?
> `optional` **journey**: `JourneyDiagramConfig`

View File

@@ -10,7 +10,7 @@
# Interface: ParseOptions
Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L89)
Defined in: [packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:89](https://github.com/mermaid-js/mer
> `optional` **suppressErrors**: `boolean`
Defined in: [packages/mermaid/src/types.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L94)
Defined in: [packages/mermaid/src/types.ts:93](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L93)
If `true`, parse will return `false` instead of throwing error when the diagram is invalid.
The `parseError` function will not be called.

View File

@@ -10,7 +10,7 @@
# Interface: ParseResult
Defined in: [packages/mermaid/src/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L97)
Defined in: [packages/mermaid/src/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L96)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:97](https://github.com/mermaid-js/mer
> **config**: [`MermaidConfig`](MermaidConfig.md)
Defined in: [packages/mermaid/src/types.ts:105](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L105)
Defined in: [packages/mermaid/src/types.ts:104](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L104)
The config passed as YAML frontmatter or directives
@@ -28,6 +28,6 @@ The config passed as YAML frontmatter or directives
> **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L101)
Defined in: [packages/mermaid/src/types.ts:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L100)
The diagram type, e.g. 'flowchart', 'sequence', etc.

View File

@@ -10,7 +10,7 @@
# Interface: RenderResult
Defined in: [packages/mermaid/src/types.ts:115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L115)
Defined in: [packages/mermaid/src/types.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L114)
## Properties
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/types.ts:115](https://github.com/mermaid-js/me
> `optional` **bindFunctions**: (`element`) => `void`
Defined in: [packages/mermaid/src/types.ts:133](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L133)
Defined in: [packages/mermaid/src/types.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L132)
Bind function to be called after the svg has been inserted into the DOM.
This is necessary for adding event listeners to the elements in the svg.
@@ -45,7 +45,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
> **diagramType**: `string`
Defined in: [packages/mermaid/src/types.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L123)
Defined in: [packages/mermaid/src/types.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L122)
The diagram type, e.g. 'flowchart', 'sequence', etc.
@@ -55,6 +55,6 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
> **svg**: `string`
Defined in: [packages/mermaid/src/types.ts:119](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L119)
Defined in: [packages/mermaid/src/types.ts:118](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L118)
The svg code for the rendered graph.

View File

@@ -12,7 +12,7 @@
> **ParseErrorFunction** = (`err`, `hash?`) => `void`
Defined in: [packages/mermaid/src/Diagram.ts:10](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/Diagram.ts#L10)
Defined in: [packages/mermaid/src/Diagram.ts:11](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/Diagram.ts#L11)
## Parameters

View File

@@ -57,9 +57,6 @@ To add an integration to this list, see the [Integrations - create page](./integ
- [GitHub Writer](https://github.com/ckeditor/github-writer)
- [SVG diagram generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) ✅
- [GNU Octave](https://octave.org/) ✅
- [octave_mermaid_js](https://github.com/CNOCTAVE/octave_mermaid_js) ✅
- [HackMD](https://hackmd.io/c/tutorials/%2F%40docs%2Fflowchart-en#Create-more-complex-flowcharts) ✅
- [Mermaid Plugin for JetBrains IDEs](https://plugins.jetbrains.com/plugin/20146-mermaid)
- [MonsterWriter](https://www.monsterwriter.com/) ✅
- [Joplin](https://joplinapp.org) ✅
@@ -275,7 +272,6 @@ Communication tools and platforms
- [reveal.js-mermaid-plugin](https://github.com/ludwick/reveal.js-mermaid-plugin)
- [Reveal CK](https://github.com/jedcn/reveal-ck)
- [reveal-ck-mermaid-plugin](https://github.com/tmtm/reveal-ck-mermaid-plugin)
- [speccharts: Turn your test suites into specification diagrams](https://github.com/arnaudrenaud/speccharts)
- [Vitepress Plugin](https://github.com/sametcn99/vitepress-mermaid-renderer)
<!--- cspell:ignore Blazorade HueHive --->

View File

@@ -402,7 +402,7 @@ block
blockArrowId4<["Label"]>(down)
blockArrowId5<["Label"]>(x)
blockArrowId6<["Label"]>(y)
blockArrowId7<["Label"]>(x, down)
blockArrowId6<["Label"]>(x, down)
```
```mermaid
@@ -413,7 +413,7 @@ block
blockArrowId4<["Label"]>(down)
blockArrowId5<["Label"]>(x)
blockArrowId6<["Label"]>(y)
blockArrowId7<["Label"]>(x, down)
blockArrowId6<["Label"]>(x, down)
```
#### Example - Space Blocks

View File

@@ -62,7 +62,7 @@ radar-beta
radar-beta
title Restaurant Comparison
axis food["Food Quality"], service["Service"], price["Price"]
axis ambiance["Ambiance"]
axis ambiance["Ambiance"],
curve a["Restaurant A"]{4, 3, 2, 4}
curve b["Restaurant B"]{3, 4, 3, 3}
@@ -78,7 +78,7 @@ radar-beta
radar-beta
title Restaurant Comparison
axis food["Food Quality"], service["Service"], price["Price"]
axis ambiance["Ambiance"]
axis ambiance["Ambiance"],
curve a["Restaurant A"]{4, 3, 2, 4}
curve b["Restaurant B"]{3, 4, 3, 3}

View File

@@ -196,11 +196,7 @@ sequenceDiagram
### Aliases
The actor can have a convenient identifier and a descriptive label. Aliases can be defined in two ways: using external syntax with the `as` keyword, or inline within the configuration object.
#### External Alias Syntax
You can define an alias using the `as` keyword after the participant declaration:
The actor can have a convenient identifier and a descriptive label.
```mermaid-example
sequenceDiagram
@@ -218,78 +214,6 @@ sequenceDiagram
J->>A: Great!
```
The external alias syntax also works with participant stereotype configurations, allowing you to combine type specification with aliases:
```mermaid-example
sequenceDiagram
participant API@{ "type": "boundary" } as Public API
actor DB@{ "type": "database" } as User Database
participant Svc@{ "type": "control" } as Auth Service
API->>Svc: Authenticate
Svc->>DB: Query user
DB-->>Svc: User data
Svc-->>API: Token
```
```mermaid
sequenceDiagram
participant API@{ "type": "boundary" } as Public API
actor DB@{ "type": "database" } as User Database
participant Svc@{ "type": "control" } as Auth Service
API->>Svc: Authenticate
Svc->>DB: Query user
DB-->>Svc: User data
Svc-->>API: Token
```
#### Inline Alias Syntax
Alternatively, you can define an alias directly inside the configuration object using the `"alias"` field. This works with both `participant` and `actor` keywords:
```mermaid-example
sequenceDiagram
participant API@{ "type": "boundary", "alias": "Public API" }
participant Auth@{ "type": "control", "alias": "Auth Service" }
participant DB@{ "type": "database", "alias": "User Database" }
API->>Auth: Login request
Auth->>DB: Query user
DB-->>Auth: User data
Auth-->>API: Access token
```
```mermaid
sequenceDiagram
participant API@{ "type": "boundary", "alias": "Public API" }
participant Auth@{ "type": "control", "alias": "Auth Service" }
participant DB@{ "type": "database", "alias": "User Database" }
API->>Auth: Login request
Auth->>DB: Query user
DB-->>Auth: User data
Auth-->>API: Access token
```
#### Alias Precedence
When both inline alias (in the configuration object) and external alias (using `as` keyword) are provided, the **external alias takes precedence**:
```mermaid-example
sequenceDiagram
participant API@{ "type": "boundary", "alias": "Internal Name" } as External Name
participant DB@{ "type": "database", "alias": "Internal DB" } as External DB
API->>DB: Query
DB-->>API: Result
```
```mermaid
sequenceDiagram
participant API@{ "type": "boundary", "alias": "Internal Name" } as External Name
participant DB@{ "type": "database", "alias": "Internal DB" } as External DB
API->>DB: Query
DB-->>API: Result
```
In the example above, "External Name" and "External DB" will be displayed, not "Internal Name" and "Internal DB".
### Actor Creation and Destruction (v10.3.0+)
It is possible to create and destroy actors by messages. To do so, add a create or destroy directive before the message.

View File

@@ -11,8 +11,7 @@ import unicorn from 'eslint-plugin-unicorn';
import globals from 'globals';
import tseslint from 'typescript-eslint';
/** @type {import('eslint').Linter.FlatConfig[]} */
const config = tseslint.config(
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
...tseslint.configs.stylisticTypeChecked,
@@ -24,7 +23,6 @@ const config = tseslint.config(
'.git/',
'**/generated/',
'**/coverage/',
'.esbuild/dev-explorer/**',
'packages/mermaid/src/config.type.ts',
'packages/mermaid/src/docs/.vitepress/components.d.ts',
],
@@ -223,5 +221,3 @@ const config = tseslint.config(
processor: 'markdown/markdown',
}
);
export default config;

View File

@@ -63,22 +63,21 @@
]
},
"devDependencies": {
"@applitools/eyes-cypress": "^3.56.5",
"@argos-ci/cypress": "^6.2.2",
"@changesets/changelog-github": "^0.5.2",
"@changesets/cli": "^2.29.8",
"@cspell/eslint-plugin": "^9.3.2",
"@cypress/code-coverage": "^3.14.7",
"@applitools/eyes-cypress": "^3.55.2",
"@argos-ci/cypress": "^6.1.3",
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.29.7",
"@cspell/eslint-plugin": "^8.19.4",
"@cypress/code-coverage": "^3.14.6",
"@eslint/js": "^9.26.0",
"@rollup/plugin-typescript": "^12.1.4",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.6",
"@types/express": "^5.0.3",
"@types/js-yaml": "^4.0.9",
"@types/jsdom": "^21.1.7",
"@types/lodash": "^4.17.21",
"@types/lodash": "^4.17.20",
"@types/mdast": "^4.0.4",
"@types/node": "^22.19.1",
"@shoelace-style/shoelace": "^2.20.1",
"@types/node": "^22.18.6",
"@types/rollup-plugin-visualizer": "^5.0.3",
"@vitest/coverage-v8": "^3.2.4",
"@vitest/spy": "^3.2.4",
@@ -89,31 +88,30 @@
"cors": "^2.8.5",
"cpy-cli": "^5.0.0",
"cross-env": "^7.0.3",
"cspell": "^9.3.2",
"cspell": "^9.2.1",
"cypress": "^14.5.4",
"cypress-image-snapshot": "^4.0.1",
"cypress-split": "^1.24.25",
"esbuild": "^0.25.12",
"cypress-split": "^1.24.23",
"esbuild": "^0.25.10",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-cypress": "^5.2.0",
"eslint-plugin-cypress": "^4.3.0",
"eslint-plugin-html": "^8.1.3",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-jsdoc": "^61.1.12",
"eslint-plugin-jest": "^28.14.0",
"eslint-plugin-jsdoc": "^50.8.0",
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-lodash": "^8.0.0",
"eslint-plugin-markdown": "^5.1.0",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-tsdoc": "^0.4.0",
"eslint-plugin-unicorn": "^62.0.0",
"lit": "^3.2.1",
"express": "^5.2.1",
"eslint-plugin-unicorn": "^59.0.1",
"express": "^5.1.0",
"globals": "^16.4.0",
"globby": "^14.1.0",
"husky": "^9.1.7",
"jest": "^30.1.3",
"jison": "^0.4.18",
"js-yaml": "^4.1.1",
"js-yaml": "^4.1.0",
"jsdom": "^26.1.0",
"langium-cli": "3.3.0",
"lint-staged": "^16.1.6",
@@ -123,13 +121,13 @@
"prettier": "^3.6.2",
"prettier-plugin-jsdoc": "^1.3.3",
"rimraf": "^6.0.1",
"rollup-plugin-visualizer": "^6.0.5",
"start-server-and-test": "^2.1.3",
"rollup-plugin-visualizer": "^6.0.3",
"start-server-and-test": "^2.1.2",
"tslib": "^2.8.1",
"tsx": "^4.20.6",
"tsx": "^4.20.5",
"typescript": "~5.7.3",
"typescript-eslint": "^8.38.0",
"vite": "^7.0.8",
"vite": "^7.0.7",
"vite-plugin-istanbul": "^7.0.0",
"vitest": "^3.2.4"
},

View File

@@ -18,9 +18,7 @@
"elk",
"mermaid"
],
"scripts": {
"clean": "rimraf dist"
},
"scripts": {},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"

View File

@@ -19,9 +19,7 @@
"mermaid",
"layout"
],
"scripts": {
"clean": "rimraf dist"
},
"scripts": {},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"

View File

@@ -33,7 +33,7 @@
],
"license": "MIT",
"dependencies": {
"@zenuml/core": "^3.41.6"
"@zenuml/core": "^3.41.4"
},
"devDependencies": {
"mermaid": "workspace:^"

View File

@@ -77,9 +77,9 @@
"d3": "^7.9.0",
"d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.13",
"dayjs": "^1.11.19",
"dompurify": "^3.3.1",
"katex": "^0.16.25",
"dayjs": "^1.11.18",
"dompurify": "^3.2.5",
"katex": "^0.16.22",
"khroma": "^2.1.0",
"lodash-es": "^4.17.21",
"marked": "^16.3.0",
@@ -89,11 +89,11 @@
"uuid": "^11.1.0"
},
"devDependencies": {
"@adobe/jsonschema2md": "^8.0.8",
"@adobe/jsonschema2md": "^8.0.5",
"@iconify/types": "^2.0.0",
"@types/cytoscape": "^3.21.9",
"@types/cytoscape-fcose": "^2.2.5",
"@types/d3-sankey": "^0.12.5",
"@types/cytoscape-fcose": "^2.2.4",
"@types/d3-sankey": "^0.12.4",
"@types/d3-scale": "^4.0.9",
"@types/d3-scale-chromatic": "^3.1.0",
"@types/d3-selection": "^3.0.11",
@@ -101,7 +101,7 @@
"@types/jsdom": "^21.1.7",
"@types/katex": "^0.16.7",
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.10",
"@types/micromatch": "^4.0.9",
"@types/stylis": "^4.2.7",
"@types/uuid": "^10.0.0",
"ajv": "^8.17.1",
@@ -121,9 +121,9 @@
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.1",
"rimraf": "^6.0.1",
"start-server-and-test": "^2.1.3",
"start-server-and-test": "^2.1.2",
"type-fest": "^4.41.0",
"typedoc": "^0.28.15",
"typedoc": "^0.28.13",
"typedoc-plugin-markdown": "^4.8.1",
"typescript": "~5.7.3",
"unist-util-flatmap": "^1.0.0",

Some files were not shown because too many files have changed in this diff Show More