{"_id":"@ceynri/astro-toc-rail","name":"@ceynri/astro-toc-rail","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@ceynri/astro-toc-rail","version":"0.1.0","type":"module","description":"A restrained, PC-only rail-style table of contents for long-form Astro articles, with 2D scroll progress and a reading percentage.","author":{"name":"ceynri","email":"ceynri@gmail.com","url":"https://github.com/ceynri/"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/ceynri/astro-toc-rail.git"},"homepage":"https://github.com/ceynri/astro-toc-rail#readme","bugs":{"url":"https://github.com/ceynri/astro-toc-rail/issues"},"keywords":["astro-component","withastro","astro","ui","toc","table-of-contents","reading-progress","minimap","sidebar"],"exports":{".":"./index.ts","./toc-rail.astro":"./toc-rail.astro","./core":"./core.ts"},"publishConfig":{"access":"public"},"scripts":{"dev":"astro dev","build":"astro build","preview":"astro build && astro preview","check":"biome check --write","typecheck":"astro check","test":"vitest run","test:watch":"vitest","clean":"rm -rf node_modules dist .astro"},"peerDependencies":{"astro":"^5 || ^6"},"devDependencies":{"@astrojs/check":"^0.9.9","@biomejs/biome":"^2.5.2","@types/node":"^24.0.0","astro":"^6.4.8","typescript":"^5.8.3","vitest":"^4.1.9"},"gitHead":"45e80f94a6b94c8f8c75043ff5cc0dd6ae4804af","_id":"@ceynri/astro-toc-rail@0.1.0","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-iAEFADw9lMvUlrbx5O/PmZ8wj7tGAaCe48EiMKlUf2A/6+RvzoXVDjghTKOzCwWatQxeO0I0B08kE5JUaMD8+w==","shasum":"7c87f13412c2ee06946b501b7519496726c14122","tarball":"https://registry.npmjs.org/@ceynri/astro-toc-rail/-/astro-toc-rail-0.1.0.tgz","fileCount":7,"unpackedSize":45502,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFaesJbqrCFHZJDSxNwBXczRtGo+J2q2uJr5quGEi03wAiAKJ1MAvLv0lkUM4H15sy+Gj4fChOu3B4b+bmNBsCs1Zw=="}]},"_npmUser":{"name":"ceynri","email":"ceynri@gmail.com"},"directories":{},"maintainers":[{"name":"ceynri","email":"ceynri@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/astro-toc-rail_0.1.0_1783268701089_0.626530407736887"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-05T16:25:00.997Z","0.1.0":"2026-07-05T16:25:01.229Z","modified":"2026-07-05T16:25:01.514Z"},"maintainers":[{"name":"ceynri","email":"ceynri@gmail.com"}],"description":"A restrained, PC-only rail-style table of contents for long-form Astro articles, with 2D scroll progress and a reading percentage.","homepage":"https://github.com/ceynri/astro-toc-rail#readme","keywords":["astro-component","withastro","astro","ui","toc","table-of-contents","reading-progress","minimap","sidebar"],"repository":{"type":"git","url":"git+https://github.com/ceynri/astro-toc-rail.git"},"author":{"name":"ceynri","email":"ceynri@gmail.com","url":"https://github.com/ceynri/"},"bugs":{"url":"https://github.com/ceynri/astro-toc-rail/issues"},"license":"MIT","readme":"# @ceynri/astro-toc-rail\n\n面向长文的 PC 端「轨道式」文章目录（TOC rail），带二维滚动进度与阅读百分比。\n\n> English: see [README.md](./README.md)\n\n收起态是一组贴右侧、垂直居中的纯线条——文章标题的 minimap；鼠标移入时向左展开为文字目录浮层。随页面滚动呈现**二维进度**（哪些章节在屏、各看了多少），并显示整篇阅读百分比。\n\n定位：克制、不打扰阅读的页内导航 + 进度感知。\n\n## 特性\n\n- **收起态轨道**：H2 线最长，逐级变短（H3…H6），右对齐使短线左缘自然内缩，形成层级缩进观感。存在感低。\n- **hover 浮层**：向左展开覆盖在正文之上，文章 H1 作卡片标题头；当前在屏章节高亮；点击目录项平滑滚动跳转。\n- **二维进度**：把当前视口窗口投影到正文，得到一段可跨多条线的高亮带（边界线按可见比例部分填充）；高亮带中点在轨道内垂直居中（头尾钳制）。\n- **阅读百分比**：以视口垂直中线为焦点、正文范围为基准计算。\n- **可配置采集深度**：组件 prop 与文章 FrontMatter 逐文覆盖。\n- **渲染门槛**：标题数过少不渲染；视口宽度低于断点不挂载。\n- **CSS 变量主题**：不硬编码颜色。\n- **不绑定统计实现**：派发 DOM `CustomEvent`，由消费侧自行接入分析/埋点。\n\n## 环境要求\n\n- Astro `^5 || ^6`。\n- 标题 `id` 由 Astro 内置的 `rehypeHeadingIds` 生成，**无需额外的 slug 插件**。\n\n## 安装\n\n```bash\npnpm add @ceynri/astro-toc-rail\n```\n\n这是**源码直发**的包（发布 `.astro` + `.ts`，无构建步骤），由 Astro/Vite 随你的应用一起编译。\n\n## 用法\n\n```astro\n---\nimport { render } from 'astro:content';\nimport { TocRail } from '@ceynri/astro-toc-rail';\n\nconst post = Astro.props.post;\nconst { title, tocDepth, toc } = post.data;\nconst { headings } = await render(post);\n---\n\n<article data-toc-body>\n  <Content />\n</article>\n\n{toc !== false && (\n  <TocRail headings={headings} title={title} maxDepth={tocDepth} />\n)}\n```\n\n要点：\n\n- 给正文容器加 `data-toc-body`：它界定阅读百分比与高亮的基准范围（排除封面图、评论区、页脚等）。\n- `headings` 直接取自 Astro 的 `render()`。\n\n## Props\n\n| Prop | 类型 | 默认 | 说明 |\n| --- | --- | --- | --- |\n| `headings` | `MarkdownHeading[]` | — | `render()` 返回的标题（含全部层级，组件内部过滤） |\n| `title` | `string` | — | 文章标题（H1），作浮层标题头 |\n| `maxDepth` | `number`（2–6） | `3` | 采集到的最深标题层级。把文章 FrontMatter 的值传进来即可逐文覆盖 |\n| `minHeadings` | `number` | `2` | 标题数低于此值时整组件不渲染 |\n| `breakpoint` | `number` | `1024` | 视口宽度低于此值时不挂载组件 |\n| `bodySelector` | `string` | `[data-toc-body]` | 正文范围选择器 |\n| `label` | `string` | `Table of contents` | nav 地标的 `aria-label`；非英文站点可覆盖为本地语言 |\n| `class` | `string` | — | 透传到根 `<nav>` |\n\nH1 不入轨道、不计入采集、不引入缩进层级，仅作浮层标题头。\n\n## 事件（埋点 / 分析接入）\n\n组件**不调用**任何统计库，而是派发冒泡的 DOM `CustomEvent`，由你接入自己的埋点。节流/去重是消费方的职责——组件每次都如实上报原始信号。\n\n| 事件 | `detail` | 触发时机 |\n| --- | --- | --- |\n| `toc:anchor-click` | `{ slug: string }` | 点击目录项 |\n| `toc:rail-hover` | — | hover 展开轨道（**每次** hover 都派发） |\n\n```ts\n// 只要从 '@ceynri/astro-toc-rail' 引入过任意内容，e.detail 即有类型提示。\ndocument.addEventListener('toc:anchor-click', (e) => {\n  window.umami?.track('toc-anchor-click', e.detail); // { slug }\n});\n\n// 每次 hover 都会触发——若埋点只想「每页一次」，自行去重：\nlet hoverTracked = false;\ndocument.addEventListener('toc:rail-hover', () => {\n  if (hoverTracked) return;\n  hoverTracked = true;\n  window.umami?.track('toc-rail-hover');\n});\n```\n\n## 逐文配置（可选）\n\n若用 FrontMatter 驱动 `maxDepth` / 开关，字段须在内容集合 schema 中声明，否则 zod 会将其 strip 掉：\n\n```ts\ntocDepth: z.number().int().min(2).max(6).optional(),\ntoc: z.boolean().optional().default(true),\n```\n\n- `tocDepth`：本文采集到第几级标题（2–6），传给 `maxDepth`。\n- `toc: false`：关闭本文目录（用 `toc !== false` 门控）。\n\n## 主题（CSS 变量）\n\n组件把全部 `--toc-*` 默认值集中声明在 `:root` 一处，并以 `var(--toc-*)` 读取。消费侧通过在 `.toc-rail` 上设置同名变量来覆盖。由于**元素自身的值天然优先于从祖先继承的值**，消费侧的覆盖必然生效——与 CSS `@layer` 顺序、作用域特异性都无关。\n\n颜色变量（命名 = `元素-部位`）：\n\n| 变量 | 元素 |\n| --- | --- |\n| `--toc-rail-line-color` | 收起态轨道的基础细线 |\n| `--toc-rail-highlight-color` | 移动的二维高亮条 |\n| `--toc-percent-color` | 阅读百分比文字 |\n| `--toc-panel-bg` / `--toc-panel-shadow` | hover 浮层背景 / 阴影 |\n| `--toc-panel-title-color` | 浮层标题头（H1） |\n| `--toc-panel-item-color` | 普通目录项 |\n| `--toc-panel-item-active-color` | 当前（在屏）章节项 |\n| `--toc-panel-item-hover-color` | 鼠标悬停项 |\n\n尺寸变量：\n`--toc-len-2`…`--toc-len-6` · `--toc-line-thickness` · `--toc-line-gap` · `--toc-max-height` · `--toc-right` · `--toc-panel-width` · `--toc-indent`\n\n示例（绑定到自己的设计 token 以随明暗切换）：\n\n```css\n.toc-rail {\n  --toc-rail-highlight-color: var(--title-color);\n  --toc-rail-line-color: var(--border-color);\n  --toc-panel-item-hover-color: var(--accent-color);\n}\n```\n\n## 边界\n\n- **仅 PC**：低于 `breakpoint` 不挂载。移动端目录如有需要，由消费项目另行实现。\n- 轨道不响应滚轮，仅由代码自动定位。线条总高超过 `--toc-max-height` 时轨道内部自动滚动，上下缘以渐变蒙层提示尚有内容。\n- 假设为 MPA（无视图过渡）。若启用 Astro `<ClientRouter />`，需在 `astro:page-load` 重新挂载、`astro:before-swap` 清理监听器/Observer。\n\n## 导出\n\n- `@ceynri/astro-toc-rail` → `{ TocRail }` 及 `./core` 的全部导出。\n- `@ceynri/astro-toc-rail/toc-rail.astro` → 直接引组件文件。\n- `@ceynri/astro-toc-rail/core` → 零依赖纯逻辑（章节区间、高亮带、居中钳制、百分比），可复用来自定义目录 UI。\n\n## 本地开发\n\n仓库根**即**包本体；`src/pages/` 下有一个小的 Astro dev 页面用于本地预览（不发布）。\n\n```bash\npnpm install\npnpm dev        # 在 http://localhost:4321 预览 dev 页面\npnpm test       # 纯逻辑单测（core.ts）\npnpm typecheck  # astro check\npnpm build      # astro build（同时验证组件可正常集成）\npnpm check      # Biome lint + format\n```\n\n## 许可\n\n[MIT](./LICENSE) © Ceynri\n","readmeFilename":"README.zh-CN.md","_rev":"1-3f5838326c312c965b6e88221061c8b8"}