hexo-theme-gnix 1.1.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 (143) hide show
  1. package/README.md +106 -0
  2. package/include/hexo/filter/locals.js +109 -0
  3. package/include/hexo/generator/categories.js +12 -0
  4. package/include/hexo/generator/category.js +52 -0
  5. package/include/hexo/generator/insight.js +50 -0
  6. package/include/hexo/generator/manifest.js +23 -0
  7. package/include/hexo/generator/tags.js +12 -0
  8. package/include/hexo/helper/cdn.js +21 -0
  9. package/include/hexo/helper/page.js +27 -0
  10. package/include/hexo/view.js +40 -0
  11. package/include/register.js +11 -0
  12. package/include/util/common.js +33 -0
  13. package/languages/en.yml +47 -0
  14. package/languages/fr.yml +46 -0
  15. package/languages/ja.yml +46 -0
  16. package/languages/zh-CN.yml +47 -0
  17. package/languages/zh-TW.yml +47 -0
  18. package/layout/archive.jsx +118 -0
  19. package/layout/categories.jsx +137 -0
  20. package/layout/category.jsx +38 -0
  21. package/layout/comment/disqus.jsx +79 -0
  22. package/layout/comment/disqusjs.jsx +127 -0
  23. package/layout/comment/giscus.jsx +193 -0
  24. package/layout/comment/gitalk.jsx +141 -0
  25. package/layout/comment/twikoo.jsx +63 -0
  26. package/layout/comment/utterances.jsx +86 -0
  27. package/layout/comment/valine.jsx +143 -0
  28. package/layout/comment/waline.jsx +156 -0
  29. package/layout/common/article.jsx +131 -0
  30. package/layout/common/article_cover.jsx +33 -0
  31. package/layout/common/article_media.jsx +34 -0
  32. package/layout/common/comment.jsx +38 -0
  33. package/layout/common/footer.jsx +228 -0
  34. package/layout/common/head.jsx +242 -0
  35. package/layout/common/navbar.jsx +219 -0
  36. package/layout/common/plugins.jsx +39 -0
  37. package/layout/common/scripts.jsx +49 -0
  38. package/layout/common/search.jsx +22 -0
  39. package/layout/common/theme_selector.jsx +79 -0
  40. package/layout/common/toc.jsx +53 -0
  41. package/layout/index.jsx +29 -0
  42. package/layout/layout.jsx +34 -0
  43. package/layout/misc/article_licensing.jsx +114 -0
  44. package/layout/misc/meta.jsx +61 -0
  45. package/layout/misc/open_graph.jsx +164 -0
  46. package/layout/misc/paginator.jsx +90 -0
  47. package/layout/misc/structured_data.jsx +110 -0
  48. package/layout/misc/web_app.jsx +106 -0
  49. package/layout/page.jsx +12 -0
  50. package/layout/plugin/bing_webmaster.jsx +47 -0
  51. package/layout/plugin/busuanzi.jsx +40 -0
  52. package/layout/plugin/clarity.jsx +22 -0
  53. package/layout/plugin/cookie_consent.jsx +136 -0
  54. package/layout/plugin/google_analytics.jsx +66 -0
  55. package/layout/plugin/google_tag_mamager.jsx +41 -0
  56. package/layout/plugin/netlify.jsx +39 -0
  57. package/layout/plugin/pjax.jsx +20 -0
  58. package/layout/plugin/statcounter.jsx +69 -0
  59. package/layout/plugin/twitter_conversion_tracking.jsx +51 -0
  60. package/layout/post.jsx +16 -0
  61. package/layout/search/insight.jsx +53 -0
  62. package/layout/tag.jsx +29 -0
  63. package/layout/tags.jsx +55 -0
  64. package/package.json +42 -0
  65. package/scripts/index.js +1 -0
  66. package/source/css/callout_blocks.css +204 -0
  67. package/source/css/default.css +1590 -0
  68. package/source/css/font/woff2/Futura-Book.woff2 +0 -0
  69. package/source/css/font/woff2/Paris2024-Variable.woff2 +0 -0
  70. package/source/css/font/woff2/doto.woff2 +0 -0
  71. package/source/css/optional/chinese.css +17 -0
  72. package/source/css/responsive/desktop.css +164 -0
  73. package/source/css/responsive/mobile.css +46 -0
  74. package/source/css/responsive/tablet.css +46 -0
  75. package/source/css/responsive/touch.css +254 -0
  76. package/source/css/shiki/shiki.min.css +1 -0
  77. package/source/css/twikoo.css +2143 -0
  78. package/source/img/avatar.webp +0 -0
  79. package/source/img/background.webp +0 -0
  80. package/source/img/favicon.svg +6 -0
  81. package/source/img/logo.svg +9 -0
  82. package/source/img/og_image.png +0 -0
  83. package/source/js/busuanzi.js +46 -0
  84. package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.css +6 -0
  85. package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.js +1 -0
  86. package/source/js/host/iconify-icon/3.0.2/iconify-icon.min.js +12 -0
  87. package/source/js/host/medium-zoom/dist/medium-zoom.min.js +2 -0
  88. package/source/js/host/mermaid/mermaid.min.js +2811 -0
  89. package/source/js/host/pjax/0.2.8/pjax.min.js +1 -0
  90. package/source/js/host/twikoo/1.6.41/dist/twikoo.all.min.js +2 -0
  91. package/source/js/insight.js +330 -0
  92. package/source/js/instant-page.min.js +1 -0
  93. package/source/js/live2d_Asoul/Model/Ava/Ava.4096/texture_00.webp +0 -0
  94. package/source/js/live2d_Asoul/Model/Ava/Ava.moc3 +0 -0
  95. package/source/js/live2d_Asoul/Model/Ava/Ava.model3.json +323 -0
  96. package/source/js/live2d_Asoul/Model/Ava/Ava.physics3.json +1225 -0
  97. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_idle.motion3.json +1 -0
  98. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_shake01.motion3.json +1 -0
  99. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_shake02.motion3.json +1 -0
  100. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap01.motion3.json +1 -0
  101. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap02.motion3.json +1 -0
  102. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap03.motion3.json +1 -0
  103. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap04.motion3.json +1 -0
  104. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap05.motion3.json +1 -0
  105. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap06.motion3.json +1 -0
  106. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap07.motion3.json +1 -0
  107. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap08.motion3.json +1 -0
  108. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap09.motion3.json +1 -0
  109. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap10.motion3.json +1 -0
  110. package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap11.motion3.json +1 -0
  111. package/source/js/live2d_Asoul/Model/Ava/raw.ex.json +16 -0
  112. package/source/js/live2d_Asoul/Model/Ava/raw.model3.json +321 -0
  113. package/source/js/live2d_Asoul/Model/Diana/Diana.4096/texture_00.webp +0 -0
  114. package/source/js/live2d_Asoul/Model/Diana/Diana.moc3 +0 -0
  115. package/source/js/live2d_Asoul/Model/Diana/Diana.model3.json +212 -0
  116. package/source/js/live2d_Asoul/Model/Diana/Diana.physics3.json +764 -0
  117. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_idle.motion3.json +1 -0
  118. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap01.motion3.json +1 -0
  119. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap02.motion3.json +1 -0
  120. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap03.motion3.json +1 -0
  121. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap04.motion3.json +1 -0
  122. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap05.motion3.json +1 -0
  123. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap06.motion3.json +1 -0
  124. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap07.motion3.json +1 -0
  125. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap08.motion3.json +1 -0
  126. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap09.motion3.json +1 -0
  127. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap10.motion3.json +1 -0
  128. package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap11.motion3.json +1 -0
  129. package/source/js/live2d_Asoul/Model/Diana/raw.ex.json +16 -0
  130. package/source/js/live2d_Asoul/Model/Diana/raw.model3.json +210 -0
  131. package/source/js/live2d_Asoul/TweenLite.js +12 -0
  132. package/source/js/live2d_Asoul/cubism4.min.js +2 -0
  133. package/source/js/live2d_Asoul/live2dcubismcore.min.js +9 -0
  134. package/source/js/live2d_Asoul/load.js +231 -0
  135. package/source/js/live2d_Asoul/pio.css +161 -0
  136. package/source/js/live2d_Asoul/pio.js +296 -0
  137. package/source/js/live2d_Asoul/pio_sdk4.js +149 -0
  138. package/source/js/live2d_Asoul/pixi.min.js +9 -0
  139. package/source/js/main.js +218 -0
  140. package/source/js/pjax.js +29 -0
  141. package/source/js/shiki/shiki.js +191 -0
  142. package/source/js/theme-selector.js +206 -0
  143. package/util/cache.js +47 -0
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Waline comment JSX component.
3
+ * @module view/comment/waline
4
+ */
5
+ const { Component, cacheComponent } = require("../../include/util/common");
6
+ /**
7
+ * Waline comment JSX component.
8
+ *
9
+ * @see https://waline.js.org/guide/get-started.html
10
+ * @example
11
+ * <Waline
12
+ * serverURL="https://path/to/waline/server"
13
+ * path="window.location.pathname"
14
+ * lang="zh-CN"
15
+ * locale={{placeholder: "", ...}}
16
+ * emoji={["https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo"]}
17
+ * dark="auto"
18
+ * meta={["nick", "mail", "link"]}
19
+ * requiredMeta={[]}
20
+ * login="enable"
21
+ * wordLimit={0},
22
+ * pageSize={10}
23
+ * imageUploader={true}
24
+ * highlighter={true}
25
+ * texRenderer={false}
26
+ * search={true}
27
+ * visitor={false}
28
+ * pageview={false}
29
+ * comment={false}
30
+ * copyright={true}
31
+ * jsUrl="/path/to/Waline.js" />
32
+ */
33
+ class Waline extends Component {
34
+ render() {
35
+ const {
36
+ serverURL,
37
+ path = "window.location.pathname",
38
+ lang = "zh-CN",
39
+ locale,
40
+ emoji = ["https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo"],
41
+ dark = "",
42
+ meta = ["nick", "mail", "link"],
43
+ requiredMeta = [],
44
+ login = "enable",
45
+ wordLimit = 0,
46
+ pageSize = 10,
47
+ imageUploader = false,
48
+ highlighter = false,
49
+ texRenderer = false,
50
+ search = false,
51
+ pageview = false,
52
+ comment = false,
53
+ copyright = true,
54
+ jsUrl,
55
+ cssUrl,
56
+ } = this.props;
57
+ if (!serverURL) {
58
+ return (
59
+ <div class="notification is-danger">
60
+ You forgot to set the <code>server_url</code> for Waline. Please set
61
+ it in <code>_config.yml</code>.
62
+ </div>
63
+ );
64
+ }
65
+ const js = `import { init } from "${jsUrl}";
66
+ init({
67
+ el: '#waline-thread',
68
+ serverURL: ${JSON.stringify(serverURL)},
69
+ path: ${path},
70
+ ${lang ? `lang: ${JSON.stringify(lang)},` : ""}
71
+ ${locale ? `locale: ${JSON.stringify(locale)},` : ""}
72
+ ${emoji ? `emoji: ${JSON.stringify(emoji)},` : ""}
73
+ ${dark ? `dark: ${JSON.stringify(dark)},` : ""}
74
+ ${meta ? `meta: ${JSON.stringify(meta)},` : ""}
75
+ ${Array.isArray(requiredMeta) ? `requiredMeta: ${JSON.stringify(requiredMeta)},` : ""}
76
+ ${login ? `login: ${JSON.stringify(login)},` : ""}
77
+ ${wordLimit ? `wordLimit: ${JSON.stringify(wordLimit)},` : ""}
78
+ ${pageSize ? `pageSize: ${JSON.stringify(pageSize)},` : ""}
79
+ ${imageUploader === false ? `imageUploader: false,` : ""}
80
+ ${highlighter === false ? `highlighter: false,` : ""}
81
+ ${texRenderer === false ? `texRenderer: false,` : ""}
82
+ ${search === false ? `search: false,` : ""}
83
+ ${typeof pageview !== "undefined" ? `pageview: ${JSON.stringify(pageview)},` : ""}
84
+ ${typeof comment !== "undefined" ? `comment: ${JSON.stringify(comment)},` : ""}
85
+ ${`copyright: ${JSON.stringify(copyright)},`}
86
+ });`;
87
+ return (
88
+ <>
89
+ <div id="waline-thread" class="content"></div>
90
+ <link rel="stylesheet" href={cssUrl} />
91
+ <script type="module" dangerouslySetInnerHTML={{ __html: js }} />
92
+ </>
93
+ );
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Cacheable Waline comment JSX component.
99
+ * <p>
100
+ * This class is supposed to be used in combination with the <code>locals</code> hexo filter
101
+ * ({@link module:hexo/filter/locals}).
102
+ *
103
+ * @see module:util/cache.cacheComponent
104
+ * @example
105
+ * <Waline.Cacheable
106
+ * comment={{
107
+ * server_url: "https://path/to/waline/server",
108
+ * path: "window.location.pathname",
109
+ * lang: "zh-CN",
110
+ * locale: {placeholder: "", ...},
111
+ * emoji: "https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo",
112
+ * dark: "",
113
+ * meta: ["nick", "mail", "link"],
114
+ * required_meta: [],
115
+ * login: false,
116
+ * word_limit: 0,
117
+ * page_size: 10,
118
+ * image_uploader: true,
119
+ * highlighter: true,
120
+ * tex_renderer: false,
121
+ * search: true,
122
+ * pageview: false,
123
+ * comment: false,
124
+ * copyright: true,
125
+ * }}
126
+ * helper={{ cdn: function() {...} }} />
127
+ */
128
+ Waline.Cacheable = cacheComponent(Waline, "comment.waline", (props) => {
129
+ const { comment, helper, page, config } = props;
130
+
131
+ return {
132
+ serverURL: comment.server_url,
133
+ path: comment.path,
134
+ lang:
135
+ comment.lang || page.lang || page.language || config.language || "zh-CN",
136
+ locale: comment.locale,
137
+ emoji: comment.emoji,
138
+ dark: comment.dark,
139
+ meta: comment.meta,
140
+ requiredMeta: comment.required_meta,
141
+ login: comment.login,
142
+ wordLimit: comment.word_limit,
143
+ pageSize: comment.page_size,
144
+ imageUploader: comment.image_uploader,
145
+ highlighter: comment.highlighter,
146
+ texRenderer: comment.tex_renderer,
147
+ search: comment.search,
148
+ pageview: comment.pageview,
149
+ comment: comment.comment,
150
+ copyright: comment.copyright,
151
+ jsUrl: helper.cdn("@waline/client", "3.3.0", "dist/waline.js"),
152
+ cssUrl: helper.cdn("@waline/client", "3.3.0", "dist/waline.css"),
153
+ };
154
+ });
155
+
156
+ module.exports = Waline;
@@ -0,0 +1,131 @@
1
+ const { Component, Fragment } = require("../../include/util/common");
2
+ const Comment = require("./comment");
3
+ const ArticleLicensing = require("../misc/article_licensing");
4
+ const ArticleCover = require("./article_cover");
5
+
6
+ /**
7
+ * Get the word count of text.
8
+ */
9
+ function getWordCount(content) {
10
+ if (typeof content === "undefined") {
11
+ return 0;
12
+ }
13
+ content = content.replace(/<\/?[a-z][^>]*>/gi, "");
14
+ content = content.trim();
15
+ return content
16
+ ? (content.match(/[\u00ff-\uffff]|[a-zA-Z]+/g) || []).length
17
+ : 0;
18
+ }
19
+
20
+ module.exports = class extends Component {
21
+ render() {
22
+ // index: true if in article list, false if in article page
23
+ const { config, helper, page, index } = this.props;
24
+
25
+ const { article } = config;
26
+ const { url_for, date, _p } = helper;
27
+
28
+ const cover = page.cover ? url_for(page.cover) : null;
29
+
30
+ return (
31
+ <Fragment>
32
+ {/* Main content */}
33
+ <div class="card">
34
+ {/* Cover image */}
35
+ {cover ? (
36
+ <ArticleCover
37
+ page={page}
38
+ cover={cover}
39
+ index={index}
40
+ helper={helper}
41
+ />
42
+ ) : null}
43
+ <article
44
+ class={`card-content article${"direction" in page ? ` ${page.direction}` : ""}`}
45
+ >
46
+ {/* Metadata */}
47
+ {page.layout !== "page" ? (
48
+ <div class="article-meta">
49
+ <p>
50
+ {page.date && <span>{date(page.date)}</span>}
51
+ {page.categories?.length ? (
52
+ <span>
53
+ {page.categories.map((category) => (
54
+ <a href={url_for(category.path)}>/{category.name}</a>
55
+ ))}
56
+ </span>
57
+ ) : null}
58
+ <span
59
+ dangerouslySetInnerHTML={{
60
+ __html: _p(
61
+ "article.word_count",
62
+ getWordCount(page._content),
63
+ ),
64
+ }}
65
+ ></span>
66
+ {/* Visitor counter */}
67
+ {!index ? (
68
+ <span
69
+ id={url_for(page.link || page.path)}
70
+ data-flag-title={page.title}
71
+ dangerouslySetInnerHTML={{
72
+ __html: _p(
73
+ "plugin.visit_count",
74
+ '<span id="busuanzi_page_pv" style="padding-right: 0"></span>',
75
+ ),
76
+ }}
77
+ ></span>
78
+ ) : null}
79
+ </p>
80
+ </div>
81
+ ) : null}
82
+ {/* Title */}
83
+ {page.title !== "" && index ? (
84
+ <p class="title">
85
+ <a href={url_for(page.link || page.path)}>{page.title}</a>
86
+ </p>
87
+ ) : null}
88
+ {page.title !== "" && !index ? (
89
+ <h1 class="title">{page.title}</h1>
90
+ ) : null}
91
+ {/* Content/Excerpt */}
92
+ <div
93
+ class="content"
94
+ dangerouslySetInnerHTML={{
95
+ __html: index && page.excerpt ? page.excerpt : page.content,
96
+ }}
97
+ ></div>
98
+ {/* Tags */}
99
+ {!index && page.tags && page.tags.length ? (
100
+ <div class="article-tags">
101
+ {page.tags.map((tag) => {
102
+ return (
103
+ <a class="tags" rel="tag" href={url_for(tag.path)}>
104
+ <span class="tag">{tag.name}</span>
105
+ <span class="tag">{tag.length}</span>
106
+ </a>
107
+ );
108
+ })}
109
+ </div>
110
+ ) : null}
111
+ {/* Licensing block */}
112
+ {!index &&
113
+ article &&
114
+ article.licenses &&
115
+ Object.keys(article.licenses) ? (
116
+ <ArticleLicensing.Cacheable
117
+ page={page}
118
+ config={config}
119
+ helper={helper}
120
+ />
121
+ ) : null}
122
+ </article>
123
+ </div>
124
+ {/* Comment */}
125
+ {!index ? (
126
+ <Comment config={config} page={page} helper={helper} />
127
+ ) : null}
128
+ </Fragment>
129
+ );
130
+ }
131
+ };
@@ -0,0 +1,33 @@
1
+ const { Component } = require("inferno");
2
+
3
+ module.exports = class extends Component {
4
+ render() {
5
+ const { page, cover, helper } = this.props;
6
+ const { url_for } = helper;
7
+
8
+ const imageSrcset = `${cover}?w=256 256w, ${cover}?w=800 800w, ${cover}?w=1500 1500w, ${cover}?w=2000 2000w, ${cover} 6144w`;
9
+ const coverLQIP = (
10
+ <img class="cover-lqip" alt="lqip" src={`${cover}?q=10&blur=25`} />
11
+ );
12
+ const CoverImage = (
13
+ <img
14
+ class="fill"
15
+ src={cover}
16
+ alt={page.title || cover}
17
+ onLoad={"this.classList.add('loaded')"}
18
+ srcset={imageSrcset}
19
+ referrerpolicy="no-referrer"
20
+ decoding="async"
21
+ loading="lazy"
22
+ />
23
+ );
24
+ return (
25
+ <div class="card-image">
26
+ <a href={url_for(page.link || page.path)} class="image is-7by3">
27
+ {CoverImage}
28
+ {coverLQIP}
29
+ </a>
30
+ </div>
31
+ );
32
+ }
33
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Article media component, used in article lists such as archive page and recent posts widget
3
+ */
4
+ const { Component } = require("inferno");
5
+ const moment = require("moment");
6
+
7
+ module.exports = class extends Component {
8
+ render() {
9
+ const { url, title, date, categories } = this.props;
10
+ // Formatted like May.15
11
+ const formattedDate = moment(date).format("MMM.DD");
12
+ const categoryTags = [];
13
+ categories.forEach((category, i) => {
14
+ categoryTags.push(<a href={category.url}>{category.name}</a>);
15
+ if (i < categories.length - 1) {
16
+ categoryTags.push("/");
17
+ }
18
+ });
19
+
20
+ return (
21
+ <article class="archive-item">
22
+ <div>
23
+ <p class="article-meta">
24
+ <span>{formattedDate}</span>
25
+ {categoryTags.length ? <span>{categoryTags}</span> : null}
26
+ </p>
27
+ <p class="title" style="font-size: 1em;">
28
+ <a href={url}>{title}</a>
29
+ </p>
30
+ </div>
31
+ </article>
32
+ );
33
+ }
34
+ };
@@ -0,0 +1,38 @@
1
+ const {
2
+ Component,
3
+ loadComponent,
4
+ handleWidgetError,
5
+ } = require("../../include/util/common");
6
+
7
+ module.exports = class extends Component {
8
+ render() {
9
+ const { config, page, helper } = this.props;
10
+ const { __ } = helper;
11
+ const { comment } = config;
12
+ if (!comment || typeof comment.type !== "string") {
13
+ return null;
14
+ }
15
+
16
+ return (
17
+ <div class="card" id="comments">
18
+ <div class="card-content">
19
+ {(() => {
20
+ const Comment = loadComponent(`comment/${comment.type}`);
21
+ if (!Comment) {
22
+ handleWidgetError(`comment "${comment.type}"`);
23
+ return null;
24
+ }
25
+ return (
26
+ <Comment
27
+ config={config}
28
+ page={page}
29
+ helper={helper}
30
+ comment={comment}
31
+ />
32
+ );
33
+ })()}
34
+ </div>
35
+ </div>
36
+ );
37
+ }
38
+ };
@@ -0,0 +1,228 @@
1
+ const { Component, cacheComponent } = require("../../include/util/common");
2
+
3
+ class Footer extends Component {
4
+ render() {
5
+ const {
6
+ siteTitle,
7
+ siteYear,
8
+ author,
9
+ links,
10
+ subdomains,
11
+ archives,
12
+ copyright,
13
+ showVisitorCounter,
14
+ visitorCounterTitle,
15
+ ICPRecord,
16
+ } = this.props;
17
+
18
+ const svg_line = (
19
+ <svg
20
+ aria-hidden="true"
21
+ width="100%"
22
+ height="8"
23
+ fill="none"
24
+ xmlns="http://www.w3.org/2000/svg"
25
+ >
26
+ <pattern id="a" width="91" height="8" patternUnits="userSpaceOnUse">
27
+ <g clip-path="url(#clip0_2426_11367)">
28
+ <path
29
+ d="M114 4c-5.067 4.667-10.133 4.667-15.2 0S88.667-.667 83.6 4 73.467 8.667 68.4 4 58.267-.667 53.2 4 43.067 8.667 38 4 27.867-.667 22.8 4 12.667 8.667 7.6 4-2.533-.667-7.6 4s-10.133 4.667-15.2 0S-32.933-.667-38 4s-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0-10.133-4.667-15.2 0-10.133 4.667-15.2 0"
30
+ stroke="var(--lavender)"
31
+ stroke-linecap="square"
32
+ ></path>
33
+ </g>
34
+ </pattern>
35
+ <rect width="100%" height="100%" fill="url(#a)"></rect>
36
+ </svg>
37
+ );
38
+
39
+ const footer_brand = (
40
+ <div class="footer-column footer-brand">
41
+ <p class="footer-title">{author || siteTitle}</p>
42
+ <p class="footer-meta">
43
+ <span
44
+ dangerouslySetInnerHTML={{
45
+ __html: `&copy; ${siteYear} ${author || siteTitle}`,
46
+ }}
47
+ ></span>
48
+ &nbsp;Powered by Hexo&nbsp;& Icarus
49
+ {showVisitorCounter ? (
50
+ <>
51
+ <br />
52
+ <span
53
+ id="busuanzi_container_site_uv"
54
+ dangerouslySetInnerHTML={{ __html: visitorCounterTitle }}
55
+ ></span>
56
+ </>
57
+ ) : null}
58
+ {ICPRecord ? (
59
+ <>
60
+ <br />
61
+ <a
62
+ href="https://beian.miit.gov.cn/"
63
+ style={"color: inherit"}
64
+ target="_blank"
65
+ rel="noopener"
66
+ dangerouslySetInnerHTML={{ __html: ICPRecord }}
67
+ ></a>
68
+ </>
69
+ ) : null}
70
+ </p>
71
+ {copyright ? (
72
+ <p
73
+ class="footer-meta"
74
+ dangerouslySetInnerHTML={{ __html: copyright }}
75
+ ></p>
76
+ ) : null}
77
+ </div>
78
+ );
79
+
80
+ const footer_social = (
81
+ <div class="footer-column footer-social">
82
+ <p class="footer-heading">Social Media</p>
83
+ <div class="footer-links">
84
+ {Object.keys(links).length
85
+ ? Object.keys(links).map((name) => {
86
+ const link = links[name];
87
+ return (
88
+ <a
89
+ class="footer-link"
90
+ target="_blank"
91
+ rel="noopener"
92
+ title={name}
93
+ href={link.url}
94
+ >
95
+ {link.icon ? (
96
+ <iconify-icon icon={link.icon}></iconify-icon>
97
+ ) : (
98
+ name
99
+ )}
100
+ </a>
101
+ );
102
+ })
103
+ : null}
104
+ </div>
105
+ </div>
106
+ );
107
+
108
+ const footer_subdomains = (
109
+ <div class="footer-column footer-subdomains">
110
+ <p class="footer-heading">Sub Domains</p>
111
+ <div class="footer-links">
112
+ {Object.keys(subdomains).length
113
+ ? Object.keys(subdomains).map((name) => {
114
+ const link = subdomains[name];
115
+ return (
116
+ <a
117
+ class="footer-link"
118
+ target="_blank"
119
+ rel="noopener"
120
+ title={name}
121
+ href={link.url}
122
+ >
123
+ {name}
124
+ </a>
125
+ );
126
+ })
127
+ : null}
128
+ </div>
129
+ </div>
130
+ );
131
+
132
+ const footer_archives = (
133
+ <div class="footer-column footer-archives">
134
+ <p class="footer-heading">Archives</p>
135
+ <div class="footer-links">
136
+ {archives?.length
137
+ ? archives.map((item) => (
138
+ <a class="footer-link" href={item.url}>
139
+ {item.year}
140
+ </a>
141
+ ))
142
+ : null}
143
+ </div>
144
+ </div>
145
+ );
146
+
147
+ return (
148
+ <>
149
+ {" "}
150
+ {svg_line}
151
+ <footer class="footer">
152
+ <div class="footer-grid">
153
+ {footer_brand}
154
+ {footer_subdomains}
155
+ {footer_archives}
156
+ {footer_social}
157
+ </div>
158
+ </footer>
159
+ </>
160
+ );
161
+ }
162
+ }
163
+
164
+ module.exports = cacheComponent(Footer, "common.footer", (props) => {
165
+ const { config, helper, site } = props;
166
+ const { url_for, _p, date } = helper;
167
+ const { title, author, footer, plugins } = config;
168
+
169
+ const links = {};
170
+ if (footer?.links) {
171
+ Object.keys(footer.links).forEach((name) => {
172
+ const link = footer.links[name];
173
+ links[name] = {
174
+ url: url_for(typeof link === "string" ? link : link.url),
175
+ icon: link.icon,
176
+ };
177
+ });
178
+ }
179
+
180
+ const subdomains = {};
181
+ if (footer?.subdomains) {
182
+ Object.keys(footer.subdomains).forEach((name) => {
183
+ const link = footer.subdomains[name];
184
+ const targetUrl = typeof link === "string" ? link : link.url;
185
+ subdomains[name] = {
186
+ url: url_for(targetUrl),
187
+ };
188
+ });
189
+ }
190
+
191
+ // Build archives grouped by year from site posts
192
+ let archives = [];
193
+ if (site?.posts?.length) {
194
+ const archiveDir = config.archive_dir || "archives";
195
+ const byYear = {};
196
+ const posts = site.posts.sort("date", -1);
197
+
198
+ posts.forEach((post) => {
199
+ let d = post.date.clone();
200
+ if (config.timezone) {
201
+ d = d.tz(config.timezone);
202
+ }
203
+ const year = d.year();
204
+ byYear[year] = (byYear[year] || 0) + 1;
205
+ });
206
+
207
+ archives = Object.keys(byYear)
208
+ .sort((a, b) => Number(b) - Number(a))
209
+ .map((year) => ({ year, url: url_for(`${archiveDir}/${year}/`) }));
210
+ }
211
+
212
+ return {
213
+ siteUrl: url_for("/"),
214
+ siteTitle: title,
215
+ siteYear: date(new Date(), "YYYY"),
216
+ author,
217
+ links,
218
+ subdomains,
219
+ archives,
220
+ copyright: footer?.copyright ?? "",
221
+ showVisitorCounter: plugins && plugins.busuanzi === true,
222
+ visitorCounterTitle: _p(
223
+ "plugin.visitor_count",
224
+ '<span id="busuanzi_value_site_uv">0</span>',
225
+ ),
226
+ ICPRecord: footer?.ICPRecord || "",
227
+ };
228
+ });