@sugarat/theme 0.2.30 → 0.3.1
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/node.d.ts +40 -3
- package/node.js +196 -117
- package/package.json +4 -2
- package/src/components/BlogApp.vue +26 -7
- package/src/components/BlogButtonAfterArticle.vue +122 -0
- package/src/components/{BlogComment.vue → BlogCommentWrapper.vue} +14 -54
- package/src/components/BlogHomeBanner.vue +1 -1
- package/src/components/BlogHomeHeaderAvatar.vue +7 -5
- package/src/components/BlogHomeInfo.vue +1 -1
- package/src/components/BlogHomeTags.vue +1 -1
- package/src/components/BlogHotArticle.vue +37 -8
- package/src/components/BlogItem.vue +29 -15
- package/src/components/BlogPopover.vue +4 -0
- package/src/components/BlogRecommendArticle.vue +30 -7
- package/src/components/CommentArtalk.vue +74 -0
- package/src/components/CommentGiscus.vue +49 -0
- package/src/composables/config/blog.ts +4 -5
- package/src/composables/config/index.ts +41 -3
- package/src/constants/svg.ts +23 -0
- package/src/hooks/useDarkTransition.ts +46 -0
- package/src/hooks/useOml2d.ts +6 -0
- package/src/node.ts +3 -1
- package/src/styles/dark-transition.css +23 -0
- package/src/utils/node/hot-reload-plugin.ts +59 -0
- package/src/utils/node/index.ts +49 -15
- package/src/utils/node/theme.ts +94 -91
- package/src/utils/node/vitePlugins.ts +7 -7
- package/types/vue-shim.d.ts +10 -0
package/node.d.ts
CHANGED
|
@@ -93,7 +93,8 @@ declare namespace Theme {
|
|
|
93
93
|
label: string;
|
|
94
94
|
type: string;
|
|
95
95
|
}
|
|
96
|
-
|
|
96
|
+
type CommentConfig = ((GiscusOption & CommentCommonConfig) | GiscusConfig | ArtalkConfig);
|
|
97
|
+
interface CommentCommonConfig {
|
|
97
98
|
/**
|
|
98
99
|
* @default '评论'
|
|
99
100
|
*/
|
|
@@ -109,7 +110,15 @@ declare namespace Theme {
|
|
|
109
110
|
*/
|
|
110
111
|
mobileMinify?: boolean;
|
|
111
112
|
}
|
|
112
|
-
interface GiscusConfig {
|
|
113
|
+
interface GiscusConfig extends CommentCommonConfig {
|
|
114
|
+
type: 'giscus';
|
|
115
|
+
options: GiscusOption;
|
|
116
|
+
}
|
|
117
|
+
interface ArtalkConfig extends CommentCommonConfig {
|
|
118
|
+
type: 'artalk';
|
|
119
|
+
options: ArtalkOption;
|
|
120
|
+
}
|
|
121
|
+
interface GiscusOption {
|
|
113
122
|
repo: Repo;
|
|
114
123
|
repoId: string;
|
|
115
124
|
category: string;
|
|
@@ -119,6 +128,10 @@ declare namespace Theme {
|
|
|
119
128
|
lang?: string;
|
|
120
129
|
loading?: 'lazy' | 'eager';
|
|
121
130
|
}
|
|
131
|
+
interface ArtalkOption {
|
|
132
|
+
site: string;
|
|
133
|
+
server: string;
|
|
134
|
+
}
|
|
122
135
|
interface HotArticle {
|
|
123
136
|
title?: string;
|
|
124
137
|
pageSize?: number;
|
|
@@ -319,7 +332,8 @@ declare namespace Theme {
|
|
|
319
332
|
search?: SearchConfig;
|
|
320
333
|
/**
|
|
321
334
|
* 配置评论
|
|
322
|
-
*
|
|
335
|
+
* giscus: https://giscus.app/zh-CN
|
|
336
|
+
* artalk: https://artalk.js.org/
|
|
323
337
|
*/
|
|
324
338
|
comment?: CommentConfig | false;
|
|
325
339
|
/**
|
|
@@ -391,6 +405,13 @@ declare namespace Theme {
|
|
|
391
405
|
*/
|
|
392
406
|
oml2d?: Options;
|
|
393
407
|
homeTags?: boolean;
|
|
408
|
+
buttonAfterArticle?: ButtonAfterArticleConfig | false;
|
|
409
|
+
/**
|
|
410
|
+
* 是否开启深色模式过渡动画
|
|
411
|
+
* @reference https://vitepress.dev/zh/guide/extending-default-theme#on-appearance-toggle
|
|
412
|
+
* @default true
|
|
413
|
+
*/
|
|
414
|
+
darkTransition?: boolean;
|
|
394
415
|
}
|
|
395
416
|
interface BackToTop {
|
|
396
417
|
/**
|
|
@@ -470,6 +491,22 @@ declare namespace Theme {
|
|
|
470
491
|
*/
|
|
471
492
|
handleChangeSlogan?: (oldSlogan: string) => string | Promise<string>;
|
|
472
493
|
}
|
|
494
|
+
interface ButtonAfterArticleConfig {
|
|
495
|
+
openTitle?: string;
|
|
496
|
+
closeTitle?: string;
|
|
497
|
+
content?: string;
|
|
498
|
+
icon?: 'aliPay' | 'wechatPay' | string;
|
|
499
|
+
/**
|
|
500
|
+
* 按钮尺寸
|
|
501
|
+
* @default 'default'
|
|
502
|
+
*/
|
|
503
|
+
size?: 'small' | 'default' | 'large';
|
|
504
|
+
/**
|
|
505
|
+
* 默认展开
|
|
506
|
+
* @default false
|
|
507
|
+
*/
|
|
508
|
+
expand?: boolean;
|
|
509
|
+
}
|
|
473
510
|
}
|
|
474
511
|
|
|
475
512
|
/**
|
package/node.js
CHANGED
|
@@ -40,7 +40,7 @@ module.exports = __toCommonJS(node_exports);
|
|
|
40
40
|
// src/utils/node/mdPlugins.ts
|
|
41
41
|
var import_module = require("module");
|
|
42
42
|
|
|
43
|
-
// ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.
|
|
43
|
+
// ../../node_modules/.pnpm/vitepress-plugin-tabs@0.2.0_vitepress@1.1.4_@algolia+client-search@4.19.1_@types+node@20.6.3__ee2wap4ljxfukruj47sfuqlmqq/node_modules/vitepress-plugin-tabs/dist/index.js
|
|
44
44
|
var tabsMarker = "=tabs";
|
|
45
45
|
var tabsMarkerLen = tabsMarker.length;
|
|
46
46
|
var ruleBlockTabs = (state, startLine, endLine, silent) => {
|
|
@@ -210,69 +210,9 @@ var import_vitepress_markdown_timeline = __toESM(require("vitepress-markdown-tim
|
|
|
210
210
|
// src/utils/node/index.ts
|
|
211
211
|
var import_node_child_process = require("child_process");
|
|
212
212
|
var import_node_path = __toESM(require("path"));
|
|
213
|
-
|
|
214
|
-
// src/utils/client/index.ts
|
|
215
|
-
function formatDate(d, fmt = "yyyy-MM-dd hh:mm:ss") {
|
|
216
|
-
if (!(d instanceof Date)) {
|
|
217
|
-
d = new Date(d);
|
|
218
|
-
}
|
|
219
|
-
const o = {
|
|
220
|
-
"M+": d.getMonth() + 1,
|
|
221
|
-
// 月份
|
|
222
|
-
"d+": d.getDate(),
|
|
223
|
-
// 日
|
|
224
|
-
"h+": d.getHours(),
|
|
225
|
-
// 小时
|
|
226
|
-
"m+": d.getMinutes(),
|
|
227
|
-
// 分
|
|
228
|
-
"s+": d.getSeconds(),
|
|
229
|
-
// 秒
|
|
230
|
-
"q+": Math.floor((d.getMonth() + 3) / 3),
|
|
231
|
-
// 季度
|
|
232
|
-
"S": d.getMilliseconds()
|
|
233
|
-
// 毫秒
|
|
234
|
-
};
|
|
235
|
-
if (/(y+)/.test(fmt)) {
|
|
236
|
-
fmt = fmt.replace(
|
|
237
|
-
RegExp.$1,
|
|
238
|
-
`${d.getFullYear()}`.substr(4 - RegExp.$1.length)
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
for (const k in o) {
|
|
242
|
-
if (new RegExp(`(${k})`).test(fmt))
|
|
243
|
-
fmt = fmt.replace(
|
|
244
|
-
RegExp.$1,
|
|
245
|
-
RegExp.$1.length === 1 ? o[k] : `00${o[k]}`.substr(`${o[k]}`.length)
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
return fmt;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// src/utils/node/index.ts
|
|
252
|
-
function clearMatterContent(content) {
|
|
253
|
-
let first___;
|
|
254
|
-
let second___;
|
|
255
|
-
const lines = content.split("\n").reduce((pre, line) => {
|
|
256
|
-
if (!line.trim() && pre.length === 0) {
|
|
257
|
-
return pre;
|
|
258
|
-
}
|
|
259
|
-
if (line.trim() === "---") {
|
|
260
|
-
if (first___ === void 0) {
|
|
261
|
-
first___ = pre.length;
|
|
262
|
-
} else if (second___ === void 0) {
|
|
263
|
-
second___ = pre.length;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
pre.push(line);
|
|
267
|
-
return pre;
|
|
268
|
-
}, []);
|
|
269
|
-
return lines.slice(second___ || 0).join("\n");
|
|
270
|
-
}
|
|
271
213
|
function getDefaultTitle(content) {
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
})?.slice(2).replace(/^\s+|\s+$/g, "") || "";
|
|
275
|
-
return title;
|
|
214
|
+
const match = content.match(/^(#+)\s+(.+)/m);
|
|
215
|
+
return match?.[2] || "";
|
|
276
216
|
}
|
|
277
217
|
function getFileBirthTime(url) {
|
|
278
218
|
let date = /* @__PURE__ */ new Date();
|
|
@@ -282,12 +222,12 @@ function getFileBirthTime(url) {
|
|
|
282
222
|
date = new Date(infoStr);
|
|
283
223
|
}
|
|
284
224
|
} catch (error) {
|
|
285
|
-
return
|
|
225
|
+
return date;
|
|
286
226
|
}
|
|
287
|
-
return
|
|
227
|
+
return date;
|
|
288
228
|
}
|
|
289
229
|
function getTextSummary(text, count = 100) {
|
|
290
|
-
return
|
|
230
|
+
return text?.replace(/^#+\s+.*/, "")?.replace(/#/g, "")?.replace(/!\[.*?\]\(.*?\)/g, "")?.replace(/\[(.*?)\]\(.*?\)/g, "$1")?.replace(/\*\*(.*?)\*\*/g, "$1")?.split("\n")?.filter((v) => !!v)?.join("\n")?.replace(/>(.*)/, "")?.trim()?.slice(0, count);
|
|
291
231
|
}
|
|
292
232
|
function aliasObjectToArray(obj) {
|
|
293
233
|
return Object.entries(obj).map(([find, replacement]) => ({
|
|
@@ -317,6 +257,37 @@ function getFirstImagURLFromMD(content, route) {
|
|
|
317
257
|
const relativePath = url.startsWith("/") ? url : import_node_path.default.join(paths.join("/") || "", url);
|
|
318
258
|
return joinPath("/", relativePath);
|
|
319
259
|
}
|
|
260
|
+
function debounce(func, delay = 1e3) {
|
|
261
|
+
let timeoutId;
|
|
262
|
+
return (...rest) => {
|
|
263
|
+
clearTimeout(timeoutId);
|
|
264
|
+
timeoutId = setTimeout(() => {
|
|
265
|
+
func(...rest);
|
|
266
|
+
}, delay);
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function isEqual(obj1, obj2, excludeKeys = []) {
|
|
270
|
+
const keys1 = Object.keys(obj1).filter((key) => !excludeKeys.includes(key));
|
|
271
|
+
const keys2 = Object.keys(obj2).filter((key) => !excludeKeys.includes(key));
|
|
272
|
+
if (keys1.length !== keys2.length) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
for (const key of keys1) {
|
|
276
|
+
if (!keys2.includes(key)) {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
const val1 = obj1[key];
|
|
280
|
+
const val2 = obj2[key];
|
|
281
|
+
const areObjects = isObject(val1) && isObject(val2);
|
|
282
|
+
if (areObjects && !isEqual(val1, val2, excludeKeys) || !areObjects && val1 !== val2) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
function isObject(obj) {
|
|
289
|
+
return obj != null && typeof obj === "object";
|
|
290
|
+
}
|
|
320
291
|
|
|
321
292
|
// src/utils/node/mdPlugins.ts
|
|
322
293
|
var import_meta = {};
|
|
@@ -384,6 +355,45 @@ var import_node_path2 = __toESM(require("path"));
|
|
|
384
355
|
var import_node_process = __toESM(require("process"));
|
|
385
356
|
var import_fast_glob = __toESM(require("fast-glob"));
|
|
386
357
|
var import_gray_matter = __toESM(require("gray-matter"));
|
|
358
|
+
|
|
359
|
+
// src/utils/client/index.ts
|
|
360
|
+
function formatDate(d, fmt = "yyyy-MM-dd hh:mm:ss") {
|
|
361
|
+
if (!(d instanceof Date)) {
|
|
362
|
+
d = new Date(d);
|
|
363
|
+
}
|
|
364
|
+
const o = {
|
|
365
|
+
"M+": d.getMonth() + 1,
|
|
366
|
+
// 月份
|
|
367
|
+
"d+": d.getDate(),
|
|
368
|
+
// 日
|
|
369
|
+
"h+": d.getHours(),
|
|
370
|
+
// 小时
|
|
371
|
+
"m+": d.getMinutes(),
|
|
372
|
+
// 分
|
|
373
|
+
"s+": d.getSeconds(),
|
|
374
|
+
// 秒
|
|
375
|
+
"q+": Math.floor((d.getMonth() + 3) / 3),
|
|
376
|
+
// 季度
|
|
377
|
+
"S": d.getMilliseconds()
|
|
378
|
+
// 毫秒
|
|
379
|
+
};
|
|
380
|
+
if (/(y+)/.test(fmt)) {
|
|
381
|
+
fmt = fmt.replace(
|
|
382
|
+
RegExp.$1,
|
|
383
|
+
`${d.getFullYear()}`.substr(4 - RegExp.$1.length)
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
for (const k in o) {
|
|
387
|
+
if (new RegExp(`(${k})`).test(fmt))
|
|
388
|
+
fmt = fmt.replace(
|
|
389
|
+
RegExp.$1,
|
|
390
|
+
RegExp.$1.length === 1 ? o[k] : `00${o[k]}`.substr(`${o[k]}`.length)
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
return fmt;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/utils/node/theme.ts
|
|
387
397
|
function patchDefaultThemeSideBar(cfg) {
|
|
388
398
|
return cfg?.blog !== false && cfg?.recommend !== false ? {
|
|
389
399
|
sidebar: [
|
|
@@ -394,64 +404,79 @@ function patchDefaultThemeSideBar(cfg) {
|
|
|
394
404
|
]
|
|
395
405
|
} : void 0;
|
|
396
406
|
}
|
|
397
|
-
|
|
407
|
+
function getPageRoute(filepath, srcDir) {
|
|
408
|
+
let route = filepath.replace(".md", "");
|
|
409
|
+
if (route.startsWith("./")) {
|
|
410
|
+
route = route.replace(
|
|
411
|
+
new RegExp(
|
|
412
|
+
`^\\.\\/${import_node_path2.default.join(srcDir, "/").replace(new RegExp(`\\${import_node_path2.default.sep}`, "g"), "/")}`
|
|
413
|
+
),
|
|
414
|
+
""
|
|
415
|
+
);
|
|
416
|
+
} else {
|
|
417
|
+
route = route.replace(
|
|
418
|
+
new RegExp(
|
|
419
|
+
`^${import_node_path2.default.join(srcDir, "/").replace(new RegExp(`\\${import_node_path2.default.sep}`, "g"), "/")}`
|
|
420
|
+
),
|
|
421
|
+
""
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
return `/${route}`;
|
|
425
|
+
}
|
|
426
|
+
var defaultTimeZoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() / -60;
|
|
427
|
+
function getArticleMeta(filepath, route, timeZone = defaultTimeZoneOffset) {
|
|
428
|
+
const fileContent = import_node_fs.default.readFileSync(filepath, "utf-8");
|
|
429
|
+
const { data: frontmatter, excerpt, content } = (0, import_gray_matter.default)(fileContent, {
|
|
430
|
+
excerpt: true
|
|
431
|
+
});
|
|
432
|
+
const meta = {
|
|
433
|
+
...frontmatter
|
|
434
|
+
};
|
|
435
|
+
if (!meta.title) {
|
|
436
|
+
meta.title = getDefaultTitle(content);
|
|
437
|
+
}
|
|
438
|
+
if (!meta.date) {
|
|
439
|
+
meta.date = formatDate(getFileBirthTime(filepath));
|
|
440
|
+
} else {
|
|
441
|
+
meta.date = formatDate(
|
|
442
|
+
/* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}+${timeZone}`)
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
meta.categories = typeof meta.categories === "string" ? [meta.categories] : meta.categories;
|
|
446
|
+
meta.tags = typeof meta.tags === "string" ? [meta.tags] : meta.tags;
|
|
447
|
+
meta.tag = [meta.tag || []].flat().concat([
|
|
448
|
+
.../* @__PURE__ */ new Set([...meta.categories || [], ...meta.tags || []])
|
|
449
|
+
]);
|
|
450
|
+
meta.description = meta.description || getTextSummary(content, 100) || excerpt;
|
|
451
|
+
meta.cover = meta.cover ?? getFirstImagURLFromMD(fileContent, route);
|
|
452
|
+
if (meta.publish === false) {
|
|
453
|
+
meta.hidden = true;
|
|
454
|
+
meta.recommend = false;
|
|
455
|
+
}
|
|
456
|
+
return meta;
|
|
457
|
+
}
|
|
398
458
|
function getArticles(cfg) {
|
|
399
459
|
const srcDir = cfg?.srcDir || import_node_process.default.argv.slice(2)?.[1] || ".";
|
|
400
460
|
const files = import_fast_glob.default.sync(`${srcDir}/**/*.md`, { ignore: ["node_modules"] });
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
route = route.replace(
|
|
405
|
-
new RegExp(
|
|
406
|
-
`^\\.\\/${import_node_path2.default.join(srcDir, "/").replace(new RegExp(`\\${import_node_path2.default.sep}`, "g"), "/")}`
|
|
407
|
-
),
|
|
408
|
-
""
|
|
409
|
-
);
|
|
410
|
-
} else {
|
|
411
|
-
route = route.replace(
|
|
412
|
-
new RegExp(
|
|
413
|
-
`^${import_node_path2.default.join(srcDir, "/").replace(new RegExp(`\\${import_node_path2.default.sep}`, "g"), "/")}`
|
|
414
|
-
),
|
|
415
|
-
""
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
pageMap.set(`/${route}`, v);
|
|
419
|
-
const fileContent = import_node_fs.default.readFileSync(v, "utf-8");
|
|
420
|
-
const { data: frontmatter, excerpt } = (0, import_gray_matter.default)(fileContent, {
|
|
421
|
-
excerpt: true
|
|
422
|
-
});
|
|
423
|
-
const meta = {
|
|
424
|
-
...frontmatter
|
|
425
|
-
};
|
|
426
|
-
if (!meta.title) {
|
|
427
|
-
meta.title = getDefaultTitle(fileContent);
|
|
428
|
-
}
|
|
429
|
-
if (!meta.date) {
|
|
430
|
-
meta.date = getFileBirthTime(v);
|
|
431
|
-
} else {
|
|
432
|
-
const timeZone = cfg?.timeZone ?? 8;
|
|
433
|
-
meta.date = formatDate(
|
|
434
|
-
/* @__PURE__ */ new Date(`${new Date(meta.date).toUTCString()}+${timeZone}`)
|
|
435
|
-
);
|
|
436
|
-
}
|
|
437
|
-
meta.categories = typeof meta.categories === "string" ? [meta.categories] : meta.categories;
|
|
438
|
-
meta.tags = typeof meta.tags === "string" ? [meta.tags] : meta.tags;
|
|
439
|
-
meta.tag = [meta.tag || []].flat().concat([
|
|
440
|
-
.../* @__PURE__ */ new Set([...meta.categories || [], ...meta.tags || []])
|
|
441
|
-
]);
|
|
442
|
-
const wordCount = 100;
|
|
443
|
-
meta.description = meta.description || getTextSummary(fileContent, wordCount);
|
|
444
|
-
meta.cover = meta.cover ?? getFirstImagURLFromMD(fileContent, `/${route}`);
|
|
445
|
-
if (meta.publish === false) {
|
|
446
|
-
meta.hidden = true;
|
|
447
|
-
meta.recommend = false;
|
|
448
|
-
}
|
|
461
|
+
const pageData = files.map((filepath) => {
|
|
462
|
+
const route = getPageRoute(filepath, srcDir);
|
|
463
|
+
const meta = getArticleMeta(filepath, route, cfg?.timeZone);
|
|
449
464
|
return {
|
|
450
|
-
route
|
|
465
|
+
route,
|
|
451
466
|
meta
|
|
452
467
|
};
|
|
453
468
|
}).filter((v) => v.meta.layout !== "home");
|
|
454
|
-
return
|
|
469
|
+
return pageData;
|
|
470
|
+
}
|
|
471
|
+
function patchVPConfig(vpConfig, cfg) {
|
|
472
|
+
vpConfig.head = vpConfig.head || [];
|
|
473
|
+
if (cfg?.comment && "type" in cfg.comment && cfg?.comment?.type === "artalk") {
|
|
474
|
+
const server = cfg.comment?.options?.server;
|
|
475
|
+
if (server) {
|
|
476
|
+
vpConfig.head.push(["link", { href: `${server}/dist/Artalk.css`, rel: "stylesheet" }]);
|
|
477
|
+
vpConfig.head.push(["script", { src: `${server}/dist/Artalk.js`, id: "artalk-script" }]);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
455
480
|
}
|
|
456
481
|
function patchVPThemeConfig(cfg, vpThemeConfig = {}) {
|
|
457
482
|
vpThemeConfig.sidebar = patchDefaultThemeSideBar(cfg)?.sidebar;
|
|
@@ -468,11 +493,64 @@ var import_node_fs2 = require("fs");
|
|
|
468
493
|
var import_node_buffer = require("buffer");
|
|
469
494
|
var import_vitepress_plugin_pagefind = require("vitepress-plugin-pagefind");
|
|
470
495
|
var import_vitepress_plugin_rss = require("vitepress-plugin-rss");
|
|
496
|
+
|
|
497
|
+
// src/utils/node/hot-reload-plugin.ts
|
|
498
|
+
function themeReloadPlugin() {
|
|
499
|
+
let blogConfig;
|
|
500
|
+
let vitepressConfig;
|
|
501
|
+
let docsDir;
|
|
502
|
+
const generateRoute = (filepath) => {
|
|
503
|
+
return filepath.replace(docsDir, "").replace(".md", "");
|
|
504
|
+
};
|
|
505
|
+
return {
|
|
506
|
+
name: "@sugarat/theme-reload",
|
|
507
|
+
apply: "serve",
|
|
508
|
+
configureServer(server) {
|
|
509
|
+
const restart = debounce(() => {
|
|
510
|
+
server.restart();
|
|
511
|
+
}, 500);
|
|
512
|
+
server.watcher.on("add", (path4) => {
|
|
513
|
+
const route = generateRoute(path4);
|
|
514
|
+
const meta = getArticleMeta(path4, route, blogConfig?.timeZone);
|
|
515
|
+
blogConfig.pagesData.push({
|
|
516
|
+
route,
|
|
517
|
+
meta
|
|
518
|
+
});
|
|
519
|
+
restart();
|
|
520
|
+
});
|
|
521
|
+
server.watcher.on("change", (path4) => {
|
|
522
|
+
const route = generateRoute(path4);
|
|
523
|
+
const meta = getArticleMeta(path4, route, blogConfig?.timeZone);
|
|
524
|
+
const matched = blogConfig.pagesData.find((v) => v.route === route);
|
|
525
|
+
if (matched && !isEqual(matched.meta, meta, ["date", "description"])) {
|
|
526
|
+
matched.meta = meta;
|
|
527
|
+
restart();
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
server.watcher.on("unlink", (path4) => {
|
|
531
|
+
const route = generateRoute(path4);
|
|
532
|
+
const idx = blogConfig.pagesData.findIndex((v) => v.route === route);
|
|
533
|
+
if (idx >= 0) {
|
|
534
|
+
blogConfig.pagesData.splice(idx, 1);
|
|
535
|
+
restart();
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
},
|
|
539
|
+
configResolved(config) {
|
|
540
|
+
vitepressConfig = config.vitepress;
|
|
541
|
+
docsDir = vitepressConfig.srcDir;
|
|
542
|
+
blogConfig = config.vitepress.site.themeConfig.blog;
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/utils/node/vitePlugins.ts
|
|
471
548
|
function getVitePlugins(cfg) {
|
|
472
549
|
const plugins = [];
|
|
473
550
|
const buildEndFn = [];
|
|
474
551
|
plugins.push(inlineBuildEndPlugin(buildEndFn));
|
|
475
552
|
plugins.push(coverImgTransform());
|
|
553
|
+
plugins.push(themeReloadPlugin());
|
|
476
554
|
if (cfg && cfg.search !== false) {
|
|
477
555
|
const ops = cfg.search instanceof Object ? cfg.search : {};
|
|
478
556
|
plugins.push(
|
|
@@ -574,6 +652,7 @@ function getThemeConfig(cfg) {
|
|
|
574
652
|
registerMdPlugins(extraVPConfig, markdownPlugin);
|
|
575
653
|
patchMermaidPluginCfg(extraVPConfig);
|
|
576
654
|
patchOptimizeDeps(extraVPConfig);
|
|
655
|
+
patchVPConfig(extraVPConfig, cfg);
|
|
577
656
|
return {
|
|
578
657
|
themeConfig: {
|
|
579
658
|
blog: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sugarat/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "简约风的 Vitepress 博客主题,sugarat vitepress blog theme",
|
|
5
5
|
"author": "sugar",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,12 +52,14 @@
|
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@element-plus/icons-vue": "^2.1.0",
|
|
55
|
+
"artalk": "^2.8.3",
|
|
55
56
|
"element-plus": "^2.3.4",
|
|
56
57
|
"javascript-stringify": "^2.1.0",
|
|
57
58
|
"pagefind": "1.0.3",
|
|
58
59
|
"sass": "^1.56.1",
|
|
59
60
|
"typescript": "^4.8.2",
|
|
60
|
-
"
|
|
61
|
+
"vite": "^5",
|
|
62
|
+
"vitepress": "1.1.4",
|
|
61
63
|
"vue": "^3.4.21"
|
|
62
64
|
},
|
|
63
65
|
"scripts": {
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
import Theme from 'vitepress/theme'
|
|
3
3
|
import { useData } from 'vitepress'
|
|
4
4
|
import { computed } from 'vue'
|
|
5
|
+
import { useDarkTransition } from '../hooks/useDarkTransition'
|
|
5
6
|
import { useOml2d } from '../hooks/useOml2d'
|
|
6
|
-
import { useBlogThemeMode } from '../composables/config/blog'
|
|
7
|
+
import { useBlogThemeMode, useDarkTransitionConfig } from '../composables/config/blog'
|
|
7
8
|
import BlogHomeInfo from './BlogHomeInfo.vue'
|
|
8
9
|
import BlogHomeBanner from './BlogHomeBanner.vue'
|
|
9
10
|
import BlogList from './BlogList.vue'
|
|
10
|
-
import BlogComment from './BlogComment.vue'
|
|
11
|
-
|
|
12
11
|
import BlogSidebar from './BlogSidebar.vue'
|
|
13
12
|
import BlogImagePreview from './BlogImagePreview.vue'
|
|
14
13
|
import BlogArticleAnalyze from './BlogArticleAnalyze.vue'
|
|
@@ -17,6 +16,11 @@ import BlogPopover from './BlogPopover.vue'
|
|
|
17
16
|
import BlogFooter from './BlogFooter.vue'
|
|
18
17
|
import BlogHomeHeaderAvatar from './BlogHomeHeaderAvatar.vue'
|
|
19
18
|
import BlogBackToTop from './BlogBackToTop.vue'
|
|
19
|
+
import CommentGiscus from './CommentGiscus.vue'
|
|
20
|
+
|
|
21
|
+
import CommentArtalk from './CommentArtalk.vue'
|
|
22
|
+
import BlogButtonAfterArticle from './BlogButtonAfterArticle.vue'
|
|
23
|
+
import BlogCommentWrapper from './BlogCommentWrapper.vue'
|
|
20
24
|
|
|
21
25
|
const { frontmatter } = useData()
|
|
22
26
|
const layout = computed(() => frontmatter.value.layout)
|
|
@@ -25,14 +29,20 @@ const { Layout } = Theme
|
|
|
25
29
|
|
|
26
30
|
// oh-my-live2d 扩展
|
|
27
31
|
useOml2d()
|
|
32
|
+
// 切换深色模式过渡
|
|
33
|
+
// https://vitepress.dev/zh/guide/extending-default-theme#on-appearance-toggle
|
|
34
|
+
useDarkTransition()
|
|
35
|
+
const openTransition = useDarkTransitionConfig()
|
|
28
36
|
</script>
|
|
29
37
|
|
|
30
38
|
<template>
|
|
31
|
-
<Layout>
|
|
39
|
+
<Layout :class="{ 'blog-theme-layout': openTransition }">
|
|
32
40
|
<template #layout-top>
|
|
33
41
|
<slot name="layout-top" />
|
|
34
|
-
<
|
|
35
|
-
|
|
42
|
+
<ClientOnly>
|
|
43
|
+
<BlogAlert />
|
|
44
|
+
<BlogPopover />
|
|
45
|
+
</ClientOnly>
|
|
36
46
|
</template>
|
|
37
47
|
|
|
38
48
|
<template #doc-before>
|
|
@@ -74,8 +84,12 @@ useOml2d()
|
|
|
74
84
|
<slot name="doc-after" />
|
|
75
85
|
<!-- 评论 -->
|
|
76
86
|
<ClientOnly>
|
|
87
|
+
<BlogButtonAfterArticle />
|
|
77
88
|
<BlogBackToTop />
|
|
78
|
-
<
|
|
89
|
+
<BlogCommentWrapper>
|
|
90
|
+
<CommentArtalk />
|
|
91
|
+
<CommentGiscus />
|
|
92
|
+
</BlogCommentWrapper>
|
|
79
93
|
</ClientOnly>
|
|
80
94
|
</template>
|
|
81
95
|
<template #layout-bottom>
|
|
@@ -191,6 +205,7 @@ useOml2d()
|
|
|
191
205
|
.blog-list-wrapper {
|
|
192
206
|
width: 100%;
|
|
193
207
|
}
|
|
208
|
+
|
|
194
209
|
.blog-info-wrapper {
|
|
195
210
|
margin-left: 16px;
|
|
196
211
|
position: sticky;
|
|
@@ -216,3 +231,7 @@ useOml2d()
|
|
|
216
231
|
}
|
|
217
232
|
}
|
|
218
233
|
</style>
|
|
234
|
+
|
|
235
|
+
<style>
|
|
236
|
+
@import url(./../styles/dark-transition.css);
|
|
237
|
+
</style>
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ElButton } from 'element-plus'
|
|
3
|
+
import { computed, ref, watch } from 'vue'
|
|
4
|
+
import { useData } from 'vitepress'
|
|
5
|
+
import { useBlogConfig } from '../composables/config/blog'
|
|
6
|
+
import { aliPaySVG, weChatPaySVG } from '../constants/svg'
|
|
7
|
+
|
|
8
|
+
const { buttonAfterArticle: _buttonAfterArticle } = useBlogConfig()
|
|
9
|
+
const { frontmatter } = useData()
|
|
10
|
+
const frontmatterConfig = computed(() => frontmatter.value.buttonAfterArticle)
|
|
11
|
+
|
|
12
|
+
const buttonAfterArticleConfig = computed(() => {
|
|
13
|
+
if (frontmatterConfig.value === false || (!frontmatterConfig.value && !_buttonAfterArticle)) {
|
|
14
|
+
return false
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return { ..._buttonAfterArticle, ...frontmatterConfig.value }
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const showContent = ref(false)
|
|
21
|
+
|
|
22
|
+
watch(buttonAfterArticleConfig, () => {
|
|
23
|
+
showContent.value = !!buttonAfterArticleConfig.value?.expand
|
|
24
|
+
}, {
|
|
25
|
+
immediate: true
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const svg = computed(() => {
|
|
29
|
+
const icon = buttonAfterArticleConfig.value?.icon
|
|
30
|
+
if (icon === 'aliPay') {
|
|
31
|
+
return aliPaySVG
|
|
32
|
+
}
|
|
33
|
+
else if (icon === 'wechatPay') {
|
|
34
|
+
return weChatPaySVG
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return icon as string
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
function toggleContent() {
|
|
42
|
+
showContent.value = !showContent.value
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<div v-if="buttonAfterArticleConfig" class="appreciation-container">
|
|
48
|
+
<ElButton :size="buttonAfterArticleConfig.size || 'default'" class="content-button" :type="showContent ? 'danger' : 'primary'" @click="toggleContent">
|
|
49
|
+
<span class="content-icon" v-html="svg" />
|
|
50
|
+
{{ showContent ? buttonAfterArticleConfig.closeTitle : buttonAfterArticleConfig.openTitle }}
|
|
51
|
+
</ElButton>
|
|
52
|
+
<transition name="content">
|
|
53
|
+
<div v-if="showContent" class="content-container" v-html="buttonAfterArticleConfig.content" />
|
|
54
|
+
</transition>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<style scoped lang="scss">
|
|
59
|
+
.appreciation-container {
|
|
60
|
+
text-align: center;
|
|
61
|
+
padding: 20px;
|
|
62
|
+
font-size: 14px;
|
|
63
|
+
color: #606266;
|
|
64
|
+
|
|
65
|
+
:deep(.el-button.el-button--primary){
|
|
66
|
+
background-color: var(--vp-c-brand-2);
|
|
67
|
+
border-color: var(--vp-c-brand-2);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.content-container {
|
|
72
|
+
position: relative;
|
|
73
|
+
display: flex;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
align-items: center;
|
|
76
|
+
margin-top: 20px;
|
|
77
|
+
:deep(img){
|
|
78
|
+
height: 260px;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.content-icon {
|
|
83
|
+
font-family: "iconfont" !important;
|
|
84
|
+
font-size: 16px;
|
|
85
|
+
margin-right: 8px;
|
|
86
|
+
font-style: normal;
|
|
87
|
+
-webkit-font-smoothing: antialiased;
|
|
88
|
+
-moz-osx-font-smoothing: grayscale;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* 进入动画 */
|
|
92
|
+
.content-enter-active {
|
|
93
|
+
animation: fadeIn 0.5s ease forwards;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* 离开动画 */
|
|
97
|
+
.content-leave-active {
|
|
98
|
+
animation: fadeOut 0.3s ease forwards;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* 淡入 */
|
|
102
|
+
@keyframes fadeIn {
|
|
103
|
+
from {
|
|
104
|
+
opacity: 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
to {
|
|
108
|
+
opacity: 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* 淡出 */
|
|
113
|
+
@keyframes fadeOut {
|
|
114
|
+
from {
|
|
115
|
+
opacity: 1;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
to {
|
|
119
|
+
opacity: 0;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
</style>
|