hexo-theme-particlex 2.1.1 → 2.1.2

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 CHANGED
@@ -199,24 +199,13 @@ highlightStyle: github # Highlight style
199
199
 
200
200
  - 搜索
201
201
 
202
- 嵌入到 Archives 中的搜索,搜索数据是用 [Hexo-Generator-Search-Lite](https://github.com/argvchs/hexo-generator-search-lite) 生成,默认关闭,使用需要安装上述插件并**设置 `optimize: true`**
202
+ 嵌入到 Archives 中的搜索,默认关闭
203
203
 
204
- 目前只支持搜索文档的 Title Categories Tags(我太弱了)
205
-
206
- 要同时在主题和根目录的两个 `_config.yml` 添加配置
204
+ 目前只支持搜索文档标题(我太弱了)
207
205
 
208
206
  ```yaml
209
- # Theme config
210
207
  search:
211
208
  enable: false
212
- path: /search.json
213
- ```
214
-
215
- ```yaml
216
- # Site config
217
- search:
218
- path: /search.json
219
- optimize: true
220
209
  ```
221
210
 
222
211
  - Gitalk
package/_config.yml CHANGED
@@ -87,7 +87,6 @@ crypto:
87
87
  # Search
88
88
  search:
89
89
  enable: false
90
- path: /search.json
91
90
 
92
91
  # Gitalk
93
92
  # https://github.com/gitalk/gitalk
@@ -1,12 +1,10 @@
1
1
  <div id="archives">
2
2
  <% if (theme.search.enable) { %>
3
- <div class="search-mask" style="z-index: <%= site.posts.length + 1 %>"></div>
4
- <input class="ipt search-bar" placeholder="搜索..." style="z-index: <%= site.posts.length + 2 %>">
5
- <script src="<%- url_for("/js/searcher.js") %>"></script>
6
- <script>searcher.init("<%- url_for(theme.search.path) %>");</script>
3
+ <div id="search-mask" style="z-index: <%= site.posts.length + 1 %>"></div>
4
+ <input id="search-bar" class="ipt" placeholder="搜索" style="z-index: <%= site.posts.length + 2 %>">
7
5
  <% } %>
8
6
  <% site.posts.forEach((post, id) => { %>
9
- <div class="timeline" path="<%- url_for(post.path) %>" style="z-index: <%= site.posts.length - id %>">
7
+ <div class="timeline" style="z-index: <%= site.posts.length - id %>" data-title="<%- post.title.toLowerCase().replace(/\s+/gm, "") %>">
10
8
  <div class="timeline-tail"></div>
11
9
  <div class="timeline-content">
12
10
  <div class="item-time"><%- date(post.date, "YYYY/M/D") %></div>
@@ -20,7 +20,8 @@
20
20
  <%
21
21
  posts = category.posts;
22
22
  posts.data.sort((a, b) => {
23
- a.top ??= 0, b.top ??= 0;
23
+ if (typeof a.top === "undefined") a.top = 0;
24
+ if (typeof b.top === "undefined") b.top = 0;
24
25
  return a.top == b.top ? b.date - a.date : b.top - a.top
25
26
  });
26
27
  %>
package/layout/layout.ejs CHANGED
@@ -21,8 +21,9 @@
21
21
  title = "Archives | ";
22
22
  title += config.title;
23
23
  site.posts.data.sort((a, b) => {
24
- a.top ??= 0, b.top ??= 0;
25
- return a.top == b.top ? b.date - a.date : b.top - a.top
24
+ if (typeof a.top === "undefined") a.top = 0;
25
+ if (typeof b.top === "undefined") b.top = 0;
26
+ return a.top == b.top ? b.date - a.date : b.top - a.top;
26
27
  });
27
28
  %>
28
29
  <!DOCTYPE html>
package/layout/post.ejs CHANGED
@@ -41,21 +41,19 @@
41
41
  <% if (typeof page.password !== "undefined" && theme.crypto.enable) { %>
42
42
  <%
43
43
  const CryptoJS = crypto();
44
- function SHA(str) {
44
+ function sha(str) {
45
45
  return CryptoJS.SHA256(str).toString(CryptoJS.enc.Base64);
46
46
  }
47
47
  function encrypt(str, key) {
48
- return CryptoJS.AES.encrypt(str, SHA(key), {
48
+ return CryptoJS.AES.encrypt(str, sha(key), {
49
49
  mode: CryptoJS.mode.ECB,
50
50
  padding: CryptoJS.pad.Pkcs7,
51
51
  }).toString();
52
52
  }
53
53
  %>
54
- <input class="ipt crypto" placeholder="文章被加密,请输入密码">
55
- <div class="content" v-pre></div>
56
54
  <script src="https://cdn.staticfile.org/crypto-js/4.1.1/crypto-js.min.js"></script>
57
- <script src="<%- url_for("/js/cryptor.js") %>"></script>
58
- <script>cryptor.init("<%- encrypt(page.content, page.password.toString()) %>", "<%- SHA(page.content) %>")</script>
55
+ <input id="crypto" class="ipt" placeholder="文章被加密,请输入密码" data-encrypt="<%- encrypt(page.content, page.password.toString()) %>" data-check="<%- sha(page.content) %>">
56
+ <div class="content" style="opacity: 0" v-pre></div>
59
57
  <% } else { %>
60
58
  <div class="content" v-pre>
61
59
  <%- page.content %>
package/layout/tags.ejs CHANGED
@@ -20,8 +20,9 @@
20
20
  <%
21
21
  posts = tag.posts;
22
22
  posts.data.sort((a, b) => {
23
- a.top ??= 0, b.top ??= 0;
24
- return a.top == b.top ? b.date - a.date : b.top - a.top
23
+ if (typeof a.top === "undefined") a.top = 0;
24
+ if (typeof b.top === "undefined") b.top = 0;
25
+ return a.top == b.top ? b.date - a.date : b.top - a.top;
25
26
  });
26
27
  %>
27
28
  <% } %>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-particlex",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "A concise Hexo theme, based on Particle.",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -24,7 +24,6 @@
24
24
  },
25
25
  "homepage": "https://github.com/argvchs/hexo-theme-particlex#readme",
26
26
  "dependencies": {
27
- "hexo-generator-search-lite": "^1.0.8",
28
27
  "hexo-helper-crypto": "^1.0.4",
29
28
  "hexo-renderer-babeljs": "^1.0.7",
30
29
  "hexo-renderer-ejs": "^2.0.0"
@@ -61,6 +61,7 @@ body {
61
61
  margin: 0;
62
62
  padding: 0;
63
63
  word-wrap: break-word;
64
+ word-break: keep-all;
64
65
  scrollbar-width: thin;
65
66
  scrollbar-color: #8ab5ff #e6efff;
66
67
  }
@@ -200,9 +201,6 @@ footer .footer-wrap {
200
201
  color: #333;
201
202
  border-radius: 50%;
202
203
  }
203
- #home-head .home-info .info h1 {
204
- word-break: keep-all;
205
- }
206
204
  #home-head .home-info .loop:nth-child(1) {
207
205
  border-radius: 38% 62% 63% 37%/41% 44% 56% 59%;
208
206
  background: #fff;
@@ -292,8 +290,10 @@ footer .footer-wrap {
292
290
  #home-posts .post .excerpt {
293
291
  color: #1e3e3f;
294
292
  }
293
+ #home-posts .post .post-tags {
294
+ line-height: 1.5;
295
+ }
295
296
  #home-posts .post .post-tags a {
296
- color: #ffbbf4;
297
297
  font-size: 14px;
298
298
  }
299
299
  #home-posts .post .post-tags .tag {
@@ -477,7 +477,6 @@ footer .footer-wrap {
477
477
  #menu .desktop-menu .title {
478
478
  display: inline-block;
479
479
  margin-left: 30px;
480
- margin-right: 20px;
481
480
  font-family: "Lexend", "Noto Sans SC", sans-serif;
482
481
  text-transform: uppercase;
483
482
  color: #555;
@@ -718,16 +717,24 @@ footer .footer-icon {
718
717
  color: #5c6b72;
719
718
  text-decoration: none;
720
719
  }
721
- #archives .tag-icon {
722
- color: #5c6b72;
723
- text-decoration: none;
720
+ #archives .info {
721
+ line-height: 1.5;
724
722
  }
725
723
  #archives .category {
726
- margin-right: 20px;
724
+ display: inline-block;
725
+ margin-right: 10px;
726
+ }
727
+ #archives .tags {
728
+ display: inline-block;
727
729
  }
728
730
  #archives .tags .tag {
731
+ display: inline-block;
729
732
  margin-right: 10px;
730
733
  }
