From 90159a49a64b391cd6628f7f45874433e9f7c121 Mon Sep 17 00:00:00 2001 From: agent Date: Mon, 28 Sep 2026 19:07:11 +0800 Subject: [PATCH 1/4] feat(webui): render mermaid diagrams in markdown (slice 23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ```mermaid fences now render as SVG diagrams in file previews and chat messages. Mermaid is lazy-loaded: the 442 KB library lives in its own webpack chunk and only loads when a document contains a mermaid fence. A normal markdown document never pays for it. - registry seam: lib/markdown.ts now exposes registerLanguageRenderer() so future minimax-code-plugin renderers can attach without touching the markdown parser main flow. The default fenced-block shell still wins for unknown languages. A broken plugin renderer is caught by safeLanguageRenderer() and falls back to the default shell — one bad plugin never blanks the document. - mermaid renderer: lib/mermaid-renderer.ts auto-registers the 'mermaid' language. The renderer emits the
…
pair the sanitiser passes through (the allowlist keeps class on pre and div, so the source and placeholder survive). - MarkdownHtml: components/markdown-html.tsx walks the DOM after dangerouslySetInnerHTML, pairs each
  with the following 
, and replaces the placeholder with a portal. No placeholders → no MermaidBlock mounts → no mermaid import → no chunk fetch. - MermaidBlock: components/mermaid-block.tsx dynamic-imports mermaid, configures securityLevel: 'strict', sanitises the SVG through the same allowlist the markdown pipeline uses (DROP script/style/iframe/foreignObject; strip onclick/onload/ onerror/style), and re-renders on class flip (light/dark). - failure state: a parse error shows the error string and a copyable
 of the original source. The rest of the document
  renders normally.
- Chinese labels: fontFamily stack (PingFang SC, Microsoft YaHei,
  Noto Sans CJK SC, Source Han Sans SC) is passed to mermaid and
  mirrored in styles/mermaid.css for the loading/failure UI.
- tests: webapp/test/markdown-registry.test.ts pins the seam
  (default fallback, custom renderers, throwing renderers,
  non-string returns, metadata tokens, auto-register of mermaid,
  source escaping, document-without-mermaid leaves zero
  mermaid-* nodes).
- gates: 1097 webapp tests + 1946 webui server tests + both
  typechecks + check:source all green. pnpm-lock.yaml and
  dependency-licenses.json carry mermaid@11.12.1 / MIT.
- live verification: 14 fixtures (7 markdown files × 2 themes)
  including a valid flowchart, a Chinese Gantt, a sequence
  diagram, an invalid diagram, a mixed document, a hostile
  diagram (carries \n```");
+    // The script tag is escaped — the source pre's content is safe
+    // text, not a live element.
+    assert.match(html, /<script>alert\(1\)<\/script>/);
+    assert.doesNotMatch(html, /\n```");
+    // The mermaid placeholder pair survives so the walker still has
+    // something to mount.
+    assert.match(html, /mermaid-source/);
+    assert.match(html, /mermaid-block/);
+    // The literal "alert(1)" survives verbatim — the source is
+    // preserved as text, just with the angle brackets escaped. The
+    // Node path escapes the entire HTML to entities; the browser
+    // path strips