feat(outline): track reading position and flash jump landings (#64) - #68
Conversation
orange2ai
left a comment
There was a problem hiding this comment.
感谢这个 PR,方向完全符合 #64 的真实需求,实现质量在同类方案里是最好的:rAF 节流的 scrollspy、所见即所得与源码模式双支持、文末锁定最后一项、跳转锁防抢高亮、用 color-mix(var(--link-color)) 派生高亮色使 12 个主题零适配、用 ProseMirror Decoration 而非直接改 DOM 避开 unexpected-mutation 重绘——这些设计都是对的。类型检查与构建我这边复验通过。
合并前需要两处修改:
1(必须):文内锚点链接跳转没有接入跳转锁
[main.ts:309-311] 大纲点击走 outlineJumping 锁,但 editor.ts 内的锚点链接 click 处理器([editor.ts:459-462])只调用了 flashHeadingOnArrival,没有通知跳转锁。结果:点 [text](#anchor) 平滑滚动途中,scrollspy 会把大纲高亮切到沿途章节——这与 PR 自带的验收文档 outline-test.md 第三节的承诺直接矛盾。
修法建议:把跳转锁的启停下沉到 editor.ts 共用,或让 flashHeadingOnArrival 顺带通知锁,两条跳转路径行为保持一致。
2(必须):scripts/afterPack.js 的安全修复拆成独立 PR
[afterPack.js:10-16] 把 execSync 换成 execFileSync 消除 shell 注入面,修复本身正确、无副作用,我也认可合并——但它是独立的 fix(security) 提交(23e79ad),与本 PR 功能无关。拆出去单独合,保持 PR 历史可审计。
3(建议,不阻塞)
- [main.ts:398-413]
visualActiveIndex每帧重跑querySelectorAll全量标题并对视口以上每个标题读 rect。outlineItems在renderOutline时已缓存 element,滚动路径可复用缓存、仅在元素失联时回退实时查询,把每帧开销降到 O(命中数)。 - [editor.ts:101] 900ms 兜底在长距离平滑滚动时会提前播完闪烁带,可放宽到 ~1500ms 或仅在无滚动位移时启用。
4(rebase 提示):本 PR 在 feature-requests.md 里把「可调侧栏」re-affirm 为 declined——维护者刚刚已决定接受可调面板(design.md 已改为允许 180–420px 拖拽并持久化),rebase 时请移除该 declined 复核段落。
处理完 1、2 即可合并。
…veai#64) - Highlight the outline entry for the section at the top of the viewport while scrolling, in both visual and source modes; the entry stays revealed in long documents. Colors derive from each theme's link color. - Flash the landing heading once after outline or anchor-link jumps, held until the smooth scroll settles so it plays where the user is looking. The band is a ProseMirror decoration: a direct DOM class trips ProseMirror's unexpected-mutation redraw, which recreates the heading and destroys the band instantly. - Re-resolve outline entries against the live DOM on click and during scrollspy updates: Milkdown recreates heading nodes whenever the content is re-set, which silently detached the cached references and killed outline jumps entirely. - Pause scrollspy updates while a click-driven jump is in flight so the clicked entry is not overwritten mid-scroll. - Add hover tooltips so truncated outline titles stay readable. Side panel reveal now scrolls the panel itself instead of using scrollIntoView, whose ancestor walk also cancelled the content pane's smooth scroll.
Add a multi-level heading document for manual verification of the outline scrollspy, jump flash, and tooltip behavior.
- Emit an editor jump phase signal ('start'/'settle') from
flashHeadingOnArrival and drive the outline jump lock from it, so
anchor-link jumps hold the reading-position highlight exactly like
outline-driven jumps instead of letting scrollspy switch to sections
passed along the way (review on marswaveai#68).
- Gate both fallback timers on scroll displacement: they re-arm while the
position keeps changing, so long smooth jumps no longer release early.
- Reuse the cached heading references in the scrollspy hot path and fall
back to a live query only when a cached node went missing, dropping the
per-frame querySelectorAll.
- Drop the re-affirmed declined note for the resizable file panel: the
panel width decision now lives in design.md (180-420px drag).
505ae1e to
222f34a
Compare
|
感谢详尽的 review,两点必须项与两项建议都已处理(force-push 更新,rebase 到最新 main):
验证:tsc / build / check-theme-colors 通过,锚点锁与大纲点击回归均已实测。 |


解决的 Issue
Closes #64
改动内容
1. 阅读进度同步(进度视图)
--link-color通过color-mix派生(复用搜索高亮的既有模式),12 个内置/独立主题零适配自动跟随,深色主题同样适用。2. 跳转反馈(对标飞书的闪烁提醒)
[text](#anchor)锚点链接跳转后,落点标题整行闪过一次主题色高亮带(约 1.6s 渐隐)。scrollend)后才开始播放,保证用户看到落点时反馈刚好出现。3. 顺带修复:大纲点击跳转静默失效
scrollIntoView变成对游离节点调用,点击跳转就「丢了」。点击时实时重新解析 DOM;scrollspy 热路径复用renderOutline缓存的引用、仅在节点失联时回退实时查询,避免每帧querySelectorAll。scrollIntoView的祖先链滚动会连带取消正文窗格进行中的平滑滚动。4. 长标题显示
Review 修正(2026-09-03)
start/settlephase 信号,锚点链接与大纲点击走同一把锁——长距离锚点滚动途中大纲高亮不再被沿途章节抢走(CDP 实测:滚动途中高亮保持原章节,落点后切换到目标章节)。验证
tsc --noEmit、electron-vite build、check:theme-colors(12 主题)全部通过。outline-test.md多级标题验收文档(含超长标题、重复标题锚点、文内跳转链接),按文档开头清单可逐项手动验证。