npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@embedpdf-editor/chapter-snippet

v1.4.5

Published

框架无关的章节 PDF 阅读器运行时。它把 Preact 阅读器封装成 Web Component。

Downloads

833

Readme

@embedpdf-editor/chapter-snippet

框架无关的章节 PDF 阅读器运行时。它把 Preact 阅读器封装成 Web Component。

Vue 2 请使用本包@embedpdf-editor/chapter-snippet),不要用已移除的 @embedpdf-editor/vue2-chapter-viewer。宿主为 Vue 2 / 传统页面 / 微前端,或任何不想引入 React/Vue3 渲染器的场景。

能力与 React/Vue3 渲染器保持一致:多章连续滚动、划线/高亮、附注、段落书签、章节目录和标注导入导出。

安装

pnpm add @embedpdf-editor/chapter-snippet

Vite

chapterSnippetViteResolve() 用于 Vite + Vue2/snippet 场景:

// vite.config.ts
import { defineConfig } from 'vite';
import { chapterSnippetViteResolve } from '@embedpdf-editor/chapter-snippet/vite';

export default defineConfig({
  ...chapterSnippetViteResolve(),
});

它会:

| 配置 | 作用 | | --- | --- | | server.headers | 开发环境添加 COOP/COEP,满足 worker/wasm 常见要求 | | optimizeDeps.exclude | 避免 Vite 二次预构建 snippet 和 PDFium 引擎 |

Vue CLI / Webpack

无需修改 vue.config.js 安装后直接:

import ChapterEmbedPDF from '@embedpdf-editor/chapter-snippet';

发包构建会对 dist/embedpdf-chapter.js 做语法降级(去掉 ?? 等 ES2020 语法),Webpack / Vue CLI 默认可 parse。默认 wasmUrl 指向 jsDelivr 上的 pdfium.wasm,无需复制到 public/

离线或内网部署时,可传 wasmUrlinit 顶层,与 options 同级):

// 相对路径:自行把 dist/pdfium.wasm 放到静态目录
wasmUrl: '/pdfium.wasm',

// 自有 OSS / CDN(完整 HTTPS 地址,示例)
wasmUrl:
  'https://hep-editor.oss-cn-beijing.aliyuncs.com/public/editor-public/js/pdfium.wasm',

详见 docs/get-started/01-installation.md

@embedpdf-editor/chapter-snippet/webpack可选辅助(仅 monorepo Vue 2.6 解析、或自定义 devServer COOP/COEP),普通用户不必使用。

完整示例见 examples/chapter-viewer-demo-vue2

快速开始

不需要把 pdfium.wasm 手动放到宿主项目的 public/。默认会从 @embedpdf-editor/chapter-snippet/dist/pdfium.wasm 加载。只有当你要走 CDN 或自定义静态域名时,才需要传 wasmUrl

import ChapterEmbedPDF, {
  CHAPTER_SNIPPET_EVENTS,
} from '@embedpdf-editor/chapter-snippet';

const viewer = ChapterEmbedPDF.init({
  type: 'container',
  target: document.getElementById('reader'),
  worker: true,
  options: {
    manifest: {
      chapters: [
        {
          chapterId: 'ch-1',
          title: '第一章',
          globalPageRange: [1, 12],
          localPageRange: [0, 11],
          source: { url: '/pdfs/chapter-1.pdf' },
        },
      ],
    },
    notes: {
      loadNotes: async () => [],
      onUpdateNote: async () => {},
      // 两阶段删除:业务先确认外部数据,返回 true 才让插件擦 PDF 笔记
      onRequestDeleteNote: async () => true,
    },
    bookmarks: {
      load: async () => [],
      persist: async () => {},
      onRequestRemove: async () => true,
    },
    features: {
      markup: true,
      bookmarks: true,
      notes: true,
      selectionToolbar: true,
      zoom: { pageWidth: 720 },
    },
  },
});

