markstream-vue

Simon-He95/markstream-vue — AI / SSE 向けストリーミング Markdown レンダラ(Vue 3 中心のパッケージ群)

1 — 概要

markstream-vue は、AI チャットや LLM トークンストリーム向けの ストリーミング Markdown レンダラ。未完成の Markdown、長い回答、Mermaid / KaTeX、ストリーミング中のコードブロックを、ちらつきを抑えつつ描画する。完成済み文書向けの marked / markdown-it / react-markdown とは用途が分かれる。

リポジトリ名は markstream-vue だが、Markstream として Vue / React / Svelte / Angular / Vue 2 向けのレンダラ群と、フレームワーク非依存の stream-markdown-parser・markstream-core を同梱する。

Vue 3
Primary package
1.x
Stable API contract
5+
Framework packages
MIT
License
向き先: SSE / WebSocket で Markdown が増え続ける UI、長い AI 回答、プログレッシブ図・コードブロックが必要なチャット面。向かない先: 静的な完成 Markdown だけを一度描画する用途(通常の Markdown ライブラリで足りる)。
2 — パッケージ

公式ドキュメントの framework overview が選択ガイド。Vue / Nuxt / VitePress なら markstream-vue、パースだけなら stream-markdown-parser。

パッケージ対象インストール
markstream-vueVue 3 / Nuxt / VitePresspnpm add markstream-vue
markstream-reactReact / Next.js / Remixpnpm add markstream-react
markstream-svelteSvelte 5pnpm add markstream-svelte svelte@^5
markstream-angularAngular standalonepnpm add markstream-angular
markstream-vue2Vue 2.6 / 2.7pnpm add markstream-vue2
stream-markdown-parser任意 JS/TS(パース)pnpm add stream-markdown-parser
markstream-coreストリーム制御ユーティリティpnpm add markstream-core
flowchart TD
  parser["stream-markdown-parser"]
  core["markstream-core"]
  vue["markstream-vue"]
  react["markstream-react"]
  svelte["markstream-svelte"]
  angular["markstream-angular"]
  vue2["markstream-vue2"]
  parser --> vue
  parser --> react
  parser --> svelte
  parser --> angular
  parser --> vue2
  core --> vue
  core --> react
    
3 — クイックスタート

CSS を一度 import し、MarkdownRender に content とストリーム完了フラグ final を渡す。

pnpm add markstream-vue
<script setup lang="ts">
import MarkdownRender from 'markstream-vue'
import 'markstream-vue/index.css'

defineProps<{ content: string; isDone: boolean }>()
</script>

<template>
  <MarkdownRender mode="chat" :content="content" :final="isDone" />
</template>

Tailwind 利用時は @import 'markstream-vue/index.css' layer(components); のようにレイヤを指定し、レンダラ側スタイルがユーティリティに負けないようにする。モバイルでルート rem をスケールしている場合は markstream-vue/index.px.css を検討する。

const content = ref('')
const final = ref(false)

eventSource.onmessage = (event) => {
  content.value += event.data
}
eventSource.addEventListener('done', () => {
  final.value = true
})

// <MarkdownRender :content="content" :final="final" :fade="false" />
4 — モードと描画

mode="chat"

AI / SSE 向け。ペースを安定させ、不透明度アニメによるちらつきを避ける。典型では fade={false} と smooth-streaming="auto"。

mode="docs"

リッチなドキュメント向け。大きめの描画バッチ、ツールチップ、fade が既定で有効。コードレンダラとレイアウト戦略が chat と異なる。

mode="minimal"

chat と同系統の軽量既定を、非チャット面で中立名として使いたいとき。

同一メッセージで mode を切り替えない。 final が立ったからといって chat → docs に変えない。ペースは smooth-streaming / typewriter / fade で調整する。smooth-streaming の高頻度更新と fade の併用は、opacity 再スタートの連続になりやすい。
プリセット設定用途
Virtual window max-live-nodes 既定 220 長文のスクロールバックとメモリを安定。ノードはすぐ描画し、マウント窓をスライド。
Incremental :max-live-nodes="0" タイプライタ風。バッチ系 props(renderBatchSize など)でスライス描画。

チャットでは例として renderBatchSize={16}、renderBatchDelay={8}、renderBatchBudgetMs を下げて CPU を抑える組み合わせが README に示されている。

5 — パイプライン

content(生 Markdown)または nodes(事前パース結果)のどちらかを受け取り、NodeRenderer 経由でブロック/インラインを描画する。サーバーや Worker で parseMarkdownToStructure した結果をクライアントに渡すと、SSR / hydration を決定的にしやすい。

