pd-markdown 1.1.0 → 2.0.1

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 (71) hide show
  1. package/README.md +52 -170
  2. package/package.json +12 -11
  3. package/packages/parser/README.md +120 -0
  4. package/packages/utils/README.md +130 -0
  5. package/packages/web/README.md +110 -0
  6. package/packages/parser/dist/index.cjs +0 -191
  7. package/packages/parser/dist/index.cjs.map +0 -1
  8. package/packages/parser/dist/index.d.ts +0 -4
  9. package/packages/parser/dist/index.d.ts.map +0 -1
  10. package/packages/parser/dist/index.mjs +0 -185
  11. package/packages/parser/dist/index.mjs.map +0 -1
  12. package/packages/parser/dist/plugins/index.d.ts +0 -4
  13. package/packages/parser/dist/plugins/index.d.ts.map +0 -1
  14. package/packages/parser/dist/plugins/transform/heading.d.ts +0 -6
  15. package/packages/parser/dist/plugins/transform/heading.d.ts.map +0 -1
  16. package/packages/parser/dist/plugins/transform/list.d.ts +0 -14
  17. package/packages/parser/dist/plugins/transform/list.d.ts.map +0 -1
  18. package/packages/parser/dist/plugins/transform/table.d.ts +0 -27
  19. package/packages/parser/dist/plugins/transform/table.d.ts.map +0 -1
  20. package/packages/parser/dist/processor.d.ts +0 -22
  21. package/packages/parser/dist/processor.d.ts.map +0 -1
  22. package/packages/parser/dist/types/index.d.ts +0 -55
  23. package/packages/parser/dist/types/index.d.ts.map +0 -1
  24. package/packages/utils/dist/ast/query.d.ts +0 -36
  25. package/packages/utils/dist/ast/query.d.ts.map +0 -1
  26. package/packages/utils/dist/ast/traverse.d.ts +0 -22
  27. package/packages/utils/dist/ast/traverse.d.ts.map +0 -1
  28. package/packages/utils/dist/index.cjs +0 -358
  29. package/packages/utils/dist/index.cjs.map +0 -1
  30. package/packages/utils/dist/index.d.ts +0 -7
  31. package/packages/utils/dist/index.d.ts.map +0 -1
  32. package/packages/utils/dist/index.mjs +0 -343
  33. package/packages/utils/dist/index.mjs.map +0 -1
  34. package/packages/utils/dist/string/sanitize.d.ts +0 -22
  35. package/packages/utils/dist/string/sanitize.d.ts.map +0 -1
  36. package/packages/utils/dist/string/slugify.d.ts +0 -16
  37. package/packages/utils/dist/string/slugify.d.ts.map +0 -1
  38. package/packages/utils/dist/types/index.d.ts +0 -49
  39. package/packages/utils/dist/types/index.d.ts.map +0 -1
  40. package/packages/web/dist/components/MarkdownRenderer.d.ts +0 -27
  41. package/packages/web/dist/components/MarkdownRenderer.d.ts.map +0 -1
  42. package/packages/web/dist/components/NodeRenderer.d.ts +0 -10
  43. package/packages/web/dist/components/NodeRenderer.d.ts.map +0 -1
  44. package/packages/web/dist/components/context.d.ts +0 -17
  45. package/packages/web/dist/components/context.d.ts.map +0 -1
  46. package/packages/web/dist/components/defaults/Blockquote.d.ts +0 -8
  47. package/packages/web/dist/components/defaults/Blockquote.d.ts.map +0 -1
  48. package/packages/web/dist/components/defaults/Code.d.ts +0 -13
  49. package/packages/web/dist/components/defaults/Code.d.ts.map +0 -1
  50. package/packages/web/dist/components/defaults/Heading.d.ts +0 -8
  51. package/packages/web/dist/components/defaults/Heading.d.ts.map +0 -1
  52. package/packages/web/dist/components/defaults/Image.d.ts +0 -7
  53. package/packages/web/dist/components/defaults/Image.d.ts.map +0 -1
  54. package/packages/web/dist/components/defaults/Link.d.ts +0 -8
  55. package/packages/web/dist/components/defaults/Link.d.ts.map +0 -1
  56. package/packages/web/dist/components/defaults/List.d.ts +0 -13
  57. package/packages/web/dist/components/defaults/List.d.ts.map +0 -1
  58. package/packages/web/dist/components/defaults/Paragraph.d.ts +0 -8
  59. package/packages/web/dist/components/defaults/Paragraph.d.ts.map +0 -1
  60. package/packages/web/dist/components/defaults/Table.d.ts +0 -19
  61. package/packages/web/dist/components/defaults/Table.d.ts.map +0 -1
  62. package/packages/web/dist/components/defaults/index.d.ts +0 -34
  63. package/packages/web/dist/components/defaults/index.d.ts.map +0 -1
  64. package/packages/web/dist/hooks/useMarkdown.d.ts +0 -11
  65. package/packages/web/dist/hooks/useMarkdown.d.ts.map +0 -1
  66. package/packages/web/dist/index.cjs +0 -306
  67. package/packages/web/dist/index.cjs.map +0 -1
  68. package/packages/web/dist/index.d.ts +0 -6
  69. package/packages/web/dist/index.d.ts.map +0 -1
  70. package/packages/web/dist/index.mjs +0 -287
  71. package/packages/web/dist/index.mjs.map +0 -1