viewer?.addEventListener(CHAPTER_SNIPPET_EVENTS.noteRequestCreate, (event) => {
  const { record, complete } = event.detail;
  openNoteDialog(record, async (content) => {
    const noteId = 'note-' + Date.now();
    const nodeId = await saveNote(content, record);
    await complete({ noteId, content, nodeId });
  });
});

viewer?.addEventListener(CHAPTER_SNIPPET_EVENTS.ready, (event) => {
  console.log('registry ready', event.detail.registry);
});

容器需要稳定高度:

#reader {
  height: 100vh;
  min-height: 0;
}

配置

| 字段 | 默认 | 说明 | | --- | --- | --- | | type | 必填 | 目前使用 'container' | | target | 必填 | 挂载目标元素 | | options | 必填 | 推荐的章节阅读器配置,和 React/Vue3 的 options 语义一致 | | wasmUrl | dist/pdfium.wasm | 自定义 PDFium wasm 地址 | | fontFallback | 默认 jsDelivr | 自定义 PDFium 缺字 fallback 字体地址;可换成业务 CDN/OSS | | worker | true | 是否启用 PDFium worker | | fallbackToDirectEngine | true | 首章 worker 加载超时后自动降级到 direct engine | | workerOpenTimeoutMs | 8000 | 首章加载超时时间 | | features | - | 与 options.features 深度合并;顶层同名字段优先。勿放进 createChapterViewerEditorOptions() | | className / viewportClassName | - | 传给内部阅读器容器 |

旧版本的 editorInput 仍可使用,但新代码应改为 options,并把 features 写在 options.features 或顶层 features

字体 fallback CDN

PDF 缺少内嵌字体时,PDFium 会按需请求 fallback 字体。默认使用 jsDelivr;如果字体加载慢,可锁定版本或改成业务 CDN/OSS:

import ChapterEmbedPDF, {
  FontCharset,
  createCdnFontConfig,
} from '@embedpdf-editor/chapter-snippet';

ChapterEmbedPDF.init({
  type: 'container',
  target: document.getElementById('viewer'),
  options,

  // 锁定 @embedpdf/fonts-* 版本,避免 latest 解析抖动
  fontFallback: createCdnFontConfig('1.0.0'),
});

ChapterEmbedPDF.init({
  type: 'container',
  target: document.getElementById('viewer'),
  options,

  // 完全自定义简体中文字体地址
  fontFallback: {
    fonts: {
      [FontCharset.GB2312]: [
        {
          url: 'https://cdn.jsdelivr.net/npm/@embedpdf/fonts-sc@latest/fonts/NotoSansHans-Regular.otf',
          weight: 400,
        },
      ],
    },
  },
});

内置字体 URL 完整清单、FontCharset 对应关系,以及“下载后上传到自有服务器再替换 URL”的完整配置见 docs/get-started/01-installation.md

PDF 三步加载(Vue 2 推荐)

| 步骤 | 说明 | | --- | --- | | 1 | manifest.chapters:仅页码;segmentPageThreshold 写在章节上 | | 2 | chapterPdfLoader.loadChapterUrls:按章 getOneChap每章只调一次 | | 3 | chapterPdfLoader.openPdf(可选):解密等;省略则直接打开 ctx.url |

options: {
  manifest: {
    chapters: [
      {
        chapterId: item._id,
        title: item.title,
        globalPageRange: [item.startPage, item.endPage],
        localPageRange: [0, item.endPage - item.startPage],
        segmentPageThreshold: item.page,
      },
    ],
  },
  chapterPdfLoader: {
    async loadChapterUrls(chapter) {
      const res = await getOneChap(chapter.chapterId);
      if (!res.success) throw new Error(res.message);
      const raw = res.data.resourceUrl;
      return Array.isArray(raw) ? raw : raw ? [raw] : [];
    },
    async openPdf(ctx) {
      const decrypted = await decryptPdf(ctx.url);
      return { buffer: decrypted }; // ArrayBuffer / Uint8Array / DataView 都支持
    },
  },
  notes: { /* ... */ },
  bookmarks: { /* ... */ },
},

