feat: 移植 homepage 的多级目录自动展开与隐藏 - #152
Conversation
Deploying documents with
|
| Latest commit: |
32f8b49
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://f8691dd1.documents-dq4.pages.dev |
| Branch Preview URL: | https://codex-adaptive-page-outline.documents-dq4.pages.dev |
m1ngsama
left a comment
There was a problem hiding this comment.
Thanks for the careful write-up and the verification notes — the underlying need is real, long tutorial pages do want a deeper outline. But I can't merge this as it stands: two of the defects below mean the feature does not actually work as described, and the approach itself costs more than it returns for this site.
Scope and positioning
.vitepress/theme/is currently 17 files / 794 lines; this PR adds 276 lines in one file, growing the theme layer by ~35% for a single interaction nicety.- Roughly 90% of the visible gain comes from the one-line
outline.level: [2, 4]inconfig.mts; the component only adds "collapse the non-active sections". Layout.vuehides.VPDocAsideOutlinewithdisplay: noneinstead of replacing it, so the native component stays mounted and itsuseActiveAnchorscroll handler keeps running alongside the new one — two outline engines on every page.- The native outline is built from build-time page data and ships in the static HTML;
CurrentOutlinescans the DOM inonMounted, so the desktop outline is empty in the build output. That is a real regression for a static docs site.
Cross-device behaviour
outline.level: [2, 4]is global and also feeds the mobileVPLocalNavdropdown (getHeaders(frontmatter.outline ?? theme.outline)). The default is2, so mobile currently lists onlyh2; after this change it flattensh2/h3/h4into one dropdown — 53 entries ontutorial/manual/windows-from-scratch.md— with none of the collapsing this PR adds. The PR notes mobile keeps the native outline, but not that the native outline gets several times longer.- The aside is
display: nonebelow 1280px, yet the component still mounts there. On phones, tablets and narrow desktop windows the scroll handler runs onegetBoundingClientRect()per heading per animation frame — 53 forced layout reads per frame on that same page — for UI nobody can see. VitePress guards this withif (!isAsideEnabled.value) returnplusthrottleAndDebounce(fn, 100); neither was ported. - Desktop and mobile will list different entries: desktop uses the custom grouping (which drops level-skipping headings), mobile uses the native
buildTree(which keeps them).
Defects, most severe first
CurrentOutline.vue:228— the active marker never renders..current-outline-linkis bothposition: relativeandoverflow: hidden(needed for the ellipsis), so its own::beforeatleft: -17pxis clipped by its containing block. Even withoutoverflow,-17pxonly lines up for level two; the nested<ol>s add 14px and 14+12px of padding, putting the bar inside the text column for levels three and four. VitePress sidesteps this by making.outline-markera sibling in.content, not a child of the link.CurrentOutline.vue:70— nothing in the last viewport ever activates. The test isabsoluteTop <= scrollY + getScrollOffset() + 4, and at maximum scrollscrollY = docHeight - innerHeight, so any heading in the finalinnerHeight - scrollOffsetpixels (~700px on a laptop) can never match. Since children only render whenactiveSectionId === section.id, the last##of every page never expands its subheadings — exactly the case this PR is meant to serve. VitePress'ssetActiveLinkhas an explicitisBottombranch for this; the port dropped it.CurrentOutline.vue:55-61— level-skipping headings are silently dropped:h4is kept only after anh3,h3only after anh2.archived/2023/developer/2023-10-newcomer-training.md:20,26hits this today —#### 基础and#### 进阶sit under a list-nested## Blog From Scratchwith no###between, so both vanish from the desktop outline with no fallback, since the native one is hidden unconditionally.CurrentOutline.vue:41— theignore-headerguard mis-parents. On an ignoredh2the loopcontinues without resettingcurrentSection/currentSubsection, so every followingh3is pushed into the previoush2and appears when the wrong section is active. No page uses{.ignore-header}yet, so it is latent, but the guard as written is worse than none. VitePress marks the whole ignored subtree instead.CurrentOutline.vue:127-128— noisAsideEnabledguard and no throttle on thescroll/resizelisteners (see cross-device note above).CurrentOutline.vue:29-33—headingTitlestrips only.header-anchor; VitePress'sserializeHeaderalso dropsVPBadge,footnote-refandignore-headerchildren. No heading contains a badge today, so this is a robustness gap rather than a live bug.
Smaller points
- 276 new lines with no test. The repo has 8 vitest files; the
readHeadingsgrouping is plain, testable logic. - Active items are missing
aria-current, which the native outline sets. - Only
frontmatter.outline === falseis honoured; a per-page level override is ignored. - The checklist marks browser verification of the two→three→four expansion, but items 1 and 2 are visible at a glance in a browser, so that pass needs redoing.
- The four
verify-distfailures were attributed to backslash paths on Windows and left unconfirmed; please settle that on Linux CI before the next round.
Suggested path
My preference is the minimal one: keep outline.level: [2, 4] in config.mts and drop the component. One line gets the deeper outline, with zero maintenance, identical desktop and mobile output, and the static HTML intact. The cost is no collapsing, which I think is the right trade for a site optimised for being correct and durable rather than clever. If the longer mobile dropdown is a concern we can settle on [2, 3] separately.
If you want to keep the collapsing behaviour, please rebuild it on the native data rather than a DOM scan: reuse getHeaders/buildTree and add collapsing only in the render layer, then add the isAsideEnabled guard, the 100ms throttle and the isBottom branch, move the marker to a sibling of the links, and cover the grouping with a unit test. That keeps the two devices consistent and the build-time outline intact.
Either way the direction is worth having — happy to review the follow-up.
Summary
移植 Yuna-Celisse/homepage 原有的 CurrentOutline,让桌面端本页目录随阅读位置展开当前章节的子标题,并隐藏其他章节的子标题。
二级标题始终显示;当前二级章节显示三级标题,当前三级小节显示四级标题。例如在「计算机硬件系统的搭建与维护」中,读到「如何评估硬件参数」时显示 CPU、内存等评估要点,进入「购入技巧」后隐藏上一章节的子目录。
适配中文目录标签、现有 ArchiveMeta 布局和
outline: false;使用 VitePress 的getScrollOffset()对齐本站锚点跳转偏移,并清理待执行的动画帧。移动端保留 VitePress 原生目录。保留来源组件的自动展开交互,没有加入手动折叠按钮。Scope
Checklist
Verification
本机默认 Node 24 / pnpm 11 与仓库要求的 Node 22 / pnpm 9 不匹配,因此直接运行已安装依赖的 CLI;以下为相应脚本的等效验证,标准环境由 CI 再检查。
git diff --check通过。Notes
只修改主题组件、Layout 和 outline 层级配置,文章内容、路由和侧边栏链接均未改动。
Windows CRLF 检出触发现有维护人正则测试及一处 Markdown 格式检查问题;在独立 LF 检出中通过上述检查,未将换行修改带入 PR。
verify-dist在 Windows 下报告 4 个包含反斜杠路径的缺失页面问题,尚需 Linux CI 确认。