hexo-theme-stellar 2.0.0-rc.1 → 2.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.0-rc.2
4
+
5
+ > 发布日期:2026-09-07
6
+
7
+ 本节记录从公开候选 `2.0.0-rc.1` 到 `2.0.0-rc.2` 最终文件树的净变化。
8
+
9
+ ### 新功能
10
+
11
+ - Notebook 列表、设置页与错误页的专属配置统一收敛到 `profiles`;404 页新增默认关闭的评论区,可独立覆盖标题、线程、Provider 与参数。
12
+ - 可信页面注入补全 `head_begin/head_end/body_begin/body_end` 四个位置,站点主题配置与页面 Front Matter 使用同一合并顺序和原样输出语义。
13
+
14
+ ### 修复与工程变化
15
+
16
+ - 将 `hexo-front-matter` 声明为主题直接依赖,避免源码配置与 `stellar new note` 依赖 Hexo 宿主的传递依赖。
17
+ - 重构中英文 README,明确 npm、源码与 Blueprint 三种安装入口,并补充随 npm 包分发的 Stellar 图标、项目描述、关键词和作者元数据。
18
+
19
+ ### 升级注意(配置变更与破坏性改动)
20
+
21
+ - 从 RC1 升级时,将顶层 `notebook` 移到 `profiles.notebook`,将 `settings.about` 移到 `profiles.settings.about`,将 `error_page.image` 移到 `profiles.error.image`。
22
+ - `profiles.notebook_index` 更名为 `profiles.notebooks`,`profiles.note_index` 更名为 `profiles.notebook`;Note 内容页继续使用 `profiles.note`。
23
+ - `inject` 内容会作为可信 HTML 原样输出;只应配置维护者完全信任的内容,站点级内容在前、页面 Front Matter 在后。
24
+
25
+ Full Changelog: [2.0.0-rc.1...2.0.0-rc.2](https://github.com/xaoxuu/hexo-theme-stellar/compare/2.0.0-rc.1...2.0.0-rc.2)
26
+
3
27
  ## 2.0.0-rc.1
4
28
 
5
29
  > 发布日期:2026-09-07
package/README.md CHANGED
@@ -1,81 +1,87 @@
1
- # Stellar - 每个人的独立博客
1
+ <div align="center">
2
+ <img src="assets/icon-v2-x512.webp" width="160" alt="Stellar">
3
+ <h1>Stellar</h1>
4
+ <p>每个人的独立博客</p>
5
+ <p>基于 Hexo 的全能个人知识库,开箱即用,内置海量标签和动态数据组件。可用于个人博客、项目文档、知识库、专栏、笔记等。</p>
6
+ <p><a href="README.md">简体中文</a> · <a href="README_EN.md">English</a></p>
7
+ <p>
8
+ <a href="https://github.com/xaoxuu/hexo-theme-stellar/releases"><img src="https://img.shields.io/github/v/release/xaoxuu/hexo-theme-stellar" alt="release"></a>
9
+ <a href="https://www.npmjs.com/package/hexo-theme-stellar"><img src="https://img.shields.io/npm/v/hexo-theme-stellar" alt="npm"></a>
10
+ <a href="https://www.npmjs.com/package/hexo-theme-stellar"><img src="https://img.shields.io/npm/dm/hexo-theme-stellar" alt="npm downloads"></a>
11
+ <a href="https://github.com/xaoxuu/hexo-theme-stellar"><img src="https://img.shields.io/github/stars/xaoxuu/hexo-theme-stellar" alt="stars"></a>
12
+ <a href="LICENSE"><img src="https://img.shields.io/github/license/xaoxuu/hexo-theme-stellar" alt="license"></a>
13
+ </p>
14
+ <p>
15
+ <a href="https://xaoxuu.com/wiki/stellar/start/install/">创建第一个博客</a> ·
16
+ <a href="https://xaoxuu.com/wiki/stellar/support/examples/">站点示例</a> ·
17
+ <a href="https://xaoxuu.com/wiki/stellar/">完整文档</a>
18
+ </p>
19
+ </div>
2
20
 
3
- [简体中文](README.md) · [English](README_EN.md)
21
+ ## 在纷繁中建立秩序
4
22
 
5
- 一个博客,也是一套可以长期生长的内容系统。
23
+ > 真正的简约不止删繁就简,而是在纷繁中建立秩序。
6
24
 
7
- 从一篇文章开始。以后想加项目文档、专栏或笔记,仍然在同一个站点里写。
25
+ ### 完善的内容组织体系
8
26
 
9
- [创建第一个博客](https://xaoxuu.com/wiki/stellar/start/install/) · [蓝图与展示墙](https://xaoxuu.com/wiki/stellar/support/examples/) · [完整文档](https://xaoxuu.com/wiki/stellar/)
27
+ 从普通文章开始。项目需要稳定的文档目录时,用 Wiki;文章逐渐形成系列时,收进专栏;零散知识需要长期积累时,放进笔记本。它们共享同一套导航、搜索、侧栏与阅读体验,没有启用的内容系统不会增加额外负担。
10
28
 
11
- > Stellar v2 当前版本为 `2.0.0-rc.1`;是否已经公开发布,以 npm 页面和 GitHub Releases 为准。
29
+ ### 灵活的排版与表达方式
12
30
 
13
- [![npm](https://img.shields.io/npm/v/hexo-theme-stellar)](https://www.npmjs.com/package/hexo-theme-stellar)
14
- [![license](https://img.shields.io/github/license/xaoxuu/hexo-theme-stellar)](https://github.com/xaoxuu/hexo-theme-stellar/blob/main/LICENSE)
15
- [![stars](https://img.shields.io/github/stars/xaoxuu/hexo-theme-stellar)](https://github.com/xaoxuu/hexo-theme-stellar)
16
- [![npm downloads](https://img.shields.io/npm/dm/hexo-theme-stellar)](https://www.npmjs.com/package/hexo-theme-stellar)
17
- [![release](https://img.shields.io/github/v/release/xaoxuu/hexo-theme-stellar)](https://github.com/xaoxuu/hexo-theme-stellar/releases)
31
+ 从外观预设、配色和字体,到分栏、选项卡、折叠块与图库,站点的样子和内容的表达方式都可以按需组合。本地搜索可以直接定位到相关章节,响应式布局会在窄屏上把主要空间留给正文。
18
32
 
19
- ## 内容多起来,也不用推倒重来
33
+ ### 静态页面,也能常看常新
20
34
 
21
- 普通文章照常发布。项目需要一套稳定目录时,用 Wiki;几篇文章写成了系列,收进专栏;零散知识想长期补充,放进笔记本。
35
+ 手写内容沉淀观点,外部数据保持新鲜。远程内容、社区数据和外部服务可以按需加载,与静态正文保持解耦;经常变化的数据不必每次都随整站重新生成。
22
36
 
23
- 博客、Wiki、专栏和笔记本共享同一套导航、搜索、侧栏与阅读体验。只写博客时,其它系统不会跑出来打扰你。
37
+ ## 站点示例
24
38
 
25
- ## 静态博客,也能放一些活的内容
39
+ [Stellar Examples](https://github.com/xaoxuu/hexo-theme-stellar-examples/) 提供可以直接运行的示例站,适合先体验接近自己目标的站点,再逐步替换内容与配置。更多真实站点见[站点示例](https://xaoxuu.com/wiki/stellar/support/examples/)。
26
40
 
27
- 动态时间线、自动友链、远程 README、GitHub 仓库与贡献者、评分和投票都可以按需接入。博客仍然是静态文件,但经常变化的内容不必每次都跟着整站重新部署。
41
+ ## 开始使用
28
42
 
29
- 远程服务不会挡住正文先显示。接口暂时不可用时,文章仍然可以读。
30
-
31
- ## 写作、检索和阅读是一件事
32
-
33
- 提示框、折叠、标签页、图库、时间线、聊天和表格等内容组件可以互相组合。Markdown 表达不够时,不需要临时拼一套风格各异的插件。
34
-
35
- 本地搜索由主题生成索引,结果可以直接落到文章章节;桌面端目录和信息栏到了手机上会变成抽屉,把位置让给正文。
43
+ ### 环境要求
36
44
 
37
- [内容组件](https://xaoxuu.com/wiki/stellar/reference/tags/) · [站内搜索](https://xaoxuu.com/wiki/stellar/guides/search/) · [外观与排版](https://xaoxuu.com/wiki/stellar/guides/appearance/)
45
+ - Node.js 22 或更高版本
46
+ - Hexo 8 或更高版本
38
47
 
39
- > 如果你只想写博客,它不会逼你理解这些系统;内容真的多起来时,它们已经在那里了。
48
+ ### 从蓝图创建
40
49
 
41
- ## 选择一个起点
50
+ 准备好 Git 和 npm 后,一条命令即可启动交互式蓝图安装器:
42
51
 
43
- | 示例 | 适合什么样的站点 |
44
- | --- | --- |
45
- | 留白 · Light Blog | 安静写长文、随笔和生活记录 |
46
- | 星迹 · Blog | 使用经典侧栏整理文章、分类与专栏 |
47
- | 个人知识库 · Knowledge | 把博客、项目资料和长期主题放在一起 |
48
- | 项目文档 · Docs | 给一个项目维护清晰的文档目录 |
52
+ ```bash
53
+ sh -c "$(curl -fsSL https://github.com/xaoxuu/hexo-theme-stellar-examples/raw/main/install.sh)"
54
+ ```
49
55
 
50
- 示例源码在 [hexo-theme-stellar-examples](https://github.com/xaoxuu/hexo-theme-stellar-examples/)
56
+ 安装器会读取示例仓库的 [`blueprints.json`](https://github.com/xaoxuu/hexo-theme-stellar-examples/blob/main/blueprints.json),展示当前可用的蓝图,并依次确认蓝图、项目目录、依赖安装和创建计划。
51
57
 
52
- ## 快速开始
58
+ ### 从 npm 安装
53
59
 
54
- ### 环境要求
60
+ 在博客根目录安装 npm 当前提供的默认版本,并确认实际安装结果:
55
61
 
56
- ```yaml
57
- Hexo: '>= 8'
58
- hexo-cli: 4.3.0 ~ latest
59
- node: '>= 22'
60
- npm: '>= 10'
62
+ ```bash
63
+ npm install hexo-theme-stellar
64
+ npm ls hexo-theme-stellar
61
65
  ```
62
66
 
63
- ### 使用最新源码
67
+ ### 跟随最新源码
64
68
 
65
- 在博客根目录添加主题源码并安装依赖:
69
+ 将官方仓库的 `main` 分支添加为子模块,并安装主题自身的依赖:
66
70
 
67
71
  ```bash
68
72
  git submodule add -b main https://github.com/xaoxuu/hexo-theme-stellar.git themes/stellar
69
73
  npm install --prefix themes/stellar
70
74
  ```
71
75
 
72
- 然后在博客 `_config.yml` 中启用主题:
76
+ ### 启用并检查
77
+
78
+ 在博客 `_config.yml` 中启用主题:
73
79
 
74
80
  ```yaml
75
81
  theme: stellar
76
82
  ```
77
83
 
78
- 运行 Doctor 和生成:
84
+ 然后运行 Doctor 并生成站点:
79
85
 
80
86
  ```bash
81
87
  npx hexo stellar doctor
@@ -84,29 +90,15 @@ npx hexo generate
84
90
 
85
91
  完整步骤见[环境与安装](https://xaoxuu.com/wiki/stellar/start/install/)和[创建第一个站点](https://xaoxuu.com/wiki/stellar/start/first-site/)。
86
92
 
87
- ### 使用 npm 公开稳定版
88
-
89
- ```bash
90
- npm install hexo-theme-stellar
91
- ```
93
+ ## 文档与社区
92
94
 
93
- 这条命令不保证安装到 v2。复制 v2 配置前,请先运行 `npm ls hexo-theme-stellar` 核对实际版本。
94
-
95
- ## 文档
96
-
97
- - [从第一篇文章开始](https://xaoxuu.com/wiki/stellar/start/first-site/)
98
- - [把博客变成自己的样子](https://xaoxuu.com/wiki/stellar/start/configuration/)
99
- - [用 Wiki 写项目文档](https://xaoxuu.com/wiki/stellar/guides/wiki/)
100
- - [把文章整理成专栏](https://xaoxuu.com/wiki/stellar/guides/topic/)
101
- - [建立自己的笔记本](https://xaoxuu.com/wiki/stellar/guides/notebook/)
95
+ - [配置自己的站点](https://xaoxuu.com/wiki/stellar/start/configuration/)
96
+ - [使用内容组件](https://xaoxuu.com/wiki/stellar/reference/tags/)
97
+ - [管理 Wiki、专栏与笔记本](https://xaoxuu.com/wiki/stellar/reference/collection/)
102
98
  - [主题配置参考](https://xaoxuu.com/wiki/stellar/reference/theme/)
103
99
  - [从 v1 迁移到 v2](https://xaoxuu.com/wiki/stellar/migration/v1-to-v2/)
104
-
105
- ## 社区
106
-
107
100
  - [Issues](https://github.com/xaoxuu/hexo-theme-stellar/issues/):反馈可以复现的问题
108
101
  - [Discussions](https://github.com/xaoxuu/hexo-theme-stellar/discussions/):交流使用经验和想法
109
- - [蓝图与展示墙](https://xaoxuu.com/wiki/stellar/support/examples/):看看不同的人怎样使用 Stellar
110
102
  - [贡献指南](CONTRIBUTING.md):参与主题开发与文档维护
111
103
 
112
104
  ## 开源许可
package/README_EN.md CHANGED
@@ -1,79 +1,102 @@
1
- # Stellar — a blog that can grow with your work
1
+ <div align="center">
2
+ <img src="assets/icon-v2-x512.webp" width="160" alt="Stellar">
3
+ <h1>Stellar</h1>
4
+ <p>A blog that grows with your work</p>
5
+ <p>A complete, versatile, and feature-rich personal knowledge base designed to grow over time.</p>
6
+ <p><a href="README_EN.md">English</a> · <a href="README.md">简体中文</a></p>
7
+ <p>
8
+ <a href="https://github.com/xaoxuu/hexo-theme-stellar/releases"><img src="https://img.shields.io/github/v/release/xaoxuu/hexo-theme-stellar" alt="release"></a>
9
+ <a href="https://www.npmjs.com/package/hexo-theme-stellar"><img src="https://img.shields.io/npm/v/hexo-theme-stellar" alt="npm"></a>
10
+ <a href="https://www.npmjs.com/package/hexo-theme-stellar"><img src="https://img.shields.io/npm/dm/hexo-theme-stellar" alt="npm downloads"></a>
11
+ <a href="https://github.com/xaoxuu/hexo-theme-stellar"><img src="https://img.shields.io/github/stars/xaoxuu/hexo-theme-stellar" alt="stars"></a>
12
+ <a href="LICENSE"><img src="https://img.shields.io/github/license/xaoxuu/hexo-theme-stellar" alt="license"></a>
13
+ </p>
14
+ <p>
15
+ <a href="https://xaoxuu.com/wiki/stellar/start/install/">Create your first blog</a> ·
16
+ <a href="https://xaoxuu.com/wiki/stellar/support/examples/">Site examples</a> ·
17
+ <a href="https://xaoxuu.com/wiki/stellar/">Documentation</a>
18
+ </p>
19
+ </div>
2
20
 
3
- [English](README_EN.md) · [简体中文](README.md)
21
+ ## Bring order to everything you publish
4
22
 
5
- Stellar can begin as a quiet personal blog and grow into a home for your articles, project documentation, series, and notes.
23
+ > True simplicity is not just subtraction; it is creating order within complexity.
6
24
 
7
- [See the examples](https://xaoxuu.com/wiki/stellar/support/examples/) · [Getting started](https://xaoxuu.com/wiki/stellar/start/install/) · [Chinese documentation](https://xaoxuu.com/wiki/stellar/)
25
+ ### A complete system for organizing content
8
26
 
9
- > The current Stellar v2 version is `2.0.0-rc.1`. Check npm and GitHub Releases for its public availability.
27
+ Start with regular posts. Give a project its own Wiki when it needs stable documentation, collect related posts into a Topic, and keep evolving knowledge in a Notebook. They share the same navigation, search, sidebars, and reading experience, while unused content systems stay out of the way.
10
28
 
11
- [![npm](https://img.shields.io/npm/v/hexo-theme-stellar)](https://www.npmjs.com/package/hexo-theme-stellar)
12
- [![license](https://img.shields.io/github/license/xaoxuu/hexo-theme-stellar)](https://github.com/xaoxuu/hexo-theme-stellar/blob/main/LICENSE)
13
- [![stars](https://img.shields.io/github/stars/xaoxuu/hexo-theme-stellar)](https://github.com/xaoxuu/hexo-theme-stellar)
14
- [![npm downloads](https://img.shields.io/npm/dm/hexo-theme-stellar)](https://www.npmjs.com/package/hexo-theme-stellar)
29
+ ### Flexible layout and expression
15
30
 
16
- ## Four kinds of content, one site
31
+ From appearance presets, colors, and type to columns, tabs, folding sections, and galleries, you can shape both the site and the way its content is presented. Local search can take readers directly to a matching section, while responsive layouts leave the main space to the article on narrow screens.
17
32
 
18
- Publish regular blog posts as usual. Give a project its own Wiki when it needs stable navigation. Keep a series of posts together as a Topic, or collect smaller pieces of knowledge in a Notebook.
33
+ ### Static pages that stay fresh
19
34
 
20
- They share the same navigation, search, sidebars, and reading experience. If all you need is a blog, the other systems stay out of the way.
35
+ Your writing preserves ideas while external data stays current. Remote content, community data, and external services can load on demand without being coupled to the static article, so frequently changing data does not require a full-site rebuild.
21
36
 
22
- ## A static site with room for live data
37
+ ## Site examples
23
38
 
24
- Timelines, link status, remote README files, GitHub repositories and contributors, ratings, and polls can load when they are needed. Your site remains static, while frequently changing data does not require a full rebuild.
39
+ [Stellar Examples](https://github.com/xaoxuu/hexo-theme-stellar-examples/) provides runnable sites you can explore before replacing their content and configuration at your own pace. Visit [Site examples](https://xaoxuu.com/wiki/stellar/support/examples/) for more real sites.
25
40
 
26
- The article remains readable when a remote service is unavailable.
41
+ ## Get started
27
42
 
28
- ## Writing, finding, and reading belong together
43
+ ### Requirements
29
44
 
30
- Stellar includes content components for notes, folding sections, tabs, galleries, timelines, chat, tables, and more. Built-in local search can take a reader straight to a matching section. On smaller screens, navigation moves into drawers and leaves the page to the article.
45
+ - Node.js 22 or newer
46
+ - Hexo 8 or newer
31
47
 
32
- ## Choose a starting point
48
+ ### Create from a Blueprint
33
49
 
34
- | Example | A good fit for |
35
- | --- | --- |
36
- | Light Blog | Essays, personal notes, and distraction-free reading |
37
- | Blog | A classic sidebar blog with categories and series |
38
- | Knowledge | A personal site combining articles and long-lived knowledge |
39
- | Docs | Documentation for a single project |
50
+ With Git and npm available, launch the interactive Blueprint installer with one command:
40
51
 
41
- The source for these sites lives in the catalog-driven [hexo-theme-stellar-examples](https://github.com/xaoxuu/hexo-theme-stellar-examples/) repository.
52
+ ```bash
53
+ sh -c "$(curl -fsSL https://github.com/xaoxuu/hexo-theme-stellar-examples/raw/main/install.sh)"
54
+ ```
55
+
56
+ The installer reads the example repository's [`blueprints.json`](https://github.com/xaoxuu/hexo-theme-stellar-examples/blob/main/blueprints.json), presents the available Blueprints, then confirms the Blueprint, project directory, dependency installation, and creation plan.
57
+
58
+ ### Install from npm
59
+
60
+ Install the default version currently provided by npm, then confirm what was installed:
61
+
62
+ ```bash
63
+ npm install hexo-theme-stellar
64
+ npm ls hexo-theme-stellar
65
+ ```
42
66
 
43
- ## Use the latest source
67
+ ### Follow the latest source
44
68
 
45
- You need Node.js 22 or newer and Hexo 8 or newer. In the root of an existing Hexo site:
69
+ Add the official repository's `main` branch as a submodule and install the theme's own dependencies:
46
70
 
47
71
  ```bash
48
72
  git submodule add -b main https://github.com/xaoxuu/hexo-theme-stellar.git themes/stellar
49
73
  npm install --prefix themes/stellar
50
74
  ```
51
75
 
52
- Enable the theme in `_config.yml`:
76
+ ### Enable and check
77
+
78
+ Enable the theme in your site's `_config.yml`:
53
79
 
54
80
  ```yaml
55
81
  theme: stellar
56
82
  ```
57
83
 
58
- Then check and build the site:
84
+ Then run Doctor and generate the site:
59
85
 
60
86
  ```bash
61
87
  npx hexo stellar doctor
62
88
  npx hexo generate
63
89
  ```
64
90
 
65
- The full v2 documentation is currently maintained in Chinese. The example repository provides an English quick start and complete runnable sites that can be explored without reading the whole reference.
66
-
67
- ## Stable npm release
68
-
69
- ```bash
70
- npm install hexo-theme-stellar
71
- ```
72
-
73
- This command may still install v1. Run `npm ls hexo-theme-stellar` before using any v2 configuration.
91
+ See [Installation](https://xaoxuu.com/wiki/stellar/start/install/) and [Create your first site](https://xaoxuu.com/wiki/stellar/start/first-site/) for the complete workflow.
74
92
 
75
- ## Community
93
+ ## Documentation and community
76
94
 
95
+ - [Configure your site](https://xaoxuu.com/wiki/stellar/start/configuration/)
96
+ - [Use content components](https://xaoxuu.com/wiki/stellar/reference/tags/)
97
+ - [Manage Wikis, Topics, and Notebooks](https://xaoxuu.com/wiki/stellar/reference/collection/)
98
+ - [Theme configuration reference](https://xaoxuu.com/wiki/stellar/reference/theme/)
99
+ - [Migrate from v1 to v2](https://xaoxuu.com/wiki/stellar/migration/v1-to-v2/)
77
100
  - [Issues](https://github.com/xaoxuu/hexo-theme-stellar/issues/) for reproducible problems
78
101
  - [Discussions](https://github.com/xaoxuu/hexo-theme-stellar/discussions/) for ideas and usage questions
79
102
  - [Contributing](CONTRIBUTING.md) for code and documentation work
package/_config.yml CHANGED
@@ -133,7 +133,7 @@ footer:
133
133
  # 设置主内容区页脚导航分栏;空数组表示不显示分栏。
134
134
  sections: []
135
135
 
136
- # 配置各页面类型的路由、导航和 Region 默认值;省略字段时继承站点级 Region。
136
+ # 配置各页面与内容 Profile 的专属设置、路由、导航和 Region 默认值;省略字段时继承站点级 Region。
137
137
  profiles:
138
138
  # 配置主页默认布局。
139
139
  home:
@@ -265,8 +265,37 @@ profiles:
265
265
  - ghrepo
266
266
  - toc
267
267
 
268
+ # 配置单个 Notebook Collection 与列表页。
269
+ notebook:
270
+ # 设置 Notebook 列表页高亮的主菜单项;null 表示不高亮。
271
+ active_menu: null
272
+ # 配置 Notebook 列表页 Leftbar 覆盖项。
273
+ leftbar:
274
+ # 设置 Notebook 列表页 Leftbar 内容 Widget;数组会整体替换站点级列表。
275
+ widgets:
276
+ - tagtree
277
+ - recent
278
+ # 配置 Notebook 列表页 Rightbar 覆盖项。
279
+ rightbar:
280
+ # 设置 Notebook 列表页 Rightbar Widget;空数组表示显式隐藏。
281
+ widgets: []
282
+ # 配置 Notebook 条目列表。
283
+ listing:
284
+ # 设置每页条目数;null 表示继承站点分页配置,0 表示禁用分页。
285
+ per_page: null
286
+ # 配置 Notebook 条目排序。
287
+ sort:
288
+ # 设置排序字段;可选值为 date、updated 或 title。
289
+ field: updated
290
+ # 设置排序方向;可选值为 asc 或 desc。
291
+ direction: desc
292
+ # 设置没有 excerpt 和 description 时的自动摘要最大字符数;0 表示不自动生成摘要。
293
+ excerpt_length: 128
294
+ # 配置 Notebook 标签对应的图标;空对象表示不指定图标。
295
+ tag_icons: {}
296
+
268
297
  # 配置 Notebook 总索引页默认布局。
269
- notebook_index:
298
+ notebooks:
270
299
  # 设置 Notebook 总索引页的自动生成路径;null 表示不生成总索引页。
271
300
  path: /notebooks/
272
301
  # 设置 Notebook 总索引页高亮的主菜单项;null 表示不高亮。
@@ -281,21 +310,6 @@ profiles:
281
310
  # 设置 Notebook 总索引页 Rightbar Widget;空数组表示显式隐藏。
282
311
  widgets: []
283
312
 
284
- # 配置 Notebook 列表页默认布局。
285
- note_index:
286
- # 设置 Notebook 列表页高亮的主菜单项;null 表示不高亮。
287
- active_menu: null
288
- # 配置 Notebook 列表页 Leftbar 覆盖项。
289
- leftbar:
290
- # 设置 Notebook 列表页 Leftbar 内容 Widget;数组会整体替换站点级列表。
291
- widgets:
292
- - tagtree
293
- - recent
294
- # 配置 Notebook 列表页 Rightbar 覆盖项。
295
- rightbar:
296
- # 设置 Notebook 列表页 Rightbar Widget;空数组表示显式隐藏。
297
- widgets: []
298
-
299
313
  # 配置 Notebook 内容页默认布局。
300
314
  note:
301
315
  # 设置 Notebook 内容页高亮的主菜单项;null 表示不高亮。
@@ -347,6 +361,22 @@ profiles:
347
361
  settings:
348
362
  # 设置设置页的生成路径。
349
363
  path: /settings/
364
+ # 配置设置页的关于信息。
365
+ about:
366
+ # 设置关于信息条目;数组会整体替换。
367
+ items:
368
+ # 设置条目名称。
369
+ - key: 博客框架
370
+ # 设置条目内容,支持主题模板变量。
371
+ value: Hexo {hexo.version}
372
+ # 设置条目链接。
373
+ url: https://hexo.io/
374
+ # 设置条目名称。
375
+ - key: 主题版本
376
+ # 设置条目内容,支持主题模板变量。
377
+ value: Stellar {theme.version}
378
+ # 设置条目链接,支持主题模板变量。
379
+ url: '{theme.tree}'
350
380
  # 设置设置页高亮的主菜单项;null 表示不高亮。
351
381
  active_menu: null
352
382
  # 配置设置页 Leftbar 覆盖项。
@@ -363,6 +393,20 @@ profiles:
363
393
  error:
364
394
  # 设置错误页的生成路径。
365
395
  path: /404.html
396
+ # 设置错误页插图地址;null 表示不显示插图。
397
+ image: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/404/1c830bfcd517d.svg
398
+ # 配置错误页评论区。
399
+ comments:
400
+ # 控制是否显示错误页评论区。
401
+ enabled: false
402
+ # 设置错误页评论区标题;null 表示使用默认标题。
403
+ title: null
404
+ # 设置错误页评论线程标识;null 表示由页面上下文决定。
405
+ id: null
406
+ # 设置错误页专用评论 Provider;null 表示使用全局 Provider。
407
+ provider: null
408
+ # 设置错误页专用评论 Provider 参数;空对象表示不覆盖参数。
409
+ options: {}
366
410
  # 设置错误页高亮的主菜单项;null 表示不高亮。
367
411
  active_menu: post
368
412
  # 配置错误页 Leftbar 覆盖项。
@@ -427,42 +471,6 @@ article:
427
471
  # 控制文章页页脚是否显示标签。
428
472
  show_tags: true
429
473
 
430
- # 配置 Notebook 列表与内容页的展示行为。
431
- notebook:
432
- # 配置 Notebook 列表。
433
- listing:
434
- # 设置每页条目数;null 表示继承站点分页配置,0 表示禁用分页。
435
- per_page: null
436
- # 配置 Notebook 条目排序。
437
- sort:
438
- # 设置排序字段;可选值为 date、updated 或 title。
439
- field: updated
440
- # 设置排序方向;可选值为 asc 或 desc。
441
- direction: desc
442
- # 设置没有 excerpt 和 description 时的自动摘要最大字符数;0 表示不自动生成摘要。
443
- excerpt_length: 128
444
- # 配置 Notebook 标签对应的图标;空对象表示不指定图标。
445
- tag_icons: {}
446
-
447
- # 配置设置页内容。
448
- settings:
449
- # 配置设置页的关于信息。
450
- about:
451
- # 设置关于信息条目;数组会整体替换。
452
- items:
453
- # 设置条目名称。
454
- - key: 博客框架
455
- # 设置条目内容,支持主题模板变量。
456
- value: Hexo {hexo.version}
457
- # 设置条目链接。
458
- url: https://hexo.io/
459
- # 设置条目名称。
460
- - key: 主题版本
461
- # 设置条目内容,支持主题模板变量。
462
- value: Stellar {theme.version}
463
- # 设置条目链接,支持主题模板变量。
464
- url: '{theme.tree}'
465
-
466
474
  # -----------------------------------------------------------------------------
467
475
  # 外观
468
476
  # -----------------------------------------------------------------------------
@@ -893,11 +901,6 @@ fallbacks:
893
901
  # 设置封面占位图地址。
894
902
  cover: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/cover/76b86c0226ffd.svg
895
903
 
896
- # 配置错误页资源。
897
- error_page:
898
- # 设置错误页插图地址;null 表示不显示插图。
899
- image: https://gcore.jsdelivr.net/gh/cdn-x/placeholder@1.0.12/404/1c830bfcd517d.svg
900
-
901
904
  # -----------------------------------------------------------------------------
902
905
  # SEO
903
906
  # -----------------------------------------------------------------------------
@@ -928,7 +931,11 @@ structured_data:
928
931
 
929
932
  # 配置原样注入页面的可信内容。
930
933
  inject:
934
+ # 设置注入到 <head> 后的内容;空字符串表示不注入。
935
+ head_begin: ''
931
936
  # 设置注入到 </head> 前的内容;空字符串表示不注入。
932
937
  head_end: ''
938
+ # 设置注入到 <body> 后的内容;空字符串表示不注入。
939
+ body_begin: ''
933
940
  # 设置注入到 </body> 前的内容;空字符串表示不注入。
934
941
  body_end: ''
Binary file
package/layout/404.ejs CHANGED
@@ -1,8 +1,10 @@
1
1
  <%
2
2
  page.robots = 'none';
3
+ var errorProfile = stellar_config('profiles.error') || {}
4
+ var errorComments = comments_model(page, errorProfile.comments)
3
5
  %>
4
6
  <article class='md-text error-page'>
5
- <% var errorImage = stellar_config('errorPage.image') %>
7
+ <% var errorImage = errorProfile.image %>
6
8
  <% if (errorImage) { %>
7
9
  <h1><img id='error' src='<%- errorImage %>' alt='404'></h1>
8
10
  <% } %>
@@ -17,3 +19,4 @@ page.robots = 'none';
17
19
  <br><br>
18
20
  <a class='button' id='back' href="<%- config.root %>"><%- __('page.error.action') %></a>
19
21
  </article>
22
+ <%- partial('_partial/comments/layout', {comments: errorComments}) %>
@@ -11,8 +11,7 @@ var stellarSeo = {
11
11
  }
12
12
  var stellarResources = {
13
13
  preconnect: stellar_config('preconnect'),
14
- fallbacks: stellar_config('fallbacks'),
15
- errorPage: stellar_config('errorPage')
14
+ fallbacks: stellar_config('fallbacks')
16
15
  }
17
16
 
18
17
  function generate_title() {
@@ -159,8 +158,8 @@ function preconnect() {
159
158
  return el;
160
159
  }
161
160
 
162
- function custom_inject() {
163
- return stellar_inject('headEnd', pageRender ? pageRender.document.headEndInject : pageConfig.inject?.headEnd)
161
+ function custom_inject(kind) {
162
+ return stellar_inject(kind, pageRender ? pageRender.document[`${kind}Inject`] : pageConfig.inject?.[kind])
164
163
  }
165
164
 
166
165
  function generate_canonical() {
@@ -183,6 +182,7 @@ function generate_canonical() {
183
182
  %>
184
183
 
185
184
  <head>
185
+ <%- custom_inject('headBegin') %>
186
186
  <meta charset="utf-8">
187
187
  <meta name="hexo-theme" content="<%- stellar_info('tree') %>" theme-name="<%- stellar_info('name') %>" theme-version="<%- stellar_info('version') %>">
188
188
  <%- generate_canonical() %>
@@ -248,7 +248,7 @@ function generate_canonical() {
248
248
  <% } %>
249
249
 
250
250
  <%- json_ld(pageViewModel || undefined) %>
251
- <%- custom_inject() %>
251
+ <%- custom_inject('headEnd') %>
252
252
 
253
253
  <!-- icons.yml 驱动的 CSS 装饰图标 -->
254
254
  <%
@@ -15,6 +15,7 @@ var html = '<!DOCTYPE html>'
15
15
  html += `<html lang="${language}"${htmlAttributes}>`
16
16
  html += head
17
17
  html += `<body${pageAttributes}>`
18
+ html += bodyBegin || ''
18
19
  html += siteBackground || ''
19
20
  html += cover || ''
20
21
  html += `<div class="site-shell" id="start" data-regions="${presentRegions.join(' ')}">`
@@ -15,6 +15,10 @@ var runtimeHomeComments = stellar_config('profiles.home.comments')
15
15
  if (!runtimeViewModel && runtimeHomeComments && runtimeHomeComments !== false && is_home_first_page()) {
16
16
  runtimeCommentOverrides = typeof runtimeHomeComments === 'object' ? runtimeHomeComments : {enabled: true}
17
17
  }
18
+ var runtimeErrorComments = stellar_config('profiles.error.comments')
19
+ if (!runtimeViewModel && page.layout === '404' && runtimeErrorComments && runtimeErrorComments !== false) {
20
+ runtimeCommentOverrides = typeof runtimeErrorComments === 'object' ? runtimeErrorComments : {enabled: true}
21
+ }
18
22
  var runtimeComments = runtimeViewModel?.render?.article?.comments || comments_model(page, runtimeCommentOverrides)
19
23
  var runtimeProfile = runtimeViewModel?.collection?.profile
20
24
  || (page.notebookIndex?.mode === 'notes' ? 'notebook' : page.layout || '')
@@ -15,7 +15,7 @@ function isLeaf(tag) {
15
15
  }
16
16
 
17
17
  function getTagcon(tag) {
18
- var tagIcons = stellar_config('notebook.tagIcons') || {}
18
+ var tagIcons = stellar_config('profiles.notebook.tagIcons') || {}
19
19
  return tagIcons[tag.name] || tagIcons[tag.id] || tagIcons[tag.label] || tagIcons[tag.label.toLowerCase()] || 'quot:hashtag'
20
20
  }
21
21
 
package/layout/layout.ejs CHANGED
@@ -53,6 +53,7 @@ function shellDocument(options) {
53
53
  documentModel: options.documentModel,
54
54
  pageModel: options.pageModel,
55
55
  head: options.head,
56
+ bodyBegin: options.bodyBegin,
56
57
  siteBackground: options.siteBackground,
57
58
  cover: options.cover,
58
59
  regions: options.regions,
@@ -118,6 +119,7 @@ if (renderViewModel) {
118
119
  indent: render.layout.indent
119
120
  },
120
121
  head: partial('_partial/head', {viewModel: renderViewModel}),
122
+ bodyBegin: stellar_inject('bodyBegin', render.document.bodyBeginInject),
121
123
  siteBackground: siteBackground(render.layout.siteBackground),
122
124
  cover: coverSlot(renderViewModel),
123
125
  regions: {
@@ -148,9 +150,9 @@ if (renderViewModel) {
148
150
  if (is_home()) {
149
151
  profileKey = 'home'
150
152
  } else if (page.layout === 'notebooks') {
151
- profileKey = 'notebookIndex'
153
+ profileKey = 'notebooks'
152
154
  } else if (page.layout === 'notes') {
153
- profileKey = 'noteIndex'
155
+ profileKey = 'notebook'
154
156
  collectionRegions = notebookIndex?.layout
155
157
  } else if (is_category() || is_tag() || is_archive() || ['categories', 'tags', 'archives'].includes(page.layout)) {
156
158
  profileKey = 'blogIndex'
@@ -187,6 +189,7 @@ if (renderViewModel) {
187
189
  indent: indent
188
190
  },
189
191
  head: partial('_partial/head'),
192
+ bodyBegin: stellar_inject('bodyBegin', pageConfig.inject?.bodyBegin),
190
193
  siteBackground: siteBackground(appearance.backgrounds.page.image),
191
194
  cover: coverSlot(),
192
195
  regions: {
@@ -2,7 +2,7 @@
2
2
  page.robots = 'noindex,nofollow'
3
3
  var provider = stellar_config('comments.provider') || ''
4
4
  var providerSupported = ['artalk', 'waline', 'twikoo'].includes(String(provider).toLowerCase())
5
- var settingsConfig = stellar_config('settings') || {}
5
+ var settingsConfig = stellar_config('profiles.settings') || {}
6
6
  var aboutItems = settingsConfig.about?.items || []
7
7
  var cacheItems = [
8
8
  { id: 'search', label: __('settings.cache.search') },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hexo-theme-stellar",
3
- "version": "2.0.0-rc.1",
4
- "description": "Elegant and powerful theme for Hexo.",
3
+ "version": "2.0.0-rc.2",
4
+ "description": "An all-in-one Hexo theme for blogs, project documentation, knowledge bases, series, and notebooks.",
5
5
  "main": "package.json",
6
6
  "scripts": {
7
7
  "lint": "eslint .",
@@ -24,10 +24,19 @@
24
24
  },
25
25
  "keywords": [
26
26
  "hexo",
27
+ "hexo-theme",
27
28
  "theme",
28
- "stellar"
29
+ "stellar",
30
+ "blog",
31
+ "wiki",
32
+ "documentation",
33
+ "knowledge-base",
34
+ "notebook"
29
35
  ],
30
- "author": "[@xaoxuu](https://github.com/xaoxuu/)",
36
+ "author": {
37
+ "name": "xaoxuu",
38
+ "url": "https://github.com/xaoxuu/"
39
+ },
31
40
  "license": "MIT",
32
41
  "engines": {
33
42
  "node": ">=22"
@@ -38,6 +47,7 @@
38
47
  "dependencies": {
39
48
  "cheerio": "^1.1.0",
40
49
  "glob": "^10.4.0",
50
+ "hexo-front-matter": "^4.2.1",
41
51
  "hexo-renderer-ejs": "^2.0.0",
42
52
  "hexo-renderer-stylus": "^3.0.1",
43
53
  "hexo-pagination": "^3.0.0",
@@ -12,7 +12,7 @@ const { selectListingItems, stableSort } = require("../lib/collection-pipeline/s
12
12
  hexo.extend.generator.register("notebooks", function (locals) {
13
13
  const { notebookIndex } = hexo.stellar.data;
14
14
  const profiles = requireLayoutProfiles(hexo.stellar?.config);
15
- const profile = profiles.notebookIndex;
15
+ const profile = profiles.notebooks;
16
16
  if (!notebookIndex?.items || notebookIndex.items.length === 0) {
17
17
  return [];
18
18
  }
@@ -79,7 +79,7 @@ hexo.extend.generator.register("notebooks", function (locals) {
79
79
  layout: ["notes"],
80
80
  data: {
81
81
  layout: "notes",
82
- navigation: { menu: notebook.navigation.menu ?? profiles.noteIndex.activeMenu }
82
+ navigation: { menu: notebook.navigation.menu ?? profiles.notebook.activeMenu }
83
83
  }
84
84
  });
85
85
  for (const slice of slices) {
@@ -21,8 +21,8 @@ function shouldShowMobileBrand({ profileKey }) {
21
21
  "home",
22
22
  "blogIndex",
23
23
  "wikiIndex",
24
- "notebookIndex",
25
- "noteIndex"
24
+ "notebooks",
25
+ "notebook"
26
26
  ].includes(profileKey);
27
27
  }
28
28
 
@@ -8,14 +8,18 @@ const {
8
8
  } = require("./config-schema");
9
9
 
10
10
  function requireContentConfig(stellarConfig, source = "_config.stellar.yml") {
11
- if (isPlainObject(stellarConfig?.article) && isPlainObject(stellarConfig?.notebook)) {
12
- return { article: stellarConfig.article, notebook: stellarConfig.notebook };
11
+ const article = stellarConfig?.article;
12
+ const notebook = stellarConfig?.profiles?.notebook;
13
+ if (isPlainObject(article) && isPlainObject(notebook)) {
14
+ return { article, notebook };
13
15
  }
16
+ const path = isPlainObject(article) ? "stellarConfig.profiles.notebook" : "stellarConfig.article";
17
+ const actual = isPlainObject(article) ? notebook : article;
14
18
  throw new ConfigSchemaError([Object.freeze({
15
19
  code: "invalid_type",
16
20
  source,
17
- path: "stellarConfig.article",
18
- actualType: valueType(stellarConfig?.article),
21
+ path,
22
+ actualType: valueType(actual),
19
23
  expected: "normalized content defaults object",
20
24
  migration: null
21
25
  })]);
@@ -177,7 +177,7 @@ const CONTRIBUTIONS = defineContributions([
177
177
  tests: [RUNTIME_TEST, RUNTIME_CONSUMPTION_TEST],
178
178
  defaultsOwner: CONFIG_OWNER("comments.provider"),
179
179
  project(context) {
180
- if (typeof context.comments.service !== "string" || context.comments.service.length === 0) return null;
180
+ if (context.comments.enabled !== true || typeof context.comments.service !== "string" || context.comments.service.length === 0) return null;
181
181
  return configResult({
182
182
  provider: context.comments.service,
183
183
  options: context.comments.options || {},
@@ -499,7 +499,9 @@ function buildPostRenderModel(input, collection, item) {
499
499
  return {
500
500
  document: {
501
501
  language: normalizeLanguage(page.lang, page.language, frontMatter.lang, frontMatter.language, siteConfig.language),
502
+ headBeginInject: normalizeInject(frontMatter.inject?.headBegin),
502
503
  headEndInject: normalizeInject(frontMatter.inject?.headEnd),
504
+ bodyBeginInject: normalizeInject(frontMatter.inject?.bodyBegin),
503
505
  bodyEndInject: normalizeInject(frontMatter.inject?.bodyEnd),
504
506
  preferredTheme: appearance.colorScheme === "auto"
505
507
  ? "auto"
@@ -690,7 +692,9 @@ function buildWikiRenderModel(input, collection, item) {
690
692
  return {
691
693
  document: {
692
694
  language,
695
+ headBeginInject: normalizeInject(frontMatter.inject?.headBegin),
693
696
  headEndInject: normalizeInject(frontMatter.inject?.headEnd),
697
+ bodyBeginInject: normalizeInject(frontMatter.inject?.bodyBegin),
694
698
  bodyEndInject: normalizeInject(frontMatter.inject?.bodyEnd),
695
699
  preferredTheme: appearance.colorScheme === "auto" ? "auto" : String(appearance.colorScheme || "")
696
700
  },
@@ -1059,7 +1063,7 @@ function notebookBaseDir(collectionId, collectionConfig, stellarConfig) {
1059
1063
  if (typeof collectionConfig.route?.path === "string" && collectionConfig.route.path.length > 0) {
1060
1064
  return normalizeCollectionPath(collectionConfig.route.path);
1061
1065
  }
1062
- const root = profilePath(requireLayoutProfiles(stellarConfig).notebookIndex.path);
1066
+ const root = profilePath(requireLayoutProfiles(stellarConfig).notebooks.path);
1063
1067
  return normalizeCollectionPath([root, collectionId].filter(Boolean).join("/"));
1064
1068
  }
1065
1069
 
@@ -1098,11 +1102,11 @@ function buildNotebookCollectionModel(input, collectionId) {
1098
1102
  const defaultListing = notebookDefaults.listing;
1099
1103
  const baseDir = notebookBaseDir(collectionId, collectionConfig, input.stellarConfig);
1100
1104
  const identity = normalizeCollectionIdentity(collectionConfig);
1101
- const profileNavigation = toRenderNavigation(profiles.noteIndex);
1105
+ const profileNavigation = toRenderNavigation(profiles.notebook);
1102
1106
  const collectionNavigation = toContentNavigation(collectionConfig);
1103
1107
  const collectionRegions = pick(collectionConfig, CONTENT_MODEL_FIELDS.regionIds);
1104
1108
  const defaultBrand = collectionBrand(identity, baseDir, {
1105
- indexPath: profiles.notebookIndex.path,
1109
+ indexPath: profiles.notebooks.path,
1106
1110
  backLabel: "btn.all_notebook"
1107
1111
  });
1108
1112
  assertCollectionBrandConfig(collectionConfig, "collection", input.collectionSource || "<collection>");
@@ -1218,7 +1222,7 @@ function buildNotebookRenderModel(input, collection, item) {
1218
1222
  articleStyle: core.layout.articleStyle,
1219
1223
  indent: core.layout.indent,
1220
1224
  siteBackground: core.layout.siteBackground,
1221
- notebookIndexPath: profilePath(requireLayoutProfiles(input.stellarConfig).notebookIndex.path),
1225
+ notebookIndexPath: profilePath(requireLayoutProfiles(input.stellarConfig).notebooks.path),
1222
1226
  notebookPath: collection.route.baseDir,
1223
1227
  algoliaFilterPath: collection.route.baseDir,
1224
1228
  topbar: core.layout.topbar,
@@ -152,7 +152,6 @@ const inheritedRegionBrand = properties => ({
152
152
  });
153
153
 
154
154
  const CONFIG_RULES = Object.freeze([
155
- ["settings.about.items", { items: aboutItem }],
156
155
  ["footer.sections", { items: footerSection }],
157
156
  ["footer.content", { normalizer: "trusted_text" }],
158
157
 
@@ -182,10 +181,12 @@ const CONFIG_RULES = Object.freeze([
182
181
  ["profiles.*.rightbar.enabled", { type: ["boolean", "null"], defaultValue: null }],
183
182
  ["profiles.*.rightbar.widgets", { type: ["array", "null"], defaultValue: null, items: widgetItems }],
184
183
  ["profiles.*.listing_nav.tabs", { validator: "navigation_tabs", items: navigationTab }],
185
- ["profiles.home.comments.title", { type: ["string", "null"] }],
186
- ["profiles.home.comments.id", { type: ["string", "null"], normalizer: "nullable_trimmed_string", validator: "nullable_non_empty_string" }],
187
- ["profiles.home.comments.provider", { type: ["string", "null"], values: [null, "beaudar", "utterances", "giscus", "twikoo", "waline", "artalk"] }],
188
- ["profiles.home.comments.options", parameterBag],
184
+ ["profiles.error.image", { type: ["string", "null"], validator: "nullable_resource" }],
185
+ ["profiles.settings.about.items", { items: aboutItem }],
186
+ ["profiles.*.comments.title", { type: ["string", "null"] }],
187
+ ["profiles.*.comments.id", { type: ["string", "null"], normalizer: "nullable_trimmed_string", validator: "nullable_non_empty_string" }],
188
+ ["profiles.*.comments.provider", { type: ["string", "null"], values: [null, "beaudar", "utterances", "giscus", "twikoo", "waline", "artalk"] }],
189
+ ["profiles.*.comments.options", parameterBag],
189
190
 
190
191
  ["article.style", { values: ["tech", "story"] }],
191
192
  ["article.paragraph_indent", { values: ["auto", "always", "never"] }],
@@ -198,11 +199,11 @@ const CONFIG_RULES = Object.freeze([
198
199
  ["article.footer.license", { type: ["boolean", "string"], validator: "license_value" }],
199
200
  ["article.footer.share", { items: item("string", { defaultValue: "", values: SHARE_SERVICE_IDS }), normalizer: "trimmed_string_list" }],
200
201
  ["article.related_posts_limit", { minimum: 0, validator: "non_negative_integer" }],
201
- ["notebook.listing.excerpt_length", { minimum: 0, validator: "non_negative_integer" }],
202
- ["notebook.listing.per_page", { type: ["number", "null"], minimum: 0, validator: "nullable_non_negative_integer" }],
203
- ["notebook.listing.sort.field", { values: ["date", "updated", "title"] }],
204
- ["notebook.listing.sort.direction", { values: ["asc", "desc"] }],
205
- ["notebook.tag_icons", { ...stringRecord(), validator: "non_empty_record_keys" }],
202
+ ["profiles.notebook.listing.excerpt_length", { minimum: 0, validator: "non_negative_integer" }],
203
+ ["profiles.notebook.listing.per_page", { type: ["number", "null"], minimum: 0, validator: "nullable_non_negative_integer" }],
204
+ ["profiles.notebook.listing.sort.field", { values: ["date", "updated", "title"] }],
205
+ ["profiles.notebook.listing.sort.direction", { values: ["asc", "desc"] }],
206
+ ["profiles.notebook.tag_icons", { ...stringRecord(), validator: "non_empty_record_keys" }],
206
207
 
207
208
  ["appearance.preset", { values: ["card", "glass", "minimal", "flat"] }],
208
209
  ["appearance.color_scheme", { values: ["auto", "light", "dark"] }],
@@ -231,7 +232,6 @@ const CONFIG_RULES = Object.freeze([
231
232
  ["structured_data.same_as", { items: item("string", { defaultValue: "" }), normalizer: "trimmed_string_list" }],
232
233
  ["preconnect", { items: item("string", { defaultValue: "" }), normalizer: "origin_list" }],
233
234
  ["fallbacks.*", { validator: "resource" }],
234
- ["error_page.image", { type: ["string", "null"], validator: "nullable_resource" }],
235
235
 
236
236
  ["search.provider", { type: ["string", "null"], values: [null, "local", "algolia"] }],
237
237
  ["search.local.cache_ttl_seconds", { minimum: 0, validator: "non_negative_integer" }],
@@ -278,7 +278,9 @@ const CONFIG_RULES = Object.freeze([
278
278
  ["services.github_card.provider", { values: ["github_readme_stats"] }],
279
279
  ["services.github_card.github_readme_stats", { runtimeKey: "github_readme_stats" }],
280
280
  ["services.*.*.endpoint", { validator: "absolute_http_url" }],
281
+ ["inject.head_begin", { normalizer: "trusted_text" }],
281
282
  ["inject.head_end", { normalizer: "trusted_text" }],
283
+ ["inject.body_begin", { normalizer: "trusted_text" }],
282
284
  ["inject.body_end", { normalizer: "trusted_text" }]
283
285
  ]);
284
286
 
@@ -19,7 +19,8 @@ const LEGACY_THEME_ROOTS = Object.freeze({
19
19
  data_cache: null,
20
20
  plugins: "features",
21
21
  style: "appearance",
22
- default: "fallbacks | error_page",
22
+ notebook: "profiles.notebook",
23
+ default: "fallbacks | profiles.error.image",
23
24
  api_host: "services.github | services.github_card",
24
25
  system: null
25
26
  });
@@ -228,7 +228,9 @@ const FRONT_MATTER_TARGET_DEFINITIONS = [
228
228
  ["seo", "object", literal({}), { boundary: "sealed" }],
229
229
  ["seo.open_graph", "object", literal({}), { boundary: "parameter_bag" }],
230
230
  ["inject", "object", literal({}), { boundary: "sealed" }],
231
+ ["inject.head_begin", "string", literal("")],
231
232
  ["inject.head_end", "string", literal("")],
233
+ ["inject.body_begin", "string", literal("")],
232
234
  ["inject.body_end", "string", literal("")]
233
235
  ];
234
236
 
@@ -452,12 +452,12 @@ function collectionSchema(profile) {
452
452
  cardStyle: field(["string", "null"], { default: inherited("hexo.stellar.config.article.listing.cardLayout"), example: "hero", required: true })
453
453
  } : {}),
454
454
  excerptLength: field(["number", "null"], {
455
- default: profile === "notebook" ? inherited("hexo.stellar.config.notebook.listing.excerptLength") : literal(null),
455
+ default: profile === "notebook" ? inherited("hexo.stellar.config.profiles.notebook.listing.excerptLength") : literal(null),
456
456
  example: 128,
457
457
  required: true
458
458
  }),
459
459
  perPage: field(["number", "null"], {
460
- default: profile === "notebook" ? inherited("hexo.stellar.config.notebook.listing.perPage", "site.per_page") : literal(null),
460
+ default: profile === "notebook" ? inherited("hexo.stellar.config.profiles.notebook.listing.perPage", "site.per_page") : literal(null),
461
461
  example: 10,
462
462
  required: true
463
463
  }),
@@ -465,7 +465,7 @@ function collectionSchema(profile) {
465
465
  sort: object({
466
466
  field: field("string", { default: profile === "topic" ? literal("date") : literal("updated"), example: "updated", required: true }),
467
467
  direction: field("string", { default: literal("desc"), example: "desc", required: true })
468
- }, { default: derived("collection.listing.sort", "hexo.stellar.config.notebook.listing.sort"), example: { field: "updated", direction: "desc" }, required: true })
468
+ }, { default: derived("collection.listing.sort", "hexo.stellar.config.profiles.notebook.listing.sort"), example: { field: "updated", direction: "desc" }, required: true })
469
469
  })
470
470
  };
471
471
 
@@ -618,10 +618,12 @@ function pageViewModelSchema(profile) {
618
618
  properties.render = object({
619
619
  document: object({
620
620
  language: field("string", { default: derived("page.lang", "page.language", "site.language"), example: "zh-CN", required: true }),
621
+ headBeginInject: field("string", { default: literal(""), example: "", required: true }),
621
622
  headEndInject: field("string", { default: literal(""), example: "", required: true }),
623
+ bodyBeginInject: field("string", { default: literal(""), example: "", required: true }),
622
624
  bodyEndInject: field("string", { default: literal(""), example: "", required: true }),
623
625
  preferredTheme: field("string", { default: derived("hexo.stellar.config.appearance.colorScheme"), example: "auto", required: true })
624
- }, { required: true, example: { language: "zh-CN", headEndInject: "", bodyEndInject: "", preferredTheme: "auto" } }),
626
+ }, { required: true, example: { language: "zh-CN", headBeginInject: "", headEndInject: "", bodyBeginInject: "", bodyEndInject: "", preferredTheme: "auto" } }),
625
627
  layout: object({
626
628
  pageType: field("string", { default: literal("content"), example: "content", required: true }),
627
629
  articleStyle: field(["string", "null"], { default: inherited("item.presentation.article.style"), example: "tech", required: true }),
@@ -692,7 +694,7 @@ function pageViewModelSchema(profile) {
692
694
  }, { required: true, example: { href: "blog/hello", title: "Hello Stellar", layout: "post", date: null, cover: "", caption: "", excerpt: "", categories: [], categoryStyle: "", tags: [], authorId: "", priority: 0, listed: true, cardStyle: "classic" } })
693
695
  }, {
694
696
  default: computed(`由 ${profile === "topic" ? "Topic" : "Post"} PageViewModel 构建器生成`),
695
- example: { document: { language: "zh-CN", headEndInject: "", bodyEndInject: "" }, layout: { pageType: "content" }, seo: { title: "Hello Stellar - Stellar" } },
697
+ example: { document: { language: "zh-CN", headBeginInject: "", headEndInject: "", bodyBeginInject: "", bodyEndInject: "" }, layout: { pageType: "content" }, seo: { title: "Hello Stellar - Stellar" } },
696
698
  required: true
697
699
  });
698
700
  }
@@ -761,10 +763,12 @@ function pageViewModelSchema(profile) {
761
763
  properties.render = object({
762
764
  document: object({
763
765
  language: field("string", { default: derived("page.lang", "page.language", "site.language"), example: "zh-CN", required: true }),
766
+ headBeginInject: field("string", { default: literal(""), example: "", required: true }),
764
767
  headEndInject: field("string", { default: literal(""), example: "", required: true }),
768
+ bodyBeginInject: field("string", { default: literal(""), example: "", required: true }),
765
769
  bodyEndInject: field("string", { default: literal(""), example: "", required: true }),
766
770
  preferredTheme: field("string", { default: derived("hexo.stellar.config.appearance.colorScheme"), example: "auto", required: true })
767
- }, { required: true, example: { language: "zh-CN", headEndInject: "", bodyEndInject: "", preferredTheme: "auto" } }),
771
+ }, { required: true, example: { language: "zh-CN", headBeginInject: "", headEndInject: "", bodyBeginInject: "", bodyEndInject: "", preferredTheme: "auto" } }),
768
772
  layout: object({
769
773
  pageType: field("string", { default: literal("content"), example: "content", required: true }),
770
774
  articleStyle: field(["string", "null"], { default: inherited("item.presentation.article.style"), example: "tech", required: true }),
@@ -873,16 +877,18 @@ function pageViewModelSchema(profile) {
873
877
  properties.render = object({
874
878
  document: object({
875
879
  language: field("string", { default: derived("page.lang", "page.language", "site.language"), example: "zh-CN", required: true }),
880
+ headBeginInject: field("string", { default: literal(""), example: "", required: true }),
876
881
  headEndInject: field("string", { default: literal(""), example: "", required: true }),
882
+ bodyBeginInject: field("string", { default: literal(""), example: "", required: true }),
877
883
  bodyEndInject: field("string", { default: literal(""), example: "", required: true }),
878
884
  preferredTheme: field("string", { default: derived("appearance.colorScheme"), example: "auto", required: true })
879
- }, { required: true, example: { language: "zh-CN", headEndInject: "", bodyEndInject: "", preferredTheme: "auto" } }),
885
+ }, { required: true, example: { language: "zh-CN", headBeginInject: "", headEndInject: "", bodyBeginInject: "", bodyEndInject: "", preferredTheme: "auto" } }),
880
886
  layout: object({
881
887
  pageType: field("string", { default: literal("content"), example: "content", required: true }),
882
888
  articleStyle: field(["string", "null"], { default: inherited("item.presentation.article.style"), example: "tech", required: true }),
883
889
  indent: field("boolean", { default: computed("由 article.paragraphIndent 与 style 解析"), example: false, required: true }),
884
890
  siteBackground: field("boolean", { default: derived("appearance.backgrounds.page.image"), example: false, required: true }),
885
- notebookIndexPath: field("string", { default: derived("hexo.stellar.config.profiles.notebookIndex.path"), example: "notebooks", required: true }),
891
+ notebookIndexPath: field("string", { default: derived("hexo.stellar.config.profiles.notebooks.path"), example: "notebooks", required: true }),
886
892
  notebookPath: field("string", { default: inherited("collection.route.baseDir"), example: "notes/dev", required: true }),
887
893
  algoliaFilterPath: field("string", { default: inherited("collection.route.baseDir"), example: "notes/dev", required: true }),
888
894
  ...renderRegionsSchema(factory),