mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-09 17:29:12 +00:00
feat(web): add Mermaid diagram rendering and off-thread KaTeX/Mermaid workers (#1226)
* feat(web): add Mermaid diagram rendering and off-thread KaTeX/Mermaid workers Enable Mermaid diagram support in the web chat via markstream-vue's enableMermaid(). Set up Web Workers for both KaTeX rendering and Mermaid parsing using markstream-vue's pre-built workers (katexRenderer.worker, mermaidParser.worker), keeping heavy computation off the main thread during live streaming. * fix(web): skip Mermaid SVG subtrees in file link and markdown link rewriters processFileLinks() uses a TreeWalker that scans all text nodes in mdRef. Mermaid diagrams render as inline SVG, and diagram labels containing file-path-like strings (e.g. src/App.vue) would be replaced with HTML <button> elements inside SVG <text> nodes, corrupting the rendered diagram. processMarkdownLinks() similarly queries a[href] inside SVGs; while isLocalLink() mostly filters these out, explicitly skipping SVG subtrees is safer. Add svg to the closest() exclusion in processFileLinks(), and skip links inside svg in processMarkdownLinks(). * fix(web): satisfy worker import lint * chore: align mermaid dependency versions * chore: change mermaid workers changeset to patch --------- Co-authored-by: qer <wbxl2000@outlook.com>
This commit is contained in:
parent
8ac337a2b2
commit
7f05f589e7
7 changed files with 70 additions and 4 deletions
5
.changeset/web-mermaid-workers.md
Normal file
5
.changeset/web-mermaid-workers.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@moonshot-ai/kimi-code": patch
|
||||
---
|
||||
|
||||
Add Mermaid diagram rendering to the web chat. Fenced `mermaid` blocks in assistant responses now render as diagrams. KaTeX math and Mermaid diagram parsing also run in Web Workers to keep the UI responsive during live streaming.
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
"@xterm/xterm": "^6.0.0",
|
||||
"katex": "^0.17.0",
|
||||
"markstream-vue": "^1.0.4",
|
||||
"mermaid": "^11.15.0",
|
||||
"shiki": "^4.3.0",
|
||||
"stream-markdown": "^0.0.16",
|
||||
"vue": "^3.5.35",
|
||||
|
|
|
|||
|
|
@ -2,13 +2,23 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, inject, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { MarkdownRender, enableKatex } from 'markstream-vue';
|
||||
import {
|
||||
MarkdownRender,
|
||||
enableKatex,
|
||||
enableMermaid,
|
||||
setKaTeXWorker,
|
||||
clearKaTeXWorker,
|
||||
setMermaidWorker,
|
||||
clearMermaidWorker,
|
||||
} from 'markstream-vue';
|
||||
import type { MarkdownIt } from 'markstream-vue';
|
||||
import { useIsDark } from '../../composables/useIsDark';
|
||||
import type { FilePreviewRequest } from '../../types';
|
||||
import { collectFilePathAliases, findFilePathLinks } from '../../lib/filePathLinks';
|
||||
import { markdownRenderPlan } from '../../lib/markdownPerformance';
|
||||
import { copyTextToClipboard } from '../../lib/clipboard';
|
||||
import * as katexWorkerModule from 'markstream-vue/workers/katexRenderer.worker?worker&type=module';
|
||||
import * as mermaidWorkerModule from 'markstream-vue/workers/mermaidParser.worker?worker&type=module';
|
||||
// px-based CSS build (our app is px, not rem). Imported here so the styles
|
||||
// load wherever Markdown is used; scoped overrides below re-skin it to
|
||||
// Terminal Pro. Importing the same file from multiple components is a no-op
|
||||
|
|
@ -23,6 +33,35 @@ import 'markstream-vue/index.px.css';
|
|||
import 'katex/dist/katex.min.css';
|
||||
enableKatex();
|
||||
|
||||
// Mermaid diagram rendering. enableMermaid() registers the default
|
||||
// `import('mermaid')` loader — same pattern as enableKatex(). Without a worker,
|
||||
// mermaid.parse() runs on the main thread; with a worker (set via
|
||||
// setMermaidWorker), the MermaidBlockNode can validate partial-stream code
|
||||
// off-thread so the UI stays responsive during live diagram output.
|
||||
enableMermaid();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Off-main-thread workers for KaTeX and Mermaid
|
||||
//
|
||||
// Both katex.renderToString and mermaid.parse are CPU-heavy. markstream-vue
|
||||
// ships pre-built workers (katexRenderer.worker.js, mermaidParser.worker.js)
|
||||
// that follow the exact protocol its internal worker clients expect. We import
|
||||
// them via Vite's `?worker&type=module` so they're built as ES module chunks
|
||||
// (supporting code-splitting, which mermaid needs for per-diagram dynamic
|
||||
// imports).
|
||||
//
|
||||
// markstream-vue's MermaidBlockNode and MathBlockNode auto-detect the presence
|
||||
// of a worker: when set, heavy parsing/rendering is dispatched off-thread; when
|
||||
// absent, everything runs on the main thread.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Tear down any previous worker (e.g. from HMR) before setting a new one.
|
||||
clearKaTeXWorker();
|
||||
clearMermaidWorker();
|
||||
|
||||
setKaTeXWorker(new katexWorkerModule.default());
|
||||
setMermaidWorker(new mermaidWorkerModule.default());
|
||||
|
||||
// Only `$$…$$` display math is rendered; single `$` inline math is disabled so
|
||||
// prices, env vars, and shell paths (`$5`, `$PATH`, `$HOME/bin`) stay literal
|
||||
// without any escaping or code-detection gymnastics. `math_block` (the $$ rule)
|
||||
|
|
@ -172,7 +211,7 @@ function processFileLinks(): void {
|
|||
const parent = text.parentElement;
|
||||
if (
|
||||
parent &&
|
||||
!parent.closest('a, pre, .md-file-link') &&
|
||||
!parent.closest('a, pre, .md-file-link, svg') &&
|
||||
text.data.trim().length > 0
|
||||
) {
|
||||
textNodes.push(text);
|
||||
|
|
@ -231,6 +270,9 @@ function processMarkdownLinks(): void {
|
|||
const links = mdRef.value.querySelectorAll<HTMLAnchorElement>('a[href]');
|
||||
for (const link of links) {
|
||||
if (link.dataset.mdLinkHandled === 'true') continue;
|
||||
// Skip links inside Mermaid SVGs — their hrefs are diagram semantics, not
|
||||
// workspace file paths.
|
||||
if (link.closest('svg')) continue;
|
||||
const href = link.getAttribute('href') ?? '';
|
||||
if (!isLocalLink(href)) continue;
|
||||
link.dataset.mdLinkHandled = 'true';
|
||||
|
|
|
|||
9
apps/kimi-web/src/env.d.ts
vendored
9
apps/kimi-web/src/env.d.ts
vendored
|
|
@ -18,3 +18,12 @@ declare module '*.vue' {
|
|||
const component: DefineComponent<Record<string, never>, Record<string, never>, unknown>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
// Vite's `?worker&type=module` imports — not declared in `vite/client`,
|
||||
// which only covers `?worker`, `?worker&inline`, and `?worker&url` for classic
|
||||
// workers. ES module workers need this additional declaration so TypeScript
|
||||
// can resolve the import without errors.
|
||||
declare module '*?worker&type=module' {
|
||||
const WorkerFactory: new () => Worker;
|
||||
export default WorkerFactory;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,4 +47,10 @@ export default defineConfig({
|
|||
emptyOutDir: true,
|
||||
target: 'es2022',
|
||||
},
|
||||
// Workers that import modules with code-splitting (e.g. mermaid's dynamic
|
||||
// diagram imports) need ES format — IIFE cannot split chunks. The app
|
||||
// already targets ES2022 so all supported browsers handle module workers.
|
||||
worker: {
|
||||
format: 'es',
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
"vitepress": "^1.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"mermaid": "^11.12.2",
|
||||
"mermaid": "^11.15.0",
|
||||
"vitepress-plugin-llms": "^1.10.0",
|
||||
"vitepress-plugin-mermaid": "^2.0.17"
|
||||
}
|
||||
|
|
|
|||
5
pnpm-lock.yaml
generated
5
pnpm-lock.yaml
generated
|
|
@ -184,6 +184,9 @@ importers:
|
|||
markstream-vue:
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4(katex@0.17.0)(mermaid@11.15.0)(stream-markdown@0.0.16(react@19.2.5)(shiki@4.3.0)(vue@3.5.35(typescript@6.0.2)))(vue-i18n@11.4.5(vue@3.5.35(typescript@6.0.2)))(vue@3.5.35(typescript@6.0.2))
|
||||
mermaid:
|
||||
specifier: ^11.15.0
|
||||
version: 11.15.0
|
||||
shiki:
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.0
|
||||
|
|
@ -311,7 +314,7 @@ importers:
|
|||
docs:
|
||||
dependencies:
|
||||
mermaid:
|
||||
specifier: ^11.12.2
|
||||
specifier: ^11.15.0
|
||||
version: 11.15.0
|
||||
vitepress-plugin-llms:
|
||||
specifier: ^1.10.0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue