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.
Files changed (74) hide show
  1. package/README.md +173 -106
  2. package/dist/Tooltip-DSM4ImfZ.js +207 -0
  3. package/dist/codeBlockExtraProps-RaPzFq78.js +3 -0
  4. package/dist/index-D0DspXy9.js +85 -0
  5. package/dist/index.css +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.js +1 -1
  8. package/dist/index.px.css +1 -1
  9. package/dist/index.tailwind.css +1 -1
  10. package/dist/markstream-react.css +1 -1
  11. package/dist/next.d.ts +41 -19
  12. package/dist/next.js +1 -1
  13. package/dist/server.d.ts +67 -45
  14. package/dist/server.js +1 -1
  15. package/dist/tailwind.cjs +4 -0
  16. package/dist/tailwind.d.ts +2 -0
  17. package/dist/tailwind.js +2 -0
  18. package/dist/types/components/AdmonitionNode/AdmonitionNode.d.ts +2 -1
  19. package/dist/types/components/BlockquoteNode/BlockquoteNode.d.ts +2 -1
  20. package/dist/types/components/CheckboxNode/CheckboxNode.d.ts +2 -1
  21. package/dist/types/components/CodeBlockNode/CodeBlockNode.d.ts +2 -1
  22. package/dist/types/components/CodeBlockNode/PreCodeNode.d.ts +2 -1
  23. package/dist/types/components/D2BlockNode/D2BlockNode.d.ts +2 -1
  24. package/dist/types/components/DefinitionListNode/DefinitionListNode.d.ts +2 -1
  25. package/dist/types/components/EmojiNode/EmojiNode.d.ts +2 -1
  26. package/dist/types/components/EmphasisNode/EmphasisNode.d.ts +2 -1
  27. package/dist/types/components/FootnoteAnchorNode/FootnoteAnchorNode.d.ts +2 -1
  28. package/dist/types/components/FootnoteNode/FootnoteNode.d.ts +2 -1
  29. package/dist/types/components/FootnoteReferenceNode/FootnoteReferenceNode.d.ts +2 -1
  30. package/dist/types/components/HardBreakNode/HardBreakNode.d.ts +2 -1
  31. package/dist/types/components/HeadingNode/HeadingNode.d.ts +2 -1
  32. package/dist/types/components/HighlightNode/HighlightNode.d.ts +2 -1
  33. package/dist/types/components/HtmlBlockNode/HtmlBlockNode.d.ts +1 -1
  34. package/dist/types/components/HtmlInlineNode/HtmlInlineNode.d.ts +6 -2
  35. package/dist/types/components/ImageNode/ImageNode.d.ts +1 -1
  36. package/dist/types/components/InfographicBlockNode/InfographicBlockNode.d.ts +2 -1
  37. package/dist/types/components/InlineCodeNode/InlineCodeNode.d.ts +2 -1
  38. package/dist/types/components/InsertNode/InsertNode.d.ts +2 -1
  39. package/dist/types/components/LinkNode/LinkNode.d.ts +2 -1
  40. package/dist/types/components/ListItemNode/ListItemNode.d.ts +2 -1
  41. package/dist/types/components/ListNode/ListNode.d.ts +2 -1
  42. package/dist/types/components/MarkdownCodeBlockNode/MarkdownCodeBlockNode.d.ts +2 -1
  43. package/dist/types/components/Math/MathBlockNode.d.ts +1 -1
  44. package/dist/types/components/Math/MathInlineNode.d.ts +1 -1
  45. package/dist/types/components/MermaidBlockNode/MermaidBlockNode.d.ts +2 -1
  46. package/dist/types/components/NodeRenderer/FallbackComponent.d.ts +2 -1
  47. package/dist/types/components/NodeRenderer.d.ts +2 -1
  48. package/dist/types/components/ParagraphNode/ParagraphNode.d.ts +2 -1
  49. package/dist/types/components/ReferenceNode/ReferenceNode.d.ts +1 -1
  50. package/dist/types/components/StrikethroughNode/StrikethroughNode.d.ts +2 -1
  51. package/dist/types/components/StrongNode/StrongNode.d.ts +2 -1
  52. package/dist/types/components/SubscriptNode/SubscriptNode.d.ts +2 -1
  53. package/dist/types/components/SuperscriptNode/SuperscriptNode.d.ts +2 -1
  54. package/dist/types/components/TableNode/TableNode.d.ts +2 -1
  55. package/dist/types/components/TextNode/TextNode.d.ts +2 -1
  56. package/dist/types/components/ThematicBreakNode/ThematicBreakNode.d.ts +2 -1
  57. package/dist/types/components/VmrContainerNode/VmrContainerNode.d.ts +2 -1
  58. package/dist/types/context/viewportPriority.d.ts +1 -1
  59. package/dist/types/customComponents.d.ts +20 -1
  60. package/dist/types/index.d.ts +2 -2
  61. package/dist/types/next.d.ts +7 -3
  62. package/dist/types/renderers/renderChildren.d.ts +1 -1
  63. package/dist/types/renderers/renderNode.d.ts +1 -1
  64. package/dist/types/server-renderer/index.d.ts +45 -41
  65. package/dist/types/server.d.ts +2 -2
  66. package/dist/types/styles.d.ts +1 -0
  67. package/dist/types/types.d.ts +8 -3
  68. package/dist/types/utils/customHtmlTag.d.ts +5 -4
  69. package/dist/types/utils/htmlToReact.d.ts +17 -6
  70. package/package.json +41 -42
  71. package/dist/Tooltip-D786qxIk.js +0 -207
  72. package/dist/codeBlockExtraProps-7n9FWJXk.js +0 -3
  73. package/dist/index-o_DISWz2.js +0 -85
  74. package/dist/tailwind.ts +0 -3