734
+ #archives .tag-icon {
735
+ color: #5c6b72;
736
+ text-decoration: none;
737
+ }
731
738
  #archives h3 {
732
739
  margin: 10px 0;
733
740
  }
@@ -778,6 +785,9 @@ footer .footer-icon {
778
785
  font-weight: bold;
779
786
  margin: 20px 0;
780
787
  }
788
+ .article .info {
789
+ line-height: 1.5;
790
+ }
781
791
  .article .info a {
782
792
  color: #5c6b72;
783
793
  text-decoration: none;
@@ -785,25 +795,18 @@ footer .footer-icon {
785
795
  .article .info .date {
786
796
  color: #5c6b72;
787
797
  display: inline-block;
788
- margin-right: 20px;
789
- }
790
- .article .info .date .tag {
791
798
  margin-right: 10px;
792
799
  }
793
800
  .article .info .category {
794
- color: #5c6b72;
795
801
  display: inline-block;
796
- margin-right: 20px;
797
- }
798
- .article .info .category .tag {
799
802
  margin-right: 10px;
800
803
  }
801
804
  .article .info .tags {
802
- color: #5c6b72;
803
805
  display: inline-block;
804
- margin-right: 20px;
806
+ margin-right: 10px;
805
807
  }
806
808
  .article .info .tags .tag {
809
+ display: inline-block;
807
810
  margin-right: 10px;
808
811
  }
809
812
  .article .content {
@@ -862,24 +865,24 @@ input.ipt:focus {
862
865
  border-color: #0969da;
863
866
  box-shadow: 0 0 0 3px #0969da4d;
864
867
  }
865
- input.crypto {
868
+ #crypto {
866
869
  margin: 50px auto 0;
867
870
  }
868
- input.crypto.fail {
871
+ #crypto.fail {
869
872
  color: #ea4a5a;
870
873
  border-color: #ea4a5a;
871
874
  }
872
- input.crypto.fail:focus {
875
+ #crypto.fail:focus {
873
876
  box-shadow: 0 0 0 3px #ea4a5a4d;
874
877
  }
875
- input.crypto.success {
878
+ #crypto.success {
876
879
  color: #34d058;
877
880
  border-color: #34d058;
878
881
  }
879
- input.search-bar {
882
+ #search-bar {
880
883
  margin: 0 auto 50px;
881
884
  }
882
- .search-mask {
885
+ #search-mask {
883
886
  position: relative;
884
887
  margin: auto;
885
888
  margin-top: -125px;
@@ -888,58 +891,6 @@ input.search-bar {
888
891
  height: 150px;
889
892
  background: #f6f8fa;
890
893
  }
891
- .gt-container *:not(.gt-header-textarea) {
892
- font-family: unset !important;
893
- }
894
- .gt-header-textarea {
895
- font-family: "Fira Code", "Noto Sans SC", monospace;
896
- }
897
- .gt-container .gt-comment-content {
898
- border: 1px solid #0000001a;
899
- border-radius: 5px;
900
- }
901
- .gt-container .gt-comment-content:hover,
902
- .gt-container .gt-header-textarea:hover,
903
- .gt-container .gt-header-textarea:focus {
904
- box-shadow: 0 2px 8px #00000017 !important;
905
- background-color: #fbfbfb;
906
- }
907
- .gt-container .gt-header-textarea:focus {
908
- background-color: #fbfbfb;
909
- }
910
- .gt-container .gt-avatar img {
911
- border-radius: 50% !important;
912
- }
913
- .gt-container .gt-popup {
914
- border-radius: 10px;
915
- }
916
- .gt-container .markdown-body {
917
- color: unset !important;
918
- }
919
- .gt-container .markdown-body p {
920
- margin: 10px 0 !important;
921
- }
922
- .gt-container .markdown-body blockquote {
923
- border-left: unset !important;
924
- padding: 1px 20px !important;
925
- border-left: 3px solid #1e3e3f !important;
926
- color: unset !important;
927
- }
928
- .gt-container .markdown-body code {
929
- font-family: "Fira Code" !important;
930
- background: #bddcf76b;
931
- border-radius: 4px;
932
- font-size: 14px;
933
- color: #4b616b;
934
- }
935
- .gt-container .markdown-body pre {
936
- font-family: "Fira Code" !important;
937
- font-weight: 500;
938
- border: 1px solid #ebeef5;
939
- padding: 20px;
940
- margin: 25px 0;
941
- border-radius: 15px !important;
942
- }
943
894
  #showimg {
944
895
  position: fixed !important;
945
896
  display: flex;
@@ -962,6 +913,9 @@ input.search-bar {
962
913
  max-height: 95%;
963
914
  box-shadow: 0 0 50px 10px #d9d9d980;
964
915
  }
916
+ .math.display .katex {
917
+ overflow: auto;
918
+ }
965
919
  ::-webkit-scrollbar {
966
920
  width: 12px;
967
921
  height: 12px;
@@ -1,12 +1,14 @@
1
- const sleep = ms => new Promise(res => setTimeout(res, ms));
1
+ function sleep(ms) {
2
+ return new Promise(resolve => setTimeout(resolve, ms));
3
+ }
2
4
  let copying = false;
3
5
  function highlight() {
4
6
  hljs.configure({ ignoreUnescapedHTML: true });
5
7
  let codes = document.getElementsByTagName("pre");
6
- for (let code of codes) {
7
- let lang = [...code.classList, ...code.firstChild.classList][0] || "text";
8
- code.innerHTML = `<div class="code-content">${code.innerHTML}</div><div class="language">${lang}</div><div class="copycode"><i class="fa-solid fa-copy fa-fw"></i><i class="fa-solid fa-clone fa-fw"></i></div>`;
9
- let copycode = code.getElementsByClassName("copycode")[0];
8
+ for (let i of codes) {
9
+ let lang = [...i.classList, ...i.firstChild.classList][0] || "text";
10
+ i.innerHTML = `<div class="code-content">${i.innerHTML}</div><div class="language">${lang}</div><div class="copycode"><i class="fa-solid fa-copy fa-fw"></i><i class="fa-solid fa-clone fa-fw"></i></div>`;
11
+ let copycode = i.getElementsByClassName("copycode")[0];
10
12
  copycode.addEventListener("click", async function () {
11
13
  if (copying) return;
12
14
  copying = true;
@@ -16,13 +18,13 @@ function highlight() {
16
18
  this.classList.remove("copied");
17
19
  copying = false;
18
20
  });
19
- hljs.highlightElement(code.getElementsByClassName("code-content")[0]);
21
+ hljs.highlightElement(i.getElementsByClassName("code-content")[0]);
20
22
  }
21
23
  }
22
24
  function showimg() {
23
25
  let wrap = document.getElementById("showimg"),
24
26
  content = document.getElementById("showimg-content"),
25
- imgs = document.querySelectorAll(".article .content img");
27
+ images = document.querySelectorAll(".article .content img");
26
28
  function show(src) {
27
29
  content.setAttribute("src", src);
28
30
  wrap.style.opacity = 1;
@@ -32,12 +34,13 @@ function showimg() {
32
34
  wrap.style.opacity = 0;
33
35
  wrap.style.visibility = "hidden";
34
36
  }
35
- for (let img of imgs)
36
- img.addEventListener("click", function () {
37
+ for (let i of images)
38
+ i.addEventListener("click", function () {
37
39
  show(this.getAttribute("src"));
38
40
  });
39
- wrap.addEventListener("click", () => hide());
40
- window.addEventListener("resize", () => hide());
41
+ wrap.addEventListener("click", hide);
42
+ window.addEventListener("resize", hide);
43
+ window.addEventListener("scroll", hide);
41
44
  }
42
45
  function rendermath() {
43
46
  if (typeof renderMathInElement !== "undefined")
@@ -50,3 +53,17 @@ function rendermath() {
50
53
  ],
51
54
  });
52
55
  }
56
+ function sha(str) {
57
+ return CryptoJS.SHA256(str).toString(CryptoJS.enc.Base64);
58
+ }
59
+ function decrypt(encrypt, key, check) {
60
+ try {
61
+ let res = CryptoJS.AES.decrypt(encrypt, sha(key), {
62
+ mode: CryptoJS.mode.ECB,
63
+ padding: CryptoJS.pad.Pkcs7,
64
+ }).toString(CryptoJS.enc.Utf8);
65
+ return { decrypt: res, check: sha(res) == check };
66
+ } catch {
67
+ return { check: false };
68
+ }
69
+ }
@@ -5,19 +5,41 @@ const app = Vue.createApp({
5
5
  menushow: false,
6
6
  cardtop: 100,
7
7
  barlocal: 0,
8
+ composition: false,
8
9
  };
9
10
  },
10
- created() {
11
- let that = this;
12
- window.addEventListener("load", () => {
13
- that.showpage = true;
14
- document.getElementById("loading").style.opacity = 0;
15
- document.getElementById("loading").style.visibility = "hidden";
16
- });
17
- },
18
11
  mounted() {
12
+ this.showpage = true;
13
+ document.getElementById("loading").style.opacity = 0;
14
+ document.getElementById("loading").style.visibility = "hidden";
19
15
  if (document.getElementById("home-head"))
20
16
  document.getElementById("menu").className += " menu-color";
17
+ if (document.getElementById("crypto")) {
18
+ let input = document.getElementById("crypto");
19
+ input.addEventListener("input", () => {
20
+ if (!this.composition) this.handlecrypto();
21
+ });
22
+ input.addEventListener("compositionstart", () => {
23
+ this.composition = true;
24
+ });
25
+ input.addEventListener("compositionend", () => {
26
+ this.handlecrypto();
27
+ this.composition = false;
28
+ });
29
+ }
30
+ if (document.getElementById("search-bar")) {
31
+ let input = document.getElementById("search-bar");
32
+ input.addEventListener("input", () => {
33
+ if (!this.composition) this.handlesearch();
34
+ });
35
+ input.addEventListener("compositionstart", () => {
36
+ this.composition = true;
37
+ });
38
+ input.addEventListener("compositionend", () => {
39
+ this.handlesearch();
40
+ this.composition = false;
41
+ });
42
+ }
21
43
  window.addEventListener("scroll", this.handlescroll, true);
22
44
  highlight();
23
45
  showimg();
@@ -25,20 +47,16 @@ const app = Vue.createApp({
25
47
  },
26
48
  methods: {
27
49
  homeclick() {
28
- window.scrollTo({
29
- top: window.innerHeight,
30
- behavior: "smooth",
31
- });
50
+ window.scrollTo({ top: window.innerHeight, behavior: "smooth" });
32
51
  },
33
52
  handlescroll() {
34
53
  let newlocal = document.documentElement.scrollTop;
35
54
  let menu = document.getElementById("menu");
36
55
  let wrap = document.getElementById("home-posts-wrap");
37
56
  let footer = document.getElementById("footer");
38
- let that = this;
39
57
  if (this.barlocal < newlocal) {
40
58
  menu.className = "hidden-menu";
41
- that.menushow = false;
59
+ this.menushow = false;
42
60
  } else menu.className = "show-menu";
43
61
  if (wrap) {
44
62
  if (newlocal <= window.innerHeight - 100) menu.className += " menu-color";
@@ -52,6 +70,36 @@ const app = Vue.createApp({
52
70
  }
53
71
  this.barlocal = newlocal;
54
72
  },
73
+ handlecrypto() {
74
+ let input = document.getElementById("crypto"),
75
+ content = document.getElementsByClassName("content")[0];
76
+ let res = decrypt(input.dataset.encrypt, input.value, input.dataset.check);
77
+ if (res.check) {
78
+ input.disabled = true;
79
+ input.classList.remove("fail");
80
+ input.classList.add("success");
81
+ content.innerHTML = res.decrypt;
82
+ content.style.opacity = 1;
83
+ highlight();
84
+ showimg();
85
+ rendermath();
86
+ } else input.classList.add("fail");
87
+ },
88
+ handlesearch() {
89
+ let input = document.getElementById("search-bar"),
90
+ timeline = document.getElementsByClassName("timeline"),
91
+ key = input.value.toLowerCase().replace(/s+/gm, "");
92
+ for (let i of timeline)
93
+ if (!key || i.dataset.title.includes(key)) {
94
+ i.style.opacity = 1;
95
+ i.style.pointerEvents = "";
96
+ i.style.marginTop = "";
97
+ } else {
98
+ i.style.opacity = 0;
99
+ i.style.pointerEvents = "none";
100
+ i.style.marginTop = -i.offsetHeight - 30 + "px";
101
+ }
102
+ },
55
103
  },
56
104
  });
57
105
  app.mount("#layout");
@@ -1,44 +0,0 @@
1
- const cryptor = {
2
- init(enc, sha) {
3
- this.enc = enc;
4
- this.sha = sha;
5
- this.composition = false;
6
- window.addEventListener("load", () => {
7
- this.input = document.getElementsByClassName("crypto")[0];
8
- this.content = document.getElementsByClassName("content")[0];
9
- this.content.style.opacity = 0;
10
- this.input.addEventListener("input", () => this.composition || this.update());
11
- this.input.addEventListener("compositionstart", () => (this.composition = true));
12
- this.input.addEventListener("compositionend", () => {
13
- this.update(), (this.composition = false);
14
- });
15
- });
16
- },
17
- SHA(str) {
18
- return CryptoJS.SHA256(str).toString(CryptoJS.enc.Base64);
19
- },
20
- decrypt(enc, key, sha) {
21
- try {
22
- let res = CryptoJS.AES.decrypt(enc, this.SHA(key), {
23
- mode: CryptoJS.mode.ECB,
24
- padding: CryptoJS.pad.Pkcs7,
25
- }).toString(CryptoJS.enc.Utf8);
26
- return { dec: res, check: this.SHA(res) == sha };
27
- } catch {
28
- return { check: false };
29
- }
30
- },
31
- update() {
32
- let res = this.decrypt(this.enc, this.input.value, this.sha);
33
- if (res.check) {
34
- this.input.disabled = true;
35
- this.input.classList.remove("fail");
36
- this.input.classList.add("success");
37
- this.content.innerHTML = res.dec;
38
- this.content.style.opacity = 1;
39
- highlight();
40
- showimg();
41
- rendermath();
42
- } else this.input.classList.add("fail");
43
- },
44
- };
@@ -1,43 +0,0 @@
1
- const searcher = {
2
- init(path) {
3
- this.composition = false;
4
- window.addEventListener("load", () => {
5
- this.input = document.getElementsByClassName("search-bar")[0];
6
- this.timeline = document.getElementsByClassName("timeline");
7
- this.input.addEventListener("input", () => this.composition || this.update());
8
- this.input.addEventListener("compositionstart", () => (this.composition = true));
9
- this.input.addEventListener("compositionend", () => {
10
- this.update();
11
- this.composition = false;
12
- });
13
- fetch(path)
14
- .then(res => res.json())
15
- .then(data => {
16
- this.data = data;
17
- });
18
- });
19
- },
20
- rstr(s) {
21
- if (!s) return "";
22
- return s.toLowerCase().replace(/\s+/gm, "");
23
- },
24
- match(s, rs) {
25
- return s.indexOf(rs) != -1;
26
- },
27
- update() {
28
- let res = [],
29
- rs = this.rstr(this.input.value);
30
- if (rs) res = this.data.filter(i => this.match(i.odata, rs)).map(i => i.path);
31
- else res = this.data.map(i => i.path);
32
- for (let line of this.timeline)
33
- if (res.indexOf(line.getAttribute("path")) == -1) {
34
- line.style.opacity = 0;
35
- line.style.pointerEvents = "none";
36
- line.style.marginTop = -line.offsetHeight - 30 + "px";
37
- } else {
38
- line.style.opacity = 1;
39
- line.style.pointerEvents = "";
40
- line.style.marginTop = "";
41
- }
42
- },
43
- };