ds-markdown 0.0.10-beta.4 → 0.0.11-beta.0
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.en.md +491 -0
- package/README.ja.md +491 -0
- package/README.ko.md +491 -0
- package/README.md +346 -239
- package/dist/cjs/MarkdownCMD/index.js +1 -1
- package/dist/cjs/MarkdownCMD/index.js.map +1 -1
- package/dist/esm/MarkdownCMD/index.js +1 -1
- package/dist/esm/MarkdownCMD/index.js.map +1 -1
- package/package.json +10 -11
package/README.md
CHANGED
|
@@ -1,36 +1,105 @@
|
|
|
1
1
|
# ds-markdown
|
|
2
2
|
|
|
3
|
-
> 🚀
|
|
3
|
+
> 🚀 高性能 React Markdown 打字动画组件,完美复刻 DeepSeek 聊天界面效果
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**🇨🇳 中文 | [🇺🇸 English](./README.en.md) | [🇯🇵 日本語](./README.ja.md) | [🇰🇷 한국어](./README.ko.md)**
|
|
6
|
+
|
|
7
|
+
一个专为现代 AI 应用设计的 React 组件,提供流畅的实时打字动画和完整的 Markdown 渲染能力。
|
|
6
8
|
|
|
7
9
|
[](https://www.npmjs.com/package/ds-markdown)
|
|
8
10
|
[](https://www.npmjs.com/package/ds-markdown)
|
|
9
11
|
[](https://bundlephobia.com/package/ds-markdown)
|
|
10
12
|
[](https://react.dev)
|
|
13
|
+
[](https://www.typescriptlang.org/)
|
|
11
14
|
|
|
12
15
|
[📖 在线演示](https://onshinpei.github.io/ds-markdown/) | [🔧 StackBlitz 体验](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## ✨ 核心特性
|
|
20
|
+
|
|
21
|
+
### 🎯 **完美还原**
|
|
22
|
+
|
|
23
|
+
- 1:1 复刻 [DeepSeek 官网](https://chat.deepseek.com/) 聊天响应效果
|
|
24
|
+
- 支持思考过程 (`thinking`) 和回答内容 (`answer`) 双模式
|
|
25
|
+
- 原生 Markdown 语法支持,包括代码高亮、表格、列表等
|
|
26
|
+
|
|
27
|
+
### ⚡ **极致性能**
|
|
28
|
+
|
|
29
|
+
- 智能分批处理,大文档渲染零卡顿
|
|
30
|
+
- 双模式定时器:`requestAnimationFrame` + `setTimeout`
|
|
31
|
+
- 内置流式语法缓冲,避免不完整 Markdown 渲染错误
|
|
15
32
|
|
|
16
|
-
|
|
17
|
-
- ⚡ **高性能** - 智能分批处理,大文档渲染无卡顿
|
|
18
|
-
- 🎬 **流畅动画** - 双模式定时器,支持高频打字效果
|
|
19
|
-
- 🎨 **完整渲染** - 内置常用 Markdown 格式支持
|
|
20
|
-
- 🔧 **灵活配置** - 声明式 + 命令式双重 API 设计
|
|
21
|
-
- 📱 **现代兼容** - 支持 React 16.8+ 和现代浏览器
|
|
33
|
+
### 🎬 **流畅动画**
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
- 高频打字支持(`requestAnimationFrame`模式下打字间隔最低可接近于`0ms`)
|
|
36
|
+
- 帧同步渲染,与浏览器 60fps 完美配合
|
|
37
|
+
- 智能字符批量处理,视觉效果更自然
|
|
38
|
+
|
|
39
|
+
### 🔧 **灵活易用**
|
|
40
|
+
|
|
41
|
+
- **声明式 API**:适合简单场景,React 风格
|
|
42
|
+
- **命令式 API**:适合流式数据,性能更优
|
|
43
|
+
- **TypeScript 原生支持**:完整类型提示
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 📦 快速安装
|
|
24
48
|
|
|
25
49
|
```bash
|
|
50
|
+
# npm
|
|
26
51
|
npm install ds-markdown
|
|
27
|
-
|
|
52
|
+
|
|
53
|
+
# yarn
|
|
28
54
|
yarn add ds-markdown
|
|
29
|
-
|
|
55
|
+
|
|
56
|
+
# pnpm
|
|
30
57
|
pnpm add ds-markdown
|
|
31
58
|
```
|
|
32
59
|
|
|
33
|
-
|
|
60
|
+
### 通过 ESM CDN 使用
|
|
61
|
+
|
|
62
|
+
无需安装,直接在浏览器中使用:
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<!-- 引入样式 -->
|
|
66
|
+
<link rel="stylesheet" href="https://esm.sh/ds-markdown/style.css" />
|
|
67
|
+
|
|
68
|
+
<!-- 引入组件 -->
|
|
69
|
+
<script type="importmap">
|
|
70
|
+
{
|
|
71
|
+
"imports": {
|
|
72
|
+
"react": "https://esm.sh/react@19.1.0",
|
|
73
|
+
"react-dom/client": "https://esm.sh/react-dom@19.1.0/client",
|
|
74
|
+
"ds-markdown": "https://esm.sh/ds-markdown@0.0.10"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
</script>
|
|
78
|
+
<script type="module" src="https://esm.sh/tsx"></script>
|
|
79
|
+
|
|
80
|
+
<script type="text/babel">
|
|
81
|
+
import { createRoot } from 'react-dom/client';
|
|
82
|
+
import DsMarkdown from 'ds-markdown';
|
|
83
|
+
|
|
84
|
+
const markdown = `
|
|
85
|
+
# Hello ds-markdown
|
|
86
|
+
|
|
87
|
+
这是一个**高性能**的打字动画组件!
|
|
88
|
+
|
|
89
|
+
## 特性
|
|
90
|
+
- ⚡ 零延迟流式处理
|
|
91
|
+
- 🎬 流畅打字动画
|
|
92
|
+
- 🎯 完美语法支持
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const root = createRoot(document.getElementById('root'));
|
|
96
|
+
root.render(<DsMarkdown interval={20}>{markdown}</DsMarkdown>);
|
|
97
|
+
</script>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## 🚀 5分钟上手
|
|
101
|
+
|
|
102
|
+
### 基础用法
|
|
34
103
|
|
|
35
104
|
```tsx
|
|
36
105
|
import DsMarkdown from 'ds-markdown';
|
|
@@ -38,59 +107,128 @@ import 'ds-markdown/style.css';
|
|
|
38
107
|
|
|
39
108
|
function App() {
|
|
40
109
|
return (
|
|
41
|
-
<DsMarkdown
|
|
42
|
-
# Hello
|
|
110
|
+
<DsMarkdown interval={20} answerType="answer">
|
|
111
|
+
# Hello ds-markdown 这是一个**高性能**的打字动画组件! ## 特性 - ⚡ 零延迟流式处理 - 🎬 流畅打字动画 - 🎯 完美语法支持
|
|
43
112
|
</DsMarkdown>
|
|
44
113
|
);
|
|
45
114
|
}
|
|
46
115
|
```
|
|
47
116
|
|
|
48
|
-
|
|
117
|
+
### AI 对话场景
|
|
49
118
|
|
|
50
|
-
|
|
119
|
+
```tsx
|
|
120
|
+
function ChatDemo() {
|
|
121
|
+
const [thinking, setThinking] = useState('');
|
|
122
|
+
const [answer, setAnswer] = useState('');
|
|
123
|
+
|
|
124
|
+
const handleAsk = () => {
|
|
125
|
+
setThinking('🤔 正在思考您的问题...');
|
|
126
|
+
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
setAnswer(`# 关于 React 19
|
|
129
|
+
|
|
130
|
+
React 19 带来了许多激动人心的新特性:
|
|
131
|
+
|
|
132
|
+
## 🚀 主要更新
|
|
133
|
+
1. **React Compiler** - 自动优化性能
|
|
134
|
+
2. **Actions** - 简化表单处理
|
|
135
|
+
3. **Document Metadata** - 内置 SEO 支持
|
|
136
|
+
|
|
137
|
+
让我们一起探索这些新功能!`);
|
|
138
|
+
}, 2000);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div>
|
|
143
|
+
<button onClick={handleAsk}>询问 AI</button>
|
|
144
|
+
|
|
145
|
+
{thinking && (
|
|
146
|
+
<DsMarkdown answerType="thinking" interval={30}>
|
|
147
|
+
{thinking}
|
|
148
|
+
</DsMarkdown>
|
|
149
|
+
)}
|
|
51
150
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
151
|
+
{answer && (
|
|
152
|
+
<DsMarkdown answerType="answer" interval={15}>
|
|
153
|
+
{answer}
|
|
154
|
+
</DsMarkdown>
|
|
155
|
+
)}
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
60
160
|
|
|
61
|
-
|
|
161
|
+
---
|
|
62
162
|
|
|
63
|
-
|
|
163
|
+
## 📚 完整 API 文档
|
|
164
|
+
|
|
165
|
+
### 声明式 API (推荐新手)
|
|
166
|
+
|
|
167
|
+
| 属性 | 类型 | 说明 | 默认值 |
|
|
168
|
+
| ------------- | ------------------------------------------- | ----------------------- | ----------------------------------------------------------- |
|
|
169
|
+
| `interval` | `number` | 打字间隔 (毫秒) | `30` |
|
|
170
|
+
| `timerType` | `'setTimeout'` \| `'requestAnimationFrame'` | 定时器类型 | 当前默认值是`setTimeout`,后期会改为`requestAnimationFrame` |
|
|
171
|
+
| `answerType` | `'thinking'` \| `'answer'` | 内容类型 (影响样式主题) | `'answer'` |
|
|
172
|
+
| `onEnd` | `(data: EndData) => void` | 打字结束回调 | - |
|
|
173
|
+
| `onStart` | `(data: StartData) => void` | 打字开始回调 | - |
|
|
174
|
+
| `onTypedChar` | `(data: CharData) => void` | 每字符打字回调 | - |
|
|
175
|
+
|
|
176
|
+
### 命令式 API (推荐流式场景)
|
|
64
177
|
|
|
65
178
|
```typescript
|
|
66
|
-
|
|
67
|
-
- 🎯 时间驱动:基于真实经过时间计算字符数量
|
|
68
|
-
- 📊 批量处理:单帧内可处理多个字符
|
|
69
|
-
- 🎬 帧同步:与浏览器 60fps 刷新率同步
|
|
70
|
-
- ⚡ 高频优化:完美支持 interval < 16ms 的高速打字
|
|
179
|
+
import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
|
|
71
180
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
181
|
+
interface MarkdownRef {
|
|
182
|
+
push: (content: string, answerType: AnswerType) => void;
|
|
183
|
+
clear: () => void;
|
|
184
|
+
triggerWholeEnd: () => void;
|
|
185
|
+
flushBuffer: (answerType?: AnswerType) => void;
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
| 方法 | 参数 | 说明 |
|
|
190
|
+
| ----------------- | ------------------------------------------- | ------------------ |
|
|
191
|
+
| `push` | `(content: string, answerType: AnswerType)` | 添加内容并开始打字 |
|
|
192
|
+
| `clear` | - | 清空所有内容和状态 |
|
|
193
|
+
| `triggerWholeEnd` | - | 手动触发完成回调 |
|
|
194
|
+
| `flushBuffer` | `answerType?: AnswerType` | 强制刷新缓冲区内容 |
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 🎛️ 定时器模式详解
|
|
199
|
+
|
|
200
|
+
### `requestAnimationFrame` 模式 🌟 (推荐)
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// 🎯 特性
|
|
204
|
+
- 时间驱动:基于真实经过时间计算字符数量
|
|
205
|
+
- 批量处理:单帧内可处理多个字符
|
|
206
|
+
- 帧同步:与浏览器 60fps 刷新率同步
|
|
207
|
+
- 高频优化:完美支持 interval < 16ms 的高速打字
|
|
208
|
+
|
|
209
|
+
// 🎯 适用场景
|
|
75
210
|
- 现代 Web 应用的默认选择
|
|
211
|
+
- 追求流畅动画效果
|
|
212
|
+
- 高频打字 (interval > 0 即可)
|
|
213
|
+
- AI 实时对话场景
|
|
76
214
|
```
|
|
77
215
|
|
|
78
|
-
|
|
216
|
+
### `setTimeout` 模式 📟 (兼容)
|
|
79
217
|
|
|
80
218
|
```typescript
|
|
81
|
-
// 特性
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
219
|
+
// 🎯 特性
|
|
220
|
+
- 单字符:每次精确处理一个字符
|
|
221
|
+
- 固定间隔:严格按设定时间执行
|
|
222
|
+
- 节拍感:经典打字机的节奏感
|
|
223
|
+
- 精确控制:适合特定时序要求
|
|
86
224
|
|
|
87
|
-
// 适用场景
|
|
225
|
+
// 🎯 适用场景
|
|
88
226
|
- 需要精确时间控制
|
|
89
227
|
- 营造复古打字机效果
|
|
90
228
|
- 兼容性要求较高的场景
|
|
91
229
|
```
|
|
92
230
|
|
|
93
|
-
|
|
231
|
+
### 📊 性能对比
|
|
94
232
|
|
|
95
233
|
| 特性 | requestAnimationFrame | setTimeout |
|
|
96
234
|
| ------------ | ---------------------------- | ---------------- |
|
|
@@ -100,280 +238,243 @@ function App() {
|
|
|
100
238
|
| **视觉效果** | 🎬 流畅动画感 | ⚡ 精确节拍感 |
|
|
101
239
|
| **性能开销** | 🟢 低 (帧同步) | 🟡 中等 (定时器) |
|
|
102
240
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
### 📝 声明式用法 (推荐)
|
|
106
|
-
|
|
107
|
-
```tsx
|
|
108
|
-
import { useState } from 'react';
|
|
109
|
-
import DsMarkdown from 'ds-markdown';
|
|
110
|
-
import 'ds-markdown/style.css';
|
|
111
|
-
|
|
112
|
-
const markdown = `# ds-markdown
|
|
113
|
-
|
|
114
|
-
\`ds-markdown\` 是一个高性能的 React Markdown 打字动画组件
|
|
115
|
-
|
|
116
|
-
## ✨ 特性
|
|
117
|
-
|
|
118
|
-
- 🎯 **1:1 还原** - 完美复刻 DeepSeek 聊天界面效果
|
|
119
|
-
- ⚡ **高性能** - 智能分批处理,大文档渲染无卡顿
|
|
120
|
-
- 🎬 **流畅动画** - 双模式定时器,支持高频打字效果
|
|
121
|
-
`;
|
|
122
|
-
|
|
123
|
-
function App() {
|
|
124
|
-
const [thinkingContent, setThinkingContent] = useState('');
|
|
125
|
-
const [answerContent, setAnswerContent] = useState('');
|
|
126
|
-
|
|
127
|
-
const handleStart = () => {
|
|
128
|
-
// 清空之前的内容
|
|
129
|
-
setThinkingContent('🤔 正在分析您的问题...\n\n分析完成,准备回答');
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
return (
|
|
133
|
-
<div>
|
|
134
|
-
<button onClick={handleStart}>开始演示</button>
|
|
135
|
-
|
|
136
|
-
{/* 思考过程 */}
|
|
137
|
-
<DsMarkdown
|
|
138
|
-
answerType="thinking"
|
|
139
|
-
interval={15}
|
|
140
|
-
timerType="requestAnimationFrame"
|
|
141
|
-
onEnd={() => {
|
|
142
|
-
console.log('思考完成');
|
|
143
|
-
setAnswerContent(markdown);
|
|
144
|
-
}}
|
|
145
|
-
>
|
|
146
|
-
{thinkingContent}
|
|
147
|
-
</DsMarkdown>
|
|
148
|
-
|
|
149
|
-
{/* 回答内容 */}
|
|
150
|
-
{answerContent && (
|
|
151
|
-
<DsMarkdown answerType="answer" interval={10} timerType="requestAnimationFrame">
|
|
152
|
-
{answerContent}
|
|
153
|
-
</DsMarkdown>
|
|
154
|
-
)}
|
|
155
|
-
</div>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export default App;
|
|
160
|
-
```
|
|
241
|
+
高频推荐`requestAnimationFrame`,低频推荐 `setTimeout`
|
|
161
242
|
|
|
162
|
-
|
|
243
|
+
---
|
|
163
244
|
|
|
164
|
-
|
|
245
|
+
## 💡 实战示例
|
|
165
246
|
|
|
166
|
-
|
|
247
|
+
### 📝 AI 流式对话
|
|
167
248
|
|
|
168
|
-
|
|
249
|
+
````tsx
|
|
169
250
|
import { useRef } from 'react';
|
|
170
|
-
import { MarkdownCMD } from 'ds-markdown';
|
|
171
|
-
import 'ds-markdown/style.css';
|
|
251
|
+
import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
|
|
172
252
|
|
|
173
253
|
function StreamingChat() {
|
|
174
|
-
const markdownRef = useRef<
|
|
254
|
+
const markdownRef = useRef<MarkdownRef>(null);
|
|
175
255
|
|
|
176
|
-
//
|
|
177
|
-
const
|
|
256
|
+
// 模拟 AI 流式响应
|
|
257
|
+
const simulateAIResponse = async () => {
|
|
178
258
|
markdownRef.current?.clear();
|
|
179
259
|
|
|
180
|
-
//
|
|
181
|
-
markdownRef.current?.push('🤔
|
|
260
|
+
// 思考阶段
|
|
261
|
+
markdownRef.current?.push('🤔 正在分析您的问题...', 'thinking');
|
|
262
|
+
await delay(1000);
|
|
263
|
+
markdownRef.current?.push('\n\n✅ 分析完成,开始回答', 'thinking');
|
|
182
264
|
|
|
183
|
-
//
|
|
265
|
+
// 流式回答
|
|
184
266
|
const chunks = [
|
|
185
|
-
'#
|
|
186
|
-
'
|
|
187
|
-
'
|
|
188
|
-
'-
|
|
189
|
-
'-
|
|
190
|
-
'-
|
|
191
|
-
'
|
|
267
|
+
'# React 19 新特性解析\n\n',
|
|
268
|
+
'## 🚀 React Compiler\n',
|
|
269
|
+
'React 19 最大的亮点是引入了 **React Compiler**:\n\n',
|
|
270
|
+
'- 🎯 **自动优化**:无需手动 memo 和 useMemo\n',
|
|
271
|
+
'- ⚡ **性能提升**:编译时优化,运行时零开销\n',
|
|
272
|
+
'- 🔧 **向后兼容**:现有代码无需修改\n\n',
|
|
273
|
+
'## 📝 Actions 简化表单\n',
|
|
274
|
+
'新的 Actions API 让表单处理变得更简单:\n\n',
|
|
275
|
+
'```tsx\n',
|
|
276
|
+
'function ContactForm({ action }) {\n',
|
|
277
|
+
' const [state, formAction] = useActionState(action, null);\n',
|
|
278
|
+
' return (\n',
|
|
279
|
+
' <form action={formAction}>\n',
|
|
280
|
+
' <input name="email" type="email" />\n',
|
|
281
|
+
' <button>提交</button>\n',
|
|
282
|
+
' </form>\n',
|
|
283
|
+
' );\n',
|
|
284
|
+
'}\n',
|
|
285
|
+
'```\n\n',
|
|
286
|
+
'希望这个解答对您有帮助!🎉',
|
|
192
287
|
];
|
|
193
288
|
|
|
194
289
|
for (const chunk of chunks) {
|
|
195
|
-
await
|
|
290
|
+
await delay(100);
|
|
196
291
|
markdownRef.current?.push(chunk, 'answer');
|
|
197
292
|
}
|
|
198
293
|
};
|
|
199
294
|
|
|
200
295
|
return (
|
|
201
|
-
<div>
|
|
202
|
-
<button onClick={
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
interval={10}
|
|
206
|
-
timerType="requestAnimationFrame"
|
|
207
|
-
onEnd={(data) => {
|
|
208
|
-
console.log('段落完成:', data);
|
|
209
|
-
}}
|
|
210
|
-
/>
|
|
296
|
+
<div className="chat-container">
|
|
297
|
+
<button onClick={simulateAIResponse}>🤖 询问 React 19 新特性</button>
|
|
298
|
+
|
|
299
|
+
<MarkdownCMD ref={markdownRef} interval={10} timerType="requestAnimationFrame" onEnd={(data) => console.log('段落完成:', data)} />
|
|
211
300
|
</div>
|
|
212
301
|
);
|
|
213
302
|
}
|
|
214
|
-
```
|
|
215
303
|
|
|
216
|
-
|
|
304
|
+
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
305
|
+
````
|
|
217
306
|
|
|
218
|
-
###
|
|
307
|
+
### 🔄 流式 Markdown 语法处理
|
|
219
308
|
|
|
220
|
-
|
|
221
|
-
interface MarkdownRef {
|
|
222
|
-
push: (content: string, answerType: 'thinking' | 'answer') => void;
|
|
223
|
-
clear: () => void;
|
|
224
|
-
triggerWholeEnd: () => void;
|
|
225
|
-
flushBuffer: (answerType?: AnswerType) => void;
|
|
226
|
-
}
|
|
227
|
-
```
|
|
309
|
+
**核心问题**:流式输出时,不完整的 Markdown 语法会导致渲染错误
|
|
228
310
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
311
|
+
```tsx
|
|
312
|
+
// 🚨 问题场景
|
|
313
|
+
push('#'); // "# "
|
|
314
|
+
push(' '); // "# "
|
|
315
|
+
push('标题'); // "# 标题"
|
|
316
|
+
push('\n'); // "# 标题\n"
|
|
317
|
+
push('1'); // "# 标题\n1" ← 这里会被误解析为段落
|
|
318
|
+
push('.'); // "# 标题\n1." ← 形成正确的列表
|
|
319
|
+
push(' 项目'); // "# 标题\n1. 项目"
|
|
320
|
+
```
|
|
235
321
|
|
|
236
|
-
|
|
322
|
+
**✅ 智能解决方案**:组件内置同步缓冲机制
|
|
237
323
|
|
|
238
324
|
```tsx
|
|
239
|
-
|
|
325
|
+
// 组件会智能处理语法边界
|
|
326
|
+
const handleStreamingMarkdown = () => {
|
|
327
|
+
const chunks = ['#', ' ', '使用', '技能', '\n', '1', '.', ' ', '技能1', '\n', '2', '.', ' 技能2'];
|
|
240
328
|
|
|
241
|
-
|
|
242
|
-
|
|
329
|
+
chunks.forEach((chunk) => {
|
|
330
|
+
markdownRef.current?.push(chunk, 'answer');
|
|
331
|
+
// 无需延迟,组件内部智能缓冲
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// 可选:手动刷新剩余缓冲内容
|
|
335
|
+
markdownRef.current?.flushBuffer();
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// 🧠 智能处理流程:
|
|
339
|
+
// 1. 实时检测 "# 使用技能\n1" 语法不完整
|
|
340
|
+
// 2. 智能缓冲,等待更多字符
|
|
341
|
+
// 3. 收到 "." 形成 "1." 后立即处理
|
|
342
|
+
// 4. 零延迟,纯同步处理
|
|
243
343
|
```
|
|
244
344
|
|
|
245
|
-
|
|
345
|
+
**缓冲机制特性**:
|
|
246
346
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
border-left: 3px solid #ffc107;
|
|
252
|
-
padding: 12px;
|
|
253
|
-
border-radius: 4px;
|
|
254
|
-
}
|
|
347
|
+
- ⚡ **零延迟**:无 setTimeout,纯同步实时处理
|
|
348
|
+
- 🧠 **智能边界**:基于 Markdown 语法规则的边界检测
|
|
349
|
+
- 🔄 **实时分割**:遇到完整语法立即处理,不完整时智能缓冲
|
|
350
|
+
- 🛡️ **安全保障**:提供 `flushBuffer()` 方法处理剩余内容
|
|
255
351
|
|
|
256
|
-
|
|
257
|
-
.ds-markdown-answer {
|
|
258
|
-
color: #333;
|
|
259
|
-
line-height: 1.6;
|
|
260
|
-
}
|
|
352
|
+
**支持的语法检测**:
|
|
261
353
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
```
|
|
354
|
+
````typescript
|
|
355
|
+
// ✅ 完整语法 (立即处理)
|
|
356
|
+
'## 标题'; // 完整标题
|
|
357
|
+
'1. 列表项'; // 完整列表项
|
|
358
|
+
'- 项目'; // 完整无序列表
|
|
359
|
+
'> 引用内容'; // 完整引用
|
|
360
|
+
'```javascript'; // 代码块开始
|
|
361
|
+
'```'; // 代码块结束
|
|
362
|
+
'内容以换行结尾\n'; // 换行边界
|
|
363
|
+
|
|
364
|
+
// 🔄 不完整语法 (智能缓冲)
|
|
365
|
+
'##'; // 只有标题符号
|
|
366
|
+
'1'; // 只有数字
|
|
367
|
+
'```java'; // 可能的代码块开始
|
|
368
|
+
````
|
|
369
|
+
|
|
370
|
+
---
|
|
269
371
|
|
|
270
372
|
## 🔧 最佳实践
|
|
271
373
|
|
|
272
|
-
### 1.
|
|
374
|
+
### 1. 性能优化
|
|
273
375
|
|
|
274
376
|
```tsx
|
|
275
|
-
// ✅
|
|
377
|
+
// ✅ 推荐配置
|
|
276
378
|
<DsMarkdown
|
|
277
379
|
timerType="requestAnimationFrame"
|
|
278
380
|
interval={15} // 15-30ms 为最佳体验
|
|
279
381
|
/>
|
|
280
382
|
|
|
281
|
-
// ❌
|
|
383
|
+
// ❌ 避免过小间隔
|
|
282
384
|
<DsMarkdown interval={1} /> // 可能导致性能问题
|
|
283
385
|
```
|
|
284
386
|
|
|
285
387
|
### 2. 流式数据处理
|
|
286
388
|
|
|
287
389
|
```tsx
|
|
288
|
-
// ✅
|
|
289
|
-
const ref = useRef();
|
|
390
|
+
// ✅ 推荐:命令式 API
|
|
391
|
+
const ref = useRef<MarkdownRef>(null);
|
|
290
392
|
useEffect(() => {
|
|
291
|
-
// 实时接收数据时使用 push 方法
|
|
292
393
|
ref.current?.push(newChunk, 'answer');
|
|
293
394
|
}, [newChunk]);
|
|
294
395
|
|
|
295
|
-
// ❌ 避免:频繁更新 children
|
|
396
|
+
// ❌ 避免:频繁更新 children
|
|
296
397
|
const [content, setContent] = useState('');
|
|
297
398
|
// 每次更新都会重新解析整个内容
|
|
298
399
|
```
|
|
299
400
|
|
|
300
|
-
### 3.
|
|
301
|
-
|
|
302
|
-
**问题场景**: 流式输出时,不完整的 Markdown 语法会导致渲染错误
|
|
401
|
+
### 3. 类型安全
|
|
303
402
|
|
|
304
403
|
```tsx
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
push('\n'); // "# 标题\n"
|
|
310
|
-
push('1'); // "# 标题\n1" ← 这里会被误解析
|
|
311
|
-
push('.'); // "# 标题\n1." ← 形成正确的列表
|
|
312
|
-
push(' 列表项'); // "# 标题\n1. 列表项"
|
|
404
|
+
import { MarkdownRef } from 'ds-markdown';
|
|
405
|
+
|
|
406
|
+
const ref = useRef<MarkdownRef>(null);
|
|
407
|
+
// 完整的 TypeScript 类型提示
|
|
313
408
|
```
|
|
314
409
|
|
|
315
|
-
|
|
410
|
+
### 4. 样式定制
|
|
316
411
|
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
412
|
+
```css
|
|
413
|
+
/* 思考区域样式 */
|
|
414
|
+
.ds-markdown-thinking {
|
|
415
|
+
background: rgba(255, 193, 7, 0.1);
|
|
416
|
+
border-left: 3px solid #ffc107;
|
|
417
|
+
padding: 12px;
|
|
418
|
+
border-radius: 6px;
|
|
419
|
+
margin: 8px 0;
|
|
420
|
+
}
|
|
321
421
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
422
|
+
/* 回答区域样式 */
|
|
423
|
+
.ds-markdown-answer {
|
|
424
|
+
color: #333;
|
|
425
|
+
line-height: 1.6;
|
|
426
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
427
|
+
}
|
|
326
428
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
429
|
+
/* 代码块样式 */
|
|
430
|
+
.ds-markdown pre {
|
|
431
|
+
background: #f8f9fa;
|
|
432
|
+
border-radius: 8px;
|
|
433
|
+
padding: 16px;
|
|
434
|
+
overflow-x: auto;
|
|
435
|
+
}
|
|
330
436
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
437
|
+
/* 表格样式 */
|
|
438
|
+
.ds-markdown-table {
|
|
439
|
+
border-collapse: collapse;
|
|
440
|
+
width: 100%;
|
|
441
|
+
margin: 16px 0;
|
|
442
|
+
}
|
|
337
443
|
|
|
338
|
-
|
|
444
|
+
.ds-markdown-table th,
|
|
445
|
+
.ds-markdown-table td {
|
|
446
|
+
border: 1px solid #ddd;
|
|
447
|
+
padding: 8px 12px;
|
|
448
|
+
text-align: left;
|
|
449
|
+
}
|
|
450
|
+
```
|
|
339
451
|
|
|
340
|
-
|
|
341
|
-
- 🧠 **智能边界**: 基于 Markdown 语法规则的边界检测
|
|
342
|
-
- 🔄 **实时分割**: 遇到完整语法立即处理,不完整时智能缓冲
|
|
343
|
-
- 🛡️ **安全保障**: 提供 `flushBuffer()` 方法处理剩余内容
|
|
452
|
+
---
|
|
344
453
|
|
|
345
|
-
|
|
454
|
+
## 🌐 兼容性
|
|
346
455
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
'```javascript'; // 代码块开始
|
|
354
|
-
'```'; // 代码块结束
|
|
355
|
-
'内容以换行结尾\n'; // 换行边界
|
|
456
|
+
| 环境 | 版本要求 | 说明 |
|
|
457
|
+
| -------------- | ----------------------------------- | --------------- |
|
|
458
|
+
| **React** | 16.8.0+ | 需要 Hooks 支持 |
|
|
459
|
+
| **TypeScript** | 4.0+ | 可选,但推荐 |
|
|
460
|
+
| **浏览器** | Chrome 60+, Firefox 55+, Safari 12+ | 现代浏览器 |
|
|
461
|
+
| **Node.js** | 14.0+ | 构建环境 |
|
|
356
462
|
|
|
357
|
-
|
|
358
|
-
'##'; // 只有标题符号
|
|
359
|
-
'1'; // 只有数字
|
|
360
|
-
'```java'; // 可能的代码块开始
|
|
361
|
-
````
|
|
463
|
+
---
|
|
362
464
|
|
|
363
|
-
|
|
465
|
+
## 🤝 贡献指南
|
|
364
466
|
|
|
365
|
-
|
|
366
|
-
| ------------- | ------------------------- | ------------------ |
|
|
367
|
-
| `flushBuffer` | `answerType?: AnswerType` | 强制刷新缓冲区内容 |
|
|
467
|
+
欢迎提交 Issue 和 Pull Request!
|
|
368
468
|
|
|
369
|
-
|
|
469
|
+
1. Fork 本仓库
|
|
470
|
+
2. 创建特性分支:`git checkout -b feature/amazing-feature`
|
|
471
|
+
3. 提交更改:`git commit -m 'Add amazing feature'`
|
|
472
|
+
4. 推送分支:`git push origin feature/amazing-feature`
|
|
473
|
+
5. 提交 Pull Request
|
|
370
474
|
|
|
371
|
-
|
|
372
|
-
- **TypeScript**: 4.0+
|
|
373
|
-
- **浏览器**: Chrome 60+, Firefox 55+, Safari 12+
|
|
374
|
-
- **Node.js**: 14.0+
|
|
475
|
+
---
|
|
375
476
|
|
|
376
|
-
## 📄
|
|
477
|
+
## 📄 开源协议
|
|
377
478
|
|
|
378
479
|
MIT © [onshinpei](https://github.com/onshinpei)
|
|
379
480
|
|
|
@@ -381,4 +482,10 @@ MIT © [onshinpei](https://github.com/onshinpei)
|
|
|
381
482
|
|
|
382
483
|
<div align="center">
|
|
383
484
|
<strong>如果这个项目对你有帮助,请给个 ⭐️ Star 支持一下!</strong>
|
|
485
|
+
|
|
486
|
+
<br><br>
|
|
487
|
+
|
|
488
|
+
[🐛 报告问题](https://github.com/onshinpei/ds-markdown/issues) |
|
|
489
|
+
[💡 功能建议](https://github.com/onshinpei/ds-markdown/issues) |
|
|
490
|
+
[📖 查看文档](https://onshinpei.github.io/ds-markdown/)
|
|
384
491
|
</div>
|