@embedpdf-editor/vue3-chapter-viewer 1.0.0 → 1.0.3

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.md CHANGED
@@ -136,7 +136,12 @@ type ChapterManifest = {
136
136
  title: string;
137
137
  globalPageRange: [number, number];
138
138
  localPageRange: [number, number];
139
- source?: { url: string } | { buffer: ArrayBuffer } | { load: () => Promise<{ url: string } | { buffer: ArrayBuffer }> };
139
+ source?:
140
+ | { url: string }
141
+ | { buffer: ArrayBuffer }
142
+ | { load: () => Promise<{ url: string } | { buffer: ArrayBuffer }> }
143
+ | { urls: string[]; segmentPageThreshold: number }; // legacy
144
+ segmentPageThreshold?: number;
140
145
  encrypted?: boolean;
141
146
  ownedGlobalPages?: number[];
142
147
  }>;
@@ -144,7 +149,7 @@ type ChapterManifest = {
144
149
  };
145
150
  ```
146
151
 
147
- `globalPageRange` 和 `localPageRange` 都是闭区间,且页数必须一致。`chapterId` 同时作为内部 `documentId`,需要在整本书内唯一。
152
+ `globalPageRange` 和 `localPageRange` 都是闭区间,且页数必须一致。`chapterId` 在整本书内唯一;单 URL 章时 `documentId === chapterId`,分段章内部为 `chapterId#s0`、`#s1`…(业务存储仍只用 `chapterId`)。
148
153
 
149
154
  | 字段 | 说明 |
150
155
  | --- | --- |
