pdf-presenter 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hsiehting Lin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README-zhtw.md ADDED
@@ -0,0 +1,211 @@
1
+ # pdf-presenter
2
+
3
+ > 輕量級 CLI 工具,用瀏覽器播放 PDF 投影片,並提供完整的主講者模式 —— 包含講者備註、下一張預覽、計時器。不需要轉換 Markdown,也不需要原生相依套件。指向 PDF,就能開始。
4
+
5
+ [English version →](./README.md)
6
+
7
+ ```bash
8
+ npx pdf-presenter slides.pdf # 啟動伺服器並開啟瀏覽器
9
+ npx pdf-presenter -gn slides.pdf # 產生備註樣板
10
+ npx pdf-presenter slides.pdf -t 20 # 20 分鐘倒數計時
11
+ ```
12
+
13
+ ---
14
+
15
+ ## 為什麼要有這個工具
16
+
17
+ 用 PDF 進行簡報通常只有兩條路:
18
+
19
+ - 用 PDF 檢視器開啟,但就失去講者備註和下一張預覽,或
20
+ - 把投影片轉成 HTML(然後跟版面、字型、建構工具纏鬥)。
21
+
22
+ `pdf-presenter` 走第三條路:保留 PDF 原狀,用瀏覽器裡的 `pdf.js` 渲染,並在上面疊一個主講者視窗(目前 + 下一張 + 備註 + 計時器)。投影片狀態透過 `BroadcastChannel` 在觀眾與主講者視窗之間同步 —— 不用 WebSocket,不用跟伺服器來回通訊。
23
+
24
+ ## 功能特色
25
+
26
+ - **零設定。** 只要一個參數:PDF 檔案。
27
+ - **雙視圖。** 觀眾視圖(全螢幕投影片)與主講者視圖(目前、下一張、備註、計時器、頁碼)。
28
+ - **講者備註** 使用簡單的 JSON 檔。可用 `-gn` 從 PDF 文字產生樣板,或**直接在主講者視圖中編輯備註** —— 變更會自動寫回 `slides.notes.json`(防抖動延遲儲存)。
29
+ - **計時器。** 預設為計時,或用 `--timer <分鐘>` 倒數計時。最後 5 分鐘轉黃、最後 1 分鐘轉紅。
30
+ - **鍵盤優先。** 方向鍵、`Space`、`PageUp/Down`、`Home/End`,加上 `F` 凍結、`B` 黑畫面、`R` 重置計時器。
31
+ - **多視窗同步** 透過 `BroadcastChannel` —— 主講者視窗驅動同一瀏覽器內的一個或多個觀眾視窗。
32
+ - **無原生相依。** 純 JavaScript。由 `pdf.js` 處理重活。
33
+
34
+ ## 安裝
35
+
36
+ 不安裝直接執行:
37
+
38
+ ```bash
39
+ npx pdf-presenter slides.pdf
40
+ ```
41
+
42
+ 或全域安裝:
43
+
44
+ ```bash
45
+ npm i -g pdf-presenter
46
+ pdf-presenter slides.pdf
47
+ ```
48
+
49
+ 需要 Node.js ≥ 18。
50
+
51
+ ## 用法
52
+
53
+ ### 播放 PDF
54
+
55
+ ```bash
56
+ pdf-presenter <file.pdf> [選項]
57
+ ```
58
+
59
+ | 旗標 | 簡寫 | 預設值 | 說明 |
60
+ |------|------|--------|------|
61
+ | `--port <n>` | `-p` | `3000`(若被佔用會自動遞增) | 伺服器連接埠 |
62
+ | `--no-open` | | | 不要自動開啟瀏覽器 |
63
+ | `--presenter` | | `false` | 直接以主講者模式開啟 |
64
+ | `--notes <path>` | `-n` | `<file>.notes.json` | 備註 JSON 檔路徑 |
65
+ | `--timer <minutes>` | `-t` | 無 | 倒數計時分鐘數 |
66
+ | `--version` | `-v` | | 印出版本 |
67
+ | `--help` | `-h` | | 印出說明 |
68
+
69
+ 啟動後會看到:
70
+
71
+ ```
72
+ 🎯 pdf-presenter v1.0.0
73
+
74
+ Audience: http://localhost:3000
75
+ Presenter: http://localhost:3000/presenter
76
+
77
+ PDF: slides.pdf
78
+ Notes: slides.notes.json (18/24 slides have notes)
79
+ Timer: 20:00
80
+
81
+ Press Ctrl+C to stop.
82
+ ```
83
+
84
+ 把 **Audience** URL(或視窗)分享給觀眾,**Presenter** URL 留給自己。
85
+
86
+ ### 產生備註樣板
87
+
88
+ ```bash
89
+ pdf-presenter -gn slides.pdf
90
+ pdf-presenter --generate-presenter-note-template slides.pdf
91
+ pdf-presenter -gn slides.pdf --force # 覆蓋既有檔案
92
+ ```
93
+
94
+ 這會讀取 PDF、擷取每頁的第一行文字做為 `hint`(提示),並在 PDF 旁邊寫入 `slides.notes.json`:
95
+
96
+ ```json
97
+ {
98
+ "meta": {
99
+ "pdf": "slides.pdf",
100
+ "totalSlides": 24,
101
+ "generatedAt": "2026-04-10T12:00:00.000Z",
102
+ "generator": "pdf-presenter"
103
+ },
104
+ "notes": {
105
+ "1": { "hint": "Introduction — Welcome", "note": "" },
106
+ "2": { "hint": "Agenda — Topics for Today", "note": "" }
107
+ }
108
+ }
109
+ ```
110
+
111
+ - `hint` 是自動擷取的,當該張投影片沒有 `note` 時會顯示在主講者視圖。
112
+ - `note` 是你要寫的內容,會在主講者視圖中作為講者備註顯示。
113
+ - 鍵值是從 1 開始的投影片編號。
114
+
115
+ 若 `slides.notes.json` 已存在,`-gn` 會出警告並中止 —— 用 `--force` 強制覆蓋。
116
+
117
+ ### 即時編輯備註
118
+
119
+ 主講者視圖本身就是編輯器:點進備註區域開始打字。變更會自動寫回 `slides.notes.json`(停止打字約 600 毫秒後觸發防抖動儲存)。面板標題旁的小指示器會顯示 `Saving…` / `Saved` / `Save failed`。
120
+
121
+ - 當備註區塊取得焦點時,鍵盤快捷鍵(方向鍵、`F`、`B`、`R`)會暫時停用,避免打字被吃掉。按 **`Esc`** 可離開編輯器並回到投影片導覽模式。
122
+ - 若 `slides.notes.json` 尚未存在,首次儲存會自動建立。
123
+ - 既有的 `hint` 欄位會被保留。面板標題會以淡色顯示當前投影片的 hint。
124
+ - 伺服器端的寫入是序列化的,所以從多個視窗同時編輯也不會互相蓋掉。
125
+
126
+ ## 鍵盤快捷鍵
127
+
128
+ 兩種視圖共用:
129
+
130
+ | 按鍵 | 動作 |
131
+ |------|------|
132
+ | `→` / `Space` / `PageDown` | 下一張投影片 |
133
+ | `←` / `PageUp` | 上一張投影片 |
134
+ | `Home` / `End` | 第一張 / 最後一張(主講者) |
135
+
136
+ 只在觀眾視圖:
137
+
138
+ | 按鍵 | 動作 |
139
+ |------|------|
140
+ | `P` | 在新視窗開啟主講者視圖 |
141
+
142
+ 只在主講者視圖:
143
+
144
+ | 按鍵 | 動作 |
145
+ |------|------|
146
+ | `F` | 凍結 / 解除凍結觀眾視圖 |
147
+ | `B` | 觀眾視圖黑畫面 |
148
+ | `R` | 重置計時器 |
149
+
150
+ ## 典型流程
151
+
152
+ ```bash
153
+ # 1. 產生備註樣板
154
+ pdf-presenter -gn slides.pdf
155
+
156
+ # 2. 在編輯器裡開啟 slides.notes.json,填入 "note" 欄位
157
+
158
+ # 3. 開始簡報
159
+ pdf-presenter slides.pdf --timer 20
160
+ ```
161
+
162
+ 接著:
163
+
164
+ 1. 觀眾 URL 會在瀏覽器打開 —— 把那個視窗分享出去(或拖到投影機 / 第二螢幕)。
165
+ 2. 在筆電螢幕上用第二個視窗開啟主講者 URL。
166
+ 3. 從主講者視窗控制投影片,觀眾視窗會自動跟隨。
167
+
168
+ ## 運作原理
169
+
170
+ - `pdfjs-dist`(Mozilla `pdf.js`)把每頁渲染到瀏覽器的 `<canvas>`,以 device-pixel-ratio 解析度縮放以符合視窗大小。
171
+ - Node CLI 內建一個極簡的 `http` 伺服器(沒有 Express、沒有 middleware),提供以下路由:
172
+ - `/` → 觀眾 HTML
173
+ - `/presenter` → 主講者 HTML
174
+ - `/slides.pdf` → 你的 PDF,從磁碟以串流方式回傳
175
+ - `/notes.json` → 備註檔(若不存在則回傳空白樣板)
176
+ - `/assets/*` → 前端 JS/CSS
177
+ - `/assets/pdfjs/*` → `pdfjs-dist` 函式庫和 worker
178
+ - 主講者視窗透過 `BroadcastChannel("pdf-presenter")` 廣播投影片變更事件,同一瀏覽器內的觀眾視窗監聽並重新渲染。
179
+ - `-gn` 的文字擷取使用 `pdfjs-dist` 的 legacy build,能在 Node 中執行而不需要 DOM 或 canvas。
180
+
181
+ ## 開發
182
+
183
+ ```bash
184
+ make install # pnpm install
185
+ make build # tsup 建置 → dist/
186
+ make check # tsc --noEmit
187
+ make run # 建置並以 test/fixtures/sample.pdf 執行
188
+ make clean # 移除 dist/ 和 node_modules/
189
+ ```
190
+
191
+ 專案結構:
192
+
193
+ ```
194
+ pdf-presenter/
195
+ ├── bin/pdf-presenter.ts # CLI 進入點
196
+ ├── src/
197
+ │ ├── cli.ts # commander 設定、命令分派
198
+ │ ├── server.ts # HTTP 伺服器與路由表
199
+ │ ├── generate-notes.ts # -gn 指令
200
+ │ ├── utils.ts # 路徑/連接埠輔助
201
+ │ └── ui/ # 靜態前端
202
+ │ ├── audience.html
203
+ │ ├── presenter.html
204
+ │ ├── presenter.css
205
+ │ └── presenter.js # pdf.js 渲染 + BroadcastChannel 同步
206
+ └── test/fixtures/ # 煙霧測試用 PDF 範例
207
+ ```
208
+
209
+ ## 授權
210
+
211
+ MIT
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # pdf-presenter
2
+
3
+ > Lightweight CLI that serves a PDF as browser slides with a full presenter mode — speaker notes, next-slide preview, and a timer. No markdown conversion, no native dependencies. Point it at a PDF and go.
4
+
5
+ [繁體中文版 →](./README-zhtw.md)
6
+
7
+ ```bash
8
+ npx pdf-presenter slides.pdf # serve & open browser
9
+ npx pdf-presenter -gn slides.pdf # generate a notes template
10
+ npx pdf-presenter slides.pdf -t 20 # 20-minute countdown
11
+ ```
12
+
13
+ ---
14
+
15
+ ## Why
16
+
17
+ Presenting a PDF deck usually means either:
18
+
19
+ - Opening the PDF in a viewer and losing speaker notes / next-slide preview, or
20
+ - Converting slides to HTML (and fighting layout, fonts, and build tooling).
21
+
22
+ `pdf-presenter` takes a third path: keep the PDF as-is, render it with `pdf.js` in the browser, and layer a presenter window (current + next + notes + timer) on top. Slide state syncs between the audience and presenter windows via `BroadcastChannel` — no WebSocket, no server round-trip.
23
+
24
+ ## Features
25
+
26
+ - **Zero config.** One argument: the PDF file.
27
+ - **Dual views.** Audience view (fullscreen slide) and presenter view (current, next, notes, timer, counter).
28
+ - **Speaker notes** from a simple JSON file. Generate a template from the PDF text with `-gn`, or **edit notes directly in the presenter view** — changes are saved back to `slides.notes.json` automatically (debounced).
29
+ - **Timer.** Count up by default, or count down with `--timer <minutes>`. Colour shifts yellow in the last 5 min, red in the last 1 min.
30
+ - **Keyboard-first.** Arrow keys, `Space`, `PageUp/Down`, `Home/End`, plus `F` freeze, `B` black, `R` reset timer.
31
+ - **Multi-window sync** via `BroadcastChannel` — the presenter window drives one or more audience windows in the same browser.
32
+ - **No native deps.** Pure JavaScript. `pdf.js` does the heavy lifting.
33
+
34
+ ## Install
35
+
36
+ Run without installing:
37
+
38
+ ```bash
39
+ npx pdf-presenter slides.pdf
40
+ ```
41
+
42
+ Or install globally:
43
+
44
+ ```bash
45
+ npm i -g pdf-presenter
46
+ pdf-presenter slides.pdf
47
+ ```
48
+
49
+ Requires Node.js ≥ 18.
50
+
51
+ ## Usage
52
+
53
+ ### Serve a PDF
54
+
55
+ ```bash
56
+ pdf-presenter <file.pdf> [options]
57
+ ```
58
+
59
+ | Flag | Alias | Default | Description |
60
+ |------|-------|---------|-------------|
61
+ | `--port <n>` | `-p` | `3000` (auto-increments if taken) | Server port |
62
+ | `--no-open` | | | Don't auto-open the browser |
63
+ | `--presenter` | | `false` | Open directly in presenter mode |
64
+ | `--notes <path>` | `-n` | `<file>.notes.json` | Path to notes JSON |
65
+ | `--timer <minutes>` | `-t` | none | Countdown timer in minutes |
66
+ | `--version` | `-v` | | Print version |
67
+ | `--help` | `-h` | | Print help |
68
+
69
+ On start you'll see:
70
+
71
+ ```
72
+ 🎯 pdf-presenter v1.0.0
73
+
74
+ Audience: http://localhost:3000
75
+ Presenter: http://localhost:3000/presenter
76
+
77
+ PDF: slides.pdf
78
+ Notes: slides.notes.json (18/24 slides have notes)
79
+ Timer: 20:00
80
+
81
+ Press Ctrl+C to stop.
82
+ ```
83
+
84
+ Share the **audience** URL (or window) with your viewers. Keep the **presenter** URL for yourself.
85
+
86
+ ### Generate a notes template
87
+
88
+ ```bash
89
+ pdf-presenter -gn slides.pdf
90
+ pdf-presenter --generate-presenter-note-template slides.pdf
91
+ pdf-presenter -gn slides.pdf --force # overwrite existing
92
+ ```
93
+
94
+ This reads the PDF, extracts the first line of text from each page as a `hint`, and writes `slides.notes.json` next to the PDF:
95
+
96
+ ```json
97
+ {
98
+ "meta": {
99
+ "pdf": "slides.pdf",
100
+ "totalSlides": 24,
101
+ "generatedAt": "2026-04-10T12:00:00.000Z",
102
+ "generator": "pdf-presenter"
103
+ },
104
+ "notes": {
105
+ "1": { "hint": "Introduction — Welcome", "note": "" },
106
+ "2": { "hint": "Agenda — Topics for Today", "note": "" }
107
+ }
108
+ }
109
+ ```
110
+
111
+ - `hint` is auto-extracted and shown in the presenter view when a slide has no `note`.
112
+ - `note` is what you write. It appears as the speaker notes in the presenter view.
113
+ - Keys are 1-indexed slide numbers.
114
+
115
+ If `slides.notes.json` already exists, `-gn` aborts with a warning — use `--force` to overwrite.
116
+
117
+ ### Editing notes live
118
+
119
+ The presenter view doubles as an editor: click into the notes panel and type. Changes are saved back to `slides.notes.json` automatically (debounced ~600 ms after you stop typing). A small status indicator shows `Saving…` / `Saved` / `Save failed` next to the panel title.
120
+
121
+ - While the notes textarea is focused, keyboard shortcuts (arrow keys, `F`, `B`, `R`) are disabled so typing isn't hijacked. Press **`Esc`** to blur the editor and return to slide navigation.
122
+ - If `slides.notes.json` doesn't exist yet, the first save creates it.
123
+ - Existing `hint` fields are preserved. The panel title shows the hint for the current slide in a muted style.
124
+ - Writes are serialized server-side, so edits from multiple windows can't clobber each other mid-write.
125
+
126
+ ## Keyboard shortcuts
127
+
128
+ Both views:
129
+
130
+ | Key | Action |
131
+ |-----|--------|
132
+ | `→` / `Space` / `PageDown` | Next slide |
133
+ | `←` / `PageUp` | Previous slide |
134
+ | `Home` / `End` | First / last slide (presenter) |
135
+
136
+ Audience view only:
137
+
138
+ | Key | Action |
139
+ |-----|--------|
140
+ | `P` | Open presenter view in a new window |
141
+
142
+ Presenter view only:
143
+
144
+ | Key | Action |
145
+ |-----|--------|
146
+ | `F` | Freeze / unfreeze the audience view |
147
+ | `B` | Black out the audience view |
148
+ | `R` | Reset the timer |
149
+
150
+ ## Typical workflow
151
+
152
+ ```bash
153
+ # 1. Generate the notes template
154
+ pdf-presenter -gn slides.pdf
155
+
156
+ # 2. Edit slides.notes.json in your editor, fill in "note" fields
157
+
158
+ # 3. Present
159
+ pdf-presenter slides.pdf --timer 20
160
+ ```
161
+
162
+ Then:
163
+
164
+ 1. The audience URL opens in your browser — share that window (or drag it to your projector / second display).
165
+ 2. Open the presenter URL in a second window on your laptop screen.
166
+ 3. Drive the deck from the presenter window. The audience window follows automatically.
167
+
168
+ ## How it works
169
+
170
+ - `pdfjs-dist` (Mozilla `pdf.js`) renders each page to a `<canvas>` in the browser, scaled to fit the viewport at device-pixel-ratio resolution.
171
+ - The Node CLI ships a minimal `http` server (no Express, no middleware) that serves:
172
+ - `/` → audience HTML
173
+ - `/presenter` → presenter HTML
174
+ - `/slides.pdf` → your PDF, streamed from disk
175
+ - `/notes.json` → the notes file (or an empty stub if missing)
176
+ - `/assets/*` → UI JS/CSS
177
+ - `/assets/pdfjs/*` → the `pdfjs-dist` library and worker
178
+ - The presenter window broadcasts slide changes via `BroadcastChannel("pdf-presenter")`. Audience windows in the same browser listen and re-render.
179
+ - Notes text extraction for `-gn` uses the legacy `pdfjs-dist` build, which runs in Node without a DOM or canvas.
180
+
181
+ ## Development
182
+
183
+ ```bash
184
+ make install # pnpm install
185
+ make build # tsup build → dist/
186
+ make check # tsc --noEmit
187
+ make run # build + run against test/fixtures/sample.pdf
188
+ make clean # remove dist/ and node_modules/
189
+ ```
190
+
191
+ Project layout:
192
+
193
+ ```
194
+ pdf-presenter/
195
+ ├── bin/pdf-presenter.ts # CLI entry point
196
+ ├── src/
197
+ │ ├── cli.ts # commander setup, command dispatch
198
+ │ ├── server.ts # HTTP server + route map
199
+ │ ├── generate-notes.ts # -gn command
200
+ │ ├── utils.ts # path/port helpers
201
+ │ └── ui/ # static frontend
202
+ │ ├── audience.html
203
+ │ ├── presenter.html
204
+ │ ├── presenter.css
205
+ │ └── presenter.js # pdf.js rendering + BroadcastChannel sync
206
+ └── test/fixtures/ # sample PDF for smoke tests
207
+ ```
208
+
209
+ ## License
210
+
211
+ MIT