3-hexo改造需求

文章关系图谱页面

我想给 3-hexo 增加一个独立的“文章星图”页面。它不是把所有文章平铺成标签云,而是把文章当作星点:相近的文章自然聚集,跨主题文章通过细线连接,读者可以在图中漫游并进入具体文章。

一句话先说结论:关系数据在 Hexo 构建期生成,页面只读取一份静态 JSON;浏览器端使用 Canvas 渲染力导向星图。

这样做不需要后端,也不需要在访问页面时扫描文章全文,部署到 GitHub Pages 后仍然可用。

目标和边界

这个页面解决的是“这篇文章附近还有什么”,而不是替代首页、分类页或全文搜索。

  • 入口固定为 /article-graph/,导航菜单增加“星图”。
  • 每篇已发布且未隐藏的文章是一个节点;草稿、hidden: true 文章不进入数据集。
  • 标签、分类和手工指定的关联文章构成边;关系必须可解释,不能做黑箱相似度推荐。
  • 单击节点进入文章,悬停和选中时显示摘要、标签、分类、发布日期和关联理由。
  • 首版目标是流畅浏览几百篇文章;不做登录、收藏、评论和服务端推荐。

关系如何计算

节点只保留页面必需的数据,避免把正文全部塞进 JSON:

{
	"id": "2026/01/29/Web/03-任务执行架构/",
	"title": "03-任务执行架构",
	"path": "/2026/01/29/Web/03-任务执行架构/",
	"date": "2026-01-29",
	"categories": ["Web", "长链路耗时任务怎么处理"],
	"tags": ["web", "long-task"],
	"summary": "API、队列、Worker 和状态存储的职责分层。",
	"relations": ["2026/01/28/Web/02-消息队列/"]
}

边按下面的规则生成,权重越高,连线越清晰、力导向布局中的吸引力越强:

关系 权重 说明
手工 relations 6 系列上下篇、前置知识、明确推荐阅读
相同二级分类 4 例如同属一个专题目录
相同一级分类 2 主题接近,但不应把整片图拉成一团
每个共同标签 1 最多累加到 3,避免热门标签制造过多边

只保留权重不低于 2 的边;每个节点最多展示权重最高的 8 条边。这样既能看见主题团簇,也不会出现一张无法阅读的蜘蛛网。

手工关系使用文章 front matter 中的相对 permalink 或文章 id,例如:

relations:
	- 2026/01/28/Web/02-消息队列/
	- 2026/01/30/Web/04-任务编排/

第一版可以不回填历史文章,先凭分类和标签自动成图;之后只为系列文章、跨领域但确实值得串联的文章补 relations

页面体验

页面采用一整块星图画布,不把主体塞进大卡片里。桌面端右侧保留窄信息栏,移动端改为底部抽屉。

星图行为

  • 节点:默认是低亮度象牙白点;分类决定细描边颜色,文章字数或关联度决定半径,置顶文章不额外放大。
  • 连线:默认只显示低透明度细线;悬停、搜索或选中节点时,只高亮它的一跳关系。
  • 聚类:布局初始按一级分类分区,随后以力导向计算微调;同类文章像星团,但不强行画封闭区域。
  • 漫游:支持拖拽平移、滚轮或双指缩放、双击复位。视口外节点不绘制标题,缩小时只画点。
  • 定位:搜索标题、标签或分类后自动聚焦第一个结果;列表显示匹配项,避免只靠在画布里找小点。
  • 降级:启用 prefers-reduced-motion 时关闭持续模拟;不支持 Canvas 或脚本加载失败时显示按分类排列的文章列表和普通链接。

信息与筛选

顶部只保留三个高频控件:搜索、分类筛选、重置视图。筛选项使用复选框而不是彩色文字按钮,颜色只作为辅助信息。

选中节点后,信息栏展示:

  • 文章标题和发布日期
  • 分类、标签
  • 文章摘要
  • 关联文章列表,以及“同标签”“同分类”或“手工关联”的关系说明
  • 一个明确的“阅读文章”链接

键盘操作至少支持 Tab 进入控件、Enter 打开当前结果、Esc 清除选中状态。星图本身要提供一个可切换的列表视图,保证触摸屏和读屏软件都有可用路径。

NASA 灵感的视觉规范

