mdv-live 0.5.4 → 0.5.8
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/CHANGELOG.md +127 -0
- package/README.md +154 -23
- package/bin/mdv.js +141 -81
- package/package.json +1 -1
- package/src/api/marpNote/guards.js +79 -0
- package/src/api/marpNote/handleGet.js +65 -0
- package/src/api/marpNote/handlePut.js +162 -0
- package/src/api/marpNote/readDeck.js +42 -0
- package/src/api/marpNote.js +40 -0
- package/src/api/pdf.js +65 -8
- package/src/concurrency/pathLock.js +39 -0
- package/src/rendering/index.js +9 -1
- package/src/rendering/markdown.js +24 -21
- package/src/rendering/marp.js +11 -32
- package/src/rendering/marpNoteWriter.js +156 -0
- package/src/rendering/marpitAdapter.js +139 -0
- package/src/server.js +29 -4
- package/src/static/app.js +369 -22
- package/src/static/index.html +24 -0
- package/src/static/lib/apiClient.js +73 -0
- package/src/static/lib/presenterChannel.js +33 -0
- package/src/static/lib/saveQueue.js +71 -0
- package/src/static/lib/tabRegistry.js +32 -0
- package/src/static/presenter.html +687 -0
- package/src/static/styles.css +34 -0
- package/src/styles/index.js +90 -0
- package/src/styles/report.example.css +201 -0
- package/src/styles/report.pdf-options.example.json +10 -0
- package/src/utils/atomicWrite.js +159 -0
- package/src/utils/errors.js +50 -0
- package/src/utils/etag.js +11 -0
- package/src/utils/lineMath.js +86 -0
- package/src/rendering/slides.js +0 -152
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,133 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.8] - 2026-05-08
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Symlink TOCTOU on note auto-save** (codex-loop で 4 round 連鎖修正):
|
|
13
|
+
- 旧コードの TOCTOU guard が `earlyDeck.realPath` (lock 取得前) と比較
|
|
14
|
+
していた → 進入後 swap、戻し、書き込みで別ファイル読み出しが original
|
|
15
|
+
path に書ける race を塞ぐため `deck.realPath` (in-lock) と比較に修正
|
|
16
|
+
- in-lock で realpath が変わったら mutex 範囲外の書込みになる →
|
|
17
|
+
detection を入れて再 lock 取得
|
|
18
|
+
- 再 lock 取得を server-side 自動 retry で実装 (client は STALE 以外を
|
|
19
|
+
terminal 扱いするため)
|
|
20
|
+
- retarget retry の入れ子 lock が opposite retarget で deadlock し得る
|
|
21
|
+
→ trampoline で **outer lock 解放後に新 realpath を取得**
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- TOCTOU 正常系の API regression test
|
|
26
|
+
- SaveQueue coalesce/serialize/dropPath/例外耐性 5 件
|
|
27
|
+
- Sec-Fetch-Site=same-origin の B 受理パス + cross-site 拒否
|
|
28
|
+
|
|
29
|
+
### Architecture (refactor)
|
|
30
|
+
|
|
31
|
+
- `src/api/marpNote.js` orchestration を 38 行に。実装は `src/api/marpNote/`
|
|
32
|
+
配下の `guards.js` / `readDeck.js` / `handleGet.js` / `handlePut.js` に分割
|
|
33
|
+
- `src/static/lib/saveQueue.js` (per-deck queue + per-slide coalesce、純 JS)
|
|
34
|
+
- `src/static/lib/tabRegistry.js` (tab close hook → メモリリーク解消)
|
|
35
|
+
- `src/static/lib/apiClient.js` を deck/file/tree/info/pdf 用に拡張、
|
|
36
|
+
app.js の fetch 直叩きを 13 → 2 (WebSocket / /raw/ のみ残存)
|
|
37
|
+
- `src/concurrency/pathLock.js`: promise-chain ベースの正しい mutex
|
|
38
|
+
(旧 naive Map 実装の thundering-herd race を排除)
|
|
39
|
+
- `src/utils/errors.js`: mkError + ERROR_STATUS テーブル + sendError SSOT
|
|
40
|
+
- `src/utils/etag.js`: ETag 計算 SSOT
|
|
41
|
+
- placeholder を CSS pseudo-element 化 (`:empty::before`) で
|
|
42
|
+
contenteditable に placeholder text が混入する罠を構造的に解消
|
|
43
|
+
- STALE 通知時に編集テキストを localStorage に自動退避
|
|
44
|
+
|
|
45
|
+
### Tests
|
|
46
|
+
|
|
47
|
+
- 222 → **236 件 (+14)**、全 PASS
|
|
48
|
+
|
|
49
|
+
## [0.5.7] - 2026-05-08
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- **Presenter View** (Marp スピーカーノート別ウィンドウ表示・編集)
|
|
54
|
+
- P キー (Cmd/Ctrl 修飾なし) または Marp ナビボタンで起動
|
|
55
|
+
- Current / Next スライド + Speaker Notes + 経過タイマーを並列表示
|
|
56
|
+
- パネルサイズはドラッグハンドルで変更可能 (localStorage 永続化)
|
|
57
|
+
- BroadcastChannel `mdv-marp-presenter` でメイン⇄presenter 双方向同期
|
|
58
|
+
- **スピーカーノートの自動保存**: presenter ノートパネルをクリック→編集→
|
|
59
|
+
800ms デバウンスでサーバへ PUT。ソース markdown の HTML コメントを書き換え
|
|
60
|
+
- **`/api/marp/decks/:path` エンドポイント** (GET/PUT/OPTIONS)
|
|
61
|
+
- **ETag 楽観ロック** (`sha256:`) で外部編集との衝突検出
|
|
62
|
+
- **per-path 非同期 mutex** で同時 PUT を直列化
|
|
63
|
+
- **Multi-note Guard**: 1 slide に複数ノートがある場合は read-only
|
|
64
|
+
- **CSRF**: Origin + Sec-Fetch-Site + Content-Type 厳密検証
|
|
65
|
+
- **PNA preflight 拒否** (localhost 同一オリジン要求)
|
|
66
|
+
- **128KB body limit + 専用 413 ハンドラ** で情報漏洩防止
|
|
67
|
+
|
|
68
|
+
### Architecture
|
|
69
|
+
|
|
70
|
+
- Marp スライド範囲・ノート位置の特定を **Marpit token** に委譲する
|
|
71
|
+
`marpitAdapter` を新設。regex 再実装の脆弱性を構造的に解消
|
|
72
|
+
- `validatePathReal` + `O_NOFOLLOW` + realpath 二重解決で symlink swap
|
|
73
|
+
best-effort 防御
|
|
74
|
+
- `atomicWrite` で `O_EXCL` temp + chmod EPERM 限定 + EXDEV 二段 rename +
|
|
75
|
+
uid+mtime sweep
|
|
76
|
+
- BOM/CRLF/CR/UTF-8 surrogate pair 安全な行↔バイト変換ヘルパに集約
|
|
77
|
+
- 共通 error コード/HTTP status マッピングを `utils/errors.js` に SSOT 化
|
|
78
|
+
- promise-chain ベースの正しい mutex (`concurrency/pathLock.js`) で
|
|
79
|
+
thundering-herd race を排除
|
|
80
|
+
- HTTP client / BroadcastChannel 名 / message schema を専用ライブラリに分離
|
|
81
|
+
- セキュリティ脆弱性 5 件 (basic-ftp / ip-address / postcss) を `npm audit fix`
|
|
82
|
+
|
|
83
|
+
### Tests
|
|
84
|
+
|
|
85
|
+
- 既存 119 → **228 件 (+109)** すべて PASS
|
|
86
|
+
- 性能: 500 slides / 155 KiB ファイルで parseDeck+rewrite 86ms
|
|
87
|
+
|
|
88
|
+
## [0.5.6] - 2026-04-27
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
|
|
92
|
+
- Markdown PDF変換用の `mdv convert` サブコマンドを追加
|
|
93
|
+
- `-s <css-file>` によるPDF変換用CSS指定を追加
|
|
94
|
+
- `--pdf-options <json-file>` によるPuppeteer PDF options指定を追加
|
|
95
|
+
- Web UIのStyleパネルを追加
|
|
96
|
+
- CSSファイルパスを指定可能
|
|
97
|
+
- PDF options JSONファイルパスを指定可能
|
|
98
|
+
- 指定CSSをMarkdownプレビューに反映
|
|
99
|
+
- `Clear` でスタイル指定を解除可能
|
|
100
|
+
- 通常MarkdownのWeb UI PDF exportを `md-to-pdf` に対応
|
|
101
|
+
- PDFスタイル指定のサンプルを追加
|
|
102
|
+
- `src/styles/report.example.css`
|
|
103
|
+
- `src/styles/report.pdf-options.example.json`
|
|
104
|
+
|
|
105
|
+
### Changed
|
|
106
|
+
|
|
107
|
+
- PDF出力設定をCSSとPDF options JSONに分離
|
|
108
|
+
- Marp PDF出力は従来どおりMarp CLIを使用し、通常Markdown PDF出力のみ `md-to-pdf` を使用
|
|
109
|
+
|
|
110
|
+
## [0.5.5] - 2026-04-05
|
|
111
|
+
|
|
112
|
+
### Fixed
|
|
113
|
+
|
|
114
|
+
- タスクリストのインライン要素(太字・リンク・コード)が二重表示されるバグを修正
|
|
115
|
+
- markdown-it-task-lists の labelAfter オプション誤用が原因
|
|
116
|
+
- Mermaidプレースホルダがユーザーコンテンツと衝突する問題を修正(nonce付与)
|
|
117
|
+
- 空frontmatter(`---\n\n---`)で空のyamlコードブロックが生成される問題を修正
|
|
118
|
+
|
|
119
|
+
### Removed
|
|
120
|
+
|
|
121
|
+
- 未使用の `src/rendering/slides.js` を削除(marp.jsに統合済み)
|
|
122
|
+
|
|
123
|
+
### Added
|
|
124
|
+
|
|
125
|
+
- WebSocketテスト7件(接続追跡・watch・broadcast・通知・cleanup・不正入力耐性)
|
|
126
|
+
- レンダリングテスト10件(strikethrough・CJK emphasis・linkify・breaks・mermaid edge cases)
|
|
127
|
+
- テスト総数: 92 → 109
|
|
128
|
+
|
|
129
|
+
## [0.5.4] - 2026-04-04
|
|
130
|
+
|
|
131
|
+
### Fixed
|
|
132
|
+
|
|
133
|
+
- 4件の依存関係脆弱性を修正
|
|
134
|
+
|
|
8
135
|
## [0.5.3] - 2026-03-29
|
|
9
136
|
|
|
10
137
|
### Fixed
|
package/README.md
CHANGED
|
@@ -10,16 +10,18 @@
|
|
|
10
10
|
- 📁 左側にフォルダツリー表示(遅延読み込み対応)
|
|
11
11
|
- 📄 Markdownをリアルタイムレンダリング
|
|
12
12
|
- 🎬 **Marp完全対応**(公式テーマ・ディレクティブ・数式)
|
|
13
|
+
- 🎤 **Presenter View**(スピーカーノート別ウィンドウ・自動保存・タイマー) — `P` キーで起動
|
|
13
14
|
- 🔄 ファイル更新時に自動リロード(WebSocket)
|
|
14
15
|
- 🎨 シンタックスハイライト(highlight.js)
|
|
15
16
|
- 📊 Mermaid図のレンダリング
|
|
16
17
|
- 🌙 ダーク/ライトテーマ切り替え
|
|
17
18
|
- ✏️ インラインエディタ(Cmd+E)
|
|
18
19
|
- ✅ タスクリスト(チェックボックス)対応
|
|
19
|
-
- 📥 PDF出力(Cmd+P)
|
|
20
|
+
- 📥 PDF出力(Cmd+P / CLI convert)
|
|
21
|
+
- 🎛️ PDF用CSS・PDF options指定(CLI / Web UI)
|
|
20
22
|
- 🎬 動画/音声ストリーミング再生(Range Request対応)
|
|
21
23
|
- 📤 ファイルアップロード(ドラッグ&ドロップ)
|
|
22
|
-
- 🔒
|
|
24
|
+
- 🔒 セキュリティ強化(パストラバーサル防止 + ETag 楽観ロック + CSRF 防御)
|
|
23
25
|
|
|
24
26
|
## Installation
|
|
25
27
|
|
|
@@ -59,13 +61,59 @@ mdv -k 12345
|
|
|
59
61
|
mdv -k -a
|
|
60
62
|
|
|
61
63
|
# PDFに変換
|
|
62
|
-
mdv
|
|
63
|
-
|
|
64
|
+
mdv convert -i report.md -o report.pdf
|
|
65
|
+
|
|
66
|
+
# PDFに変換(CSSとPDF optionsを指定)
|
|
67
|
+
mdv convert \
|
|
68
|
+
-i report.md \
|
|
69
|
+
-o report.pdf \
|
|
70
|
+
-s ./src/styles/report.example.css \
|
|
71
|
+
--pdf-options ./src/styles/report.pdf-options.example.json
|
|
64
72
|
|
|
65
73
|
# バージョン表示
|
|
66
74
|
mdv -v
|
|
67
75
|
```
|
|
68
76
|
|
|
77
|
+
## PDF Export
|
|
78
|
+
|
|
79
|
+
Markdown ファイルは CLI または Web UI から PDF に変換できます。
|
|
80
|
+
|
|
81
|
+
### CLI
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
mdv convert -i input.md -o output.pdf
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
CSS を指定する場合は `-s` に CSS ファイルパスを渡します。
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
mdv convert \
|
|
91
|
+
-i input.md \
|
|
92
|
+
-o output.pdf \
|
|
93
|
+
-s ./src/styles/report.example.css
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`printBackground` や余白などの PDF 生成オプションは、CSS と分離して JSON ファイルで指定できます。
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
mdv convert \
|
|
100
|
+
-i input.md \
|
|
101
|
+
-o output.pdf \
|
|
102
|
+
-s ./src/styles/report.example.css \
|
|
103
|
+
--pdf-options ./src/styles/report.pdf-options.example.json
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`src/styles/report.example.css` と `src/styles/report.pdf-options.example.json` はサンプルです。必要に応じて任意の CSS / JSON ファイルを指定してください。
|
|
107
|
+
|
|
108
|
+
### Web UI
|
|
109
|
+
|
|
110
|
+
ビューア上部の `Style` から以下を指定できます。
|
|
111
|
+
|
|
112
|
+
- CSS ファイルパス
|
|
113
|
+
- PDF options JSON ファイルパス
|
|
114
|
+
|
|
115
|
+
CSS は Markdown プレビューにも反映されます。指定を解除する場合は `Clear` を押してください。
|
|
116
|
+
|
|
69
117
|
### ポート自動増分
|
|
70
118
|
|
|
71
119
|
ポートが使用中の場合、自動的に次のポート番号を試します。
|
|
@@ -118,6 +166,8 @@ paginate: true
|
|
|
118
166
|
|
|
119
167
|
内容...
|
|
120
168
|
|
|
169
|
+
<!-- スピーカーノート (Presenter View で表示・編集できます) -->
|
|
170
|
+
|
|
121
171
|
---
|
|
122
172
|
|
|
123
173
|
# 次のスライド
|
|
@@ -129,9 +179,55 @@ paginate: true
|
|
|
129
179
|
### サポートされるMarp機能
|
|
130
180
|
|
|
131
181
|
- **テーマ**: default, gaia, uncover
|
|
132
|
-
- **ディレクティブ**: paginate, header, footer, backgroundColor, etc.
|
|
182
|
+
- **ディレクティブ**: paginate, header, footer, backgroundColor, lang, headingDivider, etc.
|
|
183
|
+
- **headingDivider**: scalar (`headingDivider: 2`) / inline-array (`[1, 2]`) / block-array 全形式
|
|
184
|
+
- **スライド区切り**: `---` / `***` / `___` (CommonMark thematic break 全形式)
|
|
133
185
|
- **画像構文**: `![bg]`, `![w:100px]`, `![bg left]`
|
|
134
186
|
- **数式**: KaTeX対応(インライン `$...$`、ブロック `$$...$$`)
|
|
187
|
+
- **スピーカーノート**: HTML コメント (`<!-- ... -->`) で記述
|
|
188
|
+
|
|
189
|
+
## Presenter View
|
|
190
|
+
|
|
191
|
+
Marp ファイルを開いた状態で **`P` キー** を押すと、別ウィンドウで登壇者ビューが起動します。
|
|
192
|
+
|
|
193
|
+
### 機能
|
|
194
|
+
|
|
195
|
+
- **3 ペインレイアウト**: 現在のスライド (大) / 次のスライド (小) / スピーカーノート
|
|
196
|
+
- **経過タイマー**: 上部に MM:SS 表示、Reset ボタンで 0 にリセット
|
|
197
|
+
- **ノート編集 → 自動保存**: ノートパネルをクリックして編集 → 800ms デバウンスで markdown ソースのコメントを書き戻し
|
|
198
|
+
- **キーボードナビ**: ← / → でスライド移動、メイン画面と双方向同期
|
|
199
|
+
- **レイアウト調整**: ペイン境界をドラッグで自由に変更、ダブルクリックでデフォルト復元 (localStorage 永続化)
|
|
200
|
+
- **Multi-note Guard**: 1 スライドに複数のノートコメントがある場合は自動保存を無効化(先頭ノート消失防止)
|
|
201
|
+
- **STALE 検出**: 外部エディタによる変更を ETag 楽観ロックで検出、編集中テキストを localStorage に自動退避
|
|
202
|
+
|
|
203
|
+
### スピーカーノートの書き方
|
|
204
|
+
|
|
205
|
+
```markdown
|
|
206
|
+
# スライドタイトル
|
|
207
|
+
|
|
208
|
+
スライドの本文
|
|
209
|
+
|
|
210
|
+
<!-- ここがスピーカーノート。Presenter View で編集すると
|
|
211
|
+
このコメントが書き換わります。 -->
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
複数行のノートも OK:
|
|
215
|
+
|
|
216
|
+
```markdown
|
|
217
|
+
<!--
|
|
218
|
+
- ポイント 1: 〜を強調する
|
|
219
|
+
- ポイント 2: ここで質問を投げかける
|
|
220
|
+
- 想定時間: 2 分
|
|
221
|
+
-->
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Presenter View ショートカット
|
|
225
|
+
|
|
226
|
+
| キー | 動作 |
|
|
227
|
+
|---|---|
|
|
228
|
+
| `← / →` | スライド移動 |
|
|
229
|
+
| `Space / PageDown` | 次のスライド |
|
|
230
|
+
| `Home / End` | 最初 / 最後のスライド |
|
|
135
231
|
|
|
136
232
|
## Keyboard Shortcuts
|
|
137
233
|
|
|
@@ -143,6 +239,10 @@ paginate: true
|
|
|
143
239
|
| Cmd/Ctrl + P | PDF出力 |
|
|
144
240
|
| Cmd/Ctrl + W | タブを閉じる |
|
|
145
241
|
| ← / → | スライド移動(Marp時) |
|
|
242
|
+
| F | フルスクリーン切替(Marp時) |
|
|
243
|
+
| N | ナビバー表示切替(Marp時) |
|
|
244
|
+
| **P** | **Presenter View 起動(Marp時)** |
|
|
245
|
+
| Esc | フルスクリーン解除 |
|
|
146
246
|
| F2 | ファイル名変更 |
|
|
147
247
|
| Delete | ファイル削除 |
|
|
148
248
|
|
|
@@ -150,7 +250,7 @@ paginate: true
|
|
|
150
250
|
|
|
151
251
|
| Endpoint | Method | Description |
|
|
152
252
|
|----------|--------|-------------|
|
|
153
|
-
| `/api/file` | GET | ファイル内容取得 |
|
|
253
|
+
| `/api/file` | GET | ファイル内容取得 (Marp 時は etag/notes/notesMultiplicity も同梱) |
|
|
154
254
|
| `/api/file` | POST | ファイル保存 |
|
|
155
255
|
| `/api/file` | DELETE | ファイル/ディレクトリ削除 |
|
|
156
256
|
| `/api/tree` | GET | ファイルツリー取得 |
|
|
@@ -159,7 +259,12 @@ paginate: true
|
|
|
159
259
|
| `/api/move` | POST | ファイル移動/リネーム |
|
|
160
260
|
| `/api/download` | GET | ファイルダウンロード |
|
|
161
261
|
| `/api/upload` | POST | ファイルアップロード |
|
|
262
|
+
| `/api/pdf/export` | POST | PDF出力 |
|
|
162
263
|
| `/api/info` | GET | サーバー情報 |
|
|
264
|
+
| `/api/marp/decks/:path` | GET | Marp デッキ情報取得 (etag, notes, notesMultiplicity) |
|
|
265
|
+
| `/api/marp/decks/:path/slides/:N/note` | PUT | スピーカーノート更新 (`If-Match` 必須、ETag 楽観ロック) |
|
|
266
|
+
|
|
267
|
+
`/api/marp/decks/*` は Origin / Sec-Fetch-Site / Content-Type を厳密に検証し、cross-origin / cross-site / non-JSON リクエストは `403 ORIGIN_REJECTED` または `415 UNSUPPORTED_MEDIA_TYPE` で拒否します(CSRF / DNS rebinding 防御)。
|
|
163
268
|
|
|
164
269
|
## Tech Stack
|
|
165
270
|
|
|
@@ -192,28 +297,54 @@ npm test
|
|
|
192
297
|
|
|
193
298
|
```
|
|
194
299
|
mdv/
|
|
195
|
-
├── bin/mdv.js
|
|
300
|
+
├── bin/mdv.js # CLI entry point
|
|
196
301
|
├── src/
|
|
197
|
-
│ ├── server.js
|
|
198
|
-
│ ├── watcher.js
|
|
302
|
+
│ ├── server.js # Express server setup
|
|
303
|
+
│ ├── watcher.js # File watching (chokidar)
|
|
304
|
+
│ ├── websocket.js # WebSocket setup
|
|
199
305
|
│ ├── api/
|
|
200
|
-
│ │ ├── file.js
|
|
201
|
-
│ │ ├──
|
|
202
|
-
│ │
|
|
306
|
+
│ │ ├── file.js # File operations API
|
|
307
|
+
│ │ ├── pdf.js # PDF export API
|
|
308
|
+
│ │ ├── tree.js # File tree API
|
|
309
|
+
│ │ ├── upload.js # Upload API
|
|
310
|
+
│ │ ├── marpNote.js # Marp note autosave routes (orchestration)
|
|
311
|
+
│ │ └── marpNote/
|
|
312
|
+
│ │ ├── guards.js # Origin / Host / Content-Type / If-Match guards
|
|
313
|
+
│ │ ├── readDeck.js # Path-safe deck reader (O_NOFOLLOW + realpath)
|
|
314
|
+
│ │ ├── handleGet.js # GET /api/marp/decks/:path
|
|
315
|
+
│ │ └── handlePut.js # PUT /api/marp/decks/:path/slides/:N/note
|
|
203
316
|
│ ├── rendering/
|
|
204
|
-
│ │ ├── index.js
|
|
205
|
-
│ │ ├── markdown.js
|
|
206
|
-
│ │
|
|
317
|
+
│ │ ├── index.js # Rendering entry
|
|
318
|
+
│ │ ├── markdown.js # Markdown rendering
|
|
319
|
+
│ │ ├── marp.js # Marp rendering (delegates to adapter)
|
|
320
|
+
│ │ ├── marpitAdapter.js # Marpit token adapter (SSOT)
|
|
321
|
+
│ │ └── marpNoteWriter.js # Pure-function note splice
|
|
322
|
+
│ ├── concurrency/
|
|
323
|
+
│ │ └── pathLock.js # Promise-chain mutex (per-path serialization)
|
|
207
324
|
│ ├── utils/
|
|
208
|
-
│ │ ├──
|
|
209
|
-
│ │
|
|
210
|
-
│
|
|
211
|
-
│
|
|
212
|
-
│
|
|
213
|
-
│
|
|
325
|
+
│ │ ├── errors.js # Error codes / status mapping (SSOT)
|
|
326
|
+
│ │ ├── etag.js # sha256 ETag (SSOT)
|
|
327
|
+
│ │ ├── lineMath.js # BOM / CRLF / line ↔ byte conversion
|
|
328
|
+
│ │ ├── atomicWrite.js # Atomic file write (O_EXCL + EXDEV fallback)
|
|
329
|
+
│ │ ├── fileTypes.js # File type detection
|
|
330
|
+
│ │ └── path.js # Path security (validatePath / validatePathReal)
|
|
331
|
+
│ ├── static/ # Frontend files
|
|
332
|
+
│ │ ├── index.html
|
|
333
|
+
│ │ ├── app.js
|
|
334
|
+
│ │ ├── presenter.html # Presenter View (3-pane + autosave)
|
|
335
|
+
│ │ ├── styles.css
|
|
336
|
+
│ │ └── lib/
|
|
337
|
+
│ │ ├── apiClient.js # HTTP client wrapper
|
|
338
|
+
│ │ ├── presenterChannel.js # BroadcastChannel SSOT
|
|
339
|
+
│ │ ├── saveQueue.js # Per-deck save queue + per-slide coalesce
|
|
340
|
+
│ │ └── tabRegistry.js # Tab life-cycle hooks
|
|
341
|
+
│ └── styles/
|
|
342
|
+
│ ├── index.js
|
|
343
|
+
│ ├── report.example.css
|
|
344
|
+
│ └── report.pdf-options.example.json
|
|
214
345
|
├── scripts/
|
|
215
|
-
│ └── setup-macos-app.sh
|
|
216
|
-
└── tests/
|
|
346
|
+
│ └── setup-macos-app.sh # macOS app setup
|
|
347
|
+
└── tests/ # Test files (236 件、全 PASS)
|
|
217
348
|
```
|
|
218
349
|
|
|
219
350
|
## Requirements
|