markstream-react 0.0.50 → 0.0.53
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 +173 -106
- package/dist/Tooltip-DSM4ImfZ.js +207 -0
- package/dist/codeBlockExtraProps-RaPzFq78.js +3 -0
- package/dist/index-D0DspXy9.js +85 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.px.css +1 -1
- package/dist/index.tailwind.css +1 -1
- package/dist/markstream-react.css +1 -1
- package/dist/next.d.ts +41 -19
- package/dist/next.js +1 -1
- package/dist/server.d.ts +67 -45
- package/dist/server.js +1 -1
- package/dist/tailwind.cjs +4 -0
- package/dist/tailwind.d.ts +2 -0
- package/dist/tailwind.js +2 -0
- package/dist/types/components/AdmonitionNode/AdmonitionNode.d.ts +2 -1
- package/dist/types/components/BlockquoteNode/BlockquoteNode.d.ts +2 -1
- package/dist/types/components/CheckboxNode/CheckboxNode.d.ts +2 -1
- package/dist/types/components/CodeBlockNode/CodeBlockNode.d.ts +2 -1
- package/dist/types/components/CodeBlockNode/PreCodeNode.d.ts +2 -1
- package/dist/types/components/D2BlockNode/D2BlockNode.d.ts +2 -1
- package/dist/types/components/DefinitionListNode/DefinitionListNode.d.ts +2 -1
- package/dist/types/components/EmojiNode/EmojiNode.d.ts +2 -1
- package/dist/types/components/EmphasisNode/EmphasisNode.d.ts +2 -1
- package/dist/types/components/FootnoteAnchorNode/FootnoteAnchorNode.d.ts +2 -1
- package/dist/types/components/FootnoteNode/FootnoteNode.d.ts +2 -1
- package/dist/types/components/FootnoteReferenceNode/FootnoteReferenceNode.d.ts +2 -1
- package/dist/types/components/HardBreakNode/HardBreakNode.d.ts +2 -1
- package/dist/types/components/HeadingNode/HeadingNode.d.ts +2 -1
- package/dist/types/components/HighlightNode/HighlightNode.d.ts +2 -1
- package/dist/types/components/HtmlBlockNode/HtmlBlockNode.d.ts +1 -1
- package/dist/types/components/HtmlInlineNode/HtmlInlineNode.d.ts +6 -2
- package/dist/types/components/ImageNode/ImageNode.d.ts +1 -1
- package/dist/types/components/InfographicBlockNode/InfographicBlockNode.d.ts +2 -1
- package/dist/types/components/InlineCodeNode/InlineCodeNode.d.ts +2 -1
- package/dist/types/components/InsertNode/InsertNode.d.ts +2 -1
- package/dist/types/components/LinkNode/LinkNode.d.ts +2 -1
- package/dist/types/components/ListItemNode/ListItemNode.d.ts +2 -1
- package/dist/types/components/ListNode/ListNode.d.ts +2 -1
- package/dist/types/components/MarkdownCodeBlockNode/MarkdownCodeBlockNode.d.ts +2 -1
- package/dist/types/components/Math/MathBlockNode.d.ts +1 -1
- package/dist/types/components/Math/MathInlineNode.d.ts +1 -1
- package/dist/types/components/MermaidBlockNode/MermaidBlockNode.d.ts +2 -1
- package/dist/types/components/NodeRenderer/FallbackComponent.d.ts +2 -1
- package/dist/types/components/NodeRenderer.d.ts +2 -1
- package/dist/types/components/ParagraphNode/ParagraphNode.d.ts +2 -1
- package/dist/types/components/ReferenceNode/ReferenceNode.d.ts +1 -1
- package/dist/types/components/StrikethroughNode/StrikethroughNode.d.ts +2 -1
- package/dist/types/components/StrongNode/StrongNode.d.ts +2 -1
- package/dist/types/components/SubscriptNode/SubscriptNode.d.ts +2 -1
- package/dist/types/components/SuperscriptNode/SuperscriptNode.d.ts +2 -1
- package/dist/types/components/TableNode/TableNode.d.ts +2 -1
- package/dist/types/components/TextNode/TextNode.d.ts +2 -1
- package/dist/types/components/ThematicBreakNode/ThematicBreakNode.d.ts +2 -1
- package/dist/types/components/VmrContainerNode/VmrContainerNode.d.ts +2 -1
- package/dist/types/context/viewportPriority.d.ts +1 -1
- package/dist/types/customComponents.d.ts +20 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/next.d.ts +7 -3
- package/dist/types/renderers/renderChildren.d.ts +1 -1
- package/dist/types/renderers/renderNode.d.ts +1 -1
- package/dist/types/server-renderer/index.d.ts +45 -41
- package/dist/types/server.d.ts +2 -2
- package/dist/types/styles.d.ts +1 -0
- package/dist/types/types.d.ts +8 -3
- package/dist/types/utils/customHtmlTag.d.ts +5 -4
- package/dist/types/utils/htmlToReact.d.ts +17 -6
- package/package.json +41 -42
- package/dist/Tooltip-D786qxIk.js +0 -207
- package/dist/codeBlockExtraProps-7n9FWJXk.js +0 -3
- package/dist/index-o_DISWz2.js +0 -85
- package/dist/tailwind.ts +0 -3
package/README.md
CHANGED
|
@@ -1,166 +1,233 @@
|
|
|
1
1
|
# markstream-react
|
|
2
2
|
|
|
3
|
-
React renderer
|
|
3
|
+
React/Next.js streaming Markdown renderer for AI chat, SSE/WebSocket output, long AI responses, Mermaid, KaTeX, and code blocks.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`markstream-react` is the React renderer in the Markstream family. It renders raw Markdown strings with `content`, and it can also accept pre-parsed `nodes` when a worker or store already owns parsing.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
pnpm
|
|
10
|
+
pnpm add markstream-react
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
Optional features are peer dependencies. Install only what your Markdown output needs.
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
pnpm --filter markstream-react build
|
|
15
|
-
pnpm --filter markstream-react build:analyze
|
|
16
|
-
pnpm --filter markstream-react size:check
|
|
17
|
-
```
|
|
15
|
+
## Quick Start
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
Import one Markstream CSS file explicitly. The JavaScript entry does not inject styles automatically.
|
|
20
18
|
|
|
21
19
|
```tsx
|
|
22
|
-
import
|
|
20
|
+
import MarkdownRender from 'markstream-react'
|
|
23
21
|
import 'markstream-react/index.css'
|
|
24
22
|
|
|
25
|
-
export default function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
export default function ChatMessage({
|
|
24
|
+
content,
|
|
25
|
+
isDone,
|
|
26
|
+
}: {
|
|
27
|
+
content: string
|
|
28
|
+
isDone: boolean
|
|
29
|
+
}) {
|
|
30
|
+
return <MarkdownRender content={content} final={isDone} fade={false} />
|
|
29
31
|
}
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
Use `markstream-react/index.px.css` instead when your app scales the root font size on mobile and you want renderer sizing to stay pixel-based.
|
|
35
|
+
|
|
36
|
+
## Streaming Example
|
|
37
|
+
|
|
38
|
+
For most SSE/WebSocket chat surfaces, accumulate the Markdown string and pass `content` plus `final`:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import MarkdownRender from 'markstream-react'
|
|
42
|
+
import { useEffect, useState } from 'react'
|
|
43
|
+
import 'markstream-react/index.css'
|
|
44
|
+
|
|
45
|
+
export function ChatView() {
|
|
46
|
+
const [content, setContent] = useState('')
|
|
47
|
+
const [isDone, setIsDone] = useState(false)
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const eventSource = new EventSource('/api/chat/stream')
|
|
51
|
+
eventSource.onmessage = (event) => {
|
|
52
|
+
if (event.data === '[DONE]') {
|
|
53
|
+
setIsDone(true)
|
|
54
|
+
eventSource.close()
|
|
55
|
+
return
|
|
56
|
+
}
|
|
33
57
|
|
|
34
|
-
|
|
58
|
+
const data = JSON.parse(event.data) as { content?: string }
|
|
59
|
+
setContent(prev => prev + (data.content ?? ''))
|
|
60
|
+
}
|
|
35
61
|
|
|
36
|
-
|
|
62
|
+
return () => eventSource.close()
|
|
63
|
+
}, [])
|
|
37
64
|
|
|
38
|
-
|
|
39
|
-
|
|
65
|
+
return <MarkdownRender content={content} final={isDone} fade={false} />
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If parsing is already external, pass `nodes`. Use a per-message parser id so generated code-block DOM ids stay unique across chat lists.
|
|
40
70
|
|
|
41
71
|
```tsx
|
|
42
|
-
import
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
72
|
+
import MarkdownRender from 'markstream-react'
|
|
73
|
+
import { useMemo } from 'react'
|
|
74
|
+
import { getMarkdown, parseMarkdownToStructure } from 'stream-markdown-parser'
|
|
75
|
+
|
|
76
|
+
export function ParsedChatMessage({
|
|
77
|
+
messageId,
|
|
78
|
+
content,
|
|
79
|
+
isDone,
|
|
80
|
+
}: {
|
|
81
|
+
messageId: string
|
|
82
|
+
content: string
|
|
83
|
+
isDone: boolean
|
|
84
|
+
}) {
|
|
85
|
+
const md = useMemo(() => getMarkdown(`chat-${messageId}`), [messageId])
|
|
86
|
+
const nodes = useMemo(
|
|
87
|
+
() => parseMarkdownToStructure(content, md, { final: isDone }),
|
|
88
|
+
[content, isDone, md],
|
|
52
89
|
)
|
|
90
|
+
|
|
91
|
+
return <MarkdownRender nodes={nodes} final={isDone} fade={false} />
|
|
53
92
|
}
|
|
54
93
|
```
|
|
55
94
|
|
|
56
|
-
##
|
|
95
|
+
## Next.js SSR
|
|
57
96
|
|
|
58
|
-
|
|
97
|
+
Import styles once from your app shell:
|
|
59
98
|
|
|
60
99
|
```tsx
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
mermaidProps={{
|
|
64
|
-
showHeader: false,
|
|
65
|
-
renderDebounceMs: 180,
|
|
66
|
-
previewPollDelayMs: 500,
|
|
67
|
-
}}
|
|
68
|
-
d2Props={{ progressiveIntervalMs: 500 }}
|
|
69
|
-
infographicProps={{ showHeader: false }}
|
|
70
|
-
/>
|
|
100
|
+
// app/layout.tsx or pages/_app.tsx
|
|
101
|
+
import 'markstream-react/index.css'
|
|
71
102
|
```
|
|
72
103
|
|
|
73
|
-
|
|
74
|
-
- These props are forwarded to the built-in Mermaid / D2 / Infographic blocks and to custom `mermaid` / `d2` / `infographic` overrides registered with `setCustomComponents(...)`.
|
|
75
|
-
- `viewportPriority` applies to those heavy nodes too, so offscreen graphs will not keep doing background work while the text stream is still updating.
|
|
104
|
+
Use the root package in client components for live SSE/WebSocket streams:
|
|
76
105
|
|
|
77
|
-
|
|
106
|
+
```tsx
|
|
107
|
+
'use client'
|
|
78
108
|
|
|
79
|
-
|
|
109
|
+
import MarkdownRender from 'markstream-react'
|
|
110
|
+
|
|
111
|
+
export function LiveMessage({ content, isDone }: { content: string, isDone: boolean }) {
|
|
112
|
+
return <MarkdownRender content={content} final={isDone} fade={false} />
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Use `markstream-react/next` for SSR-first Markdown with client enhancement, or `markstream-react/server` for server-only rendering:
|
|
80
117
|
|
|
81
118
|
```tsx
|
|
82
|
-
import
|
|
83
|
-
import { setCustomComponents } from 'markstream-react'
|
|
119
|
+
import MarkdownRender from 'markstream-react/next'
|
|
84
120
|
|
|
85
|
-
function
|
|
86
|
-
return <
|
|
121
|
+
export default function Page() {
|
|
122
|
+
return <MarkdownRender content="# Server HTML first" final />
|
|
87
123
|
}
|
|
88
|
-
|
|
89
|
-
setCustomComponents('docs', {
|
|
90
|
-
echarts: EChartsBlockNode,
|
|
91
|
-
})
|
|
92
124
|
```
|
|
93
125
|
|
|
94
|
-
|
|
95
|
-
- `echarts` only catches fences whose language is `echarts`.
|
|
96
|
-
- Code block routing priority is exact language key -> built-in `mermaid` / `d2` / `infographic` routes -> `code_block`.
|
|
97
|
-
- Custom `mermaid` / `d2` / `infographic` overrides keep their specialized top-level props; other custom language keys use the normal custom component contract (`node`, `ctx`, `renderNode`, and friends).
|
|
126
|
+
## Optional Peers
|
|
98
127
|
|
|
99
|
-
|
|
128
|
+
| Feature | Package |
|
|
129
|
+
| --- | --- |
|
|
130
|
+
| Shiki code blocks | `stream-markdown` |
|
|
131
|
+
| Monaco editor code blocks | `stream-monaco` |
|
|
132
|
+
| Mermaid diagrams | `mermaid` |
|
|
133
|
+
| KaTeX math | `katex` |
|
|
134
|
+
| D2 diagrams | `@terrastruct/d2` |
|
|
135
|
+
| Infographic blocks | `@antv/infographic` |
|
|
100
136
|
|
|
101
|
-
|
|
137
|
+
KaTeX still needs its CSS in your app when math rendering is enabled:
|
|
102
138
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
- `previewPollMaxDelayMs`: cap for preview polling backoff.
|
|
107
|
-
- `previewPollMaxAttempts`: maximum retry count while the Mermaid source is still incomplete.
|
|
139
|
+
```tsx
|
|
140
|
+
import 'katex/dist/katex.min.css'
|
|
141
|
+
```
|
|
108
142
|
|
|
109
|
-
##
|
|
143
|
+
## Tailwind
|
|
110
144
|
|
|
111
|
-
-
|
|
112
|
-
- Infrequent language icons are split into an async chunk and loaded on demand.
|
|
113
|
-
- To avoid first-hit fallback icons, preload once when the app is idle:
|
|
145
|
+
Non-Tailwind projects should import the precompiled CSS:
|
|
114
146
|
|
|
115
147
|
```tsx
|
|
116
|
-
import
|
|
148
|
+
import 'markstream-react/index.css'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Tailwind projects can import the Tailwind-ready CSS and include the extracted class list in `tailwind.config.js`:
|
|
117
152
|
|
|
118
|
-
|
|
119
|
-
|
|
153
|
+
```tsx
|
|
154
|
+
import 'markstream-react/index.tailwind.css'
|
|
120
155
|
```
|
|
121
156
|
|
|
122
|
-
|
|
157
|
+
```js
|
|
158
|
+
module.exports = {
|
|
159
|
+
content: [
|
|
160
|
+
'./src/**/*.{js,ts,jsx,tsx}',
|
|
161
|
+
require('markstream-react/tailwind'),
|
|
162
|
+
],
|
|
163
|
+
}
|
|
164
|
+
```
|
|
123
165
|
|
|
124
|
-
|
|
125
|
-
- Tailwind projects (avoid duplicate utilities): import `markstream-react/index.tailwind.css` and add `require('markstream-react/tailwind')` to your `tailwind.config.js` `content`.
|
|
166
|
+
## Custom Components
|
|
126
167
|
|
|
127
|
-
|
|
168
|
+
For HTML-like custom tags in new React code, prefer renderer-local component maps:
|
|
128
169
|
|
|
129
|
-
|
|
170
|
+
- `streamingComponents` receives parser-backed `NodeComponentProps`, including `node.attrs`, `node.content`, and `node.loading`.
|
|
171
|
+
- `htmlComponents` renders through the raw/dynamic HTML path and receives normal React props plus `children`.
|
|
130
172
|
|
|
131
173
|
```tsx
|
|
132
174
|
import type { NodeComponentProps } from 'markstream-react'
|
|
133
|
-
import
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
customId={ctx?.customId}
|
|
143
|
-
isDark={ctx?.isDark}
|
|
144
|
-
typewriter={false}
|
|
145
|
-
batchRendering={false}
|
|
146
|
-
deferNodesUntilVisible={false}
|
|
147
|
-
viewportPriority={false}
|
|
148
|
-
maxLiveNodes={0}
|
|
149
|
-
/>
|
|
150
|
-
</div>
|
|
151
|
-
)
|
|
175
|
+
import type React from 'react'
|
|
176
|
+
import MarkdownRender from 'markstream-react'
|
|
177
|
+
|
|
178
|
+
function DocumentLink(props: NodeComponentProps<{ type: 'documentlink', content: string, loading?: boolean }>) {
|
|
179
|
+
return <span aria-busy={props.node.loading || undefined}>{props.node.content}</span>
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function Badge({ kind, children }: React.PropsWithChildren<{ kind?: string }>) {
|
|
183
|
+
return <span data-kind={kind}>{children}</span>
|
|
152
184
|
}
|
|
153
185
|
|
|
154
|
-
|
|
186
|
+
const renderer = (
|
|
187
|
+
<MarkdownRender
|
|
188
|
+
content={content}
|
|
189
|
+
final={isDone}
|
|
190
|
+
streamingComponents={{ documentlink: DocumentLink }}
|
|
191
|
+
htmlComponents={{ badge: Badge }}
|
|
192
|
+
/>
|
|
193
|
+
)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`streamingComponents` keys are normalized and automatically added to the parser's effective `customHtmlTags`, so incomplete tags can render while content is streaming.
|
|
197
|
+
|
|
198
|
+
`customHtmlTags` remains available as a lower-level parser option. `setCustomComponents` and `customId` also remain supported for compatibility, shared application-level registration, and existing node overrides:
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import MarkdownRender, { setCustomComponents } from 'markstream-react'
|
|
202
|
+
|
|
203
|
+
setCustomComponents('chat', {
|
|
204
|
+
documentlink: DocumentLink,
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
const legacyRenderer = (
|
|
208
|
+
<MarkdownRender
|
|
209
|
+
customId="chat"
|
|
210
|
+
customHtmlTags={['documentlink']}
|
|
211
|
+
content={content}
|
|
212
|
+
/>
|
|
213
|
+
)
|
|
155
214
|
```
|
|
156
215
|
|
|
157
|
-
|
|
216
|
+
Without `customHtmlTags` or `streamingComponents`, registered tag components render through the raw HTML path and receive HTML-style props/children instead of `props.node`. HTML safety is still handled by `htmlPolicy` and sanitization; the API split is not a security boundary.
|
|
217
|
+
|
|
218
|
+
## When Not to Use It
|
|
219
|
+
|
|
220
|
+
Use `react-markdown`, `marked`, or `markdown-it` when you only render short static Markdown, need the smallest possible Markdown stack, or already have a complete remark/rehype pipeline and do not need streaming mid-state handling.
|
|
221
|
+
|
|
222
|
+
## Type Exports
|
|
158
223
|
|
|
159
|
-
|
|
224
|
+
The package root exports the public component and renderer types, including `NodeRendererProps`, `NodeComponentProps`, `StreamingComponentMap`, `HtmlComponentMap`, `RenderContext`, `RenderNodeFn`, `CustomComponentMap`, and code-block option types.
|
|
160
225
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
-
|
|
165
|
-
-
|
|
166
|
-
-
|
|
226
|
+
## Development
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pnpm --filter markstream-react dev
|
|
230
|
+
pnpm --filter markstream-react build
|
|
231
|
+
pnpm --filter markstream-react check:exports
|
|
232
|
+
pnpm --filter markstream-react size:check
|
|
233
|
+
```
|