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