dsh-book-reader 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,47 @@ All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.3.0] — 2026-10-20
8
+
9
+ ### Changed
10
+
11
+ - Search is now a basic in-page find bar: `Cmd/Ctrl+F` opens it, matches are
12
+ highlighted on the PDF text layer, and `Enter` / `Shift+Enter` (or the
13
+ previous/next buttons) jump between occurrences with an `n / total` counter.
14
+ - The search result list, its snippets, and the per-result “→ AI” insertion are
15
+ gone; search no longer inserts anything into the composer.
16
+ - `pdf_search` is now normalized text search: wrapping quotes/backticks and
17
+ `$…$` are stripped, NFKC folds math alphanumerics and ligatures, dashes and
18
+ line-break hyphenation are removed, and if no literal phrase matches it falls
19
+ back to a space-insensitive pass (`compact`) and then keyword AND (`terms`).
20
+ The result gains `mode`; hits rank body pages before front matter (at most 3
21
+ front-matter hits), `pages` counts every matched page, and the tool default
22
+ limit is 30. The tool description now states these rules so the model stops
23
+ passing quoted or LaTeX-marked phrases.
24
+ - `pdf_search` accepts an optional `pages` selection (the same syntax as
25
+ `pdf_read_pages`; printed pages by default, `page_numbers:"pdf"` for physical
26
+ pages) to scan only a page range and report matches inside it. This keeps a
27
+ late-chapter search from being crowded out by earlier pages.
28
+
29
+ ### Fixed
30
+
31
+ - Opening a book now jumps to the remembered page immediately, before a saved
32
+ fixed zoom is replayed, so the cover no longer flashes for a few hundred
33
+ milliseconds while the zoom settles. The scroll is re-applied after the zoom
34
+ commit so the page stays aligned.
35
+ - `pdf_read_pages` no longer disappears from the model's tool list: its output
36
+ schema used a property-level `required: false`, which DSH's `defineTool`
37
+ rejects at registration time. The exception only reached the Host log, so the
38
+ plugin looked enabled with two tools instead of three.
39
+
40
+ ## [0.3.1] — 2026-10-22
41
+
42
+ ### Fixed
43
+
44
+ - Text-only `pdf_read_pages` calls (`include_image:false`) no longer crash while
45
+ rendering their output, and they no longer require an image-capable model
46
+ route or the attachment store; image reads still require both.
47
+
7
48
  ## [0.2.0] — 2026-10-20
8
49
 
9
50
  ### Added
@@ -16,8 +57,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
16
57
  - New config key `rememberView` (default `true`).
17
58
  - Host routes `GET /dsh-pdf-reader/view?path=…` and
18
59
  `POST /dsh-pdf-reader/view` for the browser half.
19
- - Docs: the README is split into a concise English `README.md` and a concise
20
- Simplified Chinese `README.zh-CN.md`; they no longer share one page.
60
+ - Docs: the README is split into an English `README.md` and a Simplified Chinese
61
+ `README.zh-CN.md`, both rewritten in a product-page style (badges, quick start,
62
+ feature table, model-tool examples, FAQ, data/privacy notes).
21
63
 
22
64
  ## [0.1.1] — 2026-10-18
23
65
 
@@ -71,6 +113,8 @@ First public release.
71
113
  - `pdfjs-dist` is pinned to `6.3.289` to match the host process.
72
114
  - MIT licensed.
73
115
 
116
+ [0.3.1]: https://github.com/Strepsiades2/dsh-book-reader/releases/tag/v0.3.1
117
+ [0.3.0]: https://github.com/Strepsiades2/dsh-book-reader/releases/tag/v0.3.0
74
118
  [0.2.0]: https://github.com/Strepsiades2/dsh-book-reader/releases/tag/v0.2.0
75
119
  [0.1.1]: https://github.com/Strepsiades2/dsh-book-reader/releases/tag/v0.1.1
76
120
  [0.1.0]: https://github.com/Strepsiades2/dsh-book-reader/releases/tag/v0.1.0
package/README.md CHANGED
@@ -1,54 +1,90 @@
1
1
  # dsh-book-reader
2
2
 
