Skip to content

feat: 移植 homepage 的多级目录自动展开与隐藏 - #152

Open
Yuna-Celisse wants to merge 1 commit into
mainfrom
codex/adaptive-page-outline
Open

feat: 移植 homepage 的多级目录自动展开与隐藏#152
Yuna-Celisse wants to merge 1 commit into
mainfrom
codex/adaptive-page-outline

Conversation

@Yuna-Celisse

Copy link
Copy Markdown
Contributor

Summary

移植 Yuna-Celisse/homepage 原有的 CurrentOutline,让桌面端本页目录随阅读位置展开当前章节的子标题,并隐藏其他章节的子标题。

二级标题始终显示;当前二级章节显示三级标题,当前三级小节显示四级标题。例如在「计算机硬件系统的搭建与维护」中,读到「如何评估硬件参数」时显示 CPU、内存等评估要点,进入「购入技巧」后隐藏上一章节的子目录。

适配中文目录标签、现有 ArchiveMeta 布局和 outline: false;使用 VitePress 的 getScrollOffset() 对齐本站锚点跳转偏移,并清理待执行的动画帧。移动端保留 VitePress 原生目录。保留来源组件的自动展开交互,没有加入手动折叠按钮。

Scope

  • Tutorial
  • Process
  • Repair
  • Archived
  • Navigation
  • Assets or templates
  • CI or tooling
  • Governance docs

Checklist

  • I updated the relevant sidebar or confirmed no navigation change is needed.
  • I checked internal links and asset paths affected by this PR.
  • I kept historical archived content unchanged unless this PR is explicitly about archive maintenance.
  • I documented follow-up work that is intentionally outside this PR.

Verification

本机默认 Node 24 / pnpm 11 与仓库要求的 Node 22 / pnpm 9 不匹配,因此直接运行已安装依赖的 CLI;以下为相应脚本的等效验证,标准环境由 CI 再检查。

  • ESLint 全仓检查通过(LF 验证副本)。
  • Markdownlint:222 个文件,0 错误。
  • Vitest:8 个测试文件、60 项测试通过(LF 验证副本)。
  • VitePress 生产构建通过。
  • git diff --check 通过。
  • 浏览器验证二级→三级→四级自动展开、跨章节隐藏、中文锚点跳转、页面切换刷新、桌面布局及 390px 移动端原生目录。

Notes

只修改主题组件、Layout 和 outline 层级配置,文章内容、路由和侧边栏链接均未改动。

Windows CRLF 检出触发现有维护人正则测试及一处 Markdown 格式检查问题;在独立 LF 检出中通过上述检查,未将换行修改带入 PR。verify-dist 在 Windows 下报告 4 个包含反斜杠路径的缺失页面问题,尚需 Linux CI 确认。

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying documents with  Cloudflare Pages  Cloudflare Pages

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

View logs

@m1ngsama m1ngsama left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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] in config.mts; the component only adds "collapse the non-active sections".
  • Layout.vue hides .VPDocAsideOutline with display: none instead of replacing it, so the native component stays mounted and its useActiveAnchor scroll 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; CurrentOutline scans the DOM in onMounted, 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 mobile VPLocalNav dropdown (getHeaders(frontmatter.outline ?? theme.outline)). The default is 2, so mobile currently lists only h2; after this change it flattens h2/h3/h4 into one dropdown — 53 entries on tutorial/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: none below 1280px, yet the component still mounts there. On phones, tablets and narrow desktop windows the scroll handler runs one getBoundingClientRect() per heading per animation frame — 53 forced layout reads per frame on that same page — for UI nobody can see. VitePress guards this with if (!isAsideEnabled.value) return plus throttleAndDebounce(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

  1. CurrentOutline.vue:228 — the active marker never renders. .current-outline-link is both position: relative and overflow: hidden (needed for the ellipsis), so its own ::before at left: -17px is clipped by its containing block. Even without overflow, -17px only 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-marker a sibling in .content, not a child of the link.
  2. CurrentOutline.vue:70 — nothing in the last viewport ever activates. The test is absoluteTop <= scrollY + getScrollOffset() + 4, and at maximum scroll scrollY = docHeight - innerHeight, so any heading in the final innerHeight - scrollOffset pixels (~700px on a laptop) can never match. Since children only render when activeSectionId === section.id, the last ## of every page never expands its subheadings — exactly the case this PR is meant to serve. VitePress's setActiveLink has an explicit isBottom branch for this; the port dropped it.
  3. CurrentOutline.vue:55-61 — level-skipping headings are silently dropped: h4 is kept only after an h3, h3 only after an h2. archived/2023/developer/2023-10-newcomer-training.md:20,26 hits this today — #### 基础 and #### 进阶 sit under a list-nested ## Blog From Scratch with no ### between, so both vanish from the desktop outline with no fallback, since the native one is hidden unconditionally.
  4. CurrentOutline.vue:41 — the ignore-header guard mis-parents. On an ignored h2 the loop continues without resetting currentSection/currentSubsection, so every following h3 is pushed into the previous h2 and 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.
  5. CurrentOutline.vue:127-128 — no isAsideEnabled guard and no throttle on the scroll/resize listeners (see cross-device note above).
  6. CurrentOutline.vue:29-33headingTitle strips only .header-anchor; VitePress's serializeHeader also drops VPBadge, footnote-ref and ignore-header children. 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 readHeadings grouping is plain, testable logic.
  • Active items are missing aria-current, which the native outline sets.
  • Only frontmatter.outline === false is 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-dist failures 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.

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