+
+-
++
+
+@@
+
+
+-
++ 0 ? "" : undefined}>
+@@
+
+
+
+-
++
+```
+
+`data-has-toc` 是给客户端 JS 读取的标记,用来判断当前页面是否需要触发平移。必须同时满足两个条件才标记:
+
+- `siteConfig.toc.enable` 为 `true`(用户在 `src/config.ts` 中开启了 TOC 功能)
+- `headings.length > 0`(当前文章有可渲染的标题树)
+
+只要其中一个不满足,就不会标记 `data-has-toc`,JS 也就不会给 body 加 `toc-offset` 类,正文保持原版居中行为。
+
+### 3. 客户端根据情况给 body 加 `toc-offset` 类
+
+打开 `src/layouts/Layout.astro`,先在 frontmatter 的 import 里加上 `TOC_BREAKPOINT`
+```diff title="src/layouts/Layout.astro" lang="astro"
+ import {
+ AUTO_MODE,
+ BANNER_HEIGHT,
+ BANNER_HEIGHT_EXTEND,
+ BANNER_HEIGHT_HOME,
+ DARK_MODE,
+ DEFAULT_THEME,
+ LIGHT_MODE,
+ PAGE_WIDTH,
++ TOC_BREAKPOINT,
+ } from "../constants/constants";
+```
+
+然后在文件末尾追加一段脚本。注意必须用 `is:inline define:vars` 把常量注入到客户端脚本,否则 `
+```
+
+逻辑:当前页面有 `data-has-toc` 且视口 ≥ `TOC_BREAKPOINT` 时,给 `` 加 `toc-offset` 类,否则移除。
+
+之所以要监听 Swup 的 `content:replace` / `page:view` 钩子,是因为 Fuwari 用 Swup 做无刷新跳转,`` 不在替换范围内,必须由 JS 在每次页面切换后重新判定。
+
+### 4. 用 CSS 实现平移、过渡与顶栏对齐
+
+回到 `src/layouts/MainGridLayout.astro`,在文件末尾追加 `
+```
+
+要点说明:
+
+- 用 `margin: calc((100% - var(--page-width)) / 2)` 取代 `mx-auto`,是因为 `auto` 无法参与 `transition`,显式 calc 才能让平移有过渡动画
+- `body.toc-offset` 下左右 margin 各减 `toc-width / 2`,等于把整个容器向左平移 `toc-width / 2`,使「正文 + TOC」整体居中
+- **顶栏对齐**:`#top-row` 自带 `md:px-4`(左右各 1rem padding),Navbar 受 padding 限制,如果直接让 `#top-row` 的 max-width = `page-width + toc-width`,Navbar 右边会比 TOC 右边少 1rem。所以让 `#top-row` 右边多伸出 1rem(`margin-right` 少 1rem、`max-width` 多 1rem),Navbar 减去右 padding 后刚好对齐 TOC 右边
+- `#main-grid` / `#sidebar-sticky` 强制关闭 `transition-property`,防止正文内部在平移期间发生位移抖动
+
+## 如何使用
+
+方案默认开启,无需任何配置。只需保证 `src/config.ts` 中 TOC 处于启用状态
+```ts title="src/config.ts"
+toc: {
+ enable: true, // Display the table of contents on the right side of the post
+ depth: 3, // Maximum heading depth to show in the table, from 1 to 3
+},
+```
+
+配置好后 `pnpm dev` 测试一下,访问任意带标题的文章页,宽屏下「正文 + TOC」会作为一个整体在视口居中,顶栏 Navbar 的左右边缘也会与正文内容、TOC 右边对齐;窗口缩到 1536px 以下或进入无 TOC 的页面时,自动恢复正文正中布局,整个过程带 500ms 缓动。
+
+## 触发条件
+
+| 条件 | 是否触发 `toc-offset` | 表现 |
+| --- | --- | --- |
+| 有 TOC 且视口 ≥ 1536px | 是 | 正文 + TOC 整体居中,Navbar 对齐 TOC 右边 |
+| 有 TOC 但视口 < 1536px | 否 | TOC 隐藏,正文居中 |
+| 无 TOC(如首页 / 关于页) | 否 | 正文居中(原版行为) |
+
+如果想调整正文宽度,修改 `src/constants/constants.ts` 的 `PAGE_WIDTH` 即可,`--toc-width` 会自动跟随。
+
+如果想调整 TOC 显示的断点,修改 `src/constants/constants.ts` 的 `TOC_BREAKPOINT`,**同时**把第 4 步 CSS 里的 `@media (min-width: 1536px)` 同步改成新值。
diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro
index 7849d78..39380d3 100644
--- a/src/layouts/Layout.astro
+++ b/src/layouts/Layout.astro
@@ -15,6 +15,7 @@ import {
DEFAULT_THEME,
LIGHT_MODE,
PAGE_WIDTH,
+ TOC_BREAKPOINT,
} from "../constants/constants";
import { defaultFavicons } from "../constants/icon";
import type { Favicon } from "../types/config";
@@ -572,10 +573,10 @@ if (window.swup) {
}
-