mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 01:29:56 +00:00
chore(ui): update Mermaid to 12.0.0 (#162618)
This commit is contained in:
parent
4dbd5fb852
commit
9faca1d104
13 changed files with 271 additions and 30 deletions
|
|
@ -1,6 +1,6 @@
|
|||
Mermaid Parser 1.2.1 (@mermaid-js/parser)
|
||||
Mermaid Parser 2.0.0 (@mermaid-js/parser)
|
||||
|
||||
Source: https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.1.tgz
|
||||
Source: https://registry.npmjs.org/@mermaid-js/parser/-/parser-2.0.0.tgz
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Mermaid Parser 1.2.1 (@mermaid-js/parser)
|
||||
Mermaid Parser 2.0.0 (@mermaid-js/parser)
|
||||
|
||||
Source: https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.1.tgz
|
||||
Source: https://registry.npmjs.org/@mermaid-js/parser/-/parser-2.0.0.tgz
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
|
|
|
|||
|
|
@ -2430,9 +2430,9 @@ SOFTWARE.
|
|||
Mermaid Parser
|
||||
========================================================================
|
||||
|
||||
Mermaid Parser 1.2.1 (@mermaid-js/parser)
|
||||
Mermaid Parser 2.0.0 (@mermaid-js/parser)
|
||||
|
||||
Source: https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.1.tgz
|
||||
Source: https://registry.npmjs.org/@mermaid-js/parser/-/parser-2.0.0.tgz
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"dependencies": {
|
||||
"@openclaw/normalization-core": "workspace:*",
|
||||
"dompurify": "3.4.16",
|
||||
"mermaid": "11.17.2"
|
||||
"mermaid": "12.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "8.3.0"
|
||||
|
|
|
|||
3
packages/mermaid-renderer/src/mermaid-script-url.ts
Normal file
3
packages/mermaid-renderer/src/mermaid-script-url.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
const mermaidScriptUrl = "";
|
||||
|
||||
export default mermaidScriptUrl;
|
||||
3
packages/mermaid-renderer/src/mermaid.min.ts
Normal file
3
packages/mermaid-renderer/src/mermaid.min.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import mermaid from "mermaid";
|
||||
|
||||
Object.assign(globalThis, { mermaid });
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { asRecord } from "@openclaw/normalization-core/record-coerce";
|
||||
import createDOMPurify from "dompurify";
|
||||
import type { MermaidConfig } from "mermaid";
|
||||
import mermaidScriptUrl from "mermaid/dist/mermaid.min.js?url&no-inline";
|
||||
import frameScriptUrl from "./frame.js?url&no-inline";
|
||||
import mermaidScriptUrl from "./mermaid-script-url.ts";
|
||||
|
||||
export type MermaidTheme = {
|
||||
background: string;
|
||||
|
|
@ -185,6 +185,9 @@ function sanitizeMermaidSvg(source: string, backgroundColor: string): string {
|
|||
}
|
||||
|
||||
function createMermaidFrame(): MermaidFrame {
|
||||
if (!mermaidScriptUrl) {
|
||||
throw new MermaidTransientError("The Mermaid classic script URL was not injected.");
|
||||
}
|
||||
const frame = document.createElement("iframe");
|
||||
const channel = new MessageChannel();
|
||||
let readyResolve: () => void;
|
||||
|
|
@ -335,6 +338,12 @@ export function renderMermaidSvg(source: string, theme: MermaidTheme): Promise<s
|
|||
maxEdges: MAX_EDGES,
|
||||
arrowMarkerAbsolute: false,
|
||||
theme: "base",
|
||||
look: "classic",
|
||||
flowchart: { layout: "dagre" },
|
||||
state: { layout: "dagre" },
|
||||
class: { layout: "dagre" },
|
||||
er: { layout: "dagre" },
|
||||
requirement: { layout: "dagre" },
|
||||
themeCSS: "",
|
||||
fontFamily: theme.fontFamily,
|
||||
themeVariables: {
|
||||
|
|
|
|||
116
packages/mermaid-renderer/vite-plugin.ts
Normal file
116
packages/mermaid-renderer/vite-plugin.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import { readFile } from "node:fs/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import type { Plugin } from "vite";
|
||||
import { build } from "vite";
|
||||
|
||||
const MERMAID_CORE_SUFFIX = "/mermaid/dist/mermaid.core.mjs";
|
||||
const MERMAID_LAYOUT_CHUNK = /\/mermaid\/dist\/chunks\/mermaid\.core\/chunk-[^/]+\.mjs$/u;
|
||||
const MERMAID_SCRIPT_URL_SUFFIX = "/mermaid-renderer/src/mermaid-script-url.ts";
|
||||
const DEV_MERMAID_SCRIPT_PATH = "/@openclaw/mermaid.min.js";
|
||||
|
||||
function resolveMermaidScriptEntry(): string {
|
||||
return fileURLToPath(new URL("./src/mermaid.min.ts", import.meta.url));
|
||||
}
|
||||
|
||||
async function buildDevMermaidScript(): Promise<string> {
|
||||
const result = await build({
|
||||
configFile: false,
|
||||
logLevel: "error",
|
||||
plugins: [mermaidClassicBundlePlugin()],
|
||||
build: {
|
||||
write: false,
|
||||
minify: true,
|
||||
lib: {
|
||||
entry: resolveMermaidScriptEntry(),
|
||||
name: "OpenClawMermaid",
|
||||
formats: ["iife"],
|
||||
},
|
||||
},
|
||||
});
|
||||
const builds = Array.isArray(result) ? result : "output" in result ? [result] : [];
|
||||
if (builds.length === 0) {
|
||||
throw new Error("Vite started a watcher instead of building the Mermaid classic script.");
|
||||
}
|
||||
const outputs = builds.flatMap((entry) => entry.output);
|
||||
const script = outputs.find((entry) => entry.type === "chunk" && entry.isEntry);
|
||||
if (!script || script.type !== "chunk") {
|
||||
throw new Error("Vite did not emit the Mermaid classic script.");
|
||||
}
|
||||
return script.code;
|
||||
}
|
||||
|
||||
export function mermaidClassicBundlePlugin(): Plugin {
|
||||
let command: "build" | "serve";
|
||||
let devScript: Promise<string> | undefined;
|
||||
return {
|
||||
name: "openclaw-mermaid-classic-bundle",
|
||||
enforce: "pre",
|
||||
configResolved(config) {
|
||||
command = config.command;
|
||||
},
|
||||
configureServer(server) {
|
||||
server.middlewares.use((request, response, next) => {
|
||||
if (request.url?.split("?", 1)[0] !== DEV_MERMAID_SCRIPT_PATH) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
devScript ??= buildDevMermaidScript();
|
||||
void devScript
|
||||
.then((script) => {
|
||||
response.statusCode = 200;
|
||||
response.setHeader("Content-Type", "text/javascript; charset=utf-8");
|
||||
response.setHeader("Cache-Control", "no-store");
|
||||
response.end(script);
|
||||
})
|
||||
.catch(next);
|
||||
});
|
||||
},
|
||||
async load(id) {
|
||||
const normalizedId = id.replaceAll("\\", "/");
|
||||
if (normalizedId.endsWith(MERMAID_SCRIPT_URL_SUFFIX)) {
|
||||
if (command === "serve") {
|
||||
return `export default ${JSON.stringify(DEV_MERMAID_SCRIPT_PATH)};`;
|
||||
}
|
||||
const scriptEntry = resolveMermaidScriptEntry();
|
||||
return `import url from ${JSON.stringify(`${scriptEntry}?worker&url`)}; export default url;`;
|
||||
}
|
||||
if (!normalizedId.endsWith(MERMAID_CORE_SUFFIX) && !MERMAID_LAYOUT_CHUNK.test(normalizedId)) {
|
||||
return null;
|
||||
}
|
||||
const source = await readFile(id.replace(/\?.*$/u, ""), "utf8");
|
||||
if (normalizedId.endsWith(MERMAID_CORE_SUFFIX)) {
|
||||
const withoutNewDiagrams = source
|
||||
.replace(
|
||||
" registerLazyLoadedDiagrams(\n afDetector_default,\n",
|
||||
" registerLazyLoadedDiagrams(\n",
|
||||
)
|
||||
.replace(",\n usecase\n );", "\n );");
|
||||
if (
|
||||
withoutNewDiagrams === source ||
|
||||
withoutNewDiagrams.includes("registerLazyLoadedDiagrams(\n afDetector_default,") ||
|
||||
withoutNewDiagrams.includes(",\n usecase\n );")
|
||||
) {
|
||||
throw new Error("Mermaid 12 legacy diagram registry contract changed.");
|
||||
}
|
||||
return withoutNewDiagrams;
|
||||
}
|
||||
if (!source.includes("elkLayoutLoaders")) {
|
||||
return null;
|
||||
}
|
||||
const withoutElk = source
|
||||
.replace(
|
||||
/import\("\.\/elk-[^"]+\.mjs"\)/u,
|
||||
'Promise.reject(new Error("ELK layout is unavailable."))',
|
||||
)
|
||||
.replace(/,\s*\.\.\.elkLayoutLoaders\(\)/u, "");
|
||||
if (
|
||||
withoutElk === source ||
|
||||
withoutElk.includes('import("./elk-') ||
|
||||
withoutElk.includes("...elkLayoutLoaders()")
|
||||
) {
|
||||
throw new Error("Mermaid 12 ELK loader contract changed.");
|
||||
}
|
||||
return withoutElk;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { copyFile, mkdir } from "node:fs/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { defineConfig } from "vite";
|
||||
import { mermaidClassicBundlePlugin } from "./vite-plugin.ts";
|
||||
|
||||
const output = fileURLToPath(new URL("../../apps/shared/mermaid/assets/mermaid", import.meta.url));
|
||||
|
||||
|
|
@ -8,6 +9,13 @@ export default defineConfig({
|
|||
base: "./",
|
||||
// Native builds run directly from source, before workspace dist files exist.
|
||||
resolve: { tsconfigPaths: true },
|
||||
worker: {
|
||||
format: "iife",
|
||||
plugins: () => [mermaidClassicBundlePlugin()],
|
||||
rolldownOptions: {
|
||||
output: { codeSplitting: false },
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: output,
|
||||
emptyOutDir: true,
|
||||
|
|
@ -20,6 +28,7 @@ export default defineConfig({
|
|||
},
|
||||
},
|
||||
plugins: [
|
||||
mermaidClassicBundlePlugin(),
|
||||
{
|
||||
name: "native-mermaid-document",
|
||||
async closeBundle() {
|
||||
|
|
|
|||
79
pnpm-lock.yaml
generated
79
pnpm-lock.yaml
generated
|
|
@ -2703,8 +2703,8 @@ importers:
|
|||
specifier: 3.4.16
|
||||
version: 3.4.16
|
||||
mermaid:
|
||||
specifier: 11.17.2
|
||||
version: 11.17.2
|
||||
specifier: 12.0.0
|
||||
version: 12.0.0
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: 8.3.0
|
||||
|
|
@ -3432,9 +3432,21 @@ packages:
|
|||
'@cacheable/utils@2.5.0':
|
||||
resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==}
|
||||
|
||||
'@chevrotain/cst-dts-gen@11.1.2':
|
||||
resolution: {integrity: sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q==}
|
||||
|
||||
'@chevrotain/gast@11.1.2':
|
||||
resolution: {integrity: sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g==}
|
||||
|
||||
'@chevrotain/regexp-to-ast@11.1.2':
|
||||
resolution: {integrity: sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw==}
|
||||
|
||||
'@chevrotain/types@11.1.2':
|
||||
resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==}
|
||||
|
||||
'@chevrotain/utils@11.1.2':
|
||||
resolution: {integrity: sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==}
|
||||
|
||||
'@clack/core@1.5.1':
|
||||
resolution: {integrity: sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==}
|
||||
engines: {node: '>= 20.12.0'}
|
||||
|
|
@ -4402,8 +4414,9 @@ packages:
|
|||
'@mdx-js/mdx@3.1.1':
|
||||
resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==}
|
||||
|
||||
'@mermaid-js/parser@1.2.1':
|
||||
resolution: {integrity: sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==}
|
||||
'@mermaid-js/parser@2.0.0':
|
||||
resolution: {integrity: sha512-K8BeapFUfrfxbRAUQAG5oBOCwo1+bNWzaVnPxTCutkfrTBn6T/j91FIhqxWJ32SUeQ9T3iy1zcmPZ5ROZEvDrg==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
|
||||
'@microsoft/mxc-sdk@0.8.0':
|
||||
resolution: {integrity: sha512-pnf5QsASwp+qtRi5uth2GDjwuyG0rHWRpxCf3RbAjQ4wDTNfBX/9l0A+RVZspU2agpF3/11uWB1JisIS7WrNYg==}
|
||||
|
|
@ -6679,6 +6692,9 @@ packages:
|
|||
charenc@0.0.2:
|
||||
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
|
||||
|
||||
chevrotain@11.1.2:
|
||||
resolution: {integrity: sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg==}
|
||||
|
||||
chownr@3.0.0:
|
||||
resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
|
||||
engines: {node: '>=18'}
|
||||
|
|
@ -7181,6 +7197,9 @@ packages:
|
|||
ee-first@1.1.1:
|
||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||
|
||||
elkjs@0.9.3:
|
||||
resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==}
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
|
||||
|
|
@ -7389,9 +7408,6 @@ packages:
|
|||
resolution: {integrity: sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==}
|
||||
hasBin: true
|
||||
|
||||
fastdom@1.0.12:
|
||||
resolution: {integrity: sha512-LB+xjSTEbjHE1cWsxu+tN2Xqr1kpi+V9aADI7sVM5ZMaXyYGPHULQMzpJMYqOTULK/73pUkWVzzObFRBkPr+hg==}
|
||||
|
||||
fastest-levenshtein@1.0.16:
|
||||
resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==}
|
||||
engines: {node: '>= 4.9.1'}
|
||||
|
|
@ -8433,8 +8449,9 @@ packages:
|
|||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
mermaid@11.17.2:
|
||||
resolution: {integrity: sha512-V6K3C8EBdEsPFZXSKMJe6ppQOENxuHARr9GvHX4hh47lAbhMRD9qf4oEK7LoaRQxULMa80/qt5gHO73aCleBBg==}
|
||||
mermaid@12.0.0:
|
||||
resolution: {integrity: sha512-/wQXC9iBxoGV8p3erbvaXs9h77VyLDBH6GdayVjj3hEcSQhFU4N1WUhUppotCEqlIxI2pRMwjwBSwTB1MfZBgQ==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
|
||||
micromark-core-commonmark@2.0.3:
|
||||
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
|
||||
|
|
@ -9562,9 +9579,6 @@ packages:
|
|||
streamx@2.28.1:
|
||||
resolution: {integrity: sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==}
|
||||
|
||||
strictdom@1.0.1:
|
||||
resolution: {integrity: sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==}
|
||||
|
||||
string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -11016,8 +11030,23 @@ snapshots:
|
|||
hashery: 1.5.1
|
||||
keyv: 5.6.0
|
||||
|
||||
'@chevrotain/cst-dts-gen@11.1.2':
|
||||
dependencies:
|
||||
'@chevrotain/gast': 11.1.2
|
||||
'@chevrotain/types': 11.1.2
|
||||
lodash-es: 4.18.1
|
||||
|
||||
'@chevrotain/gast@11.1.2':
|
||||
dependencies:
|
||||
'@chevrotain/types': 11.1.2
|
||||
lodash-es: 4.18.1
|
||||
|
||||
'@chevrotain/regexp-to-ast@11.1.2': {}
|
||||
|
||||
'@chevrotain/types@11.1.2': {}
|
||||
|
||||
'@chevrotain/utils@11.1.2': {}
|
||||
|
||||
'@clack/core@1.5.1':
|
||||
dependencies:
|
||||
fast-wrap-ansi: 0.2.2
|
||||
|
|
@ -12062,7 +12091,7 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@mermaid-js/parser@1.2.1':
|
||||
'@mermaid-js/parser@2.0.0':
|
||||
dependencies:
|
||||
'@chevrotain/types': 11.1.2
|
||||
|
||||
|
|
@ -14267,6 +14296,15 @@ snapshots:
|
|||
charenc@0.0.2:
|
||||
optional: true
|
||||
|
||||
chevrotain@11.1.2:
|
||||
dependencies:
|
||||
'@chevrotain/cst-dts-gen': 11.1.2
|
||||
'@chevrotain/gast': 11.1.2
|
||||
'@chevrotain/regexp-to-ast': 11.1.2
|
||||
'@chevrotain/types': 11.1.2
|
||||
'@chevrotain/utils': 11.1.2
|
||||
lodash-es: 4.18.1
|
||||
|
||||
chownr@3.0.0: {}
|
||||
|
||||
chrome-devtools-mcp@1.9.0(patch_hash=832ce1d2ff002b44edb93bae88858d6edd8bc833acc87dd0fe82454d8834e340): {}
|
||||
|
|
@ -14739,6 +14777,8 @@ snapshots:
|
|||
|
||||
ee-first@1.1.1: {}
|
||||
|
||||
elkjs@0.9.3: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
empathic@2.0.1: {}
|
||||
|
|
@ -15000,10 +15040,6 @@ snapshots:
|
|||
strnum: 2.4.2
|
||||
xml-naming: 0.3.0
|
||||
|
||||
fastdom@1.0.12:
|
||||
dependencies:
|
||||
strictdom: 1.0.1
|
||||
|
||||
fastest-levenshtein@1.0.16: {}
|
||||
|
||||
fastq@1.20.3:
|
||||
|
|
@ -16244,13 +16280,14 @@ snapshots:
|
|||
|
||||
merge2@1.4.1: {}
|
||||
|
||||
mermaid@11.17.2:
|
||||
mermaid@12.0.0:
|
||||
dependencies:
|
||||
'@braintree/sanitize-url': 7.1.2
|
||||
'@iconify/utils': 3.1.7
|
||||
'@mermaid-js/parser': 1.2.1
|
||||
'@mermaid-js/parser': 2.0.0
|
||||
'@types/d3': 7.4.3
|
||||
'@upsetjs/venn.js': 2.0.0
|
||||
chevrotain: 11.1.2
|
||||
cytoscape: 3.34.3
|
||||
cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.3)
|
||||
cytoscape-fcose: 2.2.0(cytoscape@3.34.3)
|
||||
|
|
@ -16259,8 +16296,8 @@ snapshots:
|
|||
dagre-d3-es: 7.0.14
|
||||
dayjs: 1.11.23
|
||||
dompurify: 3.4.16
|
||||
elkjs: 0.9.3
|
||||
es-toolkit: 1.52.0
|
||||
fastdom: 1.0.12
|
||||
katex: 0.16.47
|
||||
khroma: 2.1.0
|
||||
marked: 16.4.2
|
||||
|
|
@ -17496,8 +17533,6 @@ snapshots:
|
|||
- bare-abort-controller
|
||||
- react-native-b4a
|
||||
|
||||
strictdom@1.0.1: {}
|
||||
|
||||
string-width@4.2.3:
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
|
|
|
|||
|
|
@ -15,6 +15,16 @@ function parseSvg(svg: string): Element {
|
|||
return new DOMParser().parseFromString(svg, "image/svg+xml").documentElement;
|
||||
}
|
||||
|
||||
function parseTranslate(element: Element): { x: number; y: number } {
|
||||
const match = /^translate\(([-\d.]+), ([-\d.]+)\)$/u.exec(
|
||||
element.getAttribute("transform") ?? "",
|
||||
);
|
||||
if (!match) {
|
||||
throw new Error("Expected a translated Mermaid element.");
|
||||
}
|
||||
return { x: Number(match[1]), y: Number(match[2]) };
|
||||
}
|
||||
|
||||
// The renderer belongs to the page, so these cases share its warm engine.
|
||||
// Error cases still dispose it; the file boundary owns final page teardown.
|
||||
afterAll(() => {
|
||||
|
|
@ -100,6 +110,18 @@ G --> S`;
|
|||
["sequence", "sequenceDiagram\nAlice->>Bob: Hello\nBob-->>Alice: Ready", "Alice"],
|
||||
["class", "classDiagram\nVehicle <|-- Car\nVehicle : +move()", "Vehicle"],
|
||||
["state", "stateDiagram-v2\n[*] --> Ready\nReady --> Running\nRunning --> [*]", "Running"],
|
||||
["mindmap", "mindmap\n root((OpenClaw))\n Gateway\n Runtime", "Gateway"],
|
||||
[
|
||||
"swimlane",
|
||||
"swimlane-beta LR\n subgraph Gateway\n Admit[Admit]\n end\n subgraph Runtime\n Execute[Execute]\n end\n Admit --> Execute",
|
||||
"Execute",
|
||||
],
|
||||
[
|
||||
"architecture",
|
||||
"architecture-beta\nservice gateway(server)[Gateway]\nservice runtime(server)[Runtime]\ngateway:R -- L:runtime",
|
||||
"Gateway",
|
||||
],
|
||||
["math", 'flowchart LR\nA["$$x^2$$"] --> B[Result]', "Result"],
|
||||
])("preserves %s diagram content in the passive image", async (_name, source, label) => {
|
||||
const root = parseSvg(await renderMermaidSvg(source, theme));
|
||||
expect(root.textContent).toContain(label);
|
||||
|
|
@ -107,6 +129,39 @@ G --> S`;
|
|||
expect(root.querySelector("style,script,a,image,foreignObject,[style],[href]")).toBeNull();
|
||||
});
|
||||
|
||||
it("preserves diagram-specific mindmap and swimlane layouts", async () => {
|
||||
const mindmap = parseSvg(
|
||||
await renderMermaidSvg(
|
||||
"mindmap\n root((OpenClaw))\n Gateway\n Admission\n Runtime\n Execution",
|
||||
theme,
|
||||
),
|
||||
);
|
||||
const swimlane = parseSvg(
|
||||
await renderMermaidSvg(
|
||||
"swimlane-beta LR\n subgraph Gateway\n Admit[Admit]\n end\n subgraph Runtime\n Execute[Execute]\n end\n Admit --> Execute",
|
||||
theme,
|
||||
),
|
||||
);
|
||||
const mindmapNodes = Array.from(
|
||||
mindmap.querySelectorAll<SVGGElement>('[id*="-node_"][transform]'),
|
||||
);
|
||||
const root = mindmapNodes.find((node) => node.id.endsWith("-node_0"));
|
||||
expect(root).toBeDefined();
|
||||
const rootY = parseTranslate(root!).y;
|
||||
const childY = mindmapNodes
|
||||
.filter((node) => node !== root)
|
||||
.map((node) => parseTranslate(node).y);
|
||||
expect(childY.some((y) => y < rootY)).toBe(true);
|
||||
expect(childY.some((y) => y > rootY)).toBe(true);
|
||||
|
||||
expect(swimlane.querySelector("#Gateway")).not.toBeNull();
|
||||
expect(swimlane.querySelector("#Runtime")).not.toBeNull();
|
||||
const admit = parseTranslate(swimlane.querySelector('[id*="-flowchart-Admit-"]')!);
|
||||
const execute = parseTranslate(swimlane.querySelector('[id*="-flowchart-Execute-"]')!);
|
||||
expect(admit.x).toBe(execute.x);
|
||||
expect(admit.y).not.toBe(execute.y);
|
||||
});
|
||||
|
||||
it("enforces host source and edge limits despite source configuration, then recovers", async () => {
|
||||
await expect(renderMermaidSvg("x".repeat(20_001), theme)).rejects.toThrow(/characters/u);
|
||||
const source = `%%{init: {"maxEdges": 1000}}%%\nflowchart LR\n${Array.from({ length: 201 }, (_, index) => `A${index} --> A${index + 1}`).join("\n")}`;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { fileURLToPath } from "node:url";
|
|||
import { brotliCompressSync, constants as zlibConstants } from "node:zlib";
|
||||
import { gzip } from "pako";
|
||||
import type { Plugin, ResolveModulePreloadDependenciesFn, UserConfig } from "vite";
|
||||
import { mermaidClassicBundlePlugin } from "../packages/mermaid-renderer/vite-plugin.ts";
|
||||
import { CONTROL_UI_LOCALE_ENTRIES } from "../scripts/lib/control-ui-i18n-config.ts";
|
||||
import {
|
||||
CONTROL_UI_ASSET_MANIFEST_FILENAME,
|
||||
|
|
@ -626,6 +627,13 @@ export default function controlUiViteConfig(
|
|||
};
|
||||
return {
|
||||
base,
|
||||
worker: {
|
||||
format: "iife",
|
||||
plugins: () => [mermaidClassicBundlePlugin()],
|
||||
rolldownOptions: {
|
||||
output: { codeSplitting: false },
|
||||
},
|
||||
},
|
||||
define: {
|
||||
"globalThis.OPENCLAW_CONTROL_UI_BUILD_INFO": JSON.stringify(buildInfo),
|
||||
"globalThis.OPENCLAW_UI_DEV_GATEWAY": devGateway
|
||||
|
|
@ -678,6 +686,7 @@ export default function controlUiViteConfig(
|
|||
...(devGateway ? { proxy: devGateway.proxy } : {}),
|
||||
},
|
||||
plugins: [
|
||||
mermaidClassicBundlePlugin(),
|
||||
controlUiIsolatedDesktopRuntimePlugin(),
|
||||
{
|
||||
name: "control-ui-static-import-preloads",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { chromium } from "playwright";
|
|||
import type { Plugin } from "vite";
|
||||
import { defineConfig, defineProject, type ViteUserConfig } from "vitest/config";
|
||||
import type { Vitest } from "vitest/node";
|
||||
import { mermaidClassicBundlePlugin } from "../packages/mermaid-renderer/vite-plugin.ts";
|
||||
import {
|
||||
filterFilesByPatterns,
|
||||
intersectIncludePatterns,
|
||||
|
|
@ -212,6 +213,7 @@ export function createUiBrowserVitestConfig(env = process.env): ViteUserConfig {
|
|||
return defineProject({
|
||||
root: here,
|
||||
plugins: [
|
||||
mermaidClassicBundlePlugin(),
|
||||
controlUiLocaleModulesPlugin(),
|
||||
createVitestProjectCachePlugin(),
|
||||
createRedactingReporterPlugin(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue