hexo-theme-gnix 7.0.0 → 9.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 +6 -2
- package/include/hexo/encrypt.js +42 -0
- package/include/hexo/feed.js +330 -0
- package/include/util/common.js +7 -16
- package/languages/en.yml +5 -2
- package/languages/zh-CN.yml +5 -2
- package/layout/archive.jsx +8 -204
- package/layout/comment/twikoo.jsx +2 -11
- package/layout/common/article.jsx +45 -32
- package/layout/common/article_cover.jsx +11 -1
- package/layout/common/article_media.jsx +2 -4
- package/layout/common/footer.jsx +10 -14
- package/layout/common/head.jsx +7 -15
- package/layout/common/navbar.jsx +3 -18
- package/layout/common/scripts.jsx +6 -5
- package/layout/common/theme_selector.jsx +5 -6
- package/layout/common/toc.jsx +8 -14
- package/layout/index.jsx +2 -4
- package/layout/misc/open_graph.jsx +4 -4
- package/layout/misc/paginator.jsx +10 -4
- package/layout/misc/structured_data.jsx +3 -4
- package/layout/plugin/busuanzi.jsx +1 -1
- package/layout/plugin/cookie_consent.jsx +40 -31
- package/layout/plugin/swup.jsx +2 -22
- package/layout/search/insight.jsx +16 -3
- package/package.json +12 -8
- package/scripts/hot-reload.js +92 -0
- package/scripts/index.js +2 -0
- package/source/css/archive.css +251 -0
- package/source/css/default.css +300 -309
- package/source/css/encrypt.css +55 -0
- package/source/css/responsive/desktop.css +0 -119
- package/source/css/responsive/mobile.css +2 -22
- package/source/css/responsive/touch.css +9 -103
- package/source/css/twikoo.css +265 -249
- package/source/img/og_image.webp +0 -0
- package/source/js/archive-breadcrumb.js +1 -5
- package/source/js/busuanzi.js +1 -12
- package/source/js/components/chat.js +239 -0
- package/source/js/components/image-carousel.js +410 -0
- package/source/js/components/text-image-section.js +180 -0
- package/source/js/components/theme-stacked.js +165 -246
- package/source/js/components/tree.js +437 -0
- package/source/js/decrypt.js +112 -0
- package/source/js/insight.js +75 -65
- package/source/js/main.js +48 -31
- package/source/js/mdit/mermaid.js +12 -4
- package/source/js/swup.bundle.js +1 -0
- package/source/js/theme-selector.js +94 -113
- package/source/img/og_image.png +0 -0
- package/source/js/host/swup/Swup.umd.min.js +0 -1
- package/source/js/host/swup/head-plugin.umd.min.js +0 -1
- package/source/js/host/swup/scripts-plugin.umd.min.js +0 -2
package/README.md
CHANGED
|
@@ -54,9 +54,13 @@ bun i hexo-renderer-markdown-exit
|
|
|
54
54
|
## Links
|
|
55
55
|
|
|
56
56
|
- Change log: http://vluv.space/change
|
|
57
|
-
- Live Preview: http://vluv.space/test_markdown/
|
|
57
|
+
- Live Preview for Markdown: http://vluv.space/test_markdown/
|
|
58
|
+
- Live Preview for Components: https://vluv.space/test_components/
|
|
59
|
+
- Live Preview for Flavoured Markdown & HTML Element: https://vluv.space/test_flavored_md/
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
## Credit
|
|
61
63
|
|
|
62
|
-
[ppoffice/hexo-theme-icarus: A simple, delicate, and modern theme for the static site generator Hexo.](https://github.com/ppoffice/hexo-theme-icarus)
|
|
64
|
+
- [ppoffice/hexo-theme-icarus: A simple, delicate, and modern theme for the static site generator Hexo.](https://github.com/ppoffice/hexo-theme-icarus)
|
|
65
|
+
- [D0n9X1n/hexo-blog-encrypt: Yet, just another hexo plugin for security.](https://github.com/D0n9X1n/hexo-blog-encrypt)
|
|
66
|
+
- [hexojs/hexo-generator-feed: Feed generator for Hexo.](https://github.com/hexojs/hexo-generator-feed)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const crypto = require("node:crypto");
|
|
2
|
+
|
|
3
|
+
const ITERATIONS = 100_000;
|
|
4
|
+
|
|
5
|
+
module.exports = (hexo) => {
|
|
6
|
+
hexo.extend.filter.register(
|
|
7
|
+
"after_post_render",
|
|
8
|
+
(data) => {
|
|
9
|
+
const password = data.password;
|
|
10
|
+
if (!password && password !== 0) return data;
|
|
11
|
+
|
|
12
|
+
const passphrase = String(password);
|
|
13
|
+
const __ = hexo.theme.i18n.__(hexo.config.language);
|
|
14
|
+
|
|
15
|
+
const salt = crypto.randomBytes(16);
|
|
16
|
+
const iv = crypto.randomBytes(12);
|
|
17
|
+
const key = crypto.pbkdf2Sync(passphrase, salt, ITERATIONS, 32, "sha256");
|
|
18
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
19
|
+
const encrypted = Buffer.concat([cipher.update(data.content, "utf8"), cipher.final()]);
|
|
20
|
+
const tag = cipher.getAuthTag();
|
|
21
|
+
const base64 = Buffer.concat([salt, iv, encrypted, tag]).toString("base64");
|
|
22
|
+
|
|
23
|
+
data.content = `
|
|
24
|
+
<div class="encrypted-content" id="encrypted-article">
|
|
25
|
+
<div class="encrypted-data" style="display:none">${base64}</div>
|
|
26
|
+
<form class="encrypt-form" id="encrypt-form">
|
|
27
|
+
<p class="encrypt-message">${__("encrypt.message")}</p>
|
|
28
|
+
<div class="encrypt-input-wrap">
|
|
29
|
+
<input type="password" id="encrypt-pass" placeholder="${__("encrypt.placeholder")}" enterkeyhint="done" autocomplete="off" />
|
|
30
|
+
</div>
|
|
31
|
+
</form>
|
|
32
|
+
</div>`;
|
|
33
|
+
|
|
34
|
+
data.excerpt = `<p>${__("encrypt.abstract")}</p>`;
|
|
35
|
+
data.more = "";
|
|
36
|
+
data.encrypt = true;
|
|
37
|
+
|
|
38
|
+
return data;
|
|
39
|
+
},
|
|
40
|
+
1000,
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
const { extname } = require("node:path");
|
|
2
|
+
const { generateRssFeed, generateAtomFeed } = require("feedsmith");
|
|
3
|
+
const { gravatar, full_url_for, encodeURL, url_for } = require("hexo-util");
|
|
4
|
+
|
|
5
|
+
const VALID_FEED_TYPES = new Set(["atom", "rss2"]);
|
|
6
|
+
const EXISTING_FEED_LINK_RE = /type=['|"]?application\/(atom|rss)\+xml['|"]?/i;
|
|
7
|
+
const HEAD_RE = /<head>(?!<\/head>).+?<\/head>/s;
|
|
8
|
+
|
|
9
|
+
function stripControlChars(value) {
|
|
10
|
+
let sanitizedParts;
|
|
11
|
+
let start = 0;
|
|
12
|
+
|
|
13
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
14
|
+
const code = value.charCodeAt(index);
|
|
15
|
+
if (code < 0x20 || code === 0x7f) {
|
|
16
|
+
sanitizedParts ||= [];
|
|
17
|
+
if (start < index) {
|
|
18
|
+
sanitizedParts.push(value.slice(start, index));
|
|
19
|
+
}
|
|
20
|
+
start = index + 1;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!sanitizedParts) {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (start < value.length) {
|
|
29
|
+
sanitizedParts.push(value.slice(start));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return sanitizedParts.join("");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = (hexo) => {
|
|
36
|
+
hexo.config.feed = Object.assign(
|
|
37
|
+
{
|
|
38
|
+
enable: true,
|
|
39
|
+
type: "atom",
|
|
40
|
+
limit: 20,
|
|
41
|
+
hub: "",
|
|
42
|
+
content: true,
|
|
43
|
+
content_limit: 140,
|
|
44
|
+
content_limit_delim: "",
|
|
45
|
+
order_by: "-date",
|
|
46
|
+
autodiscovery: true,
|
|
47
|
+
},
|
|
48
|
+
hexo.config.feed,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const config = hexo.config.feed;
|
|
52
|
+
|
|
53
|
+
if (!config.enable) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let type = config.type;
|
|
58
|
+
let path = config.path;
|
|
59
|
+
|
|
60
|
+
if (!type || (typeof type !== "string" && !Array.isArray(type))) {
|
|
61
|
+
type = "atom";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (Array.isArray(type)) {
|
|
65
|
+
type = [...new Set(type.filter((item) => VALID_FEED_TYPES.has(item)))];
|
|
66
|
+
if (type.length === 0) {
|
|
67
|
+
type = "atom";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof type === "string") {
|
|
72
|
+
if (!VALID_FEED_TYPES.has(type)) type = "atom";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!path || typeof path !== typeof type) {
|
|
76
|
+
if (typeof type === "string") path = type.concat(".xml");
|
|
77
|
+
else path = type.map((str) => str.concat(".xml"));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (Array.isArray(path)) {
|
|
81
|
+
if (path.length !== type.length) {
|
|
82
|
+
if (path.length > type.length) path = path.slice(0, type.length);
|
|
83
|
+
else if (path.length === 0) path = type.map((str) => str.concat(".xml"));
|
|
84
|
+
else path.push(type[1].concat(".xml"));
|
|
85
|
+
}
|
|
86
|
+
path = path.map((str) => {
|
|
87
|
+
if (!extname(str)) return str.concat(".xml");
|
|
88
|
+
return str;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (typeof path === "string") {
|
|
93
|
+
if (!extname(path)) path += ".xml";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
config.type = type;
|
|
97
|
+
config.path = path;
|
|
98
|
+
const feedEntries = typeof type === "string" ? [{ type, path }] : type.map((feedType, index) => ({ type: feedType, path: path[index] }));
|
|
99
|
+
|
|
100
|
+
function composePosts(posts, feedConfig) {
|
|
101
|
+
const { limit, order_by } = feedConfig;
|
|
102
|
+
let processedPosts = posts.sort(order_by || "-date");
|
|
103
|
+
processedPosts = processedPosts.filter((post) => post.draft !== true);
|
|
104
|
+
processedPosts = processedPosts.filter((post) => !post.password);
|
|
105
|
+
if (limit) processedPosts = processedPosts.limit(limit);
|
|
106
|
+
return processedPosts;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function composeFeed(feedConfig, urlConfig, email, feedPath, context, posts) {
|
|
110
|
+
const { icon: iconConfig, hub } = feedConfig;
|
|
111
|
+
const latestPost = posts.first();
|
|
112
|
+
let url = urlConfig;
|
|
113
|
+
if (url[url.length - 1] !== "/") url += "/";
|
|
114
|
+
|
|
115
|
+
let icon = "";
|
|
116
|
+
if (iconConfig) icon = full_url_for.call(context, iconConfig);
|
|
117
|
+
else if (email) icon = gravatar(email);
|
|
118
|
+
|
|
119
|
+
const feedUrl = full_url_for.call(context, feedPath);
|
|
120
|
+
const currentYear = new Date().getFullYear();
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
title: context.title,
|
|
124
|
+
description: context.subtitle || context.description,
|
|
125
|
+
url,
|
|
126
|
+
feedUrl,
|
|
127
|
+
icon,
|
|
128
|
+
hub,
|
|
129
|
+
language: context.language,
|
|
130
|
+
author: { name: context.author, email: context.email },
|
|
131
|
+
copyright: context.author && `All rights reserved ${currentYear}, ${context.author}`,
|
|
132
|
+
updated: latestPost.updated ? latestPost.updated.toDate() : latestPost.date.toDate(),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function composeFeedLinks(feedUrl, hub, feedType) {
|
|
137
|
+
const links = [{ href: encodeURL(feedUrl), rel: "self", type: feedType }];
|
|
138
|
+
if (hub) links.push({ href: encodeURL(hub), rel: "hub" });
|
|
139
|
+
return links;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function composeItemDescription(post, feedConfig) {
|
|
143
|
+
const { content_limit, content_limit_delim } = feedConfig;
|
|
144
|
+
if (post.description) return post.description;
|
|
145
|
+
if (post.intro) return post.intro;
|
|
146
|
+
if (post.excerpt) return post.excerpt;
|
|
147
|
+
if (post.content) {
|
|
148
|
+
const shortContent = post.content.substring(0, content_limit || 140);
|
|
149
|
+
if (content_limit_delim) {
|
|
150
|
+
const delimPos = shortContent.lastIndexOf(content_limit_delim);
|
|
151
|
+
if (delimPos > -1) {
|
|
152
|
+
return shortContent.substring(0, delimPos);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return shortContent;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return "";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function composeItemContent(post, feedConfig) {
|
|
162
|
+
const { content } = feedConfig;
|
|
163
|
+
if (content && post.content) {
|
|
164
|
+
return stripControlChars(post.content);
|
|
165
|
+
}
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function composeItemCategories(post) {
|
|
170
|
+
const categories = [];
|
|
171
|
+
|
|
172
|
+
if (post.categories) {
|
|
173
|
+
for (const item of post.categories.toArray()) {
|
|
174
|
+
categories.push({ name: item.name, domain: item.permalink });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (post.tags) {
|
|
179
|
+
for (const item of post.tags.toArray()) {
|
|
180
|
+
categories.push({ name: item.name, domain: item.permalink });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return categories;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function composeItem(post, feedConfig, context) {
|
|
188
|
+
const published = post.date.toDate();
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
title: post.title,
|
|
192
|
+
link: encodeURL(full_url_for.call(context, post.permalink)),
|
|
193
|
+
description: composeItemDescription(post, feedConfig),
|
|
194
|
+
published,
|
|
195
|
+
updated: post.updated ? post.updated.toDate() : published,
|
|
196
|
+
content: composeItemContent(post, feedConfig),
|
|
197
|
+
enclosures: post.image && [{ url: full_url_for.call(context, post.image) }],
|
|
198
|
+
categories: composeItemCategories(post),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function composeRssItem(feed, item) {
|
|
203
|
+
return {
|
|
204
|
+
title: item.title,
|
|
205
|
+
link: item.link,
|
|
206
|
+
guid: item.link,
|
|
207
|
+
description: item.description,
|
|
208
|
+
pubDate: item.published,
|
|
209
|
+
authors: [feed.author],
|
|
210
|
+
content: { encoded: item.content },
|
|
211
|
+
enclosures: item.enclosures,
|
|
212
|
+
categories: item.categories,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function composeAtomEntry(feed, item) {
|
|
217
|
+
const entryLinks = [{ href: item.link }, ...(item.enclosures || []).map((enclosure) => ({ href: enclosure.url, rel: "enclosure" }))];
|
|
218
|
+
return {
|
|
219
|
+
title: item.title,
|
|
220
|
+
id: item.link,
|
|
221
|
+
links: entryLinks,
|
|
222
|
+
summary: item.description,
|
|
223
|
+
content: item.content,
|
|
224
|
+
published: item.published,
|
|
225
|
+
updated: item.updated || item.published,
|
|
226
|
+
authors: feed.author.name && [feed.author],
|
|
227
|
+
categories: item.categories.map((cat) => ({ term: cat.name, scheme: cat.domain })),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function generateRss(feed, items) {
|
|
232
|
+
const links = composeFeedLinks(feed.feedUrl, feed.hub, "application/rss+xml");
|
|
233
|
+
const siteUrl = encodeURL(feed.url);
|
|
234
|
+
|
|
235
|
+
return generateRssFeed(
|
|
236
|
+
{
|
|
237
|
+
title: feed.title,
|
|
238
|
+
description: feed.description,
|
|
239
|
+
link: siteUrl,
|
|
240
|
+
language: feed.language,
|
|
241
|
+
copyright: feed.copyright,
|
|
242
|
+
generator: "Hexo",
|
|
243
|
+
lastBuildDate: feed.updated,
|
|
244
|
+
image: feed.icon && {
|
|
245
|
+
url: feed.icon,
|
|
246
|
+
title: feed.title,
|
|
247
|
+
link: siteUrl,
|
|
248
|
+
},
|
|
249
|
+
atom: { links },
|
|
250
|
+
items: items.map((item) => composeRssItem(feed, item)),
|
|
251
|
+
},
|
|
252
|
+
{ lenient: true },
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function generateAtom(feed, items) {
|
|
257
|
+
const siteUrl = encodeURL(feed.url);
|
|
258
|
+
const links = [{ href: siteUrl, rel: "alternate" }, ...composeFeedLinks(feed.feedUrl, feed.hub)];
|
|
259
|
+
|
|
260
|
+
return generateAtomFeed(
|
|
261
|
+
{
|
|
262
|
+
title: feed.title,
|
|
263
|
+
id: siteUrl,
|
|
264
|
+
subtitle: feed.description,
|
|
265
|
+
updated: feed.updated,
|
|
266
|
+
links,
|
|
267
|
+
generator: { text: "Hexo", uri: "https://hexo.io/" },
|
|
268
|
+
icon: feed.icon,
|
|
269
|
+
rights: feed.copyright,
|
|
270
|
+
authors: feed.author.name && [feed.author],
|
|
271
|
+
entries: items.map((item) => composeAtomEntry(feed, item)),
|
|
272
|
+
language: feed.language,
|
|
273
|
+
},
|
|
274
|
+
{ lenient: true },
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function createFeedGenerator(feedType, feedPath) {
|
|
279
|
+
return function (locals) {
|
|
280
|
+
const { config } = this;
|
|
281
|
+
const { feed: feedConfig } = config;
|
|
282
|
+
const posts = composePosts(locals.posts, feedConfig);
|
|
283
|
+
|
|
284
|
+
if (posts.length <= 0) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const feed = composeFeed(feedConfig, config.url, config.email, feedPath, this, posts);
|
|
289
|
+
const items = posts.toArray().map((post) => composeItem(post, feedConfig, this));
|
|
290
|
+
|
|
291
|
+
let data;
|
|
292
|
+
switch (feedType) {
|
|
293
|
+
case "rss2":
|
|
294
|
+
data = generateRss(feed, items);
|
|
295
|
+
break;
|
|
296
|
+
default:
|
|
297
|
+
data = generateAtom(feed, items);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
path: feedPath,
|
|
302
|
+
data,
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (typeof type === "string") {
|
|
308
|
+
hexo.extend.generator.register(type, createFeedGenerator(type, path));
|
|
309
|
+
} else {
|
|
310
|
+
for (const entry of feedEntries) {
|
|
311
|
+
hexo.extend.generator.register(entry.type, createFeedGenerator(entry.type, entry.path));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (typeof config.autodiscovery !== "boolean") config.autodiscovery = true;
|
|
316
|
+
|
|
317
|
+
if (config.autodiscovery === true) {
|
|
318
|
+
hexo.extend.filter.register("after_render:html", function (data) {
|
|
319
|
+
if (EXISTING_FEED_LINK_RE.test(data) || hexo.config.feed.autodiscovery === false) {
|
|
320
|
+
return data;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const autodiscoveryTag = feedEntries
|
|
324
|
+
.map((entry) => `<link rel="alternate" href="${url_for.call(this, entry.path)}" title="${hexo.config.title}" type="application/${entry.type.replace(/2$/, "")}+xml">\n`)
|
|
325
|
+
.join("");
|
|
326
|
+
|
|
327
|
+
return data.replace(HEAD_RE, (str) => str.replace("</head>", `${autodiscoveryTag}</head>`));
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
};
|
package/include/util/common.js
CHANGED
|
@@ -26,27 +26,11 @@ function cacheComponent(type, prefix, transform) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function lazy_load_css(href) {
|
|
30
|
-
return `
|
|
31
|
-
(function () {
|
|
32
|
-
if (document.querySelector('link[rel="stylesheet"][href="${href}"]')) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
var link = document.createElement('link');
|
|
37
|
-
link.rel = 'stylesheet';
|
|
38
|
-
link.href = '${href}';
|
|
39
|
-
document.head.appendChild(link);
|
|
40
|
-
})();
|
|
41
|
-
`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
29
|
module.exports = {
|
|
45
30
|
// 导出常用的依赖
|
|
46
31
|
Component,
|
|
47
32
|
Fragment,
|
|
48
33
|
view,
|
|
49
|
-
lazy_load_css,
|
|
50
34
|
cacheComponent,
|
|
51
35
|
|
|
52
36
|
// 通用的组件加载函数
|
|
@@ -58,4 +42,11 @@ module.exports = {
|
|
|
58
42
|
return fallback;
|
|
59
43
|
}
|
|
60
44
|
},
|
|
45
|
+
|
|
46
|
+
isValidDate: (val) => val instanceof Date && !Number.isNaN(val.getTime()),
|
|
47
|
+
parseISO: (str) => new Date(str),
|
|
48
|
+
dateFormatters: {
|
|
49
|
+
shortDay: new Intl.DateTimeFormat("en", { month: "short", day: "2-digit" }),
|
|
50
|
+
longMonth: new Intl.DateTimeFormat("en", { month: "long" }),
|
|
51
|
+
},
|
|
61
52
|
};
|
package/languages/en.yml
CHANGED
|
@@ -14,8 +14,6 @@ common:
|
|
|
14
14
|
page:
|
|
15
15
|
one: "Page"
|
|
16
16
|
other: "Pages"
|
|
17
|
-
prev: "Previous"
|
|
18
|
-
next: "Next"
|
|
19
17
|
article:
|
|
20
18
|
created_at: "Posted %s"
|
|
21
19
|
more: "Read more"
|
|
@@ -45,3 +43,8 @@ search:
|
|
|
45
43
|
no_result: "No results for"
|
|
46
44
|
untitled: "(Untitled)"
|
|
47
45
|
empty_preview: "(No preview)"
|
|
46
|
+
encrypt:
|
|
47
|
+
message: "This article is encrypted, please enter the password to view"
|
|
48
|
+
placeholder: "Enter password..."
|
|
49
|
+
wrong_pass: "Wrong password, please try again"
|
|
50
|
+
abstract: "This article is encrypted"
|
package/languages/zh-CN.yml
CHANGED
|
@@ -14,8 +14,6 @@ common:
|
|
|
14
14
|
page:
|
|
15
15
|
one: "PAGE"
|
|
16
16
|
other: "PAGE"
|
|
17
|
-
prev: "Previous"
|
|
18
|
-
next: "Next"
|
|
19
17
|
article:
|
|
20
18
|
created_at: "%s"
|
|
21
19
|
more: "Read"
|
|
@@ -45,3 +43,8 @@ search:
|
|
|
45
43
|
no_result: "未找到搜索结果"
|
|
46
44
|
untitled: "(无标题)"
|
|
47
45
|
empty_preview: "(无内容预览)"
|
|
46
|
+
encrypt:
|
|
47
|
+
message: "此文章已加密,请输入密码查看"
|
|
48
|
+
placeholder: "输入密码..."
|
|
49
|
+
wrong_pass: "密码错误,请重试"
|
|
50
|
+
abstract: "此文章已加密"
|