Skip to content

Fix annotation visibility and current chapter picker - #151

Open
q107580018 wants to merge 6 commits into
finlater:mainfrom
q107580018:feat/single-chapter-annotation-sync
Open

Fix annotation visibility and current chapter picker#151
q107580018 wants to merge 6 commits into
finlater:mainfrom
q107580018:feat/single-chapter-annotation-sync

Conversation

@q107580018

Copy link
Copy Markdown

变更说明

修复划线和想法显示状态,以及“选择章节匹配”的章节定位体验:

  • 菜单左侧勾选框直接反映用户的显示设置,避免数据尚未匹配时错误显示为未勾选。
  • “选择章节匹配”自动跳转到当前阅读位置所在页。
  • 当前阅读位置所在章节以粗体显示。
  • 增加章节选择器和显示设置相关回归测试。

类型

  • Bugfix
  • Feature
  • Refactor
  • Documentation
  • Other

Bugfix 要求

修复前复现步骤:

  1. 打开已绑定 WeRead 书籍。
  2. 打开“显示划线和想法”菜单。
  3. 即使显示设置已开启,但当前文档尚未完成标注匹配,左侧勾选框仍显示为未勾选。
  4. 进入“选择章节匹配”时,需要手动翻页寻找当前阅读章节。

修复后,勾选框反映实际显示偏好,章节选择器会定位并突出显示当前章节。

测试

  • 已在 KOReader 中手动测试
  • 已运行 bash scripts/run_lua_specs.sh
  • 已运行 bash scripts/check_lua_namespace.sh
  • 已运行 luacheck main.lua _meta.lua weread spec
  • 如涉及插件加载/KOReader 兼容性,已运行或触发 KOReader integration
  • 不适用,仅文档或注释变更

测试说明:

已运行 bash scripts/run_lua_specs.sh。
与本次修改直接相关的 annotation_sync_controller_spec.lua 和 menu_prefetch_spec.lua 已通过。
完整测试在既有 external_annotations_single_chapter_spec.lua 的 syncExternalAnnotationsChapter mock 缺失处停止。

非公开 WeRead API

  • 不涉及非公开 WeRead API
  • 已新增或更新可复现的 Python 验证脚本

脚本路径:

不适用

复现命令与脱敏结果:

不适用

模块结构

本 PR 未新增项目 Lua 模块,现有修改遵循 weread/lib/weread/ui/ 命名空间规范。

截图

本 PR 涉及菜单交互,但修改已通过 KOReader 实机验证;当前未附截图。

Checklist

  • 我已经说明这个 PR 解决的问题或新增的特性。
  • 如果是 bugfix,我已经提供复现步骤或关联 issue。
  • 如果是新增 UI/交互特性,我已经提供截图或录屏。
  • 我没有提交 KOReader settings/weread.lua、API key、cookie、token、x-wrpa-* 或私人书籍内容。
  • 新增或移动的项目 Lua 模块遵循 weread/lib/weread/ui/ 命名空间规范。
  • 新增或修复的逻辑包含对应回归测试;若无法自动化,已在测试说明中解释原因。
  • 如果修改了用户可见文本,我已经更新 weread/lib/i18n.lua
  • 如果修改了菜单结构,我已经同步更新 README 菜单结构。
  • 如果涉及非公开 WeRead Web API,我已经在 scripts/ 中提交可独立运行、可复现的 Python 验证脚本,并填写了复现命令和脱敏结果。

… matching

Add "Sync single chapter…" to the local-book annotation menu so users can
sync one WeRead chapter at a time, keeping per-session request counts low
enough to stay under WeRead rate limits. The picker maps WeRead chapters to
local TOC entries by title (ignoring the whole-book chapterIdx, so volume
two's "第二十四章 序列2" stays chapter 24) and marks checkpointed chapters
as already synced.

Locating no longer runs one CREngine full-text search per underline:
findText with origin=0 always scans from the viewport to the end of the
book, which is what made single-chapter matching take minutes. Instead:

- Extract the bounded chapter text once via getTextFromXPointers and match
  quotes against a whitespace-normalized flat rendering in pure Lua
  (paragraph breaks, spaces and full-width spaces are ignored).
- Map flat byte offsets back to XPointers with a lazy getNextVisibleChar
  walk that stops at the furthest needed offset; never moves the viewport.
- Bound fallback hits with compareXPointers instead of rendered positions,
  and clear the transient selection created by text extraction/search.
- Fall back to whole-book search only when a chapter has no usable bounds
  or the walk diverges from the extracted text.

Other fixes:
- Normalize chapter titles byte-safely (Lua pattern classes truncate
  multi-byte UTF-8) and strip WeRead update suffixes like
  (第一更求推荐票); index normalized TOC titles for O(catalog+toc)
  matching (was 2.7s for 1400+ chapters).
- A single-chapter pick re-locates only the picked chapter plus
  checkpointed chapters without saved records, so matching does not get
  slower as the checkpoint grows.
- Add INFO-level external_annotation_perf logs (per chapter and per quote
  timings, quote byte length and short hash only, never full text) and
  performance-path specs asserting bounded chapters never call
  findAllText and never move the reading position.
…ocal text

Two correctness fixes discovered while testing the chapter-bounded locator on
device.

Locate improvements:
- Walk visible characters backwards from the next chapter boundary instead
  of forwards from the current chapter start.  A TOC XPointer can name an
  element rather than a text position, in which case
  getNextVisibleChar(<heading element>) may be treated as a move past the
  element; the next chapter's boundary XPointer is a stable exclusive point
  and getPrevVisibleChar from it lands on the final visible character of the
  current chapter.
- After mapping a quote to XPointers, read the mapped range back with
  getTextFromXPointers and compare its flattened text to the quote; on
  mismatch invalidate the walk and fall back instead of emitting a
  mislocated record.  This also guards against paragraph-separator and
  punctuation-width differences.

Thought popup fix:
- When tapping a local underline, title the popup with the exact local
  underline text instead of the WeRead review abstract, which can carry
  nearby context that differs from the sentence shown in the overlay.
- Deep-copy the tapped record's items before opening the popup so editing
  the popup never mutates the saved overlay records.

Tests updated for the reverse walk, the mapped-range validation, and the
popup title behavior.
@finlater

finlater commented Sep 5, 2026

Copy link
Copy Markdown
Owner

这个修改将主菜单勾选状态统一为全局 cache.show_annotations,方向符合预期。不过快捷菜单目前仍通过 reader_navigation.lua 调用 _annotationsVisibleForCurrentDocument() 计算 annotations_visible,因此在本地书只匹配了部分章节、尚未写入文档级 display=true 时,快捷菜单仍会错误显示“显示划线和想法”,与主菜单的全局开关状态不一致。

建议快捷菜单也直接读取同一个全局 cache.show_annotations 状态,并补充回归测试,验证全局开关开启和关闭时,主菜单勾选与快捷菜单“显示/隐藏”文案始终一致。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants