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,53 @@
1
+ const { Component } = require("../../include/util/common");
2
+
3
+ class FloatingToc extends Component {
4
+ render() {
5
+ const { helper, page } = this.props;
6
+ const tocContent = helper.toc(page.content, {
7
+ class: "toc",
8
+ list_number: false,
9
+ });
10
+
11
+ if (!tocContent) {
12
+ return null;
13
+ }
14
+
15
+ return (
16
+ <div class="toc-container" id="icarus-toc-container">
17
+ <button class="toc-button" onclick="document.getElementById('icarus-toc-container').classList.toggle('is-open')">
18
+ <svg
19
+ xmlns="http://www.w3.org/2000/svg"
20
+ width="24"
21
+ height="24"
22
+ viewBox="0 0 24 24"
23
+ fill="none"
24
+ stroke="currentColor"
25
+ stroke-width="2"
26
+ stroke-linecap="round"
27
+ stroke-linejoin="round"
28
+ aria-label="Table of Contents"
29
+ >
30
+ <title>Table of Contents</title>
31
+ <line x1="8" y1="6" x2="21" y2="6"></line>
32
+ <line x1="8" y1="12" x2="21" y2="12"></line>
33
+ <line x1="8" y1="18" x2="21" y2="18"></line>
34
+ <line x1="3" y1="6" x2="3.01" y2="6"></line>
35
+ <line x1="3" y1="12" x2="3.01" y2="12"></line>
36
+ <line x1="3" y1="18" x2="3.01" y2="18"></line>
37
+ </svg>
38
+ </button>
39
+ <div class="toc-body" onclick="if(event.target === this || event.target.closest('.toc-link')) { document.getElementById('icarus-toc-container').classList.remove('is-open'); }">
40
+ <div dangerouslySetInnerHTML={{ __html: tocContent }} />
41
+ </div>
42
+ </div>
43
+ );
44
+ }
45
+ }
46
+
47
+ class Widgets extends Component {
48
+ render() {
49
+ return <FloatingToc {...this.props} />;
50
+ }
51
+ }
52
+
53
+ module.exports = Widgets;
@@ -0,0 +1,29 @@
1
+ const { Component, Fragment } = require("../include/util/common");
2
+ const Paginator = require("./misc/paginator");
3
+ const Article = require("./common/article");
4
+
5
+ module.exports = class extends Component {
6
+ render() {
7
+ const { config, page, helper } = this.props;
8
+ const { __, url_for } = helper;
9
+
10
+ return (
11
+ <Fragment>
12
+ {page.posts.map((post) => (
13
+ <Article config={config} page={post} helper={helper} index={true} />
14
+ ))}
15
+ {page.total > 1 ? (
16
+ <Paginator
17
+ current={page.current}
18
+ total={page.total}
19
+ baseUrl={page.base}
20
+ path={config.pagination_dir}
21
+ urlFor={url_for}
22
+ prevTitle={__("common.prev")}
23
+ nextTitle={__("common.next")}
24
+ />
25
+ ) : null}
26
+ </Fragment>
27
+ );
28
+ }
29
+ };
@@ -0,0 +1,34 @@
1
+ const { Component } = require("../include/util/common");
2
+ const Head = require("./common/head");
3
+ const Navbar = require("./common/navbar");
4
+ const Footer = require("./common/footer");
5
+ const Scripts = require("./common/scripts");
6
+ const Search = require("./common/search");
7
+ const ThemeSelector = require("./common/theme_selector");
8
+
9
+ module.exports = class extends Component {
10
+ render() {
11
+ const { site, config, page, helper, body } = this.props;
12
+
13
+ const language = page.lang || page.language || config.language || "en";
14
+
15
+ return (
16
+ <html lang={language ? language : ""}>
17
+ <Head site={site} config={config} helper={helper} page={page} />
18
+ <body>
19
+ <Navbar config={config} helper={helper} page={page} />
20
+ <ThemeSelector />
21
+ <section class="section">
22
+ <div
23
+ class="main-content"
24
+ dangerouslySetInnerHTML={{ __html: body }}
25
+ ></div>
26
+ </section>
27
+ <Footer site={site} config={config} helper={helper} />
28
+ <Scripts site={site} config={config} helper={helper} page={page} />
29
+ <Search config={config} helper={helper} />
30
+ </body>
31
+ </html>
32
+ );
33
+ }
34
+ };
@@ -0,0 +1,114 @@
1
+ const { Component, cacheComponent } = require("../../include/util/common");
2
+ class ArticleLicensing extends Component {
3
+ render() {
4
+ const {
5
+ title,
6
+ link,
7
+ author,
8
+ authorTitle,
9
+ createdAt,
10
+ createdTitle,
11
+ updatedAt,
12
+ updatedTitle,
13
+ licenses,
14
+ licensedTitle,
15
+ } = this.props;
16
+ return (
17
+ <div class="article-licensing">
18
+ <div class="article-licensing-bg-icon">
19
+ <iconify-icon icon="mdi:creative-commons" />
20
+ </div>
21
+ <div class="licensing-title">
22
+ {title ? <p>{title}</p> : null}
23
+ <p>
24
+ <a href={link}>{link}</a>
25
+ </p>
26
+ </div>
27
+ <div class="licensing-meta level is-mobile">
28
+ <div style="display: flex">
29
+ {author ? (
30
+ <div class="level-item is-narrow">
31
+ <div>
32
+ <p>{authorTitle}</p>
33
+ <p>{author}</p>
34
+ </div>
35
+ </div>
36
+ ) : null}
37
+ {createdAt ? (
38
+ <div class="level-item is-narrow">
39
+ <div>
40
+ <p>{createdTitle}</p>
41
+ <p>{createdAt}</p>
42
+ </div>
43
+ </div>
44
+ ) : null}
45
+ {updatedAt ? (
46
+ <div class="level-item is-narrow">
47
+ <div>
48
+ <p>{updatedTitle}</p>
49
+ <p>{updatedAt}</p>
50
+ </div>
51
+ </div>
52
+ ) : null}
53
+ {licenses && Object.keys(licenses).length ? (
54
+ <div class="level-item is-narrow">
55
+ <div>
56
+ <p>{licensedTitle}</p>
57
+ <p>
58
+ {Object.keys(licenses).map((name) => (
59
+ <a
60
+ rel="noopener"
61
+ target="_blank"
62
+ title={name}
63
+ href={licenses[name].url}
64
+ >
65
+ <iconify-icon icon={licenses[name].icon} />
66
+ </a>
67
+ ))}
68
+ </p>
69
+ </div>
70
+ </div>
71
+ ) : null}
72
+ </div>
73
+ </div>
74
+ </div>
75
+ );
76
+ }
77
+ }
78
+
79
+ ArticleLicensing.Cacheable = cacheComponent(
80
+ ArticleLicensing,
81
+ "misc.articlelicensing",
82
+ (props) => {
83
+ const { config, page, helper } = props;
84
+ const { licenses } = config.article || {};
85
+
86
+ const links = {};
87
+ if (licenses) {
88
+ Object.keys(licenses).forEach((name) => {
89
+ const license = licenses[name];
90
+ links[name] = {
91
+ url: helper.url_for(
92
+ typeof license === "string" ? license : license.url,
93
+ ),
94
+ icon: license.icon,
95
+ };
96
+ });
97
+ }
98
+
99
+ return {
100
+ title: page.title,
101
+ link: decodeURI(page.permalink),
102
+ author: page.author || config.author,
103
+ authorTitle: helper.__("article.licensing.author"),
104
+ createdAt: page.date ? helper.date(page.date) : null,
105
+ createdTitle: helper.__("article.licensing.created_at"),
106
+ updatedAt: page.updated ? helper.date(page.updated) : null,
107
+ updatedTitle: helper.__("article.licensing.updated_at"),
108
+ licenses: links,
109
+ licensedTitle: helper.__("article.licensing.licensed_under"),
110
+ };
111
+ },
112
+ );
113
+
114
+ module.exports = ArticleLicensing;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * A JSX component that renders &lt;meta&gt; tags.
3
+ * @module view/misc/meta
4
+ */
5
+ const { Component } = require("inferno");
6
+
7
+ function trim(str) {
8
+ return str
9
+ .trim()
10
+ .replace(/^"(.*)"$/, "$1")
11
+ .replace(/^'(.*)'$/, "$1");
12
+ }
13
+
14
+ function split(str, sep) {
15
+ const result = [];
16
+ let matched = null;
17
+ while ((matched = sep.exec(str)) !== null) {
18
+ result.push(matched[0]);
19
+ }
20
+ return result;
21
+ }
22
+
23
+ /**
24
+ * A JSX component that renders &lt;meta&gt; tags.
25
+ *
26
+ * @name Meta
27
+ * @example
28
+ * <Meta meta={[
29
+ * 'name="generator";content="Hexo 4.2.0"'
30
+ * 'property="article:author";content="PPOffice"'
31
+ * ]} />
32
+ */
33
+ module.exports = class extends Component {
34
+ render() {
35
+ let { meta = [] } = this.props;
36
+ if (!Array.isArray(meta)) {
37
+ meta = [meta];
38
+ }
39
+ const tags = meta
40
+ .filter((entry) => typeof entry === "string")
41
+ .map((entry) => {
42
+ const props = split(entry, /(?:[^\\;]+|\\.)+/g)
43
+ .map((property) => {
44
+ const entry = split(property, /(?:[^\\=]+|\\.)+/g);
45
+ if (entry.length < 2) {
46
+ return null;
47
+ }
48
+ return { [trim(entry[0])]: trim(entry[1]) };
49
+ })
50
+ .filter((property) => {
51
+ return property !== null;
52
+ })
53
+ .reduce((prev, current) => {
54
+ return Object.assign(prev, current);
55
+ }, {});
56
+ return <meta {...props} />;
57
+ });
58
+
59
+ return <>{tags}</>;
60
+ }
61
+ };
@@ -0,0 +1,164 @@
1
+ /**
2
+ * A JSX component that renders Open Graph tags.
3
+ * @module view/misc/open_graph
4
+ *
5
+ * @see https://hexo.io/docs/helpers#open-graph
6
+ * @see https://github.com/hexojs/hexo/blob/4.2.0/lib/plugins/helper/open_graph.js
7
+ */
8
+ const urlFn = require("node:url");
9
+ const moment = require("moment");
10
+ const { Component } = require("inferno");
11
+ const { encodeURL, stripHTML, escapeHTML } = require("hexo-util");
12
+ const localeMap = {
13
+ en: "en_US",
14
+ de: "de_DE",
15
+ es: "es_ES",
16
+ fr: "fr_FR",
17
+ hu: "hu_HU",
18
+ id: "id_ID",
19
+ it: "it_IT",
20
+ ja: "ja_JP",
21
+ ko: "ko_KR",
22
+ nl: "nl_NL",
23
+ ru: "ru_RU",
24
+ th: "th_TH",
25
+ tr: "tr_TR",
26
+ vi: "vi_VN",
27
+ };
28
+ const localeRegex = new RegExp(Object.keys(localeMap).join("|"), "i");
29
+
30
+ module.exports = class extends Component {
31
+ render() {
32
+ const {
33
+ type,
34
+ title,
35
+ date,
36
+ updated,
37
+ author,
38
+ url,
39
+ siteName,
40
+ twitterCard,
41
+ twitterSite,
42
+ googlePlus,
43
+ facebookAdmins,
44
+ facebookAppId,
45
+ } = this.props;
46
+ let { description, language, images, keywords, twitterId } = this.props;
47
+
48
+ const htmlTags = [];
49
+
50
+ if (description) {
51
+ description = escapeHTML(
52
+ stripHTML(description).substring(0, 200).trim(),
53
+ ).replace(/\n/g, " ");
54
+ htmlTags.push(<meta name="description" content={description} />);
55
+ }
56
+
57
+ htmlTags.push(<meta property="og:type" content={type || "website"} />);
58
+ htmlTags.push(<meta property="og:title" content={title} />);
59
+ htmlTags.push(<meta property="og:url" content={encodeURL(url)} />);
60
+ htmlTags.push(<meta property="og:site_name" content={siteName} />);
61
+
62
+ if (description) {
63
+ htmlTags.push(<meta property="og:description" content={description} />);
64
+ }
65
+
66
+ if (language) {
67
+ if (language.length === 2) {
68
+ language = language.replace(localeRegex, (str) => localeMap[str]);
69
+ htmlTags.push(<meta property="og:locale" content={language} />);
70
+ } else if (language.length === 5) {
71
+ const territory = language.slice(-2);
72
+ const territoryRegex = new RegExp(territory.concat("$"));
73
+ language = language
74
+ .replace("-", "_")
75
+ .replace(territoryRegex, territory.toUpperCase());
76
+ htmlTags.push(<meta property="og:locale" content={language} />);
77
+ }
78
+ }
79
+
80
+ if (!Array.isArray(images)) {
81
+ images = [images];
82
+ }
83
+ images
84
+ .map((path) => {
85
+ if (!urlFn.parse(path).host) {
86
+ // resolve `path`'s absolute path relative to current page's url
87
+ // `path` can be both absolute (starts with `/`) or relative.
88
+ return urlFn.resolve(url, path);
89
+ }
90
+ return path;
91
+ })
92
+ .forEach((path) => {
93
+ htmlTags.push(<meta property="og:image" content={path} />);
94
+ });
95
+
96
+ if (
97
+ date &&
98
+ (moment.isMoment(date) || moment.isDate(date)) &&
99
+ !Number.isNaN(date.valueOf())
100
+ ) {
101
+ htmlTags.push(
102
+ <meta property="article:published_time" content={date.toISOString()} />,
103
+ );
104
+ }
105
+
106
+ if (
107
+ updated &&
108
+ (moment.isMoment(updated) || moment.isDate(updated)) &&
109
+ !Number.isNaN(updated.valueOf())
110
+ ) {
111
+ htmlTags.push(
112
+ <meta
113
+ property="article:modified_time"
114
+ content={updated.toISOString()}
115
+ />,
116
+ );
117
+ }
118
+
119
+ if (author) {
120
+ htmlTags.push(<meta property="article:author" content={author} />);
121
+ }
122
+
123
+ if (keywords) {
124
+ if (typeof keywords === "string") {
125
+ keywords = [keywords];
126
+ }
127
+
128
+ keywords
129
+ .map((tag) => {
130
+ return tag.name ? tag.name : tag;
131
+ })
132
+ .filter(Boolean)
133
+ .forEach((keyword) => {
134
+ htmlTags.push(<meta property="article:tag" content={keyword} />);
135
+ });
136
+ }
137
+
138
+ htmlTags.push(
139
+ <meta property="twitter:card" content={twitterCard || "summary"} />,
140
+ );
141
+
142
+ if (twitterId) {
143
+ htmlTags.push(<meta property="twitter:creator" content={twitterId} />);
144
+ }
145
+
146
+ if (twitterSite) {
147
+ htmlTags.push(<meta property="twitter:site" content={twitterSite} />);
148
+ }
149
+
150
+ if (googlePlus) {
151
+ htmlTags.push(<link rel="publisher" href={googlePlus} />);
152
+ }
153
+
154
+ if (facebookAdmins) {
155
+ htmlTags.push(<meta property="fb:admins" content={facebookAdmins} />);
156
+ }
157
+
158
+ if (facebookAppId) {
159
+ htmlTags.push(<meta property="fb:app_id" content={facebookAppId} />);
160
+ }
161
+
162
+ return <>{htmlTags}</>;
163
+ }
164
+ };
@@ -0,0 +1,90 @@
1
+ const { Component } = require("inferno");
2
+
3
+ module.exports = class extends Component {
4
+ render() {
5
+ const { current, total, baseUrl, path, urlFor, prevTitle, nextTitle } =
6
+ this.props;
7
+
8
+ function getPageUrl(i) {
9
+ return urlFor(i === 1 ? baseUrl : `${baseUrl + path}/${i}/`);
10
+ }
11
+
12
+ function pagination(c, m) {
13
+ const current = c;
14
+ const last = m;
15
+ const delta = 2;
16
+ const left = current - delta;
17
+ const right = current + delta + 1;
18
+ const range = [];
19
+ const elements = [];
20
+ let l;
21
+
22
+ for (let i = 1; i <= last; i++) {
23
+ if (i === 1 || i === last || (i >= left && i < right)) {
24
+ range.push(i);
25
+ }
26
+ }
27
+
28
+ for (const i of range) {
29
+ if (l) {
30
+ if (i - l === 2) {
31
+ elements.push(
32
+ <li>
33
+ <a class="pagination-link" href={getPageUrl(l + 1)}>
34
+ {l + 1}
35
+ </a>
36
+ </li>,
37
+ );
38
+ } else if (i - l !== 1) {
39
+ elements.push(
40
+ <li>
41
+ <span
42
+ class="pagination-ellipsis"
43
+ dangerouslySetInnerHTML={{ __html: "&hellip;" }}
44
+ ></span>
45
+ </li>,
46
+ );
47
+ }
48
+ }
49
+ elements.push(
50
+ <li>
51
+ <a
52
+ class={`pagination-link${c === i ? " is-current" : ""}`}
53
+ href={getPageUrl(i)}
54
+ >
55
+ {i}
56
+ </a>
57
+ </li>,
58
+ );
59
+ l = i;
60
+ }
61
+ return elements;
62
+ }
63
+
64
+ return (
65
+ <nav
66
+ class="pagination is-centered"
67
+ aria-label="pagination"
68
+ style="padding-top: 1.5em;"
69
+ >
70
+ <a
71
+ href={getPageUrl(current - 1)}
72
+ class={`pagination-previous`}
73
+ style={current > 1 ? {} : { visibility: "hidden" }}
74
+ >
75
+ {prevTitle}
76
+ </a>
77
+ <a
78
+ href={getPageUrl(current + 1)}
79
+ class={`pagination-next`}
80
+ style={current < total ? {} : { visibility: "hidden" }}
81
+ >
82
+ {nextTitle}
83
+ </a>
84
+ <ul class="pagination-list is-hidden-mobile">
85
+ {pagination(current, total)}
86
+ </ul>
87
+ </nav>
88
+ );
89
+ }
90
+ };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * A JSX component that renders simple Google structured data.
3
+ * @module view/misc/structured_data
4
+ */
5
+ const urlFn = require("node:url");
6
+ const moment = require("moment");
7
+ const { Component } = require("inferno");
8
+ const { stripHTML, escapeHTML } = require("hexo-util");
9
+
10
+ /**
11
+ * A JSX component that renders simple Google structured data.
12
+ *
13
+ * @name StructuredData
14
+ * @example
15
+ * <StructuredData
16
+ * title="Page title"
17
+ * url="/page/url"
18
+ * author="Page author name"
19
+ * publisher="Page publisher name"
20
+ * publisherLogo="/path/to/logo"
21
+ * description="Page description"
22
+ * images={[ '/path/to/image' ]}
23
+ * date="Page publish date"
24
+ * updated="Page update date" />
25
+ */
26
+ module.exports = class extends Component {
27
+ render() {
28
+ const { title, url, author, publisher } = this.props;
29
+ let { description, images, date, updated, publisherLogo } = this.props;
30
+
31
+ if (description) {
32
+ description = escapeHTML(
33
+ stripHTML(description).substring(0, 200).trim(),
34
+ ).replace(/\n/g, " ");
35
+ }
36
+
37
+ if (!Array.isArray(images)) {
38
+ images = [images];
39
+ }
40
+ images = images
41
+ .map((path) => {
42
+ if (!urlFn.parse(path).host) {
43
+ // resolve `path`'s absolute path relative to current page's url
44
+ // `path` can be both absolute (starts with `/`) or relative.
45
+ return urlFn.resolve(url, path);
46
+ }
47
+ return path;
48
+ })
49
+ .filter(
50
+ (url) =>
51
+ url.endsWith(".jpg") ||
52
+ url.endsWith(".png") ||
53
+ url.endsWith(".gif") ||
54
+ url.endsWith(".webp"),
55
+ );
56
+
57
+ if (typeof publisherLogo === "string" && !urlFn.parse(publisherLogo).host) {
58
+ publisherLogo = urlFn.resolve(url, publisherLogo);
59
+ }
60
+
61
+ if (
62
+ date &&
63
+ (moment.isMoment(date) || moment.isDate(date)) &&
64
+ !Number.isNaN(date.valueOf())
65
+ ) {
66
+ date = date.toISOString();
67
+ }
68
+
69
+ if (
70
+ updated &&
71
+ (moment.isMoment(updated) || moment.isDate(updated)) &&
72
+ !Number.isNaN(updated.valueOf())
73
+ ) {
74
+ updated = updated.toISOString();
75
+ }
76
+
77
+ const data = {
78
+ "@context": "https://schema.org",
79
+ "@type": "BlogPosting",
80
+ mainEntityOfPage: {
81
+ "@type": "WebPage",
82
+ "@id": url,
83
+ },
84
+ headline: title,
85
+ image: images,
86
+ datePublished: date,
87
+ dateModified: updated,
88
+ author: {
89
+ "@type": "Person",
90
+ name: author,
91
+ },
92
+ publisher: {
93
+ "@type": "Organization",
94
+ name: publisher,
95
+ logo: {
96
+ "@type": "ImageObject",
97
+ url: publisherLogo,
98
+ },
99
+ },
100
+ description: description,
101
+ };
102
+
103
+ return (
104
+ <script
105
+ type="application/ld+json"
106
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
107
+ ></script>
108
+ );
109
+ }
110
+ };