hexo-theme-stellar 1.33.0 → 1.34.0

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.
Files changed (83) hide show
  1. package/CLAUDE.md +217 -0
  2. package/README.md +2 -0
  3. package/_config.yml +35 -26
  4. package/docs/audits/2026-08-08-stellar-analysis.md +267 -0
  5. package/docs/release-process.md +59 -0
  6. package/layout/_partial/comments/artalk/script.ejs +56 -41
  7. package/layout/_partial/comments/beaudar/script.ejs +35 -20
  8. package/layout/_partial/comments/giscus/script.ejs +34 -19
  9. package/layout/_partial/comments/twikoo/script.ejs +29 -14
  10. package/layout/_partial/comments/utterances/script.ejs +35 -20
  11. package/layout/_partial/comments/waline/script.ejs +40 -28
  12. package/layout/_partial/cover/wiki_cover.ejs +1 -1
  13. package/layout/_partial/head.ejs +53 -4
  14. package/layout/_partial/scripts/defines.ejs +21 -3
  15. package/layout/_partial/scripts/lazyload.ejs +2 -2
  16. package/layout/_partial/scripts/services.ejs +101 -41
  17. package/layout/_partial/scripts/utils.ejs +144 -5
  18. package/layout/_partial/sidebar/index_leftbar.ejs +3 -0
  19. package/layout/_partial/sidebar/search.ejs +3 -0
  20. package/layout/_partial/widgets/ghuser.ejs +18 -4
  21. package/layout/_partial/widgets/linklist.ejs +1 -1
  22. package/layout/_partial/widgets/timeline.ejs +36 -33
  23. package/layout/_plugins/copycode.ejs +9 -5
  24. package/layout/_plugins/mathjax.ejs +47 -18
  25. package/layout/_plugins/mermaid.ejs +28 -24
  26. package/layout/_plugins/pjax.ejs +8 -0
  27. package/layout/_plugins/scrollreveal.ejs +14 -13
  28. package/layout/_plugins/swiper.ejs +2 -2
  29. package/layout/index.ejs +4 -2
  30. package/layout/index_topic.ejs +4 -2
  31. package/layout/index_wiki.ejs +6 -4
  32. package/layout/layout.ejs +3 -2
  33. package/package.json +2 -1
  34. package/scripts/commands/new-note.js +49 -0
  35. package/scripts/filters/lib/img_onerror.js +0 -2
  36. package/scripts/helpers/json_ld.js +11 -8
  37. package/scripts/helpers/pretty_url.js +5 -0
  38. package/scripts/tags/lib/chat.js +16 -9
  39. package/scripts/tags/lib/frame.js +2 -1
  40. package/scripts/tags/lib/image.js +6 -5
  41. package/scripts/tags/lib/link.js +6 -4
  42. package/scripts/tags/lib/timeline.js +101 -79
  43. package/source/css/_common/canonical.styl +11 -6
  44. package/source/css/_common/highlight.styl +4 -1
  45. package/source/css/_common/html.styl +4 -0
  46. package/source/css/_common/loading.styl +5 -1
  47. package/source/css/_components/md.styl +3 -0
  48. package/source/css/_components/pages/article-story.styl +2 -0
  49. package/source/css/_components/partial/article-footer.styl +1 -0
  50. package/source/css/_components/partial/navbar.styl +4 -2
  51. package/source/css/_components/sidebar/menu.styl +1 -0
  52. package/source/css/_components/sidebar/search.styl +4 -0
  53. package/source/css/_components/sidebar/sidebar.styl +12 -5
  54. package/source/css/_components/tag-plugins/chat.styl +6 -8
  55. package/source/css/_components/tag-plugins/folding.styl +1 -0
  56. package/source/css/_components/tag-plugins/gallery.styl +2 -0
  57. package/source/css/_components/tag-plugins/grid.styl +1 -0
  58. package/source/css/_components/tag-plugins/image.styl +3 -0
  59. package/source/css/_components/tag-plugins/link.styl +1 -0
  60. package/source/css/_components/tag-plugins/navbar.styl +15 -13
  61. package/source/css/_components/tag-plugins/note.styl +1 -0
  62. package/source/css/_components/tag-plugins/okr.styl +1 -0
  63. package/source/css/_components/tag-plugins/sites.styl +2 -0
  64. package/source/css/_components/tag-plugins/timeline.styl +9 -0
  65. package/source/css/_components/tag-plugins/vote.styl +2 -0
  66. package/source/css/_components/widgets/components.styl +1 -0
  67. package/source/css/_components/widgets/ghuser.styl +19 -3
  68. package/source/css/_components/widgets/list.styl +1 -0
  69. package/source/css/_components/widgets/toc.styl +3 -0
  70. package/source/css/_components/widgets/widgets.styl +4 -0
  71. package/source/css/_custom.styl +6 -3
  72. package/source/css/_defines/func.styl +24 -8
  73. package/source/css/_defines/theme_base.styl +1 -1
  74. package/source/css/_plugins/comments/artalk.styl +6 -2
  75. package/source/css/_plugins/comments/twikoo.styl +2 -0
  76. package/source/css/_plugins/index.styl +2 -0
  77. package/source/css/_plugins/pjax.styl +61 -0
  78. package/source/js/main.js +80 -26
  79. package/source/js/plugins/copycode.js +34 -27
  80. package/source/js/plugins/pjax.js +646 -0
  81. package/source/js/search/local-search.js +5 -2
  82. package/source/js/services/memos.js +42 -4
  83. package/source/js/services/rss.js +200 -0
