diff --git a/docs/mobile-pdf-text-selection-fix.md b/docs/mobile-pdf-text-selection-fix.md
new file mode 100644
index 000000000..c7b3867ac
--- /dev/null
+++ b/docs/mobile-pdf-text-selection-fix.md
@@ -0,0 +1,208 @@
+# 移动端 PDF 文字选择修复 + 统一 pdfjs-dist 版本
+
+> 日期:2026-08-01
+> 范围:仅基于 `main` 分支(`9c601b82` 桌面修复之后);不依赖 `fix/android-pdf-custom-selection(-publish)` 等未合入 main 的分支。
+> 参考实现:readest(`packages/foliate-js/pdf.js` 的 `getFontScale` / `MAX_RENDER_DPR` / `MAX_CANVAS_PIXELS` / `isMobileWebView` workaround)。
+
+---
+
+## 一、问题现象
+
+- **移动端**(Expo + react-native-webview)打开 PDF 书时**无法选中文字**:长按不出选区、拖选高亮与字形错位。
+- **桌面端**(macOS/PC,Tauri)PDF 文字选择已修好(`9c601b82`),移动端仍坏。
+- 另外全仓 `pdfjs-dist` 版本混乱(`app`/`cli`/`core` 为 `^5.5.207`,`foliate-js` 为 `^4.7.76`,解析为 4.10.38,还有 npm 遗留的 `package-lock.json` 钉死旧版)。
+
+---
+
+## 二、根源分析(Root Cause)
+
+### 根因 1:移动端加载的是过期的构建产物
+
+移动端 `ReaderScreen.tsx:165` 用 `Asset.fromModule(require("../../assets/reader/reader.html"))` 加载**提交进 git 的 bundle**(`reader.html`),而不是像桌面端 vite 那样直接打包当前源码。
+
+| | 桌面端 | 移动端 |
+|---|---|---|
+| 加载方式 | vite 实时打包当前源码 | 提交的静态 `reader.html` |
+| 最后构建 | 随源码 | **2026-07-04**(`4d25e6bb`),早于桌面修复 22 天 |
+| 内嵌 pdfjs | 5.5.207 | **4.10.38** |
+| 渲染方式 | 显示坐标渲染(canvas 过采样 + CSS 盒为显示尺寸,`9c601b82` 重写的 `render()`) | 旧的 `documentElement.style.transform = scale(1/devicePixelRatio)` 缩放 |
+
+旧 bundle 用的缩放方式是 readest `de6d810` 点名批评的"**misplacing text selection**"写法:缩放文档根元素而不是只放大 canvas 位图,导致选区/标注工具栏定位错位。**桌面能修好是因为 vite 直接打包了当前源码;移动端 bundle 22 天没重建,等于桌面修复完全没进移动端。**
+
+> **版本差异澄清**:`includeMarkedContent`/`dontFlip` 并不是 4.10.38 → 5.5.207 的差异 —— 两个版本的 bundle 里都有这些字符串(它们本来就是 pdf.js API 选项)。真正的差异是 foliate-js 的 `render()` 在 `9c601b82` 的重写(canvas 过采样 + CSS 盒显示尺寸 + 选择/复制逻辑),桌面修复的正是这部分,而移动端 bundle 22 天没重建,所以桌面修复完全没进去。
+
+### 根因 2:模板把 reflow 样式注入 PDF iframe(独立新 bug)
+
+即使重建 bundle,`reader.template.html` 仍有第二个问题:它把 EPUB 的 reflow 排版样式**注入到每个 doc 的 iframe**,包括 PDF 的 fixed-layout iframe:
+
+- `applySettings()` 的 `baseStyles` 注入 `font-size: ${fSize}px !important`、`font-family: var(--readany-font-family) !important`、`line-height` —— 而 **pdf.js 的 TextLayer 字形字号完全由 CSS 变量计算**(`font-size: calc(var(--text-scale-factor) * var(--font-height))`),注入会击穿这些计算,让选区错位。注意这一点**并非 5.5.207 版本特有**:旧 4.10.38 bundle 里的 CSS 变量 text layer 同样会被击穿 —— 这恰恰**加强了**根因 2(无论版本,模板注入 reflow 样式都会破坏 PDF TextLayer,只是之前被旧 bundle 的缩放 bug 掩盖了)。
+- `applyDocStyles()` 注入 `p, div, span … { color: inherit !important }` —— 覆盖 `.textLayer span { color: transparent }`,让 PDF 透明字形**显形**叠在 canvas 上。
+
+桌面端 `FoliateViewer.tsx:3275-3284` 早已有这个 `isFixedLayout` 守卫(注释写明"否则会破坏 PDF TextLayer 定位"),**移动端模板缺这个守卫**。
+
+### 根因 3(潜在):移动端缺少 readest 的移动端内存/字号 workaround
+
+即便前两个修好,还有两个移动端特有的问题:
+
+- **iOS 辅助功能"大字号"**:系统字号缩放会把 WebView 里所有文字(包括透明的 TextLayer 字形)按 `fontScale` 放大,但 **canvas 位图不变** → 字形比 canvas 上的大 `fontScale` 倍,选区整体偏大、下移。
+- **iOS WKWebView ~2GB 内存天花板**:iPad 真机 dpr=3,canvas 位图 + WebKit backing layer 的内存按 **dpr 的平方**增长,翻几页就 WebContent OOM 崩溃。
+
+---
+
+## 三、解决方案(怎么做)
+
+### Part 1 — 修复核心:`packages/foliate-js/pdf.js` 加入移动端 workaround
+
+参考 readest,在 pdf.js 渲染路径上加三个函数:
+
+1. **`getFontScale(doc)`**:插入一个 `font-size:100px; line-height:1; text-size-adjust:none` 的探测 div,用 `offsetHeight / 100` 量出系统字号缩放比(`offsetHeight` 反映 OS 字号缩放,但不反映 dpr 和 CSS transform,能精确隔离它)。
+
+2. **`isMobileWebView()`**:UA 含 `Android|iPhone|iPad|iPod`,或 `Macintosh + navigator.maxTouchPoints > 1`(覆盖 iPadOS 伪装桌面 UA 的情况)。
+
+3. **`getRenderDpr(page, zoom)`**:移动端 dpr 钳到 `MAX_RENDER_DPR = 2`,且按单页 canvas 面积上限 `MAX_CANVAS_PIXELS = 2048*1536` 降采样;桌面端直接返回真实 dpr。
+
+在 `render(page, doc, zoom)` 内接入:
+
+- `outputScale = devicePixelRatio` → `renderDpr = getRenderDpr(page, zoom)`,canvas 的 `width/height/transform` 全部改用它。**保持"canvas 过采样 + CSS 盒是显示尺寸"的结构不变**,只是把过采样系数换成了受钳制的 `renderDpr` —— 等价 readest 的 renderViewport/displayViewport 分离,但不重写已经工作的桌面路径。
+- `textContainer.replaceChildren()` 之后、`textLayer.render()` 之前:先清除旧的 inline `--text-scale-factor`,再在 `fontScale !== 1` 时设为 `calc(var(--total-scale-factor) * var(--min-font-size) / ${fontScale})` —— **只除字形字号杠杆,不动位置杠杆**(`--total-scale-factor`)。这保证同一 iframe 从大字号回到 `fontScale === 1` 时不会保留旧除数。
+- 补健壮性:annotation linkService 加 `getAnchorUrl: () => ""`(pdf.js AnnotationLayer 对 named-action 注解会调用,缺失会 reject)。
+
+### Part 2 — 模板层加 fixed-layout 样式注入守卫(对照桌面)
+
+文件:`packages/app-expo/assets/reader/reader.template.html`
+
+新增 `buildFixedLayoutStyles(bg, fg, primary)` —— 只含安全块:
+
+- `:root { --readany-font-family: … }`
+- `html, body` 的 `background-color` / `color`
+- `.textLayer, .textLayer :is(span, br) { color: transparent !important; }`(保住 TextLayer 字形透明)
+- `::selection` 高亮色
+
+三个注入路径都加 `view.isFixedLayout` 守卫:
+
+| 路径 | 原行为 | 改后(fixed-layout) |
+|---|---|---|
+| `applySettings()` | 注入含 font-size/font-family/line-height 的 `baseStyles` | 用 `buildFixedLayoutStyles`,且**不经过** `injectThemeIntoStyles`(避免重新引入 `color: inherit`) |
+| `applyDocStyles()` | 注入 reflow 样式 + `color: inherit !important` | 用安全块 + `* { user-select: text }` 等对选择有益的部分,去掉 reflow |
+| `setThemeColors()` | `injectThemeIntoStyles` 重新包装 `lastRendererStyles` | 直接用新颜色重建安全块 |
+
+### Part 3 — 重建并提交 `reader.html`
+
+改完源码后执行 `node packages/app-expo/scripts/build-reader.js` 重新生成 `reader.html` 并**提交**(维持"提交产物"机制,`Asset.fromModule` 需要 Metro 构建期的静态资源)。
+
+**加固构建脚本**(`build-reader.js`):build id 从硬编码 `'android-local-server-cors'` 改为待注入 id 的完整 HTML(模板 + bundle)的确定性 SHA-256,注入模板里的 `__READANY_READER_BUILD_ID = 'build-id-placeholder'`。相同源码和依赖会生成字节相同的 `reader.html`;重建后的 id 或文件差异可诊断 bundle 漂移,且不依赖无法包含生成产物的 commit SHA。
+
+### Part 4 — 统一 pdfjs-dist 版本
+
+1. `packages/foliate-js/package.json`:`"pdfjs-dist": "^4.7.76"` → `"^5.5.207"`(保留在 devDependencies,foliate-js 是 workspace 源码包)。
+2. **删除** `packages/foliate-js/package-lock.json`(npm 遗留物,钉死 4.7.76)。
+3. `pnpm install` 重新生成 `pnpm-lock.yaml`,移除 `pdfjs-dist@4.10.38` 条目。
+4. 清理死配置:
+ - `packages/app/vite.config.ts`:删除 `@pdfjs` alias(指向不存在的 `foliate-js/vendor/pdfjs`)和 `optimizeDeps.exclude`。
+ - `packages/foliate-js/rollup.config.js`:删除 `copyPDFJS()`(把 pdfjs 复制到无人引用的 `vendor/pdfjs/`)及未用依赖 `fs-extra`。
+
+---
+
+## 四、为什么这么改(设计决策)
+
+1. **只在 foliate-js/pdf.js 层加 workaround,不动渲染架构**:桌面端已修好且经测试,重写成 readest 的 renderViewport/displayViewport 双视口方案风险大。用 `getRenderDpr` 替换 `outputScale` 是**最小侵入** —— 桌面 `isMobileWebView() === false`,渲染路径与改动前完全一致,**零桌面回归**。
+
+2. **`fontScale` 只除字形字号杠杆(`--text-scale-factor`)**:TextLayer 有两个 CSS 变量 —— `--total-scale-factor` 管位置、`--text-scale-factor` 管字形字号。OS 字号缩放只影响字形(font-size),所以只除字形杠杆,位置(随 `--total-scale-factor`)不受影响,任何字号设置下选区都能与 canvas 对齐。
+
+3. **移动端 dpr 钳到 2 而不是 3**:canvas 位图和 WebKit backing layer 内存随 dpr **平方**增长,dpr=3 → 2 省 ~2.25 倍每页内存,仍 retina 清晰(可选的透明文字层是独立 DOM 层不受影响)。**桌面不钳**:桌面无每进程内存天花板,钳制只买来模糊(readest #5251)。iPadOS 伪装桌面 UA,所以用 `maxTouchPoints` 补识别。
+
+4. **fixed-layout 守卫采用"跳过 + 白名单"而不是"硬编码 PDF 特判"**:用 `view.isFixedLayout`(view.js 对 PDF/CBZ 已置位)判 `isFixedLayout`,不硬编码 `pdf.js`。这样 EPUB/CBZ 的 reflow 不受影响,只有 PDF 走安全块。
+
+5. **不经过 `injectThemeIntoStyles`**:那个函数会加 `color: inherit` 规则,正是让 PDF TextLayer 字形显形的元凶;fixed-layout 的安全块已经自包含主题色,直接注入即可。
+
+6. **继续提交 `reader.html` 产物**:`Asset.fromModule(require(...))` 需要 Metro 构建期静态资源,改动 `require` 为动态加载成本高、收益低;EAS 构建已有 `eas-build-post-install` 再跑一次 `build:reader` 双保险。加固 build id 让"过期 bundle"这种事故可检测。
+
+7. **版本统一到 `^5.5.207`**:桌面端已在用且验证过;旧 `^4.7.76` 既没有 `includeMarkedContent`/`dontFlip` 等新逻辑,也靠 hoist 巧合才解析到根 node_modules 的 5.5.207,是隐藏的时炸弹。
+
+---
+
+## 五、改了什么内容(Diff 清单)
+
+### 源码改动(9 个文件)
+
+| 文件 | 改动 |
+|---|---|
+| `packages/foliate-js/pdf.js` | +88 行。新增 `getFontScale` / `isMobileWebView` / `getRenderDpr` / `MAX_RENDER_DPR=2` / `MAX_CANVAS_PIXELS=2048*1536`;`render()` 里 `outputScale` → `renderDpr`;每次重建 TextLayer 前重置并按当前 fontScale 校正字号变量;annotation linkService 补 `getAnchorUrl` |
+| `packages/app-expo/assets/reader/reader.template.html` | +112 行。新增 `buildFixedLayoutStyles()`;`applySettings()` / `applyDocStyles()` / `setThemeColors()` 三处加 `view.isFixedLayout` 守卫,fixed-layout 不注入 reflow 样式、不走 `injectThemeIntoStyles`;build id 占位符改 `'build-id-placeholder'`。**复核修复**:`applyDocStyles` 的 fixed-layout 分支改同步 `fixedLayoutStyles`(当前主题色)而非 `lastRendererStyles`,修复 EPUB→PDF 同 WebView 切换时陈旧 reflow 样式泄漏进 PDF 第一页(见"五·补") |
+| `packages/app-expo/assets/reader/reader.html` | 重建产物。内嵌 pdfjs **5.5.207** + 全部新 workaround;旧的 `scale(1/devicePixelRatio)` 缩放消失 |
+| `packages/app-expo/scripts/build-reader.js` | +24 行。`getBuildId(html)` 以待注入 id 的完整 HTML 计算确定性 SHA-256,并替换模板中的 build id 占位符 |
+| `packages/foliate-js/package.json` | `pdfjs-dist ^4.7.76` → `^5.5.207`;删除未用的 `fs-extra` |
+| `packages/foliate-js/rollup.config.js` | 删除 `copyPDFJS()` 插件及 `fs-extra` import |
+| `packages/foliate-js/package-lock.json` | **删除**(npm 遗留,钉死 4.7.76) |
+| `packages/app/vite.config.ts` | 删除死 `@pdfjs` alias(指向不存在的 `foliate-js/vendor/pdfjs`)和 `optimizeDeps.exclude` |
+| `pnpm-lock.yaml` | 重新生成;`pdfjs-dist@4.10.38` / `pdfjs-dist@4.7.76` 全部消失,foliate-js importer → 5.5.207 |
+
+### 验证结果
+
+| 检查 | 结果 |
+|---|---|
+| core 测试(vitest) | ✅ 565 passed |
+| cli 构建 | ✅ exit 0 |
+| app(vite)构建 | ✅ exit 0 |
+| biome lint | ✅ 通过 |
+| `reader.html` 可复现性 | ✅ 连续两次 `pnpm --filter @readany/app-expo build:reader` 产物字节相同;当前 id 为 `sha256-b06a46ef05bc16354adb4ea35e2267d3b61ba8a65a2d07dc579aa4f39c57873d` |
+| bundle 标记 | ✅ 含 probe `font-size:100px` / `maxTouchPoints` / `2048*1536` / `5.5.207`,旧 `scale(1/dpr)` 已消失(注:esbuild 压缩后函数名被改名,`getFontScale`→`dJ` 等,核对需按代码特征而非名字) |
+| lockfile | ✅ 无 4.x pdfjs 残留 |
+
+---
+
+## 五·补、独立复核发现与修复(2026-08-01)
+
+对本文档的分析/方案及代码实现做了三路独立复核(pdf.js 实现正确性 / 模板守卫与构建脚本 / 文档与依赖改动),并逐一从实际代码验证。结论:
+
+| 复核维度 | 结论 |
+|---|---|
+| pdf.js 实现 | ✅ 正确,桌面零回归成立 |
+| 文档 + 依赖 | ✅ 大部分准确(3 处小错已修订),方案可行 |
+| 模板守卫 + 构建脚本 | ⚠️ With fixes → **发现并修复 1 个 Important 缺陷** |
+
+### 复核确认的关键点(此前未写进正文)
+
+1. **bundle 过期与重建可证**:旧 bundle 的确含 `documentElement.style.transform = scale(${1/devicePixelRatio...` 且 build id 为 `'android-local-server-cors'`;新 bundle 的 probe/`maxTouchPoints`/`2048*1536`/`/ ${d}` 等字符串只存在于未提交的 `pdf.js` 源码 → **证明 bundle 确由当前工作区重建**。
+2. **桌面零回归成立**:Tauri macOS WKWebView UA 是 `Macintosh` 但 `maxTouchPoints=0`(触控板不算触控点),Windows 无 mobile token → `getRenderDpr` 短路返回真实 dpr,与旧 `outputScale` 逐字节等价。
+3. **fontScale 校正不会被丢**:pdf.js `TextLayer.render` 只写 `--min-font-size`/`--font-height`,从不写 `--text-scale-factor`;span 字号是活的 var 引用,改 var 后全部 span 重新流式。
+4. **`MAX_CANVAS_PIXELS` 并非硬上限**(Important):`zoom²·dpr²` 把 dpr 压到 1 以下时被 `Math.max(1, dpr)` 挡住,重度放大的平板页仍可能超预算。与 readest 相同、且远好于旧 bundle 的无钳制 dpr-3,不算回归,但注释里"hard ceiling"措辞偏乐观 —— 已接受。
+5. **`getFontScale` 量化到整数 CSS px**:iOS Dynamic Type 档位(1.15/1.3…)够用,但不是连续测量 —— 已接受。
+
+### Important 缺陷:`lastRendererStyles` 陈旧泄漏(已修复)
+
+- **根因**:`lastRendererStyles`(模板 L197)是模块级变量,`openBook()` 里**从未重置**;而 `FixedLayout.getContents()`(foliate-js/fixed-layout.js:351)返回的项没有 `index`,模板 `getRendererContents()`(L566)过滤 `content.index == null` → **fixed-layout 下返回空数组**,`syncReaderOverrideStylesForAllDocs()` 对 PDF 恒为 no-op。
+- **触发场景**:**同一 WebView 内 EPUB → PDF 切换**(ReaderScreen 在 `bookId` 变化时复用同一 Reader 挂载,如 TTS 跳转/深链)。EPUB 先打开把 `lastRendererStyles` 设为 reflow 样式(font-size/font-family/line-height `!important`);PDF 第一页 `load` → `applyDocStyles` 把**陈旧的 reflow 样式**注入第一页;之后 `applySettings` 更新了 `lastRendererStyles`,但同步对 fixed-layout 是 no-op → **第一页永久残留 reflow 覆盖,TextLayer 字号被击穿**。
+- **修复**:`applyDocStyles` 的 fixed-layout 分支改同步**刚计算的 `fixedLayoutStyles`**(总是取自当前主题色,免疫陈旧状态)而非 `lastRendererStyles`。已重建 bundle 验证 `syncReaderOverrideStylesForDoc(doc, fixedLayoutStyles)` 在内。
+
+### 复核记录的其他事项
+
+- **Minor — `setThemeColors` fixed-layout 分支是死代码**:`FixedLayout` 没有 `setStyles`(只有 `Paginator` 有),`if (view.renderer.setStyles)` 对 PDF/CBZ 恒 false。与改动前行为一致(非回归),但注释"rebuild the safe block with the new colors"言过其实;主题变更不重绘 PDF(改动前亦如此)。**未改,仅记录。**
+- **Minor — 选区颜色不一致**:fixed-layout 用蓝 `rgba(59,130,246,0.3)`,reflow 用黄 `rgba(250,204,21,0.4)`。PDF 选区高亮与 EPUB 不同。**未改,仅记录。**
+- **Minor — build-reader.js 的 `replace` 不匹配时静默失败**:模板占位符若漂移,bundle 会带着 `'build-id-placeholder'` 发出且构建期无警告。**未改,仅记录。**
+
+### build-id 与可复现性(重要)
+
+当前 bundle build id 是 `sha256-b06a46ef05bc16354adb4ea35e2267d3b61ba8a65a2d07dc579aa4f39c57873d`。它是待注入 id 的完整 HTML(模板 + bundle)的内容 SHA-256,不含 git SHA 或时间戳;因此相同源码和依赖重建会得到相同的 `reader.html` 与 `[ReaderBuild]` 值。提交产物后不需要为了匹配新的 commit SHA 而额外重建;若重建后的 id 或文件内容变化,则说明提交的 bundle 与当前构建输入发生了漂移。
+
+---
+
+## 六、剩余验证(需真机/模拟器)
+
+开发环境无法启动模拟器,以下需人工确认:
+
+1. **桌面回归**:开 PDF,验证文字选择对齐、跨行复制、双页 spread 无缝隙、缩放清晰度与改前一致(理论上零回归,桌面 `isMobileWebView() === false`)。
+2. **移动端**(`expo run:ios` / `android:dev`):
+ - iPhone 真机多页 PDF:长按选词、拖选跨行,选区与字形严格对齐,复制进工具条。
+ - iOS 设置 → 辅助功能 → 显示与文字大小 → 大字号 → 重开 PDF:选区仍对齐(getFontScale);EPUB 不受影响。
+ - iPad(dpr=3):连续翻页/缩放不触发 WebContent 崩溃(getRenderDpr 钳制)。
+ - Android:选择 + 复制正常,无 OOM。
+ - 回归:EPUB 选择/注解/TTS、PDF 翻页/TOC/搜索/章节提取正常,`.textLayer span color` 修复后无可见文本叠加。
+
+---
+
+## 七、涉及的关键概念
+
+- **pdf.js TextLayer**:canvas 是位图不可选,TextLayer 是在 canvas 上叠一层透明的 DOM 文字(span),靠 CSS 变量算字号/位置,天然支持浏览器原生选择。**视图层(canvas)与 TextLayer 必须严格一致**,任何一方被额外缩放/注入样式都会错位。
+- **`--total-scale-factor` vs `--text-scale-factor`**:前者管位置(百分比容器缩放),后者管字形字号(font-size)。修复时只动字形杠杆。
+- **fixed-layout vs reflow**:PDF/CBZ 是 fixed-layout(每页固定尺寸的 iframe),EPUB 是 reflow(流式排版,可注入字号/行距)。reflow 样式注入 fixed-layout 文档会破坏 TextLayer。
diff --git a/packages/app-expo/assets/reader/reader.html b/packages/app-expo/assets/reader/reader.html
index 067a35347..7ac8ca2fc 100644
--- a/packages/app-expo/assets/reader/reader.html
+++ b/packages/app-expo/assets/reader/reader.html
@@ -171,7 +171,9 @@
function postToRN(type, data) {
if (RN) RN.postMessage(JSON.stringify({ type, ...data }));
}
- window.__READANY_READER_BUILD_ID = 'android-local-server-cors';
+ // build-reader.js stamps a deterministic content id into this line; a stale
+ // committed bundle is detectable via the RN 'debug' message.
+ window.__READANY_READER_BUILD_ID = "sha256-b06a46ef05bc16354adb4ea35e2267d3b61ba8a65a2d07dc579aa4f39c57873d";
postToRN('debug', { message: '[ReaderBuild] ' + window.__READANY_READER_BUILD_ID });
Promise.withResolvers ??= function () {
let resolve;
@@ -1773,6 +1775,30 @@
}
// ─── Settings ───
+
+ // Safe style block for fixed-layout docs (PDF/CBZ). PDF.js TextLayer glyphs
+ // are transparent spans whose size/position are computed from CSS variables;
+ // injecting font-size/font-family/line-height or `color: inherit` would
+ // break glyph sizing and make the text layer visible over the canvas.
+ function buildFixedLayoutStyles(bg, fg, primary) {
+ return `
+ :root {
+ --readany-font-family: -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
+ }
+ html, body {
+ background-color: ${bg} !important;
+ color: ${fg} !important;
+ }
+ .textLayer,
+ .textLayer :is(span, br) {
+ color: transparent !important;
+ }
+ ::selection {
+ background: rgba(59, 130, 246, 0.3) !important;
+ }
+ `;
+ }
+
function applySettings(settings) {
if (!view || !view.renderer) return;
const renderer = view.renderer;
@@ -1836,7 +1862,19 @@
const layoutScale = currentFontSize / BASELINE_FONT_SIZE;
const ps = Math.round(currentParagraphSpacing * layoutScale);
- const baseStyles = `
+ // Fixed layout (PDF/CBZ): never inject font-size/font-family/line-height
+ // reflow overrides — they would break the pdf.js TextLayer (glyph sizes
+ // are computed from CSS variables in PDF.js 5.x, not set inline). Only a
+ // theme-safe block is used, mirroring the desktop FoliateViewer
+ // fixed-layout guard.
+ const isFixedLayout = !!(view && view.isFixedLayout);
+ const baseStyles = isFixedLayout
+ ? buildFixedLayoutStyles(
+ currentThemeColors.bg,
+ currentThemeColors.fg,
+ currentThemeColors.primary,
+ )
+ : `
:root {
--readany-font-family: ${fontFamily};
}
@@ -1897,12 +1935,17 @@
${getVerticalWritingCompatibilityStyles()}
`;
lastRendererStyles = baseStyles;
- const themedStyles = injectThemeIntoStyles(
- baseStyles,
- currentThemeColors.bg,
- currentThemeColors.fg,
- currentThemeColors.primary,
- );
+ // For fixed layout baseStyles is already a self-contained safe block;
+ // re-wrapping it with injectThemeIntoStyles would re-add the
+ // `color: inherit` rule that makes PDF TextLayer glyphs visible.
+ const themedStyles = isFixedLayout
+ ? baseStyles
+ : injectThemeIntoStyles(
+ baseStyles,
+ currentThemeColors.bg,
+ currentThemeColors.fg,
+ currentThemeColors.primary,
+ );
if (typeof renderer.setStyles === 'function') {
renderer.setStyles(themedStyles);
}
@@ -2055,8 +2098,15 @@
document.body.style.color = fg;
if (view && view.renderer && view.renderer.setStyles) {
- const currentStyles = lastRendererStyles || '';
- const themedStyles = injectThemeIntoStyles(currentStyles, bg, fg, primary);
+ const isFixedLayout = !!(view && view.isFixedLayout);
+ // For fixed layout, lastRendererStyles carries baked-in theme colors, so
+ // rebuild the safe block with the new colors; injectThemeIntoStyles
+ // would re-add the `color: inherit` rule that makes PDF TextLayer glyphs
+ // visible.
+ const themedStyles = isFixedLayout
+ ? buildFixedLayoutStyles(bg, fg, primary)
+ : injectThemeIntoStyles(lastRendererStyles || '', bg, fg, primary);
+ lastRendererStyles = themedStyles;
view.renderer.setStyles(themedStyles);
syncReaderOverrideStylesForAllDocs(themedStyles);
}
@@ -2213,6 +2263,15 @@
}
function applyDocStyles(doc) {
+ const isFixedLayout = !!(view && view.isFixedLayout);
+ // Fixed layout (PDF/CBZ): the pdf.js TextLayer glyphs are transparent spans
+ // positioned by CSS variables — `color: inherit` would make them visible and
+ // reflow overrides would misalign selection. Keep only the safe baseline.
+ const fixedLayoutStyles = buildFixedLayoutStyles(
+ currentThemeColors.bg,
+ currentThemeColors.fg,
+ currentThemeColors.primary,
+ );
syncCustomFontStylesForDoc(doc, currentCustomFontFaceCSS);
const verticalDoc = isVerticalDoc(doc);
if (!verticalDoc) {
@@ -2225,7 +2284,18 @@
}
const style = doc.createElement('style');
const { bg, fg, primary } = currentThemeColors;
- style.textContent = `
+ style.textContent = isFixedLayout
+ ? `
+ * { -webkit-touch-callout: none !important; -webkit-user-select: text !important; user-select: text !important; }
+ html, body { background-color: ${bg} !important; color: ${fg} !important; }
+ img, picture, svg, image, canvas {
+ -webkit-user-drag: none !important;
+ touch-action: manipulation !important;
+ }
+ ${fixedLayoutStyles}
+ ${verticalDoc ? getVerticalWritingCompatibilityStyles() : ''}
+ `
+ : `
* { -webkit-touch-callout: none !important; -webkit-user-select: text !important; user-select: text !important; }
html, body { background-color: ${bg} !important; color: ${fg} !important; }
img, picture, svg, image, canvas {
@@ -2239,13 +2309,25 @@
`;
doc.head.appendChild(style);
if (lastRendererStyles) {
- const themedStyles = injectThemeIntoStyles(
- lastRendererStyles,
- currentThemeColors.bg,
- currentThemeColors.fg,
- currentThemeColors.primary,
- );
- syncReaderOverrideStylesForDoc(doc, themedStyles);
+ if (isFixedLayout) {
+ // For PDF/CBZ, sync the freshly computed safe block instead of
+ // lastRendererStyles: lastRendererStyles is never reset in openBook,
+ // so after an EPUB→PDF switch in the same WebView it can still hold
+ // stale reflow styles (font-size/font-family/line-height !important)
+ // that would break the pdf.js TextLayer. fixedLayoutStyles is derived
+ // from the current theme colors, so it is always correct. Re-wrapping
+ // it with injectThemeIntoStyles would re-add `color: inherit`, so
+ // inject it verbatim.
+ syncReaderOverrideStylesForDoc(doc, fixedLayoutStyles);
+ } else {
+ const themedStyles = injectThemeIntoStyles(
+ lastRendererStyles,
+ currentThemeColors.bg,
+ currentThemeColors.fg,
+ currentThemeColors.primary,
+ );
+ syncReaderOverrideStylesForDoc(doc, themedStyles);
+ }
}
doc.addEventListener('contextmenu', (e) => { e.preventDefault(); e.stopPropagation(); return false; }, true);
}
@@ -4693,8 +4775,8 @@
diff --git a/packages/app-expo/assets/reader/reader.template.html b/packages/app-expo/assets/reader/reader.template.html
index 7a981ea1d..7b79d5f16 100644
--- a/packages/app-expo/assets/reader/reader.template.html
+++ b/packages/app-expo/assets/reader/reader.template.html
@@ -171,7 +171,9 @@
function postToRN(type, data) {
if (RN) RN.postMessage(JSON.stringify({ type, ...data }));
}
- window.__READANY_READER_BUILD_ID = 'android-local-server-cors';
+ // build-reader.js stamps a deterministic content id into this line; a stale
+ // committed bundle is detectable via the RN 'debug' message.
+ window.__READANY_READER_BUILD_ID = 'build-id-placeholder';
postToRN('debug', { message: '[ReaderBuild] ' + window.__READANY_READER_BUILD_ID });
Promise.withResolvers ??= function () {
let resolve;
@@ -1773,6 +1775,30 @@
}
// ─── Settings ───
+
+ // Safe style block for fixed-layout docs (PDF/CBZ). PDF.js TextLayer glyphs
+ // are transparent spans whose size/position are computed from CSS variables;
+ // injecting font-size/font-family/line-height or `color: inherit` would
+ // break glyph sizing and make the text layer visible over the canvas.
+ function buildFixedLayoutStyles(bg, fg, primary) {
+ return `
+ :root {
+ --readany-font-family: -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
+ }
+ html, body {
+ background-color: ${bg} !important;
+ color: ${fg} !important;
+ }
+ .textLayer,
+ .textLayer :is(span, br) {
+ color: transparent !important;
+ }
+ ::selection {
+ background: rgba(59, 130, 246, 0.3) !important;
+ }
+ `;
+ }
+
function applySettings(settings) {
if (!view || !view.renderer) return;
const renderer = view.renderer;
@@ -1836,7 +1862,19 @@
const layoutScale = currentFontSize / BASELINE_FONT_SIZE;
const ps = Math.round(currentParagraphSpacing * layoutScale);
- const baseStyles = `
+ // Fixed layout (PDF/CBZ): never inject font-size/font-family/line-height
+ // reflow overrides — they would break the pdf.js TextLayer (glyph sizes
+ // are computed from CSS variables in PDF.js 5.x, not set inline). Only a
+ // theme-safe block is used, mirroring the desktop FoliateViewer
+ // fixed-layout guard.
+ const isFixedLayout = !!(view && view.isFixedLayout);
+ const baseStyles = isFixedLayout
+ ? buildFixedLayoutStyles(
+ currentThemeColors.bg,
+ currentThemeColors.fg,
+ currentThemeColors.primary,
+ )
+ : `
:root {
--readany-font-family: ${fontFamily};
}
@@ -1897,12 +1935,17 @@
${getVerticalWritingCompatibilityStyles()}
`;
lastRendererStyles = baseStyles;
- const themedStyles = injectThemeIntoStyles(
- baseStyles,
- currentThemeColors.bg,
- currentThemeColors.fg,
- currentThemeColors.primary,
- );
+ // For fixed layout baseStyles is already a self-contained safe block;
+ // re-wrapping it with injectThemeIntoStyles would re-add the
+ // `color: inherit` rule that makes PDF TextLayer glyphs visible.
+ const themedStyles = isFixedLayout
+ ? baseStyles
+ : injectThemeIntoStyles(
+ baseStyles,
+ currentThemeColors.bg,
+ currentThemeColors.fg,
+ currentThemeColors.primary,
+ );
if (typeof renderer.setStyles === 'function') {
renderer.setStyles(themedStyles);
}
@@ -2055,8 +2098,15 @@
document.body.style.color = fg;
if (view && view.renderer && view.renderer.setStyles) {
- const currentStyles = lastRendererStyles || '';
- const themedStyles = injectThemeIntoStyles(currentStyles, bg, fg, primary);
+ const isFixedLayout = !!(view && view.isFixedLayout);
+ // For fixed layout, lastRendererStyles carries baked-in theme colors, so
+ // rebuild the safe block with the new colors; injectThemeIntoStyles
+ // would re-add the `color: inherit` rule that makes PDF TextLayer glyphs
+ // visible.
+ const themedStyles = isFixedLayout
+ ? buildFixedLayoutStyles(bg, fg, primary)
+ : injectThemeIntoStyles(lastRendererStyles || '', bg, fg, primary);
+ lastRendererStyles = themedStyles;
view.renderer.setStyles(themedStyles);
syncReaderOverrideStylesForAllDocs(themedStyles);
}
@@ -2213,6 +2263,15 @@
}
function applyDocStyles(doc) {
+ const isFixedLayout = !!(view && view.isFixedLayout);
+ // Fixed layout (PDF/CBZ): the pdf.js TextLayer glyphs are transparent spans
+ // positioned by CSS variables — `color: inherit` would make them visible and
+ // reflow overrides would misalign selection. Keep only the safe baseline.
+ const fixedLayoutStyles = buildFixedLayoutStyles(
+ currentThemeColors.bg,
+ currentThemeColors.fg,
+ currentThemeColors.primary,
+ );
syncCustomFontStylesForDoc(doc, currentCustomFontFaceCSS);
const verticalDoc = isVerticalDoc(doc);
if (!verticalDoc) {
@@ -2225,7 +2284,18 @@
}
const style = doc.createElement('style');
const { bg, fg, primary } = currentThemeColors;
- style.textContent = `
+ style.textContent = isFixedLayout
+ ? `
+ * { -webkit-touch-callout: none !important; -webkit-user-select: text !important; user-select: text !important; }
+ html, body { background-color: ${bg} !important; color: ${fg} !important; }
+ img, picture, svg, image, canvas {
+ -webkit-user-drag: none !important;
+ touch-action: manipulation !important;
+ }
+ ${fixedLayoutStyles}
+ ${verticalDoc ? getVerticalWritingCompatibilityStyles() : ''}
+ `
+ : `
* { -webkit-touch-callout: none !important; -webkit-user-select: text !important; user-select: text !important; }
html, body { background-color: ${bg} !important; color: ${fg} !important; }
img, picture, svg, image, canvas {
@@ -2239,13 +2309,25 @@
`;
doc.head.appendChild(style);
if (lastRendererStyles) {
- const themedStyles = injectThemeIntoStyles(
- lastRendererStyles,
- currentThemeColors.bg,
- currentThemeColors.fg,
- currentThemeColors.primary,
- );
- syncReaderOverrideStylesForDoc(doc, themedStyles);
+ if (isFixedLayout) {
+ // For PDF/CBZ, sync the freshly computed safe block instead of
+ // lastRendererStyles: lastRendererStyles is never reset in openBook,
+ // so after an EPUB→PDF switch in the same WebView it can still hold
+ // stale reflow styles (font-size/font-family/line-height !important)
+ // that would break the pdf.js TextLayer. fixedLayoutStyles is derived
+ // from the current theme colors, so it is always correct. Re-wrapping
+ // it with injectThemeIntoStyles would re-add `color: inherit`, so
+ // inject it verbatim.
+ syncReaderOverrideStylesForDoc(doc, fixedLayoutStyles);
+ } else {
+ const themedStyles = injectThemeIntoStyles(
+ lastRendererStyles,
+ currentThemeColors.bg,
+ currentThemeColors.fg,
+ currentThemeColors.primary,
+ );
+ syncReaderOverrideStylesForDoc(doc, themedStyles);
+ }
}
doc.addEventListener('contextmenu', (e) => { e.preventDefault(); e.stopPropagation(); return false; }, true);
}
diff --git a/packages/app-expo/scripts/build-reader.js b/packages/app-expo/scripts/build-reader.js
index 5be4e1f41..bb2f1b943 100644
--- a/packages/app-expo/scripts/build-reader.js
+++ b/packages/app-expo/scripts/build-reader.js
@@ -5,6 +5,7 @@
* Run: node scripts/build-reader.js
*/
const esbuild = require("esbuild");
+const crypto = require("node:crypto");
const fs = require("node:fs");
const path = require("node:path");
@@ -13,6 +14,14 @@ const ASSETS_DIR = path.resolve(__dirname, "../assets/reader");
const TEMPLATE = path.resolve(ASSETS_DIR, "reader.template.html");
const OUTPUT = path.resolve(ASSETS_DIR, "reader.html");
+// Build id: content hash of the complete output with its deterministic
+// placeholder still present. It changes for either template or bundled-source
+// changes, so rebuilding makes a stale committed reader.html easy to diagnose
+// without depending on a commit SHA that cannot include the generated artifact.
+function getBuildId(html) {
+ return `sha256-${crypto.createHash("sha256").update(html).digest("hex")}`;
+}
+
async function buildReader() {
// Create a temporary entry point
const entryContent = `
@@ -65,7 +74,15 @@ async function buildReader() {
// Use split/join instead of replace to avoid $ replacement patterns in JS bundle
const MARKER = "";
const parts = template.split(MARKER);
- const html = `${parts[0]}${parts.slice(1).join(MARKER)}`;
+ let html = `${parts[0]}${parts.slice(1).join(MARKER)}`;
+
+ // Stamp a deterministic content id so stale bundles are detectable while
+ // identical source and dependencies produce byte-identical output.
+ // The template keeps a placeholder value that gets replaced here.
+ html = html.replace(
+ "__READANY_READER_BUILD_ID = 'build-id-placeholder'",
+ `__READANY_READER_BUILD_ID = ${JSON.stringify(getBuildId(html))}`,
+ );
// Write to output file (separate from template)
fs.writeFileSync(OUTPUT, html);
diff --git a/packages/app/src/components/reader/FoliateViewer.tsx b/packages/app/src/components/reader/FoliateViewer.tsx
index 6b7fddeb9..1f6338774 100644
--- a/packages/app/src/components/reader/FoliateViewer.tsx
+++ b/packages/app/src/components/reader/FoliateViewer.tsx
@@ -120,6 +120,9 @@ function getRangeTextWithoutRuby(range: Range, fallback = ""): string {
for (const node of fragment.querySelectorAll("rt, rp")) {
node.remove();
}
+ for (const node of fragment.querySelectorAll("br")) {
+ node.replaceWith(fragment.ownerDocument.createTextNode("\n"));
+ }
const text = fragment.textContent?.trim();
if (text) return text;
} catch {
diff --git a/packages/app/vite.config.ts b/packages/app/vite.config.ts
index d17093a73..c6f598789 100644
--- a/packages/app/vite.config.ts
+++ b/packages/app/vite.config.ts
@@ -18,15 +18,9 @@ export default defineConfig(async () => ({
"@": path.resolve(__dirname, "./src"),
"pdfjs-dist/build/pdf.worker.mjs": path.join(pdfjsDist, "build/pdf.worker.mjs"),
"pdfjs-dist": pdfjsDist,
- // Map @pdfjs/* to foliate-js vendored pdfjs (v4.7, compatible with foliate-js)
- "@pdfjs": path.resolve(__dirname, "../../foliate-js/vendor/pdfjs"),
},
dedupe: ["i18next", "react-i18next", "react", "react-dom"],
},
- optimizeDeps: {
- // Exclude foliate-js pdf.js from pre-bundling so that @pdfjs alias works
- exclude: ["foliate-js/pdf.js"],
- },
clearScreen: false,
server: {
port: 1420,
diff --git a/packages/foliate-js/package-lock.json b/packages/foliate-js/package-lock.json
deleted file mode 100644
index 2341a20f0..000000000
--- a/packages/foliate-js/package-lock.json
+++ /dev/null
@@ -1,1575 +0,0 @@
-{
- "name": "foliate-js",
- "version": "0.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "foliate-js",
- "version": "0.0.0",
- "license": "MIT",
- "devDependencies": {
- "@eslint/js": "^9.9.1",
- "@rollup/plugin-node-resolve": "^15.2.3",
- "@rollup/plugin-terser": "^0.4.4",
- "@zip.js/zip.js": "^2.7.52",
- "fflate": "^0.8.2",
- "fs-extra": "^11.2.0",
- "globals": "^15.9.0",
- "pdfjs-dist": "^4.7.76",
- "rollup": "^4.22.4"
- }
- },
- "node_modules/@eslint/js": {
- "version": "9.12.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.12.0.tgz",
- "integrity": "sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
- "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/set-array": "^1.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/set-array": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/source-map": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
- "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.25"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
- "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.25",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
- "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@mapbox/node-pre-gyp": {
- "version": "1.0.11",
- "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
- "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "optional": true,
- "dependencies": {
- "detect-libc": "^2.0.0",
- "https-proxy-agent": "^5.0.0",
- "make-dir": "^3.1.0",
- "node-fetch": "^2.6.7",
- "nopt": "^5.0.0",
- "npmlog": "^5.0.1",
- "rimraf": "^3.0.2",
- "semver": "^7.3.5",
- "tar": "^6.1.11"
- },
- "bin": {
- "node-pre-gyp": "bin/node-pre-gyp"
- }
- },
- "node_modules/@rollup/plugin-node-resolve": {
- "version": "15.3.0",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz",
- "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@rollup/pluginutils": "^5.0.1",
- "@types/resolve": "1.20.2",
- "deepmerge": "^4.2.2",
- "is-module": "^1.0.0",
- "resolve": "^1.22.1"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "rollup": "^2.78.0||^3.0.0||^4.0.0"
- },
- "peerDependenciesMeta": {
- "rollup": {
- "optional": true
- }
- }
- },
- "node_modules/@rollup/plugin-terser": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
- "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "serialize-javascript": "^6.0.1",
- "smob": "^1.0.0",
- "terser": "^5.17.4"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "rollup": "^2.0.0||^3.0.0||^4.0.0"
- },
- "peerDependenciesMeta": {
- "rollup": {
- "optional": true
- }
- }
- },
- "node_modules/@rollup/pluginutils": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz",
- "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "^1.0.0",
- "estree-walker": "^2.0.2",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
- },
- "peerDependenciesMeta": {
- "rollup": {
- "optional": true
- }
- }
- },
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz",
- "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz",
- "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz",
- "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz",
- "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz",
- "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz",
- "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz",
- "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz",
- "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz",
- "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz",
- "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz",
- "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz",
- "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz",
- "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz",
- "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz",
- "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz",
- "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/resolve": {
- "version": "1.20.2",
- "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
- "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@zip.js/zip.js": {
- "version": "2.7.52",
- "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.52.tgz",
- "integrity": "sha512-+5g7FQswvrCHwYKNMd/KFxZSObctLSsQOgqBSi0LzwHo3li9Eh1w5cF5ndjQw9Zbr3ajVnd2+XyiX85gAetx1Q==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "bun": ">=0.7.0",
- "deno": ">=1.0.0",
- "node": ">=16.5.0"
- }
- },
- "node_modules/abbrev": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/acorn": {
- "version": "8.12.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
- "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "debug": "4"
- },
- "engines": {
- "node": ">= 6.0.0"
- }
- },
- "node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/aproba": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
- "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/are-we-there-yet": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
- "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
- "deprecated": "This package is no longer supported.",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "delegates": "^1.0.0",
- "readable-stream": "^3.6.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/canvas": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
- "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@mapbox/node-pre-gyp": "^1.0.0",
- "nan": "^2.17.0",
- "simple-get": "^3.0.3"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/chownr": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
- "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/color-support": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
- "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "bin": {
- "color-support": "bin.js"
- }
- },
- "node_modules/commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/console-control-strings": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
- "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/debug": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
- "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/decompress-response": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
- "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "mimic-response": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/deepmerge": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/delegates": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
- "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/detect-libc": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
- "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
- "dev": true,
- "license": "Apache-2.0",
- "optional": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/estree-walker": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
- "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fflate": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
- "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fs-extra": {
- "version": "11.2.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
- "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=14.14"
- }
- },
- "node_modules/fs-minipass": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
- "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "minipass": "^3.0.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/fs-minipass/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/gauge": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
- "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
- "deprecated": "This package is no longer supported.",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "aproba": "^1.0.3 || ^2.0.0",
- "color-support": "^1.1.2",
- "console-control-strings": "^1.0.0",
- "has-unicode": "^2.0.1",
- "object-assign": "^4.1.1",
- "signal-exit": "^3.0.0",
- "string-width": "^4.2.3",
- "strip-ansi": "^6.0.1",
- "wide-align": "^1.1.2"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/globals": {
- "version": "15.11.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz",
- "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/has-unicode": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
- "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/https-proxy-agent": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "agent-base": "6",
- "debug": "4"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/is-core-module": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
- "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/is-module": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
- "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/jsonfile": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
- "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "semver": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/make-dir/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/mimic-response": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
- "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/minipass": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
- "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/minizlib": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
- "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "minipass": "^3.0.0",
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/minizlib/node_modules/minipass": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
- "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/mkdirp": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
- "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "bin": {
- "mkdirp": "bin/cmd.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/nan": {
- "version": "2.22.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
- "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
- "node_modules/nopt": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
- "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "abbrev": "1"
- },
- "bin": {
- "nopt": "bin/nopt.js"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/npmlog": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
- "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
- "deprecated": "This package is no longer supported.",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "are-we-there-yet": "^2.0.0",
- "console-control-strings": "^1.1.0",
- "gauge": "^3.0.0",
- "set-blocking": "^2.0.0"
- }
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/path2d": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.1.tgz",
- "integrity": "sha512-Fl2z/BHvkTNvkuBzYTpTuirHZg6wW9z8+4SND/3mDTEcYbbNKWAy21dz9D3ePNNwrrK8pqZO5vLPZ1hLF6T7XA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/pdfjs-dist": {
- "version": "4.7.76",
- "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.7.76.tgz",
- "integrity": "sha512-8y6wUgC/Em35IumlGjaJOCm3wV4aY/6sqnIT3fVW/67mXsOZ9HWBn8GDKmJUK0GSzpbmX3gQqwfoFayp78Mtqw==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "canvas": "^2.11.2",
- "path2d": "^0.2.1"
- }
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "^5.1.0"
- }
- },
- "node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/resolve": {
- "version": "1.22.8",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
- "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.13.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/rollup": {
- "version": "4.24.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz",
- "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "1.0.6"
- },
- "bin": {
- "rollup": "dist/bin/rollup"
- },
- "engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
- },
- "optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.24.0",
- "@rollup/rollup-android-arm64": "4.24.0",
- "@rollup/rollup-darwin-arm64": "4.24.0",
- "@rollup/rollup-darwin-x64": "4.24.0",
- "@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
- "@rollup/rollup-linux-arm-musleabihf": "4.24.0",
- "@rollup/rollup-linux-arm64-gnu": "4.24.0",
- "@rollup/rollup-linux-arm64-musl": "4.24.0",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0",
- "@rollup/rollup-linux-riscv64-gnu": "4.24.0",
- "@rollup/rollup-linux-s390x-gnu": "4.24.0",
- "@rollup/rollup-linux-x64-gnu": "4.24.0",
- "@rollup/rollup-linux-x64-musl": "4.24.0",
- "@rollup/rollup-win32-arm64-msvc": "4.24.0",
- "@rollup/rollup-win32-ia32-msvc": "4.24.0",
- "@rollup/rollup-win32-x64-msvc": "4.24.0",
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/semver": {
- "version": "7.6.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
- "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/serialize-javascript": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
- "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "randombytes": "^2.1.0"
- }
- },
- "node_modules/set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/simple-concat": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
- "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "optional": true
- },
- "node_modules/simple-get": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
- "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "decompress-response": "^4.2.0",
- "once": "^1.3.1",
- "simple-concat": "^1.0.0"
- }
- },
- "node_modules/smob": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz",
- "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/source-map-support": {
- "version": "0.5.21",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
- "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
- "node_modules/string_decoder": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
- "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "safe-buffer": "~5.2.0"
- }
- },
- "node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/tar": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
- "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "chownr": "^2.0.0",
- "fs-minipass": "^2.0.0",
- "minipass": "^5.0.0",
- "minizlib": "^2.1.1",
- "mkdirp": "^1.0.3",
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/terser": {
- "version": "5.34.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.34.1.tgz",
- "integrity": "sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "bin": {
- "terser": "bin/terser"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "optional": true
- },
- "node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
- "node_modules/wide-align": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
- "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "dependencies": {
- "string-width": "^1.0.2 || 2 || 3 || 4"
- }
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true,
- "license": "ISC",
- "optional": true
- }
- }
-}
diff --git a/packages/foliate-js/package.json b/packages/foliate-js/package.json
index e457e98e3..248ac796c 100644
--- a/packages/foliate-js/package.json
+++ b/packages/foliate-js/package.json
@@ -22,9 +22,8 @@
"@rollup/plugin-terser": "^0.4.4",
"@zip.js/zip.js": "^2.7.52",
"fflate": "^0.8.2",
- "fs-extra": "^11.2.0",
"globals": "^15.9.0",
- "pdfjs-dist": "^4.7.76",
+ "pdfjs-dist": "^5.5.207",
"rollup": "^4.22.4"
},
"scripts": {
diff --git a/packages/foliate-js/pdf.js b/packages/foliate-js/pdf.js
index 30aabb2b4..433093894 100644
--- a/packages/foliate-js/pdf.js
+++ b/packages/foliate-js/pdf.js
@@ -20,6 +20,65 @@ const PDFJS_DOCUMENT_OPTIONS = {
standardFontDataUrl: `${PDFJS_CDN_BASE}/standard_fonts/`,
};
+// The OS accessibility "font size" setting scales every piece of WebView-rendered
+// text (including this transparent selection/highlight text layer) but leaves the
+// page's canvas bitmap untouched. Only the glyph *size* (a font-size) is scaled;
+// the text layer's positions are percentages of the `--total-scale-factor`-sized
+// container and are not. Left uncorrected the glyphs render `fontScale`x larger
+// than the ones baked into the canvas, so selection and highlight rectangles
+// overshoot the text into the blank margins and sit too low (readest #4480).
+// Measure the scale here so render() can divide it back out of the glyph-size
+// lever only. offsetHeight of a 100px/line-height-1 box reflects the OS font
+// scaling but not devicePixelRatio or CSS transforms, so it isolates it.
+const getFontScale = (doc) => {
+ const probe = doc.createElement("div");
+ probe.style.cssText =
+ "position:absolute;left:-9999px;top:0;visibility:hidden;font-size:100px;line-height:1;text-size-adjust:none;-webkit-text-size-adjust:none";
+ probe.textContent = "x";
+ doc.body.append(probe);
+ const fontScale = probe.offsetHeight / 100;
+ probe.remove();
+ return fontScale > 0 ? fontScale : 1;
+};
+
+// iOS WKWebView has a ~2GB per-process memory ceiling. Both a page's canvas
+// bitmap and its WebKit backing layer are allocated at the render scale, so
+// their memory grows with the SQUARE of the device pixel ratio. Phones report
+// dpr 3, which is the tipping factor. Rendering at 2x instead of 3x is still
+// retina-sharp but uses ~2.25x less memory per page (the crisp, selectable
+// text layer is a separate DOM layer, unaffected).
+const MAX_RENDER_DPR = 2;
+// Hard ceiling on a single page's bitmap area (~3.1 Mpx ≈ 12.6 MB) so a large
+// tablet page can't blow the budget even after the dpr clamp.
+const MAX_CANVAS_PIXELS = 2048 * 1536;
+
+// Only mobile WebViews get that budget. Desktop browsers have no per-process
+// memory ceiling, so clamping there bought nothing and cost sharpness: the
+// raster ended up coarser than the screen, the browser upscaled it into the
+// CSS box, and PDF text looked blurry (readest #5251). iPadOS reports a
+// desktop ("Macintosh") user agent, so touch points are what give a tablet
+// away.
+const isMobileWebView = () => {
+ const ua = navigator.userAgent;
+ return (
+ /Android|iPhone|iPad|iPod/i.test(ua) || (/Macintosh/i.test(ua) && navigator.maxTouchPoints > 1)
+ );
+};
+
+// The device pixel ratio to rasterise this page at: the real dpr on desktop,
+// or on mobile the dpr clamped by both MAX_RENDER_DPR and the per-canvas pixel
+// budget. Never below 1 (CSS resolution).
+const getRenderDpr = (page, zoom) => {
+ let dpr = globalThis.devicePixelRatio || 1;
+ if (isMobileWebView()) {
+ dpr = Math.min(dpr, MAX_RENDER_DPR);
+ const { width, height } = page.getViewport({ scale: zoom || 1 });
+ const area = width * height * dpr * dpr;
+ if (area > MAX_CANVAS_PIXELS) dpr *= Math.sqrt(MAX_CANVAS_PIXELS / area);
+ }
+ return Math.max(1, dpr);
+};
+
// Inline text_layer_builder CSS
const TEXT_LAYER_CSS = `
.textLayer {
@@ -34,6 +93,8 @@ const TEXT_LAYER_CSS = `
transform-origin: 0 0;
caret-color: CanvasText;
z-index: 0;
+ letter-spacing: normal;
+ word-spacing: normal;
}
.textLayer.highlighting { touch-action: none; }
.textLayer :is(span, br) {
@@ -43,7 +104,6 @@ const TEXT_LAYER_CSS = `
cursor: text;
transform-origin: 0% 0%;
}
-.textLayer --min-font-size: 1;
.textLayer {
--min-font-size: 1;
--text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size));
@@ -104,18 +164,121 @@ const ANNOTATION_LAYER_CSS = `
.annotationLayer .linkAnnotation.hasBorder:hover {
background-color: rgb(255 255 0 / 0.2);
}
+.textLayer.selecting ~ .annotationLayer section {
+ pointer-events: none;
+}
`;
+const NULL_CHAR = String.fromCharCode(0);
+const removeNullCharacters = (str) => str.replaceAll(NULL_CHAR, "");
+const normalizeSelectedText = (str) => removeNullCharacters(pdfjsLib.normalizeUnicode(str)).trim();
+
+const getRangeTextWithLineBreaks = (range) => {
+ try {
+ const fragment = range.cloneContents();
+ for (const node of fragment.querySelectorAll(".endOfContent")) {
+ node.remove();
+ }
+ for (const node of fragment.querySelectorAll("br")) {
+ node.replaceWith(fragment.ownerDocument.createTextNode("\n"));
+ }
+ return fragment.textContent || "";
+ } catch {
+ return "";
+ }
+};
+
+const getSelectedText = (selection, container) => {
+ const doc = container.ownerDocument;
+ const view = doc.defaultView;
+ const segments = [];
+ let textWithExplicitLineBreaks = "";
+
+ const walker = doc.createTreeWalker(container, view.NodeFilter.SHOW_TEXT);
+ for (let i = 0; i < selection.rangeCount; i++) {
+ const range = selection.getRangeAt(i);
+ if (!range.intersectsNode(container)) continue;
+
+ const rangeText = getRangeTextWithLineBreaks(range);
+ if (rangeText.trim()) {
+ textWithExplicitLineBreaks += `${textWithExplicitLineBreaks ? "\n" : ""}${rangeText}`;
+ }
+
+ walker.currentNode = container;
+ while (walker.nextNode()) {
+ const node = walker.currentNode;
+ const parent = node.parentElement;
+ if (!parent || parent.closest(".endOfContent")) continue;
+ if (!range.intersectsNode(node)) continue;
+
+ let start = 0;
+ let end = node.nodeValue.length;
+ if (range.startContainer === node) start = range.startOffset;
+ if (range.endContainer === node) end = range.endOffset;
+ const text = node.nodeValue.slice(start, end);
+ if (!text) continue;
+
+ const rect = parent.getBoundingClientRect();
+ if (rect.width === 0 && rect.height === 0) continue;
+ segments.push({
+ text,
+ left: rect.left,
+ centerY: rect.top + rect.height / 2,
+ height: rect.height || 1,
+ });
+ }
+ }
+
+ if (textWithExplicitLineBreaks.trim()) {
+ return normalizeSelectedText(textWithExplicitLineBreaks);
+ }
+
+ if (segments.length === 0) {
+ return normalizeSelectedText(selection.toString());
+ }
+
+ segments.sort((a, b) => {
+ const tolerance = Math.max(2, Math.min(a.height, b.height) * 0.6);
+ if (Math.abs(a.centerY - b.centerY) > tolerance) return a.centerY - b.centerY;
+ return a.left - b.left;
+ });
+
+ const lines = [];
+ for (const segment of segments) {
+ const line = lines.at(-1);
+ const tolerance = Math.max(2, segment.height * 0.6);
+ if (!line || Math.abs(line.centerY - segment.centerY) > tolerance) {
+ lines.push({ centerY: segment.centerY, height: segment.height, parts: [segment] });
+ continue;
+ }
+ line.parts.push(segment);
+ line.centerY = (line.centerY * (line.parts.length - 1) + segment.centerY) / line.parts.length;
+ line.height = Math.max(line.height, segment.height);
+ }
+
+ const text = lines
+ .map((line) =>
+ line.parts
+ .sort((a, b) => a.left - b.left)
+ .map((segment) => segment.text)
+ .join(""),
+ )
+ .join("\n");
+
+ return normalizeSelectedText(text);
+};
+
/**
* Render canvas + text layer + annotation layer for a PDF page inside an iframe document.
* Called on initial load and on every zoom change.
*/
const render = async (page, doc, zoom) => {
if (!doc) return;
- const scale = zoom * devicePixelRatio;
+ const scale = zoom;
+ const renderDpr = getRenderDpr(page, zoom);
- doc.documentElement.style.transform = `scale(${1 / devicePixelRatio})`;
- doc.documentElement.style.transformOrigin = "top left";
+ doc.documentElement.style.removeProperty("transform");
+ doc.documentElement.style.removeProperty("transform-origin");
doc.documentElement.style.setProperty("--total-scale-factor", scale);
doc.documentElement.style.setProperty("--user-unit", "1");
doc.documentElement.style.setProperty("--scale-round-x", "1px");
@@ -123,12 +286,18 @@ const render = async (page, doc, zoom) => {
const viewport = page.getViewport({ scale });
- // Render canvas (in main document for font loading, then adopt into iframe)
+ // Render canvas (in main document for font loading, then adopt into iframe).
+ // The bitmap is over-sampled at renderDpr but the CSS box is the display size,
+ // so the raster stays crisp WITHOUT scaling the document (scaling the document
+ // with `transform` misplaces text selection and the annotation toolbar).
const canvas = document.createElement("canvas");
- canvas.height = viewport.height;
- canvas.width = viewport.width;
+ canvas.height = Math.floor(viewport.height * renderDpr);
+ canvas.width = Math.floor(viewport.width * renderDpr);
+ canvas.style.width = `${Math.floor(viewport.width)}px`;
+ canvas.style.height = `${Math.floor(viewport.height)}px`;
const canvasContext = canvas.getContext("2d");
- await page.render({ canvasContext, viewport }).promise;
+ const transform = renderDpr !== 1 ? [renderDpr, 0, 0, renderDpr, 0, 0] : null;
+ await page.render({ canvasContext, transform, viewport }).promise;
const canvasContainer = doc.querySelector("#canvas");
if (!canvasContainer) return;
@@ -138,12 +307,36 @@ const render = async (page, doc, zoom) => {
const textContainer = doc.querySelector(".textLayer");
if (textContainer) {
textContainer.replaceChildren();
- const textLayer = new pdfjsLib.TextLayer({
- textContentSource: await page.streamTextContent(),
- container: textContainer,
- viewport,
- });
- await textLayer.render();
+
+ // `replaceChildren` leaves inline styles intact, so reset a scale left by a
+ // previous accessibility-font-size render before rendering this page.
+ textContainer.style.removeProperty("--text-scale-factor");
+
+ // Counteract the OS font-size accessibility scaling on the text layer's glyph
+ // size only (see getFontScale). `--text-scale-factor` feeds `font-size` and
+ // nothing else, so dividing it leaves positions (which scale with
+ // `--total-scale-factor`) aligned with the canvas at any font-size setting.
+ const fontScale = getFontScale(doc);
+ if (fontScale !== 1) {
+ textContainer.style.setProperty(
+ "--text-scale-factor",
+ `calc(var(--total-scale-factor) * var(--min-font-size) / ${fontScale})`,
+ );
+ }
+
+ try {
+ const textLayer = new pdfjsLib.TextLayer({
+ textContentSource: await page.streamTextContent({
+ includeMarkedContent: true,
+ disableNormalization: true,
+ }),
+ container: textContainer,
+ viewport,
+ });
+ await textLayer.render();
+ } catch (error) {
+ console.error(`Failed to render PDF text layer for page ${page.pageNumber}.`, error);
+ }
// Hide offscreen canvases created by TextLayer
for (const c of document.querySelectorAll(".hiddenCanvasElement")) {
@@ -157,10 +350,87 @@ const render = async (page, doc, zoom) => {
});
}
- // Fix text selection end-of-content marker
- const endOfContent = document.createElement("div");
+ doc.__readanyPdfTextSelectionAbortController?.abort();
+ const selectionAbortController = new AbortController();
+ doc.__readanyPdfTextSelectionAbortController = selectionAbortController;
+ const { signal } = selectionAbortController;
+ const SelectionRange = doc.defaultView.Range;
+ const endOfContent = doc.createElement("div");
endOfContent.className = "endOfContent";
textContainer.append(endOfContent);
+ let previousSelectionRange = null;
+
+ const resetEndOfContent = () => {
+ textContainer.append(endOfContent);
+ endOfContent.style.width = "";
+ endOfContent.style.height = "";
+ endOfContent.style.userSelect = "";
+ textContainer.classList.remove("selecting");
+ previousSelectionRange = null;
+ };
+
+ const moveEndOfContent = (selection) => {
+ if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
+ resetEndOfContent();
+ return;
+ }
+
+ let range = null;
+ for (let i = 0; i < selection.rangeCount; i++) {
+ const candidate = selection.getRangeAt(i);
+ if (candidate.intersectsNode(textContainer)) {
+ range = candidate;
+ break;
+ }
+ }
+
+ if (!range) {
+ resetEndOfContent();
+ return;
+ }
+
+ textContainer.classList.add("selecting");
+
+ const isModifyingStart =
+ previousSelectionRange &&
+ (range.compareBoundaryPoints(SelectionRange.END_TO_END, previousSelectionRange) === 0 ||
+ range.compareBoundaryPoints(SelectionRange.START_TO_END, previousSelectionRange) === 0);
+
+ let anchor = isModifyingStart ? range.startContainer : range.endContainer;
+ if (anchor.nodeType === Node.TEXT_NODE) anchor = anchor.parentNode;
+ if (anchor.classList?.contains("highlight")) anchor = anchor.parentNode;
+
+ if (!isModifyingStart && range.endOffset === 0) {
+ while (anchor && anchor !== textContainer && !anchor.previousSibling) {
+ anchor = anchor.parentNode;
+ }
+ if (anchor?.previousSibling) anchor = anchor.previousSibling;
+ }
+
+ const parentTextLayer = anchor?.parentElement?.closest(".textLayer");
+ if (parentTextLayer === textContainer && anchor.parentElement) {
+ endOfContent.style.width = `${Math.ceil(viewport.width)}px`;
+ endOfContent.style.height = `${Math.ceil(viewport.height)}px`;
+ endOfContent.style.userSelect = "text";
+ anchor.parentElement.insertBefore(
+ endOfContent,
+ isModifyingStart ? anchor : anchor.nextSibling,
+ );
+ }
+
+ previousSelectionRange = range.cloneRange();
+ };
+
+ const handleCopy = (event) => {
+ const selection = doc.getSelection();
+ const text = selection ? getSelectedText(selection, textContainer) : "";
+ if (!text || !event.clipboardData) return;
+ event.clipboardData.setData("text/plain", text);
+ event.preventDefault();
+ event.stopPropagation();
+ };
+ textContainer.oncopy = handleCopy;
+ doc.addEventListener("copy", handleCopy, { signal });
// Panning + text selection cursor logic
let isPanning = false;
@@ -240,7 +510,7 @@ const render = async (page, doc, zoom) => {
isPanning = false;
scrollParent = null;
textContainer.style.cursor = "grab";
- } else textContainer.classList.remove("selecting");
+ } else resetEndOfContent();
};
textContainer.onpointerleave = () => {
@@ -251,11 +521,19 @@ const render = async (page, doc, zoom) => {
}
};
- doc.addEventListener("selectionchange", () => {
- const selection = doc.getSelection();
- if (selection && selection.toString().length > 0) textContainer.style.cursor = "text";
- else if (!isPanning) textContainer.style.cursor = "grab";
- });
+ doc.addEventListener(
+ "selectionchange",
+ () => {
+ const selection = doc.getSelection();
+ moveEndOfContent(selection);
+ if (selection && selection.toString().length > 0) textContainer.style.cursor = "text";
+ else if (!isPanning) textContainer.style.cursor = "grab";
+ },
+ { signal },
+ );
+ doc.addEventListener("pointerup", resetEndOfContent, { signal });
+ doc.addEventListener("keyup", resetEndOfContent, { signal });
+ doc.defaultView.addEventListener("blur", resetEndOfContent, { signal });
textContainer.style.cursor = "grab";
}
@@ -267,6 +545,10 @@ const render = async (page, doc, zoom) => {
const linkService = {
goToDestination: () => {},
getDestinationHash: (dest) => JSON.stringify(dest),
+ // pdf.js AnnotationLayer calls getAnchorUrl for named-action / GoTo link
+ // annotations; without it the render rejects. Match pdf.js SimpleLinkService,
+ // which returns ''.
+ getAnchorUrl: () => "",
addLinkAttributes: (link, url) => {
link.href = url;
},
@@ -274,7 +556,7 @@ const render = async (page, doc, zoom) => {
try {
await new pdfjsLib.AnnotationLayer({
page,
- viewport,
+ viewport: viewport.clone({ dontFlip: true }),
div: annotationDiv,
linkService,
}).render({ annotations: await page.getAnnotations() });
diff --git a/packages/foliate-js/rollup.config.js b/packages/foliate-js/rollup.config.js
index a86dea040..212133d96 100644
--- a/packages/foliate-js/rollup.config.js
+++ b/packages/foliate-js/rollup.config.js
@@ -1,21 +1,5 @@
import { nodeResolve } from "@rollup/plugin-node-resolve";
import terser from "@rollup/plugin-terser";
-import { copy } from "fs-extra";
-
-const copyPDFJS = () => ({
- name: "copy-pdfjs",
- async writeBundle() {
- await copy("node_modules/pdfjs-dist/build/pdf.mjs", "vendor/pdfjs/pdf.mjs");
- await copy("node_modules/pdfjs-dist/build/pdf.mjs.map", "vendor/pdfjs/pdf.mjs.map");
- await copy("node_modules/pdfjs-dist/build/pdf.worker.mjs", "vendor/pdfjs/pdf.worker.mjs");
- await copy(
- "node_modules/pdfjs-dist/build/pdf.worker.mjs.map",
- "vendor/pdfjs/pdf.worker.mjs.map",
- );
- await copy("node_modules/pdfjs-dist/cmaps", "vendor/pdfjs/cmaps");
- await copy("node_modules/pdfjs-dist/standard_fonts", "vendor/pdfjs/standard_fonts");
- },
-});
export default [
{
@@ -32,6 +16,6 @@ export default [
dir: "vendor/",
format: "esm",
},
- plugins: [nodeResolve(), terser(), copyPDFJS()],
+ plugins: [nodeResolve(), terser()],
},
];
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c2c748e11..4e5817823 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -592,15 +592,12 @@ importers:
fflate:
specifier: ^0.8.2
version: 0.8.2
- fs-extra:
- specifier: ^11.2.0
- version: 11.3.4
globals:
specifier: ^15.9.0
version: 15.15.0
pdfjs-dist:
- specifier: ^4.7.76
- version: 4.10.38
+ specifier: ^5.5.207
+ version: 5.5.207
rollup:
specifier: ^4.22.4
version: 4.59.0
@@ -1385,24 +1382,28 @@ packages:
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@biomejs/cli-linux-arm64@1.9.4':
resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@biomejs/cli-linux-x64-musl@1.9.4':
resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@biomejs/cli-linux-x64@1.9.4':
resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@biomejs/cli-win32-arm64@1.9.4':
resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==}
@@ -2218,89 +2219,105 @@ packages:
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.4':
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.4':
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.2.4':
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.4':
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.4':
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@img/sharp-linux-arm64@0.34.5':
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-arm@0.34.5':
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-ppc64@0.34.5':
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-riscv64@0.34.5':
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-s390x@0.34.5':
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@img/sharp-linux-x64@0.34.5':
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.34.5':
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.5':
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@img/sharp-wasm32@0.34.5':
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
@@ -2491,30 +2508,35 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@napi-rs/canvas-linux-arm64-musl@0.1.96':
resolution: {integrity: sha512-UvOi7fii3IE2KDfEfhh8m+LpzSRvhGK7o1eho99M2M0HTik11k3GX+2qgVx9EtujN3/bhFFS1kSO3+vPMaJ0Mg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@napi-rs/canvas-linux-riscv64-gnu@0.1.96':
resolution: {integrity: sha512-MBSukhGCQ5nRtf9NbFYWOU080yqkZU1PbuH4o1ROvB4CbPl12fchDR35tU83Wz8gWIM9JTn99lBn9DenPIv7Ig==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@napi-rs/canvas-linux-x64-gnu@0.1.96':
resolution: {integrity: sha512-I/ccu2SstyKiV3HIeVzyBIWfrJo8cN7+MSQZPnabewWV6hfJ2nY7Df2WqOHmobBRUw84uGR6zfQHsUEio/m5Vg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@napi-rs/canvas-linux-x64-musl@0.1.96':
resolution: {integrity: sha512-H3uov7qnTl73GDT4h52lAqpJPsl1tIUyNPWJyhQ6gHakohNqqRq3uf80+NEpzcytKGEOENP1wX3yGwZxhjiWEQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@napi-rs/canvas-win32-arm64-msvc@0.1.96':
resolution: {integrity: sha512-ATp6Y+djOjYtkfV/VRH7CZ8I1MEtkUQBmKUbuWw5zWEHHqfL0cEcInE4Cxgx7zkNAhEdBbnH8HMVrqNp+/gwxA==}
@@ -3251,66 +3273,79 @@ packages:
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
cpu: [arm]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
cpu: [arm]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.59.0':
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.59.0':
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.59.0':
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
cpu: [loong64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.59.0':
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
cpu: [loong64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.59.0':
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
cpu: [ppc64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.59.0':
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
cpu: [riscv64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.59.0':
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.59.0':
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.59.0':
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@rollup/rollup-openbsd-x64@4.59.0':
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
@@ -3740,24 +3775,28 @@ packages:
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.2.1':
resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.2.1':
resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.2.1':
resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.2.1':
resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==}
@@ -3818,30 +3857,35 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@tauri-apps/cli-linux-arm64-musl@2.10.1':
resolution: {integrity: sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@tauri-apps/cli-linux-riscv64-gnu@2.10.1':
resolution: {integrity: sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
+ libc: [glibc]
'@tauri-apps/cli-linux-x64-gnu@2.10.1':
resolution: {integrity: sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@tauri-apps/cli-linux-x64-musl@2.10.1':
resolution: {integrity: sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@tauri-apps/cli-win32-arm64-msvc@2.10.1':
resolution: {integrity: sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==}
@@ -4348,7 +4392,7 @@ packages:
'@xmldom/xmldom@0.7.13':
resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==}
engines: {node: '>=10.0.0'}
- deprecated: this version has critical issues, please update to the latest version
+ deprecated: this version is no longer supported, please update to at least 0.8.*
'@xmldom/xmldom@0.8.11':
resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
@@ -6016,11 +6060,11 @@ packages:
glob@6.0.4:
resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==}
- deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
+ deprecated: Glob versions prior to v9 are no longer supported
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
- deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
+ deprecated: Glob versions prior to v9 are no longer supported
glob@9.3.5:
resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==}
@@ -6625,48 +6669,56 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-arm64-gnu@1.32.0:
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-arm64-musl@1.31.1:
resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
lightningcss-linux-x64-gnu@1.31.1:
resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-x64-musl@1.31.1:
resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [musl]
lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [musl]
lightningcss-win32-arm64-msvc@1.31.1:
resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==}
@@ -7656,10 +7708,6 @@ packages:
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
- pdfjs-dist@4.10.38:
- resolution: {integrity: sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ==}
- engines: {node: '>=20'}
-
pdfjs-dist@5.5.207:
resolution: {integrity: sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw==}
engines: {node: '>=20.19.0 || >=22.13.0 || >=24'}
@@ -18268,10 +18316,6 @@ snapshots:
pathe@2.0.3: {}
- pdfjs-dist@4.10.38:
- optionalDependencies:
- '@napi-rs/canvas': 0.1.96
-
pdfjs-dist@5.5.207:
optionalDependencies:
'@napi-rs/canvas': 0.1.96