package/README.md CHANGED
@@ -1,14 +1,15 @@
1
1
  # pd-markdown
2
2
 
3
- 一个现代化的 Markdown 解析和渲染工具库,基于 unified/remark 构建,支持 React
3
+ 一个现代化的 Markdown 解析和渲染工具库,基于 unified/remark 构建,专为 React 和现代流式 Web 应用设计。
4
4
 
5
5
  ## 特性
6
6
 
7
7
  - 🚀 **高性能解析** - 基于 unified/remark 的 Markdown 解析器
8
+ - ⚡ **流式渲染** - 专为 AI 场景设计的 `StreamMarkdownRenderer` 和 `useStreamMarkdown`
8
9
  - 📝 **GFM 支持** - 完整支持 GitHub Flavored Markdown
9
10
  - 📋 **Frontmatter** - 支持 YAML frontmatter 解析
10
11
  - ⚛️ **React 组件** - 提供开箱即用的 React 渲染组件
11
- - 🎨 **可定制** - 支持自定义组件和插件
12
+ - 🎨 **可定制** - 支持自定义组件覆盖(Heading, Code, Table 等)
12
13
  - 🔗 **自动锚点** - 标题自动生成 slug 锚点
13
14
  - 📦 **Tree-shakable** - 支持 ESM,优化打包体积
14
15
 
@@ -30,7 +31,7 @@ yarn add pd-markdown
30
31
  | 模块路径 | 描述 |
31
32
  | --- | --- |
32
33
  | `pd-markdown/parser` | Markdown 解析器,将 Markdown 转换为 AST |
33
- | `pd-markdown/web` | React 组件,用于渲染 Markdown |
34
+ | `pd-markdown/web` | React 组件和 Hooks,用于渲染 Markdown 和处理流式内容 |
34
35
  | `pd-markdown/utils` | 工具函数库,提供 AST 操作和字符串处理 |
35
36
 
36
37
  ## 快速开始
