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.ja.md
CHANGED
|
@@ -18,6 +18,57 @@
|
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
## 📋 目次
|
|
22
|
+
|
|
23
|
+
- [✨ コア機能](#-コア機能)
|
|
24
|
+
- [📦 クイックインストール](#-クイックインストール)
|
|
25
|
+
- [🚀 5分クイックスタート](#-5分クイックスタート)
|
|
26
|
+
- [基本的な使用法](#基本的な使用法)
|
|
27
|
+
- [タイピングアニメーションの無効化](#タイピングアニメーションの無効化)
|
|
28
|
+
- [数式サポート](#数式サポート)
|
|
29
|
+
- [AI 会話シナリオ](#ai-会話シナリオ)
|
|
30
|
+
- [🎯 高度なコールバック制御](#-高度なコールバック制御)
|
|
31
|
+
- [🔄 アニメーション再開デモ](#-アニメーション再開デモ)
|
|
32
|
+
- [▶️ 手動開始アニメーションデモ](#️-手動開始アニメーションデモ)
|
|
33
|
+
- [📚 完全 API ドキュメント](#-完全-api-ドキュメント)
|
|
34
|
+
- [🧮 数式使用ガイド](#-数式使用ガイド)
|
|
35
|
+
- [🔌 プラグインシステム](#-プラグインシステム)
|
|
36
|
+
- [🎛️ タイマーモード詳細](#️-タイマーモード詳細)
|
|
37
|
+
- [💡 実戦例](#-実戦例)
|
|
38
|
+
- [🔧 ベストプラクティス](#-ベストプラクティス)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## ❓ なぜ ds-markdown を使うのか?
|
|
43
|
+
|
|
44
|
+
- **AI チャット体験を完全再現**
|
|
45
|
+
DeepSeek などの主要な AI チャット UI のタイピングアニメーションとストリーミング応答を1:1で再現し、「AI が考え中/回答中」のリアルな体験を提供します。
|
|
46
|
+
|
|
47
|
+
- **バックエンドのストリーミングデータに完全対応**
|
|
48
|
+
多くの AI/LLM バックエンド(OpenAI、DeepSeek など)は、一度に複数文字を含む chunk を送信します。
|
|
49
|
+
**ds-markdown は各 chunk を自動的に1文字ずつ分割し、どんなにまとめて送られても滑らかに1文字ずつアニメーション表示します。**
|
|
50
|
+
|
|
51
|
+
- **Markdown & 数式完全対応**
|
|
52
|
+
KaTeX 内蔵、主要な Markdown 構文と数式をすべてサポート。技術 Q&A、教育、ナレッジベースに最適。
|
|
53
|
+
|
|
54
|
+
- **優れた開発体験**
|
|
55
|
+
豊富な命令型 API、ストリーミングデータ・非同期コールバック・プラグイン拡張に対応し、柔軟な制御が可能。
|
|
56
|
+
|
|
57
|
+
- **軽量・高性能**
|
|
58
|
+
小さなバンドルサイズ、高速、モバイル・デスクトップ両対応。コア依存は [react-markdown](https://github.com/remarkjs/react-markdown)(業界標準の成熟した Markdown レンダラー)のみで、他に重い依存はありません。すぐに使えます。
|
|
59
|
+
|
|
60
|
+
- **多テーマ・プラグインアーキテクチャ**
|
|
61
|
+
ライト/ダークテーマ切替、remark/rehype プラグイン互換、拡張性抜群。
|
|
62
|
+
|
|
63
|
+
- **幅広い用途**
|
|
64
|
+
- AI チャットボット/アシスタント
|
|
65
|
+
- リアルタイム Q&A/ナレッジベース
|
|
66
|
+
- 教育/数学/プログラミングコンテンツ
|
|
67
|
+
- プロダクトデモ、インタラクティブドキュメント
|
|
68
|
+
- 「タイプライター」アニメーションやストリーミング Markdown が必要なあらゆる場面
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
21
72
|
## ✨ コア機能
|
|
22
73
|
|
|
23
74
|
### 🤖 **AI 会話シナリオ**
|
|
@@ -33,11 +84,6 @@
|
|
|
33
84
|
- ライト/ダークテーマサポート、様々なプロダクトスタイルに対応
|
|
34
85
|
- remark/rehype プラグイン拡張をサポートするプラグインアーキテクチャ
|
|
35
86
|
|
|
36
|
-
### �� **開発者エクスペリエンス**
|
|
37
|
-
|
|
38
|
-
- タイピングの中断 `stop` と再開 `resume` をサポート
|
|
39
|
-
- タイピングの無効化と有効化をサポート
|
|
40
|
-
|
|
41
87
|
### 🎬 **滑らかなアニメーション**
|
|
42
88
|
|
|
43
89
|
- デュアルタイマーモード最適化、`requestAnimationFrame` と `setTimeout` モードをサポート
|
|
@@ -184,6 +230,195 @@ React 19 は多くのエキサイティングな新機能をもたらします
|
|
|
184
230
|
}
|
|
185
231
|
```
|
|
186
232
|
|
|
233
|
+
### 🎯 高度なコールバック制御
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
import { useRef } from 'react';
|
|
237
|
+
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
|
|
238
|
+
|
|
239
|
+
function AdvancedCallbackDemo() {
|
|
240
|
+
const markdownRef = useRef<MarkdownCMDRef>(null);
|
|
241
|
+
|
|
242
|
+
const handleStart = () => {
|
|
243
|
+
markdownRef.current?.start();
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const handleStop = () => {
|
|
247
|
+
markdownRef.current?.stop();
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const handleResume = () => {
|
|
251
|
+
markdownRef.current?.resume();
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const handleRestart = () => {
|
|
255
|
+
markdownRef.current?.restart();
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const handleEnd = (data: EndData) => {
|
|
259
|
+
console.log('セクション完了:', data);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<div>
|
|
264
|
+
<div style={{ marginBottom: '10px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
265
|
+
<button onClick={handleStart}>🚀 コンテンツ開始</button>
|
|
266
|
+
<button onClick={handleStop}>⏸️ 一時停止</button>
|
|
267
|
+
<button onClick={handleResume}>▶️ 再開</button>
|
|
268
|
+
<button onClick={handleRestart}>🔄 再開</button>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<MarkdownCMD ref={markdownRef} interval={20} onEnd={handleEnd} />
|
|
272
|
+
</div>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 🔄 アニメーション再開デモ
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
import { useRef, useState } from 'react';
|
|
281
|
+
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
|
|
282
|
+
|
|
283
|
+
function RestartDemo() {
|
|
284
|
+
const markdownRef = useRef<MarkdownCMDRef>(null);
|
|
285
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
286
|
+
const [hasStarted, setHasStarted] = useState(false);
|
|
287
|
+
|
|
288
|
+
const startContent = () => {
|
|
289
|
+
markdownRef.current?.clear();
|
|
290
|
+
markdownRef.current?.push(
|
|
291
|
+
'# アニメーション再開デモ\n\n' +
|
|
292
|
+
'この例は `restart()` メソッドの使用方法を示しています:\n\n' +
|
|
293
|
+
'- 🔄 **再開**:現在のコンテンツを最初から再生\n' +
|
|
294
|
+
'- ⏸️ **一時停止/再開**:いつでも一時停止と再開が可能\n' +
|
|
295
|
+
'- 🎯 **精密制御**:アニメーション再生状態の完全制御\n\n' +
|
|
296
|
+
'現在の状態:' +
|
|
297
|
+
(isPlaying ? '再生中' : '一時停止') +
|
|
298
|
+
'\n\n' +
|
|
299
|
+
'これは非常に実用的な機能です!',
|
|
300
|
+
'answer',
|
|
301
|
+
);
|
|
302
|
+
setIsPlaying(true);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const handleStart = () => {
|
|
306
|
+
if (hasStarted) {
|
|
307
|
+
// 既に開始されている場合は再開
|
|
308
|
+
markdownRef.current?.restart();
|
|
309
|
+
} else {
|
|
310
|
+
// 初回開始
|
|
311
|
+
markdownRef.current?.start();
|
|
312
|
+
setHasStarted(true);
|
|
313
|
+
}
|
|
314
|
+
setIsPlaying(true);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const handleStop = () => {
|
|
318
|
+
markdownRef.current?.stop();
|
|
319
|
+
setIsPlaying(false);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const handleResume = () => {
|
|
323
|
+
markdownRef.current?.resume();
|
|
324
|
+
setIsPlaying(true);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const handleRestart = () => {
|
|
328
|
+
markdownRef.current?.restart();
|
|
329
|
+
setIsPlaying(true);
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const handleEnd = () => {
|
|
333
|
+
setIsPlaying(false);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
return (
|
|
337
|
+
<div>
|
|
338
|
+
<div style={{ marginBottom: '10px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
339
|
+
<button onClick={startContent}>🚀 コンテンツ開始</button>
|
|
340
|
+
<button onClick={handleStart} disabled={isPlaying}>
|
|
341
|
+
{hasStarted ? '🔄 再開' : '▶️ 開始'}
|
|
342
|
+
</button>
|
|
343
|
+
<button onClick={handleStop} disabled={!isPlaying}>
|
|
344
|
+
⏸️ 一時停止
|
|
345
|
+
</button>
|
|
346
|
+
<button onClick={handleResume} disabled={isPlaying}>
|
|
347
|
+
▶️ 再開
|
|
348
|
+
</button>
|
|
349
|
+
<button onClick={handleRestart}>🔄 再開</button>
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
353
|
+
<strong>アニメーション状態:</strong> {isPlaying ? '🟢 再生中' : '🔴 一時停止'}
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<MarkdownCMD ref={markdownRef} interval={25} onEnd={handleEnd} />
|
|
357
|
+
</div>
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### ▶️ 手動開始アニメーションデモ
|
|
363
|
+
|
|
364
|
+
```tsx
|
|
365
|
+
import { useRef, useState } from 'react';
|
|
366
|
+
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
|
|
367
|
+
|
|
368
|
+
function StartDemo() {
|
|
369
|
+
const markdownRef = useRef<MarkdownCMDRef>(null);
|
|
370
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
371
|
+
const [hasStarted, setHasStarted] = useState(false);
|
|
372
|
+
|
|
373
|
+
const loadContent = () => {
|
|
374
|
+
markdownRef.current?.clear();
|
|
375
|
+
markdownRef.current?.push(
|
|
376
|
+
'# 手動開始アニメーションデモ\n\n' +
|
|
377
|
+
'この例は `start()` メソッドの使用方法を示しています:\n\n' +
|
|
378
|
+
'- 🎯 **手動制御**:`autoStartTyping=false` の場合、手動で `start()` を呼び出す必要があります\n' +
|
|
379
|
+
'- ⏱️ **遅延開始**:ユーザーインタラクション後にアニメーションを開始できます\n' +
|
|
380
|
+
'- 🎮 **ゲーミフィケーション**:ユーザーの積極性を必要とするシナリオに適しています\n\n' +
|
|
381
|
+
'"アニメーション開始"ボタンをクリックしてタイピング効果を手動でトリガーしてください!',
|
|
382
|
+
'answer',
|
|
383
|
+
);
|
|
384
|
+
setIsPlaying(false);
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const handleStart = () => {
|
|
388
|
+
if (hasStarted) {
|
|
389
|
+
// 既に開始されている場合は再開
|
|
390
|
+
markdownRef.current?.restart();
|
|
391
|
+
} else {
|
|
392
|
+
// 初回開始
|
|
393
|
+
markdownRef.current?.start();
|
|
394
|
+
setHasStarted(true);
|
|
395
|
+
}
|
|
396
|
+
setIsPlaying(true);
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const handleEnd = () => {
|
|
400
|
+
setIsPlaying(false);
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
return (
|
|
404
|
+
<div>
|
|
405
|
+
<div style={{ marginBottom: '10px', display: 'flex', gap: '10px', flexWrap: 'wrap' }}>
|
|
406
|
+
<button onClick={loadContent}>📝 コンテンツ読み込み</button>
|
|
407
|
+
<button onClick={handleStart} disabled={isPlaying}>
|
|
408
|
+
{hasStarted ? '🔄 再開' : '▶️ アニメーション開始'}
|
|
409
|
+
</button>
|
|
410
|
+
</div>
|
|
411
|
+
|
|
412
|
+
<div style={{ margin: '10px 0', padding: '10px', background: '#f5f5f5', borderRadius: '4px' }}>
|
|
413
|
+
<strong>状態:</strong> {isPlaying ? '🟢 アニメーション再生中' : '🔴 開始待機中'}
|
|
414
|
+
</div>
|
|
415
|
+
|
|
416
|
+
<MarkdownCMD ref={markdownRef} interval={30} autoStartTyping={false} onEnd={handleEnd} />
|
|
417
|
+
</div>
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
187
422
|
---
|
|
188
423
|
|
|
189
424
|
## 📚 完全 API ドキュメント
|
|
@@ -194,28 +429,43 @@ React 19 は多くのエキサイティングな新機能をもたらします
|
|
|
194
429
|
import DsMarkdown, { MarkdownCMD } from 'ds-markdown';
|
|
195
430
|
```
|
|
196
431
|
|
|
197
|
-
| プロパティ
|
|
198
|
-
|
|
|
199
|
-
| `interval`
|
|
200
|
-
| `timerType`
|
|
201
|
-
| `answerType`
|
|
202
|
-
| `theme`
|
|
203
|
-
| `plugins`
|
|
204
|
-
| `math`
|
|
205
|
-
| `onEnd`
|
|
206
|
-
| `onStart`
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
432
|
+
| プロパティ | 型 | 説明 | デフォルト |
|
|
433
|
+
| ------------------- | ------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
|
|
434
|
+
| `interval` | `number` | タイピング間隔(ミリ秒) | `30` |
|
|
435
|
+
| `timerType` | `'setTimeout'` \| `'requestAnimationFrame'` | タイマータイプ | 現在のデフォルトは`setTimeout`、後で`requestAnimationFrame`に変更予定 |
|
|
436
|
+
| `answerType` | `'thinking'` \| `'answer'` | コンテンツタイプ(スタイルに影響) | `'answer'` |
|
|
437
|
+
| `theme` | `'light'` \| `'dark'` | テーマタイプ | `'light'` |
|
|
438
|
+
| `plugins` | `IMarkdownPlugin[]` | プラグイン設定 | `[]` |
|
|
439
|
+
| `math` | [IMarkdownMath](#IMarkdownMath) | 数式設定 | `{ splitSymbol: 'dollar' }` |
|
|
440
|
+
| `onEnd` | `(data: EndData) => void` | タイピング完了コールバック | - |
|
|
441
|
+
| `onStart` | `(data: StartData) => void` | タイピング開始コールバック | - |
|
|
442
|
+
| `onBeforeTypedChar` | `(data: IBeforeTypedChar) => Promise<void>` | 文字タイピング前コールバック、非同期操作をサポート、後続のタイピングをブロック | - |
|
|
443
|
+
| `onTypedChar` | `(data: ITypedChar) => void` | 文字タイピング後コールバック | - |
|
|
444
|
+
| `disableTyping` | `boolean` | タイピングアニメーション効果を無効 | `false` |
|
|
445
|
+
| `autoStartTyping` | `boolean` | タイピングアニメーションを自動開始するかどうか、falseの場合は手動トリガー | `true` |
|
|
209
446
|
|
|
210
447
|
> 注意: タイピング中に `disableTyping` が `true` から `false` に変わると、次のタイピングトリガー時に残りの全文字が一度に表示されます。
|
|
211
448
|
|
|
449
|
+
### IBeforeTypedChar
|
|
450
|
+
|
|
451
|
+
| プロパティ | 型 | 説明 | デフォルト |
|
|
452
|
+
| -------------- | ------------ | ------------------------------------- | ---------- |
|
|
453
|
+
| `currentIndex` | `number` | 文字列全体での現在のインデックス | `0` |
|
|
454
|
+
| `currentChar` | `string` | タイピング予定の文字 | - |
|
|
455
|
+
| `answerType` | `AnswerType` | コンテンツタイプ(thinking/answer) | - |
|
|
456
|
+
| `prevStr` | `string` | 現在のタイプコンテンツの前の文字列 | - |
|
|
457
|
+
| `percent` | `number` | タイピング進行パーセンテージ(0-100) | `0` |
|
|
458
|
+
|
|
212
459
|
### ITypedChar
|
|
213
460
|
|
|
214
|
-
| プロパティ | 型
|
|
215
|
-
| -------------- |
|
|
216
|
-
| `
|
|
217
|
-
| `currentChar` | `string`
|
|
218
|
-
| `
|
|
461
|
+
| プロパティ | 型 | 説明 | デフォルト |
|
|
462
|
+
| -------------- | ------------ | ------------------------------------- | ---------- |
|
|
463
|
+
| `currentIndex` | `number` | 文字列全体での現在のインデックス | `0` |
|
|
464
|
+
| `currentChar` | `string` | タイピング済みの文字 | - |
|
|
465
|
+
| `answerType` | `AnswerType` | コンテンツタイプ(thinking/answer) | - |
|
|
466
|
+
| `prevStr` | `string` | 現在のタイプコンテンツの前の文字列 | - |
|
|
467
|
+
| `currentStr` | `string` | 現在のタイプコンテンツの完全な文字列 | - |
|
|
468
|
+
| `percent` | `number` | タイピング進行パーセンテージ(0-100) | `0` |
|
|
219
469
|
|
|
220
470
|
#### IMarkdownMath
|
|
221
471
|
|
|
@@ -241,26 +491,32 @@ import DsMarkdown, { MarkdownCMD } from 'ds-markdown';
|
|
|
241
491
|
|
|
242
492
|
#### デフォルトエクスポート DsMarkdown
|
|
243
493
|
|
|
244
|
-
| メソッド
|
|
245
|
-
|
|
|
246
|
-
| `
|
|
247
|
-
| `
|
|
494
|
+
| メソッド | パラメータ | 説明 |
|
|
495
|
+
| --------- | ---------- | -------------------------------------------------------------- |
|
|
496
|
+
| `start` | - | タイピングアニメーションを開始 |
|
|
497
|
+
| `stop` | - | タイピングを一時停止 |
|
|
498
|
+
| `resume` | - | タイピングを再開 |
|
|
499
|
+
| `restart` | - | タイピングアニメーションを再開、現在のコンテンツを最初から再生 |
|
|
248
500
|
|
|
249
501
|
#### MarkdownCMD 公開メソッド
|
|
250
502
|
|
|
251
|
-
| メソッド | パラメータ | 説明
|
|
252
|
-
| ----------------- | ------------------------------------------- |
|
|
253
|
-
| `push` | `(content: string, answerType: AnswerType)` | コンテンツを追加してタイピング開始
|
|
254
|
-
| `clear` | - | 全コンテンツと状態をクリア
|
|
255
|
-
| `triggerWholeEnd` | - | 手動で完了コールバックを発火
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
503
|
+
| メソッド | パラメータ | 説明 |
|
|
504
|
+
| ----------------- | ------------------------------------------- | -------------------------------------------------------------- |
|
|
505
|
+
| `push` | `(content: string, answerType: AnswerType)` | コンテンツを追加してタイピング開始 |
|
|
506
|
+
| `clear` | - | 全コンテンツと状態をクリア |
|
|
507
|
+
| `triggerWholeEnd` | - | 手動で完了コールバックを発火 |
|
|
508
|
+
| `start` | - | タイピングアニメーションを開始 |
|
|
509
|
+
| `stop` | - | タイピングを一時停止 |
|
|
510
|
+
| `resume` | - | タイピングを再開 |
|
|
511
|
+
| `restart` | - | タイピングアニメーションを再開、現在のコンテンツを最初から再生 |
|
|
258
512
|
|
|
259
513
|
**使用例:**
|
|
260
514
|
|
|
261
515
|
```tsx
|
|
516
|
+
markdownRef.current?.start(); // アニメーションを開始
|
|
262
517
|
markdownRef.current?.stop(); // アニメーションを一時停止
|
|
263
518
|
markdownRef.current?.resume(); // アニメーションを再開
|
|
519
|
+
markdownRef.current?.restart(); // アニメーションを再開
|
|
264
520
|
```
|
|
265
521
|
|
|
266
522
|
---
|