hexo-theme-gnix 13.0.0 → 14.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/include/hexo/generator/archive.js +14 -1
- package/include/hexo/generator/index.js +0 -5
- package/include/hexo/generator/page.js +18 -4
- package/include/hexo/generator/tag.js +1 -1
- package/include/hexo/helper.js +0 -4
- package/include/hexo/i18n.js +21 -136
- package/include/util/i18n.js +92 -106
- package/layout/archive.jsx +155 -78
- package/layout/common/article.jsx +82 -121
- package/layout/common/article_cover.jsx +3 -3
- package/layout/common/article_media.jsx +9 -2
- package/layout/common/footer.jsx +4 -1
- package/layout/common/navbar.jsx +15 -12
- package/layout/common/scripts.jsx +1 -1
- package/layout/layout.jsx +12 -11
- package/layout/tag.jsx +3 -70
- package/layout/tags.jsx +26 -23
- package/package.json +4 -4
- package/source/css/archive.css +287 -166
- package/source/css/default.css +102 -80
- package/source/css/responsive.css +1 -45
- package/source/css/tags.css +53 -59
- package/source/js/components/archive-popup.js +313 -0
- package/source/js/components/friends-list.js +0 -1
- package/source/js/main.js +14 -13
- package/include/hexo/generator/home.js +0 -64
- package/layout/index.jsx +0 -19
- package/layout/misc/paginator.jsx +0 -69
- package/source/js/host/iconify-icon/3.0.2/iconify-icon.min.js +0 -12
package/layout/archive.jsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const { Component, Fragment, isValidDate, parseISO, dateFormatters } = require("../include/util/common");
|
|
2
|
-
const { filterByLanguage } = require("../include/util/i18n");
|
|
3
2
|
const ArticleMedia = require("./common/article_media");
|
|
4
3
|
|
|
5
4
|
function collectPosts(collection) {
|
|
@@ -12,6 +11,14 @@ function collectPosts(collection) {
|
|
|
12
11
|
return posts;
|
|
13
12
|
}
|
|
14
13
|
|
|
14
|
+
function estimateReadMinutes(content) {
|
|
15
|
+
if (typeof content !== "string" || !content) return 0;
|
|
16
|
+
const stripped = content.replace(/<\/?[a-z][^>]*>/gi, "").trim();
|
|
17
|
+
if (!stripped) return 0;
|
|
18
|
+
const tokens = stripped.match(/[ÿ-]|[a-zA-Z]+/g);
|
|
19
|
+
return tokens ? Math.max(1, Math.ceil(tokens.length / 200)) : 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
function getSeason(month) {
|
|
16
23
|
if (month >= 2 && month <= 4) return "Spring";
|
|
17
24
|
if (month >= 5 && month <= 7) return "Summer";
|
|
@@ -44,6 +51,47 @@ function groupPostsBySeason(posts) {
|
|
|
44
51
|
}, []);
|
|
45
52
|
}
|
|
46
53
|
|
|
54
|
+
function groupSeasonGroupsByYear(seasonGroups) {
|
|
55
|
+
const years = [];
|
|
56
|
+
for (const group of seasonGroups) {
|
|
57
|
+
const last = years[years.length - 1];
|
|
58
|
+
if (last && last.year === group.year) {
|
|
59
|
+
last.groups.push(group);
|
|
60
|
+
last.total += group.posts.length;
|
|
61
|
+
} else {
|
|
62
|
+
years.push({ year: group.year, total: group.posts.length, groups: [group] });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return years;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function toRoman(num) {
|
|
69
|
+
const map = [
|
|
70
|
+
[1000, "M"],
|
|
71
|
+
[900, "CM"],
|
|
72
|
+
[500, "D"],
|
|
73
|
+
[400, "CD"],
|
|
74
|
+
[100, "C"],
|
|
75
|
+
[90, "XC"],
|
|
76
|
+
[50, "L"],
|
|
77
|
+
[40, "XL"],
|
|
78
|
+
[10, "X"],
|
|
79
|
+
[9, "IX"],
|
|
80
|
+
[5, "V"],
|
|
81
|
+
[4, "IV"],
|
|
82
|
+
[1, "I"],
|
|
83
|
+
];
|
|
84
|
+
let n = num;
|
|
85
|
+
let out = "";
|
|
86
|
+
for (const [value, sym] of map) {
|
|
87
|
+
while (n >= value) {
|
|
88
|
+
out += sym;
|
|
89
|
+
n -= value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
|
|
47
95
|
function collectArchiveYears(posts) {
|
|
48
96
|
return Array.from(new Set(posts.map((post) => post.date.year()))).sort((a, b) => b - a);
|
|
49
97
|
}
|
|
@@ -58,104 +106,133 @@ function getPostDateParts(postDate, dateXml, date) {
|
|
|
58
106
|
};
|
|
59
107
|
}
|
|
60
108
|
|
|
109
|
+
function renderSeasonGroup({ posts, year, season, month, sectionTitle, url_for, date_xml, date }) {
|
|
110
|
+
const title = sectionTitle || getArchiveRangeLabel(year, month, season);
|
|
111
|
+
const kicker = year ? String(year) : "Archive";
|
|
112
|
+
const marker = season ? season.toLowerCase() : "all";
|
|
113
|
+
const sectionId = `archive-${kicker}-${marker}-${month || "all"}`;
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<section class={["archive-group", marker].filter(Boolean).join(" ")} aria-labelledby={sectionId}>
|
|
117
|
+
<header class="archive-group__header">
|
|
118
|
+
<h2 id={sectionId} class="archive-group__title">
|
|
119
|
+
{title}
|
|
120
|
+
</h2>
|
|
121
|
+
<span class="archive-group__count">{String(posts.length).padStart(2, "0")}</span>
|
|
122
|
+
</header>
|
|
123
|
+
<div class="timeline">
|
|
124
|
+
{posts.map((post) => {
|
|
125
|
+
const postDate = getPostDateParts(post.date, date_xml, date);
|
|
126
|
+
const readMinutes = estimateReadMinutes(post._content);
|
|
127
|
+
return (
|
|
128
|
+
<ArticleMedia
|
|
129
|
+
key={post.path}
|
|
130
|
+
url={url_for(post.link || post.path)}
|
|
131
|
+
title={post.title}
|
|
132
|
+
date={postDate.label}
|
|
133
|
+
dateXml={postDate.xml}
|
|
134
|
+
excerpt={post.excerpt || null}
|
|
135
|
+
readTime={readMinutes ? `${readMinutes} min` : null}
|
|
136
|
+
/>
|
|
137
|
+
);
|
|
138
|
+
})}
|
|
139
|
+
</div>
|
|
140
|
+
</section>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
61
144
|
module.exports = class extends Component {
|
|
62
145
|
render() {
|
|
63
|
-
const { page, helper
|
|
146
|
+
const { page, helper } = this.props;
|
|
64
147
|
const { url_for, date_xml, date } = helper;
|
|
65
|
-
const langKey = helper.language_key(page);
|
|
66
|
-
|
|
67
|
-
function renderArticleList(posts, year, month = null, sectionTitle = null, season = null) {
|
|
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"}`;
|
|
73
|
-
|
|
74
|
-
return (
|
|
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>
|
|
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
|
-
})}
|
|
89
|
-
</div>
|
|
90
|
-
</section>
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
148
|
|
|
94
149
|
const visiblePosts = collectPosts(page.posts);
|
|
95
150
|
const totalVisiblePosts = visiblePosts.length;
|
|
96
|
-
const latestPost = visiblePosts[0];
|
|
97
151
|
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const
|
|
152
|
+
const years = collectArchiveYears(visiblePosts);
|
|
153
|
+
|
|
154
|
+
const currentYear = page.year ? Number(page.year) : null;
|
|
155
|
+
const currentMonth = page.month ? Number(page.month) : null;
|
|
156
|
+
const isTagPage = Boolean(page.tag);
|
|
157
|
+
const entriesLabel = `${totalVisiblePosts} ${totalVisiblePosts === 1 ? "entry" : "entries"}`;
|
|
158
|
+
|
|
101
159
|
let articleList;
|
|
102
160
|
if (!page.year) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
161
|
+
const seasonGroups = groupPostsBySeason(visiblePosts);
|
|
162
|
+
const yearBlocks = groupSeasonGroupsByYear(seasonGroups);
|
|
163
|
+
articleList = yearBlocks.map((block) => (
|
|
164
|
+
<Fragment key={block.year}>
|
|
165
|
+
<div class="archive-era" id={`archive-year-${block.year}`}>
|
|
166
|
+
<span class="archive-era__roman">{toRoman(block.year)}</span>
|
|
167
|
+
<span class="archive-era__year" aria-hidden="true">
|
|
168
|
+
{" "}
|
|
169
|
+
{block.year}{" "}
|
|
170
|
+
</span>
|
|
171
|
+
</div>
|
|
172
|
+
{block.groups.map((group) => (
|
|
173
|
+
<Fragment key={`${group.year}-${group.season}`}>
|
|
174
|
+
{renderSeasonGroup({
|
|
175
|
+
posts: group.posts,
|
|
176
|
+
year: group.year,
|
|
177
|
+
season: group.season,
|
|
178
|
+
sectionTitle: group.season,
|
|
179
|
+
url_for,
|
|
180
|
+
date_xml,
|
|
181
|
+
date,
|
|
182
|
+
})}
|
|
183
|
+
</Fragment>
|
|
184
|
+
))}
|
|
185
|
+
</Fragment>
|
|
186
|
+
));
|
|
107
187
|
} else {
|
|
108
188
|
const season = page.month ? getSeason(page.month - 1) : null;
|
|
109
|
-
articleList =
|
|
189
|
+
articleList = renderSeasonGroup({
|
|
190
|
+
posts: visiblePosts,
|
|
191
|
+
year: page.year,
|
|
192
|
+
season,
|
|
193
|
+
month: page.month,
|
|
194
|
+
url_for,
|
|
195
|
+
date_xml,
|
|
196
|
+
date,
|
|
197
|
+
});
|
|
110
198
|
}
|
|
111
199
|
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
const currentYear = page.year ? Number(page.year) : null;
|
|
115
|
-
const currentMonth = page.month ? Number(page.month) : null;
|
|
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";
|
|
200
|
+
const heroTitle = isTagPage ? page.tag : currentYear ? getArchiveRangeLabel(currentYear, currentMonth) : "Index";
|
|
201
|
+
const heroKind = isTagPage ? "Tag" : currentYear ? "Archive" : "Volume";
|
|
120
202
|
|
|
121
203
|
return (
|
|
122
204
|
<Fragment>
|
|
123
205
|
<link rel="stylesheet" href={url_for("/css/archive.css")} data-page-head />
|
|
124
206
|
<main class="archive-page">
|
|
125
207
|
<header class="archive-hero">
|
|
126
|
-
<
|
|
127
|
-
<
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
</
|
|
132
|
-
</
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
</
|
|
138
|
-
|
|
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>
|
|
208
|
+
<p class="archive-hero__eyebrow">
|
|
209
|
+
<span>{heroKind}</span>
|
|
210
|
+
<span class="archive-hero__sep" aria-hidden="true">
|
|
211
|
+
·
|
|
212
|
+
</span>
|
|
213
|
+
<span class="archive-hero__count">{entriesLabel}</span>
|
|
214
|
+
</p>
|
|
215
|
+
<h1 class="archive-hero__title">{heroTitle}</h1>
|
|
216
|
+
{years.length > 0 && (
|
|
217
|
+
<span class="archive-hero__roman" aria-hidden="true">
|
|
218
|
+
{years.length === 1 ? toRoman(years[0]) : `${toRoman(years[years.length - 1])}–${toRoman(years[0])}`}
|
|
219
|
+
</span>
|
|
220
|
+
)}
|
|
147
221
|
</header>
|
|
148
222
|
|
|
149
|
-
|
|
150
|
-
<
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
223
|
+
{!page.year && years.length > 1 && (
|
|
224
|
+
<aside class="archive-rail" aria-label="Jump to year">
|
|
225
|
+
<ol class="archive-rail__list">
|
|
226
|
+
{years.map((year) => (
|
|
227
|
+
<li key={year} class="archive-rail__item">
|
|
228
|
+
<a href={`#archive-year-${year}`} class="archive-rail__link">
|
|
229
|
+
<span class="archive-rail__year">{year}</span>
|
|
230
|
+
</a>
|
|
231
|
+
</li>
|
|
232
|
+
))}
|
|
233
|
+
</ol>
|
|
234
|
+
</aside>
|
|
235
|
+
)}
|
|
159
236
|
|
|
160
237
|
<div class="archive-stack">{articleList}</div>
|
|
161
238
|
</main>
|
|
@@ -17,15 +17,14 @@ function getWordCount(content) {
|
|
|
17
17
|
|
|
18
18
|
module.exports = class extends Component {
|
|
19
19
|
render() {
|
|
20
|
-
|
|
21
|
-
const { config, helper, page, index } = this.props;
|
|
20
|
+
const { config, helper, page } = this.props;
|
|
22
21
|
|
|
23
22
|
const { url_for } = helper;
|
|
24
23
|
|
|
25
24
|
const cover = page.cover ? url_for(page.cover) : null;
|
|
26
25
|
const wordCount = getWordCount(page._content);
|
|
27
26
|
const readTime = Math.ceil(wordCount / 200); // 假设每分钟阅读200字
|
|
28
|
-
const hasComment =
|
|
27
|
+
const hasComment = config.comment && typeof config.comment.type === "string";
|
|
29
28
|
const translatedCommentsLabel = helper.__("article.comments");
|
|
30
29
|
const commentsLabel = translatedCommentsLabel === "article.comments" ? "Comments" : translatedCommentsLabel;
|
|
31
30
|
|
|
@@ -34,7 +33,7 @@ module.exports = class extends Component {
|
|
|
34
33
|
{/* Main content */}
|
|
35
34
|
<div class="card">
|
|
36
35
|
{/* Cover image */}
|
|
37
|
-
{cover ? <ArticleCover page={page} cover={cover}
|
|
36
|
+
{cover ? <ArticleCover page={page} cover={cover} helper={helper} /> : null}
|
|
38
37
|
<article class={`card-content article${"direction" in page ? ` ${page.direction}` : ""}`}>
|
|
39
38
|
{/* Metadata - Medium style */}
|
|
40
39
|
{page.layout !== "page" ? (
|
|
@@ -45,102 +44,41 @@ module.exports = class extends Component {
|
|
|
45
44
|
{dateFormatters.shortDay.format(page.date)}
|
|
46
45
|
</time>
|
|
47
46
|
)}
|
|
48
|
-
{page.date &&
|
|
47
|
+
{page.date && <span class="meta-separator">·</span>}
|
|
49
48
|
{wordCount > 0 && <span class="article-reading-time">{readTime} min</span>}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}}
|
|
59
|
-
></span>
|
|
60
|
-
</Fragment>
|
|
61
|
-
)}
|
|
49
|
+
<span class="meta-separator">·</span>
|
|
50
|
+
<span
|
|
51
|
+
class="article-visit-count"
|
|
52
|
+
data-flag-title={page.title}
|
|
53
|
+
dangerouslySetInnerHTML={{
|
|
54
|
+
__html: '<span id="busuanzi_page_pv"></span> PV',
|
|
55
|
+
}}
|
|
56
|
+
></span>
|
|
62
57
|
</div>
|
|
63
58
|
</div>
|
|
64
59
|
) : null}
|
|
65
60
|
|
|
66
61
|
{/* Title */}
|
|
67
|
-
{page.title !== ""
|
|
68
|
-
<h2 class="article-title">
|
|
69
|
-
<a href={url_for(page.link || page.path)}>{page.title}</a>
|
|
70
|
-
</h2>
|
|
71
|
-
) : null}
|
|
72
|
-
{page.title !== "" && !index ? <h1 class="article-title">{page.title}</h1> : null}
|
|
62
|
+
{page.title !== "" ? <h1 class="article-title">{page.title}</h1> : null}
|
|
73
63
|
|
|
74
|
-
{
|
|
64
|
+
{page.excerpt && <div class="article-excerpt" dangerouslySetInnerHTML={{ __html: page.excerpt }}></div>}
|
|
75
65
|
|
|
76
|
-
|
|
77
|
-
<div
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<a class="article-tag" rel="tag" href={helper.localized_tag_url(tag, helper.language_key(page))}>
|
|
93
|
-
{tag.name}
|
|
94
|
-
</a>
|
|
95
|
-
</Fragment>
|
|
96
|
-
))
|
|
97
|
-
: null}
|
|
98
|
-
</div>
|
|
99
|
-
<div class="article-title-actions">
|
|
100
|
-
{hasComment && (
|
|
101
|
-
<button type="button" class="article-action-btn" popovertarget="article-comment-popover" aria-label={commentsLabel} title={commentsLabel}>
|
|
102
|
-
<svg
|
|
103
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
104
|
-
width="24"
|
|
105
|
-
height="24"
|
|
106
|
-
viewBox="0 0 24 24"
|
|
107
|
-
fill="none"
|
|
108
|
-
stroke="currentColor"
|
|
109
|
-
stroke-width="2"
|
|
110
|
-
stroke-linecap="round"
|
|
111
|
-
stroke-linejoin="round"
|
|
112
|
-
role="img"
|
|
113
|
-
aria-label={commentsLabel}
|
|
114
|
-
>
|
|
115
|
-
<title>{commentsLabel}</title>
|
|
116
|
-
<path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719" />
|
|
117
|
-
<path d="M8 12h.01" />
|
|
118
|
-
<path d="M12 12h.01" />
|
|
119
|
-
<path d="M16 12h.01" />
|
|
120
|
-
</svg>
|
|
121
|
-
</button>
|
|
122
|
-
)}
|
|
123
|
-
<button type="button" class="article-action-btn" popovertarget="article-font-settings" aria-label={helper.__("article.font_settings")} title={helper.__("article.font_settings")}>
|
|
124
|
-
<svg
|
|
125
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
126
|
-
width="24"
|
|
127
|
-
height="24"
|
|
128
|
-
viewBox="0 0 24 24"
|
|
129
|
-
fill="none"
|
|
130
|
-
stroke="currentColor"
|
|
131
|
-
stroke-width="2"
|
|
132
|
-
stroke-linecap="round"
|
|
133
|
-
stroke-linejoin="round"
|
|
134
|
-
role="img"
|
|
135
|
-
aria-label={helper.__("article.font_settings")}
|
|
136
|
-
>
|
|
137
|
-
<title>{helper.__("article.font_settings")}</title>
|
|
138
|
-
<path d="M12 4v16" />
|
|
139
|
-
<path d="M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2" />
|
|
140
|
-
<path d="M9 20h6" />
|
|
141
|
-
</svg>
|
|
142
|
-
</button>
|
|
143
|
-
<button type="button" class="article-action-btn" popovertarget="article-info-popover" aria-label={helper.__("article.article_info")} title={helper.__("article.article_info")}>
|
|
66
|
+
<div class="article-meta-bar">
|
|
67
|
+
<div class="article-tags">
|
|
68
|
+
{page.tags?.length
|
|
69
|
+
? page.tags.map((tag, i) => (
|
|
70
|
+
<Fragment>
|
|
71
|
+
{i > 0 && <span class="meta-separator">·</span>}
|
|
72
|
+
<a class="article-tag" rel="tag" href={helper.localized_tag_url(tag, helper.language_key(page))}>
|
|
73
|
+
{tag.name}
|
|
74
|
+
</a>
|
|
75
|
+
</Fragment>
|
|
76
|
+
))
|
|
77
|
+
: null}
|
|
78
|
+
</div>
|
|
79
|
+
<div class="article-title-actions">
|
|
80
|
+
{hasComment && (
|
|
81
|
+
<button type="button" class="article-action-btn" popovertarget="article-comment-popover" aria-label={commentsLabel} title={commentsLabel}>
|
|
144
82
|
<svg
|
|
145
83
|
xmlns="http://www.w3.org/2000/svg"
|
|
146
84
|
width="24"
|
|
@@ -152,40 +90,64 @@ module.exports = class extends Component {
|
|
|
152
90
|
stroke-linecap="round"
|
|
153
91
|
stroke-linejoin="round"
|
|
154
92
|
role="img"
|
|
155
|
-
aria-label={
|
|
93
|
+
aria-label={commentsLabel}
|
|
156
94
|
>
|
|
157
|
-
<title>{
|
|
158
|
-
<
|
|
159
|
-
<path d="
|
|
160
|
-
<path d="M12
|
|
95
|
+
<title>{commentsLabel}</title>
|
|
96
|
+
<path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719" />
|
|
97
|
+
<path d="M8 12h.01" />
|
|
98
|
+
<path d="M12 12h.01" />
|
|
99
|
+
<path d="M16 12h.01" />
|
|
161
100
|
</svg>
|
|
162
101
|
</button>
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
102
|
+
)}
|
|
103
|
+
<button type="button" class="article-action-btn" popovertarget="article-font-settings" aria-label={helper.__("article.font_settings")} title={helper.__("article.font_settings")}>
|
|
104
|
+
<svg
|
|
105
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
106
|
+
width="24"
|
|
107
|
+
height="24"
|
|
108
|
+
viewBox="0 0 24 24"
|
|
109
|
+
fill="none"
|
|
110
|
+
stroke="currentColor"
|
|
111
|
+
stroke-width="2"
|
|
112
|
+
stroke-linecap="round"
|
|
113
|
+
stroke-linejoin="round"
|
|
114
|
+
role="img"
|
|
115
|
+
aria-label={helper.__("article.font_settings")}
|
|
116
|
+
>
|
|
117
|
+
<title>{helper.__("article.font_settings")}</title>
|
|
118
|
+
<path d="M12 4v16" />
|
|
119
|
+
<path d="M4 7V5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2" />
|
|
120
|
+
<path d="M9 20h6" />
|
|
121
|
+
</svg>
|
|
122
|
+
</button>
|
|
123
|
+
<button type="button" class="article-action-btn" popovertarget="article-info-popover" aria-label={helper.__("article.article_info")} title={helper.__("article.article_info")}>
|
|
124
|
+
<svg
|
|
125
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
126
|
+
width="24"
|
|
127
|
+
height="24"
|
|
128
|
+
viewBox="0 0 24 24"
|
|
129
|
+
fill="none"
|
|
130
|
+
stroke="currentColor"
|
|
131
|
+
stroke-width="2"
|
|
132
|
+
stroke-linecap="round"
|
|
133
|
+
stroke-linejoin="round"
|
|
134
|
+
role="img"
|
|
135
|
+
aria-label={helper.__("article.article_info")}
|
|
136
|
+
>
|
|
137
|
+
<title>{helper.__("article.article_info")}</title>
|
|
138
|
+
<circle cx="12" cy="12" r="10" />
|
|
139
|
+
<path d="M12 16v-4" />
|
|
140
|
+
<path d="M12 8h.01" />
|
|
141
|
+
</svg>
|
|
142
|
+
</button>
|
|
181
143
|
</div>
|
|
182
|
-
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div class="content" dangerouslySetInnerHTML={{ __html: page.content }}></div>
|
|
183
147
|
|
|
184
|
-
{!index && page.excerpt && <div class="content" dangerouslySetInnerHTML={{ __html: page.content }}></div>}
|
|
185
148
|
</article>
|
|
186
149
|
|
|
187
|
-
|
|
188
|
-
<div id="article-font-settings" popover="auto" class="article-popover article-font-popover">
|
|
150
|
+
<div id="article-font-settings" popover="auto" class="article-popover article-font-popover">
|
|
189
151
|
<div class="article-popover-header">
|
|
190
152
|
<h3>Display Settings</h3>
|
|
191
153
|
<button type="button" class="article-popover-close" popovertarget="article-font-settings" popovertargetaction="hide" aria-label="Close">
|
|
@@ -366,7 +328,6 @@ module.exports = class extends Component {
|
|
|
366
328
|
</aside>
|
|
367
329
|
</div>
|
|
368
330
|
</div>
|
|
369
|
-
)}
|
|
370
331
|
|
|
371
332
|
{hasComment && (
|
|
372
333
|
<div id="article-comment-popover" popover="auto" class="article-popover article-comment-popover">
|
|
@@ -398,7 +359,7 @@ module.exports = class extends Component {
|
|
|
398
359
|
</div>
|
|
399
360
|
)}
|
|
400
361
|
|
|
401
|
-
|
|
362
|
+
<ArticleInfo page={page} config={config} helper={helper} />
|
|
402
363
|
</div>
|
|
403
364
|
</Fragment>
|
|
404
365
|
);
|
|
@@ -2,7 +2,7 @@ const { Component } = require("inferno");
|
|
|
2
2
|
|
|
3
3
|
module.exports = class extends Component {
|
|
4
4
|
render() {
|
|
5
|
-
const { page, cover, helper
|
|
5
|
+
const { page, cover, helper } = this.props;
|
|
6
6
|
const { url_for } = helper;
|
|
7
7
|
|
|
8
8
|
const imageSrcset = `${cover}?w=800 800w, ${cover}?w=1500 1500w, ${cover}?w=2000 2000w, ${cover} 6144w`;
|
|
@@ -19,8 +19,8 @@ module.exports = class extends Component {
|
|
|
19
19
|
sizes="(max-width: 768px) 100vw, (max-width: 1024px) 100vw, 960px"
|
|
20
20
|
referrerpolicy="no-referrer"
|
|
21
21
|
decoding="async"
|
|
22
|
-
loading=
|
|
23
|
-
fetchpriority=
|
|
22
|
+
loading="eager"
|
|
23
|
+
fetchpriority="high"
|
|
24
24
|
/>
|
|
25
25
|
</a>
|
|
26
26
|
);
|
|
@@ -12,11 +12,15 @@ function formatDate(date, dateXml) {
|
|
|
12
12
|
|
|
13
13
|
module.exports = class extends Component {
|
|
14
14
|
render() {
|
|
15
|
-
const { url, title, date, dateXml } = this.props;
|
|
15
|
+
const { url, title, date, dateXml, excerpt, readTime } = this.props;
|
|
16
16
|
const formattedDate = formatDate(date, dateXml);
|
|
17
|
+
const hasPreview = excerpt || readTime;
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
|
-
<article
|
|
20
|
+
<article
|
|
21
|
+
class={hasPreview ? "archive-item has-preview" : "archive-item"}
|
|
22
|
+
data-read-time={readTime || null}
|
|
23
|
+
>
|
|
20
24
|
<div>
|
|
21
25
|
<p class="article-meta">
|
|
22
26
|
<time dateTime={dateXml || null}>{formattedDate}</time>
|
|
@@ -25,6 +29,9 @@ module.exports = class extends Component {
|
|
|
25
29
|
{title}
|
|
26
30
|
</a>
|
|
27
31
|
</div>
|
|
32
|
+
{excerpt && (
|
|
33
|
+
<template class="archive-item__excerpt" dangerouslySetInnerHTML={{ __html: excerpt }}></template>
|
|
34
|
+
)}
|
|
28
35
|
</article>
|
|
29
36
|
);
|
|
30
37
|
}
|
package/layout/common/footer.jsx
CHANGED
|
@@ -8,7 +8,10 @@ class Footer extends Component {
|
|
|
8
8
|
<footer class="footer">
|
|
9
9
|
<div class="footer-brand">
|
|
10
10
|
<p class="footer-credit">
|
|
11
|
-
2022–PRESENT <span class="footer-author">© GnixAij Oag</span
|
|
11
|
+
2022–PRESENT <span class="footer-author">© GnixAij Oag</span> 
|
|
12
|
+
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="license noopener noreferrer">
|
|
13
|
+
CC BY-NC-SA 4.0
|
|
14
|
+
</a>
|
|
12
15
|
</p>
|
|
13
16
|
{showVisitorCounter ? (
|
|
14
17
|
<p class="footer-meta">
|