@@ -0,0 +1,59 @@
1
+ # 发版流程
2
+
3
+ > 创建日期: 2026-08-08
4
+
5
+ ## 概述
6
+
7
+ Stellar 发版分两步:脚本完成版本号更新和推送 → CI 自动化完成 npm 发布和 tag 创建。
8
+
9
+ ```
10
+ npm-publish.sh → push main + npm → CI (.github/workflows/npm-publish.yml) → npm publish + git tag
11
+ ```
12
+
13
+ ## 前置条件
14
+
15
+ - 当前在 `main` 分支,且已同步最新代码
16
+ - 工作区无无关改动(仅允许 `_config.yml` 和 `package.json` 有未提交变更)
17
+
18
+ ## 脚本功能
19
+
20
+ `npm-publish.sh` 负责:
21
+
22
+ 1. 更新 `_config.yml` → `stellar.version`
23
+ 2. 更新 `package.json` → `version`
24
+ 3. 更新 `_config.yml` 中 CDN URL 的版本号(jsdelivr)
25
+ 4. `git add _config.yml package.json` + commit(`release: {version}`)
26
+ 5. push main 分支 → rebase npm 分支 → push npm 分支
27
+
28
+ ## CI 自动化
29
+
30
+ push npm 分支后,[npm-publish.yml](../.github/workflows/npm-publish.yml) 自动触发:
31
+
32
+ - 检测 commit message 匹配 `^release:\ (.*)`,提取版本号
33
+ - 发布到 [npm](https://www.npmjs.com/package/hexo-theme-stellar)
34
+ - 创建 Git tag(如 `1.33.2`)
35
+
36
+ ## 使用方式
37
+
38
+ ```bash
39
+ # 正常发版(CI 自动处理 npm publish 和 tag)
40
+ bash npm-publish.sh 1.33.2
41
+
42
+ # 预览(仅显示变更,不提交/推送,执行后自动回滚)
43
+ bash npm-publish.sh 1.33.2 --dry-run
44
+ ```
45
+
46
+ ## 发版 Checklist
47
+
48
+ - [ ] 所有功能变更已合并到 main,且通过 `gulp minify` 验证
49
+ - [ ] 通过 `bash npm-publish.sh <version> --dry-run` 检查版本号变更
50
+ - [ ] 执行 `bash npm-publish.sh <version>` 推送
51
+ - [ ] 等待 CI 完成(GitHub Actions → npm-publish workflow)
52
+ - [ ] 验证 [GitHub Releases](https://github.com/xaoxuu/hexo-theme-stellar/releases) 有对应 tag
53
+ - [ ] 验证 [npm 页面](https://www.npmjs.com/package/hexo-theme-stellar) 版本已更新
54
+
55
+ ## 版本号规范
56
+
57
+ - 格式: `x.y.z` 或 `x.y.z-rc.n`
58
+ - Tag 格式: 不带 `v` 前缀(如 `1.33.2`,非 `v1.33.2`)
59
+ - 仅 rc 测试版本和正式版本发布到 npm(CI 自动处理)
@@ -1,42 +1,57 @@
1
- <script type="module">
2
- const el = document.getElementById('artalk_container');
3
- util.viewportLazyload(el, load_artalk, <%= theme.comments.lazyload ?? false %>);
4
-
5
- function load_artalk() {
6
- if (!el) return;
7
- utils.css('<%- theme.comments.artalk.css %>');
8
- utils.js('<%- theme.comments.artalk.js %>', {defer: true}).then(function () {
9
- // 监听 Artalk 事件
10
- // FIXME: Artalk开启了懒加载会导致评论输入预览框里的图也变成data-src,导致看不到图
11
- // Artalk.use((ctx) => {
12
- // ctx.on('list-loaded', () => window.lazyLoadInstance?.update())
13
- // })
14
- const path = el.getAttribute('comment_id') ?? decodeURI(window.location.pathname);
15
- const artalk = Artalk.init({
16
- el: '#artalk_container',
17
- pageKey: path,
18
- pageTitle: '<%= page.title %>',
19
- server: '<%= theme.comments.artalk.server %>',
20
- site: '<%= theme.comments.artalk.site %>',
21
- darkMode: '<%= theme.comments.artalk.darkMode %>',
22
- <% if (theme.comments.artalk.imageUploader?.api) { %>
23
- imgUploader: function(file) {
24
- let headers = new Headers();
25
- headers.set('Accept', 'application/json');
26
- <% if (theme.comments.artalk.imageUploader?.token) { %>
27
- headers.set('Authorization', '<%- theme.comments.artalk.imageUploader?.token %>')
28
- <% } %>
29
- let formData = new FormData();
30
- formData.append('file', file);
31
- return fetch('<%- theme.comments.artalk.imageUploader?.api %>',{
32
- method: 'POST',
33
- body: formData,
34
- headers: headers
35
- }).then((resp) => resp.json())
36
- .then((resp) => resp.<%- theme.comments.artalk.imageUploader?.resp %>)
37
- },
38
- <% } %>
39
- });
40
- });
41
- }
1
+ <script>
2
+ (function() {
3
+ const lazyload = <%= theme.comments.lazyload ?? false %>;
4
+
5
+ function initArtalk() {
6
+ const el = document.getElementById('artalk_container');
7
+ if (!el) return;
8
+
9
+ function load_artalk() {
10
+ utils.css('<%- theme.comments.artalk.css %>');
11
+ utils.js('<%- theme.comments.artalk.js %>', {defer: true}).then(function () {
12
+ // 监听 Artalk 事件
13
+ // FIXME: Artalk开启了懒加载会导致评论输入预览框里的图也变成data-src,导致看不到图
14
+ // Artalk.use((ctx) => {
15
+ // ctx.on('list-loaded', () => window.lazyLoadInstance?.update())
16
+ // })
17
+ const path = el.getAttribute('comment_id') ?? decodeURI(window.location.pathname);
18
+ const artalk = Artalk.init({
19
+ el: '#artalk_container',
20
+ pageKey: path,
21
+ pageTitle: '<%= page.title %>',
22
+ server: '<%= theme.comments.artalk.server %>',
23
+ site: '<%= theme.comments.artalk.site %>',
24
+ darkMode: '<%= theme.comments.artalk.darkMode %>',
25
+ <% if (theme.comments.artalk.imageUploader?.api) { %>
26
+ imgUploader: function(file) {
27
+ let headers = new Headers();
28
+ headers.set('Accept', 'application/json');
29
+ <% if (theme.comments.artalk.imageUploader?.token) { %>
30
+ headers.set('Authorization', '<%- theme.comments.artalk.imageUploader?.token %>')
31
+ <% } %>
32
+ let formData = new FormData();
33
+ formData.append('file', file);
34
+ return fetch('<%- theme.comments.artalk.imageUploader?.api %>',{
35
+ method: 'POST',
36
+ body: formData,
37
+ headers: headers
38
+ }).then((resp) => resp.json())
39
+ .then((resp) => resp.<%- theme.comments.artalk.imageUploader?.resp %>)
40
+ },
41
+ <% } %>
42
+ });
43
+ });
44
+ }
45
+
46
+ util.viewportLazyload(el, load_artalk, lazyload);
47
+ }
48
+
49
+ // Store in global stellar namespace for PJAX reinitialization
50
+ window.stellar = window.stellar || {};
51
+ window.stellar.initComments = window.stellar.initComments || {};
52
+ window.stellar.initComments.artalk = initArtalk;
53
+
54
+ // Initialize on page load
55
+ initArtalk();
56
+ })();
42
57
  </script>
@@ -1,23 +1,38 @@
1
- <script type="module">
2
- const el = document.querySelector('#comments #beaudar');
3
- util.viewportLazyload(el, load_beaudar, <%= theme.comments.lazyload ?? false %>);
4
-
5
- function load_beaudar() {
6
- if (!el) return;
7
- try {
8
- el.innerHTML = '';
9
- } catch (error) {
10
- console.error(error);
11
- }
12
- const script = document.createElement('script');
13
- script.src = 'https://beaudar.lipk.org/client.js';
14
- script.async = true;
15
- for (const key of Object.keys(el.attributes)) {
16
- const attr = el.attributes[key];
17
- if (['class', 'id'].includes(attr.name) === false) {
18
- script.setAttribute(attr.name, attr.value);
1
+ <script>
2
+ (function() {
3
+ const lazyload = <%= theme.comments.lazyload ?? false %>;
4
+
5
+ function initBeaudar() {
6
+ const el = document.querySelector('#comments #beaudar');
7
+ if (!el) return;
8
+
9
+ function load_beaudar() {
10
+ try {
11
+ el.innerHTML = '';
12
+ } catch (error) {
13
+ console.error(error);
14
+ }
15
+ const script = document.createElement('script');
16
+ script.src = 'https://beaudar.lipk.org/client.js';
17
+ script.async = true;
18
+ for (const key of Object.keys(el.attributes)) {
19
+ const attr = el.attributes[key];
20
+ if (['class', 'id'].includes(attr.name) === false) {
21
+ script.setAttribute(attr.name, attr.value);
22
+ }
23
+ }
24
+ el.appendChild(script);
19
25
  }
26
+
27
+ util.viewportLazyload(el, load_beaudar, lazyload);
20
28
  }
21
- el.appendChild(script);
22
- }
29
+
30
+ // Store in global stellar namespace for PJAX reinitialization
31
+ window.stellar = window.stellar || {};
32
+ window.stellar.initComments = window.stellar.initComments || {};
33
+ window.stellar.initComments.beaudar = initBeaudar;
34
+
35
+ // Initialize on page load
36
+ initBeaudar();
37
+ })();
23
38
  </script>
@@ -1,22 +1,37 @@
1
- <script type="module">
2
- const el = document.querySelector('#comments #giscus');
3
- util.viewportLazyload(el, load_discus, <%= theme.comments.lazyload ?? false %>);
4
-
5
- function load_discus() {
6
- if (!el) return;
7
- try {
8
- el.innerHTML = '';
9
- } catch (error) {
10
- console.error(error);
11
- }
12
- const script = document.createElement('script');
13
- script.async = true;
14
- for (const key of Object.keys(el.attributes)) {
15
- const attr = el.attributes[key];
16
- if (['class', 'id'].includes(attr.name) === false) {
17
- script.setAttribute(attr.name, attr.value);
1
+ <script>
2
+ (function() {
3
+ const lazyload = <%= theme.comments.lazyload ?? false %>;
4
+
5
+ function initGiscus() {
6
+ const el = document.querySelector('#comments #giscus');
7
+ if (!el) return;
8
+
9
+ function load_giscus() {
10
+ try {
11
+ el.innerHTML = '';
12
+ } catch (error) {
13
+ console.error(error);
14
+ }
15
+ const script = document.createElement('script');
16
+ script.async = true;
17
+ for (const key of Object.keys(el.attributes)) {
18
+ const attr = el.attributes[key];
19
+ if (['class', 'id'].includes(attr.name) === false) {
20
+ script.setAttribute(attr.name, attr.value);
21
+ }
18
22
  }
23
+ el.appendChild(script);
19
24
  }
20
- el.appendChild(script);
21
- }
25
+
26
+ util.viewportLazyload(el, load_giscus, lazyload);
27
+ }
28
+
29
+ // Store in global stellar namespace for PJAX reinitialization
30
+ window.stellar = window.stellar || {};
31
+ window.stellar.initComments = window.stellar.initComments || {};
32
+ window.stellar.initComments.giscus = initGiscus;
33
+
34
+ // Initialize on page load
35
+ initGiscus();
36
+ })();
22
37
  </script>
@@ -1,15 +1,30 @@
1
- <script type="module">
2
- const el = document.getElementById('twikoo_container');
3
- util.viewportLazyload(el, load_twikoo, <%= theme.comments.lazyload ?? false %>);
4
-
5
- function load_twikoo() {
6
- if (!el) return;
7
- utils.js('<%- theme.comments.twikoo.js %>', {defer: true}).then(function () {
8
- const path = el.getAttribute('comment_id') ?? decodeURI(window.location.pathname);
9
- twikoo.init(Object.assign(<%- JSON.stringify(theme.comments.twikoo) %>, {
10
- el: '#twikoo_container',
11
- path: path,
12
- }));
13
- });
14
- }
1
+ <script>
2
+ (function() {
3
+ const lazyload = <%= theme.comments.lazyload ?? false %>;
4
+
5
+ function initTwikoo() {
6
+ const el = document.getElementById('twikoo_container');
7
+ if (!el) return;
8
+
9
+ function load_twikoo() {
10
+ utils.js('<%- theme.comments.twikoo.js %>', {defer: true}).then(function () {
11
+ const path = el.getAttribute('comment_id') ?? decodeURI(window.location.pathname);
12
+ twikoo.init(Object.assign(<%- JSON.stringify(theme.comments.twikoo) %>, {
13
+ el: '#twikoo_container',
14
+ path: path,
15
+ }));
16
+ });
17
+ }
18
+
19
+ util.viewportLazyload(el, load_twikoo, lazyload);
20
+ }
21
+
22
+ // Store in global stellar namespace for PJAX reinitialization
23
+ window.stellar = window.stellar || {};
24
+ window.stellar.initComments = window.stellar.initComments || {};
25
+ window.stellar.initComments.twikoo = initTwikoo;
26
+
27
+ // Initialize on page load
28
+ initTwikoo();
29
+ })();
15
30
  </script>
@@ -1,23 +1,38 @@
1
- <script type="module">
2
- const el = document.querySelector("#comments #utterances");
3
- util.viewportLazyload(el, load_utterances, <%= theme.comments.lazyload ?? false %>);
4
-
5
- function load_utterances() {
6
- if (!el) return;
7
- try {
8
- el.innerHTML = '';
9
- } catch (error) {
10
- console.error(error);
11
- }
12
- const script = document.createElement('script');
13
- script.src = 'https://utteranc.es/client.js';
14
- script.async = true;
15
- for (const key of Object.keys(el.attributes)) {
16
- const attr = el.attributes[key];
17
- if (['class', 'id'].includes(attr.name) === false) {
18
- script.setAttribute(attr.name, attr.value);
1
+ <script>
2
+ (function() {
3
+ const lazyload = <%= theme.comments.lazyload ?? false %>;
4
+
5
+ function initUtterances() {
6
+ const el = document.querySelector("#comments #utterances");
7
+ if (!el) return;
8
+
9
+ function load_utterances() {
10
+ try {
11
+ el.innerHTML = '';
12
+ } catch (error) {
13
+ console.error(error);
14
+ }
15
+ const script = document.createElement('script');
16
+ script.src = 'https://utteranc.es/client.js';
17
+ script.async = true;
18
+ for (const key of Object.keys(el.attributes)) {
19
+ const attr = el.attributes[key];
20
+ if (['class', 'id'].includes(attr.name) === false) {
21
+ script.setAttribute(attr.name, attr.value);
22
+ }
23
+ }
24
+ el.appendChild(script);
19
25
  }
26
+
27
+ util.viewportLazyload(el, load_utterances, lazyload);
20
28
  }
21
- el.appendChild(script);
22
- }
29
+
30
+ // Store in global stellar namespace for PJAX reinitialization
31
+ window.stellar = window.stellar || {};
32
+ window.stellar.initComments = window.stellar.initComments || {};
33
+ window.stellar.initComments.utterances = initUtterances;
34
+
35
+ // Initialize on page load
36
+ initUtterances();
37
+ })();
23
38
  </script>
@@ -1,36 +1,48 @@
1
1
  <script type="module">
2
2
  import { init } from '<%- theme.comments.waline.js %>';
3
3
 
4
- const el = document.getElementById('waline_container');
5
- util.viewportLazyload(el, load_waline, <%= theme.comments.lazyload ?? false %>);
6
-
7
- function load_waline(){
4
+ const lazyload = <%= theme.comments.lazyload ?? false %>;
5
+
6
+ function initWaline() {
7
+ const el = document.getElementById('waline_container');
8
8
  if (!el) return;
9
9
 
10
- utils.css('<%- theme.comments.waline.css %>');
11
- utils.css('<%- theme.comments.waline.meta_css %>');
10
+ function load_waline() {
11
+ utils.css('<%- theme.comments.waline.css %>');
12
+ utils.css('<%- theme.comments.waline.meta_css %>');
12
13
 
13
- const path = el.getAttribute('comment_id') ?? decodeURI(window.location.pathname);
14
- const waline = init(Object.assign(<%- JSON.stringify(theme.comments.waline) %>, {
15
- el: '#waline_container',
16
- path: path,
17
- <% if(!!theme.comments.waline.imageUploader?.api){ %>
18
- imageUploader: function(file) {
19
- const headers = new Headers();
20
- headers.set('Accept', 'application/json');
21
- <% if(!!theme.comments.waline.imageUploader?.token) { %>
22
- headers.set('<%= theme.comments.waline.imageUploader?.tokenName %>', '<%= theme.comments.waline.imageUploader?.token %>')
23
- <% } %>
24
- const formData = new FormData();
25
- formData.append('<%= theme.comments.waline.imageUploader?.fileName %>', file);
26
- return fetch('<%= theme.comments.waline.imageUploader?.api %>',{
27
- method: 'POST',
28
- body: formData,
29
- headers: headers
30
- }).then((resp) => resp.json())
31
- .then((resp) => resp.<%= theme.comments.waline.imageUploader?.resp %>)
32
- },
33
- <% } %>
34
- }));
14
+ const path = el.getAttribute('comment_id') ?? decodeURI(window.location.pathname);
15
+ const waline = init(Object.assign(<%- JSON.stringify(theme.comments.waline) %>, {
16
+ el: '#waline_container',
17
+ path: path,
18
+ <% if(!!theme.comments.waline.imageUploader?.api){ %>
19
+ imageUploader: function(file) {
20
+ const headers = new Headers();
21
+ headers.set('Accept', 'application/json');
22
+ <% if(!!theme.comments.waline.imageUploader?.token) { %>
23
+ headers.set('<%= theme.comments.waline.imageUploader?.tokenName %>', '<%= theme.comments.waline.imageUploader?.token %>')
24
+ <% } %>
25
+ const formData = new FormData();
26
+ formData.append('<%= theme.comments.waline.imageUploader?.fileName %>', file);
27
+ return fetch('<%= theme.comments.waline.imageUploader?.api %>',{
28
+ method: 'POST',
29
+ body: formData,
30
+ headers: headers
31
+ }).then((resp) => resp.json())
32
+ .then((resp) => resp.<%= theme.comments.waline.imageUploader?.resp %>)
33
+ },
34
+ <% } %>
35
+ }));
36
+ }
37
+
38
+ util.viewportLazyload(el, load_waline, lazyload);
35
39
  }
40
+
41
+ // Store in global stellar namespace for PJAX reinitialization
42
+ window.stellar = window.stellar || {};
43
+ window.stellar.initComments = window.stellar.initComments || {};
44
+ window.stellar.initComments.waline = initWaline;
45
+
46
+ // Initialize on page load
47
+ initWaline();
36
48
  </script>
@@ -6,7 +6,7 @@ function layoutWikiCover() {
6
6
  return el;
7
7
  }
8
8
  if (proj.homepage.path !== page.path) {
9
- return;
9
+ return el;
10
10
  }
11
11
  if (proj.coverpage == undefined || proj.coverpage === false || proj.coverpage === '[]') {
12
12
  return el;
@@ -70,6 +70,46 @@ function og_args() {
70
70
  return Object.assign(args, page.open_graph);
71
71
  }
72
72
 
73
+ function escape_meta_content(content) {
74
+ return String(content || '')
75
+ .replace(/&/g, '&amp;')
76
+ .replace(/"/g, '&quot;')
77
+ .replace(/</g, '&lt;')
78
+ .replace(/>/g, '&gt;');
79
+ }
80
+
81
+ function generate_og_title() {
82
+ return escape_meta_content(strip_html(page.title || config.title));
83
+ }
84
+
85
+ function generate_og_site_name() {
86
+ if (page.layout === 'post' && page.title) {
87
+ return generate_og_title();
88
+ }
89
+ return escape_meta_content(strip_html(config.title));
90
+ }
91
+
92
+ function render_open_graph() {
93
+ if (!(theme.open_graph && theme.open_graph.enable)) {
94
+ return '';
95
+ }
96
+ let meta = open_graph(og_args());
97
+ meta = meta.replace(/<meta property="og:title" content="[^"]*">/, `<meta property="og:title" content="${generate_og_title()}">`);
98
+ if (meta.match(/<meta property="og:site_name" content="[^"]*">/)) {
99
+ meta = meta.replace(/<meta property="og:site_name" content="[^"]*">/, `<meta property="og:site_name" content="${generate_og_site_name()}">`);
100
+ } else {
101
+ meta += `<meta property="og:site_name" content="${generate_og_site_name()}">`;
102
+ }
103
+ if (page.title) {
104
+ if (meta.match(/<meta name="twitter:title" content="[^"]*">/)) {
105
+ meta = meta.replace(/<meta name="twitter:title" content="[^"]*">/, `<meta name="twitter:title" content="${generate_og_title()}">`);
106
+ } else {
107
+ meta += `<meta name="twitter:title" content="${generate_og_title()}">`;
108
+ }
109
+ }
110
+ return meta;
111
+ }
112
+
73
113
  function preconnect() {
74
114
  var el = '';
75
115
  for (let href of (theme.preconnect || [])) {
@@ -127,18 +167,27 @@ function generate_canonical() {
127
167
  <meta name="theme-color" content="#f9fafb">
128
168
  <title><%- generate_title() %></title>
129
169
 
130
- <% if (theme.open_graph && theme.open_graph.enable) { %>
131
- <%- open_graph(og_args()) %>
132
- <% } %>
170
+ <%- render_open_graph() %>
133
171
  <%- generate_description() %>
134
172
 
135
173
  <%- generate_keywords() %>
136
174
 
137
175
  <!-- feed -->
138
176
  <% if (config.feed && config.feed.path) { %>
139
- <%- feed_tag(config.feed.path, {title: config.title}) %>
177
+ <%
178
+ var paths = Array.isArray(config.feed.path)
179
+ ? config.feed.path
180
+ : [config.feed.path];
181
+ %>
182
+
183
+ <% paths.forEach(function (path) { %>
184
+ <% if (path) { %>
185
+ <%- feed_tag(path, { title: config.title }) %>
186
+ <% } %>
187
+ <% }) %>
140
188
  <% } %>
141
189
 
190
+ <link rel="preload" href="<%- url_for(`${theme.stellar.main_css}?v=${stellar_info('version')}`) %>" as="style">
142
191
  <link rel="stylesheet" href="<%- url_for(`${theme.stellar.main_css}?v=${stellar_info('version')}`) %>">
143
192
 
144
193
 
@@ -4,10 +4,30 @@
4
4
  const canonicalWithEncoded = Object.assign({}, canonical, {
5
5
  encoded: encoded
6
6
  });
7
+ const permalink = page.permalink || canonical.originalHost;
8
+ const checklink = theme.data_services.video.js
7
9
  %>
8
10
 
9
11
  <script type="text/javascript">
10
12
  window.canonical = <%- JSON.stringify(canonicalWithEncoded) %>;
13
+ window.canonical["param"] = <%- JSON.stringify({permalink, checklink}) %>;
14
+ // Fix: Ensure page is at the correct top position on data load
15
+ if (history.scrollRestoration) {
16
+ history.scrollRestoration = 'auto';
17
+ }
18
+ (function() {
19
+ const scrollHandler = () => {
20
+ const wikiCover = document.querySelector('#l_cover .l_cover.wiki');
21
+ const startEl = document.getElementById('start');
22
+ if (wikiCover && startEl) {
23
+ window.scrollTo(0, startEl.offsetTop);
24
+ } else {
25
+ window.scrollTo(0, 0);
26
+ }
27
+ };
28
+ if (document.readyState !== 'loading') scrollHandler();
29
+ else document.addEventListener('DOMContentLoaded', scrollHandler);
30
+ })();
11
31
  const ctx = {
12
32
  date_suffix: {
13
33
  just: `<%- __('meta.date_suffix.just') %>`,
@@ -38,8 +58,6 @@
38
58
  const deps = {
39
59
  jquery: `<%- url_for(theme.dependencies.jquery) %>`,
40
60
  marked: `<%- url_for(theme.dependencies.marked) %>`,
41
- lazyload: `<%- url_for(theme.dependencies.lazyload) %>`
61
+ lazyload: `<%- url_for(theme.dependencies.lazyload.js) %>`
42
62
  }
43
-
44
-
45
63
  </script>
@@ -30,7 +30,7 @@
30
30
  container = document.querySelector(container);
31
31
  }
32
32
  if (!container) return;
33
-
33
+
34
34
  const images = container.querySelectorAll('img');
35
35
  images.forEach((img) => {
36
36
  if (img.classList.contains('lazy')) return;
@@ -63,5 +63,5 @@
63
63
  window.lazyLoadInstance.update();
64
64
  }
65
65
  }
66
-
66
+
67
67
  </script>