@@ -206,50 +211,46 @@ const options: ChapterViewerOptions = {
206
211
 
207
212
  ### PDF 加载与预处理
208
213
 
209
- 打开章节前,引擎按以下优先级解析 PDF(`ChapterManagerPlugin.resolvePdfPayload`):
210
-
211
- 1. `chapter.source.url` / `source.buffer` — 直接使用
212
- 2. `chapter.source.load()` — **按章**异步预处理,返回 `{ url }` 或 `{ buffer }`
213
- 3. `chapterPdfLoader.loadPdf(chapter)` — **全局**统一逻辑
214
-
215
- **按章 `source.load`(单章解密、鉴权 URL):**
214
+ React 包一致,推荐 **三步 `chapterPdfLoader`**:`loadChapterUrls` → 可选 `openPdf`。
215
+ `loadChapterUrls` **每章只请求一次**(引擎缓存);PDF 按段各打开一次。
216
216
 
217
217
  ```ts
218
- {
219
- chapterId: 'ch-1',
220
- title: '第一章',
221
- globalPageRange: [1, 10],
222
- localPageRange: [0, 9],
223
- source: {
224
- load: async () => {
225
- const bytes = await fetchDecryptedPdf('ch-1');
226
- return { buffer: bytes };
227
- // 或 return { url: URL.createObjectURL(blob) };
218
+ import type { ChapterViewerOptions, ChapterPdfLoadContext } from '@embedpdf-editor/vue3-chapter-viewer';
219
+
220
+ const options: ChapterViewerOptions = {
221
+ manifest: {
222
+ chapters: [
223
+ {
224
+ chapterId: 'ch-1',
225
+ title: '第一章',
226
+ globalPageRange: [1, 13],
227
+ localPageRange: [0, 12],
228
+ segmentPageThreshold: 5,
229
+ },
230
+ ],
231
+ },
232
+ chapterPdfLoader: {
233
+ async loadChapterUrls(chapter) {
234
+ const res = await getOneChap(chapter.chapterId);
235
+ const raw = res.data.resourceUrl;
236
+ return Array.isArray(raw) ? raw : raw ? [raw] : [];
237
+ },
238
+ async openPdf(ctx: ChapterPdfLoadContext) {
239
+ return { url: ctx.url };
228
240
  },
229
241
  },
230
- }
242
+ notes: { /* ... */ },
243
+ bookmarks: { /* ... */ },
244
+ };
231
245
  ```
232
246
 
233
- **全局 `chapterPdfLoader`(所有章走同一套 API):**
247
+ 静态单文件仍可用 `source.url` / `load()`。兼容 `loadPdf(chapter, segmentIndex)`。
234
248
 
235
- ```ts
236
- import type { ChapterViewerOptions, IChapterPdfLoader } from '@embedpdf-editor/vue3-chapter-viewer';
249
+ [03-manifest.md](../../docs/get-started/03-manifest.md) · [12-segmented-pdf-and-per-chapter-storage.md](../../docs/get-started/12-segmented-pdf-and-per-chapter-storage.md)
237
250
 
238
- const loader: IChapterPdfLoader = {
239
- async loadPdf(chapter) {
240
- const res = await api.getChapterPdf(chapter.chapterId);
241
- return { url: res.signedUrl };
242
- // 或 return { buffer: await res.arrayBuffer() };
243
- },
244
- };
251
+ ### 按章持久化(笔记 / 书签)
245
252
 
246
- const options: ChapterViewerOptions = {
247
- manifest: { chapters: [/* 可省略各条 source */] },
248
- chapterPdfLoader: loader,
249
- notes: { /* ... */ },
250
- bookmarks: { /* ... */ },
251
- };
252
- ```
253
+ `NoteAnchor` / `ParagraphBookmark.anchor` `chapterId`、`localPageIndex` 与单 URL 章相同。`loadNotes` / `bookmarks.load` 按章查询即可。划线备份用 `exportChapterAnnotations`(归档键为 `chapterId`)。
253
254
 
254
255
  ### 加密 PDF 与 `passwordProvider`
255
256
 
@@ -275,9 +276,9 @@ const passwordProvider = new CallbackPasswordProvider(async (chapter, attempt) =
275
276
  | `notes` | `enabled`、`marker`(`renderIcon`、`renderMenuActions`、`iconSize`、`highlightColor`) |
276
277
  | `zoom` | `enabled`(默认 true)、`min` 0.5、`max` 3、`initial` 1、`pageWidth`;实际上限由 `[data-chapter-scroll-viewport]` 宽度决定,resize 时自动更新 |
277
278
  | `scrollViewport` | `background`(默认 `#f1f5f9`),滚动视口背景 |
278
- | `selectionToolbar` | `enabled`、`hiddenBuiltinActions`、`extraActions` |
279
+ | `selectionToolbar` | `enabled`、`hiddenBuiltinActions`(含 `copy`)、`renderCopyIcon`、`extraActions` |
279
280
 
280
- 默认划线 `offsetY`:`squiggly` 为 **4**。内置工具条按钮:`highlight` | `underline` | `squiggly` | `strikeout` | `note`。
281
+ 默认划线 `offsetY`:`squiggly` 为 **4**。内置工具条顺序:**`copy`** 划线类 扩展 **`note`**。
281
282
 
282
283
  ---
283
284
 
@@ -303,12 +304,21 @@ const passwordProvider = new CallbackPasswordProvider(async (chapter, attempt) =
303
304
  usePdfiumEngine();
304
305
  ```
305
306
 
306
- 如果业务要求内网部署、固定版本或自定义 CDN,再传入自托管地址:
307
+ 如果业务要求内网部署、固定版本或自定义 CDN,再传入自托管地址(**完整 URL** 或站点相对路径):
307
308
 
308
309
  ```ts
309
310
  usePdfiumEngine({ wasmUrl: '/assets/pdfium.wasm', worker: true });
311
+
312
+ // 自有 OSS / CDN(示例)
313
+ usePdfiumEngine({
314
+ wasmUrl:
315
+ 'https://hep-editor.oss-cn-beijing.aliyuncs.com/public/editor-public/js/pdfium.wasm',
316
+ worker: true,
317
+ });
310
318
  ```
311
319
 
320
+ 详见 [docs/get-started/01-installation.md](../../docs/get-started/01-installation.md)。
321
+
312
322
  使用 `worker: true` 时,部署环境需要允许 worker 加载 wasm。若只在 worker 模式失败,可先用 `worker: false` 定位资源或响应头问题。
313
323
 
314
324
  ## `features` 配置示例
@@ -377,6 +387,28 @@ features: {
377
387
 
378
388
  ### 选区工具栏
379
389
 
390
+ #### 复制(默认开启,浮窗最左侧)
391
+
392
+ ```ts
393
+ import { h } from 'vue';
394
+
395
+ features: {
396
+ selectionToolbar: {
397
+ // hiddenBuiltinActions: ['copy'],
398
+ renderCopyIcon: () =>
399
+ h('span', { 'aria-hidden': true, style: { fontSize: '18px' } }, '📋'),
400
+ },
401
+ },
402
+ ```
403
+
404
+ ```ts
405
+ import { copyTextToClipboard } from '@embedpdf-editor/vue3-chapter-viewer';
406
+
407
+ await copyTextToClipboard('文本');
408
+ ```
409
+
410
+ #### 划线 + 扩展按钮
411
+
380
412
  ```ts
381
413
  features: {
382
414
  selectionToolbar: {
@@ -386,7 +418,8 @@ features: {
386
418
  },
387
419
  ```
388
420
 
389
- 配合 `ChapterPdfViewer` 的 `@extra-selection-action` 或 `build-selection-menu`。
421
+ 配合 `ChapterPdfViewer` 的 `@extra-selection-action` 或 `build-selection-menu`。
422
+ 更多字段说明见 [React 包 `features.selectionToolbar`](../react-chapter-viewer/README.md#featuresselectiontoolbar)。
390
423
 
391
424
  ## 进阶组合
392
425
 
@@ -430,10 +463,14 @@ import {
430
463
  | --- | --- |
431
464
  | `mode` | `replace` \| `merge` |
432
465
  | `bookmarks` / `notes` / `markup` | 导入导出子集,默认 true |
433
- | `ensureChapterLoaded` | 默认 true |
466
+ | `ensureChapterLoaded` | 默认 true;导出含 markup 的分段章会加载 **全部段** |
434
467
  | `persistNotes` / `persistBookmarks` | 导入后写回存储 |
435
468
 
436
- 详见 [React README 标注章节](../react-chapter-viewer/README.md#标注导入导出)。
469
+ 归档按 `chapterId` 分桶。详见 [10-annotations-io.md](../../docs/get-started/10-annotations-io.md)、[12-segmented-pdf-and-per-chapter-storage.md](../../docs/get-started/12-segmented-pdf-and-per-chapter-storage.md)、[React README 标注章节](../react-chapter-viewer/README.md#标注导入导出)。
470
+
471
+ ## 教程索引
472
+
473
+ [docs/get-started/README.md](../../docs/get-started/README.md)(含 `wasmUrl`、划词复制、事件全集 [11](./../../docs/get-started/11-events-callbacks-and-component-api.md)、分段 [12](./../../docs/get-started/12-segmented-pdf-and-per-chapter-storage.md))
437
474
 
438
475
  ## 常见问题
439
476
 
@@ -444,3 +481,4 @@ import {
444
481
  | 页码或滚动错位 | 检查 `globalPageRange` 与 `localPageRange` 页数是否一致 |
445
482
  | 划词笔记没有保存 | 实现 `notes.onCreateNote` 或自定义 `onRequestCreateNote` 流程 |
446
483
  | 书签删除无效 | `bookmarks.onRequestRemove` 需要返回 `true` 才会删除 |
484
+ | 分段章业务 ID 混乱 | 对外只用 `chapterId` + `localPageIndex`,勿用 `chapterId#sN` |