# 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 ``` ## Heavy-node prop forwarding `MarkdownRender` can forward framework-level props directly into specialized heavy renderers: ```vue ``` 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 ``` `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' ```