这里借鉴的是航天任务的科学可视化语言,不使用 NASA 的 Logo、徽章或官方字体。

元素 建议
背景 #07131F,叠加极弱的点阵和坐标网格
主要文字 #F3EFE6,保证深色背景上的阅读对比度
主强调色 #6EC8D6,用于焦点、链接和主分类
次强调色 #C8D9EA,用于次级分类和辅助刻度
选中状态 #F0A24A,只用于当前节点、结果计数和必要提示
字体 Space Mono 用于数字/坐标,Noto Sans SC 或现有中文无衬线字体用于正文
形状 直角或 2px 小圆角、1px 细线、充足留白;不使用玻璃拟态、大圆角面板和霓虹光晕

背景网格、坐标、节点编号和细分割线需要克制到不影响文章标题阅读。节点文字遵循“按缩放级别渐进显示”:远看是星点,中距离显示标题,聚焦时才展示完整信息。

实现拆分

1. 构建期数据生成

新增 scripts/article-graph.js,注册 Hexo generator

  1. 遍历 hexo.locals.get('posts'),过滤草稿和隐藏文章。
  2. 提取 titlepathdatecategoriestagsexcerpt 和可选 relations
  3. 规范化 ID、去重、生成加权边并裁剪低价值连接。
  4. 输出 /article-graph/data.json,同时生成 /article-graph/ 对应的页面数据。

摘要优先使用 Hexo 已生成的 excerpt;没有摘要时只截取纯文本前约 120 个字符。生成器不得把 HTML 正文直接输出,避免数据文件膨胀和未过滤标签混入画布。

2. 页面与静态资源

新增一个独立的 EJS 页面布局,例如 themes/3-hexo/layout/article-graph.ejs,复用当前主题的 header、侧边导航和 footer。页面只放必要骨架:canvas、筛选区、无障碍列表、详情区域和加载失败提示。

页面脚本和样式放在主题资源目录,例如:

themes/3-hexo/source/js/article-graph.js
themes/3-hexo/source/css/_partial/article-graph.styl

图布局使用本地打包的 d3-force,而不是访问外部 CDN。Canvas 负责节点与连线绘制,HTML 只负责输入控件、提示和详情内容;这样在节点增多时比 SVG 更稳定,也更适合缩放浏览。

3. 入口与配置

_config.3-hexo.yml 增加专用配置,所有可调项集中在这里:

article_graph:
	on: true
	path: article-graph
	max_edges_per_node: 8
	min_edge_weight: 2
	show_hidden: false

再在主题的 menu.menus 增加“星图”入口,采用站内跳转。数据生成开关、边阈值和页面路径都不要写死在脚本中,后续调密度时只改配置即可。

实施顺序

  1. 先完成生成器,并在本地检查 data.json 的节点数、边数、重复 ID 和无效路径。
  2. 再实现静态页面骨架、主题样式和列表降级视图,先保证没有 JavaScript 也能打开文章。
  3. 接入 Canvas 与 d3-force,实现初始布局、缩放、拖拽、悬停和选中。
  4. 增加搜索、分类筛选、键盘焦点、移动端抽屉和减少动态效果支持。
  5. 给 2 到 3 个系列文章补 relations,观察自动关系和手工关系是否符合阅读路径。
  6. 用真实文章规模调整边阈值、节点半径和文字显示规则,再决定是否需要加入年份筛选。

验收标准

  • npm run build 成功,生成 /article-graph/index.html/article-graph/data.json
  • data.json 中只包含公开文章;每个节点 ID 和 path 唯一,所有边两端都能找到节点。
  • 桌面端能搜索、缩放、拖拽、选中并跳转到文章;筛选不会重新请求页面。
  • 移动端可缩放和查看节点详情,控制区与标题不重叠。
  • 键盘可完成搜索、查看结果、清除选中和打开文章;关闭 JavaScript 时仍有分类文章列表。
  • 在当前文章规模下连续操作没有明显掉帧,初始布局完成后不持续消耗 CPU。

需要后续确认的点

首版不建议自动分析正文语义。它需要额外的分词、向量模型或构建时间,产出的关系也不如标签和手工系列关系容易解释。

等基础星图稳定后,再评估两个增强项:按年份播放文章演进,以及用离线向量相似度补充“可能相关”的弱连接。后者必须明确标注为“语义相近”,不能和作者手工关联混在一起。

github