chore(ui): update Mermaid to 12.0.0 (#162618)

This commit is contained in:
Vincent Koc 2026-10-01 19:22:10 +07:00 • committed by GitHub
parent 4dbd5fb852
commit 9faca1d104
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 271 additions and 30 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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"

View file

@ -0,0 +1,3 @@
const mermaidScriptUrl = "";
export default mermaidScriptUrl;

View file

@ -0,0 +1,3 @@
import mermaid from "mermaid";
Object.assign(globalThis, { mermaid });

View file

@ -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: {

View 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;
},
};
}

View file

@ -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
View file

@ -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

View file

@ -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")}`;

View file

@ -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",

View file

@ -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(),