flowchart TD
  src["SSE / WebSocket / string"]
  md["content または nodes"]
  parse["parseMarkdownToStructure\nstreamParse auto / true"]
  batch["virtual window または\nincremental batches"]
  nodes["NodeRenderer"]
  peers["optional: Mermaid / KaTeX\nMonaco / Shiki / D2"]
  dom[".markstream-vue DOM"]
  src --> md
  md --> parse
  parse --> batch
  batch --> nodes
  nodes --> peers
  peers --> dom
  nodes --> dom
    
  • parseMarkdownToStructure の既定は streamParse: 'auto'。非 final のトップレベルでは md.stream.parse とキャッシュを使う。
  • MarkdownRender が自分でパースする場合、ストリーム向けに streamParse: true が既定。 final が変わるとキャッシュを無効化し、完了セマンティクスで再パースする。
  • 無関係な one-shot 文書で同一 md インスタンスを再利用するときは { final: true } または { streamParse: false } を渡す。
6 — 主要 props
prop / API要点
content / nodes生 Markdown か、事前パース済みノード。
finalストリーム終端。途中状態の loading パースを止め、未閉じ構文を確定させる。
max-live-nodes220(仮想化)または 0(増分バッチ)。
batchRendering 系initialRenderBatchSize / renderBatchSize / renderBatchDelay / renderBatchBudgetMs。
smooth-streamingtypewriter / max-live-nodes <= 0 では既定オン。生チャンク間隔にしたい面では false。
parse-optionspreTransformTokens や requireClosingStrong などパーサフック。
custom-html-tags例: ['thinking']。カスタムタグをストリーム許可し、カスタムノードとして出す。
setCustomComponentscustom-id スコープで Vue コンポーネントを登録。
code-block-props例: theme: { light, dark }。構造キー(node, ctx 等)は無視される。
htmlPolicy信頼できない UGC では "escape" を推奨(生 HTML をテキスト扱い)。
is-dark / .dark祖先に .dark、または prop でレンダラ内ダークを指定。
7 — 任意統合

mermaid / katex / stream-diffs / shiki などは任意 peer。使わない限りバンドルに乗らない。強化コードブロック(File / Diff)が要るなら stream-diffs。無い場合は意図的に <pre> へフォールバックする。

import { enableKatex, enableMermaid } from 'markstream-vue'
import 'markstream-vue/index.css'
import 'katex/dist/katex.min.css'

enableMermaid()
enableKatex()

CDN 経由で KaTeX / Mermaid を載せる場合は、createKaTeXWorkerFromCDN + setKaTeXWorker、createMermaidWorkerFromCDN + setMermaidWorker で Worker を注入できる(オフメインスレッド描画・パース向け)。

レンダラ CSS は内部コンテナ .markstream-vue にスコープされる。デザイン方針は「既定スタイル付き + CSS 変数で換装可能(非 headless)」。shadcn 互換のため --ms-* bridge token で宿主変数を継承し、未定義時は内蔵デフォルトへフォールバックする(design/architecture.md)。

MarkdownRender 外でノードコンポーネント単体を使うときは、親に class="markstream-vue" を付ける。

8 — 注意点
症状 / 状況対応
Mermaid / KaTeX が出ない peer を入れ、enableMermaid / enableKatex、または prop の enable 系。CDN なら window.mermaid / window.katex も拾う。
リッチコードブロックが出ない stream-diffs を入れるか、:render-code-blocks-as-pre="true" で明示的に軽量にする。
末尾の $$ やフェンスがずっと loading ストリーム終了時に :final="true"、または parseMarkdownToStructure(..., { final: true })。
信頼できない HTML htmlPolicy="escape"。
1.0 の安定面 MarkdownRender、ストリーミング / 事前ノード、安全 HTML 方針、任意統合、virtual-scroll 連携、CSS / worker / SSR エントリが契約。npm タグは beta のままの期間があり得る。
ニーズ一般的な Markdown プレビューmarkstream-vue
ストリーム入力全体再描画・フラッシュ増分バッチ + virtual window
大きなコード再ハイライトが重いstream-diffs / Shiki オプション
図パース完了までブロックしやすいプログレッシブ Mermaid
カスタム UIスロットが限られがちインライン Vue コンポーネント + 型付きノード
長文メモリが跳ねやすいlive-node 上限で定常化

出典: Simon-He95/markstream-vue の README.md / design/architecture.md(解説時点の upstream)