markstream-vue 0.0.3-beta.1 → 0.0.3-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +279 -6
- package/README.zh-CN.md +287 -15
- package/dist/exports-CzGTWeP_.js +5323 -0
- package/dist/exports-Dt9os3zH.cjs +246 -0
- package/dist/exports.js +1 -1
- package/dist/{index-DGTqKnzM.js → index-BJRqEAxf.js} +409 -383
- package/dist/index-BOMIs5jY.cjs +22 -0
- package/dist/{index-ClFlwUOZ.cjs → index-DVzzMFW2.cjs} +1 -1
- package/dist/{index-BZqRC51k.js → index-DWSDjapj.js} +1 -1
- package/dist/{index-D-TkURzk.js → index-DoGGVWiX.js} +1 -1
- package/dist/{index-Cn5LHgOE.cjs → index-W52qnVsg.cjs} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +44 -27
- package/dist/index.js +64 -63
- package/dist/index.tailwind.css +1 -1
- package/dist/index4.js +1 -1
- package/dist/tailwind.ts +0 -0
- package/package.json +14 -5
- package/dist/exports-ByrWvfQh.cjs +0 -246
- package/dist/exports-CuW10Rlw.js +0 -4909
- package/dist/index-CapyHsmU.cjs +0 -22
package/README.md
CHANGED
|
@@ -5,24 +5,63 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/markstream-vue)
|
|
6
6
|
[](README.zh-CN.md)
|
|
7
7
|
[](https://markstream-vue-docs.simonhe.me/)
|
|
8
|
+
[](https://markstream-vue.simonhe.me/)
|
|
9
|
+
[](https://markstream-vue.simonhe.me/test)
|
|
8
10
|
[](https://www.npmjs.com/package/markstream-vue)
|
|
9
11
|
[](https://bundlephobia.com/package/markstream-vue)
|
|
12
|
+
[](https://github.com/Simon-He95/markstream-vue/releases)
|
|
13
|
+
[](https://github.com/Simon-He95/markstream-vue/discussions)
|
|
14
|
+
[](https://discord.gg/vkzdkjeRCW)
|
|
15
|
+
[](./SUPPORT.md)
|
|
16
|
+
[](./SECURITY.md)
|
|
17
|
+
[](https://github.com/Simon-He95/markstream-vue/actions/workflows/ci.yml)
|
|
10
18
|
[](./LICENSE)
|
|
11
19
|
|
|
20
|
+
## Contents
|
|
21
|
+
|
|
22
|
+
- [TL;DR Highlights](#tldr-highlights)
|
|
23
|
+
- [Try It Now](#-try-it-now)
|
|
24
|
+
- [Quick Start](#-quick-start)
|
|
25
|
+
- [Common commands](#-common-commands)
|
|
26
|
+
- [Streaming in 30 seconds](#-streaming-in-30-seconds)
|
|
27
|
+
- [Performance presets](#-performance-presets)
|
|
28
|
+
- [Key props & options](#-key-props--options-cheatsheet)
|
|
29
|
+
- [Where it shines](#-where-it-shines)
|
|
30
|
+
- [FAQ](#-faq-quick-answers)
|
|
31
|
+
- [Why markstream-vue](#-why-markstream-vue-over-a-typical-markdown-renderer)
|
|
32
|
+
- [Releases](#-releases)
|
|
33
|
+
- [Showcase](#-showcase--examples)
|
|
34
|
+
- [Contributing & community](#-contributing--community)
|
|
35
|
+
- [Community & support](#-community--support)
|
|
36
|
+
- [Troubleshooting](#troubleshooting--common-issues)
|
|
12
37
|
> 📖 All detailed documentation, API, examples, and advanced usage have been migrated to the VitePress documentation site:
|
|
13
38
|
> https://markstream-vue-docs.simonhe.me/guide/
|
|
14
39
|
|
|
15
|
-
##
|
|
40
|
+
## TL;DR Highlights
|
|
41
|
+
|
|
42
|
+
- Purpose-built for **streaming Markdown** (AI/chat/SSE) with zero flicker and predictable memory.
|
|
43
|
+
- **Two render modes**: virtual window for long docs, incremental batching for “typing” effects.
|
|
44
|
+
- **Progressive diagrams** (Mermaid) and **streaming code blocks** (Monaco/Shiki) that keep up with diffs.
|
|
45
|
+
- Works with **raw Markdown strings or pre-parsed nodes**, supports **custom Vue components** inline.
|
|
46
|
+
- TypeScript-first, ship-ready defaults — import CSS and render.
|
|
47
|
+
|
|
48
|
+
## 🚀 Try It Now
|
|
16
49
|
|
|
17
50
|
- Playground (interactive demo): https://markstream-vue.simonhe.me/
|
|
18
|
-
- Interactive test page: https://markstream-vue.simonhe.me/test
|
|
51
|
+
- Interactive test page (shareable links, easy reproduction): https://markstream-vue.simonhe.me/test
|
|
52
|
+
- Docs: https://markstream-vue-docs.simonhe.me/guide/
|
|
53
|
+
- One-click StackBlitz demo: https://stackblitz.com/github/Simon-He95/markstream-vue?file=playground/src/App.vue
|
|
54
|
+
- Changelog: [CHANGELOG.md](./CHANGELOG.md)
|
|
55
|
+
- Nuxt playground: `pnpm play:nuxt`
|
|
56
|
+
- Discord: https://discord.gg/vkzdkjeRCW
|
|
19
57
|
|
|
20
|
-
|
|
21
|
-
https://markstream-vue.simonhe.me/test
|
|
58
|
+
## 💬 Community & support
|
|
22
59
|
|
|
23
|
-
|
|
60
|
+
- Discussions: https://github.com/Simon-He95/markstream-vue/discussions
|
|
61
|
+
- Discord: https://discord.gg/vkzdkjeRCW
|
|
62
|
+
- Issues: please use templates and attach a repro link (https://markstream-vue.simonhe.me/test)
|
|
24
63
|
|
|
25
|
-
|
|
64
|
+
The test page gives you an editor + live preview plus “generate share link” that encodes the input in the URL (with a fallback to open directly or pre-fill a GitHub Issue for long payloads).
|
|
26
65
|
|
|
27
66
|
## ⚡ Quick Start
|
|
28
67
|
|
|
@@ -50,6 +89,123 @@ createApp({
|
|
|
50
89
|
|
|
51
90
|
Import `markstream-vue/index.css` after your reset (e.g., Tailwind `@layer components`) so renderer styles win over utility classes. Install optional peers such as `stream-monaco`, `shiki`, `mermaid`, and `katex` only when you need Monaco code blocks, Shiki highlighting, diagrams, or math.
|
|
52
91
|
|
|
92
|
+
Enable heavy peers only when needed:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { enableKatex, enableMermaid } from 'markstream-vue'
|
|
96
|
+
import 'markstream-vue/index.css'
|
|
97
|
+
|
|
98
|
+
// after you install `mermaid` / `katex` peers
|
|
99
|
+
enableMermaid()
|
|
100
|
+
enableKatex()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Nuxt quick drop-in
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// plugins/markstream-vue.client.ts
|
|
107
|
+
import { defineNuxtPlugin } from '#app'
|
|
108
|
+
import MarkdownRender from 'markstream-vue'
|
|
109
|
+
import 'markstream-vue/index.css'
|
|
110
|
+
|
|
111
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
112
|
+
nuxtApp.vueApp.component('MarkdownRender', MarkdownRender)
|
|
113
|
+
})
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Then use `<MarkdownRender :content="md" />` in your pages.
|
|
117
|
+
|
|
118
|
+
## 🛠️ Common commands
|
|
119
|
+
|
|
120
|
+
- `pnpm dev` — playground dev server
|
|
121
|
+
- `pnpm play:nuxt` — Nuxt playground dev
|
|
122
|
+
- `pnpm build` — library + CSS build
|
|
123
|
+
- `pnpm test` — Vitest suite (`pnpm test:update` for snapshots)
|
|
124
|
+
- `pnpm typecheck` / `pnpm lint` — type and lint checks
|
|
125
|
+
|
|
126
|
+
## ⏱️ Streaming in 30 seconds
|
|
127
|
+
|
|
128
|
+
Render streamed Markdown (SSE/websocket) with incremental updates:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import type { ParsedNode } from 'markstream-vue'
|
|
132
|
+
import MarkdownRender, {
|
|
133
|
+
getMarkdown,
|
|
134
|
+
|
|
135
|
+
parseMarkdownToStructure
|
|
136
|
+
} from 'markstream-vue'
|
|
137
|
+
import { ref } from 'vue'
|
|
138
|
+
|
|
139
|
+
const nodes = ref<ParsedNode[]>([])
|
|
140
|
+
const buffer = ref('')
|
|
141
|
+
const md = getMarkdown()
|
|
142
|
+
|
|
143
|
+
function addChunk(chunk: string) {
|
|
144
|
+
buffer.value += chunk
|
|
145
|
+
nodes.value = parseMarkdownToStructure(buffer.value, md)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// e.g., inside your SSE/onmessage handler
|
|
149
|
+
eventSource.onmessage = event => addChunk(event.data)
|
|
150
|
+
|
|
151
|
+
// template
|
|
152
|
+
// <MarkdownRender
|
|
153
|
+
// :nodes="nodes"
|
|
154
|
+
// :max-live-nodes="0"
|
|
155
|
+
// :batch-rendering="{
|
|
156
|
+
// renderBatchSize: 16,
|
|
157
|
+
// renderBatchDelay: 8,
|
|
158
|
+
// }"
|
|
159
|
+
// />
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Switch rendering style per surface:
|
|
163
|
+
|
|
164
|
+
- Virtualized window (default): steady scrolling and memory usage for long docs.
|
|
165
|
+
- Incremental batches: set `:max-live-nodes="0"` for AI-like “typing” with lightweight placeholders.
|
|
166
|
+
|
|
167
|
+
### SSR / Worker usage (deterministic output)
|
|
168
|
+
|
|
169
|
+
Pre-parse Markdown on the server or in a worker and render typed nodes on the client:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
// server or worker
|
|
173
|
+
import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'
|
|
174
|
+
|
|
175
|
+
const md = getMarkdown()
|
|
176
|
+
const nodes = parseMarkdownToStructure('# Hello\n\nThis is parsed once', md)
|
|
177
|
+
// send `nodes` JSON to the client
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```vue
|
|
181
|
+
<!-- client -->
|
|
182
|
+
<MarkdownRender :nodes="nodesFromServer" />
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
This avoids client-side parsing and keeps SSR/hydration deterministic.
|
|
186
|
+
|
|
187
|
+
### Hybrid: SSR + streaming handoff
|
|
188
|
+
|
|
189
|
+
- Server: parse the first Markdown batch to nodes and serialize `initialNodes` (and the raw `initialMarkdown` if you also stream later chunks).
|
|
190
|
+
- Client: hydrate with the same parser options, then keep streaming:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import type { ParsedNode } from 'markstream-vue'
|
|
194
|
+
import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'
|
|
195
|
+
import { ref } from 'vue'
|
|
196
|
+
|
|
197
|
+
const nodes = ref<ParsedNode[]>(initialNodes)
|
|
198
|
+
const buffer = ref(initialMarkdown)
|
|
199
|
+
const md = getMarkdown() // match server setup
|
|
200
|
+
|
|
201
|
+
function addChunk(chunk: string) {
|
|
202
|
+
buffer.value += chunk
|
|
203
|
+
nodes.value = parseMarkdownToStructure(buffer.value, md)
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
This avoids re-parsing SSR content while letting later SSE/WebSocket chunks continue the stream.
|
|
208
|
+
|
|
53
209
|
## ⚙️ Performance presets
|
|
54
210
|
|
|
55
211
|
- **Virtual window (default)** – keep `max-live-nodes` at its default `320` to enable virtualization. Nodes render immediately and the renderer keeps a sliding window of elements mounted so long docs remain responsive without showing skeleton placeholders.
|
|
@@ -57,6 +213,99 @@ Import `markstream-vue/index.css` after your reset (e.g., Tailwind `@layer compo
|
|
|
57
213
|
|
|
58
214
|
Pick one mode per surface: virtualization for best scrollback and steady memory usage, or incremental batching for AI-style “typing” previews.
|
|
59
215
|
|
|
216
|
+
> Tip: In chats, combine `max-live-nodes="0"` with small `renderBatchSize` (e.g., `16`) and a tiny `renderBatchDelay` (e.g., `8ms`) to keep the “typing” feel smooth without jumping large chunks. Tune `renderBatchBudgetMs` down if you need to cap CPU per frame.
|
|
217
|
+
|
|
218
|
+
## 🧰 Key props & options (cheatsheet)
|
|
219
|
+
|
|
220
|
+
- `content` vs `nodes`: pass raw Markdown or pre-parsed nodes (from `parseMarkdownToStructure`).
|
|
221
|
+
- `max-live-nodes`: `320` (default virtualization) or `0` (incremental batches).
|
|
222
|
+
- `batchRendering`: fine-tune batches with `initialRenderBatchSize`, `renderBatchSize`, `renderBatchDelay`, `renderBatchBudgetMs`.
|
|
223
|
+
- `enableMermaid` / `enableKatex` / `enableMonaco`: opt-in heavy deps when needed.
|
|
224
|
+
- `parse-options`: reuse parser hooks (e.g., `preTransformTokens`, `requireClosingStrong`) on the component.
|
|
225
|
+
- `custom-components`: register inline Vue components for custom tags/markers.
|
|
226
|
+
|
|
227
|
+
Example: map Markdown placeholders to Vue components
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import { setCustomComponents } from 'markstream-vue'
|
|
231
|
+
|
|
232
|
+
setCustomComponents({
|
|
233
|
+
CALLOUT: () => import('./components/Callout.vue'),
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
// Markdown: [[CALLOUT:warning title="Heads up" body="Details here"]]
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Or pass per-renderer:
|
|
240
|
+
|
|
241
|
+
```vue
|
|
242
|
+
<MarkdownRender
|
|
243
|
+
:content="doc"
|
|
244
|
+
:custom-components="{
|
|
245
|
+
CALLOUT: () => import('./components/Callout.vue'),
|
|
246
|
+
}"
|
|
247
|
+
/>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Parse hooks example (match server + client):
|
|
251
|
+
|
|
252
|
+
```vue
|
|
253
|
+
<MarkdownRender
|
|
254
|
+
:content="doc"
|
|
255
|
+
:parse-options="{
|
|
256
|
+
requireClosingStrong: true,
|
|
257
|
+
preTransformTokens: (tokens) => tokens,
|
|
258
|
+
}"
|
|
259
|
+
/>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## 🔥 Where it shines
|
|
263
|
+
|
|
264
|
+
- AI/chat UIs with long-form answers and Markdown tokens arriving over SSE/websocket.
|
|
265
|
+
- Docs, changelogs, and knowledge bases that need instant load but stay responsive as they grow.
|
|
266
|
+
- Streaming diffs and code review panes that benefit from Monaco live updates.
|
|
267
|
+
- Diagram-heavy content that should render progressively (Mermaid) without blocking.
|
|
268
|
+
- Embedding Vue components in Markdown-driven surfaces (callouts, widgets, CTA buttons).
|
|
269
|
+
|
|
270
|
+
## ❓ FAQ (quick answers)
|
|
271
|
+
|
|
272
|
+
- Mermaid/KaTeX not rendering? Install the peer (`mermaid` / `katex`) and pass `:enable-mermaid="true"` / `:enable-katex="true"` or call the loader setters.
|
|
273
|
+
- Bundle size: peers are optional and not bundled; import only `markstream-vue/index.css` once. Use Shiki (`MarkdownCodeBlockNode`) when Monaco is too heavy.
|
|
274
|
+
- Custom UI: register components via `setCustomComponents` (global) or `custom-components` prop, then emit markers/placeholders in Markdown and map them to Vue components.
|
|
275
|
+
|
|
276
|
+
## 🆚 Why markstream-vue over a typical Markdown renderer?
|
|
277
|
+
|
|
278
|
+
| Needs | Typical Markdown preview | markstream-vue |
|
|
279
|
+
| --- | --- | --- |
|
|
280
|
+
| Streaming input | Re-renders whole tree, flashes | Incremental batches with virtual windowing |
|
|
281
|
+
| Large code blocks | Slow re-highlight | Monaco streaming updates + Shiki option |
|
|
282
|
+
| Diagrams | Blocks while parsing | Progressive Mermaid with graceful fallback |
|
|
283
|
+
| Custom UI | Limited slots | Inline Vue components & typed nodes |
|
|
284
|
+
| Long docs | Memory spikes | Configurable live-node cap for steady usage |
|
|
285
|
+
|
|
286
|
+
## 🗺️ Roadmap (snapshot)
|
|
287
|
+
|
|
288
|
+
- More “instant start” templates (Vite + Nuxt + Tailwind) and updated StackBlitz.
|
|
289
|
+
- Additional codeblock presets (diff-friendly Shiki themes, Monaco decoration helpers).
|
|
290
|
+
- Cookbook docs for AI/chat patterns (SSE/WebSocket, retry/resume, markdown mid-states).
|
|
291
|
+
- More showcase examples for embedding Vue components inside Markdown surfaces.
|
|
292
|
+
|
|
293
|
+
## 📦 Releases
|
|
294
|
+
|
|
295
|
+
- Latest: [Releases](https://github.com/Simon-He95/markstream-vue/releases) — see highlights and upgrade notes.
|
|
296
|
+
- Full history: [CHANGELOG.md](./CHANGELOG.md)
|
|
297
|
+
- Recent highlights (0.0.3-beta.1/beta.0):
|
|
298
|
+
- Parser bumped to `stream-markdown-parser@0.0.36` for parsing fixes.
|
|
299
|
+
- Monaco upgrades with more languages/themes + diff-friendly code block tweaks.
|
|
300
|
+
- HTML/SVG preview dialog and AST debug view in the playground.
|
|
301
|
+
|
|
302
|
+
## 🧭 Showcase & examples
|
|
303
|
+
|
|
304
|
+
Build something with markstream-vue? Open a PR to add it here (include a link + 1 screenshot/GIF). Ideal fits: AI/chat UIs, streaming docs, diff/code-review panes, or Markdown-driven pages with embedded Vue components.
|
|
305
|
+
|
|
306
|
+
- **FlowNote** — streaming Markdown note app demo (SSE + virtual window) — https://markstream-vue.simonhe.me/
|
|
307
|
+
- **AI Chat surface** — playground “test” page showing incremental batches + share links — https://markstream-vue.simonhe.me/test
|
|
308
|
+
|
|
60
309
|
## 📺 Introduction Video
|
|
61
310
|
|
|
62
311
|
A short video introduces the key features and usage of markstream-vue:
|
|
@@ -79,6 +328,25 @@ Watch on Bilibili: [Open in Bilibili](https://www.bilibili.com/video/BV17Z4qzpE9
|
|
|
79
328
|
- 🎨 Flexible code block rendering: choose Monaco editor (`CodeBlockNode`) or lightweight Shiki highlighting (`MarkdownCodeBlockNode`)
|
|
80
329
|
- 🧰 Parser toolkit: [`stream-markdown-parser`](./packages/markdown-parser) now documents how to reuse the parser in workers/SSE streams and feed `<MarkdownRender :nodes>` directly, plus APIs for registering global plugins and custom math helpers.
|
|
81
330
|
|
|
331
|
+
## 🙌 Contributing & community
|
|
332
|
+
|
|
333
|
+
- Read the contributor guide: [CONTRIBUTING.md](./CONTRIBUTING.md) and follow the PR template.
|
|
334
|
+
- Be kind and follow the [Code of Conduct](./CODE_OF_CONDUCT.md).
|
|
335
|
+
- Issues: use templates for bugs/requests; attach a repro from https://markstream-vue.simonhe.me/test when possible.
|
|
336
|
+
- Questions? Start a discussion: https://github.com/Simon-He95/markstream-vue/discussions
|
|
337
|
+
- Chat live: Discord https://discord.gg/vkzdkjeRCW
|
|
338
|
+
- Looking to contribute? Start with [good first issues](https://github.com/Simon-He95/markstream-vue/labels/good%20first%20issue).
|
|
339
|
+
- Support guide: [SUPPORT.md](./SUPPORT.md)
|
|
340
|
+
- PRs: follow Conventional Commits, add tests for parser/render changes, and include screenshots/GIFs for UI tweaks.
|
|
341
|
+
- If the project helps you, consider starring and sharing the repo — it keeps the work sustainable.
|
|
342
|
+
- Security: see [SECURITY.md](./SECURITY.md) to report vulnerabilities privately.
|
|
343
|
+
|
|
344
|
+
### Quick ways to help
|
|
345
|
+
|
|
346
|
+
- Add repro links/screenshots to existing issues.
|
|
347
|
+
- Improve docs/examples (especially streaming + SSR/worker patterns).
|
|
348
|
+
- Share playground/test links that showcase performance edge cases.
|
|
349
|
+
|
|
82
350
|
## Troubleshooting & Common Issues
|
|
83
351
|
|
|
84
352
|
Troubleshooting has moved into the docs:
|
|
@@ -87,6 +355,11 @@ https://markstream-vue-docs.simonhe.me/guide/troubleshooting
|
|
|
87
355
|
If you can't find a solution there, open a GitHub issue:
|
|
88
356
|
https://github.com/Simon-He95/markstream-vue/issues
|
|
89
357
|
|
|
358
|
+
### Report an issue quickly
|
|
359
|
+
|
|
360
|
+
1. Reproduce in the test page and click “generate share link”: https://markstream-vue.simonhe.me/test
|
|
361
|
+
2. Open a bug report with the link and a screenshot: https://github.com/Simon-He95/markstream-vue/issues/new?template=bug_report.yml
|
|
362
|
+
|
|
90
363
|
## Thanks
|
|
91
364
|
|
|
92
365
|
This project uses and benefits from:
|
package/README.zh-CN.md
CHANGED
|
@@ -4,33 +4,63 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/markstream-vue)
|
|
6
6
|
[](https://markstream-vue-docs.simonhe.me/zh/guide/)
|
|
7
|
+
[](https://markstream-vue.simonhe.me/)
|
|
8
|
+
[](https://markstream-vue.simonhe.me/test)
|
|
7
9
|
[](https://www.npmjs.com/package/markstream-vue)
|
|
8
10
|
[](https://bundlephobia.com/package/markstream-vue)
|
|
11
|
+
[](https://github.com/Simon-He95/markstream-vue/releases)
|
|
12
|
+
[](https://github.com/Simon-He95/markstream-vue/discussions)
|
|
13
|
+
[](https://discord.gg/vkzdkjeRCW)
|
|
14
|
+
[](./SUPPORT.md)
|
|
15
|
+
[](./SECURITY.md)
|
|
16
|
+
[](https://github.com/Simon-He95/markstream-vue/actions/workflows/ci.yml)
|
|
9
17
|
[](./LICENSE)
|
|
10
18
|
|
|
19
|
+
## 目录
|
|
20
|
+
|
|
21
|
+
- [速览](#速览)
|
|
22
|
+
- [立即试用](#-立即试用)
|
|
23
|
+
- [快速上手](#-快速上手)
|
|
24
|
+
- [常用命令](#-常用命令)
|
|
25
|
+
- [30 秒流式接入](#-30-秒流式接入)
|
|
26
|
+
- [性能模式](#-性能模式)
|
|
27
|
+
- [关键属性速览](#-关键属性速览)
|
|
28
|
+
- [适用场景](#-适用场景)
|
|
29
|
+
- [快问快答](#-快问快答)
|
|
30
|
+
- [为什么选择 markstream-vue](#-为什么选择-markstream-vue而不是普通-markdown-渲染器)
|
|
31
|
+
- [发布](#-发布)
|
|
32
|
+
- [案例与展示](#-案例与展示)
|
|
33
|
+
- [贡献与社区](#-贡献与社区)
|
|
34
|
+
- [社区与支持](#-社区与支持)
|
|
35
|
+
- [故障排查](#故障排查--常见问题)
|
|
11
36
|
> 📖 所有详细文档、API、示例和高级用法已迁移至 VitePress 中文文档站点:
|
|
12
37
|
> https://markstream-vue-docs.simonhe.me/zh/guide/
|
|
13
38
|
|
|
14
|
-
##
|
|
39
|
+
## 速览
|
|
15
40
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- 试用交互式测试页面以便快速验证与调试: https://markstream-vue.simonhe.me/test
|
|
41
|
+
- 为 **流式 Markdown**(AI/聊天/SSE)打造,避免闪烁,内存可预期。
|
|
42
|
+
- **双渲染模式**:长文档虚拟化窗口,或“打字机”式增量批次。
|
|
43
|
+
- **渐进式图表**(Mermaid)与 **流式代码块**(Monaco/Shiki),跟上 diff/增量输出。
|
|
44
|
+
- 同时支持 **Markdown 字符串或预解析节点**,可在 Markdown 中嵌入 **自定义 Vue 组件**。
|
|
45
|
+
- TypeScript 优先,开箱默认即可上线(导入 CSS 即用)。
|
|
22
46
|
|
|
23
|
-
|
|
47
|
+
## 🚀 立即试用
|
|
24
48
|
|
|
25
|
-
|
|
49
|
+
- Playground(交互演示): https://markstream-vue.simonhe.me/
|
|
50
|
+
- 交互测试页(可分享链接,便于复现): https://markstream-vue.simonhe.me/test
|
|
51
|
+
- 文档: https://markstream-vue-docs.simonhe.me/zh/guide/
|
|
52
|
+
- 一键 StackBlitz 体验: https://stackblitz.com/github/Simon-He95/markstream-vue?file=playground/src/App.vue
|
|
53
|
+
- 更新日志: [CHANGELOG.md](./CHANGELOG.md)
|
|
54
|
+
- Nuxt playground:`pnpm play:nuxt`
|
|
55
|
+
- Discord: https://discord.gg/vkzdkjeRCW
|
|
26
56
|
|
|
27
|
-
|
|
57
|
+
## 💬 社区与支持
|
|
28
58
|
|
|
29
|
-
|
|
59
|
+
- Discussions:https://github.com/Simon-He95/markstream-vue/discussions
|
|
60
|
+
- Discord:https://discord.gg/vkzdkjeRCW
|
|
61
|
+
- Issues:请使用模板并附上复现链接(https://markstream-vue.simonhe.me/test)
|
|
30
62
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
在 Bilibili 上观看: [Open in Bilibili](https://www.bilibili.com/video/BV17Z4qzpE9c/)
|
|
63
|
+
测试页内置编辑器 + 实时预览,并提供“生成分享链接”功能(过长内容会回退为直接打开或预填 GitHub Issue)。
|
|
34
64
|
|
|
35
65
|
## ⚡ 快速上手
|
|
36
66
|
|
|
@@ -58,6 +88,123 @@ createApp({
|
|
|
58
88
|
|
|
59
89
|
确保在 CSS reset(如 `@tailwind base` 或 `@unocss/reset`)之后导入 `markstream-vue/index.css`,最好放在 `@layer components` 中以避免 Tailwind/UnoCSS 覆盖组件样式。根据需求再按需安装可选 peer 依赖:`stream-monaco`(Monaco 代码块)、`shiki`(Shiki 高亮)、`mermaid`(Mermaid 图表)、`katex`(数学公式)。
|
|
60
90
|
|
|
91
|
+
按需启用重型依赖:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { enableKatex, enableMermaid } from 'markstream-vue'
|
|
95
|
+
import 'markstream-vue/index.css'
|
|
96
|
+
|
|
97
|
+
// 安装对应 peer 后再启用
|
|
98
|
+
enableMermaid()
|
|
99
|
+
enableKatex()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Nuxt 快速接入
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// plugins/markstream-vue.client.ts
|
|
106
|
+
import { defineNuxtPlugin } from '#app'
|
|
107
|
+
import MarkdownRender from 'markstream-vue'
|
|
108
|
+
import 'markstream-vue/index.css'
|
|
109
|
+
|
|
110
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
111
|
+
nuxtApp.vueApp.component('MarkdownRender', MarkdownRender)
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
然后在页面中直接使用 `<MarkdownRender :content=\"md\" />`。
|
|
116
|
+
|
|
117
|
+
## 🛠️ 常用命令
|
|
118
|
+
|
|
119
|
+
- `pnpm dev` — playground 开发
|
|
120
|
+
- `pnpm play:nuxt` — Nuxt playground 开发
|
|
121
|
+
- `pnpm build` — 构建库与 CSS
|
|
122
|
+
- `pnpm test` — Vitest 测试(快照用 `pnpm test:update`)
|
|
123
|
+
- `pnpm typecheck` / `pnpm lint` — 类型检查与 Lint
|
|
124
|
+
|
|
125
|
+
## ⏱️ 30 秒流式接入
|
|
126
|
+
|
|
127
|
+
用 SSE / WebSocket 增量渲染 Markdown:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import type { ParsedNode } from 'markstream-vue'
|
|
131
|
+
import MarkdownRender, {
|
|
132
|
+
getMarkdown,
|
|
133
|
+
|
|
134
|
+
parseMarkdownToStructure
|
|
135
|
+
} from 'markstream-vue'
|
|
136
|
+
import { ref } from 'vue'
|
|
137
|
+
|
|
138
|
+
const nodes = ref<ParsedNode[]>([])
|
|
139
|
+
const buffer = ref('')
|
|
140
|
+
const md = getMarkdown()
|
|
141
|
+
|
|
142
|
+
function addChunk(chunk: string) {
|
|
143
|
+
buffer.value += chunk
|
|
144
|
+
nodes.value = parseMarkdownToStructure(buffer.value, md)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 例如在 SSE / onmessage 处理器中
|
|
148
|
+
eventSource.onmessage = event => addChunk(event.data)
|
|
149
|
+
|
|
150
|
+
// template
|
|
151
|
+
// <MarkdownRender
|
|
152
|
+
// :nodes="nodes"
|
|
153
|
+
// :max-live-nodes="0"
|
|
154
|
+
// :batch-rendering="{
|
|
155
|
+
// renderBatchSize: 16,
|
|
156
|
+
// renderBatchDelay: 8,
|
|
157
|
+
// }"
|
|
158
|
+
// />
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
按页面需要切换渲染风格:
|
|
162
|
+
|
|
163
|
+
- 虚拟化窗口(默认):长文档滚动平稳、内存稳定。
|
|
164
|
+
- 增量批次:将 `:max-live-nodes="0"`,获得更明显的“打字机”体验与轻量占位。
|
|
165
|
+
|
|
166
|
+
### SSR / Worker(确定性输出)
|
|
167
|
+
|
|
168
|
+
在服务端或 Worker 预解析 Markdown,前端直接渲染节点:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
// server or worker
|
|
172
|
+
import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'
|
|
173
|
+
|
|
174
|
+
const md = getMarkdown()
|
|
175
|
+
const nodes = parseMarkdownToStructure('# Hello\n\n服务端解析一次', md)
|
|
176
|
+
// 将 nodes JSON 下发到客户端
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```vue
|
|
180
|
+
<!-- client -->
|
|
181
|
+
<MarkdownRender :nodes="nodesFromServer" />
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
这样可以避免前端解析,保持 SSR/水合的一致性。
|
|
185
|
+
|
|
186
|
+
### 混合模式:SSR + 流式续写
|
|
187
|
+
|
|
188
|
+
- 服务端:解析首批 Markdown,序列化 `initialNodes`(以及 `initialMarkdown`,便于后续流式追加)。
|
|
189
|
+
- 客户端:用相同的解析配置水合,然后继续流式追加:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import type { ParsedNode } from 'markstream-vue'
|
|
193
|
+
import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'
|
|
194
|
+
import { ref } from 'vue'
|
|
195
|
+
|
|
196
|
+
const nodes = ref<ParsedNode[]>(initialNodes)
|
|
197
|
+
const buffer = ref(initialMarkdown)
|
|
198
|
+
const md = getMarkdown() // 与服务端保持一致
|
|
199
|
+
|
|
200
|
+
function addChunk(chunk: string) {
|
|
201
|
+
buffer.value += chunk
|
|
202
|
+
nodes.value = parseMarkdownToStructure(buffer.value, md)
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
这样无需重新解析 SSR 内容,同时还能通过 SSE/WebSocket 持续追加后续片段。
|
|
207
|
+
|
|
61
208
|
## ⚙️ 性能模式
|
|
62
209
|
|
|
63
210
|
- **默认虚拟化窗口**:保持 `max-live-nodes` 默认值(`320`),渲染器会立即渲染当前窗口的节点,同时只保留有限数量的 DOM 节点,实现平滑滚动与可控内存,占位骨架极少。
|
|
@@ -65,7 +212,108 @@ createApp({
|
|
|
65
212
|
|
|
66
213
|
可根据页面类型选择最合适的模式:虚拟化适合长文档/回溯需求,增量流式适合聊天或 AI 输出面板。
|
|
67
214
|
|
|
68
|
-
|
|
215
|
+
> 小贴士:聊天场景可使用 `max-live-nodes="0"`,并将 `renderBatchSize` 调小(如 `16`),`renderBatchDelay` 设为较小值(如 `8ms`),获得平滑的“打字”节奏且避免大段跳变。如需限制单帧 CPU,可适当调低 `renderBatchBudgetMs`。
|
|
216
|
+
|
|
217
|
+
## 🧰 关键属性速览
|
|
218
|
+
|
|
219
|
+
- `content` 与 `nodes`:传原始 Markdown 或预解析节点(来自 `parseMarkdownToStructure`)。
|
|
220
|
+
- `max-live-nodes`:`320`(默认虚拟化)或 `0`(增量批次)。
|
|
221
|
+
- `batchRendering`:用 `initialRenderBatchSize`、`renderBatchSize`、`renderBatchDelay`、`renderBatchBudgetMs` 微调批次。
|
|
222
|
+
- `enableMermaid` / `enableKatex` / `enableMonaco`:按需启用重型依赖。
|
|
223
|
+
- `parse-options`:在组件上复用解析钩子(如 `preTransformTokens`、`requireClosingStrong`)。
|
|
224
|
+
- `custom-components`:为自定义标签/标记注册内嵌 Vue 组件。
|
|
225
|
+
|
|
226
|
+
示例:将 Markdown 占位符映射到 Vue 组件
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { setCustomComponents } from 'markstream-vue'
|
|
230
|
+
|
|
231
|
+
setCustomComponents({
|
|
232
|
+
CALLOUT: () => import('./components/Callout.vue'),
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// Markdown: [[CALLOUT:warning title="提示" body="具体内容"]]
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
或在组件上按需传入:
|
|
239
|
+
|
|
240
|
+
```vue
|
|
241
|
+
<MarkdownRender
|
|
242
|
+
:content="doc"
|
|
243
|
+
:custom-components="{
|
|
244
|
+
CALLOUT: () => import('./components/Callout.vue'),
|
|
245
|
+
}"
|
|
246
|
+
/>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
解析钩子示例(服务端/客户端保持一致):
|
|
250
|
+
|
|
251
|
+
```vue
|
|
252
|
+
<MarkdownRender
|
|
253
|
+
:content="doc"
|
|
254
|
+
:parse-options="{
|
|
255
|
+
requireClosingStrong: true,
|
|
256
|
+
preTransformTokens: (tokens) => tokens,
|
|
257
|
+
}"
|
|
258
|
+
/>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## 🔥 适用场景
|
|
262
|
+
|
|
263
|
+
- AI / 聊天界面:Markdown token 通过 SSE/WebSocket 持续抵达,要求无闪烁与稳定内存。
|
|
264
|
+
- 文档、变更日志、知识库:需要即时加载,同时保持长内容滚动的流畅性。
|
|
265
|
+
- 流式 diff / 代码审查:Monaco 增量更新让大代码块也能跟上变更。
|
|
266
|
+
- 图表与示意:Mermaid 渐进式渲染,避免阻塞主渲染。
|
|
267
|
+
- Markdown 驱动的界面中嵌入 Vue 组件(callout、交互式挂件、CTA 等)。
|
|
268
|
+
|
|
269
|
+
## ❓ 快问快答
|
|
270
|
+
|
|
271
|
+
- Mermaid / KaTeX 不显示?安装对应 peer(`mermaid` / `katex`),并传入 `:enable-mermaid="true"` / `:enable-katex="true"` 或调用 loader 设置函数。
|
|
272
|
+
- 体积问题:可选 peer 不会被打包,CSS 只需导入一次;对代码块可用 Shiki(`MarkdownCodeBlockNode`)替代 Monaco 以减轻体积。
|
|
273
|
+
- 自定义 UI:通过 `setCustomComponents`(全局)或 `custom-components` prop 注册组件,在 Markdown 中放置占位标记并映射到 Vue 组件。
|
|
274
|
+
|
|
275
|
+
## 🆚 为什么选择 markstream-vue,而不是普通 Markdown 渲染器?
|
|
276
|
+
|
|
277
|
+
| 需求 | 普通 Markdown 预览 | markstream-vue |
|
|
278
|
+
| --- | --- | --- |
|
|
279
|
+
| 流式输入 | 全量重渲染、易闪烁 | 虚拟窗口 + 增量批次 |
|
|
280
|
+
| 大代码块 | 重新高亮速度慢 | Monaco 流式更新 + 可选 Shiki |
|
|
281
|
+
| 图表 | 解析/渲染阻塞 | Mermaid 渐进式渲染与回退 |
|
|
282
|
+
| 自定义 UI | 插槽有限 | Markdown 内嵌 Vue 组件与类型化节点 |
|
|
283
|
+
| 长文档 | 内存峰值高 | 可配置 live-node 上限,滚动稳定 |
|
|
284
|
+
|
|
285
|
+
## 🗺️ Roadmap(快照)
|
|
286
|
+
|
|
287
|
+
- 更多「即开即用」模板(Vite / Nuxt / Tailwind)与 StackBlitz 更新。
|
|
288
|
+
- 代码块预设扩展(适合 diff 的 Shiki 主题、Monaco 装饰/标注辅助)。
|
|
289
|
+
- AI / 聊天场景的 Cookbook(SSE/WebSocket、重试与续传、Markdown 中间态处理)。
|
|
290
|
+
- 展示更多在 Markdown 中嵌入 Vue 组件的示例与实践。
|
|
291
|
+
|
|
292
|
+
## 📦 发布
|
|
293
|
+
|
|
294
|
+
- 最新版本与升级提示:[Releases](https://github.com/Simon-He95/markstream-vue/releases)
|
|
295
|
+
- 完整历史:[CHANGELOG.md](./CHANGELOG.md)
|
|
296
|
+
- 最新亮点(0.0.3-beta.1/beta.0):
|
|
297
|
+
- 解析器升级到 `stream-markdown-parser@0.0.36`,修复多项解析问题。
|
|
298
|
+
- Monaco 升级,更多语言/主题,代码块对 diff 更友好。
|
|
299
|
+
- Playground 增加 HTML/SVG 预览对话框与 AST 调试视图。
|
|
300
|
+
|
|
301
|
+
## 🧭 案例与展示
|
|
302
|
+
|
|
303
|
+
用 markstream-vue 做了什么?欢迎提 PR 把你的项目放到这里(附链接 + 截图/GIF)。理想场景:AI/聊天界面、流式文档、diff/代码审查、或在 Markdown 驱动页面中嵌入 Vue 组件。
|
|
304
|
+
|
|
305
|
+
- **FlowNote** —— 流式 Markdown 笔记示例(SSE + 虚拟化窗口):https://markstream-vue.simonhe.me/
|
|
306
|
+
- **AI Chat 场景** —— playground “test” 页展示增量批次与分享链接:https://markstream-vue.simonhe.me/test
|
|
307
|
+
|
|
308
|
+
## 介绍视频
|
|
309
|
+
|
|
310
|
+
一段短视频介绍了 markstream-vue 的关键特性与使用方式。
|
|
311
|
+
|
|
312
|
+
[](https://www.bilibili.com/video/BV17Z4qzpE9c/)
|
|
313
|
+
|
|
314
|
+
在 Bilibili 上观看: [Open in Bilibili](https://www.bilibili.com/video/BV17Z4qzpE9c/)
|
|
315
|
+
|
|
316
|
+
## 核心特性
|
|
69
317
|
|
|
70
318
|
- ⚡ 极致性能:为流式场景设计的最小化重渲染和高效 DOM 更新
|
|
71
319
|
- 🌊 流式优先:原生支持不完整或频繁更新的 token 化 Markdown 内容
|
|
@@ -79,6 +327,25 @@ createApp({
|
|
|
79
327
|
- 🎨 灵活的代码块渲染:可选 Monaco 编辑器 (`CodeBlockNode`) 或轻量的 Shiki 高亮 (`MarkdownCodeBlockNode`)
|
|
80
328
|
- 🧰 解析工具集:[`stream-markdown-parser`](./packages/markdown-parser) 文档现已覆盖如何在 Worker/SSE 流中复用解析器、直接向 `<MarkdownRender :nodes>` 输送 AST、以及注册全局插件/数学辅助函数的方式。
|
|
81
329
|
|
|
330
|
+
## 🙌 贡献与社区
|
|
331
|
+
|
|
332
|
+
- 阅读贡献指南与 PR 模板:[CONTRIBUTING.md](./CONTRIBUTING.md)
|
|
333
|
+
- 遵守 [行为准则](./CODE_OF_CONDUCT.md)。
|
|
334
|
+
- 提交 Issue 时使用模板;渲染问题尽量附上测试页复现链接:https://markstream-vue.simonhe.me/test
|
|
335
|
+
- 有问题先讨论:https://github.com/Simon-He95/markstream-vue/discussions
|
|
336
|
+
- 实时交流:Discord https://discord.gg/vkzdkjeRCW
|
|
337
|
+
- 新手贡献入口:[good first issues](https://github.com/Simon-He95/markstream-vue/labels/good%20first%20issue)
|
|
338
|
+
- 支持与求助入口:[SUPPORT.md](./SUPPORT.md)
|
|
339
|
+
- 提交 PR 时遵循 Conventional Commits,渲染/解析改动补充测试,UI 改动附上截图/GIF。
|
|
340
|
+
- 如果本项目对你有帮助,欢迎点 Star、分享给需要的人,助力项目持续演进。
|
|
341
|
+
- 安全披露:请通过 [SECURITY.md](./SECURITY.md) 中的邮件私下报告潜在漏洞。
|
|
342
|
+
|
|
343
|
+
### 快速参与的方式
|
|
344
|
+
|
|
345
|
+
- 在现有 issue 中补充复现链接/截图。
|
|
346
|
+
- 完善文档或示例(尤其是流式 + SSR/Worker 场景)。
|
|
347
|
+
- 分享 playground/test 链接,展示性能边界或有趣用法。
|
|
348
|
+
|
|
82
349
|
## 故障排查 & 常见问题
|
|
83
350
|
|
|
84
351
|
详细故障排查与常见问题已迁移至文档站点:
|
|
@@ -87,6 +354,11 @@ https://markstream-vue-docs.simonhe.me/zh/guide/troubleshooting
|
|
|
87
354
|
如需更多帮助,请到 GitHub Issues 创建问题:
|
|
88
355
|
https://github.com/Simon-He95/markstream-vue/issues
|
|
89
356
|
|
|
357
|
+
### 快速提交问题
|
|
358
|
+
|
|
359
|
+
1. 在测试页复现并点击“生成分享链接”:https://markstream-vue.simonhe.me/test
|
|
360
|
+
2. 打开 Bug 模板并附上链接与截图:https://github.com/Simon-He95/markstream-vue/issues/new?template=bug_report.yml
|
|
361
|
+
|
|
90
362
|
## 鸣谢
|
|
91
363
|
|
|
92
364
|
本项目使用并受益于:
|