Files
boos e0a5f57793 feat(desktop): embed BoosAPI Desktop client as a subproject
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as
a monorepo subdirectory under desktop/. Source only — node_modules,
dist, Cargo target and the upstream .git history are excluded.

desktop/ keeps its own independent Cargo workspace (desktop/src-tauri
with crates/*); it is not part of the backend boosapi crate, so a
`cargo check` at the metapi root still compiles only the backend. Build
the desktop app from desktop/src-tauri per desktop/README.md.

Note: app icons are still placeholder assets pending the final BoosAPI
logo. The updater signing keys live outside the repo and are not
included.
2026-06-22 11:32:57 +08:00

80 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MermaidBlockNode (Component)
`MermaidBlockNode` 提供一个易于使用且可扩展的 Mermaid 渲染器,适用于需要在文档中内嵌交互式 Mermaid 图的场景。组件支持渐进式渲染、源码/预览切换、复制源码、导出 SVG,以及伪全屏查看(带缩放与拖拽)。
## Props(关键)
- `node: any` — Mermaid 代码节点(必须)
- `isDark?: boolean` — 暗色模式开关
- `loading?: boolean` — 初始加载占位
- `maxHeight?: string | null` — 最大高度
- `estimatedPreviewHeightPx?: number` — Mermaid 完成渲染前预留的首屏 preview 高度;通过 `MarkdownRender` 渲染 Mermaid 围栏时会自动填入
- `isStrict?: boolean` — 开启 `securityLevel: 'strict'` + DOMPurify,并禁用 HTML labels;用于渲染不可信的 Mermaid 输入(例如用户/LLM 生成的内容),防止 `<script>`、`javascript:` 链接或内联事件落入最终 SVG
- Header/按钮控制(全部可选,默认 `true`):
- `showHeader` / `showModeToggle` / `showCopyButton` / `showExportButton` / `showFullscreenButton` / `showCollapseButton` / `showZoomControls` / `showTooltips`
- `enableWheelZoom?: boolean` — 开启 Ctrl/Cmd + 滚轮缩放(默认 `false`)
- 超时(毫秒):`workerTimeoutMs` / `parseTimeoutMs` / `renderTimeoutMs` / `fullRenderTimeoutMs`(默认 `1400/1800/2500/4000`)
- 流式调优:`renderDebounceMs` / `contentStableDelayMs` / `previewPollDelayMs` / `previewPollMaxDelayMs` / `previewPollMaxAttempts`
## Slots
- `header-left` — 替换左侧(默认是 Mermaid 图标 + 标签)
- `header-center` — 替换中间区域(默认 preview/source 切换)
- `header-right` — 替换右侧操作按钮(完整接管默认按钮)
## Emits
组件发出的事件都使用统一的 `MermaidBlockEvent` 对象,支持 `preventDefault()` 来阻止组件默认行为:
- `copy` — 点击复制按钮,处理签名:`(ev: MermaidBlockEvent<{ type: 'copy'; text: string }>)`(调用 `ev.preventDefault()` 可阻止组件默认的复制到剪贴板与 “Copied” 状态)
- `export` — 导出按钮点击,处理签名:`(ev: MermaidBlockEvent<{ type: 'export' }>)`
- `openModal` — 请求打开 pseudo-fullscreen,处理签名:`(ev: MermaidBlockEvent<{ type: 'open-modal' }>)`(组件内部 emit 名称为 `openModal`)
- `toggleMode` — 切换 `source | preview`,处理签名:`(target: 'source' | 'preview', ev: MermaidBlockEvent<{ type: 'toggle-mode'; target: 'source' | 'preview' }>)`(组件内部 emit 名称为 `toggleMode`)
### 拦截示例
完全替换组件默认导出行为:
```vue
<script setup lang="ts">
import type { MermaidBlockEvent } from 'markstream-vue'
import { MermaidBlockNode } from 'markstream-vue'
function onExport(ev: any /* MermaidBlockEvent */) {
ev.preventDefault()
// 组件在事件对象中暴露了 svgElement,直接使用它更方便
const svgEl = ev.svgElement as SVGElement | null
if (!svgEl) {
console.warn('No svg element available')
return
}
const svgString = new XMLSerializer().serializeToString(svgEl)
uploadSvg(svgString)
}
</script>
<template>
<MermaidBlockNode :node="node" @export="onExport" />
</template>
```
> 注意:`export` / `openModal` 事件的事件对象现已同时包含 `svgElement`(DOM 节点)和 `svgString`(已序列化的 SVG 字符串),任选其一使用更方便。
## Slot 示例:完全接管右侧操作按钮
```vue
<MermaidBlockNode :node="node" :showExportButton="false">
<template #header-right>
<button @click="downloadSvg">Download</button>
<button @click="openCustomModal">Open custom modal</button>
</template>
</MermaidBlockNode>
```
## 推荐用法
- 如果你要实现自定义导出/上传,最佳做法是:在 `export` 监听器中 `preventDefault()`,并在监听回调中直接从组件渲染的 DOM 中读取 `svg`。
- 如果你想要完全替换头部的 UI,使用 `header-*` 插槽并把相应 `show*` props 设为 `false` 来隐藏默认按钮。
- 若 Mermaid 内容来自用户/LLM 或任何不可信来源,建议加上 `:is-strict="true"`,组件会对 SVG 进行清理并禁用 HTML labels,从而堵住恶意 `<script>` 或 `javascript:` 链接注入的风险。
## 参考
- 覆盖 `MermaidBlockNode`(在 `MarkdownRender` 中使用 `setCustomComponents`)的示例(中文):[覆盖 MermaidBlockNode(MarkdownRender 示例)](./mermaid-block-node-override.md)
Playground 示例:仓库包含一个可运行的 playground 页面演示如何拦截 `export` 并上传 `ev.svgString` —— 路由: `mermaid-export-demo` (文件:`playground/src/pages/mermaid-export-demo.vue`)。