hexo-theme-gnix 8.0.0 → 10.0.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 +4 -2
- package/include/hexo/feed.js +6 -5
- package/include/hexo/filter.js +25 -1
- package/include/hexo/generator/archive.js +116 -0
- package/include/hexo/generator/home.js +64 -0
- package/include/hexo/generator/index.js +82 -0
- package/include/hexo/generator/md_generator.js +87 -0
- package/include/hexo/generator/page.js +55 -0
- package/include/hexo/generator/tag.js +84 -0
- package/include/hexo/helper.js +38 -0
- package/include/hexo/i18n.js +183 -0
- package/include/util/article_font.js +132 -0
- package/include/util/i18n.js +280 -0
- package/include/util/theme.js +84 -0
- package/languages/en.yml +28 -0
- package/languages/zh-CN.yml +28 -0
- package/layout/archive.jsx +131 -127
- package/layout/common/article.jsx +316 -34
- package/layout/common/article_info.jsx +339 -0
- package/layout/common/article_media.jsx +11 -4
- package/layout/common/comment.jsx +15 -7
- package/layout/common/footer.jsx +6 -5
- package/layout/common/head.jsx +122 -33
- package/layout/common/navbar.jsx +195 -65
- package/layout/common/theme_selector.jsx +16 -14
- package/layout/layout.jsx +43 -5
- package/layout/misc/open_graph.jsx +162 -66
- package/layout/misc/paginator.jsx +2 -8
- package/layout/plugin/cookie_consent.jsx +252 -53
- package/layout/plugin/swup.jsx +1 -1
- package/layout/search/insight.jsx +1 -1
- package/layout/tag.jsx +3 -2
- package/layout/tags.jsx +81 -73
- package/package.json +5 -5
- package/scripts/index.js +1 -0
- package/source/css/archive.css +225 -180
- package/source/css/default.css +1223 -126
- package/source/css/responsive.css +426 -0
- package/source/css/shiki/shiki.css +12 -2081
- package/source/css/tags.css +183 -0
- package/source/css/twikoo.css +1053 -1049
- package/source/img/favicon.svg +1 -6
- package/source/img/og_image.webp +0 -0
- package/source/js/article-font-utils.js +99 -0
- package/source/js/busuanzi.js +91 -24
- package/source/js/components/chat.js +169 -50
- package/source/js/components/image-carousel.js +152 -108
- package/source/js/components/sidenote.js +210 -0
- package/source/js/components/text-image-section.js +78 -90
- package/source/js/components/theme-stacked.js +65 -33
- package/source/js/components/tree.js +30 -16
- package/source/js/decrypt.js +7 -2
- package/source/js/main.js +428 -5
- package/source/js/swup.js +39 -0
- package/source/js/theme-selector.js +26 -16
- package/include/hexo/generator.js +0 -53
- package/layout/misc/article_licensing.jsx +0 -99
- package/source/css/responsive/desktop.css +0 -36
- package/source/css/responsive/mobile.css +0 -38
- package/source/css/responsive/tablet.css +0 -43
- package/source/css/responsive/touch.css +0 -155
- package/source/img/logo.svg +0 -9
- package/source/js/archive-breadcrumb.js +0 -132
- package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.css +0 -6
- package/source/js/host/cookieconsent/3.1.1/build/cookieconsent.min.js +0 -1
- package/source/js/swup.bundle.js +0 -1
package/languages/en.yml
CHANGED
|
@@ -14,6 +14,13 @@ common:
|
|
|
14
14
|
page:
|
|
15
15
|
one: "Page"
|
|
16
16
|
other: "Pages"
|
|
17
|
+
navbar:
|
|
18
|
+
language_switch: "Switch to %s"
|
|
19
|
+
language_unavailable_title: "Translation unavailable"
|
|
20
|
+
language_unavailable: "This page is not available in %s yet."
|
|
21
|
+
language_stay: "Stay here"
|
|
22
|
+
language_home: "Go to home"
|
|
23
|
+
language_close: "Close"
|
|
17
24
|
article:
|
|
18
25
|
created_at: "Posted %s"
|
|
19
26
|
more: "Read more"
|
|
@@ -22,6 +29,27 @@ article:
|
|
|
22
29
|
word_count:
|
|
23
30
|
one: "About %d word"
|
|
24
31
|
other: "About %d words"
|
|
32
|
+
font_settings: "Font Settings"
|
|
33
|
+
article_info: "Article Info"
|
|
34
|
+
close: "Close"
|
|
35
|
+
created_time: "Created"
|
|
36
|
+
updated_time: "Updated"
|
|
37
|
+
license: "License"
|
|
38
|
+
location: "Location"
|
|
39
|
+
author: "Author"
|
|
40
|
+
article_title: "Title"
|
|
41
|
+
url: "URL"
|
|
42
|
+
markdown_source: "Markdown source"
|
|
43
|
+
original_work: "Original work"
|
|
44
|
+
translation_note: "Translation note"
|
|
45
|
+
translation_reviewed: "manually reviewed"
|
|
46
|
+
translation_not_reviewed: "not manually reviewed"
|
|
47
|
+
yes: "Yes"
|
|
48
|
+
no: "No"
|
|
49
|
+
translation_methods:
|
|
50
|
+
llm: "LLM translation"
|
|
51
|
+
machine: "Machine translation"
|
|
52
|
+
human: "Human translation"
|
|
25
53
|
licensing:
|
|
26
54
|
author: "Author"
|
|
27
55
|
created_at: "Posted on"
|
package/languages/zh-CN.yml
CHANGED
|
@@ -14,6 +14,13 @@ common:
|
|
|
14
14
|
page:
|
|
15
15
|
one: "PAGE"
|
|
16
16
|
other: "PAGE"
|
|
17
|
+
navbar:
|
|
18
|
+
language_switch: "切换到%s"
|
|
19
|
+
language_unavailable_title: "暂无对应翻译"
|
|
20
|
+
language_unavailable: "此页面暂未提供%s版本。"
|
|
21
|
+
language_stay: "留在此页"
|
|
22
|
+
language_home: "回到主页"
|
|
23
|
+
language_close: "关闭"
|
|
17
24
|
article:
|
|
18
25
|
created_at: "%s"
|
|
19
26
|
more: "Read"
|
|
@@ -22,6 +29,27 @@ article:
|
|
|
22
29
|
word_count:
|
|
23
30
|
one: "约%d字"
|
|
24
31
|
other: "约%d字"
|
|
32
|
+
font_settings: "字体设置"
|
|
33
|
+
article_info: "文章信息"
|
|
34
|
+
close: "关闭"
|
|
35
|
+
created_time: "创建时间"
|
|
36
|
+
updated_time: "更新时间"
|
|
37
|
+
license: "许可证"
|
|
38
|
+
location: "位置"
|
|
39
|
+
author: "作者"
|
|
40
|
+
article_title: "标题"
|
|
41
|
+
url: "链接"
|
|
42
|
+
markdown_source: "Markdown 源码"
|
|
43
|
+
original_work: "原语言作品"
|
|
44
|
+
translation_note: "翻译说明"
|
|
45
|
+
translation_reviewed: "已人工审阅"
|
|
46
|
+
translation_not_reviewed: "未人工审阅"
|
|
47
|
+
yes: "是"
|
|
48
|
+
no: "否"
|
|
49
|
+
translation_methods:
|
|
50
|
+
llm: "LLM 翻译"
|
|
51
|
+
machine: "机器翻译"
|
|
52
|
+
human: "人工翻译"
|
|
25
53
|
licensing:
|
|
26
54
|
author: "Author"
|
|
27
55
|
created_at: "Posted"
|
package/layout/archive.jsx
CHANGED
|
@@ -1,160 +1,164 @@
|
|
|
1
1
|
const { Component, Fragment, isValidDate, parseISO, dateFormatters } = require("../include/util/common");
|
|
2
|
+
const { filterByLanguage } = require("../include/util/i18n");
|
|
2
3
|
const ArticleMedia = require("./common/article_media");
|
|
3
4
|
|
|
5
|
+
function collectPosts(collection) {
|
|
6
|
+
const posts = [];
|
|
7
|
+
if (collection?.each) {
|
|
8
|
+
collection.each((post) => posts.push(post));
|
|
9
|
+
} else if (Array.isArray(collection)) {
|
|
10
|
+
posts.push(...collection);
|
|
11
|
+
}
|
|
12
|
+
return posts;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getSeason(month) {
|
|
16
|
+
if (month >= 2 && month <= 4) return "Spring";
|
|
17
|
+
if (month >= 5 && month <= 7) return "Summer";
|
|
18
|
+
if (month >= 8 && month <= 10) return "Autumn";
|
|
19
|
+
return "Winter";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getArchiveRangeLabel(year, month = null, season = null) {
|
|
23
|
+
if (!year) return "All Posts";
|
|
24
|
+
if (month) {
|
|
25
|
+
const time = parseISO(`${year}-${String(month).padStart(2, "0")}-01T00:00:00.000Z`);
|
|
26
|
+
return isValidDate(time) ? `${dateFormatters.longMonth.format(time)} ${year}` : String(year);
|
|
27
|
+
}
|
|
28
|
+
return season ? `${season} ${year}` : String(year);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function groupPostsBySeason(posts) {
|
|
32
|
+
return posts.reduce((groups, post) => {
|
|
33
|
+
const year = post.date.year();
|
|
34
|
+
const season = getSeason(post.date.month());
|
|
35
|
+
const lastGroup = groups[groups.length - 1];
|
|
36
|
+
|
|
37
|
+
if (lastGroup && lastGroup.year === year && lastGroup.season === season) {
|
|
38
|
+
lastGroup.posts.push(post);
|
|
39
|
+
} else {
|
|
40
|
+
groups.push({ year, season, posts: [post] });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return groups;
|
|
44
|
+
}, []);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function collectArchiveYears(posts) {
|
|
48
|
+
return Array.from(new Set(posts.map((post) => post.date.year()))).sort((a, b) => b - a);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getPostDateParts(postDate, dateXml, date) {
|
|
52
|
+
const xml = dateXml(postDate);
|
|
53
|
+
const parsed = parseISO(xml);
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
label: isValidDate(parsed) ? dateFormatters.shortDay.format(parsed) : date(postDate),
|
|
57
|
+
xml,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
4
61
|
module.exports = class extends Component {
|
|
5
62
|
render() {
|
|
6
63
|
const { page, helper, site, config } = this.props;
|
|
7
64
|
const { url_for, date_xml, date } = helper;
|
|
8
|
-
|
|
9
|
-
function getSeason(month) {
|
|
10
|
-
if (month >= 2 && month <= 4) return "Spring";
|
|
11
|
-
if (month >= 5 && month <= 7) return "Summer";
|
|
12
|
-
if (month >= 8 && month <= 10) return "Autumn";
|
|
13
|
-
return "Winter";
|
|
14
|
-
}
|
|
65
|
+
const langKey = helper.language_key(page);
|
|
15
66
|
|
|
16
67
|
function renderArticleList(posts, year, month = null, sectionTitle = null, season = null) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
let title = `'${String(year).slice(-2)}`;
|
|
24
|
-
if (sectionTitle) {
|
|
25
|
-
title = sectionTitle;
|
|
26
|
-
} else if (month !== null && isValidDate(time)) {
|
|
27
|
-
title = dateFormatters.longMonth.format(time);
|
|
28
|
-
}
|
|
68
|
+
const title = sectionTitle || getArchiveRangeLabel(year, month, season);
|
|
69
|
+
const kicker = year ? String(year) : "Archive";
|
|
70
|
+
const marker = season ? season.toLowerCase() : "all";
|
|
71
|
+
const countLabel = posts.length === 1 ? "1 entry" : `${posts.length} entries`;
|
|
72
|
+
const sectionId = `archive-${kicker}-${marker}-${month || "all"}`;
|
|
29
73
|
|
|
30
74
|
return (
|
|
31
|
-
<
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
})}
|
|
75
|
+
<section class={["archive-group", marker].filter(Boolean).join(" ")} aria-labelledby={sectionId}>
|
|
76
|
+
<header class="archive-group__header">
|
|
77
|
+
<div>
|
|
78
|
+
<h2 id={sectionId} class="archive-group__title">
|
|
79
|
+
{title}
|
|
80
|
+
</h2>
|
|
38
81
|
</div>
|
|
82
|
+
<span class="archive-group__count">{countLabel}</span>
|
|
83
|
+
</header>
|
|
84
|
+
<div class="timeline">
|
|
85
|
+
{posts.map((post) => {
|
|
86
|
+
const postDate = getPostDateParts(post.date, date_xml, date);
|
|
87
|
+
return <ArticleMedia key={post.path} url={url_for(post.link || post.path)} title={post.title} date={postDate.label} dateXml={postDate.xml} />;
|
|
88
|
+
})}
|
|
39
89
|
</div>
|
|
40
|
-
</
|
|
90
|
+
</section>
|
|
41
91
|
);
|
|
42
92
|
}
|
|
43
93
|
|
|
94
|
+
const visiblePosts = collectPosts(page.posts);
|
|
95
|
+
const totalVisiblePosts = visiblePosts.length;
|
|
96
|
+
const latestPost = visiblePosts[0];
|
|
97
|
+
|
|
98
|
+
const allPostsSource = site?.posts ? filterByLanguage(site.posts.sort("date", -1), langKey, config) : page.posts;
|
|
99
|
+
const allPosts = collectPosts(allPostsSource);
|
|
100
|
+
const years = collectArchiveYears(allPosts);
|
|
44
101
|
let articleList;
|
|
45
102
|
if (!page.year) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const month = p.date.month(); // 0-11
|
|
50
|
-
const season = getSeason(month);
|
|
51
|
-
|
|
52
|
-
const lastGroup = groups[groups.length - 1];
|
|
53
|
-
if (lastGroup && lastGroup.year === year && lastGroup.season === season) {
|
|
54
|
-
lastGroup.posts.push(p);
|
|
55
|
-
} else {
|
|
56
|
-
groups.push({
|
|
57
|
-
year,
|
|
58
|
-
season,
|
|
59
|
-
posts: [p],
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
articleList = groups.map((group) => {
|
|
65
|
-
const title = `'${String(group.year).slice(-2)} ${group.season}`;
|
|
66
|
-
return renderArticleList(group.posts, group.year, null, title, group.season);
|
|
103
|
+
articleList = groupPostsBySeason(visiblePosts).map((group) => {
|
|
104
|
+
const title = getArchiveRangeLabel(group.year, null, group.season);
|
|
105
|
+
return <Fragment key={`${group.year}-${group.season}`}>{renderArticleList(group.posts, group.year, null, title, group.season)}</Fragment>;
|
|
67
106
|
});
|
|
68
107
|
} else {
|
|
69
108
|
const season = page.month ? getSeason(page.month - 1) : null;
|
|
70
|
-
articleList = renderArticleList(
|
|
109
|
+
articleList = renderArticleList(visiblePosts, page.year, page.month, null, season);
|
|
71
110
|
}
|
|
72
111
|
|
|
73
112
|
const archiveDir = config?.archive_dir || "archives";
|
|
74
|
-
const archiveBasePath =
|
|
113
|
+
const archiveBasePath = helper.localized_url_for(`/${archiveDir}/`, langKey);
|
|
75
114
|
const currentYear = page.year ? Number(page.year) : null;
|
|
76
115
|
const currentMonth = page.month ? Number(page.month) : null;
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
allPosts.each((p) => {
|
|
82
|
-
const y = p.date.year();
|
|
83
|
-
const m = p.date.month() + 1;
|
|
84
|
-
const existing = yearToMonths.get(y);
|
|
85
|
-
if (existing) {
|
|
86
|
-
existing.add(m);
|
|
87
|
-
} else {
|
|
88
|
-
yearToMonths.set(y, new Set([m]));
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
const years = Array.from(yearToMonths.keys()).sort((a, b) => b - a);
|
|
93
|
-
const monthsForCurrentYear = currentYear && yearToMonths.get(currentYear) ? Array.from(yearToMonths.get(currentYear)).sort((a, b) => a - b) : [];
|
|
116
|
+
const activeScope = getArchiveRangeLabel(currentYear, currentMonth);
|
|
117
|
+
const yearCountLabel = years.length === 1 ? "1 year" : `${years.length} years`;
|
|
118
|
+
const scopeSummaryLabel = currentYear ? `from ${activeScope}` : `across ${yearCountLabel}`;
|
|
119
|
+
const latestLabel = latestPost ? date(latestPost.date) : "No posts yet";
|
|
94
120
|
|
|
95
121
|
return (
|
|
96
122
|
<Fragment>
|
|
97
|
-
<
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
</span>
|
|
107
|
-
<span class="archive-breadcrumb__cmd" style="padding: 0">
|
|
108
|
-
/
|
|
109
|
-
</span>
|
|
110
|
-
<span class="archive-breadcrumb__picker" data-picker="year">
|
|
111
|
-
<button type="button" class="archive-breadcrumb__trigger" aria-haspopup="listbox" aria-expanded="false" aria-label="Select year">
|
|
112
|
-
<span data-label="year">{currentYear ? String(currentYear) : "*"}</span>
|
|
113
|
-
</button>
|
|
114
|
-
<div class="archive-breadcrumb__menu" role="listbox" tabindex="-1" data-menu="year">
|
|
115
|
-
<button type="button" class="archive-breadcrumb__option" role="option" data-href={archiveBasePath} aria-selected={!currentYear ? "true" : "false"}>
|
|
116
|
-
*
|
|
117
|
-
</button>
|
|
118
|
-
{years.map((y) => (
|
|
119
|
-
<button type="button" class="archive-breadcrumb__option" role="option" data-href={url_for(`/${archiveDir}/${y}/`)} aria-selected={currentYear === y ? "true" : "false"}>
|
|
120
|
-
{y}
|
|
121
|
-
</button>
|
|
122
|
-
))}
|
|
123
|
+
<link rel="stylesheet" href={url_for("/css/archive.css")} data-page-head />
|
|
124
|
+
<main class="archive-page">
|
|
125
|
+
<header class="archive-hero">
|
|
126
|
+
<div class="archive-hero__copy">
|
|
127
|
+
<p class="archive-eyebrow">Archive Index</p>
|
|
128
|
+
<h1>{activeScope}</h1>
|
|
129
|
+
<p class="archive-hero__summary">
|
|
130
|
+
{totalVisiblePosts ? `Browsing ${totalVisiblePosts} ${totalVisiblePosts === 1 ? "post" : "posts"} ${scopeSummaryLabel}.` : "No posts are available in this archive yet."}
|
|
131
|
+
</p>
|
|
123
132
|
</div>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
</div>
|
|
154
|
-
</span>
|
|
155
|
-
<span class="cursor">_</span>
|
|
156
|
-
</nav>
|
|
157
|
-
{articleList}
|
|
133
|
+
<dl class="archive-stats" aria-label="Archive summary">
|
|
134
|
+
<div>
|
|
135
|
+
<dt>Posts</dt>
|
|
136
|
+
<dd>{totalVisiblePosts}</dd>
|
|
137
|
+
</div>
|
|
138
|
+
<div>
|
|
139
|
+
<dt>Years</dt>
|
|
140
|
+
<dd>{years.length}</dd>
|
|
141
|
+
</div>
|
|
142
|
+
<div>
|
|
143
|
+
<dt>Latest</dt>
|
|
144
|
+
<dd>{latestLabel}</dd>
|
|
145
|
+
</div>
|
|
146
|
+
</dl>
|
|
147
|
+
</header>
|
|
148
|
+
|
|
149
|
+
<nav class="archive-years" aria-label="Archive years">
|
|
150
|
+
<a href={archiveBasePath} class={!currentYear ? "is-active" : null} aria-current={!currentYear ? "page" : null}>
|
|
151
|
+
All
|
|
152
|
+
</a>
|
|
153
|
+
{years.map((year) => (
|
|
154
|
+
<a key={year} href={helper.localized_url_for(`/${archiveDir}/${year}/`, langKey)} class={currentYear === year ? "is-active" : null} aria-current={currentYear === year ? "page" : null}>
|
|
155
|
+
{year}
|
|
156
|
+
</a>
|
|
157
|
+
))}
|
|
158
|
+
</nav>
|
|
159
|
+
|
|
160
|
+
<div class="archive-stack">{articleList}</div>
|
|
161
|
+
</main>
|
|
158
162
|
</Fragment>
|
|
159
163
|
);
|
|
160
164
|
}
|