ds-markdown 0.0.10 β 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 +42 -0
- package/package.json +11 -12
package/README.en.md
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
# ds-markdown
|
|
2
|
+
|
|
3
|
+
> π High-performance React Markdown typing animation component, perfectly replicating DeepSeek chat interface effects
|
|
4
|
+
|
|
5
|
+
**[π¨π³ δΈζ](./README.md) | πΊπΈ English | [π―π΅ ζ₯ζ¬θͺ](./README.ja.md) | [π°π· νκ΅μ΄](./README.ko.md)**
|
|
6
|
+
|
|
7
|
+
A React component designed specifically for modern AI applications, providing smooth real-time typing animations and complete Markdown rendering capabilities.
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/ds-markdown)
|
|
10
|
+
[](https://www.npmjs.com/package/ds-markdown)
|
|
11
|
+
[](https://bundlephobia.com/package/ds-markdown)
|
|
12
|
+
[](https://react.dev)
|
|
13
|
+
[](https://www.typescriptlang.org/)
|
|
14
|
+
|
|
15
|
+
[π Live Demo](https://onshinpei.github.io/ds-markdown/) | [π§ StackBlitz Experience](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## β¨ Core Features
|
|
20
|
+
|
|
21
|
+
### π― **Perfect Replication**
|
|
22
|
+
|
|
23
|
+
- 1:1 recreation of [DeepSeek website](https://chat.deepseek.com/) chat response effects
|
|
24
|
+
- Support for both thinking process (`thinking`) and answer content (`answer`) modes
|
|
25
|
+
- Native Markdown syntax support, including code highlighting, tables, lists, etc.
|
|
26
|
+
|
|
27
|
+
### β‘ **Ultimate Performance**
|
|
28
|
+
|
|
29
|
+
- Smart batch processing, zero lag rendering for large documents
|
|
30
|
+
- Dual timer modes: `requestAnimationFrame` + `setTimeout`
|
|
31
|
+
- Built-in streaming syntax buffering, avoiding incomplete Markdown rendering errors
|
|
32
|
+
|
|
33
|
+
### π¬ **Smooth Animation**
|
|
34
|
+
|
|
35
|
+
- High-frequency typing support (`requestAnimationFrame` mode supports typing intervals as low as `0ms`)
|
|
36
|
+
- Frame-synchronized rendering, perfectly matching browser 60fps
|
|
37
|
+
- Smart character batch processing for more natural visual effects
|
|
38
|
+
|
|
39
|
+
### π§ **Flexible and Easy to Use**
|
|
40
|
+
|
|
41
|
+
- **Declarative API**: Suitable for simple scenarios, React-style
|
|
42
|
+
- **Imperative API**: Suitable for streaming data, better performance
|
|
43
|
+
- **Native TypeScript support**: Complete type hints
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## π¦ Quick Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# npm
|
|
51
|
+
npm install ds-markdown
|
|
52
|
+
|
|
53
|
+
# yarn
|
|
54
|
+
yarn add ds-markdown
|
|
55
|
+
|
|
56
|
+
# pnpm
|
|
57
|
+
pnpm add ds-markdown
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Using via ESM CDN
|
|
61
|
+
|
|
62
|
+
No installation required, use directly in browser:
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<!-- Import styles -->
|
|
66
|
+
<link rel="stylesheet" href="https://esm.sh/ds-markdown/style.css" />
|
|
67
|
+
|
|
68
|
+
<!-- Import component -->
|
|
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
|
+
This is a **high-performance** typing animation component!
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
- β‘ Zero-delay streaming
|
|
91
|
+
- π¬ Smooth typing animation
|
|
92
|
+
- π― Perfect syntax support
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const root = createRoot(document.getElementById('root'));
|
|
96
|
+
root.render(<DsMarkdown interval={20}>{markdown}</DsMarkdown>);
|
|
97
|
+
</script>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## π 5-Minute Quick Start
|
|
101
|
+
|
|
102
|
+
### Basic Usage
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
import DsMarkdown from 'ds-markdown';
|
|
106
|
+
import 'ds-markdown/style.css';
|
|
107
|
+
|
|
108
|
+
function App() {
|
|
109
|
+
return (
|
|
110
|
+
<DsMarkdown interval={20} answerType="answer">
|
|
111
|
+
# Hello ds-markdown This is a **high-performance** typing animation component! ## Features - β‘ Zero-delay streaming processing - π¬ Smooth typing animation - π― Perfect syntax support
|
|
112
|
+
</DsMarkdown>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### AI Conversation Scenario
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
function ChatDemo() {
|
|
121
|
+
const [thinking, setThinking] = useState('');
|
|
122
|
+
const [answer, setAnswer] = useState('');
|
|
123
|
+
|
|
124
|
+
const handleAsk = () => {
|
|
125
|
+
setThinking('π€ Thinking about your question...');
|
|
126
|
+
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
setAnswer(`# About React 19
|
|
129
|
+
|
|
130
|
+
React 19 brings many exciting new features:
|
|
131
|
+
|
|
132
|
+
## π Major Updates
|
|
133
|
+
1. **React Compiler** - Automatic performance optimization
|
|
134
|
+
2. **Actions** - Simplified form handling
|
|
135
|
+
3. **Document Metadata** - Built-in SEO support
|
|
136
|
+
|
|
137
|
+
Let's explore these new features together!`);
|
|
138
|
+
}, 2000);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div>
|
|
143
|
+
<button onClick={handleAsk}>Ask AI</button>
|
|
144
|
+
|
|
145
|
+
{thinking && (
|
|
146
|
+
<DsMarkdown answerType="thinking" interval={30}>
|
|
147
|
+
{thinking}
|
|
148
|
+
</DsMarkdown>
|
|
149
|
+
)}
|
|
150
|
+
|
|
151
|
+
{answer && (
|
|
152
|
+
<DsMarkdown answerType="answer" interval={15}>
|
|
153
|
+
{answer}
|
|
154
|
+
</DsMarkdown>
|
|
155
|
+
)}
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## π Complete API Documentation
|
|
164
|
+
|
|
165
|
+
### Declarative API (Recommended for Beginners)
|
|
166
|
+
|
|
167
|
+
| Property | Type | Description | Default |
|
|
168
|
+
| ------------- | ------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------- |
|
|
169
|
+
| `interval` | `number` | Typing interval (milliseconds) | `30` |
|
|
170
|
+
| `timerType` | `'setTimeout'` \| `'requestAnimationFrame'` | Timer type | Current default is `setTimeout`, will change to `requestAnimationFrame` later |
|
|
171
|
+
| `answerType` | `'thinking'` \| `'answer'` | Content type (affects style theme) | `'answer'` |
|
|
172
|
+
| `onEnd` | `(data: EndData) => void` | Typing completion callback | - |
|
|
173
|
+
| `onStart` | `(data: StartData) => void` | Typing start callback | - |
|
|
174
|
+
| `onTypedChar` | `(data: CharData) => void` | Per-character typing callback | - |
|
|
175
|
+
|
|
176
|
+
### Imperative API (Recommended for Streaming Scenarios)
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
|
|
180
|
+
|
|
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
|
+
| Method | Parameters | Description |
|
|
190
|
+
| ----------------- | ------------------------------------------- | ------------------------------------ |
|
|
191
|
+
| `push` | `(content: string, answerType: AnswerType)` | Add content and start typing |
|
|
192
|
+
| `clear` | - | Clear all content and state |
|
|
193
|
+
| `triggerWholeEnd` | - | Manually trigger completion callback |
|
|
194
|
+
| `flushBuffer` | `answerType?: AnswerType` | Force flush buffer content |
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## ποΈ Timer Mode Detailed Explanation
|
|
199
|
+
|
|
200
|
+
### `requestAnimationFrame` Mode π (Recommended)
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// π― Features
|
|
204
|
+
- Time-driven: Calculate character count based on actual elapsed time
|
|
205
|
+
- Batch processing: Can process multiple characters within a single frame
|
|
206
|
+
- Frame-synchronized: Synchronized with browser 60fps refresh rate
|
|
207
|
+
- High-frequency optimized: Perfect support for interval < 16ms high-speed typing
|
|
208
|
+
|
|
209
|
+
// π― Use Cases
|
|
210
|
+
- Default choice for modern web applications
|
|
211
|
+
- Pursuing smooth animation effects
|
|
212
|
+
- High-frequency typing (interval > 0 is sufficient)
|
|
213
|
+
- AI real-time conversation scenarios
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### `setTimeout` Mode π (Compatible)
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// π― Features
|
|
220
|
+
- Single character: Precisely processes one character at a time
|
|
221
|
+
- Fixed interval: Executes strictly according to set time
|
|
222
|
+
- Rhythmic feel: Classic typewriter rhythm
|
|
223
|
+
- Precise control: Suitable for specific timing requirements
|
|
224
|
+
|
|
225
|
+
// π― Use Cases
|
|
226
|
+
- Need precise time control
|
|
227
|
+
- Creating retro typewriter effects
|
|
228
|
+
- Scenarios with high compatibility requirements
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### π Performance Comparison
|
|
232
|
+
|
|
233
|
+
| Feature | requestAnimationFrame | setTimeout |
|
|
234
|
+
| --------------------------- | ----------------------------------------- | ------------------------------- |
|
|
235
|
+
| **Character Processing** | Can process multiple characters per frame | Process one character at a time |
|
|
236
|
+
| **High-frequency Interval** | β
Excellent (5ms β 3 chars per frame) | β May lag |
|
|
237
|
+
| **Low-frequency Interval** | β
Normal (100ms β 1 char after 6 frames) | β
Precise |
|
|
238
|
+
| **Visual Effect** | π¬ Smooth animation feel | β‘ Precise rhythmic feel |
|
|
239
|
+
| **Performance Overhead** | π’ Low (frame-synchronized) | π‘ Medium (timer) |
|
|
240
|
+
|
|
241
|
+
High-frequency recommends `requestAnimationFrame`, low-frequency recommends `setTimeout`
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## π‘ Practical Examples
|
|
246
|
+
|
|
247
|
+
### π AI Streaming Conversation
|
|
248
|
+
|
|
249
|
+
````tsx
|
|
250
|
+
import { useRef } from 'react';
|
|
251
|
+
import { MarkdownCMD, MarkdownRef } from 'ds-markdown';
|
|
252
|
+
|
|
253
|
+
function StreamingChat() {
|
|
254
|
+
const markdownRef = useRef<MarkdownRef>(null);
|
|
255
|
+
|
|
256
|
+
// Simulate AI streaming response
|
|
257
|
+
const simulateAIResponse = async () => {
|
|
258
|
+
markdownRef.current?.clear();
|
|
259
|
+
|
|
260
|
+
// Thinking phase
|
|
261
|
+
markdownRef.current?.push('π€ Analyzing your question...', 'thinking');
|
|
262
|
+
await delay(1000);
|
|
263
|
+
markdownRef.current?.push('\n\nβ
Analysis complete, starting answer', 'thinking');
|
|
264
|
+
|
|
265
|
+
// Streaming answer
|
|
266
|
+
const chunks = [
|
|
267
|
+
'# React 19 New Features Analysis\n\n',
|
|
268
|
+
'## π React Compiler\n',
|
|
269
|
+
'The biggest highlight of React 19 is the introduction of **React Compiler**:\n\n',
|
|
270
|
+
'- π― **Automatic Optimization**: No need for manual memo and useMemo\n',
|
|
271
|
+
'- β‘ **Performance Boost**: Compile-time optimization, zero runtime overhead\n',
|
|
272
|
+
'- π§ **Backward Compatible**: Existing code needs no modification\n\n',
|
|
273
|
+
'## π Actions Simplify Forms\n',
|
|
274
|
+
'The new Actions API makes form handling simpler:\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>Submit</button>\n',
|
|
282
|
+
' </form>\n',
|
|
283
|
+
' );\n',
|
|
284
|
+
'}\n',
|
|
285
|
+
'```\n\n',
|
|
286
|
+
'Hope this answer helps you! π',
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
for (const chunk of chunks) {
|
|
290
|
+
await delay(100);
|
|
291
|
+
markdownRef.current?.push(chunk, 'answer');
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
return (
|
|
296
|
+
<div className="chat-container">
|
|
297
|
+
<button onClick={simulateAIResponse}>π€ Ask About React 19 Features</button>
|
|
298
|
+
|
|
299
|
+
<MarkdownCMD ref={markdownRef} interval={10} timerType="requestAnimationFrame" onEnd={(data) => console.log('Paragraph completed:', data)} />
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
305
|
+
````
|
|
306
|
+
|
|
307
|
+
### π Streaming Markdown Syntax Processing
|
|
308
|
+
|
|
309
|
+
**Core Issue**: During streaming output, incomplete Markdown syntax can cause rendering errors
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
// π¨ Problem Scenario
|
|
313
|
+
push('#'); // "# "
|
|
314
|
+
push(' '); // "# "
|
|
315
|
+
push('Title'); // "# Title"
|
|
316
|
+
push('\n'); // "# Title\n"
|
|
317
|
+
push('1'); // "# Title\n1" β This will be misinterpreted as paragraph
|
|
318
|
+
push('.'); // "# Title\n1." β Forms correct list
|
|
319
|
+
push(' Item'); // "# Title\n1. Item"
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**β
Smart Solution**: Built-in synchronous buffering mechanism in component
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
// Component intelligently handles syntax boundaries
|
|
326
|
+
const handleStreamingMarkdown = () => {
|
|
327
|
+
const chunks = ['#', ' ', 'Using', 'Skills', '\n', '1', '.', ' ', 'Skill1', '\n', '2', '.', ' Skill2'];
|
|
328
|
+
|
|
329
|
+
chunks.forEach((chunk) => {
|
|
330
|
+
markdownRef.current?.push(chunk, 'answer');
|
|
331
|
+
// No delay needed, component buffers intelligently internally
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Optional: manually flush remaining buffer content
|
|
335
|
+
markdownRef.current?.flushBuffer();
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// π§ Smart Processing Flow:
|
|
339
|
+
// 1. Real-time detection of "# Using Skills\n1" incomplete syntax
|
|
340
|
+
// 2. Smart buffering, waiting for more characters
|
|
341
|
+
// 3. Process immediately after receiving "." to form "1."
|
|
342
|
+
// 4. Zero delay, pure synchronous processing
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Buffer Mechanism Features**:
|
|
346
|
+
|
|
347
|
+
- β‘ **Zero Delay**: No setTimeout, pure synchronous real-time processing
|
|
348
|
+
- π§ **Smart Boundaries**: Boundary detection based on Markdown syntax rules
|
|
349
|
+
- π **Real-time Splitting**: Process immediately when complete syntax is encountered, smart buffering when incomplete
|
|
350
|
+
- π‘οΈ **Safety Guarantee**: Provides `flushBuffer()` method to handle remaining content
|
|
351
|
+
|
|
352
|
+
**Supported Syntax Detection**:
|
|
353
|
+
|
|
354
|
+
````typescript
|
|
355
|
+
// β
Complete syntax (process immediately)
|
|
356
|
+
'## Title'; // Complete title
|
|
357
|
+
'1. List item'; // Complete list item
|
|
358
|
+
'- Item'; // Complete unordered list
|
|
359
|
+
'> Quote content'; // Complete quote
|
|
360
|
+
'```javascript'; // Code block start
|
|
361
|
+
'```'; // Code block end
|
|
362
|
+
'Content ending with newline\n'; // Newline boundary
|
|
363
|
+
|
|
364
|
+
// π Incomplete syntax (smart buffering)
|
|
365
|
+
'##'; // Only title symbols
|
|
366
|
+
'1'; // Only number
|
|
367
|
+
'```java'; // Possible code block start
|
|
368
|
+
````
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## π§ Best Practices
|
|
373
|
+
|
|
374
|
+
### 1. Performance Optimization
|
|
375
|
+
|
|
376
|
+
```tsx
|
|
377
|
+
// β
Recommended configuration
|
|
378
|
+
<DsMarkdown
|
|
379
|
+
timerType="requestAnimationFrame"
|
|
380
|
+
interval={15} // 15-30ms for optimal experience
|
|
381
|
+
/>
|
|
382
|
+
|
|
383
|
+
// β Avoid too small intervals
|
|
384
|
+
<DsMarkdown interval={1} /> // May cause performance issues
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### 2. Streaming Data Processing
|
|
388
|
+
|
|
389
|
+
```tsx
|
|
390
|
+
// β
Recommended: Imperative API
|
|
391
|
+
const ref = useRef<MarkdownRef>(null);
|
|
392
|
+
useEffect(() => {
|
|
393
|
+
ref.current?.push(newChunk, 'answer');
|
|
394
|
+
}, [newChunk]);
|
|
395
|
+
|
|
396
|
+
// β Avoid: Frequent children updates
|
|
397
|
+
const [content, setContent] = useState('');
|
|
398
|
+
// Each update will re-parse the entire content
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### 3. Type Safety
|
|
402
|
+
|
|
403
|
+
```tsx
|
|
404
|
+
import { MarkdownRef } from 'ds-markdown';
|
|
405
|
+
|
|
406
|
+
const ref = useRef<MarkdownRef>(null);
|
|
407
|
+
// Complete TypeScript type hints
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### 4. Style Customization
|
|
411
|
+
|
|
412
|
+
```css
|
|
413
|
+
/* Thinking area styles */
|
|
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
|
+
}
|
|
421
|
+
|
|
422
|
+
/* Answer area styles */
|
|
423
|
+
.ds-markdown-answer {
|
|
424
|
+
color: #333;
|
|
425
|
+
line-height: 1.6;
|
|
426
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* Code block styles */
|
|
430
|
+
.ds-markdown pre {
|
|
431
|
+
background: #f8f9fa;
|
|
432
|
+
border-radius: 8px;
|
|
433
|
+
padding: 16px;
|
|
434
|
+
overflow-x: auto;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/* Table styles */
|
|
438
|
+
.ds-markdown-table {
|
|
439
|
+
border-collapse: collapse;
|
|
440
|
+
width: 100%;
|
|
441
|
+
margin: 16px 0;
|
|
442
|
+
}
|
|
443
|
+
|
|
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
|
+
```
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## π Compatibility
|
|
455
|
+
|
|
456
|
+
| Environment | Version Requirement | Description |
|
|
457
|
+
| -------------- | ----------------------------------- | ------------------------- |
|
|
458
|
+
| **React** | 16.8.0+ | Requires Hooks support |
|
|
459
|
+
| **TypeScript** | 4.0+ | Optional, but recommended |
|
|
460
|
+
| **Browser** | Chrome 60+, Firefox 55+, Safari 12+ | Modern browsers |
|
|
461
|
+
| **Node.js** | 14.0+ | Build environment |
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## π€ Contributing Guide
|
|
466
|
+
|
|
467
|
+
Welcome to submit Issues and Pull Requests!
|
|
468
|
+
|
|
469
|
+
1. Fork this repository
|
|
470
|
+
2. Create feature branch: `git checkout -b feature/amazing-feature`
|
|
471
|
+
3. Commit changes: `git commit -m 'Add amazing feature'`
|
|
472
|
+
4. Push branch: `git push origin feature/amazing-feature`
|
|
473
|
+
5. Submit Pull Request
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## π License
|
|
478
|
+
|
|
479
|
+
MIT Β© [onshinpei](https://github.com/onshinpei)
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
<div align="center">
|
|
484
|
+
<strong>If this project helps you, please give it a βοΈ Star for support!</strong>
|
|
485
|
+
|
|
486
|
+
<br><br>
|
|
487
|
+
|
|
488
|
+
[π Report Issues](https://github.com/onshinpei/ds-markdown/issues) |
|
|
489
|
+
[π‘ Feature Suggestions](https://github.com/onshinpei/ds-markdown/issues) |
|
|
490
|
+
[π View Documentation](https://onshinpei.github.io/ds-markdown/)
|
|
491
|
+
</div>
|