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

6.3 KiB
Raw Permalink Blame History

description
description
在 content 与 nodes 之间做出正确选择,并理解 markstream-vue 在 Vite、VitePress、Nuxt 与流式界面中的接入方式。

使用示例与 API

本页聚焦三个问题:如何在 Vite/VitePress/Nuxt 中集成、解析器如何配合渲染器、样式出错时去哪查看 reset 与 Tailwind/UnoCSS 排障指南。

先决定用 content 还是 nodes

场景 推荐输入
文档页、静态文章、低频更新 content
SSE、token 流式输出、AI Chat、高频增量更新 nodes + final
SSR 或 Worker 里预解析完成 nodes

如果你只是在调现有能力,继续看本页和 Props 与选项 就够了。只有在你要改变渲染行为时,再跳去看 覆盖内置组件。

如果需要在既有设计系统里覆盖样式,务必传入 custom-id 并阅读 样式排查清单。

如果你真正要做的是 AI Chat、逐 token 输出或 SSE 响应预览,建议直接走专门的 AI 聊天与流式输出 路径,不用自己拼这些页面。

如果你真正要做的是文档站或 VitePress 主题接入,建议改走 文档站与 VitePress 集成,那里会把 content、enhanceApp 和 CSS 顺序串成一条路径。

最小渲染示例

<script setup lang="ts">
import MarkdownRender from 'markstream-vue'

const doc = '# 使用示例\n\n支持 **streaming** 渲染。'
</script>

<template>
  <MarkdownRender custom-id="docs" :content="doc" />
</template>
@import 'modern-css-reset';

@layer components {
  @import 'markstream-vue/index.css';
}

VitePress + 自定义标签

如果这不是一个零散示例,而是你的主要落地场景,看完这里后直接继续 文档站与 VitePress 集成。

在 VitePress 中,你只需要在 enhanceApp 里注册一次自定义节点组件,然后在 MarkdownRender 上使用 custom-html-tags,解析器就会自动输出对应的自定义节点。

import type { Component } from 'vue'
import MarkdownRender, { setCustomComponents } from 'markstream-vue'
// docs/.vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import 'markstream-vue/index.css'

declare const ThinkingNode: Component

export default {
  extends: DefaultTheme,
  enhanceApp() {
    setCustomComponents('docs', { thinking: ThinkingNode })
  },
}
<!-- 在 VitePress 页面里 -->
<MarkdownRender
  custom-id="docs"
  :custom-html-tags="['thinking']"
  :content="source"
/>

提示:像 <question> 这类未知的 HTML 类标签在完整闭合后会按原生 HTML 渲染。未闭合或格式不完整的片段会保持为纯文本,避免吞掉附近的 Markdown。只有当你希望它参与解析并产出自定义节点(type + attrs/content)时,才需要加入 custom-html-tags。

解析流程

import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'

const md = getMarkdown()

const nodes = parseMarkdownToStructure('# 标题', md)
// 将 nodes 传入 <MarkdownRender :nodes="nodes" />
  • getMarkdown(msgId?, options?) 返回预设配置的 markdown-it-ts。
  • parseMarkdownToStructure(content, md) 将 Markdown 字符串转为渲染器使用的 AST。
  • 可搭配 setCustomComponents(id?, mapping) 为特定 custom-id 替换节点渲染器。

流式推荐用法

content 模式适合低频更新或一次性渲染;如果你在做 AI Chat、SSE、逐 token 输出,推荐把解析放到外部,然后用 :nodes + :final 驱动渲染器。这样可以减少整篇重解析、降低重绘次数,也更容易把解析工作放到 Worker 或独立状态层。

import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'

const md = getMarkdown('chat-message')
declare const streamedText: string
declare const final: boolean
const nodes = parseMarkdownToStructure(streamedText, md, { final })
<script setup lang="ts">
import type { BaseNode } from 'markstream-vue'
import MarkdownRender from 'markstream-vue'

const nodes: BaseNode[] = []
const final = false
</script>

<template>
  <MarkdownRender :nodes="nodes" :final="final" />
</template>

如果你想要一条从安装、接法、性能到排障都串好的完整路径,继续看 AI 聊天与流式输出。

组件速览

完整说明参考 组件与节点渲染器:

  • CodeBlockNode — Monaco 代码块(需要安装 stream-monaco)。
  • MarkdownCodeBlockNode — 基于 Shiki,适合轻量场景。
  • MermaidBlockNode — 需要 mermaid ≥ 11(无需额外 CSS)。
  • D2BlockNode — 需要 @terrastruct/d2(无需额外 CSS)。
  • ImageNode — 通过 click/load/error 事件接管图片预览。

样式提醒

  1. 先 reset —— modern-css-reset、@tailwind base 或 @unocss/reset,之后再导入库的 CSS。
  2. 使用 CSS layer —— Tailwind/UnoCSS 项目请在 @layer components { ... } 中导入 markstream-vue/index.css。
  3. 处理 Uno/Tailwind 冲突 —— 参见 Tailwind 指南(包含 UnoCSS 示例)。
  4. 同伴 CSS —— KaTeX 需要对应的 CSS;Mermaid/D2 不需要。Monaco 不需要额外导入 CSS。

CSS 作用域(重要)

本包打包出来的 CSS 会限定在内部 .markstream-vue 容器下(包含 Tailwind 工具类与主题变量),以尽量降低全局冲突风险。

  • 使用 MarkdownRender 时会自动包含该容器,无需额外处理。
  • 如果你单独使用导出的节点组件(例如 PreCodeNode、FootnoteNode),请在外层包一层容器:
<script setup lang="ts">
import type { PreCodeNodeProps } from 'markstream-vue'
import { PreCodeNode } from 'markstream-vue'

const node = {
  type: 'code_block',
  language: 'ts',
  code: 'console.log(1)',
  raw: 'console.log(1)',
} satisfies PreCodeNodeProps['node']
</script>

<template>
  <div class="markstream-vue">
    <PreCodeNode :node="node" />
  </div>
</template>

若仍无法解决,请运行 pnpm play 在 playground 中复现,并附带链接到 issue 中。