勿在 loader 外再请求章节详情;勿把 segmentPageThreshold 放进 source

03-manifest.md · 12-segmented-pdf-and-per-chapter-storage.md

按章持久化

options.notes / options.bookmarks 回调里的 chapterIdlocalPageIndex 与单 URL 章相同。划线备份用 exportChapterAnnotations,JSON 键为 chapters[chapterId](导出 markup 时会拉全部分段)。

ChapterViewerOptions(与 React / Vue3 一致)

| 字段 | 说明 | | --- | --- | | manifest | 章节目录,见 React README Manifest | | notes | NoteCallbacks,见 React README | | bookmarks | ParagraphBookmarkCallbacks,见 React README | | chapterPdfLoader / overlapStrategy | 自定义 PDF 加载与重叠页策略 | | features | 功能开关与 UI 定制,见下 |

加密 PDF 可在底层 createPdfChapterEditor 选项中设置 passwordProvider(如 CallbackPasswordProvider from @embedpdf-editor/chapter-core)。

features 配置

与 React / Vue3 同一套 ChapterViewerConfig@embedpdf-editor/chapter-viewer)。完整字段表见 React README:features 配置

| 模块 | 要点 | | --- | --- | | markup | styles 四类划线;annotationMenu 默认选中后在下方显示「删除」;onClick 配置后不弹菜单;squiggly.offsetY 默认 4 | | bookmarks | marker / hover 自定义图标;marker.onClick 配置后不弹删除浮窗 | | notes | marker.renderIconrenderMenuActionsiconSizeoffsetX/offsetYhighlightColorhighlightStyle(任意 CSS)、alwaysVisibleonClick(高亮区与图标同一事件)、selectedOutline | | zoom | pageWidthfitParentWidth(勿与 initial 同配)、clampMaxToViewport(默认 true)、pinchExponent(默认 1)、min / max / enabled;滚轮 / 捏合锚点缩放;移动端推荐 fitParentWidth + clampMaxToViewport: false + max: 8 | | scrollViewport | background(默认 #f1f5f9),[data-chapter-scroll-viewport] 背景 | | page | background(默认 #ffffff),单页 PDF 画布背景 | | selectionToolbar | 两行浮窗、色盘 markupColorsonCopySuccess(复制成功回调,配置后不显示默认「已复制」提示)、dismissToolbarOnAction{ copy?, note? } 默认 true)、dismissToolbarOnclick / scroll,默认 ['click'])、extraActions[].dismissToolbarOnAction(默认 true);或监听 DOM 事件 selectionExtraAction | | selectionHandles | enabled(默认 true)、color(默认 #2196f3)、size(圆球直径 px,默认 16);移动端长按选词后的可拖动选区手柄,仅触摸设备生效 | | pageOverlays | 页内矩形框;defaultBoxStyle(任意 CSS)、renderContentonClickctx.payload);数据经 importChapterAnnotationspageOverlays 注入 | | boxSelect | 用户自框选(PC,默认 enabled:false);defaultBoxStylemenuAlign(浮窗左/中/右对齐)、renderBox(框内 UI,禁缩放)、renderMenu(浮窗容器);开关 / 增删 / 滚动经 chapter-viewer-readyregistry 后调 box-select capability。见 用户自框选 | | markup | 除 styles 外可配 onCreatedonRequestDeletePromise<boolean>true 才真删)、onDeleted | | bookmarksoptions) | 除 load / persist 外可配 onAdded(新增书签,含 position) |

移动端交互开箱即用,无需配置:单指滑动只滚动 PDF,绝不触发划词(严格);长按约 450ms 选中所在词并弹出浮窗。双指捏合以两指中心为锚点缩放(pinchExponent: 1 默认 1:1)。仅阅读模式生效,与桌面 Ctrl+滚轮互不影响(Shadow DOM 内已自动注入所需样式)。Vue 2 / Web Component 端仍通过 selectionToolbar 配置和 selectionExtraAction 事件扩展浮窗。

移动端 / PC 分端 zoomoptions.features 或顶层 features):

