Skip to content

重构阅读器导览与朗读#467

Merged
chiimagnus merged 24 commits into
mainfrom
crh1
Jun 22, 2026
Merged

重构阅读器导览与朗读#467
chiimagnus merged 24 commits into
mainfrom
crh1

Conversation

@chiimagnus

Copy link
Copy Markdown
Member

背景

  • 旧的 reader 工具条是横向 popover,章节导览、正文设置和朗读控制分散在不同入口里,阅读时没有一个稳定的导览栏承载章节跳转。
  • 旧的句子高亮按块级元素和 inline style 处理,多个句子共享一个块时高亮范围会偏,DOM 变动后还会一次性重装饰,长文首帧容易被阻塞。
  • 旧的朗读状态把 cursor 和 idle 混在一起,停止、切换文章或失败后容易留下陈旧的 activeIndex / activeSentence
  • 旧的排版 preset 和 legacy profile 迁移不是同一套基线,重置和迁移后的默认阅读态不一致。

变更

阅读器导览栏

  • ReaderToolbar 收敛成竖向 rail,统一承载正文排版、主题、朗读和文章目录四个入口。
  • 新增 ReaderRailPanel 统一浮层容器,桌面在左侧展开,窄屏改为 trigger 下方弹层,Escape 可关闭,hover leave 160ms 后再收起。
  • 新增 ArticleOutlineMinimap,从正文 DOM 的 h1/h2/h3 构建目录条目,strip 和 panel 使用同一组 ReaderOutlineDomEntry,并通过 30% spy line 计算当前章节。
  • 这样改是为了把“章节跳转”变成阅读时始终可见的导览入口,而不是散落在普通菜单里。
  • 关键实现细节:目录条目保留既有 id,没有 id 时生成 reader-outline-*;主动项计算会过滤非法 rect,并按 heading 顺序排序。

句子级朗读与重装饰

  • 新增 reader-sentence-dom / reader-sentence-decoration,把文章文本按 sentence span 包裹成 data-reader-sentence-index 结构,并通过 Range / 文本分段回填真实 DOM。
  • 现在点击句子会先解析目标索引:idle 时只 seek 定位,不立刻播放;播放/暂停中则从该句继续 play
  • 长文装饰改成分批策略:句子数超过 400 或文本长度超过 16000 时,idle 不强行 eager decorate;首次装饰最多按 48 句/帧推进,避免首屏被整段包裹卡住。
  • DOM 变更由 MutationObserver 合并,静置 180ms 后才触发重建,避免连续更新时反复重装饰。
  • 这样改是为了解决块级高亮不够细、链接/按钮节点容易误伤,以及长文重装饰阻塞主线程的问题。
  • 关键实现细节:ReaderTtsEngine 新增 hasCursor / seek()stop() 和 source reload 会清空游标;globalThis.__syncnosReaderPerformance 只暴露聚合计数和耗时,方便排查性能但不泄露正文内容。

排版与偏好

  • 把文本排版的多个 preset 收敛成一个 canonical default reset,TextLayoutPanel 现在只保留 reset,不再提供三套预设按钮。
  • NarrationPanel 把语速滑杆改成固定档位按钮,并在 Web Speech 不可用时禁用对应引擎选项。
  • reader_prefs_v1 的默认排版基线和 legacy profile 迁移统一到了同一个默认值,contentWidth 上限放宽到 2000,tts.rate 范围收窄到 0.8-2。
  • 这样改是为了避免“重置”“迁移”“默认值”三套入口各自落到不同版式,导致用户看到的默认阅读态不一致。
  • 关键实现细节:迁移路径仍然只读,不回写 legacy key;UI 只写 reader_prefs_v1

测试

  • 打开 article/video 详情,期望右侧 Reader tools 变成纵向 rail,且同一时间只会展开一个面板。
  • 把 viewport 宽度缩到 720px 以下,期望面板从左侧浮出改为出现在 trigger 下方,并带有 300px 宽度和 70vh 高度上限。
  • 打开包含 h1/h2/h3 的文章并滚动页面,期望目录高亮跟随当前 spy line 变化;点击 strip 条目保持面板打开,点击 panel 条目滚动到对应 heading 并关闭面板。
  • 在含链接的文章里点击某个句子,期望 idle 时只定位 cursor 不开始播放;播放中点击另一个句子,期望从该句继续播放,链接点击不触发句子点击。
  • 打开超过 400 句或 16000 字符的长文并保持 idle,期望首帧不一次性包裹全部句子;开始朗读后,期望句子 span 逐批补齐并最终与句子数一致。
  • 切换文章 source 或 stop 朗读后,期望 activeIndexactiveSentencehasCursor 和 debug snapshot 的 lastError 都清空。
  • 在 NarrationPanel 点击 0.8x / 1.25x / 2x,期望写入对应的 tts.rate 固定值;在 Web Speech 不可用时,期望 Web 引擎选项被禁用。
  • 在 TextLayoutPanel 点击 Reset,期望恢复到 canonical default typography preset,而不是旧的三套 preset 之一。

@chiimagnus chiimagnus merged commit 43f0340 into main Jun 22, 2026
1 check failed
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.

1 participant