@@ -45,19 +46,37 @@ function App() {
45
46
  # Hello World
46
47
 
47
48
  This is a **markdown** document.
48
-
49
- ## Features
50
-
51
- - GFM support
52
- - Frontmatter parsing
53
- - Custom components
54
49
  `
55
50
 
56
51
  return <MarkdownRenderer source={markdown} />
57
52
  }
58
53
  ```
59
54
 
60
- ### 使用预解析的 AST(SSR 优化)
55
+ ### 流式渲染 (AI 场景)
56
+
57
+ ```tsx
58
+ import { StreamMarkdownRenderer, useStreamMarkdown } from 'pd-markdown/web'
59
+
60
+ function AIResponse() {
61
+ const stream = useStreamMarkdown()
62
+
63
+ // 模拟从流中读取数据
64
+ const onNewChunk = (chunk: string) => {
65
+ stream.append(chunk)
66
+ if (isLastChunk) stream.done()
67
+ }
68
+
69
+ return (
70
+ <StreamMarkdownRenderer
71
+ source={stream.source}
72
+ isStreaming={stream.isStreaming}
73
+ showCursor={true}
74
+ />
75
+ )
76
+ }
77
+ ```
78
+
79
+ ### 使用预解析的 AST (SSR 优化)
61
80
 
62
81
  ```tsx
63
82
  import { createParser } from 'pd-markdown/parser'
@@ -73,19 +92,6 @@ function Page({ ast }) {
73
92
  }
74
93
  ```
75
94
 
76
- ### 使用 Hook
77
-
78
- ```tsx
79
- import { useMarkdown, MarkdownRenderer } from 'pd-markdown/web'
80
-
81
- function MarkdownPreview({ source }) {
82
- const ast = useMarkdown(source)
83
-
84
- // 可以对 AST 进行自定义处理
85
- return <MarkdownRenderer ast={ast} />
86
- }
87
- ```
88
-
89
95
  ## API 文档
90
96
 
91
97
  ### pd-markdown/parser
@@ -100,192 +106,68 @@ import { createParser } from 'pd-markdown/parser'
100
106
  const parser = createParser({
101
107
  gfm: true, // 启用 GFM 支持(默认 true)
102
108
  frontmatter: true, // 启用 frontmatter 解析(默认 true)
103
- plugins: [], // 自定义插件
104
109
  })
105
110
 
106
111
  const ast = parser.parse('# Hello World')
107
112
  ```
108
113
 
109
- #### `definePlugin(config)`
114
+ ### pd-markdown/web
110
115
 
111
- 定义自定义解析器插件。
116
+ #### `<MarkdownRenderer />`
112
117
 
113
- ```ts
114
- import { definePlugin } from 'pd-markdown/parser'
115
-
116
- const myPlugin = definePlugin({
117
- name: 'my-plugin',
118
- phase: 'after', // 'before' | 'after'
119
- transform: () => (tree, file) => {
120
- // 处理 AST
121
- console.log(tree)
122
- },
123
- })
124
- ```
118
+ 主渲染组件。支持 `source` 字符串或预解析的 `ast`。
125
119
 
126
- ### pd-markdown/web
120
+ #### `<StreamMarkdownRenderer />`
127
121
 
128
- #### `<MarkdownRenderer />`
122
+ 流式渲染组件,专为实时生成的内容优化。
129
123
 
130
- 主渲染组件。
124
+ - `showCursor`: 是否显示打字机光标
125
+ - `enableAnimation`: 是否启用新块淡入动画
126
+ - `isStreaming`: 标记当前流是否仍在进行
131
127
 
132
- ```tsx
133
- interface MarkdownRendererProps {
134
- /** Markdown 源字符串(将被解析) */
135
- source?: string
136
- /** 预解析的 AST(跳过解析,适用于 SSR) */
137
- ast?: Root
138
- /** 自定义组件覆盖 */
139
- components?: Partial<ComponentMap>
140
- /** 包装器 CSS 类名 */
141
- className?: string
142
- /** 包装器内联样式 */
143
- style?: CSSProperties
144
- /** 解析器选项(仅在使用 source 时生效) */
145
- parserOptions?: ParserOptions
146
- }
128
+ #### `useStreamMarkdown(options?)`
129
+
130
+ 管理流式 Markdown 状态的 Hook。
131
+
132
+ ```ts
133
+ const { source, ast, append, done, reset } = useStreamMarkdown({
134
+ parseDebounceMs: 30, // 解析防抖,优化超快流速下的性能
135
+ })
147
136
  ```
148
137
 
149
- #### 自定义组件
138
+ #### 自定义组件覆盖
150
139
 
151
140
  ```tsx
152
141
  import { MarkdownRenderer, type ComponentMap } from 'pd-markdown/web'
153
142
 
154
143
  const customComponents: Partial<ComponentMap> = {
155
144
  heading: ({ node, children }) => (
156
- <h2 className="custom-heading">{children}</h2>
157
- ),
158
- code: ({ node, children }) => (
159
- <pre className="custom-code-block">
160
- <code>{children}</code>
161
- </pre>
145
+ <h2 style={{ color: 'blue' }}>{children}</h2>
162
146
  ),
163
147
  }
164
148
 
165
149
  function App() {
166
- return (
167
- <MarkdownRenderer
168
- source={markdown}
169
- components={customComponents}
170
- />
171
- )
150
+ return <MarkdownRenderer source={md} components={customComponents} />
172
151
  }
173
152
  ```
174
153
 
175
- #### 可自定义的组件列表
176
-
177
- | 组件名 | 对应节点 |
178
- | --- | --- |
179
- | `heading` | 标题 (h1-h6) |
180
- | `paragraph` | 段落 |
181
- | `list` | 列表 |
182
- | `listItem` | 列表项 |
183
- | `table` | 表格 |
184
- | `tableRow` | 表格行 |
185
- | `tableCell` | 表格单元格 |
186
- | `code` | 代码块 |
187
- | `inlineCode` | 行内代码 |
188
- | `link` | 链接 |
189
- | `image` | 图片 |
190
- | `blockquote` | 引用块 |
191
-
192
154
  ### pd-markdown/utils
193
155
 
194
- 工具函数库,提供 AST 操作和字符串处理功能。
156
+ 提供强大的 AST 遍历与处理工具。
195
157
 
196
158
  ```ts
197
- import {
198
- // AST 遍历
199
- traverseAst,
200
- findNodes,
201
- findNode,
202
- // 类型守卫
203
- isParent,
204
- isLiteral,
205
- isNodeType,
206
- // 字符串处理
207
- slugify,
208
- uniqueSlugify,
209
- escapeHtml,
210
- sanitizeHtml,
211
- } from 'pd-markdown/utils'
212
-
213
- // 查找所有标题节点
214
- const headings = findNodes(ast, 'heading')
215
-
216
- // 生成 slug
217
- const slug = slugify('Hello World') // 'hello-world'
218
- ```
219
-
220
- ## 示例
221
-
222
- ### 博客文章渲染
223
-
224
- ```tsx
225
- import { createParser } from 'pd-markdown/parser'
226
- import { MarkdownRenderer } from 'pd-markdown/web'
227
-
228
- const parser = createParser()
229
-
230
- function BlogPost({ content }) {
231
- const ast = parser.parse(content)
232
-
233
- return (
234
- <article className="prose">
235
- <MarkdownRenderer
236
- ast={ast}
237
- className="markdown-body"
238
- />
239
- </article>
240
- )
241
- }
242
- ```
243
-
244
- ### 代码高亮
245
-
246
- ```tsx
247
- import { MarkdownRenderer, type CodeProps } from 'pd-markdown/web'
248
- import Prism from 'prismjs'
249
-
250
- function CodeBlock({ node, children }: CodeProps) {
251
- const lang = node.lang || 'text'
252
- const highlighted = Prism.highlight(
253
- String(children),
254
- Prism.languages[lang] || Prism.languages.text,
255
- lang
256
- )
257
-
258
- return (
259
- <pre className={`language-${lang}`}>
260
- <code dangerouslySetInnerHTML={{ __html: highlighted }} />
261
- </pre>
262
- )
263
- }
159
+ import { traverseAst, findNodes, slugify } from 'pd-markdown/utils'
264
160
 
265
- function App() {
266
- return (
267
- <MarkdownRenderer
268
- source={markdown}
269
- components={{ code: CodeBlock }}
270
- />
271
- )
272
- }
161
+ // 提取所有标题
162
+ const headings = findNodes(ast, 'heading')
273
163
  ```
274
164
 
275
165
  ## 开发
276
166
 
277
167
  ```bash
278
- # 安装依赖
279
168
  pnpm install
280
-
281
- # 构建
282
169
  pnpm build
283
-
284
- # 运行测试
285
170
  pnpm test
286
-
287
- # 类型检查
288
- pnpm typecheck
289
171
  ```
290
172
 
291
173
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pd-markdown",
3
- "version": "1.1.0",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
5
  "description": "A modular markdown parsing and rendering library",
6
6
  "exports": {
@@ -36,6 +36,15 @@
36
36
  "files": [
37
37
  "packages/*/dist"
38
38
  ],
39
+ "scripts": {
40
+ "build": "pnpm -r run build",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "test:coverage": "vitest run --coverage",
44
+ "typecheck": "tsc --noEmit",
45
+ "clean": "pnpm -r run clean",
46
+ "prepublishOnly": "pnpm run build"
47
+ },
39
48
  "peerDependencies": {
40
49
  "react": "^18.0.0 || ^19.0.0",
41
50
  "react-dom": "^18.0.0 || ^19.0.0"
@@ -82,13 +91,5 @@
82
91
  "type": "git",
83
92
  "url": "https://github.com/LaoChen1994/pd-markdown"
84
93
  },
85
- "sideEffects": false,
86
- "scripts": {
87
- "build": "pnpm -r run build",
88
- "test": "vitest run",
89
- "test:watch": "vitest",
90
- "test:coverage": "vitest run --coverage",
91
- "typecheck": "tsc --noEmit",
92
- "clean": "pnpm -r run clean"
93
- }
94
- }
94
+ "sideEffects": false
95
+ }
@@ -0,0 +1,120 @@
1
+ # pd-markdown/parser
2
+
3
+ 基于 unified/remark 的 Markdown 解析器,将 Markdown 转换为 AST。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install pd-markdown
9
+ ```
10
+
11
+ ## 使用
12
+
13
+ ### 基础解析
14
+
15
+ ```ts
16
+ import { createParser } from 'pd-markdown/parser'
17
+
18
+ const parser = createParser()
19
+ const ast = parser.parse('# Hello World')
20
+
21
+ console.log(ast)
22
+ // {
23
+ // type: 'root',
24
+ // children: [
25
+ // { type: 'heading', depth: 1, children: [...] }
26
+ // ]
27
+ // }
28
+ ```
29
+
30
+ ### 解析选项
31
+
32
+ ```ts
33
+ const parser = createParser({
34
+ gfm: true, // 启用 GFM 支持(默认 true)
35
+ frontmatter: true, // 启用 frontmatter 解析(默认 true)
36
+ })
37
+ ```
38
+
39
+ ### Frontmatter 支持
40
+
41
+ 解析 YAML frontmatter:
42
+
43
+ ```ts
44
+ const markdown = `---
45
+ title: My Article
46
+ date: 2024-01-01
47
+ ---
48
+
49
+ # Hello World
50
+ `
51
+
52
+ const parser = createParser({ frontmatter: true })
53
+ const ast = parser.parse(markdown)
54
+ ```
55
+
56
+ ### 自定义插件
57
+
58
+ ```ts
59
+ import { createParser, definePlugin } from 'pd-markdown/parser'
60
+
61
+ const myPlugin = definePlugin({
62
+ name: 'my-plugin',
63
+ phase: 'after',
64
+ transform: () => (tree, file) => {
65
+ // 处理 AST
66
+ console.log('Processing tree:', tree.type)
67
+ },
68
+ })
69
+
70
+ const parser = createParser({
71
+ plugins: [myPlugin],
72
+ })
73
+ ```
74
+
75
+ ## API
76
+
77
+ ### `createParser(options?)`
78
+
79
+ 创建解析器实例。
80
+
81
+ **参数:**
82
+ - `options.gfm` - 启用 GFM 支持(默认 `true`)
83
+ - `options.frontmatter` - 启用 frontmatter 解析(默认 `true`)
84
+ - `options.plugins` - 自定义插件数组
85
+
86
+ **返回:**
87
+ - `Parser` 实例,包含 `parse(content)` 方法
88
+
89
+ ### `definePlugin(config)`
90
+
91
+ 定义自定义插件。
92
+
93
+ **参数:**
94
+ - `config.name` - 插件名称
95
+ - `config.phase` - 执行阶段:`'before'` 或 `'after'`
96
+ - `config.transform` - 转换函数
97
+
98
+ ## 导出
99
+
100
+ ```ts
101
+ // 核心函数
102
+ export { createParser, definePlugin }
103
+
104
+ // 类型
105
+ export type {
106
+ Parser,
107
+ ParserOptions,
108
+ ParserPlugin,
109
+ PluginConfig,
110
+ FrontmatterData,
111
+ FileData,
112
+ }
113
+
114
+ // 内置转换插件(高级用法)
115
+ export { transformHeading, transformList, transformTable }
116
+ ```
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1,130 @@
1
+ # pd-markdown/utils
2
+
3
+ Markdown 处理工具函数库。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install pd-markdown
9
+ ```
10
+
11
+ ## API
12
+
13
+ ### AST 遍历
14
+
15
+ ```ts
16
+ import { traverseAst, traverseAstWithCallbacks } from 'pd-markdown/utils'
17
+
18
+ // 遍历所有节点
19
+ traverseAst(tree, (node, index, parent) => {
20
+ console.log(node.type)
21
+ })
22
+
23
+ // 带回调的遍历
24
+ traverseAstWithCallbacks(tree, {
25
+ heading: (node) => console.log('Found heading:', node.depth),
26
+ paragraph: (node) => console.log('Found paragraph'),
27
+ })
28
+ ```
29
+
30
+ ### AST 查询
31
+
32
+ ```ts
33
+ import { findNodes, findNode, findNodesBy, findParent } from 'pd-markdown/utils'
34
+
35
+ // 查找所有指定类型的节点
36
+ const headings = findNodes(tree, 'heading')
37
+
38
+ // 查找第一个匹配的节点
39
+ const firstHeading = findNode(tree, 'heading')
40
+
41
+ // 按条件查找
42
+ const h1Headings = findNodesBy(tree,
43
+ (node) => node.type === 'heading' && node.depth === 1
44
+ )
45
+
46
+ // 查找父节点
47
+ const parent = findParent(tree, someNode)
48
+ ```
49
+
50
+ ### 类型守卫
51
+
52
+ ```ts
53
+ import { isParent, isLiteral, isNodeType } from 'pd-markdown/utils'
54
+
55
+ if (isParent(node)) {
56
+ console.log('Children:', node.children)
57
+ }
58
+
59
+ if (isLiteral(node)) {
60
+ console.log('Value:', node.value)
61
+ }
62
+
63
+ if (isNodeType(node, 'heading')) {
64
+ console.log('Heading depth:', node.depth)
65
+ }
66
+ ```
67
+
68
+ ### Slug 生成
69
+
70
+ ```ts
71
+ import { slugify, uniqueSlugify } from 'pd-markdown/utils'
72
+
73
+ // 基础 slug 生成
74
+ slugify('Hello World') // 'hello-world'
75
+ slugify('API 参考') // 'api-参考'
76
+ slugify('What is React?') // 'what-is-react'
77
+
78
+ // 唯一 slug 生成(避免重复)
79
+ const slugs = new Set<string>()
80
+ slug = uniqueSlugify('Hello', slugs) // 'hello'
81
+ slug = uniqueSlugify('Hello', slugs) // 'hello-1'
82
+ slug = uniqueSlugify('Hello', slugs) // 'hello-2'
83
+ ```
84
+
85
+ ### HTML 处理
86
+
87
+ ```ts
88
+ import { escapeHtml, sanitizeHtml, stripHtml } from 'pd-markdown/utils'
89
+
90
+ // 转义 HTML 特殊字符
91
+ escapeHtml('<script>alert("xss")</script>')
92
+ // '&lt;script&gt;alert("xss")&lt;/script&gt;'
93
+
94
+ // 清理 HTML 标签
95
+ sanitizeHtml('<p>Hello</p>') // 'Hello'
96
+
97
+ // 移除 HTML 标签
98
+ stripHtml('<p>Hello <b>World</b></p>') // 'Hello World'
99
+ ```
100
+
101
+ ## 导出
102
+
103
+ ```ts
104
+ // 类型
105
+ export type {
106
+ MdNode,
107
+ MdRoot,
108
+ Parent,
109
+ Literal,
110
+ Visitor,
111
+ PluginOptions,
112
+ Position,
113
+ Location,
114
+ }
115
+
116
+ // AST 操作
117
+ export { traverseAst, traverseAstWithCallbacks }
118
+ export { findNodes, findNode, findNodesBy, findParent }
119
+
120
+ // 类型守卫
121
+ export { isParent, isLiteral, isNodeType }
122
+
123
+ // 字符串处理
124
+ export { slugify, uniqueSlugify }
125
+ export { escapeHtml, sanitizeHtml, stripHtml }
126
+ ```
127
+
128
+ ## License
129
+
130
+ MIT
@@ -0,0 +1,110 @@
1
+ # pd-markdown/web
2
+
3
+ React 组件库,用于渲染 Markdown,支持静态渲染和流式输出。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install pd-markdown
9
+ ```
10
+
11
+ **Peer Dependencies:**
12
+ - react >= 18.0.0
13
+ - react-dom >= 18.0.0
14
+
15
+ ## 使用
16
+
17
+ ### 基础渲染
18
+
19
+ ```tsx
20
+ import { MarkdownRenderer } from 'pd-markdown/web'
21
+
22
+ function App() {
23
+ const markdown = '# Hello World'
24
+ return <MarkdownRenderer source={markdown} />
25
+ }
26
+ ```
27
+
28
+ ### 流式渲染 (专为 AI 场景优化)
29
+
30
+ 支持实时显示正在生成的 Markdown 内容,带有光标指示器和区块加载动画。
31
+
32
+ ```tsx
33
+ import { StreamMarkdownRenderer, useStreamMarkdown } from 'pd-markdown/web'
34
+
35
+ function AIConversation() {
36
+ const stream = useStreamMarkdown()
37
+
38
+ // 当收到新的内容块时
39
+ const onNewData = (chunk: string) => {
40
+ stream.append(chunk)
41
+ }
42
+
43
+ // 结束流
44
+ const onDataDone = () => {
45
+ stream.done()
46
+ }
47
+
48
+ return (
49
+ <StreamMarkdownRenderer
50
+ source={stream.source}
51
+ ast={stream.ast} // 使用 hook 提供的预解析 AST 性能更佳
52
+ isStreaming={stream.isStreaming}
53
+ showCursor={true}
54
+ />
55
+ )
56
+ }
57
+ ```
58
+
59
+ ### 自定义组件覆盖
60
+
61
+ ```tsx
62
+ import { MarkdownRenderer, type ComponentMap } from 'pd-markdown/web'
63
+
64
+ const components: Partial<ComponentMap> = {
65
+ // 覆盖标题渲染
66
+ heading: ({ node, children }) => (
67
+ <h2 className={`heading-level-${node.depth}`}>{children}</h2>
68
+ ),
69
+ // 覆盖代码块渲染
70
+ code: ({ node }) => (
71
+ <pre><code>{node.value}</code></pre>
72
+ )
73
+ }
74
+
75
+ function App() {
76
+ return <MarkdownRenderer source={md} components={components} />
77
+ }
78
+ ```
79
+
80
+ ## API 参考
81
+
82
+ ### 组件
83
+
84
+ #### `<MarkdownRenderer />`
85
+ 用于普通 Markdown 字符串或 AST 的渲染。
86
+
87
+ #### `<StreamMarkdownRenderer />`
88
+ 继承自 `MarkdownRenderer`,增加了对流式状态的支持。
89
+ - `showCursor`: `boolean` - 是否显示闪烁的光标。
90
+ - `enableAnimation`: `boolean` - 是否开启新内容飞入/淡入动画。
91
+ - `isStreaming`: `boolean` - 标记流是否活跃。
92
+
93
+ ### Hooks
94
+
95
+ #### `useStreamMarkdown(options?)`
96
+ 管理流式内容的状态管理 Hook。返回:
97
+ - `source`: 当前累计的所有文本。
98
+ - `ast`: 当前文本对应的 Root 节点。
99
+ - `isStreaming`: 是否正在流式传输中。
100
+ - `isDone`: 是否已完成。
101
+ - `append(chunk)`: 追加新的内容块。
102
+ - `done()`: 标记流已终结。
103
+ - `reset()`: 重置所有状态。
104
+
105
+ #### `useMarkdown(source, options?)`
106
+ 用于手动触发解析并获取 AST 的 Hook。
107
+
108
+ ## License
109
+
110
+ MIT