const isMobile = window.matchMedia('(max-width: 768px)').matches;

features: {
  zoom: isMobile
    ? { fitParentWidth: true, clampMaxToViewport: false, max: 8, pinchExponent: 1, enabled: true }
    : { pageWidth: 800, max: 3, enabled: true },
},

详版见 React README:features.zoom

原生风格选区手柄:长按选词后,选区首/尾各出现一个可拖动的圆球手柄,拖动即可扩大 / 收缩选中内容(接近 iOS / Android 原生),仅触摸设备显示。通过 features.selectionHandles 配置(默认开启):enabledcolor(默认 #2196f3)、size(圆球直径 px,默认 16)。手柄挂在文档 body 上、根节点带 class embedpdf-chapter-selection-handle(首/尾附加 --start / --end),可用全局 CSS 覆写图标。完善支持单页选区内拖动;跨页 / 旋转页为已知限制。

features: {
  markup: {
    styles: {
      highlight: { color: '#fef08a', opacity: 0.45 },
      underline: { color: '#dc2626', thickness: 1.5, offsetY: 2.5 },
      squiggly: { color: '#dc2626', thickness: 1.5, offsetY: 4 },
    },
    annotationMenu: {
      enabled: true,
      renderMenu: ({ onDelete }) => {
        const btn = document.createElement('button');
        btn.textContent = '移除';
        btn.onclick = onDelete;
        return btn;
      },
    },
  },
  notes: {
    marker: {
      renderIcon: () => /* DOM */,
      renderMenuActions: ({ onEdit, onDelete }) => /* DOM */,
      // 默认 false:仅 hover 高亮时显示图标。
      // true:图标常驻显示,hover 感知区仅覆盖图标本身。
      alwaysVisible: true,
    },
  },
  selectionToolbar: {
    selectionBackground: 'rgba(255, 193, 7, 0.35)',
    // 复制默认开启,浮窗最左侧;隐藏:hiddenBuiltinActions: ['copy']
    renderCopyIcon: () => {
      const span = document.createElement('span');
      span.textContent = '📋';
      span.setAttribute('aria-hidden', 'true');
      return span;
    },
    markupColors: ['#93c5fd', '#fde047'],
    extraActions: [
      {
        id: 'translate',
        label: '翻译',
        onClick: ({ selectedText, chapterId, localPageIndex }) => {
          console.log('翻译', selectedText, chapterId, localPageIndex);
        },
      },
    ],
    // 或统一回调:onExtraAction: (ctx) => { ... }
  },
  markup: {
    onCreated: ({ kind, strokeColor, position }) => {
      console.log('新划线', kind, strokeColor, position);
    },
  },
},

划词后点复制会将选中文本写入剪贴板。扩展按钮回调与 DOM 事件 detail 均含 selectedText。程序化:

import { copyTextToClipboard } from '@embedpdf-editor/chapter-snippet';

await copyTextToClipboard('文本');

renderMenu / renderIcon / renderCopyIcon 在 snippet(Preact)内执行,请返回 DOM 或 Preact 节点;Vue 2 宿主不要用 h() 直接塞进 Shadow DOM。

pageOverlays(矩形叠加层)

features: {
  pageOverlays: {
    defaultBoxStyle: {
      border: '2px dashed #ef4444',
      cursor: 'pointer',
    },
    renderContent: ({ payload }) => {
      const span = document.createElement('span');
      span.textContent = payload ?? 'QR';
      return span;
    },
    onClick: ({ overlay, payload }) => {
      console.log(overlay.overlayId, payload);
    },
  },
},

通过 importChapterAnnotations(registry, { chapterId, pageOverlays: [...] }) 注入数据;后端 QR 比例坐标转换见 13-page-overlays.mdqrTargetToPageOverlayRecord 由 React/Vue3 包导出,snippet 可手写 rectPdfCoord 或自行实现相同公式)。