3
- A reading toolbar for DSH's official PDF preview, plus three PDF tools the model can use.
3
+ [![npm version](https://img.shields.io/npm/v/dsh-book-reader.svg)](https://www.npmjs.com/package/dsh-book-reader)
4
+ [![license](https://img.shields.io/npm/l/dsh-book-reader.svg)](LICENSE)
5
+ [![DSH](https://img.shields.io/badge/DSH-0.2.x-blue)](https://github.com/deepseek-ai/deepseek-harness)
6
+
7
+ **Read PDFs in DSH together with the model.** A native toolbar on the official PDF preview, plus three tools the model can use to look up the TOC, search the full text, and read pages — page images included, when a formula or diagram needs to be seen.
4
8
 
5
9
  [中文说明](README.zh-CN.md)
6
10
 
7
- ## Features
11
+ ## Why this one
12
+
13
+ - **Native, not a replacement.** It decorates DSH's own PDF preview, so zoom, text selection, reload, and Office→PDF keep working. No extra viewer, no sidebar dependency.
14
+ - **The model can actually read the book.** Three tools give it the page map, text search, and page content, with page images only when needed.
15
+ - **It remembers where you were.** Reopening a book restores the last page and zoom.
16
+
17
+ ## Quick start
8
18
 
9
- **Reading toolbar** — only on `.pdf` previews in the right sidebar; the stock previewer is untouched.
19
+ 1. DSH **Plugins → Add plugin** → `dsh-book-reader` (or `dsh plugin add dsh-book-reader`). Restart DSH.
20
+ 2. Open any `.pdf` in the right sidebar — the toolbar appears in the preview header.
21
+ 3. Ask the model about the PDF; it can search and read from now on.
22
+
23
+ Optional — give the model the reading guide (costs, page numbering, pitfalls):
24
+
25
+ ```sh
26
+ ln -sfn "<installed package dir>" "<workspace>/.dsh/skills/pdf-reader"
27
+ ```
28
+
29
+ ## Features
10
30
 
11
- - Previous/next page, a current-page readout, and a jump box (printed page numbers, or `p152` for PDF page 152).
12
- - Full-text search with `Cmd/Ctrl+Shift+F`; click a result to jump, or insert its page reference into the chat.
13
- - AI position hint: before each turn the model may receive one short “you are reading page N” line (the `AI●` toggle, on by default).
14
- - **Remembers your place**: reopening a book restores the last page and zoom. State is stored by DSH per book and survives restarts.
15
- - Hover/focus explanations on every control, in English or Chinese.
31
+ | | Feature | What it does |
32
+ | --- | --- | --- |
33
+ | 🧭 | Navigation | Previous/next page, page readout, jump box (printed page, or `p152` for PDF page 152) |
34
+ | 🔎 | Search | Full-text search (`Cmd/Ctrl+F`) with in-page highlights; `Enter` / `Shift+Enter` jump between occurrences, no snippets |
35
+ | 🤖 | AI position hint | One short “you are reading page N” line per turn, only when the page changed (`AI●` toggle) |
36
+ | 💾 | Reading memory | Remembers page and zoom per book; restores them when you reopen it |
37
+ | 💬 | Tooltips | Hover/focus explanations on every control, English or Chinese |
38
+ | 🧰 | Model tools | `pdf_info`, `pdf_search`, `pdf_read_pages` — see below |
16
39
 
17
- **Model tools** — the model calls them when you ask about a PDF.
40
+ ## Model tools
18
41
 
19
- | Tool | What it does |
20
- | --- | --- |
21
- | `pdf_info` | Page count, printed↔physical page offset, outline/TOC |
22
- | `pdf_search` | Literal full-text search; page numbers and snippets |
23
- | `pdf_read_pages` | Reads chosen pages as text, optionally with page images |
42
+ | Tool | Use it for | Cost |
43
+ | --- | --- | --- |
44
+ | `pdf_info` | Page count, printed↔physical page offset, outline/TOC | text, ~free |
45
+ | `pdf_search` | Finding a phrase, theorem number, or figure caption. Tolerates quotes/LaTeX, hyphen and math-notation splits, with keyword fallbacks; body pages first; optional `pages` range | text, ~free |
46
+ | `pdf_read_pages` | Reading chosen pages as text, optionally with page images | text ~free; images ~350–1024 tokens/page |
24
47
 
25
- ## Install
48
+ Example prompts:
26
49
 
27
- 1. **Plugins → Add plugin** in DSH and enter `dsh-book-reader` (or run `dsh plugin add dsh-book-reader`).
28
- 2. Restart DSH after installing or upgrading.
29
- 3. Optional: give the model the reading guide by linking the skill into your workspace:
50
+ > “What does chapter 5 of this book cover?” → `pdf_info` + `pdf_read_pages`
51
+ > “Find where ‘singular value’ appears.” → `pdf_search`
52
+ > “Search only chapter 7 (printed pp. 270–295) for ‘SVD’.” → `pdf_search` with `pages:"270-295"`
53
+ > “Show me the formula in theorem 5.6.” → `pdf_read_pages` with images
30
54
 
31
- ```sh
32
- ln -sfn "<installed package dir>" "<workspace>/.dsh/skills/pdf-reader"
33
- ```
55
+ The model decides when text is enough and when an image is worth it.
34
56
 
35
57
  ## Configuration
36
58
 
37
- All keys are optional and the defaults are safe. Common ones:
59
+ Every key is optional and the defaults are safe. Common ones:
38
60
 
39
61
  - `rememberView` (default `true`) — remember page/zoom per book.
40
- - `maxPagesPerCall` (default `4`) — page cap for `pdf_read_pages`.
41
- - `defaultScale` (default `2.5`) — image render scale.
62
+ - `maxPagesPerCall` (default `4`) — page cap per read call.
63
+ - `defaultScale` (default `2.5`) — image render scale (~180 DPI).
42
64
  - `autoShare` (default `true`) — the AI position hint.
43
65
 
44
66
  The full list with defaults is in [`cordis.patch.yml`](cordis.patch.yml).
45
67
 
68
+ ## FAQ
69
+
70
+ **The toolbar does not appear.** Only `.pdf` previews get it (not `.docx`/`.pptx`/images). Update/reinstall the plugin and restart DSH.
71
+
72
+ **The model does not know my page.** The hint is sent at most once per turn and only when the page changed; turn on `AI●`, or mention the page in your message.
73
+
74
+ **Scanned PDFs.** No text layer: search finds nothing and text reads are empty. Page images still work — check `textCoverage.isLikelyScanned` from `pdf_info`.
75
+
76
+ **Search finds nothing.** Quotes and `$…$` are stripped, hyphen/math splits and missing spaces have fallbacks, and when no literal phrase matches the tool falls back to keyword AND (`mode` in the result says which pass matched). Try one short distinctive word.
77
+
78
+ **Does it modify my PDF?** No. It only reads and renders; nothing is written next to your books.
79
+
80
+ **Where is the remembered state?** In DSH's data directory (`~/.dsh/storages/book_reader_views.json`), at most 500 books, keyed by file path.
81
+
82
+ **A DSH upgrade broke the toolbar.** The toolbar relies on the preview's private DOM markers; page tools keep working regardless.
83
+
46
84
  ## Notes
47
85
 
48
- - Only the official PDF preview is supported; the toolbar relies on that preview's private DOM markers, so a DSH upgrade may change behavior.
49
- - Tools read only inside the session workspace unless `allowOutsideWorkspace` is set; the local HTTP routes are loopback-only and unauthenticated.
50
- - Scanned PDFs have no text layer — page images are the only option (no OCR).
51
- - `pdfjs-dist` is pinned to `6.3.289` to match the host process.
86
+ - Local HTTP routes are loopback-only and unauthenticated; model tools stay inside the session workspace by default (`allowOutsideWorkspace` opts out).
87
+ - `pdfjs-dist` is pinned to `6.3.289` to match the DSH host process.
52
88
 
53
89
  ## More
54
90
 
package/README.zh-CN.md CHANGED
@@ -1,36 +1,58 @@
1
1
  # dsh-book-reader
2
2
 
3
- 给 DSH 官方 PDF 预览加一条读书工具栏,并给模型三个 PDF 工具。
3
+ [![npm version](https://img.shields.io/npm/v/dsh-book-reader.svg)](https://www.npmjs.com/package/dsh-book-reader)
4
+ [![license](https://img.shields.io/npm/l/dsh-book-reader.svg)](LICENSE)
5
+ [![DSH](https://img.shields.io/badge/DSH-0.2.x-blue)](https://github.com/deepseek-ai/deepseek-harness)
6
+
7
+ **在 DSH 里和模型一起读 PDF。** 官方 PDF 预览上加一条原生工具栏,并给模型三个工具:查目录、全文搜索、读指定页——公式和图表需要时还能直接看页面图片。
4
8
 
5
9
  [English](README.md)
6
10
 
7
- ## 功能
11
+ ## 为什么用它
12
+
13
+ - **原生,不替换。** 装饰 DSH 自己的 PDF 预览器,缩放、选中、刷新、Office→PDF 都保持原样;不额外装阅读器、不依赖侧边栏插件。
14
+ - **模型真的能读这本书。** 三个工具给它页码/目录、文本搜索和页面内容;只有需要看公式、图表时才花图片 token。
15
+ - **记住你读到哪。** 再次打开同一本书,自动回到上次的页码和缩放。
16
+
17
+ ## 三步上手
8
18
 
9
- **阅读工具栏** —— 只在右侧栏的 `.pdf` 预览上出现,不替换官方预览器。
19
+ 1. DSH 的 **Plugins → Add plugin** → `dsh-book-reader`(或 `dsh plugin add dsh-book-reader`),重启 DSH。
20
+ 2. 在右侧栏打开任意 `.pdf`,预览头部出现工具栏。
21
+ 3. 直接问模型关于这本书的问题,它就能自己搜、自己读。
22
+
23
+ 可选——把阅读指南交给模型(成本、页码规则、常见坑):
24
+
25
+ ```sh
26
+ ln -sfn "<已安装的包目录>" "<workspace>/.dsh/skills/pdf-reader"
27
+ ```
28
+
29
+ ## 功能
10
30
 
11
- - 上一页/下一页、跟随滚动的页码读数、跳页框(印刷页码,或 `p152` 表示 PDF 第 152 页)。
12
- - 全文搜索(`Cmd/Ctrl+Shift+F`):点结果跳页,或把该页引用插入对话框。
13
- - AI 位置提示:每轮对话开始前,模型可能收到一行“你正在看第 N 页”的短提示(`AI●` 开关,默认开)。
14
- - **记住阅读位置**:再次打开同一本书时恢复上次的页码和缩放;状态按书保存在 DSH 侧,重启后仍有效。
15
- - 每个控件都有悬停/聚焦说明,跟随 DSH 界面语言(中文或英文)。
31
+ | | 功能 | 说明 |
32
+ | --- | --- | --- |
33
+ | 🧭 | 翻页与跳页 | 上一页/下一页、跟随滚动的页码读数、跳页框(印刷页码,或 `p152` 表示 PDF 第 152 页) |
34
+ | 🔎 | 全文搜索 | `Cmd/Ctrl+F` 打开搜索,命中文字在页面上高亮;`Enter` / `Shift+Enter` 在命中之间顺序跳转,不显示摘要 |
35
+ | 🤖 | AI 位置提示 | 每轮对话最多一行“你正在看第 N 页”,页码变了才发(`AI●` 开关) |
36
+ | 💾 | 阅读记忆 | 按书记住页码和缩放,重新打开时恢复 |
37
+ | 💬 | 悬停说明 | 每个控件都有悬停/聚焦提示,跟随 DSH 界面语言(中/英) |
38
+ | 🧰 | 模型工具 | `pdf_info`、`pdf_search`、`pdf_read_pages`,见下 |
16
39
 
17
- **模型工具** —— 你问 PDF 内容时由模型调用。
40
+ ## 模型工具
18
41
 
19
- | 工具 | 作用 |
20
- | --- | --- |
21
- | `pdf_info` | 页数、印刷页↔物理页偏移、目录/TOC |
22
- | `pdf_search` | 字面全文搜索;返回页码和摘要 |
23
- | `pdf_read_pages` | 读取指定页的文本,也可返回页面图片 |
42
+ | 工具 | 用途 | 成本 |
43
+ | --- | --- | --- |
44
+ | `pdf_info` | 页数、印刷页↔物理页偏移、目录/TOC | 文本,几乎免费 |
45
+ | `pdf_search` | 找短语、定理编号、图表标题。容忍引号/LaTeX、连字符与数学符号拆分,支持关键词回退;正文页优先;可用 `pages` 限定页范围 | 文本,几乎免费 |
46
+ | `pdf_read_pages` | 读指定页的文本,可选返回页面图片 | 文本几乎免费;图片约 350–1024 token/页 |
24
47
 
25
- ## 安装
48
+ 可以这样问:
26
49
 
27
- 1. DSH 的 **Plugins → Add plugin** 输入 `dsh-book-reader`(或命令行 `dsh plugin add dsh-book-reader`)。
28
- 2. 安装或升级后重启 DSH。
29
- 3. 可选:把 skill 软链到工作区,让模型拿到阅读流程说明:
50
+ > “这本书第 5 章讲了什么?” → `pdf_info` + `pdf_read_pages`
51
+ > “找一下 singular value 出现在哪几页。” → `pdf_search`
52
+ > “只在第 7 章(印刷页 270–295)里找 SVD。” → `pdf_search` 加 `pages:"270-295"`
53
+ > “把定理 5.6 的公式截给我看。” → `pdf_read_pages` 带图片
30
54
 
31
- ```sh
32
- ln -sfn "<已安装的包目录>" "<workspace>/.dsh/skills/pdf-reader"
33
- ```
55
+ 什么时候用文本、什么时候值得花图片 token,由模型自己判断。
34
56
 
35
57
  ## 配置
36
58
 
@@ -38,17 +60,31 @@
38
60
 
39
61
  - `rememberView`(默认 `true`)—— 按书记住页码/缩放。
40
62
  - `maxPagesPerCall`(默认 `4`)—— 单次读页上限。
41
- - `defaultScale`(默认 `2.5`)—— 图片渲染倍率。
63
+ - `defaultScale`(默认 `2.5`)—— 图片渲染倍率(约 180 DPI)。
42
64
  - `autoShare`(默认 `true`)—— AI 位置提示。
43
65
 
44
66
  完整键和默认值见 [`cordis.patch.yml`](cordis.patch.yml) 注释。
45
67
 
68
+ ## 常见问题
69
+
70
+ **看不到工具栏?** 只有 `.pdf` 预览会挂工具栏(`.docx`/`.pptx`/图片不会)。升级/重装插件后重启 DSH。
71
+
72
+ **模型不知道我在哪一页?** 位置提示每轮最多一次、页码变了才发;打开 `AI●`,或直接在消息里提页码。
73
+
74
+ **扫描件?** 没有文本层:搜索为空、文本读页为空;页面图片仍然可用,先用 `pdf_info` 的 `textCoverage.isLikelyScanned` 判断。
75
+
76
+ **搜索找不到东西?** 引号和 `$…$` 会被去掉,连字符/数学符号拆分、缺空格有回退;整句没有字面命中时会退化成关键词 AND 搜索(结果的 `mode` 会说明命中方式)。试一个更短、更独特的词。
77
+
78
+ **会改我的 PDF 吗?** 不会。只读取和渲染,不会往书目录写任何东西。
79
+
80
+ **记住的状态存哪?** DSH 数据目录下的 `~/.dsh/storages/book_reader_views.json`,按文件路径索引,最多保留 500 本。
81
+
82
+ **DSH 升级后工具栏失效?** 工具栏依赖预览器的私有 DOM 标记;即使失效,模型工具仍然可用。
83
+
46
84
  ## 说明
47
85
 
48
- - 只支持官方 PDF 预览;工具栏依赖该预览的私有 DOM 标记,DSH 升级可能影响行为。
49
- - 工具默认只在会话工作区内读取(可设 `allowOutsideWorkspace`);本地 HTTP 路由只认回环地址、无鉴权。
50
- - 扫描件没有文本层,只能看图(没有 OCR)。
51
- - `pdfjs-dist` 锁定 `6.3.289`,以和宿主进程一致。
86
+ - 本地 HTTP 路由只认回环地址、无鉴权;模型工具默认只在会话工作区内读取(可设 `allowOutsideWorkspace`)。
87
+ - `pdfjs-dist` 锁定 `6.3.289`,与 DSH 宿主进程保持一致。
52
88
 
53
89
  ## 更多
54
90
 
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pdf-reader
3
- description: 'Read a PDF (book, paper, manual) cheaply and accurately with the dsh-book-reader tools: page map/TOC, literal full-text search, then selective page reads (text first, images only when typography matters). Use whenever the user asks about specific pages, a section, a theorem, a figure, or "this page" in a PDF.'
3
+ description: 'Read a PDF (book, paper, manual) cheaply and accurately with the dsh-book-reader tools: page map/TOC, normalized text search (quote/LaTeX-tolerant, with hyphen/space and keyword fallbacks), then selective page reads (text first, images only when typography matters). Use whenever the user asks about specific pages, a section, a theorem, a figure, or "this page" in a PDF.'
4
4
  ---
5
5
 
6
6
  # Reading a PDF with `dsh-book-reader`
@@ -11,7 +11,7 @@ expense, not the first move.
11
11
  | Tool | Cost | Use for |
12
12
  | --- | --- | --- |
13
13
  | `pdf_info` | ~0 (text) | page count, printed↔physical offset, outline/TOC |
14
- | `pdf_search` | ~0 (text) | locating a phrase, theorem number, or figure caption |
14
+ | `pdf_search` | ~0 (text) | locating a phrase, theorem number, or figure caption (quotes and `$…$` are stripped; optional `pages` range) |
15
15
  | `pdf_read_pages` + `include_image:false` | ~0 (text, per page) | prose, theorem statements, exercise text |
16
16
  | `pdf_read_pages` (images on) | **~350–1024 tokens/page** | formulas, diagrams, tables, layout |
17
17
 
@@ -19,7 +19,12 @@ expense, not the first move.
19
19
 
20
20
  1. `pdf_info` once per document → note `printedOffset` and the outline. Never
21
21
  hardcode an offset; it is detected (`printed = pdf − printedOffset`).
22
- 2. `pdf_search` for a distinctive phrase → page numbers.
22
+ 2. `pdf_search` for a short, distinctive phrase → page numbers. If the result's
23
+ `mode` is not `"phrase"` (i.e. `"compact"` or `"terms"`), treat the hits as
24
+ candidate pages and read them before quoting exact wording. When the reader
25
+ is late in the book (or the position hint says so), pass `pages:"…"` around
26
+ the current printed page (e.g. `pages:"270-295"`) so earlier chapters cannot
27
+ crowd the 30-hit budget; `page_numbers:"pdf"` switches to physical pages.
23
28
  3. `pdf_read_pages` with **`include_image:false`** to read those pages as text.
24
29
  4. Only if the text is ambiguous or the question is about typography (a formula,
25
30
  a diagram, a table) → call again with images on, and crop.
@@ -82,7 +87,20 @@ needs it, and prefer `include_image:false` first.
82
87
  - `pdf_info` also reports `title`, `author`, `fileSizeBytes`, `pageSize` and
83
88
  `textCoverage` (`isLikelyScanned`). Check `textCoverage.isLikelyScanned`
84
89
  *before* searching: a scanned PDF has no text layer, so go straight to images.
85
- - Search is literal: it does not unfold hyphenation and is not a regex engine.
90
+ - **`pdf_search` is normalized text search, not a regex engine.** Wrapping
91
+ quotes/backticks and `$…$` are stripped; NFKC folds math alphanumerics
92
+ (`ℒ (𝑉)`→`L (V)`) and ligatures; dash variants are dropped, so line-break
93
+ hyphenation (`com- plex`) matches `complex`. If no literal phrase matches, the
94
+ tool falls back to a space-insensitive pass (`mode: "compact"`, so `L(V)`
95
+ finds `ℒ (𝑉)`) and then to keyword AND (`mode: "terms"`). Body pages rank
96
+ before front matter (at most 3 front-matter hits), `pages` counts every matched
97
+ page before `limit`, and the default limit is 30. If a query finds nothing, try
98
+ one shorter distinctive word and read the pages rather than assuming absence.
99
+ - **`pdf_search` can be scoped to a page range.** `pages:"270-295"` (same syntax
100
+ as `pdf_read_pages`, printed pages by default) only scans those pages; the
101
+ returned `pages` count then counts matches inside that range. Use it when the
102
+ user is reading a later chapter and whole-book hits would be dominated by
103
+ earlier pages.
86
104
  - Page numbers are 1-based; relative paths resolve against the session directory.
87
105
  - No annotation/note features: this plugin reads and renders only.
88
106