e0a5f57793
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.
333 lines
9.1 KiB
Markdown
333 lines
9.1 KiB
Markdown
# markstream-vue2
|
|
|
|
Vue 2.6-compatible renderer for markstream-vue.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
pnpm add markstream-vue2
|
|
# npm i markstream-vue2
|
|
# yarn add markstream-vue2
|
|
```
|
|
|
|
## Requirements
|
|
|
|
- Vue 2.6.14+ (Vue 2.7 recommended for better TS support)
|
|
- @vue/composition-api (required for Vue 2.6.x)
|
|
|
|
## Composition API compatibility
|
|
|
|
| Vue version | Composition API availability | What to install | How to import |
|
|
|-------------|------------------------------|-----------------|---------------|
|
|
| **2.6.x** | Not built-in | `@vue/composition-api` | `import { ref, computed, defineComponent } from '@vue/composition-api'` |
|
|
| **2.7.x** | Built-in | None | `import { ref, computed, defineComponent } from 'vue'` |
|
|
| **3.x** | Built-in | None | `import { ref, computed, defineComponent } from 'vue'` |
|
|
|
|
Notes:
|
|
- **Vue 2.6.x** must install and **Vue.use(@vue/composition-api)**.
|
|
- **Vue 2.7.x** should **not** install `@vue/composition-api`.
|
|
- **Vue 3.x** should use **markstream-vue** (not markstream-vue2).
|
|
|
|
## Quick start by version
|
|
|
|
### Vue 2.6.x
|
|
|
|
```bash
|
|
pnpm add markstream-vue2 vue@2.6.14 vue-template-compiler@2.6.14 @vue/composition-api
|
|
```
|
|
|
|
```ts
|
|
import VueCompositionAPI from '@vue/composition-api'
|
|
import MarkdownRender, { VueRendererMarkdown } from 'markstream-vue2'
|
|
import Vue from 'vue'
|
|
import 'markstream-vue2/index.css'
|
|
|
|
Vue.use(VueCompositionAPI)
|
|
Vue.use(VueRendererMarkdown)
|
|
```
|
|
|
|
If your app increases root font size on mobile (`html` / `body`), prefer `markstream-vue2/index.px.css` to avoid `rem`-driven scaling.
|
|
|
|
Repo example:
|
|
- `playground-vue2-cli` (Vue 2.6 + Vue CLI / Webpack 4)
|
|
|
|
Start:
|
|
|
|
```bash
|
|
pnpm -C playground-vue2-cli dev
|
|
```
|
|
|
|
From repo root:
|
|
|
|
```bash
|
|
pnpm play:vue2-cli
|
|
```
|
|
|
|
### Vue 2.7.x
|
|
|
|
```bash
|
|
pnpm add markstream-vue2 vue@2.7.16 vue-template-compiler@2.7.16
|
|
```
|
|
|
|
```ts
|
|
import MarkdownRender, { VueRendererMarkdown } from 'markstream-vue2'
|
|
import Vue from 'vue'
|
|
import 'markstream-vue2/index.css'
|
|
|
|
Vue.use(VueRendererMarkdown)
|
|
```
|
|
|
|
Repo example:
|
|
- `playground-vue2` (Vue 2.7 + Vite)
|
|
|
|
Start:
|
|
|
|
```bash
|
|
pnpm -C playground-vue2 dev
|
|
```
|
|
|
|
From repo root:
|
|
|
|
```bash
|
|
pnpm play:vue2
|
|
```
|
|
|
|
### Vue 3.x (use markstream-vue)
|
|
|
|
```bash
|
|
pnpm add markstream-vue vue@^3
|
|
```
|
|
|
|
If your workspace also installs Vue 3, ensure `vue-demi` targets Vue 2:
|
|
|
|
```bash
|
|
pnpm vue-demi-switch 2
|
|
```
|
|
|
|
If you cannot run `vue-demi-switch`, you can force the Vue 2 build via bundler alias (common in Vue CLI / Webpack 4):
|
|
|
|
```js
|
|
// vue.config.js / webpack config
|
|
module.exports = {
|
|
configureWebpack: {
|
|
resolve: {
|
|
alias: {
|
|
'vue-demi$': 'vue-demi/lib/v2/index.cjs',
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
## Usage (Vue 2.6)
|
|
|
|
```ts
|
|
import VueCompositionAPI from '@vue/composition-api'
|
|
import MarkdownRender, { VueRendererMarkdown } from 'markstream-vue2'
|
|
import Vue from 'vue'
|
|
import 'markstream-vue2/index.css'
|
|
|
|
Vue.use(VueCompositionAPI)
|
|
Vue.use(VueRendererMarkdown)
|
|
|
|
new Vue({
|
|
render: h => h(MarkdownRender, {
|
|
props: {
|
|
content: '# Hello from Vue 2',
|
|
},
|
|
}),
|
|
}).$mount('#app')
|
|
```
|
|
|
|
## Streaming best practices
|
|
|
|
- For high-frequency SSE / token streaming, prefer parsing outside the renderer and pass `nodes` instead of reparsing the whole `content` string every chunk.
|
|
- Keep `viewport-priority` enabled unless you explicitly want eager rendering. Mermaid / Monaco / D2 blocks now stay idle while offscreen and resume when they approach the viewport.
|
|
|
|
```vue
|
|
<template>
|
|
<MarkdownRender
|
|
:nodes="nodes"
|
|
:final="final"
|
|
:viewport-priority="true"
|
|
:defer-nodes-until-visible="true"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
## Heavy-node prop forwarding
|
|
|
|
`MarkdownRender` can forward framework-level props directly into specialized heavy renderers:
|
|
|
|
```vue
|
|
<template>
|
|
<MarkdownRender
|
|
:content="markdown"
|
|
:mermaid-props="{
|
|
showHeader: false,
|
|
renderDebounceMs: 180,
|
|
previewPollDelayMs: 500,
|
|
}"
|
|
:d2-props="{ progressiveIntervalMs: 500 }"
|
|
:infographic-props="{ showHeader: false }"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
Notes:
|
|
- Use kebab-case in templates: `mermaid-props`, `d2-props`, `infographic-props`.
|
|
- These props are forwarded to the built-in Mermaid / D2 / Infographic blocks and to custom `mermaid` / `d2` / `infographic` overrides registered with `setCustomComponents(...)`.
|
|
|
|
## Language-specific code block overrides
|
|
|
|
You can also register a renderer for one fenced language directly:
|
|
|
|
```ts
|
|
import { setCustomComponents } from 'markstream-vue2'
|
|
|
|
setCustomComponents('docs', {
|
|
echarts: EChartsBlockNode,
|
|
})
|
|
```
|
|
|
|
Notes:
|
|
- `echarts` only catches fences whose language is `echarts`.
|
|
- Code block routing priority is exact language key -> built-in `mermaid` / `d2` / `infographic` routes -> `code_block`.
|
|
- `mermaid`, `d2`, and `infographic` overrides keep their specialized prop forwarding; other language keys receive the normal code-block-level bindings.
|
|
|
|
## Mermaid tuning
|
|
|
|
Common `mermaid-props` keys for streaming scenarios:
|
|
|
|
- `renderDebounceMs`: delay partial/full progressive work during rapid token bursts.
|
|
- `contentStableDelayMs`: how long source mode waits before auto-switching back to preview when content stabilizes.
|
|
- `previewPollDelayMs`: initial delay before preview polling tries to upgrade a partial preview into a full render.
|
|
- `previewPollMaxDelayMs`: cap for preview polling backoff.
|
|
- `previewPollMaxAttempts`: maximum retry count while the Mermaid source is still incomplete.
|
|
|
|
## Build and size checks
|
|
|
|
```bash
|
|
pnpm --filter markstream-vue2 build
|
|
pnpm --filter markstream-vue2 build:analyze
|
|
pnpm --filter markstream-vue2 size:check
|
|
```
|
|
|
|
## Bundle size notes
|
|
|
|
- Optional peers are not bundled; install only the features you need.
|
|
- Infrequent language icons are split into an async chunk and loaded on demand.
|
|
- If you want to avoid first-hit fallback icons, preload once when the app is idle:
|
|
|
|
```ts
|
|
import { preloadExtendedLanguageIcons } from 'markstream-vue2'
|
|
|
|
if (typeof window !== 'undefined')
|
|
void preloadExtendedLanguageIcons()
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### `defineComponent is not a function`
|
|
Cause: `vue-demi` is in Vue 3 mode while the app runs Vue 2.x.
|
|
Fix: run `pnpm vue-demi-switch 2` or alias `vue-demi$` to `vue-demi/lib/v2/index.cjs`.
|
|
|
|
### `Vue packages version mismatch`
|
|
Cause: `vue` and `vue-template-compiler` versions differ.
|
|
Fix: align both to the same version (e.g. `2.6.14` or `2.7.16`).
|
|
|
|
### `Cannot read properties of undefined (reading 'props')`
|
|
Cause: Vue 2.6 + Composition API missing `_setupProxy` patch, or plugin not installed.
|
|
Fix: ensure `@vue/composition-api` is installed + `Vue.use(...)`, and update to the latest markstream-vue2 build.
|
|
|
|
### Custom node renders trigger `infinite update loop`
|
|
Cause: in Vue 2.6 / Vue CLI 4, recursively mounting another `MarkdownRender` inside a custom node component can create a render loop when the parent renderer is still streaming.
|
|
Fix: prefer `NestedRenderer` for nested streaming content. If you only need static HTML output, `renderNestedMarkdownToHtml(...)` is still available.
|
|
|
|
```ts
|
|
import { NestedRenderer } from 'markstream-vue2'
|
|
|
|
export default {
|
|
components: { NestedRenderer },
|
|
props: {
|
|
node: { type: Object, required: true },
|
|
},
|
|
}
|
|
```
|
|
|
|
```vue
|
|
<template>
|
|
<NestedRenderer
|
|
:node="node"
|
|
custom-id="chat-demo"
|
|
:custom-html-tags="['thinking']"
|
|
:typewriter="false"
|
|
:batch-rendering="false"
|
|
:viewport-priority="false"
|
|
:defer-nodes-until-visible="false"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
`NestedRenderer` keeps the inner content streamable, but switches the nested renderer to the `nodes` path so Vue 2.6 does not recurse through a second `content`-driven renderer.
|
|
|
|
If you cannot mount another renderer and just need a static nested body, use `renderNestedMarkdownToHtml(...)`:
|
|
|
|
```ts
|
|
import { renderNestedMarkdownToHtml } from 'markstream-vue2'
|
|
|
|
export default {
|
|
props: {
|
|
node: { type: Object, required: true },
|
|
},
|
|
computed: {
|
|
renderedHtml() {
|
|
return renderNestedMarkdownToHtml(
|
|
{ node: this.node },
|
|
{
|
|
customHtmlTags: ['thinking'],
|
|
customNodeClass(node) {
|
|
return node.type === 'thinking' ? 'thinking-node__nested' : ''
|
|
},
|
|
},
|
|
)
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
This keeps deeply nested custom content streamable without recursively mounting a second `content`-driven `MarkdownRender` / `NodeRenderer`.
|
|
|
|
## Tailwind
|
|
|
|
If your app uses Tailwind and you want to avoid shipping duplicated utility CSS, import the Tailwind-ready output instead:
|
|
|
|
```ts
|
|
import 'markstream-vue2/index.tailwind.css'
|
|
```
|
|
|
|
Then include the extracted class list in `tailwind.config.js`:
|
|
|
|
```js
|
|
module.exports = {
|
|
content: [
|
|
'./src/**/*.{js,ts,vue}',
|
|
require('markstream-vue2/tailwind'),
|
|
],
|
|
}
|
|
```
|
|
|
|
## Notes
|
|
|
|
- The Vue 2 package mirrors the Vue 3 renderer feature set where possible (virtualization, streaming code blocks, Monaco, Mermaid, KaTeX, tooltip singleton).
|
|
- Optional peers are still required for those features (`stream-monaco`, `stream-markdown`, `mermaid`, `katex`, etc.).
|
|
- Custom node components are supported via `setCustomComponents` from `markstream-vue2`.
|
|
|
|
## TypeScript
|
|
|
|
Parser node types are re-exported from `stream-markdown-parser`, and Vue 2 specific mapping / Monaco types are available from `markstream-vue2` itself:
|
|
|
|
```ts
|
|
import type { CodeBlockMonacoOptions, CustomComponents, ParsedNode } from 'markstream-vue2'
|
|
```
|