用户自框选 boxSelect

用户拖拽画矩形框,框可点击选中、拖动、缩放,选中即弹自定义浮窗。PC 端能力,默认关闭renderBox / renderMenu 是渲染函数(Preact 节点或 DOM),须通过 JS el.config(或 init({...}))传入,不能走 HTML 属性。

import { boxSelectBackendToRecord } from '@embedpdf-editor/chapter-snippet';

el.config = {
  manifest,
  features: {
    boxSelect: {
      enabled: false, // 默认关,运行时用 registry 开
      menuAlign: 'center', // left | center | right
      renderMenu: ({ info, getInfo, getRecord, applySave, payload, getPayload, deleteSelf, dismissMenu, menuWrapperProps, menuBarStyle }) => {
        const wrap = document.createElement('div');
        Object.assign(wrap.style, menuWrapperProps.style);
        const bar = document.createElement('div');
        Object.assign(bar.style, menuBarStyle, { display: 'flex', gap: '8px' });
        const save = document.createElement('button'); save.textContent = '保存'; save.onclick = async () => {
          const current = getRecord!();
          const infoNow = getInfo?.() ?? info;
          const payloadNow = getPayload?.() ?? payload;
          const isCreate = current.id.startsWith('bs_');
          const savedId = isCreate ? `custom-${infoNow.chapterId}-p${infoNow.pageIndex}` : current.id;
          await saveToBackend(getInfo?.() ?? info, payloadNow, isCreate);
          applySave?.({ id: savedId, payload: payloadNow });
        };
        const del = document.createElement('button'); del.textContent = '删除'; del.onclick = deleteSelf;
        bar.append(save, del);
        wrap.append(bar);
        return wrap;
      },
    },
  },
};

命令式 API 经 chapter-viewer-readyregistry 后调 box-select capability:

el.addEventListener('chapter-viewer-ready', (event) => {
  const registry = event.detail.registry;
  const box = registry.getPlugin('box-select').provides();

  box.setEnabled(true);                 // 开启框选(或 box.toggle())
  box.onEnd(({ record, info }) => saveToBackend(info)); // 建框/改框完成
  box.replaceForChapter('ch-1', [       // 按需绘制后端数据
    boxSelectBackendToRecord({
      target: { id: 'b1', pageIndex: 1, x: 0.1, y: 0.2, width: 0.3, height: 0.15, pageWidth: 531.39, pageHeight: 746.85 },
      chapterId: 'ch-1',
    }),
  ]);
  box.removeById('b1');                  // 删除
});

info: BoxSelectRectInfo 含 1-based pageIndex + 归一化 x/y/width/height + 页宽高,可直接回后端。滚动到指定框可用 React/Vue3 的 scrollToBoxSelectRecord(snippet 亦 re-export),或经 chapter-scroll capability 的 scrollToGlobalPdfPoint

事件

snippet 通过 DOM CustomEvent 抛出动作。常量见 CHAPTER_SNIPPET_EVENTS
若在 options 里已写 onClick / onCreated / onAdded 等回调,仍会派发同名 DOM 事件(便于 Vue 2 只绑 @xxx)。

