mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-24 21:26:45 +01:00
Compare commits
9 Commits
dev-tool
...
sidv/iconi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8a0a61b5e | ||
|
|
525e630ebc | ||
|
|
928ae32063 | ||
|
|
25d96a90de | ||
|
|
c24a1fb1b9 | ||
|
|
c607163999 | ||
|
|
df21885a27 | ||
|
|
172030377f | ||
|
|
a23c2baed8 |
@@ -28,6 +28,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [
|
||||
'packet',
|
||||
'architecture',
|
||||
'radar',
|
||||
'icons',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Prevent HTML tags from being escaped in sandbox label rendering
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix(treemap): Fixed treemap classDef style application to properly apply user-defined styles
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
feat: add alias support for new participant syntax of sequence diagrams
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Support ComponentQueue_Ext to prevent parsing error
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: validate dates and tick interval to prevent UI freeze/crash in gantt diagramtype
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix: Mindmap rendering issue when the number of Level 2 nodes exceeds 11
|
||||
@@ -1,5 +1,3 @@
|
||||
!viewbox
|
||||
# It should be viewBox
|
||||
# This file contains coding related terms
|
||||
ALPHANUM
|
||||
antiscript
|
||||
|
||||
@@ -64,7 +64,6 @@ rscratch
|
||||
shiki
|
||||
Slidev
|
||||
sparkline
|
||||
speccharts
|
||||
sphinxcontrib
|
||||
ssim
|
||||
stylis
|
||||
|
||||
@@ -22,6 +22,7 @@ mermaidchart
|
||||
mermaidjs
|
||||
mindmap
|
||||
mindmaps
|
||||
mmdc
|
||||
mrtree
|
||||
multigraph
|
||||
nodesep
|
||||
|
||||
@@ -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 Lit’s 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 doesn’t apply”, especially for simple class selectors.
|
||||
- Fast check:
|
||||
- DevTools console: `document.querySelector('dev-explorer-app')?.shadowRoot`
|
||||
- If non-null, global CSS won’t style inside it.
|
||||
- Or: right-click an element you expect styled → “Reveal in Elements” → see if it’s under `#shadow-root`.
|
||||
|
||||
- **“Light DOM child inside shadow DOM parent” trap**
|
||||
- Even if a child component uses `createRenderRoot() { return this; }`,
|
||||
if it’s _rendered inside the parent’s shadow root_, it’s still effectively in shadow for document styles.
|
||||
|
||||
- **Dev loop trap (CSS-only changes don’t trigger reload)**
|
||||
- The server watches TypeScript bundle inputs + `.mmd` files; static `/dev/styles.css` previously didn’t 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 don’t 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.
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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`));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
2
.github/workflows/e2e-timings.yml
vendored
2
.github/workflows/e2e-timings.yml
vendored
@@ -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
|
||||
|
||||
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -42,4 +42,5 @@ jobs:
|
||||
publish: pnpm changeset:publish
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
||||
2
.github/workflows/scorecard.yml
vendored
2
.github/workflows/scorecard.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/update-browserlist.yml
vendored
2
.github/workflows/update-browserlist.yml
vendored
@@ -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
|
||||
|
||||
7
.github/workflows/validate-lockfile.yml
vendored
7
.github/workflows/validate-lockfile.yml
vendored
@@ -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: |
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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")
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
240
cypress/integration/rendering/icons.spec.ts
Normal file
240
cypress/integration/rendering/icons.spec.ts
Normal 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
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -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 */
|
||||
});
|
||||
|
||||
@@ -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 } }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 }
|
||||
@@ -1,3 +0,0 @@
|
||||
flowchart LR
|
||||
A e1@==> B
|
||||
e1@{ animate: true }
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart TB
|
||||
subgraph container_Beta
|
||||
process_C
|
||||
end
|
||||
|
||||
process_B-->|via_AWSBatch|container_Beta
|
||||
@@ -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!"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}
|
||||
@@ -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
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
|
||||
---
|
||||
flowchart LR
|
||||
%% subgraph s1["Untitled subgraph"]
|
||||
C["Evaluate"]
|
||||
%% end
|
||||
|
||||
B --> C
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
flowchart:
|
||||
//curve: linear
|
||||
---
|
||||
flowchart LR
|
||||
%% A ==> B
|
||||
%% A2 --> B2
|
||||
A{A} --> B((Bo boo)) & B & B & B
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
subgraph s1["Untitled subgraph"]
|
||||
n1["Evaluate"]
|
||||
n2["Option 1"]
|
||||
end
|
||||
n1 -- One --> n2
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
A{A} --> B & C
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
config:
|
||||
layout: elk
|
||||
---
|
||||
flowchart LR
|
||||
A{A} --> B & C
|
||||
subgraph "subbe"
|
||||
A
|
||||
end
|
||||
@@ -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}
|
||||
@@ -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 }
|
||||
@@ -1,2 +0,0 @@
|
||||
flowchart LR
|
||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||
@@ -1,3 +0,0 @@
|
||||
flowchart LR
|
||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
|
||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
flowchart LR
|
||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
|
||||
@@ -1,2 +0,0 @@
|
||||
flowchart LR
|
||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||
@@ -1,3 +0,0 @@
|
||||
flowchart LR
|
||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
|
||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
flowchart LR
|
||||
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
|
||||
@@ -1,2 +0,0 @@
|
||||
flowchart LR
|
||||
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||
@@ -1,3 +0,0 @@
|
||||
flowchart LR
|
||||
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
|
||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,3 +0,0 @@
|
||||
flowchart LR
|
||||
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
|
||||
style A fill:#f9f,stroke:#333,stroke-width:4px
|
||||
@@ -1,3 +0,0 @@
|
||||
kanban
|
||||
id2[In progress]
|
||||
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
|
||||
@@ -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]
|
||||
@@ -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")
|
||||
@@ -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<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 },
|
||||
|
||||
@@ -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
|
||||
<<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!"
|
||||
</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>
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 --->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
46
package.json
46
package.json
@@ -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"
|
||||
},
|
||||
|
||||
@@ -18,9 +18,7 @@
|
||||
"elk",
|
||||
"mermaid"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"scripts": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mermaid-js/mermaid"
|
||||
|
||||
@@ -19,9 +19,7 @@
|
||||
"mermaid",
|
||||
"layout"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"scripts": {},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mermaid-js/mermaid"
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zenuml/core": "^3.41.6"
|
||||
"@zenuml/core": "^3.41.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mermaid": "workspace:^"
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user