package/README.md CHANGED
@@ -1,166 +1,233 @@
1
1
  # markstream-react
2
2
 
3
- React renderer that consumes the structured AST output from `stream-markdown-parser` and renders it with lightweight semantic HTML components. This is the React counter-part to the Vue renderer that powers `markstream-vue`.
3
+ React/Next.js streaming Markdown renderer for AI chat, SSE/WebSocket output, long AI responses, Mermaid, KaTeX, and code blocks.
4
4
 
5
- ## Development
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 --filter markstream-react dev
10
+ pnpm add markstream-react
9
11
  ```
10
12
 
11
- ## Build
13
+ Optional features are peer dependencies. Install only what your Markdown output needs.
12
14
 
13
- ```bash
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
- ## Usage
17
+ Import one Markstream CSS file explicitly. The JavaScript entry does not inject styles automatically.
20
18
 
21
19
  ```tsx
22
- import NodeRenderer from 'markstream-react'
20
+ import MarkdownRender from 'markstream-react'
23
21
  import 'markstream-react/index.css'
24
22
 
25
- export default function Article({ markdown }: { markdown: string }) {
26
- return (
27
- <NodeRenderer content={markdown} />
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
- If your app scales root font size on mobile (`html` / `body`), use `markstream-react/index.px.css` to prevent `rem`-based global scaling side effects.
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
- You can also pass a pre-parsed `nodes` array if you already have AST data.
58
+ const data = JSON.parse(event.data) as { content?: string }
59
+ setContent(prev => prev + (data.content ?? ''))
60
+ }
35
61
 
36
- ## Streaming best practices
62
+ return () => eventSource.close()
63
+ }, [])
37
64
 
38
- - For high-frequency SSE / token streaming, prefer parsing outside the component and pass `nodes` instead of reparsing the full `content` string every chunk.
39
- - Keep `viewportPriority` enabled unless you explicitly want eager rendering. Mermaid / Monaco / D2 blocks now stay idle while offscreen and resume when they approach the viewport.
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 NodeRenderer from 'markstream-react'
43
-
44
- export default function StreamView({ nodes, final }: { nodes: any[], final: boolean }) {
45
- return (
46
- <NodeRenderer
47
- nodes={nodes}
48
- final={final}
49
- viewportPriority
50
- deferNodesUntilVisible
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
- ## Heavy-node prop forwarding
95
+ ## Next.js SSR
57
96
 
58
- `NodeRenderer` can forward renderer-level props directly into Mermaid / D2 / Infographic blocks:
97
+ Import styles once from your app shell:
59
98
 
60
99
  ```tsx
61
- <NodeRenderer
62
- content={markdown}
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
- Notes:
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
- ## Language-specific code block overrides
106
+ ```tsx
107
+ 'use client'
78
108
 
79
- You can register a custom component under a fenced language key without wrapping the generic `code_block` renderer:
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 type { NodeComponentProps } from 'markstream-react'
83
- import { setCustomComponents } from 'markstream-react'
119
+ import MarkdownRender from 'markstream-react/next'
84
120
 
85
- function EChartsBlockNode(props: NodeComponentProps<any>) {
86
- return <div data-language={String(props.node?.language)}>{String(props.node?.code || '')}</div>
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
- Notes:
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
- ## Mermaid tuning
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
- Common `mermaidProps` keys for streaming scenarios:
137
+ KaTeX still needs its CSS in your app when math rendering is enabled:
102
138
 
103
- - `renderDebounceMs`: delay progressive work during rapid token bursts.
104
- - `contentStableDelayMs`: how long source mode waits before auto-switching back to preview when content stabilizes.
105
- - `previewPollDelayMs`: initial delay before preview polling tries to upgrade a partial preview into a full render.
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
- ## Bundle size notes
143
+ ## Tailwind
110
144
 
111
- - Optional peers are not bundled; install only what you use (`stream-monaco`, `stream-markdown`, `mermaid`, `katex`, etc.).
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 { preloadExtendedLanguageIcons } from 'markstream-react'
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
- if (typeof window !== 'undefined')
119
- void preloadExtendedLanguageIcons()
153
+ ```tsx
154
+ import 'markstream-react/index.tailwind.css'
120
155
  ```
121
156
 
122
- ## Tailwind
157
+ ```js
158
+ module.exports = {
159
+ content: [
160
+ './src/**/*.{js,ts,jsx,tsx}',
161
+ require('markstream-react/tailwind'),
162
+ ],
163
+ }
164
+ ```
123
165
 
124
- - Non-Tailwind projects: keep importing `markstream-react/index.css` (includes precompiled utilities for the renderer).
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
- ## Custom components (e.g. `<thinking>`)
168
+ For HTML-like custom tags in new React code, prefer renderer-local component maps:
128
169
 
129
- Custom tag-like blocks are exposed as nodes with `type: 'thinking'` (the tag name, no angle brackets) when you register the tag in `customHtmlTags` or register a custom component mapping for it.
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 NodeRenderer, { setCustomComponents } from 'markstream-react'
134
-
135
- function ThinkingNode(props: NodeComponentProps<{ type: 'thinking', content: string }>) {
136
- const { node, ctx } = props
137
- return (
138
- <div className="thinking-node">
139
- <div className="thinking-title">Thinking</div>
140
- <NodeRenderer
141
- content={node.content}
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
- setCustomComponents('chat', { thinking: ThinkingNode })
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
- ## Type exports
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
- `markstream-react` now exposes the core public types directly from the package root, including:
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
- - `NodeRendererProps`
162
- - `NodeComponentProps`
163
- - `RenderContext`
164
- - `RenderNodeFn`
165
- - `CustomComponentMap`
166
- - `CodeBlockMonacoOptions`
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
+ ```