ds-markdown 0.1.2-beta.2 โ 0.1.2
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 +542 -28
- package/README.ja.md +289 -33
- package/README.ko.md +245 -28
- package/README.md +397 -28
- package/dist/cjs/Markdown/index.js +3 -0
- package/dist/cjs/Markdown/index.js.map +1 -1
- package/dist/cjs/MarkdownCMD/index.js +26 -13
- package/dist/cjs/MarkdownCMD/index.js.map +1 -1
- package/dist/cjs/defined.d.ts +15 -6
- package/dist/cjs/hooks/useTypingTask.d.ts +4 -1
- package/dist/cjs/hooks/useTypingTask.js +82 -57
- package/dist/cjs/hooks/useTypingTask.js.map +1 -1
- package/dist/esm/Markdown/index.js +3 -0
- package/dist/esm/Markdown/index.js.map +1 -1
- package/dist/esm/MarkdownCMD/index.js +26 -13
- package/dist/esm/MarkdownCMD/index.js.map +1 -1
- package/dist/esm/defined.d.ts +15 -6
- package/dist/esm/hooks/useTypingTask.d.ts +4 -1
- package/dist/esm/hooks/useTypingTask.js +82 -57
- package/dist/esm/hooks/useTypingTask.js.map +1 -1
- package/package.json +2 -2
package/README.en.md
CHANGED
|
@@ -18,6 +18,57 @@ A React component designed specifically for modern AI applications, providing sm
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
## ๐ Table of Contents
|
|
22
|
+
|
|
23
|
+
- [โจ Core Features](#-core-features)
|
|
24
|
+
- [๐ฆ Quick Installation](#-quick-installation)
|
|
25
|
+
- [๐ 5-Minute Quick Start](#-5-minute-quick-start)
|
|
26
|
+
- [Basic Usage](#basic-usage)
|
|
27
|
+
- [Disable Typing Animation](#disable-typing-animation)
|
|
28
|
+
- [Mathematical Formula Support](#mathematical-formula-support)
|
|
29
|
+
- [AI Conversation Scenario](#ai-conversation-scenario)
|
|
30
|
+
- [๐ฏ Advanced Callback Control](#-advanced-callback-control)
|
|
31
|
+
- [๐ Restart Animation Demo](#-restart-animation-demo)
|
|
32
|
+
- [โถ๏ธ Manual Start Animation Demo](#๏ธ-manual-start-animation-demo)
|
|
33
|
+
- [๐ Complete API Documentation](#-complete-api-documentation)
|
|
34
|
+
- [๐งฎ Mathematical Formula Usage Guide](#-mathematical-formula-usage-guide)
|
|
35
|
+
- [๐ Plugin System](#-plugin-system)
|
|
36
|
+
- [๐๏ธ Timer Mode Details](#๏ธ-timer-mode-details)
|
|
37
|
+
- [๐ก Practical Examples](#-practical-examples)
|
|
38
|
+
- [๐ง Best Practices](#-best-practices)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## โ Why use ds-markdown?
|
|
43
|
+
|
|
44
|
+
- **Ultimate AI Chat Experience**
|
|
45
|
+
Faithfully recreates the typing animation and streaming response of leading AI chat interfaces (like DeepSeek), delivering a truly immersive "AI is thinking/answering" experience.
|
|
46
|
+
|
|
47
|
+
- **Perfect for Streaming Backend Data**
|
|
48
|
+
Many AI/LLM backends (OpenAI, DeepSeek, etc.) send data chunks containing multiple characters at once.
|
|
49
|
+
**ds-markdown automatically splits each chunk into single characters and animates them one by one, ensuring smooth typing even if the backend sends several characters at a time.**
|
|
50
|
+
|
|
51
|
+
- **Full Markdown & Math Formula Support**
|
|
52
|
+
Built-in KaTeX, supports all major Markdown syntax and math formulasโideal for technical Q&A, education, and knowledge bases.
|
|
53
|
+
|
|
54
|
+
- **Excellent Developer Experience**
|
|
55
|
+
Rich imperative API, supports streaming data, async callbacks, and plugin extensions for flexible animation and content control.
|
|
56
|
+
|
|
57
|
+
- **Lightweight & High Performance**
|
|
58
|
+
Small bundle size, fast, mobile and desktop ready. The only core dependency is [react-markdown](https://github.com/remarkjs/react-markdown) (a widely used, mature Markdown renderer). No other heavy dependenciesโworks out of the box.
|
|
59
|
+
|
|
60
|
+
- **Multi-theme & Plugin Architecture**
|
|
61
|
+
Light/dark theme switching, remark/rehype plugin compatibility, and advanced extensibility.
|
|
62
|
+
|
|
63
|
+
- **Wide Range of Use Cases**
|
|
64
|
+
- AI chatbots/assistants
|
|
65
|
+
- Real-time Q&A/knowledge bases
|
|
66
|
+
- Education/math/programming content
|
|
67
|
+
- Product demos, interactive docs
|
|
68
|
+
- Any scenario needing "typewriter" animation and streaming Markdown rendering
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
21
72
|
## โจ Core Features
|
|
22
73
|
|
|
23
74
|
### ๐ค **AI Conversation Scenarios**
|
|
@@ -184,6 +235,227 @@ Let's explore these new features together!`);
|
|
|
184
235
|
}
|
|
185
236
|
```
|
|
186
237
|
|
|
238
|
+
### ๐ฏ Advanced Callback Control
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
import { useRef, useState } from 'react';
|
|
242
|
+
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
|
|
243
|
+
|
|
244
|
+
function AdvancedCallbackDemo() {
|
|
245
|
+
const markdownRef = useRef<MarkdownCMDRef>(null);
|
|
246
|
+
const [typingStats, setTypingStats] = useState({ progress: 0, currentChar: '', totalChars: 0 });
|
|
247
|
+
|
|
248
|
+
const handleBeforeTypedChar = async (data) => {
|
|
249
|
+
// Perform async operations before character typing
|
|
250
|
+
console.log('About to type:', data.currentChar);
|
|
251
|
+
|
|
252
|
+
// Can perform network requests, data validation, etc.
|
|
253
|
+
if (data.currentChar === '!') {
|
|
254
|
+
await new Promise((resolve) => setTimeout(resolve, 500)); // Simulate delay
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const handleTypedChar = (data) => {
|
|
259
|
+
// Update typing statistics
|
|
260
|
+
setTypingStats({
|
|
261
|
+
progress: Math.round(data.percent),
|
|
262
|
+
currentChar: data.currentChar,
|
|
263
|
+
totalChars: data.currentIndex + 1,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Can add sound effects, animations, etc.
|
|
267
|
+
if (data.currentChar === '.') {
|
|
268
|
+
// Play period sound effect
|
|
269
|
+
console.log('Play period sound effect');
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const handleStart = (data) => {
|
|
274
|
+
console.log('Start typing:', data.currentChar);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const handleEnd = (data) => {
|
|
278
|
+
console.log('Typing complete:', data.str);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const startDemo = () => {
|
|
282
|
+
markdownRef.current?.clear();
|
|
283
|
+
markdownRef.current?.push(
|
|
284
|
+
'# Advanced Callback Demo\n\n' +
|
|
285
|
+
'This example shows how to use `onBeforeTypedChar` and `onTypedChar` callbacks:\n\n' +
|
|
286
|
+
'- ๐ฏ **Pre-typing callback**: Can perform async operations before character display\n' +
|
|
287
|
+
'- ๐ **Post-typing callback**: Can update progress in real-time and add effects\n' +
|
|
288
|
+
'- โก **Performance optimization**: Supports async operations without affecting typing smoothness\n\n' +
|
|
289
|
+
'Current progress: ' +
|
|
290
|
+
typingStats.progress +
|
|
291
|
+
'%\n' +
|
|
292
|
+
'Characters typed: ' +
|
|
293
|
+
typingStats.totalChars +
|
|
294
|
+
'\n\n' +
|
|
295
|
+
'This is a very powerful feature!',
|
|
296
|
+
'answer',
|
|
297
|
+
);
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<div>
|
|
302
|
+
<button onClick={startDemo}>๐ Start Advanced Demo</button>
|
|
303
|
+
|
|
304
|
+
<div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
305
|
+
<strong>Typing Stats:</strong> Progress {typingStats.progress}% | Current char: "{typingStats.currentChar}" | Total chars: {typingStats.totalChars}
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
<MarkdownCMD ref={markdownRef} interval={30} onBeforeTypedChar={handleBeforeTypedChar} onTypedChar={handleTypedChar} onStart={handleStart} onEnd={handleEnd} />
|
|
309
|
+
</div>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### ๐ Restart Animation Demo
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import { useRef, useState } from 'react';
|
|
318
|
+
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
|
|
319
|
+
|
|
320
|
+
function RestartDemo() {
|
|
321
|
+
const markdownRef = useRef<MarkdownCMDRef>(null);
|
|
322
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
323
|
+
const [hasStarted, setHasStarted] = useState(false);
|
|
324
|
+
|
|
325
|
+
const startContent = () => {
|
|
326
|
+
markdownRef.current?.clear();
|
|
327
|
+
markdownRef.current?.push(
|
|
328
|
+
'# Restart Animation Demo\n\n' +
|
|
329
|
+
'This example shows how to use the `restart()` method:\n\n' +
|
|
330
|
+
'- ๐ **Restart**: Play current content from the beginning\n' +
|
|
331
|
+
'- โธ๏ธ **Pause/Resume**: Can pause and resume at any time\n' +
|
|
332
|
+
'- ๐ฏ **Precise Control**: Complete control over animation playback state\n\n' +
|
|
333
|
+
'Current state: ' +
|
|
334
|
+
(isPlaying ? 'Playing' : 'Paused') +
|
|
335
|
+
'\n\n' +
|
|
336
|
+
'This is a very practical feature!',
|
|
337
|
+
'answer',
|
|
338
|
+
);
|
|
339
|
+
setIsPlaying(true);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const handleStart = () => {
|
|
343
|
+
if (hasStarted) {
|
|
344
|
+
// If already started, restart
|
|
345
|
+
markdownRef.current?.restart();
|
|
346
|
+
} else {
|
|
347
|
+
// First time start
|
|
348
|
+
markdownRef.current?.start();
|
|
349
|
+
setHasStarted(true);
|
|
350
|
+
}
|
|
351
|
+
setIsPlaying(true);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const handleStop = () => {
|
|
355
|
+
markdownRef.current?.stop();
|
|
356
|
+
setIsPlaying(false);
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const handleResume = () => {
|
|
360
|
+
markdownRef.current?.resume();
|
|
361
|
+
setIsPlaying(true);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const handleRestart = () => {
|
|
365
|
+
markdownRef.current?.restart();
|
|
366
|
+
setIsPlaying(true);
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const handleEnd = () => {
|
|
370
|
+
setIsPlaying(false);
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
return (
|
|
374
|
+
<div>
|
|
375
|
+
<div style={{ marginBottom: '10px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
376
|
+
<button onClick={startContent}>๐ Start Content</button>
|
|
377
|
+
<button onClick={handleStart} disabled={isPlaying}>
|
|
378
|
+
{hasStarted ? '๐ Restart' : 'โถ๏ธ Start'}
|
|
379
|
+
</button>
|
|
380
|
+
<button onClick={handleStop} disabled={!isPlaying}>
|
|
381
|
+
โธ๏ธ Pause
|
|
382
|
+
</button>
|
|
383
|
+
<button onClick={handleResume} disabled={isPlaying}>
|
|
384
|
+
โถ๏ธ Resume
|
|
385
|
+
</button>
|
|
386
|
+
<button onClick={handleRestart}>๐ Restart</button>
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
<div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
390
|
+
<strong>Animation State:</strong> {isPlaying ? '๐ข Playing' : '๐ด Paused'}
|
|
391
|
+
</div>
|
|
392
|
+
|
|
393
|
+
<MarkdownCMD ref={markdownRef} interval={25} onEnd={handleEnd} />
|
|
394
|
+
</div>
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### โถ๏ธ Manual Start Animation Demo
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
import { useRef, useState } from 'react';
|
|
403
|
+
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
|
|
404
|
+
|
|
405
|
+
function StartDemo() {
|
|
406
|
+
const markdownRef = useRef<MarkdownCMDRef>(null);
|
|
407
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
408
|
+
const [hasStarted, setHasStarted] = useState(false);
|
|
409
|
+
|
|
410
|
+
const loadContent = () => {
|
|
411
|
+
markdownRef.current?.clear();
|
|
412
|
+
markdownRef.current?.push(
|
|
413
|
+
'# Manual Start Animation Demo\n\n' +
|
|
414
|
+
'This example shows how to use the `start()` method:\n\n' +
|
|
415
|
+
'- ๐ฏ **Manual Control**: When `autoStartTyping=false`, need to manually call `start()`\n' +
|
|
416
|
+
'- โฑ๏ธ **Delayed Start**: Can start animation after user interaction\n' +
|
|
417
|
+
'- ๐ฎ **Gamification**: Suitable for scenarios requiring user initiative\n\n' +
|
|
418
|
+
'Click the "Start Animation" button to manually trigger the typing effect!',
|
|
419
|
+
'answer',
|
|
420
|
+
);
|
|
421
|
+
setIsPlaying(false);
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const handleStart = () => {
|
|
425
|
+
if (hasStarted) {
|
|
426
|
+
// If already started, restart
|
|
427
|
+
markdownRef.current?.restart();
|
|
428
|
+
} else {
|
|
429
|
+
// First time start
|
|
430
|
+
markdownRef.current?.start();
|
|
431
|
+
setHasStarted(true);
|
|
432
|
+
}
|
|
433
|
+
setIsPlaying(true);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const handleEnd = () => {
|
|
437
|
+
setIsPlaying(false);
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
return (
|
|
441
|
+
<div>
|
|
442
|
+
<div style={{ marginBottom: '10px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
443
|
+
<button onClick={loadContent}>๐ Load Content</button>
|
|
444
|
+
<button onClick={handleStart} disabled={isPlaying}>
|
|
445
|
+
{hasStarted ? '๐ Restart' : 'โถ๏ธ Start Animation'}
|
|
446
|
+
</button>
|
|
447
|
+
</div>
|
|
448
|
+
|
|
449
|
+
<div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
450
|
+
<strong>State:</strong> {isPlaying ? '๐ข Animation Playing' : '๐ด Waiting to Start'}
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
<MarkdownCMD ref={markdownRef} interval={30} autoStartTyping={false} onEnd={handleEnd} />
|
|
454
|
+
</div>
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
187
459
|
---
|
|
188
460
|
|
|
189
461
|
## ๐ Complete API Documentation
|
|
@@ -194,28 +466,43 @@ Let's explore these new features together!`);
|
|
|
194
466
|
import DsMarkdown, { MarkdownCMD } from 'ds-markdown';
|
|
195
467
|
```
|
|
196
468
|
|
|
197
|
-
| Property
|
|
198
|
-
|
|
|
199
|
-
| `interval`
|
|
200
|
-
| `timerType`
|
|
201
|
-
| `answerType`
|
|
202
|
-
| `theme`
|
|
203
|
-
| `plugins`
|
|
204
|
-
| `math`
|
|
205
|
-
| `onEnd`
|
|
206
|
-
| `onStart`
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
469
|
+
| Property | Type | Description | Default |
|
|
470
|
+
| ------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
471
|
+
| `interval` | `number` | Typing interval (milliseconds) | `30` |
|
|
472
|
+
| `timerType` | `'setTimeout'` \| `'requestAnimationFrame'` | Timer type | Current default is `setTimeout`, will change to `requestAnimationFrame` later |
|
|
473
|
+
| `answerType` | `'thinking'` \| `'answer'` | Content type (affects styling) | `'answer'` |
|
|
474
|
+
| `theme` | `'light'` \| `'dark'` | Theme type | `'light'` |
|
|
475
|
+
| `plugins` | `IMarkdownPlugin[]` | Plugin configuration | `[]` |
|
|
476
|
+
| `math` | [IMarkdownMath](#IMarkdownMath) | Mathematical formula config | `{ splitSymbol: 'dollar' }` |
|
|
477
|
+
| `onEnd` | `(data: EndData) => void` | Typing completion callback | - |
|
|
478
|
+
| `onStart` | `(data: StartData) => void` | Typing start callback | - |
|
|
479
|
+
| `onBeforeTypedChar` | `(data: IBeforeTypedChar) => Promise<void>` | Character typing pre-callback, supports async operations, blocks subsequent typing | - |
|
|
480
|
+
| `onTypedChar` | `(data: ITypedChar) => void` | Character typing post-callback | - |
|
|
481
|
+
| `disableTyping` | `boolean` | Disable typing animation | `false` |
|
|
482
|
+
| `autoStartTyping` | `boolean` | Whether to auto-start typing animation, set to false for manual trigger | `true` |
|
|
209
483
|
|
|
210
484
|
> Note: If `disableTyping` changes from `true` to `false` during typing, all remaining characters will be displayed at once on the next typing trigger.
|
|
211
485
|
|
|
486
|
+
### IBeforeTypedChar
|
|
487
|
+
|
|
488
|
+
| Property | Type | Description | Default |
|
|
489
|
+
| -------------- | ------------ | -------------------------------------------- | ------- |
|
|
490
|
+
| `currentIndex` | `number` | Current character index in the entire string | `0` |
|
|
491
|
+
| `currentChar` | `string` | Character about to be typed | - |
|
|
492
|
+
| `answerType` | `AnswerType` | Content type (thinking/answer) | - |
|
|
493
|
+
| `prevStr` | `string` | Previous string of current type content | - |
|
|
494
|
+
| `percent` | `number` | Typing progress percentage (0-100) | `0` |
|
|
495
|
+
|
|
212
496
|
### ITypedChar
|
|
213
497
|
|
|
214
|
-
| Property | Type
|
|
215
|
-
| -------------- |
|
|
216
|
-
| `
|
|
217
|
-
| `currentChar` | `string`
|
|
218
|
-
| `
|
|
498
|
+
| Property | Type | Description | Default |
|
|
499
|
+
| -------------- | ------------ | -------------------------------------------- | ------- |
|
|
500
|
+
| `currentIndex` | `number` | Current character index in the entire string | `0` |
|
|
501
|
+
| `currentChar` | `string` | Character that was just typed | - |
|
|
502
|
+
| `answerType` | `AnswerType` | Content type (thinking/answer) | - |
|
|
503
|
+
| `prevStr` | `string` | Previous string of current type content | - |
|
|
504
|
+
| `currentStr` | `string` | Complete string of current type content | - |
|
|
505
|
+
| `percent` | `number` | Typing progress percentage (0-100) | `0` |
|
|
219
506
|
|
|
220
507
|
#### IMarkdownMath
|
|
221
508
|
|
|
@@ -241,26 +528,32 @@ import DsMarkdown, { MarkdownCMD } from 'ds-markdown';
|
|
|
241
528
|
|
|
242
529
|
#### Default export DsMarkdown
|
|
243
530
|
|
|
244
|
-
| Method
|
|
245
|
-
|
|
|
246
|
-
| `
|
|
247
|
-
| `
|
|
531
|
+
| Method | Parameters | Description |
|
|
532
|
+
| --------- | ---------- | ------------------------------------------------------------- |
|
|
533
|
+
| `start` | - | Start typing animation |
|
|
534
|
+
| `stop` | - | Pause typing |
|
|
535
|
+
| `resume` | - | Resume typing |
|
|
536
|
+
| `restart` | - | Restart typing animation, play current content from beginning |
|
|
248
537
|
|
|
249
538
|
#### MarkdownCMD Exposed Methods
|
|
250
539
|
|
|
251
|
-
| Method | Parameters | Description
|
|
252
|
-
| ----------------- | ------------------------------------------- |
|
|
253
|
-
| `push` | `(content: string, answerType: AnswerType)` | Add content and start typing
|
|
254
|
-
| `clear` | - | Clear all content and state
|
|
255
|
-
| `triggerWholeEnd` | - | Manually trigger end callback
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
540
|
+
| Method | Parameters | Description |
|
|
541
|
+
| ----------------- | ------------------------------------------- | ------------------------------------------------------------- |
|
|
542
|
+
| `push` | `(content: string, answerType: AnswerType)` | Add content and start typing |
|
|
543
|
+
| `clear` | - | Clear all content and state |
|
|
544
|
+
| `triggerWholeEnd` | - | Manually trigger end callback |
|
|
545
|
+
| `start` | - | Start typing animation |
|
|
546
|
+
| `stop` | - | Pause typing |
|
|
547
|
+
| `resume` | - | Resume typing |
|
|
548
|
+
| `restart` | - | Restart typing animation, play current content from beginning |
|
|
258
549
|
|
|
259
550
|
**Usage example:**
|
|
260
551
|
|
|
261
552
|
```tsx
|
|
553
|
+
markdownRef.current?.start(); // Start animation
|
|
262
554
|
markdownRef.current?.stop(); // Pause animation
|
|
263
555
|
markdownRef.current?.resume(); // Resume animation
|
|
556
|
+
markdownRef.current?.restart(); // Restart animation
|
|
264
557
|
```
|
|
265
558
|
|
|
266
559
|
---
|
|
@@ -533,6 +826,227 @@ function MathStreamingDemo() {
|
|
|
533
826
|
}
|
|
534
827
|
```
|
|
535
828
|
|
|
829
|
+
### ๐ฏ Advanced Callback Control
|
|
830
|
+
|
|
831
|
+
```tsx
|
|
832
|
+
import { useRef, useState } from 'react';
|
|
833
|
+
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
|
|
834
|
+
|
|
835
|
+
function AdvancedCallbackDemo() {
|
|
836
|
+
const markdownRef = useRef<MarkdownCMDRef>(null);
|
|
837
|
+
const [typingStats, setTypingStats] = useState({ progress: 0, currentChar: '', totalChars: 0 });
|
|
838
|
+
|
|
839
|
+
const handleBeforeTypedChar = async (data) => {
|
|
840
|
+
// Perform async operations before character typing
|
|
841
|
+
console.log('About to type:', data.currentChar);
|
|
842
|
+
|
|
843
|
+
// Can perform network requests, data validation, etc.
|
|
844
|
+
if (data.currentChar === '!') {
|
|
845
|
+
await new Promise((resolve) => setTimeout(resolve, 500)); // Simulate delay
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
const handleTypedChar = (data) => {
|
|
850
|
+
// Update typing statistics
|
|
851
|
+
setTypingStats({
|
|
852
|
+
progress: Math.round(data.percent),
|
|
853
|
+
currentChar: data.currentChar,
|
|
854
|
+
totalChars: data.currentIndex + 1,
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
// Can add sound effects, animations, etc.
|
|
858
|
+
if (data.currentChar === '.') {
|
|
859
|
+
// Play period sound effect
|
|
860
|
+
console.log('Play period sound effect');
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
const handleStart = (data) => {
|
|
865
|
+
console.log('Start typing:', data.currentChar);
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
const handleEnd = (data) => {
|
|
869
|
+
console.log('Typing complete:', data.str);
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
const startDemo = () => {
|
|
873
|
+
markdownRef.current?.clear();
|
|
874
|
+
markdownRef.current?.push(
|
|
875
|
+
'# Advanced Callback Demo\n\n' +
|
|
876
|
+
'This example shows how to use `onBeforeTypedChar` and `onTypedChar` callbacks:\n\n' +
|
|
877
|
+
'- ๐ฏ **Pre-typing callback**: Can perform async operations before character display\n' +
|
|
878
|
+
'- ๐ **Post-typing callback**: Can update progress in real-time and add effects\n' +
|
|
879
|
+
'- โก **Performance optimization**: Supports async operations without affecting typing smoothness\n\n' +
|
|
880
|
+
'Current progress: ' +
|
|
881
|
+
typingStats.progress +
|
|
882
|
+
'%\n' +
|
|
883
|
+
'Characters typed: ' +
|
|
884
|
+
typingStats.totalChars +
|
|
885
|
+
'\n\n' +
|
|
886
|
+
'This is a very powerful feature!',
|
|
887
|
+
'answer',
|
|
888
|
+
);
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
return (
|
|
892
|
+
<div>
|
|
893
|
+
<button onClick={startDemo}>๐ Start Advanced Demo</button>
|
|
894
|
+
|
|
895
|
+
<div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
896
|
+
<strong>Typing Stats:</strong> Progress {typingStats.progress}% | Current char: "{typingStats.currentChar}" | Total chars: {typingStats.totalChars}
|
|
897
|
+
</div>
|
|
898
|
+
|
|
899
|
+
<MarkdownCMD ref={markdownRef} interval={30} onBeforeTypedChar={handleBeforeTypedChar} onTypedChar={handleTypedChar} onStart={handleStart} onEnd={handleEnd} />
|
|
900
|
+
</div>
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
### ๐ Restart Animation Demo
|
|
906
|
+
|
|
907
|
+
```tsx
|
|
908
|
+
import { useRef, useState } from 'react';
|
|
909
|
+
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
|
|
910
|
+
|
|
911
|
+
function RestartDemo() {
|
|
912
|
+
const markdownRef = useRef<MarkdownCMDRef>(null);
|
|
913
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
914
|
+
const [hasStarted, setHasStarted] = useState(false);
|
|
915
|
+
|
|
916
|
+
const startContent = () => {
|
|
917
|
+
markdownRef.current?.clear();
|
|
918
|
+
markdownRef.current?.push(
|
|
919
|
+
'# Restart Animation Demo\n\n' +
|
|
920
|
+
'This example shows how to use the `restart()` method:\n\n' +
|
|
921
|
+
'- ๐ **Restart**: Play current content from the beginning\n' +
|
|
922
|
+
'- โธ๏ธ **Pause/Resume**: Can pause and resume at any time\n' +
|
|
923
|
+
'- ๐ฏ **Precise Control**: Complete control over animation playback state\n\n' +
|
|
924
|
+
'Current state: ' +
|
|
925
|
+
(isPlaying ? 'Playing' : 'Paused') +
|
|
926
|
+
'\n\n' +
|
|
927
|
+
'This is a very practical feature!',
|
|
928
|
+
'answer',
|
|
929
|
+
);
|
|
930
|
+
setIsPlaying(true);
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
const handleStart = () => {
|
|
934
|
+
if (hasStarted) {
|
|
935
|
+
// If already started, restart
|
|
936
|
+
markdownRef.current?.restart();
|
|
937
|
+
} else {
|
|
938
|
+
// First time start
|
|
939
|
+
markdownRef.current?.start();
|
|
940
|
+
setHasStarted(true);
|
|
941
|
+
}
|
|
942
|
+
setIsPlaying(true);
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
const handleStop = () => {
|
|
946
|
+
markdownRef.current?.stop();
|
|
947
|
+
setIsPlaying(false);
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
const handleResume = () => {
|
|
951
|
+
markdownRef.current?.resume();
|
|
952
|
+
setIsPlaying(true);
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
const handleRestart = () => {
|
|
956
|
+
markdownRef.current?.restart();
|
|
957
|
+
setIsPlaying(true);
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
const handleEnd = () => {
|
|
961
|
+
setIsPlaying(false);
|
|
962
|
+
};
|
|
963
|
+
|
|
964
|
+
return (
|
|
965
|
+
<div>
|
|
966
|
+
<div style={{ marginBottom: '10px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
967
|
+
<button onClick={startContent}>๐ Start Content</button>
|
|
968
|
+
<button onClick={handleStart} disabled={isPlaying}>
|
|
969
|
+
{hasStarted ? '๐ Restart' : 'โถ๏ธ Start'}
|
|
970
|
+
</button>
|
|
971
|
+
<button onClick={handleStop} disabled={!isPlaying}>
|
|
972
|
+
โธ๏ธ Pause
|
|
973
|
+
</button>
|
|
974
|
+
<button onClick={handleResume} disabled={isPlaying}>
|
|
975
|
+
โถ๏ธ Resume
|
|
976
|
+
</button>
|
|
977
|
+
<button onClick={handleRestart}>๐ Restart</button>
|
|
978
|
+
</div>
|
|
979
|
+
|
|
980
|
+
<div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
981
|
+
<strong>Animation State:</strong> {isPlaying ? '๐ข Playing' : '๐ด Paused'}
|
|
982
|
+
</div>
|
|
983
|
+
|
|
984
|
+
<MarkdownCMD ref={markdownRef} interval={25} onEnd={handleEnd} />
|
|
985
|
+
</div>
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
### โถ๏ธ Manual Start Animation Demo
|
|
991
|
+
|
|
992
|
+
```tsx
|
|
993
|
+
import { useRef, useState } from 'react';
|
|
994
|
+
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
|
|
995
|
+
|
|
996
|
+
function StartDemo() {
|
|
997
|
+
const markdownRef = useRef<MarkdownCMDRef>(null);
|
|
998
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
999
|
+
const [hasStarted, setHasStarted] = useState(false);
|
|
1000
|
+
|
|
1001
|
+
const loadContent = () => {
|
|
1002
|
+
markdownRef.current?.clear();
|
|
1003
|
+
markdownRef.current?.push(
|
|
1004
|
+
'# Manual Start Animation Demo\n\n' +
|
|
1005
|
+
'This example shows how to use the `start()` method:\n\n' +
|
|
1006
|
+
'- ๐ฏ **Manual Control**: When `autoStartTyping=false`, need to manually call `start()`\n' +
|
|
1007
|
+
'- โฑ๏ธ **Delayed Start**: Can start animation after user interaction\n' +
|
|
1008
|
+
'- ๐ฎ **Gamification**: Suitable for scenarios requiring user initiative\n\n' +
|
|
1009
|
+
'Click the "Start Animation" button to manually trigger the typing effect!',
|
|
1010
|
+
'answer',
|
|
1011
|
+
);
|
|
1012
|
+
setIsPlaying(false);
|
|
1013
|
+
};
|
|
1014
|
+
|
|
1015
|
+
const handleStart = () => {
|
|
1016
|
+
if (hasStarted) {
|
|
1017
|
+
// If already started, restart
|
|
1018
|
+
markdownRef.current?.restart();
|
|
1019
|
+
} else {
|
|
1020
|
+
// First time start
|
|
1021
|
+
markdownRef.current?.start();
|
|
1022
|
+
setHasStarted(true);
|
|
1023
|
+
}
|
|
1024
|
+
setIsPlaying(true);
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
const handleEnd = () => {
|
|
1028
|
+
setIsPlaying(false);
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
return (
|
|
1032
|
+
<div>
|
|
1033
|
+
<div style={{ marginBottom: '10px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
1034
|
+
<button onClick={loadContent}>๐ Load Content</button>
|
|
1035
|
+
<button onClick={handleStart} disabled={isPlaying}>
|
|
1036
|
+
{hasStarted ? '๐ Restart' : 'โถ๏ธ Start Animation'}
|
|
1037
|
+
</button>
|
|
1038
|
+
</div>
|
|
1039
|
+
|
|
1040
|
+
<div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
1041
|
+
<strong>State:</strong> {isPlaying ? '๐ข Animation Playing' : '๐ด Waiting to Start'}
|
|
1042
|
+
</div>
|
|
1043
|
+
|
|
1044
|
+
<MarkdownCMD ref={markdownRef} interval={30} autoStartTyping={false} onEnd={handleEnd} />
|
|
1045
|
+
</div>
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
```
|
|
1049
|
+
|
|
536
1050
|
## ๐ง Best Practices
|
|
537
1051
|
|
|
538
1052
|
### 1. Performance Optimization
|