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 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 | Type | Description | Default |
198
- | --------------- | --------------------------------------------- | ------------------------------- | ----------------------------------------------------------------------------- |
199
- | `interval` | `number` | Typing interval (milliseconds) | `30` |
200
- | `timerType` | `'setTimeout'` \| `'requestAnimationFrame'` | Timer type | Current default is `setTimeout`, will change to `requestAnimationFrame` later |
201
- | `answerType` | `'thinking'` \| `'answer'` | Content type (affects styling) | `'answer'` |
202
- | `theme` | `'light'` \| `'dark'` | Theme type | `'light'` |
203
- | `plugins` | `IMarkdownPlugin[]` | Plugin configuration | `[]` |
204
- | `math` | [IMarkdownMath](#IMarkdownMath) | Mathematical formula config | `{ splitSymbol: 'dollar' }` |
205
- | `onEnd` | `(data: EndData) => void` | Typing completion callback | - |
206
- | `onStart` | `(data: StartData) => void` | Typing start callback | - |
207
- | `onTypedChar` | `(data: `[ITypedChar](#ITypedChar)`) => void` | Character-by-character callback | - |
208
- | `disableTyping` | `boolean` | Disable typing animation | `false` |
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 | Description | Default |
215
- | -------------- | -------- | ---------------------------------- | ------- |
216
- | `percent` | `number` | Typing progress percentage | `0` |
217
- | `currentChar` | `string` | Current character being typed | - |
218
- | `currentIndex` | `number` | Current index in the entire string | `0` |
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 | Parameters | Description |
245
- | -------- | ---------- | ------------- |
246
- | `stop` | - | Pause typing |
247
- | `resume` | - | Resume typing |
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
- | `stop` | - | Pause typing |
257
- | `resume` | - | Resume typing |
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