ds-markdown 0.1.4-beta.1 → 0.1.5

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.ja.md DELETED
@@ -1,942 +0,0 @@
1
- # ds-markdown
2
-
3
- > 🚀 高性能 React Markdown タイピングアニメーションコンポーネント、DeepSeek チャットインターフェース効果を完璧に再現
4
-
5
- **[🇨🇳 中文](./README.md) | [🇺🇸 English](./README.en.md) | 🇯🇵 日本語 | [🇰🇷 한국어](./README.ko.md)**
6
-
7
- 現代の AI アプリケーション向けに特別に設計された React コンポーネントで、滑らかなリアルタイムタイピングアニメーションと完全な Markdown レンダリング機能を提供します。
8
-
9
- [![npm version](https://img.shields.io/npm/v/ds-markdown)](https://www.npmjs.com/package/ds-markdown)
10
- [![npm downloads](https://img.shields.io/npm/dm/ds-markdown.svg)](https://www.npmjs.com/package/ds-markdown)
11
- [![bundle size](https://img.shields.io/bundlephobia/minzip/ds-markdown)](https://bundlephobia.com/package/ds-markdown)
12
- [![React](https://img.shields.io/badge/React-16.8+-blue)](https://react.dev)
13
- [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
14
-
15
- [📖 オンラインデモ](https://onshinpei.github.io/ds-markdown/)
16
-
17
- [DEMO:🔧 StackBlitz 体験](https://stackblitz.com/edit/vitejs-vite-ddfw8avb?file=src%2FApp.tsx)
18
-
19
- ---
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
-
72
- ## ✨ コア機能
73
-
74
- ### 🤖 **AI 会話シナリオ**
75
-
76
- - [DeepSeek ウェブサイト](https://chat.deepseek.com/) のチャット応答効果を1:1で再現
77
- - 思考プロセス(`thinking`)と回答内容(`answer`)の両方のモードをサポート
78
- - ストリーミングデータに完璧に対応、ユーザー入力への遅延ゼロ応答
79
-
80
- ### 📊 **コンテンツ表示シナリオ**
81
-
82
- - コードハイライト、テーブル、リストなどを含む完全な Markdown 構文サポート
83
- - 数式レンダリング (KaTeX)、`$...$` と `\[...\]` 構文をサポート
84
- - ライト/ダークテーマサポート、様々なプロダクトスタイルに対応
85
- - remark/rehype プラグイン拡張をサポートするプラグインアーキテクチャ
86
-
87
- ### 🎬 **滑らかなアニメーション**
88
-
89
- - デュアルタイマーモード最適化、`requestAnimationFrame` と `setTimeout` モードをサポート
90
- - 高頻度タイピングサポート(`requestAnimationFrame` モードでは `0ms` に近いタイピング間隔をサポート)
91
- - フレーム同期レンダリング、ブラウザのリフレッシュレートと完璧にマッチ
92
- - スマート文字バッチ処理により、より自然な視覚効果
93
-
94
- ---
95
-
96
- ## 📦 クイックインストール
97
-
98
- ```bash
99
- # npm
100
- npm install ds-markdown
101
-
102
- # yarn
103
- yarn add ds-markdown
104
-
105
- # pnpm
106
- pnpm add ds-markdown
107
- ```
108
-
109
- ### ESM CDN での使用
110
-
111
- インストール不要、ブラウザで直接使用できます:
112
-
113
- [DEMO](https://stackblitz.com/edit/stackblitz-starters-7vcclcw7?file=index.html)
114
-
115
- ```html
116
- <!-- スタイルのインポート、必須 -->
117
- <link rel="stylesheet" href="https://esm.sh/ds-markdown/dist/style.css" />
118
-
119
- <!-- katex数学公式スタイルのインポート、必要な場合のみ -->
120
- <link rel="stylesheet" href="https://esm.sh/ds-markdown/dist/katex.css" />
121
-
122
- <!-- コンポーネントのインポート -->
123
- <script type="module">
124
- import Markdown from 'https://esm.sh/ds-markdown';
125
- </script>
126
- ```
127
-
128
- ## 🚀 5分クイックスタート
129
-
130
- ### 基本的な使用法
131
-
132
- [DEMO](https://stackblitz.com/edit/vitejs-vite-z94syu8j?file=src%2FApp.tsx)
133
-
134
- ```tsx
135
- import DsMarkdown from 'ds-markdown';
136
- import 'ds-markdown/style.css';
137
-
138
- function App() {
139
- return (
140
- <DsMarkdown interval={20} answerType="answer">
141
- # Hello ds-markdown これは**高性能**なタイピングアニメーションコンポーネントです! ## 機能 - ⚡ ゼロ遅延ストリーミング処理 - 🎬 滑らかなタイピングアニメーション - 🎯 完璧な構文サポート
142
- </DsMarkdown>
143
- );
144
- }
145
- ```
146
-
147
- ### タイピングアニメーションの無効化
148
-
149
- ```tsx
150
- import DsMarkdown from 'ds-markdown';
151
- import 'ds-markdown/style.css';
152
-
153
- function StaticDemo() {
154
- const [disableTyping, setDisableTyping] = useState(false);
155
-
156
- return (
157
- <div>
158
- <button onClick={() => setDisableTyping(!disableTyping)}>{disableTyping ? '有効' : '無効'}にするタイピング効果</button>
159
-
160
- <DsMarkdown interval={20} answerType="answer" disableTyping={disableTyping}>
161
- # 静的表示モード `disableTyping` が `true` の場合、コンテンツはタイピングアニメーション効果なしで即座に全て表示されます。これは特定のシナリオで非常に有用です: - 📄 静的ドキュメント表示 - 🔄
162
- 表示モードの切り替え - ⚡ コンテンツの素早いプレビュー
163
- </DsMarkdown>
164
- </div>
165
- );
166
- }
167
- ```
168
-
169
- ### 数式サポート
170
-
171
- ```tsx
172
- import DsMarkdown from 'ds-markdown';
173
- // 数式を表示する必要がある場合は、数式変換プラグインを取り込む
174
- import { katexPlugin } from 'ds-markdown/plugins';
175
- import 'ds-markdown/style.css';
176
- // 数式を表示する必要がある場合は、数学公式スタイルを取り込む
177
- import 'ds-markdown/katex.css';
178
-
179
- function MathDemo() {
180
- return (
181
- <DsMarkdown interval={20} answerType="answer" plugins={[katexPlugin]} math={{ splitSymbol: 'dollar' }}>
182
- # ピタゴラスの定理 直角三角形では、斜辺の二乗は二つの直角辺の二乗の和に等しい: $a^2 + b^2 = c^2$ ここで: - $a$ と $b$ は直角辺 - $c$ は斜辺 古典的な「勾三股四弦五」の場合: $c = \sqrt
183
- {3 ^ (2 + 4) ^ 2} = \sqrt{25} = 5$
184
- </DsMarkdown>
185
- );
186
- }
187
- ```
188
-
189
- ### AI 会話シナリオ
190
-
191
- ```tsx
192
- function ChatDemo() {
193
- const [thinking, setThinking] = useState('');
194
- const [answer, setAnswer] = useState('');
195
-
196
- const handleAsk = () => {
197
- setThinking('🤔 あなたの質問について考えています...');
198
-
199
- setTimeout(() => {
200
- setAnswer(`# React 19 について
201
-
202
- React 19 は多くのエキサイティングな新機能をもたらします:
203
-
204
- ## 🚀 主要アップデート
205
- 1. **React Compiler** - 自動パフォーマンス最適化
206
- 2. **Actions** - フォーム処理の簡素化
207
- 3. **Document Metadata** - 内蔵 SEO サポート
208
-
209
- これらの新機能を一緒に探求しましょう!`);
210
- }, 2000);
211
- };
212
-
213
- return (
214
- <div>
215
- <button onClick={handleAsk}>AI に質問</button>
216
-
217
- {thinking && (
218
- <DsMarkdown answerType="thinking" interval={30}>
219
- {thinking}
220
- </DsMarkdown>
221
- )}
222
-
223
- {answer && (
224
- <DsMarkdown answerType="answer" interval={15}>
225
- {answer}
226
- </DsMarkdown>
227
- )}
228
- </div>
229
- );
230
- }
231
- ```
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
-
422
- ---
423
-
424
- ## 📚 完全 API ドキュメント
425
-
426
- ### デフォルトエクスポート DsMarkdown と MarkdownCMD の props
427
-
428
- ```js
429
- import DsMarkdown, { MarkdownCMD } from 'ds-markdown';
430
- ```
431
-
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` |
446
-
447
- > 注意: タイピング中に `disableTyping` が `true` から `false` に変わると、次のタイピングトリガー時に残りの全文字が一度に表示されます。
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
-
459
- ### ITypedChar
460
-
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` |
469
-
470
- #### IMarkdownMath
471
-
472
- | プロパティ | 型 | 説明 | デフォルト |
473
- | ------------- | ------------------------- | -------------------- | ---------- |
474
- | `splitSymbol` | `'dollar'` \| `'bracket'` | 数式区切り文字タイプ | `'dollar'` |
475
-
476
- **区切り文字の説明:**
477
-
478
- - `'dollar'`:`$...$` と `$$...$$` 構文を使用
479
- - `'bracket'`:`\(...\)` と `\[...\]` 構文を使用
480
-
481
- #### IMarkdownPlugin
482
-
483
- | プロパティ | 型 | 説明 | デフォルト |
484
- | -------------- | ------------------------- | ----------------- | ---------- |
485
- | `remarkPlugin` | `unknown` | remark プラグイン | - |
486
- | `rehypePlugin` | `unknown` | rehype プラグイン | - |
487
- | `type` | `'buildIn'` \| `'custom'` | プラグインタイプ | - |
488
- | `id` | `any` | プラグイン固有ID | - |
489
-
490
- ### コンポーネント公開メソッド
491
-
492
- #### デフォルトエクスポート DsMarkdown
493
-
494
- | メソッド | パラメータ | 説明 |
495
- | --------- | ---------- | -------------------------------------------------------------- |
496
- | `start` | - | タイピングアニメーションを開始 |
497
- | `stop` | - | タイピングを一時停止 |
498
- | `resume` | - | タイピングを再開 |
499
- | `restart` | - | タイピングアニメーションを再開、現在のコンテンツを最初から再生 |
500
-
501
- #### MarkdownCMD 公開メソッド
502
-
503
- | メソッド | パラメータ | 説明 |
504
- | ----------------- | ------------------------------------------- | -------------------------------------------------------------- |
505
- | `push` | `(content: string, answerType: AnswerType)` | コンテンツを追加してタイピング開始 |
506
- | `clear` | - | 全コンテンツと状態をクリア |
507
- | `triggerWholeEnd` | - | 手動で完了コールバックを発火 |
508
- | `start` | - | タイピングアニメーションを開始 |
509
- | `stop` | - | タイピングを一時停止 |
510
- | `resume` | - | タイピングを再開 |
511
- | `restart` | - | タイピングアニメーションを再開、現在のコンテンツを最初から再生 |
512
-
513
- **使用例:**
514
-
515
- ```tsx
516
- markdownRef.current?.start(); // アニメーションを開始
517
- markdownRef.current?.stop(); // アニメーションを一時停止
518
- markdownRef.current?.resume(); // アニメーションを再開
519
- markdownRef.current?.restart(); // アニメーションを再開
520
- ```
521
-
522
- ---
523
-
524
- ## 🧮 数式使用ガイド
525
-
526
- [DEMO1:ピタゴラスの定理](https://stackblitz.com/edit/vitejs-vite-z94syu8j?file=src%2FApp.tsx)
527
-
528
- [DEMO2:問題解答](https://stackblitz.com/edit/vitejs-vite-xk9lxagc?file=README.md)
529
-
530
- ### 基本構文
531
-
532
- ```tsx
533
- import { katexPlugin } from 'ds-markdown/plugins';
534
-
535
- // 1. 数式サポートを有効化
536
- <DsMarkdown plugins={[katexPlugin]}>
537
- # 数式の例
538
-
539
- // インライン数式
540
- これはインライン数式です:$E = mc^2$
541
-
542
- // ブロック数式
543
- $$\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}$$
544
- </DsMarkdown>
545
- ```
546
-
547
- ### 区切り文字の選択
548
-
549
- ```tsx
550
- // ドル記号区切り文字を使用(デフォルト)
551
- <DsMarkdown
552
- plugins={[katexPlugin]}
553
- math={{ splitSymbol: 'dollar' }}
554
- >
555
- インライン:$a + b = c$
556
- ブロック:$$\sum_{i=1}^{n} x_i = x_1 + x_2 + \cdots + x_n$$
557
- </DsMarkdown>
558
-
559
- // 括弧区切り文字を使用
560
- <DsMarkdown
561
- plugins={[katexPlugin]}
562
- math={{ splitSymbol: 'bracket' }}
563
- >
564
- インライン:\(a + b = c\)
565
- ブロック:\[\sum_{i=1}^{n} x_i = x_1 + x_2 + \cdots + x_n\]
566
- </DsMarkdown>
567
- ```
568
-
569
- ### ストリーミング数式
570
-
571
- ```tsx
572
- // ストリーミング出力での数式を完璧にサポート
573
- const mathContent = [
574
- 'ピタゴラスの定理:',
575
- '$a^2 + b^2 = c^2$',
576
- '\n\n',
577
- 'ここで:',
578
- '- $a$ と $b$ は直角辺\n',
579
- '- $c$ は斜辺\n\n',
580
- '古典的な「勾三股四弦五」の場合:\n',
581
- '$c = \\sqrt{3^2 + 4^2} = \\sqrt{25} = 5$\n\n',
582
- 'この定理は幾何学で広く応用されています!',
583
- ];
584
-
585
- mathContent.forEach((chunk) => {
586
- markdownRef.current?.push(chunk, 'answer');
587
- });
588
- ```
589
-
590
- ### スタイルカスタマイズ
591
-
592
- ```css
593
- /* 数式スタイルカスタマイズ */
594
- .katex {
595
- font-size: 1.1em;
596
- }
597
-
598
- .katex-display {
599
- margin: 1em 0;
600
- text-align: center;
601
- }
602
-
603
- /* ダークテーマ対応 */
604
- [data-theme='dark'] .katex {
605
- color: #e1e1e1;
606
- }
607
- ```
608
-
609
- ---
610
-
611
- ## 🔌 プラグインシステム
612
-
613
- ### 内蔵プラグイン
614
-
615
- #### KaTeX 数式プラグイン
616
-
617
- ```tsx
618
- import { katexPlugin } from 'ds-markdown/plugins';
619
-
620
- // 数式サポートを有効化
621
- <DsMarkdown plugins={[katexPlugin]}>数式:$E = mc^2$</DsMarkdown>;
622
- ```
623
-
624
- ### カスタムプラグイン
625
-
626
- ```tsx
627
- import { createBuildInPlugin } from 'ds-markdown/plugins';
628
-
629
- // カスタムプラグインを作成
630
- const customPlugin = createBuildInPlugin({
631
- remarkPlugin: yourRemarkPlugin,
632
- rehypePlugin: yourRehypePlugin,
633
- id: Symbol('custom-plugin'),
634
- });
635
-
636
- // カスタムプラグインを使用
637
- <DsMarkdown plugins={[katexPlugin, customPlugin]}>コンテンツ</DsMarkdown>;
638
- ```
639
-
640
- ---
641
-
642
- ## 🎛️ タイマーモード詳細
643
-
644
- ### `requestAnimationFrame` モード 🌟 (推奨)
645
-
646
- ```typescript
647
- // 🎯 特徴
648
- - 時間駆動:実際の経過時間に基づいて文字数を計算
649
- - バッチ処理:単一フレーム内で複数文字を処理可能
650
- - フレーム同期:ブラウザの60fpsリフレッシュレートと同期
651
- - 高頻度最適化:interval < 16msの高速タイピングを完璧にサポート
652
-
653
- // 🎯 適用シナリオ
654
- - モダンWebアプリケーションのデフォルト選択
655
- - 滑らかなアニメーション効果の追求
656
- - 高頻度タイピング (interval > 0 で十分)
657
- - AI リアルタイム会話シナリオ
658
- ```
659
-
660
- ### `setTimeout` モード 📟 (互換)
661
-
662
- ```typescript
663
- // 🎯 特徴
664
- - 単一文字:毎回正確に一文字を処理
665
- - 固定間隔:設定時間通りに厳密実行
666
- - リズム感:クラシックタイプライターのリズム感
667
- - 精密制御:特定のタイミング要件に適している
668
-
669
- // 🎯 適用シナリオ
670
- - 精密な時間制御が必要
671
- - レトロタイプライター効果の創出
672
- - 互換性要件が高いシナリオ
673
- ```
674
-
675
- ### 📊 パフォーマンス比較
676
-
677
- | 特徴 | requestAnimationFrame | setTimeout |
678
- | -------------------------------- | ----------------------------------- | -------------------- |
679
- | **文字処理** | フレームあたり複数文字 | 一度に一文字 |
680
- | **高頻度間隔** | ✅ 優秀 (5ms → フレームあたり3文字) | ❌ ラグ可能性あり |
681
- | **低頻度間隔** | ✅ 正常 (100ms → 6フレーム後1文字) | ✅ 精密 |
682
- | **視覚効果** | 🎬 滑らかなアニメーション感 | ⚡ 精密なリズム感 |
683
- | **パフォーマンスオーバーヘッド** | 🟢 低 (フレーム同期) | 🟡 中程度 (タイマー) |
684
-
685
- 高頻度は `requestAnimationFrame` 推奨、低頻度は `setTimeout` 推奨
686
-
687
- ---
688
-
689
- ## 💡 実戦例
690
-
691
- ### 📝 AI ストリーミング会話
692
-
693
- [DEMO: 🔧 StackBlitz 体験](https://stackblitz.com/edit/vitejs-vite-2ri8kex3?file=src%2FApp.tsx)
694
-
695
- ````tsx
696
- import { useRef } from 'react';
697
- import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
698
-
699
- function StreamingChat() {
700
- const markdownRef = useRef<MarkdownCMDRef>(null);
701
-
702
- // AI ストリーミング応答のシミュレート
703
- const simulateAIResponse = async () => {
704
- markdownRef.current?.clear();
705
-
706
- // 思考段階
707
- markdownRef.current?.push('🤔 あなたの質問を分析中...', 'thinking');
708
- await delay(1000);
709
- markdownRef.current?.push('\n\n✅ 分析完了、回答開始', 'thinking');
710
-
711
- // ストリーミング回答
712
- const chunks = [
713
- '# React 19 新機能解析\n\n',
714
- '## 🚀 React Compiler\n',
715
- 'React 19 の最大のハイライトは **React Compiler** の導入です:\n\n',
716
- '- 🎯 **自動最適化**:手動でのmemoやuseMemoが不要\n',
717
- '- ⚡ **パフォーマンス向上**:コンパイル時最適化、ランタイムゼロオーバーヘッド\n',
718
- '- 🔧 **後方互換性**:既存コードの修正不要\n\n',
719
- '## 📝 Actions でフォーム簡素化\n',
720
- '新しい Actions API でフォーム処理がより簡単に:\n\n',
721
- '```tsx\n',
722
- 'function ContactForm({ action }) {\n',
723
- ' const [state, formAction] = useActionState(action, null);\n',
724
- ' return (\n',
725
- ' <form action={formAction}>\n',
726
- ' <input name="email" type="email" />\n',
727
- ' <button>送信</button>\n',
728
- ' </form>\n',
729
- ' );\n',
730
- '}\n',
731
- '```\n\n',
732
- 'この回答がお役に立てれば幸いです!🎉',
733
- ];
734
-
735
- for (const chunk of chunks) {
736
- await delay(100);
737
- markdownRef.current?.push(chunk, 'answer');
738
- }
739
- };
740
-
741
- return (
742
- <div className="chat-container">
743
- <button onClick={simulateAIResponse}>🤖 React 19 新機能について質問</button>
744
-
745
- <MarkdownCMD ref={markdownRef} interval={10} timerType="requestAnimationFrame" onEnd={(data) => console.log('セクション完了:', data)} />
746
- </div>
747
- );
748
- }
749
-
750
- const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
751
- ````
752
-
753
- ### 🧮 数式ストリーミングレンダリング
754
-
755
- ```tsx
756
- import { katexPlugin } from 'ds-markdown/plugins';
757
-
758
- function MathStreamingDemo() {
759
- const markdownRef = useRef<MarkdownCMDRef>(null);
760
-
761
- const simulateMathResponse = async () => {
762
- markdownRef.current?.clear();
763
-
764
- const mathChunks = [
765
- '# ピタゴラスの定理詳解\n\n',
766
- '直角三角形では、斜辺の二乗は二つの直角辺の二乗の和に等しい:\n\n',
767
- '$a^2 + b^2 = c^2$\n\n',
768
- 'ここで:\n',
769
- '- $a$ と $b$ は直角辺\n',
770
- '- $c$ は斜辺\n\n',
771
- '古典的な「勾三股四弦五」の場合:\n',
772
- '$c = \\sqrt{3^2 + 4^2} = \\sqrt{25} = 5$\n\n',
773
- 'この定理は幾何学で広く応用されています!',
774
- ];
775
-
776
- for (const chunk of mathChunks) {
777
- await delay(150);
778
- markdownRef.current?.push(chunk, 'answer');
779
- }
780
- };
781
-
782
- return (
783
- <div>
784
- <button onClick={simulateMathResponse}>📐 ピタゴラスの定理を説明</button>
785
-
786
- <MarkdownCMD ref={markdownRef} interval={20} timerType="requestAnimationFrame" plugins={[katexPlugin]} math={{ splitSymbol: 'dollar' }} />
787
- </div>
788
- );
789
- }
790
- ```
791
-
792
- ## 🔧 ベストプラクティス
793
-
794
- ### 1. パフォーマンス最適化
795
-
796
- ```tsx
797
- // ✅ 推奨設定
798
- <DsMarkdown
799
- timerType="requestAnimationFrame"
800
- interval={15} // 15-30ms が最適な体験
801
- />
802
-
803
- // ❌ 過小な間隔は避ける
804
- <DsMarkdown interval={1} /> // パフォーマンス問題を引き起こす可能性
805
- ```
806
-
807
- ### 2. ストリーミングデータ処理
808
-
809
- ```tsx
810
- // ✅ 推奨:命令的 API
811
- const ref = useRef<MarkdownCMDRef>(null);
812
- useEffect(() => {
813
- ref.current?.push(newChunk, 'answer');
814
- }, [newChunk]);
815
-
816
- // ❌ 避ける:頻繁な children 更新
817
- const [content, setContent] = useState('');
818
- // 各更新で全コンテンツを再解析
819
- ```
820
-
821
- ### 3. 数式最適化
822
-
823
- ```tsx
824
- // ✅ 推奨:オンデマンドで数式スタイルを読み込み
825
- import 'ds-markdown/style.css';
826
- import 'ds-markdown/katex.css'; // 必要な時のみインポート
827
-
828
- // ✅ 推奨:適切な区切り文字の使用
829
- // シンプルな数式には $...$ がより簡潔
830
- // 複雑な数式には $$...$$ がより明確
831
-
832
- // ✅ 推奨:プラグイン設定
833
- import { katexPlugin } from 'ds-markdown/plugins';
834
- <DsMarkdown plugins={[katexPlugin]}>数式コンテンツ</DsMarkdown>;
835
- ```
836
-
837
- ### 4. 型安全性
838
-
839
- ```tsx
840
- import { MarkdownCMDRef } from 'ds-markdown';
841
-
842
- const ref = useRef<MarkdownCMDRef>(null);
843
- // 完全な TypeScript 型ヒント
844
- ```
845
-
846
- ### 5. スタイルカスタマイズ
847
-
848
- ```css
849
- /* 思考エリアスタイル */
850
- .ds-markdown-thinking {
851
- background: rgba(255, 193, 7, 0.1);
852
- border-left: 3px solid #ffc107;
853
- padding: 12px;
854
- border-radius: 6px;
855
- margin: 8px 0;
856
- }
857
-
858
- /* 回答エリアスタイル */
859
- .ds-markdown-answer {
860
- color: #333;
861
- line-height: 1.6;
862
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
863
- }
864
-
865
- /* コードブロックスタイル */
866
- .ds-markdown pre {
867
- background: #f8f9fa;
868
- border-radius: 8px;
869
- padding: 16px;
870
- overflow-x: auto;
871
- }
872
-
873
- /* テーブルスタイル */
874
- .ds-markdown-table {
875
- border-collapse: collapse;
876
- width: 100%;
877
- margin: 16px 0;
878
- }
879
-
880
- .ds-markdown-table th,
881
- .ds-markdown-table td {
882
- border: 1px solid #ddd;
883
- padding: 8px 12px;
884
- text-align: left;
885
- }
886
-
887
- /* 数式スタイル */
888
- .katex {
889
- font-size: 1.1em;
890
- }
891
-
892
- .katex-display {
893
- margin: 1em 0;
894
- text-align: center;
895
- }
896
-
897
- /* ダークテーマ数式 */
898
- [data-theme='dark'] .katex {
899
- color: #e1e1e1;
900
- }
901
- ```
902
-
903
- ---
904
-
905
- ## 🌐 互換性
906
-
907
- | 環境 | バージョン要件 | 説明 |
908
- | -------------- | ----------------------------------- | ------------------- |
909
- | **React** | 16.8.0+ | Hooksサポートが必要 |
910
- | **TypeScript** | 4.0+ | オプションだが推奨 |
911
- | **ブラウザ** | Chrome 60+, Firefox 55+, Safari 12+ | モダンブラウザ |
912
- | **Node.js** | 14.0+ | ビルド環境 |
913
-
914
- ---
915
-
916
- ## 🤝 貢献ガイド
917
-
918
- Issue と Pull Request の提出を歓迎します!
919
-
920
- 1. このリポジトリをFork
921
- 2. 機能ブランチを作成:`git checkout -b feature/amazing-feature`
922
- 3. 変更をコミット:`git commit -m 'Add amazing feature'`
923
- 4. ブランチをプッシュ:`git push origin feature/amazing-feature`
924
- 5. Pull Request を提出
925
-
926
- ---
927
-
928
- ## 📄 ライセンス
929
-
930
- MIT © [onshinpei](https://github.com/onshinpei)
931
-
932
- ---
933
-
934
- <div align="center">
935
- <strong>このプロジェクトがお役に立てば、⭐️ Star をお願いします!</strong>
936
-
937
- <br><br>
938
-
939
- [🐛 問題報告](https://github.com/onshinpei/ds-markdown/issues) |
940
- [💡 機能提案](https://github.com/onshinpei/ds-markdown/issues) |
941
- [📖 ドキュメント閲覧](https://onshinpei.github.io/ds-markdown/)
942
- </div>