inkpaper 功能详解

inkpaper 功能详解

inkpaper 的功能不多,但每个都想清楚了再做。这篇把各个功能点拆开讲。

侧边栏配置

侧边栏是用得最多的导航元素,所以做了比较细的配置粒度。

Astro 版本通过 getSidebarItems 函数生成侧边栏数据,配置方式与 VitePress 版对齐:

inkpaper({
  sidebar: {
    home:    { show: true, tree: 'directory' },
    archive: { show: true, tree: 'date' },
    tags:    { show: false },
    article: { show: true, tree: 'directory' },
  },
})

四种页面类型,每种可以独立配置:

页面类型默认 show默认 tree
hometruedirectory
archivetruedate
tagsfalse
articletruedirectory

两种树类型:

  • directory:按内容集合的目录结构组织,目录名作为分组标题,文件按日期倒序排列。适合文章有明确分类的场景。
  • date:按年份→月份的时间线组织。适合归档浏览。

不传 sidebar 配置就是默认行为,和上面表格里写的一样。

首页组件

HomeLayout 显示四块内容:

  1. 标题和副标题:分别读取集成配置的 titledescription。改配置就能改首页显示,不用动组件代码。
  2. 统计栏:文章总数、标签总数、最近更新日期。一行排开,信息密度高。
  3. 标签云:取出现次数最多的前 10 个标签,点击跳转到标签页并自动筛选。
  4. 最近文章:最新的 10 篇文章,显示标题、日期、标签。列表项有交错的 fadeInUp 动画。

归档页组件

ArchivePage 把所有文章按年份分组,年份倒序排列。每个年份标题后面带文章数量。

实现上就是拿到 posts 数据,按 date.slice(0, 4) 分组,排序,渲染。没有分页——个人博客很少有上千篇文章的情况,全量渲染反而搜索体验更好。

标签页组件

TagsPage 分两个区域:

顶部是标签云,显示所有标签和对应的文章数。点击标签切换筛选,再点一次取消。当前选中的标签用朱砂色标记。

下面是文章列表,根据选中标签实时过滤。不选标签时显示全部文章。

标签页支持 URL query 参数:访问 /tags?tag=CSS 会自动选中 CSS 标签。首页标签云的链接就是用这个机制跳转的。

交互使用 vanilla JS + data-* 属性实现,无需引入额外的前端框架。

文章侧边栏

每篇文章右侧显示三块信息:

字数统计和阅读时间:在构建时从 Markdown 源文件计算,去掉 frontmatter 和标记语法后统计字符数。阅读时间按 400 字/分钟计算,至少显示 1 min。

当前文章标签:从 frontmatter 读取,每个标签链接到标签页。

相关文章:推荐逻辑分两层。优先查找用户通过配置提供的 related 映射。如果没有,fallback 到标签交集:遍历所有文章,计算与当前文章共有标签数作为分数,取前 5 篇。

related 配置格式:

inkpaper({
  related: {
    '/posts/some-article': [
      { link: '/posts/another-article', title: '另一篇文章' }
    ]
  }
})

路由自动注入

Astro 版本通过 injectRoute API 自动注入四个默认页面路由:

路由页面
/首页
/archive归档页
/tags标签页
/posts/[...slug]文章详情页

用户不需要手动创建任何页面文件。如果需要自定义某个页面,在 src/pages/ 下创建同名文件即可覆盖默认路由——Astro 的路由优先级机制保证用户定义的路由优先。

虚拟模块 virtual:inkpaper-config 的 TypeScript 类型声明也通过 injectTypes API 自动注入,用户不需要在 env.d.ts 里手写。

内容集合

Astro 版本使用 Content Collections 管理文章。这是目前唯一需要用户手动配置的文件——Astro 暂未提供集成级别注入 content collection 的 API。

// src/content/config.ts
import { defineCollection } from 'astro:content'
import { postSchema } from '@inkpaper/astro/content'

export const collections = {
  posts: defineCollection({ type: 'content', schema: postSchema }),
}

getSortedPosts 从内容集合读取文章,提取 titledatetagsorder,按日期倒序排列。

包结构

主题分成四个 npm 包:

@inkpaper/core 是纯 CSS,定义设计变量、字体加载、宣纸纹理、通用组件样式。不依赖任何框架。

@inkpaper/vitepress 依赖 core,提供 Vue 组件、Layout、数据注入、sidebar 配置函数、内容加载器。

@inkpaper/astro 依赖 core,提供 Astro 集成(自动注入路由和类型声明)、Astro 组件、Layout、sidebar 生成、内容集合 schema。

@inkpaper/create-for-vitepress 是 VitePress 版本的脚手架工具。Astro 版本不需要脚手架——集成本身已包含所有默认页面。

两个框架版本共享 @inkpaper/core 的全部 CSS,组件层各自实现。