| 常量 | 事件名 | detail(关键字段) | | --- | --- | --- | | noteRequestCreate | chapter-note-request-create | record(含 selectedTextposition)、complete({ noteId, content?, nodeId? }) | | noteRequestEdit | chapter-note-request-edit | recordnodeId? | | selectionExtraAction | chapter-selection-extra-action | actionIdselectedTextchapterIdrectsPdfCoord | | bookmarkAdded | chapter-bookmark-added | bookmarkposition | | markupCreated | chapter-markup-created | kindannotationIdstrokeColorpositionrecord | | boxSelectStart | chapter-box-select-start | { chapterId, localPageIndex, pageIndex, startPdfPoint } | | boxSelectChange | chapter-box-select-change | { record, info }(拖拽/缩放实时) | | boxSelectEnd | chapter-box-select-end | { record, info }(建框完成或拖拽结束,可落库) | | setPdfPageBackground | chapter-set-pdf-page-background | background(CSS 颜色,派发以更新页背景) | | pdfPageBackgroundChange | chapter-pdf-page-background-change | background(更新后通知) | | ready | chapter-viewer-ready | registry(导出标注用) |

el.addEventListener('chapter-selection-extra-action', (e) => {
  const { actionId, selectedText, chapterId } = e.detail;
  if (actionId === 'translate') translate(selectedText, chapterId);
});

el.addEventListener('chapter-markup-created', (e) => {
  const { strokeColor, position, record } = e.detail;
  saveMarkup({ strokeColor, position, record });
});

// Vue 2:框选完成(落库 info 即可,含 1-based pageIndex + 归一化坐标)
el.addEventListener('chapter-box-select-end', (e) => {
  saveBoxSelect(e.detail.info);
});

// 运行时更新 PDF 页背景色
el.dispatchEvent(
  new CustomEvent('chapter-set-pdf-page-background', {
    detail: { background: '#1e293b' },
    bubbles: true,
    composed: true,
  }),
);
el.addEventListener('chapter-pdf-page-background-change', (e) => {
  console.log('page background', e.detail.background);
});

markup record 顶层额外带 localPageIndexglobalPageIndexglobalPageNumber。宿主若持有 ChapterPdfViewer ref 或兼容的滚动对象,可直接调用 scrollToMarkupRecord(viewer, markupRecord)scrollToNoteRecord(viewer, noteRecord)scrollToBookmarkRecord(viewer, bookmarkRecord)scrollToPageOverlayRecord(viewer, overlayRecord)scrollToBackendPageOverlayItem(viewer, backendItem) 回到对应位置(内部会先滚到页、等 PDF 真实尺寸加载后再精确定位 Y,与 QR overlay 一致)。

外部列表删除标记后,需要同步移除渲染器内存里的对象。snippet 没有 ChapterPdfViewer ref,可在 chapter-viewer-ready 里保存 registry 后调用插件 capability:

import { removeChapterMarkupAnnotation } from '@embedpdf-editor/chapter-snippet';

let registry;
el.addEventListener('chapter-viewer-ready', (event) => {
  registry = event.detail.registry;
});

async function deleteNote(noteId) {
  await api.deleteNote(noteId);
  registry?.getPlugin('note')?.provides()?.removeNote(noteId);
}

async function deleteBookmark(bookmarkId) {
  await api.deleteBookmark(bookmarkId);
  registry?.getPlugin('paragraph-bookmark')?.provides()?.purgeBookmark(bookmarkId);
}

async function deleteMarkup(record) {
  await api.deleteMarkup(record.nodeId ?? record.annotation.id);
  if (registry) await removeChapterMarkupAnnotation(registry, record);
}

removeChapterMarkupAnnotation(registry, record) 会把 chapterId + localPageIndex 解析成实际 documentId + pageIndex,分段 PDF 也可直接使用。

附注创建:宿主弹窗 → 保存 → 调用 complete({ noteId, content?, nodeId? }) 注册锚点(也可事先写 record.nodeId)。划线 onCreated、书签 onAdded 同样可写 record.nodeIdreturn { nodeId }。编辑/删除 DOM 事件与回调均带 nodeId
完整回调表:11-events-callbacks-and-component-api.md

Vue 2 用法

Vue 2 组件只需要在 mounted 时初始化,在 beforeDestroy 时销毁:

import ChapterEmbedPDF from '@embedpdf-editor/chapter-snippet';

