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.
- package/README.md +106 -0
- package/include/hexo/filter/locals.js +109 -0
- package/include/hexo/generator/categories.js +12 -0
- package/include/hexo/generator/category.js +52 -0
- package/include/hexo/generator/insight.js +50 -0
- package/include/hexo/generator/manifest.js +23 -0
- package/include/hexo/generator/tags.js +12 -0
- package/include/hexo/helper/cdn.js +21 -0
- package/include/hexo/helper/page.js +27 -0
- package/include/hexo/view.js +40 -0
- package/include/register.js +11 -0
- package/include/util/common.js +33 -0
- package/languages/en.yml +47 -0
- package/languages/fr.yml +46 -0
- package/languages/ja.yml +46 -0
- package/languages/zh-CN.yml +47 -0
- package/languages/zh-TW.yml +47 -0
- package/layout/archive.jsx +118 -0
- package/layout/categories.jsx +137 -0
- package/layout/category.jsx +38 -0
- package/layout/comment/disqus.jsx +79 -0
- package/layout/comment/disqusjs.jsx +127 -0
- package/layout/comment/giscus.jsx +193 -0
- package/layout/comment/gitalk.jsx +141 -0
- package/layout/comment/twikoo.jsx +63 -0
- package/layout/comment/utterances.jsx +86 -0
- package/layout/comment/valine.jsx +143 -0
- package/layout/comment/waline.jsx +156 -0
- package/layout/common/article.jsx +131 -0
- package/layout/common/article_cover.jsx +33 -0
- package/layout/common/article_media.jsx +34 -0
- package/layout/common/comment.jsx +38 -0
- package/layout/common/footer.jsx +228 -0
- package/layout/common/head.jsx +242 -0
- package/layout/common/navbar.jsx +219 -0
- package/layout/common/plugins.jsx +39 -0
- package/layout/common/scripts.jsx +49 -0
- package/layout/common/search.jsx +22 -0
- package/layout/common/theme_selector.jsx +79 -0
- package/layout/common/toc.jsx +53 -0
- package/layout/index.jsx +29 -0
- package/layout/layout.jsx +34 -0
- package/layout/misc/article_licensing.jsx +114 -0
- package/layout/misc/meta.jsx +61 -0
- package/layout/misc/open_graph.jsx +164 -0
- package/layout/misc/paginator.jsx +90 -0
- package/layout/misc/structured_data.jsx +110 -0
- package/layout/misc/web_app.jsx +106 -0
- package/layout/page.jsx +12 -0
- package/layout/plugin/bing_webmaster.jsx +47 -0
- package/layout/plugin/busuanzi.jsx +40 -0
- package/layout/plugin/clarity.jsx +22 -0
- package/layout/plugin/cookie_consent.jsx +136 -0
- package/layout/plugin/google_analytics.jsx +66 -0
- package/layout/plugin/google_tag_mamager.jsx +41 -0
- package/layout/plugin/netlify.jsx +39 -0
- package/layout/plugin/pjax.jsx +20 -0
- package/layout/plugin/statcounter.jsx +69 -0
- package/layout/plugin/twitter_conversion_tracking.jsx +51 -0
- package/layout/post.jsx +16 -0
- package/layout/search/insight.jsx +53 -0
- package/layout/tag.jsx +29 -0
- package/layout/tags.jsx +55 -0
- package/package.json +42 -0
- package/scripts/index.js +1 -0
- package/source/css/callout_blocks.css +204 -0
- package/source/css/default.css +1590 -0
- package/source/css/font/woff2/Futura-Book.woff2 +0 -0
- package/source/css/font/woff2/Paris2024-Variable.woff2 +0 -0
- package/source/css/font/woff2/doto.woff2 +0 -0
- package/source/css/optional/chinese.css +17 -0
- package/source/css/responsive/desktop.css +164 -0
- package/source/css/responsive/mobile.css +46 -0
- package/source/css/responsive/tablet.css +46 -0
- package/source/css/responsive/touch.css +254 -0
- package/source/css/shiki/shiki.min.css +1 -0
- package/source/css/twikoo.css +2143 -0
- package/source/img/avatar.webp +0 -0
- package/source/img/background.webp +0 -0
- package/source/img/favicon.svg +6 -0
- package/source/img/logo.svg +9 -0
- package/source/img/og_image.png +0 -0
- package/source/js/busuanzi.js +46 -0
- package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.css +6 -0
- package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.js +1 -0
- package/source/js/host/iconify-icon/3.0.2/iconify-icon.min.js +12 -0
- package/source/js/host/medium-zoom/dist/medium-zoom.min.js +2 -0
- package/source/js/host/mermaid/mermaid.min.js +2811 -0
- package/source/js/host/pjax/0.2.8/pjax.min.js +1 -0
- package/source/js/host/twikoo/1.6.41/dist/twikoo.all.min.js +2 -0
- package/source/js/insight.js +330 -0
- package/source/js/instant-page.min.js +1 -0
- package/source/js/live2d_Asoul/Model/Ava/Ava.4096/texture_00.webp +0 -0
- package/source/js/live2d_Asoul/Model/Ava/Ava.moc3 +0 -0
- package/source/js/live2d_Asoul/Model/Ava/Ava.model3.json +323 -0
- package/source/js/live2d_Asoul/Model/Ava/Ava.physics3.json +1225 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_idle.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_shake01.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_shake02.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap01.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap02.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap03.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap04.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap05.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap06.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap07.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap08.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap09.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap10.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/motions/Ava_tap11.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Ava/raw.ex.json +16 -0
- package/source/js/live2d_Asoul/Model/Ava/raw.model3.json +321 -0
- package/source/js/live2d_Asoul/Model/Diana/Diana.4096/texture_00.webp +0 -0
- package/source/js/live2d_Asoul/Model/Diana/Diana.moc3 +0 -0
- package/source/js/live2d_Asoul/Model/Diana/Diana.model3.json +212 -0
- package/source/js/live2d_Asoul/Model/Diana/Diana.physics3.json +764 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_idle.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap01.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap02.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap03.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap04.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap05.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap06.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap07.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap08.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap09.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap10.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/motions/Diana_tap11.motion3.json +1 -0
- package/source/js/live2d_Asoul/Model/Diana/raw.ex.json +16 -0
- package/source/js/live2d_Asoul/Model/Diana/raw.model3.json +210 -0
- package/source/js/live2d_Asoul/TweenLite.js +12 -0
- package/source/js/live2d_Asoul/cubism4.min.js +2 -0
- package/source/js/live2d_Asoul/live2dcubismcore.min.js +9 -0
- package/source/js/live2d_Asoul/load.js +231 -0
- package/source/js/live2d_Asoul/pio.css +161 -0
- package/source/js/live2d_Asoul/pio.js +296 -0
- package/source/js/live2d_Asoul/pio_sdk4.js +149 -0
- package/source/js/live2d_Asoul/pixi.min.js +9 -0
- package/source/js/main.js +218 -0
- package/source/js/pjax.js +29 -0
- package/source/js/shiki/shiki.js +191 -0
- package/source/js/theme-selector.js +206 -0
- 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;
|
package/layout/index.jsx
ADDED
|
@@ -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 <meta> 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 <meta> 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: "…" }}
|
|
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
|
+
};
|