@sugarat/theme 0.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/LICENSE +21 -0
- package/README.md +35 -0
- package/node.d.ts +174 -0
- package/node.js +185 -0
- package/package.json +57 -0
- package/src/components/BlogAlert.vue +74 -0
- package/src/components/BlogApp.vue +103 -0
- package/src/components/BlogArticleAnalyze.vue +154 -0
- package/src/components/BlogComment.vue +131 -0
- package/src/components/BlogFriendLink.vue +94 -0
- package/src/components/BlogHomeBanner.vue +105 -0
- package/src/components/BlogHomeInfo.vue +38 -0
- package/src/components/BlogHomeOverview.vue +97 -0
- package/src/components/BlogHomeTags.vue +123 -0
- package/src/components/BlogHotArticle.vue +177 -0
- package/src/components/BlogImagePreview.vue +52 -0
- package/src/components/BlogItem.vue +112 -0
- package/src/components/BlogList.vue +75 -0
- package/src/components/BlogPopover.vue +218 -0
- package/src/components/BlogRecommendArticle.vue +173 -0
- package/src/components/BlogSearch.vue +198 -0
- package/src/components/BlogSidebar.vue +19 -0
- package/src/composables/config/blog.ts +96 -0
- package/src/composables/config/index.ts +159 -0
- package/src/index.ts +18 -0
- package/src/node.ts +175 -0
- package/src/styles/bg.png +0 -0
- package/src/styles/index.scss +95 -0
- package/src/utils/index.ts +84 -0
- package/types/vue-shim.d.ts +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 sugar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @sugarat/theme
|
|
2
|
+
|
|
3
|
+
简约风的 Vitepress 博客主题,更多细节见 https://theme.sugarat.top
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
① 初始化项目模板
|
|
9
|
+
```sh
|
|
10
|
+
npx degit ATQQ/sugar-blog/packages/template my-blog-demo
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
② 安装依赖
|
|
14
|
+
```sh
|
|
15
|
+
pnpm install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
③ 开发启动
|
|
19
|
+
```sh
|
|
20
|
+
pnpm dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
④ 构建产物
|
|
24
|
+
```sh
|
|
25
|
+
pnpm build
|
|
26
|
+
```
|
|
27
|
+
## Advanced Ssage
|
|
28
|
+
详细配置见文档 https://theme.sugarat.top
|
|
29
|
+
|
|
30
|
+
## Thanks
|
|
31
|
+
从以下项目中获得了灵感&经验
|
|
32
|
+
* [vuepress-reco/vuepress-theme-reco-1.x](https://github.com/vuepress-reco/vuepress-theme-reco-1.x)
|
|
33
|
+
* [@vue/theme](https://github.com/vuejs/theme)
|
|
34
|
+
* [vitest](https://vitest.dev/)
|
|
35
|
+
* [element-plus](https://github.com/element-plus/element-plus)
|
package/node.d.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { DefaultTheme, UserConfig } from 'vitepress';
|
|
2
|
+
import { ElButton } from 'element-plus';
|
|
3
|
+
|
|
4
|
+
declare namespace BlogPopover {
|
|
5
|
+
interface Title {
|
|
6
|
+
type: 'title';
|
|
7
|
+
content: string;
|
|
8
|
+
style?: string;
|
|
9
|
+
}
|
|
10
|
+
interface Text {
|
|
11
|
+
type: 'text';
|
|
12
|
+
content: string;
|
|
13
|
+
style?: string;
|
|
14
|
+
}
|
|
15
|
+
interface Image {
|
|
16
|
+
type: 'image';
|
|
17
|
+
src: string;
|
|
18
|
+
style?: string;
|
|
19
|
+
}
|
|
20
|
+
interface Button {
|
|
21
|
+
type: 'button';
|
|
22
|
+
link: string;
|
|
23
|
+
content: string;
|
|
24
|
+
style?: string;
|
|
25
|
+
props?: InstanceType<typeof ElButton>['$props'];
|
|
26
|
+
}
|
|
27
|
+
type Value = Title | Text | Image | Button;
|
|
28
|
+
}
|
|
29
|
+
declare namespace Theme {
|
|
30
|
+
interface PageMeta {
|
|
31
|
+
title: string;
|
|
32
|
+
date: string;
|
|
33
|
+
tag?: string[];
|
|
34
|
+
description?: string;
|
|
35
|
+
cover?: string;
|
|
36
|
+
sticky?: number;
|
|
37
|
+
author?: string;
|
|
38
|
+
hidden?: boolean;
|
|
39
|
+
layout?: string;
|
|
40
|
+
categories: string[];
|
|
41
|
+
tags: string[];
|
|
42
|
+
}
|
|
43
|
+
interface PageData {
|
|
44
|
+
route: string;
|
|
45
|
+
meta: PageMeta;
|
|
46
|
+
}
|
|
47
|
+
interface activeTag {
|
|
48
|
+
label: string;
|
|
49
|
+
type: string;
|
|
50
|
+
}
|
|
51
|
+
interface GiscusConfig {
|
|
52
|
+
repo: string;
|
|
53
|
+
repoId: string;
|
|
54
|
+
category: string;
|
|
55
|
+
categoryId: string;
|
|
56
|
+
mapping?: string;
|
|
57
|
+
inputPosition?: 'top' | 'bottom';
|
|
58
|
+
lang?: string;
|
|
59
|
+
loading?: 'lazy' | '';
|
|
60
|
+
}
|
|
61
|
+
interface HotArticle {
|
|
62
|
+
title?: string;
|
|
63
|
+
pageSize?: number;
|
|
64
|
+
nextText?: string;
|
|
65
|
+
empty?: string | boolean;
|
|
66
|
+
}
|
|
67
|
+
interface RecommendArticle {
|
|
68
|
+
title?: string;
|
|
69
|
+
pageSize?: number;
|
|
70
|
+
nextText?: string;
|
|
71
|
+
empty?: string | boolean;
|
|
72
|
+
}
|
|
73
|
+
interface HomeBlog {
|
|
74
|
+
name?: string;
|
|
75
|
+
motto?: string;
|
|
76
|
+
inspiring?: string;
|
|
77
|
+
pageSize?: number;
|
|
78
|
+
}
|
|
79
|
+
interface ArticleConfig {
|
|
80
|
+
readingTime?: boolean;
|
|
81
|
+
}
|
|
82
|
+
interface Alert {
|
|
83
|
+
type: 'success' | 'warning' | 'info' | 'error';
|
|
84
|
+
/**
|
|
85
|
+
* 细粒度的时间控制
|
|
86
|
+
* 默认展示时间,-1 只展示1次,其它数字为每次都展示,一定时间后自动消失,0为不自动消失
|
|
87
|
+
* 配置改变时,会重新触发展示
|
|
88
|
+
*/
|
|
89
|
+
duration: number;
|
|
90
|
+
title?: string;
|
|
91
|
+
description?: string;
|
|
92
|
+
closable?: boolean;
|
|
93
|
+
center?: boolean;
|
|
94
|
+
closeText?: string;
|
|
95
|
+
showIcon?: boolean;
|
|
96
|
+
html?: string;
|
|
97
|
+
}
|
|
98
|
+
interface Popover {
|
|
99
|
+
title: string;
|
|
100
|
+
/**
|
|
101
|
+
* 细粒度的时间控制
|
|
102
|
+
* 默认展示时间,-1 只展示1次,其它数字为每次都展示,一定时间后自动消失,0为不自动消失
|
|
103
|
+
* 配置改变时,会重新触发展示
|
|
104
|
+
*/
|
|
105
|
+
duration: number;
|
|
106
|
+
body?: BlogPopover.Value[];
|
|
107
|
+
footer?: BlogPopover.Value[];
|
|
108
|
+
/**
|
|
109
|
+
* 手动重新打开
|
|
110
|
+
*/
|
|
111
|
+
reopen?: boolean;
|
|
112
|
+
}
|
|
113
|
+
interface FriendLink {
|
|
114
|
+
nickname: string;
|
|
115
|
+
des: string;
|
|
116
|
+
url: string;
|
|
117
|
+
avatar: string;
|
|
118
|
+
}
|
|
119
|
+
interface BlogConfig {
|
|
120
|
+
pagesData: PageData[];
|
|
121
|
+
srcDir?: string;
|
|
122
|
+
author?: string;
|
|
123
|
+
hotArticle?: HotArticle;
|
|
124
|
+
home?: HomeBlog;
|
|
125
|
+
search?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* 配置评论
|
|
128
|
+
* power by https://giscus.app/zh-CN
|
|
129
|
+
*/
|
|
130
|
+
comment?: GiscusConfig | false;
|
|
131
|
+
recommend?: RecommendArticle;
|
|
132
|
+
article?: ArticleConfig;
|
|
133
|
+
/**
|
|
134
|
+
* el-alert
|
|
135
|
+
*/
|
|
136
|
+
alert?: Alert;
|
|
137
|
+
popover?: Popover;
|
|
138
|
+
friend?: FriendLink[];
|
|
139
|
+
}
|
|
140
|
+
interface Config extends DefaultTheme.Config {
|
|
141
|
+
blog: BlogConfig;
|
|
142
|
+
}
|
|
143
|
+
interface HomeConfig {
|
|
144
|
+
handleChangeSlogan?: (oldSlogan: string) => string | Promise<string>;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
declare function getThemeConfig(cfg?: Partial<Theme.BlogConfig>): {
|
|
149
|
+
blog: {
|
|
150
|
+
pagesData: Theme.PageData[];
|
|
151
|
+
srcDir?: string | undefined;
|
|
152
|
+
author?: string | undefined;
|
|
153
|
+
hotArticle?: Theme.HotArticle | undefined;
|
|
154
|
+
home?: Theme.HomeBlog | undefined;
|
|
155
|
+
search?: boolean | undefined;
|
|
156
|
+
comment?: false | Theme.GiscusConfig | undefined;
|
|
157
|
+
recommend?: Theme.RecommendArticle | undefined;
|
|
158
|
+
article?: Theme.ArticleConfig | undefined;
|
|
159
|
+
alert?: Theme.Alert | undefined;
|
|
160
|
+
popover?: Theme.Popover | undefined;
|
|
161
|
+
friend?: Theme.FriendLink[] | undefined;
|
|
162
|
+
};
|
|
163
|
+
sidebar: {
|
|
164
|
+
text: string;
|
|
165
|
+
items: never[];
|
|
166
|
+
}[];
|
|
167
|
+
};
|
|
168
|
+
declare function getDefaultTitle(content: string): string;
|
|
169
|
+
declare function clearMatterContent(content: string): string;
|
|
170
|
+
declare function getFileBirthTime(url: string): string;
|
|
171
|
+
declare function getGitTimestamp(file: string): Promise<unknown>;
|
|
172
|
+
declare function defineConfig(config: UserConfig<Theme.Config>): UserConfig<Theme.Config>;
|
|
173
|
+
|
|
174
|
+
export { clearMatterContent, defineConfig, getDefaultTitle, getFileBirthTime, getGitTimestamp, getThemeConfig };
|
package/node.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
|
+
|
|
26
|
+
// src/node.ts
|
|
27
|
+
var node_exports = {};
|
|
28
|
+
__export(node_exports, {
|
|
29
|
+
clearMatterContent: () => clearMatterContent,
|
|
30
|
+
defineConfig: () => defineConfig,
|
|
31
|
+
getDefaultTitle: () => getDefaultTitle,
|
|
32
|
+
getFileBirthTime: () => getFileBirthTime,
|
|
33
|
+
getGitTimestamp: () => getGitTimestamp,
|
|
34
|
+
getThemeConfig: () => getThemeConfig
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(node_exports);
|
|
37
|
+
var import_fast_glob = __toESM(require("fast-glob"));
|
|
38
|
+
var import_gray_matter = __toESM(require("gray-matter"));
|
|
39
|
+
var import_fs = __toESM(require("fs"));
|
|
40
|
+
var import_child_process = require("child_process");
|
|
41
|
+
var import_path = __toESM(require("path"));
|
|
42
|
+
|
|
43
|
+
// src/utils/index.ts
|
|
44
|
+
function formatDate(d, fmt = "yyyy-MM-dd hh:mm:ss") {
|
|
45
|
+
if (!(d instanceof Date)) {
|
|
46
|
+
d = new Date(d);
|
|
47
|
+
}
|
|
48
|
+
const o = {
|
|
49
|
+
"M+": d.getMonth() + 1,
|
|
50
|
+
"d+": d.getDate(),
|
|
51
|
+
"h+": d.getHours(),
|
|
52
|
+
"m+": d.getMinutes(),
|
|
53
|
+
"s+": d.getSeconds(),
|
|
54
|
+
"q+": Math.floor((d.getMonth() + 3) / 3),
|
|
55
|
+
S: d.getMilliseconds()
|
|
56
|
+
};
|
|
57
|
+
if (/(y+)/.test(fmt)) {
|
|
58
|
+
fmt = fmt.replace(
|
|
59
|
+
RegExp.$1,
|
|
60
|
+
`${d.getFullYear()}`.substr(4 - RegExp.$1.length)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
for (const k in o) {
|
|
64
|
+
if (new RegExp(`(${k})`).test(fmt))
|
|
65
|
+
fmt = fmt.replace(
|
|
66
|
+
RegExp.$1,
|
|
67
|
+
RegExp.$1.length === 1 ? o[k] : `00${o[k]}`.substr(`${o[k]}`.length)
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return fmt;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/node.ts
|
|
74
|
+
function getThemeConfig(cfg) {
|
|
75
|
+
const srcDir = cfg?.srcDir || process.argv.slice(2)?.[1] || ".";
|
|
76
|
+
const files = import_fast_glob.default.sync(`${srcDir}/**/*.md`, { ignore: ["node_modules"] });
|
|
77
|
+
const data = files.map((v) => {
|
|
78
|
+
let route = v.replace(".md", "");
|
|
79
|
+
if (route.startsWith("./")) {
|
|
80
|
+
route = route.replace(
|
|
81
|
+
new RegExp(`^\\.\\/${import_path.default.join(srcDir, "/")}`),
|
|
82
|
+
""
|
|
83
|
+
);
|
|
84
|
+
} else {
|
|
85
|
+
route = route.replace(new RegExp(`^${import_path.default.join(srcDir, "/")}`), "");
|
|
86
|
+
}
|
|
87
|
+
const fileContent = import_fs.default.readFileSync(v, "utf-8");
|
|
88
|
+
const meta = {
|
|
89
|
+
...(0, import_gray_matter.default)(fileContent).data
|
|
90
|
+
};
|
|
91
|
+
if (!meta.title) {
|
|
92
|
+
meta.title = getDefaultTitle(fileContent);
|
|
93
|
+
}
|
|
94
|
+
if (!meta.date) {
|
|
95
|
+
meta.date = getFileBirthTime(v);
|
|
96
|
+
} else {
|
|
97
|
+
meta.date = formatDate(
|
|
98
|
+
new Date(`${new Date(meta.date).toUTCString()}+8`)
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
meta.tag = (meta.tag || []).concat([
|
|
102
|
+
.../* @__PURE__ */ new Set([...meta.categories || [], ...meta.tags || []])
|
|
103
|
+
]);
|
|
104
|
+
const wordCount = 100;
|
|
105
|
+
meta.description = meta.description || getTextSummary(fileContent, wordCount);
|
|
106
|
+
meta.cover = meta.cover || fileContent.match(/[!]\[.+?\]\((https:\/\/.+)\)/)?.[1] || "";
|
|
107
|
+
return {
|
|
108
|
+
route: `/${route}`,
|
|
109
|
+
meta
|
|
110
|
+
};
|
|
111
|
+
}).filter((v) => v.meta.layout !== "home");
|
|
112
|
+
return {
|
|
113
|
+
blog: {
|
|
114
|
+
pagesData: data,
|
|
115
|
+
...cfg
|
|
116
|
+
},
|
|
117
|
+
sidebar: [
|
|
118
|
+
{
|
|
119
|
+
text: "",
|
|
120
|
+
items: []
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function getDefaultTitle(content) {
|
|
126
|
+
const title = clearMatterContent(content).split("\n")?.find((str) => {
|
|
127
|
+
return str.startsWith("# ");
|
|
128
|
+
})?.slice(2).replace(/[\s]/g, "") || "";
|
|
129
|
+
return title;
|
|
130
|
+
}
|
|
131
|
+
function clearMatterContent(content) {
|
|
132
|
+
let first___;
|
|
133
|
+
let second___;
|
|
134
|
+
const lines = content.split("\n").reduce((pre, line) => {
|
|
135
|
+
if (!line.trim() && pre.length === 0) {
|
|
136
|
+
return pre;
|
|
137
|
+
}
|
|
138
|
+
if (line.trim() === "---") {
|
|
139
|
+
if (first___ === void 0) {
|
|
140
|
+
first___ = pre.length;
|
|
141
|
+
} else if (second___ === void 0) {
|
|
142
|
+
second___ = pre.length;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
pre.push(line);
|
|
146
|
+
return pre;
|
|
147
|
+
}, []);
|
|
148
|
+
return lines.slice(second___ || 0).join("\n");
|
|
149
|
+
}
|
|
150
|
+
function getFileBirthTime(url) {
|
|
151
|
+
const infoStr = (0, import_child_process.execSync)(`git log -1 --pretty="%ci" ${url}`).toString("utf-8").trim();
|
|
152
|
+
let date = new Date();
|
|
153
|
+
if (infoStr) {
|
|
154
|
+
date = new Date(infoStr);
|
|
155
|
+
}
|
|
156
|
+
return formatDate(date);
|
|
157
|
+
}
|
|
158
|
+
function getGitTimestamp(file) {
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
const child = (0, import_child_process.spawn)("git", ["log", "-1", '--pretty="%ci"', file]);
|
|
161
|
+
let output = "";
|
|
162
|
+
child.stdout.on("data", (d) => {
|
|
163
|
+
output += String(d);
|
|
164
|
+
});
|
|
165
|
+
child.on("close", () => {
|
|
166
|
+
resolve(+new Date(output));
|
|
167
|
+
});
|
|
168
|
+
child.on("error", reject);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function getTextSummary(text, count = 100) {
|
|
172
|
+
return clearMatterContent(text).match(/^# ([\s\S]+)/m)?.[1]?.replace(/#/g, "")?.replace(/!\[.*?\]\(.*?\)/g, "")?.replace(/\[(.*?)\]\(.*?\)/g, "$1")?.replace(/\*\*(.*?)\*\*/g, "$1")?.split("\n")?.filter((v) => !!v)?.slice(1)?.join("\n")?.replace(/>(.*)/, "")?.slice(0, count);
|
|
173
|
+
}
|
|
174
|
+
function defineConfig(config) {
|
|
175
|
+
return config;
|
|
176
|
+
}
|
|
177
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
178
|
+
0 && (module.exports = {
|
|
179
|
+
clearMatterContent,
|
|
180
|
+
defineConfig,
|
|
181
|
+
getDefaultTitle,
|
|
182
|
+
getFileBirthTime,
|
|
183
|
+
getGitTimestamp,
|
|
184
|
+
getThemeConfig
|
|
185
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sugarat/theme",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "粥里有勺糖的博客主题,sugarat vitepress blog theme",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./node": {
|
|
8
|
+
"types": "./node.d.ts",
|
|
9
|
+
"default": "./node.js"
|
|
10
|
+
},
|
|
11
|
+
".": "./src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"types",
|
|
16
|
+
"node.js",
|
|
17
|
+
"node.d.ts"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/ATQQ/sugar-blog.git"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"vitepress",
|
|
25
|
+
"theme",
|
|
26
|
+
"粥里有勺糖"
|
|
27
|
+
],
|
|
28
|
+
"author": "sugar",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"homepage": "https://next.sugarat.top",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/ATQQ/sugar-blog/issues"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@element-plus/icons-vue": "^2.0.10",
|
|
36
|
+
"@vue/shared": "^3.2.45",
|
|
37
|
+
"@vueuse/core": "^9.6.0",
|
|
38
|
+
"element-plus": "^2.2.26",
|
|
39
|
+
"fast-glob": "^3.2.12",
|
|
40
|
+
"gray-matter": "^4.0.3"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"sass": "^1.56.1",
|
|
44
|
+
"tsup": " ^6.5.0",
|
|
45
|
+
"typescript": "^4.8.2",
|
|
46
|
+
"vitepress": "^1.0.0-alpha.33",
|
|
47
|
+
"vue": "^3.2.45"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"dev": "vitepress dev demo",
|
|
51
|
+
"dev:lib": "npm run build:node && vitepress dev demo",
|
|
52
|
+
"build": "npm run build:node && npm run build:docs",
|
|
53
|
+
"build:docs": "vitepress build demo",
|
|
54
|
+
"build:node": "npx tsup src/node.ts --dts --out-dir=./",
|
|
55
|
+
"serve": "npm run build && vitepress serve demo"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="global-alert" v-if="show">
|
|
3
|
+
<el-alert
|
|
4
|
+
:title="alertProps?.title"
|
|
5
|
+
:type="alertProps?.type"
|
|
6
|
+
:show-icon="alertProps?.showIcon"
|
|
7
|
+
:center="alertProps?.center"
|
|
8
|
+
:closable="alertProps?.closable"
|
|
9
|
+
:close-text="alertProps?.closeText"
|
|
10
|
+
:description="alertProps?.description"
|
|
11
|
+
>
|
|
12
|
+
<div v-if="alertProps?.html" v-html="alertProps?.html"></div>
|
|
13
|
+
</el-alert>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script lang="ts" setup>
|
|
18
|
+
import { ElAlert } from 'element-plus'
|
|
19
|
+
import { ref, onMounted } from 'vue'
|
|
20
|
+
import { useBlogConfig } from '../composables/config/blog'
|
|
21
|
+
|
|
22
|
+
const { alert: alertProps } = useBlogConfig()
|
|
23
|
+
const show = ref(false)
|
|
24
|
+
|
|
25
|
+
onMounted(() => {
|
|
26
|
+
const storageKey = 'theme-blog-alert'
|
|
27
|
+
// 取旧值
|
|
28
|
+
const oldValue = localStorage.getItem(storageKey)
|
|
29
|
+
const newValue = JSON.stringify(alertProps)
|
|
30
|
+
localStorage.setItem(storageKey, newValue)
|
|
31
|
+
|
|
32
|
+
// >= 0 每次都展示,区别是否自动消失
|
|
33
|
+
if (Number(alertProps?.duration) >= 0) {
|
|
34
|
+
show.value = true
|
|
35
|
+
if (alertProps?.duration) {
|
|
36
|
+
setTimeout(() => {
|
|
37
|
+
show.value = false
|
|
38
|
+
}, alertProps?.duration)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (oldValue !== newValue && alertProps?.duration === -1) {
|
|
43
|
+
// 当做新值处理
|
|
44
|
+
show.value = true
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style lang="scss" scoped>
|
|
50
|
+
.global-alert {
|
|
51
|
+
position: fixed;
|
|
52
|
+
z-index: 999;
|
|
53
|
+
top: 66px;
|
|
54
|
+
max-width: 500px;
|
|
55
|
+
margin: 0 auto;
|
|
56
|
+
left: 50%;
|
|
57
|
+
transform: translateX(-50%);
|
|
58
|
+
width: auto;
|
|
59
|
+
:deep(.el-alert__content) {
|
|
60
|
+
padding-right: 20px;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
@media screen and (max-width: 1100px) {
|
|
64
|
+
.global-alert {
|
|
65
|
+
width: 50%;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@media screen and (max-width: 600px) {
|
|
70
|
+
.global-alert {
|
|
71
|
+
width: 90%;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
</style>
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script setup lang="ts" name="BlogApp">
|
|
2
|
+
import Theme from 'vitepress/theme'
|
|
3
|
+
import BlogHomeInfo from './BlogHomeInfo.vue'
|
|
4
|
+
import BlogHomeBanner from './BlogHomeBanner.vue'
|
|
5
|
+
import BlogList from './BlogList.vue'
|
|
6
|
+
import BlogComment from './BlogComment.vue'
|
|
7
|
+
import BlogSearch from './BlogSearch.vue'
|
|
8
|
+
import BlogSidebar from './BlogSidebar.vue'
|
|
9
|
+
import BlogImagePreview from './BlogImagePreview.vue'
|
|
10
|
+
import BlogArticleAnalyze from './BlogArticleAnalyze.vue'
|
|
11
|
+
import BlogAlert from './BlogAlert.vue'
|
|
12
|
+
import BlogPopover from './BlogPopover.vue'
|
|
13
|
+
|
|
14
|
+
const { Layout } = Theme
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<Layout>
|
|
19
|
+
<template #layout-top>
|
|
20
|
+
<BlogAlert />
|
|
21
|
+
<BlogPopover />
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<template #doc-before>
|
|
25
|
+
<!-- 阅读时间分析 -->
|
|
26
|
+
<ClientOnly>
|
|
27
|
+
<BlogArticleAnalyze />
|
|
28
|
+
</ClientOnly>
|
|
29
|
+
<!-- 图片预览 -->
|
|
30
|
+
<BlogImagePreview />
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<!-- 自定义搜索,临时替代Algolia -->
|
|
34
|
+
<template #nav-bar-content-before>
|
|
35
|
+
<BlogSearch />
|
|
36
|
+
</template>
|
|
37
|
+
<!-- 自定义首页 -->
|
|
38
|
+
<template #home-hero-before>
|
|
39
|
+
<div class="home">
|
|
40
|
+
<div class="header-banner">
|
|
41
|
+
<BlogHomeBanner />
|
|
42
|
+
</div>
|
|
43
|
+
<div class="content-wrapper">
|
|
44
|
+
<div class="blog-list-wrapper"><blog-list /></div>
|
|
45
|
+
<div class="blog-info-wrapper"><BlogHomeInfo /></div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
<template #sidebar-nav-after>
|
|
50
|
+
<BlogSidebar />
|
|
51
|
+
</template>
|
|
52
|
+
<!-- 评论 -->
|
|
53
|
+
<template #doc-after>
|
|
54
|
+
<BlogComment />
|
|
55
|
+
</template>
|
|
56
|
+
</Layout>
|
|
57
|
+
</template>
|
|
58
|
+
<style scoped lang="scss">
|
|
59
|
+
.home {
|
|
60
|
+
margin: 0 auto;
|
|
61
|
+
padding: 20px;
|
|
62
|
+
max-width: 1126px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.header-banner {
|
|
66
|
+
width: 100%;
|
|
67
|
+
padding: 60px 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.content-wrapper {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: flex-start;
|
|
73
|
+
justify-content: center;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.blog-list-wrapper {
|
|
77
|
+
width: 100%;
|
|
78
|
+
}
|
|
79
|
+
.blog-info-wrapper {
|
|
80
|
+
margin-left: 16px;
|
|
81
|
+
position: sticky;
|
|
82
|
+
top: 100px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@media screen and (max-width: 959px) {
|
|
86
|
+
.blog-info-wrapper {
|
|
87
|
+
margin-left: 16px;
|
|
88
|
+
position: sticky;
|
|
89
|
+
top: 40px;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@media screen and (max-width: 767px) {
|
|
94
|
+
.content-wrapper {
|
|
95
|
+
flex-wrap: wrap;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.blog-info-wrapper {
|
|
99
|
+
margin: 20px 0;
|
|
100
|
+
width: 100%;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
</style>
|