hexo-theme-particlex 2.5.8 → 2.5.9
Sign up to get free protection for your applications and to get access to all the features.
- package/layout/card.ejs +1 -1
- package/layout/comment.ejs +1 -1
- package/layout/index.ejs +3 -5
- package/layout/layout.ejs +14 -23
- package/layout/menu.ejs +4 -4
- package/layout/post.ejs +6 -13
- package/package.json +1 -1
- package/source/css/main.css +13 -17
- package/source/js/lib/crypto.js +18 -27
- package/source/js/lib/highlight.js +17 -12
- package/source/js/lib/home.js +5 -6
- package/source/js/lib/preview.js +4 -5
- package/source/js/main.js +14 -13
package/layout/card.ejs
CHANGED
package/layout/comment.ejs
CHANGED
package/layout/index.ejs
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
<div
|
3
3
|
id="home-background"
|
4
4
|
ref="homeBackground"
|
5
|
-
data-
|
5
|
+
data-images="<%- theme.background.map(i => url_for(i)) %>"
|
6
6
|
></div>
|
7
7
|
<div id="home-info" @click="homeClick">
|
8
8
|
<span class="loop"></span>
|
@@ -20,10 +20,8 @@
|
|
20
20
|
</div>
|
21
21
|
<div id="home-posts-wrap" <%- theme.card.enable ? "" : 'class="home-posts-wrap-no-card"' %> ref="homePostsWrap">
|
22
22
|
<div id="home-posts">
|
23
|
-
|
24
|
-
|
25
|
-
<%- partial("current") %>
|
26
|
-
</div>
|
23
|
+
<%- partial("posts") %>
|
24
|
+
<%- partial("current") %>
|
27
25
|
</div>
|
28
26
|
<% if (theme.card.enable) { %>
|
29
27
|
<div id="home-card">
|
package/layout/layout.ejs
CHANGED
@@ -1,25 +1,16 @@
|
|
1
1
|
<%
|
2
|
-
let type
|
3
|
-
if (is_home())
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
if (page.title)
|
15
|
-
title = page.title + " | ";
|
16
|
-
else if (is_category())
|
17
|
-
title = "Categories: " + page.category + " | ";
|
18
|
-
else if (is_tag())
|
19
|
-
title = "Tags: " + page.tag + " | ";
|
20
|
-
else if (is_archive())
|
21
|
-
title = "Archives | ";
|
22
|
-
title += config.title;
|
2
|
+
let type;
|
3
|
+
if (is_home()) type = "index";
|
4
|
+
if (is_post() || is_page()) type = "post";
|
5
|
+
if (is_category() || page.type === "categories") type = "categories";
|
6
|
+
if (is_tag() || page.type === "tags") type = "tags";
|
7
|
+
if (is_archive()) type = "archives";
|
8
|
+
let title;
|
9
|
+
if (is_home()) title = config.title;
|
10
|
+
if (is_post() || is_page()) title = page.title + " | " + config.title;
|
11
|
+
if (is_category()) title = "Categories: " + page.category + " | " + config.title;
|
12
|
+
if (is_tag()) title = "Tags: " + page.tag + " | " + config.title;
|
13
|
+
if (is_archive()) title = "Archives | " + config.title;
|
23
14
|
%>
|
24
15
|
<!DOCTYPE html>
|
25
16
|
<html lang="<%- config.language %>">
|
@@ -39,14 +30,14 @@
|
|
39
30
|
<div id="loading" v-show="loading">
|
40
31
|
<div id="loading-circle">
|
41
32
|
<h2>LOADING</h2>
|
42
|
-
<p
|
33
|
+
<p>加载过慢请开启缓存 浏览器默认开启</p>
|
43
34
|
<img src="<%- url_for("/images/loading.gif") %>" />
|
44
35
|
</div>
|
45
36
|
</div>
|
46
37
|
</transition>
|
38
|
+
<%- partial("menu") %>
|
47
39
|
<transition name="into">
|
48
40
|
<div id="main" v-show="!loading">
|
49
|
-
<%- partial("menu") %>
|
50
41
|
<%- partial(type) %>
|
51
42
|
<%- partial("footer") %>
|
52
43
|
</div>
|
package/layout/menu.ejs
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
<nav id="menu"
|
1
|
+
<nav id="menu" :class="{ hidden: hiddenMenu, 'menu-color': menuColor}">
|
2
2
|
<div class="desktop-menu">
|
3
3
|
<a class="title" href="<%- config.root %>">
|
4
4
|
<span><%= config.title.toUpperCase() %></span>
|
@@ -11,13 +11,13 @@
|
|
11
11
|
<% }); %>
|
12
12
|
</div>
|
13
13
|
<div id="mobile-menu">
|
14
|
-
<div class="curtain"
|
15
|
-
<div class="title" @click="
|
14
|
+
<div class="curtain" @click="shouMenuItems = !shouMenuItems" v-show="shouMenuItems"></div>
|
15
|
+
<div class="title" @click="shouMenuItems = !shouMenuItems">
|
16
16
|
<i class="fa-solid fa-bars fa-fw"></i>
|
17
17
|
<span> <%= config.title.toUpperCase() %></span>
|
18
18
|
</div>
|
19
19
|
<transition name="slide">
|
20
|
-
<div class="items" v-show="
|
20
|
+
<div class="items" v-show="shouMenuItems">
|
21
21
|
<% Object.keys(theme.menu).forEach(key => { %>
|
22
22
|
<a href="<%- url_for(theme.menu[key].link) %>">
|
23
23
|
<div class="item">
|
package/layout/post.ejs
CHANGED
@@ -43,26 +43,19 @@
|
|
43
43
|
<% } %>
|
44
44
|
</div>
|
45
45
|
<% if (theme.crypto.enable && typeof page.secret !== "undefined") { %>
|
46
|
-
<%
|
47
|
-
const CryptoJS = crypto();
|
48
|
-
function SHA(word) {
|
49
|
-
return CryptoJS.SHA256(word).toString();
|
50
|
-
}
|
51
|
-
function encrypt(word, secret) {
|
52
|
-
return CryptoJS.AES.encrypt(word, secret).toString();
|
53
|
-
}
|
54
|
-
%>
|
46
|
+
<% const CryptoJS = crypto(); %>
|
55
47
|
<input
|
56
48
|
id="crypto"
|
57
|
-
class="input"
|
49
|
+
:class="['input', cryptoClass]"
|
50
|
+
:disabled="check"
|
58
51
|
ref="crypto"
|
59
52
|
placeholder="文章被加密,请输入密码"
|
60
|
-
data-encrypted="<%- encrypt(page.content, page.secret) %>"
|
61
|
-
data-shasum="<%-
|
53
|
+
data-encrypted="<%- CryptoJS.AES.encrypt(page.content, page.secret).toString() %>"
|
54
|
+
data-shasum="<%- CryptoJS.SHA256(page.content).toString() %>"
|
62
55
|
v-model="crypto"
|
63
56
|
/>
|
64
57
|
<transition name="fade">
|
65
|
-
<div class="content" ref="content" v-show="check"></div>
|
58
|
+
<div class="content" ref="content" v-html="decrypted" v-show="check"></div>
|
66
59
|
</transition>
|
67
60
|
<% } else { %>
|
68
61
|
<div class="content" v-pre>
|
package/package.json
CHANGED
package/source/css/main.css
CHANGED
@@ -92,6 +92,7 @@
|
|
92
92
|
box-shadow: 0 0 20px #d9d9d980;
|
93
93
|
padding: 25px 0;
|
94
94
|
text-align: center;
|
95
|
+
width: 300px;
|
95
96
|
}
|
96
97
|
#home-card .card-style .avatar {
|
97
98
|
border: #f1f1f1 solid 3px;
|
@@ -360,10 +361,10 @@
|
|
360
361
|
height: 50vmin;
|
361
362
|
justify-content: center;
|
362
363
|
padding: 50px;
|
364
|
+
text-align: center;
|
363
365
|
width: 50vmin;
|
364
366
|
}
|
365
367
|
#main {
|
366
|
-
display: grid;
|
367
368
|
margin-right: calc(100% - 100vw);
|
368
369
|
}
|
369
370
|
#menu {
|
@@ -488,6 +489,7 @@
|
|
488
489
|
line-height: 2;
|
489
490
|
overflow: auto;
|
490
491
|
padding: 50px 30px 20px;
|
492
|
+
white-space: pre;
|
491
493
|
}
|
492
494
|
.comment iframe,
|
493
495
|
body::-webkit-scrollbar-track {
|
@@ -504,24 +506,24 @@ body::-webkit-scrollbar-track {
|
|
504
506
|
margin: 15px auto;
|
505
507
|
max-width: 75%;
|
506
508
|
}
|
507
|
-
.
|
509
|
+
.copycode {
|
508
510
|
color: #5c6b72;
|
509
511
|
position: absolute;
|
510
512
|
right: 0;
|
511
513
|
top: 0;
|
512
514
|
}
|
513
|
-
.
|
515
|
+
.copycode i {
|
514
516
|
padding: 15px;
|
515
517
|
position: absolute;
|
516
518
|
right: 0;
|
517
519
|
top: 0;
|
518
520
|
transition: transform 0.25s;
|
519
521
|
}
|
520
|
-
.
|
522
|
+
.copycode.copied i {
|
521
523
|
transform: scale(1.25);
|
522
524
|
}
|
523
|
-
.
|
524
|
-
.
|
525
|
+
.copycode.copied i:first-child,
|
526
|
+
.copycode:not(.copied) i:last-child {
|
525
527
|
opacity: 0;
|
526
528
|
}
|
527
529
|
.fade-enter-active,
|
@@ -658,7 +660,7 @@ a {
|
|
658
660
|
text-decoration: none;
|
659
661
|
}
|
660
662
|
a:hover,
|
661
|
-
.content .
|
663
|
+
.content .copycode:hover {
|
662
664
|
opacity: 0.8;
|
663
665
|
}
|
664
666
|
b,
|
@@ -694,13 +696,12 @@ iframe,
|
|
694
696
|
.friend-link a,
|
695
697
|
.icon-link a,
|
696
698
|
.language,
|
697
|
-
.
|
699
|
+
.copycode {
|
698
700
|
user-select: none;
|
699
701
|
}
|
700
702
|
code {
|
701
703
|
background: #bddcf76b;
|
702
704
|
border-radius: 4px;
|
703
|
-
font-size: 14px;
|
704
705
|
line-height: 2.5;
|
705
706
|
padding: 4px 8px;
|
706
707
|
}
|
@@ -764,6 +765,7 @@ pre {
|
|
764
765
|
box-shadow: 0 2px 12px 0 #0000001a;
|
765
766
|
margin: 25px 0;
|
766
767
|
margin: 25px 0;
|
768
|
+
white-space: normal;
|
767
769
|
}
|
768
770
|
pre,
|
769
771
|
code,
|
@@ -824,9 +826,6 @@ ol li {
|
|
824
826
|
}
|
825
827
|
}
|
826
828
|
@media (min-width: 900px) {
|
827
|
-
#home-card {
|
828
|
-
margin-right: auto;
|
829
|
-
}
|
830
829
|
#home-head #home-info .info .wrap {
|
831
830
|
padding: 25px;
|
832
831
|
}
|
@@ -855,9 +854,8 @@ ol li {
|
|
855
854
|
width: 500px;
|
856
855
|
}
|
857
856
|
#home-posts {
|
858
|
-
margin-left: auto;
|
859
857
|
margin-right: 50px;
|
860
|
-
width:
|
858
|
+
width: 850px;
|
861
859
|
}
|
862
860
|
#home-posts .post {
|
863
861
|
margin-bottom: 25px;
|
@@ -867,8 +865,7 @@ ol li {
|
|
867
865
|
padding: 20px 0;
|
868
866
|
}
|
869
867
|
#home-posts-wrap {
|
870
|
-
max-width:
|
871
|
-
padding: 30px 0;
|
868
|
+
max-width: 1200px;
|
872
869
|
}
|
873
870
|
#menu .desktop-menu {
|
874
871
|
display: block;
|
@@ -894,7 +891,6 @@ ol li {
|
|
894
891
|
display: none;
|
895
892
|
}
|
896
893
|
#home-posts {
|
897
|
-
margin: auto;
|
898
894
|
width: 100%;
|
899
895
|
}
|
900
896
|
#home-posts-wrap,
|
package/source/js/lib/crypto.js
CHANGED
@@ -2,40 +2,31 @@ mixins.crypto = {
|
|
2
2
|
data() {
|
3
3
|
return {
|
4
4
|
crypto: "",
|
5
|
-
check:
|
5
|
+
check: null,
|
6
6
|
};
|
7
7
|
},
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
watch: {
|
9
|
+
crypto(value) {
|
10
|
+
let input = this.$refs.crypto,
|
11
|
+
content = this.$refs.content;
|
12
|
+
let { encrypted, shasum } = input.dataset;
|
13
13
|
try {
|
14
|
-
let
|
15
|
-
|
14
|
+
let decrypted = CryptoJS.AES.decrypt(encrypted, value).toString(CryptoJS.enc.Utf8);
|
15
|
+
if (CryptoJS.SHA256(decrypted).toString() === shasum) {
|
16
|
+
this.check = true;
|
17
|
+
content.innerHTML = decrypted;
|
18
|
+
this.render();
|
19
|
+
} else this.check = false;
|
16
20
|
} catch {
|
17
|
-
|
21
|
+
this.check = false;
|
18
22
|
}
|
19
23
|
},
|
20
24
|
},
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
input.dataset.encrypted,
|
27
|
-
value,
|
28
|
-
input.dataset.shasum
|
29
|
-
);
|
30
|
-
this.check = check;
|
31
|
-
if (check) {
|
32
|
-
input.classList.remove("fail");
|
33
|
-
input.classList.add("success");
|
34
|
-
input.disabled = true;
|
35
|
-
content.innerHTML = decrypted;
|
36
|
-
this.render();
|
37
|
-
} else input.classList.add("fail");
|
25
|
+
computed: {
|
26
|
+
cryptoClass() {
|
27
|
+
if (this.check === null) return "";
|
28
|
+
if (this.check === true) return "success";
|
29
|
+
if (this.check === false) return "fail";
|
38
30
|
},
|
39
31
|
},
|
40
32
|
};
|
41
|
-
mixins.push(cryptoMixin);
|
@@ -10,24 +10,29 @@ mixins.highlight = {
|
|
10
10
|
},
|
11
11
|
methods: {
|
12
12
|
highlight() {
|
13
|
-
let that = this;
|
14
13
|
let codes = document.querySelectorAll("pre");
|
15
14
|
for (let i of codes) {
|
16
|
-
let lang = [...i.classList, ...i.firstChild.classList][0] || "plaintext";
|
17
15
|
let code = i.innerText;
|
18
|
-
|
19
|
-
let
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
let language = [...i.classList, ...i.firstChild.classList][0] || "plaintext";
|
17
|
+
let highlighted = hljs.highlight(code, { language }).value;
|
18
|
+
i.innerHTML = `
|
19
|
+
<div class="code-content">${highlighted}</div>
|
20
|
+
<div class="language">${language}</div>
|
21
|
+
<div class="copycode">
|
22
|
+
<i class="fa-solid fa-copy fa-fw"></i>
|
23
|
+
<i class="fa-solid fa-clone fa-fw"></i>
|
24
|
+
</div>
|
25
|
+
`;
|
26
|
+
let copycode = i.querySelector(".copycode");
|
27
|
+
copycode.addEventListener("click", async () => {
|
28
|
+
if (this.copying) return;
|
29
|
+
this.copying = true;
|
30
|
+
copycode.classList.add("copied");
|
24
31
|
await navigator.clipboard.writeText(code);
|
25
32
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
26
|
-
|
27
|
-
|
33
|
+
copycode.classList.remove("copied");
|
34
|
+
this.copying = false;
|
28
35
|
});
|
29
|
-
let content = i.querySelector(".code-content");
|
30
|
-
hljs.highlightElement(content);
|
31
36
|
}
|
32
37
|
},
|
33
38
|
},
|
package/source/js/lib/home.js
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
mixins.home = {
|
2
2
|
mounted() {
|
3
|
-
let
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
background.style.backgroundImage = `url('${image[id]}')`;
|
3
|
+
let background = this.$refs.homeBackground;
|
4
|
+
let images = background.dataset.images.split(",");
|
5
|
+
let id = Math.floor(Math.random() * images.length);
|
6
|
+
background.style.backgroundImage = `url('${images[id]}')`;
|
7
|
+
this.menuColor = true;
|
9
8
|
},
|
10
9
|
methods: {
|
11
10
|
homeClick() {
|
package/source/js/lib/preview.js
CHANGED
@@ -9,15 +9,14 @@ mixins.preview = {
|
|
9
9
|
},
|
10
10
|
methods: {
|
11
11
|
preview() {
|
12
|
-
let that = this;
|
13
12
|
let preview = this.$refs.preview,
|
14
13
|
content = this.$refs.previewContent;
|
15
14
|
let images = document.querySelectorAll("img");
|
16
15
|
for (let i of images)
|
17
|
-
i.addEventListener("click",
|
18
|
-
content.alt =
|
19
|
-
content.src =
|
20
|
-
|
16
|
+
i.addEventListener("click", () => {
|
17
|
+
content.alt = i.alt;
|
18
|
+
content.src = i.src;
|
19
|
+
this.previewShow = true;
|
21
20
|
});
|
22
21
|
preview.addEventListener("click", () => {
|
23
22
|
this.previewShow = false;
|
package/source/js/main.js
CHANGED
@@ -3,8 +3,10 @@ const app = Vue.createApp({
|
|
3
3
|
data() {
|
4
4
|
return {
|
5
5
|
loading: true,
|
6
|
-
|
7
|
-
|
6
|
+
hiddenMenu: false,
|
7
|
+
showMenuItems: false,
|
8
|
+
menuColor: false,
|
9
|
+
scrollTop: 0,
|
8
10
|
renderers: [],
|
9
11
|
};
|
10
12
|
},
|
@@ -23,20 +25,19 @@ const app = Vue.createApp({
|
|
23
25
|
for (let i of this.renderers) i();
|
24
26
|
},
|
25
27
|
handleScroll() {
|
26
|
-
let
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
this.
|
31
|
-
|
32
|
-
} else menu.classList.remove("hidden");
|
28
|
+
let wrap = this.$refs.homePostsWrap;
|
29
|
+
let newScrollTop = document.documentElement.scrollTop;
|
30
|
+
if (this.scrollTop < newScrollTop) {
|
31
|
+
this.hiddenMenu = true;
|
32
|
+
this.showMenuItems = false;
|
33
|
+
} else this.hiddenMenu = false;
|
33
34
|
if (wrap) {
|
34
|
-
if (
|
35
|
-
else
|
36
|
-
if (
|
35
|
+
if (newScrollTop <= window.innerHeight - 100) this.menuColor = true;
|
36
|
+
else this.menuColor = false;
|
37
|
+
if (newScrollTop <= 400) wrap.style.marginTop = -newScrollTop / 5 + "px";
|
37
38
|
else wrap.style.marginTop = "-80px";
|
38
39
|
}
|
39
|
-
this.
|
40
|
+
this.scrollTop = newScrollTop;
|
40
41
|
},
|
41
42
|
},
|
42
43
|
});
|