export default {
  props: {
    options: { type: Object, required: true },
  },
  mounted() {
    this.viewer = ChapterEmbedPDF.init({
      type: 'container',
      target: this.$refs.host,
      options: this.options,
      worker: true,
    });
  },
  beforeDestroy() {
    this.viewer?.destroy?.();
  },
};

完整示例见 examples/chapter-viewer-demo-vue2

标注导入导出

包内直接导出章节标注 IO:

import {
  exportAllChapterAnnotations,
  importChapterAnnotationsArchive,
  chapterAnnotationsArchiveToJson,
  parseChapterAnnotationsArchiveJson,
} from '@embedpdf-editor/chapter-snippet';

在 snippet 中可通过 chapter-viewer-ready 事件拿到 registry,再调用这些 API。导出格式包含 bookmarksnotesmarkuppageOverlaysboxSelects,版本常量为 CHAPTER_ANNOTATIONS_ARCHIVE_VERSION

| 选项 | 说明 | | --- | --- | | mode | replace 清空后导入;merge 合并 | | ensureChapterLoaded | 默认 true;含 markup 的分段章会加载 全部段 再合并页码 | | bookmarks / notes / markup / pageOverlays / boxSelects | 默认 true,可关闭某一类 | | persistNotes / persistBookmarks | 导入后写回业务存储 |

示例见 examples/chapter-viewer-demo-vue2/src/components/AnnotationsDemoBar.vue。详见 10-annotations-io.md

教程索引

| 主题 | 文档 | | --- | --- | | 目录 | docs/get-started/README.md | | wasmUrl(含 OSS 示例) | 01-installation.md | | 划词复制 | 07-selection-toolbar.md | | 事件常量 | 11-events-callbacks-and-component-api.md | | 分段 + 按章存储 | 12-segmented-pdf-and-per-chapter-storage.md | | 页内矩形叠加层 | 13-page-overlays.md |

与 React / Vue3 渲染器的区别

| 项 | React / Vue3 | chapter-snippet(本包,含 Vue 2) | | --- | --- | --- | | 运行时 | 框架内 editor-engine 组件 | Shadow DOM Web Component + Preact | | 初始化 | <ChapterPdfViewer options={...} /> | ChapterEmbedPDF.init({ type: 'container', target, options }) | | 动态 manifest | 改 options.manifest(自动 setManifest) | 改 initoptions.manifest(Preact 内同步) | | Registry | onInitialized / useRegistry() | chapter-viewer-readydetail.registry | | Vite | chapterViewerViteResolve() | chapterSnippetViteResolve() |

许可

  • 本包 JavaScript / 类型定义:MIT(见 LICENSENOTICE
  • dist/pdfium.wasm:来自 PDFium,适用 Apache License 2.0(见 LICENSE.pdfiumNOTICE.pdfiumTHIRD-PARTY-NOTICES.md
  • 再分发本包或随产品打包 wasm 时,须保留上述许可文件

常见问题

| 现象 | 处理 | | --- | --- | | 卡在“正在加载 PDFium” | 检查 dist/pdfium.wasm 是否随包发布并能被浏览器访问;自定义部署时传 wasmUrl | | 卡在“正在加载 xxx.pdf” | 优先确认章节 PDF URL 是否 200;如只在 worker 模式出现,可保留默认 fallback 或临时设 worker: false 定位环境问题 | | 只显示空白 | 确认宿主元素有高度,且没有被父容器 overflow/flex 布局压到 0 | | 划词后没有笔记弹窗 | 监听 chapter-note-request-create,保存后调用 detail.complete({ noteId, ... }) | | Vue/Vite 开发环境异常预构建 | 使用 chapterSnippetViteResolve(),确保 snippet 和 PDFium 引擎没有被 optimizeDeps 预构建 | | 分段章存了 #sN | 业务层只用 chapterId;引擎段 ID 勿写入库 |