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.
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# MermaidBlockNode (Component)
|
||||
|
||||
`MermaidBlockNode` is a lightweight, extensible renderer for Mermaid diagrams intended for embedding interactive Mermaid charts within documents. The component supports progressive rendering, source/preview toggling, copying source, exporting SVG, and a pseudo-fullscreen modal (with zoom and drag).
|
||||
|
||||
## Props (key)
|
||||
- `node: any` — the Mermaid code node (required)
|
||||
- `isDark?: boolean` — dark mode flag
|
||||
- `loading?: boolean` — initial loading placeholder
|
||||
- `maxHeight?: string | null` — maximum height
|
||||
- `estimatedPreviewHeightPx?: number` — first-preview height reserved before Mermaid finishes rendering; `MarkdownRender` fills this automatically for Mermaid fences
|
||||
- `isStrict?: boolean` — run Mermaid in `securityLevel: 'strict'` with DOMPurify + HTML-label hardening; use this when rendering untrusted diagrams to strip inline scripts/`javascript:` URLs that could otherwise leak into the SVG
|
||||
- `onRenderError?: (error: unknown, code: string, container: HTMLElement) => boolean | void` — custom error handler called when mermaid rendering fails. Return `true` to prevent the default error display. Receives the error, the raw mermaid source code, and the container DOM element so you can render custom content.
|
||||
- Header / control props (all optional, default `true`):
|
||||
- `showHeader`, `showModeToggle`, `showCopyButton`, `showExportButton`, `showFullscreenButton`, `showCollapseButton`, `showZoomControls`, `showTooltips`
|
||||
- `enableWheelZoom?: boolean` — enable Ctrl/Cmd + wheel zoom in the canvas (default `false`)
|
||||
- Timeouts (ms): `workerTimeoutMs`, `parseTimeoutMs`, `renderTimeoutMs`, `fullRenderTimeoutMs` (defaults: `1400/1800/2500/4000`)
|
||||
- Streaming tuning: `renderDebounceMs`, `contentStableDelayMs`, `previewPollDelayMs`, `previewPollMaxDelayMs`, `previewPollMaxAttempts`
|
||||
|
||||
## Slots
|
||||
- `header-left` — replace the left area (defaults to Mermaid icon + label)
|
||||
- `header-center` — replace the center area (defaults to preview/source toggle)
|
||||
- `header-right` — replace the right-side action buttons (take full control of the default controls)
|
||||
|
||||
## Emits
|
||||
The component emits events using a unified `MermaidBlockEvent` object. Listeners can call `preventDefault()` on the event to stop the component's default behavior:
|
||||
|
||||
- `copy` — copy button clicked, signature: `(ev: MermaidBlockEvent<{ type: 'copy'; text: string }>)` (call `ev.preventDefault()` to stop the built-in clipboard write + "Copied" state)
|
||||
- `export` — export button clicked, signature: `(ev: MermaidBlockEvent<{ type: 'export' }>)`
|
||||
- `openModal` — request to open the pseudo-fullscreen modal, signature: `(ev: MermaidBlockEvent<{ type: 'open-modal' }>)`
|
||||
- `toggleMode` — toggle between `source` and `preview`, signature: `(target: 'source' | 'preview', ev: MermaidBlockEvent<{ type: 'toggle-mode'; target: 'source' | 'preview' }>)`
|
||||
|
||||
### Intercept example
|
||||
Completely override the component's default export behavior:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MermaidBlockEvent } from 'markstream-vue'
|
||||
import { MermaidBlockNode } from 'markstream-vue'
|
||||
|
||||
function onExport(ev: any /* MermaidBlockEvent */) {
|
||||
ev.preventDefault()
|
||||
// The component exposes the rendered SVG element on the event as `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>
|
||||
```
|
||||
|
||||
> Note: The event object includes both `svgElement` (DOM node) and `svgString` (serialized SVG) on `export` and `openModal` events — use whichever is more convenient.
|
||||
|
||||
## Slot example — fully replace right-side controls
|
||||
|
||||
```vue
|
||||
<MermaidBlockNode :node="node" :showExportButton="false">
|
||||
<template #header-right>
|
||||
<button @click="downloadSvg">Download</button>
|
||||
<button @click="openCustomModal">Open custom modal</button>
|
||||
</template>
|
||||
</MermaidBlockNode>
|
||||
```
|
||||
|
||||
## Recommended usage
|
||||
- To implement custom export/upload behavior, call `preventDefault()` in the `export` listener and extract the SVG from the rendered DOM in your handler.
|
||||
- To fully replace the header UI, use the `header-*` slots and set the corresponding `show*` props to `false` to hide the default controls.
|
||||
- If Mermaid content originates from users/LLMs or any untrusted source, set `:is-strict="true"` so the component sanitizes the SVG and disables HTML labels; this closes the gap where crafted `javascript:` URLs or event handlers could sneak into the rendered output.
|
||||
- In AI chat scenarios, use `onRenderError` to show raw mermaid source instead of the default error message:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { MermaidBlockNode } from 'markstream-vue'
|
||||
|
||||
function handleMermaidError(_err: unknown, code: string, container: HTMLElement) {
|
||||
// Show the raw mermaid text as a code block instead of an error message
|
||||
const pre = document.createElement('pre')
|
||||
pre.className = 'text-sm font-mono whitespace-pre-wrap p-4'
|
||||
pre.textContent = code
|
||||
container.replaceChildren(pre)
|
||||
return true // prevent default error display
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MermaidBlockNode :node="node" :on-render-error="handleMermaidError" />
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- `svgString` is already included in `export` and `openModal` event payloads (the serialized SVG string is available on the event), so consumers can upload or post the SVG without re-serializing.
|
||||
- This page is linked from the docs sidebar and a runnable playground demo is provided in the repo (route: `/mermaid-export-demo`, file: `playground/src/pages/mermaid-export-demo.vue`).
|
||||
|
||||
## See also
|
||||
|
||||
- Override `MermaidBlockNode` (use `setCustomComponents` with `MarkdownRender`): [Override MermaidBlockNode in MarkdownRender](./mermaid-block-node-override.md)
|
||||
|
||||
Playground demo: there's a runnable playground page demonstrating how to intercept `export` and upload `ev.svgString` — see the playground route `mermaid-export-demo` (file: `playground/src/pages/mermaid-export-demo.vue`).
|
||||
|
||||
Try this — quick mermaid example you can paste into a component:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import MarkdownRender from 'markstream-vue'
|
||||
|
||||
const md = `\n\`\`\`mermaid\ngraph TD\nA-->B\n\`\`\`\n`
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MarkdownRender :content="md" />
|
||||
</template>
|
||||
```
|
||||
Reference in New Issue
Block a user